tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

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:
Mlayout/generic/AbsoluteContainingBlock.cpp | 87+++++++++++++++++++++++++++++++++++++++----------------------------------------
Mlayout/generic/ReflowInput.cpp | 83+------------------------------------------------------------------------------
Mlayout/generic/ReflowInput.h | 8--------
Mtesting/web-platform/meta/css/css-position/position-absolute-with-negative-sized-imcb.html.ini | 18------------------
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