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