tor-browser

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

nsBlockFrame.cpp (346943B)


      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 /*
      8 * rendering object for CSS display:block, inline-block, and list-item
      9 * boxes, also used for various anonymous boxes
     10 */
     11 
     12 #include "nsBlockFrame.h"
     13 
     14 #include <inttypes.h>
     15 
     16 #include <algorithm>
     17 
     18 #include "BlockReflowState.h"
     19 #include "CounterStyleManager.h"
     20 #include "TextOverflow.h"
     21 #include "fmt/format.h"
     22 #include "gfxContext.h"
     23 #include "mozilla/AbsoluteContainingBlock.h"
     24 #include "mozilla/AppUnits.h"
     25 #include "mozilla/Baseline.h"
     26 #include "mozilla/ComputedStyle.h"
     27 #include "mozilla/DebugOnly.h"
     28 #include "mozilla/DefineEnum.h"
     29 #include "mozilla/Likely.h"
     30 #include "mozilla/Maybe.h"
     31 #include "mozilla/PresShell.h"
     32 #include "mozilla/RestyleManager.h"
     33 #include "mozilla/SVGUtils.h"
     34 #include "mozilla/ScrollContainerFrame.h"
     35 #include "mozilla/ServoStyleSet.h"
     36 #include "mozilla/Sprintf.h"
     37 #include "mozilla/StaticPrefs_browser.h"
     38 #include "mozilla/StaticPrefs_layout.h"
     39 #include "mozilla/ToString.h"
     40 #include "mozilla/UniquePtr.h"
     41 #include "mozilla/dom/Selection.h"
     42 #include "nsBidiPresUtils.h"
     43 #include "nsBlockReflowContext.h"
     44 #include "nsCOMPtr.h"
     45 #include "nsCRT.h"
     46 #include "nsCSSFrameConstructor.h"
     47 #include "nsCSSRendering.h"
     48 #include "nsDisplayList.h"
     49 #include "nsError.h"
     50 #include "nsFirstLetterFrame.h"
     51 #include "nsFlexContainerFrame.h"
     52 #include "nsFloatManager.h"
     53 #include "nsFontMetrics.h"
     54 #include "nsFrameManager.h"
     55 #include "nsGenericHTMLElement.h"
     56 #include "nsGkAtoms.h"
     57 #include "nsHTMLParts.h"
     58 #include "nsIFrameInlines.h"
     59 #include "nsLayoutUtils.h"
     60 #include "nsLineBox.h"
     61 #include "nsLineLayout.h"
     62 #include "nsPlaceholderFrame.h"
     63 #include "nsPresContext.h"
     64 #include "nsPresContextInlines.h"
     65 #include "nsStyleConsts.h"
     66 #include "nsTextControlFrame.h"
     67 #include "prenv.h"
     68 
     69 static const int MIN_LINES_NEEDING_CURSOR = 20;
     70 
     71 using namespace mozilla;
     72 using namespace mozilla::css;
     73 using namespace mozilla::dom;
     74 using namespace mozilla::layout;
     75 using ClearFloatsResult = BlockReflowState::ClearFloatsResult;
     76 using ShapeType = nsFloatManager::ShapeType;
     77 
     78 static void MarkAllInlineLinesDirty(nsBlockFrame* aBlock) {
     79  for (auto& line : aBlock->Lines()) {
     80    if (line.IsInline()) {
     81      line.MarkDirty();
     82    }
     83  }
     84 }
     85 
     86 static void MarkAllDescendantLinesDirty(nsBlockFrame* aBlock) {
     87  for (auto& line : aBlock->Lines()) {
     88    if (line.IsBlock()) {
     89      nsBlockFrame* bf = do_QueryFrame(line.mFirstChild);
     90      if (bf) {
     91        MarkAllDescendantLinesDirty(bf);
     92      }
     93    }
     94    line.MarkDirty();
     95  }
     96 }
     97 
     98 static void MarkSameFloatManagerLinesDirty(nsBlockFrame* aBlock) {
     99  nsBlockFrame* blockWithFloatMgr = aBlock;
    100  while (!blockWithFloatMgr->HasAnyStateBits(NS_BLOCK_BFC)) {
    101    nsBlockFrame* bf = do_QueryFrame(blockWithFloatMgr->GetParent());
    102    if (!bf) {
    103      break;
    104    }
    105    blockWithFloatMgr = bf;
    106  }
    107 
    108  // Mark every line at and below the line where the float was
    109  // dirty, and mark their lines dirty too. We could probably do
    110  // something more efficient --- e.g., just dirty the lines that intersect
    111  // the float vertically.
    112  MarkAllDescendantLinesDirty(blockWithFloatMgr);
    113 }
    114 
    115 /**
    116 * Returns true if aFrame is a block that has one or more float children.
    117 */
    118 static bool BlockHasAnyFloats(nsIFrame* aFrame) {
    119  nsBlockFrame* block = do_QueryFrame(aFrame);
    120  if (!block) {
    121    return false;
    122  }
    123  if (block->GetChildList(FrameChildListID::Float).FirstChild()) {
    124    return true;
    125  }
    126 
    127  for (const auto& line : block->Lines()) {
    128    if (line.IsBlock() && BlockHasAnyFloats(line.mFirstChild)) {
    129      return true;
    130    }
    131  }
    132  return false;
    133 }
    134 
    135 // Determines whether the given frame is visible text or has visible text that
    136 // participate in the same line. Frames that are not line participants do not
    137 // have their children checked.
    138 static bool FrameHasVisibleInlineText(nsIFrame* aFrame) {
    139  MOZ_ASSERT(aFrame, "Frame argument cannot be null");
    140  if (!aFrame->IsLineParticipant()) {
    141    return false;
    142  }
    143  if (aFrame->IsTextFrame()) {
    144    return aFrame->StyleVisibility()->IsVisible() &&
    145           NS_GET_A(aFrame->StyleText()->mWebkitTextFillColor.CalcColor(
    146               aFrame)) != 0;
    147  }
    148  for (nsIFrame* kid : aFrame->PrincipalChildList()) {
    149    if (FrameHasVisibleInlineText(kid)) {
    150      return true;
    151    }
    152  }
    153  return false;
    154 }
    155 
    156 // Determines whether any of the frames from the given line have visible text.
    157 static bool LineHasVisibleInlineText(nsLineBox* aLine) {
    158  nsIFrame* kid = aLine->mFirstChild;
    159  int32_t n = aLine->GetChildCount();
    160  while (n-- > 0) {
    161    if (FrameHasVisibleInlineText(kid)) {
    162      return true;
    163    }
    164    kid = kid->GetNextSibling();
    165  }
    166  return false;
    167 }
    168 
    169 /**
    170 * Iterates through the frame's in-flow children and
    171 * unions the ink overflow of all text frames which
    172 * participate in the line aFrame belongs to.
    173 * If a child of aFrame is not a text frame,
    174 * we recurse with the child as the aFrame argument.
    175 * If aFrame isn't a line participant, we skip it entirely
    176 * and return an empty rect.
    177 * The resulting nsRect is offset relative to the parent of aFrame.
    178 */
    179 static nsRect GetFrameTextArea(nsIFrame* aFrame,
    180                               nsDisplayListBuilder* aBuilder) {
    181  nsRect textArea;
    182  if (const nsTextFrame* textFrame = do_QueryFrame(aFrame)) {
    183    if (!textFrame->IsEntirelyWhitespace()) {
    184      textArea = aFrame->InkOverflowRect();
    185    }
    186  } else if (aFrame->IsLineParticipant()) {
    187    for (nsIFrame* kid : aFrame->PrincipalChildList()) {
    188      nsRect kidTextArea = GetFrameTextArea(kid, aBuilder);
    189      textArea.OrWith(kidTextArea);
    190    }
    191  }
    192  // add aFrame's position to keep textArea relative to aFrame's parent
    193  return textArea + aFrame->GetPosition();
    194 }
    195 
    196 /**
    197 * Iterates through the line's children and
    198 * unions the ink overflow of all text frames.
    199 * GetFrameTextArea unions and returns the ink overflow
    200 * from all line-participating text frames within the given child.
    201 * The nsRect returned from GetLineTextArea is offset
    202 * relative to the given line.
    203 */
    204 static nsRect GetLineTextArea(nsLineBox* aLine,
    205                              nsDisplayListBuilder* aBuilder) {
    206  nsRect textArea;
    207  nsIFrame* kid = aLine->mFirstChild;
    208  int32_t n = aLine->GetChildCount();
    209  while (n-- > 0) {
    210    nsRect kidTextArea = GetFrameTextArea(kid, aBuilder);
    211    textArea.OrWith(kidTextArea);
    212    kid = kid->GetNextSibling();
    213  }
    214 
    215  return textArea;
    216 }
    217 
    218 /**
    219 * Starting with aFrame, iterates upward through parent frames and checks for
    220 * non-transparent background colors. If one is found, we use that as our
    221 * backplate color. Otheriwse, we use the default background color from
    222 * our high contrast theme.
    223 */
    224 static nscolor GetBackplateColor(nsIFrame* aFrame) {
    225  nsPresContext* pc = aFrame->PresContext();
    226  nscolor currentBackgroundColor = NS_TRANSPARENT;
    227  for (nsIFrame* frame = aFrame; frame; frame = frame->GetParent()) {
    228    // NOTE(emilio): We assume themed frames (frame->IsThemed()) have correct
    229    // background-color information so as to compute the right backplate color.
    230    //
    231    // This holds because HTML widgets with author-specified backgrounds or
    232    // borders disable theming. So as long as the UA-specified background colors
    233    // match the actual theme (which they should because we always use system
    234    // colors with the non-native theme, and native system colors should also
    235    // match the native theme), then we're alright and we should compute an
    236    // appropriate backplate color.
    237    const auto* style = frame->Style();
    238    if (style->StyleBackground()->IsTransparent(style)) {
    239      continue;
    240    }
    241    bool drawImage = false, drawColor = false;
    242    nscolor backgroundColor = nsCSSRendering::DetermineBackgroundColor(
    243        pc, style, frame, drawImage, drawColor);
    244    if (!drawColor && !drawImage) {
    245      continue;
    246    }
    247    if (NS_GET_A(backgroundColor) == 0) {
    248      // Even if there's a background image, if there's no background color we
    249      // keep going up the frame tree, see bug 1723938.
    250      continue;
    251    }
    252    if (NS_GET_A(currentBackgroundColor) == 0) {
    253      // Try to avoid somewhat expensive math in the common case.
    254      currentBackgroundColor = backgroundColor;
    255    } else {
    256      currentBackgroundColor =
    257          NS_ComposeColors(backgroundColor, currentBackgroundColor);
    258    }
    259    if (NS_GET_A(currentBackgroundColor) == 0xff) {
    260      // If fully opaque, we're done, otherwise keep going up blending with our
    261      // background.
    262      return currentBackgroundColor;
    263    }
    264  }
    265  nscolor backgroundColor = aFrame->PresContext()->DefaultBackgroundColor();
    266  if (NS_GET_A(currentBackgroundColor) == 0) {
    267    return backgroundColor;
    268  }
    269  return NS_ComposeColors(backgroundColor, currentBackgroundColor);
    270 }
    271 
    272 static nsRect GetNormalMarginRect(const nsIFrame& aFrame,
    273                                  bool aIncludePositiveMargins = true) {
    274  nsMargin m = aFrame.GetUsedMargin().ApplySkipSides(aFrame.GetSkipSides());
    275  if (!aIncludePositiveMargins) {
    276    m.EnsureAtMost(nsMargin());
    277  }
    278  auto rect = aFrame.GetRectRelativeToSelf();
    279  rect.Inflate(m);
    280  return rect + aFrame.GetNormalPosition();
    281 }
    282 
    283 #ifdef DEBUG
    284 #  include "nsBlockDebugFlags.h"
    285 
    286 bool nsBlockFrame::gLamePaintMetrics;
    287 bool nsBlockFrame::gLameReflowMetrics;
    288 bool nsBlockFrame::gNoisy;
    289 bool nsBlockFrame::gNoisyDamageRepair;
    290 bool nsBlockFrame::gNoisyIntrinsic;
    291 bool nsBlockFrame::gNoisyReflow;
    292 bool nsBlockFrame::gReallyNoisyReflow;
    293 bool nsBlockFrame::gNoisyFloatManager;
    294 bool nsBlockFrame::gVerifyLines;
    295 bool nsBlockFrame::gDisableResizeOpt;
    296 
    297 int32_t nsBlockFrame::gNoiseIndent;
    298 
    299 struct BlockDebugFlags {
    300  const char* name;
    301  bool* on;
    302 };
    303 
    304 static const BlockDebugFlags gFlags[] = {
    305    {"reflow", &nsBlockFrame::gNoisyReflow},
    306    {"really-noisy-reflow", &nsBlockFrame::gReallyNoisyReflow},
    307    {"intrinsic", &nsBlockFrame::gNoisyIntrinsic},
    308    {"float-manager", &nsBlockFrame::gNoisyFloatManager},
    309    {"verify-lines", &nsBlockFrame::gVerifyLines},
    310    {"damage-repair", &nsBlockFrame::gNoisyDamageRepair},
    311    {"lame-paint-metrics", &nsBlockFrame::gLamePaintMetrics},
    312    {"lame-reflow-metrics", &nsBlockFrame::gLameReflowMetrics},
    313    {"disable-resize-opt", &nsBlockFrame::gDisableResizeOpt},
    314 };
    315 #  define NUM_DEBUG_FLAGS (sizeof(gFlags) / sizeof(gFlags[0]))
    316 
    317 static void ShowDebugFlags() {
    318  printf("Here are the available GECKO_BLOCK_DEBUG_FLAGS:\n");
    319  const BlockDebugFlags* bdf = gFlags;
    320  const BlockDebugFlags* end = gFlags + NUM_DEBUG_FLAGS;
    321  for (; bdf < end; bdf++) {
    322    printf("  %s\n", bdf->name);
    323  }
    324  printf("Note: GECKO_BLOCK_DEBUG_FLAGS is a comma separated list of flag\n");
    325  printf("names (no whitespace)\n");
    326 }
    327 
    328 void nsBlockFrame::InitDebugFlags() {
    329  static bool firstTime = true;
    330  if (firstTime) {
    331    firstTime = false;
    332    char* flags = PR_GetEnv("GECKO_BLOCK_DEBUG_FLAGS");
    333    if (flags) {
    334      bool error = false;
    335      for (;;) {
    336        char* cm = strchr(flags, ',');
    337        if (cm) {
    338          *cm = '\0';
    339        }
    340 
    341        bool found = false;
    342        const BlockDebugFlags* bdf = gFlags;
    343        const BlockDebugFlags* end = gFlags + NUM_DEBUG_FLAGS;
    344        for (; bdf < end; bdf++) {
    345          if (nsCRT::strcasecmp(bdf->name, flags) == 0) {
    346            *(bdf->on) = true;
    347            printf("nsBlockFrame: setting %s debug flag on\n", bdf->name);
    348            gNoisy = true;
    349            found = true;
    350            break;
    351          }
    352        }
    353        if (!found) {
    354          error = true;
    355        }
    356 
    357        if (!cm) {
    358          break;
    359        }
    360        *cm = ',';
    361        flags = cm + 1;
    362      }
    363      if (error) {
    364        ShowDebugFlags();
    365      }
    366    }
    367  }
    368 }
    369 
    370 MOZ_DEFINE_ENUM_TOSTRING_FUNC(LineReflowStatus,
    371                              (OK, Stop, RedoNoPull, RedoMoreFloats,
    372                               RedoNextBand, Truncated));
    373 #endif
    374 
    375 #ifdef REFLOW_STATUS_COVERAGE
    376 static void RecordReflowStatus(bool aChildIsBlock,
    377                               const nsReflowStatus& aFrameReflowStatus) {
    378  static uint32_t record[2];
    379 
    380  // 0: child-is-block
    381  // 1: child-is-inline
    382  int index = 0;
    383  if (!aChildIsBlock) {
    384    index |= 1;
    385  }
    386 
    387  // Compute new status
    388  uint32_t newS = record[index];
    389  if (aFrameReflowStatus.IsInlineBreak()) {
    390    if (aFrameReflowStatus.IsInlineBreakBefore()) {
    391      newS |= 1;
    392    } else if (aFrameReflowStatus.IsIncomplete()) {
    393      newS |= 2;
    394    } else {
    395      newS |= 4;
    396    }
    397  } else if (aFrameReflowStatus.IsIncomplete()) {
    398    newS |= 8;
    399  } else {
    400    newS |= 16;
    401  }
    402 
    403  // Log updates to the status that yield different values
    404  if (record[index] != newS) {
    405    record[index] = newS;
    406    printf("record(%d): %02x %02x\n", index, record[0], record[1]);
    407  }
    408 }
    409 #endif
    410 
    411 NS_DECLARE_FRAME_PROPERTY_WITH_DTOR_NEVER_CALLED(OverflowLinesProperty,
    412                                                 nsBlockFrame::FrameLines)
    413 NS_DECLARE_FRAME_PROPERTY_FRAMELIST(OverflowOutOfFlowsProperty)
    414 NS_DECLARE_FRAME_PROPERTY_FRAMELIST(FloatsProperty)
    415 NS_DECLARE_FRAME_PROPERTY_FRAMELIST(PushedFloatsProperty)
    416 NS_DECLARE_FRAME_PROPERTY_FRAMELIST(OutsideMarkerProperty)
    417 NS_DECLARE_FRAME_PROPERTY_WITHOUT_DTOR(InsideMarkerProperty, nsIFrame)
    418 
    419 //----------------------------------------------------------------------
    420 
    421 nsBlockFrame* NS_NewBlockFrame(PresShell* aPresShell, ComputedStyle* aStyle) {
    422  return new (aPresShell) nsBlockFrame(aStyle, aPresShell->GetPresContext());
    423 }
    424 
    425 NS_IMPL_FRAMEARENA_HELPERS(nsBlockFrame)
    426 
    427 nsBlockFrame::~nsBlockFrame() = default;
    428 
    429 void nsBlockFrame::AddSizeOfExcludingThisForTree(
    430    nsWindowSizes& aWindowSizes) const {
    431  nsContainerFrame::AddSizeOfExcludingThisForTree(aWindowSizes);
    432 
    433  // Add the size of any nsLineBox::mFrames hashtables we might have:
    434  for (const auto& line : Lines()) {
    435    line.AddSizeOfExcludingThis(aWindowSizes);
    436  }
    437  const FrameLines* overflowLines = GetOverflowLines();
    438  if (overflowLines) {
    439    ConstLineIterator line = overflowLines->mLines.begin(),
    440                      line_end = overflowLines->mLines.end();
    441    for (; line != line_end; ++line) {
    442      line->AddSizeOfExcludingThis(aWindowSizes);
    443    }
    444  }
    445 }
    446 
    447 void nsBlockFrame::Destroy(DestroyContext& aContext) {
    448  ClearLineCursors();
    449  DestroyAbsoluteFrames(aContext);
    450  nsPresContext* presContext = PresContext();
    451  mozilla::PresShell* presShell = presContext->PresShell();
    452  if (HasFloats()) {
    453    SafelyDestroyFrameListProp(aContext, presShell, FloatsProperty());
    454    RemoveStateBits(NS_BLOCK_HAS_FLOATS);
    455  }
    456  nsLineBox::DeleteLineList(presContext, mLines, &mFrames, aContext);
    457 
    458  if (HasPushedFloats()) {
    459    SafelyDestroyFrameListProp(aContext, presShell, PushedFloatsProperty());
    460    RemoveStateBits(NS_BLOCK_HAS_PUSHED_FLOATS);
    461  }
    462 
    463  // destroy overflow lines now
    464  FrameLines* overflowLines = RemoveOverflowLines();
    465  if (overflowLines) {
    466    nsLineBox::DeleteLineList(presContext, overflowLines->mLines,
    467                              &overflowLines->mFrames, aContext);
    468    delete overflowLines;
    469  }
    470 
    471  if (HasAnyStateBits(NS_BLOCK_HAS_OVERFLOW_OUT_OF_FLOWS)) {
    472    SafelyDestroyFrameListProp(aContext, presShell,
    473                               OverflowOutOfFlowsProperty());
    474    RemoveStateBits(NS_BLOCK_HAS_OVERFLOW_OUT_OF_FLOWS);
    475  }
    476 
    477  if (HasMarker()) {
    478    SafelyDestroyFrameListProp(aContext, presShell, OutsideMarkerProperty());
    479    RemoveStateBits(NS_BLOCK_HAS_MARKER);
    480  }
    481 
    482  nsContainerFrame::Destroy(aContext);
    483 }
    484 
    485 /* virtual */
    486 nsILineIterator* nsBlockFrame::GetLineIterator() {
    487  nsLineIterator* iter = GetProperty(LineIteratorProperty());
    488  if (!iter) {
    489    const nsStyleVisibility* visibility = StyleVisibility();
    490    iter = new nsLineIterator(mLines,
    491                              visibility->mDirection == StyleDirection::Rtl);
    492    SetProperty(LineIteratorProperty(), iter);
    493  }
    494  return iter;
    495 }
    496 
    497 NS_QUERYFRAME_HEAD(nsBlockFrame)
    498  NS_QUERYFRAME_ENTRY(nsBlockFrame)
    499 NS_QUERYFRAME_TAIL_INHERITING(nsContainerFrame)
    500 
    501 #ifdef DEBUG_FRAME_DUMP
    502 void nsBlockFrame::List(FILE* out, const char* aPrefix,
    503                        ListFlags aFlags) const {
    504  nsCString str;
    505  ListGeneric(str, aPrefix, aFlags);
    506 
    507  fprintf_stderr(out, "%s <\n", str.get());
    508 
    509  nsCString pfx(aPrefix);
    510  pfx += "  ";
    511 
    512  // Output the lines
    513  if (!mLines.empty()) {
    514    ConstLineIterator line = LinesBegin(), line_end = LinesEnd();
    515    for (; line != line_end; ++line) {
    516      line->List(out, pfx.get(), aFlags);
    517    }
    518  }
    519 
    520  // Output the overflow lines.
    521  const FrameLines* overflowLines = GetOverflowLines();
    522  if (overflowLines && !overflowLines->mLines.empty()) {
    523    fprintf_stderr(out, "%sOverflow-lines %p/%p <\n", pfx.get(), overflowLines,
    524                   &overflowLines->mFrames);
    525    nsCString nestedPfx(pfx);
    526    nestedPfx += "  ";
    527    ConstLineIterator line = overflowLines->mLines.begin(),
    528                      line_end = overflowLines->mLines.end();
    529    for (; line != line_end; ++line) {
    530      line->List(out, nestedPfx.get(), aFlags);
    531    }
    532    fprintf_stderr(out, "%s>\n", pfx.get());
    533  }
    534 
    535  // skip the principal list - we printed the lines above
    536  // skip the overflow list - we printed the overflow lines above
    537  ChildListIDs skip = {FrameChildListID::Principal, FrameChildListID::Overflow};
    538  ListChildLists(out, pfx.get(), aFlags, skip);
    539 
    540  fprintf_stderr(out, "%s>\n", aPrefix);
    541 }
    542 
    543 nsresult nsBlockFrame::GetFrameName(nsAString& aResult) const {
    544  return MakeFrameName(u"Block"_ns, aResult);
    545 }
    546 #endif
    547 
    548 void nsBlockFrame::InvalidateFrame(uint32_t aDisplayItemKey,
    549                                   bool aRebuildDisplayItems) {
    550  if (IsInSVGTextSubtree()) {
    551    NS_ASSERTION(GetParent()->IsSVGTextFrame(),
    552                 "unexpected block frame in SVG text");
    553    GetParent()->InvalidateFrame();
    554    return;
    555  }
    556  nsContainerFrame::InvalidateFrame(aDisplayItemKey, aRebuildDisplayItems);
    557 }
    558 
    559 void nsBlockFrame::InvalidateFrameWithRect(const nsRect& aRect,
    560                                           uint32_t aDisplayItemKey,
    561                                           bool aRebuildDisplayItems) {
    562  if (IsInSVGTextSubtree()) {
    563    NS_ASSERTION(GetParent()->IsSVGTextFrame(),
    564                 "unexpected block frame in SVG text");
    565    GetParent()->InvalidateFrame();
    566    return;
    567  }
    568  nsContainerFrame::InvalidateFrameWithRect(aRect, aDisplayItemKey,
    569                                            aRebuildDisplayItems);
    570 }
    571 
    572 nscoord nsBlockFrame::SynthesizeFallbackBaseline(
    573    WritingMode aWM, BaselineSharingGroup aBaselineGroup) const {
    574  if (IsButtonLike() && StyleDisplay()->IsInlineOutsideStyle()) {
    575    return Baseline::SynthesizeBOffsetFromContentBox(this, aWM, aBaselineGroup);
    576  }
    577  return Baseline::SynthesizeBOffsetFromMarginBox(this, aWM, aBaselineGroup);
    578 }
    579 
    580 template <typename LineIteratorType>
    581 Maybe<nscoord> nsBlockFrame::GetBaselineBOffset(
    582    LineIteratorType aStart, LineIteratorType aEnd, WritingMode aWM,
    583    BaselineSharingGroup aBaselineGroup,
    584    BaselineExportContext aExportContext) const {
    585  MOZ_ASSERT((std::is_same_v<LineIteratorType, ConstLineIterator> &&
    586              aBaselineGroup == BaselineSharingGroup::First) ||
    587                 (std::is_same_v<LineIteratorType, ConstReverseLineIterator> &&
    588                  aBaselineGroup == BaselineSharingGroup::Last),
    589             "Iterator direction must match baseline sharing group.");
    590  for (auto line = aStart; line != aEnd; ++line) {
    591    if (!line->IsBlock()) {
    592      // XXX Is this the right test?  We have some bogus empty lines
    593      // floating around, but IsEmpty is perhaps too weak.
    594      if (line->BSize() != 0 || !line->IsEmpty()) {
    595        const auto ascent = line->BStart() + line->GetLogicalAscent();
    596        if (aBaselineGroup == BaselineSharingGroup::Last) {
    597          return Some(BSize(aWM) - ascent);
    598        }
    599        return Some(ascent);
    600      }
    601      continue;
    602    }
    603    nsIFrame* kid = line->mFirstChild;
    604    if (aWM.IsOrthogonalTo(kid->GetWritingMode())) {
    605      continue;
    606    }
    607    if (aExportContext == BaselineExportContext::LineLayout &&
    608        kid->IsTableWrapperFrame()) {
    609      // `<table>` in inline-block context does not export any baseline.
    610      continue;
    611    }
    612    const auto kidBaselineGroup =
    613        aExportContext == BaselineExportContext::LineLayout
    614            ? kid->GetDefaultBaselineSharingGroup()
    615            : aBaselineGroup;
    616    const auto kidBaseline =
    617        kid->GetNaturalBaselineBOffset(aWM, kidBaselineGroup, aExportContext);
    618    if (!kidBaseline) {
    619      continue;
    620    }
    621    auto result = *kidBaseline;
    622    if (kidBaselineGroup == BaselineSharingGroup::Last) {
    623      result = kid->BSize(aWM) - result;
    624    }
    625    // Ignore relative positioning for baseline calculations.
    626    const nsSize& sz = line->mContainerSize;
    627    result += kid->GetLogicalNormalPosition(aWM, sz).B(aWM);
    628    if (aBaselineGroup == BaselineSharingGroup::Last) {
    629      return Some(BSize(aWM) - result);
    630    }
    631    return Some(result);
    632  }
    633  return Nothing{};
    634 }
    635 
    636 Maybe<nscoord> nsBlockFrame::GetNaturalBaselineBOffset(
    637    WritingMode aWM, BaselineSharingGroup aBaselineGroup,
    638    BaselineExportContext aExportContext) const {
    639  if (StyleDisplay()->IsContainLayout()) {
    640    return Nothing{};
    641  }
    642 
    643  Maybe<nscoord> offset =
    644      aBaselineGroup == BaselineSharingGroup::First
    645          ? GetBaselineBOffset(LinesBegin(), LinesEnd(), aWM, aBaselineGroup,
    646                               aExportContext)
    647          : GetBaselineBOffset(LinesRBegin(), LinesREnd(), aWM, aBaselineGroup,
    648                               aExportContext);
    649  if (!offset && IsButtonLike()) {
    650    for (const auto& line : Reversed(Lines())) {
    651      if (line.IsEmpty()) {
    652        continue;
    653      }
    654      // Buttons use the end of the content as a baseline if we haven't found
    655      // one yet.
    656      nscoord bEnd = line.BEnd();
    657      offset.emplace(aBaselineGroup == BaselineSharingGroup::Last
    658                         ? BSize(aWM) - bEnd
    659                         : bEnd);
    660      break;
    661    }
    662  }
    663  return offset;
    664 }
    665 
    666 nscoord nsBlockFrame::GetCaretBaseline() const {
    667  const auto wm = GetWritingMode();
    668  if (!mLines.empty()) {
    669    ConstLineIterator line = LinesBegin();
    670    if (!line->IsEmpty()) {
    671      if (line->IsBlock()) {
    672        return GetLogicalUsedBorderAndPadding(wm).BStart(wm) +
    673               line->mFirstChild->GetCaretBaseline();
    674      }
    675      return line->BStart() + line->GetLogicalAscent();
    676    }
    677  }
    678  return GetFontMetricsDerivedCaretBaseline(ContentBSize(wm));
    679 }
    680 
    681 /////////////////////////////////////////////////////////////////////////////
    682 // Child frame enumeration
    683 
    684 const nsFrameList& nsBlockFrame::GetChildList(ChildListID aListID) const {
    685  switch (aListID) {
    686    case FrameChildListID::Principal:
    687      return mFrames;
    688    case FrameChildListID::Overflow: {
    689      FrameLines* overflowLines = GetOverflowLines();
    690      return overflowLines ? overflowLines->mFrames : nsFrameList::EmptyList();
    691    }
    692    case FrameChildListID::OverflowOutOfFlow: {
    693      const nsFrameList* list = GetOverflowOutOfFlows();
    694      return list ? *list : nsFrameList::EmptyList();
    695    }
    696    case FrameChildListID::Float: {
    697      const nsFrameList* list = GetFloats();
    698      return list ? *list : nsFrameList::EmptyList();
    699    }
    700    case FrameChildListID::PushedFloats: {
    701      const nsFrameList* list = GetPushedFloats();
    702      return list ? *list : nsFrameList::EmptyList();
    703    }
    704    case FrameChildListID::Marker: {
    705      const nsFrameList* list = GetOutsideMarkerList();
    706      return list ? *list : nsFrameList::EmptyList();
    707    }
    708    default:
    709      return nsContainerFrame::GetChildList(aListID);
    710  }
    711 }
    712 
    713 void nsBlockFrame::GetChildLists(nsTArray<ChildList>* aLists) const {
    714  nsContainerFrame::GetChildLists(aLists);
    715  FrameLines* overflowLines = GetOverflowLines();
    716  if (overflowLines) {
    717    overflowLines->mFrames.AppendIfNonempty(aLists, FrameChildListID::Overflow);
    718  }
    719  if (const nsFrameList* list = GetOverflowOutOfFlows()) {
    720    list->AppendIfNonempty(aLists, FrameChildListID::OverflowOutOfFlow);
    721  }
    722  if (const nsFrameList* list = GetOutsideMarkerList()) {
    723    list->AppendIfNonempty(aLists, FrameChildListID::Marker);
    724  }
    725  if (const nsFrameList* list = GetFloats()) {
    726    list->AppendIfNonempty(aLists, FrameChildListID::Float);
    727  }
    728  if (const nsFrameList* list = GetPushedFloats()) {
    729    list->AppendIfNonempty(aLists, FrameChildListID::PushedFloats);
    730  }
    731 }
    732 
    733 /* virtual */
    734 bool nsBlockFrame::IsFloatContainingBlock() const { return true; }
    735 
    736 /**
    737 * Remove the first line from aFromLines and adjust the associated frame list
    738 * aFromFrames accordingly.  The removed line is assigned to *aOutLine and
    739 * a frame list with its frames is assigned to *aOutFrames, i.e. the frames
    740 * that were extracted from the head of aFromFrames.
    741 * aFromLines must contain at least one line, the line may be empty.
    742 * @return true if aFromLines becomes empty
    743 */
    744 static bool RemoveFirstLine(nsLineList& aFromLines, nsFrameList& aFromFrames,
    745                            nsLineBox** aOutLine, nsFrameList* aOutFrames) {
    746  LineListIterator removedLine = aFromLines.begin();
    747  *aOutLine = removedLine;
    748  LineListIterator next = aFromLines.erase(removedLine);
    749  bool isLastLine = next == aFromLines.end();
    750  nsIFrame* firstFrameInNextLine = isLastLine ? nullptr : next->mFirstChild;
    751  *aOutFrames = aFromFrames.TakeFramesBefore(firstFrameInNextLine);
    752  return isLastLine;
    753 }
    754 
    755 //////////////////////////////////////////////////////////////////////
    756 // Reflow methods
    757 
    758 /* virtual */
    759 void nsBlockFrame::MarkIntrinsicISizesDirty() {
    760  nsBlockFrame* dirtyBlock = static_cast<nsBlockFrame*>(FirstContinuation());
    761  dirtyBlock->mCachedIntrinsics.Clear();
    762  if (!HasAnyStateBits(NS_BLOCK_NEEDS_BIDI_RESOLUTION)) {
    763    for (nsIFrame* frame = dirtyBlock; frame;
    764         frame = frame->GetNextContinuation()) {
    765      frame->AddStateBits(NS_BLOCK_NEEDS_BIDI_RESOLUTION);
    766    }
    767  }
    768 
    769  nsContainerFrame::MarkIntrinsicISizesDirty();
    770 }
    771 
    772 void nsBlockFrame::CheckIntrinsicCacheAgainstShrinkWrapState() {
    773  nsPresContext* presContext = PresContext();
    774  if (!nsLayoutUtils::FontSizeInflationEnabled(presContext)) {
    775    return;
    776  }
    777  bool inflationEnabled = !presContext->mInflationDisabledForShrinkWrap;
    778  if (inflationEnabled != HasAnyStateBits(NS_BLOCK_INTRINSICS_INFLATED)) {
    779    mCachedIntrinsics.Clear();
    780    AddOrRemoveStateBits(NS_BLOCK_INTRINSICS_INFLATED, inflationEnabled);
    781  }
    782 }
    783 
    784 // Whether this line is indented by the text-indent amount.
    785 bool nsBlockFrame::TextIndentAppliesTo(const LineIterator& aLine) const {
    786  const auto& textIndent = StyleText()->mTextIndent;
    787 
    788  bool isFirstLineOrAfterHardBreak = [&] {
    789    if (aLine != LinesBegin()) {
    790      // If not the first line of the block, but 'each-line' is in effect,
    791      // check if the previous line was not wrapped.
    792      return textIndent.each_line && !aLine.prev()->IsLineWrapped();
    793    }
    794    if (nsBlockFrame* prevBlock = do_QueryFrame(GetPrevInFlow())) {
    795      // There's a prev-in-flow, so this only counts as a first-line if
    796      // 'each-line' and the prev-in-flow's last line was not wrapped.
    797      return textIndent.each_line &&
    798             (prevBlock->Lines().empty() ||
    799              !prevBlock->LinesEnd().prev()->IsLineWrapped());
    800    }
    801    return true;
    802  }();
    803 
    804  // The 'hanging' option inverts which lines are/aren't indented.
    805  return isFirstLineOrAfterHardBreak != textIndent.hanging;
    806 }
    807 
    808 nscoord nsBlockFrame::IntrinsicISize(const IntrinsicSizeInput& aInput,
    809                                     IntrinsicISizeType aType) {
    810  nsIFrame* firstCont = FirstContinuation();
    811  if (firstCont != this) {
    812    return firstCont->IntrinsicISize(aInput, aType);
    813  }
    814 
    815  CheckIntrinsicCacheAgainstShrinkWrapState();
    816 
    817  return mCachedIntrinsics.GetOrSet(*this, aType, aInput, [&] {
    818    return aType == IntrinsicISizeType::MinISize ? MinISize(aInput)
    819                                                 : PrefISize(aInput);
    820  });
    821 }
    822 
    823 /* virtual */
    824 nscoord nsBlockFrame::MinISize(const IntrinsicSizeInput& aInput) {
    825  if (Maybe<nscoord> containISize = ContainIntrinsicISize()) {
    826    return *containISize;
    827  }
    828 
    829 #ifdef DEBUG
    830  if (gNoisyIntrinsic) {
    831    IndentBy(stdout, gNoiseIndent);
    832    ListTag(stdout);
    833    printf(": MinISize\n");
    834  }
    835  AutoNoisyIndenter indenter(gNoisyIntrinsic);
    836 #endif
    837 
    838  for (nsBlockFrame* curFrame = this; curFrame;
    839       curFrame = static_cast<nsBlockFrame*>(curFrame->GetNextContinuation())) {
    840    curFrame->LazyMarkLinesDirty();
    841  }
    842 
    843  if (HasAnyStateBits(NS_BLOCK_NEEDS_BIDI_RESOLUTION) &&
    844      PresContext()->BidiEnabled()) {
    845    ResolveBidi();
    846  }
    847 
    848  const bool whiteSpaceCanWrap = StyleText()->WhiteSpaceCanWrapStyle();
    849  InlineMinISizeData data;
    850  for (nsBlockFrame* curFrame = this; curFrame;
    851       curFrame = static_cast<nsBlockFrame*>(curFrame->GetNextContinuation())) {
    852    for (LineIterator line = curFrame->LinesBegin(),
    853                      line_end = curFrame->LinesEnd();
    854         line != line_end; ++line) {
    855 #ifdef DEBUG
    856      if (gNoisyIntrinsic) {
    857        IndentBy(stdout, gNoiseIndent);
    858        printf("line (%s%s)\n", line->IsBlock() ? "block" : "inline",
    859               line->IsEmpty() ? ", empty" : "");
    860      }
    861      AutoNoisyIndenter lineindent(gNoisyIntrinsic);
    862 #endif
    863      if (line->IsBlock()) {
    864        data.ForceBreak();
    865        nsIFrame* kid = line->mFirstChild;
    866        const IntrinsicSizeInput kidInput(aInput, kid->GetWritingMode(),
    867                                          GetWritingMode());
    868        data.mCurrentLine = nsLayoutUtils::IntrinsicForContainer(
    869            kidInput.mContext, kid, IntrinsicISizeType::MinISize,
    870            kidInput.mPercentageBasisForChildren);
    871        data.ForceBreak();
    872      } else {
    873        if (!curFrame->GetPrevContinuation() && TextIndentAppliesTo(line)) {
    874          data.mCurrentLine += StyleText()->mTextIndent.length.Resolve(0);
    875        }
    876        data.mLine = &line;
    877        data.SetLineContainer(curFrame);
    878        nsIFrame* kid = line->mFirstChild;
    879        for (int32_t i = 0, i_end = line->GetChildCount(); i != i_end;
    880             ++i, kid = kid->GetNextSibling()) {
    881          const IntrinsicSizeInput kidInput(aInput, kid->GetWritingMode(),
    882                                            GetWritingMode());
    883          kid->AddInlineMinISize(kidInput, &data);
    884          if (whiteSpaceCanWrap && data.mTrailingWhitespace) {
    885            data.OptionallyBreak();
    886          }
    887        }
    888      }
    889 #ifdef DEBUG
    890      if (gNoisyIntrinsic) {
    891        IndentBy(stdout, gNoiseIndent);
    892        printf("min: [prevLines=%d currentLine=%d]\n", data.mPrevLines,
    893               data.mCurrentLine);
    894      }
    895 #endif
    896    }
    897  }
    898  data.ForceBreak();
    899  return data.mPrevLines;
    900 }
    901 
    902 /* virtual */
    903 nscoord nsBlockFrame::PrefISize(const IntrinsicSizeInput& aInput) {
    904  if (Maybe<nscoord> containISize = ContainIntrinsicISize()) {
    905    return *containISize;
    906  }
    907 
    908 #ifdef DEBUG
    909  if (gNoisyIntrinsic) {
    910    IndentBy(stdout, gNoiseIndent);
    911    ListTag(stdout);
    912    printf(": PrefISize\n");
    913  }
    914  AutoNoisyIndenter indenter(gNoisyIntrinsic);
    915 #endif
    916 
    917  for (nsBlockFrame* curFrame = this; curFrame;
    918       curFrame = static_cast<nsBlockFrame*>(curFrame->GetNextContinuation())) {
    919    curFrame->LazyMarkLinesDirty();
    920  }
    921 
    922  if (HasAnyStateBits(NS_BLOCK_NEEDS_BIDI_RESOLUTION) &&
    923      PresContext()->BidiEnabled()) {
    924    ResolveBidi();
    925  }
    926  InlinePrefISizeData data;
    927  for (nsBlockFrame* curFrame = this; curFrame;
    928       curFrame = static_cast<nsBlockFrame*>(curFrame->GetNextContinuation())) {
    929    for (LineIterator line = curFrame->LinesBegin(),
    930                      line_end = curFrame->LinesEnd();
    931         line != line_end; ++line) {
    932 #ifdef DEBUG
    933      if (gNoisyIntrinsic) {
    934        IndentBy(stdout, gNoiseIndent);
    935        printf("line (%s%s)\n", line->IsBlock() ? "block" : "inline",
    936               line->IsEmpty() ? ", empty" : "");
    937      }
    938      AutoNoisyIndenter lineindent(gNoisyIntrinsic);
    939 #endif
    940      if (line->IsBlock()) {
    941        nsIFrame* kid = line->mFirstChild;
    942        UsedClear clearType;
    943        if (!data.mLineIsEmpty || BlockCanIntersectFloats(kid)) {
    944          clearType = UsedClear::Both;
    945        } else {
    946          clearType = kid->StyleDisplay()->UsedClear(GetWritingMode());
    947        }
    948        data.ForceBreak(clearType);
    949        const IntrinsicSizeInput kidInput(aInput, kid->GetWritingMode(),
    950                                          GetWritingMode());
    951        data.mCurrentLine = nsLayoutUtils::IntrinsicForContainer(
    952            kidInput.mContext, kid, IntrinsicISizeType::PrefISize,
    953            kidInput.mPercentageBasisForChildren);
    954        data.ForceBreak();
    955      } else {
    956        if (!curFrame->GetPrevContinuation() && TextIndentAppliesTo(line)) {
    957          nscoord indent = StyleText()->mTextIndent.length.Resolve(0);
    958          data.mCurrentLine += indent;
    959          // XXXmats should the test below be indent > 0?
    960          if (indent != nscoord(0)) {
    961            data.mLineIsEmpty = false;
    962          }
    963        }
    964        data.mLine = &line;
    965        data.SetLineContainer(curFrame);
    966        nsIFrame* kid = line->mFirstChild;
    967        for (int32_t i = 0, i_end = line->GetChildCount(); i != i_end;
    968             ++i, kid = kid->GetNextSibling()) {
    969          const IntrinsicSizeInput kidInput(aInput, kid->GetWritingMode(),
    970                                            GetWritingMode());
    971          kid->AddInlinePrefISize(kidInput, &data);
    972        }
    973      }
    974 #ifdef DEBUG
    975      if (gNoisyIntrinsic) {
    976        IndentBy(stdout, gNoiseIndent);
    977        printf("pref: [prevLines=%d currentLine=%d]\n", data.mPrevLines,
    978               data.mCurrentLine);
    979      }
    980 #endif
    981    }
    982  }
    983  data.ForceBreak();
    984  return data.mPrevLines;
    985 }
    986 
    987 nsRect nsBlockFrame::ComputeTightBounds(DrawTarget* aDrawTarget) const {
    988  // be conservative
    989  if (Style()->HasTextDecorationLines()) {
    990    return InkOverflowRect();
    991  }
    992  return ComputeSimpleTightBounds(aDrawTarget);
    993 }
    994 
    995 /* virtual */
    996 nsresult nsBlockFrame::GetPrefWidthTightBounds(gfxContext* aRenderingContext,
    997                                               nscoord* aX, nscoord* aXMost) {
    998  nsIFrame* firstInFlow = FirstContinuation();
    999  if (firstInFlow != this) {
   1000    return firstInFlow->GetPrefWidthTightBounds(aRenderingContext, aX, aXMost);
   1001  }
   1002 
   1003  *aX = 0;
   1004  *aXMost = 0;
   1005 
   1006  nsresult rv;
   1007  InlinePrefISizeData data;
   1008  for (nsBlockFrame* curFrame = this; curFrame;
   1009       curFrame = static_cast<nsBlockFrame*>(curFrame->GetNextContinuation())) {
   1010    for (LineIterator line = curFrame->LinesBegin(),
   1011                      line_end = curFrame->LinesEnd();
   1012         line != line_end; ++line) {
   1013      nscoord childX, childXMost;
   1014      if (line->IsBlock()) {
   1015        data.ForceBreak();
   1016        rv = line->mFirstChild->GetPrefWidthTightBounds(aRenderingContext,
   1017                                                        &childX, &childXMost);
   1018        NS_ENSURE_SUCCESS(rv, rv);
   1019        *aX = std::min(*aX, childX);
   1020        *aXMost = std::max(*aXMost, childXMost);
   1021      } else {
   1022        if (!curFrame->GetPrevContinuation() && TextIndentAppliesTo(line)) {
   1023          data.mCurrentLine += StyleText()->mTextIndent.length.Resolve(0);
   1024        }
   1025        data.mLine = &line;
   1026        data.SetLineContainer(curFrame);
   1027        nsIFrame* kid = line->mFirstChild;
   1028        // Per comment in nsIFrame::GetPrefWidthTightBounds(), the function is
   1029        // only implemented for nsBlockFrame and nsTextFrame and is used to
   1030        // determine the intrinsic inline sizes of MathML token elements. These
   1031        // elements shouldn't have percentage block sizes that require a
   1032        // percentage basis for resolution.
   1033        const IntrinsicSizeInput kidInput(aRenderingContext, Nothing(),
   1034                                          Nothing());
   1035        for (int32_t i = 0, i_end = line->GetChildCount(); i != i_end;
   1036             ++i, kid = kid->GetNextSibling()) {
   1037          rv = kid->GetPrefWidthTightBounds(aRenderingContext, &childX,
   1038                                            &childXMost);
   1039          NS_ENSURE_SUCCESS(rv, rv);
   1040          *aX = std::min(*aX, data.mCurrentLine + childX);
   1041          *aXMost = std::max(*aXMost, data.mCurrentLine + childXMost);
   1042          kid->AddInlinePrefISize(kidInput, &data);
   1043        }
   1044      }
   1045    }
   1046  }
   1047  data.ForceBreak();
   1048 
   1049  return NS_OK;
   1050 }
   1051 
   1052 /**
   1053 * Return whether aNewAvailableSpace is smaller *on either side*
   1054 * (inline-start or inline-end) than aOldAvailableSpace, so that we know
   1055 * if we need to redo layout on an line, replaced block, or block
   1056 * formatting context, because its height (which we used to compute
   1057 * aNewAvailableSpace) caused it to intersect additional floats.
   1058 */
   1059 static bool AvailableSpaceShrunk(WritingMode aWM,
   1060                                 const LogicalRect& aOldAvailableSpace,
   1061                                 const LogicalRect& aNewAvailableSpace,
   1062                                 bool aCanGrow /* debug-only */) {
   1063  if (aNewAvailableSpace.ISize(aWM) == 0) {
   1064    // Positions are not significant if the inline size is zero.
   1065    return aOldAvailableSpace.ISize(aWM) != 0;
   1066  }
   1067  if (aCanGrow) {
   1068    NS_ASSERTION(
   1069        aNewAvailableSpace.IStart(aWM) <= aOldAvailableSpace.IStart(aWM) ||
   1070            aNewAvailableSpace.IEnd(aWM) <= aOldAvailableSpace.IEnd(aWM),
   1071        "available space should not shrink on the start side and "
   1072        "grow on the end side");
   1073    NS_ASSERTION(
   1074        aNewAvailableSpace.IStart(aWM) >= aOldAvailableSpace.IStart(aWM) ||
   1075            aNewAvailableSpace.IEnd(aWM) >= aOldAvailableSpace.IEnd(aWM),
   1076        "available space should not grow on the start side and "
   1077        "shrink on the end side");
   1078  } else {
   1079    NS_ASSERTION(
   1080        aOldAvailableSpace.IStart(aWM) <= aNewAvailableSpace.IStart(aWM) &&
   1081            aOldAvailableSpace.IEnd(aWM) >= aNewAvailableSpace.IEnd(aWM),
   1082        "available space should never grow");
   1083  }
   1084  // Have we shrunk on either side?
   1085  return aNewAvailableSpace.IStart(aWM) > aOldAvailableSpace.IStart(aWM) ||
   1086         aNewAvailableSpace.IEnd(aWM) < aOldAvailableSpace.IEnd(aWM);
   1087 }
   1088 
   1089 /**
   1090 * Returns aFrame if it is an in-flow, non-BFC block frame, and null otherwise.
   1091 *
   1092 * This is used to determine whether to recurse into aFrame when applying
   1093 * -webkit-line-clamp.
   1094 */
   1095 static const nsBlockFrame* GetAsLineClampDescendant(const nsIFrame* aFrame) {
   1096  const nsBlockFrame* block = do_QueryFrame(aFrame);
   1097  if (!block || block->HasAnyStateBits(NS_FRAME_OUT_OF_FLOW | NS_BLOCK_BFC)) {
   1098    return nullptr;
   1099  }
   1100  return block;
   1101 }
   1102 
   1103 static nsBlockFrame* GetAsLineClampDescendant(nsIFrame* aFrame) {
   1104  return const_cast<nsBlockFrame*>(
   1105      GetAsLineClampDescendant(const_cast<const nsIFrame*>(aFrame)));
   1106 }
   1107 
   1108 static bool IsLineClampRoot(const nsBlockFrame* aFrame) {
   1109  if (!aFrame->StyleDisplay()->mWebkitLineClamp) {
   1110    return false;
   1111  }
   1112 
   1113  if (!aFrame->HasAnyStateBits(NS_BLOCK_BFC)) {
   1114    return false;
   1115  }
   1116 
   1117  if (StaticPrefs::layout_css_webkit_line_clamp_block_enabled() ||
   1118      aFrame->PresContext()->Document()->ChromeRulesEnabled()) {
   1119    return true;
   1120  }
   1121 
   1122  // For now, -webkit-box is the only thing allowed to be a line-clamp root.
   1123  // Ideally we'd just make this work everywhere, but for now we're carrying
   1124  // this forward as a limitation on the legacy -webkit-line-clamp feature,
   1125  // since relaxing this limitation might create webcompat trouble.
   1126  auto origDisplay = [&] {
   1127    if (aFrame->Style()->GetPseudoType() == PseudoStyleType::scrolledContent) {
   1128      // If we're the anonymous block inside the scroll frame, we need to look
   1129      // at the original display of our parent frame.
   1130      MOZ_ASSERT(aFrame->GetParent());
   1131      const auto& parentDisp = *aFrame->GetParent()->StyleDisplay();
   1132      MOZ_ASSERT(parentDisp.mWebkitLineClamp ==
   1133                     aFrame->StyleDisplay()->mWebkitLineClamp,
   1134                 ":-moz-scrolled-content should inherit -webkit-line-clamp, "
   1135                 "via rule in UA stylesheet");
   1136      return parentDisp.mOriginalDisplay;
   1137    }
   1138    return aFrame->StyleDisplay()->mOriginalDisplay;
   1139  }();
   1140  return origDisplay.Inside() == StyleDisplayInside::WebkitBox;
   1141 }
   1142 
   1143 nsBlockFrame* nsBlockFrame::GetLineClampRoot() const {
   1144  if (IsLineClampRoot(this)) {
   1145    return const_cast<nsBlockFrame*>(this);
   1146  }
   1147  const nsBlockFrame* cur = this;
   1148  while (GetAsLineClampDescendant(cur)) {
   1149    cur = do_QueryFrame(cur->GetParent());
   1150    if (!cur) {
   1151      break;
   1152    }
   1153    if (IsLineClampRoot(cur)) {
   1154      return const_cast<nsBlockFrame*>(cur);
   1155    }
   1156  }
   1157  return nullptr;
   1158 }
   1159 
   1160 bool nsBlockFrame::MaybeHasFloats() const {
   1161  if (HasFloats()) {
   1162    return true;
   1163  }
   1164  if (HasPushedFloats()) {
   1165    return true;
   1166  }
   1167  // For the OverflowOutOfFlowsProperty I think we do enforce that, but it's
   1168  // a mix of out-of-flow frames, so that's why the method name has "Maybe".
   1169  return HasAnyStateBits(NS_BLOCK_HAS_OVERFLOW_OUT_OF_FLOWS);
   1170 }
   1171 
   1172 /**
   1173 * Iterator over all descendant inline line boxes, except for those that are
   1174 * under an independent formatting context.
   1175 */
   1176 class MOZ_RAII LineClampLineIterator {
   1177 public:
   1178  LineClampLineIterator(nsBlockFrame* aFrame, const nsBlockFrame* aStopAtFrame)
   1179      : mCur(aFrame->LinesBegin()),
   1180        mEnd(aFrame->LinesEnd()),
   1181        mCurrentFrame(mCur == mEnd ? nullptr : aFrame),
   1182        mStopAtFrame(aStopAtFrame) {
   1183    if (mCur != mEnd && !mCur->IsInline()) {
   1184      Advance();
   1185    }
   1186  }
   1187 
   1188  nsLineBox* GetCurrentLine() { return mCurrentFrame ? mCur.get() : nullptr; }
   1189  nsBlockFrame* GetCurrentFrame() { return mCurrentFrame; }
   1190 
   1191  // Advances the iterator to the next line line.
   1192  //
   1193  // Next() shouldn't be called once the iterator is at the end, which can be
   1194  // checked for by GetCurrentLine() or GetCurrentFrame() returning null.
   1195  void Next() {
   1196    MOZ_ASSERT(mCur != mEnd && mCurrentFrame,
   1197               "Don't call Next() when the iterator is at the end");
   1198    ++mCur;
   1199    Advance();
   1200  }
   1201 
   1202 private:
   1203  void Advance() {
   1204    for (;;) {
   1205      if (mCur == mEnd) {
   1206        // Reached the end of the current block.  Pop the parent off the
   1207        // stack; if there isn't one, then we've reached the end.
   1208        if (mStack.IsEmpty()) {
   1209          mCurrentFrame = nullptr;
   1210          break;
   1211        }
   1212        if (mCurrentFrame == mStopAtFrame) {
   1213          mStack.Clear();
   1214          mCurrentFrame = nullptr;
   1215          break;
   1216        }
   1217 
   1218        auto entry = mStack.PopLastElement();
   1219        mCurrentFrame = entry.first;
   1220        mCur = entry.second;
   1221        mEnd = mCurrentFrame->LinesEnd();
   1222      } else if (mCur->IsBlock()) {
   1223        if (nsBlockFrame* child = GetAsLineClampDescendant(mCur->mFirstChild)) {
   1224          nsBlockFrame::LineIterator next = mCur;
   1225          ++next;
   1226          mStack.AppendElement(std::make_pair(mCurrentFrame, next));
   1227          mCur = child->LinesBegin();
   1228          mEnd = child->LinesEnd();
   1229          mCurrentFrame = child;
   1230        } else {
   1231          // Some kind of frame we shouldn't descend into.
   1232          ++mCur;
   1233        }
   1234      } else {
   1235        MOZ_ASSERT(mCur->IsInline());
   1236        break;
   1237      }
   1238    }
   1239  }
   1240 
   1241  // The current line within the current block.
   1242  //
   1243  // When this is equal to mEnd, the iterator is at its end, and mCurrentFrame
   1244  // is set to null.
   1245  nsBlockFrame::LineIterator mCur;
   1246 
   1247  // The iterator end for the current block.
   1248  nsBlockFrame::LineIterator mEnd;
   1249 
   1250  // The current block.
   1251  nsBlockFrame* mCurrentFrame;
   1252 
   1253  // The block past which we can't look at line-clamp.
   1254  const nsBlockFrame* mStopAtFrame;
   1255 
   1256  // Stack of mCurrentFrame and mEnd values that we push and pop as we enter and
   1257  // exist blocks.
   1258  AutoTArray<std::pair<nsBlockFrame*, nsBlockFrame::LineIterator>, 8> mStack;
   1259 };
   1260 
   1261 static bool ClearLineClampEllipsis(nsBlockFrame* aFrame) {
   1262  if (aFrame->HasLineClampEllipsis()) {
   1263    MOZ_ASSERT(!aFrame->HasLineClampEllipsisDescendant());
   1264    aFrame->SetHasLineClampEllipsis(false);
   1265    for (auto& line : aFrame->Lines()) {
   1266      if (line.HasLineClampEllipsis()) {
   1267        line.ClearHasLineClampEllipsis();
   1268        break;
   1269      }
   1270    }
   1271    return true;
   1272  }
   1273 
   1274  if (aFrame->HasLineClampEllipsisDescendant()) {
   1275    aFrame->SetHasLineClampEllipsisDescendant(false);
   1276    for (nsIFrame* f : aFrame->PrincipalChildList()) {
   1277      if (nsBlockFrame* child = GetAsLineClampDescendant(f)) {
   1278        if (ClearLineClampEllipsis(child)) {
   1279          return true;
   1280        }
   1281      }
   1282    }
   1283  }
   1284 
   1285  return false;
   1286 }
   1287 
   1288 void nsBlockFrame::ClearLineClampEllipsis() { ::ClearLineClampEllipsis(this); }
   1289 
   1290 void nsBlockFrame::Reflow(nsPresContext* aPresContext, ReflowOutput& aMetrics,
   1291                          const ReflowInput& aReflowInput,
   1292                          nsReflowStatus& aStatus) {
   1293  if (IsHiddenByContentVisibilityOfInFlowParentForLayout()) {
   1294    FinishAndStoreOverflow(&aMetrics, aReflowInput.mStyleDisplay);
   1295    return;
   1296  }
   1297 
   1298  MarkInReflow();
   1299  DO_GLOBAL_REFLOW_COUNT("nsBlockFrame");
   1300  MOZ_ASSERT(aStatus.IsEmpty(), "Caller should pass a fresh reflow status!");
   1301 
   1302 #ifdef DEBUG
   1303  if (gNoisyReflow) {
   1304    IndentBy(stdout, gNoiseIndent);
   1305    fmt::println(FMT_STRING("{}: begin reflow: availSize={} computedSize={}"),
   1306                 ListTag().get(), ToString(aReflowInput.AvailableSize()),
   1307                 ToString(aReflowInput.ComputedSize()));
   1308  }
   1309  AutoNoisyIndenter indent(gNoisy);
   1310  PRTime start = 0;  // Initialize these variablies to silence the compiler.
   1311  int32_t ctc = 0;   // We only use these if they are set (gLameReflowMetrics).
   1312  if (gLameReflowMetrics) {
   1313    start = PR_Now();
   1314    ctc = nsLineBox::GetCtorCount();
   1315  }
   1316 #endif
   1317 
   1318  // ColumnSetWrapper's children depend on ColumnSetWrapper's block-size or
   1319  // max-block-size because both affect the children's available block-size.
   1320  if (IsColumnSetWrapperFrame()) {
   1321    AddStateBits(NS_FRAME_CONTAINS_RELATIVE_BSIZE);
   1322  }
   1323 
   1324  Maybe<nscoord> restoreReflowInputAvailBSize;
   1325  auto MaybeRestore = MakeScopeExit([&] {
   1326    if (MOZ_UNLIKELY(restoreReflowInputAvailBSize)) {
   1327      const_cast<ReflowInput&>(aReflowInput)
   1328          .SetAvailableBSize(*restoreReflowInputAvailBSize);
   1329    }
   1330  });
   1331 
   1332  WritingMode wm = aReflowInput.GetWritingMode();
   1333  const nscoord consumedBSize = CalcAndCacheConsumedBSize();
   1334  const nscoord effectiveContentBoxBSize =
   1335      GetEffectiveComputedBSize(aReflowInput, consumedBSize);
   1336  // If we have non-auto block size, we're clipping our kids and we fit,
   1337  // make sure our kids fit too.
   1338  if (aReflowInput.AvailableBSize() != NS_UNCONSTRAINEDSIZE &&
   1339      aReflowInput.ComputedBSize() != NS_UNCONSTRAINEDSIZE &&
   1340      ShouldApplyOverflowClipping(aReflowInput.mStyleDisplay)
   1341          .contains(wm.PhysicalAxis(LogicalAxis::Block))) {
   1342    LogicalMargin blockDirExtras =
   1343        aReflowInput.ComputedLogicalBorderPadding(wm);
   1344    if (GetLogicalSkipSides().BStart()) {
   1345      blockDirExtras.BStart(wm) = 0;
   1346    } else {
   1347      // Block-end margin never causes us to create continuations, so we
   1348      // don't need to worry about whether it fits in its entirety.
   1349      blockDirExtras.BStart(wm) +=
   1350          aReflowInput.ComputedLogicalMargin(wm).BStart(wm);
   1351    }
   1352 
   1353    if (effectiveContentBoxBSize + blockDirExtras.BStartEnd(wm) <=
   1354        aReflowInput.AvailableBSize()) {
   1355      restoreReflowInputAvailBSize.emplace(aReflowInput.AvailableBSize());
   1356      const_cast<ReflowInput&>(aReflowInput)
   1357          .SetAvailableBSize(NS_UNCONSTRAINEDSIZE);
   1358    }
   1359  }
   1360 
   1361  if (IsFrameTreeTooDeep(aReflowInput, aMetrics, aStatus)) {
   1362    return;
   1363  }
   1364 
   1365  // OK, some lines may be reflowed. Blow away any saved line cursor
   1366  // because we may invalidate the nondecreasing
   1367  // overflowArea.InkOverflow().y/yMost invariant, and we may even
   1368  // delete the line with the line cursor.
   1369  ClearLineCursors();
   1370 
   1371  // See comment below about oldSize. Use *only* for the
   1372  // abs-pos-containing-block-size-change optimization!
   1373  nsSize oldSize = GetSize();
   1374 
   1375  // Should we create a float manager?
   1376  nsAutoFloatManager autoFloatManager(const_cast<ReflowInput&>(aReflowInput));
   1377 
   1378  // XXXldb If we start storing the float manager in the frame rather
   1379  // than keeping it around only during reflow then we should create it
   1380  // only when there are actually floats to manage.  Otherwise things
   1381  // like tables will gain significant bloat.
   1382  //
   1383  // See https://bugzilla.mozilla.org/show_bug.cgi?id=1931286:
   1384  // if we're a reflow root and no float manager is provided by the caller
   1385  // in aReflowInput, we'd normally expect the block to be a BFC and so
   1386  // BlockNeedsFloatManager will return true. But sometimes the block may
   1387  // have lost its BFC-ness since it was recorded as a dirty reflow root
   1388  // but before the reflow actually happens. Creating a float manager here
   1389  // avoids crashing, but may not be entirely correct in such a case.
   1390  bool needFloatManager =
   1391      !aReflowInput.mFloatManager || nsBlockFrame::BlockNeedsFloatManager(this);
   1392  if (needFloatManager) {
   1393    autoFloatManager.CreateFloatManager(aPresContext);
   1394  }
   1395 
   1396  if (HasAnyStateBits(NS_BLOCK_NEEDS_BIDI_RESOLUTION) &&
   1397      PresContext()->BidiEnabled()) {
   1398    static_cast<nsBlockFrame*>(FirstContinuation())->ResolveBidi();
   1399  }
   1400 
   1401  // Whether to apply text-wrap: balance behavior.
   1402  bool tryBalance =
   1403      StyleText()->mTextWrapStyle == StyleTextWrapStyle::Balance &&
   1404      !GetPrevContinuation();
   1405 
   1406  // Struct used to hold the "target" number of lines or clamp position to
   1407  // maintain when doing text-wrap: balance.
   1408  struct BalanceTarget {
   1409    // If line-clamp is in effect, mContent and mOffset indicate the starting
   1410    // position of the first line after the clamp limit, and mBlockCoord is the
   1411    // block-axis offset of its position.
   1412    // If line-clamp is not in use, mContent is null, mOffset is the total
   1413    // number of lines that the block must contain, and mBlockCoord is its end
   1414    // edge in the block direction.
   1415    nsIContent* mContent = nullptr;
   1416    int32_t mOffset = -1;
   1417    nscoord mBlockCoord = 0;
   1418 
   1419    bool operator==(const BalanceTarget& aOther) const = default;
   1420    bool operator!=(const BalanceTarget& aOther) const = default;
   1421  };
   1422 
   1423  BalanceTarget balanceTarget;
   1424 
   1425  // Helpers for text-wrap: balance implementation:
   1426 
   1427  // Count the number of inline lines in the mLines list, but return -1 (to
   1428  // suppress balancing) instead if the count is going to exceed aLimit.
   1429  auto countLinesUpTo = [&](int32_t aLimit) -> int32_t {
   1430    int32_t n = 0;
   1431    for (auto iter = mLines.begin(); iter != mLines.end(); ++iter) {
   1432      // Block lines are ignored as they do not participate in balancing.
   1433      if (iter->IsInline() && ++n > aLimit) {
   1434        return -1;
   1435      }
   1436    }
   1437    return n;
   1438  };
   1439 
   1440  // Return a BalanceTarget record representing the position at which line-clamp
   1441  // will take effect for the current line list. Only to be used when there are
   1442  // enough lines that the clamp will apply.
   1443  auto getClampPosition = [&](uint32_t aClampCount) -> BalanceTarget {
   1444    if (NS_WARN_IF(aClampCount >= mLines.size())) {
   1445      return BalanceTarget{};
   1446    }
   1447    auto iter = mLines.begin();
   1448    for (uint32_t i = 0; i < aClampCount; i++) {
   1449      ++iter;
   1450    }
   1451    nsIFrame* firstChild = iter->mFirstChild;
   1452    if (!firstChild) {
   1453      return BalanceTarget{};
   1454    }
   1455    nsIContent* content = firstChild->GetContent();
   1456    if (!content) {
   1457      return BalanceTarget{};
   1458    }
   1459    int32_t offset = 0;
   1460    if (firstChild->IsTextFrame()) {
   1461      auto* textFrame = static_cast<nsTextFrame*>(firstChild);
   1462      offset = textFrame->GetContentOffset();
   1463    }
   1464    return BalanceTarget{content, offset, iter.get()->BStart()};
   1465  };
   1466 
   1467  // "balancing" is implemented by shortening the effective inline-size of the
   1468  // lines, so that content will tend to be pushed down to fill later lines of
   1469  // the block. `balanceInset` is the current amount of "inset" to apply, and
   1470  // `balanceStep` is the increment to adjust it by for the next iteration.
   1471  nscoord balanceStep = 0;
   1472 
   1473  // text-wrap: balance loop, executed only once if balancing is not required.
   1474  nsReflowStatus reflowStatus;
   1475  TrialReflowState trialState(consumedBSize, effectiveContentBoxBSize,
   1476                              needFloatManager);
   1477  while (true) {
   1478    // Save the initial floatManager state for repeated trial reflows.
   1479    // We'll restore (and re-save) the initial state each time we repeat the
   1480    // reflow.
   1481    nsFloatManager::SavedState floatManagerState;
   1482    aReflowInput.mFloatManager->PushState(&floatManagerState);
   1483 
   1484    aMetrics = ReflowOutput(aMetrics.GetWritingMode());
   1485    reflowStatus =
   1486        TrialReflow(aPresContext, aMetrics, aReflowInput, trialState);
   1487 
   1488    // Do we need to start a `text-wrap: balance` iteration?
   1489    if (tryBalance) {
   1490      tryBalance = false;
   1491      // Don't try to balance an incomplete block, or if we had to use an
   1492      // overflow-wrap break position in the initial reflow.
   1493      if (!reflowStatus.IsFullyComplete() || trialState.mUsedOverflowWrap) {
   1494        break;
   1495      }
   1496      balanceTarget.mOffset =
   1497          countLinesUpTo(StaticPrefs::layout_css_text_wrap_balance_limit());
   1498      if (balanceTarget.mOffset < 2) {
   1499        // If there are less than 2 lines, or the number exceeds the limit,
   1500        // no balancing is needed; just break from the balance loop.
   1501        break;
   1502      }
   1503      balanceTarget.mBlockCoord = mLines.back()->BEnd();
   1504      // Initialize the amount of inset to try, and the iteration step size.
   1505      balanceStep = aReflowInput.ComputedISize() / balanceTarget.mOffset;
   1506      trialState.ResetForBalance(balanceStep);
   1507      balanceStep /= 2;
   1508 
   1509      // If -webkit-line-clamp is in effect, then we need to maintain the
   1510      // content location at which clamping occurs, rather than the total
   1511      // number of lines in the block.
   1512      if (StaticPrefs::layout_css_text_wrap_balance_after_clamp_enabled() &&
   1513          IsLineClampRoot(this)) {
   1514        uint32_t lineClampCount = aReflowInput.mStyleDisplay->mWebkitLineClamp;
   1515        if (uint32_t(balanceTarget.mOffset) > lineClampCount) {
   1516          auto t = getClampPosition(lineClampCount);
   1517          if (t.mContent) {
   1518            balanceTarget = t;
   1519          }
   1520        }
   1521      }
   1522 
   1523      // Restore initial floatManager state for a new trial with updated inset.
   1524      aReflowInput.mFloatManager->PopState(&floatManagerState);
   1525      continue;
   1526    }
   1527 
   1528    // Helper to determine whether the current trial succeeded (i.e. was able
   1529    // to fit the content into the expected number of lines).
   1530    auto trialSucceeded = [&]() -> bool {
   1531      if (!reflowStatus.IsFullyComplete() || trialState.mUsedOverflowWrap) {
   1532        return false;
   1533      }
   1534      if (balanceTarget.mContent) {
   1535        auto t = getClampPosition(aReflowInput.mStyleDisplay->mWebkitLineClamp);
   1536        return t == balanceTarget;
   1537      }
   1538      int32_t numLines =
   1539          countLinesUpTo(StaticPrefs::layout_css_text_wrap_balance_limit());
   1540      return numLines == balanceTarget.mOffset &&
   1541             mLines.back()->BEnd() == balanceTarget.mBlockCoord;
   1542    };
   1543 
   1544    // If we're in the process of a balance operation, check whether we've
   1545    // inset by too much and either increase or reduce the inset for the next
   1546    // iteration.
   1547    if (balanceStep > 0) {
   1548      if (trialSucceeded()) {
   1549        trialState.ResetForBalance(balanceStep);
   1550      } else {
   1551        trialState.ResetForBalance(-balanceStep);
   1552      }
   1553      balanceStep /= 2;
   1554 
   1555      aReflowInput.mFloatManager->PopState(&floatManagerState);
   1556      continue;
   1557    }
   1558 
   1559    // If we were attempting to balance, check whether the final iteration was
   1560    // successful, and if not, back up by one step.
   1561    if (balanceTarget.mOffset >= 0) {
   1562      if (!trialState.mInset || trialSucceeded()) {
   1563        break;
   1564      }
   1565      trialState.ResetForBalance(-1);
   1566 
   1567      aReflowInput.mFloatManager->PopState(&floatManagerState);
   1568      continue;
   1569    }
   1570 
   1571    // If we reach here, no balancing was required, so just exit; we don't
   1572    // reset (pop) the floatManager state because this is the reflow we're
   1573    // going to keep. So the saved state is just dropped.
   1574    break;
   1575  }  // End of text-wrap: balance retry loop
   1576 
   1577  // If the block direction is right-to-left, we need to update the bounds of
   1578  // lines that were placed relative to mContainerSize during reflow, as
   1579  // we typically do not know the true container size until we've reflowed all
   1580  // its children. So we use a dummy mContainerSize during reflow (see
   1581  // BlockReflowState's constructor) and then fix up the positions of the
   1582  // lines here, once the final block size is known.
   1583  //
   1584  // Note that writing-mode:vertical-rl is the only case where the block
   1585  // logical direction progresses in a negative physical direction, and
   1586  // therefore block-dir coordinate conversion depends on knowing the width
   1587  // of the coordinate space in order to translate between the logical and
   1588  // physical origins.
   1589  if (aReflowInput.GetWritingMode().IsVerticalRL()) {
   1590    nsSize containerSize = aMetrics.PhysicalSize();
   1591    nscoord deltaX = containerSize.width - trialState.mContainerWidth;
   1592    if (deltaX != 0) {
   1593      // We compute our lines and markers' overflow areas later in
   1594      // ComputeOverflowAreas(), so we don't need to adjust their overflow areas
   1595      // here.
   1596      const nsPoint physicalDelta(deltaX, 0);
   1597      for (auto& line : Lines()) {
   1598        UpdateLineContainerSize(&line, containerSize);
   1599      }
   1600      trialState.mFcBounds.Clear();
   1601      if (nsFrameList* floats = GetFloats()) {
   1602        for (nsIFrame* f : *floats) {
   1603          f->MovePositionBy(physicalDelta);
   1604          ConsiderChildOverflow(trialState.mFcBounds, f);
   1605        }
   1606      }
   1607      if (nsFrameList* markerList = GetOutsideMarkerList()) {
   1608        for (nsIFrame* f : *markerList) {
   1609          f->MovePositionBy(physicalDelta);
   1610        }
   1611      }
   1612      if (nsFrameList* overflowContainers = GetOverflowContainers()) {
   1613        trialState.mOcBounds.Clear();
   1614        for (nsIFrame* f : *overflowContainers) {
   1615          f->MovePositionBy(physicalDelta);
   1616          ConsiderChildOverflow(trialState.mOcBounds, f);
   1617        }
   1618      }
   1619    }
   1620  }
   1621 
   1622  aMetrics.SetOverflowAreasToDesiredBounds();
   1623  ComputeOverflowAreas(aMetrics.mOverflowAreas, aReflowInput.mStyleDisplay);
   1624  // Factor overflow container child bounds into the overflow area
   1625  aMetrics.mOverflowAreas.UnionWith(trialState.mOcBounds);
   1626  // Factor pushed float child bounds into the overflow area
   1627  aMetrics.mOverflowAreas.UnionWith(trialState.mFcBounds);
   1628 
   1629  // Let the absolutely positioned container reflow any absolutely positioned
   1630  // child frames that need to be reflowed, e.g., elements with a percentage
   1631  // based width/height
   1632  // We want to do this under either of two conditions:
   1633  //  1. If we didn't do the incremental reflow above.
   1634  //  2. If our size changed.
   1635  // Even though it's the padding edge that's the containing block, we
   1636  // can use our rect (the border edge) since if the border style
   1637  // changed, the reflow would have been targeted at us so we'd satisfy
   1638  // condition 1.
   1639  // XXX checking oldSize is bogus, there are various reasons we might have
   1640  // reflowed but our size might not have been changed to what we
   1641  // asked for (e.g., we ended up being pushed to a new page)
   1642  // When WillReflowAgainForClearance is true, we will reflow again without
   1643  // resetting the size. Because of this, we must not reflow our abs-pos
   1644  // children in that situation --- what we think is our "new size" will not be
   1645  // our real new size. This also happens to be more efficient.
   1646  auto* absoluteContainer = GetAbsoluteContainingBlock();
   1647  if (absoluteContainer && absoluteContainer->PrepareAbsoluteFrames(this)) {
   1648    bool haveInterrupt = aPresContext->HasPendingInterrupt();
   1649    if (aReflowInput.WillReflowAgainForClearance() || haveInterrupt) {
   1650      // Make sure that when we reflow again we'll actually reflow all the abs
   1651      // pos frames that might conceivably depend on our size (or all of them,
   1652      // if we're dirty right now and interrupted; in that case we also need
   1653      // to mark them all with NS_FRAME_IS_DIRTY).  Sadly, we can't do much
   1654      // better than that, because we don't really know what our size will be,
   1655      // and it might in fact not change on the followup reflow!
   1656      if (haveInterrupt && HasAnyStateBits(NS_FRAME_IS_DIRTY)) {
   1657        absoluteContainer->MarkAllFramesDirty();
   1658      } else {
   1659        absoluteContainer->MarkSizeDependentFramesDirty();
   1660      }
   1661      if (haveInterrupt) {
   1662        // We're not going to reflow absolute frames; make sure to account for
   1663        // their existing overflow areas, which is usually a side effect of this
   1664        // reflow.
   1665        //
   1666        // TODO(emilio): AbsoluteContainingBlock::Reflow already checks for
   1667        // interrupt, can we just rely on it and unconditionally take the else
   1668        // branch below? That's a bit more subtle / risky, since I don't see
   1669        // what would reflow them in that case if they depended on our size.
   1670        for (nsIFrame* kid : absoluteContainer->GetChildList()) {
   1671          ConsiderChildOverflow(aMetrics.mOverflowAreas, kid);
   1672        }
   1673      }
   1674    } else {
   1675      // Mark frames that depend on changes we just made to this frame as dirty:
   1676      // Now we can assume that the padding edge hasn't moved.
   1677      // We need to reflow the absolutes if one of them depends on
   1678      // its placeholder position, or the containing block size in a
   1679      // direction in which the containing block size might have
   1680      // changed.
   1681 
   1682      // XXX "width" and "height" in this block will become ISize and BSize
   1683      // when AbsoluteContainingBlock is logicalized
   1684      bool cbWidthChanged = aMetrics.Width() != oldSize.width;
   1685      bool isRoot = !GetContent()->GetParent();
   1686      // If isRoot and we have auto height, then we are the initial
   1687      // containing block and the containing block height is the
   1688      // viewport height, which can't change during incremental
   1689      // reflow.
   1690      bool cbHeightChanged =
   1691          !(isRoot && NS_UNCONSTRAINEDSIZE == aReflowInput.ComputedHeight()) &&
   1692          aMetrics.Height() != oldSize.height;
   1693 
   1694      AbsPosReflowFlags flags{AbsPosReflowFlag::AllowFragmentation};
   1695      if (cbWidthChanged) {
   1696        flags += AbsPosReflowFlag::CBWidthChanged;
   1697      }
   1698      if (cbHeightChanged) {
   1699        flags += AbsPosReflowFlag::CBHeightChanged;
   1700      }
   1701      // Setup the line cursor here to optimize line searching for
   1702      // calculating hypothetical position of absolutely-positioned
   1703      // frames.
   1704      SetupLineCursorForQuery();
   1705 
   1706      LogicalRect cbRect(wm, LogicalPoint(wm), aMetrics.Size(wm));
   1707      cbRect.Deflate(wm, aReflowInput.ComputedLogicalBorder(wm).ApplySkipSides(
   1708                             PreReflowBlockLevelLogicalSkipSides()));
   1709      absoluteContainer->Reflow(
   1710          this, aPresContext, aReflowInput, reflowStatus,
   1711          cbRect.GetPhysicalRect(wm, aMetrics.PhysicalSize()), flags,
   1712          &aMetrics.mOverflowAreas);
   1713    }
   1714  }
   1715 
   1716  FinishAndStoreOverflow(&aMetrics, aReflowInput.mStyleDisplay);
   1717 
   1718  aStatus = reflowStatus;
   1719 
   1720 #ifdef DEBUG
   1721  // Between when we drain pushed floats and when we complete reflow,
   1722  // we're allowed to have multiple continuations of the same float on
   1723  // our floats list, since a first-in-flow might get pushed to a later
   1724  // continuation of its containing block.  But it's not permitted
   1725  // outside that time.
   1726  nsLayoutUtils::AssertNoDuplicateContinuations(
   1727      this, GetChildList(FrameChildListID::Float));
   1728 
   1729  if (gNoisyReflow) {
   1730    IndentBy(stdout, gNoiseIndent);
   1731    fmt::print(FMT_STRING("{}: status={} metrics={} carriedMargin={}"),
   1732               ListTag().get(), ToString(aStatus), ToString(aMetrics.Size(wm)),
   1733               aMetrics.mCarriedOutBEndMargin.Get());
   1734    if (HasOverflowAreas()) {
   1735      fmt::print(FMT_STRING(" overflow-ink={} overflow-scr={}"),
   1736                 ToString(aMetrics.InkOverflow()),
   1737                 ToString(aMetrics.ScrollableOverflow()));
   1738    }
   1739    printf("\n");
   1740  }
   1741 
   1742  if (gLameReflowMetrics) {
   1743    PRTime end = PR_Now();
   1744 
   1745    int32_t ectc = nsLineBox::GetCtorCount();
   1746    int32_t numLines = mLines.size();
   1747    if (!numLines) {
   1748      numLines = 1;
   1749    }
   1750    PRTime delta, perLineDelta, lines;
   1751    lines = int64_t(numLines);
   1752    delta = end - start;
   1753    perLineDelta = delta / lines;
   1754 
   1755    ListTag(stdout);
   1756    char buf[400];
   1757    SprintfLiteral(buf,
   1758                   ": %" PRId64 " elapsed (%" PRId64
   1759                   " per line) (%d lines; %d new lines)",
   1760                   delta, perLineDelta, numLines, ectc - ctc);
   1761    printf("%s\n", buf);
   1762  }
   1763 #endif
   1764 }
   1765 
   1766 nsReflowStatus nsBlockFrame::TrialReflow(nsPresContext* aPresContext,
   1767                                         ReflowOutput& aMetrics,
   1768                                         const ReflowInput& aReflowInput,
   1769                                         TrialReflowState& aTrialState) {
   1770 #ifdef DEBUG
   1771  // Between when we drain pushed floats and when we complete reflow,
   1772  // we're allowed to have multiple continuations of the same float on
   1773  // our floats list, since a first-in-flow might get pushed to a later
   1774  // continuation of its containing block.  But it's not permitted
   1775  // outside that time.
   1776  nsLayoutUtils::AssertNoDuplicateContinuations(
   1777      this, GetChildList(FrameChildListID::Float));
   1778 #endif
   1779 
   1780  // ALWAYS drain overflow. We never want to leave the previnflow's
   1781  // overflow lines hanging around; block reflow depends on the
   1782  // overflow line lists being cleared out between reflow passes.
   1783  DrainOverflowLines();
   1784 
   1785  // Clear any existing -webkit-line-clamp ellipsis if we're reflowing the
   1786  // line-clamp root.
   1787  if (IsLineClampRoot(this)) {
   1788    ClearLineClampEllipsis();
   1789  }
   1790 
   1791  bool blockStartMarginRoot, blockEndMarginRoot;
   1792  IsMarginRoot(&blockStartMarginRoot, &blockEndMarginRoot);
   1793 
   1794  BlockReflowState state(aReflowInput, aPresContext, this, blockStartMarginRoot,
   1795                         blockEndMarginRoot, aTrialState.mNeedFloatManager,
   1796                         aTrialState.mConsumedBSize,
   1797                         aTrialState.mEffectiveContentBoxBSize,
   1798                         aTrialState.mInset);
   1799 
   1800  // Handle paginated overflow (see nsContainerFrame.h)
   1801  nsReflowStatus ocStatus;
   1802  if (GetPrevInFlow()) {
   1803    ReflowOverflowContainerChildren(
   1804        aPresContext, aReflowInput, aTrialState.mOcBounds,
   1805        ReflowChildFlags::Default, ocStatus, DefaultChildFrameMerge,
   1806        Some(state.ContainerSize()));
   1807  }
   1808 
   1809  // Now that we're done cleaning up our overflow container lists, we can
   1810  // give |state| its nsOverflowContinuationTracker.
   1811  nsOverflowContinuationTracker tracker(this, false);
   1812  state.mOverflowTracker = &tracker;
   1813 
   1814  // Drain & handle pushed floats
   1815  DrainPushedFloats();
   1816  ReflowPushedFloats(state, aTrialState.mFcBounds);
   1817 
   1818  // If we're not dirty (which means we'll mark everything dirty later)
   1819  // and our inline-size has changed, mark the lines dirty that we need to
   1820  // mark dirty for a resize reflow.
   1821  if (!HasAnyStateBits(NS_FRAME_IS_DIRTY) && aReflowInput.IsIResize()) {
   1822    PrepareResizeReflow(state);
   1823  }
   1824 
   1825  // The same for percentage text-indent, except conditioned on the
   1826  // parent resizing.
   1827  if (!HasAnyStateBits(NS_FRAME_IS_DIRTY) && aReflowInput.mCBReflowInput &&
   1828      aReflowInput.mCBReflowInput->IsIResize() &&
   1829      StyleText()->mTextIndent.length.HasPercent() && !mLines.empty()) {
   1830    mLines.front()->MarkDirty();
   1831  }
   1832 
   1833  // For text-wrap:balance trials, we need to reflow all the inline lines even
   1834  // if they're not all "dirty", but we don't need to reflow any block lines.
   1835  if (aTrialState.mBalancing) {
   1836    MarkAllInlineLinesDirty(this);
   1837  } else {
   1838    LazyMarkLinesDirty();
   1839  }
   1840 
   1841  // Now reflow...
   1842  aTrialState.mUsedOverflowWrap = ReflowDirtyLines(state);
   1843 
   1844  // If we have a next-in-flow, and that next-in-flow has pushed floats from
   1845  // this frame from a previous iteration of reflow, then we should not return
   1846  // a status with IsFullyComplete() equals to true, since we actually have
   1847  // overflow, it's just already been handled.
   1848 
   1849  // NOTE: This really shouldn't happen, since we _should_ pull back our floats
   1850  // and reflow them, but just in case it does, this is a safety precaution so
   1851  // we don't end up with a placeholder pointing to frames that have already
   1852  // been deleted as part of removing our next-in-flow.
   1853  if (state.mReflowStatus.IsFullyComplete()) {
   1854    nsBlockFrame* nif = static_cast<nsBlockFrame*>(GetNextInFlow());
   1855    while (nif) {
   1856      if (nif->HasPushedFloatsFromPrevContinuation()) {
   1857        if (nif->HasAnyStateBits(NS_FRAME_IS_OVERFLOW_CONTAINER)) {
   1858          state.mReflowStatus.SetOverflowIncomplete();
   1859        } else {
   1860          state.mReflowStatus.SetIncomplete();
   1861        }
   1862        break;
   1863      }
   1864 
   1865      nif = static_cast<nsBlockFrame*>(nif->GetNextInFlow());
   1866    }
   1867  }
   1868 
   1869  state.mReflowStatus.MergeCompletionStatusFrom(ocStatus);
   1870 
   1871  // If we end in a BR with clear and affected floats continue,
   1872  // we need to continue, too.
   1873  if (NS_UNCONSTRAINEDSIZE != aReflowInput.AvailableBSize() &&
   1874      state.mReflowStatus.IsComplete() &&
   1875      state.FloatManager()->ClearContinues(FindTrailingClear())) {
   1876    state.mReflowStatus.SetIncomplete();
   1877  }
   1878 
   1879  if (!state.mReflowStatus.IsFullyComplete()) {
   1880    if (HasOverflowLines() || HasPushedFloats()) {
   1881      state.mReflowStatus.SetNextInFlowNeedsReflow();
   1882    }
   1883  }
   1884 
   1885  // Place the ::marker's frame if it is placed next to a block child.
   1886  //
   1887  // According to the CSS2 spec, section 12.6.1, the ::marker's box
   1888  // participates in the height calculation of the list-item box's
   1889  // first line box.
   1890  //
   1891  // There are exactly two places a ::marker can be placed: near the
   1892  // first or second line. It's only placed on the second line in a
   1893  // rare case: an empty first line followed by a second line that
   1894  // contains a block (example: <LI>\n<P>... ). This is where
   1895  // the second case can happen.
   1896  nsIFrame* outsideMarker = GetOutsideMarker();
   1897  if (outsideMarker && !mLines.empty() &&
   1898      (mLines.front()->IsBlock() ||
   1899       (0 == mLines.front()->BSize() && mLines.front() != mLines.back() &&
   1900        mLines.begin().next()->IsBlock()))) {
   1901    // Reflow the ::marker's frame.
   1902    ReflowOutput reflowOutput(aReflowInput);
   1903    // XXX Use the entire line when we fix bug 25888.
   1904    nsLayoutUtils::LinePosition position;
   1905    WritingMode wm = aReflowInput.GetWritingMode();
   1906    bool havePosition =
   1907        nsLayoutUtils::GetFirstLinePosition(wm, this, &position);
   1908    nscoord lineBStart =
   1909        havePosition ? position.mBStart
   1910                     : aReflowInput.ComputedLogicalBorderPadding(wm).BStart(wm);
   1911    ReflowOutsideMarker(outsideMarker, state, reflowOutput, lineBStart);
   1912    NS_ASSERTION(!MarkerIsEmpty(outsideMarker) || reflowOutput.BSize(wm) == 0,
   1913                 "empty ::marker frame took up space");
   1914 
   1915    if (havePosition && !MarkerIsEmpty(outsideMarker)) {
   1916      // We have some lines to align the ::marker with.
   1917 
   1918      // Doing the alignment using the baseline will also cater for
   1919      // ::markers that are placed next to a child block (bug 92896)
   1920 
   1921      // Tall ::markers won't look particularly nice here...
   1922      LogicalRect bbox =
   1923          outsideMarker->GetLogicalRect(wm, reflowOutput.PhysicalSize());
   1924      const auto baselineGroup = BaselineSharingGroup::First;
   1925      Maybe<nscoord> result;
   1926      if (MOZ_LIKELY(!wm.IsOrthogonalTo(outsideMarker->GetWritingMode()))) {
   1927        result = outsideMarker->GetNaturalBaselineBOffset(
   1928            wm, baselineGroup, BaselineExportContext::LineLayout);
   1929      }
   1930      const auto markerBaseline =
   1931          result.valueOrFrom([bbox, wm, outsideMarker]() {
   1932            return bbox.BSize(wm) +
   1933                   outsideMarker->GetLogicalUsedMargin(wm).BEnd(wm);
   1934          });
   1935      bbox.BStart(wm) = position.mBaseline - markerBaseline;
   1936      outsideMarker->SetRect(wm, bbox, reflowOutput.PhysicalSize());
   1937    }
   1938    // Otherwise just leave the ::marker where it is, up against our
   1939    // block-start padding.
   1940  }
   1941 
   1942  CheckFloats(state);
   1943 
   1944  // Compute our final size (for this trial layout)
   1945  aTrialState.mBlockEndEdgeOfChildren =
   1946      ComputeFinalSize(aReflowInput, state, aMetrics);
   1947  aTrialState.mContainerWidth = state.ContainerSize().width;
   1948 
   1949  // Align content
   1950  AlignContent(state, aMetrics, aTrialState.mBlockEndEdgeOfChildren);
   1951 
   1952  return state.mReflowStatus;
   1953 }
   1954 
   1955 bool nsBlockFrame::CheckForCollapsedBEndMarginFromClearanceLine() {
   1956  for (auto& line : Reversed(Lines())) {
   1957    if (0 != line.BSize() || !line.CachedIsEmpty()) {
   1958      return false;
   1959    }
   1960    if (line.HasClearance()) {
   1961      return true;
   1962    }
   1963  }
   1964  return false;
   1965 }
   1966 
   1967 std::pair<nsBlockFrame*, nsLineBox*> FindLineClampTarget(
   1968    nsBlockFrame* const aRootFrame, const nsBlockFrame* const aStopAtFrame,
   1969    StyleLineClamp aLineNumber) {
   1970  MOZ_ASSERT(aLineNumber > 0);
   1971 
   1972  nsLineBox* targetLine = nullptr;
   1973  nsBlockFrame* targetFrame = nullptr;
   1974  bool foundFollowingLine = false;
   1975 
   1976  LineClampLineIterator iter(aRootFrame, aStopAtFrame);
   1977 
   1978  while (nsLineBox* line = iter.GetCurrentLine()) {
   1979    // Don't count a line that only has collapsible white space (as might exist
   1980    // after calling e.g. getBoxQuads).
   1981    if (line->IsEmpty()) {
   1982      iter.Next();
   1983      continue;
   1984    }
   1985 
   1986    if (aLineNumber == 0) {
   1987      // We already previously found our target line, and now we have
   1988      // confirmed that there is another line after it.
   1989      foundFollowingLine = true;
   1990      break;
   1991    }
   1992 
   1993    if (--aLineNumber == 0) {
   1994      // This is our target line.  Continue looping to confirm that we
   1995      // have another line after us.
   1996      targetLine = line;
   1997      targetFrame = iter.GetCurrentFrame();
   1998    }
   1999 
   2000    iter.Next();
   2001  }
   2002 
   2003  if (!foundFollowingLine) {
   2004    MOZ_ASSERT(!aRootFrame->HasLineClampEllipsis(),
   2005               "should have been removed earlier");
   2006    return std::pair(nullptr, nullptr);
   2007  }
   2008 
   2009  MOZ_ASSERT(targetLine);
   2010  MOZ_ASSERT(targetFrame);
   2011 
   2012  // If targetFrame is not the same as the line-clamp root, any ellipsis on the
   2013  // root should have been previously cleared.
   2014  MOZ_ASSERT(targetFrame == aRootFrame || !aRootFrame->HasLineClampEllipsis(),
   2015             "line-clamp target mismatch");
   2016 
   2017  return std::pair(targetFrame, targetLine);
   2018 }
   2019 
   2020 nscoord nsBlockFrame::ApplyLineClamp(nscoord aContentBlockEndEdge) {
   2021  auto* root = GetLineClampRoot();
   2022  if (!root) {
   2023    return aContentBlockEndEdge;
   2024  }
   2025 
   2026  auto lineClamp = root->StyleDisplay()->mWebkitLineClamp;
   2027  auto [target, line] = FindLineClampTarget(root, this, lineClamp);
   2028  if (!line) {
   2029    // The number of lines did not exceed the -webkit-line-clamp value.
   2030    return aContentBlockEndEdge;
   2031  }
   2032 
   2033  // Mark the line as having an ellipsis so that TextOverflow will render it.
   2034  line->SetHasLineClampEllipsis();
   2035  target->SetHasLineClampEllipsis(true);
   2036 
   2037  // Translate the b-end edge of the line up to aFrame's space.
   2038  nscoord edge = line->BEnd();
   2039  for (nsIFrame* f = target; f; f = f->GetParent()) {
   2040    MOZ_ASSERT(f->IsBlockFrameOrSubclass(),
   2041               "GetAsLineClampDescendant guarantees this");
   2042    if (f != target) {
   2043      static_cast<nsBlockFrame*>(f)->SetHasLineClampEllipsisDescendant(true);
   2044    }
   2045    if (f == this) {
   2046      break;
   2047    }
   2048    if (f == root) {
   2049      // The clamped line is not in our subtree.
   2050      return aContentBlockEndEdge;
   2051    }
   2052    const auto wm = f->GetWritingMode();
   2053    const nsSize parentSize = f->GetParent()->GetSize();
   2054    edge = f->GetLogicalRect(parentSize).BEnd(wm);
   2055  }
   2056 
   2057  return edge;
   2058 }
   2059 
   2060 nscoord nsBlockFrame::ComputeFinalSize(const ReflowInput& aReflowInput,
   2061                                       BlockReflowState& aState,
   2062                                       ReflowOutput& aMetrics) {
   2063  WritingMode wm = aState.mReflowInput.GetWritingMode();
   2064  const LogicalMargin& borderPadding = aState.BorderPadding();
   2065 #ifdef NOISY_FINAL_SIZE
   2066  ListTag(stdout);
   2067  printf(": mBCoord=%d mIsBEndMarginRoot=%s mPrevBEndMargin=%d bp=%d,%d\n",
   2068         aState.mBCoord, aState.mFlags.mIsBEndMarginRoot ? "yes" : "no",
   2069         aState.mPrevBEndMargin.get(), borderPadding.BStart(wm),
   2070         borderPadding.BEnd(wm));
   2071 #endif
   2072 
   2073  // Compute final inline size
   2074  LogicalSize finalSize(wm);
   2075  finalSize.ISize(wm) =
   2076      NSCoordSaturatingAdd(NSCoordSaturatingAdd(borderPadding.IStart(wm),
   2077                                                aReflowInput.ComputedISize()),
   2078                           borderPadding.IEnd(wm));
   2079 
   2080  // Return block-end margin information
   2081  // rbs says he hit this assertion occasionally (see bug 86947), so
   2082  // just set the margin to zero and we'll figure out why later
   2083  // NS_ASSERTION(aMetrics.mCarriedOutBEndMargin.IsZero(),
   2084  //             "someone else set the margin");
   2085  nscoord nonCarriedOutBDirMargin = 0;
   2086  if (!aState.mFlags.mIsBEndMarginRoot) {
   2087    // Apply rule from CSS 2.1 section 8.3.1. If we have some empty
   2088    // line with clearance and a non-zero block-start margin and all
   2089    // subsequent lines are empty, then we do not allow our children's
   2090    // carried out block-end margin to be carried out of us and collapse
   2091    // with our own block-end margin.
   2092    if (CheckForCollapsedBEndMarginFromClearanceLine()) {
   2093      // Convert the children's carried out margin to something that
   2094      // we will include in our height
   2095      nonCarriedOutBDirMargin = aState.mPrevBEndMargin.Get();
   2096      aState.mPrevBEndMargin.Zero();
   2097    }
   2098    aMetrics.mCarriedOutBEndMargin = aState.mPrevBEndMargin;
   2099  } else {
   2100    aMetrics.mCarriedOutBEndMargin.Zero();
   2101  }
   2102 
   2103  nscoord blockEndEdgeOfChildren = aState.mBCoord + nonCarriedOutBDirMargin;
   2104  // Shrink wrap our height around our contents.
   2105  if (aState.mFlags.mIsBEndMarginRoot ||
   2106      NS_UNCONSTRAINEDSIZE != aReflowInput.ComputedBSize()) {
   2107    // When we are a block-end-margin root make sure that our last
   2108    // child's block-end margin is fully applied. We also do this when
   2109    // we have a computed height, since in that case the carried out
   2110    // margin is not going to be applied anywhere, so we should note it
   2111    // here to be included in the overflow area.
   2112    // Apply the margin only if there's space for it.
   2113    if (blockEndEdgeOfChildren < aState.mReflowInput.AvailableBSize()) {
   2114      // Truncate block-end margin if it doesn't fit to our available BSize.
   2115      blockEndEdgeOfChildren =
   2116          std::min(blockEndEdgeOfChildren + aState.mPrevBEndMargin.Get(),
   2117                   aState.mReflowInput.AvailableBSize());
   2118    }
   2119  }
   2120  if (aState.mFlags.mBlockNeedsFloatManager) {
   2121    // Include the float manager's state to properly account for the
   2122    // block-end margin of any floated elements; e.g., inside a table cell.
   2123    //
   2124    // Note: The block coordinate returned by ClearFloats is always greater than
   2125    // or equal to blockEndEdgeOfChildren.
   2126    std::tie(blockEndEdgeOfChildren, std::ignore) =
   2127        aState.ClearFloats(blockEndEdgeOfChildren, UsedClear::Both);
   2128  }
   2129 
   2130  // undo cached alignment shift for sizing purposes
   2131  // (we used shifted positions because the float manager uses them)
   2132  blockEndEdgeOfChildren -= aState.mAlignContentShift;
   2133  aState.UndoAlignContentShift();
   2134 
   2135  if (NS_UNCONSTRAINEDSIZE != aReflowInput.ComputedBSize()) {
   2136    // Note: We don't use blockEndEdgeOfChildren because it includes the
   2137    // previous margin.
   2138    const nscoord contentBSizeWithBStartBP =
   2139        aState.mBCoord + nonCarriedOutBDirMargin;
   2140 
   2141    // We don't care about ApplyLineClamp's return value (the line-clamped
   2142    // content BSize) in this explicit-BSize codepath, but we do still need to
   2143    // call ApplyLineClamp for ellipsis markers to be placed as-needed.
   2144    ApplyLineClamp(contentBSizeWithBStartBP);
   2145 
   2146    finalSize.BSize(wm) = ComputeFinalBSize(aState, contentBSizeWithBStartBP);
   2147 
   2148    // If the content block-size is larger than the effective computed
   2149    // block-size, we extend the block-size to contain all the content.
   2150    // https://drafts.csswg.org/css-sizing-4/#aspect-ratio-minimum
   2151    if (aReflowInput.ShouldApplyAutomaticMinimumOnBlockAxis()) {
   2152      // Note: finalSize.BSize(wm) is the border-box size, so we compare it with
   2153      // the content's block-size plus our border and padding..
   2154      finalSize.BSize(wm) =
   2155          std::max(finalSize.BSize(wm),
   2156                   contentBSizeWithBStartBP + borderPadding.BEnd(wm));
   2157 
   2158      // The size should be capped by its maximum block size.
   2159      if (aReflowInput.ComputedMaxBSize() != NS_UNCONSTRAINEDSIZE) {
   2160        finalSize.BSize(wm) =
   2161            std::min(finalSize.BSize(wm), aReflowInput.ComputedMaxBSize() +
   2162                                              borderPadding.BStartEnd(wm));
   2163      }
   2164    }
   2165 
   2166    // Don't carry out a block-end margin when our BSize is fixed.
   2167    //
   2168    // Note: this also includes the case that aReflowInput.ComputedBSize() is
   2169    // calculated from aspect-ratio. i.e. Don't carry out block margin-end if it
   2170    // is replaced by the block size from aspect-ratio and inline size.
   2171    aMetrics.mCarriedOutBEndMargin.Zero();
   2172  } else if (Maybe<nscoord> containBSize = ContainIntrinsicBSize(
   2173                 IsComboboxControlFrame() ? aReflowInput.GetLineHeight() : 0)) {
   2174    // If we're size-containing in block axis and we don't have a specified
   2175    // block size, then our final size should actually be computed from only
   2176    // our border, padding and contain-intrinsic-block-size, ignoring the
   2177    // actual contents. Hence this case is a simplified version of the case
   2178    // below.
   2179    nscoord contentBSize = *containBSize;
   2180    nscoord autoBSize =
   2181        aReflowInput.ApplyMinMaxBSize(contentBSize, aState.mConsumedBSize);
   2182    aMetrics.mCarriedOutBEndMargin.Zero();
   2183    autoBSize += borderPadding.BStartEnd(wm);
   2184    finalSize.BSize(wm) = autoBSize;
   2185  } else if (aState.mReflowStatus.IsInlineBreakBefore()) {
   2186    // Our parent is expected to push this frame to the next page/column so
   2187    // what size we set here doesn't really matter.
   2188    finalSize.BSize(wm) = aReflowInput.AvailableBSize();
   2189  } else if (aState.mReflowStatus.IsComplete()) {
   2190    const nscoord lineClampedContentBlockEndEdge =
   2191        ApplyLineClamp(blockEndEdgeOfChildren);
   2192 
   2193    const nscoord bpBStart = borderPadding.BStart(wm);
   2194    const nscoord contentBSize = blockEndEdgeOfChildren - bpBStart;
   2195    const nscoord lineClampedContentBSize =
   2196        lineClampedContentBlockEndEdge - bpBStart;
   2197 
   2198    const nscoord autoBSize = aReflowInput.ApplyMinMaxBSize(
   2199        lineClampedContentBSize, aState.mConsumedBSize);
   2200    if (autoBSize != contentBSize) {
   2201      // Our min-block-size, max-block-size, or -webkit-line-clamp value made
   2202      // our bsize change.  Don't carry out our kids' block-end margins.
   2203      aMetrics.mCarriedOutBEndMargin.Zero();
   2204    }
   2205    nscoord bSize = autoBSize + borderPadding.BStartEnd(wm);
   2206    if (MOZ_UNLIKELY(autoBSize > contentBSize &&
   2207                     bSize > aReflowInput.AvailableBSize() &&
   2208                     aReflowInput.AvailableBSize() != NS_UNCONSTRAINEDSIZE)) {
   2209      // Applying `min-size` made us overflow our available size.
   2210      // Clamp it and report that we're Incomplete, or BreakBefore if we have
   2211      // 'break-inside: avoid' that is applicable.
   2212      bSize = aReflowInput.AvailableBSize();
   2213      if (ShouldAvoidBreakInside(aReflowInput)) {
   2214        aState.mReflowStatus.SetInlineLineBreakBeforeAndReset();
   2215      } else {
   2216        aState.mReflowStatus.SetIncomplete();
   2217      }
   2218    }
   2219    finalSize.BSize(wm) = bSize;
   2220  } else {
   2221    NS_ASSERTION(aReflowInput.AvailableBSize() != NS_UNCONSTRAINEDSIZE,
   2222                 "Shouldn't be incomplete if availableBSize is UNCONSTRAINED.");
   2223    nscoord bSize = std::max(aState.mBCoord, aReflowInput.AvailableBSize());
   2224    if (aReflowInput.AvailableBSize() == NS_UNCONSTRAINEDSIZE) {
   2225      // This should never happen, but it does. See bug 414255
   2226      bSize = aState.mBCoord;
   2227    }
   2228    const nscoord maxBSize = aReflowInput.ComputedMaxBSize();
   2229    if (maxBSize != NS_UNCONSTRAINEDSIZE &&
   2230        aState.mConsumedBSize + bSize - borderPadding.BStart(wm) > maxBSize) {
   2231      // Compute this fragment's block-size, with the max-block-size
   2232      // constraint taken into consideration.
   2233      const nscoord clampedBSizeWithoutEndBP =
   2234          std::max(0, maxBSize - aState.mConsumedBSize) +
   2235          borderPadding.BStart(wm);
   2236      const nscoord clampedBSize =
   2237          clampedBSizeWithoutEndBP + borderPadding.BEnd(wm);
   2238      if (clampedBSize <= aReflowInput.AvailableBSize()) {
   2239        // We actually fit after applying `max-size` so we should be
   2240        // Overflow-Incomplete instead.
   2241        bSize = clampedBSize;
   2242        aState.mReflowStatus.SetOverflowIncomplete();
   2243      } else {
   2244        // We cannot fit after applying `max-size` with our block-end BP, so
   2245        // we should draw it in our next continuation.
   2246        bSize = clampedBSizeWithoutEndBP;
   2247      }
   2248    }
   2249    finalSize.BSize(wm) = bSize;
   2250  }
   2251 
   2252  if (IsTrueOverflowContainer()) {
   2253    if (aState.mReflowStatus.IsIncomplete()) {
   2254      // Overflow containers can only be overflow complete.
   2255      // Note that auto height overflow containers have no normal children
   2256      NS_ASSERTION(finalSize.BSize(wm) == 0,
   2257                   "overflow containers must be zero-block-size");
   2258      aState.mReflowStatus.SetOverflowIncomplete();
   2259    }
   2260  } else if (aReflowInput.AvailableBSize() != NS_UNCONSTRAINEDSIZE &&
   2261             !aState.mReflowStatus.IsInlineBreakBefore() &&
   2262             aState.mReflowStatus.IsComplete()) {
   2263    // Currently only used for grid items, but could be used in other contexts.
   2264    // The FragStretchBSizeProperty is our expected non-fragmented block-size
   2265    // we should stretch to (for align-self:stretch etc).  In some fragmentation
   2266    // cases though, the last fragment (this frame since we're complete), needs
   2267    // to have extra size applied because earlier fragments consumed too much of
   2268    // our computed size due to overflowing their containing block.  (E.g. this
   2269    // ensures we fill the last row when a multi-row grid item is fragmented).
   2270    bool found;
   2271    nscoord bSize = GetProperty(FragStretchBSizeProperty(), &found);
   2272    if (found) {
   2273      finalSize.BSize(wm) = std::max(bSize, finalSize.BSize(wm));
   2274    }
   2275  }
   2276 
   2277  // Clamp the content size to fit within the margin-box clamp size, if any.
   2278  if (MOZ_UNLIKELY(aReflowInput.mComputeSizeFlags.contains(
   2279          ComputeSizeFlag::BClampMarginBoxMinSize)) &&
   2280      aState.mReflowStatus.IsComplete()) {
   2281    bool found;
   2282    nscoord cbSize = GetProperty(BClampMarginBoxMinSizeProperty(), &found);
   2283    if (found) {
   2284      auto marginBoxBSize =
   2285          finalSize.BSize(wm) +
   2286          aReflowInput.ComputedLogicalMargin(wm).BStartEnd(wm);
   2287      auto overflow = marginBoxBSize - cbSize;
   2288      if (overflow > 0) {
   2289        auto contentBSize = finalSize.BSize(wm) - borderPadding.BStartEnd(wm);
   2290        auto newContentBSize = std::max(nscoord(0), contentBSize - overflow);
   2291        // XXXmats deal with percentages better somehow?
   2292        finalSize.BSize(wm) -= contentBSize - newContentBSize;
   2293      }
   2294    }
   2295  }
   2296 
   2297  // Screen out negative block sizes --- can happen due to integer overflows :-(
   2298  finalSize.BSize(wm) = std::max(0, finalSize.BSize(wm));
   2299  aMetrics.SetSize(wm, finalSize);
   2300 
   2301  return blockEndEdgeOfChildren;
   2302 }
   2303 
   2304 void nsBlockFrame::AlignContent(BlockReflowState& aState,
   2305                                ReflowOutput& aMetrics,
   2306                                nscoord aBEndEdgeOfChildren) {
   2307  StyleAlignFlags alignment = EffectiveAlignContent();
   2308  alignment &= ~StyleAlignFlags::FLAG_BITS;
   2309 
   2310  // Short circuit
   2311  const bool isCentered = alignment == StyleAlignFlags::CENTER ||
   2312                          alignment == StyleAlignFlags::SPACE_AROUND ||
   2313                          alignment == StyleAlignFlags::SPACE_EVENLY;
   2314  const bool isEndAlign = alignment == StyleAlignFlags::END ||
   2315                          alignment == StyleAlignFlags::FLEX_END ||
   2316                          alignment == StyleAlignFlags::LAST_BASELINE;
   2317  if (!isEndAlign && !isCentered && !aState.mAlignContentShift) {
   2318    // desired shift = 0, no cached shift to undo
   2319    return;
   2320  }
   2321 
   2322  // NOTE: ComputeFinalSize already called aState.UndoAlignContentShift(),
   2323  //       so metrics no longer include cached shift.
   2324  // NOTE: Content is currently positioned at cached shift
   2325  // NOTE: Content has been fragmented against 0-shift assumption.
   2326 
   2327  // Calculate shift
   2328  nscoord shift = 0;
   2329  WritingMode wm = aState.mReflowInput.GetWritingMode();
   2330  if ((isCentered || isEndAlign) && !mLines.empty() &&
   2331      aState.mReflowStatus.IsFullyComplete() && !GetPrevInFlow()) {
   2332    nscoord availB = aState.mReflowInput.AvailableBSize();
   2333    nscoord endB =
   2334        aMetrics.Size(wm).BSize(wm) - aState.BorderPadding().BEnd(wm);
   2335    shift = std::min(availB, endB) - aBEndEdgeOfChildren;
   2336 
   2337    // note: these measures all include start BP, so it subtracts out
   2338    if (!(StylePosition()->mAlignContent.primary & StyleAlignFlags::UNSAFE)) {
   2339      shift = std::max(0, shift);
   2340    }
   2341    if (isCentered) {
   2342      shift = shift / 2;
   2343    }
   2344  }
   2345  // else: zero shift if start-aligned or if fragmented
   2346 
   2347  nscoord delta = shift - aState.mAlignContentShift;
   2348  if (delta) {
   2349    // Shift children
   2350    LogicalPoint translation(wm, 0, delta);
   2351    for (nsLineBox& line : Lines()) {
   2352      SlideLine(aState, &line, delta);
   2353    }
   2354    for (nsIFrame* kid : GetChildList(FrameChildListID::Float)) {
   2355      kid->MovePositionBy(wm, translation);
   2356    }
   2357    nsIFrame* outsideMarker = GetOutsideMarker();
   2358    if (outsideMarker && !mLines.empty()) {
   2359      outsideMarker->MovePositionBy(wm, translation);
   2360    }
   2361  }
   2362 
   2363  if (shift) {
   2364    // Cache shift
   2365    SetProperty(AlignContentShift(), shift);
   2366  } else {
   2367    RemoveProperty(AlignContentShift());
   2368  }
   2369 }
   2370 
   2371 void nsBlockFrame::ComputeOverflowAreas(OverflowAreas& aOverflowAreas,
   2372                                        const nsStyleDisplay* aDisplay) const {
   2373  // XXX_perf: This can be done incrementally.  It is currently one of
   2374  // the things that makes incremental reflow O(N^2).
   2375  auto overflowClipAxes = ShouldApplyOverflowClipping(aDisplay);
   2376  auto overflowClipMargin =
   2377      OverflowClipMargin(overflowClipAxes, /* aAllowNegative = */ false);
   2378  if (overflowClipAxes == kPhysicalAxesBoth && overflowClipMargin.IsAllZero()) {
   2379    return;
   2380  }
   2381 
   2382  // We rely here on our caller having called SetOverflowAreasToDesiredBounds().
   2383  const nsRect frameBounds = aOverflowAreas.ScrollableOverflow();
   2384 
   2385  const auto wm = GetWritingMode();
   2386  const auto borderPadding =
   2387      GetLogicalUsedBorderAndPadding(wm).GetPhysicalMargin(wm);
   2388  // Compute content-box by subtracting borderPadding off of frame rect.
   2389  // This gives us a reasonable starting-rect for the child-rect-unioning
   2390  // below, which we can then inflate by our padding (without needing to
   2391  // worry about having double-counted our padding or anything).
   2392  auto frameContentBounds = frameBounds;
   2393  frameContentBounds.Deflate(borderPadding);
   2394  // Margin rects (Zero-area rects included) of in-flow children (And floats,
   2395  // as discussed later) are unioned (starting with the scroller's own
   2396  // content-box), then inflated by the scroll container's padding...
   2397  auto inFlowChildBounds = frameContentBounds;
   2398  // ... While scrollable overflow rects contributed from further descendants
   2399  // (Regardless of if they're in-flow or out-of-flow) are unioned separately
   2400  // and their union does not get inflated by the scroll container's padding.
   2401  auto inFlowScrollableOverflow = frameContentBounds;
   2402 
   2403  for (const auto& line : Lines()) {
   2404    aOverflowAreas.InkOverflow() =
   2405        aOverflowAreas.InkOverflow().Union(line.InkOverflowRect());
   2406    if (aDisplay->IsContainLayout()) {
   2407      // If we have layout containment, we should only consider our child's
   2408      // ink overflow, leaving the scrollable regions of the parent
   2409      // unaffected.
   2410      // Note: Any overflow must be treated as ink overflow (As per
   2411      // https://drafts.csswg.org/css-contain/#containment-layout part 3).
   2412      // However, by unioning the children's ink overflow, we've already
   2413      // incorporated its scrollable overflow, since scrollable overflow
   2414      // is a subset of ink overflow.
   2415      continue;
   2416    }
   2417 
   2418    if (line.IsInline()) {
   2419      // This is the maximum contribution for inline line-participating frames -
   2420      // See `GetLineFrameInFlowBounds`.
   2421      inFlowChildBounds =
   2422          inFlowChildBounds.UnionEdges(line.GetPhysicalBounds());
   2423    }
   2424    auto lineInFlowChildBounds = line.GetInFlowChildBounds();
   2425    if (lineInFlowChildBounds) {
   2426      inFlowChildBounds = inFlowChildBounds.UnionEdges(*lineInFlowChildBounds);
   2427    }
   2428    inFlowScrollableOverflow =
   2429        inFlowScrollableOverflow.Union(line.ScrollableOverflowRect());
   2430  }
   2431 
   2432  if (Style()->GetPseudoType() == PseudoStyleType::scrolledContent) {
   2433    // Padding inflation only applies to scrolled containers.
   2434    const auto paddingInflatedOverflow =
   2435        ComputePaddingInflatedScrollableOverflow(inFlowChildBounds);
   2436    aOverflowAreas.UnionAllWith(paddingInflatedOverflow);
   2437  }
   2438  // Note: we're using UnionAllWith so as to maintain the invariant of
   2439  // ink overflow being a superset of scrollable overflow.
   2440  aOverflowAreas.UnionAllWith(inFlowScrollableOverflow);
   2441 
   2442  // Factor an outside ::marker in; normally the ::marker will be factored
   2443  // into the line-box's overflow areas. However, if the line is a block
   2444  // line then it won't; if there are no lines, it won't. So just
   2445  // factor it in anyway (it can't hurt if it was already done).
   2446  // XXXldb Can we just fix GetOverflowArea instead?
   2447  if (nsIFrame* outsideMarker = GetOutsideMarker()) {
   2448    aOverflowAreas.UnionAllWith(outsideMarker->GetRect());
   2449  }
   2450 
   2451  if (!overflowClipAxes.isEmpty()) {
   2452    aOverflowAreas.ApplyClipping(frameBounds, overflowClipAxes,
   2453                                 overflowClipMargin);
   2454  }
   2455 
   2456 #ifdef NOISY_OVERFLOW_AREAS
   2457  printf("%s: InkOverflowArea=%s, ScrollableOverflowArea=%s\n", ListTag().get(),
   2458         ToString(aOverflowAreas.InkOverflow()).c_str(),
   2459         ToString(aOverflowAreas.ScrollableOverflow()).c_str());
   2460 #endif
   2461 }
   2462 
   2463 // Depending on our ancestor, determine if we need to restrict padding inflation
   2464 // in inline direction. This assumes that the passed-in frame is a scrolled
   2465 // frame. HACK(dshin): Reaching out and querying the type like this isn't ideal.
   2466 static bool RestrictPaddingInflationInInline(const nsIFrame* aFrame) {
   2467  MOZ_ASSERT(aFrame);
   2468  if (aFrame->Style()->GetPseudoType() != PseudoStyleType::scrolledContent) {
   2469    // This can only happen when computing scrollable overflow for overflow:
   2470    // visible frames (for scroll{Width,Height}).
   2471    return false;
   2472  }
   2473  // If we're `input` or `textarea`, our grandparent element must be the text
   2474  // control element that we can query.
   2475  const auto* parent = aFrame->GetParent();
   2476  if (!parent) {
   2477    return false;
   2478  }
   2479  MOZ_ASSERT(parent->IsScrollContainerOrSubclass(), "Not a scrolled frame?");
   2480 
   2481  nsTextControlFrame* textControl = do_QueryFrame(parent->GetParent());
   2482  if (MOZ_LIKELY(!textControl)) {
   2483    return false;
   2484  }
   2485 
   2486  // We implement `textarea` as a special case of a div, but based on
   2487  // web-platform-tests, different rules apply for it - namely, no inline
   2488  // padding inflation. See
   2489  // `textarea-padding-iend-overlaps-content-001.tentative.html`.
   2490  if (!textControl->IsTextArea()) {
   2491    return false;
   2492  }
   2493  return true;
   2494 }
   2495 
   2496 nsRect nsBlockFrame::ComputePaddingInflatedScrollableOverflow(
   2497    const nsRect& aInFlowChildBounds) const {
   2498  auto result = aInFlowChildBounds;
   2499  const auto wm = GetWritingMode();
   2500  auto padding = GetLogicalUsedPadding(wm);
   2501  if (RestrictPaddingInflationInInline(this)) {
   2502    padding.IStart(wm) = padding.IEnd(wm) = 0;
   2503  }
   2504  result.Inflate(padding.GetPhysicalMargin(wm));
   2505  return result;
   2506 }
   2507 
   2508 Maybe<nsRect> nsBlockFrame::GetLineFrameInFlowBounds(
   2509    const nsLineBox& aLine, const nsIFrame& aLineChildFrame,
   2510    bool aConsiderPositiveMargins) const {
   2511  MOZ_ASSERT(aLineChildFrame.GetParent() == this,
   2512             "Line's frame doesn't belong to this block frame?");
   2513  // Line participants are considered in-flow for content within the line
   2514  // bounds, which should be accounted for from the line bounds. This is
   2515  // consistent with e.g. inline element's `margin-bottom` not affecting the
   2516  // placement of the next line.
   2517  if (aLineChildFrame.IsPlaceholderFrame() ||
   2518      aLineChildFrame.IsLineParticipant()) {
   2519    return Nothing{};
   2520  }
   2521  if (aLine.IsInline()) {
   2522    return Some(GetNormalMarginRect(aLineChildFrame, aConsiderPositiveMargins));
   2523  }
   2524  const auto wm = GetWritingMode();
   2525  auto rect = aLineChildFrame.GetRectRelativeToSelf();
   2526  // Special handling is required for boxes of zero block size, which carry
   2527  // out margin collapsing with themselves. We end up "rewinding" the line
   2528  // position after carrying out the block start margin. This is not reflected
   2529  // in the zero-sized frame's own frame-position.
   2530  const auto linePoint = aLine.GetPhysicalBounds().TopLeft();
   2531  const auto normalPosition = aLineChildFrame.GetLogicalSize(wm).BSize(wm) == 0
   2532                                  ? linePoint
   2533                                  : aLineChildFrame.GetNormalPosition();
   2534  // Ensure we use the margin we actually carried out.
   2535  nsMargin margin;
   2536  if (aConsiderPositiveMargins) {
   2537    auto logicalMargin = aLineChildFrame.GetLogicalUsedMargin(wm);
   2538    logicalMargin.BEnd(wm) = aLine.GetCarriedOutBEndMargin().Get();
   2539    margin = logicalMargin.GetPhysicalMargin(wm).ApplySkipSides(
   2540        aLineChildFrame.GetSkipSides());
   2541  } else {
   2542    margin = aLineChildFrame.GetUsedMargin().ApplySkipSides(
   2543        aLineChildFrame.GetSkipSides());
   2544    margin.EnsureAtMost(nsMargin());
   2545  }
   2546  rect.Inflate(margin);
   2547  return Some(rect + normalPosition);
   2548 }
   2549 
   2550 void nsBlockFrame::UnionChildOverflow(OverflowAreas& aOverflowAreas,
   2551                                      bool aAsIfScrolled) {
   2552  // We need to update the overflow areas of lines manually, as they
   2553  // get cached and re-used otherwise. Lines aren't exposed as normal
   2554  // frame children, so calling UnionChildOverflow alone will end up
   2555  // using the old cached values.
   2556  const auto wm = GetWritingMode();
   2557 
   2558  // ButtonControlFrame elements don't support scrolling, and some like
   2559  // comboboxes intentionally ignore padding to place their inner elements like
   2560  // the button, so to preserve behavior of stuff like the scroll{Width,Height}
   2561  // APIs, forcefully ignore aAsIfScrolled there.
   2562  aAsIfScrolled = aAsIfScrolled && !IsButtonControlFrame();
   2563 
   2564  // Overflow area computed here should agree with one computed in
   2565  // `ComputeOverflowAreas` (see bug 1800939 and bug 1800719). So the
   2566  // documentation in that function applies here as well.
   2567  const bool isScrolled = aAsIfScrolled || Style()->GetPseudoType() ==
   2568                                               PseudoStyleType::scrolledContent;
   2569  // Note that we don't add line in-flow margins if we're not a BFC (which can
   2570  // happen only for overflow: visible), so that we don't incorrectly account
   2571  // for margins that otherwise collapse through, see bug 1936156. Note that
   2572  // ::-moz-scrolled-content is always a BFC (see `AnonymousBoxIsBFC`).
   2573  const bool considerPositiveMarginsForInFlowChildBounds =
   2574      isScrolled && HasAnyStateBits(NS_BLOCK_BFC);
   2575 
   2576  // Relying on aOverflowAreas having been set to frame border rect (if
   2577  // aAsIfScrolled is false), or padding rect (if true).
   2578  auto frameContentBounds = aOverflowAreas.ScrollableOverflow();
   2579  frameContentBounds.Deflate((aAsIfScrolled
   2580                                  ? GetLogicalUsedPadding(wm)
   2581                                  : GetLogicalUsedBorderAndPadding(wm))
   2582                                 .GetPhysicalMargin(wm));
   2583  // We need to take in-flow children's margin rect into account, and inflate
   2584  // it by the padding.
   2585  auto inFlowChildBounds = frameContentBounds;
   2586  auto inFlowScrollableOverflow = frameContentBounds;
   2587 
   2588  const auto inkOverflowOnly =
   2589      !aAsIfScrolled && StyleDisplay()->IsContainLayout();
   2590 
   2591  for (auto& line : Lines()) {
   2592    nsRect bounds = line.GetPhysicalBounds();
   2593    OverflowAreas lineAreas(bounds, bounds);
   2594 
   2595    int32_t n = line.GetChildCount();
   2596    for (nsIFrame* lineFrame = line.mFirstChild; n > 0;
   2597         lineFrame = lineFrame->GetNextSibling(), --n) {
   2598      // Ensure this is called for each frame in the line
   2599      ConsiderChildOverflow(lineAreas, lineFrame, aAsIfScrolled);
   2600 
   2601      if (inkOverflowOnly || !isScrolled) {
   2602        continue;
   2603      }
   2604 
   2605      if (auto lineFrameBounds = GetLineFrameInFlowBounds(
   2606              line, *lineFrame, considerPositiveMarginsForInFlowChildBounds)) {
   2607        inFlowChildBounds = inFlowChildBounds.UnionEdges(*lineFrameBounds);
   2608      }
   2609    }
   2610 
   2611    // Consider the overflow areas of the floats attached to the line as well
   2612    if (line.HasFloats()) {
   2613      for (nsIFrame* f : line.Floats()) {
   2614        ConsiderChildOverflow(lineAreas, f, aAsIfScrolled);
   2615        if (inkOverflowOnly || !isScrolled) {
   2616          continue;
   2617        }
   2618        auto rect = GetNormalMarginRect(
   2619            *f, considerPositiveMarginsForInFlowChildBounds);
   2620        inFlowChildBounds = inFlowChildBounds.UnionEdges(rect);
   2621      }
   2622    }
   2623 
   2624    if (!aAsIfScrolled) {
   2625      line.SetOverflowAreas(lineAreas);
   2626    }
   2627    aOverflowAreas.InkOverflow() =
   2628        aOverflowAreas.InkOverflow().Union(lineAreas.InkOverflow());
   2629    if (!inkOverflowOnly) {
   2630      inFlowScrollableOverflow =
   2631          inFlowScrollableOverflow.Union(lineAreas.ScrollableOverflow());
   2632    }
   2633  }
   2634 
   2635  if (isScrolled) {
   2636    const auto paddingInflatedOverflow =
   2637        ComputePaddingInflatedScrollableOverflow(inFlowChildBounds);
   2638    aOverflowAreas.UnionAllWith(paddingInflatedOverflow);
   2639  }
   2640  aOverflowAreas.UnionAllWith(inFlowScrollableOverflow);
   2641 
   2642  // Union with child frames, skipping the principal and float lists
   2643  // since we already handled those using the line boxes.
   2644  nsLayoutUtils::UnionChildOverflow(
   2645      this, aOverflowAreas,
   2646      {FrameChildListID::Principal, FrameChildListID::Float});
   2647 }
   2648 
   2649 bool nsBlockFrame::ComputeCustomOverflow(OverflowAreas& aOverflowAreas) {
   2650  // Line cursor invariants depend on the overflow areas of the lines, so
   2651  // we must clear the line cursor since those areas may have changed.
   2652  ClearLineCursors();
   2653  return nsContainerFrame::ComputeCustomOverflow(aOverflowAreas);
   2654 }
   2655 
   2656 void nsBlockFrame::LazyMarkLinesDirty() {
   2657  if (HasAnyStateBits(NS_BLOCK_LOOK_FOR_DIRTY_FRAMES)) {
   2658    for (LineIterator line = LinesBegin(), line_end = LinesEnd();
   2659         line != line_end; ++line) {
   2660      int32_t n = line->GetChildCount();
   2661      for (nsIFrame* lineFrame = line->mFirstChild; n > 0;
   2662           lineFrame = lineFrame->GetNextSibling(), --n) {
   2663        if (lineFrame->IsSubtreeDirty()) {
   2664          // NOTE:  MarkLineDirty does more than just marking the line dirty.
   2665          MarkLineDirty(line, &mLines);
   2666          break;
   2667        }
   2668      }
   2669    }
   2670    RemoveStateBits(NS_BLOCK_LOOK_FOR_DIRTY_FRAMES);
   2671  }
   2672 }
   2673 
   2674 void nsBlockFrame::MarkLineDirty(LineIterator aLine,
   2675                                 const nsLineList* aLineList) {
   2676  // Mark aLine dirty
   2677  aLine->MarkDirty();
   2678  aLine->SetInvalidateTextRuns(true);
   2679 #ifdef DEBUG
   2680  if (gNoisyReflow) {
   2681    IndentBy(stdout, gNoiseIndent);
   2682    ListTag(stdout);
   2683    printf(": mark line %p dirty\n", static_cast<void*>(aLine.get()));
   2684  }
   2685 #endif
   2686 
   2687  // Mark previous line dirty if it's an inline line so that it can
   2688  // maybe pullup something from the line just affected.
   2689  // XXX We don't need to do this if aPrevLine ends in a break-after...
   2690  if (aLine != aLineList->front() && aLine->IsInline() &&
   2691      aLine.prev()->IsInline()) {
   2692    aLine.prev()->MarkDirty();
   2693    aLine.prev()->SetInvalidateTextRuns(true);
   2694 #ifdef DEBUG
   2695    if (gNoisyReflow) {
   2696      IndentBy(stdout, gNoiseIndent);
   2697      ListTag(stdout);
   2698      printf(": mark prev-line %p dirty\n",
   2699             static_cast<void*>(aLine.prev().get()));
   2700    }
   2701 #endif
   2702  }
   2703 }
   2704 
   2705 /**
   2706 * Test whether lines are certain to be aligned left so that we can make
   2707 * resizing optimizations
   2708 */
   2709 static inline bool IsAlignedLeft(StyleTextAlign aAlignment,
   2710                                 StyleDirection aDirection,
   2711                                 StyleUnicodeBidi aUnicodeBidi,
   2712                                 nsIFrame* aFrame) {
   2713  return aFrame->IsInSVGTextSubtree() || StyleTextAlign::Left == aAlignment ||
   2714         (((StyleTextAlign::Start == aAlignment &&
   2715            StyleDirection::Ltr == aDirection) ||
   2716           (StyleTextAlign::End == aAlignment &&
   2717            StyleDirection::Rtl == aDirection)) &&
   2718          aUnicodeBidi != StyleUnicodeBidi::Plaintext);
   2719 }
   2720 
   2721 void nsBlockFrame::PrepareResizeReflow(BlockReflowState& aState) {
   2722  // See if we can try and avoid marking all the lines as dirty
   2723  // FIXME(emilio): This should be writing-mode aware, I guess.
   2724  bool tryAndSkipLines =
   2725      // The left content-edge must be a constant distance from the left
   2726      // border-edge.
   2727      !StylePadding()->mPadding.Get(eSideLeft).HasPercent();
   2728 
   2729 #ifdef DEBUG
   2730  if (gDisableResizeOpt) {
   2731    tryAndSkipLines = false;
   2732  }
   2733  if (gNoisyReflow) {
   2734    if (!tryAndSkipLines) {
   2735      IndentBy(stdout, gNoiseIndent);
   2736      ListTag(stdout);
   2737      printf(": marking all lines dirty: availISize=%d\n",
   2738             aState.mReflowInput.AvailableISize());
   2739    }
   2740  }
   2741 #endif
   2742 
   2743  if (tryAndSkipLines) {
   2744    WritingMode wm = aState.mReflowInput.GetWritingMode();
   2745    nscoord newAvailISize =
   2746        aState.mReflowInput.ComputedLogicalBorderPadding(wm).IStart(wm) +
   2747        aState.mReflowInput.ComputedISize();
   2748 
   2749 #ifdef DEBUG
   2750    if (gNoisyReflow) {
   2751      IndentBy(stdout, gNoiseIndent);
   2752      ListTag(stdout);
   2753      printf(": trying to avoid marking all lines dirty\n");
   2754    }
   2755 #endif
   2756 
   2757    for (LineIterator line = LinesBegin(), line_end = LinesEnd();
   2758         line != line_end; ++line) {
   2759      // We let child blocks make their own decisions the same
   2760      // way we are here.
   2761      bool isLastLine = line == mLines.back() && !GetNextInFlow();
   2762      if (line->IsBlock() || line->HasFloats() ||
   2763          (!isLastLine && !line->HasForcedLineBreakAfter()) ||
   2764          ((isLastLine || !line->IsLineWrapped())) ||
   2765          line->ResizeReflowOptimizationDisabled() ||
   2766          line->IsImpactedByFloat() || (line->IEnd() > newAvailISize)) {
   2767        line->MarkDirty();
   2768      }
   2769 
   2770 #ifdef REALLY_NOISY_REFLOW
   2771      if (!line->IsBlock()) {
   2772        printf("PrepareResizeReflow thinks line %p is %simpacted by floats\n",
   2773               line.get(), line->IsImpactedByFloat() ? "" : "not ");
   2774      }
   2775 #endif
   2776 #ifdef DEBUG
   2777      if (gNoisyReflow && !line->IsDirty()) {
   2778        IndentBy(stdout, gNoiseIndent + 1);
   2779        printf(
   2780            "skipped: line=%p next=%p %s %s%s%s clearTypeBefore/After=%s/%s "
   2781            "xmost=%d\n",
   2782            static_cast<void*>(line.get()),
   2783            static_cast<void*>(
   2784                (line.next() != LinesEnd() ? line.next().get() : nullptr)),
   2785            line->IsBlock() ? "block" : "inline",
   2786            line->HasForcedLineBreakAfter() ? "has-break-after " : "",
   2787            line->HasFloats() ? "has-floats " : "",
   2788            line->IsImpactedByFloat() ? "impacted " : "",
   2789            line->UsedClearToString(line->FloatClearTypeBefore()),
   2790            line->UsedClearToString(line->FloatClearTypeAfter()), line->IEnd());
   2791      }
   2792 #endif
   2793    }
   2794  } else {
   2795    // Mark everything dirty
   2796    for (auto& line : Lines()) {
   2797      line.MarkDirty();
   2798    }
   2799  }
   2800 }
   2801 
   2802 //----------------------------------------
   2803 
   2804 /**
   2805 * Propagate reflow "damage" from from earlier lines to the current
   2806 * line.  The reflow damage comes from the following sources:
   2807 *  1. The regions of float damage remembered during reflow.
   2808 *  2. The combination of nonzero |aDeltaBCoord| and any impact by a
   2809 *     float, either the previous reflow or now.
   2810 *
   2811 * When entering this function, |aLine| is still at its old position and
   2812 * |aDeltaBCoord| indicates how much it will later be slid (assuming it
   2813 * doesn't get marked dirty and reflowed entirely).
   2814 */
   2815 void nsBlockFrame::PropagateFloatDamage(BlockReflowState& aState,
   2816                                        nsLineBox* aLine,
   2817                                        nscoord aDeltaBCoord) {
   2818  nsFloatManager* floatManager = aState.FloatManager();
   2819  NS_ASSERTION(
   2820      (aState.mReflowInput.mParentReflowInput &&
   2821       aState.mReflowInput.mParentReflowInput->mFloatManager == floatManager) ||
   2822          aState.mReflowInput.mBlockDelta == 0,
   2823      "Bad block delta passed in");
   2824 
   2825  // Check to see if there are any floats; if there aren't, there can't
   2826  // be any float damage
   2827  if (!floatManager->HasAnyFloats()) {
   2828    return;
   2829  }
   2830 
   2831  // Check the damage region recorded in the float damage.
   2832  if (floatManager->HasFloatDamage()) {
   2833    // Need to check mBounds *and* mCombinedArea to find intersections
   2834    // with aLine's floats
   2835    nscoord lineBCoordBefore = aLine->BStart() + aDeltaBCoord;
   2836    nscoord lineBCoordAfter = lineBCoordBefore + aLine->BSize();
   2837    // Scrollable overflow should be sufficient for things that affect
   2838    // layout.
   2839    WritingMode wm = aState.mReflowInput.GetWritingMode();
   2840    nsSize containerSize = aState.ContainerSize();
   2841    LogicalRect overflow =
   2842        aLine->GetOverflowArea(OverflowType::Scrollable, wm, containerSize);
   2843    nscoord lineBCoordCombinedBefore = overflow.BStart(wm) + aDeltaBCoord;
   2844    nscoord lineBCoordCombinedAfter =
   2845        lineBCoordCombinedBefore + overflow.BSize(wm);
   2846 
   2847    bool isDirty =
   2848        floatManager->IntersectsDamage(lineBCoordBefore, lineBCoordAfter) ||
   2849        floatManager->IntersectsDamage(lineBCoordCombinedBefore,
   2850                                       lineBCoordCombinedAfter);
   2851    if (isDirty) {
   2852      aLine->MarkDirty();
   2853      return;
   2854    }
   2855  }
   2856 
   2857  // Check if the line is moving relative to the float manager
   2858  if (aDeltaBCoord + aState.mReflowInput.mBlockDelta != 0) {
   2859    if (aLine->IsBlock()) {
   2860      // Unconditionally reflow sliding blocks; we only really need to reflow
   2861      // if there's a float impacting this block, but the current float manager
   2862      // makes it difficult to check that.  Therefore, we let the child block
   2863      // decide what it needs to reflow.
   2864      aLine->MarkDirty();
   2865    } else {
   2866      bool wasImpactedByFloat = aLine->IsImpactedByFloat();
   2867      nsFlowAreaRect floatAvailableSpace =
   2868          aState.GetFloatAvailableSpaceForBSize(
   2869              aState.mReflowInput.GetWritingMode(),
   2870              aLine->BStart() + aDeltaBCoord, aLine->BSize(), nullptr);
   2871 
   2872 #ifdef REALLY_NOISY_REFLOW
   2873      printf("nsBlockFrame::PropagateFloatDamage %p was = %d, is=%d\n", this,
   2874             wasImpactedByFloat, floatAvailableSpace.HasFloats());
   2875 #endif
   2876 
   2877      // Mark the line dirty if it was or is affected by a float
   2878      // We actually only really need to reflow if the amount of impact
   2879      // changes, but that's not straightforward to check
   2880      if (wasImpactedByFloat || floatAvailableSpace.HasFloats()) {
   2881        aLine->MarkDirty();
   2882      }
   2883    }
   2884  }
   2885 }
   2886 
   2887 static bool LineHasClear(nsLineBox* aLine) {
   2888  return aLine->IsBlock()
   2889             ? (aLine->HasFloatClearTypeBefore() ||
   2890                aLine->mFirstChild->HasAnyStateBits(
   2891                    NS_BLOCK_HAS_CLEAR_CHILDREN) ||
   2892                !nsBlockFrame::BlockCanIntersectFloats(aLine->mFirstChild))
   2893             : aLine->HasFloatClearTypeAfter();
   2894 }
   2895 
   2896 /**
   2897 * Reparent a whole list of floats from aOldParent to this block.  The
   2898 * floats might be taken from aOldParent's overflow list. They will be
   2899 * removed from the list. They end up appended to our floats list.
   2900 */
   2901 void nsBlockFrame::ReparentFloats(nsIFrame* aFirstFrame,
   2902                                  nsBlockFrame* aOldParent,
   2903                                  bool aReparentSiblings) {
   2904  nsFrameList list;
   2905  aOldParent->CollectFloats(aFirstFrame, list, aReparentSiblings);
   2906  if (list.NotEmpty()) {
   2907    for (nsIFrame* f : list) {
   2908      MOZ_ASSERT(!f->HasAnyStateBits(NS_FRAME_IS_PUSHED_OUT_OF_FLOW),
   2909                 "CollectFloats should've removed that bit");
   2910      ReparentFrame(f, aOldParent, this);
   2911    }
   2912    EnsureFloats()->AppendFrames(nullptr, std::move(list));
   2913  }
   2914 }
   2915 
   2916 static void DumpLine(const BlockReflowState& aState, nsLineBox* aLine,
   2917                     nscoord aDeltaBCoord, int32_t aDeltaIndent) {
   2918 #ifdef DEBUG
   2919  if (nsBlockFrame::gNoisyReflow) {
   2920    nsBlockFrame::IndentBy(stdout, nsBlockFrame::gNoiseIndent + aDeltaIndent);
   2921    fmt::println(
   2922        FMT_STRING("line={} mBCoord={} dirty={} bounds={} overflow-ink={} "
   2923                   "overflow-scr={} deltaBCoord={} mPrevBEndMargin={} "
   2924                   "childCount={}"),
   2925        static_cast<void*>(aLine), aState.mBCoord, YesOrNo(aLine->IsDirty()),
   2926        ToString(aLine->GetBounds()), ToString(aLine->InkOverflowRect()),
   2927        ToString(aLine->ScrollableOverflowRect()), aDeltaBCoord,
   2928        aState.mPrevBEndMargin.Get(), aLine->GetChildCount());
   2929  }
   2930 #endif
   2931 }
   2932 
   2933 bool nsBlockFrame::LinesAreEmpty() const {
   2934  for (const auto& line : mLines) {
   2935    if (!line.IsEmpty()) {
   2936      return false;
   2937    }
   2938  }
   2939  return true;
   2940 }
   2941 
   2942 bool nsBlockFrame::ReflowDirtyLines(BlockReflowState& aState) {
   2943  bool keepGoing = true;
   2944  bool foundAnyClears = aState.mTrailingClearFromPIF != UsedClear::None;
   2945  bool willReflowAgain = false;
   2946  bool usedOverflowWrap = false;
   2947 
   2948 #ifdef DEBUG
   2949  if (gNoisyReflow) {
   2950    IndentBy(stdout, gNoiseIndent);
   2951    ListTag(stdout);
   2952    printf(": reflowing dirty lines");
   2953    printf(" computedISize=%d\n", aState.mReflowInput.ComputedISize());
   2954  }
   2955  AutoNoisyIndenter indent(gNoisyReflow);
   2956 #endif
   2957 
   2958  bool selfDirty = HasAnyStateBits(NS_FRAME_IS_DIRTY) ||
   2959                   (aState.mReflowInput.IsBResize() &&
   2960                    HasAnyStateBits(NS_FRAME_CONTAINS_RELATIVE_BSIZE));
   2961 
   2962  // Reflow our last line if our availableBSize has increased
   2963  // so that we (and our last child) pull up content as necessary
   2964  if (aState.mReflowInput.AvailableBSize() != NS_UNCONSTRAINEDSIZE &&
   2965      GetNextInFlow() &&
   2966      aState.mReflowInput.AvailableBSize() >
   2967          GetLogicalSize().BSize(aState.mReflowInput.GetWritingMode())) {
   2968    LineIterator lastLine = LinesEnd();
   2969    if (lastLine != LinesBegin()) {
   2970      --lastLine;
   2971      lastLine->MarkDirty();
   2972    }
   2973  }
   2974  // the amount by which we will slide the current line if it is not
   2975  // dirty
   2976  nscoord deltaBCoord = 0;
   2977 
   2978  // whether we did NOT reflow the previous line and thus we need to
   2979  // recompute the carried out margin before the line if we want to
   2980  // reflow it or if its previous margin is dirty
   2981  bool needToRecoverState = false;
   2982  // Float continuations were reflowed in ReflowPushedFloats
   2983  bool reflowedFloat =
   2984      HasFloats() && GetFloats()->FirstChild()->HasAnyStateBits(
   2985                         NS_FRAME_IS_PUSHED_OUT_OF_FLOW);
   2986  bool lastLineMovedUp = false;
   2987  // We save up information about BR-clearance here
   2988  UsedClear inlineFloatClearType = aState.mTrailingClearFromPIF;
   2989 
   2990  LineIterator line = LinesBegin(), line_end = LinesEnd();
   2991 
   2992  // Determine if children of this frame could have breaks between them for
   2993  // page names.
   2994  //
   2995  // We need to check for paginated layout, the named-page pref, and if the
   2996  // available block-size is constrained.
   2997  //
   2998  // Note that we need to check for paginated layout as named-pages are only
   2999  // used during paginated reflow. We need to additionally check for
   3000  // unconstrained block-size to avoid introducing fragmentation breaks during
   3001  // "measuring" reflows within an overall paginated reflow, and to avoid
   3002  // fragmentation in monolithic containers like 'inline-block'.
   3003  //
   3004  // Because we can only break for named pages using Class A breakpoints, we
   3005  // also need to check that the block flow direction of the containing frame
   3006  // of these items (which is this block) is parallel to that of this page.
   3007  // See: https://www.w3.org/TR/css-break-3/#btw-blocks
   3008  const nsPresContext* const presCtx = aState.mPresContext;
   3009  const bool canBreakForPageNames =
   3010      aState.mReflowInput.mFlags.mCanHaveClassABreakpoints &&
   3011      aState.mReflowInput.AvailableBSize() != NS_UNCONSTRAINEDSIZE &&
   3012      presCtx->GetPresShell()->GetRootFrame()->GetWritingMode().IsVertical() ==
   3013          GetWritingMode().IsVertical();
   3014 
   3015  // ReflowInput.mFlags.mCanHaveClassABreakpoints should respect the named
   3016  // pages pref and presCtx->IsPaginated, so we did not explicitly check these
   3017  // above when setting canBreakForPageNames.
   3018  if (canBreakForPageNames) {
   3019    MOZ_ASSERT(presCtx->IsPaginated(),
   3020               "canBreakForPageNames should not be set during non-paginated "
   3021               "reflow");
   3022  }
   3023 
   3024  // Reflow the lines that are already ours
   3025  for (; line != line_end; ++line, aState.AdvanceToNextLine()) {
   3026    DumpLine(aState, line, deltaBCoord, 0);
   3027 #ifdef DEBUG
   3028    AutoNoisyIndenter indent2(gNoisyReflow);
   3029 #endif
   3030 
   3031    if (selfDirty) {
   3032      line->MarkDirty();
   3033    }
   3034 
   3035    // This really sucks, but we have to look inside any blocks that have clear
   3036    // elements inside them.
   3037    // XXX what can we do smarter here?
   3038    if (!line->IsDirty() && line->IsBlock() &&
   3039        line->mFirstChild->HasAnyStateBits(NS_BLOCK_HAS_CLEAR_CHILDREN) &&
   3040        aState.FloatManager()->HasAnyFloats()) {
   3041      line->MarkDirty();
   3042    }
   3043 
   3044    nsIFrame* floatAvoidingBlock = nullptr;
   3045    if (line->IsBlock() &&
   3046        !nsBlockFrame::BlockCanIntersectFloats(line->mFirstChild)) {
   3047      floatAvoidingBlock = line->mFirstChild;
   3048    }
   3049 
   3050    // We have to reflow the line if it's a block whose clearance
   3051    // might have changed, so detect that.
   3052    if (!line->IsDirty() &&
   3053        (line->HasFloatClearTypeBefore() || floatAvoidingBlock)) {
   3054      nscoord curBCoord = aState.mBCoord;
   3055      // See where we would be after applying any clearance due to
   3056      // BRs.
   3057      if (inlineFloatClearType != UsedClear::None) {
   3058        std::tie(curBCoord, std::ignore) =
   3059            aState.ClearFloats(curBCoord, inlineFloatClearType);
   3060      }
   3061 
   3062      auto [newBCoord, result] = aState.ClearFloats(
   3063          curBCoord, line->FloatClearTypeBefore(), floatAvoidingBlock);
   3064 
   3065      if (line->HasClearance()) {
   3066        // Reflow the line if it might not have clearance anymore.
   3067        if (result == ClearFloatsResult::BCoordNoChange
   3068            // aState.mBCoord is the clearance point which should be the
   3069            // block-start border-edge of the block frame. If sliding the
   3070            // block by deltaBCoord isn't going to put it in the predicted
   3071            // position, then we'd better reflow the line.
   3072            || newBCoord != line->BStart() + deltaBCoord) {
   3073          line->MarkDirty();
   3074        }
   3075      } else {
   3076        // Reflow the line if the line might have clearance now.
   3077        if (result != ClearFloatsResult::BCoordNoChange) {
   3078          line->MarkDirty();
   3079        }
   3080      }
   3081    }
   3082 
   3083    // We might have to reflow a line that is after a clearing BR.
   3084    if (inlineFloatClearType != UsedClear::None) {
   3085      std::tie(aState.mBCoord, std::ignore) =
   3086          aState.ClearFloats(aState.mBCoord, inlineFloatClearType);
   3087      if (aState.mBCoord != line->BStart() + deltaBCoord) {
   3088        // SlideLine is not going to put the line where the clearance
   3089        // put it. Reflow the line to be sure.
   3090        line->MarkDirty();
   3091      }
   3092      inlineFloatClearType = UsedClear::None;
   3093    }
   3094 
   3095    bool previousMarginWasDirty = line->IsPreviousMarginDirty();
   3096    if (previousMarginWasDirty) {
   3097      // If the previous margin is dirty, reflow the current line
   3098      line->MarkDirty();
   3099      line->ClearPreviousMarginDirty();
   3100    } else if (aState.ContentBSize() != NS_UNCONSTRAINEDSIZE) {
   3101      const nscoord scrollableOverflowBEnd =
   3102          LogicalRect(line->mWritingMode, line->ScrollableOverflowRect(),
   3103                      line->mContainerSize)
   3104              .BEnd(line->mWritingMode);
   3105      if (scrollableOverflowBEnd + deltaBCoord > aState.ContentBEnd()) {
   3106        // Lines that aren't dirty but get slid past our available block-size
   3107        // constraint must be reflowed.
   3108        line->MarkDirty();
   3109      }
   3110    }
   3111 
   3112    if (!line->IsDirty()) {
   3113      const bool isPaginated =
   3114          // Last column can be reflowed unconstrained during column balancing.
   3115          // Hence the additional NS_FRAME_HAS_MULTI_COLUMN_ANCESTOR bit check
   3116          // as a fail-safe fallback.
   3117          aState.mReflowInput.AvailableBSize() != NS_UNCONSTRAINEDSIZE ||
   3118          HasAnyStateBits(NS_FRAME_HAS_MULTI_COLUMN_ANCESTOR) ||
   3119          // Table can also be reflowed unconstrained during printing.
   3120          aState.mPresContext->IsPaginated();
   3121      if (isPaginated) {
   3122        // We are in a paginated context, i.e. in columns or pages.
   3123        const bool mayContainFloats =
   3124            line->IsBlock() || line->HasFloats() || line->HadFloatPushed();
   3125        if (mayContainFloats) {
   3126          // The following if-else conditions check whether this line -- which
   3127          // might have floats in its subtree, or has floats as direct children,
   3128          // or had floats pushed -- needs to be reflowed.
   3129          if (deltaBCoord != 0 || aState.mReflowInput.IsBResize()) {
   3130            // The distance to the block-end edge might have changed. Reflow the
   3131            // line both because the breakpoints within its floats may have
   3132            // changed and because we might have to push/pull the floats in
   3133            // their entirety.
   3134            line->MarkDirty();
   3135          } else if (HasPushedFloats()) {
   3136            // We had pushed floats which haven't been drained by our
   3137            // next-in-flow, which means our parent is currently reflowing us
   3138            // again due to clearance without creating a next-in-flow for us.
   3139            // Reflow the line to redo the floats split logic to correctly set
   3140            // our reflow status.
   3141            line->MarkDirty();
   3142          } else if (aState.mReflowInput.mFlags.mMustReflowPlaceholders) {
   3143            // Reflow the line (that may containing a float's placeholder frame)
   3144            // if our parent tells us to do so.
   3145            line->MarkDirty();
   3146          } else if (aState.mReflowInput.mFlags.mMovedBlockFragments) {
   3147            // Our parent's line containing us moved to a different fragment.
   3148            // Reflow the line because the decision about whether the float fits
   3149            // may be different in a different fragment.
   3150            line->MarkDirty();
   3151          }
   3152        }
   3153      }
   3154    }
   3155 
   3156    if (!line->IsDirty()) {
   3157      // See if there's any reflow damage that requires that we mark the
   3158      // line dirty.
   3159      PropagateFloatDamage(aState, line, deltaBCoord);
   3160    }
   3161 
   3162    // If the container size has changed, reset mContainerSize. If the
   3163    // line's writing mode is not ltr, or if the line is not left-aligned, also
   3164    // mark the line dirty.
   3165    if (aState.ContainerSize() != line->mContainerSize) {
   3166      line->mContainerSize = aState.ContainerSize();
   3167 
   3168      const bool isLastLine = line == mLines.back() && !GetNextInFlow();
   3169      const auto align = isLastLine ? StyleText()->TextAlignForLastLine()
   3170                                    : StyleText()->mTextAlign;
   3171      if (line->mWritingMode.IsVertical() || line->mWritingMode.IsBidiRTL() ||
   3172          !IsAlignedLeft(align, StyleVisibility()->mDirection,
   3173                         StyleTextReset()->mUnicodeBidi, this)) {
   3174        line->MarkDirty();
   3175      }
   3176    }
   3177 
   3178    // Check for a page break caused by CSS named pages.
   3179    //
   3180    // We should break for named pages when two frames meet at a class A
   3181    // breakpoint, where the first frame has a different end page value to the
   3182    // second frame's start page value. canBreakForPageNames is true iff
   3183    // children of this frame can form class A breakpoints, and that we are not
   3184    // in a measurement reflow or in a monolithic container such as
   3185    // 'inline-block'.
   3186    //
   3187    // We specifically do not want to cause a page-break for named pages when
   3188    // we are at the top of a page. This would otherwise happen when the
   3189    // previous sibling is an nsPageBreakFrame, or all previous siblings on the
   3190    // current page are zero-height. The latter may not be per-spec, but is
   3191    // compatible with Chrome's implementation of named pages.
   3192    const nsAtom* nextPageName = nullptr;
   3193    bool shouldBreakForPageName = false;
   3194    if (canBreakForPageNames && (!aState.mReflowInput.mFlags.mIsTopOfPage ||
   3195                                 !aState.IsAdjacentWithBStart())) {
   3196      const nsIFrame* const frame = line->mFirstChild;
   3197      if (!frame->IsPlaceholderFrame() && !frame->IsPageBreakFrame()) {
   3198        nextPageName = frame->GetStartPageValue();
   3199        // Walk back to the last frame that isn't a placeholder.
   3200        const nsIFrame* prevFrame = frame->GetPrevSibling();
   3201        while (prevFrame && prevFrame->IsPlaceholderFrame()) {
   3202          prevFrame = prevFrame->GetPrevSibling();
   3203        }
   3204        if (prevFrame && prevFrame->GetEndPageValue() != nextPageName) {
   3205          shouldBreakForPageName = true;
   3206          line->MarkDirty();
   3207        }
   3208      }
   3209    }
   3210 
   3211    if (needToRecoverState && line->IsDirty()) {
   3212      // We need to reconstruct the block-end margin only if we didn't
   3213      // reflow the previous line and we do need to reflow (or repair
   3214      // the block-start position of) the next line.
   3215      aState.ReconstructMarginBefore(line);
   3216    }
   3217 
   3218    bool reflowedPrevLine = !needToRecoverState;
   3219    if (needToRecoverState) {
   3220      needToRecoverState = false;
   3221 
   3222      // Update aState.mPrevChild as if we had reflowed all of the frames in
   3223      // this line.
   3224      if (line->IsDirty()) {
   3225        NS_ASSERTION(
   3226            line->mFirstChild->GetPrevSibling() == line.prev()->LastChild(),
   3227            "unexpected line frames");
   3228        aState.mPrevChild = line->mFirstChild->GetPrevSibling();
   3229      }
   3230    }
   3231 
   3232    // Now repair the line and update |aState.mBCoord| by calling
   3233    // |ReflowLine| or |SlideLine|.
   3234    // If we're going to reflow everything again, then no need to reflow
   3235    // the dirty line ... unless the line has floats, in which case we'd
   3236    // better reflow it now to refresh its float cache, which may contain
   3237    // dangling frame pointers! Ugh! This reflow of the line may be
   3238    // incorrect because we skipped reflowing previous lines (e.g., floats
   3239    // may be placed incorrectly), but that's OK because we'll mark the
   3240    // line dirty below under "if (aState.mReflowInput.mDiscoveredClearance..."
   3241    if (line->IsDirty() && (line->HasFloats() || !willReflowAgain)) {
   3242      lastLineMovedUp = true;
   3243 
   3244      bool maybeReflowingForFirstTime =
   3245          line->IStart() == 0 && line->BStart() == 0 && line->ISize() == 0 &&
   3246          line->BSize() == 0;
   3247 
   3248      // Compute the dirty lines "before" BEnd, after factoring in
   3249      // the running deltaBCoord value - the running value is implicit in
   3250      // aState.mBCoord.
   3251      nscoord oldB = line->BStart();
   3252      nscoord oldBMost = line->BEnd();
   3253 
   3254      NS_ASSERTION(!willReflowAgain || !line->IsBlock(),
   3255                   "Don't reflow blocks while willReflowAgain is true, reflow "
   3256                   "of block abs-pos children depends on this");
   3257 
   3258      if (shouldBreakForPageName) {
   3259        // Immediately fragment for page-name. It is possible we could break
   3260        // out of the loop right here, but this should make it more similar to
   3261        // what happens when reflow causes fragmentation.
   3262        // Set the page name, so that PushTruncatedLine does not need to
   3263        // recalculate the new page name.
   3264        PresShell()->FrameConstructor()->SetNextPageContentFramePageName(
   3265            nextPageName ? nextPageName : GetAutoPageValue());
   3266        PushTruncatedLine(aState, line, &keepGoing,
   3267                          ComputeNewPageNameIfNeeded::No);
   3268      } else {
   3269        // Reflow the dirty line. If it's an incremental reflow, then force
   3270        // it to invalidate the dirty area if necessary
   3271        usedOverflowWrap |= ReflowLine(aState, line, &keepGoing);
   3272      }
   3273 
   3274      if (aState.mReflowInput.WillReflowAgainForClearance()) {
   3275        line->MarkDirty();
   3276        willReflowAgain = true;
   3277        // Note that once we've entered this state, every line that gets here
   3278        // (e.g. because it has floats) gets marked dirty and reflowed again.
   3279        // in the next pass. This is important, see above.
   3280      }
   3281 
   3282      if (line->HasFloats()) {
   3283        reflowedFloat = true;
   3284      }
   3285 
   3286      if (!keepGoing) {
   3287        DumpLine(aState, line, deltaBCoord, -1);
   3288        if (0 == line->GetChildCount()) {
   3289          DeleteLine(aState, line, line_end);
   3290        }
   3291        break;
   3292      }
   3293 
   3294      // Test to see whether the margin that should be carried out
   3295      // to the next line (NL) might have changed. In ReflowBlockFrame
   3296      // we call nextLine->MarkPreviousMarginDirty if the block's
   3297      // actual carried-out block-end margin changed. So here we only
   3298      // need to worry about the following effects:
   3299      // 1) the line was just created, and it might now be blocking
   3300      // a carried-out block-end margin from previous lines that
   3301      // used to reach NL from reaching NL
   3302      // 2) the line used to be empty, and is now not empty,
   3303      // thus blocking a carried-out block-end margin from previous lines
   3304      // that used to reach NL from reaching NL
   3305      // 3) the line wasn't empty, but now is, so a carried-out
   3306      // block-end margin from previous lines that didn't used to reach NL
   3307      // now does
   3308      // 4) the line might have changed in a way that affects NL's
   3309      // ShouldApplyBStartMargin decision. The three things that matter
   3310      // are the line's emptiness, its adjacency to the block-start edge of the
   3311      // block, and whether it has clearance (the latter only matters if the
   3312      // block was and is adjacent to the block-start and empty).
   3313      //
   3314      // If the line is empty now, we can't reliably tell if the line was empty
   3315      // before, so we just assume it was and do
   3316      // nextLine->MarkPreviousMarginDirty. This means the checks in 4) are
   3317      // redundant; if the line is empty now we don't need to check 4), but if
   3318      // the line is not empty now and we're sure it wasn't empty before, any
   3319      // adjacency and clearance changes are irrelevant to the result of
   3320      // nextLine->ShouldApplyBStartMargin.
   3321      if (line.next() != LinesEnd()) {
   3322        bool maybeWasEmpty = oldB == line.next()->BStart();
   3323        bool isEmpty = line->CachedIsEmpty();
   3324        if (maybeReflowingForFirstTime /*1*/ ||
   3325            (isEmpty || maybeWasEmpty) /*2/3/4*/) {
   3326          line.next()->MarkPreviousMarginDirty();
   3327          // since it's marked dirty, nobody will care about |deltaBCoord|
   3328        }
   3329      }
   3330 
   3331      // If the line was just reflowed for the first time, then its
   3332      // old mBounds cannot be trusted so this deltaBCoord computation is
   3333      // bogus. But that's OK because we just did
   3334      // MarkPreviousMarginDirty on the next line which will force it
   3335      // to be reflowed, so this computation of deltaBCoord will not be
   3336      // used.
   3337      deltaBCoord = line->BEnd() - oldBMost;
   3338 
   3339      // Now do an interrupt check. We want to do this only in the case when we
   3340      // actually reflow the line, so that if we get back in here we'll get
   3341      // further on the reflow before interrupting.
   3342      aState.mPresContext->CheckForInterrupt(this);
   3343    } else {
   3344      aState.mOverflowTracker->Skip(line->mFirstChild, aState.mReflowStatus);
   3345      // Nop except for blocks (we don't create overflow container
   3346      // continuations for any inlines atm), so only checking mFirstChild
   3347      // is enough
   3348 
   3349      lastLineMovedUp = deltaBCoord < 0;
   3350 
   3351      if (deltaBCoord != 0) {
   3352        SlideLine(aState, line, deltaBCoord);
   3353      }
   3354 
   3355      NS_ASSERTION(!line->IsDirty() || !line->HasFloats(),
   3356                   "Possibly stale float cache here!");
   3357      if (willReflowAgain && line->IsBlock()) {
   3358        // If we're going to reflow everything again, and this line is a block,
   3359        // then there is no need to recover float state. The line may contain
   3360        // other lines with floats, but in that case RecoverStateFrom would only
   3361        // add floats to the float manager. We don't need to do that because
   3362        // everything's going to get reflowed again "for real". Calling
   3363        // RecoverStateFrom in this situation could be lethal because the
   3364        // block's descendant lines may have float caches containing dangling
   3365        // frame pointers. Ugh!
   3366        // If this line is inline, then we need to recover its state now
   3367        // to make sure that we don't forget to move its floats by deltaBCoord.
   3368      } else {
   3369        // XXX EVIL O(N^2) EVIL
   3370        aState.RecoverStateFrom(line, deltaBCoord);
   3371      }
   3372 
   3373      // Keep mBCoord up to date in case we're propagating reflow damage
   3374      // and also because our final height may depend on it. If the
   3375      // line is inlines, then only update mBCoord if the line is not
   3376      // empty, because that's what PlaceLine does. (Empty blocks may
   3377      // want to update mBCoord, e.g. if they have clearance.)
   3378      if (line->IsBlock() || !line->CachedIsEmpty()) {
   3379        aState.mBCoord = line->BEnd();
   3380      }
   3381 
   3382      needToRecoverState = true;
   3383 
   3384      if (reflowedPrevLine && !line->IsBlock() &&
   3385          aState.mPresContext->HasPendingInterrupt()) {
   3386        // Need to make sure to pull overflows from any prev-in-flows
   3387        for (nsIFrame* inlineKid = line->mFirstChild; inlineKid;
   3388             inlineKid = inlineKid->PrincipalChildList().FirstChild()) {
   3389          inlineKid->PullOverflowsFromPrevInFlow();
   3390        }
   3391      }
   3392    }
   3393 
   3394    // Record if we need to clear floats before reflowing the next
   3395    // line. Note that inlineFloatClearType will be handled and
   3396    // cleared before the next line is processed, so there is no
   3397    // need to combine break types here.
   3398    if (line->HasFloatClearTypeAfter()) {
   3399      inlineFloatClearType = line->FloatClearTypeAfter();
   3400    }
   3401 
   3402    if (LineHasClear(line.get())) {
   3403      foundAnyClears = true;
   3404    }
   3405 
   3406    DumpLine(aState, line, deltaBCoord, -1);
   3407 
   3408    if (aState.mPresContext->HasPendingInterrupt()) {
   3409      willReflowAgain = true;
   3410      // Another option here might be to leave |line| clean if
   3411      // !HasPendingInterrupt() before the CheckForInterrupt() call, since in
   3412      // that case the line really did reflow as it should have.  Not sure
   3413      // whether that would be safe, so doing this for now instead.  Also not
   3414      // sure whether we really want to mark all lines dirty after an
   3415      // interrupt, but until we get better at propagating float damage we
   3416      // really do need to do it this way; see comments inside MarkLineDirty.
   3417      MarkLineDirtyForInterrupt(line);
   3418    }
   3419  }
   3420 
   3421  // Handle BR-clearance from the last line of the block
   3422  if (inlineFloatClearType != UsedClear::None) {
   3423    std::tie(aState.mBCoord, std::ignore) =
   3424        aState.ClearFloats(aState.mBCoord, inlineFloatClearType);
   3425  }
   3426 
   3427  if (needToRecoverState) {
   3428    // Is this expensive?
   3429    aState.ReconstructMarginBefore(line);
   3430 
   3431    // Update aState.mPrevChild as if we had reflowed all of the frames in
   3432    // the last line.
   3433    NS_ASSERTION(line == line_end || line->mFirstChild->GetPrevSibling() ==
   3434                                         line.prev()->LastChild(),
   3435                 "unexpected line frames");
   3436    aState.mPrevChild = line == line_end ? mFrames.LastChild()
   3437                                         : line->mFirstChild->GetPrevSibling();
   3438  }
   3439 
   3440  // We can skip trying to pull up the next line if our height is constrained
   3441  // (so we can report being incomplete) and there is no next in flow or we
   3442  // were told not to or we know it will be futile, i.e.,
   3443  // -- the next in flow is not changing
   3444  // -- and we cannot have added more space for its first line to be
   3445  // pulled up into,
   3446  // -- it's an incremental reflow of a descendant
   3447  // -- and we didn't reflow any floats (so the available space
   3448  // didn't change)
   3449  // -- my chain of next-in-flows either has no first line, or its first
   3450  // line isn't dirty.
   3451  bool heightConstrained =
   3452      aState.mReflowInput.AvailableBSize() != NS_UNCONSTRAINEDSIZE;
   3453  bool skipPull = willReflowAgain && heightConstrained;
   3454  if (!skipPull && heightConstrained && aState.mNextInFlow &&
   3455      (aState.mReflowInput.mFlags.mNextInFlowUntouched && !lastLineMovedUp &&
   3456       !HasAnyStateBits(NS_FRAME_IS_DIRTY) && !reflowedFloat)) {
   3457    // We'll place lineIter at the last line of this block, so that
   3458    // nsBlockInFlowLineIterator::Next() will take us to the first
   3459    // line of my next-in-flow-chain.  (But first, check that I
   3460    // have any lines -- if I don't, just bail out of this
   3461    // optimization.)
   3462    LineIterator lineIter = this->LinesEnd();
   3463    if (lineIter != this->LinesBegin()) {
   3464      lineIter--;  // I have lines; step back from dummy iterator to last line.
   3465      nsBlockInFlowLineIterator bifLineIter(this, lineIter);
   3466 
   3467      // Check for next-in-flow-chain's first line.
   3468      // (First, see if there is such a line, and second, see if it's clean)
   3469      if (!bifLineIter.Next() || !bifLineIter.GetLine()->IsDirty()) {
   3470        skipPull = true;
   3471      }
   3472    }
   3473  }
   3474 
   3475  if (skipPull && aState.mNextInFlow) {
   3476    NS_ASSERTION(heightConstrained, "Height should be constrained here\n");
   3477    if (aState.mNextInFlow->IsTrueOverflowContainer()) {
   3478      aState.mReflowStatus.SetOverflowIncomplete();
   3479    } else {
   3480      aState.mReflowStatus.SetIncomplete();
   3481    }
   3482  }
   3483 
   3484  if (!skipPull && aState.mNextInFlow) {
   3485    // Pull data from a next-in-flow if there's still room for more
   3486    // content here.
   3487    while (keepGoing && aState.mNextInFlow) {
   3488      // Grab first line from our next-in-flow
   3489      nsBlockFrame* nextInFlow = aState.mNextInFlow;
   3490      nsLineBox* pulledLine;
   3491      nsFrameList pulledFrames;
   3492      if (!nextInFlow->mLines.empty()) {
   3493        RemoveFirstLine(nextInFlow->mLines, nextInFlow->mFrames, &pulledLine,
   3494                        &pulledFrames);
   3495        ClearLineCursors();
   3496      } else {
   3497        // Grab an overflow line if there are any
   3498        FrameLines* overflowLines = nextInFlow->GetOverflowLines();
   3499        if (!overflowLines) {
   3500          aState.mNextInFlow =
   3501              static_cast<nsBlockFrame*>(nextInFlow->GetNextInFlow());
   3502          continue;
   3503        }
   3504        bool last =
   3505            RemoveFirstLine(overflowLines->mLines, overflowLines->mFrames,
   3506                            &pulledLine, &pulledFrames);
   3507        if (last) {
   3508          nextInFlow->DestroyOverflowLines();
   3509        }
   3510      }
   3511 
   3512      if (pulledFrames.IsEmpty()) {
   3513        // The line is empty. Try the next one.
   3514        NS_ASSERTION(
   3515            pulledLine->GetChildCount() == 0 && !pulledLine->mFirstChild,
   3516            "bad empty line");
   3517        nextInFlow->FreeLineBox(pulledLine);
   3518        continue;
   3519      }
   3520 
   3521      if (nextInFlow->MaybeHasLineCursor()) {
   3522        if (pulledLine == nextInFlow->GetLineCursorForDisplay()) {
   3523          nextInFlow->ClearLineCursorForDisplay();
   3524        }
   3525        if (pulledLine == nextInFlow->GetLineCursorForQuery()) {
   3526          nextInFlow->ClearLineCursorForQuery();
   3527        }
   3528      }
   3529      ReparentFrames(pulledFrames, nextInFlow, this);
   3530      pulledLine->SetMovedFragments();
   3531 
   3532      NS_ASSERTION(pulledFrames.LastChild() == pulledLine->LastChild(),
   3533                   "Unexpected last frame");
   3534      NS_ASSERTION(aState.mPrevChild || mLines.empty(),
   3535                   "should have a prevchild here");
   3536      NS_ASSERTION(aState.mPrevChild == mFrames.LastChild(),
   3537                   "Incorrect aState.mPrevChild before inserting line at end");
   3538 
   3539      // Shift pulledLine's frames into our mFrames list.
   3540      mFrames.AppendFrames(nullptr, std::move(pulledFrames));
   3541 
   3542      // Add line to our line list, and set its last child as our new prev-child
   3543      line = mLines.before_insert(LinesEnd(), pulledLine);
   3544      aState.mPrevChild = mFrames.LastChild();
   3545 
   3546      // Reparent floats whose placeholders are in the line.
   3547      ReparentFloats(pulledLine->mFirstChild, nextInFlow, true);
   3548 
   3549      DumpLine(aState, pulledLine, deltaBCoord, 0);
   3550 #ifdef DEBUG
   3551      AutoNoisyIndenter indent2(gNoisyReflow);
   3552 #endif
   3553 
   3554      if (aState.mPresContext->HasPendingInterrupt()) {
   3555        MarkLineDirtyForInterrupt(line);
   3556      } else {
   3557        // Now reflow it and any lines that it makes during it's reflow
   3558        // (we have to loop here because reflowing the line may cause a new
   3559        // line to be created; see SplitLine's callers for examples of
   3560        // when this happens).
   3561        while (line != LinesEnd()) {
   3562          usedOverflowWrap |= ReflowLine(aState, line, &keepGoing);
   3563 
   3564          if (aState.mReflowInput.WillReflowAgainForClearance()) {
   3565            line->MarkDirty();
   3566            keepGoing = false;
   3567            aState.mReflowStatus.SetIncomplete();
   3568            break;
   3569          }
   3570 
   3571          DumpLine(aState, line, deltaBCoord, -1);
   3572          if (!keepGoing) {
   3573            if (0 == line->GetChildCount()) {
   3574              DeleteLine(aState, line, line_end);
   3575            }
   3576            break;
   3577          }
   3578 
   3579          if (LineHasClear(line.get())) {
   3580            foundAnyClears = true;
   3581          }
   3582 
   3583          if (aState.mPresContext->CheckForInterrupt(this)) {
   3584            MarkLineDirtyForInterrupt(line);
   3585            break;
   3586          }
   3587 
   3588          // If this is an inline frame then its time to stop
   3589          ++line;
   3590          aState.AdvanceToNextLine();
   3591        }
   3592      }
   3593    }
   3594 
   3595    if (aState.mReflowStatus.IsIncomplete()) {
   3596      aState.mReflowStatus.SetNextInFlowNeedsReflow();
   3597    }  // XXXfr shouldn't set this flag when nextinflow has no lines
   3598  }
   3599 
   3600  // Handle an odd-ball case: a list-item with no lines
   3601  nsIFrame* outsideMarker = GetOutsideMarker();
   3602  if (outsideMarker && mLines.empty()) {
   3603    ReflowOutput metrics(aState.mReflowInput);
   3604    WritingMode wm = aState.mReflowInput.GetWritingMode();
   3605    ReflowOutsideMarker(
   3606        outsideMarker, aState, metrics,
   3607        aState.mReflowInput.ComputedPhysicalBorderPadding().top);
   3608    NS_ASSERTION(!MarkerIsEmpty(outsideMarker) || metrics.BSize(wm) == 0,
   3609                 "empty ::marker frame took up space");
   3610 
   3611    if (!MarkerIsEmpty(outsideMarker)) {
   3612      // There are no lines so we have to fake up some y motion so that
   3613      // we end up with *some* height.
   3614      // (Note: if we're layout-contained, we have to be sure to leave our
   3615      // ReflowOutput's BlockStartAscent() (i.e. the baseline) untouched,
   3616      // because layout-contained frames have no baseline.)
   3617      if (!aState.mReflowInput.mStyleDisplay->IsContainLayout() &&
   3618          metrics.BlockStartAscent() == ReflowOutput::ASK_FOR_BASELINE) {
   3619        nscoord ascent;
   3620        WritingMode wm = aState.mReflowInput.GetWritingMode();
   3621        if (nsLayoutUtils::GetFirstLineBaseline(wm, outsideMarker, &ascent)) {
   3622          metrics.SetBlockStartAscent(ascent);
   3623        } else {
   3624          metrics.SetBlockStartAscent(metrics.BSize(wm));
   3625        }
   3626      }
   3627 
   3628      RefPtr<nsFontMetrics> fm =
   3629          nsLayoutUtils::GetInflatedFontMetricsForFrame(this);
   3630 
   3631      nscoord minAscent = nsLayoutUtils::GetCenteredFontBaseline(
   3632          fm, aState.mMinLineHeight, wm.IsLineInverted());
   3633      nscoord minDescent = aState.mMinLineHeight - minAscent;
   3634 
   3635      aState.mBCoord +=
   3636          std::max(minAscent, metrics.BlockStartAscent()) +
   3637          std::max(minDescent, metrics.BSize(wm) - metrics.BlockStartAscent());
   3638 
   3639      nscoord offset = minAscent - metrics.BlockStartAscent();
   3640      if (offset > 0) {
   3641        outsideMarker->SetRect(outsideMarker->GetRect() + nsPoint(0, offset));
   3642      }
   3643    }
   3644  }
   3645 
   3646  if (LinesAreEmpty() && ShouldHaveLineIfEmpty()) {
   3647    aState.mBCoord += aState.mMinLineHeight;
   3648  }
   3649 
   3650  if (foundAnyClears) {
   3651    AddStateBits(NS_BLOCK_HAS_CLEAR_CHILDREN);
   3652  } else {
   3653    RemoveStateBits(NS_BLOCK_HAS_CLEAR_CHILDREN);
   3654  }
   3655 
   3656 #ifdef DEBUG
   3657  VerifyLines(true);
   3658  VerifyOverflowSituation();
   3659  if (gNoisyReflow) {
   3660    IndentBy(stdout, gNoiseIndent - 1);
   3661    ListTag(stdout);
   3662    printf(": done reflowing dirty lines (status=%s)\n",
   3663           ToString(aState.mReflowStatus).c_str());
   3664  }
   3665 #endif
   3666 
   3667  return usedOverflowWrap;
   3668 }
   3669 
   3670 void nsBlockFrame::MarkLineDirtyForInterrupt(nsLineBox* aLine) {
   3671  aLine->MarkDirty();
   3672 
   3673  // Just checking NS_FRAME_IS_DIRTY is ok, because we've already
   3674  // marked the lines that need to be marked dirty based on our
   3675  // vertical resize stuff.  So we'll definitely reflow all those kids;
   3676  // the only question is how they should behave.
   3677  if (HasAnyStateBits(NS_FRAME_IS_DIRTY)) {
   3678    // Mark all our child frames dirty so we make sure to reflow them
   3679    // later.
   3680    int32_t n = aLine->GetChildCount();
   3681    for (nsIFrame* f = aLine->mFirstChild; n > 0;
   3682         f = f->GetNextSibling(), --n) {
   3683      f->MarkSubtreeDirty();
   3684    }
   3685    // And mark all the floats whose reflows we might be skipping dirty too.
   3686    if (aLine->HasFloats()) {
   3687      for (nsIFrame* f : aLine->Floats()) {
   3688        f->MarkSubtreeDirty();
   3689      }
   3690    }
   3691  } else {
   3692    // Dirty all the descendant lines of block kids to handle float damage,
   3693    // since our nsFloatManager will go away by the next time we're reflowing.
   3694    // XXXbz Can we do something more like what PropagateFloatDamage does?
   3695    // Would need to sort out the exact business with mBlockDelta for that....
   3696    // This marks way too much dirty.  If we ever make this better, revisit
   3697    // which lines we mark dirty in the interrupt case in ReflowDirtyLines.
   3698    nsBlockFrame* bf = do_QueryFrame(aLine->mFirstChild);
   3699    if (bf) {
   3700      MarkAllDescendantLinesDirty(bf);
   3701    }
   3702  }
   3703 }
   3704 
   3705 void nsBlockFrame::DeleteLine(BlockReflowState& aState,
   3706                              nsLineList::iterator aLine,
   3707                              nsLineList::iterator aLineEnd) {
   3708  MOZ_ASSERT(0 == aLine->GetChildCount(), "can't delete !empty line");
   3709  if (0 == aLine->GetChildCount()) {
   3710    NS_ASSERTION(aState.mCurrentLine == aLine,
   3711                 "using function more generally than designed, "
   3712                 "but perhaps OK now");
   3713    nsLineBox* line = aLine;
   3714    aLine = mLines.erase(aLine);
   3715    FreeLineBox(line);
   3716    ClearLineCursors();
   3717    // Mark the previous margin of the next line dirty since we need to
   3718    // recompute its top position.
   3719    if (aLine != aLineEnd) {
   3720      aLine->MarkPreviousMarginDirty();
   3721    }
   3722  }
   3723 }
   3724 
   3725 /**
   3726 * Reflow a line. The line will either contain a single block frame
   3727 * or contain 1 or more inline frames. aKeepReflowGoing indicates
   3728 * whether or not the caller should continue to reflow more lines.
   3729 * Returns true if the reflow used an overflow-wrap breakpoint.
   3730 */
   3731 bool nsBlockFrame::ReflowLine(BlockReflowState& aState, LineIterator aLine,
   3732                              bool* aKeepReflowGoing) {
   3733  MOZ_ASSERT(aLine->GetChildCount(), "reflowing empty line");
   3734 
   3735  // Setup the line-layout for the new line
   3736  aState.mCurrentLine = aLine;
   3737  aLine->ClearDirty();
   3738  aLine->InvalidateCachedIsEmpty();
   3739  aLine->ClearHadFloatPushed();
   3740 
   3741  // If this line contains a single block that is hidden by `content-visibility`
   3742  // don't reflow the line. If this line contains inlines and the first one is
   3743  // hidden by `content-visibility`, all of them are, so avoid reflow in that
   3744  // case as well.
   3745  // For frames that own anonymous children, even the first child is hidden by
   3746  // `content-visibility`, there could be some anonymous children need reflow,
   3747  // so we don't skip reflow this line.
   3748  nsIFrame* firstChild = aLine->mFirstChild;
   3749  if (firstChild->IsHiddenByContentVisibilityOfInFlowParentForLayout() &&
   3750      !HasAnyStateBits(NS_FRAME_OWNS_ANON_BOXES)) {
   3751    return false;
   3752  }
   3753 
   3754  // Now that we know what kind of line we have, reflow it
   3755  bool usedOverflowWrap = false;
   3756  if (aLine->IsBlock()) {
   3757    ReflowBlockFrame(aState, aLine, aKeepReflowGoing);
   3758  } else {
   3759    aLine->SetLineWrapped(false);
   3760    usedOverflowWrap = ReflowInlineFrames(aState, aLine, aKeepReflowGoing);
   3761 
   3762    // Store the line's float edges for overflow marker analysis if needed.
   3763    aLine->ClearFloatEdges();
   3764    if (aState.mFlags.mCanHaveOverflowMarkers) {
   3765      WritingMode wm = aLine->mWritingMode;
   3766      nsFlowAreaRect r = aState.GetFloatAvailableSpaceForBSize(
   3767          wm, aLine->BStart(), aLine->BSize(), nullptr);
   3768      if (r.HasFloats()) {
   3769        LogicalRect so = aLine->GetOverflowArea(OverflowType::Scrollable, wm,
   3770                                                aLine->mContainerSize);
   3771        nscoord s = r.mRect.IStart(wm);
   3772        nscoord e = r.mRect.IEnd(wm);
   3773        if (so.IEnd(wm) > e || so.IStart(wm) < s) {
   3774          // This line is overlapping a float - store the edges marking the area
   3775          // between the floats for text-overflow analysis.
   3776          aLine->SetFloatEdges(s, e);
   3777        }
   3778      }
   3779    }
   3780  }
   3781 
   3782  aLine->ClearMovedFragments();
   3783 
   3784  return usedOverflowWrap;
   3785 }
   3786 
   3787 nsIFrame* nsBlockFrame::PullFrame(BlockReflowState& aState,
   3788                                  LineIterator aLine) {
   3789  // First check our remaining lines.
   3790  if (LinesEnd() != aLine.next()) {
   3791    return PullFrameFrom(aLine, this, aLine.next());
   3792  }
   3793 
   3794  NS_ASSERTION(
   3795      !GetOverflowLines(),
   3796      "Our overflow lines should have been removed at the start of reflow");
   3797 
   3798  // Try each next-in-flow.
   3799  nsBlockFrame* nextInFlow = aState.mNextInFlow;
   3800  while (nextInFlow) {
   3801    if (nextInFlow->mLines.empty()) {
   3802      nextInFlow->DrainSelfOverflowList();
   3803    }
   3804    if (!nextInFlow->mLines.empty()) {
   3805      return PullFrameFrom(aLine, nextInFlow, nextInFlow->mLines.begin());
   3806    }
   3807    nextInFlow = static_cast<nsBlockFrame*>(nextInFlow->GetNextInFlow());
   3808    aState.mNextInFlow = nextInFlow;
   3809  }
   3810 
   3811  return nullptr;
   3812 }
   3813 
   3814 nsIFrame* nsBlockFrame::PullFrameFrom(nsLineBox* aLine,
   3815                                      nsBlockFrame* aFromContainer,
   3816                                      nsLineList::iterator aFromLine) {
   3817  nsLineBox* fromLine = aFromLine;
   3818  MOZ_ASSERT(fromLine, "bad line to pull from");
   3819  MOZ_ASSERT(fromLine->GetChildCount(), "empty line");
   3820  MOZ_ASSERT(aLine->GetChildCount(), "empty line");
   3821  MOZ_ASSERT(!HasProperty(LineIteratorProperty()),
   3822             "Shouldn't have line iterators mid-reflow");
   3823 
   3824  NS_ASSERTION(fromLine->IsBlock() == fromLine->mFirstChild->IsBlockOutside(),
   3825               "Disagreement about whether it's a block or not");
   3826 
   3827  if (fromLine->IsBlock()) {
   3828    // If our line is not empty and the child in aFromLine is a block
   3829    // then we cannot pull up the frame into this line. In this case
   3830    // we stop pulling.
   3831    return nullptr;
   3832  }
   3833  // Take frame from fromLine
   3834  nsIFrame* frame = fromLine->mFirstChild;
   3835  nsIFrame* newFirstChild = frame->GetNextSibling();
   3836 
   3837  if (aFromContainer != this) {
   3838    // The frame is being pulled from a next-in-flow; therefore we need to add
   3839    // it to our sibling list.
   3840    MOZ_ASSERT(aLine == mLines.back());
   3841    MOZ_ASSERT(aFromLine == aFromContainer->mLines.begin(),
   3842               "should only pull from first line");
   3843    aFromContainer->mFrames.RemoveFrame(frame);
   3844 
   3845    // When pushing and pulling frames we need to check for whether any
   3846    // views need to be reparented.
   3847    ReparentFrame(frame, aFromContainer, this);
   3848    mFrames.AppendFrame(nullptr, frame);
   3849 
   3850    // The frame might have (or contain) floats that need to be brought
   3851    // over too. (pass 'false' since there are no siblings to check)
   3852    ReparentFloats(frame, aFromContainer, false);
   3853  } else {
   3854    MOZ_ASSERT(aLine == aFromLine.prev());
   3855  }
   3856 
   3857  aLine->NoteFrameAdded(frame);
   3858  fromLine->NoteFrameRemoved(frame);
   3859 
   3860  if (fromLine->GetChildCount() > 0) {
   3861    // Mark line dirty now that we pulled a child
   3862    fromLine->MarkDirty();
   3863    fromLine->mFirstChild = newFirstChild;
   3864  } else {
   3865    // Free up the fromLine now that it's empty.
   3866    // Its bounds might need to be redrawn, though.
   3867    if (aFromLine.next() != aFromContainer->mLines.end()) {
   3868      aFromLine.next()->MarkPreviousMarginDirty();
   3869    }
   3870    aFromContainer->mLines.erase(aFromLine);
   3871    // aFromLine is now invalid
   3872    aFromContainer->FreeLineBox(fromLine);
   3873  }
   3874 
   3875 #ifdef DEBUG
   3876  VerifyLines(true);
   3877  VerifyOverflowSituation();
   3878 #endif
   3879 
   3880  return frame;
   3881 }
   3882 
   3883 void nsBlockFrame::SlideLine(BlockReflowState& aState, nsLineBox* aLine,
   3884                             nscoord aDeltaBCoord) {
   3885  MOZ_ASSERT(aDeltaBCoord != 0, "why slide a line nowhere?");
   3886 
   3887  // Adjust line state
   3888  aLine->SlideBy(aDeltaBCoord, aState.ContainerSize());
   3889 
   3890  // Adjust the frames in the line
   3891  MoveChildFramesOfLine(aLine, aDeltaBCoord);
   3892 }
   3893 
   3894 void nsBlockFrame::UpdateLineContainerSize(nsLineBox* aLine,
   3895                                           const nsSize& aNewContainerSize) {
   3896  if (aNewContainerSize == aLine->mContainerSize) {
   3897    return;
   3898  }
   3899 
   3900  // Adjust line state
   3901  nsSize sizeDelta = aLine->UpdateContainerSize(aNewContainerSize);
   3902 
   3903  // Changing container width only matters if writing mode is vertical-rl
   3904  if (GetWritingMode().IsVerticalRL()) {
   3905    MoveChildFramesOfLine(aLine, sizeDelta.width);
   3906  }
   3907 }
   3908 
   3909 void nsBlockFrame::MoveChildFramesOfLine(nsLineBox* aLine,
   3910                                         nscoord aDeltaBCoord) {
   3911  // Adjust the frames in the line
   3912  nsIFrame* kid = aLine->mFirstChild;
   3913  if (!kid) {
   3914    return;
   3915  }
   3916 
   3917  WritingMode wm = GetWritingMode();
   3918  LogicalPoint translation(wm, 0, aDeltaBCoord);
   3919 
   3920  if (aLine->IsBlock()) {
   3921    if (aDeltaBCoord) {
   3922      kid->MovePositionBy(wm, translation);
   3923    }
   3924  } else {
   3925    // Adjust the block-dir coordinate of the frames in the line.
   3926    // Note: we need to re-position views even if aDeltaBCoord is 0, because
   3927    // one of our parent frames may have moved and so the view's position
   3928    // relative to its parent may have changed.
   3929    if (aDeltaBCoord) {
   3930      int32_t n = aLine->GetChildCount();
   3931      while (--n >= 0) {
   3932        kid->MovePositionBy(wm, translation);
   3933        kid = kid->GetNextSibling();
   3934      }
   3935    }
   3936  }
   3937 }
   3938 
   3939 static inline bool IsNonAutoNonZeroBSize(const StyleSize& aCoord) {
   3940  // The "extremum length" values (see ExtremumLength) that return true from
   3941  // 'BehavesLikeInitialValueOnBlockAxis()' were originally aimed at
   3942  // inline-size (or width, as it was before logicalization). For now, let them
   3943  // return false here, so we treat them like 'auto' pending a real
   3944  // implementation. (See bug 1126420.)
   3945  if (aCoord.BehavesLikeInitialValueOnBlockAxis()) {
   3946    return false;
   3947  }
   3948  if (aCoord.BehavesLikeStretchOnBlockAxis()) {
   3949    // We return true for "stretch" because it's essentially equivalent to
   3950    // "100%" for the purposes of this function (and this function returns true
   3951    // for nonzero percentage values, in the final return statement below).
   3952    return true;
   3953  }
   3954  MOZ_ASSERT(aCoord.IsLengthPercentage());
   3955  // If we evaluate the length/percent/calc at a percentage basis of
   3956  // both nscoord_MAX and 0, and it's zero both ways, then it's a zero
   3957  // length, percent, or combination thereof.  Test > 0 so we clamp
   3958  // negative calc() results to 0.
   3959  return aCoord.AsLengthPercentage().Resolve(nscoord_MAX) > 0 ||
   3960         aCoord.AsLengthPercentage().Resolve(0) > 0;
   3961 }
   3962 
   3963 /* virtual */
   3964 bool nsBlockFrame::IsSelfEmpty() {
   3965  if (IsHiddenByContentVisibilityOfInFlowParentForLayout()) {
   3966    return true;
   3967  }
   3968 
   3969  // Blocks which are margin-roots (including inline-blocks) cannot be treated
   3970  // as empty for margin-collapsing and other purposes. They're more like
   3971  // replaced elements.
   3972  if (HasAnyStateBits(NS_BLOCK_BFC)) {
   3973    return false;
   3974  }
   3975 
   3976  WritingMode wm = GetWritingMode();
   3977  const nsStylePosition* position = StylePosition();
   3978  const auto anchorResolutionParams = AnchorPosResolutionParams::From(this);
   3979  const auto bSize = position->BSize(wm, anchorResolutionParams);
   3980 
   3981  if (IsNonAutoNonZeroBSize(*position->MinBSize(wm, anchorResolutionParams)) ||
   3982      IsNonAutoNonZeroBSize(*bSize)) {
   3983    return false;
   3984  }
   3985 
   3986  // FIXME: Bug 1646100 - Take intrinsic size into account.
   3987  // FIXME: Handle the case that both inline and block sizes are auto.
   3988  // https://github.com/w3c/csswg-drafts/issues/5060.
   3989  // Note: block-size could be zero or auto/intrinsic keywords here.
   3990  if (bSize->BehavesLikeInitialValueOnBlockAxis() &&
   3991      position->mAspectRatio.HasFiniteRatio()) {
   3992    return false;
   3993  }
   3994 
   3995  const nsStyleBorder* border = StyleBorder();
   3996  const nsStylePadding* padding = StylePadding();
   3997 
   3998  if (border->GetComputedBorderWidth(wm.PhysicalSide(LogicalSide::BStart)) !=
   3999          0 ||
   4000      border->GetComputedBorderWidth(wm.PhysicalSide(LogicalSide::BEnd)) != 0 ||
   4001      !nsLayoutUtils::IsPaddingZero(padding->mPadding.GetBStart(wm)) ||
   4002      !nsLayoutUtils::IsPaddingZero(padding->mPadding.GetBEnd(wm))) {
   4003    return false;
   4004  }
   4005 
   4006  nsIFrame* outsideMarker = GetOutsideMarker();
   4007  if (outsideMarker && !MarkerIsEmpty(outsideMarker)) {
   4008    return false;
   4009  }
   4010 
   4011  return true;
   4012 }
   4013 
   4014 bool nsBlockFrame::CachedIsEmpty() {
   4015  if (!IsSelfEmpty()) {
   4016    return false;
   4017  }
   4018  for (auto& line : mLines) {
   4019    if (!line.CachedIsEmpty()) {
   4020      return false;
   4021    }
   4022  }
   4023  return true;
   4024 }
   4025 
   4026 bool nsBlockFrame::IsEmpty() {
   4027  if (!IsSelfEmpty()) {
   4028    return false;
   4029  }
   4030  return LinesAreEmpty();
   4031 }
   4032 
   4033 bool nsBlockFrame::ShouldApplyBStartMargin(BlockReflowState& aState,
   4034                                           nsLineBox* aLine) {
   4035  if (aLine->mFirstChild->IsPageBreakFrame()) {
   4036    // A page break frame consumes margins adjacent to it.
   4037    // https://drafts.csswg.org/css-break/#break-margins
   4038    return false;
   4039  }
   4040 
   4041  if (aState.mFlags.mShouldApplyBStartMargin) {
   4042    // Apply short-circuit check to avoid searching the line list
   4043    return true;
   4044  }
   4045 
   4046  if (!aState.IsAdjacentWithBStart()) {
   4047    // If we aren't at the start block-coordinate then something of non-zero
   4048    // height must have been placed. Therefore the childs block-start margin
   4049    // applies.
   4050    aState.mFlags.mShouldApplyBStartMargin = true;
   4051    return true;
   4052  }
   4053 
   4054  // Determine if this line is "essentially" the first line
   4055  LineIterator line = LinesBegin();
   4056  if (aState.mFlags.mHasLineAdjacentToTop) {
   4057    line = aState.mLineAdjacentToTop;
   4058  }
   4059  while (line != aLine) {
   4060    if (!line->CachedIsEmpty() || line->HasClearance()) {
   4061      // A line which precedes aLine is non-empty, or has clearance,
   4062      // so therefore the block-start margin applies.
   4063      aState.mFlags.mShouldApplyBStartMargin = true;
   4064      return true;
   4065    }
   4066    // No need to apply the block-start margin if the line has floats.  We
   4067    // should collapse anyway (bug 44419)
   4068    ++line;
   4069    aState.mFlags.mHasLineAdjacentToTop = true;
   4070    aState.mLineAdjacentToTop = line;
   4071  }
   4072 
   4073  // The line being reflowed is "essentially" the first line in the
   4074  // block. Therefore its block-start margin will be collapsed by the
   4075  // generational collapsing logic with its parent (us).
   4076  return false;
   4077 }
   4078 
   4079 void nsBlockFrame::ReflowBlockFrame(BlockReflowState& aState,
   4080                                    LineIterator aLine,
   4081                                    bool* aKeepReflowGoing) {
   4082  MOZ_ASSERT(*aKeepReflowGoing, "bad caller");
   4083 
   4084  nsIFrame* frame = aLine->mFirstChild;
   4085  if (!frame) {
   4086    NS_ASSERTION(false, "program error - unexpected empty line");
   4087    return;
   4088  }
   4089 
   4090  // If the previous frame was a page-break-frame, then preemptively push this
   4091  // frame to the next page.
   4092  // This is primarily important for the placeholders for abspos frames, which
   4093  // measure as zero height and then would be placed on this page.
   4094  if (aState.ContentBSize() != NS_UNCONSTRAINEDSIZE) {
   4095    const nsIFrame* const prev = frame->GetPrevSibling();
   4096    if (prev && prev->IsPageBreakFrame()) {
   4097      PushTruncatedLine(aState, aLine, aKeepReflowGoing);
   4098      return;
   4099    }
   4100  }
   4101 
   4102  // Prepare the block reflow engine
   4103  nsBlockReflowContext brc(aState.mPresContext, aState.mReflowInput);
   4104 
   4105  WritingMode cbWM = frame->GetContainingBlock()->GetWritingMode();
   4106  UsedClear clearType = frame->StyleDisplay()->UsedClear(cbWM);
   4107  if (aState.mTrailingClearFromPIF != UsedClear::None) {
   4108    clearType = nsLayoutUtils::CombineClearType(clearType,
   4109                                                aState.mTrailingClearFromPIF);
   4110    aState.mTrailingClearFromPIF = UsedClear::None;
   4111  }
   4112 
   4113  // Clear past floats before the block if the clear style is not none
   4114  aLine->ClearForcedLineBreak();
   4115  if (clearType != UsedClear::None) {
   4116    aLine->SetFloatClearTypeBefore(clearType);
   4117  }
   4118 
   4119  // See if we should apply the block-start margin. If the block frame being
   4120  // reflowed is a continuation, then we don't apply its block-start margin
   4121  // because it's not significant. Otherwise, dig deeper.
   4122  bool applyBStartMargin =
   4123      !frame->GetPrevContinuation() && ShouldApplyBStartMargin(aState, aLine);
   4124  if (applyBStartMargin) {
   4125    // The HasClearance setting is only valid if ShouldApplyBStartMargin
   4126    // returned false (in which case the block-start margin-root set our
   4127    // clearance flag). Otherwise clear it now. We'll set it later on
   4128    // ourselves if necessary.
   4129    aLine->ClearHasClearance();
   4130  }
   4131  bool treatWithClearance = aLine->HasClearance();
   4132 
   4133  bool mightClearFloats = clearType != UsedClear::None;
   4134  nsIFrame* floatAvoidingBlock = nullptr;
   4135  if (!nsBlockFrame::BlockCanIntersectFloats(frame)) {
   4136    mightClearFloats = true;
   4137    floatAvoidingBlock = frame;
   4138  }
   4139 
   4140  // If our block-start margin was counted as part of some parent's block-start
   4141  // margin collapse, and we are being speculatively reflowed assuming this
   4142  // frame DID NOT need clearance, then we need to check that
   4143  // assumption.
   4144  if (!treatWithClearance && !applyBStartMargin && mightClearFloats &&
   4145      aState.mReflowInput.mDiscoveredClearance) {
   4146    nscoord curBCoord = aState.mBCoord + aState.mPrevBEndMargin.Get();
   4147    if (auto [clearBCoord, result] =
   4148            aState.ClearFloats(curBCoord, clearType, floatAvoidingBlock);
   4149        result != ClearFloatsResult::BCoordNoChange) {
   4150      (void)clearBCoord;
   4151 
   4152      // Only record the first frame that requires clearance
   4153      if (!*aState.mReflowInput.mDiscoveredClearance) {
   4154        *aState.mReflowInput.mDiscoveredClearance = frame;
   4155      }
   4156      aState.mPrevChild = frame;
   4157      // Exactly what we do now is flexible since we'll definitely be
   4158      // reflowed.
   4159      return;
   4160    }
   4161  }
   4162  if (treatWithClearance) {
   4163    applyBStartMargin = true;
   4164  }
   4165 
   4166  nsIFrame* clearanceFrame = nullptr;
   4167  const nscoord startingBCoord = aState.mBCoord;
   4168  const CollapsingMargin incomingMargin = aState.mPrevBEndMargin;
   4169  nscoord clearance;
   4170  while (true) {
   4171    clearance = 0;
   4172    nscoord bStartMargin = 0;
   4173    bool mayNeedRetry = false;
   4174    bool clearedFloats = false;
   4175    bool clearedPushedOrSplitFloat = false;
   4176    if (applyBStartMargin) {
   4177      // Precompute the blocks block-start margin value so that we can get the
   4178      // correct available space (there might be a float that's
   4179      // already been placed below the aState.mPrevBEndMargin
   4180 
   4181      // Setup a reflowInput to get the style computed block-start margin
   4182      // value. We'll use a reason of `resize' so that we don't fudge
   4183      // any incremental reflow input.
   4184 
   4185      // The availSpace here is irrelevant to our needs - all we want
   4186      // out if this setup is the block-start margin value which doesn't depend
   4187      // on the childs available space.
   4188      // XXX building a complete ReflowInput just to get the block-start
   4189      // margin seems like a waste. And we do this for almost every block!
   4190      WritingMode wm = frame->GetWritingMode();
   4191      LogicalSize availSpace = aState.ContentSize(wm);
   4192      ReflowInput reflowInput(aState.mPresContext, aState.mReflowInput, frame,
   4193                              availSpace);
   4194 
   4195      if (treatWithClearance) {
   4196        aState.mBCoord += aState.mPrevBEndMargin.Get();
   4197        aState.mPrevBEndMargin.Zero();
   4198      }
   4199 
   4200      // Now compute the collapsed margin-block-start value into
   4201      // aState.mPrevBEndMargin, assuming that all child margins
   4202      // collapse down to clearanceFrame.
   4203      brc.ComputeCollapsedBStartMargin(reflowInput, &aState.mPrevBEndMargin,
   4204                                       clearanceFrame, &mayNeedRetry);
   4205 
   4206      // XXX optimization; we could check the collapsing children to see if they
   4207      // are sure to require clearance, and so avoid retrying them
   4208 
   4209      if (clearanceFrame) {
   4210        // Don't allow retries on the second pass. The clearance decisions for
   4211        // the blocks whose block-start margins collapse with ours are now
   4212        // fixed.
   4213        mayNeedRetry = false;
   4214      }
   4215 
   4216      if (!treatWithClearance && !clearanceFrame && mightClearFloats) {
   4217        // We don't know if we need clearance and this is the first,
   4218        // optimistic pass.  So determine whether *this block* needs
   4219        // clearance. Note that we do not allow the decision for whether
   4220        // this block has clearance to change on the second pass; that
   4221        // decision is only allowed to be made under the optimistic
   4222        // first pass.
   4223        nscoord curBCoord = aState.mBCoord + aState.mPrevBEndMargin.Get();
   4224        if (auto [clearBCoord, result] =
   4225                aState.ClearFloats(curBCoord, clearType, floatAvoidingBlock);
   4226            result != ClearFloatsResult::BCoordNoChange) {
   4227          (void)clearBCoord;
   4228 
   4229          // Looks like we need clearance and we didn't know about it already.
   4230          // So recompute collapsed margin
   4231          treatWithClearance = true;
   4232          // Remember this decision, needed for incremental reflow
   4233          aLine->SetHasClearance();
   4234 
   4235          // Apply incoming margins
   4236          aState.mBCoord += aState.mPrevBEndMargin.Get();
   4237          aState.mPrevBEndMargin.Zero();
   4238 
   4239          // Compute the collapsed margin again, ignoring the incoming margin
   4240          // this time
   4241          mayNeedRetry = false;
   4242          brc.ComputeCollapsedBStartMargin(reflowInput, &aState.mPrevBEndMargin,
   4243                                           clearanceFrame, &mayNeedRetry);
   4244        }
   4245      }
   4246 
   4247      // Temporarily advance the running block-direction value so that the
   4248      // GetFloatAvailableSpace method will return the right available space.
   4249      // This undone as soon as the horizontal margins are computed.
   4250      bStartMargin = aState.mPrevBEndMargin.Get();
   4251 
   4252      if (treatWithClearance) {
   4253        nscoord currentBCoord = aState.mBCoord;
   4254        // advance mBCoord to the clear position.
   4255        auto [clearBCoord, result] =
   4256            aState.ClearFloats(aState.mBCoord, clearType, floatAvoidingBlock);
   4257        aState.mBCoord = clearBCoord;
   4258 
   4259        clearedFloats = result != ClearFloatsResult::BCoordNoChange;
   4260        clearedPushedOrSplitFloat =
   4261            result == ClearFloatsResult::FloatsPushedOrSplit;
   4262 
   4263        // Compute clearance. It's the amount we need to add to the block-start
   4264        // border-edge of the frame, after applying collapsed margins
   4265        // from the frame and its children, to get it to line up with
   4266        // the block-end of the floats. The former is
   4267        // currentBCoord + bStartMargin, the latter is the current
   4268        // aState.mBCoord.
   4269        // Note that negative clearance is possible
   4270        clearance = aState.mBCoord - (currentBCoord + bStartMargin);
   4271 
   4272        // Add clearance to our block-start margin while we compute available
   4273        // space for the frame
   4274        bStartMargin += clearance;
   4275 
   4276        // Note that aState.mBCoord should stay where it is: at the block-start
   4277        // border-edge of the frame
   4278      } else {
   4279        // Advance aState.mBCoord to the block-start border-edge of the frame.
   4280        aState.mBCoord += bStartMargin;
   4281      }
   4282    }
   4283 
   4284    aLine->SetLineIsImpactedByFloat(false);
   4285 
   4286    // Here aState.mBCoord is the block-start border-edge of the block.
   4287    // Compute the available space for the block
   4288    nsFlowAreaRect floatAvailableSpace = aState.GetFloatAvailableSpace(cbWM);
   4289    WritingMode wm = aState.mReflowInput.GetWritingMode();
   4290    LogicalRect availSpace = aState.ComputeBlockAvailSpace(
   4291        frame, floatAvailableSpace, (floatAvoidingBlock));
   4292 
   4293    // The check for
   4294    //   (!aState.mReflowInput.mFlags.mIsTopOfPage || clearedFloats)
   4295    // is to some degree out of paranoia:  if we reliably eat up block-start
   4296    // margins at the top of the page as we ought to, it wouldn't be
   4297    // needed.
   4298    if ((!aState.mReflowInput.mFlags.mIsTopOfPage || clearedFloats) &&
   4299        (availSpace.BSize(wm) < 0 || clearedPushedOrSplitFloat)) {
   4300      // We know already that this child block won't fit on this
   4301      // page/column due to the block-start margin or the clearance.  So we
   4302      // need to get out of here now.  (If we don't, most blocks will handle
   4303      // things fine, and report break-before, but zero-height blocks
   4304      // won't, and will thus make their parent overly-large and force
   4305      // *it* to be pushed in its entirety.)
   4306      aState.mBCoord = startingBCoord;
   4307      aState.mPrevBEndMargin = incomingMargin;
   4308      if (ShouldAvoidBreakInside(aState.mReflowInput)) {
   4309        SetBreakBeforeStatusBeforeLine(aState, aLine, aKeepReflowGoing);
   4310      } else {
   4311        PushTruncatedLine(aState, aLine, aKeepReflowGoing);
   4312      }
   4313      return;
   4314    }
   4315 
   4316    // Now put the block-dir coordinate back to the start of the
   4317    // block-start-margin + clearance.
   4318    aState.mBCoord -= bStartMargin;
   4319    availSpace.BStart(wm) -= bStartMargin;
   4320    if (NS_UNCONSTRAINEDSIZE != availSpace.BSize(wm)) {
   4321      availSpace.BSize(wm) += bStartMargin;
   4322    }
   4323 
   4324    // Construct the reflow input for the block.
   4325    Maybe<ReflowInput> childReflowInput;
   4326    Maybe<LogicalSize> cbSize;
   4327    LogicalSize availSize = availSpace.Size(wm);
   4328    bool columnSetWrapperHasNoBSizeLeft = false;
   4329    if (Style()->GetPseudoType() == PseudoStyleType::columnContent) {
   4330      // Calculate the multicol containing block's block size so that the
   4331      // children with percentage block size get correct percentage basis.
   4332      const ReflowInput* cbReflowInput =
   4333          aState.mReflowInput.mParentReflowInput->mCBReflowInput;
   4334      MOZ_ASSERT(cbReflowInput->mFrame->StyleColumn()->IsColumnContainerStyle(),
   4335                 "Get unexpected reflow input of multicol containing block!");
   4336 
   4337      // Use column-width as the containing block's inline-size, i.e. the column
   4338      // content's computed inline-size.
   4339      cbSize.emplace(LogicalSize(wm, aState.mReflowInput.ComputedISize(),
   4340                                 cbReflowInput->ComputedBSize())
   4341                         .ConvertTo(frame->GetWritingMode(), wm));
   4342 
   4343      // If a ColumnSetWrapper is in a balancing column content, it may be
   4344      // pushed or pulled back and forth between column contents. Always add
   4345      // NS_FRAME_HAS_DIRTY_CHILDREN bit to it so that its ColumnSet children
   4346      // can have a chance to reflow under current block size constraint.
   4347      if (aState.mReflowInput.mFlags.mIsColumnBalancing &&
   4348          frame->IsColumnSetWrapperFrame()) {
   4349        frame->AddStateBits(NS_FRAME_HAS_DIRTY_CHILDREN);
   4350      }
   4351    } else if (IsColumnSetWrapperFrame()) {
   4352      // If we are reflowing our ColumnSet children, we want to apply our block
   4353      // size constraint to the available block size when constructing reflow
   4354      // input for ColumnSet so that ColumnSet can use it to compute its max
   4355      // column block size.
   4356      if (frame->IsColumnSetFrame()) {
   4357        nscoord contentBSize = aState.mReflowInput.ComputedBSize();
   4358        if (aState.mReflowInput.ComputedMaxBSize() != NS_UNCONSTRAINEDSIZE) {
   4359          contentBSize =
   4360              std::min(contentBSize, aState.mReflowInput.ComputedMaxBSize());
   4361        }
   4362        if (contentBSize != NS_UNCONSTRAINEDSIZE) {
   4363          // To get the remaining content block-size, subtract the content
   4364          // block-size consumed by our previous continuations.
   4365          contentBSize -= aState.mConsumedBSize;
   4366 
   4367          // ColumnSet is not the outermost frame in the column container, so it
   4368          // cannot have any margin. We don't need to consider any margin that
   4369          // can be generated by "box-decoration-break: clone" as we do in
   4370          // BlockReflowState::ComputeBlockAvailSpace().
   4371          const nscoord availContentBSize = std::max(
   4372              0, contentBSize - (aState.mBCoord - aState.ContentBStart()));
   4373          if (availSize.BSize(wm) >= availContentBSize) {
   4374            availSize.BSize(wm) = availContentBSize;
   4375            columnSetWrapperHasNoBSizeLeft = true;
   4376          }
   4377        }
   4378      }
   4379    }
   4380 
   4381    childReflowInput.emplace(aState.mPresContext, aState.mReflowInput, frame,
   4382                             availSize.ConvertTo(frame->GetWritingMode(), wm),
   4383                             cbSize);
   4384 
   4385    childReflowInput->mFlags.mColumnSetWrapperHasNoBSizeLeft =
   4386        columnSetWrapperHasNoBSizeLeft;
   4387 
   4388    if (aLine->MovedFragments()) {
   4389      // We only need to set this the first reflow, since if we reflow
   4390      // again (and replace childReflowInput) we'll be reflowing it
   4391      // again in the same fragment as the previous time.
   4392      childReflowInput->mFlags.mMovedBlockFragments = true;
   4393    }
   4394 
   4395    nsFloatManager::SavedState floatManagerState;
   4396    nsReflowStatus frameReflowStatus;
   4397    do {
   4398      if (floatAvailableSpace.HasFloats()) {
   4399        // Set if floatAvailableSpace.HasFloats() is true for any
   4400        // iteration of the loop.
   4401        aLine->SetLineIsImpactedByFloat(true);
   4402      }
   4403 
   4404      // We might need to store into mDiscoveredClearance later if it's
   4405      // currently null; we want to overwrite any writes that
   4406      // brc.ReflowBlock() below does, so we need to remember now
   4407      // whether it's empty.
   4408      const bool shouldStoreClearance =
   4409          aState.mReflowInput.mDiscoveredClearance &&
   4410          !*aState.mReflowInput.mDiscoveredClearance;
   4411 
   4412      // Reflow the block into the available space
   4413      if (mayNeedRetry || floatAvoidingBlock) {
   4414        aState.FloatManager()->PushState(&floatManagerState);
   4415      }
   4416 
   4417      if (mayNeedRetry) {
   4418        childReflowInput->mDiscoveredClearance = &clearanceFrame;
   4419      } else if (!applyBStartMargin) {
   4420        childReflowInput->mDiscoveredClearance =
   4421            aState.mReflowInput.mDiscoveredClearance;
   4422      }
   4423 
   4424      frameReflowStatus.Reset();
   4425      brc.ReflowBlock(availSpace, applyBStartMargin, aState.mPrevBEndMargin,
   4426                      clearance, aLine.get(), *childReflowInput,
   4427                      frameReflowStatus, aState);
   4428 
   4429      if (frameReflowStatus.IsInlineBreakBefore()) {
   4430        // No need to retry this loop if there is a break opportunity before the
   4431        // child block.
   4432        break;
   4433      }
   4434 
   4435      // Now the block has a height.  Using that height, get the
   4436      // available space again and call ComputeBlockAvailSpace again.
   4437      // If ComputeBlockAvailSpace gives a different result, we need to
   4438      // reflow again.
   4439      if (!floatAvoidingBlock) {
   4440        break;
   4441      }
   4442 
   4443      LogicalRect oldFloatAvailableSpaceRect(floatAvailableSpace.mRect);
   4444      floatAvailableSpace = aState.GetFloatAvailableSpaceForBSize(
   4445          cbWM, aState.mBCoord + bStartMargin, brc.GetMetrics().BSize(wm),
   4446          &floatManagerState);
   4447      NS_ASSERTION(floatAvailableSpace.mRect.BStart(wm) ==
   4448                       oldFloatAvailableSpaceRect.BStart(wm),
   4449                   "yikes");
   4450      // Restore the height to the position of the next band.
   4451      floatAvailableSpace.mRect.BSize(wm) =
   4452          oldFloatAvailableSpaceRect.BSize(wm);
   4453      // Determine whether the available space shrunk on either side,
   4454      // because (the first time round) we now know the block's height,
   4455      // and it may intersect additional floats, or (on later
   4456      // iterations) because narrowing the width relative to the
   4457      // previous time may cause the block to become taller.  Note that
   4458      // since we're reflowing the block, narrowing the width might also
   4459      // make it shorter, so we must pass aCanGrow as true.
   4460      if (!AvailableSpaceShrunk(wm, oldFloatAvailableSpaceRect,
   4461                                floatAvailableSpace.mRect, true)) {
   4462        // The size and position we chose before are fine (i.e., they
   4463        // don't cause intersecting with floats that requires a change
   4464        // in size or position), so we're done.
   4465        break;
   4466      }
   4467 
   4468      bool advanced = false;
   4469      if (!aState.FloatAvoidingBlockFitsInAvailSpace(floatAvoidingBlock,
   4470                                                     floatAvailableSpace)) {
   4471        // Advance to the next band.
   4472        nscoord newBCoord = aState.mBCoord;
   4473        if (aState.AdvanceToNextBand(floatAvailableSpace.mRect, &newBCoord)) {
   4474          advanced = true;
   4475        }
   4476        // ClearFloats might be able to advance us further once we're there.
   4477        std::tie(aState.mBCoord, std::ignore) =
   4478            aState.ClearFloats(newBCoord, UsedClear::None, floatAvoidingBlock);
   4479 
   4480        // Start over with a new available space rect at the new height.
   4481        floatAvailableSpace = aState.GetFloatAvailableSpaceWithState(
   4482            cbWM, aState.mBCoord, ShapeType::ShapeOutside, &floatManagerState);
   4483      }
   4484 
   4485      const LogicalRect oldAvailSpace = availSpace;
   4486      availSpace = aState.ComputeBlockAvailSpace(frame, floatAvailableSpace,
   4487                                                 (floatAvoidingBlock));
   4488 
   4489      if (!advanced && availSpace.IsEqualEdges(oldAvailSpace)) {
   4490        break;
   4491      }
   4492 
   4493      // We need another reflow.
   4494      aState.FloatManager()->PopState(&floatManagerState);
   4495 
   4496      if (!treatWithClearance && !applyBStartMargin &&
   4497          aState.mReflowInput.mDiscoveredClearance) {
   4498        // We set shouldStoreClearance above to record only the first
   4499        // frame that requires clearance.
   4500        if (shouldStoreClearance) {
   4501          *aState.mReflowInput.mDiscoveredClearance = frame;
   4502        }
   4503        aState.mPrevChild = frame;
   4504        // Exactly what we do now is flexible since we'll definitely be
   4505        // reflowed.
   4506        return;
   4507      }
   4508 
   4509      if (advanced) {
   4510        // We're pushing down the border-box, so we don't apply margin anymore.
   4511        // This should never cause us to move up since the call to
   4512        // GetFloatAvailableSpaceForBSize above included the margin.
   4513        applyBStartMargin = false;
   4514        bStartMargin = 0;
   4515        treatWithClearance = true;  // avoid hitting test above
   4516        clearance = 0;
   4517      }
   4518 
   4519      childReflowInput.reset();
   4520      childReflowInput.emplace(
   4521          aState.mPresContext, aState.mReflowInput, frame,
   4522          availSpace.Size(wm).ConvertTo(frame->GetWritingMode(), wm));
   4523    } while (true);
   4524 
   4525    if (mayNeedRetry && clearanceFrame) {
   4526      // Found a clearance frame, so we need to reflow |frame| a second time.
   4527      // Restore the states and start over again.
   4528      aState.FloatManager()->PopState(&floatManagerState);
   4529      aState.mBCoord = startingBCoord;
   4530      aState.mPrevBEndMargin = incomingMargin;
   4531      continue;
   4532    }
   4533 
   4534    aState.mPrevChild = frame;
   4535 
   4536    if (childReflowInput->WillReflowAgainForClearance()) {
   4537      return;
   4538    }
   4539 
   4540 #if defined(REFLOW_STATUS_COVERAGE)
   4541    RecordReflowStatus(true, frameReflowStatus);
   4542 #endif
   4543 
   4544    if (frameReflowStatus.IsInlineBreakBefore()) {
   4545      // None of the child block fits.
   4546      if (ShouldAvoidBreakInside(aState.mReflowInput)) {
   4547        SetBreakBeforeStatusBeforeLine(aState, aLine, aKeepReflowGoing);
   4548      } else {
   4549        PushTruncatedLine(aState, aLine, aKeepReflowGoing);
   4550      }
   4551    } else {
   4552      // Note: line-break-after a block is a nop
   4553 
   4554      // Try to place the child block.
   4555      // Don't force the block to fit if we have positive clearance, because
   4556      // pushing it to the next page would give it more room.
   4557      // Don't force the block to fit if it's impacted by a float. If it is,
   4558      // then pushing it to the next page would give it more room. Note that
   4559      // isImpacted doesn't include impact from the block's own floats.
   4560      bool forceFit = aState.IsAdjacentWithBStart() && clearance <= 0 &&
   4561                      !floatAvailableSpace.HasFloats();
   4562      CollapsingMargin collapsedBEndMargin;
   4563      OverflowAreas overflowAreas;
   4564      *aKeepReflowGoing =
   4565          brc.PlaceBlock(*childReflowInput, forceFit, aLine.get(),
   4566                         collapsedBEndMargin, overflowAreas, frameReflowStatus);
   4567      if (!frameReflowStatus.IsFullyComplete() &&
   4568          ShouldAvoidBreakInside(aState.mReflowInput)) {
   4569        *aKeepReflowGoing = false;
   4570        aLine->MarkDirty();
   4571      }
   4572 
   4573      if (aLine->SetCarriedOutBEndMargin(collapsedBEndMargin)) {
   4574        LineIterator nextLine = aLine;
   4575        ++nextLine;
   4576        if (nextLine != LinesEnd()) {
   4577          nextLine->MarkPreviousMarginDirty();
   4578        }
   4579      }
   4580 
   4581      if (Style()->GetPseudoType() == PseudoStyleType::scrolledContent) {
   4582        auto lineFrameBounds = GetLineFrameInFlowBounds(*aLine, *frame);
   4583        MOZ_ASSERT(aLine->GetChildCount() == 1,
   4584                   "More than one child in block line?");
   4585        // Inline-line (i.e. Multiple frames in one line) handled in one of
   4586        // other callsites.
   4587        aLine->SetInFlowChildBounds(lineFrameBounds);
   4588      }
   4589 
   4590      aLine->SetOverflowAreas(overflowAreas);
   4591      if (*aKeepReflowGoing) {
   4592        // Some of the child block fit
   4593 
   4594        // Advance to new Y position
   4595        nscoord newBCoord = aLine->BEnd();
   4596        aState.mBCoord = newBCoord;
   4597 
   4598        // Continue the block frame now if it didn't completely fit in
   4599        // the available space.
   4600        if (!frameReflowStatus.IsFullyComplete()) {
   4601          bool madeContinuation = CreateContinuationFor(aState, nullptr, frame);
   4602 
   4603          nsIFrame* nextFrame = frame->GetNextInFlow();
   4604          NS_ASSERTION(nextFrame,
   4605                       "We're supposed to have a next-in-flow by now");
   4606 
   4607          if (frameReflowStatus.IsIncomplete()) {
   4608            // If nextFrame used to be an overflow container, make it a normal
   4609            // block
   4610            if (!madeContinuation &&
   4611                nextFrame->HasAnyStateBits(NS_FRAME_IS_OVERFLOW_CONTAINER)) {
   4612              nsOverflowContinuationTracker::AutoFinish fini(
   4613                  aState.mOverflowTracker, frame);
   4614              nsContainerFrame* parent = nextFrame->GetParent();
   4615              parent->StealFrame(nextFrame);
   4616              if (parent != this) {
   4617                ReparentFrame(nextFrame, parent, this);
   4618              }
   4619              mFrames.InsertFrame(nullptr, frame, nextFrame);
   4620              madeContinuation = true;  // needs to be added to mLines
   4621              nextFrame->RemoveStateBits(NS_FRAME_IS_OVERFLOW_CONTAINER);
   4622              frameReflowStatus.SetNextInFlowNeedsReflow();
   4623            }
   4624 
   4625            // Push continuation to a new line, but only if we actually made
   4626            // one.
   4627            if (madeContinuation) {
   4628              nsLineBox* line = NewLineBox(nextFrame, true);
   4629              mLines.after_insert(aLine, line);
   4630            }
   4631 
   4632            PushTruncatedLine(aState, aLine.next(), aKeepReflowGoing);
   4633 
   4634            // If we need to reflow the continuation of the block child,
   4635            // then we'd better reflow our continuation
   4636            if (frameReflowStatus.NextInFlowNeedsReflow()) {
   4637              aState.mReflowStatus.SetNextInFlowNeedsReflow();
   4638              // We also need to make that continuation's line dirty so
   4639              // it gets reflowed when we reflow our next in flow. The
   4640              // nif's line must always be either a line of the nif's
   4641              // parent block (only if we didn't make a continuation) or
   4642              // else one of our own overflow lines. In the latter case
   4643              // the line is already marked dirty, so just handle the
   4644              // first case.
   4645              if (!madeContinuation) {
   4646                nsBlockFrame* nifBlock = do_QueryFrame(nextFrame->GetParent());
   4647                NS_ASSERTION(
   4648                    nifBlock,
   4649                    "A block's child's next in flow's parent must be a block!");
   4650                for (auto& line : nifBlock->Lines()) {
   4651                  if (line.Contains(nextFrame)) {
   4652                    line.MarkDirty();
   4653                    break;
   4654                  }
   4655                }
   4656              }
   4657            }
   4658 
   4659            // The block-end margin for a block is only applied on the last
   4660            // flow block. Since we just continued the child block frame,
   4661            // we know that line->mFirstChild is not the last flow block
   4662            // therefore zero out the running margin value.
   4663 #ifdef NOISY_BLOCK_DIR_MARGINS
   4664            ListTag(stdout);
   4665            printf(": reflow incomplete, frame=");
   4666            frame->ListTag(stdout);
   4667            printf(" prevBEndMargin=%d, setting to zero\n",
   4668                   aState.mPrevBEndMargin.get());
   4669 #endif
   4670            aState.mPrevBEndMargin.Zero();
   4671          } else {  // frame is complete but its overflow is not complete
   4672            // Disconnect the next-in-flow and put it in our overflow tracker
   4673            if (!madeContinuation &&
   4674                !nextFrame->HasAnyStateBits(NS_FRAME_IS_OVERFLOW_CONTAINER)) {
   4675              // It already exists, but as a normal next-in-flow, so we need
   4676              // to dig it out of the child lists.
   4677              nextFrame->GetParent()->StealFrame(nextFrame);
   4678            } else if (madeContinuation) {
   4679              mFrames.RemoveFrame(nextFrame);
   4680            }
   4681 
   4682            // Put it in our overflow list
   4683            aState.mOverflowTracker->Insert(nextFrame, frameReflowStatus);
   4684            aState.mReflowStatus.MergeCompletionStatusFrom(frameReflowStatus);
   4685 
   4686 #ifdef NOISY_BLOCK_DIR_MARGINS
   4687            ListTag(stdout);
   4688            printf(": reflow complete but overflow incomplete for ");
   4689            frame->ListTag(stdout);
   4690            printf(" prevBEndMargin=%d collapsedBEndMargin=%d\n",
   4691                   aState.mPrevBEndMargin.get(), collapsedBEndMargin.get());
   4692 #endif
   4693            aState.mPrevBEndMargin = collapsedBEndMargin;
   4694          }
   4695        } else {  // frame is fully complete
   4696 #ifdef NOISY_BLOCK_DIR_MARGINS
   4697          ListTag(stdout);
   4698          printf(": reflow complete for ");
   4699          frame->ListTag(stdout);
   4700          printf(" prevBEndMargin=%d collapsedBEndMargin=%d\n",
   4701                 aState.mPrevBEndMargin.get(), collapsedBEndMargin.get());
   4702 #endif
   4703          aState.mPrevBEndMargin = collapsedBEndMargin;
   4704        }
   4705 #ifdef NOISY_BLOCK_DIR_MARGINS
   4706        ListTag(stdout);
   4707        printf(": frame=");
   4708        frame->ListTag(stdout);
   4709        printf(" carriedOutBEndMargin=%d collapsedBEndMargin=%d => %d\n",
   4710               brc.GetCarriedOutBEndMargin().get(), collapsedBEndMargin.get(),
   4711               aState.mPrevBEndMargin.get());
   4712 #endif
   4713      } else {
   4714        if (!frameReflowStatus.IsFullyComplete()) {
   4715          // The frame reported an incomplete status, but then it also didn't
   4716          // fit.  This means we need to reflow it again so that it can
   4717          // (again) report the incomplete status.
   4718          frame->AddStateBits(NS_FRAME_HAS_DIRTY_CHILDREN);
   4719        }
   4720 
   4721        if ((aLine == mLines.front() && !GetPrevInFlow()) ||
   4722            ShouldAvoidBreakInside(aState.mReflowInput)) {
   4723          // If it's our very first line *or* we're not at the top of the page
   4724          // and we have page-break-inside:avoid, then we need to be pushed to
   4725          // our parent's next-in-flow.
   4726          SetBreakBeforeStatusBeforeLine(aState, aLine, aKeepReflowGoing);
   4727        } else {
   4728          // Push the line that didn't fit and any lines that follow it
   4729          // to our next-in-flow.
   4730          PushTruncatedLine(aState, aLine, aKeepReflowGoing);
   4731        }
   4732      }
   4733    }
   4734    break;  // out of the reflow retry loop
   4735  }
   4736 
   4737 #ifdef DEBUG
   4738  VerifyLines(true);
   4739 #endif
   4740 }
   4741 
   4742 // Returns true if an overflow-wrap break was used.
   4743 bool nsBlockFrame::ReflowInlineFrames(BlockReflowState& aState,
   4744                                      LineIterator aLine,
   4745                                      bool* aKeepReflowGoing) {
   4746  *aKeepReflowGoing = true;
   4747  bool usedOverflowWrap = false;
   4748 
   4749  aLine->SetLineIsImpactedByFloat(false);
   4750 
   4751  // Setup initial coordinate system for reflowing the inline frames
   4752  // into. Apply a previous block frame's block-end margin first.
   4753  if (ShouldApplyBStartMargin(aState, aLine)) {
   4754    aState.mBCoord += aState.mPrevBEndMargin.Get();
   4755  }
   4756  nsFlowAreaRect floatAvailableSpace =
   4757      aState.GetFloatAvailableSpace(GetWritingMode());
   4758 
   4759  LineReflowStatus lineReflowStatus;
   4760  do {
   4761    nscoord availableSpaceBSize = 0;
   4762    aState.mLineBSize.reset();
   4763    do {
   4764      bool allowPullUp = true;
   4765      nsIFrame* forceBreakInFrame = nullptr;
   4766      int32_t forceBreakOffset = -1;
   4767      gfxBreakPriority forceBreakPriority = gfxBreakPriority::eNoBreak;
   4768      do {
   4769        nsFloatManager::SavedState floatManagerState;
   4770        aState.FloatManager()->PushState(&floatManagerState);
   4771 
   4772        // Once upon a time we allocated the first 30 nsLineLayout objects
   4773        // on the stack, and then we switched to the heap.  At that time
   4774        // these objects were large (1100 bytes on a 32 bit system).
   4775        // Then the nsLineLayout object was shrunk to 156 bytes by
   4776        // removing some internal buffers.  Given that it is so much
   4777        // smaller, the complexity of 2 different ways of allocating
   4778        // no longer makes sense.  Now we always allocate on the stack.
   4779        nsLineLayout lineLayout(aState.mPresContext, aState.FloatManager(),
   4780                                aState.mReflowInput, &aLine, nullptr);
   4781        lineLayout.Init(&aState, aState.mMinLineHeight, aState.mLineNumber);
   4782        if (forceBreakInFrame) {
   4783          lineLayout.ForceBreakAtPosition(forceBreakInFrame, forceBreakOffset);
   4784        }
   4785        DoReflowInlineFrames(aState, lineLayout, aLine, floatAvailableSpace,
   4786                             availableSpaceBSize, &floatManagerState,
   4787                             aKeepReflowGoing, &lineReflowStatus, allowPullUp);
   4788        usedOverflowWrap = lineLayout.EndLineReflow();
   4789 
   4790        if (LineReflowStatus::RedoNoPull == lineReflowStatus ||
   4791            LineReflowStatus::RedoMoreFloats == lineReflowStatus ||
   4792            LineReflowStatus::RedoNextBand == lineReflowStatus) {
   4793          if (lineLayout.NeedsBackup()) {
   4794            NS_ASSERTION(!forceBreakInFrame,
   4795                         "Backing up twice; this should never be necessary");
   4796            // If there is no saved break position, then this will set
   4797            // set forceBreakInFrame to null and we won't back up, which is
   4798            // correct.
   4799            forceBreakInFrame = lineLayout.GetLastOptionalBreakPosition(
   4800                &forceBreakOffset, &forceBreakPriority);
   4801          } else {
   4802            forceBreakInFrame = nullptr;
   4803          }
   4804          // restore the float manager state
   4805          aState.FloatManager()->PopState(&floatManagerState);
   4806          // Clear out float lists
   4807          aState.mCurrentLineFloats.Clear();
   4808          aState.mBelowCurrentLineFloats.Clear();
   4809          aState.mNoWrapFloats.Clear();
   4810        }
   4811 
   4812        // Don't allow pullup on a subsequent LineReflowStatus::RedoNoPull pass
   4813        allowPullUp = false;
   4814      } while (LineReflowStatus::RedoNoPull == lineReflowStatus);
   4815    } while (LineReflowStatus::RedoMoreFloats == lineReflowStatus);
   4816  } while (LineReflowStatus::RedoNextBand == lineReflowStatus);
   4817 
   4818  return usedOverflowWrap;
   4819 }
   4820 
   4821 void nsBlockFrame::SetBreakBeforeStatusBeforeLine(BlockReflowState& aState,
   4822                                                  LineIterator aLine,
   4823                                                  bool* aKeepReflowGoing) {
   4824  aState.mReflowStatus.SetInlineLineBreakBeforeAndReset();
   4825  // Reflow the line again when we reflow at our new position.
   4826  aLine->MarkDirty();
   4827  *aKeepReflowGoing = false;
   4828 }
   4829 
   4830 void nsBlockFrame::PushTruncatedLine(
   4831    BlockReflowState& aState, LineIterator aLine, bool* aKeepReflowGoing,
   4832    ComputeNewPageNameIfNeeded aComputeNewPageName) {
   4833  PushLines(aState, aLine.prev());
   4834  *aKeepReflowGoing = false;
   4835 
   4836  if (aComputeNewPageName == ComputeNewPageNameIfNeeded::Yes) {
   4837    // mCanHaveClassABreakpoints can only be true during paginated reflow, and
   4838    // we expect this function to only be called when the available bsize is
   4839    // constrained.
   4840    const WritingMode wm = GetWritingMode();
   4841    const bool canBreakForPageNames =
   4842        aState.mReflowInput.mFlags.mCanHaveClassABreakpoints &&
   4843        !PresShell()->GetRootFrame()->GetWritingMode().IsOrthogonalTo(wm);
   4844    if (canBreakForPageNames) {
   4845      PresShell()->FrameConstructor()->MaybeSetNextPageContentFramePageName(
   4846          aLine->mFirstChild);
   4847    }
   4848  }
   4849  aState.mReflowStatus.SetIncomplete();
   4850 }
   4851 
   4852 void nsBlockFrame::DoReflowInlineFrames(
   4853    BlockReflowState& aState, nsLineLayout& aLineLayout, LineIterator aLine,
   4854    nsFlowAreaRect& aFloatAvailableSpace, nscoord& aAvailableSpaceBSize,
   4855    nsFloatManager::SavedState* aFloatStateBeforeLine, bool* aKeepReflowGoing,
   4856    LineReflowStatus* aLineReflowStatus, bool aAllowPullUp) {
   4857  // Forget all of the floats on the line
   4858  aLine->ClearFloats();
   4859  aState.mFloatOverflowAreas.Clear();
   4860 
   4861  // We need to set this flag on the line if any of our reflow passes
   4862  // are impacted by floats.
   4863  if (aFloatAvailableSpace.HasFloats()) {
   4864    aLine->SetLineIsImpactedByFloat(true);
   4865  }
   4866 #ifdef REALLY_NOISY_REFLOW
   4867  printf("nsBlockFrame::DoReflowInlineFrames %p impacted = %d\n", this,
   4868         aFloatAvailableSpace.HasFloats());
   4869 #endif
   4870 
   4871  WritingMode outerWM = aState.mReflowInput.GetWritingMode();
   4872  WritingMode lineWM = WritingModeForLine(outerWM, aLine->mFirstChild);
   4873  LogicalRect lineRect = aFloatAvailableSpace.mRect.ConvertTo(
   4874      lineWM, outerWM, aState.ContainerSize());
   4875 
   4876  nscoord iStart = lineRect.IStart(lineWM);
   4877  nscoord availISize = lineRect.ISize(lineWM);
   4878  nscoord availBSize;
   4879  if (aState.mReflowInput.AvailableBSize() == NS_UNCONSTRAINEDSIZE) {
   4880    availBSize = NS_UNCONSTRAINEDSIZE;
   4881  } else {
   4882    /* XXX get the height right! */
   4883    availBSize = lineRect.BSize(lineWM);
   4884  }
   4885 
   4886  // Make sure to enable resize optimization before we call BeginLineReflow
   4887  // because it might get disabled there
   4888  aLine->EnableResizeReflowOptimization();
   4889 
   4890  aLineLayout.BeginLineReflow(iStart, aState.mBCoord, availISize, availBSize,
   4891                              aFloatAvailableSpace.HasFloats(),
   4892                              false /*XXX isTopOfPage*/, lineWM,
   4893                              aState.mContainerSize, aState.mInsetForBalance);
   4894 
   4895  aState.mFlags.mIsLineLayoutEmpty = false;
   4896 
   4897  // XXX Unfortunately we need to know this before reflowing the first
   4898  // inline frame in the line. FIX ME.
   4899  if (0 == aLineLayout.GetLineNumber() &&
   4900      HasAllStateBits(NS_BLOCK_HAS_FIRST_LETTER_CHILD |
   4901                      NS_BLOCK_HAS_FIRST_LETTER_STYLE)) {
   4902    aLineLayout.SetFirstLetterStyleOK(true);
   4903  }
   4904  NS_ASSERTION(!(HasAnyStateBits(NS_BLOCK_HAS_FIRST_LETTER_CHILD) &&
   4905                 GetPrevContinuation()),
   4906               "first letter child bit should only be on first continuation");
   4907 
   4908  // Reflow the frames that are already on the line first
   4909  LineReflowStatus lineReflowStatus = LineReflowStatus::OK;
   4910  int32_t i;
   4911  nsIFrame* frame = aLine->mFirstChild;
   4912 
   4913  if (aFloatAvailableSpace.HasFloats()) {
   4914    // There is a soft break opportunity at the start of the line, because
   4915    // we can always move this line down below float(s).
   4916    if (aLineLayout.NotifyOptionalBreakPosition(
   4917            frame, 0, true, gfxBreakPriority::eNormalBreak)) {
   4918      lineReflowStatus = LineReflowStatus::RedoNextBand;
   4919    }
   4920  }
   4921 
   4922  // need to repeatedly call GetChildCount here, because the child
   4923  // count can change during the loop!
   4924  for (i = 0;
   4925       LineReflowStatus::OK == lineReflowStatus && i < aLine->GetChildCount();
   4926       i++, frame = frame->GetNextSibling()) {
   4927    SetLineCursorForDisplay(aLine);
   4928    ReflowInlineFrame(aState, aLineLayout, aLine, frame, &lineReflowStatus);
   4929    if (LineReflowStatus::OK != lineReflowStatus) {
   4930      // It is possible that one or more of next lines are empty
   4931      // (because of DeleteNextInFlowChild). If so, delete them now
   4932      // in case we are finished.
   4933      ++aLine;
   4934      while ((aLine != LinesEnd()) && (0 == aLine->GetChildCount())) {
   4935        // XXX Is this still necessary now that DeleteNextInFlowChild
   4936        // uses DoRemoveFrame?
   4937        nsLineBox* toremove = aLine;
   4938        aLine = mLines.erase(aLine);
   4939        NS_ASSERTION(nullptr == toremove->mFirstChild, "bad empty line");
   4940        FreeLineBox(toremove);
   4941        ClearLineCursors();
   4942      }
   4943      --aLine;
   4944 
   4945      NS_ASSERTION(lineReflowStatus != LineReflowStatus::Truncated,
   4946                   "ReflowInlineFrame should never determine that a line "
   4947                   "needs to go to the next page/column");
   4948    }
   4949  }
   4950 
   4951  // Don't pull up new frames into lines with continuation placeholders
   4952  if (aAllowPullUp) {
   4953    // Pull frames and reflow them until we can't
   4954    while (LineReflowStatus::OK == lineReflowStatus) {
   4955      frame = PullFrame(aState, aLine);
   4956      if (!frame) {
   4957        break;
   4958      }
   4959 
   4960      while (LineReflowStatus::OK == lineReflowStatus) {
   4961        int32_t oldCount = aLine->GetChildCount();
   4962        SetLineCursorForDisplay(aLine);
   4963        ReflowInlineFrame(aState, aLineLayout, aLine, frame, &lineReflowStatus);
   4964        if (aLine->GetChildCount() != oldCount) {
   4965          // We just created a continuation for aFrame AND its going
   4966          // to end up on this line (e.g. :first-letter
   4967          // situation). Therefore we have to loop here before trying
   4968          // to pull another frame.
   4969          frame = frame->GetNextSibling();
   4970        } else {
   4971          break;
   4972        }
   4973      }
   4974    }
   4975  }
   4976  ClearLineCursors();
   4977 
   4978  aState.mFlags.mIsLineLayoutEmpty = aLineLayout.LineIsEmpty();
   4979 
   4980  // We only need to backup if the line isn't going to be reflowed again anyway
   4981  bool needsBackup = aLineLayout.NeedsBackup() &&
   4982                     (lineReflowStatus == LineReflowStatus::Stop ||
   4983                      lineReflowStatus == LineReflowStatus::OK);
   4984  if (needsBackup && aLineLayout.HaveForcedBreakPosition()) {
   4985    NS_WARNING(
   4986        "We shouldn't be backing up more than once! "
   4987        "Someone must have set a break opportunity beyond the available width, "
   4988        "even though there were better break opportunities before it");
   4989    needsBackup = false;
   4990  }
   4991  if (needsBackup) {
   4992    // We need to try backing up to before a text run
   4993    // XXX It's possible, in fact not unusual, for the break opportunity to
   4994    // already be the end of the line. We should detect that and optimize to not
   4995    // re-do the line.
   4996    if (aLineLayout.HasOptionalBreakPosition()) {
   4997      // We can back up!
   4998      lineReflowStatus = LineReflowStatus::RedoNoPull;
   4999    }
   5000  } else {
   5001    // In case we reflow this line again, remember that we don't
   5002    // need to force any breaking
   5003    aLineLayout.ClearOptionalBreakPosition();
   5004  }
   5005 
   5006  if (LineReflowStatus::RedoNextBand == lineReflowStatus) {
   5007    // This happens only when we have a line that is impacted by
   5008    // floats and the first element in the line doesn't fit with
   5009    // the floats.
   5010    //
   5011    // If there's block space available, we either try to reflow the line
   5012    // past the current band (if it's non-zero and the band definitely won't
   5013    // widen around a shape-outside), otherwise we try one pixel down. If
   5014    // there's no block space available, we push the line to the next
   5015    // page/column.
   5016    NS_ASSERTION(
   5017        NS_UNCONSTRAINEDSIZE != aFloatAvailableSpace.mRect.BSize(outerWM),
   5018        "unconstrained block size on totally empty line");
   5019 
   5020    // See the analogous code for blocks in BlockReflowState::ClearFloats.
   5021    nscoord bandBSize = aFloatAvailableSpace.mRect.BSize(outerWM);
   5022    if (bandBSize > 0 ||
   5023        NS_UNCONSTRAINEDSIZE == aState.mReflowInput.AvailableBSize()) {
   5024      NS_ASSERTION(bandBSize == 0 || aFloatAvailableSpace.HasFloats(),
   5025                   "redo line on totally empty line with non-empty band...");
   5026      // We should never hit this case if we've placed floats on the
   5027      // line; if we have, then the GetFloatAvailableSpace call is wrong
   5028      // and needs to happen after the caller pops the float manager
   5029      // state.
   5030      aState.FloatManager()->AssertStateMatches(aFloatStateBeforeLine);
   5031 
   5032      if (!aFloatAvailableSpace.MayWiden() && bandBSize > 0) {
   5033        // Move it down far enough to clear the current band.
   5034        aState.mBCoord += bandBSize;
   5035      } else {
   5036        // Move it down by one dev pixel.
   5037        aState.mBCoord += aState.mPresContext->DevPixelsToAppUnits(1);
   5038      }
   5039 
   5040      aFloatAvailableSpace = aState.GetFloatAvailableSpace(GetWritingMode());
   5041    } else {
   5042      // There's nowhere to retry placing the line, so we want to push
   5043      // it to the next page/column where its contents can fit not
   5044      // next to a float.
   5045      lineReflowStatus = LineReflowStatus::Truncated;
   5046      PushTruncatedLine(aState, aLine, aKeepReflowGoing);
   5047    }
   5048 
   5049    // XXX: a small optimization can be done here when paginating:
   5050    // if the new Y coordinate is past the end of the block then
   5051    // push the line and return now instead of later on after we are
   5052    // past the float.
   5053  } else if (LineReflowStatus::Truncated != lineReflowStatus &&
   5054             LineReflowStatus::RedoNoPull != lineReflowStatus) {
   5055    // If we are propagating out a break-before status then there is
   5056    // no point in placing the line.
   5057    if (!aState.mReflowStatus.IsInlineBreakBefore()) {
   5058      if (!PlaceLine(aState, aLineLayout, aLine, aFloatStateBeforeLine,
   5059                     aFloatAvailableSpace, aAvailableSpaceBSize,
   5060                     aKeepReflowGoing)) {
   5061        lineReflowStatus = LineReflowStatus::RedoMoreFloats;
   5062        // PlaceLine already called GetFloatAvailableSpaceForBSize or its
   5063        // variant for us.
   5064      }
   5065    }
   5066  }
   5067 #ifdef DEBUG
   5068  if (gNoisyReflow) {
   5069    IndentBy(stdout, gNoiseIndent);
   5070    fmt::println(FMT_STRING("LineReflowStatus={}"), ToString(lineReflowStatus));
   5071  }
   5072 #endif
   5073 
   5074  if (aLineLayout.GetDirtyNextLine()) {
   5075    // aLine may have been pushed to the overflow lines.
   5076    FrameLines* overflowLines = GetOverflowLines();
   5077    // We can't just compare iterators front() to aLine here, since they may be
   5078    // in different lists.
   5079    bool pushedToOverflowLines =
   5080        overflowLines && overflowLines->mLines.front() == aLine.get();
   5081    if (pushedToOverflowLines) {
   5082      // aLine is stale, it's associated with the main line list but it should
   5083      // be associated with the overflow line list now
   5084      aLine = overflowLines->mLines.begin();
   5085    }
   5086    nsBlockInFlowLineIterator iter(this, aLine, pushedToOverflowLines);
   5087    if (iter.Next() && iter.GetLine()->IsInline()) {
   5088      iter.GetLine()->MarkDirty();
   5089      if (iter.GetContainer() != this) {
   5090        aState.mReflowStatus.SetNextInFlowNeedsReflow();
   5091      }
   5092    }
   5093  }
   5094 
   5095  *aLineReflowStatus = lineReflowStatus;
   5096 }
   5097 
   5098 /**
   5099 * Reflow an inline frame. The reflow status is mapped from the frames
   5100 * reflow status to the lines reflow status (not to our reflow status).
   5101 * The line reflow status is simple: true means keep placing frames
   5102 * on the line; false means don't (the line is done). If the line
   5103 * has some sort of breaking affect then aLine's break-type will be set
   5104 * to something other than UsedClear::None.
   5105 */
   5106 void nsBlockFrame::ReflowInlineFrame(BlockReflowState& aState,
   5107                                     nsLineLayout& aLineLayout,
   5108                                     LineIterator aLine, nsIFrame* aFrame,
   5109                                     LineReflowStatus* aLineReflowStatus) {
   5110  MOZ_ASSERT(aFrame);
   5111  *aLineReflowStatus = LineReflowStatus::OK;
   5112 
   5113 #ifdef NOISY_FIRST_LETTER
   5114  ListTag(stdout);
   5115  printf(": reflowing ");
   5116  aFrame->ListTag(stdout);
   5117  printf(" reflowingFirstLetter=%s\n",
   5118         aLineLayout.GetFirstLetterStyleOK() ? "on" : "off");
   5119 #endif
   5120 
   5121  if (aFrame->IsPlaceholderFrame()) {
   5122    auto ph = static_cast<nsPlaceholderFrame*>(aFrame);
   5123    ph->ForgetLineIsEmptySoFar();
   5124  }
   5125 
   5126  // Reflow the inline frame
   5127  nsReflowStatus frameReflowStatus;
   5128  bool pushedFrame;
   5129  aLineLayout.ReflowFrame(aFrame, frameReflowStatus, nullptr, pushedFrame);
   5130 
   5131  if (frameReflowStatus.NextInFlowNeedsReflow()) {
   5132    aLineLayout.SetDirtyNextLine();
   5133  }
   5134 
   5135 #ifdef REALLY_NOISY_REFLOW
   5136  aFrame->ListTag(stdout);
   5137  printf(": status=%s\n", ToString(frameReflowStatus).c_str());
   5138 #endif
   5139 
   5140 #if defined(REFLOW_STATUS_COVERAGE)
   5141  RecordReflowStatus(false, frameReflowStatus);
   5142 #endif
   5143 
   5144  // Send post-reflow notification
   5145  aState.mPrevChild = aFrame;
   5146 
   5147  /* XXX
   5148     This is where we need to add logic to handle some odd behavior.
   5149     For one thing, we should usually place at least one thing next
   5150     to a left float, even when that float takes up all the width on a line.
   5151     see bug 22496
   5152  */
   5153 
   5154  // Process the child frames reflow status. There are 5 cases:
   5155  // complete, not-complete, break-before, break-after-complete,
   5156  // break-after-not-complete. There are two situations: we are a
   5157  // block or we are an inline. This makes a total of 10 cases
   5158  // (fortunately, there is some overlap).
   5159  aLine->ClearForcedLineBreak();
   5160  if (frameReflowStatus.IsInlineBreak() ||
   5161      aState.mTrailingClearFromPIF != UsedClear::None) {
   5162    // Always abort the line reflow (because a line break is the
   5163    // minimal amount of break we do).
   5164    *aLineReflowStatus = LineReflowStatus::Stop;
   5165 
   5166    // XXX what should aLine's break-type be set to in all these cases?
   5167    if (frameReflowStatus.IsInlineBreakBefore()) {
   5168      // Break-before cases.
   5169      if (aFrame == aLine->mFirstChild) {
   5170        // If we break before the first frame on the line then we must
   5171        // be trying to place content where there's no room (e.g. on a
   5172        // line with wide floats). Inform the caller to reflow the
   5173        // line after skipping past a float.
   5174        *aLineReflowStatus = LineReflowStatus::RedoNextBand;
   5175      } else {
   5176        // It's not the first child on this line so go ahead and split
   5177        // the line. We will see the frame again on the next-line.
   5178        SplitLine(aState, aLineLayout, aLine, aFrame, aLineReflowStatus);
   5179 
   5180        // If we're splitting the line because the frame didn't fit and it
   5181        // was pushed, then mark the line as having word wrapped. We need to
   5182        // know that if we're shrink wrapping our width
   5183        if (pushedFrame) {
   5184          aLine->SetLineWrapped(true);
   5185        }
   5186      }
   5187    } else {
   5188      MOZ_ASSERT(frameReflowStatus.IsInlineBreakAfter() ||
   5189                     aState.mTrailingClearFromPIF != UsedClear::None,
   5190                 "We should've handled inline break-before in the if-branch!");
   5191 
   5192      // If a float split and its prev-in-flow was followed by a <BR>, then
   5193      // combine the <BR>'s float clear type with the inline's float clear type
   5194      // (the inline will be the very next frame after the split float).
   5195      UsedClear clearType = frameReflowStatus.FloatClearType();
   5196      if (aState.mTrailingClearFromPIF != UsedClear::None) {
   5197        clearType = nsLayoutUtils::CombineClearType(
   5198            clearType, aState.mTrailingClearFromPIF);
   5199        aState.mTrailingClearFromPIF = UsedClear::None;
   5200      }
   5201      // Break-after cases
   5202      if (clearType != UsedClear::None || aLineLayout.GetLineEndsInBR()) {
   5203        aLine->SetForcedLineBreakAfter(clearType);
   5204      }
   5205      if (frameReflowStatus.IsComplete()) {
   5206        // Split line, but after the frame just reflowed
   5207        SplitLine(aState, aLineLayout, aLine, aFrame->GetNextSibling(),
   5208                  aLineReflowStatus);
   5209 
   5210        if (frameReflowStatus.IsInlineBreakAfter() &&
   5211            !aLineLayout.GetLineEndsInBR()) {
   5212          aLineLayout.SetDirtyNextLine();
   5213        }
   5214      }
   5215    }
   5216  }
   5217 
   5218  if (!frameReflowStatus.IsFullyComplete()) {
   5219    // Create a continuation for the incomplete frame. Note that the
   5220    // frame may already have a continuation.
   5221    CreateContinuationFor(aState, aLine, aFrame);
   5222 
   5223    // Remember that the line has wrapped
   5224    if (!aLineLayout.GetLineEndsInBR()) {
   5225      aLine->SetLineWrapped(true);
   5226    }
   5227 
   5228    // If we just ended a first-letter frame or reflowed a placeholder then
   5229    // don't split the line and don't stop the line reflow...
   5230    // But if we are going to stop anyways we'd better split the line.
   5231    if ((!frameReflowStatus.FirstLetterComplete() &&
   5232         !aFrame->IsPlaceholderFrame()) ||
   5233        *aLineReflowStatus == LineReflowStatus::Stop) {
   5234      // Split line after the current frame
   5235      *aLineReflowStatus = LineReflowStatus::Stop;
   5236      SplitLine(aState, aLineLayout, aLine, aFrame->GetNextSibling(),
   5237                aLineReflowStatus);
   5238    }
   5239  }
   5240 }
   5241 
   5242 bool nsBlockFrame::CreateContinuationFor(BlockReflowState& aState,
   5243                                         nsLineBox* aLine, nsIFrame* aFrame) {
   5244  nsIFrame* newFrame = nullptr;
   5245 
   5246  if (!aFrame->GetNextInFlow()) {
   5247    newFrame =
   5248        PresShell()->FrameConstructor()->CreateContinuingFrame(aFrame, this);
   5249 
   5250    mFrames.InsertFrame(nullptr, aFrame, newFrame);
   5251 
   5252    if (aLine) {
   5253      aLine->NoteFrameAdded(newFrame);
   5254    }
   5255  }
   5256 #ifdef DEBUG
   5257  VerifyLines(false);
   5258 #endif
   5259  return !!newFrame;
   5260 }
   5261 
   5262 void nsBlockFrame::SplitFloat(BlockReflowState& aState, nsIFrame* aFloat,
   5263                              const nsReflowStatus& aFloatStatus) {
   5264  MOZ_ASSERT(!aFloatStatus.IsFullyComplete(),
   5265             "why split the frame if it's fully complete?");
   5266  MOZ_ASSERT(aState.mBlock == this);
   5267 
   5268  nsIFrame* nextInFlow = aFloat->GetNextInFlow();
   5269  if (nextInFlow) {
   5270    nsContainerFrame* oldParent = nextInFlow->GetParent();
   5271    oldParent->StealFrame(nextInFlow);
   5272    if (oldParent != this) {
   5273      ReparentFrame(nextInFlow, oldParent, this);
   5274    }
   5275    if (!aFloatStatus.IsOverflowIncomplete()) {
   5276      nextInFlow->RemoveStateBits(NS_FRAME_IS_OVERFLOW_CONTAINER);
   5277    }
   5278  } else {
   5279    nextInFlow =
   5280        PresShell()->FrameConstructor()->CreateContinuingFrame(aFloat, this);
   5281  }
   5282  if (aFloatStatus.IsOverflowIncomplete()) {
   5283    nextInFlow->AddStateBits(NS_FRAME_IS_OVERFLOW_CONTAINER);
   5284  }
   5285 
   5286  UsedFloat floatStyle =
   5287      aFloat->StyleDisplay()->UsedFloat(aState.mReflowInput.GetWritingMode());
   5288  if (floatStyle == UsedFloat::Left) {
   5289    aState.FloatManager()->SetSplitLeftFloatAcrossBreak();
   5290  } else {
   5291    MOZ_ASSERT(floatStyle == UsedFloat::Right, "Unexpected float side!");
   5292    aState.FloatManager()->SetSplitRightFloatAcrossBreak();
   5293  }
   5294 
   5295  aState.AppendPushedFloatChain(nextInFlow);
   5296  if (MOZ_LIKELY(!HasAnyStateBits(NS_BLOCK_BFC)) ||
   5297      MOZ_UNLIKELY(IsTrueOverflowContainer())) {
   5298    aState.mReflowStatus.SetOverflowIncomplete();
   5299  } else {
   5300    aState.mReflowStatus.SetIncomplete();
   5301  }
   5302 }
   5303 
   5304 static bool CheckPlaceholderInLine(nsIFrame* aBlock, nsLineBox* aLine,
   5305                                   nsIFrame* aFloat) {
   5306  if (!aFloat) {
   5307    return true;
   5308  }
   5309  NS_ASSERTION(!aFloat->GetPrevContinuation(),
   5310               "float in a line should never be a continuation");
   5311  NS_ASSERTION(!aFloat->HasAnyStateBits(NS_FRAME_IS_PUSHED_OUT_OF_FLOW),
   5312               "float in a line should never be a pushed float");
   5313  nsIFrame* ph = aFloat->FirstInFlow()->GetPlaceholderFrame();
   5314  for (nsIFrame* f = ph; f; f = f->GetParent()) {
   5315    if (f->GetParent() == aBlock) {
   5316      return aLine->Contains(f);
   5317    }
   5318  }
   5319  NS_ASSERTION(false, "aBlock is not an ancestor of aFrame!");
   5320  return true;
   5321 }
   5322 
   5323 void nsBlockFrame::SplitLine(BlockReflowState& aState,
   5324                             nsLineLayout& aLineLayout, LineIterator aLine,
   5325                             nsIFrame* aFrame,
   5326                             LineReflowStatus* aLineReflowStatus) {
   5327  MOZ_ASSERT(aLine->IsInline(), "illegal SplitLine on block line");
   5328 
   5329  int32_t pushCount =
   5330      aLine->GetChildCount() - aLineLayout.GetCurrentSpanCount();
   5331  MOZ_ASSERT(pushCount >= 0, "bad push count");
   5332 
   5333 #ifdef DEBUG
   5334  if (gNoisyReflow) {
   5335    nsIFrame::IndentBy(stdout, gNoiseIndent);
   5336    printf("split line: from line=%p pushCount=%d aFrame=",
   5337           static_cast<void*>(aLine.get()), pushCount);
   5338    if (aFrame) {
   5339      aFrame->ListTag(stdout);
   5340    } else {
   5341      printf("(null)");
   5342    }
   5343    printf("\n");
   5344    if (gReallyNoisyReflow) {
   5345      aLine->List(stdout, gNoiseIndent + 1);
   5346    }
   5347  }
   5348 #endif
   5349 
   5350  if (0 != pushCount) {
   5351    MOZ_ASSERT(aLine->GetChildCount() > pushCount, "bad push");
   5352    MOZ_ASSERT(nullptr != aFrame, "whoops");
   5353 #ifdef DEBUG
   5354    {
   5355      nsIFrame* f = aFrame;
   5356      int32_t count = pushCount;
   5357      while (f && count > 0) {
   5358        f = f->GetNextSibling();
   5359        --count;
   5360      }
   5361      NS_ASSERTION(count == 0, "Not enough frames to push");
   5362    }
   5363 #endif
   5364 
   5365    // Put frames being split out into their own line
   5366    nsLineBox* newLine = NewLineBox(aLine, aFrame, pushCount);
   5367    mLines.after_insert(aLine, newLine);
   5368 #ifdef DEBUG
   5369    if (gReallyNoisyReflow) {
   5370      newLine->List(stdout, gNoiseIndent + 1);
   5371    }
   5372 #endif
   5373 
   5374    // Let line layout know that some frames are no longer part of its
   5375    // state.
   5376    aLineLayout.SplitLineTo(aLine->GetChildCount());
   5377 
   5378    // If floats have been placed whose placeholders have been pushed to the new
   5379    // line, we need to reflow the old line again. We don't want to look at the
   5380    // frames in the new line, because as a large paragraph is laid out the
   5381    // we'd get O(N^2) performance. So instead we just check that the last
   5382    // float and the last below-current-line float are still in aLine.
   5383    if (!CheckPlaceholderInLine(
   5384            this, aLine,
   5385            aLine->HasFloats() ? aLine->Floats().LastElement() : nullptr) ||
   5386        !CheckPlaceholderInLine(
   5387            this, aLine,
   5388            aState.mBelowCurrentLineFloats.SafeLastElement(nullptr))) {
   5389      *aLineReflowStatus = LineReflowStatus::RedoNoPull;
   5390    }
   5391 
   5392 #ifdef DEBUG
   5393    VerifyLines(true);
   5394 #endif
   5395  }
   5396 }
   5397 
   5398 bool nsBlockFrame::IsLastLine(BlockReflowState& aState, LineIterator aLine) {
   5399  while (++aLine != LinesEnd()) {
   5400    // There is another line
   5401    if (0 != aLine->GetChildCount()) {
   5402      // If the next line is a block line then this line is the last in a
   5403      // group of inline lines.
   5404      return aLine->IsBlock();
   5405    }
   5406    // The next line is empty, try the next one
   5407  }
   5408 
   5409  // Try our next-in-flows lines to answer the question
   5410  nsBlockFrame* nextInFlow = (nsBlockFrame*)GetNextInFlow();
   5411  while (nullptr != nextInFlow) {
   5412    for (const auto& line : nextInFlow->Lines()) {
   5413      if (0 != line.GetChildCount()) {
   5414        return line.IsBlock();
   5415      }
   5416    }
   5417    nextInFlow = (nsBlockFrame*)nextInFlow->GetNextInFlow();
   5418  }
   5419 
   5420  // This is the last line - so don't allow justification
   5421  return true;
   5422 }
   5423 
   5424 bool nsBlockFrame::PlaceLine(BlockReflowState& aState,
   5425                             nsLineLayout& aLineLayout, LineIterator aLine,
   5426                             nsFloatManager::SavedState* aFloatStateBeforeLine,
   5427                             nsFlowAreaRect& aFlowArea,
   5428                             nscoord& aAvailableSpaceBSize,
   5429                             bool* aKeepReflowGoing) {
   5430  // Try to position the floats in a nowrap context.
   5431  aLineLayout.FlushNoWrapFloats();
   5432 
   5433  // Trim extra white-space from the line before placing the frames
   5434  aLineLayout.TrimTrailingWhiteSpace();
   5435 
   5436  // Vertically align the frames on this line.
   5437  //
   5438  // According to the CSS2 spec, section 12.6.1, the "marker" box
   5439  // participates in the height calculation of the list-item box's
   5440  // first line box.
   5441  //
   5442  // There are exactly two places a ::marker can be placed: near the
   5443  // first or second line. It's only placed on the second line in a
   5444  // rare case: when the first line is empty.
   5445  WritingMode wm = aState.mReflowInput.GetWritingMode();
   5446  bool addedMarker = false;
   5447  nsIFrame* outsideMarker = GetOutsideMarker();
   5448  if (outsideMarker &&
   5449      ((aLine == mLines.front() &&
   5450        (!aLineLayout.IsZeroBSize() || (aLine == mLines.back()))) ||
   5451       (mLines.front() != mLines.back() && 0 == mLines.front()->BSize() &&
   5452        aLine == mLines.begin().next()))) {
   5453    ReflowOutput metrics(aState.mReflowInput);
   5454    ReflowOutsideMarker(outsideMarker, aState, metrics, aState.mBCoord);
   5455    NS_ASSERTION(!MarkerIsEmpty(outsideMarker) || metrics.BSize(wm) == 0,
   5456                 "empty ::marker frame took up space");
   5457    aLineLayout.AddMarkerFrame(outsideMarker, metrics);
   5458    addedMarker = true;
   5459  }
   5460  aLineLayout.VerticalAlignLine();
   5461 
   5462  // We want to consider the floats in the current line when determining
   5463  // whether the float available space is shrunk. If mLineBSize doesn't
   5464  // exist, we are in the first pass trying to place the line. Calling
   5465  // GetFloatAvailableSpace() like we did in BlockReflowState::AddFloat()
   5466  // for UpdateBand().
   5467 
   5468  // floatAvailableSpaceWithOldLineBSize is the float available space with
   5469  // the old BSize, but including the floats that were added in this line.
   5470  LogicalRect floatAvailableSpaceWithOldLineBSize =
   5471      aState.mLineBSize.isNothing()
   5472          ? aState.GetFloatAvailableSpace(wm, aLine->BStart()).mRect
   5473          : aState
   5474                .GetFloatAvailableSpaceForBSize(
   5475                    wm, aLine->BStart(), aState.mLineBSize.value(), nullptr)
   5476                .mRect;
   5477 
   5478  // As we redo for floats, we can't reduce the amount of BSize we're
   5479  // checking.
   5480  aAvailableSpaceBSize = std::max(aAvailableSpaceBSize, aLine->BSize());
   5481  LogicalRect floatAvailableSpaceWithLineBSize =
   5482      aState
   5483          .GetFloatAvailableSpaceForBSize(wm, aLine->BStart(),
   5484                                          aAvailableSpaceBSize, nullptr)
   5485          .mRect;
   5486 
   5487  // If the available space between the floats is smaller now that we
   5488  // know the BSize, return false (and cause another pass with
   5489  // LineReflowStatus::RedoMoreFloats).  We ensure aAvailableSpaceBSize
   5490  // never decreases, which means that we can't reduce the set of floats
   5491  // we intersect, which means that the available space cannot grow.
   5492  if (AvailableSpaceShrunk(wm, floatAvailableSpaceWithOldLineBSize,
   5493                           floatAvailableSpaceWithLineBSize, false)) {
   5494    // Prepare data for redoing the line.
   5495    aState.mLineBSize = Some(aLine->BSize());
   5496 
   5497    // Since we want to redo the line, we update aFlowArea by using the
   5498    // aFloatStateBeforeLine, which is the float manager's state before the
   5499    // line is placed.
   5500    LogicalRect oldFloatAvailableSpace(aFlowArea.mRect);
   5501    aFlowArea = aState.GetFloatAvailableSpaceForBSize(
   5502        wm, aLine->BStart(), aAvailableSpaceBSize, aFloatStateBeforeLine);
   5503 
   5504    NS_ASSERTION(
   5505        aFlowArea.mRect.BStart(wm) == oldFloatAvailableSpace.BStart(wm),
   5506        "yikes");
   5507    // Restore the BSize to the position of the next band.
   5508    aFlowArea.mRect.BSize(wm) = oldFloatAvailableSpace.BSize(wm);
   5509 
   5510    // Enforce both IStart() and IEnd() never move outwards to prevent
   5511    // infinite grow-shrink loops.
   5512    const nscoord iStartDiff =
   5513        aFlowArea.mRect.IStart(wm) - oldFloatAvailableSpace.IStart(wm);
   5514    const nscoord iEndDiff =
   5515        aFlowArea.mRect.IEnd(wm) - oldFloatAvailableSpace.IEnd(wm);
   5516    if (iStartDiff < 0) {
   5517      aFlowArea.mRect.IStart(wm) -= iStartDiff;
   5518      aFlowArea.mRect.ISize(wm) += iStartDiff;
   5519    }
   5520    if (iEndDiff > 0) {
   5521      aFlowArea.mRect.ISize(wm) -= iEndDiff;
   5522    }
   5523 
   5524    return false;
   5525  }
   5526 
   5527 #ifdef DEBUG
   5528  if (!GetParent()->IsAbsurdSizeAssertSuppressed()) {
   5529    static nscoord lastHeight = 0;
   5530    if (ABSURD_SIZE(aLine->BStart())) {
   5531      lastHeight = aLine->BStart();
   5532      if (abs(aLine->BStart() - lastHeight) > ABSURD_COORD / 10) {
   5533        nsIFrame::ListTag(stdout);
   5534        printf(": line=%p y=%d line.bounds.height=%d\n",
   5535               static_cast<void*>(aLine.get()), aLine->BStart(),
   5536               aLine->BSize());
   5537      }
   5538    } else {
   5539      lastHeight = 0;
   5540    }
   5541  }
   5542 #endif
   5543 
   5544  // Only block frames horizontally align their children because
   5545  // inline frames "shrink-wrap" around their children (therefore
   5546  // there is no extra horizontal space).
   5547  const nsStyleText* styleText = StyleText();
   5548 
   5549  /**
   5550   * We don't care checking for IsLastLine properly if we don't care (if it
   5551   * can't change the used text-align value for the line).
   5552   *
   5553   * In other words, isLastLine really means isLastLineAndWeCare.
   5554   */
   5555  const bool isLastLine =
   5556      !IsInSVGTextSubtree() &&
   5557      styleText->TextAlignForLastLine() != styleText->mTextAlign &&
   5558      (aLineLayout.GetLineEndsInBR() || IsLastLine(aState, aLine));
   5559 
   5560  aLineLayout.TextAlignLine(aLine, isLastLine);
   5561 
   5562  // From here on, pfd->mBounds rectangles are incorrect because bidi
   5563  // might have moved frames around!
   5564  OverflowAreas overflowAreas;
   5565  aLineLayout.RelativePositionFrames(overflowAreas);
   5566  if (Style()->GetPseudoType() == PseudoStyleType::scrolledContent) {
   5567    Maybe<nsRect> inFlowBounds;
   5568    int32_t n = aLine->GetChildCount();
   5569    for (nsIFrame* lineFrame = aLine->mFirstChild; n > 0;
   5570         lineFrame = lineFrame->GetNextSibling(), --n) {
   5571      auto lineFrameBounds = GetLineFrameInFlowBounds(*aLine, *lineFrame);
   5572      if (!lineFrameBounds) {
   5573        continue;
   5574      }
   5575      if (inFlowBounds) {
   5576        *inFlowBounds = inFlowBounds->UnionEdges(*lineFrameBounds);
   5577      } else {
   5578        inFlowBounds = Some(*lineFrameBounds);
   5579      }
   5580    }
   5581    aLine->SetInFlowChildBounds(inFlowBounds);
   5582  }
   5583  aLine->SetOverflowAreas(overflowAreas);
   5584  if (addedMarker) {
   5585    aLineLayout.RemoveMarkerFrame(GetOutsideMarker());
   5586  }
   5587 
   5588  // Inline lines do not have margins themselves; however they are
   5589  // impacted by prior block margins. If this line ends up having some
   5590  // height then we zero out the previous block-end margin value that was
   5591  // already applied to the line's starting Y coordinate. Otherwise we
   5592  // leave it be so that the previous blocks block-end margin can be
   5593  // collapsed with a block that follows.
   5594  nscoord newBCoord;
   5595 
   5596  if (!aLine->CachedIsEmpty()) {
   5597    // This line has some height. Therefore the application of the
   5598    // previous-bottom-margin should stick.
   5599    aState.mPrevBEndMargin.Zero();
   5600    newBCoord = aLine->BEnd();
   5601  } else {
   5602    // Don't let the previous-bottom-margin value affect the newBCoord
   5603    // coordinate (it was applied in ReflowInlineFrames speculatively)
   5604    // since the line is empty.
   5605    // We already called |ShouldApplyBStartMargin|, and if we applied it
   5606    // then mShouldApplyBStartMargin is set.
   5607    nscoord dy = aState.mFlags.mShouldApplyBStartMargin
   5608                     ? -aState.mPrevBEndMargin.Get()
   5609                     : 0;
   5610    newBCoord = aState.mBCoord + dy;
   5611  }
   5612 
   5613  if (!aState.mReflowStatus.IsFullyComplete() &&
   5614      ShouldAvoidBreakInside(aState.mReflowInput)) {
   5615    aLine->AppendFloats(std::move(aState.mCurrentLineFloats));
   5616    SetBreakBeforeStatusBeforeLine(aState, aLine, aKeepReflowGoing);
   5617    return true;
   5618  }
   5619 
   5620  // See if the line fit (our first line always does).
   5621  if (mLines.front() != aLine &&
   5622      aState.ContentBSize() != NS_UNCONSTRAINEDSIZE &&
   5623      newBCoord > aState.ContentBEnd()) {
   5624    NS_ASSERTION(aState.mCurrentLine == aLine, "oops");
   5625    if (ShouldAvoidBreakInside(aState.mReflowInput)) {
   5626      // All our content doesn't fit, start on the next page.
   5627      SetBreakBeforeStatusBeforeLine(aState, aLine, aKeepReflowGoing);
   5628    } else {
   5629      // Push aLine and all of its children and anything else that
   5630      // follows to our next-in-flow.
   5631      PushTruncatedLine(aState, aLine, aKeepReflowGoing);
   5632    }
   5633    return true;
   5634  }
   5635 
   5636  // Note that any early return before this update of aState.mBCoord
   5637  // must either (a) return false or (b) set aKeepReflowGoing to false.
   5638  // Otherwise we'll keep reflowing later lines at an incorrect
   5639  // position, and we might not come back and clean up the damage later.
   5640  aState.mBCoord = newBCoord;
   5641 
   5642  // Add the already placed current-line floats to the line
   5643  aLine->AppendFloats(std::move(aState.mCurrentLineFloats));
   5644 
   5645  // Any below current line floats to place?
   5646  if (!aState.mBelowCurrentLineFloats.IsEmpty()) {
   5647    // Reflow the below-current-line floats, which places on the line's
   5648    // float list.
   5649    aState.PlaceBelowCurrentLineFloats(aLine);
   5650  }
   5651 
   5652  // When a line has floats, factor them into the overflow areas computations.
   5653  if (aLine->HasFloats()) {
   5654    // Union the float overflow areas (stored in aState) and the value computed
   5655    // by the line layout code.
   5656    OverflowAreas lineOverflowAreas = aState.mFloatOverflowAreas;
   5657    lineOverflowAreas.UnionWith(aLine->GetOverflowAreas());
   5658    aLine->SetOverflowAreas(lineOverflowAreas);
   5659    if (Style()->GetPseudoType() == PseudoStyleType::scrolledContent) {
   5660      Span<const nsIFrame* const> floats(aLine->Floats());
   5661      // Guaranteed to have at least 1 element since `HasFloats()` is true.
   5662      auto floatRect = GetNormalMarginRect(*floats[0]);
   5663      for (const nsIFrame* f : floats.From(1)) {
   5664        floatRect = floatRect.UnionEdges(GetNormalMarginRect(*f));
   5665      }
   5666      auto inFlowBounds = aLine->GetInFlowChildBounds();
   5667      aLine->SetInFlowChildBounds(
   5668          Some(inFlowBounds ? inFlowBounds->UnionEdges(floatRect) : floatRect));
   5669    }
   5670 
   5671 #ifdef NOISY_OVERFLOW_AREAS
   5672    printf("%s: Line %p, InkOverflowRect=%s, ScrollableOverflowRect=%s\n",
   5673           ListTag().get(), aLine.get(),
   5674           ToString(aLine->InkOverflowRect()).c_str(),
   5675           ToString(aLine->ScrollableOverflowRect()).c_str());
   5676 #endif
   5677  }
   5678 
   5679  // Apply break-after clearing if necessary
   5680  // This must stay in sync with |ReflowDirtyLines|.
   5681  if (aLine->HasFloatClearTypeAfter()) {
   5682    std::tie(aState.mBCoord, std::ignore) =
   5683        aState.ClearFloats(aState.mBCoord, aLine->FloatClearTypeAfter());
   5684  }
   5685  return true;
   5686 }
   5687 
   5688 void nsBlockFrame::PushLines(BlockReflowState& aState,
   5689                             nsLineList::iterator aLineBefore) {
   5690  // NOTE: aLineBefore is always a normal line, not an overflow line.
   5691  // The following expression will assert otherwise.
   5692  DebugOnly<bool> check = aLineBefore == mLines.begin();
   5693 
   5694  nsLineList::iterator overBegin(aLineBefore.next());
   5695 
   5696  // PushTruncatedPlaceholderLine sometimes pushes the first line.  Ugh.
   5697  bool firstLine = overBegin == LinesBegin();
   5698 
   5699  if (overBegin != LinesEnd()) {
   5700    // Remove floats in the lines from floats list.
   5701    nsFrameList floats;
   5702    CollectFloats(overBegin->mFirstChild, floats, true);
   5703 
   5704    if (floats.NotEmpty()) {
   5705 #ifdef DEBUG
   5706      for (nsIFrame* f : floats) {
   5707        MOZ_ASSERT(!f->HasAnyStateBits(NS_FRAME_IS_PUSHED_OUT_OF_FLOW),
   5708                   "CollectFloats should've removed that bit");
   5709      }
   5710 #endif
   5711      // Push the floats onto the front of the overflow out-of-flows list
   5712      nsAutoOOFFrameList oofs(this);
   5713      oofs.mList.InsertFrames(nullptr, nullptr, std::move(floats));
   5714    }
   5715 
   5716    // overflow lines can already exist in some cases, in particular,
   5717    // when shrinkwrapping and we discover that the shrinkwap causes
   5718    // the height of some child block to grow which creates additional
   5719    // overflowing content. In such cases we must prepend the new
   5720    // overflow to the existing overflow.
   5721    FrameLines* overflowLines = RemoveOverflowLines();
   5722    if (!overflowLines) {
   5723      // XXXldb use presshell arena!
   5724      overflowLines = new FrameLines();
   5725    }
   5726    if (overflowLines) {
   5727      nsIFrame* lineBeforeLastFrame;
   5728      if (firstLine) {
   5729        lineBeforeLastFrame = nullptr;  // removes all frames
   5730      } else {
   5731        nsIFrame* f = overBegin->mFirstChild;
   5732        lineBeforeLastFrame = f ? f->GetPrevSibling() : mFrames.LastChild();
   5733        NS_ASSERTION(!f || lineBeforeLastFrame == aLineBefore->LastChild(),
   5734                     "unexpected line frames");
   5735      }
   5736      nsFrameList pushedFrames = mFrames.TakeFramesAfter(lineBeforeLastFrame);
   5737      overflowLines->mFrames.InsertFrames(nullptr, nullptr,
   5738                                          std::move(pushedFrames));
   5739 
   5740      overflowLines->mLines.splice(overflowLines->mLines.begin(), mLines,
   5741                                   overBegin, LinesEnd());
   5742      NS_ASSERTION(!overflowLines->mLines.empty(), "should not be empty");
   5743      // this takes ownership but it won't delete it immediately so we
   5744      // can keep using it.
   5745      SetOverflowLines(overflowLines);
   5746 
   5747      // Mark all the overflow lines dirty so that they get reflowed when
   5748      // they are pulled up by our next-in-flow.
   5749 
   5750      nsLineBox* cursor = GetLineCursorForDisplay();
   5751 
   5752      // XXXldb Can this get called O(N) times making the whole thing O(N^2)?
   5753      for (LineIterator line = overflowLines->mLines.begin(),
   5754                        line_end = overflowLines->mLines.end();
   5755           line != line_end; ++line) {
   5756        if (line == cursor) {
   5757          ClearLineCursors();
   5758        }
   5759        line->MarkDirty();
   5760        line->MarkPreviousMarginDirty();
   5761        line->SetMovedFragments();
   5762        line->SetBoundsEmpty();
   5763        if (line->HasFloats()) {
   5764          line->ClearFloats();
   5765        }
   5766      }
   5767    }
   5768  }
   5769 
   5770 #ifdef DEBUG
   5771  VerifyOverflowSituation();
   5772 #endif
   5773 }
   5774 
   5775 // The overflowLines property is stored as a pointer to a line list,
   5776 // which must be deleted.  However, the following functions all maintain
   5777 // the invariant that the property is never set if the list is empty.
   5778 
   5779 bool nsBlockFrame::DrainOverflowLines() {
   5780 #ifdef DEBUG
   5781  VerifyOverflowSituation();
   5782 #endif
   5783 
   5784  // Steal the prev-in-flow's overflow lines and prepend them.
   5785  bool didFindOverflow = false;
   5786  nsBlockFrame* prevBlock = static_cast<nsBlockFrame*>(GetPrevInFlow());
   5787  if (prevBlock) {
   5788    prevBlock->ClearLineCursors();
   5789    FrameLines* overflowLines = prevBlock->RemoveOverflowLines();
   5790    if (overflowLines) {
   5791      // Make all the frames on the overflow line list mine.
   5792      ReparentFrames(overflowLines->mFrames, prevBlock, this);
   5793 
   5794      // Collect overflow containers from our OverflowContainers list that are
   5795      // continuations from the frames we picked up from our prev-in-flow, then
   5796      // prepend those to ExcessOverflowContainers to ensure the continuations
   5797      // are ordered.
   5798      if (GetOverflowContainers()) {
   5799        nsFrameList ocContinuations;
   5800        for (auto* f : overflowLines->mFrames) {
   5801          auto* cont = f;
   5802          bool done = false;
   5803          while (!done && (cont = cont->GetNextContinuation()) &&
   5804                 cont->GetParent() == this) {
   5805            bool onlyChild = !cont->GetPrevSibling() && !cont->GetNextSibling();
   5806            if (cont->HasAnyStateBits(NS_FRAME_IS_OVERFLOW_CONTAINER) &&
   5807                TryRemoveFrame(OverflowContainersProperty(), cont)) {
   5808              ocContinuations.AppendFrame(nullptr, cont);
   5809              done = onlyChild;
   5810              continue;
   5811            }
   5812            break;
   5813          }
   5814          if (done) {
   5815            break;
   5816          }
   5817        }
   5818        if (!ocContinuations.IsEmpty()) {
   5819          if (nsFrameList* eoc = GetExcessOverflowContainers()) {
   5820            eoc->InsertFrames(nullptr, nullptr, std::move(ocContinuations));
   5821          } else {
   5822            SetExcessOverflowContainers(std::move(ocContinuations));
   5823          }
   5824        }
   5825      }
   5826 
   5827      // Make the overflow out-of-flow frames mine too.
   5828      nsAutoOOFFrameList oofs(prevBlock);
   5829      if (oofs.mList.NotEmpty()) {
   5830        // In case we own any next-in-flows of any of the drained frames, then
   5831        // move those to the PushedFloat list.
   5832        nsFrameList pushedFloats;
   5833        for (nsIFrame* f : oofs.mList) {
   5834          nsIFrame* nif = f->GetNextInFlow();
   5835          for (; nif && nif->GetParent() == this; nif = nif->GetNextInFlow()) {
   5836            MOZ_ASSERT(nif->HasAnyStateBits(NS_FRAME_IS_PUSHED_OUT_OF_FLOW));
   5837            RemoveFloat(nif);
   5838            pushedFloats.AppendFrame(nullptr, nif);
   5839          }
   5840        }
   5841        ReparentFrames(oofs.mList, prevBlock, this);
   5842        EnsureFloats()->InsertFrames(nullptr, nullptr, std::move(oofs.mList));
   5843        if (!pushedFloats.IsEmpty()) {
   5844          nsFrameList* pf = EnsurePushedFloats();
   5845          pf->InsertFrames(nullptr, nullptr, std::move(pushedFloats));
   5846        }
   5847      }
   5848 
   5849      if (!mLines.empty()) {
   5850        // Remember to recompute the margins on the first line. This will
   5851        // also recompute the correct deltaBCoord if necessary.
   5852        mLines.front()->MarkPreviousMarginDirty();
   5853      }
   5854      // The overflow lines have already been marked dirty and their previous
   5855      // margins marked dirty also.
   5856 
   5857      // Prepend the overflow frames/lines to our principal list.
   5858      mFrames.InsertFrames(nullptr, nullptr, std::move(overflowLines->mFrames));
   5859      mLines.splice(mLines.begin(), overflowLines->mLines);
   5860      NS_ASSERTION(overflowLines->mLines.empty(), "splice should empty list");
   5861      delete overflowLines;
   5862      didFindOverflow = true;
   5863    }
   5864  }
   5865 
   5866  // Now append our own overflow lines.
   5867  return DrainSelfOverflowList() || didFindOverflow;
   5868 }
   5869 
   5870 bool nsBlockFrame::DrainSelfOverflowList() {
   5871  UniquePtr<FrameLines> ourOverflowLines(RemoveOverflowLines());
   5872  if (!ourOverflowLines) {
   5873    return false;
   5874  }
   5875 
   5876  // No need to reparent frames in our own overflow lines/oofs, because they're
   5877  // already ours. But we should put overflow floats back in our floats list.
   5878  // (explicit scope to remove the OOF list before VerifyOverflowSituation)
   5879  {
   5880    nsAutoOOFFrameList oofs(this);
   5881    if (oofs.mList.NotEmpty()) {
   5882 #ifdef DEBUG
   5883      for (nsIFrame* f : oofs.mList) {
   5884        MOZ_ASSERT(!f->HasAnyStateBits(NS_FRAME_IS_PUSHED_OUT_OF_FLOW),
   5885                   "CollectFloats should've removed that bit");
   5886      }
   5887 #endif
   5888      // The overflow floats go after our regular floats.
   5889      EnsureFloats()->AppendFrames(nullptr, std::move(oofs).mList);
   5890    }
   5891  }
   5892  if (!ourOverflowLines->mLines.empty()) {
   5893    mFrames.AppendFrames(nullptr, std::move(ourOverflowLines->mFrames));
   5894    mLines.splice(mLines.end(), ourOverflowLines->mLines);
   5895  }
   5896 
   5897 #ifdef DEBUG
   5898  VerifyOverflowSituation();
   5899 #endif
   5900  return true;
   5901 }
   5902 
   5903 /**
   5904 * Pushed floats are floats whose placeholders are in a previous
   5905 * continuation.  They might themselves be next-continuations of a float
   5906 * that partially fit in an earlier continuation, or they might be the
   5907 * first continuation of a float that couldn't be placed at all.
   5908 *
   5909 * Pushed floats live permanently at the beginning of a block's float
   5910 * list, where they must live *before* any floats whose placeholders are
   5911 * in that block.
   5912 *
   5913 * Temporarily, during reflow, they also live on the pushed floats list,
   5914 * which only holds them between (a) when one continuation pushes them to
   5915 * its pushed floats list because they don't fit and (b) when the next
   5916 * continuation pulls them onto the beginning of its float list.
   5917 *
   5918 * DrainPushedFloats sets up pushed floats the way we need them at the
   5919 * start of reflow; they are then reflowed by ReflowPushedFloats (which
   5920 * might push some of them on).  Floats with placeholders in this block
   5921 * are reflowed by (BlockReflowState/nsLineLayout)::AddFloat, which
   5922 * also maintains these invariants.
   5923 *
   5924 * DrainSelfPushedFloats moves any pushed floats from this block's own
   5925 * pushed floats list back into floats list.  DrainPushedFloats additionally
   5926 * moves frames from its prev-in-flow's pushed floats list into floats list.
   5927 */
   5928 void nsBlockFrame::DrainSelfPushedFloats() {
   5929  // If we're getting reflowed multiple times without our
   5930  // next-continuation being reflowed, we might need to pull back floats
   5931  // that we just put in the list to be pushed to our next-in-flow.
   5932  // We don't want to pull back any next-in-flows of floats on our own
   5933  // float list, and we only need to pull back first-in-flows whose
   5934  // placeholders were in earlier blocks (since first-in-flows whose
   5935  // placeholders are in this block will get pulled appropriately by
   5936  // AddFloat, and will then be more likely to be in the correct order).
   5937  mozilla::PresShell* presShell = PresShell();
   5938  nsFrameList* ourPushedFloats = GetPushedFloats();
   5939  if (ourPushedFloats) {
   5940    nsFrameList* floats = GetFloats();
   5941 
   5942    // When we pull back floats, we want to put them with the pushed
   5943    // floats, which must live at the start of our float list, but we
   5944    // want them at the end of those pushed floats.
   5945    // FIXME: This isn't quite right!  What if they're all pushed floats?
   5946    nsIFrame* insertionPrevSibling = nullptr; /* beginning of list */
   5947    for (nsIFrame* f = floats ? floats->FirstChild() : nullptr;
   5948         f && f->HasAnyStateBits(NS_FRAME_IS_PUSHED_OUT_OF_FLOW);
   5949         f = f->GetNextSibling()) {
   5950      insertionPrevSibling = f;
   5951    }
   5952 
   5953    nsIFrame* f = ourPushedFloats->LastChild();
   5954    while (f) {
   5955      nsIFrame* prevSibling = f->GetPrevSibling();
   5956 
   5957      nsPlaceholderFrame* placeholder = f->GetPlaceholderFrame();
   5958      nsIFrame* floatOriginalParent =
   5959          presShell->FrameConstructor()->GetFloatContainingBlock(placeholder);
   5960      if (floatOriginalParent != this) {
   5961        // This is a first continuation that was pushed from one of our
   5962        // previous continuations.  Take it out of the pushed floats
   5963        // list and put it in our floats list, before any of our
   5964        // floats, but after other pushed floats.
   5965        ourPushedFloats->RemoveFrame(f);
   5966        if (!floats) {
   5967          floats = EnsureFloats();
   5968        }
   5969        floats->InsertFrame(nullptr, insertionPrevSibling, f);
   5970      }
   5971 
   5972      f = prevSibling;
   5973    }
   5974 
   5975    if (ourPushedFloats->IsEmpty()) {
   5976      StealPushedFloats()->Delete(presShell);
   5977    }
   5978  }
   5979 }
   5980 
   5981 void nsBlockFrame::DrainPushedFloats() {
   5982  DrainSelfPushedFloats();
   5983 
   5984  // After our prev-in-flow has completed reflow, it may have a pushed
   5985  // floats list, containing floats that we need to own.  Take these.
   5986  nsBlockFrame* prevBlock = static_cast<nsBlockFrame*>(GetPrevInFlow());
   5987  if (prevBlock) {
   5988    AutoFrameListPtr list(PresContext(), prevBlock->StealPushedFloats());
   5989    if (list && list->NotEmpty()) {
   5990      EnsureFloats()->InsertFrames(this, nullptr, std::move(*list));
   5991    }
   5992  }
   5993 }
   5994 
   5995 nsBlockFrame::FrameLines* nsBlockFrame::GetOverflowLines() const {
   5996  if (!HasOverflowLines()) {
   5997    return nullptr;
   5998  }
   5999  FrameLines* prop = GetProperty(OverflowLinesProperty());
   6000  NS_ASSERTION(
   6001      prop && !prop->mLines.empty() &&
   6002              prop->mLines.front()->GetChildCount() == 0
   6003          ? prop->mFrames.IsEmpty()
   6004          : prop->mLines.front()->mFirstChild == prop->mFrames.FirstChild(),
   6005      "value should always be stored and non-empty when state set");
   6006  return prop;
   6007 }
   6008 
   6009 nsBlockFrame::FrameLines* nsBlockFrame::RemoveOverflowLines() {
   6010  if (!HasOverflowLines()) {
   6011    return nullptr;
   6012  }
   6013  FrameLines* prop = TakeProperty(OverflowLinesProperty());
   6014  NS_ASSERTION(
   6015      prop && !prop->mLines.empty() &&
   6016              prop->mLines.front()->GetChildCount() == 0
   6017          ? prop->mFrames.IsEmpty()
   6018          : prop->mLines.front()->mFirstChild == prop->mFrames.FirstChild(),
   6019      "value should always be stored and non-empty when state set");
   6020  RemoveStateBits(NS_BLOCK_HAS_OVERFLOW_LINES);
   6021  return prop;
   6022 }
   6023 
   6024 void nsBlockFrame::DestroyOverflowLines() {
   6025  NS_ASSERTION(HasOverflowLines(), "huh?");
   6026  FrameLines* prop = TakeProperty(OverflowLinesProperty());
   6027  NS_ASSERTION(prop && prop->mLines.empty(),
   6028               "value should always be stored but empty when destroying");
   6029  RemoveStateBits(NS_BLOCK_HAS_OVERFLOW_LINES);
   6030  delete prop;
   6031 }
   6032 
   6033 // This takes ownership of aOverflowLines.
   6034 // XXX We should allocate overflowLines from presShell arena!
   6035 void nsBlockFrame::SetOverflowLines(FrameLines* aOverflowLines) {
   6036  NS_ASSERTION(aOverflowLines, "null lines");
   6037  NS_ASSERTION(!aOverflowLines->mLines.empty(), "empty lines");
   6038  NS_ASSERTION(aOverflowLines->mLines.front()->mFirstChild ==
   6039                   aOverflowLines->mFrames.FirstChild(),
   6040               "invalid overflow lines / frames");
   6041  NS_ASSERTION(!HasAnyStateBits(NS_BLOCK_HAS_OVERFLOW_LINES),
   6042               "Overwriting existing overflow lines");
   6043 
   6044  // Verify that we won't overwrite an existing overflow list
   6045  NS_ASSERTION(!GetProperty(OverflowLinesProperty()), "existing overflow list");
   6046  SetProperty(OverflowLinesProperty(), aOverflowLines);
   6047  AddStateBits(NS_BLOCK_HAS_OVERFLOW_LINES);
   6048 }
   6049 
   6050 nsFrameList* nsBlockFrame::GetOverflowOutOfFlows() const {
   6051  if (!HasAnyStateBits(NS_BLOCK_HAS_OVERFLOW_OUT_OF_FLOWS)) {
   6052    return nullptr;
   6053  }
   6054  nsFrameList* result = GetProperty(OverflowOutOfFlowsProperty());
   6055  NS_ASSERTION(result, "value should always be non-empty when state set");
   6056  return result;
   6057 }
   6058 
   6059 void nsBlockFrame::SetOverflowOutOfFlows(nsFrameList&& aList,
   6060                                         nsFrameList* aPropValue) {
   6061  MOZ_ASSERT(
   6062      HasAnyStateBits(NS_BLOCK_HAS_OVERFLOW_OUT_OF_FLOWS) == !!aPropValue,
   6063      "state does not match value");
   6064 
   6065  if (aList.IsEmpty()) {
   6066    if (!HasAnyStateBits(NS_BLOCK_HAS_OVERFLOW_OUT_OF_FLOWS)) {
   6067      return;
   6068    }
   6069    nsFrameList* list = TakeProperty(OverflowOutOfFlowsProperty());
   6070    NS_ASSERTION(aPropValue == list, "prop value mismatch");
   6071    list->Clear();
   6072    list->Delete(PresShell());
   6073    RemoveStateBits(NS_BLOCK_HAS_OVERFLOW_OUT_OF_FLOWS);
   6074  } else if (HasAnyStateBits(NS_BLOCK_HAS_OVERFLOW_OUT_OF_FLOWS)) {
   6075    NS_ASSERTION(aPropValue == GetProperty(OverflowOutOfFlowsProperty()),
   6076                 "prop value mismatch");
   6077    *aPropValue = std::move(aList);
   6078  } else {
   6079    SetProperty(OverflowOutOfFlowsProperty(),
   6080                new (PresShell()) nsFrameList(std::move(aList)));
   6081    AddStateBits(NS_BLOCK_HAS_OVERFLOW_OUT_OF_FLOWS);
   6082  }
   6083 }
   6084 
   6085 nsIFrame* nsBlockFrame::GetInsideMarker() const {
   6086  if (!HasMarker()) {
   6087    return nullptr;
   6088  }
   6089  if (nsIFrame* frame = GetProperty(InsideMarkerProperty())) {
   6090    return frame;
   6091  }
   6092  return nullptr;
   6093 }
   6094 
   6095 nsIFrame* nsBlockFrame::GetOutsideMarker() const {
   6096  nsFrameList* list = GetOutsideMarkerList();
   6097  return list ? list->FirstChild() : nullptr;
   6098 }
   6099 
   6100 nsFrameList* nsBlockFrame::GetOutsideMarkerList() const {
   6101  if (!HasMarker()) {
   6102    return nullptr;
   6103  }
   6104  if (nsFrameList* list = GetProperty(OutsideMarkerProperty())) {
   6105    MOZ_ASSERT(list->GetLength() == 1, "bogus outside ::marker list");
   6106    return list;
   6107  }
   6108  return nullptr;
   6109 }
   6110 
   6111 bool nsBlockFrame::HasFloats() const {
   6112  const bool isStateBitSet = HasAnyStateBits(NS_BLOCK_HAS_FLOATS);
   6113  MOZ_ASSERT(
   6114      isStateBitSet == HasProperty(FloatsProperty()),
   6115      "State bit should accurately reflect presence/absence of the property!");
   6116  return isStateBitSet;
   6117 }
   6118 
   6119 nsFrameList* nsBlockFrame::GetFloats() const {
   6120  if (!HasFloats()) {
   6121    return nullptr;
   6122  }
   6123  nsFrameList* list = GetProperty(FloatsProperty());
   6124  MOZ_ASSERT(list, "List should always be valid when the property is set!");
   6125  MOZ_ASSERT(list->NotEmpty(),
   6126             "Someone forgot to delete the list when it is empty!");
   6127  return list;
   6128 }
   6129 
   6130 nsFrameList* nsBlockFrame::EnsureFloats() {
   6131  nsFrameList* list = GetFloats();
   6132  if (list) {
   6133    return list;
   6134  }
   6135  list = new (PresShell()) nsFrameList;
   6136  SetProperty(FloatsProperty(), list);
   6137  AddStateBits(NS_BLOCK_HAS_FLOATS);
   6138  return list;
   6139 }
   6140 
   6141 nsFrameList* nsBlockFrame::StealFloats() {
   6142  if (!HasFloats()) {
   6143    return nullptr;
   6144  }
   6145  nsFrameList* list = TakeProperty(FloatsProperty());
   6146  RemoveStateBits(NS_BLOCK_HAS_FLOATS);
   6147  MOZ_ASSERT(list, "List should always be valid when the property is set!");
   6148  return list;
   6149 }
   6150 
   6151 bool nsBlockFrame::HasPushedFloats() const {
   6152  const bool isStateBitSet = HasAnyStateBits(NS_BLOCK_HAS_PUSHED_FLOATS);
   6153  MOZ_ASSERT(
   6154      isStateBitSet == HasProperty(PushedFloatsProperty()),
   6155      "State bit should accurately reflect presence/absence of the property!");
   6156  return isStateBitSet;
   6157 }
   6158 
   6159 nsFrameList* nsBlockFrame::GetPushedFloats() const {
   6160  if (!HasPushedFloats()) {
   6161    return nullptr;
   6162  }
   6163  nsFrameList* list = GetProperty(PushedFloatsProperty());
   6164  MOZ_ASSERT(list, "List should always be valid when the property is set!");
   6165  MOZ_ASSERT(list->NotEmpty(),
   6166             "Someone forgot to delete the list when it is empty!");
   6167  return list;
   6168 }
   6169 
   6170 nsFrameList* nsBlockFrame::EnsurePushedFloats() {
   6171  nsFrameList* result = GetPushedFloats();
   6172  if (result) {
   6173    return result;
   6174  }
   6175 
   6176  result = new (PresShell()) nsFrameList;
   6177  SetProperty(PushedFloatsProperty(), result);
   6178  AddStateBits(NS_BLOCK_HAS_PUSHED_FLOATS);
   6179 
   6180  return result;
   6181 }
   6182 
   6183 nsFrameList* nsBlockFrame::StealPushedFloats() {
   6184  if (!HasPushedFloats()) {
   6185    return nullptr;
   6186  }
   6187  nsFrameList* list = TakeProperty(PushedFloatsProperty());
   6188  RemoveStateBits(NS_BLOCK_HAS_PUSHED_FLOATS);
   6189  MOZ_ASSERT(list, "List should always be valid when the property is set!");
   6190  return list;
   6191 }
   6192 
   6193 //////////////////////////////////////////////////////////////////////
   6194 // Frame list manipulation routines
   6195 
   6196 void nsBlockFrame::AppendFrames(ChildListID aListID, nsFrameList&& aFrameList) {
   6197  if (aFrameList.IsEmpty()) {
   6198    return;
   6199  }
   6200  if (aListID != FrameChildListID::Principal) {
   6201    if (FrameChildListID::Float == aListID) {
   6202      DrainSelfPushedFloats();  // ensure the last frame is in floats list.
   6203      EnsureFloats()->AppendFrames(nullptr, std::move(aFrameList));
   6204      return;
   6205    }
   6206    MOZ_ASSERT(FrameChildListID::NoReflowPrincipal == aListID,
   6207               "unexpected child list");
   6208  }
   6209 
   6210  // Find the proper last-child for where the append should go
   6211  nsIFrame* lastKid = mFrames.LastChild();
   6212  NS_ASSERTION(
   6213      (mLines.empty() ? nullptr : mLines.back()->LastChild()) == lastKid,
   6214      "out-of-sync mLines / mFrames");
   6215 
   6216 #ifdef NOISY_REFLOW_REASON
   6217  ListTag(stdout);
   6218  printf(": append ");
   6219  for (nsIFrame* frame : aFrameList) {
   6220    frame->ListTag(stdout);
   6221  }
   6222  if (lastKid) {
   6223    printf(" after ");
   6224    lastKid->ListTag(stdout);
   6225  }
   6226  printf("\n");
   6227 #endif
   6228 
   6229  if (IsInSVGTextSubtree()) {
   6230    MOZ_ASSERT(GetParent()->IsSVGTextFrame(),
   6231               "unexpected block frame in SVG text");
   6232    // Workaround for bug 1399425 in case this bit has been removed from the
   6233    // SVGTextFrame just before the parser adds more descendant nodes.
   6234    GetParent()->AddStateBits(NS_STATE_SVG_TEXT_CORRESPONDENCE_DIRTY);
   6235  }
   6236 
   6237  AddFrames(std::move(aFrameList), lastKid, nullptr);
   6238  if (aListID != FrameChildListID::NoReflowPrincipal) {
   6239    PresShell()->FrameNeedsReflow(
   6240        this, IntrinsicDirty::FrameAndAncestors,
   6241        NS_FRAME_HAS_DIRTY_CHILDREN);  // XXX sufficient?
   6242  }
   6243 }
   6244 
   6245 void nsBlockFrame::InsertFrames(ChildListID aListID, nsIFrame* aPrevFrame,
   6246                                const nsLineList::iterator* aPrevFrameLine,
   6247                                nsFrameList&& aFrameList) {
   6248  NS_ASSERTION(!aPrevFrame || aPrevFrame->GetParent() == this,
   6249               "inserting after sibling frame with different parent");
   6250 
   6251  if (aListID != FrameChildListID::Principal) {
   6252    if (FrameChildListID::Float == aListID) {
   6253      DrainSelfPushedFloats();  // ensure aPrevFrame is in floats list.
   6254      EnsureFloats()->InsertFrames(this, aPrevFrame, std::move(aFrameList));
   6255      return;
   6256    }
   6257    MOZ_ASSERT(FrameChildListID::NoReflowPrincipal == aListID,
   6258               "unexpected child list");
   6259  }
   6260 
   6261 #ifdef NOISY_REFLOW_REASON
   6262  ListTag(stdout);
   6263  printf(": insert ");
   6264  for (nsIFrame* frame : aFrameList) {
   6265    frame->ListTag(stdout);
   6266  }
   6267  if (aPrevFrame) {
   6268    printf(" after ");
   6269    aPrevFrame->ListTag(stdout);
   6270  }
   6271  printf("\n");
   6272 #endif
   6273 
   6274  AddFrames(std::move(aFrameList), aPrevFrame, aPrevFrameLine);
   6275  if (aListID != FrameChildListID::NoReflowPrincipal) {
   6276    PresShell()->FrameNeedsReflow(
   6277        this, IntrinsicDirty::FrameAndAncestors,
   6278        NS_FRAME_HAS_DIRTY_CHILDREN);  // XXX sufficient?
   6279  }
   6280 }
   6281 
   6282 void nsBlockFrame::RemoveFrame(DestroyContext& aContext, ChildListID aListID,
   6283                               nsIFrame* aOldFrame) {
   6284 #ifdef NOISY_REFLOW_REASON
   6285  ListTag(stdout);
   6286  printf(": remove ");
   6287  aOldFrame->ListTag(stdout);
   6288  printf("\n");
   6289 #endif
   6290 
   6291  if (aListID == FrameChildListID::Principal) {
   6292    bool hasFloats = BlockHasAnyFloats(aOldFrame);
   6293    DoRemoveFrame(aContext, aOldFrame, REMOVE_FIXED_CONTINUATIONS);
   6294    if (hasFloats) {
   6295      MarkSameFloatManagerLinesDirty(this);
   6296    }
   6297  } else if (FrameChildListID::Float == aListID) {
   6298    // Make sure to mark affected lines dirty for the float frame
   6299    // we are removing; this way is a bit messy, but so is the rest of the code.
   6300    // See bug 390762.
   6301    NS_ASSERTION(!aOldFrame->GetPrevContinuation(),
   6302                 "RemoveFrame should not be called on pushed floats.");
   6303    for (nsIFrame* f = aOldFrame;
   6304         f && !f->HasAnyStateBits(NS_FRAME_IS_OVERFLOW_CONTAINER);
   6305         f = f->GetNextContinuation()) {
   6306      MarkSameFloatManagerLinesDirty(
   6307          static_cast<nsBlockFrame*>(f->GetParent()));
   6308    }
   6309    DoRemoveFloats(aContext, aOldFrame);
   6310  } else if (FrameChildListID::NoReflowPrincipal == aListID) {
   6311    // Skip the call to |FrameNeedsReflow| below by returning now.
   6312    DoRemoveFrame(aContext, aOldFrame, REMOVE_FIXED_CONTINUATIONS);
   6313    return;
   6314  } else {
   6315    MOZ_CRASH("unexpected child list");
   6316  }
   6317 
   6318  PresShell()->FrameNeedsReflow(
   6319      this, IntrinsicDirty::FrameAndAncestors,
   6320      NS_FRAME_HAS_DIRTY_CHILDREN);  // XXX sufficient?
   6321 }
   6322 
   6323 static bool ShouldPutNextSiblingOnNewLine(nsIFrame* aLastFrame) {
   6324  LayoutFrameType type = aLastFrame->Type();
   6325  if (type == LayoutFrameType::Br) {
   6326    return true;
   6327  }
   6328  // XXX the TEXT_OFFSETS_NEED_FIXING check is a wallpaper for bug 822910.
   6329  if (type == LayoutFrameType::Text &&
   6330      !aLastFrame->HasAnyStateBits(TEXT_OFFSETS_NEED_FIXING)) {
   6331    return aLastFrame->HasSignificantTerminalNewline();
   6332  }
   6333  return false;
   6334 }
   6335 
   6336 void nsBlockFrame::AddFrames(nsFrameList&& aFrameList, nsIFrame* aPrevSibling,
   6337                             const nsLineList::iterator* aPrevSiblingLine) {
   6338  // Clear our line cursor, since our lines may change.
   6339  ClearLineCursors();
   6340 
   6341  if (aFrameList.IsEmpty()) {
   6342    return;
   6343  }
   6344 
   6345  // Attempt to find the line that contains the previous sibling
   6346  nsLineList* lineList = &mLines;
   6347  nsFrameList* frames = &mFrames;
   6348  nsLineList::iterator prevSibLine;
   6349  int32_t prevSiblingIndex;
   6350  if (aPrevSiblingLine) {
   6351    MOZ_ASSERT(aPrevSibling);
   6352    prevSibLine = *aPrevSiblingLine;
   6353    FrameLines* overflowLines = GetOverflowLines();
   6354    MOZ_ASSERT(prevSibLine.IsInSameList(mLines.begin()) ||
   6355                   (overflowLines &&
   6356                    prevSibLine.IsInSameList(overflowLines->mLines.begin())),
   6357               "must be one of our line lists");
   6358    if (overflowLines) {
   6359      // We need to find out which list it's actually in.  Assume that
   6360      // *if* we have overflow lines, that our primary lines aren't
   6361      // huge, but our overflow lines might be.
   6362      nsLineList::iterator line = mLines.begin(), lineEnd = mLines.end();
   6363      while (line != lineEnd) {
   6364        if (line == prevSibLine) {
   6365          break;
   6366        }
   6367        ++line;
   6368      }
   6369      if (line == lineEnd) {
   6370        // By elimination, the line must be in our overflow lines.
   6371        lineList = &overflowLines->mLines;
   6372        frames = &overflowLines->mFrames;
   6373      }
   6374    }
   6375 
   6376    nsLineList::iterator nextLine = prevSibLine.next();
   6377    nsIFrame* lastFrameInLine = nextLine == lineList->end()
   6378                                    ? frames->LastChild()
   6379                                    : nextLine->mFirstChild->GetPrevSibling();
   6380    prevSiblingIndex = prevSibLine->RLIndexOf(aPrevSibling, lastFrameInLine);
   6381    MOZ_ASSERT(prevSiblingIndex >= 0,
   6382               "aPrevSibling must be in aPrevSiblingLine");
   6383  } else {
   6384    prevSibLine = lineList->end();
   6385    prevSiblingIndex = -1;
   6386    if (aPrevSibling) {
   6387      // XXX_perf This is technically O(N^2) in some cases, but by using
   6388      // RFind instead of Find, we make it O(N) in the most common case,
   6389      // which is appending content.
   6390 
   6391      // Find the line that contains the previous sibling
   6392      if (!nsLineBox::RFindLineContaining(aPrevSibling, lineList->begin(),
   6393                                          prevSibLine, mFrames.LastChild(),
   6394                                          &prevSiblingIndex)) {
   6395        // Not in mLines - try overflow lines.
   6396        FrameLines* overflowLines = GetOverflowLines();
   6397        bool found = false;
   6398        if (overflowLines) {
   6399          prevSibLine = overflowLines->mLines.end();
   6400          prevSiblingIndex = -1;
   6401          found = nsLineBox::RFindLineContaining(
   6402              aPrevSibling, overflowLines->mLines.begin(), prevSibLine,
   6403              overflowLines->mFrames.LastChild(), &prevSiblingIndex);
   6404        }
   6405        if (MOZ_LIKELY(found)) {
   6406          lineList = &overflowLines->mLines;
   6407          frames = &overflowLines->mFrames;
   6408        } else {
   6409          // Note: defensive code! RFindLineContaining must not return
   6410          // false in this case, so if it does...
   6411          MOZ_ASSERT_UNREACHABLE("prev sibling not in line list");
   6412          aPrevSibling = nullptr;
   6413          prevSibLine = lineList->end();
   6414        }
   6415      }
   6416    }
   6417  }
   6418 
   6419  // Find the frame following aPrevSibling so that we can join up the
   6420  // two lists of frames.
   6421  if (aPrevSibling) {
   6422    // Split line containing aPrevSibling in two if the insertion
   6423    // point is somewhere in the middle of the line.
   6424    int32_t rem = prevSibLine->GetChildCount() - prevSiblingIndex - 1;
   6425    if (rem) {
   6426      // Split the line in two where the frame(s) are being inserted.
   6427      nsLineBox* line =
   6428          NewLineBox(prevSibLine, aPrevSibling->GetNextSibling(), rem);
   6429      lineList->after_insert(prevSibLine, line);
   6430      // Mark prevSibLine dirty and as needing textrun invalidation, since
   6431      // we may be breaking up text in the line. Its previous line may also
   6432      // need to be invalidated because it may be able to pull some text up.
   6433      MarkLineDirty(prevSibLine, lineList);
   6434      // The new line will also need its textruns recomputed because of the
   6435      // frame changes.
   6436      line->MarkDirty();
   6437      line->SetInvalidateTextRuns(true);
   6438    }
   6439  } else if (!lineList->empty()) {
   6440    lineList->front()->MarkDirty();
   6441    lineList->front()->SetInvalidateTextRuns(true);
   6442  }
   6443  const nsFrameList::Slice& newFrames =
   6444      frames->InsertFrames(nullptr, aPrevSibling, std::move(aFrameList));
   6445 
   6446  // Walk through the new frames being added and update the line data
   6447  // structures to fit.
   6448  for (nsIFrame* newFrame : newFrames) {
   6449    NS_ASSERTION(!aPrevSibling || aPrevSibling->GetNextSibling() == newFrame,
   6450                 "Unexpected aPrevSibling");
   6451    NS_ASSERTION(
   6452        !newFrame->IsPlaceholderFrame() ||
   6453            (!newFrame->IsAbsolutelyPositioned() && !newFrame->IsFloating()),
   6454        "Placeholders should not float or be positioned");
   6455 
   6456    bool isBlock = newFrame->IsBlockOutside();
   6457 
   6458    // If the frame is a block frame, or if there is no previous line or if the
   6459    // previous line is a block line we need to make a new line.  We also make
   6460    // a new line, as an optimization, in the two cases we know we'll need it:
   6461    // if the previous line ended with a <br>, or if it has significant
   6462    // whitespace and ended in a newline.
   6463    if (isBlock || prevSibLine == lineList->end() || prevSibLine->IsBlock() ||
   6464        (aPrevSibling && ShouldPutNextSiblingOnNewLine(aPrevSibling))) {
   6465      // Create a new line for the frame and add its line to the line
   6466      // list.
   6467      nsLineBox* line = NewLineBox(newFrame, isBlock);
   6468      if (prevSibLine != lineList->end()) {
   6469        // Append new line after prevSibLine
   6470        lineList->after_insert(prevSibLine, line);
   6471        ++prevSibLine;
   6472      } else {
   6473        // New line is going before the other lines
   6474        lineList->push_front(line);
   6475        prevSibLine = lineList->begin();
   6476      }
   6477    } else {
   6478      prevSibLine->NoteFrameAdded(newFrame);
   6479      // We're adding inline content to prevSibLine, so we need to mark it
   6480      // dirty, ensure its textruns are recomputed, and possibly do the same
   6481      // to its previous line since that line may be able to pull content up.
   6482      MarkLineDirty(prevSibLine, lineList);
   6483    }
   6484 
   6485    aPrevSibling = newFrame;
   6486  }
   6487 
   6488 #ifdef DEBUG
   6489  MOZ_ASSERT(aFrameList.IsEmpty());
   6490  VerifyLines(true);
   6491 #endif
   6492 }
   6493 
   6494 nsContainerFrame* nsBlockFrame::GetRubyContentPseudoFrame() {
   6495  auto* firstChild = PrincipalChildList().FirstChild();
   6496  if (firstChild && firstChild->IsRubyFrame() &&
   6497      firstChild->Style()->GetPseudoType() ==
   6498          PseudoStyleType::blockRubyContent) {
   6499    return static_cast<nsContainerFrame*>(firstChild);
   6500  }
   6501  return nullptr;
   6502 }
   6503 
   6504 nsContainerFrame* nsBlockFrame::GetContentInsertionFrame() {
   6505  // 'display:block ruby' use the inner (Ruby) frame for insertions.
   6506  if (auto* rubyContentPseudoFrame = GetRubyContentPseudoFrame()) {
   6507    return rubyContentPseudoFrame;
   6508  }
   6509  return this;
   6510 }
   6511 
   6512 void nsBlockFrame::AppendDirectlyOwnedAnonBoxes(
   6513    nsTArray<OwnedAnonBox>& aResult) {
   6514  if (auto* rubyContentPseudoFrame = GetRubyContentPseudoFrame()) {
   6515    aResult.AppendElement(OwnedAnonBox(rubyContentPseudoFrame));
   6516  }
   6517 }
   6518 
   6519 void nsBlockFrame::RemoveFloatFromFloatCache(nsIFrame* aFloat) {
   6520  // Find which line contains the float, so we can update
   6521  // the float cache.
   6522  for (auto& line : Lines()) {
   6523    if (line.IsInline() && line.RemoveFloat(aFloat)) {
   6524      break;
   6525    }
   6526  }
   6527 }
   6528 
   6529 void nsBlockFrame::RemoveFloat(nsIFrame* aFloat) {
   6530  MOZ_ASSERT(aFloat);
   6531 
   6532  // Floats live in floats list, pushed floats list, or overflow out-of-flow
   6533  // list.
   6534  MOZ_ASSERT(
   6535      GetChildList(FrameChildListID::Float).ContainsFrame(aFloat) ||
   6536          GetChildList(FrameChildListID::PushedFloats).ContainsFrame(aFloat) ||
   6537          GetChildList(FrameChildListID::OverflowOutOfFlow)
   6538              .ContainsFrame(aFloat),
   6539      "aFloat is not our child or on an unexpected frame list");
   6540 
   6541  bool didStartRemovingFloat = false;
   6542  if (nsFrameList* floats = GetFloats()) {
   6543    didStartRemovingFloat = true;
   6544    if (floats->StartRemoveFrame(aFloat)) {
   6545      if (floats->IsEmpty()) {
   6546        StealFloats()->Delete(PresShell());
   6547      }
   6548      return;
   6549    }
   6550  }
   6551 
   6552  if (nsFrameList* pushedFloats = GetPushedFloats()) {
   6553    bool found;
   6554    if (didStartRemovingFloat) {
   6555      found = pushedFloats->ContinueRemoveFrame(aFloat);
   6556    } else {
   6557      didStartRemovingFloat = true;
   6558      found = pushedFloats->StartRemoveFrame(aFloat);
   6559    }
   6560    if (found) {
   6561      if (pushedFloats->IsEmpty()) {
   6562        StealPushedFloats()->Delete(PresShell());
   6563      }
   6564      return;
   6565    }
   6566  }
   6567 
   6568  {
   6569    nsAutoOOFFrameList oofs(this);
   6570    if (didStartRemovingFloat ? oofs.mList.ContinueRemoveFrame(aFloat)
   6571                              : oofs.mList.StartRemoveFrame(aFloat)) {
   6572      return;
   6573    }
   6574  }
   6575 }
   6576 
   6577 void nsBlockFrame::DoRemoveFloats(DestroyContext& aContext, nsIFrame* aFrame) {
   6578  // Note: nsFloatingFirstLetterFrame might lose its floating style after
   6579  // nsBlockFrame::UpdateFirstLetterStyle().
   6580  MOZ_ASSERT(aFrame->IsFloating() || static_cast<nsFloatingFirstLetterFrame*>(
   6581                                         do_QueryFrame(aFrame)),
   6582             "DoRemoveFloats() can only remove float elements!");
   6583 
   6584  // The containing block is always the parent of aFrame.
   6585  auto* block = static_cast<nsBlockFrame*>(aFrame->GetParent());
   6586 
   6587  // First remove aFrame's next-in-flows.
   6588  if (nsIFrame* nif = aFrame->GetNextInFlow()) {
   6589    nif->GetParent()->DeleteNextInFlowChild(aContext, nif, false);
   6590  }
   6591  // Now remove aFrame from its child list and Destroy it.
   6592  block->RemoveFloatFromFloatCache(aFrame);
   6593  block->RemoveFloat(aFrame);
   6594  aFrame->Destroy(aContext);
   6595 }
   6596 
   6597 /**
   6598 * This helps us iterate over the list of all normal + overflow lines
   6599 */
   6600 void nsBlockFrame::TryAllLines(nsLineList::iterator* aIterator,
   6601                               nsLineList::iterator* aStartIterator,
   6602                               nsLineList::iterator* aEndIterator,
   6603                               bool* aInOverflowLines,
   6604                               FrameLines** aOverflowLines) {
   6605  if (*aIterator == *aEndIterator) {
   6606    if (!*aInOverflowLines) {
   6607      // Try the overflow lines
   6608      *aInOverflowLines = true;
   6609      FrameLines* lines = GetOverflowLines();
   6610      if (lines) {
   6611        *aStartIterator = lines->mLines.begin();
   6612        *aIterator = *aStartIterator;
   6613        *aEndIterator = lines->mLines.end();
   6614        *aOverflowLines = lines;
   6615      }
   6616    }
   6617  }
   6618 }
   6619 
   6620 nsBlockInFlowLineIterator::nsBlockInFlowLineIterator(nsBlockFrame* aFrame,
   6621                                                     LineIterator aLine)
   6622    : mFrame(aFrame), mLine(aLine), mLineList(&aFrame->mLines) {
   6623  // This will assert if aLine isn't in mLines of aFrame:
   6624  DebugOnly<bool> check = aLine == mFrame->LinesBegin();
   6625 }
   6626 
   6627 nsBlockInFlowLineIterator::nsBlockInFlowLineIterator(nsBlockFrame* aFrame,
   6628                                                     LineIterator aLine,
   6629                                                     bool aInOverflow)
   6630    : mFrame(aFrame),
   6631      mLine(aLine),
   6632      mLineList(aInOverflow ? &aFrame->GetOverflowLines()->mLines
   6633                            : &aFrame->mLines) {}
   6634 
   6635 nsBlockInFlowLineIterator::nsBlockInFlowLineIterator(nsBlockFrame* aFrame,
   6636                                                     bool* aFoundValidLine)
   6637    : mFrame(aFrame), mLineList(&aFrame->mLines) {
   6638  mLine = aFrame->LinesBegin();
   6639  *aFoundValidLine = FindValidLine();
   6640 }
   6641 
   6642 static bool AnonymousBoxIsBFC(const ComputedStyle* aStyle) {
   6643  switch (aStyle->GetPseudoType()) {
   6644    case PseudoStyleType::fieldsetContent:
   6645    case PseudoStyleType::columnContent:
   6646    case PseudoStyleType::cellContent:
   6647    case PseudoStyleType::scrolledContent:
   6648    case PseudoStyleType::anonymousItem:
   6649      return true;
   6650    default:
   6651      return false;
   6652  }
   6653 }
   6654 
   6655 static bool StyleEstablishesBFC(const ComputedStyle* aStyle) {
   6656  // paint/layout containment boxes and multi-column containers establish an
   6657  // independent formatting context.
   6658  // https://drafts.csswg.org/css-contain/#containment-paint
   6659  // https://drafts.csswg.org/css-contain/#containment-layout
   6660  // https://github.com/w3c/csswg-drafts/issues/10544
   6661  // https://drafts.csswg.org/css-align/#distribution-block
   6662  // https://drafts.csswg.org/css-multicol/#columns
   6663  const auto* disp = aStyle->StyleDisplay();
   6664  return disp->IsContainPaint() || disp->IsContainLayout() ||
   6665         disp->mContainerType &
   6666             (StyleContainerType::SIZE | StyleContainerType::INLINE_SIZE) ||
   6667         disp->DisplayInside() == StyleDisplayInside::FlowRoot ||
   6668         disp->IsAbsolutelyPositionedStyle() || disp->IsFloatingStyle() ||
   6669         aStyle->IsRootElementStyle() || AnonymousBoxIsBFC(aStyle);
   6670 }
   6671 
   6672 static bool EstablishesBFC(const nsBlockFrame* aFrame) {
   6673  if (aFrame->HasAnyClassFlag(LayoutFrameClassFlags::BlockFormattingContext)) {
   6674    return true;
   6675  }
   6676 
   6677  if (nsIFrame* parent = aFrame->GetParent()) {
   6678    if (parent->IsFieldSetFrame()) {
   6679      // A rendered legend always establishes a new formatting context, and so
   6680      // does the fieldset content frame, so we can just return true here.
   6681      // https://html.spec.whatwg.org/#rendered-legend
   6682      return true;
   6683    }
   6684 
   6685    const auto wm = aFrame->GetWritingMode();
   6686    const auto parentWM = parent->GetWritingMode();
   6687    if (wm.GetBlockDir() != parentWM.GetBlockDir() ||
   6688        wm.IsVerticalSideways() != parentWM.IsVerticalSideways()) {
   6689      // If a box has a different writing-mode value than its containing block
   6690      // [...] if the box is a block container, then it establishes a new block
   6691      // formatting context.
   6692      // https://drafts.csswg.org/css-writing-modes/#block-flow
   6693      return true;
   6694    }
   6695  }
   6696 
   6697  if (aFrame->IsColumnSpan()) {
   6698    return true;
   6699  }
   6700 
   6701  if (aFrame->IsContentAligned()) {
   6702    return true;
   6703  }
   6704 
   6705  if (aFrame->IsSuppressedScrollableBlockForPrint()) {
   6706    return true;
   6707  }
   6708 
   6709  const auto* style = aFrame->Style();
   6710  if (style->GetPseudoType() == PseudoStyleType::marker) {
   6711    if (aFrame->GetParent() &&
   6712        aFrame->GetParent()->StyleList()->mListStylePosition ==
   6713            StyleListStylePosition::Outside) {
   6714      // An outside ::marker needs to be an independent formatting context
   6715      // to avoid being influenced by the float manager etc.
   6716      return true;
   6717    }
   6718  }
   6719  return StyleEstablishesBFC(style);
   6720 }
   6721 
   6722 void nsBlockFrame::DidSetComputedStyle(ComputedStyle* aOldStyle) {
   6723  nsContainerFrame::DidSetComputedStyle(aOldStyle);
   6724  if (IsInSVGTextSubtree() &&
   6725      (StyleSVGReset()->HasNonScalingStroke() &&
   6726       (!aOldStyle || !aOldStyle->StyleSVGReset()->HasNonScalingStroke()))) {
   6727    nsIFrame* textFrame =
   6728        nsLayoutUtils::GetClosestFrameOfType(this, LayoutFrameType::SVGText);
   6729    MOZ_ASSERT(textFrame, "Expecting to find an SVG text frame");
   6730    SVGUtils::UpdateNonScalingStrokeStateBit(textFrame);
   6731  }
   6732  if (!aOldStyle) {
   6733    return;
   6734  }
   6735 
   6736  const bool isBFC = EstablishesBFC(this);
   6737  if (HasAnyStateBits(NS_BLOCK_BFC) != isBFC) {
   6738    if (MaybeHasFloats()) {
   6739      // If the frame contains floats, this update may change their float
   6740      // manager. Be safe by dirtying all descendant lines of the nearest
   6741      // ancestor's float manager.
   6742      RemoveStateBits(NS_BLOCK_BFC);
   6743      MarkSameFloatManagerLinesDirty(this);
   6744    }
   6745    AddOrRemoveStateBits(NS_BLOCK_BFC, isBFC);
   6746  }
   6747 }
   6748 
   6749 void nsBlockFrame::UpdateFirstLetterStyle(ServoRestyleState& aRestyleState) {
   6750  nsIFrame* letterFrame = GetFirstLetter();
   6751  if (!letterFrame) {
   6752    return;
   6753  }
   6754 
   6755  // Figure out what the right style parent is.  This needs to match
   6756  // nsCSSFrameConstructor::CreateLetterFrame.
   6757  nsIFrame* inFlowFrame = letterFrame;
   6758  if (inFlowFrame->HasAnyStateBits(NS_FRAME_OUT_OF_FLOW)) {
   6759    inFlowFrame = inFlowFrame->GetPlaceholderFrame();
   6760  }
   6761  nsIFrame* styleParent = CorrectStyleParentFrame(inFlowFrame->GetParent(),
   6762                                                  PseudoStyleType::firstLetter);
   6763  ComputedStyle* parentStyle = styleParent->Style();
   6764  RefPtr<ComputedStyle> firstLetterStyle =
   6765      aRestyleState.StyleSet().ResolvePseudoElementStyle(
   6766          *mContent->AsElement(), PseudoStyleType::firstLetter, nullptr,
   6767          parentStyle);
   6768  // Note that we don't need to worry about changehints for the continuation
   6769  // styles: those will be handled by the styleParent already.
   6770  RefPtr<ComputedStyle> continuationStyle =
   6771      aRestyleState.StyleSet().ResolveStyleForFirstLetterContinuation(
   6772          parentStyle);
   6773  UpdateStyleOfOwnedChildFrame(letterFrame, firstLetterStyle, aRestyleState,
   6774                               Some(continuationStyle.get()));
   6775 
   6776  // We also want to update the style on the textframe inside the first-letter.
   6777  // We don't need to compute a changehint for this, though, since any changes
   6778  // to it are handled by the first-letter anyway.
   6779  nsIFrame* textFrame = letterFrame->PrincipalChildList().FirstChild();
   6780  RefPtr<ComputedStyle> firstTextStyle =
   6781      aRestyleState.StyleSet().ResolveStyleForText(textFrame->GetContent(),
   6782                                                   firstLetterStyle);
   6783  textFrame->SetComputedStyle(firstTextStyle);
   6784 
   6785  // We don't need to update style for textFrame's continuations: it's already
   6786  // set up to inherit from parentStyle, which is what we want.
   6787 }
   6788 
   6789 static nsIFrame* FindChildContaining(nsBlockFrame* aFrame,
   6790                                     nsIFrame* aFindFrame) {
   6791  NS_ASSERTION(aFrame, "must have frame");
   6792  nsIFrame* child;
   6793  while (true) {
   6794    nsIFrame* block = aFrame;
   6795    do {
   6796      child = nsLayoutUtils::FindChildContainingDescendant(block, aFindFrame);
   6797      if (child) {
   6798        break;
   6799      }
   6800      block = block->GetNextContinuation();
   6801    } while (block);
   6802    if (!child) {
   6803      return nullptr;
   6804    }
   6805    if (!child->HasAnyStateBits(NS_FRAME_OUT_OF_FLOW)) {
   6806      break;
   6807    }
   6808    aFindFrame = child->GetPlaceholderFrame();
   6809  }
   6810 
   6811  return child;
   6812 }
   6813 
   6814 nsBlockInFlowLineIterator::nsBlockInFlowLineIterator(nsBlockFrame* aFrame,
   6815                                                     nsIFrame* aFindFrame,
   6816                                                     bool* aFoundValidLine)
   6817    : mFrame(aFrame), mLineList(&aFrame->mLines) {
   6818  *aFoundValidLine = false;
   6819 
   6820  nsIFrame* child = FindChildContaining(aFrame, aFindFrame);
   6821  if (!child) {
   6822    return;
   6823  }
   6824 
   6825  LineIterator line_end = aFrame->LinesEnd();
   6826  mLine = aFrame->LinesBegin();
   6827  if (mLine != line_end && mLine.next() == line_end &&
   6828      !aFrame->HasOverflowLines()) {
   6829    // The block has a single line - that must be it!
   6830    *aFoundValidLine = true;
   6831    return;
   6832  }
   6833 
   6834  // Try to use the cursor if it exists, otherwise fall back to the first line
   6835  if (nsLineBox* const cursor = aFrame->GetLineCursorForQuery()) {
   6836    mLine = line_end;
   6837    // Perform a simultaneous forward and reverse search starting from the
   6838    // line cursor.
   6839    nsBlockFrame::LineIterator line = aFrame->LinesBeginFrom(cursor);
   6840    nsBlockFrame::ReverseLineIterator rline = aFrame->LinesRBeginFrom(cursor);
   6841    nsBlockFrame::ReverseLineIterator rline_end = aFrame->LinesREnd();
   6842    // rline is positioned on the line containing 'cursor', so it's not
   6843    // rline_end. So we can safely increment it (i.e. move it to one line
   6844    // earlier) to start searching there.
   6845    ++rline;
   6846    while (line != line_end || rline != rline_end) {
   6847      if (line != line_end) {
   6848        if (line->Contains(child)) {
   6849          mLine = line;
   6850          break;
   6851        }
   6852        ++line;
   6853      }
   6854      if (rline != rline_end) {
   6855        if (rline->Contains(child)) {
   6856          mLine = rline;
   6857          break;
   6858        }
   6859        ++rline;
   6860      }
   6861    }
   6862    if (mLine != line_end) {
   6863      *aFoundValidLine = true;
   6864      if (mLine != cursor) {
   6865        aFrame->SetProperty(nsBlockFrame::LineCursorPropertyQuery(), mLine);
   6866      }
   6867      return;
   6868    }
   6869  } else {
   6870    for (mLine = aFrame->LinesBegin(); mLine != line_end; ++mLine) {
   6871      if (mLine->Contains(child)) {
   6872        *aFoundValidLine = true;
   6873        return;
   6874      }
   6875    }
   6876  }
   6877  // Didn't find the line
   6878  MOZ_ASSERT(mLine == line_end, "mLine should be line_end at this point");
   6879 
   6880  // If we reach here, it means that we have not been able to find the
   6881  // desired frame in our in-flow lines.  So we should start looking at
   6882  // our overflow lines. In order to do that, we set mLine to the end
   6883  // iterator so that FindValidLine starts to look at overflow lines,
   6884  // if any.
   6885 
   6886  if (!FindValidLine()) {
   6887    return;
   6888  }
   6889 
   6890  do {
   6891    if (mLine->Contains(child)) {
   6892      *aFoundValidLine = true;
   6893      return;
   6894    }
   6895  } while (Next());
   6896 }
   6897 
   6898 nsBlockFrame::LineIterator nsBlockInFlowLineIterator::End() {
   6899  return mLineList->end();
   6900 }
   6901 
   6902 bool nsBlockInFlowLineIterator::IsLastLineInList() {
   6903  LineIterator end = End();
   6904  return mLine != end && mLine.next() == end;
   6905 }
   6906 
   6907 bool nsBlockInFlowLineIterator::Next() {
   6908  ++mLine;
   6909  return FindValidLine();
   6910 }
   6911 
   6912 bool nsBlockInFlowLineIterator::Prev() {
   6913  LineIterator begin = mLineList->begin();
   6914  if (mLine != begin) {
   6915    --mLine;
   6916    return true;
   6917  }
   6918  bool currentlyInOverflowLines = GetInOverflow();
   6919  while (true) {
   6920    if (currentlyInOverflowLines) {
   6921      mLineList = &mFrame->mLines;
   6922      mLine = mLineList->end();
   6923      if (mLine != mLineList->begin()) {
   6924        --mLine;
   6925        return true;
   6926      }
   6927    } else {
   6928      mFrame = static_cast<nsBlockFrame*>(mFrame->GetPrevInFlow());
   6929      if (!mFrame) {
   6930        return false;
   6931      }
   6932      nsBlockFrame::FrameLines* overflowLines = mFrame->GetOverflowLines();
   6933      if (overflowLines) {
   6934        mLineList = &overflowLines->mLines;
   6935        mLine = mLineList->end();
   6936        NS_ASSERTION(mLine != mLineList->begin(), "empty overflow line list?");
   6937        --mLine;
   6938        return true;
   6939      }
   6940    }
   6941    currentlyInOverflowLines = !currentlyInOverflowLines;
   6942  }
   6943 }
   6944 
   6945 bool nsBlockInFlowLineIterator::FindValidLine() {
   6946  LineIterator end = mLineList->end();
   6947  if (mLine != end) {
   6948    return true;
   6949  }
   6950  bool currentlyInOverflowLines = GetInOverflow();
   6951  while (true) {
   6952    if (currentlyInOverflowLines) {
   6953      mFrame = static_cast<nsBlockFrame*>(mFrame->GetNextInFlow());
   6954      if (!mFrame) {
   6955        return false;
   6956      }
   6957      mLineList = &mFrame->mLines;
   6958      mLine = mLineList->begin();
   6959      if (mLine != mLineList->end()) {
   6960        return true;
   6961      }
   6962    } else {
   6963      nsBlockFrame::FrameLines* overflowLines = mFrame->GetOverflowLines();
   6964      if (overflowLines) {
   6965        mLineList = &overflowLines->mLines;
   6966        mLine = mLineList->begin();
   6967        NS_ASSERTION(mLine != mLineList->end(), "empty overflow line list?");
   6968        return true;
   6969      }
   6970    }
   6971    currentlyInOverflowLines = !currentlyInOverflowLines;
   6972  }
   6973 }
   6974 
   6975 // This function removes aDeletedFrame and all its continuations.  It
   6976 // is optimized for deleting a whole series of frames. The easy
   6977 // implementation would invoke itself recursively on
   6978 // aDeletedFrame->GetNextContinuation, then locate the line containing
   6979 // aDeletedFrame and remove aDeletedFrame from that line. But here we
   6980 // start by locating aDeletedFrame and then scanning from that point
   6981 // on looking for continuations.
   6982 void nsBlockFrame::DoRemoveFrame(DestroyContext& aContext,
   6983                                 nsIFrame* aDeletedFrame, uint32_t aFlags) {
   6984  MOZ_ASSERT(!aDeletedFrame->HasAnyStateBits(NS_FRAME_OUT_OF_FLOW |
   6985                                             NS_FRAME_IS_OVERFLOW_CONTAINER),
   6986             "DoRemoveFrame() does not support removing out-of-flow frames or "
   6987             "overflow containers!");
   6988 
   6989  // Find the line that contains deletedFrame. Start from the line cursor
   6990  // (if available) and search to the end of the normal line list, then
   6991  // from the start to the line cursor, and last the overflow lines.
   6992  nsLineList::iterator line_start = mLines.begin(), line_end = mLines.end();
   6993  nsLineList::iterator line = line_start;
   6994 
   6995  // We use the line cursor to attempt to optimize removal, but must ensure
   6996  // it is cleared if lines change such that it may become invalid.
   6997  bool found = false;
   6998  if (nsLineBox* cursor = GetLineCursorForDisplay()) {
   6999    for (line.SetPosition(cursor); line != line_end; ++line) {
   7000      if (line->Contains(aDeletedFrame)) {
   7001        found = true;
   7002        break;
   7003      }
   7004    }
   7005    if (!found) {
   7006      // Setup for a shorter TryAllLines normal line search to avoid searching
   7007      // the [cursor .. line_end] range again.
   7008      line = line_start;
   7009      line_end.SetPosition(cursor);
   7010    }
   7011  }
   7012 
   7013  FrameLines* overflowLines = nullptr;
   7014  bool searchingOverflowList = false;
   7015  if (!found) {
   7016    // Make sure we look in the overflow lines even if the normal line
   7017    // list is empty.
   7018    TryAllLines(&line, &line_start, &line_end, &searchingOverflowList,
   7019                &overflowLines);
   7020    while (line != line_end) {
   7021      if (line->Contains(aDeletedFrame)) {
   7022        break;
   7023      }
   7024      ++line;
   7025      TryAllLines(&line, &line_start, &line_end, &searchingOverflowList,
   7026                  &overflowLines);
   7027    }
   7028    if (!searchingOverflowList && (GetStateBits() & NS_BLOCK_HAS_LINE_CURSOR)) {
   7029      // Restore line_end since we shortened the search to the cursor.
   7030      line_end = mLines.end();
   7031      // Clear our line cursors, since our normal line list may change.
   7032      ClearLineCursors();
   7033    }
   7034  }
   7035 
   7036  if (line == line_end) {
   7037    NS_ERROR("can't find deleted frame in lines");
   7038    return;
   7039  }
   7040 
   7041  if (!(aFlags & FRAMES_ARE_EMPTY)) {
   7042    if (line != line_start) {
   7043      line.prev()->MarkDirty();
   7044      line.prev()->SetInvalidateTextRuns(true);
   7045    } else if (searchingOverflowList && !mLines.empty()) {
   7046      mLines.back()->MarkDirty();
   7047      mLines.back()->SetInvalidateTextRuns(true);
   7048    }
   7049  }
   7050 
   7051  while (line != line_end && aDeletedFrame) {
   7052    MOZ_ASSERT(this == aDeletedFrame->GetParent(), "messed up delete code");
   7053    MOZ_ASSERT(line->Contains(aDeletedFrame), "frame not in line");
   7054 
   7055    if (!(aFlags & FRAMES_ARE_EMPTY)) {
   7056      line->MarkDirty();
   7057      line->SetInvalidateTextRuns(true);
   7058    }
   7059 
   7060    // If the frame being deleted is the last one on the line then
   7061    // optimize away the line->Contains(next-in-flow) call below.
   7062    bool isLastFrameOnLine = 1 == line->GetChildCount();
   7063    if (!isLastFrameOnLine) {
   7064      LineIterator next = line.next();
   7065      nsIFrame* lastFrame =
   7066          next != line_end
   7067              ? next->mFirstChild->GetPrevSibling()
   7068              : (searchingOverflowList ? overflowLines->mFrames.LastChild()
   7069                                       : mFrames.LastChild());
   7070      NS_ASSERTION(next == line_end || lastFrame == line->LastChild(),
   7071                   "unexpected line frames");
   7072      isLastFrameOnLine = lastFrame == aDeletedFrame;
   7073    }
   7074 
   7075    // Remove aDeletedFrame from the line
   7076    if (line->mFirstChild == aDeletedFrame) {
   7077      // We should be setting this to null if aDeletedFrame
   7078      // is the only frame on the line. HOWEVER in that case
   7079      // we will be removing the line anyway, see below.
   7080      line->mFirstChild = aDeletedFrame->GetNextSibling();
   7081    }
   7082 
   7083    // Hmm, this won't do anything if we're removing a frame in the first
   7084    // overflow line... Hopefully doesn't matter
   7085    --line;
   7086    if (line != line_end && !line->IsBlock()) {
   7087      // Since we just removed a frame that follows some inline
   7088      // frames, we need to reflow the previous line.
   7089      line->MarkDirty();
   7090    }
   7091    ++line;
   7092 
   7093    // Take aDeletedFrame out of the sibling list. Note that
   7094    // prevSibling will only be nullptr when we are deleting the very
   7095    // first frame in the main or overflow list.
   7096    if (searchingOverflowList) {
   7097      overflowLines->mFrames.RemoveFrame(aDeletedFrame);
   7098    } else {
   7099      mFrames.RemoveFrame(aDeletedFrame);
   7100    }
   7101 
   7102    // Update the child count of the line to be accurate
   7103    line->NoteFrameRemoved(aDeletedFrame);
   7104 
   7105    // Destroy frame; capture its next continuation first in case we need
   7106    // to destroy that too.
   7107    nsIFrame* deletedNextContinuation =
   7108        (aFlags & REMOVE_FIXED_CONTINUATIONS)
   7109            ? aDeletedFrame->GetNextContinuation()
   7110            : aDeletedFrame->GetNextInFlow();
   7111 #ifdef NOISY_REMOVE_FRAME
   7112    printf("DoRemoveFrame: %s line=%p frame=",
   7113           searchingOverflowList ? "overflow" : "normal", line.get());
   7114    aDeletedFrame->ListTag(stdout);
   7115    printf(" prevSibling=%p deletedNextContinuation=%p\n",
   7116           aDeletedFrame->GetPrevSibling(), deletedNextContinuation);
   7117 #endif
   7118 
   7119    // If next-in-flow is an overflow container, must remove it first.
   7120    // FIXME: Can we do this unconditionally?
   7121    if (deletedNextContinuation && deletedNextContinuation->HasAnyStateBits(
   7122                                       NS_FRAME_IS_OVERFLOW_CONTAINER)) {
   7123      deletedNextContinuation->GetParent()->DeleteNextInFlowChild(
   7124          aContext, deletedNextContinuation, false);
   7125      deletedNextContinuation = nullptr;
   7126    }
   7127 
   7128    aDeletedFrame->Destroy(aContext);
   7129    aDeletedFrame = deletedNextContinuation;
   7130 
   7131    bool haveAdvancedToNextLine = false;
   7132    // If line is empty, remove it now.
   7133    if (0 == line->GetChildCount()) {
   7134 #ifdef NOISY_REMOVE_FRAME
   7135      printf("DoRemoveFrame: %s line=%p became empty so it will be removed\n",
   7136             searchingOverflowList ? "overflow" : "normal", line.get());
   7137 #endif
   7138      nsLineBox* cur = line;
   7139      if (!searchingOverflowList) {
   7140        line = mLines.erase(line);
   7141        ClearLineCursors();
   7142        // Invalidate the space taken up by the line.
   7143        // XXX We need to do this if we're removing a frame as a result of
   7144        // a call to RemoveFrame(), but we may not need to do this in all
   7145        // cases...
   7146 #ifdef NOISY_BLOCK_INVALIDATE
   7147        nsRect inkOverflow(cur->InkOverflowRect());
   7148        printf("%p invalidate 10 (%d, %d, %d, %d)\n", this, inkOverflow.x,
   7149               inkOverflow.y, inkOverflow.width, inkOverflow.height);
   7150 #endif
   7151      } else {
   7152        line = overflowLines->mLines.erase(line);
   7153        if (overflowLines->mLines.empty()) {
   7154          DestroyOverflowLines();
   7155          overflowLines = nullptr;
   7156          // We just invalidated our iterators.  Since we were in
   7157          // the overflow lines list, which is now empty, set them
   7158          // so we're at the end of the regular line list.
   7159          line_start = mLines.begin();
   7160          line_end = mLines.end();
   7161          line = line_end;
   7162        }
   7163      }
   7164      FreeLineBox(cur);
   7165 
   7166      // If we're removing a line, ReflowDirtyLines isn't going to
   7167      // know that it needs to slide lines unless something is marked
   7168      // dirty.  So mark the previous margin of the next line dirty if
   7169      // there is one.
   7170      if (line != line_end) {
   7171        line->MarkPreviousMarginDirty();
   7172      }
   7173      haveAdvancedToNextLine = true;
   7174    } else {
   7175      // Make the line that just lost a frame dirty, and advance to
   7176      // the next line.
   7177      if (!deletedNextContinuation || isLastFrameOnLine ||
   7178          !line->Contains(deletedNextContinuation)) {
   7179        line->MarkDirty();
   7180        ++line;
   7181        haveAdvancedToNextLine = true;
   7182      }
   7183    }
   7184 
   7185    if (deletedNextContinuation) {
   7186      // See if we should keep looking in the current flow's line list.
   7187      if (deletedNextContinuation->GetParent() != this) {
   7188        // The deceased frames continuation is not a child of the
   7189        // current block. So break out of the loop so that we advance
   7190        // to the next parent.
   7191        //
   7192        // If we have a continuation in a different block then all bets are
   7193        // off regarding whether we are deleting frames without actual content,
   7194        // so don't propagate FRAMES_ARE_EMPTY any further.
   7195        aFlags &= ~FRAMES_ARE_EMPTY;
   7196        break;
   7197      }
   7198 
   7199      // If we advanced to the next line then check if we should switch to the
   7200      // overflow line list.
   7201      if (haveAdvancedToNextLine) {
   7202        if (line != line_end && !searchingOverflowList &&
   7203            !line->Contains(deletedNextContinuation)) {
   7204          // We have advanced to the next *normal* line but the next-in-flow
   7205          // is not there - force a switch to the overflow line list.
   7206          line = line_end;
   7207        }
   7208 
   7209        TryAllLines(&line, &line_start, &line_end, &searchingOverflowList,
   7210                    &overflowLines);
   7211 #ifdef NOISY_REMOVE_FRAME
   7212        printf("DoRemoveFrame: now on %s line=%p\n",
   7213               searchingOverflowList ? "overflow" : "normal", line.get());
   7214 #endif
   7215      }
   7216    }
   7217  }
   7218 
   7219  if (!(aFlags & FRAMES_ARE_EMPTY) && line.next() != line_end) {
   7220    line.next()->MarkDirty();
   7221    line.next()->SetInvalidateTextRuns(true);
   7222  }
   7223 
   7224 #ifdef DEBUG
   7225  VerifyLines(true);
   7226  VerifyOverflowSituation();
   7227 #endif
   7228 
   7229  // Advance to next flow block if the frame has more continuations.
   7230  if (!aDeletedFrame) {
   7231    return;
   7232  }
   7233  nsBlockFrame* nextBlock = do_QueryFrame(aDeletedFrame->GetParent());
   7234  NS_ASSERTION(nextBlock, "Our child's continuation's parent is not a block?");
   7235  uint32_t flags = (aFlags & REMOVE_FIXED_CONTINUATIONS);
   7236  nextBlock->DoRemoveFrame(aContext, aDeletedFrame, flags);
   7237 }
   7238 
   7239 static bool FindBlockLineFor(nsIFrame* aChild, nsLineList::iterator aBegin,
   7240                             nsLineList::iterator aEnd,
   7241                             nsLineList::iterator* aResult) {
   7242  MOZ_ASSERT(aChild->IsBlockOutside());
   7243  for (nsLineList::iterator line = aBegin; line != aEnd; ++line) {
   7244    MOZ_ASSERT(line->GetChildCount() > 0);
   7245    if (line->IsBlock() && line->mFirstChild == aChild) {
   7246      MOZ_ASSERT(line->GetChildCount() == 1);
   7247      *aResult = line;
   7248      return true;
   7249    }
   7250  }
   7251  return false;
   7252 }
   7253 
   7254 static bool FindInlineLineFor(nsIFrame* aChild, const nsFrameList& aFrameList,
   7255                              nsLineList::iterator aBegin,
   7256                              nsLineList::iterator aEnd,
   7257                              nsLineList::iterator* aResult) {
   7258  MOZ_ASSERT(!aChild->IsBlockOutside());
   7259  for (nsLineList::iterator line = aBegin; line != aEnd; ++line) {
   7260    MOZ_ASSERT(line->GetChildCount() > 0);
   7261    if (!line->IsBlock()) {
   7262      // Optimize by comparing the line's last child first.
   7263      nsLineList::iterator next = line.next();
   7264      if (aChild == (next == aEnd ? aFrameList.LastChild()
   7265                                  : next->mFirstChild->GetPrevSibling()) ||
   7266          line->Contains(aChild)) {
   7267        *aResult = line;
   7268        return true;
   7269      }
   7270    }
   7271  }
   7272  return false;
   7273 }
   7274 
   7275 static bool FindLineFor(nsIFrame* aChild, const nsFrameList& aFrameList,
   7276                        nsLineList::iterator aBegin, nsLineList::iterator aEnd,
   7277                        nsLineList::iterator* aResult) {
   7278  return aChild->IsBlockOutside()
   7279             ? FindBlockLineFor(aChild, aBegin, aEnd, aResult)
   7280             : FindInlineLineFor(aChild, aFrameList, aBegin, aEnd, aResult);
   7281 }
   7282 
   7283 void nsBlockFrame::StealFrame(nsIFrame* aChild) {
   7284  MOZ_ASSERT(aChild->GetParent() == this);
   7285 
   7286  if (aChild->IsFloating()) {
   7287    RemoveFloat(aChild);
   7288    return;
   7289  }
   7290 
   7291  if (MaybeStealOverflowContainerFrame(aChild)) {
   7292    return;
   7293  }
   7294 
   7295  MOZ_ASSERT(!aChild->HasAnyStateBits(NS_FRAME_OUT_OF_FLOW));
   7296 
   7297  nsLineList::iterator line;
   7298  if (FindLineFor(aChild, mFrames, mLines.begin(), mLines.end(), &line)) {
   7299    RemoveFrameFromLine(aChild, line, mFrames, mLines);
   7300  } else {
   7301    FrameLines* overflowLines = GetOverflowLines();
   7302    DebugOnly<bool> found;
   7303    found = FindLineFor(aChild, overflowLines->mFrames,
   7304                        overflowLines->mLines.begin(),
   7305                        overflowLines->mLines.end(), &line);
   7306    MOZ_ASSERT(found, "Why can't we find aChild in our overflow lines?");
   7307    RemoveFrameFromLine(aChild, line, overflowLines->mFrames,
   7308                        overflowLines->mLines);
   7309    if (overflowLines->mLines.empty()) {
   7310      DestroyOverflowLines();
   7311    }
   7312  }
   7313 }
   7314 
   7315 void nsBlockFrame::RemoveFrameFromLine(nsIFrame* aChild,
   7316                                       nsLineList::iterator aLine,
   7317                                       nsFrameList& aFrameList,
   7318                                       nsLineList& aLineList) {
   7319  aFrameList.RemoveFrame(aChild);
   7320  if (aChild == aLine->mFirstChild) {
   7321    aLine->mFirstChild = aChild->GetNextSibling();
   7322  }
   7323  aLine->NoteFrameRemoved(aChild);
   7324  if (aLine->GetChildCount() > 0) {
   7325    aLine->MarkDirty();
   7326  } else {
   7327    // The line became empty - destroy it.
   7328    nsLineBox* lineBox = aLine;
   7329    aLine = aLineList.erase(aLine);
   7330    if (aLine != aLineList.end()) {
   7331      aLine->MarkPreviousMarginDirty();
   7332    }
   7333    FreeLineBox(lineBox);
   7334    ClearLineCursors();
   7335  }
   7336 }
   7337 
   7338 void nsBlockFrame::DeleteNextInFlowChild(DestroyContext& aContext,
   7339                                         nsIFrame* aNextInFlow,
   7340                                         bool aDeletingEmptyFrames) {
   7341  MOZ_ASSERT(aNextInFlow->GetPrevInFlow(), "bad next-in-flow");
   7342 
   7343  if (aNextInFlow->HasAnyStateBits(NS_FRAME_OUT_OF_FLOW |
   7344                                   NS_FRAME_IS_OVERFLOW_CONTAINER)) {
   7345    nsContainerFrame::DeleteNextInFlowChild(aContext, aNextInFlow,
   7346                                            aDeletingEmptyFrames);
   7347  } else {
   7348 #ifdef DEBUG
   7349    if (aDeletingEmptyFrames) {
   7350      nsLayoutUtils::AssertTreeOnlyEmptyNextInFlows(aNextInFlow);
   7351    }
   7352 #endif
   7353    DoRemoveFrame(aContext, aNextInFlow,
   7354                  aDeletingEmptyFrames ? FRAMES_ARE_EMPTY : 0);
   7355  }
   7356 }
   7357 
   7358 const nsStyleText* nsBlockFrame::StyleTextForLineLayout() {
   7359  // Return the pointer to an unmodified style text
   7360  return StyleText();
   7361 }
   7362 
   7363 void nsBlockFrame::ReflowFloat(BlockReflowState& aState, ReflowInput& aFloatRI,
   7364                               nsIFrame* aFloat,
   7365                               nsReflowStatus& aReflowStatus) {
   7366  MOZ_ASSERT(aReflowStatus.IsEmpty(),
   7367             "Caller should pass a fresh reflow status!");
   7368  MOZ_ASSERT(aFloat->HasAnyStateBits(NS_FRAME_OUT_OF_FLOW),
   7369             "aFloat must be an out-of-flow frame");
   7370 
   7371  WritingMode wm = aState.mReflowInput.GetWritingMode();
   7372 
   7373  // Setup a block reflow context to reflow the float.
   7374  nsBlockReflowContext brc(aState.mPresContext, aState.mReflowInput);
   7375 
   7376  nsIFrame* clearanceFrame = nullptr;
   7377  do {
   7378    CollapsingMargin margin;
   7379    bool mayNeedRetry = false;
   7380    aFloatRI.mDiscoveredClearance = nullptr;
   7381    // Only first in flow gets a block-start margin.
   7382    if (!aFloat->GetPrevInFlow()) {
   7383      brc.ComputeCollapsedBStartMargin(aFloatRI, &margin, clearanceFrame,
   7384                                       &mayNeedRetry);
   7385 
   7386      if (mayNeedRetry && !clearanceFrame) {
   7387        aFloatRI.mDiscoveredClearance = &clearanceFrame;
   7388        // We don't need to push the float manager state because the the block
   7389        // has its own float manager that will be destroyed and recreated
   7390      }
   7391    }
   7392 
   7393    // When reflowing a float, aSpace argument doesn't matter because we pass
   7394    // nullptr to aLine and we don't call nsBlockReflowContext::PlaceBlock()
   7395    // later.
   7396    brc.ReflowBlock(LogicalRect(wm), true, margin, 0, nullptr, aFloatRI,
   7397                    aReflowStatus, aState);
   7398  } while (clearanceFrame);
   7399 
   7400  if (aFloat->IsLetterFrame()) {
   7401    // We never split floating first letters; an incomplete status for such
   7402    // frames simply means that there is more content to be reflowed on the
   7403    // line.
   7404    if (aReflowStatus.IsIncomplete()) {
   7405      aReflowStatus.Reset();
   7406    }
   7407  }
   7408 
   7409  NS_ASSERTION(aReflowStatus.IsFullyComplete() ||
   7410                   aFloatRI.AvailableBSize() != NS_UNCONSTRAINEDSIZE,
   7411               "The status can only be incomplete or overflow-incomplete if "
   7412               "the available block-size is constrained!");
   7413 
   7414  if (aReflowStatus.NextInFlowNeedsReflow()) {
   7415    aState.mReflowStatus.SetNextInFlowNeedsReflow();
   7416  }
   7417 
   7418  const ReflowOutput& metrics = brc.GetMetrics();
   7419 
   7420  // Set the rect, make sure the view is properly sized and positioned,
   7421  // and tell the frame we're done reflowing it
   7422  // XXXldb This seems like the wrong place to be doing this -- shouldn't
   7423  // we be doing this in BlockReflowState::FlowAndPlaceFloat after
   7424  // we've positioned the float, and shouldn't we be doing the equivalent
   7425  // of |PlaceFrameView| here?
   7426  WritingMode metricsWM = metrics.GetWritingMode();
   7427  aFloat->SetSize(metricsWM, metrics.Size(metricsWM));
   7428  aFloat->DidReflow(aState.mPresContext, &aFloatRI);
   7429 }
   7430 
   7431 UsedClear nsBlockFrame::FindTrailingClear() {
   7432  for (nsBlockFrame* b = this; b;
   7433       b = static_cast<nsBlockFrame*>(b->GetPrevInFlow())) {
   7434    auto endLine = b->LinesRBegin();
   7435    if (endLine != b->LinesREnd()) {
   7436      return endLine->FloatClearTypeAfter();
   7437    }
   7438  }
   7439  return UsedClear::None;
   7440 }
   7441 
   7442 void nsBlockFrame::ReflowPushedFloats(BlockReflowState& aState,
   7443                                      OverflowAreas& aOverflowAreas) {
   7444  // Pushed floats live at the start of our float list; see comment
   7445  // above nsBlockFrame::DrainPushedFloats.
   7446  nsFrameList* floats = GetFloats();
   7447  nsIFrame* f = floats ? floats->FirstChild() : nullptr;
   7448  nsIFrame* prev = nullptr;
   7449  while (f && f->HasAnyStateBits(NS_FRAME_IS_PUSHED_OUT_OF_FLOW)) {
   7450    MOZ_ASSERT(prev == f->GetPrevSibling());
   7451    // When we push a first-continuation float in a non-initial reflow,
   7452    // it's possible that we end up with two continuations with the same
   7453    // parent.  This happens if, on the previous reflow of the block or
   7454    // a previous reflow of the line containing the block, the float was
   7455    // split between continuations A and B of the parent, but on the
   7456    // current reflow, none of the float can fit in A.
   7457    //
   7458    // When this happens, we might even have the two continuations
   7459    // out-of-order due to the management of the pushed floats.  In
   7460    // particular, if the float's placeholder was in a pushed line that
   7461    // we reflowed before it was pushed, and we split the float during
   7462    // that reflow, we might have the continuation of the float before
   7463    // the float itself.  (In the general case, however, it's correct
   7464    // for floats in the pushed floats list to come before floats
   7465    // anchored in pushed lines; however, in this case it's wrong.  We
   7466    // should probably find a way to fix it somehow, since it leads to
   7467    // incorrect layout in some cases.)
   7468    //
   7469    // When we have these out-of-order continuations, we might hit the
   7470    // next-continuation before the previous-continuation.  When that
   7471    // happens, just push it.  When we reflow the next continuation,
   7472    // we'll either pull all of its content back and destroy it (by
   7473    // calling DeleteNextInFlowChild), or nsBlockFrame::SplitFloat will
   7474    // pull it out of its current position and push it again (and
   7475    // potentially repeat this cycle for the next continuation, although
   7476    // hopefully then they'll be in the right order).
   7477    //
   7478    // We should also need this code for the in-order case if the first
   7479    // continuation of a float gets moved across more than one
   7480    // continuation of the containing block.  In this case we'd manage
   7481    // to push the second continuation without this check, but not the
   7482    // third and later.
   7483    nsIFrame* prevContinuation = f->GetPrevContinuation();
   7484    if (prevContinuation && prevContinuation->GetParent() == f->GetParent()) {
   7485      floats->RemoveFrame(f);
   7486      if (floats->IsEmpty()) {
   7487        StealFloats()->Delete(PresShell());
   7488        floats = nullptr;
   7489      }
   7490      aState.AppendPushedFloatChain(f);
   7491      if (!floats) {
   7492        // The floats list becomes empty after removing |f|. Bail out.
   7493        f = prev = nullptr;
   7494        break;
   7495      }
   7496      // Even if we think |floats| is valid, AppendPushedFloatChain() can also
   7497      // push |f|'s next-in-flows in our floats list to our pushed floats list.
   7498      // If all the floats in the floats list are pushed, the floats list will
   7499      // be deleted, and |floats| will be stale and poisoned. Therefore, we need
   7500      // to get the floats list again to check its validity.
   7501      floats = GetFloats();
   7502      if (!floats) {
   7503        f = prev = nullptr;
   7504        break;
   7505      }
   7506      f = !prev ? floats->FirstChild() : prev->GetNextSibling();
   7507      continue;
   7508    }
   7509 
   7510    // Always call FlowAndPlaceFloat; we might need to place this float if it
   7511    // didn't belong to this block the last time it was reflowed.  Note that if
   7512    // the float doesn't get placed, we don't consider its overflow areas.
   7513    // (Not-getting-placed means it didn't fit and we pushed it instead of
   7514    // placing it, and its position could be stale.)
   7515    if (aState.FlowAndPlaceFloat(f) ==
   7516        BlockReflowState::PlaceFloatResult::Placed) {
   7517      ConsiderChildOverflow(aOverflowAreas, f);
   7518    }
   7519 
   7520    // If f is the only child in the floats list, pushing it to the pushed
   7521    // floats list in FlowAndPlaceFloat() can result in the floats list being
   7522    // deleted. Get the floats list again.
   7523    floats = GetFloats();
   7524    if (!floats) {
   7525      f = prev = nullptr;
   7526      break;
   7527    }
   7528 
   7529    nsIFrame* next = !prev ? floats->FirstChild() : prev->GetNextSibling();
   7530    if (next == f) {
   7531      // We didn't push |f| so its next-sibling is next.
   7532      next = f->GetNextSibling();
   7533      prev = f;
   7534    }  // else: we did push |f| so |prev|'s new next-sibling is next.
   7535    f = next;
   7536  }
   7537 
   7538  // If there are pushed or split floats, then we may need to continue BR
   7539  // clearance
   7540  if (auto [bCoord, result] = aState.ClearFloats(0, UsedClear::Both);
   7541      result != ClearFloatsResult::BCoordNoChange) {
   7542    (void)bCoord;
   7543    if (auto* prevBlock = static_cast<nsBlockFrame*>(GetPrevInFlow())) {
   7544      aState.mTrailingClearFromPIF = prevBlock->FindTrailingClear();
   7545    }
   7546  }
   7547 }
   7548 
   7549 void nsBlockFrame::RecoverFloats(nsFloatManager& aFloatManager, WritingMode aWM,
   7550                                 const nsSize& aContainerSize) {
   7551  // Recover our own floats
   7552  nsIFrame* stop = nullptr;  // Stop before we reach pushed floats that
   7553                             // belong to our next-in-flow
   7554  const nsFrameList* floats = GetFloats();
   7555  for (nsIFrame* f = floats ? floats->FirstChild() : nullptr; f && f != stop;
   7556       f = f->GetNextSibling()) {
   7557    LogicalRect region = nsFloatManager::GetRegionFor(aWM, f, aContainerSize);
   7558    aFloatManager.AddFloat(f, region, aWM, aContainerSize);
   7559    if (!stop && f->GetNextInFlow()) {
   7560      stop = f->GetNextInFlow();
   7561    }
   7562  }
   7563 
   7564  // Recurse into our overflow container children
   7565  for (nsIFrame* oc =
   7566           GetChildList(FrameChildListID::OverflowContainers).FirstChild();
   7567       oc; oc = oc->GetNextSibling()) {
   7568    RecoverFloatsFor(oc, aFloatManager, aWM, aContainerSize);
   7569  }
   7570 
   7571  // Recurse into our normal children
   7572  for (const auto& line : Lines()) {
   7573    if (line.IsBlock()) {
   7574      RecoverFloatsFor(line.mFirstChild, aFloatManager, aWM, aContainerSize);
   7575    }
   7576  }
   7577 }
   7578 
   7579 void nsBlockFrame::RecoverFloatsFor(nsIFrame* aFrame,
   7580                                    nsFloatManager& aFloatManager,
   7581                                    WritingMode aWM,
   7582                                    const nsSize& aContainerSize) {
   7583  MOZ_ASSERT(aFrame, "null frame");
   7584 
   7585  // Only blocks have floats
   7586  nsBlockFrame* block = do_QueryFrame(aFrame);
   7587  // Don't recover any state inside a block that has its own float manager
   7588  // (we don't currently have any blocks like this, though, thanks to our
   7589  // use of extra frames for 'overflow')
   7590  if (block && !nsBlockFrame::BlockNeedsFloatManager(block)) {
   7591    // If the element is relatively positioned, then adjust x and y
   7592    // accordingly so that we consider relatively positioned frames
   7593    // at their original position.
   7594 
   7595    const LogicalRect rect = block->GetLogicalNormalRect(aWM, aContainerSize);
   7596    nscoord lineLeft = rect.LineLeft(aWM, aContainerSize);
   7597    nscoord blockStart = rect.BStart(aWM);
   7598    aFloatManager.Translate(lineLeft, blockStart);
   7599    block->RecoverFloats(aFloatManager, aWM, aContainerSize);
   7600    aFloatManager.Translate(-lineLeft, -blockStart);
   7601  }
   7602 }
   7603 
   7604 bool nsBlockFrame::HasPushedFloatsFromPrevContinuation() const {
   7605  if (const nsFrameList* floats = GetFloats()) {
   7606    // If we have pushed floats, then they should be at the beginning of our
   7607    // float list.
   7608    if (floats->FirstChild()->HasAnyStateBits(NS_FRAME_IS_PUSHED_OUT_OF_FLOW)) {
   7609      return true;
   7610    }
   7611 #ifdef DEBUG
   7612    // Double-check the above assertion that pushed floats should be at the
   7613    // beginning of our floats list.
   7614    for (nsIFrame* f : *floats) {
   7615      NS_ASSERTION(!f->HasAnyStateBits(NS_FRAME_IS_PUSHED_OUT_OF_FLOW),
   7616                   "pushed floats must be at the beginning of the float list");
   7617    }
   7618 #endif
   7619  }
   7620 
   7621  // We may have a pending push of pushed floats, too.
   7622  return HasPushedFloats();
   7623 }
   7624 
   7625 //////////////////////////////////////////////////////////////////////
   7626 // Painting, event handling
   7627 
   7628 #ifdef DEBUG
   7629 static void ComputeInkOverflowArea(nsLineList& aLines, nscoord aWidth,
   7630                                   nscoord aHeight, nsRect& aResult) {
   7631  nscoord xa = 0, ya = 0, xb = aWidth, yb = aHeight;
   7632  for (nsLineList::iterator line = aLines.begin(), line_end = aLines.end();
   7633       line != line_end; ++line) {
   7634    // Compute min and max x/y values for the reflowed frame's
   7635    // combined areas
   7636    nsRect inkOverflow(line->InkOverflowRect());
   7637    nscoord x = inkOverflow.x;
   7638    nscoord y = inkOverflow.y;
   7639    nscoord xmost = x + inkOverflow.width;
   7640    nscoord ymost = y + inkOverflow.height;
   7641    if (x < xa) {
   7642      xa = x;
   7643    }
   7644    if (xmost > xb) {
   7645      xb = xmost;
   7646    }
   7647    if (y < ya) {
   7648      ya = y;
   7649    }
   7650    if (ymost > yb) {
   7651      yb = ymost;
   7652    }
   7653  }
   7654 
   7655  aResult.x = xa;
   7656  aResult.y = ya;
   7657  aResult.width = xb - xa;
   7658  aResult.height = yb - ya;
   7659 }
   7660 #endif
   7661 
   7662 #ifdef DEBUG
   7663 static void DebugOutputDrawLine(int32_t aDepth, nsLineBox* aLine, bool aDrawn) {
   7664  if (nsBlockFrame::gNoisyDamageRepair) {
   7665    nsIFrame::IndentBy(stdout, aDepth + 1);
   7666    nsRect lineArea = aLine->InkOverflowRect();
   7667    printf("%s line=%p bounds=%d,%d,%d,%d ca=%d,%d,%d,%d\n",
   7668           aDrawn ? "draw" : "skip", static_cast<void*>(aLine), aLine->IStart(),
   7669           aLine->BStart(), aLine->ISize(), aLine->BSize(), lineArea.x,
   7670           lineArea.y, lineArea.width, lineArea.height);
   7671  }
   7672 }
   7673 #endif
   7674 
   7675 static void DisplayLine(nsDisplayListBuilder* aBuilder,
   7676                        nsBlockFrame::LineIterator& aLine,
   7677                        const bool aLineInLine, const nsDisplayListSet& aLists,
   7678                        nsBlockFrame* aFrame, TextOverflow* aTextOverflow,
   7679                        uint32_t aLineNumberForTextOverflow, int32_t aDepth,
   7680                        int32_t& aDrawnLines, bool& aFoundLineClamp) {
   7681 #ifdef DEBUG
   7682  if (nsBlockFrame::gLamePaintMetrics) {
   7683    aDrawnLines++;
   7684  }
   7685  const bool intersect =
   7686      aLine->InkOverflowRect().Intersects(aBuilder->GetDirtyRect());
   7687  DebugOutputDrawLine(aDepth, aLine.get(), intersect);
   7688 #endif
   7689 
   7690  // Collect our line's display items in a temporary nsDisplayListCollection,
   7691  // so that we can apply any "text-overflow" clipping to the entire collection
   7692  // without affecting previous lines.
   7693  nsDisplayListCollection collection(aBuilder);
   7694 
   7695  // Block-level child backgrounds go on the blockBorderBackgrounds list ...
   7696  // Inline-level child backgrounds go on the regular child content list.
   7697  nsDisplayListSet childLists(
   7698      collection,
   7699      aLineInLine ? collection.Content() : collection.BlockBorderBackgrounds());
   7700 
   7701  auto flags =
   7702      aLineInLine
   7703          ? nsIFrame::DisplayChildFlags(nsIFrame::DisplayChildFlag::Inline)
   7704          : nsIFrame::DisplayChildFlags();
   7705 
   7706  nsIFrame* kid = aLine->mFirstChild;
   7707  int32_t n = aLine->GetChildCount();
   7708  while (--n >= 0) {
   7709    aFrame->BuildDisplayListForChild(aBuilder, kid, childLists, flags);
   7710    kid = kid->GetNextSibling();
   7711  }
   7712 
   7713  if (aFrame->HasLineClampEllipsisDescendant() && !aLineInLine) {
   7714    if (nsBlockFrame* f = GetAsLineClampDescendant(aLine->mFirstChild)) {
   7715      if (f->HasLineClampEllipsis() || f->HasLineClampEllipsisDescendant()) {
   7716        aFoundLineClamp = true;
   7717      }
   7718    }
   7719  }
   7720 
   7721  if (aTextOverflow && aLineInLine) {
   7722    aTextOverflow->ProcessLine(collection, aLine.get(),
   7723                               aLineNumberForTextOverflow);
   7724  }
   7725 
   7726  collection.MoveTo(aLists);
   7727 }
   7728 
   7729 void nsBlockFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder,
   7730                                    const nsDisplayListSet& aLists) {
   7731  int32_t drawnLines;  // Will only be used if set (gLamePaintMetrics).
   7732  int32_t depth = 0;
   7733 #ifdef DEBUG
   7734  if (gNoisyDamageRepair) {
   7735    nsRect dirty = aBuilder->GetDirtyRect();
   7736    depth = GetDepth();
   7737    nsRect ca;
   7738    ::ComputeInkOverflowArea(mLines, mRect.width, mRect.height, ca);
   7739    nsIFrame::IndentBy(stdout, depth);
   7740    ListTag(stdout);
   7741    printf(": bounds=%d,%d,%d,%d dirty(absolute)=%d,%d,%d,%d ca=%d,%d,%d,%d\n",
   7742           mRect.x, mRect.y, mRect.width, mRect.height, dirty.x, dirty.y,
   7743           dirty.width, dirty.height, ca.x, ca.y, ca.width, ca.height);
   7744  }
   7745  PRTime start = 0;  // Initialize these variables to silence the compiler.
   7746  if (gLamePaintMetrics) {
   7747    start = PR_Now();
   7748    drawnLines = 0;
   7749  }
   7750 #endif
   7751 
   7752  // TODO(heycam): Should we boost the load priority of any shape-outside
   7753  // images using CATEGORY_DISPLAY, now that this block is being displayed?
   7754  // We don't have a float manager here.
   7755 
   7756  DisplayBorderBackgroundOutline(aBuilder, aLists);
   7757 
   7758  if (HidesContent()) {
   7759    return;
   7760  }
   7761 
   7762  if (GetPrevInFlow()) {
   7763    DisplayOverflowContainers(aBuilder, aLists);
   7764    DisplayPushedAbsoluteFrames(aBuilder, aLists);
   7765    for (nsIFrame* f : GetChildList(FrameChildListID::Float)) {
   7766      if (f->HasAnyStateBits(NS_FRAME_IS_PUSHED_OUT_OF_FLOW)) {
   7767        BuildDisplayListForChild(aBuilder, f, aLists);
   7768      }
   7769    }
   7770  }
   7771 
   7772  aBuilder->MarkFramesForDisplayList(this,
   7773                                     GetChildList(FrameChildListID::Float));
   7774 
   7775  if (nsIFrame* outsideMarker = GetOutsideMarker()) {
   7776    // Display outside ::marker manually.
   7777    BuildDisplayListForChild(aBuilder, outsideMarker, aLists);
   7778  }
   7779 
   7780  // Prepare for text-overflow processing.
   7781  Maybe<TextOverflow> textOverflow =
   7782      TextOverflow::WillProcessLines(aBuilder, this);
   7783 
   7784  const bool hasDescendantPlaceHolders =
   7785      HasAnyStateBits(NS_FRAME_FORCE_DISPLAY_LIST_DESCEND_INTO) ||
   7786      ForceDescendIntoIfVisible() || aBuilder->GetIncludeAllOutOfFlows();
   7787 
   7788  const auto ShouldDescendIntoLine = [&](const nsRect& aLineArea) -> bool {
   7789    // TODO(miko): Unfortunately |descendAlways| cannot be cached, because with
   7790    // some frame trees, building display list for child lines can change it.
   7791    // See bug 1552789.
   7792    const bool descendAlways =
   7793        HasAnyStateBits(NS_FRAME_FORCE_DISPLAY_LIST_DESCEND_INTO) ||
   7794        aBuilder->GetIncludeAllOutOfFlows();
   7795 
   7796    return descendAlways || aLineArea.Intersects(aBuilder->GetDirtyRect()) ||
   7797           (ForceDescendIntoIfVisible() &&
   7798            aLineArea.Intersects(aBuilder->GetVisibleRect()));
   7799  };
   7800 
   7801  Maybe<nscolor> backplateColor;
   7802 
   7803  // We'll try to draw an accessibility backplate behind text (to ensure it's
   7804  // readable over any possible background-images), if all of the following
   7805  // hold:
   7806  //    (A) we are not honoring the document colors
   7807  //    (B) the backplate feature is preffed on
   7808  //    (C) the force color adjust property is set to auto
   7809  if (PresContext()->ForcingColors() &&
   7810      StaticPrefs::browser_display_permit_backplate() &&
   7811      StyleText()->mForcedColorAdjust != StyleForcedColorAdjust::None) {
   7812    backplateColor.emplace(GetBackplateColor(this));
   7813  }
   7814 
   7815  const bool canUseCursor = [&] {
   7816    if (hasDescendantPlaceHolders) {
   7817      // Don't use the line cursor if we might have a descendant placeholder. It
   7818      // might skip lines that contain placeholders but don't themselves
   7819      // intersect with the dirty area.
   7820      //
   7821      // In particular, we really want to check ShouldDescendIntoFrame()
   7822      // on all our child frames, but that might be expensive.  So we
   7823      // approximate it by checking it on |this|; if it's true for any
   7824      // frame in our child list, it's also true for |this|.
   7825      return false;
   7826    }
   7827    if (textOverflow.isSome()) {
   7828      // Also skip the cursor if we're creating text overflow markers, since we
   7829      // need to know what line number we're up to in order to generate unique
   7830      // display item keys.
   7831      return false;
   7832    }
   7833    if (backplateColor) {
   7834      // Cursors should be skipped if we're drawing backplates behind text. When
   7835      // backplating we consider consecutive runs of text as a whole, which
   7836      // requires we iterate through all lines to find our backplate size.
   7837      return false;
   7838    }
   7839    if ((HasLineClampEllipsis() || HasLineClampEllipsisDescendant()) &&
   7840        StaticPrefs::layout_css_webkit_line_clamp_skip_paint()) {
   7841      // We can't use the cursor if we're in a line-clamping situation, and
   7842      // we're configured to not paint its clamped content, as we need to know
   7843      // whether we've hit the clamp point which requires iterating over all
   7844      // lines.
   7845      return false;
   7846    }
   7847    return true;
   7848  }();
   7849 
   7850  nsLineBox* cursor = canUseCursor
   7851                          ? GetFirstLineContaining(aBuilder->GetDirtyRect().y)
   7852                          : nullptr;
   7853  LineIterator line_end = LinesEnd();
   7854 
   7855  TextOverflow* textOverflowPtr = textOverflow.ptrOr(nullptr);
   7856  bool foundClamp = false;
   7857 
   7858  if (cursor) {
   7859    for (LineIterator line = mLines.begin(cursor); line != line_end; ++line) {
   7860      const nsRect lineArea = line->InkOverflowRect();
   7861      if (!lineArea.IsEmpty()) {
   7862        // Because we have a cursor, the combinedArea.ys are non-decreasing.
   7863        // Once we've passed aDirtyRect.YMost(), we can never see it again.
   7864        if (lineArea.y >= aBuilder->GetDirtyRect().YMost()) {
   7865          break;
   7866        }
   7867        MOZ_ASSERT(textOverflow.isNothing());
   7868 
   7869        if (ShouldDescendIntoLine(lineArea)) {
   7870          DisplayLine(aBuilder, line, line->IsInline(), aLists, this, nullptr,
   7871                      0, depth, drawnLines, foundClamp);
   7872          MOZ_ASSERT(!foundClamp ||
   7873                     !StaticPrefs::layout_css_webkit_line_clamp_skip_paint());
   7874        }
   7875      }
   7876    }
   7877  } else {
   7878    bool nonDecreasingYs = true;
   7879    uint32_t lineCount = 0;
   7880    nscoord lastY = INT32_MIN;
   7881    nscoord lastYMost = INT32_MIN;
   7882 
   7883    // A frame's display list cannot contain more than one copy of a
   7884    // given display item unless the items are uniquely identifiable.
   7885    // Because backplate occasionally requires multiple
   7886    // SolidColor items, we use an index (backplateIndex) to maintain
   7887    // uniqueness among them. Note this is a mapping of index to
   7888    // item, and the mapping is stable even if the dirty rect changes.
   7889    uint16_t backplateIndex = 0;
   7890    nsRect curBackplateArea;
   7891 
   7892    auto AddBackplate = [&]() {
   7893      aLists.BorderBackground()->AppendNewToTopWithIndex<nsDisplaySolidColor>(
   7894          aBuilder, this, backplateIndex, curBackplateArea,
   7895          backplateColor.value());
   7896    };
   7897 
   7898    for (LineIterator line = LinesBegin(); line != line_end; ++line) {
   7899      const nsRect lineArea = line->InkOverflowRect();
   7900      const bool lineInLine = line->IsInline();
   7901 
   7902      if ((lineInLine && textOverflowPtr) || ShouldDescendIntoLine(lineArea)) {
   7903        DisplayLine(aBuilder, line, lineInLine, aLists, this, textOverflowPtr,
   7904                    lineCount, depth, drawnLines, foundClamp);
   7905      }
   7906 
   7907      if (!lineInLine && !curBackplateArea.IsEmpty()) {
   7908        // If we have encountered a non-inline line but were previously
   7909        // forming a backplate, we should add the backplate to the display
   7910        // list as-is and render future backplates disjointly.
   7911        MOZ_ASSERT(backplateColor,
   7912                   "if this master switch is off, curBackplateArea "
   7913                   "must be empty and we shouldn't get here");
   7914        AddBackplate();
   7915        backplateIndex++;
   7916        curBackplateArea = nsRect();
   7917      }
   7918 
   7919      if (!lineArea.IsEmpty()) {
   7920        if (lineArea.y < lastY || lineArea.YMost() < lastYMost) {
   7921          nonDecreasingYs = false;
   7922        }
   7923        lastY = lineArea.y;
   7924        lastYMost = lineArea.YMost();
   7925        if (lineInLine && backplateColor && LineHasVisibleInlineText(line)) {
   7926          nsRect lineBackplate = GetLineTextArea(line, aBuilder) +
   7927                                 aBuilder->ToReferenceFrame(this);
   7928          if (curBackplateArea.IsEmpty()) {
   7929            curBackplateArea = lineBackplate;
   7930          } else {
   7931            curBackplateArea.OrWith(lineBackplate);
   7932          }
   7933        }
   7934      }
   7935      foundClamp = foundClamp || line->HasLineClampEllipsis();
   7936      if (foundClamp &&
   7937          StaticPrefs::layout_css_webkit_line_clamp_skip_paint()) {
   7938        break;
   7939      }
   7940      lineCount++;
   7941    }
   7942 
   7943    if (nonDecreasingYs && lineCount >= MIN_LINES_NEEDING_CURSOR) {
   7944      SetupLineCursorForDisplay();
   7945    }
   7946 
   7947    if (!curBackplateArea.IsEmpty()) {
   7948      AddBackplate();
   7949    }
   7950  }
   7951 
   7952  if (textOverflow.isSome()) {
   7953    // Put any text-overflow:ellipsis markers on top of the non-positioned
   7954    // content of the block's lines. (If we ever start sorting the Content()
   7955    // list this will end up in the wrong place.)
   7956    aLists.Content()->AppendToTop(&textOverflow->GetMarkers());
   7957  }
   7958 
   7959 #ifdef DEBUG
   7960  if (gLamePaintMetrics) {
   7961    PRTime end = PR_Now();
   7962 
   7963    int32_t numLines = mLines.size();
   7964    if (!numLines) {
   7965      numLines = 1;
   7966    }
   7967    PRTime lines, deltaPerLine, delta;
   7968    lines = int64_t(numLines);
   7969    delta = end - start;
   7970    deltaPerLine = delta / lines;
   7971 
   7972    ListTag(stdout);
   7973    char buf[400];
   7974    SprintfLiteral(buf,
   7975                   ": %" PRId64 " elapsed (%" PRId64
   7976                   " per line) lines=%d drawn=%d skip=%d",
   7977                   delta, deltaPerLine, numLines, drawnLines,
   7978                   numLines - drawnLines);
   7979    printf("%s\n", buf);
   7980  }
   7981 #endif
   7982 }
   7983 
   7984 #ifdef ACCESSIBILITY
   7985 a11y::AccType nsBlockFrame::AccessibleType() {
   7986  if (IsTableCaption()) {
   7987    return GetRect().IsEmpty() ? a11y::eNoType : a11y::eHTMLCaptionType;
   7988  }
   7989 
   7990  // block frame may be for <hr>
   7991  if (mContent->IsHTMLElement(nsGkAtoms::hr)) {
   7992    return a11y::eHTMLHRType;
   7993  }
   7994 
   7995  if (IsButtonLike()) {
   7996    return a11y::eHTMLButtonType;
   7997  }
   7998 
   7999  if (HasMarker()) {
   8000    // Create special list item accessible since we have a ::marker.
   8001    return a11y::eHTMLLiType;
   8002  }
   8003 
   8004  // XXXsmaug What if we're in the shadow dom?
   8005  if (!mContent->GetParent()) {
   8006    // Don't create accessible objects for the root content node, they are
   8007    // redundant with the nsDocAccessible object created with the document
   8008    // node
   8009    return a11y::eNoType;
   8010  }
   8011 
   8012  if (mContent == mContent->OwnerDoc()->GetBody()) {
   8013    // Don't create accessible objects for the body, they are redundant with
   8014    // the nsDocAccessible object created with the document node
   8015    return a11y::eNoType;
   8016  }
   8017 
   8018  // Not a list item with a ::marker, treat as normal HTML container.
   8019  return a11y::eHyperTextType;
   8020 }
   8021 #endif
   8022 
   8023 void nsBlockFrame::SetupLineCursorForDisplay() {
   8024  if (mLines.empty() || HasProperty(LineCursorPropertyDisplay())) {
   8025    return;
   8026  }
   8027 
   8028  SetProperty(LineCursorPropertyDisplay(), mLines.front());
   8029  AddStateBits(NS_BLOCK_HAS_LINE_CURSOR);
   8030 }
   8031 
   8032 void nsBlockFrame::SetupLineCursorForQuery() {
   8033  if (mLines.empty() || HasProperty(LineCursorPropertyQuery())) {
   8034    return;
   8035  }
   8036 
   8037  SetProperty(LineCursorPropertyQuery(), mLines.front());
   8038  AddStateBits(NS_BLOCK_HAS_LINE_CURSOR);
   8039 }
   8040 
   8041 nsLineBox* nsBlockFrame::GetFirstLineContaining(nscoord y) {
   8042  // Although this looks like a "querying" method, it is used by the
   8043  // display-list building code, so uses the Display cursor.
   8044  nsLineBox* property = GetLineCursorForDisplay();
   8045  if (!property) {
   8046    return nullptr;
   8047  }
   8048  LineIterator cursor = mLines.begin(property);
   8049  nsRect cursorArea = cursor->InkOverflowRect();
   8050 
   8051  while ((cursorArea.IsEmpty() || cursorArea.YMost() > y) &&
   8052         cursor != mLines.front()) {
   8053    cursor = cursor.prev();
   8054    cursorArea = cursor->InkOverflowRect();
   8055  }
   8056  while ((cursorArea.IsEmpty() || cursorArea.YMost() <= y) &&
   8057         cursor != mLines.back()) {
   8058    cursor = cursor.next();
   8059    cursorArea = cursor->InkOverflowRect();
   8060  }
   8061 
   8062  if (cursor.get() != property) {
   8063    SetProperty(LineCursorPropertyDisplay(), cursor.get());
   8064  }
   8065 
   8066  return cursor.get();
   8067 }
   8068 
   8069 /* virtual */
   8070 void nsBlockFrame::ChildIsDirty(nsIFrame* aChild) {
   8071  // See if the child is absolutely positioned
   8072  if (aChild->IsAbsolutelyPositioned()) {
   8073    // do nothing
   8074  } else if (aChild == GetOutsideMarker()) {
   8075    // The ::marker lives in the first line, unless the first line has
   8076    // height 0 and there is a second line, in which case it lives
   8077    // in the second line.
   8078    LineIterator markerLine = LinesBegin();
   8079    if (markerLine != LinesEnd() && markerLine->BSize() == 0 &&
   8080        markerLine != mLines.back()) {
   8081      markerLine = markerLine.next();
   8082    }
   8083 
   8084    if (markerLine != LinesEnd()) {
   8085      MarkLineDirty(markerLine, &mLines);
   8086    }
   8087    // otherwise we have an empty line list, and ReflowDirtyLines
   8088    // will handle reflowing the ::marker.
   8089  } else {
   8090    // Note that we should go through our children to mark lines dirty
   8091    // before the next reflow.  Doing it now could make things O(N^2)
   8092    // since finding the right line is O(N).
   8093    // We don't need to worry about marking lines on the overflow list
   8094    // as dirty; we're guaranteed to reflow them if we take them off the
   8095    // overflow list.
   8096    // However, we might have gotten a float, in which case we need to
   8097    // reflow the line containing its placeholder.  So find the
   8098    // ancestor-or-self of the placeholder that's a child of the block,
   8099    // and mark it as NS_FRAME_HAS_DIRTY_CHILDREN too, so that we mark
   8100    // its line dirty when we handle NS_BLOCK_LOOK_FOR_DIRTY_FRAMES.
   8101    // We need to take some care to handle the case where a float is in
   8102    // a different continuation than its placeholder, including marking
   8103    // an extra block with NS_BLOCK_LOOK_FOR_DIRTY_FRAMES.
   8104    if (!aChild->HasAnyStateBits(NS_FRAME_OUT_OF_FLOW)) {
   8105      AddStateBits(NS_BLOCK_LOOK_FOR_DIRTY_FRAMES);
   8106    } else {
   8107      NS_ASSERTION(aChild->IsFloating(), "should be a float");
   8108      nsIFrame* thisFC = FirstContinuation();
   8109      nsIFrame* placeholderPath = aChild->GetPlaceholderFrame();
   8110      // SVG code sometimes sends FrameNeedsReflow notifications during
   8111      // frame destruction, leading to null placeholders, but we're safe
   8112      // ignoring those.
   8113      if (placeholderPath) {
   8114        for (;;) {
   8115          nsIFrame* parent = placeholderPath->GetParent();
   8116          if (parent->GetContent() == mContent &&
   8117              parent->FirstContinuation() == thisFC) {
   8118            parent->AddStateBits(NS_BLOCK_LOOK_FOR_DIRTY_FRAMES);
   8119            break;
   8120          }
   8121          placeholderPath = parent;
   8122        }
   8123        placeholderPath->AddStateBits(NS_FRAME_HAS_DIRTY_CHILDREN);
   8124      }
   8125    }
   8126  }
   8127 
   8128  nsContainerFrame::ChildIsDirty(aChild);
   8129 }
   8130 
   8131 void nsBlockFrame::Init(nsIContent* aContent, nsContainerFrame* aParent,
   8132                        nsIFrame* aPrevInFlow) {
   8133  // These are all the block specific frame bits, they are copied from
   8134  // the prev-in-flow to a newly created next-in-flow, except for the
   8135  // NS_BLOCK_FLAGS_NON_INHERITED_MASK bits below.
   8136  constexpr nsFrameState NS_BLOCK_FLAGS_MASK =
   8137      NS_BLOCK_BFC | NS_BLOCK_HAS_FIRST_LETTER_STYLE |
   8138      NS_BLOCK_HAS_FIRST_LETTER_CHILD | NS_BLOCK_HAS_MARKER;
   8139 
   8140  // This is the subset of NS_BLOCK_FLAGS_MASK that is NOT inherited
   8141  // by default.  They should only be set on the first-in-flow.
   8142  constexpr nsFrameState NS_BLOCK_FLAGS_NON_INHERITED_MASK =
   8143      NS_BLOCK_HAS_FIRST_LETTER_CHILD | NS_BLOCK_HAS_MARKER;
   8144 
   8145  if (aPrevInFlow) {
   8146    // Copy over the inherited block frame bits from the prev-in-flow.
   8147    RemoveStateBits(NS_BLOCK_FLAGS_MASK);
   8148    AddStateBits(aPrevInFlow->GetStateBits() &
   8149                 (NS_BLOCK_FLAGS_MASK & ~NS_BLOCK_FLAGS_NON_INHERITED_MASK));
   8150  }
   8151 
   8152  nsContainerFrame::Init(aContent, aParent, aPrevInFlow);
   8153 
   8154  if (!aPrevInFlow ||
   8155      aPrevInFlow->HasAnyStateBits(NS_BLOCK_NEEDS_BIDI_RESOLUTION)) {
   8156    AddStateBits(NS_BLOCK_NEEDS_BIDI_RESOLUTION);
   8157  }
   8158 
   8159  if (EstablishesBFC(this)) {
   8160    AddStateBits(NS_BLOCK_BFC);
   8161  }
   8162 
   8163  if (HasAnyStateBits(NS_FRAME_FONT_INFLATION_CONTAINER) &&
   8164      HasAnyStateBits(NS_BLOCK_BFC)) {
   8165    AddStateBits(NS_FRAME_FONT_INFLATION_FLOW_ROOT);
   8166  }
   8167 }
   8168 
   8169 void nsBlockFrame::SetInitialChildList(ChildListID aListID,
   8170                                       nsFrameList&& aChildList) {
   8171  if (FrameChildListID::Float == aListID) {
   8172    nsFrameList* floats = EnsureFloats();
   8173    *floats = std::move(aChildList);
   8174  } else if (FrameChildListID::Principal == aListID) {
   8175 #ifdef DEBUG
   8176    // The only times a block that is an anonymous box is allowed to have a
   8177    // first-letter frame are when it's the block inside a non-anonymous cell,
   8178    // the block inside a fieldset, button or column set, or a scrolled content
   8179    // block, except for <select>.  Note that this means that blocks which are
   8180    // the anonymous block in {ib} splits do NOT get first-letter frames.
   8181    // Note that NS_BLOCK_HAS_FIRST_LETTER_STYLE gets set on all continuations
   8182    // of the block.
   8183    auto pseudo = Style()->GetPseudoType();
   8184    bool haveFirstLetterStyle =
   8185        (pseudo == PseudoStyleType::NotPseudo ||
   8186         PseudoStyle::IsElementBackedPseudo(pseudo) ||
   8187         (pseudo == PseudoStyleType::cellContent &&
   8188          !GetParent()->Style()->IsPseudoOrAnonBox()) ||
   8189         pseudo == PseudoStyleType::fieldsetContent ||
   8190         pseudo == PseudoStyleType::columnContent ||
   8191         (pseudo == PseudoStyleType::scrolledContent &&
   8192          !GetParent()->IsListControlFrame()) ||
   8193         pseudo == PseudoStyleType::mozSVGText) &&
   8194        !IsMathMLFrame() && !IsColumnSetWrapperFrame() &&
   8195        !IsComboboxControlFrame() &&
   8196        RefPtr<ComputedStyle>(GetFirstLetterStyle(PresContext())) != nullptr;
   8197    NS_ASSERTION(haveFirstLetterStyle ==
   8198                     HasAnyStateBits(NS_BLOCK_HAS_FIRST_LETTER_STYLE),
   8199                 "NS_BLOCK_HAS_FIRST_LETTER_STYLE state out of sync");
   8200 #endif
   8201 
   8202    AddFrames(std::move(aChildList), nullptr, nullptr);
   8203  } else {
   8204    nsContainerFrame::SetInitialChildList(aListID, std::move(aChildList));
   8205  }
   8206 }
   8207 
   8208 void nsBlockFrame::SetMarkerFrameForListItem(nsIFrame* aMarkerFrame) {
   8209  MOZ_ASSERT(aMarkerFrame);
   8210  MOZ_ASSERT(!HasMarker(), "How can we have a ::marker frame already?");
   8211 
   8212  if (StyleList()->mListStylePosition == StyleListStylePosition::Inside) {
   8213    SetProperty(InsideMarkerProperty(), aMarkerFrame);
   8214  } else {
   8215    SetProperty(OutsideMarkerProperty(),
   8216                new (PresShell()) nsFrameList(aMarkerFrame, aMarkerFrame));
   8217  }
   8218  AddStateBits(NS_BLOCK_HAS_MARKER);
   8219 }
   8220 
   8221 bool nsBlockFrame::MarkerIsEmpty(const nsIFrame* aMarker) const {
   8222  MOZ_ASSERT(mContent->GetPrimaryFrame()->StyleDisplay()->IsListItem() &&
   8223                 aMarker == GetOutsideMarker(),
   8224             "should only care about an outside ::marker");
   8225  const nsStyleList* list = aMarker->StyleList();
   8226  return aMarker->StyleContent()->mContent.IsNone() ||
   8227         (list->mListStyleType.IsNone() && list->mListStyleImage.IsNone() &&
   8228          aMarker->StyleContent()->NonAltContentItems().IsEmpty());
   8229 }
   8230 
   8231 bool nsBlockFrame::HasOutsideMarker() const {
   8232  return HasMarker() && HasProperty(OutsideMarkerProperty());
   8233 }
   8234 
   8235 void nsBlockFrame::ReflowOutsideMarker(nsIFrame* aMarkerFrame,
   8236                                       BlockReflowState& aState,
   8237                                       ReflowOutput& aMetrics,
   8238                                       nscoord aLineTop) {
   8239  const ReflowInput& ri = aState.mReflowInput;
   8240 
   8241  WritingMode markerWM = aMarkerFrame->GetWritingMode();
   8242  LogicalSize availSize(markerWM);
   8243  // Make up an inline-size since it doesn't really matter (XXX).
   8244  availSize.ISize(markerWM) = aState.ContentISize();
   8245  availSize.BSize(markerWM) = NS_UNCONSTRAINEDSIZE;
   8246 
   8247  ReflowInput reflowInput(aState.mPresContext, ri, aMarkerFrame, availSize,
   8248                          Nothing(), {}, {}, {ComputeSizeFlag::ShrinkWrap});
   8249  nsReflowStatus status;
   8250  aMarkerFrame->Reflow(aState.mPresContext, aMetrics, reflowInput, status);
   8251 
   8252  // Get the float available space using our saved state from before we
   8253  // started reflowing the block, so that we ignore any floats inside
   8254  // the block.
   8255  // FIXME: aLineTop isn't actually set correctly by some callers, since
   8256  // they reposition the line.
   8257  LogicalRect floatAvailSpace =
   8258      aState
   8259          .GetFloatAvailableSpaceWithState(ri.GetWritingMode(), aLineTop,
   8260                                           ShapeType::ShapeOutside,
   8261                                           &aState.mFloatManagerStateBefore)
   8262          .mRect;
   8263  // FIXME (bug 25888): need to check the entire region that the first
   8264  // line overlaps, not just the top pixel.
   8265 
   8266  // Place the ::marker now.  We want to place the ::marker relative to the
   8267  // border-box of the associated block (using the right/left margin of
   8268  // the ::marker frame as separation).  However, if a line box would be
   8269  // displaced by floats that are *outside* the associated block, we
   8270  // want to displace it by the same amount.  That is, we act as though
   8271  // the edge of the floats is the content-edge of the block, and place
   8272  // the ::marker at a position offset from there by the block's padding,
   8273  // the block's border, and the ::marker frame's margin.
   8274 
   8275  // IStart from floatAvailSpace gives us the content/float start edge
   8276  // in the current writing mode. Then we subtract out the start
   8277  // border/padding and the ::marker's width and margin to offset the position.
   8278  WritingMode wm = ri.GetWritingMode();
   8279  // Get the ::marker's margin, converted to our writing mode so that we can
   8280  // combine it with other logical values here.
   8281  LogicalMargin markerMargin = reflowInput.ComputedLogicalMargin(wm);
   8282  nscoord iStart = floatAvailSpace.IStart(wm) -
   8283                   ri.ComputedLogicalBorderPadding(wm).IStart(wm) -
   8284                   markerMargin.IEnd(wm) - aMetrics.ISize(wm);
   8285 
   8286  // Approximate the ::marker's position; vertical alignment will provide
   8287  // the final vertical location. We pass our writing-mode here, because
   8288  // it may be different from the ::marker frame's mode.
   8289  nscoord bStart = floatAvailSpace.BStart(wm);
   8290  aMarkerFrame->SetRect(
   8291      wm,
   8292      LogicalRect(wm, iStart, bStart, aMetrics.ISize(wm), aMetrics.BSize(wm)),
   8293      aState.ContainerSize());
   8294  aMarkerFrame->DidReflow(aState.mPresContext, &aState.mReflowInput);
   8295 }
   8296 
   8297 // This is used to scan frames for any float placeholders, add their
   8298 // floats to the list represented by aList, and remove the
   8299 // floats from whatever list they might be in. We don't search descendants
   8300 // that are float containing blocks.  Floats that or not children of 'this'
   8301 // are ignored (they are not added to aList).
   8302 void nsBlockFrame::DoCollectFloats(nsIFrame* aFrame, nsFrameList& aList,
   8303                                   bool aCollectSiblings) {
   8304  while (aFrame) {
   8305    // Don't descend into float containing blocks.
   8306    if (!aFrame->IsFloatContainingBlock()) {
   8307      nsIFrame* outOfFlowFrame =
   8308          aFrame->IsPlaceholderFrame()
   8309              ? nsLayoutUtils::GetFloatFromPlaceholder(aFrame)
   8310              : nullptr;
   8311      while (outOfFlowFrame && outOfFlowFrame->GetParent() == this) {
   8312        RemoveFloat(outOfFlowFrame);
   8313        // Remove the NS_FRAME_IS_PUSHED_OUT_OF_FLOW bit, in case
   8314        // |outOfFlowFrame| came from the PushedFloats list.
   8315        outOfFlowFrame->RemoveStateBits(NS_FRAME_IS_PUSHED_OUT_OF_FLOW);
   8316        aList.AppendFrame(nullptr, outOfFlowFrame);
   8317        outOfFlowFrame = outOfFlowFrame->GetNextInFlow();
   8318        // FIXME: By not pulling floats whose parent is one of our
   8319        // later siblings, are we risking the pushed floats getting
   8320        // out-of-order?
   8321        // XXXmats nsInlineFrame's lazy reparenting depends on NOT doing that.
   8322      }
   8323 
   8324      DoCollectFloats(aFrame->PrincipalChildList().FirstChild(), aList, true);
   8325      DoCollectFloats(
   8326          aFrame->GetChildList(FrameChildListID::Overflow).FirstChild(), aList,
   8327          true);
   8328    }
   8329    if (!aCollectSiblings) {
   8330      break;
   8331    }
   8332    aFrame = aFrame->GetNextSibling();
   8333  }
   8334 }
   8335 
   8336 void nsBlockFrame::CheckFloats(BlockReflowState& aState) {
   8337 #ifdef DEBUG
   8338  // If any line is still dirty, that must mean we're going to reflow this
   8339  // block again soon (e.g. because we bailed out after noticing that
   8340  // clearance was imposed), so don't worry if the floats are out of sync.
   8341  bool anyLineDirty = false;
   8342 
   8343  // Check that the float list is what we would have built
   8344  AutoTArray<nsIFrame*, 8> lineFloats;
   8345  for (auto& line : Lines()) {
   8346    if (line.HasFloats()) {
   8347      lineFloats.AppendElements(line.Floats());
   8348    }
   8349    if (line.IsDirty()) {
   8350      anyLineDirty = true;
   8351    }
   8352  }
   8353 
   8354  AutoTArray<nsIFrame*, 8> storedFloats;
   8355  bool equal = true;
   8356  bool hasHiddenFloats = false;
   8357  uint32_t i = 0;
   8358  for (nsIFrame* f : GetChildList(FrameChildListID::Float)) {
   8359    if (f->HasAnyStateBits(NS_FRAME_IS_PUSHED_OUT_OF_FLOW)) {
   8360      continue;
   8361    }
   8362    // There are chances that the float children won't be added to lines,
   8363    // because in nsBlockFrame::ReflowLine, it skips reflow line if the first
   8364    // child of the line is IsHiddenByContentVisibilityOfInFlowParentForLayout.
   8365    // There are also chances that the floats in line are out of date, for
   8366    // instance, lines could reflow if
   8367    // PresShell::IsForcingLayoutForHiddenContent, and after forcingLayout is
   8368    // off, the reflow of lines could be skipped, but the floats are still in
   8369    // there. Here we can't know whether the floats hidden by c-v are included
   8370    // in the lines or not. So we use hasHiddenFloats to skip the float length
   8371    // checking.
   8372    if (!hasHiddenFloats &&
   8373        f->IsHiddenByContentVisibilityOfInFlowParentForLayout()) {
   8374      hasHiddenFloats = true;
   8375    }
   8376    storedFloats.AppendElement(f);
   8377    if (i < lineFloats.Length() && lineFloats.ElementAt(i) != f) {
   8378      equal = false;
   8379    }
   8380    ++i;
   8381  }
   8382 
   8383  if ((!equal || lineFloats.Length() != storedFloats.Length()) &&
   8384      !anyLineDirty && !hasHiddenFloats) {
   8385    NS_ERROR(
   8386        "nsBlockFrame::CheckFloats: Explicit float list is out of sync with "
   8387        "float cache");
   8388  }
   8389 #endif
   8390 
   8391  const nsFrameList* oofs = GetOverflowOutOfFlows();
   8392  if (oofs && oofs->NotEmpty()) {
   8393    // Floats that were pushed should be removed from our float
   8394    // manager.  Otherwise the float manager's YMost or XMost might
   8395    // be larger than necessary, causing this block to get an
   8396    // incorrect desired height (or width).  Some of these floats
   8397    // may not actually have been added to the float manager because
   8398    // they weren't reflowed before being pushed; that's OK,
   8399    // RemoveRegions will ignore them. It is safe to do this here
   8400    // because we know from here on the float manager will only be
   8401    // used for its XMost and YMost, not to place new floats and
   8402    // lines.
   8403    aState.FloatManager()->RemoveTrailingRegions(oofs->FirstChild());
   8404  }
   8405 }
   8406 
   8407 void nsBlockFrame::IsMarginRoot(bool* aBStartMarginRoot,
   8408                                bool* aBEndMarginRoot) {
   8409  nsIFrame* parent = GetParent();
   8410  if (!HasAnyStateBits(NS_BLOCK_BFC)) {
   8411    if (!parent || parent->IsFloatContainingBlock()) {
   8412      *aBStartMarginRoot = false;
   8413      *aBEndMarginRoot = false;
   8414      return;
   8415    }
   8416  }
   8417 
   8418  if (parent && parent->IsColumnSetFrame()) {
   8419    // The first column is a start margin root and the last column is an end
   8420    // margin root.  (If the column-set is split by a column-span:all box then
   8421    // the first and last column in each column-set fragment are margin roots.)
   8422    *aBStartMarginRoot = GetPrevInFlow() == nullptr;
   8423    *aBEndMarginRoot = GetNextInFlow() == nullptr;
   8424    return;
   8425  }
   8426 
   8427  *aBStartMarginRoot = true;
   8428  *aBEndMarginRoot = true;
   8429 }
   8430 
   8431 /* static */
   8432 bool nsBlockFrame::BlockNeedsFloatManager(nsIFrame* aBlock) {
   8433  MOZ_ASSERT(aBlock, "Must have a frame");
   8434  NS_ASSERTION(aBlock->IsBlockFrameOrSubclass(), "aBlock must be a block");
   8435 
   8436  nsIFrame* parent = aBlock->GetParent();
   8437  return aBlock->HasAnyStateBits(NS_BLOCK_BFC) ||
   8438         (parent && !parent->IsFloatContainingBlock());
   8439 }
   8440 
   8441 /* static */
   8442 bool nsBlockFrame::BlockCanIntersectFloats(nsIFrame* aFrame) {
   8443  // NS_BLOCK_BFC is block specific bit, check first as an optimization, it's
   8444  // okay because we also check that it is a block frame.
   8445  return !aFrame->HasAnyStateBits(NS_BLOCK_BFC) && !aFrame->IsReplaced() &&
   8446         aFrame->IsBlockFrameOrSubclass();
   8447 }
   8448 
   8449 // Note that this width can vary based on the vertical position.
   8450 // However, the cases where it varies are the cases where the width fits
   8451 // in the available space given, which means that variation shouldn't
   8452 // matter.
   8453 /* static */
   8454 nsBlockFrame::FloatAvoidingISizeToClear nsBlockFrame::ISizeToClearPastFloats(
   8455    const BlockReflowState& aState, const LogicalRect& aFloatAvailableSpace,
   8456    nsIFrame* aFloatAvoidingBlock) {
   8457  nscoord inlineStartOffset, inlineEndOffset;
   8458  WritingMode wm = aState.mReflowInput.GetWritingMode();
   8459 
   8460  FloatAvoidingISizeToClear result;
   8461  aState.ComputeFloatAvoidingOffsets(aFloatAvoidingBlock, aFloatAvailableSpace,
   8462                                     inlineStartOffset, inlineEndOffset);
   8463  nscoord availISize =
   8464      aState.mContentArea.ISize(wm) - inlineStartOffset - inlineEndOffset;
   8465 
   8466  // We actually don't want the min width here; see bug 427782; we only
   8467  // want to displace if the width won't compute to a value small enough
   8468  // to fit.
   8469  // All we really need here is the result of ComputeSize, and we
   8470  // could *almost* get that from an SizeComputationInput, except for the
   8471  // last argument.
   8472  WritingMode frWM = aFloatAvoidingBlock->GetWritingMode();
   8473  LogicalSize availSpace =
   8474      LogicalSize(wm, availISize, NS_UNCONSTRAINEDSIZE).ConvertTo(frWM, wm);
   8475  ReflowInput reflowInput(aState.mPresContext, aState.mReflowInput,
   8476                          aFloatAvoidingBlock, availSpace);
   8477  result.borderBoxISize =
   8478      reflowInput.ComputedSizeWithBorderPadding(wm).ISize(wm);
   8479 
   8480  // Use the margins from sizingInput rather than reflowInput so that
   8481  // they aren't reduced by ignoring margins in overconstrained cases.
   8482  SizeComputationInput sizingInput(aFloatAvoidingBlock,
   8483                                   aState.mReflowInput.mRenderingContext, wm,
   8484                                   aState.mContentArea.ISize(wm));
   8485  const LogicalMargin computedMargin = sizingInput.ComputedLogicalMargin(wm);
   8486 
   8487  nscoord marginISize = computedMargin.IStartEnd(wm);
   8488  const auto iSize = reflowInput.mStylePosition->ISize(
   8489      wm, AnchorPosResolutionParams::From(&reflowInput));
   8490  if (marginISize < 0 &&
   8491      (iSize->IsAuto() || iSize->BehavesLikeStretchOnInlineAxis())) {
   8492    // If we get here, floatAvoidingBlock has a negative amount of inline-axis
   8493    // margin and an 'auto' (or ~equivalently, -moz-available) inline
   8494    // size. Under these circumstances, we use the margin to establish a
   8495    // (positive) minimum size for the border-box, in order to satisfy the
   8496    // equation in CSS2 10.3.3.  That equation essentially simplifies to the
   8497    // following:
   8498    //
   8499    //   iSize of margins + iSize of borderBox = iSize of containingBlock
   8500    //
   8501    // ...where "iSize of borderBox" is the sum of floatAvoidingBlock's
   8502    // inline-axis components of border, padding, and {width,height}.
   8503    //
   8504    // Right now, in the above equation, "iSize of margins" is the only term
   8505    // that we know for sure. (And we also know that it's negative, since we
   8506    // got here.) The other terms are as-yet unresolved, since the frame has an
   8507    // 'auto' iSize, and since we aren't yet sure if we'll clear this frame
   8508    // beyond floats or place it alongside them.
   8509    //
   8510    // However: we *do* know that the equation's "iSize of containingBlock"
   8511    // term *must* be non-negative, since boxes' widths and heights generally
   8512    // can't be negative in CSS.  To satisfy that requirement, we can then
   8513    // infer that the equation's "iSize of borderBox" term *must* be large
   8514    // enough to cancel out the (known-to-be-negative) "iSize of margins"
   8515    // term. Therefore, marginISize value (negated to make it positive)
   8516    // establishes a lower-bound for how much inline-axis space our border-box
   8517    // will really require in order to fit alongside any floats.
   8518    //
   8519    // XXXdholbert This explanation is admittedly a bit hand-wavy and may not
   8520    // precisely match what any particular spec requires. It's the best
   8521    // reasoning I could come up with to explain engines' behavior.  Also, our
   8522    // behavior with -moz-available doesn't seem particularly correct here, per
   8523    // bug 1767217, though that's probably due to a bug elsewhere in our float
   8524    // handling code...
   8525    result.borderBoxISize = std::max(result.borderBoxISize, -marginISize);
   8526  }
   8527 
   8528  result.marginIStart = computedMargin.IStart(wm);
   8529  return result;
   8530 }
   8531 
   8532 /* static */
   8533 nsBlockFrame* nsBlockFrame::GetNearestAncestorBlock(nsIFrame* aCandidate) {
   8534  nsBlockFrame* block = nullptr;
   8535  while (aCandidate) {
   8536    block = do_QueryFrame(aCandidate);
   8537    if (block) {
   8538      // yay, candidate is a block!
   8539      return block;
   8540    }
   8541    // Not a block. Check its parent next.
   8542    aCandidate = aCandidate->GetParent();
   8543  }
   8544  MOZ_ASSERT_UNREACHABLE("Fell off frame tree looking for ancestor block!");
   8545  return nullptr;
   8546 }
   8547 
   8548 nscoord nsBlockFrame::ComputeFinalBSize(BlockReflowState& aState,
   8549                                        nscoord aBEndEdgeOfChildren) {
   8550  const WritingMode wm = aState.mReflowInput.GetWritingMode();
   8551 
   8552  const nscoord effectiveContentBoxBSize =
   8553      GetEffectiveComputedBSize(aState.mReflowInput, aState.mConsumedBSize);
   8554  const nscoord blockStartBP = aState.BorderPadding().BStart(wm);
   8555  const nscoord blockEndBP = aState.BorderPadding().BEnd(wm);
   8556 
   8557  NS_ASSERTION(
   8558      !IsTrueOverflowContainer() || (effectiveContentBoxBSize == 0 &&
   8559                                     blockStartBP == 0 && blockEndBP == 0),
   8560      "An overflow container's effective content-box block-size, block-start "
   8561      "BP, and block-end BP should all be zero!");
   8562 
   8563  const nscoord effectiveContentBoxBSizeWithBStartBP =
   8564      NSCoordSaturatingAdd(blockStartBP, effectiveContentBoxBSize);
   8565  const nscoord effectiveBorderBoxBSize =
   8566      NSCoordSaturatingAdd(effectiveContentBoxBSizeWithBStartBP, blockEndBP);
   8567 
   8568  if (HasColumnSpanSiblings()) {
   8569    MOZ_ASSERT(LastInFlow()->GetNextContinuation(),
   8570               "Frame constructor should've created column-span siblings!");
   8571 
   8572    // If a block is split by any column-spans, we calculate the final
   8573    // block-size by shrinkwrapping our children's block-size for all the
   8574    // fragments except for those after the final column-span, but we should
   8575    // take no more than our effective border-box block-size. If there's any
   8576    // leftover block-size, our next continuations will take up rest.
   8577    //
   8578    // We don't need to adjust aBri.mReflowStatus because our children's status
   8579    // is the same as ours.
   8580    return std::min(effectiveBorderBoxBSize, aBEndEdgeOfChildren);
   8581  }
   8582 
   8583  const nscoord availBSize = aState.mReflowInput.AvailableBSize();
   8584  if (availBSize == NS_UNCONSTRAINEDSIZE) {
   8585    return effectiveBorderBoxBSize;
   8586  }
   8587 
   8588  // Save our children's reflow status.
   8589  const bool isChildStatusComplete = aState.mReflowStatus.IsComplete();
   8590  if (isChildStatusComplete && effectiveContentBoxBSize > 0 &&
   8591      effectiveBorderBoxBSize > availBSize &&
   8592      ShouldAvoidBreakInside(aState.mReflowInput)) {
   8593    aState.mReflowStatus.SetInlineLineBreakBeforeAndReset();
   8594    return effectiveBorderBoxBSize;
   8595  }
   8596 
   8597  const bool isBDBClone =
   8598      aState.mReflowInput.mStyleBorder->mBoxDecorationBreak ==
   8599      StyleBoxDecorationBreak::Clone;
   8600 
   8601  // The maximum value our content-box block-size can take within the given
   8602  // available block-size.
   8603  const nscoord maxContentBoxBSize = aState.ContentBSize();
   8604 
   8605  // The block-end edge of our content-box (relative to this frame's origin) if
   8606  // we consumed the maximum block-size available to us (maxContentBoxBSize).
   8607  const nscoord maxContentBoxBEnd = aState.ContentBEnd();
   8608 
   8609  // These variables are uninitialized intentionally so that the compiler can
   8610  // check they are assigned in every if-else branch below.
   8611  nscoord finalContentBoxBSizeWithBStartBP;
   8612  bool isOurStatusComplete;
   8613 
   8614  if (effectiveBorderBoxBSize <= availBSize) {
   8615    // Our effective border-box block-size can fit in the available block-size,
   8616    // so we are complete.
   8617    finalContentBoxBSizeWithBStartBP = effectiveContentBoxBSizeWithBStartBP;
   8618    isOurStatusComplete = true;
   8619  } else if (effectiveContentBoxBSizeWithBStartBP <= maxContentBoxBEnd) {
   8620    // Note: The following assertion should generally hold because, for
   8621    // box-decoration-break:clone, this "else if" branch is mathematically
   8622    // equivalent to the initial "if".
   8623    NS_ASSERTION(!isBDBClone,
   8624                 "This else-if branch is handling a situation that's specific "
   8625                 "to box-decoration-break:slice, i.e. a case when we can skip "
   8626                 "our block-end border and padding!");
   8627 
   8628    // Our effective content-box block-size plus the block-start border and
   8629    // padding can fit in the available block-size, but it cannot fit after
   8630    // adding the block-end border and padding. Thus, we need a continuation
   8631    // (unless we already weren't asking for any block-size, in which case we
   8632    // stay complete to avoid looping forever).
   8633    finalContentBoxBSizeWithBStartBP = effectiveContentBoxBSizeWithBStartBP;
   8634    isOurStatusComplete = effectiveContentBoxBSize == 0;
   8635  } else {
   8636    // We aren't going to be able to fit our content-box in the space available
   8637    // to it, which means we'll probably call ourselves incomplete to request a
   8638    // continuation. But before making that decision, we check for certain
   8639    // conditions which would force us to overflow beyond the available space --
   8640    // these might result in us actually being complete if we're forced to
   8641    // overflow far enough.
   8642    if (MOZ_UNLIKELY(aState.mReflowInput.mFlags.mIsTopOfPage && isBDBClone &&
   8643                     maxContentBoxBSize <= 0 &&
   8644                     aBEndEdgeOfChildren == blockStartBP)) {
   8645      // In this rare case, we are at the top of page/column, we have
   8646      // box-decoration-break:clone and zero available block-size for our
   8647      // content-box (e.g. our own block-start border and padding already exceed
   8648      // the available block-size), and we didn't lay out any child to consume
   8649      // our content-box block-size. To ensure we make progress (avoid looping
   8650      // forever), use 1px as our content-box block-size regardless of our
   8651      // effective content-box block-size, in the spirit of
   8652      // https://drafts.csswg.org/css-break/#breaking-rules.
   8653      finalContentBoxBSizeWithBStartBP = blockStartBP + AppUnitsPerCSSPixel();
   8654      isOurStatusComplete = effectiveContentBoxBSize <= AppUnitsPerCSSPixel();
   8655    } else if (aBEndEdgeOfChildren > maxContentBoxBEnd) {
   8656      // We have a unbreakable child whose block-end edge exceeds the available
   8657      // block-size for children.
   8658      if (aBEndEdgeOfChildren >= effectiveContentBoxBSizeWithBStartBP) {
   8659        // The unbreakable child's block-end edge forces us to consume all of
   8660        // our effective content-box block-size.
   8661        finalContentBoxBSizeWithBStartBP = effectiveContentBoxBSizeWithBStartBP;
   8662 
   8663        // Even though we've consumed all of our effective content-box
   8664        // block-size, we may still need to report an incomplete status in order
   8665        // to get another continuation, which will be responsible for laying out
   8666        // & drawing our block-end border & padding. But if we have no such
   8667        // border & padding, or if we're forced to apply that border & padding
   8668        // on this frame due to box-decoration-break:clone, then we don't need
   8669        // to bother with that additional continuation.
   8670        isOurStatusComplete = (isBDBClone || blockEndBP == 0);
   8671      } else {
   8672        // The unbreakable child's block-end edge doesn't force us to consume
   8673        // all of our effective content-box block-size.
   8674        finalContentBoxBSizeWithBStartBP = aBEndEdgeOfChildren;
   8675        isOurStatusComplete = false;
   8676      }
   8677    } else {
   8678      // The children's block-end edge can fit in the content-box space that we
   8679      // have available for it. Consume all the space that is available so that
   8680      // our inline-start/inline-end borders extend all the way to the block-end
   8681      // edge of column/page.
   8682      finalContentBoxBSizeWithBStartBP = maxContentBoxBEnd;
   8683      isOurStatusComplete = false;
   8684    }
   8685  }
   8686 
   8687  nscoord finalBorderBoxBSize = finalContentBoxBSizeWithBStartBP;
   8688  if (isOurStatusComplete) {
   8689    finalBorderBoxBSize = NSCoordSaturatingAdd(finalBorderBoxBSize, blockEndBP);
   8690    if (isChildStatusComplete) {
   8691      // We want to use children's reflow status as ours, which can be overflow
   8692      // incomplete. Suppress the urge to call aBri.mReflowStatus.Reset() here.
   8693    } else {
   8694      aState.mReflowStatus.SetOverflowIncomplete();
   8695    }
   8696  } else {
   8697    NS_ASSERTION(!IsTrueOverflowContainer(),
   8698                 "An overflow container should always be complete because of "
   8699                 "its zero border-box block-size!");
   8700    if (isBDBClone) {
   8701      finalBorderBoxBSize =
   8702          NSCoordSaturatingAdd(finalBorderBoxBSize, blockEndBP);
   8703    }
   8704    aState.mReflowStatus.SetIncomplete();
   8705    if (!GetNextInFlow()) {
   8706      aState.mReflowStatus.SetNextInFlowNeedsReflow();
   8707    }
   8708  }
   8709 
   8710  return finalBorderBoxBSize;
   8711 }
   8712 
   8713 nsresult nsBlockFrame::ResolveBidi() {
   8714  NS_ASSERTION(!GetPrevInFlow(),
   8715               "ResolveBidi called on non-first continuation");
   8716  MOZ_ASSERT(PresContext()->BidiEnabled());
   8717  return nsBidiPresUtils::Resolve(this);
   8718 }
   8719 
   8720 void nsBlockFrame::UpdatePseudoElementStyles(ServoRestyleState& aRestyleState) {
   8721  // first-letter needs to be updated before first-line, because first-line can
   8722  // change the style of the first-letter.
   8723  if (HasFirstLetterChild()) {
   8724    UpdateFirstLetterStyle(aRestyleState);
   8725  }
   8726 
   8727  if (nsIFrame* firstLineFrame = GetFirstLineFrame()) {
   8728    nsIFrame* styleParent = CorrectStyleParentFrame(firstLineFrame->GetParent(),
   8729                                                    PseudoStyleType::firstLine);
   8730 
   8731    ComputedStyle* parentStyle = styleParent->Style();
   8732    RefPtr<ComputedStyle> firstLineStyle =
   8733        aRestyleState.StyleSet().ResolvePseudoElementStyle(
   8734            *mContent->AsElement(), PseudoStyleType::firstLine, nullptr,
   8735            parentStyle);
   8736 
   8737    // FIXME(bz): Can we make first-line continuations be non-inheriting anon
   8738    // boxes?
   8739    RefPtr<ComputedStyle> continuationStyle =
   8740        aRestyleState.StyleSet().ResolveInheritingAnonymousBoxStyle(
   8741            PseudoStyleType::mozLineFrame, parentStyle);
   8742 
   8743    UpdateStyleOfOwnedChildFrame(firstLineFrame, firstLineStyle, aRestyleState,
   8744                                 Some(continuationStyle.get()));
   8745 
   8746    // We also want to update the styles of the first-line's descendants.  We
   8747    // don't need to compute a changehint for this, though, since any changes to
   8748    // them are handled by the first-line anyway.
   8749    RestyleManager* manager = PresContext()->RestyleManager();
   8750    for (nsIFrame* kid : firstLineFrame->PrincipalChildList()) {
   8751      manager->ReparentComputedStyleForFirstLine(kid);
   8752    }
   8753  }
   8754 }
   8755 
   8756 nsIFrame* nsBlockFrame::GetFirstLetter() const {
   8757  if (!HasAnyStateBits(NS_BLOCK_HAS_FIRST_LETTER_STYLE)) {
   8758    // Certainly no first-letter frame.
   8759    return nullptr;
   8760  }
   8761 
   8762  return GetProperty(FirstLetterProperty());
   8763 }
   8764 
   8765 nsIFrame* nsBlockFrame::GetFirstLineFrame() const {
   8766  nsIFrame* maybeFirstLine = PrincipalChildList().FirstChild();
   8767  if (maybeFirstLine && maybeFirstLine->IsLineFrame()) {
   8768    return maybeFirstLine;
   8769  }
   8770 
   8771  return nullptr;
   8772 }
   8773 
   8774 #ifdef DEBUG
   8775 void nsBlockFrame::VerifyLines(bool aFinalCheckOK) {
   8776  if (!gVerifyLines) {
   8777    return;
   8778  }
   8779  if (mLines.empty()) {
   8780    return;
   8781  }
   8782 
   8783  nsLineBox* cursor = GetLineCursorForQuery();
   8784 
   8785  // Add up the counts on each line. Also validate that IsFirstLine is
   8786  // set properly.
   8787  int32_t count = 0;
   8788  for (const auto& line : Lines()) {
   8789    if (&line == cursor) {
   8790      cursor = nullptr;
   8791    }
   8792    if (aFinalCheckOK) {
   8793      MOZ_ASSERT(line.GetChildCount(), "empty line");
   8794      if (line.IsBlock()) {
   8795        NS_ASSERTION(1 == line.GetChildCount(), "bad first line");
   8796      }
   8797    }
   8798    count += line.GetChildCount();
   8799  }
   8800 
   8801  // Then count the frames
   8802  int32_t frameCount = 0;
   8803  nsIFrame* frame = mLines.front()->mFirstChild;
   8804  while (frame) {
   8805    frameCount++;
   8806    frame = frame->GetNextSibling();
   8807  }
   8808  NS_ASSERTION(count == frameCount, "bad line list");
   8809 
   8810  // Next: test that each line has right number of frames on it
   8811  for (LineIterator line = LinesBegin(), line_end = LinesEnd();
   8812       line != line_end;) {
   8813    count = line->GetChildCount();
   8814    frame = line->mFirstChild;
   8815    while (--count >= 0) {
   8816      frame = frame->GetNextSibling();
   8817    }
   8818    ++line;
   8819    if ((line != line_end) && (0 != line->GetChildCount())) {
   8820      NS_ASSERTION(frame == line->mFirstChild, "bad line list");
   8821    }
   8822  }
   8823 
   8824  if (cursor) {
   8825    FrameLines* overflowLines = GetOverflowLines();
   8826    if (overflowLines) {
   8827      LineIterator line = overflowLines->mLines.begin();
   8828      LineIterator line_end = overflowLines->mLines.end();
   8829      for (; line != line_end; ++line) {
   8830        if (line == cursor) {
   8831          cursor = nullptr;
   8832          break;
   8833        }
   8834      }
   8835    }
   8836  }
   8837  NS_ASSERTION(!cursor, "stale LineCursorProperty");
   8838 }
   8839 
   8840 void nsBlockFrame::VerifyOverflowSituation() {
   8841  // Overflow out-of-flows must not have a next-in-flow in floats list or
   8842  // mFrames.
   8843  nsFrameList* oofs = GetOverflowOutOfFlows();
   8844  if (oofs) {
   8845    for (nsIFrame* f : *oofs) {
   8846      nsIFrame* nif = f->GetNextInFlow();
   8847      MOZ_ASSERT(!nif ||
   8848                 (!GetChildList(FrameChildListID::Float).ContainsFrame(nif) &&
   8849                  !mFrames.ContainsFrame(nif)));
   8850    }
   8851  }
   8852 
   8853  // Pushed floats must not have a next-in-flow in floats list or mFrames.
   8854  oofs = GetPushedFloats();
   8855  if (oofs) {
   8856    for (nsIFrame* f : *oofs) {
   8857      nsIFrame* nif = f->GetNextInFlow();
   8858      MOZ_ASSERT(!nif ||
   8859                 (!GetChildList(FrameChildListID::Float).ContainsFrame(nif) &&
   8860                  !mFrames.ContainsFrame(nif)));
   8861    }
   8862  }
   8863 
   8864  // A child float next-in-flow's parent must be |this| or a next-in-flow of
   8865  // |this|. Later next-in-flows must have the same or later parents.
   8866  ChildListID childLists[] = {FrameChildListID::Float,
   8867                              FrameChildListID::PushedFloats};
   8868  for (size_t i = 0; i < std::size(childLists); ++i) {
   8869    const nsFrameList& children = GetChildList(childLists[i]);
   8870    for (nsIFrame* f : children) {
   8871      nsIFrame* parent = this;
   8872      nsIFrame* nif = f->GetNextInFlow();
   8873      for (; nif; nif = nif->GetNextInFlow()) {
   8874        bool found = false;
   8875        for (nsIFrame* p = parent; p; p = p->GetNextInFlow()) {
   8876          if (nif->GetParent() == p) {
   8877            parent = p;
   8878            found = true;
   8879            break;
   8880          }
   8881        }
   8882        MOZ_ASSERT(
   8883            found,
   8884            "next-in-flow is a child of parent earlier in the frame tree?");
   8885      }
   8886    }
   8887  }
   8888 
   8889  nsBlockFrame* flow = static_cast<nsBlockFrame*>(FirstInFlow());
   8890  while (flow) {
   8891    FrameLines* overflowLines = flow->GetOverflowLines();
   8892    if (overflowLines) {
   8893      NS_ASSERTION(!overflowLines->mLines.empty(),
   8894                   "should not be empty if present");
   8895      NS_ASSERTION(overflowLines->mLines.front()->mFirstChild,
   8896                   "bad overflow lines");
   8897      NS_ASSERTION(overflowLines->mLines.front()->mFirstChild ==
   8898                       overflowLines->mFrames.FirstChild(),
   8899                   "bad overflow frames / lines");
   8900    }
   8901    auto checkCursor = [&](nsLineBox* cursor) -> bool {
   8902      if (!cursor) {
   8903        return true;
   8904      }
   8905      LineIterator line = flow->LinesBegin();
   8906      LineIterator line_end = flow->LinesEnd();
   8907      for (; line != line_end && line != cursor; ++line);
   8908      if (line == line_end && overflowLines) {
   8909        line = overflowLines->mLines.begin();
   8910        line_end = overflowLines->mLines.end();
   8911        for (; line != line_end && line != cursor; ++line);
   8912      }
   8913      return line != line_end;
   8914    };
   8915    MOZ_ASSERT(checkCursor(flow->GetLineCursorForDisplay()),
   8916               "stale LineCursorPropertyDisplay");
   8917    MOZ_ASSERT(checkCursor(flow->GetLineCursorForQuery()),
   8918               "stale LineCursorPropertyQuery");
   8919    flow = static_cast<nsBlockFrame*>(flow->GetNextInFlow());
   8920  }
   8921 }
   8922 
   8923 int32_t nsBlockFrame::GetDepth() const {
   8924  int32_t depth = 0;
   8925  nsIFrame* parent = GetParent();
   8926  while (parent) {
   8927    parent = parent->GetParent();
   8928    depth++;
   8929  }
   8930  return depth;
   8931 }
   8932 
   8933 already_AddRefed<ComputedStyle> nsBlockFrame::GetFirstLetterStyle(
   8934    nsPresContext* aPresContext) {
   8935  return aPresContext->StyleSet()->ProbePseudoElementStyle(
   8936      *mContent->AsElement(), PseudoStyleType::firstLetter, nullptr, Style());
   8937 }
   8938 #endif