commit 5133ef8effde727ac7afb1d26e36d63b13809945
parent 51c6668010a948107a143fc30a58ab0e9d9bd6d4
Author: Emilio Cobos Álvarez <emilio@crisal.io>
Date: Thu, 30 Oct 2025 08:36:34 +0000
Bug 1994674 - Adjust sizes for flips. r=layout-anchor-positioning-reviewers,layout-reviewers,firefox-style-system-reviewers,dshin
We need to adjust for anchor-size(height) becoming anchor-size(width)
and so on. Luckily the infrastructure added on the previous patch
extends nicely to this.
And make the trait work with a mutable ref because it makes the calc
code a bit easier and more compact.
Differential Revision: https://phabricator.services.mozilla.com/D270595
Diffstat:
7 files changed, 173 insertions(+), 150 deletions(-)
diff --git a/servo/components/style/style_adjuster.rs b/servo/components/style/style_adjuster.rs
@@ -26,23 +26,6 @@ use crate::values::specified::align::AlignFlags;
#[cfg(feature = "gecko")]
use selectors::parser::PseudoElement;
-macro_rules! flip_property {
- ($adjuster:ident, $struct_getter:ident, $struct_setter:ident, $a_getter:ident, $a_setter:ident, $b_getter:ident, $b_setter:ident) => {{
- loop {
- let s = $adjuster.style.$struct_getter();
- let a = s.$a_getter();
- let b = s.$b_getter();
- if a == b {
- break;
- }
- let s = $adjuster.style.$struct_setter();
- s.$b_setter(a);
- s.$a_setter(b);
- break;
- }
- }};
-}
-
/// A struct that implements all the adjustment methods.
///
/// NOTE(emilio): If new adjustments are introduced that depend on reset
@@ -977,17 +960,14 @@ impl<'a, 'b: 'a> StyleAdjuster<'a, 'b> {
pos.set_position_area(new);
}
+ // TODO: Could avoid some clones here and below.
fn swap_insets(&mut self, a_side: PhysicalSide, b_side: PhysicalSide) {
debug_assert_ne!(a_side, b_side);
- let pos = self.style.get_position();
- let a = pos.get_inset(a_side);
- let b = pos.get_inset(b_side);
- if a == b {
- return;
- }
- let a = a.clone().try_tactic_adjustment(a_side, b_side);
- let b = b.clone().try_tactic_adjustment(b_side, a_side);
let pos = self.style.mutate_position();
+ let mut a = pos.get_inset(a_side).clone();
+ a.try_tactic_adjustment(a_side, b_side);
+ let mut b = pos.get_inset(b_side).clone();
+ b.try_tactic_adjustment(b_side, a_side);
pos.set_inset(a_side, b);
pos.set_inset(b_side, a);
}
@@ -995,51 +975,47 @@ impl<'a, 'b: 'a> StyleAdjuster<'a, 'b> {
fn swap_margins(&mut self, a_side: PhysicalSide, b_side: PhysicalSide) {
debug_assert_ne!(a_side, b_side);
let margin = self.style.get_margin();
- let a = margin.get_margin(a_side);
- let b = margin.get_margin(b_side);
- if a == b {
- return;
- }
- let a = a.clone().try_tactic_adjustment(a_side, b_side);
- let b = b.clone().try_tactic_adjustment(b_side, a_side);
+ let mut a = margin.get_margin(a_side).clone();
+ a.try_tactic_adjustment(a_side, b_side);
+ let mut b = margin.get_margin(b_side).clone();
+ b.try_tactic_adjustment(b_side, a_side);
let margin = self.style.mutate_margin();
margin.set_margin(a_side, b);
margin.set_margin(b_side, a);
}
+ fn swap_sizes(&mut self, block_start: PhysicalSide, inline_start: PhysicalSide) {
+ let pos = self.style.mutate_position();
+ let mut min_width = pos.clone_min_width();
+ min_width.try_tactic_adjustment(inline_start, block_start);
+ let mut max_width = pos.clone_max_width();
+ max_width.try_tactic_adjustment(inline_start, block_start);
+ let mut width = pos.clone_width();
+ width.try_tactic_adjustment(inline_start, block_start);
+
+ let mut min_height = pos.clone_min_height();
+ min_height.try_tactic_adjustment(block_start, inline_start);
+ let mut max_height = pos.clone_max_height();
+ max_height.try_tactic_adjustment(block_start, inline_start);
+ let mut height = pos.clone_height();
+ height.try_tactic_adjustment(block_start, inline_start);
+
+ let pos = self.style.mutate_position();
+ pos.set_width(height);
+ pos.set_height(width);
+ pos.set_max_width(max_height);
+ pos.set_max_height(max_width);
+ pos.set_min_width(min_height);
+ pos.set_min_height(min_width);
+ }
+
fn flip_start(&mut self) {
- flip_property!(
- self,
- get_position,
- mutate_position,
- clone_width,
- set_width,
- clone_height,
- set_height
- );
- flip_property!(
- self,
- get_position,
- mutate_position,
- clone_min_width,
- set_min_width,
- clone_min_height,
- set_min_height
- );
- flip_property!(
- self,
- get_position,
- mutate_position,
- clone_max_width,
- set_max_width,
- clone_max_height,
- set_max_height
- );
let wm = self.style.writing_mode;
let bs = wm.block_start_physical_side();
let is = wm.inline_start_physical_side();
let be = wm.block_end_physical_side();
let ie = wm.inline_end_physical_side();
+ self.swap_sizes(bs, is);
self.swap_insets(bs, is);
self.swap_insets(ie, be);
self.swap_margins(bs, is);
diff --git a/servo/components/style/values/computed/length.rs b/servo/components/style/values/computed/length.rs
@@ -561,36 +561,70 @@ pub fn resolve_anchor_size(
/// A computed type for `margin` properties.
pub type Margin = generics::GenericMargin<LengthPercentage>;
-impl TryTacticAdjustment for Percentage {
- fn try_tactic_adjustment(self, old_side: PhysicalSide, new_side: PhysicalSide) -> Self {
- if old_side.parallel_to(new_side) {
- return Self(1.0 - self.0)
+impl TryTacticAdjustment for MaxSize {
+ fn try_tactic_adjustment(&mut self, old_side: PhysicalSide, new_side: PhysicalSide) {
+ debug_assert!(
+ old_side.orthogonal_to(new_side),
+ "Sizes should only change axes"
+ );
+ match self {
+ Self::FitContentFunction(lp)
+ | Self::LengthPercentage(lp)
+ | Self::AnchorContainingCalcFunction(lp) => {
+ lp.try_tactic_adjustment(old_side, new_side);
+ },
+ Self::AnchorSizeFunction(s) => s.try_tactic_adjustment(old_side, new_side),
+ Self::None
+ | Self::MaxContent
+ | Self::MinContent
+ | Self::FitContent
+ | Self::MozAvailable
+ | Self::WebkitFillAvailable
+ | Self::Stretch => {},
}
- self
}
}
-impl TryTacticAdjustment for LengthPercentage {
- fn try_tactic_adjustment(self, old_side: PhysicalSide, new_side: PhysicalSide) -> Self {
- if let Some(p) = self.to_percentage() {
- return Self::new_percent(p.try_tactic_adjustment(old_side, new_side));
+impl TryTacticAdjustment for Size {
+ fn try_tactic_adjustment(&mut self, old_side: PhysicalSide, new_side: PhysicalSide) {
+ debug_assert!(
+ old_side.orthogonal_to(new_side),
+ "Sizes should only change axes"
+ );
+ match self {
+ Self::FitContentFunction(lp)
+ | Self::LengthPercentage(lp)
+ | Self::AnchorContainingCalcFunction(lp) => {
+ lp.try_tactic_adjustment(old_side, new_side);
+ },
+ Self::AnchorSizeFunction(s) => s.try_tactic_adjustment(old_side, new_side),
+ Self::Auto
+ | Self::MaxContent
+ | Self::MinContent
+ | Self::FitContent
+ | Self::MozAvailable
+ | Self::WebkitFillAvailable
+ | Self::Stretch => {},
+ }
+ }
+}
+
+impl TryTacticAdjustment for Percentage {
+ fn try_tactic_adjustment(&mut self, old_side: PhysicalSide, new_side: PhysicalSide) {
+ if old_side.parallel_to(new_side) {
+ self.0 = 1.0 - self.0;
}
- self
}
}
impl TryTacticAdjustment for Margin {
- fn try_tactic_adjustment(self, old_side: PhysicalSide, new_side: PhysicalSide) -> Self {
+ fn try_tactic_adjustment(&mut self, old_side: PhysicalSide, new_side: PhysicalSide) {
match self {
- Self::Auto => self,
- Self::LengthPercentage(lp) => {
- Self::LengthPercentage(lp.try_tactic_adjustment(old_side, new_side))
- },
- Self::AnchorSizeFunction(anchor) => {
- Self::AnchorSizeFunction(anchor.try_tactic_adjustment(old_side, new_side))
+ Self::Auto => {},
+ Self::LengthPercentage(lp) | Self::AnchorContainingCalcFunction(lp) => {
+ lp.try_tactic_adjustment(old_side, new_side)
},
- // TODO
- Self::AnchorContainingCalcFunction(..) => self,
+ Self::AnchorSizeFunction(anchor) => anchor.try_tactic_adjustment(old_side, new_side),
}
}
}
diff --git a/servo/components/style/values/computed/length_percentage.rs b/servo/components/style/values/computed/length_percentage.rs
@@ -31,6 +31,7 @@ use crate::logical_geometry::{PhysicalAxis, PhysicalSide};
use crate::values::animated::{
Animate, Context as AnimatedContext, Procedure, ToAnimatedValue, ToAnimatedZero,
};
+use crate::values::computed::position::TryTacticAdjustment;
use crate::values::distance::{ComputeSquaredDistance, SquaredDistance};
use crate::values::generics::calc::{CalcUnits, PositivePercentageBasis};
#[cfg(feature = "gecko")]
@@ -1331,3 +1332,29 @@ impl NonNegativeLengthPercentage {
Some(std::cmp::max(resolved, Au(0)))
}
}
+
+impl TryTacticAdjustment for LengthPercentage {
+ fn try_tactic_adjustment(&mut self, old_side: PhysicalSide, new_side: PhysicalSide) {
+ match self.unpack_mut() {
+ UnpackedMut::Calc(calc) => calc.node.try_tactic_adjustment(old_side, new_side),
+ UnpackedMut::Percentage(mut p) => {
+ p.try_tactic_adjustment(old_side, new_side);
+ *self = Self::new_percent(p);
+ },
+ UnpackedMut::Length(..) => {},
+ }
+ }
+}
+
+impl TryTacticAdjustment for CalcNode {
+ fn try_tactic_adjustment(&mut self, old_side: PhysicalSide, new_side: PhysicalSide) {
+ self.visit_depth_first(|node| match node {
+ Self::Leaf(CalcLengthPercentageLeaf::Percentage(p)) => {
+ p.try_tactic_adjustment(old_side, new_side)
+ },
+ Self::Anchor(a) => a.try_tactic_adjustment(old_side, new_side),
+ Self::AnchorSize(a) => a.try_tactic_adjustment(old_side, new_side),
+ _ => {},
+ });
+ }
+}
diff --git a/servo/components/style/values/computed/position.rs b/servo/components/style/values/computed/position.rs
@@ -10,21 +10,18 @@
use crate::values::computed::{
Context, Integer, LengthPercentage, NonNegativeNumber, Percentage, ToComputedValue,
};
-use crate::values::generics::position::Position as GenericPosition;
-use crate::values::generics::position::PositionComponent as GenericPositionComponent;
-use crate::values::generics::position::PositionOrAuto as GenericPositionOrAuto;
-use crate::values::generics::position::ZIndex as GenericZIndex;
+use crate::values::generics;
use crate::values::generics::position::{
- AnchorSideKeyword, GenericAnchorFunction, GenericAnchorSide,
+ AnchorSideKeyword, AspectRatio as GenericAspectRatio, GenericAnchorFunction, GenericAnchorSide,
+ GenericInset, Position as GenericPosition, PositionComponent as GenericPositionComponent,
+ PositionOrAuto as GenericPositionOrAuto, ZIndex as GenericZIndex,
};
-use crate::values::generics::position::{AspectRatio as GenericAspectRatio, GenericInset};
pub use crate::values::specified::position::{
- AnchorName, AnchorScope, DashedIdentAndOrTryTactic, PositionAnchor, PositionArea,
- PositionAreaAxis, PositionAreaKeyword, PositionAreaType, PositionTryFallbacks,
- PositionTryFallbacksTryTactic, PositionTryFallbacksTryTacticKeyword, PositionTryOrder,
- PositionVisibility,
+ AnchorName, AnchorScope, DashedIdentAndOrTryTactic, GridAutoFlow, GridTemplateAreas,
+ MasonryAutoFlow, PositionAnchor, PositionArea, PositionAreaAxis, PositionAreaKeyword,
+ PositionAreaType, PositionTryFallbacks, PositionTryFallbacksTryTactic,
+ PositionTryFallbacksTryTacticKeyword, PositionTryOrder, PositionVisibility,
};
-pub use crate::values::specified::position::{GridAutoFlow, GridTemplateAreas, MasonryAutoFlow};
use crate::Zero;
use std::fmt::{self, Write};
use style_traits::{CssWriter, ToCss};
@@ -107,34 +104,38 @@ impl AnchorFunction {
pub(crate) trait TryTacticAdjustment {
/// Performs the adjustments necessary given an old side we're relative to, and a new side
/// we're relative to.
- fn try_tactic_adjustment(self, old_side: PhysicalSide, new_side: PhysicalSide) -> Self;
+ fn try_tactic_adjustment(&mut self, old_side: PhysicalSide, new_side: PhysicalSide);
}
impl<T: TryTacticAdjustment> TryTacticAdjustment for Box<T> {
- fn try_tactic_adjustment(mut self, old_side: PhysicalSide, new_side: PhysicalSide) -> Self {
- *self = (*self).try_tactic_adjustment(old_side, new_side);
- self
+ fn try_tactic_adjustment(&mut self, old_side: PhysicalSide, new_side: PhysicalSide) {
+ (**self).try_tactic_adjustment(old_side, new_side);
}
}
-impl TryTacticAdjustment for GenericAnchorSide<Percentage> {
- fn try_tactic_adjustment(self, old_side: PhysicalSide, new_side: PhysicalSide) -> Self {
+impl<T: TryTacticAdjustment> TryTacticAdjustment for generics::NonNegative<T> {
+ fn try_tactic_adjustment(&mut self, old_side: PhysicalSide, new_side: PhysicalSide) {
+ self.0.try_tactic_adjustment(old_side, new_side);
+ }
+}
+
+impl<Percentage: TryTacticAdjustment> TryTacticAdjustment for GenericAnchorSide<Percentage> {
+ fn try_tactic_adjustment(&mut self, old_side: PhysicalSide, new_side: PhysicalSide) {
match self {
- Self::Percentage(p) => Self::Percentage(p.try_tactic_adjustment(old_side, new_side)),
- Self::Keyword(side) => Self::Keyword(side.try_tactic_adjustment(old_side, new_side)),
+ Self::Percentage(p) => p.try_tactic_adjustment(old_side, new_side),
+ Self::Keyword(side) => side.try_tactic_adjustment(old_side, new_side),
}
}
}
-impl<Fallback: TryTacticAdjustment> TryTacticAdjustment
+impl<Percentage: TryTacticAdjustment, Fallback: TryTacticAdjustment> TryTacticAdjustment
for GenericAnchorFunction<Percentage, Fallback>
{
- fn try_tactic_adjustment(mut self, old_side: PhysicalSide, new_side: PhysicalSide) -> Self {
- self.side = self.side.try_tactic_adjustment(old_side, new_side);
- self.fallback = self
- .fallback
- .map(|f| f.try_tactic_adjustment(old_side, new_side));
- self
+ fn try_tactic_adjustment(&mut self, old_side: PhysicalSide, new_side: PhysicalSide) {
+ self.side.try_tactic_adjustment(old_side, new_side);
+ if let Some(fallback) = self.fallback.as_mut() {
+ fallback.try_tactic_adjustment(old_side, new_side);
+ }
}
}
@@ -148,20 +149,14 @@ impl TryTacticAdjustment for Inset {
//
// If a <percentage> is used, and directions are opposing, change it to 100% minus the
// original percentage.
- fn try_tactic_adjustment(self, old_side: PhysicalSide, new_side: PhysicalSide) -> Self {
+ fn try_tactic_adjustment(&mut self, old_side: PhysicalSide, new_side: PhysicalSide) {
match self {
- Self::Auto => self,
- Self::LengthPercentage(lp) => {
- Self::LengthPercentage(lp.try_tactic_adjustment(old_side, new_side))
- },
- Self::AnchorFunction(anchor) => {
- Self::AnchorFunction(anchor.try_tactic_adjustment(old_side, new_side))
- },
- Self::AnchorSizeFunction(anchor) => {
- Self::AnchorSizeFunction(anchor.try_tactic_adjustment(old_side, new_side))
+ Self::Auto => {},
+ Self::AnchorContainingCalcFunction(lp) | Self::LengthPercentage(lp) => {
+ lp.try_tactic_adjustment(old_side, new_side)
},
- // TODO
- Self::AnchorContainingCalcFunction(..) => self,
+ Self::AnchorFunction(anchor) => anchor.try_tactic_adjustment(old_side, new_side),
+ Self::AnchorSizeFunction(anchor) => anchor.try_tactic_adjustment(old_side, new_side),
}
}
}
diff --git a/servo/components/style/values/generics/length.rs b/servo/components/style/values/generics/length.rs
@@ -405,12 +405,11 @@ pub struct GenericAnchorSizeFunction<Fallback> {
}
impl<Fallback: TryTacticAdjustment> TryTacticAdjustment for GenericAnchorSizeFunction<Fallback> {
- fn try_tactic_adjustment(mut self, old_side: PhysicalSide, new_side: PhysicalSide) -> Self {
- self.size = self.size.try_tactic_adjustment(old_side, new_side);
- self.fallback = self
- .fallback
- .map(|f| f.try_tactic_adjustment(old_side, new_side));
- self
+ fn try_tactic_adjustment(&mut self, old_side: PhysicalSide, new_side: PhysicalSide) {
+ self.size.try_tactic_adjustment(old_side, new_side);
+ if let Some(fallback) = self.fallback.as_mut() {
+ fallback.try_tactic_adjustment(old_side, new_side);
+ }
}
}
@@ -567,11 +566,11 @@ pub enum AnchorSizeKeyword {
}
impl TryTacticAdjustment for AnchorSizeKeyword {
- fn try_tactic_adjustment(self, old_side: PhysicalSide, new_side: PhysicalSide) -> Self {
+ fn try_tactic_adjustment(&mut self, old_side: PhysicalSide, new_side: PhysicalSide) {
if old_side.parallel_to(new_side) {
- return self;
+ return;
}
- match self {
+ *self = match *self {
Self::None => Self::None,
Self::Width => Self::Height,
Self::Height => Self::Width,
diff --git a/servo/components/style/values/generics/position.rs b/servo/components/style/values/generics/position.rs
@@ -462,25 +462,26 @@ impl AnchorSideKeyword {
}
impl TryTacticAdjustment for AnchorSideKeyword {
- fn try_tactic_adjustment(self, old_side: PhysicalSide, new_side: PhysicalSide) -> Self {
+ fn try_tactic_adjustment(&mut self, old_side: PhysicalSide, new_side: PhysicalSide) {
if !old_side.parallel_to(new_side) {
- if let Some(s) = self.physical_side() {
- return Self::from_physical_side(if s == new_side {
- old_side
- } else if s == old_side {
- new_side
- } else if s == new_side.opposite_side() {
- old_side.opposite_side()
- } else {
- debug_assert_eq!(s, old_side.opposite_side());
- new_side.opposite_side()
- });
- }
- return self;
+ let Some(s) = self.physical_side() else {
+ return;
+ };
+ *self = Self::from_physical_side(if s == new_side {
+ old_side
+ } else if s == old_side {
+ new_side
+ } else if s == new_side.opposite_side() {
+ old_side.opposite_side()
+ } else {
+ debug_assert_eq!(s, old_side.opposite_side());
+ new_side.opposite_side()
+ });
+ return;
}
- match self {
- Self::Center | Self::Inside | Self::Outside => self,
+ *self = match self {
+ Self::Center | Self::Inside | Self::Outside => *self,
Self::SelfStart => Self::SelfEnd,
Self::SelfEnd => Self::SelfStart,
Self::Start => Self::End,
diff --git a/testing/web-platform/meta/css/css-anchor-position/try-tactic-anchor.html.ini b/testing/web-platform/meta/css/css-anchor-position/try-tactic-anchor.html.ini
@@ -1,9 +0,0 @@
-[try-tactic-anchor.html]
- [flip-start]
- expected: FAIL
-
- [flip-inline flip-start]
- expected: FAIL
-
- [flip-start flip-block]
- expected: FAIL