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