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