style/values/specified/
calc.rs

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
9use crate::color::parsing::ChannelKeyword;
10use crate::derives::*;
11use crate::parser::{Parse, ParserContext};
12use crate::values::generics::calc::{
13    self as generic, CalcNodeLeaf, CalcUnits, MinMaxOp, ModRemOp, PositivePercentageBasis,
14    RoundingStrategy, SortKey,
15};
16use crate::values::generics::length::GenericAnchorSizeFunction;
17use crate::values::generics::position::{
18    AnchorSideKeyword, GenericAnchorFunction, GenericAnchorSide,
19};
20use crate::values::specified::length::{AbsoluteLength, FontRelativeLength, NoCalcLength};
21use crate::values::specified::length::{ContainerRelativeLength, ViewportPercentageLength};
22use crate::values::specified::{self, Angle, Resolution, Time};
23use crate::values::{serialize_number, serialize_percentage, CSSFloat, DashedIdent};
24use cssparser::{match_ignore_ascii_case, CowRcStr, Parser, Token};
25use debug_unreachable::debug_unreachable;
26use smallvec::SmallVec;
27use std::cmp;
28use std::fmt::{self, Write};
29use style_traits::values::specified::AllowedNumericType;
30use 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)]
36pub 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)]
84pub 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
101impl Leaf {
102    fn as_length(&self) -> Option<&NoCalcLength> {
103        match *self {
104            Self::Length(ref l) => Some(l),
105            _ => None,
106        }
107    }
108}
109
110impl 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
127impl 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)]
146pub struct CalcLengthPercentage {
147    #[css(skip)]
148    pub clamping_mode: AllowedNumericType,
149    pub node: CalcNode,
150}
151
152impl 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
170impl SpecifiedValueInfo for CalcLengthPercentage {}
171
172/// Should parsing anchor-positioning functions in `calc()` be allowed?
173#[derive(Clone, Copy, PartialEq)]
174pub 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
183bitflags! {
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)]
197pub 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
204impl 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
225impl 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::Sec,
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
509impl 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
525impl 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
569impl 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.
593pub type CalcAnchorFunction = generic::GenericCalcAnchorFunction<Leaf>;
594/// Specified `anchor-size()` function in math functions.
595pub type CalcAnchorSizeFunction = generic::GenericCalcAnchorSizeFunction<Leaf>;
596
597/// A calc node representation for specified values.
598pub type CalcNode = generic::GenericCalcNode<Leaf>;
599impl 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}