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"
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"
24 # include "nsBlockDebugFlags.h"
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
)
36 mPresContext(aPresContext
),
37 mReflowInput(aReflowInput
),
38 mContentArea(aReflowInput
.GetWritingMode()),
39 mInsetForBalance(aInset
),
40 mPushedFloats(nullptr),
41 mOverflowTracker(nullptr),
44 .ComputedLogicalBorderPadding(mReflowInput
.GetWritingMode())
45 .ApplySkipSides(aFrame
->PreReflowBlockLevelLogicalSkipSides())),
47 mMinLineHeight(aReflowInput
.GetLineHeight()),
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
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
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
) >
126 const nscoord bp
= reserveSpaceForBlockEndBP
? mBorderPadding
.BStartEnd(wm
)
127 : mBorderPadding
.BStart(wm
);
128 mContentArea
.BSize(wm
) = std::max(0, availableBSize
- bp
);
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.
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
);
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());
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.
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
) {
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
);
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
);
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
);
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
));
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.
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()) {
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.
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();
308 // Verify that the caller setup the coordinate system properly
310 FloatManager()->GetTranslation(wI
, wB
);
312 NS_ASSERTION((wI
== mFloatManagerI
) && (wB
== mFloatManagerB
),
316 nscoord blockSize
= (mContentArea
.BSize(wm
) == 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;
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());
338 nsFlowAreaRect
BlockReflowState::GetFloatAvailableSpaceForBSize(
339 nscoord aBCoord
, nscoord aBSize
, nsFloatManager::SavedState
* aState
) const {
340 WritingMode wm
= mReflowInput
.GetWritingMode();
342 // Verify that the caller setup the coordinate system properly
344 FloatManager()->GetTranslation(wI
, wB
);
346 NS_ASSERTION((wI
== mFloatManagerI
) && (wB
== mFloatManagerB
),
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;
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());
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();
388 if (aLine
->IsBlock()) {
389 mPrevBEndMargin
= aLine
->GetCarriedOutBEndMargin();
392 if (!aLine
->IsEmpty()) {
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();
406 void BlockReflowState::SetupPushedFloatList() {
407 MOZ_ASSERT(!mFlags
.mIsFloatListInBlockPropertyTable
== !mPushedFloats
,
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();
425 aFloatCont
->AddStateBits(NS_FRAME_IS_PUSHED_FLOAT
);
426 mPushedFloats
->AppendFrame(mBlock
, aFloatCont
);
427 aFloatCont
= aFloatCont
->GetNextInFlow();
428 if (!aFloatCont
|| aFloatCont
->GetParent() != mBlock
) {
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
);
456 if (nsBlockFrame::gNoisyReflow
|| nsBlockFrame::gNoisyFloatManager
) {
458 FloatManager()->GetTranslation(tI
, tB
);
459 nsIFrame::IndentBy(stdout
, nsBlockFrame::gNoiseIndent
);
460 printf("RecoverFloats: tIB=%d,%d (%d,%d) ", tI
, tB
, mFloatManagerI
,
462 floatFrame
->ListTag(stdout
);
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
),
470 FloatManager()->AddFloat(
472 nsFloatManager::GetRegionFor(wm
, floatFrame
, ContainerSize()), wm
,
475 } else if (aLine
->IsBlock()) {
476 nsBlockFrame::RecoverFloatsFor(aLine
->mFirstChild
, *FloatManager(), wm
,
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
);
503 if (nsBlockFrame::gNoisyReflow
|| nsBlockFrame::gNoisyFloatManager
) {
504 FloatManager()->List(stdout
);
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.
562 FloatManager()->GetTranslation(oI
, oB
);
563 nscoord dI
= oI
- mFloatManagerI
;
564 nscoord dB
= oB
- mFloatManagerB
;
565 FloatManager()->Translate(-dI
, -dB
);
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
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
) {
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();
602 MOZ_ASSERT(result
== PlaceFloatResult::ShouldPlaceBelowCurrentLine
);
603 shouldPlaceFloatBelowCurrentLine
= true;
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
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
);
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
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()) >=
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
) {
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
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
;
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
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
);
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.
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
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
);
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
831 if (!earlyFloatReflow
) {
832 const LogicalSize oldAvailSize
= availSize
;
833 availSize
= ComputeAvailableSizeForFloat();
834 if (oldAvailSize
!= availSize
) {
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 "
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
,
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
;
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
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())) {
921 std::max(region
.BSize(wm
), ContentBSize() - floatPos
.B(wm
));
923 FloatManager()->AddFloat(aFloat
, region
, wm
, ContainerSize());
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
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
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
);
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
);
960 MOZ_ASSERT(!aFloat
->GetNextInFlow());
964 if (nsBlockFrame::gNoisyFloatManager
) {
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
);
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
991 StyleFloat floatStyle
= aFloat
->StyleDisplay()->mFloat
;
992 if (floatStyle
== StyleFloat::Left
) {
993 FloatManager()->SetPushedLeftFloatPastBreak();
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
) {
1014 if (nsBlockFrame::gNoisyReflow
) {
1015 nsIFrame::IndentBy(stdout
, nsBlockFrame::gNoiseIndent
);
1016 printf("placing bcl 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
) {
1041 if (nsBlockFrame::gNoisyReflow
) {
1042 nsIFrame::IndentBy(stdout
, nsBlockFrame::gNoiseIndent
);
1043 printf("clear floats: in: aBCoord=%d\n", aBCoord
);
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
) {
1063 nsFlowAreaRect floatAvailableSpace
= GetFloatAvailableSpace(newBCoord
);
1064 if (FloatAvoidingBlockFitsInAvailSpace(aFloatAvoidingBlock
,
1065 floatAvailableSpace
)) {
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.
1079 if (nsBlockFrame::gNoisyReflow
) {
1080 nsIFrame::IndentBy(stdout
, nsBlockFrame::gNoiseIndent
);
1081 printf("clear floats: out: y=%d\n", newBCoord
);
1085 ClearFloatsResult result
= newBCoord
== aBCoord
1086 ? ClearFloatsResult::BCoordNoChange
1087 : ClearFloatsResult::BCoordAdvanced
;
1088 return {newBCoord
, result
};