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