1use crate::color::parsing::ChannelKeyword;
10use crate::derives::*;
11use crate::parser::{Parse, ParserContext};
12use crate::typed_om::{ToTyped, TypedValue};
13use crate::values::computed::{self, ToComputedValue};
14use crate::values::generics::calc::{
15 self as generic, CalcNodeLeaf, CalcUnits, GenericAnchorFunctionFallback, MinMaxOp, ModRemOp,
16 PositivePercentageBasis, RoundingStrategy, SortKey,
17};
18use crate::values::generics::length::GenericAnchorSizeFunction;
19use crate::values::generics::position::{
20 AnchorSideKeyword, GenericAnchorFunction, GenericAnchorSide, TreeScoped,
21};
22use crate::values::specified::length::NoCalcLength;
23use crate::values::specified::{
24 NoCalcAngle, NoCalcNumber, NoCalcPercentage, NoCalcResolution, NoCalcTime,
25};
26use crate::values::DashedIdent;
27use cssparser::{match_ignore_ascii_case, CowRcStr, Parser, Token};
28use debug_unreachable::debug_unreachable;
29use smallvec::SmallVec;
30use std::cmp;
31use std::fmt::{self, Write};
32use style_traits::values::specified::AllowedNumericType;
33use style_traits::{CssWriter, ParseError, SpecifiedValueInfo, StyleParseErrorKind, ToCss};
34use thin_vec::ThinVec;
35
36#[derive(Clone, Copy, Debug, Parse)]
38pub enum MathFunction {
39 Calc,
41 Min,
43 Max,
45 Clamp,
47 Round,
49 Mod,
51 Rem,
53 Sin,
55 Cos,
57 Tan,
59 Asin,
61 Acos,
63 Atan,
65 Atan2,
67 Pow,
69 Sqrt,
71 Hypot,
73 Log,
75 Exp,
77 Abs,
79 Sign,
81}
82
83#[derive(Clone, Debug, MallocSizeOf, PartialEq, ToShmem)]
85#[repr(u8)]
86pub enum Leaf {
87 Length(NoCalcLength),
89 Angle(NoCalcAngle),
91 Time(NoCalcTime),
93 Resolution(NoCalcResolution),
95 ColorComponent(ChannelKeyword),
97 Percentage(NoCalcPercentage),
99 Number(NoCalcNumber),
101}
102
103impl ToCss for Leaf {
104 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
105 where
106 W: Write,
107 {
108 match *self {
109 Self::Length(ref l) => l.to_css(dest),
110 Self::Number(n) => n.to_css(dest),
111 Self::Resolution(ref r) => r.to_css(dest),
112 Self::Percentage(p) => p.to_css(dest),
113 Self::Angle(ref a) => a.to_css(dest),
114 Self::Time(ref t) => t.to_css(dest),
115 Self::ColorComponent(ref s) => s.to_css(dest),
116 }
117 }
118}
119
120impl ToTyped for Leaf {
121 fn to_typed(&self, dest: &mut ThinVec<TypedValue>) -> Result<(), ()> {
122 match *self {
125 Self::Length(ref l) => l.to_typed(dest),
126 Self::Number(n) => n.to_typed(dest),
127 Self::Percentage(p) => p.to_typed(dest),
128 Self::Angle(ref a) => a.to_typed(dest),
129 Self::Time(t) => t.to_typed(dest),
130 _ => Err(()),
131 }
132 }
133}
134
135#[derive(Clone, Debug, MallocSizeOf, PartialEq, ToCss, ToShmem, ToTyped)]
142#[allow(missing_docs)]
143pub struct CalcNumeric {
144 #[css(skip)]
145 pub clamping_mode: AllowedNumericType,
146 pub node: CalcNode,
147}
148
149impl CalcNumeric {
150 pub fn with_clamping_mode(&self, clamping_mode: AllowedNumericType) -> Self {
152 Self {
153 clamping_mode,
154 node: self.node.clone(),
155 }
156 }
157
158 pub fn with_leaf_node(&self, leaf: Leaf) -> Self {
160 Self {
161 clamping_mode: self.clamping_mode,
162 node: CalcNode::Leaf(leaf),
163 }
164 }
165
166 pub fn resolve(
168 &self,
169 context: &computed::Context,
170 leaf_to_f32: impl FnOnce(Result<Leaf, ()>) -> f32,
171 ) -> f32 {
172 let result = self
173 .node
174 .resolve_computed(Some(context), |leaf| Ok(leaf.clone()));
175 self.clamping_mode.clamp(leaf_to_f32(result))
176 }
177
178 pub fn as_number(&self) -> Option<NoCalcNumber> {
180 match self.node.resolve() {
181 Ok(Leaf::Number(n)) => Some(n),
182 _ => None,
183 }
184 }
185
186 pub fn as_percentage(&self) -> Option<NoCalcPercentage> {
188 match self.node.resolve() {
189 Ok(Leaf::Percentage(p)) => Some(p),
190 _ => None,
191 }
192 }
193
194 pub fn as_time(&self) -> Option<NoCalcTime> {
196 match self.node.resolve() {
197 Ok(Leaf::Time(t)) => Some(t),
198 _ => None,
199 }
200 }
201
202 pub fn as_resolution(&self) -> Option<NoCalcResolution> {
204 match self.node.resolve() {
205 Ok(Leaf::Resolution(r)) => Some(r),
206 _ => None,
207 }
208 }
209
210 pub fn as_angle(&self) -> Option<NoCalcAngle> {
212 match self.node.resolve() {
213 Ok(Leaf::Angle(a)) => Some(a),
214 _ => None,
215 }
216 }
217}
218
219impl SpecifiedValueInfo for CalcNumeric {}
220
221#[derive(Clone, Debug, MallocSizeOf, PartialEq, ToCss, ToShmem, ToTyped)]
223pub struct CalcLengthPercentage(pub CalcNumeric);
224
225impl SpecifiedValueInfo for CalcLengthPercentage {}
226
227#[derive(Clone, Copy, PartialEq)]
229pub enum AllowAnchorPositioningFunctions {
230 No,
232 AllowAnchorSize,
234 AllowAnchorAndAnchorSize,
236}
237
238bitflags! {
239 #[derive(Clone, Copy, PartialEq, Eq)]
242 struct AdditionalFunctions: u8 {
243 const ANCHOR = 1 << 0;
245 const ANCHOR_SIZE = 1 << 1;
247 }
248}
249
250#[derive(Clone, Copy)]
252pub struct AllowParse {
253 units: CalcUnits,
255 pub color_components: bool,
257 additional_functions: AdditionalFunctions,
259}
260
261impl AllowParse {
262 pub fn new(units: CalcUnits) -> Self {
264 Self {
265 units,
266 color_components: false,
267 additional_functions: AdditionalFunctions::empty(),
268 }
269 }
270
271 fn new_including(mut self, units: CalcUnits) -> Self {
273 self.units |= units;
274 self
275 }
276
277 fn includes(&self, unit: CalcUnits) -> bool {
279 self.units.intersects(unit)
280 }
281}
282
283impl generic::CalcNodeLeaf for Leaf {
284 fn unit(&self) -> CalcUnits {
285 match self {
286 Leaf::Length(_) => CalcUnits::LENGTH,
287 Leaf::Angle(_) => CalcUnits::ANGLE,
288 Leaf::Time(_) => CalcUnits::TIME,
289 Leaf::Resolution(_) => CalcUnits::RESOLUTION,
290 Leaf::Percentage(_) => CalcUnits::PERCENTAGE,
291 Leaf::ColorComponent(_) | Leaf::Number(_) => CalcUnits::empty(),
292 }
293 }
294
295 fn unitless_value(&self) -> Option<f32> {
296 Some(match *self {
297 Self::Length(ref l) => l.unitless_value(),
298 Self::Percentage(ref p) => p.get(),
299 Self::Number(ref n) => n.value(),
300 Self::Resolution(ref r) => r.dppx(),
301 Self::Angle(ref a) => a.degrees(),
302 Self::Time(ref t) => t.seconds(),
303 Self::ColorComponent(_) => return None,
304 })
305 }
306
307 fn is_same_unit_as(&self, other: &Self) -> bool {
308 use self::Leaf::*;
309
310 if std::mem::discriminant(self) != std::mem::discriminant(other) {
311 return false;
312 }
313
314 match (self, other) {
315 (Length(a), Length(b)) => a.length_unit() == b.length_unit(),
316 (Angle(a), Angle(b)) => a.angle_unit() == b.angle_unit(),
317 (Time(a), Time(b)) => a.time_unit() == b.time_unit(),
318 (Resolution(a), Resolution(b)) => a.resolution_unit() == b.resolution_unit(),
319 (ColorComponent(_), ColorComponent(_))
320 | (Percentage(_), Percentage(_))
321 | (Number(_), Number(_)) => true,
322 _ => {
323 match *other {
324 Number(..) | Percentage(..) | Angle(..) | Time(..) | Resolution(..)
325 | Length(..) | ColorComponent(..) => {},
326 }
327 unsafe {
328 debug_unreachable!();
329 }
330 },
331 }
332 }
333
334 fn as_angle_radians(&self) -> Option<f32> {
335 if let Self::Angle(ref a) = *self {
336 Some(a.radians())
337 } else {
338 None
339 }
340 }
341
342 fn new_angle_from_radians(radians: f32) -> Self {
343 Self::Angle(NoCalcAngle::from_degrees(radians.to_degrees()))
344 }
345
346 fn new_number(value: f32) -> Self {
347 Self::Number(NoCalcNumber::new(value))
348 }
349
350 fn compare(&self, other: &Self, basis: PositivePercentageBasis) -> Option<cmp::Ordering> {
351 use self::Leaf::*;
352
353 if std::mem::discriminant(self) != std::mem::discriminant(other) {
354 return None;
355 }
356
357 if matches!(self, Percentage(..)) && matches!(basis, PositivePercentageBasis::Unknown) {
358 return None;
359 }
360
361 let self_negative = self.is_negative().unwrap_or(false);
362 if self_negative != other.is_negative().unwrap_or(false) {
363 return Some(if self_negative {
364 cmp::Ordering::Less
365 } else {
366 cmp::Ordering::Greater
367 });
368 }
369
370 match (self, other) {
371 (&Percentage(ref one), &Percentage(ref other)) => one.get().partial_cmp(&other.get()),
372 (&Length(ref one), &Length(ref other)) => one.partial_cmp(other),
373 (&Angle(ref one), &Angle(ref other)) => one.degrees().partial_cmp(&other.degrees()),
374 (&Time(ref one), &Time(ref other)) => one.seconds().partial_cmp(&other.seconds()),
375 (&Resolution(ref one), &Resolution(ref other)) => one.dppx().partial_cmp(&other.dppx()),
376 (&Number(ref one), &Number(ref other)) => one.partial_cmp(other),
377 (&ColorComponent(ref one), &ColorComponent(ref other)) => one.partial_cmp(other),
378 _ => {
379 match *self {
380 Length(..) | Percentage(..) | Angle(..) | Time(..) | Number(..)
381 | Resolution(..) | ColorComponent(..) => {},
382 }
383 unsafe {
384 debug_unreachable!("Forgot a branch?");
385 }
386 },
387 }
388 }
389
390 fn as_number(&self) -> Option<f32> {
391 match *self {
392 Leaf::Length(_)
393 | Leaf::Angle(_)
394 | Leaf::Time(_)
395 | Leaf::Resolution(_)
396 | Leaf::Percentage(_)
397 | Leaf::ColorComponent(_) => None,
398 Leaf::Number(n) => Some(n.value()),
399 }
400 }
401
402 fn sort_key(&self) -> SortKey {
403 match *self {
404 Self::Number(..) => SortKey::Number,
405 Self::Percentage(..) => SortKey::Percentage,
406 Self::Time(..) => SortKey::S,
407 Self::Resolution(..) => SortKey::Dppx,
408 Self::Angle(..) => SortKey::Deg,
409 Self::Length(ref l) => l.sort_key(),
410 Self::ColorComponent(..) => SortKey::ColorComponent,
411 }
412 }
413
414 fn simplify(&mut self) {
415 match self {
416 Leaf::Length(ref mut l) => {
417 if let Some(px) = l.to_px_if_absolute() {
418 *l = NoCalcLength::from_px(px);
419 }
420 },
421 Leaf::Resolution(ref mut r) => *r = NoCalcResolution::from_dppx(r.dppx()),
422 Leaf::Time(ref mut t) => *t = NoCalcTime::from_seconds(t.seconds()),
423 Leaf::Angle(ref mut a) => *a = NoCalcAngle::from_degrees(a.degrees()),
424 _ => (),
425 }
426 }
427
428 fn try_sum_in_place(&mut self, other: &Self) -> Result<(), ()> {
433 use self::Leaf::*;
434
435 if std::mem::discriminant(self) != std::mem::discriminant(other) {
436 return Err(());
437 }
438
439 match (self, other) {
440 (&mut Number(ref mut one), &Number(ref other)) => {
441 *one = NoCalcNumber::new(one.value() + other.value());
442 },
443 (&mut Percentage(ref mut one), &Percentage(ref other)) => {
444 *one = NoCalcPercentage::new(one.get() + other.get());
445 },
446 (&mut Angle(ref mut one), &Angle(ref other)) => {
447 *one = NoCalcAngle::from_degrees(one.degrees() + other.degrees());
448 },
449 (&mut Time(ref mut one), &Time(ref other)) => {
450 *one = NoCalcTime::from_seconds(one.seconds() + other.seconds());
451 },
452 (&mut Resolution(ref mut one), &Resolution(ref other)) => {
453 *one = NoCalcResolution::from_dppx(one.dppx() + other.dppx());
454 },
455 (&mut Length(ref mut one), &Length(ref other)) => {
456 *one = one.try_op(other, std::ops::Add::add)?;
457 },
458 (&mut ColorComponent(_), &ColorComponent(_)) => {
459 return Err(());
461 },
462 _ => {
463 match *other {
464 Number(..) | Percentage(..) | Angle(..) | Time(..) | Resolution(..)
465 | Length(..) | ColorComponent(..) => {},
466 }
467 unsafe {
468 debug_unreachable!();
469 }
470 },
471 }
472
473 Ok(())
474 }
475
476 fn try_product_in_place(&mut self, other: &mut Self) -> bool {
477 if let Self::Number(ref mut left) = *self {
478 if let Self::Number(ref right) = *other {
479 *left = NoCalcNumber::new(left.value() * right.value());
481 true
482 } else {
483 let left_val = left.value();
486 if other.map(|v| v * left_val).is_ok() {
487 std::mem::swap(self, other);
488 true
489 } else {
490 false
491 }
492 }
493 } else if let Self::Number(ref right) = *other {
494 let right_val = right.value();
497 self.map(|v| v * right_val).is_ok()
498 } else {
499 false
501 }
502 }
503
504 fn try_op<O>(&self, other: &Self, op: O) -> Result<Self, ()>
505 where
506 O: Fn(f32, f32) -> f32,
507 {
508 use self::Leaf::*;
509
510 if std::mem::discriminant(self) != std::mem::discriminant(other) {
511 return Err(());
512 }
513
514 match (self, other) {
515 (&Number(one), &Number(other)) => {
516 return Ok(Leaf::Number(NoCalcNumber::new(op(
517 one.value(),
518 other.value(),
519 ))));
520 },
521 (&Percentage(one), &Percentage(other)) => {
522 return Ok(Leaf::Percentage(NoCalcPercentage::new(op(
523 one.get(),
524 other.get(),
525 ))));
526 },
527 (&Angle(ref one), &Angle(ref other)) => {
528 return Ok(Leaf::Angle(NoCalcAngle::from_degrees(op(
529 one.degrees(),
530 other.degrees(),
531 ))));
532 },
533 (&Resolution(ref one), &Resolution(ref other)) => {
534 return Ok(Leaf::Resolution(NoCalcResolution::from_dppx(op(
535 one.dppx(),
536 other.dppx(),
537 ))));
538 },
539 (&Time(ref one), &Time(ref other)) => {
540 return Ok(Leaf::Time(NoCalcTime::from_seconds(op(
541 one.seconds(),
542 other.seconds(),
543 ))));
544 },
545 (&Length(ref one), &Length(ref other)) => {
546 return Ok(Leaf::Length(one.try_op(other, op)?));
547 },
548 (&ColorComponent(..), &ColorComponent(..)) => {
549 return Err(());
550 },
551 _ => {
552 match *other {
553 Number(..) | Percentage(..) | Angle(..) | Time(..) | Length(..)
554 | Resolution(..) | ColorComponent(..) => {},
555 }
556 unsafe {
557 debug_unreachable!();
558 }
559 },
560 }
561 }
562
563 fn map(&mut self, mut op: impl FnMut(f32) -> f32) -> Result<(), ()> {
564 Ok(match self {
565 Leaf::Length(one) => *one = one.map(op),
566 Leaf::Angle(one) => *one = NoCalcAngle::from_degrees(op(one.degrees())),
567 Leaf::Time(one) => *one = NoCalcTime::from_seconds(op(one.seconds())),
568 Leaf::Resolution(one) => *one = NoCalcResolution::from_dppx(op(one.dppx())),
569 Leaf::Percentage(one) => *one = NoCalcPercentage::new(op(one.get())),
570 Leaf::Number(one) => *one = NoCalcNumber::new(op(one.value())),
571 Leaf::ColorComponent(..) => return Err(()),
572 })
573 }
574}
575
576impl GenericAnchorSide<Box<CalcNode>> {
577 fn parse_in_calc<'i, 't>(
578 context: &ParserContext,
579 input: &mut Parser<'i, 't>,
580 ) -> Result<Self, ParseError<'i>> {
581 if let Ok(k) = input.try_parse(|i| AnchorSideKeyword::parse(i)) {
582 return Ok(Self::Keyword(k));
583 }
584 Ok(Self::Percentage(Box::new(CalcNode::parse_argument(
585 context,
586 input,
587 AllowParse::new(CalcUnits::PERCENTAGE),
588 )?)))
589 }
590}
591
592fn parse_anchor_function_fallback<'i, 't>(
593 context: &ParserContext,
594 additional_functions: AdditionalFunctions,
595 input: &mut Parser<'i, 't>,
596) -> Result<Box<GenericAnchorFunctionFallback<Leaf>>, ParseError<'i>> {
597 if let Ok(l) = input.try_parse(|i| -> Result<CalcNode, ParseError<'i>> {
598 Ok(CalcNode::Leaf(match i.next()? {
599 &Token::Number { value, .. } => {
600 if value != 0.0 {
601 return Err(i.new_custom_error(StyleParseErrorKind::UnspecifiedError));
602 }
603 Leaf::Length(NoCalcLength::from_px(0.0))
604 },
605 &Token::Dimension {
606 value, ref unit, ..
607 } => Leaf::Length(
608 NoCalcLength::parse_dimension_with_context(context, value, unit)
609 .map_err(|_| i.new_custom_error(StyleParseErrorKind::UnspecifiedError))?,
610 ),
611 &Token::Percentage { unit_value, .. } => {
612 Leaf::Percentage(NoCalcPercentage::new(unit_value))
613 },
614 _ => return Err(i.new_custom_error(StyleParseErrorKind::UnspecifiedError)),
615 }))
616 }) {
617 return Ok(Box::new(GenericAnchorFunctionFallback::new(false, l)));
618 }
619 let node = CalcNode::parse_argument(
620 context,
621 input,
622 AllowParse {
623 units: CalcUnits::LENGTH_PERCENTAGE,
624 color_components: false,
625 additional_functions,
626 },
627 )?
628 .into_length_or_percentage(AllowedNumericType::All)
629 .map_err(|_| input.new_custom_error(StyleParseErrorKind::UnspecifiedError))?
630 .0
631 .node;
632 Ok(Box::new(GenericAnchorFunctionFallback::new(true, node)))
633}
634
635impl GenericAnchorFunction<Box<CalcNode>, Box<GenericAnchorFunctionFallback<Leaf>>> {
636 fn parse_in_calc<'i, 't>(
637 context: &ParserContext,
638 additional_functions: AdditionalFunctions,
639 input: &mut Parser<'i, 't>,
640 ) -> Result<Self, ParseError<'i>> {
641 if !static_prefs::pref!("layout.css.anchor-positioning.enabled") {
642 return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));
643 }
644 input.parse_nested_block(|i| {
645 let target_element = i.try_parse(|i| DashedIdent::parse(context, i)).ok();
646 let side = GenericAnchorSide::parse_in_calc(context, i)?;
647 let target_element = if target_element.is_none() {
648 i.try_parse(|i| DashedIdent::parse(context, i)).ok()
649 } else {
650 target_element
651 };
652 let fallback = i
653 .try_parse(|i| {
654 i.expect_comma()?;
655 parse_anchor_function_fallback(context, additional_functions, i)
656 })
657 .ok();
658 Ok(Self {
659 target_element: TreeScoped::with_default_level(
660 target_element.unwrap_or_else(DashedIdent::empty),
661 ),
662 side,
663 fallback: fallback.into(),
664 })
665 })
666 }
667}
668
669impl GenericAnchorSizeFunction<Box<GenericAnchorFunctionFallback<Leaf>>> {
670 fn parse_in_calc<'i, 't>(
671 context: &ParserContext,
672 input: &mut Parser<'i, 't>,
673 ) -> Result<Self, ParseError<'i>> {
674 if !static_prefs::pref!("layout.css.anchor-positioning.enabled") {
675 return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));
676 }
677 GenericAnchorSizeFunction::parse_inner(context, input, |i| {
678 parse_anchor_function_fallback(context, AdditionalFunctions::ANCHOR_SIZE, i)
679 })
680 }
681}
682
683pub type CalcAnchorFunction = generic::GenericCalcAnchorFunction<Leaf>;
685pub type CalcAnchorSizeFunction = generic::GenericCalcAnchorSizeFunction<Leaf>;
687
688pub type CalcNode = generic::GenericCalcNode<Leaf>;
690impl CalcNode {
691 fn parse_one<'i, 't>(
697 context: &ParserContext,
698 input: &mut Parser<'i, 't>,
699 allowed: AllowParse,
700 ) -> Result<Self, ParseError<'i>> {
701 let location = input.current_source_location();
702 match input.next()? {
703 &Token::Number { value, .. } => {
704 Ok(CalcNode::Leaf(Leaf::Number(NoCalcNumber::new(value))))
705 },
706 &Token::Dimension {
707 value, ref unit, ..
708 } => {
709 if allowed.includes(CalcUnits::LENGTH) {
710 if let Ok(l) = NoCalcLength::parse_dimension_with_context(context, value, unit)
711 {
712 return Ok(CalcNode::Leaf(Leaf::Length(l)));
713 }
714 }
715 if allowed.includes(CalcUnits::ANGLE) {
716 if let Ok(a) = NoCalcAngle::parse_dimension(value, unit) {
717 return Ok(CalcNode::Leaf(Leaf::Angle(a)));
718 }
719 }
720 if allowed.includes(CalcUnits::TIME) {
721 if let Ok(t) = NoCalcTime::parse_dimension(value, unit) {
722 return Ok(CalcNode::Leaf(Leaf::Time(t)));
723 }
724 }
725 if allowed.includes(CalcUnits::RESOLUTION) {
726 if let Ok(t) = NoCalcResolution::parse_dimension(value, unit) {
727 return Ok(CalcNode::Leaf(Leaf::Resolution(t)));
728 }
729 }
730 return Err(location.new_custom_error(StyleParseErrorKind::UnspecifiedError));
731 },
732 &Token::Percentage { unit_value, .. } if allowed.includes(CalcUnits::PERCENTAGE) => Ok(
733 CalcNode::Leaf(Leaf::Percentage(NoCalcPercentage::new(unit_value))),
734 ),
735 &Token::ParenthesisBlock => {
736 input.parse_nested_block(|input| CalcNode::parse_argument(context, input, allowed))
737 },
738 &Token::Function(ref name)
739 if allowed
740 .additional_functions
741 .intersects(AdditionalFunctions::ANCHOR)
742 && name.eq_ignore_ascii_case("anchor") =>
743 {
744 let anchor_function = GenericAnchorFunction::parse_in_calc(
745 context,
746 allowed.additional_functions,
747 input,
748 )?;
749 Ok(CalcNode::Anchor(Box::new(anchor_function)))
750 },
751 &Token::Function(ref name)
752 if allowed
753 .additional_functions
754 .intersects(AdditionalFunctions::ANCHOR_SIZE)
755 && name.eq_ignore_ascii_case("anchor-size") =>
756 {
757 let anchor_size_function =
758 GenericAnchorSizeFunction::parse_in_calc(context, input)?;
759 Ok(CalcNode::AnchorSize(Box::new(anchor_size_function)))
760 },
761 &Token::Function(ref name) => {
762 let function = CalcNode::math_function(context, name, location)?;
763 CalcNode::parse(context, input, function, allowed)
764 },
765 &Token::Ident(ref ident) => {
766 let leaf = match_ignore_ascii_case! { &**ident,
767 "e" => Leaf::Number(NoCalcNumber::new(std::f32::consts::E)),
768 "pi" => Leaf::Number(NoCalcNumber::new(std::f32::consts::PI)),
769 "infinity" => Leaf::Number(NoCalcNumber::new(f32::INFINITY)),
770 "-infinity" => Leaf::Number(NoCalcNumber::new(f32::NEG_INFINITY)),
771 "nan" => Leaf::Number(NoCalcNumber::new(f32::NAN)),
772 _ => {
773 if !allowed.color_components {
774 return Err(
775 location.new_unexpected_token_error(Token::Ident(ident.clone()))
776 );
777 }
778 if let Ok(channel_keyword) = ChannelKeyword::from_ident(&ident) {
779 Leaf::ColorComponent(channel_keyword)
780 } else {
781 return Err(location
782 .new_unexpected_token_error(Token::Ident(ident.clone())));
783 }
784 },
785 };
786 Ok(CalcNode::Leaf(leaf))
787 },
788 t => Err(location.new_unexpected_token_error(t.clone())),
789 }
790 }
791
792 pub fn parse<'i, 't>(
796 context: &ParserContext,
797 input: &mut Parser<'i, 't>,
798 function: MathFunction,
799 allowed: AllowParse,
800 ) -> Result<Self, ParseError<'i>> {
801 input.parse_nested_block(|input| {
802 match function {
803 MathFunction::Calc => Self::parse_argument(context, input, allowed),
804 MathFunction::Clamp => {
805 let min_val = if input
806 .try_parse(|min| min.expect_ident_matching("none"))
807 .ok()
808 .is_none()
809 {
810 Some(Self::parse_argument(context, input, allowed)?)
811 } else {
812 None
813 };
814
815 input.expect_comma()?;
816 let center = Self::parse_argument(context, input, allowed)?;
817 input.expect_comma()?;
818
819 let max_val = if input
820 .try_parse(|max| max.expect_ident_matching("none"))
821 .ok()
822 .is_none()
823 {
824 Some(Self::parse_argument(context, input, allowed)?)
825 } else {
826 None
827 };
828
829 Ok(match (min_val, max_val) {
836 (None, None) => center,
837 (None, Some(max)) => Self::MinMax(vec![center, max].into(), MinMaxOp::Min),
838 (Some(min), None) => Self::MinMax(vec![min, center].into(), MinMaxOp::Max),
839 (Some(min), Some(max)) => Self::Clamp {
840 min: Box::new(min),
841 center: Box::new(center),
842 max: Box::new(max),
843 },
844 })
845 },
846 MathFunction::Round => {
847 let strategy = input.try_parse(parse_rounding_strategy);
848
849 fn parse_rounding_strategy<'i, 't>(
852 input: &mut Parser<'i, 't>,
853 ) -> Result<RoundingStrategy, ParseError<'i>> {
854 Ok(try_match_ident_ignore_ascii_case! { input,
855 "nearest" => RoundingStrategy::Nearest,
856 "up" => RoundingStrategy::Up,
857 "down" => RoundingStrategy::Down,
858 "to-zero" => RoundingStrategy::ToZero,
859 })
860 }
861
862 if strategy.is_ok() {
863 input.expect_comma()?;
864 }
865
866 let value = Self::parse_argument(context, input, allowed)?;
867
868 let step = input.try_parse(|input| {
871 input.expect_comma()?;
872 Self::parse_argument(context, input, allowed)
873 });
874
875 let step = step.unwrap_or(Self::Leaf(Leaf::Number(NoCalcNumber::new(1.0))));
876
877 Ok(Self::Round {
878 strategy: strategy.unwrap_or(RoundingStrategy::Nearest),
879 value: Box::new(value),
880 step: Box::new(step),
881 })
882 },
883 MathFunction::Mod | MathFunction::Rem => {
884 let dividend = Self::parse_argument(context, input, allowed)?;
885 input.expect_comma()?;
886 let divisor = Self::parse_argument(context, input, allowed)?;
887
888 let op = match function {
889 MathFunction::Mod => ModRemOp::Mod,
890 MathFunction::Rem => ModRemOp::Rem,
891 _ => unreachable!(),
892 };
893 Ok(Self::ModRem {
894 dividend: Box::new(dividend),
895 divisor: Box::new(divisor),
896 op,
897 })
898 },
899 MathFunction::Min | MathFunction::Max => {
900 let arguments = input.parse_comma_separated(|input| {
906 let result = Self::parse_argument(context, input, allowed)?;
907 Ok(result)
908 })?;
909
910 let op = match function {
911 MathFunction::Min => MinMaxOp::Min,
912 MathFunction::Max => MinMaxOp::Max,
913 _ => unreachable!(),
914 };
915
916 Ok(Self::MinMax(arguments.into(), op))
917 },
918 MathFunction::Sin | MathFunction::Cos | MathFunction::Tan => {
919 let node = Self::parse_argument(
920 context,
921 input,
922 allowed.new_including(CalcUnits::ANGLE),
923 )?;
924 Ok(match function {
925 MathFunction::Sin => Self::Sin(Box::new(node)),
926 MathFunction::Cos => Self::Cos(Box::new(node)),
927 MathFunction::Tan => Self::Tan(Box::new(node)),
928 _ => unsafe { debug_unreachable!("We just checked!") },
929 })
930 },
931 MathFunction::Asin | MathFunction::Acos | MathFunction::Atan => {
932 let node = Self::parse_argument(context, input, allowed)?;
933 Ok(match function {
934 MathFunction::Asin => Self::Asin(Box::new(node)),
935 MathFunction::Acos => Self::Acos(Box::new(node)),
936 MathFunction::Atan => Self::Atan(Box::new(node)),
937 _ => unsafe { debug_unreachable!("We just checked!") },
938 })
939 },
940 MathFunction::Atan2 => {
941 let allow_all = allowed.new_including(CalcUnits::ALL);
942 let a = Self::parse_argument(context, input, allow_all)?;
943 input.expect_comma()?;
944 let b = Self::parse_argument(context, input, allow_all)?;
945 if a.unit() != b.unit() {
947 return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));
948 }
949 Ok(Self::Atan2(Box::new(a), Box::new(b)))
950 },
951 MathFunction::Pow => {
952 let a = Self::parse_argument(context, input, allowed)?;
953 input.expect_comma()?;
954 let b = Self::parse_argument(context, input, allowed)?;
955 Ok(Self::Pow(Box::new(a), Box::new(b)))
956 },
957 MathFunction::Sqrt => {
958 let a = Self::parse_argument(context, input, allowed)?;
959 Ok(Self::Sqrt(Box::new(a)))
960 },
961 MathFunction::Hypot => {
962 let arguments = input.parse_comma_separated(|input| {
963 let result = Self::parse_argument(context, input, allowed)?;
964 Ok(result)
965 })?;
966
967 Ok(Self::Hypot(arguments.into()))
968 },
969 MathFunction::Log => {
970 let a = Self::parse_argument(context, input, allowed)?;
971 let b = input
972 .try_parse(|input| {
973 input.expect_comma()?;
974 Self::parse_argument(context, input, allowed)
975 })
976 .ok();
977 Ok(Self::Log(Box::new(a), b.map(Box::new).into()))
978 },
979 MathFunction::Exp => {
980 let a = Self::parse_argument(context, input, allowed)?;
981 Ok(Self::Exp(Box::new(a)))
982 },
983 MathFunction::Abs => {
984 let node = Self::parse_argument(context, input, allowed)?;
985 Ok(Self::Abs(Box::new(node)))
986 },
987 MathFunction::Sign => {
988 let node = Self::parse_argument(
992 context,
993 input,
994 allowed.new_including(CalcUnits::ALL - CalcUnits::PERCENTAGE),
995 )?;
996 Ok(Self::Sign(Box::new(node)))
997 },
998 }
999 })
1000 }
1001
1002 fn parse_argument<'i, 't>(
1003 context: &ParserContext,
1004 input: &mut Parser<'i, 't>,
1005 allowed: AllowParse,
1006 ) -> Result<Self, ParseError<'i>> {
1007 let mut sum = SmallVec::<[CalcNode; 1]>::new();
1008 let first = Self::parse_product(context, input, allowed)?;
1009 sum.push(first);
1010 loop {
1011 let start = input.state();
1012 match input.next_including_whitespace() {
1013 Ok(&Token::WhiteSpace(_)) => {
1014 if input.is_exhausted() {
1015 break; }
1017 match *input.next()? {
1018 Token::Delim('+') => {
1019 let rhs = Self::parse_product(context, input, allowed)?;
1020 if sum.last_mut().unwrap().try_sum_in_place(&rhs).is_err() {
1021 sum.push(rhs);
1022 }
1023 },
1024 Token::Delim('-') => {
1025 let mut rhs = Self::parse_product(context, input, allowed)?;
1026 rhs.negate();
1027 if sum.last_mut().unwrap().try_sum_in_place(&rhs).is_err() {
1028 sum.push(rhs);
1029 }
1030 },
1031 _ => {
1032 input.reset(&start);
1033 break;
1034 },
1035 }
1036 },
1037 _ => {
1038 input.reset(&start);
1039 break;
1040 },
1041 }
1042 }
1043
1044 Ok(if sum.len() == 1 {
1045 sum.drain(..).next().unwrap()
1046 } else {
1047 Self::Sum(sum.into_boxed_slice().into())
1048 })
1049 }
1050
1051 fn parse_product<'i, 't>(
1061 context: &ParserContext,
1062 input: &mut Parser<'i, 't>,
1063 allowed: AllowParse,
1064 ) -> Result<Self, ParseError<'i>> {
1065 let mut product = SmallVec::<[CalcNode; 1]>::new();
1066 let first = Self::parse_one(context, input, allowed)?;
1067 product.push(first);
1068
1069 loop {
1070 let start = input.state();
1071 match input.next() {
1072 Ok(&Token::Delim('*')) => {
1073 let mut rhs = Self::parse_one(context, input, allowed)?;
1074
1075 if !product.last_mut().unwrap().try_product_in_place(&mut rhs) {
1078 product.push(rhs);
1079 }
1080 },
1081 Ok(&Token::Delim('/')) => {
1082 let rhs = Self::parse_one(context, input, allowed)?;
1083
1084 enum InPlaceDivisionResult {
1085 Merged,
1087 Unchanged,
1090 Invalid,
1093 }
1094
1095 fn try_division_in_place(
1096 left: &mut CalcNode,
1097 right: &CalcNode,
1098 ) -> InPlaceDivisionResult {
1099 if let Ok(resolved) = right.resolve() {
1100 if let Some(number) = resolved.as_number() {
1101 if number != 1.0 && left.is_product_distributive() {
1102 if left.map(|l| l / number).is_err() {
1103 return InPlaceDivisionResult::Invalid;
1104 }
1105 return InPlaceDivisionResult::Merged;
1106 }
1107 } else {
1108 return if resolved.unit().is_empty() {
1111 InPlaceDivisionResult::Unchanged
1112 } else {
1113 InPlaceDivisionResult::Invalid
1114 };
1115 }
1116 }
1117 InPlaceDivisionResult::Unchanged
1118 }
1119
1120 match try_division_in_place(&mut product.last_mut().unwrap(), &rhs) {
1125 InPlaceDivisionResult::Merged => {},
1126 InPlaceDivisionResult::Unchanged => {
1127 product.push(Self::Invert(Box::new(rhs)))
1128 },
1129 InPlaceDivisionResult::Invalid => {
1130 return Err(
1131 input.new_custom_error(StyleParseErrorKind::UnspecifiedError)
1132 )
1133 },
1134 }
1135 },
1136 _ => {
1137 input.reset(&start);
1138 break;
1139 },
1140 }
1141 }
1142
1143 Ok(if product.len() == 1 {
1144 product.drain(..).next().unwrap()
1145 } else {
1146 Self::Product(product.into_boxed_slice().into())
1147 })
1148 }
1149
1150 pub fn resolve_computed<F>(
1155 &self,
1156 context: Option<&computed::Context>,
1157 leaf_to_output_fn: F,
1158 ) -> Result<Leaf, ()>
1159 where
1160 F: Fn(&Leaf) -> Result<Leaf, ()>,
1161 {
1162 self.resolve_map(|leaf| {
1164 Ok(match leaf {
1165 Leaf::Length(length) => Leaf::Length(NoCalcLength::from_px(match context {
1166 Some(ctx) => length.to_computed_value(ctx).px(),
1167 None => length.to_computed_pixel_length_without_context()?,
1168 })),
1169 _ => leaf_to_output_fn(leaf)?,
1170 })
1171 })
1172 }
1173
1174 pub fn into_length_or_percentage(
1177 mut self,
1178 clamping_mode: AllowedNumericType,
1179 ) -> Result<CalcLengthPercentage, ()> {
1180 self.simplify_and_sort();
1181
1182 let unit = self.unit()?;
1185 if !CalcUnits::LENGTH_PERCENTAGE.intersects(unit) {
1186 Err(())
1187 } else {
1188 Ok(CalcLengthPercentage(CalcNumeric {
1189 clamping_mode,
1190 node: self,
1191 }))
1192 }
1193 }
1194
1195 fn into_time(mut self, clamping_mode: AllowedNumericType) -> Result<CalcNumeric, ()> {
1197 self.simplify_and_sort();
1198
1199 let unit: CalcUnits = self.unit()?;
1200 if !CalcUnits::TIME.intersects(unit) {
1201 Err(())
1202 } else {
1203 Ok(CalcNumeric {
1204 clamping_mode,
1205 node: self,
1206 })
1207 }
1208 }
1209
1210 fn into_resolution(mut self) -> Result<CalcNumeric, ()> {
1212 self.simplify_and_sort();
1213
1214 let unit: CalcUnits = self.unit()?;
1215 if !CalcUnits::RESOLUTION.intersects(unit) {
1216 Err(())
1217 } else {
1218 Ok(CalcNumeric {
1219 clamping_mode: AllowedNumericType::NonNegative,
1220 node: self,
1221 })
1222 }
1223 }
1224
1225 fn into_angle(mut self, clamping_mode: AllowedNumericType) -> Result<CalcNumeric, ()> {
1227 self.simplify_and_sort();
1228
1229 let unit: CalcUnits = self.unit()?;
1230 if !CalcUnits::ANGLE.intersects(unit) {
1231 Err(())
1232 } else {
1233 Ok(CalcNumeric {
1234 clamping_mode,
1235 node: self,
1236 })
1237 }
1238 }
1239
1240 fn into_number(mut self, clamping_mode: AllowedNumericType) -> Result<CalcNumeric, ()> {
1243 self.simplify_and_sort();
1244
1245 let unit: CalcUnits = self.unit()?;
1246 if !unit.is_empty() {
1247 Err(())
1248 } else {
1249 Ok(CalcNumeric {
1250 clamping_mode,
1251 node: self,
1252 })
1253 }
1254 }
1255
1256 fn into_percentage(mut self, clamping_mode: AllowedNumericType) -> Result<CalcNumeric, ()> {
1259 self.simplify_and_sort();
1260
1261 let unit: CalcUnits = self.unit()?;
1262 if !CalcUnits::PERCENTAGE.intersects(unit) {
1263 Err(())
1264 } else {
1265 Ok(CalcNumeric {
1266 clamping_mode,
1267 node: self,
1268 })
1269 }
1270 }
1271
1272 #[inline]
1275 pub fn math_function<'i>(
1276 _: &ParserContext,
1277 name: &CowRcStr<'i>,
1278 location: cssparser::SourceLocation,
1279 ) -> Result<MathFunction, ParseError<'i>> {
1280 let function = match MathFunction::from_ident(&*name) {
1281 Ok(f) => f,
1282 Err(()) => {
1283 return Err(location.new_unexpected_token_error(Token::Function(name.clone())))
1284 },
1285 };
1286
1287 Ok(function)
1288 }
1289
1290 pub fn parse_length_or_percentage<'i, 't>(
1292 context: &ParserContext,
1293 input: &mut Parser<'i, 't>,
1294 clamping_mode: AllowedNumericType,
1295 function: MathFunction,
1296 allow_anchor: AllowAnchorPositioningFunctions,
1297 ) -> Result<CalcLengthPercentage, ParseError<'i>> {
1298 let allowed = if allow_anchor == AllowAnchorPositioningFunctions::No {
1299 AllowParse::new(CalcUnits::LENGTH_PERCENTAGE)
1300 } else {
1301 AllowParse {
1302 units: CalcUnits::LENGTH_PERCENTAGE,
1303 color_components: false,
1304 additional_functions: match allow_anchor {
1305 AllowAnchorPositioningFunctions::No => unreachable!(),
1306 AllowAnchorPositioningFunctions::AllowAnchorSize => {
1307 AdditionalFunctions::ANCHOR_SIZE
1308 },
1309 AllowAnchorPositioningFunctions::AllowAnchorAndAnchorSize => {
1310 AdditionalFunctions::ANCHOR | AdditionalFunctions::ANCHOR_SIZE
1311 },
1312 },
1313 }
1314 };
1315 Self::parse(context, input, function, allowed)?
1316 .into_length_or_percentage(clamping_mode)
1317 .map_err(|()| input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
1318 }
1319
1320 pub fn parse_percentage<'i, 't>(
1322 context: &ParserContext,
1323 input: &mut Parser<'i, 't>,
1324 clamping_mode: AllowedNumericType,
1325 function: MathFunction,
1326 ) -> Result<CalcNumeric, ParseError<'i>> {
1327 Self::parse(
1328 context,
1329 input,
1330 function,
1331 AllowParse::new(CalcUnits::PERCENTAGE),
1332 )?
1333 .into_percentage(clamping_mode)
1334 .map_err(|()| input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
1335 }
1336
1337 pub fn parse_length<'i, 't>(
1339 context: &ParserContext,
1340 input: &mut Parser<'i, 't>,
1341 clamping_mode: AllowedNumericType,
1342 function: MathFunction,
1343 ) -> Result<CalcLengthPercentage, ParseError<'i>> {
1344 Self::parse(context, input, function, AllowParse::new(CalcUnits::LENGTH))?
1345 .into_length_or_percentage(clamping_mode)
1346 .map_err(|()| input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
1347 }
1348
1349 pub fn parse_number<'i, 't>(
1351 context: &ParserContext,
1352 input: &mut Parser<'i, 't>,
1353 clamping_mode: AllowedNumericType,
1354 function: MathFunction,
1355 ) -> Result<CalcNumeric, ParseError<'i>> {
1356 Self::parse(
1357 context,
1358 input,
1359 function,
1360 AllowParse::new(CalcUnits::empty()),
1361 )?
1362 .into_number(clamping_mode)
1363 .map_err(|()| input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
1364 }
1365
1366 pub fn parse_angle<'i, 't>(
1368 context: &ParserContext,
1369 input: &mut Parser<'i, 't>,
1370 function: MathFunction,
1371 ) -> Result<CalcNumeric, ParseError<'i>> {
1372 Self::parse(context, input, function, AllowParse::new(CalcUnits::ANGLE))?
1373 .into_angle(AllowedNumericType::All)
1374 .map_err(|()| input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
1375 }
1376
1377 pub fn parse_time<'i, 't>(
1379 context: &ParserContext,
1380 input: &mut Parser<'i, 't>,
1381 clamping_mode: AllowedNumericType,
1382 function: MathFunction,
1383 ) -> Result<CalcNumeric, ParseError<'i>> {
1384 Self::parse(context, input, function, AllowParse::new(CalcUnits::TIME))?
1385 .into_time(clamping_mode)
1386 .map_err(|()| input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
1387 }
1388
1389 pub fn parse_resolution<'i, 't>(
1391 context: &ParserContext,
1392 input: &mut Parser<'i, 't>,
1393 function: MathFunction,
1394 ) -> Result<CalcNumeric, ParseError<'i>> {
1395 Self::parse(
1396 context,
1397 input,
1398 function,
1399 AllowParse::new(CalcUnits::RESOLUTION),
1400 )?
1401 .into_resolution()
1402 .map_err(|()| input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
1403 }
1404}