Coverage Report

Created: 2018-09-25 14:53

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