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 _ => {
384 match *other {
385 Number(..) | Percentage(..) | Angle(..) | Time(..) | Resolution(..)
386 | Length(..) | ColorComponent(..) => {},
387 }
388 unsafe {
389 debug_unreachable!();
390 }
391 },
392 }
393
394 Ok(())
395 }
396
397 fn try_product_in_place(&mut self, other: &mut Self) -> bool {
398 if let Self::Number(ref mut left) = *self {
399 if let Self::Number(ref right) = *other {
400 *left *= *right;
402 true
403 } else {
404 if other.map(|v| v * *left).is_ok() {
407 std::mem::swap(self, other);
408 true
409 } else {
410 false
411 }
412 }
413 } else if let Self::Number(ref right) = *other {
414 self.map(|v| v * *right).is_ok()
417 } else {
418 false
420 }
421 }
422
423 fn try_op<O>(&self, other: &Self, op: O) -> Result<Self, ()>
424 where
425 O: Fn(f32, f32) -> f32,
426 {
427 use self::Leaf::*;
428
429 if std::mem::discriminant(self) != std::mem::discriminant(other) {
430 return Err(());
431 }
432
433 match (self, other) {
434 (&Number(one), &Number(other)) => {
435 return Ok(Leaf::Number(op(one, other)));
436 },
437 (&Percentage(one), &Percentage(other)) => {
438 return Ok(Leaf::Percentage(op(one, other)));
439 },
440 (&Angle(ref one), &Angle(ref other)) => {
441 return Ok(Leaf::Angle(specified::Angle::from_calc(op(
442 one.degrees(),
443 other.degrees(),
444 ))));
445 },
446 (&Resolution(ref one), &Resolution(ref other)) => {
447 return Ok(Leaf::Resolution(specified::Resolution::from_dppx(op(
448 one.dppx(),
449 other.dppx(),
450 ))));
451 },
452 (&Time(ref one), &Time(ref other)) => {
453 return Ok(Leaf::Time(specified::Time::from_seconds(op(
454 one.seconds(),
455 other.seconds(),
456 ))));
457 },
458 (&Length(ref one), &Length(ref other)) => {
459 return Ok(Leaf::Length(one.try_op(other, op)?));
460 },
461 _ => {
462 match *other {
463 Number(..) | Percentage(..) | Angle(..) | Time(..) | Length(..)
464 | Resolution(..) | ColorComponent(..) => {},
465 }
466 unsafe {
467 debug_unreachable!();
468 }
469 },
470 }
471 }
472
473 fn map(&mut self, mut op: impl FnMut(f32) -> f32) -> Result<(), ()> {
474 Ok(match self {
475 Leaf::Length(one) => *one = one.map(op),
476 Leaf::Angle(one) => *one = specified::Angle::from_calc(op(one.degrees())),
477 Leaf::Time(one) => *one = specified::Time::from_seconds(op(one.seconds())),
478 Leaf::Resolution(one) => *one = specified::Resolution::from_dppx(op(one.dppx())),
479 Leaf::Percentage(one) => *one = op(*one),
480 Leaf::Number(one) => *one = op(*one),
481 Leaf::ColorComponent(..) => return Err(()),
482 })
483 }
484}
485
486impl GenericAnchorSide<Box<CalcNode>> {
487 fn parse_in_calc<'i, 't>(
488 context: &ParserContext,
489 input: &mut Parser<'i, 't>,
490 ) -> Result<Self, ParseError<'i>> {
491 if let Ok(k) = input.try_parse(|i| AnchorSideKeyword::parse(i)) {
492 return Ok(Self::Keyword(k));
493 }
494 Ok(Self::Percentage(Box::new(CalcNode::parse_argument(
495 context,
496 input,
497 AllowParse::new(CalcUnits::PERCENTAGE),
498 )?)))
499 }
500}
501
502impl GenericAnchorFunction<Box<CalcNode>, Box<CalcNode>> {
503 fn parse_in_calc<'i, 't>(
504 context: &ParserContext,
505 additional_functions: AdditionalFunctions,
506 input: &mut Parser<'i, 't>,
507 ) -> Result<Self, ParseError<'i>> {
508 if !static_prefs::pref!("layout.css.anchor-positioning.enabled") {
509 return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));
510 }
511 input.parse_nested_block(|i| {
512 let target_element = i.try_parse(|i| DashedIdent::parse(context, i)).ok();
513 let side = GenericAnchorSide::parse_in_calc(context, i)?;
514 let target_element = if target_element.is_none() {
515 i.try_parse(|i| DashedIdent::parse(context, i)).ok()
516 } else {
517 target_element
518 };
519 let fallback = i
520 .try_parse(|i| {
521 i.expect_comma()?;
522 let node = CalcNode::parse_argument(
523 context,
524 i,
525 AllowParse {
526 units: CalcUnits::LENGTH_PERCENTAGE,
527 additional_functions,
528 },
529 )?;
530 Ok::<Box<CalcNode>, ParseError<'i>>(Box::new(node))
531 })
532 .ok();
533 Ok(Self {
534 target_element: target_element.unwrap_or_else(DashedIdent::empty),
535 side,
536 fallback: fallback.into(),
537 })
538 })
539 }
540}
541
542impl GenericAnchorSizeFunction<Box<CalcNode>> {
543 fn parse_in_calc<'i, 't>(
544 context: &ParserContext,
545 input: &mut Parser<'i, 't>,
546 ) -> Result<Self, ParseError<'i>> {
547 if !static_prefs::pref!("layout.css.anchor-positioning.enabled") {
548 return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));
549 }
550 GenericAnchorSizeFunction::parse_inner(context, input, |i| {
551 CalcNode::parse_argument(context, i, AllowParse::new(CalcUnits::LENGTH_PERCENTAGE))
552 .map(|r| Box::new(r))
553 })
554 }
555}
556
557pub type CalcAnchorFunction = generic::GenericCalcAnchorFunction<Leaf>;
559pub type CalcAnchorSizeFunction = generic::GenericCalcAnchorSizeFunction<Leaf>;
561
562pub type CalcNode = generic::GenericCalcNode<Leaf>;
564impl CalcNode {
565 fn parse_one<'i, 't>(
571 context: &ParserContext,
572 input: &mut Parser<'i, 't>,
573 allowed: AllowParse,
574 ) -> Result<Self, ParseError<'i>> {
575 let location = input.current_source_location();
576 match input.next()? {
577 &Token::Number { value, .. } => Ok(CalcNode::Leaf(Leaf::Number(value))),
578 &Token::Dimension {
579 value, ref unit, ..
580 } => {
581 if allowed.includes(CalcUnits::LENGTH) {
582 if let Ok(l) = NoCalcLength::parse_dimension(context, value, unit) {
583 return Ok(CalcNode::Leaf(Leaf::Length(l)));
584 }
585 }
586 if allowed.includes(CalcUnits::ANGLE) {
587 if let Ok(a) = Angle::parse_dimension(value, unit, true) {
588 return Ok(CalcNode::Leaf(Leaf::Angle(a)));
589 }
590 }
591 if allowed.includes(CalcUnits::TIME) {
592 if let Ok(t) = Time::parse_dimension(value, unit) {
593 return Ok(CalcNode::Leaf(Leaf::Time(t)));
594 }
595 }
596 if allowed.includes(CalcUnits::RESOLUTION) {
597 if let Ok(t) = Resolution::parse_dimension(value, unit) {
598 return Ok(CalcNode::Leaf(Leaf::Resolution(t)));
599 }
600 }
601 return Err(location.new_custom_error(StyleParseErrorKind::UnspecifiedError));
602 },
603 &Token::Percentage { unit_value, .. } if allowed.includes(CalcUnits::PERCENTAGE) => {
604 Ok(CalcNode::Leaf(Leaf::Percentage(unit_value)))
605 },
606 &Token::ParenthesisBlock => {
607 input.parse_nested_block(|input| CalcNode::parse_argument(context, input, allowed))
608 },
609 &Token::Function(ref name)
610 if allowed
611 .additional_functions
612 .intersects(AdditionalFunctions::ANCHOR)
613 && name.eq_ignore_ascii_case("anchor") =>
614 {
615 let anchor_function = GenericAnchorFunction::parse_in_calc(
616 context,
617 allowed.additional_functions,
618 input,
619 )?;
620 Ok(CalcNode::Anchor(Box::new(anchor_function)))
621 },
622 &Token::Function(ref name)
623 if allowed
624 .additional_functions
625 .intersects(AdditionalFunctions::ANCHOR_SIZE)
626 && name.eq_ignore_ascii_case("anchor-size") =>
627 {
628 let anchor_size_function =
629 GenericAnchorSizeFunction::parse_in_calc(context, input)?;
630 Ok(CalcNode::AnchorSize(Box::new(anchor_size_function)))
631 },
632 &Token::Function(ref name) => {
633 let function = CalcNode::math_function(context, name, location)?;
634 CalcNode::parse(context, input, function, allowed)
635 },
636 &Token::Ident(ref ident) => {
637 let leaf = match_ignore_ascii_case! { &**ident,
638 "e" => Leaf::Number(std::f32::consts::E),
639 "pi" => Leaf::Number(std::f32::consts::PI),
640 "infinity" => Leaf::Number(f32::INFINITY),
641 "-infinity" => Leaf::Number(f32::NEG_INFINITY),
642 "nan" => Leaf::Number(f32::NAN),
643 _ => {
644 if crate::color::parsing::rcs_enabled() &&
645 allowed.includes(CalcUnits::COLOR_COMPONENT)
646 {
647 if let Ok(channel_keyword) = ChannelKeyword::from_ident(&ident) {
648 Leaf::ColorComponent(channel_keyword)
649 } else {
650 return Err(location
651 .new_unexpected_token_error(Token::Ident(ident.clone())));
652 }
653 } else {
654 return Err(
655 location.new_unexpected_token_error(Token::Ident(ident.clone()))
656 );
657 }
658 },
659 };
660 Ok(CalcNode::Leaf(leaf))
661 },
662 t => Err(location.new_unexpected_token_error(t.clone())),
663 }
664 }
665
666 pub fn parse<'i, 't>(
670 context: &ParserContext,
671 input: &mut Parser<'i, 't>,
672 function: MathFunction,
673 allowed: AllowParse,
674 ) -> Result<Self, ParseError<'i>> {
675 input.parse_nested_block(|input| {
676 match function {
677 MathFunction::Calc => Self::parse_argument(context, input, allowed),
678 MathFunction::Clamp => {
679 let min = Self::parse_argument(context, input, allowed)?;
680 input.expect_comma()?;
681 let center = Self::parse_argument(context, input, allowed)?;
682 input.expect_comma()?;
683 let max = Self::parse_argument(context, input, allowed)?;
684 Ok(Self::Clamp {
685 min: Box::new(min),
686 center: Box::new(center),
687 max: Box::new(max),
688 })
689 },
690 MathFunction::Round => {
691 let strategy = input.try_parse(parse_rounding_strategy);
692
693 fn parse_rounding_strategy<'i, 't>(
696 input: &mut Parser<'i, 't>,
697 ) -> Result<RoundingStrategy, ParseError<'i>> {
698 Ok(try_match_ident_ignore_ascii_case! { input,
699 "nearest" => RoundingStrategy::Nearest,
700 "up" => RoundingStrategy::Up,
701 "down" => RoundingStrategy::Down,
702 "to-zero" => RoundingStrategy::ToZero,
703 })
704 }
705
706 if strategy.is_ok() {
707 input.expect_comma()?;
708 }
709
710 let value = Self::parse_argument(context, input, allowed)?;
711
712 let step = input.try_parse(|input| {
715 input.expect_comma()?;
716 Self::parse_argument(context, input, allowed)
717 });
718
719 let step = step.unwrap_or(Self::Leaf(Leaf::Number(1.0)));
720
721 Ok(Self::Round {
722 strategy: strategy.unwrap_or(RoundingStrategy::Nearest),
723 value: Box::new(value),
724 step: Box::new(step),
725 })
726 },
727 MathFunction::Mod | MathFunction::Rem => {
728 let dividend = Self::parse_argument(context, input, allowed)?;
729 input.expect_comma()?;
730 let divisor = Self::parse_argument(context, input, allowed)?;
731
732 let op = match function {
733 MathFunction::Mod => ModRemOp::Mod,
734 MathFunction::Rem => ModRemOp::Rem,
735 _ => unreachable!(),
736 };
737 Ok(Self::ModRem {
738 dividend: Box::new(dividend),
739 divisor: Box::new(divisor),
740 op,
741 })
742 },
743 MathFunction::Min | MathFunction::Max => {
744 let arguments = input.parse_comma_separated(|input| {
750 let result = Self::parse_argument(context, input, allowed)?;
751 Ok(result)
752 })?;
753
754 let op = match function {
755 MathFunction::Min => MinMaxOp::Min,
756 MathFunction::Max => MinMaxOp::Max,
757 _ => unreachable!(),
758 };
759
760 Ok(Self::MinMax(arguments.into(), op))
761 },
762 MathFunction::Sin | MathFunction::Cos | MathFunction::Tan => {
763 let a = Self::parse_angle_argument(context, input)?;
764
765 let number = match function {
766 MathFunction::Sin => a.sin(),
767 MathFunction::Cos => a.cos(),
768 MathFunction::Tan => a.tan(),
769 _ => unsafe {
770 debug_unreachable!("We just checked!");
771 },
772 };
773
774 Ok(Self::Leaf(Leaf::Number(number)))
775 },
776 MathFunction::Asin | MathFunction::Acos | MathFunction::Atan => {
777 let a = Self::parse_number_argument(context, input)?;
778
779 let radians = match function {
780 MathFunction::Asin => a.asin(),
781 MathFunction::Acos => a.acos(),
782 MathFunction::Atan => a.atan(),
783 _ => unsafe {
784 debug_unreachable!("We just checked!");
785 },
786 };
787
788 Ok(Self::Leaf(Leaf::Angle(Angle::from_radians(radians))))
789 },
790 MathFunction::Atan2 => {
791 let allow_all = allowed.new_including(CalcUnits::ALL);
792 let a = Self::parse_argument(context, input, allow_all)?;
793 input.expect_comma()?;
794 let b = Self::parse_argument(context, input, allow_all)?;
795
796 let radians = Self::try_resolve(input, || {
797 if let Ok(a) = a.to_number() {
798 let b = b.to_number()?;
799 return Ok(a.atan2(b));
800 }
801
802 if let Ok(a) = a.to_percentage() {
803 let b = b.to_percentage()?;
804 return Ok(a.atan2(b));
805 }
806
807 if let Ok(a) = a.to_time(None) {
808 let b = b.to_time(None)?;
809 return Ok(a.seconds().atan2(b.seconds()));
810 }
811
812 if let Ok(a) = a.to_angle() {
813 let b = b.to_angle()?;
814 return Ok(a.radians().atan2(b.radians()));
815 }
816
817 if let Ok(a) = a.to_resolution() {
818 let b = b.to_resolution()?;
819 return Ok(a.dppx().atan2(b.dppx()));
820 }
821
822 let a = a.into_length_or_percentage(AllowedNumericType::All)?;
823 let b = b.into_length_or_percentage(AllowedNumericType::All)?;
824 let (a, b) = CalcLengthPercentage::same_unit_length_as(&a, &b).ok_or(())?;
825
826 Ok(a.atan2(b))
827 })?;
828
829 Ok(Self::Leaf(Leaf::Angle(Angle::from_radians(radians))))
830 },
831 MathFunction::Pow => {
832 let a = Self::parse_number_argument(context, input)?;
833 input.expect_comma()?;
834 let b = Self::parse_number_argument(context, input)?;
835
836 let number = a.powf(b);
837
838 Ok(Self::Leaf(Leaf::Number(number)))
839 },
840 MathFunction::Sqrt => {
841 let a = Self::parse_number_argument(context, input)?;
842
843 let number = a.sqrt();
844
845 Ok(Self::Leaf(Leaf::Number(number)))
846 },
847 MathFunction::Hypot => {
848 let arguments = input.parse_comma_separated(|input| {
849 let result = Self::parse_argument(context, input, allowed)?;
850 Ok(result)
851 })?;
852
853 Ok(Self::Hypot(arguments.into()))
854 },
855 MathFunction::Log => {
856 let a = Self::parse_number_argument(context, input)?;
857 let b = input
858 .try_parse(|input| {
859 input.expect_comma()?;
860 Self::parse_number_argument(context, input)
861 })
862 .ok();
863
864 let number = match b {
865 Some(b) => a.log(b),
866 None => a.ln(),
867 };
868
869 Ok(Self::Leaf(Leaf::Number(number)))
870 },
871 MathFunction::Exp => {
872 let a = Self::parse_number_argument(context, input)?;
873 let number = a.exp();
874 Ok(Self::Leaf(Leaf::Number(number)))
875 },
876 MathFunction::Abs => {
877 let node = Self::parse_argument(context, input, allowed)?;
878 Ok(Self::Abs(Box::new(node)))
879 },
880 MathFunction::Sign => {
881 let node = Self::parse_argument(
885 context,
886 input,
887 allowed.new_including(CalcUnits::ALL - CalcUnits::PERCENTAGE),
888 )?;
889 Ok(Self::Sign(Box::new(node)))
890 },
891 }
892 })
893 }
894
895 fn parse_angle_argument<'i, 't>(
896 context: &ParserContext,
897 input: &mut Parser<'i, 't>,
898 ) -> Result<CSSFloat, ParseError<'i>> {
899 let argument = Self::parse_argument(context, input, AllowParse::new(CalcUnits::ANGLE))?;
900 argument
901 .to_number()
902 .or_else(|()| Ok(argument.to_angle()?.radians()))
903 .map_err(|()| input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
904 }
905
906 fn parse_number_argument<'i, 't>(
907 context: &ParserContext,
908 input: &mut Parser<'i, 't>,
909 ) -> Result<CSSFloat, ParseError<'i>> {
910 Self::parse_argument(context, input, AllowParse::new(CalcUnits::empty()))?
911 .to_number()
912 .map_err(|()| input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
913 }
914
915 fn parse_argument<'i, 't>(
916 context: &ParserContext,
917 input: &mut Parser<'i, 't>,
918 allowed: AllowParse,
919 ) -> Result<Self, ParseError<'i>> {
920 let mut sum = SmallVec::<[CalcNode; 1]>::new();
921 let first = Self::parse_product(context, input, allowed)?;
922 sum.push(first);
923 loop {
924 let start = input.state();
925 match input.next_including_whitespace() {
926 Ok(&Token::WhiteSpace(_)) => {
927 if input.is_exhausted() {
928 break; }
930 match *input.next()? {
931 Token::Delim('+') => {
932 let rhs = Self::parse_product(context, input, allowed)?;
933 if sum.last_mut().unwrap().try_sum_in_place(&rhs).is_err() {
934 sum.push(rhs);
935 }
936 },
937 Token::Delim('-') => {
938 let mut rhs = Self::parse_product(context, input, allowed)?;
939 rhs.negate();
940 if sum.last_mut().unwrap().try_sum_in_place(&rhs).is_err() {
941 sum.push(rhs);
942 }
943 },
944 _ => {
945 input.reset(&start);
946 break;
947 },
948 }
949 },
950 _ => {
951 input.reset(&start);
952 break;
953 },
954 }
955 }
956
957 Ok(if sum.len() == 1 {
958 sum.drain(..).next().unwrap()
959 } else {
960 Self::Sum(sum.into_boxed_slice().into())
961 })
962 }
963
964 fn parse_product<'i, 't>(
974 context: &ParserContext,
975 input: &mut Parser<'i, 't>,
976 allowed: AllowParse,
977 ) -> Result<Self, ParseError<'i>> {
978 let mut product = SmallVec::<[CalcNode; 1]>::new();
979 let first = Self::parse_one(context, input, allowed)?;
980 product.push(first);
981
982 loop {
983 let start = input.state();
984 match input.next() {
985 Ok(&Token::Delim('*')) => {
986 let mut rhs = Self::parse_one(context, input, allowed)?;
987
988 if !product.last_mut().unwrap().try_product_in_place(&mut rhs) {
991 product.push(rhs);
992 }
993 },
994 Ok(&Token::Delim('/')) => {
995 let rhs = Self::parse_one(context, input, allowed)?;
996
997 enum InPlaceDivisionResult {
998 Merged,
1000 Unchanged,
1003 Invalid,
1006 }
1007
1008 fn try_division_in_place(
1009 left: &mut CalcNode,
1010 right: &CalcNode,
1011 ) -> InPlaceDivisionResult {
1012 if let Ok(resolved) = right.resolve() {
1013 if let Some(number) = resolved.as_number() {
1014 if number != 1.0 && left.is_product_distributive() {
1015 if left.map(|l| l / number).is_err() {
1016 return InPlaceDivisionResult::Invalid;
1017 }
1018 return InPlaceDivisionResult::Merged;
1019 }
1020 } else {
1021 return if resolved.unit().contains(CalcUnits::COLOR_COMPONENT) {
1024 InPlaceDivisionResult::Unchanged
1025 } else {
1026 InPlaceDivisionResult::Invalid
1027 };
1028 }
1029 }
1030 InPlaceDivisionResult::Unchanged
1031 }
1032
1033 match try_division_in_place(&mut product.last_mut().unwrap(), &rhs) {
1038 InPlaceDivisionResult::Merged => {},
1039 InPlaceDivisionResult::Unchanged => {
1040 product.push(Self::Invert(Box::new(rhs)))
1041 },
1042 InPlaceDivisionResult::Invalid => {
1043 return Err(
1044 input.new_custom_error(StyleParseErrorKind::UnspecifiedError)
1045 )
1046 },
1047 }
1048 },
1049 _ => {
1050 input.reset(&start);
1051 break;
1052 },
1053 }
1054 }
1055
1056 Ok(if product.len() == 1 {
1057 product.drain(..).next().unwrap()
1058 } else {
1059 Self::Product(product.into_boxed_slice().into())
1060 })
1061 }
1062
1063 fn try_resolve<'i, 't, F>(
1064 input: &Parser<'i, 't>,
1065 closure: F,
1066 ) -> Result<CSSFloat, ParseError<'i>>
1067 where
1068 F: FnOnce() -> Result<CSSFloat, ()>,
1069 {
1070 closure().map_err(|()| input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
1071 }
1072
1073 pub fn into_length_or_percentage(
1076 mut self,
1077 clamping_mode: AllowedNumericType,
1078 ) -> Result<CalcLengthPercentage, ()> {
1079 self.simplify_and_sort();
1080
1081 let unit = self.unit()?;
1084 if !CalcUnits::LENGTH_PERCENTAGE.intersects(unit) {
1085 Err(())
1086 } else {
1087 Ok(CalcLengthPercentage {
1088 clamping_mode,
1089 node: self,
1090 })
1091 }
1092 }
1093
1094 fn to_time(&self, clamping_mode: Option<AllowedNumericType>) -> Result<Time, ()> {
1096 let seconds = if let Leaf::Time(time) = self.resolve()? {
1097 time.seconds()
1098 } else {
1099 return Err(());
1100 };
1101
1102 Ok(Time::from_seconds_with_calc_clamping_mode(
1103 seconds,
1104 clamping_mode,
1105 ))
1106 }
1107
1108 fn to_resolution(&self) -> Result<Resolution, ()> {
1110 let dppx = if let Leaf::Resolution(resolution) = self.resolve()? {
1111 resolution.dppx()
1112 } else {
1113 return Err(());
1114 };
1115
1116 Ok(Resolution::from_dppx_calc(dppx))
1117 }
1118
1119 fn to_angle(&self) -> Result<Angle, ()> {
1121 let degrees = if let Leaf::Angle(angle) = self.resolve()? {
1122 angle.degrees()
1123 } else {
1124 return Err(());
1125 };
1126
1127 let result = Angle::from_calc(degrees);
1128 Ok(result)
1129 }
1130
1131 fn to_number(&self) -> Result<CSSFloat, ()> {
1133 let number = if let Leaf::Number(number) = self.resolve()? {
1134 number
1135 } else {
1136 return Err(());
1137 };
1138
1139 let result = number;
1140
1141 Ok(result)
1142 }
1143
1144 fn to_percentage(&self) -> Result<CSSFloat, ()> {
1146 if let Leaf::Percentage(percentage) = self.resolve()? {
1147 Ok(percentage)
1148 } else {
1149 Err(())
1150 }
1151 }
1152
1153 #[inline]
1156 pub fn math_function<'i>(
1157 _: &ParserContext,
1158 name: &CowRcStr<'i>,
1159 location: cssparser::SourceLocation,
1160 ) -> Result<MathFunction, ParseError<'i>> {
1161 let function = match MathFunction::from_ident(&*name) {
1162 Ok(f) => f,
1163 Err(()) => {
1164 return Err(location.new_unexpected_token_error(Token::Function(name.clone())))
1165 },
1166 };
1167
1168 Ok(function)
1169 }
1170
1171 pub fn parse_length_or_percentage<'i, 't>(
1173 context: &ParserContext,
1174 input: &mut Parser<'i, 't>,
1175 clamping_mode: AllowedNumericType,
1176 function: MathFunction,
1177 allow_anchor: AllowAnchorPositioningFunctions,
1178 ) -> Result<CalcLengthPercentage, ParseError<'i>> {
1179 let allowed = if allow_anchor == AllowAnchorPositioningFunctions::No {
1180 AllowParse::new(CalcUnits::LENGTH_PERCENTAGE)
1181 } else {
1182 AllowParse {
1183 units: CalcUnits::LENGTH_PERCENTAGE,
1184 additional_functions: match allow_anchor {
1185 AllowAnchorPositioningFunctions::No => unreachable!(),
1186 AllowAnchorPositioningFunctions::AllowAnchorSize => {
1187 AdditionalFunctions::ANCHOR_SIZE
1188 },
1189 AllowAnchorPositioningFunctions::AllowAnchorAndAnchorSize => {
1190 AdditionalFunctions::ANCHOR | AdditionalFunctions::ANCHOR_SIZE
1191 },
1192 },
1193 }
1194 };
1195 Self::parse(context, input, function, allowed)?
1196 .into_length_or_percentage(clamping_mode)
1197 .map_err(|()| input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
1198 }
1199
1200 pub fn parse_percentage<'i, 't>(
1202 context: &ParserContext,
1203 input: &mut Parser<'i, 't>,
1204 function: MathFunction,
1205 ) -> Result<CSSFloat, ParseError<'i>> {
1206 Self::parse(
1207 context,
1208 input,
1209 function,
1210 AllowParse::new(CalcUnits::PERCENTAGE),
1211 )?
1212 .to_percentage()
1213 .map(crate::values::normalize)
1214 .map_err(|()| input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
1215 }
1216
1217 pub fn parse_length<'i, 't>(
1219 context: &ParserContext,
1220 input: &mut Parser<'i, 't>,
1221 clamping_mode: AllowedNumericType,
1222 function: MathFunction,
1223 ) -> Result<CalcLengthPercentage, ParseError<'i>> {
1224 Self::parse(context, input, function, AllowParse::new(CalcUnits::LENGTH))?
1225 .into_length_or_percentage(clamping_mode)
1226 .map_err(|()| input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
1227 }
1228
1229 pub fn parse_number<'i, 't>(
1231 context: &ParserContext,
1232 input: &mut Parser<'i, 't>,
1233 function: MathFunction,
1234 ) -> Result<CSSFloat, ParseError<'i>> {
1235 Self::parse(
1236 context,
1237 input,
1238 function,
1239 AllowParse::new(CalcUnits::empty()),
1240 )?
1241 .to_number()
1242 .map_err(|()| input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
1243 }
1244
1245 pub fn parse_angle<'i, 't>(
1247 context: &ParserContext,
1248 input: &mut Parser<'i, 't>,
1249 function: MathFunction,
1250 ) -> Result<Angle, ParseError<'i>> {
1251 Self::parse(context, input, function, AllowParse::new(CalcUnits::ANGLE))?
1252 .to_angle()
1253 .map_err(|()| input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
1254 }
1255
1256 pub fn parse_time<'i, 't>(
1258 context: &ParserContext,
1259 input: &mut Parser<'i, 't>,
1260 clamping_mode: AllowedNumericType,
1261 function: MathFunction,
1262 ) -> Result<Time, ParseError<'i>> {
1263 Self::parse(context, input, function, AllowParse::new(CalcUnits::TIME))?
1264 .to_time(Some(clamping_mode))
1265 .map_err(|()| input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
1266 }
1267
1268 pub fn parse_resolution<'i, 't>(
1270 context: &ParserContext,
1271 input: &mut Parser<'i, 't>,
1272 function: MathFunction,
1273 ) -> Result<Resolution, ParseError<'i>> {
1274 Self::parse(
1275 context,
1276 input,
1277 function,
1278 AllowParse::new(CalcUnits::RESOLUTION),
1279 )?
1280 .to_resolution()
1281 .map_err(|()| input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
1282 }
1283}