tor-browser

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

commit fb40638bf7baee173d13c948ed397bf66c0d6215
parent bea800de3bb2e1a29db79dfc16ee6adfc99c4990
Author: Emilio Cobos Álvarez <emilio@crisal.io>
Date:   Mon, 20 Oct 2025 20:24:05 +0000

Bug 1995121 - Move property flips to style adjuster. r=dshin,firefox-style-system-reviewers,layout-anchor-positioning-reviewers

Differential Revision: https://phabricator.services.mozilla.com/D269138

Diffstat:
Mservo/components/style/properties/cascade.rs | 16++++++++++------
Mservo/components/style/properties/gecko.mako.rs | 25++++++++++++++++++++++++-
Mservo/components/style/style_adjuster.rs | 136++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-
Mservo/components/style/values/specified/position.rs | 75---------------------------------------------------------------------------
4 files changed, 169 insertions(+), 83 deletions(-)

diff --git a/servo/components/style/properties/cascade.rs b/servo/components/style/properties/cascade.rs @@ -383,8 +383,11 @@ where context.builder.clear_modified_reset(); if matches!(cascade_mode, CascadeMode::Unvisited { .. }) { - StyleAdjuster::new(&mut context.builder) - .adjust(layout_parent_style.unwrap_or(inherited_style), element, try_tactic); + StyleAdjuster::new(&mut context.builder).adjust( + layout_parent_style.unwrap_or(inherited_style), + element, + try_tactic, + ); } if context.builder.modified_reset() || using_cached_reset_properties { @@ -642,7 +645,11 @@ struct Cascade<'b> { } impl<'b> Cascade<'b> { - fn new(first_line_reparenting: FirstLineReparenting<'b>, try_tactic: PositionTryFallbacksTryTactic, ignore_colors: bool) -> Self { + fn new( + first_line_reparenting: FirstLineReparenting<'b>, + try_tactic: PositionTryFallbacksTryTactic, + ignore_colors: bool, + ) -> Self { Self { first_line_reparenting, try_tactic, @@ -869,9 +876,6 @@ impl<'b> Cascade<'b> { .set_writing_mode_dependency(wm); longhand_id = longhand_id.to_physical(wm); } - if !self.try_tactic.is_empty() { - longhand_id = self.try_tactic.apply_to_property(longhand_id, context.builder.writing_mode); - } self.apply_one_longhand( context, longhand_id, diff --git a/servo/components/style/properties/gecko.mako.rs b/servo/components/style/properties/gecko.mako.rs @@ -11,6 +11,7 @@ <%namespace name="helpers" file="/helpers.mako.rs" /> use crate::Atom; +use crate::logical_geometry::PhysicalSide; use app_units::Au; use crate::computed_value_flags::*; use crate::custom_properties::ComputedCustomProperties; @@ -273,6 +274,25 @@ impl ComputedValuesInner { } </%def> +<%def name="impl_physical_sides(ident, props)"> + pub fn get_${ident}(&self, s: PhysicalSide) -> &longhands::${data.longhands_by_name[props[0]].ident}::computed_value::T { + match s { + PhysicalSide::Top => &self.${data.longhands_by_name[props[0]].gecko_ffi_name}, + PhysicalSide::Right => &self.${data.longhands_by_name[props[1]].gecko_ffi_name}, + PhysicalSide::Bottom => &self.${data.longhands_by_name[props[2]].gecko_ffi_name}, + PhysicalSide::Left => &self.${data.longhands_by_name[props[3]].gecko_ffi_name}, + } + } + pub fn set_${ident}(&mut self, s: PhysicalSide, v: longhands::${data.longhands_by_name[props[0]].ident}::computed_value::T) { + match s { + PhysicalSide::Top => self.set_${data.longhands_by_name[props[0]].ident}(v), + PhysicalSide::Right => self.set_${data.longhands_by_name[props[1]].ident}(v), + PhysicalSide::Bottom => self.set_${data.longhands_by_name[props[2]].ident}(v), + PhysicalSide::Left => self.set_${data.longhands_by_name[props[3]].ident}(v), + } + } +</%def> + <%def name="impl_simple_copy(ident, gecko_ffi_name, *kwargs)"> #[allow(non_snake_case)] pub fn copy_${ident}_from(&mut self, other: &Self) { @@ -611,11 +631,14 @@ fn static_assert() { % endfor </%self:impl_trait> -<%self:impl_trait style_struct_name="Margin"></%self:impl_trait> +<%self:impl_trait style_struct_name="Margin"> + ${impl_physical_sides("margin", ["margin-top", "margin-right", "margin-bottom", "margin-left"])} +</%self:impl_trait> <%self:impl_trait style_struct_name="Padding"></%self:impl_trait> <%self:impl_trait style_struct_name="Page"></%self:impl_trait> <%self:impl_trait style_struct_name="Position"> + ${impl_physical_sides("inset", ["top", "right", "bottom", "left"])} pub fn set_computed_justify_items(&mut self, v: values::specified::JustifyItems) { debug_assert_ne!(v, values::specified::JustifyItems::legacy()); self.mJustifyItems.computed = v; diff --git a/servo/components/style/style_adjuster.rs b/servo/components/style/style_adjuster.rs @@ -7,6 +7,7 @@ use crate::computed_value_flags::ComputedValueFlags; use crate::dom::TElement; +use crate::logical_geometry::PhysicalSide; use crate::properties::longhands::display::computed_value::T as Display; use crate::properties::longhands::float::computed_value::T as Float; use crate::properties::longhands::position::computed_value::T as Position; @@ -25,6 +26,23 @@ use crate::values::specified::position::{ #[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 @@ -917,23 +935,139 @@ impl<'a, 'b: 'a> StyleAdjuster<'a, 'b> { /// overkill for now. fn adjust_for_try_tactic(&mut self, tactic: PositionTryFallbacksTryTactic) { debug_assert!(!tactic.is_empty()); + let horizontal = self.style.writing_mode.is_horizontal(); // TODO: Flip inset / margin / sizes percentages and anchor lookup sides as necessary. for tactic in tactic.into_iter() { match tactic { PositionTryFallbacksTryTacticKeyword::None => break, PositionTryFallbacksTryTacticKeyword::FlipBlock => { self.flip_self_alignment(/* block = */ true); + self.flip_insets_and_margins(!horizontal); }, PositionTryFallbacksTryTacticKeyword::FlipInline => { self.flip_self_alignment(/* block = */ false); + self.flip_insets_and_margins(horizontal); }, PositionTryFallbacksTryTacticKeyword::FlipStart => { - self.flip_alignment_start(); + self.flip_start(); }, } } } + 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(); + let b = b.clone(); + let pos = self.style.mutate_position(); + pos.set_inset(a_side, b); + pos.set_inset(b_side, a); + } + + 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(); + let b = b.clone(); + let margin = self.style.mutate_margin(); + margin.set_margin(a_side, b); + margin.set_margin(b_side, a); + } + + 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_insets(bs, is); + self.swap_insets(ie, be); + self.swap_margins(bs, is); + self.swap_margins(ie, be); + self.flip_alignment_start(); + } + + fn flip_insets_and_margins(&mut self, horizontal: bool) { + if horizontal { + // TODO: Avoid the clone here? + flip_property!( + self, + get_position, + mutate_position, + clone_left, + set_left, + clone_right, + set_right + ); + flip_property!( + self, + get_margin, + mutate_margin, + clone_margin_left, + set_margin_left, + clone_margin_right, + set_margin_right + ); + } else { + flip_property!( + self, + get_position, + mutate_position, + clone_top, + set_top, + clone_bottom, + set_bottom + ); + flip_property!( + self, + get_margin, + mutate_margin, + clone_margin_top, + set_margin_top, + clone_margin_bottom, + set_margin_bottom + ); + } + } + fn flip_alignment_start(&mut self) { let pos = self.style.get_position(); let align = pos.clone_align_self(); diff --git a/servo/components/style/values/specified/position.rs b/servo/components/style/values/specified/position.rs @@ -7,9 +7,7 @@ //! //! [position]: https://drafts.csswg.org/css-backgrounds-3/#position -use crate::logical_geometry::{PhysicalSide, WritingMode}; use crate::parser::{Parse, ParserContext}; -use crate::properties::LonghandId; use crate::selector_map::PrecomputedHashMap; use crate::str::HTML_SPACE_CHARACTERS; use crate::values::computed::LengthPercentage as ComputedLengthPercentage; @@ -586,84 +584,11 @@ impl PositionTryFallbacksTryTactic { self.0.is_none() } - fn flip_vertical(id: LonghandId) -> LonghandId { - match id { - LonghandId::Top => LonghandId::Bottom, - LonghandId::Bottom => LonghandId::Top, - LonghandId::MarginTop => LonghandId::MarginBottom, - LonghandId::MarginBottom => LonghandId::MarginTop, - _ => id, - } - } - - fn flip_horizontal(id: LonghandId) -> LonghandId { - match id { - LonghandId::Left => LonghandId::Right, - LonghandId::Right => LonghandId::Left, - LonghandId::MarginLeft => LonghandId::MarginRight, - LonghandId::MarginRight => LonghandId::MarginLeft, - _ => id, - } - } - - fn flip_start(id: LonghandId, wm: WritingMode) -> LonghandId { - use LonghandId::*; - let (physical_side, is_margin) = match id { - Width => return Height, - Height => return Width, - MinWidth => return MinHeight, - MinHeight => return MinWidth, - MaxWidth => return MaxHeight, - MaxHeight => return MaxWidth, - - Top => (PhysicalSide::Top, false), - Right => (PhysicalSide::Right, false), - Bottom => (PhysicalSide::Bottom, false), - Left => (PhysicalSide::Left, false), - - MarginTop => (PhysicalSide::Top, true), - MarginRight => (PhysicalSide::Right, true), - MarginBottom => (PhysicalSide::Bottom, true), - MarginLeft => (PhysicalSide::Left, true), - _ => return id, - }; - - match wm.flipped_start_side(physical_side) { - PhysicalSide::Top => if is_margin { MarginTop } else { Top }, - PhysicalSide::Right => if is_margin { MarginRight } else { Right }, - PhysicalSide::Bottom => if is_margin { MarginBottom } else { Bottom }, - PhysicalSide::Left => if is_margin { MarginLeft } else { Left }, - } - } - /// Iterates over the fallbacks in order. #[inline] pub fn into_iter(&self) -> impl IntoIterator<Item = PositionTryFallbacksTryTacticKeyword> { [self.0, self.1, self.2] } - - /// Applies a try tactic to a given property. - pub fn apply_to_property(&self, mut id: LonghandId, wm: WritingMode) -> LonghandId { - debug_assert!(!id.is_logical(), "Logical props should've been replaced already"); - debug_assert!(!self.is_empty(), "Should have something to do"); - // TODO(emilio): Consider building a LonghandIdSet to check for unaffected properties, and - // bailing out earlier? - for tactic in self.into_iter() { - id = match tactic { - PositionTryFallbacksTryTacticKeyword::None => break, - PositionTryFallbacksTryTacticKeyword::FlipInline | - PositionTryFallbacksTryTacticKeyword::FlipBlock => { - if wm.is_horizontal() == (tactic == PositionTryFallbacksTryTacticKeyword::FlipInline) { - Self::flip_horizontal(id) - } else { - Self::flip_vertical(id) - } - }, - PositionTryFallbacksTryTacticKeyword::FlipStart => Self::flip_start(id, wm), - } - } - id - } } #[derive(