tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

nsLineBox.h (35470B)


      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 <algorithm>
     13 
     14 #include "mozilla/Likely.h"
     15 #include "nsIFrame.h"
     16 #include "nsILineIterator.h"
     17 #include "nsStyleConsts.h"
     18 #include "nsTHashSet.h"
     19 
     20 class nsLineBox;
     21 class nsWindowSizes;
     22 
     23 namespace mozilla {
     24 class PresShell;
     25 }  // namespace mozilla
     26 
     27 /**
     28 * Function to create a line box and initialize it with a single frame.
     29 * The allocation is infallible.
     30 * If the frame was moved from another line then you're responsible
     31 * for notifying that line using NoteFrameRemoved().  Alternatively,
     32 * it's better to use the next function that does that for you in an
     33 * optimal way.
     34 */
     35 nsLineBox* NS_NewLineBox(mozilla::PresShell* aPresShell, nsIFrame* aFrame,
     36                         bool aIsBlock);
     37 /**
     38 * Function to create a line box and initialize it with aCount frames
     39 * that are currently on aFromLine.  The allocation is infallible.
     40 */
     41 nsLineBox* NS_NewLineBox(mozilla::PresShell* aPresShell, nsLineBox* aFromLine,
     42                         nsIFrame* aFrame, int32_t aCount);
     43 
     44 /**
     45 * Users must have the class that is to be part of the list inherit
     46 * from nsLineLink.  If they want to be efficient, it should be the
     47 * first base class.  (This was originally nsCLink in a templatized
     48 * nsCList, but it's still useful separately.)
     49 */
     50 
     51 class nsLineList;
     52 
     53 class nsLineLink {
     54  template <typename Link, bool>
     55  friend class GenericLineListIterator;
     56  friend class nsLineList;
     57 
     58  nsLineLink* _mNext;  // or head
     59  nsLineLink* _mPrev;  // or tail
     60 };
     61 
     62 /**
     63 * The nsLineBox class represents a horizontal line of frames. It contains
     64 * enough state to support incremental reflow of the frames, event handling
     65 * for the frames, and rendering of the frames.
     66 */
     67 class nsLineBox final : public nsLineLink {
     68 private:
     69  nsLineBox(nsIFrame* aFrame, int32_t aCount, bool aIsBlock);
     70  ~nsLineBox();
     71 
     72  // Infallible overloaded new operator. Uses an arena (which comes from the
     73  // presShell) to perform the allocation.
     74  void* operator new(size_t sz, mozilla::PresShell* aPresShell);
     75  void operator delete(void* aPtr, size_t sz) = delete;
     76 
     77 public:
     78  // Use these functions to allocate and destroy line boxes
     79  friend nsLineBox* NS_NewLineBox(mozilla::PresShell* aPresShell,
     80                                  nsIFrame* aFrame, bool aIsBlock);
     81  friend nsLineBox* NS_NewLineBox(mozilla::PresShell* aPresShell,
     82                                  nsLineBox* aFromLine, nsIFrame* aFrame,
     83                                  int32_t aCount);
     84  void Destroy(mozilla::PresShell* aPresShell);
     85 
     86  // mBlock bit
     87  bool IsBlock() const { return mFlags.mBlock; }
     88  bool IsInline() const { return !mFlags.mBlock; }
     89 
     90  // mDirty bit
     91  void MarkDirty() { mFlags.mDirty = 1; }
     92  void ClearDirty() { mFlags.mDirty = 0; }
     93  bool IsDirty() const { return mFlags.mDirty; }
     94 
     95  // mPreviousMarginDirty bit
     96  void MarkPreviousMarginDirty() { mFlags.mPreviousMarginDirty = 1; }
     97  void ClearPreviousMarginDirty() { mFlags.mPreviousMarginDirty = 0; }
     98  bool IsPreviousMarginDirty() const { return mFlags.mPreviousMarginDirty; }
     99 
    100  // mHasClearance bit
    101  void SetHasClearance() { mFlags.mHasClearance = 1; }
    102  void ClearHasClearance() { mFlags.mHasClearance = 0; }
    103  bool HasClearance() const { return mFlags.mHasClearance; }
    104 
    105  // mImpactedByFloat bit
    106  void SetLineIsImpactedByFloat(bool aValue) {
    107    mFlags.mImpactedByFloat = aValue;
    108  }
    109  bool IsImpactedByFloat() const { return mFlags.mImpactedByFloat; }
    110 
    111  // mLineWrapped bit
    112  void SetLineWrapped(bool aOn) { mFlags.mLineWrapped = aOn; }
    113  bool IsLineWrapped() const { return mFlags.mLineWrapped; }
    114 
    115  // mInvalidateTextRuns bit
    116  void SetInvalidateTextRuns(bool aOn) { mFlags.mInvalidateTextRuns = aOn; }
    117  bool GetInvalidateTextRuns() const { return mFlags.mInvalidateTextRuns; }
    118 
    119  // mResizeReflowOptimizationDisabled bit
    120  void DisableResizeReflowOptimization() {
    121    mFlags.mResizeReflowOptimizationDisabled = true;
    122  }
    123  void EnableResizeReflowOptimization() {
    124    mFlags.mResizeReflowOptimizationDisabled = false;
    125  }
    126  bool ResizeReflowOptimizationDisabled() const {
    127    return mFlags.mResizeReflowOptimizationDisabled;
    128  }
    129 
    130  // mHasMarker bit
    131  void SetHasMarker() {
    132    mFlags.mHasMarker = true;
    133    InvalidateCachedIsEmpty();
    134  }
    135  void ClearHasMarker() {
    136    mFlags.mHasMarker = false;
    137    InvalidateCachedIsEmpty();
    138  }
    139  bool HasMarker() const { return mFlags.mHasMarker; }
    140 
    141  // mHadFloatPushed bit
    142  void SetHadFloatPushed() { mFlags.mHadFloatPushed = true; }
    143  void ClearHadFloatPushed() { mFlags.mHadFloatPushed = false; }
    144  bool HadFloatPushed() const { return mFlags.mHadFloatPushed; }
    145 
    146  // mHasLineClampEllipsis bit
    147  void SetHasLineClampEllipsis() { mFlags.mHasLineClampEllipsis = true; }
    148  void ClearHasLineClampEllipsis() { mFlags.mHasLineClampEllipsis = false; }
    149  bool HasLineClampEllipsis() const { return mFlags.mHasLineClampEllipsis; }
    150 
    151  // mMovedFragments bit
    152  void SetMovedFragments() { mFlags.mMovedFragments = true; }
    153  void ClearMovedFragments() { mFlags.mMovedFragments = false; }
    154  bool MovedFragments() const { return mFlags.mMovedFragments; }
    155 
    156 private:
    157  // Add a hash table for fast lookup when the line has more frames than this.
    158  static const uint32_t kMinChildCountForHashtable = 200;
    159 
    160  /**
    161   * Take ownership of aFromLine's hash table and remove the frames that
    162   * stay on aFromLine from it, i.e. aFromLineNewCount frames starting with
    163   * mFirstChild.  This method is used to optimize moving a large number
    164   * of frames from one line to the next.
    165   */
    166  void StealHashTableFrom(nsLineBox* aFromLine, uint32_t aFromLineNewCount);
    167 
    168  /**
    169   * Does the equivalent of this->NoteFrameAdded and aFromLine->NoteFrameRemoved
    170   * for each frame on this line, but in a optimized way.
    171   */
    172  void NoteFramesMovedFrom(nsLineBox* aFromLine);
    173 
    174  void SwitchToHashtable() {
    175    MOZ_ASSERT(!mFlags.mHasHashedFrames);
    176    uint32_t count = GetChildCount();
    177    mFlags.mHasHashedFrames = 1;
    178    uint32_t minLength =
    179        std::max(kMinChildCountForHashtable,
    180                 uint32_t(PLDHashTable::kDefaultInitialLength));
    181    mFrames = new nsTHashSet<nsIFrame*>(std::max(count, minLength));
    182    for (nsIFrame* f = mFirstChild; count-- > 0; f = f->GetNextSibling()) {
    183      mFrames->Insert(f);
    184    }
    185  }
    186  void SwitchToCounter() {
    187    MOZ_ASSERT(mFlags.mHasHashedFrames);
    188    uint32_t count = GetChildCount();
    189    delete mFrames;
    190    mFlags.mHasHashedFrames = 0;
    191    mChildCount = count;
    192  }
    193 
    194 public:
    195  int32_t GetChildCount() const {
    196    return MOZ_UNLIKELY(mFlags.mHasHashedFrames) ? mFrames->Count()
    197                                                 : mChildCount;
    198  }
    199 
    200  /**
    201   * Register that aFrame is now on this line.
    202   */
    203  void NoteFrameAdded(nsIFrame* aFrame) {
    204    if (MOZ_UNLIKELY(mFlags.mHasHashedFrames)) {
    205      mFrames->Insert(aFrame);
    206    } else {
    207      if (++mChildCount >= kMinChildCountForHashtable) {
    208        SwitchToHashtable();
    209      }
    210    }
    211  }
    212 
    213  /**
    214   * Register that aFrame is not on this line anymore.
    215   */
    216  void NoteFrameRemoved(nsIFrame* aFrame) {
    217    MOZ_ASSERT(GetChildCount() > 0);
    218    if (MOZ_UNLIKELY(mFlags.mHasHashedFrames)) {
    219      mFrames->Remove(aFrame);
    220      if (mFrames->Count() < kMinChildCountForHashtable) {
    221        SwitchToCounter();
    222      }
    223    } else {
    224      --mChildCount;
    225    }
    226  }
    227 
    228  // mHasForcedLineBreakAfter bit & mFloatClearType value
    229  void ClearForcedLineBreak() {
    230    mFlags.mHasForcedLineBreakAfter = false;
    231    mFlags.mFloatClearType = mozilla::UsedClear::None;
    232  }
    233 
    234  bool HasFloatClearTypeBefore() const {
    235    return FloatClearTypeBefore() != mozilla::UsedClear::None;
    236  }
    237  void SetFloatClearTypeBefore(mozilla::UsedClear aClearType) {
    238    MOZ_ASSERT(IsBlock(), "Only block lines have break-before status!");
    239    MOZ_ASSERT(aClearType != mozilla::UsedClear::None,
    240               "Only UsedClear:Left/Right/Both are allowed before a line");
    241    mFlags.mFloatClearType = aClearType;
    242  }
    243  mozilla::UsedClear FloatClearTypeBefore() const {
    244    return IsBlock() ? mFlags.mFloatClearType : mozilla::UsedClear::None;
    245  }
    246 
    247  bool HasForcedLineBreakAfter() const {
    248    MOZ_ASSERT(IsInline() || !mFlags.mHasForcedLineBreakAfter,
    249               "A block line shouldn't set mHasForcedLineBreakAfter bit!");
    250    return IsInline() && mFlags.mHasForcedLineBreakAfter;
    251  }
    252  void SetForcedLineBreakAfter(mozilla::UsedClear aClearType) {
    253    MOZ_ASSERT(IsInline(), "Only inline lines have break-after status!");
    254    mFlags.mHasForcedLineBreakAfter = true;
    255    mFlags.mFloatClearType = aClearType;
    256  }
    257  bool HasFloatClearTypeAfter() const {
    258    return FloatClearTypeAfter() != mozilla::UsedClear::None;
    259  }
    260  mozilla::UsedClear FloatClearTypeAfter() const {
    261    return IsInline() ? mFlags.mFloatClearType : mozilla::UsedClear::None;
    262  }
    263 
    264  // mCarriedOutBEndMargin value
    265  mozilla::CollapsingMargin GetCarriedOutBEndMargin() const;
    266  // Returns true if the margin changed
    267  bool SetCarriedOutBEndMargin(mozilla::CollapsingMargin aValue);
    268 
    269  // mFloats
    270  bool HasFloats() const {
    271    return (IsInline() && mInlineData) && !mInlineData->mFloats.IsEmpty();
    272  }
    273  const nsTArray<nsIFrame*>& Floats() const {
    274    MOZ_ASSERT(HasFloats());
    275    return mInlineData->mFloats;
    276  }
    277  // Append aFloats to mFloat. aFloats will be empty.
    278  void AppendFloats(nsTArray<nsIFrame*>&& aFloats);
    279  void ClearFloats();
    280  bool RemoveFloat(nsIFrame* aFrame);
    281 
    282  // The ink overflow area should never be used for things that affect layout.
    283  // The scrollable overflow area are permitted to affect layout for handling of
    284  // overflow and scrollbars.
    285  void SetOverflowAreas(const mozilla::OverflowAreas& aOverflowAreas);
    286  mozilla::LogicalRect GetOverflowArea(mozilla::OverflowType aType,
    287                                       mozilla::WritingMode aWM,
    288                                       const nsSize& aContainerSize) {
    289    return mozilla::LogicalRect(aWM, GetOverflowArea(aType), aContainerSize);
    290  }
    291  nsRect GetOverflowArea(mozilla::OverflowType aType) const {
    292    return mData ? mData->mOverflowAreas.Overflow(aType) : GetPhysicalBounds();
    293  }
    294  mozilla::OverflowAreas GetOverflowAreas() const {
    295    if (mData) {
    296      return mData->mOverflowAreas;
    297    }
    298    nsRect bounds = GetPhysicalBounds();
    299    return mozilla::OverflowAreas(bounds, bounds);
    300  }
    301  nsRect InkOverflowRect() const {
    302    return GetOverflowArea(mozilla::OverflowType::Ink);
    303  }
    304  nsRect ScrollableOverflowRect() const {
    305    return GetOverflowArea(mozilla::OverflowType::Scrollable);
    306  }
    307 
    308  // See comment on `mInFlowChildBounds`.
    309  void SetInFlowChildBounds(const mozilla::Maybe<nsRect>& aInFlowChildBounds);
    310  mozilla::Maybe<nsRect> GetInFlowChildBounds() const;
    311 
    312  void SlideBy(nscoord aDBCoord, const nsSize& aContainerSize) {
    313    NS_ASSERTION(
    314        aContainerSize == mContainerSize || mContainerSize == nsSize(-1, -1),
    315        "container size doesn't match");
    316    mContainerSize = aContainerSize;
    317    mBounds.BStart(mWritingMode) += aDBCoord;
    318    if (mData) {
    319      // Use a null containerSize to convert vector from logical to physical.
    320      const nsSize nullContainerSize;
    321      nsPoint physicalDelta =
    322          mozilla::LogicalPoint(mWritingMode, 0, aDBCoord)
    323              .GetPhysicalPoint(mWritingMode, nullContainerSize);
    324      for (const auto otype : mozilla::AllOverflowTypes()) {
    325        mData->mOverflowAreas.Overflow(otype) += physicalDelta;
    326      }
    327      if (mData->mInFlowChildBounds) {
    328        *mData->mInFlowChildBounds += physicalDelta;
    329      }
    330    }
    331  }
    332 
    333  // Container-size for the line is changing (and therefore if writing mode
    334  // was vertical-rl, the line will move physically; this is like SlideBy,
    335  // but it is the container size instead of the line's own logical coord
    336  // that is changing.
    337  nsSize UpdateContainerSize(const nsSize aNewContainerSize) {
    338    NS_ASSERTION(mContainerSize != nsSize(-1, -1), "container size not set");
    339    nsSize delta = mContainerSize - aNewContainerSize;
    340    mContainerSize = aNewContainerSize;
    341    // this has a physical-coordinate effect only in vertical-rl mode
    342    if (mWritingMode.IsVerticalRL() && mData) {
    343      nsPoint physicalDelta(-delta.width, 0);
    344      for (const auto otype : mozilla::AllOverflowTypes()) {
    345        mData->mOverflowAreas.Overflow(otype) += physicalDelta;
    346      }
    347      if (mData->mInFlowChildBounds) {
    348        *mData->mInFlowChildBounds += physicalDelta;
    349      }
    350    }
    351    return delta;
    352  }
    353 
    354  void IndentBy(nscoord aDICoord, const nsSize& aContainerSize) {
    355    NS_ASSERTION(
    356        aContainerSize == mContainerSize || mContainerSize == nsSize(-1, -1),
    357        "container size doesn't match");
    358    mContainerSize = aContainerSize;
    359    mBounds.IStart(mWritingMode) += aDICoord;
    360  }
    361 
    362  void ExpandBy(nscoord aDISize, const nsSize& aContainerSize) {
    363    NS_ASSERTION(
    364        aContainerSize == mContainerSize || mContainerSize == nsSize(-1, -1),
    365        "container size doesn't match");
    366    mContainerSize = aContainerSize;
    367    mBounds.ISize(mWritingMode) += aDISize;
    368  }
    369 
    370  /**
    371   * The logical ascent (distance from block-start to baseline) of the
    372   * linebox is the logical ascent of the anonymous inline box (for
    373   * which we don't actually create a frame) that wraps all the
    374   * consecutive inline children of a block.
    375   *
    376   * This is currently unused for block lines.
    377   */
    378  nscoord GetLogicalAscent() const { return mAscent; }
    379  void SetLogicalAscent(nscoord aAscent) { mAscent = aAscent; }
    380 
    381  nscoord BStart() const { return mBounds.BStart(mWritingMode); }
    382  nscoord BSize() const { return mBounds.BSize(mWritingMode); }
    383  nscoord BEnd() const { return mBounds.BEnd(mWritingMode); }
    384  nscoord IStart() const { return mBounds.IStart(mWritingMode); }
    385  nscoord ISize() const { return mBounds.ISize(mWritingMode); }
    386  nscoord IEnd() const { return mBounds.IEnd(mWritingMode); }
    387  void SetBoundsEmpty() {
    388    mBounds.IStart(mWritingMode) = 0;
    389    mBounds.ISize(mWritingMode) = 0;
    390    mBounds.BStart(mWritingMode) = 0;
    391    mBounds.BSize(mWritingMode) = 0;
    392  }
    393 
    394  using DestroyContext = nsIFrame::DestroyContext;
    395  static void DeleteLineList(nsPresContext* aPresContext, nsLineList& aLines,
    396                             nsFrameList* aFrames, DestroyContext&);
    397 
    398  // search from end to beginning of [aBegin, aEnd)
    399  // Returns true if it found the line and false if not.
    400  // Moves aEnd as it searches so that aEnd points to the resulting line.
    401  // aLastFrameBeforeEnd is the last frame before aEnd (so if aEnd is
    402  // the end of the line list, it's just the last frame in the frame
    403  // list).
    404  static bool RFindLineContaining(nsIFrame* aFrame,
    405                                  const LineListIterator& aBegin,
    406                                  LineListIterator& aEnd,
    407                                  nsIFrame* aLastFrameBeforeEnd,
    408                                  int32_t* aFrameIndexInLine);
    409 
    410 #ifdef DEBUG_FRAME_DUMP
    411  static const char* UsedClearToString(mozilla::UsedClear aClearType);
    412 
    413  void List(FILE* out, int32_t aIndent,
    414            nsIFrame::ListFlags aFlags = nsIFrame::ListFlags()) const;
    415  void List(FILE* out = stderr, const char* aPrefix = "",
    416            nsIFrame::ListFlags aFlags = nsIFrame::ListFlags()) const;
    417  nsIFrame* LastChild() const;
    418 #endif
    419 
    420  void AddSizeOfExcludingThis(nsWindowSizes& aSizes) const;
    421 
    422  // Find the index of aFrame within the line, starting search at the start.
    423  int32_t IndexOf(const nsIFrame* aFrame) const;
    424 
    425  // Find the index of aFrame within the line, starting search from both ends
    426  // of the line and working inwards.
    427  // (Produces the same result as IndexOf, but with different performance
    428  // characteristics.)  The caller must provide the last frame in the line.
    429  int32_t RLIndexOf(const nsIFrame* aFrame,
    430                    const nsIFrame* aLastFrameInLine) const;
    431 
    432  bool Contains(const nsIFrame* aFrame) const {
    433    return MOZ_UNLIKELY(mFlags.mHasHashedFrames) ? mFrames->Contains(aFrame)
    434                                                 : IndexOf(aFrame) >= 0;
    435  }
    436 
    437  // whether the line box is "logically" empty (just like nsIFrame::IsEmpty)
    438  bool IsEmpty() const;
    439 
    440  // Call this only while in Reflow() for the block the line belongs
    441  // to, only between reflowing the line (or sliding it, if we skip
    442  // reflowing it) and the end of reflowing the block.
    443  bool CachedIsEmpty();
    444 
    445  void InvalidateCachedIsEmpty() { mFlags.mEmptyCacheValid = false; }
    446 
    447  // For debugging purposes
    448  bool IsValidCachedIsEmpty() { return mFlags.mEmptyCacheValid; }
    449 
    450 #ifdef DEBUG
    451  static int32_t GetCtorCount();
    452 #endif
    453 
    454  nsIFrame* mFirstChild;
    455 
    456  mozilla::WritingMode mWritingMode;
    457 
    458  // Physical size. Use only for physical <-> logical coordinate conversion.
    459  nsSize mContainerSize;
    460 
    461 private:
    462  mozilla::LogicalRect mBounds;
    463 
    464 public:
    465  const mozilla::LogicalRect& GetBounds() { return mBounds; }
    466  nsRect GetPhysicalBounds() const {
    467    if (mBounds.IsAllZero()) {
    468      return nsRect(0, 0, 0, 0);
    469    }
    470 
    471    NS_ASSERTION(mContainerSize != nsSize(-1, -1),
    472                 "mContainerSize not initialized");
    473    return mBounds.GetPhysicalRect(mWritingMode, mContainerSize);
    474  }
    475  void SetBounds(mozilla::WritingMode aWritingMode, nscoord aIStart,
    476                 nscoord aBStart, nscoord aISize, nscoord aBSize,
    477                 const nsSize& aContainerSize) {
    478    mWritingMode = aWritingMode;
    479    mContainerSize = aContainerSize;
    480    mBounds =
    481        mozilla::LogicalRect(aWritingMode, aIStart, aBStart, aISize, aBSize);
    482  }
    483 
    484  // mFlags.mHasHashedFrames says which one to use
    485  union {
    486    nsTHashSet<nsIFrame*>* mFrames;
    487    uint32_t mChildCount;
    488  };
    489 
    490  struct FlagBits {
    491    bool mDirty : 1;
    492    bool mPreviousMarginDirty : 1;
    493    bool mHasClearance : 1;
    494    bool mBlock : 1;
    495    bool mImpactedByFloat : 1;
    496    bool mLineWrapped : 1;
    497    bool mInvalidateTextRuns : 1;
    498    // default 0 = means that the opt potentially applies to this line.
    499    // 1 = never skip reflowing this line for a resize reflow
    500    bool mResizeReflowOptimizationDisabled : 1;
    501    bool mEmptyCacheValid : 1;
    502    bool mEmptyCacheState : 1;
    503    // mHasMarker indicates that this is an inline line whose block's
    504    // ::marker is adjacent to this line and non-empty.
    505    bool mHasMarker : 1;
    506    // Indicates that this line *may* have a placeholder for a float
    507    // that was pushed to a later column or page.
    508    bool mHadFloatPushed : 1;
    509    bool mHasHashedFrames : 1;
    510    // Indicates that this line is the one identified by an ancestor block
    511    // with -webkit-line-clamp on its legacy flex container, and that subsequent
    512    // lines under that block are "clamped" away, and therefore we need to
    513    // render a 'text-overflow: ellipsis'-like marker in this line.  At most one
    514    // line in the set of lines found by LineClampLineIterator for a given
    515    // block will have this flag set.
    516    bool mHasLineClampEllipsis : 1;
    517    // Has this line moved to a different fragment of the block since
    518    // the last time it was reflowed?
    519    bool mMovedFragments : 1;
    520    // mHasForcedLineBreakAfter indicates that this *inline* line has a
    521    // break-after status due to a float clearance or ending with <br>. A block
    522    // line shouldn't set this bit.
    523    //
    524    // Note: This bit is unrelated to CSS break-after property because it is all
    525    // about line break-after for inline-level boxes.
    526    bool mHasForcedLineBreakAfter : 1;
    527    // mFloatClearType indicates that there's a float clearance before a block
    528    // line, or after an inline line.
    529    mozilla::UsedClear mFloatClearType;
    530  };
    531 
    532  struct ExtraData {
    533    explicit ExtraData(const nsRect& aBounds)
    534        : mOverflowAreas(aBounds, aBounds) {}
    535    mozilla::OverflowAreas mOverflowAreas;
    536    // Union of the margin-boxes of our in-flow children (only children,
    537    // *not* their descendants). This is part of a special contribution to
    538    // the scrollable overflow of a scrolled block; as such, this is only
    539    // emplaced if our block is a scrolled frame (and we have in-flow children,
    540    // and floats, which are considered in-flow for scrollable overflow).
    541    mozilla::Maybe<nsRect> mInFlowChildBounds;
    542  };
    543 
    544  struct ExtraBlockData : public ExtraData {
    545    explicit ExtraBlockData(const nsRect& aBounds) : ExtraData(aBounds) {}
    546    mozilla::CollapsingMargin mCarriedOutBEndMargin;
    547  };
    548 
    549  struct ExtraInlineData : public ExtraData {
    550    explicit ExtraInlineData(const nsRect& aBounds)
    551        : ExtraData(aBounds),
    552          mFloatEdgeIStart(nscoord_MIN),
    553          mFloatEdgeIEnd(nscoord_MIN) {}
    554    nscoord mFloatEdgeIStart;
    555    nscoord mFloatEdgeIEnd;
    556    nsTArray<nsIFrame*> mFloats;
    557  };
    558 
    559  bool GetFloatEdges(nscoord* aStart, nscoord* aEnd) const {
    560    MOZ_ASSERT(IsInline(), "block line can't have float edges");
    561    if (mInlineData && mInlineData->mFloatEdgeIStart != nscoord_MIN) {
    562      *aStart = mInlineData->mFloatEdgeIStart;
    563      *aEnd = mInlineData->mFloatEdgeIEnd;
    564      return true;
    565    }
    566    return false;
    567  }
    568  void SetFloatEdges(nscoord aStart, nscoord aEnd);
    569  void ClearFloatEdges();
    570 
    571 protected:
    572  nscoord mAscent;  // see |SetAscent| / |GetAscent|
    573  static_assert(sizeof(FlagBits) <= sizeof(uint32_t),
    574                "size of FlagBits should not be larger than size of uint32_t");
    575  union {
    576    uint32_t mAllFlags;
    577    FlagBits mFlags;
    578  };
    579 
    580  union {
    581    ExtraData* mData;
    582    ExtraBlockData* mBlockData;
    583    ExtraInlineData* mInlineData;
    584  };
    585 
    586  void Cleanup();
    587  void MaybeFreeData();
    588 };
    589 
    590 /**
    591 * A linked list type where the items in the list must inherit from
    592 * a link type to fuse allocations.
    593 *
    594 * API heavily based on the |list| class in the C++ standard.
    595 */
    596 
    597 template <typename Link, bool IsReverse>
    598 class GenericLineListIterator {
    599  template <typename OtherLink, bool>
    600  friend class GenericLineListIterator;
    601 
    602 public:
    603  friend class nsLineList;
    604 
    605  using self_type = GenericLineListIterator<Link, IsReverse>;
    606  static constexpr bool is_const = std::is_const_v<Link>;
    607 
    608  using const_reference = const nsLineBox&;
    609  using const_pointer = const nsLineBox*;
    610  using reference = std::conditional_t<is_const, const_reference, nsLineBox&>;
    611  using pointer = std::conditional_t<is_const, const_pointer, nsLineBox*>;
    612  using size_type = uint32_t;
    613  using link_type = Link;
    614 
    615 #ifdef DEBUG
    616  GenericLineListIterator() : mListLink(nullptr) {
    617    memset(&mCurrent, 0xcd, sizeof(mCurrent));
    618  }
    619 #else
    620  // Auto generated default constructor OK.
    621 #endif
    622  // Auto generated copy-constructor OK.
    623 
    624  template <typename OtherLink, bool OtherIsReverse>
    625  self_type& operator=(
    626      const GenericLineListIterator<OtherLink, OtherIsReverse>& aOther) {
    627    mCurrent = aOther.mCurrent;
    628 #ifdef DEBUG
    629    mListLink = aOther.mListLink;
    630 #endif
    631    return *this;
    632  }
    633 
    634  self_type& SetPosition(pointer p) {
    635    mCurrent = p;
    636    return *this;
    637  }
    638 
    639  self_type& operator++() {
    640    mCurrent = IsReverse ? mCurrent->_mPrev : mCurrent->_mNext;
    641    return *this;
    642  }
    643 
    644  self_type operator++(int) {
    645    self_type rv(*this);
    646    mCurrent = IsReverse ? mCurrent->_mPrev : mCurrent->_mNext;
    647    return rv;
    648  }
    649 
    650  self_type& operator--() {
    651    mCurrent = IsReverse ? mCurrent->_mNext : mCurrent->_mPrev;
    652    return *this;
    653  }
    654 
    655  self_type operator--(int) {
    656    self_type rv(*this);
    657    mCurrent = IsReverse ? mCurrent->_mNext : mCurrent->_mPrev;
    658    return rv;
    659  }
    660 
    661  pointer get() {
    662    MOZ_ASSERT(mListLink);
    663    MOZ_ASSERT(mCurrent != mListLink, "running past end");
    664    return static_cast<pointer>(mCurrent);
    665  }
    666 
    667  const_pointer get() const {
    668    MOZ_ASSERT(mListLink);
    669    MOZ_ASSERT(mCurrent != mListLink, "running past end");
    670    return static_cast<const_pointer>(mCurrent);
    671  }
    672 
    673  reference operator*() { return *get(); }
    674  pointer operator->() { return get(); }
    675  operator pointer() { return get(); }
    676  const_reference operator*() const { return *get(); }
    677  const_pointer operator->() const { return get(); }
    678  operator const_pointer() const { return get(); }
    679 
    680  self_type next() {
    681    self_type copy(*this);
    682    return ++copy;
    683  }
    684 
    685  self_type next() const {
    686    self_type copy(*this);
    687    return ++copy;
    688  }
    689 
    690  self_type prev() {
    691    self_type copy(*this);
    692    return --copy;
    693  }
    694 
    695  self_type prev() const {
    696    self_type copy(*this);
    697    return --copy;
    698  }
    699 
    700  bool operator==(const self_type& aOther) const {
    701    MOZ_ASSERT(mListLink);
    702    MOZ_ASSERT(mListLink == aOther.mListLink,
    703               "comparing iterators over different lists");
    704    return mCurrent == aOther.mCurrent;
    705  }
    706  bool operator!=(const self_type&) const = default;
    707 
    708 #ifdef DEBUG
    709  bool IsInSameList(const self_type& aOther) const {
    710    return mListLink == aOther.mListLink;
    711  }
    712 #endif
    713 
    714 private:
    715  link_type* mCurrent;
    716 #ifdef DEBUG
    717  link_type* mListLink;  // the list's link, i.e., the end
    718 #endif
    719 };
    720 
    721 class nsLineList {
    722 public:
    723  using self_type = nsLineList;
    724  using const_reference = const nsLineBox&;
    725  using pointer = nsLineBox*;
    726  using const_pointer = const nsLineBox*;
    727  using size_type = uint32_t;
    728  using link_type = nsLineLink;
    729 
    730 private:
    731  link_type mLink;
    732 
    733 public:
    734  using iterator = GenericLineListIterator<nsLineLink, false>;
    735  using reverse_iterator = GenericLineListIterator<nsLineLink, true>;
    736  using const_iterator = GenericLineListIterator<const nsLineLink, false>;
    737  using const_reverse_iterator =
    738      GenericLineListIterator<const nsLineLink, true>;
    739 
    740  nsLineList() {
    741    MOZ_COUNT_CTOR(nsLineList);
    742    clear();
    743  }
    744 
    745  MOZ_COUNTED_DTOR(nsLineList)
    746 
    747  const_iterator begin() const {
    748    const_iterator rv;
    749    rv.mCurrent = mLink._mNext;
    750 #ifdef DEBUG
    751    rv.mListLink = &mLink;
    752 #endif
    753    return rv;
    754  }
    755 
    756  iterator begin() {
    757    iterator rv;
    758    rv.mCurrent = mLink._mNext;
    759 #ifdef DEBUG
    760    rv.mListLink = &mLink;
    761 #endif
    762    return rv;
    763  }
    764 
    765  iterator begin(nsLineBox* aLine) {
    766    iterator rv;
    767    rv.mCurrent = aLine;
    768 #ifdef DEBUG
    769    rv.mListLink = &mLink;
    770 #endif
    771    return rv;
    772  }
    773 
    774  const_iterator end() const {
    775    const_iterator rv;
    776    rv.mCurrent = &mLink;
    777 #ifdef DEBUG
    778    rv.mListLink = &mLink;
    779 #endif
    780    return rv;
    781  }
    782 
    783  iterator end() {
    784    iterator rv;
    785    rv.mCurrent = &mLink;
    786 #ifdef DEBUG
    787    rv.mListLink = &mLink;
    788 #endif
    789    return rv;
    790  }
    791 
    792  const_reverse_iterator rbegin() const {
    793    const_reverse_iterator rv;
    794    rv.mCurrent = mLink._mPrev;
    795 #ifdef DEBUG
    796    rv.mListLink = &mLink;
    797 #endif
    798    return rv;
    799  }
    800 
    801  reverse_iterator rbegin() {
    802    reverse_iterator rv;
    803    rv.mCurrent = mLink._mPrev;
    804 #ifdef DEBUG
    805    rv.mListLink = &mLink;
    806 #endif
    807    return rv;
    808  }
    809 
    810  reverse_iterator rbegin(nsLineBox* aLine) {
    811    reverse_iterator rv;
    812    rv.mCurrent = aLine;
    813 #ifdef DEBUG
    814    rv.mListLink = &mLink;
    815 #endif
    816    return rv;
    817  }
    818 
    819  const_reverse_iterator rend() const {
    820    const_reverse_iterator rv;
    821    rv.mCurrent = &mLink;
    822 #ifdef DEBUG
    823    rv.mListLink = &mLink;
    824 #endif
    825    return rv;
    826  }
    827 
    828  reverse_iterator rend() {
    829    reverse_iterator rv;
    830    rv.mCurrent = &mLink;
    831 #ifdef DEBUG
    832    rv.mListLink = &mLink;
    833 #endif
    834    return rv;
    835  }
    836 
    837  bool empty() const { return mLink._mNext == &mLink; }
    838 
    839  // NOTE: O(N).
    840  size_type size() const {
    841    size_type count = 0;
    842    for (const link_type* cur = mLink._mNext; cur != &mLink;
    843         cur = cur->_mNext) {
    844      ++count;
    845    }
    846    return count;
    847  }
    848 
    849  pointer front() {
    850    NS_ASSERTION(!empty(), "no element to return");
    851    return static_cast<pointer>(mLink._mNext);
    852  }
    853 
    854  const_pointer front() const {
    855    NS_ASSERTION(!empty(), "no element to return");
    856    return static_cast<const_pointer>(mLink._mNext);
    857  }
    858 
    859  pointer back() {
    860    NS_ASSERTION(!empty(), "no element to return");
    861    return static_cast<pointer>(mLink._mPrev);
    862  }
    863 
    864  const_pointer back() const {
    865    NS_ASSERTION(!empty(), "no element to return");
    866    return static_cast<const_pointer>(mLink._mPrev);
    867  }
    868 
    869  void push_front(pointer aNew) {
    870    aNew->_mNext = mLink._mNext;
    871    mLink._mNext->_mPrev = aNew;
    872    aNew->_mPrev = &mLink;
    873    mLink._mNext = aNew;
    874  }
    875 
    876  void pop_front()
    877  // NOTE: leaves dangling next/prev pointers
    878  {
    879    NS_ASSERTION(!empty(), "no element to pop");
    880    link_type* newFirst = mLink._mNext->_mNext;
    881    newFirst->_mPrev = &mLink;
    882    // mLink._mNext->_mNext = nullptr;
    883    // mLink._mNext->_mPrev = nullptr;
    884    mLink._mNext = newFirst;
    885  }
    886 
    887  void push_back(pointer aNew) {
    888    aNew->_mPrev = mLink._mPrev;
    889    mLink._mPrev->_mNext = aNew;
    890    aNew->_mNext = &mLink;
    891    mLink._mPrev = aNew;
    892  }
    893 
    894  void pop_back()
    895  // NOTE: leaves dangling next/prev pointers
    896  {
    897    NS_ASSERTION(!empty(), "no element to pop");
    898    link_type* newLast = mLink._mPrev->_mPrev;
    899    newLast->_mNext = &mLink;
    900    // mLink._mPrev->_mPrev = nullptr;
    901    // mLink._mPrev->_mNext = nullptr;
    902    mLink._mPrev = newLast;
    903  }
    904 
    905  // inserts x before position
    906  iterator before_insert(iterator position, pointer x) {
    907    // use |mCurrent| to prevent DEBUG_PASS_END assertions
    908    x->_mPrev = position.mCurrent->_mPrev;
    909    x->_mNext = position.mCurrent;
    910    position.mCurrent->_mPrev->_mNext = x;
    911    position.mCurrent->_mPrev = x;
    912    return --position;
    913  }
    914 
    915  // inserts x after position
    916  iterator after_insert(iterator position, pointer x) {
    917    // use |mCurrent| to prevent DEBUG_PASS_END assertions
    918    x->_mNext = position.mCurrent->_mNext;
    919    x->_mPrev = position.mCurrent;
    920    position.mCurrent->_mNext->_mPrev = x;
    921    position.mCurrent->_mNext = x;
    922    return ++position;
    923  }
    924 
    925  // returns iterator pointing to after the element
    926  iterator erase(iterator position)
    927  // NOTE: leaves dangling next/prev pointers (except in DEBUG build)
    928  {
    929    position->_mPrev->_mNext = position->_mNext;
    930    position->_mNext->_mPrev = position->_mPrev;
    931 #ifdef DEBUG
    932    nsLineLink* dead = position;
    933    iterator next = ++position;
    934    memset(dead, 0, sizeof(*dead));
    935    return next;
    936 #else
    937    return ++position;
    938 #endif
    939  }
    940 
    941  void swap(self_type& y) {
    942    link_type tmp(y.mLink);
    943    y.mLink = mLink;
    944    mLink = tmp;
    945 
    946    if (!empty()) {
    947      mLink._mNext->_mPrev = &mLink;
    948      mLink._mPrev->_mNext = &mLink;
    949    }
    950 
    951    if (!y.empty()) {
    952      y.mLink._mNext->_mPrev = &y.mLink;
    953      y.mLink._mPrev->_mNext = &y.mLink;
    954    }
    955  }
    956 
    957  void clear()
    958  // NOTE:  leaves dangling next/prev pointers
    959  {
    960    mLink._mNext = &mLink;
    961    mLink._mPrev = &mLink;
    962  }
    963 
    964  // inserts the conts of x before position and makes x empty
    965  void splice(iterator position, self_type& x) {
    966    // use |mCurrent| to prevent DEBUG_PASS_END assertions
    967    position.mCurrent->_mPrev->_mNext = x.mLink._mNext;
    968    x.mLink._mNext->_mPrev = position.mCurrent->_mPrev;
    969    x.mLink._mPrev->_mNext = position.mCurrent;
    970    position.mCurrent->_mPrev = x.mLink._mPrev;
    971    x.clear();
    972  }
    973 
    974  // Inserts element *i from list x before position and removes
    975  // it from x.
    976  void splice(iterator position, self_type& x, iterator i) {
    977    NS_ASSERTION(!x.empty(), "Can't insert from empty list.");
    978    NS_ASSERTION(position != i && position.mCurrent != i->_mNext,
    979                 "We don't check for this case.");
    980 
    981    // remove from |x|
    982    i->_mPrev->_mNext = i->_mNext;
    983    i->_mNext->_mPrev = i->_mPrev;
    984 
    985    // use |mCurrent| to prevent DEBUG_PASS_END assertions
    986    // link into |this|, before-side
    987    i->_mPrev = position.mCurrent->_mPrev;
    988    position.mCurrent->_mPrev->_mNext = i.get();
    989 
    990    // link into |this|, after-side
    991    i->_mNext = position.mCurrent;
    992    position.mCurrent->_mPrev = i.get();
    993  }
    994 
    995  // Inserts elements in [|first|, |last|), which are in |x|,
    996  // into |this| before |position| and removes them from |x|.
    997  void splice(iterator position, self_type& x, iterator first, iterator last) {
    998    NS_ASSERTION(!x.empty(), "Can't insert from empty list.");
    999 
   1000    if (first == last) {
   1001      return;
   1002    }
   1003 
   1004    --last;  // so we now want to move [first, last]
   1005    // remove from |x|
   1006    first->_mPrev->_mNext = last->_mNext;
   1007    last->_mNext->_mPrev = first->_mPrev;
   1008 
   1009    // use |mCurrent| to prevent DEBUG_PASS_END assertions
   1010    // link into |this|, before-side
   1011    first->_mPrev = position.mCurrent->_mPrev;
   1012    position.mCurrent->_mPrev->_mNext = first.get();
   1013 
   1014    // link into |this|, after-side
   1015    last->_mNext = position.mCurrent;
   1016    position.mCurrent->_mPrev = last.get();
   1017  }
   1018 };
   1019 
   1020 //----------------------------------------------------------------------
   1021 
   1022 class nsLineIterator final : public nsILineIterator {
   1023 public:
   1024  nsLineIterator(const nsLineList& aLines, bool aRightToLeft)
   1025      : mLines(aLines), mRightToLeft(aRightToLeft) {
   1026    mIter = mLines.begin();
   1027    if (mIter != mLines.end()) {
   1028      mIndex = 0;
   1029    }
   1030  }
   1031 
   1032  int32_t GetNumLines() const final {
   1033    if (mNumLines < 0) {
   1034      mNumLines = int32_t(mLines.size());  // This is O(N) in number of lines!
   1035    }
   1036    return mNumLines;
   1037  }
   1038 
   1039  bool IsLineIteratorFlowRTL() final { return mRightToLeft; }
   1040 
   1041  // Note that this updates the iterator's current position!
   1042  mozilla::Result<LineInfo, nsresult> GetLine(int32_t aLineNumber) final;
   1043 
   1044  int32_t FindLineContaining(const nsIFrame* aFrame,
   1045                             int32_t aStartLine = 0) final;
   1046 
   1047  NS_IMETHOD FindFrameAt(int32_t aLineNumber, nsPoint aPos,
   1048                         nsIFrame** aFrameFound, bool* aPosIsBeforeFirstFrame,
   1049                         bool* aPosIsAfterLastFrame) final;
   1050 
   1051  NS_IMETHOD CheckLineOrder(int32_t aLine, bool* aIsReordered,
   1052                            nsIFrame** aFirstVisual,
   1053                            nsIFrame** aLastVisual) final;
   1054 
   1055 private:
   1056  nsLineIterator() = delete;
   1057  nsLineIterator(const nsLineIterator& aOther) = delete;
   1058 
   1059  const nsLineBox* GetNextLine() {
   1060    MOZ_ASSERT(mIter != mLines.end(), "Already at end!");
   1061    ++mIndex;
   1062    ++mIter;
   1063    if (mIter == mLines.end()) {
   1064      MOZ_ASSERT(mNumLines < 0 || mNumLines == mIndex);
   1065      mNumLines = mIndex;
   1066      return nullptr;
   1067    }
   1068    return mIter.get();
   1069  }
   1070 
   1071  // Note that this updates the iterator's current position to the given line.
   1072  const nsLineBox* GetLineAt(int32_t aIndex) {
   1073    MOZ_ASSERT(mIndex >= 0);
   1074    if (aIndex < 0 || (mNumLines >= 0 && aIndex >= mNumLines)) {
   1075      return nullptr;
   1076    }
   1077    // Check if we should start counting lines from mIndex, or reset to the
   1078    // start or end of the list and count from there (if the requested index is
   1079    // closer to an end than to the current position).
   1080    if (aIndex < mIndex / 2) {
   1081      // Reset to the beginning and search from there.
   1082      mIter = mLines.begin();
   1083      mIndex = 0;
   1084    } else if (mNumLines > 0 && aIndex > (mNumLines + mIndex) / 2) {
   1085      // Jump to the end and search back from there.
   1086      mIter = mLines.end();
   1087      --mIter;
   1088      mIndex = mNumLines - 1;
   1089    }
   1090    while (mIndex > aIndex) {
   1091      // This cannot run past the start of the list, because we checked that
   1092      // aIndex is non-negative.
   1093      --mIter;
   1094      --mIndex;
   1095    }
   1096    while (mIndex < aIndex) {
   1097      // Here we have to check for reaching the end, as aIndex could be out of
   1098      // range (if mNumLines was not initialized, so we couldn't range-check
   1099      // aIndex on entry).
   1100      if (mIter == mLines.end()) {
   1101        MOZ_ASSERT(mNumLines < 0 || mNumLines == mIndex);
   1102        mNumLines = mIndex;
   1103        return nullptr;
   1104      }
   1105      ++mIter;
   1106      ++mIndex;
   1107    }
   1108    return mIter.get();
   1109  }
   1110 
   1111  const nsLineList& mLines;
   1112  nsLineList::const_iterator mIter;
   1113  int32_t mIndex = -1;
   1114  mutable int32_t mNumLines = -1;
   1115  const bool mRightToLeft;
   1116 };
   1117 
   1118 #endif /* nsLineBox_h___ */