Skip to main content

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