servo: Merge #18429 - style: Remove a few uses of ComputedValueAsSpecified (from emilio:cvas-die); r=nox
authorEmilio Cobos Álvarez <emilio@crisal.io>
Mon, 11 Sep 2017 04:31:10 -0500
changeset 429526 629ebf9fa7259fc0a4e7ea4dbf62f88d61ccb105
parent 429525 4e34fdb8b3ff80c1518c68f4626f6eb5d5ef93c4
child 429527 994883c61ef580925e5669360a4bb619886f9615
push id7761
push userjlund@mozilla.com
push dateFri, 15 Sep 2017 00:19:52 +0000
treeherdermozilla-beta@c38455951db4 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersnox
milestone57.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 #18429 - style: Remove a few uses of ComputedValueAsSpecified (from emilio:cvas-die); r=nox Source-Repo: https://github.com/servo/servo Source-Revision: 7fc2c435513feadf1dc666e7873095884dfd6d84
servo/components/style/gecko/conversions.rs
servo/components/style/macros.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/box.mako.rs
servo/components/style/properties/longhand/font.mako.rs
servo/components/style/properties/longhand/inherited_svg.mako.rs
servo/components/style/properties/longhand/inherited_text.mako.rs
servo/components/style/properties/longhand/position.mako.rs
servo/components/style/properties/longhand/table.mako.rs
servo/components/style/properties/shorthand/position.mako.rs
servo/components/style/values/computed/align.rs
servo/components/style/values/computed/mod.rs
servo/components/style/values/generics/basic_shape.rs
servo/components/style/values/generics/grid.rs
servo/components/style/values/generics/image.rs
servo/components/style/values/mod.rs
servo/components/style/values/specified/align.rs
servo/components/style/values/specified/grid.rs
servo/components/style/values/specified/mod.rs
--- a/servo/components/style/gecko/conversions.rs
+++ b/servo/components/style/gecko/conversions.rs
@@ -13,17 +13,17 @@ use gecko::values::{convert_rgba_to_nsco
 use gecko_bindings::bindings::{Gecko_CreateGradient, Gecko_SetGradientImageValue, Gecko_SetLayerImageImageValue};
 use gecko_bindings::bindings::{Gecko_InitializeImageCropRect, Gecko_SetImageElement};
 use gecko_bindings::structs::{self, nsCSSUnit, nsStyleCoord_CalcValue};
 use gecko_bindings::structs::{nsStyleImage, nsresult, SheetType};
 use gecko_bindings::sugar::ns_style_coord::{CoordDataValue, CoordData, CoordDataMut};
 use std::f32::consts::PI;
 use stylesheets::{Origin, RulesMutateError};
 use values::computed::{Angle, CalcLengthOrPercentage, Gradient, Image};
-use values::computed::{LengthOrPercentage, LengthOrPercentageOrAuto, Percentage};
+use values::computed::{Integer, LengthOrPercentage, LengthOrPercentageOrAuto, Percentage};
 use values::generics::box_::VerticalAlign;
 use values::generics::grid::{TrackListValue, TrackSize};
 use values::generics::image::{CompatMode, Image as GenericImage, GradientItem};
 use values::generics::rect::Rect;
 use values::specified::url::SpecifiedUrl;
 
 impl From<CalcLengthOrPercentage> for nsStyleCoord_CalcValue {
     fn from(other: CalcLengthOrPercentage) -> nsStyleCoord_CalcValue {
@@ -891,17 +891,17 @@ impl TrackSize<LengthOrPercentage> {
             TrackSize::Minmax(ref min, ref max) => {
                 min.to_gecko_style_coord(gecko_min);
                 max.to_gecko_style_coord(gecko_max);
             },
         }
     }
 }
 
-impl TrackListValue<LengthOrPercentage> {
+impl TrackListValue<LengthOrPercentage, Integer> {
     /// Return TrackSize from given two nsStyleCoord
     pub fn from_gecko_style_coords<T: CoordData>(gecko_min: &T, gecko_max: &T) -> Self {
         TrackListValue::TrackSize(TrackSize::from_gecko_style_coords(gecko_min, gecko_max))
     }
 
     /// Save TrackSize to given gecko fields.
     pub fn to_gecko_style_coords<T: CoordDataMut>(&self, gecko_min: &mut T, gecko_max: &mut T) {
         use values::generics::grid::TrackListValue;
--- a/servo/components/style/macros.rs
+++ b/servo/components/style/macros.rs
@@ -1,14 +1,30 @@
 /* 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/. */
 
 //! Various macro helpers.
 
+macro_rules! trivial_to_computed_value {
+    ($name: ident) => {
+        impl $crate::values::computed::ToComputedValue for $name {
+            type ComputedValue = $name;
+
+            fn to_computed_value(&self, _: &$crate::values::computed::Context) -> Self {
+                self.clone()
+            }
+
+            fn from_computed_value(other: &Self) -> Self {
+                other.clone()
+            }
+        }
+    }
+}
+
 /// A macro to parse an identifier, or return an `UnexpectedIndent` error
 /// otherwise.
 ///
 /// FIXME(emilio): The fact that `UnexpectedIdent` is a `SelectorParseError`
 /// doesn't make a lot of sense to me.
 macro_rules! try_match_ident_ignore_ascii_case {
     ($ident:expr, $( $match_body:tt )*) => {
         let __ident = $ident;
@@ -30,77 +46,79 @@ macro_rules! define_numbered_css_keyword
         #[allow(non_camel_case_types, missing_docs)]
         #[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd)]
         #[cfg_attr(feature = "servo", derive(HeapSizeOf, Deserialize, Serialize))]
         pub enum $name {
             $( $variant = $value ),+
         }
 
         impl $crate::parser::Parse for $name {
-            #[allow(missing_docs)]
-            fn parse<'i, 't>(_context: &$crate::parser::ParserContext,
-                             input: &mut ::cssparser::Parser<'i, 't>)
-                             -> Result<$name, ::style_traits::ParseError<'i>> {
+            fn parse<'i, 't>(
+                _context: &$crate::parser::ParserContext,
+                input: &mut ::cssparser::Parser<'i, 't>,
+            ) -> Result<$name, ::style_traits::ParseError<'i>> {
                 try_match_ident_ignore_ascii_case! { input.expect_ident()?,
                     $( $css => Ok($name::$variant), )+
                 }
             }
         }
 
         impl ::style_traits::values::ToCss for $name {
             fn to_css<W>(&self, dest: &mut W) -> ::std::fmt::Result
-                where W: ::std::fmt::Write,
+            where
+                W: ::std::fmt::Write,
             {
                 match *self {
                     $( $name::$variant => dest.write_str($css) ),+
                 }
             }
         }
     }
 }
 
-/// A macro for implementing `ComputedValueAsSpecified`, and `Parse` traits for
+/// A macro for implementing `ToComputedValue`, and `Parse` traits for
 /// the enums defined using `define_css_keyword_enum` macro.
 ///
 /// NOTE: We should either move `Parse` trait to `style_traits`
 /// or `define_css_keyword_enum` macro to this crate, but that
 /// may involve significant cleanup in both the crates.
 macro_rules! add_impls_for_keyword_enum {
     ($name:ident) => {
         impl $crate::parser::Parse for $name {
             #[inline]
-            fn parse<'i, 't>(_context: &$crate::parser::ParserContext,
-                             input: &mut ::cssparser::Parser<'i, 't>)
-                             -> Result<Self, ::style_traits::ParseError<'i>> {
+            fn parse<'i, 't>(
+                _context: &$crate::parser::ParserContext,
+                input: &mut ::cssparser::Parser<'i, 't>,
+            ) -> Result<Self, ::style_traits::ParseError<'i>> {
                 $name::parse(input)
             }
         }
 
-        impl $crate::values::computed::ComputedValueAsSpecified for $name {}
+        trivial_to_computed_value!($name);
     };
 }
 
 macro_rules! define_keyword_type {
     ($name: ident, $css: expr) => {
         #[allow(missing_docs)]
         #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
         #[derive(Animate, Clone, ComputeSquaredDistance, Copy, PartialEq)]
-        #[derive(ToAnimatedZero, ToCss)]
+        #[derive(ToAnimatedZero, ToComputedValue, ToCss)]
         pub struct $name;
 
         impl fmt::Debug for $name {
             fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
                 f.write_str($css)
             }
         }
 
         impl $crate::parser::Parse for $name {
-            fn parse<'i, 't>(_context: &$crate::parser::ParserContext,
-                             input: &mut ::cssparser::Parser<'i, 't>)
-                             -> Result<$name, ::style_traits::ParseError<'i>> {
+            fn parse<'i, 't>(
+                _context: &$crate::parser::ParserContext,
+                input: &mut ::cssparser::Parser<'i, 't>
+            ) -> Result<$name, ::style_traits::ParseError<'i>> {
                 input.expect_ident_matching($css).map(|_| $name).map_err(|e| e.into())
             }
         }
 
-        impl $crate::values::computed::ComputedValueAsSpecified for $name {}
         impl $crate::values::animated::AnimatedValueAsComputed for $name {}
     };
 }
--- a/servo/components/style/properties/gecko.mako.rs
+++ b/servo/components/style/properties/gecko.mako.rs
@@ -1695,34 +1695,33 @@ fn static_assert() {
         use gecko_bindings::structs::{nsStyleGridLine_kMinLine, nsStyleGridLine_kMaxLine};
 
         let ident = v.ident.as_ref().map_or(&[] as &[_], |ident| ident.0.as_slice());
         self.gecko.${value.gecko}.mLineName.assign(ident);
         self.gecko.${value.gecko}.mHasSpan = v.is_span;
         if let Some(integer) = v.line_num {
             // clamping the integer between a range
             self.gecko.${value.gecko}.mInteger = cmp::max(nsStyleGridLine_kMinLine,
-                cmp::min(integer.value(), nsStyleGridLine_kMaxLine));
+                cmp::min(integer, nsStyleGridLine_kMaxLine));
         }
     }
 
     pub fn copy_${value.name}_from(&mut self, other: &Self) {
         self.gecko.${value.gecko}.mHasSpan = other.gecko.${value.gecko}.mHasSpan;
         self.gecko.${value.gecko}.mInteger = other.gecko.${value.gecko}.mInteger;
         self.gecko.${value.gecko}.mLineName.assign(&*other.gecko.${value.gecko}.mLineName);
     }
 
     pub fn reset_${value.name}(&mut self, other: &Self) {
         self.copy_${value.name}_from(other)
     }
 
     pub fn clone_${value.name}(&self) -> longhands::${value.name}::computed_value::T {
         use gecko_bindings::structs::{nsStyleGridLine_kMinLine, nsStyleGridLine_kMaxLine};
         use string_cache::Atom;
-        use values::specified::Integer;
 
         longhands::${value.name}::computed_value::T {
             is_span: self.gecko.${value.gecko}.mHasSpan,
             ident: {
                 let name = self.gecko.${value.gecko}.mLineName.to_string();
                 if name.len() == 0 {
                     None
                 } else {
@@ -1730,17 +1729,17 @@ fn static_assert() {
                 }
             },
             line_num:
                 if self.gecko.${value.gecko}.mInteger == 0 {
                     None
                 } else {
                     debug_assert!(nsStyleGridLine_kMinLine <= self.gecko.${value.gecko}.mInteger);
                     debug_assert!(self.gecko.${value.gecko}.mInteger <= nsStyleGridLine_kMaxLine);
-                    Some(Integer::new(self.gecko.${value.gecko}.mInteger))
+                    Some(self.gecko.${value.gecko}.mInteger)
                 },
         }
     }
     % endfor
 
     % for kind in ["rows", "columns"]:
     pub fn set_grid_auto_${kind}(&mut self, v: longhands::grid_auto_${kind}::computed_value::T) {
         v.to_gecko_style_coords(&mut self.gecko.mGridAuto${kind.title()}Min,
--- a/servo/components/style/properties/helpers.mako.rs
+++ b/servo/components/style/properties/helpers.mako.rs
@@ -495,42 +495,36 @@
                 }
             }
         }
     </%call>
 </%def>
 
 <%def name="single_keyword(name, values, vector=False, **kwargs)">
     <%call expr="single_keyword_computed(name, values, vector, **kwargs)">
-        % if not "extra_specified" in kwargs and ("aliases" in kwargs or (("extra_%s_aliases" % product) in kwargs)):
-            impl ToComputedValue for SpecifiedValue {
-                type ComputedValue = computed_value::T;
+        impl ToComputedValue for SpecifiedValue {
+            type ComputedValue = computed_value::T;
 
-                #[inline]
-                fn to_computed_value(&self, _context: &Context) -> computed_value::T {
-                    match *self {
-                        % for value in data.longhands_by_name[name].keyword.values_for(product):
-                            SpecifiedValue::${to_rust_ident(value)} => computed_value::T::${to_rust_ident(value)},
-                        % endfor
-                    }
-                }
-                #[inline]
-                fn from_computed_value(computed: &computed_value::T) -> Self {
-                    match *computed {
-                        % for value in data.longhands_by_name[name].keyword.values_for(product):
-                            computed_value::T::${to_rust_ident(value)} => SpecifiedValue::${to_rust_ident(value)},
-                        % endfor
-                    }
+            #[inline]
+            fn to_computed_value(&self, _context: &Context) -> computed_value::T {
+                match *self {
+                    % for value in data.longhands_by_name[name].keyword.values_for(product):
+                        SpecifiedValue::${to_rust_ident(value)} => computed_value::T::${to_rust_ident(value)},
+                    % endfor
                 }
             }
-        % else:
-            use values::computed::ComputedValueAsSpecified;
-            impl ComputedValueAsSpecified for SpecifiedValue {}
-        % endif
-
+            #[inline]
+            fn from_computed_value(computed: &computed_value::T) -> Self {
+                match *computed {
+                    % for value in data.longhands_by_name[name].keyword.values_for(product):
+                        computed_value::T::${to_rust_ident(value)} => SpecifiedValue::${to_rust_ident(value)},
+                    % endfor
+                }
+            }
+        }
     </%call>
 </%def>
 
 <%def name="gecko_keyword_conversion(keyword, values=None, type='SpecifiedValue', cast_to=None)">
     <%
         if not values:
             values = keyword.values_for(product)
         maybe_cast = "as %s" % cast_to if cast_to else ""
--- a/servo/components/style/properties/helpers/animated_properties.mako.rs
+++ b/servo/components/style/properties/helpers/animated_properties.mako.rs
@@ -41,17 +41,17 @@ use values::Auto;
 use values::{CSSFloat, CustomIdent, Either};
 use values::animated::{Animate, Procedure, ToAnimatedValue, ToAnimatedZero};
 use values::animated::color::{Color as AnimatedColor, RGBA as AnimatedRGBA};
 use values::animated::effects::BoxShadowList as AnimatedBoxShadowList;
 use values::animated::effects::Filter as AnimatedFilter;
 use values::animated::effects::FilterList as AnimatedFilterList;
 use values::animated::effects::TextShadowList as AnimatedTextShadowList;
 use values::computed::{Angle, BorderCornerRadius, CalcLengthOrPercentage};
-use values::computed::{ClipRect, Context, ComputedUrl, ComputedValueAsSpecified};
+use values::computed::{ClipRect, Context, ComputedUrl};
 use values::computed::{LengthOrPercentage, LengthOrPercentageOrAuto};
 use values::computed::{LengthOrPercentageOrNone, MaxLength, NonNegativeAu};
 use values::computed::{NonNegativeNumber, Number, NumberOrPercentage, Percentage};
 use values::computed::{PositiveIntegerOrAuto, ToComputedValue};
 #[cfg(feature = "gecko")] use values::computed::MozLength;
 use values::computed::length::{NonNegativeLengthOrAuto, NonNegativeLengthOrNormal};
 use values::computed::length::NonNegativeLengthOrPercentage;
 use values::computed::transform::DirectionVector;
@@ -198,34 +198,31 @@ pub fn nscsspropertyid_is_animatable(pro
     }
 }
 
 /// A given transition property, that is either `All`, a transitionable longhand property,
 /// a shorthand with at least one transitionable longhand component, or an unsupported property.
 // NB: This needs to be here because it needs all the longhands generated
 // beforehand.
 #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
-#[derive(Clone, Debug, Eq, Hash, PartialEq, ToCss)]
+#[derive(Clone, Debug, Eq, Hash, PartialEq, ToCss, ToComputedValue)]
 pub enum TransitionProperty {
     /// All, any transitionable property changing should generate a transition.
     All,
     % for prop in data.longhands + data.shorthands_except_all():
         % if prop.transitionable:
             /// ${prop.name}
             ${prop.camel_case},
         % endif
     % endfor
     /// Unrecognized property which could be any non-transitionable, custom property, or
     /// unknown property.
     Unsupported(CustomIdent)
 }
 
-
-impl ComputedValueAsSpecified for TransitionProperty {}
-
 impl TransitionProperty {
     /// Iterates over each longhand property.
     pub fn each<F: FnMut(&TransitionProperty) -> ()>(mut cb: F) {
         % for prop in data.longhands:
             % if prop.transitionable:
                 cb(&TransitionProperty::${prop.camel_case});
             % endif
         % endfor
--- a/servo/components/style/properties/longhand/box.mako.rs
+++ b/servo/components/style/properties/longhand/box.mako.rs
@@ -29,17 +29,16 @@
         values += webkit_prefixed_values
         if product == "gecko":
             values += """grid inline-grid ruby ruby-base ruby-base-container
                 ruby-text ruby-text-container contents flow-root -webkit-box
                 -webkit-inline-box -moz-box -moz-inline-box -moz-grid -moz-inline-grid
                 -moz-grid-group -moz-grid-line -moz-stack -moz-inline-stack -moz-deck
                 -moz-popup -moz-groupbox""".split()
     %>
-    use values::computed::ComputedValueAsSpecified;
     use style_traits::ToCss;
 
     pub mod computed_value {
         pub use super::SpecifiedValue as T;
 
         impl T {
             /// Returns whether this "display" value is the display of a flex or
             /// grid container.
@@ -132,17 +131,17 @@
                     T::_webkit_box => T::_webkit_inline_box,
                     other => other,
                 }
             }
         }
     }
 
     #[allow(non_camel_case_types)]
-    #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
+    #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, ToComputedValue)]
     #[cfg_attr(feature = "servo", derive(HeapSizeOf, Deserialize, Serialize))]
     pub enum SpecifiedValue {
         % for value in values:
             ${to_rust_ident(value)},
         % endfor
     }
 
     impl ToCss for SpecifiedValue {
@@ -175,18 +174,16 @@
             % for value in webkit_prefixed_values:
                 "-webkit-${value}" => {
                     Ok(computed_value::T::${to_rust_ident(value)})
                 },
             % endfor
         }
     }
 
-    impl ComputedValueAsSpecified for SpecifiedValue {}
-
     % if product == "servo":
         fn cascade_property_custom(_declaration: &PropertyDeclaration,
                                    context: &mut computed::Context) {
             longhands::_servo_display_for_hypothetical_box::derive_from_display(context);
             longhands::_servo_text_decorations_in_effect::derive_from_display(context);
             longhands::_servo_under_display_none::derive_from_display(context);
         }
     % endif
@@ -398,24 +395,23 @@
                           need_index="True"
                           animation_value_type="none",
                           extra_prefixes="moz webkit"
                           allowed_in_keyframe_block="False"
                           spec="https://drafts.csswg.org/css-animations/#propdef-animation-name">
     use Atom;
     use std::fmt;
     use style_traits::ToCss;
-    use values::computed::ComputedValueAsSpecified;
     use values::KeyframesName;
 
     pub mod computed_value {
         pub use super::SpecifiedValue as T;
     }
 
-    #[derive(Clone, Debug, Eq, Hash, PartialEq)]
+    #[derive(Clone, Debug, Eq, Hash, PartialEq, ToComputedValue)]
     #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
     pub struct SpecifiedValue(pub Option<KeyframesName>);
 
     impl SpecifiedValue {
         /// As an Atom
         pub fn as_atom(&self) -> Option< &Atom> {
             self.0.as_ref().map(|n| n.as_atom())
         }
@@ -443,32 +439,35 @@
                 name.to_css(dest)
             } else {
                 dest.write_str("none")
             }
         }
     }
 
     impl Parse for SpecifiedValue {
-        fn parse<'i, 't>(context: &ParserContext, input: &mut ::cssparser::Parser<'i, 't>)
-                         -> Result<Self, ParseError<'i>> {
+        fn parse<'i, 't>(
+            context: &ParserContext,
+            input: &mut ::cssparser::Parser<'i, 't>
+        ) -> Result<Self, ParseError<'i>> {
             if let Ok(name) = input.try(|input| KeyframesName::parse(context, input)) {
                 Ok(SpecifiedValue(Some(name)))
             } else {
                 input.expect_ident_matching("none").map(|_| SpecifiedValue(None)).map_err(|e| e.into())
             }
         }
     }
 
-    pub fn parse<'i, 't>(context: &ParserContext, input: &mut Parser<'i, 't>)
-                         -> Result<SpecifiedValue,ParseError<'i>> {
+    pub fn parse<'i, 't>(
+        context: &ParserContext,
+        input: &mut Parser<'i, 't>,
+    ) -> Result<SpecifiedValue,ParseError<'i>> {
         SpecifiedValue::parse(context, input)
     }
 
-    impl ComputedValueAsSpecified for SpecifiedValue {}
 </%helpers:vector_longhand>
 
 ${helpers.predefined_type("animation-duration",
                           "Time",
                           "computed::Time::zero()",
                           initial_specified_value="specified::Time::zero()",
                           parse_method="parse_non_negative",
                           vector=True,
@@ -491,25 +490,23 @@
                           spec="https://drafts.csswg.org/css-transitions/#propdef-animation-timing-function")}
 
 <%helpers:vector_longhand name="animation-iteration-count"
                           need_index="True"
                           animation_value_type="none",
                           extra_prefixes="moz webkit"
                           spec="https://drafts.csswg.org/css-animations/#propdef-animation-iteration-count",
                           allowed_in_keyframe_block="False">
-    use values::computed::ComputedValueAsSpecified;
-
     pub mod computed_value {
         pub use super::SpecifiedValue as T;
     }
 
     // https://drafts.csswg.org/css-animations/#animation-iteration-count
     #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
-    #[derive(Clone, Debug, PartialEq, ToCss)]
+    #[derive(Clone, Debug, PartialEq, ToCss, ToComputedValue)]
     pub enum SpecifiedValue {
         Number(f32),
         Infinite,
     }
 
     impl Parse for SpecifiedValue {
         fn parse<'i, 't>(_context: &ParserContext, input: &mut ::cssparser::Parser<'i, 't>)
                          -> Result<Self, ParseError<'i>> {
@@ -533,22 +530,22 @@
     }
 
     #[inline]
     pub fn get_initial_specified_value() -> SpecifiedValue {
         SpecifiedValue::Number(1.0)
     }
 
     #[inline]
-    pub fn parse<'i, 't>(context: &ParserContext, input: &mut Parser<'i, 't>)
-                         -> Result<SpecifiedValue, ParseError<'i>> {
+    pub fn parse<'i, 't>(
+        context: &ParserContext,
+        input: &mut Parser<'i, 't>,
+    ) -> Result<SpecifiedValue, ParseError<'i>> {
         SpecifiedValue::parse(context, input)
     }
-
-    impl ComputedValueAsSpecified for SpecifiedValue {}
 </%helpers:vector_longhand>
 
 <% animation_direction_custom_consts = { "alternate-reverse": "Alternate_reverse" } %>
 ${helpers.single_keyword("animation-direction",
                          "normal reverse alternate alternate-reverse",
                          need_index=True,
                          animation_value_type="none",
                          vector=True,
@@ -1621,25 +1618,23 @@
 // FIXME: `size` and `content` values are not implemented and `strict` is implemented
 // like `content`(layout style paint) in gecko. We should implement `size` and `content`,
 // also update the glue once they are implemented in gecko.
 <%helpers:longhand name="contain" animation_value_type="discrete" products="gecko" need_clone="True"
                    flags="FIXPOS_CB"
                    spec="https://drafts.csswg.org/css-contain/#contain-property">
     use std::fmt;
     use style_traits::ToCss;
-    use values::computed::ComputedValueAsSpecified;
-
-    impl ComputedValueAsSpecified for SpecifiedValue {}
 
     pub mod computed_value {
         pub type T = super::SpecifiedValue;
     }
 
     bitflags! {
+        #[derive(ToComputedValue)]
         #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
         pub flags SpecifiedValue: u8 {
             const LAYOUT = 0x01,
             const STYLE = 0x02,
             const PAINT = 0x04,
             const STRICT = 0x8,
             const STRICT_BITS = LAYOUT.bits | STYLE.bits | PAINT.bits,
         }
@@ -1765,25 +1760,22 @@
                           gecko_inexhaustive="True",
                           animation_value_type="discrete")}
 
 <%helpers:longhand name="will-change" products="gecko" animation_value_type="discrete"
                    spec="https://drafts.csswg.org/css-will-change/#will-change">
     use std::fmt;
     use style_traits::ToCss;
     use values::CustomIdent;
-    use values::computed::ComputedValueAsSpecified;
-
-    impl ComputedValueAsSpecified for SpecifiedValue {}
 
     pub mod computed_value {
         pub use super::SpecifiedValue as T;
     }
 
-    #[derive(Clone, Debug, PartialEq)]
+    #[derive(Clone, Debug, PartialEq, ToComputedValue)]
     #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
     pub enum SpecifiedValue {
         Auto,
         AnimateableFeatures(Vec<CustomIdent>),
     }
 
     impl ToCss for SpecifiedValue {
         fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
@@ -1838,26 +1830,24 @@
 
 <%helpers:longhand name="touch-action"
                    products="gecko"
                    animation_value_type="discrete"
                    spec="https://compat.spec.whatwg.org/#touch-action">
     use gecko_bindings::structs;
     use std::fmt;
     use style_traits::ToCss;
-    use values::computed::ComputedValueAsSpecified;
-
-    impl ComputedValueAsSpecified for SpecifiedValue {}
 
     pub mod computed_value {
         pub use super::SpecifiedValue as T;
     }
 
     bitflags! {
         /// These constants match Gecko's `NS_STYLE_TOUCH_ACTION_*` constants.
+        #[derive(ToComputedValue)]
         pub flags SpecifiedValue: u8 {
             const TOUCH_ACTION_NONE = structs::NS_STYLE_TOUCH_ACTION_NONE as u8,
             const TOUCH_ACTION_AUTO = structs::NS_STYLE_TOUCH_ACTION_AUTO as u8,
             const TOUCH_ACTION_PAN_X = structs::NS_STYLE_TOUCH_ACTION_PAN_X as u8,
             const TOUCH_ACTION_PAN_Y = structs::NS_STYLE_TOUCH_ACTION_PAN_Y as u8,
             const TOUCH_ACTION_MANIPULATION = structs::NS_STYLE_TOUCH_ACTION_MANIPULATION as u8,
         }
     }
--- a/servo/components/style/properties/longhand/font.mako.rs
+++ b/servo/components/style/properties/longhand/font.mako.rs
@@ -1170,25 +1170,22 @@ macro_rules! impl_gecko_keyword_conversi
     }
 </%helpers:longhand>
 
 <%helpers:longhand products="gecko" name="font-synthesis" animation_value_type="discrete"
                    flags="APPLIES_TO_FIRST_LETTER APPLIES_TO_FIRST_LINE APPLIES_TO_PLACEHOLDER"
                    spec="https://drafts.csswg.org/css-fonts/#propdef-font-synthesis">
     use std::fmt;
     use style_traits::ToCss;
-    use values::computed::ComputedValueAsSpecified;
-
-    impl ComputedValueAsSpecified for SpecifiedValue {}
 
     pub mod computed_value {
         pub use super::SpecifiedValue as T;
     }
 
-    #[derive(Clone, Debug, PartialEq)]
+    #[derive(Clone, Debug, PartialEq, ToComputedValue)]
     #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
     pub struct SpecifiedValue {
         pub weight: bool,
         pub style: bool,
     }
 
     impl ToCss for computed_value::T {
         fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
@@ -2181,33 +2178,30 @@ https://drafts.csswg.org/css-fonts-4/#lo
         fn from(v: computed_value::T) -> u32 {
             v.0
         }
     }
 </%helpers:longhand>
 
 <%helpers:longhand name="-x-lang" products="gecko" animation_value_type="none" internal="True"
                    spec="Internal (not web-exposed)">
-    use values::computed::ComputedValueAsSpecified;
     pub use self::computed_value::T as SpecifiedValue;
 
-    impl ComputedValueAsSpecified for SpecifiedValue {}
-
     pub mod computed_value {
         use Atom;
         use std::fmt;
         use style_traits::ToCss;
 
         impl ToCss for T {
             fn to_css<W>(&self, _: &mut W) -> fmt::Result where W: fmt::Write {
                 Ok(())
             }
         }
 
-        #[derive(Clone, Debug, PartialEq)]
+        #[derive(Clone, Debug, PartialEq, ToComputedValue)]
         #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
         pub struct T(pub Atom);
     }
 
     #[inline]
     pub fn get_initial_value() -> computed_value::T {
         computed_value::T(atom!(""))
     }
@@ -2388,32 +2382,29 @@ https://drafts.csswg.org/css-fonts-4/#lo
                          -> Result<SpecifiedValue, ParseError<'i>> {
         debug_assert!(false, "Should be set directly by presentation attributes only.");
         Err(StyleParseError::UnspecifiedError.into())
     }
 </%helpers:longhand>
 
 <%helpers:longhand name="-x-text-zoom" products="gecko" animation_value_type="none" internal="True"
                    spec="Internal (not web-exposed)">
-    use values::computed::ComputedValueAsSpecified;
     pub use self::computed_value::T as SpecifiedValue;
 
-    impl ComputedValueAsSpecified for SpecifiedValue {}
-
     pub mod computed_value {
         use std::fmt;
         use style_traits::ToCss;
 
         impl ToCss for T {
             fn to_css<W>(&self, _: &mut W) -> fmt::Result where W: fmt::Write {
                 Ok(())
             }
         }
 
-        #[derive(Clone, Debug, PartialEq)]
+        #[derive(Clone, Debug, PartialEq, ToComputedValue)]
         #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
         /// text-zoom. Enable if true, disable if false
         pub struct T(pub bool);
     }
 
     #[inline]
     pub fn get_initial_value() -> computed_value::T {
         computed_value::T(true)
--- a/servo/components/style/properties/longhand/inherited_svg.mako.rs
+++ b/servo/components/style/properties/longhand/inherited_svg.mako.rs
@@ -263,20 +263,16 @@
     impl ComputedValueAsSpecified for SpecifiedValue { }
 </%helpers:longhand>
 <%helpers:vector_longhand name="-moz-context-properties"
                    animation_value_type="none"
                    products="gecko"
                    spec="Nonstandard (Internal-only)"
                    allow_empty="True">
     use values::CustomIdent;
-    use values::computed::ComputedValueAsSpecified;
-
-
-    impl ComputedValueAsSpecified for SpecifiedValue { }
 
     pub type SpecifiedValue = CustomIdent;
 
     pub mod computed_value {
         pub type T = super::SpecifiedValue;
     }
 
 
--- a/servo/components/style/properties/longhand/inherited_text.mako.rs
+++ b/servo/components/style/properties/longhand/inherited_text.mako.rs
@@ -250,19 +250,18 @@
             }
 
             #[inline]
             fn from_computed_value(computed: &computed_value::T) -> Self {
                 SpecifiedValue::Keyword(*computed)
             }
         }
     % else:
-        use values::computed::ComputedValueAsSpecified;
-        impl ComputedValueAsSpecified for SpecifiedValue {}
         pub use self::computed_value::T as SpecifiedValue;
+        add_impls_for_keyword_enum!(SpecifiedValue);
         pub fn parse<'i, 't>(_context: &ParserContext, input: &mut Parser<'i, 't>)
                              -> Result<SpecifiedValue, ParseError<'i>> {
             computed_value::T::parse(input)
         }
     % endif
 </%helpers:longhand>
 
 ${helpers.predefined_type("letter-spacing",
@@ -282,28 +281,27 @@
 <%helpers:longhand name="-servo-text-decorations-in-effect"
                    derived_from="display text-decoration"
                    need_clone="True" products="servo"
                    animation_value_type="none"
                    spec="Nonstandard (Internal property used by Servo)">
     use cssparser::RGBA;
     use std::fmt;
     use style_traits::ToCss;
-    use values::computed::ComputedValueAsSpecified;
-
-    impl ComputedValueAsSpecified for SpecifiedValue {}
 
     #[derive(Clone, Copy, Debug, PartialEq)]
     #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
     pub struct SpecifiedValue {
         pub underline: Option<RGBA>,
         pub overline: Option<RGBA>,
         pub line_through: Option<RGBA>,
     }
 
+    trivial_to_computed_value!(SpecifiedValue);
+
     pub mod computed_value {
         pub type T = super::SpecifiedValue;
     }
 
     impl ToCss for SpecifiedValue {
         fn to_css<W>(&self, _: &mut W) -> fmt::Result where W: fmt::Write {
             // Web compat doesn't matter here.
             Ok(())
@@ -369,19 +367,17 @@
                                   gecko_enum_prefix="StyleWhiteSpace"
                                   needs_conversion="True"
                                   gecko_inexhaustive="True"
                                   animation_value_type="discrete"
                                   // Only allowed for UA sheets, which set it
                                   // !important.
                                   flags="APPLIES_TO_PLACEHOLDER"
                                   spec="https://drafts.csswg.org/css-text/#propdef-white-space">
-    use values::computed::ComputedValueAsSpecified;
-    impl ComputedValueAsSpecified for SpecifiedValue {}
-
+    trivial_to_computed_value!(SpecifiedValue);
     % if product != "gecko":
     impl SpecifiedValue {
         pub fn allow_wrap(&self) -> bool {
             match *self {
                 SpecifiedValue::nowrap |
                 SpecifiedValue::pre => false,
                 SpecifiedValue::normal |
                 SpecifiedValue::pre_wrap |
@@ -428,34 +424,34 @@
                    spec="https://drafts.csswg.org/css-text-decor/#propdef-text-emphasis-style">
     use computed_values::writing_mode::T as writing_mode;
     use std::fmt;
     use style_traits::ToCss;
     use unicode_segmentation::UnicodeSegmentation;
 
 
     pub mod computed_value {
-        #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
         #[derive(Clone, Debug, PartialEq, ToCss)]
+        #[cfg_attr(feature = "servo", derive(HeapSizeOf, ToComputedValue))]
         pub enum T {
             Keyword(KeywordValue),
             None,
             String(String),
         }
 
         #[derive(Clone, Debug, PartialEq)]
         #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
         pub struct KeywordValue {
             pub fill: bool,
             pub shape: super::ShapeKeyword,
         }
     }
 
+    #[derive(Clone, Debug, PartialEq, ToCss)]
     #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
-    #[derive(Clone, Debug, PartialEq, ToCss)]
     pub enum SpecifiedValue {
         Keyword(KeywordValue),
         None,
         String(String),
     }
 
     #[derive(Clone, Debug, PartialEq)]
     #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
@@ -609,36 +605,35 @@
             _ => return Err(StyleParseError::UnspecifiedError.into()),
         };
         Ok(SpecifiedValue::Keyword(keyword_value))
     }
 </%helpers:longhand>
 
 <%helpers:longhand name="text-emphasis-position" animation_value_type="discrete" products="gecko"
                    spec="https://drafts.csswg.org/css-text-decor/#propdef-text-emphasis-position">
-    use values::computed::ComputedValueAsSpecified;
     use style_traits::ToCss;
 
     define_css_keyword_enum!(HorizontalWritingModeValue:
                              "over" => Over,
                              "under" => Under);
+    add_impls_for_keyword_enum!(VerticalWritingModeValue);
     define_css_keyword_enum!(VerticalWritingModeValue:
                              "right" => Right,
                              "left" => Left);
+    add_impls_for_keyword_enum!(HorizontalWritingModeValue);
 
     #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
-    #[derive(Clone, Debug, PartialEq, ToCss)]
+    #[derive(Clone, Debug, PartialEq, ToComputedValue, ToCss)]
     pub struct SpecifiedValue(pub HorizontalWritingModeValue, pub VerticalWritingModeValue);
 
     pub mod computed_value {
         pub type T = super::SpecifiedValue;
     }
 
-    impl ComputedValueAsSpecified for SpecifiedValue {}
-
     pub fn get_initial_value() -> computed_value::T {
         SpecifiedValue(HorizontalWritingModeValue::Over, VerticalWritingModeValue::Right)
     }
 
     pub fn parse<'i, 't>(_context: &ParserContext, input: &mut Parser<'i, 't>)
                          -> Result<SpecifiedValue, ParseError<'i>> {
        if let Ok(horizontal) = input.try(|input| HorizontalWritingModeValue::parse(input)) {
             let vertical = VerticalWritingModeValue::parse(input)?;
--- a/servo/components/style/properties/longhand/position.mako.rs
+++ b/servo/components/style/properties/longhand/position.mako.rs
@@ -286,38 +286,35 @@ macro_rules! impl_align_conversions {
 % endfor
 
 <%helpers:longhand name="grid-auto-flow"
         spec="https://drafts.csswg.org/css-grid/#propdef-grid-auto-flow"
         products="gecko"
         animation_value_type="discrete">
     use std::fmt;
     use style_traits::ToCss;
-    use values::computed::ComputedValueAsSpecified;
 
     pub type SpecifiedValue = computed_value::T;
 
     pub mod computed_value {
-        #[derive(Clone, Copy, Debug, Eq, PartialEq)]
+        #[derive(Clone, Copy, Debug, Eq, PartialEq, ToComputedValue)]
         #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
         pub enum AutoFlow {
             Row,
             Column,
         }
 
-        #[derive(Clone, Copy, Debug, Eq, PartialEq)]
+        #[derive(Clone, Copy, Debug, Eq, PartialEq, ToComputedValue)]
         #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
         pub struct T {
             pub autoflow: AutoFlow,
             pub dense: bool,
         }
     }
 
-    impl ComputedValueAsSpecified for SpecifiedValue {}
-
     impl ToCss for computed_value::T {
         fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
             dest.write_str(match self.autoflow {
                 computed_value::AutoFlow::Column => "column",
                 computed_value::AutoFlow::Row => "row"
             })?;
 
             if self.dense { dest.write_str(" dense")?; }
@@ -416,17 +413,16 @@ macro_rules! impl_align_conversions {
         products="gecko"
         animation_value_type="discrete"
         boxed="True">
     use hash::HashMap;
     use std::fmt;
     use std::ops::Range;
     use str::HTML_SPACE_CHARACTERS;
     use style_traits::ToCss;
-    use values::computed::ComputedValueAsSpecified;
 
     pub mod computed_value {
         pub use super::SpecifiedValue as T;
     }
 
     pub type SpecifiedValue = Either<TemplateAreas, None_>;
 
     #[inline]
@@ -448,21 +444,23 @@ macro_rules! impl_align_conversions {
 
     #[derive(Clone, Debug, PartialEq)]
     pub struct NamedArea {
         pub name: Box<str>,
         pub rows: Range<u32>,
         pub columns: Range<u32>,
     }
 
-    impl ComputedValueAsSpecified for TemplateAreas {}
+    trivial_to_computed_value!(TemplateAreas);
 
     impl Parse for TemplateAreas {
-        fn parse<'i, 't>(_context: &ParserContext, input: &mut Parser<'i, 't>)
-                         -> Result<Self, ParseError<'i>> {
+        fn parse<'i, 't>(
+            _context: &ParserContext,
+            input: &mut Parser<'i, 't>,
+        ) -> Result<Self, ParseError<'i>> {
             let mut strings = vec![];
             while let Ok(string) = input.try(|i| i.expect_string().map(|s| s.as_ref().into())) {
                 strings.push(string);
             }
 
             TemplateAreas::from_vec(strings)
                 .map_err(|()| StyleParseError::UnspecifiedError.into())
         }
--- a/servo/components/style/properties/longhand/table.mako.rs
+++ b/servo/components/style/properties/longhand/table.mako.rs
@@ -9,38 +9,37 @@
 ${helpers.single_keyword("table-layout", "auto fixed",
                          gecko_ffi_name="mLayoutStrategy", animation_value_type="discrete",
                          spec="https://drafts.csswg.org/css-tables/#propdef-table-layout")}
 
 <%helpers:longhand name="-x-span" products="gecko"
                    spec="Internal-only (for `<col span>` pres attr)"
                    animation_value_type="none"
                    internal="True">
-    use values::computed::ComputedValueAsSpecified;
-
-    impl ComputedValueAsSpecified for SpecifiedValue {}
     pub type SpecifiedValue = computed_value::T;
     pub mod computed_value {
         use std::fmt;
         use style_traits::ToCss;
 
-        #[derive(Clone, Copy, Debug, PartialEq)]
+        #[derive(Clone, Copy, Debug, PartialEq, ToComputedValue)]
         #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
         pub struct T(pub i32);
 
         impl ToCss for T {
             fn to_css<W>(&self, _: &mut W) -> fmt::Result where W: fmt::Write {
                 Ok(())
             }
         }
     }
 
     #[inline]
     pub fn get_initial_value() -> computed_value::T {
         computed_value::T(1)
     }
 
     // never parse it, only set via presentation attribute
-    fn parse<'i, 't>(_: &ParserContext, _: &mut Parser<'i, 't>)
-                     -> Result<SpecifiedValue, ParseError<'i>> {
+    fn parse<'i, 't>(
+        _: &ParserContext,
+        _: &mut Parser<'i, 't>,
+    ) -> Result<SpecifiedValue, ParseError<'i>> {
         Err(StyleParseError::UnspecifiedError.into())
     }
 </%helpers:longhand>
--- a/servo/components/style/properties/shorthand/position.mako.rs
+++ b/servo/components/style/properties/shorthand/position.mako.rs
@@ -142,17 +142,17 @@
     // the other. This might not be a big deal for now, but we should consider looking into this in the future
     // to limit the amount of code generated.
     pub fn parse_value<'i, 't>(context: &ParserContext, input: &mut Parser<'i, 't>)
                                -> Result<Longhands, ParseError<'i>> {
         let start = input.try(|i| GridLine::parse(context, i))?;
         let end = if input.try(|i| i.expect_delim('/')).is_ok() {
             GridLine::parse(context, input)?
         } else {
-            let mut line = GridLine::default();
+            let mut line = GridLine::auto();
             if start.line_num.is_none() && !start.is_span {
                 line.ident = start.ident.clone();       // ident from start value should be taken
             }
 
             line
         };
 
         Ok(expanded! {
@@ -177,17 +177,17 @@
                     products="gecko">
     use values::specified::GridLine;
     use parser::Parse;
 
     // The code is the same as `grid-{row,column}` except that this can have four values at most.
     pub fn parse_value<'i, 't>(context: &ParserContext, input: &mut Parser<'i, 't>)
                                -> Result<Longhands, ParseError<'i>> {
         fn line_with_ident_from(other: &GridLine) -> GridLine {
-            let mut this = GridLine::default();
+            let mut this = GridLine::auto();
             if other.line_num.is_none() && !other.is_span {
                 this.ident = other.ident.clone();
             }
 
             this
         }
 
         let row_start = input.try(|i| GridLine::parse(context, i))?;
--- a/servo/components/style/values/computed/align.rs
+++ b/servo/components/style/values/computed/align.rs
@@ -3,17 +3,17 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 //! Values for CSS Box Alignment properties
 //!
 //! https://drafts.csswg.org/css-align/
 
 use std::fmt;
 use style_traits::ToCss;
-use values::computed::{ComputedValueAsSpecified, Context, ToComputedValue};
+use values::computed::{Context, ToComputedValue};
 use values::specified;
 
 pub use super::specified::{AlignItems, AlignJustifyContent, AlignJustifySelf};
 
 /// The computed value for the `justify-items` property.
 ///
 /// Need to carry around both the specified and computed value to handle the
 /// special legacy keyword. Sigh.
@@ -64,12 +64,8 @@ impl ToComputedValue for specified::Just
         JustifyItems { specified, computed }
     }
 
     #[inline]
     fn from_computed_value(computed: &JustifyItems) -> Self {
         computed.specified
     }
 }
-
-impl ComputedValueAsSpecified for AlignItems {}
-impl ComputedValueAsSpecified for AlignJustifyContent {}
-impl ComputedValueAsSpecified for AlignJustifySelf {}
--- a/servo/components/style/values/computed/mod.rs
+++ b/servo/components/style/values/computed/mod.rs
@@ -16,19 +16,19 @@ use properties::{ComputedValues, StyleBu
 use servo_url::ServoUrl;
 use std::f32;
 use std::fmt;
 #[cfg(feature = "servo")]
 use std::sync::Arc;
 use style_traits::ToCss;
 use super::{CSSFloat, CSSInteger};
 use super::generics::{GreaterThanOrEqualToOne, NonNegative};
-use super::generics::grid::{TrackBreadth as GenericTrackBreadth, TrackSize as GenericTrackSize};
+use super::generics::grid::{GridLine as GenericGridLine, TrackBreadth as GenericTrackBreadth};
+use super::generics::grid::{TrackSize as GenericTrackSize, TrackList as GenericTrackList};
 use super::generics::grid::GridTemplateComponent as GenericGridTemplateComponent;
-use super::generics::grid::TrackList as GenericTrackList;
 use super::specified;
 
 pub use app_units::Au;
 pub use properties::animated_properties::TransitionProperty;
 #[cfg(feature = "gecko")]
 pub use self::align::{AlignItems, AlignJustifyContent, AlignJustifySelf, JustifyItems};
 pub use self::angle::Angle;
 pub use self::background::BackgroundSize;
@@ -39,17 +39,16 @@ pub use self::color::{Color, ColorProper
 pub use self::effects::{BoxShadow, Filter, SimpleShadow};
 pub use self::flex::FlexBasis;
 pub use self::image::{Gradient, GradientItem, Image, ImageLayer, LineDirection, MozImageRect};
 #[cfg(feature = "gecko")]
 pub use self::gecko::ScrollSnapPoint;
 pub use self::rect::LengthOrNumberRect;
 pub use super::{Auto, Either, None_};
 pub use super::specified::BorderStyle;
-pub use super::generics::grid::GridLine;
 pub use self::length::{CalcLengthOrPercentage, Length, LengthOrNone, LengthOrNumber, LengthOrPercentage};
 pub use self::length::{LengthOrPercentageOrAuto, LengthOrPercentageOrNone, MaxLength, MozLength};
 pub use self::length::NonNegativeLengthOrPercentage;
 pub use self::percentage::Percentage;
 pub use self::position::Position;
 pub use self::svg::{SVGLength, SVGOpacity, SVGPaint, SVGPaintKind, SVGStrokeDashArray, SVGWidth};
 pub use self::text::{InitialLetter, LetterSpacing, LineHeight, WordSpacing};
 pub use self::time::Time;
@@ -328,21 +327,23 @@ impl<T> ToComputedValue for T
     }
 
     #[inline]
     fn from_computed_value(computed: &T) -> Self {
         computed.clone()
     }
 }
 
-impl ComputedValueAsSpecified for Atom {}
-impl ComputedValueAsSpecified for bool {}
-impl ComputedValueAsSpecified for f32 {}
-
-impl ComputedValueAsSpecified for specified::BorderStyle {}
+trivial_to_computed_value!(Atom);
+trivial_to_computed_value!(u8);
+trivial_to_computed_value!(u16);
+trivial_to_computed_value!(bool);
+trivial_to_computed_value!(i32);
+trivial_to_computed_value!(f32);
+trivial_to_computed_value!(BorderStyle);
 
 /// A `<number>` value.
 pub type Number = CSSFloat;
 
 /// A wrapper of Number, but the value >= 0.
 pub type NonNegativeNumber = NonNegative<CSSFloat>;
 
 impl From<CSSFloat> for NonNegativeNumber {
@@ -496,20 +497,23 @@ pub type ClipRectOrAuto = Either<ClipRec
 /// The computed value of a grid `<track-breadth>`
 pub type TrackBreadth = GenericTrackBreadth<LengthOrPercentage>;
 
 /// The computed value of a grid `<track-size>`
 pub type TrackSize = GenericTrackSize<LengthOrPercentage>;
 
 /// The computed value of a grid `<track-list>`
 /// (could also be `<auto-track-list>` or `<explicit-track-list>`)
-pub type TrackList = GenericTrackList<LengthOrPercentage>;
+pub type TrackList = GenericTrackList<LengthOrPercentage, Integer>;
+
+/// The computed value of a `<grid-line>`.
+pub type GridLine = GenericGridLine<Integer>;
 
 /// `<grid-template-rows> | <grid-template-columns>`
-pub type GridTemplateComponent = GenericGridTemplateComponent<LengthOrPercentage>;
+pub type GridTemplateComponent = GenericGridTemplateComponent<LengthOrPercentage, Integer>;
 
 impl ClipRectOrAuto {
     /// Return an auto (default for clip-rect and image-region) value
     pub fn auto() -> Self {
         Either::Second(Auto)
     }
 
     /// Check if it is auto
--- a/servo/components/style/values/generics/basic_shape.rs
+++ b/servo/components/style/values/generics/basic_shape.rs
@@ -3,36 +3,34 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 //! CSS handling for the [`basic-shape`](https://drafts.csswg.org/css-shapes/#typedef-basic-shape)
 //! types that are generic over their `ToCss` implementations.
 
 use std::fmt;
 use style_traits::ToCss;
 use values::animated::{Animate, Procedure, ToAnimatedZero};
-use values::computed::ComputedValueAsSpecified;
 use values::distance::{ComputeSquaredDistance, SquaredDistance};
 use values::generics::border::BorderRadius;
 use values::generics::position::Position;
 use values::generics::rect::Rect;
 
 /// A clipping shape, for `clip-path`.
 pub type ClippingShape<BasicShape, Url> = ShapeSource<BasicShape, GeometryBox, Url>;
 
 /// https://drafts.fxtf.org/css-masking-1/#typedef-geometry-box
 #[allow(missing_docs)]
 #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
-#[derive(Clone, Copy, Debug, PartialEq, ToCss)]
+#[derive(Clone, Copy, Debug, PartialEq, ToComputedValue, ToCss)]
 pub enum GeometryBox {
     FillBox,
     StrokeBox,
     ViewBox,
     ShapeBox(ShapeBox),
 }
-impl ComputedValueAsSpecified for GeometryBox {}
 
 /// A float area shape, for `shape-outside`.
 pub type FloatAreaShape<BasicShape, Url> = ShapeSource<BasicShape, ShapeBox, Url>;
 
 // https://drafts.csswg.org/css-shapes-1/#typedef-shape-box
 define_css_keyword_enum!(ShapeBox:
     "margin-box" => MarginBox,
     "border-box" => BorderBox,
--- a/servo/components/style/values/generics/grid.rs
+++ b/servo/components/style/values/generics/grid.rs
@@ -5,80 +5,82 @@
 //! Generic types for the handling of
 //! [grids](https://drafts.csswg.org/css-grid/).
 
 use cssparser::Parser;
 use parser::{Parse, ParserContext};
 use std::{fmt, mem, usize};
 use style_traits::{ToCss, ParseError, StyleParseError};
 use values::{CSSFloat, CustomIdent, serialize_dimension};
-use values::computed::{ComputedValueAsSpecified, Context, ToComputedValue};
-use values::specified::Integer;
+use values::computed::{Context, ToComputedValue};
+use values::specified;
 use values::specified::grid::parse_line_names;
 
-#[derive(Clone, Debug, PartialEq)]
+#[derive(Clone, Debug, Default, PartialEq, ToComputedValue)]
 #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
 /// A `<grid-line>` type.
 ///
 /// https://drafts.csswg.org/css-grid/#typedef-grid-row-start-grid-line
-pub struct GridLine {
+pub struct GridLine<Integer> {
     /// Flag to check whether it's a `span` keyword.
     pub is_span: bool,
     /// A custom identifier for named lines.
     ///
     /// https://drafts.csswg.org/css-grid/#grid-placement-slot
     pub ident: Option<CustomIdent>,
     /// Denotes the nth grid line from grid item's placement.
     pub line_num: Option<Integer>,
 }
 
-impl GridLine {
+impl<Integer> GridLine<Integer> {
+    /// The `auto` value.
+    pub fn auto() -> Self {
+        Self {
+            is_span: false,
+            line_num: None,
+            ident: None,
+        }
+    }
+
     /// Check whether this `<grid-line>` represents an `auto` value.
     pub fn is_auto(&self) -> bool {
         self.ident.is_none() && self.line_num.is_none() && !self.is_span
     }
 }
 
-impl Default for GridLine {
-    fn default() -> Self {
-        GridLine {
-            is_span: false,
-            ident: None,
-            line_num: None,
-        }
-    }
-}
-
-impl ToCss for GridLine {
+impl<Integer> ToCss for GridLine<Integer>
+where
+    Integer: ToCss,
+{
     fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
         if self.is_auto() {
             return dest.write_str("auto")
         }
 
         if self.is_span {
             dest.write_str("span")?;
         }
 
-        if let Some(i) = self.line_num {
+        if let Some(ref i) = self.line_num {
             dest.write_str(" ")?;
-            i.value().to_css(dest)?;
+            i.to_css(dest)?;
         }
 
         if let Some(ref s) = self.ident {
             dest.write_str(" ")?;
             s.to_css(dest)?;
         }
 
         Ok(())
     }
 }
 
-impl Parse for GridLine {
+impl Parse for GridLine<specified::Integer> {
     fn parse<'i, 't>(context: &ParserContext, input: &mut Parser<'i, 't>) -> Result<Self, ParseError<'i>> {
-        let mut grid_line = Default::default();
+        let mut grid_line = Self::auto();
         if input.try(|i| i.expect_ident_matching("auto")).is_ok() {
             return Ok(grid_line)
         }
 
         // <custom-ident> | [ <integer> && <custom-ident>? ] | [ span && [ <integer> || <custom-ident> ] ]
         // This <grid-line> horror is simply,
         // [ span? && [ <custom-ident> || <integer> ] ]
         // And, for some magical reason, "span" should be the first or last value and not in-between.
@@ -90,17 +92,18 @@ impl Parse for GridLine {
                     return Err(StyleParseError::UnspecifiedError.into())
                 }
 
                 if grid_line.line_num.is_some() || grid_line.ident.is_some() {
                     val_before_span = true;
                 }
 
                 grid_line.is_span = true;
-            } else if let Ok(i) = input.try(|i| Integer::parse(context, i)) {
+            } else if let Ok(i) = input.try(|i| specified::Integer::parse(context, i)) {
+                // FIXME(emilio): Probably shouldn't reject if it's calc()...
                 if i.value() == 0 || val_before_span || grid_line.line_num.is_some() {
                     return Err(StyleParseError::UnspecifiedError.into())
                 }
 
                 grid_line.line_num = Some(i);
             } else if let Ok(name) = input.try(|i| i.expect_ident_cloned()) {
                 if val_before_span || grid_line.ident.is_some() {
                     return Err(StyleParseError::UnspecifiedError.into());
@@ -124,24 +127,22 @@ impl Parse for GridLine {
                 return Err(StyleParseError::UnspecifiedError.into())
             }
         }
 
         Ok(grid_line)
     }
 }
 
-impl ComputedValueAsSpecified for GridLine {}
-
 define_css_keyword_enum!{ TrackKeyword:
     "auto" => Auto,
     "max-content" => MaxContent,
     "min-content" => MinContent
 }
-impl ComputedValueAsSpecified for TrackKeyword {}
+add_impls_for_keyword_enum!(TrackKeyword);
 
 /// A track breadth for explicit grid track sizing. It's generic solely to
 /// avoid re-implementing it for the computed type.
 ///
 /// https://drafts.csswg.org/css-grid/#typedef-track-breadth
 #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
 #[derive(Clone, Debug, PartialEq, ToComputedValue)]
 pub enum TrackBreadth<L> {
@@ -311,19 +312,25 @@ impl<L: ToComputedValue> ToComputedValue
                 TrackSize::FitContent(ToComputedValue::from_computed_value(lop))
             },
         }
     }
 }
 
 /// Helper function for serializing identifiers with a prefix and suffix, used
 /// for serializing <line-names> (in grid).
-pub fn concat_serialize_idents<W>(prefix: &str, suffix: &str,
-                                  slice: &[CustomIdent], sep: &str, dest: &mut W) -> fmt::Result
-    where W: fmt::Write
+pub fn concat_serialize_idents<W>(
+    prefix: &str,
+    suffix: &str,
+    slice: &[CustomIdent],
+    sep: &str,
+    dest: &mut W,
+) -> fmt::Result
+where
+    W: fmt::Write
 {
     if let Some((ref first, rest)) = slice.split_first() {
         dest.write_str(prefix)?;
         first.to_css(dest)?;
         for thing in rest {
             dest.write_str(sep)?;
             thing.to_css(dest)?;
         }
@@ -333,71 +340,65 @@ pub fn concat_serialize_idents<W>(prefix
 
     Ok(())
 }
 
 /// The initial argument of the `repeat` function.
 ///
 /// https://drafts.csswg.org/css-grid/#typedef-track-repeat
 #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
-#[derive(Clone, Copy, Debug, PartialEq, ToCss)]
-pub enum RepeatCount {
+#[derive(Clone, Copy, Debug, PartialEq, ToComputedValue, ToCss)]
+pub enum RepeatCount<Integer> {
     /// A positive integer. This is allowed only for `<track-repeat>` and `<fixed-repeat>`
     Number(Integer),
     /// An `<auto-fill>` keyword allowed only for `<auto-repeat>`
     AutoFill,
     /// An `<auto-fit>` keyword allowed only for `<auto-repeat>`
     AutoFit,
 }
 
-impl Parse for RepeatCount {
+impl Parse for RepeatCount<specified::Integer> {
     fn parse<'i, 't>(context: &ParserContext, input: &mut Parser<'i, 't>) -> Result<Self, ParseError<'i>> {
         // Maximum number of repeat is 10000. The greater numbers should be clamped.
         const MAX_LINE: i32 = 10000;
-        if let Ok(mut i) = input.try(|i| Integer::parse(context, i)) {
-            if i.value() > 0 {
-                if i.value() > MAX_LINE {
-                    i = Integer::new(MAX_LINE);
-                }
-                Ok(RepeatCount::Number(i))
-            } else {
-                Err(StyleParseError::UnspecifiedError.into())
+        if let Ok(mut i) = input.try(|i| specified::Integer::parse_positive(context, i)) {
+            if i.value() > MAX_LINE {
+                i = specified::Integer::new(MAX_LINE);
             }
+            Ok(RepeatCount::Number(i))
         } else {
             try_match_ident_ignore_ascii_case! { input.expect_ident()?,
                 "auto-fill" => Ok(RepeatCount::AutoFill),
                 "auto-fit" => Ok(RepeatCount::AutoFit),
             }
         }
     }
 }
 
-impl ComputedValueAsSpecified for RepeatCount {}
-
 /// The structure containing `<line-names>` and `<track-size>` values.
 ///
 /// It can also hold `repeat()` function parameters, which expands into the respective
 /// values in its computed form.
 #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
 #[derive(Clone, Debug, PartialEq, ToComputedValue)]
-pub struct TrackRepeat<L> {
+pub struct TrackRepeat<L, I> {
     /// The number of times for the value to be repeated (could also be `auto-fit` or `auto-fill`)
-    pub count: RepeatCount,
+    pub count: RepeatCount<I>,
     /// `<line-names>` accompanying `<track_size>` values.
     ///
     /// If there's no `<line-names>`, then it's represented by an empty vector.
     /// For N `<track-size>` values, there will be N+1 `<line-names>`, and so this vector's
     /// length is always one value more than that of the `<track-size>`.
     #[compute(clone)]
     pub line_names: Box<[Box<[CustomIdent]>]>,
     /// `<track-size>` values.
     pub track_sizes: Vec<TrackSize<L>>,
 }
 
-impl<L: ToCss> ToCss for TrackRepeat<L> {
+impl<L: ToCss, I: ToCss> ToCss for TrackRepeat<L, I> {
     fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
         dest.write_str("repeat(")?;
         self.count.to_css(dest)?;
         dest.write_str(", ")?;
 
         let mut line_names_iter = self.line_names.iter();
         for (i, (ref size, ref names)) in self.track_sizes.iter()
                                               .zip(&mut line_names_iter).enumerate() {
@@ -413,17 +414,17 @@ impl<L: ToCss> ToCss for TrackRepeat<L> 
             concat_serialize_idents(" [", "]", line_names_last, " ", dest)?;
         }
 
         dest.write_str(")")?;
 
         Ok(())
     }
 }
-impl<L: Clone> TrackRepeat<L> {
+impl<L: Clone> TrackRepeat<L, specified::Integer> {
     /// If the repeat count is numeric, then expand the values and merge accordingly.
     pub fn expand(&self) -> Self {
         if let RepeatCount::Number(num) = self.count {
             let mut line_names = vec![];
             let mut track_sizes = vec![];
             let mut prev_names = vec![];
 
             for _ in 0..num.value() {
@@ -455,27 +456,27 @@ impl<L: Clone> TrackRepeat<L> {
             }
         }
     }
 }
 
 /// Track list values. Can be <track-size> or <track-repeat>
 #[derive(Clone, Debug, PartialEq, ToComputedValue, ToCss)]
 #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
-pub enum TrackListValue<T> {
+pub enum TrackListValue<LengthOrPercentage, Integer> {
     /// A <track-size> value.
-    TrackSize(TrackSize<T>),
+    TrackSize(TrackSize<LengthOrPercentage>),
     /// A <track-repeat> value.
-    TrackRepeat(TrackRepeat<T>),
+    TrackRepeat(TrackRepeat<LengthOrPercentage, Integer>),
 }
 
 /// The type of a `<track-list>` as determined during parsing.
 ///
 /// https://drafts.csswg.org/css-grid/#typedef-track-list
-#[derive(Clone, Copy, Debug, PartialEq)]
+#[derive(Clone, Copy, Debug, PartialEq, ToComputedValue)]
 #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
 pub enum TrackListType {
     /// [`<auto-track-list>`](https://drafts.csswg.org/css-grid/#typedef-auto-track-list)
     ///
     /// If this type exists, then the value at the index in `line_names` field in `TrackList`
     /// has the `<line-names>?` list that comes before `<auto-repeat>`. If it's a specified value,
     /// then the `repeat()` function (that follows the line names list) is also at the given index
     /// in `values` field. On the contrary, if it's a computed value, then the `repeat()` function
@@ -485,42 +486,40 @@ pub enum TrackListType {
     Normal,
     /// [`<explicit-track-list>`](https://drafts.csswg.org/css-grid/#typedef-explicit-track-list)
     ///
     /// Note that this is a subset of the normal `<track-list>`, and so it could be used in place
     /// of the latter.
     Explicit,
 }
 
-impl ComputedValueAsSpecified for TrackListType {}
-
 /// A grid `<track-list>` type.
 ///
 /// https://drafts.csswg.org/css-grid/#typedef-track-list
 #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
 #[derive(Clone, Debug, PartialEq)]
-pub struct TrackList<T> {
+pub struct TrackList<LengthOrPercentage, Integer> {
     /// The type of this `<track-list>` (auto, explicit or general).
     ///
     /// In order to avoid parsing the same value multiple times, this does a single traversal
     /// and arrives at the type of value it has parsed (or bails out gracefully with an error).
     pub list_type: TrackListType,
     /// A vector of `<track-size> | <track-repeat>` values.
-    pub values: Vec<TrackListValue<T>>,
+    pub values: Vec<TrackListValue<LengthOrPercentage, Integer>>,
     /// `<line-names>` accompanying `<track-size> | <track-repeat>` values.
     ///
     /// If there's no `<line-names>`, then it's represented by an empty vector.
     /// For N values, there will be N+1 `<line-names>`, and so this vector's
     /// length is always one value more than that of the `<track-size>`.
     pub line_names: Box<[Box<[CustomIdent]>]>,
     /// `<auto-repeat>` value. There can only be one `<auto-repeat>` in a TrackList.
-    pub auto_repeat: Option<TrackRepeat<T>>,
+    pub auto_repeat: Option<TrackRepeat<LengthOrPercentage, Integer>>,
 }
 
-impl<T: ToCss> ToCss for TrackList<T> {
+impl<L: ToCss, I: ToCss> ToCss for TrackList<L, I> {
     fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
         let auto_idx = match self.list_type {
             TrackListType::Auto(i) => i as usize,
             _ => usize::MAX,
         };
 
         let mut values_iter = self.values.iter().peekable();
         let mut line_names_iter = self.line_names.iter().peekable();
@@ -567,16 +566,18 @@ impl<T: ToCss> ToCss for TrackList<T> {
 #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
 pub struct LineNameList {
     /// The optional `<line-name-list>`
     pub names: Box<[Box<[CustomIdent]>]>,
     /// Indicates the line name that requires `auto-fill`
     pub fill_idx: Option<u32>,
 }
 
+trivial_to_computed_value!(LineNameList);
+
 impl Parse for LineNameList {
     fn parse<'i, 't>(context: &ParserContext, input: &mut Parser<'i, 't>) -> Result<Self, ParseError<'i>> {
         input.expect_ident_matching("subgrid")?;
         let mut line_names = vec![];
         let mut fill_idx = None;
 
         loop {
             let repeat_parse_result = input.try(|input| {
@@ -649,33 +650,31 @@ impl ToCss for LineNameList {
                 dest.write_str(")")?;
             }
         }
 
         Ok(())
     }
 }
 
-impl ComputedValueAsSpecified for LineNameList {}
-
 /// Variants for `<grid-template-rows> | <grid-template-columns>`
 /// Subgrid deferred to Level 2 spec due to lack of implementation.
 /// But it's implemented in gecko, so we have to as well.
 #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
 #[derive(Clone, Debug, PartialEq, ToComputedValue, ToCss)]
-pub enum GridTemplateComponent<L> {
+pub enum GridTemplateComponent<L, I> {
     /// `none` value.
     None,
     /// The grid `<track-list>`
-    TrackList(TrackList<L>),
+    TrackList(TrackList<L, I>),
     /// A `subgrid <line-name-list>?`
     Subgrid(LineNameList),
 }
 
-impl<L> GridTemplateComponent<L> {
+impl<L, I> GridTemplateComponent<L, I> {
     /// Returns length of the <track-list>s <track-size>
     pub fn track_list_len(&self) -> usize {
         match *self {
             GridTemplateComponent::TrackList(ref tracklist) => tracklist.values.len(),
             _ => 0,
         }
     }
 }
--- a/servo/components/style/values/generics/image.rs
+++ b/servo/components/style/values/generics/image.rs
@@ -3,20 +3,20 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 //! Generic types for the handling of [images].
 //!
 //! [images]: https://drafts.csswg.org/css-images/#image-values
 
 use Atom;
 use cssparser::serialize_identifier;
-use custom_properties::SpecifiedValue;
+use custom_properties;
 use std::fmt;
 use style_traits::ToCss;
-use values::computed::{ComputedValueAsSpecified, Context, ToComputedValue};
+use values::computed::{Context, ToComputedValue};
 
 /// An [image].
 ///
 /// [image]: https://drafts.csswg.org/css-images/#image-values
 #[derive(Clone, PartialEq)]
 #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
 pub enum Image<Gradient, MozImageRect, ImageUrl> {
     /// A `<url()>` image.
@@ -148,17 +148,17 @@ pub enum Ellipse<LengthOrPercentage> {
 define_css_keyword_enum!(ShapeExtent:
     "closest-side" => ClosestSide,
     "farthest-side" => FarthestSide,
     "closest-corner" => ClosestCorner,
     "farthest-corner" => FarthestCorner,
     "contain" => Contain,
     "cover" => Cover
 );
-impl ComputedValueAsSpecified for ShapeExtent {}
+add_impls_for_keyword_enum!(ShapeExtent);
 
 /// A gradient item.
 /// https://drafts.csswg.org/css-images-4/#color-stop-syntax
 #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
 #[derive(Clone, Copy, Debug, PartialEq, ToComputedValue, ToCss)]
 pub enum GradientItem<Color, LengthOrPercentage> {
     /// A color stop.
     ColorStop(ColorStop<Color, LengthOrPercentage>),
@@ -181,20 +181,20 @@ pub struct ColorStop<Color, LengthOrPerc
 /// https://drafts.css-houdini.org/css-paint-api/
 #[derive(Clone, Debug, PartialEq)]
 #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
 pub struct PaintWorklet {
     /// The name the worklet was registered with.
     pub name: Atom,
     /// The arguments for the worklet.
     /// TODO: store a parsed representation of the arguments.
-    pub arguments: Vec<SpecifiedValue>,
+    pub arguments: Vec<custom_properties::SpecifiedValue>,
 }
 
-impl ComputedValueAsSpecified for PaintWorklet {}
+trivial_to_computed_value!(PaintWorklet);
 
 impl ToCss for PaintWorklet {
     fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
         dest.write_str("paint(")?;
         serialize_identifier(&*self.name.to_string(), dest)?;
         for argument in &self.arguments {
             dest.write_str(", ")?;
             argument.to_css(dest)?;
--- a/servo/components/style/values/mod.rs
+++ b/servo/components/style/values/mod.rs
@@ -93,17 +93,17 @@ impl<A: Parse, B: Parse> Parse for Eithe
             Ok(Either::First(v))
         } else {
             B::parse(context, input).map(Either::Second)
         }
     }
 }
 
 /// https://drafts.csswg.org/css-values-4/#custom-idents
-#[derive(Clone, Debug, Eq, Hash, PartialEq)]
+#[derive(Clone, Debug, Eq, Hash, PartialEq, ToComputedValue)]
 #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
 pub struct CustomIdent(pub Atom);
 
 impl CustomIdent {
     /// Parse an already-tokenizer identifier
     pub fn from_ident<'i>(ident: &CowRcStr<'i>, excluding: &[&str]) -> Result<Self, ParseError<'i>> {
         let valid = match_ignore_ascii_case! { ident,
             "initial" | "inherit" | "unset" | "default" => false,
@@ -122,17 +122,17 @@ impl CustomIdent {
 
 impl ToCss for CustomIdent {
     fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
         serialize_identifier(&self.0.to_string(), dest)
     }
 }
 
 /// https://drafts.csswg.org/css-animations/#typedef-keyframes-name
-#[derive(Clone, Debug)]
+#[derive(Clone, Debug, ToComputedValue)]
 #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
 pub enum KeyframesName {
     /// <custom-ident>
     Ident(CustomIdent),
     /// <string>
     QuotedString(Atom),
 }
 
--- a/servo/components/style/values/specified/align.rs
+++ b/servo/components/style/values/specified/align.rs
@@ -13,16 +13,17 @@ use selectors::parser::SelectorParseErro
 use std::ascii::AsciiExt;
 use std::fmt;
 use style_traits::{ToCss, ParseError, StyleParseError};
 
 bitflags! {
     /// Constants shared by multiple CSS Box Alignment properties
     ///
     /// These constants match Gecko's `NS_STYLE_ALIGN_*` constants.
+    #[derive(ToComputedValue)]
     pub flags AlignFlags: u8 {
         // Enumeration stored in the lower 5 bits:
         /// 'auto'
         const ALIGN_AUTO =            structs::NS_STYLE_ALIGN_AUTO as u8,
         /// 'normal'
         const ALIGN_NORMAL =          structs::NS_STYLE_ALIGN_NORMAL as u8,
         /// 'start'
         const ALIGN_START =           structs::NS_STYLE_ALIGN_START as u8,
@@ -108,17 +109,17 @@ const ALIGN_ALL_BITS: u16 = structs::NS_
 const ALIGN_ALL_SHIFT: u32 = structs::NS_STYLE_ALIGN_ALL_SHIFT;
 
 /// Value of the `align-content` or `justify-content` property.
 ///
 /// https://drafts.csswg.org/css-align/#content-distribution
 ///
 /// The 16-bit field stores the primary value in its lower 8 bits, and the optional fallback value
 /// in its upper 8 bits.  This matches the representation of these properties in Gecko.
-#[derive(Clone, Copy, Debug, Eq, PartialEq)]
+#[derive(Clone, Copy, Debug, Eq, PartialEq, ToComputedValue)]
 #[cfg_attr(feature = "servo", derive(HeapSizeOf, Deserialize, Serialize))]
 pub struct AlignJustifyContent(u16);
 
 impl AlignJustifyContent {
     /// The initial value 'normal'
     #[inline]
     pub fn normal() -> Self {
         Self::new(ALIGN_NORMAL)
@@ -200,17 +201,17 @@ impl Parse for AlignJustifyContent {
         }
         Err(StyleParseError::UnspecifiedError.into())
     }
 }
 
 /// Value of the `align-self` or `justify-self` property.
 ///
 /// https://drafts.csswg.org/css-align/#self-alignment
-#[derive(Clone, Copy, Debug, Eq, PartialEq, ToCss)]
+#[derive(Clone, Copy, Debug, Eq, PartialEq, ToComputedValue, ToCss)]
 pub struct AlignJustifySelf(pub AlignFlags);
 
 impl AlignJustifySelf {
     /// The initial value 'auto'
     #[inline]
     pub fn auto() -> Self {
         AlignJustifySelf(ALIGN_AUTO)
     }
@@ -237,17 +238,17 @@ impl Parse for AlignJustifySelf {
         }
         Err(StyleParseError::UnspecifiedError.into())
     }
 }
 
 /// Value of the `align-items` property
 ///
 /// https://drafts.csswg.org/css-align/#self-alignment
-#[derive(Clone, Copy, Debug, Eq, PartialEq, ToCss)]
+#[derive(Clone, Copy, Debug, Eq, PartialEq, ToComputedValue, ToCss)]
 pub struct AlignItems(pub AlignFlags);
 
 impl AlignItems {
     /// The initial value 'normal'
     #[inline]
     pub fn normal() -> Self {
         AlignItems(ALIGN_NORMAL)
     }
--- a/servo/components/style/values/specified/grid.rs
+++ b/servo/components/style/values/specified/grid.rs
@@ -9,17 +9,17 @@ use cssparser::{Parser, Token, BasicPars
 use parser::{Parse, ParserContext};
 use std::ascii::AsciiExt;
 use std::mem;
 use style_traits::{ParseError, StyleParseError};
 use values::{CSSFloat, CustomIdent};
 use values::computed::{self, Context, ToComputedValue};
 use values::generics::grid::{GridTemplateComponent, RepeatCount, TrackBreadth, TrackKeyword, TrackRepeat};
 use values::generics::grid::{LineNameList, TrackSize, TrackList, TrackListType, TrackListValue};
-use values::specified::LengthOrPercentage;
+use values::specified::{LengthOrPercentage, Integer};
 
 /// Parse a single flexible length.
 pub fn parse_flex<'i, 't>(input: &mut Parser<'i, 't>) -> Result<CSSFloat, ParseError<'i>> {
     match *input.next()? {
         Token::Dimension { value, ref unit, .. } if unit.eq_ignore_ascii_case("fr") && value.is_sign_positive()
             => Ok(value),
         ref t => Err(BasicParseError::UnexpectedToken(t.clone()).into()),
     }
@@ -92,19 +92,21 @@ enum RepeatType {
     /// [`<auto-repeat>`](https://drafts.csswg.org/css-grid/#typedef-auto-repeat)
     Auto,
     /// [`<track-repeat>`](https://drafts.csswg.org/css-grid/#typedef-track-repeat)
     Normal,
     /// [`<fixed-repeat>`](https://drafts.csswg.org/css-grid/#typedef-fixed-repeat)
     Fixed,
 }
 
-impl TrackRepeat<LengthOrPercentage> {
-    fn parse_with_repeat_type<'i, 't>(context: &ParserContext, input: &mut Parser<'i, 't>)
-                                      -> Result<(TrackRepeat<LengthOrPercentage>, RepeatType), ParseError<'i>> {
+impl TrackRepeat<LengthOrPercentage, Integer> {
+    fn parse_with_repeat_type<'i, 't>(
+        context: &ParserContext,
+        input: &mut Parser<'i, 't>,
+    ) -> Result<(Self, RepeatType), ParseError<'i>> {
         input.try(|i| i.expect_function_matching("repeat").map_err(|e| e.into())).and_then(|_| {
             input.parse_nested_block(|input| {
                 let count = RepeatCount::parse(context, input)?;
                 input.expect_comma()?;
 
                 let is_auto = count == RepeatCount::AutoFit || count == RepeatCount::AutoFill;
                 let mut repeat_type = if is_auto {
                     RepeatType::Auto
@@ -160,17 +162,17 @@ impl TrackRepeat<LengthOrPercentage> {
                 };
 
                 Ok((repeat, repeat_type))
             })
         })
     }
 }
 
-impl Parse for TrackList<LengthOrPercentage> {
+impl Parse for TrackList<LengthOrPercentage, Integer> {
     fn parse<'i, 't>(context: &ParserContext, input: &mut Parser<'i, 't>) -> Result<Self, ParseError<'i>> {
         let mut current_names = vec![];
         let mut names = vec![];
         let mut values = vec![];
 
         let mut list_type = TrackListType::Explicit;    // assume it's the simplest case
         // holds <auto-repeat> value. It can only be only one in a TrackList.
         let mut auto_repeat = None;
@@ -243,18 +245,18 @@ impl Parse for TrackList<LengthOrPercent
             list_type: list_type,
             values: values,
             line_names: names.into_boxed_slice(),
             auto_repeat: auto_repeat,
         })
     }
 }
 
-impl ToComputedValue for TrackList<LengthOrPercentage> {
-    type ComputedValue = TrackList<computed::LengthOrPercentage>;
+impl ToComputedValue for TrackList<LengthOrPercentage, Integer> {
+    type ComputedValue = TrackList<computed::LengthOrPercentage, computed::Integer>;
 
     #[inline]
     fn to_computed_value(&self, context: &Context) -> Self::ComputedValue {
         // Merge the line names while computing values. The resulting values will
         // all be bunch of `<track-size>` and one <auto-repeat>.
         //
         // For example,
         // `[a b] 100px [c d] repeat(1, 30px [g]) [h]` will be merged as `[a b] 100px [c d] 30px [g h]`
@@ -316,30 +318,32 @@ impl ToComputedValue for TrackList<Lengt
             list_type: computed.list_type,
             values: values,
             line_names: computed.line_names.clone(),
             auto_repeat: computed.auto_repeat.clone().map(|ref repeat| TrackRepeat::from_computed_value(repeat)),
         }
     }
 }
 
-impl Parse for GridTemplateComponent<LengthOrPercentage> {
+impl Parse for GridTemplateComponent<LengthOrPercentage, Integer> {
     // FIXME: Derive Parse (probably with None_)
     fn parse<'i, 't>(context: &ParserContext, input: &mut Parser<'i, 't>) -> Result<Self, ParseError<'i>> {
         if input.try(|i| i.expect_ident_matching("none")).is_ok() {
             return Ok(GridTemplateComponent::None)
         }
 
         Self::parse_without_none(context, input)
     }
 }
 
-impl GridTemplateComponent<LengthOrPercentage> {
+impl GridTemplateComponent<LengthOrPercentage, Integer> {
     /// Parses a `GridTemplateComponent<LengthOrPercentage>` except `none` keyword.
-    pub fn parse_without_none<'i, 't>(context: &ParserContext, input: &mut Parser<'i, 't>)
-                                      -> Result<Self, ParseError<'i>> {
+    pub fn parse_without_none<'i, 't>(
+        context: &ParserContext,
+        input: &mut Parser<'i, 't>,
+    ) -> Result<Self, ParseError<'i>> {
         if let Ok(t) = input.try(|i| TrackList::parse(context, i)) {
             return Ok(GridTemplateComponent::TrackList(t))
         }
 
         LineNameList::parse(context, input).map(GridTemplateComponent::Subgrid)
     }
 }
--- a/servo/components/style/values/specified/mod.rs
+++ b/servo/components/style/values/specified/mod.rs
@@ -14,18 +14,18 @@ use self::url::SpecifiedUrl;
 use std::ascii::AsciiExt;
 use std::f32;
 use std::fmt;
 use style_traits::{ToCss, ParseError, StyleParseError};
 use style_traits::values::specified::AllowedNumericType;
 use super::{Auto, CSSFloat, CSSInteger, Either, None_};
 use super::computed::{Context, ToComputedValue};
 use super::generics::{GreaterThanOrEqualToOne, NonNegative};
-use super::generics::grid::{TrackBreadth as GenericTrackBreadth, TrackSize as GenericTrackSize};
-use super::generics::grid::TrackList as GenericTrackList;
+use super::generics::grid::{GridLine as GenericGridLine, TrackBreadth as GenericTrackBreadth};
+use super::generics::grid::{TrackSize as GenericTrackSize, TrackList as GenericTrackList};
 use values::computed::ComputedValueAsSpecified;
 use values::specified::calc::CalcNode;
 
 pub use properties::animated_properties::TransitionProperty;
 pub use self::angle::Angle;
 #[cfg(feature = "gecko")]
 pub use self::align::{AlignItems, AlignJustifyContent, AlignJustifySelf, JustifyItems};
 pub use self::background::BackgroundSize;
@@ -47,17 +47,16 @@ pub use self::length::{NoCalcLength, Vie
 pub use self::length::NonNegativeLengthOrPercentage;
 pub use self::rect::LengthOrNumberRect;
 pub use self::percentage::Percentage;
 pub use self::position::{Position, PositionComponent};
 pub use self::svg::{SVGLength, SVGOpacity, SVGPaint, SVGPaintKind, SVGStrokeDashArray, SVGWidth};
 pub use self::text::{InitialLetter, LetterSpacing, LineHeight, WordSpacing};
 pub use self::time::Time;
 pub use self::transform::{TimingFunction, TransformOrigin};
-pub use super::generics::grid::GridLine;
 pub use super::generics::grid::GridTemplateComponent as GenericGridTemplateComponent;
 
 #[cfg(feature = "gecko")]
 pub mod align;
 pub mod angle;
 pub mod background;
 pub mod basic_shape;
 pub mod border;
@@ -351,19 +350,21 @@ impl ToComputedValue for Opacity {
     }
 
     #[inline]
     fn from_computed_value(computed: &CSSFloat) -> Self {
         Opacity(Number::from_computed_value(computed))
     }
 }
 
+/// An specified `<integer>`, optionally coming from a `calc()` expression.
+///
+/// https://drafts.csswg.org/css-values/#integers
 #[derive(Clone, Copy, Debug, PartialEq, PartialOrd)]
 #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
-#[allow(missing_docs)]
 pub struct Integer {
     value: CSSInteger,
     was_calc: bool,
 }
 
 impl Integer {
     /// Trivially constructs a new `Integer` value.
     pub fn new(val: CSSInteger) -> Self {
@@ -382,43 +383,54 @@ impl Integer {
     fn from_calc(val: CSSInteger) -> Self {
         Integer {
             value: val,
             was_calc: true,
         }
     }
 }
 
-
 impl Parse for Integer {
     fn parse<'i, 't>(context: &ParserContext, input: &mut Parser<'i, 't>) -> Result<Self, ParseError<'i>> {
         parse_integer(context, input)
     }
 }
 
 impl Integer {
-    #[allow(missing_docs)]
-    pub fn parse_with_minimum<'i, 't>(context: &ParserContext, input: &mut Parser<'i, 't>, min: i32)
-                                      -> Result<Integer, ParseError<'i>> {
+    /// Parse an integer value which is at least `min`.
+    pub fn parse_with_minimum<'i, 't>(
+        context: &ParserContext,
+        input: &mut Parser<'i, 't>,
+        min: i32
+    ) -> Result<Integer, ParseError<'i>> {
         match parse_integer(context, input) {
+            // FIXME(emilio): The spec asks us to avoid rejecting it at parse
+            // time except until computed value time.
+            //
+            // It's not totally clear it's worth it though, and no other browser
+            // does this.
             Ok(value) if value.value() >= min => Ok(value),
             Ok(_value) => Err(StyleParseError::UnspecifiedError.into()),
             Err(e) => Err(e),
         }
     }
 
-    #[allow(missing_docs)]
-    pub fn parse_non_negative<'i, 't>(context: &ParserContext, input: &mut Parser<'i, 't>)
-                                      -> Result<Integer, ParseError<'i>> {
+    /// Parse a non-negative integer.
+    pub fn parse_non_negative<'i, 't>(
+        context: &ParserContext,
+        input: &mut Parser<'i, 't>,
+    ) -> Result<Integer, ParseError<'i>> {
         Integer::parse_with_minimum(context, input, 0)
     }
 
-    #[allow(missing_docs)]
-    pub fn parse_positive<'i, 't>(context: &ParserContext, input: &mut Parser<'i, 't>)
-                                  -> Result<Integer, ParseError<'i>> {
+    /// Parse a positive integer (>= 1).
+    pub fn parse_positive<'i, 't>(
+        context: &ParserContext,
+        input: &mut Parser<'i, 't>
+    ) -> Result<Integer, ParseError<'i>> {
         Integer::parse_with_minimum(context, input, 1)
     }
 }
 
 impl ToComputedValue for Integer {
     type ComputedValue = i32;
 
     #[inline]
@@ -479,20 +491,23 @@ pub type UrlOrNone = Either<SpecifiedUrl
 /// The specified value of a grid `<track-breadth>`
 pub type TrackBreadth = GenericTrackBreadth<LengthOrPercentage>;
 
 /// The specified value of a grid `<track-size>`
 pub type TrackSize = GenericTrackSize<LengthOrPercentage>;
 
 /// The specified value of a grid `<track-list>`
 /// (could also be `<auto-track-list>` or `<explicit-track-list>`)
-pub type TrackList = GenericTrackList<LengthOrPercentage>;
+pub type TrackList = GenericTrackList<LengthOrPercentage, Integer>;
+
+/// The specified value of a `<grid-line>`.
+pub type GridLine = GenericGridLine<Integer>;
 
 /// `<grid-template-rows> | <grid-template-columns>`
-pub type GridTemplateComponent = GenericGridTemplateComponent<LengthOrPercentage>;
+pub type GridTemplateComponent = GenericGridTemplateComponent<LengthOrPercentage, Integer>;
 
 /// <length> | <percentage> | <number>
 pub type LengthOrPercentageOrNumber = Either<Number, LengthOrPercentage>;
 
 /// NonNegativeLengthOrPercentage | NonNegativeNumber
 pub type NonNegativeLengthOrPercentageOrNumber = Either<NonNegativeNumber, NonNegativeLengthOrPercentage>;
 
 #[derive(Clone, Debug, PartialEq)]