From efea71ff9bb2e3b737d4503e7c18b023cde5d991 Mon Sep 17 00:00:00 2001 From: Oriol Brufau Date: Tue, 16 May 2023 08:02:28 +0200 Subject: [PATCH 01/65] style: Cherry-pick various servo changes All formatting and not-part-of-the-gecko-build changes. Differential Revision: https://phabricator.services.mozilla.com/D106634 --- components/style/values/specified/counters.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/components/style/values/specified/counters.rs b/components/style/values/specified/counters.rs index ece5739ba0e..61014f8d3b4 100644 --- a/components/style/values/specified/counters.rs +++ b/components/style/values/specified/counters.rs @@ -17,7 +17,7 @@ use crate::values::specified::Attr; use crate::values::specified::Integer; use crate::values::CustomIdent; use cssparser::{Parser, Token}; -#[cfg(feature = "servo-layout-2013")] +#[cfg(any(feature = "gecko", feature = "servo-layout-2013"))] use selectors::parser::SelectorParseErrorKind; use style_traits::{ParseError, StyleParseErrorKind}; @@ -171,6 +171,7 @@ impl Parse for Content { Ok(generics::ContentItem::Attr(Attr::parse_function(context, input)?)) }), _ => { + use style_traits::StyleParseErrorKind; let name = name.clone(); return Err(input.new_custom_error( StyleParseErrorKind::UnexpectedFunction(name), From 320f12aa07333ec3d627486a2d7cf7716beb421c Mon Sep 17 00:00:00 2001 From: Oriol Brufau Date: Tue, 16 May 2023 07:17:07 +0200 Subject: [PATCH 02/65] style: Simplify StyleColor representation There's no need for CurrentColor / Numeric variants when we can represent them with the complex form. Differential Revision: https://phabricator.services.mozilla.com/D106690 --- components/style/values/animated/color.rs | 290 +++++++++++---------- components/style/values/computed/color.rs | 42 +-- components/style/values/generics/color.rs | 62 +++-- components/style/values/resolved/color.rs | 4 +- components/style/values/specified/color.rs | 12 +- 5 files changed, 212 insertions(+), 198 deletions(-) diff --git a/components/style/values/animated/color.rs b/components/style/values/animated/color.rs index 9f4fa5c52b5..4ef73dda276 100644 --- a/components/style/values/animated/color.rs +++ b/components/style/values/animated/color.rs @@ -35,17 +35,14 @@ impl RGBA { #[inline] pub fn new(red: f32, green: f32, blue: f32, alpha: f32) -> Self { RGBA { - red: red, - green: green, - blue: blue, - alpha: alpha, + red, + green, + blue, + alpha, } } } -/// Unlike Animate for computed colors, we don't clamp any component values. -/// -/// FIXME(nox): Why do computed colors even implement Animate? impl Animate for RGBA { #[inline] fn animate(&self, other: &Self, procedure: Procedure) -> Result { @@ -57,15 +54,11 @@ impl Animate for RGBA { } alpha = alpha.min(1.); - let red = - (self.red * self.alpha).animate(&(other.red * other.alpha), procedure)? * 1. / alpha; - let green = (self.green * self.alpha).animate(&(other.green * other.alpha), procedure)? * - 1. / - alpha; - let blue = - (self.blue * self.alpha).animate(&(other.blue * other.alpha), procedure)? * 1. / alpha; - - Ok(RGBA::new(red, green, blue, alpha)) + let red = (self.red * self.alpha).animate(&(other.red * other.alpha), procedure)?; + let green = (self.green * self.alpha).animate(&(other.green * other.alpha), procedure)?; + let blue = (self.blue * self.alpha).animate(&(other.blue * other.alpha), procedure)?; + let inv = 1. / alpha; + Ok(RGBA::new(red * inv, green * inv, blue * inv, alpha)) } } @@ -97,21 +90,34 @@ pub type Color = GenericColor; impl Color { fn effective_intermediate_rgba(&self) -> RGBA { - match *self { - GenericColor::Numeric(color) => color, - GenericColor::CurrentColor => RGBA::transparent(), - GenericColor::Complex { color, ratios } => RGBA { - alpha: color.alpha * ratios.bg, - ..color.clone() - }, + if self.ratios.bg == 0. { + return RGBA::transparent(); + } + + if self.ratios.bg == 1. { + return self.color; + } + + RGBA { + alpha: self.color.alpha * self.ratios.bg, + ..self.color } } - fn effective_ratios(&self) -> ComplexColorRatios { - match *self { - GenericColor::Numeric(..) => ComplexColorRatios::NUMERIC, - GenericColor::CurrentColor => ComplexColorRatios::CURRENT_COLOR, - GenericColor::Complex { ratios, .. } => ratios, + fn scaled_rgba(&self) -> RGBA { + if self.ratios.bg == 0. { + return RGBA::transparent(); + } + + if self.ratios.bg == 1. { + return self.color; + } + + RGBA { + red: self.color.red * self.ratios.bg, + green: self.color.green * self.ratios.bg, + blue: self.color.blue * self.ratios.bg, + alpha: self.color.alpha * self.ratios.bg, } } } @@ -119,140 +125,140 @@ impl Color { impl Animate for Color { #[inline] fn animate(&self, other: &Self, procedure: Procedure) -> Result { - use self::GenericColor::*; + let self_numeric = self.is_numeric(); + let other_numeric = other.is_numeric(); - // Common cases are interpolating between two numeric colors, - // two currentcolors, and a numeric color and a currentcolor. - let (this_weight, other_weight) = procedure.weights(); + if self_numeric && other_numeric { + return Ok(Self::rgba(self.color.animate(&other.color, procedure)?)); + } - Ok(match (*self, *other, procedure) { - // Any interpolation of currentcolor with currentcolor returns currentcolor. - (CurrentColor, CurrentColor, Procedure::Interpolate { .. }) => CurrentColor, - // Animating two numeric colors. - (Numeric(c1), Numeric(c2), _) => Numeric(c1.animate(&c2, procedure)?), - // Combinations of numeric color and currentcolor - (CurrentColor, Numeric(color), _) => Self::with_ratios( - color, - ComplexColorRatios { - bg: other_weight as f32, - fg: this_weight as f32, - }, - ), - (Numeric(color), CurrentColor, _) => Self::with_ratios( - color, - ComplexColorRatios { - bg: this_weight as f32, - fg: other_weight as f32, - }, - ), + let self_currentcolor = self.is_currentcolor(); + let other_currentcolor = other.is_currentcolor(); - // Any other animation of currentcolor with currentcolor. - (CurrentColor, CurrentColor, _) => Self::with_ratios( + if self_currentcolor && other_currentcolor { + let (self_weight, other_weight) = procedure.weights(); + return Ok(Self::new( RGBA::transparent(), ComplexColorRatios { bg: 0., - fg: (this_weight + other_weight) as f32, + fg: (self_weight + other_weight) as f32, }, - ), + )); + } - // Defer to complex calculations - _ => { - // Compute the "scaled" contribution for `color`. - fn scaled_rgba(color: &Color) -> RGBA { - match *color { - GenericColor::Numeric(color) => color, - GenericColor::CurrentColor => RGBA::transparent(), - GenericColor::Complex { color, ratios } => RGBA { - red: color.red * ratios.bg, - green: color.green * ratios.bg, - blue: color.blue * ratios.bg, - alpha: color.alpha * ratios.bg, - }, - } - } + // FIXME(emilio): Without these special cases tests fail, looks fairly + // sketchy! + if (self_currentcolor && other_numeric) || (self_numeric && other_currentcolor) { + let (self_weight, other_weight) = procedure.weights(); + return Ok(if self_numeric { + Self::new( + self.color, + ComplexColorRatios { + bg: self_weight as f32, + fg: other_weight as f32, + }, + ) + } else { + Self::new( + other.color, + ComplexColorRatios { + bg: other_weight as f32, + fg: self_weight as f32, + }, + ) + }); + } - // Each `Color`, represents a complex combination of foreground color and - // background color where fg and bg represent the overall - // contributions. ie: - // - // color = { bg * mColor, fg * foreground } - // = { bg_color , fg_color } - // = bg_color + fg_color - // - // where `foreground` is `currentcolor`, and `bg_color`, - // `fg_color` are the scaled background and foreground - // contributions. - // - // Each operation, lerp, addition, or accumulate, can be - // represented as a scaled-addition each complex color. ie: - // - // p * col1 + q * col2 - // - // where p = (1 - a), q = a for lerp(a), p = 1, q = 1 for - // addition, etc. - // - // Therefore: - // - // col1 op col2 - // = p * col1 + q * col2 - // = p * { bg_color1, fg_color1 } + q * { bg_color2, fg_color2 } - // = p * (bg_color1 + fg_color1) + q * (bg_color2 + fg_color2) - // = p * bg_color1 + p * fg_color1 + q * bg_color2 + p * fg_color2 - // = (p * bg_color1 + q * bg_color2) + (p * fg_color1 + q * fg_color2) - // = (bg_color1 op bg_color2) + (fg_color1 op fg_color2) - // - // fg_color1 op fg_color2 is equivalent to (fg1 op fg2) * foreground, - // so the final color is: - // - // = { bg_color, fg_color } - // = { 1 * (bg_color1 op bg_color2), (fg1 op fg2) * foreground } + // Compute the "scaled" contribution for `color`. + // Each `Color`, represents a complex combination of foreground color and + // background color where fg and bg represent the overall + // contributions. ie: + // + // color = { bg * mColor, fg * foreground } + // = { bg_color , fg_color } + // = bg_color + fg_color + // + // where `foreground` is `currentcolor`, and `bg_color`, + // `fg_color` are the scaled background and foreground + // contributions. + // + // Each operation, lerp, addition, or accumulate, can be + // represented as a scaled-addition each complex color. ie: + // + // p * col1 + q * col2 + // + // where p = (1 - a), q = a for lerp(a), p = 1, q = 1 for + // addition, etc. + // + // Therefore: + // + // col1 op col2 + // = p * col1 + q * col2 + // = p * { bg_color1, fg_color1 } + q * { bg_color2, fg_color2 } + // = p * (bg_color1 + fg_color1) + q * (bg_color2 + fg_color2) + // = p * bg_color1 + p * fg_color1 + q * bg_color2 + p * fg_color2 + // = (p * bg_color1 + q * bg_color2) + (p * fg_color1 + q * fg_color2) + // = (bg_color1 op bg_color2) + (fg_color1 op fg_color2) + // + // fg_color1 op fg_color2 is equivalent to (fg1 op fg2) * foreground, + // so the final color is: + // + // = { bg_color, fg_color } + // = { 1 * (bg_color1 op bg_color2), (fg1 op fg2) * foreground } + // + // To perform the operation on two complex colors, we need to + // generate the scaled contributions of each background color + // component. + let bg_color1 = self.scaled_rgba(); + let bg_color2 = other.scaled_rgba(); - // To perform the operation on two complex colors, we need to - // generate the scaled contributions of each background color - // component. - let bg_color1 = scaled_rgba(self); - let bg_color2 = scaled_rgba(other); - // Perform bg_color1 op bg_color2 - let bg_color = bg_color1.animate(&bg_color2, procedure)?; + // Perform bg_color1 op bg_color2 + let bg_color = bg_color1.animate(&bg_color2, procedure)?; - // Calculate the final foreground color ratios; perform - // animation on effective fg ratios. - let ComplexColorRatios { fg: fg1, .. } = self.effective_ratios(); - let ComplexColorRatios { fg: fg2, .. } = other.effective_ratios(); - // Perform fg1 op fg2 - let fg = fg1.animate(&fg2, procedure)?; + // Calculate the final foreground color ratios; perform + // animation on effective fg ratios. + let fg = self.ratios.fg.animate(&other.ratios.fg, procedure)?; - Self::with_ratios(bg_color, ComplexColorRatios { bg: 1., fg }) - }, - }) + Ok(Self::new(bg_color, ComplexColorRatios { bg: 1., fg })) } } impl ComputeSquaredDistance for Color { #[inline] fn compute_squared_distance(&self, other: &Self) -> Result { - use self::GenericColor::*; + // All comments from the Animate impl also apply here. + let self_numeric = self.is_numeric(); + let other_numeric = other.is_numeric(); - // All comments from the Animate impl also applies here. - Ok(match (*self, *other) { - (CurrentColor, CurrentColor) => SquaredDistance::from_sqrt(0.), - (Numeric(c1), Numeric(c2)) => c1.compute_squared_distance(&c2)?, - (CurrentColor, Numeric(color)) | (Numeric(color), CurrentColor) => { - // `computed_squared_distance` is symmetric. - color.compute_squared_distance(&RGBA::transparent())? + - SquaredDistance::from_sqrt(1.) - }, - (_, _) => { - let self_color = self.effective_intermediate_rgba(); - let other_color = other.effective_intermediate_rgba(); - let self_ratios = self.effective_ratios(); - let other_ratios = other.effective_ratios(); + if self_numeric && other_numeric { + return self.color.compute_squared_distance(&other.color); + } - self_color.compute_squared_distance(&other_color)? + - self_ratios.bg.compute_squared_distance(&other_ratios.bg)? + - self_ratios.fg.compute_squared_distance(&other_ratios.fg)? - }, - }) + let self_currentcolor = self.is_currentcolor(); + let other_currentcolor = other.is_currentcolor(); + if self_currentcolor && other_currentcolor { + return Ok(SquaredDistance::from_sqrt(0.)); + } + + if (self_currentcolor && other_numeric) || (self_numeric && other_currentcolor) { + let color = if self_numeric { + &self.color + } else { + &other.color + }; + // `computed_squared_distance` is symmetric. + return Ok(color.compute_squared_distance(&RGBA::transparent())? + + SquaredDistance::from_sqrt(1.)); + } + + let self_color = self.effective_intermediate_rgba(); + let other_color = other.effective_intermediate_rgba(); + let self_ratios = self.ratios; + let other_ratios = other.ratios; + + Ok(self_color.compute_squared_distance(&other_color)? + + self_ratios.bg.compute_squared_distance(&other_ratios.bg)? + + self_ratios.fg.compute_squared_distance(&other_ratios.fg)?) } } diff --git a/components/style/values/computed/color.rs b/components/style/values/computed/color.rs index 6098ea4590e..5fdc29a5feb 100644 --- a/components/style/values/computed/color.rs +++ b/components/style/values/computed/color.rs @@ -29,13 +29,18 @@ impl Color { /// Combine this complex color with the given foreground color into /// a numeric RGBA color. It currently uses linear blending. pub fn to_rgba(&self, fg_color: RGBA) -> RGBA { - let (color, ratios) = match *self { - // Common cases that the complex color is either pure numeric - // color or pure currentcolor. - GenericColor::Numeric(color) => return color, - GenericColor::CurrentColor => return fg_color, - GenericColor::Complex { color, ratios } => (color, ratios), - }; + // Common cases that the complex color is either pure numeric color or + // pure currentcolor. + if self.is_numeric() { + return self.color; + } + + if self.is_currentcolor() { + return fg_color; + } + + let ratios = &self.ratios; + let color = &self.color; // For the more complicated case that the alpha value differs, // we use the following formula to compute the components: @@ -59,13 +64,14 @@ impl Color { if a <= 0. { return RGBA::transparent(); } - let a = f32::min(a, 1.); + let a = a.min(1.); - let inverse_a = 1. / a; - let r = (p1 * r1 + p2 * r2) * inverse_a; - let g = (p1 * g1 + p2 * g2) * inverse_a; - let b = (p1 * b1 + p2 * b2) * inverse_a; - return RGBA::from_floats(r, g, b, a); + let inv = 1. / a; + + let r = (p1 * r1 + p2 * r2) * inv; + let g = (p1 * g1 + p2 * g2) * inv; + let b = (p1 * b1 + p2 * b2) * inv; + RGBA::from_floats(r, g, b, a) } } @@ -74,11 +80,13 @@ impl ToCss for Color { where W: fmt::Write, { - match *self { - GenericColor::Numeric(color) => color.to_css(dest), - GenericColor::CurrentColor => CSSParserColor::CurrentColor.to_css(dest), - _ => Ok(()), + if self.is_currentcolor() { + return CSSParserColor::CurrentColor.to_css(dest); } + if self.is_numeric() { + return self.color.to_css(dest); + } + Ok(()) } } diff --git a/components/style/values/generics/color.rs b/components/style/values/generics/color.rs index b4f2e7445ea..63c4c401689 100644 --- a/components/style/values/generics/color.rs +++ b/components/style/values/generics/color.rs @@ -6,6 +6,11 @@ /// Ratios representing the contribution of color and currentcolor to /// the final color value. +/// +/// NOTE(emilio): For animated colors, the sum of these two might be more than +/// one (because the background color would've been scaled down already). So +/// beware that it is not generally safe to assume that if bg is 1 then fg is 0, +/// for example. #[derive(Clone, Copy, Debug, MallocSizeOf, PartialEq, ToAnimatedValue, ToShmem)] #[repr(C)] pub struct ComplexColorRatios { @@ -22,59 +27,52 @@ impl ComplexColorRatios { pub const CURRENT_COLOR: ComplexColorRatios = ComplexColorRatios { bg: 0., fg: 1. }; } -/// This enum represents a combined color from a numeric color and +/// This struct represents a combined color from a numeric color and /// the current foreground color (currentcolor keyword). #[derive(Clone, Copy, Debug, MallocSizeOf, PartialEq, ToAnimatedValue, ToShmem)] -#[repr(C, u8)] -pub enum GenericColor { - /// Numeric RGBA color. - Numeric(RGBA), - - /// The current foreground color. - CurrentColor, - - /// A linear combination of numeric color and currentcolor. +#[repr(C)] +pub struct GenericColor { + /// The actual numeric color. + pub color: RGBA, + /// The ratios of mixing between numeric and currentcolor. /// The formula is: `color * ratios.bg + currentcolor * ratios.fg`. - Complex { - /// The actual numeric color. - color: RGBA, - /// The ratios of mixing between numeric and currentcolor. - ratios: ComplexColorRatios, - }, + pub ratios: ComplexColorRatios, } pub use self::GenericColor as Color; +impl Color { + /// Returns a color value representing currentcolor. + pub fn currentcolor() -> Self { + Color { + color: cssparser::RGBA::transparent(), + ratios: ComplexColorRatios::CURRENT_COLOR, + } + } +} + impl Color { /// Create a color based upon the specified ratios. - pub fn with_ratios(color: RGBA, ratios: ComplexColorRatios) -> Self { - if ratios == ComplexColorRatios::NUMERIC { - Color::Numeric(color) - } else if ratios == ComplexColorRatios::CURRENT_COLOR { - Color::CurrentColor - } else { - Color::Complex { color, ratios } - } + pub fn new(color: RGBA, ratios: ComplexColorRatios) -> Self { + Self { color, ratios } } /// Returns a numeric color representing the given RGBA value. pub fn rgba(color: RGBA) -> Self { - Color::Numeric(color) - } - - /// Returns a complex color value representing currentcolor. - pub fn currentcolor() -> Self { - Color::CurrentColor + Self { + color, + ratios: ComplexColorRatios::NUMERIC, + } } /// Whether it is a numeric color (no currentcolor component). pub fn is_numeric(&self) -> bool { - matches!(*self, Color::Numeric(..)) + self.ratios == ComplexColorRatios::NUMERIC } /// Whether it is a currentcolor value (no numeric color component). pub fn is_currentcolor(&self) -> bool { - matches!(*self, Color::CurrentColor) + self.ratios == ComplexColorRatios::CURRENT_COLOR } } diff --git a/components/style/values/resolved/color.rs b/components/style/values/resolved/color.rs index 1b845c58e9f..c098815701c 100644 --- a/components/style/values/resolved/color.rs +++ b/components/style/values/resolved/color.rs @@ -20,7 +20,7 @@ impl ToResolvedValue for computed::Color { #[inline] fn from_resolved_value(resolved: Self::ResolvedValue) -> Self { - generics::Color::Numeric(resolved) + generics::Color::rgba(resolved) } } @@ -33,7 +33,7 @@ impl ToResolvedValue for computed::ColorOrAuto { fn to_resolved_value(self, context: &Context) -> Self::ResolvedValue { let color = match self { generics::ColorOrAuto::Color(color) => color, - generics::ColorOrAuto::Auto => generics::Color::CurrentColor, + generics::ColorOrAuto::Auto => generics::Color::currentcolor(), }; color.to_resolved_value(context) } diff --git a/components/style/values/specified/color.rs b/components/style/values/specified/color.rs index e6491049619..71b1c5b077c 100644 --- a/components/style/values/specified/color.rs +++ b/components/style/values/specified/color.rs @@ -9,7 +9,7 @@ use super::AllowQuirks; use crate::gecko_bindings::structs::nscolor; use crate::parser::{Parse, ParserContext}; use crate::values::computed::{Color as ComputedColor, Context, ToComputedValue}; -use crate::values::generics::color::{Color as GenericColor, ColorOrAuto as GenericColorOrAuto}; +use crate::values::generics::color::{ColorOrAuto as GenericColorOrAuto}; use crate::values::specified::calc::CalcNode; use cssparser::{AngleOrNumber, Color as CSSParserColor, Parser, Token, RGBA}; use cssparser::{BasicParseErrorKind, NumberOrPercentage, ParseErrorKind}; @@ -585,11 +585,13 @@ impl ToComputedValue for Color { } fn from_computed_value(computed: &ComputedColor) -> Self { - match *computed { - GenericColor::Numeric(color) => Color::rgba(color), - GenericColor::CurrentColor => Color::currentcolor(), - GenericColor::Complex { .. } => Color::Complex(*computed), + if computed.is_numeric() { + return Color::rgba(computed.color); } + if computed.is_currentcolor() { + return Color::currentcolor(); + } + Color::Complex(*computed) } } From 291b3ee57325c50077c3a3a5693a92a79a9b8fe3 Mon Sep 17 00:00:00 2001 From: Oriol Brufau Date: Tue, 16 May 2023 07:22:42 +0200 Subject: [PATCH 03/65] style: Move extremum lengths to the individual Size / MaxSize types This will prevent growing them when introducing fit-content(). This can _almost_ be derived, if it wasn't because of the quirky stuff. I think the macro is probably good enough for now but let me know if you disagree. Differential Revision: https://phabricator.services.mozilla.com/D106713 --- components/style/values/computed/length.rs | 36 ++--------------- components/style/values/computed/mod.rs | 2 +- components/style/values/generics/length.rs | 21 +++++++--- components/style/values/specified/length.rs | 43 +++++++++++---------- 4 files changed, 44 insertions(+), 58 deletions(-) diff --git a/components/style/values/computed/length.rs b/components/style/values/computed/length.rs index 0908273b88c..bcba3eb9bce 100644 --- a/components/style/values/computed/length.rs +++ b/components/style/values/computed/length.rs @@ -185,7 +185,10 @@ impl Size { GenericSize::Auto => false, GenericSize::LengthPercentage(ref lp) => lp.is_definitely_zero(), #[cfg(feature = "gecko")] - GenericSize::ExtremumLength(..) => false, + GenericSize::MinContent | + GenericSize::MaxContent | + GenericSize::MozFitContent | + GenericSize::MozAvailable => false } } } @@ -495,37 +498,6 @@ pub type NonNegativeLengthPercentageOrNormal = /// Either a non-negative `` or a ``. pub type NonNegativeLengthOrNumber = GenericLengthOrNumber; -/// A type for possible values for min- and max- flavors of width, height, -/// block-size, and inline-size. -#[allow(missing_docs)] -#[cfg_attr(feature = "servo", derive(Deserialize, Serialize))] -#[derive( - Clone, - Copy, - Debug, - Eq, - FromPrimitive, - MallocSizeOf, - Parse, - PartialEq, - SpecifiedValueInfo, - ToAnimatedValue, - ToAnimatedZero, - ToComputedValue, - ToCss, - ToResolvedValue, - ToShmem, -)] -#[repr(u8)] -pub enum ExtremumLength { - #[parse(aliases = "-moz-max-content")] - MaxContent, - #[parse(aliases = "-moz-min-content")] - MinContent, - MozFitContent, - MozAvailable, -} - /// A computed value for `min-width`, `min-height`, `width` or `height` property. pub type Size = GenericSize; diff --git a/components/style/values/computed/mod.rs b/components/style/values/computed/mod.rs index 9dbbf40b5f4..0c9496c2fad 100644 --- a/components/style/values/computed/mod.rs +++ b/components/style/values/computed/mod.rs @@ -63,7 +63,7 @@ pub use self::font::{FontVariantAlternates, FontWeight}; pub use self::font::{FontVariantEastAsian, FontVariationSettings}; pub use self::font::{MathDepth, MozScriptMinSize, MozScriptSizeMultiplier, XLang, XTextZoom}; pub use self::image::{Gradient, Image, LineDirection, MozImageRect}; -pub use self::length::{CSSPixelLength, ExtremumLength, NonNegativeLength}; +pub use self::length::{CSSPixelLength, NonNegativeLength}; pub use self::length::{Length, LengthOrNumber, LengthPercentage, NonNegativeLengthOrNumber}; pub use self::length::{LengthOrAuto, LengthPercentageOrAuto, MaxSize, Size}; pub use self::length::{NonNegativeLengthPercentage, NonNegativeLengthPercentageOrAuto}; diff --git a/components/style/values/generics/length.rs b/components/style/values/generics/length.rs index d9a074623c3..879f3a0f502 100644 --- a/components/style/values/generics/length.rs +++ b/components/style/values/generics/length.rs @@ -6,7 +6,6 @@ use crate::parser::{Parse, ParserContext}; #[cfg(feature = "gecko")] -use crate::values::computed::ExtremumLength; use crate::Zero; use cssparser::Parser; use style_traits::ParseError; @@ -151,9 +150,14 @@ impl Parse for LengthPercentageOrAuto pub enum GenericSize { LengthPercentage(LengthPercent), Auto, - #[cfg(feature = "gecko")] #[animation(error)] - ExtremumLength(ExtremumLength), + MaxContent, + #[animation(error)] + MinContent, + #[animation(error)] + MozFitContent, + #[animation(error)] + MozAvailable, } pub use self::GenericSize as Size; @@ -194,9 +198,16 @@ impl Size { pub enum GenericMaxSize { LengthPercentage(LengthPercent), None, - #[cfg(feature = "gecko")] #[animation(error)] - ExtremumLength(ExtremumLength), + #[parse(aliases = "-moz-max-content")] + MaxContent, + #[animation(error)] + #[parse(aliases = "-moz-min-content")] + MinContent, + #[animation(error)] + MozFitContent, + #[animation(error)] + MozAvailable, } pub use self::GenericMaxSize as MaxSize; diff --git a/components/style/values/specified/length.rs b/components/style/values/specified/length.rs index cf529408c98..aaad895dbce 100644 --- a/components/style/values/specified/length.rs +++ b/components/style/values/specified/length.rs @@ -1226,6 +1226,27 @@ impl Parse for Size { } } +macro_rules! parse_size_non_length { + ($size:ident, $input:expr, $auto_or_none:expr => $auto_or_none_ident:ident) => {{ + let size = $input.try_parse(|input| { + Ok(try_match_ident_ignore_ascii_case! { input, + #[cfg(feature = "gecko")] + "min-content" | "-moz-min-content" => $size::MinContent, + #[cfg(feature = "gecko")] + "max-content" | "-moz-max-content" => $size::MaxContent, + #[cfg(feature = "gecko")] + "-moz-fit-content" => $size::MozFitContent, + #[cfg(feature = "gecko")] + "-moz-available" => $size::MozAvailable, + $auto_or_none => $size::$auto_or_none_ident, + }) + }); + if size.is_ok() { + return size; + } + }}; +} + impl Size { /// Parses, with quirks. pub fn parse_quirky<'i, 't>( @@ -1233,16 +1254,7 @@ impl Size { input: &mut Parser<'i, 't>, allow_quirks: AllowQuirks, ) -> Result> { - #[cfg(feature = "gecko")] - { - if let Ok(l) = input.try_parse(computed::ExtremumLength::parse) { - return Ok(GenericSize::ExtremumLength(l)); - } - } - - if input.try_parse(|i| i.expect_ident_matching("auto")).is_ok() { - return Ok(GenericSize::Auto); - } + parse_size_non_length!(Size, input, "auto" => Auto); let length = NonNegativeLengthPercentage::parse_quirky(context, input, allow_quirks)?; Ok(GenericSize::LengthPercentage(length)) @@ -1274,16 +1286,7 @@ impl MaxSize { input: &mut Parser<'i, 't>, allow_quirks: AllowQuirks, ) -> Result> { - #[cfg(feature = "gecko")] - { - if let Ok(l) = input.try_parse(computed::ExtremumLength::parse) { - return Ok(GenericMaxSize::ExtremumLength(l)); - } - } - - if input.try_parse(|i| i.expect_ident_matching("none")).is_ok() { - return Ok(GenericMaxSize::None); - } + parse_size_non_length!(MaxSize, input, "none" => None); let length = NonNegativeLengthPercentage::parse_quirky(context, input, allow_quirks)?; Ok(GenericMaxSize::LengthPercentage(length)) From 252b50931de9ac8f9ff353e2eca02761f2386018 Mon Sep 17 00:00:00 2001 From: Oriol Brufau Date: Tue, 16 May 2023 08:24:40 +0200 Subject: [PATCH 04/65] Further changes required by Servo --- components/style/values/generics/length.rs | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/components/style/values/generics/length.rs b/components/style/values/generics/length.rs index 879f3a0f502..f3e025f80b7 100644 --- a/components/style/values/generics/length.rs +++ b/components/style/values/generics/length.rs @@ -5,7 +5,6 @@ //! Generic types for CSS values related to length. use crate::parser::{Parse, ParserContext}; -#[cfg(feature = "gecko")] use crate::Zero; use cssparser::Parser; use style_traits::ParseError; @@ -150,12 +149,16 @@ impl Parse for LengthPercentageOrAuto pub enum GenericSize { LengthPercentage(LengthPercent), Auto, + #[cfg(feature = "gecko")] #[animation(error)] MaxContent, + #[cfg(feature = "gecko")] #[animation(error)] MinContent, + #[cfg(feature = "gecko")] #[animation(error)] MozFitContent, + #[cfg(feature = "gecko")] #[animation(error)] MozAvailable, } @@ -198,14 +201,18 @@ impl Size { pub enum GenericMaxSize { LengthPercentage(LengthPercent), None, + #[cfg(feature = "gecko")] #[animation(error)] #[parse(aliases = "-moz-max-content")] MaxContent, + #[cfg(feature = "gecko")] #[animation(error)] #[parse(aliases = "-moz-min-content")] MinContent, + #[cfg(feature = "gecko")] #[animation(error)] MozFitContent, + #[cfg(feature = "gecko")] #[animation(error)] MozAvailable, } From 980d10fc172566a702efc66704a9b17e0545877a Mon Sep 17 00:00:00 2001 From: Oriol Brufau Date: Tue, 16 May 2023 07:26:09 +0200 Subject: [PATCH 05/65] style: Implement basic color-mix() functionality, behind a pref, but exposed to chrome code This is straight-forward and builds on the color animation code. This implements only the syntax, not the whole syntax, which seems fairly more complex. Of course, this only uses sRGB because that's all the colors we support, but it should be feasible to extend to lab() / lch() colors once we support those. I believe this subset of syntax is useful and worth implementing, so people can play with it and say if it's useful. Differential Revision: https://phabricator.services.mozilla.com/D106698 --- components/style/properties/cascade.rs | 44 +++------- components/style/values/animated/color.rs | 5 ++ components/style/values/specified/color.rs | 99 ++++++++++++++++++++-- 3 files changed, 109 insertions(+), 39 deletions(-) diff --git a/components/style/properties/cascade.rs b/components/style/properties/cascade.rs index f7ab9f95fde..275ed7afc38 100644 --- a/components/style/properties/cascade.rs +++ b/components/style/properties/cascade.rs @@ -379,13 +379,15 @@ where type DeclarationsToApplyUnlessOverriden = SmallVec<[PropertyDeclaration; 2]>; fn tweak_when_ignoring_colors( - builder: &StyleBuilder, + context: &computed::Context, longhand_id: LonghandId, origin: Origin, declaration: &mut Cow, declarations_to_apply_unless_overriden: &mut DeclarationsToApplyUnlessOverriden, ) { use crate::values::specified::Color; + use crate::values::computed::ToComputedValue; + use cssparser::RGBA; if !longhand_id.ignored_when_document_colors_disabled() { return; @@ -399,34 +401,16 @@ fn tweak_when_ignoring_colors( // Don't override background-color on ::-moz-color-swatch. It is set as an // author style (via the style attribute), but it's pretty important for it // to show up for obvious reasons :) - if builder.pseudo.map_or(false, |p| p.is_color_swatch()) && + if context.builder.pseudo.map_or(false, |p| p.is_color_swatch()) && longhand_id == LonghandId::BackgroundColor { return; } - fn alpha_channel(color: &Color) -> u8 { - match *color { - // Seems safe enough to assume that the default color and system - // colors are opaque in HCM, though maybe we shouldn't asume the - // later? - #[cfg(feature = "gecko")] - Color::InheritFromBodyQuirk | Color::System(..) => 255, - // We don't have the actual color here, but since except for color: - // transparent we force opaque text colors, it seems sane to do - // this. You can technically fool this bit of code with: - // - // color: transparent; background-color: currentcolor; - // - // but this is best-effort, and that seems unlikely to happen in - // practice. - Color::CurrentColor => 255, - // Complex colors are results of interpolation only and probably - // shouldn't show up around here in HCM, but we've always treated - // them as opaque effectively so keep doing it. - Color::Complex { .. } => 255, - Color::Numeric { ref parsed, .. } => parsed.alpha, - } + fn alpha_channel(color: &Color, context: &computed::Context) -> u8 { + // We assume here currentColor is opaque. + let color = color.to_computed_value(context).to_rgba(RGBA::new(0, 0, 0, 255)); + color.alpha } // A few special-cases ahead. @@ -440,9 +424,9 @@ fn tweak_when_ignoring_colors( // should consider not doing that even if it causes some issues like // bug 1625036, or finding a performant way to preserve the original // widget background color's rgb channels but not alpha... - let alpha = alpha_channel(color); + let alpha = alpha_channel(color, context); if alpha != 0 { - let mut color = builder.device.default_background_color(); + let mut color = context.builder.device.default_background_color(); color.alpha = alpha; declarations_to_apply_unless_overriden .push(PropertyDeclaration::BackgroundColor(color.into())) @@ -450,14 +434,14 @@ fn tweak_when_ignoring_colors( }, PropertyDeclaration::Color(ref color) => { // We honor color: transparent, and "revert-or-initial" otherwise. - if alpha_channel(&color.0) == 0 { + if alpha_channel(&color.0, context) == 0 { return; } // If the inherited color would be transparent, but we would // override this with a non-transparent color, then override it with // the default color. Otherwise just let it inherit through. - if builder.get_parent_inherited_text().clone_color().alpha == 0 { - let color = builder.device.default_color(); + if context.builder.get_parent_inherited_text().clone_color().alpha == 0 { + let color = context.builder.device.default_color(); declarations_to_apply_unless_overriden.push(PropertyDeclaration::Color( specified::ColorPropertyValue(color.into()), )) @@ -631,7 +615,7 @@ impl<'a, 'b: 'a> Cascade<'a, 'b> { // properties that are marked as ignored in that mode. if ignore_colors { tweak_when_ignoring_colors( - &self.context.builder, + &self.context, longhand_id, origin, &mut declaration, diff --git a/components/style/values/animated/color.rs b/components/style/values/animated/color.rs index 4ef73dda276..77091da01ec 100644 --- a/components/style/values/animated/color.rs +++ b/components/style/values/animated/color.rs @@ -104,6 +104,11 @@ impl Color { } } + /// Mix two colors into one. + pub fn mix(left: &Color, right: &Color, progress: f32) -> Self { + left.animate(right, Procedure::Interpolate { progress: progress as f64 }).unwrap() + } + fn scaled_rgba(&self) -> RGBA { if self.ratios.bg == 0. { return RGBA::transparent(); diff --git a/components/style/values/specified/color.rs b/components/style/values/specified/color.rs index 71b1c5b077c..903b867bf73 100644 --- a/components/style/values/specified/color.rs +++ b/components/style/values/specified/color.rs @@ -9,8 +9,9 @@ use super::AllowQuirks; use crate::gecko_bindings::structs::nscolor; use crate::parser::{Parse, ParserContext}; use crate::values::computed::{Color as ComputedColor, Context, ToComputedValue}; -use crate::values::generics::color::{ColorOrAuto as GenericColorOrAuto}; +use crate::values::generics::color::ColorOrAuto as GenericColorOrAuto; use crate::values::specified::calc::CalcNode; +use crate::values::specified::Percentage; use cssparser::{AngleOrNumber, Color as CSSParserColor, Parser, Token, RGBA}; use cssparser::{BasicParseErrorKind, NumberOrPercentage, ParseErrorKind}; use itoa; @@ -19,6 +20,74 @@ use std::io::Write as IoWrite; use style_traits::{CssType, CssWriter, KeywordsCollectFn, ParseError, StyleParseErrorKind}; use style_traits::{SpecifiedValueInfo, ToCss, ValueParseErrorKind}; +/// A restricted version of the css `color-mix()` function, which only supports +/// percentages and sRGB color-space interpolation. +/// +/// https://drafts.csswg.org/css-color-5/#color-mix +#[derive(Clone, Debug, MallocSizeOf, PartialEq, ToShmem)] +#[allow(missing_docs)] +pub struct ColorMix { + pub left: Color, + pub right: Color, + pub percentage: Percentage, +} + +// NOTE(emilio): Syntax is still a bit in-flux, since [1] doesn't seem +// particularly complete, and disagrees with the examples. +// +// [1]: https://github.com/w3c/csswg-drafts/commit/a4316446112f9e814668c2caff7f826f512f8fed +impl Parse for ColorMix { + fn parse<'i, 't>( + context: &ParserContext, + input: &mut Parser<'i, 't>, + ) -> Result> { + let enabled = + context.chrome_rules_enabled() || static_prefs::pref!("layout.css.color-mix.enabled"); + + if !enabled { + return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError)); + } + + input.expect_function_matching("color-mix")?; + + // NOTE(emilio): This implements the syntax described here for now, + // might need to get updated in the future. + // + // https://github.com/w3c/csswg-drafts/issues/6066#issuecomment-789836765 + input.parse_nested_block(|input| { + input.expect_ident_matching("in")?; + // TODO: support multiple interpolation spaces. + input.expect_ident_matching("srgb")?; + input.expect_comma()?; + let left = Color::parse(context, input)?; + let percentage = input.try_parse(|input| { + Percentage::parse(context, input) + }).unwrap_or_else(|_| Percentage::new(0.5)); + input.expect_comma()?; + let right = Color::parse(context, input)?; + + Ok(ColorMix { left, right, percentage }) + }) + } +} + +impl ToCss for ColorMix { + fn to_css(&self, dest: &mut CssWriter) -> fmt::Result + where + W: Write, + { + dest.write_str("color-mix(in srgb, ")?; + self.left.to_css(dest)?; + if self.percentage.get() != 0.5 || self.percentage.is_calc() { + dest.write_str(" ")?; + self.percentage.to_css(dest)?; + } + dest.write_str(", ")?; + self.right.to_css(dest)?; + dest.write_str(")") + } +} + /// Specified color value #[derive(Clone, Debug, MallocSizeOf, PartialEq, ToShmem)] pub enum Color { @@ -36,6 +105,8 @@ pub enum Color { /// A system color #[cfg(feature = "gecko")] System(SystemColor), + /// A color mix. + ColorMix(Box), /// Quirksmode-only rule for inheriting color from the body #[cfg(feature = "gecko")] InheritFromBodyQuirk, @@ -338,8 +409,6 @@ impl<'a, 'b: 'a, 'i: 'a> ::cssparser::ColorComponentParser<'i> for ColorComponen } fn parse_percentage<'t>(&self, input: &mut Parser<'i, 't>) -> Result> { - use crate::values::specified::Percentage; - Ok(Percentage::parse(self.0, input)?.get()) } @@ -398,6 +467,10 @@ impl Parse for Color { } } + if let Ok(mix) = input.try_parse(|i| ColorMix::parse(context, i)) { + return Ok(Color::ColorMix(Box::new(mix))); + } + match e.kind { ParseErrorKind::Basic(BasicParseErrorKind::UnexpectedToken(t)) => { Err(e.location.new_custom_error(StyleParseErrorKind::ValueError( @@ -425,7 +498,9 @@ impl ToCss for Color { Color::Numeric { parsed: ref rgba, .. } => rgba.to_css(dest), + // TODO: Could represent this as a color-mix() instead. Color::Complex(_) => Ok(()), + Color::ColorMix(ref mix) => mix.to_css(dest), #[cfg(feature = "gecko")] Color::System(system) => system.to_css(dest), #[cfg(feature = "gecko")] @@ -562,17 +637,23 @@ impl Color { /// /// If `context` is `None`, and the specified color requires data from /// the context to resolve, then `None` is returned. - pub fn to_computed_color(&self, _context: Option<&Context>) -> Option { + pub fn to_computed_color(&self, context: Option<&Context>) -> Option { Some(match *self { Color::CurrentColor => ComputedColor::currentcolor(), Color::Numeric { ref parsed, .. } => ComputedColor::rgba(*parsed), Color::Complex(ref complex) => *complex, - #[cfg(feature = "gecko")] - Color::System(system) => system.compute(_context?), - #[cfg(feature = "gecko")] - Color::InheritFromBodyQuirk => { - ComputedColor::rgba(_context?.device().body_text_color()) + Color::ColorMix(ref mix) => { + use crate::values::animated::color::Color as AnimatedColor; + use crate::values::animated::ToAnimatedValue; + + let left = mix.left.to_computed_color(context)?.to_animated_value(); + let right = mix.right.to_computed_color(context)?.to_animated_value(); + ToAnimatedValue::from_animated_value(AnimatedColor::mix(&left, &right, mix.percentage.get())) }, + #[cfg(feature = "gecko")] + Color::System(system) => system.compute(context?), + #[cfg(feature = "gecko")] + Color::InheritFromBodyQuirk => ComputedColor::rgba(context?.device().body_text_color()), }) } } From e1f254854d14a73de4478a5551f58b284a0059bf Mon Sep 17 00:00:00 2001 From: Oriol Brufau Date: Tue, 16 May 2023 08:54:16 +0200 Subject: [PATCH 06/65] Further changes required by Servo --- components/style/values/specified/color.rs | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/components/style/values/specified/color.rs b/components/style/values/specified/color.rs index 903b867bf73..6e608d2eb37 100644 --- a/components/style/values/specified/color.rs +++ b/components/style/values/specified/color.rs @@ -32,6 +32,18 @@ pub struct ColorMix { pub percentage: Percentage, } +#[cfg(feature = "gecko")] +#[inline] +fn allow_color_mix() -> bool { + static_prefs::pref!("layout.css.color-mix.enabled") +} + +#[cfg(feature = "servo")] +#[inline] +fn allow_color_mix() -> bool { + false +} + // NOTE(emilio): Syntax is still a bit in-flux, since [1] doesn't seem // particularly complete, and disagrees with the examples. // @@ -42,7 +54,7 @@ impl Parse for ColorMix { input: &mut Parser<'i, 't>, ) -> Result> { let enabled = - context.chrome_rules_enabled() || static_prefs::pref!("layout.css.color-mix.enabled"); + context.chrome_rules_enabled() || allow_color_mix(); if !enabled { return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError)); From 685d269e31b8a7835cb1ce4dc25142f1df9f2b65 Mon Sep 17 00:00:00 2001 From: Oriol Brufau Date: Tue, 16 May 2023 07:28:58 +0200 Subject: [PATCH 07/65] style: Support image-set() on the cursor property Differential Revision: https://phabricator.services.mozilla.com/D106745 --- components/style/values/computed/ui.rs | 4 +-- components/style/values/generics/ui.rs | 9 +++-- components/style/values/specified/image.rs | 42 +++++++++++++++++----- components/style/values/specified/ui.rs | 14 +++++--- 4 files changed, 49 insertions(+), 20 deletions(-) diff --git a/components/style/values/computed/ui.rs b/components/style/values/computed/ui.rs index ae12dfcdae0..562b7d15897 100644 --- a/components/style/values/computed/ui.rs +++ b/components/style/values/computed/ui.rs @@ -5,7 +5,7 @@ //! Computed values for UI properties use crate::values::computed::color::Color; -use crate::values::computed::url::ComputedImageUrl; +use crate::values::computed::image::Image; use crate::values::computed::Number; use crate::values::generics::ui as generics; @@ -16,7 +16,7 @@ pub use crate::values::specified::ui::{MozForceBrokenImageIcon, UserSelect}; pub type Cursor = generics::GenericCursor; /// A computed value for item of `image cursors`. -pub type CursorImage = generics::GenericCursorImage; +pub type CursorImage = generics::GenericCursorImage; /// A computed value for `scrollbar-color` property. pub type ScrollbarColor = generics::GenericScrollbarColor; diff --git a/components/style/values/generics/ui.rs b/components/style/values/generics/ui.rs index 04c2951c70b..ff6aefdc63e 100644 --- a/components/style/values/generics/ui.rs +++ b/components/style/values/generics/ui.rs @@ -61,15 +61,14 @@ impl ToCss for Cursor { Debug, MallocSizeOf, PartialEq, - SpecifiedValueInfo, ToComputedValue, ToResolvedValue, ToShmem, )] #[repr(C)] -pub struct GenericCursorImage { +pub struct GenericCursorImage { /// The url to parse images from. - pub url: ImageUrl, + pub image: Image, /// Whether the image has a hotspot or not. pub has_hotspot: bool, /// The x coordinate. @@ -80,12 +79,12 @@ pub struct GenericCursorImage { pub use self::GenericCursorImage as CursorImage; -impl ToCss for CursorImage { +impl ToCss for CursorImage { fn to_css(&self, dest: &mut CssWriter) -> fmt::Result where W: Write, { - self.url.to_css(dest)?; + self.image.to_css(dest)?; if self.has_hotspot { dest.write_str(" ")?; self.hotspot_x.to_css(dest)?; diff --git a/components/style/values/specified/image.rs b/components/style/values/specified/image.rs index 3ec678cd237..0be0fcd2a0c 100644 --- a/components/style/values/specified/image.rs +++ b/components/style/values/specified/image.rs @@ -181,7 +181,7 @@ impl Parse for Image { context: &ParserContext, input: &mut Parser<'i, 't>, ) -> Result> { - Image::parse_with_cors_mode(context, input, CorsMode::None, /* allow_none = */ true) + Image::parse_with_cors_mode(context, input, CorsMode::None, /* allow_none = */ true, /* only_url = */ false) } } @@ -191,23 +191,32 @@ impl Image { input: &mut Parser<'i, 't>, cors_mode: CorsMode, allow_none: bool, + only_url: bool, ) -> Result> { if allow_none && input.try_parse(|i| i.expect_ident_matching("none")).is_ok() { return Ok(generic::Image::None); } + if let Ok(url) = input .try_parse(|input| SpecifiedImageUrl::parse_with_cors_mode(context, input, cors_mode)) { return Ok(generic::Image::Url(url)); } - if let Ok(gradient) = input.try_parse(|i| Gradient::parse(context, i)) { - return Ok(generic::Image::Gradient(Box::new(gradient))); - } + if image_set_enabled() { - if let Ok(is) = input.try_parse(|input| ImageSet::parse(context, input, cors_mode)) { + if let Ok(is) = input.try_parse(|input| ImageSet::parse(context, input, cors_mode, only_url)) { return Ok(generic::Image::ImageSet(Box::new(is))); } } + + if only_url { + return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError)); + } + + if let Ok(gradient) = input.try_parse(|i| Gradient::parse(context, i)) { + return Ok(generic::Image::Gradient(Box::new(gradient))); + } + if cross_fade_enabled() { if let Ok(cf) = input.try_parse(|input| CrossFade::parse(context, input, cors_mode)) { return Ok(generic::Image::CrossFade(Box::new(cf))); @@ -264,6 +273,21 @@ impl Image { input, CorsMode::Anonymous, /* allow_none = */ true, + /* only_url = */ false, + ) + } + + /// Provides an alternate method for parsing, but only for urls. + pub fn parse_only_url<'i, 't>( + context: &ParserContext, + input: &mut Parser<'i, 't>, + ) -> Result> { + Self::parse_with_cors_mode( + context, + input, + CorsMode::None, + /* allow_none = */ false, + /* only_url = */ true, ) } } @@ -310,7 +334,7 @@ impl CrossFadeImage { cors_mode: CorsMode, ) -> Result> { if let Ok(image) = input.try_parse(|input| { - Image::parse_with_cors_mode(context, input, cors_mode, /* allow_none = */ false) + Image::parse_with_cors_mode(context, input, cors_mode, /* allow_none = */ false, /* only_url = */ false) }) { return Ok(Self::Image(image)); } @@ -339,10 +363,11 @@ impl ImageSet { context: &ParserContext, input: &mut Parser<'i, 't>, cors_mode: CorsMode, + only_url: bool, ) -> Result> { input.expect_function_matching("image-set")?; let items = input.parse_nested_block(|input| { - input.parse_comma_separated(|input| ImageSetItem::parse(context, input, cors_mode)) + input.parse_comma_separated(|input| ImageSetItem::parse(context, input, cors_mode, only_url)) })?; Ok(Self { selected_index: 0, @@ -356,6 +381,7 @@ impl ImageSetItem { context: &ParserContext, input: &mut Parser<'i, 't>, cors_mode: CorsMode, + only_url: bool, ) -> Result> { let image = match input.try_parse(|i| i.expect_url_or_string()) { Ok(url) => Image::Url(SpecifiedImageUrl::parse_from_string( @@ -364,7 +390,7 @@ impl ImageSetItem { cors_mode, )), Err(..) => Image::parse_with_cors_mode( - context, input, cors_mode, /* allow_none = */ false, + context, input, cors_mode, /* allow_none = */ false, /* only_url = */ only_url )?, }; let resolution = input diff --git a/components/style/values/specified/ui.rs b/components/style/values/specified/ui.rs index 74440a67205..d37fe647976 100644 --- a/components/style/values/specified/ui.rs +++ b/components/style/values/specified/ui.rs @@ -7,17 +7,17 @@ use crate::parser::{Parse, ParserContext}; use crate::values::generics::ui as generics; use crate::values::specified::color::Color; -use crate::values::specified::url::SpecifiedImageUrl; +use crate::values::specified::image::Image; use crate::values::specified::Number; use cssparser::Parser; use std::fmt::{self, Write}; -use style_traits::{CssWriter, ParseError, StyleParseErrorKind, ToCss}; +use style_traits::{CssWriter, ParseError, SpecifiedValueInfo, StyleParseErrorKind, ToCss}; /// A specified value for the `cursor` property. pub type Cursor = generics::GenericCursor; /// A specified value for item of `image cursors`. -pub type CursorImage = generics::GenericCursorImage; +pub type CursorImage = generics::GenericCursorImage; impl Parse for Cursor { /// cursor: [ [ ]?]# [auto | default | ...] @@ -47,7 +47,7 @@ impl Parse for CursorImage { ) -> Result> { use crate::Zero; - let url = SpecifiedImageUrl::parse(context, input)?; + let image = Image::parse_only_url(context, input)?; let mut has_hotspot = false; let mut hotspot_x = Number::zero(); let mut hotspot_y = Number::zero(); @@ -59,7 +59,7 @@ impl Parse for CursorImage { } Ok(Self { - url, + image, has_hotspot, hotspot_x, hotspot_y, @@ -67,6 +67,10 @@ impl Parse for CursorImage { } } +// This trait is manually implemented because we don't support the whole +// syntax for cursors +impl SpecifiedValueInfo for CursorImage {} + /// Specified value of `-moz-force-broken-image-icon` #[derive( Clone, From 2bafbb46f5bbc23c9b7a4afc681f06e88fc474f0 Mon Sep 17 00:00:00 2001 From: Oriol Brufau Date: Tue, 16 May 2023 07:32:20 +0200 Subject: [PATCH 08/65] style: Address spec changes re. color-mix It was clarified that the percentages are weights, and two weights are now allowed. Missing percentages are computed as 100% - the other or 50%. Other than that, commas are required etc, which is good since that's how I implemented it originally. Differential Revision: https://phabricator.services.mozilla.com/D107295 --- components/style/values/animated/color.rs | 40 ++++++++++++++- components/style/values/specified/color.rs | 58 ++++++++++++++++++---- 2 files changed, 85 insertions(+), 13 deletions(-) diff --git a/components/style/values/animated/color.rs b/components/style/values/animated/color.rs index 77091da01ec..773310dd9b6 100644 --- a/components/style/values/animated/color.rs +++ b/components/style/values/animated/color.rs @@ -105,8 +105,44 @@ impl Color { } /// Mix two colors into one. - pub fn mix(left: &Color, right: &Color, progress: f32) -> Self { - left.animate(right, Procedure::Interpolate { progress: progress as f64 }).unwrap() + pub fn mix( + left_color: &Color, + left_weight: f32, + right_color: &Color, + right_weight: f32, + ) -> Self { + let left_bg = left_color.scaled_rgba(); + let right_bg = right_color.scaled_rgba(); + let alpha = (left_bg.alpha * left_weight + + right_bg.alpha * right_weight) + .min(1.); + + let mut fg = 0.; + let mut red = 0.; + let mut green = 0.; + let mut blue = 0.; + + let colors = [ + (left_color, &left_bg, left_weight), + (right_color, &right_bg, right_weight), + ]; + + for &(color, bg, weight) in &colors { + fg += color.ratios.fg * weight; + + red += bg.red * bg.alpha * weight; + green += bg.green * bg.alpha * weight; + blue += bg.blue * bg.alpha * weight; + } + + let color = if alpha <= 0. { + RGBA::transparent() + } else { + let inv = 1. / alpha; + RGBA::new(red * inv, green * inv, blue * inv, alpha) + }; + + Self::new(color, ComplexColorRatios { bg: 1., fg }) } fn scaled_rgba(&self) -> RGBA { diff --git a/components/style/values/specified/color.rs b/components/style/values/specified/color.rs index 6e608d2eb37..21b8abe6de8 100644 --- a/components/style/values/specified/color.rs +++ b/components/style/values/specified/color.rs @@ -28,8 +28,9 @@ use style_traits::{SpecifiedValueInfo, ToCss, ValueParseErrorKind}; #[allow(missing_docs)] pub struct ColorMix { pub left: Color, + pub left_percentage: Percentage, pub right: Color, - pub percentage: Percentage, + pub right_percentage: Percentage, } #[cfg(feature = "gecko")] @@ -71,14 +72,27 @@ impl Parse for ColorMix { // TODO: support multiple interpolation spaces. input.expect_ident_matching("srgb")?; input.expect_comma()?; - let left = Color::parse(context, input)?; - let percentage = input.try_parse(|input| { - Percentage::parse(context, input) - }).unwrap_or_else(|_| Percentage::new(0.5)); - input.expect_comma()?; - let right = Color::parse(context, input)?; - Ok(ColorMix { left, right, percentage }) + let left = Color::parse(context, input)?; + let left_percentage = input.try_parse(|input| Percentage::parse(context, input)).ok(); + + input.expect_comma()?; + + let right = Color::parse(context, input)?; + let right_percentage = input + .try_parse(|input| Percentage::parse(context, input)) + .unwrap_or_else(|_| { + Percentage::new(1.0 - left_percentage.map_or(0.5, |p| p.get())) + }); + + let left_percentage = + left_percentage.unwrap_or_else(|| Percentage::new(1.0 - right_percentage.get())); + Ok(ColorMix { + left, + left_percentage, + right, + right_percentage, + }) }) } } @@ -88,14 +102,31 @@ impl ToCss for ColorMix { where W: Write, { + fn can_omit(percent: &Percentage, other: &Percentage, is_left: bool) -> bool { + if percent.is_calc() { + return false; + } + if percent.get() == 0.5 { + return other.get() == 0.5; + } + if is_left { + return false; + } + (1.0 - percent.get() - other.get()).abs() <= f32::EPSILON + } + dest.write_str("color-mix(in srgb, ")?; self.left.to_css(dest)?; - if self.percentage.get() != 0.5 || self.percentage.is_calc() { + if !can_omit(&self.left_percentage, &self.right_percentage, true) { dest.write_str(" ")?; - self.percentage.to_css(dest)?; + self.left_percentage.to_css(dest)?; } dest.write_str(", ")?; self.right.to_css(dest)?; + if !can_omit(&self.right_percentage, &self.left_percentage, false) { + dest.write_str(" ")?; + self.right_percentage.to_css(dest)?; + } dest.write_str(")") } } @@ -660,7 +691,12 @@ impl Color { let left = mix.left.to_computed_color(context)?.to_animated_value(); let right = mix.right.to_computed_color(context)?.to_animated_value(); - ToAnimatedValue::from_animated_value(AnimatedColor::mix(&left, &right, mix.percentage.get())) + ToAnimatedValue::from_animated_value(AnimatedColor::mix( + &left, + mix.left_percentage.get(), + &right, + mix.right_percentage.get(), + )) }, #[cfg(feature = "gecko")] Color::System(system) => system.compute(context?), From 5c3be71f25b7c52ec5da9f6f5d6effbcc06ce61d Mon Sep 17 00:00:00 2001 From: Oriol Brufau Date: Tue, 16 May 2023 07:33:34 +0200 Subject: [PATCH 09/65] style: Have collect_completion_keywords return `url` and `image-set` for CursorImage Differential Revision: https://phabricator.services.mozilla.com/D107381 --- components/style/values/specified/ui.rs | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/components/style/values/specified/ui.rs b/components/style/values/specified/ui.rs index d37fe647976..4594cbfcc77 100644 --- a/components/style/values/specified/ui.rs +++ b/components/style/values/specified/ui.rs @@ -11,7 +11,7 @@ use crate::values::specified::image::Image; use crate::values::specified::Number; use cssparser::Parser; use std::fmt::{self, Write}; -use style_traits::{CssWriter, ParseError, SpecifiedValueInfo, StyleParseErrorKind, ToCss}; +use style_traits::{CssWriter, KeywordsCollectFn, ParseError, SpecifiedValueInfo, StyleParseErrorKind, ToCss}; /// A specified value for the `cursor` property. pub type Cursor = generics::GenericCursor; @@ -69,8 +69,11 @@ impl Parse for CursorImage { // This trait is manually implemented because we don't support the whole // syntax for cursors -impl SpecifiedValueInfo for CursorImage {} - +impl SpecifiedValueInfo for CursorImage { + fn collect_completion_keywords(f: KeywordsCollectFn) { + f(&["url", "image-set"]); + } +} /// Specified value of `-moz-force-broken-image-icon` #[derive( Clone, From 4cee8cf93748df46cb02bb7d4fc2f2130368cbd8 Mon Sep 17 00:00:00 2001 From: Oriol Brufau Date: Tue, 16 May 2023 08:03:48 +0200 Subject: [PATCH 10/65] style: Support image-set in the content property Differential Revision: https://phabricator.services.mozilla.com/D107397 --- components/style/values/computed/counters.rs | 6 ++-- components/style/values/generics/counters.rs | 13 ++++----- components/style/values/specified/counters.rs | 29 +++++++++++++++---- 3 files changed, 32 insertions(+), 16 deletions(-) diff --git a/components/style/values/computed/counters.rs b/components/style/values/computed/counters.rs index 40cfe46efa6..1ae46c772ab 100644 --- a/components/style/values/computed/counters.rs +++ b/components/style/values/computed/counters.rs @@ -4,7 +4,7 @@ //! Computed values for counter properties -use crate::values::computed::url::ComputedImageUrl; +use crate::values::computed::image::Image; use crate::values::generics::counters as generics; use crate::values::generics::counters::CounterIncrement as GenericCounterIncrement; use crate::values::generics::counters::CounterSetOrReset as GenericCounterSetOrReset; @@ -16,7 +16,7 @@ pub type CounterIncrement = GenericCounterIncrement; pub type CounterSetOrReset = GenericCounterSetOrReset; /// A computed value for the `content` property. -pub type Content = generics::GenericContent; +pub type Content = generics::GenericContent; /// A computed content item. -pub type ContentItem = generics::GenericContentItem; +pub type ContentItem = generics::GenericContentItem; diff --git a/components/style/values/generics/counters.rs b/components/style/values/generics/counters.rs index 76ee97ce175..2a8f70c8f44 100644 --- a/components/style/values/generics/counters.rs +++ b/components/style/values/generics/counters.rs @@ -148,18 +148,18 @@ fn is_decimal(counter_type: &CounterStyleType) -> bool { Clone, Debug, Eq, MallocSizeOf, PartialEq, SpecifiedValueInfo, ToComputedValue, ToCss, ToShmem, )] #[repr(u8)] -pub enum GenericContent { +pub enum GenericContent { /// `normal` reserved keyword. Normal, /// `none` reserved keyword. None, /// Content items. - Items(#[css(iterable)] crate::OwnedSlice>), + Items(#[css(iterable)] crate::OwnedSlice>), } pub use self::GenericContent as Content; -impl Content { +impl Content { /// Whether `self` represents list of items. #[inline] pub fn is_items(&self) -> bool { @@ -180,14 +180,13 @@ impl Content { Eq, MallocSizeOf, PartialEq, - SpecifiedValueInfo, ToComputedValue, ToCss, ToResolvedValue, ToShmem, )] #[repr(u8)] -pub enum GenericContentItem { +pub enum GenericContentItem { /// Literal string content. String(crate::OwnedStr), /// `counter(name, style)`. @@ -220,8 +219,8 @@ pub enum GenericContentItem { /// `attr([namespace? `|`]? ident)` #[cfg(any(feature = "gecko", feature = "servo-layout-2020"))] Attr(Attr), - /// `url(url)` - Url(ImageUrl), + /// image-set(url) | url(url) + Image(I), } pub use self::GenericContentItem as ContentItem; diff --git a/components/style/values/specified/counters.rs b/components/style/values/specified/counters.rs index 61014f8d3b4..aa442549fa5 100644 --- a/components/style/values/specified/counters.rs +++ b/components/style/values/specified/counters.rs @@ -11,7 +11,7 @@ use crate::values::generics::counters as generics; use crate::values::generics::counters::CounterPair; #[cfg(feature = "gecko")] use crate::values::generics::CounterStyle; -use crate::values::specified::url::SpecifiedImageUrl; +use crate::values::specified::image::Image; #[cfg(any(feature = "gecko", feature = "servo-layout-2020"))] use crate::values::specified::Attr; use crate::values::specified::Integer; @@ -19,7 +19,7 @@ use crate::values::CustomIdent; use cssparser::{Parser, Token}; #[cfg(any(feature = "gecko", feature = "servo-layout-2013"))] use selectors::parser::SelectorParseErrorKind; -use style_traits::{ParseError, StyleParseErrorKind}; +use style_traits::{KeywordsCollectFn, ParseError, SpecifiedValueInfo, StyleParseErrorKind}; /// A specified value for the `counter-increment` property. pub type CounterIncrement = generics::GenericCounterIncrement; @@ -83,10 +83,10 @@ fn parse_counters<'i, 't>( } /// The specified value for the `content` property. -pub type Content = generics::GenericContent; +pub type Content = generics::GenericContent; /// The specified value for a content item in the `content` property. -pub type ContentItem = generics::GenericContentItem; +pub type ContentItem = generics::GenericContentItem; impl Content { #[cfg(feature = "servo-layout-2013")] @@ -137,8 +137,8 @@ impl Parse for Content { loop { #[cfg(any(feature = "gecko", feature = "servo-layout-2020"))] { - if let Ok(url) = input.try_parse(|i| SpecifiedImageUrl::parse(context, i)) { - content.push(generics::ContentItem::Url(url)); + if let Ok(image) = input.try_parse(|i| Image::parse_only_url(context, i)) { + content.push(generics::ContentItem::Image(image)); continue; } } @@ -214,3 +214,20 @@ impl Parse for Content { Ok(generics::Content::Items(content.into())) } } + +impl SpecifiedValueInfo for generics::GenericContentItem { + fn collect_completion_keywords(f: KeywordsCollectFn) { + f(&[ + "url", + "image-set", + "counter", + "counters", + "attr", + "open-quote", + "close-quote", + "no-open-quote", + "no-close-quote", + "-moz-alt-content", + ]); + } +} From 108c50c6dffd3e15fd1dcff2be2fa33da66c889e Mon Sep 17 00:00:00 2001 From: Oriol Brufau Date: Tue, 16 May 2023 09:40:29 +0200 Subject: [PATCH 11/65] Further changes required by Servo --- components/layout/generated_content.rs | 4 ++-- components/layout_2020/dom_traversal.rs | 4 ++-- components/layout_2020/replaced.rs | 12 ++++++++++++ 3 files changed, 16 insertions(+), 4 deletions(-) diff --git a/components/layout/generated_content.rs b/components/layout/generated_content.rs index d69ddd65991..89f65732761 100644 --- a/components/layout/generated_content.rs +++ b/components/layout/generated_content.rs @@ -272,8 +272,8 @@ impl<'a, 'b> ResolveGeneratedContentFragmentMutator<'a, 'b> { self.traversal.quote -= 1 } }, - GeneratedContentInfo::ContentItem(ContentItem::Url(..)) => { - unreachable!("Servo doesn't parse content: url(..) yet") + GeneratedContentInfo::ContentItem(ContentItem::Image(..)) => { + unreachable!("Servo doesn't parse content: url(..) nor image-set(..) yet") }, } }; diff --git a/components/layout_2020/dom_traversal.rs b/components/layout_2020/dom_traversal.rs index ab20c88c721..7b619dacf45 100644 --- a/components/layout_2020/dom_traversal.rs +++ b/components/layout_2020/dom_traversal.rs @@ -354,9 +354,9 @@ where attr_val.map_or("".to_string(), |s| s.to_string()), )); }, - ContentItem::Url(image_url) => { + ContentItem::Image(image) => { if let Some(replaced_content) = - ReplacedContent::from_image_url(element, context, image_url) + ReplacedContent::from_image(element, context, image) { vec.push(PseudoElementContentItem::Replaced(replaced_content)); } diff --git a/components/layout_2020/replaced.rs b/components/layout_2020/replaced.rs index 50722dab917..642e83f7905 100644 --- a/components/layout_2020/replaced.rs +++ b/components/layout_2020/replaced.rs @@ -21,6 +21,7 @@ use std::fmt; use std::sync::{Arc, Mutex}; use style::properties::ComputedValues; use style::servo::url::ComputedUrl; +use style::values::computed::image::Image as ComputedImage; use style::values::computed::{Length, LengthOrAuto}; use style::values::CSSFloat; use style::Zero; @@ -184,6 +185,17 @@ impl ReplacedContent { None } + pub fn from_image<'dom>( + element: impl NodeExt<'dom>, + context: &LayoutContext, + image: &ComputedImage, + ) -> Option { + match image { + ComputedImage::Url(image_url) => Self::from_image_url(element, context, image_url), + _ => None, // TODO + } + } + fn flow_relative_intrinsic_size(&self, style: &ComputedValues) -> Vec2> { let intrinsic_size = PhysicalSize::new(self.intrinsic.width, self.intrinsic.height); Vec2::from_physical_size(&intrinsic_size, style.writing_mode) From b222bd3162b36aa91863f794709eaa94d31b8ea4 Mon Sep 17 00:00:00 2001 From: Oriol Brufau Date: Tue, 16 May 2023 08:04:42 +0200 Subject: [PATCH 12/65] style: Paper over a crash in non-nightly Differential Revision: https://phabricator.services.mozilla.com/D107400 --- components/style/context.rs | 15 --------------- components/style/properties/properties.mako.rs | 16 +++++++++++++++- 2 files changed, 15 insertions(+), 16 deletions(-) diff --git a/components/style/context.rs b/components/style/context.rs index 08fdb4d1eef..a7d5aa4a67b 100644 --- a/components/style/context.rs +++ b/components/style/context.rs @@ -116,21 +116,6 @@ impl Default for StyleSystemOptions { } } -impl StyleSystemOptions { - #[cfg(feature = "servo")] - /// On Gecko's nightly build? - pub fn is_nightly(&self) -> bool { - false - } - - #[cfg(feature = "gecko")] - /// On Gecko's nightly build? - #[inline] - pub fn is_nightly(&self) -> bool { - structs::GECKO_IS_NIGHTLY - } -} - /// A shared style context. /// /// There's exactly one of these during a given restyle traversal, and it's diff --git a/components/style/properties/properties.mako.rs b/components/style/properties/properties.mako.rs index 95d59df064d..b27831a5785 100644 --- a/components/style/properties/properties.mako.rs +++ b/components/style/properties/properties.mako.rs @@ -1757,7 +1757,21 @@ impl UnparsedValue { shorthand_cache.insert((shorthand, longhand), declaration); } - Cow::Borrowed(&shorthand_cache[&(shorthand, longhand_id)]) + let key = (shorthand, longhand_id); + match shorthand_cache.get(&key) { + Some(decl) => Cow::Borrowed(decl), + None => { + // FIXME: We should always have the key here but it seems + // sometimes we don't, see bug 1696409. + #[cfg(feature = "gecko")] + { + if structs::GECKO_IS_NIGHTLY { + panic!("Expected {:?} to be in the cache but it was not!", key); + } + } + invalid_at_computed_value_time() + } + } } } From 17ae374c655a94c6be394f1b11c6a7095c46506c Mon Sep 17 00:00:00 2001 From: Oriol Brufau Date: Tue, 16 May 2023 08:05:27 +0200 Subject: [PATCH 13/65] style: part 1 - Support parsing ruby-position: alternate Differential Revision: https://phabricator.services.mozilla.com/D107382 --- components/style/properties/data.py | 1 + .../longhands/inherited_text.mako.rs | 8 +- components/style/values/computed/mod.rs | 2 +- components/style/values/computed/text.rs | 2 +- components/style/values/specified/mod.rs | 1 + components/style/values/specified/text.rs | 83 +++++++++++++++++++ 6 files changed, 91 insertions(+), 6 deletions(-) diff --git a/components/style/properties/data.py b/components/style/properties/data.py index 4fe90fb560b..d95eed44dd9 100644 --- a/components/style/properties/data.py +++ b/components/style/properties/data.py @@ -495,6 +495,7 @@ class Longhand(Property): "Percentage", "PositiveIntegerOrNone", "Resize", + "RubyPosition", "SVGOpacity", "SVGPaintOrder", "ScrollSnapAlign", diff --git a/components/style/properties/longhands/inherited_text.mako.rs b/components/style/properties/longhands/inherited_text.mako.rs index 0ce27e0e18f..b8b603e8b2e 100644 --- a/components/style/properties/longhands/inherited_text.mako.rs +++ b/components/style/properties/longhands/inherited_text.mako.rs @@ -329,13 +329,13 @@ ${helpers.single_keyword( spec="https://drafts.csswg.org/css-ruby/#ruby-align-property", )} -${helpers.single_keyword( +${helpers.predefined_type( "ruby-position", - "over under", + "RubyPosition", + "Default::default()", engines="gecko", - animation_value_type="discrete", spec="https://drafts.csswg.org/css-ruby/#ruby-position-property", - gecko_enum_prefix="StyleRubyPosition", + animation_value_type="discrete", )} // CSS Writing Modes Module Level 3 diff --git a/components/style/values/computed/mod.rs b/components/style/values/computed/mod.rs index 0c9496c2fad..5ed404cf5e3 100644 --- a/components/style/values/computed/mod.rs +++ b/components/style/values/computed/mod.rs @@ -87,7 +87,7 @@ pub use self::svg::{SVGLength, SVGOpacity, SVGPaint, SVGPaintKind}; pub use self::svg::{SVGPaintOrder, SVGStrokeDashArray, SVGWidth}; pub use self::text::TextUnderlinePosition; pub use self::text::{InitialLetter, LetterSpacing, LineBreak, LineHeight}; -pub use self::text::{OverflowWrap, TextOverflow, WordBreak, WordSpacing}; +pub use self::text::{OverflowWrap, RubyPosition, TextOverflow, WordBreak, WordSpacing}; pub use self::text::{TextAlign, TextAlignLast, TextEmphasisPosition, TextEmphasisStyle}; pub use self::text::{TextDecorationLength, TextDecorationSkipInk}; pub use self::time::Time; diff --git a/components/style/values/computed/text.rs b/components/style/values/computed/text.rs index b77695e06c0..4f952fa21cc 100644 --- a/components/style/values/computed/text.rs +++ b/components/style/values/computed/text.rs @@ -19,7 +19,7 @@ use std::fmt::{self, Write}; use style_traits::{CssWriter, ToCss}; pub use crate::values::specified::text::{TextAlignLast, TextUnderlinePosition}; -pub use crate::values::specified::{LineBreak, OverflowWrap, WordBreak}; +pub use crate::values::specified::{LineBreak, OverflowWrap, RubyPosition, WordBreak}; pub use crate::values::specified::{TextDecorationLine, TextEmphasisPosition}; pub use crate::values::specified::{TextDecorationSkipInk, TextTransform}; diff --git a/components/style/values/specified/mod.rs b/components/style/values/specified/mod.rs index d91f9b8195f..946bf3c2cc9 100644 --- a/components/style/values/specified/mod.rs +++ b/components/style/values/specified/mod.rs @@ -86,6 +86,7 @@ pub use self::svg::{SVGPaintOrder, SVGStrokeDashArray, SVGWidth}; pub use self::svg_path::SVGPathData; pub use self::text::TextAlignLast; pub use self::text::TextUnderlinePosition; +pub use self::text::RubyPosition; pub use self::text::{InitialLetter, LetterSpacing, LineBreak, LineHeight, TextAlign}; pub use self::text::{OverflowWrap, TextEmphasisPosition, TextEmphasisStyle, WordBreak}; pub use self::text::{TextAlignKeyword, TextDecorationLine, TextOverflow, WordSpacing}; diff --git a/components/style/values/specified/text.rs b/components/style/values/specified/text.rs index 7c5c712eb3e..3fe3b92c354 100644 --- a/components/style/values/specified/text.rs +++ b/components/style/values/specified/text.rs @@ -22,6 +22,7 @@ use selectors::parser::SelectorParseErrorKind; use std::fmt::{self, Write}; use style_traits::values::SequenceWriter; use style_traits::{CssWriter, ParseError, StyleParseErrorKind, ToCss}; +use style_traits::{KeywordsCollectFn, SpecifiedValueInfo}; use unicode_segmentation::UnicodeSegmentation; /// A specified type for the `initial-letter` property. @@ -1193,3 +1194,85 @@ impl ToCss for TextUnderlinePosition { Ok(()) } } + +/// Values for `ruby-position` property +#[repr(u8)] +#[derive( + Clone, + Copy, + Debug, + Eq, + MallocSizeOf, + PartialEq, + ToComputedValue, + ToResolvedValue, + ToShmem, +)] +#[allow(missing_docs)] +pub enum RubyPosition { + AlternateOver, + AlternateUnder, + Over, + Under, +} + +impl Default for RubyPosition { + fn default() -> Self { + if static_prefs::pref!("layout.css.ruby.position-alternate.enabled") { + RubyPosition::AlternateOver + } else { + RubyPosition::Over + } + } +} + +impl Parse for RubyPosition { + fn parse<'i, 't>( + _context: &ParserContext, + input: &mut Parser<'i, 't>, + ) -> Result> { + // Parse alternate before + let alternate_enabled = static_prefs::pref!("layout.css.ruby.position-alternate.enabled"); + let alternate = alternate_enabled && + input.try_parse(|i| i.expect_ident_matching("alternate")).is_ok(); + if alternate && input.is_exhausted() { + return Ok(RubyPosition::AlternateOver); + } + // Parse over / under + let over = try_match_ident_ignore_ascii_case! { input, + "over" => true, + "under" => false, + }; + // Parse alternate after + let alternate = alternate || + (alternate_enabled && + input.try_parse(|i| i.expect_ident_matching("alternate")).is_ok()); + + Ok(match (over, alternate) { + (true, true) => RubyPosition::AlternateOver, + (false, true) => RubyPosition::AlternateUnder, + (true, false) => RubyPosition::Over, + (false, false) => RubyPosition::Under, + }) + } +} + +impl ToCss for RubyPosition { + fn to_css(&self, dest: &mut CssWriter) -> fmt::Result + where + W: Write, + { + dest.write_str(match self { + RubyPosition::AlternateOver => "alternate", + RubyPosition::AlternateUnder => "alternate under", + RubyPosition::Over => "over", + RubyPosition::Under => "under", + }) + } +} + +impl SpecifiedValueInfo for RubyPosition { + fn collect_completion_keywords(f: KeywordsCollectFn) { + f(&["alternate", "over", "under"]) + } +} From b38517757fb663c0ab7f430d11b388999c3b6ade Mon Sep 17 00:00:00 2001 From: Oriol Brufau Date: Tue, 16 May 2023 08:46:45 +0200 Subject: [PATCH 14/65] style: Remove layout.css.ruby.position-alternate.enabled pref Differential Revision: https://phabricator.services.mozilla.com/D114044 --- .../properties/longhands/inherited_text.mako.rs | 2 +- components/style/values/specified/text.rs | 17 ++--------------- 2 files changed, 3 insertions(+), 16 deletions(-) diff --git a/components/style/properties/longhands/inherited_text.mako.rs b/components/style/properties/longhands/inherited_text.mako.rs index b8b603e8b2e..6e6bd79c0e6 100644 --- a/components/style/properties/longhands/inherited_text.mako.rs +++ b/components/style/properties/longhands/inherited_text.mako.rs @@ -332,7 +332,7 @@ ${helpers.single_keyword( ${helpers.predefined_type( "ruby-position", "RubyPosition", - "Default::default()", + "computed::RubyPosition::AlternateOver", engines="gecko", spec="https://drafts.csswg.org/css-ruby/#ruby-position-property", animation_value_type="discrete", diff --git a/components/style/values/specified/text.rs b/components/style/values/specified/text.rs index 3fe3b92c354..7ee4dbedda3 100644 --- a/components/style/values/specified/text.rs +++ b/components/style/values/specified/text.rs @@ -1216,25 +1216,13 @@ pub enum RubyPosition { Under, } -impl Default for RubyPosition { - fn default() -> Self { - if static_prefs::pref!("layout.css.ruby.position-alternate.enabled") { - RubyPosition::AlternateOver - } else { - RubyPosition::Over - } - } -} - impl Parse for RubyPosition { fn parse<'i, 't>( _context: &ParserContext, input: &mut Parser<'i, 't>, ) -> Result> { // Parse alternate before - let alternate_enabled = static_prefs::pref!("layout.css.ruby.position-alternate.enabled"); - let alternate = alternate_enabled && - input.try_parse(|i| i.expect_ident_matching("alternate")).is_ok(); + let alternate = input.try_parse(|i| i.expect_ident_matching("alternate")).is_ok(); if alternate && input.is_exhausted() { return Ok(RubyPosition::AlternateOver); } @@ -1245,8 +1233,7 @@ impl Parse for RubyPosition { }; // Parse alternate after let alternate = alternate || - (alternate_enabled && - input.try_parse(|i| i.expect_ident_matching("alternate")).is_ok()); + input.try_parse(|i| i.expect_ident_matching("alternate")).is_ok(); Ok(match (over, alternate) { (true, true) => RubyPosition::AlternateOver, From 11153c63fa9d908355573722614a5bb8f23dac9a Mon Sep 17 00:00:00 2001 From: Oriol Brufau Date: Tue, 16 May 2023 09:47:54 +0200 Subject: [PATCH 15/65] style: Make the cascade data cache generic We're going to use it both for UA sheets and for author styles in Shadow DOM. Differential Revision: https://phabricator.services.mozilla.com/D107265 --- components/style/stylesheet_set.rs | 48 +++++--- components/style/stylist.rs | 188 ++++++++++++++++++++--------- 2 files changed, 158 insertions(+), 78 deletions(-) diff --git a/components/style/stylesheet_set.rs b/components/style/stylesheet_set.rs index 9e858a4566c..d392d1795ec 100644 --- a/components/style/stylesheet_set.rs +++ b/components/style/stylesheet_set.rs @@ -184,7 +184,9 @@ pub struct SheetCollectionFlusher<'a, S> where S: StylesheetInDocument + PartialEq + 'static, { - iter: slice::IterMut<'a, StylesheetSetEntry>, + // TODO: This can be made an iterator again once + // https://github.com/rust-lang/rust/pull/82771 lands on stable. + entries: &'a mut [StylesheetSetEntry], validity: DataValidity, dirty: bool, } @@ -204,32 +206,42 @@ where pub fn data_validity(&self) -> DataValidity { self.validity } + + /// Returns an iterator over the remaining list of sheets to consume. + pub fn sheets<'b>(&'b self) -> impl Iterator { + self.entries.iter().map(|entry| &entry.sheet) + } } -impl<'a, S> Iterator for SheetCollectionFlusher<'a, S> +impl<'a, S> SheetCollectionFlusher<'a, S> where S: StylesheetInDocument + PartialEq + 'static, { - type Item = (&'a S, SheetRebuildKind); - - fn next(&mut self) -> Option { - loop { - let potential_sheet = self.iter.next()?; - + /// Iterates over all sheets and values that we have to invalidate. + /// + /// TODO(emilio): This would be nicer as an iterator but we can't do that + /// until https://github.com/rust-lang/rust/pull/82771 stabilizes. + /// + /// Since we don't have a good use-case for partial iteration, this does the + /// trick for now. + pub fn each(self, mut callback: impl FnMut(&S, SheetRebuildKind) -> bool) { + for potential_sheet in self.entries.iter_mut() { let committed = mem::replace(&mut potential_sheet.committed, true); - if !committed { + let rebuild_kind = if !committed { // If the sheet was uncommitted, we need to do a full rebuild // anyway. - return Some((&potential_sheet.sheet, SheetRebuildKind::Full)); - } - - let rebuild_kind = match self.validity { - DataValidity::Valid => continue, - DataValidity::CascadeInvalid => SheetRebuildKind::CascadeOnly, - DataValidity::FullyInvalid => SheetRebuildKind::Full, + SheetRebuildKind::Full + } else { + match self.validity { + DataValidity::Valid => continue, + DataValidity::CascadeInvalid => SheetRebuildKind::CascadeOnly, + DataValidity::FullyInvalid => SheetRebuildKind::Full, + } }; - return Some((&potential_sheet.sheet, rebuild_kind)); + if !callback(&potential_sheet.sheet, rebuild_kind) { + return; + } } } } @@ -357,7 +369,7 @@ where let validity = mem::replace(&mut self.data_validity, DataValidity::Valid); SheetCollectionFlusher { - iter: self.entries.iter_mut(), + entries: &mut self.entries, dirty, validity, } diff --git a/components/style/stylist.rs b/components/style/stylist.rs index 5cb40b968eb..e2392cf5742 100644 --- a/components/style/stylist.rs +++ b/components/style/stylist.rs @@ -60,17 +60,33 @@ pub type StylistSheet = crate::stylesheets::DocumentStyleSheet; #[cfg(feature = "gecko")] pub type StylistSheet = crate::gecko::data::GeckoStyleSheet; -lazy_static! { - /// A cache of computed user-agent data, to be shared across documents. - static ref UA_CASCADE_DATA_CACHE: Mutex = - Mutex::new(UserAgentCascadeDataCache::new()); +trait CascadeDataCacheEntry : Sized { + /// Returns a reference to the cascade data. + fn cascade_data(&self) -> &CascadeData; + /// Rebuilds the cascade data for the new stylesheet collection. The + /// collection is guaranteed to be dirty. + fn rebuild( + device: &Device, + quirks_mode: QuirksMode, + collection: SheetCollectionFlusher, + guard: &SharedRwLockReadGuard, + old_entry: &Self, + ) -> Result, FailedAllocationError> + where + S: StylesheetInDocument + ToMediaListKey + PartialEq + 'static; + /// Measures heap memory usage. + #[cfg(feature = "gecko")] + fn add_size_of(&self, ops: &mut MallocSizeOfOps, sizes: &mut ServoStyleSetSizes); } -struct UserAgentCascadeDataCache { - entries: Vec>, +struct CascadeDataCache { + entries: Vec>, } -impl UserAgentCascadeDataCache { +impl CascadeDataCache +where + Entry: CascadeDataCacheEntry, +{ fn new() -> Self { Self { entries: vec![] } } @@ -79,53 +95,50 @@ impl UserAgentCascadeDataCache { self.entries.len() } - // FIXME(emilio): This may need to be keyed on quirks-mode too, though there - // aren't class / id selectors on those sheets, usually, so it's probably - // ok... - fn lookup<'a, I, S>( + // FIXME(emilio): This may need to be keyed on quirks-mode too, though for + // UA sheets there aren't class / id selectors on those sheets, usually, so + // it's probably ok... For the other cache the quirks mode shouldn't differ + // so also should be fine. + fn lookup<'a, S>( &'a mut self, - sheets: I, device: &Device, quirks_mode: QuirksMode, + collection: SheetCollectionFlusher, guard: &SharedRwLockReadGuard, - ) -> Result, FailedAllocationError> + old_entry: &Entry, + ) -> Result>, FailedAllocationError> where - I: Iterator + Clone, S: StylesheetInDocument + ToMediaListKey + PartialEq + 'static, { + debug!("StyleSheetCache::lookup({})", self.len()); + + if !collection.dirty() { + return Ok(None); + } + let mut key = EffectiveMediaQueryResults::new(); - debug!("UserAgentCascadeDataCache::lookup({:?})", device); - for sheet in sheets.clone() { + for sheet in collection.sheets() { CascadeData::collect_applicable_media_query_results_into(device, sheet, guard, &mut key) } for entry in &self.entries { - if entry.cascade_data.effective_media_query_results == key { - return Ok(entry.clone()); + if entry.cascade_data().effective_media_query_results == key { + return Ok(Some(entry.clone())); } } - let mut new_data = UserAgentCascadeData { - cascade_data: CascadeData::new(), - precomputed_pseudo_element_decls: PrecomputedPseudoElementDeclarations::default(), - }; - debug!("> Picking the slow path"); - for sheet in sheets { - new_data.cascade_data.add_stylesheet( - device, - quirks_mode, - sheet, - guard, - SheetRebuildKind::Full, - Some(&mut new_data.precomputed_pseudo_element_decls), - )?; - } + let new_entry = Entry::rebuild( + device, + quirks_mode, + collection, + guard, + old_entry, + )?; - let new_data = Arc::new(new_data); - self.entries.push(new_data.clone()); - Ok(new_data) + self.entries.push(new_entry.clone()); + Ok(Some(new_entry)) } /// Returns all the cascade datas that are not being used (that is, that are @@ -135,7 +148,7 @@ impl UserAgentCascadeDataCache { /// keep alive some other documents (like the SVG documents kept alive by /// URL references), and thus we don't want to drop them while locking the /// cache to not deadlock. - fn take_unused(&mut self) -> SmallVec<[Arc; 3]> { + fn take_unused(&mut self) -> SmallVec<[Arc; 3]> { let mut unused = SmallVec::new(); for i in (0..self.entries.len()).rev() { // is_unique() returns false for static references, but we never @@ -148,7 +161,7 @@ impl UserAgentCascadeDataCache { unused } - fn take_all(&mut self) -> Vec> { + fn take_all(&mut self) -> Vec> { mem::replace(&mut self.entries, Vec::new()) } @@ -173,6 +186,58 @@ pub fn add_size_of_ua_cache(ops: &mut MallocSizeOfOps, sizes: &mut ServoStyleSet .add_size_of(ops, sizes); } +lazy_static! { + /// A cache of computed user-agent data, to be shared across documents. + static ref UA_CASCADE_DATA_CACHE: Mutex = + Mutex::new(UserAgentCascadeDataCache::new()); +} + +impl CascadeDataCacheEntry for UserAgentCascadeData { + fn cascade_data(&self) -> &CascadeData { + &self.cascade_data + } + + fn rebuild( + device: &Device, + quirks_mode: QuirksMode, + collection: SheetCollectionFlusher, + guard: &SharedRwLockReadGuard, + _old: &Self, + ) -> Result, FailedAllocationError> + where + S: StylesheetInDocument + ToMediaListKey + PartialEq + 'static + { + // TODO: Maybe we should support incremental rebuilds, though they seem + // uncommon and rebuild() doesn't deal with + // precomputed_pseudo_element_decls for now so... + let mut new_data = Self { + cascade_data: CascadeData::new(), + precomputed_pseudo_element_decls: PrecomputedPseudoElementDeclarations::default(), + }; + + for sheet in collection.sheets() { + new_data.cascade_data.add_stylesheet( + device, + quirks_mode, + sheet, + guard, + SheetRebuildKind::Full, + Some(&mut new_data.precomputed_pseudo_element_decls), + )?; + } + + Ok(Arc::new(new_data)) + } + + #[cfg(feature = "gecko")] + fn add_size_of(&self, ops: &mut MallocSizeOfOps, sizes: &mut ServoStyleSetSizes) { + self.cascade_data.add_size_of(ops, sizes); + sizes.mPrecomputedPseudos += self.precomputed_pseudo_element_decls.size_of(ops); + } +} + +type UserAgentCascadeDataCache = CascadeDataCache; + type PrecomputedPseudoElementDeclarations = PerPseudoElementMap>; #[derive(Default)] @@ -188,14 +253,6 @@ struct UserAgentCascadeData { precomputed_pseudo_element_decls: PrecomputedPseudoElementDeclarations, } -impl UserAgentCascadeData { - #[cfg(feature = "gecko")] - fn add_size_of(&self, ops: &mut MallocSizeOfOps, sizes: &mut ServoStyleSetSizes) { - self.cascade_data.add_size_of(ops, sizes); - sizes.mPrecomputedPseudos += self.precomputed_pseudo_element_decls.size_of(ops); - } -} - /// All the computed information for all the stylesheets that apply to the /// document. #[derive(Default)] @@ -266,15 +323,23 @@ impl DocumentCascadeData { { // First do UA sheets. { - if flusher.flush_origin(Origin::UserAgent).dirty() { - let origin_sheets = flusher.origin_sheets(Origin::UserAgent); - let _unused_cascade_datas = { - let mut ua_cache = UA_CASCADE_DATA_CACHE.lock().unwrap(); - self.user_agent = - ua_cache.lookup(origin_sheets, device, quirks_mode, guards.ua_or_user)?; - debug!("User agent data cache size {:?}", ua_cache.len()); - ua_cache.take_unused() - }; + let origin_flusher = flusher.flush_origin(Origin::UserAgent); + if origin_flusher.dirty() { + let mut ua_cache = UA_CASCADE_DATA_CACHE.lock().unwrap(); + let new_data = ua_cache.lookup( + device, + quirks_mode, + origin_flusher, + guards.ua_or_user, + &self.user_agent, + )?; + if let Some(new_data) = new_data { + self.user_agent = new_data; + } + let _unused_entries = ua_cache.take_unused(); + // See the comments in take_unused() as for why the following + // line. + std::mem::drop(ua_cache); } } @@ -1836,18 +1901,21 @@ impl CascadeData { DataValidity::FullyInvalid => self.clear(), } - for (stylesheet, rebuild_kind) in collection { - self.add_stylesheet( + let mut result = Ok(()); + + collection.each(|stylesheet, rebuild_kind| { + result = self.add_stylesheet( device, quirks_mode, stylesheet, guard, rebuild_kind, /* precomputed_pseudo_element_decls = */ None, - )?; - } + ); + result.is_ok() + }); - Ok(()) + result } /// Returns the invalidation map. From 060d74ba3bcccf8ace74ac8d00c7589783a2a45d Mon Sep 17 00:00:00 2001 From: Oriol Brufau Date: Tue, 16 May 2023 09:51:46 +0200 Subject: [PATCH 16/65] style: Share CascadeData instances across ShadowRoots This should be both a memory and speed win for pages using a lot of Shadow DOM. In order to make the cache properly work we need to start keying media query results on the actual StyleSheetContents, as that's what we share on Gecko, but that should all be fine. Differential Revision: https://phabricator.services.mozilla.com/D107266 --- components/style/author_styles.rs | 26 ++-- components/style/gecko/data.rs | 31 ++--- .../invalidation/element/invalidation_map.rs | 2 +- .../style/invalidation/media_queries.rs | 6 +- components/style/selector_map.rs | 4 +- components/style/selector_parser.rs | 2 +- components/style/stylesheet_set.rs | 22 +--- components/style/stylesheets/import_rule.rs | 73 +++-------- .../style/stylesheets/rules_iterator.rs | 8 +- components/style/stylesheets/stylesheet.rs | 57 +++----- components/style/stylist.rs | 122 ++++++++++++++---- 11 files changed, 175 insertions(+), 178 deletions(-) diff --git a/components/style/author_styles.rs b/components/style/author_styles.rs index 58d9bda423a..dfd33711ed2 100644 --- a/components/style/author_styles.rs +++ b/components/style/author_styles.rs @@ -5,16 +5,16 @@ //! A set of author stylesheets and their computed representation, such as the //! ones used for ShadowRoot. -use crate::context::QuirksMode; use crate::dom::TElement; #[cfg(feature = "gecko")] use crate::gecko_bindings::sugar::ownership::{HasBoxFFI, HasFFI, HasSimpleFFI}; use crate::invalidation::media_queries::ToMediaListKey; -use crate::media_queries::Device; use crate::shared_lock::SharedRwLockReadGuard; +use crate::stylist::Stylist; use crate::stylesheet_set::AuthorStylesheetSet; use crate::stylesheets::StylesheetInDocument; use crate::stylist::CascadeData; +use servo_arc::Arc; /// A set of author stylesheets and their computed representation, such as the /// ones used for ShadowRoot. @@ -27,7 +27,14 @@ where /// and all that stuff. pub stylesheets: AuthorStylesheetSet, /// The actual cascade data computed from the stylesheets. - pub data: CascadeData, + #[ignore_malloc_size_of = "Measured as part of the stylist"] + pub data: Arc, +} + +lazy_static! { + static ref EMPTY_CASCADE_DATA: Arc = { + Arc::new_leaked(CascadeData::new()) + }; } impl AuthorStyles @@ -39,7 +46,7 @@ where pub fn new() -> Self { Self { stylesheets: AuthorStylesheetSet::new(), - data: CascadeData::new(), + data: EMPTY_CASCADE_DATA.clone(), } } @@ -50,8 +57,7 @@ where #[inline] pub fn flush( &mut self, - device: &Device, - quirks_mode: QuirksMode, + stylist: &mut Stylist, guard: &SharedRwLockReadGuard, ) where E: TElement, @@ -61,10 +67,10 @@ where .stylesheets .flush::(/* host = */ None, /* snapshot_map = */ None); - // Ignore OOM. - let _ = self - .data - .rebuild(device, quirks_mode, flusher.sheets, guard); + let result = stylist.rebuild_author_data(&self.data, flusher.sheets, guard); + if let Ok(Some(new_data)) = result { + self.data = new_data; + } } } diff --git a/components/style/gecko/data.rs b/components/style/gecko/data.rs index 0ce43bca46d..0689aa6c0c4 100644 --- a/components/style/gecko/data.rs +++ b/components/style/gecko/data.rs @@ -4,7 +4,6 @@ //! Data needed to style a Gecko document. -use crate::context::QuirksMode; use crate::dom::TElement; use crate::gecko_bindings::bindings; use crate::gecko_bindings::structs::{self, RawServoStyleSet, ServoStyleSetSizes}; @@ -15,7 +14,7 @@ use crate::media_queries::{Device, MediaList}; use crate::properties::ComputedValues; use crate::selector_parser::SnapshotMap; use crate::shared_lock::{Locked, SharedRwLockReadGuard, StylesheetGuards}; -use crate::stylesheets::{CssRule, Origin, StylesheetContents, StylesheetInDocument}; +use crate::stylesheets::{StylesheetContents, StylesheetInDocument}; use crate::stylist::Stylist; use atomic_refcell::{AtomicRef, AtomicRefCell, AtomicRefMut}; use malloc_size_of::MallocSizeOfOps; @@ -69,16 +68,6 @@ impl GeckoStyleSheet { fn inner(&self) -> &StyleSheetInfo { unsafe { &*(self.raw().mInner as *const StyleSheetInfo) } } - - /// Gets the StylesheetContents for this stylesheet. - pub fn contents(&self) -> &StylesheetContents { - debug_assert!(!self.inner().mContents.mRawPtr.is_null()); - unsafe { - let contents = - (&**StylesheetContents::as_arc(&&*self.inner().mContents.mRawPtr)) as *const _; - &*contents - } - } } impl Drop for GeckoStyleSheet { @@ -95,14 +84,6 @@ impl Clone for GeckoStyleSheet { } impl StylesheetInDocument for GeckoStyleSheet { - fn origin(&self, _guard: &SharedRwLockReadGuard) -> Origin { - self.contents().origin - } - - fn quirks_mode(&self, _guard: &SharedRwLockReadGuard) -> QuirksMode { - self.contents().quirks_mode - } - fn media<'a>(&'a self, guard: &'a SharedRwLockReadGuard) -> Option<&'a MediaList> { use crate::gecko_bindings::structs::mozilla::dom::MediaList as DomMediaList; use std::mem; @@ -120,13 +101,19 @@ impl StylesheetInDocument for GeckoStyleSheet { // All the stylesheets Servo knows about are enabled, because that state is // handled externally by Gecko. + #[inline] fn enabled(&self) -> bool { true } #[inline] - fn rules<'a, 'b: 'a>(&'a self, guard: &'b SharedRwLockReadGuard) -> &'a [CssRule] { - self.contents().rules(guard) + fn contents(&self) -> &StylesheetContents { + debug_assert!(!self.inner().mContents.mRawPtr.is_null()); + unsafe { + let contents = + (&**StylesheetContents::as_arc(&&*self.inner().mContents.mRawPtr)) as *const _; + &*contents + } } } diff --git a/components/style/invalidation/element/invalidation_map.rs b/components/style/invalidation/element/invalidation_map.rs index 29e0962a897..a7c2b04df13 100644 --- a/components/style/invalidation/element/invalidation_map.rs +++ b/components/style/invalidation/element/invalidation_map.rs @@ -185,7 +185,7 @@ pub struct DocumentStateDependency { /// In particular, we want to lookup as few things as possible to get the fewer /// selectors the better, so this looks up by id, class, or looks at the list of /// state/other attribute affecting selectors. -#[derive(Debug, MallocSizeOf)] +#[derive(Clone, Debug, MallocSizeOf)] pub struct InvalidationMap { /// A map from a given class name to all the selectors with that class /// selector. diff --git a/components/style/invalidation/media_queries.rs b/components/style/invalidation/media_queries.rs index 75149a02891..6928b29d3d9 100644 --- a/components/style/invalidation/media_queries.rs +++ b/components/style/invalidation/media_queries.rs @@ -8,7 +8,7 @@ use crate::context::QuirksMode; use crate::media_queries::Device; use crate::shared_lock::SharedRwLockReadGuard; use crate::stylesheets::{DocumentRule, ImportRule, MediaRule}; -use crate::stylesheets::{NestedRuleIterationCondition, Stylesheet, SupportsRule}; +use crate::stylesheets::{NestedRuleIterationCondition, StylesheetContents, SupportsRule}; use fxhash::FxHashSet; /// A key for a given media query result. @@ -43,13 +43,13 @@ pub trait ToMediaListKey: Sized { } } -impl ToMediaListKey for Stylesheet {} +impl ToMediaListKey for StylesheetContents {} impl ToMediaListKey for ImportRule {} impl ToMediaListKey for MediaRule {} /// A struct that holds the result of a media query evaluation pass for the /// media queries that evaluated successfully. -#[derive(Debug, MallocSizeOf, PartialEq)] +#[derive(Clone, Debug, MallocSizeOf, PartialEq)] pub struct EffectiveMediaQueryResults { /// The set of media lists that matched last time. set: FxHashSet, diff --git a/components/style/selector_map.rs b/components/style/selector_map.rs index 4ea8cc1a018..40806ed47af 100644 --- a/components/style/selector_map.rs +++ b/components/style/selector_map.rs @@ -94,7 +94,7 @@ pub trait SelectorMapEntry: Sized + Clone { /// * https://bugzilla.mozilla.org/show_bug.cgi?id=681755 /// /// TODO: Tune the initial capacity of the HashMap -#[derive(Debug, MallocSizeOf)] +#[derive(Clone, Debug, MallocSizeOf)] pub struct SelectorMap { /// Rules that have `:root` selectors. pub root: SmallVec<[T; 1]>, @@ -615,7 +615,7 @@ fn find_bucket<'a>( } /// Wrapper for PrecomputedHashMap that does ASCII-case-insensitive lookup in quirks mode. -#[derive(Debug, MallocSizeOf)] +#[derive(Clone, Debug, MallocSizeOf)] pub struct MaybeCaseInsensitiveHashMap( PrecomputedHashMap, ); diff --git a/components/style/selector_parser.rs b/components/style/selector_parser.rs index e3841c15482..67d4d4f6981 100644 --- a/components/style/selector_parser.rs +++ b/components/style/selector_parser.rs @@ -108,7 +108,7 @@ pub enum PseudoElementCascadeType { } /// A per-pseudo map, from a given pseudo to a `T`. -#[derive(MallocSizeOf)] +#[derive(Clone, MallocSizeOf)] pub struct PerPseudoElementMap { entries: [Option; PSEUDO_COUNT], } diff --git a/components/style/stylesheet_set.rs b/components/style/stylesheet_set.rs index d392d1795ec..e2937e06e75 100644 --- a/components/style/stylesheet_set.rs +++ b/components/style/stylesheet_set.rs @@ -420,7 +420,7 @@ macro_rules! sheet_set_methods { ) { debug!(concat!($set_name, "::append_stylesheet")); self.collect_invalidations_for(device, &sheet, guard); - let collection = self.collection_for(&sheet, guard); + let collection = self.collection_for(&sheet); collection.append(sheet); } @@ -435,7 +435,7 @@ macro_rules! sheet_set_methods { debug!(concat!($set_name, "::insert_stylesheet_before")); self.collect_invalidations_for(device, &sheet, guard); - let collection = self.collection_for(&sheet, guard); + let collection = self.collection_for(&sheet); collection.insert_before(sheet, &before_sheet); } @@ -449,7 +449,7 @@ macro_rules! sheet_set_methods { debug!(concat!($set_name, "::remove_stylesheet")); self.collect_invalidations_for(device, &sheet, guard); - let collection = self.collection_for(&sheet, guard); + let collection = self.collection_for(&sheet); collection.remove(&sheet) } @@ -499,7 +499,7 @@ macro_rules! sheet_set_methods { RuleChangeKind::StyleRuleDeclarations => DataValidity::FullyInvalid, }; - let collection = self.collection_for(&sheet, guard); + let collection = self.collection_for(&sheet); collection.set_data_validity_at_least(validity); } }; @@ -517,12 +517,8 @@ where } } - fn collection_for( - &mut self, - sheet: &S, - guard: &SharedRwLockReadGuard, - ) -> &mut SheetCollection { - let origin = sheet.origin(guard); + fn collection_for(&mut self, sheet: &S) -> &mut SheetCollection { + let origin = sheet.contents().origin; self.collections.borrow_mut_for_origin(&origin) } @@ -670,11 +666,7 @@ where self.collection.len() } - fn collection_for( - &mut self, - _sheet: &S, - _guard: &SharedRwLockReadGuard, - ) -> &mut SheetCollection { + fn collection_for(&mut self, _sheet: &S) -> &mut SheetCollection { &mut self.collection } diff --git a/components/style/stylesheets/import_rule.rs b/components/style/stylesheets/import_rule.rs index 5efae0a3ee5..201513760b3 100644 --- a/components/style/stylesheets/import_rule.rs +++ b/components/style/stylesheets/import_rule.rs @@ -61,6 +61,19 @@ impl ImportSheet { ImportSheet::Pending(_) => None, } } + + /// Returns the media list for this import rule. + pub fn media<'a>(&'a self, guard: &'a SharedRwLockReadGuard) -> Option<&'a MediaList> { + self.as_sheet().and_then(|s| s.media(guard)) + } + + /// Returns the rule list for this import rule. + pub fn rules<'a>(&'a self, guard: &'a SharedRwLockReadGuard) -> &'a [CssRule] { + match self.as_sheet() { + Some(s) => s.rules(guard), + None => &[], + } + } } #[cfg(feature = "gecko")] @@ -85,69 +98,21 @@ impl DeepCloneWithLock for ImportSheet { } } -#[cfg(feature = "gecko")] -impl StylesheetInDocument for ImportSheet { - fn origin(&self, _guard: &SharedRwLockReadGuard) -> Origin { - match *self { - ImportSheet::Sheet(ref s) => s.contents().origin, - ImportSheet::Pending(ref p) => p.origin, - } - } - - fn quirks_mode(&self, _guard: &SharedRwLockReadGuard) -> QuirksMode { - match *self { - ImportSheet::Sheet(ref s) => s.contents().quirks_mode, - ImportSheet::Pending(ref p) => p.quirks_mode, - } - } - - fn enabled(&self) -> bool { - match *self { - ImportSheet::Sheet(ref s) => s.enabled(), - ImportSheet::Pending(_) => true, - } - } - - fn media<'a>(&'a self, guard: &'a SharedRwLockReadGuard) -> Option<&'a MediaList> { - match *self { - ImportSheet::Sheet(ref s) => s.media(guard), - ImportSheet::Pending(_) => None, - } - } - - fn rules<'a, 'b: 'a>(&'a self, guard: &'b SharedRwLockReadGuard) -> &'a [CssRule] { - match *self { - ImportSheet::Sheet(ref s) => s.contents().rules(guard), - ImportSheet::Pending(_) => &[], - } - } -} - /// A sheet that is held from an import rule. #[cfg(feature = "servo")] #[derive(Debug)] pub struct ImportSheet(pub ::servo_arc::Arc); #[cfg(feature = "servo")] -impl StylesheetInDocument for ImportSheet { - fn origin(&self, guard: &SharedRwLockReadGuard) -> Origin { - self.0.origin(guard) - } - - fn quirks_mode(&self, guard: &SharedRwLockReadGuard) -> QuirksMode { - self.0.quirks_mode(guard) - } - - fn enabled(&self) -> bool { - self.0.enabled() - } - - fn media<'a>(&'a self, guard: &'a SharedRwLockReadGuard) -> Option<&'a MediaList> { +impl ImportSheet { + /// Returns the media list for this import rule. + pub fn media<'a>(&'a self, guard: &'a SharedRwLockReadGuard) -> Option<&'a MediaList> { self.0.media(guard) } - fn rules<'a, 'b: 'a>(&'a self, guard: &'b SharedRwLockReadGuard) -> &'a [CssRule] { - self.0.rules(guard) + /// Returns the rules for this import rule. + pub fn rules<'a>(&'a self, guard: &'a SharedRwLockReadGuard) -> &'a [CssRule] { + self.0.rules() } } diff --git a/components/style/stylesheets/rules_iterator.rs b/components/style/stylesheets/rules_iterator.rs index cc52a9550be..a7010ff066e 100644 --- a/components/style/stylesheets/rules_iterator.rs +++ b/components/style/stylesheets/rules_iterator.rs @@ -7,7 +7,6 @@ use crate::context::QuirksMode; use crate::media_queries::Device; use crate::shared_lock::SharedRwLockReadGuard; -use crate::stylesheets::StylesheetInDocument; use crate::stylesheets::{CssRule, DocumentRule, ImportRule, MediaRule, SupportsRule}; use smallvec::SmallVec; use std::slice; @@ -227,10 +226,13 @@ impl NestedRuleIterationCondition for EffectiveRules { fn process_import( guard: &SharedRwLockReadGuard, device: &Device, - _quirks_mode: QuirksMode, + quirks_mode: QuirksMode, rule: &ImportRule, ) -> bool { - rule.stylesheet.is_effective_for_device(device, guard) + match rule.stylesheet.media(guard) { + Some(m) => m.evaluate(device, quirks_mode), + None => true, + } } fn process_media( diff --git a/components/style/stylesheets/stylesheet.rs b/components/style/stylesheets/stylesheet.rs index 41c80c3cc66..2f7706a6979 100644 --- a/components/style/stylesheets/stylesheet.rs +++ b/components/style/stylesheets/stylesheet.rs @@ -4,7 +4,6 @@ use crate::context::QuirksMode; use crate::error_reporting::{ContextualParseError, ParseErrorReporter}; -use crate::invalidation::media_queries::{MediaListKey, ToMediaListKey}; use crate::media_queries::{Device, MediaList}; use crate::parser::ParserContext; use crate::shared_lock::{DeepCloneParams, DeepCloneWithLock, Locked}; @@ -102,10 +101,10 @@ impl StylesheetContents { Self { rules: CssRules::new(rules, &shared_lock), - origin: origin, + origin, url_data: RwLock::new(url_data), - namespaces: namespaces, - quirks_mode: quirks_mode, + namespaces, + quirks_mode, source_map_url: RwLock::new(source_map_url), source_url: RwLock::new(source_url), } @@ -218,12 +217,6 @@ macro_rules! rule_filter { /// A trait to represent a given stylesheet in a document. pub trait StylesheetInDocument: ::std::fmt::Debug { - /// Get the stylesheet origin. - fn origin(&self, guard: &SharedRwLockReadGuard) -> Origin; - - /// Get the stylesheet quirks mode. - fn quirks_mode(&self, guard: &SharedRwLockReadGuard) -> QuirksMode; - /// Get whether this stylesheet is enabled. fn enabled(&self) -> bool; @@ -231,7 +224,12 @@ pub trait StylesheetInDocument: ::std::fmt::Debug { fn media<'a>(&'a self, guard: &'a SharedRwLockReadGuard) -> Option<&'a MediaList>; /// Returns a reference to the list of rules in this stylesheet. - fn rules<'a, 'b: 'a>(&'a self, guard: &'b SharedRwLockReadGuard) -> &'a [CssRule]; + fn rules<'a, 'b: 'a>(&'a self, guard: &'b SharedRwLockReadGuard) -> &'a [CssRule] { + self.contents().rules(guard) + } + + /// Returns a reference to the contents of the stylesheet. + fn contents(&self) -> &StylesheetContents; /// Return an iterator using the condition `C`. #[inline] @@ -243,18 +241,19 @@ pub trait StylesheetInDocument: ::std::fmt::Debug { where C: NestedRuleIterationCondition, { + let contents = self.contents(); RulesIterator::new( device, - self.quirks_mode(guard), + contents.quirks_mode, guard, - self.rules(guard).iter(), + contents.rules(guard).iter(), ) } /// Returns whether the style-sheet applies for the current device. fn is_effective_for_device(&self, device: &Device, guard: &SharedRwLockReadGuard) -> bool { match self.media(guard) { - Some(medialist) => medialist.evaluate(device, self.quirks_mode(guard)), + Some(medialist) => medialist.evaluate(device, self.contents().quirks_mode), None => true, } } @@ -285,14 +284,6 @@ pub trait StylesheetInDocument: ::std::fmt::Debug { } impl StylesheetInDocument for Stylesheet { - fn origin(&self, _guard: &SharedRwLockReadGuard) -> Origin { - self.contents.origin - } - - fn quirks_mode(&self, _guard: &SharedRwLockReadGuard) -> QuirksMode { - self.contents.quirks_mode - } - fn media<'a>(&'a self, guard: &'a SharedRwLockReadGuard) -> Option<&'a MediaList> { Some(self.media.read_with(guard)) } @@ -302,8 +293,8 @@ impl StylesheetInDocument for Stylesheet { } #[inline] - fn rules<'a, 'b: 'a>(&'a self, guard: &'b SharedRwLockReadGuard) -> &'a [CssRule] { - self.contents.rules(guard) + fn contents(&self) -> &StylesheetContents { + &self.contents } } @@ -321,21 +312,7 @@ impl PartialEq for DocumentStyleSheet { } } -impl ToMediaListKey for DocumentStyleSheet { - fn to_media_list_key(&self) -> MediaListKey { - self.0.to_media_list_key() - } -} - impl StylesheetInDocument for DocumentStyleSheet { - fn origin(&self, guard: &SharedRwLockReadGuard) -> Origin { - self.0.origin(guard) - } - - fn quirks_mode(&self, guard: &SharedRwLockReadGuard) -> QuirksMode { - self.0.quirks_mode(guard) - } - fn media<'a>(&'a self, guard: &'a SharedRwLockReadGuard) -> Option<&'a MediaList> { self.0.media(guard) } @@ -345,8 +322,8 @@ impl StylesheetInDocument for DocumentStyleSheet { } #[inline] - fn rules<'a, 'b: 'a>(&'a self, guard: &'b SharedRwLockReadGuard) -> &'a [CssRule] { - self.0.rules(guard) + fn contents(&self) -> &StylesheetContents { + self.0.contents() } } diff --git a/components/style/stylist.rs b/components/style/stylist.rs index e2392cf5742..15783b54db0 100644 --- a/components/style/stylist.rs +++ b/components/style/stylist.rs @@ -11,7 +11,7 @@ use crate::element_state::{DocumentState, ElementState}; #[cfg(feature = "gecko")] use crate::gecko_bindings::structs::{ServoStyleSetSizes, StyleRuleInclusion}; use crate::invalidation::element::invalidation_map::InvalidationMap; -use crate::invalidation::media_queries::{EffectiveMediaQueryResults, ToMediaListKey}; +use crate::invalidation::media_queries::EffectiveMediaQueryResults; use crate::invalidation::stylesheets::RuleChangeKind; use crate::media_queries::Device; use crate::properties::{self, CascadeMode, ComputedValues}; @@ -73,7 +73,7 @@ trait CascadeDataCacheEntry : Sized { old_entry: &Self, ) -> Result, FailedAllocationError> where - S: StylesheetInDocument + ToMediaListKey + PartialEq + 'static; + S: StylesheetInDocument + PartialEq + 'static; /// Measures heap memory usage. #[cfg(feature = "gecko")] fn add_size_of(&self, ops: &mut MallocSizeOfOps, sizes: &mut ServoStyleSetSizes); @@ -108,7 +108,7 @@ where old_entry: &Entry, ) -> Result>, FailedAllocationError> where - S: StylesheetInDocument + ToMediaListKey + PartialEq + 'static, + S: StylesheetInDocument + PartialEq + 'static, { debug!("StyleSheetCache::lookup({})", self.len()); @@ -122,9 +122,23 @@ where } for entry in &self.entries { - if entry.cascade_data().effective_media_query_results == key { - return Ok(Some(entry.clone())); + if std::ptr::eq(&**entry, old_entry) { + // Avoid reusing our old entry (this can happen if we get + // invalidated due to CSSOM mutations and our old stylesheet + // contents were already unique, for example). This old entry + // will be pruned from the cache with take_unused() afterwards. + continue; } + if entry.cascade_data().effective_media_query_results != key { + continue; + } + if log_enabled!(log::Level::Debug) { + debug!("cache hit for:"); + for sheet in collection.sheets() { + debug!(" > {:?}", sheet); + } + } + return Ok(Some(entry.clone())); } debug!("> Picking the slow path"); @@ -205,7 +219,7 @@ impl CascadeDataCacheEntry for UserAgentCascadeData { _old: &Self, ) -> Result, FailedAllocationError> where - S: StylesheetInDocument + ToMediaListKey + PartialEq + 'static + S: StylesheetInDocument + PartialEq + 'static { // TODO: Maybe we should support incremental rebuilds, though they seem // uncommon and rebuild() doesn't deal with @@ -319,11 +333,13 @@ impl DocumentCascadeData { guards: &StylesheetGuards, ) -> Result<(), FailedAllocationError> where - S: StylesheetInDocument + ToMediaListKey + PartialEq + 'static, + S: StylesheetInDocument + PartialEq + 'static, { // First do UA sheets. { let origin_flusher = flusher.flush_origin(Origin::UserAgent); + // Dirty check is just a minor optimization (no need to grab the + // lock if nothing has changed). if origin_flusher.dirty() { let mut ua_cache = UA_CASCADE_DATA_CACHE.lock().unwrap(); let new_data = ua_cache.lookup( @@ -436,6 +452,9 @@ pub struct Stylist { /// The list of stylesheets. stylesheets: StylistStylesheetSet, + /// A cache of CascadeDatas for AuthorStylesheetSets (i.e., shadow DOM). + author_data_cache: CascadeDataCache, + /// If true, the quirks-mode stylesheet is applied. #[cfg_attr(feature = "servo", ignore_malloc_size_of = "defined in selectors")] quirks_mode: QuirksMode, @@ -487,6 +506,7 @@ impl Stylist { device, quirks_mode, stylesheets: StylistStylesheetSet::new(), + author_data_cache: CascadeDataCache::new(), cascade_data: Default::default(), author_styles_enabled: AuthorStylesEnabled::Yes, rule_tree: RuleTree::new(), @@ -512,6 +532,31 @@ impl Stylist { self.cascade_data.iter_origins() } + /// Does what the name says, to prevent author_data_cache to grow without + /// bound. + pub fn remove_unique_author_data_cache_entries(&mut self) { + self.author_data_cache.take_unused(); + } + + /// Rebuilds (if needed) the CascadeData given a sheet collection. + pub fn rebuild_author_data( + &mut self, + old_data: &CascadeData, + collection: SheetCollectionFlusher, + guard: &SharedRwLockReadGuard, + ) -> Result>, FailedAllocationError> + where + S: StylesheetInDocument + PartialEq + 'static, + { + self.author_data_cache.lookup( + &self.device, + self.quirks_mode, + collection, + guard, + old_data, + ) + } + /// Iterate over the extra data in origin order. #[inline] pub fn iter_extra_data_origins(&self) -> ExtraStyleDataIterator { @@ -1420,6 +1465,7 @@ impl Stylist { #[cfg(feature = "gecko")] pub fn add_size_of(&self, ops: &mut MallocSizeOfOps, sizes: &mut ServoStyleSetSizes) { self.cascade_data.add_size_of(ops, sizes); + self.author_data_cache.add_size_of(ops, sizes); sizes.mRuleTree += self.rule_tree.size_of(ops); // We may measure other fields in the future if DMD says it's worth it. @@ -1433,7 +1479,7 @@ impl Stylist { /// This struct holds data which users of Stylist may want to extract /// from stylesheets which can be done at the same time as updating. -#[derive(Debug, Default)] +#[derive(Clone, Debug, Default)] #[cfg_attr(feature = "servo", derive(MallocSizeOf))] pub struct ExtraStyleData { /// A list of effective font-face rules and their origin. @@ -1453,11 +1499,6 @@ pub struct ExtraStyleData { pub pages: Vec>>, } -#[cfg(feature = "gecko")] -unsafe impl Sync for ExtraStyleData {} -#[cfg(feature = "gecko")] -unsafe impl Send for ExtraStyleData {} - #[cfg(feature = "gecko")] impl ExtraStyleData { /// Add the given @font-face rule. @@ -1698,7 +1739,7 @@ impl<'a> SelectorVisitor for StylistSelectorVisitor<'a> { } /// A set of rules for element and pseudo-elements. -#[derive(Debug, Default, MallocSizeOf)] +#[derive(Clone, Debug, Default, MallocSizeOf)] struct GenericElementAndPseudoRules { /// Rules from stylesheets at this `CascadeData`'s origin. element_map: Map, @@ -1777,7 +1818,7 @@ impl PartElementAndPseudoRules { /// /// FIXME(emilio): Consider renaming and splitting in `CascadeData` and /// `InvalidationData`? That'd make `clear_cascade_data()` clearer. -#[derive(Debug, MallocSizeOf)] +#[derive(Debug, Clone, MallocSizeOf)] pub struct CascadeData { /// The data coming from normal style rules that apply to elements at this /// cascade level. @@ -1887,7 +1928,7 @@ impl CascadeData { guard: &SharedRwLockReadGuard, ) -> Result<(), FailedAllocationError> where - S: StylesheetInDocument + ToMediaListKey + PartialEq + 'static, + S: StylesheetInDocument + PartialEq + 'static, { if !collection.dirty() { return Ok(()); @@ -1990,14 +2031,14 @@ impl CascadeData { guard: &SharedRwLockReadGuard, results: &mut EffectiveMediaQueryResults, ) where - S: StylesheetInDocument + ToMediaListKey + 'static, + S: StylesheetInDocument + 'static, { if !stylesheet.enabled() || !stylesheet.is_effective_for_device(device, guard) { return; } debug!(" + {:?}", stylesheet); - results.saw_effective(stylesheet); + results.saw_effective(stylesheet.contents()); for rule in stylesheet.effective_rules(device, guard) { match *rule { @@ -2027,16 +2068,17 @@ impl CascadeData { mut precomputed_pseudo_element_decls: Option<&mut PrecomputedPseudoElementDeclarations>, ) -> Result<(), FailedAllocationError> where - S: StylesheetInDocument + ToMediaListKey + 'static, + S: StylesheetInDocument + 'static, { if !stylesheet.enabled() || !stylesheet.is_effective_for_device(device, guard) { return Ok(()); } - let origin = stylesheet.origin(guard); + let contents = stylesheet.contents(); + let origin = contents.origin; if rebuild_kind.should_rebuild_invalidation() { - self.effective_media_query_results.saw_effective(stylesheet); + self.effective_media_query_results.saw_effective(contents); } for rule in stylesheet.effective_rules(device, guard) { @@ -2211,13 +2253,13 @@ impl CascadeData { quirks_mode: QuirksMode, ) -> bool where - S: StylesheetInDocument + ToMediaListKey + 'static, + S: StylesheetInDocument + 'static, { use crate::invalidation::media_queries::PotentiallyEffectiveMediaRules; let effective_now = stylesheet.is_effective_for_device(device, guard); - let effective_then = self.effective_media_query_results.was_effective(stylesheet); + let effective_then = self.effective_media_query_results.was_effective(stylesheet.contents()); if effective_now != effective_then { debug!( @@ -2252,9 +2294,10 @@ impl CascadeData { }, CssRule::Import(ref lock) => { let import_rule = lock.read_with(guard); - let effective_now = import_rule - .stylesheet - .is_effective_for_device(&device, guard); + let effective_now = match import_rule.stylesheet.media(guard) { + Some(m) => m.evaluate(device, quirks_mode), + None => true, + }; let effective_then = self .effective_media_query_results .was_effective(import_rule); @@ -2326,8 +2369,33 @@ impl CascadeData { self.selectors_for_cache_revalidation.clear(); self.effective_media_query_results.clear(); } +} + +impl CascadeDataCacheEntry for CascadeData { + fn cascade_data(&self) -> &CascadeData { + self + } + + fn rebuild( + device: &Device, + quirks_mode: QuirksMode, + collection: SheetCollectionFlusher, + guard: &SharedRwLockReadGuard, + old: &Self, + ) -> Result, FailedAllocationError> + where + S: StylesheetInDocument + PartialEq + 'static + { + debug_assert!(collection.dirty(), "We surely need to do something?"); + // If we're doing a full rebuild anyways, don't bother cloning the data. + let mut updatable_entry = match collection.data_validity() { + DataValidity::Valid | DataValidity::CascadeInvalid => old.clone(), + DataValidity::FullyInvalid => Self::new(), + }; + updatable_entry.rebuild(device, quirks_mode, collection, guard)?; + Ok(Arc::new(updatable_entry)) + } - /// Measures heap usage. #[cfg(feature = "gecko")] fn add_size_of(&self, ops: &mut MallocSizeOfOps, sizes: &mut ServoStyleSetSizes) { self.normal_rules.add_size_of(ops, sizes); From 60e206143c7346b2255c9c1a6af745d4b0c77985 Mon Sep 17 00:00:00 2001 From: Oriol Brufau Date: Fri, 12 May 2023 19:35:00 +0200 Subject: [PATCH 17/65] Further changes required by Servo --- components/layout_thread/lib.rs | 8 ++------ components/layout_thread_2020/lib.rs | 8 ++------ components/script/dom/documentorshadowroot.rs | 17 ++++------------- components/script/dom/shadowroot.rs | 12 ++++-------- components/script/layout_dom/document.rs | 7 +++---- components/script/layout_dom/shadow_root.rs | 9 +++------ components/style/stylesheets/import_rule.rs | 5 ++--- components/style/stylist.rs | 1 + 8 files changed, 21 insertions(+), 46 deletions(-) diff --git a/components/layout_thread/lib.rs b/components/layout_thread/lib.rs index bf4ff1b85bc..1e8bd2848be 100644 --- a/components/layout_thread/lib.rs +++ b/components/layout_thread/lib.rs @@ -96,7 +96,7 @@ use std::time::Duration; use style::animation::{AnimationSetKey, DocumentAnimationSet, ElementAnimationSet}; use style::context::SharedStyleContext; use style::context::{QuirksMode, RegisteredSpeculativePainter, RegisteredSpeculativePainters}; -use style::dom::{ShowSubtree, ShowSubtreeDataAndPrimaryValues, TDocument, TElement, TNode}; +use style::dom::{ShowSubtree, ShowSubtreeDataAndPrimaryValues, TElement, TNode}; use style::driver; use style::error_reporting::RustLogReporter; use style::global_style_data::{GLOBAL_STYLE_DATA, STYLE_THREAD_POOL}; @@ -1285,11 +1285,7 @@ impl LayoutThread { ); // Flush shadow roots stylesheets if dirty. - document.flush_shadow_roots_stylesheets( - &self.stylist.device(), - document.quirks_mode(), - guards.author.clone(), - ); + document.flush_shadow_roots_stylesheets(&mut self.stylist, guards.author.clone()); let restyles = std::mem::take(&mut data.pending_restyles); debug!("Draining restyles: {}", restyles.len()); diff --git a/components/layout_thread_2020/lib.rs b/components/layout_thread_2020/lib.rs index 90c6f4da952..13bef27f3be 100644 --- a/components/layout_thread_2020/lib.rs +++ b/components/layout_thread_2020/lib.rs @@ -82,7 +82,7 @@ use style::animation::DocumentAnimationSet; use style::context::{ QuirksMode, RegisteredSpeculativePainter, RegisteredSpeculativePainters, SharedStyleContext, }; -use style::dom::{TDocument, TElement, TNode}; +use style::dom::{TElement, TNode}; use style::driver; use style::error_reporting::RustLogReporter; use style::global_style_data::{GLOBAL_STYLE_DATA, STYLE_THREAD_POOL}; @@ -943,11 +943,7 @@ impl LayoutThread { } // Flush shadow roots stylesheets if dirty. - document.flush_shadow_roots_stylesheets( - &self.stylist.device(), - document.quirks_mode(), - guards.author.clone(), - ); + document.flush_shadow_roots_stylesheets(&mut self.stylist, guards.author.clone()); let restyles = std::mem::take(&mut data.pending_restyles); debug!("Draining restyles: {}", restyles.len()); diff --git a/components/script/dom/documentorshadowroot.rs b/components/script/dom/documentorshadowroot.rs index 43d345e5e01..51bce1c7b2d 100644 --- a/components/script/dom/documentorshadowroot.rs +++ b/components/script/dom/documentorshadowroot.rs @@ -20,11 +20,10 @@ use servo_arc::Arc; use servo_atoms::Atom; use std::collections::HashMap; use std::fmt; -use style::context::QuirksMode; use style::invalidation::media_queries::{MediaListKey, ToMediaListKey}; use style::media_queries::MediaList; use style::shared_lock::{SharedRwLock as StyleSharedRwLock, SharedRwLockReadGuard}; -use style::stylesheets::{CssRule, Origin, Stylesheet}; +use style::stylesheets::{Stylesheet, StylesheetContents}; #[derive(Clone, JSTraceable, MallocSizeOf)] #[unrooted_must_root_lint::must_root] @@ -48,19 +47,11 @@ impl PartialEq for StyleSheetInDocument { impl ToMediaListKey for StyleSheetInDocument { fn to_media_list_key(&self) -> MediaListKey { - self.sheet.to_media_list_key() + self.sheet.contents.to_media_list_key() } } impl ::style::stylesheets::StylesheetInDocument for StyleSheetInDocument { - fn origin(&self, guard: &SharedRwLockReadGuard) -> Origin { - self.sheet.origin(guard) - } - - fn quirks_mode(&self, guard: &SharedRwLockReadGuard) -> QuirksMode { - self.sheet.quirks_mode(guard) - } - fn enabled(&self) -> bool { self.sheet.enabled() } @@ -69,8 +60,8 @@ impl ::style::stylesheets::StylesheetInDocument for StyleSheetInDocument { self.sheet.media(guard) } - fn rules<'a, 'b: 'a>(&'a self, guard: &'b SharedRwLockReadGuard) -> &'a [CssRule] { - self.sheet.rules(guard) + fn contents(&self) -> &StylesheetContents { + self.sheet.contents() } } diff --git a/components/script/dom/shadowroot.rs b/components/script/dom/shadowroot.rs index c70c13d8cef..1fa9faae287 100644 --- a/components/script/dom/shadowroot.rs +++ b/components/script/dom/shadowroot.rs @@ -19,15 +19,13 @@ use crate::dom::stylesheetlist::{StyleSheetList, StyleSheetListOwner}; use crate::dom::window::Window; use crate::stylesheet_set::StylesheetSetRef; use dom_struct::dom_struct; -use selectors::context::QuirksMode; use servo_arc::Arc; use servo_atoms::Atom; use style::author_styles::AuthorStyles; use style::dom::TElement; -use style::media_queries::Device; use style::shared_lock::SharedRwLockReadGuard; use style::stylesheets::Stylesheet; -use style::stylist::CascadeData; +use style::stylist::{CascadeData, Stylist}; /// Whether a shadow root hosts an User Agent widget. #[derive(JSTraceable, MallocSizeOf, PartialEq)] @@ -245,8 +243,7 @@ pub trait LayoutShadowRootHelpers<'dom> { fn get_style_data_for_layout(self) -> &'dom CascadeData; unsafe fn flush_stylesheets( self, - device: &Device, - quirks_mode: QuirksMode, + stylist: &mut Stylist, guard: &SharedRwLockReadGuard, ); } @@ -277,13 +274,12 @@ impl<'dom> LayoutShadowRootHelpers<'dom> for LayoutDom<'dom, ShadowRoot> { #[allow(unsafe_code)] unsafe fn flush_stylesheets( self, - device: &Device, - quirks_mode: QuirksMode, + stylist: &mut Stylist, guard: &SharedRwLockReadGuard, ) { let author_styles = self.unsafe_get().author_styles.borrow_mut_for_layout(); if author_styles.stylesheets.dirty() { - author_styles.flush::(device, quirks_mode, guard); + author_styles.flush::(stylist, guard); } } } diff --git a/components/script/layout_dom/document.rs b/components/script/layout_dom/document.rs index e723f731cab..392c1686f97 100644 --- a/components/script/layout_dom/document.rs +++ b/components/script/layout_dom/document.rs @@ -12,10 +12,10 @@ use script_layout_interface::wrapper_traits::LayoutDataTrait; use selectors::matching::QuirksMode; use std::marker::PhantomData; use style::dom::{TDocument, TNode}; -use style::media_queries::Device; use style::shared_lock::{ SharedRwLock as StyleSharedRwLock, SharedRwLockReadGuard as StyleSharedRwLockReadGuard, }; +use style::stylist::Stylist; // A wrapper around documents that ensures ayout can only ever access safe properties. pub struct ServoLayoutDocument<'dom, LayoutDataType: LayoutDataTrait> { @@ -90,8 +90,7 @@ impl<'ld, LayoutDataType: LayoutDataTrait> ServoLayoutDocument<'ld, LayoutDataTy pub fn flush_shadow_roots_stylesheets( &self, - device: &Device, - quirks_mode: QuirksMode, + stylist: &mut Stylist, guard: &StyleSharedRwLockReadGuard, ) { unsafe { @@ -100,7 +99,7 @@ impl<'ld, LayoutDataType: LayoutDataTrait> ServoLayoutDocument<'ld, LayoutDataTy } self.document.flush_shadow_roots_stylesheets(); for shadow_root in self.shadow_roots() { - shadow_root.flush_stylesheets(device, quirks_mode, guard); + shadow_root.flush_stylesheets(stylist, guard); } } } diff --git a/components/script/layout_dom/shadow_root.rs b/components/script/layout_dom/shadow_root.rs index 2fd1511ccfa..19336befe74 100644 --- a/components/script/layout_dom/shadow_root.rs +++ b/components/script/layout_dom/shadow_root.rs @@ -7,13 +7,11 @@ use crate::dom::shadowroot::{LayoutShadowRootHelpers, ShadowRoot}; use crate::layout_dom::ServoLayoutElement; use crate::layout_dom::ServoLayoutNode; use script_layout_interface::wrapper_traits::LayoutDataTrait; -use selectors::matching::QuirksMode; use std::fmt; use std::marker::PhantomData; use style::dom::TShadowRoot; -use style::media_queries::Device; use style::shared_lock::SharedRwLockReadGuard as StyleSharedRwLockReadGuard; -use style::stylist::CascadeData; +use style::stylist::{CascadeData, Stylist}; pub struct ServoShadowRoot<'dom, LayoutDataType: LayoutDataTrait> { /// The wrapped private DOM ShadowRoot. @@ -74,11 +72,10 @@ impl<'dom, LayoutDataType: LayoutDataTrait> ServoShadowRoot<'dom, LayoutDataType pub unsafe fn flush_stylesheets( &self, - device: &Device, - quirks_mode: QuirksMode, + stylist: &mut Stylist, guard: &StyleSharedRwLockReadGuard, ) { self.shadow_root - .flush_stylesheets::>(device, quirks_mode, guard) + .flush_stylesheets::>(stylist, guard) } } diff --git a/components/style/stylesheets/import_rule.rs b/components/style/stylesheets/import_rule.rs index 201513760b3..396be242024 100644 --- a/components/style/stylesheets/import_rule.rs +++ b/components/style/stylesheets/import_rule.rs @@ -6,12 +6,11 @@ //! //! [import]: https://drafts.csswg.org/css-cascade-3/#at-import -use crate::context::QuirksMode; use crate::media_queries::MediaList; use crate::shared_lock::{DeepCloneParams, DeepCloneWithLock}; use crate::shared_lock::{SharedRwLock, SharedRwLockReadGuard, ToCssWithGuard}; use crate::str::CssStringWriter; -use crate::stylesheets::{CssRule, Origin, StylesheetInDocument}; +use crate::stylesheets::{CssRule, StylesheetInDocument}; use crate::values::CssUrl; use cssparser::SourceLocation; use std::fmt::{self, Write}; @@ -112,7 +111,7 @@ impl ImportSheet { /// Returns the rules for this import rule. pub fn rules<'a>(&'a self, guard: &'a SharedRwLockReadGuard) -> &'a [CssRule] { - self.0.rules() + self.0.rules(guard) } } diff --git a/components/style/stylist.rs b/components/style/stylist.rs index 15783b54db0..c4eebefeab9 100644 --- a/components/style/stylist.rs +++ b/components/style/stylist.rs @@ -453,6 +453,7 @@ pub struct Stylist { stylesheets: StylistStylesheetSet, /// A cache of CascadeDatas for AuthorStylesheetSets (i.e., shadow DOM). + #[cfg_attr(feature = "servo", ignore_malloc_size_of = "XXX: how to handle this?")] author_data_cache: CascadeDataCache, /// If true, the quirks-mode stylesheet is applied. From 1be19485bb20fcea7588181dcafc5f4359f2fbf7 Mon Sep 17 00:00:00 2001 From: Oriol Brufau Date: Tue, 16 May 2023 10:04:00 +0200 Subject: [PATCH 18/65] style: Make -moz-accent-color reflect the windows accent color And remove the windows-specific versions of this color. Also fix the hard-coded defaults by the colors I get in the default windows theme. Differential Revision: https://phabricator.services.mozilla.com/D108325 --- components/style/values/specified/color.rs | 4 ---- 1 file changed, 4 deletions(-) diff --git a/components/style/values/specified/color.rs b/components/style/values/specified/color.rs index 21b8abe6de8..486d22fa101 100644 --- a/components/style/values/specified/color.rs +++ b/components/style/values/specified/color.rs @@ -349,10 +349,6 @@ pub enum SystemColor { #[parse(condition = "ParserContext::in_ua_or_chrome_sheet")] MozAccentColorForeground, - /// Accent color for title bar. - MozWinAccentcolor, - /// Color from drawing text over the accent color. - MozWinAccentcolortext, /// Media rebar text. MozWinMediatext, /// Communications rebar text. From ed19da405fd6755d4e8777b039e70cc04555b637 Mon Sep 17 00:00:00 2001 From: Oriol Brufau Date: Tue, 16 May 2023 10:06:29 +0200 Subject: [PATCH 19/65] style: Invalidate a bit more aggressively when a pseudo-element matches, in order to also invalidate the cached pseudo-styles on the parent This should probably be fine. If it becomes a perf issue somehow we can implement the RESTYLE_PSEUDOS hint or what not. Differential Revision: https://phabricator.services.mozilla.com/D108338 --- .../style/invalidation/element/invalidator.rs | 98 +++++-------------- .../element/state_and_attributes.rs | 8 +- 2 files changed, 31 insertions(+), 75 deletions(-) diff --git a/components/style/invalidation/element/invalidator.rs b/components/style/invalidation/element/invalidator.rs index c148b63e3e3..79131a8008f 100644 --- a/components/style/invalidation/element/invalidator.rs +++ b/components/style/invalidation/element/invalidator.rs @@ -20,10 +20,10 @@ pub trait InvalidationProcessor<'a, E> where E: TElement, { - /// Whether an invalidation that contains only an eager pseudo-element - /// selector like ::before or ::after triggers invalidation of the element - /// that would originate it. - fn invalidates_on_eager_pseudo_element(&self) -> bool { + /// Whether an invalidation that contains only a pseudo-element selector + /// like ::before or ::after triggers invalidation of the element that would + /// originate it. + fn invalidates_on_pseudo_element(&self) -> bool { false } @@ -878,76 +878,32 @@ where .selector .combinator_at_parse_order(next_invalidation.offset - 1); - if matches!(next_combinator, Combinator::PseudoElement) { - // This will usually be the very next component, except for - // the fact that we store compound selectors the other way - // around, so there could also be state pseudo-classes. - let pseudo = next_invalidation - .dependency - .selector - .iter_raw_parse_order_from(next_invalidation.offset) - .flat_map(|c| { - if let Component::PseudoElement(ref pseudo) = *c { - return Some(pseudo); - } - - // TODO: Would be nice to make this a diagnostic_assert! of - // sorts. - debug_assert!( - c.maybe_allowed_after_pseudo_element(), - "Someone seriously messed up selector parsing: \ - {:?} at offset {:?}: {:?}", - next_invalidation.dependency, - next_invalidation.offset, - c, - ); - - None - }) - .next() - .unwrap(); - - // FIXME(emilio): This is not ideal, and could not be - // accurate if we ever have stateful element-backed eager - // pseudos. + if matches!(next_combinator, Combinator::PseudoElement) && + self.processor.invalidates_on_pseudo_element() + { + // We need to invalidate the element whenever pseudos change, for + // two reasons: // - // Ideally, we'd just remove element-backed eager pseudos - // altogether, given they work fine without it. Only gotcha - // is that we wouldn't style them in parallel, which may or - // may not be an issue. + // * Eager pseudo styles are stored as part of the originating + // element's computed style. // - // Also, this could be more fine grained now (perhaps a - // RESTYLE_PSEUDOS hint?). + // * Lazy pseudo-styles might be cached on the originating + // element's pseudo-style cache. // - // Note that we'll also restyle the pseudo-element because - // it would match this invalidation. - if self.processor.invalidates_on_eager_pseudo_element() { - if pseudo.is_eager() { - invalidated_self = true; - } - // If we start or stop matching some marker rules, and - // don't have a marker, then we need to restyle the - // element to potentially create one. - // - // Same caveats as for other eager pseudos apply, this - // could be more fine-grained. - if pseudo.is_marker() && self.element.marker_pseudo_element().is_none() { - invalidated_self = true; - } - - // FIXME: ::selection doesn't generate elements, so the - // regular invalidation doesn't work for it. We store - // the cached selection style holding off the originating - // element, so we need to restyle it in order to invalidate - // it. This is still not quite correct, since nothing - // triggers a repaint necessarily, but matches old Gecko - // behavior, and the ::selection implementation needs to - // change significantly anyway to implement - // https://github.com/w3c/csswg-drafts/issues/2474. - if pseudo.is_selection() { - invalidated_self = true; - } - } + // This could be more fine-grained (perhaps with a RESTYLE_PSEUDOS + // hint?). + // + // Note that we'll also restyle the pseudo-element because it would + // match this invalidation. + // + // FIXME: For non-element-backed pseudos this is still not quite + // correct. For example for ::selection even though we invalidate + // the style properly there's nothing that triggers a repaint + // necessarily. Though this matches old Gecko behavior, and the + // ::selection implementation needs to change significantly anyway + // to implement https://github.com/w3c/csswg-drafts/issues/2474 for + // example. + invalidated_self = true; } debug!( diff --git a/components/style/invalidation/element/state_and_attributes.rs b/components/style/invalidation/element/state_and_attributes.rs index 128feae76be..bbb1fb46a80 100644 --- a/components/style/invalidation/element/state_and_attributes.rs +++ b/components/style/invalidation/element/state_and_attributes.rs @@ -158,10 +158,10 @@ impl<'a, 'b: 'a, E: 'a> InvalidationProcessor<'a, E> where E: TElement, { - /// We need to invalidate style on an eager pseudo-element, in order to - /// process changes that could otherwise end up in ::before or ::after - /// content being generated. - fn invalidates_on_eager_pseudo_element(&self) -> bool { + /// We need to invalidate style on pseudo-elements, in order to process + /// changes that could otherwise end up in ::before or ::after content being + /// generated, and invalidate lazy pseudo caches. + fn invalidates_on_pseudo_element(&self) -> bool { true } From b0a1eaebaacaf3ed60ec89a9062c77bb8cc00527 Mon Sep 17 00:00:00 2001 From: Oriol Brufau Date: Tue, 16 May 2023 10:16:12 +0200 Subject: [PATCH 20/65] style: Respect system colors in the color and background-color properties The reason why this doesn't work is because these styles come from datetimebox.css, which is really an author style. We could special-case these elements, but the approach that the CSSWG resolved on for the new forced-colors spec is to respect system colors specified by authors, see: https://drafts.csswg.org/css-color-adjust-1/#forced-colors-properties So this moves us towards that, and fixes the issue nicely. Differential Revision: https://phabricator.services.mozilla.com/D108321 --- components/style/properties/cascade.rs | 9 ++++++++- components/style/values/specified/color.rs | 5 +++++ 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/components/style/properties/cascade.rs b/components/style/properties/cascade.rs index 275ed7afc38..9c3e6ebd85e 100644 --- a/components/style/properties/cascade.rs +++ b/components/style/properties/cascade.rs @@ -416,6 +416,10 @@ fn tweak_when_ignoring_colors( // A few special-cases ahead. match **declaration { PropertyDeclaration::BackgroundColor(ref color) => { + // We honor system colors. + if color.is_system() { + return; + } // For background-color, we revert or initial-with-preserved-alpha // otherwise, this is needed to preserve semi-transparent // backgrounds. @@ -433,7 +437,10 @@ fn tweak_when_ignoring_colors( } }, PropertyDeclaration::Color(ref color) => { - // We honor color: transparent, and "revert-or-initial" otherwise. + // We honor color: transparent and system colors. + if color.0.is_system() { + return; + } if alpha_channel(&color.0, context) == 0 { return; } diff --git a/components/style/values/specified/color.rs b/components/style/values/specified/color.rs index 486d22fa101..c70d69e01b8 100644 --- a/components/style/values/specified/color.rs +++ b/components/style/values/specified/color.rs @@ -561,6 +561,11 @@ fn parse_hash_color(value: &[u8]) -> Result { } impl Color { + /// Returns whether this color is a system color. + pub fn is_system(&self) -> bool { + matches!(self, Color::System(..)) + } + /// Returns currentcolor value. #[inline] pub fn currentcolor() -> Color { From 261dcf1f6dd8aa89ee16bfb0557a19c90ee7b01a Mon Sep 17 00:00:00 2001 From: Oriol Brufau Date: Tue, 16 May 2023 10:42:02 +0200 Subject: [PATCH 21/65] Further changes required by Servo --- components/style/values/specified/color.rs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/components/style/values/specified/color.rs b/components/style/values/specified/color.rs index c70d69e01b8..dc311680b36 100644 --- a/components/style/values/specified/color.rs +++ b/components/style/values/specified/color.rs @@ -562,10 +562,17 @@ fn parse_hash_color(value: &[u8]) -> Result { impl Color { /// Returns whether this color is a system color. + #[cfg(feature = "gecko")] pub fn is_system(&self) -> bool { matches!(self, Color::System(..)) } + /// Returns whether this color is a system color. + #[cfg(feature = "servo")] + pub fn is_system(&self) -> bool { + false + } + /// Returns currentcolor value. #[inline] pub fn currentcolor() -> Color { From cbf2a91ec8a132c139c8ae16facde23e4f1b9057 Mon Sep 17 00:00:00 2001 From: Oriol Brufau Date: Tue, 16 May 2023 10:17:17 +0200 Subject: [PATCH 22/65] style: Remove unused -moz-default-appearance values -moz-mac-vibrancy-light/dark, and the system colors of the same name Starting with macOS 10.14, the generic light/dark vibrancy is deprecated, and semantic vibrancy names are preferred. If we ever need more vibrancy, we can add new values with semantic names. Depends on D107910 Differential Revision: https://phabricator.services.mozilla.com/D108152 --- components/style/values/specified/box.rs | 4 ---- components/style/values/specified/color.rs | 2 -- 2 files changed, 6 deletions(-) diff --git a/components/style/values/specified/box.rs b/components/style/values/specified/box.rs index e30ea5215d0..3039a990ff9 100644 --- a/components/style/values/specified/box.rs +++ b/components/style/values/specified/box.rs @@ -1838,10 +1838,6 @@ pub enum Appearance { #[parse(condition = "ParserContext::in_ua_or_chrome_sheet")] MozMacSourceListSelection, #[parse(condition = "ParserContext::in_ua_or_chrome_sheet")] - MozMacVibrancyDark, - #[parse(condition = "ParserContext::in_ua_or_chrome_sheet")] - MozMacVibrancyLight, - #[parse(condition = "ParserContext::in_ua_or_chrome_sheet")] MozMacVibrantTitlebarDark, #[parse(condition = "ParserContext::in_ua_or_chrome_sheet")] MozMacVibrantTitlebarLight, diff --git a/components/style/values/specified/color.rs b/components/style/values/specified/color.rs index dc311680b36..d60f4801a11 100644 --- a/components/style/values/specified/color.rs +++ b/components/style/values/specified/color.rs @@ -329,8 +329,6 @@ pub enum SystemColor { /// Font smoothing background colors needed by the Mac OS X theme, based on /// -moz-appearance names. - MozMacVibrancyLight, - MozMacVibrancyDark, MozMacVibrantTitlebarLight, MozMacVibrantTitlebarDark, MozMacMenupopup, From cdce0d3b34dd5d09579e9185bc2669b8a4b9d1ee Mon Sep 17 00:00:00 2001 From: Oriol Brufau Date: Tue, 16 May 2023 10:20:11 +0200 Subject: [PATCH 23/65] style: Part 1 - Ignore any properties which aren't valid for a style rule when generating CSS2Properties and testing propertie To know the valid rules for each property, we need to put this information into the Servo prop list and add an appropriate getter to Longhand/Shorthand. Differential Revision: https://phabricator.services.mozilla.com/D105825 --- components/style/properties/data.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/components/style/properties/data.py b/components/style/properties/data.py index d95eed44dd9..dd64f799fdd 100644 --- a/components/style/properties/data.py +++ b/components/style/properties/data.py @@ -261,6 +261,11 @@ class Property(object): self.extra_prefixes = parse_property_aliases(extra_prefixes) self.flags = flags.split() if flags else [] + def rule_types_allowed_names(self): + for name in RULE_VALUES: + if self.rule_types_allowed & RULE_VALUES[name] != 0: + yield name + def experimental(self, engine): if engine == "gecko": return bool(self.gecko_pref) @@ -598,6 +603,11 @@ class Alias(object): def type(): return "alias" + def rule_types_allowed_names(self): + for name in RULE_VALUES: + if self.rule_types_allowed & RULE_VALUES[name] != 0: + yield name + def experimental(self, engine): if engine == "gecko": return bool(self.gecko_pref) From 3b2eef3d7d308da5ebc7619e6a3268b66a3c9b6e Mon Sep 17 00:00:00 2001 From: Oriol Brufau Date: Tue, 16 May 2023 10:23:08 +0200 Subject: [PATCH 24/65] style: Part 2 - Add page-size attribute to nsStyleStruct and property parsing This parsing is hidden behind the pref layout.css.page-size.enabled. It isn't ideal that we parse this as a property, but we can't treat it as a descriptor because of compatibility issues with other browsers. There are also outstanding spec issues related to how descriptors like page-size are cascaded, and whether the !important specifier is valid or not. Differential Revision: https://phabricator.services.mozilla.com/D103958 --- components/style/properties/build.py | 1 + .../properties/counted_unknown_properties.py | 1 - components/style/properties/gecko.mako.rs | 3 +++ .../style/properties/longhands/page.mako.rs | 21 +++++++++++++++++++ components/style/values/computed/page.rs | 2 +- components/style/values/generics/page.rs | 4 +++- 6 files changed, 29 insertions(+), 3 deletions(-) create mode 100644 components/style/properties/longhands/page.mako.rs diff --git a/components/style/properties/build.py b/components/style/properties/build.py index 97ad10408ea..6df38fcefe3 100644 --- a/components/style/properties/build.py +++ b/components/style/properties/build.py @@ -37,6 +37,7 @@ STYLE_STRUCT_LIST = [ "list", "margin", "outline", + "page", "padding", "position", "svg", diff --git a/components/style/properties/counted_unknown_properties.py b/components/style/properties/counted_unknown_properties.py index 047d129a2ff..577547d099d 100644 --- a/components/style/properties/counted_unknown_properties.py +++ b/components/style/properties/counted_unknown_properties.py @@ -19,7 +19,6 @@ COUNTED_UNKNOWN_PROPERTIES = [ "text-size-adjust", "-webkit-font-feature-settings", "-webkit-user-drag", - "size", "-webkit-clip-path", "orphans", "widows", diff --git a/components/style/properties/gecko.mako.rs b/components/style/properties/gecko.mako.rs index 9bb13eb62b0..7a864c06d27 100644 --- a/components/style/properties/gecko.mako.rs +++ b/components/style/properties/gecko.mako.rs @@ -781,6 +781,9 @@ fn static_assert() { % endfor +<%self:impl_trait style_struct_name="Page"> + + <% skip_position_longhands = " ".join(x.ident for x in SIDES) %> <%self:impl_trait style_struct_name="Position" skip_longhands="${skip_position_longhands} diff --git a/components/style/properties/longhands/page.mako.rs b/components/style/properties/longhands/page.mako.rs new file mode 100644 index 00000000000..298456cb753 --- /dev/null +++ b/components/style/properties/longhands/page.mako.rs @@ -0,0 +1,21 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ + +<%namespace name="helpers" file="/helpers.mako.rs" /> +<% from data import PAGE_RULE %> + +<% data.new_style_struct("Page", inherited=False) %> + +${helpers.predefined_type( + "size", + "PageSize", + "computed::PageSize::auto()", + engines="gecko", + gecko_pref="layout.css.page-size.enabled", + initial_specified_value="specified::PageSize::auto()", + spec="https://drafts.csswg.org/css-page-3/#page-size-prop", + boxed=True, + animation_value_type="none", + rule_types_allowed=PAGE_RULE, +)} diff --git a/components/style/values/computed/page.rs b/components/style/values/computed/page.rs index 4dd8804596e..d8ef03c1f6d 100644 --- a/components/style/values/computed/page.rs +++ b/components/style/values/computed/page.rs @@ -11,4 +11,4 @@ use crate::values::generics::size::Size2D; pub use generics::page::Orientation; pub use generics::page::PaperSize; /// Computed value of the @page size descriptor -pub type PageSize = generics::page::PageSize>; +pub type PageSize = generics::page::GenericPageSize>; diff --git a/components/style/values/generics/page.rs b/components/style/values/generics/page.rs index fc771062333..35ffee7b97e 100644 --- a/components/style/values/generics/page.rs +++ b/components/style/values/generics/page.rs @@ -86,7 +86,7 @@ pub enum Orientation { ToShmem, )] #[repr(C, u8)] -pub enum PageSize { +pub enum GenericPageSize { /// Page dimensions. Size(S), /// Paper size with no orientation. @@ -99,6 +99,8 @@ pub enum PageSize { Auto, } +pub use self::GenericPageSize as PageSize; + impl PageSize { /// `auto` value. #[inline] From 4d4fb4b41448424a092eb05d1010e6b76ed4ae9b Mon Sep 17 00:00:00 2001 From: Oriol Brufau Date: Tue, 16 May 2023 10:53:25 +0200 Subject: [PATCH 25/65] style: Create a NonNegativePercentage type in values:specified Differential Revision: https://phabricator.services.mozilla.com/D109285 --- components/style/values/specified/mod.rs | 2 +- components/style/values/specified/percentage.rs | 14 ++++++++++++++ 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/components/style/values/specified/mod.rs b/components/style/values/specified/mod.rs index 946bf3c2cc9..6341d495d6c 100644 --- a/components/style/values/specified/mod.rs +++ b/components/style/values/specified/mod.rs @@ -71,7 +71,7 @@ pub use self::list::Quotes; pub use self::motion::{OffsetPath, OffsetRotate}; pub use self::outline::OutlineStyle; pub use self::page::{Orientation, PageSize, PaperSize}; -pub use self::percentage::Percentage; +pub use self::percentage::{Percentage, NonNegativePercentage}; pub use self::position::AspectRatio; pub use self::position::{ GridAutoFlow, GridTemplateAreas, MasonryAutoFlow, Position, PositionOrAuto, diff --git a/components/style/values/specified/percentage.rs b/components/style/values/specified/percentage.rs index eac4d91cf8f..c73616a3848 100644 --- a/components/style/values/specified/percentage.rs +++ b/components/style/values/specified/percentage.rs @@ -7,6 +7,7 @@ use crate::parser::{Parse, ParserContext}; use crate::values::computed::percentage::Percentage as ComputedPercentage; use crate::values::computed::{Context, ToComputedValue}; +use crate::values::generics::NonNegative; use crate::values::specified::calc::CalcNode; use crate::values::specified::Number; use crate::values::{serialize_percentage, CSSFloat}; @@ -172,3 +173,16 @@ impl ToComputedValue for Percentage { } impl SpecifiedValueInfo for Percentage {} + +/// A wrapper of Percentage, whose value must be >= 0. +pub type NonNegativePercentage = NonNegative; + +impl Parse for NonNegativePercentage { + #[inline] + fn parse<'i, 't>( + context: &ParserContext, + input: &mut Parser<'i, 't>, + ) -> Result> { + Ok(NonNegative(Percentage::parse_non_negative(context, input)?)) + } +} From 5ef832b7787b80ab9771e92e790d6d599d187472 Mon Sep 17 00:00:00 2001 From: Oriol Brufau Date: Tue, 16 May 2023 10:56:02 +0200 Subject: [PATCH 26/65] style: Convert the font-stretch descriptor to use NonNegativePercentage Depends on D109285 Differential Revision: https://phabricator.services.mozilla.com/D109286 --- components/style/font_face.rs | 2 +- .../style/properties/shorthands/font.mako.rs | 2 +- components/style/values/specified/font.rs | 31 +++---------------- 3 files changed, 7 insertions(+), 28 deletions(-) diff --git a/components/style/font_face.rs b/components/style/font_face.rs index 884978c5c3b..b04701649e1 100644 --- a/components/style/font_face.rs +++ b/components/style/font_face.rs @@ -194,7 +194,7 @@ impl FontStretchRange { fn compute_stretch(s: &FontStretch) -> f32 { match *s { FontStretch::Keyword(ref kw) => kw.compute().0, - FontStretch::Stretch(ref p) => p.get(), + FontStretch::Stretch(ref p) => p.0.get(), FontStretch::System(..) => unreachable!(), } } diff --git a/components/style/properties/shorthands/font.mako.rs b/components/style/properties/shorthands/font.mako.rs index 176164c4b1e..2ed36ae88a5 100644 --- a/components/style/properties/shorthands/font.mako.rs +++ b/components/style/properties/shorthands/font.mako.rs @@ -197,7 +197,7 @@ let font_stretch = match *self.font_stretch { FontStretch::Keyword(kw) => kw, FontStretch::Stretch(percentage) => { - match FontStretchKeyword::from_percentage(percentage.get()) { + match FontStretchKeyword::from_percentage(percentage.0.get()) { Some(kw) => kw, None => return Ok(()), } diff --git a/components/style/values/specified/font.rs b/components/style/values/specified/font.rs index b83b6411fee..f8556fe379e 100644 --- a/components/style/values/specified/font.rs +++ b/components/style/values/specified/font.rs @@ -19,7 +19,7 @@ use crate::values::generics::font::{self as generics, FeatureTagValue, FontSetti use crate::values::generics::NonNegative; use crate::values::specified::length::{FontBaseSize, PX_PER_PT}; use crate::values::specified::{AllowQuirks, Angle, Integer, LengthPercentage}; -use crate::values::specified::{NoCalcLength, NonNegativeNumber, Number, Percentage}; +use crate::values::specified::{NoCalcLength, NonNegativeNumber, Number, NonNegativePercentage}; use crate::values::CustomIdent; use crate::Atom; use cssparser::{Parser, Token}; @@ -359,13 +359,11 @@ impl ToComputedValue for FontStyle { /// A value for the `font-stretch` property. /// /// https://drafts.csswg.org/css-fonts-4/#font-stretch-prop -/// -/// TODO(emilio): We could derive Parse if we had NonNegativePercentage. #[allow(missing_docs)] -#[derive(Clone, Copy, Debug, MallocSizeOf, PartialEq, SpecifiedValueInfo, ToCss, ToShmem)] +#[derive(Clone, Copy, Debug, MallocSizeOf, Parse, PartialEq, SpecifiedValueInfo, ToCss, ToShmem)] #[repr(u8)] pub enum FontStretch { - Stretch(Percentage), + Stretch(NonNegativePercentage), Keyword(FontStretchKeyword), #[css(skip)] System(SystemFont), @@ -452,32 +450,13 @@ impl FontStretch { system_font_methods!(FontStretch, font_stretch); } -impl Parse for FontStretch { - fn parse<'i, 't>( - context: &ParserContext, - input: &mut Parser<'i, 't>, - ) -> Result> { - // From https://drafts.csswg.org/css-fonts-4/#font-stretch-prop: - // - // Values less than 0% are not allowed and are treated as parse - // errors. - if let Ok(percentage) = - input.try_parse(|input| Percentage::parse_non_negative(context, input)) - { - return Ok(FontStretch::Stretch(percentage)); - } - - Ok(FontStretch::Keyword(FontStretchKeyword::parse(input)?)) - } -} - impl ToComputedValue for FontStretch { type ComputedValue = computed::FontStretch; fn to_computed_value(&self, context: &Context) -> Self::ComputedValue { match *self { FontStretch::Stretch(ref percentage) => { - computed::FontStretch(NonNegative(percentage.to_computed_value(context))) + computed::FontStretch(percentage.to_computed_value(context)) }, FontStretch::Keyword(ref kw) => computed::FontStretch(NonNegative(kw.compute())), FontStretch::System(_) => self.compute_system(context), @@ -485,7 +464,7 @@ impl ToComputedValue for FontStretch { } fn from_computed_value(computed: &Self::ComputedValue) -> Self { - FontStretch::Stretch(Percentage::from_computed_value(&(computed.0).0)) + FontStretch::Stretch(NonNegativePercentage::from_computed_value(&NonNegative((computed.0).0))) } } From 1cd257d54f6af7a744ac1207523361a41a9c864f Mon Sep 17 00:00:00 2001 From: Oriol Brufau Date: Tue, 16 May 2023 10:57:00 +0200 Subject: [PATCH 27/65] style: Add ascent-, descent- and line-gap-override descriptors to the style system Differential Revision: https://phabricator.services.mozilla.com/D109287 --- components/style/font_face.rs | 20 ++++++++++++++- components/style/values/specified/font.rs | 31 +++++++++++++++++++++++ 2 files changed, 50 insertions(+), 1 deletion(-) diff --git a/components/style/font_face.rs b/components/style/font_face.rs index b04701649e1..cb5a2c06e23 100644 --- a/components/style/font_face.rs +++ b/components/style/font_face.rs @@ -19,7 +19,7 @@ use crate::values::specified::font::SpecifiedFontFeatureSettings; use crate::values::specified::font::SpecifiedFontStyle; #[cfg(feature = "gecko")] use crate::values::specified::font::SpecifiedFontVariationSettings; -use crate::values::specified::font::{AbsoluteFontWeight, FontStretch}; +use crate::values::specified::font::{AbsoluteFontWeight, FontStretch, MetricsOverride}; use crate::values::specified::url::SpecifiedUrl; use crate::values::specified::Angle; #[cfg(feature = "gecko")] @@ -418,6 +418,15 @@ macro_rules! is_descriptor_enabled { ("font-variation-settings") => { static_prefs::pref!("layout.css.font-variations.enabled") }; + ("ascent-override") => { + static_prefs::pref!("layout.css.font-metrics-overrides.enabled") + }; + ("descent-override") => { + static_prefs::pref!("layout.css.font-metrics-overrides.enabled") + }; + ("line-gap-override") => { + static_prefs::pref!("layout.css.font-metrics-overrides.enabled") + }; ($name:tt) => { true }; @@ -576,6 +585,15 @@ font_face_descriptors! { /// The language override of this font face. "font-language-override" language_override / mFontLanguageOverride: font_language_override::SpecifiedValue, + + /// The ascent override for this font face. + "ascent-override" ascent_override / mAscentOverride: MetricsOverride, + + /// The descent override for this font face. + "descent-override" descent_override / mDescentOverride: MetricsOverride, + + /// The line-gap override for this font face. + "line-gap-override" line_gap_override / mLineGapOverride: MetricsOverride, ] } diff --git a/components/style/values/specified/font.rs b/components/style/values/specified/font.rs index f8556fe379e..6e43c34d53b 100644 --- a/components/style/values/specified/font.rs +++ b/components/style/values/specified/font.rs @@ -2205,6 +2205,37 @@ impl Parse for VariationValue { } } +/// A metrics override value for a @font-face descriptor +/// +/// https://drafts.csswg.org/css-fonts/#font-metrics-override-desc +#[derive(Clone, Copy, Debug, MallocSizeOf, Parse, PartialEq, SpecifiedValueInfo, ToCss, ToShmem)] +pub enum MetricsOverride { + /// A non-negative `` of the computed font size + Override(NonNegativePercentage), + /// Normal metrics from the font. + Normal, +} + +impl MetricsOverride { + #[inline] + /// Get default value with `normal` + pub fn normal() -> MetricsOverride { + MetricsOverride::Normal + } + + /// The ToComputedValue implementation, used for @font-face descriptors. + /// + /// Valid override percentages must be non-negative; we return -1.0 to indicate + /// the absence of an override (i.e. 'normal'). + #[inline] + pub fn compute(&self) -> ComputedPercentage { + match *self { + MetricsOverride::Normal => ComputedPercentage(-1.0), + MetricsOverride::Override(percent) => ComputedPercentage(percent.0.get()), + } + } +} + #[derive( Clone, Copy, From 7da6d525ac61e5fd4cacdfb74cad7358fc5ae6f3 Mon Sep 17 00:00:00 2001 From: Oriol Brufau Date: Tue, 16 May 2023 11:08:12 +0200 Subject: [PATCH 28/65] Further changes required by Servo --- components/style/font_face.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/components/style/font_face.rs b/components/style/font_face.rs index cb5a2c06e23..901bafe5101 100644 --- a/components/style/font_face.rs +++ b/components/style/font_face.rs @@ -19,7 +19,9 @@ use crate::values::specified::font::SpecifiedFontFeatureSettings; use crate::values::specified::font::SpecifiedFontStyle; #[cfg(feature = "gecko")] use crate::values::specified::font::SpecifiedFontVariationSettings; -use crate::values::specified::font::{AbsoluteFontWeight, FontStretch, MetricsOverride}; +use crate::values::specified::font::{AbsoluteFontWeight, FontStretch}; +#[cfg(feature = "gecko")] +use crate::values::specified::font::MetricsOverride; use crate::values::specified::url::SpecifiedUrl; use crate::values::specified::Angle; #[cfg(feature = "gecko")] From 4d2ccaf445dc770a45b94901c697e3e9668c7761 Mon Sep 17 00:00:00 2001 From: Oriol Brufau Date: Tue, 16 May 2023 13:16:53 +0200 Subject: [PATCH 29/65] style: Use a single system font definition rather than two Follow the pattern we use for system colors. Differential Revision: https://phabricator.services.mozilla.com/D108822 --- components/style/properties/helpers.mako.rs | 2 +- .../style/properties/longhands/font.mako.rs | 333 ++++++++---------- .../style/properties/properties.mako.rs | 2 +- .../style/properties/shorthands/font.mako.rs | 6 +- components/style/values/specified/font.rs | 28 +- 5 files changed, 171 insertions(+), 200 deletions(-) diff --git a/components/style/properties/helpers.mako.rs b/components/style/properties/helpers.mako.rs index 30eaac343ac..c3987874dc1 100644 --- a/components/style/properties/helpers.mako.rs +++ b/components/style/properties/helpers.mako.rs @@ -554,7 +554,7 @@ keyword = keyword=Keyword(name, values, **keyword_kwargs) %> <%call expr="longhand(name, keyword=Keyword(name, values, **keyword_kwargs), **kwargs)"> - use crate::properties::longhands::system_font::SystemFont; + use crate::values::specified::font::SystemFont; pub mod computed_value { #[cfg_attr(feature = "servo", derive(Deserialize, Serialize))] diff --git a/components/style/properties/longhands/font.mako.rs b/components/style/properties/longhands/font.mako.rs index 1fb52a01c77..e84a5574d1a 100644 --- a/components/style/properties/longhands/font.mako.rs +++ b/components/style/properties/longhands/font.mako.rs @@ -288,208 +288,153 @@ ${helpers.predefined_type( )} % if engine == "gecko": - pub mod system_font { - //! We deal with system fonts here - //! - //! System fonts can only be set as a group via the font shorthand. - //! They resolve at compute time (not parse time -- this lets the - //! browser respond to changes to the OS font settings). - //! - //! While Gecko handles these as a separate property and keyword - //! values on each property indicating that the font should be picked - //! from the -x-system-font property, we avoid this. Instead, - //! each font longhand has a special SystemFont variant which contains - //! the specified system font. When the cascade function (in helpers) - //! detects that a value has a system font, it will resolve it, and - //! cache it on the ComputedValues. After this, it can be just fetched - //! whenever a font longhand on the same element needs the system font. - //! - //! When a longhand property is holding a SystemFont, it's serialized - //! to an empty string as if its value comes from a shorthand with - //! variable reference. We may want to improve this behavior at some - //! point. See also https://github.com/w3c/csswg-drafts/issues/1586. +pub mod system_font { + //! We deal with system fonts here + //! + //! System fonts can only be set as a group via the font shorthand. + //! They resolve at compute time (not parse time -- this lets the + //! browser respond to changes to the OS font settings). + //! + //! While Gecko handles these as a separate property and keyword + //! values on each property indicating that the font should be picked + //! from the -x-system-font property, we avoid this. Instead, + //! each font longhand has a special SystemFont variant which contains + //! the specified system font. When the cascade function (in helpers) + //! detects that a value has a system font, it will resolve it, and + //! cache it on the ComputedValues. After this, it can be just fetched + //! whenever a font longhand on the same element needs the system font. + //! + //! When a longhand property is holding a SystemFont, it's serialized + //! to an empty string as if its value comes from a shorthand with + //! variable reference. We may want to improve this behavior at some + //! point. See also https://github.com/w3c/csswg-drafts/issues/1586. - use cssparser::{Parser, ToCss}; - use crate::values::computed::font::GenericFontFamily; - use crate::properties::longhands; - use std::fmt; - use std::hash::{Hash, Hasher}; - use style_traits::ParseError; - use crate::values::computed::{ToComputedValue, Context}; + use crate::values::computed::font::GenericFontFamily; + use crate::properties::longhands; + use std::hash::{Hash, Hasher}; + use crate::values::computed::{ToComputedValue, Context}; + use crate::values::specified::font::SystemFont; - <% - system_fonts = """caption icon menu message-box small-caption status-bar - -moz-window -moz-document -moz-workspace -moz-desktop - -moz-info -moz-dialog -moz-button -moz-pull-down-menu - -moz-list -moz-field""".split() - kw_font_props = """font_variant_caps - font_kerning font_variant_position font_variant_ligatures - font_variant_east_asian font_variant_numeric - font_optical_sizing""".split() - kw_cast = """font_variant_caps font_kerning font_variant_position - font_optical_sizing""".split() - %> - #[derive(Clone, Copy, Debug, Eq, Hash, MallocSizeOf, PartialEq, - SpecifiedValueInfo, ToCss, ToShmem)] - pub enum SystemFont { - % for font in system_fonts: - ${to_camel_case(font)}, - % endfor - } + <% + kw_font_props = """font_variant_caps + font_kerning font_variant_position font_variant_ligatures + font_variant_east_asian font_variant_numeric + font_optical_sizing""".split() + kw_cast = """font_variant_caps font_kerning font_variant_position + font_optical_sizing""".split() + %> - // ComputedValues are compared at times - // so we need these impls. We don't want to - // add Eq to Number (which contains a float) - // so instead we have an eq impl which skips the - // cached values - impl PartialEq for ComputedSystemFont { - fn eq(&self, other: &Self) -> bool { - self.system_font == other.system_font - } - } - impl Eq for ComputedSystemFont {} - - impl Hash for ComputedSystemFont { - fn hash(&self, hasher: &mut H) { - self.system_font.hash(hasher) - } - } - - impl ToComputedValue for SystemFont { - type ComputedValue = ComputedSystemFont; - - fn to_computed_value(&self, cx: &Context) -> Self::ComputedValue { - use crate::gecko_bindings::bindings; - use crate::gecko_bindings::structs::{LookAndFeel_FontID, nsFont}; - use std::mem; - use crate::values::computed::Percentage; - use crate::values::specified::font::KeywordInfo; - use crate::values::computed::font::{FontFamily, FontSize, FontStretch, FontStyle, FontFamilyList}; - use crate::values::generics::NonNegative; - - let id = match *self { - % for font in system_fonts: - SystemFont::${to_camel_case(font)} => { - LookAndFeel_FontID::${to_camel_case(font.replace("-moz-", ""))} - } - % endfor - }; - - let mut system = mem::MaybeUninit::::uninit(); - let system = unsafe { - bindings::Gecko_nsFont_InitSystem( - system.as_mut_ptr(), - id as i32, - cx.style().get_font().gecko(), - cx.device().document() - ); - &mut *system.as_mut_ptr() - }; - let font_weight = longhands::font_weight::computed_value::T::from_gecko_weight(system.weight); - let font_stretch = FontStretch(NonNegative(Percentage(unsafe { - bindings::Gecko_FontStretch_ToFloat(system.stretch) - }))); - let font_style = FontStyle::from_gecko(system.style); - let ret = ComputedSystemFont { - font_family: FontFamily { - families: FontFamilyList::SharedFontList( - unsafe { system.fontlist.mFontlist.mBasePtr.to_safe() } - ), - is_system_font: true, - }, - font_size: FontSize { - size: NonNegative(cx.maybe_zoom_text(system.size.0)), - keyword_info: KeywordInfo::none() - }, - font_weight, - font_stretch, - font_style, - font_size_adjust: longhands::font_size_adjust::computed_value - ::T::from_gecko_adjust(system.sizeAdjust), - % for kwprop in kw_font_props: - ${kwprop}: longhands::${kwprop}::computed_value::T::from_gecko_keyword( - system.${to_camel_case_lower(kwprop.replace('font_', ''))} - % if kwprop in kw_cast: - as u32 - % endif - ), - % endfor - font_language_override: longhands::font_language_override::computed_value - ::T(system.languageOverride), - font_feature_settings: longhands::font_feature_settings::get_initial_value(), - font_variation_settings: longhands::font_variation_settings::get_initial_value(), - font_variant_alternates: longhands::font_variant_alternates::get_initial_value(), - system_font: *self, - default_font_type: system.fontlist.mDefaultFontType, - }; - unsafe { bindings::Gecko_nsFont_Destroy(system); } - ret - } - - fn from_computed_value(_: &ComputedSystemFont) -> Self { - unreachable!() - } - } - - #[inline] - /// Compute and cache a system font - /// - /// Must be called before attempting to compute a system font - /// specified value - pub fn resolve_system_font(system: SystemFont, context: &mut Context) { - // Checking if context.cached_system_font.is_none() isn't enough, - // if animating from one system font to another the cached system font - // may change - if Some(system) != context.cached_system_font.as_ref().map(|x| x.system_font) { - let computed = system.to_computed_value(context); - context.cached_system_font = Some(computed); - } - } - - #[derive(Clone, Debug)] - pub struct ComputedSystemFont { - % for name in SYSTEM_FONT_LONGHANDS: - pub ${name}: longhands::${name}::computed_value::T, - % endfor - pub system_font: SystemFont, - pub default_font_type: GenericFontFamily, - } - - impl SystemFont { - pub fn parse<'i, 't>(input: &mut Parser<'i, 't>) -> Result> { - try_match_ident_ignore_ascii_case! { input, - % for font in system_fonts: - "${font}" => Ok(SystemFont::${to_camel_case(font)}), - % endfor - } - } - } - - impl ToCss for SystemFont { - fn to_css(&self, dest: &mut W) -> fmt::Result where W: fmt::Write { - // We may want to do something better in the future, see - // w3c/csswg-drafts#1586. - dest.write_str("-moz-use-system-font") - } + // ComputedValues are compared at times + // so we need these impls. We don't want to + // add Eq to Number (which contains a float) + // so instead we have an eq impl which skips the + // cached values + impl PartialEq for ComputedSystemFont { + fn eq(&self, other: &Self) -> bool { + self.system_font == other.system_font } } -% else: - pub mod system_font { - use cssparser::Parser; + impl Eq for ComputedSystemFont {} - // We don't parse system fonts, but in the interest of not littering - // a lot of code with `if engine == "gecko"` conditionals, we have a - // dummy system font module that does nothing - - #[derive(Clone, Copy, Debug, Eq, Hash, MallocSizeOf, PartialEq, SpecifiedValueInfo, ToCss, ToShmem)] - /// void enum for system font, can never exist - pub enum SystemFont {} - impl SystemFont { - pub fn parse(_: &mut Parser) -> Result { - Err(()) - } + impl Hash for ComputedSystemFont { + fn hash(&self, hasher: &mut H) { + self.system_font.hash(hasher) } } + + impl ToComputedValue for SystemFont { + type ComputedValue = ComputedSystemFont; + + fn to_computed_value(&self, cx: &Context) -> Self::ComputedValue { + use crate::gecko_bindings::bindings; + use crate::gecko_bindings::structs::nsFont; + use std::mem; + use crate::values::computed::Percentage; + use crate::values::specified::font::KeywordInfo; + use crate::values::computed::font::{FontFamily, FontSize, FontStretch, FontStyle, FontFamilyList}; + use crate::values::generics::NonNegative; + + let mut system = mem::MaybeUninit::::uninit(); + let system = unsafe { + bindings::Gecko_nsFont_InitSystem( + system.as_mut_ptr(), + *self, + cx.style().get_font().gecko(), + cx.device().document() + ); + &mut *system.as_mut_ptr() + }; + let font_weight = longhands::font_weight::computed_value::T::from_gecko_weight(system.weight); + let font_stretch = FontStretch(NonNegative(Percentage(unsafe { + bindings::Gecko_FontStretch_ToFloat(system.stretch) + }))); + let font_style = FontStyle::from_gecko(system.style); + let ret = ComputedSystemFont { + font_family: FontFamily { + families: FontFamilyList::SharedFontList( + unsafe { system.fontlist.mFontlist.mBasePtr.to_safe() } + ), + is_system_font: true, + }, + font_size: FontSize { + size: NonNegative(cx.maybe_zoom_text(system.size.0)), + keyword_info: KeywordInfo::none() + }, + font_weight, + font_stretch, + font_style, + font_size_adjust: longhands::font_size_adjust::computed_value + ::T::from_gecko_adjust(system.sizeAdjust), + % for kwprop in kw_font_props: + ${kwprop}: longhands::${kwprop}::computed_value::T::from_gecko_keyword( + system.${to_camel_case_lower(kwprop.replace('font_', ''))} + % if kwprop in kw_cast: + as u32 + % endif + ), + % endfor + font_language_override: longhands::font_language_override::computed_value + ::T(system.languageOverride), + font_feature_settings: longhands::font_feature_settings::get_initial_value(), + font_variation_settings: longhands::font_variation_settings::get_initial_value(), + font_variant_alternates: longhands::font_variant_alternates::get_initial_value(), + system_font: *self, + default_font_type: system.fontlist.mDefaultFontType, + }; + unsafe { bindings::Gecko_nsFont_Destroy(system); } + ret + } + + fn from_computed_value(_: &ComputedSystemFont) -> Self { + unreachable!() + } + } + + #[inline] + /// Compute and cache a system font + /// + /// Must be called before attempting to compute a system font + /// specified value + pub fn resolve_system_font(system: SystemFont, context: &mut Context) { + // Checking if context.cached_system_font.is_none() isn't enough, + // if animating from one system font to another the cached system font + // may change + if Some(system) != context.cached_system_font.as_ref().map(|x| x.system_font) { + let computed = system.to_computed_value(context); + context.cached_system_font = Some(computed); + } + } + + #[derive(Clone, Debug)] + pub struct ComputedSystemFont { + % for name in SYSTEM_FONT_LONGHANDS: + pub ${name}: longhands::${name}::computed_value::T, + % endfor + pub system_font: SystemFont, + pub default_font_type: GenericFontFamily, + } + +} % endif ${helpers.single_keyword( diff --git a/components/style/properties/properties.mako.rs b/components/style/properties/properties.mako.rs index b27831a5785..ea3e043ea49 100644 --- a/components/style/properties/properties.mako.rs +++ b/components/style/properties/properties.mako.rs @@ -32,7 +32,6 @@ use crate::computed_value_flags::*; use crate::hash::FxHashMap; use crate::media_queries::Device; use crate::parser::ParserContext; -use crate::properties::longhands::system_font::SystemFont; use crate::selector_parser::PseudoElement; #[cfg(feature = "servo")] use servo_config::prefs; use style_traits::{CssWriter, KeywordsCollectFn, ParseError, ParsingMode}; @@ -44,6 +43,7 @@ use crate::values::generics::text::LineHeight; use crate::values::{computed, resolved}; use crate::values::computed::NonNegativeLength; use crate::values::serialize_atom_name; +use crate::values::specified::font::SystemFont; use crate::rule_tree::StrongRuleNode; use crate::Zero; use crate::str::{CssString, CssStringWriter}; diff --git a/components/style/properties/shorthands/font.mako.rs b/components/style/properties/shorthands/font.mako.rs index 2ed36ae88a5..99caaa0ca43 100644 --- a/components/style/properties/shorthands/font.mako.rs +++ b/components/style/properties/shorthands/font.mako.rs @@ -34,11 +34,11 @@ use crate::parser::Parse; use crate::properties::longhands::{font_family, font_style, font_weight, font_stretch}; use crate::properties::longhands::font_variant_caps; - #[cfg(feature = "gecko")] - use crate::properties::longhands::system_font::SystemFont; use crate::values::specified::text::LineHeight; use crate::values::specified::FontSize; use crate::values::specified::font::{FontStretch, FontStretchKeyword}; + #[cfg(feature = "gecko")] + use crate::values::specified::font::SystemFont; <% gecko_sub_properties = "kerning language_override size_adjust \ @@ -289,7 +289,7 @@ % for p in subprops_for_value_info: ${p}::collect_completion_keywords(f); % endfor - ::collect_completion_keywords(f); + ::collect_completion_keywords(f); } } diff --git a/components/style/values/specified/font.rs b/components/style/values/specified/font.rs index 6e43c34d53b..b536ddf3e21 100644 --- a/components/style/values/specified/font.rs +++ b/components/style/values/specified/font.rs @@ -9,7 +9,6 @@ use crate::context::QuirksMode; #[cfg(feature = "gecko")] use crate::gecko_bindings::bindings; use crate::parser::{Parse, ParserContext}; -use crate::properties::longhands::system_font::SystemFont; use crate::values::computed::font::{FamilyName, FontFamilyList, FontStyleAngle, SingleFontFamily}; use crate::values::computed::{font as computed, Length, NonNegativeLength}; use crate::values::computed::{Angle as ComputedAngle, Percentage as ComputedPercentage}; @@ -65,6 +64,33 @@ macro_rules! system_font_methods { }; } +/// System fonts. +#[repr(u8)] +#[derive( + Clone, Copy, Debug, Eq, Hash, MallocSizeOf, Parse, PartialEq, SpecifiedValueInfo, ToCss, ToShmem +)] +#[allow(missing_docs)] +pub enum SystemFont { + Caption, + Icon, + Menu, + MessageBox, + SmallCaption, + StatusBar, + MozWindow, + MozDocument, + MozWorkspace, + MozDesktop, + MozInfo, + MozDialog, + MozButton, + MozPullDownMenu, + MozList, + MozField, + #[css(skip)] + End, // Just for indexing purposes. +} + const DEFAULT_SCRIPT_MIN_SIZE_PT: u32 = 8; const DEFAULT_SCRIPT_SIZE_MULTIPLIER: f64 = 0.71; From 30245160198c7f17dcf77a5cbb2117cab3f97b9f Mon Sep 17 00:00:00 2001 From: Oriol Brufau Date: Tue, 16 May 2023 14:12:14 +0200 Subject: [PATCH 30/65] Further changes required by Servo --- .../style/properties/shorthands/font.mako.rs | 2 ++ components/style/values/specified/font.rs | 21 +++++++++++++++++++ 2 files changed, 23 insertions(+) diff --git a/components/style/properties/shorthands/font.mako.rs b/components/style/properties/shorthands/font.mako.rs index 99caaa0ca43..26205c2ea9c 100644 --- a/components/style/properties/shorthands/font.mako.rs +++ b/components/style/properties/shorthands/font.mako.rs @@ -289,7 +289,9 @@ % for p in subprops_for_value_info: ${p}::collect_completion_keywords(f); % endfor + % if engine == "gecko": ::collect_completion_keywords(f); + % endif } } diff --git a/components/style/values/specified/font.rs b/components/style/values/specified/font.rs index b536ddf3e21..329dedd31ca 100644 --- a/components/style/values/specified/font.rs +++ b/components/style/values/specified/font.rs @@ -70,6 +70,7 @@ macro_rules! system_font_methods { Clone, Copy, Debug, Eq, Hash, MallocSizeOf, Parse, PartialEq, SpecifiedValueInfo, ToCss, ToShmem )] #[allow(missing_docs)] +#[cfg(feature = "gecko")] pub enum SystemFont { Caption, Icon, @@ -91,6 +92,26 @@ pub enum SystemFont { End, // Just for indexing purposes. } +// We don't parse system fonts in servo, but in the interest of not +// littering a lot of code with `if engine == "gecko"` conditionals, +// we have a dummy system font module that does nothing + +#[derive( + Clone, Copy, Debug, Eq, Hash, MallocSizeOf, PartialEq, SpecifiedValueInfo, ToCss, ToShmem +)] +#[allow(missing_docs)] +#[cfg(feature = "servo")] +/// void enum for system font, can never exist +pub enum SystemFont {} + +#[allow(missing_docs)] +#[cfg(feature = "servo")] +impl SystemFont { + pub fn parse(_: &mut Parser) -> Result { + Err(()) + } +} + const DEFAULT_SCRIPT_MIN_SIZE_PT: u32 = 8; const DEFAULT_SCRIPT_SIZE_MULTIPLIER: f64 = 0.71; From 4b973885a3d16c007e99d0f51ea0f507b64b276f Mon Sep 17 00:00:00 2001 From: Oriol Brufau Date: Tue, 16 May 2023 22:56:28 +0200 Subject: [PATCH 31/65] style: Add the size-adjust descriptor to the style system Differential Revision: https://phabricator.services.mozilla.com/D110022 --- components/style/font_face.rs | 8 +++++++- components/style/values/specified/percentage.rs | 8 ++++++++ 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/components/style/font_face.rs b/components/style/font_face.rs index 901bafe5101..f7c95c10323 100644 --- a/components/style/font_face.rs +++ b/components/style/font_face.rs @@ -23,7 +23,7 @@ use crate::values::specified::font::{AbsoluteFontWeight, FontStretch}; #[cfg(feature = "gecko")] use crate::values::specified::font::MetricsOverride; use crate::values::specified::url::SpecifiedUrl; -use crate::values::specified::Angle; +use crate::values::specified::{Angle, NonNegativePercentage}; #[cfg(feature = "gecko")] use cssparser::UnicodeRange; use cssparser::{AtRuleParser, DeclarationListParser, DeclarationParser, Parser}; @@ -429,6 +429,9 @@ macro_rules! is_descriptor_enabled { ("line-gap-override") => { static_prefs::pref!("layout.css.font-metrics-overrides.enabled") }; + ("size-adjust") => { + static_prefs::pref!("layout.css.size-adjust.enabled") + }; ($name:tt) => { true }; @@ -596,6 +599,9 @@ font_face_descriptors! { /// The line-gap override for this font face. "line-gap-override" line_gap_override / mLineGapOverride: MetricsOverride, + + /// The size adjustment for this font face. + "size-adjust" size_adjust / mSizeAdjust: NonNegativePercentage, ] } diff --git a/components/style/values/specified/percentage.rs b/components/style/values/specified/percentage.rs index c73616a3848..a152f7d8dc0 100644 --- a/components/style/values/specified/percentage.rs +++ b/components/style/values/specified/percentage.rs @@ -186,3 +186,11 @@ impl Parse for NonNegativePercentage { Ok(NonNegative(Percentage::parse_non_negative(context, input)?)) } } + +impl NonNegativePercentage { + /// Convert to ComputedPercentage, for FontFaceRule size-adjust getter. + #[inline] + pub fn compute(&self) -> ComputedPercentage { + ComputedPercentage(self.0.get()) + } +} From 8a16ba74ec22efbbe718dbba370a2417b4d617d3 Mon Sep 17 00:00:00 2001 From: Oriol Brufau Date: Tue, 16 May 2023 22:58:29 +0200 Subject: [PATCH 32/65] Further changes required by Servo --- components/style/font_face.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/components/style/font_face.rs b/components/style/font_face.rs index f7c95c10323..5016f91182c 100644 --- a/components/style/font_face.rs +++ b/components/style/font_face.rs @@ -23,7 +23,9 @@ use crate::values::specified::font::{AbsoluteFontWeight, FontStretch}; #[cfg(feature = "gecko")] use crate::values::specified::font::MetricsOverride; use crate::values::specified::url::SpecifiedUrl; -use crate::values::specified::{Angle, NonNegativePercentage}; +use crate::values::specified::Angle; +#[cfg(feature = "gecko")] +use crate::values::specified::NonNegativePercentage; #[cfg(feature = "gecko")] use cssparser::UnicodeRange; use cssparser::{AtRuleParser, DeclarationListParser, DeclarationParser, Parser}; From 7c26fb82b172c93012da8cf645ec1f1ad5f4bccf Mon Sep 17 00:00:00 2001 From: Oriol Brufau Date: Tue, 16 May 2023 23:00:33 +0200 Subject: [PATCH 33/65] style: add an appearance type that forces a dropshadow on menus on Windows, and uses SetWindowRgn to clip the popup to the border radius Differential Revision: https://phabricator.services.mozilla.com/D109833 --- components/style/properties/longhands/ui.mako.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/style/properties/longhands/ui.mako.rs b/components/style/properties/longhands/ui.mako.rs index 1dfdfcc1a73..400eaedf577 100644 --- a/components/style/properties/longhands/ui.mako.rs +++ b/components/style/properties/longhands/ui.mako.rs @@ -53,7 +53,7 @@ ${helpers.single_keyword( ${helpers.single_keyword( "-moz-window-shadow", - "default none menu tooltip sheet", + "default none menu tooltip sheet cliprounded", engines="gecko", gecko_ffi_name="mWindowShadow", gecko_enum_prefix="StyleWindowShadow", From 3d9eabcccb49e91e6793fa9e6917b89381dd4afe Mon Sep 17 00:00:00 2001 From: Oriol Brufau Date: Tue, 16 May 2023 23:01:29 +0200 Subject: [PATCH 34/65] style: Convert proton pref checks from @supports rules to media features This means that dynamic changes will be handled correctly, we can use StaticPrefs, etc. Differential Revision: https://phabricator.services.mozilla.com/D110816 --- components/style/gecko/media_features.rs | 35 +++++++++++++++++++++++- 1 file changed, 34 insertions(+), 1 deletion(-) diff --git a/components/style/gecko/media_features.rs b/components/style/gecko/media_features.rs index bfc78b84b15..c76968be5b0 100644 --- a/components/style/gecko/media_features.rs +++ b/components/style/gecko/media_features.rs @@ -584,6 +584,31 @@ macro_rules! system_metric_feature { ) } + feature!( + $feature_name, + AllowsRanges::No, + Evaluator::BoolInteger(__eval), + ParsingRequirements::CHROME_AND_UA_ONLY, + ) + }} +} + +/// bool pref-based features are an slightly less convenient to start using +/// version of @supports -moz-bool-pref, but with some benefits, mainly that +/// they can support dynamic changes, and don't require a pref lookup every time +/// they're used. +/// +/// In order to use them you need to make sure that the pref defined as a static +/// pref, with `rust: true`. The feature name needs to be defined in +/// `StaticAtoms.py` just like the others. In order to support dynamic changes, +/// you also need to add them to kBoolMediaQueryPrefs in nsXPLookAndFeel.cpp +macro_rules! bool_pref_feature { + ($feature_name:expr, $pref:tt) => {{ + fn __eval(_: &Device, query_value: Option, _: Option) -> bool { + let value = static_prefs::pref!($pref); + query_value.map_or(value, |v| v == value) + } + feature!( $feature_name, AllowsRanges::No, @@ -598,7 +623,7 @@ macro_rules! system_metric_feature { /// to support new types in these entries and (2) ensuring that either /// nsPresContext::MediaFeatureValuesChanged is called when the value that /// would be returned by the evaluator function could change. -pub static MEDIA_FEATURES: [MediaFeatureDescription; 56] = [ +pub static MEDIA_FEATURES: [MediaFeatureDescription; 63] = [ feature!( atom!("width"), AllowsRanges::Yes, @@ -831,4 +856,12 @@ pub static MEDIA_FEATURES: [MediaFeatureDescription; 56] = [ system_metric_feature!(atom!("-moz-gtk-csd-close-button")), system_metric_feature!(atom!("-moz-gtk-csd-reversed-placement")), system_metric_feature!(atom!("-moz-system-dark-theme")), + + bool_pref_feature!(atom!("-moz-proton"), "browser.proton.enabled"), + bool_pref_feature!(atom!("-moz-proton-urlbar"), "browser.proton.urlbar.enabled"), + bool_pref_feature!(atom!("-moz-proton-modals"), "browser.proton.modals.enabled"), + bool_pref_feature!(atom!("-moz-proton-contextmenus"), "browser.proton.contextmenus.enabled"), + bool_pref_feature!(atom!("-moz-proton-doorhangers"), "browser.proton.doorhangers.enabled"), + bool_pref_feature!(atom!("-moz-proton-infobars"), "browser.proton.infobars.enabled"), + bool_pref_feature!(atom!("-moz-proton-places-tooltip"), "browser.proton.places-tooltip.enabled"), ]; From f44a6909df18ecce2f45eadc73aa31cb33043378 Mon Sep 17 00:00:00 2001 From: Oriol Brufau Date: Tue, 16 May 2023 23:02:50 +0200 Subject: [PATCH 35/65] style: Reduce boilerplate needed to add system-metric media features There's no reason we can't just query LookAndFeel and we need to use sSystemMetrics. In the past, LookAndFeel queries were not cached, but this is no longer the case, so perf wise should be pretty equivalent. Note that we don't need the NS_SUCCEEDED checks because the default value from GetInt if the platform doesn't support it is 0 anyways. Differential Revision: https://phabricator.services.mozilla.com/D110805 --- components/style/gecko/media_features.rs | 106 ++++++++++++----------- 1 file changed, 57 insertions(+), 49 deletions(-) diff --git a/components/style/gecko/media_features.rs b/components/style/gecko/media_features.rs index c76968be5b0..a47783e5c01 100644 --- a/components/style/gecko/media_features.rs +++ b/components/style/gecko/media_features.rs @@ -541,22 +541,6 @@ fn eval_moz_is_resource_document( query_value.map_or(is_resource_doc, |v| v == is_resource_doc) } -fn eval_system_metric( - device: &Device, - query_value: Option, - metric: Atom, - accessible_from_content: bool, -) -> bool { - let supports_metric = unsafe { - bindings::Gecko_MediaFeatures_HasSystemMetric( - device.document(), - metric.as_ptr(), - accessible_from_content, - ) - }; - query_value.map_or(supports_metric, |v| v == supports_metric) -} - fn eval_moz_os_version( device: &Device, query_value: Option, @@ -573,15 +557,35 @@ fn eval_moz_os_version( query_value.as_ptr() == os_version } -macro_rules! system_metric_feature { - ($feature_name:expr) => {{ - fn __eval(device: &Device, query_value: Option, _: Option) -> bool { - eval_system_metric( - device, - query_value, - $feature_name, - /* accessible_from_content = */ false, - ) +fn get_lnf_int(int_id: i32) -> i32 { + unsafe { bindings::Gecko_GetLookAndFeelInt(int_id) } +} + +fn get_lnf_int_as_bool(int_id: i32) -> bool { + get_lnf_int(int_id) != 0 +} + +fn get_scrollbar_start_backward(int_id: i32) -> bool { + (get_lnf_int(int_id) & bindings::LookAndFeel_eScrollArrow_StartBackward as i32) != 0 +} + +fn get_scrollbar_start_forward(int_id: i32) -> bool { + (get_lnf_int(int_id) & bindings::LookAndFeel_eScrollArrow_StartForward as i32) != 0 +} + +fn get_scrollbar_end_backward(int_id: i32) -> bool { + (get_lnf_int(int_id) & bindings::LookAndFeel_eScrollArrow_EndBackward as i32) != 0 +} + +fn get_scrollbar_end_forward(int_id: i32) -> bool { + (get_lnf_int(int_id) & bindings::LookAndFeel_eScrollArrow_EndForward as i32) != 0 +} + +macro_rules! lnf_int_feature { + ($feature_name:expr, $int_id:ident, $get_value:ident) => {{ + fn __eval(_: &Device, query_value: Option, _: Option) -> bool { + let value = $get_value(bindings::LookAndFeel_IntID::$int_id as i32); + query_value.map_or(value, |v| v == value) } feature!( @@ -590,7 +594,10 @@ macro_rules! system_metric_feature { Evaluator::BoolInteger(__eval), ParsingRequirements::CHROME_AND_UA_ONLY, ) - }} + }}; + ($feature_name:expr, $int_id:ident) => {{ + lnf_int_feature!($feature_name, $int_id, get_lnf_int_as_bool) + }}; } /// bool pref-based features are an slightly less convenient to start using @@ -833,29 +840,30 @@ pub static MEDIA_FEATURES: [MediaFeatureDescription; 63] = [ Evaluator::BoolInteger(eval_moz_non_native_content_theme), ParsingRequirements::CHROME_AND_UA_ONLY, ), - system_metric_feature!(atom!("-moz-scrollbar-start-backward")), - system_metric_feature!(atom!("-moz-scrollbar-start-forward")), - system_metric_feature!(atom!("-moz-scrollbar-end-backward")), - system_metric_feature!(atom!("-moz-scrollbar-end-forward")), - system_metric_feature!(atom!("-moz-scrollbar-thumb-proportional")), - system_metric_feature!(atom!("-moz-overlay-scrollbars")), - system_metric_feature!(atom!("-moz-windows-default-theme")), - system_metric_feature!(atom!("-moz-mac-graphite-theme")), - system_metric_feature!(atom!("-moz-mac-big-sur-theme")), - system_metric_feature!(atom!("-moz-windows-accent-color-in-titlebar")), - system_metric_feature!(atom!("-moz-windows-compositor")), - system_metric_feature!(atom!("-moz-windows-classic")), - system_metric_feature!(atom!("-moz-windows-glass")), - system_metric_feature!(atom!("-moz-menubar-drag")), - system_metric_feature!(atom!("-moz-swipe-animation-enabled")), - system_metric_feature!(atom!("-moz-gtk-csd-available")), - system_metric_feature!(atom!("-moz-gtk-csd-hide-titlebar-by-default")), - system_metric_feature!(atom!("-moz-gtk-csd-transparent-background")), - system_metric_feature!(atom!("-moz-gtk-csd-minimize-button")), - system_metric_feature!(atom!("-moz-gtk-csd-maximize-button")), - system_metric_feature!(atom!("-moz-gtk-csd-close-button")), - system_metric_feature!(atom!("-moz-gtk-csd-reversed-placement")), - system_metric_feature!(atom!("-moz-system-dark-theme")), + + lnf_int_feature!(atom!("-moz-scrollbar-start-backward"), ScrollArrowStyle, get_scrollbar_start_backward), + lnf_int_feature!(atom!("-moz-scrollbar-start-forward"), ScrollArrowStyle, get_scrollbar_start_forward), + lnf_int_feature!(atom!("-moz-scrollbar-end-backward"), ScrollArrowStyle, get_scrollbar_end_backward), + lnf_int_feature!(atom!("-moz-scrollbar-end-forward"), ScrollArrowStyle, get_scrollbar_end_forward), + lnf_int_feature!(atom!("-moz-scrollbar-thumb-proportional"), ScrollSliderStyle), + lnf_int_feature!(atom!("-moz-overlay-scrollbars"), UseOverlayScrollbars), + lnf_int_feature!(atom!("-moz-menubar-drag"), MenuBarDrag), + lnf_int_feature!(atom!("-moz-windows-default-theme"), WindowsDefaultTheme), + lnf_int_feature!(atom!("-moz-mac-graphite-theme"), MacGraphiteTheme), + lnf_int_feature!(atom!("-moz-mac-big-sur-theme"), MacBigSurTheme), + lnf_int_feature!(atom!("-moz-windows-accent-color-in-titlebar"), WindowsAccentColorInTitlebar), + lnf_int_feature!(atom!("-moz-windows-compositor"), DWMCompositor), + lnf_int_feature!(atom!("-moz-windows-classic"), WindowsClassic), + lnf_int_feature!(atom!("-moz-windows-glass"), WindowsGlass), + lnf_int_feature!(atom!("-moz-swipe-animation-enabled"), SwipeAnimationEnabled), + lnf_int_feature!(atom!("-moz-gtk-csd-available"), GTKCSDAvailable), + lnf_int_feature!(atom!("-moz-gtk-csd-hide-titlebar-by-default"), GTKCSDHideTitlebarByDefault), + lnf_int_feature!(atom!("-moz-gtk-csd-transparent-background"), GTKCSDTransparentBackground), + lnf_int_feature!(atom!("-moz-gtk-csd-minimize-button"), GTKCSDMinimizeButton), + lnf_int_feature!(atom!("-moz-gtk-csd-maximize-button"), GTKCSDMaximizeButton), + lnf_int_feature!(atom!("-moz-gtk-csd-close-button"), GTKCSDCloseButton), + lnf_int_feature!(atom!("-moz-gtk-csd-reversed-placement"), GTKCSDReversedPlacement), + lnf_int_feature!(atom!("-moz-system-dark-theme"), SystemUsesDarkTheme), bool_pref_feature!(atom!("-moz-proton"), "browser.proton.enabled"), bool_pref_feature!(atom!("-moz-proton-urlbar"), "browser.proton.urlbar.enabled"), From 3da3d76a08b4c3999317359a78433e417f32c027 Mon Sep 17 00:00:00 2001 From: Oriol Brufau Date: Tue, 16 May 2023 23:03:45 +0200 Subject: [PATCH 36/65] style: Clean up NPAPI plugin fallback behavior Similifies use of EventStates and ObjectType/FallbackType enums since most states they represented are no longer valid with the removal of NPAPI plugins. The state machine for (unsupported) plugin elements is now much simpler but still distinguishes between HTML fallbacks, fallbacks leading to a "BROKEN" state (e.g. failing to load the image the element refers to), and fallbacks that would simply lead the element to occupy an empty region. The last type of fallback is behind a pref "layout.use-plugin-fallback" and is disabled by default. Simplifying the state machine allows us to clean up nsObjectLoadingContent. We also update many of the enums which refered to plugins, which would otherwise get confusing. Differential Revision: https://phabricator.services.mozilla.com/D107158 --- components/style/element_state.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/components/style/element_state.rs b/components/style/element_state.rs index 0852b4574fa..853d4039b2a 100644 --- a/components/style/element_state.rs +++ b/components/style/element_state.rs @@ -132,8 +132,6 @@ bitflags! { const IN_MOZINERT_STATE = 1 << 54; /// State for the topmost dialog element in top layer const IN_TOPMOST_MODAL_DIALOG_STATE = 1 << 55; - /// Non-standard & undocumented. - const IN_HANDLER_NOPLUGINS = 1 << 56; } } From 7bb15f19f1956f5cb81a24358aea379beb64a1ef Mon Sep 17 00:00:00 2001 From: Oriol Brufau Date: Tue, 16 May 2023 23:12:19 +0200 Subject: [PATCH 37/65] style: Remove dead prefers-color-scheme: no preference code It was removed in bug 1643656. Differential Revision: https://phabricator.services.mozilla.com/D111461 --- components/style/gecko/media_features.rs | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/components/style/gecko/media_features.rs b/components/style/gecko/media_features.rs index a47783e5c01..62d674aa318 100644 --- a/components/style/gecko/media_features.rs +++ b/components/style/gecko/media_features.rs @@ -275,10 +275,6 @@ enum PrefersReducedMotion { Reduce, } -fn color_scheme_no_preference_enabled(_: &crate::parser::ParserContext) -> bool { - static_prefs::pref!("layout.css.prefers-color-scheme-no-preference.enabled") -} - /// Values for the prefers-color-scheme media feature. #[derive(Clone, Copy, Debug, FromPrimitive, Parse, PartialEq, ToCss)] #[repr(u8)] @@ -286,8 +282,6 @@ fn color_scheme_no_preference_enabled(_: &crate::parser::ParserContext) -> bool pub enum PrefersColorScheme { Light, Dark, - #[parse(condition = "color_scheme_no_preference_enabled")] - NoPreference, } /// https://drafts.csswg.org/mediaqueries-5/#prefers-reduced-motion @@ -408,7 +402,7 @@ fn eval_prefers_color_scheme(device: &Device, query_value: Option prefers_color_scheme == v, - None => prefers_color_scheme != PrefersColorScheme::NoPreference, + None => true, } } From bbc03a25775e062265c9197df82956e9d9615174 Mon Sep 17 00:00:00 2001 From: Oriol Brufau Date: Tue, 16 May 2023 23:13:14 +0200 Subject: [PATCH 38/65] style: Enable Proton Urlbar Differential Revision: https://phabricator.services.mozilla.com/D110998 --- components/style/gecko/media_features.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/components/style/gecko/media_features.rs b/components/style/gecko/media_features.rs index 62d674aa318..a3000f46bb4 100644 --- a/components/style/gecko/media_features.rs +++ b/components/style/gecko/media_features.rs @@ -624,7 +624,7 @@ macro_rules! bool_pref_feature { /// to support new types in these entries and (2) ensuring that either /// nsPresContext::MediaFeatureValuesChanged is called when the value that /// would be returned by the evaluator function could change. -pub static MEDIA_FEATURES: [MediaFeatureDescription; 63] = [ +pub static MEDIA_FEATURES: [MediaFeatureDescription; 62] = [ feature!( atom!("width"), AllowsRanges::Yes, @@ -860,7 +860,6 @@ pub static MEDIA_FEATURES: [MediaFeatureDescription; 63] = [ lnf_int_feature!(atom!("-moz-system-dark-theme"), SystemUsesDarkTheme), bool_pref_feature!(atom!("-moz-proton"), "browser.proton.enabled"), - bool_pref_feature!(atom!("-moz-proton-urlbar"), "browser.proton.urlbar.enabled"), bool_pref_feature!(atom!("-moz-proton-modals"), "browser.proton.modals.enabled"), bool_pref_feature!(atom!("-moz-proton-contextmenus"), "browser.proton.contextmenus.enabled"), bool_pref_feature!(atom!("-moz-proton-doorhangers"), "browser.proton.doorhangers.enabled"), From b40f4b6fec68d95ff97eff6d0bd2a2e57c12ac95 Mon Sep 17 00:00:00 2001 From: Oriol Brufau Date: Tue, 16 May 2023 23:14:19 +0200 Subject: [PATCH 39/65] style: Clean up text-justify, and make distribute a parse-time alias Since it's simpler, as discussed in the CSSWG issue. Differential Revision: https://phabricator.services.mozilla.com/D111346 --- components/style/properties/data.py | 1 + components/style/properties/helpers.mako.rs | 45 ++++--------------- .../longhands/inherited_text.mako.rs | 45 ++++--------------- components/style/values/computed/mod.rs | 2 +- components/style/values/computed/text.rs | 2 +- components/style/values/specified/mod.rs | 2 +- components/style/values/specified/text.rs | 27 +++++++++++ 7 files changed, 48 insertions(+), 76 deletions(-) diff --git a/components/style/properties/data.py b/components/style/properties/data.py index dd64f799fdd..559048913b6 100644 --- a/components/style/properties/data.py +++ b/components/style/properties/data.py @@ -511,6 +511,7 @@ class Longhand(Property): "TextAlignLast", "TextDecorationLine", "TextEmphasisPosition", + "TextJustify", "TextTransform", "TextUnderlinePosition", "TouchAction", diff --git a/components/style/properties/helpers.mako.rs b/components/style/properties/helpers.mako.rs index c3987874dc1..74161793e4e 100644 --- a/components/style/properties/helpers.mako.rs +++ b/components/style/properties/helpers.mako.rs @@ -707,8 +707,8 @@ <%def name="single_keyword(name, values, vector=False, - extra_specified=None, needs_conversion=False, - gecko_pref_controlled_initial_value=None, **kwargs)"> + needs_conversion=False, gecko_pref_controlled_initial_value=None, + **kwargs)"> <% keyword_kwargs = {a: kwargs.pop(a, None) for a in [ 'gecko_constant_prefix', @@ -725,11 +725,10 @@ ]} %> - <%def name="inner_body(keyword, extra_specified=None, needs_conversion=False, + <%def name="inner_body(keyword, needs_conversion=False, gecko_pref_controlled_initial_value=None)"> - <%def name="variants(variants, include_aliases)"> + <%def name="variants(variants)"> % for variant in variants: - % if include_aliases: <% aliases = [] for alias, v in keyword.aliases_for(engine).items(): @@ -739,38 +738,15 @@ % if aliases: #[parse(aliases = "${','.join(sorted(aliases))}")] % endif - % endif ${to_camel_case(variant)}, % endfor - % if extra_specified: - #[cfg_attr(feature = "servo", derive(Deserialize, Serialize))] - #[derive( - Clone, - Copy, - Debug, - Eq, - MallocSizeOf, - Parse, - PartialEq, - SpecifiedValueInfo, - ToCss, - ToShmem, - )] - pub enum SpecifiedValue { - ${variants(keyword.values_for(engine) + extra_specified.split(), bool(extra_specified))} - } - % else: - pub use self::computed_value::T as SpecifiedValue; - % endif + pub use self::computed_value::T as SpecifiedValue; pub mod computed_value { #[cfg_attr(feature = "servo", derive(Deserialize, Serialize))] - #[derive(Clone, Copy, Debug, Eq, FromPrimitive, MallocSizeOf, PartialEq, ToCss, ToResolvedValue)] - % if not extra_specified: - #[derive(Parse, SpecifiedValueInfo, ToComputedValue, ToShmem)] - % endif + #[derive(Clone, Copy, Debug, Eq, FromPrimitive, MallocSizeOf, Parse, PartialEq, SpecifiedValueInfo, ToComputedValue, ToCss, ToResolvedValue, ToShmem)] pub enum T { - ${variants(data.longhands_by_name[name].keyword.values_for(engine), not extra_specified)} + ${variants(data.longhands_by_name[name].keyword.values_for(engine))} } } #[inline] @@ -799,10 +775,7 @@ % if needs_conversion: <% - conversion_values = keyword.values_for(engine) - if extra_specified: - conversion_values += extra_specified.split() - conversion_values += keyword.aliases_for(engine).keys() + conversion_values = keyword.values_for(engine) + list(keyword.aliases_for(engine).keys()) %> ${gecko_keyword_conversion(keyword, values=conversion_values)} % endif @@ -817,7 +790,7 @@ % else: <%call expr="longhand(name, keyword=Keyword(name, values, **keyword_kwargs), **kwargs)"> ${inner_body(Keyword(name, values, **keyword_kwargs), - extra_specified=extra_specified, needs_conversion=needs_conversion, + needs_conversion=needs_conversion, gecko_pref_controlled_initial_value=gecko_pref_controlled_initial_value)} % if caller: ${caller.body()} diff --git a/components/style/properties/longhands/inherited_text.mako.rs b/components/style/properties/longhands/inherited_text.mako.rs index 6e6bd79c0e6..b0407054ddd 100644 --- a/components/style/properties/longhands/inherited_text.mako.rs +++ b/components/style/properties/longhands/inherited_text.mako.rs @@ -96,45 +96,16 @@ ${helpers.predefined_type( servo_restyle_damage="rebuild_and_reflow", )} -// TODO(pcwalton): Support `text-justify: distribute`. -<%helpers:single_keyword - name="text-justify" - values="auto none inter-word" +${helpers.predefined_type( + "text-justify", + "TextJustify", + "computed::TextJustify::Auto", engines="gecko servo-2013 servo-2020", servo_2020_pref="layout.2020.unimplemented", - extra_gecko_values="inter-character" - extra_specified="${'distribute' if engine == 'gecko' else ''}" - gecko_enum_prefix="StyleTextJustify" - animation_value_type="discrete" - spec="https://drafts.csswg.org/css-text/#propdef-text-justify" - servo_restyle_damage="rebuild_and_reflow" -> - % if engine == 'gecko': - impl ToComputedValue for SpecifiedValue { - type ComputedValue = computed_value::T; - - #[inline] - fn to_computed_value(&self, _: &Context) -> computed_value::T { - match *self { - % for value in "Auto None InterCharacter InterWord".split(): - SpecifiedValue::${value} => computed_value::T::${value}, - % endfor - // https://drafts.csswg.org/css-text-3/#valdef-text-justify-distribute - SpecifiedValue::Distribute => computed_value::T::InterCharacter, - } - } - - #[inline] - fn from_computed_value(computed: &computed_value::T) -> SpecifiedValue { - match *computed { - % for value in "Auto None InterCharacter InterWord".split(): - computed_value::T::${value} => SpecifiedValue::${value}, - % endfor - } - } - } - % endif - + animation_value_type="discrete", + spec="https://drafts.csswg.org/css-text/#propdef-text-justify", + servo_restyle_damage="rebuild_and_reflow", +)} ${helpers.predefined_type( "text-align-last", diff --git a/components/style/values/computed/mod.rs b/components/style/values/computed/mod.rs index 5ed404cf5e3..826bb2a2448 100644 --- a/components/style/values/computed/mod.rs +++ b/components/style/values/computed/mod.rs @@ -89,7 +89,7 @@ pub use self::text::TextUnderlinePosition; pub use self::text::{InitialLetter, LetterSpacing, LineBreak, LineHeight}; pub use self::text::{OverflowWrap, RubyPosition, TextOverflow, WordBreak, WordSpacing}; pub use self::text::{TextAlign, TextAlignLast, TextEmphasisPosition, TextEmphasisStyle}; -pub use self::text::{TextDecorationLength, TextDecorationSkipInk}; +pub use self::text::{TextDecorationLength, TextDecorationSkipInk, TextJustify}; pub use self::time::Time; pub use self::transform::{Rotate, Scale, Transform, TransformOperation}; pub use self::transform::{TransformOrigin, TransformStyle, Translate}; diff --git a/components/style/values/computed/text.rs b/components/style/values/computed/text.rs index 4f952fa21cc..c0331643fa7 100644 --- a/components/style/values/computed/text.rs +++ b/components/style/values/computed/text.rs @@ -21,7 +21,7 @@ use style_traits::{CssWriter, ToCss}; pub use crate::values::specified::text::{TextAlignLast, TextUnderlinePosition}; pub use crate::values::specified::{LineBreak, OverflowWrap, RubyPosition, WordBreak}; pub use crate::values::specified::{TextDecorationLine, TextEmphasisPosition}; -pub use crate::values::specified::{TextDecorationSkipInk, TextTransform}; +pub use crate::values::specified::{TextDecorationSkipInk, TextJustify, TextTransform}; /// A computed value for the `initial-letter` property. pub type InitialLetter = GenericInitialLetter; diff --git a/components/style/values/specified/mod.rs b/components/style/values/specified/mod.rs index 6341d495d6c..8a9cb73f4da 100644 --- a/components/style/values/specified/mod.rs +++ b/components/style/values/specified/mod.rs @@ -90,7 +90,7 @@ pub use self::text::RubyPosition; pub use self::text::{InitialLetter, LetterSpacing, LineBreak, LineHeight, TextAlign}; pub use self::text::{OverflowWrap, TextEmphasisPosition, TextEmphasisStyle, WordBreak}; pub use self::text::{TextAlignKeyword, TextDecorationLine, TextOverflow, WordSpacing}; -pub use self::text::{TextDecorationLength, TextDecorationSkipInk, TextTransform}; +pub use self::text::{TextDecorationLength, TextDecorationSkipInk, TextJustify, TextTransform}; pub use self::time::Time; pub use self::transform::{Rotate, Scale, Transform}; pub use self::transform::{TransformOrigin, TransformStyle, Translate}; diff --git a/components/style/values/specified/text.rs b/components/style/values/specified/text.rs index 7ee4dbedda3..7af55b81258 100644 --- a/components/style/values/specified/text.rs +++ b/components/style/values/specified/text.rs @@ -1001,6 +1001,33 @@ pub enum WordBreak { BreakWord, } +/// Values for the `text-justify` CSS property. +#[repr(u8)] +#[derive( + Clone, + Copy, + Debug, + Eq, + MallocSizeOf, + Parse, + PartialEq, + SpecifiedValueInfo, + ToComputedValue, + ToCss, + ToResolvedValue, + ToShmem, +)] +#[allow(missing_docs)] +pub enum TextJustify { + Auto, + None, + InterWord, + // See https://drafts.csswg.org/css-text-3/#valdef-text-justify-distribute + // and https://github.com/w3c/csswg-drafts/issues/6156 for the alias. + #[parse(aliases = "distribute")] + InterCharacter, +} + /// Values for the `line-break` property. #[repr(u8)] #[derive( From 693c2f0d27043361d5a6f93da2bedde4e5e0e9bb Mon Sep 17 00:00:00 2001 From: Oriol Brufau Date: Tue, 16 May 2023 23:15:26 +0200 Subject: [PATCH 40/65] style: Remove -moz-proton-infobars media query support Differential Revision: https://phabricator.services.mozilla.com/D110963 --- components/style/gecko/media_features.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/components/style/gecko/media_features.rs b/components/style/gecko/media_features.rs index a3000f46bb4..287ab6a1797 100644 --- a/components/style/gecko/media_features.rs +++ b/components/style/gecko/media_features.rs @@ -624,7 +624,7 @@ macro_rules! bool_pref_feature { /// to support new types in these entries and (2) ensuring that either /// nsPresContext::MediaFeatureValuesChanged is called when the value that /// would be returned by the evaluator function could change. -pub static MEDIA_FEATURES: [MediaFeatureDescription; 62] = [ +pub static MEDIA_FEATURES: [MediaFeatureDescription; 61] = [ feature!( atom!("width"), AllowsRanges::Yes, @@ -863,6 +863,5 @@ pub static MEDIA_FEATURES: [MediaFeatureDescription; 62] = [ bool_pref_feature!(atom!("-moz-proton-modals"), "browser.proton.modals.enabled"), bool_pref_feature!(atom!("-moz-proton-contextmenus"), "browser.proton.contextmenus.enabled"), bool_pref_feature!(atom!("-moz-proton-doorhangers"), "browser.proton.doorhangers.enabled"), - bool_pref_feature!(atom!("-moz-proton-infobars"), "browser.proton.infobars.enabled"), bool_pref_feature!(atom!("-moz-proton-places-tooltip"), "browser.proton.places-tooltip.enabled"), ]; From fac547276f0c26e3d7f7229340e2aabd6a1e529d Mon Sep 17 00:00:00 2001 From: Oriol Brufau Date: Tue, 16 May 2023 23:16:24 +0200 Subject: [PATCH 41/65] style: Remove various image-orientation related prefs These shipped in 77. Differential Revision: https://phabricator.services.mozilla.com/D111550 --- components/style/properties/longhands/inherited_box.mako.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/components/style/properties/longhands/inherited_box.mako.rs b/components/style/properties/longhands/inherited_box.mako.rs index 32f9819a42d..cea3fde021a 100644 --- a/components/style/properties/longhands/inherited_box.mako.rs +++ b/components/style/properties/longhands/inherited_box.mako.rs @@ -84,8 +84,7 @@ ${helpers.single_keyword( ${helpers.single_keyword( "image-orientation", - "none from-image", - gecko_pref_controlled_initial_value="layout.css.image-orientation.initial-from-image=from-image", + "from-image none", engines="gecko", gecko_enum_prefix="StyleImageOrientation", animation_value_type="discrete", From 743f213c257ba05a9e4234f1ef886354da579d00 Mon Sep 17 00:00:00 2001 From: Oriol Brufau Date: Tue, 16 May 2023 23:17:16 +0200 Subject: [PATCH 42/65] style: Move moz-control-character-visibility out of mako, and remove support for gecko_pref_controlled_initial_value No behavior change, just cleanup. Actually seem this technically _adds_ some code even though it's a cleanup, but that's mostly because of the wrapping of the derive list. The resulting code is simpler (more in-line with our usual things, so I think it's an improvement). Differential Revision: https://phabricator.services.mozilla.com/D111551 --- components/style/properties/data.py | 1 + components/style/properties/helpers.mako.rs | 34 +++++-------------- .../longhands/inherited_text.mako.rs | 10 +++--- components/style/values/computed/text.rs | 2 +- components/style/values/specified/text.rs | 33 ++++++++++++++++++ 5 files changed, 48 insertions(+), 32 deletions(-) diff --git a/components/style/properties/data.py b/components/style/properties/data.py index 559048913b6..dd07ddece32 100644 --- a/components/style/properties/data.py +++ b/components/style/properties/data.py @@ -483,6 +483,7 @@ class Longhand(Property): "LineBreak", "MasonryAutoFlow", "MozForceBrokenImageIcon", + "text::MozControlCharacterVisibility", "MozListReversed", "MathDepth", "MozScriptMinSize", diff --git a/components/style/properties/helpers.mako.rs b/components/style/properties/helpers.mako.rs index 74161793e4e..ecd714cebfc 100644 --- a/components/style/properties/helpers.mako.rs +++ b/components/style/properties/helpers.mako.rs @@ -707,8 +707,7 @@ <%def name="single_keyword(name, values, vector=False, - needs_conversion=False, gecko_pref_controlled_initial_value=None, - **kwargs)"> + needs_conversion=False, **kwargs)"> <% keyword_kwargs = {a: kwargs.pop(a, None) for a in [ 'gecko_constant_prefix', @@ -725,10 +724,13 @@ ]} %> - <%def name="inner_body(keyword, needs_conversion=False, - gecko_pref_controlled_initial_value=None)"> - <%def name="variants(variants)"> - % for variant in variants: + <%def name="inner_body(keyword, needs_conversion=False)"> + pub use self::computed_value::T as SpecifiedValue; + pub mod computed_value { + #[cfg_attr(feature = "servo", derive(Deserialize, Serialize))] + #[derive(Clone, Copy, Debug, Eq, FromPrimitive, MallocSizeOf, Parse, PartialEq, SpecifiedValueInfo, ToComputedValue, ToCss, ToResolvedValue, ToShmem)] + pub enum T { + % for variant in keyword.values_for(engine): <% aliases = [] for alias, v in keyword.aliases_for(engine).items(): @@ -740,31 +742,14 @@ % endif ${to_camel_case(variant)}, % endfor - - pub use self::computed_value::T as SpecifiedValue; - pub mod computed_value { - #[cfg_attr(feature = "servo", derive(Deserialize, Serialize))] - #[derive(Clone, Copy, Debug, Eq, FromPrimitive, MallocSizeOf, Parse, PartialEq, SpecifiedValueInfo, ToComputedValue, ToCss, ToResolvedValue, ToShmem)] - pub enum T { - ${variants(data.longhands_by_name[name].keyword.values_for(engine))} } } #[inline] pub fn get_initial_value() -> computed_value::T { - % if engine == "gecko" and gecko_pref_controlled_initial_value: - if static_prefs::pref!("${gecko_pref_controlled_initial_value.split('=')[0]}") { - return computed_value::T::${to_camel_case(gecko_pref_controlled_initial_value.split('=')[1])}; - } - % endif computed_value::T::${to_camel_case(values.split()[0])} } #[inline] pub fn get_initial_specified_value() -> SpecifiedValue { - % if engine == "gecko" and gecko_pref_controlled_initial_value: - if static_prefs::pref!("${gecko_pref_controlled_initial_value.split('=')[0]}") { - return SpecifiedValue::${to_camel_case(gecko_pref_controlled_initial_value.split('=')[1])}; - } - % endif SpecifiedValue::${to_camel_case(values.split()[0])} } #[inline] @@ -790,8 +775,7 @@ % else: <%call expr="longhand(name, keyword=Keyword(name, values, **keyword_kwargs), **kwargs)"> ${inner_body(Keyword(name, values, **keyword_kwargs), - needs_conversion=needs_conversion, - gecko_pref_controlled_initial_value=gecko_pref_controlled_initial_value)} + needs_conversion=needs_conversion)} % if caller: ${caller.body()} % endif diff --git a/components/style/properties/longhands/inherited_text.mako.rs b/components/style/properties/longhands/inherited_text.mako.rs index b0407054ddd..246e7ff36a6 100644 --- a/components/style/properties/longhands/inherited_text.mako.rs +++ b/components/style/properties/longhands/inherited_text.mako.rs @@ -331,15 +331,13 @@ ${helpers.single_keyword( servo_restyle_damage="rebuild_and_reflow", )} -${helpers.single_keyword( +${helpers.predefined_type( "-moz-control-character-visibility", - "hidden visible", + "text::MozControlCharacterVisibility", + "Default::default()", engines="gecko", - gecko_enum_prefix="StyleControlCharacterVisibility", - gecko_pref_controlled_initial_value="layout.css.control-characters.visible=visible", animation_value_type="none", - gecko_ffi_name="mControlCharacterVisibility", - spec="Nonstandard", + spec="Nonstandard" )} // text underline offset diff --git a/components/style/values/computed/text.rs b/components/style/values/computed/text.rs index c0331643fa7..b42dfb488a3 100644 --- a/components/style/values/computed/text.rs +++ b/components/style/values/computed/text.rs @@ -18,7 +18,7 @@ use crate::Zero; use std::fmt::{self, Write}; use style_traits::{CssWriter, ToCss}; -pub use crate::values::specified::text::{TextAlignLast, TextUnderlinePosition}; +pub use crate::values::specified::text::{TextAlignLast, TextUnderlinePosition, MozControlCharacterVisibility}; pub use crate::values::specified::{LineBreak, OverflowWrap, RubyPosition, WordBreak}; pub use crate::values::specified::{TextDecorationLine, TextEmphasisPosition}; pub use crate::values::specified::{TextDecorationSkipInk, TextJustify, TextTransform}; diff --git a/components/style/values/specified/text.rs b/components/style/values/specified/text.rs index 7af55b81258..629eafd6417 100644 --- a/components/style/values/specified/text.rs +++ b/components/style/values/specified/text.rs @@ -1028,6 +1028,39 @@ pub enum TextJustify { InterCharacter, } +/// Values for the `-moz-control-character-visibility` CSS property. +#[repr(u8)] +#[derive( + Clone, + Copy, + Debug, + Eq, + MallocSizeOf, + Parse, + PartialEq, + SpecifiedValueInfo, + ToComputedValue, + ToCss, + ToResolvedValue, + ToShmem, +)] +#[allow(missing_docs)] +pub enum MozControlCharacterVisibility { + Hidden, + Visible, +} + +impl Default for MozControlCharacterVisibility { + fn default() -> Self { + if static_prefs::pref!("layout.css.control-characters.visible") { + Self::Visible + } else { + Self::Hidden + } + } +} + + /// Values for the `line-break` property. #[repr(u8)] #[derive( From d8993eef7ed05402296301d37ba445f782be61cb Mon Sep 17 00:00:00 2001 From: Oriol Brufau Date: Tue, 16 May 2023 23:40:17 +0200 Subject: [PATCH 43/65] Further changes required by Servo --- components/style/values/specified/text.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/components/style/values/specified/text.rs b/components/style/values/specified/text.rs index 629eafd6417..ce652734060 100644 --- a/components/style/values/specified/text.rs +++ b/components/style/values/specified/text.rs @@ -1050,6 +1050,7 @@ pub enum MozControlCharacterVisibility { Visible, } +#[cfg(feature = "gecko")] impl Default for MozControlCharacterVisibility { fn default() -> Self { if static_prefs::pref!("layout.css.control-characters.visible") { From a7ca8022d3db119a77afdba6bdb70b82a90c58ac Mon Sep 17 00:00:00 2001 From: Oriol Brufau Date: Tue, 16 May 2023 23:19:06 +0200 Subject: [PATCH 44/65] style: [css-pseudo] Use 'white-space: pre' by default in ::marker Differential Revision: https://phabricator.services.mozilla.com/D107359 --- components/style/properties/data.py | 1 + 1 file changed, 1 insertion(+) diff --git a/components/style/properties/data.py b/components/style/properties/data.py index dd07ddece32..8a523ccb66f 100644 --- a/components/style/properties/data.py +++ b/components/style/properties/data.py @@ -879,6 +879,7 @@ class PropertyRestrictions: def marker(data): return set( [ + "white-space", "color", "text-combine-upright", "text-transform", From e23a8bf0ad5fcfc23395b74dccb571fe360ee0bb Mon Sep 17 00:00:00 2001 From: Oriol Brufau Date: Tue, 16 May 2023 23:20:00 +0200 Subject: [PATCH 45/65] style: Add attribute names to the bloom filter Safari does this. This reduces the runtime in the example linked from comment 0 quite a lot (40ms on a local opt build, from ~130ms on a release nightly build). I added a pref because there's a slight chance of performance regressions on pages that do not use attribute selectors, as we're now doing more unconditional work per element (adding the attributes to the bloom filter). But the trade-off should be worth it, I think. Differential Revision: https://phabricator.services.mozilla.com/D111689 --- components/selectors/parser.rs | 26 ++++++++++++ components/style/bloom.rs | 17 ++++++++ components/style/dom.rs | 5 +++ components/style/gecko/selector_parser.rs | 5 +++ components/style/gecko/wrapper.rs | 51 +++++++++++++++++++---- 5 files changed, 96 insertions(+), 8 deletions(-) diff --git a/components/selectors/parser.rs b/components/selectors/parser.rs index e99b1b18412..73c1239035c 100644 --- a/components/selectors/parser.rs +++ b/components/selectors/parser.rs @@ -212,6 +212,12 @@ macro_rules! with_all_bounds { /// pseudo-elements type PseudoElement: $($CommonBounds)* + PseudoElement; + + /// Whether attribute hashes should be collected for filtering + /// purposes. + fn should_collect_attr_hash(_name: &Self::LocalName) -> bool { + false + } } } } @@ -482,6 +488,26 @@ where Component::Class(ref class) if quirks_mode != QuirksMode::Quirks => { class.precomputed_hash() }, + Component::AttributeInNoNamespace { ref local_name, .. } if Impl::should_collect_attr_hash(local_name) => { + // AttributeInNoNamespace is only used when local_name == + // local_name_lower. + local_name.precomputed_hash() + }, + Component::AttributeInNoNamespaceExists { ref local_name, ref local_name_lower, .. } => { + // Only insert the local-name into the filter if it's all + // lowercase. Otherwise we would need to test both hashes, and + // our data structures aren't really set up for that. + if local_name != local_name_lower || !Impl::should_collect_attr_hash(local_name) { + continue; + } + local_name.precomputed_hash() + }, + Component::AttributeOther(ref selector) => { + if selector.local_name != selector.local_name_lower || !Impl::should_collect_attr_hash(&selector.local_name) { + continue; + } + selector.local_name.precomputed_hash() + }, Component::Is(ref list) | Component::Where(ref list) => { // :where and :is OR their selectors, so we can't put any hash // in the filter if there's more than one selector, as that'd diff --git a/components/style/bloom.rs b/components/style/bloom.rs index c17b31d1bee..d75abaa4f93 100644 --- a/components/style/bloom.rs +++ b/components/style/bloom.rs @@ -102,6 +102,15 @@ impl PushedElement { } } +/// Returns whether the attribute name is excluded from the bloom filter. +/// +/// We do this for attributes that are very common but not commonly used in +/// selectors. +#[inline] +pub fn is_attr_name_excluded_from_filter(atom: &crate::Atom) -> bool { + *atom == atom!("class") || *atom == atom!("id") || *atom == atom!("style") +} + fn each_relevant_element_hash(element: E, mut f: F) where E: TElement, @@ -115,6 +124,14 @@ where } element.each_class(|class| f(class.get_hash())); + + if static_prefs::pref!("layout.css.bloom-filter-attribute-names.enabled") { + element.each_attr_name(|name| { + if !is_attr_name_excluded_from_filter(name) { + f(name.get_hash()) + } + }); + } } impl Drop for StyleBloom { diff --git a/components/style/dom.rs b/components/style/dom.rs index c23b985a628..f16e45ce681 100644 --- a/components/style/dom.rs +++ b/components/style/dom.rs @@ -519,6 +519,11 @@ pub trait TElement: { } + /// Internal iterator for the attribute names of this element. + fn each_attr_name(&self, callback: F) + where + F: FnMut(&AtomIdent); + /// Internal iterator for the part names that this element exports for a /// given part name. fn each_exported_part(&self, _name: &AtomIdent, _callback: F) diff --git a/components/style/gecko/selector_parser.rs b/components/style/gecko/selector_parser.rs index a9083581234..b2fe46a9a2c 100644 --- a/components/style/gecko/selector_parser.rs +++ b/components/style/gecko/selector_parser.rs @@ -247,6 +247,11 @@ impl ::selectors::SelectorImpl for SelectorImpl { type PseudoElement = PseudoElement; type NonTSPseudoClass = NonTSPseudoClass; + + fn should_collect_attr_hash(name: &AtomIdent) -> bool { + static_prefs::pref!("layout.css.bloom-filter-attribute-names.enabled") && + !crate::bloom::is_attr_name_excluded_from_filter(name) + } } impl<'a> SelectorParser<'a> { diff --git a/components/style/gecko/wrapper.rs b/components/style/gecko/wrapper.rs index 7d4b4e03f73..3f23981a823 100644 --- a/components/style/gecko/wrapper.rs +++ b/components/style/gecko/wrapper.rs @@ -570,7 +570,7 @@ impl<'le> GeckoElement<'le> { } #[inline(always)] - fn attrs(&self) -> &[structs::AttrArray_InternalAttr] { + fn non_mapped_attrs(&self) -> &[structs::AttrArray_InternalAttr] { unsafe { let attrs = match self.0.mAttrs.mImpl.mPtr.as_ref() { Some(attrs) => attrs, @@ -581,12 +581,29 @@ impl<'le> GeckoElement<'le> { } } + #[inline(always)] + fn mapped_attrs(&self) -> &[structs::AttrArray_InternalAttr] { + unsafe { + let attrs = match self.0.mAttrs.mImpl.mPtr.as_ref() { + Some(attrs) => attrs, + None => return &[], + }; + + let attrs = match attrs.mMappedAttrs.as_ref() { + Some(attrs) => attrs, + None => return &[], + }; + + attrs.mBuffer.as_slice(attrs.mAttrCount as usize) + } + } + #[inline(always)] fn get_part_attr(&self) -> Option<&structs::nsAttrValue> { if !self.has_part_attr() { return None; } - snapshot_helpers::find_attr(self.attrs(), &atom!("part")) + snapshot_helpers::find_attr(self.non_mapped_attrs(), &atom!("part")) } #[inline(always)] @@ -602,7 +619,7 @@ impl<'le> GeckoElement<'le> { } } - snapshot_helpers::find_attr(self.attrs(), &atom!("class")) + snapshot_helpers::find_attr(self.non_mapped_attrs(), &atom!("class")) } #[inline] @@ -1167,7 +1184,7 @@ impl<'le> TElement for GeckoElement<'le> { #[inline] fn exports_any_part(&self) -> bool { - snapshot_helpers::find_attr(self.attrs(), &atom!("exportparts")).is_some() + snapshot_helpers::find_attr(self.non_mapped_attrs(), &atom!("exportparts")).is_some() } // FIXME(emilio): we should probably just return a reference to the Atom. @@ -1177,7 +1194,25 @@ impl<'le> TElement for GeckoElement<'le> { return None; } - snapshot_helpers::get_id(self.attrs()) + snapshot_helpers::get_id(self.non_mapped_attrs()) + } + + fn each_attr_name(&self, mut callback: F) + where + F: FnMut(&AtomIdent), + { + for attr in self.non_mapped_attrs().iter().chain(self.mapped_attrs().iter()) { + let is_nodeinfo = attr.mName.mBits & 1 != 0; + unsafe { + let atom = if is_nodeinfo { + let node_info = &*((attr.mName.mBits & !1) as *const structs::NodeInfo); + node_info.mInner.mName + } else { + attr.mName.mBits as *const nsAtom + }; + AtomIdent::with(atom, |a| callback(a)) + } + } } fn each_class(&self, callback: F) @@ -1197,7 +1232,7 @@ impl<'le> TElement for GeckoElement<'le> { where F: FnMut(&AtomIdent), { - snapshot_helpers::each_exported_part(self.attrs(), name, callback) + snapshot_helpers::each_exported_part(self.non_mapped_attrs(), name, callback) } fn each_part(&self, callback: F) @@ -2058,7 +2093,7 @@ impl<'le> ::selectors::Element for GeckoElement<'le> { return false; } - let element_id = match snapshot_helpers::get_id(self.attrs()) { + let element_id = match snapshot_helpers::get_id(self.non_mapped_attrs()) { Some(id) => id, None => return false, }; @@ -2078,7 +2113,7 @@ impl<'le> ::selectors::Element for GeckoElement<'le> { #[inline] fn imported_part(&self, name: &AtomIdent) -> Option { - snapshot_helpers::imported_part(self.attrs(), name) + snapshot_helpers::imported_part(self.non_mapped_attrs(), name) } #[inline(always)] From b5dcb5c9610643cb534b1abdae885a792291659d Mon Sep 17 00:00:00 2001 From: Oriol Brufau Date: Wed, 17 May 2023 00:12:28 +0200 Subject: [PATCH 46/65] Further changes required by Servo --- components/selectors/parser.rs | 14 +++++++++++--- components/style/bloom.rs | 2 ++ components/style/dom.rs | 1 + 3 files changed, 14 insertions(+), 3 deletions(-) diff --git a/components/selectors/parser.rs b/components/selectors/parser.rs index 73c1239035c..3c728116e97 100644 --- a/components/selectors/parser.rs +++ b/components/selectors/parser.rs @@ -488,12 +488,18 @@ where Component::Class(ref class) if quirks_mode != QuirksMode::Quirks => { class.precomputed_hash() }, - Component::AttributeInNoNamespace { ref local_name, .. } if Impl::should_collect_attr_hash(local_name) => { + Component::AttributeInNoNamespace { ref local_name, .. } + if Impl::should_collect_attr_hash(local_name) => + { // AttributeInNoNamespace is only used when local_name == // local_name_lower. local_name.precomputed_hash() }, - Component::AttributeInNoNamespaceExists { ref local_name, ref local_name_lower, .. } => { + Component::AttributeInNoNamespaceExists { + ref local_name, + ref local_name_lower, + .. + } => { // Only insert the local-name into the filter if it's all // lowercase. Otherwise we would need to test both hashes, and // our data structures aren't really set up for that. @@ -503,7 +509,9 @@ where local_name.precomputed_hash() }, Component::AttributeOther(ref selector) => { - if selector.local_name != selector.local_name_lower || !Impl::should_collect_attr_hash(&selector.local_name) { + if selector.local_name != selector.local_name_lower || + !Impl::should_collect_attr_hash(&selector.local_name) + { continue; } selector.local_name.precomputed_hash() diff --git a/components/style/bloom.rs b/components/style/bloom.rs index d75abaa4f93..1840c780506 100644 --- a/components/style/bloom.rs +++ b/components/style/bloom.rs @@ -107,6 +107,7 @@ impl PushedElement { /// We do this for attributes that are very common but not commonly used in /// selectors. #[inline] +#[cfg(feature = "gecko")] pub fn is_attr_name_excluded_from_filter(atom: &crate::Atom) -> bool { *atom == atom!("class") || *atom == atom!("id") || *atom == atom!("style") } @@ -125,6 +126,7 @@ where element.each_class(|class| f(class.get_hash())); + #[cfg(feature = "gecko")] if static_prefs::pref!("layout.css.bloom-filter-attribute-names.enabled") { element.each_attr_name(|name| { if !is_attr_name_excluded_from_filter(name) { diff --git a/components/style/dom.rs b/components/style/dom.rs index f16e45ce681..3d088956289 100644 --- a/components/style/dom.rs +++ b/components/style/dom.rs @@ -520,6 +520,7 @@ pub trait TElement: } /// Internal iterator for the attribute names of this element. + #[cfg(feature = "gecko")] fn each_attr_name(&self, callback: F) where F: FnMut(&AtomIdent); From 7425f2d9224c5c47d477b79d4d9a5dd8f713a023 Mon Sep 17 00:00:00 2001 From: Oriol Brufau Date: Tue, 16 May 2023 23:21:07 +0200 Subject: [PATCH 47/65] style: Enable forced-colors in UA and chrome always Differential Revision: https://phabricator.services.mozilla.com/D112069 --- components/style/media_queries/media_feature_expression.rs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/components/style/media_queries/media_feature_expression.rs b/components/style/media_queries/media_feature_expression.rs index 41be485d5c0..10bf16500ef 100644 --- a/components/style/media_queries/media_feature_expression.rs +++ b/components/style/media_queries/media_feature_expression.rs @@ -221,7 +221,11 @@ fn disabled_by_pref(feature: &Atom, context: &ParserContext) -> bool { #[cfg(feature = "gecko")] { if *feature == atom!("forced-colors") { - return !static_prefs::pref!("layout.css.forced-colors.enabled"); + // forced-colors is always enabled in the ua and chrome. On + // the web it is hidden behind a preference, which is defaulted + // to 'true' as of bug 1659511. + return !context.in_ua_or_chrome_sheet() && + !static_prefs::pref!("layout.css.forced-colors.enabled"); } // prefers-contrast is always enabled in the ua and chrome. On // the web it is hidden behind a preference. From 90a2687545b8c06fc311ba250cd7db7858b5b297 Mon Sep 17 00:00:00 2001 From: Oriol Brufau Date: Wed, 17 May 2023 00:28:18 +0200 Subject: [PATCH 48/65] style: Add a CSS error to the console when using non-featureless :host selectors (which would never match by definition). Differential Revision: https://phabricator.services.mozilla.com/D111610 --- components/selectors/parser.rs | 9 ++++- components/style/error_reporting.rs | 5 +++ components/style/stylesheets/rule_parser.rs | 40 +++++++++++++++++++-- 3 files changed, 51 insertions(+), 3 deletions(-) diff --git a/components/selectors/parser.rs b/components/selectors/parser.rs index 3c728116e97..0b8d7d2331e 100644 --- a/components/selectors/parser.rs +++ b/components/selectors/parser.rs @@ -859,7 +859,7 @@ impl<'a, Impl: 'a + SelectorImpl> SelectorIter<'a, Impl> { #[inline] pub(crate) fn is_featureless_host_selector(&mut self) -> bool { self.selector_length() > 0 && - self.all(|component| matches!(*component, Component::Host(..))) && + self.all(|component| component.is_host()) && self.next_sequence().is_none() } @@ -1123,10 +1123,17 @@ pub enum Component { impl Component { /// Returns true if this is a combinator. + #[inline] pub fn is_combinator(&self) -> bool { matches!(*self, Component::Combinator(_)) } + /// Returns true if this is a :host() selector. + #[inline] + pub fn is_host(&self) -> bool { + matches!(*self, Component::Host(..)) + } + /// Returns the value as a combinator if applicable, None otherwise. pub fn as_combinator(&self) -> Option { match *self { diff --git a/components/style/error_reporting.rs b/components/style/error_reporting.rs index 6e63b0ad68a..eebb0901309 100644 --- a/components/style/error_reporting.rs +++ b/components/style/error_reporting.rs @@ -54,6 +54,8 @@ pub enum ContextualParseError<'a> { InvalidMediaRule(&'a str, ParseError<'a>), /// A value was not recognized. UnsupportedValue(&'a str, ParseError<'a>), + /// A never-matching `:host` selector was found. + NeverMatchingHostSelector(String), } impl<'a> fmt::Display for ContextualParseError<'a> { @@ -210,6 +212,9 @@ impl<'a> fmt::Display for ContextualParseError<'a> { parse_error_to_str(err, f) }, ContextualParseError::UnsupportedValue(_value, ref err) => parse_error_to_str(err, f), + ContextualParseError::NeverMatchingHostSelector(ref selector) => { + write!(f, ":host selector is not featureless: {}", selector) + } } } } diff --git a/components/style/stylesheets/rule_parser.rs b/components/style/stylesheets/rule_parser.rs index 524d54d9c33..914af2cfb66 100644 --- a/components/style/stylesheets/rule_parser.rs +++ b/components/style/stylesheets/rule_parser.rs @@ -53,7 +53,7 @@ pub struct TopLevelRuleParser<'a> { /// This won't contain any namespaces, and only nested parsers created with /// `ParserContext::new_with_rule_type` will. pub context: ParserContext<'a>, - /// The current stajkj/te of the parser. + /// The current state of the parser. pub state: State, /// Whether we have tried to parse was invalid due to being in the wrong /// place (e.g. an @import rule was found while in the `Body` state). Reset @@ -587,6 +587,38 @@ impl<'a, 'b, 'i> AtRuleParser<'i> for NestedRuleParser<'a, 'b> { } } +#[inline(never)] +fn check_for_useless_selector( + input: &mut Parser, + context: &ParserContext, + selectors: &SelectorList, +) { + use cssparser::ToCss; + + 'selector_loop: for selector in selectors.0.iter() { + let mut current = selector.iter(); + loop { + let mut found_host = false; + let mut found_non_host = false; + for component in &mut current { + if component.is_host() { + found_host = true; + } else { + found_non_host = true; + } + if found_host && found_non_host { + let location = input.current_source_location(); + context.log_css_error(location, ContextualParseError::NeverMatchingHostSelector(selector.to_css_string())); + continue 'selector_loop; + } + } + if current.next_sequence().is_none() { + break; + } + } + } +} + impl<'a, 'b, 'i> QualifiedRuleParser<'i> for NestedRuleParser<'a, 'b> { type Prelude = SelectorList; type QualifiedRule = CssRule; @@ -601,7 +633,11 @@ impl<'a, 'b, 'i> QualifiedRuleParser<'i> for NestedRuleParser<'a, 'b> { namespaces: self.namespaces, url_data: Some(self.context.url_data), }; - SelectorList::parse(&selector_parser, input) + let selectors = SelectorList::parse(&selector_parser, input)?; + if self.context.error_reporting_enabled() { + check_for_useless_selector(input, &self.context, &selectors); + } + Ok(selectors) } fn parse_block<'t>( From 7567ddd262f93c059142550f0077eaa1ed0306ff Mon Sep 17 00:00:00 2001 From: Oriol Brufau Date: Wed, 17 May 2023 00:29:11 +0200 Subject: [PATCH 49/65] style: Hide -moz-outline-radius behind a pref This property does nothing since bug 315209 got implemented. Every single user that I checked was doing the same math by hand, so hooray for good defaults :-) Differential Revision: https://phabricator.services.mozilla.com/D112253 --- components/style/properties/longhands/outline.mako.rs | 1 + components/style/properties/shorthands/outline.mako.rs | 1 + 2 files changed, 2 insertions(+) diff --git a/components/style/properties/longhands/outline.mako.rs b/components/style/properties/longhands/outline.mako.rs index 5c3dd15cbb6..e5cd09fbe82 100644 --- a/components/style/properties/longhands/outline.mako.rs +++ b/components/style/properties/longhands/outline.mako.rs @@ -51,6 +51,7 @@ ${helpers.predefined_type( engines="gecko", boxed=True, animation_value_type="BorderCornerRadius", + gecko_pref="layout.css.moz-outline-radius.enabled", spec="Nonstandard (https://developer.mozilla.org/en-US/docs/Web/CSS/-moz-outline-radius)", )} % endfor diff --git a/components/style/properties/shorthands/outline.mako.rs b/components/style/properties/shorthands/outline.mako.rs index 75b5fcb717b..db397498919 100644 --- a/components/style/properties/shorthands/outline.mako.rs +++ b/components/style/properties/shorthands/outline.mako.rs @@ -83,6 +83,7 @@ <%helpers:shorthand name="-moz-outline-radius" engines="gecko" + gecko_pref="layout.css.moz-outline-radius.enabled" sub_properties="${' '.join( '-moz-outline-radius-%s' % corner for corner in ['topleft', 'topright', 'bottomright', 'bottomleft'] From 773ddf31fe6f207b1eb23ac835a27dad1ac46917 Mon Sep 17 00:00:00 2001 From: Oriol Brufau Date: Wed, 17 May 2023 00:30:20 +0200 Subject: [PATCH 50/65] style: Support the type() function in image-set Differential Revision: https://phabricator.services.mozilla.com/D109201 --- components/style/gecko/media_queries.rs | 7 +++++ components/style/values/computed/image.rs | 17 ++++++++++-- components/style/values/generics/image.rs | 30 ++++++++++++++++++++-- components/style/values/specified/image.rs | 25 +++++++++++++++--- 4 files changed, 71 insertions(+), 8 deletions(-) diff --git a/components/style/gecko/media_queries.rs b/components/style/gecko/media_queries.rs index 08ef628f1ff..5d960a8e9e1 100644 --- a/components/style/gecko/media_queries.rs +++ b/components/style/gecko/media_queries.rs @@ -432,4 +432,11 @@ impl Device { }; SideOffsets2D::new(top, right, bottom, left) } + + /// Returns true if the given MIME type is supported + pub fn is_supported_mime_type(&self, mime_type: &str) -> bool { + unsafe { + bindings::Gecko_IsSupportedImageMimeType(mime_type.as_ptr(), mime_type.len() as u32) + } + } } diff --git a/components/style/values/computed/image.rs b/components/style/values/computed/image.rs index 5e123a07abb..6290ac68a5d 100644 --- a/components/style/values/computed/image.rs +++ b/components/style/values/computed/image.rs @@ -74,9 +74,20 @@ impl ToComputedValue for specified::ImageSet { fn to_computed_value(&self, context: &Context) -> Self::ComputedValue { let items = self.items.to_computed_value(context); let dpr = context.device().device_pixel_ratio().get(); + + // If no item have a supported MIME type, the behavior is undefined by the standard + // By default, we select the first item + let mut supported_image = false; let mut selected_index = 0; let mut selected_resolution = items[0].resolution.dppx(); - for (i, item) in items.iter().enumerate().skip(1) { + + for (i, item) in items.iter().enumerate() { + + // If the MIME type is not supported, we discard the ImageSetItem + if item.has_mime_type && !context.device().is_supported_mime_type(&item.mime_type) { + continue; + } + let candidate_resolution = item.resolution.dppx(); // https://drafts.csswg.org/css-images-4/#image-set-notation: @@ -97,7 +108,9 @@ impl ToComputedValue for specified::ImageSet { false }; - if better_candidate() { + // The first item with a supported MIME type is obviously the current best candidate + if !supported_image || better_candidate() { + supported_image = true; selected_index = i; selected_resolution = candidate_resolution; } diff --git a/components/style/values/generics/image.rs b/components/style/values/generics/image.rs index d49a8a2b4de..014ae46db02 100644 --- a/components/style/values/generics/image.rs +++ b/components/style/values/generics/image.rs @@ -132,7 +132,7 @@ pub struct GenericImageSet { /// An optional percent and a cross fade image. #[derive( - Clone, Debug, MallocSizeOf, PartialEq, ToComputedValue, ToResolvedValue, ToShmem, ToCss, + Clone, Debug, MallocSizeOf, PartialEq, ToComputedValue, ToResolvedValue, ToShmem, )] #[repr(C)] pub struct GenericImageSetItem { @@ -142,7 +142,33 @@ pub struct GenericImageSetItem { /// /// TODO: Skip serialization if it is 1x. pub resolution: Resolution, - // TODO: type() function. + + /// The `type()` + /// (Optional) Specify the image's MIME type + pub mime_type: crate::OwnedStr, + + /// True if mime_type has been specified + pub has_mime_type: bool, +} + +impl ToCss for GenericImageSetItem +{ + fn to_css(&self, dest: &mut CssWriter) -> fmt::Result + where + W: fmt::Write, + { + self.image.to_css(dest)?; + dest.write_str(" ")?; + self.resolution.to_css(dest)?; + + if self.has_mime_type { + dest.write_str(" ")?; + dest.write_str("type(")?; + self.mime_type.to_css(dest)?; + dest.write_str(")")?; + } + Ok(()) + } } pub use self::GenericImageSet as ImageSet; diff --git a/components/style/values/specified/image.rs b/components/style/values/specified/image.rs index 0be0fcd2a0c..85c178e064d 100644 --- a/components/style/values/specified/image.rs +++ b/components/style/values/specified/image.rs @@ -377,6 +377,13 @@ impl ImageSet { } impl ImageSetItem { + fn parse_type<'i>(p: &mut Parser<'i, '_>) -> Result> { + p.expect_function_matching("type")?; + p.parse_nested_block(|input| { + Ok(input.expect_string()?.as_ref().to_owned().into()) + }) + } + fn parse<'i, 't>( context: &ParserContext, input: &mut Parser<'i, 't>, @@ -393,10 +400,20 @@ impl ImageSetItem { context, input, cors_mode, /* allow_none = */ false, /* only_url = */ only_url )?, }; - let resolution = input - .try_parse(|input| Resolution::parse(context, input)) - .unwrap_or(Resolution::X(1.0)); - Ok(Self { image, resolution }) + + let mut resolution = input.try_parse(|input| Resolution::parse(context, input)).ok(); + let mime_type = input.try_parse(Self::parse_type).ok(); + + // Try to parse resolution after type(). + if mime_type.is_some() && resolution.is_none() { + resolution = input.try_parse(|input| Resolution::parse(context, input)).ok(); + } + + let resolution = resolution.unwrap_or(Resolution::X(1.0)); + let has_mime_type = mime_type.is_some(); + let mime_type = mime_type.unwrap_or_default(); + + Ok(Self { image, resolution, has_mime_type, mime_type }) } } From 29bca57333883c2416143f583cf3f754b09aa205 Mon Sep 17 00:00:00 2001 From: Oriol Brufau Date: Wed, 17 May 2023 01:17:20 +0200 Subject: [PATCH 51/65] Further changes required by Servo --- Cargo.lock | 1 + components/net/mime_classifier.rs | 2 ++ components/style/Cargo.toml | 1 + components/style/servo/media_queries.rs | 18 ++++++++++++++++++ 4 files changed, 22 insertions(+) diff --git a/Cargo.lock b/Cargo.lock index d945407f3ec..b810a5bdd43 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -6237,6 +6237,7 @@ dependencies = [ "log", "malloc_size_of", "malloc_size_of_derive", + "mime", "new_debug_unreachable", "num-derive", "num-integer", diff --git a/components/net/mime_classifier.rs b/components/net/mime_classifier.rs index 2a32fcf9948..f3ced86dbc5 100644 --- a/components/net/mime_classifier.rs +++ b/components/net/mime_classifier.rs @@ -461,6 +461,8 @@ impl GroupedClassifier { fn image_classifer() -> GroupedClassifier { GroupedClassifier { byte_matchers: vec![ + // Keep this in sync with 'is_supported_mime_type' from + // components/style/servo/media_queries.rs Box::new(ByteMatcher::image_x_icon()), Box::new(ByteMatcher::image_x_icon_cursor()), Box::new(ByteMatcher::image_bmp()), diff --git a/components/style/Cargo.toml b/components/style/Cargo.toml index a88ccf0fd88..70219416452 100644 --- a/components/style/Cargo.toml +++ b/components/style/Cargo.toml @@ -50,6 +50,7 @@ lazy_static = "1" log = { version = "0.4", features = ["std"] } malloc_size_of = { path = "../malloc_size_of" } malloc_size_of_derive = "0.1" +mime = "0.3.13" new_debug_unreachable = "1.0" num-derive = "0.3" num-integer = "0.1" diff --git a/components/style/servo/media_queries.rs b/components/style/servo/media_queries.rs index 4b535a9b875..cee76053ec0 100644 --- a/components/style/servo/media_queries.rs +++ b/components/style/servo/media_queries.rs @@ -19,6 +19,7 @@ use app_units::Au; use cssparser::RGBA; use euclid::default::Size2D as UntypedSize2D; use euclid::{Scale, SideOffsets2D, Size2D}; +use mime::Mime; use std::sync::atomic::{AtomicBool, AtomicU32, Ordering}; use style_traits::viewport::ViewportConstraints; use style_traits::{CSSPixel, DevicePixel}; @@ -202,6 +203,23 @@ impl Device { pub fn safe_area_insets(&self) -> SideOffsets2D { SideOffsets2D::zero() } + + /// Returns true if the given MIME type is supported + pub fn is_supported_mime_type(&self, mime_type: &str) -> bool { + match mime_type.parse::() { + Ok(m) => { + // Keep this in sync with 'image_classifer' from + // components/net/mime_classifier.rs + m == mime::IMAGE_BMP + || m == mime::IMAGE_GIF + || m == mime::IMAGE_PNG + || m == mime::IMAGE_JPEG + || m == "image/x-icon" + || m == "image/webp" + } + _ => false, + } + } } /// https://drafts.csswg.org/mediaqueries-4/#width From d12cb17d73f4261d3292544e651495c0891f4763 Mon Sep 17 00:00:00 2001 From: Oriol Brufau Date: Wed, 17 May 2023 00:31:25 +0200 Subject: [PATCH 52/65] style: -moz-toolbar-prefers-color-scheme for dark theme detection This adds a new @media query -moz-toolbar-prefers-color-scheme which works like prefers-color-scheme but is set based on the browser theme rather than the OS theme. The background colour of the toolbar is used to determine the theme dark/light preference. This will be used for in-content common.css pages and other UI elements that include that stylesheet in the browser-chrome through shadow DOM. The end result is that about: pages, infobars, and modals will now "match" the browser theme (just light/dark mode, not LWT theming support). Differential Revision: https://phabricator.services.mozilla.com/D111486 --- components/style/gecko/media_features.rs | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/components/style/gecko/media_features.rs b/components/style/gecko/media_features.rs index 287ab6a1797..f6703bc9b3f 100644 --- a/components/style/gecko/media_features.rs +++ b/components/style/gecko/media_features.rs @@ -406,6 +406,21 @@ fn eval_prefers_color_scheme(device: &Device, query_value: Option) -> bool { + let prefers_color_scheme = if static_prefs::pref!("browser.theme.dark-toolbar-theme") { + PrefersColorScheme::Dark + } else { + PrefersColorScheme::Light + }; + + match query_value { + Some(v) => prefers_color_scheme == v, + None => true, + } +} + bitflags! { /// https://drafts.csswg.org/mediaqueries-4/#mf-interaction struct PointerCapabilities: u8 { @@ -624,7 +639,7 @@ macro_rules! bool_pref_feature { /// to support new types in these entries and (2) ensuring that either /// nsPresContext::MediaFeatureValuesChanged is called when the value that /// would be returned by the evaluator function could change. -pub static MEDIA_FEATURES: [MediaFeatureDescription; 61] = [ +pub static MEDIA_FEATURES: [MediaFeatureDescription; 62] = [ feature!( atom!("width"), AllowsRanges::Yes, @@ -834,6 +849,12 @@ pub static MEDIA_FEATURES: [MediaFeatureDescription; 61] = [ Evaluator::BoolInteger(eval_moz_non_native_content_theme), ParsingRequirements::CHROME_AND_UA_ONLY, ), + feature!( + atom!("-moz-toolbar-prefers-color-scheme"), + AllowsRanges::No, + keyword_evaluator!(eval_toolbar_prefers_color_scheme, PrefersColorScheme), + ParsingRequirements::CHROME_AND_UA_ONLY, + ), lnf_int_feature!(atom!("-moz-scrollbar-start-backward"), ScrollArrowStyle, get_scrollbar_start_backward), lnf_int_feature!(atom!("-moz-scrollbar-start-forward"), ScrollArrowStyle, get_scrollbar_start_forward), From 54965bbcb9b6261d74c6ff587dffa3d748c1ac56 Mon Sep 17 00:00:00 2001 From: Oriol Brufau Date: Wed, 17 May 2023 00:32:26 +0200 Subject: [PATCH 53/65] style: Fix whitespace handling in clamp() parsing We should just restore the state when seeing whitespace and an unknown token like when we do when finding any other token a few lines below. This is not an issue for most callers (it's only an issue for clamp()) because the other callers use either `parse_comma_separated()` (for min/max), or `parse_nested_block()` (for parens / nested calc()). Both of those functions restrict the input in such a way that is_exhausted returns true (a few lines above) and thus we parse successfully. Differential Revision: https://phabricator.services.mozilla.com/D112681 --- components/style/values/specified/calc.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/components/style/values/specified/calc.rs b/components/style/values/specified/calc.rs index c42683bce1f..d5733a6ce4a 100644 --- a/components/style/values/specified/calc.rs +++ b/components/style/values/specified/calc.rs @@ -374,9 +374,9 @@ impl CalcNode { rhs.negate(); sum.push(rhs); }, - ref t => { - let t = t.clone(); - return Err(input.new_unexpected_token_error(t)); + _ => { + input.reset(&start); + break; }, } }, From 69fc5ca9fd2f8410999a83159c20b852b6c22ecd Mon Sep 17 00:00:00 2001 From: Oriol Brufau Date: Wed, 17 May 2023 00:33:24 +0200 Subject: [PATCH 54/65] style: Garbage-collect event state bits We had about 9 gaps / unused bits. I moved the devtools ones at the end because I think we should be able to remove them (but separate bug). Differential Revision: https://phabricator.services.mozilla.com/D113365 --- components/style/element_state.rs | 81 +++++++++++------------ components/style/servo/selector_parser.rs | 2 +- 2 files changed, 39 insertions(+), 44 deletions(-) diff --git a/components/style/element_state.rs b/components/style/element_state.rs index 853d4039b2a..8a9f4065b8b 100644 --- a/components/style/element_state.rs +++ b/components/style/element_state.rs @@ -54,84 +54,79 @@ bitflags! { /// Non-standard: https://developer.mozilla.org/en-US/docs/Web/CSS/:-moz-broken const IN_BROKEN_STATE = 1 << 14; /// Non-standard: https://developer.mozilla.org/en-US/docs/Web/CSS/:-moz-loading - const IN_LOADING_STATE = 1 << 17; + const IN_LOADING_STATE = 1 << 15; /// - const IN_REQUIRED_STATE = 1 << 21; + const IN_REQUIRED_STATE = 1 << 16; /// - const IN_OPTIONAL_STATE = 1 << 22; - /// - const IN_READ_WRITE_STATE = 1 << 22; + const IN_OPTIONAL_STATE = 1 << 17; /// - const IN_DEFINED_STATE = 1 << 23; + const IN_DEFINED_STATE = 1 << 18; /// - const IN_VISITED_STATE = 1 << 24; + const IN_VISITED_STATE = 1 << 19; /// - const IN_UNVISITED_STATE = 1 << 25; + const IN_UNVISITED_STATE = 1 << 20; /// const IN_VISITED_OR_UNVISITED_STATE = ElementState::IN_VISITED_STATE.bits | ElementState::IN_UNVISITED_STATE.bits; /// Non-standard: https://developer.mozilla.org/en-US/docs/Web/CSS/:-moz-drag-over - const IN_DRAGOVER_STATE = 1 << 26; + const IN_DRAGOVER_STATE = 1 << 21; /// - const IN_INRANGE_STATE = 1 << 27; + const IN_INRANGE_STATE = 1 << 22; /// - const IN_OUTOFRANGE_STATE = 1 << 28; + const IN_OUTOFRANGE_STATE = 1 << 23; /// - const IN_READONLY_STATE = 1 << 29; + const IN_READONLY_STATE = 1 << 24; /// - const IN_READWRITE_STATE = 1 << 30; + const IN_READWRITE_STATE = 1 << 25; /// - const IN_DEFAULT_STATE = 1 << 31; + const IN_DEFAULT_STATE = 1 << 26; /// Non-standard: https://developer.mozilla.org/en-US/docs/Web/CSS/:-moz-submit-invalid - const IN_MOZ_SUBMITINVALID_STATE = 1 << 32; + const IN_MOZ_SUBMITINVALID_STATE = 1 << 27; /// Non-standard & undocumented. - const IN_OPTIMUM_STATE = 1 << 33; + const IN_OPTIMUM_STATE = 1 << 28; /// Non-standard & undocumented. - const IN_SUB_OPTIMUM_STATE = 1 << 34; + const IN_SUB_OPTIMUM_STATE = 1 << 29; /// Non-standard & undocumented. - const IN_SUB_SUB_OPTIMUM_STATE = 1 << 35; + const IN_SUB_SUB_OPTIMUM_STATE = 1 << 30; /// Non-standard & undocumented. - const IN_DEVTOOLS_HIGHLIGHTED_STATE = 1 << 36; - /// Non-standard & undocumented. - const IN_STYLEEDITOR_TRANSITIONING_STATE = 1 << 37; - /// Non-standard & undocumented. - const IN_INCREMENT_SCRIPT_LEVEL_STATE = 1 << 38; - /// Non-standard: https://developer.mozilla.org/en-US/docs/Web/CSS/:-moz-focusring - /// - /// But also https://drafts.csswg.org/selectors-4/#the-focus-visible-pseudo - const IN_FOCUSRING_STATE = 1 << 39; + const IN_INCREMENT_SCRIPT_LEVEL_STATE = 1 << 31; + /// + const IN_FOCUSRING_STATE = 1 << 32; /// - const IN_FOCUS_WITHIN_STATE = 1 << 43; + const IN_FOCUS_WITHIN_STATE = 1 << 33; /// :dir matching; the states are used for dynamic change detection. /// State that elements that match :dir(ltr) are in. - const IN_LTR_STATE = 1 << 44; + const IN_LTR_STATE = 1 << 34; /// State that elements that match :dir(rtl) are in. - const IN_RTL_STATE = 1 << 45; + const IN_RTL_STATE = 1 << 35; /// State that HTML elements that have a "dir" attr are in. - const IN_HAS_DIR_ATTR_STATE = 1 << 46; + const IN_HAS_DIR_ATTR_STATE = 1 << 36; /// State that HTML elements with dir="ltr" (or something /// case-insensitively equal to "ltr") are in. - const IN_HAS_DIR_ATTR_LTR_STATE = 1 << 47; + const IN_HAS_DIR_ATTR_LTR_STATE = 1 << 37; /// State that HTML elements with dir="rtl" (or something /// case-insensitively equal to "rtl") are in. - const IN_HAS_DIR_ATTR_RTL_STATE = 1 << 48; + const IN_HAS_DIR_ATTR_RTL_STATE = 1 << 38; /// State that HTML elements without a valid-valued "dir" attr or /// any HTML elements (including ) with dir="auto" (or something /// case-insensitively equal to "auto") are in. - const IN_HAS_DIR_ATTR_LIKE_AUTO_STATE = 1 << 49; + const IN_HAS_DIR_ATTR_LIKE_AUTO_STATE = 1 << 39; /// Non-standard & undocumented. - const IN_AUTOFILL_STATE = 1 << 50; + const IN_AUTOFILL_STATE = 1 << 40; /// Non-standard & undocumented. - const IN_AUTOFILL_PREVIEW_STATE = 1 << 51; + const IN_AUTOFILL_PREVIEW_STATE = 1 << 41; /// State that dialog element is modal, for centered alignment - /// - /// https://html.spec.whatwg.org/multipage/#centered-alignment - const IN_MODAL_DIALOG_STATE = 1 << 53; - - /// https://html.spec.whatwg.org/multipage/#inert-subtrees - const IN_MOZINERT_STATE = 1 << 54; + /// + const IN_MODAL_DIALOG_STATE = 1 << 42; + /// + const IN_MOZINERT_STATE = 1 << 43; /// State for the topmost dialog element in top layer - const IN_TOPMOST_MODAL_DIALOG_STATE = 1 << 55; + const IN_TOPMOST_MODAL_DIALOG_STATE = 1 << 44; + /// Initially used for the devtools highlighter, but now somehow only + /// used for the devtools accessibility inspector. + const IN_DEVTOOLS_HIGHLIGHTED_STATE = 1 << 45; + /// Used for the devtools style editor. Probably should go away. + const IN_STYLEEDITOR_TRANSITIONING_STATE = 1 << 46; } } diff --git a/components/style/servo/selector_parser.rs b/components/style/servo/selector_parser.rs index ce61c172553..7d5b8c5772a 100644 --- a/components/style/servo/selector_parser.rs +++ b/components/style/servo/selector_parser.rs @@ -372,7 +372,7 @@ impl NonTSPseudoClass { Disabled => ElementState::IN_DISABLED_STATE, Checked => ElementState::IN_CHECKED_STATE, Indeterminate => ElementState::IN_INDETERMINATE_STATE, - ReadOnly | ReadWrite => ElementState::IN_READ_WRITE_STATE, + ReadOnly | ReadWrite => ElementState::IN_READWRITE_STATE, PlaceholderShown => ElementState::IN_PLACEHOLDER_SHOWN_STATE, Target => ElementState::IN_TARGET_STATE, From 373d22119ffdbbf66b40f98ff9ceb64634ad1124 Mon Sep 17 00:00:00 2001 From: Oriol Brufau Date: Wed, 17 May 2023 01:51:57 +0200 Subject: [PATCH 55/65] Further changes required by Servo --- components/script/dom/element.rs | 4 ++-- components/script/dom/htmlinputelement.rs | 2 +- components/script/dom/htmltextareaelement.rs | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/components/script/dom/element.rs b/components/script/dom/element.rs index de34eb401e0..d8b80df754a 100644 --- a/components/script/dom/element.rs +++ b/components/script/dom/element.rs @@ -3525,11 +3525,11 @@ impl Element { } pub fn read_write_state(&self) -> bool { - self.state.get().contains(ElementState::IN_READ_WRITE_STATE) + self.state.get().contains(ElementState::IN_READWRITE_STATE) } pub fn set_read_write_state(&self, value: bool) { - self.set_state(ElementState::IN_READ_WRITE_STATE, value) + self.set_state(ElementState::IN_READWRITE_STATE, value) } pub fn placeholder_shown_state(&self) -> bool { diff --git a/components/script/dom/htmlinputelement.rs b/components/script/dom/htmlinputelement.rs index 0463fb14f0b..696e4566b63 100755 --- a/components/script/dom/htmlinputelement.rs +++ b/components/script/dom/htmlinputelement.rs @@ -297,7 +297,7 @@ impl HTMLInputElement { .clone(); HTMLInputElement { htmlelement: HTMLElement::new_inherited_with_state( - ElementState::IN_ENABLED_STATE | ElementState::IN_READ_WRITE_STATE, + ElementState::IN_ENABLED_STATE | ElementState::IN_READWRITE_STATE, local_name, prefix, document, diff --git a/components/script/dom/htmltextareaelement.rs b/components/script/dom/htmltextareaelement.rs index c6d9331532f..aee9c1eadff 100755 --- a/components/script/dom/htmltextareaelement.rs +++ b/components/script/dom/htmltextareaelement.rs @@ -148,7 +148,7 @@ impl HTMLTextAreaElement { .clone(); HTMLTextAreaElement { htmlelement: HTMLElement::new_inherited_with_state( - ElementState::IN_ENABLED_STATE | ElementState::IN_READ_WRITE_STATE, + ElementState::IN_ENABLED_STATE | ElementState::IN_READWRITE_STATE, local_name, prefix, document, From ab445a02b098df29c3aeb5e1885026d229709c73 Mon Sep 17 00:00:00 2001 From: Oriol Brufau Date: Wed, 17 May 2023 00:34:33 +0200 Subject: [PATCH 56/65] style: Implement accent-color in nsNativeBasicTheme This is a new addition for CSS UI Level 4: https://drafts.csswg.org/css-ui-4/#widget-accent I want to provide feedback on some spec issues, and thought it was a kinda neat thing to prototype (it also makes testing contrast and such with random GTK themes easier). For now enable for Nightly only. Differential Revision: https://phabricator.services.mozilla.com/D112312 --- .../properties/longhands/inherited_ui.mako.rs | 17 +++++++++-- components/style/values/computed/color.rs | 5 +++- components/style/values/generics/color.rs | 30 +++++++++++++++++++ components/style/values/resolved/color.rs | 8 ++--- components/style/values/specified/color.rs | 14 ++++++++- 5 files changed, 66 insertions(+), 8 deletions(-) diff --git a/components/style/properties/longhands/inherited_ui.mako.rs b/components/style/properties/longhands/inherited_ui.mako.rs index 233ef365329..f08abbc6235 100644 --- a/components/style/properties/longhands/inherited_ui.mako.rs +++ b/components/style/properties/longhands/inherited_ui.mako.rs @@ -73,13 +73,26 @@ ${helpers.single_keyword( ${helpers.predefined_type( "caret-color", + "color::CaretColor", + "generics::color::CaretColor::auto()", + engines="gecko", + spec="https://drafts.csswg.org/css-ui/#caret-color", + animation_value_type="CaretColor", + boxed=True, + ignored_when_colors_disabled=True, +)} + +${helpers.predefined_type( + "accent-color", "ColorOrAuto", "generics::color::ColorOrAuto::Auto", engines="gecko", - spec="https://drafts.csswg.org/css-ui/#caret-color", - animation_value_type="AnimatedCaretColor", + spec="https://drafts.csswg.org/css-ui-4/#widget-accent", + gecko_pref="layout.css.accent-color.enabled", + animation_value_type="ColorOrAuto", boxed=True, ignored_when_colors_disabled=True, + has_effect_on_gecko_scrollbars=False, )} ${helpers.predefined_type( diff --git a/components/style/values/computed/color.rs b/components/style/values/computed/color.rs index 5fdc29a5feb..664fed95886 100644 --- a/components/style/values/computed/color.rs +++ b/components/style/values/computed/color.rs @@ -6,7 +6,7 @@ use crate::values::animated::color::RGBA as AnimatedRGBA; use crate::values::animated::ToAnimatedValue; -use crate::values::generics::color::{Color as GenericColor, ColorOrAuto as GenericColorOrAuto}; +use crate::values::generics::color::{GenericColor, GenericColorOrAuto, GenericCaretColor}; use cssparser::{Color as CSSParserColor, RGBA}; use std::fmt; use style_traits::{CssWriter, ToCss}; @@ -112,3 +112,6 @@ impl ToAnimatedValue for RGBA { /// auto | pub type ColorOrAuto = GenericColorOrAuto; + +/// caret-color +pub type CaretColor = GenericCaretColor; diff --git a/components/style/values/generics/color.rs b/components/style/values/generics/color.rs index 63c4c401689..5b477dee60d 100644 --- a/components/style/values/generics/color.rs +++ b/components/style/values/generics/color.rs @@ -96,6 +96,7 @@ impl From for Color { ToAnimatedValue, ToAnimatedZero, ToComputedValue, + ToResolvedValue, ToCss, ToShmem, )] @@ -108,3 +109,32 @@ pub enum GenericColorOrAuto { } pub use self::GenericColorOrAuto as ColorOrAuto; + +/// Caret color is effectively a ColorOrAuto, but resolves `auto` to +/// currentColor. +#[derive( + Animate, + Clone, + ComputeSquaredDistance, + Copy, + Debug, + MallocSizeOf, + PartialEq, + SpecifiedValueInfo, + ToAnimatedValue, + ToAnimatedZero, + ToComputedValue, + ToCss, + ToShmem, +)] +#[repr(transparent)] +pub struct GenericCaretColor(pub GenericColorOrAuto); + +impl GenericCaretColor { + /// Returns the `auto` value. + pub fn auto() -> Self { + GenericCaretColor(GenericColorOrAuto::Auto) + } +} + +pub use self::GenericCaretColor as CaretColor; diff --git a/components/style/values/resolved/color.rs b/components/style/values/resolved/color.rs index c098815701c..dbf6375e5bf 100644 --- a/components/style/values/resolved/color.rs +++ b/components/style/values/resolved/color.rs @@ -6,7 +6,7 @@ use super::{Context, ToResolvedValue}; -use crate::values::computed; +use crate::values::computed::color as computed; use crate::values::generics::color as generics; impl ToResolvedValue for computed::Color { @@ -24,14 +24,14 @@ impl ToResolvedValue for computed::Color { } } -impl ToResolvedValue for computed::ColorOrAuto { +impl ToResolvedValue for computed::CaretColor { // A resolved caret-color value is an rgba color, with auto resolving to // currentcolor. type ResolvedValue = cssparser::RGBA; #[inline] fn to_resolved_value(self, context: &Context) -> Self::ResolvedValue { - let color = match self { + let color = match self.0 { generics::ColorOrAuto::Color(color) => color, generics::ColorOrAuto::Auto => generics::Color::currentcolor(), }; @@ -40,6 +40,6 @@ impl ToResolvedValue for computed::ColorOrAuto { #[inline] fn from_resolved_value(resolved: Self::ResolvedValue) -> Self { - generics::ColorOrAuto::Color(computed::Color::from_resolved_value(resolved)) + generics::CaretColor(generics::ColorOrAuto::Color(computed::Color::from_resolved_value(resolved))) } } diff --git a/components/style/values/specified/color.rs b/components/style/values/specified/color.rs index d60f4801a11..7c903c5735b 100644 --- a/components/style/values/specified/color.rs +++ b/components/style/values/specified/color.rs @@ -9,7 +9,7 @@ use super::AllowQuirks; use crate::gecko_bindings::structs::nscolor; use crate::parser::{Parse, ParserContext}; use crate::values::computed::{Color as ComputedColor, Context, ToComputedValue}; -use crate::values::generics::color::ColorOrAuto as GenericColorOrAuto; +use crate::values::generics::color::{GenericColorOrAuto, GenericCaretColor}; use crate::values::specified::calc::CalcNode; use crate::values::specified::Percentage; use cssparser::{AngleOrNumber, Color as CSSParserColor, Parser, Token, RGBA}; @@ -808,3 +808,15 @@ impl Parse for ColorPropertyValue { /// auto | pub type ColorOrAuto = GenericColorOrAuto; + +/// caret-color +pub type CaretColor = GenericCaretColor; + +impl Parse for CaretColor { + fn parse<'i, 't>( + context: &ParserContext, + input: &mut Parser<'i, 't>, + ) -> Result> { + ColorOrAuto::parse(context, input).map(GenericCaretColor) + } +} From 90781493fcd035df2a3a25617fe31d7a0fa8218b Mon Sep 17 00:00:00 2001 From: Oriol Brufau Date: Wed, 17 May 2023 00:35:27 +0200 Subject: [PATCH 57/65] style: Make the -moz-toolbar-prefers-color-scheme a tri-state This will allow detecting the system theme, which allows fixing some of the blocked bugs. Note that when using the system theme we will still match light or dark appropriately, so this shouldn't change behavior just yet. Differential Revision: https://phabricator.services.mozilla.com/D113516 --- components/style/gecko/media_features.rs | 42 +++++++++++++++++++----- 1 file changed, 33 insertions(+), 9 deletions(-) diff --git a/components/style/gecko/media_features.rs b/components/style/gecko/media_features.rs index f6703bc9b3f..4c23620dd15 100644 --- a/components/style/gecko/media_features.rs +++ b/components/style/gecko/media_features.rs @@ -406,18 +406,42 @@ fn eval_prefers_color_scheme(device: &Device, query_value: Option) -> bool { - let prefers_color_scheme = if static_prefs::pref!("browser.theme.dark-toolbar-theme") { - PrefersColorScheme::Dark - } else { - PrefersColorScheme::Light +fn eval_toolbar_prefers_color_scheme(d: &Device, query_value: Option) -> bool { + let toolbar_value = match static_prefs::pref!("browser.theme.toolbar-theme") { + 0 => ToolbarPrefersColorScheme::Dark, + 1 => ToolbarPrefersColorScheme::Light, + _ => ToolbarPrefersColorScheme::System, }; + let query_value = match query_value { + Some(v) => v, + None => return true, + }; + + if query_value == toolbar_value { + return true; + } + + if toolbar_value != ToolbarPrefersColorScheme::System { + return false; + } + + // System might match light and dark as well. match query_value { - Some(v) => prefers_color_scheme == v, - None => true, + ToolbarPrefersColorScheme::Dark => eval_prefers_color_scheme(d, Some(PrefersColorScheme::Dark)), + ToolbarPrefersColorScheme::Light => eval_prefers_color_scheme(d, Some(PrefersColorScheme::Light)), + ToolbarPrefersColorScheme::System => true, } } @@ -617,7 +641,7 @@ macro_rules! lnf_int_feature { /// In order to use them you need to make sure that the pref defined as a static /// pref, with `rust: true`. The feature name needs to be defined in /// `StaticAtoms.py` just like the others. In order to support dynamic changes, -/// you also need to add them to kBoolMediaQueryPrefs in nsXPLookAndFeel.cpp +/// you also need to add them to kMediaQueryPrefs in nsXPLookAndFeel.cpp macro_rules! bool_pref_feature { ($feature_name:expr, $pref:tt) => {{ fn __eval(_: &Device, query_value: Option, _: Option) -> bool { @@ -852,7 +876,7 @@ pub static MEDIA_FEATURES: [MediaFeatureDescription; 62] = [ feature!( atom!("-moz-toolbar-prefers-color-scheme"), AllowsRanges::No, - keyword_evaluator!(eval_toolbar_prefers_color_scheme, PrefersColorScheme), + keyword_evaluator!(eval_toolbar_prefers_color_scheme, ToolbarPrefersColorScheme), ParsingRequirements::CHROME_AND_UA_ONLY, ), From fb9d8ddc73c3d7709d174724f68e92464128ff33 Mon Sep 17 00:00:00 2001 From: Oriol Brufau Date: Wed, 17 May 2023 00:36:44 +0200 Subject: [PATCH 58/65] style: Make sure to consume the flusher when we hit the cascade data cache So that sheets are properly marked as committed. The issue is that we have this "committed" optimization, to avoid doing work if somebody adds and removes an stylesheet without us flushing in the meantime: https://searchfox.org/mozilla-central/rev/f018480dfed4fc583703a5770a6db9ab9dc0fb99/servo/components/style/stylesheet_set.rs#308-319 The "committed" bit is set when we consume the flusher (in each(..)). However when we hit the cache, before this patch, we wouldn't consume it, which means that we may fail to do some full rebuilds even though we need them. Fix it by making sure we call each() in that case. We add a test (the test would show a red square before this patch, and a lime square with the fix). Differential Revision: https://phabricator.services.mozilla.com/D113206 --- components/style/stylist.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/components/style/stylist.rs b/components/style/stylist.rs index c4eebefeab9..74704e707df 100644 --- a/components/style/stylist.rs +++ b/components/style/stylist.rs @@ -138,6 +138,9 @@ where debug!(" > {:?}", sheet); } } + // The line below ensures the "committed" bit is updated properly + // below. + collection.each(|_, _| true); return Ok(Some(entry.clone())); } From 43cec05c04f705a5bab0f3689b9268fe668227b9 Mon Sep 17 00:00:00 2001 From: Oriol Brufau Date: Wed, 17 May 2023 00:37:57 +0200 Subject: [PATCH 59/65] style: Don't allow whitespace between media query operator delim tokens Differential Revision: https://phabricator.services.mozilla.com/D113648 --- .../media_queries/media_feature_expression.rs | 46 ++++++++++++------- 1 file changed, 30 insertions(+), 16 deletions(-) diff --git a/components/style/media_queries/media_feature_expression.rs b/components/style/media_queries/media_feature_expression.rs index 10bf16500ef..1658439da21 100644 --- a/components/style/media_queries/media_feature_expression.rs +++ b/components/style/media_queries/media_feature_expression.rs @@ -196,23 +196,37 @@ fn consume_operation_or_colon(input: &mut Parser) -> Result, () _ => return Err(()), } }; - Ok(Some(match first_delim { - '=' => Operator::Equal, - '>' => { - if input.try_parse(|i| i.expect_delim('=')).is_ok() { - Operator::GreaterThanEqual - } else { - Operator::GreaterThan - } - }, - '<' => { - if input.try_parse(|i| i.expect_delim('=')).is_ok() { - Operator::LessThanEqual - } else { - Operator::LessThan - } - }, + let operator = match first_delim { + '=' => return Ok(Some(Operator::Equal)), + '>' => Operator::GreaterThan, + '<' => Operator::LessThan, _ => return Err(()), + }; + + // https://drafts.csswg.org/mediaqueries-4/#mq-syntax: + // + // No whitespace is allowed between the “<” or “>” + // s and the following “=” , if it’s + // present. + // + // TODO(emilio): Maybe we should ignore comments as well? + // https://github.com/w3c/csswg-drafts/issues/6248 + let parsed_equal = input.try_parse(|i| { + let t = i.next_including_whitespace().map_err(|_| ())?; + if !matches!(t, Token::Delim('=')) { + return Err(()) + } + Ok(()) + }).is_ok(); + + if !parsed_equal { + return Ok(Some(operator)); + } + + Ok(Some(match operator { + Operator::GreaterThan => Operator::GreaterThanEqual, + Operator::LessThan => Operator::LessThanEqual, + _ => unreachable!(), })) } From 516c7a304a78adf4eb8c2cd7c457de7a0ef93f8c Mon Sep 17 00:00:00 2001 From: Oriol Brufau Date: Wed, 17 May 2023 00:40:54 +0200 Subject: [PATCH 60/65] style: Use saturating addition for math-depth Differential Revision: https://phabricator.services.mozilla.com/D114070 --- components/style/values/computed/font.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/components/style/values/computed/font.rs b/components/style/values/computed/font.rs index df6a8ed667e..0bd177bcc85 100644 --- a/components/style/values/computed/font.rs +++ b/components/style/values/computed/font.rs @@ -828,14 +828,14 @@ impl ToComputedValue for specified::MathDepth { let parent = cx.builder.get_parent_font().clone_math_depth() as i32; let style = cx.builder.get_parent_font().clone_math_style(); if style == MathStyleValue::Compact { - parent + 1 + parent.saturating_add(1) } else { parent } }, specified::MathDepth::Add(rel) => { let parent = cx.builder.get_parent_font().clone_math_depth(); - parent as i32 + rel.to_computed_value(cx) + (parent as i32).saturating_add(rel.to_computed_value(cx)) }, specified::MathDepth::Absolute(abs) => abs.to_computed_value(cx), }; From 60867bbe7ee0fea65174b904a7095e4cc8a8a87d Mon Sep 17 00:00:00 2001 From: Oriol Brufau Date: Wed, 17 May 2023 00:41:54 +0200 Subject: [PATCH 61/65] style: Remove unused NS_CHANGE_COLOR_IF_SAME_AS_BG It was introduced in bug 1024930, but it is unused since. Differential Revision: https://phabricator.services.mozilla.com/D114389 --- components/style/values/specified/color.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/components/style/values/specified/color.rs b/components/style/values/specified/color.rs index 7c903c5735b..773a93366b8 100644 --- a/components/style/values/specified/color.rs +++ b/components/style/values/specified/color.rs @@ -186,8 +186,6 @@ pub enum SystemColor { #[css(skip)] TextSelectForeground, #[css(skip)] - TextSelectForegroundCustom, - #[css(skip)] TextSelectBackgroundDisabled, #[css(skip)] TextSelectBackgroundAttention, From 1cc44bd06536f0b9eaa0ae05a30448040155ec65 Mon Sep 17 00:00:00 2001 From: Oriol Brufau Date: Wed, 17 May 2023 00:44:00 +0200 Subject: [PATCH 62/65] style: Will-change should only create stacking contexts / containing blocks / etc if the property it'd change would apply As per https://drafts.csswg.org/css-will-change/#will-change. > If any non-initial value of a property would cause the element to > generate a containing block for absolutely positioned elements, > specifying that property in will-change must cause the element to > generate a containing block for absolutely positioned elements. But in this case the transform property wouldn't apply to the element so there's no reason to create a stacking-context. Differential Revision: https://phabricator.services.mozilla.com/D114121 --- .../style/properties/longhands/box.mako.rs | 15 ++--- .../properties/longhands/effects.mako.rs | 5 +- .../properties/longhands/position.mako.rs | 1 - .../style/properties/longhands/svg.mako.rs | 2 - .../style/properties/properties.mako.rs | 20 ++---- components/style/values/specified/box.rs | 64 +++++++++++-------- 6 files changed, 49 insertions(+), 58 deletions(-) diff --git a/components/style/properties/longhands/box.mako.rs b/components/style/properties/longhands/box.mako.rs index 73a2ec87065..f2d0234a918 100644 --- a/components/style/properties/longhands/box.mako.rs +++ b/components/style/properties/longhands/box.mako.rs @@ -48,7 +48,6 @@ ${helpers.single_keyword( engines="gecko servo-2013 servo-2020" animation_value_type="discrete" gecko_enum_prefix="StylePositionProperty" - flags="CREATES_STACKING_CONTEXT ABSPOS_CB" spec="https://drafts.csswg.org/css-position/#position-property" servo_restyle_damage="rebuild_and_reflow" > @@ -330,7 +329,7 @@ ${helpers.predefined_type( engines="gecko servo-2013 servo-2020", extra_prefixes=transform_extra_prefixes, animation_value_type="ComputedValue", - flags="CREATES_STACKING_CONTEXT FIXPOS_CB CAN_ANIMATE_ON_COMPOSITOR", + flags="CAN_ANIMATE_ON_COMPOSITOR", spec="https://drafts.csswg.org/css-transforms/#propdef-transform", servo_restyle_damage="reflow_out_of_flow", )} @@ -342,7 +341,7 @@ ${helpers.predefined_type( engines="gecko servo-2013", animation_value_type="ComputedValue", boxed=True, - flags="CREATES_STACKING_CONTEXT FIXPOS_CB CAN_ANIMATE_ON_COMPOSITOR", + flags="CAN_ANIMATE_ON_COMPOSITOR", gecko_pref="layout.css.individual-transform.enabled", spec="https://drafts.csswg.org/css-transforms-2/#individual-transforms", servo_restyle_damage = "reflow_out_of_flow", @@ -355,7 +354,7 @@ ${helpers.predefined_type( engines="gecko servo-2013", animation_value_type="ComputedValue", boxed=True, - flags="CREATES_STACKING_CONTEXT FIXPOS_CB CAN_ANIMATE_ON_COMPOSITOR", + flags="CAN_ANIMATE_ON_COMPOSITOR", gecko_pref="layout.css.individual-transform.enabled", spec="https://drafts.csswg.org/css-transforms-2/#individual-transforms", servo_restyle_damage = "reflow_out_of_flow", @@ -368,7 +367,7 @@ ${helpers.predefined_type( engines="gecko servo-2013", animation_value_type="ComputedValue", boxed=True, - flags="CREATES_STACKING_CONTEXT FIXPOS_CB CAN_ANIMATE_ON_COMPOSITOR", + flags="CAN_ANIMATE_ON_COMPOSITOR", gecko_pref="layout.css.individual-transform.enabled", spec="https://drafts.csswg.org/css-transforms-2/#individual-transforms", servo_restyle_damage="reflow_out_of_flow", @@ -382,7 +381,7 @@ ${helpers.predefined_type( engines="gecko", animation_value_type="ComputedValue", gecko_pref="layout.css.motion-path.enabled", - flags="CREATES_STACKING_CONTEXT FIXPOS_CB CAN_ANIMATE_ON_COMPOSITOR", + flags="CAN_ANIMATE_ON_COMPOSITOR", spec="https://drafts.fxtf.org/motion-1/#offset-path-property", servo_restyle_damage="reflow_out_of_flow" )} @@ -477,7 +476,6 @@ ${helpers.single_keyword( "auto isolate", engines="gecko", spec="https://drafts.fxtf.org/compositing/#isolation", - flags="CREATES_STACKING_CONTEXT", gecko_enum_prefix="StyleIsolation", animation_value_type="discrete", )} @@ -530,7 +528,6 @@ ${helpers.predefined_type( gecko_ffi_name="mChildPerspective", spec="https://drafts.csswg.org/css-transforms/#perspective", extra_prefixes=transform_extra_prefixes, - flags="CREATES_STACKING_CONTEXT FIXPOS_CB", animation_value_type="AnimatedPerspective", servo_restyle_damage = "reflow_out_of_flow", )} @@ -574,7 +571,6 @@ ${helpers.predefined_type( engines="gecko servo-2013 servo-2020", spec="https://drafts.csswg.org/css-transforms-2/#transform-style-property", extra_prefixes=transform_extra_prefixes, - flags="CREATES_STACKING_CONTEXT FIXPOS_CB", animation_value_type="discrete", servo_restyle_damage = "reflow_out_of_flow", )} @@ -598,7 +594,6 @@ ${helpers.predefined_type( "specified::Contain::empty()", engines="gecko", animation_value_type="none", - flags="CREATES_STACKING_CONTEXT FIXPOS_CB", spec="https://drafts.csswg.org/css-contain/#contain-property", )} diff --git a/components/style/properties/longhands/effects.mako.rs b/components/style/properties/longhands/effects.mako.rs index 00ede146cd4..5470c74d47c 100644 --- a/components/style/properties/longhands/effects.mako.rs +++ b/components/style/properties/longhands/effects.mako.rs @@ -13,7 +13,7 @@ ${helpers.predefined_type( "1.0", engines="gecko servo-2013 servo-2020", animation_value_type="ComputedValue", - flags="CREATES_STACKING_CONTEXT CAN_ANIMATE_ON_COMPOSITOR", + flags="CAN_ANIMATE_ON_COMPOSITOR", spec="https://drafts.csswg.org/css-color/#transparency", servo_restyle_damage = "reflow_out_of_flow", )} @@ -56,7 +56,6 @@ ${helpers.predefined_type( animation_value_type="AnimatedFilterList", vector_animation_type="with_zero", extra_prefixes="webkit", - flags="CREATES_STACKING_CONTEXT FIXPOS_CB", spec="https://drafts.fxtf.org/filters/#propdef-filter", )} @@ -71,7 +70,6 @@ ${helpers.predefined_type( separator="Space", animation_value_type="AnimatedFilterList", vector_animation_type="with_zero", - flags="CREATES_STACKING_CONTEXT FIXPOS_CB", gecko_pref="layout.css.backdrop-filter.enabled", spec="https://drafts.fxtf.org/filter-effects-2/#propdef-backdrop-filter", )} @@ -84,6 +82,5 @@ ${helpers.single_keyword( engines="gecko servo-2013 servo-2020", gecko_enum_prefix="StyleBlend", animation_value_type="discrete", - flags="CREATES_STACKING_CONTEXT", spec="https://drafts.fxtf.org/compositing/#propdef-mix-blend-mode", )} diff --git a/components/style/properties/longhands/position.mako.rs b/components/style/properties/longhands/position.mako.rs index df118faed1a..1795bde7e5f 100644 --- a/components/style/properties/longhands/position.mako.rs +++ b/components/style/properties/longhands/position.mako.rs @@ -60,7 +60,6 @@ ${helpers.predefined_type( "computed::ZIndex::auto()", engines="gecko servo-2013 servo-2020", spec="https://www.w3.org/TR/CSS2/visuren.html#z-index", - flags="CREATES_STACKING_CONTEXT", animation_value_type="ComputedValue", )} diff --git a/components/style/properties/longhands/svg.mako.rs b/components/style/properties/longhands/svg.mako.rs index a09f3e7b656..f6128693582 100644 --- a/components/style/properties/longhands/svg.mako.rs +++ b/components/style/properties/longhands/svg.mako.rs @@ -81,7 +81,6 @@ ${helpers.predefined_type( "generics::basic_shape::ClipPath::None", engines="gecko", animation_value_type="basic_shape::ClipPath", - flags="CREATES_STACKING_CONTEXT", spec="https://drafts.fxtf.org/css-masking/#propdef-clip-path", )} @@ -183,7 +182,6 @@ ${helpers.predefined_type( vector=True, extra_prefixes="webkit", animation_value_type="discrete", - flags="CREATES_STACKING_CONTEXT", )} ${helpers.predefined_type( diff --git a/components/style/properties/properties.mako.rs b/components/style/properties/properties.mako.rs index ea3e043ea49..99d283da46c 100644 --- a/components/style/properties/properties.mako.rs +++ b/components/style/properties/properties.mako.rs @@ -1068,28 +1068,20 @@ impl CSSWideKeyword { bitflags! { /// A set of flags for properties. pub struct PropertyFlags: u16 { - /// This property requires a stacking context. - const CREATES_STACKING_CONTEXT = 1 << 0; - /// This property has values that can establish a containing block for - /// fixed positioned and absolutely positioned elements. - const FIXPOS_CB = 1 << 1; - /// This property has values that can establish a containing block for - /// absolutely positioned elements. - const ABSPOS_CB = 1 << 2; /// This longhand property applies to ::first-letter. - const APPLIES_TO_FIRST_LETTER = 1 << 3; + const APPLIES_TO_FIRST_LETTER = 1 << 1; /// This longhand property applies to ::first-line. - const APPLIES_TO_FIRST_LINE = 1 << 4; + const APPLIES_TO_FIRST_LINE = 1 << 2; /// This longhand property applies to ::placeholder. - const APPLIES_TO_PLACEHOLDER = 1 << 5; + const APPLIES_TO_PLACEHOLDER = 1 << 3; /// This longhand property applies to ::cue. - const APPLIES_TO_CUE = 1 << 6; + const APPLIES_TO_CUE = 1 << 4; /// This longhand property applies to ::marker. - const APPLIES_TO_MARKER = 1 << 7; + const APPLIES_TO_MARKER = 1 << 5; /// This property is a legacy shorthand. /// /// https://drafts.csswg.org/css-cascade/#legacy-shorthand - const IS_LEGACY_SHORTHAND = 1 << 8; + const IS_LEGACY_SHORTHAND = 1 << 6; /* The following flags are currently not used in Rust code, they * only need to be listed in corresponding properties so that diff --git a/components/style/values/specified/box.rs b/components/style/values/specified/box.rs index 3039a990ff9..06a4e1ea869 100644 --- a/components/style/values/specified/box.rs +++ b/components/style/values/specified/box.rs @@ -6,7 +6,7 @@ use crate::custom_properties::Name as CustomPropertyName; use crate::parser::{Parse, ParserContext}; -use crate::properties::{LonghandId, PropertyDeclarationId, PropertyFlags}; +use crate::properties::{LonghandId, PropertyDeclarationId}; use crate::properties::{PropertyId, ShorthandId}; use crate::values::generics::box_::AnimationIterationCount as GenericAnimationIterationCount; use crate::values::generics::box_::Perspective as GenericPerspective; @@ -1086,44 +1086,54 @@ bitflags! { /// The change bits that we care about. #[derive(Default, MallocSizeOf, SpecifiedValueInfo, ToComputedValue, ToResolvedValue, ToShmem)] #[repr(C)] - pub struct WillChangeBits: u8 { - /// Whether the stacking context will change. - const STACKING_CONTEXT = 1 << 0; - /// Whether `transform` will change. + pub struct WillChangeBits: u16 { + /// Whether a property which can create a stacking context **on any + /// box** will change. + const STACKING_CONTEXT_UNCONDITIONAL = 1 << 0; + /// Whether `transform` or related properties will change. const TRANSFORM = 1 << 1; /// Whether `scroll-position` will change. const SCROLL = 1 << 2; + /// Whether `contain` will change. + const CONTAIN = 1 << 3; /// Whether `opacity` will change. - const OPACITY = 1 << 3; - /// Fixed pos containing block. - const FIXPOS_CB = 1 << 4; - /// Abs pos containing block. - const ABSPOS_CB = 1 << 5; + const OPACITY = 1 << 4; + /// Whether `perspective` will change. + const PERSPECTIVE = 1 << 5; + /// Whether `z-index` will change. + const Z_INDEX = 1 << 6; + /// Whether any property which creates a containing block for non-svg + /// text frames will change. + const FIXPOS_CB_NON_SVG = 1 << 7; + /// Whether the position property will change. + const POSITION = 1 << 8; } } fn change_bits_for_longhand(longhand: LonghandId) -> WillChangeBits { - let mut flags = match longhand { + match longhand { LonghandId::Opacity => WillChangeBits::OPACITY, - LonghandId::Transform => WillChangeBits::TRANSFORM, - #[cfg(feature = "gecko")] - LonghandId::Translate | LonghandId::Rotate | LonghandId::Scale | LonghandId::OffsetPath => { - WillChangeBits::TRANSFORM + LonghandId::Contain => WillChangeBits::CONTAIN, + LonghandId::Perspective => WillChangeBits::PERSPECTIVE, + LonghandId::Position => { + WillChangeBits::STACKING_CONTEXT_UNCONDITIONAL | WillChangeBits::POSITION }, + LonghandId::ZIndex => WillChangeBits::Z_INDEX, + LonghandId::Transform | + LonghandId::TransformStyle | + LonghandId::Translate | + LonghandId::Rotate | + LonghandId::Scale | + LonghandId::OffsetPath => WillChangeBits::TRANSFORM, + LonghandId::BackdropFilter | LonghandId::Filter => { + WillChangeBits::STACKING_CONTEXT_UNCONDITIONAL | WillChangeBits::FIXPOS_CB_NON_SVG + }, + LonghandId::MixBlendMode | + LonghandId::Isolation | + LonghandId::MaskImage | + LonghandId::ClipPath => WillChangeBits::STACKING_CONTEXT_UNCONDITIONAL, _ => WillChangeBits::empty(), - }; - - let property_flags = longhand.flags(); - if property_flags.contains(PropertyFlags::CREATES_STACKING_CONTEXT) { - flags |= WillChangeBits::STACKING_CONTEXT; } - if property_flags.contains(PropertyFlags::FIXPOS_CB) { - flags |= WillChangeBits::FIXPOS_CB; - } - if property_flags.contains(PropertyFlags::ABSPOS_CB) { - flags |= WillChangeBits::ABSPOS_CB; - } - flags } fn change_bits_for_maybe_property(ident: &str, context: &ParserContext) -> WillChangeBits { From aea14a247040301b9674d48d46a9b72dffc49f71 Mon Sep 17 00:00:00 2001 From: Oriol Brufau Date: Wed, 17 May 2023 01:35:55 +0200 Subject: [PATCH 63/65] Further changes required by Servo --- components/style/values/specified/box.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/components/style/values/specified/box.rs b/components/style/values/specified/box.rs index 06a4e1ea869..32f4f9bacae 100644 --- a/components/style/values/specified/box.rs +++ b/components/style/values/specified/box.rs @@ -1110,6 +1110,7 @@ bitflags! { } } +#[cfg(feature = "gecko")] fn change_bits_for_longhand(longhand: LonghandId) -> WillChangeBits { match longhand { LonghandId::Opacity => WillChangeBits::OPACITY, @@ -1136,6 +1137,7 @@ fn change_bits_for_longhand(longhand: LonghandId) -> WillChangeBits { } } +#[cfg(feature = "gecko")] fn change_bits_for_maybe_property(ident: &str, context: &ParserContext) -> WillChangeBits { let id = match PropertyId::parse_ignoring_rule_type(ident, context) { Ok(id) => id, @@ -1153,6 +1155,7 @@ fn change_bits_for_maybe_property(ident: &str, context: &ParserContext) -> WillC } } +#[cfg(feature = "gecko")] impl Parse for WillChange { /// auto | # fn parse<'i, 't>( From f42e2fb08f9488682c39b96c1d4126a6bfa4be0f Mon Sep 17 00:00:00 2001 From: Oriol Brufau Date: Thu, 18 May 2023 17:20:17 +0200 Subject: [PATCH 64/65] Avoid complaints from ./mach test-tidy --- servo-tidy.toml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/servo-tidy.toml b/servo-tidy.toml index 19dbd3e2c42..5dff9548b33 100644 --- a/servo-tidy.toml +++ b/servo-tidy.toml @@ -99,6 +99,11 @@ packages = [ # Files that are ignored for all tidy and lint checks. files = [ "./components/net/tests/parsable_mime/text", + # These are ignored to avoid diverging from Gecko + "./components/style/properties/helpers.mako.rs", + "./components/style/stylesheets/rule_parser.rs", + "./components/style/stylist.rs", + "./components/style/values/computed/image.rs", # Mako does not lend itself easily to splitting long lines "./components/style/properties/helpers/animated_properties.mako.rs", "./components/style/properties/shorthands/text.mako.rs", From dbb92b99ad359634e8b84417b8264f84bfc5d056 Mon Sep 17 00:00:00 2001 From: Oriol Brufau Date: Wed, 17 May 2023 13:04:37 +0200 Subject: [PATCH 65/65] Update test expectations --- .../css-values/clamp-length-computed.html.ini | 12 - .../css/css-text/inheritance.html.ini | 3 - .../text-justify-computed-legacy.html.ini | 3 - .../parsing/text-justify-computed.html.ini | 4 - .../parsing/text-justify-valid.html.ini | 4 - .../distribute-alias.tentative.html.ini | 3 - .../text-justify-interpolation.html.ini | 225 ------------------ .../css-values/clamp-length-computed.html.ini | 12 - .../mediaqueries/test_media_queries.html.ini | 6 + 9 files changed, 6 insertions(+), 266 deletions(-) delete mode 100644 tests/wpt/metadata-layout-2020/css/css-values/clamp-length-computed.html.ini delete mode 100644 tests/wpt/metadata/css/css-text/parsing/text-justify-computed-legacy.html.ini delete mode 100644 tests/wpt/metadata/css/css-text/parsing/text-justify-valid.html.ini delete mode 100644 tests/wpt/metadata/css/css-text/text-justify/distribute-alias.tentative.html.ini diff --git a/tests/wpt/metadata-layout-2020/css/css-values/clamp-length-computed.html.ini b/tests/wpt/metadata-layout-2020/css/css-values/clamp-length-computed.html.ini deleted file mode 100644 index 215c616a975..00000000000 --- a/tests/wpt/metadata-layout-2020/css/css-values/clamp-length-computed.html.ini +++ /dev/null @@ -1,12 +0,0 @@ -[clamp-length-computed.html] - [Property letter-spacing value 'clamp(10px, 35px , 30px)'] - expected: FAIL - - [Property letter-spacing value 'clamp(10px, 35px /*foo*/, 30px)'] - expected: FAIL - - [Property letter-spacing value 'clamp(10px /* foo */ , 35px, 30px)'] - expected: FAIL - - [Property letter-spacing value 'clamp(10px , 35px, 30px)'] - expected: FAIL diff --git a/tests/wpt/metadata/css/css-text/inheritance.html.ini b/tests/wpt/metadata/css/css-text/inheritance.html.ini index f8cb0572c62..151ed41303a 100644 --- a/tests/wpt/metadata/css/css-text/inheritance.html.ini +++ b/tests/wpt/metadata/css/css-text/inheritance.html.ini @@ -1,7 +1,4 @@ [inheritance.html] - [Property text-justify inherits] - expected: FAIL - [Property text-align-all has initial value start] expected: FAIL diff --git a/tests/wpt/metadata/css/css-text/parsing/text-justify-computed-legacy.html.ini b/tests/wpt/metadata/css/css-text/parsing/text-justify-computed-legacy.html.ini deleted file mode 100644 index 210a79f60ac..00000000000 --- a/tests/wpt/metadata/css/css-text/parsing/text-justify-computed-legacy.html.ini +++ /dev/null @@ -1,3 +0,0 @@ -[text-justify-computed-legacy.html] - [Property text-justify value 'distribute'] - expected: FAIL diff --git a/tests/wpt/metadata/css/css-text/parsing/text-justify-computed.html.ini b/tests/wpt/metadata/css/css-text/parsing/text-justify-computed.html.ini index ae897a3a05a..267731c47cc 100644 --- a/tests/wpt/metadata/css/css-text/parsing/text-justify-computed.html.ini +++ b/tests/wpt/metadata/css/css-text/parsing/text-justify-computed.html.ini @@ -1,7 +1,3 @@ [text-justify-computed.html] [Property text-justify value 'inter-character' computes to 'inter-character'] expected: FAIL - - [Property text-justify value 'inter-character'] - expected: FAIL - diff --git a/tests/wpt/metadata/css/css-text/parsing/text-justify-valid.html.ini b/tests/wpt/metadata/css/css-text/parsing/text-justify-valid.html.ini deleted file mode 100644 index 96b9e419553..00000000000 --- a/tests/wpt/metadata/css/css-text/parsing/text-justify-valid.html.ini +++ /dev/null @@ -1,4 +0,0 @@ -[text-justify-valid.html] - [e.style['text-justify'\] = "inter-character" should set the property value] - expected: FAIL - diff --git a/tests/wpt/metadata/css/css-text/text-justify/distribute-alias.tentative.html.ini b/tests/wpt/metadata/css/css-text/text-justify/distribute-alias.tentative.html.ini deleted file mode 100644 index 053373e177d..00000000000 --- a/tests/wpt/metadata/css/css-text/text-justify/distribute-alias.tentative.html.ini +++ /dev/null @@ -1,3 +0,0 @@ -[distribute-alias.tentative.html] - [text-justify: distribute is a parse-time alias of inter-character] - expected: FAIL diff --git a/tests/wpt/metadata/css/css-text/text-justify/text-justify-interpolation.html.ini b/tests/wpt/metadata/css/css-text/text-justify/text-justify-interpolation.html.ini index 9ddf45dbf47..b03f0bf0df1 100644 --- a/tests/wpt/metadata/css/css-text/text-justify/text-justify-interpolation.html.ini +++ b/tests/wpt/metadata/css/css-text/text-justify/text-justify-interpolation.html.ini @@ -47,18 +47,6 @@ [CSS Transitions: property from [auto\] to [inter-character\] at (0.3) should be [inter-character\]] expected: FAIL - [CSS Transitions: property from [auto\] to [inter-character\] at (0.5) should be [inter-character\]] - expected: FAIL - - [CSS Transitions: property from [auto\] to [inter-character\] at (0.6) should be [inter-character\]] - expected: FAIL - - [CSS Transitions: property from [auto\] to [inter-character\] at (1) should be [inter-character\]] - expected: FAIL - - [CSS Transitions: property from [auto\] to [inter-character\] at (1.5) should be [inter-character\]] - expected: FAIL - [CSS Transitions with transition: all: property from [auto\] to [inter-character\] at (-0.3) should be [inter-character\]] expected: FAIL @@ -68,39 +56,6 @@ [CSS Transitions with transition: all: property from [auto\] to [inter-character\] at (0.3) should be [inter-character\]] expected: FAIL - [CSS Transitions with transition: all: property from [auto\] to [inter-character\] at (0.5) should be [inter-character\]] - expected: FAIL - - [CSS Transitions with transition: all: property from [auto\] to [inter-character\] at (0.6) should be [inter-character\]] - expected: FAIL - - [CSS Transitions with transition: all: property from [auto\] to [inter-character\] at (1) should be [inter-character\]] - expected: FAIL - - [CSS Transitions with transition: all: property from [auto\] to [inter-character\] at (1.5) should be [inter-character\]] - expected: FAIL - - [CSS Animations: property from [auto\] to [inter-character\] at (-0.3) should be [auto\]] - expected: FAIL - - [CSS Animations: property from [auto\] to [inter-character\] at (0) should be [auto\]] - expected: FAIL - - [CSS Animations: property from [auto\] to [inter-character\] at (0.3) should be [auto\]] - expected: FAIL - - [CSS Animations: property from [auto\] to [inter-character\] at (0.5) should be [inter-character\]] - expected: FAIL - - [CSS Animations: property from [auto\] to [inter-character\] at (0.6) should be [inter-character\]] - expected: FAIL - - [CSS Animations: property from [auto\] to [inter-character\] at (1) should be [inter-character\]] - expected: FAIL - - [CSS Animations: property from [auto\] to [inter-character\] at (1.5) should be [inter-character\]] - expected: FAIL - [Web Animations: property from [auto\] to [inter-character\] at (-0.3) should be [auto\]] expected: FAIL @@ -131,60 +86,6 @@ [CSS Transitions: property from [inter-character\] to [distribute\] at (0.3) should be [distribute\]] expected: FAIL - [CSS Transitions: property from [inter-character\] to [distribute\] at (0.5) should be [distribute\]] - expected: FAIL - - [CSS Transitions: property from [inter-character\] to [distribute\] at (0.6) should be [distribute\]] - expected: FAIL - - [CSS Transitions: property from [inter-character\] to [distribute\] at (1) should be [distribute\]] - expected: FAIL - - [CSS Transitions: property from [inter-character\] to [distribute\] at (1.5) should be [distribute\]] - expected: FAIL - - [CSS Transitions with transition: all: property from [inter-character\] to [distribute\] at (-0.3) should be [distribute\]] - expected: FAIL - - [CSS Transitions with transition: all: property from [inter-character\] to [distribute\] at (0) should be [distribute\]] - expected: FAIL - - [CSS Transitions with transition: all: property from [inter-character\] to [distribute\] at (0.3) should be [distribute\]] - expected: FAIL - - [CSS Transitions with transition: all: property from [inter-character\] to [distribute\] at (0.5) should be [distribute\]] - expected: FAIL - - [CSS Transitions with transition: all: property from [inter-character\] to [distribute\] at (0.6) should be [distribute\]] - expected: FAIL - - [CSS Transitions with transition: all: property from [inter-character\] to [distribute\] at (1) should be [distribute\]] - expected: FAIL - - [CSS Transitions with transition: all: property from [inter-character\] to [distribute\] at (1.5) should be [distribute\]] - expected: FAIL - - [CSS Animations: property from [inter-character\] to [distribute\] at (-0.3) should be [inter-character\]] - expected: FAIL - - [CSS Animations: property from [inter-character\] to [distribute\] at (0) should be [inter-character\]] - expected: FAIL - - [CSS Animations: property from [inter-character\] to [distribute\] at (0.3) should be [inter-character\]] - expected: FAIL - - [CSS Animations: property from [inter-character\] to [distribute\] at (0.5) should be [distribute\]] - expected: FAIL - - [CSS Animations: property from [inter-character\] to [distribute\] at (0.6) should be [distribute\]] - expected: FAIL - - [CSS Animations: property from [inter-character\] to [distribute\] at (1) should be [distribute\]] - expected: FAIL - - [CSS Animations: property from [inter-character\] to [distribute\] at (1.5) should be [distribute\]] - expected: FAIL - [Web Animations: property from [inter-character\] to [distribute\] at (-0.3) should be [inter-character\]] expected: FAIL @@ -215,18 +116,6 @@ [CSS Transitions: property from [inter-word\] to [distribute\] at (0.3) should be [distribute\]] expected: FAIL - [CSS Transitions: property from [inter-word\] to [distribute\] at (0.5) should be [distribute\]] - expected: FAIL - - [CSS Transitions: property from [inter-word\] to [distribute\] at (0.6) should be [distribute\]] - expected: FAIL - - [CSS Transitions: property from [inter-word\] to [distribute\] at (1) should be [distribute\]] - expected: FAIL - - [CSS Transitions: property from [inter-word\] to [distribute\] at (1.5) should be [distribute\]] - expected: FAIL - [CSS Transitions with transition: all: property from [inter-word\] to [distribute\] at (-0.3) should be [distribute\]] expected: FAIL @@ -236,39 +125,6 @@ [CSS Transitions with transition: all: property from [inter-word\] to [distribute\] at (0.3) should be [distribute\]] expected: FAIL - [CSS Transitions with transition: all: property from [inter-word\] to [distribute\] at (0.5) should be [distribute\]] - expected: FAIL - - [CSS Transitions with transition: all: property from [inter-word\] to [distribute\] at (0.6) should be [distribute\]] - expected: FAIL - - [CSS Transitions with transition: all: property from [inter-word\] to [distribute\] at (1) should be [distribute\]] - expected: FAIL - - [CSS Transitions with transition: all: property from [inter-word\] to [distribute\] at (1.5) should be [distribute\]] - expected: FAIL - - [CSS Animations: property from [inter-word\] to [distribute\] at (-0.3) should be [inter-word\]] - expected: FAIL - - [CSS Animations: property from [inter-word\] to [distribute\] at (0) should be [inter-word\]] - expected: FAIL - - [CSS Animations: property from [inter-word\] to [distribute\] at (0.3) should be [inter-word\]] - expected: FAIL - - [CSS Animations: property from [inter-word\] to [distribute\] at (0.5) should be [distribute\]] - expected: FAIL - - [CSS Animations: property from [inter-word\] to [distribute\] at (0.6) should be [distribute\]] - expected: FAIL - - [CSS Animations: property from [inter-word\] to [distribute\] at (1) should be [distribute\]] - expected: FAIL - - [CSS Animations: property from [inter-word\] to [distribute\] at (1.5) should be [distribute\]] - expected: FAIL - [Web Animations: property from [inter-word\] to [distribute\] at (-0.3) should be [inter-word\]] expected: FAIL @@ -299,18 +155,6 @@ [CSS Transitions: property from [distribute\] to [none\] at (0.3) should be [none\]] expected: FAIL - [CSS Transitions: property from [distribute\] to [none\] at (0.5) should be [none\]] - expected: FAIL - - [CSS Transitions: property from [distribute\] to [none\] at (0.6) should be [none\]] - expected: FAIL - - [CSS Transitions: property from [distribute\] to [none\] at (1) should be [none\]] - expected: FAIL - - [CSS Transitions: property from [distribute\] to [none\] at (1.5) should be [none\]] - expected: FAIL - [CSS Transitions with transition: all: property from [distribute\] to [none\] at (-0.3) should be [none\]] expected: FAIL @@ -320,39 +164,6 @@ [CSS Transitions with transition: all: property from [distribute\] to [none\] at (0.3) should be [none\]] expected: FAIL - [CSS Transitions with transition: all: property from [distribute\] to [none\] at (0.5) should be [none\]] - expected: FAIL - - [CSS Transitions with transition: all: property from [distribute\] to [none\] at (0.6) should be [none\]] - expected: FAIL - - [CSS Transitions with transition: all: property from [distribute\] to [none\] at (1) should be [none\]] - expected: FAIL - - [CSS Transitions with transition: all: property from [distribute\] to [none\] at (1.5) should be [none\]] - expected: FAIL - - [CSS Animations: property from [distribute\] to [none\] at (-0.3) should be [distribute\]] - expected: FAIL - - [CSS Animations: property from [distribute\] to [none\] at (0) should be [distribute\]] - expected: FAIL - - [CSS Animations: property from [distribute\] to [none\] at (0.3) should be [distribute\]] - expected: FAIL - - [CSS Animations: property from [distribute\] to [none\] at (0.5) should be [none\]] - expected: FAIL - - [CSS Animations: property from [distribute\] to [none\] at (0.6) should be [none\]] - expected: FAIL - - [CSS Animations: property from [distribute\] to [none\] at (1) should be [none\]] - expected: FAIL - - [CSS Animations: property from [distribute\] to [none\] at (1.5) should be [none\]] - expected: FAIL - [Web Animations: property from [distribute\] to [none\] at (-0.3) should be [distribute\]] expected: FAIL @@ -373,39 +184,3 @@ [Web Animations: property from [distribute\] to [none\] at (1.5) should be [none\]] expected: FAIL - - [CSS Transitions: property from [auto\] to [inter-character\] at (-0.3) should be [auto\]] - expected: FAIL - - [CSS Transitions: property from [auto\] to [inter-character\] at (0) should be [auto\]] - expected: FAIL - - [CSS Transitions: property from [auto\] to [inter-character\] at (0.3) should be [auto\]] - expected: FAIL - - [CSS Transitions: property from [inter-character\] to [distribute\] at (-0.3) should be [inter-character\]] - expected: FAIL - - [CSS Transitions: property from [inter-character\] to [distribute\] at (0) should be [inter-character\]] - expected: FAIL - - [CSS Transitions: property from [inter-character\] to [distribute\] at (0.3) should be [inter-character\]] - expected: FAIL - - [CSS Transitions: property from [inter-word\] to [distribute\] at (-0.3) should be [inter-word\]] - expected: FAIL - - [CSS Transitions: property from [inter-word\] to [distribute\] at (0) should be [inter-word\]] - expected: FAIL - - [CSS Transitions: property from [inter-word\] to [distribute\] at (0.3) should be [inter-word\]] - expected: FAIL - - [CSS Transitions: property from [distribute\] to [none\] at (-0.3) should be [distribute\]] - expected: FAIL - - [CSS Transitions: property from [distribute\] to [none\] at (0) should be [distribute\]] - expected: FAIL - - [CSS Transitions: property from [distribute\] to [none\] at (0.3) should be [distribute\]] - expected: FAIL diff --git a/tests/wpt/metadata/css/css-values/clamp-length-computed.html.ini b/tests/wpt/metadata/css/css-values/clamp-length-computed.html.ini index 52278514aa7..add1bb314b6 100644 --- a/tests/wpt/metadata/css/css-values/clamp-length-computed.html.ini +++ b/tests/wpt/metadata/css/css-values/clamp-length-computed.html.ini @@ -10,15 +10,3 @@ [Property letter-spacing value 'clamp(10px, 20px, 30px)' computes to '20px'] expected: FAIL - - [Property letter-spacing value 'clamp(10px, 35px , 30px)'] - expected: FAIL - - [Property letter-spacing value 'clamp(10px, 35px /*foo*/, 30px)'] - expected: FAIL - - [Property letter-spacing value 'clamp(10px /* foo */ , 35px, 30px)'] - expected: FAIL - - [Property letter-spacing value 'clamp(10px , 35px, 30px)'] - expected: FAIL diff --git a/tests/wpt/metadata/css/mediaqueries/test_media_queries.html.ini b/tests/wpt/metadata/css/mediaqueries/test_media_queries.html.ini index fd0f36ba676..4d3fea1a136 100644 --- a/tests/wpt/metadata/css/mediaqueries/test_media_queries.html.ini +++ b/tests/wpt/metadata/css/mediaqueries/test_media_queries.html.ini @@ -4402,3 +4402,9 @@ [expression_should_be_unknown: overflow-block: optional-paged] expected: FAIL + + [expression_should_be_parseable: width > = 0px] + expected: FAIL + + [expression_should_be_parseable: width < = 0px] + expected: FAIL