style: Always compute angle values to degrees.

This matches the spec, https://drafts.csswg.org/css-values/#angles, which says:

> All <angle> units are compatible, and deg is their canonical unit.

And https://drafts.csswg.org/css-values/#compat, which says:

>When serializing computed values [...], compatible units [...] are converted into a single canonical unit.

And also other implementations (Blink always serializes angles as degrees in
computed style for example).

Also allows us to get rid of quite a bit of code, and makes computed angle value
representation just a number, which is nice.

Differential Revision: https://phabricator.services.mozilla.com/D8619
This commit is contained in:
Emilio Cobos Álvarez 2018-10-13 00:41:03 +00:00
parent 11fedf18d9
commit 42def5a011
No known key found for this signature in database
GPG key ID: 056B727BB9C1027C
11 changed files with 142 additions and 209 deletions

View file

@ -7,21 +7,53 @@
use cssparser::{Parser, Token};
use parser::{Parse, ParserContext};
use std::fmt::{self, Write};
use std::f32::consts::PI;
use style_traits::{CssWriter, ParseError, SpecifiedValueInfo, ToCss};
use values::CSSFloat;
use values::computed::{Context, ToComputedValue};
use values::computed::angle::Angle as ComputedAngle;
use values::specified::calc::CalcNode;
/// A specified angle.
///
/// Computed angles are essentially same as specified ones except for `calc()`
/// value serialization. Therefore we are storing a computed angle inside
/// to hold the actual value and its unit.
/// A specified angle dimension.
#[derive(Clone, Copy, Debug, MallocSizeOf, PartialEq, PartialOrd, ToCss)]
pub enum AngleDimension {
/// An angle with degree unit.
#[css(dimension)]
Deg(CSSFloat),
/// An angle with gradian unit.
#[css(dimension)]
Grad(CSSFloat),
/// An angle with radian unit.
#[css(dimension)]
Rad(CSSFloat),
/// An angle with turn unit.
#[css(dimension)]
Turn(CSSFloat),
}
impl AngleDimension {
/// Returns the amount of degrees this angle represents.
#[inline]
fn degrees(&self) -> CSSFloat {
const DEG_PER_RAD: f32 = 180.0 / PI;
const DEG_PER_TURN: f32 = 360.0;
const DEG_PER_GRAD: f32 = 180.0 / 200.0;
match *self {
AngleDimension::Deg(d) => d,
AngleDimension::Rad(rad) => rad * DEG_PER_RAD,
AngleDimension::Turn(turns) => turns * DEG_PER_TURN,
AngleDimension::Grad(gradians) => gradians * DEG_PER_GRAD,
}
}
}
/// A specified Angle value, which is just the angle dimension, plus whether it
/// was specified as `calc()` or not.
#[cfg_attr(feature = "servo", derive(Deserialize, Serialize))]
#[derive(Clone, Copy, Debug, MallocSizeOf, PartialEq)]
pub struct Angle {
value: ComputedAngle,
value: AngleDimension,
was_calc: bool,
}
@ -41,22 +73,18 @@ impl ToCss for Angle {
}
}
// FIXME(emilio): Probably computed angles shouldn't preserve the unit and
// should serialize to degrees per:
//
// https://drafts.csswg.org/css-values/#compat
impl ToComputedValue for Angle {
type ComputedValue = ComputedAngle;
#[inline]
fn to_computed_value(&self, _context: &Context) -> Self::ComputedValue {
self.value
ComputedAngle::from_degrees(self.degrees())
}
#[inline]
fn from_computed_value(computed: &Self::ComputedValue) -> Self {
Angle {
value: *computed,
value: AngleDimension::Deg(computed.degrees()),
was_calc: false,
}
}
@ -64,35 +92,18 @@ impl ToComputedValue for Angle {
impl Angle {
/// Creates an angle with the given value in degrees.
#[inline]
pub fn from_degrees(value: CSSFloat, was_calc: bool) -> Self {
Angle {
value: ComputedAngle::Deg(value),
value: AngleDimension::Deg(value),
was_calc,
}
}
/// Creates an angle with the given value in gradians.
pub fn from_gradians(value: CSSFloat, was_calc: bool) -> Self {
Angle {
value: ComputedAngle::Grad(value),
was_calc,
}
}
/// Creates an angle with the given value in turns.
pub fn from_turns(value: CSSFloat, was_calc: bool) -> Self {
Angle {
value: ComputedAngle::Turn(value),
was_calc,
}
}
/// Creates an angle with the given value in radians.
pub fn from_radians(value: CSSFloat, was_calc: bool) -> Self {
Angle {
value: ComputedAngle::Rad(value),
was_calc,
}
/// Returns the value of the angle in degrees, mostly for `calc()`.
#[inline]
pub fn degrees(&self) -> CSSFloat {
self.value.degrees()
}
/// Whether this specified angle came from a `calc()` expression.
@ -101,39 +112,21 @@ impl Angle {
self.was_calc
}
/// Returns the amount of radians this angle represents.
#[inline]
pub fn radians(self) -> f32 {
self.value.radians()
}
/// Returns the amount of degrees this angle represents.
#[inline]
pub fn degrees(self) -> f32 {
self.value.degrees()
}
/// Returns `0deg`.
#[inline]
pub fn zero() -> Self {
Self::from_degrees(0.0, false)
}
/// Returns an `Angle` parsed from a `calc()` expression.
pub fn from_calc(radians: CSSFloat) -> Self {
pub fn from_calc(degrees: CSSFloat) -> Self {
Angle {
value: ComputedAngle::Rad(radians),
value: AngleDimension::Deg(degrees),
was_calc: true,
}
}
}
impl AsRef<ComputedAngle> for Angle {
#[inline]
fn as_ref(&self) -> &ComputedAngle {
&self.value
}
}
/// Whether to allow parsing an unitless zero as a valid angle.
///
/// This should always be `No`, except for exceptions like:
@ -158,20 +151,26 @@ impl Parse for Angle {
impl Angle {
/// Parse an `<angle>` value given a value and an unit.
pub fn parse_dimension(value: CSSFloat, unit: &str, from_calc: bool) -> Result<Angle, ()> {
let angle = match_ignore_ascii_case! { unit,
"deg" => Angle::from_degrees(value, from_calc),
"grad" => Angle::from_gradians(value, from_calc),
"turn" => Angle::from_turns(value, from_calc),
"rad" => Angle::from_radians(value, from_calc),
pub fn parse_dimension(
value: CSSFloat,
unit: &str,
was_calc: bool,
) -> Result<Angle, ()> {
let value = match_ignore_ascii_case! { unit,
"deg" => AngleDimension::Deg(value),
"grad" => AngleDimension::Grad(value),
"turn" => AngleDimension::Turn(value),
"rad" => AngleDimension::Rad(value),
_ => return Err(())
};
Ok(angle)
Ok(Self { value, was_calc })
}
/// Parse an `<angle>` allowing unitless zero to represent a zero angle.
///
/// See the comment in `AllowUnitlessZeroAngle` for why.
#[inline]
pub fn parse_with_unitless<'i, 't>(
context: &ParserContext,
input: &mut Parser<'i, 't>,

View file

@ -469,22 +469,22 @@ impl CalcNode {
CalcNode::Sub(ref a, ref b) => {
let lhs = a.to_angle()?;
let rhs = b.to_angle()?;
Angle::from_calc(lhs.radians() - rhs.radians())
Angle::from_calc(lhs.degrees() - rhs.degrees())
},
CalcNode::Sum(ref a, ref b) => {
let lhs = a.to_angle()?;
let rhs = b.to_angle()?;
Angle::from_calc(lhs.radians() + rhs.radians())
Angle::from_calc(lhs.degrees() + rhs.degrees())
},
CalcNode::Mul(ref a, ref b) => match a.to_angle() {
Ok(lhs) => {
let rhs = b.to_number()?;
Angle::from_calc(lhs.radians() * rhs)
Angle::from_calc(lhs.degrees() * rhs)
},
Err(..) => {
let lhs = a.to_number()?;
let rhs = b.to_angle()?;
Angle::from_calc(lhs * rhs.radians())
Angle::from_calc(lhs * rhs.degrees())
},
},
CalcNode::Div(ref a, ref b) => {
@ -493,7 +493,7 @@ impl CalcNode {
if rhs == 0. {
return Err(());
}
Angle::from_calc(lhs.radians() / rhs)
Angle::from_calc(lhs.degrees() / rhs)
},
CalcNode::Number(..) |
CalcNode::Length(..) |

View file

@ -301,7 +301,7 @@ impl SpecifiedFontStyle {
}
fn compute_angle(angle: &Angle) -> ComputedAngle {
ComputedAngle::Deg(Self::compute_angle_degrees(angle))
ComputedAngle::from_degrees(Self::compute_angle_degrees(angle))
}
/// Parse a suitable angle for font-style: oblique.

View file

@ -15,7 +15,6 @@ use selectors::parser::SelectorParseErrorKind;
#[cfg(feature = "servo")]
use servo_url::ServoUrl;
use std::cmp::Ordering;
use std::f32::consts::PI;
use std::fmt::{self, Write};
use style_traits::{CssType, CssWriter, KeywordsCollectFn, ParseError};
use style_traits::{StyleParseErrorKind, SpecifiedValueInfo, ToCss};
@ -679,7 +678,7 @@ impl GradientKind {
impl generic::LineDirection for LineDirection {
fn points_downwards(&self, compat_mode: CompatMode) -> bool {
match *self {
LineDirection::Angle(ref angle) => angle.radians() == PI,
LineDirection::Angle(ref angle) => angle.degrees() == 180.0,
LineDirection::Vertical(Y::Bottom) if compat_mode == CompatMode::Modern => true,
LineDirection::Vertical(Y::Top) if compat_mode != CompatMode::Modern => true,
#[cfg(feature = "gecko")]