mirror of
https://github.com/servo/servo.git
synced 2025-06-27 18:43:40 +01:00
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
This commit is contained in:
parent
4f193fbf49
commit
4522e7f94a
2 changed files with 93 additions and 8 deletions
|
@ -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 {
|
||||
|
|
|
@ -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.
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue