michael@0: /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ michael@0: // vim:cindent:ts=2:et:sw=2: michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: /* representation of one line within a block frame, a CSS line box */ michael@0: michael@0: #include "nsLineBox.h" michael@0: #include "prprf.h" michael@0: #include "nsFrame.h" michael@0: #include "nsPresArena.h" michael@0: #include "nsBidiPresUtils.h" michael@0: #include "nsIFrameInlines.h" michael@0: #include "WritingModes.h" michael@0: #include "mozilla/Assertions.h" michael@0: #include "mozilla/Likely.h" michael@0: #include "nsPrintfCString.h" michael@0: michael@0: #ifdef DEBUG michael@0: static int32_t ctorCount; michael@0: int32_t nsLineBox::GetCtorCount() { return ctorCount; } michael@0: #endif michael@0: michael@0: #ifndef _MSC_VER michael@0: // static nsLineBox constant; initialized in the header file. michael@0: const uint32_t nsLineBox::kMinChildCountForHashtable; michael@0: #endif michael@0: michael@0: using namespace mozilla; michael@0: michael@0: nsLineBox::nsLineBox(nsIFrame* aFrame, int32_t aCount, bool aIsBlock) michael@0: : mFirstChild(aFrame) michael@0: , mContainerWidth(-1) michael@0: , mBounds(WritingMode()) // mBounds will be initialized with the correct michael@0: // writing mode when it is set michael@0: // NOTE: memory is already zeroed since we allocate with AllocateByObjectID. michael@0: { michael@0: MOZ_COUNT_CTOR(nsLineBox); michael@0: #ifdef DEBUG michael@0: ++ctorCount; michael@0: NS_ASSERTION(!aIsBlock || aCount == 1, "Blocks must have exactly one child"); michael@0: nsIFrame* f = aFrame; michael@0: for (int32_t n = aCount; n > 0; f = f->GetNextSibling(), --n) { michael@0: NS_ASSERTION(aIsBlock == f->IsBlockOutside(), michael@0: "wrong kind of child frame"); michael@0: } michael@0: #endif michael@0: michael@0: static_assert(NS_STYLE_CLEAR_MAX <= 15, michael@0: "FlagBits needs more bits to store the full range of " michael@0: "break type ('clear') values"); michael@0: #if NS_STYLE_CLEAR_NONE > 0 michael@0: mFlags.mBreakType = NS_STYLE_CLEAR_NONE; michael@0: #endif michael@0: mChildCount = aCount; michael@0: MarkDirty(); michael@0: mFlags.mBlock = aIsBlock; michael@0: } michael@0: michael@0: nsLineBox::~nsLineBox() michael@0: { michael@0: MOZ_COUNT_DTOR(nsLineBox); michael@0: if (MOZ_UNLIKELY(mFlags.mHasHashedFrames)) { michael@0: delete mFrames; michael@0: } michael@0: Cleanup(); michael@0: } michael@0: michael@0: nsLineBox* michael@0: NS_NewLineBox(nsIPresShell* aPresShell, nsIFrame* aFrame, bool aIsBlock) michael@0: { michael@0: return new (aPresShell) nsLineBox(aFrame, 1, aIsBlock); michael@0: } michael@0: michael@0: nsLineBox* michael@0: NS_NewLineBox(nsIPresShell* aPresShell, nsLineBox* aFromLine, michael@0: nsIFrame* aFrame, int32_t aCount) michael@0: { michael@0: nsLineBox* newLine = new (aPresShell) nsLineBox(aFrame, aCount, false); michael@0: newLine->NoteFramesMovedFrom(aFromLine); michael@0: newLine->mContainerWidth = aFromLine->mContainerWidth; michael@0: return newLine; michael@0: } michael@0: michael@0: void michael@0: nsLineBox::StealHashTableFrom(nsLineBox* aFromLine, uint32_t aFromLineNewCount) michael@0: { michael@0: MOZ_ASSERT(!mFlags.mHasHashedFrames); michael@0: MOZ_ASSERT(GetChildCount() >= int32_t(aFromLineNewCount)); michael@0: mFrames = aFromLine->mFrames; michael@0: mFlags.mHasHashedFrames = 1; michael@0: aFromLine->mFlags.mHasHashedFrames = 0; michael@0: aFromLine->mChildCount = aFromLineNewCount; michael@0: // remove aFromLine's frames that aren't on this line michael@0: nsIFrame* f = aFromLine->mFirstChild; michael@0: for (uint32_t i = 0; i < aFromLineNewCount; f = f->GetNextSibling(), ++i) { michael@0: mFrames->RemoveEntry(f); michael@0: } michael@0: } michael@0: michael@0: void michael@0: nsLineBox::NoteFramesMovedFrom(nsLineBox* aFromLine) michael@0: { michael@0: uint32_t fromCount = aFromLine->GetChildCount(); michael@0: uint32_t toCount = GetChildCount(); michael@0: MOZ_ASSERT(toCount <= fromCount, "moved more frames than aFromLine has"); michael@0: uint32_t fromNewCount = fromCount - toCount; michael@0: if (MOZ_LIKELY(!aFromLine->mFlags.mHasHashedFrames)) { michael@0: aFromLine->mChildCount = fromNewCount; michael@0: MOZ_ASSERT(toCount < kMinChildCountForHashtable); michael@0: } else if (fromNewCount < kMinChildCountForHashtable) { michael@0: // aFromLine has a hash table but will not have it after moving the frames michael@0: // so this line can steal the hash table if it needs it. michael@0: if (toCount >= kMinChildCountForHashtable) { michael@0: StealHashTableFrom(aFromLine, fromNewCount); michael@0: } else { michael@0: delete aFromLine->mFrames; michael@0: aFromLine->mFlags.mHasHashedFrames = 0; michael@0: aFromLine->mChildCount = fromNewCount; michael@0: } michael@0: } else { michael@0: // aFromLine still needs a hash table. michael@0: if (toCount < kMinChildCountForHashtable) { michael@0: // remove the moved frames from it michael@0: nsIFrame* f = mFirstChild; michael@0: for (uint32_t i = 0; i < toCount; f = f->GetNextSibling(), ++i) { michael@0: aFromLine->mFrames->RemoveEntry(f); michael@0: } michael@0: } else if (toCount <= fromNewCount) { michael@0: // This line needs a hash table, allocate a hash table for it since that michael@0: // means fewer hash ops. michael@0: nsIFrame* f = mFirstChild; michael@0: for (uint32_t i = 0; i < toCount; f = f->GetNextSibling(), ++i) { michael@0: aFromLine->mFrames->RemoveEntry(f); // toCount RemoveEntry michael@0: } michael@0: SwitchToHashtable(); // toCount PutEntry michael@0: } else { michael@0: // This line needs a hash table, but it's fewer hash ops to steal michael@0: // aFromLine's hash table and allocate a new hash table for that line. michael@0: StealHashTableFrom(aFromLine, fromNewCount); // fromNewCount RemoveEntry michael@0: aFromLine->SwitchToHashtable(); // fromNewCount PutEntry michael@0: } michael@0: } michael@0: } michael@0: michael@0: // Overloaded new operator. Uses an arena (which comes from the presShell) michael@0: // to perform the allocation. michael@0: void* michael@0: nsLineBox::operator new(size_t sz, nsIPresShell* aPresShell) CPP_THROW_NEW michael@0: { michael@0: return aPresShell->AllocateByObjectID(nsPresArena::nsLineBox_id, sz); michael@0: } michael@0: michael@0: void michael@0: nsLineBox::Destroy(nsIPresShell* aPresShell) michael@0: { michael@0: this->nsLineBox::~nsLineBox(); michael@0: aPresShell->FreeByObjectID(nsPresArena::nsLineBox_id, this); michael@0: } michael@0: michael@0: void michael@0: nsLineBox::Cleanup() michael@0: { michael@0: if (mData) { michael@0: if (IsBlock()) { michael@0: delete mBlockData; michael@0: } michael@0: else { michael@0: delete mInlineData; michael@0: } michael@0: mData = nullptr; michael@0: } michael@0: } michael@0: michael@0: #ifdef DEBUG_FRAME_DUMP michael@0: static void michael@0: ListFloats(FILE* out, const char* aPrefix, const nsFloatCacheList& aFloats) michael@0: { michael@0: nsFloatCache* fc = aFloats.Head(); michael@0: while (fc) { michael@0: nsCString str(aPrefix); michael@0: nsIFrame* frame = fc->mFloat; michael@0: str += nsPrintfCString("floatframe@%p ", static_cast(frame)); michael@0: if (frame) { michael@0: nsAutoString frameName; michael@0: frame->GetFrameName(frameName); michael@0: str += NS_ConvertUTF16toUTF8(frameName).get(); michael@0: } michael@0: else { michael@0: str += "\n###!!! NULL out-of-flow frame"; michael@0: } michael@0: fprintf_stderr(out, "%s\n", str.get()); michael@0: fc = fc->Next(); michael@0: } michael@0: } michael@0: michael@0: const char * michael@0: BreakTypeToString(uint8_t aBreakType) michael@0: { michael@0: switch (aBreakType) { michael@0: case NS_STYLE_CLEAR_NONE: return "nobr"; michael@0: case NS_STYLE_CLEAR_LEFT: return "leftbr"; michael@0: case NS_STYLE_CLEAR_RIGHT: return "rightbr"; michael@0: case NS_STYLE_CLEAR_BOTH: return "leftbr+rightbr"; michael@0: case NS_STYLE_CLEAR_LINE: return "linebr"; michael@0: default: michael@0: break; michael@0: } michael@0: return "unknown"; michael@0: } michael@0: michael@0: char* michael@0: nsLineBox::StateToString(char* aBuf, int32_t aBufSize) const michael@0: { michael@0: PR_snprintf(aBuf, aBufSize, "%s,%s,%s,%s,%s,before:%s,after:%s[0x%x]", michael@0: IsBlock() ? "block" : "inline", michael@0: IsDirty() ? "dirty" : "clean", michael@0: IsPreviousMarginDirty() ? "prevmargindirty" : "prevmarginclean", michael@0: IsImpactedByFloat() ? "impacted" : "not impacted", michael@0: IsLineWrapped() ? "wrapped" : "not wrapped", michael@0: BreakTypeToString(GetBreakTypeBefore()), michael@0: BreakTypeToString(GetBreakTypeAfter()), michael@0: mAllFlags); michael@0: return aBuf; michael@0: } michael@0: michael@0: void michael@0: nsLineBox::List(FILE* out, int32_t aIndent, uint32_t aFlags) const michael@0: { michael@0: nsCString str; michael@0: while (aIndent-- > 0) { michael@0: str += " "; michael@0: } michael@0: List(out, str.get(), aFlags); michael@0: } michael@0: michael@0: void michael@0: nsLineBox::List(FILE* out, const char* aPrefix, uint32_t aFlags) const michael@0: { michael@0: nsCString str(aPrefix); michael@0: char cbuf[100]; michael@0: str += nsPrintfCString("line %p: count=%d state=%s ", michael@0: static_cast(this), GetChildCount(), michael@0: StateToString(cbuf, sizeof(cbuf))); michael@0: if (IsBlock() && !GetCarriedOutBottomMargin().IsZero()) { michael@0: str += nsPrintfCString("bm=%d ", GetCarriedOutBottomMargin().get()); michael@0: } michael@0: nsRect bounds = GetPhysicalBounds(); michael@0: str += nsPrintfCString("{%d,%d,%d,%d} ", michael@0: bounds.x, bounds.y, bounds.width, bounds.height); michael@0: if (mData && michael@0: (!mData->mOverflowAreas.VisualOverflow().IsEqualEdges(bounds) || michael@0: !mData->mOverflowAreas.ScrollableOverflow().IsEqualEdges(bounds))) { michael@0: str += nsPrintfCString("vis-overflow=%d,%d,%d,%d scr-overflow=%d,%d,%d,%d ", michael@0: mData->mOverflowAreas.VisualOverflow().x, michael@0: mData->mOverflowAreas.VisualOverflow().y, michael@0: mData->mOverflowAreas.VisualOverflow().width, michael@0: mData->mOverflowAreas.VisualOverflow().height, michael@0: mData->mOverflowAreas.ScrollableOverflow().x, michael@0: mData->mOverflowAreas.ScrollableOverflow().y, michael@0: mData->mOverflowAreas.ScrollableOverflow().width, michael@0: mData->mOverflowAreas.ScrollableOverflow().height); michael@0: } michael@0: fprintf_stderr(out, "%s<\n", str.get()); michael@0: michael@0: nsIFrame* frame = mFirstChild; michael@0: int32_t n = GetChildCount(); michael@0: nsCString pfx(aPrefix); michael@0: pfx += " "; michael@0: while (--n >= 0) { michael@0: frame->List(out, pfx.get(), aFlags); michael@0: frame = frame->GetNextSibling(); michael@0: } michael@0: michael@0: if (HasFloats()) { michael@0: fprintf_stderr(out, "%s> floats <\n", aPrefix); michael@0: ListFloats(out, pfx.get(), mInlineData->mFloats); michael@0: } michael@0: fprintf_stderr(out, "%s>\n", aPrefix); michael@0: } michael@0: #endif michael@0: michael@0: #ifdef DEBUG michael@0: nsIFrame* michael@0: nsLineBox::LastChild() const michael@0: { michael@0: nsIFrame* frame = mFirstChild; michael@0: int32_t n = GetChildCount() - 1; michael@0: while (--n >= 0) { michael@0: frame = frame->GetNextSibling(); michael@0: } michael@0: return frame; michael@0: } michael@0: #endif michael@0: michael@0: int32_t michael@0: nsLineBox::IndexOf(nsIFrame* aFrame) const michael@0: { michael@0: int32_t i, n = GetChildCount(); michael@0: nsIFrame* frame = mFirstChild; michael@0: for (i = 0; i < n; i++) { michael@0: if (frame == aFrame) { michael@0: return i; michael@0: } michael@0: frame = frame->GetNextSibling(); michael@0: } michael@0: return -1; michael@0: } michael@0: michael@0: bool michael@0: nsLineBox::IsEmpty() const michael@0: { michael@0: if (IsBlock()) michael@0: return mFirstChild->IsEmpty(); michael@0: michael@0: int32_t n; michael@0: nsIFrame *kid; michael@0: for (n = GetChildCount(), kid = mFirstChild; michael@0: n > 0; michael@0: --n, kid = kid->GetNextSibling()) michael@0: { michael@0: if (!kid->IsEmpty()) michael@0: return false; michael@0: } michael@0: if (HasBullet()) { michael@0: return false; michael@0: } michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: nsLineBox::CachedIsEmpty() michael@0: { michael@0: if (mFlags.mDirty) { michael@0: return IsEmpty(); michael@0: } michael@0: michael@0: if (mFlags.mEmptyCacheValid) { michael@0: return mFlags.mEmptyCacheState; michael@0: } michael@0: michael@0: bool result; michael@0: if (IsBlock()) { michael@0: result = mFirstChild->CachedIsEmpty(); michael@0: } else { michael@0: int32_t n; michael@0: nsIFrame *kid; michael@0: result = true; michael@0: for (n = GetChildCount(), kid = mFirstChild; michael@0: n > 0; michael@0: --n, kid = kid->GetNextSibling()) michael@0: { michael@0: if (!kid->CachedIsEmpty()) { michael@0: result = false; michael@0: break; michael@0: } michael@0: } michael@0: if (HasBullet()) { michael@0: result = false; michael@0: } michael@0: } michael@0: michael@0: mFlags.mEmptyCacheValid = true; michael@0: mFlags.mEmptyCacheState = result; michael@0: return result; michael@0: } michael@0: michael@0: void michael@0: nsLineBox::DeleteLineList(nsPresContext* aPresContext, nsLineList& aLines, michael@0: nsIFrame* aDestructRoot, nsFrameList* aFrames) michael@0: { michael@0: nsIPresShell* shell = aPresContext->PresShell(); michael@0: michael@0: // Keep our line list and frame list up to date as we michael@0: // remove frames, in case something wants to traverse the michael@0: // frame tree while we're destroying. michael@0: while (!aLines.empty()) { michael@0: nsLineBox* line = aLines.front(); michael@0: if (MOZ_UNLIKELY(line->mFlags.mHasHashedFrames)) { michael@0: line->SwitchToCounter(); // Avoid expensive has table removals. michael@0: } michael@0: while (line->GetChildCount() > 0) { michael@0: nsIFrame* child = aFrames->RemoveFirstChild(); michael@0: MOZ_ASSERT(child == line->mFirstChild, "Lines out of sync"); michael@0: line->mFirstChild = aFrames->FirstChild(); michael@0: line->NoteFrameRemoved(child); michael@0: child->DestroyFrom(aDestructRoot); michael@0: } michael@0: michael@0: aLines.pop_front(); michael@0: line->Destroy(shell); michael@0: } michael@0: } michael@0: michael@0: bool michael@0: nsLineBox::RFindLineContaining(nsIFrame* aFrame, michael@0: const nsLineList::iterator& aBegin, michael@0: nsLineList::iterator& aEnd, michael@0: nsIFrame* aLastFrameBeforeEnd, michael@0: int32_t* aFrameIndexInLine) michael@0: { michael@0: NS_PRECONDITION(aFrame, "null ptr"); michael@0: michael@0: nsIFrame* curFrame = aLastFrameBeforeEnd; michael@0: while (aBegin != aEnd) { michael@0: --aEnd; michael@0: NS_ASSERTION(aEnd->LastChild() == curFrame, "Unexpected curFrame"); michael@0: if (MOZ_UNLIKELY(aEnd->mFlags.mHasHashedFrames) && michael@0: !aEnd->Contains(aFrame)) { michael@0: if (aEnd->mFirstChild) { michael@0: curFrame = aEnd->mFirstChild->GetPrevSibling(); michael@0: } michael@0: continue; michael@0: } michael@0: // i is the index of curFrame in aEnd michael@0: int32_t i = aEnd->GetChildCount() - 1; michael@0: while (i >= 0) { michael@0: if (curFrame == aFrame) { michael@0: *aFrameIndexInLine = i; michael@0: return true; michael@0: } michael@0: --i; michael@0: curFrame = curFrame->GetPrevSibling(); michael@0: } michael@0: MOZ_ASSERT(!aEnd->mFlags.mHasHashedFrames, "Contains lied to us!"); michael@0: } michael@0: *aFrameIndexInLine = -1; michael@0: return false; michael@0: } michael@0: michael@0: nsCollapsingMargin michael@0: nsLineBox::GetCarriedOutBottomMargin() const michael@0: { michael@0: NS_ASSERTION(IsBlock(), michael@0: "GetCarriedOutBottomMargin called on non-block line."); michael@0: return (IsBlock() && mBlockData) michael@0: ? mBlockData->mCarriedOutBottomMargin michael@0: : nsCollapsingMargin(); michael@0: } michael@0: michael@0: bool michael@0: nsLineBox::SetCarriedOutBottomMargin(nsCollapsingMargin aValue) michael@0: { michael@0: bool changed = false; michael@0: if (IsBlock()) { michael@0: if (!aValue.IsZero()) { michael@0: if (!mBlockData) { michael@0: mBlockData = new ExtraBlockData(GetPhysicalBounds()); michael@0: } michael@0: changed = aValue != mBlockData->mCarriedOutBottomMargin; michael@0: mBlockData->mCarriedOutBottomMargin = aValue; michael@0: } michael@0: else if (mBlockData) { michael@0: changed = aValue != mBlockData->mCarriedOutBottomMargin; michael@0: mBlockData->mCarriedOutBottomMargin = aValue; michael@0: MaybeFreeData(); michael@0: } michael@0: } michael@0: return changed; michael@0: } michael@0: michael@0: void michael@0: nsLineBox::MaybeFreeData() michael@0: { michael@0: nsRect bounds = GetPhysicalBounds(); michael@0: if (mData && mData->mOverflowAreas == nsOverflowAreas(bounds, bounds)) { michael@0: if (IsInline()) { michael@0: if (mInlineData->mFloats.IsEmpty()) { michael@0: delete mInlineData; michael@0: mInlineData = nullptr; michael@0: } michael@0: } michael@0: else if (mBlockData->mCarriedOutBottomMargin.IsZero()) { michael@0: delete mBlockData; michael@0: mBlockData = nullptr; michael@0: } michael@0: } michael@0: } michael@0: michael@0: // XXX get rid of this??? michael@0: nsFloatCache* michael@0: nsLineBox::GetFirstFloat() michael@0: { michael@0: NS_ABORT_IF_FALSE(IsInline(), "block line can't have floats"); michael@0: return mInlineData ? mInlineData->mFloats.Head() : nullptr; michael@0: } michael@0: michael@0: // XXX this might be too eager to free memory michael@0: void michael@0: nsLineBox::FreeFloats(nsFloatCacheFreeList& aFreeList) michael@0: { michael@0: NS_ABORT_IF_FALSE(IsInline(), "block line can't have floats"); michael@0: if (IsInline() && mInlineData) { michael@0: if (mInlineData->mFloats.NotEmpty()) { michael@0: aFreeList.Append(mInlineData->mFloats); michael@0: } michael@0: MaybeFreeData(); michael@0: } michael@0: } michael@0: michael@0: void michael@0: nsLineBox::AppendFloats(nsFloatCacheFreeList& aFreeList) michael@0: { michael@0: NS_ABORT_IF_FALSE(IsInline(), "block line can't have floats"); michael@0: if (IsInline()) { michael@0: if (aFreeList.NotEmpty()) { michael@0: if (!mInlineData) { michael@0: mInlineData = new ExtraInlineData(GetPhysicalBounds()); michael@0: } michael@0: mInlineData->mFloats.Append(aFreeList); michael@0: } michael@0: } michael@0: } michael@0: michael@0: bool michael@0: nsLineBox::RemoveFloat(nsIFrame* aFrame) michael@0: { michael@0: NS_ABORT_IF_FALSE(IsInline(), "block line can't have floats"); michael@0: if (IsInline() && mInlineData) { michael@0: nsFloatCache* fc = mInlineData->mFloats.Find(aFrame); michael@0: if (fc) { michael@0: // Note: the placeholder is part of the line's child list michael@0: // and will be removed later. michael@0: mInlineData->mFloats.Remove(fc); michael@0: delete fc; michael@0: MaybeFreeData(); michael@0: return true; michael@0: } michael@0: } michael@0: return false; michael@0: } michael@0: michael@0: void michael@0: nsLineBox::SetOverflowAreas(const nsOverflowAreas& aOverflowAreas) michael@0: { michael@0: NS_FOR_FRAME_OVERFLOW_TYPES(otype) { michael@0: NS_ASSERTION(aOverflowAreas.Overflow(otype).width >= 0, michael@0: "illegal width for combined area"); michael@0: NS_ASSERTION(aOverflowAreas.Overflow(otype).height >= 0, michael@0: "illegal height for combined area"); michael@0: } michael@0: nsRect bounds = GetPhysicalBounds(); michael@0: if (!aOverflowAreas.VisualOverflow().IsEqualInterior(bounds) || michael@0: !aOverflowAreas.ScrollableOverflow().IsEqualEdges(bounds)) { michael@0: if (!mData) { michael@0: if (IsInline()) { michael@0: mInlineData = new ExtraInlineData(bounds); michael@0: } michael@0: else { michael@0: mBlockData = new ExtraBlockData(bounds); michael@0: } michael@0: } michael@0: mData->mOverflowAreas = aOverflowAreas; michael@0: } michael@0: else if (mData) { michael@0: // Store away new value so that MaybeFreeData compares against michael@0: // the right value. michael@0: mData->mOverflowAreas = aOverflowAreas; michael@0: MaybeFreeData(); michael@0: } michael@0: } michael@0: michael@0: //---------------------------------------------------------------------- michael@0: michael@0: michael@0: static nsLineBox* gDummyLines[1]; michael@0: michael@0: nsLineIterator::nsLineIterator() michael@0: { michael@0: mLines = gDummyLines; michael@0: mNumLines = 0; michael@0: mIndex = 0; michael@0: mRightToLeft = false; michael@0: } michael@0: michael@0: nsLineIterator::~nsLineIterator() michael@0: { michael@0: if (mLines != gDummyLines) { michael@0: delete [] mLines; michael@0: } michael@0: } michael@0: michael@0: /* virtual */ void michael@0: nsLineIterator::DisposeLineIterator() michael@0: { michael@0: delete this; michael@0: } michael@0: michael@0: nsresult michael@0: nsLineIterator::Init(nsLineList& aLines, bool aRightToLeft) michael@0: { michael@0: mRightToLeft = aRightToLeft; michael@0: michael@0: // Count the lines michael@0: int32_t numLines = aLines.size(); michael@0: if (0 == numLines) { michael@0: // Use gDummyLines so that we don't need null pointer checks in michael@0: // the accessor methods michael@0: mLines = gDummyLines; michael@0: return NS_OK; michael@0: } michael@0: michael@0: // Make a linear array of the lines michael@0: mLines = new nsLineBox*[numLines]; michael@0: if (!mLines) { michael@0: // Use gDummyLines so that we don't need null pointer checks in michael@0: // the accessor methods michael@0: mLines = gDummyLines; michael@0: return NS_ERROR_OUT_OF_MEMORY; michael@0: } michael@0: nsLineBox** lp = mLines; michael@0: for (nsLineList::iterator line = aLines.begin(), line_end = aLines.end() ; michael@0: line != line_end; michael@0: ++line) michael@0: { michael@0: *lp++ = line; michael@0: } michael@0: mNumLines = numLines; michael@0: return NS_OK; michael@0: } michael@0: michael@0: int32_t michael@0: nsLineIterator::GetNumLines() michael@0: { michael@0: return mNumLines; michael@0: } michael@0: michael@0: bool michael@0: nsLineIterator::GetDirection() michael@0: { michael@0: return mRightToLeft; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsLineIterator::GetLine(int32_t aLineNumber, michael@0: nsIFrame** aFirstFrameOnLine, michael@0: int32_t* aNumFramesOnLine, michael@0: nsRect& aLineBounds, michael@0: uint32_t* aLineFlags) michael@0: { michael@0: NS_ENSURE_ARG_POINTER(aFirstFrameOnLine); michael@0: NS_ENSURE_ARG_POINTER(aNumFramesOnLine); michael@0: NS_ENSURE_ARG_POINTER(aLineFlags); michael@0: michael@0: if ((aLineNumber < 0) || (aLineNumber >= mNumLines)) { michael@0: *aFirstFrameOnLine = nullptr; michael@0: *aNumFramesOnLine = 0; michael@0: aLineBounds.SetRect(0, 0, 0, 0); michael@0: return NS_OK; michael@0: } michael@0: nsLineBox* line = mLines[aLineNumber]; michael@0: *aFirstFrameOnLine = line->mFirstChild; michael@0: *aNumFramesOnLine = line->GetChildCount(); michael@0: aLineBounds = line->GetPhysicalBounds(); michael@0: michael@0: uint32_t flags = 0; michael@0: if (line->IsBlock()) { michael@0: flags |= NS_LINE_FLAG_IS_BLOCK; michael@0: } michael@0: else { michael@0: if (line->HasBreakAfter()) michael@0: flags |= NS_LINE_FLAG_ENDS_IN_BREAK; michael@0: } michael@0: *aLineFlags = flags; michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: int32_t michael@0: nsLineIterator::FindLineContaining(nsIFrame* aFrame, int32_t aStartLine) michael@0: { michael@0: NS_PRECONDITION(aStartLine <= mNumLines, "Bogus line numbers"); michael@0: int32_t lineNumber = aStartLine; michael@0: while (lineNumber != mNumLines) { michael@0: nsLineBox* line = mLines[lineNumber]; michael@0: if (line->Contains(aFrame)) { michael@0: return lineNumber; michael@0: } michael@0: ++lineNumber; michael@0: } michael@0: return -1; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsLineIterator::CheckLineOrder(int32_t aLine, michael@0: bool *aIsReordered, michael@0: nsIFrame **aFirstVisual, michael@0: nsIFrame **aLastVisual) michael@0: { michael@0: NS_ASSERTION (aLine >= 0 && aLine < mNumLines, "aLine out of range!"); michael@0: nsLineBox* line = mLines[aLine]; michael@0: michael@0: if (!line->mFirstChild) { // empty line michael@0: *aIsReordered = false; michael@0: *aFirstVisual = nullptr; michael@0: *aLastVisual = nullptr; michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsIFrame* leftmostFrame; michael@0: nsIFrame* rightmostFrame; michael@0: *aIsReordered = nsBidiPresUtils::CheckLineOrder(line->mFirstChild, line->GetChildCount(), &leftmostFrame, &rightmostFrame); michael@0: michael@0: // map leftmost/rightmost to first/last according to paragraph direction michael@0: *aFirstVisual = mRightToLeft ? rightmostFrame : leftmostFrame; michael@0: *aLastVisual = mRightToLeft ? leftmostFrame : rightmostFrame; michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsLineIterator::FindFrameAt(int32_t aLineNumber, michael@0: nscoord aX, michael@0: nsIFrame** aFrameFound, michael@0: bool* aXIsBeforeFirstFrame, michael@0: bool* aXIsAfterLastFrame) michael@0: { michael@0: NS_PRECONDITION(aFrameFound && aXIsBeforeFirstFrame && aXIsAfterLastFrame, michael@0: "null OUT ptr"); michael@0: if (!aFrameFound || !aXIsBeforeFirstFrame || !aXIsAfterLastFrame) { michael@0: return NS_ERROR_NULL_POINTER; michael@0: } michael@0: if ((aLineNumber < 0) || (aLineNumber >= mNumLines)) { michael@0: return NS_ERROR_INVALID_ARG; michael@0: } michael@0: michael@0: nsLineBox* line = mLines[aLineNumber]; michael@0: if (!line) { michael@0: *aFrameFound = nullptr; michael@0: *aXIsBeforeFirstFrame = true; michael@0: *aXIsAfterLastFrame = false; michael@0: return NS_OK; michael@0: } michael@0: michael@0: if (line->ISize() == 0 && line->BSize() == 0) michael@0: return NS_ERROR_FAILURE; michael@0: michael@0: nsIFrame* frame = line->mFirstChild; michael@0: nsIFrame* closestFromLeft = nullptr; michael@0: nsIFrame* closestFromRight = nullptr; michael@0: int32_t n = line->GetChildCount(); michael@0: while (n--) { michael@0: nsRect rect = frame->GetRect(); michael@0: if (rect.width > 0) { michael@0: // If aX is inside this frame - this is it michael@0: if (rect.x <= aX && rect.XMost() > aX) { michael@0: closestFromLeft = closestFromRight = frame; michael@0: break; michael@0: } michael@0: if (rect.x < aX) { michael@0: if (!closestFromLeft || michael@0: rect.XMost() > closestFromLeft->GetRect().XMost()) michael@0: closestFromLeft = frame; michael@0: } michael@0: else { michael@0: if (!closestFromRight || michael@0: rect.x < closestFromRight->GetRect().x) michael@0: closestFromRight = frame; michael@0: } michael@0: } michael@0: frame = frame->GetNextSibling(); michael@0: } michael@0: if (!closestFromLeft && !closestFromRight) { michael@0: // All frames were zero-width. Just take the first one. michael@0: closestFromLeft = closestFromRight = line->mFirstChild; michael@0: } michael@0: *aXIsBeforeFirstFrame = mRightToLeft ? !closestFromRight : !closestFromLeft; michael@0: *aXIsAfterLastFrame = mRightToLeft ? !closestFromLeft : !closestFromRight; michael@0: if (closestFromLeft == closestFromRight) { michael@0: *aFrameFound = closestFromLeft; michael@0: } michael@0: else if (!closestFromLeft) { michael@0: *aFrameFound = closestFromRight; michael@0: } michael@0: else if (!closestFromRight) { michael@0: *aFrameFound = closestFromLeft; michael@0: } michael@0: else { // we're between two frames michael@0: nscoord delta = closestFromRight->GetRect().x - closestFromLeft->GetRect().XMost(); michael@0: if (aX < closestFromLeft->GetRect().XMost() + delta/2) michael@0: *aFrameFound = closestFromLeft; michael@0: else michael@0: *aFrameFound = closestFromRight; michael@0: } michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsLineIterator::GetNextSiblingOnLine(nsIFrame*& aFrame, int32_t aLineNumber) michael@0: { michael@0: aFrame = aFrame->GetNextSibling(); michael@0: return NS_OK; michael@0: } michael@0: michael@0: //---------------------------------------------------------------------- michael@0: michael@0: #ifdef NS_BUILD_REFCNT_LOGGING michael@0: nsFloatCacheList::nsFloatCacheList() : michael@0: mHead(nullptr) michael@0: { michael@0: MOZ_COUNT_CTOR(nsFloatCacheList); michael@0: } michael@0: #endif michael@0: michael@0: nsFloatCacheList::~nsFloatCacheList() michael@0: { michael@0: DeleteAll(); michael@0: MOZ_COUNT_DTOR(nsFloatCacheList); michael@0: } michael@0: michael@0: void michael@0: nsFloatCacheList::DeleteAll() michael@0: { michael@0: nsFloatCache* c = mHead; michael@0: while (c) { michael@0: nsFloatCache* next = c->Next(); michael@0: delete c; michael@0: c = next; michael@0: } michael@0: mHead = nullptr; michael@0: } michael@0: michael@0: nsFloatCache* michael@0: nsFloatCacheList::Tail() const michael@0: { michael@0: nsFloatCache* fc = mHead; michael@0: while (fc) { michael@0: if (!fc->mNext) { michael@0: break; michael@0: } michael@0: fc = fc->mNext; michael@0: } michael@0: return fc; michael@0: } michael@0: michael@0: void michael@0: nsFloatCacheList::Append(nsFloatCacheFreeList& aList) michael@0: { michael@0: NS_PRECONDITION(aList.NotEmpty(), "Appending empty list will fail"); michael@0: michael@0: nsFloatCache* tail = Tail(); michael@0: if (tail) { michael@0: NS_ASSERTION(!tail->mNext, "Bogus!"); michael@0: tail->mNext = aList.mHead; michael@0: } michael@0: else { michael@0: NS_ASSERTION(!mHead, "Bogus!"); michael@0: mHead = aList.mHead; michael@0: } michael@0: aList.mHead = nullptr; michael@0: aList.mTail = nullptr; michael@0: } michael@0: michael@0: nsFloatCache* michael@0: nsFloatCacheList::Find(nsIFrame* aOutOfFlowFrame) michael@0: { michael@0: nsFloatCache* fc = mHead; michael@0: while (fc) { michael@0: if (fc->mFloat == aOutOfFlowFrame) { michael@0: break; michael@0: } michael@0: fc = fc->Next(); michael@0: } michael@0: return fc; michael@0: } michael@0: michael@0: nsFloatCache* michael@0: nsFloatCacheList::RemoveAndReturnPrev(nsFloatCache* aElement) michael@0: { michael@0: nsFloatCache* fc = mHead; michael@0: nsFloatCache* prev = nullptr; michael@0: while (fc) { michael@0: if (fc == aElement) { michael@0: if (prev) { michael@0: prev->mNext = fc->mNext; michael@0: } else { michael@0: mHead = fc->mNext; michael@0: } michael@0: return prev; michael@0: } michael@0: prev = fc; michael@0: fc = fc->mNext; michael@0: } michael@0: return nullptr; michael@0: } michael@0: michael@0: //---------------------------------------------------------------------- michael@0: michael@0: #ifdef NS_BUILD_REFCNT_LOGGING michael@0: nsFloatCacheFreeList::nsFloatCacheFreeList() : michael@0: mTail(nullptr) michael@0: { michael@0: MOZ_COUNT_CTOR(nsFloatCacheFreeList); michael@0: } michael@0: michael@0: nsFloatCacheFreeList::~nsFloatCacheFreeList() michael@0: { michael@0: MOZ_COUNT_DTOR(nsFloatCacheFreeList); michael@0: } michael@0: #endif michael@0: michael@0: void michael@0: nsFloatCacheFreeList::Append(nsFloatCacheList& aList) michael@0: { michael@0: NS_PRECONDITION(aList.NotEmpty(), "Appending empty list will fail"); michael@0: michael@0: if (mTail) { michael@0: NS_ASSERTION(!mTail->mNext, "Bogus"); michael@0: mTail->mNext = aList.mHead; michael@0: } michael@0: else { michael@0: NS_ASSERTION(!mHead, "Bogus"); michael@0: mHead = aList.mHead; michael@0: } michael@0: mTail = aList.Tail(); michael@0: aList.mHead = nullptr; michael@0: } michael@0: michael@0: void michael@0: nsFloatCacheFreeList::Remove(nsFloatCache* aElement) michael@0: { michael@0: nsFloatCache* prev = nsFloatCacheList::RemoveAndReturnPrev(aElement); michael@0: if (mTail == aElement) { michael@0: mTail = prev; michael@0: } michael@0: } michael@0: michael@0: void michael@0: nsFloatCacheFreeList::DeleteAll() michael@0: { michael@0: nsFloatCacheList::DeleteAll(); michael@0: mTail = nullptr; michael@0: } michael@0: michael@0: nsFloatCache* michael@0: nsFloatCacheFreeList::Alloc(nsIFrame* aFloat) michael@0: { michael@0: NS_PRECONDITION(aFloat->GetStateBits() & NS_FRAME_OUT_OF_FLOW, michael@0: "This is a float cache, why isn't the frame out-of-flow?"); michael@0: nsFloatCache* fc = mHead; michael@0: if (mHead) { michael@0: if (mHead == mTail) { michael@0: mHead = mTail = nullptr; michael@0: } michael@0: else { michael@0: mHead = fc->mNext; michael@0: } michael@0: fc->mNext = nullptr; michael@0: } michael@0: else { michael@0: fc = new nsFloatCache(); michael@0: } michael@0: fc->mFloat = aFloat; michael@0: return fc; michael@0: } michael@0: michael@0: void michael@0: nsFloatCacheFreeList::Append(nsFloatCache* aFloat) michael@0: { michael@0: NS_ASSERTION(!aFloat->mNext, "Bogus!"); michael@0: aFloat->mNext = nullptr; michael@0: if (mTail) { michael@0: NS_ASSERTION(!mTail->mNext, "Bogus!"); michael@0: mTail->mNext = aFloat; michael@0: mTail = aFloat; michael@0: } michael@0: else { michael@0: NS_ASSERTION(!mHead, "Bogus!"); michael@0: mHead = mTail = aFloat; michael@0: } michael@0: } michael@0: michael@0: //---------------------------------------------------------------------- michael@0: michael@0: nsFloatCache::nsFloatCache() michael@0: : mFloat(nullptr), michael@0: mNext(nullptr) michael@0: { michael@0: MOZ_COUNT_CTOR(nsFloatCache); michael@0: } michael@0: michael@0: #ifdef NS_BUILD_REFCNT_LOGGING michael@0: nsFloatCache::~nsFloatCache() michael@0: { michael@0: MOZ_COUNT_DTOR(nsFloatCache); michael@0: } michael@0: #endif