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