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