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