tor-browser

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

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:
Mservo/components/style/style_adjuster.rs | 94++++++++++++++++++++++++++++++-------------------------------------------------
Mservo/components/style/values/computed/length.rs | 72+++++++++++++++++++++++++++++++++++++++++++++++++++++-------------------
Mservo/components/style/values/computed/length_percentage.rs | 27+++++++++++++++++++++++++++
Mservo/components/style/values/computed/position.rs | 71+++++++++++++++++++++++++++++++++--------------------------------------
Mservo/components/style/values/generics/length.rs | 17++++++++---------
Mservo/components/style/values/generics/position.rs | 33+++++++++++++++++----------------
Dtesting/web-platform/meta/css/css-anchor-position/try-tactic-anchor.html.ini | 9---------
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