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,
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::Sec,
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: target_element.unwrap_or_else(DashedIdent::empty),
562 side,
563 fallback: fallback.into(),
564 })
565 })
566 }
567}
568
569impl GenericAnchorSizeFunction<Box<CalcNode>> {
570 fn parse_in_calc<'i, 't>(
571 context: &ParserContext,
572 input: &mut Parser<'i, 't>,
573 ) -> Result<Self, ParseError<'i>> {
574 if !static_prefs::pref!("layout.css.anchor-positioning.enabled") {
575 return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));
576 }
577 GenericAnchorSizeFunction::parse_inner(context, input, |i| {
578 Ok(Box::new(
579 CalcNode::parse_argument(
580 context,
581 i,
582 AllowParse::new(CalcUnits::LENGTH_PERCENTAGE),
583 )?
584 .into_length_or_percentage(AllowedNumericType::All)
585 .map_err(|_| i.new_custom_error(StyleParseErrorKind::UnspecifiedError))?
586 .node,
587 ))
588 })
589 }
590}
591
592pub type CalcAnchorFunction = generic::GenericCalcAnchorFunction<Leaf>;
594pub type CalcAnchorSizeFunction = generic::GenericCalcAnchorSizeFunction<Leaf>;
596
597pub type CalcNode = generic::GenericCalcNode<Leaf>;
599impl CalcNode {
600 fn parse_one<'i, 't>(
606 context: &ParserContext,
607 input: &mut Parser<'i, 't>,
608 allowed: AllowParse,
609 ) -> Result<Self, ParseError<'i>> {
610 let location = input.current_source_location();
611 match input.next()? {
612 &Token::Number { value, .. } => Ok(CalcNode::Leaf(Leaf::Number(value))),
613 &Token::Dimension {
614 value, ref unit, ..
615 } => {
616 if allowed.includes(CalcUnits::LENGTH) {
617 if let Ok(l) = NoCalcLength::parse_dimension(context, value, unit) {
618 return Ok(CalcNode::Leaf(Leaf::Length(l)));
619 }
620 }
621 if allowed.includes(CalcUnits::ANGLE) {
622 if let Ok(a) = Angle::parse_dimension(value, unit, true) {
623 return Ok(CalcNode::Leaf(Leaf::Angle(a)));
624 }
625 }
626 if allowed.includes(CalcUnits::TIME) {
627 if let Ok(t) = Time::parse_dimension(value, unit) {
628 return Ok(CalcNode::Leaf(Leaf::Time(t)));
629 }
630 }
631 if allowed.includes(CalcUnits::RESOLUTION) {
632 if let Ok(t) = Resolution::parse_dimension(value, unit) {
633 return Ok(CalcNode::Leaf(Leaf::Resolution(t)));
634 }
635 }
636 return Err(location.new_custom_error(StyleParseErrorKind::UnspecifiedError));
637 },
638 &Token::Percentage { unit_value, .. } if allowed.includes(CalcUnits::PERCENTAGE) => {
639 Ok(CalcNode::Leaf(Leaf::Percentage(unit_value)))
640 },
641 &Token::ParenthesisBlock => {
642 input.parse_nested_block(|input| CalcNode::parse_argument(context, input, allowed))
643 },
644 &Token::Function(ref name)
645 if allowed
646 .additional_functions
647 .intersects(AdditionalFunctions::ANCHOR)
648 && name.eq_ignore_ascii_case("anchor") =>
649 {
650 let anchor_function = GenericAnchorFunction::parse_in_calc(
651 context,
652 allowed.additional_functions,
653 input,
654 )?;
655 Ok(CalcNode::Anchor(Box::new(anchor_function)))
656 },
657 &Token::Function(ref name)
658 if allowed
659 .additional_functions
660 .intersects(AdditionalFunctions::ANCHOR_SIZE)
661 && name.eq_ignore_ascii_case("anchor-size") =>
662 {
663 let anchor_size_function =
664 GenericAnchorSizeFunction::parse_in_calc(context, input)?;
665 Ok(CalcNode::AnchorSize(Box::new(anchor_size_function)))
666 },
667 &Token::Function(ref name) => {
668 let function = CalcNode::math_function(context, name, location)?;
669 CalcNode::parse(context, input, function, allowed)
670 },
671 &Token::Ident(ref ident) => {
672 let leaf = match_ignore_ascii_case! { &**ident,
673 "e" => Leaf::Number(std::f32::consts::E),
674 "pi" => Leaf::Number(std::f32::consts::PI),
675 "infinity" => Leaf::Number(f32::INFINITY),
676 "-infinity" => Leaf::Number(f32::NEG_INFINITY),
677 "nan" => Leaf::Number(f32::NAN),
678 _ => {
679 if crate::color::parsing::rcs_enabled() &&
680 allowed.includes(CalcUnits::COLOR_COMPONENT)
681 {
682 if let Ok(channel_keyword) = ChannelKeyword::from_ident(&ident) {
683 Leaf::ColorComponent(channel_keyword)
684 } else {
685 return Err(location
686 .new_unexpected_token_error(Token::Ident(ident.clone())));
687 }
688 } else {
689 return Err(
690 location.new_unexpected_token_error(Token::Ident(ident.clone()))
691 );
692 }
693 },
694 };
695 Ok(CalcNode::Leaf(leaf))
696 },
697 t => Err(location.new_unexpected_token_error(t.clone())),
698 }
699 }
700
701 pub fn parse<'i, 't>(
705 context: &ParserContext,
706 input: &mut Parser<'i, 't>,
707 function: MathFunction,
708 allowed: AllowParse,
709 ) -> Result<Self, ParseError<'i>> {
710 input.parse_nested_block(|input| {
711 match function {
712 MathFunction::Calc => Self::parse_argument(context, input, allowed),
713 MathFunction::Clamp => {
714 let min = Self::parse_argument(context, input, allowed)?;
715 input.expect_comma()?;
716 let center = Self::parse_argument(context, input, allowed)?;
717 input.expect_comma()?;
718 let max = Self::parse_argument(context, input, allowed)?;
719 Ok(Self::Clamp {
720 min: Box::new(min),
721 center: Box::new(center),
722 max: Box::new(max),
723 })
724 },
725 MathFunction::Round => {
726 let strategy = input.try_parse(parse_rounding_strategy);
727
728 fn parse_rounding_strategy<'i, 't>(
731 input: &mut Parser<'i, 't>,
732 ) -> Result<RoundingStrategy, ParseError<'i>> {
733 Ok(try_match_ident_ignore_ascii_case! { input,
734 "nearest" => RoundingStrategy::Nearest,
735 "up" => RoundingStrategy::Up,
736 "down" => RoundingStrategy::Down,
737 "to-zero" => RoundingStrategy::ToZero,
738 })
739 }
740
741 if strategy.is_ok() {
742 input.expect_comma()?;
743 }
744
745 let value = Self::parse_argument(context, input, allowed)?;
746
747 let step = input.try_parse(|input| {
750 input.expect_comma()?;
751 Self::parse_argument(context, input, allowed)
752 });
753
754 let step = step.unwrap_or(Self::Leaf(Leaf::Number(1.0)));
755
756 Ok(Self::Round {
757 strategy: strategy.unwrap_or(RoundingStrategy::Nearest),
758 value: Box::new(value),
759 step: Box::new(step),
760 })
761 },
762 MathFunction::Mod | MathFunction::Rem => {
763 let dividend = Self::parse_argument(context, input, allowed)?;
764 input.expect_comma()?;
765 let divisor = Self::parse_argument(context, input, allowed)?;
766
767 let op = match function {
768 MathFunction::Mod => ModRemOp::Mod,
769 MathFunction::Rem => ModRemOp::Rem,
770 _ => unreachable!(),
771 };
772 Ok(Self::ModRem {
773 dividend: Box::new(dividend),
774 divisor: Box::new(divisor),
775 op,
776 })
777 },
778 MathFunction::Min | MathFunction::Max => {
779 let arguments = input.parse_comma_separated(|input| {
785 let result = Self::parse_argument(context, input, allowed)?;
786 Ok(result)
787 })?;
788
789 let op = match function {
790 MathFunction::Min => MinMaxOp::Min,
791 MathFunction::Max => MinMaxOp::Max,
792 _ => unreachable!(),
793 };
794
795 Ok(Self::MinMax(arguments.into(), op))
796 },
797 MathFunction::Sin | MathFunction::Cos | MathFunction::Tan => {
798 let a = Self::parse_angle_argument(context, input)?;
799
800 let number = match function {
801 MathFunction::Sin => a.sin(),
802 MathFunction::Cos => a.cos(),
803 MathFunction::Tan => a.tan(),
804 _ => unsafe {
805 debug_unreachable!("We just checked!");
806 },
807 };
808
809 Ok(Self::Leaf(Leaf::Number(number)))
810 },
811 MathFunction::Asin | MathFunction::Acos | MathFunction::Atan => {
812 let a = Self::parse_number_argument(context, input)?;
813
814 let radians = match function {
815 MathFunction::Asin => a.asin(),
816 MathFunction::Acos => a.acos(),
817 MathFunction::Atan => a.atan(),
818 _ => unsafe {
819 debug_unreachable!("We just checked!");
820 },
821 };
822
823 Ok(Self::Leaf(Leaf::Angle(Angle::from_radians(radians))))
824 },
825 MathFunction::Atan2 => {
826 let allow_all = allowed.new_including(CalcUnits::ALL);
827 let a = Self::parse_argument(context, input, allow_all)?;
828 input.expect_comma()?;
829 let b = Self::parse_argument(context, input, allow_all)?;
830
831 let radians = Self::try_resolve(input, || {
832 if let Ok(a) = a.to_number() {
833 let b = b.to_number()?;
834 return Ok(a.atan2(b));
835 }
836
837 if let Ok(a) = a.to_percentage() {
838 let b = b.to_percentage()?;
839 return Ok(a.atan2(b));
840 }
841
842 if let Ok(a) = a.to_time(None) {
843 let b = b.to_time(None)?;
844 return Ok(a.seconds().atan2(b.seconds()));
845 }
846
847 if let Ok(a) = a.to_angle() {
848 let b = b.to_angle()?;
849 return Ok(a.radians().atan2(b.radians()));
850 }
851
852 if let Ok(a) = a.to_resolution() {
853 let b = b.to_resolution()?;
854 return Ok(a.dppx().atan2(b.dppx()));
855 }
856
857 let a = a.into_length_or_percentage(AllowedNumericType::All)?;
858 let b = b.into_length_or_percentage(AllowedNumericType::All)?;
859 let (a, b) = CalcLengthPercentage::same_unit_length_as(&a, &b).ok_or(())?;
860
861 Ok(a.atan2(b))
862 })?;
863
864 Ok(Self::Leaf(Leaf::Angle(Angle::from_radians(radians))))
865 },
866 MathFunction::Pow => {
867 let a = Self::parse_number_argument(context, input)?;
868 input.expect_comma()?;
869 let b = Self::parse_number_argument(context, input)?;
870
871 let number = a.powf(b);
872
873 Ok(Self::Leaf(Leaf::Number(number)))
874 },
875 MathFunction::Sqrt => {
876 let a = Self::parse_number_argument(context, input)?;
877
878 let number = a.sqrt();
879
880 Ok(Self::Leaf(Leaf::Number(number)))
881 },
882 MathFunction::Hypot => {
883 let arguments = input.parse_comma_separated(|input| {
884 let result = Self::parse_argument(context, input, allowed)?;
885 Ok(result)
886 })?;
887
888 Ok(Self::Hypot(arguments.into()))
889 },
890 MathFunction::Log => {
891 let a = Self::parse_number_argument(context, input)?;
892 let b = input
893 .try_parse(|input| {
894 input.expect_comma()?;
895 Self::parse_number_argument(context, input)
896 })
897 .ok();
898
899 let number = match b {
900 Some(b) => a.log(b),
901 None => a.ln(),
902 };
903
904 Ok(Self::Leaf(Leaf::Number(number)))
905 },
906 MathFunction::Exp => {
907 let a = Self::parse_number_argument(context, input)?;
908 let number = a.exp();
909 Ok(Self::Leaf(Leaf::Number(number)))
910 },
911 MathFunction::Abs => {
912 let node = Self::parse_argument(context, input, allowed)?;
913 Ok(Self::Abs(Box::new(node)))
914 },
915 MathFunction::Sign => {
916 let node = Self::parse_argument(
920 context,
921 input,
922 allowed.new_including(CalcUnits::ALL - CalcUnits::PERCENTAGE),
923 )?;
924 Ok(Self::Sign(Box::new(node)))
925 },
926 }
927 })
928 }
929
930 fn parse_angle_argument<'i, 't>(
931 context: &ParserContext,
932 input: &mut Parser<'i, 't>,
933 ) -> Result<CSSFloat, ParseError<'i>> {
934 let argument = Self::parse_argument(context, input, AllowParse::new(CalcUnits::ANGLE))?;
935 argument
936 .to_number()
937 .or_else(|()| Ok(argument.to_angle()?.radians()))
938 .map_err(|()| input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
939 }
940
941 fn parse_number_argument<'i, 't>(
942 context: &ParserContext,
943 input: &mut Parser<'i, 't>,
944 ) -> Result<CSSFloat, ParseError<'i>> {
945 Self::parse_argument(context, input, AllowParse::new(CalcUnits::empty()))?
946 .to_number()
947 .map_err(|()| input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
948 }
949
950 fn parse_argument<'i, 't>(
951 context: &ParserContext,
952 input: &mut Parser<'i, 't>,
953 allowed: AllowParse,
954 ) -> Result<Self, ParseError<'i>> {
955 let mut sum = SmallVec::<[CalcNode; 1]>::new();
956 let first = Self::parse_product(context, input, allowed)?;
957 sum.push(first);
958 loop {
959 let start = input.state();
960 match input.next_including_whitespace() {
961 Ok(&Token::WhiteSpace(_)) => {
962 if input.is_exhausted() {
963 break; }
965 match *input.next()? {
966 Token::Delim('+') => {
967 let rhs = Self::parse_product(context, input, allowed)?;
968 if sum.last_mut().unwrap().try_sum_in_place(&rhs).is_err() {
969 sum.push(rhs);
970 }
971 },
972 Token::Delim('-') => {
973 let mut rhs = Self::parse_product(context, input, allowed)?;
974 rhs.negate();
975 if sum.last_mut().unwrap().try_sum_in_place(&rhs).is_err() {
976 sum.push(rhs);
977 }
978 },
979 _ => {
980 input.reset(&start);
981 break;
982 },
983 }
984 },
985 _ => {
986 input.reset(&start);
987 break;
988 },
989 }
990 }
991
992 Ok(if sum.len() == 1 {
993 sum.drain(..).next().unwrap()
994 } else {
995 Self::Sum(sum.into_boxed_slice().into())
996 })
997 }
998
999 fn parse_product<'i, 't>(
1009 context: &ParserContext,
1010 input: &mut Parser<'i, 't>,
1011 allowed: AllowParse,
1012 ) -> Result<Self, ParseError<'i>> {
1013 let mut product = SmallVec::<[CalcNode; 1]>::new();
1014 let first = Self::parse_one(context, input, allowed)?;
1015 product.push(first);
1016
1017 loop {
1018 let start = input.state();
1019 match input.next() {
1020 Ok(&Token::Delim('*')) => {
1021 let mut rhs = Self::parse_one(context, input, allowed)?;
1022
1023 if !product.last_mut().unwrap().try_product_in_place(&mut rhs) {
1026 product.push(rhs);
1027 }
1028 },
1029 Ok(&Token::Delim('/')) => {
1030 let rhs = Self::parse_one(context, input, allowed)?;
1031
1032 enum InPlaceDivisionResult {
1033 Merged,
1035 Unchanged,
1038 Invalid,
1041 }
1042
1043 fn try_division_in_place(
1044 left: &mut CalcNode,
1045 right: &CalcNode,
1046 ) -> InPlaceDivisionResult {
1047 if let Ok(resolved) = right.resolve() {
1048 if let Some(number) = resolved.as_number() {
1049 if number != 1.0 && left.is_product_distributive() {
1050 if left.map(|l| l / number).is_err() {
1051 return InPlaceDivisionResult::Invalid;
1052 }
1053 return InPlaceDivisionResult::Merged;
1054 }
1055 } else {
1056 return if resolved.unit().contains(CalcUnits::COLOR_COMPONENT) {
1059 InPlaceDivisionResult::Unchanged
1060 } else {
1061 InPlaceDivisionResult::Invalid
1062 };
1063 }
1064 }
1065 InPlaceDivisionResult::Unchanged
1066 }
1067
1068 match try_division_in_place(&mut product.last_mut().unwrap(), &rhs) {
1073 InPlaceDivisionResult::Merged => {},
1074 InPlaceDivisionResult::Unchanged => {
1075 product.push(Self::Invert(Box::new(rhs)))
1076 },
1077 InPlaceDivisionResult::Invalid => {
1078 return Err(
1079 input.new_custom_error(StyleParseErrorKind::UnspecifiedError)
1080 )
1081 },
1082 }
1083 },
1084 _ => {
1085 input.reset(&start);
1086 break;
1087 },
1088 }
1089 }
1090
1091 Ok(if product.len() == 1 {
1092 product.drain(..).next().unwrap()
1093 } else {
1094 Self::Product(product.into_boxed_slice().into())
1095 })
1096 }
1097
1098 fn try_resolve<'i, 't, F>(
1099 input: &Parser<'i, 't>,
1100 closure: F,
1101 ) -> Result<CSSFloat, ParseError<'i>>
1102 where
1103 F: FnOnce() -> Result<CSSFloat, ()>,
1104 {
1105 closure().map_err(|()| input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
1106 }
1107
1108 pub fn into_length_or_percentage(
1111 mut self,
1112 clamping_mode: AllowedNumericType,
1113 ) -> Result<CalcLengthPercentage, ()> {
1114 self.simplify_and_sort();
1115
1116 let unit = self.unit()?;
1119 if !CalcUnits::LENGTH_PERCENTAGE.intersects(unit) {
1120 Err(())
1121 } else {
1122 Ok(CalcLengthPercentage {
1123 clamping_mode,
1124 node: self,
1125 })
1126 }
1127 }
1128
1129 fn to_time(&self, clamping_mode: Option<AllowedNumericType>) -> Result<Time, ()> {
1131 let seconds = if let Leaf::Time(time) = self.resolve()? {
1132 time.seconds()
1133 } else {
1134 return Err(());
1135 };
1136
1137 Ok(Time::from_seconds_with_calc_clamping_mode(
1138 seconds,
1139 clamping_mode,
1140 ))
1141 }
1142
1143 fn to_resolution(&self) -> Result<Resolution, ()> {
1145 let dppx = if let Leaf::Resolution(resolution) = self.resolve()? {
1146 resolution.dppx()
1147 } else {
1148 return Err(());
1149 };
1150
1151 Ok(Resolution::from_dppx_calc(dppx))
1152 }
1153
1154 fn to_angle(&self) -> Result<Angle, ()> {
1156 let degrees = if let Leaf::Angle(angle) = self.resolve()? {
1157 angle.degrees()
1158 } else {
1159 return Err(());
1160 };
1161
1162 let result = Angle::from_calc(degrees);
1163 Ok(result)
1164 }
1165
1166 fn to_number(&self) -> Result<CSSFloat, ()> {
1168 let number = if let Leaf::Number(number) = self.resolve()? {
1169 number
1170 } else {
1171 return Err(());
1172 };
1173
1174 let result = number;
1175
1176 Ok(result)
1177 }
1178
1179 fn to_percentage(&self) -> Result<CSSFloat, ()> {
1181 if let Leaf::Percentage(percentage) = self.resolve()? {
1182 Ok(percentage)
1183 } else {
1184 Err(())
1185 }
1186 }
1187
1188 #[inline]
1191 pub fn math_function<'i>(
1192 _: &ParserContext,
1193 name: &CowRcStr<'i>,
1194 location: cssparser::SourceLocation,
1195 ) -> Result<MathFunction, ParseError<'i>> {
1196 let function = match MathFunction::from_ident(&*name) {
1197 Ok(f) => f,
1198 Err(()) => {
1199 return Err(location.new_unexpected_token_error(Token::Function(name.clone())))
1200 },
1201 };
1202
1203 Ok(function)
1204 }
1205
1206 pub fn parse_length_or_percentage<'i, 't>(
1208 context: &ParserContext,
1209 input: &mut Parser<'i, 't>,
1210 clamping_mode: AllowedNumericType,
1211 function: MathFunction,
1212 allow_anchor: AllowAnchorPositioningFunctions,
1213 ) -> Result<CalcLengthPercentage, ParseError<'i>> {
1214 let allowed = if allow_anchor == AllowAnchorPositioningFunctions::No {
1215 AllowParse::new(CalcUnits::LENGTH_PERCENTAGE)
1216 } else {
1217 AllowParse {
1218 units: CalcUnits::LENGTH_PERCENTAGE,
1219 additional_functions: match allow_anchor {
1220 AllowAnchorPositioningFunctions::No => unreachable!(),
1221 AllowAnchorPositioningFunctions::AllowAnchorSize => {
1222 AdditionalFunctions::ANCHOR_SIZE
1223 },
1224 AllowAnchorPositioningFunctions::AllowAnchorAndAnchorSize => {
1225 AdditionalFunctions::ANCHOR | AdditionalFunctions::ANCHOR_SIZE
1226 },
1227 },
1228 }
1229 };
1230 Self::parse(context, input, function, allowed)?
1231 .into_length_or_percentage(clamping_mode)
1232 .map_err(|()| input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
1233 }
1234
1235 pub fn parse_percentage<'i, 't>(
1237 context: &ParserContext,
1238 input: &mut Parser<'i, 't>,
1239 function: MathFunction,
1240 ) -> Result<CSSFloat, ParseError<'i>> {
1241 Self::parse(
1242 context,
1243 input,
1244 function,
1245 AllowParse::new(CalcUnits::PERCENTAGE),
1246 )?
1247 .to_percentage()
1248 .map(crate::values::normalize)
1249 .map_err(|()| input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
1250 }
1251
1252 pub fn parse_length<'i, 't>(
1254 context: &ParserContext,
1255 input: &mut Parser<'i, 't>,
1256 clamping_mode: AllowedNumericType,
1257 function: MathFunction,
1258 ) -> Result<CalcLengthPercentage, ParseError<'i>> {
1259 Self::parse(context, input, function, AllowParse::new(CalcUnits::LENGTH))?
1260 .into_length_or_percentage(clamping_mode)
1261 .map_err(|()| input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
1262 }
1263
1264 pub fn parse_number<'i, 't>(
1266 context: &ParserContext,
1267 input: &mut Parser<'i, 't>,
1268 function: MathFunction,
1269 ) -> Result<CSSFloat, ParseError<'i>> {
1270 Self::parse(
1271 context,
1272 input,
1273 function,
1274 AllowParse::new(CalcUnits::empty()),
1275 )?
1276 .to_number()
1277 .map_err(|()| input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
1278 }
1279
1280 pub fn parse_angle<'i, 't>(
1282 context: &ParserContext,
1283 input: &mut Parser<'i, 't>,
1284 function: MathFunction,
1285 ) -> Result<Angle, ParseError<'i>> {
1286 Self::parse(context, input, function, AllowParse::new(CalcUnits::ANGLE))?
1287 .to_angle()
1288 .map_err(|()| input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
1289 }
1290
1291 pub fn parse_time<'i, 't>(
1293 context: &ParserContext,
1294 input: &mut Parser<'i, 't>,
1295 clamping_mode: AllowedNumericType,
1296 function: MathFunction,
1297 ) -> Result<Time, ParseError<'i>> {
1298 Self::parse(context, input, function, AllowParse::new(CalcUnits::TIME))?
1299 .to_time(Some(clamping_mode))
1300 .map_err(|()| input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
1301 }
1302
1303 pub fn parse_resolution<'i, 't>(
1305 context: &ParserContext,
1306 input: &mut Parser<'i, 't>,
1307 function: MathFunction,
1308 ) -> Result<Resolution, ParseError<'i>> {
1309 Self::parse(
1310 context,
1311 input,
1312 function,
1313 AllowParse::new(CalcUnits::RESOLUTION),
1314 )?
1315 .to_resolution()
1316 .map_err(|()| input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
1317 }
1318}