servo: Merge #16674 - Preserve units in computed Angle (from canaltinova:angle-unit); r=emilio
authorNazım Can Altınova <canaltinova@gmail.com>
Mon, 01 May 2017 09:45:04 -0500
changeset 355901 8baa872d36ba8c72322d611076bfe71339edb0e6
parent 355900 538011159bb16c24c8d3d63f91288dba2daa6510
child 355902 717ed8f6b2ee4da76509f8909614c042de86ec68
push id89783
push userkwierso@gmail.com
push dateMon, 01 May 2017 23:06:57 +0000
treeherdermozilla-inbound@253441e75216 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersemilio
bugs1360659
milestone55.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
servo: Merge #16674 - Preserve units in computed Angle (from canaltinova:angle-unit); r=emilio <!-- Please describe your changes on the following line: --> It was converting all angles to radians before. But other browsers preserves the angle units. Fixed that behavior. --- <!-- Thank you for contributing to Servo! Please replace each `[ ]` by `[X]` when the step is complete, and replace `__` with appropriate data: --> - [X] `./mach build -d` does not report any errors - [X] `./mach test-tidy` does not report any errors - [X] These changes fix #16594 and [Bug 1360659](https://bugzilla.mozilla.org/show_bug.cgi?id=1360659) <!-- Also, please make sure that "Allow edits from maintainers" checkbox is checked, so that we can help you if you get stuck somewhere along the way.--> <!-- Pull requests that do not address these steps are welcome, but they will require additional verification as part of the review process. --> Source-Repo: https://github.com/servo/servo Source-Revision: 0abd5bbabd19695a5a42437dc42ab3bdf76f6150
servo/components/style/gecko/conversions.rs
servo/components/style/gecko/values.rs
servo/components/style/gecko_bindings/bindings.rs
servo/components/style/gecko_bindings/sugar/ns_css_value.rs
servo/components/style/properties/gecko.mako.rs
servo/components/style/properties/longhand/effects.mako.rs
servo/components/style/values/computed/mod.rs
servo/components/style/values/specified/mod.rs
--- a/servo/components/style/gecko/conversions.rs
+++ b/servo/components/style/gecko/conversions.rs
@@ -7,21 +7,21 @@
 //! forces us to keep the traits and implementations here
 
 #![allow(unsafe_code)]
 
 use app_units::Au;
 use gecko::values::{convert_rgba_to_nscolor, GeckoStyleCoordConvertible};
 use gecko_bindings::bindings::{Gecko_CreateGradient, Gecko_SetGradientImageValue, Gecko_SetUrlImageValue};
 use gecko_bindings::bindings::{Gecko_InitializeImageCropRect, Gecko_SetImageElement};
-use gecko_bindings::structs::{nsStyleCoord_CalcValue, nsStyleImage};
+use gecko_bindings::structs::{nsCSSUnit, nsStyleCoord_CalcValue, nsStyleImage};
 use gecko_bindings::structs::{nsresult, SheetType};
 use gecko_bindings::sugar::ns_style_coord::{CoordDataValue, CoordDataMut};
 use stylesheets::{Origin, RulesMutateError};
-use values::computed::{CalcLengthOrPercentage, Gradient, GradientItem, Image};
+use values::computed::{Angle, CalcLengthOrPercentage, Gradient, GradientItem, Image};
 use values::computed::{LengthOrPercentage, LengthOrPercentageOrAuto};
 
 impl From<CalcLengthOrPercentage> for nsStyleCoord_CalcValue {
     fn from(other: CalcLengthOrPercentage) -> nsStyleCoord_CalcValue {
         let has_percentage = other.percentage.is_some();
         nsStyleCoord_CalcValue {
             mLength: other.length.0,
             mPercent: other.percentage.unwrap_or(0.0),
@@ -95,16 +95,50 @@ impl From<nsStyleCoord_CalcValue> for Le
         match (other.mHasPercent, other.mLength) {
             (false, _) => LengthOrPercentage::Length(Au(other.mLength)),
             (true, 0) => LengthOrPercentage::Percentage(other.mPercent),
             _ => LengthOrPercentage::Calc(other.into()),
         }
     }
 }
 
+impl From<Angle> for CoordDataValue {
+    fn from(reference: Angle) -> Self {
+        match reference {
+            Angle::Degree(val) => CoordDataValue::Degree(val),
+            Angle::Gradian(val) => CoordDataValue::Grad(val),
+            Angle::Radian(val) => CoordDataValue::Radian(val),
+            Angle::Turn(val) => CoordDataValue::Turn(val),
+        }
+    }
+}
+
+impl Angle {
+    /// Converts Angle struct into (value, unit) pair.
+    pub fn to_gecko_values(&self) -> (f32, nsCSSUnit) {
+        match *self {
+            Angle::Degree(val) => (val, nsCSSUnit::eCSSUnit_Degree),
+            Angle::Gradian(val) => (val, nsCSSUnit::eCSSUnit_Grad),
+            Angle::Radian(val) => (val, nsCSSUnit::eCSSUnit_Radian),
+            Angle::Turn(val) => (val, nsCSSUnit::eCSSUnit_Turn),
+        }
+    }
+
+    /// Converts gecko (value, unit) pair into Angle struct
+    pub fn from_gecko_values(value: f32, unit: nsCSSUnit) -> Angle {
+        match unit {
+            nsCSSUnit::eCSSUnit_Degree => Angle::Degree(value),
+            nsCSSUnit::eCSSUnit_Grad => Angle::Gradian(value),
+            nsCSSUnit::eCSSUnit_Radian => Angle::Radian(value),
+            nsCSSUnit::eCSSUnit_Turn => Angle::Turn(value),
+            _ => panic!("Unexpected unit {:?} for angle", unit),
+        }
+    }
+}
+
 impl nsStyleImage {
     /// Set a given Servo `Image` value into this `nsStyleImage`.
     pub fn set(&mut self, image: Image, cacheable: &mut bool) {
         match image {
             Image::Gradient(gradient) => {
                 self.set_gradient(gradient)
             },
             Image::Url(ref url) => {
--- a/servo/components/style/gecko/values.rs
+++ b/servo/components/style/gecko/values.rs
@@ -244,25 +244,26 @@ impl<T: GeckoStyleCoordConvertible> Geck
 
     fn from_gecko_style_coord<U: CoordData>(coord: &U) -> Option<Self> {
         Some(T::from_gecko_style_coord(coord))
     }
 }
 
 impl GeckoStyleCoordConvertible for Angle {
     fn to_gecko_style_coord<T: CoordDataMut>(&self, coord: &mut T) {
-        coord.set_value(CoordDataValue::Radian(self.radians()))
+        coord.set_value(CoordDataValue::from(*self));
     }
 
     fn from_gecko_style_coord<T: CoordData>(coord: &T) -> Option<Self> {
-        if let CoordDataValue::Radian(r) = coord.as_value() {
-            Some(Angle::from_radians(r))
-            // XXXManishearth should this handle Degree too?
-        } else {
-            None
+        match coord.as_value() {
+            CoordDataValue::Degree(val) => Some(Angle::Degree(val)),
+            CoordDataValue::Grad(val) => Some(Angle::Gradian(val)),
+            CoordDataValue::Radian(val) => Some(Angle::Radian(val)),
+            CoordDataValue::Turn(val) => Some(Angle::Turn(val)),
+            _ => None,
         }
     }
 }
 
 impl GeckoStyleCoordConvertible for Auto {
     fn to_gecko_style_coord<T: CoordDataMut>(&self, coord: &mut T) {
         coord.set_value(CoordDataValue::Auto)
     }
--- a/servo/components/style/gecko_bindings/bindings.rs
+++ b/servo/components/style/gecko_bindings/bindings.rs
@@ -1070,19 +1070,16 @@ extern "C" {
     pub fn Gecko_CSSValue_GetArrayItemConst(css_value: nsCSSValueBorrowed,
                                             index: i32) -> nsCSSValueBorrowed;
 }
 extern "C" {
     pub fn Gecko_CSSValue_GetAbsoluteLength(css_value: nsCSSValueBorrowed)
      -> nscoord;
 }
 extern "C" {
-    pub fn Gecko_CSSValue_GetAngle(css_value: nsCSSValueBorrowed) -> f32;
-}
-extern "C" {
     pub fn Gecko_CSSValue_GetKeyword(aCSSValue: nsCSSValueBorrowed)
      -> nsCSSKeyword;
 }
 extern "C" {
     pub fn Gecko_CSSValue_GetNumber(css_value: nsCSSValueBorrowed) -> f32;
 }
 extern "C" {
     pub fn Gecko_CSSValue_GetPercentage(css_value: nsCSSValueBorrowed) -> f32;
@@ -1106,20 +1103,16 @@ extern "C" {
     pub fn Gecko_CSSValue_SetKeyword(css_value: nsCSSValueBorrowedMut,
                                      keyword: nsCSSKeyword);
 }
 extern "C" {
     pub fn Gecko_CSSValue_SetPercentage(css_value: nsCSSValueBorrowedMut,
                                         percent: f32);
 }
 extern "C" {
-    pub fn Gecko_CSSValue_SetAngle(css_value: nsCSSValueBorrowedMut,
-                                   radians: f32);
-}
-extern "C" {
     pub fn Gecko_CSSValue_SetCalc(css_value: nsCSSValueBorrowedMut,
                                   calc: nsStyleCoord_CalcValue);
 }
 extern "C" {
     pub fn Gecko_CSSValue_SetFunction(css_value: nsCSSValueBorrowedMut,
                                       len: i32);
 }
 extern "C" {
--- a/servo/components/style/gecko_bindings/sugar/ns_css_value.rs
+++ b/servo/components/style/gecko_bindings/sugar/ns_css_value.rs
@@ -7,17 +7,17 @@
 use app_units::Au;
 use gecko_bindings::bindings;
 use gecko_bindings::structs::{nsCSSValue, nsCSSUnit};
 use gecko_bindings::structs::{nsCSSValue_Array, nscolor};
 use gecko_string_cache::Atom;
 use std::mem;
 use std::ops::{Index, IndexMut};
 use std::slice;
-use values::computed::LengthOrPercentage;
+use values::computed::{Angle, LengthOrPercentage};
 use values::specified::url::SpecifiedUrl;
 
 impl nsCSSValue {
     /// Create a CSSValue with null unit, useful to be used as a return value.
     #[inline]
     pub fn null() -> Self {
         unsafe { mem::zeroed() }
     }
@@ -168,16 +168,36 @@ impl nsCSSValue {
         unsafe { bindings::Gecko_CSSValue_SetArray(self, len) }
         unsafe { self.mValue.mArray.as_mut().as_mut() }.unwrap()
     }
 
     /// Generic set from any value that implements the ToNsCssValue trait.
     pub fn set_from<T: ToNsCssValue>(&mut self, value: &T) {
         value.convert(self)
     }
+
+    /// Returns an `Angle` value from this `nsCSSValue`.
+    ///
+    /// Panics if the unit is not `eCSSUnit_Degree` `eCSSUnit_Grad`, `eCSSUnit_Turn`
+    /// or `eCSSUnit_Radian`.
+    pub fn get_angle(&self) -> Angle {
+        unsafe {
+            Angle::from_gecko_values(self.float_unchecked(), self.mUnit)
+        }
+    }
+
+    /// Sets Angle value to this nsCSSValue.
+    pub fn set_angle(&mut self, angle: Angle) {
+        debug_assert_eq!(self.mUnit, nsCSSUnit::eCSSUnit_Null);
+        let (value, unit) = angle.to_gecko_values();
+        self.mUnit = unit;
+        unsafe {
+            *self.mValue.mFloat.as_mut() = value;
+        }
+    }
 }
 
 impl Drop for nsCSSValue {
     fn drop(&mut self) {
         unsafe { bindings::Gecko_CSSValue_Drop(self) };
     }
 }
 
--- a/servo/components/style/properties/gecko.mako.rs
+++ b/servo/components/style/properties/gecko.mako.rs
@@ -1976,17 +1976,17 @@ fn static_assert() {
                 pattern = ", ".join([b + str(a+1) for (a,b) in enumerate(items)])
 
             # First %s substituted with the call to GetArrayItem, the second
             # %s substituted with the corresponding variable
             css_value_setters = {
                 "length" : "bindings::Gecko_CSSValue_SetAbsoluteLength(%s, %s.0)",
                 "percentage" : "bindings::Gecko_CSSValue_SetPercentage(%s, %s)",
                 "lop" : "%s.set_lop(%s)",
-                "angle" : "bindings::Gecko_CSSValue_SetAngle(%s, %s.radians())",
+                "angle" : "%s.set_angle(%s)",
                 "number" : "bindings::Gecko_CSSValue_SetNumber(%s, %s)",
             }
         %>
         longhands::transform::computed_value::ComputedOperation::${name}(${pattern}) => {
             bindings::Gecko_CSSValue_SetFunction(gecko_value, ${len(items) + 1});
             bindings::Gecko_CSSValue_SetKeyword(
                 bindings::Gecko_CSSValue_GetArrayItem(gecko_value, 0),
                 eCSSKeyword_${keyword}
@@ -2053,17 +2053,17 @@ fn static_assert() {
     }
 
     <%def name="computed_operation_arm(name, keyword, items)">
         <%
             # %s is substituted with the call to GetArrayItem.
             css_value_getters = {
                 "length" : "Au(bindings::Gecko_CSSValue_GetAbsoluteLength(%s))",
                 "lop" : "%s.get_lop()",
-                "angle" : "Angle::from_radians(bindings::Gecko_CSSValue_GetAngle(%s))",
+                "angle" : "%s.get_angle()",
                 "number" : "bindings::Gecko_CSSValue_GetNumber(%s)",
             }
         %>
         eCSSKeyword_${keyword} => {
             ComputedOperation::${name}(
             % if keyword == "matrix3d":
                 ComputedMatrix {
             % endif
@@ -2082,17 +2082,16 @@ fn static_assert() {
         },
     </%def>
     pub fn clone_transform(&self) -> longhands::transform::computed_value::T {
         use app_units::Au;
         use gecko_bindings::structs::nsCSSKeyword::*;
         use properties::longhands::transform::computed_value;
         use properties::longhands::transform::computed_value::ComputedMatrix;
         use properties::longhands::transform::computed_value::ComputedOperation;
-        use values::computed::Angle;
 
         if self.gecko.mSpecifiedTransform.mRawPtr.is_null() {
             return computed_value::T(None);
         }
 
         let mut result = vec![];
         let mut cur = unsafe { (*self.gecko.mSpecifiedTransform.to_safe().get()).mHead };
         while !cur.is_null() {
@@ -3129,17 +3128,17 @@ fn static_assert() {
                                                   gecko_filter),
                 Contrast(factor)   => fill_filter(NS_STYLE_FILTER_CONTRAST,
                                                   CoordDataValue::Factor(factor),
                                                   gecko_filter),
                 Grayscale(factor)  => fill_filter(NS_STYLE_FILTER_GRAYSCALE,
                                                   CoordDataValue::Factor(factor),
                                                   gecko_filter),
                 HueRotate(angle)   => fill_filter(NS_STYLE_FILTER_HUE_ROTATE,
-                                                  CoordDataValue::Radian(angle.radians()),
+                                                  CoordDataValue::from(angle),
                                                   gecko_filter),
                 Invert(factor)     => fill_filter(NS_STYLE_FILTER_INVERT,
                                                   CoordDataValue::Factor(factor),
                                                   gecko_filter),
                 Opacity(factor)    => fill_filter(NS_STYLE_FILTER_OPACITY,
                                                   CoordDataValue::Factor(factor),
                                                   gecko_filter),
                 Saturate(factor)   => fill_filter(NS_STYLE_FILTER_SATURATE,
--- a/servo/components/style/properties/longhand/effects.mako.rs
+++ b/servo/components/style/properties/longhand/effects.mako.rs
@@ -134,17 +134,17 @@
         Url(SpecifiedUrl),
         % endif
     }
 
     pub mod computed_value {
         use app_units::Au;
         use values::CSSFloat;
         use values::computed::{CSSColor, Shadow};
-        use values::specified::Angle;
+        use values::computed::Angle;
         use values::specified::url::SpecifiedUrl;
 
         #[derive(Clone, PartialEq, Debug)]
         #[cfg_attr(feature = "servo", derive(HeapSizeOf, Deserialize, Serialize))]
         pub enum Filter {
             Blur(Au),
             Brightness(CSSFloat),
             Contrast(CSSFloat),
@@ -377,17 +377,19 @@
         fn to_computed_value(&self, context: &Context) -> computed_value::T {
             computed_value::T{ filters: self.0.iter().map(|value| {
                 match *value {
                     SpecifiedFilter::Blur(ref factor) =>
                         computed_value::Filter::Blur(factor.to_computed_value(context)),
                     SpecifiedFilter::Brightness(factor) => computed_value::Filter::Brightness(factor),
                     SpecifiedFilter::Contrast(factor) => computed_value::Filter::Contrast(factor),
                     SpecifiedFilter::Grayscale(factor) => computed_value::Filter::Grayscale(factor),
-                    SpecifiedFilter::HueRotate(factor) => computed_value::Filter::HueRotate(factor),
+                    SpecifiedFilter::HueRotate(ref factor) => {
+                        computed_value::Filter::HueRotate(factor.to_computed_value(context))
+                    },
                     SpecifiedFilter::Invert(factor) => computed_value::Filter::Invert(factor),
                     SpecifiedFilter::Opacity(factor) => computed_value::Filter::Opacity(factor),
                     SpecifiedFilter::Saturate(factor) => computed_value::Filter::Saturate(factor),
                     SpecifiedFilter::Sepia(factor) => computed_value::Filter::Sepia(factor),
                     % if product == "gecko":
                     SpecifiedFilter::DropShadow(ref shadow) => {
                         computed_value::Filter::DropShadow(shadow.to_computed_value(context))
                     },
@@ -402,17 +404,19 @@
         fn from_computed_value(computed: &computed_value::T) -> Self {
             SpecifiedValue(computed.filters.iter().map(|value| {
                 match *value {
                     computed_value::Filter::Blur(factor) =>
                         SpecifiedFilter::Blur(ToComputedValue::from_computed_value(&factor)),
                     computed_value::Filter::Brightness(factor) => SpecifiedFilter::Brightness(factor),
                     computed_value::Filter::Contrast(factor) => SpecifiedFilter::Contrast(factor),
                     computed_value::Filter::Grayscale(factor) => SpecifiedFilter::Grayscale(factor),
-                    computed_value::Filter::HueRotate(factor) => SpecifiedFilter::HueRotate(factor),
+                    computed_value::Filter::HueRotate(ref factor) => {
+                        SpecifiedFilter::HueRotate(ToComputedValue::from_computed_value(factor))
+                    },
                     computed_value::Filter::Invert(factor) => SpecifiedFilter::Invert(factor),
                     computed_value::Filter::Opacity(factor) => SpecifiedFilter::Opacity(factor),
                     computed_value::Filter::Saturate(factor) => SpecifiedFilter::Saturate(factor),
                     computed_value::Filter::Sepia(factor) => SpecifiedFilter::Sepia(factor),
                     % if product == "gecko":
                     computed_value::Filter::DropShadow(ref shadow) => {
                         SpecifiedFilter::DropShadow(
                             ToComputedValue::from_computed_value(shadow),
--- a/servo/components/style/values/computed/mod.rs
+++ b/servo/components/style/values/computed/mod.rs
@@ -6,16 +6,18 @@
 
 use context::QuirksMode;
 use euclid::size::Size2D;
 use font_metrics::FontMetricsProvider;
 use media_queries::Device;
 #[cfg(feature = "gecko")]
 use properties;
 use properties::{ComputedValues, StyleBuilder};
+use std::f32;
+use std::f32::consts::PI;
 use std::fmt;
 use style_traits::ToCss;
 use super::{CSSFloat, CSSInteger, RGBA};
 use super::generics::BorderRadiusSize as GenericBorderRadiusSize;
 use super::specified;
 use super::specified::grid::{TrackBreadth as GenericTrackBreadth, TrackSize as GenericTrackSize};
 
 pub use app_units::Au;
@@ -134,45 +136,65 @@ impl<T> ToComputedValue for T
     fn from_computed_value(computed: &T) -> Self {
         computed.clone()
     }
 }
 
 /// A computed `<angle>` value.
 #[derive(Clone, PartialEq, PartialOrd, Copy, Debug)]
 #[cfg_attr(feature = "servo", derive(HeapSizeOf, Deserialize, Serialize))]
-pub struct Angle {
-    radians: CSSFloat,
+pub enum Angle {
+    /// An angle with degree unit
+    Degree(CSSFloat),
+    /// An angle with gradian unit
+    Gradian(CSSFloat),
+    /// An angle with radian unit
+    Radian(CSSFloat),
+    /// An angle with turn unit
+    Turn(CSSFloat),
 }
 
 impl Angle {
     /// Construct a computed `Angle` value from a radian amount.
     pub fn from_radians(radians: CSSFloat) -> Self {
-        Angle {
-            radians: radians,
-        }
+        Angle::Radian(radians)
     }
 
     /// Return the amount of radians this angle represents.
     #[inline]
     pub fn radians(&self) -> CSSFloat {
-        self.radians
+        const RAD_PER_DEG: CSSFloat = PI / 180.0;
+        const RAD_PER_GRAD: CSSFloat = PI / 200.0;
+        const RAD_PER_TURN: CSSFloat = PI * 2.0;
+
+        let radians = match *self {
+            Angle::Degree(val) => val * RAD_PER_DEG,
+            Angle::Gradian(val) => val * RAD_PER_GRAD,
+            Angle::Turn(val) => val * RAD_PER_TURN,
+            Angle::Radian(val) => val,
+        };
+        radians.min(f32::MAX).max(f32::MIN)
     }
 
     /// Returns an angle that represents a rotation of zero radians.
     pub fn zero() -> Self {
-        Self::from_radians(0.0)
+        Angle::Radian(0.0)
     }
 }
 
 impl ToCss for Angle {
     fn to_css<W>(&self, dest: &mut W) -> fmt::Result
         where W: fmt::Write,
     {
-        write!(dest, "{}rad", self.radians())
+        match *self {
+            Angle::Degree(val) => write!(dest, "{}deg", val),
+            Angle::Gradian(val) => write!(dest, "{}grad", val),
+            Angle::Radian(val) => write!(dest, "{}rad", val),
+            Angle::Turn(val) => write!(dest, "{}turn", val),
+        }
     }
 }
 
 /// A computed `<time>` value.
 #[derive(Clone, PartialEq, PartialOrd, Copy, Debug)]
 #[cfg_attr(feature = "servo", derive(HeapSizeOf, Deserialize, Serialize))]
 pub struct Time {
     seconds: CSSFloat,
--- a/servo/components/style/values/specified/mod.rs
+++ b/servo/components/style/values/specified/mod.rs
@@ -10,17 +10,16 @@ use app_units::Au;
 use context::QuirksMode;
 use cssparser::{self, Parser, Token};
 use euclid::size::Size2D;
 use parser::{ParserContext, Parse};
 use self::grid::{TrackBreadth as GenericTrackBreadth, TrackSize as GenericTrackSize};
 use self::url::SpecifiedUrl;
 use std::ascii::AsciiExt;
 use std::f32;
-use std::f32::consts::PI;
 use std::fmt;
 use std::ops::Mul;
 use style_traits::ToCss;
 use style_traits::values::specified::AllowedNumericType;
 use super::{Auto, CSSFloat, CSSInteger, HasViewportPercentage, Either, None_};
 use super::computed::{self, Context};
 use super::computed::{Shadow as ComputedShadow, ToComputedValue};
 use super::generics::BorderRadiusSize as GenericBorderRadiusSize;
@@ -295,120 +294,86 @@ impl Parse for BorderRadiusSize {
             .unwrap_or_else(|()| first.clone());
         Ok(GenericBorderRadiusSize(Size2D::new(first, second)))
     }
 }
 
 #[derive(Clone, PartialEq, Copy, Debug)]
 #[cfg_attr(feature = "servo", derive(HeapSizeOf, Deserialize, Serialize))]
 /// An angle consisting of a value and a unit.
+///
+/// Computed Angle is essentially same as specified angle except calc
+/// value serialization. Therefore we are using computed Angle enum
+/// to hold the value and unit type.
 pub struct Angle {
-    value: CSSFloat,
-    unit: AngleUnit,
+    value: computed::Angle,
     was_calc: bool,
 }
 
-#[derive(Copy, Clone, Debug, PartialEq)]
-#[cfg_attr(feature = "servo", derive(HeapSizeOf, Deserialize, Serialize))]
-/// A unit used together with an angle.
-pub enum AngleUnit {
-    /// Degrees, short name "deg".
-    Degree,
-    /// Gradians, short name "grad".
-    Gradian,
-    /// Radians, short name "rad".
-    Radian,
-    /// Turns, short name "turn".
-    Turn,
-}
-
-impl ToCss for AngleUnit {
-    fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
-        use self::AngleUnit::*;
-        dest.write_str(match *self {
-            Degree => "deg",
-            Gradian => "grad",
-            Radian => "rad",
-            Turn => "turn",
-        })
-    }
-}
-
 impl ToCss for Angle {
     fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
         if self.was_calc {
             dest.write_str("calc(")?;
         }
         self.value.to_css(dest)?;
-        self.unit.to_css(dest)?;
         if self.was_calc {
             dest.write_str(")")?;
         }
         Ok(())
     }
 }
 
 impl ToComputedValue for Angle {
     type ComputedValue = computed::Angle;
 
     fn to_computed_value(&self, _context: &Context) -> Self::ComputedValue {
-        computed::Angle::from_radians(self.radians())
+        self.value
     }
 
     fn from_computed_value(computed: &Self::ComputedValue) -> Self {
-        Angle::from_radians(computed.radians())
+        Angle {
+            value: *computed,
+            was_calc: false,
+        }
     }
 }
 
 impl Angle {
     /// Returns an angle with the given value in degrees.
     pub fn from_degrees(value: CSSFloat) -> Self {
-        Angle { value: value, unit: AngleUnit::Degree, was_calc: false }
+        Angle { value: computed::Angle::Degree(value), was_calc: false }
     }
     /// Returns an angle with the given value in gradians.
     pub fn from_gradians(value: CSSFloat) -> Self {
-        Angle { value: value, unit: AngleUnit::Gradian, was_calc: false }
+        Angle { value: computed::Angle::Gradian(value), was_calc: false }
     }
     /// Returns an angle with the given value in turns.
     pub fn from_turns(value: CSSFloat) -> Self {
-        Angle { value: value, unit: AngleUnit::Turn, was_calc: false }
+        Angle { value: computed::Angle::Turn(value), was_calc: false }
     }
     /// Returns an angle with the given value in radians.
     pub fn from_radians(value: CSSFloat) -> Self {
-        Angle { value: value, unit: AngleUnit::Radian, was_calc: false }
+        Angle { value: computed::Angle::Radian(value), was_calc: false }
     }
 
     #[inline]
     #[allow(missing_docs)]
     pub fn radians(self) -> f32 {
-        use self::AngleUnit::*;
-
-        const RAD_PER_DEG: CSSFloat = PI / 180.0;
-        const RAD_PER_GRAD: CSSFloat = PI / 200.0;
-        const RAD_PER_TURN: CSSFloat = PI * 2.0;
-
-        let radians = match self.unit {
-            Degree => self.value * RAD_PER_DEG,
-            Gradian => self.value * RAD_PER_GRAD,
-            Turn => self.value * RAD_PER_TURN,
-            Radian => self.value,
-        };
-        radians.min(f32::MAX).max(f32::MIN)
+        self.value.radians()
     }
 
     /// Returns an angle value that represents zero.
     pub fn zero() -> Self {
         Self::from_degrees(0.0)
     }
 
     /// Returns an `Angle` parsed from a `calc()` expression.
     pub fn from_calc(radians: CSSFloat) -> Self {
         Angle {
-            value: radians,
-            unit: AngleUnit::Radian,
+            value: computed::Angle::Radian(radians),
             was_calc: true,
         }
     }
 }
 
 impl Parse for Angle {
     /// Parses an angle according to CSS-VALUES ยง 6.1.
     fn parse(context: &ParserContext, input: &mut Parser) -> Result<Self, ()> {