1use crate::color::AbsoluteColor;
10use crate::color::parsing::ChannelKeyword;
11use crate::derives::*;
12use crate::parser::{Parse, ParserContext};
13use crate::typed_om::{NumericBaseType, NumericType, ToTyped, TypedValue};
14use crate::values::DashedIdent;
15use crate::values::computed::{self, ToComputedValue};
16use crate::values::generics::Optional;
17use crate::values::generics::calc::{
18 self as generic, CalcNodeLeaf, CalcType, GenericAnchorFunctionFallback, GenericRandomFunction,
19 MinMaxOp, ModRemOp, ProgressClampingMode, RoundingStrategy, SimplificationResult, SortKey,
20};
21use crate::values::generics::length::GenericAnchorSizeFunction;
22use crate::values::generics::position::{
23 AnchorSideKeyword, GenericAnchorFunction, GenericAnchorSide, TreeScoped,
24};
25use crate::values::specified::length::NoCalcLength;
26use crate::values::specified::random::RandomKey;
27use crate::values::specified::{
28 NoCalcAngle, NoCalcNumber, NoCalcPercentage, NoCalcResolution, NoCalcTime, TreeCountingFunction,
29};
30use cssparser::{CowRcStr, Parser, Token, match_ignore_ascii_case};
31use debug_unreachable::debug_unreachable;
32use smallvec::SmallVec;
33use std::cmp;
34use std::convert::AsRef;
35use strum::IntoEnumIterator;
36use strum_macros::{AsRefStr, EnumIter};
37use style_traits::values::specified::AllowedNumericType;
38use style_traits::{ParseError, SpecifiedValueInfo, StyleParseErrorKind};
39use thin_vec::ThinVec;
40
41#[derive(AsRefStr, Clone, Copy, Debug, EnumIter, Parse)]
43#[strum(serialize_all = "lowercase")]
44pub enum MathFunction {
45 Calc,
47 Min,
49 Max,
51 Clamp,
53 Round,
55 Mod,
57 Rem,
59 Sin,
61 Cos,
63 Tan,
65 Asin,
67 Acos,
69 Atan,
71 Atan2,
73 Pow,
75 Sqrt,
77 Hypot,
79 Log,
81 Exp,
83 Abs,
85 Sign,
87 Progress,
89 Random,
91 #[strum(serialize = "sibling-count")]
93 SiblingCount,
94 #[strum(serialize = "sibling-index")]
96 SiblingIndex,
97}
98
99impl MathFunction {
100 pub fn variants() -> MathFunctionIter {
102 MathFunction::iter()
103 }
104}
105
106pub use crate::values::generics::calc::CalcPercentageLeaf;
107
108#[derive(Clone, Debug, MallocSizeOf, PartialEq, ToCss, ToShmem)]
110#[repr(u8)]
111pub enum Leaf {
112 Length(NoCalcLength),
114 Angle(NoCalcAngle),
116 Time(NoCalcTime),
118 Resolution(NoCalcResolution),
120 ColorComponent(ChannelKeyword),
122 Percentage(CalcPercentageLeaf),
124 Number(NoCalcNumber),
126 TreeCountingFunction(TreeCountingFunction),
128 RandomKey(Box<RandomKey>),
130}
131
132impl ToTyped for Leaf {
133 fn to_typed(&self, dest: &mut ThinVec<TypedValue>) -> Result<(), ()> {
134 match *self {
136 Self::Length(ref l) => l.to_typed(dest),
137 Self::Number(n) => n.to_typed(dest),
138 Self::Percentage(ref p) => p.to_typed(dest),
139 Self::Angle(ref a) => a.to_typed(dest),
140 Self::Time(t) => t.to_typed(dest),
141 _ => Err(()),
142 }
143 }
144}
145
146impl Leaf {
147 pub fn to_computed_value(
152 &self,
153 context: Option<&computed::Context>,
154 origin_color: Option<&AbsoluteColor>,
155 ) -> Result<Self, ()> {
156 Ok(match self {
157 Self::Length(l) => {
158 let px = match context {
159 Some(context) => l.to_computed_value(context).px(),
160 None => l.to_computed_pixel_length_without_context()?,
161 };
162 Self::Length(NoCalcLength::from_px(px))
163 },
164 Self::TreeCountingFunction(f) => match context {
165 Some(context) => {
166 Self::Number(NoCalcNumber::new(f.to_computed_value(context) as f32))
167 },
168 None => return Err(()),
169 },
170 Self::RandomKey(key) => match context {
171 Some(context) => Self::Number(NoCalcNumber::new(*key.to_computed_value(context))),
172 None => return Err(()),
173 },
174 Self::ColorComponent(channel_keyword) => {
175 let channel_value = origin_color
176 .and_then(|c| c.get_component_by_channel_keyword(*channel_keyword).ok());
177 match channel_value {
178 Some(value) => Self::Number(NoCalcNumber::new(value.unwrap_or(0.0))),
179 None => Self::ColorComponent(*channel_keyword),
182 }
183 },
184 Self::Angle(..)
187 | Self::Time(..)
188 | Self::Resolution(..)
189 | Self::Percentage(..)
190 | Self::Number(..) => self.clone(),
191 })
192 }
193}
194
195#[derive(Clone, Debug, MallocSizeOf, PartialEq, ToCss, ToShmem, ToTyped)]
202#[allow(missing_docs)]
203pub struct CalcNumeric {
204 #[css(skip)]
205 pub clamping_mode: AllowedNumericType,
206 pub node: CalcNode,
207}
208
209impl CalcNumeric {
210 pub fn with_clamping_mode(&self, clamping_mode: AllowedNumericType) -> Self {
212 Self {
213 clamping_mode,
214 node: self.node.clone(),
215 }
216 }
217
218 pub fn with_leaf_node(&self, leaf: Leaf) -> Self {
220 Self {
221 clamping_mode: self.clamping_mode,
222 node: CalcNode::Leaf(leaf),
223 }
224 }
225
226 pub fn resolve(
228 &self,
229 context: &computed::Context,
230 leaf_to_f32: impl FnOnce(Result<Leaf, ()>) -> f32,
231 ) -> f32 {
232 let result = self
233 .node
234 .to_computed_value(Some(context), None)
235 .and_then(|r| r.resolve());
236 self.clamping_mode.clamp(leaf_to_f32(result))
237 }
238
239 pub fn as_number(&self) -> Option<NoCalcNumber> {
241 match self.node.resolve() {
242 Ok(Leaf::Number(n)) => Some(n),
243 _ => None,
244 }
245 }
246
247 pub fn as_percentage(&self) -> Option<NoCalcPercentage> {
249 match self.node.resolve() {
250 Ok(Leaf::Percentage(p)) => Some(NoCalcPercentage::new(p.get())),
251 _ => None,
252 }
253 }
254
255 pub fn as_time(&self) -> Option<NoCalcTime> {
257 match self.node.resolve() {
258 Ok(Leaf::Time(t)) => Some(t),
259 _ => None,
260 }
261 }
262
263 pub fn as_resolution(&self) -> Option<NoCalcResolution> {
265 match self.node.resolve() {
266 Ok(Leaf::Resolution(r)) => Some(r),
267 _ => None,
268 }
269 }
270
271 pub fn as_angle(&self) -> Option<NoCalcAngle> {
273 match self.node.resolve() {
274 Ok(Leaf::Angle(a)) => Some(a),
275 _ => None,
276 }
277 }
278}
279
280impl SpecifiedValueInfo for CalcNumeric {}
281
282#[derive(Clone, Debug, MallocSizeOf, PartialEq, ToCss, ToShmem, ToTyped)]
284pub struct CalcLengthPercentage(pub CalcNumeric);
285
286impl SpecifiedValueInfo for CalcLengthPercentage {}
287
288#[derive(Clone, Copy, PartialEq)]
290pub enum AllowAnchorPositioningFunctions {
291 No,
293 AllowAnchorSize,
295 AllowAnchorAndAnchorSize,
297}
298
299bitflags! {
300 #[derive(Clone, Copy, PartialEq, Eq)]
303 pub struct AdditionalFunctions: u8 {
304 const ANCHOR = 1 << 0;
306 const ANCHOR_SIZE = 1 << 1;
308 }
309}
310
311#[derive(Copy, Clone, Debug, PartialEq)]
316pub enum PercentageContext {
317 NotAllowed,
319 Allowed(Optional<NumericBaseType>),
321}
322
323#[allow(missing_docs)]
324impl PercentageContext {
325 pub fn not_allowed() -> Self {
326 Self::NotAllowed
327 }
328
329 pub fn allowed() -> Self {
330 Self::Allowed(Optional::None)
331 }
332
333 pub fn allowed_with_hint(hint: NumericBaseType) -> Self {
334 Self::Allowed(Optional::Some(hint))
335 }
336}
337
338#[derive(Clone, Copy)]
340pub struct CalcParseFlags {
341 pub percentage_context: PercentageContext,
344 pub color_components: ChannelKeyword,
346 pub additional_functions: AdditionalFunctions,
348 pub in_place_operations: CalcNodeParseInPlaceOperations,
351}
352
353impl CalcParseFlags {
354 pub fn new(percentage_context: PercentageContext) -> Self {
356 Self {
357 percentage_context,
358 ..Default::default()
359 }
360 }
361}
362
363impl Default for CalcParseFlags {
364 fn default() -> Self {
365 Self {
366 percentage_context: PercentageContext::not_allowed(),
367 color_components: ChannelKeyword::empty(),
368 additional_functions: AdditionalFunctions::empty(),
369 in_place_operations: CalcNodeParseInPlaceOperations::Yes,
370 }
371 }
372}
373
374impl generic::CalcNodeLeaf for Leaf {
375 fn numeric_type(&self) -> NumericType {
376 match self {
377 Leaf::Length(_) => NumericType::length(),
378 Leaf::Angle(_) => NumericType::angle(),
379 Leaf::Time(_) => NumericType::time(),
380 Leaf::Resolution(_) => NumericType::resolution(),
381 Leaf::Percentage(p) => p.numeric_type(),
382 Leaf::ColorComponent(_)
383 | Leaf::Number(_)
384 | Leaf::TreeCountingFunction(_)
385 | Leaf::RandomKey(_) => NumericType::number(),
386 }
387 }
388
389 fn unitless_value(&self) -> Option<f32> {
390 Some(match *self {
391 Self::Length(ref l) => l.unitless_value(),
392 Self::Percentage(ref p) => p.get(),
393 Self::Number(ref n) => n.value(),
394 Self::Resolution(ref r) => r.dppx(),
395 Self::Angle(ref a) => a.degrees(),
396 Self::Time(ref t) => t.seconds(),
397 Self::RandomKey(ref k) => {
398 return match &**k {
399 RandomKey::Fixed(number) => number.resolve(),
400 RandomKey::CacheKey(_) => None,
401 };
402 },
403 Self::ColorComponent(_) | Self::TreeCountingFunction(_) => return None,
404 })
405 }
406
407 fn canonical_value(&self) -> Option<f32> {
408 Some(match *self {
409 Self::Length(ref l) => l.to_px_if_absolute()?,
410 Self::Percentage(ref p) => match p.hint {
411 Optional::Some(NumericBaseType::Percent) => p.get(),
415 _ => return None,
416 },
417 Self::Number(ref n) => n.value(),
418 Self::Resolution(ref r) => r.dppx(),
419 Self::Angle(ref a) => a.degrees(),
420 Self::Time(ref t) => t.seconds(),
421 Self::RandomKey(ref k) => {
422 return match &**k {
423 RandomKey::Fixed(number) => number.resolve(),
424 RandomKey::CacheKey(_) => None,
425 };
426 },
427 Self::ColorComponent(_) | Self::TreeCountingFunction(_) => return None,
428 })
429 }
430
431 fn is_same_unit_as(&self, other: &Self) -> bool {
432 use self::Leaf::*;
433
434 if std::mem::discriminant(self) != std::mem::discriminant(other) {
435 return false;
436 }
437
438 match (self, other) {
439 (Length(a), Length(b)) => a.length_unit() == b.length_unit(),
440 (Angle(a), Angle(b)) => a.angle_unit() == b.angle_unit(),
441 (Time(a), Time(b)) => a.time_unit() == b.time_unit(),
442 (Resolution(a), Resolution(b)) => a.resolution_unit() == b.resolution_unit(),
443 (ColorComponent(_), ColorComponent(_))
444 | (Percentage(_), Percentage(_))
445 | (Number(_), Number(_))
446 | (TreeCountingFunction(_), TreeCountingFunction(_))
447 | (RandomKey(_), RandomKey(_)) => true,
448 _ => {
449 match *other {
450 Number(..)
451 | Percentage(..)
452 | Angle(..)
453 | Time(..)
454 | Resolution(..)
455 | Length(..)
456 | ColorComponent(..)
457 | TreeCountingFunction(..)
458 | RandomKey(..) => {},
459 }
460 unsafe {
461 debug_unreachable!();
462 }
463 },
464 }
465 }
466
467 fn as_percentage(&self) -> Option<(f32, Optional<NumericBaseType>)> {
468 match *self {
469 Self::Percentage(p) => Some((p.get(), p.hint)),
470 _ => None,
471 }
472 }
473
474 fn as_angle_radians(&self) -> Option<f32> {
475 if let Self::Angle(ref a) = *self {
476 Some(a.radians())
477 } else {
478 None
479 }
480 }
481
482 fn new_angle_from_radians(radians: f32) -> Self {
483 Self::Angle(NoCalcAngle::from_degrees(radians.to_degrees()))
484 }
485
486 fn new_number(value: f32) -> Self {
487 Self::Number(NoCalcNumber::new(value))
488 }
489
490 fn new_from_typed_value(value: f32, numeric_type: NumericType) -> Result<Self, ()> {
491 let calc_type = numeric_type.as_calc_type()?;
492 let percent_hint = numeric_type.percent_hint();
493 Ok(match calc_type {
494 CalcType::Number => Self::new_number(value),
495 CalcType::Length => Self::Length(NoCalcLength::from_px(value)),
496 CalcType::Angle => Self::Angle(NoCalcAngle::from_degrees(value)),
497 CalcType::Time => Self::Time(NoCalcTime::from_seconds(value)),
498 CalcType::Resolution => Self::Resolution(NoCalcResolution::from_dppx(value)),
499 CalcType::Percentage => Self::Percentage(CalcPercentageLeaf::new(value, percent_hint)),
500 })
501 }
502
503 fn compare(&self, other: &Self) -> Option<cmp::Ordering> {
504 use self::Leaf::*;
505
506 if std::mem::discriminant(self) != std::mem::discriminant(other) {
507 return None;
508 }
509
510 if matches!(self, Percentage(p) if p.hint != Optional::Some(NumericBaseType::Percent)) {
512 return None;
513 }
514
515 let self_negative = self.is_negative().unwrap_or(false);
516 if self_negative != other.is_negative().unwrap_or(false) {
517 return Some(if self_negative {
518 cmp::Ordering::Less
519 } else {
520 cmp::Ordering::Greater
521 });
522 }
523
524 match (self, other) {
525 (Percentage(one), Percentage(other)) => one.get().partial_cmp(&other.get()),
526 (Length(one), Length(other)) => one.partial_cmp(other),
527 (Angle(one), Angle(other)) => one.degrees().partial_cmp(&other.degrees()),
528 (Time(one), Time(other)) => one.seconds().partial_cmp(&other.seconds()),
529 (Resolution(one), Resolution(other)) => one.dppx().partial_cmp(&other.dppx()),
530 (Number(one), Number(other)) => one.partial_cmp(other),
531 (ColorComponent(one), ColorComponent(other)) => one.partial_cmp(other),
532 (TreeCountingFunction(one), TreeCountingFunction(other)) => one.partial_cmp(other),
533 (RandomKey(_), RandomKey(_)) => None,
534 _ => {
535 match *self {
536 Length(..)
537 | Percentage(..)
538 | Angle(..)
539 | Time(..)
540 | Number(..)
541 | Resolution(..)
542 | ColorComponent(..)
543 | TreeCountingFunction(..)
544 | RandomKey(..) => {},
545 }
546 unsafe {
547 debug_unreachable!("Forgot a branch?");
548 }
549 },
550 }
551 }
552
553 fn as_number(&self) -> Option<f32> {
554 match *self {
555 Leaf::Length(_)
556 | Leaf::Angle(_)
557 | Leaf::Time(_)
558 | Leaf::Resolution(_)
559 | Leaf::Percentage(_)
560 | Leaf::ColorComponent(_)
561 | Leaf::TreeCountingFunction(_)
562 | Leaf::RandomKey(_) => None,
563 Leaf::Number(n) => Some(n.value()),
564 }
565 }
566
567 fn sort_key(&self) -> SortKey {
568 match *self {
569 Self::Number(..) => SortKey::Number,
570 Self::Percentage(..) => SortKey::Percentage,
571 Self::Time(..) => SortKey::S,
572 Self::Resolution(..) => SortKey::Dppx,
573 Self::Angle(..) => SortKey::Deg,
574 Self::Length(ref l) => l.sort_key(),
575 Self::ColorComponent(..) => SortKey::ColorComponent,
576 Self::TreeCountingFunction(..) | Self::RandomKey(..) => SortKey::Other,
577 }
578 }
579
580 fn simplify(&mut self) -> SimplificationResult {
581 match self {
582 Leaf::Length(l) => {
583 if let Some(px) = l.to_px_if_absolute() {
584 *l = NoCalcLength::from_px(px);
585 return SimplificationResult::Simplified;
586 }
587 },
588 Leaf::Resolution(r) => {
589 *r = NoCalcResolution::from_dppx(r.dppx());
590 return SimplificationResult::Simplified;
591 },
592 Leaf::Time(t) => {
593 *t = NoCalcTime::from_seconds(t.seconds());
594 return SimplificationResult::Simplified;
595 },
596 Leaf::Angle(a) => {
597 *a = NoCalcAngle::from_degrees(a.degrees());
598 return SimplificationResult::Simplified;
599 },
600 _ => (),
601 }
602 SimplificationResult::Unchanged
603 }
604
605 fn try_sum_in_place(&mut self, other: &Self) -> Result<(), ()> {
610 use self::Leaf::*;
611
612 if std::mem::discriminant(self) != std::mem::discriminant(other) {
613 return Err(());
614 }
615
616 match (self, other) {
617 (&mut Number(ref mut one), Number(other)) => {
618 *one = NoCalcNumber::new(one.value() + other.value());
619 },
620 (&mut Percentage(ref mut one), Percentage(other)) => {
621 *one = CalcPercentageLeaf::new(one.get() + other.get(), one.combined_hint(other));
622 },
623 (&mut Angle(ref mut one), Angle(other)) => {
624 *one = NoCalcAngle::from_degrees(one.degrees() + other.degrees());
625 },
626 (&mut Time(ref mut one), Time(other)) => {
627 *one = NoCalcTime::from_seconds(one.seconds() + other.seconds());
628 },
629 (&mut Resolution(ref mut one), Resolution(other)) => {
630 *one = NoCalcResolution::from_dppx(one.dppx() + other.dppx());
631 },
632 (&mut Length(ref mut one), Length(other)) => {
633 *one = one.try_op(other, std::ops::Add::add)?;
634 },
635 (&mut ColorComponent(_), &ColorComponent(_)) => {
636 return Err(());
638 },
639 (&mut TreeCountingFunction(_), &TreeCountingFunction(_)) => {
640 return Err(());
642 },
643 (&mut RandomKey(_), &RandomKey(_)) => {
644 return Err(());
646 },
647 _ => {
648 match *other {
649 Number(..)
650 | Percentage(..)
651 | Angle(..)
652 | Time(..)
653 | Resolution(..)
654 | Length(..)
655 | ColorComponent(..)
656 | TreeCountingFunction(..)
657 | RandomKey(..) => {},
658 }
659 unsafe {
660 debug_unreachable!();
661 }
662 },
663 }
664
665 Ok(())
666 }
667
668 fn try_product_in_place(&mut self, other: &mut Self) -> bool {
669 if let Self::Number(ref mut left) = *self {
670 if let Self::Number(ref right) = *other {
671 *left = NoCalcNumber::new(left.value() * right.value());
673 true
674 } else {
675 let left_val = left.value();
678 if other.map(|v| v * left_val).is_ok() {
679 std::mem::swap(self, other);
680 true
681 } else {
682 false
683 }
684 }
685 } else if let Self::Number(ref right) = *other {
686 let right_val = right.value();
689 self.map(|v| v * right_val).is_ok()
690 } else {
691 false
693 }
694 }
695
696 fn try_op<O>(&self, other: &Self, op: O) -> Result<Self, ()>
697 where
698 O: Fn(f32, f32) -> f32,
699 {
700 use self::Leaf::*;
701
702 if std::mem::discriminant(self) != std::mem::discriminant(other) {
703 return Err(());
704 }
705
706 match (self, other) {
707 (&Number(one), &Number(other)) => Ok(Leaf::Number(NoCalcNumber::new(op(
708 one.value(),
709 other.value(),
710 )))),
711 (Percentage(one), Percentage(other)) => Ok(Leaf::Percentage(CalcPercentageLeaf::new(
712 op(one.get(), other.get()),
713 one.combined_hint(other),
714 ))),
715 (Angle(one), Angle(other)) => Ok(Leaf::Angle(NoCalcAngle::from_degrees(op(
716 one.degrees(),
717 other.degrees(),
718 )))),
719 (Resolution(one), Resolution(other)) => Ok(Leaf::Resolution(
720 NoCalcResolution::from_dppx(op(one.dppx(), other.dppx())),
721 )),
722 (Time(one), Time(other)) => Ok(Leaf::Time(NoCalcTime::from_seconds(op(
723 one.seconds(),
724 other.seconds(),
725 )))),
726 (Length(one), Length(other)) => Ok(Leaf::Length(one.try_op(other, op)?)),
727 (&ColorComponent(..), &ColorComponent(..)) => Err(()),
728 (&TreeCountingFunction(_), &TreeCountingFunction(_)) => Err(()),
729 (&RandomKey(_), &RandomKey(_)) => Err(()),
730 _ => {
731 match *other {
732 Number(..)
733 | Percentage(..)
734 | Angle(..)
735 | Time(..)
736 | Length(..)
737 | Resolution(..)
738 | ColorComponent(..)
739 | TreeCountingFunction(..)
740 | RandomKey(..) => {},
741 }
742 unsafe {
743 debug_unreachable!();
744 }
745 },
746 }
747 }
748
749 fn map(&mut self, mut op: impl FnMut(f32) -> f32) -> Result<(), ()> {
750 let _: () = match self {
751 Leaf::Length(one) => *one = one.map(op),
752 Leaf::Angle(one) => *one = NoCalcAngle::from_degrees(op(one.degrees())),
753 Leaf::Time(one) => *one = NoCalcTime::from_seconds(op(one.seconds())),
754 Leaf::Resolution(one) => *one = NoCalcResolution::from_dppx(op(one.dppx())),
755 Leaf::Percentage(one) => *one = CalcPercentageLeaf::new(op(one.get()), one.hint),
756 Leaf::Number(one) => *one = NoCalcNumber::new(op(one.value())),
757 Leaf::ColorComponent(..) | Leaf::TreeCountingFunction(..) | Leaf::RandomKey(..) => {
758 return Err(());
759 },
760 };
761 Ok(())
762 }
763
764 fn should_serialize_with_root_calc_wrapper(&self) -> bool {
765 match self {
766 Leaf::Length(_)
767 | Leaf::Angle(_)
768 | Leaf::Time(_)
769 | Leaf::Resolution(_)
770 | Leaf::ColorComponent(_)
771 | Leaf::Percentage(_)
772 | Leaf::Number(_) => true,
773 Leaf::TreeCountingFunction(_) | Leaf::RandomKey(_) => false,
774 }
775 }
776}
777
778impl GenericAnchorSide<Box<CalcNode>> {
779 fn parse_in_calc(context: &ParserContext, input: &mut Parser) -> Result<Self, ParseError> {
780 if let Ok(k) = input.try_parse(|i| AnchorSideKeyword::parse(i)) {
781 return Ok(Self::Keyword(k));
782 }
783 Ok(Self::Percentage(Box::new(CalcNode::parse_argument(
784 context,
785 input,
786 CalcParseFlags::new(PercentageContext::allowed_with_hint(
787 NumericBaseType::Percent,
788 )),
789 )?)))
790 }
791}
792
793fn parse_anchor_function_fallback(
794 context: &ParserContext,
795 additional_functions: AdditionalFunctions,
796 input: &mut Parser,
797) -> Result<Box<GenericAnchorFunctionFallback<Leaf>>, ParseError> {
798 if let Ok(l) = input.try_parse(|i| -> Result<CalcNode, ParseError> {
799 Ok(CalcNode::Leaf(match *(i.next()?) {
800 Token::Number { value, .. } => {
801 if value != 0.0 {
802 return Err(ParseError::custom(StyleParseErrorKind::UnspecifiedError));
803 }
804 Leaf::Length(NoCalcLength::from_px(0.0))
805 },
806 Token::Dimension {
807 value, ref unit, ..
808 } => Leaf::Length(
809 NoCalcLength::parse_dimension_with_context(context, value, unit)
810 .map_err(|_| ParseError::custom(StyleParseErrorKind::UnspecifiedError))?,
811 ),
812 Token::Percentage { unit_value, .. } => Leaf::Percentage(CalcPercentageLeaf::new(
813 unit_value,
814 Optional::Some(NumericBaseType::Length),
815 )),
816 _ => return Err(ParseError::custom(StyleParseErrorKind::UnspecifiedError)),
817 }))
818 }) {
819 return Ok(Box::new(GenericAnchorFunctionFallback::new(false, l)));
820 }
821 let node = CalcNode::parse_argument(
822 context,
823 input,
824 CalcParseFlags {
825 additional_functions,
826 percentage_context: PercentageContext::allowed_with_hint(NumericBaseType::Length),
827 ..Default::default()
828 },
829 )?
830 .into_length_or_percentage(AllowedNumericType::All)
831 .map_err(|_| ParseError::custom(StyleParseErrorKind::UnspecifiedError))?
832 .0
833 .node;
834 Ok(Box::new(GenericAnchorFunctionFallback::new(true, node)))
835}
836
837impl GenericAnchorFunction<Box<CalcNode>, Box<GenericAnchorFunctionFallback<Leaf>>> {
838 fn parse_in_calc(
839 context: &ParserContext,
840 additional_functions: AdditionalFunctions,
841 input: &mut Parser,
842 ) -> Result<Self, ParseError> {
843 if !crate::pref!("layout.css.anchor-positioning.enabled", gecko = true) {
844 return Err(ParseError::custom(StyleParseErrorKind::UnspecifiedError));
845 }
846 input.parse_nested_block(|i| {
847 let target_element = i.try_parse(|i| DashedIdent::parse(context, i)).ok();
848 let side = GenericAnchorSide::parse_in_calc(context, i)?;
849 let target_element = if target_element.is_none() {
850 i.try_parse(|i| DashedIdent::parse(context, i)).ok()
851 } else {
852 target_element
853 };
854 let fallback = i
855 .try_parse(|i| {
856 i.expect_comma()?;
857 parse_anchor_function_fallback(context, additional_functions, i)
858 })
859 .ok();
860 Ok(Self {
861 target_element: TreeScoped::with_default_level(
862 target_element.unwrap_or_else(DashedIdent::empty),
863 ),
864 side,
865 fallback: fallback.into(),
866 })
867 })
868 }
869}
870
871impl GenericAnchorSizeFunction<Box<GenericAnchorFunctionFallback<Leaf>>> {
872 fn parse_in_calc(context: &ParserContext, input: &mut Parser) -> Result<Self, ParseError> {
873 if !crate::pref!("layout.css.anchor-positioning.enabled", gecko = true) {
874 return Err(ParseError::custom(StyleParseErrorKind::UnspecifiedError));
875 }
876 GenericAnchorSizeFunction::parse_inner(context, input, |i| {
877 parse_anchor_function_fallback(context, AdditionalFunctions::ANCHOR_SIZE, i)
878 })
879 }
880}
881
882pub type CalcAnchorFunction = generic::GenericCalcAnchorFunction<Leaf>;
884pub type CalcAnchorSizeFunction = generic::GenericCalcAnchorSizeFunction<Leaf>;
886
887#[derive(Clone, Copy, PartialEq, Eq)]
889pub enum CalcNodeParseInPlaceOperations {
890 No,
892 Yes,
894}
895
896pub type SpecifiedCalcNode = generic::GenericCalcNode<Leaf>;
898pub use self::SpecifiedCalcNode as CalcNode;
899
900impl CalcNode {
901 fn parse_one(
907 context: &ParserContext,
908 input: &mut Parser,
909 flags: CalcParseFlags,
910 ) -> Result<Self, ParseError> {
911 match input.next()? {
912 &Token::Number { value, .. } => {
913 Ok(CalcNode::Leaf(Leaf::Number(NoCalcNumber::new(value))))
914 },
915 &Token::Dimension {
916 value, ref unit, ..
917 } => {
918 if let Ok(l) = NoCalcLength::parse_dimension_with_context(context, value, unit) {
919 return Ok(CalcNode::Leaf(Leaf::Length(l)));
920 }
921 if let Ok(a) = NoCalcAngle::parse_dimension(value, unit) {
922 return Ok(CalcNode::Leaf(Leaf::Angle(a)));
923 }
924 if let Ok(t) = NoCalcTime::parse_dimension(value, unit) {
925 return Ok(CalcNode::Leaf(Leaf::Time(t)));
926 }
927 if let Ok(t) = NoCalcResolution::parse_dimension(value, unit) {
928 return Ok(CalcNode::Leaf(Leaf::Resolution(t)));
929 }
930 Err(ParseError::custom(StyleParseErrorKind::UnspecifiedError))
931 },
932 &Token::Percentage { unit_value, .. } => {
933 let hint = match flags.percentage_context {
934 PercentageContext::NotAllowed => {
935 return Err(ParseError::custom(StyleParseErrorKind::UnspecifiedError));
936 },
937 PercentageContext::Allowed(hint) => hint,
938 };
939 Ok(CalcNode::Leaf(Leaf::Percentage(CalcPercentageLeaf::new(
940 unit_value, hint,
941 ))))
942 },
943 &Token::ParenthesisBlock => {
944 input.parse_nested_block(|input| CalcNode::parse_argument(context, input, flags))
945 },
946 Token::Function(name)
947 if flags
948 .additional_functions
949 .intersects(AdditionalFunctions::ANCHOR)
950 && name.eq_ignore_ascii_case("anchor") =>
951 {
952 let anchor_function = GenericAnchorFunction::parse_in_calc(
953 context,
954 flags.additional_functions,
955 input,
956 )?;
957 Ok(CalcNode::Anchor(Box::new(anchor_function)))
958 },
959 Token::Function(name)
960 if flags
961 .additional_functions
962 .intersects(AdditionalFunctions::ANCHOR_SIZE)
963 && name.eq_ignore_ascii_case("anchor-size") =>
964 {
965 let anchor_size_function =
966 GenericAnchorSizeFunction::parse_in_calc(context, input)?;
967 Ok(CalcNode::AnchorSize(Box::new(anchor_size_function)))
968 },
969 Token::Function(name) => {
970 let function = CalcNode::math_function(context, name)?;
971 CalcNode::parse(context, input, function, flags)
972 },
973 Token::Ident(ident) => {
974 let leaf = match_ignore_ascii_case! { &**ident,
975 "e" => Leaf::Number(NoCalcNumber::new(std::f32::consts::E)),
976 "pi" => Leaf::Number(NoCalcNumber::new(std::f32::consts::PI)),
977 "infinity" => Leaf::Number(NoCalcNumber::new(f32::INFINITY)),
978 "-infinity" => Leaf::Number(NoCalcNumber::new(f32::NEG_INFINITY)),
979 "nan" => Leaf::Number(NoCalcNumber::new(f32::NAN)),
980 _ => {
981 match ChannelKeyword::from_ident(ident) {
982 Ok(channel_keyword) if flags.color_components.contains(channel_keyword) => Leaf::ColorComponent(channel_keyword),
983 _ => return Err(ParseError::unexpected_token()),
984 }
985 },
986 };
987 Ok(CalcNode::Leaf(leaf))
988 },
989 _ => Err(ParseError::unexpected_token()),
990 }
991 }
992
993 pub fn parse(
997 context: &ParserContext,
998 input: &mut Parser,
999 function: MathFunction,
1000 flags: CalcParseFlags,
1001 ) -> Result<Self, ParseError> {
1002 input.parse_nested_block(|input| {
1003 fn consistent_type(a: &CalcNode, b: &CalcNode) -> Result<CalcType, ()> {
1004 let a_ty = a.numeric_type()?;
1005 let b_ty = b.numeric_type()?;
1006 NumericType::add_two_types(&a_ty, &b_ty).and_then(|ty| ty.as_calc_type())
1007 }
1008
1009 fn consistent_type_multi(arguments: &[CalcNode]) -> Result<CalcType, ()> {
1010 let mut ty = arguments.first().unwrap().numeric_type()?;
1011 for arg in arguments.iter().skip(1) {
1012 let arg_ty = arg.numeric_type()?;
1013 ty = NumericType::add_two_types(&ty, &arg_ty)?;
1014 }
1015 ty.as_calc_type()
1016 }
1017
1018 macro_rules! require_consistent_type {
1019 ($a:expr, $b:expr) => {{
1020 let _ = consistent_type(&$a, &$b)
1021 .map_err(|_| ParseError::custom(StyleParseErrorKind::UnspecifiedError))?;
1022 }};
1023 ($nodes:expr) => {{
1024 let _ = consistent_type_multi(&$nodes)
1025 .map_err(|_| ParseError::custom(StyleParseErrorKind::UnspecifiedError))?;
1026 }};
1027 }
1028
1029 match function {
1030 MathFunction::Calc => Self::parse_argument(context, input, flags),
1031 MathFunction::Clamp => {
1032 let min_val = if input
1033 .try_parse(|min| min.expect_ident_matching("none"))
1034 .ok()
1035 .is_none()
1036 {
1037 Some(Self::parse_argument(context, input, flags)?)
1038 } else {
1039 None
1040 };
1041
1042 input.expect_comma()?;
1043 let center = Self::parse_argument(context, input, flags)?;
1044 input.expect_comma()?;
1045
1046 let max_val = if input
1047 .try_parse(|max| max.expect_ident_matching("none"))
1048 .ok()
1049 .is_none()
1050 {
1051 Some(Self::parse_argument(context, input, flags)?)
1052 } else {
1053 None
1054 };
1055
1056 Ok(match (min_val, max_val) {
1063 (None, None) => center,
1064 (None, Some(max)) => {
1065 require_consistent_type!(center, max);
1066 Self::MinMax(vec![center, max].into(), MinMaxOp::Min)
1067 },
1068 (Some(min), None) => {
1069 require_consistent_type!(min, center);
1070 Self::MinMax(vec![min, center].into(), MinMaxOp::Max)
1071 },
1072 (Some(min), Some(max)) => {
1073 require_consistent_type!(min, center);
1074 require_consistent_type!(center, max);
1075 require_consistent_type!(min, max);
1076 Self::Clamp {
1077 min: Box::new(min),
1078 center: Box::new(center),
1079 max: Box::new(max),
1080 }
1081 },
1082 })
1083 },
1084 MathFunction::Round => {
1085 let strategy = input.try_parse(parse_rounding_strategy);
1086
1087 fn parse_rounding_strategy(
1090 input: &mut Parser,
1091 ) -> Result<RoundingStrategy, ParseError> {
1092 Ok(try_match_ident_ignore_ascii_case! { input,
1093 "nearest" => RoundingStrategy::Nearest,
1094 "up" => RoundingStrategy::Up,
1095 "down" => RoundingStrategy::Down,
1096 "to-zero" => RoundingStrategy::ToZero,
1097 })
1098 }
1099
1100 if strategy.is_ok() {
1101 input.expect_comma()?;
1102 }
1103
1104 let value = Self::parse_argument(context, input, flags)?;
1105
1106 let step = input.try_parse(|input| {
1109 input.expect_comma()?;
1110 Self::parse_argument(context, input, flags)
1111 });
1112
1113 let step = step.unwrap_or(Self::Leaf(Leaf::Number(NoCalcNumber::new(1.0))));
1114 require_consistent_type!(value, step);
1115
1116 Ok(Self::Round {
1117 strategy: strategy.unwrap_or(RoundingStrategy::Nearest),
1118 value: Box::new(value),
1119 step: Box::new(step),
1120 })
1121 },
1122 MathFunction::Mod | MathFunction::Rem => {
1123 let dividend = Self::parse_argument(context, input, flags)?;
1124 input.expect_comma()?;
1125 let divisor = Self::parse_argument(context, input, flags)?;
1126 require_consistent_type!(dividend, divisor);
1127
1128 let op = match function {
1129 MathFunction::Mod => ModRemOp::Mod,
1130 MathFunction::Rem => ModRemOp::Rem,
1131 _ => unreachable!(),
1132 };
1133 Ok(Self::ModRem {
1134 dividend: Box::new(dividend),
1135 divisor: Box::new(divisor),
1136 op,
1137 })
1138 },
1139 MathFunction::Min | MathFunction::Max => {
1140 let arguments = input.parse_comma_separated(|input| {
1146 let result = Self::parse_argument(context, input, flags)?;
1147 Ok(result)
1148 })?;
1149 require_consistent_type!(arguments);
1150
1151 let op = match function {
1152 MathFunction::Min => MinMaxOp::Min,
1153 MathFunction::Max => MinMaxOp::Max,
1154 _ => unreachable!(),
1155 };
1156
1157 Ok(Self::MinMax(arguments.into(), op))
1158 },
1159 MathFunction::Sin | MathFunction::Cos | MathFunction::Tan => {
1160 let node = Self::parse_argument(context, input, flags)?;
1161 Ok(match function {
1162 MathFunction::Sin => Self::Sin(Box::new(node)),
1163 MathFunction::Cos => Self::Cos(Box::new(node)),
1164 MathFunction::Tan => Self::Tan(Box::new(node)),
1165 _ => unsafe { debug_unreachable!("We just checked!") },
1166 })
1167 },
1168 MathFunction::Asin | MathFunction::Acos | MathFunction::Atan => {
1169 let node = Self::parse_argument(context, input, flags)?;
1170 Ok(match function {
1171 MathFunction::Asin => Self::Asin(Box::new(node)),
1172 MathFunction::Acos => Self::Acos(Box::new(node)),
1173 MathFunction::Atan => Self::Atan(Box::new(node)),
1174 _ => unsafe { debug_unreachable!("We just checked!") },
1175 })
1176 },
1177 MathFunction::Atan2 => {
1178 let a = Self::parse_argument(context, input, flags)?;
1179 input.expect_comma()?;
1180 let b = Self::parse_argument(context, input, flags)?;
1181 require_consistent_type!(a, b);
1182 Ok(Self::Atan2(Box::new(a), Box::new(b)))
1183 },
1184 MathFunction::Pow => {
1185 let a = Self::parse_argument(context, input, flags)?;
1186 input.expect_comma()?;
1187 let b = Self::parse_argument(context, input, flags)?;
1188 Ok(Self::Pow(Box::new(a), Box::new(b)))
1189 },
1190 MathFunction::Sqrt => {
1191 let a = Self::parse_argument(context, input, flags)?;
1192 Ok(Self::Sqrt(Box::new(a)))
1193 },
1194 MathFunction::Hypot => {
1195 let arguments = input.parse_comma_separated(|input| {
1196 let result = Self::parse_argument(context, input, flags)?;
1197 Ok(result)
1198 })?;
1199 require_consistent_type!(arguments);
1200 Ok(Self::Hypot(arguments.into()))
1201 },
1202 MathFunction::Log => {
1203 let a = Self::parse_argument(context, input, flags)?;
1204 let b = input
1205 .try_parse(|input| {
1206 input.expect_comma()?;
1207 Self::parse_argument(context, input, flags)
1208 })
1209 .ok();
1210 Ok(Self::Log(Box::new(a), b.map(Box::new).into()))
1211 },
1212 MathFunction::Exp => {
1213 let a = Self::parse_argument(context, input, flags)?;
1214 Ok(Self::Exp(Box::new(a)))
1215 },
1216 MathFunction::Abs => {
1217 let node = Self::parse_argument(context, input, flags)?;
1218 Ok(Self::Abs(Box::new(node)))
1219 },
1220 MathFunction::Sign => {
1221 let node = Self::parse_argument(context, input, flags)?;
1222 Ok(Self::Sign(Box::new(node)))
1223 },
1224 MathFunction::Progress => {
1225 if !crate::pref!("layout.css.progress-function.enabled") {
1226 return Err(ParseError::custom(StyleParseErrorKind::UnspecifiedError));
1227 }
1228
1229 let clamping_mode = input
1230 .try_parse(|i| ProgressClampingMode::parse(i))
1231 .unwrap_or(ProgressClampingMode::Clamp);
1232
1233 let value = Self::parse_argument(context, input, flags)?;
1234 input.expect_comma()?;
1235 let start = Self::parse_argument(context, input, flags)?;
1236 input.expect_comma()?;
1237 let end = Self::parse_argument(context, input, flags)?;
1238
1239 require_consistent_type!(value, start);
1240 require_consistent_type!(value, end);
1241 require_consistent_type!(start, end);
1242
1243 Ok(Self::Progress {
1244 clamping_mode,
1245 value: Box::new(value),
1246 start: Box::new(start),
1247 end: Box::new(end),
1248 })
1249 },
1250 MathFunction::Random => {
1251 if !crate::pref!("layout.css.random.enabled") {
1252 return Err(ParseError::custom(StyleParseErrorKind::UnspecifiedError));
1253 }
1254
1255 context
1258 .property_declaration_context
1259 .increment_random_count();
1260
1261 let key = match input.try_parse(|input| RandomKey::parse(context, input)) {
1262 Ok(key) => {
1263 input.expect_comma()?;
1264 key
1265 },
1266 Err(_) => RandomKey::auto(context)?,
1268 };
1269 let min = Self::parse_argument(context, input, flags)?;
1270 input.expect_comma()?;
1271 let max = Self::parse_argument(context, input, flags)?;
1272 let step = input.try_parse(|input| {
1273 input.expect_comma()?;
1274 Self::parse_argument(context, input, flags)
1275 });
1276
1277 Ok(Self::Random(Box::new(GenericRandomFunction {
1278 key: Self::Leaf(Leaf::RandomKey(Box::new(key))),
1279 min,
1280 max,
1281 step: step.ok().into(),
1282 })))
1283 },
1284 MathFunction::SiblingCount | MathFunction::SiblingIndex => {
1285 if !crate::pref!("layout.css.tree-counting-functions.enabled") {
1286 return Err(ParseError::custom(StyleParseErrorKind::UnspecifiedError));
1287 }
1288
1289 if !context.has_element_context() {
1290 return Err(ParseError::custom(StyleParseErrorKind::UnspecifiedError));
1291 }
1292
1293 input.expect_exhausted()?;
1295
1296 Ok(Self::Leaf(Leaf::TreeCountingFunction(match function {
1297 MathFunction::SiblingCount => TreeCountingFunction::SiblingCount,
1298 MathFunction::SiblingIndex => TreeCountingFunction::SiblingIndex,
1299 _ => unsafe { debug_unreachable!("We just checked!") },
1300 })))
1301 },
1302 }
1303 })
1304 }
1305
1306 fn parse_argument(
1307 context: &ParserContext,
1308 input: &mut Parser,
1309 flags: CalcParseFlags,
1310 ) -> Result<Self, ParseError> {
1311 let mut sum = SmallVec::<[CalcNode; 1]>::new();
1312 let first = Self::parse_product(context, input, flags)?;
1313 sum.push(first);
1314 loop {
1315 let start = input.state();
1316 match input.next_including_whitespace() {
1317 Ok(&Token::WhiteSpace(_)) => {
1318 if input.is_exhausted() {
1319 break; }
1321 match *input.next()? {
1322 Token::Delim('+') => {
1323 let rhs = Self::parse_product(context, input, flags)?;
1324 if flags.in_place_operations == CalcNodeParseInPlaceOperations::No
1325 || sum.last_mut().unwrap().try_sum_in_place(&rhs).is_err()
1326 {
1327 sum.push(rhs);
1328 }
1329 },
1330 Token::Delim('-') => {
1331 let mut rhs = Self::parse_product(context, input, flags)?;
1332 rhs.negate();
1333 if flags.in_place_operations == CalcNodeParseInPlaceOperations::No
1334 || sum.last_mut().unwrap().try_sum_in_place(&rhs).is_err()
1335 {
1336 sum.push(rhs);
1337 }
1338 },
1339 _ => {
1340 input.reset(&start);
1341 break;
1342 },
1343 }
1344 },
1345 _ => {
1346 input.reset(&start);
1347 break;
1348 },
1349 }
1350 }
1351
1352 Ok(if sum.len() == 1 {
1353 sum.drain(..).next().unwrap()
1354 } else {
1355 Self::Sum(sum.into_boxed_slice().into())
1356 })
1357 }
1358
1359 fn parse_product(
1369 context: &ParserContext,
1370 input: &mut Parser,
1371 flags: CalcParseFlags,
1372 ) -> Result<Self, ParseError> {
1373 let mut product = SmallVec::<[CalcNode; 1]>::new();
1374 let first = Self::parse_one(context, input, flags)?;
1375 product.push(first);
1376
1377 loop {
1378 let start = input.state();
1379 match input.next() {
1380 Ok(&Token::Delim('*')) => {
1381 let mut rhs = Self::parse_one(context, input, flags)?;
1382
1383 if flags.in_place_operations == CalcNodeParseInPlaceOperations::No
1386 || !product.last_mut().unwrap().try_product_in_place(&mut rhs)
1387 {
1388 product.push(rhs);
1389 }
1390 },
1391 Ok(&Token::Delim('/')) => {
1392 let rhs = Self::parse_one(context, input, flags)?;
1393
1394 enum InPlaceDivisionResult {
1395 Merged,
1397 Unchanged,
1400 Invalid,
1403 }
1404
1405 fn try_division_in_place(
1406 left: &mut CalcNode,
1407 right: &CalcNode,
1408 in_place_operations: CalcNodeParseInPlaceOperations,
1409 ) -> InPlaceDivisionResult {
1410 if in_place_operations == CalcNodeParseInPlaceOperations::No {
1411 return InPlaceDivisionResult::Unchanged;
1412 }
1413
1414 if let Ok(resolved) = right.resolve()
1415 && let Some(number) = resolved.as_number()
1416 && number != 1.0
1417 && left.is_product_distributive()
1418 {
1419 if left.map(|l| l / number).is_err() {
1420 return InPlaceDivisionResult::Invalid;
1421 }
1422 return InPlaceDivisionResult::Merged;
1423 }
1424 InPlaceDivisionResult::Unchanged
1425 }
1426
1427 match try_division_in_place(
1432 product.last_mut().unwrap(),
1433 &rhs,
1434 flags.in_place_operations,
1435 ) {
1436 InPlaceDivisionResult::Merged => {},
1437 InPlaceDivisionResult::Unchanged => {
1438 product.push(Self::Invert(Box::new(rhs)))
1439 },
1440 InPlaceDivisionResult::Invalid => {
1441 return Err(ParseError::custom(StyleParseErrorKind::UnspecifiedError));
1442 },
1443 }
1444 },
1445 _ => {
1446 input.reset(&start);
1447 break;
1448 },
1449 }
1450 }
1451
1452 Ok(if product.len() == 1 {
1453 product.drain(..).next().unwrap()
1454 } else {
1455 Self::Product(product.into_boxed_slice().into())
1456 })
1457 }
1458
1459 pub fn to_computed_value(
1463 &self,
1464 context: Option<&computed::Context>,
1465 origin_color: Option<&AbsoluteColor>,
1466 ) -> Result<Self, ()> {
1467 let mut failed = false;
1468 let result = self.map_leaves(|leaf| {
1469 match leaf.to_computed_value(context, origin_color) {
1470 Ok(c) => c,
1471 Err(()) => {
1472 failed = true;
1474 leaf.clone()
1475 },
1476 }
1477 });
1478 if failed {
1479 return Err(());
1480 }
1481 return Ok(result);
1482 }
1483
1484 pub fn into_length(
1487 mut self,
1488 clamping_mode: AllowedNumericType,
1489 ) -> Result<CalcLengthPercentage, ()> {
1490 self.simplify_and_sort();
1491
1492 if self.numeric_type_as_calc_type()? != CalcType::Length {
1493 return Err(());
1494 }
1495
1496 Ok(CalcLengthPercentage(CalcNumeric {
1497 clamping_mode,
1498 node: self,
1499 }))
1500 }
1501
1502 pub fn into_length_or_percentage(
1505 mut self,
1506 clamping_mode: AllowedNumericType,
1507 ) -> Result<CalcLengthPercentage, ()> {
1508 self.simplify_and_sort();
1509
1510 let ty = self.numeric_type_as_calc_type()?;
1511 if ty != CalcType::Length && ty != CalcType::Percentage {
1512 return Err(());
1513 }
1514
1515 Ok(CalcLengthPercentage(CalcNumeric {
1516 clamping_mode,
1517 node: self,
1518 }))
1519 }
1520
1521 fn into_time(mut self, clamping_mode: AllowedNumericType) -> Result<CalcNumeric, ()> {
1523 self.simplify_and_sort();
1524
1525 if self.numeric_type_as_calc_type()? != CalcType::Time {
1526 return Err(());
1527 }
1528
1529 Ok(CalcNumeric {
1530 clamping_mode,
1531 node: self,
1532 })
1533 }
1534
1535 fn into_resolution(mut self) -> Result<CalcNumeric, ()> {
1537 self.simplify_and_sort();
1538
1539 if self.numeric_type_as_calc_type()? != CalcType::Resolution {
1540 return Err(());
1541 }
1542
1543 Ok(CalcNumeric {
1544 clamping_mode: AllowedNumericType::NonNegative,
1545 node: self,
1546 })
1547 }
1548
1549 fn into_angle(mut self, clamping_mode: AllowedNumericType) -> Result<CalcNumeric, ()> {
1551 self.simplify_and_sort();
1552
1553 if self.numeric_type_as_calc_type()? != CalcType::Angle {
1554 return Err(());
1555 }
1556
1557 Ok(CalcNumeric {
1558 clamping_mode,
1559 node: self,
1560 })
1561 }
1562
1563 fn into_number(mut self, clamping_mode: AllowedNumericType) -> Result<CalcNumeric, ()> {
1566 self.simplify_and_sort();
1567
1568 if self.numeric_type_as_calc_type()? != CalcType::Number {
1569 return Err(());
1570 }
1571
1572 Ok(CalcNumeric {
1573 clamping_mode,
1574 node: self,
1575 })
1576 }
1577
1578 fn into_percentage(mut self, clamping_mode: AllowedNumericType) -> Result<CalcNumeric, ()> {
1581 self.simplify_and_sort();
1582
1583 if self.numeric_type_as_calc_type()? != CalcType::Percentage {
1584 return Err(());
1585 }
1586
1587 Ok(CalcNumeric {
1588 clamping_mode,
1589 node: self,
1590 })
1591 }
1592
1593 #[inline]
1596 pub fn math_function<'i>(
1597 _: &ParserContext,
1598 name: &CowRcStr<'i>,
1599 ) -> Result<MathFunction, ParseError> {
1600 let function = match MathFunction::from_ident(name) {
1601 Ok(f) => f,
1602 Err(()) => return Err(ParseError::unexpected_token()),
1603 };
1604
1605 Ok(function)
1606 }
1607
1608 pub fn parse_length_or_percentage(
1610 context: &ParserContext,
1611 input: &mut Parser,
1612 clamping_mode: AllowedNumericType,
1613 function: MathFunction,
1614 allow_anchor: AllowAnchorPositioningFunctions,
1615 ) -> Result<CalcLengthPercentage, ParseError> {
1616 let percentage_context = PercentageContext::allowed_with_hint(NumericBaseType::Length);
1617 let additional_functions = match allow_anchor {
1618 AllowAnchorPositioningFunctions::No => AdditionalFunctions::empty(),
1619 AllowAnchorPositioningFunctions::AllowAnchorSize => AdditionalFunctions::ANCHOR_SIZE,
1620 AllowAnchorPositioningFunctions::AllowAnchorAndAnchorSize => {
1621 AdditionalFunctions::ANCHOR | AdditionalFunctions::ANCHOR_SIZE
1622 },
1623 };
1624 let flags = CalcParseFlags {
1625 additional_functions,
1626 percentage_context,
1627 ..Default::default()
1628 };
1629 Self::parse(context, input, function, flags)?
1630 .into_length_or_percentage(clamping_mode)
1631 .map_err(|()| ParseError::custom(StyleParseErrorKind::UnspecifiedError))
1632 }
1633
1634 pub fn parse_percentage(
1636 context: &ParserContext,
1637 input: &mut Parser,
1638 clamping_mode: AllowedNumericType,
1639 function: MathFunction,
1640 ) -> Result<CalcNumeric, ParseError> {
1641 Self::parse(
1642 context,
1643 input,
1644 function,
1645 CalcParseFlags::new(PercentageContext::allowed_with_hint(
1646 NumericBaseType::Percent,
1647 )),
1648 )?
1649 .into_percentage(clamping_mode)
1650 .map_err(|()| ParseError::custom(StyleParseErrorKind::UnspecifiedError))
1651 }
1652
1653 pub fn parse_length(
1655 context: &ParserContext,
1656 input: &mut Parser,
1657 clamping_mode: AllowedNumericType,
1658 function: MathFunction,
1659 percentage_context: PercentageContext,
1660 ) -> Result<CalcLengthPercentage, ParseError> {
1661 Self::parse(
1662 context,
1663 input,
1664 function,
1665 CalcParseFlags::new(percentage_context),
1666 )?
1667 .into_length(clamping_mode)
1668 .map_err(|()| ParseError::custom(StyleParseErrorKind::UnspecifiedError))
1669 }
1670
1671 pub fn parse_number(
1673 context: &ParserContext,
1674 input: &mut Parser,
1675 clamping_mode: AllowedNumericType,
1676 function: MathFunction,
1677 percentage_context: PercentageContext,
1678 ) -> Result<CalcNumeric, ParseError> {
1679 Self::parse(
1680 context,
1681 input,
1682 function,
1683 CalcParseFlags::new(percentage_context),
1684 )?
1685 .into_number(clamping_mode)
1686 .map_err(|()| ParseError::custom(StyleParseErrorKind::UnspecifiedError))
1687 }
1688
1689 pub fn parse_angle(
1691 context: &ParserContext,
1692 input: &mut Parser,
1693 function: MathFunction,
1694 percentage_context: PercentageContext,
1695 ) -> Result<CalcNumeric, ParseError> {
1696 Self::parse(
1697 context,
1698 input,
1699 function,
1700 CalcParseFlags::new(percentage_context),
1701 )?
1702 .into_angle(AllowedNumericType::All)
1703 .map_err(|()| ParseError::custom(StyleParseErrorKind::UnspecifiedError))
1704 }
1705
1706 pub fn parse_time(
1708 context: &ParserContext,
1709 input: &mut Parser,
1710 clamping_mode: AllowedNumericType,
1711 function: MathFunction,
1712 percentage_context: PercentageContext,
1713 ) -> Result<CalcNumeric, ParseError> {
1714 Self::parse(
1715 context,
1716 input,
1717 function,
1718 CalcParseFlags::new(percentage_context),
1719 )?
1720 .into_time(clamping_mode)
1721 .map_err(|()| ParseError::custom(StyleParseErrorKind::UnspecifiedError))
1722 }
1723
1724 pub fn parse_resolution(
1726 context: &ParserContext,
1727 input: &mut Parser,
1728 function: MathFunction,
1729 percentage_context: PercentageContext,
1730 ) -> Result<CalcNumeric, ParseError> {
1731 Self::parse(
1732 context,
1733 input,
1734 function,
1735 CalcParseFlags::new(percentage_context),
1736 )?
1737 .into_resolution()
1738 .map_err(|()| ParseError::custom(StyleParseErrorKind::UnspecifiedError))
1739 }
1740}