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