1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4  * License, v. 2.0. If a copy of the MPL was not distributed with this
5  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 
7 /* representation of one line within a block frame, a CSS line box */
8 
9 #ifndef nsLineBox_h___
10 #define nsLineBox_h___
11 
12 #include "mozilla/Attributes.h"
13 #include "mozilla/Likely.h"
14 
15 #include "nsILineIterator.h"
16 #include "nsIFrame.h"
17 #include "nsTHashSet.h"
18 
19 #include <algorithm>
20 
21 class nsLineBox;
22 class nsFloatCache;
23 class nsFloatCacheList;
24 class nsFloatCacheFreeList;
25 class nsWindowSizes;
26 
27 namespace mozilla {
28 class PresShell;
29 }  // namespace mozilla
30 
31 // State cached after reflowing a float. This state is used during
32 // incremental reflow when we avoid reflowing a float.
33 class nsFloatCache {
34  public:
35   nsFloatCache();
36 #ifdef NS_BUILD_REFCNT_LOGGING
37   ~nsFloatCache();
38 #else
39   ~nsFloatCache() = default;
40 #endif
41 
Next()42   nsFloatCache* Next() const { return mNext; }
43 
44   nsIFrame* mFloat;  // floating frame
45 
46  protected:
47   nsFloatCache* mNext;
48 
49   friend class nsFloatCacheList;
50   friend class nsFloatCacheFreeList;
51 };
52 
53 //----------------------------------------
54 
55 class nsFloatCacheList {
56  public:
57 #ifdef NS_BUILD_REFCNT_LOGGING
58   nsFloatCacheList();
59 #else
60   nsFloatCacheList() : mHead(nullptr) {}
61 #endif
62   ~nsFloatCacheList();
63 
IsEmpty()64   bool IsEmpty() const { return nullptr == mHead; }
65 
NotEmpty()66   bool NotEmpty() const { return nullptr != mHead; }
67 
Head()68   nsFloatCache* Head() const { return mHead; }
69 
70   nsFloatCache* Tail() const;
71 
72   void DeleteAll();
73 
74   nsFloatCache* Find(nsIFrame* aOutOfFlowFrame);
75 
76   // Remove a nsFloatCache from this list.  Deleting this nsFloatCache
77   // becomes the caller's responsibility.
Remove(nsFloatCache * aElement)78   void Remove(nsFloatCache* aElement) { RemoveAndReturnPrev(aElement); }
79 
80   // Steal away aList's nsFloatCache objects and put them in this
81   // list.  aList must not be empty.
82   void Append(nsFloatCacheFreeList& aList);
83 
84  protected:
85   nsFloatCache* mHead;
86 
87   // Remove a nsFloatCache from this list.  Deleting this nsFloatCache
88   // becomes the caller's responsibility. Returns the nsFloatCache that was
89   // before aElement, or nullptr if aElement was the first.
90   nsFloatCache* RemoveAndReturnPrev(nsFloatCache* aElement);
91 
92   friend class nsFloatCacheFreeList;
93 };
94 
95 //---------------------------------------
96 // Like nsFloatCacheList, but with fast access to the tail
97 
98 class nsFloatCacheFreeList : private nsFloatCacheList {
99  public:
100 #ifdef NS_BUILD_REFCNT_LOGGING
101   nsFloatCacheFreeList();
102   ~nsFloatCacheFreeList();
103 #else
104   nsFloatCacheFreeList() : mTail(nullptr) {}
105   ~nsFloatCacheFreeList() = default;
106 #endif
107 
108   // Reimplement trivial functions
IsEmpty()109   bool IsEmpty() const { return nullptr == mHead; }
110 
Head()111   nsFloatCache* Head() const { return mHead; }
112 
Tail()113   nsFloatCache* Tail() const { return mTail; }
114 
NotEmpty()115   bool NotEmpty() const { return nullptr != mHead; }
116 
117   void DeleteAll();
118 
119   // Steal away aList's nsFloatCache objects and put them on this
120   // free-list.  aList must not be empty.
121   void Append(nsFloatCacheList& aList);
122 
123   void Append(nsFloatCache* aFloatCache);
124 
125   void Remove(nsFloatCache* aElement);
126 
127   // Remove an nsFloatCache object from this list and return it, or create
128   // a new one if this one is empty; Set its mFloat to aFloat.
129   nsFloatCache* Alloc(nsIFrame* aFloat);
130 
131  protected:
132   nsFloatCache* mTail;
133 
134   friend class nsFloatCacheList;
135 };
136 
137 //----------------------------------------------------------------------
138 
139 #define LINE_MAX_CHILD_COUNT INT32_MAX
140 
141 /**
142  * Function to create a line box and initialize it with a single frame.
143  * The allocation is infallible.
144  * If the frame was moved from another line then you're responsible
145  * for notifying that line using NoteFrameRemoved().  Alternatively,
146  * it's better to use the next function that does that for you in an
147  * optimal way.
148  */
149 nsLineBox* NS_NewLineBox(mozilla::PresShell* aPresShell, nsIFrame* aFrame,
150                          bool aIsBlock);
151 /**
152  * Function to create a line box and initialize it with aCount frames
153  * that are currently on aFromLine.  The allocation is infallible.
154  */
155 nsLineBox* NS_NewLineBox(mozilla::PresShell* aPresShell, nsLineBox* aFromLine,
156                          nsIFrame* aFrame, int32_t aCount);
157 
158 class nsLineList;
159 
160 // don't use the following names outside of this file.  Instead, use
161 // nsLineList::iterator, etc.  These are just here to allow them to
162 // be specified as parameters to methods of nsLineBox.
163 class nsLineList_iterator;
164 class nsLineList_const_iterator;
165 class nsLineList_reverse_iterator;
166 class nsLineList_const_reverse_iterator;
167 
168 /**
169  * Users must have the class that is to be part of the list inherit
170  * from nsLineLink.  If they want to be efficient, it should be the
171  * first base class.  (This was originally nsCLink in a templatized
172  * nsCList, but it's still useful separately.)
173  */
174 
175 class nsLineLink {
176  public:
177   friend class nsLineList;
178   friend class nsLineList_iterator;
179   friend class nsLineList_reverse_iterator;
180   friend class nsLineList_const_iterator;
181   friend class nsLineList_const_reverse_iterator;
182 
183  private:
184   nsLineLink* _mNext;  // or head
185   nsLineLink* _mPrev;  // or tail
186 };
187 
188 /**
189  * The nsLineBox class represents a horizontal line of frames. It contains
190  * enough state to support incremental reflow of the frames, event handling
191  * for the frames, and rendering of the frames.
192  */
193 class nsLineBox final : public nsLineLink {
194  private:
195   nsLineBox(nsIFrame* aFrame, int32_t aCount, bool aIsBlock);
196   ~nsLineBox();
197 
198   // Infallible overloaded new operator. Uses an arena (which comes from the
199   // presShell) to perform the allocation.
200   void* operator new(size_t sz, mozilla::PresShell* aPresShell);
201   void operator delete(void* aPtr, size_t sz) = delete;
202 
203  public:
204   // Use these functions to allocate and destroy line boxes
205   friend nsLineBox* NS_NewLineBox(mozilla::PresShell* aPresShell,
206                                   nsIFrame* aFrame, bool aIsBlock);
207   friend nsLineBox* NS_NewLineBox(mozilla::PresShell* aPresShell,
208                                   nsLineBox* aFromLine, nsIFrame* aFrame,
209                                   int32_t aCount);
210   void Destroy(mozilla::PresShell* aPresShell);
211 
212   // mBlock bit
IsBlock()213   bool IsBlock() const { return mFlags.mBlock; }
IsInline()214   bool IsInline() const { return !mFlags.mBlock; }
215 
216   // mDirty bit
MarkDirty()217   void MarkDirty() { mFlags.mDirty = 1; }
ClearDirty()218   void ClearDirty() { mFlags.mDirty = 0; }
IsDirty()219   bool IsDirty() const { return mFlags.mDirty; }
220 
221   // mPreviousMarginDirty bit
MarkPreviousMarginDirty()222   void MarkPreviousMarginDirty() { mFlags.mPreviousMarginDirty = 1; }
ClearPreviousMarginDirty()223   void ClearPreviousMarginDirty() { mFlags.mPreviousMarginDirty = 0; }
IsPreviousMarginDirty()224   bool IsPreviousMarginDirty() const { return mFlags.mPreviousMarginDirty; }
225 
226   // mHasClearance bit
SetHasClearance()227   void SetHasClearance() { mFlags.mHasClearance = 1; }
ClearHasClearance()228   void ClearHasClearance() { mFlags.mHasClearance = 0; }
HasClearance()229   bool HasClearance() const { return mFlags.mHasClearance; }
230 
231   // mImpactedByFloat bit
SetLineIsImpactedByFloat(bool aValue)232   void SetLineIsImpactedByFloat(bool aValue) {
233     mFlags.mImpactedByFloat = aValue;
234   }
IsImpactedByFloat()235   bool IsImpactedByFloat() const { return mFlags.mImpactedByFloat; }
236 
237   // mLineWrapped bit
SetLineWrapped(bool aOn)238   void SetLineWrapped(bool aOn) { mFlags.mLineWrapped = aOn; }
IsLineWrapped()239   bool IsLineWrapped() const { return mFlags.mLineWrapped; }
240 
241   // mInvalidateTextRuns bit
SetInvalidateTextRuns(bool aOn)242   void SetInvalidateTextRuns(bool aOn) { mFlags.mInvalidateTextRuns = aOn; }
GetInvalidateTextRuns()243   bool GetInvalidateTextRuns() const { return mFlags.mInvalidateTextRuns; }
244 
245   // mResizeReflowOptimizationDisabled bit
DisableResizeReflowOptimization()246   void DisableResizeReflowOptimization() {
247     mFlags.mResizeReflowOptimizationDisabled = true;
248   }
EnableResizeReflowOptimization()249   void EnableResizeReflowOptimization() {
250     mFlags.mResizeReflowOptimizationDisabled = false;
251   }
ResizeReflowOptimizationDisabled()252   bool ResizeReflowOptimizationDisabled() const {
253     return mFlags.mResizeReflowOptimizationDisabled;
254   }
255 
256   // mHasMarker bit
SetHasMarker()257   void SetHasMarker() {
258     mFlags.mHasMarker = true;
259     InvalidateCachedIsEmpty();
260   }
ClearHasMarker()261   void ClearHasMarker() {
262     mFlags.mHasMarker = false;
263     InvalidateCachedIsEmpty();
264   }
HasMarker()265   bool HasMarker() const { return mFlags.mHasMarker; }
266 
267   // mHadFloatPushed bit
SetHadFloatPushed()268   void SetHadFloatPushed() { mFlags.mHadFloatPushed = true; }
ClearHadFloatPushed()269   void ClearHadFloatPushed() { mFlags.mHadFloatPushed = false; }
HadFloatPushed()270   bool HadFloatPushed() const { return mFlags.mHadFloatPushed; }
271 
272   // mHasLineClampEllipsis bit
SetHasLineClampEllipsis()273   void SetHasLineClampEllipsis() { mFlags.mHasLineClampEllipsis = true; }
ClearHasLineClampEllipsis()274   void ClearHasLineClampEllipsis() { mFlags.mHasLineClampEllipsis = false; }
HasLineClampEllipsis()275   bool HasLineClampEllipsis() const { return mFlags.mHasLineClampEllipsis; }
276 
277   // mMovedFragments bit
SetMovedFragments()278   void SetMovedFragments() { mFlags.mMovedFragments = true; }
ClearMovedFragments()279   void ClearMovedFragments() { mFlags.mMovedFragments = false; }
MovedFragments()280   bool MovedFragments() const { return mFlags.mMovedFragments; }
281 
282  private:
283   // Add a hash table for fast lookup when the line has more frames than this.
284   static const uint32_t kMinChildCountForHashtable = 200;
285 
286   /**
287    * Take ownership of aFromLine's hash table and remove the frames that
288    * stay on aFromLine from it, i.e. aFromLineNewCount frames starting with
289    * mFirstChild.  This method is used to optimize moving a large number
290    * of frames from one line to the next.
291    */
292   void StealHashTableFrom(nsLineBox* aFromLine, uint32_t aFromLineNewCount);
293 
294   /**
295    * Does the equivalent of this->NoteFrameAdded and aFromLine->NoteFrameRemoved
296    * for each frame on this line, but in a optimized way.
297    */
298   void NoteFramesMovedFrom(nsLineBox* aFromLine);
299 
SwitchToHashtable()300   void SwitchToHashtable() {
301     MOZ_ASSERT(!mFlags.mHasHashedFrames);
302     uint32_t count = GetChildCount();
303     mFlags.mHasHashedFrames = 1;
304     uint32_t minLength =
305         std::max(kMinChildCountForHashtable,
306                  uint32_t(PLDHashTable::kDefaultInitialLength));
307     mFrames = new nsTHashSet<nsIFrame*>(std::max(count, minLength));
308     for (nsIFrame* f = mFirstChild; count-- > 0; f = f->GetNextSibling()) {
309       mFrames->Insert(f);
310     }
311   }
SwitchToCounter()312   void SwitchToCounter() {
313     MOZ_ASSERT(mFlags.mHasHashedFrames);
314     uint32_t count = GetChildCount();
315     delete mFrames;
316     mFlags.mHasHashedFrames = 0;
317     mChildCount = count;
318   }
319 
320  public:
GetChildCount()321   int32_t GetChildCount() const {
322     return MOZ_UNLIKELY(mFlags.mHasHashedFrames) ? mFrames->Count()
323                                                  : mChildCount;
324   }
325 
326   /**
327    * Register that aFrame is now on this line.
328    */
NoteFrameAdded(nsIFrame * aFrame)329   void NoteFrameAdded(nsIFrame* aFrame) {
330     if (MOZ_UNLIKELY(mFlags.mHasHashedFrames)) {
331       mFrames->Insert(aFrame);
332     } else {
333       if (++mChildCount >= kMinChildCountForHashtable) {
334         SwitchToHashtable();
335       }
336     }
337   }
338 
339   /**
340    * Register that aFrame is not on this line anymore.
341    */
NoteFrameRemoved(nsIFrame * aFrame)342   void NoteFrameRemoved(nsIFrame* aFrame) {
343     MOZ_ASSERT(GetChildCount() > 0);
344     if (MOZ_UNLIKELY(mFlags.mHasHashedFrames)) {
345       mFrames->Remove(aFrame);
346       if (mFrames->Count() < kMinChildCountForHashtable) {
347         SwitchToCounter();
348       }
349     } else {
350       --mChildCount;
351     }
352   }
353 
354   // mBreakType value
355   // Break information is applied *before* the line if the line is a block,
356   // or *after* the line if the line is an inline. Confusing, I know, but
357   // using different names should help.
358   using StyleClear = mozilla::StyleClear;
HasBreakBefore()359   bool HasBreakBefore() const {
360     return IsBlock() && StyleClear::None != BreakType();
361   }
SetBreakTypeBefore(StyleClear aBreakType)362   void SetBreakTypeBefore(StyleClear aBreakType) {
363     MOZ_ASSERT(IsBlock(), "Only blocks have break-before");
364     MOZ_ASSERT(
365         aBreakType == StyleClear::None || aBreakType == StyleClear::Left ||
366             aBreakType == StyleClear::Right || aBreakType == StyleClear::Both,
367         "Only float break types are allowed before a line");
368     mFlags.mBreakType = aBreakType;
369   }
GetBreakTypeBefore()370   StyleClear GetBreakTypeBefore() const {
371     return IsBlock() ? BreakType() : StyleClear::None;
372   }
373 
HasBreakAfter()374   bool HasBreakAfter() const {
375     return !IsBlock() && StyleClear::None != BreakType();
376   }
SetBreakTypeAfter(StyleClear aBreakType)377   void SetBreakTypeAfter(StyleClear aBreakType) {
378     MOZ_ASSERT(!IsBlock(), "Only inlines have break-after");
379     mFlags.mBreakType = aBreakType;
380   }
HasFloatBreakAfter()381   bool HasFloatBreakAfter() const {
382     return !IsBlock() && (StyleClear::Left == BreakType() ||
383                           StyleClear::Right == BreakType() ||
384                           StyleClear::Both == BreakType());
385   }
GetBreakTypeAfter()386   StyleClear GetBreakTypeAfter() const {
387     return !IsBlock() ? BreakType() : StyleClear::None;
388   }
389 
390   // mCarriedOutBEndMargin value
391   nsCollapsingMargin GetCarriedOutBEndMargin() const;
392   // Returns true if the margin changed
393   bool SetCarriedOutBEndMargin(nsCollapsingMargin aValue);
394 
395   // mFloats
HasFloats()396   bool HasFloats() const {
397     return (IsInline() && mInlineData) && mInlineData->mFloats.NotEmpty();
398   }
399   nsFloatCache* GetFirstFloat();
400   void FreeFloats(nsFloatCacheFreeList& aFreeList);
401   void AppendFloats(nsFloatCacheFreeList& aFreeList);
402   bool RemoveFloat(nsIFrame* aFrame);
403 
404   // The ink overflow area should never be used for things that affect layout.
405   // The scrollable overflow area are permitted to affect layout for handling of
406   // overflow and scrollbars.
407   void SetOverflowAreas(const mozilla::OverflowAreas& aOverflowAreas);
GetOverflowArea(mozilla::OverflowType aType,mozilla::WritingMode aWM,const nsSize & aContainerSize)408   mozilla::LogicalRect GetOverflowArea(mozilla::OverflowType aType,
409                                        mozilla::WritingMode aWM,
410                                        const nsSize& aContainerSize) {
411     return mozilla::LogicalRect(aWM, GetOverflowArea(aType), aContainerSize);
412   }
GetOverflowArea(mozilla::OverflowType aType)413   nsRect GetOverflowArea(mozilla::OverflowType aType) const {
414     return mData ? mData->mOverflowAreas.Overflow(aType) : GetPhysicalBounds();
415   }
GetOverflowAreas()416   mozilla::OverflowAreas GetOverflowAreas() const {
417     if (mData) {
418       return mData->mOverflowAreas;
419     }
420     nsRect bounds = GetPhysicalBounds();
421     return mozilla::OverflowAreas(bounds, bounds);
422   }
InkOverflowRect()423   nsRect InkOverflowRect() const {
424     return GetOverflowArea(mozilla::OverflowType::Ink);
425   }
ScrollableOverflowRect()426   nsRect ScrollableOverflowRect() {
427     return GetOverflowArea(mozilla::OverflowType::Scrollable);
428   }
429 
SlideBy(nscoord aDBCoord,const nsSize & aContainerSize)430   void SlideBy(nscoord aDBCoord, const nsSize& aContainerSize) {
431     NS_ASSERTION(
432         aContainerSize == mContainerSize || mContainerSize == nsSize(-1, -1),
433         "container size doesn't match");
434     mContainerSize = aContainerSize;
435     mBounds.BStart(mWritingMode) += aDBCoord;
436     if (mData) {
437       // Use a null containerSize to convert vector from logical to physical.
438       const nsSize nullContainerSize;
439       nsPoint physicalDelta =
440           mozilla::LogicalPoint(mWritingMode, 0, aDBCoord)
441               .GetPhysicalPoint(mWritingMode, nullContainerSize);
442       for (const auto otype : mozilla::AllOverflowTypes()) {
443         mData->mOverflowAreas.Overflow(otype) += physicalDelta;
444       }
445     }
446   }
447 
448   // Container-size for the line is changing (and therefore if writing mode
449   // was vertical-rl, the line will move physically; this is like SlideBy,
450   // but it is the container size instead of the line's own logical coord
451   // that is changing.
UpdateContainerSize(const nsSize aNewContainerSize)452   nsSize UpdateContainerSize(const nsSize aNewContainerSize) {
453     NS_ASSERTION(mContainerSize != nsSize(-1, -1), "container size not set");
454     nsSize delta = mContainerSize - aNewContainerSize;
455     mContainerSize = aNewContainerSize;
456     // this has a physical-coordinate effect only in vertical-rl mode
457     if (mWritingMode.IsVerticalRL() && mData) {
458       nsPoint physicalDelta(-delta.width, 0);
459       for (const auto otype : mozilla::AllOverflowTypes()) {
460         mData->mOverflowAreas.Overflow(otype) += physicalDelta;
461       }
462     }
463     return delta;
464   }
465 
IndentBy(nscoord aDICoord,const nsSize & aContainerSize)466   void IndentBy(nscoord aDICoord, const nsSize& aContainerSize) {
467     NS_ASSERTION(
468         aContainerSize == mContainerSize || mContainerSize == nsSize(-1, -1),
469         "container size doesn't match");
470     mContainerSize = aContainerSize;
471     mBounds.IStart(mWritingMode) += aDICoord;
472   }
473 
ExpandBy(nscoord aDISize,const nsSize & aContainerSize)474   void ExpandBy(nscoord aDISize, const nsSize& aContainerSize) {
475     NS_ASSERTION(
476         aContainerSize == mContainerSize || mContainerSize == nsSize(-1, -1),
477         "container size doesn't match");
478     mContainerSize = aContainerSize;
479     mBounds.ISize(mWritingMode) += aDISize;
480   }
481 
482   /**
483    * The logical ascent (distance from block-start to baseline) of the
484    * linebox is the logical ascent of the anonymous inline box (for
485    * which we don't actually create a frame) that wraps all the
486    * consecutive inline children of a block.
487    *
488    * This is currently unused for block lines.
489    */
GetLogicalAscent()490   nscoord GetLogicalAscent() const { return mAscent; }
SetLogicalAscent(nscoord aAscent)491   void SetLogicalAscent(nscoord aAscent) { mAscent = aAscent; }
492 
BStart()493   nscoord BStart() const { return mBounds.BStart(mWritingMode); }
BSize()494   nscoord BSize() const { return mBounds.BSize(mWritingMode); }
BEnd()495   nscoord BEnd() const { return mBounds.BEnd(mWritingMode); }
IStart()496   nscoord IStart() const { return mBounds.IStart(mWritingMode); }
ISize()497   nscoord ISize() const { return mBounds.ISize(mWritingMode); }
IEnd()498   nscoord IEnd() const { return mBounds.IEnd(mWritingMode); }
SetBoundsEmpty()499   void SetBoundsEmpty() {
500     mBounds.IStart(mWritingMode) = 0;
501     mBounds.ISize(mWritingMode) = 0;
502     mBounds.BStart(mWritingMode) = 0;
503     mBounds.BSize(mWritingMode) = 0;
504   }
505 
506   using PostDestroyData = nsIFrame::PostDestroyData;
507   static void DeleteLineList(nsPresContext* aPresContext, nsLineList& aLines,
508                              nsIFrame* aDestructRoot, nsFrameList* aFrames,
509                              PostDestroyData& aPostDestroyData);
510 
511   // search from end to beginning of [aBegin, aEnd)
512   // Returns true if it found the line and false if not.
513   // Moves aEnd as it searches so that aEnd points to the resulting line.
514   // aLastFrameBeforeEnd is the last frame before aEnd (so if aEnd is
515   // the end of the line list, it's just the last frame in the frame
516   // list).
517   static bool RFindLineContaining(nsIFrame* aFrame,
518                                   const nsLineList_iterator& aBegin,
519                                   nsLineList_iterator& aEnd,
520                                   nsIFrame* aLastFrameBeforeEnd,
521                                   int32_t* aFrameIndexInLine);
522 
523 #ifdef DEBUG_FRAME_DUMP
524   static const char* BreakTypeToString(StyleClear aBreakType);
525   char* StateToString(char* aBuf, int32_t aBufSize) const;
526 
527   void List(FILE* out, int32_t aIndent,
528             nsIFrame::ListFlags aFlags = nsIFrame::ListFlags()) const;
529   void List(FILE* out = stderr, const char* aPrefix = "",
530             nsIFrame::ListFlags aFlags = nsIFrame::ListFlags()) const;
531   nsIFrame* LastChild() const;
532 #endif
533 
534   void AddSizeOfExcludingThis(nsWindowSizes& aSizes) const;
535 
536   // Find the index of aFrame within the line, starting search at the start.
537   int32_t IndexOf(nsIFrame* aFrame) const;
538 
539   // Find the index of aFrame within the line, starting search at the end.
540   // (Produces the same result as IndexOf, but with different performance
541   // characteristics.)  The caller must provide the last frame in the line.
542   int32_t RIndexOf(nsIFrame* aFrame, nsIFrame* aLastFrameInLine) const;
543 
Contains(nsIFrame * aFrame)544   bool Contains(nsIFrame* aFrame) const {
545     return MOZ_UNLIKELY(mFlags.mHasHashedFrames) ? mFrames->Contains(aFrame)
546                                                  : IndexOf(aFrame) >= 0;
547   }
548 
549   // whether the line box is "logically" empty (just like nsIFrame::IsEmpty)
550   bool IsEmpty() const;
551 
552   // Call this only while in Reflow() for the block the line belongs
553   // to, only between reflowing the line (or sliding it, if we skip
554   // reflowing it) and the end of reflowing the block.
555   bool CachedIsEmpty();
556 
InvalidateCachedIsEmpty()557   void InvalidateCachedIsEmpty() { mFlags.mEmptyCacheValid = false; }
558 
559   // For debugging purposes
IsValidCachedIsEmpty()560   bool IsValidCachedIsEmpty() { return mFlags.mEmptyCacheValid; }
561 
562 #ifdef DEBUG
563   static int32_t GetCtorCount();
564 #endif
565 
566   nsIFrame* mFirstChild;
567 
568   mozilla::WritingMode mWritingMode;
569 
570   // Physical size. Use only for physical <-> logical coordinate conversion.
571   nsSize mContainerSize;
572 
573  private:
574   mozilla::LogicalRect mBounds;
575 
576  public:
GetBounds()577   const mozilla::LogicalRect& GetBounds() { return mBounds; }
GetPhysicalBounds()578   nsRect GetPhysicalBounds() const {
579     if (mBounds.IsAllZero()) {
580       return nsRect(0, 0, 0, 0);
581     }
582 
583     NS_ASSERTION(mContainerSize != nsSize(-1, -1),
584                  "mContainerSize not initialized");
585     return mBounds.GetPhysicalRect(mWritingMode, mContainerSize);
586   }
SetBounds(mozilla::WritingMode aWritingMode,nscoord aIStart,nscoord aBStart,nscoord aISize,nscoord aBSize,const nsSize & aContainerSize)587   void SetBounds(mozilla::WritingMode aWritingMode, nscoord aIStart,
588                  nscoord aBStart, nscoord aISize, nscoord aBSize,
589                  const nsSize& aContainerSize) {
590     mWritingMode = aWritingMode;
591     mContainerSize = aContainerSize;
592     mBounds =
593         mozilla::LogicalRect(aWritingMode, aIStart, aBStart, aISize, aBSize);
594   }
595 
596   // mFlags.mHasHashedFrames says which one to use
597   union {
598     nsTHashSet<nsIFrame*>* mFrames;
599     uint32_t mChildCount;
600   };
601 
602   struct FlagBits {
603     bool mDirty : 1;
604     bool mPreviousMarginDirty : 1;
605     bool mHasClearance : 1;
606     bool mBlock : 1;
607     bool mImpactedByFloat : 1;
608     bool mLineWrapped : 1;
609     bool mInvalidateTextRuns : 1;
610     // default 0 = means that the opt potentially applies to this line.
611     // 1 = never skip reflowing this line for a resize reflow
612     bool mResizeReflowOptimizationDisabled : 1;
613     bool mEmptyCacheValid : 1;
614     bool mEmptyCacheState : 1;
615     // mHasMarker indicates that this is an inline line whose block's
616     // ::marker is adjacent to this line and non-empty.
617     bool mHasMarker : 1;
618     // Indicates that this line *may* have a placeholder for a float
619     // that was pushed to a later column or page.
620     bool mHadFloatPushed : 1;
621     bool mHasHashedFrames : 1;
622     // Indicates that this line is the one identified by an ancestor block
623     // with -webkit-line-clamp on its legacy flex container, and that subsequent
624     // lines under that block are "clamped" away, and therefore we need to
625     // render a 'text-overflow: ellipsis'-like marker in this line.  At most one
626     // line in the set of lines found by LineClampLineIterator for a given
627     // block will have this flag set.
628     bool mHasLineClampEllipsis : 1;
629     // Has this line moved to a different fragment of the block since
630     // the last time it was reflowed?
631     bool mMovedFragments : 1;
632     StyleClear mBreakType;
633   };
634 
635   struct ExtraData {
ExtraDataExtraData636     explicit ExtraData(const nsRect& aBounds)
637         : mOverflowAreas(aBounds, aBounds) {}
638     mozilla::OverflowAreas mOverflowAreas;
639   };
640 
641   struct ExtraBlockData : public ExtraData {
ExtraBlockDataExtraBlockData642     explicit ExtraBlockData(const nsRect& aBounds)
643         : ExtraData(aBounds), mCarriedOutBEndMargin() {}
644     nsCollapsingMargin mCarriedOutBEndMargin;
645   };
646 
647   struct ExtraInlineData : public ExtraData {
ExtraInlineDataExtraInlineData648     explicit ExtraInlineData(const nsRect& aBounds)
649         : ExtraData(aBounds),
650           mFloatEdgeIStart(nscoord_MIN),
651           mFloatEdgeIEnd(nscoord_MIN) {}
652     nscoord mFloatEdgeIStart;
653     nscoord mFloatEdgeIEnd;
654     nsFloatCacheList mFloats;
655   };
656 
GetFloatEdges(nscoord * aStart,nscoord * aEnd)657   bool GetFloatEdges(nscoord* aStart, nscoord* aEnd) const {
658     MOZ_ASSERT(IsInline(), "block line can't have float edges");
659     if (mInlineData && mInlineData->mFloatEdgeIStart != nscoord_MIN) {
660       *aStart = mInlineData->mFloatEdgeIStart;
661       *aEnd = mInlineData->mFloatEdgeIEnd;
662       return true;
663     }
664     return false;
665   }
666   void SetFloatEdges(nscoord aStart, nscoord aEnd);
667   void ClearFloatEdges();
668 
669  protected:
670   nscoord mAscent;  // see |SetAscent| / |GetAscent|
671   static_assert(sizeof(FlagBits) <= sizeof(uint32_t),
672                 "size of FlagBits should not be larger than size of uint32_t");
673   union {
674     uint32_t mAllFlags;
675     FlagBits mFlags;
676   };
677 
BreakType()678   StyleClear BreakType() const { return mFlags.mBreakType; };
679 
680   union {
681     ExtraData* mData;
682     ExtraBlockData* mBlockData;
683     ExtraInlineData* mInlineData;
684   };
685 
686   void Cleanup();
687   void MaybeFreeData();
688 };
689 
690 /**
691  * A linked list type where the items in the list must inherit from
692  * a link type to fuse allocations.
693  *
694  * API heavily based on the |list| class in the C++ standard.
695  */
696 
697 class nsLineList_iterator {
698  public:
699   friend class nsLineList;
700   friend class nsLineList_reverse_iterator;
701   friend class nsLineList_const_iterator;
702   friend class nsLineList_const_reverse_iterator;
703 
704   typedef nsLineList_iterator iterator_self_type;
705   typedef nsLineList_reverse_iterator iterator_reverse_type;
706 
707   typedef nsLineBox& reference;
708   typedef const nsLineBox& const_reference;
709 
710   typedef nsLineBox* pointer;
711   typedef const nsLineBox* const_pointer;
712 
713   typedef uint32_t size_type;
714   typedef int32_t difference_type;
715 
716   typedef nsLineLink link_type;
717 
718 #ifdef DEBUG
nsLineList_iterator()719   nsLineList_iterator() : mListLink(nullptr) {
720     memset(&mCurrent, 0xcd, sizeof(mCurrent));
721   }
722 #else
723   // Auto generated default constructor OK.
724 #endif
725   // Auto generated copy-constructor OK.
726 
727   inline iterator_self_type& operator=(const iterator_self_type& aOther);
728   inline iterator_self_type& operator=(const iterator_reverse_type& aOther);
729 
730   iterator_self_type& operator++() {
731     mCurrent = mCurrent->_mNext;
732     return *this;
733   }
734 
735   iterator_self_type operator++(int) {
736     iterator_self_type rv(*this);
737     mCurrent = mCurrent->_mNext;
738     return rv;
739   }
740 
741   iterator_self_type& operator--() {
742     mCurrent = mCurrent->_mPrev;
743     return *this;
744   }
745 
746   iterator_self_type operator--(int) {
747     iterator_self_type rv(*this);
748     mCurrent = mCurrent->_mPrev;
749     return rv;
750   }
751 
752   reference operator*() {
753     MOZ_ASSERT(mListLink);
754     MOZ_ASSERT(mCurrent != mListLink, "running past end");
755     return *static_cast<pointer>(mCurrent);
756   }
757 
758   pointer operator->() {
759     MOZ_ASSERT(mListLink);
760     MOZ_ASSERT(mCurrent != mListLink, "running past end");
761     return static_cast<pointer>(mCurrent);
762   }
763 
get()764   pointer get() {
765     MOZ_ASSERT(mListLink);
766     MOZ_ASSERT(mCurrent != mListLink, "running past end");
767     return static_cast<pointer>(mCurrent);
768   }
769 
pointer()770   operator pointer() {
771     MOZ_ASSERT(mListLink);
772     MOZ_ASSERT(mCurrent != mListLink, "running past end");
773     return static_cast<pointer>(mCurrent);
774   }
775 
776   const_reference operator*() const {
777     MOZ_ASSERT(mListLink);
778     MOZ_ASSERT(mCurrent != mListLink, "running past end");
779     return *static_cast<const_pointer>(mCurrent);
780   }
781 
782   const_pointer operator->() const {
783     MOZ_ASSERT(mListLink);
784     MOZ_ASSERT(mCurrent != mListLink, "running past end");
785     return static_cast<const_pointer>(mCurrent);
786   }
787 
788 #ifndef __MWERKS__
const_pointer()789   operator const_pointer() const {
790     MOZ_ASSERT(mListLink);
791     MOZ_ASSERT(mCurrent != mListLink, "running past end");
792     return static_cast<const_pointer>(mCurrent);
793   }
794 #endif /* !__MWERKS__ */
795 
next()796   iterator_self_type next() {
797     iterator_self_type copy(*this);
798     return ++copy;
799   }
800 
next()801   const iterator_self_type next() const {
802     iterator_self_type copy(*this);
803     return ++copy;
804   }
805 
prev()806   iterator_self_type prev() {
807     iterator_self_type copy(*this);
808     return --copy;
809   }
810 
prev()811   const iterator_self_type prev() const {
812     iterator_self_type copy(*this);
813     return --copy;
814   }
815 
816   // Passing by value rather than by reference and reference to const
817   // to keep AIX happy.
818   bool operator==(const iterator_self_type aOther) const {
819     MOZ_ASSERT(mListLink);
820     MOZ_ASSERT(mListLink == aOther.mListLink,
821                "comparing iterators over different lists");
822     return mCurrent == aOther.mCurrent;
823   }
824   bool operator!=(const iterator_self_type aOther) const {
825     MOZ_ASSERT(mListLink);
826     MOZ_ASSERT(mListLink == aOther.mListLink,
827                "comparing iterators over different lists");
828     return mCurrent != aOther.mCurrent;
829   }
830   bool operator==(const iterator_self_type aOther) {
831     MOZ_ASSERT(mListLink);
832     MOZ_ASSERT(mListLink == aOther.mListLink,
833                "comparing iterators over different lists");
834     return mCurrent == aOther.mCurrent;
835   }
836   bool operator!=(const iterator_self_type aOther) {
837     MOZ_ASSERT(mListLink);
838     MOZ_ASSERT(mListLink == aOther.mListLink,
839                "comparing iterators over different lists");
840     return mCurrent != aOther.mCurrent;
841   }
842 
843 #ifdef DEBUG
IsInSameList(const iterator_self_type aOther)844   bool IsInSameList(const iterator_self_type aOther) const {
845     return mListLink == aOther.mListLink;
846   }
847 #endif
848 
849  private:
850   link_type* mCurrent;
851 #ifdef DEBUG
852   link_type* mListLink;  // the list's link, i.e., the end
853 #endif
854 };
855 
856 class nsLineList_reverse_iterator {
857  public:
858   friend class nsLineList;
859   friend class nsLineList_iterator;
860   friend class nsLineList_const_iterator;
861   friend class nsLineList_const_reverse_iterator;
862 
863   typedef nsLineList_reverse_iterator iterator_self_type;
864   typedef nsLineList_iterator iterator_reverse_type;
865 
866   typedef nsLineBox& reference;
867   typedef const nsLineBox& const_reference;
868 
869   typedef nsLineBox* pointer;
870   typedef const nsLineBox* const_pointer;
871 
872   typedef uint32_t size_type;
873   typedef int32_t difference_type;
874 
875   typedef nsLineLink link_type;
876 
877 #ifdef DEBUG
nsLineList_reverse_iterator()878   nsLineList_reverse_iterator() : mListLink(nullptr) {
879     memset(&mCurrent, 0xcd, sizeof(mCurrent));
880   }
881 #else
882   // Auto generated default constructor OK.
883 #endif
884   // Auto generated copy-constructor OK.
885 
886   inline iterator_self_type& operator=(const iterator_reverse_type& aOther);
887   inline iterator_self_type& operator=(const iterator_self_type& aOther);
888 
889   iterator_self_type& operator++() {
890     mCurrent = mCurrent->_mPrev;
891     return *this;
892   }
893 
894   iterator_self_type operator++(int) {
895     iterator_self_type rv(*this);
896     mCurrent = mCurrent->_mPrev;
897     return rv;
898   }
899 
900   iterator_self_type& operator--() {
901     mCurrent = mCurrent->_mNext;
902     return *this;
903   }
904 
905   iterator_self_type operator--(int) {
906     iterator_self_type rv(*this);
907     mCurrent = mCurrent->_mNext;
908     return rv;
909   }
910 
911   reference operator*() {
912     MOZ_ASSERT(mListLink);
913     MOZ_ASSERT(mCurrent != mListLink, "running past end");
914     return *static_cast<pointer>(mCurrent);
915   }
916 
917   pointer operator->() {
918     MOZ_ASSERT(mListLink);
919     MOZ_ASSERT(mCurrent != mListLink, "running past end");
920     return static_cast<pointer>(mCurrent);
921   }
922 
get()923   pointer get() {
924     MOZ_ASSERT(mListLink);
925     MOZ_ASSERT(mCurrent != mListLink, "running past end");
926     return static_cast<pointer>(mCurrent);
927   }
928 
pointer()929   operator pointer() {
930     MOZ_ASSERT(mListLink);
931     MOZ_ASSERT(mCurrent != mListLink, "running past end");
932     return static_cast<pointer>(mCurrent);
933   }
934 
935   const_reference operator*() const {
936     MOZ_ASSERT(mListLink);
937     MOZ_ASSERT(mCurrent != mListLink, "running past end");
938     return *static_cast<const_pointer>(mCurrent);
939   }
940 
941   const_pointer operator->() const {
942     MOZ_ASSERT(mListLink);
943     MOZ_ASSERT(mCurrent != mListLink, "running past end");
944     return static_cast<const_pointer>(mCurrent);
945   }
946 
947 #ifndef __MWERKS__
const_pointer()948   operator const_pointer() const {
949     MOZ_ASSERT(mListLink);
950     MOZ_ASSERT(mCurrent != mListLink, "running past end");
951     return static_cast<const_pointer>(mCurrent);
952   }
953 #endif /* !__MWERKS__ */
954 
955   // Passing by value rather than by reference and reference to const
956   // to keep AIX happy.
957   bool operator==(const iterator_self_type aOther) const {
958     MOZ_ASSERT(mListLink);
959     NS_ASSERTION(mListLink == aOther.mListLink,
960                  "comparing iterators over different lists");
961     return mCurrent == aOther.mCurrent;
962   }
963   bool operator!=(const iterator_self_type aOther) const {
964     MOZ_ASSERT(mListLink);
965     NS_ASSERTION(mListLink == aOther.mListLink,
966                  "comparing iterators over different lists");
967     return mCurrent != aOther.mCurrent;
968   }
969   bool operator==(const iterator_self_type aOther) {
970     MOZ_ASSERT(mListLink);
971     NS_ASSERTION(mListLink == aOther.mListLink,
972                  "comparing iterators over different lists");
973     return mCurrent == aOther.mCurrent;
974   }
975   bool operator!=(const iterator_self_type aOther) {
976     MOZ_ASSERT(mListLink);
977     NS_ASSERTION(mListLink == aOther.mListLink,
978                  "comparing iterators over different lists");
979     return mCurrent != aOther.mCurrent;
980   }
981 
982 #ifdef DEBUG
IsInSameList(const iterator_self_type aOther)983   bool IsInSameList(const iterator_self_type aOther) const {
984     return mListLink == aOther.mListLink;
985   }
986 #endif
987 
988  private:
989   link_type* mCurrent;
990 #ifdef DEBUG
991   link_type* mListLink;  // the list's link, i.e., the end
992 #endif
993 };
994 
995 class nsLineList_const_iterator {
996  public:
997   friend class nsLineList;
998   friend class nsLineList_iterator;
999   friend class nsLineList_reverse_iterator;
1000   friend class nsLineList_const_reverse_iterator;
1001 
1002   typedef nsLineList_const_iterator iterator_self_type;
1003   typedef nsLineList_const_reverse_iterator iterator_reverse_type;
1004   typedef nsLineList_iterator iterator_nonconst_type;
1005   typedef nsLineList_reverse_iterator iterator_nonconst_reverse_type;
1006 
1007   typedef nsLineBox& reference;
1008   typedef const nsLineBox& const_reference;
1009 
1010   typedef nsLineBox* pointer;
1011   typedef const nsLineBox* const_pointer;
1012 
1013   typedef uint32_t size_type;
1014   typedef int32_t difference_type;
1015 
1016   typedef nsLineLink link_type;
1017 
1018 #ifdef DEBUG
nsLineList_const_iterator()1019   nsLineList_const_iterator() : mListLink(nullptr) {
1020     memset(&mCurrent, 0xcd, sizeof(mCurrent));
1021   }
1022 #else
1023   // Auto generated default constructor OK.
1024 #endif
1025   // Auto generated copy-constructor OK.
1026 
1027   inline iterator_self_type& operator=(const iterator_nonconst_type& aOther);
1028   inline iterator_self_type& operator=(
1029       const iterator_nonconst_reverse_type& aOther);
1030   inline iterator_self_type& operator=(const iterator_self_type& aOther);
1031   inline iterator_self_type& operator=(const iterator_reverse_type& aOther);
1032 
1033   iterator_self_type& operator++() {
1034     mCurrent = mCurrent->_mNext;
1035     return *this;
1036   }
1037 
1038   iterator_self_type operator++(int) {
1039     iterator_self_type rv(*this);
1040     mCurrent = mCurrent->_mNext;
1041     return rv;
1042   }
1043 
1044   iterator_self_type& operator--() {
1045     mCurrent = mCurrent->_mPrev;
1046     return *this;
1047   }
1048 
1049   iterator_self_type operator--(int) {
1050     iterator_self_type rv(*this);
1051     mCurrent = mCurrent->_mPrev;
1052     return rv;
1053   }
1054 
1055   const_reference operator*() const {
1056     MOZ_ASSERT(mListLink);
1057     MOZ_ASSERT(mCurrent != mListLink, "running past end");
1058     return *static_cast<const_pointer>(mCurrent);
1059   }
1060 
1061   const_pointer operator->() const {
1062     MOZ_ASSERT(mListLink);
1063     MOZ_ASSERT(mCurrent != mListLink, "running past end");
1064     return static_cast<const_pointer>(mCurrent);
1065   }
1066 
get()1067   const_pointer get() const {
1068     MOZ_ASSERT(mListLink);
1069     MOZ_ASSERT(mCurrent != mListLink, "running past end");
1070     return static_cast<const_pointer>(mCurrent);
1071   }
1072 
1073 #ifndef __MWERKS__
const_pointer()1074   operator const_pointer() const {
1075     MOZ_ASSERT(mListLink);
1076     MOZ_ASSERT(mCurrent != mListLink, "running past end");
1077     return static_cast<const_pointer>(mCurrent);
1078   }
1079 #endif /* !__MWERKS__ */
1080 
next()1081   const iterator_self_type next() const {
1082     iterator_self_type copy(*this);
1083     return ++copy;
1084   }
1085 
prev()1086   const iterator_self_type prev() const {
1087     iterator_self_type copy(*this);
1088     return --copy;
1089   }
1090 
1091   // Passing by value rather than by reference and reference to const
1092   // to keep AIX happy.
1093   bool operator==(const iterator_self_type aOther) const {
1094     MOZ_ASSERT(mListLink);
1095     NS_ASSERTION(mListLink == aOther.mListLink,
1096                  "comparing iterators over different lists");
1097     return mCurrent == aOther.mCurrent;
1098   }
1099   bool operator!=(const iterator_self_type aOther) const {
1100     MOZ_ASSERT(mListLink);
1101     NS_ASSERTION(mListLink == aOther.mListLink,
1102                  "comparing iterators over different lists");
1103     return mCurrent != aOther.mCurrent;
1104   }
1105   bool operator==(const iterator_self_type aOther) {
1106     MOZ_ASSERT(mListLink);
1107     NS_ASSERTION(mListLink == aOther.mListLink,
1108                  "comparing iterators over different lists");
1109     return mCurrent == aOther.mCurrent;
1110   }
1111   bool operator!=(const iterator_self_type aOther) {
1112     MOZ_ASSERT(mListLink);
1113     NS_ASSERTION(mListLink == aOther.mListLink,
1114                  "comparing iterators over different lists");
1115     return mCurrent != aOther.mCurrent;
1116   }
1117 
1118 #ifdef DEBUG
IsInSameList(const iterator_self_type aOther)1119   bool IsInSameList(const iterator_self_type aOther) const {
1120     return mListLink == aOther.mListLink;
1121   }
1122 #endif
1123 
1124  private:
1125   const link_type* mCurrent;
1126 #ifdef DEBUG
1127   const link_type* mListLink;  // the list's link, i.e., the end
1128 #endif
1129 };
1130 
1131 class nsLineList_const_reverse_iterator {
1132  public:
1133   friend class nsLineList;
1134   friend class nsLineList_iterator;
1135   friend class nsLineList_reverse_iterator;
1136   friend class nsLineList_const_iterator;
1137 
1138   typedef nsLineList_const_reverse_iterator iterator_self_type;
1139   typedef nsLineList_const_iterator iterator_reverse_type;
1140   typedef nsLineList_iterator iterator_nonconst_reverse_type;
1141   typedef nsLineList_reverse_iterator iterator_nonconst_type;
1142 
1143   typedef nsLineBox& reference;
1144   typedef const nsLineBox& const_reference;
1145 
1146   typedef nsLineBox* pointer;
1147   typedef const nsLineBox* const_pointer;
1148 
1149   typedef uint32_t size_type;
1150   typedef int32_t difference_type;
1151 
1152   typedef nsLineLink link_type;
1153 
1154 #ifdef DEBUG
nsLineList_const_reverse_iterator()1155   nsLineList_const_reverse_iterator() : mListLink(nullptr) {
1156     memset(&mCurrent, 0xcd, sizeof(mCurrent));
1157   }
1158 #else
1159   // Auto generated default constructor OK.
1160 #endif
1161   // Auto generated copy-constructor OK.
1162 
1163   inline iterator_self_type& operator=(const iterator_nonconst_type& aOther);
1164   inline iterator_self_type& operator=(
1165       const iterator_nonconst_reverse_type& aOther);
1166   inline iterator_self_type& operator=(const iterator_self_type& aOther);
1167   inline iterator_self_type& operator=(const iterator_reverse_type& aOther);
1168 
1169   iterator_self_type& operator++() {
1170     mCurrent = mCurrent->_mPrev;
1171     return *this;
1172   }
1173 
1174   iterator_self_type operator++(int) {
1175     iterator_self_type rv(*this);
1176     mCurrent = mCurrent->_mPrev;
1177     return rv;
1178   }
1179 
1180   iterator_self_type& operator--() {
1181     mCurrent = mCurrent->_mNext;
1182     return *this;
1183   }
1184 
1185   iterator_self_type operator--(int) {
1186     iterator_self_type rv(*this);
1187     mCurrent = mCurrent->_mNext;
1188     return rv;
1189   }
1190 
1191   const_reference operator*() const {
1192     MOZ_ASSERT(mListLink);
1193     MOZ_ASSERT(mCurrent != mListLink, "running past end");
1194     return *static_cast<const_pointer>(mCurrent);
1195   }
1196 
1197   const_pointer operator->() const {
1198     MOZ_ASSERT(mListLink);
1199     MOZ_ASSERT(mCurrent != mListLink, "running past end");
1200     return static_cast<const_pointer>(mCurrent);
1201   }
1202 
get()1203   const_pointer get() const {
1204     MOZ_ASSERT(mListLink);
1205     MOZ_ASSERT(mCurrent != mListLink, "running past end");
1206     return static_cast<const_pointer>(mCurrent);
1207   }
1208 
1209 #ifndef __MWERKS__
const_pointer()1210   operator const_pointer() const {
1211     MOZ_ASSERT(mListLink);
1212     MOZ_ASSERT(mCurrent != mListLink, "running past end");
1213     return static_cast<const_pointer>(mCurrent);
1214   }
1215 #endif /* !__MWERKS__ */
1216 
1217   // Passing by value rather than by reference and reference to const
1218   // to keep AIX happy.
1219   bool operator==(const iterator_self_type aOther) const {
1220     MOZ_ASSERT(mListLink);
1221     NS_ASSERTION(mListLink == aOther.mListLink,
1222                  "comparing iterators over different lists");
1223     return mCurrent == aOther.mCurrent;
1224   }
1225   bool operator!=(const iterator_self_type aOther) const {
1226     MOZ_ASSERT(mListLink);
1227     NS_ASSERTION(mListLink == aOther.mListLink,
1228                  "comparing iterators over different lists");
1229     return mCurrent != aOther.mCurrent;
1230   }
1231   bool operator==(const iterator_self_type aOther) {
1232     MOZ_ASSERT(mListLink);
1233     NS_ASSERTION(mListLink == aOther.mListLink,
1234                  "comparing iterators over different lists");
1235     return mCurrent == aOther.mCurrent;
1236   }
1237   bool operator!=(const iterator_self_type aOther) {
1238     MOZ_ASSERT(mListLink);
1239     NS_ASSERTION(mListLink == aOther.mListLink,
1240                  "comparing iterators over different lists");
1241     return mCurrent != aOther.mCurrent;
1242   }
1243 
1244 #ifdef DEBUG
IsInSameList(const iterator_self_type aOther)1245   bool IsInSameList(const iterator_self_type aOther) const {
1246     return mListLink == aOther.mListLink;
1247   }
1248 #endif
1249 
1250   // private:
1251   const link_type* mCurrent;
1252 #ifdef DEBUG
1253   const link_type* mListLink;  // the list's link, i.e., the end
1254 #endif
1255 };
1256 
1257 class nsLineList {
1258  public:
1259   friend class nsLineList_iterator;
1260   friend class nsLineList_reverse_iterator;
1261   friend class nsLineList_const_iterator;
1262   friend class nsLineList_const_reverse_iterator;
1263 
1264   typedef uint32_t size_type;
1265   typedef int32_t difference_type;
1266 
1267   typedef nsLineLink link_type;
1268 
1269  private:
1270   link_type mLink;
1271 
1272  public:
1273   typedef nsLineList self_type;
1274 
1275   typedef nsLineBox& reference;
1276   typedef const nsLineBox& const_reference;
1277 
1278   typedef nsLineBox* pointer;
1279   typedef const nsLineBox* const_pointer;
1280 
1281   typedef nsLineList_iterator iterator;
1282   typedef nsLineList_reverse_iterator reverse_iterator;
1283   typedef nsLineList_const_iterator const_iterator;
1284   typedef nsLineList_const_reverse_iterator const_reverse_iterator;
1285 
nsLineList()1286   nsLineList() {
1287     MOZ_COUNT_CTOR(nsLineList);
1288     clear();
1289   }
1290 
MOZ_COUNTED_DTOR(nsLineList)1291   MOZ_COUNTED_DTOR(nsLineList)
1292 
1293   const_iterator begin() const {
1294     const_iterator rv;
1295     rv.mCurrent = mLink._mNext;
1296 #ifdef DEBUG
1297     rv.mListLink = &mLink;
1298 #endif
1299     return rv;
1300   }
1301 
begin()1302   iterator begin() {
1303     iterator rv;
1304     rv.mCurrent = mLink._mNext;
1305 #ifdef DEBUG
1306     rv.mListLink = &mLink;
1307 #endif
1308     return rv;
1309   }
1310 
begin(nsLineBox * aLine)1311   iterator begin(nsLineBox* aLine) {
1312     iterator rv;
1313     rv.mCurrent = aLine;
1314 #ifdef DEBUG
1315     rv.mListLink = &mLink;
1316 #endif
1317     return rv;
1318   }
1319 
end()1320   const_iterator end() const {
1321     const_iterator rv;
1322     rv.mCurrent = &mLink;
1323 #ifdef DEBUG
1324     rv.mListLink = &mLink;
1325 #endif
1326     return rv;
1327   }
1328 
end()1329   iterator end() {
1330     iterator rv;
1331     rv.mCurrent = &mLink;
1332 #ifdef DEBUG
1333     rv.mListLink = &mLink;
1334 #endif
1335     return rv;
1336   }
1337 
rbegin()1338   const_reverse_iterator rbegin() const {
1339     const_reverse_iterator rv;
1340     rv.mCurrent = mLink._mPrev;
1341 #ifdef DEBUG
1342     rv.mListLink = &mLink;
1343 #endif
1344     return rv;
1345   }
1346 
rbegin()1347   reverse_iterator rbegin() {
1348     reverse_iterator rv;
1349     rv.mCurrent = mLink._mPrev;
1350 #ifdef DEBUG
1351     rv.mListLink = &mLink;
1352 #endif
1353     return rv;
1354   }
1355 
rbegin(nsLineBox * aLine)1356   reverse_iterator rbegin(nsLineBox* aLine) {
1357     reverse_iterator rv;
1358     rv.mCurrent = aLine;
1359 #ifdef DEBUG
1360     rv.mListLink = &mLink;
1361 #endif
1362     return rv;
1363   }
1364 
rend()1365   const_reverse_iterator rend() const {
1366     const_reverse_iterator rv;
1367     rv.mCurrent = &mLink;
1368 #ifdef DEBUG
1369     rv.mListLink = &mLink;
1370 #endif
1371     return rv;
1372   }
1373 
rend()1374   reverse_iterator rend() {
1375     reverse_iterator rv;
1376     rv.mCurrent = &mLink;
1377 #ifdef DEBUG
1378     rv.mListLink = &mLink;
1379 #endif
1380     return rv;
1381   }
1382 
empty()1383   bool empty() const { return mLink._mNext == &mLink; }
1384 
1385   // NOTE: O(N).
size()1386   size_type size() const {
1387     size_type count = 0;
1388     for (const link_type* cur = mLink._mNext; cur != &mLink;
1389          cur = cur->_mNext) {
1390       ++count;
1391     }
1392     return count;
1393   }
1394 
front()1395   pointer front() {
1396     NS_ASSERTION(!empty(), "no element to return");
1397     return static_cast<pointer>(mLink._mNext);
1398   }
1399 
front()1400   const_pointer front() const {
1401     NS_ASSERTION(!empty(), "no element to return");
1402     return static_cast<const_pointer>(mLink._mNext);
1403   }
1404 
back()1405   pointer back() {
1406     NS_ASSERTION(!empty(), "no element to return");
1407     return static_cast<pointer>(mLink._mPrev);
1408   }
1409 
back()1410   const_pointer back() const {
1411     NS_ASSERTION(!empty(), "no element to return");
1412     return static_cast<const_pointer>(mLink._mPrev);
1413   }
1414 
push_front(pointer aNew)1415   void push_front(pointer aNew) {
1416     aNew->_mNext = mLink._mNext;
1417     mLink._mNext->_mPrev = aNew;
1418     aNew->_mPrev = &mLink;
1419     mLink._mNext = aNew;
1420   }
1421 
pop_front()1422   void pop_front()
1423   // NOTE: leaves dangling next/prev pointers
1424   {
1425     NS_ASSERTION(!empty(), "no element to pop");
1426     link_type* newFirst = mLink._mNext->_mNext;
1427     newFirst->_mPrev = &mLink;
1428     // mLink._mNext->_mNext = nullptr;
1429     // mLink._mNext->_mPrev = nullptr;
1430     mLink._mNext = newFirst;
1431   }
1432 
push_back(pointer aNew)1433   void push_back(pointer aNew) {
1434     aNew->_mPrev = mLink._mPrev;
1435     mLink._mPrev->_mNext = aNew;
1436     aNew->_mNext = &mLink;
1437     mLink._mPrev = aNew;
1438   }
1439 
pop_back()1440   void pop_back()
1441   // NOTE: leaves dangling next/prev pointers
1442   {
1443     NS_ASSERTION(!empty(), "no element to pop");
1444     link_type* newLast = mLink._mPrev->_mPrev;
1445     newLast->_mNext = &mLink;
1446     // mLink._mPrev->_mPrev = nullptr;
1447     // mLink._mPrev->_mNext = nullptr;
1448     mLink._mPrev = newLast;
1449   }
1450 
1451   // inserts x before position
before_insert(iterator position,pointer x)1452   iterator before_insert(iterator position, pointer x) {
1453     // use |mCurrent| to prevent DEBUG_PASS_END assertions
1454     x->_mPrev = position.mCurrent->_mPrev;
1455     x->_mNext = position.mCurrent;
1456     position.mCurrent->_mPrev->_mNext = x;
1457     position.mCurrent->_mPrev = x;
1458     return --position;
1459   }
1460 
1461   // inserts x after position
after_insert(iterator position,pointer x)1462   iterator after_insert(iterator position, pointer x) {
1463     // use |mCurrent| to prevent DEBUG_PASS_END assertions
1464     x->_mNext = position.mCurrent->_mNext;
1465     x->_mPrev = position.mCurrent;
1466     position.mCurrent->_mNext->_mPrev = x;
1467     position.mCurrent->_mNext = x;
1468     return ++position;
1469   }
1470 
1471   // returns iterator pointing to after the element
erase(iterator position)1472   iterator erase(iterator position)
1473   // NOTE: leaves dangling next/prev pointers
1474   {
1475     position->_mPrev->_mNext = position->_mNext;
1476     position->_mNext->_mPrev = position->_mPrev;
1477     return ++position;
1478   }
1479 
swap(self_type & y)1480   void swap(self_type& y) {
1481     link_type tmp(y.mLink);
1482     y.mLink = mLink;
1483     mLink = tmp;
1484 
1485     if (!empty()) {
1486       mLink._mNext->_mPrev = &mLink;
1487       mLink._mPrev->_mNext = &mLink;
1488     }
1489 
1490     if (!y.empty()) {
1491       y.mLink._mNext->_mPrev = &y.mLink;
1492       y.mLink._mPrev->_mNext = &y.mLink;
1493     }
1494   }
1495 
clear()1496   void clear()
1497   // NOTE:  leaves dangling next/prev pointers
1498   {
1499     mLink._mNext = &mLink;
1500     mLink._mPrev = &mLink;
1501   }
1502 
1503   // inserts the conts of x before position and makes x empty
splice(iterator position,self_type & x)1504   void splice(iterator position, self_type& x) {
1505     // use |mCurrent| to prevent DEBUG_PASS_END assertions
1506     position.mCurrent->_mPrev->_mNext = x.mLink._mNext;
1507     x.mLink._mNext->_mPrev = position.mCurrent->_mPrev;
1508     x.mLink._mPrev->_mNext = position.mCurrent;
1509     position.mCurrent->_mPrev = x.mLink._mPrev;
1510     x.clear();
1511   }
1512 
1513   // Inserts element *i from list x before position and removes
1514   // it from x.
splice(iterator position,self_type & x,iterator i)1515   void splice(iterator position, self_type& x, iterator i) {
1516     NS_ASSERTION(!x.empty(), "Can't insert from empty list.");
1517     NS_ASSERTION(position != i && position.mCurrent != i->_mNext,
1518                  "We don't check for this case.");
1519 
1520     // remove from |x|
1521     i->_mPrev->_mNext = i->_mNext;
1522     i->_mNext->_mPrev = i->_mPrev;
1523 
1524     // use |mCurrent| to prevent DEBUG_PASS_END assertions
1525     // link into |this|, before-side
1526     i->_mPrev = position.mCurrent->_mPrev;
1527     position.mCurrent->_mPrev->_mNext = i.get();
1528 
1529     // link into |this|, after-side
1530     i->_mNext = position.mCurrent;
1531     position.mCurrent->_mPrev = i.get();
1532   }
1533 
1534   // Inserts elements in [|first|, |last|), which are in |x|,
1535   // into |this| before |position| and removes them from |x|.
splice(iterator position,self_type & x,iterator first,iterator last)1536   void splice(iterator position, self_type& x, iterator first, iterator last) {
1537     NS_ASSERTION(!x.empty(), "Can't insert from empty list.");
1538 
1539     if (first == last) return;
1540 
1541     --last;  // so we now want to move [first, last]
1542     // remove from |x|
1543     first->_mPrev->_mNext = last->_mNext;
1544     last->_mNext->_mPrev = first->_mPrev;
1545 
1546     // use |mCurrent| to prevent DEBUG_PASS_END assertions
1547     // link into |this|, before-side
1548     first->_mPrev = position.mCurrent->_mPrev;
1549     position.mCurrent->_mPrev->_mNext = first.get();
1550 
1551     // link into |this|, after-side
1552     last->_mNext = position.mCurrent;
1553     position.mCurrent->_mPrev = last.get();
1554   }
1555 };
1556 
1557 // Many of these implementations of operator= don't work yet.  I don't
1558 // know why.
1559 
1560 #ifdef DEBUG
1561 
1562 // NOTE: ASSIGN_FROM is meant to be used *only* as the entire body
1563 // of a function and therefore lacks PR_{BEGIN,END}_MACRO
1564 #  define ASSIGN_FROM(other_)     \
1565     mCurrent = other_.mCurrent;   \
1566     mListLink = other_.mListLink; \
1567     return *this;
1568 
1569 #else /* !NS_LINELIST_DEBUG_PASS_END */
1570 
1571 #  define ASSIGN_FROM(other_)   \
1572     mCurrent = other_.mCurrent; \
1573     return *this;
1574 
1575 #endif /* !NS_LINELIST_DEBUG_PASS_END */
1576 
1577 inline nsLineList_iterator& nsLineList_iterator::operator=(
1578     const nsLineList_iterator& aOther) = default;
1579 
1580 inline nsLineList_iterator& nsLineList_iterator::operator=(
1581     const nsLineList_reverse_iterator& aOther) {
1582   ASSIGN_FROM(aOther)
1583 }
1584 
1585 inline nsLineList_reverse_iterator& nsLineList_reverse_iterator::operator=(
1586     const nsLineList_iterator& aOther) {
1587   ASSIGN_FROM(aOther)
1588 }
1589 
1590 inline nsLineList_reverse_iterator& nsLineList_reverse_iterator::operator=(
1591     const nsLineList_reverse_iterator& aOther) = default;
1592 
1593 inline nsLineList_const_iterator& nsLineList_const_iterator::operator=(
1594     const nsLineList_iterator& aOther) {
1595   ASSIGN_FROM(aOther)
1596 }
1597 
1598 inline nsLineList_const_iterator& nsLineList_const_iterator::operator=(
1599     const nsLineList_reverse_iterator& aOther) {
1600   ASSIGN_FROM(aOther)
1601 }
1602 
1603 inline nsLineList_const_iterator& nsLineList_const_iterator::operator=(
1604     const nsLineList_const_iterator& aOther) = default;
1605 
1606 inline nsLineList_const_iterator& nsLineList_const_iterator::operator=(
1607     const nsLineList_const_reverse_iterator& aOther) {
1608   ASSIGN_FROM(aOther)
1609 }
1610 
1611 inline nsLineList_const_reverse_iterator&
1612 nsLineList_const_reverse_iterator::operator=(
1613     const nsLineList_iterator& aOther) {
1614   ASSIGN_FROM(aOther)
1615 }
1616 
1617 inline nsLineList_const_reverse_iterator&
1618 nsLineList_const_reverse_iterator::operator=(
1619     const nsLineList_reverse_iterator& aOther) {
1620   ASSIGN_FROM(aOther)
1621 }
1622 
1623 inline nsLineList_const_reverse_iterator&
1624 nsLineList_const_reverse_iterator::operator=(
1625     const nsLineList_const_iterator& aOther) {
1626   ASSIGN_FROM(aOther)
1627 }
1628 
1629 inline nsLineList_const_reverse_iterator&
1630 nsLineList_const_reverse_iterator::operator=(
1631     const nsLineList_const_reverse_iterator& aOther) = default;
1632 
1633 //----------------------------------------------------------------------
1634 
1635 class nsLineIterator final : public nsILineIterator {
1636  public:
1637   nsLineIterator();
1638   ~nsLineIterator();
1639 
1640   virtual void DisposeLineIterator() override;
1641 
1642   virtual int32_t GetNumLines() const override;
1643   virtual bool GetDirection() override;
1644 
1645   mozilla::Result<LineInfo, nsresult> GetLine(
1646       int32_t aLineNumber) const override;
1647   virtual int32_t FindLineContaining(nsIFrame* aFrame,
1648                                      int32_t aStartLine = 0) override;
1649   NS_IMETHOD FindFrameAt(int32_t aLineNumber, nsPoint aPos,
1650                          nsIFrame** aFrameFound, bool* aPosIsBeforeFirstFrame,
1651                          bool* aPosIsAfterLastFrame) const override;
1652 
1653   NS_IMETHOD GetNextSiblingOnLine(nsIFrame*& aFrame,
1654                                   int32_t aLineNumber) const override;
1655   NS_IMETHOD CheckLineOrder(int32_t aLine, bool* aIsReordered,
1656                             nsIFrame** aFirstVisual,
1657                             nsIFrame** aLastVisual) override;
1658   nsresult Init(nsLineList& aLines, bool aRightToLeft);
1659 
1660  private:
PrevLine()1661   nsLineBox* PrevLine() {
1662     if (0 == mIndex) {
1663       return nullptr;
1664     }
1665     return mLines[--mIndex];
1666   }
1667 
NextLine()1668   nsLineBox* NextLine() {
1669     if (mIndex >= mNumLines - 1) {
1670       return nullptr;
1671     }
1672     return mLines[++mIndex];
1673   }
1674 
LineAt(int32_t aIndex)1675   nsLineBox* LineAt(int32_t aIndex) {
1676     if ((aIndex < 0) || (aIndex >= mNumLines)) {
1677       return nullptr;
1678     }
1679     return mLines[aIndex];
1680   }
1681 
1682   nsLineBox** mLines;
1683   int32_t mIndex;
1684   int32_t mNumLines;
1685   bool mRightToLeft;
1686 };
1687 
1688 #endif /* nsLineBox_h___ */
1689