diff options
author | Emilio Cobos Álvarez <emilio@crisal.io> | 2023-05-27 07:12:22 +0200 |
---|---|---|
committer | Oriol Brufau <obrufau@igalia.com> | 2023-05-30 23:26:01 +0200 |
commit | 4522e7f94a05e15546ffd2bc1559fa0634e22f3d (patch) | |
tree | 6b8b73ae4abb99bfa05ede4cc01037b694e7cac0 | |
parent | 4f193fbf4921cd48d7a00df0b3b734bb7090c4a4 (diff) | |
download | servo-4522e7f94a05e15546ffd2bc1559fa0634e22f3d.tar.gz servo-4522e7f94a05e15546ffd2bc1559fa0634e22f3d.zip |
style: Add experimental support for "e", "pi", and various trigonometric functions in calc()
I'll add some tests before enabling. Also, WebKit folks (who have
implemented cos() / tan() / sin()) said they will upstream their tests
to WPT, so I'll extend those with the inverse functions before landing
as well.
Differential Revision: https://phabricator.services.mozilla.com/D124990
-rw-r--r-- | components/style/values/specified/angle.rs | 16 | ||||
-rw-r--r-- | components/style/values/specified/calc.rs | 85 |
2 files changed, 93 insertions, 8 deletions
diff --git a/components/style/values/specified/angle.rs b/components/style/values/specified/angle.rs index fa60f66507d..08c6b443678 100644 --- a/components/style/values/specified/angle.rs +++ b/components/style/values/specified/angle.rs @@ -130,6 +130,15 @@ impl Angle { } } + /// Creates an angle with the given value in radians. + #[inline] + pub fn from_radians(value: CSSFloat) -> Self { + Angle { + value: AngleDimension::Rad(value), + was_calc: false, + } + } + /// Return `0deg`. pub fn zero() -> Self { Self::from_degrees(0.0, false) @@ -141,6 +150,13 @@ impl Angle { self.value.degrees() } + /// Returns the value of the angle in radians. + #[inline] + pub fn radians(&self) -> CSSFloat { + const RAD_PER_DEG: f32 = PI / 180.0; + self.value.degrees() * RAD_PER_DEG + } + /// Whether this specified angle came from a `calc()` expression. #[inline] pub fn was_calc(&self) -> bool { diff --git a/components/style/values/specified/calc.rs b/components/style/values/specified/calc.rs index a22d6fd5f18..39167a13647 100644 --- a/components/style/values/specified/calc.rs +++ b/components/style/values/specified/calc.rs @@ -21,7 +21,7 @@ use style_traits::values::specified::AllowedNumericType; use style_traits::{CssWriter, ParseError, SpecifiedValueInfo, StyleParseErrorKind, ToCss}; /// The name of the mathematical function that we're parsing. -#[derive(Clone, Copy, Debug)] +#[derive(Clone, Copy, Debug, Parse)] pub enum MathFunction { /// `calc()`: https://drafts.csswg.org/css-values-4/#funcdef-calc Calc, @@ -31,6 +31,18 @@ pub enum MathFunction { Max, /// `clamp()`: https://drafts.csswg.org/css-values-4/#funcdef-clamp Clamp, + /// `sin()`: https://drafts.csswg.org/css-values-4/#funcdef-sin + Sin, + /// `cos()`: https://drafts.csswg.org/css-values-4/#funcdef-cos + Cos, + /// `tan()`: https://drafts.csswg.org/css-values-4/#funcdef-tan + Tan, + /// `asin()`: https://drafts.csswg.org/css-values-4/#funcdef-asin + Asin, + /// `acos()`: https://drafts.csswg.org/css-values-4/#funcdef-acos + Acos, + /// `atan()`: https://drafts.csswg.org/css-values-4/#funcdef-atan + Atan, } /// A leaf node inside a `Calc` expression's AST. @@ -301,6 +313,17 @@ impl CalcNode { let function = CalcNode::math_function(name, location)?; CalcNode::parse(context, input, function, expected_unit) }, + (&Token::Ident(ref ident), _) => { + if !static_prefs::pref!("layout.css.trig.enabled") { + return Err(location.new_unexpected_token_error(Token::Ident(ident.clone()))); + } + let number = match_ignore_ascii_case! { &**ident, + "e" => std::f32::consts::E, + "pi" => std::f32::consts::PI, + _ => return Err(location.new_unexpected_token_error(Token::Ident(ident.clone()))), + }; + Ok(CalcNode::Leaf(Leaf::Number(number))) + }, (t, _) => Err(location.new_unexpected_token_error(t.clone())), } } @@ -350,6 +373,47 @@ impl CalcNode { Ok(Self::MinMax(arguments.into(), op)) }, + MathFunction::Sin | + MathFunction::Cos | + MathFunction::Tan => { + let argument = Self::parse_argument(context, input, CalcUnit::Angle)?; + let radians = match argument.to_number() { + Ok(v) => v, + Err(()) => match argument.to_angle() { + Ok(angle) => angle.radians(), + Err(()) => return Err( + input.new_custom_error(StyleParseErrorKind::UnspecifiedError) + ), + }, + }; + let number = match function { + MathFunction::Sin => radians.sin(), + MathFunction::Cos => radians.cos(), + MathFunction::Tan => radians.tan(), + _ => unsafe { debug_unreachable!("We just checked!"); }, + }; + Ok(Self::Leaf(Leaf::Number(number))) + }, + MathFunction::Asin | + MathFunction::Acos | + MathFunction::Atan => { + let argument = Self::parse_argument(context, input, CalcUnit::Number)?; + let number = match argument.to_number() { + Ok(v) => v, + Err(()) => return Err( + input.new_custom_error(StyleParseErrorKind::UnspecifiedError) + ), + }; + + let radians = match function { + MathFunction::Asin => number.asin(), + MathFunction::Acos => number.acos(), + MathFunction::Atan => number.atan(), + _ => unsafe { debug_unreachable!("We just checked!"); }, + }; + + Ok(Self::Leaf(Leaf::Angle(Angle::from_radians(radians)))) + }, } }) } @@ -522,13 +586,18 @@ impl CalcNode { name: &CowRcStr<'i>, location: cssparser::SourceLocation, ) -> Result<MathFunction, ParseError<'i>> { - Ok(match_ignore_ascii_case! { &*name, - "calc" => MathFunction::Calc, - "min" => MathFunction::Min, - "max" => MathFunction::Max, - "clamp" => MathFunction::Clamp, - _ => return Err(location.new_unexpected_token_error(Token::Function(name.clone()))), - }) + use self::MathFunction::*; + + let function = match MathFunction::from_ident(&*name) { + Ok(f) => f, + Err(()) => return Err(location.new_unexpected_token_error(Token::Function(name.clone()))), + }; + + if matches!(function, Sin | Cos | Tan | Asin | Acos | Atan) && !static_prefs::pref!("layout.css.trig.enabled") { + return Err(location.new_unexpected_token_error(Token::Function(name.clone()))); + } + + Ok(function) } /// Convenience parsing function for integers. |