diff options
author | Matt A. Tobin <mattatobin@localhost.localdomain> | 2018-02-02 04:16:08 -0500 |
---|---|---|
committer | Matt A. Tobin <mattatobin@localhost.localdomain> | 2018-02-02 04:16:08 -0500 |
commit | 5f8de423f190bbb79a62f804151bc24824fa32d8 (patch) | |
tree | 10027f336435511475e392454359edea8e25895d /layout/generic/nsLineBox.cpp | |
parent | 49ee0794b5d912db1f95dce6eb52d781dc210db5 (diff) | |
download | UXP-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.cpp | 989 |
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 |