Bug 1455576 part 2 - Add a ValueInfo trait for exposing types needed by devtools. r?emilio draft
authorXidorn Quan <me@upsuper.org>
Mon, 23 Apr 2018 20:45:04 +1000
changeset 787542 36b938680d729709725a5872276cdf5b545891b3
parent 787541 452a68930d96300a0ac35f1a261f72a2fa04e513
child 787543 76eea9b3819f17b1338e137bd19ec8feba0bca58
push id107742
push userxquan@mozilla.com
push dateTue, 24 Apr 2018 23:00:03 +0000
reviewersemilio
bugs1455576, 1434130
milestone61.0a1
Bug 1455576 part 2 - Add a ValueInfo trait for exposing types needed by devtools. r?emilio Most of types just derive it using proc_macro directly. Some of value types need manual impl. In my current plan, this new trait will be used in bug 1434130 to expose values as well. MozReview-Commit-ID: LI7fy45VkRw
servo/components/style/gecko/url.rs
servo/components/style/gecko_string_cache/mod.rs
servo/components/style/macros.rs
servo/components/style/properties/helpers.mako.rs
servo/components/style/properties/helpers/animated_properties.mako.rs
servo/components/style/properties/longhand/font.mako.rs
servo/components/style/properties/properties.mako.rs
servo/components/style/values/computed/length.rs
servo/components/style/values/computed/percentage.rs
servo/components/style/values/generics/background.rs
servo/components/style/values/generics/basic_shape.rs
servo/components/style/values/generics/border.rs
servo/components/style/values/generics/box.rs
servo/components/style/values/generics/column.rs
servo/components/style/values/generics/counters.rs
servo/components/style/values/generics/effects.rs
servo/components/style/values/generics/flex.rs
servo/components/style/values/generics/font.rs
servo/components/style/values/generics/gecko.rs
servo/components/style/values/generics/grid.rs
servo/components/style/values/generics/image.rs
servo/components/style/values/generics/mod.rs
servo/components/style/values/generics/pointing.rs
servo/components/style/values/generics/position.rs
servo/components/style/values/generics/rect.rs
servo/components/style/values/generics/size.rs
servo/components/style/values/generics/svg.rs
servo/components/style/values/generics/text.rs
servo/components/style/values/generics/transform.rs
servo/components/style/values/generics/url.rs
servo/components/style/values/mod.rs
servo/components/style/values/specified/align.rs
servo/components/style/values/specified/angle.rs
servo/components/style/values/specified/background.rs
servo/components/style/values/specified/border.rs
servo/components/style/values/specified/box.rs
servo/components/style/values/specified/calc.rs
servo/components/style/values/specified/color.rs
servo/components/style/values/specified/counters.rs
servo/components/style/values/specified/effects.rs
servo/components/style/values/specified/font.rs
servo/components/style/values/specified/grid.rs
servo/components/style/values/specified/image.rs
servo/components/style/values/specified/inherited_box.rs
servo/components/style/values/specified/length.rs
servo/components/style/values/specified/list.rs
servo/components/style/values/specified/mod.rs
servo/components/style/values/specified/outline.rs
servo/components/style/values/specified/percentage.rs
servo/components/style/values/specified/position.rs
servo/components/style/values/specified/svg.rs
servo/components/style/values/specified/table.rs
servo/components/style/values/specified/text.rs
servo/components/style/values/specified/time.rs
servo/components/style/values/specified/transform.rs
servo/components/style/values/specified/ui.rs
servo/components/style_derive/animate.rs
servo/components/style_derive/cg.rs
servo/components/style_derive/compute_squared_distance.rs
servo/components/style_derive/lib.rs
servo/components/style_derive/parse.rs
servo/components/style_derive/specified_value_info.rs
servo/components/style_derive/to_animated_zero.rs
servo/components/style_derive/to_css.rs
servo/components/style_traits/cursor.rs
servo/components/style_traits/lib.rs
servo/components/style_traits/specified_value_info.rs
--- a/servo/components/style/gecko/url.rs
+++ b/servo/components/style/gecko/url.rs
@@ -10,17 +10,17 @@ use gecko_bindings::structs::{ServoBundl
 use gecko_bindings::structs::mozilla::css::URLValueData;
 use gecko_bindings::structs::root::{RustString, nsStyleImageRequest};
 use gecko_bindings::structs::root::mozilla::css::{ImageValue, URLValue};
 use gecko_bindings::sugar::refptr::RefPtr;
 use malloc_size_of::{MallocSizeOf, MallocSizeOfOps};
 use parser::{Parse, ParserContext};
 use servo_arc::{Arc, RawOffsetArc};
 use std::mem;
-use style_traits::ParseError;
+use style_traits::{ParseError, SpecifiedValueInfo};
 
 /// A CSS url() value for gecko.
 #[css(function = "url")]
 #[derive(Clone, Debug, PartialEq, ToCss)]
 pub struct CssUrl {
     /// The URL in unresolved string form.
     ///
     /// Refcounted since cloning this should be cheap and data: uris can be
@@ -115,18 +115,20 @@ impl MallocSizeOf for CssUrl {
 
         // We ignore `extra_data`, because RefPtr is tricky, and there aren't
         // many of them in practise (sharing is common).
 
         0
     }
 }
 
+impl SpecifiedValueInfo for CssUrl {}
+
 /// A specified url() value for general usage.
-#[derive(Clone, Debug, ToComputedValue, ToCss)]
+#[derive(Clone, Debug, SpecifiedValueInfo, ToComputedValue, ToCss)]
 pub struct SpecifiedUrl {
     /// The specified url value.
     pub url: CssUrl,
     /// Gecko's URLValue so that we can reuse it while rematching a
     /// property with this specified value.
     #[css(skip)]
     pub url_value: RefPtr<URLValue>,
 }
@@ -174,17 +176,17 @@ impl MallocSizeOf for SpecifiedUrl {
         n += unsafe { bindings::Gecko_URLValue_SizeOfIncludingThis(self.url_value.get()) };
         n
     }
 }
 
 /// A specified url() value for image.
 ///
 /// This exists so that we can construct `ImageValue` and reuse it.
-#[derive(Clone, Debug, ToComputedValue, ToCss)]
+#[derive(Clone, Debug, SpecifiedValueInfo, ToComputedValue, ToCss)]
 pub struct SpecifiedImageUrl {
     /// The specified url value.
     pub url: CssUrl,
     /// Gecko's ImageValue so that we can reuse it while rematching a
     /// property with this specified value.
     #[css(skip)]
     pub image_value: RefPtr<ImageValue>,
 }
--- a/servo/components/style/gecko_string_cache/mod.rs
+++ b/servo/components/style/gecko_string_cache/mod.rs
@@ -15,16 +15,17 @@ use nsstring::{nsAString, nsStr};
 use precomputed_hash::PrecomputedHash;
 use std::{mem, slice, str};
 use std::borrow::{Borrow, Cow};
 use std::char::{self, DecodeUtf16};
 use std::fmt::{self, Write};
 use std::hash::{Hash, Hasher};
 use std::iter::Cloned;
 use std::ops::Deref;
+use style_traits::SpecifiedValueInfo;
 
 #[macro_use]
 #[allow(improper_ctypes, non_camel_case_types, missing_docs)]
 pub mod atom_macro {
     include!(concat!(env!("OUT_DIR"), "/gecko/atom_macro.rs"));
 }
 
 #[macro_use]
@@ -410,8 +411,10 @@ impl<'a> From<Cow<'a, str>> for Atom {
 impl From<String> for Atom {
     #[inline]
     fn from(string: String) -> Atom {
         Atom::from(&*string)
     }
 }
 
 malloc_size_of_is_0!(Atom);
+
+impl SpecifiedValueInfo for Atom {}
--- a/servo/components/style/macros.rs
+++ b/servo/components/style/macros.rs
@@ -63,18 +63,19 @@ macro_rules! try_match_ident_ignore_asci
             ))
         }
     }}
 }
 
 macro_rules! define_keyword_type {
     ($name:ident, $css:expr) => {
         #[allow(missing_docs)]
-        #[derive(Animate, Clone, ComputeSquaredDistance, Copy, MallocSizeOf, PartialEq,
-                 ToAnimatedValue, ToAnimatedZero, ToComputedValue, ToCss)]
+        #[derive(Animate, Clone, ComputeSquaredDistance, Copy, MallocSizeOf,
+                 PartialEq, SpecifiedValueInfo, ToAnimatedValue, ToAnimatedZero,
+                 ToComputedValue, ToCss)]
         pub struct $name;
 
         impl fmt::Debug for $name {
             fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
                 f.write_str($css)
             }
         }
 
--- a/servo/components/style/properties/helpers.mako.rs
+++ b/servo/components/style/properties/helpers.mako.rs
@@ -159,17 +159,17 @@
                 }
             }
         }
 
         /// The specified value of ${name}.
         % if separator == "Comma":
         #[css(comma)]
         % endif
-        #[derive(Clone, Debug, MallocSizeOf, PartialEq, ToCss)]
+        #[derive(Clone, Debug, MallocSizeOf, PartialEq, SpecifiedValueInfo, ToCss)]
         pub struct SpecifiedValue(
             % if not allow_empty:
             #[css(iterable)]
             % else:
             #[css(if_empty = "none", iterable)]
             % endif
             pub Vec<single_value::SpecifiedValue>,
         );
@@ -453,16 +453,18 @@
             pub fn get_system(&self) -> Option<SystemFont> {
                 if let SpecifiedValue::System(s) = *self {
                     Some(s)
                 } else {
                     None
                 }
             }
         }
+
+        impl ::style_traits::SpecifiedValueInfo for SpecifiedValue {}
     </%call>
 </%def>
 
 <%def name="gecko_keyword_conversion(keyword, values=None, type='SpecifiedValue', cast_to=None)">
     <%
         if not values:
             values = keyword.values_for(product)
         maybe_cast = "as %s" % cast_to if cast_to else ""
@@ -583,16 +585,17 @@
         pub fn get_initial_specified_value() -> SpecifiedValue {
             SpecifiedValue::${to_camel_case(values.split()[0])}
         }
         #[inline]
         pub fn parse<'i, 't>(_context: &ParserContext, input: &mut Parser<'i, 't>)
                              -> Result<SpecifiedValue, ParseError<'i>> {
             SpecifiedValue::parse(input)
         }
+        impl ::style_traits::SpecifiedValueInfo for SpecifiedValue {}
 
         % if needs_conversion:
             <%
                 conversion_values = keyword.values_for(product)
                 if extra_specified:
                     conversion_values += extra_specified.split()
                 conversion_values += keyword.aliases_for(product).keys()
             %>
@@ -629,17 +632,17 @@
         use properties::{PropertyDeclaration, SourcePropertyDeclaration, MaybeBoxed, longhands};
         #[allow(unused_imports)]
         use selectors::parser::SelectorParseErrorKind;
         #[allow(unused_imports)]
         use std::fmt::{self, Write};
         #[allow(unused_imports)]
         use style_traits::{ParseError, StyleParseErrorKind};
         #[allow(unused_imports)]
-        use style_traits::{CssWriter, ToCss};
+        use style_traits::{CssWriter, SpecifiedValueInfo, ToCss};
 
         pub struct Longhands {
             % for sub_property in shorthand.sub_properties:
                 pub ${sub_property.ident}:
                     % if sub_property.boxed:
                         Box<
                     % endif
                     longhands::${sub_property.ident}::SpecifiedValue
@@ -737,16 +740,36 @@
                     ));
                 % if sub_property.may_be_disabled_in(shorthand, product):
                 }
                 % endif
                 % endfor
             })
         }
 
+        <%
+            sub_properties_for_value_info = shorthand.sub_properties
+            if shorthand.name == "border":
+                # border-image subproperties are simply reset by border
+                # shorthand, so border cannot accept values of them.
+                # XXX We may want a better mechanism for this, but this
+                # is probably fine for now.
+                sub_properties_for_value_info = [
+                    subprop for subprop in shorthand.sub_properties
+                    if not subprop.name.startswith("border-image")
+                ]
+        %>
+        impl SpecifiedValueInfo for Longhands {
+            const SUPPORTED_TYPES: u8 = 0
+                % for subprop in sub_properties_for_value_info:
+                | <longhands::${subprop.ident}::SpecifiedValue as SpecifiedValueInfo>::SUPPORTED_TYPES
+                % endfor
+                ;
+        }
+
         ${caller.body()}
     }
     % endif
 </%def>
 
 <%def name="four_sides_shorthand(name, sub_property_pattern, parser_function,
                                  needs_context=True, allow_quirks=False, **kwargs)">
     <% sub_properties=' '.join(sub_property_pattern % side for side in ['top', 'right', 'bottom', 'left']) %>
--- a/servo/components/style/properties/helpers/animated_properties.mako.rs
+++ b/servo/components/style/properties/helpers/animated_properties.mako.rs
@@ -22,17 +22,17 @@ use properties::longhands::font_weight::
 use properties::longhands::visibility::computed_value::T as Visibility;
 use properties::PropertyId;
 use properties::{LonghandId, ShorthandId};
 use servo_arc::Arc;
 use smallvec::SmallVec;
 use std::{cmp, ptr};
 use std::mem::{self, ManuallyDrop};
 #[cfg(feature = "gecko")] use hash::FnvHashMap;
-use style_traits::ParseError;
+use style_traits::{ParseError, SpecifiedValueInfo};
 use super::ComputedValues;
 use values::{CSSFloat, CustomIdent, Either};
 use values::animated::{Animate, Procedure, ToAnimatedValue, ToAnimatedZero};
 use values::animated::color::RGBA as AnimatedRGBA;
 use values::animated::effects::Filter as AnimatedFilter;
 use values::animated::effects::FilterList as AnimatedFilterList;
 use values::computed::{Angle, CalcLengthOrPercentage};
 use values::computed::{ClipRect, Context};
@@ -167,16 +167,18 @@ impl From<nsCSSPropertyID> for Transitio
             }
             _ => {
                 panic!("non-convertible nsCSSPropertyID")
             }
         }
     }
 }
 
+impl SpecifiedValueInfo for TransitionProperty {}
+
 /// Returns true if this nsCSSPropertyID is one of the transitionable properties.
 #[cfg(feature = "gecko")]
 pub fn nscsspropertyid_is_transitionable(property: nsCSSPropertyID) -> bool {
     match property {
         % for prop in data.longhands + data.shorthands_except_all():
             % if prop.transitionable:
                 ${prop.nscsspropertyid()} => true,
             % endif
--- a/servo/components/style/properties/longhand/font.mako.rs
+++ b/servo/components/style/properties/longhand/font.mako.rs
@@ -291,17 +291,18 @@ https://drafts.csswg.org/css-fonts-4/#lo
                               -moz-list -moz-field""".split()
             kw_font_props = """font_variant_caps
                                font_kerning font_variant_position font_variant_ligatures
                                font_variant_east_asian font_variant_numeric
                                font_optical_sizing""".split()
             kw_cast = """font_variant_caps font_kerning font_variant_position
                          font_optical_sizing""".split()
         %>
-        #[derive(Clone, Copy, Debug, Eq, Hash, MallocSizeOf, PartialEq, ToCss)]
+        #[derive(Clone, Copy, Debug, Eq, Hash, MallocSizeOf, PartialEq,
+                 SpecifiedValueInfo, ToCss)]
         pub enum SystemFont {
             % for font in system_fonts:
                 ${to_camel_case(font)},
             % endfor
         }
 
         // ComputedValues are compared at times
         // so we need these impls. We don't want to
--- a/servo/components/style/properties/properties.mako.rs
+++ b/servo/components/style/properties/properties.mako.rs
@@ -37,17 +37,18 @@ use logical_geometry::WritingMode;
 use media_queries::Device;
 use parser::ParserContext;
 #[cfg(feature = "gecko")] use properties::longhands::system_font::SystemFont;
 use rule_cache::{RuleCache, RuleCacheConditions};
 use selector_parser::PseudoElement;
 use selectors::parser::SelectorParseErrorKind;
 #[cfg(feature = "servo")] use servo_config::prefs::PREFS;
 use shared_lock::StylesheetGuards;
-use style_traits::{CssWriter, ParseError, ParsingMode, StyleParseErrorKind, ToCss};
+use style_traits::{CssWriter, ParseError, ParsingMode};
+use style_traits::{SpecifiedValueInfo, StyleParseErrorKind, ToCss};
 use stylesheets::{CssRuleType, Origin, UrlExtraData};
 #[cfg(feature = "servo")] use values::Either;
 use values::generics::text::LineHeight;
 use values::computed;
 use values::computed::NonNegativeLength;
 use rule_tree::{CascadeLevel, StrongRuleNode};
 use self::computed_value_flags::*;
 use str::{CssString, CssStringBorrow, CssStringWriter};
@@ -536,16 +537,34 @@ impl NonCustomPropertyId {
         }
 
         if context.chrome_rules_enabled() && ENABLED_IN_CHROME.contains(self) {
             return true
         }
 
         false
     }
+
+    /// The supported types of this property. The return value should be
+    /// style_traits::CssType when it can become a bitflags type.
+    fn supported_types(&self) -> u8 {
+        const SUPPORTED_TYPES: [u8; ${len(data.longhands) + len(data.shorthands)}] = [
+            % for prop in data.longhands:
+                <${prop.specified_type()} as SpecifiedValueInfo>::SUPPORTED_TYPES,
+            % endfor
+            % for prop in data.shorthands:
+            % if prop.name == "all":
+                0, // 'all' accepts no value other than CSS-wide keywords
+            % else:
+                <shorthands::${prop.ident}::Longhands as SpecifiedValueInfo>::SUPPORTED_TYPES,
+            % endif
+            % endfor
+        ];
+        SUPPORTED_TYPES[self.0]
+    }
 }
 
 impl From<LonghandId> for NonCustomPropertyId {
     fn from(id: LonghandId) -> Self {
         NonCustomPropertyId(id as usize)
     }
 }
 
@@ -1708,16 +1727,29 @@ impl PropertyId {
     fn allowed_in(&self, context: &ParserContext) -> bool {
         let id = match self.non_custom_id() {
             // Custom properties are allowed everywhere
             None => return true,
             Some(id) => id,
         };
         id.allowed_in(context)
     }
+
+    /// Whether the property supports the given CSS type.
+    /// `ty` should a bitflags of constants in style_traits::CssType.
+    pub fn supports_type(&self, ty: u8) -> bool {
+        let non_custom_id: NonCustomPropertyId = match *self {
+            PropertyId::Custom(_) => return false,
+            PropertyId::Shorthand(id) => id.into(),
+            PropertyId::Longhand(id) => id.into(),
+            PropertyId::ShorthandAlias(id, _) => id.into(),
+            PropertyId::LonghandAlias(id, _) => id.into(),
+        };
+        non_custom_id.supported_types() & ty != 0
+    }
 }
 
 /// A declaration using a CSS-wide keyword.
 #[cfg_attr(feature = "gecko", derive(MallocSizeOf))]
 #[derive(Clone, PartialEq, ToCss)]
 pub struct WideKeywordDeclaration {
     #[css(skip)]
     id: LonghandId,
--- a/servo/components/style/values/computed/length.rs
+++ b/servo/components/style/values/computed/length.rs
@@ -911,17 +911,18 @@ pub type NonNegativeLengthOrNormal = Eit
 
 /// Either a computed NonNegativeLengthOrPercentage or the `normal` keyword.
 pub type NonNegativeLengthOrPercentageOrNormal = Either<NonNegativeLengthOrPercentage, Normal>;
 
 /// A type for possible values for min- and max- flavors of width, height,
 /// block-size, and inline-size.
 #[allow(missing_docs)]
 #[cfg_attr(feature = "servo", derive(Deserialize, Serialize))]
-#[derive(Clone, Copy, Debug, Eq, MallocSizeOf, Parse, PartialEq, ToCss)]
+#[derive(Clone, Copy, Debug, Eq, MallocSizeOf, Parse, PartialEq,
+         SpecifiedValueInfo, ToCss)]
 pub enum ExtremumLength {
     MozMaxContent,
     MozMinContent,
     MozFitContent,
     MozAvailable,
 }
 
 impl ExtremumLength {
--- a/servo/components/style/values/computed/percentage.rs
+++ b/servo/components/style/values/computed/percentage.rs
@@ -7,18 +7,19 @@
 use std::fmt;
 use style_traits::{CssWriter, ToCss};
 use values::animated::ToAnimatedValue;
 use values::{serialize_percentage, CSSFloat};
 use values::generics::NonNegative;
 
 /// A computed percentage.
 #[cfg_attr(feature = "servo", derive(Deserialize, Serialize))]
-#[derive(Animate, Clone, ComputeSquaredDistance, Copy, Debug, Default, MallocSizeOf, PartialEq,
-         PartialOrd, ToAnimatedZero, ToComputedValue)]
+#[derive(Animate, Clone, ComputeSquaredDistance, Copy, Debug, Default,
+         MallocSizeOf, PartialEq, PartialOrd, SpecifiedValueInfo,
+         ToAnimatedZero, ToComputedValue)]
 pub struct Percentage(pub CSSFloat);
 
 impl Percentage {
     /// 0%
     #[inline]
     pub fn zero() -> Self {
         Percentage(0.)
     }
--- a/servo/components/style/values/generics/background.rs
+++ b/servo/components/style/values/generics/background.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/. */
 
 //! Generic types for CSS values related to backgrounds.
 
 /// A generic value for the `background-size` property.
-#[derive(Animate, Clone, ComputeSquaredDistance, Copy, Debug, MallocSizeOf, PartialEq,
-         ToComputedValue, ToCss)]
+#[derive(Animate, Clone, ComputeSquaredDistance, Copy, Debug, MallocSizeOf,
+         PartialEq, SpecifiedValueInfo, ToComputedValue, ToCss)]
 pub enum BackgroundSize<LengthOrPercentageOrAuto> {
     /// `<width> <height>`
     Explicit {
         /// Explicit width.
         width: LengthOrPercentageOrAuto,
         /// Explicit height.
         height: LengthOrPercentageOrAuto,
     },
--- a/servo/components/style/values/generics/basic_shape.rs
+++ b/servo/components/style/values/generics/basic_shape.rs
@@ -13,119 +13,125 @@ use values::generics::border::BorderRadi
 use values::generics::position::Position;
 use values::generics::rect::Rect;
 
 /// A clipping shape, for `clip-path`.
 pub type ClippingShape<BasicShape, Url> = ShapeSource<BasicShape, GeometryBox, Url>;
 
 /// <https://drafts.fxtf.org/css-masking-1/#typedef-geometry-box>
 #[allow(missing_docs)]
-#[derive(Animate, Clone, Copy, Debug, MallocSizeOf, PartialEq, ToComputedValue, ToCss)]
+#[derive(Animate, Clone, Copy, Debug, MallocSizeOf, PartialEq,
+         SpecifiedValueInfo, ToComputedValue, ToCss)]
 pub enum GeometryBox {
     FillBox,
     StrokeBox,
     ViewBox,
     ShapeBox(ShapeBox),
 }
 
 /// A float area shape, for `shape-outside`.
 pub type FloatAreaShape<BasicShape, Image> = ShapeSource<BasicShape, ShapeBox, Image>;
 
 /// https://drafts.csswg.org/css-shapes-1/#typedef-shape-box
 #[allow(missing_docs)]
 #[cfg_attr(feature = "servo", derive(Deserialize, Serialize))]
-#[derive(Animate, Clone, Copy, Debug, Eq, MallocSizeOf, Parse, PartialEq, ToComputedValue, ToCss)]
+#[derive(Animate, Clone, Copy, Debug, Eq, MallocSizeOf, Parse, PartialEq,
+         SpecifiedValueInfo, ToComputedValue, ToCss)]
 pub enum ShapeBox {
     MarginBox,
     BorderBox,
     PaddingBox,
     ContentBox,
 }
 
 /// A shape source, for some reference box.
 #[allow(missing_docs)]
 #[animation(no_bound(ImageOrUrl))]
-#[derive(Animate, Clone, Debug, MallocSizeOf, PartialEq, ToComputedValue, ToCss)]
+#[derive(Animate, Clone, Debug, MallocSizeOf, PartialEq, SpecifiedValueInfo,
+         ToComputedValue, ToCss)]
 pub enum ShapeSource<BasicShape, ReferenceBox, ImageOrUrl> {
     #[animation(error)]
     ImageOrUrl(ImageOrUrl),
     Shape(BasicShape, Option<ReferenceBox>),
     #[animation(error)]
     Box(ReferenceBox),
     #[animation(error)]
     None,
 }
 
 #[allow(missing_docs)]
-#[derive(Animate, Clone, ComputeSquaredDistance, Debug, MallocSizeOf, PartialEq, ToComputedValue,
-         ToCss)]
+#[derive(Animate, Clone, ComputeSquaredDistance, Debug, MallocSizeOf, PartialEq,
+         SpecifiedValueInfo, ToComputedValue, ToCss)]
 pub enum BasicShape<H, V, LengthOrPercentage> {
     Inset(#[css(field_bound)] InsetRect<LengthOrPercentage>),
     Circle(#[css(field_bound)] Circle<H, V, LengthOrPercentage>),
     Ellipse(#[css(field_bound)] Ellipse<H, V, LengthOrPercentage>),
     Polygon(Polygon<LengthOrPercentage>),
 }
 
 /// <https://drafts.csswg.org/css-shapes/#funcdef-inset>
 #[allow(missing_docs)]
-#[derive(Animate, Clone, ComputeSquaredDistance, Debug, MallocSizeOf, PartialEq, ToComputedValue)]
+#[derive(Animate, Clone, ComputeSquaredDistance, Debug, MallocSizeOf, PartialEq,
+         SpecifiedValueInfo, ToComputedValue)]
 pub struct InsetRect<LengthOrPercentage> {
     pub rect: Rect<LengthOrPercentage>,
     pub round: Option<BorderRadius<LengthOrPercentage>>,
 }
 
 /// <https://drafts.csswg.org/css-shapes/#funcdef-circle>
 #[allow(missing_docs)]
-#[derive(Animate, Clone, ComputeSquaredDistance, Copy, Debug, MallocSizeOf, PartialEq,
-         ToComputedValue)]
+#[derive(Animate, Clone, ComputeSquaredDistance, Copy, Debug, MallocSizeOf,
+         PartialEq, SpecifiedValueInfo, ToComputedValue)]
 pub struct Circle<H, V, LengthOrPercentage> {
     pub position: Position<H, V>,
     pub radius: ShapeRadius<LengthOrPercentage>,
 }
 
 /// <https://drafts.csswg.org/css-shapes/#funcdef-ellipse>
 #[allow(missing_docs)]
-#[derive(Animate, Clone, ComputeSquaredDistance, Copy, Debug, MallocSizeOf, PartialEq,
-         ToComputedValue)]
+#[derive(Animate, Clone, ComputeSquaredDistance, Copy, Debug, MallocSizeOf,
+         PartialEq, SpecifiedValueInfo, ToComputedValue)]
 pub struct Ellipse<H, V, LengthOrPercentage> {
     pub position: Position<H, V>,
     pub semiaxis_x: ShapeRadius<LengthOrPercentage>,
     pub semiaxis_y: ShapeRadius<LengthOrPercentage>,
 }
 
 /// <https://drafts.csswg.org/css-shapes/#typedef-shape-radius>
 #[allow(missing_docs)]
-#[derive(Animate, Clone, ComputeSquaredDistance, Copy, Debug, MallocSizeOf, PartialEq,
-         ToComputedValue, ToCss)]
+#[derive(Animate, Clone, ComputeSquaredDistance, Copy, Debug, MallocSizeOf,
+         PartialEq, SpecifiedValueInfo, ToComputedValue, ToCss)]
 pub enum ShapeRadius<LengthOrPercentage> {
     Length(LengthOrPercentage),
     #[animation(error)]
     ClosestSide,
     #[animation(error)]
     FarthestSide,
 }
 
 /// A generic type for representing the `polygon()` function
 ///
 /// <https://drafts.csswg.org/css-shapes/#funcdef-polygon>
-#[derive(Clone, Debug, MallocSizeOf, PartialEq, ToComputedValue)]
+#[derive(Clone, Debug, MallocSizeOf, PartialEq, SpecifiedValueInfo,
+         ToComputedValue)]
 pub struct Polygon<LengthOrPercentage> {
     /// The filling rule for a polygon.
     pub fill: FillRule,
     /// A collection of (x, y) coordinates to draw the polygon.
     pub coordinates: Vec<(LengthOrPercentage, LengthOrPercentage)>,
 }
 
 // https://drafts.csswg.org/css-shapes/#typedef-fill-rule
 // NOTE: Basic shapes spec says that these are the only two values, however
 // https://www.w3.org/TR/SVG/painting.html#FillRuleProperty
 // says that it can also be `inherit`
 #[allow(missing_docs)]
 #[cfg_attr(feature = "servo", derive(Deserialize, Serialize))]
-#[derive(Clone, Copy, Debug, Eq, MallocSizeOf, Parse, PartialEq, ToComputedValue, ToCss)]
+#[derive(Clone, Copy, Debug, Eq, MallocSizeOf, Parse, PartialEq,
+         SpecifiedValueInfo, ToComputedValue, ToCss)]
 pub enum FillRule {
     Nonzero,
     Evenodd,
 }
 
 // FIXME(nox): Implement ComputeSquaredDistance for T types and stop
 // using PartialEq here, this will let us derive this impl.
 impl<B, T, U> ComputeSquaredDistance for ShapeSource<B, T, U>
--- a/servo/components/style/values/generics/border.rs
+++ b/servo/components/style/values/generics/border.rs
@@ -5,64 +5,67 @@
 //! Generic types for CSS values related to borders.
 
 use std::fmt::{self, Write};
 use style_traits::{CssWriter, ToCss};
 use values::generics::rect::Rect;
 use values::generics::size::Size;
 
 /// A generic value for a single side of a `border-image-width` property.
-#[derive(Clone, Copy, Debug, MallocSizeOf, PartialEq, ToComputedValue, ToCss)]
+#[derive(Clone, Copy, Debug, MallocSizeOf, PartialEq, SpecifiedValueInfo,
+         ToComputedValue, ToCss)]
 pub enum BorderImageSideWidth<LengthOrPercentage, Number> {
     /// `<length-or-percentage>`
     Length(LengthOrPercentage),
     /// `<number>`
     Number(Number),
     /// `auto`
     Auto,
 }
 
 /// A generic value for the `border-image-slice` property.
-#[derive(Clone, Copy, Debug, MallocSizeOf, PartialEq, ToComputedValue)]
+#[derive(Clone, Copy, Debug, MallocSizeOf, PartialEq, SpecifiedValueInfo,
+         ToComputedValue)]
 pub struct BorderImageSlice<NumberOrPercentage> {
     /// The offsets.
     pub offsets: Rect<NumberOrPercentage>,
     /// Whether to fill the middle part.
     pub fill: bool,
 }
 
 /// A generic value for the `border-*-radius` longhand properties.
-#[derive(Animate, Clone, ComputeSquaredDistance, Copy, Debug, MallocSizeOf, PartialEq,
-         ToComputedValue, ToCss)]
+#[derive(Animate, Clone, ComputeSquaredDistance, Copy, Debug, MallocSizeOf,
+         PartialEq, SpecifiedValueInfo, ToComputedValue, ToCss)]
 pub struct BorderCornerRadius<L>(#[css(field_bound)] pub Size<L>);
 
 impl<L> BorderCornerRadius<L> {
     /// Trivially create a `BorderCornerRadius`.
     pub fn new(w: L, h: L) -> Self {
         BorderCornerRadius(Size::new(w, h))
     }
 }
 
 /// A generic value for the `border-spacing` property.
-#[derive(Animate, Clone, ComputeSquaredDistance, Copy, Debug, MallocSizeOf, PartialEq,
-         ToAnimatedValue, ToAnimatedZero, ToComputedValue, ToCss)]
+#[derive(Animate, Clone, ComputeSquaredDistance, Copy, Debug, MallocSizeOf,
+         PartialEq, SpecifiedValueInfo, ToAnimatedValue, ToAnimatedZero,
+         ToComputedValue, ToCss)]
 pub struct BorderSpacing<L>(#[css(field_bound)] pub Size<L>);
 
 impl<L> BorderSpacing<L> {
     /// Trivially create a `BorderCornerRadius`.
     pub fn new(w: L, h: L) -> Self {
         BorderSpacing(Size::new(w, h))
     }
 }
 
 /// A generic value for `border-radius`, `outline-radius` and `inset()`.
 ///
 /// <https://drafts.csswg.org/css-backgrounds-3/#border-radius>
-#[derive(Animate, Clone, ComputeSquaredDistance, Copy, Debug, MallocSizeOf, PartialEq,
-         ToComputedValue)]
+#[derive(Animate, Clone, ComputeSquaredDistance, Copy, Debug, MallocSizeOf,
+         PartialEq, SpecifiedValueInfo, ToComputedValue)]
 pub struct BorderRadius<LengthOrPercentage> {
     /// The top left radius.
     pub top_left: BorderCornerRadius<LengthOrPercentage>,
     /// The top right radius.
     pub top_right: BorderCornerRadius<LengthOrPercentage>,
     /// The bottom right radius.
     pub bottom_right: BorderCornerRadius<LengthOrPercentage>,
     /// The bottom left radius.
--- a/servo/components/style/values/generics/box.rs
+++ b/servo/components/style/values/generics/box.rs
@@ -2,18 +2,18 @@
  * 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 box properties.
 
 use values::animated::ToAnimatedZero;
 
 /// A generic value for the `vertical-align` property.
-#[derive(Animate, Clone, ComputeSquaredDistance, Copy, Debug, MallocSizeOf, PartialEq,
-         ToComputedValue, ToCss)]
+#[derive(Animate, Clone, ComputeSquaredDistance, Copy, Debug, MallocSizeOf,
+         PartialEq, SpecifiedValueInfo, ToComputedValue, ToCss)]
 pub enum VerticalAlign<LengthOrPercentage> {
     /// `baseline`
     Baseline,
     /// `sub`
     Sub,
     /// `super`
     Super,
     /// `top`
@@ -43,27 +43,29 @@ impl<L> VerticalAlign<L> {
 
 impl<L> ToAnimatedZero for VerticalAlign<L> {
     fn to_animated_zero(&self) -> Result<Self, ()> {
         Err(())
     }
 }
 
 /// https://drafts.csswg.org/css-animations/#animation-iteration-count
-#[derive(Clone, Debug, MallocSizeOf, PartialEq, ToComputedValue, ToCss)]
+#[derive(Clone, Debug, MallocSizeOf, PartialEq, SpecifiedValueInfo,
+         ToComputedValue, ToCss)]
 pub enum AnimationIterationCount<Number> {
     /// A `<number>` value.
     Number(Number),
     /// The `infinite` keyword.
     Infinite,
 }
 
 /// A generic value for the `perspective` property.
-#[derive(Animate, Clone, ComputeSquaredDistance, Copy, Debug, MallocSizeOf, PartialEq,
-         ToAnimatedValue, ToAnimatedZero, ToComputedValue, ToCss)]
+#[derive(Animate, Clone, ComputeSquaredDistance, Copy, Debug, MallocSizeOf,
+         PartialEq, SpecifiedValueInfo, ToAnimatedValue, ToAnimatedZero,
+         ToComputedValue, ToCss)]
 pub enum Perspective<NonNegativeLength> {
     /// A non-negative length.
     Length(NonNegativeLength),
     /// The keyword `none`.
     None,
 }
 
 impl<L> Perspective<L> {
--- a/servo/components/style/values/generics/column.rs
+++ b/servo/components/style/values/generics/column.rs
@@ -1,17 +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 the column properties.
 
 /// A generic type for `column-count` values.
-#[derive(Animate, Clone, ComputeSquaredDistance, Copy, Debug, MallocSizeOf, PartialEq,
-         ToAnimatedValue, ToAnimatedZero, ToComputedValue, ToCss)]
+#[derive(Animate, Clone, ComputeSquaredDistance, Copy, Debug, MallocSizeOf,
+         PartialEq, SpecifiedValueInfo, ToAnimatedValue, ToAnimatedZero,
+         ToComputedValue, ToCss)]
 pub enum ColumnCount<PositiveInteger> {
     /// A positive integer.
     Integer(PositiveInteger),
     /// The keyword `auto`.
     #[animation(error)]
     Auto,
 }
 
--- a/servo/components/style/values/generics/counters.rs
+++ b/servo/components/style/values/generics/counters.rs
@@ -6,17 +6,18 @@
 
 use std::fmt;
 use std::fmt::Write;
 use std::ops::Deref;
 use style_traits::{CssWriter, ToCss};
 use values::CustomIdent;
 
 /// A generic value for the `counter-increment` property.
-#[derive(Clone, Debug, Default, MallocSizeOf, PartialEq, ToComputedValue, ToCss)]
+#[derive(Clone, Debug, Default, MallocSizeOf, PartialEq, SpecifiedValueInfo,
+         ToComputedValue, ToCss)]
 pub struct CounterIncrement<I>(Counters<I>);
 
 impl<I> CounterIncrement<I> {
     /// Returns a new value for `counter-increment`.
     #[inline]
     pub fn new(counters: Vec<(CustomIdent, I)>) -> Self {
         CounterIncrement(Counters(counters.into_boxed_slice()))
     }
@@ -27,17 +28,18 @@ impl<I> Deref for CounterIncrement<I> {
 
     #[inline]
     fn deref(&self) -> &Self::Target {
         &(self.0).0
     }
 }
 
 /// A generic value for the `counter-reset` property.
-#[derive(Clone, Debug, Default, MallocSizeOf, PartialEq, ToComputedValue, ToCss)]
+#[derive(Clone, Debug, Default, MallocSizeOf, PartialEq, SpecifiedValueInfo,
+         ToComputedValue, ToCss)]
 pub struct CounterReset<I>(Counters<I>);
 
 impl<I> CounterReset<I> {
     /// Returns a new value for `counter-reset`.
     #[inline]
     pub fn new(counters: Vec<(CustomIdent, I)>) -> Self {
         CounterReset(Counters(counters.into_boxed_slice()))
     }
@@ -50,17 +52,18 @@ impl<I> Deref for CounterReset<I> {
     fn deref(&self) -> &Self::Target {
         &(self.0).0
     }
 }
 
 /// A generic value for lists of counters.
 ///
 /// Keyword `none` is represented by an empty vector.
-#[derive(Clone, Debug, MallocSizeOf, PartialEq, ToComputedValue)]
+#[derive(Clone, Debug, MallocSizeOf, PartialEq, SpecifiedValueInfo,
+         ToComputedValue)]
 pub struct Counters<I>(Box<[(CustomIdent, I)]>);
 
 impl<I> Default for Counters<I> {
     #[inline]
     fn default() -> Self {
         Counters(vec![].into_boxed_slice())
     }
 }
--- a/servo/components/style/values/generics/effects.rs
+++ b/servo/components/style/values/generics/effects.rs
@@ -5,31 +5,32 @@
 //! Generic types for CSS values related to effects.
 
 use std::fmt::{self, Write};
 use style_traits::values::{CssWriter, SequenceWriter, ToCss};
 #[cfg(feature = "gecko")]
 use values::specified::url::SpecifiedUrl;
 
 /// A generic value for a single `box-shadow`.
-#[derive(Animate, Clone, Debug, MallocSizeOf, PartialEq, ToAnimatedValue, ToAnimatedZero)]
+#[derive(Animate, Clone, Debug, MallocSizeOf, PartialEq, SpecifiedValueInfo,
+         ToAnimatedValue, ToAnimatedZero)]
 pub struct BoxShadow<Color, SizeLength, BlurShapeLength, ShapeLength> {
     /// The base shadow.
     pub base: SimpleShadow<Color, SizeLength, BlurShapeLength>,
     /// The spread radius.
     pub spread: ShapeLength,
     /// Whether this is an inset box shadow.
     #[animation(constant)]
     pub inset: bool,
 }
 
 /// A generic value for a single `filter`.
 #[cfg_attr(feature = "servo", derive(Deserialize, Serialize))]
-#[derive(Clone, ComputeSquaredDistance, Debug, MallocSizeOf, PartialEq, ToAnimatedValue,
-         ToComputedValue, ToCss)]
+#[derive(Clone, ComputeSquaredDistance, Debug, MallocSizeOf, PartialEq,
+         SpecifiedValueInfo, ToAnimatedValue, ToComputedValue, ToCss)]
 pub enum Filter<Angle, Factor, Length, DropShadow> {
     /// `blur(<length>)`
     #[css(function)]
     Blur(Length),
     /// `brightness(<factor>)`
     #[css(function)]
     Brightness(Factor),
     /// `contrast(<factor>)`
@@ -61,18 +62,18 @@ pub enum Filter<Angle, Factor, Length, D
     #[cfg(feature = "gecko")]
     Url(SpecifiedUrl),
 }
 
 /// A generic value for the `drop-shadow()` filter and the `text-shadow` property.
 ///
 /// Contrary to the canonical order from the spec, the color is serialised
 /// first, like in Gecko and Webkit.
-#[derive(Animate, Clone, ComputeSquaredDistance, Debug, MallocSizeOf, PartialEq, ToAnimatedValue,
-         ToAnimatedZero, ToCss)]
+#[derive(Animate, Clone, ComputeSquaredDistance, Debug, MallocSizeOf, PartialEq,
+         SpecifiedValueInfo, ToAnimatedValue, ToAnimatedZero, ToCss)]
 pub struct SimpleShadow<Color, SizeLength, ShapeLength> {
     /// Color.
     pub color: Color,
     /// Horizontal radius.
     pub horizontal: SizeLength,
     /// Vertical radius.
     pub vertical: SizeLength,
     /// Blur radius.
--- a/servo/components/style/values/generics/flex.rs
+++ b/servo/components/style/values/generics/flex.rs
@@ -1,16 +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/. */
 
 //! Generic types for CSS values related to flexbox.
 
 /// A generic value for the `flex-basis` property.
 #[cfg_attr(feature = "servo", derive(MallocSizeOf))]
-#[derive(Animate, Clone, ComputeSquaredDistance, Copy, Debug, PartialEq, ToAnimatedValue,
-         ToAnimatedZero, ToComputedValue, ToCss)]
+#[derive(Animate, Clone, ComputeSquaredDistance, Copy, Debug, PartialEq,
+         SpecifiedValueInfo, ToAnimatedValue, ToAnimatedZero, ToComputedValue,
+         ToCss)]
 pub enum FlexBasis<Width> {
     /// `content`
     Content,
     /// `<width>`
     Width(Width),
 }
--- a/servo/components/style/values/generics/font.rs
+++ b/servo/components/style/values/generics/font.rs
@@ -6,21 +6,22 @@
 
 use app_units::Au;
 use byteorder::{BigEndian, ReadBytesExt};
 use cssparser::Parser;
 use num_traits::One;
 use parser::{Parse, ParserContext};
 use std::fmt::{self, Write};
 use std::io::Cursor;
-use style_traits::{CssWriter, ParseError, StyleParseErrorKind, ToCss};
+use style_traits::{CssWriter, ParseError, SpecifiedValueInfo, StyleParseErrorKind, ToCss};
 use values::distance::{ComputeSquaredDistance, SquaredDistance};
 
 /// https://drafts.csswg.org/css-fonts-4/#feature-tag-value
-#[derive(Clone, Debug, Eq, MallocSizeOf, PartialEq, ToComputedValue)]
+#[derive(Clone, Debug, Eq, MallocSizeOf, PartialEq, SpecifiedValueInfo,
+         ToComputedValue)]
 pub struct FeatureTagValue<Integer> {
     /// A four-character tag, packed into a u32 (one byte per character).
     pub tag: FontTag,
     /// The actual value.
     pub value: Integer,
 }
 
 impl<Integer> ToCss for FeatureTagValue<Integer>
@@ -40,17 +41,18 @@ where
 
         Ok(())
     }
 }
 
 /// Variation setting for a single feature, see:
 ///
 /// https://drafts.csswg.org/css-fonts-4/#font-variation-settings-def
-#[derive(Animate, Clone, Debug, Eq, MallocSizeOf, PartialEq, ToComputedValue, ToCss)]
+#[derive(Animate, Clone, Debug, Eq, MallocSizeOf, PartialEq, SpecifiedValueInfo,
+         ToComputedValue, ToCss)]
 pub struct VariationValue<Number> {
     /// A four-character tag, packed into a u32 (one byte per character).
     #[animation(constant)]
     pub tag: FontTag,
     /// The actual value.
     pub value: Number,
 }
 
@@ -64,17 +66,18 @@ where
             return Err(());
         }
         self.value.compute_squared_distance(&other.value)
     }
 }
 
 /// A value both for font-variation-settings and font-feature-settings.
 #[css(comma)]
-#[derive(Clone, Debug, Eq, MallocSizeOf, PartialEq, ToComputedValue, ToCss)]
+#[derive(Clone, Debug, Eq, MallocSizeOf, PartialEq, SpecifiedValueInfo,
+         ToComputedValue, ToCss)]
 pub struct FontSettings<T>(#[css(if_empty = "normal", iterable)] pub Box<[T]>);
 
 impl<T> FontSettings<T> {
     /// Default value of font settings as `normal`.
     #[inline]
     pub fn normal() -> Self {
         FontSettings(vec![].into_boxed_slice())
     }
@@ -100,17 +103,18 @@ impl<T: Parse> Parse for FontSettings<T>
 }
 
 /// A font four-character tag, represented as a u32 for convenience.
 ///
 /// See:
 ///   https://drafts.csswg.org/css-fonts-4/#font-variation-settings-def
 ///   https://drafts.csswg.org/css-fonts-4/#descdef-font-face-font-feature-settings
 ///
-#[derive(Clone, Copy, Debug, Eq, MallocSizeOf, PartialEq, ToComputedValue)]
+#[derive(Clone, Copy, Debug, Eq, MallocSizeOf, PartialEq, SpecifiedValueInfo,
+         ToComputedValue)]
 pub struct FontTag(pub u32);
 
 impl ToCss for FontTag {
     fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
     where
         W: Write,
     {
         use byteorder::{BigEndian, ByteOrder};
@@ -172,19 +176,23 @@ where
         KeywordInfo {
             kw: x,
             factor: 1.,
             offset: Au(0).into(),
         }
     }
 }
 
+impl<Length> SpecifiedValueInfo for KeywordInfo<Length> {
+    const SUPPORTED_TYPES: u8 = <KeywordSize as SpecifiedValueInfo>::SUPPORTED_TYPES;
+}
+
 /// CSS font keywords
 #[derive(Animate, Clone, ComputeSquaredDistance, Copy, Debug, MallocSizeOf, PartialEq,
-         ToAnimatedValue, ToAnimatedZero)]
+         ToAnimatedValue, ToAnimatedZero, SpecifiedValueInfo)]
 #[allow(missing_docs)]
 pub enum KeywordSize {
     XXSmall,
     XSmall,
     Small,
     Medium,
     Large,
     XLarge,
@@ -232,16 +240,16 @@ impl ToCss for KeywordSize {
     }
 }
 
 /// A generic value for the `font-style` property.
 ///
 /// https://drafts.csswg.org/css-fonts-4/#font-style-prop
 #[allow(missing_docs)]
 #[derive(Animate, Clone, ComputeSquaredDistance, Copy, Debug, MallocSizeOf,
-         PartialEq, ToAnimatedValue, ToAnimatedZero)]
+         PartialEq, SpecifiedValueInfo, ToAnimatedValue, ToAnimatedZero)]
 pub enum FontStyle<Angle> {
     #[animation(error)]
     Normal,
     #[animation(error)]
     Italic,
     Oblique(Angle),
 }
--- a/servo/components/style/values/generics/gecko.rs
+++ b/servo/components/style/values/generics/gecko.rs
@@ -2,17 +2,18 @@
  * 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 legacy Gecko-only properties that should probably be
 //! unshipped at some point in the future.
 
 /// A generic value for scroll snap points.
 #[cfg_attr(feature = "gecko", derive(MallocSizeOf))]
-#[derive(Clone, Copy, Debug, PartialEq, ToComputedValue, ToCss)]
+#[derive(Clone, Copy, Debug, PartialEq, SpecifiedValueInfo, ToComputedValue,
+         ToCss)]
 pub enum ScrollSnapPoint<LengthOrPercentage> {
     /// `none`
     None,
     /// `repeat(<length-or-percentage>)`
     #[css(function)]
     Repeat(LengthOrPercentage),
 }
 
--- a/servo/components/style/values/generics/grid.rs
+++ b/servo/components/style/values/generics/grid.rs
@@ -13,17 +13,18 @@ use style_traits::{CssWriter, ParseError
 use values::{CSSFloat, CustomIdent};
 use values::computed::{Context, ToComputedValue};
 use values::specified;
 use values::specified::grid::parse_line_names;
 
 /// A `<grid-line>` type.
 ///
 /// <https://drafts.csswg.org/css-grid/#typedef-grid-row-start-grid-line>
-#[derive(Clone, Debug, Default, MallocSizeOf, PartialEq, ToComputedValue)]
+#[derive(Clone, Debug, Default, MallocSizeOf, PartialEq, SpecifiedValueInfo,
+         ToComputedValue)]
 pub struct GridLine<Integer> {
     /// Flag to check whether it's a `span` keyword.
     pub is_span: bool,
     /// A custom identifier for named lines.
     ///
     /// <https://drafts.csswg.org/css-grid/#grid-placement-slot>
     pub ident: Option<CustomIdent>,
     /// Denotes the nth grid line from grid item's placement.
@@ -143,28 +144,30 @@ impl Parse for GridLine<specified::Integ
         }
 
         Ok(grid_line)
     }
 }
 
 #[allow(missing_docs)]
 #[cfg_attr(feature = "servo", derive(Deserialize, Serialize))]
-#[derive(Clone, Copy, Debug, Eq, MallocSizeOf, Parse, PartialEq, ToComputedValue, ToCss)]
+#[derive(Clone, Copy, Debug, Eq, MallocSizeOf, Parse, PartialEq,
+         SpecifiedValueInfo, ToComputedValue, ToCss)]
 pub enum TrackKeyword {
     Auto,
     MaxContent,
     MinContent,
 }
 
 /// A track breadth for explicit grid track sizing. It's generic solely to
 /// avoid re-implementing it for the computed type.
 ///
 /// <https://drafts.csswg.org/css-grid/#typedef-track-breadth>
-#[derive(Clone, Debug, MallocSizeOf, PartialEq, ToComputedValue, ToCss)]
+#[derive(Clone, Debug, MallocSizeOf, PartialEq, SpecifiedValueInfo,
+         ToComputedValue, ToCss)]
 pub enum TrackBreadth<L> {
     /// The generic type is almost always a non-negative `<length-percentage>`
     Breadth(L),
     /// A flex fraction specified in `fr` units.
     #[css(dimension)]
     Fr(CSSFloat),
     /// One of the track-sizing keywords (`auto`, `min-content`, `max-content`)
     Keyword(TrackKeyword),
@@ -182,17 +185,17 @@ impl<L> TrackBreadth<L> {
         }
     }
 }
 
 /// 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>
-#[derive(Clone, Debug, MallocSizeOf, PartialEq)]
+#[derive(Clone, Debug, MallocSizeOf, PartialEq, SpecifiedValueInfo)]
 pub enum TrackSize<L> {
     /// A flexible `<track-breadth>`
     Breadth(TrackBreadth<L>),
     /// A `minmax` function for a range over an inflexible `<track-breadth>`
     /// and a flexible `<track-breadth>`
     ///
     /// <https://drafts.csswg.org/css-grid/#valdef-grid-template-columns-minmax>
     Minmax(TrackBreadth<L>, TrackBreadth<L>),
@@ -338,17 +341,18 @@ where
     }
 
     Ok(())
 }
 
 /// The initial argument of the `repeat` function.
 ///
 /// <https://drafts.csswg.org/css-grid/#typedef-track-repeat>
-#[derive(Clone, Copy, Debug, MallocSizeOf, PartialEq, ToComputedValue, ToCss)]
+#[derive(Clone, Copy, Debug, MallocSizeOf, PartialEq, SpecifiedValueInfo,
+         ToComputedValue, ToCss)]
 pub enum RepeatCount<Integer> {
     /// A positive integer. This is allowed only for `<track-repeat>` and `<fixed-repeat>`
     Number(Integer),
     /// An `<auto-fill>` keyword allowed only for `<auto-repeat>`
     AutoFill,
     /// An `<auto-fit>` keyword allowed only for `<auto-repeat>`
     AutoFit,
 }
@@ -373,17 +377,18 @@ impl Parse for RepeatCount<specified::In
         }
     }
 }
 
 /// The structure containing `<line-names>` and `<track-size>` values.
 ///
 /// It can also hold `repeat()` function parameters, which expands into the respective
 /// values in its computed form.
-#[derive(Clone, Debug, MallocSizeOf, PartialEq, ToComputedValue)]
+#[derive(Clone, Debug, MallocSizeOf, PartialEq, SpecifiedValueInfo,
+         ToComputedValue)]
 pub struct TrackRepeat<L, I> {
     /// The number of times for the value to be repeated (could also be `auto-fit` or `auto-fill`)
     pub count: RepeatCount<I>,
     /// `<line-names>` accompanying `<track_size>` values.
     ///
     /// If there's no `<line-names>`, then it's represented by an empty vector.
     /// For N `<track-size>` values, there will be N+1 `<line-names>`, and so this vector's
     /// length is always one value more than that of the `<track-size>`.
@@ -459,28 +464,30 @@ impl<L: Clone> TrackRepeat<L, specified:
                 track_sizes: self.track_sizes.clone(),
                 line_names: self.line_names.clone(),
             }
         }
     }
 }
 
 /// Track list values. Can be <track-size> or <track-repeat>
-#[derive(Clone, Debug, MallocSizeOf, PartialEq, ToComputedValue, ToCss)]
+#[derive(Clone, Debug, MallocSizeOf, PartialEq, SpecifiedValueInfo,
+         ToComputedValue, ToCss)]
 pub enum TrackListValue<LengthOrPercentage, Integer> {
     /// A <track-size> value.
     TrackSize(TrackSize<LengthOrPercentage>),
     /// A <track-repeat> value.
     TrackRepeat(TrackRepeat<LengthOrPercentage, Integer>),
 }
 
 /// The type of a `<track-list>` as determined during parsing.
 ///
 /// <https://drafts.csswg.org/css-grid/#typedef-track-list>
-#[derive(Clone, Copy, Debug, MallocSizeOf, PartialEq, ToComputedValue)]
+#[derive(Clone, Copy, Debug, MallocSizeOf, PartialEq, SpecifiedValueInfo,
+         ToComputedValue)]
 pub enum TrackListType {
     /// [`<auto-track-list>`](https://drafts.csswg.org/css-grid/#typedef-auto-track-list)
     ///
     /// If this type exists, then the value at the index in `line_names` field in `TrackList`
     /// has the `<line-names>?` list that comes before `<auto-repeat>`. If it's a specified value,
     /// then the `repeat()` function (that follows the line names list) is also at the given index
     /// in `values` field. On the contrary, if it's a computed value, then the `repeat()` function
     /// is in the `auto_repeat` field.
@@ -492,17 +499,17 @@ pub enum TrackListType {
     /// Note that this is a subset of the normal `<track-list>`, and so it could be used in place
     /// of the latter.
     Explicit,
 }
 
 /// A grid `<track-list>` type.
 ///
 /// <https://drafts.csswg.org/css-grid/#typedef-track-list>
-#[derive(Clone, Debug, MallocSizeOf, PartialEq)]
+#[derive(Clone, Debug, MallocSizeOf, PartialEq, SpecifiedValueInfo)]
 pub struct TrackList<LengthOrPercentage, Integer> {
     /// The type of this `<track-list>` (auto, explicit or general).
     ///
     /// In order to avoid parsing the same value multiple times, this does a single traversal
     /// and arrives at the type of value it has parsed (or bails out gracefully with an error).
     pub list_type: TrackListType,
     /// A vector of `<track-size> | <track-repeat>` values.
     pub values: Vec<TrackListValue<LengthOrPercentage, Integer>>,
@@ -564,17 +571,18 @@ impl<L: ToCss, I: ToCss> ToCss for Track
         Ok(())
     }
 }
 
 /// The `<line-name-list>` for subgrids.
 ///
 /// `subgrid [ <line-names> | repeat(<positive-integer> | auto-fill, <line-names>+) ]+`
 /// Old spec: https://www.w3.org/TR/2015/WD-css-grid-1-20150917/#typedef-line-name-list
-#[derive(Clone, Debug, Default, MallocSizeOf, PartialEq, ToComputedValue)]
+#[derive(Clone, Debug, Default, MallocSizeOf, PartialEq, SpecifiedValueInfo,
+         ToComputedValue)]
 pub struct LineNameList {
     /// The optional `<line-name-list>`
     pub names: Box<[Box<[CustomIdent]>]>,
     /// Indicates the line name that requires `auto-fill`
     pub fill_idx: Option<u32>,
 }
 
 impl Parse for LineNameList {
@@ -667,17 +675,18 @@ impl ToCss for LineNameList {
 
         Ok(())
     }
 }
 
 /// Variants for `<grid-template-rows> | <grid-template-columns>`
 /// Subgrid deferred to Level 2 spec due to lack of implementation.
 /// But it's implemented in gecko, so we have to as well.
-#[derive(Clone, Debug, MallocSizeOf, PartialEq, ToComputedValue, ToCss)]
+#[derive(Clone, Debug, MallocSizeOf, PartialEq, SpecifiedValueInfo,
+         ToComputedValue, ToCss)]
 pub enum GridTemplateComponent<L, I> {
     /// `none` value.
     None,
     /// The grid `<track-list>`
     TrackList(#[compute(field_bound)] TrackList<L, I>),
     /// A `subgrid <line-name-list>?`
     Subgrid(LineNameList),
 }
--- a/servo/components/style/values/generics/image.rs
+++ b/servo/components/style/values/generics/image.rs
@@ -11,17 +11,17 @@ use custom_properties;
 use servo_arc::Arc;
 use std::fmt::{self, Write};
 use style_traits::{CssWriter, ToCss};
 use values::serialize_atom_identifier;
 
 /// An [image].
 ///
 /// [image]: https://drafts.csswg.org/css-images/#image-values
-#[derive(Clone, MallocSizeOf, PartialEq, ToComputedValue)]
+#[derive(Clone, MallocSizeOf, PartialEq, SpecifiedValueInfo, ToComputedValue)]
 pub enum Image<Gradient, MozImageRect, ImageUrl> {
     /// A `<url()>` image.
     Url(ImageUrl),
     /// A `<gradient>` image.  Gradients are rather large, and not nearly as
     /// common as urls, so we box them here to keep the size of this enum sane.
     Gradient(Box<Gradient>),
     /// A `-moz-image-rect` image.  Also fairly large and rare.
     Rect(Box<MozImageRect>),
@@ -159,17 +159,18 @@ impl ToCss for PaintWorklet {
     }
 }
 
 /// Values for `moz-image-rect`.
 ///
 /// `-moz-image-rect(<uri>, top, right, bottom, left);`
 #[allow(missing_docs)]
 #[css(comma, function)]
-#[derive(Clone, Debug, MallocSizeOf, PartialEq, ToComputedValue, ToCss)]
+#[derive(Clone, Debug, MallocSizeOf, PartialEq, SpecifiedValueInfo,
+         ToComputedValue, ToCss)]
 pub struct MozImageRect<NumberOrPercentage, MozImageRectUrl> {
     pub url: MozImageRectUrl,
     pub top: NumberOrPercentage,
     pub right: NumberOrPercentage,
     pub bottom: NumberOrPercentage,
     pub left: NumberOrPercentage,
 }
 
--- a/servo/components/style/values/generics/mod.rs
+++ b/servo/components/style/values/generics/mod.rs
@@ -74,17 +74,18 @@ impl SymbolsType {
     }
 }
 
 /// <https://drafts.csswg.org/css-counter-styles/#typedef-counter-style>
 ///
 /// Since wherever <counter-style> is used, 'none' is a valid value as
 /// well, we combine them into one type to make code simpler.
 #[cfg_attr(feature = "gecko", derive(MallocSizeOf))]
-#[derive(Clone, Debug, Eq, PartialEq, ToComputedValue, ToCss)]
+#[derive(Clone, Debug, Eq, PartialEq, SpecifiedValueInfo, ToComputedValue,
+         ToCss)]
 pub enum CounterStyleOrNone {
     /// `none`
     None,
     /// `<counter-style-name>`
     Name(CustomIdent),
     /// `symbols()`
     #[css(function)]
     Symbols(SymbolsType, Symbols),
@@ -134,17 +135,19 @@ impl Parse for CounterStyleOrNone {
             });
         }
         Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
     }
 }
 
 /// A wrapper of Non-negative values.
 #[cfg_attr(feature = "servo", derive(Deserialize, Serialize))]
-#[derive(Animate, Clone, ComputeSquaredDistance, Copy, Debug, MallocSizeOf, PartialEq,
-         PartialOrd, ToAnimatedZero, ToComputedValue, ToCss)]
+#[derive(Animate, Clone, ComputeSquaredDistance, Copy, Debug, MallocSizeOf,
+         PartialEq, PartialOrd, SpecifiedValueInfo, ToAnimatedZero,
+         ToComputedValue, ToCss)]
 pub struct NonNegative<T>(pub T);
 
 /// A wrapper of greater-than-or-equal-to-one values.
 #[cfg_attr(feature = "servo", derive(Deserialize, Serialize))]
-#[derive(Animate, Clone, ComputeSquaredDistance, Copy, Debug, MallocSizeOf, PartialEq,
-         PartialOrd, ToAnimatedZero, ToComputedValue, ToCss)]
+#[derive(Animate, Clone, ComputeSquaredDistance, Copy, Debug, MallocSizeOf,
+         PartialEq, PartialOrd, SpecifiedValueInfo, ToAnimatedZero,
+         ToComputedValue, ToCss)]
 pub struct GreaterThanOrEqualToOne<T>(pub T);
--- a/servo/components/style/values/generics/pointing.rs
+++ b/servo/components/style/values/generics/pointing.rs
@@ -4,29 +4,31 @@
 
 //! Generic values for pointing properties.
 
 use std::fmt::{self, Write};
 use style_traits::{CssWriter, ToCss};
 use style_traits::cursor::CursorKind;
 
 /// A generic value for the `caret-color` property.
-#[derive(Animate, Clone, ComputeSquaredDistance, Copy, Debug, MallocSizeOf, PartialEq,
-         ToAnimatedValue, ToAnimatedZero, ToComputedValue, ToCss)]
+#[derive(Animate, Clone, ComputeSquaredDistance, Copy, Debug, MallocSizeOf,
+         PartialEq, SpecifiedValueInfo, ToAnimatedValue, ToAnimatedZero,
+         ToComputedValue, ToCss)]
 pub enum CaretColor<Color> {
     /// An explicit color.
     Color(Color),
     /// The keyword `auto`.
     Auto,
 }
 
 /// A generic value for the `cursor` property.
 ///
 /// https://drafts.csswg.org/css-ui/#cursor
-#[derive(Clone, Debug, MallocSizeOf, PartialEq, ToComputedValue)]
+#[derive(Clone, Debug, MallocSizeOf, PartialEq, SpecifiedValueInfo,
+         ToComputedValue)]
 pub struct Cursor<Image> {
     /// The parsed images for the cursor.
     pub images: Box<[Image]>,
     /// The kind of the cursor [default | help | ...].
     pub keyword: CursorKind,
 }
 
 impl<Image> Cursor<Image> {
@@ -49,17 +51,18 @@ impl<Image: ToCss> ToCss for Cursor<Imag
             image.to_css(dest)?;
             dest.write_str(", ")?;
         }
         self.keyword.to_css(dest)
     }
 }
 
 /// A generic value for item of `image cursors`.
-#[derive(Clone, Debug, MallocSizeOf, PartialEq, ToComputedValue)]
+#[derive(Clone, Debug, MallocSizeOf, PartialEq, SpecifiedValueInfo,
+         ToComputedValue)]
 pub struct CursorImage<ImageUrl, Number> {
     /// The url to parse images from.
     pub url: ImageUrl,
     /// The <x> and <y> coordinates.
     pub hotspot: Option<(Number, Number)>,
 }
 
 impl<ImageUrl: ToCss, Number: ToCss> ToCss for CursorImage<ImageUrl, Number> {
--- a/servo/components/style/values/generics/position.rs
+++ b/servo/components/style/values/generics/position.rs
@@ -1,18 +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)
 
 /// A generic type for representing a CSS [position](https://drafts.csswg.org/css-values/#position).
-#[derive(Animate, Clone, ComputeSquaredDistance, Copy, Debug, MallocSizeOf, PartialEq,
-         ToAnimatedZero, ToComputedValue)]
+#[derive(Animate, Clone, ComputeSquaredDistance, Copy, Debug, MallocSizeOf,
+         PartialEq, SpecifiedValueInfo, ToAnimatedZero, ToComputedValue)]
 pub struct Position<H, V> {
     /// The horizontal component of position.
     pub horizontal: H,
     /// The vertical component of position.
     pub vertical: V,
 }
 
 impl<H, V> Position<H, V> {
@@ -21,18 +21,18 @@ impl<H, V> Position<H, V> {
         Self {
             horizontal: horizontal,
             vertical: vertical,
         }
     }
 }
 
 /// A generic value for the `z-index` property.
-#[derive(Animate, Clone, ComputeSquaredDistance, Copy, Debug, MallocSizeOf, PartialEq,
-         ToAnimatedZero, ToComputedValue, ToCss)]
+#[derive(Animate, Clone, ComputeSquaredDistance, Copy, Debug, MallocSizeOf,
+         PartialEq, SpecifiedValueInfo, ToAnimatedZero, ToComputedValue, ToCss)]
 pub enum ZIndex<Integer> {
     /// An integer value.
     Integer(Integer),
     /// The keyword `auto`.
     Auto,
 }
 
 impl<Integer> ZIndex<Integer> {
--- a/servo/components/style/values/generics/rect.rs
+++ b/servo/components/style/values/generics/rect.rs
@@ -6,18 +6,18 @@
 
 use cssparser::Parser;
 use parser::{Parse, ParserContext};
 use std::fmt::{self, Write};
 use style_traits::{CssWriter, ParseError, ToCss};
 
 /// A CSS value made of four components, where its `ToCss` impl will try to
 /// serialize as few components as possible, like for example in `border-width`.
-#[derive(Animate, Clone, ComputeSquaredDistance, Copy, Debug, MallocSizeOf, PartialEq,
-         ToComputedValue)]
+#[derive(Animate, Clone, ComputeSquaredDistance, Copy, Debug, MallocSizeOf,
+         PartialEq, SpecifiedValueInfo, ToComputedValue)]
 pub struct Rect<T>(pub T, pub T, pub T, pub T);
 
 impl<T> Rect<T> {
     /// Returns a new `Rect<T>` value.
     pub fn new(first: T, second: T, third: T, fourth: T) -> Self {
         Rect(first, second, third, fourth)
     }
 }
--- a/servo/components/style/values/generics/size.rs
+++ b/servo/components/style/values/generics/size.rs
@@ -3,17 +3,17 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 //! Generic type for CSS properties that are composed by two dimensions.
 
 use cssparser::Parser;
 use euclid::Size2D;
 use parser::ParserContext;
 use std::fmt::{self, Write};
-use style_traits::{CssWriter, ParseError, ToCss};
+use style_traits::{CssWriter, ParseError, SpecifiedValueInfo, ToCss};
 use values::animated::ToAnimatedValue;
 
 /// A generic size, for `border-*-radius` longhand properties, or
 /// `border-spacing`.
 #[derive(Animate, Clone, ComputeSquaredDistance, Copy, Debug, MallocSizeOf, PartialEq,
          ToAnimatedZero, ToComputedValue)]
 pub struct Size<L>(pub Size2D<L>);
 
@@ -88,8 +88,12 @@ where
     #[inline]
     fn from_animated_value(animated: Self::AnimatedValue) -> Self {
         Size(Size2D::new(
             L::from_animated_value(animated.0.width),
             L::from_animated_value(animated.0.height),
         ))
     }
 }
+
+impl<L: SpecifiedValueInfo> SpecifiedValueInfo for Size<L> {
+    const SUPPORTED_TYPES: u8 = L::SUPPORTED_TYPES;
+}
--- a/servo/components/style/values/generics/svg.rs
+++ b/servo/components/style/values/generics/svg.rs
@@ -11,33 +11,34 @@ use values::{Either, None_};
 use values::computed::NumberOrPercentage;
 use values::computed::length::LengthOrPercentage;
 use values::distance::{ComputeSquaredDistance, SquaredDistance};
 
 /// An SVG paint value
 ///
 /// <https://www.w3.org/TR/SVG2/painting.html#SpecifyingPaint>
 #[animation(no_bound(UrlPaintServer))]
-#[derive(Animate, Clone, ComputeSquaredDistance, Debug, MallocSizeOf, PartialEq, ToAnimatedValue,
-         ToComputedValue, ToCss)]
+#[derive(Animate, Clone, ComputeSquaredDistance, Debug, MallocSizeOf, PartialEq,
+         SpecifiedValueInfo, ToAnimatedValue, ToComputedValue, ToCss)]
 pub struct SVGPaint<ColorType, UrlPaintServer> {
     /// The paint source
     pub kind: SVGPaintKind<ColorType, UrlPaintServer>,
     /// The fallback color. It would be empty, the `none` keyword or <color>.
     pub fallback: Option<Either<ColorType, None_>>,
 }
 
 /// An SVG paint value without the fallback
 ///
 /// Whereas the spec only allows PaintServer
 /// to have a fallback, Gecko lets the context
 /// properties have a fallback as well.
 #[animation(no_bound(UrlPaintServer))]
-#[derive(Animate, Clone, ComputeSquaredDistance, Debug, MallocSizeOf, PartialEq, ToAnimatedValue,
-         ToAnimatedZero, ToComputedValue, ToCss)]
+#[derive(Animate, Clone, ComputeSquaredDistance, Debug, MallocSizeOf, PartialEq,
+         SpecifiedValueInfo, ToAnimatedValue, ToAnimatedZero, ToComputedValue,
+         ToCss)]
 pub enum SVGPaintKind<ColorType, UrlPaintServer> {
     /// `none`
     #[animation(error)]
     None,
     /// `<color>`
     Color(ColorType),
     /// `url(...)`
     #[animation(error)]
@@ -107,18 +108,18 @@ impl<ColorType: Parse, UrlPaintServer: P
         } else {
             Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
         }
     }
 }
 
 /// A value of <length> | <percentage> | <number> for svg which allow unitless length.
 /// <https://www.w3.org/TR/SVG11/painting.html#StrokeProperties>
-#[derive(Clone, Copy, Debug, MallocSizeOf, PartialEq, ToAnimatedValue, ToAnimatedZero,
-         ToComputedValue, ToCss)]
+#[derive(Clone, Copy, Debug, MallocSizeOf, PartialEq, SpecifiedValueInfo,
+         ToAnimatedValue, ToAnimatedZero, ToComputedValue, ToCss)]
 pub enum SvgLengthOrPercentageOrNumber<LengthOrPercentage, Number> {
     /// <length> | <percentage>
     LengthOrPercentage(LengthOrPercentage),
     /// <number>
     Number(Number),
 }
 
 impl<L, N> ComputeSquaredDistance for SvgLengthOrPercentageOrNumber<L, N>
@@ -185,44 +186,45 @@ impl<LengthOrPercentageType: Parse, Numb
         if let Ok(lop) = input.try(|i| LengthOrPercentageType::parse(context, i)) {
             return Ok(SvgLengthOrPercentageOrNumber::LengthOrPercentage(lop));
         }
         Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
     }
 }
 
 /// An SVG length value supports `context-value` in addition to length.
-#[derive(Clone, ComputeSquaredDistance, Copy, Debug, MallocSizeOf, PartialEq, ToAnimatedValue,
-         ToAnimatedZero, ToComputedValue, ToCss)]
+#[derive(Clone, ComputeSquaredDistance, Copy, Debug, MallocSizeOf, PartialEq,
+         SpecifiedValueInfo, ToAnimatedValue, ToAnimatedZero, ToComputedValue,
+         ToCss)]
 pub enum SVGLength<LengthType> {
     /// `<length> | <percentage> | <number>`
     Length(LengthType),
     /// `context-value`
     ContextValue,
 }
 
 /// Generic value for stroke-dasharray.
-#[derive(Clone, ComputeSquaredDistance, Debug, MallocSizeOf, PartialEq, ToAnimatedValue,
-         ToComputedValue, ToCss)]
+#[derive(Clone, ComputeSquaredDistance, Debug, MallocSizeOf, PartialEq,
+         SpecifiedValueInfo, ToAnimatedValue, ToComputedValue, ToCss)]
 pub enum SVGStrokeDashArray<LengthType> {
     /// `[ <length> | <percentage> | <number> ]#`
     #[css(comma)]
     Values(
         #[css(if_empty = "none", iterable)]
         #[distance(field_bound)]
         Vec<LengthType>,
     ),
     /// `context-value`
     ContextValue,
 }
 
 /// An SVG opacity value accepts `context-{fill,stroke}-opacity` in
 /// addition to opacity value.
-#[derive(Clone, ComputeSquaredDistance, Copy, Debug, MallocSizeOf, PartialEq, ToAnimatedZero,
-         ToComputedValue, ToCss)]
+#[derive(Clone, ComputeSquaredDistance, Copy, Debug, MallocSizeOf, PartialEq,
+         SpecifiedValueInfo, ToAnimatedZero, ToComputedValue, ToCss)]
 pub enum SVGOpacity<OpacityType> {
     /// `<opacity-value>`
     Opacity(OpacityType),
     /// `context-fill-opacity`
     ContextFillOpacity,
     /// `context-stroke-opacity`
     ContextStrokeOpacity,
 }
--- a/servo/components/style/values/generics/text.rs
+++ b/servo/components/style/values/generics/text.rs
@@ -7,34 +7,36 @@
 use app_units::Au;
 use cssparser::Parser;
 use parser::ParserContext;
 use style_traits::ParseError;
 use values::animated::{Animate, Procedure, ToAnimatedZero};
 use values::distance::{ComputeSquaredDistance, SquaredDistance};
 
 /// A generic value for the `initial-letter` property.
-#[derive(Clone, Copy, Debug, MallocSizeOf, PartialEq, ToComputedValue, ToCss)]
+#[derive(Clone, Copy, Debug, MallocSizeOf, PartialEq, SpecifiedValueInfo,
+         ToComputedValue, ToCss)]
 pub enum InitialLetter<Number, Integer> {
     /// `normal`
     Normal,
     /// `<number> <integer>?`
     Specified(Number, Option<Integer>),
 }
 
 impl<N, I> InitialLetter<N, I> {
     /// Returns `normal`.
     #[inline]
     pub fn normal() -> Self {
         InitialLetter::Normal
     }
 }
 
 /// A generic spacing value for the `letter-spacing` and `word-spacing` properties.
-#[derive(Clone, Copy, Debug, MallocSizeOf, PartialEq, ToComputedValue, ToCss)]
+#[derive(Clone, Copy, Debug, MallocSizeOf, PartialEq, SpecifiedValueInfo,
+         ToComputedValue, ToCss)]
 pub enum Spacing<Value> {
     /// `normal`
     Normal,
     /// `<value>`
     Value(Value),
 }
 
 impl<Value> Spacing<Value> {
@@ -105,18 +107,18 @@ where
 {
     #[inline]
     fn to_animated_zero(&self) -> Result<Self, ()> {
         Err(())
     }
 }
 
 /// A generic value for the `line-height` property.
-#[derive(Animate, Clone, ComputeSquaredDistance, Copy, Debug, MallocSizeOf, PartialEq,
-         ToAnimatedValue, ToCss)]
+#[derive(Animate, Clone, ComputeSquaredDistance, Copy, Debug, MallocSizeOf,
+         PartialEq, SpecifiedValueInfo, ToAnimatedValue, ToCss)]
 pub enum LineHeight<Number, LengthOrPercentage> {
     /// `normal`
     Normal,
     /// `-moz-block-height`
     #[cfg(feature = "gecko")]
     MozBlockHeight,
     /// `<number>`
     Number(Number),
@@ -135,16 +137,17 @@ impl<N, L> LineHeight<N, L> {
     /// Returns `normal`.
     #[inline]
     pub fn normal() -> Self {
         LineHeight::Normal
     }
 }
 
 /// A generic value for the `-moz-tab-size` property.
-#[derive(Animate, Clone, ComputeSquaredDistance, Copy, Debug, MallocSizeOf, PartialEq,
-         ToAnimatedValue, ToAnimatedZero, ToComputedValue, ToCss)]
+#[derive(Animate, Clone, ComputeSquaredDistance, Copy, Debug, MallocSizeOf,
+         PartialEq, SpecifiedValueInfo, ToAnimatedValue, ToAnimatedZero,
+         ToComputedValue, ToCss)]
 pub enum MozTabSize<Number, Length> {
     /// A number.
     Number(Number),
     /// A length.
     Length(Length),
 }
--- a/servo/components/style/values/generics/transform.rs
+++ b/servo/components/style/values/generics/transform.rs
@@ -10,31 +10,33 @@ use num_traits::Zero;
 use values::{computed, CSSFloat};
 use values::computed::length::Length as ComputedLength;
 use values::computed::length::LengthOrPercentage as ComputedLengthOrPercentage;
 use values::specified::length::Length as SpecifiedLength;
 use values::specified::length::LengthOrPercentage as SpecifiedLengthOrPercentage;
 
 /// A generic 2D transformation matrix.
 #[allow(missing_docs)]
-#[derive(Clone, Copy, Debug, MallocSizeOf, PartialEq, ToComputedValue, ToCss)]
+#[derive(Clone, Copy, Debug, MallocSizeOf, PartialEq, SpecifiedValueInfo,
+         ToComputedValue, ToCss)]
 #[css(comma, function)]
 pub struct Matrix<T> {
     pub a: T,
     pub b: T,
     pub c: T,
     pub d: T,
     pub e: T,
     pub f: T,
 }
 
 #[allow(missing_docs)]
 #[cfg_attr(rustfmt, rustfmt_skip)]
 #[css(comma, function = "matrix3d")]
-#[derive(Clone, Copy, Debug, MallocSizeOf, PartialEq, ToComputedValue, ToCss)]
+#[derive(Clone, Copy, Debug, MallocSizeOf, PartialEq, SpecifiedValueInfo,
+         ToComputedValue, ToCss)]
 pub struct Matrix3D<T> {
     pub m11: T, pub m12: T, pub m13: T, pub m14: T,
     pub m21: T, pub m22: T, pub m23: T, pub m24: T,
     pub m31: T, pub m32: T, pub m33: T, pub m34: T,
     pub m41: T, pub m42: T, pub m43: T, pub m44: T,
 }
 
 #[cfg_attr(rustfmt, rustfmt_skip)]
@@ -59,18 +61,18 @@ impl<T: Into<f64>> From<Matrix3D<T>> for
             m.m21.into(), m.m22.into(), m.m23.into(), m.m24.into(),
             m.m31.into(), m.m32.into(), m.m33.into(), m.m34.into(),
             m.m41.into(), m.m42.into(), m.m43.into(), m.m44.into(),
         )
     }
 }
 
 /// A generic transform origin.
-#[derive(Animate, Clone, ComputeSquaredDistance, Copy, Debug, MallocSizeOf, PartialEq,
-         ToAnimatedZero, ToComputedValue, ToCss)]
+#[derive(Animate, Clone, ComputeSquaredDistance, Copy, Debug, MallocSizeOf,
+         PartialEq, SpecifiedValueInfo, ToAnimatedZero, ToComputedValue, ToCss)]
 pub struct TransformOrigin<H, V, Depth> {
     /// The horizontal origin.
     pub horizontal: H,
     /// The vertical origin.
     pub vertical: V,
     /// The depth.
     pub depth: Depth,
 }
@@ -152,17 +154,18 @@ impl TimingKeyword {
             TimingKeyword::Ease => (0.25, 0.1, 0.25, 1.),
             TimingKeyword::EaseIn => (0.42, 0., 1., 1.),
             TimingKeyword::EaseOut => (0., 0., 0.58, 1.),
             TimingKeyword::EaseInOut => (0.42, 0., 0.58, 1.),
         }
     }
 }
 
-#[derive(Clone, Debug, MallocSizeOf, PartialEq, ToComputedValue, ToCss)]
+#[derive(Clone, Debug, MallocSizeOf, PartialEq, SpecifiedValueInfo,
+         ToComputedValue, ToCss)]
 /// A single operation in the list of a `transform` value
 pub enum TransformOperation<Angle, Number, Length, Integer, LengthOrPercentage> {
     /// Represents a 2D 2x3 matrix.
     Matrix(Matrix<Number>),
     /// Represents a 3D 4x4 matrix.
     Matrix3D(Matrix3D<Number>),
     /// A 2D skew.
     ///
@@ -256,17 +259,18 @@ pub enum TransformOperation<Angle, Numbe
     AccumulateMatrix {
         from_list:
             Transform<TransformOperation<Angle, Number, Length, Integer, LengthOrPercentage>>,
         to_list: Transform<TransformOperation<Angle, Number, Length, Integer, LengthOrPercentage>>,
         count: Integer,
     },
 }
 
-#[derive(Clone, Debug, MallocSizeOf, PartialEq, ToComputedValue, ToCss)]
+#[derive(Clone, Debug, MallocSizeOf, PartialEq, SpecifiedValueInfo,
+         ToComputedValue, ToCss)]
 /// A value of the `transform` property
 pub struct Transform<T>(#[css(if_empty = "none", iterable)] pub Vec<T>);
 
 impl<Angle, Number, Length, Integer, LengthOrPercentage>
     TransformOperation<Angle, Number, Length, Integer, LengthOrPercentage>
 {
     /// Check if it is any translate function
     pub fn is_translate(&self) -> bool {
@@ -549,63 +553,64 @@ pub fn get_normalized_vector_and_angle<T
         // rotation to not be applied, so we use identity matrix (i.e. rotate3d(0, 0, 1, 0)).
         (0., 0., 1., T::zero())
     } else {
         let vector = vector.normalize();
         (vector.x, vector.y, vector.z, angle)
     }
 }
 
-#[derive(Clone, ComputeSquaredDistance, Copy, Debug, MallocSizeOf, PartialEq, ToAnimatedZero,
-         ToComputedValue, ToCss)]
+#[derive(Clone, ComputeSquaredDistance, Copy, Debug, MallocSizeOf, PartialEq,
+         SpecifiedValueInfo, ToAnimatedZero, ToComputedValue, ToCss)]
 /// A value of the `Rotate` property
 ///
 /// <https://drafts.csswg.org/css-transforms-2/#individual-transforms>
 pub enum Rotate<Number, Angle> {
     /// 'none'
     None,
     /// '<angle>'
     Rotate(Angle),
     /// '<number>{3} <angle>'
     Rotate3D(Number, Number, Number, Angle),
 }
 
-#[derive(Clone, ComputeSquaredDistance, Copy, Debug, MallocSizeOf, PartialEq, ToAnimatedZero,
-         ToComputedValue, ToCss)]
+#[derive(Clone, ComputeSquaredDistance, Copy, Debug, MallocSizeOf, PartialEq,
+         SpecifiedValueInfo, ToAnimatedZero, ToComputedValue, ToCss)]
 /// A value of the `Scale` property
 ///
 /// <https://drafts.csswg.org/css-transforms-2/#individual-transforms>
 pub enum Scale<Number> {
     /// 'none'
     None,
     /// '<number>'
     ScaleX(Number),
     /// '<number>{2}'
     Scale(Number, Number),
     /// '<number>{3}'
     Scale3D(Number, Number, Number),
 }
 
-#[derive(Clone, ComputeSquaredDistance, Debug, MallocSizeOf, PartialEq, ToAnimatedZero,
-         ToComputedValue, ToCss)]
+#[derive(Clone, ComputeSquaredDistance, Debug, MallocSizeOf, PartialEq,
+         SpecifiedValueInfo, ToAnimatedZero, ToComputedValue, ToCss)]
 /// A value of the `Translate` property
 ///
 /// <https://drafts.csswg.org/css-transforms-2/#individual-transforms>
 pub enum Translate<LengthOrPercentage, Length> {
     /// 'none'
     None,
     /// '<length-percentage>'
     TranslateX(LengthOrPercentage),
     /// '<length-percentage> <length-percentage>'
     Translate(LengthOrPercentage, LengthOrPercentage),
     /// '<length-percentage> <length-percentage> <length>'
     Translate3D(LengthOrPercentage, LengthOrPercentage, Length),
 }
 
 #[allow(missing_docs)]
-#[derive(Clone, Copy, Debug, MallocSizeOf, Parse, PartialEq, ToComputedValue, ToCss)]
+#[derive(Clone, Copy, Debug, MallocSizeOf, Parse, PartialEq, SpecifiedValueInfo,
+         ToComputedValue, ToCss)]
 pub enum TransformStyle {
     #[cfg(feature = "servo")]
     Auto,
     Flat,
     #[css(keyword = "preserve-3d")]
     Preserve3d,
 }
--- a/servo/components/style/values/generics/url.rs
+++ b/servo/components/style/values/generics/url.rs
@@ -4,18 +4,19 @@
 
 //! Generic types for url properties.
 
 use cssparser::Parser;
 use parser::{Parse, ParserContext};
 use style_traits::ParseError;
 
 /// An image url or none, used for example in list-style-image
-#[derive(Animate, Clone, ComputeSquaredDistance, Debug, MallocSizeOf, PartialEq, ToAnimatedValue,
-         ToAnimatedZero, ToComputedValue, ToCss)]
+#[derive(Animate, Clone, ComputeSquaredDistance, Debug, MallocSizeOf, PartialEq,
+         SpecifiedValueInfo, ToAnimatedValue, ToAnimatedZero, ToComputedValue,
+         ToCss)]
 pub enum UrlOrNone<Url> {
     /// `none`
     None,
     /// `A URL`
     Url(Url),
 }
 
 impl<Url> UrlOrNone<Url> {
--- a/servo/components/style/values/mod.rs
+++ b/servo/components/style/values/mod.rs
@@ -88,18 +88,19 @@ impl Parse for Impossible {
         _context: &ParserContext,
         input: &mut Parser<'i, 't>,
     ) -> Result<Self, ParseError<'i>> {
         Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
     }
 }
 
 /// A struct representing one of two kinds of values.
-#[derive(Animate, Clone, ComputeSquaredDistance, Copy, MallocSizeOf, PartialEq, ToAnimatedValue,
-         ToAnimatedZero, ToComputedValue, ToCss)]
+#[derive(Animate, Clone, ComputeSquaredDistance, Copy, MallocSizeOf, PartialEq,
+         SpecifiedValueInfo, ToAnimatedValue, ToAnimatedZero, ToComputedValue,
+         ToCss)]
 pub enum Either<A, B> {
     /// The first value.
     First(A),
     /// The second kind of value.
     Second(B),
 }
 
 impl<A: Debug, B: Debug> Debug for Either<A, B> {
@@ -120,17 +121,18 @@ impl<A: Parse, B: Parse> Parse for Eithe
             Ok(Either::First(v))
         } else {
             B::parse(context, input).map(Either::Second)
         }
     }
 }
 
 /// <https://drafts.csswg.org/css-values-4/#custom-idents>
-#[derive(Clone, Debug, Eq, Hash, MallocSizeOf, PartialEq, ToComputedValue)]
+#[derive(Clone, Debug, Eq, Hash, MallocSizeOf, PartialEq, SpecifiedValueInfo,
+         ToComputedValue)]
 pub struct CustomIdent(pub Atom);
 
 impl CustomIdent {
     /// Parse an already-tokenizer identifier
     pub fn from_ident<'i>(
         location: SourceLocation,
         ident: &CowRcStr<'i>,
         excluding: &[&str],
@@ -155,17 +157,17 @@ impl ToCss for CustomIdent {
     where
         W: Write,
     {
         serialize_atom_identifier(&self.0, dest)
     }
 }
 
 /// <https://drafts.csswg.org/css-animations/#typedef-keyframes-name>
-#[derive(Clone, Debug, MallocSizeOf, ToComputedValue)]
+#[derive(Clone, Debug, MallocSizeOf, ToComputedValue, SpecifiedValueInfo)]
 pub enum KeyframesName {
     /// <custom-ident>
     Ident(CustomIdent),
     /// <string>
     QuotedString(Atom),
 }
 
 impl KeyframesName {
--- a/servo/components/style/values/specified/align.rs
+++ b/servo/components/style/values/specified/align.rs
@@ -11,17 +11,17 @@ use gecko_bindings::structs;
 use parser::{Parse, ParserContext};
 use std::fmt::{self, Write};
 use style_traits::{CssWriter, ParseError, ToCss};
 
 bitflags! {
     /// Constants shared by multiple CSS Box Alignment properties
     ///
     /// These constants match Gecko's `NS_STYLE_ALIGN_*` constants.
-    #[derive(MallocSizeOf, ToComputedValue)]
+    #[derive(MallocSizeOf, SpecifiedValueInfo, ToComputedValue)]
     pub struct AlignFlags: u8 {
         // Enumeration stored in the lower 5 bits:
         /// 'auto'
         const AUTO =            structs::NS_STYLE_ALIGN_AUTO as u8;
         /// 'normal'
         const NORMAL =          structs::NS_STYLE_ALIGN_NORMAL as u8;
         /// 'start'
         const START =           structs::NS_STYLE_ALIGN_START as u8;
@@ -130,17 +130,18 @@ pub enum AxisDirection {
     Block,
     /// Inline direction.
     Inline,
 }
 
 /// Shared value for the `align-content` and `justify-content` properties.
 ///
 /// <https://drafts.csswg.org/css-align/#content-distribution>
-#[derive(Clone, Copy, Debug, Eq, MallocSizeOf, PartialEq, ToComputedValue, ToCss)]
+#[derive(Clone, Copy, Debug, Eq, MallocSizeOf, PartialEq, SpecifiedValueInfo,
+         ToComputedValue, ToCss)]
 #[cfg_attr(feature = "servo", derive(Deserialize, Serialize))]
 pub struct ContentDistribution {
     primary: AlignFlags,
     // FIXME(https://github.com/w3c/csswg-drafts/issues/1002): This will need to
     // accept fallback alignment, eventually.
 }
 
 impl ContentDistribution {
@@ -227,17 +228,18 @@ impl ContentDistribution {
             content_position | overflow_position,
         ))
     }
 }
 
 /// Value for the `align-content` property.
 ///
 /// <https://drafts.csswg.org/css-align/#propdef-align-content>
-#[derive(Clone, Copy, Debug, Eq, MallocSizeOf, PartialEq, ToComputedValue, ToCss)]
+#[derive(Clone, Copy, Debug, Eq, MallocSizeOf, PartialEq, SpecifiedValueInfo,
+         ToComputedValue, ToCss)]
 pub struct AlignContent(pub ContentDistribution);
 
 impl Parse for AlignContent {
     fn parse<'i, 't>(
         _: &ParserContext,
         input: &mut Parser<'i, 't>,
     ) -> Result<Self, ParseError<'i>> {
         Ok(AlignContent(ContentDistribution::parse(
@@ -259,17 +261,18 @@ impl From<AlignContent> for u16 {
     fn from(v: AlignContent) -> u16 {
         v.0.as_bits()
     }
 }
 
 /// Value for the `justify-content` property.
 ///
 /// <https://drafts.csswg.org/css-align/#propdef-align-content>
-#[derive(Clone, Copy, Debug, Eq, MallocSizeOf, PartialEq, ToComputedValue, ToCss)]
+#[derive(Clone, Copy, Debug, Eq, MallocSizeOf, PartialEq, SpecifiedValueInfo,
+         ToComputedValue, ToCss)]
 pub struct JustifyContent(pub ContentDistribution);
 
 impl Parse for JustifyContent {
     fn parse<'i, 't>(
         _: &ParserContext,
         input: &mut Parser<'i, 't>,
     ) -> Result<Self, ParseError<'i>> {
         Ok(JustifyContent(ContentDistribution::parse(
@@ -289,17 +292,18 @@ impl From<u16> for JustifyContent {
 #[cfg(feature = "gecko")]
 impl From<JustifyContent> for u16 {
     fn from(v: JustifyContent) -> u16 {
         v.0.as_bits()
     }
 }
 
 /// <https://drafts.csswg.org/css-align/#self-alignment>
-#[derive(Clone, Copy, Debug, Eq, MallocSizeOf, PartialEq, ToComputedValue, ToCss)]
+#[derive(Clone, Copy, Debug, Eq, MallocSizeOf, PartialEq, SpecifiedValueInfo,
+         ToComputedValue, ToCss)]
 pub struct SelfAlignment(pub AlignFlags);
 
 impl SelfAlignment {
     /// The initial value 'auto'
     #[inline]
     pub fn auto() -> Self {
         SelfAlignment(AlignFlags::AUTO)
     }
@@ -339,17 +343,18 @@ impl SelfAlignment {
         let self_position = parse_self_position(input, axis)?;
         Ok(SelfAlignment(overflow_position | self_position))
     }
 }
 
 /// The specified value of the align-self property.
 ///
 /// <https://drafts.csswg.org/css-align/#propdef-align-self>
-#[derive(Clone, Copy, Debug, Eq, MallocSizeOf, PartialEq, ToComputedValue, ToCss)]
+#[derive(Clone, Copy, Debug, Eq, MallocSizeOf, PartialEq, SpecifiedValueInfo,
+         ToComputedValue, ToCss)]
 pub struct AlignSelf(pub SelfAlignment);
 
 impl Parse for AlignSelf {
     fn parse<'i, 't>(
         _: &ParserContext,
         input: &mut Parser<'i, 't>,
     ) -> Result<Self, ParseError<'i>> {
         Ok(AlignSelf(SelfAlignment::parse(
@@ -369,17 +374,18 @@ impl From<AlignSelf> for u8 {
     fn from(align: AlignSelf) -> u8 {
         (align.0).0.bits()
     }
 }
 
 /// The specified value of the justify-self property.
 ///
 /// <https://drafts.csswg.org/css-align/#propdef-justify-self>
-#[derive(Clone, Copy, Debug, Eq, MallocSizeOf, PartialEq, ToComputedValue, ToCss)]
+#[derive(Clone, Copy, Debug, Eq, MallocSizeOf, PartialEq, SpecifiedValueInfo,
+         ToComputedValue, ToCss)]
 pub struct JustifySelf(pub SelfAlignment);
 
 impl Parse for JustifySelf {
     fn parse<'i, 't>(
         _: &ParserContext,
         input: &mut Parser<'i, 't>,
     ) -> Result<Self, ParseError<'i>> {
         Ok(JustifySelf(SelfAlignment::parse(
@@ -399,17 +405,18 @@ impl From<JustifySelf> for u8 {
     fn from(justify: JustifySelf) -> u8 {
         (justify.0).0.bits()
     }
 }
 
 /// Value of the `align-items` property
 ///
 /// <https://drafts.csswg.org/css-align/#self-alignment>
-#[derive(Clone, Copy, Debug, Eq, MallocSizeOf, PartialEq, ToComputedValue, ToCss)]
+#[derive(Clone, Copy, Debug, Eq, MallocSizeOf, PartialEq, SpecifiedValueInfo,
+         ToComputedValue, ToCss)]
 pub struct AlignItems(pub AlignFlags);
 
 impl AlignItems {
     /// The initial value 'normal'
     #[inline]
     pub fn normal() -> Self {
         AlignItems(AlignFlags::NORMAL)
     }
@@ -438,17 +445,18 @@ impl Parse for AlignItems {
         let self_position = parse_self_position(input, AxisDirection::Block)?;
         Ok(AlignItems(self_position | overflow))
     }
 }
 
 /// Value of the `justify-items` property
 ///
 /// <https://drafts.csswg.org/css-align/#justify-items-property>
-#[derive(Clone, Copy, Debug, Eq, MallocSizeOf, PartialEq, ToCss)]
+#[derive(Clone, Copy, Debug, Eq, MallocSizeOf, PartialEq, SpecifiedValueInfo,
+         ToCss)]
 pub struct JustifyItems(pub AlignFlags);
 
 impl JustifyItems {
     /// The initial value 'legacy'
     #[inline]
     pub fn legacy() -> Self {
         JustifyItems(AlignFlags::LEGACY)
     }
--- a/servo/components/style/values/specified/angle.rs
+++ b/servo/components/style/values/specified/angle.rs
@@ -2,17 +2,17 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 //! Specified angles.
 
 use cssparser::{Parser, Token};
 use parser::{Parse, ParserContext};
 use std::fmt::{self, Write};
-use style_traits::{CssWriter, ParseError, ToCss};
+use style_traits::{CssWriter, ParseError, SpecifiedValueInfo, ToCss};
 use values::CSSFloat;
 use values::computed::{Context, ToComputedValue};
 use values::computed::angle::Angle as ComputedAngle;
 use values::specified::calc::CalcNode;
 
 /// A specified angle.
 ///
 /// Computed angles are essentially same as specified ones except for `calc()`
@@ -198,8 +198,10 @@ impl Angle {
             },
             Token::Function(ref name) if name.eq_ignore_ascii_case("calc") => {
                 return input.parse_nested_block(|i| CalcNode::parse_angle(context, i))
             },
             _ => Err(()),
         }.map_err(|()| input.new_unexpected_token_error(token.clone()))
     }
 }
+
+impl SpecifiedValueInfo for Angle {}
--- a/servo/components/style/values/specified/background.rs
+++ b/servo/components/style/values/specified/background.rs
@@ -43,29 +43,31 @@ impl BackgroundSize {
         GenericBackgroundSize::Explicit {
             width: LengthOrPercentageOrAuto::Auto,
             height: LengthOrPercentageOrAuto::Auto,
         }
     }
 }
 
 /// One of the keywords for `background-repeat`.
-#[derive(Clone, Copy, Debug, Eq, MallocSizeOf, Parse, PartialEq, ToComputedValue, ToCss)]
+#[derive(Clone, Copy, Debug, Eq, MallocSizeOf, Parse, PartialEq,
+         SpecifiedValueInfo, ToComputedValue, ToCss)]
 #[allow(missing_docs)]
 pub enum BackgroundRepeatKeyword {
     Repeat,
     Space,
     Round,
     NoRepeat,
 }
 
 /// The specified value for the `background-repeat` property.
 ///
 /// https://drafts.csswg.org/css-backgrounds/#the-background-repeat
-#[derive(Clone, Copy, Debug, MallocSizeOf, PartialEq, ToCss)]
+#[derive(Clone, Copy, Debug, MallocSizeOf, PartialEq, SpecifiedValueInfo,
+         ToCss)]
 pub enum BackgroundRepeat {
     /// `repeat-x`
     RepeatX,
     /// `repeat-y`
     RepeatY,
     /// `[repeat | space | round | no-repeat]{1,2}`
     Keywords(BackgroundRepeatKeyword, Option<BackgroundRepeatKeyword>),
 }
--- a/servo/components/style/values/specified/border.rs
+++ b/servo/components/style/values/specified/border.rs
@@ -15,17 +15,17 @@ use values::generics::border::BorderImag
 use values::generics::border::BorderRadius as GenericBorderRadius;
 use values::generics::border::BorderSpacing as GenericBorderSpacing;
 use values::generics::rect::Rect;
 use values::generics::size::Size;
 use values::specified::{AllowQuirks, Number, NumberOrPercentage};
 use values::specified::length::{Length, LengthOrPercentage, NonNegativeLength};
 
 /// A specified value for a single side of the `border-width` property.
-#[derive(Clone, Debug, MallocSizeOf, PartialEq, ToCss)]
+#[derive(Clone, Debug, MallocSizeOf, PartialEq, SpecifiedValueInfo, ToCss)]
 pub enum BorderSideWidth {
     /// `thin`
     Thin,
     /// `medium`
     Medium,
     /// `thick`
     Thick,
     /// `<length>`
@@ -184,28 +184,30 @@ impl Parse for BorderSpacing {
             Length::parse_non_negative_quirky(context, input, AllowQuirks::Yes).map(From::from)
         }).map(GenericBorderSpacing)
     }
 }
 
 /// A single border-image-repeat keyword.
 #[allow(missing_docs)]
 #[cfg_attr(feature = "servo", derive(Deserialize, Serialize))]
-#[derive(Clone, Copy, Debug, Eq, MallocSizeOf, Parse, PartialEq, ToCss)]
+#[derive(Clone, Copy, Debug, Eq, MallocSizeOf, Parse, PartialEq,
+         SpecifiedValueInfo, ToCss)]
 pub enum BorderImageRepeatKeyword {
     Stretch,
     Repeat,
     Round,
     Space,
 }
 
 /// The specified value for the `border-image-repeat` property.
 ///
 /// https://drafts.csswg.org/css-backgrounds/#the-border-image-repeat
-#[derive(Clone, Copy, Debug, MallocSizeOf, PartialEq, ToComputedValue)]
+#[derive(Clone, Copy, Debug, MallocSizeOf, PartialEq, SpecifiedValueInfo,
+         ToComputedValue)]
 pub struct BorderImageRepeat(pub BorderImageRepeatKeyword, pub BorderImageRepeatKeyword);
 
 impl ToCss for BorderImageRepeat {
     fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
     where
         W: Write,
     {
         self.0.to_css(dest)?;
--- a/servo/components/style/values/specified/box.rs
+++ b/servo/components/style/values/specified/box.rs
@@ -4,27 +4,27 @@
 
 //! Specified types for box properties.
 
 use Atom;
 use cssparser::Parser;
 use parser::{Parse, ParserContext};
 use selectors::parser::SelectorParseErrorKind;
 use std::fmt::{self, Write};
-use style_traits::{CssWriter, ParseError, StyleParseErrorKind, ToCss};
-use values::CustomIdent;
-use values::KeyframesName;
+use style_traits::{CssWriter, ParseError, SpecifiedValueInfo, StyleParseErrorKind, ToCss};
+use values::{CustomIdent, KeyframesName};
 use values::generics::box_::AnimationIterationCount as GenericAnimationIterationCount;
 use values::generics::box_::Perspective as GenericPerspective;
 use values::generics::box_::VerticalAlign as GenericVerticalAlign;
 use values::specified::{AllowQuirks, Number};
 use values::specified::length::{LengthOrPercentage, NonNegativeLength};
 
 #[allow(missing_docs)]
-#[derive(Clone, Copy, Debug, Eq, Hash, MallocSizeOf, Parse, PartialEq, ToComputedValue, ToCss)]
+#[derive(Clone, Copy, Debug, Eq, Hash, MallocSizeOf, Parse, PartialEq,
+         SpecifiedValueInfo, ToComputedValue, ToCss)]
 #[cfg_attr(feature = "servo", derive(Deserialize, Serialize))]
 /// Defines an element’s display type, which consists of
 /// the two basic qualities of how an element generates boxes
 /// <https://drafts.csswg.org/css-display/#propdef-display>
 pub enum Display {
     Inline,
     Block,
     InlineBlock,
@@ -291,17 +291,18 @@ impl AnimationIterationCount {
     /// Returns the value `1.0`.
     #[inline]
     pub fn one() -> Self {
         GenericAnimationIterationCount::Number(Number::new(1.0))
     }
 }
 
 /// A value for the `animation-name` property.
-#[derive(Clone, Debug, Eq, Hash, MallocSizeOf, PartialEq, ToComputedValue)]
+#[derive(Clone, Debug, Eq, Hash, MallocSizeOf, PartialEq, SpecifiedValueInfo,
+         ToComputedValue)]
 pub struct AnimationName(pub Option<KeyframesName>);
 
 impl AnimationName {
     /// Get the name of the animation as an `Atom`.
     pub fn as_atom(&self) -> Option<&Atom> {
         self.0.as_ref().map(|n| n.as_atom())
     }
 
@@ -334,41 +335,45 @@ impl Parse for AnimationName {
 
         input.expect_ident_matching("none")?;
         Ok(AnimationName(None))
     }
 }
 
 #[allow(missing_docs)]
 #[cfg_attr(feature = "servo", derive(Deserialize, Serialize))]
-#[derive(Clone, Copy, Debug, Eq, MallocSizeOf, Parse, PartialEq, ToComputedValue, ToCss)]
+#[derive(Clone, Copy, Debug, Eq, MallocSizeOf, Parse, PartialEq,
+         SpecifiedValueInfo, ToComputedValue, ToCss)]
 pub enum ScrollSnapType {
     None,
     Mandatory,
     Proximity,
 }
 
 #[allow(missing_docs)]
 #[cfg_attr(feature = "servo", derive(Deserialize, Serialize))]
-#[derive(Clone, Copy, Debug, Eq, MallocSizeOf, Parse, PartialEq, ToComputedValue, ToCss)]
+#[derive(Clone, Copy, Debug, Eq, MallocSizeOf, Parse, PartialEq,
+         SpecifiedValueInfo, ToComputedValue, ToCss)]
 pub enum OverscrollBehavior {
     Auto,
     Contain,
     None,
 }
 
 #[allow(missing_docs)]
 #[cfg_attr(feature = "servo", derive(Deserialize, Serialize))]
-#[derive(Clone, Copy, Debug, Eq, MallocSizeOf, Parse, PartialEq, ToComputedValue, ToCss)]
+#[derive(Clone, Copy, Debug, Eq, MallocSizeOf, Parse, PartialEq,
+         SpecifiedValueInfo, ToComputedValue, ToCss)]
 pub enum OverflowClipBox {
     PaddingBox,
     ContentBox,
 }
 
-#[derive(Clone, Debug, MallocSizeOf, PartialEq, ToComputedValue, ToCss)]
+#[derive(Clone, Debug, MallocSizeOf, PartialEq, SpecifiedValueInfo,
+         ToComputedValue, ToCss)]
 /// Provides a rendering hint to the user agent,
 /// stating what kinds of changes the author expects
 /// to perform on the element
 ///
 /// <https://drafts.csswg.org/css-will-change/#will-change>
 pub enum WillChange {
     /// Expresses no particular intent
     Auto,
@@ -483,16 +488,18 @@ impl Parse for TouchAction {
                 } else {
                     Ok(TouchAction::TOUCH_ACTION_PAN_Y)
                 }
             },
         }
     }
 }
 
+impl SpecifiedValueInfo for TouchAction {}
+
 #[cfg(feature = "gecko")]
 impl_bitflags_conversions!(TouchAction);
 
 /// Asserts that all touch-action matches its NS_STYLE_TOUCH_ACTION_* value.
 #[cfg(feature = "gecko")]
 #[inline]
 pub fn assert_touch_action_matches() {
     use gecko_bindings::structs;
@@ -593,16 +600,18 @@ impl Parse for Contain {
         if !result.is_empty() {
             Ok(result)
         } else {
             Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
         }
     }
 }
 
+impl SpecifiedValueInfo for Contain {}
+
 /// A specified value for the `perspective` property.
 pub type Perspective = GenericPerspective<NonNegativeLength>;
 
 impl Parse for Perspective {
     fn parse<'i, 't>(
         context: &ParserContext,
         input: &mut Parser<'i, 't>,
     ) -> Result<Self, ParseError<'i>> {
--- a/servo/components/style/values/specified/calc.rs
+++ b/servo/components/style/values/specified/calc.rs
@@ -4,17 +4,17 @@
 
 //! [Calc expressions][calc].
 //!
 //! [calc]: https://drafts.csswg.org/css-values/#calc-notation
 
 use cssparser::{AngleOrNumber, NumberOrPercentage, Parser, Token};
 use parser::ParserContext;
 use std::fmt::{self, Write};
-use style_traits::{CssWriter, ParseError, StyleParseErrorKind, ToCss};
+use style_traits::{CssWriter, ParseError, SpecifiedValueInfo, StyleParseErrorKind, ToCss};
 use style_traits::values::specified::AllowedNumericType;
 use values::{CSSFloat, CSSInteger};
 use values::computed;
 use values::specified::{Angle, Time};
 use values::specified::length::{AbsoluteLength, FontRelativeLength, NoCalcLength};
 use values::specified::length::ViewportPercentageLength;
 
 /// A node inside a `Calc` expression's AST.
@@ -145,16 +145,18 @@ impl ToCss for CalcLengthOrPercentage {
         serialize!(em, ex);
         serialize_abs!(In, Mm, Pc, Pt, Px, Q);
         serialize!(rem, vh, vmax, vmin, vw);
 
         dest.write_str(")")
     }
 }
 
+impl SpecifiedValueInfo for CalcLengthOrPercentage {}
+
 impl CalcNode {
     /// Tries to parse a single element in the expression, that is, a
     /// `<length>`, `<angle>`, `<time>`, `<percentage>`, according to
     /// `expected_unit`.
     ///
     /// May return a "complex" `CalcNode`, in the presence of a parenthesized
     /// expression, for example.
     fn parse_one<'i, 't>(
--- a/servo/components/style/values/specified/color.rs
+++ b/servo/components/style/values/specified/color.rs
@@ -9,17 +9,18 @@ use cssparser::{BasicParseErrorKind, Num
 #[cfg(feature = "gecko")]
 use gecko_bindings::structs::nscolor;
 use itoa;
 use parser::{Parse, ParserContext};
 #[cfg(feature = "gecko")]
 use properties::longhands::system_colors::SystemColor;
 use std::fmt::{self, Write};
 use std::io::Write as IoWrite;
-use style_traits::{CssWriter, ParseError, StyleParseErrorKind, ToCss, ValueParseErrorKind};
+use style_traits::{CssType, CssWriter, ParseError, StyleParseErrorKind};
+use style_traits::{SpecifiedValueInfo, ToCss, ValueParseErrorKind};
 use super::AllowQuirks;
 use values::computed::{Color as ComputedColor, Context, ToComputedValue};
 use values::specified::calc::CalcNode;
 
 /// Specified color value
 #[derive(Clone, Debug, MallocSizeOf, PartialEq)]
 pub enum Color {
     /// The 'currentColor' keyword
@@ -389,17 +390,17 @@ impl ToComputedValue for Color {
         } else {
             Color::Complex(*computed)
         }
     }
 }
 
 /// Specified color value, but resolved to just RGBA for computed value
 /// with value from color property at the same context.
-#[derive(Clone, Debug, MallocSizeOf, PartialEq, ToCss)]
+#[derive(Clone, Debug, MallocSizeOf, PartialEq, SpecifiedValueInfo, ToCss)]
 pub struct RGBAColor(pub Color);
 
 impl Parse for RGBAColor {
     fn parse<'i, 't>(
         context: &ParserContext,
         input: &mut Parser<'i, 't>,
     ) -> Result<Self, ParseError<'i>> {
         Color::parse(context, input).map(RGBAColor)
@@ -421,20 +422,24 @@ impl ToComputedValue for RGBAColor {
 }
 
 impl From<Color> for RGBAColor {
     fn from(color: Color) -> RGBAColor {
         RGBAColor(color)
     }
 }
 
+impl SpecifiedValueInfo for Color {
+    const SUPPORTED_TYPES: u8 = CssType::COLOR;
+}
+
 /// Specified value for the "color" property, which resolves the `currentcolor`
 /// keyword to the parent color instead of self's color.
 #[cfg_attr(feature = "gecko", derive(MallocSizeOf))]
-#[derive(Clone, Debug, PartialEq, ToCss)]
+#[derive(Clone, Debug, PartialEq, SpecifiedValueInfo, ToCss)]
 pub struct ColorPropertyValue(pub Color);
 
 impl ToComputedValue for ColorPropertyValue {
     type ComputedValue = RGBA;
 
     #[inline]
     fn to_computed_value(&self, context: &Context) -> RGBA {
         self.0
--- a/servo/components/style/values/specified/counters.rs
+++ b/servo/components/style/values/specified/counters.rs
@@ -94,31 +94,33 @@ fn is_decimal(counter_type: &CounterStyl
 #[inline]
 fn is_decimal(counter_type: &CounterStyleType) -> bool {
     *counter_type == CounterStyleOrNone::decimal()
 }
 
 /// The specified value for the `content` property.
 ///
 /// https://drafts.csswg.org/css-content/#propdef-content
-#[derive(Clone, Debug, Eq, MallocSizeOf, PartialEq, ToComputedValue, ToCss)]
+#[derive(Clone, Debug, Eq, MallocSizeOf, PartialEq, SpecifiedValueInfo,
+         ToComputedValue, ToCss)]
 pub enum Content {
     /// `normal` reserved keyword.
     Normal,
     /// `none` reserved keyword.
     None,
     /// `-moz-alt-content`.
     #[cfg(feature = "gecko")]
     MozAltContent,
     /// Content items.
     Items(#[css(iterable)] Box<[ContentItem]>),
 }
 
 /// Items for the `content` property.
-#[derive(Clone, Debug, Eq, MallocSizeOf, PartialEq, ToComputedValue, ToCss)]
+#[derive(Clone, Debug, Eq, MallocSizeOf, PartialEq, SpecifiedValueInfo,
+         ToComputedValue, ToCss)]
 pub enum ContentItem {
     /// Literal string content.
     String(Box<str>),
     /// `counter(name, style)`.
     #[css(comma, function)]
     Counter(CustomIdent, #[css(skip_if = "is_decimal")] CounterStyleType),
     /// `counters(name, separator, style)`.
     #[css(comma, function)]
--- a/servo/components/style/values/specified/effects.rs
+++ b/servo/components/style/values/specified/effects.rs
@@ -30,17 +30,17 @@ pub type BoxShadow =
 #[cfg(feature = "gecko")]
 pub type Filter = GenericFilter<Angle, Factor, NonNegativeLength, SimpleShadow>;
 
 /// A specified value for a single `filter`.
 #[cfg(not(feature = "gecko"))]
 pub type Filter = GenericFilter<Angle, Factor, NonNegativeLength, Impossible>;
 
 /// A value for the `<factor>` parts in `Filter`.
-#[derive(Clone, Debug, MallocSizeOf, PartialEq, ToCss)]
+#[derive(Clone, Debug, MallocSizeOf, PartialEq, SpecifiedValueInfo, ToCss)]
 pub struct Factor(NumberOrPercentage);
 
 impl Factor {
     /// Parse this factor but clamp to one if the value is over 100%.
     #[inline]
     pub fn parse_with_clamping_to_one<'i, 't>(
         context: &ParserContext,
         input: &mut Parser<'i, 't>,
--- a/servo/components/style/values/specified/font.rs
+++ b/servo/components/style/values/specified/font.rs
@@ -10,17 +10,17 @@ use byteorder::{BigEndian, ByteOrder};
 use cssparser::{Parser, Token};
 #[cfg(feature = "gecko")]
 use gecko_bindings::bindings;
 #[cfg(feature = "gecko")]
 use malloc_size_of::{MallocSizeOf, MallocSizeOfOps};
 use parser::{Parse, ParserContext};
 use properties::longhands::system_font::SystemFont;
 use std::fmt::{self, Write};
-use style_traits::{CssWriter, ParseError, StyleParseErrorKind, ToCss};
+use style_traits::{CssWriter, ParseError, SpecifiedValueInfo, StyleParseErrorKind, ToCss};
 use values::CustomIdent;
 use values::computed::{Angle as ComputedAngle, Percentage as ComputedPercentage};
 use values::computed::{font as computed, Context, Length, NonNegativeLength, ToComputedValue};
 use values::computed::font::{FamilyName, FontFamilyList, FontStyleAngle, SingleFontFamily};
 use values::generics::NonNegative;
 use values::generics::font::{self as generics, FeatureTagValue, FontSettings, FontTag};
 use values::generics::font::{KeywordInfo as GenericKeywordInfo, KeywordSize, VariationValue};
 use values::specified::{AllowQuirks, Angle, Integer, LengthOrPercentage, NoCalcLength, Number, Percentage};
@@ -72,17 +72,17 @@ pub const MIN_FONT_WEIGHT: f32 = 1.;
 /// The maximum font-weight value per:
 ///
 /// https://drafts.csswg.org/css-fonts-4/#font-weight-numeric-values
 pub const MAX_FONT_WEIGHT: f32 = 1000.;
 
 /// A specified font-weight value.
 ///
 /// https://drafts.csswg.org/css-fonts-4/#propdef-font-weight
-#[derive(Clone, Copy, Debug, MallocSizeOf, PartialEq, ToCss)]
+#[derive(Clone, Copy, Debug, MallocSizeOf, PartialEq, SpecifiedValueInfo, ToCss)]
 pub enum FontWeight {
     /// `<font-weight-absolute>`
     Absolute(AbsoluteFontWeight),
     /// Bolder variant
     Bolder,
     /// Lighter variant
     Lighter,
     /// System font variant.
@@ -149,17 +149,17 @@ impl ToComputedValue for FontWeight {
             Number::from_computed_value(&computed.0)
         ))
     }
 }
 
 /// An absolute font-weight value for a @font-face rule.
 ///
 /// https://drafts.csswg.org/css-fonts-4/#font-weight-absolute-values
-#[derive(Clone, Copy, Debug, MallocSizeOf, PartialEq, ToCss)]
+#[derive(Clone, Copy, Debug, MallocSizeOf, PartialEq, SpecifiedValueInfo, ToCss)]
 pub enum AbsoluteFontWeight {
     /// A `<number>`, with the additional constraints specified in:
     ///
     ///   https://drafts.csswg.org/css-fonts-4/#font-weight-numeric-values
     Weight(Number),
     /// Normal font weight. Same as 400.
     Normal,
     /// Bold font weight. Same as 700.
@@ -328,17 +328,18 @@ impl SpecifiedFontStyle {
         Angle::from_degrees(
             DEFAULT_FONT_STYLE_OBLIQUE_ANGLE_DEGREES,
             /* was_calc = */ false,
         )
     }
 }
 
 /// The specified value of the `font-style` property.
-#[derive(Clone, Copy, Debug, MallocSizeOf, PartialEq, ToCss)]
+#[derive(Clone, Copy, Debug, MallocSizeOf, PartialEq, SpecifiedValueInfo,
+         ToCss)]
 #[allow(missing_docs)]
 pub enum FontStyle {
     Specified(SpecifiedFontStyle),
     System(SystemFont),
 }
 
 impl FontStyle {
     /// Return the `normal` value.
@@ -373,25 +374,27 @@ impl Parse for FontStyle {
         Ok(FontStyle::Specified(SpecifiedFontStyle::parse(context, input)?))
     }
 }
 
 /// A value for the `font-stretch` property.
 ///
 /// https://drafts.csswg.org/css-fonts-4/#font-stretch-prop
 #[allow(missing_docs)]
-#[derive(Clone, Copy, Debug, MallocSizeOf, PartialEq, ToCss)]
+#[derive(Clone, Copy, Debug, MallocSizeOf, PartialEq, SpecifiedValueInfo,
+         ToCss)]
 pub enum FontStretch {
     Stretch(Percentage),
     Keyword(FontStretchKeyword),
     System(SystemFont),
 }
 
 /// A keyword value for `font-stretch`.
-#[derive(Clone, Copy, Debug, MallocSizeOf, Parse, PartialEq, ToCss)]
+#[derive(Clone, Copy, Debug, MallocSizeOf, Parse, PartialEq, SpecifiedValueInfo,
+         ToCss)]
 #[allow(missing_docs)]
 pub enum FontStretchKeyword {
     Normal,
     Condensed,
     UltraCondensed,
     ExtraCondensed,
     SemiCondensed,
     SemiExpanded,
@@ -496,17 +499,17 @@ impl ToComputedValue for FontStretch {
         }
     }
 
     fn from_computed_value(computed: &Self::ComputedValue) -> Self {
         FontStretch::Stretch(Percentage::from_computed_value(&computed.0))
     }
 }
 
-#[derive(Clone, Debug, MallocSizeOf, PartialEq, ToCss)]
+#[derive(Clone, Debug, MallocSizeOf, PartialEq, SpecifiedValueInfo, ToCss)]
 /// A specified font-size value
 pub enum FontSize {
     /// A length; e.g. 10px.
     Length(LengthOrPercentage),
     /// A keyword value, along with a ratio and absolute offset.
     /// The ratio in any specified keyword value
     /// will be 1 (with offset 0), but we cascade keywordness even
     /// after font-relative (percent and em) values
@@ -597,16 +600,18 @@ impl Parse for FontFamily {
     fn parse<'i, 't>(
         _: &ParserContext,
         input: &mut Parser<'i, 't>,
     ) -> Result<FontFamily, ParseError<'i>> {
         FontFamily::parse_specified(input)
     }
 }
 
+impl SpecifiedValueInfo for FontFamily {}
+
 /// `FamilyName::parse` is based on `SingleFontFamily::parse` and not the other way around
 /// because we want the former to exclude generic family keywords.
 impl Parse for FamilyName {
     fn parse<'i, 't>(
         _: &ParserContext,
         input: &mut Parser<'i, 't>,
     ) -> Result<Self, ParseError<'i>> {
         match SingleFontFamily::parse(input) {
@@ -614,17 +619,17 @@ impl Parse for FamilyName {
             Ok(SingleFontFamily::Generic(_)) => {
                 Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
             },
             Err(e) => Err(e),
         }
     }
 }
 
-#[derive(Clone, Copy, Debug, MallocSizeOf, PartialEq, ToCss)]
+#[derive(Clone, Copy, Debug, MallocSizeOf, PartialEq, SpecifiedValueInfo, ToCss)]
 /// Preserve the readability of text when font fallback occurs
 pub enum FontSizeAdjust {
     /// None variant
     None,
     /// Number variant
     Number(Number),
     /// system font
     System(SystemFont),
@@ -1074,17 +1079,17 @@ bitflags! {
         const SWASH = 0x10;
         /// Ornaments glyphs
         const ORNAMENTS = 0x20;
         /// Annotation forms
         const ANNOTATION = 0x40;
     }
 }
 
-#[derive(Clone, Debug, MallocSizeOf, PartialEq, ToCss)]
+#[derive(Clone, Debug, MallocSizeOf, PartialEq, SpecifiedValueInfo, ToCss)]
 /// Set of variant alternates
 pub enum VariantAlternates {
     /// Enables display of stylistic alternates
     #[css(function)]
     Stylistic(CustomIdent),
     /// Enables display with stylistic sets
     #[css(comma, function)]
     Styleset(#[css(iterable)] Box<[CustomIdent]>),
@@ -1099,17 +1104,17 @@ pub enum VariantAlternates {
     Ornaments(CustomIdent),
     /// Enables display of alternate annotation forms
     #[css(function)]
     Annotation(CustomIdent),
     /// Enables display of historical forms
     HistoricalForms,
 }
 
-#[derive(Clone, Debug, MallocSizeOf, PartialEq, ToCss)]
+#[derive(Clone, Debug, MallocSizeOf, PartialEq, SpecifiedValueInfo, ToCss)]
 /// List of Variant Alternates
 pub struct VariantAlternatesList(
     #[css(if_empty = "normal", iterable)] pub Box<[VariantAlternates]>,
 );
 
 impl VariantAlternatesList {
     /// Returns the length of all variant alternates.
     pub fn len(&self) -> usize {
@@ -1120,17 +1125,17 @@ impl VariantAlternatesList {
             VariantAlternates::Annotation(_) => acc + 1,
             VariantAlternates::Styleset(ref slice) |
             VariantAlternates::CharacterVariant(ref slice) => acc + slice.len(),
             _ => acc,
         })
     }
 }
 
-#[derive(Clone, Debug, MallocSizeOf, PartialEq, ToCss)]
+#[derive(Clone, Debug, MallocSizeOf, PartialEq, SpecifiedValueInfo, ToCss)]
 /// Control over the selection of these alternate glyphs
 pub enum FontVariantAlternates {
     /// Use alternative glyph from value
     Value(VariantAlternatesList),
     /// Use system font glyph
     System(SystemFont),
 }
 
@@ -1341,16 +1346,18 @@ impl ToCss for VariantEastAsian {
         debug_assert!(has_any);
         Ok(())
     }
 }
 
 #[cfg(feature = "gecko")]
 impl_gecko_keyword_conversions!(VariantEastAsian, u16);
 
+impl SpecifiedValueInfo for VariantEastAsian {}
+
 /// Asserts that all variant-east-asian matches its NS_FONT_VARIANT_EAST_ASIAN_* value.
 #[cfg(feature = "gecko")]
 #[inline]
 pub fn assert_variant_east_asian_matches() {
     use gecko_bindings::structs;
 
     macro_rules! check_variant_east_asian {
         ( $( $a:ident => $b:path),*, ) => {
@@ -1371,17 +1378,17 @@ pub fn assert_variant_east_asian_matches
         NS_FONT_VARIANT_EAST_ASIAN_PROP_WIDTH => VariantEastAsian::PROPORTIONAL_WIDTH,
         NS_FONT_VARIANT_EAST_ASIAN_RUBY => VariantEastAsian::RUBY,
         NS_FONT_VARIANT_EAST_ASIAN_SIMPLIFIED => VariantEastAsian::SIMPLIFIED,
         NS_FONT_VARIANT_EAST_ASIAN_TRADITIONAL => VariantEastAsian::TRADITIONAL,
     }
 }
 
 #[cfg_attr(feature = "gecko", derive(MallocSizeOf))]
-#[derive(Clone, Debug, PartialEq, ToCss)]
+#[derive(Clone, Debug, PartialEq, SpecifiedValueInfo, ToCss)]
 /// Allows control of glyph substitution and sizing in East Asian text.
 pub enum FontVariantEastAsian {
     /// Value variant with `variant-east-asian`
     Value(VariantEastAsian),
     /// System font variant
     System(SystemFont),
 }
 
@@ -1563,16 +1570,18 @@ impl ToCss for VariantLigatures {
         write_value!(VariantLigatures::CONTEXTUAL => "contextual");
         write_value!(VariantLigatures::NO_CONTEXTUAL => "no-contextual");
 
         debug_assert!(has_any);
         Ok(())
     }
 }
 
+impl SpecifiedValueInfo for VariantLigatures {}
+
 #[cfg(feature = "gecko")]
 impl_gecko_keyword_conversions!(VariantLigatures, u16);
 
 /// Asserts that all variant-east-asian matches its NS_FONT_VARIANT_EAST_ASIAN_* value.
 #[cfg(feature = "gecko")]
 #[inline]
 pub fn assert_variant_ligatures_matches() {
     use gecko_bindings::structs;
@@ -1596,17 +1605,17 @@ pub fn assert_variant_ligatures_matches(
         NS_FONT_VARIANT_LIGATURES_HISTORICAL => VariantLigatures::HISTORICAL_LIGATURES,
         NS_FONT_VARIANT_LIGATURES_NO_HISTORICAL => VariantLigatures::NO_HISTORICAL_LIGATURES,
         NS_FONT_VARIANT_LIGATURES_CONTEXTUAL => VariantLigatures::CONTEXTUAL,
         NS_FONT_VARIANT_LIGATURES_NO_CONTEXTUAL => VariantLigatures::NO_CONTEXTUAL,
     }
 }
 
 #[cfg_attr(feature = "gecko", derive(MallocSizeOf))]
-#[derive(Clone, Debug, PartialEq, ToCss)]
+#[derive(Clone, Debug, PartialEq, SpecifiedValueInfo, ToCss)]
 /// Ligatures and contextual forms are ways of combining glyphs
 /// to produce more harmonized forms
 pub enum FontVariantLigatures {
     /// Value variant with `variant-ligatures`
     Value(VariantLigatures),
     /// System font variant
     System(SystemFont),
 }
@@ -1793,16 +1802,18 @@ impl ToCss for VariantNumeric {
         write_value!(VariantNumeric::SLASHED_ZERO => "slashed-zero");
         write_value!(VariantNumeric::ORDINAL => "ordinal");
 
         debug_assert!(has_any);
         Ok(())
     }
 }
 
+impl SpecifiedValueInfo for VariantNumeric {}
+
 #[cfg(feature = "gecko")]
 impl_gecko_keyword_conversions!(VariantNumeric, u8);
 
 /// Asserts that all variant-east-asian matches its NS_FONT_VARIANT_EAST_ASIAN_* value.
 #[cfg(feature = "gecko")]
 #[inline]
 pub fn assert_variant_numeric_matches() {
     use gecko_bindings::structs;
@@ -1825,17 +1836,17 @@ pub fn assert_variant_numeric_matches() 
         NS_FONT_VARIANT_NUMERIC_DIAGONAL_FRACTIONS => VariantNumeric::DIAGONAL_FRACTIONS,
         NS_FONT_VARIANT_NUMERIC_STACKED_FRACTIONS => VariantNumeric::STACKED_FRACTIONS,
         NS_FONT_VARIANT_NUMERIC_SLASHZERO => VariantNumeric::SLASHED_ZERO,
         NS_FONT_VARIANT_NUMERIC_ORDINAL => VariantNumeric::ORDINAL,
     }
 }
 
 #[cfg_attr(feature = "gecko", derive(MallocSizeOf))]
-#[derive(Clone, Debug, PartialEq, ToCss)]
+#[derive(Clone, Debug, PartialEq, SpecifiedValueInfo, ToCss)]
 /// Specifies control over numerical forms.
 pub enum FontVariantNumeric {
     /// Value variant with `variant-numeric`
     Value(VariantNumeric),
     /// System font
     System(SystemFont),
 }
 
@@ -1933,17 +1944,17 @@ impl Parse for FontVariantNumeric {
     }
 }
 
 /// This property provides low-level control over OpenType or TrueType font features.
 pub type SpecifiedFontFeatureSettings = FontSettings<FeatureTagValue<Integer>>;
 
 /// Define initial settings that apply when the font defined by an @font-face
 /// rule is rendered.
-#[derive(Clone, Debug, MallocSizeOf, PartialEq, ToCss)]
+#[derive(Clone, Debug, MallocSizeOf, PartialEq, SpecifiedValueInfo, ToCss)]
 pub enum FontFeatureSettings {
     /// Value of `FontSettings`
     Value(SpecifiedFontFeatureSettings),
     /// System font
     System(SystemFont),
 }
 
 impl FontFeatureSettings {
@@ -1976,17 +1987,18 @@ impl Parse for FontFeatureSettings {
     fn parse<'i, 't>(
         context: &ParserContext,
         input: &mut Parser<'i, 't>,
     ) -> Result<FontFeatureSettings, ParseError<'i>> {
         SpecifiedFontFeatureSettings::parse(context, input).map(FontFeatureSettings::Value)
     }
 }
 
-#[derive(Clone, Copy, Debug, MallocSizeOf, PartialEq, ToComputedValue)]
+#[derive(Clone, Copy, Debug, MallocSizeOf, PartialEq, SpecifiedValueInfo,
+         ToComputedValue)]
 /// Whether user agents are allowed to synthesize bold or oblique font faces
 /// when a font family lacks bold or italic faces
 pub struct FontSynthesis {
     /// If a `font-weight` is requested that the font family does not contain,
     /// the user agent may synthesize the requested weight from the weights
     /// that do exist in the font family.
     pub weight: bool,
     /// If a font-style is requested that the font family does not contain,
@@ -2074,17 +2086,17 @@ impl From<FontSynthesis> for u8 {
         }
         if v.style {
             bits |= structs::NS_FONT_SYNTHESIS_STYLE as u8;
         }
         bits
     }
 }
 
-#[derive(Clone, Debug, Eq, MallocSizeOf, PartialEq, ToCss)]
+#[derive(Clone, Debug, Eq, MallocSizeOf, PartialEq, SpecifiedValueInfo, ToCss)]
 /// Allows authors to explicitly specify the language system of the font,
 /// overriding the language system implied by the content language
 pub enum FontLanguageOverride {
     /// When rendering with OpenType fonts,
     /// the content language of the element is
     /// used to infer the OpenType language system
     Normal,
     /// Single three-letter case-sensitive OpenType language system tag,
@@ -2164,17 +2176,17 @@ impl Parse for FontLanguageOverride {
 }
 
 /// This property provides low-level control over OpenType or TrueType font
 /// variations.
 pub type SpecifiedFontVariationSettings = FontSettings<VariationValue<Number>>;
 
 /// Define initial settings that apply when the font defined by an @font-face
 /// rule is rendered.
-#[derive(Clone, Debug, MallocSizeOf, PartialEq, ToCss)]
+#[derive(Clone, Debug, MallocSizeOf, PartialEq, SpecifiedValueInfo, ToCss)]
 pub enum FontVariationSettings {
     /// Value of `FontSettings`
     Value(SpecifiedFontVariationSettings),
     /// System font
     System(SystemFont),
 }
 
 impl FontVariationSettings {
@@ -2249,34 +2261,36 @@ impl Parse for VariationValue<Number> {
         input: &mut Parser<'i, 't>,
     ) -> Result<Self, ParseError<'i>> {
         let tag = FontTag::parse(context, input)?;
         let value = Number::parse(context, input)?;
         Ok(Self { tag, value })
     }
 }
 
-#[derive(Clone, Copy, Debug, MallocSizeOf, PartialEq, ToComputedValue, ToCss)]
+#[derive(Clone, Copy, Debug, MallocSizeOf, PartialEq, SpecifiedValueInfo,
+         ToComputedValue, ToCss)]
 /// text-zoom. Enable if true, disable if false
 pub struct XTextZoom(#[css(skip)] pub bool);
 
 impl Parse for XTextZoom {
     fn parse<'i, 't>(
         _: &ParserContext,
         input: &mut Parser<'i, 't>,
     ) -> Result<XTextZoom, ParseError<'i>> {
         debug_assert!(
             false,
             "Should be set directly by presentation attributes only."
         );
         Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
     }
 }
 
-#[derive(Clone, Debug, MallocSizeOf, PartialEq, ToComputedValue, ToCss)]
+#[derive(Clone, Debug, MallocSizeOf, PartialEq, SpecifiedValueInfo,
+         ToComputedValue, ToCss)]
 /// Internal property that reflects the lang attribute
 pub struct XLang(#[css(skip)] pub Atom);
 
 impl XLang {
     #[inline]
     /// Get default value for `-x-lang`
     pub fn get_initial_value() -> XLang {
         XLang(atom!(""))
@@ -2292,17 +2306,17 @@ impl Parse for XLang {
             false,
             "Should be set directly by presentation attributes only."
         );
         Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
     }
 }
 
 #[cfg_attr(feature = "gecko", derive(MallocSizeOf))]
-#[derive(Clone, Copy, Debug, PartialEq, ToCss)]
+#[derive(Clone, Copy, Debug, PartialEq, SpecifiedValueInfo, ToCss)]
 /// Specifies the minimum font size allowed due to changes in scriptlevel.
 /// Ref: https://wiki.mozilla.org/MathML:mstyle
 pub struct MozScriptMinSize(pub NoCalcLength);
 
 impl MozScriptMinSize {
     #[inline]
     /// Calculate initial value of -moz-script-min-size.
     pub fn get_initial_value() -> Length {
@@ -2319,17 +2333,17 @@ impl Parse for MozScriptMinSize {
             false,
             "Should be set directly by presentation attributes only."
         );
         Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
     }
 }
 
 #[cfg_attr(feature = "gecko", derive(MallocSizeOf))]
-#[derive(Clone, Copy, Debug, PartialEq, ToCss)]
+#[derive(Clone, Copy, Debug, PartialEq, SpecifiedValueInfo, ToCss)]
 /// Changes the scriptlevel in effect for the children.
 /// Ref: https://wiki.mozilla.org/MathML:mstyle
 ///
 /// The main effect of scriptlevel is to control the font size.
 /// https://www.w3.org/TR/MathML3/chapter3.html#presm.scriptlevel
 pub enum MozScriptLevel {
     /// Change `font-size` relatively.
     Relative(i32),
@@ -2354,17 +2368,18 @@ impl Parse for MozScriptLevel {
             return Ok(MozScriptLevel::Relative(i));
         }
         input.expect_ident_matching("auto")?;
         Ok(MozScriptLevel::Auto)
     }
 }
 
 #[cfg_attr(feature = "gecko", derive(MallocSizeOf))]
-#[derive(Clone, Copy, Debug, PartialEq, ToComputedValue, ToCss)]
+#[derive(Clone, Copy, Debug, PartialEq, SpecifiedValueInfo, ToComputedValue,
+         ToCss)]
 /// Specifies the multiplier to be used to adjust font size
 /// due to changes in scriptlevel.
 ///
 /// Ref: https://www.w3.org/TR/MathML3/chapter3.html#presm.mstyle.attrs
 pub struct MozScriptSizeMultiplier(pub f32);
 
 impl MozScriptSizeMultiplier {
     #[inline]
--- a/servo/components/style/values/specified/grid.rs
+++ b/servo/components/style/values/specified/grid.rs
@@ -99,17 +99,17 @@ pub fn parse_line_names<'i, 't>(
 
         Ok(values.into_boxed_slice())
     })
 }
 
 /// The type of `repeat` function (only used in parsing).
 ///
 /// <https://drafts.csswg.org/css-grid/#typedef-track-repeat>
-#[derive(Clone, Copy, Debug, PartialEq)]
+#[derive(Clone, Copy, Debug, PartialEq, SpecifiedValueInfo)]
 #[cfg_attr(feature = "servo", derive(MallocSizeOf))]
 enum RepeatType {
     /// [`<auto-repeat>`](https://drafts.csswg.org/css-grid/#typedef-auto-repeat)
     Auto,
     /// [`<track-repeat>`](https://drafts.csswg.org/css-grid/#typedef-track-repeat)
     Normal,
     /// [`<fixed-repeat>`](https://drafts.csswg.org/css-grid/#typedef-fixed-repeat)
     Fixed,
--- a/servo/components/style/values/specified/image.rs
+++ b/servo/components/style/values/specified/image.rs
@@ -12,17 +12,18 @@ use cssparser::{Parser, Token};
 use custom_properties::SpecifiedValue;
 use parser::{Parse, ParserContext};
 use selectors::parser::SelectorParseErrorKind;
 #[cfg(feature = "servo")]
 use servo_url::ServoUrl;
 use std::cmp::Ordering;
 use std::f32::consts::PI;
 use std::fmt::{self, Write};
-use style_traits::{CssWriter, ParseError, StyleParseErrorKind, ToCss};
+use style_traits::{CssType, CssWriter, ParseError, StyleParseErrorKind};
+use style_traits::{SpecifiedValueInfo, ToCss};
 use values::{Either, None_};
 #[cfg(feature = "gecko")]
 use values::computed::{Context, Position as ComputedPosition, ToComputedValue};
 use values::generics::image::{self as generic, Circle, CompatMode, Ellipse, ShapeExtent};
 use values::generics::image::PaintWorklet;
 use values::generics::position::Position as GenericPosition;
 use values::specified::{Angle, Color, Length, LengthOrPercentage};
 use values::specified::{Number, NumberOrPercentage, Percentage, RGBAColor};
@@ -49,16 +50,20 @@ pub type Gradient = generic::Gradient<
     LineDirection,
     Length,
     LengthOrPercentage,
     GradientPosition,
     RGBAColor,
     Angle,
 >;
 
+impl SpecifiedValueInfo for Gradient {
+    const SUPPORTED_TYPES: u8 = CssType::GRADIENT;
+}
+
 /// A specified gradient kind.
 #[cfg(not(feature = "gecko"))]
 pub type GradientKind =
     generic::GradientKind<LineDirection, Length, LengthOrPercentage, Position, Angle>;
 
 /// A specified gradient kind.
 #[cfg(feature = "gecko")]
 pub type GradientKind =
--- a/servo/components/style/values/specified/inherited_box.rs
+++ b/servo/components/style/values/specified/inherited_box.rs
@@ -10,17 +10,17 @@ use std::f64::consts::PI;
 use std::fmt::{self, Write};
 use style_traits::{CssWriter, ParseError, StyleParseErrorKind, ToCss};
 use values::computed;
 use values::computed::{Context, Orientation, ToComputedValue};
 use values::specified::Angle;
 
 /// The specified value of the `image-orientation` property.
 /// https://drafts.csswg.org/css-images/#propdef-image-orientation
-#[derive(Clone, Copy, Debug, MallocSizeOf, PartialEq)]
+#[derive(Clone, Copy, Debug, MallocSizeOf, PartialEq, SpecifiedValueInfo)]
 pub struct ImageOrientation {
     /// The angle specified, if any
     pub angle: Option<Angle>,
 
     /// Whether or not "flip" was specified
     pub flipped: bool,
 }
 
--- a/servo/components/style/values/specified/length.rs
+++ b/servo/components/style/values/specified/length.rs
@@ -8,17 +8,17 @@
 
 use app_units::Au;
 use cssparser::{Parser, Token};
 use euclid::Size2D;
 use font_metrics::FontMetricsQueryResult;
 use parser::{Parse, ParserContext};
 use std::cmp;
 use std::ops::{Add, Mul};
-use style_traits::{ParseError, StyleParseErrorKind};
+use style_traits::{ParseError, SpecifiedValueInfo, StyleParseErrorKind};
 use style_traits::values::specified::AllowedNumericType;
 use super::{AllowQuirks, Number, Percentage, ToComputedValue};
 use values::{Auto, CSSFloat, Either, Normal};
 use values::computed::{self, CSSPixelLength, Context, ExtremumLength};
 use values::generics::NonNegative;
 use values::specified::calc::CalcNode;
 
 pub use values::specified::calc::CalcLengthOrPercentage;
@@ -489,21 +489,23 @@ impl NoCalcLength {
 
     /// Get an absolute length from a px value.
     #[inline]
     pub fn from_px(px_value: CSSFloat) -> NoCalcLength {
         NoCalcLength::Absolute(AbsoluteLength::Px(px_value))
     }
 }
 
+impl SpecifiedValueInfo for NoCalcLength {}
+
 /// 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, Debug, MallocSizeOf, PartialEq, ToCss)]
+#[derive(Clone, Debug, MallocSizeOf, PartialEq, SpecifiedValueInfo, ToCss)]
 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>),
 }
@@ -694,17 +696,17 @@ impl NonNegativeLength {
     }
 }
 
 /// Either a NonNegativeLength or the `auto` keyword.
 pub type NonNegativeLengthOrAuto = Either<NonNegativeLength, Auto>;
 
 /// A length or a percentage value.
 #[allow(missing_docs)]
-#[derive(Clone, Debug, MallocSizeOf, PartialEq, ToCss)]
+#[derive(Clone, Debug, MallocSizeOf, PartialEq, SpecifiedValueInfo, ToCss)]
 pub enum LengthOrPercentage {
     Length(NoCalcLength),
     Percentage(computed::Percentage),
     Calc(Box<CalcLengthOrPercentage>),
 }
 
 impl From<Length> for LengthOrPercentage {
     fn from(len: Length) -> LengthOrPercentage {
@@ -840,17 +842,17 @@ impl LengthOrPercentage {
         allow_quirks: AllowQuirks,
     ) -> Result<Self, ParseError<'i>> {
         Self::parse_internal(context, input, AllowedNumericType::All, allow_quirks)
     }
 }
 
 /// Either a `<length>`, a `<percentage>`, or the `auto` keyword.
 #[allow(missing_docs)]
-#[derive(Clone, Debug, MallocSizeOf, PartialEq, ToCss)]
+#[derive(Clone, Debug, MallocSizeOf, PartialEq, SpecifiedValueInfo, ToCss)]
 pub enum LengthOrPercentageOrAuto {
     Length(NoCalcLength),
     Percentage(computed::Percentage),
     Auto,
     Calc(Box<CalcLengthOrPercentage>),
 }
 
 impl From<NoCalcLength> for LengthOrPercentageOrAuto {
@@ -1010,17 +1012,17 @@ impl Parse for NonNegativeLengthOrPercen
         Ok(NonNegative(LengthOrPercentageOrAuto::parse_non_negative(
             context,
             input,
         )?))
     }
 }
 
 /// Either a `<length>`, a `<percentage>`, or the `none` keyword.
-#[derive(Clone, Debug, MallocSizeOf, PartialEq, ToCss)]
+#[derive(Clone, Debug, MallocSizeOf, PartialEq, SpecifiedValueInfo, ToCss)]
 #[allow(missing_docs)]
 pub enum LengthOrPercentageOrNone {
     Length(NoCalcLength),
     Percentage(computed::Percentage),
     Calc(Box<CalcLengthOrPercentage>),
     None,
 }
 
@@ -1190,17 +1192,17 @@ impl LengthOrNumber {
 
 /// A value suitable for a `min-width` or `min-height` property.
 ///
 /// Unlike `max-width` or `max-height` properties, a MozLength can be `auto`,
 /// and cannot be `none`.
 ///
 /// Note that it only accepts non-negative values.
 #[allow(missing_docs)]
-#[derive(Clone, Debug, MallocSizeOf, PartialEq, ToCss)]
+#[derive(Clone, Debug, MallocSizeOf, PartialEq, SpecifiedValueInfo, ToCss)]
 pub enum MozLength {
     LengthOrPercentageOrAuto(LengthOrPercentageOrAuto),
     ExtremumLength(ExtremumLength),
 }
 
 impl Parse for MozLength {
     fn parse<'i, 't>(
         context: &ParserContext,
@@ -1247,17 +1249,17 @@ impl MozLength {
     #[inline]
     pub fn zero_percent() -> Self {
         MozLength::LengthOrPercentageOrAuto(LengthOrPercentageOrAuto::zero_percent())
     }
 }
 
 /// A value suitable for a `max-width` or `max-height` property.
 #[allow(missing_docs)]
-#[derive(Clone, Debug, MallocSizeOf, PartialEq, ToCss)]
+#[derive(Clone, Debug, MallocSizeOf, PartialEq, SpecifiedValueInfo, ToCss)]
 pub enum MaxLength {
     LengthOrPercentageOrNone(LengthOrPercentageOrNone),
     ExtremumLength(ExtremumLength),
 }
 
 impl Parse for MaxLength {
     fn parse<'i, 't>(
         context: &ParserContext,
--- a/servo/components/style/values/specified/list.rs
+++ b/servo/components/style/values/specified/list.rs
@@ -10,17 +10,18 @@ use std::fmt::{self, Write};
 use style_traits::{CssWriter, ParseError, StyleParseErrorKind, ToCss};
 #[cfg(feature = "gecko")]
 use values::CustomIdent;
 #[cfg(feature = "gecko")]
 use values::generics::CounterStyleOrNone;
 
 /// Specified and computed `list-style-type` property.
 #[cfg(feature = "gecko")]
-#[derive(Clone, Debug, Eq, MallocSizeOf, PartialEq, ToComputedValue, ToCss)]
+#[derive(Clone, Debug, Eq, MallocSizeOf, PartialEq, SpecifiedValueInfo,
+         ToComputedValue, ToCss)]
 pub enum ListStyleType {
     /// <counter-style> | none
     CounterStyle(CounterStyleOrNone),
     /// <string>
     String(String),
 }
 
 #[cfg(feature = "gecko")]
@@ -72,17 +73,18 @@ impl Parse for ListStyleType {
         ))
     }
 }
 
 /// Specified and computed `quote` property.
 ///
 /// FIXME(emilio): It's a shame that this allocates all the time it's computed,
 /// probably should just be refcounted.
-#[derive(Clone, Debug, MallocSizeOf, PartialEq, ToComputedValue)]
+#[derive(Clone, Debug, MallocSizeOf, PartialEq, SpecifiedValueInfo,
+         ToComputedValue)]
 pub struct Quotes(pub Box<[(Box<str>, Box<str>)]>);
 
 impl ToCss for Quotes {
     fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
     where
         W: Write,
     {
         let mut iter = self.0.iter();
--- a/servo/components/style/values/specified/mod.rs
+++ b/servo/components/style/values/specified/mod.rs
@@ -8,17 +8,17 @@
 
 use {Atom, Namespace, Prefix};
 use context::QuirksMode;
 use cssparser::{Parser, Token};
 use num_traits::One;
 use parser::{Parse, ParserContext};
 use std::f32;
 use std::fmt::{self, Write};
-use style_traits::{CssWriter, ParseError, StyleParseErrorKind, ToCss};
+use style_traits::{CssWriter, ParseError, SpecifiedValueInfo, StyleParseErrorKind, ToCss};
 use style_traits::values::specified::AllowedNumericType;
 use super::{Auto, CSSFloat, CSSInteger, Either};
 use super::computed::{Context, ToComputedValue};
 use super::generics::{GreaterThanOrEqualToOne, NonNegative};
 use super::generics::grid::{GridLine as GenericGridLine, TrackBreadth as GenericTrackBreadth};
 use super::generics::grid::{TrackList as GenericTrackList, TrackSize as GenericTrackSize};
 use values::serialize_atom_identifier;
 use values::specified::calc::CalcNode;
@@ -144,18 +144,18 @@ fn parse_number_with_clamping_mode<'i, '
 }
 
 // The integer values here correspond to the border conflict resolution rules in CSS 2.1 §
 // 17.6.2.1. Higher values override lower values.
 //
 // FIXME(emilio): Should move to border.rs
 #[allow(missing_docs)]
 #[cfg_attr(feature = "servo", derive(Deserialize, Serialize))]
-#[derive(Clone, Copy, Debug, Eq, MallocSizeOf, Ord, Parse, PartialEq, PartialOrd,
-         ToComputedValue, ToCss)]
+#[derive(Clone, Copy, Debug, Eq, MallocSizeOf, Ord, Parse, PartialEq,
+         PartialOrd, SpecifiedValueInfo, ToComputedValue, ToCss)]
 pub enum BorderStyle {
     None = -1,
     Solid = 6,
     Double = 7,
     Dotted = 4,
     Dashed = 5,
     Hidden = -2,
     Groove = 1,
@@ -268,16 +268,18 @@ impl ToCss for Number {
         self.value.to_css(dest)?;
         if self.calc_clamping_mode.is_some() {
             dest.write_str(")")?;
         }
         Ok(())
     }
 }
 
+impl SpecifiedValueInfo for Number {}
+
 impl From<Number> for f32 {
     #[inline]
     fn from(n: Number) -> Self {
         n.get()
     }
 }
 
 impl From<Number> for f64 {
@@ -321,17 +323,18 @@ impl Parse for GreaterThanOrEqualToOneNu
 }
 
 /// <number> | <percentage>
 ///
 /// Accepts only non-negative numbers.
 ///
 /// FIXME(emilio): Should probably use Either.
 #[allow(missing_docs)]
-#[derive(Clone, Copy, Debug, MallocSizeOf, PartialEq, ToCss)]
+#[derive(Clone, Copy, Debug, MallocSizeOf, PartialEq, SpecifiedValueInfo,
+         ToCss)]
 pub enum NumberOrPercentage {
     Percentage(Percentage),
     Number(Number),
 }
 
 impl NumberOrPercentage {
     fn parse_with_clamping_mode<'i, 't>(
         context: &ParserContext,
@@ -359,17 +362,18 @@ impl Parse for NumberOrPercentage {
         context: &ParserContext,
         input: &mut Parser<'i, 't>,
     ) -> Result<Self, ParseError<'i>> {
         Self::parse_with_clamping_mode(context, input, AllowedNumericType::All)
     }
 }
 
 #[allow(missing_docs)]
-#[derive(Clone, Copy, Debug, MallocSizeOf, PartialEq, PartialOrd, ToCss)]
+#[derive(Clone, Copy, Debug, MallocSizeOf, PartialEq, PartialOrd,
+         SpecifiedValueInfo, ToCss)]
 pub struct Opacity(Number);
 
 impl Parse for Opacity {
     fn parse<'i, 't>(
         context: &ParserContext,
         input: &mut Parser<'i, 't>,
     ) -> Result<Self, ParseError<'i>> {
         Number::parse(context, input).map(Opacity)
@@ -528,16 +532,18 @@ impl ToCss for Integer {
         self.value.to_css(dest)?;
         if self.was_calc {
             dest.write_str(")")?;
         }
         Ok(())
     }
 }
 
+impl SpecifiedValueInfo for Integer {}
+
 /// A wrapper of Integer, with value >= 1.
 pub type PositiveInteger = GreaterThanOrEqualToOne<Integer>;
 
 impl Parse for PositiveInteger {
     #[inline]
     fn parse<'i, 't>(
         context: &ParserContext,
         input: &mut Parser<'i, 't>,
@@ -557,17 +563,17 @@ pub type TrackSize = GenericTrackSize<Le
 pub type TrackList = GenericTrackList<LengthOrPercentage, Integer>;
 
 /// The specified value of a `<grid-line>`.
 pub type GridLine = GenericGridLine<Integer>;
 
 /// `<grid-template-rows> | <grid-template-columns>`
 pub type GridTemplateComponent = GenericGridTemplateComponent<LengthOrPercentage, Integer>;
 
-#[derive(Clone, Debug, MallocSizeOf, PartialEq)]
+#[derive(Clone, Debug, MallocSizeOf, PartialEq, SpecifiedValueInfo)]
 /// 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>)
     pub bottom: Option<Length>,
@@ -841,8 +847,10 @@ impl ToCss for Attr {
         if let Some((ref prefix, ref _url)) = self.namespace {
             serialize_atom_identifier(prefix, dest)?;
             dest.write_str("|")?;
         }
         serialize_atom_identifier(&self.attribute, dest)?;
         dest.write_str(")")
     }
 }
+
+impl SpecifiedValueInfo for Attr {}
--- a/servo/components/style/values/specified/outline.rs
+++ b/servo/components/style/values/specified/outline.rs
@@ -5,18 +5,18 @@
 //! Specified values for outline properties
 
 use cssparser::Parser;
 use parser::{Parse, ParserContext};
 use selectors::parser::SelectorParseErrorKind;
 use style_traits::ParseError;
 use values::specified::BorderStyle;
 
-#[derive(Clone, Copy, Debug, Eq, MallocSizeOf, Ord, PartialEq, PartialOrd, ToComputedValue,
-         ToCss)]
+#[derive(Clone, Copy, Debug, Eq, MallocSizeOf, Ord, PartialEq, PartialOrd,
+         SpecifiedValueInfo, ToComputedValue, ToCss)]
 /// <https://drafts.csswg.org/css-ui/#propdef-outline-style>
 pub enum OutlineStyle {
     /// auto
     Auto,
     /// <border-style>
     Other(BorderStyle),
 }
 
--- a/servo/components/style/values/specified/percentage.rs
+++ b/servo/components/style/values/specified/percentage.rs
@@ -2,17 +2,17 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 //! Specified percentages.
 
 use cssparser::{Parser, Token};
 use parser::{Parse, ParserContext};
 use std::fmt::{self, Write};
-use style_traits::{CssWriter, ParseError, ToCss};
+use style_traits::{CssWriter, ParseError, SpecifiedValueInfo, ToCss};
 use style_traits::values::specified::AllowedNumericType;
 use values::{serialize_percentage, CSSFloat};
 use values::computed::{Context, ToComputedValue};
 use values::computed::percentage::Percentage as ComputedPercentage;
 use values::specified::calc::CalcNode;
 
 /// A percentage value.
 #[derive(Clone, Copy, Debug, Default, MallocSizeOf, PartialEq)]
@@ -153,8 +153,10 @@ impl ToComputedValue for Percentage {
         ComputedPercentage(self.get())
     }
 
     #[inline]
     fn from_computed_value(computed: &Self::ComputedValue) -> Self {
         Percentage::new(computed.0)
     }
 }
+
+impl SpecifiedValueInfo for Percentage {}
--- a/servo/components/style/values/specified/position.rs
+++ b/servo/components/style/values/specified/position.rs
@@ -29,36 +29,38 @@ pub type Position = GenericPosition<Hori
 
 /// The specified value of a horizontal position.
 pub type HorizontalPosition = PositionComponent<X>;
 
 /// The specified value of a vertical position.
 pub type VerticalPosition = PositionComponent<Y>;
 
 /// The specified value of a component of a CSS `<position>`.
-#[derive(Clone, Debug, MallocSizeOf, PartialEq, ToCss)]
+#[derive(Clone, Debug, MallocSizeOf, PartialEq, SpecifiedValueInfo, ToCss)]
 pub enum PositionComponent<S> {
     /// `center`
     Center,
     /// `<lop>`
     Length(LengthOrPercentage),
     /// `<side> <lop>?`
     Side(S, Option<LengthOrPercentage>),
 }
 
 /// A keyword for the X direction.
-#[derive(Clone, Copy, Debug, Eq, Hash, MallocSizeOf, Parse, PartialEq, ToComputedValue, ToCss)]
+#[derive(Clone, Copy, Debug, Eq, Hash, MallocSizeOf, Parse, PartialEq,
+         SpecifiedValueInfo, ToComputedValue, ToCss)]
 #[allow(missing_docs)]
 pub enum X {
     Left,
     Right,
 }
 
 /// A keyword for the Y direction.
-#[derive(Clone, Copy, Debug, Eq, Hash, MallocSizeOf, Parse, PartialEq, ToComputedValue, ToCss)]
+#[derive(Clone, Copy, Debug, Eq, Hash, MallocSizeOf, Parse, PartialEq,
+         SpecifiedValueInfo, ToComputedValue, ToCss)]
 #[allow(missing_docs)]
 pub enum Y {
     Top,
     Bottom,
 }
 
 impl Parse for Position {
     fn parse<'i, 't>(
@@ -404,28 +406,30 @@ impl ToCss for LegacyPosition {
         W: Write,
     {
         self.horizontal.to_css(dest)?;
         dest.write_str(" ")?;
         self.vertical.to_css(dest)
     }
 }
 
-#[derive(Clone, Copy, Debug, Eq, MallocSizeOf, PartialEq, ToComputedValue, ToCss)]
+#[derive(Clone, Copy, Debug, Eq, MallocSizeOf, PartialEq, SpecifiedValueInfo,
+         ToComputedValue, ToCss)]
 /// Auto-placement algorithm Option
 pub enum AutoFlow {
     /// The auto-placement algorithm places items by filling each row in turn,
     /// adding new rows as necessary.
     Row,
     /// The auto-placement algorithm places items by filling each column in turn,
     /// adding new columns as necessary.
     Column,
 }
 
-#[derive(Clone, Copy, Debug, Eq, MallocSizeOf, PartialEq, ToComputedValue)]
+#[derive(Clone, Copy, Debug, Eq, MallocSizeOf, PartialEq, SpecifiedValueInfo,
+         ToComputedValue)]
 /// Controls how the auto-placement algorithm works
 /// specifying exactly how auto-placed items get flowed into the grid
 pub struct GridAutoFlow {
     /// Specifiy how auto-placement algorithm fills each `row` or `column` in turn
     pub autoflow: AutoFlow,
     /// Specify use `dense` packing algorithm or not
     pub dense: bool,
 }
@@ -528,17 +532,17 @@ impl From<GridAutoFlow> for u8 {
         if v.dense {
             result |= structs::NS_STYLE_GRID_AUTO_FLOW_DENSE as u8;
         }
         result
     }
 }
 
 #[cfg_attr(feature = "gecko", derive(MallocSizeOf))]
-#[derive(Clone, Debug, PartialEq, ToComputedValue, ToCss)]
+#[derive(Clone, Debug, PartialEq, SpecifiedValueInfo, ToComputedValue, ToCss)]
 /// https://drafts.csswg.org/css-grid/#named-grid-area
 pub struct TemplateAreas {
     /// `named area` containing for each template area
     #[css(skip)]
     pub areas: Box<[NamedArea]>,
     /// The original CSS string value of each template area
     #[css(iterable)]
     pub strings: Box<[Box<str>]>,
@@ -634,32 +638,33 @@ impl Parse for TemplateAreas {
         }
 
         TemplateAreas::from_vec(strings)
             .map_err(|()| input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
     }
 }
 
 /// Arc type for `Arc<TemplateAreas>`
-#[derive(Clone, Debug, MallocSizeOf, PartialEq, ToComputedValue, ToCss)]
+#[derive(Clone, Debug, MallocSizeOf, PartialEq, SpecifiedValueInfo,
+         ToComputedValue, ToCss)]
 pub struct TemplateAreasArc(#[ignore_malloc_size_of = "Arc"] pub Arc<TemplateAreas>);
 
 impl Parse for TemplateAreasArc {
     fn parse<'i, 't>(
         context: &ParserContext,
         input: &mut Parser<'i, 't>,
     ) -> Result<Self, ParseError<'i>> {
         let parsed = TemplateAreas::parse(context, input)?;
 
         Ok(TemplateAreasArc(Arc::new(parsed)))
     }
 }
 
 #[cfg_attr(feature = "gecko", derive(MallocSizeOf))]
-#[derive(Clone, Debug, PartialEq)]
+#[derive(Clone, Debug, PartialEq, SpecifiedValueInfo)]
 /// Not associated with any particular grid item, but can
 /// be referenced from the grid-placement properties.
 pub struct NamedArea {
     /// Name of the `named area`
     pub name: Box<str>,
     /// Rows of the `named area`
     pub rows: Range<u32>,
     /// Columns of the `named area`
--- a/servo/components/style/values/specified/svg.rs
+++ b/servo/components/style/values/specified/svg.rs
@@ -168,17 +168,18 @@ const PAINT_ORDER_MASK: u8 = 0b11;
 ///
 /// Each pair can be set to FILL, STROKE, or MARKERS
 /// Lowest significant bit pairs are highest priority.
 ///  `normal` is the empty bitfield. The three pairs are
 /// never zero in any case other than `normal`.
 ///
 /// Higher priority values, i.e. the values specified first,
 /// will be painted first (and may be covered by paintings of lower priority)
-#[derive(Clone, Copy, Debug, MallocSizeOf, PartialEq, ToComputedValue)]
+#[derive(Clone, Copy, Debug, MallocSizeOf, PartialEq, SpecifiedValueInfo,
+         ToComputedValue)]
 pub struct SVGPaintOrder(pub u8);
 
 impl SVGPaintOrder {
     /// Get default `paint-order` with `0`
     pub fn normal() -> Self {
         SVGPaintOrder(0)
     }
 
@@ -275,17 +276,18 @@ impl ToCss for SVGPaintOrder {
             self.order_at(pos).to_css(dest)?;
         }
         Ok(())
     }
 }
 
 /// Specified MozContextProperties value.
 /// Nonstandard (https://developer.mozilla.org/en-US/docs/Web/CSS/-moz-context-properties)
-#[derive(Clone, Debug, MallocSizeOf, PartialEq, ToComputedValue, ToCss)]
+#[derive(Clone, Debug, MallocSizeOf, PartialEq, SpecifiedValueInfo,
+         ToComputedValue, ToCss)]
 pub struct MozContextProperties(pub CustomIdent);
 
 impl Parse for MozContextProperties {
     fn parse<'i, 't>(
         _context: &ParserContext,
         input: &mut Parser<'i, 't>,
     ) -> Result<MozContextProperties, ParseError<'i>> {
         let location = input.current_source_location();
--- a/servo/components/style/values/specified/table.rs
+++ b/servo/components/style/values/specified/table.rs
@@ -3,17 +3,18 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 //! Specified types for table properties.
 
 use cssparser::Parser;
 use parser::{Parse, ParserContext};
 use style_traits::{ParseError, StyleParseErrorKind};
 
-#[derive(Clone, Copy, Debug, MallocSizeOf, PartialEq, ToComputedValue, ToCss)]
+#[derive(Clone, Copy, Debug, MallocSizeOf, PartialEq, SpecifiedValueInfo,
+         ToComputedValue, ToCss)]
 /// span. for `<col span>` pres attr
 pub struct XSpan(#[css(skip)] pub i32);
 
 impl Parse for XSpan {
     // never parse it, only set via presentation attribute
     fn parse<'i, 't>(
         _: &ParserContext,
         input: &mut Parser<'i, 't>,
--- a/servo/components/style/values/specified/text.rs
+++ b/servo/components/style/values/specified/text.rs
@@ -153,17 +153,17 @@ impl ToComputedValue for LineHeight {
             GenericLineHeight::Length(ref length) => {
                 GenericLineHeight::Length(NoCalcLength::from_computed_value(&length.0).into())
             },
         }
     }
 }
 
 /// A generic value for the `text-overflow` property.
-#[derive(Clone, Debug, Eq, MallocSizeOf, PartialEq, ToCss)]
+#[derive(Clone, Debug, Eq, MallocSizeOf, PartialEq, SpecifiedValueInfo, ToCss)]
 pub enum TextOverflowSide {
     /// Clip inline content.
     Clip,
     /// Render ellipsis to represent clipped inline content.
     Ellipsis,
     /// Render a given string to represent clipped inline content.
     String(Box<str>),
 }
@@ -187,17 +187,17 @@ impl Parse for TextOverflowSide {
             Token::QuotedString(ref v) => Ok(TextOverflowSide::String(
                 v.as_ref().to_owned().into_boxed_str(),
             )),
             ref t => Err(location.new_unexpected_token_error(t.clone())),
         }
     }
 }
 
-#[derive(Clone, Debug, Eq, MallocSizeOf, PartialEq, ToCss)]
+#[derive(Clone, Debug, Eq, MallocSizeOf, PartialEq, SpecifiedValueInfo, ToCss)]
 /// text-overflow. Specifies rendering when inline content overflows its line box edge.
 pub struct TextOverflow {
     /// First value. Applies to end line box edge if no second is supplied; line-left edge otherwise.
     pub first: TextOverflowSide,
     /// Second value. Applies to the line-right edge if supplied.
     pub second: Option<TextOverflowSide>,
 }
 
@@ -247,17 +247,17 @@ impl ToComputedValue for TextOverflow {
                 first: computed.first.clone(),
                 second: Some(computed.second.clone()),
             }
         }
     }
 }
 
 bitflags! {
-    #[derive(MallocSizeOf, ToComputedValue)]
+    #[derive(MallocSizeOf, SpecifiedValueInfo, ToComputedValue)]
     /// Specified keyword values for the text-decoration-line property.
     pub struct TextDecorationLine: u8 {
         /// No text decoration line is specified
         const NONE = 0;
         /// Underline
         const UNDERLINE = 0x01;
         /// Overline
         const OVERLINE = 0x02;
@@ -352,17 +352,18 @@ impl Parse for TextDecorationLine {
             Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
         }
     }
 }
 
 macro_rules! define_text_align_keyword {
     ($($name: ident => $discriminant: expr,)+) => {
         /// Specified value of text-align keyword value.
-        #[derive(Clone, Copy, Debug, Eq, Hash, MallocSizeOf, Parse, PartialEq, ToComputedValue, ToCss)]
+        #[derive(Clone, Copy, Debug, Eq, Hash, MallocSizeOf, Parse, PartialEq,
+                 SpecifiedValueInfo, ToComputedValue, ToCss)]
         #[allow(missing_docs)]
         pub enum TextAlignKeyword {
             $(
                 $name = $discriminant,
             )+
         }
 
         impl TextAlignKeyword {
@@ -412,17 +413,17 @@ impl TextAlignKeyword {
     #[inline]
     pub fn start() -> TextAlignKeyword {
         TextAlignKeyword::Start
     }
 }
 
 /// Specified value of text-align property.
 #[cfg_attr(feature = "gecko", derive(MallocSizeOf))]
-#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
+#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, SpecifiedValueInfo)]
 pub enum TextAlign {
     /// Keyword value of text-align property.
     Keyword(TextAlignKeyword),
     /// `match-parent` value of text-align property. It has a different handling
     /// unlike other keywords.
     #[cfg(feature = "gecko")]
     MatchParent,
     /// `MozCenterOrInherit` value of text-align property. It cannot be parsed,
@@ -529,28 +530,28 @@ impl ToComputedValue for TextAlign {
 
     #[inline]
     fn from_computed_value(computed: &Self::ComputedValue) -> Self {
         TextAlign::Keyword(*computed)
     }
 }
 
 /// Specified value of text-emphasis-style property.
-#[derive(Clone, Debug, MallocSizeOf, PartialEq, ToCss)]
+#[derive(Clone, Debug, MallocSizeOf, PartialEq, SpecifiedValueInfo, ToCss)]
 pub enum TextEmphasisStyle {
     /// <fill> <shape>
     Keyword(TextEmphasisKeywordValue),
     /// `none`
     None,
     /// String (will be used only first grapheme cluster) for the text-emphasis-style property
     String(String),
 }
 
 /// Keyword value for the text-emphasis-style property
-#[derive(Clone, Debug, MallocSizeOf, PartialEq, ToCss)]
+#[derive(Clone, Debug, MallocSizeOf, PartialEq, SpecifiedValueInfo, ToCss)]
 pub enum TextEmphasisKeywordValue {
     /// <fill>
     Fill(TextEmphasisFillMode),
     /// <shape>
     Shape(TextEmphasisShapeKeyword),
     /// <fill> <shape>
     FillAndShape(TextEmphasisFillMode, TextEmphasisShapeKeyword),
 }
@@ -569,26 +570,28 @@ impl TextEmphasisKeywordValue {
             TextEmphasisKeywordValue::Shape(shape) |
             TextEmphasisKeywordValue::FillAndShape(_, shape) => Some(shape),
             _ => None,
         }
     }
 }
 
 /// Fill mode for the text-emphasis-style property
-#[derive(Clone, Copy, Debug, MallocSizeOf, Parse, PartialEq, ToCss)]
+#[derive(Clone, Copy, Debug, MallocSizeOf, Parse, PartialEq, SpecifiedValueInfo,
+         ToCss)]
 pub enum TextEmphasisFillMode {
     /// `filled`
     Filled,
     /// `open`
     Open,
 }
 
 /// Shape keyword for the text-emphasis-style property
-#[derive(Clone, Copy, Debug, Eq, MallocSizeOf, Parse, PartialEq, ToCss)]
+#[derive(Clone, Copy, Debug, Eq, MallocSizeOf, Parse, PartialEq,
+         SpecifiedValueInfo, ToCss)]
 pub enum TextEmphasisShapeKeyword {
     /// `dot`
     Dot,
     /// `circle`
     Circle,
     /// `double-circle`
     DoubleCircle,
     /// `triangle`
@@ -704,35 +707,38 @@ impl Parse for TextEmphasisStyle {
             (None, Some(shape)) => TextEmphasisKeywordValue::Shape(shape),
             _ => return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError)),
         };
         Ok(TextEmphasisStyle::Keyword(keyword_value))
     }
 }
 
 /// The allowed horizontal values for the `text-emphasis-position` property.
-#[derive(Clone, Copy, Debug, Eq, MallocSizeOf, Parse, PartialEq, ToComputedValue, ToCss)]
+#[derive(Clone, Copy, Debug, Eq, MallocSizeOf, Parse, PartialEq,
+         SpecifiedValueInfo, ToComputedValue, ToCss)]
 pub enum TextEmphasisHorizontalWritingModeValue {
     /// Draw marks over the text in horizontal writing mode.
     Over,
     /// Draw marks under the text in horizontal writing mode.
     Under,
 }
 
 /// The allowed vertical values for the `text-emphasis-position` property.
-#[derive(Clone, Copy, Debug, Eq, MallocSizeOf, Parse, PartialEq, ToComputedValue, ToCss)]
+#[derive(Clone, Copy, Debug, Eq, MallocSizeOf, Parse, PartialEq,
+         SpecifiedValueInfo, ToComputedValue, ToCss)]
 pub enum TextEmphasisVerticalWritingModeValue {
     /// Draws marks to the right of the text in vertical writing mode.
     Right,
     /// Draw marks to the left of the text in vertical writing mode.
     Left,
 }
 
 /// Specified value of `text-emphasis-position` property.
-#[derive(Clone, Debug, MallocSizeOf, PartialEq, ToComputedValue, ToCss)]
+#[derive(Clone, Debug, MallocSizeOf, PartialEq, SpecifiedValueInfo,
+         ToComputedValue, ToCss)]
 pub struct TextEmphasisPosition(
     pub TextEmphasisHorizontalWritingModeValue,
     pub TextEmphasisVerticalWritingModeValue,
 );
 
 impl TextEmphasisPosition {
     #[inline]
     /// Returns the initial value of `text-emphasis-position`
--- a/servo/components/style/values/specified/time.rs
+++ b/servo/components/style/values/specified/time.rs
@@ -2,17 +2,17 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 //! Specified time values.
 
 use cssparser::{Parser, Token};
 use parser::{Parse, ParserContext};
 use std::fmt::{self, Write};
-use style_traits::{CssWriter, ParseError, StyleParseErrorKind, ToCss};
+use style_traits::{CssWriter, ParseError, SpecifiedValueInfo, StyleParseErrorKind, ToCss};
 use style_traits::values::specified::AllowedNumericType;
 use values::CSSFloat;
 use values::computed::{Context, ToComputedValue};
 use values::computed::time::Time as ComputedTime;
 use values::specified::calc::CalcNode;
 
 /// A time value according to CSS-VALUES § 6.2.
 #[derive(Clone, Copy, Debug, MallocSizeOf, PartialEq)]
@@ -160,8 +160,10 @@ impl ToCss for Time {
             },
         }
         if self.was_calc {
             dest.write_str(")")?;
         }
         Ok(())
     }
 }
+
+impl SpecifiedValueInfo for Time {}
--- a/servo/components/style/values/specified/transform.rs
+++ b/servo/components/style/values/specified/transform.rs
@@ -2,17 +2,17 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 //! Specified types for CSS values that are related to transformations.
 
 use cssparser::Parser;
 use parser::{Parse, ParserContext};
 use selectors::parser::SelectorParseErrorKind;
-use style_traits::{ParseError, StyleParseErrorKind};
+use style_traits::{CssType, ParseError, SpecifiedValueInfo, StyleParseErrorKind};
 use values::computed::{Context, LengthOrPercentage as ComputedLengthOrPercentage};
 use values::computed::{Percentage as ComputedPercentage, ToComputedValue};
 use values::computed::transform::TimingFunction as ComputedTimingFunction;
 use values::generics::transform as generic;
 use values::generics::transform::{Matrix, Matrix3D, StepPosition, TimingKeyword};
 use values::specified::{self, Angle, Integer, Length, LengthOrPercentage, Number};
 use values::specified::position::{Side, X, Y};
 
@@ -228,29 +228,33 @@ impl Parse for Transform {
         context: &ParserContext,
         input: &mut Parser<'i, 't>,
     ) -> Result<Self, ParseError<'i>> {
         Transform::parse_internal(context, input)
     }
 }
 
 /// The specified value of a component of a CSS `<transform-origin>`.
-#[derive(Clone, Debug, MallocSizeOf, PartialEq, ToCss)]
+#[derive(Clone, Debug, MallocSizeOf, PartialEq, SpecifiedValueInfo, ToCss)]
 pub enum OriginComponent<S> {
     /// `center`
     Center,
     /// `<lop>`
     Length(LengthOrPercentage),
     /// `<side>`
     Side(S),
 }
 
 /// A specified timing function.
 pub type TimingFunction = generic::TimingFunction<Integer, Number>;
 
+impl SpecifiedValueInfo for TimingFunction {
+    const SUPPORTED_TYPES: u8 = CssType::TIMING_FUNCTION;
+}
+
 impl Parse for TransformOrigin {
     fn parse<'i, 't>(
         context: &ParserContext,
         input: &mut Parser<'i, 't>,
     ) -> Result<Self, ParseError<'i>> {
         let parse_depth = |input: &mut Parser| {
             input
                 .try(|i| Length::parse(context, i))
--- a/servo/components/style/values/specified/ui.rs
+++ b/servo/components/style/values/specified/ui.rs
@@ -5,17 +5,18 @@
 //! Specified types for UI properties.
 
 use cssparser::Parser;
 use parser::{Parse, ParserContext};
 use std::fmt::{self, Write};
 use style_traits::{CssWriter, ParseError, StyleParseErrorKind, ToCss};
 
 /// Specified value of `-moz-force-broken-image-icon`
-#[derive(Clone, Copy, Debug, MallocSizeOf, PartialEq, ToComputedValue)]
+#[derive(Clone, Copy, Debug, MallocSizeOf, PartialEq, SpecifiedValueInfo,
+         ToComputedValue)]
 pub struct MozForceBrokenImageIcon(pub bool);
 
 impl MozForceBrokenImageIcon {
     /// Return initial value of -moz-force-broken-image-icon which is false.
     #[inline]
     pub fn false_value() -> MozForceBrokenImageIcon {
         MozForceBrokenImageIcon(false)
     }
--- a/servo/components/style_derive/animate.rs
+++ b/servo/components/style_derive/animate.rs
@@ -63,17 +63,17 @@ pub fn derive(mut input: DeriveInput) ->
                     #match_body
                 }
             }
         }
     }
 }
 
 fn derive_variant_arm(variant: &VariantInfo) -> Result<Tokens, ()> {
-    let variant_attrs = cg::parse_variant_attrs::<AnimationVariantAttrs>(&variant.ast());
+    let variant_attrs = cg::parse_variant_attrs_from_ast::<AnimationVariantAttrs>(&variant.ast());
     if variant_attrs.error {
         return Err(());
     }
     let (this_pattern, this_info) = cg::ref_pattern(&variant, "this");
     let (other_pattern, other_info) = cg::ref_pattern(&variant, "other");
     let (result_value, result_info) = cg::value(&variant, "result");
     let mut computations = quote!();
     let iter = result_info.iter().zip(this_info.iter().zip(&other_info));
--- a/servo/components/style_derive/cg.rs
+++ b/servo/components/style_derive/cg.rs
@@ -183,27 +183,34 @@ where
     A: FromDeriveInput,
 {
     match A::from_derive_input(input) {
         Ok(attrs) => attrs,
         Err(e) => panic!("failed to parse input attributes: {}", e),
     }
 }
 
-pub fn parse_variant_attrs<A>(variant: &VariantAst) -> A
+pub fn parse_variant_attrs_from_ast<A>(variant: &VariantAst) -> A
 where
     A: FromVariant,
 {
     let v = Variant {
         ident: *variant.ident,
         attrs: variant.attrs.to_vec(),
         fields: variant.fields.clone(),
         discriminant: variant.discriminant.clone(),
     };
-    match A::from_variant(&v) {
+    parse_variant_attrs(&v)
+}
+
+pub fn parse_variant_attrs<A>(variant: &Variant) -> A
+where
+    A: FromVariant
+{
+    match A::from_variant(variant) {
         Ok(attrs) => attrs,
         Err(e) => panic!("failed to parse variant attributes: {}", e),
     }
 }
 
 
 pub fn ref_pattern<'a>(
     variant: &'a VariantInfo,
--- a/servo/components/style_derive/compute_squared_distance.rs
+++ b/servo/components/style_derive/compute_squared_distance.rs
@@ -21,17 +21,17 @@ pub fn derive(mut input: DeriveInput) ->
         }
     }
 
     let (mut match_body, append_error_clause) = {
         let s = synstructure::Structure::new(&input);
         let mut append_error_clause = s.variants().len() > 1;
 
         let match_body = s.variants().iter().fold(quote!(), |body, variant| {
-            let attrs = cg::parse_variant_attrs::<AnimationVariantAttrs>(&variant.ast());
+            let attrs = cg::parse_variant_attrs_from_ast::<AnimationVariantAttrs>(&variant.ast());
             if attrs.error {
                 append_error_clause = true;
                 return body;
             }
 
             let (this_pattern, this_info) = cg::ref_pattern(&variant, "this");
             let (other_pattern, other_info) = cg::ref_pattern(&variant, "other");
             let sum = if this_info.is_empty() {
--- a/servo/components/style_derive/lib.rs
+++ b/servo/components/style_derive/lib.rs
@@ -11,16 +11,17 @@ extern crate proc_macro;
 extern crate synstructure;
 
 use proc_macro::TokenStream;
 
 mod animate;
 mod cg;
 mod compute_squared_distance;
 mod parse;
+mod specified_value_info;
 mod to_animated_value;
 mod to_animated_zero;
 mod to_computed_value;
 mod to_css;
 
 #[proc_macro_derive(Animate, attributes(animate, animation))]
 pub fn derive_animate(stream: TokenStream) -> TokenStream {
     let input = syn::parse(stream).unwrap();
@@ -57,8 +58,14 @@ pub fn derive_to_computed_value(stream: 
     to_computed_value::derive(input).into()
 }
 
 #[proc_macro_derive(ToCss, attributes(css))]
 pub fn derive_to_css(stream: TokenStream) -> TokenStream {
     let input = syn::parse(stream).unwrap();
     to_css::derive(input).into()
 }
+
+#[proc_macro_derive(SpecifiedValueInfo, attributes(css))]
+pub fn derive_specified_value_info(stream: TokenStream) -> TokenStream {
+    let input = syn::parse(stream).unwrap();
+    specified_value_info::derive(input).into()
+}
--- a/servo/components/style_derive/parse.rs
+++ b/servo/components/style_derive/parse.rs
@@ -14,17 +14,17 @@ pub fn derive(input: DeriveInput) -> Tok
 
     let match_body = s.variants().iter().fold(quote!(), |match_body, variant| {
         let bindings = variant.bindings();
         assert!(
             bindings.is_empty(),
             "Parse is only supported for single-variant enums for now"
         );
 
-        let variant_attrs = cg::parse_variant_attrs::<CssVariantAttrs>(&variant.ast());
+        let variant_attrs = cg::parse_variant_attrs_from_ast::<CssVariantAttrs>(&variant.ast());
         let identifier = cg::to_css_identifier(
             &variant_attrs.keyword.unwrap_or(variant.ast().ident.as_ref().into()),
         );
         let ident = &variant.ast().ident;
 
         let mut body = quote! {
             #match_body
             #identifier => Ok(#name::#ident),
new file mode 100644
--- /dev/null
+++ b/servo/components/style_derive/specified_value_info.rs
@@ -0,0 +1,68 @@
+/* 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 cg;
+use quote::Tokens;
+use syn::{Data, DeriveInput, Fields};
+use to_css::{CssFieldAttrs, CssInputAttrs, CssVariantAttrs};
+
+pub fn derive(mut input: DeriveInput) -> Tokens {
+    let attrs = cg::parse_input_attrs::<CssInputAttrs>(&input);
+    let mut types_value = quote!(0);
+    // If the whole value is wrapped in a function, value types of its
+    // fields should not be propagated.
+    if attrs.function.is_none() {
+        let mut where_clause = input.generics.where_clause.take();
+        for param in input.generics.type_params() {
+            cg::add_predicate(
+                &mut where_clause,
+                parse_quote!(#param: ::style_traits::SpecifiedValueInfo),
+            );
+        }
+        input.generics.where_clause = where_clause;
+
+        match input.data {
+            Data::Enum(ref e) => {
+                for v in e.variants.iter() {
+                    let attrs = cg::parse_variant_attrs::<CssVariantAttrs>(&v);
+                    if attrs.function.is_none() {
+                        derive_struct_fields(&v.fields, &mut types_value);
+                    }
+                }
+            }
+            Data::Struct(ref s) => {
+                derive_struct_fields(&s.fields, &mut types_value)
+            }
+            Data::Union(_) => unreachable!("union is not supported"),
+        }
+    }
+
+    let name = &input.ident;
+    let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl();
+    quote! {
+        impl #impl_generics ::style_traits::SpecifiedValueInfo for #name #ty_generics
+        #where_clause
+        {
+            const SUPPORTED_TYPES: u8 = #types_value;
+        }
+    }
+}
+
+fn derive_struct_fields(fields: &Fields, supports_body: &mut Tokens) {
+    let fields = match *fields {
+        Fields::Unit => return,
+        Fields::Named(ref fields) => fields.named.iter(),
+        Fields::Unnamed(ref fields) => fields.unnamed.iter(),
+    };
+    supports_body.append_all(fields.map(|field| {
+        let attrs = cg::parse_field_attrs::<CssFieldAttrs>(field);
+        if attrs.skip {
+            return quote!();
+        }
+        let ty = &field.ty;
+        quote! {
+            | <#ty as ::style_traits::SpecifiedValueInfo>::SUPPORTED_TYPES
+        }
+    }));
+}
--- a/servo/components/style_derive/to_animated_zero.rs
+++ b/servo/components/style_derive/to_animated_zero.rs
@@ -17,17 +17,17 @@ pub fn derive(mut input: syn::DeriveInpu
             cg::add_predicate(
                 &mut where_clause,
                 parse_quote!(#param: ::values::animated::ToAnimatedZero),
             );
         }
     }
 
     let to_body = synstructure::Structure::new(&input).each_variant(|variant| {
-        let attrs = cg::parse_variant_attrs::<AnimationVariantAttrs>(&variant.ast());
+        let attrs = cg::parse_variant_attrs_from_ast::<AnimationVariantAttrs>(&variant.ast());
         if attrs.error {
             return Some(quote! { Err(()) });
         }
         let (mapped, mapped_bindings) = cg::value(variant, "mapped");
         let bindings_pairs = variant.bindings().into_iter().zip(mapped_bindings);
         let mut computations = quote!();
         computations.append_all(bindings_pairs.map(|(binding, mapped_binding)| {
             let field_attrs = cg::parse_field_attrs::<AnimationFieldAttrs>(&binding.ast());
--- a/servo/components/style_derive/to_css.rs
+++ b/servo/components/style_derive/to_css.rs
@@ -70,17 +70,17 @@ pub fn derive(mut input: syn::DeriveInpu
 
 fn derive_variant_arm(
     variant: &VariantInfo,
     generics: &mut Option<WhereClause>,
 ) -> Tokens {
     let bindings = variant.bindings();
     let identifier = cg::to_css_identifier(variant.ast().ident.as_ref());
     let ast = variant.ast();
-    let variant_attrs = cg::parse_variant_attrs::<CssVariantAttrs>(&ast);
+    let variant_attrs = cg::parse_variant_attrs_from_ast::<CssVariantAttrs>(&ast);
     let separator = if variant_attrs.comma { ", " } else { " " };
 
     if variant_attrs.dimension {
         assert_eq!(bindings.len(), 1);
         assert!(
             variant_attrs.function.is_none() && variant_attrs.keyword.is_none(),
             "That makes no sense"
         );
@@ -202,35 +202,35 @@ fn derive_single_field_expr(
         }
     }
 
     expr
 }
 
 #[darling(attributes(css), default)]
 #[derive(Default, FromDeriveInput)]
-struct CssInputAttrs {
-    derive_debug: bool,
+pub struct CssInputAttrs {
+    pub derive_debug: bool,
     // Here because structs variants are also their whole type definition.
-    function: Option<Override<String>>,
+    pub function: Option<Override<String>>,
     // Here because structs variants are also their whole type definition.
-    comma: bool,
+    pub comma: bool,
 }
 
 #[darling(attributes(css), default)]
 #[derive(Default, FromVariant)]
 pub struct CssVariantAttrs {
     pub function: Option<Override<String>>,
     pub comma: bool,
     pub dimension: bool,
     pub keyword: Option<String>,
     pub aliases: Option<String>,
 }
 
 #[darling(attributes(css), default)]
 #[derive(Default, FromField)]
-struct CssFieldAttrs {
-    if_empty: Option<String>,
-    field_bound: bool,
-    iterable: bool,
-    skip: bool,
-    skip_if: Option<Path>,
+pub struct CssFieldAttrs {
+    pub if_empty: Option<String>,
+    pub field_bound: bool,
+    pub iterable: bool,
+    pub skip: bool,
+    pub skip_if: Option<Path>,
 }
--- a/servo/components/style_traits/cursor.rs
+++ b/servo/components/style_traits/cursor.rs
@@ -1,15 +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/. */
 
 //! A list of common mouse cursors per CSS3-UI § 8.1.1.
 
-use super::{CssWriter, ToCss};
+use super::{CssWriter, SpecifiedValueInfo, ToCss};
 
 macro_rules! define_cursor {
     (
         common properties = [
             $( $c_css: expr => $c_variant: ident = $c_value: expr, )+
         ]
         gecko properties = [
             $( $g_css: expr => $g_variant: ident = $g_value: expr, )+
@@ -52,16 +52,18 @@ macro_rules! define_cursor {
                         ::std::fmt::Write::write_str(dest, $c_css)
                     })+
                     $(#[cfg(feature = "gecko")] CursorKind::$g_variant => {
                         ::std::fmt::Write::write_str(dest, $g_css)
                     })+
                 }
             }
         }
+
+        impl SpecifiedValueInfo for CursorKind {}
     }
 }
 
 define_cursor! {
     common properties = [
         "none" => None = 0,
         "default" => Default = 1,
         "pointer" => Pointer = 2,
--- a/servo/components/style_traits/lib.rs
+++ b/servo/components/style_traits/lib.rs
@@ -68,21 +68,23 @@ pub enum CSSPixel {}
 
 // In summary, the hierarchy of pixel units and the factors to convert from one to the next:
 //
 // DevicePixel
 //   / hidpi_ratio => DeviceIndependentPixel
 //     / desktop_zoom => CSSPixel
 
 pub mod cursor;
+pub mod specified_value_info;
 #[macro_use]
 pub mod values;
 #[macro_use]
 pub mod viewport;
 
+pub use specified_value_info::{CssType, SpecifiedValueInfo};
 pub use values::{Comma, CommaWithSpace, CssWriter, OneOrMoreSeparated, Separator, Space, ToCss};
 
 /// The error type for all CSS parsing routines.
 pub type ParseError<'i> = cssparser::ParseError<'i, StyleParseErrorKind<'i>>;
 
 /// Error in property value parsing
 pub type ValueParseError<'i> = cssparser::ParseError<'i, ValueParseErrorKind<'i>>;
 
new file mode 100644
--- /dev/null
+++ b/servo/components/style_traits/specified_value_info.rs
@@ -0,0 +1,71 @@
+/* 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/. */
+
+//! Value information for devtools.
+
+use servo_arc::Arc;
+use std::ops::Range;
+
+/// Type of value that a property supports. This is used by Gecko's
+/// devtools to make sense about value it parses, and types listed
+/// here should match TYPE_* constants in InspectorUtils.webidl.
+///
+/// XXX This should really be a bitflags rather than a namespace mod,
+/// but currently we cannot use bitflags in const.
+#[allow(non_snake_case)]
+pub mod CssType {
+    /// <color>
+    pub const COLOR: u8 = 1 << 0;
+    /// <gradient>
+    pub const GRADIENT: u8 = 1 << 1;
+    /// <timing-function>
+    pub const TIMING_FUNCTION: u8 = 1 << 2;
+}
+
+/// Information of values of a given specified value type.
+pub trait SpecifiedValueInfo {
+    /// Supported CssTypes by the given value type.
+    ///
+    /// XXX This should be typed CssType when that becomes a bitflags.
+    /// Currently we cannot do so since bitflags cannot be used in constant.
+    const SUPPORTED_TYPES: u8 = 0;
+}
+
+impl SpecifiedValueInfo for bool {}
+impl SpecifiedValueInfo for f32 {}
+impl SpecifiedValueInfo for i8 {}
+impl SpecifiedValueInfo for i32 {}
+impl SpecifiedValueInfo for u8 {}
+impl SpecifiedValueInfo for u16 {}
+impl SpecifiedValueInfo for u32 {}
+impl SpecifiedValueInfo for str {}
+impl SpecifiedValueInfo for String {}
+
+impl<T: SpecifiedValueInfo + ?Sized> SpecifiedValueInfo for Box<T> {
+    const SUPPORTED_TYPES: u8 = T::SUPPORTED_TYPES;
+}
+
+impl<T: SpecifiedValueInfo> SpecifiedValueInfo for [T] {
+    const SUPPORTED_TYPES: u8 = T::SUPPORTED_TYPES;
+}
+
+macro_rules! impl_generic_specified_value_info {
+    ($ty:ident<$param:ident>) => {
+        impl<$param: SpecifiedValueInfo> SpecifiedValueInfo for $ty<$param> {
+            const SUPPORTED_TYPES: u8 = $param::SUPPORTED_TYPES;
+        }
+    }
+}
+impl_generic_specified_value_info!(Option<T>);
+impl_generic_specified_value_info!(Vec<T>);
+impl_generic_specified_value_info!(Arc<T>);
+impl_generic_specified_value_info!(Range<Idx>);
+
+impl<T1, T2> SpecifiedValueInfo for (T1, T2)
+where
+    T1: SpecifiedValueInfo,
+    T2: SpecifiedValueInfo,
+{
+    const SUPPORTED_TYPES: u8 = T1::SUPPORTED_TYPES | T2::SUPPORTED_TYPES;
+}