aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorEmilio Cobos Álvarez <emilio@crisal.io>2023-05-27 07:12:22 +0200
committerOriol Brufau <obrufau@igalia.com>2023-05-30 23:26:01 +0200
commit4522e7f94a05e15546ffd2bc1559fa0634e22f3d (patch)
tree6b8b73ae4abb99bfa05ede4cc01037b694e7cac0
parent4f193fbf4921cd48d7a00df0b3b734bb7090c4a4 (diff)
downloadservo-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.rs16
-rw-r--r--components/style/values/specified/calc.rs85
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.