servo: Merge #16144 - style: Make numbers keep track of whether they were specified as calc() (from emilio:number-calc); r=heycam
authorEmilio Cobos Álvarez <emilio@crisal.io>
Mon, 27 Mar 2017 06:13:39 -0700
changeset 349897 c7a1b14752f4e5a1ff793d646903ebab2ed90b45
parent 349896 5a5fd4594fd2831c27eab7da97c9e3a54df58497
child 349898 9a6eebeed9047e0674bea094725b03caf26b25f9
push id88516
push userkwierso@gmail.com
push dateTue, 28 Mar 2017 00:18:55 +0000
treeherdermozilla-inbound@4346f1053ce3 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersheycam
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 #16144 - style: Make numbers keep track of whether they were specified as calc() (from emilio:number-calc); r=heycam Fixes #15960 Source-Repo: https://github.com/servo/servo Source-Revision: b6bc49225e31b7c1e3f9cc3be660bb37afe33a95
servo/components/layout/webrender_helpers.rs
servo/components/script/dom/element.rs
servo/components/style/animation.rs
servo/components/style/gecko_bindings/sugar/ns_timing_function.rs
servo/components/style/properties/gecko.mako.rs
servo/components/style/properties/helpers.mako.rs
servo/components/style/properties/helpers/animated_properties.mako.rs
servo/components/style/properties/longhand/border.mako.rs
servo/components/style/properties/longhand/box.mako.rs
servo/components/style/properties/longhand/counters.mako.rs
servo/components/style/properties/longhand/font.mako.rs
servo/components/style/properties/longhand/inherited_box.mako.rs
servo/components/style/properties/longhand/inherited_table.mako.rs
servo/components/style/properties/longhand/inherited_text.mako.rs
servo/components/style/properties/longhand/position.mako.rs
servo/components/style/properties/shorthand/position.mako.rs
servo/components/style/values/computed/image.rs
servo/components/style/values/computed/mod.rs
servo/components/style/values/specified/length.rs
servo/components/style/values/specified/mod.rs
servo/ports/geckolib/glue.rs
servo/tests/unit/style/parsing/image.rs
servo/tests/unit/style/properties/serialization.rs
--- a/servo/components/layout/webrender_helpers.rs
+++ b/servo/components/layout/webrender_helpers.rs
@@ -195,17 +195,17 @@ impl ToFilterOps for filter::T {
     fn to_filter_ops(&self) -> Vec<webrender_traits::FilterOp> {
         let mut result = Vec::with_capacity(self.filters.len());
         for filter in self.filters.iter() {
             match *filter {
                 Filter::Blur(radius) => result.push(webrender_traits::FilterOp::Blur(radius)),
                 Filter::Brightness(amount) => result.push(webrender_traits::FilterOp::Brightness(amount)),
                 Filter::Contrast(amount) => result.push(webrender_traits::FilterOp::Contrast(amount)),
                 Filter::Grayscale(amount) => result.push(webrender_traits::FilterOp::Grayscale(amount)),
-                Filter::HueRotate(angle) => result.push(webrender_traits::FilterOp::HueRotate(angle.0)),
+                Filter::HueRotate(angle) => result.push(webrender_traits::FilterOp::HueRotate(angle.radians())),
                 Filter::Invert(amount) => result.push(webrender_traits::FilterOp::Invert(amount)),
                 Filter::Opacity(amount) => result.push(webrender_traits::FilterOp::Opacity(amount.into())),
                 Filter::Saturate(amount) => result.push(webrender_traits::FilterOp::Saturate(amount)),
                 Filter::Sepia(amount) => result.push(webrender_traits::FilterOp::Sepia(amount)),
             }
         }
         result
     }
--- a/servo/components/script/dom/element.rs
+++ b/servo/components/script/dom/element.rs
@@ -491,18 +491,18 @@ impl LayoutElementHelpers for LayoutJS<E
         };
 
         if let Some(cellspacing) = cellspacing {
             let width_value = specified::Length::from_px(cellspacing as f32);
             hints.push(from_declaration(
                 shared_lock,
                 PropertyDeclaration::BorderSpacing(
                     Box::new(border_spacing::SpecifiedValue {
-                        horizontal: width_value.clone(),
-                        vertical: width_value,
+                        horizontal: width_value,
+                        vertical: None,
                     }))));
         }
 
 
         let size = if let Some(this) = self.downcast::<HTMLInputElement>() {
             // FIXME(pcwalton): More use of atoms, please!
             match (*self.unsafe_get()).get_attr_val_for_layout(&ns!(), &local_name!("type")) {
                 // Not text entry widget
--- a/servo/components/style/animation.rs
+++ b/servo/components/style/animation.rs
@@ -338,17 +338,17 @@ impl PropertyAnimation {
             }
         };
 
         self.property.update(style, progress);
     }
 
     #[inline]
     fn does_animate(&self) -> bool {
-        self.property.does_animate() && self.duration != Time(0.0)
+        self.property.does_animate() && self.duration.seconds() != 0.0
     }
 
     /// Whether this animation has the same end value as another one.
     #[inline]
     pub fn has_the_same_end_value_as(&self, other: &Self) -> bool {
         self.property.has_the_same_end_value_as(&other.property)
     }
 }
@@ -676,17 +676,17 @@ pub fn update_style_for_animation(contex
 
             let mut new_style = (*style).clone();
 
             for transition_property in &animation.properties_changed {
                 debug!("update_style_for_animation: scanning prop {:?} for animation \"{}\"",
                        transition_property, name);
                 match PropertyAnimation::from_transition_property(*transition_property,
                                                                   timing_function,
-                                                                  Time(relative_duration as f32),
+                                                                  Time::from_seconds(relative_duration as f32),
                                                                   &from_style,
                                                                   &target_style) {
                     Some(property_animation) => {
                         debug!("update_style_for_animation: got property animation for prop {:?}", transition_property);
                         debug!("update_style_for_animation: {:?}", property_animation);
                         property_animation.update(Arc::make_mut(&mut new_style), relative_progress);
                     }
                     None => {
--- a/servo/components/style/gecko_bindings/sugar/ns_timing_function.rs
+++ b/servo/components/style/gecko_bindings/sugar/ns_timing_function.rs
@@ -54,23 +54,26 @@ impl From<ComputedTimingFunction> for ns
 }
 
 impl From<SpecifiedTimingFunction> for nsTimingFunction {
     fn from(function: SpecifiedTimingFunction) -> nsTimingFunction {
         let mut tf: nsTimingFunction = unsafe { mem::zeroed() };
 
         match function {
             SpecifiedTimingFunction::Steps(steps, StartEnd::Start) => {
-                tf.set_as_step(nsTimingFunction_Type::StepStart, steps);
+                debug_assert!(steps.value() >= 0);
+                tf.set_as_step(nsTimingFunction_Type::StepStart, steps.value() as u32);
             },
             SpecifiedTimingFunction::Steps(steps, StartEnd::End) => {
-                tf.set_as_step(nsTimingFunction_Type::StepEnd, steps);
+                debug_assert!(steps.value() >= 0);
+                tf.set_as_step(nsTimingFunction_Type::StepEnd, steps.value() as u32);
             },
             SpecifiedTimingFunction::CubicBezier(p1, p2) => {
-                tf.set_as_cubic_bezier(p1, p2);
+                tf.set_as_cubic_bezier(Point2D::new(p1.x.value, p1.y.value),
+                                       Point2D::new(p2.x.value, p2.y.value));
             },
             SpecifiedTimingFunction::Keyword(keyword) => {
                 match keyword {
                     FunctionKeyword::Ease => tf.mType = nsTimingFunction_Type::Ease,
                     FunctionKeyword::Linear => tf.mType = nsTimingFunction_Type::Linear,
                     FunctionKeyword::EaseIn => tf.mType = nsTimingFunction_Type::EaseIn,
                     FunctionKeyword::EaseOut => tf.mType = nsTimingFunction_Type::EaseOut,
                     FunctionKeyword::EaseInOut => tf.mType = nsTimingFunction_Type::EaseInOut,
--- a/servo/components/style/properties/gecko.mako.rs
+++ b/servo/components/style/properties/gecko.mako.rs
@@ -1278,31 +1278,30 @@ fn static_assert() {
     pub fn copy_font_synthesis_from(&mut self, other: &Self) {
         self.gecko.mFont.synthesis = other.gecko.mFont.synthesis;
     }
 
     pub fn set_font_size_adjust(&mut self, v: longhands::font_size_adjust::computed_value::T) {
         use properties::longhands::font_size_adjust::computed_value::T;
         match v {
             T::None => self.gecko.mFont.sizeAdjust = -1.0 as f32,
-            T::Number(n) => self.gecko.mFont.sizeAdjust = n.0 as f32,
+            T::Number(n) => self.gecko.mFont.sizeAdjust = n,
         }
     }
 
     pub fn copy_font_size_adjust_from(&mut self, other: &Self) {
         self.gecko.mFont.sizeAdjust = other.gecko.mFont.sizeAdjust;
     }
 
     pub fn clone_font_size_adjust(&self) -> longhands::font_size_adjust::computed_value::T {
         use properties::longhands::font_size_adjust::computed_value::T;
-        use values::specified::Number;
 
         match self.gecko.mFont.sizeAdjust {
             -1.0 => T::None,
-            _ => T::Number(Number(self.gecko.mFont.sizeAdjust)),
+            _ => T::Number(self.gecko.mFont.sizeAdjust),
         }
     }
 
     #[allow(non_snake_case)]
     pub fn set__x_lang(&mut self, v: longhands::_x_lang::computed_value::T) {
         let ptr = v.0.as_ptr();
         forget(v);
         unsafe {
@@ -1351,18 +1350,18 @@ fn static_assert() {
         self.gecko.m${type.capitalize()}${gecko_ffi_name}Count = input_len as u32;
         for (i, gecko) in self.gecko.m${type.capitalize()}s.iter_mut().enumerate() {
             gecko.m${gecko_ffi_name} = v.0[i % input_len].seconds() * 1000.;
         }
     }
     #[allow(non_snake_case)]
     pub fn ${type}_${ident}_at(&self, index: usize)
         -> longhands::${type}_${ident}::computed_value::SingleComputedValue {
-        use values::specified::Time;
-        Time(self.gecko.m${type.capitalize()}s[index].m${gecko_ffi_name} / 1000.)
+        use values::computed::Time;
+        Time::from_seconds(self.gecko.m${type.capitalize()}s[index].m${gecko_ffi_name} / 1000.)
     }
     ${impl_animation_or_transition_count(type, ident, gecko_ffi_name)}
     ${impl_copy_animation_or_transition_value(type, ident, gecko_ffi_name)}
 </%def>
 
 <%def name="impl_animation_or_transition_timing_function(type)">
     pub fn set_${type}_timing_function(&mut self, v: longhands::${type}_timing_function::computed_value::T) {
         debug_assert!(!v.0.is_empty());
@@ -1660,17 +1659,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.0)",
+                "angle" : "bindings::Gecko_CSSValue_SetAngle(%s, %s.radians())",
                 "number" : "bindings::Gecko_CSSValue_SetNumber(%s, %s)",
             }
         %>
         longhands::transform::computed_value::ComputedOperation::${name.title()}(${pattern}) => {
             bindings::Gecko_CSSValue_SetFunction(gecko_value, ${len(items) + 1});
             bindings::Gecko_CSSValue_SetKeyword(
                 bindings::Gecko_CSSValue_GetArrayItem(gecko_value, 0),
                 eCSSKeyword_${keyword}
@@ -1734,17 +1733,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(bindings::Gecko_CSSValue_GetAngle(%s))",
+                "angle" : "Angle::from_radians(bindings::Gecko_CSSValue_GetAngle(%s))",
                 "number" : "bindings::Gecko_CSSValue_GetNumber(%s)",
             }
         %>
         eCSSKeyword_${keyword} => {
             ComputedOperation::${name.title()}(
             % if name == "matrix":
                 ComputedMatrix {
             % endif
--- a/servo/components/style/properties/helpers.mako.rs
+++ b/servo/components/style/properties/helpers.mako.rs
@@ -14,18 +14,20 @@
         pub mod computed_value {
             pub use values::computed::${type} as T;
         }
         #[inline] pub fn get_initial_value() -> computed_value::T { ${initial_value} }
         % if initial_specified_value:
         #[inline] pub fn get_initial_specified_value() -> SpecifiedValue { ${initial_specified_value} }
         % endif
         #[allow(unused_variables)]
-        #[inline] pub fn parse(context: &ParserContext, input: &mut Parser)
-                               -> Result<SpecifiedValue, ()> {
+        #[inline]
+        pub fn parse(context: &ParserContext,
+                     input: &mut Parser)
+                     -> Result<SpecifiedValue, ()> {
             % if needs_context:
             specified::${type}::${parse_method}(context, input)
             % else:
             specified::${type}::${parse_method}(input)
             % endif
         }
     </%def>
     % if vector:
--- a/servo/components/style/properties/helpers/animated_properties.mako.rs
+++ b/servo/components/style/properties/helpers/animated_properties.mako.rs
@@ -31,17 +31,16 @@ use super::ComputedValues;
 use values::CSSFloat;
 use values::{Auto, Either};
 use values::computed::{Angle, LengthOrPercentageOrAuto, LengthOrPercentageOrNone};
 use values::computed::{BorderRadiusSize, ClipRect, LengthOrNone};
 use values::computed::{CalcLengthOrPercentage, Context, LengthOrPercentage};
 use values::computed::{MaxLength, MinLength};
 use values::computed::position::{HorizontalPosition, Position, VerticalPosition};
 use values::computed::ToComputedValue;
-use values::specified::Angle as SpecifiedAngle;
 
 
 
 /// A given transition property, that is either `All`, or an animatable
 /// property.
 // NB: This needs to be here because it needs all the longhands generated
 // beforehand.
 #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
@@ -450,17 +449,17 @@ impl Interpolate for i32 {
         Ok((a + (b - a) * progress).round() as i32)
     }
 }
 
 /// https://drafts.csswg.org/css-transitions/#animtype-number
 impl Interpolate for Angle {
     #[inline]
     fn interpolate(&self, other: &Angle, progress: f64) -> Result<Self, ()> {
-        self.radians().interpolate(&other.radians(), progress).map(Angle)
+        self.radians().interpolate(&other.radians(), progress).map(Angle::from_radians)
     }
 }
 
 /// https://drafts.csswg.org/css-transitions/#animtype-visibility
 impl Interpolate for Visibility {
     #[inline]
     fn interpolate(&self, other: &Self, progress: f64) -> Result<Self, ()> {
         match (*self, *other) {
@@ -947,28 +946,28 @@ fn build_identity_transform_list(list: &
 
     for operation in list {
         match *operation {
             TransformOperation::Matrix(..) => {
                 let identity = ComputedMatrix::identity();
                 result.push(TransformOperation::Matrix(identity));
             }
             TransformOperation::Skew(..) => {
-                result.push(TransformOperation::Skew(Angle(0.0), Angle(0.0)));
+                result.push(TransformOperation::Skew(Angle::zero(), Angle::zero()))
             }
             TransformOperation::Translate(..) => {
                 result.push(TransformOperation::Translate(LengthOrPercentage::zero(),
                                                           LengthOrPercentage::zero(),
                                                           Au(0)));
             }
             TransformOperation::Scale(..) => {
                 result.push(TransformOperation::Scale(1.0, 1.0, 1.0));
             }
             TransformOperation::Rotate(..) => {
-                result.push(TransformOperation::Rotate(0.0, 0.0, 1.0, Angle(0.0)));
+                result.push(TransformOperation::Rotate(0.0, 0.0, 1.0, Angle::zero()));
             }
             TransformOperation::Perspective(..) => {
                 // http://dev.w3.org/csswg/css-transforms/#identity-transform-function
                 let identity = ComputedMatrix::identity();
                 result.push(TransformOperation::Matrix(identity));
             }
         }
     }
@@ -1047,17 +1046,17 @@ fn interpolate_transform_list(from_list:
         // TODO(gw): Implement matrix decomposition and interpolation
         result.extend_from_slice(from_list);
     }
 
     TransformList(Some(result))
 }
 
 /// https://drafts.csswg.org/css-transforms/#Rotate3dDefined
-fn rotate_to_matrix(x: f32, y: f32, z: f32, a: SpecifiedAngle) -> ComputedMatrix {
+fn rotate_to_matrix(x: f32, y: f32, z: f32, a: Angle) -> ComputedMatrix {
     let half_rad = a.radians() / 2.0;
     let sc = (half_rad).sin() * (half_rad).cos();
     let sq = (half_rad).sin().powi(2);
 
     ComputedMatrix {
         m11: 1.0 - 2.0 * (y * y + z * z) * sq,
         m12: 2.0 * (x * y * sq - z * sc),
         m13: 2.0 * (x * z * sq + y * sc),
--- a/servo/components/style/properties/longhand/border.mako.rs
+++ b/servo/components/style/properties/longhand/border.mako.rs
@@ -218,17 +218,17 @@
     #[inline]
     pub fn get_initial_value() -> computed_value::T {
         computed_value::T(Either::Second(0.0), Either::Second(0.0),
                           Either::Second(0.0), Either::Second(0.0))
     }
 
     #[inline]
     pub fn get_initial_specified_value() -> SpecifiedValue {
-        SpecifiedValue(vec![Either::Second(Number(0.0))])
+        SpecifiedValue(vec![Either::Second(Number::new(0.0))])
     }
 
     impl ToComputedValue for SpecifiedValue {
         type ComputedValue = computed_value::T;
 
         #[inline]
         fn to_computed_value(&self, context: &Context) -> computed_value::T {
             let length = self.0.len();
@@ -481,17 +481,17 @@
         computed_value::T(computed_value::SingleComputedValue::Number(1.0),
                           computed_value::SingleComputedValue::Number(1.0),
                           computed_value::SingleComputedValue::Number(1.0),
                           computed_value::SingleComputedValue::Number(1.0))
     }
 
     #[inline]
     pub fn get_initial_specified_value() -> SpecifiedValue {
-        SpecifiedValue(vec![SingleSpecifiedValue::Number(Number(1.0))])
+        SpecifiedValue(vec![SingleSpecifiedValue::Number(Number::new(1.0))])
     }
 
     impl ToComputedValue for SpecifiedValue {
         type ComputedValue = computed_value::T;
 
         #[inline]
         fn to_computed_value(&self, context: &Context) -> computed_value::T {
             let length = self.0.len();
--- a/servo/components/style/properties/longhand/box.mako.rs
+++ b/servo/components/style/properties/longhand/box.mako.rs
@@ -414,124 +414,129 @@
     use values::HasViewportPercentage;
     no_viewport_percentage!(SpecifiedValue);
 
     pub mod computed_value {
         pub use values::computed::Time as T;
     }
 
     #[inline]
-    pub fn get_initial_value() -> Time {
-        Time(0.0)
+    pub fn get_initial_value() -> computed_value::T {
+        computed_value::T::zero()
     }
 
     #[inline]
     pub fn get_initial_specified_value() -> SpecifiedValue {
-        SpecifiedValue(0.0)
+        Time::zero()
     }
 
     pub fn parse(context: &ParserContext, input: &mut Parser) -> Result<SpecifiedValue,()> {
         Time::parse(context, input)
     }
 </%helpers:vector_longhand>
 
 // TODO(pcwalton): Lots more timing functions.
 <%helpers:vector_longhand name="transition-timing-function"
                           need_index="True"
                           animatable="False"
                           extra_prefixes="moz webkit"
                           spec="https://drafts.csswg.org/css-transitions/#propdef-transition-timing-function">
     use self::computed_value::StartEnd;
-
+    use values::specified::Number;
     use euclid::point::{Point2D, TypedPoint2D};
     use std::fmt;
     use std::marker::PhantomData;
     use style_traits::ToCss;
 
     // FIXME: This could use static variables and const functions when they are available.
     #[inline(always)]
     fn ease() -> computed_value::T {
         computed_value::T::CubicBezier(TypedPoint2D::new(0.25, 0.1),
-                                              TypedPoint2D::new(0.25, 1.0))
+                                       TypedPoint2D::new(0.25, 1.0))
     }
 
     #[inline(always)]
     fn linear() -> computed_value::T {
         computed_value::T::CubicBezier(TypedPoint2D::new(0.0, 0.0),
-                                              TypedPoint2D::new(1.0, 1.0))
+                                       TypedPoint2D::new(1.0, 1.0))
     }
 
     #[inline(always)]
     fn ease_in() -> computed_value::T {
         computed_value::T::CubicBezier(TypedPoint2D::new(0.42, 0.0),
-                                              TypedPoint2D::new(1.0, 1.0))
+                                       TypedPoint2D::new(1.0, 1.0))
     }
 
     #[inline(always)]
     fn ease_out() -> computed_value::T {
         computed_value::T::CubicBezier(TypedPoint2D::new(0.0, 0.0),
-                                              TypedPoint2D::new(0.58, 1.0))
+                                       TypedPoint2D::new(0.58, 1.0))
     }
 
     #[inline(always)]
     fn ease_in_out() -> computed_value::T {
         computed_value::T::CubicBezier(TypedPoint2D::new(0.42, 0.0),
-                                              TypedPoint2D::new(0.58, 1.0))
+                                       TypedPoint2D::new(0.58, 1.0))
     }
 
     static STEP_START: computed_value::T =
         computed_value::T::Steps(1, StartEnd::Start);
     static STEP_END: computed_value::T =
         computed_value::T::Steps(1, StartEnd::End);
 
     pub mod computed_value {
         use euclid::point::Point2D;
         use parser::{Parse, ParserContext};
         use std::fmt;
         use style_traits::ToCss;
+        use values::specified;
 
         pub use super::parse;
 
         #[derive(Copy, Clone, Debug, PartialEq)]
         #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
         pub enum T {
             CubicBezier(Point2D<f32>, Point2D<f32>),
             Steps(u32, StartEnd),
         }
 
         impl ToCss for T {
-            fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
+            fn to_css<W>(&self, dest: &mut W) -> fmt::Result
+                where W: fmt::Write,
+            {
                 match *self {
                     T::CubicBezier(p1, p2) => {
                         try!(dest.write_str("cubic-bezier("));
                         try!(p1.x.to_css(dest));
                         try!(dest.write_str(", "));
                         try!(p1.y.to_css(dest));
                         try!(dest.write_str(", "));
                         try!(p2.x.to_css(dest));
                         try!(dest.write_str(", "));
                         try!(p2.y.to_css(dest));
                         dest.write_str(")")
                     }
                     T::Steps(steps, start_end) => {
-                        super::serialize_steps(dest, steps, start_end)
+                        super::serialize_steps(dest, specified::Integer::new(steps as i32), start_end)
                     }
                 }
             }
         }
 
         #[derive(Copy, Clone, Debug, PartialEq)]
         #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
         pub enum StartEnd {
             Start,
             End,
         }
 
         impl ToCss for StartEnd {
-            fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
+            fn to_css<W>(&self, dest: &mut W) -> fmt::Result
+                where W: fmt::Write,
+            {
                 match *self {
                     StartEnd::Start => dest.write_str("start"),
                     StartEnd::End => dest.write_str("end"),
                 }
             }
         }
     }
 
@@ -542,73 +547,78 @@
                              "ease-out" => EaseOut,
                              "ease-in-out" => EaseInOut,
                              "step-start" => StepStart,
                              "step-end" => StepEnd);
 
     #[derive(Copy, Clone, Debug, PartialEq)]
     #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
     pub enum SpecifiedValue {
-        CubicBezier(Point2D<f32>, Point2D<f32>),
-        Steps(u32, StartEnd),
+        CubicBezier(Point2D<Number>, Point2D<Number>),
+        Steps(specified::Integer, StartEnd),
         Keyword(FunctionKeyword),
     }
 
     impl Parse for SpecifiedValue {
         fn parse(_context: &ParserContext, input: &mut ::cssparser::Parser) -> Result<Self, ()> {
             if let Ok(function_name) = input.try(|input| input.expect_function()) {
                 return match_ignore_ascii_case! { &function_name,
                     "cubic-bezier" => {
-                        let (mut p1x, mut p1y, mut p2x, mut p2y) = (0.0, 0.0, 0.0, 0.0);
+                        let (mut p1x, mut p1y, mut p2x, mut p2y) =
+                            (Number::new(0.0), Number::new(0.0), Number::new(0.0), Number::new(0.0));
                         try!(input.parse_nested_block(|input| {
                             p1x = try!(specified::parse_number(input));
                             try!(input.expect_comma());
                             p1y = try!(specified::parse_number(input));
                             try!(input.expect_comma());
                             p2x = try!(specified::parse_number(input));
                             try!(input.expect_comma());
                             p2y = try!(specified::parse_number(input));
                             Ok(())
                         }));
-                        if p1x < 0.0 || p1x > 1.0 || p2x < 0.0 || p2x > 1.0 {
+                        if p1x.value < 0.0 || p1x.value > 1.0 ||
+                           p2x.value < 0.0 || p2x.value > 1.0 {
                             return Err(())
                         }
 
                         let (p1, p2) = (Point2D::new(p1x, p1y), Point2D::new(p2x, p2y));
                         Ok(SpecifiedValue::CubicBezier(p1, p2))
                     },
                     "steps" => {
-                        let (mut step_count, mut start_end) = (0, StartEnd::End);
+                        let (mut step_count, mut start_end) = (specified::Integer::new(0), StartEnd::End);
                         try!(input.parse_nested_block(|input| {
                             step_count = try!(specified::parse_integer(input));
-                            if step_count < 1 {
+                            if step_count.value() < 1 {
                                 return Err(())
                             }
 
                             if input.try(|input| input.expect_comma()).is_ok() {
                                 start_end = try!(match_ignore_ascii_case! {
                                     &try!(input.expect_ident()),
                                     "start" => Ok(StartEnd::Start),
                                     "end" => Ok(StartEnd::End),
                                     _ => Err(())
                                 });
                             }
                             Ok(())
                         }));
-                        Ok(SpecifiedValue::Steps(step_count as u32, start_end))
+                        Ok(SpecifiedValue::Steps(step_count, start_end))
                     },
                     _ => Err(())
                 }
             }
             Ok(SpecifiedValue::Keyword(try!(FunctionKeyword::parse(input))))
         }
     }
 
-    fn serialize_steps<W>(dest: &mut W, steps: u32,
-                          start_end: StartEnd) -> fmt::Result where W: fmt::Write {
+    fn serialize_steps<W>(dest: &mut W,
+                          steps: specified::Integer,
+                          start_end: StartEnd) -> fmt::Result
+        where W: fmt::Write,
+    {
         try!(dest.write_str("steps("));
         try!(steps.to_css(dest));
         if let StartEnd::Start = start_end {
             try!(dest.write_str(", start"));
         }
         dest.write_str(")")
     }
 
@@ -628,41 +638,43 @@
                     dest.write_str(")")
                 },
                 SpecifiedValue::Steps(steps, start_end) => {
                     serialize_steps(dest, steps, start_end)
                 },
                 SpecifiedValue::Keyword(keyword) => {
                     match keyword {
                         FunctionKeyword::StepStart => {
-                            serialize_steps(dest, 1, StartEnd::Start)
+                            serialize_steps(dest, specified::Integer::new(1), StartEnd::Start)
                         },
                         FunctionKeyword::StepEnd => {
-                            serialize_steps(dest, 1, StartEnd::End)
+                            serialize_steps(dest, specified::Integer::new(1), StartEnd::End)
                         },
                         _ => {
                             keyword.to_css(dest)
                         },
                     }
                 },
             }
         }
     }
 
     impl ToComputedValue for SpecifiedValue {
         type ComputedValue = computed_value::T;
 
         #[inline]
-        fn to_computed_value(&self, _context: &Context) -> computed_value::T {
+        fn to_computed_value(&self, context: &Context) -> computed_value::T {
             match *self {
                 SpecifiedValue::CubicBezier(p1, p2) => {
-                    computed_value::T::CubicBezier(p1, p2)
+                    computed_value::T::CubicBezier(
+                        Point2D::new(p1.x.to_computed_value(context), p1.y.to_computed_value(context)),
+                        Point2D::new(p2.x.to_computed_value(context), p2.y.to_computed_value(context)))
                 },
                 SpecifiedValue::Steps(count, start_end) => {
-                    computed_value::T::Steps(count, start_end)
+                    computed_value::T::Steps(count.to_computed_value(context) as u32, start_end)
                 },
                 SpecifiedValue::Keyword(keyword) => {
                     match keyword {
                         FunctionKeyword::Ease => ease(),
                         FunctionKeyword::Linear => linear(),
                         FunctionKeyword::EaseIn => ease_in(),
                         FunctionKeyword::EaseOut => ease_out(),
                         FunctionKeyword::EaseInOut => ease_in_out(),
@@ -671,20 +683,25 @@
                     }
                 },
             }
         }
         #[inline]
         fn from_computed_value(computed: &computed_value::T) -> Self {
             match *computed {
                 computed_value::T::CubicBezier(p1, p2) => {
-                    SpecifiedValue::CubicBezier(p1, p2)
+                    SpecifiedValue::CubicBezier(
+                        Point2D::new(Number::from_computed_value(&p1.x),
+                                     Number::from_computed_value(&p1.y)),
+                        Point2D::new(Number::from_computed_value(&p2.x),
+                                     Number::from_computed_value(&p2.y)))
                 },
                 computed_value::T::Steps(count, start_end) => {
-                    SpecifiedValue::Steps(count, start_end)
+                    let int_count = count as i32;
+                    SpecifiedValue::Steps(specified::Integer::from_computed_value(&int_count), start_end)
                 },
             }
         }
     }
 
     use values::HasViewportPercentage;
     no_viewport_percentage!(SpecifiedValue);
 
@@ -1078,16 +1095,17 @@
 
 
 <%helpers:longhand name="transform" products="gecko servo" extra_prefixes="webkit"
                    animatable="True"
                    creates_stacking_context="True"
                    fixpos_cb="True"
                    spec="https://drafts.csswg.org/css-transforms/#propdef-transform">
     use app_units::Au;
+    use values::specified::Number;
     use style_traits::ToCss;
     use values::CSSFloat;
     use values::HasViewportPercentage;
 
     use std::fmt;
 
     pub mod computed_value {
         use values::CSSFloat;
@@ -1126,45 +1144,98 @@
             Perspective(computed::Length),
         }
 
         #[derive(Clone, Debug, PartialEq)]
         #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
         pub struct T(pub Option<Vec<ComputedOperation>>);
     }
 
-    pub use self::computed_value::ComputedMatrix as SpecifiedMatrix;
+    #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
+    #[derive(Copy, Clone, Debug, PartialEq)]
+    pub struct SpecifiedMatrix {
+        pub m11: Number, pub m12: Number, pub m13: Number, pub m14: Number,
+        pub m21: Number, pub m22: Number, pub m23: Number, pub m24: Number,
+        pub m31: Number, pub m32: Number, pub m33: Number, pub m34: Number,
+        pub m41: Number, pub m42: Number, pub m43: Number, pub m44: Number,
+    }
+
+    impl ToComputedValue for SpecifiedMatrix {
+        type ComputedValue = computed_value::ComputedMatrix;
+
+        fn to_computed_value(&self, context: &Context) -> Self::ComputedValue {
+            computed_value::ComputedMatrix {
+                m11: self.m11.to_computed_value(context),
+                m12: self.m12.to_computed_value(context),
+                m13: self.m13.to_computed_value(context),
+                m14: self.m14.to_computed_value(context),
+                m21: self.m21.to_computed_value(context),
+                m22: self.m22.to_computed_value(context),
+                m23: self.m23.to_computed_value(context),
+                m24: self.m24.to_computed_value(context),
+                m31: self.m31.to_computed_value(context),
+                m32: self.m32.to_computed_value(context),
+                m33: self.m33.to_computed_value(context),
+                m34: self.m34.to_computed_value(context),
+                m41: self.m41.to_computed_value(context),
+                m42: self.m42.to_computed_value(context),
+                m43: self.m43.to_computed_value(context),
+                m44: self.m44.to_computed_value(context),
+            }
+        }
+
+        fn from_computed_value(computed: &Self::ComputedValue) -> Self {
+            SpecifiedMatrix {
+                m11: Number::from_computed_value(&computed.m11),
+                m12: Number::from_computed_value(&computed.m12),
+                m13: Number::from_computed_value(&computed.m13),
+                m14: Number::from_computed_value(&computed.m14),
+                m21: Number::from_computed_value(&computed.m21),
+                m22: Number::from_computed_value(&computed.m22),
+                m23: Number::from_computed_value(&computed.m23),
+                m24: Number::from_computed_value(&computed.m24),
+                m31: Number::from_computed_value(&computed.m31),
+                m32: Number::from_computed_value(&computed.m32),
+                m33: Number::from_computed_value(&computed.m33),
+                m34: Number::from_computed_value(&computed.m34),
+                m41: Number::from_computed_value(&computed.m41),
+                m42: Number::from_computed_value(&computed.m42),
+                m43: Number::from_computed_value(&computed.m43),
+                m44: Number::from_computed_value(&computed.m44),
+            }
+        }
+    }
 
     fn parse_two_lengths_or_percentages(context: &ParserContext, input: &mut Parser)
                                         -> Result<(specified::LengthOrPercentage,
                                                    specified::LengthOrPercentage),()> {
         let first = try!(specified::LengthOrPercentage::parse(context, input));
         let second = input.try(|input| {
             try!(input.expect_comma());
             specified::LengthOrPercentage::parse(context, input)
         }).unwrap_or(specified::LengthOrPercentage::zero());
         Ok((first, second))
     }
 
-    fn parse_two_floats(input: &mut Parser) -> Result<(CSSFloat,CSSFloat),()> {
+    fn parse_two_numbers(input: &mut Parser) -> Result<(Number, Number), ()> {
         let first = try!(specified::parse_number(input));
         let second = input.try(|input| {
             try!(input.expect_comma());
             specified::parse_number(input)
         }).unwrap_or(first);
         Ok((first, second))
     }
 
     fn parse_two_angles(context: &ParserContext, input: &mut Parser)
                        -> Result<(specified::Angle, specified::Angle),()> {
         let first = try!(specified::Angle::parse(context, input));
         let second = input.try(|input| {
             try!(input.expect_comma());
             specified::Angle::parse(context, input)
-        }).unwrap_or(specified::Angle(0.0));
+        }).unwrap_or(specified::Angle::zero());
         Ok((first, second))
     }
 
     #[derive(Copy, Clone, Debug, PartialEq)]
     #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
     enum TranslateKind {
         Translate,
         TranslateX,
@@ -1177,18 +1248,18 @@
     #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
     enum SpecifiedOperation {
         Matrix(SpecifiedMatrix),
         Skew(specified::Angle, specified::Angle),
         Translate(TranslateKind,
                   specified::LengthOrPercentage,
                   specified::LengthOrPercentage,
                   specified::Length),
-        Scale(CSSFloat, CSSFloat, CSSFloat),
-        Rotate(CSSFloat, CSSFloat, CSSFloat, specified::Angle),
+        Scale(Number, Number, Number),
+        Rotate(Number, Number, Number, specified::Angle),
         Perspective(specified::Length),
     }
 
     impl ToCss for computed_value::T {
         fn to_css<W>(&self, _: &mut W) -> fmt::Result where W: fmt::Write {
             // TODO(pcwalton)
             Ok(())
         }
@@ -1318,41 +1389,39 @@
                 "matrix" => {
                     try!(input.parse_nested_block(|input| {
                         let values = try!(input.parse_comma_separated(|input| {
                             specified::parse_number(input)
                         }));
                         if values.len() != 6 {
                             return Err(())
                         }
-                        result.push(SpecifiedOperation::Matrix(
-                                SpecifiedMatrix {
-                                    m11: values[0], m12: values[1], m13: 0.0, m14: 0.0,
-                                    m21: values[2], m22: values[3], m23: 0.0, m24: 0.0,
-                                    m31:       0.0, m32:       0.0, m33: 1.0, m34: 0.0,
-                                    m41: values[4], m42: values[5], m43: 0.0, m44: 1.0
-                                }));
+                        let matrix = SpecifiedMatrix {
+                            m11: values[0],        m12: values[1],        m13: Number::new(0.0), m14: Number::new(0.0),
+                            m21: values[2],        m22: values[3],        m23: Number::new(0.0), m24: Number::new(0.0),
+                            m31: Number::new(0.0), m32: Number::new(0.0), m33: Number::new(1.0), m34: Number::new(0.0),
+                            m41: values[4],        m42: values[5],        m43: Number::new(0.0), m44: Number::new(1.0),
+                        };
+                        result.push(SpecifiedOperation::Matrix(matrix));
                         Ok(())
                     }))
                 },
                 "matrix3d" => {
                     try!(input.parse_nested_block(|input| {
-                        let values = try!(input.parse_comma_separated(|input| {
-                            specified::parse_number(input)
-                        }));
+                        let values = try!(input.parse_comma_separated(specified::parse_number));
                         if values.len() != 16 {
                             return Err(())
                         }
                         result.push(SpecifiedOperation::Matrix(
-                                SpecifiedMatrix {
-                                    m11: values[ 0], m12: values[ 1], m13: values[ 2], m14: values[ 3],
-                                    m21: values[ 4], m22: values[ 5], m23: values[ 6], m24: values[ 7],
-                                    m31: values[ 8], m32: values[ 9], m33: values[10], m34: values[11],
-                                    m41: values[12], m42: values[13], m43: values[14], m44: values[15]
-                                }));
+                            SpecifiedMatrix {
+                                m11: values[ 0], m12: values[ 1], m13: values[ 2], m14: values[ 3],
+                                m21: values[ 4], m22: values[ 5], m23: values[ 6], m24: values[ 7],
+                                m31: values[ 8], m32: values[ 9], m33: values[10], m34: values[11],
+                                m41: values[12], m42: values[13], m43: values[14], m44: values[15]
+                            }));
                         Ok(())
                     }))
                 },
                 "translate" => {
                     try!(input.parse_nested_block(|input| {
                         let (tx, ty) = try!(parse_two_lengths_or_percentages(context, input));
                         result.push(SpecifiedOperation::Translate(TranslateKind::Translate,
                                                                   tx,
@@ -1407,39 +1476,47 @@
                             ty,
                             tz));
                         Ok(())
                     }))
 
                 },
                 "scale" => {
                     try!(input.parse_nested_block(|input| {
-                        let (sx, sy) = try!(parse_two_floats(input));
-                        result.push(SpecifiedOperation::Scale(sx, sy, 1.0));
+                        let (sx, sy) = try!(parse_two_numbers(input));
+                        result.push(SpecifiedOperation::Scale(sx,
+                                                              sy,
+                                                              Number::new(1.0)));
                         Ok(())
                     }))
                 },
                 "scalex" => {
                     try!(input.parse_nested_block(|input| {
                         let sx = try!(specified::parse_number(input));
-                        result.push(SpecifiedOperation::Scale(sx, 1.0, 1.0));
+                        result.push(SpecifiedOperation::Scale(sx,
+                                                              Number::new(1.0),
+                                                              Number::new(1.0)));
                         Ok(())
                     }))
                 },
                 "scaley" => {
                     try!(input.parse_nested_block(|input| {
                         let sy = try!(specified::parse_number(input));
-                        result.push(SpecifiedOperation::Scale(1.0, sy, 1.0));
+                        result.push(SpecifiedOperation::Scale(Number::new(1.0),
+                                                              sy,
+                                                              Number::new(1.0)));
                         Ok(())
                     }))
                 },
                 "scalez" => {
                     try!(input.parse_nested_block(|input| {
                         let sz = try!(specified::parse_number(input));
-                        result.push(SpecifiedOperation::Scale(1.0, 1.0, sz));
+                        result.push(SpecifiedOperation::Scale(Number::new(1.0),
+                                                              Number::new(1.0),
+                                                              sz));
                         Ok(())
                     }))
                 },
                 "scale3d" => {
                     try!(input.parse_nested_block(|input| {
                         let sx = try!(specified::parse_number(input));
                         try!(input.expect_comma());
                         let sy = try!(specified::parse_number(input));
@@ -1447,38 +1524,50 @@
                         let sz = try!(specified::parse_number(input));
                         result.push(SpecifiedOperation::Scale(sx, sy, sz));
                         Ok(())
                     }))
                 },
                 "rotate" => {
                     try!(input.parse_nested_block(|input| {
                         let theta = try!(specified::Angle::parse(context,input));
-                        result.push(SpecifiedOperation::Rotate(0.0, 0.0, 1.0, theta));
+                        result.push(SpecifiedOperation::Rotate(Number::new(0.0),
+                                                               Number::new(0.0),
+                                                               Number::new(1.0),
+                                                               theta));
                         Ok(())
                     }))
                 },
                 "rotatex" => {
                     try!(input.parse_nested_block(|input| {
                         let theta = try!(specified::Angle::parse(context,input));
-                        result.push(SpecifiedOperation::Rotate(1.0, 0.0, 0.0, theta));
+                        result.push(SpecifiedOperation::Rotate(Number::new(1.0),
+                                                               Number::new(0.0),
+                                                               Number::new(0.0),
+                                                               theta));
                         Ok(())
                     }))
                 },
                 "rotatey" => {
                     try!(input.parse_nested_block(|input| {
                         let theta = try!(specified::Angle::parse(context,input));
-                        result.push(SpecifiedOperation::Rotate(0.0, 1.0, 0.0, theta));
+                        result.push(SpecifiedOperation::Rotate(Number::new(0.0),
+                                                               Number::new(1.0),
+                                                               Number::new(0.0),
+                                                               theta));
                         Ok(())
                     }))
                 },
                 "rotatez" => {
                     try!(input.parse_nested_block(|input| {
                         let theta = try!(specified::Angle::parse(context,input));
-                        result.push(SpecifiedOperation::Rotate(0.0, 0.0, 1.0, theta));
+                        result.push(SpecifiedOperation::Rotate(Number::new(0.0),
+                                                               Number::new(0.0),
+                                                               Number::new(1.0),
+                                                               theta));
                         Ok(())
                     }))
                 },
                 "rotate3d" => {
                     try!(input.parse_nested_block(|input| {
                         let ax = try!(specified::parse_number(input));
                         try!(input.expect_comma());
                         let ay = try!(specified::parse_number(input));
@@ -1496,24 +1585,24 @@
                         let (theta_x, theta_y) = try!(parse_two_angles(context, input));
                         result.push(SpecifiedOperation::Skew(theta_x, theta_y));
                         Ok(())
                     }))
                 },
                 "skewx" => {
                     try!(input.parse_nested_block(|input| {
                         let theta_x = try!(specified::Angle::parse(context,input));
-                        result.push(SpecifiedOperation::Skew(theta_x, specified::Angle(0.0)));
+                        result.push(SpecifiedOperation::Skew(theta_x, specified::Angle::zero()));
                         Ok(())
                     }))
                 },
                 "skewy" => {
                     try!(input.parse_nested_block(|input| {
                         let theta_y = try!(specified::Angle::parse(context,input));
-                        result.push(SpecifiedOperation::Skew(specified::Angle(0.0), theta_y));
+                        result.push(SpecifiedOperation::Skew(specified::Angle::zero(), theta_y));
                         Ok(())
                     }))
                 },
                 "perspective" => {
                     try!(input.parse_nested_block(|input| {
                         let d = try!(specified::Length::parse(context, input));
                         result.push(SpecifiedOperation::Perspective(d));
                         Ok(())
@@ -1538,67 +1627,84 @@
             if self.0.is_empty() {
                 return computed_value::T(None)
             }
 
             let mut result = vec!();
             for operation in &self.0 {
                 match *operation {
                     SpecifiedOperation::Matrix(ref matrix) => {
-                        result.push(computed_value::ComputedOperation::Matrix(*matrix));
+                        result.push(computed_value::ComputedOperation::Matrix(matrix.to_computed_value(context)));
                     }
                     SpecifiedOperation::Translate(_, ref tx, ref ty, ref tz) => {
                         result.push(computed_value::ComputedOperation::Translate(tx.to_computed_value(context),
                                                                                  ty.to_computed_value(context),
                                                                                  tz.to_computed_value(context)));
                     }
                     SpecifiedOperation::Scale(sx, sy, sz) => {
+                        let sx = sx.to_computed_value(context);
+                        let sy = sy.to_computed_value(context);
+                        let sz = sz.to_computed_value(context);
                         result.push(computed_value::ComputedOperation::Scale(sx, sy, sz));
                     }
                     SpecifiedOperation::Rotate(ax, ay, az, theta) => {
+                        let ax = ax.to_computed_value(context);
+                        let ay = ay.to_computed_value(context);
+                        let az = az.to_computed_value(context);
+                        let theta = theta.to_computed_value(context);
                         let len = (ax * ax + ay * ay + az * az).sqrt();
                         result.push(computed_value::ComputedOperation::Rotate(ax / len, ay / len, az / len, theta));
                     }
                     SpecifiedOperation::Skew(theta_x, theta_y) => {
-                        result.push(computed_value::ComputedOperation::Skew(theta_x, theta_y));
+                        result.push(computed_value::ComputedOperation::Skew(theta_x.to_computed_value(context),
+                                                                            theta_y.to_computed_value(context)));
                     }
                     SpecifiedOperation::Perspective(ref d) => {
                         result.push(computed_value::ComputedOperation::Perspective(d.to_computed_value(context)));
                     }
                 };
             }
 
             computed_value::T(Some(result))
         }
 
         #[inline]
         fn from_computed_value(computed: &computed_value::T) -> Self {
             SpecifiedValue(computed.0.as_ref().map(|computed| {
-                let mut result = vec!();
+                let mut result = vec![];
                 for operation in computed {
                     match *operation {
                         computed_value::ComputedOperation::Matrix(ref matrix) => {
-                            result.push(SpecifiedOperation::Matrix(*matrix));
+                            result.push(SpecifiedOperation::Matrix(SpecifiedMatrix::from_computed_value(matrix)));
                         }
                         computed_value::ComputedOperation::Translate(ref tx, ref ty, ref tz) => {
                             // XXXManishearth we lose information here; perhaps we should try to
                             // recover the original function? Not sure if this can be observed.
                             result.push(SpecifiedOperation::Translate(TranslateKind::Translate,
                                               ToComputedValue::from_computed_value(tx),
                                               ToComputedValue::from_computed_value(ty),
                                               ToComputedValue::from_computed_value(tz)));
                         }
-                        computed_value::ComputedOperation::Scale(sx, sy, sz) => {
-                            result.push(SpecifiedOperation::Scale(sx, sy, sz));
+                        computed_value::ComputedOperation::Scale(ref sx, ref sy, ref sz) => {
+                            result.push(SpecifiedOperation::Scale(
+                                    Number::from_computed_value(sx),
+                                    Number::from_computed_value(sy),
+                                    Number::from_computed_value(sz)));
                         }
-                        computed_value::ComputedOperation::Rotate(ax, ay, az, theta) => {
-                            result.push(SpecifiedOperation::Rotate(ax, ay, az, theta));
+                        computed_value::ComputedOperation::Rotate(ref ax, ref ay, ref az, ref theta) => {
+                            result.push(SpecifiedOperation::Rotate(
+                                    Number::from_computed_value(ax),
+                                    Number::from_computed_value(ay),
+                                    Number::from_computed_value(az),
+                                    specified::Angle::from_computed_value(theta)));
                         }
-                        computed_value::ComputedOperation::Skew(theta_x, theta_y) => {
-                            result.push(SpecifiedOperation::Skew(theta_x, theta_y));
+                        computed_value::ComputedOperation::Skew(ref theta_x, ref theta_y) => {
+                            result.push(SpecifiedOperation::Skew(
+                                    specified::Angle::from_computed_value(theta_x),
+                                    specified::Angle::from_computed_value(theta_y)))
                         }
                         computed_value::ComputedOperation::Perspective(ref d) => {
                             result.push(SpecifiedOperation::Perspective(
                                 ToComputedValue::from_computed_value(d)
                             ));
                         }
                     };
                 }
--- a/servo/components/style/properties/longhand/counters.mako.rs
+++ b/servo/components/style/properties/longhand/counters.mako.rs
@@ -241,90 +241,133 @@
 </%helpers:longhand>
 
 <%helpers:longhand name="counter-increment" animatable="False"
                    spec="https://drafts.csswg.org/css-lists/#propdef-counter-increment">
     use std::fmt;
     use style_traits::ToCss;
     use super::content;
     use values::HasViewportPercentage;
-    use values::computed::ComputedValueAsSpecified;
 
     use cssparser::{Token, serialize_identifier};
     use std::borrow::{Cow, ToOwned};
 
-    pub use self::computed_value::T as SpecifiedValue;
+    #[derive(Debug, Clone, PartialEq)]
+    pub struct SpecifiedValue(pub Vec<(String, specified::Integer)>);
 
     pub mod computed_value {
+        use std::fmt;
+        use style_traits::ToCss;
+
         #[derive(Debug, Clone, PartialEq)]
         #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
-        pub struct T(pub Vec<(String,i32)>);
+        pub struct T(pub Vec<(String, i32)>);
+
+        impl ToCss for T {
+            fn to_css<W>(&self, dest: &mut W) -> fmt::Result
+                where W: fmt::Write,
+            {
+                use cssparser::serialize_identifier;
+                if self.0.is_empty() {
+                    return dest.write_str("none")
+                }
+
+                let mut first = true;
+                for pair in &self.0 {
+                    if !first {
+                        try!(dest.write_str(" "));
+                    }
+                    first = false;
+                    try!(serialize_identifier(&pair.0, dest));
+                    try!(dest.write_str(" "));
+                    try!(pair.1.to_css(dest));
+                }
+                Ok(())
+            }
+        }
+    }
+
+    impl ToComputedValue for SpecifiedValue {
+        type ComputedValue = computed_value::T;
+
+        fn to_computed_value(&self, context: &Context) -> Self::ComputedValue {
+            computed_value::T(self.0.iter().map(|entry| {
+                (entry.0.clone(), entry.1.to_computed_value(context))
+            }).collect::<Vec<_>>())
+        }
+
+        fn from_computed_value(computed: &Self::ComputedValue) -> Self {
+            SpecifiedValue(computed.0.iter().map(|entry| {
+                (entry.0.clone(), specified::Integer::from_computed_value(&entry.1))
+            }).collect::<Vec<_>>())
+        }
     }
 
     #[inline]
     pub fn get_initial_value() -> computed_value::T {
         computed_value::T(Vec::new())
     }
 
-    impl ComputedValueAsSpecified for SpecifiedValue {}
     no_viewport_percentage!(SpecifiedValue);
 
     impl ToCss for SpecifiedValue {
-        fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
+        fn to_css<W>(&self, dest: &mut W) -> fmt::Result
+            where W: fmt::Write,
+        {
             if self.0.is_empty() {
                 return dest.write_str("none");
             }
-
             let mut first = true;
             for pair in &self.0 {
                 if !first {
                     try!(dest.write_str(" "));
                 }
                 first = false;
                 try!(serialize_identifier(&pair.0, dest));
-                try!(write!(dest, " {}", pair.1));
+                try!(dest.write_str(" "));
+                try!(pair.1.to_css(dest));
             }
 
             Ok(())
         }
     }
 
-    pub fn parse(_: &ParserContext, input: &mut Parser) -> Result<SpecifiedValue,()> {
+    pub fn parse(_: &ParserContext, input: &mut Parser) -> Result<SpecifiedValue, ()> {
         parse_common(1, input)
     }
 
-    pub fn parse_common(default_value: i32, input: &mut Parser) -> Result<SpecifiedValue,()> {
+    pub fn parse_common(default_value: i32, input: &mut Parser) -> Result<SpecifiedValue, ()> {
         if input.try(|input| input.expect_ident_matching("none")).is_ok() {
             return Ok(SpecifiedValue(Vec::new()))
         }
 
         let mut counters = Vec::new();
         loop {
             let counter_name = match input.next() {
                 Ok(Token::Ident(ident)) => (*ident).to_owned(),
                 Ok(_) => return Err(()),
                 Err(_) => break,
             };
             if content::counter_name_is_illegal(&counter_name) {
                 return Err(())
             }
             let counter_delta =
-                input.try(|input| specified::parse_integer(input)).unwrap_or(default_value);
+                input.try(|input| specified::parse_integer(input)).unwrap_or(specified::Integer::new(default_value));
             counters.push((counter_name, counter_delta))
         }
 
         if !counters.is_empty() {
             Ok(SpecifiedValue(counters))
         } else {
             Err(())
         }
     }
 </%helpers:longhand>
 
 <%helpers:longhand name="counter-reset" animatable="False"
                    spec="https://drafts.csswg.org/css-lists-3/#propdef-counter-reset">
     pub use super::counter_increment::{SpecifiedValue, computed_value, get_initial_value};
-    use super::counter_increment::{parse_common};
+    use super::counter_increment::parse_common;
 
     pub fn parse(_: &ParserContext, input: &mut Parser) -> Result<SpecifiedValue,()> {
         parse_common(0, input)
     }
 </%helpers:longhand>
--- a/servo/components/style/properties/longhand/font.mako.rs
+++ b/servo/components/style/properties/longhand/font.mako.rs
@@ -650,59 +650,95 @@
                 _ => Err(())
             }
         }
     }
 </%helpers:longhand>
 
 <%helpers:longhand products="gecko" name="font-size-adjust" animatable="True"
                    spec="https://drafts.csswg.org/css-fonts/#propdef-font-size-adjust">
+    use std::fmt;
+    use style_traits::ToCss;
     use values::HasViewportPercentage;
-    use values::computed::ComputedValueAsSpecified;
-    use values::specified::Number;
 
-    impl ComputedValueAsSpecified for SpecifiedValue {}
     no_viewport_percentage!(SpecifiedValue);
 
     #[derive(Copy, Clone, Debug, PartialEq)]
     #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
     pub enum SpecifiedValue {
         None,
-        Number(Number),
+        Number(specified::Number),
+    }
+
+    impl ToCss for SpecifiedValue {
+        fn to_css<W>(&self, dest: &mut W) -> fmt::Result
+            where W: fmt::Write,
+        {
+            match *self {
+                SpecifiedValue::None => dest.write_str("none"),
+                SpecifiedValue::Number(number) => number.to_css(dest),
+            }
+        }
+    }
+
+    impl ToComputedValue for SpecifiedValue {
+        type ComputedValue = computed_value::T;
+
+        fn to_computed_value(&self, context: &Context) -> Self::ComputedValue {
+            match *self {
+                SpecifiedValue::None => computed_value::T::None,
+                SpecifiedValue::Number(ref n) => computed_value::T::Number(n.to_computed_value(context)),
+            }
+        }
+
+        fn from_computed_value(computed: &computed_value::T) -> Self {
+            match *computed {
+                computed_value::T::None => SpecifiedValue::None,
+                computed_value::T::Number(ref v) => SpecifiedValue::Number(specified::Number::from_computed_value(v)),
+            }
+        }
     }
 
     pub mod computed_value {
-        use style_traits::ToCss;
+        use properties::animated_properties::Interpolate;
         use std::fmt;
-        use properties::animated_properties::Interpolate;
-        use values::specified::Number;
+        use style_traits::ToCss;
+        use values::CSSFloat;
 
-        pub use super::SpecifiedValue as T;
+        #[derive(Copy, Clone, Debug, PartialEq)]
+        #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
+        pub enum T {
+            None,
+            Number(CSSFloat),
+        }
 
         impl ToCss for T {
-            fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
+            fn to_css<W>(&self, dest: &mut W) -> fmt::Result
+                where W: fmt::Write,
+            {
                 match *self {
                     T::None => dest.write_str("none"),
                     T::Number(number) => number.to_css(dest),
                 }
             }
         }
 
         impl Interpolate for T {
             fn interpolate(&self, other: &Self, time: f64) -> Result<Self, ()> {
                 match (*self, *other) {
                     (T::Number(ref number), T::Number(ref other)) =>
-                        Ok(T::Number(Number(try!(number.0.interpolate(&other.0, time))))),
+                        Ok(T::Number(try!(number.interpolate(other, time)))),
                     _ => Err(()),
                 }
             }
         }
     }
 
-    #[inline] pub fn get_initial_value() -> computed_value::T {
+    #[inline]
+    pub fn get_initial_value() -> computed_value::T {
         computed_value::T::None
     }
 
     #[inline]
     pub fn get_initial_specified_value() -> SpecifiedValue {
         SpecifiedValue::None
     }
 
--- a/servo/components/style/properties/longhand/inherited_box.mako.rs
+++ b/servo/components/style/properties/longhand/inherited_box.mako.rs
@@ -91,17 +91,17 @@
     use style_traits::ToCss;
     use values::specified::Angle;
 
     use values::HasViewportPercentage;
     no_viewport_percentage!(SpecifiedValue);
 
     use std::f32::consts::PI;
     use values::CSSFloat;
-    const TWO_PI: CSSFloat = 2.0*PI;
+    const TWO_PI: CSSFloat = 2.0 * PI;
 
     #[derive(Clone, PartialEq, Copy, Debug)]
     #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
     pub struct SpecifiedValue {
         pub angle: Option<Angle>,
         pub flipped: bool
     }
 
@@ -120,68 +120,71 @@
                 } else {
                     dest.write_str("from-image")
                 }
             }
         }
     }
 
     pub mod computed_value {
-        use values::specified::Angle;
+        use values::computed::Angle;
 
         #[derive(Clone, PartialEq, Copy, Debug)]
         #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
         pub enum T {
             FromImage,
             AngleWithFlipped(Angle, bool),
         }
     }
 
-    const INITIAL_ANGLE: Angle = Angle(0.0);
-
     #[inline]
     pub fn get_initial_value() -> computed_value::T {
-        computed_value::T::AngleWithFlipped(INITIAL_ANGLE, false)
+        computed_value::T::AngleWithFlipped(computed::Angle::zero(), false)
     }
 
     // According to CSS Content Module Level 3:
     // The computed value of the property is calculated by rounding the specified angle
     // to the nearest quarter-turn, rounding away from 0, then moduloing the value by 1 turn.
     #[inline]
-    fn normalize_angle(angle: &Angle) -> Angle {
+    fn normalize_angle(angle: &computed::Angle) -> computed::Angle {
         let radians = angle.radians();
         let rounded_quarter_turns = (4.0 * radians / TWO_PI).round();
         let normalized_quarter_turns = (rounded_quarter_turns % 4.0 + 4.0) % 4.0;
         let normalized_radians = normalized_quarter_turns/4.0 * TWO_PI;
-        Angle::from_radians(normalized_radians)
+        computed::Angle::from_radians(normalized_radians)
     }
 
     impl ToComputedValue for SpecifiedValue {
         type ComputedValue = computed_value::T;
 
         #[inline]
-        fn to_computed_value(&self, _: &Context) -> computed_value::T {
+        fn to_computed_value(&self, context: &Context) -> computed_value::T {
             if let Some(ref angle) = self.angle {
-                let normalized_angle = normalize_angle(angle);
+                let angle = angle.to_computed_value(context);
+                let normalized_angle = normalize_angle(&angle);
                 computed_value::T::AngleWithFlipped(normalized_angle, self.flipped)
             } else {
                 if self.flipped {
-                    computed_value::T::AngleWithFlipped(INITIAL_ANGLE, true)
+                    computed_value::T::AngleWithFlipped(computed::Angle::zero(), true)
                 } else {
                     computed_value::T::FromImage
                 }
             }
         }
 
         #[inline]
         fn from_computed_value(computed: &computed_value::T) -> Self {
             match *computed {
                 computed_value::T::FromImage => SpecifiedValue { angle: None, flipped: false },
-                computed_value::T::AngleWithFlipped(angle, flipped) =>
-                    SpecifiedValue { angle: Some(angle), flipped: flipped },
+                computed_value::T::AngleWithFlipped(ref angle, flipped) => {
+                    SpecifiedValue {
+                        angle: Some(Angle::from_computed_value(angle)),
+                        flipped: flipped,
+                    }
+                }
             }
         }
     }
 
     impl ToCss for computed_value::T {
         fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
             match *self {
                 computed_value::T::FromImage => dest.write_str("from-image"),
@@ -200,17 +203,17 @@
         if input.try(|input| input.expect_ident_matching("from-image")).is_ok() {
             // Handle from-image
             Ok(SpecifiedValue { angle: None, flipped: false })
         } else {
             // Handle <angle> | <angle>? flip
             let angle = input.try(|input| Angle::parse(context, input)).ok();
             let flipped = input.try(|input| input.expect_ident_matching("flip")).is_ok();
             let explicit_angle = if angle.is_none() && !flipped {
-                Some(INITIAL_ANGLE)
+                Some(Angle::zero())
             } else {
                 angle
             };
 
             Ok(SpecifiedValue { angle: explicit_angle, flipped: flipped })
         }
     }
 </%helpers:longhand>
--- a/servo/components/style/properties/longhand/inherited_table.mako.rs
+++ b/servo/components/style/properties/longhand/inherited_table.mako.rs
@@ -1,13 +1,13 @@
 /* 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/. */
 
- <%namespace name="helpers" file="/helpers.mako.rs" />
+<%namespace name="helpers" file="/helpers.mako.rs" />
 
 <% data.new_style_struct("InheritedTable", inherited=True, gecko_name="TableBorder") %>
 
 ${helpers.single_keyword("border-collapse", "separate collapse",
                          gecko_constant_prefix="NS_STYLE_BORDER",
                          animatable=False,
                          spec="https://drafts.csswg.org/css-tables/#propdef-border-collapse")}
 ${helpers.single_keyword("empty-cells", "show hide",
@@ -47,67 +47,74 @@
                     vertical: try!(self.vertical.interpolate(&other.vertical, time)),
                 })
             }
         }
     }
 
     impl HasViewportPercentage for SpecifiedValue {
         fn has_viewport_percentage(&self) -> bool {
-            return self.horizontal.has_viewport_percentage() || self.vertical.has_viewport_percentage()
+            self.horizontal.has_viewport_percentage() ||
+            self.vertical.as_ref().map_or(false, |v| v.has_viewport_percentage())
         }
     }
 
     #[derive(Clone, Debug, PartialEq)]
     #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
     pub struct SpecifiedValue {
         pub horizontal: specified::Length,
-        pub vertical: specified::Length,
+        pub vertical: Option<specified::Length>,
     }
 
     #[inline]
     pub fn get_initial_value() -> computed_value::T {
         computed_value::T {
             horizontal: Au(0),
             vertical: Au(0),
         }
     }
 
     impl ToCss for SpecifiedValue {
-        fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
+        fn to_css<W>(&self, dest: &mut W) -> fmt::Result
+            where W: fmt::Write,
+        {
             try!(self.horizontal.to_css(dest));
-            try!(dest.write_str(" "));
-            self.vertical.to_css(dest)
+            if let Some(vertical) = self.vertical.as_ref() {
+                try!(dest.write_str(" "));
+                vertical.to_css(dest)?;
+            }
+            Ok(())
         }
     }
 
     impl ToCss for computed_value::T {
         fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
             try!(self.horizontal.to_css(dest));
             try!(dest.write_str(" "));
             self.vertical.to_css(dest)
         }
     }
 
     impl ToComputedValue for SpecifiedValue {
         type ComputedValue = computed_value::T;
 
         #[inline]
         fn to_computed_value(&self, context: &Context) -> computed_value::T {
+            let horizontal = self.horizontal.to_computed_value(context);
             computed_value::T {
-                horizontal: self.horizontal.to_computed_value(context),
-                vertical: self.vertical.to_computed_value(context),
+                horizontal: horizontal,
+                vertical: self.vertical.as_ref().map_or(horizontal, |v| v.to_computed_value(context)),
             }
         }
 
         #[inline]
         fn from_computed_value(computed: &computed_value::T) -> Self {
             SpecifiedValue {
                 horizontal: ToComputedValue::from_computed_value(&computed.horizontal),
-                vertical: ToComputedValue::from_computed_value(&computed.vertical),
+                vertical: Some(ToComputedValue::from_computed_value(&computed.vertical)),
             }
         }
     }
 
     pub fn parse(_: &ParserContext, input: &mut Parser) -> Result<SpecifiedValue,()> {
         let mut first = None;
         let mut second = None;
         match specified::Length::parse_non_negative(input) {
@@ -118,22 +125,22 @@
                     second = Some(len);
                 }
             }
         }
         match (first, second) {
             (None, None) => Err(()),
             (Some(length), None) => {
                 Ok(SpecifiedValue {
-                    horizontal: length.clone(),
-                    vertical: length,
+                    horizontal: length,
+                    vertical: None,
                 })
             }
             (Some(horizontal), Some(vertical)) => {
                 Ok(SpecifiedValue {
                     horizontal: horizontal,
-                    vertical: vertical,
+                    vertical: Some(vertical),
                 })
             }
-            (None, Some(_)) => panic!("shouldn't happen"),
+            (None, Some(_)) => unreachable!(),
         }
     }
 </%helpers:longhand>
--- a/servo/components/style/properties/longhand/inherited_text.mako.rs
+++ b/servo/components/style/properties/longhand/inherited_text.mako.rs
@@ -5,17 +5,17 @@
 <%namespace name="helpers" file="/helpers.mako.rs" />
 <% from data import Keyword %>
 <% data.new_style_struct("InheritedText", inherited=True, gecko_name="Text") %>
 
 <%helpers:longhand name="line-height" animatable="True"
                    spec="https://drafts.csswg.org/css2/visudet.html#propdef-line-height">
     use std::fmt;
     use style_traits::ToCss;
-    use values::{CSSFloat, HasViewportPercentage};
+    use values::HasViewportPercentage;
 
     impl HasViewportPercentage for SpecifiedValue {
         fn has_viewport_percentage(&self) -> bool {
             match *self {
                 SpecifiedValue::LengthOrPercentage(ref length) => length.has_viewport_percentage(),
                 _ => false
             }
         }
@@ -23,58 +23,58 @@
 
     #[derive(Debug, Clone, PartialEq)]
     #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
     pub enum SpecifiedValue {
         Normal,
         % if product == "gecko":
             MozBlockHeight,
         % endif
-        Number(CSSFloat),
+        Number(specified::Number),
         LengthOrPercentage(specified::LengthOrPercentage),
     }
 
     impl ToCss for SpecifiedValue {
         fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
             match *self {
                 SpecifiedValue::Normal => dest.write_str("normal"),
                 % if product == "gecko":
                     SpecifiedValue::MozBlockHeight => dest.write_str("-moz-block-height"),
                 % endif
                 SpecifiedValue::LengthOrPercentage(ref value) => value.to_css(dest),
-                SpecifiedValue::Number(number) => write!(dest, "{}", number),
+                SpecifiedValue::Number(number) => number.to_css(dest),
             }
         }
     }
     /// normal | <number> | <length> | <percentage>
     pub fn parse(_context: &ParserContext, input: &mut Parser) -> Result<SpecifiedValue, ()> {
         use cssparser::Token;
         use std::ascii::AsciiExt;
         // We try to parse as a Number first because, for 'line-height', we want "0" to be
         // parsed as a plain Number rather than a Length (0px); this matches the behaviour
         // of all major browsers
         input.try(specified::Number::parse_non_negative)
-        .map(|n| SpecifiedValue::Number(n.0))
-        .or_else(|()| {
-            input.try(specified::LengthOrPercentage::parse_non_negative)
-            .map(SpecifiedValue::LengthOrPercentage)
+            .map(SpecifiedValue::Number)
             .or_else(|()| {
-                match try!(input.next()) {
-                    Token::Ident(ref value) if value.eq_ignore_ascii_case("normal") => {
-                        Ok(SpecifiedValue::Normal)
+                input.try(specified::LengthOrPercentage::parse_non_negative)
+                .map(SpecifiedValue::LengthOrPercentage)
+                .or_else(|()| {
+                    match try!(input.next()) {
+                        Token::Ident(ref value) if value.eq_ignore_ascii_case("normal") => {
+                            Ok(SpecifiedValue::Normal)
+                        }
+                        % if product == "gecko":
+                        Token::Ident(ref value) if value.eq_ignore_ascii_case("-moz-block-height") => {
+                            Ok(SpecifiedValue::MozBlockHeight)
+                        }
+                        % endif
+                        _ => Err(()),
                     }
-                    % if product == "gecko":
-                    Token::Ident(ref value) if value.eq_ignore_ascii_case("-moz-block-height") => {
-                        Ok(SpecifiedValue::MozBlockHeight)
-                    }
-                    % endif
-                    _ => Err(()),
-                }
+                })
             })
-        })
     }
     pub mod computed_value {
         use app_units::Au;
         use std::fmt;
         use values::CSSFloat;
         #[derive(PartialEq, Copy, Clone, Debug)]
         #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
         pub enum T {
@@ -111,17 +111,17 @@
 
         #[inline]
         fn to_computed_value(&self, context: &Context) -> computed_value::T {
             match *self {
                 SpecifiedValue::Normal => computed_value::T::Normal,
                 % if product == "gecko":
                     SpecifiedValue::MozBlockHeight => computed_value::T::MozBlockHeight,
                 % endif
-                SpecifiedValue::Number(value) => computed_value::T::Number(value),
+                SpecifiedValue::Number(value) => computed_value::T::Number(value.to_computed_value(context)),
                 SpecifiedValue::LengthOrPercentage(ref value) => {
                     match *value {
                         specified::LengthOrPercentage::Length(ref value) =>
                             computed_value::T::Length(value.to_computed_value(context)),
                         specified::LengthOrPercentage::Percentage(specified::Percentage(value)) => {
                             let fr = specified::Length::NoCalc(specified::NoCalcLength::FontRelative(
                                 specified::FontRelativeLength::Em(value)));
                             computed_value::T::Length(fr.to_computed_value(context))
@@ -139,17 +139,19 @@
 
         #[inline]
         fn from_computed_value(computed: &computed_value::T) -> Self {
             match *computed {
                 computed_value::T::Normal => SpecifiedValue::Normal,
                 % if product == "gecko":
                     computed_value::T::MozBlockHeight => SpecifiedValue::MozBlockHeight,
                 % endif
-                computed_value::T::Number(value) => SpecifiedValue::Number(value),
+                computed_value::T::Number(ref value) => {
+                    SpecifiedValue::Number(specified::Number::from_computed_value(value))
+                },
                 computed_value::T::Length(au) => {
                     SpecifiedValue::LengthOrPercentage(specified::LengthOrPercentage::Length(
                         ToComputedValue::from_computed_value(&au)
                     ))
                 }
             }
         }
     }
--- a/servo/components/style/properties/longhand/position.mako.rs
+++ b/servo/components/style/properties/longhand/position.mako.rs
@@ -125,37 +125,19 @@
     ${helpers.predefined_type(name="justify-self",
                               type="AlignJustifySelf",
                               initial_value="specified::AlignJustifySelf::auto()",
                               spec="https://drafts.csswg.org/css-align/#justify-self-property",
                               animatable=False)}
 % endif
 
 // https://drafts.csswg.org/css-flexbox/#propdef-order
-<%helpers:longhand name="order" animatable="True" extra_prefixes="webkit"
-                   spec="https://drafts.csswg.org/css-flexbox/#order-property">
-    use values::computed::ComputedValueAsSpecified;
-
-    impl ComputedValueAsSpecified for SpecifiedValue {}
-
-    pub type SpecifiedValue = computed_value::T;
-
-    pub mod computed_value {
-        pub type T = i32;
-    }
-
-    #[inline]
-    pub fn get_initial_value() -> computed_value::T {
-        0
-    }
-
-    fn parse(_context: &ParserContext, input: &mut Parser) -> Result<SpecifiedValue, ()> {
-        specified::parse_integer(input)
-    }
-</%helpers:longhand>
+${helpers.predefined_type("order", "Integer", "0",
+                          animatable=True,
+                          spec="https://drafts.csswg.org/css-flexbox/#order-property")}
 
 // FIXME: Gecko doesn't support content value yet.
 // FIXME: This property should be animatable.
 ${helpers.predefined_type("flex-basis",
                           "LengthOrPercentageOrAuto" if product == "gecko" else
                           "LengthOrPercentageOrAutoOrContent",
                           "computed::LengthOrPercentageOrAuto::Auto" if product == "gecko" else
                           "computed::LengthOrPercentageOrAutoOrContent::Auto",
--- a/servo/components/style/properties/shorthand/position.mako.rs
+++ b/servo/components/style/properties/shorthand/position.mako.rs
@@ -65,18 +65,18 @@
 
     pub fn parse_value(context: &ParserContext, input: &mut Parser) -> Result<Longhands, ()> {
         let mut grow = None;
         let mut shrink = None;
         let mut basis = None;
 
         if input.try(|input| input.expect_ident_matching("none")).is_ok() {
             return Ok(Longhands {
-                flex_grow: Number(0.0),
-                flex_shrink: Number(0.0),
+                flex_grow: Number::new(0.0),
+                flex_shrink: Number::new(0.0),
                 % if product == "gecko":
                     flex_basis: LengthOrPercentageOrAuto::Auto
                 % else:
                     flex_basis: LengthOrPercentageOrAutoOrContent::Auto
                 % endif
 
             })
         }
@@ -100,18 +100,18 @@
             }
             break
         }
 
         if grow.is_none() && basis.is_none() {
             return Err(())
         }
         Ok(Longhands {
-            flex_grow: grow.unwrap_or(Number(1.0)),
-            flex_shrink: shrink.unwrap_or(Number(1.0)),
+            flex_grow: grow.unwrap_or(Number::new(1.0)),
+            flex_shrink: shrink.unwrap_or(Number::new(1.0)),
             % if product == "gecko":
                 flex_basis: basis.unwrap_or(LengthOrPercentageOrAuto::Length(NoCalcLength::zero()))
             % else:
                 flex_basis: basis.unwrap_or(LengthOrPercentageOrAutoOrContent::Length(NoCalcLength::zero()))
             % endif
         })
     }
 
--- a/servo/components/style/values/computed/image.rs
+++ b/servo/components/style/values/computed/image.rs
@@ -532,54 +532,54 @@ pub enum AngleOrCorner {
     Angle(Angle),
     Corner(HorizontalDirection, VerticalDirection)
 }
 
 impl ToComputedValue for specified::AngleOrCorner {
     type ComputedValue = AngleOrCorner;
 
     #[inline]
-    fn to_computed_value(&self, _: &Context) -> AngleOrCorner {
+    fn to_computed_value(&self, context: &Context) -> AngleOrCorner {
         match *self {
             specified::AngleOrCorner::None => {
-                AngleOrCorner::Angle(Angle(PI))
+                AngleOrCorner::Angle(Angle::from_radians(PI))
             },
             specified::AngleOrCorner::Angle(angle) => {
-                AngleOrCorner::Angle(angle)
+                AngleOrCorner::Angle(angle.to_computed_value(context))
             },
             specified::AngleOrCorner::Corner(horizontal, vertical) => {
                 match (horizontal, vertical) {
                     (None, Some(VerticalDirection::Top)) => {
-                        AngleOrCorner::Angle(Angle(0.0))
+                        AngleOrCorner::Angle(Angle::from_radians(0.0))
                     },
                     (Some(HorizontalDirection::Right), None) => {
-                        AngleOrCorner::Angle(Angle(PI * 0.5))
+                        AngleOrCorner::Angle(Angle::from_radians(PI * 0.5))
                     },
                     (None, Some(VerticalDirection::Bottom)) => {
-                        AngleOrCorner::Angle(Angle(PI))
+                        AngleOrCorner::Angle(Angle::from_radians(PI))
                     },
                     (Some(HorizontalDirection::Left), None) => {
-                        AngleOrCorner::Angle(Angle(PI * 1.5))
+                        AngleOrCorner::Angle(Angle::from_radians(PI * 1.5))
                     },
                     (Some(horizontal), Some(vertical)) => {
                         AngleOrCorner::Corner(horizontal, vertical)
                     },
                     (None, None) => {
                         unreachable!()
                     }
                 }
             }
         }
     }
 
     #[inline]
     fn from_computed_value(computed: &AngleOrCorner) -> Self {
         match *computed {
-            AngleOrCorner::Angle(angle) => {
-                specified::AngleOrCorner::Angle(angle)
+            AngleOrCorner::Angle(ref angle) => {
+                specified::AngleOrCorner::Angle(specified::Angle::from_computed_value(angle))
             },
             AngleOrCorner::Corner(horizontal, vertical) => {
                 specified::AngleOrCorner::Corner(Some(horizontal), Some(vertical))
             }
         }
     }
 }
 
--- a/servo/components/style/values/computed/mod.rs
+++ b/servo/components/style/values/computed/mod.rs
@@ -15,17 +15,17 @@ use super::{CSSFloat, CSSInteger, RGBA, 
 use super::specified::grid::{TrackBreadth as GenericTrackBreadth, TrackSize as GenericTrackSize};
 
 pub use cssparser::Color as CSSColor;
 pub use self::image::{AngleOrCorner, EndingShape as GradientShape, Gradient, GradientKind, Image, ImageRect};
 pub use self::image::{LengthOrKeyword, LengthOrPercentageOrKeyword};
 pub use super::{Auto, Either, None_};
 #[cfg(feature = "gecko")]
 pub use super::specified::{AlignItems, AlignJustifyContent, AlignJustifySelf, JustifyItems};
-pub use super::specified::{Angle, BorderStyle, GridLine, Percentage, Time, UrlOrNone};
+pub use super::specified::{BorderStyle, GridLine, Percentage, UrlOrNone};
 pub use super::specified::url::SpecifiedUrl;
 pub use self::length::{CalcLengthOrPercentage, Length, LengthOrNumber, LengthOrPercentage, LengthOrPercentageOrAuto};
 pub use self::length::{LengthOrPercentageOrAutoOrContent, LengthOrPercentageOrNone, LengthOrNone};
 pub use self::length::{MaxLength, MinLength};
 pub use self::position::Position;
 
 pub mod basic_shape;
 pub mod image;
@@ -109,16 +109,86 @@ impl<T> ToComputedValue for T
     }
 
     #[inline]
     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,
+}
+
+impl Angle {
+    /// Construct a computed `Angle` value from a radian amount.
+    pub fn from_radians(radians: CSSFloat) -> Self {
+        Angle {
+            radians: radians,
+        }
+    }
+
+    /// Return the amount of radians this angle represents.
+    #[inline]
+    pub fn radians(&self) -> CSSFloat {
+        self.radians
+    }
+
+    /// Returns an angle that represents a rotation of zero radians.
+    pub fn zero() -> Self {
+        Self::from_radians(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())
+    }
+}
+
+/// A computed `<time>` value.
+#[derive(Clone, PartialEq, PartialOrd, Copy, Debug)]
+#[cfg_attr(feature = "servo", derive(HeapSizeOf, Deserialize, Serialize))]
+pub struct Time {
+    seconds: CSSFloat,
+}
+
+impl Time {
+    /// Construct a computed `Time` value from a seconds amount.
+    pub fn from_seconds(seconds: CSSFloat) -> Self {
+        Time {
+            seconds: seconds,
+        }
+    }
+
+    /// Construct a computed `Time` value that represents zero seconds.
+    pub fn zero() -> Self {
+        Self::from_seconds(0.0)
+    }
+
+    /// Return the amount of seconds this time represents.
+    #[inline]
+    pub fn seconds(&self) -> CSSFloat {
+        self.seconds
+    }
+}
+
+impl ToCss for Time {
+    fn to_css<W>(&self, dest: &mut W) -> fmt::Result
+        where W: fmt::Write,
+    {
+        write!(dest, "{}s", self.seconds())
+    }
+}
+
 impl ToComputedValue for specified::Color {
     type ComputedValue = RGBA;
 
     #[cfg(not(feature = "gecko"))]
     fn to_computed_value(&self, context: &Context) -> RGBA {
         match *self {
             specified::Color::RGBA(rgba) => rgba,
             specified::Color::CurrentColor => context.inherited_style.get_color().clone_color(),
--- a/servo/components/style/values/specified/length.rs
+++ b/servo/components/style/values/specified/length.rs
@@ -502,18 +502,18 @@ pub struct CalcProductNode {
     values: Vec<CalcValueNode>
 }
 
 /// A value inside a `Calc` expression.
 #[derive(Clone, Debug)]
 #[allow(missing_docs)]
 pub enum CalcValueNode {
     Length(NoCalcLength),
-    Angle(Angle),
-    Time(Time),
+    Angle(CSSFloat),
+    Time(CSSFloat),
     Percentage(CSSFloat),
     Number(CSSFloat),
     Sum(Box<CalcSumNode>),
 }
 
 #[derive(Clone, Copy, PartialEq)]
 #[allow(missing_docs)]
 pub enum CalcUnit {
@@ -610,20 +610,24 @@ impl CalcLengthOrPercentage {
     fn parse_value(input: &mut Parser, expected_unit: CalcUnit) -> Result<CalcValueNode, ()> {
         match (try!(input.next()), expected_unit) {
             (Token::Number(ref value), _) => Ok(CalcValueNode::Number(value.value)),
             (Token::Dimension(ref value, ref unit), CalcUnit::Length) |
             (Token::Dimension(ref value, ref unit), CalcUnit::LengthOrPercentage) => {
                 NoCalcLength::parse_dimension(value.value, unit).map(CalcValueNode::Length)
             }
             (Token::Dimension(ref value, ref unit), CalcUnit::Angle) => {
-                Angle::parse_dimension(value.value, unit).map(CalcValueNode::Angle)
+                Angle::parse_dimension(value.value, unit).map(|angle| {
+                    CalcValueNode::Angle(angle.radians())
+                })
             }
             (Token::Dimension(ref value, ref unit), CalcUnit::Time) => {
-                Time::parse_dimension(value.value, unit).map(CalcValueNode::Time)
+                Time::parse_dimension(value.value, unit).map(|time| {
+                    CalcValueNode::Time(time.seconds())
+                })
             }
             (Token::Percentage(ref value), CalcUnit::LengthOrPercentage) =>
                 Ok(CalcValueNode::Percentage(value.unit_value)),
             (Token::ParenthesisBlock, _) => {
                 input.parse_nested_block(|i| CalcLengthOrPercentage::parse_sum(i, expected_unit))
                      .map(|result| CalcValueNode::Sum(Box::new(result)))
             },
             _ => Err(())
@@ -797,24 +801,24 @@ impl CalcLengthOrPercentage {
                 value => simplified.push(value),
             }
         }
 
         let mut time = None;
 
         for value in simplified {
             match value {
-                SimplifiedValueNode::Time(Time(val)) =>
+                SimplifiedValueNode::Time(val) =>
                     time = Some(time.unwrap_or(0.) + val),
                 _ => return Err(()),
             }
         }
 
         match time {
-            Some(time) => Ok(Time(time)),
+            Some(time) => Ok(Time::from_calc(time)),
             _ => Err(())
         }
     }
 
     #[allow(missing_docs)]
     pub fn parse_angle(input: &mut Parser) -> Result<Angle, ()> {
         let ast = try!(CalcLengthOrPercentage::parse_sum(input, CalcUnit::Angle));
 
@@ -826,26 +830,33 @@ impl CalcLengthOrPercentage {
             }
         }
 
         let mut angle = None;
         let mut number = None;
 
         for value in simplified {
             match value {
-                SimplifiedValueNode::Angle(Angle(val)) =>
-                    angle = Some(angle.unwrap_or(0.) + val),
-                SimplifiedValueNode::Number(val) => number = Some(number.unwrap_or(0.) + val),
+                SimplifiedValueNode::Angle(val) => {
+                    angle = Some(angle.unwrap_or(0.) + val)
+                }
+                // TODO(emilio): This `Number` logic looks fishy.
+                //
+                // In particular, this allows calc(2 - 2) to parse as an
+                // `Angle`, which doesn't seem desired to me.
+                SimplifiedValueNode::Number(val) => {
+                    number = Some(number.unwrap_or(0.) + val)
+                }
                 _ => unreachable!()
             }
         }
 
         match (angle, number) {
-            (Some(angle), None) => Ok(Angle(angle)),
-            (None, Some(value)) if value == 0. => Ok(Angle(0.)),
+            (Some(angle), None) => Ok(Angle::from_calc(angle)),
+            (None, Some(value)) if value == 0. => Ok(Angle::from_calc(0.)),
             _ => Err(())
         }
     }
 }
 
 impl HasViewportPercentage for CalcLengthOrPercentage {
     fn has_viewport_percentage(&self) -> bool {
         self.vw.is_some() || self.vh.is_some() ||
@@ -894,22 +905,34 @@ impl ToCss for CalcLengthOrPercentage {
 
         write!(dest, ")")
      }
 }
 
 /// A percentage value.
 ///
 /// [0 .. 100%] maps to [0.0 .. 1.0]
+///
+/// FIXME(emilio): There's no standard property that requires a `<percentage>`
+/// without requiring also a `<length>`. If such a property existed, we'd need
+/// to add special handling for `calc()` and percentages in here in the same way
+/// as for `Angle` and `Time`, but the lack of this this is otherwise
+/// undistinguishable (we handle it correctly from `CalcLengthOrPercentage`).
+///
+/// As of today, only `-moz-image-rect` supports percentages without length.
+/// This is not a regression, and that's a non-standard extension anyway, so I'm
+/// not implementing it for now.
 #[derive(Clone, PartialEq, Copy, Debug)]
 #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
 pub struct Percentage(pub CSSFloat);
 
 impl ToCss for Percentage {
-    fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
+    fn to_css<W>(&self, dest: &mut W) -> fmt::Result
+        where W: fmt::Write,
+    {
         write!(dest, "{}%", self.0 * 100.)
     }
 }
 
 impl Percentage {
     fn parse_internal(input: &mut Parser, context: AllowedNumericType) -> Result<Self, ()> {
         match try!(input.next()) {
             Token::Percentage(ref value) if context.is_ok(value.unit_value) => {
--- a/servo/components/style/values/specified/mod.rs
+++ b/servo/components/style/values/specified/mod.rs
@@ -13,17 +13,17 @@ use parser::{ParserContext, Parse};
 use self::grid::{TrackBreadth as GenericTrackBreadth, TrackSize as GenericTrackSize};
 use self::url::SpecifiedUrl;
 use std::ascii::AsciiExt;
 use std::f32::consts::PI;
 use std::fmt;
 use std::ops::Mul;
 use style_traits::ToCss;
 use super::{Auto, CSSFloat, CSSInteger, HasViewportPercentage, Either, None_};
-use super::computed::{ComputedValueAsSpecified, Context};
+use super::computed::{self, Context};
 use super::computed::{Shadow as ComputedShadow, ToComputedValue};
 
 #[cfg(feature = "gecko")]
 pub use self::align::{AlignItems, AlignJustifyContent, AlignJustifySelf, JustifyItems};
 pub use self::color::Color;
 pub use self::grid::{GridLine, TrackKeyword};
 pub use self::image::{AngleOrCorner, ColorStop, EndingShape as GradientEndingShape, Gradient};
 pub use self::image::{GradientKind, HorizontalDirection, Image, ImageRect, LengthOrKeyword};
@@ -161,96 +161,111 @@ impl<'a> Mul<CSSFloat> for &'a Simplifie
         }
     }
 }
 
 #[derive(Clone, Debug)]
 #[allow(missing_docs)]
 pub enum SimplifiedValueNode {
     Length(NoCalcLength),
-    Angle(Angle),
-    Time(Time),
+    Angle(CSSFloat),
+    Time(CSSFloat),
     Percentage(CSSFloat),
     Number(CSSFloat),
     Sum(Box<SimplifiedSumNode>),
 }
 
 impl<'a> Mul<CSSFloat> for &'a SimplifiedValueNode {
     type Output = SimplifiedValueNode;
 
     #[inline]
     fn mul(self, scalar: CSSFloat) -> SimplifiedValueNode {
         match *self {
-            SimplifiedValueNode::Length(ref l) => SimplifiedValueNode::Length(l.clone() * scalar),
-            SimplifiedValueNode::Percentage(p) => SimplifiedValueNode::Percentage(p * scalar),
-            SimplifiedValueNode::Angle(Angle(a)) => SimplifiedValueNode::Angle(Angle(a * scalar)),
-            SimplifiedValueNode::Time(Time(t)) => SimplifiedValueNode::Time(Time(t * scalar)),
-            SimplifiedValueNode::Number(n) => SimplifiedValueNode::Number(n * scalar),
+            SimplifiedValueNode::Length(ref l) => {
+                SimplifiedValueNode::Length(l.clone() * scalar)
+            },
+            SimplifiedValueNode::Percentage(p) => {
+                SimplifiedValueNode::Percentage(p * scalar)
+            },
+            SimplifiedValueNode::Angle(a) => {
+                SimplifiedValueNode::Angle(a * scalar)
+            },
+            SimplifiedValueNode::Time(t) => {
+                SimplifiedValueNode::Time(t * scalar)
+            },
+            SimplifiedValueNode::Number(n) => {
+                SimplifiedValueNode::Number(n * scalar)
+            },
             SimplifiedValueNode::Sum(ref s) => {
                 let sum = &**s * scalar;
                 SimplifiedValueNode::Sum(Box::new(sum))
-            }
+            },
         }
     }
 }
 
 #[allow(missing_docs)]
-pub fn parse_integer(input: &mut Parser) -> Result<CSSInteger, ()> {
+pub fn parse_integer(input: &mut Parser) -> Result<Integer, ()> {
     match try!(input.next()) {
-        Token::Number(ref value) => value.int_value.ok_or(()),
+        Token::Number(ref value) => value.int_value.ok_or(()).map(Integer::new),
         Token::Function(ref name) if name.eq_ignore_ascii_case("calc") => {
-            let ast = try!(input.parse_nested_block(|i| CalcLengthOrPercentage::parse_sum(i, CalcUnit::Integer)));
+            let ast = try!(input.parse_nested_block(|i| {
+                CalcLengthOrPercentage::parse_sum(i, CalcUnit::Integer)
+            }));
 
             let mut result = None;
 
             for ref node in ast.products {
                 match try!(CalcLengthOrPercentage::simplify_product(node)) {
                     SimplifiedValueNode::Number(val) =>
                         result = Some(result.unwrap_or(0) + val as CSSInteger),
                     _ => unreachable!()
                 }
             }
 
             match result {
-                Some(result) => Ok(result),
+                Some(result) => Ok(Integer::from_calc(result)),
                 _ => Err(())
             }
         }
         _ => Err(())
     }
 }
 
 #[allow(missing_docs)]
-pub fn parse_number(input: &mut Parser) -> Result<f32, ()> {
+pub fn parse_number(input: &mut Parser) -> Result<Number, ()> {
+    use std::f32;
+
     match try!(input.next()) {
         Token::Number(ref value) => {
-            use std::f32;
-            if value.value.is_finite() {
-                Ok(value.value)
-            } else if value.value.is_sign_positive() {
-                Ok(f32::MAX)
-            } else {
-                Ok(f32::MIN)
-            }
+            Ok(Number {
+                value: value.value.min(f32::MAX).max(f32::MIN),
+                was_calc: false,
+            })
         },
         Token::Function(ref name) if name.eq_ignore_ascii_case("calc") => {
             let ast = try!(input.parse_nested_block(|i| CalcLengthOrPercentage::parse_sum(i, CalcUnit::Number)));
 
             let mut result = None;
 
             for ref node in ast.products {
                 match try!(CalcLengthOrPercentage::simplify_product(node)) {
                     SimplifiedValueNode::Number(val) =>
                         result = Some(result.unwrap_or(0.) + val),
                     _ => unreachable!()
                 }
             }
 
             match result {
-                Some(result) => Ok(result),
+                Some(result) => {
+                    Ok(Number {
+                        value: result.min(f32::MAX).max(f32::MIN),
+                        was_calc: true,
+                    })
+                },
                 _ => Err(())
             }
         }
         _ => Err(())
     }
 }
 
 #[derive(Clone, PartialEq, Debug)]
@@ -294,66 +309,112 @@ impl ToCss for BorderRadiusSize {
         try!(dest.write_str(" "));
         self.0.height.to_css(dest)
     }
 }
 
 #[derive(Clone, PartialEq, PartialOrd, Copy, Debug)]
 #[cfg_attr(feature = "servo", derive(HeapSizeOf, Deserialize, Serialize))]
 /// An angle, normalized to radians.
-pub struct Angle(pub CSSFloat);
+pub struct Angle {
+    radians: CSSFloat,
+    was_calc: bool,
+}
 
 impl ToCss for Angle {
     fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
-        write!(dest, "{}rad", self.0)
+        if self.was_calc {
+            dest.write_str("calc(")?;
+        }
+        write!(dest, "{}rad", self.radians)?;
+        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())
+    }
+
+    fn from_computed_value(computed: &Self::ComputedValue) -> Self {
+        Angle {
+            radians: computed.radians(),
+            was_calc: false,
+        }
     }
 }
 
 impl Angle {
     #[inline]
     #[allow(missing_docs)]
     pub fn radians(self) -> f32 {
-        self.0
+        self.radians
+    }
+
+    /// Returns an angle value that represents zero radians.
+    pub fn zero() -> Self {
+        Self::from_radians(0.0)
     }
 
     #[inline]
     #[allow(missing_docs)]
     pub fn from_radians(r: f32) -> Self {
-        Angle(r)
+        Angle {
+            radians: r,
+            was_calc: false,
+        }
+    }
+
+    /// Returns an `Angle` parsed from a `calc()` expression.
+    pub fn from_calc(radians: CSSFloat) -> Self {
+        Angle {
+            radians: radians,
+            was_calc: true,
+        }
     }
 }
 
 const RAD_PER_DEG: CSSFloat = PI / 180.0;
 const RAD_PER_GRAD: CSSFloat = PI / 200.0;
 const RAD_PER_TURN: CSSFloat = PI * 2.0;
 
 impl Parse for Angle {
     /// Parses an angle according to CSS-VALUES § 6.1.
     fn parse(_context: &ParserContext, input: &mut Parser) -> Result<Self, ()> {
         match try!(input.next()) {
             Token::Dimension(ref value, ref unit) => Angle::parse_dimension(value.value, unit),
-            Token::Number(ref value) if value.value == 0. => Ok(Angle(0.)),
+            Token::Number(ref value) if value.value == 0. => Ok(Angle::zero()),
             Token::Function(ref name) if name.eq_ignore_ascii_case("calc") => {
                 input.parse_nested_block(CalcLengthOrPercentage::parse_angle)
             },
             _ => Err(())
         }
     }
 }
 
 impl Angle {
     #[allow(missing_docs)]
     pub fn parse_dimension(value: CSSFloat, unit: &str) -> Result<Angle, ()> {
-        match_ignore_ascii_case! { unit,
-            "deg" => Ok(Angle(value * RAD_PER_DEG)),
-            "grad" => Ok(Angle(value * RAD_PER_GRAD)),
-            "turn" => Ok(Angle(value * RAD_PER_TURN)),
-            "rad" => Ok(Angle(value)),
-             _ => Err(())
-        }
+        let radians = match_ignore_ascii_case! { unit,
+            "deg" => value * RAD_PER_DEG,
+            "grad" => value * RAD_PER_GRAD,
+            "turn" => value * RAD_PER_TURN,
+            "rad" => value,
+             _ => return Err(())
+        };
+
+        Ok(Angle {
+            radians: radians,
+            was_calc: false,
+        })
     }
 }
 
 #[allow(missing_docs)]
 pub fn parse_border_radius(context: &ParserContext, input: &mut Parser) -> Result<BorderRadiusSize, ()> {
     input.try(|i| BorderRadiusSize::parse(context, i)).or_else(|_| {
         match_ignore_ascii_case! { &try!(input.expect_ident()),
             "thin" => Ok(BorderRadiusSize::circle(
@@ -474,38 +535,77 @@ impl BorderStyle {
     pub fn none_or_hidden(&self) -> bool {
         matches!(*self, BorderStyle::none | BorderStyle::hidden)
     }
 }
 
 /// A time in seconds according to CSS-VALUES § 6.2.
 #[derive(Clone, Copy, Debug, PartialEq, PartialOrd)]
 #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
-pub struct Time(pub CSSFloat);
+pub struct Time {
+    seconds: CSSFloat,
+    was_calc: bool,
+}
 
 impl Time {
+    /// Return a `<time>` value that represents `seconds` seconds.
+    pub fn from_seconds(seconds: CSSFloat) -> Self {
+        Time {
+            seconds: seconds,
+            was_calc: false,
+        }
+    }
+
+    /// Returns a time that represents a duration of zero.
+    pub fn zero() -> Self {
+        Self::from_seconds(0.0)
+    }
+
     /// Returns the time in fractional seconds.
-    pub fn seconds(self) -> f32 {
-        let Time(seconds) = self;
-        seconds
+    pub fn seconds(self) -> CSSFloat {
+        self.seconds
     }
 
     /// Parses a time according to CSS-VALUES § 6.2.
     fn parse_dimension(value: CSSFloat, unit: &str) -> Result<Time, ()> {
-        if unit.eq_ignore_ascii_case("s") {
-            Ok(Time(value))
-        } else if unit.eq_ignore_ascii_case("ms") {
-            Ok(Time(value / 1000.0))
-        } else {
-            Err(())
+        let seconds = match_ignore_ascii_case! { unit,
+            "s" => value,
+            "ms" => value / 1000.0,
+            _ => return Err(()),
+        };
+
+        Ok(Time {
+            seconds: seconds,
+            was_calc: false,
+        })
+    }
+
+    /// Returns a `Time` value from a CSS `calc()` expression.
+    pub fn from_calc(seconds: CSSFloat) -> Self {
+        Time {
+            seconds: seconds,
+            was_calc: true,
         }
     }
 }
 
-impl ComputedValueAsSpecified for Time {}
+impl ToComputedValue for Time {
+    type ComputedValue = computed::Time;
+
+    fn to_computed_value(&self, _context: &Context) -> Self::ComputedValue {
+        computed::Time::from_seconds(self.seconds())
+    }
+
+    fn from_computed_value(computed: &Self::ComputedValue) -> Self {
+        Time {
+            seconds: computed.seconds(),
+            was_calc: false,
+        }
+    }
+}
 
 impl Parse for Time {
     fn parse(_context: &ParserContext, input: &mut Parser) -> Result<Self, ()> {
         match input.next() {
             Ok(Token::Dimension(ref value, ref unit)) => {
                 Time::parse_dimension(value.value, &unit)
             }
             Ok(Token::Function(ref name)) if name.eq_ignore_ascii_case("calc") => {
@@ -513,67 +613,101 @@ impl Parse for Time {
             }
             _ => Err(())
         }
     }
 }
 
 impl ToCss for Time {
     fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
-        write!(dest, "{}s", self.0)
+        if self.was_calc {
+            dest.write_str("calc(")?;
+        }
+        write!(dest, "{}s", self.seconds)?;
+        if self.was_calc {
+            dest.write_str(")")?;
+        }
+        Ok(())
     }
 }
 
 #[derive(Clone, Copy, Debug, PartialEq, PartialOrd)]
 #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
 #[allow(missing_docs)]
-pub struct Number(pub CSSFloat);
+pub struct Number {
+    /// The numeric value itself.
+    pub value: CSSFloat,
+    /// Whether this came from a `calc()` expression. This is needed for
+    /// serialization purposes, since `calc(1)` should still serialize to
+    /// `calc(1)`, not just `1`.
+    was_calc: bool,
+}
 
 no_viewport_percentage!(Number);
 
 impl Parse for Number {
     fn parse(_context: &ParserContext, input: &mut Parser) -> Result<Self, ()> {
-        parse_number(input).map(Number)
+        parse_number(input)
     }
 }
 
 impl Number {
     fn parse_with_minimum(input: &mut Parser, min: CSSFloat) -> Result<Number, ()> {
         match parse_number(input) {
-            Ok(value) if value >= min => Ok(Number(value)),
+            Ok(value) if value.value >= min => Ok(value),
             _ => Err(()),
         }
     }
 
+    /// Returns a new number with the value `val`.
+    pub fn new(val: CSSFloat) -> Self {
+        Number {
+            value: val,
+            was_calc: false,
+        }
+    }
+
     #[allow(missing_docs)]
     pub fn parse_non_negative(input: &mut Parser) -> Result<Number, ()> {
         Number::parse_with_minimum(input, 0.0)
     }
 
     #[allow(missing_docs)]
     pub fn parse_at_least_one(input: &mut Parser) -> Result<Number, ()> {
         Number::parse_with_minimum(input, 1.0)
     }
 }
 
 impl ToComputedValue for Number {
     type ComputedValue = CSSFloat;
 
     #[inline]
-    fn to_computed_value(&self, _: &Context) -> CSSFloat { self.0 }
+    fn to_computed_value(&self, _: &Context) -> CSSFloat { self.value }
 
     #[inline]
     fn from_computed_value(computed: &CSSFloat) -> Self {
-        Number(*computed)
+        Number {
+            value: *computed,
+            was_calc: false,
+        }
     }
 }
 
 impl ToCss for Number {
-    fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
-        self.0.to_css(dest)
+    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)?;
+        if self.was_calc {
+            dest.write_str(")")?;
+        }
+        Ok(())
     }
 }
 
 /// <number-percentage>
 /// Accepts only non-negative numbers.
 #[derive(Debug, Clone, PartialEq)]
 #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
 #[allow(missing_docs)]
@@ -601,70 +735,90 @@ impl ToCss for NumberOrPercentage {
             NumberOrPercentage::Number(number) => number.to_css(dest),
         }
     }
 }
 
 #[derive(Clone, Copy, Debug, PartialEq, PartialOrd)]
 #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
 #[allow(missing_docs)]
-pub struct Opacity(pub CSSFloat);
+pub struct Opacity(Number);
 
 no_viewport_percentage!(Opacity);
 
 impl Parse for Opacity {
     fn parse(_context: &ParserContext, input: &mut Parser) -> Result<Self, ()> {
         parse_number(input).map(Opacity)
     }
 }
 
 impl ToComputedValue for Opacity {
     type ComputedValue = CSSFloat;
 
     #[inline]
-    fn to_computed_value(&self, _: &Context) -> CSSFloat {
-        if self.0 < 0.0 {
-            0.0
-        } else if self.0 > 1.0 {
-            1.0
-        } else {
-            self.0
-        }
+    fn to_computed_value(&self, context: &Context) -> CSSFloat {
+        self.0.to_computed_value(context).min(1.0).max(0.0)
     }
 
     #[inline]
     fn from_computed_value(computed: &CSSFloat) -> Self {
-        Opacity(*computed)
+        Opacity(Number::from_computed_value(computed))
     }
 }
 
 impl ToCss for Opacity {
     fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
         self.0.to_css(dest)
     }
 }
 
 #[derive(Clone, Copy, Debug, PartialEq, PartialOrd)]
 #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
 #[allow(missing_docs)]
-pub struct Integer(pub CSSInteger);
+pub struct Integer {
+    value: CSSInteger,
+    was_calc: bool,
+}
+
+impl Integer {
+    /// Trivially constructs a new `Integer` value.
+    pub fn new(val: CSSInteger) -> Self {
+        Integer {
+            value: val,
+            was_calc: false,
+        }
+    }
+
+    /// Returns the integer value associated with this value.
+    pub fn value(&self) -> CSSInteger {
+        self.value
+    }
+
+    /// Trivially constructs a new integer value from a `calc()` expression.
+    pub fn from_calc(val: CSSInteger) -> Self {
+        Integer {
+            value: val,
+            was_calc: true,
+        }
+    }
+}
 
 no_viewport_percentage!(Integer);
 
 impl Parse for Integer {
     fn parse(_context: &ParserContext, input: &mut Parser) -> Result<Self, ()> {
-        parse_integer(input).map(Integer)
+        parse_integer(input)
     }
 }
 
 impl Integer {
     fn parse_with_minimum(input: &mut Parser, min: i32) -> Result<Integer, ()> {
         match parse_integer(input) {
-            Ok(value) if value < min => Err(()),
-            value => value.map(Integer),
+            Ok(value) if value.value() >= min => Ok(value),
+            _ => Err(()),
         }
     }
 
     #[allow(missing_docs)]
     pub fn parse_non_negative(input: &mut Parser) -> Result<Integer, ()> {
         Integer::parse_with_minimum(input, 0)
     }
 
@@ -673,38 +827,49 @@ impl Integer {
         Integer::parse_with_minimum(input, 1)
     }
 }
 
 impl ToComputedValue for Integer {
     type ComputedValue = i32;
 
     #[inline]
-    fn to_computed_value(&self, _: &Context) -> i32 { self.0 }
+    fn to_computed_value(&self, _: &Context) -> i32 { self.value }
 
     #[inline]
     fn from_computed_value(computed: &i32) -> Self {
-        Integer(*computed)
+        Integer::new(*computed)
     }
 }
 
 impl ToCss for Integer {
-    fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
-        write!(dest, "{}", self.0)
+    fn to_css<W>(&self, dest: &mut W) -> fmt::Result
+        where W: fmt::Write,
+    {
+        if self.was_calc {
+            dest.write_str("calc(")?;
+        }
+        write!(dest, "{}", self.value)?;
+        if self.was_calc {
+            dest.write_str(")")?;
+        }
+        Ok(())
     }
 }
 
 /// <integer> | auto
 pub type IntegerOrAuto = Either<Integer, Auto>;
 
 impl IntegerOrAuto {
     #[allow(missing_docs)]
-    pub fn parse_positive(context: &ParserContext, input: &mut Parser) -> Result<IntegerOrAuto, ()> {
+    pub fn parse_positive(context: &ParserContext,
+                          input: &mut Parser)
+                          -> Result<IntegerOrAuto, ()> {
         match IntegerOrAuto::parse(context, input) {
-            Ok(Either::First(Integer(value))) if value <= 0 => Err(()),
+            Ok(Either::First(integer)) if integer.value() <= 0 => Err(()),
             result => result,
         }
     }
 }
 
 #[allow(missing_docs)]
 pub type UrlOrNone = Either<SpecifiedUrl, None_>;
 
--- a/servo/ports/geckolib/glue.rs
+++ b/servo/ports/geckolib/glue.rs
@@ -1197,17 +1197,17 @@ pub extern "C" fn Servo_DeclarationBlock
         MarginLeft => nocalc.into(),
         PaddingTop => nocalc.into(),
         PaddingRight => nocalc.into(),
         PaddingBottom => nocalc.into(),
         PaddingLeft => nocalc.into(),
         BorderSpacing => Box::new(
             BorderSpacing {
                 horizontal: nocalc.into(),
-                vertical: nocalc.into(),
+                vertical: None,
             }
         ),
     };
     write_locked_arc(declarations, |decls: &mut PropertyDeclarationBlock| {
         decls.push(prop, Importance::Normal);
     })
 }
 
--- a/servo/tests/unit/style/parsing/image.rs
+++ b/servo/tests/unit/style/parsing/image.rs
@@ -50,17 +50,17 @@ fn test_linear_gradient() {
         is_root_element: true,
         device: &device,
         inherited_style: initial_style,
         layout_parent_style: initial_style,
         style: initial_style.clone(),
         font_metrics_provider: None,
     };
     assert_eq!(specified::AngleOrCorner::None.to_computed_value(&specified_context),
-               computed::AngleOrCorner::Angle(Angle(PI)));
+               computed::AngleOrCorner::Angle(Angle::from_radians(PI)));
 }
 
 #[test]
 fn test_radial_gradient() {
     // Parsing with all values
     assert_roundtrip_with_context!(Image::parse, "radial-gradient(circle closest-side at 20px 30px, red, green)");
     assert_roundtrip_with_context!(Image::parse, "radial-gradient(ellipse closest-side at 20px 30px, red, green)");
     assert_roundtrip_with_context!(Image::parse, "radial-gradient(closest-side circle at 20px 30px, red, green)",
--- a/servo/tests/unit/style/properties/serialization.rs
+++ b/servo/tests/unit/style/properties/serialization.rs
@@ -568,25 +568,24 @@ mod shorthand_serialization {
         properties.push(PropertyDeclaration::ColumnCount(count));
 
         let serialization = shorthand_properties_to_string(properties);
         assert_eq!(serialization, "columns: auto auto;");
     }
 
     #[test]
     fn flex_should_serialize_all_available_properties() {
-        use style::values::specified::Number as NumberContainer;
-        use style::values::specified::Percentage as PercentageContainer;
+        use style::values::specified::{Number, Percentage};
 
         let mut properties = Vec::new();
 
-        let grow = NumberContainer(2f32);
-        let shrink = NumberContainer(3f32);
+        let grow = Number::new(2f32);
+        let shrink = Number::new(3f32);
         let basis =
-            LengthOrPercentageOrAutoOrContent::Percentage(PercentageContainer(0.5f32));
+            LengthOrPercentageOrAutoOrContent::Percentage(Percentage(0.5f32));
 
         properties.push(PropertyDeclaration::FlexGrow(grow));
         properties.push(PropertyDeclaration::FlexShrink(shrink));
         properties.push(PropertyDeclaration::FlexBasis(basis));
 
         let serialization = shorthand_properties_to_string(properties);
         assert_eq!(serialization, "flex: 2 3 50%;");
     }
@@ -1169,23 +1168,24 @@ mod shorthand_serialization {
 
             assert_eq!(shadow.to_css_string(), shadow_css);
         }
     }
 
     mod counter_increment {
         pub use super::*;
         pub use style::properties::longhands::counter_increment::SpecifiedValue as CounterIncrement;
+        use style::values::specified::Integer;
 
         #[test]
         fn counter_increment_with_properties_should_serialize_correctly() {
             let mut properties = Vec::new();
 
-            properties.push(("counter1".to_owned(), 1));
-            properties.push(("counter2".to_owned(), -4));
+            properties.push(("counter1".to_owned(), Integer::new(1)));
+            properties.push(("counter2".to_owned(), Integer::new(-4)));
 
             let counter_increment = CounterIncrement(properties);
             let counter_increment_css = "counter1 1 counter2 -4";
 
             assert_eq!(counter_increment.to_css_string(), counter_increment_css);
         }
 
         #[test]