summaryrefslogtreecommitdiffstats
path: root/layout/generic/nsLineBox.cpp
diff options
context:
space:
mode:
authorMatt A. Tobin <mattatobin@localhost.localdomain>2018-02-02 04:16:08 -0500
committerMatt A. Tobin <mattatobin@localhost.localdomain>2018-02-02 04:16:08 -0500
commit5f8de423f190bbb79a62f804151bc24824fa32d8 (patch)
tree10027f336435511475e392454359edea8e25895d /layout/generic/nsLineBox.cpp
parent49ee0794b5d912db1f95dce6eb52d781dc210db5 (diff)
downloadUXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar
UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.gz
UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.lz
UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.xz
UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.zip
Add m-esr52 at 52.6.0
Diffstat (limited to 'layout/generic/nsLineBox.cpp')
-rw-r--r--layout/generic/nsLineBox.cpp989
1 files changed, 989 insertions, 0 deletions
diff --git a/layout/generic/nsLineBox.cpp b/layout/generic/nsLineBox.cpp
new file mode 100644
index 000000000..7244b7f53
--- /dev/null
+++ b/layout/generic/nsLineBox.cpp
@@ -0,0 +1,989 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+// vim:cindent:ts=2:et:sw=2:
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/* representation of one line within a block frame, a CSS line box */
+
+#include "nsLineBox.h"
+
+#include "mozilla/ArenaObjectID.h"
+#include "mozilla/Assertions.h"
+#include "mozilla/Likely.h"
+#include "mozilla/WritingModes.h"
+#include "nsBidiPresUtils.h"
+#include "nsFrame.h"
+#include "nsIFrameInlines.h"
+#include "nsPresArena.h"
+#include "nsPrintfCString.h"
+#include "mozilla/Sprintf.h"
+
+#ifdef DEBUG
+static int32_t ctorCount;
+int32_t nsLineBox::GetCtorCount() { return ctorCount; }
+#endif
+
+#ifndef _MSC_VER
+// static nsLineBox constant; initialized in the header file.
+const uint32_t nsLineBox::kMinChildCountForHashtable;
+#endif
+
+using namespace mozilla;
+
+nsLineBox::nsLineBox(nsIFrame* aFrame, int32_t aCount, bool aIsBlock)
+ : mFirstChild(aFrame)
+ , mContainerSize(-1, -1)
+ , mBounds(WritingMode()) // mBounds will be initialized with the correct
+ // writing mode when it is set
+// NOTE: memory is already zeroed since we allocate with AllocateByObjectID.
+{
+ MOZ_COUNT_CTOR(nsLineBox);
+#ifdef DEBUG
+ ++ctorCount;
+ NS_ASSERTION(!aIsBlock || aCount == 1, "Blocks must have exactly one child");
+ nsIFrame* f = aFrame;
+ for (int32_t n = aCount; n > 0; f = f->GetNextSibling(), --n) {
+ NS_ASSERTION(aIsBlock == f->IsBlockOutside(),
+ "wrong kind of child frame");
+ }
+#endif
+ static_assert(static_cast<int>(StyleClear::Max) <= 15,
+ "FlagBits needs more bits to store the full range of "
+ "break type ('clear') values");
+ mChildCount = aCount;
+ MarkDirty();
+ mFlags.mBlock = aIsBlock;
+}
+
+nsLineBox::~nsLineBox()
+{
+ MOZ_COUNT_DTOR(nsLineBox);
+ if (MOZ_UNLIKELY(mFlags.mHasHashedFrames)) {
+ delete mFrames;
+ }
+ Cleanup();
+}
+
+nsLineBox*
+NS_NewLineBox(nsIPresShell* aPresShell, nsIFrame* aFrame, bool aIsBlock)
+{
+ return new (aPresShell) nsLineBox(aFrame, 1, aIsBlock);
+}
+
+nsLineBox*
+NS_NewLineBox(nsIPresShell* aPresShell, nsLineBox* aFromLine,
+ nsIFrame* aFrame, int32_t aCount)
+{
+ nsLineBox* newLine = new (aPresShell) nsLineBox(aFrame, aCount, false);
+ newLine->NoteFramesMovedFrom(aFromLine);
+ newLine->mContainerSize = aFromLine->mContainerSize;
+ return newLine;
+}
+
+void
+nsLineBox::StealHashTableFrom(nsLineBox* aFromLine, uint32_t aFromLineNewCount)
+{
+ MOZ_ASSERT(!mFlags.mHasHashedFrames);
+ MOZ_ASSERT(GetChildCount() >= int32_t(aFromLineNewCount));
+ mFrames = aFromLine->mFrames;
+ mFlags.mHasHashedFrames = 1;
+ aFromLine->mFlags.mHasHashedFrames = 0;
+ aFromLine->mChildCount = aFromLineNewCount;
+ // remove aFromLine's frames that aren't on this line
+ nsIFrame* f = aFromLine->mFirstChild;
+ for (uint32_t i = 0; i < aFromLineNewCount; f = f->GetNextSibling(), ++i) {
+ mFrames->RemoveEntry(f);
+ }
+}
+
+void
+nsLineBox::NoteFramesMovedFrom(nsLineBox* aFromLine)
+{
+ uint32_t fromCount = aFromLine->GetChildCount();
+ uint32_t toCount = GetChildCount();
+ MOZ_ASSERT(toCount <= fromCount, "moved more frames than aFromLine has");
+ uint32_t fromNewCount = fromCount - toCount;
+ if (MOZ_LIKELY(!aFromLine->mFlags.mHasHashedFrames)) {
+ aFromLine->mChildCount = fromNewCount;
+ MOZ_ASSERT(toCount < kMinChildCountForHashtable);
+ } else if (fromNewCount < kMinChildCountForHashtable) {
+ // aFromLine has a hash table but will not have it after moving the frames
+ // so this line can steal the hash table if it needs it.
+ if (toCount >= kMinChildCountForHashtable) {
+ StealHashTableFrom(aFromLine, fromNewCount);
+ } else {
+ delete aFromLine->mFrames;
+ aFromLine->mFlags.mHasHashedFrames = 0;
+ aFromLine->mChildCount = fromNewCount;
+ }
+ } else {
+ // aFromLine still needs a hash table.
+ if (toCount < kMinChildCountForHashtable) {
+ // remove the moved frames from it
+ nsIFrame* f = mFirstChild;
+ for (uint32_t i = 0; i < toCount; f = f->GetNextSibling(), ++i) {
+ aFromLine->mFrames->RemoveEntry(f);
+ }
+ } else if (toCount <= fromNewCount) {
+ // This line needs a hash table, allocate a hash table for it since that
+ // means fewer hash ops.
+ nsIFrame* f = mFirstChild;
+ for (uint32_t i = 0; i < toCount; f = f->GetNextSibling(), ++i) {
+ aFromLine->mFrames->RemoveEntry(f); // toCount RemoveEntry
+ }
+ SwitchToHashtable(); // toCount PutEntry
+ } else {
+ // This line needs a hash table, but it's fewer hash ops to steal
+ // aFromLine's hash table and allocate a new hash table for that line.
+ StealHashTableFrom(aFromLine, fromNewCount); // fromNewCount RemoveEntry
+ aFromLine->SwitchToHashtable(); // fromNewCount PutEntry
+ }
+ }
+}
+
+void*
+nsLineBox::operator new(size_t sz, nsIPresShell* aPresShell)
+{
+ return aPresShell->AllocateByObjectID(eArenaObjectID_nsLineBox, sz);
+}
+
+void
+nsLineBox::Destroy(nsIPresShell* aPresShell)
+{
+ this->nsLineBox::~nsLineBox();
+ aPresShell->FreeByObjectID(eArenaObjectID_nsLineBox, this);
+}
+
+void
+nsLineBox::Cleanup()
+{
+ if (mData) {
+ if (IsBlock()) {
+ delete mBlockData;
+ }
+ else {
+ delete mInlineData;
+ }
+ mData = nullptr;
+ }
+}
+
+#ifdef DEBUG_FRAME_DUMP
+static void
+ListFloats(FILE* out, const char* aPrefix, const nsFloatCacheList& aFloats)
+{
+ nsFloatCache* fc = aFloats.Head();
+ while (fc) {
+ nsCString str(aPrefix);
+ nsIFrame* frame = fc->mFloat;
+ str += nsPrintfCString("floatframe@%p ", static_cast<void*>(frame));
+ if (frame) {
+ nsAutoString frameName;
+ frame->GetFrameName(frameName);
+ str += NS_ConvertUTF16toUTF8(frameName).get();
+ }
+ else {
+ str += "\n###!!! NULL out-of-flow frame";
+ }
+ fprintf_stderr(out, "%s\n", str.get());
+ fc = fc->Next();
+ }
+}
+
+const char*
+nsLineBox::BreakTypeToString(StyleClear aBreakType) const
+{
+ switch (aBreakType) {
+ case StyleClear::None: return "nobr";
+ case StyleClear::Left: return "leftbr";
+ case StyleClear::Right: return "rightbr";
+ case StyleClear::InlineStart: return "inlinestartbr";
+ case StyleClear::InlineEnd: return "inlineendbr";
+ case StyleClear::Both: return "leftbr+rightbr";
+ case StyleClear::Line: return "linebr";
+ case StyleClear::Max: return "leftbr+rightbr+linebr";
+ }
+ return "unknown";
+}
+
+char*
+nsLineBox::StateToString(char* aBuf, int32_t aBufSize) const
+{
+ snprintf(aBuf, aBufSize, "%s,%s,%s,%s,%s,before:%s,after:%s[0x%x]",
+ IsBlock() ? "block" : "inline",
+ IsDirty() ? "dirty" : "clean",
+ IsPreviousMarginDirty() ? "prevmargindirty" : "prevmarginclean",
+ IsImpactedByFloat() ? "impacted" : "not impacted",
+ IsLineWrapped() ? "wrapped" : "not wrapped",
+ BreakTypeToString(GetBreakTypeBefore()),
+ BreakTypeToString(GetBreakTypeAfter()),
+ mAllFlags);
+ return aBuf;
+}
+
+void
+nsLineBox::List(FILE* out, int32_t aIndent, uint32_t aFlags) const
+{
+ nsCString str;
+ while (aIndent-- > 0) {
+ str += " ";
+ }
+ List(out, str.get(), aFlags);
+}
+
+void
+nsLineBox::List(FILE* out, const char* aPrefix, uint32_t aFlags) const
+{
+ nsCString str(aPrefix);
+ char cbuf[100];
+ str += nsPrintfCString("line %p: count=%d state=%s ",
+ static_cast<const void*>(this), GetChildCount(),
+ StateToString(cbuf, sizeof(cbuf)));
+ if (IsBlock() && !GetCarriedOutBEndMargin().IsZero()) {
+ str += nsPrintfCString("bm=%d ", GetCarriedOutBEndMargin().get());
+ }
+ nsRect bounds = GetPhysicalBounds();
+ str += nsPrintfCString("{%d,%d,%d,%d} ",
+ bounds.x, bounds.y, bounds.width, bounds.height);
+ if (mWritingMode.IsVertical() || !mWritingMode.IsBidiLTR()) {
+ str += nsPrintfCString("{%s: %d,%d,%d,%d; cs=%d,%d} ",
+ mWritingMode.DebugString(),
+ IStart(), BStart(), ISize(), BSize(),
+ mContainerSize.width, mContainerSize.height);
+ }
+ if (mData &&
+ (!mData->mOverflowAreas.VisualOverflow().IsEqualEdges(bounds) ||
+ !mData->mOverflowAreas.ScrollableOverflow().IsEqualEdges(bounds))) {
+ str += nsPrintfCString("vis-overflow=%d,%d,%d,%d scr-overflow=%d,%d,%d,%d ",
+ mData->mOverflowAreas.VisualOverflow().x,
+ mData->mOverflowAreas.VisualOverflow().y,
+ mData->mOverflowAreas.VisualOverflow().width,
+ mData->mOverflowAreas.VisualOverflow().height,
+ mData->mOverflowAreas.ScrollableOverflow().x,
+ mData->mOverflowAreas.ScrollableOverflow().y,
+ mData->mOverflowAreas.ScrollableOverflow().width,
+ mData->mOverflowAreas.ScrollableOverflow().height);
+ }
+ fprintf_stderr(out, "%s<\n", str.get());
+
+ nsIFrame* frame = mFirstChild;
+ int32_t n = GetChildCount();
+ nsCString pfx(aPrefix);
+ pfx += " ";
+ while (--n >= 0) {
+ frame->List(out, pfx.get(), aFlags);
+ frame = frame->GetNextSibling();
+ }
+
+ if (HasFloats()) {
+ fprintf_stderr(out, "%s> floats <\n", aPrefix);
+ ListFloats(out, pfx.get(), mInlineData->mFloats);
+ }
+ fprintf_stderr(out, "%s>\n", aPrefix);
+}
+
+nsIFrame*
+nsLineBox::LastChild() const
+{
+ nsIFrame* frame = mFirstChild;
+ int32_t n = GetChildCount() - 1;
+ while (--n >= 0) {
+ frame = frame->GetNextSibling();
+ }
+ return frame;
+}
+#endif
+
+int32_t
+nsLineBox::IndexOf(nsIFrame* aFrame) const
+{
+ int32_t i, n = GetChildCount();
+ nsIFrame* frame = mFirstChild;
+ for (i = 0; i < n; i++) {
+ if (frame == aFrame) {
+ return i;
+ }
+ frame = frame->GetNextSibling();
+ }
+ return -1;
+}
+
+bool
+nsLineBox::IsEmpty() const
+{
+ if (IsBlock())
+ return mFirstChild->IsEmpty();
+
+ int32_t n;
+ nsIFrame *kid;
+ for (n = GetChildCount(), kid = mFirstChild;
+ n > 0;
+ --n, kid = kid->GetNextSibling())
+ {
+ if (!kid->IsEmpty())
+ return false;
+ }
+ if (HasBullet()) {
+ return false;
+ }
+ return true;
+}
+
+bool
+nsLineBox::CachedIsEmpty()
+{
+ if (mFlags.mDirty) {
+ return IsEmpty();
+ }
+
+ if (mFlags.mEmptyCacheValid) {
+ return mFlags.mEmptyCacheState;
+ }
+
+ bool result;
+ if (IsBlock()) {
+ result = mFirstChild->CachedIsEmpty();
+ } else {
+ int32_t n;
+ nsIFrame *kid;
+ result = true;
+ for (n = GetChildCount(), kid = mFirstChild;
+ n > 0;
+ --n, kid = kid->GetNextSibling())
+ {
+ if (!kid->CachedIsEmpty()) {
+ result = false;
+ break;
+ }
+ }
+ if (HasBullet()) {
+ result = false;
+ }
+ }
+
+ mFlags.mEmptyCacheValid = true;
+ mFlags.mEmptyCacheState = result;
+ return result;
+}
+
+void
+nsLineBox::DeleteLineList(nsPresContext* aPresContext, nsLineList& aLines,
+ nsIFrame* aDestructRoot, nsFrameList* aFrames)
+{
+ nsIPresShell* shell = aPresContext->PresShell();
+
+ // Keep our line list and frame list up to date as we
+ // remove frames, in case something wants to traverse the
+ // frame tree while we're destroying.
+ while (!aLines.empty()) {
+ nsLineBox* line = aLines.front();
+ if (MOZ_UNLIKELY(line->mFlags.mHasHashedFrames)) {
+ line->SwitchToCounter(); // Avoid expensive has table removals.
+ }
+ while (line->GetChildCount() > 0) {
+ nsIFrame* child = aFrames->RemoveFirstChild();
+ MOZ_ASSERT(child == line->mFirstChild, "Lines out of sync");
+ line->mFirstChild = aFrames->FirstChild();
+ line->NoteFrameRemoved(child);
+ child->DestroyFrom(aDestructRoot);
+ }
+
+ aLines.pop_front();
+ line->Destroy(shell);
+ }
+}
+
+bool
+nsLineBox::RFindLineContaining(nsIFrame* aFrame,
+ const nsLineList::iterator& aBegin,
+ nsLineList::iterator& aEnd,
+ nsIFrame* aLastFrameBeforeEnd,
+ int32_t* aFrameIndexInLine)
+{
+ NS_PRECONDITION(aFrame, "null ptr");
+
+ nsIFrame* curFrame = aLastFrameBeforeEnd;
+ while (aBegin != aEnd) {
+ --aEnd;
+ NS_ASSERTION(aEnd->LastChild() == curFrame, "Unexpected curFrame");
+ if (MOZ_UNLIKELY(aEnd->mFlags.mHasHashedFrames) &&
+ !aEnd->Contains(aFrame)) {
+ if (aEnd->mFirstChild) {
+ curFrame = aEnd->mFirstChild->GetPrevSibling();
+ }
+ continue;
+ }
+ // i is the index of curFrame in aEnd
+ int32_t i = aEnd->GetChildCount() - 1;
+ while (i >= 0) {
+ if (curFrame == aFrame) {
+ *aFrameIndexInLine = i;
+ return true;
+ }
+ --i;
+ curFrame = curFrame->GetPrevSibling();
+ }
+ MOZ_ASSERT(!aEnd->mFlags.mHasHashedFrames, "Contains lied to us!");
+ }
+ *aFrameIndexInLine = -1;
+ return false;
+}
+
+nsCollapsingMargin
+nsLineBox::GetCarriedOutBEndMargin() const
+{
+ NS_ASSERTION(IsBlock(),
+ "GetCarriedOutBEndMargin called on non-block line.");
+ return (IsBlock() && mBlockData)
+ ? mBlockData->mCarriedOutBEndMargin
+ : nsCollapsingMargin();
+}
+
+bool
+nsLineBox::SetCarriedOutBEndMargin(nsCollapsingMargin aValue)
+{
+ bool changed = false;
+ if (IsBlock()) {
+ if (!aValue.IsZero()) {
+ if (!mBlockData) {
+ mBlockData = new ExtraBlockData(GetPhysicalBounds());
+ }
+ changed = aValue != mBlockData->mCarriedOutBEndMargin;
+ mBlockData->mCarriedOutBEndMargin = aValue;
+ }
+ else if (mBlockData) {
+ changed = aValue != mBlockData->mCarriedOutBEndMargin;
+ mBlockData->mCarriedOutBEndMargin = aValue;
+ MaybeFreeData();
+ }
+ }
+ return changed;
+}
+
+void
+nsLineBox::MaybeFreeData()
+{
+ nsRect bounds = GetPhysicalBounds();
+ if (mData && mData->mOverflowAreas == nsOverflowAreas(bounds, bounds)) {
+ if (IsInline()) {
+ if (mInlineData->mFloats.IsEmpty()) {
+ delete mInlineData;
+ mInlineData = nullptr;
+ }
+ }
+ else if (mBlockData->mCarriedOutBEndMargin.IsZero()) {
+ delete mBlockData;
+ mBlockData = nullptr;
+ }
+ }
+}
+
+// XXX get rid of this???
+nsFloatCache*
+nsLineBox::GetFirstFloat()
+{
+ MOZ_ASSERT(IsInline(), "block line can't have floats");
+ return mInlineData ? mInlineData->mFloats.Head() : nullptr;
+}
+
+// XXX this might be too eager to free memory
+void
+nsLineBox::FreeFloats(nsFloatCacheFreeList& aFreeList)
+{
+ MOZ_ASSERT(IsInline(), "block line can't have floats");
+ if (IsInline() && mInlineData) {
+ if (mInlineData->mFloats.NotEmpty()) {
+ aFreeList.Append(mInlineData->mFloats);
+ }
+ MaybeFreeData();
+ }
+}
+
+void
+nsLineBox::AppendFloats(nsFloatCacheFreeList& aFreeList)
+{
+ MOZ_ASSERT(IsInline(), "block line can't have floats");
+ if (IsInline()) {
+ if (aFreeList.NotEmpty()) {
+ if (!mInlineData) {
+ mInlineData = new ExtraInlineData(GetPhysicalBounds());
+ }
+ mInlineData->mFloats.Append(aFreeList);
+ }
+ }
+}
+
+bool
+nsLineBox::RemoveFloat(nsIFrame* aFrame)
+{
+ MOZ_ASSERT(IsInline(), "block line can't have floats");
+ if (IsInline() && mInlineData) {
+ nsFloatCache* fc = mInlineData->mFloats.Find(aFrame);
+ if (fc) {
+ // Note: the placeholder is part of the line's child list
+ // and will be removed later.
+ mInlineData->mFloats.Remove(fc);
+ delete fc;
+ MaybeFreeData();
+ return true;
+ }
+ }
+ return false;
+}
+
+void
+nsLineBox::SetOverflowAreas(const nsOverflowAreas& aOverflowAreas)
+{
+ NS_FOR_FRAME_OVERFLOW_TYPES(otype) {
+ NS_ASSERTION(aOverflowAreas.Overflow(otype).width >= 0,
+ "illegal width for combined area");
+ NS_ASSERTION(aOverflowAreas.Overflow(otype).height >= 0,
+ "illegal height for combined area");
+ }
+ nsRect bounds = GetPhysicalBounds();
+ if (!aOverflowAreas.VisualOverflow().IsEqualInterior(bounds) ||
+ !aOverflowAreas.ScrollableOverflow().IsEqualEdges(bounds)) {
+ if (!mData) {
+ if (IsInline()) {
+ mInlineData = new ExtraInlineData(bounds);
+ }
+ else {
+ mBlockData = new ExtraBlockData(bounds);
+ }
+ }
+ mData->mOverflowAreas = aOverflowAreas;
+ }
+ else if (mData) {
+ // Store away new value so that MaybeFreeData compares against
+ // the right value.
+ mData->mOverflowAreas = aOverflowAreas;
+ MaybeFreeData();
+ }
+}
+
+//----------------------------------------------------------------------
+
+
+static nsLineBox* gDummyLines[1];
+
+nsLineIterator::nsLineIterator()
+{
+ mLines = gDummyLines;
+ mNumLines = 0;
+ mIndex = 0;
+ mRightToLeft = false;
+}
+
+nsLineIterator::~nsLineIterator()
+{
+ if (mLines != gDummyLines) {
+ delete [] mLines;
+ }
+}
+
+/* virtual */ void
+nsLineIterator::DisposeLineIterator()
+{
+ delete this;
+}
+
+nsresult
+nsLineIterator::Init(nsLineList& aLines, bool aRightToLeft)
+{
+ mRightToLeft = aRightToLeft;
+
+ // Count the lines
+ int32_t numLines = aLines.size();
+ if (0 == numLines) {
+ // Use gDummyLines so that we don't need null pointer checks in
+ // the accessor methods
+ mLines = gDummyLines;
+ return NS_OK;
+ }
+
+ // Make a linear array of the lines
+ mLines = new nsLineBox*[numLines];
+ if (!mLines) {
+ // Use gDummyLines so that we don't need null pointer checks in
+ // the accessor methods
+ mLines = gDummyLines;
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+ nsLineBox** lp = mLines;
+ for (nsLineList::iterator line = aLines.begin(), line_end = aLines.end() ;
+ line != line_end;
+ ++line)
+ {
+ *lp++ = line;
+ }
+ mNumLines = numLines;
+ return NS_OK;
+}
+
+int32_t
+nsLineIterator::GetNumLines()
+{
+ return mNumLines;
+}
+
+bool
+nsLineIterator::GetDirection()
+{
+ return mRightToLeft;
+}
+
+NS_IMETHODIMP
+nsLineIterator::GetLine(int32_t aLineNumber,
+ nsIFrame** aFirstFrameOnLine,
+ int32_t* aNumFramesOnLine,
+ nsRect& aLineBounds)
+{
+ NS_ENSURE_ARG_POINTER(aFirstFrameOnLine);
+ NS_ENSURE_ARG_POINTER(aNumFramesOnLine);
+
+ if ((aLineNumber < 0) || (aLineNumber >= mNumLines)) {
+ *aFirstFrameOnLine = nullptr;
+ *aNumFramesOnLine = 0;
+ aLineBounds.SetRect(0, 0, 0, 0);
+ return NS_OK;
+ }
+ nsLineBox* line = mLines[aLineNumber];
+ *aFirstFrameOnLine = line->mFirstChild;
+ *aNumFramesOnLine = line->GetChildCount();
+ aLineBounds = line->GetPhysicalBounds();
+
+ return NS_OK;
+}
+
+int32_t
+nsLineIterator::FindLineContaining(nsIFrame* aFrame, int32_t aStartLine)
+{
+ NS_PRECONDITION(aStartLine <= mNumLines, "Bogus line numbers");
+ int32_t lineNumber = aStartLine;
+ while (lineNumber != mNumLines) {
+ nsLineBox* line = mLines[lineNumber];
+ if (line->Contains(aFrame)) {
+ return lineNumber;
+ }
+ ++lineNumber;
+ }
+ return -1;
+}
+
+NS_IMETHODIMP
+nsLineIterator::CheckLineOrder(int32_t aLine,
+ bool *aIsReordered,
+ nsIFrame **aFirstVisual,
+ nsIFrame **aLastVisual)
+{
+ NS_ASSERTION (aLine >= 0 && aLine < mNumLines, "aLine out of range!");
+ nsLineBox* line = mLines[aLine];
+
+ if (!line->mFirstChild) { // empty line
+ *aIsReordered = false;
+ *aFirstVisual = nullptr;
+ *aLastVisual = nullptr;
+ return NS_OK;
+ }
+
+ nsIFrame* leftmostFrame;
+ nsIFrame* rightmostFrame;
+ *aIsReordered = nsBidiPresUtils::CheckLineOrder(line->mFirstChild, line->GetChildCount(), &leftmostFrame, &rightmostFrame);
+
+ // map leftmost/rightmost to first/last according to paragraph direction
+ *aFirstVisual = mRightToLeft ? rightmostFrame : leftmostFrame;
+ *aLastVisual = mRightToLeft ? leftmostFrame : rightmostFrame;
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsLineIterator::FindFrameAt(int32_t aLineNumber,
+ nsPoint aPos,
+ nsIFrame** aFrameFound,
+ bool* aPosIsBeforeFirstFrame,
+ bool* aPosIsAfterLastFrame)
+{
+ NS_PRECONDITION(aFrameFound && aPosIsBeforeFirstFrame && aPosIsAfterLastFrame,
+ "null OUT ptr");
+ if (!aFrameFound || !aPosIsBeforeFirstFrame || !aPosIsAfterLastFrame) {
+ return NS_ERROR_NULL_POINTER;
+ }
+ if ((aLineNumber < 0) || (aLineNumber >= mNumLines)) {
+ return NS_ERROR_INVALID_ARG;
+ }
+
+ nsLineBox* line = mLines[aLineNumber];
+ if (!line) {
+ *aFrameFound = nullptr;
+ *aPosIsBeforeFirstFrame = true;
+ *aPosIsAfterLastFrame = false;
+ return NS_OK;
+ }
+
+ if (line->ISize() == 0 && line->BSize() == 0)
+ return NS_ERROR_FAILURE;
+
+ nsIFrame* frame = line->mFirstChild;
+ nsIFrame* closestFromStart = nullptr;
+ nsIFrame* closestFromEnd = nullptr;
+
+ WritingMode wm = line->mWritingMode;
+ nsSize containerSize = line->mContainerSize;
+
+ LogicalPoint pos(wm, aPos, containerSize);
+
+ int32_t n = line->GetChildCount();
+ while (n--) {
+ LogicalRect rect = frame->GetLogicalRect(wm, containerSize);
+ if (rect.ISize(wm) > 0) {
+ // If pos.I() is inside this frame - this is it
+ if (rect.IStart(wm) <= pos.I(wm) && rect.IEnd(wm) > pos.I(wm)) {
+ closestFromStart = closestFromEnd = frame;
+ break;
+ }
+ if (rect.IStart(wm) < pos.I(wm)) {
+ if (!closestFromStart ||
+ rect.IEnd(wm) > closestFromStart->
+ GetLogicalRect(wm, containerSize).IEnd(wm))
+ closestFromStart = frame;
+ }
+ else {
+ if (!closestFromEnd ||
+ rect.IStart(wm) < closestFromEnd->
+ GetLogicalRect(wm, containerSize).IStart(wm))
+ closestFromEnd = frame;
+ }
+ }
+ frame = frame->GetNextSibling();
+ }
+ if (!closestFromStart && !closestFromEnd) {
+ // All frames were zero-width. Just take the first one.
+ closestFromStart = closestFromEnd = line->mFirstChild;
+ }
+ *aPosIsBeforeFirstFrame = mRightToLeft ? !closestFromEnd : !closestFromStart;
+ *aPosIsAfterLastFrame = mRightToLeft ? !closestFromStart : !closestFromEnd;
+ if (closestFromStart == closestFromEnd) {
+ *aFrameFound = closestFromStart;
+ }
+ else if (!closestFromStart) {
+ *aFrameFound = closestFromEnd;
+ }
+ else if (!closestFromEnd) {
+ *aFrameFound = closestFromStart;
+ }
+ else { // we're between two frames
+ nscoord delta =
+ closestFromEnd->GetLogicalRect(wm, containerSize).IStart(wm) -
+ closestFromStart->GetLogicalRect(wm, containerSize).IEnd(wm);
+ if (pos.I(wm) < closestFromStart->
+ GetLogicalRect(wm, containerSize).IEnd(wm) + delta/2) {
+ *aFrameFound = closestFromStart;
+ } else {
+ *aFrameFound = closestFromEnd;
+ }
+ }
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsLineIterator::GetNextSiblingOnLine(nsIFrame*& aFrame, int32_t aLineNumber)
+{
+ aFrame = aFrame->GetNextSibling();
+ return NS_OK;
+}
+
+//----------------------------------------------------------------------
+
+#ifdef NS_BUILD_REFCNT_LOGGING
+nsFloatCacheList::nsFloatCacheList() :
+ mHead(nullptr)
+{
+ MOZ_COUNT_CTOR(nsFloatCacheList);
+}
+#endif
+
+nsFloatCacheList::~nsFloatCacheList()
+{
+ DeleteAll();
+ MOZ_COUNT_DTOR(nsFloatCacheList);
+}
+
+void
+nsFloatCacheList::DeleteAll()
+{
+ nsFloatCache* c = mHead;
+ while (c) {
+ nsFloatCache* next = c->Next();
+ delete c;
+ c = next;
+ }
+ mHead = nullptr;
+}
+
+nsFloatCache*
+nsFloatCacheList::Tail() const
+{
+ nsFloatCache* fc = mHead;
+ while (fc) {
+ if (!fc->mNext) {
+ break;
+ }
+ fc = fc->mNext;
+ }
+ return fc;
+}
+
+void
+nsFloatCacheList::Append(nsFloatCacheFreeList& aList)
+{
+ NS_PRECONDITION(aList.NotEmpty(), "Appending empty list will fail");
+
+ nsFloatCache* tail = Tail();
+ if (tail) {
+ NS_ASSERTION(!tail->mNext, "Bogus!");
+ tail->mNext = aList.mHead;
+ }
+ else {
+ NS_ASSERTION(!mHead, "Bogus!");
+ mHead = aList.mHead;
+ }
+ aList.mHead = nullptr;
+ aList.mTail = nullptr;
+}
+
+nsFloatCache*
+nsFloatCacheList::Find(nsIFrame* aOutOfFlowFrame)
+{
+ nsFloatCache* fc = mHead;
+ while (fc) {
+ if (fc->mFloat == aOutOfFlowFrame) {
+ break;
+ }
+ fc = fc->Next();
+ }
+ return fc;
+}
+
+nsFloatCache*
+nsFloatCacheList::RemoveAndReturnPrev(nsFloatCache* aElement)
+{
+ nsFloatCache* fc = mHead;
+ nsFloatCache* prev = nullptr;
+ while (fc) {
+ if (fc == aElement) {
+ if (prev) {
+ prev->mNext = fc->mNext;
+ } else {
+ mHead = fc->mNext;
+ }
+ return prev;
+ }
+ prev = fc;
+ fc = fc->mNext;
+ }
+ return nullptr;
+}
+
+//----------------------------------------------------------------------
+
+#ifdef NS_BUILD_REFCNT_LOGGING
+nsFloatCacheFreeList::nsFloatCacheFreeList() :
+ mTail(nullptr)
+{
+ MOZ_COUNT_CTOR(nsFloatCacheFreeList);
+}
+
+nsFloatCacheFreeList::~nsFloatCacheFreeList()
+{
+ MOZ_COUNT_DTOR(nsFloatCacheFreeList);
+}
+#endif
+
+void
+nsFloatCacheFreeList::Append(nsFloatCacheList& aList)
+{
+ NS_PRECONDITION(aList.NotEmpty(), "Appending empty list will fail");
+
+ if (mTail) {
+ NS_ASSERTION(!mTail->mNext, "Bogus");
+ mTail->mNext = aList.mHead;
+ }
+ else {
+ NS_ASSERTION(!mHead, "Bogus");
+ mHead = aList.mHead;
+ }
+ mTail = aList.Tail();
+ aList.mHead = nullptr;
+}
+
+void
+nsFloatCacheFreeList::Remove(nsFloatCache* aElement)
+{
+ nsFloatCache* prev = nsFloatCacheList::RemoveAndReturnPrev(aElement);
+ if (mTail == aElement) {
+ mTail = prev;
+ }
+}
+
+void
+nsFloatCacheFreeList::DeleteAll()
+{
+ nsFloatCacheList::DeleteAll();
+ mTail = nullptr;
+}
+
+nsFloatCache*
+nsFloatCacheFreeList::Alloc(nsIFrame* aFloat)
+{
+ NS_PRECONDITION(aFloat->GetStateBits() & NS_FRAME_OUT_OF_FLOW,
+ "This is a float cache, why isn't the frame out-of-flow?");
+ nsFloatCache* fc = mHead;
+ if (mHead) {
+ if (mHead == mTail) {
+ mHead = mTail = nullptr;
+ }
+ else {
+ mHead = fc->mNext;
+ }
+ fc->mNext = nullptr;
+ }
+ else {
+ fc = new nsFloatCache();
+ }
+ fc->mFloat = aFloat;
+ return fc;
+}
+
+void
+nsFloatCacheFreeList::Append(nsFloatCache* aFloat)
+{
+ NS_ASSERTION(!aFloat->mNext, "Bogus!");
+ aFloat->mNext = nullptr;
+ if (mTail) {
+ NS_ASSERTION(!mTail->mNext, "Bogus!");
+ mTail->mNext = aFloat;
+ mTail = aFloat;
+ }
+ else {
+ NS_ASSERTION(!mHead, "Bogus!");
+ mHead = mTail = aFloat;
+ }
+}
+
+//----------------------------------------------------------------------
+
+nsFloatCache::nsFloatCache()
+ : mFloat(nullptr),
+ mNext(nullptr)
+{
+ MOZ_COUNT_CTOR(nsFloatCache);
+}
+
+#ifdef NS_BUILD_REFCNT_LOGGING
+nsFloatCache::~nsFloatCache()
+{
+ MOZ_COUNT_DTOR(nsFloatCache);
+}
+#endif