1use 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#[derive(AsRefStr, Clone, Copy, Debug, EnumIter, Parse)]
41#[strum(serialize_all = "lowercase")]
42pub enum MathFunction {
43 Calc,
45 Min,
47 Max,
49 Clamp,
51 Round,
53 Mod,
55 Rem,
57 Sin,
59 Cos,
61 Tan,
63 Asin,
65 Acos,
67 Atan,
69 Atan2,
71 Pow,
73 Sqrt,
75 Hypot,
77 Log,
79 Exp,
81 Abs,
83 Sign,
85 Progress,
87 #[strum(serialize = "sibling-count")]
89 SiblingCount,
90 #[strum(serialize = "sibling-index")]
92 SiblingIndex,
93}
94
95impl MathFunction {
96 pub fn variants() -> MathFunctionIter {
98 return MathFunction::iter();
99 }
100}
101
102#[derive(Clone, Debug, MallocSizeOf, PartialEq, ToCss, ToShmem)]
104#[repr(u8)]
105pub enum Leaf {
106 Length(NoCalcLength),
108 Angle(NoCalcAngle),
110 Time(NoCalcTime),
112 Resolution(NoCalcResolution),
114 ColorComponent(ChannelKeyword),
116 Percentage(NoCalcPercentage),
118 Number(NoCalcNumber),
120 TreeCountingFunction(TreeCountingFunction),
122}
123
124impl ToTyped for Leaf {
125 fn to_typed(&self, dest: &mut ThinVec<TypedValue>) -> Result<(), ()> {
126 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 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 Err(()) => self.clone(),
172 }
173 },
174 None => self.clone(),
175 },
176 Self::Angle(..)
179 | Self::Time(..)
180 | Self::Resolution(..)
181 | Self::Percentage(..)
182 | Self::Number(..) => self.clone(),
183 }
184 }
185}
186
187#[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 pub fn with_clamping_mode(&self, clamping_mode: AllowedNumericType) -> Self {
204 Self {
205 clamping_mode,
206 node: self.node.clone(),
207 }
208 }
209
210 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 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 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 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 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 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 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#[derive(Clone, Debug, MallocSizeOf, PartialEq, ToCss, ToShmem, ToTyped)]
273pub struct CalcLengthPercentage(pub CalcNumeric);
274
275impl SpecifiedValueInfo for CalcLengthPercentage {}
276
277#[derive(Clone, Copy, PartialEq)]
279pub enum AllowAnchorPositioningFunctions {
280 No,
282 AllowAnchorSize,
284 AllowAnchorAndAnchorSize,
286}
287
288bitflags! {
289 #[derive(Clone, Copy, PartialEq, Eq)]
292 struct AdditionalFunctions: u8 {
293 const ANCHOR = 1 << 0;
295 const ANCHOR_SIZE = 1 << 1;
297 }
298}
299
300#[derive(Clone, Copy)]
302pub struct CalcParseFlags {
303 units: CalcUnits,
305 pub color_components: ChannelKeyword,
307 additional_functions: AdditionalFunctions,
309 in_place_operations: CalcNodeParseInPlaceOperations,
312}
313
314impl CalcParseFlags {
315 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 fn new_including(mut self, units: CalcUnits) -> Self {
327 self.units |= units;
328 self
329 }
330
331 pub fn new_without_in_place_operations(mut self) -> Self {
333 self.in_place_operations = CalcNodeParseInPlaceOperations::No;
334 self
335 }
336
337 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 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 return Err(());
552 },
553 (&mut TreeCountingFunction(_), &TreeCountingFunction(_)) => {
554 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 *left = NoCalcNumber::new(left.value() * right.value());
582 true
583 } else {
584 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 let right_val = right.value();
598 self.map(|v| v * right_val).is_ok()
599 } else {
600 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
807pub type CalcAnchorFunction = generic::GenericCalcAnchorFunction<Leaf>;
809pub type CalcAnchorSizeFunction = generic::GenericCalcAnchorSizeFunction<Leaf>;
811
812#[derive(Clone, Copy, PartialEq, Eq)]
814pub enum CalcNodeParseInPlaceOperations {
815 No,
817 Yes,
819}
820
821pub type CalcNode = generic::GenericCalcNode<Leaf>;
823impl CalcNode {
824 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 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 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 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 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 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 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 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 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 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; }
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 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 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 Merged,
1265 Unchanged,
1268 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 return if resolved.unit().is_empty() {
1294 InPlaceDivisionResult::Unchanged
1295 } else {
1296 InPlaceDivisionResult::Invalid
1297 };
1298 }
1299 }
1300 InPlaceDivisionResult::Unchanged
1301 }
1302
1303 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 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 pub fn into_length_or_percentage(
1351 mut self,
1352 clamping_mode: AllowedNumericType,
1353 ) -> Result<CalcLengthPercentage, ()> {
1354 self.simplify_and_sort();
1355
1356 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 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 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 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 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 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 #[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 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 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 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 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 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 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 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}