commit 8e1950452fbb5138d50c252ae3189de14b088a47
parent 4c851cb40c568abf19267fe97d044748a11eef83
Author: David Shin <dshin@mozilla.com>
Date: Thu, 18 Dec 2025 15:14:16 +0000
Bug 2006402: Ensure that we override auto insets/margins only when there's position-area/anchor-center + valid default anchor. r=layout-anchor-positioning-reviewers,firefox-style-system-reviewers,layout-reviewers,emilio
Reuse the `anchor-center` check code, that checks for the validity of default
anchor, and reuse the `position-area` resolution logic, where `auto` values get
mapped to 0 at resolution time.
Differential Revision: https://phabricator.services.mozilla.com/D276905
Diffstat:
9 files changed, 141 insertions(+), 122 deletions(-)
diff --git a/layout/generic/ReflowInput.cpp b/layout/generic/ReflowInput.cpp
@@ -43,23 +43,13 @@ using namespace mozilla::layout;
AnchorPosResolutionParams AnchorPosResolutionParams::From(
const mozilla::SizeComputationInput* aSizingInput,
bool aIgnorePositionArea) {
- const mozilla::StylePositionArea posArea =
- aIgnorePositionArea
- ? mozilla::StylePositionArea{}
- : aSizingInput->mFrame->StylePosition()->mPositionArea;
- bool inlineUsesAnchorCenter = false;
- bool blockUsesAnchorCenter = false;
-
- ComputeAnchorCenterUsage(aSizingInput->mFrame,
- aSizingInput->mAnchorPosResolutionCache,
- inlineUsesAnchorCenter, blockUsesAnchorCenter);
-
- return {aSizingInput->mFrame,
- aSizingInput->mFrame->StyleDisplay()->mPosition,
- posArea,
- aSizingInput->mAnchorPosResolutionCache,
- inlineUsesAnchorCenter,
- blockUsesAnchorCenter};
+ auto override = AutoResolutionOverrideParams{
+ aSizingInput->mFrame, aSizingInput->mAnchorPosResolutionCache};
+ if (aIgnorePositionArea) {
+ override.mPositionAreaInUse = false;
+ }
+ return {aSizingInput->mFrame, aSizingInput->mFrame->StyleDisplay()->mPosition,
+ aSizingInput->mAnchorPosResolutionCache, override};
}
static bool CheckNextInFlowParenthood(nsIFrame* aFrame, nsIFrame* aParent) {
@@ -68,32 +58,6 @@ static bool CheckNextInFlowParenthood(nsIFrame* aFrame, nsIFrame* aParent) {
return frameNext && parentNext && frameNext->GetParent() == parentNext;
}
-void ComputeAnchorCenterUsage(
- const nsIFrame* aFrame,
- mozilla::AnchorPosResolutionCache* aAnchorPosResolutionCache,
- bool& aInlineUsesAnchorCenter, bool& aBlockUsesAnchorCenter) {
- aInlineUsesAnchorCenter = false;
- aBlockUsesAnchorCenter = false;
- nsIFrame* parent = aFrame->GetParent();
- if (!parent || !aFrame->HasAnyStateBits(NS_FRAME_OUT_OF_FLOW) ||
- !aAnchorPosResolutionCache ||
- !aAnchorPosResolutionCache->mDefaultAnchorCache.mAnchor) {
- return;
- }
-
- const auto* stylePos = aFrame->StylePosition();
-
- auto checkAxis = [&](LogicalAxis aAxis) {
- StyleAlignFlags alignment =
- stylePos->UsedSelfAlignment(aAxis, parent->Style());
- return (alignment & ~StyleAlignFlags::FLAG_BITS) ==
- StyleAlignFlags::ANCHOR_CENTER;
- };
-
- aInlineUsesAnchorCenter = checkAxis(LogicalAxis::Inline);
- aBlockUsesAnchorCenter = checkAxis(LogicalAxis::Block);
-}
-
/**
* Adjusts the margin for a list (ol, ul), if necessary, depending on
* font inflation settings. Unfortunately, because bullets from a list are
@@ -1755,8 +1719,11 @@ void ReflowInput::InitAbsoluteConstraints(const ReflowInput* aCBReflowInput,
bool bStartIsAuto = bStartOffset->IsAuto();
bool bEndIsAuto = bEndOffset->IsAuto();
- mFlags.mIAnchorCenter = anchorResolutionParams.mBaseParams.mIAnchorCenter;
- mFlags.mBAnchorCenter = anchorResolutionParams.mBaseParams.mBAnchorCenter;
+ // TODO(dshin): Needed anymore?
+ mFlags.mIAnchorCenter = anchorResolutionParams.mBaseParams
+ .mAutoResolutionOverrideParams.mIAnchorCenter;
+ mFlags.mBAnchorCenter = anchorResolutionParams.mBaseParams
+ .mAutoResolutionOverrideParams.mBAnchorCenter;
// For anchor-center with both insets auto, insets need to be kept as 0
// so hypothetical position should be skipped.
diff --git a/layout/generic/ReflowInput.h b/layout/generic/ReflowInput.h
@@ -982,9 +982,4 @@ struct ReflowInput : public SizeComputationInput {
} // namespace mozilla
-void ComputeAnchorCenterUsage(
- const nsIFrame* aFrame,
- mozilla::AnchorPosResolutionCache* aAnchorPosResolutionCache,
- bool& aInlineUsesAnchorCenter, bool& aBlockUsesAnchorCenter);
-
#endif // mozilla_ReflowInput_h
diff --git a/layout/generic/nsIFrame.cpp b/layout/generic/nsIFrame.cpp
@@ -7229,7 +7229,7 @@ LogicalSize nsIFrame::ComputeAbsolutePosAutoSize(
const auto* stylePos = StylePosition();
const auto anchorResolutionParams =
AnchorPosOffsetResolutionParams::UseCBFrameSize(
- AnchorPosResolutionParams::From(this));
+ AnchorPosResolutionParams::From(&aSizingInput));
const auto& styleISize =
aSizeOverrides.mStyleISize
? AnchorResolvedSizeHelper::Overridden(*aSizeOverrides.mStyleISize)
diff --git a/layout/generic/nsIFrame.h b/layout/generic/nsIFrame.h
@@ -5911,17 +5911,8 @@ inline nsIFrame* nsFrameList::BackwardFrameTraversal::Prev(nsIFrame* aFrame) {
inline AnchorPosResolutionParams AnchorPosResolutionParams::From(
const nsIFrame* aFrame,
mozilla::AnchorPosResolutionCache* aAnchorPosResolutionCache) {
- bool inlineUsesAnchorCenter = false;
- bool blockUsesAnchorCenter = false;
- ComputeAnchorCenterUsage(aFrame, aAnchorPosResolutionCache,
- inlineUsesAnchorCenter, blockUsesAnchorCenter);
-
- return {aFrame,
- aFrame->StyleDisplay()->mPosition,
- aFrame->StylePosition()->mPositionArea,
- aAnchorPosResolutionCache,
- inlineUsesAnchorCenter,
- blockUsesAnchorCenter};
+ return {aFrame, aFrame->StyleDisplay()->mPosition, aAnchorPosResolutionCache,
+ AutoResolutionOverrideParams{aFrame, aAnchorPosResolutionCache}};
}
#endif /* nsIFrame_h___ */
diff --git a/layout/style/nsComputedDOMStyle.h b/layout/style/nsComputedDOMStyle.h
@@ -400,9 +400,12 @@ already_AddRefed<nsComputedDOMStyle> NS_NewComputedDOMStyle(
inline AnchorPosResolutionParams AnchorPosResolutionParams::From(
const nsComputedDOMStyle* aComputedDOMStyle) {
+ // TODO(dshin): Fix this up.
+ AutoResolutionOverrideParams overrides;
+ overrides.mPositionAreaInUse =
+ !aComputedDOMStyle->StylePosition()->mPositionArea.IsNone();
return {aComputedDOMStyle->mOuterFrame,
- aComputedDOMStyle->StyleDisplay()->mPosition,
- aComputedDOMStyle->StylePosition()->mPositionArea};
+ aComputedDOMStyle->StyleDisplay()->mPosition, nullptr, overrides};
}
#endif /* nsComputedDOMStyle_h__ */
diff --git a/layout/style/nsStyleStruct.cpp b/layout/style/nsStyleStruct.cpp
@@ -13,6 +13,7 @@
#include <algorithm>
+#include "AnchorPositioningUtils.h"
#include "CounterStyleManager.h"
#include "ImageLoader.h"
#include "imgIContainer.h"
@@ -286,6 +287,75 @@ static StyleRect<T> StyleRectWithAllSides(const T& aSide) {
return {aSide, aSide, aSide, aSide};
}
+bool AnchorPosResolutionParams::AutoResolutionOverrideParams::OverriddenToZero(
+ StylePhysicalAxis aAxis, const nsIFrame* aFrame) const {
+ if (!aFrame || !aFrame->HasAnyStateBits(NS_FRAME_OUT_OF_FLOW)) {
+ return false;
+ }
+ const auto* cb = aFrame->GetParent();
+ const auto cbwm = cb->GetWritingMode();
+ const auto logicalAxis = [&]() {
+ if (cbwm.IsVertical()) {
+ return aAxis == StylePhysicalAxis::Vertical ? LogicalAxis::Inline
+ : LogicalAxis::Block;
+ }
+ return aAxis == StylePhysicalAxis::Horizontal ? LogicalAxis::Inline
+ : LogicalAxis::Block;
+ }();
+ return OverriddenToZero(logicalAxis);
+}
+
+bool AnchorPosResolutionParams::AutoResolutionOverrideParams::OverriddenToZero(
+ Side aSide, const nsIFrame* aFrame) const {
+ return OverriddenToZero(aSide == Side::eSideBottom || aSide == Side::eSideTop
+ ? StylePhysicalAxis::Vertical
+ : StylePhysicalAxis::Horizontal,
+ aFrame);
+}
+
+bool AnchorPosResolutionParams::AutoResolutionOverrideParams::OverriddenToZero(
+ LogicalAxis aAxis) const {
+ if (mPositionAreaInUse) {
+ // If `position-area` is used "Any auto inset properties resolve to 0":
+ // https://drafts.csswg.org/css-anchor-position-1/#valdef-position-area-position-area
+ return true;
+ }
+
+ // If `anchor-center` is used with a valid anchor, "auto inset
+ // properties resolve to 0" on that axis:
+ // https://drafts.csswg.org/css-anchor-position-1/#anchor-center
+ switch (aAxis) {
+ case LogicalAxis::Block:
+ return mBAnchorCenter;
+ case LogicalAxis::Inline:
+ return mIAnchorCenter;
+ }
+}
+
+AnchorPosResolutionParams::AutoResolutionOverrideParams::
+ AutoResolutionOverrideParams(
+ const nsIFrame* aFrame,
+ const mozilla::AnchorPosResolutionCache* aCache) {
+ nsIFrame* parent = aFrame->GetParent();
+ if (!parent || !aFrame->HasAnyStateBits(NS_FRAME_OUT_OF_FLOW) || !aCache ||
+ !aCache->mDefaultAnchorCache.mAnchor) {
+ return;
+ }
+
+ const auto* stylePos = aFrame->StylePosition();
+
+ auto checkAxis = [&](LogicalAxis aAxis) {
+ StyleAlignFlags alignment =
+ stylePos->UsedSelfAlignment(aAxis, parent->Style());
+ return (alignment & ~StyleAlignFlags::FLAG_BITS) ==
+ StyleAlignFlags::ANCHOR_CENTER;
+ };
+
+ mIAnchorCenter = checkAxis(LogicalAxis::Inline);
+ mBAnchorCenter = checkAxis(LogicalAxis::Block);
+ mPositionAreaInUse = !stylePos->mPositionArea.IsNone();
+}
+
AnchorResolvedMargin AnchorResolvedMarginHelper::ResolveAnchor(
const StyleMargin& aValue, StylePhysicalAxis aAxis,
const AnchorPosResolutionParams& aParams) {
@@ -1328,25 +1398,6 @@ StyleSelfAlignment nsStylePosition::UsedJustifySelf(
return {StyleAlignFlags::NORMAL};
}
-bool AnchorResolvedInsetHelper::SideUsesAnchorCenter(
- mozilla::Side aSide, const AnchorPosOffsetResolutionParams& aParams) {
- const nsIFrame* frame = aParams.mBaseParams.mFrame;
- if (!frame) {
- return false;
- }
- const nsIFrame* parent = frame->GetParent();
- if (!parent) {
- return false;
- }
-
- WritingMode wm = parent->GetWritingMode();
- LogicalSide logicalSide = wm.LogicalSideForPhysicalSide(aSide);
- LogicalAxis axis = GetAxis(logicalSide);
-
- return axis == LogicalAxis::Inline ? aParams.mBaseParams.mIAnchorCenter
- : aParams.mBaseParams.mBAnchorCenter;
-}
-
AnchorResolvedInset AnchorResolvedInsetHelper::ResolveAnchor(
const mozilla::StyleInset& aValue, mozilla::StylePhysicalSide aSide,
const AnchorPosOffsetResolutionParams& aParams) {
diff --git a/layout/style/nsStyleStruct.h b/layout/style/nsStyleStruct.h
@@ -378,21 +378,35 @@ using AnchorResolvedMargin =
// Base set of parameters required to resolve a reference to an anchor.
struct AnchorPosResolutionParams {
+ struct AutoResolutionOverrideParams {
+ // TODO(dshin): Probably just make it physical
+ // Whether anchor-center is being used on the inline axis.
+ bool mIAnchorCenter = false;
+ // Whether anchor-center is being used on the block axis.
+ bool mBAnchorCenter = false;
+ // Whether position-area is being used.
+ bool mPositionAreaInUse = false;
+
+ AutoResolutionOverrideParams() = default;
+ AutoResolutionOverrideParams(
+ const nsIFrame* aFrame,
+ const mozilla::AnchorPosResolutionCache* aCache);
+
+ bool OverriddenToZero(mozilla::StylePhysicalAxis aAxis,
+ const nsIFrame* aFrame) const;
+ bool OverriddenToZero(mozilla::Side aSide, const nsIFrame* aFrame) const;
+ bool OverriddenToZero(mozilla::LogicalAxis aAxis) const;
+ };
// Frame of the anchor positioned element.
// If nullptr, skips anchor lookup and returns invalid, resolving fallbacks.
const nsIFrame* mFrame;
// Position property of the element in question.
mozilla::StylePositionProperty mPosition;
- // position-area property of the element in question.
- mozilla::StylePositionArea mPositionArea;
// Cache data used for anchor resolution.
mozilla::AnchorPosResolutionCache* const mCache;
- // Whether anchor-center is being used with a valid anchor on the inline axis.
- // When true, auto insets in the inline axis resolve to 0.
- bool mIAnchorCenter = false;
- // Whether anchor-center is being used with a valid anchor on the block axis.
- // When true, auto insets in the block axis resolve to 0.
- bool mBAnchorCenter = false;
+ // Set of parameters that override `auto` values to 0, if the default
+ // anchor is valid.
+ AutoResolutionOverrideParams mAutoResolutionOverrideParams;
// Helper functions for creating anchor resolution parameters.
// Defined in corresponding header files.
@@ -416,16 +430,18 @@ struct AnchorResolvedMarginHelper {
static AnchorResolvedMargin FromUnresolved(
const mozilla::StyleMargin& aValue, mozilla::StylePhysicalAxis aAxis,
const AnchorPosResolutionParams& aParams) {
- if (aValue.HasAnchorPositioningFunction()) {
- return ResolveAnchor(aValue, aAxis, aParams);
- }
- // For `position-area` values other than `none`, the used value of `auto`
- // margin properties resolves to 0:
- // <https://drafts.csswg.org/css-anchor-position-1/#valdef-position-area-position-area>
- if (aValue.IsAuto() && !aParams.mPositionArea.IsNone()) {
+ auto resolved = [&]() {
+ if (aValue.HasAnchorPositioningFunction()) {
+ return ResolveAnchor(aValue, aAxis, aParams);
+ }
+ return AnchorResolvedMargin::NonOwning(&aValue);
+ }();
+ if (resolved->IsAuto() &&
+ aParams.mAutoResolutionOverrideParams.OverriddenToZero(
+ aAxis, aParams.mFrame)) {
return Zero();
}
- return AnchorResolvedMargin::NonOwning(&aValue);
+ return resolved;
}
private:
@@ -780,24 +796,28 @@ struct AnchorResolvedInsetHelper {
return value;
}
+ static const mozilla::StyleInset& ZeroValue() {
+ static const auto value = mozilla::StyleInset::LengthPercentage(
+ mozilla::StyleLengthPercentage::Zero());
+ return value;
+ }
+
static AnchorResolvedInset FromUnresolved(
const mozilla::StyleInset& aValue, mozilla::Side aSide,
const AnchorPosOffsetResolutionParams& aParams) {
- if (aValue.HasAnchorPositioningFunction()) {
- return ResolveAnchor(aValue, mozilla::ToStylePhysicalSide(aSide),
- aParams);
- }
- // If `position-area` is used "Any auto inset properties resolve to 0":
- // https://drafts.csswg.org/css-anchor-position-1/#valdef-position-area-position-area
- // If `anchor-center` is used with a valid anchor, "auto inset
- // properties resolve to 0":
- // https://drafts.csswg.org/css-anchor-position-1/#anchor-center
- if (aValue.IsAuto() && (!aParams.mBaseParams.mPositionArea.IsNone() ||
- SideUsesAnchorCenter(aSide, aParams))) {
- return AnchorResolvedInset::UniquelyOwning(
- new mozilla::StyleInset(mozilla::LengthPercentage::Zero()));
+ auto resolved = [&]() {
+ if (aValue.HasAnchorPositioningFunction()) {
+ return ResolveAnchor(aValue, mozilla::ToStylePhysicalSide(aSide),
+ aParams);
+ }
+ return AnchorResolvedInset::NonOwning(&aValue);
+ }();
+ if (resolved->IsAuto() &&
+ aParams.mBaseParams.mAutoResolutionOverrideParams.OverriddenToZero(
+ aSide, aParams.mBaseParams.mFrame)) {
+ return AnchorResolvedInset::NonOwning(&ZeroValue());
}
- return AnchorResolvedInset::NonOwning(&aValue);
+ return resolved;
}
private:
@@ -805,9 +825,6 @@ struct AnchorResolvedInsetHelper {
return AnchorResolvedInset::NonOwning(&AutoValue());
}
- static bool SideUsesAnchorCenter(
- mozilla::Side aSide, const AnchorPosOffsetResolutionParams& aParams);
-
static AnchorResolvedInset ResolveAnchor(
const mozilla::StyleInset& aValue, mozilla::StylePhysicalSide aSide,
const AnchorPosOffsetResolutionParams& aParams);
diff --git a/servo/ports/geckolib/glue.rs b/servo/ports/geckolib/glue.rs
@@ -10799,10 +10799,8 @@ fn offset_params_from_base_params(
mBaseParams: AnchorPosResolutionParams {
mFrame: params.mFrame,
mPosition: params.mPosition,
- mPositionArea: params.mPositionArea,
mCache: params.mCache,
- mIAnchorCenter: params.mIAnchorCenter,
- mBAnchorCenter: params.mBAnchorCenter,
+ mAutoResolutionOverrideParams: params.mAutoResolutionOverrideParams,
},
}
}
diff --git a/testing/web-platform/meta/css/css-anchor-position/position-area-basic.html.ini b/testing/web-platform/meta/css/css-anchor-position/position-area-basic.html.ini
@@ -1,3 +0,0 @@
-[position-area-basic.html]
- [Offsets for: span-all top]
- expected: FAIL