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

@@ -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]