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