Bug 1964122: Work with side, not axis, when resolving anchor() r=firefox-style-system-reviewers,emilio

Previously, we mapped left: anchor(left); to "Resolve anchor(left) with in
horizontal axis" - However, this doesn't work for two reasons:

1. `left: anchor(left)` and `right: anchor(left)` have to return 2 different
   values to produce the identical result
2. We need side data for `left: anchor(inside)` and `right: anchor(outside)`

Differential Revision: https://phabricator.services.mozilla.com/D247663
This commit is contained in:
David Shin
2025-05-07 14:42:58 +00:00
committed by dshin@mozilla.com
parent b7d69dd476
commit 1a41596bbf
7 changed files with 46 additions and 48 deletions

View File

@@ -1273,17 +1273,21 @@ inline gfx::Point StyleCoordinatePair<LengthPercentage>::ToGfxPoint(
y.ResolveToCSSPixels(aBasis->Height()));
}
inline StylePhysicalAxis GetStylePhysicalAxis(mozilla::Side aSide) {
return aSide == mozilla::Side::eSideTop || aSide == mozilla::Side::eSideBottom
? StylePhysicalAxis::Vertical
: StylePhysicalAxis::Horizontal;
}
inline StylePhysicalAxis ToStylePhysicalAxis(PhysicalAxis aAxis) {
// TODO(dhsin): Should look into merging these two values...
// Assert for this casting lives in `nsStyleStruct.cpp` since
// `PhysicalAxis` is a forward decl here.
return static_cast<StylePhysicalAxis>(static_cast<uint8_t>(aAxis));
inline StylePhysicalSide ToStylePhysicalSide(mozilla::Side aSide) {
// TODO(dshin): Should look into merging these two types...
static_assert(static_cast<uint8_t>(mozilla::Side::eSideLeft) ==
static_cast<uint8_t>(StylePhysicalSide::Left),
"Left side doesn't match");
static_assert(static_cast<uint8_t>(mozilla::Side::eSideRight) ==
static_cast<uint8_t>(StylePhysicalSide::Right),
"Left side doesn't match");
static_assert(static_cast<uint8_t>(mozilla::Side::eSideTop) ==
static_cast<uint8_t>(StylePhysicalSide::Top),
"Left side doesn't match");
static_assert(static_cast<uint8_t>(mozilla::Side::eSideBottom) ==
static_cast<uint8_t>(StylePhysicalSide::Bottom),
"Left side doesn't match");
return static_cast<StylePhysicalSide>(static_cast<uint8_t>(aSide));
}
#define DEFINE_LENGTH_PERCENTAGE_CTOR(ty_) \

View File

@@ -1363,14 +1363,8 @@ StyleJustifySelf nsStylePosition::UsedJustifySelf(
}
AnchorResolvedInset AnchorResolvedInsetHelper::ResolveAnchor(
const mozilla::StyleInset& aValue, mozilla::StylePhysicalAxis aAxis,
mozilla::StylePositionProperty aPosition) {
static_assert(static_cast<uint8_t>(mozilla::PhysicalAxis::Vertical) ==
static_cast<uint8_t>(StylePhysicalAxis::Vertical),
"Vertical axis doesn't match");
static_assert(static_cast<uint8_t>(mozilla::PhysicalAxis::Horizontal) ==
static_cast<uint8_t>(StylePhysicalAxis::Horizontal),
"Horizontal axis doesn't match");
const StyleInset& aValue, StylePhysicalSide aSide,
StylePositionProperty aPosition) {
MOZ_ASSERT(aValue.HasAnchorPositioningFunction(),
"Calling anchor resolution without using it?");
switch (aValue.tag) {
@@ -1378,7 +1372,7 @@ AnchorResolvedInset AnchorResolvedInsetHelper::ResolveAnchor(
const auto& lp = aValue.AsAnchorContainingCalcFunction();
const auto& c = lp.AsCalc();
auto result = StyleCalcAnchorPositioningFunctionResolution::Invalid();
Servo_ResolveAnchorFunctionsInCalcPercentage(&c, &aAxis, aPosition,
Servo_ResolveAnchorFunctionsInCalcPercentage(&c, &aSide, aPosition,
&result);
if (result.IsInvalid()) {
return Auto();
@@ -1387,7 +1381,7 @@ AnchorResolvedInset AnchorResolvedInsetHelper::ResolveAnchor(
}
case StyleInset::Tag::AnchorFunction: {
auto resolved = StyleAnchorPositioningFunctionResolution::Invalid();
Servo_ResolveAnchorFunction(&*aValue.AsAnchorFunction(), aAxis, aPosition,
Servo_ResolveAnchorFunction(&*aValue.AsAnchorFunction(), aSide, aPosition,
&resolved);
if (resolved.IsInvalid()) {
return Auto();

View File

@@ -749,12 +749,12 @@ struct AnchorResolvedInsetHelper {
}
static AnchorResolvedInset FromUnresolved(
const mozilla::StyleInset& aValue, mozilla::StylePhysicalAxis aAxis,
const mozilla::StyleInset& aValue, mozilla::Side aSide,
mozilla::StylePositionProperty aPosition) {
if (!aValue.HasAnchorPositioningFunction()) {
return AnchorResolvedInset::NonOwning(&aValue);
}
return ResolveAnchor(aValue, aAxis, aPosition);
return ResolveAnchor(aValue, mozilla::ToStylePhysicalSide(aSide), aPosition);
}
private:
@@ -763,7 +763,7 @@ struct AnchorResolvedInsetHelper {
}
static AnchorResolvedInset ResolveAnchor(
const mozilla::StyleInset& aValue, mozilla::StylePhysicalAxis aAxis,
const mozilla::StyleInset& aValue, mozilla::StylePhysicalSide aSide,
mozilla::StylePositionProperty aPosition);
};
@@ -1014,7 +1014,7 @@ struct MOZ_NEEDS_MEMMOVABLE_MEMBERS nsStylePosition {
AnchorResolvedInset GetAnchorResolvedInset(
mozilla::Side aSide, mozilla::StylePositionProperty aPosition) const {
return AnchorResolvedInsetHelper::FromUnresolved(
mOffset.Get(aSide), GetStylePhysicalAxis(aSide), aPosition);
mOffset.Get(aSide), aSide, aPosition);
}
inline AnchorResolvedInset GetAnchorResolvedInset(
mozilla::LogicalSide aSide, WritingMode aWM,

View File

@@ -27,7 +27,7 @@
use super::{Context, Length, Percentage, PositionProperty, ToComputedValue};
#[cfg(feature = "gecko")]
use crate::gecko_bindings::structs::GeckoFontMetrics;
use crate::logical_geometry::PhysicalAxis;
use crate::logical_geometry::PhysicalSide;
use crate::values::animated::{Animate, Context as AnimatedContext, Procedure, ToAnimatedValue, ToAnimatedZero};
use crate::values::distance::{ComputeSquaredDistance, SquaredDistance};
use crate::values::generics::calc::{CalcUnits, PositivePercentageBasis};
@@ -915,10 +915,10 @@ fn resolve_anchor_functions(
) -> Result<Option<CalcNode>, ()> {
let resolution = match node {
CalcNode::Anchor(f) => {
let axis = info.axis.expect("Unexpected anchor()");
let side = info.side.expect("Unexpected anchor()");
// Invalid use of `anchor()` (i.e. Outside of inset properties) should've been
// caught at parse time.
f.resolve(axis, info.position_property)
f.resolve(side, info.position_property)
},
CalcNode::AnchorSize(f) => f.resolve(info.position_property),
_ => return Ok(None),
@@ -938,14 +938,13 @@ fn resolve_anchor_functions(
#[repr(C)]
#[derive(Clone, Copy)]
pub struct CalcAnchorFunctionResolutionInfo {
/// Which axis we're resolving anchor functions for.
/// Which side we're resolving anchor functions for.
/// This is only relevant for `anchor()`, which requires
/// the property using the function to be in the same axis
/// as the specified side [1]. `None` if we aren't expecting
/// `anchor()`, like in size properties, where only `anchor-size()`
/// is allowed.
/// the property using the function to be in the side[1].
/// `None` if we aren't expecting `anchor()`, like in size
/// properties, where only `anchor-size()` is allowed.
/// [1]: https://drafts.csswg.org/css-anchor-position-1/#anchor-valid
pub axis: Option<PhysicalAxis>,
pub side: Option<PhysicalSide>,
/// `position` property of the box for which this style is being resolved.
pub position_property: PositionProperty,
}

View File

@@ -11,7 +11,7 @@ use style_traits::CssWriter;
use style_traits::SpecifiedValueInfo;
use style_traits::ToCss;
use crate::logical_geometry::PhysicalAxis;
use crate::logical_geometry::PhysicalSide;
use crate::values::animated::ToAnimatedZero;
use crate::values::generics::box_::PositionProperty;
use crate::values::generics::length::{AnchorResolutionResult, GenericAnchorSizeFunction};
@@ -391,14 +391,14 @@ impl<Percentage, LengthPercentage> GenericAnchorFunction<Percentage, LengthPerce
/// Resolve the anchor function. On failure, return reference to fallback, if exists.
pub fn resolve<'a>(
&'a self,
axis: PhysicalAxis,
side: PhysicalSide,
position_property: PositionProperty,
) -> AnchorResolutionResult<'a, LengthPercentage> {
if !position_property.is_absolutely_positioned() {
return AnchorResolutionResult::new_anchor_invalid(self.fallback.as_ref());
}
if !self.side.valid_for_axis(axis) {
if !self.side.valid_for(side) {
return AnchorResolutionResult::new_anchor_invalid(self.fallback.as_ref());
}
@@ -456,10 +456,10 @@ pub enum AnchorSideKeyword {
}
impl AnchorSideKeyword {
fn valid_for_axis(&self, axis: PhysicalAxis) -> bool {
fn valid_for(&self, side: PhysicalSide) -> bool {
match self {
Self::Left | Self::Right => axis == PhysicalAxis::Horizontal,
Self::Top | Self::Bottom => axis == PhysicalAxis::Vertical,
Self::Left | Self::Right => matches!(side, PhysicalSide::Left | PhysicalSide::Right),
Self::Top | Self::Bottom => matches!(side, PhysicalSide::Top | PhysicalSide::Bottom),
Self::Inside |
Self::Outside |
Self::Start |
@@ -500,10 +500,10 @@ pub enum AnchorSide<P> {
}
impl<P> AnchorSide<P> {
/// Is this anchor side valid for a given axis?
pub fn valid_for_axis(&self, axis: PhysicalAxis) -> bool {
/// Is this anchor side valid for a given side?
pub fn valid_for(&self, side: PhysicalSide) -> bool {
match self {
Self::Keyword(k) => k.valid_for_axis(axis),
Self::Keyword(k) => k.valid_for(side),
Self::Percentage(_) => true,
}
}

View File

@@ -331,6 +331,7 @@ include = [
"Margin",
"PositionProperty",
"PhysicalAxis",
"PhysicalSide",
"QueryFontMetricsFlags",
]
item_types = [

View File

@@ -103,7 +103,7 @@ use style::invalidation::element::relative_selector::{
};
use style::invalidation::element::restyle_hints::RestyleHint;
use style::invalidation::stylesheets::RuleChangeKind;
use style::logical_geometry::PhysicalAxis;
use style::logical_geometry::PhysicalSide;
use style::media_queries::MediaList;
use style::parser::{Parse, ParserContext};
#[cfg(feature = "gecko_debug")]
@@ -8447,12 +8447,12 @@ pub enum CalcAnchorPositioningFunctionResolution {
#[no_mangle]
pub extern "C" fn Servo_ResolveAnchorFunctionsInCalcPercentage(
calc: &computed::length_percentage::CalcLengthPercentage,
axis: Option<&PhysicalAxis>,
side: Option<&PhysicalSide>,
position_property: PositionProperty,
out: &mut CalcAnchorPositioningFunctionResolution,
) {
let resolved = calc.resolve_anchor(CalcAnchorFunctionResolutionInfo {
axis: axis.copied(),
side: side.copied(),
position_property,
});
@@ -9869,11 +9869,11 @@ impl AnchorPositioningFunctionResolution {
#[no_mangle]
pub extern "C" fn Servo_ResolveAnchorFunction(
func: &AnchorFunction,
axis: PhysicalAxis,
side: PhysicalSide,
prop: PositionProperty,
out: &mut AnchorPositioningFunctionResolution,
) {
*out = AnchorPositioningFunctionResolution::new(func.resolve(axis, prop));
*out = AnchorPositioningFunctionResolution::new(func.resolve(side, prop));
}
#[no_mangle]