commit ab4222ad998420801334ee213e78b565db3c210e
parent c687c2137862b6a76156c9cff6d7e9f3bf76ac64
Author: Ting-Yu Lin <tlin@mozilla.com>
Date: Mon, 8 Dec 2025 17:10:53 +0000
Bug 1985982 - Defer auto margin computation of an abspos frame until it is reflowed. r=layout-reviewers,emilio
Remove auto margin resolution in both axes for an abspos frame in
`ReflowInput::InitAbsoluteConstraints()`. Auto margins are now resolved in
`AbsoluteContainingBlock::ResolveAutoMarginsAfterLayout()` after the frame’s
size is known.
Note: the old code in `InitAbsoluteConstraints()` makes the `autoBSize`
non-negative. This patch ports that behavior to make the `stretchFitSize`
non-negative as well, which fixes additional wpts.
Differential Revision: https://phabricator.services.mozilla.com/D275371
Diffstat:
4 files changed, 44 insertions(+), 152 deletions(-)
diff --git a/layout/generic/AbsoluteContainingBlock.cpp b/layout/generic/AbsoluteContainingBlock.cpp
@@ -972,51 +972,52 @@ void AbsoluteContainingBlock::ResolveAutoMarginsAfterLayout(
ReflowInput& aKidReflowInput, const LogicalSize& aCBSize,
const LogicalSize& aKidSize, LogicalMargin& aMargin,
const LogicalMargin& aOffsets) {
- MOZ_ASSERT(aKidReflowInput.mFlags.mDeferAutoMarginComputation);
-
- WritingMode wm = aKidReflowInput.GetWritingMode();
WritingMode outerWM = aKidReflowInput.mParentReflowInput->GetWritingMode();
-
- const LogicalSize cbSizeInWM = aCBSize.ConvertTo(wm, outerWM);
- const LogicalSize kidSizeInWM = aKidSize.ConvertTo(wm, outerWM);
- LogicalMargin marginInWM = aMargin.ConvertTo(wm, outerWM);
- LogicalMargin offsetsInWM = aOffsets.ConvertTo(wm, outerWM);
-
- // No need to substract border sizes because aKidSize has it included
- // already. Also, if any offset is auto, the auto margin resolves to zero.
- // https://drafts.csswg.org/css-position-3/#abspos-margins
- const bool autoOffset = offsetsInWM.BEnd(wm) == NS_AUTOOFFSET ||
- offsetsInWM.BStart(wm) == NS_AUTOOFFSET;
- nscoord availMarginSpace =
- autoOffset ? 0
- : cbSizeInWM.BSize(wm) - kidSizeInWM.BSize(wm) -
- offsetsInWM.BStartEnd(wm) - marginInWM.BStartEnd(wm);
-
const auto& styleMargin = aKidReflowInput.mStyleMargin;
const auto anchorResolutionParams =
AnchorPosResolutionParams::From(&aKidReflowInput);
- if (wm.IsOrthogonalTo(outerWM)) {
- ReflowInput::ComputeAbsPosInlineAutoMargin(
- availMarginSpace, outerWM,
- styleMargin
- ->GetMargin(LogicalSide::IStart, outerWM, anchorResolutionParams)
- ->IsAuto(),
- styleMargin
- ->GetMargin(LogicalSide::IEnd, outerWM, anchorResolutionParams)
- ->IsAuto(),
- aMargin);
- } else {
- ReflowInput::ComputeAbsPosBlockAutoMargin(
- availMarginSpace, outerWM,
- styleMargin
- ->GetMargin(LogicalSide::BStart, outerWM, anchorResolutionParams)
- ->IsAuto(),
- styleMargin
- ->GetMargin(LogicalSide::BEnd, outerWM, anchorResolutionParams)
- ->IsAuto(),
- aMargin);
- }
+ auto ResolveMarginsInAxis = [&](LogicalAxis aAxis) {
+ const auto startSide = MakeLogicalSide(aAxis, LogicalEdge::Start);
+ const auto endSide = MakeLogicalSide(aAxis, LogicalEdge::End);
+
+ // No need to substract border sizes because aKidSize has it included
+ // already. Also, if any offset is auto, the auto margin resolves to zero.
+ // https://drafts.csswg.org/css-position-3/#abspos-margins
+ const bool autoOffset =
+ aOffsets.Side(startSide, outerWM) == NS_AUTOOFFSET ||
+ aOffsets.Side(endSide, outerWM) == NS_AUTOOFFSET;
+
+ nscoord availMarginSpace;
+ if (autoOffset) {
+ availMarginSpace = 0;
+ } else {
+ const nscoord stretchFitSize = std::max(
+ 0, aCBSize.Size(aAxis, outerWM) - aOffsets.StartEnd(aAxis, outerWM) -
+ aMargin.StartEnd(aAxis, outerWM));
+ availMarginSpace = stretchFitSize - aKidSize.Size(aAxis, outerWM);
+ }
+
+ const bool startSideMarginIsAuto =
+ styleMargin->GetMargin(startSide, outerWM, anchorResolutionParams)
+ ->IsAuto();
+ const bool endSideMarginIsAuto =
+ styleMargin->GetMargin(endSide, outerWM, anchorResolutionParams)
+ ->IsAuto();
+
+ if (aAxis == LogicalAxis::Inline) {
+ ReflowInput::ComputeAbsPosInlineAutoMargin(availMarginSpace, outerWM,
+ startSideMarginIsAuto,
+ endSideMarginIsAuto, aMargin);
+ } else {
+ ReflowInput::ComputeAbsPosBlockAutoMargin(availMarginSpace, outerWM,
+ startSideMarginIsAuto,
+ endSideMarginIsAuto, aMargin);
+ }
+ };
+
+ ResolveMarginsInAxis(LogicalAxis::Inline);
+ ResolveMarginsInAxis(LogicalAxis::Block);
aKidReflowInput.SetComputedLogicalMargin(outerWM, aMargin);
nsMargin* propValue =
@@ -1421,10 +1422,8 @@ void AbsoluteContainingBlock::ReflowAbsoluteFrame(
ResolveSizeDependentOffsets(kidReflowInput, cbSize, kidSize, margin,
cb.ResolvedPositionArea(), offsets);
- if (kidReflowInput.mFlags.mDeferAutoMarginComputation) {
- ResolveAutoMarginsAfterLayout(kidReflowInput, cbSize, kidSize, margin,
- offsets);
- }
+ ResolveAutoMarginsAfterLayout(kidReflowInput, cbSize, kidSize, margin,
+ offsets);
// If the inset is constrained as non-auto, we may have a child that does
// not fill out the inset-reduced containing block. In this case, we need
diff --git a/layout/generic/ReflowInput.cpp b/layout/generic/ReflowInput.cpp
@@ -1876,29 +1876,10 @@ void ReflowInput::InitAbsoluteConstraints(const ReflowInput* aCBReflowInput,
LogicalMargin margin = ComputedLogicalMargin(cbwm);
const LogicalMargin borderPadding = ComputedLogicalBorderPadding(cbwm);
+ const LogicalSize computedSize = mComputedSize.ConvertTo(cbwm, wm);
bool iSizeIsAuto =
mStylePosition->ISize(cbwm, anchorResolutionParams.mBaseParams)->IsAuto();
- bool marginIStartIsAuto = false;
- bool marginIEndIsAuto = false;
- bool marginBStartIsAuto = false;
- bool marginBEndIsAuto = false;
- const bool hasIntrinsicKeywordForBSize =
- mFrame->HasIntrinsicKeywordForBSize();
-
- // Unconstrained size implies fit-content sizing, so auto margin(s) cannot
- // be resolved at this time, except for cases where any inset is auto (Which
- // will take up available space and leave auto margins to be zero).
- const LogicalSize computedSize = mComputedSize.ConvertTo(cbwm, wm);
- const bool nonZeroAutoMarginOnUnconstrainedSize =
- isOrthogonal ? computedSize.ISize(cbwm) == NS_UNCONSTRAINEDSIZE &&
- !(iStartIsAuto || iEndIsAuto)
- : computedSize.BSize(cbwm) == NS_UNCONSTRAINEDSIZE &&
- !(bStartIsAuto || bEndIsAuto);
- // TODO(dshin, Bug 1985982): We should defer _all_ auto margin computation for
- // simplicity.
- mFlags.mDeferAutoMarginComputation =
- nonZeroAutoMarginOnUnconstrainedSize || hasIntrinsicKeywordForBSize;
if (iStartIsAuto) {
// We know 'inset-inline-end' is not 'auto' anymore thanks to the
// hypothetical box code above. Solve for 'inset-inline-start'.
@@ -1919,28 +1900,6 @@ void ReflowInput::InitAbsoluteConstraints(const ReflowInput* aCBReflowInput,
computedSize.ISize(cbwm) - margin.IStartEnd(cbwm) -
borderPadding.IStartEnd(cbwm);
}
- } else if (!mFlags.mDeferAutoMarginComputation || !isOrthogonal) {
- // Neither 'inline-start' nor 'inline-end' is 'auto'.
- // The inline-size might not fill all the available space (even though we
- // didn't shrink-wrap) in case:
- // * insets are explicitly set and the child frame is not stretched
- // * inline-size was specified
- // * we're dealing with a replaced element
- // * width was constrained by min- or max-inline-size.
-
- nscoord availMarginSpace =
- aCBSize.ISize(cbwm) - offsets.IStartEnd(cbwm) - margin.IStartEnd(cbwm) -
- borderPadding.IStartEnd(cbwm) - computedSize.ISize(cbwm);
- marginIStartIsAuto = mStyleMargin
- ->GetMargin(LogicalSide::IStart, cbwm,
- anchorResolutionParams.mBaseParams)
- ->IsAuto();
- marginIEndIsAuto = mStyleMargin
- ->GetMargin(LogicalSide::IEnd, cbwm,
- anchorResolutionParams.mBaseParams)
- ->IsAuto();
- ComputeAbsPosInlineAutoMargin(availMarginSpace, cbwm, marginIStartIsAuto,
- marginIEndIsAuto, margin);
}
bool bSizeIsAuto =
@@ -1964,49 +1923,9 @@ void ReflowInput::InitAbsoluteConstraints(const ReflowInput* aCBReflowInput,
borderPadding.BStartEnd(cbwm) -
computedSize.BSize(cbwm) - offsets.BStart(cbwm);
}
- } else if (!mFlags.mDeferAutoMarginComputation || isOrthogonal) {
- // Neither block-start nor -end is 'auto'.
- nscoord autoBSize = aCBSize.BSize(cbwm) - margin.BStartEnd(cbwm) -
- borderPadding.BStartEnd(cbwm) - offsets.BStartEnd(cbwm);
- autoBSize = std::max(autoBSize, 0);
- // FIXME: Bug 1602669: if |autoBSize| happens to be numerically equal to
- // NS_UNCONSTRAINEDSIZE, we may get some unexpected behavior. We need a
- // better way to distinguish between unconstrained size and resolved size.
- NS_WARNING_ASSERTION(autoBSize != NS_UNCONSTRAINEDSIZE,
- "Unexpected size from block-start and block-end");
-
- // The block-size might not fill all the available space in case:
- // * insets are explicitly set and the child frame is not stretched
- // * bsize was specified
- // * we're dealing with a replaced element
- // * bsize was constrained by min- or max-bsize.
- nscoord availMarginSpace = autoBSize - computedSize.BSize(cbwm);
- marginBStartIsAuto = mStyleMargin
- ->GetMargin(LogicalSide::BStart, cbwm,
- anchorResolutionParams.mBaseParams)
- ->IsAuto();
- marginBEndIsAuto = mStyleMargin
- ->GetMargin(LogicalSide::BEnd, cbwm,
- anchorResolutionParams.mBaseParams)
- ->IsAuto();
-
- ComputeAbsPosBlockAutoMargin(availMarginSpace, cbwm, marginBStartIsAuto,
- marginBEndIsAuto, margin);
}
SetComputedLogicalOffsets(cbwm, offsets);
- SetComputedLogicalMargin(cbwm, margin);
-
- // If we have auto margins, update our UsedMarginProperty. The property
- // will have already been created by InitOffsets if it is needed.
- if (marginIStartIsAuto || marginIEndIsAuto || marginBStartIsAuto ||
- marginBEndIsAuto) {
- nsMargin* propValue = mFrame->GetProperty(nsIFrame::UsedMarginProperty());
- MOZ_ASSERT(propValue,
- "UsedMarginProperty should have been created "
- "by InitOffsets.");
- *propValue = margin.GetPhysicalMargin(cbwm);
- }
}
// This will not be converted to abstract coordinates because it's only
diff --git a/layout/generic/ReflowInput.h b/layout/generic/ReflowInput.h
@@ -532,14 +532,6 @@ struct ReflowInput : public SizeComputationInput {
// If true, then children of this frame can generate class A breakpoints
// for paginated reflow.
bool mCanHaveClassABreakpoints : 1;
-
- // If set:
- // (1) This frame is absolutely-positioned,
- // (2) Inset in that axis are non-auto, and
- // (3) Size in that axis is `auto` & resolved as fit-content size.
- // Automatic margin computation in this case requires waiting until
- // the frame reflows to compute the fit-content size.
- bool mDeferAutoMarginComputation : 1;
};
Flags mFlags;
diff --git a/testing/web-platform/meta/css/css-position/position-absolute-with-negative-sized-imcb.html.ini b/testing/web-platform/meta/css/css-position/position-absolute-with-negative-sized-imcb.html.ini
@@ -1,22 +1,4 @@
[position-absolute-with-negative-sized-imcb.html]
- [.abspos 9]
- expected: FAIL
-
- [.abspos 12]
- expected: FAIL
-
- [.abspos 13]
- expected: FAIL
-
- [.abspos 14]
- expected: FAIL
-
- [.abspos 17]
- expected: FAIL
-
- [.abspos 18]
- expected: FAIL
-
[.abspos 19]
expected: FAIL