tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

calc.rs (52292B)


      1 /* This Source Code Form is subject to the terms of the Mozilla Public
      2 * License, v. 2.0. If a copy of the MPL was not distributed with this
      3 * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
      4 
      5 //! [Calc expressions][calc].
      6 //!
      7 //! [calc]: https://drafts.csswg.org/css-values/#calc-notation
      8 
      9 use crate::color::parsing::ChannelKeyword;
     10 use crate::derives::*;
     11 use crate::parser::{Parse, ParserContext};
     12 use crate::values::generics::calc::{
     13    self as generic, CalcNodeLeaf, CalcUnits, MinMaxOp, ModRemOp, PositivePercentageBasis,
     14    RoundingStrategy, SortKey,
     15 };
     16 use crate::values::generics::length::GenericAnchorSizeFunction;
     17 use crate::values::generics::position::{
     18    AnchorSideKeyword, GenericAnchorFunction, GenericAnchorSide,
     19 };
     20 use crate::values::specified::length::{AbsoluteLength, FontRelativeLength, NoCalcLength};
     21 use crate::values::specified::length::{ContainerRelativeLength, ViewportPercentageLength};
     22 use crate::values::specified::{self, Angle, Resolution, Time};
     23 use crate::values::{serialize_number, serialize_percentage, CSSFloat, DashedIdent};
     24 use cssparser::{match_ignore_ascii_case, CowRcStr, Parser, Token};
     25 use debug_unreachable::debug_unreachable;
     26 use smallvec::SmallVec;
     27 use std::cmp;
     28 use std::fmt::{self, Write};
     29 use style_traits::values::specified::AllowedNumericType;
     30 use style_traits::{
     31    CssWriter, ParseError, SpecifiedValueInfo, StyleParseErrorKind, ToCss, ToTyped, TypedValue,
     32 };
     33 
     34 /// The name of the mathematical function that we're parsing.
     35 #[derive(Clone, Copy, Debug, Parse)]
     36 pub enum MathFunction {
     37    /// `calc()`: https://drafts.csswg.org/css-values-4/#funcdef-calc
     38    Calc,
     39    /// `min()`: https://drafts.csswg.org/css-values-4/#funcdef-min
     40    Min,
     41    /// `max()`: https://drafts.csswg.org/css-values-4/#funcdef-max
     42    Max,
     43    /// `clamp()`: https://drafts.csswg.org/css-values-4/#funcdef-clamp
     44    Clamp,
     45    /// `round()`: https://drafts.csswg.org/css-values-4/#funcdef-round
     46    Round,
     47    /// `mod()`: https://drafts.csswg.org/css-values-4/#funcdef-mod
     48    Mod,
     49    /// `rem()`: https://drafts.csswg.org/css-values-4/#funcdef-rem
     50    Rem,
     51    /// `sin()`: https://drafts.csswg.org/css-values-4/#funcdef-sin
     52    Sin,
     53    /// `cos()`: https://drafts.csswg.org/css-values-4/#funcdef-cos
     54    Cos,
     55    /// `tan()`: https://drafts.csswg.org/css-values-4/#funcdef-tan
     56    Tan,
     57    /// `asin()`: https://drafts.csswg.org/css-values-4/#funcdef-asin
     58    Asin,
     59    /// `acos()`: https://drafts.csswg.org/css-values-4/#funcdef-acos
     60    Acos,
     61    /// `atan()`: https://drafts.csswg.org/css-values-4/#funcdef-atan
     62    Atan,
     63    /// `atan2()`: https://drafts.csswg.org/css-values-4/#funcdef-atan2
     64    Atan2,
     65    /// `pow()`: https://drafts.csswg.org/css-values-4/#funcdef-pow
     66    Pow,
     67    /// `sqrt()`: https://drafts.csswg.org/css-values-4/#funcdef-sqrt
     68    Sqrt,
     69    /// `hypot()`: https://drafts.csswg.org/css-values-4/#funcdef-hypot
     70    Hypot,
     71    /// `log()`: https://drafts.csswg.org/css-values-4/#funcdef-log
     72    Log,
     73    /// `exp()`: https://drafts.csswg.org/css-values-4/#funcdef-exp
     74    Exp,
     75    /// `abs()`: https://drafts.csswg.org/css-values-4/#funcdef-abs
     76    Abs,
     77    /// `sign()`: https://drafts.csswg.org/css-values-4/#funcdef-sign
     78    Sign,
     79 }
     80 
     81 /// A leaf node inside a `Calc` expression's AST.
     82 #[derive(Clone, Debug, MallocSizeOf, PartialEq, ToShmem)]
     83 #[repr(u8)]
     84 pub enum Leaf {
     85    /// `<length>`
     86    Length(NoCalcLength),
     87    /// `<angle>`
     88    Angle(Angle),
     89    /// `<time>`
     90    Time(Time),
     91    /// `<resolution>`
     92    Resolution(Resolution),
     93    /// A component of a color.
     94    ColorComponent(ChannelKeyword),
     95    /// `<percentage>`
     96    Percentage(CSSFloat),
     97    /// `<number>`
     98    Number(CSSFloat),
     99 }
    100 
    101 impl Leaf {
    102    fn as_length(&self) -> Option<&NoCalcLength> {
    103        match *self {
    104            Self::Length(ref l) => Some(l),
    105            _ => None,
    106        }
    107    }
    108 }
    109 
    110 impl ToCss for Leaf {
    111    fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
    112    where
    113        W: Write,
    114    {
    115        match *self {
    116            Self::Length(ref l) => l.to_css(dest),
    117            Self::Number(n) => serialize_number(n, /* was_calc = */ false, dest),
    118            Self::Resolution(ref r) => r.to_css(dest),
    119            Self::Percentage(p) => serialize_percentage(p, dest),
    120            Self::Angle(ref a) => a.to_css(dest),
    121            Self::Time(ref t) => t.to_css(dest),
    122            Self::ColorComponent(ref s) => s.to_css(dest),
    123        }
    124    }
    125 }
    126 
    127 impl ToTyped for Leaf {
    128    fn to_typed(&self) -> Option<TypedValue> {
    129        // XXX Only supporting Length for now
    130        match *self {
    131            Self::Length(ref l) => l.to_typed(),
    132            _ => None,
    133        }
    134    }
    135 }
    136 
    137 /// A struct to hold a simplified `<length>` or `<percentage>` expression.
    138 ///
    139 /// In some cases, e.g. DOMMatrix, we support calc(), but reject all the
    140 /// relative lengths, and to_computed_pixel_length_without_context() handles
    141 /// this case. Therefore, if you want to add a new field, please make sure this
    142 /// function work properly.
    143 #[derive(Clone, Debug, MallocSizeOf, PartialEq, ToCss, ToShmem, ToTyped)]
    144 #[allow(missing_docs)]
    145 #[typed_value(derive_fields)]
    146 pub struct CalcLengthPercentage {
    147    #[css(skip)]
    148    pub clamping_mode: AllowedNumericType,
    149    pub node: CalcNode,
    150 }
    151 
    152 impl CalcLengthPercentage {
    153    fn same_unit_length_as(a: &Self, b: &Self) -> Option<(CSSFloat, CSSFloat)> {
    154        debug_assert_eq!(a.clamping_mode, b.clamping_mode);
    155        debug_assert_eq!(a.clamping_mode, AllowedNumericType::All);
    156 
    157        let a = a.node.as_leaf()?;
    158        let b = b.node.as_leaf()?;
    159 
    160        if a.sort_key() != b.sort_key() {
    161            return None;
    162        }
    163 
    164        let a = a.as_length()?.unitless_value();
    165        let b = b.as_length()?.unitless_value();
    166        return Some((a, b));
    167    }
    168 }
    169 
    170 impl SpecifiedValueInfo for CalcLengthPercentage {}
    171 
    172 /// Should parsing anchor-positioning functions in `calc()` be allowed?
    173 #[derive(Clone, Copy, PartialEq)]
    174 pub enum AllowAnchorPositioningFunctions {
    175    /// Don't allow any anchor positioning function.
    176    No,
    177    /// Allow `anchor-size()` to be parsed.
    178    AllowAnchorSize,
    179    /// Allow `anchor()` and `anchor-size()` to be parsed.
    180    AllowAnchorAndAnchorSize,
    181 }
    182 
    183 bitflags! {
    184    /// Additional functions within math functions that are permitted to be parsed depending on
    185    /// the context of parsing (e.g. Parsing `inset` allows use of `anchor()` within `calc()`).
    186    #[derive(Clone, Copy, PartialEq, Eq)]
    187    struct AdditionalFunctions: u8 {
    188        /// `anchor()` function.
    189        const ANCHOR = 1 << 0;
    190        /// `anchor-size()` function.
    191        const ANCHOR_SIZE = 1 << 1;
    192    }
    193 }
    194 
    195 /// What is allowed to be parsed for math functions within in this context?
    196 #[derive(Clone, Copy)]
    197 pub struct AllowParse {
    198    /// Units allowed to be parsed.
    199    units: CalcUnits,
    200    /// Additional functions allowed to be parsed in this context.
    201    additional_functions: AdditionalFunctions,
    202 }
    203 
    204 impl AllowParse {
    205    /// Allow only specified units to be parsed, without any additional functions.
    206    pub fn new(units: CalcUnits) -> Self {
    207        Self {
    208            units,
    209            additional_functions: AdditionalFunctions::empty(),
    210        }
    211    }
    212 
    213    /// Add new units to the allowed units to be parsed.
    214    fn new_including(mut self, units: CalcUnits) -> Self {
    215        self.units |= units;
    216        self
    217    }
    218 
    219    /// Should given unit be allowed to parse?
    220    fn includes(&self, unit: CalcUnits) -> bool {
    221        self.units.intersects(unit)
    222    }
    223 }
    224 
    225 impl generic::CalcNodeLeaf for Leaf {
    226    fn unit(&self) -> CalcUnits {
    227        match self {
    228            Leaf::Length(_) => CalcUnits::LENGTH,
    229            Leaf::Angle(_) => CalcUnits::ANGLE,
    230            Leaf::Time(_) => CalcUnits::TIME,
    231            Leaf::Resolution(_) => CalcUnits::RESOLUTION,
    232            Leaf::ColorComponent(_) => CalcUnits::COLOR_COMPONENT,
    233            Leaf::Percentage(_) => CalcUnits::PERCENTAGE,
    234            Leaf::Number(_) => CalcUnits::empty(),
    235        }
    236    }
    237 
    238    fn unitless_value(&self) -> Option<f32> {
    239        Some(match *self {
    240            Self::Length(ref l) => l.unitless_value(),
    241            Self::Percentage(n) | Self::Number(n) => n,
    242            Self::Resolution(ref r) => r.dppx(),
    243            Self::Angle(ref a) => a.degrees(),
    244            Self::Time(ref t) => t.seconds(),
    245            Self::ColorComponent(_) => return None,
    246        })
    247    }
    248 
    249    fn new_number(value: f32) -> Self {
    250        Self::Number(value)
    251    }
    252 
    253    fn compare(&self, other: &Self, basis: PositivePercentageBasis) -> Option<cmp::Ordering> {
    254        use self::Leaf::*;
    255 
    256        if std::mem::discriminant(self) != std::mem::discriminant(other) {
    257            return None;
    258        }
    259 
    260        if matches!(self, Percentage(..)) && matches!(basis, PositivePercentageBasis::Unknown) {
    261            return None;
    262        }
    263 
    264        let self_negative = self.is_negative().unwrap_or(false);
    265        if self_negative != other.is_negative().unwrap_or(false) {
    266            return Some(if self_negative {
    267                cmp::Ordering::Less
    268            } else {
    269                cmp::Ordering::Greater
    270            });
    271        }
    272 
    273        match (self, other) {
    274            (&Percentage(ref one), &Percentage(ref other)) => one.partial_cmp(other),
    275            (&Length(ref one), &Length(ref other)) => one.partial_cmp(other),
    276            (&Angle(ref one), &Angle(ref other)) => one.degrees().partial_cmp(&other.degrees()),
    277            (&Time(ref one), &Time(ref other)) => one.seconds().partial_cmp(&other.seconds()),
    278            (&Resolution(ref one), &Resolution(ref other)) => one.dppx().partial_cmp(&other.dppx()),
    279            (&Number(ref one), &Number(ref other)) => one.partial_cmp(other),
    280            (&ColorComponent(ref one), &ColorComponent(ref other)) => one.partial_cmp(other),
    281            _ => {
    282                match *self {
    283                    Length(..) | Percentage(..) | Angle(..) | Time(..) | Number(..)
    284                    | Resolution(..) | ColorComponent(..) => {},
    285                }
    286                unsafe {
    287                    debug_unreachable!("Forgot a branch?");
    288                }
    289            },
    290        }
    291    }
    292 
    293    fn as_number(&self) -> Option<f32> {
    294        match *self {
    295            Leaf::Length(_)
    296            | Leaf::Angle(_)
    297            | Leaf::Time(_)
    298            | Leaf::Resolution(_)
    299            | Leaf::Percentage(_)
    300            | Leaf::ColorComponent(_) => None,
    301            Leaf::Number(value) => Some(value),
    302        }
    303    }
    304 
    305    fn sort_key(&self) -> SortKey {
    306        match *self {
    307            Self::Number(..) => SortKey::Number,
    308            Self::Percentage(..) => SortKey::Percentage,
    309            Self::Time(..) => SortKey::S,
    310            Self::Resolution(..) => SortKey::Dppx,
    311            Self::Angle(..) => SortKey::Deg,
    312            Self::Length(ref l) => match *l {
    313                NoCalcLength::Absolute(..) => SortKey::Px,
    314                NoCalcLength::FontRelative(ref relative) => match *relative {
    315                    FontRelativeLength::Em(..) => SortKey::Em,
    316                    FontRelativeLength::Ex(..) => SortKey::Ex,
    317                    FontRelativeLength::Rex(..) => SortKey::Rex,
    318                    FontRelativeLength::Ch(..) => SortKey::Ch,
    319                    FontRelativeLength::Rch(..) => SortKey::Rch,
    320                    FontRelativeLength::Cap(..) => SortKey::Cap,
    321                    FontRelativeLength::Rcap(..) => SortKey::Rcap,
    322                    FontRelativeLength::Ic(..) => SortKey::Ic,
    323                    FontRelativeLength::Ric(..) => SortKey::Ric,
    324                    FontRelativeLength::Rem(..) => SortKey::Rem,
    325                    FontRelativeLength::Lh(..) => SortKey::Lh,
    326                    FontRelativeLength::Rlh(..) => SortKey::Rlh,
    327                },
    328                NoCalcLength::ViewportPercentage(ref vp) => match *vp {
    329                    ViewportPercentageLength::Vh(..) => SortKey::Vh,
    330                    ViewportPercentageLength::Svh(..) => SortKey::Svh,
    331                    ViewportPercentageLength::Lvh(..) => SortKey::Lvh,
    332                    ViewportPercentageLength::Dvh(..) => SortKey::Dvh,
    333                    ViewportPercentageLength::Vw(..) => SortKey::Vw,
    334                    ViewportPercentageLength::Svw(..) => SortKey::Svw,
    335                    ViewportPercentageLength::Lvw(..) => SortKey::Lvw,
    336                    ViewportPercentageLength::Dvw(..) => SortKey::Dvw,
    337                    ViewportPercentageLength::Vmax(..) => SortKey::Vmax,
    338                    ViewportPercentageLength::Svmax(..) => SortKey::Svmax,
    339                    ViewportPercentageLength::Lvmax(..) => SortKey::Lvmax,
    340                    ViewportPercentageLength::Dvmax(..) => SortKey::Dvmax,
    341                    ViewportPercentageLength::Vmin(..) => SortKey::Vmin,
    342                    ViewportPercentageLength::Svmin(..) => SortKey::Svmin,
    343                    ViewportPercentageLength::Lvmin(..) => SortKey::Lvmin,
    344                    ViewportPercentageLength::Dvmin(..) => SortKey::Dvmin,
    345                    ViewportPercentageLength::Vb(..) => SortKey::Vb,
    346                    ViewportPercentageLength::Svb(..) => SortKey::Svb,
    347                    ViewportPercentageLength::Lvb(..) => SortKey::Lvb,
    348                    ViewportPercentageLength::Dvb(..) => SortKey::Dvb,
    349                    ViewportPercentageLength::Vi(..) => SortKey::Vi,
    350                    ViewportPercentageLength::Svi(..) => SortKey::Svi,
    351                    ViewportPercentageLength::Lvi(..) => SortKey::Lvi,
    352                    ViewportPercentageLength::Dvi(..) => SortKey::Dvi,
    353                },
    354                NoCalcLength::ContainerRelative(ref cq) => match *cq {
    355                    ContainerRelativeLength::Cqw(..) => SortKey::Cqw,
    356                    ContainerRelativeLength::Cqh(..) => SortKey::Cqh,
    357                    ContainerRelativeLength::Cqi(..) => SortKey::Cqi,
    358                    ContainerRelativeLength::Cqb(..) => SortKey::Cqb,
    359                    ContainerRelativeLength::Cqmin(..) => SortKey::Cqmin,
    360                    ContainerRelativeLength::Cqmax(..) => SortKey::Cqmax,
    361                },
    362                NoCalcLength::ServoCharacterWidth(..) => unreachable!(),
    363            },
    364            Self::ColorComponent(..) => SortKey::ColorComponent,
    365        }
    366    }
    367 
    368    fn simplify(&mut self) {
    369        if let Self::Length(NoCalcLength::Absolute(ref mut abs)) = *self {
    370            *abs = AbsoluteLength::Px(abs.to_px());
    371        }
    372    }
    373 
    374    /// Tries to merge one sum to another, that is, perform `x` + `y`.
    375    ///
    376    /// Only handles leaf nodes, it's the caller's responsibility to simplify
    377    /// them before calling this if needed.
    378    fn try_sum_in_place(&mut self, other: &Self) -> Result<(), ()> {
    379        use self::Leaf::*;
    380 
    381        if std::mem::discriminant(self) != std::mem::discriminant(other) {
    382            return Err(());
    383        }
    384 
    385        match (self, other) {
    386            (&mut Number(ref mut one), &Number(ref other))
    387            | (&mut Percentage(ref mut one), &Percentage(ref other)) => {
    388                *one += *other;
    389            },
    390            (&mut Angle(ref mut one), &Angle(ref other)) => {
    391                *one = specified::Angle::from_calc(one.degrees() + other.degrees());
    392            },
    393            (&mut Time(ref mut one), &Time(ref other)) => {
    394                *one = specified::Time::from_seconds(one.seconds() + other.seconds());
    395            },
    396            (&mut Resolution(ref mut one), &Resolution(ref other)) => {
    397                *one = specified::Resolution::from_dppx(one.dppx() + other.dppx());
    398            },
    399            (&mut Length(ref mut one), &Length(ref other)) => {
    400                *one = one.try_op(other, std::ops::Add::add)?;
    401            },
    402            (&mut ColorComponent(_), &ColorComponent(_)) => {
    403                // Can not get the sum of color components, because they haven't been resolved yet.
    404                return Err(());
    405            },
    406            _ => {
    407                match *other {
    408                    Number(..) | Percentage(..) | Angle(..) | Time(..) | Resolution(..)
    409                    | Length(..) | ColorComponent(..) => {},
    410                }
    411                unsafe {
    412                    debug_unreachable!();
    413                }
    414            },
    415        }
    416 
    417        Ok(())
    418    }
    419 
    420    fn try_product_in_place(&mut self, other: &mut Self) -> bool {
    421        if let Self::Number(ref mut left) = *self {
    422            if let Self::Number(ref right) = *other {
    423                // Both sides are numbers, so we can just modify the left side.
    424                *left *= *right;
    425                true
    426            } else {
    427                // The right side is not a number, so the result should be in the units of the right
    428                // side.
    429                if other.map(|v| v * *left).is_ok() {
    430                    std::mem::swap(self, other);
    431                    true
    432                } else {
    433                    false
    434                }
    435            }
    436        } else if let Self::Number(ref right) = *other {
    437            // The left side is not a number, but the right side is, so the result is the left
    438            // side unit.
    439            self.map(|v| v * *right).is_ok()
    440        } else {
    441            // Neither side is a number, so a product is not possible.
    442            false
    443        }
    444    }
    445 
    446    fn try_op<O>(&self, other: &Self, op: O) -> Result<Self, ()>
    447    where
    448        O: Fn(f32, f32) -> f32,
    449    {
    450        use self::Leaf::*;
    451 
    452        if std::mem::discriminant(self) != std::mem::discriminant(other) {
    453            return Err(());
    454        }
    455 
    456        match (self, other) {
    457            (&Number(one), &Number(other)) => {
    458                return Ok(Leaf::Number(op(one, other)));
    459            },
    460            (&Percentage(one), &Percentage(other)) => {
    461                return Ok(Leaf::Percentage(op(one, other)));
    462            },
    463            (&Angle(ref one), &Angle(ref other)) => {
    464                return Ok(Leaf::Angle(specified::Angle::from_calc(op(
    465                    one.degrees(),
    466                    other.degrees(),
    467                ))));
    468            },
    469            (&Resolution(ref one), &Resolution(ref other)) => {
    470                return Ok(Leaf::Resolution(specified::Resolution::from_dppx(op(
    471                    one.dppx(),
    472                    other.dppx(),
    473                ))));
    474            },
    475            (&Time(ref one), &Time(ref other)) => {
    476                return Ok(Leaf::Time(specified::Time::from_seconds(op(
    477                    one.seconds(),
    478                    other.seconds(),
    479                ))));
    480            },
    481            (&Length(ref one), &Length(ref other)) => {
    482                return Ok(Leaf::Length(one.try_op(other, op)?));
    483            },
    484            _ => {
    485                match *other {
    486                    Number(..) | Percentage(..) | Angle(..) | Time(..) | Length(..)
    487                    | Resolution(..) | ColorComponent(..) => {},
    488                }
    489                unsafe {
    490                    debug_unreachable!();
    491                }
    492            },
    493        }
    494    }
    495 
    496    fn map(&mut self, mut op: impl FnMut(f32) -> f32) -> Result<(), ()> {
    497        Ok(match self {
    498            Leaf::Length(one) => *one = one.map(op),
    499            Leaf::Angle(one) => *one = specified::Angle::from_calc(op(one.degrees())),
    500            Leaf::Time(one) => *one = specified::Time::from_seconds(op(one.seconds())),
    501            Leaf::Resolution(one) => *one = specified::Resolution::from_dppx(op(one.dppx())),
    502            Leaf::Percentage(one) => *one = op(*one),
    503            Leaf::Number(one) => *one = op(*one),
    504            Leaf::ColorComponent(..) => return Err(()),
    505        })
    506    }
    507 }
    508 
    509 impl GenericAnchorSide<Box<CalcNode>> {
    510    fn parse_in_calc<'i, 't>(
    511        context: &ParserContext,
    512        input: &mut Parser<'i, 't>,
    513    ) -> Result<Self, ParseError<'i>> {
    514        if let Ok(k) = input.try_parse(|i| AnchorSideKeyword::parse(i)) {
    515            return Ok(Self::Keyword(k));
    516        }
    517        Ok(Self::Percentage(Box::new(CalcNode::parse_argument(
    518            context,
    519            input,
    520            AllowParse::new(CalcUnits::PERCENTAGE),
    521        )?)))
    522    }
    523 }
    524 
    525 impl GenericAnchorFunction<Box<CalcNode>, Box<CalcNode>> {
    526    fn parse_in_calc<'i, 't>(
    527        context: &ParserContext,
    528        additional_functions: AdditionalFunctions,
    529        input: &mut Parser<'i, 't>,
    530    ) -> Result<Self, ParseError<'i>> {
    531        if !static_prefs::pref!("layout.css.anchor-positioning.enabled") {
    532            return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));
    533        }
    534        input.parse_nested_block(|i| {
    535            let target_element = i.try_parse(|i| DashedIdent::parse(context, i)).ok();
    536            let side = GenericAnchorSide::parse_in_calc(context, i)?;
    537            let target_element = if target_element.is_none() {
    538                i.try_parse(|i| DashedIdent::parse(context, i)).ok()
    539            } else {
    540                target_element
    541            };
    542            let fallback = i
    543                .try_parse(|i| {
    544                    i.expect_comma()?;
    545                    Ok::<Box<CalcNode>, ParseError<'i>>(Box::new(
    546                        CalcNode::parse_argument(
    547                            context,
    548                            i,
    549                            AllowParse {
    550                                units: CalcUnits::LENGTH_PERCENTAGE,
    551                                additional_functions,
    552                            },
    553                        )?
    554                        .into_length_or_percentage(AllowedNumericType::All)
    555                        .map_err(|_| i.new_custom_error(StyleParseErrorKind::UnspecifiedError))?
    556                        .node,
    557                    ))
    558                })
    559                .ok();
    560            Ok(Self {
    561                target_element: target_element.unwrap_or_else(DashedIdent::empty),
    562                side,
    563                fallback: fallback.into(),
    564            })
    565        })
    566    }
    567 }
    568 
    569 impl GenericAnchorSizeFunction<Box<CalcNode>> {
    570    fn parse_in_calc<'i, 't>(
    571        context: &ParserContext,
    572        input: &mut Parser<'i, 't>,
    573    ) -> Result<Self, ParseError<'i>> {
    574        if !static_prefs::pref!("layout.css.anchor-positioning.enabled") {
    575            return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));
    576        }
    577        GenericAnchorSizeFunction::parse_inner(context, input, |i| {
    578            Ok(Box::new(
    579                CalcNode::parse_argument(
    580                    context,
    581                    i,
    582                    AllowParse::new(CalcUnits::LENGTH_PERCENTAGE),
    583                )?
    584                .into_length_or_percentage(AllowedNumericType::All)
    585                .map_err(|_| i.new_custom_error(StyleParseErrorKind::UnspecifiedError))?
    586                .node,
    587            ))
    588        })
    589    }
    590 }
    591 
    592 /// Specified `anchor()` function in math functions.
    593 pub type CalcAnchorFunction = generic::GenericCalcAnchorFunction<Leaf>;
    594 /// Specified `anchor-size()` function in math functions.
    595 pub type CalcAnchorSizeFunction = generic::GenericCalcAnchorSizeFunction<Leaf>;
    596 
    597 /// A calc node representation for specified values.
    598 pub type CalcNode = generic::GenericCalcNode<Leaf>;
    599 impl CalcNode {
    600    /// Tries to parse a single element in the expression, that is, a
    601    /// `<length>`, `<angle>`, `<time>`, `<percentage>`, `<resolution>`, etc.
    602    ///
    603    /// May return a "complex" `CalcNode`, in the presence of a parenthesized
    604    /// expression, for example.
    605    fn parse_one<'i, 't>(
    606        context: &ParserContext,
    607        input: &mut Parser<'i, 't>,
    608        allowed: AllowParse,
    609    ) -> Result<Self, ParseError<'i>> {
    610        let location = input.current_source_location();
    611        match input.next()? {
    612            &Token::Number { value, .. } => Ok(CalcNode::Leaf(Leaf::Number(value))),
    613            &Token::Dimension {
    614                value, ref unit, ..
    615            } => {
    616                if allowed.includes(CalcUnits::LENGTH) {
    617                    if let Ok(l) = NoCalcLength::parse_dimension(context, value, unit) {
    618                        return Ok(CalcNode::Leaf(Leaf::Length(l)));
    619                    }
    620                }
    621                if allowed.includes(CalcUnits::ANGLE) {
    622                    if let Ok(a) = Angle::parse_dimension(value, unit, /* from_calc = */ true) {
    623                        return Ok(CalcNode::Leaf(Leaf::Angle(a)));
    624                    }
    625                }
    626                if allowed.includes(CalcUnits::TIME) {
    627                    if let Ok(t) = Time::parse_dimension(value, unit) {
    628                        return Ok(CalcNode::Leaf(Leaf::Time(t)));
    629                    }
    630                }
    631                if allowed.includes(CalcUnits::RESOLUTION) {
    632                    if let Ok(t) = Resolution::parse_dimension(value, unit) {
    633                        return Ok(CalcNode::Leaf(Leaf::Resolution(t)));
    634                    }
    635                }
    636                return Err(location.new_custom_error(StyleParseErrorKind::UnspecifiedError));
    637            },
    638            &Token::Percentage { unit_value, .. } if allowed.includes(CalcUnits::PERCENTAGE) => {
    639                Ok(CalcNode::Leaf(Leaf::Percentage(unit_value)))
    640            },
    641            &Token::ParenthesisBlock => {
    642                input.parse_nested_block(|input| CalcNode::parse_argument(context, input, allowed))
    643            },
    644            &Token::Function(ref name)
    645                if allowed
    646                    .additional_functions
    647                    .intersects(AdditionalFunctions::ANCHOR)
    648                    && name.eq_ignore_ascii_case("anchor") =>
    649            {
    650                let anchor_function = GenericAnchorFunction::parse_in_calc(
    651                    context,
    652                    allowed.additional_functions,
    653                    input,
    654                )?;
    655                Ok(CalcNode::Anchor(Box::new(anchor_function)))
    656            },
    657            &Token::Function(ref name)
    658                if allowed
    659                    .additional_functions
    660                    .intersects(AdditionalFunctions::ANCHOR_SIZE)
    661                    && name.eq_ignore_ascii_case("anchor-size") =>
    662            {
    663                let anchor_size_function =
    664                    GenericAnchorSizeFunction::parse_in_calc(context, input)?;
    665                Ok(CalcNode::AnchorSize(Box::new(anchor_size_function)))
    666            },
    667            &Token::Function(ref name) => {
    668                let function = CalcNode::math_function(context, name, location)?;
    669                CalcNode::parse(context, input, function, allowed)
    670            },
    671            &Token::Ident(ref ident) => {
    672                let leaf = match_ignore_ascii_case! { &**ident,
    673                    "e" => Leaf::Number(std::f32::consts::E),
    674                    "pi" => Leaf::Number(std::f32::consts::PI),
    675                    "infinity" => Leaf::Number(f32::INFINITY),
    676                    "-infinity" => Leaf::Number(f32::NEG_INFINITY),
    677                    "nan" => Leaf::Number(f32::NAN),
    678                    _ => {
    679                        if crate::color::parsing::rcs_enabled() &&
    680                            allowed.includes(CalcUnits::COLOR_COMPONENT)
    681                        {
    682                            if let Ok(channel_keyword) = ChannelKeyword::from_ident(&ident) {
    683                                Leaf::ColorComponent(channel_keyword)
    684                            } else {
    685                                return Err(location
    686                                    .new_unexpected_token_error(Token::Ident(ident.clone())));
    687                            }
    688                        } else {
    689                            return Err(
    690                                location.new_unexpected_token_error(Token::Ident(ident.clone()))
    691                            );
    692                        }
    693                    },
    694                };
    695                Ok(CalcNode::Leaf(leaf))
    696            },
    697            t => Err(location.new_unexpected_token_error(t.clone())),
    698        }
    699    }
    700 
    701    /// Parse a top-level `calc` expression, with all nested sub-expressions.
    702    ///
    703    /// This is in charge of parsing, for example, `2 + 3 * 100%`.
    704    pub fn parse<'i, 't>(
    705        context: &ParserContext,
    706        input: &mut Parser<'i, 't>,
    707        function: MathFunction,
    708        allowed: AllowParse,
    709    ) -> Result<Self, ParseError<'i>> {
    710        input.parse_nested_block(|input| {
    711            match function {
    712                MathFunction::Calc => Self::parse_argument(context, input, allowed),
    713                MathFunction::Clamp => {
    714                    let min = Self::parse_argument(context, input, allowed)?;
    715                    input.expect_comma()?;
    716                    let center = Self::parse_argument(context, input, allowed)?;
    717                    input.expect_comma()?;
    718                    let max = Self::parse_argument(context, input, allowed)?;
    719                    Ok(Self::Clamp {
    720                        min: Box::new(min),
    721                        center: Box::new(center),
    722                        max: Box::new(max),
    723                    })
    724                },
    725                MathFunction::Round => {
    726                    let strategy = input.try_parse(parse_rounding_strategy);
    727 
    728                    // <rounding-strategy> = nearest | up | down | to-zero
    729                    // https://drafts.csswg.org/css-values-4/#calc-syntax
    730                    fn parse_rounding_strategy<'i, 't>(
    731                        input: &mut Parser<'i, 't>,
    732                    ) -> Result<RoundingStrategy, ParseError<'i>> {
    733                        Ok(try_match_ident_ignore_ascii_case! { input,
    734                            "nearest" => RoundingStrategy::Nearest,
    735                            "up" => RoundingStrategy::Up,
    736                            "down" => RoundingStrategy::Down,
    737                            "to-zero" => RoundingStrategy::ToZero,
    738                        })
    739                    }
    740 
    741                    if strategy.is_ok() {
    742                        input.expect_comma()?;
    743                    }
    744 
    745                    let value = Self::parse_argument(context, input, allowed)?;
    746 
    747                    // <step> defaults to the number 1 if not provided
    748                    // https://drafts.csswg.org/css-values-4/#funcdef-round
    749                    let step = input.try_parse(|input| {
    750                        input.expect_comma()?;
    751                        Self::parse_argument(context, input, allowed)
    752                    });
    753 
    754                    let step = step.unwrap_or(Self::Leaf(Leaf::Number(1.0)));
    755 
    756                    Ok(Self::Round {
    757                        strategy: strategy.unwrap_or(RoundingStrategy::Nearest),
    758                        value: Box::new(value),
    759                        step: Box::new(step),
    760                    })
    761                },
    762                MathFunction::Mod | MathFunction::Rem => {
    763                    let dividend = Self::parse_argument(context, input, allowed)?;
    764                    input.expect_comma()?;
    765                    let divisor = Self::parse_argument(context, input, allowed)?;
    766 
    767                    let op = match function {
    768                        MathFunction::Mod => ModRemOp::Mod,
    769                        MathFunction::Rem => ModRemOp::Rem,
    770                        _ => unreachable!(),
    771                    };
    772                    Ok(Self::ModRem {
    773                        dividend: Box::new(dividend),
    774                        divisor: Box::new(divisor),
    775                        op,
    776                    })
    777                },
    778                MathFunction::Min | MathFunction::Max => {
    779                    // TODO(emilio): The common case for parse_comma_separated
    780                    // is just one element, but for min / max is two, really...
    781                    //
    782                    // Consider adding an API to cssparser to specify the
    783                    // initial vector capacity?
    784                    let arguments = input.parse_comma_separated(|input| {
    785                        let result = Self::parse_argument(context, input, allowed)?;
    786                        Ok(result)
    787                    })?;
    788 
    789                    let op = match function {
    790                        MathFunction::Min => MinMaxOp::Min,
    791                        MathFunction::Max => MinMaxOp::Max,
    792                        _ => unreachable!(),
    793                    };
    794 
    795                    Ok(Self::MinMax(arguments.into(), op))
    796                },
    797                MathFunction::Sin | MathFunction::Cos | MathFunction::Tan => {
    798                    let a = Self::parse_angle_argument(context, input)?;
    799 
    800                    let number = match function {
    801                        MathFunction::Sin => a.sin(),
    802                        MathFunction::Cos => a.cos(),
    803                        MathFunction::Tan => a.tan(),
    804                        _ => unsafe {
    805                            debug_unreachable!("We just checked!");
    806                        },
    807                    };
    808 
    809                    Ok(Self::Leaf(Leaf::Number(number)))
    810                },
    811                MathFunction::Asin | MathFunction::Acos | MathFunction::Atan => {
    812                    let a = Self::parse_number_argument(context, input)?;
    813 
    814                    let radians = match function {
    815                        MathFunction::Asin => a.asin(),
    816                        MathFunction::Acos => a.acos(),
    817                        MathFunction::Atan => a.atan(),
    818                        _ => unsafe {
    819                            debug_unreachable!("We just checked!");
    820                        },
    821                    };
    822 
    823                    Ok(Self::Leaf(Leaf::Angle(Angle::from_radians(radians))))
    824                },
    825                MathFunction::Atan2 => {
    826                    let allow_all = allowed.new_including(CalcUnits::ALL);
    827                    let a = Self::parse_argument(context, input, allow_all)?;
    828                    input.expect_comma()?;
    829                    let b = Self::parse_argument(context, input, allow_all)?;
    830 
    831                    let radians = Self::try_resolve(input, || {
    832                        if let Ok(a) = a.to_number() {
    833                            let b = b.to_number()?;
    834                            return Ok(a.atan2(b));
    835                        }
    836 
    837                        if let Ok(a) = a.to_percentage() {
    838                            let b = b.to_percentage()?;
    839                            return Ok(a.atan2(b));
    840                        }
    841 
    842                        if let Ok(a) = a.to_time(None) {
    843                            let b = b.to_time(None)?;
    844                            return Ok(a.seconds().atan2(b.seconds()));
    845                        }
    846 
    847                        if let Ok(a) = a.to_angle() {
    848                            let b = b.to_angle()?;
    849                            return Ok(a.radians().atan2(b.radians()));
    850                        }
    851 
    852                        if let Ok(a) = a.to_resolution() {
    853                            let b = b.to_resolution()?;
    854                            return Ok(a.dppx().atan2(b.dppx()));
    855                        }
    856 
    857                        let a = a.into_length_or_percentage(AllowedNumericType::All)?;
    858                        let b = b.into_length_or_percentage(AllowedNumericType::All)?;
    859                        let (a, b) = CalcLengthPercentage::same_unit_length_as(&a, &b).ok_or(())?;
    860 
    861                        Ok(a.atan2(b))
    862                    })?;
    863 
    864                    Ok(Self::Leaf(Leaf::Angle(Angle::from_radians(radians))))
    865                },
    866                MathFunction::Pow => {
    867                    let a = Self::parse_number_argument(context, input)?;
    868                    input.expect_comma()?;
    869                    let b = Self::parse_number_argument(context, input)?;
    870 
    871                    let number = a.powf(b);
    872 
    873                    Ok(Self::Leaf(Leaf::Number(number)))
    874                },
    875                MathFunction::Sqrt => {
    876                    let a = Self::parse_number_argument(context, input)?;
    877 
    878                    let number = a.sqrt();
    879 
    880                    Ok(Self::Leaf(Leaf::Number(number)))
    881                },
    882                MathFunction::Hypot => {
    883                    let arguments = input.parse_comma_separated(|input| {
    884                        let result = Self::parse_argument(context, input, allowed)?;
    885                        Ok(result)
    886                    })?;
    887 
    888                    Ok(Self::Hypot(arguments.into()))
    889                },
    890                MathFunction::Log => {
    891                    let a = Self::parse_number_argument(context, input)?;
    892                    let b = input
    893                        .try_parse(|input| {
    894                            input.expect_comma()?;
    895                            Self::parse_number_argument(context, input)
    896                        })
    897                        .ok();
    898 
    899                    let number = match b {
    900                        Some(b) => a.log(b),
    901                        None => a.ln(),
    902                    };
    903 
    904                    Ok(Self::Leaf(Leaf::Number(number)))
    905                },
    906                MathFunction::Exp => {
    907                    let a = Self::parse_number_argument(context, input)?;
    908                    let number = a.exp();
    909                    Ok(Self::Leaf(Leaf::Number(number)))
    910                },
    911                MathFunction::Abs => {
    912                    let node = Self::parse_argument(context, input, allowed)?;
    913                    Ok(Self::Abs(Box::new(node)))
    914                },
    915                MathFunction::Sign => {
    916                    // The sign of a percentage is dependent on the percentage basis, so if
    917                    // percentages aren't allowed (so there's no basis) we shouldn't allow them in
    918                    // sign(). The rest of the units are safe tho.
    919                    let node = Self::parse_argument(
    920                        context,
    921                        input,
    922                        allowed.new_including(CalcUnits::ALL - CalcUnits::PERCENTAGE),
    923                    )?;
    924                    Ok(Self::Sign(Box::new(node)))
    925                },
    926            }
    927        })
    928    }
    929 
    930    fn parse_angle_argument<'i, 't>(
    931        context: &ParserContext,
    932        input: &mut Parser<'i, 't>,
    933    ) -> Result<CSSFloat, ParseError<'i>> {
    934        let argument = Self::parse_argument(context, input, AllowParse::new(CalcUnits::ANGLE))?;
    935        argument
    936            .to_number()
    937            .or_else(|()| Ok(argument.to_angle()?.radians()))
    938            .map_err(|()| input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
    939    }
    940 
    941    fn parse_number_argument<'i, 't>(
    942        context: &ParserContext,
    943        input: &mut Parser<'i, 't>,
    944    ) -> Result<CSSFloat, ParseError<'i>> {
    945        Self::parse_argument(context, input, AllowParse::new(CalcUnits::empty()))?
    946            .to_number()
    947            .map_err(|()| input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
    948    }
    949 
    950    fn parse_argument<'i, 't>(
    951        context: &ParserContext,
    952        input: &mut Parser<'i, 't>,
    953        allowed: AllowParse,
    954    ) -> Result<Self, ParseError<'i>> {
    955        let mut sum = SmallVec::<[CalcNode; 1]>::new();
    956        let first = Self::parse_product(context, input, allowed)?;
    957        sum.push(first);
    958        loop {
    959            let start = input.state();
    960            match input.next_including_whitespace() {
    961                Ok(&Token::WhiteSpace(_)) => {
    962                    if input.is_exhausted() {
    963                        break; // allow trailing whitespace
    964                    }
    965                    match *input.next()? {
    966                        Token::Delim('+') => {
    967                            let rhs = Self::parse_product(context, input, allowed)?;
    968                            if sum.last_mut().unwrap().try_sum_in_place(&rhs).is_err() {
    969                                sum.push(rhs);
    970                            }
    971                        },
    972                        Token::Delim('-') => {
    973                            let mut rhs = Self::parse_product(context, input, allowed)?;
    974                            rhs.negate();
    975                            if sum.last_mut().unwrap().try_sum_in_place(&rhs).is_err() {
    976                                sum.push(rhs);
    977                            }
    978                        },
    979                        _ => {
    980                            input.reset(&start);
    981                            break;
    982                        },
    983                    }
    984                },
    985                _ => {
    986                    input.reset(&start);
    987                    break;
    988                },
    989            }
    990        }
    991 
    992        Ok(if sum.len() == 1 {
    993            sum.drain(..).next().unwrap()
    994        } else {
    995            Self::Sum(sum.into_boxed_slice().into())
    996        })
    997    }
    998 
    999    /// Parse a top-level `calc` expression, and all the products that may
   1000    /// follow, and stop as soon as a non-product expression is found.
   1001    ///
   1002    /// This should parse correctly:
   1003    ///
   1004    /// * `2`
   1005    /// * `2 * 2`
   1006    /// * `2 * 2 + 2` (but will leave the `+ 2` unparsed).
   1007    ///
   1008    fn parse_product<'i, 't>(
   1009        context: &ParserContext,
   1010        input: &mut Parser<'i, 't>,
   1011        allowed: AllowParse,
   1012    ) -> Result<Self, ParseError<'i>> {
   1013        let mut product = SmallVec::<[CalcNode; 1]>::new();
   1014        let first = Self::parse_one(context, input, allowed)?;
   1015        product.push(first);
   1016 
   1017        loop {
   1018            let start = input.state();
   1019            match input.next() {
   1020                Ok(&Token::Delim('*')) => {
   1021                    let mut rhs = Self::parse_one(context, input, allowed)?;
   1022 
   1023                    // We can unwrap here, becuase we start the function by adding a node to
   1024                    // the list.
   1025                    if !product.last_mut().unwrap().try_product_in_place(&mut rhs) {
   1026                        product.push(rhs);
   1027                    }
   1028                },
   1029                Ok(&Token::Delim('/')) => {
   1030                    let rhs = Self::parse_one(context, input, allowed)?;
   1031 
   1032                    enum InPlaceDivisionResult {
   1033                        /// The right was merged into the left.
   1034                        Merged,
   1035                        /// The right is not a number or could not be resolved, so the left is
   1036                        /// unchanged.
   1037                        Unchanged,
   1038                        /// The right was resolved, but was not a number, so the calculation is
   1039                        /// invalid.
   1040                        Invalid,
   1041                    }
   1042 
   1043                    fn try_division_in_place(
   1044                        left: &mut CalcNode,
   1045                        right: &CalcNode,
   1046                    ) -> InPlaceDivisionResult {
   1047                        if let Ok(resolved) = right.resolve() {
   1048                            if let Some(number) = resolved.as_number() {
   1049                                if number != 1.0 && left.is_product_distributive() {
   1050                                    if left.map(|l| l / number).is_err() {
   1051                                        return InPlaceDivisionResult::Invalid;
   1052                                    }
   1053                                    return InPlaceDivisionResult::Merged;
   1054                                }
   1055                            } else {
   1056                                // Color components are valid denominators, but they can't resolve
   1057                                // at parse time.
   1058                                return if resolved.unit().contains(CalcUnits::COLOR_COMPONENT) {
   1059                                    InPlaceDivisionResult::Unchanged
   1060                                } else {
   1061                                    InPlaceDivisionResult::Invalid
   1062                                };
   1063                            }
   1064                        }
   1065                        InPlaceDivisionResult::Unchanged
   1066                    }
   1067 
   1068                    // The right hand side of a division *must* be a number, so if we can
   1069                    // already resolve it, then merge it with the last node on the product list.
   1070                    // We can unwrap here, becuase we start the function by adding a node to
   1071                    // the list.
   1072                    match try_division_in_place(&mut product.last_mut().unwrap(), &rhs) {
   1073                        InPlaceDivisionResult::Merged => {},
   1074                        InPlaceDivisionResult::Unchanged => {
   1075                            product.push(Self::Invert(Box::new(rhs)))
   1076                        },
   1077                        InPlaceDivisionResult::Invalid => {
   1078                            return Err(
   1079                                input.new_custom_error(StyleParseErrorKind::UnspecifiedError)
   1080                            )
   1081                        },
   1082                    }
   1083                },
   1084                _ => {
   1085                    input.reset(&start);
   1086                    break;
   1087                },
   1088            }
   1089        }
   1090 
   1091        Ok(if product.len() == 1 {
   1092            product.drain(..).next().unwrap()
   1093        } else {
   1094            Self::Product(product.into_boxed_slice().into())
   1095        })
   1096    }
   1097 
   1098    fn try_resolve<'i, 't, F>(
   1099        input: &Parser<'i, 't>,
   1100        closure: F,
   1101    ) -> Result<CSSFloat, ParseError<'i>>
   1102    where
   1103        F: FnOnce() -> Result<CSSFloat, ()>,
   1104    {
   1105        closure().map_err(|()| input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
   1106    }
   1107 
   1108    /// Tries to simplify this expression into a `<length>` or `<percentage>`
   1109    /// value.
   1110    pub fn into_length_or_percentage(
   1111        mut self,
   1112        clamping_mode: AllowedNumericType,
   1113    ) -> Result<CalcLengthPercentage, ()> {
   1114        self.simplify_and_sort();
   1115 
   1116        // Although we allow numbers inside CalcLengthPercentage, calculations that resolve to a
   1117        // number result is still not allowed.
   1118        let unit = self.unit()?;
   1119        if !CalcUnits::LENGTH_PERCENTAGE.intersects(unit) {
   1120            Err(())
   1121        } else {
   1122            Ok(CalcLengthPercentage {
   1123                clamping_mode,
   1124                node: self,
   1125            })
   1126        }
   1127    }
   1128 
   1129    /// Tries to simplify this expression into a `<time>` value.
   1130    fn to_time(&self, clamping_mode: Option<AllowedNumericType>) -> Result<Time, ()> {
   1131        let seconds = if let Leaf::Time(time) = self.resolve()? {
   1132            time.seconds()
   1133        } else {
   1134            return Err(());
   1135        };
   1136 
   1137        Ok(Time::from_seconds_with_calc_clamping_mode(
   1138            seconds,
   1139            clamping_mode,
   1140        ))
   1141    }
   1142 
   1143    /// Tries to simplify the expression into a `<resolution>` value.
   1144    fn to_resolution(&self) -> Result<Resolution, ()> {
   1145        let dppx = if let Leaf::Resolution(resolution) = self.resolve()? {
   1146            resolution.dppx()
   1147        } else {
   1148            return Err(());
   1149        };
   1150 
   1151        Ok(Resolution::from_dppx_calc(dppx))
   1152    }
   1153 
   1154    /// Tries to simplify this expression into an `Angle` value.
   1155    fn to_angle(&self) -> Result<Angle, ()> {
   1156        let degrees = if let Leaf::Angle(angle) = self.resolve()? {
   1157            angle.degrees()
   1158        } else {
   1159            return Err(());
   1160        };
   1161 
   1162        let result = Angle::from_calc(degrees);
   1163        Ok(result)
   1164    }
   1165 
   1166    /// Tries to simplify this expression into a `<number>` value.
   1167    fn to_number(&self) -> Result<CSSFloat, ()> {
   1168        let number = if let Leaf::Number(number) = self.resolve()? {
   1169            number
   1170        } else {
   1171            return Err(());
   1172        };
   1173 
   1174        let result = number;
   1175 
   1176        Ok(result)
   1177    }
   1178 
   1179    /// Tries to simplify this expression into a `<percentage>` value.
   1180    fn to_percentage(&self) -> Result<CSSFloat, ()> {
   1181        if let Leaf::Percentage(percentage) = self.resolve()? {
   1182            Ok(percentage)
   1183        } else {
   1184            Err(())
   1185        }
   1186    }
   1187 
   1188    /// Given a function name, and the location from where the token came from,
   1189    /// return a mathematical function corresponding to that name or an error.
   1190    #[inline]
   1191    pub fn math_function<'i>(
   1192        _: &ParserContext,
   1193        name: &CowRcStr<'i>,
   1194        location: cssparser::SourceLocation,
   1195    ) -> Result<MathFunction, ParseError<'i>> {
   1196        let function = match MathFunction::from_ident(&*name) {
   1197            Ok(f) => f,
   1198            Err(()) => {
   1199                return Err(location.new_unexpected_token_error(Token::Function(name.clone())))
   1200            },
   1201        };
   1202 
   1203        Ok(function)
   1204    }
   1205 
   1206    /// Convenience parsing function for `<length> | <percentage>`, and, optionally, `anchor()`.
   1207    pub fn parse_length_or_percentage<'i, 't>(
   1208        context: &ParserContext,
   1209        input: &mut Parser<'i, 't>,
   1210        clamping_mode: AllowedNumericType,
   1211        function: MathFunction,
   1212        allow_anchor: AllowAnchorPositioningFunctions,
   1213    ) -> Result<CalcLengthPercentage, ParseError<'i>> {
   1214        let allowed = if allow_anchor == AllowAnchorPositioningFunctions::No {
   1215            AllowParse::new(CalcUnits::LENGTH_PERCENTAGE)
   1216        } else {
   1217            AllowParse {
   1218                units: CalcUnits::LENGTH_PERCENTAGE,
   1219                additional_functions: match allow_anchor {
   1220                    AllowAnchorPositioningFunctions::No => unreachable!(),
   1221                    AllowAnchorPositioningFunctions::AllowAnchorSize => {
   1222                        AdditionalFunctions::ANCHOR_SIZE
   1223                    },
   1224                    AllowAnchorPositioningFunctions::AllowAnchorAndAnchorSize => {
   1225                        AdditionalFunctions::ANCHOR | AdditionalFunctions::ANCHOR_SIZE
   1226                    },
   1227                },
   1228            }
   1229        };
   1230        Self::parse(context, input, function, allowed)?
   1231            .into_length_or_percentage(clamping_mode)
   1232            .map_err(|()| input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
   1233    }
   1234 
   1235    /// Convenience parsing function for percentages.
   1236    pub fn parse_percentage<'i, 't>(
   1237        context: &ParserContext,
   1238        input: &mut Parser<'i, 't>,
   1239        function: MathFunction,
   1240    ) -> Result<CSSFloat, ParseError<'i>> {
   1241        Self::parse(
   1242            context,
   1243            input,
   1244            function,
   1245            AllowParse::new(CalcUnits::PERCENTAGE),
   1246        )?
   1247        .to_percentage()
   1248        .map(crate::values::normalize)
   1249        .map_err(|()| input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
   1250    }
   1251 
   1252    /// Convenience parsing function for `<length>`.
   1253    pub fn parse_length<'i, 't>(
   1254        context: &ParserContext,
   1255        input: &mut Parser<'i, 't>,
   1256        clamping_mode: AllowedNumericType,
   1257        function: MathFunction,
   1258    ) -> Result<CalcLengthPercentage, ParseError<'i>> {
   1259        Self::parse(context, input, function, AllowParse::new(CalcUnits::LENGTH))?
   1260            .into_length_or_percentage(clamping_mode)
   1261            .map_err(|()| input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
   1262    }
   1263 
   1264    /// Convenience parsing function for `<number>`.
   1265    pub fn parse_number<'i, 't>(
   1266        context: &ParserContext,
   1267        input: &mut Parser<'i, 't>,
   1268        function: MathFunction,
   1269    ) -> Result<CSSFloat, ParseError<'i>> {
   1270        Self::parse(
   1271            context,
   1272            input,
   1273            function,
   1274            AllowParse::new(CalcUnits::empty()),
   1275        )?
   1276        .to_number()
   1277        .map_err(|()| input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
   1278    }
   1279 
   1280    /// Convenience parsing function for `<angle>`.
   1281    pub fn parse_angle<'i, 't>(
   1282        context: &ParserContext,
   1283        input: &mut Parser<'i, 't>,
   1284        function: MathFunction,
   1285    ) -> Result<Angle, ParseError<'i>> {
   1286        Self::parse(context, input, function, AllowParse::new(CalcUnits::ANGLE))?
   1287            .to_angle()
   1288            .map_err(|()| input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
   1289    }
   1290 
   1291    /// Convenience parsing function for `<time>`.
   1292    pub fn parse_time<'i, 't>(
   1293        context: &ParserContext,
   1294        input: &mut Parser<'i, 't>,
   1295        clamping_mode: AllowedNumericType,
   1296        function: MathFunction,
   1297    ) -> Result<Time, ParseError<'i>> {
   1298        Self::parse(context, input, function, AllowParse::new(CalcUnits::TIME))?
   1299            .to_time(Some(clamping_mode))
   1300            .map_err(|()| input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
   1301    }
   1302 
   1303    /// Convenience parsing function for `<resolution>`.
   1304    pub fn parse_resolution<'i, 't>(
   1305        context: &ParserContext,
   1306        input: &mut Parser<'i, 't>,
   1307        function: MathFunction,
   1308    ) -> Result<Resolution, ParseError<'i>> {
   1309        Self::parse(
   1310            context,
   1311            input,
   1312            function,
   1313            AllowParse::new(CalcUnits::RESOLUTION),
   1314        )?
   1315        .to_resolution()
   1316        .map_err(|()| input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
   1317    }
   1318 }