diff --git a/components/style/gecko/media_queries.rs b/components/style/gecko/media_queries.rs index 3f82b832bce..390c1d4fe58 100644 --- a/components/style/gecko/media_queries.rs +++ b/components/style/gecko/media_queries.rs @@ -138,8 +138,7 @@ impl Device { /// Set the font size of the root element (for rem) pub fn set_root_font_size(&self, size: Au) { - self.root_font_size - .store(size.0 as isize, Ordering::Relaxed) + self.root_font_size.store(size.0 as isize, Ordering::Relaxed) } /// Sets the body text color for the "inherit color from body" quirk. diff --git a/components/style/values/animated/length.rs b/components/style/values/animated/length.rs index 04a0844dbe0..ece26a9c491 100644 --- a/components/style/values/animated/length.rs +++ b/components/style/values/animated/length.rs @@ -5,7 +5,7 @@ //! Animation implementation for various length-related types. use super::{Animate, Procedure}; -use crate::values::computed::length::LengthPercentage; +use crate::values::computed::length::{LengthPercentage, CalcLengthPercentage}; use crate::values::computed::Percentage; /// @@ -26,10 +26,9 @@ impl Animate for LengthPercentage { .animate(&other.unclamped_length(), procedure)?; let percentage = animate_percentage_half(self.specified_percentage(), other.specified_percentage())?; - Ok(Self::with_clamping_mode( - length, - percentage, - self.clamping_mode, - )) + + // Gets clamped as needed after the animation, so no need to specify any + // particular AllowedNumericType. + Ok(CalcLengthPercentage::new(length, percentage).to_length_percentge()) } } diff --git a/components/style/values/animated/mod.rs b/components/style/values/animated/mod.rs index ea43d01112e..7cac407adcb 100644 --- a/components/style/values/animated/mod.rs +++ b/components/style/values/animated/mod.rs @@ -424,6 +424,16 @@ impl ToAnimatedZero for i32 { } } +impl ToAnimatedZero for Box +where + T: ToAnimatedZero, +{ + #[inline] + fn to_animated_zero(&self) -> Result { + Ok(Box::new((**self).to_animated_zero()?)) + } +} + impl ToAnimatedZero for Option where T: ToAnimatedZero, diff --git a/components/style/values/computed/length.rs b/components/style/values/computed/length.rs index 485ef954d8e..285d9d2e6d4 100644 --- a/components/style/values/computed/length.rs +++ b/components/style/values/computed/length.rs @@ -18,7 +18,6 @@ use crate::values::specified::length::{AbsoluteLength, FontBaseSize, FontRelativ use crate::values::{specified, CSSFloat}; use crate::Zero; use app_units::Au; -use ordered_float::NotNan; use std::fmt::{self, Write}; use std::ops::{Add, AddAssign, Div, Mul, Neg, Sub}; use style_traits::values::specified::AllowedNumericType; @@ -54,13 +53,13 @@ impl ToComputedValue for specified::NoCalcLength { } impl ToComputedValue for specified::Length { - type ComputedValue = CSSPixelLength; + type ComputedValue = Length; #[inline] fn to_computed_value(&self, context: &Context) -> Self::ComputedValue { match *self { specified::Length::NoCalc(l) => l.to_computed_value(context), - specified::Length::Calc(ref calc) => calc.to_computed_value(context).length(), + specified::Length::Calc(ref calc) => calc.to_computed_value(context).length_component(), } } @@ -73,15 +72,32 @@ impl ToComputedValue for specified::Length { /// A `` value. This can be either a ``, a /// ``, or a combination of both via `calc()`. /// +/// cbindgen:private-default-tagged-enum-constructor=false +/// cbindgen:derive-mut-casts=true +/// /// https://drafts.csswg.org/css-values-4/#typedef-length-percentage #[allow(missing_docs)] -#[derive(Clone, Debug, Deserialize, MallocSizeOf, Serialize, ToAnimatedZero, ToResolvedValue)] +#[derive(Clone, Debug, Deserialize, MallocSizeOf, PartialEq, Serialize, ToAnimatedZero, ToResolvedValue)] +#[repr(u8)] +pub enum LengthPercentage { + Length(Length), + Percentage(Percentage), + Calc(Box), +} + +/// The representation of a calc() function. +#[derive( + Clone, Debug, Deserialize, MallocSizeOf, Serialize, ToAnimatedZero, ToResolvedValue, +)] #[repr(C)] -pub struct LengthPercentage { +pub struct CalcLengthPercentage { length: Length, + percentage: Percentage, + #[animation(constant)] - pub clamping_mode: AllowedNumericType, + clamping_mode: AllowedNumericType, + /// Whether we specified a percentage or not. #[animation(constant)] pub has_percentage: bool, @@ -99,7 +115,7 @@ pub struct LengthPercentage { // We may want to just eagerly-detect whether we can clamp in // `LengthPercentage::new` and switch to `AllowedNumericType::NonNegative` then, // maybe. -impl PartialEq for LengthPercentage { +impl PartialEq for CalcLengthPercentage { fn eq(&self, other: &Self) -> bool { self.length == other.length && self.percentage == other.percentage && @@ -115,123 +131,8 @@ impl ComputeSquaredDistance for LengthPercentage { Ok(self .unclamped_length() .compute_squared_distance(&other.unclamped_length())? + - self.percentage - .compute_squared_distance(&other.percentage)?) - } -} - -impl LengthPercentage { - /// Returns a new `LengthPercentage`. - #[inline] - pub fn new(length: Length, percentage: Option) -> Self { - Self::with_clamping_mode(length, percentage, AllowedNumericType::All) - } - - /// Returns a new `LengthPercentage` with zero length and some percentage. - pub fn new_percent(percentage: Percentage) -> Self { - Self::new(Length::zero(), Some(percentage)) - } - - /// Returns a new `LengthPercentage` with a specific clamping mode. - #[inline] - pub fn with_clamping_mode( - length: Length, - percentage: Option, - clamping_mode: AllowedNumericType, - ) -> Self { - Self { - clamping_mode, - length, - percentage: percentage.unwrap_or_default(), - has_percentage: percentage.is_some(), - } - } - - /// Returns this `calc()` as a ``. - /// - /// Panics in debug mode if a percentage is present in the expression. - #[inline] - pub fn length(&self) -> CSSPixelLength { - debug_assert!(!self.has_percentage); - self.length_component() - } - - /// Returns the length component of this `calc()` - #[inline] - pub fn length_component(&self) -> CSSPixelLength { - CSSPixelLength::new(self.clamping_mode.clamp(self.length.px())) - } - - /// Returns the `` component of this `calc()`, unclamped. - #[inline] - pub fn unclamped_length(&self) -> CSSPixelLength { - self.length - } - - /// Returns the percentage component of this `calc()` - #[inline] - pub fn percentage_component(&self) -> Percentage { - Percentage(self.clamping_mode.clamp(self.percentage.0)) - } - - /// Return the percentage value as CSSFloat. - #[inline] - pub fn percentage(&self) -> CSSFloat { - self.percentage.0 - } - - /// Return the specified percentage if any. - #[inline] - pub fn specified_percentage(&self) -> Option { - if self.has_percentage { - Some(self.percentage) - } else { - None - } - } - - /// Returns the length component if this could be represented as a - /// non-calc length. - pub fn as_length(&self) -> Option { - if !self.has_percentage { - Some(self.length_component()) - } else { - None - } - } - - /// Returns the percentage component if this could be represented as a - /// non-calc percentage. - pub fn as_percentage(&self) -> Option { - if !self.has_percentage || self.length.px() != 0. { - return None; - } - - Some(Percentage(self.clamping_mode.clamp(self.percentage.0))) - } - - /// Resolves the percentage. - #[inline] - pub fn percentage_relative_to(&self, basis: Length) -> Length { - let length = self.unclamped_length().0 + basis.0 * self.percentage.0; - Length::new(self.clamping_mode.clamp(length)) - } - - /// Convert the computed value into used value. - #[inline] - pub fn maybe_to_used_value(&self, container_len: Option) -> Option { - self.maybe_percentage_relative_to(container_len) - .map(Au::from) - } - - /// If there are special rules for computing percentages in a value (e.g. - /// the height property), they apply whenever a calc() expression contains - /// percentages. - pub fn maybe_percentage_relative_to(&self, container_len: Option) -> Option { - if self.has_percentage { - return Some(self.percentage_relative_to(container_len?)); - } - Some(self.length()) + self.percentage() + .compute_squared_distance(&other.percentage())?) } } @@ -251,7 +152,7 @@ impl specified::CalcLengthPercentage { context: &Context, zoom_fn: F, base_size: FontBaseSize, - ) -> LengthPercentage + ) -> CalcLengthPercentage where F: Fn(Length) -> Length, { @@ -285,7 +186,7 @@ impl specified::CalcLengthPercentage { } } - LengthPercentage::with_clamping_mode( + CalcLengthPercentage::with_clamping_mode( Length::new(length.min(f32::MAX).max(f32::MIN)), self.percentage, self.clamping_mode, @@ -297,7 +198,7 @@ impl specified::CalcLengthPercentage { &self, context: &Context, base_size: FontBaseSize, - ) -> LengthPercentage { + ) -> CalcLengthPercentage { self.to_computed_value_with_zoom( context, |abs| context.maybe_zoom_text(abs.into()), @@ -329,18 +230,14 @@ impl specified::CalcLengthPercentage { }, } } -} -impl ToComputedValue for specified::CalcLengthPercentage { - type ComputedValue = LengthPercentage; - - fn to_computed_value(&self, context: &Context) -> LengthPercentage { + fn to_computed_value(&self, context: &Context) -> CalcLengthPercentage { // normal properties don't zoom, and compute em units against the current style's font-size self.to_computed_value_with_zoom(context, |abs| abs, FontBaseSize::CurrentStyle) } #[inline] - fn from_computed_value(computed: &LengthPercentage) -> Self { + fn from_computed_value(computed: &CalcLengthPercentage) -> Self { specified::CalcLengthPercentage { clamping_mode: computed.clamping_mode, absolute: Some(AbsoluteLength::from_computed_value(&computed.length)), @@ -353,50 +250,262 @@ impl ToComputedValue for specified::CalcLengthPercentage { impl LengthPercentage { /// 1px length value for SVG defaults #[inline] - pub fn one() -> LengthPercentage { - LengthPercentage::new(Length::new(1.), None) + pub fn one() -> Self { + Self::Length(Length::new(1.)) + } + + /// Constructs a length value. + #[inline] + pub fn new_length(l: Length) -> Self { + Self::Length(l) + } + + /// Constructs a percentage value. + #[inline] + pub fn new_percent(p: Percentage) -> Self { + Self::Percentage(p) + } + + /// Constructs a `calc()` value. + #[inline] + pub fn new_calc(l: Length, percentage: Percentage) -> Self { + CalcLengthPercentage::new(l, Some(percentage)).to_length_percentge() } /// Returns true if the computed value is absolute 0 or 0%. #[inline] pub fn is_definitely_zero(&self) -> bool { - self.unclamped_length().px() == 0.0 && self.percentage.0 == 0.0 + match *self { + Self::Length(l) => l.px() == 0.0, + Self::Percentage(p) => p.0 == 0.0, + Self::Calc(ref c) => c.is_definitely_zero(), + } } - // CSSFloat doesn't implement Hash, so does CSSPixelLength. Therefore, we - // still use Au as the hash key. - #[allow(missing_docs)] - pub fn to_hash_key(&self) -> (Au, NotNan) { - ( - Au::from(self.unclamped_length()), - NotNan::new(self.percentage.0).unwrap(), - ) + /// Returns the `` component of this `calc()`, unclamped. + #[inline] + pub fn unclamped_length(&self) -> Length { + match *self { + Self::Length(l) => l, + Self::Percentage(..) => Zero::zero(), + Self::Calc(ref c) => c.unclamped_length(), + } + } + + /// Returns this `calc()` as a ``. + /// + /// Panics in debug mode if a percentage is present in the expression. + #[inline] + fn length(&self) -> Length { + debug_assert!(!self.has_percentage()); + self.length_component() + } + + /// Returns the `` component of this `calc()`, clamped. + #[inline] + pub fn length_component(&self) -> Length { + match *self { + Self::Length(l) => l, + Self::Percentage(..) => Zero::zero(), + Self::Calc(ref c) => c.length_component(), + } + } + + /// Returns the `` component of this `calc()`, unclamped, as a + /// float. + /// + /// FIXME: This are very different semantics from length(), we should + /// probably rename this. + #[inline] + pub fn percentage(&self) -> CSSFloat { + match *self { + Self::Length(..) => 0., + Self::Percentage(p) => p.0, + Self::Calc(ref c) => c.percentage.0, + } + } + + /// Returns the `` component of this `calc()`, clamped. + #[inline] + pub fn as_percentage(&self) -> Option { + match *self { + Self::Length(..) => None, + Self::Percentage(p) => Some(p), + Self::Calc(ref c) => c.as_percentage(), + } + } + + /// Resolves the percentage. + #[inline] + pub fn resolve(&self, basis: Length) -> Length { + match *self { + Self::Length(l) => l, + Self::Percentage(p) => Length::new(basis.0 * p.0), + Self::Calc(ref c) => c.resolve(basis), + } + } + + /// Resolves the percentage. Just an alias of resolve(). + #[inline] + pub fn percentage_relative_to(&self, basis: Length) -> Length { + self.resolve(basis) + } + + /// Return whether there's any percentage in this value. + #[inline] + pub fn has_percentage(&self) -> bool { + match *self { + Self::Length(..) => false, + Self::Percentage(..) => true, + Self::Calc(ref c) => c.has_percentage, + } + } + + /// Return the specified percentage if any. + #[inline] + pub fn specified_percentage(&self) -> Option { + match *self { + Self::Length(..) => None, + Self::Percentage(p) => Some(p), + Self::Calc(ref c) => c.specified_percentage(), + } } /// Returns the used value. + #[inline] pub fn to_used_value(&self, containing_length: Au) -> Au { Au::from(self.to_pixel_length(containing_length)) } /// Returns the used value as CSSPixelLength. + #[inline] pub fn to_pixel_length(&self, containing_length: Au) -> Length { - self.percentage_relative_to(containing_length.into()) + self.resolve(containing_length.into()) + } + + /// Convert the computed value into used value. + #[inline] + fn maybe_to_used_value(&self, container_len: Option) -> Option { + self.maybe_percentage_relative_to(container_len).map(Au::from) + } + + /// If there are special rules for computing percentages in a value (e.g. + /// the height property), they apply whenever a calc() expression contains + /// percentages. + pub fn maybe_percentage_relative_to(&self, container_len: Option) -> Option { + if self.has_percentage() { + return Some(self.resolve(container_len?)); + } + Some(self.length()) } /// Returns the clamped non-negative values. #[inline] pub fn clamp_to_non_negative(self) -> Self { - if let Some(p) = self.specified_percentage() { + match self { + Self::Length(l) => Self::Length(l.clamp_to_non_negative()), + Self::Percentage(p) => Self::Percentage(p.clamp_to_non_negative()), + Self::Calc(c) => c.clamp_to_non_negative().to_length_percentge(), + } + } +} + +impl CalcLengthPercentage { + /// Returns a new `LengthPercentage`. + #[inline] + pub fn new(length: Length, percentage: Option) -> Self { + Self::with_clamping_mode(length, percentage, AllowedNumericType::All) + } + + /// Converts this to a `LengthPercentage`, simplifying if possible. + #[inline] + pub fn to_length_percentge(self) -> LengthPercentage { + if !self.has_percentage { + return LengthPercentage::Length(self.length_component()) + } + if self.length.is_zero() { + return LengthPercentage::Percentage(Percentage(self.clamping_mode.clamp(self.percentage.0))); + } + LengthPercentage::Calc(Box::new(self)) + } + + fn specified_percentage(&self) -> Option { + if self.has_percentage { + Some(self.percentage) + } else { + None + } + } + + /// Returns a new `LengthPercentage` with a specific clamping mode. + #[inline] + fn with_clamping_mode( + length: Length, + percentage: Option, + clamping_mode: AllowedNumericType, + ) -> Self { + Self { + clamping_mode, + length, + percentage: percentage.unwrap_or_default(), + has_percentage: percentage.is_some(), + } + } + + /// Returns the length component of this `calc()` + #[inline] + fn length_component(&self) -> CSSPixelLength { + Length::new(self.clamping_mode.clamp(self.length.px())) + } + + /// Returns the percentage component if this could be represented as a + /// non-calc percentage. + fn as_percentage(&self) -> Option { + if !self.has_percentage || self.length.px() != 0. { + return None; + } + + Some(Percentage(self.clamping_mode.clamp(self.percentage.0))) + } + + /// Resolves the percentage. + #[inline] + pub fn resolve(&self, basis: Length) -> Length { + let length = self.length.0 + basis.0 * self.percentage.0; + Length::new(self.clamping_mode.clamp(length)) + } + + /// Resolves the percentage. + #[inline] + pub fn percentage_relative_to(&self, basis: Length) -> Length { + self.resolve(basis) + } + + /// Returns the length, without clamping. + #[inline] + pub fn unclamped_length(&self) -> Length { + self.length + } + + /// Returns true if the computed value is absolute 0 or 0%. + #[inline] + fn is_definitely_zero(&self) -> bool { + self.length.px() == 0.0 && self.percentage.0 == 0.0 + } + + /// Returns the clamped non-negative values. + #[inline] + fn clamp_to_non_negative(self) -> Self { + if self.has_percentage { // If we can eagerly clamp the percentage then just do that. if self.length.is_zero() { return Self::with_clamping_mode( Length::zero(), - Some(p.clamp_to_non_negative()), + Some(self.percentage.clamp_to_non_negative()), AllowedNumericType::NonNegative, ); } - - return Self::with_clamping_mode(self.length, Some(p), AllowedNumericType::NonNegative); + return Self::with_clamping_mode(self.length, Some(self.percentage), AllowedNumericType::NonNegative); } Self::with_clamping_mode( @@ -413,31 +522,41 @@ impl ToComputedValue for specified::LengthPercentage { fn to_computed_value(&self, context: &Context) -> LengthPercentage { match *self { specified::LengthPercentage::Length(ref value) => { - LengthPercentage::new(value.to_computed_value(context), None) + LengthPercentage::Length(value.to_computed_value(context)) + }, + specified::LengthPercentage::Percentage(value) => { + LengthPercentage::Percentage(value) + }, + specified::LengthPercentage::Calc(ref calc) => { + (**calc).to_computed_value(context).to_length_percentge() }, - specified::LengthPercentage::Percentage(value) => LengthPercentage::new_percent(value), - specified::LengthPercentage::Calc(ref calc) => (**calc).to_computed_value(context), } } fn from_computed_value(computed: &LengthPercentage) -> Self { - if let Some(p) = computed.as_percentage() { - return specified::LengthPercentage::Percentage(p); + match *computed { + LengthPercentage::Length(ref l) => { + specified::LengthPercentage::Length(ToComputedValue::from_computed_value(l)) + } + LengthPercentage::Percentage(p) => { + specified::LengthPercentage::Percentage(p) + } + LengthPercentage::Calc(ref c) => { + if let Some(p) = c.as_percentage() { + return specified::LengthPercentage::Percentage(p) + } + if !c.has_percentage { + return specified::LengthPercentage::Length(ToComputedValue::from_computed_value(&c.length_component())) + } + specified::LengthPercentage::Calc(Box::new(specified::CalcLengthPercentage::from_computed_value(c))) + } } - - if !computed.has_percentage { - return specified::LengthPercentage::Length(ToComputedValue::from_computed_value( - &computed.length(), - )); - } - - specified::LengthPercentage::Calc(Box::new(ToComputedValue::from_computed_value(computed))) } } impl Zero for LengthPercentage { fn zero() -> Self { - LengthPercentage::new(Length::zero(), None) + LengthPercentage::Length(Length::zero()) } #[inline] @@ -541,7 +660,7 @@ impl ToAnimatedValue for NonNegativeLengthPercentage { impl From for NonNegativeLengthPercentage { #[inline] fn from(length: NonNegativeLength) -> Self { - NonNegative(LengthPercentage::new(length.0, None)) + NonNegative(LengthPercentage::new_length(length.0)) } } @@ -557,7 +676,7 @@ impl From for NonNegativeLengthPercentage { impl From for LengthPercentage { #[inline] fn from(length: Au) -> Self { - LengthPercentage::new(length.into(), None) + LengthPercentage::new_length(length.into()) } } @@ -572,7 +691,7 @@ impl NonNegativeLengthPercentage { #[inline] pub fn to_used_value(&self, containing_length: Au) -> Au { let resolved = self.0.to_used_value(containing_length); - ::std::cmp::max(resolved, Au(0)) + std::cmp::max(resolved, Au(0)) } /// Convert the computed value into used value. @@ -581,7 +700,7 @@ impl NonNegativeLengthPercentage { let resolved = self .0 .maybe_to_used_value(containing_length.map(|v| v.into()))?; - Some(::std::cmp::max(resolved, Au(0))) + Some(std::cmp::max(resolved, Au(0))) } } diff --git a/components/style/values/specified/gecko.rs b/components/style/values/specified/gecko.rs index 4c85d1df668..3e3085c8849 100644 --- a/components/style/values/specified/gecko.rs +++ b/components/style/values/specified/gecko.rs @@ -23,7 +23,7 @@ fn parse_pixel_or_percent<'i, 't>( value, ref unit, .. } => { match_ignore_ascii_case! { unit, - "px" => Ok(LengthPercentage::new(Length::new(value), None)), + "px" => Ok(LengthPercentage::new_length(Length::new(value))), _ => Err(()), } }, diff --git a/components/style/values/specified/position.rs b/components/style/values/specified/position.rs index 8d35671991d..420133810ed 100644 --- a/components/style/values/specified/position.rs +++ b/components/style/values/specified/position.rs @@ -297,7 +297,7 @@ impl ToComputedValue for PositionComponent { let p = Percentage(1. - length.percentage()); let l = -length.unclamped_length(); // We represent ` ` as `calc(100% - )`. - ComputedLengthPercentage::with_clamping_mode(l, Some(p), length.clamping_mode) + ComputedLengthPercentage::new_calc(l, p) }, PositionComponent::Side(_, Some(ref length)) | PositionComponent::Length(ref length) => length.to_computed_value(context),