servo: Merge #15411 - Box larger specified values to avoid memmove impact (from canaltinova:property-declaration); r=SimonSapin
authorNazım Can Altınova <canaltinova@gmail.com>
Thu, 09 Feb 2017 02:21:36 -0800
changeset 341670 240cc49eae0705a0c0641cde20597e7b877f5ee2
parent 341669 d868371b41867400fb4e108c9eef61d104327c33
child 341671 f0f7d425527e142eb16240a3dc0e1900471a636b
push id31340
push userkwierso@gmail.com
push dateFri, 10 Feb 2017 00:36:17 +0000
treeherdermozilla-central@00beaa76c5b1 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersSimonSapin
milestone54.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
servo: Merge #15411 - Box larger specified values to avoid memmove impact (from canaltinova:property-declaration); r=SimonSapin <!-- Please describe your changes on the following line: --> Box larger specified values to avoid memmove impact. --- <!-- Thank you for contributing to Servo! Please replace each `[ ]` by `[X]` when the step is complete, and replace `__` with appropriate data: --> - [X] `./mach build -d` does not report any errors - [X] `./mach test-tidy` does not report any errors - [X] These changes fix #15322 (github issue number if applicable). <!-- Either: --> - [ ] There are tests for these changes OR - [ ] These changes do not require tests because _____ <!-- Pull requests that do not address these steps are welcome, but they will require additional verification as part of the review process. --> Source-Repo: https://github.com/servo/servo Source-Revision: 5c609213d68a17f65ae4e64c34d8dc6b66d35784
servo/components/script/dom/element.rs
servo/components/style/custom_properties.rs
servo/components/style/properties/data.py
servo/components/style/properties/gecko.mako.rs
servo/components/style/properties/helpers.mako.rs
servo/components/style/properties/helpers/animated_properties.mako.rs
servo/components/style/properties/longhand/background.mako.rs
servo/components/style/properties/longhand/border.mako.rs
servo/components/style/properties/longhand/box.mako.rs
servo/components/style/properties/longhand/color.mako.rs
servo/components/style/properties/longhand/column.mako.rs
servo/components/style/properties/longhand/counters.mako.rs
servo/components/style/properties/longhand/effects.mako.rs
servo/components/style/properties/longhand/font.mako.rs
servo/components/style/properties/longhand/inherited_box.mako.rs
servo/components/style/properties/longhand/inherited_table.mako.rs
servo/components/style/properties/longhand/inherited_text.mako.rs
servo/components/style/properties/longhand/list.mako.rs
servo/components/style/properties/longhand/outline.mako.rs
servo/components/style/properties/longhand/pointing.mako.rs
servo/components/style/properties/longhand/position.mako.rs
servo/components/style/properties/longhand/svg.mako.rs
servo/components/style/properties/longhand/text.mako.rs
servo/components/style/properties/properties.mako.rs
servo/components/style/properties/shorthand/box.mako.rs
servo/components/style/properties/shorthand/serialize.mako.rs
servo/components/style/values/mod.rs
servo/components/style/values/specified/grid.rs
servo/components/style/values/specified/mod.rs
servo/components/style/values/specified/url.rs
servo/components/style_traits/values.rs
servo/ports/geckolib/glue.rs
servo/tests/unit/style/lib.rs
servo/tests/unit/style/properties/serialization.rs
servo/tests/unit/style/size_of.rs
servo/tests/unit/style/stylesheets.rs
--- a/servo/components/script/dom/element.rs
+++ b/servo/components/script/dom/element.rs
@@ -402,17 +402,17 @@ impl LayoutElementHelpers for LayoutJS<E
             this.get_background_color()
         } else {
             None
         };
 
         if let Some(color) = bgcolor {
             hints.push(from_declaration(
                 PropertyDeclaration::BackgroundColor(DeclaredValue::Value(
-                    CSSColor { parsed: Color::RGBA(color), authored: None }))));
+                    Box::new(CSSColor { parsed: Color::RGBA(color), authored: None })))));
         }
 
         let background = if let Some(this) = self.downcast::<HTMLBodyElement>() {
             this.get_background()
         } else {
             None
         };
 
@@ -435,20 +435,20 @@ impl LayoutElementHelpers for LayoutJS<E
             // https://html.spec.whatwg.org/multipage/#the-hr-element-2:presentational-hints-5
             this.get_color()
         } else {
             None
         };
 
         if let Some(color) = color {
             hints.push(from_declaration(
-                PropertyDeclaration::Color(DeclaredValue::Value(CSSRGBA {
+                PropertyDeclaration::Color(DeclaredValue::Value(Box::new(CSSRGBA {
                     parsed: color,
                     authored: None,
-                }))));
+                })))));
         }
 
         let font_family = if let Some(this) = self.downcast::<HTMLFontElement>() {
             this.get_face()
         } else {
             None
         };
 
@@ -475,20 +475,20 @@ impl LayoutElementHelpers for LayoutJS<E
         } else {
             None
         };
 
         if let Some(cellspacing) = cellspacing {
             let width_value = specified::Length::from_px(cellspacing as f32);
             hints.push(from_declaration(
                 PropertyDeclaration::BorderSpacing(DeclaredValue::Value(
-                    border_spacing::SpecifiedValue {
+                    Box::new(border_spacing::SpecifiedValue {
                         horizontal: width_value.clone(),
                         vertical: width_value,
-                    }))));
+                    })))));
         }
 
 
         let size = if let Some(this) = self.downcast::<HTMLInputElement>() {
             // FIXME(pcwalton): More use of atoms, please!
             match (*self.unsafe_get()).get_attr_val_for_layout(&ns!(), &local_name!("type")) {
                 // Not text entry widget
                 Some("hidden") | Some("date") | Some("month") | Some("week") |
--- a/servo/components/style/custom_properties.rs
+++ b/servo/components/style/custom_properties.rs
@@ -3,17 +3,17 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 //! Support for [custom properties for cascading variables][custom].
 //!
 //! [custom]: https://drafts.csswg.org/css-variables/
 
 use Atom;
 use cssparser::{Delimiter, Parser, SourcePosition, Token, TokenSerializationType};
-use parser::{Parse, ParserContext};
+use parser::ParserContext;
 use properties::DeclaredValue;
 use std::ascii::AsciiExt;
 use std::borrow::Cow;
 use std::collections::{HashMap, HashSet};
 use std::fmt;
 use std::sync::Arc;
 use style_traits::ToCss;
 
@@ -124,26 +124,27 @@ impl ComputedValue {
         self.push(input.slice_from(position.0), position.1, last_token_type)
     }
 
     fn push_variable(&mut self, variable: &ComputedValue) {
         self.push(&variable.css, variable.first_token_type, variable.last_token_type)
     }
 }
 
-impl Parse for SpecifiedValue {
-    fn parse(_context: &ParserContext, input: &mut Parser) -> Result<Self, ()> {
+impl SpecifiedValue {
+    /// Parse a custom property SpecifiedValue.
+    pub fn parse(_context: &ParserContext, input: &mut Parser) -> Result<Box<Self>, ()> {
         let mut references = Some(HashSet::new());
         let (first, css, last) = try!(parse_self_contained_declaration_value(input, &mut references));
-        Ok(SpecifiedValue {
+        Ok(Box::new(SpecifiedValue {
             css: css.into_owned(),
             first_token_type: first,
             last_token_type: last,
             references: references.unwrap(),
-        })
+        }))
     }
 }
 
 /// Parse the value of a non-custom property that contains `var()` references.
 pub fn parse_non_custom_with_var<'i, 't>
                                 (input: &mut Parser<'i, 't>)
                                 -> Result<(TokenSerializationType, Cow<'i, str>), ()> {
     let (first_token_type, css, _) = try!(parse_self_contained_declaration_value(input, &mut None));
@@ -323,17 +324,17 @@ fn parse_var_function<'i, 't>(input: &mu
 }
 
 /// Add one custom property declaration to a map, unless another with the same
 /// name was already there.
 pub fn cascade<'a>(custom_properties: &mut Option<HashMap<&'a Name, BorrowedSpecifiedValue<'a>>>,
                    inherited: &'a Option<Arc<HashMap<Name, ComputedValue>>>,
                    seen: &mut HashSet<&'a Name>,
                    name: &'a Name,
-                   specified_value: &'a DeclaredValue<SpecifiedValue>) {
+                   specified_value: &'a DeclaredValue<Box<SpecifiedValue>>) {
     let was_already_present = !seen.insert(name);
     if was_already_present {
         return;
     }
 
     let map = match *custom_properties {
         Some(ref mut map) => map,
         None => {
@@ -355,17 +356,17 @@ pub fn cascade<'a>(custom_properties: &m
         DeclaredValue::Value(ref specified_value) => {
             map.insert(name, BorrowedSpecifiedValue {
                 css: &specified_value.css,
                 first_token_type: specified_value.first_token_type,
                 last_token_type: specified_value.last_token_type,
                 references: Some(&specified_value.references),
             });
         },
-        DeclaredValue::WithVariables { .. } => unreachable!(),
+        DeclaredValue::WithVariables(_) => unreachable!(),
         DeclaredValue::Initial => {
             map.remove(&name);
         }
         DeclaredValue::Unset | // Custom properties are inherited by default.
         DeclaredValue::Inherit => {}  // The inherited value is what we already have.
     }
 }
 
--- a/servo/components/style/properties/data.py
+++ b/servo/components/style/properties/data.py
@@ -91,17 +91,17 @@ def arg_to_bool(arg):
     return arg == "True"
 
 
 class Longhand(object):
     def __init__(self, style_struct, name, spec=None, animatable=None, derived_from=None, keyword=None,
                  predefined_type=None, custom_cascade=False, experimental=False, internal=False,
                  need_clone=False, need_index=False, gecko_ffi_name=None, depend_on_viewport_size=False,
                  allowed_in_keyframe_block=True, complex_color=False, cast_type='u8',
-                 has_uncacheable_values=False, logical=False, alias=None, extra_prefixes=None):
+                 has_uncacheable_values=False, logical=False, alias=None, extra_prefixes=None, boxed=False):
         self.name = name
         if not spec:
             raise TypeError("Spec should be specified for %s" % name)
         self.spec = spec
         self.keyword = keyword
         self.predefined_type = predefined_type
         self.ident = to_rust_ident(name)
         self.camel_case = to_camel_case(self.ident)
@@ -114,16 +114,17 @@ class Longhand(object):
         self.gecko_ffi_name = gecko_ffi_name or "m" + self.camel_case
         self.depend_on_viewport_size = depend_on_viewport_size
         self.derived_from = (derived_from or "").split()
         self.complex_color = complex_color
         self.cast_type = cast_type
         self.logical = arg_to_bool(logical)
         self.alias = alias.split() if alias else []
         self.extra_prefixes = extra_prefixes.split() if extra_prefixes else []
+        self.boxed = arg_to_bool(boxed)
 
         # https://drafts.csswg.org/css-animations/#keyframes
         # > The <declaration-list> inside of <keyframe-block> accepts any CSS property
         # > except those defined in this specification,
         # > but does accept the `animation-play-state` property and interprets it specially.
         self.allowed_in_keyframe_block = allowed_in_keyframe_block \
             and allowed_in_keyframe_block != "False"
 
--- a/servo/components/style/properties/gecko.mako.rs
+++ b/servo/components/style/properties/gecko.mako.rs
@@ -163,18 +163,26 @@ impl ComputedValues {
     pub fn to_declaration_block(&self, property: PropertyDeclarationId) -> PropertyDeclarationBlock {
         match property {
             % for prop in data.longhands:
                 % if prop.animatable:
                     PropertyDeclarationId::Longhand(LonghandId::${prop.camel_case}) => {
                         PropertyDeclarationBlock {
                             declarations: vec![
                                 (PropertyDeclaration::${prop.camel_case}(DeclaredValue::Value(
+                                    % if prop.boxed:
+                                        Box::new(
+                                    % endif
                                     longhands::${prop.ident}::SpecifiedValue::from_computed_value(
-                                      &self.get_${prop.style_struct.ident.strip("_")}().clone_${prop.ident}()))),
+                                      &self.get_${prop.style_struct.ident.strip("_")}().clone_${prop.ident}())
+                                    % if prop.boxed:
+                                        )
+                                    % endif
+
+                                 )),
                                  Importance::Normal)
                             ],
                             important_count: 0
                         }
                     },
                 % endif
             % endfor
             PropertyDeclarationId::Custom(_name) => unimplemented!(),
--- a/servo/components/style/properties/helpers.mako.rs
+++ b/servo/components/style/properties/helpers.mako.rs
@@ -4,18 +4,23 @@
 
 <%! from data import Keyword, to_rust_ident, to_camel_case, LOGICAL_SIDES, PHYSICAL_SIDES, LOGICAL_SIZES %>
 
 <%def name="longhand(name, **kwargs)">
     <%call expr="raw_longhand(name, **kwargs)">
         ${caller.body()}
         % if not data.longhands_by_name[name].derived_from:
             pub fn parse_specified(context: &ParserContext, input: &mut Parser)
-                               -> Result<DeclaredValue<SpecifiedValue>, ()> {
-                parse(context, input).map(DeclaredValue::Value)
+                % if data.longhands_by_name[name].boxed:
+                                   -> Result<DeclaredValue<Box<SpecifiedValue>>, ()> {
+                    parse(context, input).map(|result| DeclaredValue::Value(Box::new(result)))
+                % else:
+                                   -> Result<DeclaredValue<SpecifiedValue>, ()> {
+                    parse(context, input).map(DeclaredValue::Value)
+                % endif
             }
         % endif
     </%call>
 </%def>
 
 <%def name="predefined_type(name, type, initial_value, parse_method='parse', needs_context=True, **kwargs)">
     <%call expr="longhand(name, predefined_type=type, **kwargs)">
         #[allow(unused_imports)]
@@ -186,17 +191,17 @@
             return ""
     %>
     /// ${property.spec}
     pub mod ${property.ident} {
         #![allow(unused_imports)]
         % if not property.derived_from:
             use cssparser::Parser;
             use parser::{Parse, ParserContext, ParserContextExtraData};
-            use properties::{CSSWideKeyword, DeclaredValue, ShorthandId};
+            use properties::{CSSWideKeyword, DeclaredValue, UnparsedValue, ShorthandId};
         % endif
         use values::{Auto, Either, None_, Normal};
         use cascade_info::CascadeInfo;
         use error_reporting::ParseErrorReporter;
         use properties::longhands;
         use properties::property_bit_field::PropertyBitField;
         use properties::{ComputedValues, PropertyDeclaration};
         use properties::style_structs;
@@ -249,17 +254,17 @@
                                 % if property.has_uncacheable_values:
                                 context.mutate_style().mutate_${data.current_style_struct.name_lower}()
                                                       .set_${property.ident}(computed, cacheable ${maybe_wm});
                                 % else:
                                 context.mutate_style().mutate_${data.current_style_struct.name_lower}()
                                                       .set_${property.ident}(computed ${maybe_wm});
                                 % endif
                             }
-                            DeclaredValue::WithVariables { .. } => unreachable!(),
+                            DeclaredValue::WithVariables(_) => unreachable!(),
                             % if not data.current_style_struct.inherited:
                             DeclaredValue::Unset |
                             % endif
                             DeclaredValue::Initial => {
                                 // We assume that it's faster to use copy_*_from rather than
                                 // set_*(get_initial_value());
                                 let initial_struct = default_style
                                                       .get_${data.current_style_struct.name_lower}();
@@ -293,54 +298,58 @@
                                             error_reporter);
                 % endif
             % else:
                 // Do not allow stylesheets to set derived properties.
             % endif
         }
         % if not property.derived_from:
             pub fn parse_declared(context: &ParserContext, input: &mut Parser)
-                               -> Result<DeclaredValue<SpecifiedValue>, ()> {
+                               % if property.boxed:
+                                   -> Result<DeclaredValue<Box<SpecifiedValue>>, ()> {
+                               % else:
+                                   -> Result<DeclaredValue<SpecifiedValue>, ()> {
+                               % endif
                 match input.try(|i| CSSWideKeyword::parse(context, i)) {
                     Ok(CSSWideKeyword::InheritKeyword) => Ok(DeclaredValue::Inherit),
                     Ok(CSSWideKeyword::InitialKeyword) => Ok(DeclaredValue::Initial),
                     Ok(CSSWideKeyword::UnsetKeyword) => Ok(DeclaredValue::Unset),
                     Err(()) => {
                         input.look_for_var_functions();
                         let start = input.position();
                         let specified = parse_specified(context, input);
                         if specified.is_err() {
                             while let Ok(_) = input.next() {}  // Look for var() after the error.
                         }
                         let var = input.seen_var_functions();
                         if specified.is_err() && var {
                             input.reset(start);
                             let (first_token_type, css) = try!(
                                 ::custom_properties::parse_non_custom_with_var(input));
-                            return Ok(DeclaredValue::WithVariables {
+                            return Ok(DeclaredValue::WithVariables(Box::new(UnparsedValue {
                                 css: css.into_owned(),
                                 first_token_type: first_token_type,
                                 base_url: context.base_url.clone(),
                                 from_shorthand: None,
-                            })
+                            })))
                         }
                         specified
                     }
                 }
             }
         % endif
     }
 </%def>
 
 <%def name="single_keyword(name, values, vector=False, **kwargs)">
     <%call expr="single_keyword_computed(name, values, vector, **kwargs)">
         use values::computed::ComputedValueAsSpecified;
-        use values::NoViewportPercentage;
+        use values::HasViewportPercentage;
         impl ComputedValueAsSpecified for SpecifiedValue {}
-        impl NoViewportPercentage for SpecifiedValue {}
+        no_viewport_percentage!(SpecifiedValue);
     </%call>
 </%def>
 
 <%def name="single_keyword_computed(name, values, vector=False, extra_specified=None, **kwargs)">
     <%
         keyword_kwargs = {a: kwargs.pop(a, None) for a in [
             'gecko_constant_prefix', 'gecko_enum_prefix',
             'extra_gecko_values', 'extra_servo_values',
@@ -407,34 +416,41 @@
                                        **kwargs)
 %>
     % if shorthand:
     /// ${shorthand.spec}
     pub mod ${shorthand.ident} {
         #[allow(unused_imports)]
         use cssparser::Parser;
         use parser::ParserContext;
-        use properties::{longhands, PropertyDeclaration, DeclaredValue, ShorthandId};
+        use properties::{DeclaredValue, PropertyDeclaration, UnparsedValue};
+        use properties::{ShorthandId, longhands};
         use properties::declaration_block::Importance;
         use std::fmt;
         use style_traits::ToCss;
         use super::{SerializeFlags, ALL_INHERIT, ALL_INITIAL, ALL_UNSET};
 
         pub struct Longhands {
             % for sub_property in shorthand.sub_properties:
                 pub ${sub_property.ident}:
                     Option<longhands::${sub_property.ident}::SpecifiedValue>,
             % endfor
         }
 
         /// Represents a serializable set of all of the longhand properties that
         /// correspond to a shorthand.
         pub struct LonghandsToSerialize<'a> {
             % for sub_property in shorthand.sub_properties:
-                pub ${sub_property.ident}: &'a DeclaredValue<longhands::${sub_property.ident}::SpecifiedValue>,
+                % if sub_property.boxed:
+                    pub ${sub_property.ident}:
+                        &'a DeclaredValue<Box<longhands::${sub_property.ident}::SpecifiedValue>>,
+                % else:
+                    pub ${sub_property.ident}:
+                        &'a DeclaredValue<longhands::${sub_property.ident}::SpecifiedValue>,
+                % endif
             % endfor
         }
 
         impl<'a> LonghandsToSerialize<'a> {
             /// Tries to get a serializable set of longhands given a set of
             /// property declarations.
             pub fn from_iter<I>(iter: I) -> Result<Self, ()>
                 where I: Iterator<Item=&'a PropertyDeclaration>,
@@ -484,17 +500,17 @@
             {
                 let mut all_flags = SerializeFlags::all();
                 let mut with_variables = false;
                 % for sub_property in shorthand.sub_properties:
                     match *self.${sub_property.ident} {
                         DeclaredValue::Initial => all_flags &= ALL_INITIAL,
                         DeclaredValue::Inherit => all_flags &= ALL_INHERIT,
                         DeclaredValue::Unset => all_flags &= ALL_UNSET,
-                        DeclaredValue::WithVariables {..} => with_variables = true,
+                        DeclaredValue::WithVariables(_) => with_variables = true,
                         DeclaredValue::Value(..) => {
                             all_flags = SerializeFlags::empty();
                         }
                     }
                 % endfor
 
                 if with_variables {
                     // We don't serialize shorthands with variables
@@ -524,34 +540,38 @@
             if value.is_err() {
                 while let Ok(_) = input.next() {}  // Look for var() after the error.
             }
             let var = input.seen_var_functions();
             if let Ok(value) = value {
                 % for sub_property in shorthand.sub_properties:
                     declarations.push((PropertyDeclaration::${sub_property.camel_case}(
                         match value.${sub_property.ident} {
-                            Some(value) => DeclaredValue::Value(value),
+                            % if sub_property.boxed:
+                                Some(value) => DeclaredValue::Value(Box::new(value)),
+                            % else:
+                                Some(value) => DeclaredValue::Value(value),
+                            % endif
                             None => DeclaredValue::Initial,
                         }
                     ), Importance::Normal));
                 % endfor
                 Ok(())
             } else if var {
                 input.reset(start);
                 let (first_token_type, css) = try!(
                     ::custom_properties::parse_non_custom_with_var(input));
                 % for sub_property in shorthand.sub_properties:
                     declarations.push((PropertyDeclaration::${sub_property.camel_case}(
-                        DeclaredValue::WithVariables {
+                        DeclaredValue::WithVariables(Box::new(UnparsedValue {
                             css: css.clone().into_owned(),
                             first_token_type: first_token_type,
                             base_url: context.base_url.clone(),
                             from_shorthand: Some(ShorthandId::${shorthand.camel_case}),
-                        }
+                        }))
                     ), Importance::Normal));
                 % endfor
                 Ok(())
             } else {
                 Err(())
             }
         }
 
--- a/servo/components/style/properties/helpers/animated_properties.mako.rs
+++ b/servo/components/style/properties/helpers/animated_properties.mako.rs
@@ -273,32 +273,36 @@ impl AnimationValue {
     pub fn uncompute(&self) -> PropertyDeclaration {
         use properties::{longhands, DeclaredValue};
         match *self {
             % for prop in data.longhands:
                 % if prop.animatable:
                     AnimationValue::${prop.camel_case}(ref from) => {
                         PropertyDeclaration::${prop.camel_case}(
                             DeclaredValue::Value(
-                                longhands::${prop.ident}::SpecifiedValue::from_computed_value(from)))
+                                % if prop.boxed:
+                                    Box::new(longhands::${prop.ident}::SpecifiedValue::from_computed_value(from))))
+                                % else:
+                                    longhands::${prop.ident}::SpecifiedValue::from_computed_value(from)))
+                                % endif
                     }
                 % endif
             % endfor
         }
     }
 
     /// Construct an AnimationValue from a property declaration
     pub fn from_declaration(decl: &PropertyDeclaration, context: &Context, initial: &ComputedValues) -> Option<Self> {
         match *decl {
             % for prop in data.longhands:
                 % if prop.animatable:
                     PropertyDeclaration::${prop.camel_case}(ref val) => {
                         let computed = match *val {
                             // https://bugzilla.mozilla.org/show_bug.cgi?id=1326131
-                            DeclaredValue::WithVariables{..} => unimplemented!(),
+                            DeclaredValue::WithVariables(_) => unimplemented!(),
                             DeclaredValue::Value(ref val) => val.to_computed_value(context),
                             % if not prop.style_struct.inherited:
                                 DeclaredValue::Unset |
                             % endif
                             DeclaredValue::Initial => {
                                 let initial_struct = initial.get_${prop.style_struct.name_lower}();
                                 initial_struct.clone_${prop.ident}()
                             },
--- a/servo/components/style/properties/longhand/background.mako.rs
+++ b/servo/components/style/properties/longhand/background.mako.rs
@@ -4,25 +4,25 @@
 
 <%namespace name="helpers" file="/helpers.mako.rs" />
 
 <% data.new_style_struct("Background", inherited=False) %>
 
 ${helpers.predefined_type("background-color", "CSSColor",
     "::cssparser::Color::RGBA(::cssparser::RGBA { red: 0., green: 0., blue: 0., alpha: 0. }) /* transparent */",
     spec="https://drafts.csswg.org/css-backgrounds/#background-color",
-    animatable=True, complex_color=True)}
+    animatable=True, complex_color=True, boxed=True)}
 
 <%helpers:vector_longhand name="background-image" animatable="False"
                           spec="https://drafts.csswg.org/css-backgrounds/#the-background-image"
                           has_uncacheable_values="${product == 'gecko'}">
     use std::fmt;
     use style_traits::ToCss;
+    use values::HasViewportPercentage;
     use values::specified::Image;
-    use values::NoViewportPercentage;
 
     pub mod computed_value {
         use values::computed;
         #[derive(Debug, Clone, PartialEq)]
         #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
         pub struct T(pub Option<computed::Image>);
     }
 
@@ -30,17 +30,17 @@
         fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
             match self.0 {
                 None => dest.write_str("none"),
                 Some(ref image) => image.to_css(dest),
             }
         }
     }
 
-    impl NoViewportPercentage for SpecifiedValue {}
+    no_viewport_percentage!(SpecifiedValue);
 
     #[derive(Debug, Clone, PartialEq)]
     #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
     pub struct SpecifiedValue(pub Option<Image>);
 
     impl ToCss for SpecifiedValue {
         fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
             match *self {
--- a/servo/components/style/properties/longhand/border.mako.rs
+++ b/servo/components/style/properties/longhand/border.mako.rs
@@ -15,17 +15,17 @@
         else:
             return "https://drafts.csswg.org/css-backgrounds/#border-%s-%s" % (side[0], kind)
 %>
 % for side in ALL_SIDES:
     ${helpers.predefined_type("border-%s-color" % side[0], "CSSColor",
                               "::cssparser::Color::CurrentColor",
                               alias=maybe_moz_logical_alias(product, side, "-moz-border-%s-color"),
                               spec=maybe_logical_spec(side, "color"),
-                              animatable=True, logical = side[1])}
+                              animatable=True, logical = side[1], boxed=True)}
 % endfor
 
 % for side in ALL_SIDES:
     ${helpers.predefined_type("border-%s-style" % side[0], "BorderStyle",
                               "specified::BorderStyle::none",
                               need_clone=True,
                               alias=maybe_moz_logical_alias(product, side, "-moz-border-%s-style"),
                               spec=maybe_logical_spec(side, "style"),
@@ -78,24 +78,24 @@
 ${helpers.single_keyword("-moz-float-edge", "content-box margin-box",
                          gecko_ffi_name="mFloatEdge",
                          gecko_enum_prefix="StyleFloatEdge",
                          gecko_inexhaustive=True,
                          products="gecko",
                          spec="Nonstandard (https://developer.mozilla.org/en-US/docs/Web/CSS/-moz-float-edge)",
                          animatable=False)}
 
-<%helpers:longhand name="border-image-source" products="gecko" animatable="False"
+<%helpers:longhand name="border-image-source" products="gecko" animatable="False" boxed="True"
                    spec="https://drafts.csswg.org/css-backgrounds/#border-image-source">
     use std::fmt;
     use style_traits::ToCss;
-    use values::NoViewportPercentage;
+    use values::HasViewportPercentage;
     use values::specified::Image;
 
-    impl NoViewportPercentage for SpecifiedValue {}
+    no_viewport_percentage!(SpecifiedValue);
 
     pub mod computed_value {
         use values::computed;
         #[derive(Debug, Clone, PartialEq)]
         #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
         pub struct T(pub Option<computed::Image>);
     }
 
@@ -274,19 +274,19 @@
         }
     }
 </%helpers:longhand>
 
 <%helpers:longhand name="border-image-repeat" products="gecko" animatable="False"
                    spec="https://drafts.csswg.org/css-backgrounds/#border-image-repeat">
     use std::fmt;
     use style_traits::ToCss;
-    use values::NoViewportPercentage;
+    use values::HasViewportPercentage;
 
-    impl NoViewportPercentage for SpecifiedValue {}
+    no_viewport_percentage!(SpecifiedValue);
 
     pub mod computed_value {
         pub use super::RepeatKeyword;
         use values::computed;
 
         #[derive(Debug, Clone, PartialEq)]
         #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
         pub struct T(pub RepeatKeyword, pub RepeatKeyword);
@@ -552,20 +552,20 @@
         }
     }
 </%helpers:longhand>
 
 <%helpers:longhand name="border-image-slice" products="gecko" animatable="False"
                    spec="https://drafts.csswg.org/css-backgrounds/#border-image-slice">
     use std::fmt;
     use style_traits::ToCss;
-    use values::NoViewportPercentage;
+    use values::HasViewportPercentage;
     use values::specified::{Number, Percentage};
 
-    impl NoViewportPercentage for SpecifiedValue {}
+    no_viewport_percentage!(SpecifiedValue);
 
     pub mod computed_value {
         use values::computed::Number;
         use values::specified::Percentage;
         #[derive(Debug, Clone, PartialEq)]
         #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
         pub struct T {
             pub corners: Vec<PercentageOrNumber>,
--- a/servo/components/style/properties/longhand/box.mako.rs
+++ b/servo/components/style/properties/longhand/box.mako.rs
@@ -26,18 +26,18 @@
             values += """grid inline-grid ruby ruby-base ruby-base-container
                 ruby-text ruby-text-container contents flow-root -webkit-box
                 -webkit-inline-box -moz-box -moz-inline-box -moz-grid -moz-inline-grid
                 -moz-grid-group -moz-grid-line -moz-stack -moz-inline-stack -moz-deck
                 -moz-popup -moz-groupbox""".split()
     %>
     use values::computed::ComputedValueAsSpecified;
     use style_traits::ToCss;
-    use values::NoViewportPercentage;
-    impl NoViewportPercentage for SpecifiedValue {}
+    use values::HasViewportPercentage;
+    no_viewport_percentage!(SpecifiedValue);
 
     pub mod computed_value {
         pub use super::SpecifiedValue as T;
     }
 
     #[allow(non_camel_case_types)]
     #[derive(Clone, Eq, PartialEq, Copy, Hash, RustcEncodable, Debug)]
     #[cfg_attr(feature = "servo", derive(HeapSizeOf, Deserialize, Serialize))]
@@ -108,18 +108,18 @@
                                   animatable="False"
                                   spec="https://drafts.csswg.org/css-position/#position-property">
     impl SpecifiedValue {
         pub fn is_absolutely_positioned_style(&self) -> bool {
             matches!(*self, SpecifiedValue::absolute | SpecifiedValue::fixed)
         }
     }
 
-    use values::NoViewportPercentage;
-    impl NoViewportPercentage for SpecifiedValue {}
+    use values::HasViewportPercentage;
+    no_viewport_percentage!(SpecifiedValue);
     impl ToComputedValue for SpecifiedValue {
         type ComputedValue = computed_value::T;
 
         #[inline]
         fn to_computed_value(&self, _context: &Context) -> computed_value::T {
             % if product == "gecko":
                 // https://fullscreen.spec.whatwg.org/#new-stacking-layer
                 // Any position value other than 'absolute' and 'fixed' are
@@ -147,18 +147,18 @@
                                   // https://drafts.csswg.org/css-logical-props/#float-clear
                                   extra_specified="inline-start inline-end"
                                   animatable="False"
                                   need_clone="True"
                                   gecko_enum_prefix="StyleFloat"
                                   gecko_inexhaustive="True"
                                   gecko_ffi_name="mFloat"
                                   spec="https://drafts.csswg.org/css-box/#propdef-float">
-    use values::NoViewportPercentage;
-    impl NoViewportPercentage for SpecifiedValue {}
+    use values::HasViewportPercentage;
+    no_viewport_percentage!(SpecifiedValue);
     impl ToComputedValue for SpecifiedValue {
         type ComputedValue = computed_value::T;
 
         #[inline]
         fn to_computed_value(&self, context: &Context) -> computed_value::T {
             if context.style().get_box().clone_position().is_absolutely_positioned_style() {
                 computed_value::T::none
             } else {
@@ -189,18 +189,18 @@
 <%helpers:single_keyword_computed name="clear"
                                   values="none left right both"
                                   // https://drafts.csswg.org/css-logical-props/#float-clear
                                   extra_specified="inline-start inline-end"
                                   animatable="False"
                                   gecko_enum_prefix="StyleClear"
                                   gecko_ffi_name="mBreakType"
                                   spec="https://www.w3.org/TR/CSS2/visuren.html#flow-control">
-    use values::NoViewportPercentage;
-    impl NoViewportPercentage for SpecifiedValue {}
+    use values::HasViewportPercentage;
+    no_viewport_percentage!(SpecifiedValue);
     impl ToComputedValue for SpecifiedValue {
         type ComputedValue = computed_value::T;
 
         #[inline]
         fn to_computed_value(&self, context: &Context) -> computed_value::T {
             let ltr = context.style().writing_mode.is_bidi_ltr();
             // https://drafts.csswg.org/css-logical-props/#float-clear
             match *self {
@@ -391,19 +391,19 @@
 // FIXME(pcwalton, #2742): Implement scrolling for `scroll` and `auto`.
 <%helpers:longhand name="overflow-y" need_clone="True" animatable="False"
                    spec="https://drafts.csswg.org/css-overflow/#propdef-overflow-y">
     use super::overflow_x;
 
     use std::fmt;
     use style_traits::ToCss;
     use values::computed::ComputedValueAsSpecified;
-    use values::NoViewportPercentage;
+    use values::HasViewportPercentage;
 
-    impl NoViewportPercentage for SpecifiedValue {}
+    no_viewport_percentage!(SpecifiedValue);
 
     impl ToCss for SpecifiedValue {
         fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
             self.0.to_css(dest)
         }
     }
 
     /// The specified and computed value for overflow-y is a wrapper on top of
@@ -437,18 +437,18 @@
 <%helpers:vector_longhand name="transition-duration"
                           need_index="True"
                           animatable="False"
                           extra_prefixes="moz webkit"
                           spec="https://drafts.csswg.org/css-transitions/#propdef-transition-duration">
     use values::specified::Time;
 
     pub use values::specified::Time as SpecifiedValue;
-    use values::NoViewportPercentage;
-    impl NoViewportPercentage for SpecifiedValue {}
+    use values::HasViewportPercentage;
+    no_viewport_percentage!(SpecifiedValue);
 
     pub mod computed_value {
         pub use values::computed::Time as T;
     }
 
     #[inline]
     pub fn get_initial_value() -> Time {
         Time(0.0)
@@ -698,18 +698,18 @@
                 },
                 computed_value::T::Steps(count, start_end) => {
                     SpecifiedValue::Steps(count, start_end)
                 },
             }
         }
     }
 
-    use values::NoViewportPercentage;
-    impl NoViewportPercentage for SpecifiedValue {}
+    use values::HasViewportPercentage;
+    no_viewport_percentage!(SpecifiedValue);
 
     #[inline]
     pub fn get_initial_value() -> computed_value::T {
         ease()
     }
 
     #[inline]
     pub fn get_initial_specified_value() -> SpecifiedValue {
@@ -740,18 +740,18 @@
         // generated beforehand.
         pub use super::SpecifiedValue as T;
     }
 
     pub fn parse(_context: &ParserContext, input: &mut Parser) -> Result<SpecifiedValue,()> {
         SpecifiedValue::parse(input)
     }
 
-    use values::NoViewportPercentage;
-    impl NoViewportPercentage for SpecifiedValue {}
+    use values::HasViewportPercentage;
+    no_viewport_percentage!(SpecifiedValue);
 
     impl ComputedValueAsSpecified for SpecifiedValue { }
 </%helpers:vector_longhand>
 
 <%helpers:vector_longhand name="transition-delay"
                           need_index="True"
                           animatable="False"
                           extra_prefixes="moz webkit"
@@ -768,17 +768,17 @@
                           extra_prefixes="moz webkit"
                           allowed_in_keyframe_block="False"
                           spec="https://drafts.csswg.org/css-animations/#propdef-animation-name">
     use Atom;
     use std::fmt;
     use std::ops::Deref;
     use style_traits::ToCss;
     use values::computed::ComputedValueAsSpecified;
-    use values::NoViewportPercentage;
+    use values::HasViewportPercentage;
 
     pub mod computed_value {
         pub use super::SpecifiedValue as T;
     }
 
     #[derive(Clone, Debug, Hash, Eq, PartialEq)]
     #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
     pub struct SpecifiedValue(pub Atom);
@@ -800,17 +800,17 @@
             use cssparser::Token;
             Ok(match input.next() {
                 Ok(Token::Ident(ref value)) if value != "none" => SpecifiedValue(Atom::from(&**value)),
                 Ok(Token::QuotedString(value)) => SpecifiedValue(Atom::from(&*value)),
                 _ => return Err(()),
             })
         }
     }
-    impl NoViewportPercentage for SpecifiedValue {}
+    no_viewport_percentage!(SpecifiedValue);
 
     pub fn parse(context: &ParserContext, input: &mut Parser) -> Result<SpecifiedValue,()> {
         SpecifiedValue::parse(context, input)
     }
 
     impl ComputedValueAsSpecified for SpecifiedValue {}
 </%helpers:vector_longhand>
 
@@ -842,17 +842,17 @@
                           need_index="True"
                           animatable="False",
                           extra_prefixes="moz webkit"
                           spec="https://drafts.csswg.org/css-animations/#propdef-animation-iteration-count",
                           allowed_in_keyframe_block="False">
     use std::fmt;
     use style_traits::ToCss;
     use values::computed::ComputedValueAsSpecified;
-    use values::NoViewportPercentage;
+    use values::HasViewportPercentage;
 
     pub mod computed_value {
         pub use super::SpecifiedValue as T;
     }
 
     // https://drafts.csswg.org/css-animations/#animation-iteration-count
     #[derive(Debug, Clone, PartialEq)]
     #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
@@ -880,17 +880,17 @@
         fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
             match *self {
                 SpecifiedValue::Number(n) => write!(dest, "{}", n),
                 SpecifiedValue::Infinite => dest.write_str("infinite"),
             }
         }
     }
 
-    impl NoViewportPercentage for SpecifiedValue {}
+    no_viewport_percentage!(SpecifiedValue);
 
     #[inline]
     pub fn get_initial_value() -> computed_value::T {
         computed_value::T::Number(1.0)
     }
 
     #[inline]
     pub fn parse(context: &ParserContext, input: &mut Parser) -> Result<SpecifiedValue, ()> {
@@ -1750,17 +1750,17 @@
 // `auto` keyword is not supported in gecko yet.
 ${helpers.single_keyword("transform-style",
                          "auto flat preserve-3d" if product == "servo" else
                          "flat preserve-3d",
                          spec="https://drafts.csswg.org/css-transforms/#transform-style-property",
                          extra_prefixes="moz webkit",
                          animatable=False)}
 
-<%helpers:longhand name="transform-origin" animatable="True" extra_prefixes="moz webkit"
+<%helpers:longhand name="transform-origin" animatable="True" extra_prefixes="moz webkit" boxed="True"
                    spec="https://drafts.csswg.org/css-transforms/#transform-origin-property">
     use app_units::Au;
     use std::fmt;
     use style_traits::ToCss;
     use values::HasViewportPercentage;
     use values::specified::{NoCalcLength, LengthOrPercentage, Percentage};
 
     pub mod computed_value {
@@ -1891,17 +1891,18 @@
                          products="gecko",
                          spec="Nonstandard (https://developer.mozilla.org/en-US/docs/Web/CSS/-moz-appearance)",
                          animatable=False)}
 
 ${helpers.predefined_type("-moz-binding", "UrlOrNone", "Either::Second(None_)",
                           products="gecko",
                           animatable="False",
                           spec="Nonstandard (https://developer.mozilla.org/en-US/docs/Web/CSS/-moz-binding)",
-                          disable_when_testing="True")}
+                          disable_when_testing="True",
+                          boxed=True)}
 
 ${helpers.single_keyword("-moz-orient",
                           "inline block horizontal vertical",
                           products="gecko",
                           gecko_ffi_name="mOrient",
                           gecko_enum_prefix="StyleOrient",
                           spec="Nonstandard (https://developer.mozilla.org/en-US/docs/Web/CSS/-moz-orient)",
                           animatable=False)}
--- a/servo/components/style/properties/longhand/color.mako.rs
+++ b/servo/components/style/properties/longhand/color.mako.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/. */
 
 <%namespace name="helpers" file="/helpers.mako.rs" />
 
 <% data.new_style_struct("Color", inherited=True) %>
 
-<%helpers:raw_longhand name="color" need_clone="True" animatable="True"
+<%helpers:raw_longhand name="color" need_clone="True" animatable="True" boxed="True"
                        spec="https://drafts.csswg.org/css-color/#color">
     use cssparser::Color as CSSParserColor;
     use cssparser::RGBA;
     use values::specified::{CSSColor, CSSRGBA};
 
     impl ToComputedValue for SpecifiedValue {
         type ComputedValue = computed_value::T;
 
@@ -34,20 +34,20 @@
         use cssparser;
         pub type T = cssparser::RGBA;
     }
     #[inline]
     pub fn get_initial_value() -> computed_value::T {
         RGBA { red: 0., green: 0., blue: 0., alpha: 1. }  /* black */
     }
     pub fn parse_specified(context: &ParserContext, input: &mut Parser)
-                           -> Result<DeclaredValue<SpecifiedValue>, ()> {
+                           -> Result<DeclaredValue<Box<SpecifiedValue>>, ()> {
         let value = try!(CSSColor::parse(context, input));
         let rgba = match value.parsed {
             CSSParserColor::RGBA(rgba) => rgba,
             CSSParserColor::CurrentColor => return Ok(DeclaredValue::Inherit)
         };
-        Ok(DeclaredValue::Value(CSSRGBA {
+        Ok(DeclaredValue::Value(Box::new(CSSRGBA {
             parsed: rgba,
             authored: value.authored,
-        }))
+        })))
     }
 </%helpers:raw_longhand>
--- a/servo/components/style/properties/longhand/column.mako.rs
+++ b/servo/components/style/properties/longhand/column.mako.rs
@@ -17,19 +17,19 @@
                           spec="https://drafts.csswg.org/css-multicol/#propdef-column-width")}
 
 
 // FIXME: This prop should be animatable.
 <%helpers:longhand name="column-count" experimental="True" animatable="False" extra_prefixes="moz"
                    spec="https://drafts.csswg.org/css-multicol/#propdef-column-count">
     use std::fmt;
     use style_traits::ToCss;
-    use values::NoViewportPercentage;
+    use values::HasViewportPercentage;
 
-    impl NoViewportPercentage for SpecifiedValue {}
+    no_viewport_percentage!(SpecifiedValue);
 
     #[derive(Debug, Clone, Copy, PartialEq)]
     #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
     pub enum SpecifiedValue {
         Auto,
         Specified(u32),
     }
 
@@ -142,17 +142,17 @@
         BorderWidth::parse(context, input)
     }
 </%helpers:longhand>
 
 // https://drafts.csswg.org/css-multicol-1/#crc
 ${helpers.predefined_type("column-rule-color", "CSSColor",
                           "::cssparser::Color::CurrentColor",
                           products="gecko", animatable=True, extra_prefixes="moz",
-                          complex_color=True, need_clone=True,
+                          complex_color=True, need_clone=True, boxed=True,
                           spec="https://drafts.csswg.org/css-multicol/#propdef-column-rule-color")}
 
 // It's not implemented in servo or gecko yet.
 ${helpers.single_keyword("column-span", "none all",
                          products="none", animatable=False,
                          spec="https://drafts.csswg.org/css-multicol/#propdef-column-span")}
 
 ${helpers.single_keyword("column-rule-style",
--- a/servo/components/style/properties/longhand/counters.mako.rs
+++ b/servo/components/style/properties/longhand/counters.mako.rs
@@ -5,25 +5,25 @@
 <%namespace name="helpers" file="/helpers.mako.rs" />
 
 <% data.new_style_struct("Counters", inherited=False, gecko_name="Content") %>
 
 <%helpers:longhand name="content" animatable="False" spec="https://drafts.csswg.org/css-content/#propdef-content">
     use cssparser::Token;
     use std::ascii::AsciiExt;
     use values::computed::ComputedValueAsSpecified;
-    use values::NoViewportPercentage;
+    use values::HasViewportPercentage;
 
     use super::list_style_type;
 
     pub use self::computed_value::T as SpecifiedValue;
     pub use self::computed_value::ContentItem;
 
     impl ComputedValueAsSpecified for SpecifiedValue {}
-    impl NoViewportPercentage for SpecifiedValue {}
+    no_viewport_percentage!(SpecifiedValue);
 
     pub mod computed_value {
         use super::super::list_style_type;
 
         use cssparser;
         use std::fmt;
         use style_traits::ToCss;
 
@@ -174,17 +174,17 @@
     }
 </%helpers:longhand>
 
 <%helpers:longhand name="counter-increment" products="servo" animatable="False"
                    spec="https://drafts.csswg.org/css-lists/#propdef-counter-increment">
     use std::fmt;
     use style_traits::ToCss;
     use super::content;
-    use values::NoViewportPercentage;
+    use values::HasViewportPercentage;
     use values::computed::ComputedValueAsSpecified;
 
     use cssparser::{Token, serialize_identifier};
     use std::borrow::{Cow, ToOwned};
 
     pub use self::computed_value::T as SpecifiedValue;
 
     pub mod computed_value {
@@ -194,17 +194,17 @@
     }
 
     #[inline]
     pub fn get_initial_value() -> computed_value::T {
         computed_value::T(Vec::new())
     }
 
     impl ComputedValueAsSpecified for SpecifiedValue {}
-    impl NoViewportPercentage for SpecifiedValue {}
+    no_viewport_percentage!(SpecifiedValue);
 
     impl ToCss for SpecifiedValue {
         fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
             let mut first = true;
             for pair in &self.0 {
                 if !first {
                     try!(dest.write_str(" "));
                 }
--- a/servo/components/style/properties/longhand/effects.mako.rs
+++ b/servo/components/style/properties/longhand/effects.mako.rs
@@ -72,17 +72,17 @@
     }
 
     pub fn parse(context: &ParserContext, input: &mut Parser) -> Result<specified::Shadow, ()> {
         specified::Shadow::parse(context, input, false)
     }
 </%helpers:vector_longhand>
 
 // FIXME: This prop should be animatable
-<%helpers:longhand name="clip" products="servo" animatable="False"
+<%helpers:longhand name="clip" products="servo" animatable="False" boxed="True"
                    spec="https://drafts.fxtf.org/css-masking/#clip-property">
     use std::fmt;
     use style_traits::ToCss;
     use values::HasViewportPercentage;
 
     // NB: `top` and `left` are 0 if `auto` per CSS 2.1 11.1.2.
 
     pub mod computed_value {
--- a/servo/components/style/properties/longhand/font.mako.rs
+++ b/servo/components/style/properties/longhand/font.mako.rs
@@ -6,22 +6,22 @@
 <% from data import Method %>
 
 <% data.new_style_struct("Font",
                          inherited=True,
                          additional_methods=[Method("compute_font_hash", is_mut=True)]) %>
 <%helpers:longhand name="font-family" animatable="False" need_index="True"
                    spec="https://drafts.csswg.org/css-fonts/#propdef-font-family">
     use self::computed_value::{FontFamily, FamilyName};
-    use values::NoViewportPercentage;
+    use values::HasViewportPercentage;
     use values::computed::ComputedValueAsSpecified;
     pub use self::computed_value::T as SpecifiedValue;
 
     impl ComputedValueAsSpecified for SpecifiedValue {}
-    impl NoViewportPercentage for SpecifiedValue {}
+    no_viewport_percentage!(SpecifiedValue);
 
     pub mod computed_value {
         use cssparser::CssStringWriter;
         use std::fmt::{self, Write};
         use Atom;
         use style_traits::ToCss;
         pub use self::FontFamily as SingleComputedValue;
 
@@ -218,19 +218,19 @@
                          spec="https://drafts.csswg.org/css-fonts/#propdef-font-variant-caps",
                          custom_consts=font_variant_caps_custom_consts,
                          animatable=False)}
 
 <%helpers:longhand name="font-weight" need_clone="True" animatable="True"
                    spec="https://drafts.csswg.org/css-fonts/#propdef-font-weight">
     use std::fmt;
     use style_traits::ToCss;
-    use values::NoViewportPercentage;
+    use values::HasViewportPercentage;
 
-    impl NoViewportPercentage for SpecifiedValue {}
+    no_viewport_percentage!(SpecifiedValue);
 
     #[derive(Debug, Clone, PartialEq, Eq, Copy)]
     #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
     pub enum SpecifiedValue {
         Normal,
         Bold,
         Bolder,
         Lighter,
@@ -453,22 +453,22 @@
                          .ok_or(())
                          .map(specified::LengthOrPercentage::Length)
         }).map(SpecifiedValue)
     }
 </%helpers:longhand>
 
 <%helpers:longhand products="gecko" name="font-size-adjust" animatable="True"
                    spec="https://drafts.csswg.org/css-fonts/#propdef-font-size-adjust">
-    use values::NoViewportPercentage;
+    use values::HasViewportPercentage;
     use values::computed::ComputedValueAsSpecified;
     use values::specified::Number;
 
     impl ComputedValueAsSpecified for SpecifiedValue {}
-    impl NoViewportPercentage for SpecifiedValue {}
+    no_viewport_percentage!(SpecifiedValue);
 
     #[derive(Copy, Clone, Debug, PartialEq)]
     #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
     pub enum SpecifiedValue {
         None,
         Number(Number),
     }
 
@@ -515,21 +515,21 @@
         Ok(SpecifiedValue::Number(try!(Number::parse_non_negative(input))))
     }
 </%helpers:longhand>
 
 <%helpers:longhand products="gecko" name="font-synthesis" animatable="False"
                    spec="https://drafts.csswg.org/css-fonts/#propdef-font-synthesis">
     use std::fmt;
     use style_traits::ToCss;
-    use values::NoViewportPercentage;
+    use values::HasViewportPercentage;
     use values::computed::ComputedValueAsSpecified;
 
     impl ComputedValueAsSpecified for SpecifiedValue {}
-    impl NoViewportPercentage for SpecifiedValue {}
+    no_viewport_percentage!(SpecifiedValue);
 
     pub mod computed_value {
         pub use super::SpecifiedValue as T;
     }
 
     #[derive(Debug, Clone, PartialEq)]
     #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
     pub struct SpecifiedValue {
@@ -605,22 +605,22 @@
                          gecko_constant_prefix="NS_FONT_VARIANT_POSITION",
                          spec="https://drafts.csswg.org/css-fonts/#propdef-font-variant-position",
                          animatable=False)}
 
 <%helpers:longhand name="font-feature-settings" products="none" animatable="False" extra_prefixes="moz"
                    spec="https://drafts.csswg.org/css-fonts/#propdef-font-feature-settings">
     use std::fmt;
     use style_traits::ToCss;
-    use values::NoViewportPercentage;
+    use values::HasViewportPercentage;
     use values::computed::ComputedValueAsSpecified;
     pub use self::computed_value::T as SpecifiedValue;
 
     impl ComputedValueAsSpecified for SpecifiedValue {}
-    impl NoViewportPercentage for SpecifiedValue {}
+    no_viewport_percentage!(SpecifiedValue);
 
     pub mod computed_value {
         use cssparser::Parser;
         use parser::{Parse, ParserContext};
         use std::fmt;
         use style_traits::ToCss;
 
         #[derive(Debug, Clone, PartialEq)]
@@ -714,22 +714,22 @@
                  .map(computed_value::T::Tag)
         }
     }
 </%helpers:longhand>
 
 // https://www.w3.org/TR/css-fonts-3/#propdef-font-language-override
 <%helpers:longhand name="font-language-override" products="none" animatable="False" extra_prefixes="moz"
                    spec="https://drafts.csswg.org/css-fonts-3/#propdef-font-language-override">
-    use values::NoViewportPercentage;
+    use values::HasViewportPercentage;
     use values::computed::ComputedValueAsSpecified;
     pub use self::computed_value::T as SpecifiedValue;
 
     impl ComputedValueAsSpecified for SpecifiedValue {}
-    impl NoViewportPercentage for SpecifiedValue {}
+    no_viewport_percentage!(SpecifiedValue);
 
     pub mod computed_value {
         use std::fmt;
         use style_traits::ToCss;
 
         impl ToCss for T {
             fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
                 match *self {
--- a/servo/components/style/properties/longhand/inherited_box.mako.rs
+++ b/servo/components/style/properties/longhand/inherited_box.mako.rs
@@ -30,18 +30,18 @@
     name="text-orientation"
     values="mixed upright sideways"
     extra_specified="sideways-right"
     products="gecko"
     need_clone="True"
     animatable="False"
     spec="https://drafts.csswg.org/css-writing-modes/#propdef-text-orientation"
 >
-    use values::NoViewportPercentage;
-    impl NoViewportPercentage for SpecifiedValue {}
+    use values::HasViewportPercentage;
+    no_viewport_percentage!(SpecifiedValue);
 
     impl ToComputedValue for SpecifiedValue {
         type ComputedValue = computed_value::T;
 
         #[inline]
         fn to_computed_value(&self, _: &Context) -> computed_value::T {
             match *self {
                 % for value in "mixed upright sideways".split():
@@ -86,18 +86,18 @@
                    products="None"
                    animatable="False"
     spec="https://drafts.csswg.org/css-images/#propdef-image-orientation, \
       /// additional values in https://developer.mozilla.org/en-US/docs/Web/CSS/image-orientation">
     use std::fmt;
     use style_traits::ToCss;
     use values::specified::Angle;
 
-    use values::NoViewportPercentage;
-    impl NoViewportPercentage for SpecifiedValue {}
+    use values::HasViewportPercentage;
+    no_viewport_percentage!(SpecifiedValue);
 
     use std::f32::consts::PI;
     use values::CSSFloat;
     const TWO_PI: CSSFloat = 2.0*PI;
 
     #[derive(Clone, PartialEq, Copy, Debug)]
     #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
     pub struct SpecifiedValue {
@@ -220,19 +220,19 @@
 <%helpers:longhand name="-servo-under-display-none"
                    derived_from="display"
                    products="servo"
                    animatable="False"
                    spec="Nonstandard (internal layout use only)">
     use std::fmt;
     use style_traits::ToCss;
     use values::computed::ComputedValueAsSpecified;
-    use values::NoViewportPercentage;
+    use values::HasViewportPercentage;
 
-    impl NoViewportPercentage for SpecifiedValue {}
+    no_viewport_percentage!(SpecifiedValue);
 
     #[derive(Copy, Clone, Debug, Eq, PartialEq)]
     #[cfg_attr(feature = "servo", derive(HeapSizeOf, Deserialize, Serialize))]
     pub struct SpecifiedValue(pub bool);
 
     pub mod computed_value {
         pub type T = super::SpecifiedValue;
     }
--- a/servo/components/style/properties/longhand/inherited_table.mako.rs
+++ b/servo/components/style/properties/longhand/inherited_table.mako.rs
@@ -14,17 +14,17 @@
                          gecko_constant_prefix="NS_STYLE_TABLE_EMPTY_CELLS",
                          animatable=False,
                          spec="https://drafts.csswg.org/css-tables/#propdef-empty-cells")}
 ${helpers.single_keyword("caption-side", "top bottom",
                          extra_gecko_values="right left top-outside bottom-outside",
                          animatable=False,
                          spec="https://drafts.csswg.org/css-tables/#propdef-caption-side")}
 
-<%helpers:longhand name="border-spacing" animatable="False"
+<%helpers:longhand name="border-spacing" animatable="False" boxed="True"
                    spec="https://drafts.csswg.org/css-tables/#propdef-border-spacing">
     use app_units::Au;
     use std::fmt;
     use style_traits::ToCss;
     use values::HasViewportPercentage;
 
     pub mod computed_value {
         use app_units::Au;
--- a/servo/components/style/properties/longhand/inherited_text.mako.rs
+++ b/servo/components/style/properties/longhand/inherited_text.mako.rs
@@ -199,19 +199,19 @@
                          gecko_constant_prefix="NS_STYLE_TEXT_ALIGN",
                          animatable=False,
                          spec="https://drafts.csswg.org/css-text/#propdef-text-align-last")}
 
 // TODO make this a shorthand and implement text-align-last/text-align-all
 <%helpers:longhand name="text-align" animatable="False" spec="https://drafts.csswg.org/css-text/#propdef-text-align">
     pub use self::computed_value::T as SpecifiedValue;
     use values::computed::ComputedValueAsSpecified;
-    use values::NoViewportPercentage;
+    use values::HasViewportPercentage;
     impl ComputedValueAsSpecified for SpecifiedValue {}
-    impl NoViewportPercentage for SpecifiedValue {}
+    no_viewport_percentage!(SpecifiedValue);
     pub mod computed_value {
         use style_traits::ToCss;
         macro_rules! define_text_align {
             ( $( $name: ident ( $string: expr ) => $discriminant: expr, )+ ) => {
                 define_css_keyword_enum! { T:
                     $(
                         $string => $name,
                     )+
@@ -424,26 +424,26 @@
                                           .map(SpecifiedValue::Specified)
         }
     }
 </%helpers:longhand>
 
 <%helpers:longhand name="-servo-text-decorations-in-effect"
                    derived_from="display text-decoration"
                    need_clone="True" products="servo"
-                   animatable="False"
+                   animatable="False" boxed="True"
                    spec="Nonstandard (Internal property used by Servo)">
     use cssparser::RGBA;
     use std::fmt;
     use style_traits::ToCss;
-    use values::NoViewportPercentage;
+    use values::HasViewportPercentage;
     use values::computed::ComputedValueAsSpecified;
 
     impl ComputedValueAsSpecified for SpecifiedValue {}
-    impl NoViewportPercentage for SpecifiedValue {}
+    no_viewport_percentage!(SpecifiedValue);
 
     #[derive(Clone, PartialEq, Copy, Debug)]
     #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
     pub struct SpecifiedValue {
         pub underline: Option<RGBA>,
         pub overline: Option<RGBA>,
         pub line_through: Option<RGBA>,
     }
@@ -513,19 +513,19 @@
 </%helpers:longhand>
 
 <%helpers:single_keyword_computed name="white-space"
                                   values="normal pre nowrap pre-wrap pre-line"
                                   gecko_constant_prefix="NS_STYLE_WHITESPACE"
                                   animatable="False"
                                   spec="https://drafts.csswg.org/css-text/#propdef-white-space">
     use values::computed::ComputedValueAsSpecified;
-    use values::NoViewportPercentage;
+    use values::HasViewportPercentage;
     impl ComputedValueAsSpecified for SpecifiedValue {}
-    impl NoViewportPercentage for SpecifiedValue {}
+    no_viewport_percentage!(SpecifiedValue);
 
     impl SpecifiedValue {
         pub fn allow_wrap(&self) -> bool {
             match *self {
                 SpecifiedValue::nowrap |
                 SpecifiedValue::pre => false,
                 SpecifiedValue::normal |
                 SpecifiedValue::pre_wrap |
@@ -766,19 +766,19 @@
 </%helpers:longhand>
 
 <%helpers:longhand name="text-emphasis-style" products="gecko" need_clone="True" animatable="False"
                    spec="https://drafts.csswg.org/css-text-decor/#propdef-text-emphasis-style">
     use computed_values::writing_mode::T as writing_mode;
     use std::fmt;
     use style_traits::ToCss;
     use unicode_segmentation::UnicodeSegmentation;
-    use values::NoViewportPercentage;
+    use values::HasViewportPercentage;
 
-    impl NoViewportPercentage for SpecifiedValue {}
+    no_viewport_percentage!(SpecifiedValue);
 
     pub mod computed_value {
         #[derive(Debug, Clone, PartialEq)]
         #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
         pub enum T {
             Keyword(KeywordValue),
             None,
             String(String),
@@ -972,17 +972,17 @@
         Ok(SpecifiedValue::Keyword(keyword_value))
     }
 </%helpers:longhand>
 
 <%helpers:longhand name="text-emphasis-position" animatable="False" products="none"
                    spec="https://drafts.csswg.org/css-text-decor/#propdef-text-emphasis-position">
     use std::fmt;
     use values::computed::ComputedValueAsSpecified;
-    use values::NoViewportPercentage;
+    use values::HasViewportPercentage;
     use style_traits::ToCss;
 
     define_css_keyword_enum!(HorizontalWritingModeValue:
                              "over" => Over,
                              "under" => Under);
     define_css_keyword_enum!(VerticalWritingModeValue:
                              "right" => Right,
                              "left" => Left);
@@ -991,17 +991,17 @@
     #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
     pub struct SpecifiedValue(pub HorizontalWritingModeValue, pub VerticalWritingModeValue);
 
     pub mod computed_value {
         pub type T = super::SpecifiedValue;
     }
 
     impl ComputedValueAsSpecified for SpecifiedValue {}
-    impl NoViewportPercentage for SpecifiedValue {}
+    no_viewport_percentage!(SpecifiedValue);
 
     pub fn get_initial_value() -> computed_value::T {
         SpecifiedValue(HorizontalWritingModeValue::Over, VerticalWritingModeValue::Right)
     }
 
     pub fn parse(_context: &ParserContext, input: &mut Parser) -> Result<SpecifiedValue, ()> {
        if let Ok(horizontal) = input.try(|input| HorizontalWritingModeValue::parse(input)) {
             let vertical = try!(VerticalWritingModeValue::parse(input));
@@ -1022,32 +1022,33 @@
         }
     }
 </%helpers:longhand>
 
 ${helpers.predefined_type("text-emphasis-color", "CSSColor",
                           "::cssparser::Color::CurrentColor",
                           products="gecko",animatable=True,
                           complex_color=True, need_clone=True,
+                          boxed=True,
                           spec="https://drafts.csswg.org/css-text-decor/#propdef-text-emphasis-color")}
 
 // CSS Compatibility
 // https://compat.spec.whatwg.org
 ${helpers.predefined_type(
     "-webkit-text-fill-color", "CSSColor",
     "CSSParserColor::CurrentColor",
     products="gecko", animatable=True,
-    complex_color=True, need_clone=True,
+    complex_color=True, need_clone=True, boxed=True,
     spec="https://compat.spec.whatwg.org/#the-webkit-text-fill-color")}
 
 ${helpers.predefined_type(
     "-webkit-text-stroke-color", "CSSColor",
     "CSSParserColor::CurrentColor",
     products="gecko", animatable=True,
-    complex_color=True, need_clone=True,
+    complex_color=True, need_clone=True, boxed=True,
     spec="https://compat.spec.whatwg.org/#the-webkit-text-stroke-color")}
 
 <%helpers:longhand products="gecko" name="-webkit-text-stroke-width" animatable="False"
                    spec="https://compat.spec.whatwg.org/#the-webkit-text-stroke-width">
     use app_units::Au;
     use std::fmt;
     use style_traits::ToCss;
     use values::HasViewportPercentage;
--- a/servo/components/style/properties/longhand/list.mako.rs
+++ b/servo/components/style/properties/longhand/list.mako.rs
@@ -24,38 +24,38 @@
                            myanmar oriya persian telugu thai tibetan cjk-earthly-branch
                            cjk-heavenly-stem lower-greek hiragana hiragana-iroha katakana
                            katakana-iroha lower-alpha upper-alpha""",
     gecko_constant_prefix="NS_STYLE_LIST_STYLE",
     animatable=False,
     spec="https://drafts.csswg.org/css-lists/#propdef-list-style-type")}
 
 ${helpers.predefined_type("list-style-image", "UrlOrNone", "Either::Second(None_)",
-                          animatable="False",
+                          animatable=False,
                           spec="https://drafts.csswg.org/css-lists/#propdef-list-style-image")}
 
 <%helpers:longhand name="quotes" animatable="False"
                    spec="https://drafts.csswg.org/css-content/#propdef-quotes">
     use cssparser::Token;
     use std::borrow::Cow;
     use std::fmt;
     use style_traits::ToCss;
     use values::computed::ComputedValueAsSpecified;
-    use values::NoViewportPercentage;
+    use values::HasViewportPercentage;
 
     pub use self::computed_value::T as SpecifiedValue;
 
     pub mod computed_value {
         #[derive(Debug, Clone, PartialEq)]
         #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
         pub struct T(pub Vec<(String,String)>);
     }
 
     impl ComputedValueAsSpecified for SpecifiedValue {}
-    impl NoViewportPercentage for SpecifiedValue {}
+    no_viewport_percentage!(SpecifiedValue);
 
     impl ToCss for SpecifiedValue {
         fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
             let mut first = true;
             for pair in &self.0 {
                 if !first {
                     try!(dest.write_str(" "));
                 }
--- a/servo/components/style/properties/longhand/outline.mako.rs
+++ b/servo/components/style/properties/longhand/outline.mako.rs
@@ -6,26 +6,25 @@
 <% from data import Method %>
 
 <% data.new_style_struct("Outline",
                          inherited=False,
                          additional_methods=[Method("outline_has_nonzero_width", "bool")]) %>
 
 // TODO(pcwalton): `invert`
 ${helpers.predefined_type("outline-color", "CSSColor", "::cssparser::Color::CurrentColor",
-                          animatable=True, complex_color=True, need_clone=True,
+                          animatable=True, complex_color=True, need_clone=True, boxed=True,
                           spec="https://drafts.csswg.org/css-ui/#propdef-outline-color")}
 
 <%helpers:longhand name="outline-style" need_clone="True" animatable="False"
                    spec="https://drafts.csswg.org/css-ui/#propdef-outline-style">
 
     use std::fmt;
     use style_traits::ToCss;
     use values::specified::BorderStyle;
-    use values::NoViewportPercentage;
     use values::computed::ComputedValueAsSpecified;
 
     pub type SpecifiedValue = Either<Auto, BorderStyle>;
 
     impl SpecifiedValue {
         #[inline]
         pub fn none_or_hidden(&self) -> bool {
             match *self {
--- a/servo/components/style/properties/longhand/pointing.mako.rs
+++ b/servo/components/style/properties/longhand/pointing.mako.rs
@@ -3,22 +3,22 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 <%namespace name="helpers" file="/helpers.mako.rs" />
 
 <% data.new_style_struct("Pointing", inherited=True, gecko_name="UserInterface") %>
 
 <%helpers:longhand name="cursor" animatable="False" spec="https://drafts.csswg.org/css-ui/#cursor">
     pub use self::computed_value::T as SpecifiedValue;
-    use values::NoViewportPercentage;
+    use values::HasViewportPercentage;
     use values::computed::ComputedValueAsSpecified;
     use values::specified::url::SpecifiedUrl;
 
     impl ComputedValueAsSpecified for SpecifiedValue {}
-    impl NoViewportPercentage for SpecifiedValue {}
+    no_viewport_percentage!(SpecifiedValue);
 
     pub mod computed_value {
         use std::fmt;
         use style_traits::cursor::Cursor;
         use style_traits::ToCss;
         use values::specified::url::SpecifiedUrl;
 
         #[derive(Clone, PartialEq, Copy, Debug)]
--- a/servo/components/style/properties/longhand/position.mako.rs
+++ b/servo/components/style/properties/longhand/position.mako.rs
@@ -19,21 +19,21 @@
 % for side in LOGICAL_SIDES:
     ${helpers.predefined_type("offset-%s" % side, "LengthOrPercentageOrAuto",
                               "computed::LengthOrPercentageOrAuto::Auto",
                               spec="https://drafts.csswg.org/css-logical-props/#propdef-offset-%s" % side,
                               animatable=True, logical=True)}
 % endfor
 
 <%helpers:longhand name="z-index" spec="https://www.w3.org/TR/CSS2/visuren.html#z-index" animatable="True">
-    use values::NoViewportPercentage;
+    use values::HasViewportPercentage;
     use values::computed::ComputedValueAsSpecified;
 
     impl ComputedValueAsSpecified for SpecifiedValue {}
-    impl NoViewportPercentage for SpecifiedValue {}
+    no_viewport_percentage!(SpecifiedValue);
     pub type SpecifiedValue = computed_value::T;
     pub mod computed_value {
         use std::fmt;
         use style_traits::ToCss;
 
         #[derive(PartialEq, Clone, Eq, Copy, Debug)]
         #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
         pub enum T {
@@ -213,10 +213,11 @@
 <% grid_longhands = ["grid-row-start", "grid-row-end", "grid-column-start", "grid-column-end"] %>
 
 % for longhand in grid_longhands:
     ${helpers.predefined_type("%s" % longhand,
                               "GridLine",
                               "Default::default()",
                               animatable=False,
                               spec="https://drafts.csswg.org/css-grid/#propdef-%s" % longhand,
-                              products="gecko")}
+                              products="gecko",
+                              boxed=True)}
 % endfor
--- a/servo/components/style/properties/longhand/svg.mako.rs
+++ b/servo/components/style/properties/longhand/svg.mako.rs
@@ -20,54 +20,57 @@
 
 // Section 13 - Gradients and Patterns
 
 ${helpers.predefined_type(
     "stop-color", "CSSColor",
     "CSSParserColor::RGBA(RGBA { red: 0.0, green: 0.0, blue: 0.0, alpha: 1.0 })",
     products="gecko",
     animatable=False,
+    boxed=True,
     spec="https://www.w3.org/TR/SVGTiny12/painting.html#StopColorProperty")}
 
 ${helpers.predefined_type("stop-opacity", "Opacity", "1.0",
                           products="gecko",
                           animatable=False,
                           spec="https://www.w3.org/TR/SVGTiny12/painting.html#propdef-stop-opacity")}
 
 // Section 15 - Filter Effects
 
 ${helpers.predefined_type(
     "flood-color", "CSSColor",
     "CSSParserColor::RGBA(RGBA { red: 0.0, green: 0.0, blue: 0.0, alpha: 1.0 })",
     products="gecko",
     animatable=False,
+    boxed=True,
     spec="https://www.w3.org/TR/SVG/filters.html#FloodColorProperty")}
 
 ${helpers.predefined_type("flood-opacity", "Opacity",
                           "1.0", products="gecko", animatable=False,
                           spec="https://www.w3.org/TR/SVG/filters.html#FloodOpacityProperty")}
 
 ${helpers.predefined_type(
     "lighting-color", "CSSColor",
     "CSSParserColor::RGBA(RGBA { red: 1.0, green: 1.0, blue: 1.0, alpha: 1.0 })",
     products="gecko",
     animatable=False,
+    boxed=True,
     spec="https://www.w3.org/TR/SVG/filters.html#LightingColorProperty")}
 
 // CSS Masking Module Level 1
 // https://drafts.fxtf.org/css-masking
 ${helpers.single_keyword("mask-type", "luminance alpha",
                          products="gecko", animatable=False,
                          spec="https://drafts.fxtf.org/css-masking/#propdef-mask-type")}
 
-<%helpers:longhand name="clip-path" animatable="False" products="gecko"
+<%helpers:longhand name="clip-path" animatable="False" products="gecko" boxed="True"
                    spec="https://drafts.fxtf.org/css-masking/#propdef-clip-path">
     use std::fmt;
     use style_traits::ToCss;
-    use values::NoViewportPercentage;
+    use values::HasViewportPercentage;
     use values::specified::basic_shape::{ShapeSource, GeometryBox};
 
     pub mod computed_value {
         use app_units::Au;
         use values::computed::basic_shape::{ShapeSource, GeometryBox};
 
         pub type T = ShapeSource<GeometryBox>;
     }
@@ -78,17 +81,17 @@
     pub fn get_initial_value() -> computed_value::T {
         Default::default()
     }
 
     pub fn parse(context: &ParserContext, input: &mut Parser) -> Result<SpecifiedValue, ()> {
         ShapeSource::parse(context, input)
     }
 
-    impl NoViewportPercentage for SpecifiedValue {}
+    no_viewport_percentage!(SpecifiedValue);
 </%helpers:longhand>
 
 ${helpers.single_keyword("mask-mode",
                          "alpha luminance match-source",
                          vector=True,
                          products="gecko",
                          animatable=False,
                          spec="https://drafts.fxtf.org/css-masking/#propdef-mask-mode")}
@@ -191,17 +194,17 @@
 <%helpers:vector_longhand name="mask-image" products="gecko" animatable="False" extra_prefixes="webkit"
                           has_uncacheable_values="${product == 'gecko'}",
                           spec="https://drafts.fxtf.org/css-masking/#propdef-mask-image">
     use std::fmt;
     use style_traits::ToCss;
     use std::sync::Arc;
     use values::specified::Image;
     use values::specified::url::SpecifiedUrl;
-    use values::NoViewportPercentage;
+    use values::HasViewportPercentage;
 
     pub mod computed_value {
         use std::fmt;
         use style_traits::ToCss;
         use values::computed;
         use values::specified::url::SpecifiedUrl;
         #[derive(Debug, Clone, PartialEq)]
         #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
@@ -217,17 +220,17 @@
                     T::None => dest.write_str("none"),
                     T::Image(ref image) => image.to_css(dest),
                     T::Url(ref url) => url.to_css(dest),
                 }
             }
         }
     }
 
-    impl NoViewportPercentage for SpecifiedValue {}
+    no_viewport_percentage!(SpecifiedValue);
 
     #[derive(Debug, Clone, PartialEq)]
     #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
     pub enum SpecifiedValue {
         Image(Image),
         Url(SpecifiedUrl),
         None
     }
--- a/servo/components/style/properties/longhand/text.mako.rs
+++ b/servo/components/style/properties/longhand/text.mako.rs
@@ -7,26 +7,26 @@
 
 <% data.new_style_struct("Text",
                          inherited=False,
                          gecko_name="TextReset",
                          additional_methods=[Method("has_underline", "bool"),
                                              Method("has_overline", "bool"),
                                              Method("has_line_through", "bool")]) %>
 
-<%helpers:longhand name="text-overflow" animatable="False"
+<%helpers:longhand name="text-overflow" animatable="False" boxed="True"
                    spec="https://drafts.csswg.org/css-ui/#propdef-text-overflow">
     use std::fmt;
     use style_traits::ToCss;
-    use values::NoViewportPercentage;
+    use values::HasViewportPercentage;
     use values::computed::ComputedValueAsSpecified;
     use cssparser;
 
     impl ComputedValueAsSpecified for SpecifiedValue {}
-    impl NoViewportPercentage for SpecifiedValue {}
+    no_viewport_percentage!(SpecifiedValue);
 
     #[derive(PartialEq, Eq, Clone, Debug)]
     #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
     pub enum Side {
         Clip,
         Ellipsis,
         String(String),
     }
@@ -103,21 +103,21 @@
 // FIXME: This prop should be animatable.
 <%helpers:longhand name="${'text-decoration' if product == 'servo' else 'text-decoration-line'}"
                    custom_cascade="${product == 'servo'}"
                    animatable="False"
                    disable_when_testing="True",
                    spec="https://drafts.csswg.org/css-text-decor/#propdef-text-decoration-line">
     use std::fmt;
     use style_traits::ToCss;
-    use values::NoViewportPercentage;
+    use values::HasViewportPercentage;
     use values::computed::ComputedValueAsSpecified;
 
     impl ComputedValueAsSpecified for SpecifiedValue {}
-    impl NoViewportPercentage for SpecifiedValue {}
+    no_viewport_percentage!(SpecifiedValue);
 
     #[derive(PartialEq, Eq, Copy, Clone, Debug)]
     #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
     pub struct SpecifiedValue {
         pub underline: bool,
         pub overline: bool,
         pub line_through: bool,
         pub blink: bool,
@@ -209,9 +209,10 @@
                          spec="https://drafts.csswg.org/css-text-decor/#propdef-text-decoration-style")}
 
 ${helpers.predefined_type(
     "text-decoration-color", "CSSColor",
     "CSSParserColor::RGBA(RGBA { red: 0.0, green: 0.0, blue: 0.0, alpha: 1.0 })",
     complex_color=True,
     products="gecko",
     animatable=True,
+    boxed=True,
     spec="https://drafts.csswg.org/css-text-decor/#propdef-text-decoration-color")}
--- a/servo/components/style/properties/properties.mako.rs
+++ b/servo/components/style/properties/properties.mako.rs
@@ -241,33 +241,39 @@ mod property_bit_field {
 }
 
 % for property in data.longhands:
     % if not property.derived_from:
         /// Perform CSS variable substitution if needed, and execute `f` with
         /// the resulting declared value.
         #[allow(non_snake_case)]
         fn substitute_variables_${property.ident}<F>(
-            value: &DeclaredValue<longhands::${property.ident}::SpecifiedValue>,
+            % if property.boxed:
+                value: &DeclaredValue<Box<longhands::${property.ident}::SpecifiedValue>>,
+            % else:
+                value: &DeclaredValue<longhands::${property.ident}::SpecifiedValue>,
+            % endif
             custom_properties: &Option<Arc<::custom_properties::ComputedValuesMap>>,
             f: F,
             error_reporter: &mut StdBox<ParseErrorReporter + Send>)
-            where F: FnOnce(&DeclaredValue<longhands::${property.ident}::SpecifiedValue>)
+            % if property.boxed:
+                where F: FnOnce(&DeclaredValue<Box<longhands::${property.ident}::SpecifiedValue>>)
+            % else:
+                where F: FnOnce(&DeclaredValue<longhands::${property.ident}::SpecifiedValue>)
+            % endif
         {
-            if let DeclaredValue::WithVariables {
-                ref css, first_token_type, ref base_url, from_shorthand
-            } = *value {
+            if let DeclaredValue::WithVariables(ref with_variables) = *value {
                 // FIXME(heycam): A ParserContextExtraData should be built from data
                 // stored in the WithVariables, in case variable expansion results in
                 // a url() value.
                 let extra_data = ParserContextExtraData::default();
-                substitute_variables_${property.ident}_slow(css,
-                                                            first_token_type,
-                                                            base_url,
-                                                            from_shorthand,
+                substitute_variables_${property.ident}_slow(&with_variables.css,
+                                                            with_variables.first_token_type,
+                                                            &with_variables.base_url,
+                                                            with_variables.from_shorthand,
                                                             custom_properties,
                                                             f,
                                                             error_reporter,
                                                             extra_data);
             } else {
                 f(value);
             }
         }
@@ -278,17 +284,21 @@ mod property_bit_field {
                 css: &String,
                 first_token_type: TokenSerializationType,
                 base_url: &ServoUrl,
                 from_shorthand: Option<ShorthandId>,
                 custom_properties: &Option<Arc<::custom_properties::ComputedValuesMap>>,
                 f: F,
                 error_reporter: &mut StdBox<ParseErrorReporter + Send>,
                 extra_data: ParserContextExtraData)
-                where F: FnOnce(&DeclaredValue<longhands::${property.ident}::SpecifiedValue>)
+                % if property.boxed:
+                    where F: FnOnce(&DeclaredValue<Box<longhands::${property.ident}::SpecifiedValue>>)
+                % else:
+                    where F: FnOnce(&DeclaredValue<longhands::${property.ident}::SpecifiedValue>)
+                % endif
         {
             f(&
                 ::custom_properties::substitute(css, first_token_type, custom_properties)
                 .and_then(|css| {
                     // As of this writing, only the base URL is used for property values:
                     //
                     // FIXME(pcwalton): Cloning the error reporter is slow! But so are custom
                     // properties, so whatever...
@@ -300,17 +310,21 @@ mod property_bit_field {
                             None => {
                                 longhands::${property.ident}::parse_specified(&context, input)
                             }
                             % for shorthand in data.shorthands:
                                 % if property in shorthand.sub_properties:
                                     Some(ShorthandId::${shorthand.camel_case}) => {
                                         shorthands::${shorthand.ident}::parse_value(&context, input)
                                         .map(|result| match result.${property.ident} {
-                                            Some(value) => DeclaredValue::Value(value),
+                                            % if property.boxed:
+                                                Some(value) => DeclaredValue::Value(Box::new(value)),
+                                            % else:
+                                                Some(value) => DeclaredValue::Value(value),
+                                            % endif
                                             None => DeclaredValue::Initial,
                                         })
                                     }
                                 % endif
                             % endfor
                             _ => unreachable!()
                         }
                     })
@@ -593,61 +607,68 @@ impl ShorthandId {
 
 /// Servo's representation of a declared value for a given `T`, which is the
 /// declared value for that property.
 #[derive(Clone, PartialEq, Eq, Debug)]
 #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
 pub enum DeclaredValue<T> {
     /// A known specified value from the stylesheet.
     Value(T),
-    /// A value that contained any css variables.
-    WithVariables {
-        /// The css serialization for this value.
-        css: String,
-        /// The first token type for this serialization.
-        first_token_type: TokenSerializationType,
-        /// The base url.
-        base_url: ServoUrl,
-        /// The shorthand this came from.
-        from_shorthand: Option<ShorthandId>,
-    },
+    /// An unparsed value that contains `var()` functions.
+    WithVariables(Box<UnparsedValue>),
     /// The `initial` keyword.
     Initial,
     /// The `inherit` keyword.
     Inherit,
     /// The `unset` keyword.
     Unset,
 }
 
+/// An unparsed property value that contains `var()` functions.
+#[derive(Clone, PartialEq, Eq, Debug)]
+#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
+pub struct UnparsedValue {
+    /// The css serialization for this value.
+    css: String,
+    /// The first token type for this serialization.
+    first_token_type: TokenSerializationType,
+    /// The base url.
+    base_url: ServoUrl,
+    /// The shorthand this came from.
+    from_shorthand: Option<ShorthandId>,
+}
+
 impl<T: HasViewportPercentage> HasViewportPercentage for DeclaredValue<T> {
     fn has_viewport_percentage(&self) -> bool {
         match *self {
             DeclaredValue::Value(ref v) => v.has_viewport_percentage(),
-            DeclaredValue::WithVariables { .. } => {
+            DeclaredValue::WithVariables(_) => {
                 panic!("DeclaredValue::has_viewport_percentage without \
                         resolving variables!")
             },
             DeclaredValue::Initial |
             DeclaredValue::Inherit |
             DeclaredValue::Unset => false,
         }
     }
 }
 
 impl<T: ToCss> ToCss for DeclaredValue<T> {
     fn to_css<W>(&self, dest: &mut W) -> fmt::Result
         where W: fmt::Write,
     {
         match *self {
             DeclaredValue::Value(ref inner) => inner.to_css(dest),
-            DeclaredValue::WithVariables { ref css, from_shorthand: None, .. } => {
-                dest.write_str(css)
-            }
-            // https://drafts.csswg.org/css-variables/#variables-in-shorthands
-            DeclaredValue::WithVariables { .. } => Ok(()),
+            DeclaredValue::WithVariables(ref with_variables) => {
+                // https://drafts.csswg.org/css-variables/#variables-in-shorthands
+                if with_variables.from_shorthand.is_none() {
+                    dest.write_str(&*with_variables.css)?
+                }
+                Ok(())
+            },
             DeclaredValue::Initial => dest.write_str("initial"),
             DeclaredValue::Inherit => dest.write_str("inherit"),
             DeclaredValue::Unset => dest.write_str("unset"),
         }
     }
 }
 
 /// An identifier for a given property declaration, which can be either a
@@ -821,21 +842,25 @@ impl PropertyId {
 }
 
 /// Servo's representation for a property declaration.
 #[derive(PartialEq, Clone)]
 #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
 pub enum PropertyDeclaration {
     % for property in data.longhands:
         /// ${property.name}
-        ${property.camel_case}(DeclaredValue<longhands::${property.ident}::SpecifiedValue>),
+        % if property.boxed:
+            ${property.camel_case}(DeclaredValue<Box<longhands::${property.ident}::SpecifiedValue>>),
+        % else:
+            ${property.camel_case}(DeclaredValue<longhands::${property.ident}::SpecifiedValue>),
+        % endif
     % endfor
     /// A custom property declaration, with the property name and the declared
     /// value.
-    Custom(::custom_properties::Name, DeclaredValue<::custom_properties::SpecifiedValue>),
+    Custom(::custom_properties::Name, DeclaredValue<Box<::custom_properties::SpecifiedValue>>),
 }
 
 impl HasViewportPercentage for PropertyDeclaration {
     fn has_viewport_percentage(&self) -> bool {
         match *self {
             % for property in data.longhands:
                 PropertyDeclaration::${property.camel_case}(ref val) => {
                     val.has_viewport_percentage()
@@ -934,55 +959,58 @@ impl PropertyDeclaration {
             }
         }
     }
 
     fn with_variables_from_shorthand(&self, shorthand: ShorthandId) -> Option< &str> {
         match *self {
             % for property in data.longhands:
                 PropertyDeclaration::${property.camel_case}(ref value) => match *value {
-                    DeclaredValue::WithVariables { ref css, from_shorthand: Some(s), .. }
-                    if s == shorthand => {
-                        Some(&**css)
+                    DeclaredValue::WithVariables(ref with_variables) => {
+                        if let Some(s) = with_variables.from_shorthand {
+                            if s == shorthand {
+                                Some(&*with_variables.css)
+                            } else { None }
+                        } else { None }
                     }
                     _ => None
                 },
             % endfor
             PropertyDeclaration::Custom(..) => None,
         }
     }
 
     /// Return whether this is a pending-substitution value.
     /// https://drafts.csswg.org/css-variables/#variables-in-shorthands
     pub fn with_variables(&self) -> bool {
         match *self {
             % for property in data.longhands:
                 PropertyDeclaration::${property.camel_case}(ref value) => match *value {
-                    DeclaredValue::WithVariables { .. } => true,
+                    DeclaredValue::WithVariables(_) => true,
                     _ => false,
                 },
             % endfor
             PropertyDeclaration::Custom(_, ref value) => match *value {
-                DeclaredValue::WithVariables { .. } => true,
+                DeclaredValue::WithVariables(_) => true,
                 _ => false,
             }
         }
     }
 
     /// Return whether the value is stored as it was in the CSS source,
     /// preserving whitespace (as opposed to being parsed into a more abstract
     /// data structure).
     ///
     /// This is the case of custom properties and values that contain
     /// unsubstituted variables.
     pub fn value_is_unparsed(&self) -> bool {
       match *self {
           % for property in data.longhands:
               PropertyDeclaration::${property.camel_case}(ref value) => {
-                  matches!(*value, DeclaredValue::WithVariables { .. })
+                  matches!(*value, DeclaredValue::WithVariables(_))
               },
           % endfor
           PropertyDeclaration::Custom(..) => true
       }
     }
 
     /// The `in_keyframe_block` parameter controls this:
     ///
@@ -1004,17 +1032,18 @@ impl PropertyDeclaration {
                     Ok(CSSWideKeyword::UnsetKeyword) => DeclaredValue::Unset,
                     Ok(CSSWideKeyword::InheritKeyword) => DeclaredValue::Inherit,
                     Ok(CSSWideKeyword::InitialKeyword) => DeclaredValue::Initial,
                     Err(()) => match ::custom_properties::SpecifiedValue::parse(context, input) {
                         Ok(value) => DeclaredValue::Value(value),
                         Err(()) => return PropertyDeclarationParseResult::InvalidValue,
                     }
                 };
-                result_list.push((PropertyDeclaration::Custom(name, value), Importance::Normal));
+                result_list.push((PropertyDeclaration::Custom(name, value),
+                                  Importance::Normal));
                 return PropertyDeclarationParseResult::ValidOrIgnoredDeclaration;
             }
             PropertyId::Longhand(id) => match id {
             % for property in data.longhands:
                 LonghandId::${property.camel_case} => {
                     % if not property.derived_from:
                         % if not property.allowed_in_keyframe_block:
                             if in_keyframe_block {
@@ -2295,8 +2324,23 @@ macro_rules! longhand_properties_idents 
     ($macro_name: ident) => {
         $macro_name! {
             % for property in data.longhands:
                 ${property.ident}
             % endfor
         }
     }
 }
+
+/// Retuns all longhands SpecifiedValue sizes. This is used in unit tests.
+#[cfg(feature = "testing")]
+pub fn specified_value_sizes() -> Vec<(&'static str, usize, bool)> {
+    use std::mem::size_of;
+    let mut sizes = vec![];
+
+    % for property in data.longhands:
+        sizes.push(("${property.name}",
+                    size_of::<longhands::${property.ident}::SpecifiedValue>(),
+                    ${"true" if property.boxed else "false"}));
+    % endfor
+
+    sizes
+}
--- a/servo/components/style/properties/shorthand/box.mako.rs
+++ b/servo/components/style/properties/shorthand/box.mako.rs
@@ -17,17 +17,17 @@
     }
 
     impl<'a> LonghandsToSerialize<'a>  {
         fn to_css_declared<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
             let x_and_y_equal = match (self.overflow_x, self.overflow_y) {
                 (&DeclaredValue::Value(ref x_value), &DeclaredValue::Value(ref y_container)) => {
                     *x_value == y_container.0
                 },
-                (&DeclaredValue::WithVariables { .. }, &DeclaredValue::WithVariables { .. }) => true,
+                (&DeclaredValue::WithVariables(_), &DeclaredValue::WithVariables(_)) => true,
                 (&DeclaredValue::Initial, &DeclaredValue::Initial) => true,
                 (&DeclaredValue::Inherit, &DeclaredValue::Inherit) => true,
                 (&DeclaredValue::Unset, &DeclaredValue::Unset) => true,
                 _ => false
             };
 
             if x_and_y_equal {
                 try!(self.overflow_x.to_css(dest));
@@ -38,18 +38,18 @@
         // Overflow does not behave like a normal shorthand. When overflow-x and overflow-y are not of equal
         // values, they no longer use the shared property name "overflow".
         // Other shorthands do not include their name in the to_css method
         pub fn to_css_declared_with_name<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
             let x_and_y_equal = match (self.overflow_x, self.overflow_y) {
                 (&DeclaredValue::Value(ref x_value), &DeclaredValue::Value(ref y_container)) => {
                     *x_value == y_container.0
                 },
-                (_, &DeclaredValue::WithVariables { .. }) |
-                (&DeclaredValue::WithVariables { .. }, _) => {
+                (_, &DeclaredValue::WithVariables(_)) |
+                (&DeclaredValue::WithVariables(_), _) => {
                     // We don't serialize shorthands with variables
                     return dest.write_str("");
                 },
                 (&DeclaredValue::Initial, &DeclaredValue::Initial) => true,
                 (&DeclaredValue::Inherit, &DeclaredValue::Inherit) => true,
                 (&DeclaredValue::Unset, &DeclaredValue::Unset) => true,
                 _ => false
             };
--- a/servo/components/style/properties/shorthand/serialize.mako.rs
+++ b/servo/components/style/properties/shorthand/serialize.mako.rs
@@ -53,20 +53,20 @@ pub fn serialize_four_sides<W, I>(dest: 
         try!(write!(dest, " "));
 
         try!(left.to_css(dest));
     }
 
     Ok(())
 }
 
-fn serialize_directional_border<W, I>(dest: &mut W,
+fn serialize_directional_border<W, I,>(dest: &mut W,
                                                 width: &DeclaredValue<I>,
                                                 style: &DeclaredValue<BorderStyle>,
-                                                color: &DeclaredValue<CSSColor>)
+                                                color: &DeclaredValue<Box<CSSColor>>)
                                                 -> fmt::Result where W: fmt::Write, I: ToCss {
     match *width {
         DeclaredValue::Value(ref width) => {
             try!(width.to_css(dest));
         },
         _ => {
             try!(write!(dest, "medium"));
         }
--- a/servo/components/style/values/mod.rs
+++ b/servo/components/style/values/mod.rs
@@ -42,41 +42,48 @@ macro_rules! define_numbered_css_keyword
                 match *self {
                     $( $name::$variant => dest.write_str($css) ),+
                 }
             }
         }
     }
 }
 
+/// A macro used to implement HasViewportPercentage trait
+/// for a given type that may never contain viewport units.
+macro_rules! no_viewport_percentage {
+    ($name: ident) => {
+        impl HasViewportPercentage for $name {
+            #[inline]
+            fn has_viewport_percentage(&self) -> bool {
+                false
+            }
+        }
+    };
+}
+
 pub mod computed;
 pub mod specified;
 
 /// A CSS float value.
 pub type CSSFloat = f32;
 
 /// The default font size.
 pub const FONT_MEDIUM_PX: i32 = 16;
 
 /// A trait used to query whether this value has viewport units.
 pub trait HasViewportPercentage {
     /// Returns true if this value has viewport units.
     fn has_viewport_percentage(&self) -> bool;
 }
 
-/// A trait used as a marker to represent that a given type may never contain
-/// viewport units.
-pub trait NoViewportPercentage {}
-
-impl<T> HasViewportPercentage for T
-    where T: NoViewportPercentage,
-{
+impl<T: HasViewportPercentage> HasViewportPercentage for Box<T> {
     #[inline]
     fn has_viewport_percentage(&self) -> bool {
-        false
+        (**self).has_viewport_percentage()
     }
 }
 
 use self::computed::ComputedValueAsSpecified;
 
 macro_rules! define_keyword_type {
     ($name: ident, $css: expr) => {
         #[derive(Clone, PartialEq, Copy)]
@@ -98,17 +105,17 @@ macro_rules! define_keyword_type {
 
         impl Parse for $name {
             fn parse(_context: &ParserContext, input: &mut ::cssparser::Parser) -> Result<$name, ()> {
                 input.expect_ident_matching($css).map(|_| $name)
             }
         }
 
         impl ComputedValueAsSpecified for $name {}
-        impl NoViewportPercentage for $name {}
+        no_viewport_percentage!($name);
     };
 }
 
 define_keyword_type!(None_, "none");
 define_keyword_type!(Auto, "auto");
 define_keyword_type!(Normal, "normal");
 
 #[derive(Clone, PartialEq, Copy)]
--- a/servo/components/style/values/specified/grid.rs
+++ b/servo/components/style/values/specified/grid.rs
@@ -3,17 +3,17 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 //! A grid line type.
 
 use cssparser::Parser;
 use parser::{Parse, ParserContext};
 use std::fmt;
 use style_traits::ToCss;
-use values::NoViewportPercentage;
+use values::HasViewportPercentage;
 use values::computed::ComputedValueAsSpecified;
 
 #[derive(PartialEq, Clone, Debug)]
 #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
 /// https://drafts.csswg.org/css-grid/#typedef-grid-row-start-grid-line
 #[allow(missing_docs)]
 pub struct GridLine {
     pub is_span: bool,
@@ -91,9 +91,9 @@ impl Parse for GridLine {
             }
         }
 
         Ok(grid_line)
     }
 }
 
 impl ComputedValueAsSpecified for GridLine {}
-impl NoViewportPercentage for GridLine {}
+no_viewport_percentage!(GridLine);
--- a/servo/components/style/values/specified/mod.rs
+++ b/servo/components/style/values/specified/mod.rs
@@ -11,17 +11,17 @@ use cssparser::{self, Parser, Token};
 use euclid::size::Size2D;
 use parser::{ParserContext, Parse};
 use self::url::SpecifiedUrl;
 use std::ascii::AsciiExt;
 use std::f32::consts::PI;
 use std::fmt;
 use std::ops::Mul;
 use style_traits::ToCss;
-use super::{CSSFloat, HasViewportPercentage, NoViewportPercentage, Either, None_};
+use super::{CSSFloat, HasViewportPercentage, Either, None_};
 use super::computed::{ComputedValueAsSpecified, Context, ToComputedValue};
 use super::computed::Shadow as ComputedShadow;
 
 pub use self::grid::GridLine;
 pub use self::image::{AngleOrCorner, ColorStop, EndingShape as GradientEndingShape, Gradient};
 pub use self::image::{GradientKind, HorizontalDirection, Image, LengthOrKeyword, LengthOrPercentageOrKeyword};
 pub use self::image::{SizeKeyword, VerticalDirection};
 pub use self::length::{FontRelativeLength, ViewportPercentageLength, CharacterWidth, Length, CalcLengthOrPercentage};
@@ -30,17 +30,17 @@ pub use self::length::{LengthOrPercentag
 
 pub mod basic_shape;
 pub mod grid;
 pub mod image;
 pub mod length;
 pub mod position;
 pub mod url;
 
-impl NoViewportPercentage for i32 {}  // For PropertyDeclaration::Order
+no_viewport_percentage!(i32);  // For PropertyDeclaration::Order
 
 #[derive(Clone, PartialEq, Debug)]
 #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
 #[allow(missing_docs)]
 pub struct CSSColor {
     pub parsed: cssparser::Color,
     pub authored: Option<String>,
 }
@@ -55,17 +55,17 @@ impl Parse for CSSColor {
         input.reset(start_position);
         Ok(CSSColor {
             parsed: try!(cssparser::Color::parse(input)),
             authored: authored,
         })
     }
 }
 
-impl NoViewportPercentage for CSSColor {}
+no_viewport_percentage!(CSSColor);
 
 impl ToCss for CSSColor {
     fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
         match self.authored {
             Some(ref s) => dest.write_str(s),
             None => self.parsed.to_css(dest),
         }
     }
@@ -74,17 +74,17 @@ impl ToCss for CSSColor {
 #[derive(Clone, PartialEq, Debug)]
 #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
 #[allow(missing_docs)]
 pub struct CSSRGBA {
     pub parsed: cssparser::RGBA,
     pub authored: Option<String>,
 }
 
-impl NoViewportPercentage for CSSRGBA {}
+no_viewport_percentage!(CSSRGBA);
 
 impl ToCss for CSSRGBA {
     fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
         match self.authored {
             Some(ref s) => dest.write_str(s),
             None => self.parsed.to_css(dest),
         }
     }
@@ -188,17 +188,17 @@ pub fn parse_number(input: &mut Parser) 
     }
 }
 
 #[derive(Clone, PartialEq, Debug)]
 #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
 #[allow(missing_docs)]
 pub struct BorderRadiusSize(pub Size2D<LengthOrPercentage>);
 
-impl NoViewportPercentage for BorderRadiusSize {}
+no_viewport_percentage!(BorderRadiusSize);
 
 impl BorderRadiusSize {
     #[allow(missing_docs)]
     pub fn zero() -> BorderRadiusSize {
         let zero = LengthOrPercentage::Length(NoCalcLength::zero());
         BorderRadiusSize(Size2D::new(zero.clone(), zero))
     }
 
@@ -397,17 +397,17 @@ define_numbered_css_keyword_enum! { Bord
     "dashed" => dashed = 5,
     "hidden" => hidden = -2,
     "groove" => groove = 1,
     "ridge" => ridge = 3,
     "inset" => inset = 0,
     "outset" => outset = 2,
 }
 
-impl NoViewportPercentage for BorderStyle {}
+no_viewport_percentage!(BorderStyle);
 
 impl BorderStyle {
     /// Whether this border style is either none or hidden.
     pub fn none_or_hidden(&self) -> bool {
         matches!(*self, BorderStyle::none | BorderStyle::hidden)
     }
 }
 
@@ -457,17 +457,17 @@ impl ToCss for Time {
     }
 }
 
 #[derive(Clone, Copy, Debug, PartialEq, PartialOrd)]
 #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
 #[allow(missing_docs)]
 pub struct Number(pub CSSFloat);
 
-impl NoViewportPercentage for Number {}
+no_viewport_percentage!(Number);
 
 impl Parse for Number {
     fn parse(_context: &ParserContext, input: &mut Parser) -> Result<Self, ()> {
         parse_number(input).map(Number)
     }
 }
 
 impl Number {
@@ -507,17 +507,17 @@ impl ToCss for Number {
     }
 }
 
 #[derive(Clone, Copy, Debug, PartialEq, PartialOrd)]
 #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
 #[allow(missing_docs)]
 pub struct Opacity(pub CSSFloat);
 
-impl NoViewportPercentage for Opacity {}
+no_viewport_percentage!(Opacity);
 
 impl Parse for Opacity {
     fn parse(_context: &ParserContext, input: &mut Parser) -> Result<Self, ()> {
         parse_number(input).map(Opacity)
     }
 }
 
 impl ToComputedValue for Opacity {
--- a/servo/components/style/values/specified/url.rs
+++ b/servo/components/style/values/specified/url.rs
@@ -10,17 +10,17 @@ use gecko_bindings::sugar::refptr::{Geck
 use parser::{Parse, ParserContext};
 #[cfg(feature = "gecko")]
 use parser::ParserContextExtraData;
 use servo_url::ServoUrl;
 use std::borrow::Cow;
 use std::fmt::{self, Write};
 use std::sync::Arc;
 use style_traits::ToCss;
-use values::NoViewportPercentage;
+use values::HasViewportPercentage;
 use values::computed::ComputedValueAsSpecified;
 
 /// A set of data needed in Gecko to represent a URL.
 #[derive(PartialEq, Clone, Debug)]
 #[cfg_attr(feature = "servo", derive(HeapSizeOf, Serialize, Deserialize, Eq))]
 pub struct UrlExtraData {
     /// The base URI.
     #[cfg(feature = "gecko")]
@@ -197,9 +197,9 @@ impl ToCss for SpecifiedUrl {
         try!(CssStringWriter::new(dest).write_str(string));
         dest.write_str("\")")
     }
 }
 
 // TODO(emilio): Maybe consider ComputedUrl to save a word in style structs?
 impl ComputedValueAsSpecified for SpecifiedUrl {}
 
-impl NoViewportPercentage for SpecifiedUrl {}
+no_viewport_percentage!(SpecifiedUrl);
--- a/servo/components/style_traits/values.rs
+++ b/servo/components/style_traits/values.rs
@@ -34,16 +34,24 @@ impl<T> ToCss for Vec<T> where T: ToCss 
         for item in iter {
             dest.write_str(", ")?;
             item.to_css(dest)?;
         }
         Ok(())
     }
 }
 
+impl<T: ToCss> ToCss for Box<T> {
+    fn to_css<W>(&self, dest: &mut W) -> fmt::Result
+        where W: fmt::Write,
+    {
+        (**self).to_css(dest)
+    }
+}
+
 impl ToCss for Au {
     fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
         write!(dest, "{}px", self.to_f64_px())
     }
 }
 
 macro_rules! impl_to_css_for_predefined_type {
     ($name: ty) => {
--- a/servo/ports/geckolib/glue.rs
+++ b/servo/ports/geckolib/glue.rs
@@ -1002,22 +1002,22 @@ pub extern "C" fn Servo_DeclarationBlock
                 ))
             } else {
                 error!("stylo: got unexpected non-integer value for font-size presentation attribute");
                 return
             }
         }
         LonghandId::Color => {
             if let Some(color) = css_value.color_value() {
-                PropertyDeclaration::Color(DeclaredValue::Value(
+                PropertyDeclaration::Color(DeclaredValue::Value(Box::new(
                     specified::CSSRGBA {
                         parsed: convert_nscolor_to_rgba(color),
                         authored: None
                     }
-                ))
+                )))
             } else {
                 error!("stylo: got unexpected non-integer value for color presentation attribute");
                 return
             }
         }
         _ => {
             error!("stylo: cannot handle longhand {:?} from presentation attribute", long);
             return
--- a/servo/tests/unit/style/lib.rs
+++ b/servo/tests/unit/style/lib.rs
@@ -27,16 +27,17 @@ mod attr;
 mod cache;
 mod keyframes;
 mod logical_geometry;
 mod media_queries;
 mod owning_handle;
 mod parsing;
 mod properties;
 mod rule_tree;
+mod size_of;
 mod str;
 mod stylesheets;
 mod stylist;
 mod value;
 mod viewport;
 
 mod writing_modes {
     use style::logical_geometry::WritingMode;
--- a/servo/tests/unit/style/properties/serialization.rs
+++ b/servo/tests/unit/style/properties/serialization.rs
@@ -226,25 +226,25 @@ mod shorthand_serialization {
             let serialization = shorthand_properties_to_string(properties);
             assert_eq!(serialization, "border-width: thin medium thick 15px;");
         }
 
         #[test]
         fn border_color_should_serialize_correctly() {
             let mut properties = Vec::new();
 
-            let red = DeclaredValue::Value(CSSColor {
+            let red = DeclaredValue::Value(Box::new(CSSColor {
                 parsed: ComputedColor::RGBA(RGBA { red: 1f32, green: 0f32, blue: 0f32, alpha: 1f32 }),
                 authored: None
-            });
+            }));
 
-            let blue = DeclaredValue::Value(CSSColor {
+            let blue = DeclaredValue::Value(Box::new(CSSColor {
                 parsed: ComputedColor::RGBA(RGBA { red: 0f32, green: 0f32, blue: 1f32, alpha: 1f32 }),
                 authored: None
-            });
+            }));
 
             properties.push(PropertyDeclaration::BorderTopColor(blue.clone()));
             properties.push(PropertyDeclaration::BorderRightColor(red.clone()));
             properties.push(PropertyDeclaration::BorderBottomColor(blue));
             properties.push(PropertyDeclaration::BorderLeftColor(red));
 
             let serialization = shorthand_properties_to_string(properties);
 
@@ -276,39 +276,39 @@ mod shorthand_serialization {
         // but afterwards, we only need to to one test per "directional border shorthand"
 
         #[test]
         fn directional_border_should_show_all_properties_when_values_are_set() {
             let mut properties = Vec::new();
 
             let width = DeclaredValue::Value(BorderWidth::from_length(Length::from_px(4f32)));
             let style = DeclaredValue::Value(BorderStyle::solid);
-            let color = DeclaredValue::Value(CSSColor {
+            let color = DeclaredValue::Value(Box::new(CSSColor {
                 parsed: ComputedColor::RGBA(RGBA { red: 1f32, green: 0f32, blue: 0f32, alpha: 1f32 }),
                 authored: None
-            });
+            }));
 
             properties.push(PropertyDeclaration::BorderTopWidth(width));
             properties.push(PropertyDeclaration::BorderTopStyle(style));
             properties.push(PropertyDeclaration::BorderTopColor(color));
 
             let serialization = shorthand_properties_to_string(properties);
             assert_eq!(serialization, "border-top: 4px solid rgb(255, 0, 0);");
         }
 
         #[test]
         fn directional_border_with_no_specified_style_will_show_style_as_none() {
             let mut properties = Vec::new();
 
             let width = DeclaredValue::Value(BorderWidth::from_length(Length::from_px(4f32)));
             let style = DeclaredValue::Initial;
-            let color = DeclaredValue::Value(CSSColor {
+            let color = DeclaredValue::Value(Box::new(CSSColor {
                 parsed: ComputedColor::RGBA(RGBA { red: 1f32, green: 0f32, blue: 0f32, alpha: 1f32 }),
                 authored: None
-            });
+            }));
 
             properties.push(PropertyDeclaration::BorderTopWidth(width));
             properties.push(PropertyDeclaration::BorderTopStyle(style));
             properties.push(PropertyDeclaration::BorderTopColor(color));
 
             let serialization = shorthand_properties_to_string(properties);
             assert_eq!(serialization, "border-top: 4px none rgb(255, 0, 0);");
         }
@@ -464,20 +464,20 @@ mod shorthand_serialization {
         use super::*;
 
         #[test]
         fn outline_should_show_all_properties_when_set() {
             let mut properties = Vec::new();
 
             let width = DeclaredValue::Value(WidthContainer(Length::from_px(4f32)));
             let style = DeclaredValue::Value(Either::Second(BorderStyle::solid));
-            let color = DeclaredValue::Value(CSSColor {
+            let color = DeclaredValue::Value(Box::new(CSSColor {
                 parsed: ComputedColor::RGBA(RGBA { red: 1f32, green: 0f32, blue: 0f32, alpha: 1f32 }),
                 authored: None
-            });
+            }));
 
             properties.push(PropertyDeclaration::OutlineWidth(width));
             properties.push(PropertyDeclaration::OutlineStyle(style));
             properties.push(PropertyDeclaration::OutlineColor(color));
 
             let serialization = shorthand_properties_to_string(properties);
             assert_eq!(serialization, "outline: 4px solid rgb(255, 0, 0);");
         }
@@ -499,38 +499,38 @@ mod shorthand_serialization {
         }
 
         #[test]
         fn outline_should_serialize_correctly_when_style_is_not_set() {
             let mut properties = Vec::new();
 
             let width = DeclaredValue::Value(WidthContainer(Length::from_px(4f32)));
             let style = DeclaredValue::Initial;
-            let color = DeclaredValue::Value(CSSColor {
+            let color = DeclaredValue::Value(Box::new(CSSColor {
                 parsed: ComputedColor::RGBA(RGBA { red: 1f32, green: 0f32, blue: 0f32, alpha: 1f32 }),
                 authored: None
-            });
+            }));
             properties.push(PropertyDeclaration::OutlineWidth(width));
             properties.push(PropertyDeclaration::OutlineStyle(style));
             properties.push(PropertyDeclaration::OutlineColor(color));
 
             let serialization = shorthand_properties_to_string(properties);
             assert_eq!(serialization, "outline: 4px none rgb(255, 0, 0);");
         }
 
         #[test]
         fn outline_should_serialize_correctly_when_style_is_auto() {
             let mut properties = Vec::new();
 
             let width = DeclaredValue::Value(WidthContainer(Length::from_px(4f32)));
             let style = DeclaredValue::Value(Either::First(Auto));
-            let color = DeclaredValue::Value(CSSColor {
+            let color = DeclaredValue::Value(Box::new(CSSColor {
                 parsed: ComputedColor::RGBA(RGBA { red: 1f32, green: 0f32, blue: 0f32, alpha: 1f32 }),
                 authored: None
-            });
+            }));
             properties.push(PropertyDeclaration::OutlineWidth(width));
             properties.push(PropertyDeclaration::OutlineStyle(style));
             properties.push(PropertyDeclaration::OutlineColor(color));
 
             let serialization = shorthand_properties_to_string(properties);
             assert_eq!(serialization, "outline: 4px auto rgb(255, 0, 0);");
         }
     }
@@ -725,20 +725,20 @@ mod shorthand_serialization {
                         vec![$variant]
                 ))
             };
         }
         #[test]
         fn background_should_serialize_all_available_properties_when_specified() {
             let mut properties = Vec::new();
 
-            let color = DeclaredValue::Value(CSSColor {
+            let color = DeclaredValue::Value(Box::new(CSSColor {
                 parsed: ComputedColor::RGBA(RGBA { red: 1f32, green: 0f32, blue: 0f32, alpha: 1f32 }),
                 authored: None
-            });
+            }));
 
             let position_x = single_vec_value_typedef!(position_x,
                 HorizontalPosition {
                     keyword: None,
                     position: Some(LengthOrPercentage::Length(NoCalcLength::from_px(7f32))),
                 }
             );
 
@@ -785,20 +785,20 @@ mod shorthand_serialization {
                 scroll 7px 4px / 70px 50px border-box padding-box;"
             );
         }
 
         #[test]
         fn background_should_combine_origin_and_clip_properties_when_equal() {
             let mut properties = Vec::new();
 
-            let color = DeclaredValue::Value(CSSColor {
+            let color = DeclaredValue::Value(Box::new(CSSColor {
                 parsed: ComputedColor::RGBA(RGBA { red: 1f32, green: 0f32, blue: 0f32, alpha: 1f32 }),
                 authored: None
-            });
+            }));
 
             let position_x = single_vec_value_typedef!(position_x,
                 HorizontalPosition {
                     keyword: None,
                     position: Some(LengthOrPercentage::Length(NoCalcLength::from_px(7f32))),
                 }
             );
 
@@ -844,20 +844,20 @@ mod shorthand_serialization {
                 scroll 7px 4px / 70px 50px padding-box;"
             );
         }
 
         #[test]
         fn background_should_always_print_color_and_url_and_repeat_and_attachment_and_position() {
             let mut properties = Vec::new();
 
-            let color = DeclaredValue::Value(CSSColor {
+            let color = DeclaredValue::Value(Box::new(CSSColor {
                 parsed: ComputedColor::RGBA(RGBA { red: 1f32, green: 0f32, blue: 0f32, alpha: 1f32 }),
                 authored: None
-            });
+            }));
 
             let position_x = single_vec_value_typedef!(position_x,
                 HorizontalPosition {
                     keyword: None,
                     position: Some(LengthOrPercentage::Length(NoCalcLength::from_px(0f32))),
                 }
             );
 
new file mode 100644
--- /dev/null
+++ b/servo/tests/unit/style/size_of.rs
@@ -0,0 +1,44 @@
+/* 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 std::mem::size_of;
+use style::properties::{PropertyDeclaration, specified_value_sizes};
+
+#[test]
+fn size_of_property_declaration() {
+    let old = 48;
+    let new = size_of::<PropertyDeclaration>();
+    if new < old {
+        panic!("Your changes have decreased the stack size of PropertyDeclaration enum from {} to {}. \
+                Good work! Please update the size in tests/unit/style/size_of.rs.",
+                old, new)
+    } else if new > old {
+        panic!("Your changes have increased the stack size of PropertyDeclaration enum from {} to {}. \
+                These enum is present in large quantities in the style, and increasing the size \
+                may dramatically affect our memory footprint. Please consider using `boxed=\"True\"` in \
+                the longhand If you feel that the increase is necessary, update to the new size in \
+                tests/unit/style/size_of.rs.",
+                old, new)
+    }
+}
+
+#[test]
+fn size_of_specified_values() {
+    let threshold = 40;
+    let longhands = specified_value_sizes();
+
+    for specified_value in longhands {
+        if specified_value.1 >= threshold && !specified_value.2 {
+            panic!("Your changes have increased the size of {} SpecifiedValue to {}. The threshold is \
+                    currently {}. SpecifiedValues are affect size of PropertyDeclaration enum and \
+                    increasing the size may dramatically affect our memory footprint. Please consider \
+                    using `boxed=\"True\"` in this longhand.",
+                    specified_value.0, specified_value.1, threshold)
+        } else if specified_value.1 < threshold && specified_value.2 {
+            panic!("Your changes have decreased the size of {} SpecifiedValue to {}. Good work! \
+                    The threshold is currently {}. Please consider removing `boxed=\"True\"` from this longhand.",
+                    specified_value.0, specified_value.1, threshold)
+        }
+    }
+}
--- a/servo/tests/unit/style/stylesheets.rs
+++ b/servo/tests/unit/style/stylesheets.rs
@@ -178,22 +178,22 @@ fn test_parse_stylesheet() {
                         }),
                         pseudo_element: None,
                         specificity: (1 << 20) + (1 << 10) + (0 << 0),
                     },
                 ]),
                 block: Arc::new(RwLock::new(PropertyDeclarationBlock {
                     declarations: vec![
                         (PropertyDeclaration::BackgroundColor(DeclaredValue::Value(
-                            longhands::background_color::SpecifiedValue {
+                            Box::new(longhands::background_color::SpecifiedValue {
                                 authored: Some("blue".to_owned()),
                                 parsed: cssparser::Color::RGBA(cssparser::RGBA {
                                     red: 0., green: 0., blue: 1., alpha: 1.
                                 }),
-                            }
+                            })
                          )),
                          Importance::Normal),
                         (PropertyDeclaration::BackgroundPositionX(DeclaredValue::Value(
                             longhands::background_position_x::SpecifiedValue(
                             vec![longhands::background_position_x::single_value
                                                        ::get_initial_position_value()]))),
                         Importance::Normal),
                         (PropertyDeclaration::BackgroundPositionY(DeclaredValue::Value(