From 61f3ff1de3b973efc2074e4b7251e11692bee12f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Emilio=20Cobos=20=C3=81lvarez?= Date: Mon, 13 Jan 2020 13:21:58 +0000 Subject: [PATCH] style: Split LengthPercentage again. This is needed to support min() / max() / clamp(), etc, as those need to be a tree of values and thus need heap storage. This unfortunately grows LengthPercentage to be two pointers, which is bad as it blows up the size of nsStylePosition enough to trigger the size assertions. This patch comments out the assertion for now, the follow-up patches will uncomment them. Differential Revision: https://phabricator.services.mozilla.com/D58700 --- components/style/gecko/media_queries.rs | 3 +- components/style/values/animated/length.rs | 11 +- components/style/values/animated/mod.rs | 10 + components/style/values/computed/length.rs | 453 +++++++++++------- components/style/values/specified/gecko.rs | 2 +- components/style/values/specified/position.rs | 2 +- 6 files changed, 304 insertions(+), 177 deletions(-) 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),