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