diff --git a/components/style/values/animated/length.rs b/components/style/values/animated/length.rs deleted file mode 100644 index 04690446e64..00000000000 --- a/components/style/values/animated/length.rs +++ /dev/null @@ -1,39 +0,0 @@ -/* 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/. */ - -//! Animation implementation for various length-related types. - -use super::{Animate, Procedure}; -use crate::values::computed::length::LengthPercentage; -use crate::values::computed::Percentage; -use style_traits::values::specified::AllowedNumericType; - -/// -impl Animate for LengthPercentage { - #[inline] - fn animate(&self, other: &Self, procedure: Procedure) -> Result { - let animate_percentage_half = |this: Option, other: Option| { - if this.is_none() && other.is_none() { - return Ok(None); - } - let this = this.unwrap_or_default(); - let other = other.unwrap_or_default(); - Ok(Some(this.animate(&other, procedure)?)) - }; - - let length = self - .unclamped_length() - .animate(&other.unclamped_length(), procedure)?; - let percentage = - animate_percentage_half(self.specified_percentage(), other.specified_percentage())?; - - // Gets clamped as needed after the animation if needed, so no need to - // specify any particular AllowedNumericType. - Ok(LengthPercentage::new_calc( - length, - percentage, - AllowedNumericType::All, - )) - } -} diff --git a/components/style/values/animated/mod.rs b/components/style/values/animated/mod.rs index 46a13410801..226c01a9a49 100644 --- a/components/style/values/animated/mod.rs +++ b/components/style/values/animated/mod.rs @@ -23,7 +23,6 @@ pub mod color; pub mod effects; mod font; mod grid; -mod length; mod svg; pub mod transform; @@ -454,6 +453,16 @@ where } } +impl ToAnimatedZero for Box<[T]> +where + T: ToAnimatedZero, +{ + #[inline] + fn to_animated_zero(&self) -> Result { + self.iter().map(|v| v.to_animated_zero()).collect() + } +} + impl ToAnimatedZero for crate::OwnedSlice where T: ToAnimatedZero, diff --git a/components/style/values/computed/length.rs b/components/style/values/computed/length.rs index 32b0946493c..9dac232d0d4 100644 --- a/components/style/values/computed/length.rs +++ b/components/style/values/computed/length.rs @@ -17,7 +17,7 @@ use crate::values::{specified, CSSFloat}; use crate::Zero; use app_units::Au; use std::fmt::{self, Write}; -use std::ops::{Add, AddAssign, Div, Mul, Neg, Sub}; +use std::ops::{Add, AddAssign, Div, Mul, MulAssign, Neg, Sub}; use style_traits::{CSSPixel, CssWriter, ToCss}; pub use super::image::Image; @@ -331,6 +331,13 @@ impl Div for CSSPixelLength { } } +impl MulAssign for CSSPixelLength { + #[inline] + fn mul_assign(&mut self, other: CSSFloat) { + self.0 *= other; + } +} + impl Mul for CSSPixelLength { type Output = Self; diff --git a/components/style/values/computed/length_percentage.rs b/components/style/values/computed/length_percentage.rs index 96bf76c98d4..b70117f3b18 100644 --- a/components/style/values/computed/length_percentage.rs +++ b/components/style/values/computed/length_percentage.rs @@ -25,9 +25,9 @@ //! our expectations. use super::{Context, Length, Percentage, ToComputedValue}; -use crate::values::animated::{ToAnimatedValue, ToAnimatedZero}; +use crate::values::animated::{Animate, Procedure, ToAnimatedValue, ToAnimatedZero}; use crate::values::distance::{ComputeSquaredDistance, SquaredDistance}; -use crate::values::generics::NonNegative; +use crate::values::generics::{calc, NonNegative}; use crate::values::specified::length::FontBaseSize; use crate::values::{specified, CSSFloat}; use crate::Zero; @@ -35,6 +35,7 @@ use app_units::Au; use malloc_size_of::{MallocSizeOf, MallocSizeOfOps}; use serde::{Deserialize, Serialize}; use std::fmt::{self, Write}; +use std::borrow::Cow; use style_traits::values::specified::AllowedNumericType; use style_traits::{CssWriter, ToCss}; @@ -162,7 +163,7 @@ impl MallocSizeOf for LengthPercentage { } /// An unpacked `` that borrows the `calc()` variant. -#[derive(Clone, Debug, PartialEq)] +#[derive(Clone, Debug, PartialEq, ToCss)] enum Unpacked<'a> { Calc(&'a CalcLengthPercentage), Length(Length), @@ -185,6 +186,14 @@ impl LengthPercentage { Self::new_length(Length::new(1.)) } + fn to_calc_node(&self) -> Cow { + match self.unpack() { + Unpacked::Length(l) => Cow::Owned(CalcNode::Leaf(CalcLengthPercentageLeaf::Length(l))), + Unpacked::Percentage(p) => Cow::Owned(CalcNode::Leaf(CalcLengthPercentageLeaf::Percentage(p))), + Unpacked::Calc(p) => Cow::Borrowed(&p.node), + } + } + /// Constructs a length value. #[inline] pub fn new_length(length: Length) -> Self { @@ -211,25 +220,48 @@ impl LengthPercentage { percent } + /// Given a `LengthPercentage` value `v`, construct the value representing + /// `calc(100% - v)`. + pub fn hundred_percent_minus(v: Self, clamping_mode: AllowedNumericType) -> Self { + // TODO: This could in theory take ownership of the calc node in `v` if + // possible instead of cloning. + let mut node = v.to_calc_node().into_owned(); + node.negate(); + + let new_node = CalcNode::Sum(vec![ + CalcNode::Leaf(CalcLengthPercentageLeaf::Percentage(Percentage::hundred())), + node, + ].into()); + + Self::new_calc(new_node, clamping_mode) + } + /// Constructs a `calc()` value. #[inline] pub fn new_calc( - length: Length, - percentage: Option, + mut node: CalcNode, clamping_mode: AllowedNumericType, ) -> Self { - let percentage = match percentage { - Some(p) => p, - None => return Self::new_length(Length::new(clamping_mode.clamp(length.px()))), - }; - if length.is_zero() { - return Self::new_percent(Percentage(clamping_mode.clamp(percentage.0))); + node.simplify_and_sort_children(); + + match node { + CalcNode::Leaf(l) => { + return match l { + CalcLengthPercentageLeaf::Length(l) => { + Self::new_length(Length::new(clamping_mode.clamp(l.px()))) + }, + CalcLengthPercentageLeaf::Percentage(p) => { + Self::new_percent(Percentage(clamping_mode.clamp(p.0))) + }, + } + } + _ => { + Self::new_calc_unchecked(Box::new(CalcLengthPercentage { + clamping_mode, + node, + })) + } } - Self::new_calc_unchecked(Box::new(CalcLengthPercentage { - length, - percentage, - clamping_mode, - })) } /// Private version of new_calc() that constructs a calc() variant without @@ -313,57 +345,7 @@ impl LengthPercentage { match self.unpack() { Unpacked::Length(l) => l.px() == 0.0, Unpacked::Percentage(p) => p.0 == 0.0, - Unpacked::Calc(ref c) => { - debug_assert_ne!( - c.length.px(), - 0.0, - "Should've been simplified to a percentage" - ); - false - }, - } - } - - /// Returns the `` component of this `calc()`, unclamped. - #[inline] - pub fn unclamped_length(&self) -> Length { - match self.unpack() { - Unpacked::Length(l) => l, - Unpacked::Percentage(..) => Zero::zero(), - Unpacked::Calc(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.unpack() { - Unpacked::Length(l) => l, - Unpacked::Percentage(..) => Zero::zero(), - Unpacked::Calc(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.unpack() { - Unpacked::Length(..) => 0., - Unpacked::Percentage(p) => p.0, - Unpacked::Calc(c) => c.percentage.0, + Unpacked::Calc(..) => false, } } @@ -407,25 +389,8 @@ impl LengthPercentage { #[inline] pub fn to_percentage(&self) -> Option { match self.unpack() { - Unpacked::Length(..) => None, Unpacked::Percentage(p) => Some(p), - Unpacked::Calc(ref c) => { - debug_assert!(!c.length.is_zero()); - None - }, - } - } - - /// Return the specified percentage if any. - #[inline] - pub fn specified_percentage(&self) -> Option { - match self.unpack() { - Unpacked::Length(..) => None, - Unpacked::Percentage(p) => Some(p), - Unpacked::Calc(ref c) => { - debug_assert!(self.has_percentage()); - Some(c.percentage) - }, + Unpacked::Length(..) | Unpacked::Calc(..) => None, } } @@ -452,10 +417,10 @@ impl LengthPercentage { /// 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?)); + if let Unpacked::Length(l) = self.unpack() { + return Some(l); } - Some(self.length()) + Some(self.resolve(container_len?)) } /// Returns the clamped non-negative values. @@ -549,7 +514,7 @@ impl ToCss for LengthPercentage { where W: Write, { - specified::LengthPercentage::from_computed_value(self).to_css(dest) + self.unpack().to_css(dest) } } @@ -584,46 +549,135 @@ impl<'de> Deserialize<'de> for LengthPercentage { } } +/// The leaves of a `` calc expression. +#[derive(Clone, Debug, Deserialize, MallocSizeOf, PartialEq, Serialize, ToAnimatedZero, ToCss, ToResolvedValue)] +#[allow(missing_docs)] +pub enum CalcLengthPercentageLeaf { + Length(Length), + Percentage(Percentage), +} + +impl CalcLengthPercentageLeaf { + fn is_zero_length(&self) -> bool { + match *self { + Self::Length(ref l) => l.is_zero(), + Self::Percentage(..) => false, + } + } +} + +impl PartialOrd for CalcLengthPercentageLeaf { + fn partial_cmp(&self, other: &Self) -> Option { + use self::CalcLengthPercentageLeaf::*; + + if std::mem::discriminant(self) != std::mem::discriminant(other) { + return None; + } + + match (self, other) { + (&Length(ref one), &Length(ref other)) => one.partial_cmp(other), + (&Percentage(ref one), &Percentage(ref other)) => one.partial_cmp(other), + _ => { + match *self { + Length(..) | Percentage(..) => {}, + } + unsafe { debug_unreachable!("Forgot a branch?"); } + } + } + } +} + +impl calc::CalcNodeLeaf for CalcLengthPercentageLeaf { + fn is_negative(&self) -> bool { + match *self { + Self::Length(ref l) => l.px() < 0., + Self::Percentage(ref p) => p.0 < 0., + } + } + + fn try_sum_in_place(&mut self, other: &Self) -> Result<(), ()> { + use self::CalcLengthPercentageLeaf::*; + + // 0px plus anything else is equal to the right hand side. + if self.is_zero_length() { + *self = other.clone(); + return Ok(()); + } + + if other.is_zero_length() { + return Ok(()); + } + + match (self, other) { + (&mut Length(ref mut one), &Length(ref other)) => { + *one += *other; + }, + (&mut Percentage(ref mut one), &Percentage(ref other)) => { + one.0 += other.0; + }, + _ => return Err(()), + } + + Ok(()) + } + + fn mul_by(&mut self, scalar: f32) { + match *self { + Self::Length(ref mut l) => *l = *l * scalar, + Self::Percentage(ref mut p) => p.0 *= scalar, + } + } + + fn simplify(&mut self) {} + + fn sort_key(&self) -> calc::SortKey { + match *self { + Self::Length(..) => calc::SortKey::Px, + Self::Percentage(..) => calc::SortKey::Percentage, + } + } +} + +/// The computed version of a calc() node for `` values. +pub type CalcNode = calc::GenericCalcNode; + /// The representation of a calc() function with mixed lengths and percentages. -#[derive(Clone, Debug, Deserialize, MallocSizeOf, Serialize, ToAnimatedZero, ToResolvedValue)] +#[derive(Clone, Debug, Deserialize, MallocSizeOf, Serialize, ToAnimatedZero, ToResolvedValue, ToCss)] #[repr(C)] pub struct CalcLengthPercentage { - length: Length, - - percentage: Percentage, - #[animation(constant)] + #[css(skip)] clamping_mode: AllowedNumericType, + node: CalcNode, } impl CalcLengthPercentage { - /// Returns the length component of this `calc()`, clamped. - #[inline] - fn length_component(&self) -> Length { - Length::new(self.clamping_mode.clamp(self.length.px())) - } - /// Resolves the percentage. #[inline] - pub fn resolve(&self, basis: Length) -> Length { - let length = self.length.px() + basis.px() * self.percentage.0; - Length::new(self.clamping_mode.clamp(length)) + fn resolve(&self, basis: Length) -> Length { + // TODO: This could be faster (without the extra allocations), + // potentially. + let mut resolved = self.node.map_leafs(|l| { + match l { + CalcLengthPercentageLeaf::Length(..) => l.clone(), + CalcLengthPercentageLeaf::Percentage(ref p) => { + CalcLengthPercentageLeaf::Length(Length::new(basis.px() * p.0)) + }, + } + }); + + resolved.simplify_and_sort_children(); + + match resolved { + CalcNode::Leaf(CalcLengthPercentageLeaf::Length(l)) => l, + other => unreachable!("Didn't manage to resolve : {:?}", other), + } } - /// Returns the length, without clamping. - #[inline] - fn unclamped_length(&self) -> Length { - self.length - } - - /// Returns the clamped non-negative values. - #[inline] fn clamp_to_non_negative(&self) -> LengthPercentage { - LengthPercentage::new_calc( - self.length, - Some(self.percentage), - AllowedNumericType::NonNegative, - ) + let mut new = self.clone(); + new.clamping_mode = AllowedNumericType::NonNegative; + LengthPercentage::new_calc_unchecked(Box::new(new)) } } @@ -641,7 +695,7 @@ impl CalcLengthPercentage { // maybe. impl PartialEq for CalcLengthPercentage { fn eq(&self, other: &Self) -> bool { - self.length == other.length && self.percentage == other.percentage + self.node == other.node } } @@ -656,43 +710,30 @@ impl specified::CalcLengthPercentage { where F: Fn(Length) -> Length, { - use crate::values::specified::length::{FontRelativeLength, ViewportPercentageLength}; - use std::f32; + use crate::values::specified::calc::Leaf; + use crate::values::specified::length::NoCalcLength; - let mut length = 0.; - - if let Some(absolute) = self.absolute { - length += zoom_fn(absolute.to_computed_value(context)).px(); - } - - for val in &[ - self.vw.map(ViewportPercentageLength::Vw), - self.vh.map(ViewportPercentageLength::Vh), - self.vmin.map(ViewportPercentageLength::Vmin), - self.vmax.map(ViewportPercentageLength::Vmax), - ] { - if let Some(val) = *val { - let viewport_size = context.viewport_size_for_viewport_unit_resolution(); - length += val.to_computed_value(viewport_size).px(); + let node = self.node.map_leaves(|leaf| { + match *leaf { + Leaf::Percentage(p) => CalcLengthPercentageLeaf::Percentage(Percentage(p)), + Leaf::Length(l) => { + CalcLengthPercentageLeaf::Length(match l { + NoCalcLength::Absolute(ref abs) => { + zoom_fn(abs.to_computed_value(context)) + }, + NoCalcLength::FontRelative(ref fr) => { + fr.to_computed_value(context, base_size) + }, + other => other.to_computed_value(context), + }) + }, + Leaf::Number(..) | + Leaf::Angle(..) | + Leaf::Time(..) => unreachable!("Shouldn't have parsed"), } - } + }); - for val in &[ - self.ch.map(FontRelativeLength::Ch), - self.em.map(FontRelativeLength::Em), - self.ex.map(FontRelativeLength::Ex), - self.rem.map(FontRelativeLength::Rem), - ] { - if let Some(val) = *val { - length += val.to_computed_value(context, base_size).px(); - } - } - - LengthPercentage::new_calc( - Length::new(length.min(f32::MAX).max(f32::MIN)), - self.percentage, - self.clamping_mode, - ) + LengthPercentage::new_calc(node, self.clamping_mode) } /// Compute font-size or line-height taking into account text-zoom if necessary. @@ -711,25 +752,14 @@ impl specified::CalcLengthPercentage { /// Compute the value into pixel length as CSSFloat without context, /// so it returns Err(()) if there is any non-absolute unit. pub fn to_computed_pixel_length_without_context(&self) -> Result { - if self.vw.is_some() || - self.vh.is_some() || - self.vmin.is_some() || - self.vmax.is_some() || - self.em.is_some() || - self.ex.is_some() || - self.ch.is_some() || - self.rem.is_some() || - self.percentage.is_some() - { - return Err(()); - } + use crate::values::specified::calc::Leaf; + use crate::values::specified::length::NoCalcLength; - match self.absolute { - Some(abs) => Ok(abs.to_px()), - None => { - debug_assert!(false, "Someone forgot to handle an unit here: {:?}", self); - Err(()) - }, + // Simplification should've turned this into an absolute length, + // otherwise it wouldn't have been able to. + match self.node { + calc::CalcNode::Leaf(Leaf::Length(NoCalcLength::Absolute(ref l))) => Ok(l.to_px()), + _ => Err(()), } } @@ -740,17 +770,48 @@ impl specified::CalcLengthPercentage { #[inline] fn from_computed_value(computed: &CalcLengthPercentage) -> Self { - use crate::values::specified::length::AbsoluteLength; + use crate::values::specified::calc::Leaf; + use crate::values::specified::length::NoCalcLength; specified::CalcLengthPercentage { clamping_mode: computed.clamping_mode, - absolute: Some(AbsoluteLength::from_computed_value(&computed.length)), - percentage: Some(computed.percentage), - ..Default::default() + node: computed.node.map_leaves(|l| { + match l { + CalcLengthPercentageLeaf::Length(ref l) => Leaf::Length(NoCalcLength::from_px(l.px())), + CalcLengthPercentageLeaf::Percentage(ref p) => Leaf::Percentage(p.0), + } + }) } } } +/// https://drafts.csswg.org/css-transitions/#animtype-lpcalc +/// https://drafts.csswg.org/css-values-4/#combine-math +/// https://drafts.csswg.org/css-values-4/#combine-mixed +impl Animate for LengthPercentage { + #[inline] + fn animate(&self, other: &Self, procedure: Procedure) -> Result { + Ok(match (self.unpack(), other.unpack()) { + (Unpacked::Length(one), Unpacked::Length(other)) => { + Self::new_length(one.animate(&other, procedure)?) + }, + (Unpacked::Percentage(one), Unpacked::Percentage(other)) => { + Self::new_percent(one.animate(&other, procedure)?) + }, + _ => { + let mut one = self.to_calc_node().into_owned(); + let mut other = other.to_calc_node().into_owned(); + let (l, r) = procedure.weights(); + + one.mul_by(l as f32); + other.mul_by(r as f32); + + Self::new_calc(CalcNode::Sum(vec![one, other].into()), AllowedNumericType::All) + }, + }) + } +} + /// A wrapper of LengthPercentage, whose value must be >= 0. pub type NonNegativeLengthPercentage = NonNegative; diff --git a/components/style/values/generics/calc.rs b/components/style/values/generics/calc.rs index 3a973f99f9d..6a920ddcd78 100644 --- a/components/style/values/generics/calc.rs +++ b/components/style/values/generics/calc.rs @@ -12,7 +12,7 @@ use std::{cmp, mem}; use smallvec::SmallVec; /// Whether we're a `min` or `max` function. -#[derive(Clone, Copy, Debug, MallocSizeOf, PartialEq, ToShmem)] +#[derive(Clone, Copy, Debug, Deserialize, MallocSizeOf, PartialEq, Serialize, ToAnimatedZero, ToResolvedValue, ToShmem)] #[repr(u8)] pub enum MinMaxOp { /// `min()` @@ -44,24 +44,27 @@ pub enum SortKey { } /// A generic node in a calc expression. +/// +/// FIXME: This would be much more elegant if we used `Self` in the types below, +/// but we can't because of https://github.com/serde-rs/serde/issues/1565. #[repr(u8)] -#[derive(Clone, Debug, PartialEq)] +#[derive(Clone, Debug, Deserialize, MallocSizeOf, PartialEq, Serialize, ToAnimatedZero, ToResolvedValue, ToShmem)] pub enum GenericCalcNode { /// A leaf node. Leaf(L), /// A sum node, representing `a + b + c` where a, b, and c are the /// arguments. - Sum(Box<[Self]>), + Sum(Box<[GenericCalcNode]>), /// A `min` or `max` function. - MinMax(Box<[Self]>, MinMaxOp), + MinMax(Box<[GenericCalcNode]>, MinMaxOp), /// A `clamp()` function. Clamp { /// The minimum value. - min: Box, + min: Box>, /// The central value. - center: Box, + center: Box>, /// The maximum value. - max: Box, + max: Box>, }, } @@ -111,6 +114,46 @@ impl CalcNode { } } + /// Convert this `CalcNode` into a `CalcNode` with a different leaf kind. + pub fn map_leaves(&self, mut map: F) -> CalcNode + where + O: CalcNodeLeaf, + F: FnMut(&L) -> O, + { + self.map_leaves_internal(&mut map) + } + + fn map_leaves_internal(&self, map: &mut F) -> CalcNode + where + O: CalcNodeLeaf, + F: FnMut(&L) -> O, + { + fn map_children( + children: &[CalcNode], + map: &mut F, + ) -> Box<[CalcNode]> + where + L: CalcNodeLeaf, + O: CalcNodeLeaf, + F: FnMut(&L) -> O, + { + children.iter().map(|c| c.map_leaves_internal(map)).collect() + } + + match *self { + Self::Leaf(ref l) => CalcNode::Leaf(map(l)), + Self::Sum(ref c) => CalcNode::Sum(map_children(c, map)), + Self::MinMax(ref c, op) => CalcNode::MinMax(map_children(c, map), op), + Self::Clamp { ref min, ref center, ref max } => { + let min = Box::new(min.map_leaves_internal(map)); + let center = Box::new(center.map_leaves_internal(map)); + let max = Box::new(max.map_leaves_internal(map)); + CalcNode::Clamp { min, center, max } + + } + } + } + fn is_negative_leaf(&self) -> bool { match *self { Self::Leaf(ref l) => l.is_negative(), diff --git a/components/style/values/generics/transform.rs b/components/style/values/generics/transform.rs index b77bba40b82..ef414c1f9a0 100644 --- a/components/style/values/generics/transform.rs +++ b/components/style/values/generics/transform.rs @@ -354,7 +354,7 @@ impl ToAbsoluteLength for SpecifiedLengthPercentage { match *self { Length(len) => len.to_computed_pixel_length_without_context(), Calc(ref calc) => calc.to_computed_pixel_length_without_context(), - _ => Err(()), + Percentage(..) => Err(()), } } } diff --git a/components/style/values/specified/calc.rs b/components/style/values/specified/calc.rs index 61f9103a750..9d0d407fbf4 100644 --- a/components/style/values/specified/calc.rs +++ b/components/style/values/specified/calc.rs @@ -7,7 +7,6 @@ //! [calc]: https://drafts.csswg.org/css-values/#calc-notation use crate::parser::ParserContext; -use crate::values::computed; use crate::values::generics::calc as generic; use crate::values::generics::calc::{MinMaxOp, SortKey}; use crate::values::specified::length::ViewportPercentageLength; @@ -35,7 +34,7 @@ pub enum MathFunction { } /// A leaf node inside a `Calc` expression's AST. -#[derive(Clone, Debug, PartialEq)] +#[derive(Clone, Debug, MallocSizeOf, PartialEq, ToShmem)] pub enum Leaf { /// `` Length(NoCalcLength), @@ -89,87 +88,12 @@ enum CalcUnit { /// relative lengths, and to_computed_pixel_length_without_context() handles /// this case. Therefore, if you want to add a new field, please make sure this /// function work properly. -#[derive(Clone, Copy, Debug, Default, MallocSizeOf, PartialEq, ToShmem)] +#[derive(Clone, Debug, MallocSizeOf, PartialEq, ToCss, ToShmem)] #[allow(missing_docs)] pub struct CalcLengthPercentage { + #[css(skip)] pub clamping_mode: AllowedNumericType, - pub absolute: Option, - pub vw: Option, - pub vh: Option, - pub vmin: Option, - pub vmax: Option, - pub em: Option, - pub ex: Option, - pub ch: Option, - pub rem: Option, - pub percentage: Option, -} - -impl ToCss for CalcLengthPercentage { - /// - /// - /// FIXME(emilio): Should this simplify away zeros? - #[allow(unused_assignments)] - fn to_css(&self, dest: &mut CssWriter) -> fmt::Result - where - W: Write, - { - use num_traits::Zero; - - let mut first_value = true; - macro_rules! first_value_check { - ($val:expr) => { - if !first_value { - dest.write_str(if $val < Zero::zero() { " - " } else { " + " })?; - } else if $val < Zero::zero() { - dest.write_str("-")?; - } - first_value = false; - }; - } - - macro_rules! serialize { - ( $( $val:ident ),* ) => { - $( - if let Some(val) = self.$val { - first_value_check!(val); - val.abs().to_css(dest)?; - dest.write_str(stringify!($val))?; - } - )* - }; - } - - macro_rules! serialize_abs { - ( $( $val:ident ),+ ) => { - $( - if let Some(AbsoluteLength::$val(v)) = self.absolute { - first_value_check!(v); - AbsoluteLength::$val(v.abs()).to_css(dest)?; - } - )+ - }; - } - - dest.write_str("calc(")?; - - // NOTE(emilio): Percentages first because of web-compat problems, see: - // https://github.com/w3c/csswg-drafts/issues/1731 - if let Some(val) = self.percentage { - first_value_check!(val.0); - val.abs().to_css(dest)?; - } - - // NOTE(emilio): The order here it's very intentional, and alphabetic - // per the spec linked above. - serialize!(ch); - serialize_abs!(Cm); - serialize!(em, ex); - serialize_abs!(In, Mm, Pc, Pt, Px, Q); - serialize!(rem, vh, vmax, vmin, vw); - - dest.write_str(")") - } + pub node: CalcNode, } impl SpecifiedValueInfo for CalcLengthPercentage {} @@ -591,84 +515,15 @@ impl CalcNode { /// Tries to simplify this expression into a `` or ` /// value. - fn to_length_or_percentage( - &mut self, + fn into_length_or_percentage( + mut self, clamping_mode: AllowedNumericType, ) -> Result { - let mut ret = CalcLengthPercentage { - clamping_mode, - ..Default::default() - }; self.simplify_and_sort_children(); - self.add_length_or_percentage_to(&mut ret, 1.0)?; - Ok(ret) - } - - /// Puts this `` or `` into `ret`, or error. - /// - /// `factor` is the sign or multiplicative factor to account for the sign - /// (this allows adding and substracting into the return value). - fn add_length_or_percentage_to( - &self, - ret: &mut CalcLengthPercentage, - factor: CSSFloat, - ) -> Result<(), ()> { - match *self { - CalcNode::Leaf(Leaf::Percentage(pct)) => { - ret.percentage = Some(computed::Percentage( - ret.percentage.map_or(0., |p| p.0) + pct * factor, - )); - }, - CalcNode::Leaf(Leaf::Length(ref l)) => match *l { - NoCalcLength::Absolute(abs) => { - ret.absolute = Some(match ret.absolute { - Some(value) => value + abs * factor, - None => abs * factor, - }); - }, - NoCalcLength::FontRelative(rel) => match rel { - FontRelativeLength::Em(em) => { - ret.em = Some(ret.em.unwrap_or(0.) + em * factor); - }, - FontRelativeLength::Ex(ex) => { - ret.ex = Some(ret.ex.unwrap_or(0.) + ex * factor); - }, - FontRelativeLength::Ch(ch) => { - ret.ch = Some(ret.ch.unwrap_or(0.) + ch * factor); - }, - FontRelativeLength::Rem(rem) => { - ret.rem = Some(ret.rem.unwrap_or(0.) + rem * factor); - }, - }, - NoCalcLength::ViewportPercentage(rel) => match rel { - ViewportPercentageLength::Vh(vh) => { - ret.vh = Some(ret.vh.unwrap_or(0.) + vh * factor) - }, - ViewportPercentageLength::Vw(vw) => { - ret.vw = Some(ret.vw.unwrap_or(0.) + vw * factor) - }, - ViewportPercentageLength::Vmax(vmax) => { - ret.vmax = Some(ret.vmax.unwrap_or(0.) + vmax * factor) - }, - ViewportPercentageLength::Vmin(vmin) => { - ret.vmin = Some(ret.vmin.unwrap_or(0.) + vmin * factor) - }, - }, - NoCalcLength::ServoCharacterWidth(..) => unreachable!(), - }, - CalcNode::Sum(ref children) => { - for child in &**children { - child.add_length_or_percentage_to(ret, factor)?; - } - }, - CalcNode::MinMax(..) | CalcNode::Clamp { .. } => { - // FIXME(emilio): Implement min/max/clamp for length-percentage. - return Err(()); - }, - CalcNode::Leaf(..) => return Err(()), - } - - Ok(()) + Ok(CalcLengthPercentage { + clamping_mode, + node: self, + }) } /// Tries to simplify this expression into a `