Bug 1498734 - Always compute angle values to degrees. r=xidorn
authorEmilio Cobos Álvarez <emilio@crisal.io>
Sat, 13 Oct 2018 00:41:03 +0000
changeset 440974 7584ce8674c21dfedc59ecc712eabb94306a2ba2
parent 440973 474b2a78d4fac1f3006c198ad063449ded904e8d
child 440975 ebaf166c573104897304cf5b0143eba1bd2c8960
push id34841
push useraciure@mozilla.com
push dateSat, 13 Oct 2018 09:35:03 +0000
treeherdermozilla-central@0dfb3afc7357 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersxidorn
bugs1498734
milestone64.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
Bug 1498734 - Always compute angle values to degrees. r=xidorn 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
accessible/base/StyleInfo.cpp
accessible/base/TextAttrs.cpp
dom/animation/test/mozilla/test_distance_of_filter.html
dom/animation/test/mozilla/test_distance_of_transform.html
layout/style/nsCSSValue.cpp
layout/style/nsCSSValue.h
layout/style/nsComputedDOMStyle.cpp
layout/style/nsROCSSPrimitiveValue.cpp
layout/style/nsROCSSPrimitiveValue.h
layout/style/nsStyleCoord.cpp
layout/style/nsStyleCoord.h
layout/style/nsStyleUtil.cpp
layout/style/test/test_computed_style.html
layout/style/test/test_transitions_per_property.html
servo/components/style/gecko/conversions.rs
servo/components/style/gecko/values.rs
servo/components/style/gecko_bindings/sugar/ns_css_value.rs
servo/components/style/gecko_bindings/sugar/ns_style_coord.rs
servo/components/style/values/computed/angle.rs
servo/components/style/values/computed/font.rs
servo/components/style/values/generics/transform.rs
servo/components/style/values/specified/angle.rs
servo/components/style/values/specified/calc.rs
servo/components/style/values/specified/font.rs
servo/components/style/values/specified/image.rs
testing/web-platform/tests/web-animations/animation-model/animation-types/property-types.js
--- a/accessible/base/StyleInfo.cpp
+++ b/accessible/base/StyleInfo.cpp
@@ -61,19 +61,16 @@ StyleInfo::TextIndent(nsAString& aValue)
       break;
 
     case eStyleUnit_Null:
     case eStyleUnit_Normal:
     case eStyleUnit_Auto:
     case eStyleUnit_None:
     case eStyleUnit_Factor:
     case eStyleUnit_Degree:
-    case eStyleUnit_Grad:
-    case eStyleUnit_Radian:
-    case eStyleUnit_Turn:
     case eStyleUnit_FlexFraction:
     case eStyleUnit_Integer:
     case eStyleUnit_Enumerated:
     case eStyleUnit_Calc:
       aValue.AppendLiteral("0px");
       break;
   }
 }
--- a/accessible/base/TextAttrs.cpp
+++ b/accessible/base/TextAttrs.cpp
@@ -871,19 +871,16 @@ TextAttrsMgr::TextPosTextAttr::
     }
 
     case eStyleUnit_Null:
     case eStyleUnit_Normal:
     case eStyleUnit_Auto:
     case eStyleUnit_None:
     case eStyleUnit_Factor:
     case eStyleUnit_Degree:
-    case eStyleUnit_Grad:
-    case eStyleUnit_Radian:
-    case eStyleUnit_Turn:
     case eStyleUnit_FlexFraction:
     case eStyleUnit_Integer:
     case eStyleUnit_Calc:
       break;
   }
 
   const nsIContent* content = aFrame->GetContent();
   if (content) {
--- a/dom/animation/test/mozilla/test_distance_of_filter.html
+++ b/dom/animation/test/mozilla/test_distance_of_filter.html
@@ -2,16 +2,18 @@
 <meta charset=utf-8>
 <script src='/resources/testharness.js'></script>
 <script src='/resources/testharnessreport.js'></script>
 <script src='../testcommon.js'></script>
 <div id='log'></div>
 <script type='text/javascript'>
 'use strict';
 
+const EPSILON = 1e-6;
+
 // We don't have an official spec to define the distance between two filter
 // lists, but we still need this for DevTools, so Gecko and Servo backends use
 // the similar rules to define the distance. If there is a spec for it, we have
 // to update this test file.
 // See https://github.com/w3c/fxtf-drafts/issues/91.
 
 test(function(t) {
   var target = addDiv(t);
@@ -117,25 +119,25 @@ test(function(t) {
   var dist = getDistance(target, 'filter', 'grayscale(75%)', 'grayscale(175%)');
   assert_equals(dist, 0.25, 'distance of grayscale(75%) and grayscale(175%)');
 }, 'grayscales where one has a value larger than 1.0');
 
 test(function(t) {
   var target = addDiv(t);
   var dist = getDistance(target, 'filter', 'hue-rotate(180deg)', 'none');
   // The default value of hue-rotate is 0deg.
-  assert_equals(dist, Math.PI, 'hue-rotate(180deg) and none');
+  assert_approx_equals(dist, Math.PI, EPSILON, 'hue-rotate(180deg) and none');
 }, 'hue-rotate and none');
 
 test(function(t) {
   var target = addDiv(t);
   var dist = getDistance(target, 'filter',
                          'hue-rotate(720deg)', 'hue-rotate(-180deg)');
-  assert_equals(dist, 5 * Math.PI,
-                'hue-rotate(720deg) and hue-rotate(-180deg)');
+  assert_approx_equals(dist, 5 * Math.PI, EPSILON,
+                       'hue-rotate(720deg) and hue-rotate(-180deg)');
 }, 'hue-rotates');
 
 test(function(t) {
   var target = addDiv(t);
   var dist = getDistance(target, 'filter', 'invert(25%)', 'none');
   // The default value of invert is 0%.
   assert_equals(dist, 0.25, 'invert(25%) and none');
 }, 'invert and none');
--- a/dom/animation/test/mozilla/test_distance_of_transform.html
+++ b/dom/animation/test/mozilla/test_distance_of_transform.html
@@ -7,17 +7,17 @@
 <script type='text/javascript'>
 'use strict';
 
 // We don't have an official spec to define the distance between two transform
 // lists, but we still need this for DevTools, so Gecko and Servo backend use
 // the similar rules to define the distance. If there is a spec for it, we have
 // to update this test file.
 
-const epsilon = 0.00001;
+const EPSILON = 0.00001;
 
 // |v| should be a unit vector (i.e. having length 1)
 function getQuaternion(v, angle) {
   return [
     v[0] * Math.sin(angle / 2.0),
     v[1] * Math.sin(angle / 2.0),
     v[2] * Math.sin(angle / 2.0),
     Math.cos(angle / 2.0)
@@ -131,65 +131,65 @@ test(function(t) {
                 Math.sqrt(0.5 * 0.5 + 0.5 * 0.5 + 0.5 * 0.5),
                 'distance of scale3d');
 }, 'Test distance of scale3d functions');
 
 test(function(t) {
   var target = addDiv(t);
   var dist =
     getDistance(target, 'transform', 'rotate(45deg)', 'rotate(90deg)');
-  assert_equals(dist, Math.PI/ 2.0 - Math.PI / 4.0, 'distance of rotate');
+  assert_approx_equals(dist, Math.PI / 2.0 - Math.PI / 4.0, EPSILON, 'distance of rotate');
 }, 'Test distance of rotate functions');
 
 test(function(t) {
   var target = addDiv(t);
   var dist =
     getDistance(target, 'transform', 'rotate(45deg)', 'none');
-  assert_equals(dist, Math.PI / 4.0, 'distance of rotate');
+  assert_approx_equals(dist, Math.PI / 4.0, EPSILON, 'distance of rotate');
 }, 'Test distance of rotate function and none');
 
 test(function(t) {
   var target = addDiv(t);
   var dist = getDistance(target, 'transform',
                          'rotate3d(0, 1, 0, 90deg)',
                          'none');
-  assert_equals(dist, Math.PI / 2, 'distance of rotate3d');
+  assert_approx_equals(dist, Math.PI / 2, EPSILON, 'distance of rotate3d');
 }, 'Test distance of rotate3d function and none');
 
 test(function(t) {
   var target = addDiv(t);
   var dist = getDistance(target, 'transform',
                          'rotate3d(0, 0, 1, 90deg)',
                          'rotate3d(1, 0, 0, 90deg)');
   let q1 = getQuaternion([0, 0, 1], Math.PI / 2.0);
   let q2 = getQuaternion([1, 0, 0], Math.PI / 2.0);
-  assert_equals(dist, computeRotateDistance(q1, q2), 'distance of rotate3d');
+  assert_approx_equals(dist, computeRotateDistance(q1, q2), EPSILON, 'distance of rotate3d');
 }, 'Test distance of rotate3d functions');
 
 test(function(t) {
   var target = addDiv(t);
   var dist = getDistance(target, 'transform',
                          'rotate3d(0, 0, 1, 90deg)',
                          'rotate3d(0, 0, 0, 90deg)');
-  assert_equals(dist, Math.PI / 2, 'distance of rotate3d');
+  assert_approx_equals(dist, Math.PI / 2, EPSILON, 'distance of rotate3d');
 }, 'Test distance of rotate3d functions whose direction vector cannot be ' +
    'normalized');
 
 test(function(t) {
   var target = addDiv(t);
   var dist = getDistance(target, 'transform', 'skew(1rad, 0.5rad)', 'none');
-  assert_equals(dist, Math.sqrt(1 * 1 + 0.5 * 0.5), 'distance of skew');
+  assert_approx_equals(dist, Math.sqrt(1 * 1 + 0.5 * 0.5), EPSILON, 'distance of skew');
 }, 'Test distance of skew function and none');
 
 test(function(t) {
   var target = addDiv(t);
   var dist = getDistance(target, 'transform',
                          'skew(1rad, 0.5rad)',
                          'skew(-1rad, 0)');
-  assert_equals(dist, Math.sqrt(2 * 2 + 0.5 * 0.5), 'distance of skew');
+  assert_approx_equals(dist, Math.sqrt(2 * 2 + 0.5 * 0.5), EPSILON, 'distance of skew');
 }, 'Test distance of skew functions');
 
 test(function(t) {
   var target = addDiv(t);
   var dist = getDistance(target, 'transform',
                          'perspective(128px)',
                          'none');
   assert_equals(dist, 128, 'distance of perspective');
@@ -219,17 +219,17 @@ test(function(t) {
   var cos_30 = Math.cos(Math.PI / 6);
   // matrix => translate(100, 0) rotate(30deg).
   var matrix = createMatrixFromArray([ cos_30, sin_30,
                                       -sin_30, cos_30,
                                        100, 0 ]);
   var dist = getDistance(target, 'transform', matrix, 'none');
   assert_approx_equals(dist,
                        Math.sqrt(100 * 100 + (Math.PI / 6) * (Math.PI / 6)),
-                       epsilon,
+                       EPSILON,
                        'distance of matrix');
 }, 'Test distance of matrix function and none');
 
 test(function(t) {
   var target = addDiv(t);
   var sin_30 = Math.sin(Math.PI / 6);
   var cos_30 = Math.cos(Math.PI / 6);
   // matrix1 => translate(100, 0) rotate(30deg).
@@ -238,42 +238,42 @@ test(function(t) {
                                         100, 0 ]);
   // matrix2 => translate(0, 100) scale(0.5).
   var matrix2 = createMatrixFromArray([ 0.5, 0, 0, 0.5, 0, 100 ]);
   var dist = getDistance(target, 'transform', matrix1, matrix2);
   assert_approx_equals(dist,
                        Math.sqrt(100 * 100 + 100 * 100 +          // translate
                                  (Math.PI / 6) * (Math.PI / 6) +  // rotate
                                  0.5 * 0.5 + 0.5 * 0.5),          // scale
-                       epsilon,
+                       EPSILON,
                        'distance of matrix');
 }, 'Test distance of matrix functions');
 
 test(function(t) {
   var target = addDiv(t);
   var matrix = createMatrixFromArray(rotate3dToMatrix(0, 1, 0, Math.PI / 6));
   var dist = getDistance(target, 'transform', matrix, 'none');
-  assert_approx_equals(dist, Math.PI / 6, epsilon, 'distance of matrix3d');
+  assert_approx_equals(dist, Math.PI / 6, EPSILON, 'distance of matrix3d');
 }, 'Test distance of matrix3d function and none');
 
 test(function(t) {
   var target = addDiv(t);
   // matrix1 => rotate3d(0, 1, 0, 30deg).
   var matrix1 = createMatrixFromArray(rotate3dToMatrix(0, 1, 0, Math.PI / 6));
   // matrix1 => translate3d(100, 0, 0) scale3d(0.5, 0.5, 0.5).
   var matrix2 = createMatrixFromArray([ 0.5, 0, 0, 0,
                                         0, 0.5, 0, 0,
                                         0, 0, 0.5, 0,
                                         100, 0, 0, 1 ]);
   var dist = getDistance(target, 'transform', matrix1, matrix2);
   assert_approx_equals(dist,
                        Math.sqrt(100 * 100 +                      // translate
                                  0.5 * 0.5 * 3 +                  // scale
                                  (Math.PI / 6) * (Math.PI / 6)),  // rotate
-                       epsilon,
+                       EPSILON,
                        'distance of matrix');
 }, 'Test distance of matrix3d functions');
 
 test(function(t) {
   var target = addDiv(t);
   var cos_180 = Math.cos(Math.PI);
   var sin_180 = Math.sin(Math.PI);
   // matrix1 => translate3d(100px, 50px, -10px) skew(45deg).
@@ -286,41 +286,41 @@ test(function(t) {
                                         0, cos_180, sin_180, 0,
                                         0, -sin_180, cos_180, 0,
                                         1000, 0, 0, 1 ]);
   var dist = getDistance(target, 'transform', matrix1, matrix2);
   assert_approx_equals(dist,
                        Math.sqrt(900 * 900 + 50 * 50 + 10 * 10 +  // translate
                                  Math.PI * Math.PI +              // rotate
                                  (Math.PI / 4) * (Math.PI / 4)),  // skew angle
-                       epsilon,
+                       EPSILON,
                        'distance of matrix');
 }, 'Test distance of matrix3d functions with skew factors');
 
 test(function(t) {
   var target = addDiv(t);
   var dist =
     getDistance(target, 'transform',
                 'rotate(180deg) translate(1000px)',
                 'rotate(360deg) translate(0px)');
-  assert_equals(dist, Math.sqrt(1000 * 1000 + Math.PI * Math.PI),
-                'distance of transform lists');
+  assert_approx_equals(dist, Math.sqrt(1000 * 1000 + Math.PI * Math.PI), EPSILON,
+                       'distance of transform lists');
 }, 'Test distance of transform lists');
 
 test(function(t) {
   var target = addDiv(t);
   var dist = getDistance(target, 'transform',
                          'translate(100px) rotate(180deg)',
                          'translate(50px) rotate(90deg) scale(5) skew(1rad)');
   assert_approx_equals(dist,
                        Math.sqrt(50 * 50 +
                                  Math.PI / 2 * Math.PI / 2 +
                                  4 * 4 * 2 +
                                  1 * 1),
-                       epsilon,
+                       EPSILON,
                        'distance of transform lists');
 }, 'Test distance of transform lists where one has extra items');
 
 test(function(t) {
   var target = addDiv(t);
   var dist = getDistance(target, 'transform',
                          'translate(1000px) rotate3d(1, 0, 0, 180deg)',
                          'translate(1000px) scale3d(2.5, 0.5, 1)');
@@ -330,14 +330,14 @@ test(function(t) {
 
 test(function(t) {
   var target = addDiv(t);
   var dist = getDistance(target, 'transform',
                          'translate(100px) skew(1rad)',
                          'translate(1000px) rotate3d(0, 1, 0, -2rad)');
   assert_approx_equals(dist,
                        Math.sqrt(900 * 900 + 1 * 1 + 2 * 2),
-                       epsilon,
+                       EPSILON,
                        'distance of transform lists');
 }, 'Test distance of mismatched transform lists with skew function');
 
 </script>
 </html>
--- a/layout/style/nsCSSValue.cpp
+++ b/layout/style/nsCSSValue.cpp
@@ -184,47 +184,26 @@ bool nsCSSValue::operator==(const nsCSSV
     else {
       return mValue.mFloat == aOther.mValue.mFloat;
     }
   }
   return false;
 }
 
 double
-nsCSSValue::GetAngleValueInRadians() const
+nsCSSValue::GetAngleValueInDegrees() const
 {
-  double angle = GetFloatValue();
-
-  switch (GetUnit()) {
-    case eCSSUnit_Radian: return angle;
-    case eCSSUnit_Turn:   return angle * 2 * M_PI;
-    case eCSSUnit_Degree: return angle * M_PI / 180.0;
-    case eCSSUnit_Grad:   return angle * M_PI / 200.0;
-
-    default:
-      MOZ_ASSERT(false, "unrecognized angular unit");
-      return 0.0;
-  }
+  // Note that this extends the value from float to double.
+  return GetAngleValue();
 }
 
 double
-nsCSSValue::GetAngleValueInDegrees() const
+nsCSSValue::GetAngleValueInRadians() const
 {
-  double angle = GetFloatValue();
-
-  switch (GetUnit()) {
-    case eCSSUnit_Degree: return angle;
-    case eCSSUnit_Grad:   return angle * 0.9; // grad / 400 * 360
-    case eCSSUnit_Radian: return angle * 180.0 / M_PI;  // rad / 2pi * 360
-    case eCSSUnit_Turn:   return angle * 360.0;
-
-    default:
-      MOZ_ASSERT(false, "unrecognized angular unit");
-      return 0.0;
-  }
+  return GetAngleValueInDegrees() * M_PI / 180.0;
 }
 
 nscoord nsCSSValue::GetPixelLength() const
 {
   MOZ_ASSERT(IsPixelLengthUnit(), "not a fixed length unit");
 
   double scaleFactor;
   switch (mUnit) {
@@ -609,19 +588,16 @@ nsCSSValue::SizeOfExcludingThis(mozilla:
     case eCSSUnit_Point:
     case eCSSUnit_Inch:
     case eCSSUnit_Millimeter:
     case eCSSUnit_Centimeter:
     case eCSSUnit_Pica:
     case eCSSUnit_Pixel:
     case eCSSUnit_Quarter:
     case eCSSUnit_Degree:
-    case eCSSUnit_Grad:
-    case eCSSUnit_Turn:
-    case eCSSUnit_Radian:
     case eCSSUnit_Hertz:
     case eCSSUnit_Kilohertz:
     case eCSSUnit_Seconds:
     case eCSSUnit_Milliseconds:
     case eCSSUnit_FlexFraction:
       break;
 
     default:
--- a/layout/style/nsCSSValue.h
+++ b/layout/style/nsCSSValue.h
@@ -330,19 +330,16 @@ enum nsCSSUnit {
   eCSSUnit_Millimeter   = 902,    // (float) 96/25.4 CSS pixels
   eCSSUnit_Centimeter   = 903,    // (float) 96/2.54 CSS pixels
   eCSSUnit_Pica         = 904,    // (float) 12 points == 16 CSS pixls
   eCSSUnit_Quarter      = 905,    // (float) 96/101.6 CSS pixels
   eCSSUnit_Pixel        = 906,    // (float) CSS pixel unit
 
   // Angular units
   eCSSUnit_Degree       = 1000,    // (float) 360 per circle
-  eCSSUnit_Grad         = 1001,    // (float) 400 per circle
-  eCSSUnit_Radian       = 1002,    // (float) 2*pi per circle
-  eCSSUnit_Turn         = 1003,    // (float) 1 per circle
 
   // Frequency units
   eCSSUnit_Hertz        = 2000,    // (float) 1/seconds
   eCSSUnit_Kilohertz    = 2001,    // (float) 1000 Hertz
 
   // Time units
   eCSSUnit_Seconds      = 3000,    // (float) Standard time
   eCSSUnit_Milliseconds = 3001,    // (float) 1/1000 second
@@ -428,17 +425,17 @@ public:
     { return IsPixelLengthUnit(mUnit); }
   static bool IsPercentLengthUnit(nsCSSUnit aUnit)
     { return aUnit == eCSSUnit_Percent; }
   bool      IsPercentLengthUnit()
     { return IsPercentLengthUnit(mUnit); }
   static bool IsFloatUnit(nsCSSUnit aUnit)
     { return eCSSUnit_Number <= aUnit; }
   bool      IsAngularUnit() const
-    { return eCSSUnit_Degree <= mUnit && mUnit <= eCSSUnit_Turn; }
+    { return eCSSUnit_Degree == mUnit; }
   bool      IsFrequencyUnit() const
     { return eCSSUnit_Hertz <= mUnit && mUnit <= eCSSUnit_Kilohertz; }
   bool      IsTimeUnit() const
     { return eCSSUnit_Seconds <= mUnit && mUnit <= eCSSUnit_Milliseconds; }
   bool      IsCalcUnit() const
     { return eCSSUnit_Calc <= mUnit && mUnit <= eCSSUnit_Calc_Plus; }
 
   bool      UnitHasStringValue() const
@@ -470,18 +467,17 @@ public:
   {
     MOZ_ASSERT(eCSSUnit_Number <= mUnit, "not a float value");
     MOZ_ASSERT(!mozilla::IsNaN(mValue.mFloat));
     return mValue.mFloat;
   }
 
   float GetAngleValue() const
   {
-    MOZ_ASSERT(eCSSUnit_Degree <= mUnit && mUnit <= eCSSUnit_Turn,
-               "not an angle value");
+    MOZ_ASSERT(eCSSUnit_Degree == mUnit, "not an angle value");
     return mValue.mFloat;
   }
 
   // Converts any angle to radians.
   double GetAngleValueInRadians() const;
 
   // Converts any angle to degrees.
   double GetAngleValueInDegrees() const;
--- a/layout/style/nsComputedDOMStyle.cpp
+++ b/layout/style/nsComputedDOMStyle.cpp
@@ -3882,28 +3882,16 @@ nsComputedDOMStyle::SetValueToCoord(nsRO
         SetValueToCalc(calc, aValue);
       }
       break;
 
     case eStyleUnit_Degree:
       aValue->SetDegree(aCoord.GetAngleValue());
       break;
 
-    case eStyleUnit_Grad:
-      aValue->SetGrad(aCoord.GetAngleValue());
-      break;
-
-    case eStyleUnit_Radian:
-      aValue->SetRadian(aCoord.GetAngleValue());
-      break;
-
-    case eStyleUnit_Turn:
-      aValue->SetTurn(aCoord.GetAngleValue());
-      break;
-
     case eStyleUnit_FlexFraction: {
       nsAutoString tmpStr;
       nsStyleUtil::AppendCSSNumber(aCoord.GetFlexFractionValue(), tmpStr);
       tmpStr.AppendLiteral("fr");
       aValue->SetString(tmpStr);
       break;
     }
 
--- a/layout/style/nsROCSSPrimitiveValue.cpp
+++ b/layout/style/nsROCSSPrimitiveValue.cpp
@@ -104,34 +104,16 @@ nsROCSSPrimitiveValue::GetCssText(nsAStr
         break;
       }
     case CSS_DEG:
       {
         nsStyleUtil::AppendCSSNumber(mValue.mFloat, tmpStr);
         tmpStr.AppendLiteral("deg");
         break;
       }
-    case CSS_GRAD:
-      {
-        nsStyleUtil::AppendCSSNumber(mValue.mFloat, tmpStr);
-        tmpStr.AppendLiteral("grad");
-        break;
-      }
-    case CSS_RAD:
-      {
-        nsStyleUtil::AppendCSSNumber(mValue.mFloat, tmpStr);
-        tmpStr.AppendLiteral("rad");
-        break;
-      }
-    case CSS_TURN:
-      {
-        nsStyleUtil::AppendCSSNumber(mValue.mFloat, tmpStr);
-        tmpStr.AppendLiteral("turn");
-        break;
-      }
     case CSS_S:
       {
         nsStyleUtil::AppendCSSNumber(mValue.mFloat, tmpStr);
         tmpStr.Append('s');
         break;
       }
     case CSS_CM:
     case CSS_MM:
@@ -164,148 +146,16 @@ nsROCSSPrimitiveValue::GetCssText(nsStri
 
 uint16_t
 nsROCSSPrimitiveValue::CssValueType() const
 {
   return CSSValue::CSS_PRIMITIVE_VALUE;
 }
 
 void
-nsROCSSPrimitiveValue::SetFloatValue(uint16_t aType, float aVal,
-                                     ErrorResult& aRv)
-{
-  aRv.Throw(NS_ERROR_DOM_NO_MODIFICATION_ALLOWED_ERR);
-}
-
-float
-nsROCSSPrimitiveValue::GetFloatValue(uint16_t aUnitType, ErrorResult& aRv)
-{
-  switch(aUnitType) {
-    case CSS_PX:
-      if (mType == CSS_PX) {
-        return nsPresContext::AppUnitsToFloatCSSPixels(mValue.mAppUnits);
-      }
-
-      break;
-    case CSS_CM:
-      if (mType == CSS_PX) {
-        return mValue.mAppUnits * CM_PER_INCH_FLOAT / AppUnitsPerCSSInch();
-      }
-
-      break;
-    case CSS_MM:
-      if (mType == CSS_PX) {
-        return mValue.mAppUnits * MM_PER_INCH_FLOAT / AppUnitsPerCSSInch();
-      }
-
-      break;
-    case CSS_IN:
-      if (mType == CSS_PX) {
-        return mValue.mAppUnits / AppUnitsPerCSSInch();
-      }
-
-      break;
-    case CSS_PT:
-      if (mType == CSS_PX) {
-        return mValue.mAppUnits * POINTS_PER_INCH_FLOAT / AppUnitsPerCSSInch();
-      }
-
-      break;
-    case CSS_PC:
-      if (mType == CSS_PX) {
-        return mValue.mAppUnits * 6.0f / AppUnitsPerCSSInch();
-      }
-
-      break;
-    case CSS_PERCENTAGE:
-      if (mType == CSS_PERCENTAGE) {
-        return mValue.mFloat * 100;
-      }
-
-      break;
-    case CSS_NUMBER:
-      if (mType == CSS_NUMBER) {
-        return mValue.mFloat;
-      }
-      if (mType == CSS_NUMBER_INT32) {
-        return mValue.mInt32;
-      }
-      if (mType == CSS_NUMBER_UINT32) {
-        return mValue.mUint32;
-      }
-
-      break;
-    case CSS_UNKNOWN:
-    case CSS_EMS:
-    case CSS_EXS:
-    case CSS_DEG:
-    case CSS_RAD:
-    case CSS_GRAD:
-    case CSS_MS:
-    case CSS_S:
-    case CSS_HZ:
-    case CSS_KHZ:
-    case CSS_DIMENSION:
-    case CSS_STRING:
-    case CSS_URI:
-    case CSS_IDENT:
-    case CSS_ATTR:
-    case CSS_COUNTER:
-    case CSS_RGBCOLOR:
-      break;
-  }
-
-  aRv.Throw(NS_ERROR_DOM_INVALID_ACCESS_ERR);
-  return 0;
-}
-
-void
-nsROCSSPrimitiveValue::SetStringValue(uint16_t aType, const nsAString& aString,
-                                      mozilla::ErrorResult& aRv)
-{
-  aRv.Throw(NS_ERROR_DOM_NO_MODIFICATION_ALLOWED_ERR);
-}
-
-void
-nsROCSSPrimitiveValue::GetStringValue(nsString& aReturn, ErrorResult& aRv)
-{
-  switch (mType) {
-    case CSS_IDENT:
-      CopyUTF8toUTF16(nsCSSKeywords::GetStringValue(mValue.mKeyword), aReturn);
-      break;
-    case CSS_STRING:
-    case CSS_ATTR:
-      aReturn.Assign(mValue.mString);
-      break;
-    case CSS_URI: {
-      nsAutoCString spec;
-      if (mValue.mURI) {
-        nsresult rv = mValue.mURI->GetSpec(spec);
-        if (NS_FAILED(rv)) {
-          aRv.Throw(rv);
-          return;
-        }
-      }
-      CopyUTF8toUTF16(spec, aReturn);
-      break;
-    }
-    default:
-      aReturn.Truncate();
-      aRv.Throw(NS_ERROR_DOM_INVALID_ACCESS_ERR);
-      return;
-  }
-}
-
-void
-nsROCSSPrimitiveValue::GetCounterValue(ErrorResult& aRv)
-{
-  aRv.Throw(NS_ERROR_NOT_IMPLEMENTED);
-}
-
-void
 nsROCSSPrimitiveValue::SetNumber(float aValue)
 {
   Reset();
   mValue.mFloat = aValue;
   mType = CSS_NUMBER;
 }
 
 void
@@ -336,40 +186,16 @@ void
 nsROCSSPrimitiveValue::SetDegree(float aValue)
 {
   Reset();
   mValue.mFloat = aValue;
   mType = CSS_DEG;
 }
 
 void
-nsROCSSPrimitiveValue::SetGrad(float aValue)
-{
-  Reset();
-  mValue.mFloat = aValue;
-  mType = CSS_GRAD;
-}
-
-void
-nsROCSSPrimitiveValue::SetRadian(float aValue)
-{
-  Reset();
-  mValue.mFloat = aValue;
-  mType = CSS_RAD;
-}
-
-void
-nsROCSSPrimitiveValue::SetTurn(float aValue)
-{
-  Reset();
-  mValue.mFloat = aValue;
-  mType = CSS_TURN;
-}
-
-void
 nsROCSSPrimitiveValue::SetAppUnits(nscoord aValue)
 {
   Reset();
   mValue.mAppUnits = aValue;
   mType = CSS_PX;
 }
 
 void
--- a/layout/style/nsROCSSPrimitiveValue.h
+++ b/layout/style/nsROCSSPrimitiveValue.h
@@ -31,60 +31,47 @@ public:
     CSS_EXS,
     CSS_PX,
     CSS_CM,
     CSS_MM,
     CSS_IN,
     CSS_PT,
     CSS_PC,
     CSS_DEG,
-    CSS_RAD,
-    CSS_GRAD,
     CSS_MS,
     CSS_S,
     CSS_HZ,
     CSS_KHZ,
     CSS_DIMENSION,
     CSS_STRING,
     CSS_URI,
     CSS_IDENT,
     CSS_ATTR,
     CSS_COUNTER,
     CSS_RGBCOLOR,
-    CSS_TURN,
     CSS_NUMBER_INT32,
     CSS_NUMBER_UINT32,
   };
 
   // CSSValue
   void GetCssText(nsString& aText, mozilla::ErrorResult& aRv) final;
   uint16_t CssValueType() const final;
 
   // CSSPrimitiveValue
   uint16_t PrimitiveType();
-  void SetFloatValue(uint16_t aUnitType, float aValue,
-                     mozilla::ErrorResult& aRv);
-  float GetFloatValue(uint16_t aUnitType, mozilla::ErrorResult& aRv);
-  void GetStringValue(nsString& aString, mozilla::ErrorResult& aRv);
-  void SetStringValue(uint16_t aUnitType, const nsAString& aString,
-                      mozilla::ErrorResult& aRv);
-  void GetCounterValue(mozilla::ErrorResult& aRv);
 
   // nsROCSSPrimitiveValue
   nsROCSSPrimitiveValue();
 
   nsresult GetCssText(nsAString& aText);
   void SetNumber(float aValue);
   void SetNumber(int32_t aValue);
   void SetNumber(uint32_t aValue);
   void SetPercent(float aValue);
   void SetDegree(float aValue);
-  void SetGrad(float aValue);
-  void SetRadian(float aValue);
-  void SetTurn(float aValue);
   void SetAppUnits(nscoord aValue);
   void SetAppUnits(float aValue);
   void SetIdent(nsCSSKeyword aKeyword);
   // FIXME: CSS_STRING should imply a string with "" and a need for escaping.
   void SetString(const nsACString& aString, uint16_t aType = CSS_STRING);
   // FIXME: CSS_STRING should imply a string with "" and a need for escaping.
   void SetString(const nsAString& aString, uint16_t aType = CSS_STRING);
   void SetURI(nsIURI *aURI);
--- a/layout/style/nsStyleCoord.cpp
+++ b/layout/style/nsStyleCoord.cpp
@@ -64,19 +64,16 @@ bool nsStyleCoord::operator==(const nsSt
     case eStyleUnit_Null:
     case eStyleUnit_Normal:
     case eStyleUnit_Auto:
     case eStyleUnit_None:
       return true;
     case eStyleUnit_Percent:
     case eStyleUnit_Factor:
     case eStyleUnit_Degree:
-    case eStyleUnit_Grad:
-    case eStyleUnit_Radian:
-    case eStyleUnit_Turn:
     case eStyleUnit_FlexFraction:
       return mValue.mFloat == aOther.mValue.mFloat;
     case eStyleUnit_Coord:
     case eStyleUnit_Integer:
     case eStyleUnit_Enumerated:
       return mValue.mInt == aOther.mValue.mInt;
     case eStyleUnit_Calc:
       return *this->GetCalcValue() == *aOther.GetCalcValue();
@@ -118,30 +115,16 @@ void nsStyleCoord::SetPercentValue(float
 
 void nsStyleCoord::SetFactorValue(float aValue)
 {
   Reset();
   mUnit = eStyleUnit_Factor;
   mValue.mFloat = aValue;
 }
 
-void nsStyleCoord::SetAngleValue(float aValue, nsStyleUnit aUnit)
-{
-  Reset();
-  if (aUnit == eStyleUnit_Degree ||
-      aUnit == eStyleUnit_Grad ||
-      aUnit == eStyleUnit_Radian ||
-      aUnit == eStyleUnit_Turn) {
-    mUnit = aUnit;
-    mValue.mFloat = aValue;
-  } else {
-    MOZ_ASSERT_UNREACHABLE("not an angle value");
-  }
-}
-
 void nsStyleCoord::SetFlexFractionValue(float aValue)
 {
   Reset();
   mUnit = eStyleUnit_FlexFraction;
   mValue.mFloat = aValue;
 }
 
 void nsStyleCoord::SetCalcValue(Calc* aValue)
@@ -173,34 +156,24 @@ void nsStyleCoord::SetNoneValue()
   mValue.mInt = 0;
 }
 
 // accessors that are not inlined
 
 double
 nsStyleCoord::GetAngleValueInDegrees() const
 {
-  return GetAngleValueInRadians() * (180.0 / M_PI);
+  // Note that this extends the value from float to double.
+  return GetAngleValue();
 }
 
 double
 nsStyleCoord::GetAngleValueInRadians() const
 {
-  double angle = mValue.mFloat;
-
-  switch (GetUnit()) {
-  case eStyleUnit_Radian: return angle;
-  case eStyleUnit_Turn:   return angle * 2 * M_PI;
-  case eStyleUnit_Degree: return angle * M_PI / 180.0;
-  case eStyleUnit_Grad:   return angle * M_PI / 200.0;
-
-  default:
-    MOZ_ASSERT_UNREACHABLE("unrecognized angular unit");
-    return 0.0;
-  }
+  return GetAngleValueInDegrees() * M_PI / 180.0;
 }
 
 nscoord
 nsStyleCoord::ComputeComputedCalc(nscoord aPercentageBasis) const
 {
   Calc* calc = GetCalcValue();
   return calc->mLength +
          NSToCoordFloorClamped(aPercentageBasis * calc->mPercent);
--- a/layout/style/nsStyleCoord.h
+++ b/layout/style/nsStyleCoord.h
@@ -50,19 +50,16 @@ enum LogicalCorner
 enum nsStyleUnit : uint8_t {
   eStyleUnit_Null         = 0,      // (no value) value is not specified
   eStyleUnit_Normal       = 1,      // (no value)
   eStyleUnit_Auto         = 2,      // (no value)
   eStyleUnit_None         = 3,      // (no value)
   eStyleUnit_Percent      = 10,     // (float) 1.0 == 100%
   eStyleUnit_Factor       = 11,     // (float) a multiplier
   eStyleUnit_Degree       = 12,     // (float) angle in degrees
-  eStyleUnit_Grad         = 13,     // (float) angle in grads
-  eStyleUnit_Radian       = 14,     // (float) angle in radians
-  eStyleUnit_Turn         = 15,     // (float) angle in turns
   eStyleUnit_FlexFraction = 16,     // (float) <flex> in fr units
   eStyleUnit_Coord        = 20,     // (nscoord) value is twips
   eStyleUnit_Integer      = 30,     // (int) value is simple integer
   eStyleUnit_Enumerated   = 32,     // (int) value has enumerated meaning
 
   // The following are reference counted allocated types.
   eStyleUnit_Calc         = 40,     // (Calc*) calc() toplevel; always present
                                     // to distinguish 50% from calc(50%), etc.
@@ -148,17 +145,17 @@ public:
   bool           operator!=(const nsStyleCoord& aOther) const;
 
   nsStyleUnit GetUnit() const {
     NS_ASSERTION(mUnit != eStyleUnit_Null, "reading uninitialized value");
     return mUnit;
   }
 
   bool IsAngleValue() const {
-    return eStyleUnit_Degree <= mUnit && mUnit <= eStyleUnit_Turn;
+    return eStyleUnit_Degree == mUnit;
   }
 
   static bool IsCalcUnit(nsStyleUnit aUnit) {
     return aUnit == eStyleUnit_Calc;
   }
 
   static bool IsPointerUnit(nsStyleUnit aUnit) {
     return IsCalcUnit(aUnit);
@@ -251,17 +248,16 @@ public:
   // Sets to null and releases any refcounted objects.  Only use this if the
   // object is initialized (i.e. don't use it in nsStyleCoord constructors).
   void Reset();
 
   void  SetCoordValue(nscoord aValue);
   void  SetIntValue(int32_t aValue, nsStyleUnit aUnit);
   void  SetPercentValue(float aValue);
   void  SetFactorValue(float aValue);
-  void  SetAngleValue(float aValue, nsStyleUnit aUnit);
   void  SetFlexFractionValue(float aValue);
   void  SetNormalValue();
   void  SetAutoValue();
   void  SetNoneValue();
   void  SetCalcValue(Calc* aValue);
   template<typename T,
            typename = typename std::enable_if<std::is_enum<T>::value>::type>
   void SetEnumValue(T aValue)
@@ -489,22 +485,18 @@ inline float nsStyleCoord::GetFactorOrPe
   if (mUnit == eStyleUnit_Factor || mUnit == eStyleUnit_Percent) {
     return mValue.mFloat;
   }
   return 0.0f;
 }
 
 inline float nsStyleCoord::GetAngleValue() const
 {
-  NS_ASSERTION(mUnit >= eStyleUnit_Degree &&
-               mUnit <= eStyleUnit_Turn, "not an angle value");
-  if (mUnit >= eStyleUnit_Degree && mUnit <= eStyleUnit_Turn) {
-    return mValue.mFloat;
-  }
-  return 0.0f;
+  MOZ_ASSERT(mUnit == eStyleUnit_Degree);
+  return mValue.mFloat;
 }
 
 inline float nsStyleCoord::GetFlexFractionValue() const
 {
   NS_ASSERTION(mUnit == eStyleUnit_FlexFraction, "not a fr value");
   if (mUnit == eStyleUnit_FlexFraction) {
     return mValue.mFloat;
   }
--- a/layout/style/nsStyleUtil.cpp
+++ b/layout/style/nsStyleUtil.cpp
@@ -204,19 +204,16 @@ nsStyleUtil::AppendAngleValue(const nsSt
   MOZ_ASSERT(aAngle.IsAngleValue(), "Should have angle value");
 
   // Append number.
   AppendCSSNumber(aAngle.GetAngleValue(), aResult);
 
   // Append unit.
   switch (aAngle.GetUnit()) {
     case eStyleUnit_Degree: aResult.AppendLiteral("deg");  break;
-    case eStyleUnit_Grad:   aResult.AppendLiteral("grad"); break;
-    case eStyleUnit_Radian: aResult.AppendLiteral("rad");  break;
-    case eStyleUnit_Turn:   aResult.AppendLiteral("turn"); break;
     default: MOZ_ASSERT_UNREACHABLE("unrecognized angle unit");
   }
 }
 
 /* static */ void
 nsStyleUtil::AppendPaintOrderValue(uint8_t aValue,
                                    nsAString& aResult)
 {
--- a/layout/style/test/test_computed_style.html
+++ b/layout/style/test/test_computed_style.html
@@ -220,17 +220,17 @@ var noframe_container = document.getElem
       "linear gradient 2" ],
     [ "linear-gradient(to right, red, blue)",
       "linear-gradient(to right, rgb(255, 0, 0), rgb(0, 0, 255))",
       "linear gradient 3" ],
     [ "linear-gradient(-45deg, red, blue)",
       "linear-gradient(-45deg, rgb(255, 0, 0), rgb(0, 0, 255))",
       "linear gradient with angle in degrees" ],
     [ "linear-gradient(-0.125turn, red, blue)",
-      "linear-gradient(-0.125turn, rgb(255, 0, 0), rgb(0, 0, 255))",
+      "linear-gradient(-45deg, rgb(255, 0, 0), rgb(0, 0, 255))",
       "linear gradient with angle in turns" ],
   ];
 
   var p = document.createElement("p");
   var cs = getComputedStyle(p, "");
   frame_container.appendChild(p);
 
   for (var i = 0; i < backgroundImages.length; ++i) {
--- a/layout/style/test/test_transitions_per_property.html
+++ b/layout/style/test/test_transitions_per_property.html
@@ -988,29 +988,29 @@ var filterTests = [
   { start: "brightness(0.25)", end: "brightness(75%)",
     expected: ["brightness", 0.375] },
   { start: "contrast(25%)", end: "contrast(0.75)",
     expected: ["contrast", 0.375] },
   // hue-rotate with different angle values
   { start: "hue-rotate(0deg)", end: "hue-rotate(720deg)",
     expected: ["hue-rotate", "180deg"] },
   { start: "hue-rotate(0rad)", end: "hue-rotate("+4*Math.PI+"rad)",
-    expected: ["hue-rotate", Math.PI.toFixed(5)+"rad"] },
+    expected: ["hue-rotate", "180deg"] },
   { start: "hue-rotate(0grad)", end: "hue-rotate(800grad)",
-    expected: ["hue-rotate", "200grad"] },
+    expected: ["hue-rotate", "180deg"] },
   { start: "hue-rotate(0turn)", end: "hue-rotate(2turn)",
-    expected: ["hue-rotate", "0.5turn"] },
+    expected: ["hue-rotate", "180deg"] },
   { start: "hue-rotate(0deg)", end: "hue-rotate("+4*Math.PI+"rad)",
-    expected: ["hue-rotate", Math.PI.toFixed(5)+"rad"] },
+    expected: ["hue-rotate", "180deg"] },
   { start: "hue-rotate(0turn)", end: "hue-rotate(800grad)",
-    expected: ["hue-rotate", Math.PI.toFixed(5)+"rad"] },
+    expected: ["hue-rotate", "180deg"] },
   { start: "hue-rotate(0grad)", end: "hue-rotate("+4*Math.PI+"rad)",
-    expected: ["hue-rotate", Math.PI.toFixed(5)+"rad"] },
+    expected: ["hue-rotate", "180deg"] },
   { start: "hue-rotate(0grad)", end: "hue-rotate(0turn)",
-    expected: ["hue-rotate", "0rad"] },
+    expected: ["hue-rotate", "0deg"] },
   // multiple matching functions, same length
   { start: "contrast(25%) brightness(0.25) blur(25px) sepia(75%)",
     end: "contrast(75%) brightness(0.75) blur(75px) sepia(25%)",
     expected: ["contrast", 0.375, "brightness", 0.375, "blur", 37.5, "sepia", 0.625] },
   { start: "invert(25%) brightness(0.25) blur(25px) invert(50%) brightness(0.5) blur(50px)",
     end: "invert(75%) brightness(0.75) blur(75px)",
     expected: ["invert", 0.375, "brightness", 0.375, "blur", 37.5, "invert", 0.375, "brightness", 0.625, "blur", 37.5] },
   // multiple matching functions, different length
--- a/servo/components/style/gecko/conversions.rs
+++ b/servo/components/style/gecko/conversions.rs
@@ -6,17 +6,17 @@
 //! Ideally, it would be in geckolib itself, but coherence
 //! forces us to keep the traits and implementations here
 
 #![allow(unsafe_code)]
 
 use app_units::Au;
 use gecko::values::GeckoStyleCoordConvertible;
 use gecko_bindings::bindings;
-use gecko_bindings::structs::{self, nsCSSUnit, nsStyleCoord_CalcValue};
+use gecko_bindings::structs::{self, nsStyleCoord_CalcValue};
 use gecko_bindings::structs::{nsresult, SheetType, nsStyleImage};
 use gecko_bindings::sugar::ns_style_coord::{CoordData, CoordDataMut, CoordDataValue};
 use std::f32::consts::PI;
 use stylesheets::{Origin, RulesMutateError};
 use values::computed::{Angle, CalcLengthOrPercentage, Gradient, Image};
 use values::computed::{Integer, LengthOrPercentage, LengthOrPercentageOrAuto, NonNegativeLengthOrPercentageOrAuto};
 use values::computed::{Percentage, TextAlign};
 use values::computed::image::LineDirection;
@@ -123,45 +123,17 @@ impl From<nsStyleCoord_CalcValue> for No
         } else {
             other.into()
         })
     }
 }
 
 impl From<Angle> for CoordDataValue {
     fn from(reference: Angle) -> Self {
-        match reference {
-            Angle::Deg(val) => CoordDataValue::Degree(val),
-            Angle::Grad(val) => CoordDataValue::Grad(val),
-            Angle::Rad(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::Deg(val) => (val, nsCSSUnit::eCSSUnit_Degree),
-            Angle::Grad(val) => (val, nsCSSUnit::eCSSUnit_Grad),
-            Angle::Rad(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::Deg(value),
-            nsCSSUnit::eCSSUnit_Grad => Angle::Grad(value),
-            nsCSSUnit::eCSSUnit_Radian => Angle::Rad(value),
-            nsCSSUnit::eCSSUnit_Turn => Angle::Turn(value),
-            _ => panic!("Unexpected unit for angle"),
-        }
+        CoordDataValue::Degree(reference.degrees())
     }
 }
 
 fn line_direction(horizontal: LengthOrPercentage, vertical: LengthOrPercentage) -> LineDirection {
     use values::computed::position::Position;
     use values::specified::position::{X, Y};
 
     let horizontal_percentage = match horizontal {
--- a/servo/components/style/gecko/values.rs
+++ b/servo/components/style/gecko/values.rs
@@ -320,20 +320,17 @@ impl<T: GeckoStyleCoordConvertible> Geck
 
 impl GeckoStyleCoordConvertible for Angle {
     fn to_gecko_style_coord<T: CoordDataMut>(&self, coord: &mut T) {
         coord.set_value(CoordDataValue::from(*self));
     }
 
     fn from_gecko_style_coord<T: CoordData>(coord: &T) -> Option<Self> {
         match coord.as_value() {
-            CoordDataValue::Degree(val) => Some(Angle::Deg(val)),
-            CoordDataValue::Grad(val) => Some(Angle::Grad(val)),
-            CoordDataValue::Radian(val) => Some(Angle::Rad(val)),
-            CoordDataValue::Turn(val) => Some(Angle::Turn(val)),
+            CoordDataValue::Degree(val) => Some(Angle::from_degrees(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/sugar/ns_css_value.rs
+++ b/servo/components/style/gecko_bindings/sugar/ns_css_value.rs
@@ -193,29 +193,29 @@ impl nsCSSValue {
 
     /// 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`.
+    /// Panics if the unit is not `eCSSUnit_Degree`.
+    #[inline]
     pub fn get_angle(&self) -> Angle {
-        Angle::from_gecko_values(self.float_unchecked(), self.mUnit)
+        debug_assert_eq!(self.mUnit, nsCSSUnit::eCSSUnit_Degree);
+        Angle::from_degrees(self.float_unchecked())
     }
 
     /// 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;
+        self.mUnit = nsCSSUnit::eCSSUnit_Degree;
         unsafe {
-            *self.mValue.mFloat.as_mut() = value;
+            *self.mValue.mFloat.as_mut() = angle.degrees();
         }
     }
 
     /// Set to a pair value
     ///
     /// This is only supported on the main thread.
     pub fn set_pair(&mut self, x: &nsCSSValue, y: &nsCSSValue) {
         unsafe { bindings::Gecko_CSSValue_SetPair(self, x, y) }
--- a/servo/components/style/gecko_bindings/sugar/ns_style_coord.rs
+++ b/servo/components/style/gecko_bindings/sugar/ns_style_coord.rs
@@ -195,22 +195,16 @@ pub enum CoordDataValue {
     /// eStyleUnit_None
     None,
     /// eStyleUnit_Percent
     Percent(f32),
     /// eStyleUnit_Factor
     Factor(f32),
     /// eStyleUnit_Degree
     Degree(f32),
-    /// eStyleUnit_Grad
-    Grad(f32),
-    /// eStyleUnit_Radian
-    Radian(f32),
-    /// eStyleUnit_Turn
-    Turn(f32),
     /// eStyleUnit_FlexFraction
     FlexFraction(f32),
     /// eStyleUnit_Coord
     Coord(nscoord),
     /// eStyleUnit_Integer
     Integer(i32),
     /// eStyleUnit_Enumerated
     Enumerated(u32),
@@ -312,28 +306,16 @@ pub unsafe trait CoordDataMut: CoordData
                 Factor(f) => {
                     *unit = eStyleUnit_Factor;
                     *union.mFloat.as_mut() = f;
                 },
                 Degree(f) => {
                     *unit = eStyleUnit_Degree;
                     *union.mFloat.as_mut() = f;
                 },
-                Grad(f) => {
-                    *unit = eStyleUnit_Grad;
-                    *union.mFloat.as_mut() = f;
-                },
-                Radian(f) => {
-                    *unit = eStyleUnit_Radian;
-                    *union.mFloat.as_mut() = f;
-                },
-                Turn(f) => {
-                    *unit = eStyleUnit_Turn;
-                    *union.mFloat.as_mut() = f;
-                },
                 FlexFraction(f) => {
                     *unit = eStyleUnit_FlexFraction;
                     *union.mFloat.as_mut() = f;
                 },
                 Coord(coord) => {
                     *unit = eStyleUnit_Coord;
                     *union.mInt.as_mut() = coord;
                 },
@@ -388,19 +370,16 @@ pub unsafe trait CoordData {
             match self.unit() {
                 eStyleUnit_Null => Null,
                 eStyleUnit_Normal => Normal,
                 eStyleUnit_Auto => Auto,
                 eStyleUnit_None => None,
                 eStyleUnit_Percent => Percent(self.get_float()),
                 eStyleUnit_Factor => Factor(self.get_float()),
                 eStyleUnit_Degree => Degree(self.get_float()),
-                eStyleUnit_Grad => Grad(self.get_float()),
-                eStyleUnit_Radian => Radian(self.get_float()),
-                eStyleUnit_Turn => Turn(self.get_float()),
                 eStyleUnit_FlexFraction => FlexFraction(self.get_float()),
                 eStyleUnit_Coord => Coord(self.get_integer()),
                 eStyleUnit_Integer => Integer(self.get_integer()),
                 eStyleUnit_Enumerated => Enumerated(self.get_integer() as u32),
                 eStyleUnit_Calc => Calc(self.get_calc_value()),
             }
         }
     }
@@ -408,19 +387,16 @@ pub unsafe trait CoordData {
     #[inline]
     /// Pretend inner value is a float; obtain it.
     unsafe fn get_float(&self) -> f32 {
         use gecko_bindings::structs::nsStyleUnit::*;
         debug_assert!(
             self.unit() == eStyleUnit_Percent ||
                 self.unit() == eStyleUnit_Factor ||
                 self.unit() == eStyleUnit_Degree ||
-                self.unit() == eStyleUnit_Grad ||
-                self.unit() == eStyleUnit_Radian ||
-                self.unit() == eStyleUnit_Turn ||
                 self.unit() == eStyleUnit_FlexFraction
         );
         *self.union().mFloat.as_ref()
     }
 
     #[inline]
     /// Pretend inner value is an int; obtain it.
     unsafe fn get_integer(&self) -> i32 {
--- a/servo/components/style/values/computed/angle.rs
+++ b/servo/components/style/values/computed/angle.rs
@@ -1,128 +1,97 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 //! Computed angles.
 
 use num_traits::Zero;
+use std::fmt::{self, Write};
 use std::{f32, f64};
 use std::f64::consts::PI;
 use std::ops::Add;
+use style_traits::{CssWriter, ToCss};
 use values::CSSFloat;
-use values::animated::{Animate, Procedure};
 use values::distance::{ComputeSquaredDistance, SquaredDistance};
 
-/// A computed angle.
-#[animate(fallback = "Self::animate_fallback")]
+/// A computed angle in degrees.
 #[cfg_attr(feature = "servo", derive(Deserialize, Serialize))]
-#[derive(
-    Animate, Clone, Copy, Debug, MallocSizeOf, PartialEq, PartialOrd, ToAnimatedZero, ToCss,
-)]
-pub enum Angle {
-    /// 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),
+#[derive(Animate, Clone, Copy, Debug, MallocSizeOf, PartialEq, PartialOrd, ToAnimatedZero)]
+pub struct Angle(CSSFloat);
+
+impl ToCss for Angle {
+    fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
+    where
+        W: Write,
+    {
+        self.degrees().to_css(dest)?;
+        dest.write_str("deg")
+    }
 }
 
+const RAD_PER_DEG: f64 = PI / 180.0;
+
 impl Angle {
     /// Creates a computed `Angle` value from a radian amount.
     pub fn from_radians(radians: CSSFloat) -> Self {
-        Angle::Rad(radians)
+        Angle(radians / RAD_PER_DEG as f32)
+    }
+
+    /// Creates a computed `Angle` value from a degrees amount.
+    #[inline]
+    pub fn from_degrees(degrees: CSSFloat) -> Self {
+        Angle(degrees)
     }
 
     /// Returns the amount of radians this angle represents.
     #[inline]
     pub fn radians(&self) -> CSSFloat {
         self.radians64().min(f32::MAX as f64).max(f32::MIN as f64) as f32
     }
 
     /// Returns the amount of radians this angle represents as a `f64`.
     ///
     /// Gecko stores angles as singles, but does this computation using doubles.
-    /// See nsCSSValue::GetAngleValueInRadians.
+    ///
     /// This is significant enough to mess up rounding to the nearest
     /// quarter-turn for 225 degrees, for example.
     #[inline]
     pub fn radians64(&self) -> f64 {
-        const RAD_PER_DEG: f64 = PI / 180.0;
-        const RAD_PER_GRAD: f64 = PI / 200.0;
-        const RAD_PER_TURN: f64 = PI * 2.0;
-
-        let radians = match *self {
-            Angle::Deg(val) => val as f64 * RAD_PER_DEG,
-            Angle::Grad(val) => val as f64 * RAD_PER_GRAD,
-            Angle::Turn(val) => val as f64 * RAD_PER_TURN,
-            Angle::Rad(val) => val as f64,
-        };
-        radians.min(f64::MAX).max(f64::MIN)
+        self.0 as f64 * RAD_PER_DEG as f64
     }
 
     /// Return the value in degrees.
-    pub fn degrees(&self) -> f32 {
-        use std::f32::consts::PI;
-        self.radians() * 360. / (2. * PI)
-    }
-
-    /// <https://drafts.csswg.org/css-transitions/#animtype-number>
     #[inline]
-    fn animate_fallback(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> {
-        Ok(Angle::from_radians(
-            self.radians().animate(&other.radians(), procedure)?,
-        ))
-    }
-}
-
-impl AsRef<Angle> for Angle {
-    #[inline]
-    fn as_ref(&self) -> &Self {
-        self
+    pub fn degrees(&self) -> CSSFloat {
+        self.0
     }
 }
 
 impl Add for Angle {
     type Output = Self;
 
     #[inline]
     fn add(self, rhs: Self) -> Self {
-        match (self, rhs) {
-            (Angle::Deg(x), Angle::Deg(y)) => Angle::Deg(x + y),
-            (Angle::Grad(x), Angle::Grad(y)) => Angle::Grad(x + y),
-            (Angle::Turn(x), Angle::Turn(y)) => Angle::Turn(x + y),
-            (Angle::Rad(x), Angle::Rad(y)) => Angle::Rad(x + y),
-            _ => Angle::from_radians(self.radians() + rhs.radians()),
-        }
+        Angle(self.0 + rhs.0)
     }
 }
 
 impl Zero for Angle {
     #[inline]
     fn zero() -> Self {
-        Angle::from_radians(0.0)
+        Angle(0.0)
     }
 
     #[inline]
     fn is_zero(&self) -> bool {
-        match *self {
-            Angle::Deg(val) | Angle::Grad(val) | Angle::Turn(val) | Angle::Rad(val) => val == 0.,
-        }
+        self.0 == 0.
     }
 }
 
 impl ComputeSquaredDistance for Angle {
     #[inline]
     fn compute_squared_distance(&self, other: &Self) -> Result<SquaredDistance, ()> {
         // Use the formula for calculating the distance between angles defined in SVG:
         // https://www.w3.org/TR/SVG/animate.html#complexDistances
-        self.radians64()
-            .compute_squared_distance(&other.radians64())
+        self.radians64().compute_squared_distance(&other.radians64())
     }
 }
--- a/servo/components/style/values/computed/font.rs
+++ b/servo/components/style/values/computed/font.rs
@@ -861,17 +861,17 @@ impl ToAnimatedValue for FontStyleAngle 
 
     #[inline]
     fn to_animated_value(self) -> Self::AnimatedValue {
         self.0
     }
 
     #[inline]
     fn from_animated_value(animated: Self::AnimatedValue) -> Self {
-        FontStyleAngle(Angle::Deg(
+        FontStyleAngle(Angle::from_degrees(
             animated
                 .degrees()
                 .min(specified::FONT_STYLE_OBLIQUE_MAX_ANGLE_DEGREES)
                 .max(specified::FONT_STYLE_OBLIQUE_MIN_ANGLE_DEGREES),
         ))
     }
 }
 
@@ -894,17 +894,17 @@ impl FontStyle {
         generics::FontStyle::Normal
     }
 
     /// The default angle for font-style: oblique. This is 20deg per spec:
     ///
     /// https://drafts.csswg.org/css-fonts-4/#valdef-font-style-oblique-angle
     #[inline]
     pub fn default_angle() -> FontStyleAngle {
-        FontStyleAngle(Angle::Deg(
+        FontStyleAngle(Angle::from_degrees(
             specified::DEFAULT_FONT_STYLE_OBLIQUE_ANGLE_DEGREES,
         ))
     }
 
     /// Get the font style from Gecko's nsFont struct.
     #[cfg(feature = "gecko")]
     pub fn from_gecko(style: structs::FontSlantStyle) -> Self {
         let mut angle = 0.;
@@ -914,17 +914,17 @@ impl FontStyle {
             bindings::Gecko_FontSlantStyle_Get(style, &mut normal, &mut italic, &mut angle);
         }
         if normal {
             return generics::FontStyle::Normal;
         }
         if italic {
             return generics::FontStyle::Italic;
         }
-        generics::FontStyle::Oblique(FontStyleAngle(Angle::Deg(angle)))
+        generics::FontStyle::Oblique(FontStyleAngle(Angle::from_degrees(angle)))
     }
 }
 
 impl ToCss for FontStyle {
     fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
     where
         W: fmt::Write,
     {
--- a/servo/components/style/values/generics/transform.rs
+++ b/servo/components/style/values/generics/transform.rs
@@ -5,16 +5,17 @@
 //! Generic types for CSS values that are related to transformations.
 
 use app_units::Au;
 use euclid::{self, Rect, Transform3D};
 use num_traits::Zero;
 use values::{computed, CSSFloat};
 use values::computed::length::Length as ComputedLength;
 use values::computed::length::LengthOrPercentage as ComputedLengthOrPercentage;
+use values::specified::angle::Angle as SpecifiedAngle;
 use values::specified::length::Length as SpecifiedLength;
 use values::specified::length::LengthOrPercentage as SpecifiedLengthOrPercentage;
 
 /// A generic 2D transformation matrix.
 #[allow(missing_docs)]
 #[derive(
     Clone, Copy, Debug, MallocSizeOf, PartialEq, SpecifiedValueInfo, ToComputedValue, ToCss,
 )]
@@ -389,20 +390,40 @@ impl ToAbsoluteLength for ComputedLength
 pub trait ToMatrix {
     /// Check if it is a 3d transform function.
     fn is_3d(&self) -> bool;
 
     /// Return the equivalent 3d matrix.
     fn to_3d_matrix(&self, reference_box: Option<&Rect<Au>>) -> Result<Transform3D<f64>, ()>;
 }
 
+/// A little helper to deal with both specified and computed angles.
+pub trait ToRadians {
+    /// Return the radians value as a 64-bit floating point value.
+    fn radians64(&self) -> f64;
+}
+
+impl ToRadians for computed::angle::Angle {
+    #[inline]
+    fn radians64(&self) -> f64 {
+        computed::angle::Angle::radians64(self)
+    }
+}
+
+impl ToRadians for SpecifiedAngle {
+    #[inline]
+    fn radians64(&self) -> f64 {
+        computed::angle::Angle::from_degrees(self.degrees()).radians64()
+    }
+}
+
 impl<Angle, Number, Length, Integer, LoP> ToMatrix
     for TransformOperation<Angle, Number, Length, Integer, LoP>
 where
-    Angle: Copy + AsRef<computed::angle::Angle>,
+    Angle: ToRadians + Copy,
     Number: Copy + Into<f32> + Into<f64>,
     Length: ToAbsoluteLength,
     LoP: ToAbsoluteLength,
 {
     #[inline]
     fn is_3d(&self) -> bool {
         use self::TransformOperation::*;
         match *self {
@@ -421,36 +442,36 @@ where
         use self::TransformOperation::*;
         use std::f64;
 
         const TWO_PI: f64 = 2.0f64 * f64::consts::PI;
         let reference_width = reference_box.map(|v| v.size.width);
         let reference_height = reference_box.map(|v| v.size.height);
         let matrix = match *self {
             Rotate3D(ax, ay, az, theta) => {
-                let theta = TWO_PI - theta.as_ref().radians64();
+                let theta = TWO_PI - theta.radians64();
                 let (ax, ay, az, theta) =
                     get_normalized_vector_and_angle(ax.into(), ay.into(), az.into(), theta);
                 Transform3D::create_rotation(
                     ax as f64,
                     ay as f64,
                     az as f64,
                     euclid::Angle::radians(theta),
                 )
             },
             RotateX(theta) => {
-                let theta = euclid::Angle::radians(TWO_PI - theta.as_ref().radians64());
+                let theta = euclid::Angle::radians(TWO_PI - theta.radians64());
                 Transform3D::create_rotation(1., 0., 0., theta)
             },
             RotateY(theta) => {
-                let theta = euclid::Angle::radians(TWO_PI - theta.as_ref().radians64());
+                let theta = euclid::Angle::radians(TWO_PI - theta.radians64());
                 Transform3D::create_rotation(0., 1., 0., theta)
             },
             RotateZ(theta) | Rotate(theta) => {
-                let theta = euclid::Angle::radians(TWO_PI - theta.as_ref().radians64());
+                let theta = euclid::Angle::radians(TWO_PI - theta.radians64());
                 Transform3D::create_rotation(0., 0., 1., theta)
             },
             Perspective(ref d) => {
                 let m = create_perspective_matrix(d.to_pixel_length(None)?);
                 m.cast()
             },
             Scale3D(sx, sy, sz) => Transform3D::create_scale(sx.into(), sy.into(), sz.into()),
             Scale(sx, sy) => Transform3D::create_scale(sx.into(), sy.unwrap_or(sx).into(), 1.),
@@ -474,26 +495,26 @@ where
             TranslateY(ref t) => {
                 let t = t.to_pixel_length(reference_height)? as f64;
                 Transform3D::create_translation(0., t, 0.)
             },
             TranslateZ(ref z) => {
                 Transform3D::create_translation(0., 0., z.to_pixel_length(None)? as f64)
             },
             Skew(theta_x, theta_y) => Transform3D::create_skew(
-                euclid::Angle::radians(theta_x.as_ref().radians64()),
-                euclid::Angle::radians(theta_y.map_or(0., |a| a.as_ref().radians64())),
+                euclid::Angle::radians(theta_x.radians64()),
+                euclid::Angle::radians(theta_y.map_or(0., |a| a.radians64())),
             ),
             SkewX(theta) => Transform3D::create_skew(
-                euclid::Angle::radians(theta.as_ref().radians64()),
+                euclid::Angle::radians(theta.radians64()),
                 euclid::Angle::radians(0.),
             ),
             SkewY(theta) => Transform3D::create_skew(
                 euclid::Angle::radians(0.),
-                euclid::Angle::radians(theta.as_ref().radians64()),
+                euclid::Angle::radians(theta.radians64()),
             ),
             Matrix3D(m) => m.into(),
             Matrix(m) => m.into(),
             InterpolateMatrix { .. } | AccumulateMatrix { .. } => {
                 // TODO: Convert InterpolateMatrix/AccumulateMatrix into a valid Transform3D by
                 // the reference box and do interpolation on these two Transform3D matrices.
                 // Both Gecko and Servo don't support this for computing distance, and Servo
                 // doesn't support animations on InterpolateMatrix/AccumulateMatrix, so
--- a/servo/components/style/values/specified/angle.rs
+++ b/servo/components/style/values/specified/angle.rs
@@ -2,31 +2,63 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 //! Specified angles.
 
 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,
 }
 
 impl ToCss for Angle {
     fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
     where
         W: Write,
     {
@@ -36,109 +68,70 @@ impl ToCss for Angle {
         self.value.to_css(dest)?;
         if self.was_calc {
             dest.write_str(")")?;
         }
         Ok(())
     }
 }
 
-// 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,
         }
     }
 }
 
 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),
-            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),
+            value: AngleDimension::Deg(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.
     #[inline]
     pub fn was_calc(&self) -> bool {
         self.was_calc
     }
 
-    /// Returns the amount of radians this angle represents.
+    /// Returns `0deg`.
     #[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`.
     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:
 ///
 ///   https://github.com/w3c/fxtf-drafts/issues/228
 ///
 /// See also: https://github.com/w3c/csswg-drafts/issues/1162.
 enum AllowUnitlessZeroAngle {
@@ -153,30 +146,36 @@ impl Parse for Angle {
         input: &mut Parser<'i, 't>,
     ) -> Result<Self, ParseError<'i>> {
         Self::parse_internal(context, input, AllowUnitlessZeroAngle::No)
     }
 }
 
 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>,
     ) -> Result<Self, ParseError<'i>> {
         Self::parse_internal(context, input, AllowUnitlessZeroAngle::Yes)
     }
 
     fn parse_internal<'i, 't>(
--- a/servo/components/style/values/specified/calc.rs
+++ b/servo/components/style/values/specified/calc.rs
@@ -464,41 +464,41 @@ impl CalcNode {
 
     /// Tries to simplify this expression into an `Angle` value.
     fn to_angle(&self) -> Result<Angle, ()> {
         Ok(match *self {
             CalcNode::Angle(ref angle) => angle.clone(),
             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) => {
                 let lhs = a.to_angle()?;
                 let rhs = b.to_number()?;
                 if rhs == 0. {
                     return Err(());
                 }
-                Angle::from_calc(lhs.radians() / rhs)
+                Angle::from_calc(lhs.degrees() / rhs)
             },
             CalcNode::Number(..) |
             CalcNode::Length(..) |
             CalcNode::Percentage(..) |
             CalcNode::Time(..) => return Err(()),
         })
     }
 
--- a/servo/components/style/values/specified/font.rs
+++ b/servo/components/style/values/specified/font.rs
@@ -296,17 +296,17 @@ impl SpecifiedFontStyle {
     pub fn compute_angle_degrees(angle: &Angle) -> f32 {
         angle
             .degrees()
             .max(FONT_STYLE_OBLIQUE_MIN_ANGLE_DEGREES)
             .min(FONT_STYLE_OBLIQUE_MAX_ANGLE_DEGREES)
     }
 
     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.
     pub fn parse_angle<'i, 't>(
         context: &ParserContext,
         input: &mut Parser<'i, 't>,
     ) -> Result<Angle, ParseError<'i>> {
         let angle = Angle::parse(context, input)?;
--- a/servo/components/style/values/specified/image.rs
+++ b/servo/components/style/values/specified/image.rs
@@ -10,17 +10,16 @@
 use Atom;
 use cssparser::{Parser, Token, Delimiter};
 use custom_properties::SpecifiedValue;
 use parser::{Parse, ParserContext};
 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};
 use values::{Either, None_};
 #[cfg(feature = "gecko")]
 use values::computed::{Context, Position as ComputedPosition, ToComputedValue};
 use values::generics::image::{self as generic, Circle, CompatMode, Ellipse, ShapeExtent};
 use values::generics::image::PaintWorklet;
@@ -674,17 +673,17 @@ impl GradientKind {
             return Ok(generic::GradientKind::Radial(shape, position, angle));
         }
     }
 }
 
 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")]
             LineDirection::MozPosition(
                 Some(LegacyPosition {
                     horizontal: ref x,
                     vertical: ref y,
                 }),
--- a/testing/web-platform/tests/web-animations/animation-model/animation-types/property-types.js
+++ b/testing/web-platform/tests/web-animations/animation-model/animation-types/property-types.js
@@ -2099,17 +2099,17 @@ const filterListType = {
       const idlName = propertyToIDL(property);
       const target = createTestElement(t, setup);
       const animation = target.animate({ [idlName]: ['hue-rotate(10deg)',
                                                      'hue-rotate(100rad)'] },
                                        1000);
 
       // 10deg = 0.1745rad.
       testAnimationSamples(animation, idlName,
-        [{ time: 500,    expected: 'hue-rotate(50.0873rad)' }]);
+        [{ time: 500,    expected: 'hue-rotate(2869.79deg)' }]);
     }, `${property}: hue-rotate function with different unit(deg -> rad)`);
 
     test(t => {
       const idlName = propertyToIDL(property);
       const target = createTestElement(t, setup);
       const animation = target.animate(
         { [idlName]:
           ['drop-shadow(10px 10px 10px rgba(255, 0, 0, 0.4))',