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