tor-browser

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

BlockReflowState.cpp (44032B)


      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 /* state used in reflow of block frames */
      8 
      9 #include "BlockReflowState.h"
     10 
     11 #include <algorithm>
     12 
     13 #include "LayoutLogging.h"
     14 #include "TextOverflow.h"
     15 #include "fmt/format.h"
     16 #include "mozilla/AutoRestore.h"
     17 #include "mozilla/Preferences.h"
     18 #include "mozilla/StaticPrefs_layout.h"
     19 #include "nsBlockFrame.h"
     20 #include "nsIFrameInlines.h"
     21 #include "nsLineLayout.h"
     22 #include "nsPresContext.h"
     23 
     24 #ifdef DEBUG
     25 #  include "nsBlockDebugFlags.h"
     26 #endif
     27 
     28 using namespace mozilla;
     29 using namespace mozilla::layout;
     30 
     31 BlockReflowState::BlockReflowState(
     32    const ReflowInput& aReflowInput, nsPresContext* aPresContext,
     33    nsBlockFrame* aFrame, bool aBStartMarginRoot, bool aBEndMarginRoot,
     34    bool aBlockNeedsFloatManager, const nscoord aConsumedBSize,
     35    const nscoord aEffectiveContentBoxBSize, const nscoord aInset)
     36    : mBlock(aFrame),
     37      mPresContext(aPresContext),
     38      mReflowInput(aReflowInput),
     39      mContentArea(aReflowInput.GetWritingMode()),
     40      mInsetForBalance(aInset),
     41      mContainerSize(aReflowInput.ComputedSizeAsContainerIfConstrained()),
     42      mOverflowTracker(nullptr),
     43      mBorderPadding(
     44          mReflowInput
     45              .ComputedLogicalBorderPadding(mReflowInput.GetWritingMode())
     46              .ApplySkipSides(aFrame->PreReflowBlockLevelLogicalSkipSides())),
     47      mMinLineHeight(aReflowInput.GetLineHeight()),
     48      mLineNumber(0),
     49      mTrailingClearFromPIF(UsedClear::None),
     50      mConsumedBSize(aConsumedBSize),
     51      mAlignContentShift(mBlock->GetAlignContentShift()) {
     52  NS_ASSERTION(mConsumedBSize != NS_UNCONSTRAINEDSIZE,
     53               "The consumed block-size should be constrained!");
     54 
     55  WritingMode wm = aReflowInput.GetWritingMode();
     56 
     57  if (aBStartMarginRoot || 0 != mBorderPadding.BStart(wm)) {
     58    mFlags.mIsBStartMarginRoot = true;
     59    mFlags.mShouldApplyBStartMargin = true;
     60  }
     61  if (aBEndMarginRoot || 0 != mBorderPadding.BEnd(wm)) {
     62    mFlags.mIsBEndMarginRoot = true;
     63  }
     64  if (aBlockNeedsFloatManager) {
     65    mFlags.mBlockNeedsFloatManager = true;
     66  }
     67 
     68  mFlags.mCanHaveOverflowMarkers = css::TextOverflow::CanHaveOverflowMarkers(
     69      mBlock, css::TextOverflow::BeforeReflow::Yes);
     70 
     71  MOZ_ASSERT(FloatManager(),
     72             "Float manager should be valid when creating BlockReflowState!");
     73 
     74  // Save the coordinate system origin for later.
     75  FloatManager()->GetTranslation(mFloatManagerI, mFloatManagerB);
     76  FloatManager()->PushState(&mFloatManagerStateBefore);  // never popped
     77 
     78  mNextInFlow = static_cast<nsBlockFrame*>(mBlock->GetNextInFlow());
     79 
     80  LAYOUT_WARN_IF_FALSE(NS_UNCONSTRAINEDSIZE != aReflowInput.ComputedISize(),
     81                       "have unconstrained width; this should only result "
     82                       "from very large sizes, not attempts at intrinsic "
     83                       "width calculation");
     84  mContentArea.ISize(wm) = aReflowInput.ComputedISize();
     85 
     86  // Compute content area block-size. Unlike the inline-size, if we have a
     87  // specified style block-size, we ignore it since extra content is managed by
     88  // the "overflow" property. When we don't have a specified style block-size,
     89  // then we may end up limiting our block-size if the available block-size is
     90  // constrained (this situation occurs when we are paginated).
     91  const nscoord availableBSize = aReflowInput.AvailableBSize();
     92  if (availableBSize != NS_UNCONSTRAINEDSIZE) {
     93    // We are in a paginated situation. The block-end edge of the available
     94    // space to reflow the children is within our block-end border and padding.
     95    // If we're cloning our border and padding, and we're going to request
     96    // additional continuations because of our excessive content-box block-size,
     97    // then reserve some of our available space for our (cloned) block-end
     98    // border and padding.
     99    const bool reserveSpaceForBlockEndBP =
    100        mReflowInput.mStyleBorder->mBoxDecorationBreak ==
    101            StyleBoxDecorationBreak::Clone &&
    102        (aEffectiveContentBoxBSize == NS_UNCONSTRAINEDSIZE ||
    103         aEffectiveContentBoxBSize + mBorderPadding.BStartEnd(wm) >
    104             availableBSize);
    105    const nscoord bp = reserveSpaceForBlockEndBP ? mBorderPadding.BStartEnd(wm)
    106                                                 : mBorderPadding.BStart(wm);
    107    mContentArea.BSize(wm) = std::max(0, availableBSize - bp);
    108  } else {
    109    // When we are not in a paginated situation, then we always use a
    110    // unconstrained block-size.
    111    mContentArea.BSize(wm) = NS_UNCONSTRAINEDSIZE;
    112  }
    113  mContentArea.IStart(wm) = mBorderPadding.IStart(wm);
    114  mBCoord = mContentArea.BStart(wm) = mBorderPadding.BStart(wm);
    115 
    116  // Account for existing cached shift, we'll re-position in AlignContent() if
    117  // needed.
    118  if (mAlignContentShift) {
    119    mBCoord += mAlignContentShift;
    120    mContentArea.BStart(wm) += mAlignContentShift;
    121 
    122    if (availableBSize != NS_UNCONSTRAINEDSIZE) {
    123      mContentArea.BSize(wm) += mAlignContentShift;
    124    }
    125  }
    126 
    127  mPrevChild = nullptr;
    128  mCurrentLine = aFrame->LinesEnd();
    129 }
    130 
    131 void BlockReflowState::UndoAlignContentShift() {
    132  if (!mAlignContentShift) {
    133    return;
    134  }
    135 
    136  mBCoord -= mAlignContentShift;
    137  mContentArea.BStart(mReflowInput.GetWritingMode()) -= mAlignContentShift;
    138 
    139  if (mReflowInput.AvailableBSize() != NS_UNCONSTRAINEDSIZE) {
    140    mContentArea.BSize(mReflowInput.GetWritingMode()) -= mAlignContentShift;
    141  }
    142 }
    143 
    144 void BlockReflowState::ComputeFloatAvoidingOffsets(
    145    nsIFrame* aFloatAvoidingBlock, const LogicalRect& aFloatAvailableSpace,
    146    nscoord& aIStartResult, nscoord& aIEndResult) const {
    147  WritingMode wm = mReflowInput.GetWritingMode();
    148  // The frame is clueless about the float manager and therefore we
    149  // only give it free space. An example is a table frame - the
    150  // tables do not flow around floats.
    151  // However, we can let its margins intersect floats.
    152  NS_ASSERTION(aFloatAvailableSpace.IStart(wm) >= mContentArea.IStart(wm),
    153               "bad avail space rect inline-coord");
    154  NS_ASSERTION(aFloatAvailableSpace.ISize(wm) == 0 ||
    155                   aFloatAvailableSpace.IEnd(wm) <= mContentArea.IEnd(wm),
    156               "bad avail space rect inline-size");
    157 
    158  nscoord iStartOffset, iEndOffset;
    159  if (aFloatAvailableSpace.ISize(wm) == mContentArea.ISize(wm)) {
    160    // We don't need to compute margins when there are no floats around.
    161    iStartOffset = 0;
    162    iEndOffset = 0;
    163  } else {
    164    const LogicalMargin frameMargin =
    165        SizeComputationInput(aFloatAvoidingBlock,
    166                             mReflowInput.mRenderingContext, wm,
    167                             mContentArea.ISize(wm))
    168            .ComputedLogicalMargin(wm);
    169 
    170    nscoord iStartFloatIOffset =
    171        aFloatAvailableSpace.IStart(wm) - mContentArea.IStart(wm);
    172    iStartOffset = std::max(iStartFloatIOffset, frameMargin.IStart(wm)) -
    173                   frameMargin.IStart(wm);
    174    iStartOffset = std::max(iStartOffset, 0);  // in case of negative margin
    175    nscoord iEndFloatIOffset =
    176        mContentArea.IEnd(wm) - aFloatAvailableSpace.IEnd(wm);
    177    iEndOffset =
    178        std::max(iEndFloatIOffset, frameMargin.IEnd(wm)) - frameMargin.IEnd(wm);
    179    iEndOffset = std::max(iEndOffset, 0);  // in case of negative margin
    180  }
    181  aIStartResult = iStartOffset;
    182  aIEndResult = iEndOffset;
    183 }
    184 
    185 LogicalRect BlockReflowState::ComputeBlockAvailSpace(
    186    nsIFrame* aFrame, const nsFlowAreaRect& aFloatAvailableSpace,
    187    bool aBlockAvoidsFloats) {
    188 #ifdef REALLY_NOISY_REFLOW
    189  printf("CBAS frame=%p has floats %d\n", aFrame,
    190         aFloatAvailableSpace.HasFloats());
    191 #endif
    192  WritingMode wm = mReflowInput.GetWritingMode();
    193  LogicalRect result(wm);
    194  result.BStart(wm) = mBCoord;
    195  // Note: ContentBSize() and ContentBEnd() are not our content-box size and its
    196  // block-end edge. They really mean "the available block-size for children",
    197  // and "the block-end edge of the available space for children".
    198  result.BSize(wm) = ContentBSize() == NS_UNCONSTRAINEDSIZE
    199                         ? NS_UNCONSTRAINEDSIZE
    200                         : ContentBEnd() - mBCoord;
    201  // mBCoord might be greater than ContentBEnd() if the block's top margin
    202  // pushes it off the page/column. Negative available block-size can confuse
    203  // other code and is nonsense in principle.
    204 
    205  // XXX Do we really want this condition to be this restrictive (i.e.,
    206  // more restrictive than it used to be)?  The |else| here is allowed
    207  // by the CSS spec, but only out of desperation given implementations,
    208  // and the behavior it leads to is quite undesirable (it can cause
    209  // things to become extremely narrow when they'd fit quite well a
    210  // little bit lower).  Should the else be a quirk or something that
    211  // applies to a specific set of frame classes and no new ones?
    212  // If we did that, then for those frames where the condition below is
    213  // true but nsBlockFrame::BlockCanIntersectFloats is false,
    214  // nsBlockFrame::ISizeToClearPastFloats would need to use the
    215  // shrink-wrap formula, max(MinISize, min(avail width, PrefISize))
    216  // rather than just using MinISize.
    217  NS_ASSERTION(
    218      nsBlockFrame::BlockCanIntersectFloats(aFrame) == !aBlockAvoidsFloats,
    219      "unexpected replaced width");
    220  if (!aBlockAvoidsFloats) {
    221    if (aFloatAvailableSpace.HasFloats()) {
    222      // Use the float-edge property to determine how the child block
    223      // will interact with the float.
    224      const nsStyleBorder* borderStyle = aFrame->StyleBorder();
    225      switch (borderStyle->mFloatEdge) {
    226        default:
    227        case StyleFloatEdge::ContentBox:  // content and only content does
    228                                          // runaround of floats
    229          // The child block will flow around the float. Therefore
    230          // give it all of the available space.
    231          result.IStart(wm) = mContentArea.IStart(wm);
    232          result.ISize(wm) = mContentArea.ISize(wm);
    233          break;
    234        case StyleFloatEdge::MarginBox: {
    235          // The child block's margins should be placed adjacent to,
    236          // but not overlap the float.
    237          result.IStart(wm) = aFloatAvailableSpace.mRect.IStart(wm);
    238          result.ISize(wm) = aFloatAvailableSpace.mRect.ISize(wm);
    239        } break;
    240      }
    241    } else {
    242      // Since there are no floats present the float-edge property
    243      // doesn't matter therefore give the block element all of the
    244      // available space since it will flow around the float itself.
    245      result.IStart(wm) = mContentArea.IStart(wm);
    246      result.ISize(wm) = mContentArea.ISize(wm);
    247    }
    248  } else {
    249    nscoord iStartOffset, iEndOffset;
    250    ComputeFloatAvoidingOffsets(aFrame, aFloatAvailableSpace.mRect,
    251                                iStartOffset, iEndOffset);
    252    result.IStart(wm) = mContentArea.IStart(wm) + iStartOffset;
    253    result.ISize(wm) = mContentArea.ISize(wm) - iStartOffset - iEndOffset;
    254  }
    255 
    256 #ifdef REALLY_NOISY_REFLOW
    257  printf("  CBAS: result %d %d %d %d\n", result.IStart(wm), result.BStart(wm),
    258         result.ISize(wm), result.BSize(wm));
    259 #endif
    260 
    261  return result;
    262 }
    263 
    264 LogicalSize BlockReflowState::ComputeAvailableSizeForFloat() const {
    265  const auto wm = mReflowInput.GetWritingMode();
    266  const nscoord availBSize = ContentBSize() == NS_UNCONSTRAINEDSIZE
    267                                 ? NS_UNCONSTRAINEDSIZE
    268                                 : std::max(0, ContentBEnd() - mBCoord);
    269  return LogicalSize(wm, ContentISize(), availBSize);
    270 }
    271 
    272 bool BlockReflowState::FloatAvoidingBlockFitsInAvailSpace(
    273    nsIFrame* aFloatAvoidingBlock,
    274    const nsFlowAreaRect& aFloatAvailableSpace) const {
    275  if (!aFloatAvailableSpace.HasFloats()) {
    276    // If there aren't any floats here, then we always fit.
    277    // We check this before calling ISizeToClearPastFloats, which is
    278    // somewhat expensive.
    279    return true;
    280  }
    281 
    282  // |aFloatAvailableSpace| was computed as having a negative size, which means
    283  // there are floats on both sides pushing inwards past each other, and
    284  // |aFloatAvoidingBlock| would necessarily intersect a float if we put it
    285  // here. So, it doesn't fit.
    286  if (aFloatAvailableSpace.ISizeIsActuallyNegative()) {
    287    return false;
    288  }
    289 
    290  WritingMode wm = mReflowInput.GetWritingMode();
    291  nsBlockFrame::FloatAvoidingISizeToClear replacedISize =
    292      nsBlockFrame::ISizeToClearPastFloats(*this, aFloatAvailableSpace.mRect,
    293                                           aFloatAvoidingBlock);
    294  // The inline-start side of the replaced element should be offset by
    295  // the larger of the float intrusion or the replaced element's own
    296  // start margin.  The inline-end side is similar, except for Web
    297  // compatibility we ignore the margin.
    298  return std::max(
    299             aFloatAvailableSpace.mRect.IStart(wm) - mContentArea.IStart(wm),
    300             replacedISize.marginIStart) +
    301             replacedISize.borderBoxISize +
    302             (mContentArea.IEnd(wm) - aFloatAvailableSpace.mRect.IEnd(wm)) <=
    303         mContentArea.ISize(wm);
    304 }
    305 
    306 nsFlowAreaRect BlockReflowState::GetFloatAvailableSpaceWithState(
    307    WritingMode aCBWM, nscoord aBCoord, ShapeType aShapeType,
    308    nsFloatManager::SavedState* aState) const {
    309  WritingMode wm = mReflowInput.GetWritingMode();
    310 #ifdef DEBUG
    311  // Verify that the caller setup the coordinate system properly
    312  nscoord wI, wB;
    313  FloatManager()->GetTranslation(wI, wB);
    314 
    315  NS_ASSERTION((wI == mFloatManagerI) && (wB == mFloatManagerB),
    316               "bad coord system");
    317 #endif
    318 
    319  nscoord blockSize = (mContentArea.BSize(wm) == nscoord_MAX)
    320                          ? nscoord_MAX
    321                          : std::max(mContentArea.BEnd(wm) - aBCoord, 0);
    322  nsFlowAreaRect result = FloatManager()->GetFlowArea(
    323      aCBWM, wm, aBCoord, blockSize, BandInfoType::BandFromPoint, aShapeType,
    324      mContentArea, aState, ContainerSize());
    325  // Keep the inline size >= 0 for compatibility with nsSpaceManager.
    326  if (result.mRect.ISize(wm) < 0) {
    327    result.mRect.ISize(wm) = 0;
    328  }
    329 
    330 #ifdef DEBUG
    331  if (nsBlockFrame::gNoisyReflow) {
    332    nsIFrame::IndentBy(stdout, nsBlockFrame::gNoiseIndent);
    333    fmt::println(FMT_STRING("{} band={} hasFloats={}"), __func__,
    334                 ToString(result.mRect), YesOrNo(result.HasFloats()));
    335  }
    336 #endif
    337  return result;
    338 }
    339 
    340 nsFlowAreaRect BlockReflowState::GetFloatAvailableSpaceForBSize(
    341    WritingMode aCBWM, nscoord aBCoord, nscoord aBSize,
    342    nsFloatManager::SavedState* aState) const {
    343  WritingMode wm = mReflowInput.GetWritingMode();
    344 #ifdef DEBUG
    345  // Verify that the caller setup the coordinate system properly
    346  nscoord wI, wB;
    347  FloatManager()->GetTranslation(wI, wB);
    348 
    349  NS_ASSERTION((wI == mFloatManagerI) && (wB == mFloatManagerB),
    350               "bad coord system");
    351 #endif
    352  nsFlowAreaRect result = FloatManager()->GetFlowArea(
    353      aCBWM, wm, aBCoord, aBSize, BandInfoType::WidthWithinHeight,
    354      ShapeType::ShapeOutside, mContentArea, aState, ContainerSize());
    355  // Keep the width >= 0 for compatibility with nsSpaceManager.
    356  if (result.mRect.ISize(wm) < 0) {
    357    result.mRect.ISize(wm) = 0;
    358  }
    359 
    360 #ifdef DEBUG
    361  if (nsBlockFrame::gNoisyReflow) {
    362    nsIFrame::IndentBy(stdout, nsBlockFrame::gNoiseIndent);
    363    fmt::println(FMT_STRING("{} band={} hasFloats={}"), __func__,
    364                 ToString(result.mRect), YesOrNo(result.HasFloats()));
    365  }
    366 #endif
    367  return result;
    368 }
    369 
    370 /*
    371 * Reconstruct the vertical margin before the line |aLine| in order to
    372 * do an incremental reflow that begins with |aLine| without reflowing
    373 * the line before it.  |aLine| may point to the fencepost at the end of
    374 * the line list, and it is used this way since we (for now, anyway)
    375 * always need to recover margins at the end of a block.
    376 *
    377 * The reconstruction involves walking backward through the line list to
    378 * find any collapsed margins preceding the line that would have been in
    379 * the reflow input's |mPrevBEndMargin| when we reflowed that line in
    380 * a full reflow (under the rule in CSS2 that all adjacent vertical
    381 * margins of blocks collapse).
    382 */
    383 void BlockReflowState::ReconstructMarginBefore(nsLineList::iterator aLine) {
    384  mPrevBEndMargin.Zero();
    385  nsBlockFrame* block = mBlock;
    386 
    387  nsLineList::iterator firstLine = block->LinesBegin();
    388  for (;;) {
    389    --aLine;
    390    if (aLine->IsBlock()) {
    391      mPrevBEndMargin = aLine->GetCarriedOutBEndMargin();
    392      break;
    393    }
    394    if (!aLine->IsEmpty()) {
    395      break;
    396    }
    397    if (aLine == firstLine) {
    398      // If the top margin was carried out (and thus already applied),
    399      // set it to zero.  Either way, we're done.
    400      if (!mFlags.mIsBStartMarginRoot) {
    401        mPrevBEndMargin.Zero();
    402      }
    403      break;
    404    }
    405  }
    406 }
    407 
    408 void BlockReflowState::AppendPushedFloatChain(nsIFrame* aFloatCont) {
    409  nsFrameList* pushedFloats = mBlock->EnsurePushedFloats();
    410  while (true) {
    411    aFloatCont->AddStateBits(NS_FRAME_IS_PUSHED_OUT_OF_FLOW);
    412    pushedFloats->AppendFrame(mBlock, aFloatCont);
    413    aFloatCont = aFloatCont->GetNextInFlow();
    414    if (!aFloatCont || aFloatCont->GetParent() != mBlock) {
    415      break;
    416    }
    417    mBlock->StealFrame(aFloatCont);
    418  }
    419 }
    420 
    421 /**
    422 * Restore information about floats into the float manager for an
    423 * incremental reflow, and simultaneously push the floats by
    424 * |aDeltaBCoord|, which is the amount |aLine| was pushed relative to its
    425 * parent.  The recovery of state is one of the things that makes
    426 * incremental reflow O(N^2) and this state should really be kept
    427 * around, attached to the frame tree.
    428 */
    429 void BlockReflowState::RecoverFloats(nsLineList::iterator aLine,
    430                                     nscoord aDeltaBCoord) {
    431  WritingMode wm = mReflowInput.GetWritingMode();
    432  if (aLine->HasFloats()) {
    433    // Place the floats into the float manager again. Also slide
    434    // them, just like the regular frames on the line.
    435    for (nsIFrame* floatFrame : aLine->Floats()) {
    436      if (aDeltaBCoord != 0) {
    437        floatFrame->MovePositionBy(nsPoint(0, aDeltaBCoord));
    438      }
    439 #ifdef DEBUG
    440      if (nsBlockFrame::gNoisyReflow || nsBlockFrame::gNoisyFloatManager) {
    441        nscoord tI, tB;
    442        FloatManager()->GetTranslation(tI, tB);
    443        nsIFrame::IndentBy(stdout, nsBlockFrame::gNoiseIndent);
    444        printf("RecoverFloats: tIB=%d,%d (%d,%d) ", tI, tB, mFloatManagerI,
    445               mFloatManagerB);
    446        floatFrame->ListTag(stdout);
    447        LogicalRect region =
    448            nsFloatManager::GetRegionFor(wm, floatFrame, ContainerSize());
    449        printf(" aDeltaBCoord=%d region={%d,%d,%d,%d}\n", aDeltaBCoord,
    450               region.IStart(wm), region.BStart(wm), region.ISize(wm),
    451               region.BSize(wm));
    452      }
    453 #endif
    454      FloatManager()->AddFloat(
    455          floatFrame,
    456          nsFloatManager::GetRegionFor(wm, floatFrame, ContainerSize()), wm,
    457          ContainerSize());
    458    }
    459  } else if (aLine->IsBlock()) {
    460    nsBlockFrame::RecoverFloatsFor(aLine->mFirstChild, *FloatManager(), wm,
    461                                   ContainerSize());
    462  }
    463 }
    464 
    465 /**
    466 * Everything done in this function is done O(N) times for each pass of
    467 * reflow so it is O(N*M) where M is the number of incremental reflow
    468 * passes.  That's bad.  Don't do stuff here.
    469 *
    470 * When this function is called, |aLine| has just been slid by |aDeltaBCoord|
    471 * and the purpose of RecoverStateFrom is to ensure that the
    472 * BlockReflowState is in the same state that it would have been in
    473 * had the line just been reflowed.
    474 *
    475 * Most of the state recovery that we have to do involves floats.
    476 */
    477 void BlockReflowState::RecoverStateFrom(nsLineList::iterator aLine,
    478                                        nscoord aDeltaBCoord) {
    479  // Make the line being recovered the current line
    480  mCurrentLine = aLine;
    481 
    482  // Place floats for this line into the float manager
    483  if (aLine->HasFloats() || aLine->IsBlock()) {
    484    RecoverFloats(aLine, aDeltaBCoord);
    485 
    486 #ifdef DEBUG
    487    if (nsBlockFrame::gNoisyReflow || nsBlockFrame::gNoisyFloatManager) {
    488      FloatManager()->List(stdout);
    489    }
    490 #endif
    491  }
    492 }
    493 
    494 // This is called by the line layout's AddFloat method when a
    495 // place-holder frame is reflowed in a line. If the float is a
    496 // left-most child (it's x coordinate is at the line's left margin)
    497 // then the float is place immediately, otherwise the float
    498 // placement is deferred until the line has been reflowed.
    499 
    500 // XXXldb This behavior doesn't quite fit with CSS1 and CSS2 --
    501 // technically we're supposed let the current line flow around the
    502 // float as well unless it won't fit next to what we already have.
    503 // But nobody else implements it that way...
    504 bool BlockReflowState::AddFloat(nsLineLayout* aLineLayout, nsIFrame* aFloat,
    505                                nscoord aAvailableISize) {
    506  MOZ_ASSERT(aLineLayout, "must have line layout");
    507  MOZ_ASSERT(mBlock->LinesEnd() != mCurrentLine, "null ptr");
    508  MOZ_ASSERT(aFloat->HasAnyStateBits(NS_FRAME_OUT_OF_FLOW),
    509             "aFloat must be an out-of-flow frame");
    510 
    511  MOZ_ASSERT(aFloat->GetParent(), "float must have parent");
    512  MOZ_ASSERT(aFloat->GetParent()->IsBlockFrameOrSubclass(),
    513             "float's parent must be block");
    514  if (aFloat->HasAnyStateBits(NS_FRAME_IS_PUSHED_OUT_OF_FLOW) ||
    515      aFloat->GetParent() != mBlock) {
    516    MOZ_ASSERT(aFloat->HasAnyStateBits(NS_FRAME_IS_PUSHED_OUT_OF_FLOW |
    517                                       NS_FRAME_FIRST_REFLOW),
    518               "float should be in this block unless it was marked as "
    519               "pushed out-of-flow, or just inserted");
    520    MOZ_ASSERT(aFloat->GetParent()->FirstContinuation() ==
    521               mBlock->FirstContinuation());
    522    // If, in a previous reflow, the float was pushed entirely to
    523    // another column/page, we need to steal it back.  (We might just
    524    // push it again, though.)  Likewise, if that previous reflow
    525    // reflowed this block but not its next continuation, we might need
    526    // to steal it from our own float-continuations list.
    527    //
    528    // For more about pushed floats, see the comment above
    529    // nsBlockFrame::DrainPushedFloats.
    530    auto* floatParent = static_cast<nsBlockFrame*>(aFloat->GetParent());
    531    floatParent->StealFrame(aFloat);
    532 
    533    aFloat->RemoveStateBits(NS_FRAME_IS_PUSHED_OUT_OF_FLOW);
    534 
    535    // Appending is fine, since if a float was pushed to the next
    536    // page/column, all later floats were also pushed.
    537    mBlock->EnsureFloats()->AppendFrame(mBlock, aFloat);
    538  }
    539 
    540  // Because we are in the middle of reflowing a placeholder frame
    541  // within a line (and possibly nested in an inline frame or two
    542  // that's a child of our block) we need to restore the space
    543  // manager's translation to the space that the block resides in
    544  // before placing the float.
    545  nscoord oI, oB;
    546  FloatManager()->GetTranslation(oI, oB);
    547  nscoord dI = oI - mFloatManagerI;
    548  nscoord dB = oB - mFloatManagerB;
    549  FloatManager()->Translate(-dI, -dB);
    550 
    551  bool placed = false;
    552 
    553  // Now place the float immediately if possible. Otherwise stash it
    554  // away in mBelowCurrentLineFloats and place it later.
    555  // If one or more floats has already been pushed to the next line,
    556  // don't let this one go on the current line, since that would violate
    557  // float ordering.
    558  bool shouldPlaceFloatBelowCurrentLine = false;
    559  if (mBelowCurrentLineFloats.IsEmpty()) {
    560    // If the current line is empty, we don't impose any inline-size constraint
    561    // from the line layout.
    562    Maybe<nscoord> availableISizeInCurrentLine =
    563        aLineLayout->LineIsEmpty() ? Nothing() : Some(aAvailableISize);
    564    PlaceFloatResult result =
    565        FlowAndPlaceFloat(aFloat, availableISizeInCurrentLine);
    566    if (result == PlaceFloatResult::Placed) {
    567      placed = true;
    568      // Pass on updated available space to the current inline reflow engine
    569      WritingMode wm = mReflowInput.GetWritingMode();
    570      // If we have mLineBSize, we are reflowing the line again due to
    571      // LineReflowStatus::RedoMoreFloats. We should use mLineBSize to query the
    572      // correct available space.
    573      nsFlowAreaRect floatAvailSpace =
    574          mLineBSize.isNothing()
    575              ? GetFloatAvailableSpace(wm, mBCoord)
    576              : GetFloatAvailableSpaceForBSize(wm, mBCoord, mLineBSize.value(),
    577                                               nullptr);
    578      LogicalRect availSpace(wm, floatAvailSpace.mRect.IStart(wm), mBCoord,
    579                             floatAvailSpace.mRect.ISize(wm),
    580                             floatAvailSpace.mRect.BSize(wm));
    581      aLineLayout->UpdateBand(wm, availSpace, aFloat);
    582      // Record this float in the current-line list
    583      mCurrentLineFloats.AppendElement(aFloat);
    584    } else if (result == PlaceFloatResult::ShouldPlaceInNextContinuation) {
    585      (*aLineLayout->GetLine())->SetHadFloatPushed();
    586    } else {
    587      MOZ_ASSERT(result == PlaceFloatResult::ShouldPlaceBelowCurrentLine);
    588      shouldPlaceFloatBelowCurrentLine = true;
    589    }
    590  } else {
    591    shouldPlaceFloatBelowCurrentLine = true;
    592  }
    593 
    594  if (shouldPlaceFloatBelowCurrentLine) {
    595    // Always claim to be placed; we don't know whether we fit yet, so we
    596    // deal with this in PlaceBelowCurrentLineFloats
    597    placed = true;
    598    // This float will be placed after the line is done (it is a
    599    // below-current-line float).
    600    mBelowCurrentLineFloats.AppendElement(aFloat);
    601  }
    602 
    603  // Restore coordinate system
    604  FloatManager()->Translate(dI, dB);
    605 
    606  return placed;
    607 }
    608 
    609 bool BlockReflowState::CanPlaceFloat(
    610    nscoord aFloatISize, const nsFlowAreaRect& aFloatAvailableSpace) {
    611  // A float fits at a given block-dir position if there are no floats
    612  // at its inline-dir position (no matter what its inline size) or if
    613  // its inline size fits in the space remaining after prior floats have
    614  // been placed.
    615  // FIXME: We should allow overflow by up to half a pixel here (bug 21193).
    616  return !aFloatAvailableSpace.HasFloats() ||
    617         aFloatAvailableSpace.mRect.ISize(mReflowInput.GetWritingMode()) >=
    618             aFloatISize;
    619 }
    620 
    621 // Return the inline-size that the float (including margins) will take up
    622 // in the writing mode of the containing block. If this returns
    623 // NS_UNCONSTRAINEDSIZE, we're dealing with an orthogonal block that
    624 // has block-size:auto, and we'll need to actually reflow it to find out
    625 // how much inline-size it will occupy in the containing block's mode.
    626 static nscoord FloatMarginISize(WritingMode aCBWM,
    627                                const ReflowInput& aFloatRI) {
    628  if (aFloatRI.ComputedSize(aCBWM).ISize(aCBWM) == NS_UNCONSTRAINEDSIZE) {
    629    return NS_UNCONSTRAINEDSIZE;  // reflow is needed to get the true size
    630  }
    631  return aFloatRI.ComputedSizeWithMarginBorderPadding(aCBWM).ISize(aCBWM);
    632 }
    633 
    634 // A frame property that stores the last shape source / margin / etc. if there's
    635 // any shape, in order to invalidate the float area properly when it changes.
    636 //
    637 // TODO(emilio): This could really belong to GetRegionFor / StoreRegionFor, but
    638 // when I tried it was a bit awkward because of the logical -> physical
    639 // conversion that happens there.
    640 //
    641 // Maybe all this code could be refactored to make this cleaner, but keeping the
    642 // two properties separated was slightly nicer.
    643 struct ShapeInvalidationData {
    644  StyleShapeOutside mShapeOutside{StyleShapeOutside::None()};
    645  float mShapeImageThreshold = 0.0;
    646  LengthPercentage mShapeMargin;
    647 
    648  ShapeInvalidationData() = default;
    649 
    650  explicit ShapeInvalidationData(const nsStyleDisplay& aDisplay) {
    651    Update(aDisplay);
    652  }
    653 
    654  static bool IsNeeded(const nsStyleDisplay& aDisplay) {
    655    return !aDisplay.mShapeOutside.IsNone();
    656  }
    657 
    658  void Update(const nsStyleDisplay& aDisplay) {
    659    MOZ_ASSERT(IsNeeded(aDisplay));
    660    mShapeOutside = aDisplay.mShapeOutside;
    661    mShapeImageThreshold = aDisplay.mShapeImageThreshold;
    662    mShapeMargin = aDisplay.mShapeMargin;
    663  }
    664 
    665  bool Matches(const nsStyleDisplay& aDisplay) const {
    666    return mShapeOutside == aDisplay.mShapeOutside &&
    667           mShapeImageThreshold == aDisplay.mShapeImageThreshold &&
    668           mShapeMargin == aDisplay.mShapeMargin;
    669  }
    670 };
    671 
    672 NS_DECLARE_FRAME_PROPERTY_DELETABLE(ShapeInvalidationDataProperty,
    673                                    ShapeInvalidationData)
    674 
    675 BlockReflowState::PlaceFloatResult BlockReflowState::FlowAndPlaceFloat(
    676    nsIFrame* aFloat, Maybe<nscoord> aAvailableISizeInCurrentLine) {
    677  MOZ_ASSERT(aFloat->GetParent() == mBlock, "Float frame has wrong parent");
    678 
    679  WritingMode wm = mReflowInput.GetWritingMode();
    680  // Save away the block-dir coordinate before placing the float. We will
    681  // restore mBCoord at the end after placing the float. This is
    682  // necessary because any adjustments to mBCoord during the float
    683  // placement are for the float only, not for any non-floating
    684  // content.
    685  AutoRestore<nscoord> restoreBCoord(mBCoord);
    686 
    687  // Whether the block-direction position available to place a float has been
    688  // pushed down due to the presence of other floats.
    689  auto HasFloatPushedDown = [this, &restoreBCoord]() {
    690    return mBCoord != restoreBCoord.SavedValue();
    691  };
    692 
    693  // Grab the float's display information
    694  const nsStyleDisplay* floatDisplay = aFloat->StyleDisplay();
    695 
    696  // The float's old region, so we can propagate damage.
    697  LogicalRect oldRegion =
    698      nsFloatManager::GetRegionFor(wm, aFloat, ContainerSize());
    699 
    700  ShapeInvalidationData* invalidationData =
    701      aFloat->GetProperty(ShapeInvalidationDataProperty());
    702 
    703  // Enforce CSS2 9.5.1 rule [2], i.e., make sure that a float isn't
    704  // ``above'' another float that preceded it in the flow.
    705  mBCoord = std::max(FloatManager()->LowestFloatBStart(), mBCoord);
    706 
    707  // See if the float should clear any preceding floats...
    708  // XXX We need to mark this float somehow so that it gets reflowed
    709  // when floats are inserted before it.
    710  if (StyleClear::None != floatDisplay->mClear) {
    711    // XXXldb Does this handle vertical margins correctly?
    712    auto [bCoord, result] = ClearFloats(mBCoord, floatDisplay->UsedClear(wm));
    713    if (result == ClearFloatsResult::FloatsPushedOrSplit) {
    714      PushFloatPastBreak(aFloat);
    715      return PlaceFloatResult::ShouldPlaceInNextContinuation;
    716    }
    717    mBCoord = bCoord;
    718  }
    719 
    720  LogicalSize availSize = ComputeAvailableSizeForFloat();
    721  const WritingMode floatWM = aFloat->GetWritingMode();
    722  Maybe<ReflowInput> floatRI(std::in_place, mPresContext, mReflowInput, aFloat,
    723                             availSize.ConvertTo(floatWM, wm));
    724 
    725  nscoord floatMarginISize = FloatMarginISize(wm, *floatRI);
    726  LogicalMargin floatMargin = floatRI->ComputedLogicalMargin(wm);
    727  nsReflowStatus reflowStatus;
    728 
    729  // If it's a floating first-letter, we need to reflow it before we
    730  // know how wide it is (since we don't compute which letters are part
    731  // of the first letter until reflow!).
    732  // We also need to do this early reflow if FloatMarginISize returned
    733  // an unconstrained inline-size, which can occur if the float had an
    734  // orthogonal writing mode and 'auto' block-size (in its mode).
    735  bool earlyFloatReflow =
    736      aFloat->IsLetterFrame() || floatMarginISize == NS_UNCONSTRAINEDSIZE;
    737  if (earlyFloatReflow) {
    738    mBlock->ReflowFloat(*this, *floatRI, aFloat, reflowStatus);
    739    floatMarginISize = aFloat->ISize(wm) + floatMargin.IStartEnd(wm);
    740    NS_ASSERTION(reflowStatus.IsComplete(),
    741                 "letter frames and orthogonal floats with auto block-size "
    742                 "shouldn't break, and if they do now, then they're breaking "
    743                 "at the wrong point");
    744  }
    745 
    746  // Now we've computed the float's margin inline-size.
    747  if (aAvailableISizeInCurrentLine &&
    748      floatMarginISize > *aAvailableISizeInCurrentLine) {
    749    // The float cannot fit in the available inline-size of the current line.
    750    // Let's notify our caller to place it later.
    751    return PlaceFloatResult::ShouldPlaceBelowCurrentLine;
    752  }
    753 
    754  // Find a place to place the float. The CSS2 spec doesn't want
    755  // floats overlapping each other or sticking out of the containing
    756  // block if possible (CSS2 spec section 9.5.1, see the rule list).
    757  UsedFloat floatStyle = floatDisplay->UsedFloat(wm);
    758  MOZ_ASSERT(UsedFloat::Left == floatStyle || UsedFloat::Right == floatStyle,
    759             "Invalid float type!");
    760 
    761  // Are we required to place at least part of the float because we're
    762  // at the top of the page (to avoid an infinite loop of pushing and
    763  // breaking).
    764  bool mustPlaceFloat =
    765      mReflowInput.mFlags.mIsTopOfPage && IsAdjacentWithBStart();
    766 
    767  // Get the band of available space with respect to margin box.
    768  nsFlowAreaRect floatAvailableSpace =
    769      GetFloatAvailableSpaceForPlacingFloat(wm, mBCoord);
    770 
    771  for (;;) {
    772    if (mReflowInput.AvailableBSize() != NS_UNCONSTRAINEDSIZE &&
    773        floatAvailableSpace.mRect.BSize(wm) <= 0 && !mustPlaceFloat) {
    774      // No space, nowhere to put anything.
    775      PushFloatPastBreak(aFloat);
    776      return PlaceFloatResult::ShouldPlaceInNextContinuation;
    777    }
    778 
    779    if (CanPlaceFloat(floatMarginISize, floatAvailableSpace)) {
    780      // We found an appropriate place.
    781      break;
    782    }
    783 
    784    // Nope. try to advance to the next band.
    785    mBCoord += floatAvailableSpace.mRect.BSize(wm);
    786    floatAvailableSpace = GetFloatAvailableSpaceForPlacingFloat(wm, mBCoord);
    787    mustPlaceFloat = false;
    788  }
    789 
    790  // If the float is continued, it will get the same absolute x value as its
    791  // prev-in-flow
    792 
    793  // We don't worry about the geometry of the prev in flow, let the continuation
    794  // place and size itself as required.
    795 
    796  // Assign inline and block dir coordinates to the float. We don't use
    797  // LineLeft() and LineRight() here, because we would only have to
    798  // convert the result back into this block's writing mode.
    799  LogicalPoint floatPos(wm);
    800  bool leftFloat = floatStyle == UsedFloat::Left;
    801 
    802  if (leftFloat == wm.IsBidiLTR()) {
    803    floatPos.I(wm) = floatAvailableSpace.mRect.IStart(wm);
    804  } else {
    805    floatPos.I(wm) = floatAvailableSpace.mRect.IEnd(wm) - floatMarginISize;
    806  }
    807  // CSS2 spec, 9.5.1 rule [4]: "A floating box's outer top may not
    808  // be higher than the top of its containing block."  (Since the
    809  // containing block is the content edge of the block box, this
    810  // means the margin edge of the float can't be higher than the
    811  // content edge of the block that contains it.)
    812  floatPos.B(wm) = std::max(mBCoord, ContentBStart());
    813 
    814  // Reflow the float after computing its vertical position so it knows
    815  // where to break.
    816  if (!earlyFloatReflow) {
    817    const LogicalSize oldAvailSize = availSize;
    818    availSize = ComputeAvailableSizeForFloat();
    819    if (oldAvailSize != availSize) {
    820      floatRI.reset();
    821      floatRI.emplace(mPresContext, mReflowInput, aFloat,
    822                      availSize.ConvertTo(floatWM, wm));
    823    }
    824    // Normally the mIsTopOfPage state is copied from the parent reflow input.
    825    // However, when reflowing a float, if we've placed other floats that force
    826    // this float being pushed down, we should unset the mIsTopOfPage bit.
    827    if (floatRI->mFlags.mIsTopOfPage && HasFloatPushedDown()) {
    828      // HasFloatPushedDown() implies that we increased mBCoord, and we
    829      // should've turned off mustPlaceFloat when we did that.
    830      NS_ASSERTION(!mustPlaceFloat,
    831                   "mustPlaceFloat shouldn't be set if we're not at the "
    832                   "top-of-page!");
    833      floatRI->mFlags.mIsTopOfPage = false;
    834    }
    835    mBlock->ReflowFloat(*this, *floatRI, aFloat, reflowStatus);
    836  }
    837  if (aFloat->GetPrevInFlow()) {
    838    floatMargin.BStart(wm) = 0;
    839  }
    840  if (reflowStatus.IsIncomplete()) {
    841    floatMargin.BEnd(wm) = 0;
    842  }
    843 
    844  // If the float cannot fit (e.g. via fragmenting itself if applicable), or if
    845  // we're forced to break before it for CSS break-* reasons, then it needs to
    846  // be pushed in its entirety to the next column/page.
    847  //
    848  // Note we use the available block-size in floatRI rather than use
    849  // availSize.BSize() because nsBlockReflowContext::ReflowBlock() might adjust
    850  // floatRI's available size.
    851  const nscoord availBSize = floatRI->AvailableSize(floatWM).BSize(floatWM);
    852  const bool isTruncated =
    853      availBSize != NS_UNCONSTRAINEDSIZE && aFloat->BSize(floatWM) > availBSize;
    854  if ((!floatRI->mFlags.mIsTopOfPage && isTruncated) ||
    855      reflowStatus.IsInlineBreakBefore()) {
    856    PushFloatPastBreak(aFloat);
    857    return PlaceFloatResult::ShouldPlaceInNextContinuation;
    858  }
    859 
    860  // We can't use aFloat->ShouldAvoidBreakInside(mReflowInput) here since
    861  // its mIsTopOfPage may be true even though the float isn't at the
    862  // top when floatPos.B(wm) > 0.
    863  if (ContentBSize() != NS_UNCONSTRAINEDSIZE && !mustPlaceFloat &&
    864      (!mReflowInput.mFlags.mIsTopOfPage || floatPos.B(wm) > 0) &&
    865      StyleBreakWithin::Avoid == aFloat->StyleDisplay()->mBreakInside &&
    866      (!reflowStatus.IsFullyComplete() ||
    867       aFloat->BSize(wm) + floatMargin.BStartEnd(wm) >
    868           ContentBEnd() - floatPos.B(wm)) &&
    869      !aFloat->GetPrevInFlow()) {
    870    PushFloatPastBreak(aFloat);
    871    return PlaceFloatResult::ShouldPlaceInNextContinuation;
    872  }
    873 
    874  // Calculate the actual origin of the float frame's border rect
    875  // relative to the parent block; the margin must be added in
    876  // to get the border rect
    877  LogicalPoint origin(wm, floatMargin.IStart(wm) + floatPos.I(wm),
    878                      floatMargin.BStart(wm) + floatPos.B(wm));
    879 
    880  // If float is relatively positioned, factor that in as well
    881  const LogicalMargin floatOffsets = floatRI->ComputedLogicalOffsets(wm);
    882  ReflowInput::ApplyRelativePositioning(aFloat, wm, floatOffsets, &origin,
    883                                        ContainerSize());
    884 
    885  // Position the float.
    886  bool moved = aFloat->GetLogicalPosition(wm, ContainerSize()) != origin;
    887  if (moved) {
    888    aFloat->SetPosition(wm, origin, ContainerSize());
    889  }
    890 
    891  // Update the float combined area state
    892  // XXX Floats should really just get invalidated here if necessary
    893  mFloatOverflowAreas.UnionWith(aFloat->GetOverflowAreasRelativeToParent());
    894 
    895  // Place the float in the float manager
    896  // calculate region
    897  LogicalRect region = nsFloatManager::CalculateRegionFor(
    898      wm, aFloat, floatMargin, ContainerSize());
    899  // if the float split, then take up all of the vertical height
    900  if (reflowStatus.IsIncomplete() && (NS_UNCONSTRAINEDSIZE != ContentBSize())) {
    901    region.BSize(wm) =
    902        std::max(region.BSize(wm), ContentBSize() - floatPos.B(wm));
    903  }
    904  FloatManager()->AddFloat(aFloat, region, wm, ContainerSize());
    905 
    906  // store region
    907  nsFloatManager::StoreRegionFor(wm, aFloat, region, ContainerSize());
    908 
    909  const bool invalidationDataNeeded =
    910      ShapeInvalidationData::IsNeeded(*floatDisplay);
    911 
    912  // If the float's dimensions or shape have changed, note the damage in the
    913  // float manager.
    914  if (!region.IsEqualEdges(oldRegion) ||
    915      !!invalidationData != invalidationDataNeeded ||
    916      (invalidationData && !invalidationData->Matches(*floatDisplay))) {
    917    // XXXwaterson conservative: we could probably get away with noting
    918    // less damage; e.g., if only height has changed, then only note the
    919    // area into which the float has grown or from which the float has
    920    // shrunk.
    921    nscoord blockStart = std::min(region.BStart(wm), oldRegion.BStart(wm));
    922    nscoord blockEnd = std::max(region.BEnd(wm), oldRegion.BEnd(wm));
    923    FloatManager()->IncludeInDamage(blockStart, blockEnd);
    924  }
    925 
    926  if (invalidationDataNeeded) {
    927    if (invalidationData) {
    928      invalidationData->Update(*floatDisplay);
    929    } else {
    930      aFloat->SetProperty(ShapeInvalidationDataProperty(),
    931                          new ShapeInvalidationData(*floatDisplay));
    932    }
    933  } else if (invalidationData) {
    934    invalidationData = nullptr;
    935    aFloat->RemoveProperty(ShapeInvalidationDataProperty());
    936  }
    937 
    938  if (!reflowStatus.IsFullyComplete()) {
    939    mBlock->SplitFloat(*this, aFloat, reflowStatus);
    940  } else {
    941    MOZ_ASSERT(!aFloat->GetNextInFlow());
    942  }
    943 
    944 #ifdef DEBUG
    945  if (nsBlockFrame::gNoisyFloatManager) {
    946    nscoord tI, tB;
    947    FloatManager()->GetTranslation(tI, tB);
    948    mBlock->ListTag(stdout);
    949    printf(": FlowAndPlaceFloat: AddFloat: tIB=%d,%d (%d,%d) {%d,%d,%d,%d}\n",
    950           tI, tB, mFloatManagerI, mFloatManagerB, region.IStart(wm),
    951           region.BStart(wm), region.ISize(wm), region.BSize(wm));
    952  }
    953 
    954  if (nsBlockFrame::gNoisyReflow) {
    955    nsRect r = aFloat->GetRect();
    956    nsIFrame::IndentBy(stdout, nsBlockFrame::gNoiseIndent);
    957    printf("placed float: ");
    958    aFloat->ListTag(stdout);
    959    printf(" %d,%d,%d,%d\n", r.x, r.y, r.width, r.height);
    960  }
    961 #endif
    962 
    963  return PlaceFloatResult::Placed;
    964 }
    965 
    966 void BlockReflowState::PushFloatPastBreak(nsIFrame* aFloat) {
    967  // This ensures that we:
    968  //  * don't try to place later but smaller floats (which CSS says
    969  //    must have their tops below the top of this float)
    970  //  * don't waste much time trying to reflow this float again until
    971  //    after the break
    972  WritingMode wm = mReflowInput.GetWritingMode();
    973  UsedFloat floatStyle = aFloat->StyleDisplay()->UsedFloat(wm);
    974  if (floatStyle == UsedFloat::Left) {
    975    FloatManager()->SetPushedLeftFloatPastBreak();
    976  } else {
    977    MOZ_ASSERT(floatStyle == UsedFloat::Right, "Unexpected float value!");
    978    FloatManager()->SetPushedRightFloatPastBreak();
    979  }
    980 
    981  // Put the float on the pushed floats list, even though it
    982  // isn't actually a continuation.
    983  mBlock->StealFrame(aFloat);
    984  AppendPushedFloatChain(aFloat);
    985  mReflowStatus.SetOverflowIncomplete();
    986 }
    987 
    988 /**
    989 * Place below-current-line floats.
    990 */
    991 void BlockReflowState::PlaceBelowCurrentLineFloats(nsLineBox* aLine) {
    992  MOZ_ASSERT(!mBelowCurrentLineFloats.IsEmpty());
    993  nsTArray<nsIFrame*> floatsPlacedInLine;
    994  for (nsIFrame* f : mBelowCurrentLineFloats) {
    995 #ifdef DEBUG
    996    if (nsBlockFrame::gNoisyReflow) {
    997      nsIFrame::IndentBy(stdout, nsBlockFrame::gNoiseIndent);
    998      printf("placing bcl float: ");
    999      f->ListTag(stdout);
   1000      printf("\n");
   1001    }
   1002 #endif
   1003    // Place the float
   1004    PlaceFloatResult result = FlowAndPlaceFloat(f);
   1005    MOZ_ASSERT(result != PlaceFloatResult::ShouldPlaceBelowCurrentLine,
   1006               "We are already dealing with below current line floats!");
   1007    if (result == PlaceFloatResult::Placed) {
   1008      floatsPlacedInLine.AppendElement(f);
   1009    }
   1010  }
   1011  if (floatsPlacedInLine.Length() != mBelowCurrentLineFloats.Length()) {
   1012    // We have some floats having ShouldPlaceInNextContinuation result.
   1013    aLine->SetHadFloatPushed();
   1014  }
   1015  aLine->AppendFloats(std::move(floatsPlacedInLine));
   1016  mBelowCurrentLineFloats.Clear();
   1017 }
   1018 
   1019 std::tuple<nscoord, BlockReflowState::ClearFloatsResult>
   1020 BlockReflowState::ClearFloats(nscoord aBCoord, UsedClear aClearType,
   1021                              nsIFrame* aFloatAvoidingBlock) {
   1022 #ifdef DEBUG
   1023  if (nsBlockFrame::gNoisyReflow) {
   1024    nsIFrame::IndentBy(stdout, nsBlockFrame::gNoiseIndent);
   1025    printf("clear floats: in: aBCoord=%d\n", aBCoord);
   1026  }
   1027 #endif
   1028 
   1029  if (!FloatManager()->HasAnyFloats()) {
   1030    return {aBCoord, ClearFloatsResult::BCoordNoChange};
   1031  }
   1032 
   1033  nscoord newBCoord = aBCoord;
   1034 
   1035  if (aClearType != UsedClear::None) {
   1036    newBCoord = FloatManager()->ClearFloats(newBCoord, aClearType);
   1037 
   1038    if (FloatManager()->ClearContinues(aClearType)) {
   1039      return {newBCoord, ClearFloatsResult::FloatsPushedOrSplit};
   1040    }
   1041  }
   1042 
   1043  if (aFloatAvoidingBlock) {
   1044    auto cbWM = aFloatAvoidingBlock->GetContainingBlock()->GetWritingMode();
   1045    for (;;) {
   1046      nsFlowAreaRect floatAvailableSpace =
   1047          GetFloatAvailableSpace(cbWM, newBCoord);
   1048      if (FloatAvoidingBlockFitsInAvailSpace(aFloatAvoidingBlock,
   1049                                             floatAvailableSpace)) {
   1050        break;
   1051      }
   1052      // See the analogous code for inlines in
   1053      // nsBlockFrame::DoReflowInlineFrames
   1054      if (!AdvanceToNextBand(floatAvailableSpace.mRect, &newBCoord)) {
   1055        // Stop trying to clear here; we'll just get pushed to the
   1056        // next column or page and try again there.
   1057        break;
   1058      }
   1059    }
   1060  }
   1061 
   1062 #ifdef DEBUG
   1063  if (nsBlockFrame::gNoisyReflow) {
   1064    nsIFrame::IndentBy(stdout, nsBlockFrame::gNoiseIndent);
   1065    printf("clear floats: out: y=%d\n", newBCoord);
   1066  }
   1067 #endif
   1068 
   1069  ClearFloatsResult result = newBCoord == aBCoord
   1070                                 ? ClearFloatsResult::BCoordNoChange
   1071                                 : ClearFloatsResult::BCoordAdvanced;
   1072  return {newBCoord, result};
   1073 }