Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/layout/generic/ReflowInput.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
/* struct containing the input to nsIFrame::Reflow */
8
9
#include "mozilla/ReflowInput.h"
10
11
#include "LayoutLogging.h"
12
#include "nsStyleConsts.h"
13
#include "nsCSSAnonBoxes.h"
14
#include "nsFrame.h"
15
#include "nsIContent.h"
16
#include "nsGkAtoms.h"
17
#include "nsPresContext.h"
18
#include "nsIPresShell.h"
19
#include "nsFontMetrics.h"
20
#include "nsBlockFrame.h"
21
#include "nsLineBox.h"
22
#include "nsImageFrame.h"
23
#include "nsTableFrame.h"
24
#include "nsTableCellFrame.h"
25
#include "nsIPercentBSizeObserver.h"
26
#include "nsLayoutUtils.h"
27
#include "mozilla/Preferences.h"
28
#include "nsFontInflationData.h"
29
#include "StickyScrollContainer.h"
30
#include "nsIFrameInlines.h"
31
#include "CounterStyleManager.h"
32
#include <algorithm>
33
#include "mozilla/dom/HTMLInputElement.h"
34
35
#ifdef DEBUG
36
#undef NOISY_VERTICAL_ALIGN
37
#else
38
#undef NOISY_VERTICAL_ALIGN
39
#endif
40
41
using namespace mozilla;
42
using namespace mozilla::css;
43
using namespace mozilla::dom;
44
using namespace mozilla::layout;
45
46
enum eNormalLineHeightControl {
47
  eUninitialized = -1,
48
  eNoExternalLeading = 0,   // does not include external leading
49
  eIncludeExternalLeading,  // use whatever value font vendor provides
50
  eCompensateLeading        // compensate leading if leading provided by font vendor is not enough
51
};
52
53
static eNormalLineHeightControl sNormalLineHeightControl = eUninitialized;
54
55
// Initialize a <b>root</b> reflow state with a rendering context to
56
// use for measuring things.
57
ReflowInput::ReflowInput(nsPresContext*       aPresContext,
58
                                     nsIFrame*            aFrame,
59
                                     gfxContext*          aRenderingContext,
60
                                     const LogicalSize&   aAvailableSpace,
61
                                     uint32_t             aFlags)
62
  : SizeComputationInput(aFrame, aRenderingContext)
63
  , mCBReflowInput(nullptr) // will be setup properly later in InitCBReflowInput
64
  , mBlockDelta(0)
65
  , mOrthogonalLimit(NS_UNCONSTRAINEDSIZE)
66
  , mAvailableWidth(0)
67
  , mAvailableHeight(0)
68
  , mContainingBlockSize(mWritingMode)
69
  , mReflowDepth(0)
70
0
{
71
0
  MOZ_ASSERT(aRenderingContext, "no rendering context");
72
0
  MOZ_ASSERT(aPresContext, "no pres context");
73
0
  MOZ_ASSERT(aFrame, "no frame");
74
0
  MOZ_ASSERT(aPresContext == aFrame->PresContext(), "wrong pres context");
75
0
  mParentReflowInput = nullptr;
76
0
  AvailableISize() = aAvailableSpace.ISize(mWritingMode);
77
0
  AvailableBSize() = aAvailableSpace.BSize(mWritingMode);
78
0
  mFloatManager = nullptr;
79
0
  mLineLayout = nullptr;
80
0
  mDiscoveredClearance = nullptr;
81
0
  mPercentBSizeObserver = nullptr;
82
0
83
0
  if (aFlags & DUMMY_PARENT_REFLOW_STATE) {
84
0
    mFlags.mDummyParentReflowInput = true;
85
0
  }
86
0
  if (aFlags & COMPUTE_SIZE_SHRINK_WRAP) {
87
0
    mFlags.mShrinkWrap = true;
88
0
  }
89
0
  if (aFlags & COMPUTE_SIZE_USE_AUTO_BSIZE) {
90
0
    mFlags.mUseAutoBSize = true;
91
0
  }
92
0
  if (aFlags & STATIC_POS_IS_CB_ORIGIN) {
93
0
    mFlags.mStaticPosIsCBOrigin = true;
94
0
  }
95
0
  if (aFlags & I_CLAMP_MARGIN_BOX_MIN_SIZE) {
96
0
    mFlags.mIClampMarginBoxMinSize = true;
97
0
  }
98
0
  if (aFlags & B_CLAMP_MARGIN_BOX_MIN_SIZE) {
99
0
    mFlags.mBClampMarginBoxMinSize = true;
100
0
  }
101
0
  if (aFlags & I_APPLY_AUTO_MIN_SIZE) {
102
0
    mFlags.mApplyAutoMinSize = true;
103
0
  }
104
0
105
0
  if (!(aFlags & CALLER_WILL_INIT)) {
106
0
    Init(aPresContext);
107
0
  }
108
0
}
109
110
static bool CheckNextInFlowParenthood(nsIFrame* aFrame, nsIFrame* aParent)
111
0
{
112
0
  nsIFrame* frameNext = aFrame->GetNextInFlow();
113
0
  nsIFrame* parentNext = aParent->GetNextInFlow();
114
0
  return frameNext && parentNext && frameNext->GetParent() == parentNext;
115
0
}
116
117
/**
118
 * Adjusts the margin for a list (ol, ul), if necessary, depending on
119
 * font inflation settings. Unfortunately, because bullets from a list are
120
 * placed in the margin area, we only have ~40px in which to place the
121
 * bullets. When they are inflated, however, this causes problems, since
122
 * the text takes up more space than is available in the margin.
123
 *
124
 * This method will return a small amount (in app units) by which the
125
 * margin can be adjusted, so that the space is available for list
126
 * bullets to be rendered with font inflation enabled.
127
 */
128
static  nscoord
129
FontSizeInflationListMarginAdjustment(const nsIFrame* aFrame)
130
0
{
131
0
  float inflation = nsLayoutUtils::FontSizeInflationFor(aFrame);
132
0
  if (aFrame->IsFrameOfType(nsIFrame::eBlockFrame)) {
133
0
    const nsBlockFrame* blockFrame = static_cast<const nsBlockFrame*>(aFrame);
134
0
135
0
    // We only want to adjust the margins if we're dealing with an ordered
136
0
    // list.
137
0
    if (inflation > 1.0f &&
138
0
        blockFrame->HasBullet() &&
139
0
        inflation > 1.0f) {
140
0
141
0
      auto listStyleType = aFrame->StyleList()->mCounterStyle->GetStyle();
142
0
      if (listStyleType != NS_STYLE_LIST_STYLE_NONE &&
143
0
          listStyleType != NS_STYLE_LIST_STYLE_DISC &&
144
0
          listStyleType != NS_STYLE_LIST_STYLE_CIRCLE &&
145
0
          listStyleType != NS_STYLE_LIST_STYLE_SQUARE &&
146
0
          listStyleType != NS_STYLE_LIST_STYLE_DISCLOSURE_CLOSED &&
147
0
          listStyleType != NS_STYLE_LIST_STYLE_DISCLOSURE_OPEN) {
148
0
        // The HTML spec states that the default padding for ordered lists
149
0
        // begins at 40px, indicating that we have 40px of space to place a
150
0
        // bullet. When performing font inflation calculations, we add space
151
0
        // equivalent to this, but simply inflated at the same amount as the
152
0
        // text, in app units.
153
0
        return nsPresContext::CSSPixelsToAppUnits(40) * (inflation - 1);
154
0
      }
155
0
156
0
    }
157
0
  }
158
0
159
0
  return 0;
160
0
}
161
162
SizeComputationInput::SizeComputationInput(nsIFrame *aFrame,
163
                                   gfxContext *aRenderingContext,
164
                                   WritingMode aContainingBlockWritingMode,
165
                                   nscoord aContainingBlockISize)
166
  : mFrame(aFrame)
167
  , mRenderingContext(aRenderingContext)
168
  , mWritingMode(aFrame->GetWritingMode())
169
0
{
170
0
  ReflowInputFlags flags;
171
0
  InitOffsets(aContainingBlockWritingMode, aContainingBlockISize,
172
0
              mFrame->Type(), flags);
173
0
}
174
175
// Initialize a reflow state for a child frame's reflow. Some state
176
// is copied from the parent reflow state; the remaining state is
177
// computed.
178
ReflowInput::ReflowInput(
179
                     nsPresContext*           aPresContext,
180
                     const ReflowInput& aParentReflowInput,
181
                     nsIFrame*                aFrame,
182
                     const LogicalSize&       aAvailableSpace,
183
                     const LogicalSize*       aContainingBlockSize,
184
                     uint32_t                 aFlags)
185
  : SizeComputationInput(aFrame, aParentReflowInput.mRenderingContext)
186
  , mCBReflowInput(nullptr) // will be setup properly later in InitCBReflowInput
187
  , mBlockDelta(0)
188
  , mOrthogonalLimit(NS_UNCONSTRAINEDSIZE)
189
  , mAvailableWidth(0)
190
  , mAvailableHeight(0)
191
  , mContainingBlockSize(mWritingMode)
192
  , mFlags(aParentReflowInput.mFlags)
193
  , mReflowDepth(aParentReflowInput.mReflowDepth + 1)
194
0
{
195
0
  MOZ_ASSERT(aPresContext, "no pres context");
196
0
  MOZ_ASSERT(aFrame, "no frame");
197
0
  MOZ_ASSERT(aPresContext == aFrame->PresContext(), "wrong pres context");
198
0
  MOZ_ASSERT(!mFlags.mSpecialBSizeReflow ||
199
0
                  !NS_SUBTREE_DIRTY(aFrame),
200
0
             "frame should be clean when getting special bsize reflow");
201
0
202
0
  mParentReflowInput = &aParentReflowInput;
203
0
204
0
  AvailableISize() = aAvailableSpace.ISize(mWritingMode);
205
0
  AvailableBSize() = aAvailableSpace.BSize(mWritingMode);
206
0
207
0
  if (mWritingMode.IsOrthogonalTo(aParentReflowInput.GetWritingMode())) {
208
0
    // If we're setting up for an orthogonal flow, and the parent reflow state
209
0
    // had a constrained ComputedBSize, we can use that as our AvailableISize
210
0
    // in preference to leaving it unconstrained.
211
0
    if (AvailableISize() == NS_UNCONSTRAINEDSIZE &&
212
0
        aParentReflowInput.ComputedBSize() != NS_UNCONSTRAINEDSIZE) {
213
0
      AvailableISize() = aParentReflowInput.ComputedBSize();
214
0
    }
215
0
  }
216
0
217
0
  mFloatManager = aParentReflowInput.mFloatManager;
218
0
  if (mFrame->IsFrameOfType(nsIFrame::eLineParticipant))
219
0
    mLineLayout = aParentReflowInput.mLineLayout;
220
0
  else
221
0
    mLineLayout = nullptr;
222
0
223
0
  // Note: mFlags was initialized as a copy of aParentReflowInput.mFlags up in
224
0
  // this constructor's init list, so the only flags that we need to explicitly
225
0
  // initialize here are those that may need a value other than our parent's.
226
0
  mFlags.mNextInFlowUntouched = aParentReflowInput.mFlags.mNextInFlowUntouched &&
227
0
    CheckNextInFlowParenthood(aFrame, aParentReflowInput.mFrame);
228
0
  mFlags.mAssumingHScrollbar = mFlags.mAssumingVScrollbar = false;
229
0
  mFlags.mIsColumnBalancing = false;
230
0
  mFlags.mIsFlexContainerMeasuringBSize = false;
231
0
  mFlags.mDummyParentReflowInput = false;
232
0
  mFlags.mShrinkWrap = !!(aFlags & COMPUTE_SIZE_SHRINK_WRAP);
233
0
  mFlags.mUseAutoBSize = !!(aFlags & COMPUTE_SIZE_USE_AUTO_BSIZE);
234
0
  mFlags.mStaticPosIsCBOrigin = !!(aFlags & STATIC_POS_IS_CB_ORIGIN);
235
0
  mFlags.mIOffsetsNeedCSSAlign = mFlags.mBOffsetsNeedCSSAlign = false;
236
0
  mFlags.mIClampMarginBoxMinSize = !!(aFlags & I_CLAMP_MARGIN_BOX_MIN_SIZE);
237
0
  mFlags.mBClampMarginBoxMinSize = !!(aFlags & B_CLAMP_MARGIN_BOX_MIN_SIZE);
238
0
  mFlags.mApplyAutoMinSize = !!(aFlags & I_APPLY_AUTO_MIN_SIZE);
239
0
240
0
  mDiscoveredClearance = nullptr;
241
0
  mPercentBSizeObserver = (aParentReflowInput.mPercentBSizeObserver &&
242
0
                            aParentReflowInput.mPercentBSizeObserver->NeedsToObserve(*this))
243
0
                           ? aParentReflowInput.mPercentBSizeObserver : nullptr;
244
0
245
0
  if ((aFlags & DUMMY_PARENT_REFLOW_STATE) ||
246
0
      (mParentReflowInput->mFlags.mDummyParentReflowInput &&
247
0
       mFrame->IsTableFrame())) {
248
0
    mFlags.mDummyParentReflowInput = true;
249
0
  }
250
0
251
0
  if (!(aFlags & CALLER_WILL_INIT)) {
252
0
    Init(aPresContext, aContainingBlockSize);
253
0
  }
254
0
}
255
256
inline nscoord
257
SizeComputationInput::ComputeISizeValue(nscoord aContainingBlockISize,
258
                                    nscoord aContentEdgeToBoxSizing,
259
                                    nscoord aBoxSizingToMarginEdge,
260
                                    const nsStyleCoord& aCoord) const
261
0
{
262
0
  return mFrame->ComputeISizeValue(mRenderingContext,
263
0
                                   aContainingBlockISize,
264
0
                                   aContentEdgeToBoxSizing,
265
0
                                   aBoxSizingToMarginEdge,
266
0
                                   aCoord);
267
0
}
268
269
nscoord
270
SizeComputationInput::ComputeISizeValue(nscoord aContainingBlockISize,
271
                                    StyleBoxSizing aBoxSizing,
272
                                    const nsStyleCoord& aCoord) const
273
0
{
274
0
  WritingMode wm = GetWritingMode();
275
0
  nscoord inside = 0, outside = ComputedLogicalBorderPadding().IStartEnd(wm) +
276
0
                                ComputedLogicalMargin().IStartEnd(wm);
277
0
  if (aBoxSizing == StyleBoxSizing::Border) {
278
0
    inside = ComputedLogicalBorderPadding().IStartEnd(wm);
279
0
  }
280
0
  outside -= inside;
281
0
282
0
  return ComputeISizeValue(aContainingBlockISize, inside,
283
0
                           outside, aCoord);
284
0
}
285
286
nscoord
287
SizeComputationInput::ComputeBSizeValue(nscoord aContainingBlockBSize,
288
                                    StyleBoxSizing aBoxSizing,
289
                                    const nsStyleCoord& aCoord) const
290
0
{
291
0
  WritingMode wm = GetWritingMode();
292
0
  nscoord inside = 0;
293
0
  if (aBoxSizing == StyleBoxSizing::Border) {
294
0
    inside = ComputedLogicalBorderPadding().BStartEnd(wm);
295
0
  }
296
0
  return nsLayoutUtils::ComputeBSizeValue(aContainingBlockBSize,
297
0
                                          inside, aCoord);
298
0
}
299
300
void
301
ReflowInput::SetComputedWidth(nscoord aComputedWidth)
302
0
{
303
0
  NS_ASSERTION(mFrame, "Must have a frame!");
304
0
  // It'd be nice to assert that |frame| is not in reflow, but this fails for
305
0
  // two reasons:
306
0
  //
307
0
  // 1) Viewport frames reset the computed width on a copy of their reflow
308
0
  //    state when reflowing fixed-pos kids.  In that case we actually don't
309
0
  //    want to mess with the resize flags, because comparing the frame's rect
310
0
  //    to the munged computed width is pointless.
311
0
  // 2) nsFrame::BoxReflow creates a reflow state for its parent.  This reflow
312
0
  //    state is not used to reflow the parent, but just as a parent for the
313
0
  //    frame's own reflow state.  So given a nsBoxFrame inside some non-XUL
314
0
  //    (like a text control, for example), we'll end up creating a reflow
315
0
  //    state for the parent while the parent is reflowing.
316
0
317
0
  MOZ_ASSERT(aComputedWidth >= 0, "Invalid computed width");
318
0
  if (ComputedWidth() != aComputedWidth) {
319
0
    ComputedWidth() = aComputedWidth;
320
0
    LayoutFrameType frameType = mFrame->Type();
321
0
    if (frameType != LayoutFrameType::Viewport || // Or check GetParent()?
322
0
        mWritingMode.IsVertical()) {
323
0
      InitResizeFlags(mFrame->PresContext(), frameType);
324
0
    }
325
0
  }
326
0
}
327
328
void
329
ReflowInput::SetComputedHeight(nscoord aComputedHeight)
330
0
{
331
0
  NS_ASSERTION(mFrame, "Must have a frame!");
332
0
  // It'd be nice to assert that |frame| is not in reflow, but this fails
333
0
  // because:
334
0
  //
335
0
  //    nsFrame::BoxReflow creates a reflow state for its parent.  This reflow
336
0
  //    state is not used to reflow the parent, but just as a parent for the
337
0
  //    frame's own reflow state.  So given a nsBoxFrame inside some non-XUL
338
0
  //    (like a text control, for example), we'll end up creating a reflow
339
0
  //    state for the parent while the parent is reflowing.
340
0
341
0
  MOZ_ASSERT(aComputedHeight >= 0, "Invalid computed height");
342
0
  if (ComputedHeight() != aComputedHeight) {
343
0
    ComputedHeight() = aComputedHeight;
344
0
    LayoutFrameType frameType = mFrame->Type();
345
0
    if (frameType != LayoutFrameType::Viewport || !mWritingMode.IsVertical()) {
346
0
      InitResizeFlags(mFrame->PresContext(), frameType);
347
0
    }
348
0
  }
349
0
}
350
351
void
352
ReflowInput::Init(nsPresContext*     aPresContext,
353
                        const LogicalSize* aContainingBlockSize,
354
                        const nsMargin*    aBorder,
355
                        const nsMargin*    aPadding)
356
0
{
357
0
  if ((mFrame->GetStateBits() & NS_FRAME_IS_DIRTY) &&
358
0
      !mFrame->IsXULBoxFrame()) {
359
0
    // Mark all child frames as dirty.
360
0
    //
361
0
    // We don't do this for XUL boxes because they handle their child
362
0
    // reflow separately.
363
0
    //
364
0
    // FIXME (bug 1376530): It would be better for memory locality if we
365
0
    // did this as we went.  However, we need to be careful not to do
366
0
    // this twice for any particular child if we reflow it twice.  The
367
0
    // easiest way to accomplish that is to do it at the start.
368
0
    for (nsIFrame::ChildListIterator childLists(mFrame);
369
0
         !childLists.IsDone(); childLists.Next()) {
370
0
      for (nsIFrame* childFrame : childLists.CurrentList()) {
371
0
        if (!childFrame->IsTableColGroupFrame()) {
372
0
          childFrame->AddStateBits(NS_FRAME_IS_DIRTY);
373
0
        }
374
0
      }
375
0
    }
376
0
  }
377
0
378
0
  if (AvailableISize() == NS_UNCONSTRAINEDSIZE) {
379
0
    // Look up the parent chain for an orthogonal inline limit,
380
0
    // and reset AvailableISize() if found.
381
0
    for (const ReflowInput *parent = mParentReflowInput;
382
0
         parent != nullptr; parent = parent->mParentReflowInput) {
383
0
      if (parent->GetWritingMode().IsOrthogonalTo(mWritingMode) &&
384
0
          parent->mOrthogonalLimit != NS_UNCONSTRAINEDSIZE) {
385
0
        AvailableISize() = parent->mOrthogonalLimit;
386
0
        break;
387
0
      }
388
0
    }
389
0
  }
390
0
391
0
  LAYOUT_WARN_IF_FALSE(AvailableISize() != NS_UNCONSTRAINEDSIZE,
392
0
                       "have unconstrained inline-size; this should only "
393
0
                       "result from very large sizes, not attempts at "
394
0
                       "intrinsic inline-size calculation");
395
0
396
0
  mStylePosition = mFrame->StylePosition();
397
0
  mStyleDisplay = mFrame->StyleDisplay();
398
0
  mStyleVisibility = mFrame->StyleVisibility();
399
0
  mStyleBorder = mFrame->StyleBorder();
400
0
  mStyleMargin = mFrame->StyleMargin();
401
0
  mStylePadding = mFrame->StylePadding();
402
0
  mStyleText = mFrame->StyleText();
403
0
404
0
  InitCBReflowInput();
405
0
406
0
  LayoutFrameType type = mFrame->Type();
407
0
  if (type == mozilla::LayoutFrameType::Placeholder) {
408
0
    // Placeholders have a no-op Reflow method that doesn't need the rest of
409
0
    // this initialization, so we bail out early.
410
0
    ComputedBSize() = ComputedISize() = 0;
411
0
    return;
412
0
  }
413
0
414
0
  InitFrameType(type);
415
0
416
0
  LogicalSize cbSize(mWritingMode, -1, -1);
417
0
  if (aContainingBlockSize) {
418
0
    cbSize = *aContainingBlockSize;
419
0
  }
420
0
421
0
  InitConstraints(aPresContext, cbSize, aBorder, aPadding, type);
422
0
423
0
  InitResizeFlags(aPresContext, type);
424
0
425
0
  nsIFrame *parent = mFrame->GetParent();
426
0
  if (parent &&
427
0
      (parent->GetStateBits() & NS_FRAME_IN_CONSTRAINED_BSIZE) &&
428
0
      !(parent->IsScrollFrame() &&
429
0
        parent->StyleDisplay()->mOverflowY != NS_STYLE_OVERFLOW_HIDDEN)) {
430
0
    mFrame->AddStateBits(NS_FRAME_IN_CONSTRAINED_BSIZE);
431
0
  } else if (type == LayoutFrameType::SVGForeignObject) {
432
0
    // An SVG foreignObject frame is inherently constrained block-size.
433
0
    mFrame->AddStateBits(NS_FRAME_IN_CONSTRAINED_BSIZE);
434
0
  } else {
435
0
    const nsStyleCoord& bSizeCoord = mStylePosition->BSize(mWritingMode);
436
0
    const nsStyleCoord& maxBSizeCoord = mStylePosition->MaxBSize(mWritingMode);
437
0
    if ((bSizeCoord.GetUnit() != eStyleUnit_Auto ||
438
0
         maxBSizeCoord.GetUnit() != eStyleUnit_None) &&
439
0
         // Don't set NS_FRAME_IN_CONSTRAINED_BSIZE on body or html elements.
440
0
         (mFrame->GetContent() &&
441
0
        !(mFrame->GetContent()->IsAnyOfHTMLElements(nsGkAtoms::body,
442
0
                                                   nsGkAtoms::html)))) {
443
0
444
0
      // If our block-size was specified as a percentage, then this could
445
0
      // actually resolve to 'auto', based on:
446
0
      // http://www.w3.org/TR/CSS21/visudet.html#the-height-property
447
0
      nsIFrame* containingBlk = mFrame;
448
0
      while (containingBlk) {
449
0
        const nsStylePosition* stylePos = containingBlk->StylePosition();
450
0
        const nsStyleCoord& bSizeCoord = stylePos->BSize(mWritingMode);
451
0
        const nsStyleCoord& maxBSizeCoord = stylePos->MaxBSize(mWritingMode);
452
0
        if ((bSizeCoord.IsCoordPercentCalcUnit() &&
453
0
             !bSizeCoord.HasPercent()) ||
454
0
            (maxBSizeCoord.IsCoordPercentCalcUnit() &&
455
0
             !maxBSizeCoord.HasPercent())) {
456
0
          mFrame->AddStateBits(NS_FRAME_IN_CONSTRAINED_BSIZE);
457
0
          break;
458
0
        } else if ((bSizeCoord.IsCoordPercentCalcUnit() &&
459
0
                    bSizeCoord.HasPercent()) ||
460
0
                   (maxBSizeCoord.IsCoordPercentCalcUnit() &&
461
0
                    maxBSizeCoord.HasPercent())) {
462
0
          if (!(containingBlk = containingBlk->GetContainingBlock())) {
463
0
            // If we've reached the top of the tree, then we don't have
464
0
            // a constrained block-size.
465
0
            mFrame->RemoveStateBits(NS_FRAME_IN_CONSTRAINED_BSIZE);
466
0
            break;
467
0
          }
468
0
469
0
          continue;
470
0
        } else {
471
0
          mFrame->RemoveStateBits(NS_FRAME_IN_CONSTRAINED_BSIZE);
472
0
          break;
473
0
        }
474
0
      }
475
0
    } else {
476
0
      mFrame->RemoveStateBits(NS_FRAME_IN_CONSTRAINED_BSIZE);
477
0
    }
478
0
  }
479
0
480
0
  if (mParentReflowInput &&
481
0
      mParentReflowInput->GetWritingMode().IsOrthogonalTo(mWritingMode)) {
482
0
    // Orthogonal frames are always reflowed with an unconstrained
483
0
    // dimension to avoid incomplete reflow across an orthogonal
484
0
    // boundary. Normally this is the block-size, but for column sets
485
0
    // with auto-height it's the inline-size, so that they can add
486
0
    // columns in the container's block direction
487
0
    if (type == LayoutFrameType::ColumnSet &&
488
0
        eStyleUnit_Auto == mStylePosition->ISize(mWritingMode).GetUnit()) {
489
0
      ComputedISize() = NS_UNCONSTRAINEDSIZE;
490
0
    } else {
491
0
      AvailableBSize() = NS_UNCONSTRAINEDSIZE;
492
0
    }
493
0
  }
494
0
495
0
  if (mStyleDisplay->IsContainSize()) {
496
0
    // In the case that a box is size contained, we want to ensure
497
0
    // that it is also monolithic. We do this by unsetting
498
0
    // AvailableBSize() to avoid fragmentaiton.
499
0
    AvailableBSize() = NS_UNCONSTRAINEDSIZE;
500
0
  }
501
0
502
0
  LAYOUT_WARN_IF_FALSE((mFrameType == NS_CSS_FRAME_TYPE_INLINE &&
503
0
                        !mFrame->IsFrameOfType(nsIFrame::eReplaced)) ||
504
0
                       type == LayoutFrameType::Text ||
505
0
                       ComputedISize() != NS_UNCONSTRAINEDSIZE,
506
0
                       "have unconstrained inline-size; this should only "
507
0
                       "result from very large sizes, not attempts at "
508
0
                       "intrinsic inline-size calculation");
509
0
}
510
511
void ReflowInput::InitCBReflowInput()
512
0
{
513
0
  if (!mParentReflowInput) {
514
0
    mCBReflowInput = nullptr;
515
0
    return;
516
0
  }
517
0
  if (mParentReflowInput->mFlags.mDummyParentReflowInput) {
518
0
    mCBReflowInput = mParentReflowInput;
519
0
    return;
520
0
  }
521
0
522
0
  if (mParentReflowInput->mFrame == mFrame->GetContainingBlock(0, mStyleDisplay)) {
523
0
    // Inner table frames need to use the containing block of the outer
524
0
    // table frame.
525
0
    if (mFrame->IsTableFrame()) {
526
0
      mCBReflowInput = mParentReflowInput->mCBReflowInput;
527
0
    } else {
528
0
      mCBReflowInput = mParentReflowInput;
529
0
    }
530
0
  } else {
531
0
    mCBReflowInput = mParentReflowInput->mCBReflowInput;
532
0
  }
533
0
}
534
535
/* Check whether CalcQuirkContainingBlockHeight would stop on the
536
 * given reflow state, using its block as a height.  (essentially
537
 * returns false for any case in which CalcQuirkContainingBlockHeight
538
 * has a "continue" in its main loop.)
539
 *
540
 * XXX Maybe refactor CalcQuirkContainingBlockHeight so it uses
541
 * this function as well
542
 */
543
static bool
544
IsQuirkContainingBlockHeight(const ReflowInput* rs, LayoutFrameType aFrameType)
545
0
{
546
0
  if (LayoutFrameType::Block == aFrameType ||
547
0
#ifdef MOZ_XUL
548
0
      LayoutFrameType::XULLabel == aFrameType ||
549
0
#endif
550
0
      LayoutFrameType::Scroll == aFrameType) {
551
0
    // Note: This next condition could change due to a style change,
552
0
    // but that would cause a style reflow anyway, which means we're ok.
553
0
    if (NS_AUTOHEIGHT == rs->ComputedHeight()) {
554
0
      if (!rs->mFrame->IsAbsolutelyPositioned(rs->mStyleDisplay)) {
555
0
        return false;
556
0
      }
557
0
    }
558
0
  }
559
0
  return true;
560
0
}
561
562
void
563
ReflowInput::InitResizeFlags(nsPresContext* aPresContext,
564
                             LayoutFrameType aFrameType)
565
0
{
566
0
  SetBResize(false);
567
0
  SetIResize(false);
568
0
569
0
  const WritingMode wm = mWritingMode; // just a shorthand
570
0
  // We should report that we have a resize in the inline dimension if
571
0
  // *either* the border-box size or the content-box size in that
572
0
  // dimension has changed.  It might not actually be necessary to do
573
0
  // this if the border-box size has changed and the content-box size
574
0
  // has not changed, but since we've historically used the flag to mean
575
0
  // border-box size change, continue to do that.  (It's possible for
576
0
  // the content-box size to change without a border-box size change or
577
0
  // a style change given (1) a fixed width (possibly fixed by max-width
578
0
  // or min-width), (2) box-sizing:border-box or padding-box, and
579
0
  // (3) percentage padding.)
580
0
  //
581
0
  // However, we don't actually have the information at this point to
582
0
  // tell whether the content-box size has changed, since both style
583
0
  // data and the UsedPaddingProperty() have already been updated.  So,
584
0
  // instead, we explicitly check for the case where it's possible for
585
0
  // the content-box size to have changed without either (a) a change in
586
0
  // the border-box size or (b) an nsChangeHint_NeedDirtyReflow change
587
0
  // hint due to change in border or padding.  Thus we test using the
588
0
  // conditions from the previous paragraph, except without testing (1)
589
0
  // since it's complicated to test properly and less likely to help
590
0
  // with optimizing cases away.
591
0
  bool isIResize =
592
0
    // is the border-box resizing?
593
0
    mFrame->ISize(wm) !=
594
0
      ComputedISize() + ComputedLogicalBorderPadding().IStartEnd(wm) ||
595
0
    // or is the content-box resizing?  (see comment above)
596
0
    (mStylePosition->mBoxSizing != StyleBoxSizing::Content &&
597
0
     mStylePadding->IsWidthDependent());
598
0
599
0
  if ((mFrame->GetStateBits() & NS_FRAME_FONT_INFLATION_FLOW_ROOT) &&
600
0
      nsLayoutUtils::FontSizeInflationEnabled(aPresContext)) {
601
0
    // Create our font inflation data if we don't have it already, and
602
0
    // give it our current width information.
603
0
    bool dirty = nsFontInflationData::UpdateFontInflationDataISizeFor(*this) &&
604
0
                 // Avoid running this at the box-to-block interface
605
0
                 // (where we shouldn't be inflating anyway, and where
606
0
                 // reflow state construction is probably to construct a
607
0
                 // dummy parent reflow state anyway).
608
0
                 !mFlags.mDummyParentReflowInput;
609
0
610
0
    if (dirty || (!mFrame->GetParent() && isIResize)) {
611
0
      // When font size inflation is enabled, a change in either:
612
0
      //  * the effective width of a font inflation flow root
613
0
      //  * the width of the frame
614
0
      // needs to cause a dirty reflow since they change the font size
615
0
      // inflation calculations, which in turn change the size of text,
616
0
      // line-heights, etc.  This is relatively similar to a classic
617
0
      // case of style change reflow, except that because inflation
618
0
      // doesn't affect the intrinsic sizing codepath, there's no need
619
0
      // to invalidate intrinsic sizes.
620
0
      //
621
0
      // Note that this makes horizontal resizing a good bit more
622
0
      // expensive.  However, font size inflation is targeted at a set of
623
0
      // devices (zoom-and-pan devices) where the main use case for
624
0
      // horizontal resizing needing to be efficient (window resizing) is
625
0
      // not present.  It does still increase the cost of dynamic changes
626
0
      // caused by script where a style or content change in one place
627
0
      // causes a resize in another (e.g., rebalancing a table).
628
0
629
0
      // FIXME: This isn't so great for the cases where
630
0
      // ReflowInput::SetComputedWidth is called, if the first time
631
0
      // we go through InitResizeFlags we set IsHResize() to true, and then
632
0
      // the second time we'd set it to false even without the
633
0
      // NS_FRAME_IS_DIRTY bit already set.
634
0
      if (mFrame->IsSVGForeignObjectFrame()) {
635
0
        // Foreign object frames use dirty bits in a special way.
636
0
        mFrame->AddStateBits(NS_FRAME_HAS_DIRTY_CHILDREN);
637
0
        nsIFrame *kid = mFrame->PrincipalChildList().FirstChild();
638
0
        if (kid) {
639
0
          kid->AddStateBits(NS_FRAME_IS_DIRTY);
640
0
        }
641
0
      } else {
642
0
        mFrame->AddStateBits(NS_FRAME_IS_DIRTY);
643
0
      }
644
0
645
0
      // Mark intrinsic widths on all descendants dirty.  We need to do
646
0
      // this (1) since we're changing the size of text and need to
647
0
      // clear text runs on text frames and (2) since we actually are
648
0
      // changing some intrinsic widths, but only those that live inside
649
0
      // of containers.
650
0
651
0
      // It makes sense to do this for descendants but not ancestors
652
0
      // (which is unusual) because we're only changing the unusual
653
0
      // inflation-dependent intrinsic widths (i.e., ones computed with
654
0
      // nsPresContext::mInflationDisabledForShrinkWrap set to false),
655
0
      // which should never affect anything outside of their inflation
656
0
      // flow root (or, for that matter, even their inflation
657
0
      // container).
658
0
659
0
      // This is also different from what PresShell::FrameNeedsReflow
660
0
      // does because it doesn't go through placeholders.  It doesn't
661
0
      // need to because we're actually doing something that cares about
662
0
      // frame tree geometry (the width on an ancestor) rather than
663
0
      // style.
664
0
665
0
      AutoTArray<nsIFrame*, 32> stack;
666
0
      stack.AppendElement(mFrame);
667
0
668
0
      do {
669
0
        nsIFrame *f = stack.PopLastElement();
670
0
671
0
        nsIFrame::ChildListIterator lists(f);
672
0
        for (; !lists.IsDone(); lists.Next()) {
673
0
          nsFrameList::Enumerator childFrames(lists.CurrentList());
674
0
          for (; !childFrames.AtEnd(); childFrames.Next()) {
675
0
            nsIFrame* kid = childFrames.get();
676
0
            kid->MarkIntrinsicISizesDirty();
677
0
            stack.AppendElement(kid);
678
0
          }
679
0
        }
680
0
      } while (stack.Length() != 0);
681
0
    }
682
0
  }
683
0
684
0
  SetIResize(!(mFrame->GetStateBits() & NS_FRAME_IS_DIRTY) &&
685
0
             isIResize);
686
0
687
0
  // XXX Should we really need to null check mCBReflowInput?  (We do for
688
0
  // at least nsBoxFrame).
689
0
  if (IsTableCell(aFrameType) &&
690
0
      (mFlags.mSpecialBSizeReflow ||
691
0
       (mFrame->FirstInFlow()->GetStateBits() &
692
0
         NS_TABLE_CELL_HAD_SPECIAL_REFLOW)) &&
693
0
      (mFrame->GetStateBits() & NS_FRAME_CONTAINS_RELATIVE_BSIZE)) {
694
0
    // Need to set the bit on the cell so that
695
0
    // mCBReflowInput->IsBResize() is set correctly below when
696
0
    // reflowing descendant.
697
0
    SetBResize(true);
698
0
  } else if (mCBReflowInput && mFrame->IsBlockWrapper()) {
699
0
    // XXX Is this problematic for relatively positioned inlines acting
700
0
    // as containing block for absolutely positioned elements?
701
0
    // Possibly; in that case we should at least be checking
702
0
    // NS_SUBTREE_DIRTY, I'd think.
703
0
    SetBResize(mCBReflowInput->IsBResizeForWM(wm));
704
0
  } else if (mCBReflowInput && !nsLayoutUtils::GetAsBlock(mFrame)) {
705
0
    // Some non-block frames (e.g. table frames) aggressively optimize out their
706
0
    // BSize recomputation when they don't have the BResize flag set.  This
707
0
    // means that if they go from having a computed non-auto height to having an
708
0
    // auto height and don't have that flag set, they will not actually compute
709
0
    // their auto height and will just remain at whatever size they already
710
0
    // were.  We can end up in that situation if the child has a percentage
711
0
    // specified height and the parent changes from non-auto height to auto
712
0
    // height.  When that happens, the parent will typically have the BResize
713
0
    // flag set, and we want to propagate that flag to the kid.
714
0
    //
715
0
    // Ideally it seems like we'd do this for blocks too, of course... but we'd
716
0
    // really want to restrict it to the percentage height case or something, to
717
0
    // avoid extra reflows in common cases.  Maybe we should be examining
718
0
    // mStylePosition->BSize(wm).GetUnit() for that purpose?
719
0
    //
720
0
    // Note that we _also_ need to set the BResize flag if we have auto
721
0
    // ComputedBSize() and a dirty subtree, since that might require us to
722
0
    // change BSize due to kids having been added or removed.
723
0
    SetBResize(mCBReflowInput->IsBResizeForWM(wm));
724
0
    if (ComputedBSize() == NS_AUTOHEIGHT) {
725
0
      SetBResize(IsBResize() || NS_SUBTREE_DIRTY(mFrame));
726
0
    }
727
0
  } else if (ComputedBSize() == NS_AUTOHEIGHT) {
728
0
    if (eCompatibility_NavQuirks == aPresContext->CompatibilityMode() &&
729
0
        mCBReflowInput) {
730
0
      SetBResize(mCBReflowInput->IsBResizeForWM(wm));
731
0
    } else {
732
0
      SetBResize(IsIResize());
733
0
    }
734
0
    SetBResize(IsBResize() || NS_SUBTREE_DIRTY(mFrame));
735
0
  } else {
736
0
    // not 'auto' block-size
737
0
    SetBResize(mFrame->BSize(wm) !=
738
0
               ComputedBSize() + ComputedLogicalBorderPadding().BStartEnd(wm));
739
0
  }
740
0
741
0
  bool dependsOnCBBSize =
742
0
    (mStylePosition->BSizeDependsOnContainer(wm) &&
743
0
     // FIXME: condition this on not-abspos?
744
0
     mStylePosition->BSize(wm).GetUnit() != eStyleUnit_Auto) ||
745
0
    mStylePosition->MinBSizeDependsOnContainer(wm) ||
746
0
    mStylePosition->MaxBSizeDependsOnContainer(wm) ||
747
0
    mStylePosition->OffsetHasPercent(wm.PhysicalSide(eLogicalSideBStart)) ||
748
0
    mStylePosition->mOffset.GetBEndUnit(wm) != eStyleUnit_Auto ||
749
0
    mFrame->IsXULBoxFrame();
750
0
751
0
  if (mStyleText->mLineHeight.GetUnit() == eStyleUnit_Enumerated) {
752
0
    NS_ASSERTION(mStyleText->mLineHeight.GetIntValue() ==
753
0
                 NS_STYLE_LINE_HEIGHT_BLOCK_HEIGHT,
754
0
                 "bad line-height value");
755
0
756
0
    // line-height depends on block bsize
757
0
    mFrame->AddStateBits(NS_FRAME_CONTAINS_RELATIVE_BSIZE);
758
0
    // but only on containing blocks if this frame is not a suitable block
759
0
    dependsOnCBBSize |= !nsLayoutUtils::IsNonWrapperBlock(mFrame);
760
0
  }
761
0
762
0
  // If we're the descendant of a table cell that performs special bsize
763
0
  // reflows and we could be the child that requires them, always set
764
0
  // the block-axis resize in case this is the first pass before the
765
0
  // special bsize reflow.  However, don't do this if it actually is
766
0
  // the special bsize reflow, since in that case it will already be
767
0
  // set correctly above if we need it set.
768
0
  if (!IsBResize() && mCBReflowInput &&
769
0
      (IsTableCell(mCBReflowInput->mFrame->Type()) ||
770
0
       mCBReflowInput->mFlags.mHeightDependsOnAncestorCell) &&
771
0
      !mCBReflowInput->mFlags.mSpecialBSizeReflow &&
772
0
      dependsOnCBBSize) {
773
0
    SetBResize(true);
774
0
    mFlags.mHeightDependsOnAncestorCell = true;
775
0
  }
776
0
777
0
  // Set NS_FRAME_CONTAINS_RELATIVE_BSIZE if it's needed.
778
0
779
0
  // It would be nice to check that |ComputedBSize != NS_AUTOHEIGHT|
780
0
  // &&ed with the percentage bsize check.  However, this doesn't get
781
0
  // along with table special bsize reflows, since a special bsize
782
0
  // reflow (a quirk that makes such percentage height work on children
783
0
  // of table cells) can cause not just a single percentage height to
784
0
  // become fixed, but an entire descendant chain of percentage height
785
0
  // to become fixed.
786
0
  if (dependsOnCBBSize && mCBReflowInput) {
787
0
    const ReflowInput *rs = this;
788
0
    bool hitCBReflowInput = false;
789
0
    do {
790
0
      rs = rs->mParentReflowInput;
791
0
      if (!rs) {
792
0
        break;
793
0
      }
794
0
795
0
      if (rs->mFrame->GetStateBits() & NS_FRAME_CONTAINS_RELATIVE_BSIZE) {
796
0
        break; // no need to go further
797
0
      }
798
0
      rs->mFrame->AddStateBits(NS_FRAME_CONTAINS_RELATIVE_BSIZE);
799
0
800
0
      // Keep track of whether we've hit the containing block, because
801
0
      // we need to go at least that far.
802
0
      if (rs == mCBReflowInput) {
803
0
        hitCBReflowInput = true;
804
0
      }
805
0
806
0
      // XXX What about orthogonal flows? It doesn't make sense to
807
0
      // keep propagating this bit across an orthogonal boundary,
808
0
      // where the meaning of BSize changes. Bug 1175517.
809
0
    } while (!hitCBReflowInput ||
810
0
             (eCompatibility_NavQuirks == aPresContext->CompatibilityMode() &&
811
0
              !IsQuirkContainingBlockHeight(rs, rs->mFrame->Type())));
812
0
    // Note: We actually don't need to set the
813
0
    // NS_FRAME_CONTAINS_RELATIVE_BSIZE bit for the cases
814
0
    // where we hit the early break statements in
815
0
    // CalcQuirkContainingBlockHeight. But it doesn't hurt
816
0
    // us to set the bit in these cases.
817
0
818
0
  }
819
0
  if (mFrame->GetStateBits() & NS_FRAME_IS_DIRTY) {
820
0
    // If we're reflowing everything, then we'll find out if we need
821
0
    // to re-set this.
822
0
    mFrame->RemoveStateBits(NS_FRAME_CONTAINS_RELATIVE_BSIZE);
823
0
  }
824
0
}
825
826
nscoord
827
ReflowInput::GetContainingBlockContentISize(WritingMode aWritingMode) const
828
0
{
829
0
  if (!mCBReflowInput) {
830
0
    return 0;
831
0
  }
832
0
  return mCBReflowInput->GetWritingMode().IsOrthogonalTo(aWritingMode)
833
0
    ? mCBReflowInput->ComputedBSize()
834
0
    : mCBReflowInput->ComputedISize();
835
0
}
836
837
void
838
ReflowInput::InitFrameType(LayoutFrameType aFrameType)
839
0
{
840
0
  const nsStyleDisplay *disp = mStyleDisplay;
841
0
  nsCSSFrameType frameType;
842
0
843
0
  DISPLAY_INIT_TYPE(mFrame, this);
844
0
845
0
  if (aFrameType == LayoutFrameType::Table) {
846
0
    mFrameType = NS_CSS_FRAME_TYPE_BLOCK;
847
0
    return;
848
0
  }
849
0
850
0
  NS_ASSERTION(mFrame->StyleDisplay()->IsAbsolutelyPositionedStyle() ==
851
0
                 disp->IsAbsolutelyPositionedStyle(),
852
0
               "Unexpected position style");
853
0
  NS_ASSERTION(mFrame->StyleDisplay()->IsFloatingStyle() ==
854
0
                 disp->IsFloatingStyle(), "Unexpected float style");
855
0
  if (mFrame->GetStateBits() & NS_FRAME_OUT_OF_FLOW) {
856
0
    if (disp->IsAbsolutelyPositioned(mFrame)) {
857
0
      frameType = NS_CSS_FRAME_TYPE_ABSOLUTE;
858
0
      //XXXfr hack for making frames behave properly when in overflow container lists
859
0
      //      see bug 154892; need to revisit later
860
0
      if (mFrame->GetPrevInFlow())
861
0
        frameType = NS_CSS_FRAME_TYPE_BLOCK;
862
0
    }
863
0
    else if (disp->IsFloating(mFrame)) {
864
0
      frameType = NS_CSS_FRAME_TYPE_FLOATING;
865
0
    } else {
866
0
      NS_ASSERTION(disp->mDisplay == StyleDisplay::MozPopup,
867
0
                   "unknown out of flow frame type");
868
0
      frameType = NS_CSS_FRAME_TYPE_UNKNOWN;
869
0
    }
870
0
  }
871
0
  else {
872
0
    switch (GetDisplay()) {
873
0
    case StyleDisplay::Block:
874
0
    case StyleDisplay::ListItem:
875
0
    case StyleDisplay::Table:
876
0
    case StyleDisplay::TableCaption:
877
0
    case StyleDisplay::Flex:
878
0
    case StyleDisplay::WebkitBox:
879
0
    case StyleDisplay::Grid:
880
0
    case StyleDisplay::FlowRoot:
881
0
    case StyleDisplay::RubyTextContainer:
882
0
      frameType = NS_CSS_FRAME_TYPE_BLOCK;
883
0
      break;
884
0
885
0
    case StyleDisplay::Inline:
886
0
    case StyleDisplay::InlineBlock:
887
0
    case StyleDisplay::InlineTable:
888
0
    case StyleDisplay::MozInlineBox:
889
0
    case StyleDisplay::MozInlineGrid:
890
0
    case StyleDisplay::MozInlineStack:
891
0
    case StyleDisplay::InlineFlex:
892
0
    case StyleDisplay::WebkitInlineBox:
893
0
    case StyleDisplay::InlineGrid:
894
0
    case StyleDisplay::Ruby:
895
0
    case StyleDisplay::RubyBase:
896
0
    case StyleDisplay::RubyText:
897
0
    case StyleDisplay::RubyBaseContainer:
898
0
      frameType = NS_CSS_FRAME_TYPE_INLINE;
899
0
      break;
900
0
901
0
    case StyleDisplay::TableCell:
902
0
    case StyleDisplay::TableRowGroup:
903
0
    case StyleDisplay::TableColumn:
904
0
    case StyleDisplay::TableColumnGroup:
905
0
    case StyleDisplay::TableHeaderGroup:
906
0
    case StyleDisplay::TableFooterGroup:
907
0
    case StyleDisplay::TableRow:
908
0
      frameType = NS_CSS_FRAME_TYPE_INTERNAL_TABLE;
909
0
      break;
910
0
911
0
    case StyleDisplay::None:
912
0
    default:
913
0
      frameType = NS_CSS_FRAME_TYPE_UNKNOWN;
914
0
      break;
915
0
    }
916
0
  }
917
0
918
0
  // See if the frame is replaced
919
0
  if (mFrame->IsFrameOfType(nsIFrame::eReplacedContainsBlock)) {
920
0
    frameType = NS_FRAME_REPLACED_CONTAINS_BLOCK(frameType);
921
0
  } else if (mFrame->IsFrameOfType(nsIFrame::eReplaced)) {
922
0
    frameType = NS_FRAME_REPLACED(frameType);
923
0
  }
924
0
925
0
  mFrameType = frameType;
926
0
}
927
928
/* static */ void
929
ReflowInput::ComputeRelativeOffsets(WritingMode aWM,
930
                                          nsIFrame* aFrame,
931
                                          const LogicalSize& aCBSize,
932
                                          nsMargin& aComputedOffsets)
933
0
{
934
0
  LogicalMargin offsets(aWM);
935
0
  mozilla::Side inlineStart = aWM.PhysicalSide(eLogicalSideIStart);
936
0
  mozilla::Side inlineEnd   = aWM.PhysicalSide(eLogicalSideIEnd);
937
0
  mozilla::Side blockStart  = aWM.PhysicalSide(eLogicalSideBStart);
938
0
  mozilla::Side blockEnd    = aWM.PhysicalSide(eLogicalSideBEnd);
939
0
940
0
  const nsStylePosition* position = aFrame->StylePosition();
941
0
942
0
  // Compute the 'inlineStart' and 'inlineEnd' values. 'inlineStart'
943
0
  // moves the boxes to the end of the line, and 'inlineEnd' moves the
944
0
  // boxes to the start of the line. The computed values are always:
945
0
  // inlineStart=-inlineEnd
946
0
  bool inlineStartIsAuto =
947
0
    eStyleUnit_Auto == position->mOffset.GetUnit(inlineStart);
948
0
  bool inlineEndIsAuto =
949
0
    eStyleUnit_Auto == position->mOffset.GetUnit(inlineEnd);
950
0
951
0
  // If neither 'inlineStart' nor 'inlineEnd' is auto, then we're
952
0
  // over-constrained and we ignore one of them
953
0
  if (!inlineStartIsAuto && !inlineEndIsAuto) {
954
0
    inlineEndIsAuto = true;
955
0
  }
956
0
957
0
  if (inlineStartIsAuto) {
958
0
    if (inlineEndIsAuto) {
959
0
      // If both are 'auto' (their initial values), the computed values are 0
960
0
      offsets.IStart(aWM) = offsets.IEnd(aWM) = 0;
961
0
    } else {
962
0
      // 'inlineEnd' isn't 'auto' so compute its value
963
0
      offsets.IEnd(aWM) = nsLayoutUtils::
964
0
        ComputeCBDependentValue(aCBSize.ISize(aWM),
965
0
                                position->mOffset.Get(inlineEnd));
966
0
967
0
      // Computed value for 'inlineStart' is minus the value of 'inlineEnd'
968
0
      offsets.IStart(aWM) = -offsets.IEnd(aWM);
969
0
    }
970
0
971
0
  } else {
972
0
    NS_ASSERTION(inlineEndIsAuto, "unexpected specified constraint");
973
0
974
0
    // 'InlineStart' isn't 'auto' so compute its value
975
0
    offsets.IStart(aWM) = nsLayoutUtils::
976
0
      ComputeCBDependentValue(aCBSize.ISize(aWM),
977
0
                              position->mOffset.Get(inlineStart));
978
0
979
0
    // Computed value for 'inlineEnd' is minus the value of 'inlineStart'
980
0
    offsets.IEnd(aWM) = -offsets.IStart(aWM);
981
0
  }
982
0
983
0
  // Compute the 'blockStart' and 'blockEnd' values. The 'blockStart'
984
0
  // and 'blockEnd' properties move relatively positioned elements in
985
0
  // the block progression direction. They also must be each other's
986
0
  // negative
987
0
  bool blockStartIsAuto =
988
0
    eStyleUnit_Auto == position->mOffset.GetUnit(blockStart);
989
0
  bool blockEndIsAuto =
990
0
    eStyleUnit_Auto == position->mOffset.GetUnit(blockEnd);
991
0
992
0
  // Check for percentage based values and a containing block block-size
993
0
  // that depends on the content block-size. Treat them like 'auto'
994
0
  if (NS_AUTOHEIGHT == aCBSize.BSize(aWM)) {
995
0
    if (position->OffsetHasPercent(blockStart)) {
996
0
      blockStartIsAuto = true;
997
0
    }
998
0
    if (position->OffsetHasPercent(blockEnd)) {
999
0
      blockEndIsAuto = true;
1000
0
    }
1001
0
  }
1002
0
1003
0
  // If neither is 'auto', 'block-end' is ignored
1004
0
  if (!blockStartIsAuto && !blockEndIsAuto) {
1005
0
    blockEndIsAuto = true;
1006
0
  }
1007
0
1008
0
  if (blockStartIsAuto) {
1009
0
    if (blockEndIsAuto) {
1010
0
      // If both are 'auto' (their initial values), the computed values are 0
1011
0
      offsets.BStart(aWM) = offsets.BEnd(aWM) = 0;
1012
0
    } else {
1013
0
      // 'blockEnd' isn't 'auto' so compute its value
1014
0
      offsets.BEnd(aWM) = nsLayoutUtils::
1015
0
        ComputeBSizeDependentValue(aCBSize.BSize(aWM),
1016
0
                                   position->mOffset.Get(blockEnd));
1017
0
1018
0
      // Computed value for 'blockStart' is minus the value of 'blockEnd'
1019
0
      offsets.BStart(aWM) = -offsets.BEnd(aWM);
1020
0
    }
1021
0
1022
0
  } else {
1023
0
    NS_ASSERTION(blockEndIsAuto, "unexpected specified constraint");
1024
0
1025
0
    // 'blockStart' isn't 'auto' so compute its value
1026
0
    offsets.BStart(aWM) = nsLayoutUtils::
1027
0
      ComputeBSizeDependentValue(aCBSize.BSize(aWM),
1028
0
                                 position->mOffset.Get(blockStart));
1029
0
1030
0
    // Computed value for 'blockEnd' is minus the value of 'blockStart'
1031
0
    offsets.BEnd(aWM) = -offsets.BStart(aWM);
1032
0
  }
1033
0
1034
0
  // Convert the offsets to physical coordinates and store them on the frame
1035
0
  aComputedOffsets = offsets.GetPhysicalMargin(aWM);
1036
0
  nsMargin* physicalOffsets =
1037
0
    aFrame->GetProperty(nsIFrame::ComputedOffsetProperty());
1038
0
  if (physicalOffsets) {
1039
0
    *physicalOffsets = aComputedOffsets;
1040
0
  } else {
1041
0
    aFrame->AddProperty(nsIFrame::ComputedOffsetProperty(),
1042
0
                        new nsMargin(aComputedOffsets));
1043
0
  }
1044
0
}
1045
1046
/* static */ void
1047
ReflowInput::ApplyRelativePositioning(nsIFrame* aFrame,
1048
                                            const nsMargin& aComputedOffsets,
1049
                                            nsPoint* aPosition)
1050
0
{
1051
0
  if (!aFrame->IsRelativelyPositioned()) {
1052
0
    NS_ASSERTION(!aFrame->GetProperty(nsIFrame::NormalPositionProperty()),
1053
0
                 "We assume that changing the 'position' property causes "
1054
0
                 "frame reconstruction.  If that ever changes, this code "
1055
0
                 "should call "
1056
0
                 "aFrame->DeleteProperty(nsIFrame::NormalPositionProperty())");
1057
0
    return;
1058
0
  }
1059
0
1060
0
  // Store the normal position
1061
0
  nsPoint* normalPosition =
1062
0
    aFrame->GetProperty(nsIFrame::NormalPositionProperty());
1063
0
  if (normalPosition) {
1064
0
    *normalPosition = *aPosition;
1065
0
  } else {
1066
0
    aFrame->AddProperty(nsIFrame::NormalPositionProperty(),
1067
0
                        new nsPoint(*aPosition));
1068
0
  }
1069
0
1070
0
  const nsStyleDisplay* display = aFrame->StyleDisplay();
1071
0
  if (NS_STYLE_POSITION_RELATIVE == display->mPosition) {
1072
0
    *aPosition += nsPoint(aComputedOffsets.left, aComputedOffsets.top);
1073
0
  } else if (NS_STYLE_POSITION_STICKY == display->mPosition &&
1074
0
             !aFrame->GetNextContinuation() &&
1075
0
             !aFrame->GetPrevContinuation() &&
1076
0
             !(aFrame->GetStateBits() & NS_FRAME_PART_OF_IBSPLIT)) {
1077
0
    // Sticky positioning for elements with multiple frames needs to be
1078
0
    // computed all at once. We can't safely do that here because we might be
1079
0
    // partway through (re)positioning the frames, so leave it until the scroll
1080
0
    // container reflows and calls StickyScrollContainer::UpdatePositions.
1081
0
    // For single-frame sticky positioned elements, though, go ahead and apply
1082
0
    // it now to avoid unnecessary overflow updates later.
1083
0
    StickyScrollContainer* ssc =
1084
0
      StickyScrollContainer::GetStickyScrollContainerForFrame(aFrame);
1085
0
    if (ssc) {
1086
0
      *aPosition = ssc->ComputePosition(aFrame);
1087
0
    }
1088
0
  }
1089
0
}
1090
1091
// Returns true if aFrame is non-null, a XUL frame, and "XUL-collapsed" (which
1092
// only becomes a valid question to ask if we know it's a XUL frame).
1093
static bool
1094
IsXULCollapsedXULFrame(nsIFrame* aFrame)
1095
0
{
1096
0
  return aFrame && aFrame->IsXULBoxFrame() && aFrame->IsXULCollapsed();
1097
0
}
1098
1099
nsIFrame*
1100
ReflowInput::GetHypotheticalBoxContainer(nsIFrame*    aFrame,
1101
                                               nscoord&     aCBIStartEdge,
1102
                                               LogicalSize& aCBSize) const
1103
0
{
1104
0
  aFrame = aFrame->GetContainingBlock();
1105
0
  NS_ASSERTION(aFrame != mFrame, "How did that happen?");
1106
0
1107
0
  /* Now aFrame is the containing block we want */
1108
0
1109
0
  /* Check whether the containing block is currently being reflowed.
1110
0
     If so, use the info from the reflow input. */
1111
0
  const ReflowInput* reflowInput;
1112
0
  if (aFrame->GetStateBits() & NS_FRAME_IN_REFLOW) {
1113
0
    for (reflowInput = mParentReflowInput;
1114
0
         reflowInput && reflowInput->mFrame != aFrame;
1115
0
         reflowInput = reflowInput->mParentReflowInput) {
1116
0
      /* do nothing */
1117
0
    }
1118
0
  } else {
1119
0
    reflowInput = nullptr;
1120
0
  }
1121
0
1122
0
  if (reflowInput) {
1123
0
    WritingMode wm = reflowInput->GetWritingMode();
1124
0
    NS_ASSERTION(wm == aFrame->GetWritingMode(), "unexpected writing mode");
1125
0
    aCBIStartEdge = reflowInput->ComputedLogicalBorderPadding().IStart(wm);
1126
0
    aCBSize = reflowInput->ComputedSize(wm);
1127
0
  } else {
1128
0
    /* Didn't find a reflow reflowInput for aFrame.  Just compute the information we
1129
0
       want, on the assumption that aFrame already knows its size.  This really
1130
0
       ought to be true by now. */
1131
0
    NS_ASSERTION(!(aFrame->GetStateBits() & NS_FRAME_IN_REFLOW),
1132
0
                 "aFrame shouldn't be in reflow; we'll lie if it is");
1133
0
    WritingMode wm = aFrame->GetWritingMode();
1134
0
    // Compute CB's offset & content-box size by subtracting borderpadding from
1135
0
    // frame size.  Exception: if the CB is 0-sized, it *might* be a child of a
1136
0
    // XUL-collapsed frame and might have nonzero borderpadding that was simply
1137
0
    // discarded during its layout. (See the child-zero-sizing in
1138
0
    // nsSprocketLayout::XULLayout()).  In that case, we ignore the
1139
0
    // borderpadding here (just like we did when laying it out), or else we'd
1140
0
    // produce a bogus negative content-box size.
1141
0
    aCBIStartEdge = 0;
1142
0
    aCBSize = aFrame->GetLogicalSize(wm);
1143
0
    if (!aCBSize.IsAllZero() ||
1144
0
        (!IsXULCollapsedXULFrame(aFrame->GetParent()))) {
1145
0
      // aFrame is not XUL-collapsed (nor is it a child of a XUL-collapsed
1146
0
      // frame), so we can go ahead and subtract out border padding.
1147
0
      LogicalMargin borderPadding = aFrame->GetLogicalUsedBorderAndPadding(wm);
1148
0
      aCBIStartEdge += borderPadding.IStart(wm);
1149
0
      aCBSize -= borderPadding.Size(wm);
1150
0
    }
1151
0
  }
1152
0
1153
0
  return aFrame;
1154
0
}
1155
1156
struct nsHypotheticalPosition {
1157
  // offset from inline-start edge of containing block (which is a padding edge)
1158
  nscoord       mIStart;
1159
  // offset from block-start edge of containing block (which is a padding edge)
1160
  nscoord       mBStart;
1161
  WritingMode   mWritingMode;
1162
};
1163
1164
static bool
1165
GetIntrinsicSizeFor(nsIFrame* aFrame,
1166
                    nsSize& aIntrinsicSize,
1167
                    LayoutFrameType aFrameType)
1168
0
{
1169
0
  // See if it is an image frame
1170
0
  bool success = false;
1171
0
1172
0
  // Currently the only type of replaced frame that we can get the intrinsic
1173
0
  // size for is an image frame
1174
0
  // XXX We should add back the GetReflowOutput() function and one of the
1175
0
  // things should be the intrinsic size...
1176
0
  if (aFrameType == LayoutFrameType::Image) {
1177
0
    nsImageFrame* imageFrame = (nsImageFrame*)aFrame;
1178
0
1179
0
    if (NS_SUCCEEDED(imageFrame->GetIntrinsicImageSize(aIntrinsicSize))) {
1180
0
      success = (aIntrinsicSize != nsSize(0, 0));
1181
0
    }
1182
0
  }
1183
0
  return success;
1184
0
}
1185
1186
/**
1187
 * aInsideBoxSizing returns the part of the padding, border, and margin
1188
 * in the aAxis dimension that goes inside the edge given by box-sizing;
1189
 * aOutsideBoxSizing returns the rest.
1190
 */
1191
void
1192
ReflowInput::CalculateBorderPaddingMargin(
1193
                       LogicalAxis aAxis,
1194
                       nscoord aContainingBlockSize,
1195
                       nscoord* aInsideBoxSizing,
1196
                       nscoord* aOutsideBoxSizing) const
1197
0
{
1198
0
  WritingMode wm = GetWritingMode();
1199
0
  mozilla::Side startSide =
1200
0
    wm.PhysicalSide(MakeLogicalSide(aAxis, eLogicalEdgeStart));
1201
0
  mozilla::Side endSide =
1202
0
    wm.PhysicalSide(MakeLogicalSide(aAxis, eLogicalEdgeEnd));
1203
0
1204
0
  nsMargin styleBorder = mStyleBorder->GetComputedBorder();
1205
0
  nscoord borderStartEnd =
1206
0
    styleBorder.Side(startSide) + styleBorder.Side(endSide);
1207
0
1208
0
  nscoord paddingStartEnd, marginStartEnd;
1209
0
1210
0
  // See if the style system can provide us the padding directly
1211
0
  nsMargin stylePadding;
1212
0
  if (mStylePadding->GetPadding(stylePadding)) {
1213
0
    paddingStartEnd =
1214
0
      stylePadding.Side(startSide) + stylePadding.Side(endSide);
1215
0
  } else {
1216
0
    // We have to compute the start and end values
1217
0
    nscoord start, end;
1218
0
    start = nsLayoutUtils::
1219
0
      ComputeCBDependentValue(aContainingBlockSize,
1220
0
                              mStylePadding->mPadding.Get(startSide));
1221
0
    end = nsLayoutUtils::
1222
0
      ComputeCBDependentValue(aContainingBlockSize,
1223
0
                              mStylePadding->mPadding.Get(endSide));
1224
0
    paddingStartEnd = start + end;
1225
0
  }
1226
0
1227
0
  // See if the style system can provide us the margin directly
1228
0
  nsMargin styleMargin;
1229
0
  if (mStyleMargin->GetMargin(styleMargin)) {
1230
0
    marginStartEnd =
1231
0
      styleMargin.Side(startSide) + styleMargin.Side(endSide);
1232
0
  } else {
1233
0
    nscoord start, end;
1234
0
    // We have to compute the start and end values
1235
0
    if (eStyleUnit_Auto == mStyleMargin->mMargin.GetUnit(startSide)) {
1236
0
      // We set this to 0 for now, and fix it up later in
1237
0
      // InitAbsoluteConstraints (which is caller of this function, via
1238
0
      // CalculateHypotheticalPosition).
1239
0
      start = 0;
1240
0
    } else {
1241
0
      start = nsLayoutUtils::
1242
0
        ComputeCBDependentValue(aContainingBlockSize,
1243
0
                                mStyleMargin->mMargin.Get(startSide));
1244
0
    }
1245
0
    if (eStyleUnit_Auto == mStyleMargin->mMargin.GetUnit(endSide)) {
1246
0
      // We set this to 0 for now, and fix it up later in
1247
0
      // InitAbsoluteConstraints (which is caller of this function, via
1248
0
      // CalculateHypotheticalPosition).
1249
0
      end = 0;
1250
0
    } else {
1251
0
      end = nsLayoutUtils::
1252
0
        ComputeCBDependentValue(aContainingBlockSize,
1253
0
                                mStyleMargin->mMargin.Get(endSide));
1254
0
    }
1255
0
    marginStartEnd = start + end;
1256
0
  }
1257
0
1258
0
  nscoord outside = paddingStartEnd + borderStartEnd + marginStartEnd;
1259
0
  nscoord inside = 0;
1260
0
  if (mStylePosition->mBoxSizing == StyleBoxSizing::Border) {
1261
0
    inside = borderStartEnd + paddingStartEnd;
1262
0
  }
1263
0
  outside -= inside;
1264
0
  *aInsideBoxSizing = inside;
1265
0
  *aOutsideBoxSizing = outside;
1266
0
}
1267
1268
/**
1269
 * Returns true iff a pre-order traversal of the normal child
1270
 * frames rooted at aFrame finds no non-empty frame before aDescendant.
1271
 */
1272
static bool
1273
AreAllEarlierInFlowFramesEmpty(nsIFrame* aFrame,
1274
                               nsIFrame* aDescendant,
1275
                               bool* aFound)
1276
0
{
1277
0
  if (aFrame == aDescendant) {
1278
0
    *aFound = true;
1279
0
    return true;
1280
0
  }
1281
0
  if (aFrame->IsPlaceholderFrame()) {
1282
0
    auto ph = static_cast<nsPlaceholderFrame*>(aFrame);
1283
0
    MOZ_ASSERT(ph->IsSelfEmpty() && ph->PrincipalChildList().IsEmpty());
1284
0
    ph->SetLineIsEmptySoFar(true);
1285
0
  } else {
1286
0
    if (!aFrame->IsSelfEmpty()) {
1287
0
      *aFound = false;
1288
0
      return false;
1289
0
    }
1290
0
    for (nsIFrame* f : aFrame->PrincipalChildList()) {
1291
0
      bool allEmpty = AreAllEarlierInFlowFramesEmpty(f, aDescendant, aFound);
1292
0
      if (*aFound || !allEmpty) {
1293
0
        return allEmpty;
1294
0
      }
1295
0
    }
1296
0
  }
1297
0
  *aFound = false;
1298
0
  return true;
1299
0
}
1300
1301
// Calculate the position of the hypothetical box that the element would have
1302
// if it were in the flow.
1303
// The values returned are relative to the padding edge of the absolute
1304
// containing block. The writing-mode of the hypothetical box position will
1305
// have the same block direction as the absolute containing block, but may
1306
// differ in inline-bidi direction.
1307
// In the code below, |aReflowInput->frame| is the absolute containing block, while
1308
// |containingBlock| is the nearest block container of the placeholder frame,
1309
// which may be different from the absolute containing block.
1310
void
1311
ReflowInput::CalculateHypotheticalPosition(
1312
  nsPresContext* aPresContext,
1313
  nsPlaceholderFrame* aPlaceholderFrame,
1314
  const ReflowInput* aReflowInput,
1315
  nsHypotheticalPosition& aHypotheticalPos,
1316
  LayoutFrameType aFrameType) const
1317
0
{
1318
0
  NS_ASSERTION(mStyleDisplay->mOriginalDisplay != StyleDisplay::None,
1319
0
               "mOriginalDisplay has not been properly initialized");
1320
0
1321
0
  // Find the nearest containing block frame to the placeholder frame,
1322
0
  // and its inline-start edge and width.
1323
0
  nscoord blockIStartContentEdge;
1324
0
  // Dummy writing mode for blockContentSize, will be changed as needed by
1325
0
  // GetHypotheticalBoxContainer.
1326
0
  WritingMode cbwm = aReflowInput->GetWritingMode();
1327
0
  LogicalSize blockContentSize(cbwm);
1328
0
  nsIFrame* containingBlock =
1329
0
    GetHypotheticalBoxContainer(aPlaceholderFrame, blockIStartContentEdge,
1330
0
                                blockContentSize);
1331
0
  // Now blockContentSize is in containingBlock's writing mode.
1332
0
1333
0
  // If it's a replaced element and it has a 'auto' value for
1334
0
  //'inline size', see if we can get the intrinsic size. This will allow
1335
0
  // us to exactly determine both the inline edges
1336
0
  WritingMode wm = containingBlock->GetWritingMode();
1337
0
1338
0
  nsStyleCoord styleISize = mStylePosition->ISize(wm);
1339
0
  bool isAutoISize = styleISize.GetUnit() == eStyleUnit_Auto;
1340
0
  nsSize      intrinsicSize;
1341
0
  bool        knowIntrinsicSize = false;
1342
0
  if (NS_FRAME_IS_REPLACED(mFrameType) && isAutoISize) {
1343
0
    // See if we can get the intrinsic size of the element
1344
0
    knowIntrinsicSize = GetIntrinsicSizeFor(mFrame, intrinsicSize, aFrameType);
1345
0
  }
1346
0
1347
0
  // See if we can calculate what the box inline size would have been if
1348
0
  // the element had been in the flow
1349
0
  nscoord boxISize;
1350
0
  bool    knowBoxISize = false;
1351
0
  if ((StyleDisplay::Inline == mStyleDisplay->mOriginalDisplay) &&
1352
0
      !NS_FRAME_IS_REPLACED(mFrameType)) {
1353
0
    // For non-replaced inline-level elements the 'inline size' property
1354
0
    // doesn't apply, so we don't know what the inline size would have
1355
0
    // been without reflowing it
1356
0
1357
0
  } else {
1358
0
    // It's either a replaced inline-level element or a block-level element
1359
0
1360
0
    // Determine the total amount of inline direction
1361
0
    // border/padding/margin that the element would have had if it had
1362
0
    // been in the flow. Note that we ignore any 'auto' and 'inherit'
1363
0
    // values
1364
0
    nscoord insideBoxSizing, outsideBoxSizing;
1365
0
    CalculateBorderPaddingMargin(eLogicalAxisInline,
1366
0
                                 blockContentSize.ISize(wm),
1367
0
                                 &insideBoxSizing, &outsideBoxSizing);
1368
0
1369
0
    if (NS_FRAME_IS_REPLACED(mFrameType) && isAutoISize) {
1370
0
      // It's a replaced element with an 'auto' inline size so the box
1371
0
      // inline size is its intrinsic size plus any border/padding/margin
1372
0
      if (knowIntrinsicSize) {
1373
0
        boxISize = LogicalSize(wm, intrinsicSize).ISize(wm) +
1374
0
                   outsideBoxSizing + insideBoxSizing;
1375
0
        knowBoxISize = true;
1376
0
      }
1377
0
1378
0
    } else if (isAutoISize) {
1379
0
      // The box inline size is the containing block inline size
1380
0
      boxISize = blockContentSize.ISize(wm);
1381
0
      knowBoxISize = true;
1382
0
1383
0
    } else {
1384
0
      // We need to compute it. It's important we do this, because if it's
1385
0
      // percentage based this computed value may be different from the computed
1386
0
      // value calculated using the absolute containing block width
1387
0
      boxISize = ComputeISizeValue(blockContentSize.ISize(wm),
1388
0
                                   insideBoxSizing, outsideBoxSizing,
1389
0
                                   styleISize) +
1390
0
                 insideBoxSizing + outsideBoxSizing;
1391
0
      knowBoxISize = true;
1392
0
    }
1393
0
  }
1394
0
1395
0
  // Get the placeholder x-offset and y-offset in the coordinate
1396
0
  // space of its containing block
1397
0
  // XXXbz the placeholder is not fully reflowed yet if our containing block is
1398
0
  // relatively positioned...
1399
0
  nsSize containerSize = containingBlock->GetStateBits() & NS_FRAME_IN_REFLOW
1400
0
    ? aReflowInput->ComputedSizeAsContainerIfConstrained()
1401
0
    : containingBlock->GetSize();
1402
0
  LogicalPoint
1403
0
    placeholderOffset(wm,
1404
0
                      aPlaceholderFrame->GetOffsetToIgnoringScrolling(containingBlock),
1405
0
                      containerSize);
1406
0
1407
0
  // First, determine the hypothetical box's mBStart.  We want to check the
1408
0
  // content insertion frame of containingBlock for block-ness, but make
1409
0
  // sure to compute all coordinates in the coordinate system of
1410
0
  // containingBlock.
1411
0
  nsBlockFrame* blockFrame =
1412
0
    nsLayoutUtils::GetAsBlock(containingBlock->GetContentInsertionFrame());
1413
0
  if (blockFrame) {
1414
0
    // Use a null containerSize to convert a LogicalPoint functioning as a
1415
0
    // vector into a physical nsPoint vector.
1416
0
    const nsSize nullContainerSize;
1417
0
    LogicalPoint blockOffset(wm,
1418
0
                             blockFrame->GetOffsetToIgnoringScrolling(containingBlock),
1419
0
                             nullContainerSize);
1420
0
    bool isValid;
1421
0
    nsBlockInFlowLineIterator iter(blockFrame, aPlaceholderFrame, &isValid);
1422
0
    if (!isValid) {
1423
0
      // Give up.  We're probably dealing with somebody using
1424
0
      // position:absolute inside native-anonymous content anyway.
1425
0
      aHypotheticalPos.mBStart = placeholderOffset.B(wm);
1426
0
    } else {
1427
0
      NS_ASSERTION(iter.GetContainer() == blockFrame,
1428
0
                   "Found placeholder in wrong block!");
1429
0
      nsBlockFrame::LineIterator lineBox = iter.GetLine();
1430
0
1431
0
      // How we determine the hypothetical box depends on whether the element
1432
0
      // would have been inline-level or block-level
1433
0
      LogicalRect lineBounds =
1434
0
        lineBox->GetBounds().ConvertTo(wm, lineBox->mWritingMode,
1435
0
                                       lineBox->mContainerSize);
1436
0
      if (mStyleDisplay->IsOriginalDisplayInlineOutsideStyle()) {
1437
0
        // Use the block-start of the inline box which the placeholder lives in
1438
0
        // as the hypothetical box's block-start.
1439
0
        aHypotheticalPos.mBStart = lineBounds.BStart(wm) + blockOffset.B(wm);
1440
0
      } else {
1441
0
        // The element would have been block-level which means it would
1442
0
        // be below the line containing the placeholder frame, unless
1443
0
        // all the frames before it are empty.  In that case, it would
1444
0
        // have been just before this line.
1445
0
        // XXXbz the line box is not fully reflowed yet if our
1446
0
        // containing block is relatively positioned...
1447
0
        if (lineBox != iter.End()) {
1448
0
          nsIFrame* firstFrame = lineBox->mFirstChild;
1449
0
          bool allEmpty = false;
1450
0
          if (firstFrame == aPlaceholderFrame) {
1451
0
            aPlaceholderFrame->SetLineIsEmptySoFar(true);
1452
0
            allEmpty = true;
1453
0
          } else {
1454
0
            auto prev = aPlaceholderFrame->GetPrevSibling();
1455
0
            if (prev && prev->IsPlaceholderFrame()) {
1456
0
              auto ph = static_cast<nsPlaceholderFrame*>(prev);
1457
0
              if (ph->GetLineIsEmptySoFar(&allEmpty)) {
1458
0
                aPlaceholderFrame->SetLineIsEmptySoFar(allEmpty);
1459
0
              }
1460
0
            }
1461
0
          }
1462
0
          if (!allEmpty) {
1463
0
            bool found = false;
1464
0
            while (firstFrame) { // See bug 223064
1465
0
              allEmpty = AreAllEarlierInFlowFramesEmpty(firstFrame,
1466
0
                aPlaceholderFrame, &found);
1467
0
              if (found || !allEmpty) {
1468
0
                break;
1469
0
              }
1470
0
              firstFrame = firstFrame->GetNextSibling();
1471
0
            }
1472
0
            aPlaceholderFrame->SetLineIsEmptySoFar(allEmpty);
1473
0
          }
1474
0
          NS_ASSERTION(firstFrame, "Couldn't find placeholder!");
1475
0
1476
0
          if (allEmpty) {
1477
0
            // The top of the hypothetical box is the top of the line
1478
0
            // containing the placeholder, since there is nothing in the
1479
0
            // line before our placeholder except empty frames.
1480
0
            aHypotheticalPos.mBStart =
1481
0
              lineBounds.BStart(wm) + blockOffset.B(wm);
1482
0
          } else {
1483
0
            // The top of the hypothetical box is just below the line
1484
0
            // containing the placeholder.
1485
0
            aHypotheticalPos.mBStart =
1486
0
              lineBounds.BEnd(wm) + blockOffset.B(wm);
1487
0
          }
1488
0
        } else {
1489
0
          // Just use the placeholder's block-offset wrt the containing block
1490
0
          aHypotheticalPos.mBStart = placeholderOffset.B(wm);
1491
0
        }
1492
0
      }
1493
0
    }
1494
0
  } else {
1495
0
    // The containing block is not a block, so it's probably something
1496
0
    // like a XUL box, etc.
1497
0
    // Just use the placeholder's block-offset
1498
0
    aHypotheticalPos.mBStart = placeholderOffset.B(wm);
1499
0
  }
1500
0
1501
0
  // Second, determine the hypothetical box's mIStart.
1502
0
  // How we determine the hypothetical box depends on whether the element
1503
0
  // would have been inline-level or block-level
1504
0
  if (mStyleDisplay->IsOriginalDisplayInlineOutsideStyle() ||
1505
0
      mFlags.mIOffsetsNeedCSSAlign) {
1506
0
    // The placeholder represents the IStart edge of the hypothetical box.
1507
0
    // (Or if mFlags.mIOffsetsNeedCSSAlign is set, it represents the IStart
1508
0
    // edge of the Alignment Container.)
1509
0
    aHypotheticalPos.mIStart = placeholderOffset.I(wm);
1510
0
  } else {
1511
0
    aHypotheticalPos.mIStart = blockIStartContentEdge;
1512
0
  }
1513
0
1514
0
  // The current coordinate space is that of the nearest block to the placeholder.
1515
0
  // Convert to the coordinate space of the absolute containing block.
1516
0
  nsPoint cbOffset =
1517
0
    containingBlock->GetOffsetToIgnoringScrolling(aReflowInput->mFrame);
1518
0
1519
0
  nsSize reflowSize = aReflowInput->ComputedSizeAsContainerIfConstrained();
1520
0
  LogicalPoint logCBOffs(wm, cbOffset, reflowSize - containerSize);
1521
0
  aHypotheticalPos.mIStart += logCBOffs.I(wm);
1522
0
  aHypotheticalPos.mBStart += logCBOffs.B(wm);
1523
0
1524
0
  // The specified offsets are relative to the absolute containing block's
1525
0
  // padding edge and our current values are relative to the border edge, so
1526
0
  // translate.
1527
0
  LogicalMargin border =
1528
0
    aReflowInput->ComputedLogicalBorderPadding() -
1529
0
    aReflowInput->ComputedLogicalPadding();
1530
0
  border = border.ConvertTo(wm, aReflowInput->GetWritingMode());
1531
0
  aHypotheticalPos.mIStart -= border.IStart(wm);
1532
0
  aHypotheticalPos.mBStart -= border.BStart(wm);
1533
0
1534
0
  // At this point, we have computed aHypotheticalPos using the writing mode
1535
0
  // of the placeholder's containing block.
1536
0
1537
0
  if (cbwm.GetBlockDir() != wm.GetBlockDir()) {
1538
0
    // If the block direction we used in calculating aHypotheticalPos does not
1539
0
    // match the absolute containing block's, we need to convert here so that
1540
0
    // aHypotheticalPos is usable in relation to the absolute containing block.
1541
0
    // This requires computing or measuring the abspos frame's block-size,
1542
0
    // which is not otherwise required/used here (as aHypotheticalPos
1543
0
    // records only the block-start coordinate).
1544
0
1545
0
    // This is similar to the inline-size calculation for a replaced
1546
0
    // inline-level element or a block-level element (above), except that
1547
0
    // 'auto' sizing is handled differently in the block direction for non-
1548
0
    // replaced elements and replaced elements lacking an intrinsic size.
1549
0
1550
0
    // Determine the total amount of block direction
1551
0
    // border/padding/margin that the element would have had if it had
1552
0
    // been in the flow. Note that we ignore any 'auto' and 'inherit'
1553
0
    // values.
1554
0
    nscoord insideBoxSizing, outsideBoxSizing;
1555
0
    CalculateBorderPaddingMargin(eLogicalAxisBlock,
1556
0
                                 blockContentSize.BSize(wm),
1557
0
                                 &insideBoxSizing, &outsideBoxSizing);
1558
0
1559
0
    nscoord boxBSize;
1560
0
    nsStyleCoord styleBSize = mStylePosition->BSize(wm);
1561
0
    bool isAutoBSize = styleBSize.GetUnit() == eStyleUnit_Auto;
1562
0
    if (isAutoBSize) {
1563
0
      if (NS_FRAME_IS_REPLACED(mFrameType) && knowIntrinsicSize) {
1564
0
        // It's a replaced element with an 'auto' block size so the box
1565
0
        // block size is its intrinsic size plus any border/padding/margin
1566
0
        boxBSize = LogicalSize(wm, intrinsicSize).BSize(wm) +
1567
0
                   outsideBoxSizing + insideBoxSizing;
1568
0
      } else {
1569
0
        // XXX Bug 1191801
1570
0
        // Figure out how to get the correct boxBSize here (need to reflow the
1571
0
        // positioned frame?)
1572
0
        boxBSize = 0;
1573
0
      }
1574
0
    } else {
1575
0
      // We need to compute it. It's important we do this, because if it's
1576
0
      // percentage-based this computed value may be different from the
1577
0
      // computed value calculated using the absolute containing block height.
1578
0
      boxBSize = nsLayoutUtils::ComputeBSizeValue(blockContentSize.BSize(wm),
1579
0
                                                  insideBoxSizing, styleBSize) +
1580
0
                 insideBoxSizing + outsideBoxSizing;
1581
0
    }
1582
0
1583
0
    LogicalSize boxSize(wm, knowBoxISize ? boxISize : 0, boxBSize);
1584
0
1585
0
    LogicalPoint origin(wm, aHypotheticalPos.mIStart,
1586
0
                        aHypotheticalPos.mBStart);
1587
0
    origin = origin.ConvertTo(cbwm, wm, reflowSize -
1588
0
                              boxSize.GetPhysicalSize(wm));
1589
0
1590
0
    aHypotheticalPos.mIStart = origin.I(cbwm);
1591
0
    aHypotheticalPos.mBStart = origin.B(cbwm);
1592
0
    aHypotheticalPos.mWritingMode = cbwm;
1593
0
  } else {
1594
0
    aHypotheticalPos.mWritingMode = wm;
1595
0
  }
1596
0
}
1597
1598
void
1599
ReflowInput::InitAbsoluteConstraints(nsPresContext* aPresContext,
1600
                                     const ReflowInput* aReflowInput,
1601
                                     const LogicalSize& aCBSize,
1602
                                     LayoutFrameType aFrameType)
1603
0
{
1604
0
  WritingMode wm = GetWritingMode();
1605
0
  WritingMode cbwm = aReflowInput->GetWritingMode();
1606
0
  NS_WARNING_ASSERTION(aCBSize.BSize(cbwm) != NS_AUTOHEIGHT,
1607
0
                       "containing block bsize must be constrained");
1608
0
1609
0
  NS_ASSERTION(aFrameType != LayoutFrameType::Table,
1610
0
               "InitAbsoluteConstraints should not be called on table frames");
1611
0
  NS_ASSERTION(mFrame->GetStateBits() & NS_FRAME_OUT_OF_FLOW,
1612
0
               "Why are we here?");
1613
0
1614
0
  const auto& styleOffset = mStylePosition->mOffset;
1615
0
  bool iStartIsAuto = styleOffset.GetIStartUnit(cbwm) == eStyleUnit_Auto;
1616
0
  bool iEndIsAuto   = styleOffset.GetIEndUnit(cbwm) == eStyleUnit_Auto;
1617
0
  bool bStartIsAuto = styleOffset.GetBStartUnit(cbwm) == eStyleUnit_Auto;
1618
0
  bool bEndIsAuto   = styleOffset.GetBEndUnit(cbwm) == eStyleUnit_Auto;
1619
0
1620
0
  // If both 'left' and 'right' are 'auto' or both 'top' and 'bottom' are
1621
0
  // 'auto', then compute the hypothetical box position where the element would
1622
0
  // have been if it had been in the flow
1623
0
  nsHypotheticalPosition hypotheticalPos;
1624
0
  if ((iStartIsAuto && iEndIsAuto) || (bStartIsAuto && bEndIsAuto)) {
1625
0
    nsPlaceholderFrame* placeholderFrame = mFrame->GetPlaceholderFrame();
1626
0
    MOZ_ASSERT(placeholderFrame, "no placeholder frame");
1627
0
1628
0
    if (placeholderFrame->HasAnyStateBits(
1629
0
          PLACEHOLDER_STATICPOS_NEEDS_CSSALIGN)) {
1630
0
      DebugOnly<nsIFrame*> placeholderParent = placeholderFrame->GetParent();
1631
0
      MOZ_ASSERT(placeholderParent, "shouldn't have unparented placeholders");
1632
0
      MOZ_ASSERT(placeholderParent->IsFlexOrGridContainer(),
1633
0
                 "This flag should only be set on grid/flex children");
1634
0
1635
0
      // If the (as-yet unknown) static position will determine the inline
1636
0
      // and/or block offsets, set flags to note those offsets aren't valid
1637
0
      // until we can do CSS Box Alignment on the OOF frame.
1638
0
      mFlags.mIOffsetsNeedCSSAlign = (iStartIsAuto && iEndIsAuto);
1639
0
      mFlags.mBOffsetsNeedCSSAlign = (bStartIsAuto && bEndIsAuto);
1640
0
    }
1641
0
1642
0
    if (mFlags.mStaticPosIsCBOrigin) {
1643
0
      hypotheticalPos.mWritingMode = cbwm;
1644
0
      hypotheticalPos.mIStart = nscoord(0);
1645
0
      hypotheticalPos.mBStart = nscoord(0);
1646
0
    } else {
1647
0
      CalculateHypotheticalPosition(aPresContext, placeholderFrame,
1648
0
                                    aReflowInput, hypotheticalPos, aFrameType);
1649
0
    }
1650
0
  }
1651
0
1652
0
  // Initialize the 'left' and 'right' computed offsets
1653
0
  // XXX Handle new 'static-position' value...
1654
0
1655
0
  // Size of the containing block in its writing mode
1656
0
  LogicalSize cbSize = aCBSize;
1657
0
  LogicalMargin offsets = ComputedLogicalOffsets().ConvertTo(cbwm, wm);
1658
0
1659
0
  if (iStartIsAuto) {
1660
0
    offsets.IStart(cbwm) = 0;
1661
0
  } else {
1662
0
    offsets.IStart(cbwm) = nsLayoutUtils::
1663
0
      ComputeCBDependentValue(cbSize.ISize(cbwm), styleOffset.GetIStart(cbwm));
1664
0
  }
1665
0
  if (iEndIsAuto) {
1666
0
    offsets.IEnd(cbwm) = 0;
1667
0
  } else {
1668
0
    offsets.IEnd(cbwm) = nsLayoutUtils::
1669
0
      ComputeCBDependentValue(cbSize.ISize(cbwm), styleOffset.GetIEnd(cbwm));
1670
0
  }
1671
0
1672
0
  if (iStartIsAuto && iEndIsAuto) {
1673
0
    if (cbwm.IsBidiLTR() != hypotheticalPos.mWritingMode.IsBidiLTR()) {
1674
0
      offsets.IEnd(cbwm) = hypotheticalPos.mIStart;
1675
0
      iEndIsAuto = false;
1676
0
    } else {
1677
0
      offsets.IStart(cbwm) = hypotheticalPos.mIStart;
1678
0
      iStartIsAuto = false;
1679
0
    }
1680
0
  }
1681
0
1682
0
  if (bStartIsAuto) {
1683
0
    offsets.BStart(cbwm) = 0;
1684
0
  } else {
1685
0
    offsets.BStart(cbwm) = nsLayoutUtils::
1686
0
      ComputeBSizeDependentValue(cbSize.BSize(cbwm),
1687
0
                                 styleOffset.GetBStart(cbwm));
1688
0
  }
1689
0
  if (bEndIsAuto) {
1690
0
    offsets.BEnd(cbwm) = 0;
1691
0
  } else {
1692
0
    offsets.BEnd(cbwm) = nsLayoutUtils::
1693
0
      ComputeBSizeDependentValue(cbSize.BSize(cbwm),
1694
0
                                 styleOffset.GetBEnd(cbwm));
1695
0
  }
1696
0
1697
0
  if (bStartIsAuto && bEndIsAuto) {
1698
0
    // Treat 'top' like 'static-position'
1699
0
    offsets.BStart(cbwm) = hypotheticalPos.mBStart;
1700
0
    bStartIsAuto = false;
1701
0
  }
1702
0
1703
0
  SetComputedLogicalOffsets(offsets.ConvertTo(wm, cbwm));
1704
0
1705
0
  typedef nsIFrame::ComputeSizeFlags ComputeSizeFlags;
1706
0
  ComputeSizeFlags computeSizeFlags = ComputeSizeFlags::eDefault;
1707
0
  if (mFlags.mIClampMarginBoxMinSize) {
1708
0
    computeSizeFlags = ComputeSizeFlags(computeSizeFlags |
1709
0
                         ComputeSizeFlags::eIClampMarginBoxMinSize);
1710
0
  }
1711
0
  if (mFlags.mBClampMarginBoxMinSize) {
1712
0
    computeSizeFlags = ComputeSizeFlags(computeSizeFlags |
1713
0
                         ComputeSizeFlags::eBClampMarginBoxMinSize);
1714
0
  }
1715
0
  if (mFlags.mApplyAutoMinSize) {
1716
0
    computeSizeFlags = ComputeSizeFlags(computeSizeFlags |
1717
0
                         ComputeSizeFlags::eIApplyAutoMinSize);
1718
0
  }
1719
0
  if (mFlags.mShrinkWrap) {
1720
0
    computeSizeFlags =
1721
0
      ComputeSizeFlags(computeSizeFlags | ComputeSizeFlags::eShrinkWrap);
1722
0
  }
1723
0
  if (mFlags.mUseAutoBSize) {
1724
0
    computeSizeFlags =
1725
0
      ComputeSizeFlags(computeSizeFlags | ComputeSizeFlags::eUseAutoBSize);
1726
0
  }
1727
0
  if (wm.IsOrthogonalTo(cbwm)) {
1728
0
    if (bStartIsAuto || bEndIsAuto) {
1729
0
      computeSizeFlags =
1730
0
        ComputeSizeFlags(computeSizeFlags | ComputeSizeFlags::eShrinkWrap);
1731
0
    }
1732
0
  } else {
1733
0
    if (iStartIsAuto || iEndIsAuto) {
1734
0
      computeSizeFlags =
1735
0
        ComputeSizeFlags(computeSizeFlags | ComputeSizeFlags::eShrinkWrap);
1736
0
    }
1737
0
  }
1738
0
1739
0
  LogicalSize computedSize(wm);
1740
0
  {
1741
0
    AutoMaybeDisableFontInflation an(mFrame);
1742
0
1743
0
    computedSize =
1744
0
      mFrame->ComputeSize(mRenderingContext, wm, cbSize.ConvertTo(wm, cbwm),
1745
0
                         cbSize.ConvertTo(wm, cbwm).ISize(wm), // XXX or AvailableISize()?
1746
0
                         ComputedLogicalMargin().Size(wm) +
1747
0
                           ComputedLogicalOffsets().Size(wm),
1748
0
                         ComputedLogicalBorderPadding().Size(wm) -
1749
0
                           ComputedLogicalPadding().Size(wm),
1750
0
                         ComputedLogicalPadding().Size(wm),
1751
0
                         computeSizeFlags);
1752
0
    ComputedISize() = computedSize.ISize(wm);
1753
0
    ComputedBSize() = computedSize.BSize(wm);
1754
0
    NS_ASSERTION(ComputedISize() >= 0, "Bogus inline-size");
1755
0
    NS_ASSERTION(ComputedBSize() == NS_UNCONSTRAINEDSIZE ||
1756
0
                 ComputedBSize() >= 0, "Bogus block-size");
1757
0
  }
1758
0
  computedSize = computedSize.ConvertTo(cbwm, wm);
1759
0
1760
0
  // XXX Now that we have ComputeSize, can we condense many of the
1761
0
  // branches off of widthIsAuto?
1762
0
1763
0
  LogicalMargin margin = ComputedLogicalMargin().ConvertTo(cbwm, wm);
1764
0
  const LogicalMargin borderPadding =
1765
0
    ComputedLogicalBorderPadding().ConvertTo(cbwm, wm);
1766
0
1767
0
  bool iSizeIsAuto = eStyleUnit_Auto == mStylePosition->ISize(cbwm).GetUnit();
1768
0
  bool marginIStartIsAuto = false;
1769
0
  bool marginIEndIsAuto = false;
1770
0
  bool marginBStartIsAuto = false;
1771
0
  bool marginBEndIsAuto = false;
1772
0
  if (iStartIsAuto) {
1773
0
    // We know 'right' is not 'auto' anymore thanks to the hypothetical
1774
0
    // box code above.
1775
0
    // Solve for 'left'.
1776
0
    if (iSizeIsAuto) {
1777
0
      // XXXldb This, and the corresponding code in
1778
0
      // nsAbsoluteContainingBlock.cpp, could probably go away now that
1779
0
      // we always compute widths.
1780
0
      offsets.IStart(cbwm) = NS_AUTOOFFSET;
1781
0
    } else {
1782
0
      offsets.IStart(cbwm) =
1783
0
        cbSize.ISize(cbwm) - offsets.IEnd(cbwm) -
1784
0
        computedSize.ISize(cbwm) - margin.IStartEnd(cbwm) -
1785
0
        borderPadding.IStartEnd(cbwm);
1786
0
    }
1787
0
  } else if (iEndIsAuto) {
1788
0
    // We know 'left' is not 'auto' anymore thanks to the hypothetical
1789
0
    // box code above.
1790
0
    // Solve for 'right'.
1791
0
    if (iSizeIsAuto) {
1792
0
      // XXXldb This, and the corresponding code in
1793
0
      // nsAbsoluteContainingBlock.cpp, could probably go away now that
1794
0
      // we always compute widths.
1795
0
      offsets.IEnd(cbwm) = NS_AUTOOFFSET;
1796
0
    } else {
1797
0
      offsets.IEnd(cbwm) =
1798
0
        cbSize.ISize(cbwm) - offsets.IStart(cbwm) -
1799
0
        computedSize.ISize(cbwm) - margin.IStartEnd(cbwm) -
1800
0
        borderPadding.IStartEnd(cbwm);
1801
0
    }
1802
0
  } else {
1803
0
    // Neither 'inline-start' nor 'inline-end' is 'auto'.
1804
0
1805
0
    if (wm.IsOrthogonalTo(cbwm)) {
1806
0
      // For orthogonal blocks, we need to handle the case where the block had
1807
0
      // unconstrained block-size, which mapped to unconstrained inline-size
1808
0
      // in the containing block's writing mode.
1809
0
      nscoord autoISize = cbSize.ISize(cbwm) - margin.IStartEnd(cbwm) -
1810
0
        borderPadding.IStartEnd(cbwm) - offsets.IStartEnd(cbwm);
1811
0
      if (autoISize < 0) {
1812
0
        autoISize = 0;
1813
0
      }
1814
0
1815
0
      if (computedSize.ISize(cbwm) == NS_UNCONSTRAINEDSIZE) {
1816
0
        // For non-replaced elements with block-size auto, the block-size
1817
0
        // fills the remaining space.
1818
0
        computedSize.ISize(cbwm) = autoISize;
1819
0
1820
0
        // XXX Do these need box-sizing adjustments?
1821
0
        LogicalSize maxSize = ComputedMaxSize(cbwm);
1822
0
        LogicalSize minSize = ComputedMinSize(cbwm);
1823
0
        if (computedSize.ISize(cbwm) > maxSize.ISize(cbwm)) {
1824
0
          computedSize.ISize(cbwm) = maxSize.ISize(cbwm);
1825
0
        }
1826
0
        if (computedSize.ISize(cbwm) < minSize.ISize(cbwm)) {
1827
0
          computedSize.ISize(cbwm) = minSize.ISize(cbwm);
1828
0
        }
1829
0
      }
1830
0
    }
1831
0
1832
0
    // However, the inline-size might
1833
0
    // still not fill all the available space (even though we didn't
1834
0
    // shrink-wrap) in case:
1835
0
    //  * inline-size was specified
1836
0
    //  * we're dealing with a replaced element
1837
0
    //  * width was constrained by min- or max-inline-size.
1838
0
1839
0
    nscoord availMarginSpace =
1840
0
      aCBSize.ISize(cbwm) - offsets.IStartEnd(cbwm) - margin.IStartEnd(cbwm) -
1841
0
      borderPadding.IStartEnd(cbwm) - computedSize.ISize(cbwm);
1842
0
    marginIStartIsAuto =
1843
0
      eStyleUnit_Auto == mStyleMargin->mMargin.GetIStartUnit(cbwm);
1844
0
    marginIEndIsAuto =
1845
0
      eStyleUnit_Auto == mStyleMargin->mMargin.GetIEndUnit(cbwm);
1846
0
1847
0
    if (marginIStartIsAuto) {
1848
0
      if (marginIEndIsAuto) {
1849
0
        if (availMarginSpace < 0) {
1850
0
          // Note that this case is different from the neither-'auto'
1851
0
          // case below, where the spec says to ignore 'left'/'right'.
1852
0
          // Ignore the specified value for 'margin-right'.
1853
0
          margin.IEnd(cbwm) = availMarginSpace;
1854
0
        } else {
1855
0
          // Both 'margin-left' and 'margin-right' are 'auto', so they get
1856
0
          // equal values
1857
0
          margin.IStart(cbwm) = availMarginSpace / 2;
1858
0
          margin.IEnd(cbwm) = availMarginSpace - margin.IStart(cbwm);
1859
0
        }
1860
0
      } else {
1861
0
        // Just 'margin-left' is 'auto'
1862
0
        margin.IStart(cbwm) = availMarginSpace;
1863
0
      }
1864
0
    } else {
1865
0
      if (marginIEndIsAuto) {
1866
0
        // Just 'margin-right' is 'auto'
1867
0
        margin.IEnd(cbwm) = availMarginSpace;
1868
0
      } else {
1869
0
        // We're over-constrained so use the direction of the containing
1870
0
        // block to dictate which value to ignore.  (And note that the
1871
0
        // spec says to ignore 'left' or 'right' rather than
1872
0
        // 'margin-left' or 'margin-right'.)
1873
0
        // Note that this case is different from the both-'auto' case
1874
0
        // above, where the spec says to ignore
1875
0
        // 'margin-left'/'margin-right'.
1876
0
        // Ignore the specified value for 'right'.
1877
0
        offsets.IEnd(cbwm) += availMarginSpace;
1878
0
      }
1879
0
    }
1880
0
  }
1881
0
1882
0
  bool bSizeIsAuto = eStyleUnit_Auto == mStylePosition->BSize(cbwm).GetUnit();
1883
0
  if (bStartIsAuto) {
1884
0
    // solve for block-start
1885
0
    if (bSizeIsAuto) {
1886
0
      offsets.BStart(cbwm) = NS_AUTOOFFSET;
1887
0
    } else {
1888
0
      offsets.BStart(cbwm) = cbSize.BSize(cbwm) - margin.BStartEnd(cbwm) -
1889
0
        borderPadding.BStartEnd(cbwm) - computedSize.BSize(cbwm) -
1890
0
        offsets.BEnd(cbwm);
1891
0
    }
1892
0
  } else if (bEndIsAuto) {
1893
0
    // solve for block-end
1894
0
    if (bSizeIsAuto) {
1895
0
      offsets.BEnd(cbwm) = NS_AUTOOFFSET;
1896
0
    } else {
1897
0
      offsets.BEnd(cbwm) = cbSize.BSize(cbwm) - margin.BStartEnd(cbwm) -
1898
0
        borderPadding.BStartEnd(cbwm) - computedSize.BSize(cbwm) -
1899
0
        offsets.BStart(cbwm);
1900
0
    }
1901
0
  } else {
1902
0
    // Neither block-start nor -end is 'auto'.
1903
0
    nscoord autoBSize = cbSize.BSize(cbwm) - margin.BStartEnd(cbwm) -
1904
0
      borderPadding.BStartEnd(cbwm) - offsets.BStartEnd(cbwm);
1905
0
    if (autoBSize < 0) {
1906
0
      autoBSize = 0;
1907
0
    }
1908
0
1909
0
    if (computedSize.BSize(cbwm) == NS_UNCONSTRAINEDSIZE) {
1910
0
      // For non-replaced elements with block-size auto, the block-size
1911
0
      // fills the remaining space.
1912
0
      computedSize.BSize(cbwm) = autoBSize;
1913
0
1914
0
      // XXX Do these need box-sizing adjustments?
1915
0
      LogicalSize maxSize = ComputedMaxSize(cbwm);
1916
0
      LogicalSize minSize = ComputedMinSize(cbwm);
1917
0
      if (computedSize.BSize(cbwm) > maxSize.BSize(cbwm)) {
1918
0
        computedSize.BSize(cbwm) = maxSize.BSize(cbwm);
1919
0
      }
1920
0
      if (computedSize.BSize(cbwm) < minSize.BSize(cbwm)) {
1921
0
        computedSize.BSize(cbwm) = minSize.BSize(cbwm);
1922
0
      }
1923
0
    }
1924
0
1925
0
    // The block-size might still not fill all the available space in case:
1926
0
    //  * bsize was specified
1927
0
    //  * we're dealing with a replaced element
1928
0
    //  * bsize was constrained by min- or max-bsize.
1929
0
    nscoord availMarginSpace = autoBSize - computedSize.BSize(cbwm);
1930
0
    marginBStartIsAuto =
1931
0
      eStyleUnit_Auto == mStyleMargin->mMargin.GetBStartUnit(cbwm);
1932
0
    marginBEndIsAuto =
1933
0
      eStyleUnit_Auto == mStyleMargin->mMargin.GetBEndUnit(cbwm);
1934
0
1935
0
    if (marginBStartIsAuto) {
1936
0
      if (marginBEndIsAuto) {
1937
0
        // Both 'margin-top' and 'margin-bottom' are 'auto', so they get
1938
0
        // equal values
1939
0
        margin.BStart(cbwm) = availMarginSpace / 2;
1940
0
        margin.BEnd(cbwm) = availMarginSpace - margin.BStart(cbwm);
1941
0
      } else {
1942
0
        // Just margin-block-start is 'auto'
1943
0
        margin.BStart(cbwm) = availMarginSpace;
1944
0
      }
1945
0
    } else {
1946
0
      if (marginBEndIsAuto) {
1947
0
        // Just margin-block-end is 'auto'
1948
0
        margin.BEnd(cbwm) = availMarginSpace;
1949
0
      } else {
1950
0
        // We're over-constrained so ignore the specified value for
1951
0
        // block-end.  (And note that the spec says to ignore 'bottom'
1952
0
        // rather than 'margin-bottom'.)
1953
0
        offsets.BEnd(cbwm) += availMarginSpace;
1954
0
      }
1955
0
    }
1956
0
  }
1957
0
  ComputedBSize() = computedSize.ConvertTo(wm, cbwm).BSize(wm);
1958
0
  ComputedISize() = computedSize.ConvertTo(wm, cbwm).ISize(wm);
1959
0
1960
0
  SetComputedLogicalOffsets(offsets.ConvertTo(wm, cbwm));
1961
0
1962
0
  LogicalMargin marginInOurWM = margin.ConvertTo(wm, cbwm);
1963
0
  SetComputedLogicalMargin(marginInOurWM);
1964
0
1965
0
  // If we have auto margins, update our UsedMarginProperty. The property
1966
0
  // will have already been created by InitOffsets if it is needed.
1967
0
  if (marginIStartIsAuto || marginIEndIsAuto ||
1968
0
      marginBStartIsAuto || marginBEndIsAuto) {
1969
0
    nsMargin* propValue = mFrame->GetProperty(nsIFrame::UsedMarginProperty());
1970
0
    MOZ_ASSERT(propValue, "UsedMarginProperty should have been created "
1971
0
                          "by InitOffsets.");
1972
0
    *propValue = marginInOurWM.GetPhysicalMargin(wm);
1973
0
  }
1974
0
}
1975
1976
// This will not be converted to abstract coordinates because it's only
1977
// used in CalcQuirkContainingBlockHeight
1978
static nscoord
1979
GetBlockMarginBorderPadding(const ReflowInput* aReflowInput)
1980
0
{
1981
0
  nscoord result = 0;
1982
0
  if (!aReflowInput) return result;
1983
0
1984
0
  // zero auto margins
1985
0
  nsMargin margin = aReflowInput->ComputedPhysicalMargin();
1986
0
  if (NS_AUTOMARGIN == margin.top)
1987
0
    margin.top = 0;
1988
0
  if (NS_AUTOMARGIN == margin.bottom)
1989
0
    margin.bottom = 0;
1990
0
1991
0
  result += margin.top + margin.bottom;
1992
0
  result += aReflowInput->ComputedPhysicalBorderPadding().top +
1993
0
            aReflowInput->ComputedPhysicalBorderPadding().bottom;
1994
0
1995
0
  return result;
1996
0
}
1997
1998
/* Get the height based on the viewport of the containing block specified
1999
 * in aReflowInput when the containing block has mComputedHeight == NS_AUTOHEIGHT
2000
 * This will walk up the chain of containing blocks looking for a computed height
2001
 * until it finds the canvas frame, or it encounters a frame that is not a block,
2002
 * area, or scroll frame. This handles compatibility with IE (see bug 85016 and bug 219693)
2003
 *
2004
 * When we encounter scrolledContent block frames, we skip over them,
2005
 * since they are guaranteed to not be useful for computing the containing block.
2006
 *
2007
 * See also IsQuirkContainingBlockHeight.
2008
 */
2009
static nscoord
2010
CalcQuirkContainingBlockHeight(const ReflowInput* aCBReflowInput)
2011
0
{
2012
0
  const ReflowInput* firstAncestorRI = nullptr; // a candidate for html frame
2013
0
  const ReflowInput* secondAncestorRI = nullptr; // a candidate for body frame
2014
0
2015
0
  // initialize the default to NS_AUTOHEIGHT as this is the containings block
2016
0
  // computed height when this function is called. It is possible that we
2017
0
  // don't alter this height especially if we are restricted to one level
2018
0
  nscoord result = NS_AUTOHEIGHT;
2019
0
2020
0
  const ReflowInput* ri = aCBReflowInput;
2021
0
  for (; ri; ri = ri->mParentReflowInput) {
2022
0
    LayoutFrameType frameType = ri->mFrame->Type();
2023
0
    // if the ancestor is auto height then skip it and continue up if it
2024
0
    // is the first block frame and possibly the body/html
2025
0
    if (LayoutFrameType::Block == frameType ||
2026
0
#ifdef MOZ_XUL
2027
0
        LayoutFrameType::XULLabel == frameType ||
2028
0
#endif
2029
0
        LayoutFrameType::Scroll == frameType) {
2030
0
2031
0
      secondAncestorRI = firstAncestorRI;
2032
0
      firstAncestorRI = ri;
2033
0
2034
0
      // If the current frame we're looking at is positioned, we don't want to
2035
0
      // go any further (see bug 221784).  The behavior we want here is: 1) If
2036
0
      // not auto-height, use this as the percentage base.  2) If auto-height,
2037
0
      // keep looking, unless the frame is positioned.
2038
0
      if (NS_AUTOHEIGHT == ri->ComputedHeight()) {
2039
0
        if (ri->mFrame->IsAbsolutelyPositioned(ri->mStyleDisplay)) {
2040
0
          break;
2041
0
        } else {
2042
0
          continue;
2043
0
        }
2044
0
      }
2045
0
    } else if (LayoutFrameType::Canvas == frameType) {
2046
0
      // Always continue on to the height calculation
2047
0
    } else if (LayoutFrameType::PageContent == frameType) {
2048
0
      nsIFrame* prevInFlow = ri->mFrame->GetPrevInFlow();
2049
0
      // only use the page content frame for a height basis if it is the first in flow
2050
0
      if (prevInFlow)
2051
0
        break;
2052
0
    }
2053
0
    else {
2054
0
      break;
2055
0
    }
2056
0
2057
0
    // if the ancestor is the page content frame then the percent base is
2058
0
    // the avail height, otherwise it is the computed height
2059
0
    result = (LayoutFrameType::PageContent == frameType) ? ri->AvailableHeight()
2060
0
                                                         : ri->ComputedHeight();
2061
0
    // if unconstrained - don't sutract borders - would result in huge height
2062
0
    if (NS_AUTOHEIGHT == result) return result;
2063
0
2064
0
    // if we got to the canvas or page content frame, then subtract out
2065
0
    // margin/border/padding for the BODY and HTML elements
2066
0
    if ((LayoutFrameType::Canvas == frameType) ||
2067
0
        (LayoutFrameType::PageContent == frameType)) {
2068
0
2069
0
      result -= GetBlockMarginBorderPadding(firstAncestorRI);
2070
0
      result -= GetBlockMarginBorderPadding(secondAncestorRI);
2071
0
2072
#ifdef DEBUG
2073
      // make sure the first ancestor is the HTML and the second is the BODY
2074
      if (firstAncestorRI) {
2075
        nsIContent* frameContent = firstAncestorRI->mFrame->GetContent();
2076
        if (frameContent) {
2077
          NS_ASSERTION(frameContent->IsHTMLElement(nsGkAtoms::html),
2078
                       "First ancestor is not HTML");
2079
        }
2080
      }
2081
      if (secondAncestorRI) {
2082
        nsIContent* frameContent = secondAncestorRI->mFrame->GetContent();
2083
        if (frameContent) {
2084
          NS_ASSERTION(frameContent->IsHTMLElement(nsGkAtoms::body),
2085
                       "Second ancestor is not BODY");
2086
        }
2087
      }
2088
#endif
2089
2090
0
    }
2091
0
    // if we got to the html frame (a block child of the canvas) ...
2092
0
    else if (LayoutFrameType::Block == frameType && ri->mParentReflowInput &&
2093
0
             ri->mParentReflowInput->mFrame->IsCanvasFrame()) {
2094
0
      // ... then subtract out margin/border/padding for the BODY element
2095
0
      result -= GetBlockMarginBorderPadding(secondAncestorRI);
2096
0
    }
2097
0
    break;
2098
0
  }
2099
0
2100
0
  // Make sure not to return a negative height here!
2101
0
  return std::max(result, 0);
2102
0
}
2103
2104
// Called by InitConstraints() to compute the containing block rectangle for
2105
// the element. Handles the special logic for absolutely positioned elements
2106
LogicalSize
2107
ReflowInput::ComputeContainingBlockRectangle(
2108
                     nsPresContext*           aPresContext,
2109
                     const ReflowInput* aContainingBlockRI) const
2110
0
{
2111
0
  // Unless the element is absolutely positioned, the containing block is
2112
0
  // formed by the content edge of the nearest block-level ancestor
2113
0
  LogicalSize cbSize = aContainingBlockRI->ComputedSize();
2114
0
2115
0
  WritingMode wm = aContainingBlockRI->GetWritingMode();
2116
0
2117
0
  // mFrameType for abs-pos tables is NS_CSS_FRAME_TYPE_BLOCK, so we need to
2118
0
  // special case them here.
2119
0
  if (NS_FRAME_GET_TYPE(mFrameType) == NS_CSS_FRAME_TYPE_ABSOLUTE ||
2120
0
      (mFrame->IsTableFrame() && mFrame->IsAbsolutelyPositioned(mStyleDisplay) &&
2121
0
       (mFrame->GetParent()->GetStateBits() & NS_FRAME_OUT_OF_FLOW))) {
2122
0
    // See if the ancestor is block-level or inline-level
2123
0
    if (NS_FRAME_GET_TYPE(aContainingBlockRI->mFrameType) == NS_CSS_FRAME_TYPE_INLINE) {
2124
0
      // Base our size on the actual size of the frame.  In cases when this is
2125
0
      // completely bogus (eg initial reflow), this code shouldn't even be
2126
0
      // called, since the code in nsInlineFrame::Reflow will pass in
2127
0
      // the containing block dimensions to our constructor.
2128
0
      // XXXbz we should be taking the in-flows into account too, but
2129
0
      // that's very hard.
2130
0
2131
0
      LogicalMargin computedBorder =
2132
0
        aContainingBlockRI->ComputedLogicalBorderPadding() -
2133
0
        aContainingBlockRI->ComputedLogicalPadding();
2134
0
      cbSize.ISize(wm) = aContainingBlockRI->mFrame->ISize(wm) -
2135
0
                         computedBorder.IStartEnd(wm);
2136
0
      NS_ASSERTION(cbSize.ISize(wm) >= 0,
2137
0
                   "Negative containing block isize!");
2138
0
      cbSize.BSize(wm) = aContainingBlockRI->mFrame->BSize(wm) -
2139
0
                         computedBorder.BStartEnd(wm);
2140
0
      NS_ASSERTION(cbSize.BSize(wm) >= 0,
2141
0
                   "Negative containing block bsize!");
2142
0
    } else {
2143
0
      // If the ancestor is block-level, the containing block is formed by the
2144
0
      // padding edge of the ancestor
2145
0
      cbSize.ISize(wm) +=
2146
0
        aContainingBlockRI->ComputedLogicalPadding().IStartEnd(wm);
2147
0
      cbSize.BSize(wm) +=
2148
0
        aContainingBlockRI->ComputedLogicalPadding().BStartEnd(wm);
2149
0
    }
2150
0
  } else {
2151
0
    // an element in quirks mode gets a containing block based on looking for a
2152
0
    // parent with a non-auto height if the element has a percent height
2153
0
    // Note: We don't emulate this quirk for percents in calc() or in
2154
0
    // vertical writing modes.
2155
0
    if (!wm.IsVertical() &&
2156
0
        NS_AUTOHEIGHT == cbSize.BSize(wm)) {
2157
0
      if (eCompatibility_NavQuirks == aPresContext->CompatibilityMode() &&
2158
0
          (mStylePosition->mHeight.GetUnit() == eStyleUnit_Percent ||
2159
0
           (mFrame->IsTableWrapperFrame() &&
2160
0
            mFrame->PrincipalChildList().FirstChild()->StylePosition()->
2161
0
              mHeight.GetUnit() == eStyleUnit_Percent))) {
2162
0
        cbSize.BSize(wm) = CalcQuirkContainingBlockHeight(aContainingBlockRI);
2163
0
      }
2164
0
    }
2165
0
  }
2166
0
2167
0
  return cbSize.ConvertTo(GetWritingMode(), wm);
2168
0
}
2169
2170
static eNormalLineHeightControl GetNormalLineHeightCalcControl(void)
2171
0
{
2172
0
  if (sNormalLineHeightControl == eUninitialized) {
2173
0
    // browser.display.normal_lineheight_calc_control is not user
2174
0
    // changeable, so no need to register callback for it.
2175
0
    int32_t val =
2176
0
      Preferences::GetInt("browser.display.normal_lineheight_calc_control",
2177
0
                          eNoExternalLeading);
2178
0
    sNormalLineHeightControl = static_cast<eNormalLineHeightControl>(val);
2179
0
  }
2180
0
  return sNormalLineHeightControl;
2181
0
}
2182
2183
static inline bool
2184
IsSideCaption(nsIFrame* aFrame, const nsStyleDisplay* aStyleDisplay,
2185
              WritingMode aWM)
2186
0
{
2187
0
  if (aStyleDisplay->mDisplay != StyleDisplay::TableCaption) {
2188
0
    return false;
2189
0
  }
2190
0
  uint8_t captionSide = aFrame->StyleTableBorder()->mCaptionSide;
2191
0
  return captionSide == NS_STYLE_CAPTION_SIDE_LEFT ||
2192
0
         captionSide == NS_STYLE_CAPTION_SIDE_RIGHT;
2193
0
}
2194
2195
// XXX refactor this code to have methods for each set of properties
2196
// we are computing: width,height,line-height; margin; offsets
2197
2198
void
2199
ReflowInput::InitConstraints(nsPresContext* aPresContext,
2200
                             const LogicalSize& aContainingBlockSize,
2201
                             const nsMargin* aBorder,
2202
                             const nsMargin* aPadding,
2203
                             LayoutFrameType aFrameType)
2204
0
{
2205
0
  WritingMode wm = GetWritingMode();
2206
0
  DISPLAY_INIT_CONSTRAINTS(mFrame, this,
2207
0
                           aContainingBlockSize.ISize(wm),
2208
0
                           aContainingBlockSize.BSize(wm),
2209
0
                           aBorder, aPadding);
2210
0
2211
0
  // If this is a reflow root, then set the computed width and
2212
0
  // height equal to the available space
2213
0
  if (nullptr == mParentReflowInput || mFlags.mDummyParentReflowInput) {
2214
0
    // XXXldb This doesn't mean what it used to!
2215
0
    InitOffsets(wm, aContainingBlockSize.ISize(wm),
2216
0
                aFrameType, mFlags, aBorder, aPadding, mStyleDisplay);
2217
0
    // Override mComputedMargin since reflow roots start from the
2218
0
    // frame's boundary, which is inside the margin.
2219
0
    ComputedPhysicalMargin().SizeTo(0, 0, 0, 0);
2220
0
    ComputedPhysicalOffsets().SizeTo(0, 0, 0, 0);
2221
0
2222
0
    ComputedISize() =
2223
0
      AvailableISize() - ComputedLogicalBorderPadding().IStartEnd(wm);
2224
0
    if (ComputedISize() < 0) {
2225
0
      ComputedISize() = 0;
2226
0
    }
2227
0
    if (AvailableBSize() != NS_UNCONSTRAINEDSIZE) {
2228
0
      ComputedBSize() =
2229
0
        AvailableBSize() - ComputedLogicalBorderPadding().BStartEnd(wm);
2230
0
      if (ComputedBSize() < 0) {
2231
0
        ComputedBSize() = 0;
2232
0
      }
2233
0
    } else {
2234
0
      ComputedBSize() = NS_UNCONSTRAINEDSIZE;
2235
0
    }
2236
0
2237
0
    ComputedMinWidth() = ComputedMinHeight() = 0;
2238
0
    ComputedMaxWidth() = ComputedMaxHeight() = NS_UNCONSTRAINEDSIZE;
2239
0
  } else {
2240
0
    // Get the containing block reflow state
2241
0
    const ReflowInput* cbri = mCBReflowInput;
2242
0
    MOZ_ASSERT(cbri, "no containing block");
2243
0
    MOZ_ASSERT(mFrame->GetParent());
2244
0
2245
0
    // If we weren't given a containing block width and height, then
2246
0
    // compute one
2247
0
    LogicalSize cbSize = (aContainingBlockSize == LogicalSize(wm, -1, -1))
2248
0
      ? ComputeContainingBlockRectangle(aPresContext, cbri)
2249
0
      : aContainingBlockSize;
2250
0
2251
0
    // See if the containing block height is based on the size of its
2252
0
    // content
2253
0
    if (NS_AUTOHEIGHT == cbSize.BSize(wm)) {
2254
0
      // See if the containing block is a cell frame which needs
2255
0
      // to use the mComputedHeight of the cell instead of what the cell block passed in.
2256
0
      // XXX It seems like this could lead to bugs with min-height and friends
2257
0
      if (cbri->mParentReflowInput) {
2258
0
        if (IsTableCell(cbri->mFrame->Type())) {
2259
0
          // use the cell's computed block size
2260
0
          cbSize.BSize(wm) = cbri->ComputedSize(wm).BSize(wm);
2261
0
        }
2262
0
      }
2263
0
    }
2264
0
2265
0
    // XXX Might need to also pass the CB height (not width) for page boxes,
2266
0
    // too, if we implement them.
2267
0
2268
0
    // For calculating positioning offsets, margins, borders and
2269
0
    // padding, we use the writing mode of the containing block
2270
0
    WritingMode cbwm = cbri->GetWritingMode();
2271
0
    InitOffsets(cbwm, cbSize.ConvertTo(cbwm, wm).ISize(cbwm),
2272
0
                aFrameType, mFlags, aBorder, aPadding, mStyleDisplay);
2273
0
2274
0
    // For calculating the size of this box, we use its own writing mode
2275
0
    const nsStyleCoord &blockSize = mStylePosition->BSize(wm);
2276
0
    nsStyleUnit blockSizeUnit = blockSize.GetUnit();
2277
0
2278
0
    // Check for a percentage based block size and a containing block
2279
0
    // block size that depends on the content block size
2280
0
    // XXX twiddling blockSizeUnit doesn't help anymore
2281
0
    // FIXME Shouldn't we fix that?
2282
0
    if (blockSize.HasPercent()) {
2283
0
      if (NS_AUTOHEIGHT == cbSize.BSize(wm)) {
2284
0
        // this if clause enables %-blockSize on replaced inline frames,
2285
0
        // such as images.  See bug 54119.  The else clause "blockSizeUnit = eStyleUnit_Auto;"
2286
0
        // used to be called exclusively.
2287
0
        if (NS_FRAME_REPLACED(NS_CSS_FRAME_TYPE_INLINE) == mFrameType ||
2288
0
            NS_FRAME_REPLACED_CONTAINS_BLOCK(
2289
0
                NS_CSS_FRAME_TYPE_INLINE) == mFrameType) {
2290
0
          // Get the containing block reflow state
2291
0
          NS_ASSERTION(nullptr != cbri, "no containing block");
2292
0
          // in quirks mode, get the cb height using the special quirk method
2293
0
          if (!wm.IsVertical() &&
2294
0
              eCompatibility_NavQuirks == aPresContext->CompatibilityMode()) {
2295
0
            if (!IsTableCell(cbri->mFrame->Type())) {
2296
0
              cbSize.BSize(wm) = CalcQuirkContainingBlockHeight(cbri);
2297
0
              if (cbSize.BSize(wm) == NS_AUTOHEIGHT) {
2298
0
                blockSizeUnit = eStyleUnit_Auto;
2299
0
              }
2300
0
            }
2301
0
            else {
2302
0
              blockSizeUnit = eStyleUnit_Auto;
2303
0
            }
2304
0
          }
2305
0
          // in standard mode, use the cb block size.  if it's "auto",
2306
0
          // as will be the case by default in BODY, use auto block size
2307
0
          // as per CSS2 spec.
2308
0
          else
2309
0
          {
2310
0
            nscoord computedBSize = cbri->ComputedSize(wm).BSize(wm);
2311
0
            if (NS_AUTOHEIGHT != computedBSize) {
2312
0
              cbSize.BSize(wm) = computedBSize;
2313
0
            }
2314
0
            else {
2315
0
              blockSizeUnit = eStyleUnit_Auto;
2316
0
            }
2317
0
          }
2318
0
        }
2319
0
        else {
2320
0
          // default to interpreting the blockSize like 'auto'
2321
0
          blockSizeUnit = eStyleUnit_Auto;
2322
0
        }
2323
0
      }
2324
0
    }
2325
0
2326
0
    // Compute our offsets if the element is relatively positioned.  We
2327
0
    // need the correct containing block inline-size and block-size
2328
0
    // here, which is why we need to do it after all the quirks-n-such
2329
0
    // above. (If the element is sticky positioned, we need to wait
2330
0
    // until the scroll container knows its size, so we compute offsets
2331
0
    // from StickyScrollContainer::UpdatePositions.)
2332
0
    if (mStyleDisplay->IsRelativelyPositioned(mFrame) &&
2333
0
        NS_STYLE_POSITION_RELATIVE == mStyleDisplay->mPosition) {
2334
0
      ComputeRelativeOffsets(cbwm, mFrame, cbSize.ConvertTo(cbwm, wm),
2335
0
                             ComputedPhysicalOffsets());
2336
0
    } else {
2337
0
      // Initialize offsets to 0
2338
0
      ComputedPhysicalOffsets().SizeTo(0, 0, 0, 0);
2339
0
    }
2340
0
2341
0
    // Calculate the computed values for min and max properties.  Note that
2342
0
    // this MUST come after we've computed our border and padding.
2343
0
    ComputeMinMaxValues(cbSize);
2344
0
2345
0
    // Calculate the computed inlineSize and blockSize.
2346
0
    // This varies by frame type.
2347
0
2348
0
    if (NS_CSS_FRAME_TYPE_INTERNAL_TABLE == mFrameType) {
2349
0
      // Internal table elements. The rules vary depending on the type.
2350
0
      // Calculate the computed isize
2351
0
      bool rowOrRowGroup = false;
2352
0
      const nsStyleCoord &inlineSize = mStylePosition->ISize(wm);
2353
0
      nsStyleUnit inlineSizeUnit = inlineSize.GetUnit();
2354
0
      if ((StyleDisplay::TableRow == mStyleDisplay->mDisplay) ||
2355
0
          (StyleDisplay::TableRowGroup == mStyleDisplay->mDisplay)) {
2356
0
        // 'inlineSize' property doesn't apply to table rows and row groups
2357
0
        inlineSizeUnit = eStyleUnit_Auto;
2358
0
        rowOrRowGroup = true;
2359
0
      }
2360
0
2361
0
      // calc() with percentages acts like auto on internal table elements
2362
0
      if (eStyleUnit_Auto == inlineSizeUnit ||
2363
0
          (inlineSize.IsCalcUnit() && inlineSize.CalcHasPercent())) {
2364
0
        ComputedISize() = AvailableISize();
2365
0
2366
0
        if ((ComputedISize() != NS_UNCONSTRAINEDSIZE) && !rowOrRowGroup){
2367
0
          // Internal table elements don't have margins. Only tables and
2368
0
          // cells have border and padding
2369
0
          ComputedISize() -= ComputedLogicalBorderPadding().IStartEnd(wm);
2370
0
          if (ComputedISize() < 0)
2371
0
            ComputedISize() = 0;
2372
0
        }
2373
0
        NS_ASSERTION(ComputedISize() >= 0, "Bogus computed isize");
2374
0
2375
0
      } else {
2376
0
        NS_ASSERTION(inlineSizeUnit == inlineSize.GetUnit(),
2377
0
                     "unexpected inline size unit change");
2378
0
        ComputedISize() = ComputeISizeValue(cbSize.ISize(wm),
2379
0
                                            mStylePosition->mBoxSizing,
2380
0
                                            inlineSize);
2381
0
      }
2382
0
2383
0
      // Calculate the computed block size
2384
0
      if ((StyleDisplay::TableColumn == mStyleDisplay->mDisplay) ||
2385
0
          (StyleDisplay::TableColumnGroup == mStyleDisplay->mDisplay)) {
2386
0
        // 'blockSize' property doesn't apply to table columns and column groups
2387
0
        blockSizeUnit = eStyleUnit_Auto;
2388
0
      }
2389
0
      // calc() with percentages acts like 'auto' on internal table elements
2390
0
      if (eStyleUnit_Auto == blockSizeUnit ||
2391
0
          (blockSize.IsCalcUnit() && blockSize.CalcHasPercent())) {
2392
0
        ComputedBSize() = NS_AUTOHEIGHT;
2393
0
      } else {
2394
0
        NS_ASSERTION(blockSizeUnit == blockSize.GetUnit(),
2395
0
                     "unexpected block size unit change");
2396
0
        ComputedBSize() = ComputeBSizeValue(cbSize.BSize(wm),
2397
0
                                            mStylePosition->mBoxSizing,
2398
0
                                            blockSize);
2399
0
      }
2400
0
2401
0
      // Doesn't apply to table elements
2402
0
      ComputedMinWidth() = ComputedMinHeight() = 0;
2403
0
      ComputedMaxWidth() = ComputedMaxHeight() = NS_UNCONSTRAINEDSIZE;
2404
0
2405
0
    } else if (NS_FRAME_GET_TYPE(mFrameType) == NS_CSS_FRAME_TYPE_ABSOLUTE) {
2406
0
      // XXX not sure if this belongs here or somewhere else - cwk
2407
0
      InitAbsoluteConstraints(aPresContext, cbri,
2408
0
                              cbSize.ConvertTo(cbri->GetWritingMode(), wm),
2409
0
                              aFrameType);
2410
0
    } else {
2411
0
      AutoMaybeDisableFontInflation an(mFrame);
2412
0
2413
0
      bool isBlock = NS_CSS_FRAME_TYPE_BLOCK == NS_FRAME_GET_TYPE(mFrameType);
2414
0
      typedef nsIFrame::ComputeSizeFlags ComputeSizeFlags;
2415
0
      ComputeSizeFlags computeSizeFlags =
2416
0
        isBlock ? ComputeSizeFlags::eDefault : ComputeSizeFlags::eShrinkWrap;
2417
0
      if (mFlags.mIClampMarginBoxMinSize) {
2418
0
        computeSizeFlags = ComputeSizeFlags(computeSizeFlags |
2419
0
                             ComputeSizeFlags::eIClampMarginBoxMinSize);
2420
0
      }
2421
0
      if (mFlags.mBClampMarginBoxMinSize) {
2422
0
        computeSizeFlags = ComputeSizeFlags(computeSizeFlags |
2423
0
                             ComputeSizeFlags::eBClampMarginBoxMinSize);
2424
0
      }
2425
0
      if (mFlags.mApplyAutoMinSize) {
2426
0
        computeSizeFlags = ComputeSizeFlags(computeSizeFlags |
2427
0
                             ComputeSizeFlags::eIApplyAutoMinSize);
2428
0
      }
2429
0
      if (mFlags.mShrinkWrap) {
2430
0
        computeSizeFlags =
2431
0
          ComputeSizeFlags(computeSizeFlags | ComputeSizeFlags::eShrinkWrap);
2432
0
      }
2433
0
      if (mFlags.mUseAutoBSize) {
2434
0
        computeSizeFlags =
2435
0
          ComputeSizeFlags(computeSizeFlags | ComputeSizeFlags::eUseAutoBSize);
2436
0
      }
2437
0
2438
0
      nsIFrame* alignCB = mFrame->GetParent();
2439
0
      if (alignCB->IsTableWrapperFrame() && alignCB->GetParent()) {
2440
0
        // XXX grid-specific for now; maybe remove this check after we address bug 799725
2441
0
        if (alignCB->GetParent()->IsGridContainerFrame()) {
2442
0
          alignCB = alignCB->GetParent();
2443
0
        }
2444
0
      }
2445
0
      if (alignCB->IsGridContainerFrame()) {
2446
0
        // Shrink-wrap grid items that will be aligned (rather than stretched)
2447
0
        // in its inline axis.
2448
0
        auto inlineAxisAlignment =
2449
0
          wm.IsOrthogonalTo(cbwm)
2450
0
            ? mStylePosition->UsedAlignSelf(alignCB->Style())
2451
0
            : mStylePosition->UsedJustifySelf(alignCB->Style());
2452
0
        if ((inlineAxisAlignment != NS_STYLE_ALIGN_STRETCH &&
2453
0
             inlineAxisAlignment != NS_STYLE_ALIGN_NORMAL) ||
2454
0
            mStyleMargin->mMargin.GetIStartUnit(wm) == eStyleUnit_Auto ||
2455
0
            mStyleMargin->mMargin.GetIEndUnit(wm) == eStyleUnit_Auto) {
2456
0
          computeSizeFlags =
2457
0
            ComputeSizeFlags(computeSizeFlags | ComputeSizeFlags::eShrinkWrap);
2458
0
        }
2459
0
      } else {
2460
0
        // Make sure legend frames with display:block and width:auto still
2461
0
        // shrink-wrap.
2462
0
        // Also shrink-wrap blocks that are orthogonal to their container.
2463
0
        if (isBlock &&
2464
0
            ((aFrameType == LayoutFrameType::Legend &&
2465
0
              mFrame->Style()->GetPseudo() != nsCSSAnonBoxes::scrolledContent()) ||
2466
0
             (aFrameType == LayoutFrameType::Scroll &&
2467
0
              mFrame->GetContentInsertionFrame()->IsLegendFrame()) ||
2468
0
             (mCBReflowInput &&
2469
0
              mCBReflowInput->GetWritingMode().IsOrthogonalTo(mWritingMode)))) {
2470
0
          computeSizeFlags =
2471
0
            ComputeSizeFlags(computeSizeFlags | ComputeSizeFlags::eShrinkWrap);
2472
0
        }
2473
0
2474
0
        if (alignCB->IsFlexContainerFrame()) {
2475
0
          computeSizeFlags =
2476
0
            ComputeSizeFlags(computeSizeFlags | ComputeSizeFlags::eShrinkWrap);
2477
0
2478
0
          // If we're inside of a flex container that needs to measure our
2479
0
          // auto BSize, pass that information along to ComputeSize().
2480
0
          if (mFlags.mIsFlexContainerMeasuringBSize) {
2481
0
            computeSizeFlags =
2482
0
              ComputeSizeFlags(computeSizeFlags | ComputeSizeFlags::eUseAutoBSize);
2483
0
          }
2484
0
        } else {
2485
0
          MOZ_ASSERT(!mFlags.mIsFlexContainerMeasuringBSize,
2486
0
                     "We're not in a flex container, so the flag "
2487
0
                     "'mIsFlexContainerMeasuringBSize' shouldn't be set");
2488
0
        }
2489
0
      }
2490
0
2491
0
      if (cbSize.ISize(wm) == NS_UNCONSTRAINEDSIZE) {
2492
0
        // For orthogonal flows, where we found a parent orthogonal-limit
2493
0
        // for AvailableISize() in Init(), we'll use the same here as well.
2494
0
        cbSize.ISize(wm) = AvailableISize();
2495
0
      }
2496
0
2497
0
      LogicalSize size =
2498
0
        mFrame->ComputeSize(mRenderingContext, wm, cbSize, AvailableISize(),
2499
0
                           ComputedLogicalMargin().Size(wm),
2500
0
                           ComputedLogicalBorderPadding().Size(wm) -
2501
0
                             ComputedLogicalPadding().Size(wm),
2502
0
                           ComputedLogicalPadding().Size(wm),
2503
0
                           computeSizeFlags);
2504
0
2505
0
      ComputedISize() = size.ISize(wm);
2506
0
      ComputedBSize() = size.BSize(wm);
2507
0
      NS_ASSERTION(ComputedISize() >= 0, "Bogus inline-size");
2508
0
      NS_ASSERTION(ComputedBSize() == NS_UNCONSTRAINEDSIZE ||
2509
0
                   ComputedBSize() >= 0, "Bogus block-size");
2510
0
2511
0
      // Exclude inline tables, side captions, flex and grid items from block
2512
0
      // margin calculations.
2513
0
      if (isBlock && !IsSideCaption(mFrame, mStyleDisplay, cbwm) &&
2514
0
          mStyleDisplay->mDisplay != StyleDisplay::InlineTable &&
2515
0
          !alignCB->IsFlexOrGridContainer()) {
2516
0
        CalculateBlockSideMargins(aFrameType);
2517
0
      }
2518
0
    }
2519
0
  }
2520
0
2521
0
  // Save our containing block dimensions
2522
0
  mContainingBlockSize = aContainingBlockSize;
2523
0
}
2524
2525
static void
2526
UpdateProp(nsIFrame* aFrame,
2527
           const FramePropertyDescriptor<nsMargin>* aProperty,
2528
           bool aNeeded,
2529
           const nsMargin& aNewValue)
2530
0
{
2531
0
  if (aNeeded) {
2532
0
    nsMargin* propValue = aFrame->GetProperty(aProperty);
2533
0
    if (propValue) {
2534
0
      *propValue = aNewValue;
2535
0
    } else {
2536
0
      aFrame->AddProperty(aProperty, new nsMargin(aNewValue));
2537
0
    }
2538
0
  } else {
2539
0
    aFrame->DeleteProperty(aProperty);
2540
0
  }
2541
0
}
2542
2543
void
2544
SizeComputationInput::InitOffsets(WritingMode aWM,
2545
                                  nscoord aPercentBasis,
2546
                                  LayoutFrameType aFrameType,
2547
                                  ReflowInputFlags aFlags,
2548
                                  const nsMargin* aBorder,
2549
                                  const nsMargin* aPadding,
2550
                                  const nsStyleDisplay* aDisplay)
2551
0
{
2552
0
  DISPLAY_INIT_OFFSETS(mFrame, this, aPercentBasis, aWM, aBorder, aPadding);
2553
0
2554
0
  // Since we are in reflow, we don't need to store these properties anymore
2555
0
  // unless they are dependent on width, in which case we store the new value.
2556
0
  nsPresContext *presContext = mFrame->PresContext();
2557
0
  mFrame->DeleteProperty(nsIFrame::UsedBorderProperty());
2558
0
2559
0
  // Compute margins from the specified margin style information. These
2560
0
  // become the default computed values, and may be adjusted below
2561
0
  // XXX fix to provide 0,0 for the top&bottom margins for
2562
0
  // inline-non-replaced elements
2563
0
  bool needMarginProp = ComputeMargin(aWM, aPercentBasis);
2564
0
  // Note that ComputeMargin() simplistically resolves 'auto' margins to 0.
2565
0
  // In formatting contexts where this isn't correct, some later code will
2566
0
  // need to update the UsedMargin() property with the actual resolved value.
2567
0
  // One example of this is ::CalculateBlockSideMargins().
2568
0
  ::UpdateProp(mFrame, nsIFrame::UsedMarginProperty(), needMarginProp,
2569
0
               ComputedPhysicalMargin());
2570
0
2571
0
2572
0
  const nsStyleDisplay* disp = mFrame->StyleDisplayWithOptionalParam(aDisplay);
2573
0
  bool isThemed = mFrame->IsThemed(disp);
2574
0
  bool needPaddingProp;
2575
0
  LayoutDeviceIntMargin widgetPadding;
2576
0
  if (isThemed &&
2577
0
      presContext->GetTheme()->GetWidgetPadding(presContext->DeviceContext(),
2578
0
                                                mFrame, disp->mAppearance,
2579
0
                                                &widgetPadding)) {
2580
0
    ComputedPhysicalPadding() =
2581
0
      LayoutDevicePixel::ToAppUnits(widgetPadding,
2582
0
                                    presContext->AppUnitsPerDevPixel());
2583
0
    needPaddingProp = false;
2584
0
  }
2585
0
  else if (nsSVGUtils::IsInSVGTextSubtree(mFrame)) {
2586
0
    ComputedPhysicalPadding().SizeTo(0, 0, 0, 0);
2587
0
    needPaddingProp = false;
2588
0
  }
2589
0
  else if (aPadding) { // padding is an input arg
2590
0
    ComputedPhysicalPadding() = *aPadding;
2591
0
    needPaddingProp = mFrame->StylePadding()->IsWidthDependent() ||
2592
0
    (mFrame->GetStateBits() & NS_FRAME_REFLOW_ROOT);
2593
0
  }
2594
0
  else {
2595
0
    needPaddingProp = ComputePadding(aWM, aPercentBasis, aFrameType);
2596
0
  }
2597
0
2598
0
  // Add [align|justify]-content:baseline padding contribution.
2599
0
  typedef const FramePropertyDescriptor<SmallValueHolder<nscoord>>* Prop;
2600
0
  auto ApplyBaselinePadding = [this, &needPaddingProp]
2601
0
         (LogicalAxis aAxis, Prop aProp) {
2602
0
    bool found;
2603
0
    nscoord val = mFrame->GetProperty(aProp, &found);
2604
0
    if (found) {
2605
0
      NS_ASSERTION(val != nscoord(0), "zero in this property is useless");
2606
0
      WritingMode wm = GetWritingMode();
2607
0
      LogicalSide side;
2608
0
      if (val > 0) {
2609
0
        side = MakeLogicalSide(aAxis, eLogicalEdgeStart);
2610
0
      } else {
2611
0
        side = MakeLogicalSide(aAxis, eLogicalEdgeEnd);
2612
0
        val = -val;
2613
0
      }
2614
0
      mComputedPadding.Side(wm.PhysicalSide(side)) += val;
2615
0
      needPaddingProp = true;
2616
0
    }
2617
0
  };
2618
0
  if (!aFlags.mUseAutoBSize) {
2619
0
    ApplyBaselinePadding(eLogicalAxisBlock, nsIFrame::BBaselinePadProperty());
2620
0
  }
2621
0
  if (!aFlags.mShrinkWrap) {
2622
0
    ApplyBaselinePadding(eLogicalAxisInline, nsIFrame::IBaselinePadProperty());
2623
0
  }
2624
0
2625
0
  if (isThemed) {
2626
0
    LayoutDeviceIntMargin border =
2627
0
      presContext->GetTheme()->GetWidgetBorder(presContext->DeviceContext(),
2628
0
                                               mFrame, disp->mAppearance);
2629
0
    ComputedPhysicalBorderPadding() =
2630
0
      LayoutDevicePixel::ToAppUnits(border,
2631
0
                                    presContext->AppUnitsPerDevPixel());
2632
0
  }
2633
0
  else if (nsSVGUtils::IsInSVGTextSubtree(mFrame)) {
2634
0
    ComputedPhysicalBorderPadding().SizeTo(0, 0, 0, 0);
2635
0
  }
2636
0
  else if (aBorder) {  // border is an input arg
2637
0
    ComputedPhysicalBorderPadding() = *aBorder;
2638
0
  }
2639
0
  else {
2640
0
    ComputedPhysicalBorderPadding() = mFrame->StyleBorder()->GetComputedBorder();
2641
0
  }
2642
0
  ComputedPhysicalBorderPadding() += ComputedPhysicalPadding();
2643
0
2644
0
  if (aFrameType == LayoutFrameType::Table) {
2645
0
    nsTableFrame *tableFrame = static_cast<nsTableFrame*>(mFrame);
2646
0
2647
0
    if (tableFrame->IsBorderCollapse()) {
2648
0
      // border-collapsed tables don't use any of their padding, and
2649
0
      // only part of their border.  We need to do this here before we
2650
0
      // try to do anything like handling 'auto' widths,
2651
0
      // 'box-sizing', or 'auto' margins.
2652
0
      ComputedPhysicalPadding().SizeTo(0,0,0,0);
2653
0
      SetComputedLogicalBorderPadding(
2654
0
        tableFrame->GetIncludedOuterBCBorder(mWritingMode));
2655
0
    }
2656
0
2657
0
    // The margin is inherited to the table wrapper frame via
2658
0
    // the ::-moz-table-wrapper rule in ua.css.
2659
0
    ComputedPhysicalMargin().SizeTo(0, 0, 0, 0);
2660
0
  } else if (aFrameType == LayoutFrameType::Scrollbar) {
2661
0
    // scrollbars may have had their width or height smashed to zero
2662
0
    // by the associated scrollframe, in which case we must not report
2663
0
    // any padding or border.
2664
0
    nsSize size(mFrame->GetSize());
2665
0
    if (size.width == 0 || size.height == 0) {
2666
0
      ComputedPhysicalPadding().SizeTo(0,0,0,0);
2667
0
      ComputedPhysicalBorderPadding().SizeTo(0,0,0,0);
2668
0
    }
2669
0
  }
2670
0
  ::UpdateProp(mFrame, nsIFrame::UsedPaddingProperty(), needPaddingProp,
2671
0
               ComputedPhysicalPadding());
2672
0
}
2673
2674
// This code enforces section 10.3.3 of the CSS2 spec for this formula:
2675
//
2676
// 'margin-left' + 'border-left-width' + 'padding-left' + 'width' +
2677
//   'padding-right' + 'border-right-width' + 'margin-right'
2678
//   = width of containing block
2679
//
2680
// Note: the width unit is not auto when this is called
2681
void
2682
ReflowInput::CalculateBlockSideMargins(LayoutFrameType aFrameType)
2683
0
{
2684
0
  // Calculations here are done in the containing block's writing mode,
2685
0
  // which is where margins will eventually be applied: we're calculating
2686
0
  // margins that will be used by the container in its inline direction,
2687
0
  // which in the case of an orthogonal contained block will correspond to
2688
0
  // the block direction of this reflow state. So in the orthogonal-flow
2689
0
  // case, "CalculateBlock*Side*Margins" will actually end up adjusting
2690
0
  // the BStart/BEnd margins; those are the "sides" of the block from its
2691
0
  // container's point of view.
2692
0
  WritingMode cbWM =
2693
0
    mCBReflowInput ? mCBReflowInput->GetWritingMode(): GetWritingMode();
2694
0
2695
0
  nscoord availISizeCBWM = AvailableSize(cbWM).ISize(cbWM);
2696
0
  nscoord computedISizeCBWM = ComputedSize(cbWM).ISize(cbWM);
2697
0
  if (computedISizeCBWM == NS_UNCONSTRAINEDSIZE) {
2698
0
    // For orthogonal flows, where we found a parent orthogonal-limit
2699
0
    // for AvailableISize() in Init(), we'll use the same here as well.
2700
0
    computedISizeCBWM = availISizeCBWM;
2701
0
  }
2702
0
2703
0
  LAYOUT_WARN_IF_FALSE(NS_UNCONSTRAINEDSIZE != computedISizeCBWM &&
2704
0
                       NS_UNCONSTRAINEDSIZE != availISizeCBWM,
2705
0
                       "have unconstrained inline-size; this should only "
2706
0
                       "result from very large sizes, not attempts at "
2707
0
                       "intrinsic inline-size calculation");
2708
0
2709
0
  LogicalMargin margin =
2710
0
    ComputedLogicalMargin().ConvertTo(cbWM, mWritingMode);
2711
0
  LogicalMargin borderPadding =
2712
0
    ComputedLogicalBorderPadding().ConvertTo(cbWM, mWritingMode);
2713
0
  nscoord sum = margin.IStartEnd(cbWM) +
2714
0
    borderPadding.IStartEnd(cbWM) + computedISizeCBWM;
2715
0
  if (sum == availISizeCBWM) {
2716
0
    // The sum is already correct
2717
0
    return;
2718
0
  }
2719
0
2720
0
  // Determine the start and end margin values. The isize value
2721
0
  // remains constant while we do this.
2722
0
2723
0
  // Calculate how much space is available for margins
2724
0
  nscoord availMarginSpace = availISizeCBWM - sum;
2725
0
2726
0
  // If the available margin space is negative, then don't follow the
2727
0
  // usual overconstraint rules.
2728
0
  if (availMarginSpace < 0) {
2729
0
    margin.IEnd(cbWM) += availMarginSpace;
2730
0
    SetComputedLogicalMargin(margin.ConvertTo(mWritingMode, cbWM));
2731
0
    return;
2732
0
  }
2733
0
2734
0
  // The css2 spec clearly defines how block elements should behave
2735
0
  // in section 10.3.3.
2736
0
  const nsStyleSides& styleSides = mStyleMargin->mMargin;
2737
0
  bool isAutoStartMargin = eStyleUnit_Auto == styleSides.GetIStartUnit(cbWM);
2738
0
  bool isAutoEndMargin = eStyleUnit_Auto == styleSides.GetIEndUnit(cbWM);
2739
0
  if (!isAutoStartMargin && !isAutoEndMargin) {
2740
0
    // Neither margin is 'auto' so we're over constrained. Use the
2741
0
    // 'direction' property of the parent to tell which margin to
2742
0
    // ignore
2743
0
    // First check if there is an HTML alignment that we should honor
2744
0
    const ReflowInput* pri = mParentReflowInput;
2745
0
    if (aFrameType == LayoutFrameType::Table) {
2746
0
      NS_ASSERTION(pri->mFrame->IsTableWrapperFrame(),
2747
0
                   "table not inside table wrapper");
2748
0
      // Center the table within the table wrapper based on the alignment
2749
0
      // of the table wrapper's parent.
2750
0
      pri = pri->mParentReflowInput;
2751
0
    }
2752
0
    if (pri &&
2753
0
        (pri->mStyleText->mTextAlign == NS_STYLE_TEXT_ALIGN_MOZ_LEFT ||
2754
0
         pri->mStyleText->mTextAlign == NS_STYLE_TEXT_ALIGN_MOZ_CENTER ||
2755
0
         pri->mStyleText->mTextAlign == NS_STYLE_TEXT_ALIGN_MOZ_RIGHT)) {
2756
0
      if (pri->mWritingMode.IsBidiLTR()) {
2757
0
        isAutoStartMargin =
2758
0
          pri->mStyleText->mTextAlign != NS_STYLE_TEXT_ALIGN_MOZ_LEFT;
2759
0
        isAutoEndMargin =
2760
0
          pri->mStyleText->mTextAlign != NS_STYLE_TEXT_ALIGN_MOZ_RIGHT;
2761
0
      } else {
2762
0
        isAutoStartMargin =
2763
0
          pri->mStyleText->mTextAlign != NS_STYLE_TEXT_ALIGN_MOZ_RIGHT;
2764
0
        isAutoEndMargin =
2765
0
          pri->mStyleText->mTextAlign != NS_STYLE_TEXT_ALIGN_MOZ_LEFT;
2766
0
      }
2767
0
    }
2768
0
    // Otherwise apply the CSS rules, and ignore one margin by forcing
2769
0
    // it to 'auto', depending on 'direction'.
2770
0
    else {
2771
0
      isAutoEndMargin = true;
2772
0
    }
2773
0
  }
2774
0
2775
0
  // Logic which is common to blocks and tables
2776
0
  // The computed margins need not be zero because the 'auto' could come from
2777
0
  // overconstraint or from HTML alignment so values need to be accumulated
2778
0
2779
0
  if (isAutoStartMargin) {
2780
0
    if (isAutoEndMargin) {
2781
0
      // Both margins are 'auto' so the computed addition should be equal
2782
0
      nscoord forStart = availMarginSpace / 2;
2783
0
      margin.IStart(cbWM) += forStart;
2784
0
      margin.IEnd(cbWM) += availMarginSpace - forStart;
2785
0
    } else {
2786
0
      margin.IStart(cbWM) += availMarginSpace;
2787
0
    }
2788
0
  } else if (isAutoEndMargin) {
2789
0
    margin.IEnd(cbWM) += availMarginSpace;
2790
0
  }
2791
0
  LogicalMargin marginInOurWM = margin.ConvertTo(mWritingMode, cbWM);
2792
0
  SetComputedLogicalMargin(marginInOurWM);
2793
0
2794
0
  if (isAutoStartMargin || isAutoEndMargin) {
2795
0
    // Update the UsedMargin property if we were tracking it already.
2796
0
    nsMargin* propValue = mFrame->GetProperty(nsIFrame::UsedMarginProperty());
2797
0
    if (propValue) {
2798
0
      *propValue = marginInOurWM.GetPhysicalMargin(mWritingMode);
2799
0
    }
2800
0
  }
2801
0
}
2802
2803
0
#define NORMAL_LINE_HEIGHT_FACTOR 1.2f    // in term of emHeight
2804
// For "normal" we use the font's normal line height (em height + leading).
2805
// If both internal leading and  external leading specified by font itself
2806
// are zeros, we should compensate this by creating extra (external) leading
2807
// in eCompensateLeading mode. This is necessary because without this
2808
// compensation, normal line height might looks too tight.
2809
2810
// For risk management, we use preference to control the behavior, and
2811
// eNoExternalLeading is the old behavior.
2812
static nscoord
2813
GetNormalLineHeight(nsFontMetrics* aFontMetrics)
2814
0
{
2815
0
  MOZ_ASSERT(nullptr != aFontMetrics, "no font metrics");
2816
0
2817
0
  nscoord normalLineHeight;
2818
0
2819
0
  nscoord externalLeading = aFontMetrics->ExternalLeading();
2820
0
  nscoord internalLeading = aFontMetrics->InternalLeading();
2821
0
  nscoord emHeight = aFontMetrics->EmHeight();
2822
0
  switch (GetNormalLineHeightCalcControl()) {
2823
0
  case eIncludeExternalLeading:
2824
0
    normalLineHeight = emHeight+ internalLeading + externalLeading;
2825
0
    break;
2826
0
  case eCompensateLeading:
2827
0
    if (!internalLeading && !externalLeading)
2828
0
      normalLineHeight = NSToCoordRound(emHeight * NORMAL_LINE_HEIGHT_FACTOR);
2829
0
    else
2830
0
      normalLineHeight = emHeight+ internalLeading + externalLeading;
2831
0
    break;
2832
0
  default:
2833
0
    //case eNoExternalLeading:
2834
0
    normalLineHeight = emHeight + internalLeading;
2835
0
  }
2836
0
  return normalLineHeight;
2837
0
}
2838
2839
static inline nscoord
2840
ComputeLineHeight(ComputedStyle* aComputedStyle,
2841
                  nsPresContext* aPresContext,
2842
                  nscoord aBlockBSize,
2843
                  float aFontSizeInflation)
2844
0
{
2845
0
  const nsStyleCoord& lhCoord = aComputedStyle->StyleText()->mLineHeight;
2846
0
2847
0
  if (lhCoord.GetUnit() == eStyleUnit_Coord) {
2848
0
    nscoord result = lhCoord.GetCoordValue();
2849
0
    if (aFontSizeInflation != 1.0f) {
2850
0
      result = NSToCoordRound(result * aFontSizeInflation);
2851
0
    }
2852
0
    return result;
2853
0
  }
2854
0
2855
0
  if (lhCoord.GetUnit() == eStyleUnit_Factor)
2856
0
    // For factor units the computed value of the line-height property
2857
0
    // is found by multiplying the factor by the font's computed size
2858
0
    // (adjusted for min-size prefs and text zoom).
2859
0
    return NSToCoordRound(lhCoord.GetFactorValue() * aFontSizeInflation *
2860
0
                          aComputedStyle->StyleFont()->mFont.size);
2861
0
2862
0
  NS_ASSERTION(lhCoord.GetUnit() == eStyleUnit_Normal ||
2863
0
               lhCoord.GetUnit() == eStyleUnit_Enumerated,
2864
0
               "bad line-height unit");
2865
0
2866
0
  if (lhCoord.GetUnit() == eStyleUnit_Enumerated) {
2867
0
    NS_ASSERTION(lhCoord.GetIntValue() == NS_STYLE_LINE_HEIGHT_BLOCK_HEIGHT,
2868
0
                 "bad line-height value");
2869
0
    if (aBlockBSize != NS_AUTOHEIGHT) {
2870
0
      return aBlockBSize;
2871
0
    }
2872
0
  }
2873
0
2874
0
  RefPtr<nsFontMetrics> fm = nsLayoutUtils::
2875
0
    GetFontMetricsForComputedStyle(aComputedStyle, aPresContext, aFontSizeInflation);
2876
0
  return GetNormalLineHeight(fm);
2877
0
}
2878
2879
nscoord
2880
ReflowInput::CalcLineHeight() const
2881
0
{
2882
0
  nscoord blockBSize =
2883
0
    nsLayoutUtils::IsNonWrapperBlock(mFrame) ? ComputedBSize() :
2884
0
    (mCBReflowInput ? mCBReflowInput->ComputedBSize() : NS_AUTOHEIGHT);
2885
0
2886
0
  return CalcLineHeight(mFrame->GetContent(),
2887
0
                        mFrame->Style(),
2888
0
                        mFrame->PresContext(),
2889
0
                        blockBSize,
2890
0
                        nsLayoutUtils::FontSizeInflationFor(mFrame));
2891
0
}
2892
2893
/* static */ nscoord
2894
ReflowInput::CalcLineHeight(nsIContent* aContent,
2895
                            ComputedStyle* aComputedStyle,
2896
                            nsPresContext* aPresContext,
2897
                            nscoord aBlockBSize,
2898
                            float aFontSizeInflation)
2899
0
{
2900
0
  MOZ_ASSERT(aComputedStyle, "Must have a ComputedStyle");
2901
0
2902
0
  nscoord lineHeight =
2903
0
    ComputeLineHeight(aComputedStyle,
2904
0
                      aPresContext,
2905
0
                      aBlockBSize,
2906
0
                      aFontSizeInflation);
2907
0
2908
0
  NS_ASSERTION(lineHeight >= 0, "ComputeLineHeight screwed up");
2909
0
2910
0
  HTMLInputElement* input = HTMLInputElement::FromNodeOrNull(aContent);
2911
0
  if (input && input->IsSingleLineTextControl()) {
2912
0
    // For Web-compatibility, single-line text input elements cannot
2913
0
    // have a line-height smaller than one.
2914
0
    nscoord lineHeightOne =
2915
0
      aFontSizeInflation * aComputedStyle->StyleFont()->mFont.size;
2916
0
    if (lineHeight < lineHeightOne) {
2917
0
      lineHeight = lineHeightOne;
2918
0
    }
2919
0
  }
2920
0
2921
0
  return lineHeight;
2922
0
}
2923
2924
bool
2925
SizeComputationInput::ComputeMargin(WritingMode aWM,
2926
                                    nscoord aPercentBasis)
2927
0
{
2928
0
  // SVG text frames have no margin.
2929
0
  if (nsSVGUtils::IsInSVGTextSubtree(mFrame)) {
2930
0
    return false;
2931
0
  }
2932
0
2933
0
  // If style style can provide us the margin directly, then use it.
2934
0
  const nsStyleMargin *styleMargin = mFrame->StyleMargin();
2935
0
2936
0
  bool isCBDependent = !styleMargin->GetMargin(ComputedPhysicalMargin());
2937
0
  if (isCBDependent) {
2938
0
    // We have to compute the value. Note that this calculation is
2939
0
    // performed according to the writing mode of the containing block
2940
0
    // (http://dev.w3.org/csswg/css-writing-modes-3/#orthogonal-flows)
2941
0
    LogicalMargin m(aWM);
2942
0
    m.IStart(aWM) = nsLayoutUtils::
2943
0
      ComputeCBDependentValue(aPercentBasis,
2944
0
                              styleMargin->mMargin.GetIStart(aWM));
2945
0
    m.IEnd(aWM) = nsLayoutUtils::
2946
0
      ComputeCBDependentValue(aPercentBasis,
2947
0
                              styleMargin->mMargin.GetIEnd(aWM));
2948
0
2949
0
    m.BStart(aWM) = nsLayoutUtils::
2950
0
      ComputeCBDependentValue(aPercentBasis,
2951
0
                              styleMargin->mMargin.GetBStart(aWM));
2952
0
    m.BEnd(aWM) = nsLayoutUtils::
2953
0
      ComputeCBDependentValue(aPercentBasis,
2954
0
                              styleMargin->mMargin.GetBEnd(aWM));
2955
0
2956
0
    SetComputedLogicalMargin(aWM, m);
2957
0
  }
2958
0
2959
0
  // ... but font-size-inflation-based margin adjustment uses the
2960
0
  // frame's writing mode
2961
0
  nscoord marginAdjustment = FontSizeInflationListMarginAdjustment(mFrame);
2962
0
2963
0
  if (marginAdjustment > 0) {
2964
0
    LogicalMargin m = ComputedLogicalMargin();
2965
0
    m.IStart(mWritingMode) += marginAdjustment;
2966
0
    SetComputedLogicalMargin(m);
2967
0
  }
2968
0
2969
0
  return isCBDependent;
2970
0
}
2971
2972
bool
2973
SizeComputationInput::ComputePadding(WritingMode aWM,
2974
                                     nscoord aPercentBasis,
2975
                                     LayoutFrameType aFrameType)
2976
0
{
2977
0
  // If style can provide us the padding directly, then use it.
2978
0
  const nsStylePadding *stylePadding = mFrame->StylePadding();
2979
0
  bool isCBDependent = !stylePadding->GetPadding(ComputedPhysicalPadding());
2980
0
  // a table row/col group, row/col doesn't have padding
2981
0
  // XXXldb Neither do border-collapse tables.
2982
0
  if (LayoutFrameType::TableRowGroup == aFrameType ||
2983
0
      LayoutFrameType::TableColGroup == aFrameType ||
2984
0
      LayoutFrameType::TableRow      == aFrameType ||
2985
0
      LayoutFrameType::TableCol      == aFrameType) {
2986
0
    ComputedPhysicalPadding().SizeTo(0,0,0,0);
2987
0
  }
2988
0
  else if (isCBDependent) {
2989
0
    // We have to compute the value. This calculation is performed
2990
0
    // according to the writing mode of the containing block
2991
0
    // (http://dev.w3.org/csswg/css-writing-modes-3/#orthogonal-flows)
2992
0
    // clamp negative calc() results to 0
2993
0
    LogicalMargin p(aWM);
2994
0
    p.IStart(aWM) = std::max(0, nsLayoutUtils::
2995
0
      ComputeCBDependentValue(aPercentBasis,
2996
0
                              stylePadding->mPadding.GetIStart(aWM)));
2997
0
    p.IEnd(aWM) = std::max(0, nsLayoutUtils::
2998
0
      ComputeCBDependentValue(aPercentBasis,
2999
0
                              stylePadding->mPadding.GetIEnd(aWM)));
3000
0
3001
0
    p.BStart(aWM) = std::max(0, nsLayoutUtils::
3002
0
      ComputeCBDependentValue(aPercentBasis,
3003
0
                              stylePadding->mPadding.GetBStart(aWM)));
3004
0
    p.BEnd(aWM) = std::max(0, nsLayoutUtils::
3005
0
      ComputeCBDependentValue(aPercentBasis,
3006
0
                              stylePadding->mPadding.GetBEnd(aWM)));
3007
0
3008
0
    SetComputedLogicalPadding(aWM, p);
3009
0
  }
3010
0
  return isCBDependent;
3011
0
}
3012
3013
void
3014
ReflowInput::ComputeMinMaxValues(const LogicalSize&aCBSize)
3015
0
{
3016
0
  WritingMode wm = GetWritingMode();
3017
0
3018
0
  const nsStyleCoord& minISize = mStylePosition->MinISize(wm);
3019
0
  const nsStyleCoord& maxISize = mStylePosition->MaxISize(wm);
3020
0
  const nsStyleCoord& minBSize = mStylePosition->MinBSize(wm);
3021
0
  const nsStyleCoord& maxBSize = mStylePosition->MaxBSize(wm);
3022
0
3023
0
  // NOTE: min-width:auto resolves to 0, except on a flex item. (But
3024
0
  // even there, it's supposed to be ignored (i.e. treated as 0) until
3025
0
  // the flex container explicitly resolves & considers it.)
3026
0
  if (eStyleUnit_Auto == minISize.GetUnit()) {
3027
0
    ComputedMinISize() = 0;
3028
0
  } else {
3029
0
    ComputedMinISize() = ComputeISizeValue(aCBSize.ISize(wm),
3030
0
                                           mStylePosition->mBoxSizing,
3031
0
                                           minISize);
3032
0
  }
3033
0
3034
0
  if (eStyleUnit_None == maxISize.GetUnit()) {
3035
0
    // Specified value of 'none'
3036
0
    ComputedMaxISize() = NS_UNCONSTRAINEDSIZE;  // no limit
3037
0
  } else {
3038
0
    ComputedMaxISize() = ComputeISizeValue(aCBSize.ISize(wm),
3039
0
                                           mStylePosition->mBoxSizing,
3040
0
                                           maxISize);
3041
0
  }
3042
0
3043
0
  // If the computed value of 'min-width' is greater than the value of
3044
0
  // 'max-width', 'max-width' is set to the value of 'min-width'
3045
0
  if (ComputedMinISize() > ComputedMaxISize()) {
3046
0
    ComputedMaxISize() = ComputedMinISize();
3047
0
  }
3048
0
3049
0
  // Check for percentage based values and a containing block height that
3050
0
  // depends on the content height. Treat them like 'auto'
3051
0
  // Likewise, check for calc() with percentages on internal table elements;
3052
0
  // that's treated as 'auto' too.
3053
0
  // Likewise, if we're a child of a flex container who's measuring our
3054
0
  // intrinsic height, then we want to disregard our min-height.
3055
0
3056
0
  // NOTE: min-height:auto resolves to 0, except on a flex item. (But
3057
0
  // even there, it's supposed to be ignored (i.e. treated as 0) until
3058
0
  // the flex container explicitly resolves & considers it.)
3059
0
  if (eStyleUnit_Auto == minBSize.GetUnit() ||
3060
0
      (NS_AUTOHEIGHT == aCBSize.BSize(wm) &&
3061
0
       minBSize.HasPercent()) ||
3062
0
      (mFrameType == NS_CSS_FRAME_TYPE_INTERNAL_TABLE &&
3063
0
       minBSize.IsCalcUnit() && minBSize.CalcHasPercent()) ||
3064
0
      mFlags.mIsFlexContainerMeasuringBSize) {
3065
0
    ComputedMinBSize() = 0;
3066
0
  } else {
3067
0
    ComputedMinBSize() = ComputeBSizeValue(aCBSize.BSize(wm),
3068
0
                                           mStylePosition->mBoxSizing,
3069
0
                                           minBSize);
3070
0
  }
3071
0
  nsStyleUnit maxBSizeUnit = maxBSize.GetUnit();
3072
0
  if (eStyleUnit_None == maxBSizeUnit) {
3073
0
    // Specified value of 'none'
3074
0
    ComputedMaxBSize() = NS_UNCONSTRAINEDSIZE;  // no limit
3075
0
  } else {
3076
0
    // Check for percentage based values and a containing block height that
3077
0
    // depends on the content height. Treat them like 'none'
3078
0
    // Likewise, check for calc() with percentages on internal table elements;
3079
0
    // that's treated as 'auto' too.
3080
0
    // Likewise, if we're a child of a flex container who's measuring our
3081
0
    // intrinsic height, then we want to disregard our max-height.
3082
0
    if ((NS_AUTOHEIGHT == aCBSize.BSize(wm) &&
3083
0
         maxBSize.HasPercent()) ||
3084
0
        (mFrameType == NS_CSS_FRAME_TYPE_INTERNAL_TABLE &&
3085
0
         maxBSize.IsCalcUnit() && maxBSize.CalcHasPercent()) ||
3086
0
        mFlags.mIsFlexContainerMeasuringBSize) {
3087
0
      ComputedMaxBSize() = NS_UNCONSTRAINEDSIZE;
3088
0
    } else {
3089
0
      ComputedMaxBSize() = ComputeBSizeValue(aCBSize.BSize(wm),
3090
0
                                             mStylePosition->mBoxSizing,
3091
0
                                             maxBSize);
3092
0
    }
3093
0
  }
3094
0
3095
0
  // If the computed value of 'min-height' is greater than the value of
3096
0
  // 'max-height', 'max-height' is set to the value of 'min-height'
3097
0
  if (ComputedMinBSize() > ComputedMaxBSize()) {
3098
0
    ComputedMaxBSize() = ComputedMinBSize();
3099
0
  }
3100
0
}
3101
3102
bool
3103
ReflowInput::IsFloating() const
3104
0
{
3105
0
  return mStyleDisplay->IsFloating(mFrame);
3106
0
}
3107
3108
mozilla::StyleDisplay
3109
ReflowInput::GetDisplay() const
3110
0
{
3111
0
  return mStyleDisplay->GetDisplay(mFrame);
3112
0
}