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