servo: Merge #16973 - Derive ToComputedValue (from servo:derive-all-the-things); r=emilio
authorAnthony Ramine <n.oxyde@gmail.com>
Sun, 21 May 2017 08:48:10 -0500
changeset 359820 7010b10abac2f3919f3e09356d34782899268f2f
parent 359819 20990af6ec5f36b1112f8923a38cf643780e70cb
child 359821 45eb05278a0fc5d7eb94eea2846813763366db5c
push id31859
push userihsiao@mozilla.com
push dateMon, 22 May 2017 03:28:26 +0000
treeherdermozilla-central@367944041b55 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersemilio
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 #16973 - Derive ToComputedValue (from servo:derive-all-the-things); r=emilio Source-Repo: https://github.com/servo/servo Source-Revision: a25b1e5fe3ee1c88a029835e5e27c73fdbdafa2c
servo/components/geometry/lib.rs
servo/components/style/cascade_info.rs
servo/components/style/custom_properties.rs
servo/components/style/properties/helpers.mako.rs
servo/components/style/properties/longhand/effects.mako.rs
servo/components/style/properties/longhand/font.mako.rs
servo/components/style/properties/properties.mako.rs
servo/components/style/values/computed/mod.rs
servo/components/style/values/generics/basic_shape.rs
servo/components/style/values/generics/image.rs
servo/components/style/values/generics/mod.rs
servo/components/style/values/generics/position.rs
servo/components/style/values/mod.rs
servo/components/style/values/specified/calc.rs
servo/components/style/values/specified/grid.rs
servo/components/style/values/specified/length.rs
servo/components/style/values/specified/position.rs
servo/components/style_derive/lib.rs
servo/components/style_derive/to_computed_value.rs
servo/tests/unit/style/parsing/value.rs
servo/tests/unit/style/properties/viewport.rs
--- a/servo/components/geometry/lib.rs
+++ b/servo/components/geometry/lib.rs
@@ -5,17 +5,16 @@
 extern crate app_units;
 extern crate euclid;
 #[macro_use] extern crate heapsize;
 
 use app_units::{Au, MAX_AU, MIN_AU};
 use euclid::point::Point2D;
 use euclid::rect::Rect;
 use euclid::size::Size2D;
-use std::i32;
 
 // Units for use with euclid::length and euclid::scale_factor.
 
 /// A normalized "pixel" at the default resolution for the display.
 ///
 /// Like the CSS "px" unit, the exact physical size of this unit may vary between devices, but it
 /// should approximate a device-independent reference length.  This unit corresponds to Android's
 /// "density-independent pixel" (dip), Mac OS X's "point", and Windows "device-independent pixel."
--- a/servo/components/style/cascade_info.rs
+++ b/servo/components/style/cascade_info.rs
@@ -3,17 +3,17 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 //! A structure to collect information about the cascade.
 
 #![deny(missing_docs)]
 
 use dom::TNode;
 use properties::{DeclaredValue, PropertyDeclaration};
-use values::HasViewportPercentage;
+use style_traits::HasViewportPercentage;
 
 /// A structure to collect information about the cascade.
 ///
 /// This is useful to gather information about what an element is affected by,
 /// and can be used in the future to track optimisations like when a
 /// non-inherited property is explicitly inherited, in order to cut-off the
 /// traversal.
 pub struct CascadeInfo {
--- a/servo/components/style/custom_properties.rs
+++ b/servo/components/style/custom_properties.rs
@@ -9,17 +9,17 @@
 use Atom;
 use cssparser::{Delimiter, Parser, SourcePosition, Token, TokenSerializationType};
 use parser::ParserContext;
 use properties::{CSSWideKeyword, DeclaredValue};
 use std::ascii::AsciiExt;
 use std::borrow::Cow;
 use std::collections::{HashMap, HashSet};
 use std::fmt;
-use style_traits::ToCss;
+use style_traits::{HasViewportPercentage, ToCss};
 use stylearc::Arc;
 
 /// A custom property name is just an `Atom`.
 ///
 /// Note that this does not include the `--` prefix
 pub type Name = Atom;
 
 /// Parse a custom property name.
@@ -44,17 +44,17 @@ pub struct SpecifiedValue {
 
     first_token_type: TokenSerializationType,
     last_token_type: TokenSerializationType,
 
     /// Custom property names in var() functions.
     references: HashSet<Name>,
 }
 
-impl ::values::HasViewportPercentage for SpecifiedValue {
+impl HasViewportPercentage for SpecifiedValue {
     fn has_viewport_percentage(&self) -> bool {
         panic!("has_viewport_percentage called before resolving!");
     }
 }
 
 /// This struct is a cheap borrowed version of a `SpecifiedValue`.
 pub struct BorrowedSpecifiedValue<'a> {
     css: &'a str,
--- a/servo/components/style/properties/helpers.mako.rs
+++ b/servo/components/style/properties/helpers.mako.rs
@@ -77,17 +77,17 @@
 </%doc>
 <%def name="vector_longhand(name, gecko_only=False, allow_empty=False,
             delegate_animate=False, space_separated_allowed=False, **kwargs)">
     <%call expr="longhand(name, vector=True, **kwargs)">
         % if not gecko_only:
             use smallvec::SmallVec;
             use std::fmt;
             #[allow(unused_imports)]
-            use values::HasViewportPercentage;
+            use style_traits::HasViewportPercentage;
             use style_traits::ToCss;
 
             pub mod single_value {
                 #[allow(unused_imports)]
                 use cssparser::Parser;
                 #[allow(unused_imports)]
                 use parser::{Parse, ParserContext};
                 #[allow(unused_imports)]
--- a/servo/components/style/properties/longhand/effects.mako.rs
+++ b/servo/components/style/properties/longhand/effects.mako.rs
@@ -82,18 +82,18 @@
                           spec="https://drafts.fxtf.org/css-masking/#clip-property")}
 
 // FIXME: This prop should be animatable
 <%helpers:longhand name="filter" animation_value_type="none" extra_prefixes="webkit"
                    flags="CREATES_STACKING_CONTEXT FIXPOS_CB"
                    spec="https://drafts.fxtf.org/filters/#propdef-filter">
     //pub use self::computed_value::T as SpecifiedValue;
     use std::fmt;
-    use style_traits::ToCss;
-    use values::{CSSFloat, HasViewportPercentage};
+    use style_traits::{HasViewportPercentage, ToCss};
+    use values::CSSFloat;
     use values::specified::{Angle, Length};
     #[cfg(feature = "gecko")]
     use values::specified::Shadow;
     #[cfg(feature = "gecko")]
     use values::specified::url::SpecifiedUrl;
 
     #[derive(Clone, Debug, HasViewportPercentage, PartialEq)]
     #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
--- a/servo/components/style/properties/longhand/font.mako.rs
+++ b/servo/components/style/properties/longhand/font.mako.rs
@@ -545,18 +545,18 @@
 </%helpers:longhand>
 
 <%helpers:longhand name="font-size" need_clone="True" animation_value_type="ComputedValue"
                    allow_quirks="True" spec="https://drafts.csswg.org/css-fonts/#propdef-font-size">
     use app_units::Au;
     use properties::longhands::system_font::SystemFont;
     use properties::style_structs::Font;
     use std::fmt;
-    use style_traits::ToCss;
-    use values::{FONT_MEDIUM_PX, HasViewportPercentage};
+    use style_traits::{HasViewportPercentage, ToCss};
+    use values::FONT_MEDIUM_PX;
     use values::specified::{AllowQuirks, FontRelativeLength, LengthOrPercentage};
     use values::specified::{NoCalcLength, Percentage};
     use values::specified::length::FontBaseSize;
 
     impl ToCss for SpecifiedValue {
         fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
             match *self {
                 SpecifiedValue::Length(ref lop) => lop.to_css(dest),
--- a/servo/components/style/properties/properties.mako.rs
+++ b/servo/components/style/properties/properties.mako.rs
@@ -29,20 +29,20 @@ use font_metrics::FontMetricsProvider;
 #[cfg(feature = "gecko")] use gecko_bindings::structs::{self, nsCSSPropertyID};
 #[cfg(feature = "servo")] use logical_geometry::{LogicalMargin, PhysicalSide};
 use logical_geometry::WritingMode;
 use media_queries::Device;
 use parser::{PARSING_MODE_DEFAULT, Parse, ParserContext};
 use properties::animated_properties::TransitionProperty;
 #[cfg(feature = "servo")] use servo_config::prefs::PREFS;
 use shared_lock::StylesheetGuards;
-use style_traits::ToCss;
+use style_traits::{HasViewportPercentage, ToCss};
 use stylesheets::{CssRuleType, Origin, UrlExtraData};
 #[cfg(feature = "servo")] use values::Either;
-use values::{HasViewportPercentage, computed};
+use values::computed;
 use cascade_info::CascadeInfo;
 use rule_tree::StrongRuleNode;
 use style_adjuster::StyleAdjuster;
 #[cfg(feature = "servo")] use values::specified::BorderStyle;
 
 pub use self::declaration_block::*;
 
 #[cfg(feature = "gecko")]
--- a/servo/components/style/values/computed/mod.rs
+++ b/servo/components/style/values/computed/mod.rs
@@ -1,14 +1,15 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 //! Computed values.
 
+use Atom;
 use context::QuirksMode;
 use euclid::size::Size2D;
 use font_metrics::FontMetricsProvider;
 use media_queries::Device;
 #[cfg(feature = "gecko")]
 use properties;
 use properties::{ComputedValues, StyleBuilder};
 use std::f32;
@@ -149,16 +150,89 @@ pub trait ToComputedValue {
     #[inline]
     /// Convert a computed value to specified value form.
     ///
     /// This will be used for recascading during animation.
     /// Such from_computed_valued values should recompute to the same value.
     fn from_computed_value(computed: &Self::ComputedValue) -> Self;
 }
 
+impl<A, B> ToComputedValue for (A, B)
+    where A: ToComputedValue, B: ToComputedValue,
+{
+    type ComputedValue = (
+        <A as ToComputedValue>::ComputedValue,
+        <B as ToComputedValue>::ComputedValue,
+    );
+
+    #[inline]
+    fn to_computed_value(&self, context: &Context) -> Self::ComputedValue {
+        (self.0.to_computed_value(context), self.1.to_computed_value(context))
+    }
+
+    #[inline]
+    fn from_computed_value(computed: &Self::ComputedValue) -> Self {
+        (A::from_computed_value(&computed.0), B::from_computed_value(&computed.1))
+    }
+}
+
+impl<T> ToComputedValue for Option<T>
+    where T: ToComputedValue
+{
+    type ComputedValue = Option<<T as ToComputedValue>::ComputedValue>;
+
+    #[inline]
+    fn to_computed_value(&self, context: &Context) -> Self::ComputedValue {
+        self.as_ref().map(|item| item.to_computed_value(context))
+    }
+
+    #[inline]
+    fn from_computed_value(computed: &Self::ComputedValue) -> Self {
+        computed.as_ref().map(T::from_computed_value)
+    }
+}
+
+impl<T> ToComputedValue for Size2D<T>
+    where T: ToComputedValue
+{
+    type ComputedValue = Size2D<<T as ToComputedValue>::ComputedValue>;
+
+    #[inline]
+    fn to_computed_value(&self, context: &Context) -> Self::ComputedValue {
+        Size2D::new(
+            self.width.to_computed_value(context),
+            self.height.to_computed_value(context),
+        )
+    }
+
+    #[inline]
+    fn from_computed_value(computed: &Self::ComputedValue) -> Self {
+        Size2D::new(
+            T::from_computed_value(&computed.width),
+            T::from_computed_value(&computed.height),
+        )
+    }
+}
+
+impl<T> ToComputedValue for Vec<T>
+    where T: ToComputedValue
+{
+    type ComputedValue = Vec<<T as ToComputedValue>::ComputedValue>;
+
+    #[inline]
+    fn to_computed_value(&self, context: &Context) -> Self::ComputedValue {
+        self.iter().map(|item| item.to_computed_value(context)).collect()
+    }
+
+    #[inline]
+    fn from_computed_value(computed: &Self::ComputedValue) -> Self {
+        computed.iter().map(T::from_computed_value).collect()
+    }
+}
+
 /// A marker trait to represent that the specified value is also the computed
 /// value.
 pub trait ComputedValueAsSpecified {}
 
 impl<T> ToComputedValue for T
     where T: ComputedValueAsSpecified + Clone,
 {
     type ComputedValue = T;
@@ -169,16 +243,19 @@ impl<T> ToComputedValue for T
     }
 
     #[inline]
     fn from_computed_value(computed: &T) -> Self {
         computed.clone()
     }
 }
 
+impl ComputedValueAsSpecified for Atom {}
+impl ComputedValueAsSpecified for bool {}
+
 /// A computed `<angle>` value.
 #[derive(Clone, Copy, Debug, HasViewportPercentage, PartialEq, PartialOrd)]
 #[cfg_attr(feature = "servo", derive(HeapSizeOf, Deserialize, Serialize))]
 pub enum Angle {
     /// An angle with degree unit
     Degree(CSSFloat),
     /// An angle with gradian unit
     Gradian(CSSFloat),
--- a/servo/components/style/values/generics/basic_shape.rs
+++ b/servo/components/style/values/generics/basic_shape.rs
@@ -6,26 +6,25 @@
 //! types that are generic over their `ToCss` implementations.
 
 use cssparser::Parser;
 use euclid::size::Size2D;
 use parser::{Parse, ParserContext};
 use properties::shorthands::serialize_four_sides;
 use std::ascii::AsciiExt;
 use std::fmt;
-use style_traits::ToCss;
-use values::HasViewportPercentage;
-use values::computed::{ComputedValueAsSpecified, Context, ToComputedValue};
+use style_traits::{HasViewportPercentage, ToCss};
+use values::computed::ComputedValueAsSpecified;
 use values::generics::BorderRadiusSize;
 use values::specified::url::SpecifiedUrl;
 
 /// A generic type used for `border-radius`, `outline-radius` and `inset()` values.
 ///
 /// https://drafts.csswg.org/css-backgrounds-3/#border-radius
-#[derive(Clone, PartialEq, Debug)]
+#[derive(Clone, Debug, PartialEq, ToComputedValue)]
 #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
 pub struct BorderRadius<L> {
     /// The top left radius.
     pub top_left: BorderRadiusSize<L>,
     /// The top right radius.
     pub top_right: BorderRadiusSize<L>,
     /// The bottom right radius.
     pub bottom_right: BorderRadiusSize<L>,
@@ -55,42 +54,18 @@ pub fn serialize_radius_values<L, W>(des
 impl<L: ToCss + PartialEq> ToCss for BorderRadius<L> {
     #[inline]
     fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
         serialize_radius_values(dest, &self.top_left.0, &self.top_right.0,
                                 &self.bottom_right.0, &self.bottom_left.0)
     }
 }
 
-impl<L: ToComputedValue> ToComputedValue for BorderRadius<L> {
-    type ComputedValue = BorderRadius<L::ComputedValue>;
-
-    #[inline]
-    fn to_computed_value(&self, cx: &Context) -> Self::ComputedValue {
-        BorderRadius {
-            top_left: self.top_left.to_computed_value(cx),
-            top_right: self.top_right.to_computed_value(cx),
-            bottom_right: self.bottom_right.to_computed_value(cx),
-            bottom_left: self.bottom_left.to_computed_value(cx),
-        }
-    }
-
-    #[inline]
-    fn from_computed_value(computed: &Self::ComputedValue) -> Self {
-        BorderRadius {
-            top_left: ToComputedValue::from_computed_value(&computed.top_left),
-            top_right: ToComputedValue::from_computed_value(&computed.top_right),
-            bottom_right: ToComputedValue::from_computed_value(&computed.bottom_right),
-            bottom_left: ToComputedValue::from_computed_value(&computed.bottom_left),
-        }
-    }
-}
-
 /// https://drafts.csswg.org/css-shapes/#typedef-shape-radius
-#[derive(Clone, PartialEq, Debug)]
+#[derive(Clone, Debug, PartialEq, ToComputedValue)]
 #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
 #[allow(missing_docs)]
 pub enum ShapeRadius<L> {
     Length(L),
     ClosestSide,
     FarthestSide,
 }
 
@@ -105,55 +80,33 @@ impl<L: ToCss> ToCss for ShapeRadius<L> 
         match *self {
             ShapeRadius::Length(ref lop) => lop.to_css(dest),
             ShapeRadius::ClosestSide => dest.write_str("closest-side"),
             ShapeRadius::FarthestSide => dest.write_str("farthest-side"),
         }
     }
 }
 
-impl<L: ToComputedValue> ToComputedValue for ShapeRadius<L> {
-    type ComputedValue = ShapeRadius<L::ComputedValue>;
-
-    #[inline]
-    fn to_computed_value(&self, cx: &Context) -> Self::ComputedValue {
-        match *self {
-            ShapeRadius::Length(ref lop) => ShapeRadius::Length(lop.to_computed_value(cx)),
-            ShapeRadius::ClosestSide => ShapeRadius::ClosestSide,
-            ShapeRadius::FarthestSide => ShapeRadius::FarthestSide,
-        }
-    }
-
-    #[inline]
-    fn from_computed_value(computed: &Self::ComputedValue) -> Self {
-        match *computed {
-            ShapeRadius::Length(ref lop) => ShapeRadius::Length(ToComputedValue::from_computed_value(lop)),
-            ShapeRadius::ClosestSide => ShapeRadius::ClosestSide,
-            ShapeRadius::FarthestSide => ShapeRadius::FarthestSide,
-        }
-    }
-}
-
 // https://drafts.csswg.org/css-shapes/#typedef-fill-rule
 // NOTE: Basic shapes spec says that these are the only two values, however
 // https://www.w3.org/TR/SVG/painting.html#FillRuleProperty
 // says that it can also be `inherit`
 define_css_keyword_enum!(FillRule:
     "nonzero" => NonZero,
     "evenodd" => EvenOdd
 );
 
 impl ComputedValueAsSpecified for FillRule {}
 
 impl Default for FillRule {
     #[inline]
     fn default() -> Self { FillRule::NonZero }
 }
 
-#[derive(Clone, PartialEq, Debug)]
+#[derive(Clone, Debug, PartialEq, ToComputedValue)]
 #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
 /// A generic type for representing the `polygon()` function
 ///
 /// https://drafts.csswg.org/css-shapes/#funcdef-polygon
 pub struct Polygon<L> {
     /// The filling rule for a polygon.
     pub fill: FillRule,
     /// A collection of (x, y) coordinates to draw the polygon.
@@ -207,42 +160,17 @@ impl<L: ToCss> ToCss for Polygon<L> {
             dest.write_str(" ")?;
             coord.1.to_css(dest)?;
         }
 
         dest.write_str(")")
     }
 }
 
-impl<L: ToComputedValue> ToComputedValue for Polygon<L> {
-    type ComputedValue = Polygon<L::ComputedValue>;
-
-    #[inline]
-    fn to_computed_value(&self, cx: &Context) -> Self::ComputedValue {
-        Polygon {
-            fill: self.fill.to_computed_value(cx),
-            coordinates: self.coordinates.iter().map(|c| {
-                (c.0.to_computed_value(cx), c.1.to_computed_value(cx))
-            }).collect(),
-        }
-    }
-
-    #[inline]
-    fn from_computed_value(computed: &Self::ComputedValue) -> Self {
-        Polygon {
-            fill: ToComputedValue::from_computed_value(&computed.fill),
-            coordinates: computed.coordinates.iter().map(|c| {
-                (ToComputedValue::from_computed_value(&c.0),
-                 ToComputedValue::from_computed_value(&c.1))
-            }).collect(),
-        }
-    }
-}
-
-#[derive(Clone, PartialEq, Debug)]
+#[derive(Clone, Debug, PartialEq, ToComputedValue)]
 #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
 /// https://drafts.csswg.org/css-shapes/#funcdef-inset
 #[allow(missing_docs)]
 pub struct InsetRect<L> {
     pub top: L,
     pub right: L,
     pub bottom: L,
     pub left: L,
@@ -264,47 +192,21 @@ impl<L: ToCss + PartialEq> ToCss for Ins
             dest.write_str(" round ")?;
             radius.to_css(dest)?;
         }
 
         dest.write_str(")")
     }
 }
 
-impl<L: ToComputedValue> ToComputedValue for InsetRect<L> {
-    type ComputedValue = InsetRect<L::ComputedValue>;
-
-    #[inline]
-    fn to_computed_value(&self, cx: &Context) -> Self::ComputedValue {
-        InsetRect {
-            top: self.top.to_computed_value(cx),
-            right: self.right.to_computed_value(cx),
-            bottom: self.bottom.to_computed_value(cx),
-            left: self.left.to_computed_value(cx),
-            round: self.round.as_ref().map(|r| r.to_computed_value(cx)),
-        }
-    }
-
-    #[inline]
-    fn from_computed_value(computed: &Self::ComputedValue) -> Self {
-        InsetRect {
-            top: ToComputedValue::from_computed_value(&computed.top),
-            right: ToComputedValue::from_computed_value(&computed.right),
-            bottom: ToComputedValue::from_computed_value(&computed.bottom),
-            left: ToComputedValue::from_computed_value(&computed.left),
-            round: computed.round.as_ref().map(|r| ToComputedValue::from_computed_value(r)),
-        }
-    }
-}
-
 /// A shape source, for some reference box
 ///
 /// `clip-path` uses ShapeSource<BasicShape, GeometryBox>,
 /// `shape-outside` uses ShapeSource<BasicShape, ShapeBox>
-#[derive(Clone, PartialEq, Debug)]
+#[derive(Clone, Debug, PartialEq, ToComputedValue)]
 #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
 #[allow(missing_docs)]
 pub enum ShapeSource<B, T> {
     Url(SpecifiedUrl),
     Shape(B, Option<T>),
     Box(T),
     None,
 }
@@ -360,38 +262,8 @@ impl<B: Parse, T: Parse> Parse for Shape
 
         if let Some(shp) = shape {
             return Ok(ShapeSource::Shape(shp, ref_box))
         }
 
         ref_box.map(|v| ShapeSource::Box(v)).ok_or(())
     }
 }
-
-impl<B: ToComputedValue, T: ToComputedValue> ToComputedValue for ShapeSource<B, T> {
-    type ComputedValue = ShapeSource<B::ComputedValue, T::ComputedValue>;
-
-    #[inline]
-    fn to_computed_value(&self, cx: &Context) -> Self::ComputedValue {
-        match *self {
-            ShapeSource::Url(ref url) => ShapeSource::Url(url.to_computed_value(cx)),
-            ShapeSource::Shape(ref shape, ref ref_box) => {
-                ShapeSource::Shape(shape.to_computed_value(cx),
-                                   ref_box.as_ref().map(|ref val| val.to_computed_value(cx)))
-            },
-            ShapeSource::Box(ref ref_box) => ShapeSource::Box(ref_box.to_computed_value(cx)),
-            ShapeSource::None => ShapeSource::None,
-        }
-    }
-
-    #[inline]
-    fn from_computed_value(computed: &Self::ComputedValue) -> Self {
-        match *computed {
-            ShapeSource::Url(ref url) => ShapeSource::Url(SpecifiedUrl::from_computed_value(url)),
-            ShapeSource::Shape(ref shape, ref ref_box) => {
-                ShapeSource::Shape(ToComputedValue::from_computed_value(shape),
-                                    ref_box.as_ref().map(|val| ToComputedValue::from_computed_value(val)))
-            },
-            ShapeSource::Box(ref ref_box) => ShapeSource::Box(ToComputedValue::from_computed_value(ref_box)),
-            ShapeSource::None => ShapeSource::None,
-        }
-    }
-}
--- a/servo/components/style/values/generics/image.rs
+++ b/servo/components/style/values/generics/image.rs
@@ -4,94 +4,93 @@
 
 //! Generic types for the handling of [images].
 //!
 //! [images]: https://drafts.csswg.org/css-images/#image-values
 
 use Atom;
 use cssparser::serialize_identifier;
 use std::fmt;
-use style_traits::ToCss;
-use values::HasViewportPercentage;
-use values::computed::{Context, ToComputedValue};
+use style_traits::{HasViewportPercentage, ToCss};
+use values::computed::ComputedValueAsSpecified;
 use values::specified::url::SpecifiedUrl;
 
 /// An [image].
 ///
 /// [image]: https://drafts.csswg.org/css-images/#image-values
-#[derive(Clone, PartialEq)]
+#[derive(Clone, PartialEq, ToComputedValue)]
 #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
 pub enum Image<Gradient, ImageRect> {
     /// A `<url()>` image.
     Url(SpecifiedUrl),
     /// A `<gradient>` image.
     Gradient(Gradient),
     /// A `-moz-image-rect` image
     Rect(ImageRect),
     /// A `-moz-element(# <element-id>)`
     Element(Atom),
 }
 
 /// A CSS gradient.
 /// https://drafts.csswg.org/css-images/#gradients
-#[derive(Clone, Debug, HasViewportPercentage, PartialEq)]
+#[derive(Clone, Debug, HasViewportPercentage, PartialEq, ToComputedValue)]
 #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
 pub struct Gradient<LineDirection, Length, LengthOrPercentage, Position, Color> {
     /// Gradients can be linear or radial.
     pub kind: GradientKind<LineDirection, Length, LengthOrPercentage, Position>,
     /// The color stops and interpolation hints.
     pub items: Vec<GradientItem<Color, LengthOrPercentage>>,
     /// True if this is a repeating gradient.
     pub repeating: bool,
     /// Compatibility mode.
     pub compat_mode: CompatMode,
 }
 
-#[derive(Clone, Copy, Debug, HasViewportPercentage, PartialEq)]
+#[derive(Clone, Copy, Debug, HasViewportPercentage, PartialEq, ToComputedValue)]
 #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
 /// Whether we used the modern notation or the compatibility `-webkit` prefix.
 pub enum CompatMode {
     /// Modern syntax.
     Modern,
     /// `-webkit` prefix.
     WebKit,
 }
 
 /// A gradient kind.
-#[derive(Clone, Copy, Debug, HasViewportPercentage, PartialEq)]
+#[derive(Clone, Copy, Debug, HasViewportPercentage, PartialEq, ToComputedValue)]
 #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
 pub enum GradientKind<LineDirection, Length, LengthOrPercentage, Position> {
     /// A linear gradient.
     Linear(LineDirection),
     /// A radial gradient.
     Radial(EndingShape<Length, LengthOrPercentage>, Position),
 }
 
 /// A radial gradient's ending shape.
-#[derive(Clone, Copy, Debug, HasViewportPercentage, PartialEq)]
+#[derive(Clone, Copy, Debug, HasViewportPercentage, PartialEq, ToComputedValue)]
 #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
 pub enum EndingShape<Length, LengthOrPercentage> {
     /// A circular gradient.
     Circle(Circle<Length>),
     /// An elliptic gradient.
     Ellipse(Ellipse<LengthOrPercentage>),
 }
 
 /// A circle shape.
-#[derive(Clone, Copy, Debug, HasViewportPercentage, PartialEq)]
+#[derive(Clone, Copy, Debug, HasViewportPercentage, PartialEq, ToComputedValue)]
 #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
 pub enum Circle<Length> {
     /// A circle radius.
     Radius(Length),
     /// A circle extent.
     Extent(ShapeExtent),
 }
 
 /// An ellipse shape.
-#[derive(Clone, Copy, Debug, HasViewportPercentage, PartialEq)]
+#[derive(Clone, Copy, Debug, HasViewportPercentage, PartialEq, ToComputedValue)]
 #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
 pub enum Ellipse<LengthOrPercentage> {
     /// An ellipse pair of radii.
     Radii(LengthOrPercentage, LengthOrPercentage),
     /// An ellipse extent.
     Extent(ShapeExtent),
 }
 
@@ -100,43 +99,44 @@ define_css_keyword_enum!(ShapeExtent:
     "closest-side" => ClosestSide,
     "farthest-side" => FarthestSide,
     "closest-corner" => ClosestCorner,
     "farthest-corner" => FarthestCorner,
     "contain" => Contain,
     "cover" => Cover
 );
 no_viewport_percentage!(ShapeExtent);
+impl ComputedValueAsSpecified for ShapeExtent {}
 
 /// A gradient item.
 /// https://drafts.csswg.org/css-images-4/#color-stop-syntax
-#[derive(Clone, Copy, Debug, HasViewportPercentage, PartialEq)]
+#[derive(Clone, Copy, Debug, HasViewportPercentage, PartialEq, ToComputedValue)]
 #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
 pub enum GradientItem<Color, LengthOrPercentage> {
     /// A color stop.
     ColorStop(ColorStop<Color, LengthOrPercentage>),
     /// An interpolation hint.
     InterpolationHint(LengthOrPercentage),
 }
 
 /// A color stop.
 /// https://drafts.csswg.org/css-images/#typedef-color-stop-list
-#[derive(Clone, Copy, HasViewportPercentage, PartialEq)]
+#[derive(Clone, Copy, HasViewportPercentage, PartialEq, ToComputedValue)]
 #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
 pub struct ColorStop<Color, LengthOrPercentage> {
     /// The color of this stop.
     pub color: Color,
     /// The position of this stop.
     pub position: Option<LengthOrPercentage>,
 }
 
 /// Values for `moz-image-rect`.
 ///
 /// `-moz-image-rect(<uri>, top, right, bottom, left);`
-#[derive(Clone, PartialEq, Debug)]
+#[derive(Clone, Debug, PartialEq, ToComputedValue)]
 #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
 #[allow(missing_docs)]
 pub struct ImageRect<NumberOrPercentage> {
     pub url: SpecifiedUrl,
     pub top: NumberOrPercentage,
     pub bottom: NumberOrPercentage,
     pub right: NumberOrPercentage,
     pub left: NumberOrPercentage,
@@ -182,57 +182,16 @@ impl<G, R> HasViewportPercentage for Ima
     fn has_viewport_percentage(&self) -> bool {
         match *self {
             Image::Gradient(ref gradient) => gradient.has_viewport_percentage(),
             _ => false,
         }
     }
 }
 
-impl<G, R> ToComputedValue for Image<G, R>
-    where G: ToComputedValue, R: ToComputedValue,
-{
-    type ComputedValue = Image<<G as ToComputedValue>::ComputedValue,
-                               <R as ToComputedValue>::ComputedValue>;
-
-    fn to_computed_value(&self, context: &Context) -> Self::ComputedValue {
-        match *self {
-            Image::Url(ref url) => {
-                Image::Url(url.clone())
-            },
-            Image::Gradient(ref gradient) => {
-                Image::Gradient(gradient.to_computed_value(context))
-            },
-            Image::Rect(ref rect) => {
-                Image::Rect(rect.to_computed_value(context))
-            },
-            Image::Element(ref selector) => {
-                Image::Element(selector.clone())
-            }
-        }
-    }
-
-    fn from_computed_value(computed: &Self::ComputedValue) -> Self {
-        match *computed {
-            Image::Url(ref url) => {
-                Image::Url(url.clone())
-            },
-            Image::Gradient(ref gradient) => {
-                Image::Gradient(ToComputedValue::from_computed_value(gradient))
-            },
-            Image::Rect(ref rect) => {
-                Image::Rect(ToComputedValue::from_computed_value(rect))
-            },
-            Image::Element(ref selector) => {
-                Image::Element(selector.clone())
-            },
-        }
-    }
-}
-
 impl<D, L, LoP, P, C> ToCss for Gradient<D, L, LoP, P, C>
     where D: LineDirection, L: ToCss, LoP: ToCss, P: ToCss, C: ToCss,
 {
     fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
         if self.compat_mode == CompatMode::WebKit {
             dest.write_str("-webkit-")?;
         }
         if self.repeating {
@@ -277,94 +236,25 @@ impl<D, L, LoP, P, C> ToCss for Gradient
             }
             skip_comma = false;
             item.to_css(dest)?;
         }
         dest.write_str(")")
     }
 }
 
-impl<D, L, LoP, P, C> ToComputedValue for Gradient<D, L, LoP, P, C>
-    where D: ToComputedValue,
-          L: ToComputedValue,
-          LoP: ToComputedValue,
-          P: ToComputedValue,
-          C: ToComputedValue,
-{
-    type ComputedValue = Gradient<<D as ToComputedValue>::ComputedValue,
-                                  <L as ToComputedValue>::ComputedValue,
-                                  <LoP as ToComputedValue>::ComputedValue,
-                                  <P as ToComputedValue>::ComputedValue,
-                                  <C as ToComputedValue>::ComputedValue>;
-
-    fn to_computed_value(&self, context: &Context) -> Self::ComputedValue {
-        Gradient {
-            kind: self.kind.to_computed_value(context),
-            items: self.items.iter().map(|s| s.to_computed_value(context)).collect(),
-            repeating: self.repeating,
-            compat_mode: self.compat_mode,
-        }
-    }
-
-    fn from_computed_value(computed: &Self::ComputedValue) -> Self {
-        Gradient {
-            kind: ToComputedValue::from_computed_value(&computed.kind),
-            items: computed.items.iter().map(ToComputedValue::from_computed_value).collect(),
-            repeating: computed.repeating,
-            compat_mode: computed.compat_mode,
-        }
-    }
-}
-
 impl<D, L, LoP, P> GradientKind<D, L, LoP, P> {
     fn label(&self) -> &str {
         match *self {
             GradientKind::Linear(..) => "linear",
             GradientKind::Radial(..) => "radial",
         }
     }
 }
 
-impl<D, L, LoP, P> ToComputedValue for GradientKind<D, L, LoP, P>
-    where D: ToComputedValue,
-          L: ToComputedValue,
-          LoP: ToComputedValue,
-          P: ToComputedValue,
-{
-    type ComputedValue = GradientKind<<D as ToComputedValue>::ComputedValue,
-                                      <L as ToComputedValue>::ComputedValue,
-                                      <LoP as ToComputedValue>::ComputedValue,
-                                      <P as ToComputedValue>::ComputedValue>;
-
-    fn to_computed_value(&self, context: &Context) -> Self::ComputedValue {
-        match *self {
-            GradientKind::Linear(ref direction) => {
-                GradientKind::Linear(direction.to_computed_value(context))
-            },
-            GradientKind::Radial(ref shape, ref position) => {
-                GradientKind::Radial(shape.to_computed_value(context), position.to_computed_value(context))
-            },
-        }
-    }
-
-    fn from_computed_value(computed: &Self::ComputedValue) -> Self {
-        match *computed {
-            GradientKind::Linear(ref direction) => {
-                GradientKind::Linear(ToComputedValue::from_computed_value(direction))
-            },
-            GradientKind::Radial(ref shape, ref position) => {
-                GradientKind::Radial(
-                    ToComputedValue::from_computed_value(shape),
-                    ToComputedValue::from_computed_value(position),
-                )
-            }
-        }
-    }
-}
-
 /// The direction of a linear gradient.
 pub trait LineDirection {
     /// Whether this direction points towards, and thus can be omitted.
     fn points_downwards(&self) -> bool;
 
     /// Serialises this direction according to the compatibility mode.
     fn to_css<W>(&self, dest: &mut W, compat_mode: CompatMode) -> fmt::Result
         where W: fmt::Write;
@@ -393,103 +283,27 @@ impl<L, LoP> ToCss for EndingShape<L, Lo
                 x.to_css(dest)?;
                 dest.write_str(" ")?;
                 y.to_css(dest)
             },
         }
     }
 }
 
-impl<L, LoP> ToComputedValue for EndingShape<L, LoP>
-    where L: ToComputedValue, LoP: ToComputedValue,
-{
-    type ComputedValue = EndingShape<<L as ToComputedValue>::ComputedValue,
-                                     <LoP as ToComputedValue>::ComputedValue>;
-
-    fn to_computed_value(&self, context: &Context) -> Self::ComputedValue {
-        match *self {
-            EndingShape::Circle(Circle::Radius(ref length)) => {
-                EndingShape::Circle(Circle::Radius(length.to_computed_value(context)))
-            },
-            EndingShape::Circle(Circle::Extent(extent)) => {
-                EndingShape::Circle(Circle::Extent(extent))
-            },
-            EndingShape::Ellipse(Ellipse::Radii(ref x, ref y)) => {
-                EndingShape::Ellipse(Ellipse::Radii(
-                    x.to_computed_value(context),
-                    y.to_computed_value(context),
-                ))
-            },
-            EndingShape::Ellipse(Ellipse::Extent(extent)) => {
-                EndingShape::Ellipse(Ellipse::Extent(extent))
-            },
-        }
-    }
-
-    fn from_computed_value(computed: &Self::ComputedValue) -> Self {
-        match *computed {
-            EndingShape::Circle(Circle::Radius(ref length)) => {
-                EndingShape::Circle(Circle::Radius(ToComputedValue::from_computed_value(length)))
-            },
-            EndingShape::Circle(Circle::Extent(extent)) => {
-                EndingShape::Circle(Circle::Extent(extent))
-            },
-            EndingShape::Ellipse(Ellipse::Radii(ref x, ref y)) => {
-                EndingShape::Ellipse(Ellipse::Radii(
-                    ToComputedValue::from_computed_value(x),
-                    ToComputedValue::from_computed_value(y),
-                ))
-            },
-            EndingShape::Ellipse(Ellipse::Extent(extent)) => {
-                EndingShape::Ellipse(Ellipse::Extent(extent))
-            },
-        }
-    }
-}
-
 impl<C, L> ToCss for GradientItem<C, L>
     where C: ToCss, L: ToCss,
 {
     fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
         match *self {
             GradientItem::ColorStop(ref stop) => stop.to_css(dest),
             GradientItem::InterpolationHint(ref hint) => hint.to_css(dest),
         }
     }
 }
 
-impl<C, L> ToComputedValue for GradientItem<C, L>
-    where C: ToComputedValue, L: ToComputedValue,
-{
-    type ComputedValue = GradientItem<<C as ToComputedValue>::ComputedValue,
-                                      <L as ToComputedValue>::ComputedValue>;
-
-    fn to_computed_value(&self, context: &Context) -> Self::ComputedValue {
-        match *self {
-            GradientItem::ColorStop(ref stop) => {
-                GradientItem::ColorStop(stop.to_computed_value(context))
-            },
-            GradientItem::InterpolationHint(ref hint) => {
-                GradientItem::InterpolationHint(hint.to_computed_value(context))
-            },
-        }
-    }
-
-    fn from_computed_value(computed: &Self::ComputedValue) -> Self {
-        match *computed {
-            GradientItem::ColorStop(ref stop) => {
-                GradientItem::ColorStop(ToComputedValue::from_computed_value(stop))
-            },
-            GradientItem::InterpolationHint(ref hint) => {
-                GradientItem::InterpolationHint(ToComputedValue::from_computed_value(hint))
-            },
-        }
-    }
-}
-
 impl<C, L> fmt::Debug for ColorStop<C, L>
     where C: fmt::Debug, L: fmt::Debug,
 {
     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
         write!(f, "{:?}", self.color)?;
         if let Some(ref pos) = self.position {
             write!(f, " {:?}", pos)?;
         }
@@ -505,37 +319,16 @@ impl<C, L> ToCss for ColorStop<C, L>
         if let Some(ref position) = self.position {
             dest.write_str(" ")?;
             position.to_css(dest)?;
         }
         Ok(())
     }
 }
 
-impl<C, L> ToComputedValue for ColorStop<C, L>
-    where C: ToComputedValue, L: ToComputedValue,
-{
-    type ComputedValue = ColorStop<<C as ToComputedValue>::ComputedValue,
-                                   <L as ToComputedValue>::ComputedValue>;
-
-    fn to_computed_value(&self, context: &Context) -> Self::ComputedValue {
-        ColorStop {
-            color: self.color.to_computed_value(context),
-            position: self.position.as_ref().map(|p| p.to_computed_value(context)),
-        }
-    }
-
-    fn from_computed_value(computed: &Self::ComputedValue) -> Self {
-        ColorStop {
-            color: ToComputedValue::from_computed_value(&computed.color),
-            position: computed.position.as_ref().map(ToComputedValue::from_computed_value),
-        }
-    }
-}
-
 impl<C> ToCss for ImageRect<C>
     where C: ToCss,
 {
     fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
         dest.write_str("-moz-image-rect(")?;
         self.url.to_css(dest)?;
         dest.write_str(", ")?;
         self.top.to_css(dest)?;
@@ -543,34 +336,8 @@ impl<C> ToCss for ImageRect<C>
         self.right.to_css(dest)?;
         dest.write_str(", ")?;
         self.bottom.to_css(dest)?;
         dest.write_str(", ")?;
         self.left.to_css(dest)?;
         dest.write_str(")")
     }
 }
-
-impl<C> ToComputedValue for ImageRect<C>
-    where C: ToComputedValue,
-{
-    type ComputedValue = ImageRect<<C as ToComputedValue>::ComputedValue>;
-
-    fn to_computed_value(&self, context: &Context) -> Self::ComputedValue {
-        ImageRect {
-            url: self.url.to_computed_value(context),
-            top: self.top.to_computed_value(context),
-            right: self.right.to_computed_value(context),
-            bottom: self.bottom.to_computed_value(context),
-            left: self.left.to_computed_value(context),
-        }
-    }
-
-    fn from_computed_value(computed: &Self::ComputedValue) -> Self {
-        ImageRect {
-            url: ToComputedValue::from_computed_value(&computed.url),
-            top: ToComputedValue::from_computed_value(&computed.top),
-            right: ToComputedValue::from_computed_value(&computed.right),
-            bottom: ToComputedValue::from_computed_value(&computed.bottom),
-            left: ToComputedValue::from_computed_value(&computed.left),
-        }
-    }
-}
--- a/servo/components/style/values/generics/mod.rs
+++ b/servo/components/style/values/generics/mod.rs
@@ -5,30 +5,28 @@
 //! Generic types that share their serialization implementations
 //! for both specified and computed values.
 
 use counter_style::parse_counter_style_name;
 use cssparser::Parser;
 use euclid::size::Size2D;
 use parser::{Parse, ParserContext};
 use std::fmt;
-use style_traits::ToCss;
+use style_traits::{HasViewportPercentage, ToCss};
 use super::CustomIdent;
-use super::HasViewportPercentage;
-use super::computed::{Context, ToComputedValue};
 
 pub use self::basic_shape::serialize_radius_values;
 
 pub mod basic_shape;
 pub mod image;
 pub mod position;
 
-#[derive(Clone, PartialEq, Debug)]
+#[derive(Clone, Debug, PartialEq, ToComputedValue)]
 #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
-/// A type for representing CSS `widthh` and `height` values.
+/// A type for representing CSS `width` and `height` values.
 pub struct BorderRadiusSize<L>(pub Size2D<L>);
 
 impl<L> HasViewportPercentage for BorderRadiusSize<L> {
     #[inline]
     fn has_viewport_percentage(&self) -> bool { false }
 }
 
 impl<L: Clone> From<L> for BorderRadiusSize<L> {
@@ -57,34 +55,16 @@ impl<L: ToCss> ToCss for BorderRadiusSiz
     #[inline]
     fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
         self.0.width.to_css(dest)?;
         dest.write_str(" ")?;
         self.0.height.to_css(dest)
     }
 }
 
-impl<L: ToComputedValue> ToComputedValue for BorderRadiusSize<L> {
-    type ComputedValue = BorderRadiusSize<L::ComputedValue>;
-
-    #[inline]
-    fn to_computed_value(&self, context: &Context) -> Self::ComputedValue {
-        let w = self.0.width.to_computed_value(context);
-        let h = self.0.height.to_computed_value(context);
-        BorderRadiusSize(Size2D::new(w, h))
-    }
-
-    #[inline]
-    fn from_computed_value(computed: &Self::ComputedValue) -> Self {
-        let w = ToComputedValue::from_computed_value(&computed.0.width);
-        let h = ToComputedValue::from_computed_value(&computed.0.height);
-        BorderRadiusSize(Size2D::new(w, h))
-    }
-}
-
 /// https://drafts.csswg.org/css-counter-styles/#typedef-counter-style
 ///
 /// Since wherever <counter-style> is used, 'none' is a valid value as
 /// well, we combine them into one type to make code simpler.
 #[derive(Clone, PartialEq, Eq, Debug)]
 pub enum CounterStyleOrNone {
     /// none
     None_,
--- a/servo/components/style/values/generics/position.rs
+++ b/servo/components/style/values/generics/position.rs
@@ -1,18 +1,16 @@
 /* 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/. */
 
 //! Generic types for CSS handling of specified and computed values of
 //! [`position`](https://drafts.csswg.org/css-backgrounds-3/#position)
 
-use values::computed::{Context, ToComputedValue};
-
-#[derive(Clone, Copy, Debug, HasViewportPercentage, PartialEq)]
+#[derive(Clone, Copy, Debug, HasViewportPercentage, PartialEq, ToComputedValue)]
 #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
 /// A generic type for representing a CSS [position](https://drafts.csswg.org/css-values/#position).
 pub struct Position<H, V> {
     /// The horizontal component of position.
     pub horizontal: H,
     /// The vertical component of position.
     pub vertical: V,
 }
@@ -21,29 +19,8 @@ impl<H, V> Position<H, V> {
     /// Returns a new position.
     pub fn new(horizontal: H, vertical: V) -> Self {
         Self {
             horizontal: horizontal,
             vertical: vertical,
         }
     }
 }
-
-impl<H: ToComputedValue, V: ToComputedValue> ToComputedValue for Position<H, V> {
-    type ComputedValue = Position<<H as ToComputedValue>::ComputedValue,
-                                  <V as ToComputedValue>::ComputedValue>;
-
-    #[inline]
-    fn to_computed_value(&self, context: &Context) -> Self::ComputedValue {
-        Position {
-            horizontal: self.horizontal.to_computed_value(context),
-            vertical: self.vertical.to_computed_value(context),
-        }
-    }
-
-    #[inline]
-    fn from_computed_value(computed: &Self::ComputedValue) -> Self {
-        Self {
-            horizontal: ToComputedValue::from_computed_value(&computed.horizontal),
-            vertical: ToComputedValue::from_computed_value(&computed.vertical),
-        }
-    }
-}
--- a/servo/components/style/values/mod.rs
+++ b/servo/components/style/values/mod.rs
@@ -11,17 +11,16 @@
 use Atom;
 pub use cssparser::{RGBA, Token, Parser, serialize_identifier, serialize_string};
 use parser::{Parse, ParserContext};
 use std::ascii::AsciiExt;
 use std::borrow::Cow;
 use std::fmt::{self, Debug};
 use std::hash;
 use style_traits::ToCss;
-pub use style_traits::HasViewportPercentage;
 
 pub mod computed;
 pub mod generics;
 pub mod specified;
 
 /// A CSS float value.
 pub type CSSFloat = f32;
 
--- a/servo/components/style/values/specified/calc.rs
+++ b/servo/components/style/values/specified/calc.rs
@@ -6,19 +6,19 @@
 //!
 //! [calc]: https://drafts.csswg.org/css-values/#calc-notation
 
 use app_units::Au;
 use cssparser::{Parser, Token};
 use parser::ParserContext;
 use std::ascii::AsciiExt;
 use std::fmt;
-use style_traits::ToCss;
+use style_traits::{HasViewportPercentage, ToCss};
 use style_traits::values::specified::AllowedLengthType;
-use values::{CSSInteger, CSSFloat, HasViewportPercentage};
+use values::{CSSInteger, CSSFloat};
 use values::specified::{Angle, Time};
 use values::specified::length::{FontRelativeLength, NoCalcLength, ViewportPercentageLength};
 
 /// A node inside a `Calc` expression's AST.
 #[derive(Clone, Debug)]
 pub enum CalcNode {
     /// `<length>`
     Length(NoCalcLength),
--- a/servo/components/style/values/specified/grid.rs
+++ b/servo/components/style/values/specified/grid.rs
@@ -3,18 +3,18 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 //! Necessary types for [grid](https://drafts.csswg.org/css-grid/).
 
 use cssparser::{Parser, Token, serialize_identifier};
 use parser::{Parse, ParserContext};
 use std::{fmt, mem, usize};
 use std::ascii::AsciiExt;
-use style_traits::ToCss;
-use values::{CSSFloat, CustomIdent, Either, HasViewportPercentage};
+use style_traits::{HasViewportPercentage, ToCss};
+use values::{CSSFloat, CustomIdent, Either};
 use values::computed::{self, ComputedValueAsSpecified, Context, ToComputedValue};
 use values::specified::{Integer, LengthOrPercentage};
 
 #[derive(PartialEq, Clone, Debug)]
 #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
 /// A `<grid-line>` type.
 ///
 /// https://drafts.csswg.org/css-grid/#typedef-grid-row-start-grid-line
--- a/servo/components/style/values/specified/length.rs
+++ b/servo/components/style/values/specified/length.rs
@@ -9,21 +9,21 @@
 use app_units::Au;
 use cssparser::{Parser, Token};
 use euclid::size::Size2D;
 use font_metrics::FontMetricsQueryResult;
 use parser::{Parse, ParserContext};
 use std::{cmp, fmt, mem};
 use std::ascii::AsciiExt;
 use std::ops::Mul;
-use style_traits::ToCss;
+use style_traits::{HasViewportPercentage, ToCss};
 use style_traits::values::specified::{AllowedLengthType, AllowedNumericType};
 use stylesheets::CssRuleType;
 use super::{AllowQuirks, Number, ToComputedValue};
-use values::{Auto, CSSFloat, Either, FONT_MEDIUM_PX, HasViewportPercentage, None_, Normal};
+use values::{Auto, CSSFloat, Either, FONT_MEDIUM_PX, None_, Normal};
 use values::ExtremumLength;
 use values::computed::{ComputedValueAsSpecified, Context};
 use values::specified::calc::CalcNode;
 
 pub use values::specified::calc::CalcLengthOrPercentage;
 pub use super::image::{ColorStop, EndingShape as GradientEndingShape, Gradient};
 pub use super::image::{GradientKind, Image};
 
--- a/servo/components/style/values/specified/position.rs
+++ b/servo/components/style/values/specified/position.rs
@@ -5,18 +5,17 @@
 //! CSS handling for the specified value of
 //! [`position`][position]s
 //!
 //! [position]: https://drafts.csswg.org/css-backgrounds-3/#position
 
 use cssparser::Parser;
 use parser::{Parse, ParserContext};
 use std::fmt;
-use style_traits::ToCss;
-use values::HasViewportPercentage;
+use style_traits::{HasViewportPercentage, ToCss};
 use values::computed::{CalcLengthOrPercentage, LengthOrPercentage as ComputedLengthOrPercentage};
 use values::computed::{Context, ToComputedValue};
 use values::generics::position::Position as GenericPosition;
 use values::specified::{AllowQuirks, LengthOrPercentage, Percentage};
 
 /// The specified value of a CSS `<position>`
 pub type Position = GenericPosition<HorizontalPosition, VerticalPosition>;
 
--- a/servo/components/style_derive/lib.rs
+++ b/servo/components/style_derive/lib.rs
@@ -5,14 +5,21 @@
 extern crate proc_macro;
 #[macro_use] extern crate quote;
 extern crate syn;
 extern crate synstructure;
 
 use proc_macro::TokenStream;
 
 mod has_viewport_percentage;
+mod to_computed_value;
 
 #[proc_macro_derive(HasViewportPercentage)]
 pub fn derive_has_viewport_percentage(stream: TokenStream) -> TokenStream {
     let input = syn::parse_derive_input(&stream.to_string()).unwrap();
     has_viewport_percentage::derive(input).to_string().parse().unwrap()
 }
+
+#[proc_macro_derive(ToComputedValue)]
+pub fn derive_to_computed_value(stream: TokenStream) -> TokenStream {
+    let input = syn::parse_derive_input(&stream.to_string()).unwrap();
+    to_computed_value::derive(input).to_string().parse().unwrap()
+}
new file mode 100644
--- /dev/null
+++ b/servo/components/style_derive/to_computed_value.rs
@@ -0,0 +1,142 @@
+/* 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/. */
+
+use quote;
+use syn;
+use synstructure;
+
+pub fn derive(input: syn::DeriveInput) -> quote::Tokens {
+    let name = &input.ident;
+    let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl();
+    let mut where_clause = where_clause.clone();
+    for param in &input.generics.ty_params {
+        where_clause.predicates.push(where_predicate(syn::Ty::Path(None, param.ident.clone().into()), None));
+    }
+
+    let computed_value_type = syn::Path::from(syn::PathSegment {
+        ident: name.clone(),
+        parameters: syn::PathParameters::AngleBracketed(syn::AngleBracketedParameterData {
+            lifetimes: input.generics.lifetimes.iter().map(|l| {
+                l.lifetime.clone()
+            }).collect(),
+            types: input.generics.ty_params.iter().map(|ty| {
+                syn::Ty::Path(
+                    Some(syn::QSelf {
+                        ty: Box::new(syn::Ty::Path(None, ty.ident.clone().into())),
+                        position: 3,
+                    }),
+                    syn::Path {
+                        global: true,
+                        segments: vec![
+                            "values".into(),
+                            "computed".into(),
+                            "ToComputedValue".into(),
+                            "ComputedValue".into(),
+                        ],
+                    },
+                )
+            }).collect(),
+            .. Default::default()
+        }),
+    });
+
+    let to_body = match_body(&input, |field| {
+        quote!(::values::computed::ToComputedValue::to_computed_value(#field, context))
+    });
+    let from_body = match_body(&input, |field| {
+        quote!(::values::computed::ToComputedValue::from_computed_value(#field))
+    });
+
+    quote! {
+        impl #impl_generics ::values::computed::ToComputedValue for #name #ty_generics #where_clause {
+            type ComputedValue = #computed_value_type;
+
+            #[allow(unused_variables)]
+            #[inline]
+            fn to_computed_value(&self, context: &::values::computed::Context) -> Self::ComputedValue {
+                match *self {
+                    #to_body
+                }
+            }
+
+            #[inline]
+            fn from_computed_value(computed: &Self::ComputedValue) -> Self {
+                match *computed {
+                    #from_body
+                }
+            }
+        }
+    }
+}
+
+fn match_body<F>(input: &syn::DeriveInput, f: F) -> quote::Tokens
+    where F: Fn(&synstructure::BindingInfo) -> quote::Tokens,
+{
+    let by_ref = synstructure::BindStyle::Ref.into();
+    let by_value = synstructure::BindStyle::Move.into();
+
+    synstructure::each_variant(&input, &by_ref, |fields, variant| {
+        let name = if let syn::Body::Enum(_) = input.body {
+            format!("{}::{}", input.ident, variant.ident).into()
+        } else {
+            variant.ident.clone()
+        };
+        let (computed_value, computed_fields) = synstructure::match_pattern(&name, &variant.data, &by_value);
+        let fields_pairs = fields.iter().zip(computed_fields.iter());
+        let mut computations = quote!();
+        computations.append_all(fields_pairs.map(|(field, computed_field)| {
+            let expr = f(field);
+            quote!(let #computed_field = #expr;)
+        }));
+        Some(quote!(
+            #computations
+            #computed_value
+        ))
+    })
+}
+
+/// `#ty: ::values::computed::ToComputedValue<ComputedValue = #computed_value,>`
+fn where_predicate(ty: syn::Ty, computed_value: Option<syn::Ty>) -> syn::WherePredicate {
+    syn::WherePredicate::BoundPredicate(syn::WhereBoundPredicate {
+        bound_lifetimes: vec![],
+        bounded_ty: ty,
+        bounds: vec![syn::TyParamBound::Trait(
+            syn::PolyTraitRef {
+                bound_lifetimes: vec![],
+                trait_ref: trait_ref(computed_value),
+            },
+            syn::TraitBoundModifier::None
+        )],
+    })
+}
+
+/// `::values::computed::ToComputedValue<ComputedValue = #computed_value,>`
+fn trait_ref(computed_value: Option<syn::Ty>) -> syn::Path {
+    syn::Path {
+        global: true,
+        segments: vec![
+            "values".into(),
+            "computed".into(),
+            syn::PathSegment {
+                ident: "ToComputedValue".into(),
+                parameters: syn::PathParameters::AngleBracketed(
+                    syn::AngleBracketedParameterData {
+                        bindings: trait_bindings(computed_value),
+                        .. Default::default()
+                    }
+                ),
+            }
+        ],
+    }
+}
+
+/// `ComputedValue = #computed_value,`
+fn trait_bindings(computed_value: Option<syn::Ty>) -> Vec<syn::TypeBinding> {
+    computed_value.into_iter().map(|ty| {
+        syn::TypeBinding {
+            ident: "ComputedValue".into(),
+            ty: ty,
+        }
+    }).collect()
+}
--- a/servo/tests/unit/style/parsing/value.rs
+++ b/servo/tests/unit/style/parsing/value.rs
@@ -3,18 +3,18 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 use app_units::Au;
 use cssparser::Parser;
 use media_queries::CSSErrorReporterTest;
 use style::context::QuirksMode;
 use style::parser::{PARSING_MODE_ALLOW_ALL_NUMERIC_VALUES, ParserContext};
 use style::stylesheets::{CssRuleType, Origin};
-use style::values::HasViewportPercentage;
 use style::values::specified::{AbsoluteLength, NoCalcLength, Number, ViewportPercentageLength};
+use style_traits::HasViewportPercentage;
 
 #[test]
 fn length_has_viewport_percentage() {
     let l = NoCalcLength::ViewportPercentage(ViewportPercentageLength::Vw(100.));
     assert!(l.has_viewport_percentage());
     let l = NoCalcLength::Absolute(AbsoluteLength::Px(Au(100).to_f32_px()));
     assert!(!l.has_viewport_percentage());
 }
--- a/servo/tests/unit/style/properties/viewport.rs
+++ b/servo/tests/unit/style/properties/viewport.rs
@@ -1,17 +1,17 @@
 /* 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/. */
 
 use app_units::Au;
 use style::properties::PropertyDeclaration;
 use style::properties::longhands::border_top_width;
-use style::values::HasViewportPercentage;
 use style::values::specified::{AbsoluteLength, Length, NoCalcLength, ViewportPercentageLength};
+use style_traits::HasViewportPercentage;
 
 #[test]
 fn has_viewport_percentage_for_specified_value() {
     //TODO: test all specified value with a HasViewportPercentage impl
     let pvw = PropertyDeclaration::BorderTopWidth(
         border_top_width::SpecifiedValue::from_length(
             Length::NoCalc(NoCalcLength::ViewportPercentage(ViewportPercentageLength::Vw(100.)))
         )