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