servo: Merge #16960 - Derive HasViewportPercentage 🍷 (from servo:derive-all-the-things); r=emilio
authorAnthony Ramine <n.oxyde@gmail.com>
Sat, 20 May 2017 11:34:36 -0500
changeset 407891 f30e76c54839cc7c7635be21c104580fb593602d
parent 407890 a6095408fcedfa091919bf354600928e98c259fa
child 407892 ed2b154ae61ff25f42e6b94bb435f9d20ea20a4c
push id7391
push usermtabara@mozilla.com
push dateMon, 12 Jun 2017 13:08:53 +0000
treeherdermozilla-beta@2191d7f87e2e [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 #16960 - Derive HasViewportPercentage 🍷 (from servo:derive-all-the-things); r=emilio Source-Repo: https://github.com/servo/servo Source-Revision: 4f0b24ac0c3bd04104d705898ceb4d06ef5ea092
servo/Cargo.lock
servo/components/style/Cargo.toml
servo/components/style/lib.rs
servo/components/style/macros.rs
servo/components/style/properties/helpers.mako.rs
servo/components/style/properties/longhand/background.mako.rs
servo/components/style/properties/longhand/border.mako.rs
servo/components/style/properties/longhand/box.mako.rs
servo/components/style/properties/longhand/color.mako.rs
servo/components/style/properties/longhand/counters.mako.rs
servo/components/style/properties/longhand/effects.mako.rs
servo/components/style/properties/longhand/font.mako.rs
servo/components/style/properties/longhand/inherited_box.mako.rs
servo/components/style/properties/longhand/inherited_svg.mako.rs
servo/components/style/properties/longhand/inherited_table.mako.rs
servo/components/style/properties/longhand/inherited_text.mako.rs
servo/components/style/properties/longhand/list.mako.rs
servo/components/style/properties/longhand/outline.mako.rs
servo/components/style/properties/longhand/pointing.mako.rs
servo/components/style/properties/longhand/position.mako.rs
servo/components/style/properties/longhand/svg.mako.rs
servo/components/style/properties/longhand/table.mako.rs
servo/components/style/properties/longhand/text.mako.rs
servo/components/style/properties/longhand/ui.mako.rs
servo/components/style/values/computed/mod.rs
servo/components/style/values/generics/image.rs
servo/components/style/values/generics/position.rs
servo/components/style/values/mod.rs
servo/components/style/values/specified/grid.rs
servo/components/style/values/specified/image.rs
servo/components/style/values/specified/length.rs
servo/components/style/values/specified/mod.rs
servo/components/style_derive/Cargo.toml
servo/components/style_derive/has_viewport_percentage.rs
servo/components/style_derive/lib.rs
servo/components/style_traits/lib.rs
servo/components/style_traits/viewport.rs
servo/tests/unit/style/attr.rs
servo/tests/unit/style/restyle_hints.rs
servo/tests/unit/style/stylist.rs
--- a/servo/Cargo.lock
+++ b/servo/Cargo.lock
@@ -600,17 +600,17 @@ dependencies = [
  "byteorder 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
 [[package]]
 name = "deny_public_fields"
 version = "0.0.1"
 dependencies = [
  "syn 0.11.11 (registry+https://github.com/rust-lang/crates.io-index)",
- "synstructure 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "synstructure 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
 [[package]]
 name = "deque"
 version = "0.3.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 dependencies = [
  "rand 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -1141,17 +1141,17 @@ dependencies = [
 
 [[package]]
 name = "heapsize_derive"
 version = "0.1.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 dependencies = [
  "quote 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)",
  "syn 0.11.11 (registry+https://github.com/rust-lang/crates.io-index)",
- "synstructure 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "synstructure 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
 [[package]]
 name = "heartbeats-simple"
 version = "0.4.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 dependencies = [
  "heartbeats-simple-sys 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -1341,17 +1341,17 @@ dependencies = [
 ]
 
 [[package]]
 name = "jstraceable_derive"
 version = "0.0.1"
 dependencies = [
  "quote 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)",
  "syn 0.11.11 (registry+https://github.com/rust-lang/crates.io-index)",
- "synstructure 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "synstructure 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
 [[package]]
 name = "kernel32-sys"
 version = "0.2.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 dependencies = [
  "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -2855,24 +2855,34 @@ dependencies = [
  "regex 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
  "selectors 0.18.0",
  "serde 0.9.15 (registry+https://github.com/rust-lang/crates.io-index)",
  "serde_derive 0.9.15 (registry+https://github.com/rust-lang/crates.io-index)",
  "servo_atoms 0.0.1",
  "servo_config 0.0.1",
  "servo_url 0.0.1",
  "smallvec 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
+ "style_derive 0.0.1",
  "style_traits 0.0.1",
  "time 0.1.37 (registry+https://github.com/rust-lang/crates.io-index)",
  "toml 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
  "unicode-segmentation 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "walkdir 1.0.7 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
 [[package]]
+name = "style_derive"
+version = "0.0.1"
+dependencies = [
+ "quote 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)",
+ "syn 0.11.11 (registry+https://github.com/rust-lang/crates.io-index)",
+ "synstructure 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
 name = "style_tests"
 version = "0.0.1"
 dependencies = [
  "app_units 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)",
  "byteorder 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "cssparser 0.13.5 (registry+https://github.com/rust-lang/crates.io-index)",
  "euclid 0.11.3 (registry+https://github.com/rust-lang/crates.io-index)",
  "html5ever 0.16.0 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -2941,17 +2951,17 @@ name = "synom"
 version = "0.11.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 dependencies = [
  "unicode-xid 0.0.4 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
 [[package]]
 name = "synstructure"
-version = "0.5.0"
+version = "0.5.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 dependencies = [
  "quote 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)",
  "syn 0.11.11 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
 [[package]]
 name = "syntex"
@@ -3646,17 +3656,17 @@ dependencies = [
 "checksum smallvec 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "4f8266519bc1d17d0b5b16f6c21295625d562841c708f6376f49028a43e9c11e"
 "checksum string_cache 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f55fba06c5e294108f22e8512eb598cb13388a117991e411a8df8f41a1219a75"
 "checksum string_cache_codegen 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "479cde50c3539481f33906a387f2bd17c8e87cb848c35b6021d41fb81ff9b4d7"
 "checksum string_cache_shared 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b1884d1bc09741d466d9b14e6d37ac89d6909cbcac41dd9ae982d4d063bbedfc"
 "checksum strsim 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b4d15c810519a91cf877e7e36e63fe068815c678181439f2f29e2562147c3694"
 "checksum swapper 0.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "3ca610b32bb8bfc5e7f705480c3a1edfeb70b6582495d343872c8bee0dcf758c"
 "checksum syn 0.11.11 (registry+https://github.com/rust-lang/crates.io-index)" = "d3b891b9015c88c576343b9b3e41c2c11a51c219ef067b264bd9c8aa9b441dad"
 "checksum synom 0.11.3 (registry+https://github.com/rust-lang/crates.io-index)" = "a393066ed9010ebaed60b9eafa373d4b1baac186dd7e008555b0f702b51945b6"
-"checksum synstructure 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5ccc9780bf1aa601943988c2876ab22413c01ad1739689aa6af18d0aa0b3f38b"
+"checksum synstructure 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "cf318c34a2f8381a4f3d4db2c91b45bca2b1cd8cbe56caced900647be164800c"
 "checksum syntex 0.58.1 (registry+https://github.com/rust-lang/crates.io-index)" = "a8f5e3aaa79319573d19938ea38d068056b826db9883a5d47f86c1cecc688f0e"
 "checksum syntex_errors 0.58.1 (registry+https://github.com/rust-lang/crates.io-index)" = "867cc5c2d7140ae7eaad2ae9e8bf39cb18a67ca651b7834f88d46ca98faadb9c"
 "checksum syntex_pos 0.58.1 (registry+https://github.com/rust-lang/crates.io-index)" = "13ad4762fe52abc9f4008e85c4fb1b1fe3aa91ccb99ff4826a439c7c598e1047"
 "checksum syntex_syntax 0.58.1 (registry+https://github.com/rust-lang/crates.io-index)" = "6e0e4dbae163dd98989464c23dd503161b338790640e11537686f2ef0f25c791"
 "checksum target_build_utils 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f42dc058080c19c6a58bdd1bf962904ee4f5ef1fe2a81b529f31dacc750c679f"
 "checksum tempdir 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "87974a6f5c1dfb344d733055601650059a3363de2a6104819293baff662132d6"
 "checksum tendril 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)" = "4ce04c250d202db8004921e3d3bc95eaa4f2126c6937a428ae39d12d0e38df62"
 "checksum term 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)" = "d168af3930b369cfe245132550579d47dfd873d69470755a19c2c6568dbbd989"
--- a/servo/components/style/Cargo.toml
+++ b/servo/components/style/Cargo.toml
@@ -58,16 +58,17 @@ pdqsort = "0.1.0"
 precomputed-hash = "0.1"
 rayon = "0.7"
 selectors = { path = "../selectors" }
 serde = {version = "0.9", optional = true}
 serde_derive = {version = "0.9", optional = true}
 servo_atoms = {path = "../atoms", optional = true}
 servo_config = {path = "../config", optional = true}
 smallvec = "0.3.3"
+style_derive = {path = "../style_derive"}
 style_traits = {path = "../style_traits"}
 servo_url = {path = "../url", optional = true}
 time = "0.1"
 unicode-segmentation = "1.0"
 
 [target.'cfg(windows)'.dependencies]
 kernel32-sys = "0.2"
 
--- a/servo/components/style/lib.rs
+++ b/servo/components/style/lib.rs
@@ -74,16 +74,18 @@ extern crate rayon;
 extern crate selectors;
 #[cfg(feature = "servo")] extern crate serde;
 #[cfg(feature = "servo")] #[macro_use] extern crate serde_derive;
 #[cfg(feature = "servo")] #[macro_use] extern crate servo_atoms;
 #[cfg(feature = "servo")] extern crate servo_config;
 #[cfg(feature = "servo")] extern crate servo_url;
 extern crate smallvec;
 #[macro_use]
+extern crate style_derive;
+#[macro_use]
 extern crate style_traits;
 extern crate time;
 #[allow(unused_extern_crates)]
 extern crate unicode_segmentation;
 
 #[macro_use]
 mod macros;
 
--- a/servo/components/style/macros.rs
+++ b/servo/components/style/macros.rs
@@ -33,29 +33,16 @@ macro_rules! define_numbered_css_keyword
                 match *self {
                     $( $name::$variant => dest.write_str($css) ),+
                 }
             }
         }
     }
 }
 
-/// A macro used to implement HasViewportPercentage trait
-/// for a given type that may never contain viewport units.
-macro_rules! no_viewport_percentage {
-    ($name: ident) => {
-        impl $crate::values::HasViewportPercentage for $name {
-            #[inline]
-            fn has_viewport_percentage(&self) -> bool {
-                false
-            }
-        }
-    };
-}
-
 /// A macro for implementing `ComputedValueAsSpecified`, `Parse`
 /// and `HasViewportPercentage` 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 {
--- a/servo/components/style/properties/helpers.mako.rs
+++ b/servo/components/style/properties/helpers.mako.rs
@@ -8,18 +8,21 @@
 %>
 
 <%def name="predefined_type(name, type, initial_value, parse_method='parse',
             needs_context=True, vector=False, computed_type=None, initial_specified_value=None,
             allow_quirks=False, **kwargs)">
     <%def name="predefined_type_inner(name, type, initial_value, parse_method)">
         #[allow(unused_imports)]
         use app_units::Au;
+        #[allow(unused_imports)]
         use cssparser::{Color as CSSParserColor, RGBA};
+        #[allow(unused_imports)]
         use values::specified::AllowQuirks;
+        #[allow(unused_imports)]
         use smallvec::SmallVec;
         pub use values::specified::${type} as SpecifiedValue;
         pub mod computed_value {
             % if computed_type:
             pub use ${computed_type} as T;
             % else:
             pub use values::computed::${type} as T;
             % endif
@@ -73,32 +76,32 @@
     `initial_value` need not be defined for these.
 </%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::ToCss;
 
-            impl HasViewportPercentage for SpecifiedValue {
-                fn has_viewport_percentage(&self) -> bool {
-                    let &SpecifiedValue(ref vec) = self;
-                    vec.iter().any(|ref x| x.has_viewport_percentage())
-                }
-            }
-
             pub mod single_value {
+                #[allow(unused_imports)]
                 use cssparser::Parser;
+                #[allow(unused_imports)]
                 use parser::{Parse, ParserContext};
+                #[allow(unused_imports)]
                 use properties::ShorthandId;
+                #[allow(unused_imports)]
                 use values::computed::{Context, ToComputedValue};
+                #[allow(unused_imports)]
                 use values::{computed, specified};
+                #[allow(unused_imports)]
                 use values::{Auto, Either, None_, Normal};
                 ${caller.body()}
             }
 
             /// The definition of the computed value for ${name}.
             pub mod computed_value {
                 pub use super::single_value::computed_value as single_value;
                 pub use self::single_value::T as SingleComputedValue;
@@ -163,17 +166,17 @@
                         try!(dest.write_str(", "));
                         try!(i.to_css(dest));
                     }
                     Ok(())
                 }
             }
 
             /// The specified value of ${name}.
-            #[derive(Debug, Clone, PartialEq)]
+            #[derive(Clone, Debug, HasViewportPercentage, PartialEq)]
             #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
             pub struct SpecifiedValue(pub Vec<single_value::SpecifiedValue>);
 
             impl ToCss for SpecifiedValue {
                 fn to_css<W>(&self, dest: &mut W) -> fmt::Result
                     where W: fmt::Write,
                 {
                     let mut iter = self.0.iter();
@@ -200,16 +203,17 @@
                 % else:
                     let mut v = SmallVec::new();
                     v.push(single_value::get_initial_value());
                     computed_value::T(v)
                 % endif
             }
 
             pub fn parse(context: &ParserContext, input: &mut Parser) -> Result<SpecifiedValue, ()> {
+                #[allow(unused_imports)]
                 use parser::parse_space_or_comma_separated;
 
                 <%
                     parse_func = "Parser::parse_comma_separated"
                     if space_separated_allowed:
                         parse_func = "parse_space_or_comma_separated"
                 %>
 
@@ -255,32 +259,45 @@
 <%def name="longhand(*args, **kwargs)">
     <%
         property = data.declare_longhand(*args, **kwargs)
         if property is None:
             return ""
     %>
     /// ${property.spec}
     pub mod ${property.ident} {
-        #![allow(unused_imports)]
         % if not property.derived_from:
+            #[allow(unused_imports)]
             use cssparser::Parser;
+            #[allow(unused_imports)]
             use parser::{Parse, ParserContext};
+            #[allow(unused_imports)]
             use properties::{UnparsedValue, ShorthandId};
         % endif
+        #[allow(unused_imports)]
         use values::{Auto, Either, None_, Normal};
+        #[allow(unused_imports)]
         use cascade_info::CascadeInfo;
+        #[allow(unused_imports)]
         use error_reporting::ParseErrorReporter;
+        #[allow(unused_imports)]
         use properties::longhands;
+        #[allow(unused_imports)]
         use properties::{DeclaredValue, LonghandId, LonghandIdSet};
+        #[allow(unused_imports)]
         use properties::{CSSWideKeyword, ComputedValues, PropertyDeclaration};
+        #[allow(unused_imports)]
         use properties::style_structs;
+        #[allow(unused_imports)]
         use stylearc::Arc;
+        #[allow(unused_imports)]
         use values::computed::{Context, ToComputedValue};
+        #[allow(unused_imports)]
         use values::{computed, generics, specified};
+        #[allow(unused_imports)]
         use Atom;
         ${caller.body()}
         #[allow(unused_variables)]
         pub fn cascade_property(declaration: &PropertyDeclaration,
                                 inherited_style: &ComputedValues,
                                 default_style: &ComputedValues,
                                 context: &mut computed::Context,
                                 cacheable: &mut bool,
@@ -456,17 +473,16 @@
         keyword_kwargs = {a: kwargs.pop(a, None) for a in [
             'gecko_constant_prefix', 'gecko_enum_prefix',
             'extra_gecko_values', 'extra_servo_values',
             'custom_consts', 'gecko_inexhaustive',
         ]}
         keyword = keyword=Keyword(name, values, **keyword_kwargs)
     %>
     <%call expr="longhand(name, keyword=Keyword(name, values, **keyword_kwargs), **kwargs)">
-        use values::HasViewportPercentage;
         use properties::longhands::system_font::SystemFont;
         use std::fmt;
         use style_traits::ToCss;
         no_viewport_percentage!(SpecifiedValue);
 
         pub mod computed_value {
             use cssparser::Parser;
             use parser::{Parse, ParserContext};
@@ -578,17 +594,16 @@
                     }
                 }
             }
         % else:
             use values::computed::ComputedValueAsSpecified;
             impl ComputedValueAsSpecified for SpecifiedValue {}
         % endif
 
-        use values::HasViewportPercentage;
         no_viewport_percentage!(SpecifiedValue);
     </%call>
 </%def>
 
 <%def name="gecko_keyword_conversion(keyword, values=None, type='SpecifiedValue', cast_to=None)">
     <%
         if not values:
             values = keyword.values_for(product)
@@ -740,17 +755,16 @@
 <%def name="shorthand(name, sub_properties, experimental=False, **kwargs)">
 <%
     shorthand = data.declare_shorthand(name, sub_properties.split(), experimental=experimental,
                                        **kwargs)
 %>
     % if shorthand:
     /// ${shorthand.spec}
     pub mod ${shorthand.ident} {
-        #[allow(unused_imports)]
         use cssparser::Parser;
         use parser::ParserContext;
         use properties::{PropertyDeclaration, SourcePropertyDeclaration, MaybeBoxed};
         use properties::{ShorthandId, LonghandId, UnparsedValue, longhands};
         use std::fmt;
         use stylearc::Arc;
         use style_traits::ToCss;
 
--- a/servo/components/style/properties/longhand/background.mako.rs
+++ b/servo/components/style/properties/longhand/background.mako.rs
@@ -29,17 +29,16 @@
                               spec="https://drafts.csswg.org/css-backgrounds-4/#propdef-background-position-" + axis,
                               animation_value_type="ComputedValue", vector=True, delegate_animate=True)}
 % endfor
 
 <%helpers:vector_longhand name="background-repeat" animation_value_type="none"
                           spec="https://drafts.csswg.org/css-backgrounds/#the-background-repeat">
     use std::fmt;
     use style_traits::ToCss;
-    use values::HasViewportPercentage;
 
     define_css_keyword_enum!(RepeatKeyword:
                              "repeat" => Repeat,
                              "space" => Space,
                              "round" => Round,
                              "no-repeat" => NoRepeat);
 
     #[derive(Debug, Clone, PartialEq)]
@@ -158,21 +157,18 @@
 ${helpers.single_keyword("background-origin",
                          "padding-box border-box content-box",
                          vector=True, extra_prefixes="webkit",
                          spec="https://drafts.csswg.org/css-backgrounds/#the-background-origin",
                          animation_value_type="none")}
 
 <%helpers:vector_longhand name="background-size" animation_value_type="ComputedValue" extra_prefixes="webkit"
                           spec="https://drafts.csswg.org/css-backgrounds/#the-background-size">
-    use cssparser::Token;
-    use std::ascii::AsciiExt;
     use std::fmt;
     use style_traits::ToCss;
-    use values::HasViewportPercentage;
 
     #[allow(missing_docs)]
     pub mod computed_value {
         use values::computed::LengthOrPercentageOrAuto;
         use properties::animated_properties::{Animatable, RepeatableListAnimatable};
 
         #[derive(PartialEq, Clone, Debug)]
         #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
@@ -231,23 +227,17 @@
             match *self {
                 computed_value::T::Explicit(ref size) => size.to_css(dest),
                 computed_value::T::Cover => dest.write_str("cover"),
                 computed_value::T::Contain => dest.write_str("contain"),
             }
         }
     }
 
-    impl HasViewportPercentage for ExplicitSize {
-        fn has_viewport_percentage(&self) -> bool {
-            return self.width.has_viewport_percentage() || self.height.has_viewport_percentage();
-        }
-    }
-
-    #[derive(Clone, PartialEq, Debug)]
+    #[derive(Clone, Debug, HasViewportPercentage, PartialEq)]
     #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
     #[allow(missing_docs)]
     pub struct ExplicitSize {
         pub width: specified::LengthOrPercentageOrAuto,
         pub height: specified::LengthOrPercentageOrAuto,
     }
 
     impl ToCss for ExplicitSize {
@@ -261,26 +251,17 @@
     impl ToCss for computed_value::ExplicitSize {
         fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
             try!(self.width.to_css(dest));
             try!(dest.write_str(" "));
             self.height.to_css(dest)
         }
     }
 
-    impl HasViewportPercentage for SpecifiedValue {
-        fn has_viewport_percentage(&self) -> bool {
-            match *self {
-                SpecifiedValue::Explicit(ref explicit_size) => explicit_size.has_viewport_percentage(),
-                _ => false
-            }
-        }
-    }
-
-    #[derive(Clone, PartialEq, Debug)]
+    #[derive(Clone, Debug, HasViewportPercentage, PartialEq)]
     #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
     pub enum SpecifiedValue {
         Explicit(ExplicitSize),
         Cover,
         Contain,
     }
 
     impl ToCss for SpecifiedValue {
--- a/servo/components/style/properties/longhand/border.mako.rs
+++ b/servo/components/style/properties/longhand/border.mako.rs
@@ -57,17 +57,16 @@
 /// -moz-border-*-colors: color, string, enum, none, inherit/initial
 /// These non-spec properties are just for Gecko (Stylo) internal use.
 % for side in PHYSICAL_SIDES:
     <%helpers:longhand name="-moz-border-${side}-colors" animation_value_type="none"
                        spec="Nonstandard (https://developer.mozilla.org/en-US/docs/Web/CSS/-moz-border-*-colors)"
                        products="gecko">
         use std::fmt;
         use style_traits::ToCss;
-        use values::HasViewportPercentage;
         use values::specified::CSSColor;
         no_viewport_percentage!(SpecifiedValue);
 
         pub mod computed_value {
             use values::computed::CSSColor;
             #[derive(Debug, Clone, PartialEq)]
             #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
             pub struct T(pub Option<Vec<CSSColor>>);
@@ -200,39 +199,27 @@
     animation_value_type="none",
     has_uncacheable_values=False,
     boxed="True")}
 
 <%helpers:longhand name="border-image-outset" animation_value_type="none"
                    spec="https://drafts.csswg.org/css-backgrounds/#border-image-outset">
     use std::fmt;
     use style_traits::ToCss;
-    use values::HasViewportPercentage;
     use values::specified::{LengthOrNumber, Number};
 
-    impl HasViewportPercentage for SpecifiedValue {
-        fn has_viewport_percentage(&self) -> bool {
-            let mut viewport_percentage = false;
-            for value in self.0.iter() {
-                let vp = value.has_viewport_percentage();
-                viewport_percentage = vp || viewport_percentage;
-            }
-            viewport_percentage
-        }
-    }
-
     pub mod computed_value {
         use values::computed::LengthOrNumber;
         #[derive(Debug, Clone, PartialEq)]
         #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
         pub struct T(pub LengthOrNumber, pub LengthOrNumber,
                      pub LengthOrNumber, pub LengthOrNumber);
     }
 
-    #[derive(Debug, Clone, PartialEq)]
+    #[derive(Clone, Debug, HasViewportPercentage, PartialEq)]
     #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
     pub struct SpecifiedValue(pub Vec<LengthOrNumber>);
 
     impl ToCss for computed_value::T {
         fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
             try!(self.0.to_css(dest));
             try!(dest.write_str(" "));
             try!(self.1.to_css(dest));
@@ -316,23 +303,21 @@
         }
     }
 </%helpers:longhand>
 
 <%helpers:longhand name="border-image-repeat" animation_value_type="none"
                    spec="https://drafts.csswg.org/css-backgrounds/#border-image-repeat">
     use std::fmt;
     use style_traits::ToCss;
-    use values::HasViewportPercentage;
 
     no_viewport_percentage!(SpecifiedValue);
 
     pub mod computed_value {
         pub use super::RepeatKeyword;
-        use values::computed;
 
         #[derive(Debug, Clone, PartialEq)]
         #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
         pub struct T(pub RepeatKeyword, pub RepeatKeyword);
     }
 
     #[derive(Debug, Clone, PartialEq)]
     #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
@@ -394,50 +379,35 @@
         Ok(SpecifiedValue(first, second))
     }
 </%helpers:longhand>
 
 <%helpers:longhand name="border-image-width" animation_value_type="none"
                    spec="https://drafts.csswg.org/css-backgrounds/#border-image-width">
     use std::fmt;
     use style_traits::ToCss;
-    use values::HasViewportPercentage;
     use values::specified::{LengthOrPercentage, Number};
 
-    impl HasViewportPercentage for SpecifiedValue {
-        fn has_viewport_percentage(&self) -> bool {
-            let mut viewport_percentage = false;
-            for value in self.0.clone() {
-                let vp = match value {
-                    SingleSpecifiedValue::LengthOrPercentage(len) => len.has_viewport_percentage(),
-                    _ => false,
-                };
-                viewport_percentage = vp || viewport_percentage;
-            }
-            viewport_percentage
-        }
-    }
-
     pub mod computed_value {
         use values::computed::{LengthOrPercentage, Number};
         #[derive(Debug, Clone, PartialEq)]
         #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
         pub struct T(pub SingleComputedValue, pub SingleComputedValue,
                      pub SingleComputedValue, pub SingleComputedValue);
 
         #[derive(Debug, Clone, PartialEq)]
         #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
         pub enum SingleComputedValue {
             LengthOrPercentage(LengthOrPercentage),
             Number(Number),
             Auto,
         }
     }
 
-    #[derive(Debug, Clone, PartialEq)]
+    #[derive(Clone, Debug, HasViewportPercentage, PartialEq)]
     #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
     pub struct SpecifiedValue(pub Vec<SingleSpecifiedValue>);
 
     impl ToCss for computed_value::T {
         fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
             try!(self.0.to_css(dest));
             try!(dest.write_str(" "));
             try!(self.1.to_css(dest));
@@ -453,17 +423,17 @@
             for value in self.0.iter().skip(1) {
                 try!(dest.write_str(" "));
                 try!(value.to_css(dest));
             }
             Ok(())
         }
     }
 
-    #[derive(Debug, Clone, PartialEq)]
+    #[derive(Clone, Debug, HasViewportPercentage, PartialEq)]
     #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
     pub enum SingleSpecifiedValue {
         LengthOrPercentage(LengthOrPercentage),
         Number(Number),
         Auto,
     }
 
     impl ToCss for computed_value::SingleComputedValue {
@@ -594,17 +564,16 @@
         }
     }
 </%helpers:longhand>
 
 <%helpers:longhand name="border-image-slice" boxed="True" animation_value_type="none"
                    spec="https://drafts.csswg.org/css-backgrounds/#border-image-slice">
     use std::fmt;
     use style_traits::ToCss;
-    use values::HasViewportPercentage;
     use values::computed::NumberOrPercentage as ComputedNumberOrPercentage;
     use values::specified::{NumberOrPercentage, Percentage};
 
     no_viewport_percentage!(SpecifiedValue);
 
     pub mod computed_value {
         use values::computed::NumberOrPercentage;
         #[derive(Debug, Clone, PartialEq)]
--- a/servo/components/style/properties/longhand/box.mako.rs
+++ b/servo/components/style/properties/longhand/box.mako.rs
@@ -26,17 +26,16 @@
             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;
-    use values::HasViewportPercentage;
     no_viewport_percentage!(SpecifiedValue);
 
     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.
@@ -165,17 +164,16 @@
                                   extra_specified="inline-start inline-end"
                                   needs_conversion="True"
                                   animation_value_type="none"
                                   need_clone="True"
                                   gecko_enum_prefix="StyleFloat"
                                   gecko_inexhaustive="True"
                                   gecko_ffi_name="mFloat"
                                   spec="https://drafts.csswg.org/css-box/#propdef-float">
-    use values::HasViewportPercentage;
     no_viewport_percentage!(SpecifiedValue);
     impl ToComputedValue for SpecifiedValue {
         type ComputedValue = computed_value::T;
 
         #[inline]
         fn to_computed_value(&self, context: &Context) -> computed_value::T {
             let ltr = context.style().writing_mode.is_bidi_ltr();
             // https://drafts.csswg.org/css-logical-props/#float-clear
@@ -204,17 +202,16 @@
                                   values="none left right both"
                                   // https://drafts.csswg.org/css-logical-props/#float-clear
                                   extra_specified="inline-start inline-end"
                                   needs_conversion="True"
                                   animation_value_type="none"
                                   gecko_enum_prefix="StyleClear"
                                   gecko_ffi_name="mBreakType"
                                   spec="https://www.w3.org/TR/CSS2/visuren.html#flow-control">
-    use values::HasViewportPercentage;
     no_viewport_percentage!(SpecifiedValue);
     impl ToComputedValue for SpecifiedValue {
         type ComputedValue = computed_value::T;
 
         #[inline]
         fn to_computed_value(&self, context: &Context) -> computed_value::T {
             let ltr = context.style().writing_mode.is_bidi_ltr();
             // https://drafts.csswg.org/css-logical-props/#float-clear
@@ -258,39 +255,29 @@
     }
 
 </%helpers:longhand>
 
 <%helpers:longhand name="vertical-align" animation_value_type="ComputedValue"
                    spec="https://www.w3.org/TR/CSS2/visudet.html#propdef-vertical-align">
     use std::fmt;
     use style_traits::ToCss;
-    use values::HasViewportPercentage;
     use values::specified::AllowQuirks;
 
     <% vertical_align = data.longhands_by_name["vertical-align"] %>
     <% vertical_align.keyword = Keyword("vertical-align",
                                         "baseline sub super top text-top middle bottom text-bottom",
                                         extra_gecko_values="-moz-middle-with-baseline") %>
     <% vertical_align_keywords = vertical_align.keyword.values_for(product) %>
 
     ${helpers.gecko_keyword_conversion(vertical_align.keyword)}
 
-    impl HasViewportPercentage for SpecifiedValue {
-        fn has_viewport_percentage(&self) -> bool {
-            match *self {
-                SpecifiedValue::LengthOrPercentage(ref length) => length.has_viewport_percentage(),
-                _ => false
-            }
-        }
-    }
-
     /// The `vertical-align` value.
     #[allow(non_camel_case_types)]
-    #[derive(Debug, Clone, PartialEq)]
+    #[derive(Clone, Debug, HasViewportPercentage, PartialEq)]
     #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
     pub enum SpecifiedValue {
         % for keyword in vertical_align_keywords:
             ${to_rust_ident(keyword)},
         % endfor
         LengthOrPercentage(specified::LengthOrPercentage),
     }
 
@@ -316,20 +303,19 @@
                 % endfor
                 _ => Err(())
             }
         })
     }
 
     /// The computed value for `vertical-align`.
     pub mod computed_value {
-        use app_units::Au;
         use std::fmt;
         use style_traits::ToCss;
-        use values::{CSSFloat, computed};
+        use values::computed;
 
         /// The keywords are the same, and the `LengthOrPercentage` is computed
         /// here.
         #[allow(non_camel_case_types)]
         #[derive(PartialEq, Copy, Clone, Debug)]
         #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
         pub enum T {
             % for keyword in vertical_align_keywords:
@@ -421,17 +407,16 @@
 <%helpers:vector_longhand name="transition-duration"
                           need_index="True"
                           animation_value_type="none"
                           extra_prefixes="moz webkit"
                           spec="https://drafts.csswg.org/css-transitions/#propdef-transition-duration">
     use values::specified::Time;
 
     pub use values::specified::Time as SpecifiedValue;
-    use values::HasViewportPercentage;
     no_viewport_percentage!(SpecifiedValue);
 
     pub mod computed_value {
         pub use values::computed::Time as T;
     }
 
     #[inline]
     pub fn get_initial_value() -> computed_value::T {
@@ -453,17 +438,16 @@
                           need_index="True"
                           animation_value_type="none"
                           extra_prefixes="moz webkit"
                           spec="https://drafts.csswg.org/css-transitions/#propdef-transition-timing-function">
     use self::computed_value::StartEnd;
     use values::specified::Number;
     use euclid::point::{Point2D, TypedPoint2D};
     use std::fmt;
-    use std::marker::PhantomData;
     use style_traits::ToCss;
 
     // FIXME: This could use static variables and const functions when they are available.
     #[inline(always)]
     fn ease() -> computed_value::T {
         computed_value::T::CubicBezier(TypedPoint2D::new(0.25, 0.1),
                                        TypedPoint2D::new(0.25, 1.0))
     }
@@ -494,17 +478,16 @@
 
     static STEP_START: computed_value::T =
         computed_value::T::Steps(1, StartEnd::Start);
     static STEP_END: computed_value::T =
         computed_value::T::Steps(1, StartEnd::End);
 
     pub mod computed_value {
         use euclid::point::Point2D;
-        use parser::{Parse, ParserContext};
         use std::fmt;
         use style_traits::ToCss;
         use super::FunctionKeyword;
         use values::specified;
 
         pub use super::parse;
 
         #[derive(Copy, Clone, Debug, PartialEq)]
@@ -759,17 +742,16 @@
                 FunctionKeyword::EaseOut => ease_out(),
                 FunctionKeyword::EaseInOut => ease_in_out(),
                 FunctionKeyword::StepStart => STEP_START,
                 FunctionKeyword::StepEnd => STEP_END,
             }
         }
     }
 
-    use values::HasViewportPercentage;
     no_viewport_percentage!(SpecifiedValue);
 
     #[inline]
     pub fn get_initial_value() -> computed_value::T {
         computed_value::T::Keyword(FunctionKeyword::Ease)
     }
 
     #[inline]
@@ -790,32 +772,29 @@
                           spec="https://drafts.csswg.org/css-transitions/#propdef-transition-property">
 
     use values::computed::ComputedValueAsSpecified;
 
     pub use properties::animated_properties::TransitionProperty;
     pub use properties::animated_properties::TransitionProperty as SpecifiedValue;
 
     pub mod computed_value {
-        use std::fmt;
-        use style_traits::ToCss;
         // NB: Can't generate the type here because it needs all the longhands
         // generated beforehand.
         pub use super::SpecifiedValue as T;
     }
 
     pub fn parse(_context: &ParserContext, input: &mut Parser) -> Result<SpecifiedValue,()> {
         SpecifiedValue::parse(input)
     }
 
     pub fn get_initial_specified_value() -> SpecifiedValue {
         TransitionProperty::All
     }
 
-    use values::HasViewportPercentage;
     no_viewport_percentage!(SpecifiedValue);
 
     impl ComputedValueAsSpecified for SpecifiedValue { }
 </%helpers:vector_longhand>
 
 <%helpers:vector_longhand name="transition-delay"
                           need_index="True"
                           animation_value_type="none"
@@ -834,20 +813,19 @@
 <%helpers:vector_longhand name="animation-name"
                           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 std::ops::Deref;
     use style_traits::ToCss;
     use values::computed::ComputedValueAsSpecified;
-    use values::{HasViewportPercentage, KeyframesName};
+    use values::KeyframesName;
 
     pub mod computed_value {
         pub use super::SpecifiedValue as T;
     }
 
     #[derive(Clone, Debug, Hash, Eq, PartialEq)]
     #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
     pub struct SpecifiedValue(pub Option<KeyframesName>);
@@ -932,17 +910,16 @@
                           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 std::fmt;
     use style_traits::ToCss;
     use values::computed::ComputedValueAsSpecified;
-    use values::HasViewportPercentage;
 
     pub mod computed_value {
         pub use super::SpecifiedValue as T;
     }
 
     // https://drafts.csswg.org/css-animations/#animation-iteration-count
     #[derive(Debug, Clone, PartialEq)]
     #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
@@ -1040,37 +1017,27 @@
     pub use properties::longhands::transition_delay::single_value::{get_initial_value, parse};
     pub use properties::longhands::transition_delay::single_value::SpecifiedValue;
 </%helpers:vector_longhand>
 
 <%helpers:longhand products="gecko" name="scroll-snap-points-y" animation_value_type="none"
                    spec="Nonstandard (https://www.w3.org/TR/2015/WD-css-snappoints-1-20150326/#scroll-snap-points)">
     use std::fmt;
     use style_traits::ToCss;
-    use values::HasViewportPercentage;
     use values::specified::LengthOrPercentage;
 
-    impl HasViewportPercentage for SpecifiedValue {
-        fn has_viewport_percentage(&self) -> bool {
-            match *self {
-                SpecifiedValue::Repeat(ref length) => length.has_viewport_percentage(),
-                _ => false
-            }
-        }
-    }
-
     pub mod computed_value {
         use values::computed::LengthOrPercentage;
 
         #[derive(Debug, Clone, PartialEq)]
         #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
         pub struct T(pub Option<LengthOrPercentage>);
     }
 
-    #[derive(Debug, Clone, PartialEq)]
+    #[derive(Clone, Debug, HasViewportPercentage, PartialEq)]
     #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
     pub enum SpecifiedValue {
         None,
         Repeat(LengthOrPercentage),
     }
 
     impl ToCss for computed_value::T {
         fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
@@ -1170,19 +1137,18 @@
                    spec="https://drafts.csswg.org/css-transforms/#propdef-transform">
     use app_units::Au;
     use values::computed::{LengthOrPercentageOrNumber as ComputedLoPoNumber, LengthOrNumber as ComputedLoN};
     use values::computed::{LengthOrPercentage as ComputedLoP, Length as ComputedLength};
     use values::specified::{Angle, Length, LengthOrPercentage};
     use values::specified::{LengthOrNumber, LengthOrPercentageOrNumber as LoPoNumber, Number};
     use style_traits::ToCss;
     use style_traits::values::Css;
-    use values::HasViewportPercentage;
 
-    use std::fmt::{self, Display};
+    use std::fmt;
 
     pub mod computed_value {
         use app_units::Au;
         use values::CSSFloat;
         use values::computed;
         use values::computed::{Length, LengthOrPercentage};
 
         #[derive(Clone, Copy, Debug, PartialEq)]
@@ -1248,17 +1214,17 @@
     }
 
     /// Describes a single parsed
     /// [Transform Function](https://drafts.csswg.org/css-transforms/#typedef-transform-function).
     ///
     /// Multiple transform functions compose a transformation.
     ///
     /// Some transformations can be expressed by other more general functions.
-    #[derive(Clone, Debug, PartialEq)]
+    #[derive(Clone, Debug, HasViewportPercentage, PartialEq)]
     #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
     pub enum SpecifiedOperation {
         /// Represents a 2D 2x3 matrix.
         Matrix { a: Number, b: Number, c: Number, d: Number, e: Number, f: Number },
         /// Represents a 3D 4x4 matrix with percentage and length values.
         /// For `moz-transform`.
         PrefixedMatrix { a: Number, b: Number, c: Number, d: Number, e: LoPoNumber, f: LoPoNumber },
         /// Represents a 3D 4x4 matrix.
@@ -1323,51 +1289,16 @@
 
     impl ToCss for computed_value::T {
         fn to_css<W>(&self, _: &mut W) -> fmt::Result where W: fmt::Write {
             // TODO(pcwalton)
             Ok(())
         }
     }
 
-    impl HasViewportPercentage for SpecifiedOperation {
-        fn has_viewport_percentage(&self) -> bool {
-            match *self {
-                SpecifiedOperation::Translate(ref l1, None) |
-                SpecifiedOperation::TranslateX(ref l1) |
-                SpecifiedOperation::TranslateY(ref l1)  => {
-                    l1.has_viewport_percentage()
-                }
-                SpecifiedOperation::TranslateZ(ref l1) => {
-                    l1.has_viewport_percentage()
-                }
-                SpecifiedOperation::Translate(ref l1, Some(ref l2)) => {
-                    l1.has_viewport_percentage() ||
-                    l2.has_viewport_percentage()
-                }
-                SpecifiedOperation::Translate3D(ref l1, ref l2, ref l3) => {
-                    l1.has_viewport_percentage() ||
-                    l2.has_viewport_percentage() ||
-                    l3.has_viewport_percentage()
-                },
-                SpecifiedOperation::Perspective(ref length) => length.has_viewport_percentage(),
-                SpecifiedOperation::PrefixedMatrix{ ref e, ref f, .. } => {
-                    e.has_viewport_percentage() ||
-                    f.has_viewport_percentage()
-                },
-                SpecifiedOperation::PrefixedMatrix3D{ ref m41, ref m42, ref m43, .. } => {
-                    m41.has_viewport_percentage() ||
-                    m42.has_viewport_percentage() ||
-                    m43.has_viewport_percentage()
-                },
-                _ => false
-            }
-        }
-    }
-
     impl ToCss for SpecifiedOperation {
         fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
             use self::SpecifiedOperation::*;
             match *self {
                 Matrix { a, b, c, d, e, f} => write!(
                     dest, "matrix({}, {}, {}, {}, {}, {})",
                     Css(a), Css(b), Css(c), Css(d), Css(e), Css(f)),
                 PrefixedMatrix { a, b, c, d, ref e, ref f} => write!(
@@ -1417,24 +1348,17 @@
                 Rotate3D(x, y, z, theta) => write!(
                     dest, "rotate3d({}, {}, {}, {})",
                     Css(x), Css(y), Css(z), Css(theta)),
                 Perspective(ref length) => write!(dest, "perspective({})", Css(length)),
             }
         }
     }
 
-    impl HasViewportPercentage for SpecifiedValue {
-        fn has_viewport_percentage(&self) -> bool {
-            let &SpecifiedValue(ref specified_ops) = self;
-            specified_ops.iter().any(|ref x| x.has_viewport_percentage())
-        }
-    }
-
-    #[derive(Clone, Debug, PartialEq)]
+    #[derive(Clone, Debug, HasViewportPercentage, PartialEq)]
     #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
     pub struct SpecifiedValue(Vec<SpecifiedOperation>);
 
     impl ToCss for SpecifiedValue {
         fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
 
             if self.0.is_empty() {
                 return dest.write_str("none")
@@ -2176,17 +2100,16 @@
                          flags="CREATES_STACKING_CONTEXT FIXPOS_CB",
                          animation_value_type="none")}
 
 <%helpers:longhand name="transform-origin" animation_value_type="ComputedValue" extra_prefixes="moz webkit" boxed="True"
                    spec="https://drafts.csswg.org/css-transforms/#transform-origin-property">
     use app_units::Au;
     use std::fmt;
     use style_traits::ToCss;
-    use values::HasViewportPercentage;
     use values::specified::{NoCalcLength, LengthOrPercentage, Percentage};
 
     pub mod computed_value {
         use properties::animated_properties::Animatable;
         use values::computed::{Length, LengthOrPercentage};
 
         #[derive(Clone, Copy, Debug, PartialEq)]
         #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
@@ -2218,25 +2141,17 @@
             fn compute_squared_distance(&self, other: &Self) -> Result<f64, ()> {
                 Ok(try!(self.horizontal.compute_squared_distance(&other.horizontal)) +
                    try!(self.vertical.compute_squared_distance(&other.vertical)) +
                    try!(self.depth.compute_squared_distance(&other.depth)))
             }
         }
     }
 
-    impl HasViewportPercentage for SpecifiedValue {
-        fn has_viewport_percentage(&self) -> bool {
-            self.horizontal.has_viewport_percentage() ||
-            self.vertical.has_viewport_percentage() ||
-            self.depth.has_viewport_percentage()
-        }
-    }
-
-    #[derive(Clone, Debug, PartialEq)]
+    #[derive(Clone, Debug, HasViewportPercentage, PartialEq)]
     #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
     pub struct SpecifiedValue {
         horizontal: LengthOrPercentage,
         vertical: LengthOrPercentage,
         depth: NoCalcLength,
     }
 
     impl ToCss for computed_value::T {
@@ -2302,17 +2217,16 @@
 
 // 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="none" products="gecko" need_clone="True"
                    spec="https://drafts.csswg.org/css-contain/#contain-property">
     use std::fmt;
     use style_traits::ToCss;
-    use values::HasViewportPercentage;
     use values::computed::ComputedValueAsSpecified;
 
     impl ComputedValueAsSpecified for SpecifiedValue {}
     no_viewport_percentage!(SpecifiedValue);
 
     pub mod computed_value {
         pub type T = super::SpecifiedValue;
     }
@@ -2455,17 +2369,16 @@
                           spec="Nonstandard (https://developer.mozilla.org/en-US/docs/Web/CSS/-moz-orient)",
                           animation_value_type="none")}
 
 <%helpers:longhand name="will-change" products="gecko" animation_value_type="none"
                    spec="https://drafts.csswg.org/css-will-change/#will-change">
     use cssparser::serialize_identifier;
     use std::fmt;
     use style_traits::ToCss;
-    use values::HasViewportPercentage;
     use values::computed::ComputedValueAsSpecified;
 
     impl ComputedValueAsSpecified for SpecifiedValue {}
     no_viewport_percentage!(SpecifiedValue);
 
     pub mod computed_value {
         pub use super::SpecifiedValue as T;
     }
@@ -2528,17 +2441,16 @@
 <%helpers:longhand name="touch-action"
                    products="gecko"
                    animation_value_type="none"
                    disable_when_testing="True"
                    spec="https://compat.spec.whatwg.org/#touch-action">
     use gecko_bindings::structs;
     use std::fmt;
     use style_traits::ToCss;
-    use values::HasViewportPercentage;
     use values::computed::ComputedValueAsSpecified;
 
     impl ComputedValueAsSpecified for SpecifiedValue {}
     no_viewport_percentage!(SpecifiedValue);
 
     pub mod computed_value {
         pub use super::SpecifiedValue as T;
     }
--- a/servo/components/style/properties/longhand/color.mako.rs
+++ b/servo/components/style/properties/longhand/color.mako.rs
@@ -9,17 +9,17 @@
 <% from data import to_rust_ident %>
 
 <%helpers:longhand name="color" need_clone="True"
                    animation_value_type="IntermediateRGBA"
                    spec="https://drafts.csswg.org/css-color/#color">
     use cssparser::RGBA;
     use std::fmt;
     use style_traits::ToCss;
-    use values::specified::{AllowQuirks, Color, CSSColor, CSSRGBA};
+    use values::specified::{AllowQuirks, Color, CSSColor};
 
     impl ToComputedValue for SpecifiedValue {
         type ComputedValue = computed_value::T;
 
         #[inline]
         fn to_computed_value(&self, context: &Context) -> computed_value::T {
             self.0.parsed.to_computed_value(context)
         }
@@ -117,16 +117,17 @@
             #[inline]
             fn from_computed_value(_: &Self::ComputedValue) -> Self {
                 unreachable!()
             }
         }
 
         impl SystemColor {
             pub fn parse(input: &mut Parser) -> Result<Self, ()> {
+                #[cfg(feature = "gecko")]
                 use std::ascii::AsciiExt;
                 static PARSE_ARRAY: &'static [(&'static str, SystemColor); ${len(system_colors)}] = &[
                     % for color in system_colors:
                         ("${color}", LookAndFeel_ColorID::eColorID_${to_rust_ident(color)}),
                     % endfor
                 ];
 
                 let ident = input.expect_ident()?;
--- a/servo/components/style/properties/longhand/counters.mako.rs
+++ b/servo/components/style/properties/longhand/counters.mako.rs
@@ -4,36 +4,36 @@
 
 <%namespace name="helpers" file="/helpers.mako.rs" />
 
 <% data.new_style_struct("Counters", inherited=False, gecko_name="Content") %>
 
 <%helpers:longhand name="content" boxed="True" animation_value_type="none"
                    spec="https://drafts.csswg.org/css-content/#propdef-content">
     use cssparser::Token;
-    use std::ascii::AsciiExt;
     use values::computed::ComputedValueAsSpecified;
     #[cfg(feature = "gecko")]
     use values::generics::CounterStyleOrNone;
+    #[cfg(feature = "gecko")]
     use values::specified::url::SpecifiedUrl;
-    use values::HasViewportPercentage;
 
     #[cfg(feature = "servo")]
     use super::list_style_type;
 
     pub use self::computed_value::T as SpecifiedValue;
     pub use self::computed_value::ContentItem;
 
     impl ComputedValueAsSpecified for SpecifiedValue {}
     no_viewport_percentage!(SpecifiedValue);
 
     pub mod computed_value {
         use cssparser;
         use std::fmt;
         use style_traits::ToCss;
+        #[cfg(feature = "gecko")]
         use values::specified::url::SpecifiedUrl;
 
         #[cfg(feature = "servo")]
         type CounterStyleType = super::super::list_style_type::computed_value::T;
         #[cfg(feature = "gecko")]
         type CounterStyleType = ::values::generics::CounterStyleOrNone;
 
         #[derive(Debug, PartialEq, Eq, Clone)]
@@ -251,21 +251,19 @@
         }
     }
 </%helpers:longhand>
 
 <%helpers:longhand name="counter-increment" animation_value_type="none"
                    spec="https://drafts.csswg.org/css-lists/#propdef-counter-increment">
     use std::fmt;
     use style_traits::ToCss;
-    use super::content;
-    use values::{HasViewportPercentage, CustomIdent};
+    use values::CustomIdent;
 
-    use cssparser::{Token, serialize_identifier};
-    use std::borrow::{Cow, ToOwned};
+    use cssparser::Token;
 
     #[derive(Debug, Clone, PartialEq)]
     pub struct SpecifiedValue(pub Vec<(CustomIdent, specified::Integer)>);
 
     pub mod computed_value {
         use std::fmt;
         use style_traits::ToCss;
         use values::CustomIdent;
@@ -273,17 +271,16 @@
         #[derive(Debug, Clone, PartialEq)]
         #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
         pub struct T(pub Vec<(CustomIdent, i32)>);
 
         impl ToCss for T {
             fn to_css<W>(&self, dest: &mut W) -> fmt::Result
                 where W: fmt::Write,
             {
-                use cssparser::serialize_identifier;
                 if self.0.is_empty() {
                     return dest.write_str("none")
                 }
 
                 let mut first = true;
                 for &(ref name, value) in &self.0 {
                     if !first {
                         dest.write_str(" ")?;
@@ -343,18 +340,16 @@
         }
     }
 
     pub fn parse(context: &ParserContext, input: &mut Parser) -> Result<SpecifiedValue, ()> {
         parse_common(context, 1, input)
     }
 
     pub fn parse_common(context: &ParserContext, default_value: i32, input: &mut Parser) -> Result<SpecifiedValue, ()> {
-        use std::ascii::AsciiExt;
-
         if input.try(|input| input.expect_ident_matching("none")).is_ok() {
             return Ok(SpecifiedValue(Vec::new()))
         }
 
         let mut counters = Vec::new();
         loop {
             let counter_name = match input.next() {
                 Ok(Token::Ident(ident)) => CustomIdent::from_ident(ident, &["none"])?,
--- a/servo/components/style/properties/longhand/effects.mako.rs
+++ b/servo/components/style/properties/longhand/effects.mako.rs
@@ -13,20 +13,18 @@
                           animation_value_type="ComputedValue",
                           flags="CREATES_STACKING_CONTEXT",
                           spec="https://drafts.csswg.org/css-color/#opacity")}
 
 <%helpers:vector_longhand name="box-shadow" allow_empty="True"
                           animation_value_type="IntermediateBoxShadowList"
                           extra_prefixes="webkit"
                           spec="https://drafts.csswg.org/css-backgrounds/#box-shadow">
-    use cssparser;
     use std::fmt;
     use style_traits::ToCss;
-    use values::HasViewportPercentage;
 
     pub type SpecifiedValue = specified::Shadow;
 
     impl ToCss for SpecifiedValue {
         fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
             if self.inset {
                 try!(dest.write_str("inset "));
             }
@@ -42,19 +40,16 @@
                 try!(dest.write_str(" "));
                 try!(color.to_css(dest));
             }
             Ok(())
         }
     }
 
     pub mod computed_value {
-        use app_units::Au;
-        use std::fmt;
-        use values::computed;
         use values::computed::Shadow;
 
         pub type T = Shadow;
     }
 
     impl ToCss for computed_value::T {
         fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
             if self.inset {
@@ -86,30 +81,26 @@
                           allow_quirks=True,
                           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 cssparser;
     use std::fmt;
-    use style_traits::{self, ToCss};
+    use style_traits::ToCss;
     use values::{CSSFloat, HasViewportPercentage};
-    use values::specified::{Angle, CSSColor, Length, Shadow};
+    use values::specified::{Angle, Length};
+    #[cfg(feature = "gecko")]
+    use values::specified::Shadow;
+    #[cfg(feature = "gecko")]
     use values::specified::url::SpecifiedUrl;
 
-    impl HasViewportPercentage for SpecifiedValue {
-        fn has_viewport_percentage(&self) -> bool {
-            self.0.iter().any(|ref x| x.has_viewport_percentage())
-        }
-    }
-
-    #[derive(Debug, Clone, PartialEq)]
+    #[derive(Clone, Debug, HasViewportPercentage, PartialEq)]
     #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
     pub struct SpecifiedValue(pub Vec<SpecifiedFilter>);
 
     impl HasViewportPercentage for SpecifiedFilter {
         fn has_viewport_percentage(&self) -> bool {
             match *self {
                 SpecifiedFilter::Blur(ref length) => length.has_viewport_percentage(),
                 _ => false
@@ -134,18 +125,20 @@
         DropShadow(Shadow),
         Url(SpecifiedUrl),
         % endif
     }
 
     pub mod computed_value {
         use app_units::Au;
         use values::CSSFloat;
-        use values::computed::{CSSColor, Shadow};
+        #[cfg(feature = "gecko")]
+        use values::computed::Shadow;
         use values::computed::Angle;
+        #[cfg(feature = "gecko")]
         use values::specified::url::SpecifiedUrl;
 
         #[derive(Clone, PartialEq, Debug)]
         #[cfg_attr(feature = "servo", derive(HeapSizeOf, Deserialize, Serialize))]
         pub enum Filter {
             Blur(Au),
             Brightness(CSSFloat),
             Contrast(CSSFloat),
--- a/servo/components/style/properties/longhand/font.mako.rs
+++ b/servo/components/style/properties/longhand/font.mako.rs
@@ -63,24 +63,21 @@
 </%def>
 
 <%helpers:longhand name="font-family" animation_value_type="none" need_index="True"  boxed="${product == 'gecko'}"
                    spec="https://drafts.csswg.org/css-fonts/#propdef-font-family">
     use properties::longhands::system_font::SystemFont;
     use self::computed_value::{FontFamily, FamilyName};
     use std::fmt;
     use style_traits::ToCss;
-    use values::HasViewportPercentage;
-    use values::computed::ComputedValueAsSpecified;
 
     no_viewport_percentage!(SpecifiedValue);
 
     pub mod computed_value {
         use cssparser::{CssStringWriter, Parser, serialize_identifier};
-        use properties::longhands::system_font::SystemFont;
         use std::fmt::{self, Write};
         use Atom;
         use style_traits::ToCss;
         pub use self::FontFamily as SingleComputedValue;
 
         #[derive(Debug, PartialEq, Eq, Clone, Hash)]
         #[cfg_attr(feature = "servo", derive(HeapSizeOf, Deserialize, Serialize))]
         pub enum FontFamily {
@@ -350,17 +347,16 @@
                                spec="https://drafts.csswg.org/css-fonts/#propdef-font-variant-caps",
                                custom_consts=font_variant_caps_custom_consts,
                                animation_value_type="none")}
 
 <%helpers:longhand name="font-weight" need_clone="True" animation_value_type="ComputedValue"
                    spec="https://drafts.csswg.org/css-fonts/#propdef-font-weight">
     use std::fmt;
     use style_traits::ToCss;
-    use values::HasViewportPercentage;
     use properties::longhands::system_font::SystemFont;
 
     no_viewport_percentage!(SpecifiedValue);
 
     #[derive(Debug, Clone, PartialEq, Eq, Copy)]
     #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
     pub enum SpecifiedValue {
         Normal,
@@ -444,17 +440,16 @@
                 SpecifiedValue::Bolder |
                 SpecifiedValue::Lighter => Err(()),
                 SpecifiedValue::System(..) => unreachable!(),
             }
         }
     }
 
     pub mod computed_value {
-        use std::fmt;
         #[derive(PartialEq, Eq, Copy, Clone, Hash, Debug)]
         #[cfg_attr(feature = "servo", derive(HeapSizeOf, Deserialize, Serialize))]
         #[repr(u16)]
         pub enum T {
             % for weight in range(100, 901, 100):
                 Weight${weight} = ${weight},
             % endfor
         }
@@ -552,17 +547,17 @@
 <%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 values::specified::{AllowQuirks, FontRelativeLength, LengthOrPercentage, Length};
+    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),
                 SpecifiedValue::Keyword(kw, _) => kw.to_css(dest),
@@ -689,18 +684,17 @@
                 unreachable!()
             }
         }
     % else:
         impl ToComputedValue for KeywordSize {
             type ComputedValue = Au;
             #[inline]
             fn to_computed_value(&self, cx: &Context) -> computed_value::T {
-                use gecko_bindings::bindings::Gecko_GetBaseSize;
-                use gecko_bindings::structs::{self, nsIAtom};
+                use gecko_bindings::structs::nsIAtom;
                 use values::specified::length::au_to_int_px;
                 // Data from nsRuleNode.cpp in Gecko
                 // Mapping from base size and HTML size to pixels
                 // The first index is (base_size - 9), the second is the
                 // HTML size. "0" is CSS keyword xx-small, not HTML size 0,
                 // since HTML size 0 is the same as 1.
                 //
                 //  xxs   xs      s      m     l      xl     xxl   -
@@ -972,17 +966,16 @@
     }
 </%helpers:longhand>
 
 <%helpers:longhand products="gecko" name="font-size-adjust" animation_value_type="ComputedValue"
                    spec="https://drafts.csswg.org/css-fonts/#propdef-font-size-adjust">
     use properties::longhands::system_font::SystemFont;
     use std::fmt;
     use style_traits::ToCss;
-    use values::HasViewportPercentage;
 
     no_viewport_percentage!(SpecifiedValue);
 
     #[derive(Copy, Clone, Debug, PartialEq)]
     #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
     pub enum SpecifiedValue {
         None,
         Number(specified::Number),
@@ -1114,17 +1107,16 @@
         Ok(SpecifiedValue::Number(try!(Number::parse_non_negative(context, input))))
     }
 </%helpers:longhand>
 
 <%helpers:longhand products="gecko" name="font-synthesis" animation_value_type="none"
                    spec="https://drafts.csswg.org/css-fonts/#propdef-font-synthesis">
     use std::fmt;
     use style_traits::ToCss;
-    use values::HasViewportPercentage;
     use values::computed::ComputedValueAsSpecified;
 
     impl ComputedValueAsSpecified for SpecifiedValue {}
     no_viewport_percentage!(SpecifiedValue);
 
     pub mod computed_value {
         pub use super::SpecifiedValue as T;
     }
@@ -1198,17 +1190,16 @@
 
 /// FIXME: Implement proper handling of each values.
 /// https://github.com/servo/servo/issues/15957
 <%helpers:longhand name="font-variant-alternates" products="gecko" animation_value_type="none"
                    spec="https://drafts.csswg.org/css-fonts/#propdef-font-variant-alternates">
     use properties::longhands::system_font::SystemFont;
     use std::fmt;
     use style_traits::ToCss;
-    use values::HasViewportPercentage;
 
     no_viewport_percentage!(SpecifiedValue);
 
     bitflags! {
         #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
         pub flags VariantAlternates: u8 {
             const NORMAL = 0,
             const HISTORICAL_FORMS = 0x01,
@@ -1334,17 +1325,16 @@ macro_rules! exclusive_value {
     }
 }
 
 <%helpers:longhand name="font-variant-east-asian" products="gecko" animation_value_type="none"
                    spec="https://drafts.csswg.org/css-fonts/#propdef-font-variant-east-asian">
     use properties::longhands::system_font::SystemFont;
     use std::fmt;
     use style_traits::ToCss;
-    use values::HasViewportPercentage;
 
     no_viewport_percentage!(SpecifiedValue);
 
     bitflags! {
         #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
         pub flags VariantEastAsian: u16 {
             const NORMAL = 0,
             const JIS78 = 0x01,
@@ -1475,17 +1465,16 @@ macro_rules! exclusive_value {
     }
 </%helpers:longhand>
 
 <%helpers:longhand name="font-variant-ligatures" products="gecko" animation_value_type="none"
                    spec="https://drafts.csswg.org/css-fonts/#propdef-font-variant-ligatures">
     use properties::longhands::system_font::SystemFont;
     use std::fmt;
     use style_traits::ToCss;
-    use values::HasViewportPercentage;
 
     no_viewport_percentage!(SpecifiedValue);
 
     bitflags! {
         #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
         pub flags VariantLigatures: u16 {
             const NORMAL = 0,
             const NONE = 0x01,
@@ -1626,17 +1615,16 @@ macro_rules! exclusive_value {
     }
 </%helpers:longhand>
 
 <%helpers:longhand name="font-variant-numeric" products="gecko" animation_value_type="none"
                    spec="https://drafts.csswg.org/css-fonts/#propdef-font-variant-numeric">
     use properties::longhands::system_font::SystemFont;
     use std::fmt;
     use style_traits::ToCss;
-    use values::HasViewportPercentage;
 
     no_viewport_percentage!(SpecifiedValue);
 
     bitflags! {
         #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
         pub flags VariantNumeric: u8 {
             const NORMAL = 0,
             const LINING_NUMS = 0x01,
@@ -1779,18 +1767,16 @@ macro_rules! exclusive_value {
                                 animation_value_type="none")}
 
 <%helpers:longhand name="font-feature-settings" products="gecko" animation_value_type="none"
                    extra_prefixes="moz" boxed="True"
                    spec="https://drafts.csswg.org/css-fonts/#propdef-font-feature-settings">
     use properties::longhands::system_font::SystemFont;
     use std::fmt;
     use style_traits::ToCss;
-    use values::HasViewportPercentage;
-    use values::computed::ComputedValueAsSpecified;
 
     #[derive(Debug, Clone, PartialEq)]
     pub enum SpecifiedValue {
         Value(computed_value::T),
         System(SystemFont)
     }
     no_viewport_percentage!(SpecifiedValue);
 
@@ -1863,18 +1849,16 @@ macro_rules! exclusive_value {
             }
         }
 
         impl Parse for FeatureTagValue {
             /// https://www.w3.org/TR/css-fonts-3/#propdef-font-feature-settings
             /// <string> [ on | off | <integer> ]
             fn parse(_context: &ParserContext, input: &mut Parser) -> Result<Self, ()> {
                 use std::io::Cursor;
-                use std::str;
-                use std::ops::Deref;
                 use byteorder::{ReadBytesExt, BigEndian};
 
                 let tag = try!(input.expect_string());
 
                 // allowed strings of length 4 containing chars: <U+20, U+7E>
                 if tag.len() != 4 ||
                    tag.chars().any(|c| c < ' ' || c > '~')
                 {
@@ -1923,17 +1907,16 @@ macro_rules! exclusive_value {
 
 <%helpers:longhand name="font-language-override" products="gecko" animation_value_type="none"
                    extra_prefixes="moz" boxed="True"
                    spec="https://drafts.csswg.org/css-fonts-3/#propdef-font-language-override">
     use properties::longhands::system_font::SystemFont;
     use std::fmt;
     use style_traits::ToCss;
     use byteorder::{BigEndian, ByteOrder};
-    use values::HasViewportPercentage;
     no_viewport_percentage!(SpecifiedValue);
 
     #[derive(Debug, Clone, PartialEq, Eq)]
     #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
     pub enum SpecifiedValue {
         Normal,
         Override(String),
         System(SystemFont)
@@ -2058,17 +2041,16 @@ macro_rules! exclusive_value {
                 SpecifiedValue::Override(cow.into_owned())
             })
         }
     }
 </%helpers:longhand>
 
 <%helpers:longhand name="-x-lang" products="gecko" animation_value_type="none" internal="True"
                    spec="Internal (not web-exposed)">
-    use values::HasViewportPercentage;
     use values::computed::ComputedValueAsSpecified;
     pub use self::computed_value::T as SpecifiedValue;
 
     impl ComputedValueAsSpecified for SpecifiedValue {}
     no_viewport_percentage!(SpecifiedValue);
 
     pub mod computed_value {
         use Atom;
@@ -2097,22 +2079,20 @@ macro_rules! exclusive_value {
     }
 </%helpers:longhand>
 
 // MathML properties
 <%helpers:longhand name="-moz-script-size-multiplier" products="gecko" animation_value_type="none"
                    predefined_type="Number" gecko_ffi_name="mScriptSizeMultiplier"
                    spec="Internal (not web-exposed)"
                    internal="True" disable_when_testing="True">
-    use values::HasViewportPercentage;
     use values::computed::ComputedValueAsSpecified;
     pub use self::computed_value::T as SpecifiedValue;
 
     impl ComputedValueAsSpecified for SpecifiedValue {}
-    no_viewport_percentage!(SpecifiedValue);
 
     pub mod computed_value {
         pub type T = f32;
     }
 
     #[inline]
     pub fn get_initial_value() -> computed_value::T {
         ::gecko_bindings::structs::NS_MATHML_DEFAULT_SCRIPT_SIZE_MULTIPLIER as f32
@@ -2125,17 +2105,16 @@ macro_rules! exclusive_value {
 </%helpers:longhand>
 
 <%helpers:longhand name="-moz-script-level" products="gecko" animation_value_type="none"
                    predefined_type="Integer" gecko_ffi_name="mScriptLevel"
                    spec="Internal (not web-exposed)"
                    internal="True" disable_when_testing="True" need_clone="True">
     use std::fmt;
     use style_traits::ToCss;
-    use values::HasViewportPercentage;
 
     no_viewport_percentage!(SpecifiedValue);
 
     pub mod computed_value {
         pub type T = i8;
     }
 
     #[inline]
@@ -2225,21 +2204,19 @@ macro_rules! exclusive_value {
 <%helpers:longhand name="-moz-script-min-size" products="gecko" animation_value_type="none"
                    predefined_type="Length" gecko_ffi_name="mScriptMinSize"
                    spec="Internal (not web-exposed)"
                    internal="True" disable_when_testing="True">
     use app_units::Au;
     use gecko_bindings::structs::NS_MATHML_DEFAULT_SCRIPT_MIN_SIZE_PT;
     use std::fmt;
     use style_traits::ToCss;
-    use values::HasViewportPercentage;
-    use values::computed::ComputedValueAsSpecified;
     use values::specified::length::{AU_PER_PT, FontBaseSize, NoCalcLength};
 
-    #[derive(Clone, PartialEq, Debug)]
+    #[derive(Clone, Debug, HasViewportPercentage, PartialEq)]
     pub struct SpecifiedValue(pub NoCalcLength);
 
     pub mod computed_value {
         pub type T = super::Au;
     }
 
 
     impl ToComputedValue for SpecifiedValue {
@@ -2267,22 +2244,16 @@ macro_rules! exclusive_value {
     }
 
     impl ToCss for SpecifiedValue {
         fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
             self.0.to_css(dest)
         }
     }
 
-    impl HasViewportPercentage for SpecifiedValue {
-        fn has_viewport_percentage(&self) -> bool {
-            self.0.has_viewport_percentage()
-        }
-    }
-
     #[inline]
     pub fn get_initial_value() -> computed_value::T {
         Au((NS_MATHML_DEFAULT_SCRIPT_MIN_SIZE_PT as f32 * AU_PER_PT) as i32)
     }
 
     pub fn parse(_context: &ParserContext, _input: &mut Parser) -> Result<SpecifiedValue, ()> {
         debug_assert!(false, "Should be set directly by presentation attributes only.");
         Err(())
--- a/servo/components/style/properties/longhand/inherited_box.mako.rs
+++ b/servo/components/style/properties/longhand/inherited_box.mako.rs
@@ -63,17 +63,16 @@
                    products="gecko"
                    animation_value_type="none"
     spec="https://drafts.csswg.org/css-images/#propdef-image-orientation, \
       /// additional values in https://developer.mozilla.org/en-US/docs/Web/CSS/image-orientation">
     use std::fmt;
     use style_traits::ToCss;
     use values::specified::Angle;
 
-    use values::HasViewportPercentage;
     no_viewport_percentage!(SpecifiedValue);
 
     use std::f32::consts::PI;
     use values::CSSFloat;
     const TWO_PI: CSSFloat = 2.0 * PI;
 
     #[derive(Clone, PartialEq, Copy, Debug)]
     #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
@@ -202,17 +201,16 @@
 <%helpers:longhand name="-servo-under-display-none"
                    derived_from="display"
                    products="servo"
                    animation_value_type="none"
                    spec="Nonstandard (internal layout use only)">
     use std::fmt;
     use style_traits::ToCss;
     use values::computed::ComputedValueAsSpecified;
-    use values::HasViewportPercentage;
 
     no_viewport_percentage!(SpecifiedValue);
 
     #[derive(Copy, Clone, Debug, Eq, PartialEq)]
     #[cfg_attr(feature = "servo", derive(HeapSizeOf, Deserialize, Serialize))]
     pub struct SpecifiedValue(pub bool);
 
     pub mod computed_value {
--- a/servo/components/style/properties/longhand/inherited_svg.mako.rs
+++ b/servo/components/style/properties/longhand/inherited_svg.mako.rs
@@ -139,17 +139,16 @@
 <%helpers:longhand name="paint-order"
                    animation_value_type="none"
                    products="gecko"
                    spec="https://www.w3.org/TR/SVG2/painting.html#PaintOrder">
 
     use values::computed::ComputedValueAsSpecified;
     use std::fmt;
     use style_traits::ToCss;
-    use values::HasViewportPercentage;
 
     pub const NORMAL: u8 = 0;
     pub const FILL: u8 = 1;
     pub const STROKE: u8 = 2;
     pub const MARKERS: u8 = 3;
 
     // number of bits for each component
     pub const SHIFT: u8 = 2;
--- a/servo/components/style/properties/longhand/inherited_table.mako.rs
+++ b/servo/components/style/properties/longhand/inherited_table.mako.rs
@@ -20,17 +20,16 @@
                          animation_value_type="none",
                          spec="https://drafts.csswg.org/css-tables/#propdef-caption-side")}
 
 <%helpers:longhand name="border-spacing" animation_value_type="ComputedValue" boxed="True"
                    spec="https://drafts.csswg.org/css-tables/#propdef-border-spacing">
     use app_units::Au;
     use std::fmt;
     use style_traits::ToCss;
-    use values::HasViewportPercentage;
     use values::specified::{AllowQuirks, Length};
 
     pub mod computed_value {
         use app_units::Au;
         use properties::animated_properties::Animatable;
 
         #[derive(Clone, Copy, Debug, PartialEq)]
         #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
@@ -60,24 +59,17 @@
             #[inline]
             fn compute_squared_distance(&self, other: &Self) -> Result<f64, ()> {
                 Ok(try!(self.horizontal.compute_squared_distance(&other.horizontal)) +
                    try!(self.vertical.compute_squared_distance(&other.vertical)))
             }
         }
     }
 
-    impl HasViewportPercentage for SpecifiedValue {
-        fn has_viewport_percentage(&self) -> bool {
-            self.horizontal.has_viewport_percentage() ||
-            self.vertical.as_ref().map_or(false, |v| v.has_viewport_percentage())
-        }
-    }
-
-    #[derive(Clone, Debug, PartialEq)]
+    #[derive(Clone, Debug, HasViewportPercentage, PartialEq)]
     #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
     pub struct SpecifiedValue {
         pub horizontal: Length,
         pub vertical: Option<Length>,
     }
 
     #[inline]
     pub fn get_initial_value() -> computed_value::T {
--- a/servo/components/style/properties/longhand/inherited_text.mako.rs
+++ b/servo/components/style/properties/longhand/inherited_text.mako.rs
@@ -5,28 +5,18 @@
 <%namespace name="helpers" file="/helpers.mako.rs" />
 <% from data import Keyword %>
 <% data.new_style_struct("InheritedText", inherited=True, gecko_name="Text") %>
 
 <%helpers:longhand name="line-height" animation_value_type="ComputedValue"
                    spec="https://drafts.csswg.org/css2/visudet.html#propdef-line-height">
     use std::fmt;
     use style_traits::ToCss;
-    use values::HasViewportPercentage;
 
-    impl HasViewportPercentage for SpecifiedValue {
-        fn has_viewport_percentage(&self) -> bool {
-            match *self {
-                SpecifiedValue::LengthOrPercentage(ref length) => length.has_viewport_percentage(),
-                _ => false
-            }
-        }
-    }
-
-    #[derive(Debug, Clone, PartialEq)]
+    #[derive(Clone, Debug, HasViewportPercentage, PartialEq)]
     #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
     pub enum SpecifiedValue {
         Normal,
         % if product == "gecko":
             MozBlockHeight,
         % endif
         Number(specified::Number),
         LengthOrPercentage(specified::LengthOrPercentage),
@@ -70,17 +60,16 @@
                 Ok(SpecifiedValue::MozBlockHeight)
             }
             % endif
             _ => Err(()),
         }
     }
     pub mod computed_value {
         use app_units::Au;
-        use std::fmt;
         use values::CSSFloat;
         #[derive(PartialEq, Copy, Clone, Debug)]
         #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
         pub enum T {
             Normal,
             % if product == "gecko":
                 MozBlockHeight,
             % endif
@@ -207,17 +196,16 @@
 // TODO(pcwalton): Support `text-justify: distribute`.
 <%helpers:single_keyword_computed name="text-justify"
                                   values="auto none inter-word"
                                   extra_gecko_values="inter-character"
                                   extra_specified="${'distribute' if product == 'gecko' else ''}"
                                   gecko_enum_prefix="StyleTextJustify"
                                   animation_value_type="none"
                                   spec="https://drafts.csswg.org/css-text/#propdef-text-justify">
-    use values::HasViewportPercentage;
     no_viewport_percentage!(SpecifiedValue);
 
     impl ToComputedValue for SpecifiedValue {
         type ComputedValue = computed_value::T;
 
         #[inline]
         fn to_computed_value(&self, _: &Context) -> computed_value::T {
             match *self {
@@ -251,18 +239,16 @@
                          products="gecko",
                          gecko_constant_prefix="NS_STYLE_TEXT_ALIGN",
                          animation_value_type="none",
                          spec="https://drafts.csswg.org/css-text/#propdef-text-align-last")}
 
 // TODO make this a shorthand and implement text-align-last/text-align-all
 <%helpers:longhand name="text-align" animation_value_type="none" need_clone="True"
                    spec="https://drafts.csswg.org/css-text/#propdef-text-align">
-    use values::computed::ComputedValueAsSpecified;
-    use values::HasViewportPercentage;
     no_viewport_percentage!(SpecifiedValue);
     pub mod computed_value {
         use style_traits::ToCss;
         macro_rules! define_text_align {
             ( $( $name: ident ( $string: expr ) => $discriminant: expr, )+ ) => {
                 define_css_keyword_enum! { T:
                     $(
                         $string => $name,
@@ -395,41 +381,32 @@
             }
 
             #[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;
         pub fn parse(_context: &ParserContext, input: &mut Parser) -> Result<SpecifiedValue, ()> {
             computed_value::T::parse(input)
         }
     % endif
 </%helpers:longhand>
 
 <%helpers:longhand name="letter-spacing" animation_value_type="ComputedValue"
                    spec="https://drafts.csswg.org/css-text/#propdef-letter-spacing">
     use std::fmt;
     use style_traits::ToCss;
-    use values::HasViewportPercentage;
     use values::specified::AllowQuirks;
 
-    impl HasViewportPercentage for SpecifiedValue {
-        fn has_viewport_percentage(&self) -> bool {
-            match *self {
-                SpecifiedValue::Specified(ref length) => length.has_viewport_percentage(),
-                _ => false
-            }
-        }
-    }
-
-    #[derive(Debug, Clone, PartialEq)]
+    #[derive(Clone, Debug, HasViewportPercentage, PartialEq)]
     #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
     pub enum SpecifiedValue {
         Normal,
         Specified(specified::Length),
     }
 
     impl ToCss for SpecifiedValue {
         fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
@@ -493,29 +470,19 @@
         }
     }
 </%helpers:longhand>
 
 <%helpers:longhand name="word-spacing" animation_value_type="ComputedValue"
                    spec="https://drafts.csswg.org/css-text/#propdef-word-spacing">
     use std::fmt;
     use style_traits::ToCss;
-    use values::HasViewportPercentage;
     use values::specified::AllowQuirks;
 
-    impl HasViewportPercentage for SpecifiedValue {
-        fn has_viewport_percentage(&self) -> bool {
-            match *self {
-                SpecifiedValue::Specified(ref length) => length.has_viewport_percentage(),
-                _ => false
-            }
-        }
-    }
-
-    #[derive(Debug, Clone, PartialEq)]
+    #[derive(Clone, Debug, HasViewportPercentage, PartialEq)]
     #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
     pub enum SpecifiedValue {
         Normal,
         Specified(specified::LengthOrPercentage),
     }
 
     impl ToCss for SpecifiedValue {
         fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
@@ -583,17 +550,16 @@
 <%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::HasViewportPercentage;
     use values::computed::ComputedValueAsSpecified;
 
     impl ComputedValueAsSpecified for SpecifiedValue {}
     no_viewport_percentage!(SpecifiedValue);
 
     #[derive(Clone, PartialEq, Copy, Debug)]
     #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
     pub struct SpecifiedValue {
@@ -669,17 +635,16 @@
 <%helpers:single_keyword_computed name="white-space"
                                   values="normal pre nowrap pre-wrap pre-line"
                                   extra_gecko_values="-moz-pre-space"
                                   gecko_constant_prefix="NS_STYLE_WHITESPACE"
                                   needs_conversion="True"
                                   animation_value_type="none"
                                   spec="https://drafts.csswg.org/css-text/#propdef-white-space">
     use values::computed::ComputedValueAsSpecified;
-    use values::HasViewportPercentage;
     impl ComputedValueAsSpecified for SpecifiedValue {}
     no_viewport_percentage!(SpecifiedValue);
 
     % if product != "gecko":
     impl SpecifiedValue {
         pub fn allow_wrap(&self) -> bool {
             match *self {
                 SpecifiedValue::nowrap |
@@ -715,38 +680,22 @@
 
 <%helpers:longhand name="text-shadow"
                    animation_value_type="IntermediateTextShadowList",
                    spec="https://drafts.csswg.org/css-text-decor/#propdef-text-shadow">
     use cssparser;
     use std::fmt;
     use style_traits::ToCss;
     use values::specified::Shadow;
-    use values::HasViewportPercentage;
 
-    impl HasViewportPercentage for SpecifiedValue {
-        fn has_viewport_percentage(&self) -> bool {
-            let &SpecifiedValue(ref vec) = self;
-            vec.iter().any(|ref x| x.has_viewport_percentage())
-        }
-    }
-
-    #[derive(Clone, PartialEq, Debug)]
+    #[derive(Clone, Debug, HasViewportPercentage, PartialEq)]
     #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
     pub struct SpecifiedValue(Vec<SpecifiedTextShadow>);
 
-    impl HasViewportPercentage for SpecifiedTextShadow {
-        fn has_viewport_percentage(&self) -> bool {
-            self.offset_x.has_viewport_percentage() ||
-            self.offset_y.has_viewport_percentage() ||
-            self.blur_radius.has_viewport_percentage()
-        }
-    }
-
-    #[derive(Clone, PartialEq, Debug)]
+    #[derive(Clone, Debug, HasViewportPercentage, PartialEq)]
     #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
     pub struct SpecifiedTextShadow {
         pub offset_x: specified::Length,
         pub offset_y: specified::Length,
         pub blur_radius: specified::Length,
         pub color: Option<specified::CSSColor>,
     }
 
@@ -886,17 +835,16 @@
 
 <%helpers:longhand name="text-emphasis-style" products="gecko" need_clone="True" boxed="True"
                    animation_value_type="none"
                    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;
-    use values::HasViewportPercentage;
 
     no_viewport_percentage!(SpecifiedValue);
 
     pub mod computed_value {
         #[derive(Debug, Clone, PartialEq)]
         #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
         pub enum T {
             Keyword(KeywordValue),
@@ -1092,17 +1040,16 @@
         Ok(SpecifiedValue::Keyword(keyword_value))
     }
 </%helpers:longhand>
 
 <%helpers:longhand name="text-emphasis-position" animation_value_type="none" products="gecko"
                    spec="https://drafts.csswg.org/css-text-decor/#propdef-text-emphasis-position">
     use std::fmt;
     use values::computed::ComputedValueAsSpecified;
-    use values::HasViewportPercentage;
     use style_traits::ToCss;
 
     define_css_keyword_enum!(HorizontalWritingModeValue:
                              "over" => Over,
                              "under" => Under);
     define_css_keyword_enum!(VerticalWritingModeValue:
                              "right" => Right,
                              "left" => Left);
--- a/servo/components/style/properties/longhand/list.mako.rs
+++ b/servo/components/style/properties/longhand/list.mako.rs
@@ -31,17 +31,16 @@
         animation_value_type="none",
         spec="https://drafts.csswg.org/css-lists/#propdef-list-style-type")}
 % else:
     <%helpers:longhand name="list-style-type" animation_value_type="none"
                        spec="https://drafts.csswg.org/css-lists/#propdef-list-style-type">
         use std::fmt;
         use style_traits::ToCss;
         use values::CustomIdent;
-        use values::HasViewportPercentage;
         use values::computed::ComputedValueAsSpecified;
         use values::generics::CounterStyleOrNone;
 
         pub use self::computed_value::T as SpecifiedValue;
 
         pub mod computed_value {
             use values::generics::CounterStyleOrNone;
 
@@ -100,17 +99,16 @@
         }
     </%helpers:longhand>
 % endif
 
 <%helpers:longhand name="list-style-image" animation_value_type="none"
                    boxed="${product == 'gecko'}"
                    spec="https://drafts.csswg.org/css-lists/#propdef-list-style-image">
     use std::fmt;
-    use values::HasViewportPercentage;
     use values::computed::ComputedValueAsSpecified;
     use values::specified::UrlOrNone;
     pub use self::computed_value::T as SpecifiedValue;
     use style_traits::ToCss;
 
     pub mod computed_value {
         use values::specified::UrlOrNone;
 
@@ -153,17 +151,16 @@
 
 <%helpers:longhand name="quotes" animation_value_type="none"
                    spec="https://drafts.csswg.org/css-content/#propdef-quotes">
     use cssparser::Token;
     use std::borrow::Cow;
     use std::fmt;
     use style_traits::ToCss;
     use values::computed::ComputedValueAsSpecified;
-    use values::HasViewportPercentage;
 
     pub use self::computed_value::T as SpecifiedValue;
 
     pub mod computed_value {
         #[derive(Debug, Clone, PartialEq)]
         #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
         pub struct T(pub Vec<(String,String)>);
     }
--- a/servo/components/style/properties/longhand/outline.mako.rs
+++ b/servo/components/style/properties/longhand/outline.mako.rs
@@ -12,21 +12,17 @@
 // TODO(pcwalton): `invert`
 ${helpers.predefined_type("outline-color", "CSSColor", "computed::CSSColor::CurrentColor",
                           initial_specified_value="specified::CSSColor::currentcolor()",
                           animation_value_type="IntermediateColor", complex_color=True, need_clone=True,
                           spec="https://drafts.csswg.org/css-ui/#propdef-outline-color")}
 
 <%helpers:longhand name="outline-style" need_clone="True" animation_value_type="none"
                    spec="https://drafts.csswg.org/css-ui/#propdef-outline-style">
-
-    use std::fmt;
-    use style_traits::ToCss;
     use values::specified::BorderStyle;
-    use values::computed::ComputedValueAsSpecified;
 
     pub type SpecifiedValue = Either<Auto, BorderStyle>;
 
     impl SpecifiedValue {
         #[inline]
         pub fn none_or_hidden(&self) -> bool {
             match *self {
                 Either::First(ref _auto) => false,
@@ -61,41 +57,33 @@
                     Ok(result)
                 }
             })
     }
 </%helpers:longhand>
 
 <%helpers:longhand name="outline-width" animation_value_type="ComputedValue"
                    spec="https://drafts.csswg.org/css-ui/#propdef-outline-width">
-    use app_units::Au;
     use std::fmt;
     use style_traits::ToCss;
-    use values::HasViewportPercentage;
 
     impl ToCss for SpecifiedValue {
         fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
             self.0.to_css(dest)
         }
     }
 
     pub fn parse(context: &ParserContext, input: &mut Parser) -> Result<SpecifiedValue, ()> {
         specified::parse_border_width(context, input).map(SpecifiedValue)
     }
 
-    impl HasViewportPercentage for SpecifiedValue {
-        fn has_viewport_percentage(&self) -> bool {
-            let &SpecifiedValue(ref length) = self;
-            length.has_viewport_percentage()
-        }
-    }
-
-    #[derive(Debug, Clone, PartialEq)]
+    #[derive(Clone, Debug, HasViewportPercentage, PartialEq)]
     #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
     pub struct SpecifiedValue(pub specified::Length);
+
     pub mod computed_value {
         use app_units::Au;
         pub type T = Au;
     }
 
     pub use super::border_top_width::get_initial_value;
     #[inline]
     pub fn get_initial_specified_value() -> SpecifiedValue {
--- a/servo/components/style/properties/longhand/pointing.mako.rs
+++ b/servo/components/style/properties/longhand/pointing.mako.rs
@@ -4,27 +4,28 @@
 
 <%namespace name="helpers" file="/helpers.mako.rs" />
 
 <% data.new_style_struct("Pointing", inherited=True, gecko_name="UserInterface") %>
 
 <%helpers:longhand name="cursor" boxed="${product == 'gecko'}" animation_value_type="none"
   spec="https://drafts.csswg.org/css-ui/#cursor">
     pub use self::computed_value::T as SpecifiedValue;
-    use values::HasViewportPercentage;
     use values::computed::ComputedValueAsSpecified;
+    #[cfg(feature = "gecko")]
     use values::specified::url::SpecifiedUrl;
 
     impl ComputedValueAsSpecified for SpecifiedValue {}
     no_viewport_percentage!(SpecifiedValue);
 
     pub mod computed_value {
         use std::fmt;
         use style_traits::cursor::Cursor;
         use style_traits::ToCss;
+        #[cfg(feature = "gecko")]
         use values::specified::url::SpecifiedUrl;
 
         #[derive(Clone, PartialEq, Copy, Debug)]
         #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
         pub enum Keyword {
             AutoCursor,
             SpecifiedCursor(Cursor),
         }
--- a/servo/components/style/properties/longhand/position.mako.rs
+++ b/servo/components/style/properties/longhand/position.mako.rs
@@ -173,30 +173,26 @@
             // Keyword values are only valid in the inline direction; they must
             // be replaced with auto/none in block.
             <%helpers:longhand name="${min_max}-${size}" spec="${spec % ('%s-%s' % (min_max, size))}"
                                animation_value_type="ComputedValue"
                                logical="${logical}" predefined_type="${MinMax}Length">
 
                 use std::fmt;
                 use style_traits::ToCss;
-                use values::HasViewportPercentage;
-                use values::specified::{AllowQuirks, ${MinMax}Length};
-
-                impl HasViewportPercentage for SpecifiedValue {
-                    fn has_viewport_percentage(&self) -> bool {
-                        self.0.has_viewport_percentage()
-                    }
-                }
+                % if not logical:
+                    use values::specified::AllowQuirks;
+                % endif
+                use values::specified::${MinMax}Length;
 
                 pub mod computed_value {
                     pub type T = ::values::computed::${MinMax}Length;
                 }
 
-                #[derive(PartialEq, Clone, Debug)]
+                #[derive(Clone, Debug, HasViewportPercentage, PartialEq)]
                 #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
                 pub struct SpecifiedValue(${MinMax}Length);
 
                 #[inline]
                 pub fn get_initial_value() -> computed_value::T {
                     use values::computed::${MinMax}Length;
                     ${MinMax}Length::${initial}
                 }
@@ -220,17 +216,19 @@
                         self.0.to_css(dest)
                     }
                 }
 
                 impl ToComputedValue for SpecifiedValue {
                     type ComputedValue = computed_value::T;
                     #[inline]
                     fn to_computed_value(&self, context: &Context) -> computed_value::T {
-                        use values::computed::${MinMax}Length;
+                        % if not logical or "block" in size:
+                            use values::computed::${MinMax}Length;
+                        % endif
                         let computed = self.0.to_computed_value(context);
 
                         // filter out keyword values in the block direction
                         % if logical:
                             % if "block" in size:
                                 if let ${MinMax}Length::ExtremumLength(..) = computed {
                                     return get_initial_value()
                                 }
@@ -333,17 +331,16 @@
 % endfor
 
 <%helpers:longhand name="grid-auto-flow"
         spec="https://drafts.csswg.org/css-grid/#propdef-grid-auto-flow"
         products="gecko"
         animation_value_type="none">
     use std::fmt;
     use style_traits::ToCss;
-    use values::HasViewportPercentage;
     use values::computed::ComputedValueAsSpecified;
 
     pub type SpecifiedValue = computed_value::T;
 
     pub mod computed_value {
         #[derive(PartialEq, Clone, Eq, Copy, Debug)]
         #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
         pub enum AutoFlow {
@@ -425,18 +422,16 @@
         disable_when_testing="True"
         boxed="True">
     use cssparser::serialize_string;
     use std::collections::HashMap;
     use std::fmt;
     use std::ops::Range;
     use str::HTML_SPACE_CHARACTERS;
     use style_traits::ToCss;
-    use style_traits::values::Css;
-    use values::HasViewportPercentage;
     use values::computed::ComputedValueAsSpecified;
 
     pub mod computed_value {
         pub use super::SpecifiedValue as T;
     }
 
     pub type SpecifiedValue = Either<TemplateAreas, None_>;
 
--- a/servo/components/style/properties/longhand/svg.mako.rs
+++ b/servo/components/style/properties/longhand/svg.mako.rs
@@ -72,17 +72,16 @@
                          spec="https://drafts.fxtf.org/css-masking/#propdef-mask-mode")}
 
 <%helpers:vector_longhand name="mask-repeat" products="gecko" animation_value_type="none" extra_prefixes="webkit"
                           spec="https://drafts.fxtf.org/css-masking/#propdef-mask-repeat">
     pub use properties::longhands::background_repeat::single_value::parse;
     pub use properties::longhands::background_repeat::single_value::SpecifiedValue;
     pub use properties::longhands::background_repeat::single_value::computed_value;
     pub use properties::longhands::background_repeat::single_value::RepeatKeyword;
-    use properties::longhands::background_repeat::single_value;
 
     #[inline]
     pub fn get_initial_value() -> computed_value::T {
         computed_value::T(RepeatKeyword::Repeat, RepeatKeyword::Repeat)
     }
 
     #[inline]
     pub fn get_initial_specified_value() -> SpecifiedValue {
--- a/servo/components/style/properties/longhand/table.mako.rs
+++ b/servo/components/style/properties/longhand/table.mako.rs
@@ -9,17 +9,16 @@
 ${helpers.single_keyword("table-layout", "auto fixed",
                          gecko_ffi_name="mLayoutStrategy", animation_value_type="none",
                          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::HasViewportPercentage;
     use values::computed::ComputedValueAsSpecified;
 
     impl ComputedValueAsSpecified for SpecifiedValue {}
     no_viewport_percentage!(SpecifiedValue);
     pub type SpecifiedValue = computed_value::T;
     pub mod computed_value {
         use std::fmt;
         use style_traits::ToCss;
--- a/servo/components/style/properties/longhand/text.mako.rs
+++ b/servo/components/style/properties/longhand/text.mako.rs
@@ -11,17 +11,16 @@
                          additional_methods=[Method("has_underline", "bool"),
                                              Method("has_overline", "bool"),
                                              Method("has_line_through", "bool")]) %>
 
 <%helpers:longhand name="text-overflow" animation_value_type="none" boxed="True"
                    spec="https://drafts.csswg.org/css-ui/#propdef-text-overflow">
     use std::fmt;
     use style_traits::ToCss;
-    use values::HasViewportPercentage;
     use values::computed::ComputedValueAsSpecified;
     use cssparser;
 
     impl ComputedValueAsSpecified for SpecifiedValue {}
     no_viewport_percentage!(SpecifiedValue);
 
     #[derive(PartialEq, Eq, Clone, Debug)]
     #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
@@ -102,17 +101,16 @@
 
 // FIXME: This prop should be animatable.
 <%helpers:longhand name="text-decoration-line"
                    custom_cascade="${product == 'servo'}"
                    animation_value_type="none"
                    spec="https://drafts.csswg.org/css-text-decor/#propdef-text-decoration-line">
     use std::fmt;
     use style_traits::ToCss;
-    use values::HasViewportPercentage;
     use values::computed::ComputedValueAsSpecified;
 
     impl ComputedValueAsSpecified for SpecifiedValue {}
     no_viewport_percentage!(SpecifiedValue);
 
     bitflags! {
         #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
         pub flags SpecifiedValue: u8 {
@@ -232,17 +230,16 @@
     spec="https://drafts.csswg.org/css-text-decor/#propdef-text-decoration-color")}
 
 <%helpers:longhand name="initial-letter"
                    animation_value_type="none"
                    products="gecko"
                    spec="https://drafts.csswg.org/css-inline/#sizing-drop-initials">
     use std::fmt;
     use style_traits::ToCss;
-    use values::HasViewportPercentage;
     use values::computed::ComputedValueAsSpecified;
     use values::specified::{Number, Integer};
 
     impl ComputedValueAsSpecified for SpecifiedValue {}
     no_viewport_percentage!(SpecifiedValue);
 
     #[derive(PartialEq, Clone, Debug)]
     #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
--- a/servo/components/style/properties/longhand/ui.mako.rs
+++ b/servo/components/style/properties/longhand/ui.mako.rs
@@ -30,20 +30,18 @@
                          gecko_enum_prefix="StyleWindowDragging",
                          animation_value_type="none",
                          spec="None (Nonstandard Firefox-only property)")}
 
 <%helpers:longhand name="-moz-force-broken-image-icon"
                    products="gecko"
                    animation_value_type="none"
                    spec="None (Nonstandard Firefox-only property)">
-    use cssparser::Token;
     use std::fmt;
     use style_traits::ToCss;
-    use values::HasViewportPercentage;
     use values::computed::ComputedValueAsSpecified;
 
     no_viewport_percentage!(SpecifiedValue);
 
     pub mod computed_value {
         #[derive(Debug, Clone, Copy, PartialEq)]
         #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
         pub struct T(pub bool);
--- a/servo/components/style/values/computed/mod.rs
+++ b/servo/components/style/values/computed/mod.rs
@@ -170,17 +170,17 @@ impl<T> ToComputedValue for T
 
     #[inline]
     fn from_computed_value(computed: &T) -> Self {
         computed.clone()
     }
 }
 
 /// A computed `<angle>` value.
-#[derive(Clone, PartialEq, PartialOrd, Copy, Debug)]
+#[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),
     /// An angle with radian unit
     Radian(CSSFloat),
--- a/servo/components/style/values/generics/image.rs
+++ b/servo/components/style/values/generics/image.rs
@@ -27,71 +27,71 @@ pub enum Image<Gradient, ImageRect> {
     /// 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, PartialEq, Debug)]
+#[derive(Clone, Debug, HasViewportPercentage, PartialEq)]
 #[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, PartialEq, Debug)]
+#[derive(Clone, Copy, Debug, HasViewportPercentage, PartialEq)]
 #[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, PartialEq)]
+#[derive(Clone, Copy, Debug, HasViewportPercentage, PartialEq)]
 #[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, PartialEq, Debug)]
+#[derive(Clone, Copy, Debug, HasViewportPercentage, PartialEq)]
 #[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, PartialEq, Debug)]
+#[derive(Clone, Copy, Debug, HasViewportPercentage, PartialEq)]
 #[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, PartialEq, Debug)]
+#[derive(Clone, Copy, Debug, HasViewportPercentage, PartialEq)]
 #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
 pub enum Ellipse<LengthOrPercentage> {
     /// An ellipse pair of radii.
     Radii(LengthOrPercentage, LengthOrPercentage),
     /// An ellipse extent.
     Extent(ShapeExtent),
 }
 
@@ -99,31 +99,32 @@ 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
 );
+no_viewport_percentage!(ShapeExtent);
 
 /// A gradient item.
 /// https://drafts.csswg.org/css-images-4/#color-stop-syntax
-#[derive(Clone, Copy, PartialEq, Debug)]
+#[derive(Clone, Copy, Debug, HasViewportPercentage, PartialEq)]
 #[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, PartialEq)]
+#[derive(Clone, Copy, HasViewportPercentage, PartialEq)]
 #[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>,
 }
 
@@ -276,27 +277,16 @@ 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> HasViewportPercentage for Gradient<D, L, LoP, P, C>
-    where L: HasViewportPercentage,
-          LoP: HasViewportPercentage,
-          P: HasViewportPercentage,
-{
-    fn has_viewport_percentage(&self) -> bool {
-        self.kind.has_viewport_percentage() ||
-        self.items.iter().any(|i| i.has_viewport_percentage())
-    }
-}
-
 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,
@@ -328,31 +318,16 @@ impl<D, L, LoP, P> GradientKind<D, L, Lo
     fn label(&self) -> &str {
         match *self {
             GradientKind::Linear(..) => "linear",
             GradientKind::Radial(..) => "radial",
         }
     }
 }
 
-impl<D, L, LoP, P> HasViewportPercentage for GradientKind<D, L, LoP, P>
-    where L: HasViewportPercentage,
-          LoP: HasViewportPercentage,
-          P: HasViewportPercentage
-{
-    fn has_viewport_percentage(&self) -> bool {
-        match *self {
-            GradientKind::Linear(_) => false,
-            GradientKind::Radial(ref shape, ref position) => {
-                shape.has_viewport_percentage() || position.has_viewport_percentage()
-            },
-        }
-    }
-}
-
 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,
@@ -418,32 +393,16 @@ impl<L, LoP> ToCss for EndingShape<L, Lo
                 x.to_css(dest)?;
                 dest.write_str(" ")?;
                 y.to_css(dest)
             },
         }
     }
 }
 
-impl<L, LoP> HasViewportPercentage for EndingShape<L, LoP>
-    where L: HasViewportPercentage, LoP: HasViewportPercentage,
-{
-    fn has_viewport_percentage(&self) -> bool {
-        match *self {
-            EndingShape::Circle(Circle::Radius(ref length)) => {
-                length.has_viewport_percentage()
-            },
-            EndingShape::Ellipse(Ellipse::Radii(ref x, ref y)) => {
-                x.has_viewport_percentage() || y.has_viewport_percentage()
-            },
-            _ => false,
-        }
-    }
-}
-
 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 {
@@ -492,27 +451,16 @@ impl<C, L> ToCss for GradientItem<C, L>
     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> HasViewportPercentage for GradientItem<C, L>
-    where L: HasViewportPercentage,
-{
-    fn has_viewport_percentage(&self) -> bool {
-        match *self {
-            GradientItem::ColorStop(ref stop) => stop.has_viewport_percentage(),
-            GradientItem::InterpolationHint(ref hint) => hint.has_viewport_percentage(),
-        }
-    }
-}
-
 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 {
@@ -557,24 +505,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> HasViewportPercentage for ColorStop<C, L>
-    where L: HasViewportPercentage,
-{
-    fn has_viewport_percentage(&self) -> bool {
-        self.position.as_ref().map_or(false, HasViewportPercentage::has_viewport_percentage)
-    }
-}
-
 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 {
--- a/servo/components/style/values/generics/position.rs
+++ b/servo/components/style/values/generics/position.rs
@@ -1,19 +1,18 @@
 /* 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::HasViewportPercentage;
 use values::computed::{Context, ToComputedValue};
 
-#[derive(Clone, Copy, Debug, PartialEq)]
+#[derive(Clone, Copy, Debug, HasViewportPercentage, PartialEq)]
 #[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,
 }
@@ -23,23 +22,16 @@ impl<H, V> Position<H, V> {
     pub fn new(horizontal: H, vertical: V) -> Self {
         Self {
             horizontal: horizontal,
             vertical: vertical,
         }
     }
 }
 
-impl<H: HasViewportPercentage, V: HasViewportPercentage> HasViewportPercentage for Position<H, V> {
-    #[inline]
-    fn has_viewport_percentage(&self) -> bool {
-        self.horizontal.has_viewport_percentage() || self.vertical.has_viewport_percentage()
-    }
-}
-
 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),
--- a/servo/components/style/values/mod.rs
+++ b/servo/components/style/values/mod.rs
@@ -11,48 +11,36 @@
 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 CSS integer value.
 pub type CSSInteger = i32;
 
 /// The default font size.
 pub const FONT_MEDIUM_PX: i32 = 16;
 
-/// A trait used to query whether this value has viewport units.
-pub trait HasViewportPercentage {
-    /// Returns true if this value has viewport units.
-    fn has_viewport_percentage(&self) -> bool;
-}
-
-impl<T: HasViewportPercentage> HasViewportPercentage for Box<T> {
-    #[inline]
-    fn has_viewport_percentage(&self) -> bool {
-        (**self).has_viewport_percentage()
-    }
-}
-
 define_keyword_type!(None_, "none");
 define_keyword_type!(Auto, "auto");
 define_keyword_type!(Normal, "normal");
 
-#[derive(Clone, PartialEq, Copy)]
+#[derive(Clone, Copy, HasViewportPercentage, PartialEq)]
 #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
 /// A struct representing one of two kinds of values.
 pub enum Either<A, B> {
     /// The first value.
     First(A),
     /// The second kind of value.
     Second(B),
 }
@@ -70,25 +58,16 @@ impl<A: ToCss, B: ToCss> ToCss for Eithe
     fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
         match *self {
             Either::First(ref v) => v.to_css(dest),
             Either::Second(ref v) => v.to_css(dest),
         }
     }
 }
 
-impl<A: HasViewportPercentage, B: HasViewportPercentage> HasViewportPercentage for Either<A, B> {
-    fn has_viewport_percentage(&self) -> bool {
-        match *self {
-            Either::First(ref v) => v.has_viewport_percentage(),
-            Either::Second(ref v) => v.has_viewport_percentage(),
-        }
-    }
-}
-
 impl<A: Parse, B: Parse> Parse for Either<A, B> {
     fn parse(context: &ParserContext, input: &mut Parser) -> Result<Either<A, B>, ()> {
         if let Ok(v) = input.try(|i| A::parse(context, i)) {
             Ok(Either::First(v))
         } else {
             B::parse(context, input).map(Either::Second)
         }
     }
@@ -204,8 +183,9 @@ impl ToCss for KeyframesName {
 
 // A type for possible values for min- and max- flavors of width,
 // height, block-size, and inline-size.
 define_css_keyword_enum!(ExtremumLength:
                          "-moz-max-content" => MaxContent,
                          "-moz-min-content" => MinContent,
                          "-moz-fit-content" => FitContent,
                          "-moz-available" => FillAvailable);
+no_viewport_percentage!(ExtremumLength);
--- a/servo/components/style/values/specified/grid.rs
+++ b/servo/components/style/values/specified/grid.rs
@@ -216,17 +216,17 @@ impl<L: ToComputedValue> ToComputedValue
             TrackBreadth::Breadth(ref lop) =>
                 TrackBreadth::Breadth(ToComputedValue::from_computed_value(lop)),
             TrackBreadth::Flex(fr) => TrackBreadth::Flex(fr),
             TrackBreadth::Keyword(k) => TrackBreadth::Keyword(k),
         }
     }
 }
 
-#[derive(Clone, PartialEq, Debug)]
+#[derive(Clone, Debug, HasViewportPercentage, PartialEq)]
 #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
 /// A `<track-size>` type for explicit grid track sizing. Like `<track-breadth>`, this is
 /// generic only to avoid code bloat. It only takes `<length-percentage>`
 ///
 /// https://drafts.csswg.org/css-grid/#typedef-track-size
 pub enum TrackSize<L> {
     /// A flexible `<track-breadth>`
     Breadth(TrackBreadth<L>),
@@ -316,27 +316,16 @@ impl<L: ToCss> ToCss for TrackSize<L> {
                 dest.write_str("fit-content(")?;
                 lop.to_css(dest)?;
                 dest.write_str(")")
             },
         }
     }
 }
 
-impl HasViewportPercentage for TrackSize<LengthOrPercentage> {
-    #[inline]
-    fn has_viewport_percentage(&self) -> bool {
-        match *self {
-            TrackSize::Breadth(ref b) => b.has_viewport_percentage(),
-            TrackSize::MinMax(ref inf_b, ref b) => inf_b.has_viewport_percentage() || b.has_viewport_percentage(),
-            TrackSize::FitContent(ref lop) => lop.has_viewport_percentage(),
-        }
-    }
-}
-
 impl<L: ToComputedValue> ToComputedValue for TrackSize<L> {
     type ComputedValue = TrackSize<L::ComputedValue>;
 
     #[inline]
     fn to_computed_value(&self, context: &Context) -> Self::ComputedValue {
         match *self {
             TrackSize::Breadth(ref b) => match *b {
                 // <flex> outside `minmax()` expands to `mimmax(auto, <flex>)`
--- a/servo/components/style/values/specified/image.rs
+++ b/servo/components/style/values/specified/image.rs
@@ -49,17 +49,17 @@ pub type Gradient = GenericGradient<
 pub type GradientKind = GenericGradientKind<
     LineDirection,
     Length,
     LengthOrPercentage,
     Position,
 >;
 
 /// A specified gradient line direction.
-#[derive(Clone, Debug, PartialEq)]
+#[derive(Clone, Debug, HasViewportPercentage, PartialEq)]
 #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
 pub enum LineDirection {
     /// An angular direction.
     Angle(Angle),
     /// A horizontal direction.
     Horizontal(X),
     /// A vertical direction.
     Vertical(Y),
--- a/servo/components/style/values/specified/length.rs
+++ b/servo/components/style/values/specified/length.rs
@@ -526,17 +526,17 @@ impl NoCalcLength {
         NoCalcLength::Absolute(AbsoluteLength::Px(px_value))
     }
 }
 
 /// An extension to `NoCalcLength` to parse `calc` expressions.
 /// This is commonly used for the `<length>` values.
 ///
 /// https://drafts.csswg.org/css-values/#lengths
-#[derive(Clone, PartialEq, Debug)]
+#[derive(Clone, Debug, HasViewportPercentage, PartialEq)]
 #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
 pub enum Length {
     /// The internal length type that cannot parse `calc`
     NoCalc(NoCalcLength),
     /// A calc expression.
     ///
     /// https://drafts.csswg.org/css-values/#calc-notation
     Calc(Box<CalcLengthOrPercentage>),
@@ -544,25 +544,16 @@ pub enum Length {
 
 impl From<NoCalcLength> for Length {
     #[inline]
     fn from(len: NoCalcLength) -> Self {
         Length::NoCalc(len)
     }
 }
 
-impl HasViewportPercentage for Length {
-    fn has_viewport_percentage(&self) -> bool {
-        match *self {
-            Length::NoCalc(ref inner) => inner.has_viewport_percentage(),
-            Length::Calc(ref calc) => calc.has_viewport_percentage(),
-        }
-    }
-}
-
 impl ToCss for Length {
     fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
         match *self {
             Length::NoCalc(ref inner) => inner.to_css(dest),
             Length::Calc(ref calc) => calc.to_css(dest),
         }
     }
 }
@@ -708,17 +699,17 @@ impl<T: Parse> Either<Length, T> {
 /// without requiring also a `<length>`. If such a property existed, we'd need
 /// to add special handling for `calc()` and percentages in here in the same way
 /// as for `Angle` and `Time`, but the lack of this this is otherwise
 /// undistinguishable (we handle it correctly from `CalcLengthOrPercentage`).
 ///
 /// As of today, only `-moz-image-rect` supports percentages without length.
 /// This is not a regression, and that's a non-standard extension anyway, so I'm
 /// not implementing it for now.
-#[derive(Clone, PartialEq, Copy, Debug)]
+#[derive(Clone, Copy, Debug, HasViewportPercentage, PartialEq)]
 #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
 pub struct Percentage(pub CSSFloat);
 
 impl ToCss for Percentage {
     fn to_css<W>(&self, dest: &mut W) -> fmt::Result
         where W: fmt::Write,
     {
         write!(dest, "{}%", self.0 * 100.)
@@ -749,17 +740,17 @@ impl Parse for Percentage {
     fn parse(_context: &ParserContext, input: &mut Parser) -> Result<Self, ()> {
         Self::parse_with_clamping_mode(input, AllowedNumericType::All)
     }
 }
 
 impl ComputedValueAsSpecified for Percentage {}
 
 /// A length or a percentage value.
-#[derive(Clone, PartialEq, Debug)]
+#[derive(Clone, Debug, HasViewportPercentage, PartialEq)]
 #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
 #[allow(missing_docs)]
 pub enum LengthOrPercentage {
     Length(NoCalcLength),
     Percentage(Percentage),
     Calc(Box<CalcLengthOrPercentage>),
 }
 
@@ -781,26 +772,16 @@ impl From<NoCalcLength> for LengthOrPerc
 
 impl From<Percentage> for LengthOrPercentage {
     #[inline]
     fn from(pc: Percentage) -> Self {
         LengthOrPercentage::Percentage(pc)
     }
 }
 
-impl HasViewportPercentage for LengthOrPercentage {
-    fn has_viewport_percentage(&self) -> bool {
-        match *self {
-            LengthOrPercentage::Length(ref length) => length.has_viewport_percentage(),
-            LengthOrPercentage::Calc(ref calc) => calc.has_viewport_percentage(),
-            _ => false
-        }
-    }
-}
-
 impl ToCss for LengthOrPercentage {
     fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
         match *self {
             LengthOrPercentage::Length(ref length) => length.to_css(dest),
             LengthOrPercentage::Percentage(percentage) => percentage.to_css(dest),
             LengthOrPercentage::Calc(ref calc) => calc.to_css(dest),
         }
     }
@@ -909,17 +890,17 @@ impl LengthOrPercentage {
     pub fn parse_quirky(context: &ParserContext,
                         input: &mut Parser,
                         allow_quirks: AllowQuirks) -> Result<Self, ()> {
         Self::parse_internal(context, input, AllowedLengthType::All, allow_quirks)
     }
 }
 
 /// Either a `<length>`, a `<percentage>`, or the `auto` keyword.
-#[derive(Clone, PartialEq, Debug)]
+#[derive(Clone, Debug, HasViewportPercentage, PartialEq)]
 #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
 #[allow(missing_docs)]
 pub enum LengthOrPercentageOrAuto {
     Length(NoCalcLength),
     Percentage(Percentage),
     Auto,
     Calc(Box<CalcLengthOrPercentage>),
 }
@@ -933,26 +914,16 @@ impl From<NoCalcLength> for LengthOrPerc
 
 impl From<Percentage> for LengthOrPercentageOrAuto {
     #[inline]
     fn from(pc: Percentage) -> Self {
         LengthOrPercentageOrAuto::Percentage(pc)
     }
 }
 
-impl HasViewportPercentage for LengthOrPercentageOrAuto {
-    fn has_viewport_percentage(&self) -> bool {
-        match *self {
-            LengthOrPercentageOrAuto::Length(ref length) => length.has_viewport_percentage(),
-            LengthOrPercentageOrAuto::Calc(ref calc) => calc.has_viewport_percentage(),
-            _ => false
-        }
-    }
-}
-
 impl ToCss for LengthOrPercentageOrAuto {
     fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
         match *self {
             LengthOrPercentageOrAuto::Length(ref length) => length.to_css(dest),
             LengthOrPercentageOrAuto::Percentage(percentage) => percentage.to_css(dest),
             LengthOrPercentageOrAuto::Auto => dest.write_str("auto"),
             LengthOrPercentageOrAuto::Calc(ref calc) => calc.to_css(dest),
         }
@@ -1031,36 +1002,26 @@ impl LengthOrPercentageOrAuto {
                         input: &mut Parser,
                         allow_quirks: AllowQuirks)
                         -> Result<Self, ()> {
         Self::parse_internal(context, input, AllowedLengthType::All, allow_quirks)
     }
 }
 
 /// Either a `<length>`, a `<percentage>`, or the `none` keyword.
-#[derive(Clone, PartialEq, Debug)]
+#[derive(Clone, Debug, HasViewportPercentage, PartialEq)]
 #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
 #[allow(missing_docs)]
 pub enum LengthOrPercentageOrNone {
     Length(NoCalcLength),
     Percentage(Percentage),
     Calc(Box<CalcLengthOrPercentage>),
     None,
 }
 
-impl HasViewportPercentage for LengthOrPercentageOrNone {
-    fn has_viewport_percentage(&self) -> bool {
-        match *self {
-            LengthOrPercentageOrNone::Length(ref length) => length.has_viewport_percentage(),
-            LengthOrPercentageOrNone::Calc(ref calc) => calc.has_viewport_percentage(),
-            _ => false
-        }
-    }
-}
-
 impl ToCss for LengthOrPercentageOrNone {
     fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
         match *self {
             LengthOrPercentageOrNone::Length(ref length) => length.to_css(dest),
             LengthOrPercentageOrNone::Percentage(ref percentage) => percentage.to_css(dest),
             LengthOrPercentageOrNone::Calc(ref calc) => calc.to_css(dest),
             LengthOrPercentageOrNone::None => dest.write_str("none"),
         }
@@ -1128,17 +1089,17 @@ pub type LengthOrNone = Either<Length, N
 /// Either a `<length>` or the `normal` keyword.
 pub type LengthOrNormal = Either<Length, Normal>;
 
 /// Either a `<length>` or the `auto` keyword.
 pub type LengthOrAuto = Either<Length, Auto>;
 
 /// Either a `<length>` or a `<percentage>` or the `auto` keyword or the
 /// `content` keyword.
-#[derive(Clone, PartialEq, Debug)]
+#[derive(Clone, Debug, HasViewportPercentage, PartialEq)]
 #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
 pub enum LengthOrPercentageOrAutoOrContent {
     /// A `<length>`.
     Length(NoCalcLength),
     /// A percentage.
     Percentage(Percentage),
     /// A `calc` node.
     Calc(Box<CalcLengthOrPercentage>),
@@ -1180,26 +1141,16 @@ impl LengthOrPercentageOrAutoOrContent {
     }
 
     /// Returns a value representing a `0` length.
     pub fn zero() -> Self {
         LengthOrPercentageOrAutoOrContent::Length(NoCalcLength::zero())
     }
 }
 
-impl HasViewportPercentage for LengthOrPercentageOrAutoOrContent {
-    fn has_viewport_percentage(&self) -> bool {
-        match *self {
-            LengthOrPercentageOrAutoOrContent::Length(ref length) => length.has_viewport_percentage(),
-            LengthOrPercentageOrAutoOrContent::Calc(ref calc) => calc.has_viewport_percentage(),
-            _ => false
-        }
-    }
-}
-
 impl ToCss for LengthOrPercentageOrAutoOrContent {
     fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
         match *self {
             LengthOrPercentageOrAutoOrContent::Length(ref len) => len.to_css(dest),
             LengthOrPercentageOrAutoOrContent::Percentage(perc) => perc.to_css(dest),
             LengthOrPercentageOrAutoOrContent::Auto => dest.write_str("auto"),
             LengthOrPercentageOrAutoOrContent::Content => dest.write_str("content"),
             LengthOrPercentageOrAutoOrContent::Calc(ref calc) => calc.to_css(dest),
@@ -1222,34 +1173,25 @@ impl LengthOrNumber {
 
         Length::parse_non_negative(context, input).map(Either::First)
     }
 }
 
 /// A value suitable for a `min-width` or `min-height` property.
 /// Unlike `max-width` or `max-height` properties, a MinLength can be
 /// `auto`, and cannot be `none`.
-#[derive(Debug, Clone, PartialEq)]
+#[derive(Clone, Debug, HasViewportPercentage, PartialEq)]
 #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
 #[allow(missing_docs)]
 pub enum MinLength {
     LengthOrPercentage(LengthOrPercentage),
     Auto,
     ExtremumLength(ExtremumLength),
 }
 
-impl HasViewportPercentage for MinLength {
-    fn has_viewport_percentage(&self) -> bool {
-        match *self {
-            MinLength::LengthOrPercentage(ref lop) => lop.has_viewport_percentage(),
-            _ => false
-        }
-    }
-}
-
 impl ToCss for MinLength {
     fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
         match *self {
             MinLength::LengthOrPercentage(ref lop) =>
                 lop.to_css(dest),
             MinLength::Auto =>
                 dest.write_str("auto"),
             MinLength::ExtremumLength(ref ext) =>
@@ -1272,33 +1214,25 @@ impl MinLength {
         input.try(ExtremumLength::parse).map(MinLength::ExtremumLength)
             .or_else(|()| input.try(|i| LengthOrPercentage::parse_non_negative_quirky(context, i, allow_quirks))
                                .map(MinLength::LengthOrPercentage))
             .or_else(|()| input.expect_ident_matching("auto").map(|()| MinLength::Auto))
     }
 }
 
 /// A value suitable for a `max-width` or `max-height` property.
-#[derive(Debug, Clone, PartialEq)]
+#[derive(Clone, Debug, HasViewportPercentage, PartialEq)]
 #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
 #[allow(missing_docs)]
 pub enum MaxLength {
     LengthOrPercentage(LengthOrPercentage),
     None,
     ExtremumLength(ExtremumLength),
 }
 
-impl HasViewportPercentage for MaxLength {
-    fn has_viewport_percentage(&self) -> bool {
-        match *self {
-            MaxLength::LengthOrPercentage(ref lop) => lop.has_viewport_percentage(),
-            _ => false
-        }
-    }
-}
 
 impl ToCss for MaxLength {
     fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
         match *self {
             MaxLength::LengthOrPercentage(ref lop) =>
                 lop.to_css(dest),
             MaxLength::None =>
                 dest.write_str("none"),
--- a/servo/components/style/values/specified/mod.rs
+++ b/servo/components/style/values/specified/mod.rs
@@ -16,17 +16,17 @@ use self::grid::{TrackBreadth as Generic
 use self::grid::{TrackList as GenericTrackList, TrackSizeOrRepeat};
 use self::url::SpecifiedUrl;
 use std::ascii::AsciiExt;
 use std::f32;
 use std::fmt;
 use std::io::Write;
 use style_traits::ToCss;
 use style_traits::values::specified::AllowedNumericType;
-use super::{Auto, CSSFloat, CSSInteger, HasViewportPercentage, Either, None_};
+use super::{Auto, CSSFloat, CSSInteger, Either, None_};
 use super::computed::{self, Context};
 use super::computed::{Shadow as ComputedShadow, ToComputedValue};
 use super::generics::BorderRadiusSize as GenericBorderRadiusSize;
 use values::specified::calc::CalcNode;
 
 #[cfg(feature = "gecko")]
 pub use self::align::{AlignItems, AlignJustifyContent, AlignJustifySelf, JustifyItems};
 pub use self::color::Color;
@@ -71,18 +71,16 @@ impl Parse for SpecifiedUrl {
 impl Eq for SpecifiedUrl {}
 
 // TODO(emilio): Maybe consider ComputedUrl to save a word in style structs?
 impl ComputedValueAsSpecified for SpecifiedUrl {}
 
 no_viewport_percentage!(SpecifiedUrl);
 }
 
-no_viewport_percentage!(i32);  // For PropertyDeclaration::Order
-
 #[derive(Clone, PartialEq, Debug)]
 #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
 #[allow(missing_docs)]
 pub struct CSSColor {
     pub parsed: Color,
     pub authored: Option<Box<str>>,
 }
 
@@ -284,17 +282,17 @@ impl Parse for BorderRadiusSize {
     fn parse(context: &ParserContext, input: &mut Parser) -> Result<Self, ()> {
         let first = try!(LengthOrPercentage::parse_non_negative(context, input));
         let second = input.try(|i| LengthOrPercentage::parse_non_negative(context, i))
             .unwrap_or_else(|()| first.clone());
         Ok(GenericBorderRadiusSize(Size2D::new(first, second)))
     }
 }
 
-#[derive(Clone, PartialEq, Copy, Debug)]
+#[derive(Clone, Copy, Debug, HasViewportPercentage, PartialEq)]
 #[cfg_attr(feature = "servo", derive(HeapSizeOf, Deserialize, Serialize))]
 /// An angle consisting of a value and a unit.
 ///
 /// Computed Angle is essentially same as specified angle except calc
 /// value serialization. Therefore we are using computed Angle enum
 /// to hold the value and unit type.
 pub struct Angle {
     value: computed::Angle,
@@ -450,17 +448,17 @@ pub fn parse_border_width(context: &Pars
             "thin" => Ok(Length::from_px(1.)),
             "medium" => Ok(Length::from_px(3.)),
             "thick" => Ok(Length::from_px(5.)),
             _ => Err(())
         }
     })
 }
 
-#[derive(Clone, PartialEq, Debug)]
+#[derive(Clone, Debug, HasViewportPercentage, PartialEq)]
 #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
 #[allow(missing_docs)]
 pub enum BorderWidth {
     Thin,
     Medium,
     Thick,
     Width(Length),
 }
@@ -502,25 +500,16 @@ impl ToCss for BorderWidth {
             BorderWidth::Thin => dest.write_str("thin"),
             BorderWidth::Medium => dest.write_str("medium"),
             BorderWidth::Thick => dest.write_str("thick"),
             BorderWidth::Width(ref length) => length.to_css(dest)
         }
     }
 }
 
-impl HasViewportPercentage for BorderWidth {
-    fn has_viewport_percentage(&self) -> bool {
-        match *self {
-            BorderWidth::Thin | BorderWidth::Medium | BorderWidth::Thick => false,
-            BorderWidth::Width(ref length) => length.has_viewport_percentage()
-        }
-    }
-}
-
 impl ToComputedValue for BorderWidth {
     type ComputedValue = Au;
 
     #[inline]
     fn to_computed_value(&self, context: &Context) -> Self::ComputedValue {
         // We choose the pixel length of the keyword values the same as both spec and gecko.
         // Spec: https://drafts.csswg.org/css-backgrounds-3/#line-width
         // Gecko: https://bugzilla.mozilla.org/show_bug.cgi?id=1312155#c0
@@ -945,37 +934,28 @@ pub type TrackSize = GenericTrackSize<Le
 
 /// The specified value of a grid `<track-list>`
 /// (could also be `<auto-track-list>` or `<explicit-track-list>`)
 pub type TrackList = GenericTrackList<TrackSizeOrRepeat>;
 
 /// `<track-list> | none`
 pub type TrackListOrNone = Either<TrackList, None_>;
 
-#[derive(Debug, Clone, PartialEq)]
+#[derive(Clone, Debug, HasViewportPercentage, PartialEq)]
 #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
 #[allow(missing_docs)]
 pub struct Shadow {
     pub offset_x: Length,
     pub offset_y: Length,
     pub blur_radius: Length,
     pub spread_radius: Length,
     pub color: Option<CSSColor>,
     pub inset: bool,
 }
 
-impl HasViewportPercentage for Shadow {
-    fn has_viewport_percentage(&self) -> bool {
-        self.offset_x.has_viewport_percentage() ||
-        self.offset_y.has_viewport_percentage() ||
-        self.blur_radius.has_viewport_percentage() ||
-        self.spread_radius.has_viewport_percentage()
-    }
-}
-
 impl ToComputedValue for Shadow {
     type ComputedValue = ComputedShadow;
 
     #[inline]
     fn to_computed_value(&self, context: &Context) -> Self::ComputedValue {
         ComputedShadow {
             offset_x: self.offset_x.to_computed_value(context),
             offset_y: self.offset_y.to_computed_value(context),
@@ -1225,26 +1205,17 @@ impl LengthOrPercentageOrNumber {
         if let Ok(num) = input.try(|i| Number::parse_non_negative(context, i)) {
             return Ok(Either::First(num))
         }
 
         LengthOrPercentage::parse_non_negative(context, input).map(Either::Second)
     }
 }
 
-impl HasViewportPercentage for ClipRect {
-    fn has_viewport_percentage(&self) -> bool {
-        self.top.as_ref().map_or(false, |x| x.has_viewport_percentage()) ||
-        self.right.as_ref().map_or(false, |x| x.has_viewport_percentage()) ||
-        self.bottom.as_ref().map_or(false, |x| x.has_viewport_percentage()) ||
-        self.left.as_ref().map_or(false, |x| x.has_viewport_percentage())
-    }
-}
-
-#[derive(Clone, Debug, PartialEq)]
+#[derive(Clone, Debug, HasViewportPercentage, PartialEq)]
 #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
 /// rect(<top>, <left>, <bottom>, <right>) used by clip and image-region
 pub struct ClipRect {
     /// <top> (<length> | <auto>)
     pub top: Option<Length>,
     /// <right> (<length> | <auto>)
     pub right: Option<Length>,
     /// <bottom> (<length> | <auto>)
copy from servo/components/domobject_derive/Cargo.toml
copy to servo/components/style_derive/Cargo.toml
--- a/servo/components/domobject_derive/Cargo.toml
+++ b/servo/components/style_derive/Cargo.toml
@@ -1,14 +1,15 @@
 [package]
-name = "domobject_derive"
+name = "style_derive"
 version = "0.0.1"
 authors = ["The Servo Project Developers"]
 license = "MPL-2.0"
 publish = false
 
 [lib]
 path = "lib.rs"
 proc-macro = true
 
 [dependencies]
+quote = "0.3"
 syn = "0.11"
-quote = "0.3"
+synstructure = "0.5.2"
new file mode 100644
--- /dev/null
+++ b/servo/components/style_derive/has_viewport_percentage.rs
@@ -0,0 +1,56 @@
+/* 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())))
+    }
+
+    let style = synstructure::BindStyle::Ref.into();
+    let match_body = synstructure::each_variant(&input, &style, |bindings, _| {
+        let (first, rest) = match bindings.split_first() {
+            None => return Some(quote!(false)),
+            Some(pair) => pair,
+        };
+        let mut expr = quote!(::style_traits::HasViewportPercentage::has_viewport_percentage(#first));
+        for binding in rest {
+            where_clause.predicates.push(where_predicate(binding.field.ty.clone()));
+            expr = quote!(#expr || ::style_traits::HasViewportPercentage::has_viewport_percentage(#binding));
+        }
+        Some(expr)
+    });
+
+    quote! {
+        impl #impl_generics ::style_traits::HasViewportPercentage for #name #ty_generics #where_clause {
+            #[allow(unused_variables, unused_imports)]
+            #[inline]
+            fn has_viewport_percentage(&self) -> bool {
+                match *self {
+                    #match_body
+                }
+            }
+        }
+    }
+}
+
+fn where_predicate(ty: 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: syn::parse_path("::style_traits::HasViewportPercentage").unwrap(),
+            },
+            syn::TraitBoundModifier::None
+        )],
+    })
+}
new file mode 100644
--- /dev/null
+++ b/servo/components/style_derive/lib.rs
@@ -0,0 +1,18 @@
+/* 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/. */
+
+extern crate proc_macro;
+#[macro_use] extern crate quote;
+extern crate syn;
+extern crate synstructure;
+
+use proc_macro::TokenStream;
+
+mod has_viewport_percentage;
+
+#[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()
+}
--- a/servo/components/style_traits/lib.rs
+++ b/servo/components/style_traits/lib.rs
@@ -58,11 +58,13 @@ pub enum CSSPixel {}
 //
 // DevicePixel
 //   / hidpi_ratio => DeviceIndependentPixel
 //     / desktop_zoom => CSSPixel
 
 pub mod cursor;
 #[macro_use]
 pub mod values;
+#[macro_use]
 pub mod viewport;
 
 pub use values::{ToCss, OneOrMoreCommaSeparated};
+pub use viewport::HasViewportPercentage;
--- a/servo/components/style_traits/viewport.rs
+++ b/servo/components/style_traits/viewport.rs
@@ -15,16 +15,58 @@ define_css_keyword_enum!(UserZoom:
                          "zoom" => Zoom,
                          "fixed" => Fixed);
 
 define_css_keyword_enum!(Orientation:
                          "auto" => Auto,
                          "portrait" => Portrait,
                          "landscape" => Landscape);
 
+/// A trait used to query whether this value has viewport units.
+pub trait HasViewportPercentage {
+    /// Returns true if this value has viewport units.
+    fn has_viewport_percentage(&self) -> bool;
+}
+
+/// A macro used to implement HasViewportPercentage trait
+/// for a given type that may never contain viewport units.
+#[macro_export]
+macro_rules! no_viewport_percentage {
+    ($($name: ident),+) => {
+        $(impl $crate::HasViewportPercentage for $name {
+            #[inline]
+            fn has_viewport_percentage(&self) -> bool {
+                false
+            }
+        })+
+    };
+}
+
+no_viewport_percentage!(bool, f32);
+
+impl<T: HasViewportPercentage> HasViewportPercentage for Box<T> {
+    #[inline]
+    fn has_viewport_percentage(&self) -> bool {
+        (**self).has_viewport_percentage()
+    }
+}
+
+impl<T: HasViewportPercentage> HasViewportPercentage for Option<T> {
+    #[inline]
+    fn has_viewport_percentage(&self) -> bool {
+        self.as_ref().map_or(false, T::has_viewport_percentage)
+    }
+}
+
+impl<T: HasViewportPercentage> HasViewportPercentage for Vec<T> {
+    #[inline]
+    fn has_viewport_percentage(&self) -> bool {
+        self.iter().any(T::has_viewport_percentage)
+    }
+}
 
 /// A set of viewport descriptors:
 ///
 /// https://drafts.csswg.org/css-device-adapt/#viewport-desc
 #[derive(Clone, Debug, PartialEq)]
 #[cfg_attr(feature = "servo", derive(Deserialize, Serialize, HeapSizeOf))]
 pub struct ViewportConstraints {
     /// Width and height:
--- a/servo/tests/unit/style/attr.rs
+++ b/servo/tests/unit/style/attr.rs
@@ -1,16 +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/. */
 
 use app_units::Au;
 use style::attr::{AttrValue, LengthOrPercentageOrAuto, parse_length};
 use style::values::computed::CalcLengthOrPercentage;
-use style_traits::values::specified::AllowedLengthType;
 
 #[test]
 fn test_length_calc() {
     let calc = CalcLengthOrPercentage::new(Au(10), Some(0.2));
     assert_eq!(calc.to_used_value(Some(Au(10))), Some(Au(12)));
     assert_eq!(calc.to_used_value(Some(Au(0))), Some(Au(10)));
     assert_eq!(calc.to_used_value(None), None);
 
--- a/servo/tests/unit/style/restyle_hints.rs
+++ b/servo/tests/unit/style/restyle_hints.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/. */
 
 #[test]
 fn smoke_restyle_hints() {
     use cssparser::Parser;
     use selectors::parser::SelectorList;
-    use style::restyle_hints::{DependencySet, RESTYLE_LATER_SIBLINGS};
+    use style::restyle_hints::DependencySet;
     use style::selector_parser::SelectorParser;
     use style::stylesheets::{Origin, Namespaces};
     let namespaces = Namespaces::default();
     let parser = SelectorParser {
         stylesheet_origin: Origin::Author,
         namespaces: &namespaces,
     };
 
--- a/servo/tests/unit/style/stylist.rs
+++ b/servo/tests/unit/style/stylist.rs
@@ -207,14 +207,14 @@ fn test_insert() {
     selector_map.insert(rules_list[0][0].clone());
     assert_eq!(0, selector_map.class_hash.get(&Atom::from("foo")).unwrap()[0].source_order);
     assert!(selector_map.class_hash.get(&Atom::from("intro")).is_none());
 }
 
 #[test]
 fn test_get_universal_rules() {
     thread_state::initialize(thread_state::LAYOUT);
-    let (map, shared_lock) = get_mock_map(&["*|*", "#foo > *|*", "*|* > *|*", ".klass", "#id"]);
+    let (map, _shared_lock) = get_mock_map(&["*|*", "#foo > *|*", "*|* > *|*", ".klass", "#id"]);
 
     let decls = map.get_universal_rules(CascadeLevel::UserNormal);
 
     assert_eq!(decls.len(), 1, "{:?}", decls);
 }