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