diff --git a/components/style/values/specified/calc.rs b/components/style/values/specified/calc.rs index d951d0de08e..4fb3c009e04 100644 --- a/components/style/values/specified/calc.rs +++ b/components/style/values/specified/calc.rs @@ -20,9 +20,14 @@ use style_traits::{CssWriter, ParseError, SpecifiedValueInfo, StyleParseErrorKin /// The name of the mathematical function that we're parsing. #[derive(Debug, Copy, Clone)] pub enum MathFunction { - /// `calc()` + /// `calc()`: https://drafts.csswg.org/css-values-4/#funcdef-calc Calc, - // FIXME: min() / max() / clamp + /// `min()`: https://drafts.csswg.org/css-values-4/#funcdef-min + Min, + /// `max()`: https://drafts.csswg.org/css-values-4/#funcdef-max + Max, + /// `clamp()`: https://drafts.csswg.org/css-values-4/#funcdef-clamp + Clamp, } /// A node inside a `Calc` expression's AST. @@ -46,6 +51,19 @@ pub enum CalcNode { Mul(Box, Box), /// An expression of the form `x / y` Div(Box, Box), + /// A `min()` function. + Min(Box<[CalcNode]>), + /// A `max()` function. + Max(Box<[CalcNode]>), + /// A `clamp()` function. + Clamp { + /// The minimum value. + min: Box, + /// The central value. + center: Box, + /// The maximum value. + max: Box, + }, } /// An expected unit we intend to parse within a `calc()` expression. @@ -158,6 +176,80 @@ impl ToCss for CalcLengthPercentage { impl SpecifiedValueInfo for CalcLengthPercentage {} +macro_rules! impl_generic_to_type { + ($self:ident, $self_variant:ident, $to_self:ident, $to_float:ident, $from_float:path) => {{ + if let Self::$self_variant(ref v) = *$self { + return Ok(v.clone()); + } + + Ok(match *$self { + Self::Sub(ref a, ref b) => $from_float(a.$to_self()?.$to_float() - b.$to_self()?.$to_float()), + Self::Sum(ref a, ref b) => $from_float(a.$to_self()?.$to_float() + b.$to_self()?.$to_float()), + Self::Mul(ref a, ref b) => match a.$to_self() { + Ok(lhs) => { + let rhs = b.to_number()?; + $from_float(lhs.$to_float() * rhs) + }, + Err(..) => { + let lhs = a.to_number()?; + let rhs = b.$to_self()?; + $from_float(lhs * rhs.$to_float()) + }, + }, + Self::Div(ref a, ref b) => { + let lhs = a.$to_self()?; + let rhs = b.to_number()?; + if rhs == 0. { + return Err(()); + } + $from_float(lhs.$to_float() / rhs) + }, + Self::Clamp { ref min, ref center, ref max } => { + let min = min.$to_self()?; + let center = center.$to_self()?; + let max = max.$to_self()?; + + // Equivalent to cmp::max(min, cmp::min(center, max)) + // + // But preserving units when appropriate. + let mut result = center; + if result.$to_float() > max.$to_float() { + result = max; + } + if result.$to_float() < min.$to_float() { + result = min; + } + result + }, + Self::Min(ref nodes) => { + let mut min = nodes[0].$to_self()?; + for node in nodes.iter().skip(1) { + let candidate = node.$to_self()?; + if candidate.$to_float() < min.$to_float() { + min = candidate; + } + } + min + }, + Self::Max(ref nodes) => { + let mut max = nodes[0].$to_self()?; + for node in nodes.iter().skip(1) { + let candidate = node.$to_self()?; + if candidate.$to_float() > max.$to_float() { + max = candidate; + } + } + max + }, + Self::Length(..) | + Self::Angle(..) | + Self::Time(..) | + Self::Percentage(..) | + Self::Number(..) => return Err(()), + }) + }} +} + impl CalcNode { /// Tries to parse a single element in the expression, that is, a /// ``, ``, `