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