1use crate::color::parsing::ChannelKeyword;
10use crate::derives::*;
11use crate::parser::{Parse, ParserContext};
12use crate::values::generics::calc::{
13 self as generic, CalcNodeLeaf, CalcUnits, MinMaxOp, ModRemOp, PositivePercentageBasis,
14 RoundingStrategy, SortKey,
15};
16use crate::values::generics::length::GenericAnchorSizeFunction;
17use crate::values::generics::position::{
18 AnchorSideKeyword, GenericAnchorFunction, GenericAnchorSide, TreeScoped,
19};
20use crate::values::specified::length::{AbsoluteLength, FontRelativeLength, NoCalcLength};
21use crate::values::specified::length::{ContainerRelativeLength, ViewportPercentageLength};
22use crate::values::specified::{self, Angle, Resolution, Time};
23use crate::values::{
24 reify_percentage, serialize_number, serialize_percentage, CSSFloat, DashedIdent,
25};
26use cssparser::{match_ignore_ascii_case, CowRcStr, Parser, Token};
27use debug_unreachable::debug_unreachable;
28use smallvec::SmallVec;
29use std::cmp;
30use std::fmt::{self, Write};
31use style_traits::values::specified::AllowedNumericType;
32use style_traits::{
33 CssWriter, ParseError, SpecifiedValueInfo, StyleParseErrorKind, ToCss, ToTyped, TypedValue,
34};
35
36#[derive(Clone, Copy, Debug, Parse)]
38pub enum MathFunction {
39 Calc,
41 Min,
43 Max,
45 Clamp,
47 Round,
49 Mod,
51 Rem,
53 Sin,
55 Cos,
57 Tan,
59 Asin,
61 Acos,
63 Atan,
65 Atan2,
67 Pow,
69 Sqrt,
71 Hypot,
73 Log,
75 Exp,
77 Abs,
79 Sign,
81}
82
83#[derive(Clone, Debug, MallocSizeOf, PartialEq, ToShmem)]
85#[repr(u8)]
86pub enum Leaf {
87 Length(NoCalcLength),
89 Angle(Angle),
91 Time(Time),
93 Resolution(Resolution),
95 ColorComponent(ChannelKeyword),
97 Percentage(CSSFloat),
99 Number(CSSFloat),
101}
102
103impl Leaf {
104 fn as_length(&self) -> Option<&NoCalcLength> {
105 match *self {
106 Self::Length(ref l) => Some(l),
107 _ => None,
108 }
109 }
110}
111
112impl ToCss for Leaf {
113 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
114 where
115 W: Write,
116 {
117 match *self {
118 Self::Length(ref l) => l.to_css(dest),
119 Self::Number(n) => serialize_number(n, false, dest),
120 Self::Resolution(ref r) => r.to_css(dest),
121 Self::Percentage(p) => serialize_percentage(p, dest),
122 Self::Angle(ref a) => a.to_css(dest),
123 Self::Time(ref t) => t.to_css(dest),
124 Self::ColorComponent(ref s) => s.to_css(dest),
125 }
126 }
127}
128
129impl ToTyped for Leaf {
130 fn to_typed(&self) -> Option<TypedValue> {
131 match *self {
133 Self::Length(ref l) => l.to_typed(),
134 Self::Percentage(p) => Some(TypedValue::Numeric(reify_percentage(p))),
135 _ => None,
136 }
137 }
138}
139
140#[derive(Clone, Debug, MallocSizeOf, PartialEq, ToCss, ToShmem, ToTyped)]
147#[allow(missing_docs)]
148#[typed_value(derive_fields)]
149pub struct CalcLengthPercentage {
150 #[css(skip)]
151 pub clamping_mode: AllowedNumericType,
152 pub node: CalcNode,
153}
154
155impl CalcLengthPercentage {
156 fn same_unit_length_as(a: &Self, b: &Self) -> Option<(CSSFloat, CSSFloat)> {
157 debug_assert_eq!(a.clamping_mode, b.clamping_mode);
158 debug_assert_eq!(a.clamping_mode, AllowedNumericType::All);
159
160 let a = a.node.as_leaf()?;
161 let b = b.node.as_leaf()?;
162
163 if a.sort_key() != b.sort_key() {
164 return None;
165 }
166
167 let a = a.as_length()?.unitless_value();
168 let b = b.as_length()?.unitless_value();
169 return Some((a, b));
170 }
171}
172
173impl SpecifiedValueInfo for CalcLengthPercentage {}
174
175#[derive(Clone, Copy, PartialEq)]
177pub enum AllowAnchorPositioningFunctions {
178 No,
180 AllowAnchorSize,
182 AllowAnchorAndAnchorSize,
184}
185
186bitflags! {
187 #[derive(Clone, Copy, PartialEq, Eq)]
190 struct AdditionalFunctions: u8 {
191 const ANCHOR = 1 << 0;
193 const ANCHOR_SIZE = 1 << 1;
195 }
196}
197
198#[derive(Clone, Copy)]
200pub struct AllowParse {
201 units: CalcUnits,
203 additional_functions: AdditionalFunctions,
205}
206
207impl AllowParse {
208 pub fn new(units: CalcUnits) -> Self {
210 Self {
211 units,
212 additional_functions: AdditionalFunctions::empty(),
213 }
214 }
215
216 fn new_including(mut self, units: CalcUnits) -> Self {
218 self.units |= units;
219 self
220 }
221
222 fn includes(&self, unit: CalcUnits) -> bool {
224 self.units.intersects(unit)
225 }
226}
227
228impl generic::CalcNodeLeaf for Leaf {
229 fn unit(&self) -> CalcUnits {
230 match self {
231 Leaf::Length(_) => CalcUnits::LENGTH,
232 Leaf::Angle(_) => CalcUnits::ANGLE,
233 Leaf::Time(_) => CalcUnits::TIME,
234 Leaf::Resolution(_) => CalcUnits::RESOLUTION,
235 Leaf::ColorComponent(_) => CalcUnits::COLOR_COMPONENT,
236 Leaf::Percentage(_) => CalcUnits::PERCENTAGE,
237 Leaf::Number(_) => CalcUnits::empty(),
238 }
239 }
240
241 fn unitless_value(&self) -> Option<f32> {
242 Some(match *self {
243 Self::Length(ref l) => l.unitless_value(),
244 Self::Percentage(n) | Self::Number(n) => n,
245 Self::Resolution(ref r) => r.dppx(),
246 Self::Angle(ref a) => a.degrees(),
247 Self::Time(ref t) => t.seconds(),
248 Self::ColorComponent(_) => return None,
249 })
250 }
251
252 fn new_number(value: f32) -> Self {
253 Self::Number(value)
254 }
255
256 fn compare(&self, other: &Self, basis: PositivePercentageBasis) -> Option<cmp::Ordering> {
257 use self::Leaf::*;
258
259 if std::mem::discriminant(self) != std::mem::discriminant(other) {
260 return None;
261 }
262
263 if matches!(self, Percentage(..)) && matches!(basis, PositivePercentageBasis::Unknown) {
264 return None;
265 }
266
267 let self_negative = self.is_negative().unwrap_or(false);
268 if self_negative != other.is_negative().unwrap_or(false) {
269 return Some(if self_negative {
270 cmp::Ordering::Less
271 } else {
272 cmp::Ordering::Greater
273 });
274 }
275
276 match (self, other) {
277 (&Percentage(ref one), &Percentage(ref other)) => one.partial_cmp(other),
278 (&Length(ref one), &Length(ref other)) => one.partial_cmp(other),
279 (&Angle(ref one), &Angle(ref other)) => one.degrees().partial_cmp(&other.degrees()),
280 (&Time(ref one), &Time(ref other)) => one.seconds().partial_cmp(&other.seconds()),
281 (&Resolution(ref one), &Resolution(ref other)) => one.dppx().partial_cmp(&other.dppx()),
282 (&Number(ref one), &Number(ref other)) => one.partial_cmp(other),
283 (&ColorComponent(ref one), &ColorComponent(ref other)) => one.partial_cmp(other),
284 _ => {
285 match *self {
286 Length(..) | Percentage(..) | Angle(..) | Time(..) | Number(..)
287 | Resolution(..) | ColorComponent(..) => {},
288 }
289 unsafe {
290 debug_unreachable!("Forgot a branch?");
291 }
292 },
293 }
294 }
295
296 fn as_number(&self) -> Option<f32> {
297 match *self {
298 Leaf::Length(_)
299 | Leaf::Angle(_)
300 | Leaf::Time(_)
301 | Leaf::Resolution(_)
302 | Leaf::Percentage(_)
303 | Leaf::ColorComponent(_) => None,
304 Leaf::Number(value) => Some(value),
305 }
306 }
307
308 fn sort_key(&self) -> SortKey {
309 match *self {
310 Self::Number(..) => SortKey::Number,
311 Self::Percentage(..) => SortKey::Percentage,
312 Self::Time(..) => SortKey::S,
313 Self::Resolution(..) => SortKey::Dppx,
314 Self::Angle(..) => SortKey::Deg,
315 Self::Length(ref l) => match *l {
316 NoCalcLength::Absolute(..) => SortKey::Px,
317 NoCalcLength::FontRelative(ref relative) => match *relative {
318 FontRelativeLength::Em(..) => SortKey::Em,
319 FontRelativeLength::Ex(..) => SortKey::Ex,
320 FontRelativeLength::Rex(..) => SortKey::Rex,
321 FontRelativeLength::Ch(..) => SortKey::Ch,
322 FontRelativeLength::Rch(..) => SortKey::Rch,
323 FontRelativeLength::Cap(..) => SortKey::Cap,
324 FontRelativeLength::Rcap(..) => SortKey::Rcap,
325 FontRelativeLength::Ic(..) => SortKey::Ic,
326 FontRelativeLength::Ric(..) => SortKey::Ric,
327 FontRelativeLength::Rem(..) => SortKey::Rem,
328 FontRelativeLength::Lh(..) => SortKey::Lh,
329 FontRelativeLength::Rlh(..) => SortKey::Rlh,
330 },
331 NoCalcLength::ViewportPercentage(ref vp) => match *vp {
332 ViewportPercentageLength::Vh(..) => SortKey::Vh,
333 ViewportPercentageLength::Svh(..) => SortKey::Svh,
334 ViewportPercentageLength::Lvh(..) => SortKey::Lvh,
335 ViewportPercentageLength::Dvh(..) => SortKey::Dvh,
336 ViewportPercentageLength::Vw(..) => SortKey::Vw,
337 ViewportPercentageLength::Svw(..) => SortKey::Svw,
338 ViewportPercentageLength::Lvw(..) => SortKey::Lvw,
339 ViewportPercentageLength::Dvw(..) => SortKey::Dvw,
340 ViewportPercentageLength::Vmax(..) => SortKey::Vmax,
341 ViewportPercentageLength::Svmax(..) => SortKey::Svmax,
342 ViewportPercentageLength::Lvmax(..) => SortKey::Lvmax,
343 ViewportPercentageLength::Dvmax(..) => SortKey::Dvmax,
344 ViewportPercentageLength::Vmin(..) => SortKey::Vmin,
345 ViewportPercentageLength::Svmin(..) => SortKey::Svmin,
346 ViewportPercentageLength::Lvmin(..) => SortKey::Lvmin,
347 ViewportPercentageLength::Dvmin(..) => SortKey::Dvmin,
348 ViewportPercentageLength::Vb(..) => SortKey::Vb,
349 ViewportPercentageLength::Svb(..) => SortKey::Svb,
350 ViewportPercentageLength::Lvb(..) => SortKey::Lvb,
351 ViewportPercentageLength::Dvb(..) => SortKey::Dvb,
352 ViewportPercentageLength::Vi(..) => SortKey::Vi,
353 ViewportPercentageLength::Svi(..) => SortKey::Svi,
354 ViewportPercentageLength::Lvi(..) => SortKey::Lvi,
355 ViewportPercentageLength::Dvi(..) => SortKey::Dvi,
356 },
357 NoCalcLength::ContainerRelative(ref cq) => match *cq {
358 ContainerRelativeLength::Cqw(..) => SortKey::Cqw,
359 ContainerRelativeLength::Cqh(..) => SortKey::Cqh,
360 ContainerRelativeLength::Cqi(..) => SortKey::Cqi,
361 ContainerRelativeLength::Cqb(..) => SortKey::Cqb,
362 ContainerRelativeLength::Cqmin(..) => SortKey::Cqmin,
363 ContainerRelativeLength::Cqmax(..) => SortKey::Cqmax,
364 },
365 NoCalcLength::ServoCharacterWidth(..) => unreachable!(),
366 },
367 Self::ColorComponent(..) => SortKey::ColorComponent,
368 }
369 }
370
371 fn simplify(&mut self) {
372 if let Self::Length(NoCalcLength::Absolute(ref mut abs)) = *self {
373 *abs = AbsoluteLength::Px(abs.to_px());
374 }
375 }
376
377 fn try_sum_in_place(&mut self, other: &Self) -> Result<(), ()> {
382 use self::Leaf::*;
383
384 if std::mem::discriminant(self) != std::mem::discriminant(other) {
385 return Err(());
386 }
387
388 match (self, other) {
389 (&mut Number(ref mut one), &Number(ref other))
390 | (&mut Percentage(ref mut one), &Percentage(ref other)) => {
391 *one += *other;
392 },
393 (&mut Angle(ref mut one), &Angle(ref other)) => {
394 *one = specified::Angle::from_calc(one.degrees() + other.degrees());
395 },
396 (&mut Time(ref mut one), &Time(ref other)) => {
397 *one = specified::Time::from_seconds(one.seconds() + other.seconds());
398 },
399 (&mut Resolution(ref mut one), &Resolution(ref other)) => {
400 *one = specified::Resolution::from_dppx(one.dppx() + other.dppx());
401 },
402 (&mut Length(ref mut one), &Length(ref other)) => {
403 *one = one.try_op(other, std::ops::Add::add)?;
404 },
405 (&mut ColorComponent(_), &ColorComponent(_)) => {
406 return Err(());
408 },
409 _ => {
410 match *other {
411 Number(..) | Percentage(..) | Angle(..) | Time(..) | Resolution(..)
412 | Length(..) | ColorComponent(..) => {},
413 }
414 unsafe {
415 debug_unreachable!();
416 }
417 },
418 }
419
420 Ok(())
421 }
422
423 fn try_product_in_place(&mut self, other: &mut Self) -> bool {
424 if let Self::Number(ref mut left) = *self {
425 if let Self::Number(ref right) = *other {
426 *left *= *right;
428 true
429 } else {
430 if other.map(|v| v * *left).is_ok() {
433 std::mem::swap(self, other);
434 true
435 } else {
436 false
437 }
438 }
439 } else if let Self::Number(ref right) = *other {
440 self.map(|v| v * *right).is_ok()
443 } else {
444 false
446 }
447 }
448
449 fn try_op<O>(&self, other: &Self, op: O) -> Result<Self, ()>
450 where
451 O: Fn(f32, f32) -> f32,
452 {
453 use self::Leaf::*;
454
455 if std::mem::discriminant(self) != std::mem::discriminant(other) {
456 return Err(());
457 }
458
459 match (self, other) {
460 (&Number(one), &Number(other)) => {
461 return Ok(Leaf::Number(op(one, other)));
462 },
463 (&Percentage(one), &Percentage(other)) => {
464 return Ok(Leaf::Percentage(op(one, other)));
465 },
466 (&Angle(ref one), &Angle(ref other)) => {
467 return Ok(Leaf::Angle(specified::Angle::from_calc(op(
468 one.degrees(),
469 other.degrees(),
470 ))));
471 },
472 (&Resolution(ref one), &Resolution(ref other)) => {
473 return Ok(Leaf::Resolution(specified::Resolution::from_dppx(op(
474 one.dppx(),
475 other.dppx(),
476 ))));
477 },
478 (&Time(ref one), &Time(ref other)) => {
479 return Ok(Leaf::Time(specified::Time::from_seconds(op(
480 one.seconds(),
481 other.seconds(),
482 ))));
483 },
484 (&Length(ref one), &Length(ref other)) => {
485 return Ok(Leaf::Length(one.try_op(other, op)?));
486 },
487 _ => {
488 match *other {
489 Number(..) | Percentage(..) | Angle(..) | Time(..) | Length(..)
490 | Resolution(..) | ColorComponent(..) => {},
491 }
492 unsafe {
493 debug_unreachable!();
494 }
495 },
496 }
497 }
498
499 fn map(&mut self, mut op: impl FnMut(f32) -> f32) -> Result<(), ()> {
500 Ok(match self {
501 Leaf::Length(one) => *one = one.map(op),
502 Leaf::Angle(one) => *one = specified::Angle::from_calc(op(one.degrees())),
503 Leaf::Time(one) => *one = specified::Time::from_seconds(op(one.seconds())),
504 Leaf::Resolution(one) => *one = specified::Resolution::from_dppx(op(one.dppx())),
505 Leaf::Percentage(one) => *one = op(*one),
506 Leaf::Number(one) => *one = op(*one),
507 Leaf::ColorComponent(..) => return Err(()),
508 })
509 }
510}
511
512impl GenericAnchorSide<Box<CalcNode>> {
513 fn parse_in_calc<'i, 't>(
514 context: &ParserContext,
515 input: &mut Parser<'i, 't>,
516 ) -> Result<Self, ParseError<'i>> {
517 if let Ok(k) = input.try_parse(|i| AnchorSideKeyword::parse(i)) {
518 return Ok(Self::Keyword(k));
519 }
520 Ok(Self::Percentage(Box::new(CalcNode::parse_argument(
521 context,
522 input,
523 AllowParse::new(CalcUnits::PERCENTAGE),
524 )?)))
525 }
526}
527
528impl GenericAnchorFunction<Box<CalcNode>, Box<CalcNode>> {
529 fn parse_in_calc<'i, 't>(
530 context: &ParserContext,
531 additional_functions: AdditionalFunctions,
532 input: &mut Parser<'i, 't>,
533 ) -> Result<Self, ParseError<'i>> {
534 if !static_prefs::pref!("layout.css.anchor-positioning.enabled") {
535 return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));
536 }
537 input.parse_nested_block(|i| {
538 let target_element = i.try_parse(|i| DashedIdent::parse(context, i)).ok();
539 let side = GenericAnchorSide::parse_in_calc(context, i)?;
540 let target_element = if target_element.is_none() {
541 i.try_parse(|i| DashedIdent::parse(context, i)).ok()
542 } else {
543 target_element
544 };
545 let fallback = i
546 .try_parse(|i| {
547 i.expect_comma()?;
548 Ok::<Box<CalcNode>, ParseError<'i>>(Box::new(
549 CalcNode::parse_argument(
550 context,
551 i,
552 AllowParse {
553 units: CalcUnits::LENGTH_PERCENTAGE,
554 additional_functions,
555 },
556 )?
557 .into_length_or_percentage(AllowedNumericType::All)
558 .map_err(|_| i.new_custom_error(StyleParseErrorKind::UnspecifiedError))?
559 .node,
560 ))
561 })
562 .ok();
563 Ok(Self {
564 target_element: TreeScoped::with_default_level(
565 target_element.unwrap_or_else(DashedIdent::empty),
566 ),
567 side,
568 fallback: fallback.into(),
569 })
570 })
571 }
572}
573
574impl GenericAnchorSizeFunction<Box<CalcNode>> {
575 fn parse_in_calc<'i, 't>(
576 context: &ParserContext,
577 input: &mut Parser<'i, 't>,
578 ) -> Result<Self, ParseError<'i>> {
579 if !static_prefs::pref!("layout.css.anchor-positioning.enabled") {
580 return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));
581 }
582 GenericAnchorSizeFunction::parse_inner(context, input, |i| {
583 Ok(Box::new(
584 CalcNode::parse_argument(
585 context,
586 i,
587 AllowParse::new(CalcUnits::LENGTH_PERCENTAGE),
588 )?
589 .into_length_or_percentage(AllowedNumericType::All)
590 .map_err(|_| i.new_custom_error(StyleParseErrorKind::UnspecifiedError))?
591 .node,
592 ))
593 })
594 }
595}
596
597pub type CalcAnchorFunction = generic::GenericCalcAnchorFunction<Leaf>;
599pub type CalcAnchorSizeFunction = generic::GenericCalcAnchorSizeFunction<Leaf>;
601
602pub type CalcNode = generic::GenericCalcNode<Leaf>;
604impl CalcNode {
605 fn parse_one<'i, 't>(
611 context: &ParserContext,
612 input: &mut Parser<'i, 't>,
613 allowed: AllowParse,
614 ) -> Result<Self, ParseError<'i>> {
615 let location = input.current_source_location();
616 match input.next()? {
617 &Token::Number { value, .. } => Ok(CalcNode::Leaf(Leaf::Number(value))),
618 &Token::Dimension {
619 value, ref unit, ..
620 } => {
621 if allowed.includes(CalcUnits::LENGTH) {
622 if let Ok(l) = NoCalcLength::parse_dimension_with_context(context, value, unit)
623 {
624 return Ok(CalcNode::Leaf(Leaf::Length(l)));
625 }
626 }
627 if allowed.includes(CalcUnits::ANGLE) {
628 if let Ok(a) = Angle::parse_dimension(value, unit, true) {
629 return Ok(CalcNode::Leaf(Leaf::Angle(a)));
630 }
631 }
632 if allowed.includes(CalcUnits::TIME) {
633 if let Ok(t) = Time::parse_dimension(value, unit) {
634 return Ok(CalcNode::Leaf(Leaf::Time(t)));
635 }
636 }
637 if allowed.includes(CalcUnits::RESOLUTION) {
638 if let Ok(t) = Resolution::parse_dimension(value, unit) {
639 return Ok(CalcNode::Leaf(Leaf::Resolution(t)));
640 }
641 }
642 return Err(location.new_custom_error(StyleParseErrorKind::UnspecifiedError));
643 },
644 &Token::Percentage { unit_value, .. } if allowed.includes(CalcUnits::PERCENTAGE) => {
645 Ok(CalcNode::Leaf(Leaf::Percentage(unit_value)))
646 },
647 &Token::ParenthesisBlock => {
648 input.parse_nested_block(|input| CalcNode::parse_argument(context, input, allowed))
649 },
650 &Token::Function(ref name)
651 if allowed
652 .additional_functions
653 .intersects(AdditionalFunctions::ANCHOR)
654 && name.eq_ignore_ascii_case("anchor") =>
655 {
656 let anchor_function = GenericAnchorFunction::parse_in_calc(
657 context,
658 allowed.additional_functions,
659 input,
660 )?;
661 Ok(CalcNode::Anchor(Box::new(anchor_function)))
662 },
663 &Token::Function(ref name)
664 if allowed
665 .additional_functions
666 .intersects(AdditionalFunctions::ANCHOR_SIZE)
667 && name.eq_ignore_ascii_case("anchor-size") =>
668 {
669 let anchor_size_function =
670 GenericAnchorSizeFunction::parse_in_calc(context, input)?;
671 Ok(CalcNode::AnchorSize(Box::new(anchor_size_function)))
672 },
673 &Token::Function(ref name) => {
674 let function = CalcNode::math_function(context, name, location)?;
675 CalcNode::parse(context, input, function, allowed)
676 },
677 &Token::Ident(ref ident) => {
678 let leaf = match_ignore_ascii_case! { &**ident,
679 "e" => Leaf::Number(std::f32::consts::E),
680 "pi" => Leaf::Number(std::f32::consts::PI),
681 "infinity" => Leaf::Number(f32::INFINITY),
682 "-infinity" => Leaf::Number(f32::NEG_INFINITY),
683 "nan" => Leaf::Number(f32::NAN),
684 _ => {
685 if crate::color::parsing::rcs_enabled() &&
686 allowed.includes(CalcUnits::COLOR_COMPONENT)
687 {
688 if let Ok(channel_keyword) = ChannelKeyword::from_ident(&ident) {
689 Leaf::ColorComponent(channel_keyword)
690 } else {
691 return Err(location
692 .new_unexpected_token_error(Token::Ident(ident.clone())));
693 }
694 } else {
695 return Err(
696 location.new_unexpected_token_error(Token::Ident(ident.clone()))
697 );
698 }
699 },
700 };
701 Ok(CalcNode::Leaf(leaf))
702 },
703 t => Err(location.new_unexpected_token_error(t.clone())),
704 }
705 }
706
707 pub fn parse<'i, 't>(
711 context: &ParserContext,
712 input: &mut Parser<'i, 't>,
713 function: MathFunction,
714 allowed: AllowParse,
715 ) -> Result<Self, ParseError<'i>> {
716 input.parse_nested_block(|input| {
717 match function {
718 MathFunction::Calc => Self::parse_argument(context, input, allowed),
719 MathFunction::Clamp => {
720 let min_val = if input
721 .try_parse(|min| min.expect_ident_matching("none"))
722 .ok()
723 .is_none()
724 {
725 Some(Self::parse_argument(context, input, allowed)?)
726 } else {
727 None
728 };
729
730 input.expect_comma()?;
731 let center = Self::parse_argument(context, input, allowed)?;
732 input.expect_comma()?;
733
734 let max_val = if input
735 .try_parse(|max| max.expect_ident_matching("none"))
736 .ok()
737 .is_none()
738 {
739 Some(Self::parse_argument(context, input, allowed)?)
740 } else {
741 None
742 };
743
744 Ok(match (min_val, max_val) {
751 (None, None) => center,
752 (None, Some(max)) => Self::MinMax(vec![center, max].into(), MinMaxOp::Min),
753 (Some(min), None) => Self::MinMax(vec![min, center].into(), MinMaxOp::Max),
754 (Some(min), Some(max)) => Self::Clamp {
755 min: Box::new(min),
756 center: Box::new(center),
757 max: Box::new(max),
758 },
759 })
760 },
761 MathFunction::Round => {
762 let strategy = input.try_parse(parse_rounding_strategy);
763
764 fn parse_rounding_strategy<'i, 't>(
767 input: &mut Parser<'i, 't>,
768 ) -> Result<RoundingStrategy, ParseError<'i>> {
769 Ok(try_match_ident_ignore_ascii_case! { input,
770 "nearest" => RoundingStrategy::Nearest,
771 "up" => RoundingStrategy::Up,
772 "down" => RoundingStrategy::Down,
773 "to-zero" => RoundingStrategy::ToZero,
774 })
775 }
776
777 if strategy.is_ok() {
778 input.expect_comma()?;
779 }
780
781 let value = Self::parse_argument(context, input, allowed)?;
782
783 let step = input.try_parse(|input| {
786 input.expect_comma()?;
787 Self::parse_argument(context, input, allowed)
788 });
789
790 let step = step.unwrap_or(Self::Leaf(Leaf::Number(1.0)));
791
792 Ok(Self::Round {
793 strategy: strategy.unwrap_or(RoundingStrategy::Nearest),
794 value: Box::new(value),
795 step: Box::new(step),
796 })
797 },
798 MathFunction::Mod | MathFunction::Rem => {
799 let dividend = Self::parse_argument(context, input, allowed)?;
800 input.expect_comma()?;
801 let divisor = Self::parse_argument(context, input, allowed)?;
802
803 let op = match function {
804 MathFunction::Mod => ModRemOp::Mod,
805 MathFunction::Rem => ModRemOp::Rem,
806 _ => unreachable!(),
807 };
808 Ok(Self::ModRem {
809 dividend: Box::new(dividend),
810 divisor: Box::new(divisor),
811 op,
812 })
813 },
814 MathFunction::Min | MathFunction::Max => {
815 let arguments = input.parse_comma_separated(|input| {
821 let result = Self::parse_argument(context, input, allowed)?;
822 Ok(result)
823 })?;
824
825 let op = match function {
826 MathFunction::Min => MinMaxOp::Min,
827 MathFunction::Max => MinMaxOp::Max,
828 _ => unreachable!(),
829 };
830
831 Ok(Self::MinMax(arguments.into(), op))
832 },
833 MathFunction::Sin | MathFunction::Cos | MathFunction::Tan => {
834 let a = Self::parse_angle_argument(context, input)?;
835
836 let number = match function {
837 MathFunction::Sin => a.sin(),
838 MathFunction::Cos => a.cos(),
839 MathFunction::Tan => a.tan(),
840 _ => unsafe {
841 debug_unreachable!("We just checked!");
842 },
843 };
844
845 Ok(Self::Leaf(Leaf::Number(number)))
846 },
847 MathFunction::Asin | MathFunction::Acos | MathFunction::Atan => {
848 let a = Self::parse_number_argument(context, input)?;
849
850 let radians = match function {
851 MathFunction::Asin => a.asin(),
852 MathFunction::Acos => a.acos(),
853 MathFunction::Atan => a.atan(),
854 _ => unsafe {
855 debug_unreachable!("We just checked!");
856 },
857 };
858
859 Ok(Self::Leaf(Leaf::Angle(Angle::from_radians(radians))))
860 },
861 MathFunction::Atan2 => {
862 let allow_all = allowed.new_including(CalcUnits::ALL);
863 let a = Self::parse_argument(context, input, allow_all)?;
864 input.expect_comma()?;
865 let b = Self::parse_argument(context, input, allow_all)?;
866
867 let radians = Self::try_resolve(input, || {
868 if let Ok(a) = a.to_number() {
869 let b = b.to_number()?;
870 return Ok(a.atan2(b));
871 }
872
873 if let Ok(a) = a.to_percentage() {
874 let b = b.to_percentage()?;
875 return Ok(a.atan2(b));
876 }
877
878 if let Ok(a) = a.to_time(None) {
879 let b = b.to_time(None)?;
880 return Ok(a.seconds().atan2(b.seconds()));
881 }
882
883 if let Ok(a) = a.to_angle() {
884 let b = b.to_angle()?;
885 return Ok(a.radians().atan2(b.radians()));
886 }
887
888 if let Ok(a) = a.to_resolution() {
889 let b = b.to_resolution()?;
890 return Ok(a.dppx().atan2(b.dppx()));
891 }
892
893 let a = a.into_length_or_percentage(AllowedNumericType::All)?;
894 let b = b.into_length_or_percentage(AllowedNumericType::All)?;
895 let (a, b) = CalcLengthPercentage::same_unit_length_as(&a, &b).ok_or(())?;
896
897 Ok(a.atan2(b))
898 })?;
899
900 Ok(Self::Leaf(Leaf::Angle(Angle::from_radians(radians))))
901 },
902 MathFunction::Pow => {
903 let a = Self::parse_number_argument(context, input)?;
904 input.expect_comma()?;
905 let b = Self::parse_number_argument(context, input)?;
906
907 let number = a.powf(b);
908
909 Ok(Self::Leaf(Leaf::Number(number)))
910 },
911 MathFunction::Sqrt => {
912 let a = Self::parse_number_argument(context, input)?;
913
914 let number = a.sqrt();
915
916 Ok(Self::Leaf(Leaf::Number(number)))
917 },
918 MathFunction::Hypot => {
919 let arguments = input.parse_comma_separated(|input| {
920 let result = Self::parse_argument(context, input, allowed)?;
921 Ok(result)
922 })?;
923
924 Ok(Self::Hypot(arguments.into()))
925 },
926 MathFunction::Log => {
927 let a = Self::parse_number_argument(context, input)?;
928 let b = input
929 .try_parse(|input| {
930 input.expect_comma()?;
931 Self::parse_number_argument(context, input)
932 })
933 .ok();
934
935 let number = match b {
936 Some(b) => a.log(b),
937 None => a.ln(),
938 };
939
940 Ok(Self::Leaf(Leaf::Number(number)))
941 },
942 MathFunction::Exp => {
943 let a = Self::parse_number_argument(context, input)?;
944 let number = a.exp();
945 Ok(Self::Leaf(Leaf::Number(number)))
946 },
947 MathFunction::Abs => {
948 let node = Self::parse_argument(context, input, allowed)?;
949 Ok(Self::Abs(Box::new(node)))
950 },
951 MathFunction::Sign => {
952 let node = Self::parse_argument(
956 context,
957 input,
958 allowed.new_including(CalcUnits::ALL - CalcUnits::PERCENTAGE),
959 )?;
960 Ok(Self::Sign(Box::new(node)))
961 },
962 }
963 })
964 }
965
966 fn parse_angle_argument<'i, 't>(
967 context: &ParserContext,
968 input: &mut Parser<'i, 't>,
969 ) -> Result<CSSFloat, ParseError<'i>> {
970 let argument = Self::parse_argument(context, input, AllowParse::new(CalcUnits::ANGLE))?;
971 argument
972 .to_number()
973 .or_else(|()| Ok(argument.to_angle()?.radians()))
974 .map_err(|()| input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
975 }
976
977 fn parse_number_argument<'i, 't>(
978 context: &ParserContext,
979 input: &mut Parser<'i, 't>,
980 ) -> Result<CSSFloat, ParseError<'i>> {
981 Self::parse_argument(context, input, AllowParse::new(CalcUnits::empty()))?
982 .to_number()
983 .map_err(|()| input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
984 }
985
986 fn parse_argument<'i, 't>(
987 context: &ParserContext,
988 input: &mut Parser<'i, 't>,
989 allowed: AllowParse,
990 ) -> Result<Self, ParseError<'i>> {
991 let mut sum = SmallVec::<[CalcNode; 1]>::new();
992 let first = Self::parse_product(context, input, allowed)?;
993 sum.push(first);
994 loop {
995 let start = input.state();
996 match input.next_including_whitespace() {
997 Ok(&Token::WhiteSpace(_)) => {
998 if input.is_exhausted() {
999 break; }
1001 match *input.next()? {
1002 Token::Delim('+') => {
1003 let rhs = Self::parse_product(context, input, allowed)?;
1004 if sum.last_mut().unwrap().try_sum_in_place(&rhs).is_err() {
1005 sum.push(rhs);
1006 }
1007 },
1008 Token::Delim('-') => {
1009 let mut rhs = Self::parse_product(context, input, allowed)?;
1010 rhs.negate();
1011 if sum.last_mut().unwrap().try_sum_in_place(&rhs).is_err() {
1012 sum.push(rhs);
1013 }
1014 },
1015 _ => {
1016 input.reset(&start);
1017 break;
1018 },
1019 }
1020 },
1021 _ => {
1022 input.reset(&start);
1023 break;
1024 },
1025 }
1026 }
1027
1028 Ok(if sum.len() == 1 {
1029 sum.drain(..).next().unwrap()
1030 } else {
1031 Self::Sum(sum.into_boxed_slice().into())
1032 })
1033 }
1034
1035 fn parse_product<'i, 't>(
1045 context: &ParserContext,
1046 input: &mut Parser<'i, 't>,
1047 allowed: AllowParse,
1048 ) -> Result<Self, ParseError<'i>> {
1049 let mut product = SmallVec::<[CalcNode; 1]>::new();
1050 let first = Self::parse_one(context, input, allowed)?;
1051 product.push(first);
1052
1053 loop {
1054 let start = input.state();
1055 match input.next() {
1056 Ok(&Token::Delim('*')) => {
1057 let mut rhs = Self::parse_one(context, input, allowed)?;
1058
1059 if !product.last_mut().unwrap().try_product_in_place(&mut rhs) {
1062 product.push(rhs);
1063 }
1064 },
1065 Ok(&Token::Delim('/')) => {
1066 let rhs = Self::parse_one(context, input, allowed)?;
1067
1068 enum InPlaceDivisionResult {
1069 Merged,
1071 Unchanged,
1074 Invalid,
1077 }
1078
1079 fn try_division_in_place(
1080 left: &mut CalcNode,
1081 right: &CalcNode,
1082 ) -> InPlaceDivisionResult {
1083 if let Ok(resolved) = right.resolve() {
1084 if let Some(number) = resolved.as_number() {
1085 if number != 1.0 && left.is_product_distributive() {
1086 if left.map(|l| l / number).is_err() {
1087 return InPlaceDivisionResult::Invalid;
1088 }
1089 return InPlaceDivisionResult::Merged;
1090 }
1091 } else {
1092 return if resolved.unit().contains(CalcUnits::COLOR_COMPONENT) {
1095 InPlaceDivisionResult::Unchanged
1096 } else {
1097 InPlaceDivisionResult::Invalid
1098 };
1099 }
1100 }
1101 InPlaceDivisionResult::Unchanged
1102 }
1103
1104 match try_division_in_place(&mut product.last_mut().unwrap(), &rhs) {
1109 InPlaceDivisionResult::Merged => {},
1110 InPlaceDivisionResult::Unchanged => {
1111 product.push(Self::Invert(Box::new(rhs)))
1112 },
1113 InPlaceDivisionResult::Invalid => {
1114 return Err(
1115 input.new_custom_error(StyleParseErrorKind::UnspecifiedError)
1116 )
1117 },
1118 }
1119 },
1120 _ => {
1121 input.reset(&start);
1122 break;
1123 },
1124 }
1125 }
1126
1127 Ok(if product.len() == 1 {
1128 product.drain(..).next().unwrap()
1129 } else {
1130 Self::Product(product.into_boxed_slice().into())
1131 })
1132 }
1133
1134 fn try_resolve<'i, 't, F>(
1135 input: &Parser<'i, 't>,
1136 closure: F,
1137 ) -> Result<CSSFloat, ParseError<'i>>
1138 where
1139 F: FnOnce() -> Result<CSSFloat, ()>,
1140 {
1141 closure().map_err(|()| input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
1142 }
1143
1144 pub fn into_length_or_percentage(
1147 mut self,
1148 clamping_mode: AllowedNumericType,
1149 ) -> Result<CalcLengthPercentage, ()> {
1150 self.simplify_and_sort();
1151
1152 let unit = self.unit()?;
1155 if !CalcUnits::LENGTH_PERCENTAGE.intersects(unit) {
1156 Err(())
1157 } else {
1158 Ok(CalcLengthPercentage {
1159 clamping_mode,
1160 node: self,
1161 })
1162 }
1163 }
1164
1165 fn to_time(&self, clamping_mode: Option<AllowedNumericType>) -> Result<Time, ()> {
1167 let seconds = if let Leaf::Time(time) = self.resolve()? {
1168 time.seconds()
1169 } else {
1170 return Err(());
1171 };
1172
1173 Ok(Time::from_seconds_with_calc_clamping_mode(
1174 seconds,
1175 clamping_mode,
1176 ))
1177 }
1178
1179 fn to_resolution(&self) -> Result<Resolution, ()> {
1181 let dppx = if let Leaf::Resolution(resolution) = self.resolve()? {
1182 resolution.dppx()
1183 } else {
1184 return Err(());
1185 };
1186
1187 Ok(Resolution::from_dppx_calc(dppx))
1188 }
1189
1190 fn to_angle(&self) -> Result<Angle, ()> {
1192 let degrees = if let Leaf::Angle(angle) = self.resolve()? {
1193 angle.degrees()
1194 } else {
1195 return Err(());
1196 };
1197
1198 let result = Angle::from_calc(degrees);
1199 Ok(result)
1200 }
1201
1202 fn to_number(&self) -> Result<CSSFloat, ()> {
1204 let number = if let Leaf::Number(number) = self.resolve()? {
1205 number
1206 } else {
1207 return Err(());
1208 };
1209
1210 let result = number;
1211
1212 Ok(result)
1213 }
1214
1215 fn to_percentage(&self) -> Result<CSSFloat, ()> {
1217 if let Leaf::Percentage(percentage) = self.resolve()? {
1218 Ok(percentage)
1219 } else {
1220 Err(())
1221 }
1222 }
1223
1224 #[inline]
1227 pub fn math_function<'i>(
1228 _: &ParserContext,
1229 name: &CowRcStr<'i>,
1230 location: cssparser::SourceLocation,
1231 ) -> Result<MathFunction, ParseError<'i>> {
1232 let function = match MathFunction::from_ident(&*name) {
1233 Ok(f) => f,
1234 Err(()) => {
1235 return Err(location.new_unexpected_token_error(Token::Function(name.clone())))
1236 },
1237 };
1238
1239 Ok(function)
1240 }
1241
1242 pub fn parse_length_or_percentage<'i, 't>(
1244 context: &ParserContext,
1245 input: &mut Parser<'i, 't>,
1246 clamping_mode: AllowedNumericType,
1247 function: MathFunction,
1248 allow_anchor: AllowAnchorPositioningFunctions,
1249 ) -> Result<CalcLengthPercentage, ParseError<'i>> {
1250 let allowed = if allow_anchor == AllowAnchorPositioningFunctions::No {
1251 AllowParse::new(CalcUnits::LENGTH_PERCENTAGE)
1252 } else {
1253 AllowParse {
1254 units: CalcUnits::LENGTH_PERCENTAGE,
1255 additional_functions: match allow_anchor {
1256 AllowAnchorPositioningFunctions::No => unreachable!(),
1257 AllowAnchorPositioningFunctions::AllowAnchorSize => {
1258 AdditionalFunctions::ANCHOR_SIZE
1259 },
1260 AllowAnchorPositioningFunctions::AllowAnchorAndAnchorSize => {
1261 AdditionalFunctions::ANCHOR | AdditionalFunctions::ANCHOR_SIZE
1262 },
1263 },
1264 }
1265 };
1266 Self::parse(context, input, function, allowed)?
1267 .into_length_or_percentage(clamping_mode)
1268 .map_err(|()| input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
1269 }
1270
1271 pub fn parse_percentage<'i, 't>(
1273 context: &ParserContext,
1274 input: &mut Parser<'i, 't>,
1275 function: MathFunction,
1276 ) -> Result<CSSFloat, ParseError<'i>> {
1277 Self::parse(
1278 context,
1279 input,
1280 function,
1281 AllowParse::new(CalcUnits::PERCENTAGE),
1282 )?
1283 .to_percentage()
1284 .map(crate::values::normalize)
1285 .map_err(|()| input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
1286 }
1287
1288 pub fn parse_length<'i, 't>(
1290 context: &ParserContext,
1291 input: &mut Parser<'i, 't>,
1292 clamping_mode: AllowedNumericType,
1293 function: MathFunction,
1294 ) -> Result<CalcLengthPercentage, ParseError<'i>> {
1295 Self::parse(context, input, function, AllowParse::new(CalcUnits::LENGTH))?
1296 .into_length_or_percentage(clamping_mode)
1297 .map_err(|()| input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
1298 }
1299
1300 pub fn parse_number<'i, 't>(
1302 context: &ParserContext,
1303 input: &mut Parser<'i, 't>,
1304 function: MathFunction,
1305 ) -> Result<CSSFloat, ParseError<'i>> {
1306 Self::parse(
1307 context,
1308 input,
1309 function,
1310 AllowParse::new(CalcUnits::empty()),
1311 )?
1312 .to_number()
1313 .map_err(|()| input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
1314 }
1315
1316 pub fn parse_angle<'i, 't>(
1318 context: &ParserContext,
1319 input: &mut Parser<'i, 't>,
1320 function: MathFunction,
1321 ) -> Result<Angle, ParseError<'i>> {
1322 Self::parse(context, input, function, AllowParse::new(CalcUnits::ANGLE))?
1323 .to_angle()
1324 .map_err(|()| input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
1325 }
1326
1327 pub fn parse_time<'i, 't>(
1329 context: &ParserContext,
1330 input: &mut Parser<'i, 't>,
1331 clamping_mode: AllowedNumericType,
1332 function: MathFunction,
1333 ) -> Result<Time, ParseError<'i>> {
1334 Self::parse(context, input, function, AllowParse::new(CalcUnits::TIME))?
1335 .to_time(Some(clamping_mode))
1336 .map_err(|()| input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
1337 }
1338
1339 pub fn parse_resolution<'i, 't>(
1341 context: &ParserContext,
1342 input: &mut Parser<'i, 't>,
1343 function: MathFunction,
1344 ) -> Result<Resolution, ParseError<'i>> {
1345 Self::parse(
1346 context,
1347 input,
1348 function,
1349 AllowParse::new(CalcUnits::RESOLUTION),
1350 )?
1351 .to_resolution()
1352 .map_err(|()| input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
1353 }
1354}