diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-28 14:29:10 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-28 14:29:10 +0000 |
commit | 2aa4a82499d4becd2284cdb482213d541b8804dd (patch) | |
tree | b80bf8bf13c3766139fbacc530efd0dd9d54394c /layout/generic/nsLineBox.h | |
parent | Initial commit. (diff) | |
download | firefox-2aa4a82499d4becd2284cdb482213d541b8804dd.tar.xz firefox-2aa4a82499d4becd2284cdb482213d541b8804dd.zip |
Adding upstream version 86.0.1.upstream/86.0.1upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'layout/generic/nsLineBox.h')
-rw-r--r-- | layout/generic/nsLineBox.h | 1693 |
1 files changed, 1693 insertions, 0 deletions
diff --git a/layout/generic/nsLineBox.h b/layout/generic/nsLineBox.h new file mode 100644 index 0000000000..1d2bdd8f46 --- /dev/null +++ b/layout/generic/nsLineBox.h @@ -0,0 +1,1693 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* 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 */ + +#ifndef nsLineBox_h___ +#define nsLineBox_h___ + +#include "mozilla/Attributes.h" +#include "mozilla/Likely.h" + +#include "nsILineIterator.h" +#include "nsIFrame.h" +#include <algorithm> + +class nsLineBox; +class nsFloatCache; +class nsFloatCacheList; +class nsFloatCacheFreeList; +class nsWindowSizes; + +namespace mozilla { +class PresShell; +} // namespace mozilla + +// State cached after reflowing a float. This state is used during +// incremental reflow when we avoid reflowing a float. +class nsFloatCache { + public: + nsFloatCache(); +#ifdef NS_BUILD_REFCNT_LOGGING + ~nsFloatCache(); +#else + ~nsFloatCache() = default; +#endif + + nsFloatCache* Next() const { return mNext; } + + nsIFrame* mFloat; // floating frame + + protected: + nsFloatCache* mNext; + + friend class nsFloatCacheList; + friend class nsFloatCacheFreeList; +}; + +//---------------------------------------- + +class nsFloatCacheList { + public: +#ifdef NS_BUILD_REFCNT_LOGGING + nsFloatCacheList(); +#else + nsFloatCacheList() : mHead(nullptr) {} +#endif + ~nsFloatCacheList(); + + bool IsEmpty() const { return nullptr == mHead; } + + bool NotEmpty() const { return nullptr != mHead; } + + nsFloatCache* Head() const { return mHead; } + + nsFloatCache* Tail() const; + + void DeleteAll(); + + nsFloatCache* Find(nsIFrame* aOutOfFlowFrame); + + // Remove a nsFloatCache from this list. Deleting this nsFloatCache + // becomes the caller's responsibility. + void Remove(nsFloatCache* aElement) { RemoveAndReturnPrev(aElement); } + + // Steal away aList's nsFloatCache objects and put them in this + // list. aList must not be empty. + void Append(nsFloatCacheFreeList& aList); + + protected: + nsFloatCache* mHead; + + // Remove a nsFloatCache from this list. Deleting this nsFloatCache + // becomes the caller's responsibility. Returns the nsFloatCache that was + // before aElement, or nullptr if aElement was the first. + nsFloatCache* RemoveAndReturnPrev(nsFloatCache* aElement); + + friend class nsFloatCacheFreeList; +}; + +//--------------------------------------- +// Like nsFloatCacheList, but with fast access to the tail + +class nsFloatCacheFreeList : private nsFloatCacheList { + public: +#ifdef NS_BUILD_REFCNT_LOGGING + nsFloatCacheFreeList(); + ~nsFloatCacheFreeList(); +#else + nsFloatCacheFreeList() : mTail(nullptr) {} + ~nsFloatCacheFreeList() = default; +#endif + + // Reimplement trivial functions + bool IsEmpty() const { return nullptr == mHead; } + + nsFloatCache* Head() const { return mHead; } + + nsFloatCache* Tail() const { return mTail; } + + bool NotEmpty() const { return nullptr != mHead; } + + void DeleteAll(); + + // Steal away aList's nsFloatCache objects and put them on this + // free-list. aList must not be empty. + void Append(nsFloatCacheList& aList); + + void Append(nsFloatCache* aFloatCache); + + void Remove(nsFloatCache* aElement); + + // Remove an nsFloatCache object from this list and return it, or create + // a new one if this one is empty; Set its mFloat to aFloat. + nsFloatCache* Alloc(nsIFrame* aFloat); + + protected: + nsFloatCache* mTail; + + friend class nsFloatCacheList; +}; + +//---------------------------------------------------------------------- + +#define LINE_MAX_CHILD_COUNT INT32_MAX + +/** + * Function to create a line box and initialize it with a single frame. + * The allocation is infallible. + * If the frame was moved from another line then you're responsible + * for notifying that line using NoteFrameRemoved(). Alternatively, + * it's better to use the next function that does that for you in an + * optimal way. + */ +nsLineBox* NS_NewLineBox(mozilla::PresShell* aPresShell, nsIFrame* aFrame, + bool aIsBlock); +/** + * Function to create a line box and initialize it with aCount frames + * that are currently on aFromLine. The allocation is infallible. + */ +nsLineBox* NS_NewLineBox(mozilla::PresShell* aPresShell, nsLineBox* aFromLine, + nsIFrame* aFrame, int32_t aCount); + +class nsLineList; + +// don't use the following names outside of this file. Instead, use +// nsLineList::iterator, etc. These are just here to allow them to +// be specified as parameters to methods of nsLineBox. +class nsLineList_iterator; +class nsLineList_const_iterator; +class nsLineList_reverse_iterator; +class nsLineList_const_reverse_iterator; + +/** + * Users must have the class that is to be part of the list inherit + * from nsLineLink. If they want to be efficient, it should be the + * first base class. (This was originally nsCLink in a templatized + * nsCList, but it's still useful separately.) + */ + +class nsLineLink { + public: + friend class nsLineList; + friend class nsLineList_iterator; + friend class nsLineList_reverse_iterator; + friend class nsLineList_const_iterator; + friend class nsLineList_const_reverse_iterator; + + private: + nsLineLink* _mNext; // or head + nsLineLink* _mPrev; // or tail +}; + +/** + * The nsLineBox class represents a horizontal line of frames. It contains + * enough state to support incremental reflow of the frames, event handling + * for the frames, and rendering of the frames. + */ +class nsLineBox final : public nsLineLink { + private: + nsLineBox(nsIFrame* aFrame, int32_t aCount, bool aIsBlock); + ~nsLineBox(); + + // Infallible overloaded new operator. Uses an arena (which comes from the + // presShell) to perform the allocation. + void* operator new(size_t sz, mozilla::PresShell* aPresShell); + void operator delete(void* aPtr, size_t sz) = delete; + + public: + // Use these functions to allocate and destroy line boxes + friend nsLineBox* NS_NewLineBox(mozilla::PresShell* aPresShell, + nsIFrame* aFrame, bool aIsBlock); + friend nsLineBox* NS_NewLineBox(mozilla::PresShell* aPresShell, + nsLineBox* aFromLine, nsIFrame* aFrame, + int32_t aCount); + void Destroy(mozilla::PresShell* aPresShell); + + // mBlock bit + bool IsBlock() const { return mFlags.mBlock; } + bool IsInline() const { return !mFlags.mBlock; } + + // mDirty bit + void MarkDirty() { mFlags.mDirty = 1; } + void ClearDirty() { mFlags.mDirty = 0; } + bool IsDirty() const { return mFlags.mDirty; } + + // mPreviousMarginDirty bit + void MarkPreviousMarginDirty() { mFlags.mPreviousMarginDirty = 1; } + void ClearPreviousMarginDirty() { mFlags.mPreviousMarginDirty = 0; } + bool IsPreviousMarginDirty() const { return mFlags.mPreviousMarginDirty; } + + // mHasClearance bit + void SetHasClearance() { mFlags.mHasClearance = 1; } + void ClearHasClearance() { mFlags.mHasClearance = 0; } + bool HasClearance() const { return mFlags.mHasClearance; } + + // mImpactedByFloat bit + void SetLineIsImpactedByFloat(bool aValue) { + mFlags.mImpactedByFloat = aValue; + } + bool IsImpactedByFloat() const { return mFlags.mImpactedByFloat; } + + // mLineWrapped bit + void SetLineWrapped(bool aOn) { mFlags.mLineWrapped = aOn; } + bool IsLineWrapped() const { return mFlags.mLineWrapped; } + + // mInvalidateTextRuns bit + void SetInvalidateTextRuns(bool aOn) { mFlags.mInvalidateTextRuns = aOn; } + bool GetInvalidateTextRuns() const { return mFlags.mInvalidateTextRuns; } + + // mResizeReflowOptimizationDisabled bit + void DisableResizeReflowOptimization() { + mFlags.mResizeReflowOptimizationDisabled = true; + } + void EnableResizeReflowOptimization() { + mFlags.mResizeReflowOptimizationDisabled = false; + } + bool ResizeReflowOptimizationDisabled() const { + return mFlags.mResizeReflowOptimizationDisabled; + } + + // mHasMarker bit + void SetHasMarker() { + mFlags.mHasMarker = true; + InvalidateCachedIsEmpty(); + } + void ClearHasMarker() { + mFlags.mHasMarker = false; + InvalidateCachedIsEmpty(); + } + bool HasMarker() const { return mFlags.mHasMarker; } + + // mHadFloatPushed bit + void SetHadFloatPushed() { mFlags.mHadFloatPushed = true; } + void ClearHadFloatPushed() { mFlags.mHadFloatPushed = false; } + bool HadFloatPushed() const { return mFlags.mHadFloatPushed; } + + // mHasLineClampEllipsis bit + void SetHasLineClampEllipsis() { mFlags.mHasLineClampEllipsis = true; } + void ClearHasLineClampEllipsis() { mFlags.mHasLineClampEllipsis = false; } + bool HasLineClampEllipsis() const { return mFlags.mHasLineClampEllipsis; } + + // mMovedFragments bit + void SetMovedFragments() { mFlags.mMovedFragments = true; } + void ClearMovedFragments() { mFlags.mMovedFragments = false; } + bool MovedFragments() const { return mFlags.mMovedFragments; } + + private: + // Add a hash table for fast lookup when the line has more frames than this. + static const uint32_t kMinChildCountForHashtable = 200; + + /** + * Take ownership of aFromLine's hash table and remove the frames that + * stay on aFromLine from it, i.e. aFromLineNewCount frames starting with + * mFirstChild. This method is used to optimize moving a large number + * of frames from one line to the next. + */ + void StealHashTableFrom(nsLineBox* aFromLine, uint32_t aFromLineNewCount); + + /** + * Does the equivalent of this->NoteFrameAdded and aFromLine->NoteFrameRemoved + * for each frame on this line, but in a optimized way. + */ + void NoteFramesMovedFrom(nsLineBox* aFromLine); + + void SwitchToHashtable() { + MOZ_ASSERT(!mFlags.mHasHashedFrames); + uint32_t count = GetChildCount(); + mFlags.mHasHashedFrames = 1; + uint32_t minLength = + std::max(kMinChildCountForHashtable, + uint32_t(PLDHashTable::kDefaultInitialLength)); + mFrames = + new nsTHashtable<nsPtrHashKey<nsIFrame> >(std::max(count, minLength)); + for (nsIFrame* f = mFirstChild; count-- > 0; f = f->GetNextSibling()) { + mFrames->PutEntry(f); + } + } + void SwitchToCounter() { + MOZ_ASSERT(mFlags.mHasHashedFrames); + uint32_t count = GetChildCount(); + delete mFrames; + mFlags.mHasHashedFrames = 0; + mChildCount = count; + } + + public: + int32_t GetChildCount() const { + return MOZ_UNLIKELY(mFlags.mHasHashedFrames) ? mFrames->Count() + : mChildCount; + } + + /** + * Register that aFrame is now on this line. + */ + void NoteFrameAdded(nsIFrame* aFrame) { + if (MOZ_UNLIKELY(mFlags.mHasHashedFrames)) { + mFrames->PutEntry(aFrame); + } else { + if (++mChildCount >= kMinChildCountForHashtable) { + SwitchToHashtable(); + } + } + } + + /** + * Register that aFrame is not on this line anymore. + */ + void NoteFrameRemoved(nsIFrame* aFrame) { + MOZ_ASSERT(GetChildCount() > 0); + if (MOZ_UNLIKELY(mFlags.mHasHashedFrames)) { + mFrames->RemoveEntry(aFrame); + if (mFrames->Count() < kMinChildCountForHashtable) { + SwitchToCounter(); + } + } else { + --mChildCount; + } + } + + // mBreakType value + // Break information is applied *before* the line if the line is a block, + // or *after* the line if the line is an inline. Confusing, I know, but + // using different names should help. + using StyleClear = mozilla::StyleClear; + bool HasBreakBefore() const { + return IsBlock() && StyleClear::None != BreakType(); + } + void SetBreakTypeBefore(StyleClear aBreakType) { + MOZ_ASSERT(IsBlock(), "Only blocks have break-before"); + MOZ_ASSERT( + aBreakType == StyleClear::None || aBreakType == StyleClear::Left || + aBreakType == StyleClear::Right || aBreakType == StyleClear::Both, + "Only float break types are allowed before a line"); + mFlags.mBreakType = aBreakType; + } + StyleClear GetBreakTypeBefore() const { + return IsBlock() ? BreakType() : StyleClear::None; + } + + bool HasBreakAfter() const { + return !IsBlock() && StyleClear::None != BreakType(); + } + void SetBreakTypeAfter(StyleClear aBreakType) { + MOZ_ASSERT(!IsBlock(), "Only inlines have break-after"); + mFlags.mBreakType = aBreakType; + } + bool HasFloatBreakAfter() const { + return !IsBlock() && (StyleClear::Left == BreakType() || + StyleClear::Right == BreakType() || + StyleClear::Both == BreakType()); + } + StyleClear GetBreakTypeAfter() const { + return !IsBlock() ? BreakType() : StyleClear::None; + } + + // mCarriedOutBEndMargin value + nsCollapsingMargin GetCarriedOutBEndMargin() const; + // Returns true if the margin changed + bool SetCarriedOutBEndMargin(nsCollapsingMargin aValue); + + // mFloats + bool HasFloats() const { + return (IsInline() && mInlineData) && mInlineData->mFloats.NotEmpty(); + } + nsFloatCache* GetFirstFloat(); + void FreeFloats(nsFloatCacheFreeList& aFreeList); + void AppendFloats(nsFloatCacheFreeList& aFreeList); + bool RemoveFloat(nsIFrame* aFrame); + + // The ink overflow area should never be used for things that affect layout. + // The scrollable overflow area are permitted to affect layout for handling of + // overflow and scrollbars. + void SetOverflowAreas(const mozilla::OverflowAreas& aOverflowAreas); + mozilla::LogicalRect GetOverflowArea(mozilla::OverflowType aType, + mozilla::WritingMode aWM, + const nsSize& aContainerSize) { + return mozilla::LogicalRect(aWM, GetOverflowArea(aType), aContainerSize); + } + nsRect GetOverflowArea(mozilla::OverflowType aType) const { + return mData ? mData->mOverflowAreas.Overflow(aType) : GetPhysicalBounds(); + } + mozilla::OverflowAreas GetOverflowAreas() const { + if (mData) { + return mData->mOverflowAreas; + } + nsRect bounds = GetPhysicalBounds(); + return mozilla::OverflowAreas(bounds, bounds); + } + nsRect InkOverflowRect() const { + return GetOverflowArea(mozilla::OverflowType::Ink); + } + nsRect ScrollableOverflowRect() { + return GetOverflowArea(mozilla::OverflowType::Scrollable); + } + + void SlideBy(nscoord aDBCoord, const nsSize& aContainerSize) { + NS_ASSERTION( + aContainerSize == mContainerSize || mContainerSize == nsSize(-1, -1), + "container size doesn't match"); + mContainerSize = aContainerSize; + mBounds.BStart(mWritingMode) += aDBCoord; + if (mData) { + // Use a null containerSize to convert vector from logical to physical. + const nsSize nullContainerSize; + nsPoint physicalDelta = + mozilla::LogicalPoint(mWritingMode, 0, aDBCoord) + .GetPhysicalPoint(mWritingMode, nullContainerSize); + for (const auto otype : mozilla::AllOverflowTypes()) { + mData->mOverflowAreas.Overflow(otype) += physicalDelta; + } + } + } + + // Container-size for the line is changing (and therefore if writing mode + // was vertical-rl, the line will move physically; this is like SlideBy, + // but it is the container size instead of the line's own logical coord + // that is changing. + nsSize UpdateContainerSize(const nsSize aNewContainerSize) { + NS_ASSERTION(mContainerSize != nsSize(-1, -1), "container size not set"); + nsSize delta = mContainerSize - aNewContainerSize; + mContainerSize = aNewContainerSize; + // this has a physical-coordinate effect only in vertical-rl mode + if (mWritingMode.IsVerticalRL() && mData) { + nsPoint physicalDelta(-delta.width, 0); + for (const auto otype : mozilla::AllOverflowTypes()) { + mData->mOverflowAreas.Overflow(otype) += physicalDelta; + } + } + return delta; + } + + void IndentBy(nscoord aDICoord, const nsSize& aContainerSize) { + NS_ASSERTION( + aContainerSize == mContainerSize || mContainerSize == nsSize(-1, -1), + "container size doesn't match"); + mContainerSize = aContainerSize; + mBounds.IStart(mWritingMode) += aDICoord; + } + + void ExpandBy(nscoord aDISize, const nsSize& aContainerSize) { + NS_ASSERTION( + aContainerSize == mContainerSize || mContainerSize == nsSize(-1, -1), + "container size doesn't match"); + mContainerSize = aContainerSize; + mBounds.ISize(mWritingMode) += aDISize; + } + + /** + * The logical ascent (distance from block-start to baseline) of the + * linebox is the logical ascent of the anonymous inline box (for + * which we don't actually create a frame) that wraps all the + * consecutive inline children of a block. + * + * This is currently unused for block lines. + */ + nscoord GetLogicalAscent() const { return mAscent; } + void SetLogicalAscent(nscoord aAscent) { mAscent = aAscent; } + + nscoord BStart() const { return mBounds.BStart(mWritingMode); } + nscoord BSize() const { return mBounds.BSize(mWritingMode); } + nscoord BEnd() const { return mBounds.BEnd(mWritingMode); } + nscoord IStart() const { return mBounds.IStart(mWritingMode); } + nscoord ISize() const { return mBounds.ISize(mWritingMode); } + nscoord IEnd() const { return mBounds.IEnd(mWritingMode); } + void SetBoundsEmpty() { + mBounds.IStart(mWritingMode) = 0; + mBounds.ISize(mWritingMode) = 0; + mBounds.BStart(mWritingMode) = 0; + mBounds.BSize(mWritingMode) = 0; + } + + using PostDestroyData = nsIFrame::PostDestroyData; + static void DeleteLineList(nsPresContext* aPresContext, nsLineList& aLines, + nsIFrame* aDestructRoot, nsFrameList* aFrames, + PostDestroyData& aPostDestroyData); + + // search from end to beginning of [aBegin, aEnd) + // Returns true if it found the line and false if not. + // Moves aEnd as it searches so that aEnd points to the resulting line. + // aLastFrameBeforeEnd is the last frame before aEnd (so if aEnd is + // the end of the line list, it's just the last frame in the frame + // list). + static bool RFindLineContaining(nsIFrame* aFrame, + const nsLineList_iterator& aBegin, + nsLineList_iterator& aEnd, + nsIFrame* aLastFrameBeforeEnd, + int32_t* aFrameIndexInLine); + +#ifdef DEBUG_FRAME_DUMP + static const char* BreakTypeToString(StyleClear aBreakType); + char* StateToString(char* aBuf, int32_t aBufSize) const; + + void List(FILE* out, int32_t aIndent, + nsIFrame::ListFlags aFlags = nsIFrame::ListFlags()) const; + void List(FILE* out = stderr, const char* aPrefix = "", + nsIFrame::ListFlags aFlags = nsIFrame::ListFlags()) const; + nsIFrame* LastChild() const; +#endif + + void AddSizeOfExcludingThis(nsWindowSizes& aSizes) const; + + // Find the index of aFrame within the line, starting search at the start. + int32_t IndexOf(nsIFrame* aFrame) const; + + // Find the index of aFrame within the line, starting search at the end. + // (Produces the same result as IndexOf, but with different performance + // characteristics.) The caller must provide the last frame in the line. + int32_t RIndexOf(nsIFrame* aFrame, nsIFrame* aLastFrameInLine) const; + + bool Contains(nsIFrame* aFrame) const { + return MOZ_UNLIKELY(mFlags.mHasHashedFrames) ? mFrames->Contains(aFrame) + : IndexOf(aFrame) >= 0; + } + + // whether the line box is "logically" empty (just like nsIFrame::IsEmpty) + bool IsEmpty() const; + + // Call this only while in Reflow() for the block the line belongs + // to, only between reflowing the line (or sliding it, if we skip + // reflowing it) and the end of reflowing the block. + bool CachedIsEmpty(); + + void InvalidateCachedIsEmpty() { mFlags.mEmptyCacheValid = false; } + + // For debugging purposes + bool IsValidCachedIsEmpty() { return mFlags.mEmptyCacheValid; } + +#ifdef DEBUG + static int32_t GetCtorCount(); +#endif + + nsIFrame* mFirstChild; + + mozilla::WritingMode mWritingMode; + + // Physical size. Use only for physical <-> logical coordinate conversion. + nsSize mContainerSize; + + private: + mozilla::LogicalRect mBounds; + + public: + const mozilla::LogicalRect& GetBounds() { return mBounds; } + nsRect GetPhysicalBounds() const { + if (mBounds.IsAllZero()) { + return nsRect(0, 0, 0, 0); + } + + NS_ASSERTION(mContainerSize != nsSize(-1, -1), + "mContainerSize not initialized"); + return mBounds.GetPhysicalRect(mWritingMode, mContainerSize); + } + void SetBounds(mozilla::WritingMode aWritingMode, nscoord aIStart, + nscoord aBStart, nscoord aISize, nscoord aBSize, + const nsSize& aContainerSize) { + mWritingMode = aWritingMode; + mContainerSize = aContainerSize; + mBounds = + mozilla::LogicalRect(aWritingMode, aIStart, aBStart, aISize, aBSize); + } + void SetBounds(mozilla::WritingMode aWritingMode, nsRect aRect, + const nsSize& aContainerSize) { + mWritingMode = aWritingMode; + mContainerSize = aContainerSize; + mBounds = mozilla::LogicalRect(aWritingMode, aRect, aContainerSize); + } + + // mFlags.mHasHashedFrames says which one to use + union { + nsTHashtable<nsPtrHashKey<nsIFrame> >* mFrames; + uint32_t mChildCount; + }; + + struct FlagBits { + bool mDirty : 1; + bool mPreviousMarginDirty : 1; + bool mHasClearance : 1; + bool mBlock : 1; + bool mImpactedByFloat : 1; + bool mLineWrapped : 1; + bool mInvalidateTextRuns : 1; + // default 0 = means that the opt potentially applies to this line. + // 1 = never skip reflowing this line for a resize reflow + bool mResizeReflowOptimizationDisabled : 1; + bool mEmptyCacheValid : 1; + bool mEmptyCacheState : 1; + // mHasMarker indicates that this is an inline line whose block's + // ::marker is adjacent to this line and non-empty. + bool mHasMarker : 1; + // Indicates that this line *may* have a placeholder for a float + // that was pushed to a later column or page. + bool mHadFloatPushed : 1; + bool mHasHashedFrames : 1; + // Indicates that this line is the one identified by an ancestor block + // with -webkit-line-clamp on its legacy flex container, and that subsequent + // lines under that block are "clamped" away, and therefore we need to + // render a 'text-overflow: ellipsis'-like marker in this line. At most one + // line in the set of lines found by LineClampLineIterator for a given + // block will have this flag set. + bool mHasLineClampEllipsis : 1; + // Has this line moved to a different fragment of the block since + // the last time it was reflowed? + bool mMovedFragments : 1; + StyleClear mBreakType; + }; + + struct ExtraData { + explicit ExtraData(const nsRect& aBounds) + : mOverflowAreas(aBounds, aBounds) {} + mozilla::OverflowAreas mOverflowAreas; + }; + + struct ExtraBlockData : public ExtraData { + explicit ExtraBlockData(const nsRect& aBounds) + : ExtraData(aBounds), mCarriedOutBEndMargin() {} + nsCollapsingMargin mCarriedOutBEndMargin; + }; + + struct ExtraInlineData : public ExtraData { + explicit ExtraInlineData(const nsRect& aBounds) + : ExtraData(aBounds), + mFloatEdgeIStart(nscoord_MIN), + mFloatEdgeIEnd(nscoord_MIN) {} + nscoord mFloatEdgeIStart; + nscoord mFloatEdgeIEnd; + nsFloatCacheList mFloats; + }; + + bool GetFloatEdges(nscoord* aStart, nscoord* aEnd) const { + MOZ_ASSERT(IsInline(), "block line can't have float edges"); + if (mInlineData && mInlineData->mFloatEdgeIStart != nscoord_MIN) { + *aStart = mInlineData->mFloatEdgeIStart; + *aEnd = mInlineData->mFloatEdgeIEnd; + return true; + } + return false; + } + void SetFloatEdges(nscoord aStart, nscoord aEnd); + void ClearFloatEdges(); + + protected: + nscoord mAscent; // see |SetAscent| / |GetAscent| + static_assert(sizeof(FlagBits) <= sizeof(uint32_t), + "size of FlagBits should not be larger than size of uint32_t"); + union { + uint32_t mAllFlags; + FlagBits mFlags; + }; + + StyleClear BreakType() const { return mFlags.mBreakType; }; + + union { + ExtraData* mData; + ExtraBlockData* mBlockData; + ExtraInlineData* mInlineData; + }; + + void Cleanup(); + void MaybeFreeData(); +}; + +/** + * A linked list type where the items in the list must inherit from + * a link type to fuse allocations. + * + * API heavily based on the |list| class in the C++ standard. + */ + +class nsLineList_iterator { + public: + friend class nsLineList; + friend class nsLineList_reverse_iterator; + friend class nsLineList_const_iterator; + friend class nsLineList_const_reverse_iterator; + + typedef nsLineList_iterator iterator_self_type; + typedef nsLineList_reverse_iterator iterator_reverse_type; + + typedef nsLineBox& reference; + typedef const nsLineBox& const_reference; + + typedef nsLineBox* pointer; + typedef const nsLineBox* const_pointer; + + typedef uint32_t size_type; + typedef int32_t difference_type; + + typedef nsLineLink link_type; + +#ifdef DEBUG + nsLineList_iterator() : mListLink(nullptr) { + memset(&mCurrent, 0xcd, sizeof(mCurrent)); + } +#else + // Auto generated default constructor OK. +#endif + // Auto generated copy-constructor OK. + + inline iterator_self_type& operator=(const iterator_self_type& aOther); + inline iterator_self_type& operator=(const iterator_reverse_type& aOther); + + iterator_self_type& operator++() { + mCurrent = mCurrent->_mNext; + return *this; + } + + iterator_self_type operator++(int) { + iterator_self_type rv(*this); + mCurrent = mCurrent->_mNext; + return rv; + } + + iterator_self_type& operator--() { + mCurrent = mCurrent->_mPrev; + return *this; + } + + iterator_self_type operator--(int) { + iterator_self_type rv(*this); + mCurrent = mCurrent->_mPrev; + return rv; + } + + reference operator*() { + MOZ_ASSERT(mListLink); + MOZ_ASSERT(mCurrent != mListLink, "running past end"); + return *static_cast<pointer>(mCurrent); + } + + pointer operator->() { + MOZ_ASSERT(mListLink); + MOZ_ASSERT(mCurrent != mListLink, "running past end"); + return static_cast<pointer>(mCurrent); + } + + pointer get() { + MOZ_ASSERT(mListLink); + MOZ_ASSERT(mCurrent != mListLink, "running past end"); + return static_cast<pointer>(mCurrent); + } + + operator pointer() { + MOZ_ASSERT(mListLink); + MOZ_ASSERT(mCurrent != mListLink, "running past end"); + return static_cast<pointer>(mCurrent); + } + + const_reference operator*() const { + MOZ_ASSERT(mListLink); + MOZ_ASSERT(mCurrent != mListLink, "running past end"); + return *static_cast<const_pointer>(mCurrent); + } + + const_pointer operator->() const { + MOZ_ASSERT(mListLink); + MOZ_ASSERT(mCurrent != mListLink, "running past end"); + return static_cast<const_pointer>(mCurrent); + } + +#ifndef __MWERKS__ + operator const_pointer() const { + MOZ_ASSERT(mListLink); + MOZ_ASSERT(mCurrent != mListLink, "running past end"); + return static_cast<const_pointer>(mCurrent); + } +#endif /* !__MWERKS__ */ + + iterator_self_type next() { + iterator_self_type copy(*this); + return ++copy; + } + + const iterator_self_type next() const { + iterator_self_type copy(*this); + return ++copy; + } + + iterator_self_type prev() { + iterator_self_type copy(*this); + return --copy; + } + + const iterator_self_type prev() const { + iterator_self_type copy(*this); + return --copy; + } + + // Passing by value rather than by reference and reference to const + // to keep AIX happy. + bool operator==(const iterator_self_type aOther) const { + MOZ_ASSERT(mListLink); + MOZ_ASSERT(mListLink == aOther.mListLink, + "comparing iterators over different lists"); + return mCurrent == aOther.mCurrent; + } + bool operator!=(const iterator_self_type aOther) const { + MOZ_ASSERT(mListLink); + MOZ_ASSERT(mListLink == aOther.mListLink, + "comparing iterators over different lists"); + return mCurrent != aOther.mCurrent; + } + bool operator==(const iterator_self_type aOther) { + MOZ_ASSERT(mListLink); + MOZ_ASSERT(mListLink == aOther.mListLink, + "comparing iterators over different lists"); + return mCurrent == aOther.mCurrent; + } + bool operator!=(const iterator_self_type aOther) { + MOZ_ASSERT(mListLink); + MOZ_ASSERT(mListLink == aOther.mListLink, + "comparing iterators over different lists"); + return mCurrent != aOther.mCurrent; + } + +#ifdef DEBUG + bool IsInSameList(const iterator_self_type aOther) const { + return mListLink == aOther.mListLink; + } +#endif + + private: + link_type* mCurrent; +#ifdef DEBUG + link_type* mListLink; // the list's link, i.e., the end +#endif +}; + +class nsLineList_reverse_iterator { + public: + friend class nsLineList; + friend class nsLineList_iterator; + friend class nsLineList_const_iterator; + friend class nsLineList_const_reverse_iterator; + + typedef nsLineList_reverse_iterator iterator_self_type; + typedef nsLineList_iterator iterator_reverse_type; + + typedef nsLineBox& reference; + typedef const nsLineBox& const_reference; + + typedef nsLineBox* pointer; + typedef const nsLineBox* const_pointer; + + typedef uint32_t size_type; + typedef int32_t difference_type; + + typedef nsLineLink link_type; + +#ifdef DEBUG + nsLineList_reverse_iterator() : mListLink(nullptr) { + memset(&mCurrent, 0xcd, sizeof(mCurrent)); + } +#else + // Auto generated default constructor OK. +#endif + // Auto generated copy-constructor OK. + + inline iterator_self_type& operator=(const iterator_reverse_type& aOther); + inline iterator_self_type& operator=(const iterator_self_type& aOther); + + iterator_self_type& operator++() { + mCurrent = mCurrent->_mPrev; + return *this; + } + + iterator_self_type operator++(int) { + iterator_self_type rv(*this); + mCurrent = mCurrent->_mPrev; + return rv; + } + + iterator_self_type& operator--() { + mCurrent = mCurrent->_mNext; + return *this; + } + + iterator_self_type operator--(int) { + iterator_self_type rv(*this); + mCurrent = mCurrent->_mNext; + return rv; + } + + reference operator*() { + MOZ_ASSERT(mListLink); + MOZ_ASSERT(mCurrent != mListLink, "running past end"); + return *static_cast<pointer>(mCurrent); + } + + pointer operator->() { + MOZ_ASSERT(mListLink); + MOZ_ASSERT(mCurrent != mListLink, "running past end"); + return static_cast<pointer>(mCurrent); + } + + pointer get() { + MOZ_ASSERT(mListLink); + MOZ_ASSERT(mCurrent != mListLink, "running past end"); + return static_cast<pointer>(mCurrent); + } + + operator pointer() { + MOZ_ASSERT(mListLink); + MOZ_ASSERT(mCurrent != mListLink, "running past end"); + return static_cast<pointer>(mCurrent); + } + + const_reference operator*() const { + MOZ_ASSERT(mListLink); + MOZ_ASSERT(mCurrent != mListLink, "running past end"); + return *static_cast<const_pointer>(mCurrent); + } + + const_pointer operator->() const { + MOZ_ASSERT(mListLink); + MOZ_ASSERT(mCurrent != mListLink, "running past end"); + return static_cast<const_pointer>(mCurrent); + } + +#ifndef __MWERKS__ + operator const_pointer() const { + MOZ_ASSERT(mListLink); + MOZ_ASSERT(mCurrent != mListLink, "running past end"); + return static_cast<const_pointer>(mCurrent); + } +#endif /* !__MWERKS__ */ + + // Passing by value rather than by reference and reference to const + // to keep AIX happy. + bool operator==(const iterator_self_type aOther) const { + MOZ_ASSERT(mListLink); + NS_ASSERTION(mListLink == aOther.mListLink, + "comparing iterators over different lists"); + return mCurrent == aOther.mCurrent; + } + bool operator!=(const iterator_self_type aOther) const { + MOZ_ASSERT(mListLink); + NS_ASSERTION(mListLink == aOther.mListLink, + "comparing iterators over different lists"); + return mCurrent != aOther.mCurrent; + } + bool operator==(const iterator_self_type aOther) { + MOZ_ASSERT(mListLink); + NS_ASSERTION(mListLink == aOther.mListLink, + "comparing iterators over different lists"); + return mCurrent == aOther.mCurrent; + } + bool operator!=(const iterator_self_type aOther) { + MOZ_ASSERT(mListLink); + NS_ASSERTION(mListLink == aOther.mListLink, + "comparing iterators over different lists"); + return mCurrent != aOther.mCurrent; + } + +#ifdef DEBUG + bool IsInSameList(const iterator_self_type aOther) const { + return mListLink == aOther.mListLink; + } +#endif + + private: + link_type* mCurrent; +#ifdef DEBUG + link_type* mListLink; // the list's link, i.e., the end +#endif +}; + +class nsLineList_const_iterator { + public: + friend class nsLineList; + friend class nsLineList_iterator; + friend class nsLineList_reverse_iterator; + friend class nsLineList_const_reverse_iterator; + + typedef nsLineList_const_iterator iterator_self_type; + typedef nsLineList_const_reverse_iterator iterator_reverse_type; + typedef nsLineList_iterator iterator_nonconst_type; + typedef nsLineList_reverse_iterator iterator_nonconst_reverse_type; + + typedef nsLineBox& reference; + typedef const nsLineBox& const_reference; + + typedef nsLineBox* pointer; + typedef const nsLineBox* const_pointer; + + typedef uint32_t size_type; + typedef int32_t difference_type; + + typedef nsLineLink link_type; + +#ifdef DEBUG + nsLineList_const_iterator() : mListLink(nullptr) { + memset(&mCurrent, 0xcd, sizeof(mCurrent)); + } +#else + // Auto generated default constructor OK. +#endif + // Auto generated copy-constructor OK. + + inline iterator_self_type& operator=(const iterator_nonconst_type& aOther); + inline iterator_self_type& operator=( + const iterator_nonconst_reverse_type& aOther); + inline iterator_self_type& operator=(const iterator_self_type& aOther); + inline iterator_self_type& operator=(const iterator_reverse_type& aOther); + + iterator_self_type& operator++() { + mCurrent = mCurrent->_mNext; + return *this; + } + + iterator_self_type operator++(int) { + iterator_self_type rv(*this); + mCurrent = mCurrent->_mNext; + return rv; + } + + iterator_self_type& operator--() { + mCurrent = mCurrent->_mPrev; + return *this; + } + + iterator_self_type operator--(int) { + iterator_self_type rv(*this); + mCurrent = mCurrent->_mPrev; + return rv; + } + + const_reference operator*() const { + MOZ_ASSERT(mListLink); + MOZ_ASSERT(mCurrent != mListLink, "running past end"); + return *static_cast<const_pointer>(mCurrent); + } + + const_pointer operator->() const { + MOZ_ASSERT(mListLink); + MOZ_ASSERT(mCurrent != mListLink, "running past end"); + return static_cast<const_pointer>(mCurrent); + } + + const_pointer get() const { + MOZ_ASSERT(mListLink); + MOZ_ASSERT(mCurrent != mListLink, "running past end"); + return static_cast<const_pointer>(mCurrent); + } + +#ifndef __MWERKS__ + operator const_pointer() const { + MOZ_ASSERT(mListLink); + MOZ_ASSERT(mCurrent != mListLink, "running past end"); + return static_cast<const_pointer>(mCurrent); + } +#endif /* !__MWERKS__ */ + + const iterator_self_type next() const { + iterator_self_type copy(*this); + return ++copy; + } + + const iterator_self_type prev() const { + iterator_self_type copy(*this); + return --copy; + } + + // Passing by value rather than by reference and reference to const + // to keep AIX happy. + bool operator==(const iterator_self_type aOther) const { + MOZ_ASSERT(mListLink); + NS_ASSERTION(mListLink == aOther.mListLink, + "comparing iterators over different lists"); + return mCurrent == aOther.mCurrent; + } + bool operator!=(const iterator_self_type aOther) const { + MOZ_ASSERT(mListLink); + NS_ASSERTION(mListLink == aOther.mListLink, + "comparing iterators over different lists"); + return mCurrent != aOther.mCurrent; + } + bool operator==(const iterator_self_type aOther) { + MOZ_ASSERT(mListLink); + NS_ASSERTION(mListLink == aOther.mListLink, + "comparing iterators over different lists"); + return mCurrent == aOther.mCurrent; + } + bool operator!=(const iterator_self_type aOther) { + MOZ_ASSERT(mListLink); + NS_ASSERTION(mListLink == aOther.mListLink, + "comparing iterators over different lists"); + return mCurrent != aOther.mCurrent; + } + +#ifdef DEBUG + bool IsInSameList(const iterator_self_type aOther) const { + return mListLink == aOther.mListLink; + } +#endif + + private: + const link_type* mCurrent; +#ifdef DEBUG + const link_type* mListLink; // the list's link, i.e., the end +#endif +}; + +class nsLineList_const_reverse_iterator { + public: + friend class nsLineList; + friend class nsLineList_iterator; + friend class nsLineList_reverse_iterator; + friend class nsLineList_const_iterator; + + typedef nsLineList_const_reverse_iterator iterator_self_type; + typedef nsLineList_const_iterator iterator_reverse_type; + typedef nsLineList_iterator iterator_nonconst_reverse_type; + typedef nsLineList_reverse_iterator iterator_nonconst_type; + + typedef nsLineBox& reference; + typedef const nsLineBox& const_reference; + + typedef nsLineBox* pointer; + typedef const nsLineBox* const_pointer; + + typedef uint32_t size_type; + typedef int32_t difference_type; + + typedef nsLineLink link_type; + +#ifdef DEBUG + nsLineList_const_reverse_iterator() : mListLink(nullptr) { + memset(&mCurrent, 0xcd, sizeof(mCurrent)); + } +#else + // Auto generated default constructor OK. +#endif + // Auto generated copy-constructor OK. + + inline iterator_self_type& operator=(const iterator_nonconst_type& aOther); + inline iterator_self_type& operator=( + const iterator_nonconst_reverse_type& aOther); + inline iterator_self_type& operator=(const iterator_self_type& aOther); + inline iterator_self_type& operator=(const iterator_reverse_type& aOther); + + iterator_self_type& operator++() { + mCurrent = mCurrent->_mPrev; + return *this; + } + + iterator_self_type operator++(int) { + iterator_self_type rv(*this); + mCurrent = mCurrent->_mPrev; + return rv; + } + + iterator_self_type& operator--() { + mCurrent = mCurrent->_mNext; + return *this; + } + + iterator_self_type operator--(int) { + iterator_self_type rv(*this); + mCurrent = mCurrent->_mNext; + return rv; + } + + const_reference operator*() const { + MOZ_ASSERT(mListLink); + MOZ_ASSERT(mCurrent != mListLink, "running past end"); + return *static_cast<const_pointer>(mCurrent); + } + + const_pointer operator->() const { + MOZ_ASSERT(mListLink); + MOZ_ASSERT(mCurrent != mListLink, "running past end"); + return static_cast<const_pointer>(mCurrent); + } + + const_pointer get() const { + MOZ_ASSERT(mListLink); + MOZ_ASSERT(mCurrent != mListLink, "running past end"); + return static_cast<const_pointer>(mCurrent); + } + +#ifndef __MWERKS__ + operator const_pointer() const { + MOZ_ASSERT(mListLink); + MOZ_ASSERT(mCurrent != mListLink, "running past end"); + return static_cast<const_pointer>(mCurrent); + } +#endif /* !__MWERKS__ */ + + // Passing by value rather than by reference and reference to const + // to keep AIX happy. + bool operator==(const iterator_self_type aOther) const { + MOZ_ASSERT(mListLink); + NS_ASSERTION(mListLink == aOther.mListLink, + "comparing iterators over different lists"); + return mCurrent == aOther.mCurrent; + } + bool operator!=(const iterator_self_type aOther) const { + MOZ_ASSERT(mListLink); + NS_ASSERTION(mListLink == aOther.mListLink, + "comparing iterators over different lists"); + return mCurrent != aOther.mCurrent; + } + bool operator==(const iterator_self_type aOther) { + MOZ_ASSERT(mListLink); + NS_ASSERTION(mListLink == aOther.mListLink, + "comparing iterators over different lists"); + return mCurrent == aOther.mCurrent; + } + bool operator!=(const iterator_self_type aOther) { + MOZ_ASSERT(mListLink); + NS_ASSERTION(mListLink == aOther.mListLink, + "comparing iterators over different lists"); + return mCurrent != aOther.mCurrent; + } + +#ifdef DEBUG + bool IsInSameList(const iterator_self_type aOther) const { + return mListLink == aOther.mListLink; + } +#endif + + // private: + const link_type* mCurrent; +#ifdef DEBUG + const link_type* mListLink; // the list's link, i.e., the end +#endif +}; + +class nsLineList { + public: + friend class nsLineList_iterator; + friend class nsLineList_reverse_iterator; + friend class nsLineList_const_iterator; + friend class nsLineList_const_reverse_iterator; + + typedef uint32_t size_type; + typedef int32_t difference_type; + + typedef nsLineLink link_type; + + private: + link_type mLink; + + public: + typedef nsLineList self_type; + + typedef nsLineBox& reference; + typedef const nsLineBox& const_reference; + + typedef nsLineBox* pointer; + typedef const nsLineBox* const_pointer; + + typedef nsLineList_iterator iterator; + typedef nsLineList_reverse_iterator reverse_iterator; + typedef nsLineList_const_iterator const_iterator; + typedef nsLineList_const_reverse_iterator const_reverse_iterator; + + nsLineList() { + MOZ_COUNT_CTOR(nsLineList); + clear(); + } + + MOZ_COUNTED_DTOR(nsLineList) + + const_iterator begin() const { + const_iterator rv; + rv.mCurrent = mLink._mNext; +#ifdef DEBUG + rv.mListLink = &mLink; +#endif + return rv; + } + + iterator begin() { + iterator rv; + rv.mCurrent = mLink._mNext; +#ifdef DEBUG + rv.mListLink = &mLink; +#endif + return rv; + } + + iterator begin(nsLineBox* aLine) { + iterator rv; + rv.mCurrent = aLine; +#ifdef DEBUG + rv.mListLink = &mLink; +#endif + return rv; + } + + const_iterator end() const { + const_iterator rv; + rv.mCurrent = &mLink; +#ifdef DEBUG + rv.mListLink = &mLink; +#endif + return rv; + } + + iterator end() { + iterator rv; + rv.mCurrent = &mLink; +#ifdef DEBUG + rv.mListLink = &mLink; +#endif + return rv; + } + + const_reverse_iterator rbegin() const { + const_reverse_iterator rv; + rv.mCurrent = mLink._mPrev; +#ifdef DEBUG + rv.mListLink = &mLink; +#endif + return rv; + } + + reverse_iterator rbegin() { + reverse_iterator rv; + rv.mCurrent = mLink._mPrev; +#ifdef DEBUG + rv.mListLink = &mLink; +#endif + return rv; + } + + reverse_iterator rbegin(nsLineBox* aLine) { + reverse_iterator rv; + rv.mCurrent = aLine; +#ifdef DEBUG + rv.mListLink = &mLink; +#endif + return rv; + } + + const_reverse_iterator rend() const { + const_reverse_iterator rv; + rv.mCurrent = &mLink; +#ifdef DEBUG + rv.mListLink = &mLink; +#endif + return rv; + } + + reverse_iterator rend() { + reverse_iterator rv; + rv.mCurrent = &mLink; +#ifdef DEBUG + rv.mListLink = &mLink; +#endif + return rv; + } + + bool empty() const { return mLink._mNext == &mLink; } + + // NOTE: O(N). + size_type size() const { + size_type count = 0; + for (const link_type* cur = mLink._mNext; cur != &mLink; + cur = cur->_mNext) { + ++count; + } + return count; + } + + pointer front() { + NS_ASSERTION(!empty(), "no element to return"); + return static_cast<pointer>(mLink._mNext); + } + + const_pointer front() const { + NS_ASSERTION(!empty(), "no element to return"); + return static_cast<const_pointer>(mLink._mNext); + } + + pointer back() { + NS_ASSERTION(!empty(), "no element to return"); + return static_cast<pointer>(mLink._mPrev); + } + + const_pointer back() const { + NS_ASSERTION(!empty(), "no element to return"); + return static_cast<const_pointer>(mLink._mPrev); + } + + void push_front(pointer aNew) { + aNew->_mNext = mLink._mNext; + mLink._mNext->_mPrev = aNew; + aNew->_mPrev = &mLink; + mLink._mNext = aNew; + } + + void pop_front() + // NOTE: leaves dangling next/prev pointers + { + NS_ASSERTION(!empty(), "no element to pop"); + link_type* newFirst = mLink._mNext->_mNext; + newFirst->_mPrev = &mLink; + // mLink._mNext->_mNext = nullptr; + // mLink._mNext->_mPrev = nullptr; + mLink._mNext = newFirst; + } + + void push_back(pointer aNew) { + aNew->_mPrev = mLink._mPrev; + mLink._mPrev->_mNext = aNew; + aNew->_mNext = &mLink; + mLink._mPrev = aNew; + } + + void pop_back() + // NOTE: leaves dangling next/prev pointers + { + NS_ASSERTION(!empty(), "no element to pop"); + link_type* newLast = mLink._mPrev->_mPrev; + newLast->_mNext = &mLink; + // mLink._mPrev->_mPrev = nullptr; + // mLink._mPrev->_mNext = nullptr; + mLink._mPrev = newLast; + } + + // inserts x before position + iterator before_insert(iterator position, pointer x) { + // use |mCurrent| to prevent DEBUG_PASS_END assertions + x->_mPrev = position.mCurrent->_mPrev; + x->_mNext = position.mCurrent; + position.mCurrent->_mPrev->_mNext = x; + position.mCurrent->_mPrev = x; + return --position; + } + + // inserts x after position + iterator after_insert(iterator position, pointer x) { + // use |mCurrent| to prevent DEBUG_PASS_END assertions + x->_mNext = position.mCurrent->_mNext; + x->_mPrev = position.mCurrent; + position.mCurrent->_mNext->_mPrev = x; + position.mCurrent->_mNext = x; + return ++position; + } + + // returns iterator pointing to after the element + iterator erase(iterator position) + // NOTE: leaves dangling next/prev pointers + { + position->_mPrev->_mNext = position->_mNext; + position->_mNext->_mPrev = position->_mPrev; + return ++position; + } + + void swap(self_type& y) { + link_type tmp(y.mLink); + y.mLink = mLink; + mLink = tmp; + + if (!empty()) { + mLink._mNext->_mPrev = &mLink; + mLink._mPrev->_mNext = &mLink; + } + + if (!y.empty()) { + y.mLink._mNext->_mPrev = &y.mLink; + y.mLink._mPrev->_mNext = &y.mLink; + } + } + + void clear() + // NOTE: leaves dangling next/prev pointers + { + mLink._mNext = &mLink; + mLink._mPrev = &mLink; + } + + // inserts the conts of x before position and makes x empty + void splice(iterator position, self_type& x) { + // use |mCurrent| to prevent DEBUG_PASS_END assertions + position.mCurrent->_mPrev->_mNext = x.mLink._mNext; + x.mLink._mNext->_mPrev = position.mCurrent->_mPrev; + x.mLink._mPrev->_mNext = position.mCurrent; + position.mCurrent->_mPrev = x.mLink._mPrev; + x.clear(); + } + + // Inserts element *i from list x before position and removes + // it from x. + void splice(iterator position, self_type& x, iterator i) { + NS_ASSERTION(!x.empty(), "Can't insert from empty list."); + NS_ASSERTION(position != i && position.mCurrent != i->_mNext, + "We don't check for this case."); + + // remove from |x| + i->_mPrev->_mNext = i->_mNext; + i->_mNext->_mPrev = i->_mPrev; + + // use |mCurrent| to prevent DEBUG_PASS_END assertions + // link into |this|, before-side + i->_mPrev = position.mCurrent->_mPrev; + position.mCurrent->_mPrev->_mNext = i.get(); + + // link into |this|, after-side + i->_mNext = position.mCurrent; + position.mCurrent->_mPrev = i.get(); + } + + // Inserts elements in [|first|, |last|), which are in |x|, + // into |this| before |position| and removes them from |x|. + void splice(iterator position, self_type& x, iterator first, iterator last) { + NS_ASSERTION(!x.empty(), "Can't insert from empty list."); + + if (first == last) return; + + --last; // so we now want to move [first, last] + // remove from |x| + first->_mPrev->_mNext = last->_mNext; + last->_mNext->_mPrev = first->_mPrev; + + // use |mCurrent| to prevent DEBUG_PASS_END assertions + // link into |this|, before-side + first->_mPrev = position.mCurrent->_mPrev; + position.mCurrent->_mPrev->_mNext = first.get(); + + // link into |this|, after-side + last->_mNext = position.mCurrent; + position.mCurrent->_mPrev = last.get(); + } +}; + +// Many of these implementations of operator= don't work yet. I don't +// know why. + +#ifdef DEBUG + +// NOTE: ASSIGN_FROM is meant to be used *only* as the entire body +// of a function and therefore lacks PR_{BEGIN,END}_MACRO +# define ASSIGN_FROM(other_) \ + mCurrent = other_.mCurrent; \ + mListLink = other_.mListLink; \ + return *this; + +#else /* !NS_LINELIST_DEBUG_PASS_END */ + +# define ASSIGN_FROM(other_) \ + mCurrent = other_.mCurrent; \ + return *this; + +#endif /* !NS_LINELIST_DEBUG_PASS_END */ + +inline nsLineList_iterator& nsLineList_iterator::operator=( + const nsLineList_iterator& aOther) = default; + +inline nsLineList_iterator& nsLineList_iterator::operator=( + const nsLineList_reverse_iterator& aOther) { + ASSIGN_FROM(aOther) +} + +inline nsLineList_reverse_iterator& nsLineList_reverse_iterator::operator=( + const nsLineList_iterator& aOther) { + ASSIGN_FROM(aOther) +} + +inline nsLineList_reverse_iterator& nsLineList_reverse_iterator::operator=( + const nsLineList_reverse_iterator& aOther) = default; + +inline nsLineList_const_iterator& nsLineList_const_iterator::operator=( + const nsLineList_iterator& aOther) { + ASSIGN_FROM(aOther) +} + +inline nsLineList_const_iterator& nsLineList_const_iterator::operator=( + const nsLineList_reverse_iterator& aOther) { + ASSIGN_FROM(aOther) +} + +inline nsLineList_const_iterator& nsLineList_const_iterator::operator=( + const nsLineList_const_iterator& aOther) = default; + +inline nsLineList_const_iterator& nsLineList_const_iterator::operator=( + const nsLineList_const_reverse_iterator& aOther) { + ASSIGN_FROM(aOther) +} + +inline nsLineList_const_reverse_iterator& +nsLineList_const_reverse_iterator::operator=( + const nsLineList_iterator& aOther) { + ASSIGN_FROM(aOther) +} + +inline nsLineList_const_reverse_iterator& +nsLineList_const_reverse_iterator::operator=( + const nsLineList_reverse_iterator& aOther) { + ASSIGN_FROM(aOther) +} + +inline nsLineList_const_reverse_iterator& +nsLineList_const_reverse_iterator::operator=( + const nsLineList_const_iterator& aOther) { + ASSIGN_FROM(aOther) +} + +inline nsLineList_const_reverse_iterator& +nsLineList_const_reverse_iterator::operator=( + const nsLineList_const_reverse_iterator& aOther) = default; + +//---------------------------------------------------------------------- + +class nsLineIterator final : public nsILineIterator { + public: + nsLineIterator(); + ~nsLineIterator(); + + virtual void DisposeLineIterator() override; + + virtual int32_t GetNumLines() const override; + virtual bool GetDirection() override; + + mozilla::Result<LineInfo, nsresult> GetLine( + int32_t aLineNumber) const override; + virtual int32_t FindLineContaining(nsIFrame* aFrame, + int32_t aStartLine = 0) override; + NS_IMETHOD FindFrameAt(int32_t aLineNumber, nsPoint aPos, + nsIFrame** aFrameFound, bool* aPosIsBeforeFirstFrame, + bool* aPosIsAfterLastFrame) const override; + + NS_IMETHOD GetNextSiblingOnLine(nsIFrame*& aFrame, + int32_t aLineNumber) const override; + NS_IMETHOD CheckLineOrder(int32_t aLine, bool* aIsReordered, + nsIFrame** aFirstVisual, + nsIFrame** aLastVisual) override; + nsresult Init(nsLineList& aLines, bool aRightToLeft); + + private: + nsLineBox* PrevLine() { + if (0 == mIndex) { + return nullptr; + } + return mLines[--mIndex]; + } + + nsLineBox* NextLine() { + if (mIndex >= mNumLines - 1) { + return nullptr; + } + return mLines[++mIndex]; + } + + nsLineBox* LineAt(int32_t aIndex) { + if ((aIndex < 0) || (aIndex >= mNumLines)) { + return nullptr; + } + return mLines[aIndex]; + } + + nsLineBox** mLines; + int32_t mIndex; + int32_t mNumLines; + bool mRightToLeft; +}; + +#endif /* nsLineBox_h___ */ |