tor-browser

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

nsLineBox.cpp (21606B)


      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 #include "nsLineBox.h"
     10 
     11 #include "mozilla/ArenaObjectID.h"
     12 #include "mozilla/Assertions.h"
     13 #include "mozilla/Likely.h"
     14 #include "mozilla/PresShell.h"
     15 #include "mozilla/Sprintf.h"
     16 #include "mozilla/ToString.h"
     17 #include "mozilla/WritingModes.h"
     18 #include "nsBidiPresUtils.h"
     19 #include "nsIFrame.h"
     20 #include "nsIFrameInlines.h"
     21 #include "nsPresArena.h"
     22 #include "nsPrintfCString.h"
     23 #include "nsWindowSizes.h"
     24 
     25 #ifdef DEBUG
     26 static int32_t ctorCount;
     27 int32_t nsLineBox::GetCtorCount() { return ctorCount; }
     28 #endif
     29 
     30 #ifndef _MSC_VER
     31 // static nsLineBox constant; initialized in the header file.
     32 const uint32_t nsLineBox::kMinChildCountForHashtable;
     33 #endif
     34 
     35 using namespace mozilla;
     36 
     37 nsLineBox::nsLineBox(nsIFrame* aFrame, int32_t aCount, bool aIsBlock)
     38    : mFirstChild(aFrame),
     39      mContainerSize(-1, -1),
     40      mBounds(WritingMode()),  // mBounds will be initialized with the correct
     41                               // writing mode when it is set
     42      mFrames(),
     43      mAscent(),
     44      mAllFlags(0),
     45      mData(nullptr) {
     46  // Assert that the union elements chosen for initialisation are at
     47  // least as large as all other elements in their respective unions, so
     48  // as to ensure that no parts are missed.
     49  static_assert(sizeof(mFrames) >= sizeof(mChildCount), "nsLineBox init #1");
     50  static_assert(sizeof(mAllFlags) >= sizeof(mFlags), "nsLineBox init #2");
     51  static_assert(sizeof(mData) >= sizeof(mBlockData), "nsLineBox init #3");
     52  static_assert(sizeof(mData) >= sizeof(mInlineData), "nsLineBox init #4");
     53 
     54  MOZ_COUNT_CTOR(nsLineBox);
     55 #ifdef DEBUG
     56  ++ctorCount;
     57  NS_ASSERTION(!aIsBlock || aCount == 1, "Blocks must have exactly one child");
     58  nsIFrame* f = aFrame;
     59  for (int32_t n = aCount; n > 0; f = f->GetNextSibling(), --n) {
     60    NS_ASSERTION(aIsBlock == f->IsBlockOutside(), "wrong kind of child frame");
     61  }
     62 #endif
     63  mChildCount = aCount;
     64  MarkDirty();
     65  mFlags.mBlock = aIsBlock;
     66 }
     67 
     68 nsLineBox::~nsLineBox() {
     69  MOZ_COUNT_DTOR(nsLineBox);
     70  if (MOZ_UNLIKELY(mFlags.mHasHashedFrames)) {
     71    delete mFrames;
     72  }
     73  Cleanup();
     74 }
     75 
     76 nsLineBox* NS_NewLineBox(PresShell* aPresShell, nsIFrame* aFrame,
     77                         bool aIsBlock) {
     78  return new (aPresShell) nsLineBox(aFrame, 1, aIsBlock);
     79 }
     80 
     81 nsLineBox* NS_NewLineBox(PresShell* aPresShell, nsLineBox* aFromLine,
     82                         nsIFrame* aFrame, int32_t aCount) {
     83  nsLineBox* newLine = new (aPresShell) nsLineBox(aFrame, aCount, false);
     84  newLine->NoteFramesMovedFrom(aFromLine);
     85  newLine->mContainerSize = aFromLine->mContainerSize;
     86  return newLine;
     87 }
     88 
     89 void nsLineBox::AddSizeOfExcludingThis(nsWindowSizes& aSizes) const {
     90  if (mFlags.mHasHashedFrames) {
     91    aSizes.mLayoutFramePropertiesSize +=
     92        mFrames->ShallowSizeOfIncludingThis(aSizes.mState.mMallocSizeOf);
     93  }
     94 }
     95 
     96 void nsLineBox::StealHashTableFrom(nsLineBox* aFromLine,
     97                                   uint32_t aFromLineNewCount) {
     98  MOZ_ASSERT(!mFlags.mHasHashedFrames);
     99  MOZ_ASSERT(GetChildCount() >= int32_t(aFromLineNewCount));
    100  mFrames = aFromLine->mFrames;
    101  mFlags.mHasHashedFrames = 1;
    102  aFromLine->mFlags.mHasHashedFrames = 0;
    103  aFromLine->mChildCount = aFromLineNewCount;
    104  // remove aFromLine's frames that aren't on this line
    105  nsIFrame* f = aFromLine->mFirstChild;
    106  for (uint32_t i = 0; i < aFromLineNewCount; f = f->GetNextSibling(), ++i) {
    107    mFrames->Remove(f);
    108  }
    109 }
    110 
    111 void nsLineBox::NoteFramesMovedFrom(nsLineBox* aFromLine) {
    112  uint32_t fromCount = aFromLine->GetChildCount();
    113  uint32_t toCount = GetChildCount();
    114  MOZ_ASSERT(toCount <= fromCount, "moved more frames than aFromLine has");
    115  uint32_t fromNewCount = fromCount - toCount;
    116  if (MOZ_LIKELY(!aFromLine->mFlags.mHasHashedFrames)) {
    117    aFromLine->mChildCount = fromNewCount;
    118    MOZ_ASSERT(toCount < kMinChildCountForHashtable);
    119  } else if (fromNewCount < kMinChildCountForHashtable) {
    120    // aFromLine has a hash table but will not have it after moving the frames
    121    // so this line can steal the hash table if it needs it.
    122    if (toCount >= kMinChildCountForHashtable) {
    123      StealHashTableFrom(aFromLine, fromNewCount);
    124    } else {
    125      delete aFromLine->mFrames;
    126      aFromLine->mFlags.mHasHashedFrames = 0;
    127      aFromLine->mChildCount = fromNewCount;
    128    }
    129  } else {
    130    // aFromLine still needs a hash table.
    131    if (toCount < kMinChildCountForHashtable) {
    132      // remove the moved frames from it
    133      nsIFrame* f = mFirstChild;
    134      for (uint32_t i = 0; i < toCount; f = f->GetNextSibling(), ++i) {
    135        aFromLine->mFrames->Remove(f);
    136      }
    137    } else if (toCount <= fromNewCount) {
    138      // This line needs a hash table, allocate a hash table for it since that
    139      // means fewer hash ops.
    140      nsIFrame* f = mFirstChild;
    141      for (uint32_t i = 0; i < toCount; f = f->GetNextSibling(), ++i) {
    142        aFromLine->mFrames->Remove(f);  // toCount RemoveEntry
    143      }
    144      SwitchToHashtable();  // toCount PutEntry
    145    } else {
    146      // This line needs a hash table, but it's fewer hash ops to steal
    147      // aFromLine's hash table and allocate a new hash table for that line.
    148      StealHashTableFrom(aFromLine, fromNewCount);  // fromNewCount RemoveEntry
    149      aFromLine->SwitchToHashtable();               // fromNewCount PutEntry
    150    }
    151  }
    152 }
    153 
    154 void* nsLineBox::operator new(size_t sz, PresShell* aPresShell) {
    155  return aPresShell->AllocateByObjectID(eArenaObjectID_nsLineBox, sz);
    156 }
    157 
    158 void nsLineBox::Destroy(PresShell* aPresShell) {
    159  this->nsLineBox::~nsLineBox();
    160  aPresShell->FreeByObjectID(eArenaObjectID_nsLineBox, this);
    161 }
    162 
    163 void nsLineBox::Cleanup() {
    164  if (mData) {
    165    if (IsBlock()) {
    166      delete mBlockData;
    167    } else {
    168      delete mInlineData;
    169    }
    170    mData = nullptr;
    171  }
    172 }
    173 
    174 #ifdef DEBUG_FRAME_DUMP
    175 static void ListFloats(FILE* out, const char* aPrefix,
    176                       const nsTArray<nsIFrame*>& aFloats,
    177                       bool aListOnlyDeterministic) {
    178  for (nsIFrame* f : aFloats) {
    179    nsCString str(aPrefix);
    180    str += "floatframe";
    181    nsIFrame::ListPtr(str, aListOnlyDeterministic, f, "@");
    182    nsAutoString frameName;
    183    f->GetFrameName(frameName);
    184    str += NS_ConvertUTF16toUTF8(frameName).get();
    185    fprintf_stderr(out, "%s\n", str.get());
    186  }
    187 }
    188 
    189 /* static */ const char* nsLineBox::UsedClearToString(UsedClear aClearType) {
    190  switch (aClearType) {
    191    case UsedClear::None:
    192      return "none";
    193    case UsedClear::Left:
    194      return "left";
    195    case UsedClear::Right:
    196      return "right";
    197    case UsedClear::Both:
    198      return "both";
    199  }
    200  return "unknown";
    201 }
    202 
    203 void nsLineBox::List(FILE* out, int32_t aIndent,
    204                     nsIFrame::ListFlags aFlags) const {
    205  nsCString str;
    206  while (aIndent-- > 0) {
    207    str += "  ";
    208  }
    209  List(out, str.get(), aFlags);
    210 }
    211 
    212 void nsLineBox::List(FILE* out, const char* aPrefix,
    213                     nsIFrame::ListFlags aFlags) const {
    214  nsCString str(aPrefix);
    215  str += "line";
    216  nsIFrame::ListPtr(str, aFlags, this, "@");
    217  str += nsPrintfCString(
    218      " count=%d state=%s,%s,%s,%s,%s,%s,clear-before:%s,clear-after:%s ",
    219      GetChildCount(), IsBlock() ? "block" : "inline",
    220      IsDirty() ? "dirty" : "clean",
    221      IsPreviousMarginDirty() ? "prevmargindirty" : "prevmarginclean",
    222      IsImpactedByFloat() ? "impacted" : "not-impacted",
    223      IsLineWrapped() ? "wrapped" : "not-wrapped",
    224      HasForcedLineBreakAfter() ? "forced-break-after" : "no-break",
    225      UsedClearToString(FloatClearTypeBefore()),
    226      UsedClearToString(FloatClearTypeAfter()));
    227 
    228  if (IsBlock() && !GetCarriedOutBEndMargin().IsZero()) {
    229    const nscoord bm = GetCarriedOutBEndMargin().Get();
    230    str += nsPrintfCString("bm=%s ",
    231                           nsIFrame::ConvertToString(bm, aFlags).c_str());
    232  }
    233  nsRect bounds = GetPhysicalBounds();
    234  str +=
    235      nsPrintfCString("%s ", nsIFrame::ConvertToString(bounds, aFlags).c_str());
    236  if (mWritingMode.IsVertical() || mWritingMode.IsBidiRTL()) {
    237    str += nsPrintfCString(
    238        "wm=%s cs=(%s) logical-rect=%s ", ToString(mWritingMode).c_str(),
    239        nsIFrame::ConvertToString(mContainerSize, aFlags).c_str(),
    240        nsIFrame::ConvertToString(mBounds, mWritingMode, aFlags).c_str());
    241  }
    242  if (mData) {
    243    const nsRect vo = mData->mOverflowAreas.InkOverflow();
    244    const nsRect so = mData->mOverflowAreas.ScrollableOverflow();
    245    if (!vo.IsEqualEdges(bounds) || !so.IsEqualEdges(bounds)) {
    246      str += nsPrintfCString("ink-overflow=%s scr-overflow=%s ",
    247                             nsIFrame::ConvertToString(vo, aFlags).c_str(),
    248                             nsIFrame::ConvertToString(so, aFlags).c_str());
    249    }
    250    if (mData->mInFlowChildBounds) {
    251      str += nsPrintfCString(
    252          "in-flow-scr-overflow=%s ",
    253          nsIFrame::ConvertToString(*mData->mInFlowChildBounds, aFlags)
    254              .c_str());
    255    }
    256  }
    257  fprintf_stderr(out, "%s<\n", str.get());
    258 
    259  nsIFrame* frame = mFirstChild;
    260  int32_t n = GetChildCount();
    261  nsCString pfx(aPrefix);
    262  pfx += "  ";
    263  while (--n >= 0) {
    264    frame->List(out, pfx.get(), aFlags);
    265    frame = frame->GetNextSibling();
    266  }
    267 
    268  if (HasFloats()) {
    269    fprintf_stderr(out, "%s> floats <\n", aPrefix);
    270    ListFloats(out, pfx.get(), mInlineData->mFloats,
    271               aFlags.contains(nsIFrame::ListFlag::OnlyListDeterministicInfo));
    272  }
    273  fprintf_stderr(out, "%s>\n", aPrefix);
    274 }
    275 
    276 nsIFrame* nsLineBox::LastChild() const {
    277  nsIFrame* frame = mFirstChild;
    278  int32_t n = GetChildCount() - 1;
    279  while (--n >= 0) {
    280    frame = frame->GetNextSibling();
    281  }
    282  return frame;
    283 }
    284 #endif
    285 
    286 int32_t nsLineBox::IndexOf(const nsIFrame* aFrame) const {
    287  int32_t i, n = GetChildCount();
    288  const nsIFrame* frame = mFirstChild;
    289  for (i = 0; i < n; i++) {
    290    if (frame == aFrame) {
    291      return i;
    292    }
    293    frame = frame->GetNextSibling();
    294  }
    295  return -1;
    296 }
    297 
    298 int32_t nsLineBox::RLIndexOf(const nsIFrame* aFrame,
    299                             const nsIFrame* aLastFrameInLine) const {
    300  const nsIFrame* leftFrame = mFirstChild;
    301  const nsIFrame* rightFrame = aLastFrameInLine;
    302  int32_t leftIndex = 0, rightIndex = GetChildCount() - 1;
    303  while (true) {
    304    if (aFrame == rightFrame) {
    305      return rightIndex;
    306    }
    307    if (leftIndex == rightIndex) {
    308      MOZ_ASSERT(leftFrame == rightFrame,
    309                 "caller provided incorrect last frame");
    310      break;
    311    }
    312    if (aFrame == leftFrame) {
    313      return leftIndex;
    314    }
    315    if (++leftIndex == rightIndex) {
    316      MOZ_ASSERT(leftFrame->GetNextSibling() == rightFrame,
    317                 "caller provided incorrect last frame");
    318      break;
    319    }
    320    leftFrame = leftFrame->GetNextSibling();
    321    rightFrame = rightFrame->GetPrevSibling();
    322    --rightIndex;
    323  }
    324  return -1;
    325 }
    326 
    327 bool nsLineBox::IsEmpty() const {
    328  if (IsBlock()) {
    329    return mFirstChild->IsEmpty();
    330  }
    331 
    332  nsIFrame* kid = mFirstChild;
    333  for (int32_t n = GetChildCount(); n > 0; --n, kid = kid->GetNextSibling()) {
    334    if (!kid->IsEmpty()) {
    335      return false;
    336    }
    337  }
    338  if (HasMarker()) {
    339    return false;
    340  }
    341  return true;
    342 }
    343 
    344 bool nsLineBox::CachedIsEmpty() {
    345  if (mFlags.mDirty) {
    346    return IsEmpty();
    347  }
    348 
    349  if (mFlags.mEmptyCacheValid) {
    350    return mFlags.mEmptyCacheState;
    351  }
    352 
    353  bool result;
    354  if (IsBlock()) {
    355    result = mFirstChild->CachedIsEmpty();
    356  } else {
    357    nsIFrame* kid = mFirstChild;
    358    result = true;
    359    for (int32_t n = GetChildCount(); n > 0; --n, kid = kid->GetNextSibling()) {
    360      if (!kid->CachedIsEmpty()) {
    361        result = false;
    362        break;
    363      }
    364    }
    365    if (HasMarker()) {
    366      result = false;
    367    }
    368  }
    369 
    370  mFlags.mEmptyCacheValid = true;
    371  mFlags.mEmptyCacheState = result;
    372  return result;
    373 }
    374 
    375 void nsLineBox::DeleteLineList(nsPresContext* aPresContext, nsLineList& aLines,
    376                               nsFrameList* aFrames, DestroyContext& aContext) {
    377  PresShell* presShell = aPresContext->PresShell();
    378 
    379  // Keep our line list and frame list up to date as we remove frames, in case
    380  // something wants to traverse the frame tree while we're destroying.
    381  //
    382  // Note: We delete the line list and the frames in each line in reverse order
    383  // to avoid unnecessary updates to the first-continuation and first-in-flow
    384  // cache. If we delete them from front to back, updating the cache has a
    385  // O(n^2) time complexity.
    386  while (!aLines.empty()) {
    387    nsLineBox* line = aLines.back();
    388    if (MOZ_UNLIKELY(line->mFlags.mHasHashedFrames)) {
    389      line->SwitchToCounter();  // Avoid expensive has table removals.
    390    }
    391    while (line->GetChildCount() > 0) {
    392      nsIFrame* child = aFrames->RemoveLastChild();
    393      MOZ_DIAGNOSTIC_ASSERT(child->PresContext() == aPresContext);
    394      line->NoteFrameRemoved(child);
    395      child->Destroy(aContext);
    396    }
    397    MOZ_DIAGNOSTIC_ASSERT(line == aLines.back(),
    398                          "destroying child frames messed up our lines!");
    399    aLines.pop_back();
    400    line->Destroy(presShell);
    401  }
    402 }
    403 
    404 bool nsLineBox::RFindLineContaining(nsIFrame* aFrame,
    405                                    const nsLineList::iterator& aBegin,
    406                                    nsLineList::iterator& aEnd,
    407                                    nsIFrame* aLastFrameBeforeEnd,
    408                                    int32_t* aFrameIndexInLine) {
    409  MOZ_ASSERT(aFrame, "null ptr");
    410 
    411  nsIFrame* curFrame = aLastFrameBeforeEnd;
    412  while (aBegin != aEnd) {
    413    --aEnd;
    414    NS_ASSERTION(aEnd->LastChild() == curFrame, "Unexpected curFrame");
    415    if (MOZ_UNLIKELY(aEnd->mFlags.mHasHashedFrames) &&
    416        !aEnd->Contains(aFrame)) {
    417      if (aEnd->mFirstChild) {
    418        curFrame = aEnd->mFirstChild->GetPrevSibling();
    419      }
    420      continue;
    421    }
    422    // i is the index of curFrame in aEnd
    423    int32_t i = aEnd->GetChildCount() - 1;
    424    while (i >= 0) {
    425      if (curFrame == aFrame) {
    426        *aFrameIndexInLine = i;
    427        return true;
    428      }
    429      --i;
    430      curFrame = curFrame->GetPrevSibling();
    431    }
    432    MOZ_ASSERT(!aEnd->mFlags.mHasHashedFrames, "Contains lied to us!");
    433  }
    434  *aFrameIndexInLine = -1;
    435  return false;
    436 }
    437 
    438 CollapsingMargin nsLineBox::GetCarriedOutBEndMargin() const {
    439  NS_ASSERTION(IsBlock(), "GetCarriedOutBEndMargin called on non-block line.");
    440  return (IsBlock() && mBlockData) ? mBlockData->mCarriedOutBEndMargin
    441                                   : CollapsingMargin();
    442 }
    443 
    444 bool nsLineBox::SetCarriedOutBEndMargin(CollapsingMargin aValue) {
    445  bool changed = false;
    446  if (IsBlock()) {
    447    if (!aValue.IsZero()) {
    448      if (!mBlockData) {
    449        mBlockData = new ExtraBlockData(GetPhysicalBounds());
    450      }
    451      changed = aValue != mBlockData->mCarriedOutBEndMargin;
    452      mBlockData->mCarriedOutBEndMargin = aValue;
    453    } else if (mBlockData) {
    454      changed = aValue != mBlockData->mCarriedOutBEndMargin;
    455      mBlockData->mCarriedOutBEndMargin = aValue;
    456      MaybeFreeData();
    457    }
    458  }
    459  return changed;
    460 }
    461 
    462 void nsLineBox::MaybeFreeData() {
    463  nsRect bounds = GetPhysicalBounds();
    464  // If we have space allocated for additional data but no additional data to
    465  // represent, just delete it.
    466  if (mData && mData->mOverflowAreas == OverflowAreas(bounds, bounds) &&
    467      !mData->mInFlowChildBounds) {
    468    if (IsInline()) {
    469      if (mInlineData->mFloats.IsEmpty()) {
    470        delete mInlineData;
    471        mInlineData = nullptr;
    472      }
    473    } else if (mBlockData->mCarriedOutBEndMargin.IsZero()) {
    474      delete mBlockData;
    475      mBlockData = nullptr;
    476    }
    477  }
    478 }
    479 
    480 void nsLineBox::ClearFloats() {
    481  MOZ_ASSERT(IsInline(), "block line can't have floats");
    482  if (IsInline() && mInlineData) {
    483    mInlineData->mFloats.Clear();
    484    MaybeFreeData();
    485  }
    486 }
    487 
    488 void nsLineBox::AppendFloats(nsTArray<nsIFrame*>&& aFloats) {
    489  MOZ_ASSERT(IsInline(), "block line can't have floats");
    490  if (MOZ_UNLIKELY(!IsInline())) {
    491    return;
    492  }
    493  if (!aFloats.IsEmpty()) {
    494    if (mInlineData) {
    495      mInlineData->mFloats.AppendElements(std::move(aFloats));
    496    } else {
    497      mInlineData = new ExtraInlineData(GetPhysicalBounds());
    498      mInlineData->mFloats = std::move(aFloats);
    499    }
    500  }
    501 }
    502 
    503 bool nsLineBox::RemoveFloat(nsIFrame* aFrame) {
    504  MOZ_ASSERT(IsInline(), "block line can't have floats");
    505  MOZ_ASSERT(aFrame);
    506  if (IsInline() && mInlineData) {
    507    if (mInlineData->mFloats.RemoveElement(aFrame)) {
    508      // Note: the placeholder is part of the line's child list
    509      // and will be removed later.
    510      MaybeFreeData();
    511      return true;
    512    }
    513  }
    514  return false;
    515 }
    516 
    517 void nsLineBox::SetFloatEdges(nscoord aStart, nscoord aEnd) {
    518  MOZ_ASSERT(IsInline(), "block line can't have float edges");
    519  if (!mInlineData) {
    520    mInlineData = new ExtraInlineData(GetPhysicalBounds());
    521  }
    522  mInlineData->mFloatEdgeIStart = aStart;
    523  mInlineData->mFloatEdgeIEnd = aEnd;
    524 }
    525 
    526 void nsLineBox::ClearFloatEdges() {
    527  MOZ_ASSERT(IsInline(), "block line can't have float edges");
    528  if (mInlineData) {
    529    mInlineData->mFloatEdgeIStart = nscoord_MIN;
    530    mInlineData->mFloatEdgeIEnd = nscoord_MIN;
    531  }
    532 }
    533 
    534 void nsLineBox::SetOverflowAreas(const OverflowAreas& aOverflowAreas) {
    535 #ifdef DEBUG
    536  for (const auto otype : mozilla::AllOverflowTypes()) {
    537    NS_ASSERTION(aOverflowAreas.Overflow(otype).width >= 0,
    538                 "Illegal width for an overflow area!");
    539    NS_ASSERTION(aOverflowAreas.Overflow(otype).height >= 0,
    540                 "Illegal height for an overflow area!");
    541  }
    542 #endif
    543 
    544  nsRect bounds = GetPhysicalBounds();
    545  if (!aOverflowAreas.InkOverflow().IsEqualInterior(bounds) ||
    546      !aOverflowAreas.ScrollableOverflow().IsEqualEdges(bounds)) {
    547    if (!mData) {
    548      if (IsInline()) {
    549        mInlineData = new ExtraInlineData(bounds);
    550      } else {
    551        mBlockData = new ExtraBlockData(bounds);
    552      }
    553    }
    554    mData->mOverflowAreas = aOverflowAreas;
    555  } else if (mData) {
    556    // Store away new value so that MaybeFreeData compares against
    557    // the right value.
    558    mData->mOverflowAreas = aOverflowAreas;
    559    MaybeFreeData();
    560  }
    561 }
    562 
    563 void nsLineBox::SetInFlowChildBounds(const Maybe<nsRect>& aInFlowChildBounds) {
    564  if (aInFlowChildBounds) {
    565    if (!mData) {
    566      nsRect bounds = GetPhysicalBounds();
    567      if (IsInline()) {
    568        mInlineData = new ExtraInlineData(bounds);
    569      } else {
    570        mBlockData = new ExtraBlockData(bounds);
    571      }
    572    }
    573    mData->mInFlowChildBounds = aInFlowChildBounds;
    574  } else if (mData) {
    575    mData->mInFlowChildBounds = Nothing{};
    576    MaybeFreeData();
    577  }
    578 }
    579 
    580 Maybe<nsRect> nsLineBox::GetInFlowChildBounds() const {
    581  if (!mData) {
    582    return Nothing{};
    583  }
    584  return mData->mInFlowChildBounds;
    585 }
    586 
    587 //----------------------------------------------------------------------
    588 
    589 Result<nsILineIterator::LineInfo, nsresult> nsLineIterator::GetLine(
    590    int32_t aLineNumber) {
    591  const nsLineBox* line = GetLineAt(aLineNumber);
    592  if (!line) {
    593    return Err(NS_ERROR_FAILURE);
    594  }
    595  LineInfo structure;
    596  structure.mFirstFrameOnLine = line->mFirstChild;
    597  structure.mNumFramesOnLine = line->GetChildCount();
    598  structure.mLineBounds = line->GetPhysicalBounds();
    599  structure.mIsWrapped = line->IsLineWrapped();
    600  return structure;
    601 }
    602 
    603 int32_t nsLineIterator::FindLineContaining(const nsIFrame* aFrame,
    604                                           int32_t aStartLine) {
    605  const nsLineBox* line = GetLineAt(aStartLine);
    606  MOZ_ASSERT(line, "aStartLine out of range");
    607  while (line) {
    608    if (line->Contains(aFrame)) {
    609      return mIndex;
    610    }
    611    line = GetNextLine();
    612  }
    613  return -1;
    614 }
    615 
    616 NS_IMETHODIMP
    617 nsLineIterator::CheckLineOrder(int32_t aLine, bool* aIsReordered,
    618                               nsIFrame** aFirstVisual,
    619                               nsIFrame** aLastVisual) {
    620  const nsLineBox* line = GetLineAt(aLine);
    621  MOZ_ASSERT(line, "aLine out of range!");
    622 
    623  if (!line || !line->mFirstChild) {  // empty line
    624    *aIsReordered = false;
    625    *aFirstVisual = nullptr;
    626    *aLastVisual = nullptr;
    627    return NS_OK;
    628  }
    629 
    630  nsIFrame* leftmostFrame;
    631  nsIFrame* rightmostFrame;
    632  *aIsReordered =
    633      nsBidiPresUtils::CheckLineOrder(line->mFirstChild, line->GetChildCount(),
    634                                      &leftmostFrame, &rightmostFrame);
    635 
    636  // map leftmost/rightmost to first/last according to paragraph direction
    637  *aFirstVisual = mRightToLeft ? rightmostFrame : leftmostFrame;
    638  *aLastVisual = mRightToLeft ? leftmostFrame : rightmostFrame;
    639 
    640  return NS_OK;
    641 }
    642 
    643 NS_IMETHODIMP
    644 nsLineIterator::FindFrameAt(int32_t aLineNumber, nsPoint aPos,
    645                            nsIFrame** aFrameFound,
    646                            bool* aPosIsBeforeFirstFrame,
    647                            bool* aPosIsAfterLastFrame) {
    648  MOZ_ASSERT(aFrameFound && aPosIsBeforeFirstFrame && aPosIsAfterLastFrame,
    649             "null OUT ptr");
    650 
    651  if (!aFrameFound || !aPosIsBeforeFirstFrame || !aPosIsAfterLastFrame) {
    652    return NS_ERROR_NULL_POINTER;
    653  }
    654 
    655  const nsLineBox* line = GetLineAt(aLineNumber);
    656  if (!line) {
    657    *aFrameFound = nullptr;
    658    *aPosIsBeforeFirstFrame = true;
    659    *aPosIsAfterLastFrame = false;
    660    return NS_OK;
    661  }
    662 
    663  if (line->ISize() == 0 && line->BSize() == 0) {
    664    return NS_ERROR_FAILURE;
    665  }
    666 
    667  LineFrameFinder finder(aPos, line->mContainerSize, line->mWritingMode,
    668                         mRightToLeft);
    669  int32_t n = line->GetChildCount();
    670  nsIFrame* frame = line->mFirstChild;
    671  while (n--) {
    672    finder.Scan(frame);
    673    if (finder.IsDone()) {
    674      break;
    675    }
    676    frame = frame->GetNextSibling();
    677  }
    678  finder.Finish(aFrameFound, aPosIsBeforeFirstFrame, aPosIsAfterLastFrame);
    679  return NS_OK;
    680 }