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