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