/* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ //! [Calc expressions][calc]. //! //! [calc]: https://drafts.csswg.org/css-values/#calc-notation use num_traits::Zero; use smallvec::SmallVec; use std::fmt::{self, Write}; use std::ops::{Add, Mul, Neg, Rem, Sub}; use std::{cmp, mem}; use style_traits::{CssWriter, ToCss}; /// Whether we're a `min` or `max` function. #[derive( Clone, Copy, Debug, Deserialize, MallocSizeOf, PartialEq, Serialize, ToAnimatedZero, ToResolvedValue, ToShmem, )] #[repr(u8)] pub enum MinMaxOp { /// `min()` Min, /// `max()` Max, } /// Whether we're a `mod` or `rem` function. #[derive( Clone, Copy, Debug, Deserialize, MallocSizeOf, PartialEq, Serialize, ToAnimatedZero, ToResolvedValue, ToShmem, )] #[repr(u8)] pub enum ModRemOp { /// `mod()` Mod, /// `rem()` Rem, } impl ModRemOp { fn apply(self, dividend: f32, divisor: f32) -> f32 { // In mod(A, B) only, if B is infinite and A has opposite sign to B // (including an oppositely-signed zero), the result is NaN. // https://drafts.csswg.org/css-values/#round-infinities if matches!(self, Self::Mod) && divisor.is_infinite() && dividend.is_sign_negative() != divisor.is_sign_negative() { return f32::NAN; } let (r, same_sign_as) = match self { Self::Mod => (dividend - divisor * (dividend / divisor).floor(), divisor), Self::Rem => (dividend - divisor * (dividend / divisor).trunc(), dividend), }; if r == 0.0 && same_sign_as.is_sign_negative() { -0.0 } else { r } } } /// The strategy used in `round()` #[derive( Clone, Copy, Debug, Deserialize, MallocSizeOf, PartialEq, Serialize, ToAnimatedZero, ToResolvedValue, ToShmem, )] #[repr(u8)] pub enum RoundingStrategy { /// `round(nearest, a, b)` /// round a to the nearest multiple of b Nearest, /// `round(up, a, b)` /// round a up to the nearest multiple of b Up, /// `round(down, a, b)` /// round a down to the nearest multiple of b Down, /// `round(to-zero, a, b)` /// round a to the nearest multiple of b that is towards zero ToZero, } /// This determines the order in which we serialize members of a calc() sum. /// /// See https://drafts.csswg.org/css-values-4/#sort-a-calculations-children #[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd)] #[allow(missing_docs)] pub enum SortKey { Number, Percentage, Cap, Ch, Cqb, Cqh, Cqi, Cqmax, Cqmin, Cqw, Deg, Dppx, Dvb, Dvh, Dvi, Dvmax, Dvmin, Dvw, Em, Ex, Ic, Lh, Lvb, Lvh, Lvi, Lvmax, Lvmin, Lvw, Px, Rem, Rlh, Sec, Svb, Svh, Svi, Svmax, Svmin, Svw, Vb, Vh, Vi, Vmax, Vmin, Vw, Other, } /// A generic node in a calc expression. /// /// FIXME: This would be much more elegant if we used `Self` in the types below, /// but we can't because of https://github.com/serde-rs/serde/issues/1565. /// /// FIXME: The following annotations are to workaround an LLVM inlining bug, see /// bug 1631929. /// /// cbindgen:destructor-attributes=MOZ_NEVER_INLINE /// cbindgen:copy-constructor-attributes=MOZ_NEVER_INLINE /// cbindgen:eq-attributes=MOZ_NEVER_INLINE #[repr(u8)] #[derive( Clone, Debug, Deserialize, MallocSizeOf, PartialEq, Serialize, ToAnimatedZero, ToResolvedValue, ToShmem, )] pub enum GenericCalcNode { /// A leaf node. Leaf(L), /// A node that negates its child, e.g. Negate(1) == -1. Negate(Box>), /// A node that inverts its child, e.g. Invert(10) == 1 / 10 == 0.1. The child must always /// resolve to a number unit. Invert(Box>), /// A sum node, representing `a + b + c` where a, b, and c are the /// arguments. Sum(crate::OwnedSlice>), /// A product node, representing `a * b * c` where a, b, and c are the /// arguments. Product(crate::OwnedSlice>), /// A `min` or `max` function. MinMax(crate::OwnedSlice>, MinMaxOp), /// A `clamp()` function. Clamp { /// The minimum value. min: Box>, /// The central value. center: Box>, /// The maximum value. max: Box>, }, /// A `round()` function. Round { /// The rounding strategy. strategy: RoundingStrategy, /// The value to round. value: Box>, /// The step value. step: Box>, }, /// A `mod()` or `rem()` function. ModRem { /// The dividend calculation. dividend: Box>, /// The divisor calculation. divisor: Box>, /// Is the function mod or rem? op: ModRemOp, }, /// A `hypot()` function Hypot(crate::OwnedSlice>), /// An `abs()` function. Abs(Box>), /// A `sign()` function. Sign(Box>), } pub use self::GenericCalcNode as CalcNode; bitflags! { /// Expected units we allow parsing within a `calc()` expression. /// /// This is used as a hint for the parser to fast-reject invalid /// expressions. Numbers are always allowed because they multiply other /// units. #[derive(Clone, Copy, PartialEq, Eq)] pub struct CalcUnits: u8 { /// const LENGTH = 1 << 0; /// const PERCENTAGE = 1 << 1; /// const ANGLE = 1 << 2; ///