servo: Merge #15779 - Don't serialize shorthand if some but not all its longhands have CSS-wide keyword (from upsuper:shorthand); r=emilio
authorXidorn Quan <me@upsuper.org>
Wed, 01 Mar 2017 16:12:36 -0800
changeset 345488 1a3de0af9d95b3f3b8b0711c718fd175d8d7c83a
parent 345487 6846c1427f28a9baf6498ce06d424c87c2a8fc1f
child 345489 ab41a61ca37b4dd9ddfc234b1b99460c0fe92fb3
push id31437
push usercbook@mozilla.com
push dateThu, 02 Mar 2017 13:00:04 +0000
treeherdermozilla-central@180a160ae22a [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersemilio
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 #15779 - Don't serialize shorthand if some but not all its longhands have CSS-wide keyword (from upsuper:shorthand); r=emilio This also changes `LonghandsToSerialize` to store references to specified value directly rather than declared value, which significantly simplify many serialization code. Source-Repo: https://github.com/servo/servo Source-Revision: c87524c8888d9c4a1f1eaedb5e3a29886f627697
servo/components/style/custom_properties.rs
servo/components/style/properties/helpers.mako.rs
servo/components/style/properties/helpers/animated_properties.mako.rs
servo/components/style/properties/properties.mako.rs
servo/components/style/properties/shorthand/background.mako.rs
servo/components/style/properties/shorthand/border.mako.rs
servo/components/style/properties/shorthand/box.mako.rs
servo/components/style/properties/shorthand/column.mako.rs
servo/components/style/properties/shorthand/font.mako.rs
servo/components/style/properties/shorthand/inherited_svg.mako.rs
servo/components/style/properties/shorthand/inherited_text.mako.rs
servo/components/style/properties/shorthand/list.mako.rs
servo/components/style/properties/shorthand/mask.mako.rs
servo/components/style/properties/shorthand/outline.mako.rs
servo/components/style/properties/shorthand/position.mako.rs
servo/components/style/properties/shorthand/serialize.mako.rs
servo/components/style/properties/shorthand/text.mako.rs
servo/tests/unit/style/properties/serialization.rs
servo/tests/unit/style/stylesheets.rs
--- a/servo/components/style/custom_properties.rs
+++ b/servo/components/style/custom_properties.rs
@@ -4,17 +4,17 @@
 
 //! 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::ParserContext;
-use properties::DeclaredValue;
+use properties::{CSSWideKeyword, 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;
 
 /// A custom property name is just an `Atom`.
@@ -357,21 +357,23 @@ pub fn cascade<'a>(custom_properties: &m
             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::Initial => {
-            map.remove(&name);
+        DeclaredValue::CSSWideKeyword(keyword) => match keyword {
+            CSSWideKeyword::Initial => {
+                map.remove(&name);
+            }
+            CSSWideKeyword::Unset | // Custom properties are inherited by default.
+            CSSWideKeyword::Inherit => {} // The inherited value is what we already have.
         }
-        DeclaredValue::Unset | // Custom properties are inherited by default.
-        DeclaredValue::Inherit => {}  // The inherited value is what we already have.
     }
 }
 
 /// Returns the final map of applicable custom properties.
 ///
 /// If there was any specified property, we've created a new map and now we need
 /// to remove any potential cycles, and wrap it in an arc.
 ///
--- a/servo/components/style/properties/helpers.mako.rs
+++ b/servo/components/style/properties/helpers.mako.rs
@@ -65,17 +65,17 @@
                     let &SpecifiedValue(ref vec) = self;
                     vec.iter().any(|ref x| x.has_viewport_percentage())
                 }
             }
 
             pub mod single_value {
                 use cssparser::Parser;
                 use parser::{Parse, ParserContext, ParserContextExtraData};
-                use properties::{CSSWideKeyword, DeclaredValue, ShorthandId};
+                use properties::{DeclaredValue, ShorthandId};
                 use values::computed::{Context, ToComputedValue};
                 use values::{computed, specified};
                 use values::{Auto, Either, None_, Normal};
                 ${caller.body()}
             }
 
             /// The definition of the computed value for ${name}.
             pub mod computed_value {
@@ -205,24 +205,24 @@
             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, UnparsedValue, ShorthandId};
+            use properties::{DeclaredValue, UnparsedValue, ShorthandId};
         % endif
         use values::{Auto, Either, None_, Normal};
         use cascade_info::CascadeInfo;
         use error_reporting::ParseErrorReporter;
         use properties::longhands;
         use properties::LonghandIdSet;
-        use properties::{ComputedValues, PropertyDeclaration};
+        use properties::{CSSWideKeyword, ComputedValues, PropertyDeclaration};
         use properties::style_structs;
         use std::boxed::Box as StdBox;
         use std::collections::HashMap;
         use std::sync::Arc;
         use values::computed::{Context, ToComputedValue};
         use values::{computed, specified};
         use Atom;
         ${caller.body()}
@@ -262,40 +262,42 @@
                                 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!(),
-                            % 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}();
-                                context.mutate_style().mutate_${data.current_style_struct.name_lower}()
-                                                      .copy_${property.ident}_from(initial_struct ${maybe_wm});
-                            },
-                            % if data.current_style_struct.inherited:
-                            DeclaredValue::Unset |
-                            % endif
-                            DeclaredValue::Inherit => {
-                                // This is a bit slow, but this is rare so it shouldn't
-                                // matter.
-                                //
-                                // FIXME: is it still?
-                                *cacheable = false;
-                                let inherited_struct =
-                                    inherited_style.get_${data.current_style_struct.name_lower}();
-                                context.mutate_style().mutate_${data.current_style_struct.name_lower}()
-                                       .copy_${property.ident}_from(inherited_struct ${maybe_wm});
+                            DeclaredValue::CSSWideKeyword(keyword) => match keyword {
+                                % if not data.current_style_struct.inherited:
+                                CSSWideKeyword::Unset |
+                                % endif
+                                CSSWideKeyword::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}();
+                                    context.mutate_style().mutate_${data.current_style_struct.name_lower}()
+                                                        .copy_${property.ident}_from(initial_struct ${maybe_wm});
+                                },
+                                % if data.current_style_struct.inherited:
+                                CSSWideKeyword::Unset |
+                                % endif
+                                CSSWideKeyword::Inherit => {
+                                    // This is a bit slow, but this is rare so it shouldn't
+                                    // matter.
+                                    //
+                                    // FIXME: is it still?
+                                    *cacheable = false;
+                                    let inherited_struct =
+                                        inherited_style.get_${data.current_style_struct.name_lower}();
+                                    context.mutate_style().mutate_${data.current_style_struct.name_lower}()
+                                        .copy_${property.ident}_from(inherited_struct ${maybe_wm});
+                                }
                             }
                         }
                     }, error_reporter);
                 }
 
                 % if property.custom_cascade:
                     cascade_property_custom(declaration,
                                             inherited_style,
@@ -319,19 +321,17 @@
             }
             pub fn parse_declared(context: &ParserContext, input: &mut Parser)
                                % 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),
+                    Ok(keyword) => Ok(DeclaredValue::CSSWideKeyword(keyword)),
                     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();
@@ -478,39 +478,38 @@
                                        **kwargs)
 %>
     % if shorthand:
     /// ${shorthand.spec}
     pub mod ${shorthand.ident} {
         #[allow(unused_imports)]
         use cssparser::Parser;
         use parser::ParserContext;
-        use properties::{DeclaredValue, PropertyDeclaration, UnparsedValue};
-        use properties::{ShorthandId, longhands};
+        use properties::{DeclaredValue, PropertyDeclaration};
+        use properties::{ShorthandId, UnparsedValue, 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}: 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:
                 % if sub_property.boxed:
                     pub ${sub_property.ident}:
-                        &'a DeclaredValue<Box<longhands::${sub_property.ident}::SpecifiedValue>>,
+                        &'a Box<longhands::${sub_property.ident}::SpecifiedValue>,
                 % else:
                     pub ${sub_property.ident}:
-                        &'a DeclaredValue<longhands::${sub_property.ident}::SpecifiedValue>,
+                        &'a 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, ()>
@@ -520,17 +519,17 @@
                 % for sub_property in shorthand.sub_properties:
                     let mut ${sub_property.ident} = None;
                 % endfor
 
                 // Attempt to assign the incoming declarations to the expected variables
                 for longhand in iter {
                     match *longhand {
                         % for sub_property in shorthand.sub_properties:
-                            PropertyDeclaration::${sub_property.camel_case}(ref value) => {
+                            PropertyDeclaration::${sub_property.camel_case}(DeclaredValue::Value(ref value)) => {
                                 ${sub_property.ident} = Some(value)
                             },
                         % endfor
                         _ => {}
                     };
                 }
 
                 // If any of the expected variables are missing, return an error
@@ -550,50 +549,16 @@
                             ${sub_property.ident}: ${sub_property.ident},
                         % endfor
                     }),
                     _ => Err(())
                 }
             }
         }
 
-        impl<'a> ToCss for LonghandsToSerialize<'a> {
-            fn to_css<W>(&self, dest: &mut W) -> fmt::Result
-                where W: fmt::Write,
-            {
-                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::Value(..) => {
-                            all_flags = SerializeFlags::empty();
-                        }
-                    }
-                % endfor
-
-                if with_variables {
-                    // We don't serialize shorthands with variables
-                    dest.write_str("")
-                } else if all_flags == ALL_INHERIT {
-                    dest.write_str("inherit")
-                } else if all_flags == ALL_INITIAL {
-                    dest.write_str("initial")
-                } else if all_flags == ALL_UNSET {
-                    dest.write_str("unset")
-                } else {
-                    self.to_css_declared(dest)
-                }
-            }
-        }
-
-
         /// Parse the given shorthand and fill the result into the
         /// `declarations` vector.
         pub fn parse(context: &ParserContext,
                      input: &mut Parser,
                      declarations: &mut Vec<(PropertyDeclaration, Importance)>)
                      -> Result<(), ()> {
             input.look_for_var_functions();
             let start = input.position();
@@ -656,18 +621,18 @@
             % endif
             Ok(Longhands {
                 % for side in ["top", "right", "bottom", "left"]:
                     ${to_rust_ident(sub_property_pattern % side)}: ${side},
                 % endfor
             })
         }
 
-        impl<'a> LonghandsToSerialize<'a> {
-            fn to_css_declared<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
+        impl<'a> ToCss for LonghandsToSerialize<'a> {
+            fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
                 super::serialize_four_sides(
                     dest,
                     self.${to_rust_ident(sub_property_pattern % 'top')},
                     self.${to_rust_ident(sub_property_pattern % 'right')},
                     self.${to_rust_ident(sub_property_pattern % 'bottom')},
                     self.${to_rust_ident(sub_property_pattern % 'left')}
                 )
             }
--- a/servo/components/style/properties/helpers/animated_properties.mako.rs
+++ b/servo/components/style/properties/helpers/animated_properties.mako.rs
@@ -3,17 +3,17 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 <%namespace name="helpers" file="/helpers.mako.rs" />
 
 use app_units::Au;
 use cssparser::{Color as CSSParserColor, Parser, RGBA};
 use euclid::{Point2D, Size2D};
 #[cfg(feature = "gecko")] use gecko_bindings::structs::nsCSSPropertyID;
-use properties::{DeclaredValue, PropertyDeclaration};
+use properties::{CSSWideKeyword, DeclaredValue, PropertyDeclaration};
 use properties::longhands;
 use properties::longhands::background_size::computed_value::T as BackgroundSize;
 use properties::longhands::font_weight::computed_value::T as FontWeight;
 use properties::longhands::line_height::computed_value::T as LineHeight;
 use properties::longhands::text_shadow::computed_value::T as TextShadowList;
 use properties::longhands::text_shadow::computed_value::TextShadow;
 use properties::longhands::box_shadow::computed_value::T as BoxShadowList;
 use properties::longhands::box_shadow::single_value::computed_value::T as BoxShadow;
@@ -282,31 +282,33 @@ impl AnimationValue {
         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::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}()
-                            },
-                            % if prop.style_struct.inherited:
-                                DeclaredValue::Unset |
-                            % endif
-                            DeclaredValue::Inherit => {
-                                let inherit_struct = context.inherited_style
-                                                            .get_${prop.style_struct.name_lower}();
-                                inherit_struct.clone_${prop.ident}()
-                            },
+                            DeclaredValue::CSSWideKeyword(keyword) => match keyword {
+                                % if not prop.style_struct.inherited:
+                                    CSSWideKeyword::Unset |
+                                % endif
+                                CSSWideKeyword::Initial => {
+                                    let initial_struct = initial.get_${prop.style_struct.name_lower}();
+                                    initial_struct.clone_${prop.ident}()
+                                },
+                                % if prop.style_struct.inherited:
+                                    CSSWideKeyword::Unset |
+                                % endif
+                                CSSWideKeyword::Inherit => {
+                                    let inherit_struct = context.inherited_style
+                                                                .get_${prop.style_struct.name_lower}();
+                                    inherit_struct.clone_${prop.ident}()
+                                },
+                            }
                         };
                         Some(AnimationValue::${prop.camel_case}(computed))
                     }
                 % endif
             % endfor
             _ => None // non animatable properties will get included because of shorthands. ignore.
         }
     }
--- a/servo/components/style/properties/properties.mako.rs
+++ b/servo/components/style/properties/properties.mako.rs
@@ -97,24 +97,16 @@ macro_rules! unwrap_or_initial {
 /// A module with code for all the shorthand css properties, and a few
 /// serialization helpers.
 #[allow(missing_docs)]
 pub mod shorthands {
     use cssparser::Parser;
     use parser::{Parse, ParserContext};
     use values::specified;
 
-    bitflags! {
-        flags SerializeFlags: u8 {
-            const ALL_INHERIT = 0b001,
-            const ALL_INITIAL = 0b010,
-            const ALL_UNSET   = 0b100,
-        }
-    }
-
     /// Parses a property for four different sides per CSS syntax.
     ///
     ///  * Zero or more than four values is invalid.
     ///  * One value sets them all
     ///  * Two values set (top, bottom) and (left, right)
     ///  * Three values set top, (left, right) and bottom
     ///  * Four values set them in order
     ///
@@ -360,42 +352,65 @@ impl PropertyDeclarationIdSet {
                                 % endif
                             % endfor
                             _ => unreachable!()
                         }
                     })
                 })
                 .unwrap_or(
                     // Invalid at computed-value time.
-                    DeclaredValue::${"Inherit" if property.style_struct.inherited else "Initial"}
+                    DeclaredValue::CSSWideKeyword(
+                        % if property.style_struct.inherited:
+                            CSSWideKeyword::Inherit
+                        % else:
+                            CSSWideKeyword::Initial
+                        % endif
+                    )
                 )
             );
         }
     % endif
 % endfor
 
 /// An enum to represent a CSS Wide keyword.
 #[derive(Copy, Clone, PartialEq, Eq, Debug)]
+#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
 pub enum CSSWideKeyword {
     /// The `initial` keyword.
-    InitialKeyword,
+    Initial,
     /// The `inherit` keyword.
-    InheritKeyword,
+    Inherit,
     /// The `unset` keyword.
-    UnsetKeyword,
+    Unset,
+}
+
+impl CSSWideKeyword {
+    fn to_str(&self) -> &'static str {
+        match *self {
+            CSSWideKeyword::Initial => "initial",
+            CSSWideKeyword::Inherit => "inherit",
+            CSSWideKeyword::Unset => "unset",
+        }
+    }
+}
+
+impl ToCss for CSSWideKeyword {
+    fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
+        dest.write_str(self.to_str())
+    }
 }
 
 impl Parse for CSSWideKeyword {
     fn parse(_context: &ParserContext, input: &mut Parser) -> Result<Self, ()> {
         let ident = input.expect_ident()?;
         input.expect_exhausted()?;
         match_ignore_ascii_case! { &ident,
-            "initial" => Ok(CSSWideKeyword::InitialKeyword),
-            "inherit" => Ok(CSSWideKeyword::InheritKeyword),
-            "unset" => Ok(CSSWideKeyword::UnsetKeyword),
+            "initial" => Ok(CSSWideKeyword::Initial),
+            "inherit" => Ok(CSSWideKeyword::Inherit),
+            "unset" => Ok(CSSWideKeyword::Unset),
             _ => Err(())
         }
     }
 }
 
 /// An identifier for a given longhand property.
 #[derive(Clone, Copy, Eq, PartialEq, Debug)]
 #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
@@ -523,39 +538,44 @@ impl ShorthandId {
         // https://drafts.csswg.org/css-variables/#variables-in-shorthands
         if let Some(css) = first_declaration.with_variables_from_shorthand(self) {
             if declarations2.all(|d| d.with_variables_from_shorthand(self) == Some(css)) {
                return Some(AppendableValue::Css(css));
            }
            return None;
         }
 
-        if !declarations3.any(|d| d.with_variables()) {
+        // Check whether they are all the same CSS-wide keyword.
+        if let Some(keyword) = first_declaration.get_css_wide_keyword() {
+            if declarations2.all(|d| d.get_css_wide_keyword() == Some(keyword)) {
+                return Some(AppendableValue::Css(keyword.to_str()));
+            }
+            return None;
+        }
+
+        // Check whether all declarations can be serialized as part of shorthand.
+        if declarations3.all(|d| d.may_serialize_as_part_of_shorthand()) {
             return Some(AppendableValue::DeclarationsForShorthand(self, declarations));
         }
 
         None
     }
 }
 
 /// 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),
     /// An unparsed value that contains `var()` functions.
     WithVariables(Box<UnparsedValue>),
-    /// The `initial` keyword.
-    Initial,
-    /// The `inherit` keyword.
-    Inherit,
-    /// The `unset` keyword.
-    Unset,
+    /// An CSS-wide keyword.
+    CSSWideKeyword(CSSWideKeyword),
 }
 
 /// 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,
@@ -570,19 +590,17 @@ pub struct UnparsedValue {
 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(_) => {
                 panic!("DeclaredValue::has_viewport_percentage without \
                         resolving variables!")
             },
-            DeclaredValue::Initial |
-            DeclaredValue::Inherit |
-            DeclaredValue::Unset => false,
+            DeclaredValue::CSSWideKeyword(_) => false,
         }
     }
 }
 
 impl<T: ToCss> ToCss for DeclaredValue<T> {
     fn to_css<W>(&self, dest: &mut W) -> fmt::Result
         where W: fmt::Write,
     {
@@ -590,19 +608,17 @@ impl<T: ToCss> ToCss for DeclaredValue<T
             DeclaredValue::Value(ref inner) => inner.to_css(dest),
             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"),
+            DeclaredValue::CSSWideKeyword(ref keyword) => keyword.to_css(dest),
         }
     }
 }
 
 /// An identifier for a given property declaration, which can be either a
 /// longhand or a custom property.
 #[derive(PartialEq, Clone, Copy)]
 #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
@@ -913,30 +929,54 @@ impl PropertyDeclaration {
                     }
                     _ => 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 {
+    /// Returns a CSS-wide keyword if the declaration's value is one.
+    pub fn get_css_wide_keyword(&self) -> Option<CSSWideKeyword> {
         match *self {
             % for property in data.longhands:
                 PropertyDeclaration::${property.camel_case}(ref value) => match *value {
-                    DeclaredValue::WithVariables(_) => true,
+                    DeclaredValue::CSSWideKeyword(keyword) => Some(keyword),
+                    _ => None,
+                },
+            % endfor
+            PropertyDeclaration::Custom(_, ref value) => match *value {
+                DeclaredValue::CSSWideKeyword(keyword) => Some(keyword),
+                _ => None,
+            }
+        }
+    }
+
+    /// Returns whether the declaration may be serialized as part of a shorthand.
+    ///
+    /// This method returns false if this declaration contains variable or has a
+    /// CSS-wide keyword value, since these values cannot be serialized as part
+    /// of a shorthand.
+    ///
+    /// Caller should check `with_variables_from_shorthand()` and whether all
+    /// needed declarations has the same CSS-wide keyword first.
+    ///
+    /// Note that, serialization of a shorthand may still fail because of other
+    /// property-specific requirement even when this method returns true for all
+    /// the longhand declarations.
+    pub fn may_serialize_as_part_of_shorthand(&self) -> bool {
+        match *self {
+            % for property in data.longhands:
+                PropertyDeclaration::${property.camel_case}(ref value) => match *value {
+                    DeclaredValue::Value(_) => true,
                     _ => false,
                 },
             % endfor
-            PropertyDeclaration::Custom(_, ref value) => match *value {
-                DeclaredValue::WithVariables(_) => true,
-                _ => false,
-            }
+            PropertyDeclaration::Custom(..) =>
+                unreachable!("Serialize a custom property as part of shorthand?"),
         }
     }
 
     /// 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
@@ -964,19 +1004,17 @@ impl PropertyDeclaration {
     /// we only set them here so that we don't have to reallocate
     pub fn parse(id: PropertyId, context: &ParserContext, input: &mut Parser,
                  result_list: &mut Vec<(PropertyDeclaration, Importance)>,
                  in_keyframe_block: bool)
                  -> PropertyDeclarationParseResult {
         match id {
             PropertyId::Custom(name) => {
                 let value = match input.try(|i| CSSWideKeyword::parse(context, i)) {
-                    Ok(CSSWideKeyword::UnsetKeyword) => DeclaredValue::Unset,
-                    Ok(CSSWideKeyword::InheritKeyword) => DeclaredValue::Inherit,
-                    Ok(CSSWideKeyword::InitialKeyword) => DeclaredValue::Initial,
+                    Ok(keyword) => DeclaredValue::CSSWideKeyword(keyword),
                     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));
                 return PropertyDeclarationParseResult::ValidOrIgnoredDeclaration;
@@ -1024,36 +1062,21 @@ impl PropertyDeclaration {
                         if context.stylesheet_origin != Origin::UserAgent {
                             return PropertyDeclarationParseResult::UnknownProperty
                         }
                     % endif
 
                     ${property_pref_check(shorthand)}
 
                     match input.try(|i| CSSWideKeyword::parse(context, i)) {
-                        Ok(CSSWideKeyword::InheritKeyword) => {
+                        Ok(keyword) => {
                             % for sub_property in shorthand.sub_properties:
                                 result_list.push((
                                     PropertyDeclaration::${sub_property.camel_case}(
-                                        DeclaredValue::Inherit), Importance::Normal));
-                            % endfor
-                            PropertyDeclarationParseResult::ValidOrIgnoredDeclaration
-                        },
-                        Ok(CSSWideKeyword::InitialKeyword) => {
-                            % for sub_property in shorthand.sub_properties:
-                                result_list.push((
-                                    PropertyDeclaration::${sub_property.camel_case}(
-                                        DeclaredValue::Initial), Importance::Normal));
-                            % endfor
-                            PropertyDeclarationParseResult::ValidOrIgnoredDeclaration
-                        },
-                        Ok(CSSWideKeyword::UnsetKeyword) => {
-                            % for sub_property in shorthand.sub_properties:
-                                result_list.push((PropertyDeclaration::${sub_property.camel_case}(
-                                        DeclaredValue::Unset), Importance::Normal));
+                                        DeclaredValue::CSSWideKeyword(keyword)), Importance::Normal));
                             % endfor
                             PropertyDeclarationParseResult::ValidOrIgnoredDeclaration
                         },
                         Err(()) => match shorthands::${shorthand.ident}::parse(context, input, result_list) {
                             Ok(()) => PropertyDeclarationParseResult::ValidOrIgnoredDeclaration,
                             Err(()) => PropertyDeclarationParseResult::InvalidValue,
                         }
                     }
--- a/servo/components/style/properties/shorthand/background.mako.rs
+++ b/servo/components/style/properties/shorthand/background.mako.rs
@@ -122,73 +122,46 @@
              background_repeat: background_repeat,
              background_attachment: background_attachment,
              background_size: background_size,
              background_origin: background_origin,
              background_clip: background_clip,
          })
     }
 
-    impl<'a> LonghandsToSerialize<'a>  {
-        fn to_css_declared<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
-            // mako doesn't like ampersands following `<`
-            fn extract_value<T>(x: &DeclaredValue<T>) -> Option< &T> {
-                match *x {
-                    DeclaredValue::Value(ref val) => Some(val),
-                    _ => None,
-                }
-            }
-
-            let len = extract_value(self.background_image).map(|i| i.0.len()).unwrap_or(0);
+    impl<'a> ToCss for LonghandsToSerialize<'a>  {
+        fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
+            let len = self.background_image.0.len();
             // There should be at least one declared value
             if len == 0 {
-                return dest.write_str("")
+                return Ok(());
             }
 
             // If a value list length is differs then we don't do a shorthand serialization.
             // The exceptions to this is color which appears once only and is serialized
             // with the last item.
             % for name in "image position_x position_y size repeat origin clip attachment".split():
-                if len != extract_value(self.background_${name}).map(|i| i.0.len()).unwrap_or(0) {
-                    return dest.write_str("")
+                if len != self.background_${name}.0.len() {
+                    return Ok(());
                 }
             % endfor
 
-            let mut first = true;
             for i in 0..len {
                 % for name in "image position_x position_y repeat size attachment origin clip".split():
-                    let ${name} = if let DeclaredValue::Value(ref arr) = *self.background_${name} {
-                        &arr.0[i]
-                    } else {
-                        unreachable!()
-                    };
+                    let ${name} = &self.background_${name}.0[i];
                 % endfor
 
-                let color = if i == len - 1 {
-                    Some(self.background_color)
-                } else {
-                    None
-                };
-
-                if first {
-                    first = false;
-                } else {
+                if i != 0 {
                     try!(write!(dest, ", "));
                 }
-                match color {
-                    Some(&DeclaredValue::Value(ref color)) => {
-                        try!(color.to_css(dest));
-                        try!(write!(dest, " "));
-                    },
-                    Some(_) => {
-                        try!(write!(dest, "transparent "));
-                    }
-                    // Not yet the last one
-                    None => ()
-                };
+
+                if i == len - 1 {
+                    try!(self.background_color.to_css(dest));
+                    try!(write!(dest, " "));
+                }
 
                 % for name in "image repeat attachment position_x position_y".split():
                     try!(${name}.to_css(dest));
                     try!(write!(dest, " "));
                 % endfor
 
                 try!(write!(dest, "/ "));
                 try!(size.to_css(dest));
@@ -246,55 +219,23 @@
         }
 
         Ok(Longhands {
             background_position_x: position_x,
             background_position_y: position_y,
         })
     }
 
-    impl<'a> LonghandsToSerialize<'a>  {
-        fn to_css_declared<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
-            // mako doesn't like ampersands following `<`
-            fn extract_value<T>(x: &DeclaredValue<T>) -> Option< &T> {
-                match *x {
-                    DeclaredValue::Value(ref val) => Some(val),
-                    _ => None,
-                }
-            }
-            use std::cmp;
-            let mut len = 0;
-            % for name in "x y".split():
-                len = cmp::max(len, extract_value(self.background_position_${name})
-                                                      .map(|i| i.0.len())
-                                                      .unwrap_or(0));
-            % endfor
-
-            // There should be at least one declared value
-            if len == 0 {
-                return dest.write_str("")
+    impl<'a> ToCss for LonghandsToSerialize<'a>  {
+        fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
+            let len = self.background_position_x.0.len();
+            if len == 0 || len != self.background_position_y.0.len() {
+                return Ok(());
             }
-
             for i in 0..len {
-                % for name in "x y".split():
-                    let position_${name} = if let DeclaredValue::Value(ref arr) =
-                                           *self.background_position_${name} {
-                        arr.0.get(i % arr.0.len())
-                    } else {
-                        None
-                    };
-                % endfor
-
-                try!(position_x.unwrap_or(&background_position_x::single_value
-                                                                ::get_initial_position_value())
-                               .to_css(dest));
-
-                try!(write!(dest, " "));
-
-                try!(position_y.unwrap_or(&background_position_y::single_value
-                                                                ::get_initial_position_value())
-                               .to_css(dest));
+                self.background_position_x.0[i].to_css(dest)?;
+                dest.write_str(" ")?;
+                self.background_position_y.0[i].to_css(dest)?;
             }
-
             Ok(())
         }
     }
 </%helpers:shorthand>
--- a/servo/components/style/properties/shorthand/border.mako.rs
+++ b/servo/components/style/properties/shorthand/border.mako.rs
@@ -24,18 +24,18 @@
         let (top, right, bottom, left) = try!(parse_four_sides(input, |i| specified::BorderWidth::parse(context, i)));
         Ok(Longhands {
             % for side in ["top", "right", "bottom", "left"]:
                 ${to_rust_ident('border-%s-width' % side)}: ${side},
             % endfor
         })
     }
 
-    impl<'a> LonghandsToSerialize<'a>  {
-        fn to_css_declared<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
+    impl<'a> ToCss for LonghandsToSerialize<'a>  {
+        fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
             % for side in ["top", "right", "bottom", "left"]:
                 let ${side} = self.border_${side}_width.clone();
             % endfor
 
             super::serialize_four_sides(dest, &top, &right, &bottom, &left)
         }
     }
 </%helpers:shorthand>
@@ -103,18 +103,18 @@ pub fn parse_border(context: &ParserCont
         let (color, style, width) = try!(super::parse_border(context, input));
         Ok(Longhands {
             border_${to_rust_ident(side)}_color: color,
             border_${to_rust_ident(side)}_style: style,
             border_${to_rust_ident(side)}_width: width
         })
     }
 
-    impl<'a> LonghandsToSerialize<'a>  {
-        fn to_css_declared<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
+    impl<'a> ToCss for LonghandsToSerialize<'a>  {
+        fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
             super::serialize_directional_border(
                 dest,
                 self.border_${to_rust_ident(side)}_width,
                 self.border_${to_rust_ident(side)}_style,
                 self.border_${to_rust_ident(side)}_color
             )
         }
     }
@@ -134,18 +134,18 @@ pub fn parse_border(context: &ParserCont
             % for side in ["top", "right", "bottom", "left"]:
                 border_${side}_color: color.clone(),
                 border_${side}_style: style,
                 border_${side}_width: width.clone(),
             % endfor
         })
     }
 
-    impl<'a> LonghandsToSerialize<'a>  {
-        fn to_css_declared<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
+    impl<'a> ToCss for LonghandsToSerialize<'a>  {
+        fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
             let all_equal = {
                 % for side in PHYSICAL_SIDES:
                   let border_${side}_width = self.border_${side}_width;
                   let border_${side}_style = self.border_${side}_style;
                   let border_${side}_color = self.border_${side}_color;
                 % endfor
 
                 border_top_width == border_right_width &&
@@ -193,18 +193,18 @@ pub fn parse_border(context: &ParserCont
             border_bottom_right_radius: radii.bottom_right,
             border_bottom_left_radius: radii.bottom_left,
         })
     }
 
     // TODO: I do not understand how border radius works with respect to the slashes /,
     // so putting a default generic impl for now
     // https://developer.mozilla.org/en-US/docs/Web/CSS/border-radius
-    impl<'a> LonghandsToSerialize<'a>  {
-        fn to_css_declared<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
+    impl<'a> ToCss for LonghandsToSerialize<'a>  {
+        fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
             try!(self.border_top_left_radius.to_css(dest));
             try!(write!(dest, " "));
 
             try!(self.border_top_right_radius.to_css(dest));
             try!(write!(dest, " "));
 
             try!(self.border_bottom_right_radius.to_css(dest));
             try!(write!(dest, " "));
@@ -284,60 +284,22 @@ pub fn parse_border(context: &ParserCont
 
         Ok(Longhands {
             % for name in "outset repeat slice source width".split():
                 border_image_${name}: border_image_${name},
             % endfor
          })
     }
 
-    impl<'a> LonghandsToSerialize<'a>  {
-        fn to_css_declared<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
-            % for name in "outset repeat slice source width".split():
-                let ${name} = if let DeclaredValue::Value(ref value) = *self.border_image_${name} {
-                    Some(value)
-                } else {
-                    None
-                };
-            % endfor
-
-            if let Some(source) = source {
-                try!(source.to_css(dest));
-            } else {
-                try!(write!(dest, "none"));
-            }
-
-            try!(write!(dest, " "));
-
-            if let Some(slice) = slice {
-                try!(slice.to_css(dest));
-            } else {
-                try!(write!(dest, "100%"));
-            }
-
-            try!(write!(dest, " / "));
-
-            if let Some(width) = width {
-                try!(width.to_css(dest));
-            } else {
-                try!(write!(dest, "1"));
-            }
-
-            try!(write!(dest, " / "));
-
-            if let Some(outset) = outset {
-                try!(outset.to_css(dest));
-            } else {
-                try!(write!(dest, "0"));
-            }
-
-            try!(write!(dest, " "));
-
-            if let Some(repeat) = repeat {
-                try!(repeat.to_css(dest));
-            } else {
-                try!(write!(dest, "stretch"));
-            }
-
-            Ok(())
+    impl<'a> ToCss for LonghandsToSerialize<'a>  {
+        fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
+            self.border_image_source.to_css(dest)?;
+            dest.write_str(" ")?;
+            self.border_image_slice.to_css(dest)?;
+            dest.write_str(" / ")?;
+            self.border_image_width.to_css(dest)?;
+            dest.write_str(" / ")?;
+            self.border_image_outset.to_css(dest)?;
+            dest.write_str(" ")?;
+            self.border_image_repeat.to_css(dest)
         }
     }
 </%helpers:shorthand>
--- a/servo/components/style/properties/shorthand/box.mako.rs
+++ b/servo/components/style/properties/shorthand/box.mako.rs
@@ -11,33 +11,23 @@
     pub fn parse_value(context: &ParserContext, input: &mut Parser) -> Result<Longhands, ()> {
         let overflow = try!(overflow_x::parse(context, input));
         Ok(Longhands {
             overflow_x: overflow,
             overflow_y: overflow_y::SpecifiedValue(overflow),
         })
     }
 
-    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::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));
+    impl<'a> ToCss for LonghandsToSerialize<'a>  {
+        fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
+            if *self.overflow_x == self.overflow_y.0 {
+                self.overflow_x.to_css(dest)
+            } else {
+                Ok(())
             }
-            Ok(())
         }
     }
 </%helpers:shorthand>
 
 macro_rules! try_parse_one {
     ($input: expr, $var: ident, $prop_module: ident) => {
         if $var.is_none() {
             if let Ok(value) = $input.try($prop_module::SingleSpecifiedValue::parse) {
@@ -120,60 +110,40 @@ macro_rules! try_parse_one {
             transition_property: transition_property::SpecifiedValue(properties),
             transition_duration: transition_duration::SpecifiedValue(durations),
             transition_timing_function:
                 transition_timing_function::SpecifiedValue(timing_functions),
             transition_delay: transition_delay::SpecifiedValue(delays),
         })
     }
 
-    impl<'a> LonghandsToSerialize<'a>  {
-        fn to_css_declared<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
-            fn extract_value<T>(x: &DeclaredValue<T>) -> Option< &T> {
-                match *x {
-                    DeclaredValue::Value(ref val) => Some(val),
-                    _ => None,
-                }
-            }
-
-            let len = extract_value(self.transition_property).map(|i| i.0.len()).unwrap_or(0);
+    impl<'a> ToCss for LonghandsToSerialize<'a>  {
+        fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
+            let len = self.transition_property.0.len();
             // There should be at least one declared value
             if len == 0 {
-                return dest.write_str("")
+                return Ok(());
             }
 
             // If any value list length is differs then we don't do a shorthand serialization
             // either.
             % for name in "property duration delay timing_function".split():
-                if len != extract_value(self.transition_${name}).map(|i| i.0.len()).unwrap_or(0) {
-                    return dest.write_str("")
+                if len != self.transition_${name}.0.len() {
+                    return Ok(());
                 }
             % endfor
 
-            let mut first = true;
             for i in 0..len {
-                % for name in "property duration delay timing_function".split():
-                    let ${name} = if let DeclaredValue::Value(ref arr) = *self.transition_${name} {
-                        &arr.0[i]
-                    } else {
-                        unreachable!()
-                    };
-                % endfor
-
-                if first {
-                    first = false;
-                } else {
-                    try!(write!(dest, ", "));
+                if i != 0 {
+                    write!(dest, ", ")?;
                 }
-
-                try!(property.to_css(dest));
-
+                self.transition_property.0[i].to_css(dest)?;
                 % for name in "duration timing_function delay".split():
-                    try!(write!(dest, " "));
-                    try!(${name}.to_css(dest));
+                    dest.write_str(" ")?;
+                    self.transition_${name}.0[i].to_css(dest)?;
                 % endfor
             }
             Ok(())
         }
     }
 </%helpers:shorthand>
 
 <%helpers:shorthand name="animation" extra_prefixes="moz webkit"
@@ -282,61 +252,47 @@ macro_rules! try_parse_one {
             animation_delay: animation_delay::SpecifiedValue(delays),
             animation_iteration_count: animation_iteration_count::SpecifiedValue(iteration_counts),
             animation_direction: animation_direction::SpecifiedValue(directions),
             animation_fill_mode: animation_fill_mode::SpecifiedValue(fill_modes),
             animation_play_state: animation_play_state::SpecifiedValue(play_states),
         })
     }
 
-    impl<'a> LonghandsToSerialize<'a>  {
-        fn to_css_declared<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
-            fn extract_value<T>(x: &DeclaredValue<T>) -> Option< &T> {
-                match *x {
-                    DeclaredValue::Value(ref val) => Some(val),
-                    _ => None,
-                }
+    impl<'a> ToCss for LonghandsToSerialize<'a>  {
+        fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
+            let len = self.animation_name.0.len();
+            // There should be at least one declared value
+            if len == 0 {
+                return Ok(());
             }
 
-            let len = extract_value(self.animation_name).map(|i| i.0.len()).unwrap_or(0);
-            // There should be at least one declared value
-            if len == 0 {
-                return dest.write_str("")
-            }
+            <%
+                subproperties = "duration timing_function delay direction \
+                                 fill_mode iteration_count play_state".split()
+            %>
 
             // If any value list length is differs then we don't do a shorthand serialization
             // either.
-            % for name in "duration timing_function delay direction fill_mode iteration_count play_state".split():
-                if len != extract_value(self.animation_${name}).map(|i| i.0.len()).unwrap_or(0) {
-                    return dest.write_str("")
+            % for name in subproperties:
+                if len != self.animation_${name}.0.len() {
+                    return Ok(())
                 }
             % endfor
 
-            let mut first = true;
             for i in 0..len {
-            % for name in "duration timing_function delay direction fill_mode iteration_count play_state name".split():
-                let ${name} = if let DeclaredValue::Value(ref arr) = *self.animation_${name} {
-                    &arr.0[i]
-                } else {
-                    unreachable!()
-                };
-            % endfor
-
-                if first {
-                    first = false;
-                } else {
+                if i != 0 {
                     try!(write!(dest, ", "));
                 }
 
-            % for name in "duration timing_function delay direction fill_mode iteration_count play_state".split():
-                try!(${name}.to_css(dest));
-                try!(write!(dest, " "));
-            % endfor
-
-                try!(name.to_css(dest));
+                % for name in subproperties:
+                    self.animation_${name}.0[i].to_css(dest)?;
+                    dest.write_str(" ")?;
+                % endfor
+                self.animation_name.0[i].to_css(dest)?;
             }
             Ok(())
         }
     }
 </%helpers:shorthand>
 
 <%helpers:shorthand name="scroll-snap-type" products="gecko"
                     sub_properties="scroll-snap-type-x scroll-snap-type-y"
@@ -346,30 +302,20 @@ macro_rules! try_parse_one {
     pub fn parse_value(context: &ParserContext, input: &mut Parser) -> Result<Longhands, ()> {
         let result = try!(scroll_snap_type_x::parse(context, input));
         Ok(Longhands {
             scroll_snap_type_x: result,
             scroll_snap_type_y: result,
         })
     }
 
-    impl<'a> LonghandsToSerialize<'a>  {
+    impl<'a> ToCss for LonghandsToSerialize<'a>  {
         // Serializes into the single keyword value if both scroll-snap-type and scroll-snap-type-y are same.
         // Otherwise into an empty string. This is done to match Gecko's behaviour.
-        fn to_css_declared<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
-            let x_and_y_equal = match (self.scroll_snap_type_x, self.scroll_snap_type_y) {
-                (&DeclaredValue::Value(ref x_value), &DeclaredValue::Value(ref y_value)) => {
-                    *x_value == *y_value
-                },
-                (&DeclaredValue::Initial, &DeclaredValue::Initial) => true,
-                (&DeclaredValue::Inherit, &DeclaredValue::Inherit) => true,
-                (&DeclaredValue::Unset, &DeclaredValue::Unset) => true,
-                (x, y) => { *x == *y },
-            };
-
-            if x_and_y_equal {
+        fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
+            if self.scroll_snap_type_x == self.scroll_snap_type_y {
                 self.scroll_snap_type_x.to_css(dest)
             } else {
                 Ok(())
             }
         }
     }
 </%helpers:shorthand>
--- a/servo/components/style/properties/shorthand/column.mako.rs
+++ b/servo/components/style/properties/shorthand/column.mako.rs
@@ -44,18 +44,18 @@
         } else {
             Ok(Longhands {
                 column_count: unwrap_or_initial!(column_count),
                 column_width: unwrap_or_initial!(column_width),
             })
         }
     }
 
-    impl<'a> LonghandsToSerialize<'a>  {
-        fn to_css_declared<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
+    impl<'a> ToCss for LonghandsToSerialize<'a>  {
+        fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
             try!(self.column_width.to_css(dest));
             try!(write!(dest, " "));
 
             self.column_count.to_css(dest)
         }
     }
 </%helpers:shorthand>
 
@@ -91,35 +91,18 @@
                 column_rule_style: unwrap_or_initial!(column_rule_style),
                 column_rule_color: unwrap_or_initial!(column_rule_color),
             })
         } else {
             Err(())
         }
     }
 
-    impl<'a> LonghandsToSerialize<'a>  {
-        fn to_css_declared<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
-            let mut need_space = false;
-
-            if let DeclaredValue::Value(ref width) = *self.column_rule_width {
-                try!(width.to_css(dest));
-                need_space = true;
-            }
-
-            if let DeclaredValue::Value(ref style) = *self.column_rule_style {
-                if need_space {
-                    try!(write!(dest, " "));
-                }
-                try!(style.to_css(dest));
-                need_space = true;
-            }
-
-            if let DeclaredValue::Value(ref color) = *self.column_rule_color {
-                if need_space {
-                    try!(write!(dest, " "));
-                }
-                try!(color.to_css(dest));
-            }
-            Ok(())
+    impl<'a> ToCss for LonghandsToSerialize<'a>  {
+        fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
+            self.column_rule_width.to_css(dest)?;
+            dest.write_str(" ")?;
+            self.column_rule_style.to_css(dest)?;
+            dest.write_str(" ")?;
+            self.column_rule_color.to_css(dest)
         }
     }
 </%helpers:shorthand>
--- a/servo/components/style/properties/shorthand/font.mako.rs
+++ b/servo/components/style/properties/shorthand/font.mako.rs
@@ -90,47 +90,33 @@
             % endif
             % if product == "none":
                 font_language_override: font_language_override::get_initial_specified_value(),
             % endif
         })
     }
 
     // This may be a bit off, unsure, possibly needs changes
-    impl<'a> LonghandsToSerialize<'a>  {
-        fn to_css_declared<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
-            if let DeclaredValue::Value(ref style) = *self.font_style {
-                try!(style.to_css(dest));
-                try!(write!(dest, " "));
-            }
-
-            if let DeclaredValue::Value(ref variant) = *self.font_variant {
-                try!(variant.to_css(dest));
-                try!(write!(dest, " "));
-            }
+    impl<'a> ToCss for LonghandsToSerialize<'a>  {
+        fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
+            self.font_style.to_css(dest)?;
+            dest.write_str(" ")?;
+            self.font_variant.to_css(dest)?;
+            dest.write_str(" ")?;
+            self.font_weight.to_css(dest)?;
+            dest.write_str(" ")?;
+            self.font_stretch.to_css(dest)?;
+            dest.write_str(" ")?;
 
-            if let DeclaredValue::Value(ref weight) = *self.font_weight {
-                try!(weight.to_css(dest));
-                try!(write!(dest, " "));
-            }
-
-            if let DeclaredValue::Value(ref stretch) = *self.font_stretch {
-                try!(stretch.to_css(dest));
-                try!(write!(dest, " "));
-            }
-
-            try!(self.font_size.to_css(dest));
-            if let DeclaredValue::Value(ref height) = *self.line_height {
-                match *height {
-                    line_height::SpecifiedValue::Normal => {},
-                    _ => {
-                        try!(write!(dest, "/"));
-                        try!(height.to_css(dest));
-                    }
+            self.font_size.to_css(dest)?;
+            match *self.line_height {
+                line_height::SpecifiedValue::Normal => {},
+                _ => {
+                    dest.write_str("/")?;
+                    self.line_height.to_css(dest)?;
                 }
             }
 
-            try!(write!(dest, " "));
-
+            dest.write_str(" ")?;
             self.font_family.to_css(dest)
         }
     }
 </%helpers:shorthand>
--- a/servo/components/style/properties/shorthand/inherited_svg.mako.rs
+++ b/servo/components/style/properties/shorthand/inherited_svg.mako.rs
@@ -15,23 +15,18 @@
 
         Ok(Longhands {
             marker_start: url.clone(),
             marker_mid: url.clone(),
             marker_end: url,
         })
     }
 
-    impl<'a> LonghandsToSerialize<'a>  {
-        fn to_css_declared<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
-            if let DeclaredValue::Value(ref start) = *self.marker_start {
-                if let DeclaredValue::Value(ref mid) = *self.marker_mid {
-                    if let DeclaredValue::Value(ref end) = *self.marker_end {
-                        if start == mid && mid == end {
-                            start.to_css(dest)?;
-                        }
-                    }
-                }
+    impl<'a> ToCss for LonghandsToSerialize<'a>  {
+        fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
+            if self.marker_start == self.marker_mid && self.marker_mid == self.marker_end {
+                self.marker_start.to_css(dest)
+            } else {
+                Ok(())
             }
-            Ok(())
         }
     }
 </%helpers:shorthand>
--- a/servo/components/style/properties/shorthand/inherited_text.mako.rs
+++ b/servo/components/style/properties/shorthand/inherited_text.mako.rs
@@ -33,31 +33,21 @@
                 text_emphasis_color: unwrap_or_initial!(text_emphasis_color, color),
                 text_emphasis_style: unwrap_or_initial!(text_emphasis_style, style),
             })
         } else {
             Err(())
         }
     }
 
-    impl<'a> LonghandsToSerialize<'a>  {
-        fn to_css_declared<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
-            let mut style_present = false;
-            if let DeclaredValue::Value(ref value) = *self.text_emphasis_style {
-                style_present = true;
-                try!(value.to_css(dest));
-            }
-
-            if let DeclaredValue::Value(ref color) = *self.text_emphasis_color {
-                if style_present {
-                    try!(write!(dest, " "));
-                }
-                try!(color.to_css(dest));
-            }
-            Ok(())
+    impl<'a> ToCss for LonghandsToSerialize<'a>  {
+        fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
+            self.text_emphasis_style.to_css(dest)?;
+            dest.write_str(" ")?;
+            self.text_emphasis_color.to_css(dest)
         }
     }
 </%helpers:shorthand>
 
 // CSS Compatibility
 // https://compat.spec.whatwg.org/
 <%helpers:shorthand name="-webkit-text-stroke"
                     sub_properties="-webkit-text-stroke-color
@@ -91,27 +81,16 @@
                 _webkit_text_stroke_color: unwrap_or_initial!(_webkit_text_stroke_color, color),
                 _webkit_text_stroke_width: unwrap_or_initial!(_webkit_text_stroke_width, width),
             })
         } else {
             Err(())
         }
     }
 
-    impl<'a> LonghandsToSerialize<'a>  {
-        fn to_css_declared<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
-            let mut style_present = false;
-            if let DeclaredValue::Value(ref width) = *self._webkit_text_stroke_width {
-                style_present = true;
-                try!(width.to_css(dest));
-            }
-
-            if let DeclaredValue::Value(ref color) = *self._webkit_text_stroke_color {
-                if style_present {
-                    try!(write!(dest, " "));
-                }
-                try!(color.to_css(dest));
-            }
-
-            Ok(())
+    impl<'a> ToCss for LonghandsToSerialize<'a>  {
+        fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
+            self._webkit_text_stroke_width.to_css(dest)?;
+            dest.write_str(" ")?;
+            self._webkit_text_stroke_color.to_css(dest)
         }
     }
 </%helpers:shorthand>
--- a/servo/components/style/properties/shorthand/list.mako.rs
+++ b/servo/components/style/properties/shorthand/list.mako.rs
@@ -91,31 +91,18 @@
                     list_style_image: unwrap_or_initial!(list_style_image, image),
                     list_style_type: unwrap_or_initial!(list_style_type),
                 })
             }
             _ => Err(()),
         }
     }
 
-    impl<'a> LonghandsToSerialize<'a>  {
-        fn to_css_declared<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
-            match *self.list_style_position {
-                DeclaredValue::Initial => try!(write!(dest, "outside")),
-                _ => try!(self.list_style_position.to_css(dest))
-            }
-
-            try!(write!(dest, " "));
-
-            match *self.list_style_image {
-                DeclaredValue::Initial => try!(write!(dest, "none")),
-                _ => try!(self.list_style_image.to_css(dest))
-            };
-
-            try!(write!(dest, " "));
-
-            match *self.list_style_type {
-                DeclaredValue::Initial => write!(dest, "disc"),
-                _ => self.list_style_type.to_css(dest)
-            }
+    impl<'a> ToCss for LonghandsToSerialize<'a>  {
+        fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
+            self.list_style_position.to_css(dest)?;
+            dest.write_str(" ")?;
+            self.list_style_image.to_css(dest)?;
+            dest.write_str(" ")?;
+            self.list_style_type.to_css(dest)
         }
     }
 </%helpers:shorthand>
--- a/servo/components/style/properties/shorthand/mask.mako.rs
+++ b/servo/components/style/properties/shorthand/mask.mako.rs
@@ -115,123 +115,72 @@
 
         Ok(Longhands {
             % for name in "image mode position_x position_y size repeat origin clip composite".split():
                 mask_${name}: mask_${name},
             % endfor
          })
     }
 
-    impl<'a> LonghandsToSerialize<'a>  {
-        fn to_css_declared<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
-            // mako doesn't like ampersands following `<`
-            fn extract_value<T>(x: &DeclaredValue<T>) -> Option< &T> {
-                match *x {
-                    DeclaredValue::Value(ref val) => Some(val),
-                    _ => None,
-                }
+    impl<'a> ToCss for LonghandsToSerialize<'a>  {
+        fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
+            use properties::longhands::mask_origin::single_value::computed_value::T as Origin;
+            use properties::longhands::mask_clip::single_value::computed_value::T as Clip;
+
+            let len = self.mask_image.0.len();
+            if len == 0 {
+                return Ok(());
             }
-            use std::cmp;
-            let mut len = 0;
-            % for name in "image mode position_x position_y size repeat origin clip composite".split():
-                len = cmp::max(len, extract_value(self.mask_${name}).map(|i| i.0.len())
-                                                                   .unwrap_or(0));
+            % for name in "mode position_x position_y size repeat origin clip composite".split():
+                if self.mask_${name}.0.len() != len {
+                    return Ok(());
+                }
             % endfor
 
-            // There should be at least one declared value
-            if len == 0 {
-                return dest.write_str("")
-            }
-
             for i in 0..len {
                 if i > 0 {
-                    try!(dest.write_str(", "));
+                    dest.write_str(", ")?;
                 }
 
                 % for name in "image mode position_x position_y size repeat origin clip composite".split():
-                    let ${name} = if let DeclaredValue::Value(ref arr) = *self.mask_${name} {
-                        arr.0.get(i % arr.0.len())
-                    } else {
-                        None
-                    };
+                    let ${name} = &self.mask_${name}.0[i];
                 % endfor
 
-                if let Some(image) = image {
-                    try!(image.to_css(dest));
-                } else {
-                    try!(write!(dest, "none"));
-                }
-
-                try!(write!(dest, " "));
-
-                if let Some(mode) = mode {
-                    try!(mode.to_css(dest));
-                } else {
-                    try!(write!(dest, "match-source"));
-                }
+                image.to_css(dest)?;
+                dest.write_str(" ")?;
+                mode.to_css(dest)?;
+                dest.write_str(" ")?;
+                position_x.to_css(dest)?;
+                dest.write_str(" ")?;
+                position_y.to_css(dest)?;
+                dest.write_str(" / ")?;
+                size.to_css(dest)?;
+                dest.write_str(" ")?;
+                repeat.to_css(dest)?;
+                dest.write_str(" ")?;
 
-                try!(write!(dest, " "));
-
-                try!(position_x.unwrap_or(&mask_position_x::single_value
-                                                      ::get_initial_position_value())
-                     .to_css(dest));
-
-                try!(write!(dest, " "));
-
-                try!(position_y.unwrap_or(&mask_position_y::single_value
-                                                      ::get_initial_position_value())
-                     .to_css(dest));
-
-                if let Some(size) = size {
-                    try!(write!(dest, " / "));
-                    try!(size.to_css(dest));
+                match (origin, clip) {
+                    (&Origin::padding_box, &Clip::padding_box) => {
+                        try!(origin.to_css(dest));
+                    },
+                    (&Origin::border_box, &Clip::border_box) => {
+                        try!(origin.to_css(dest));
+                    },
+                    (&Origin::content_box, &Clip::content_box) => {
+                        try!(origin.to_css(dest));
+                    },
+                    _ => {
+                        try!(origin.to_css(dest));
+                        try!(write!(dest, " "));
+                        try!(clip.to_css(dest));
+                    }
                 }
 
-                try!(write!(dest, " "));
-
-                if let Some(repeat) = repeat {
-                    try!(repeat.to_css(dest));
-                } else {
-                    try!(write!(dest, "repeat"));
-                }
-
-                match (origin, clip) {
-                    (Some(origin), Some(clip)) => {
-                        use properties::longhands::mask_origin::single_value::computed_value::T as Origin;
-                        use properties::longhands::mask_clip::single_value::computed_value::T as Clip;
-
-                        try!(write!(dest, " "));
-
-                        match (origin, clip) {
-                            (&Origin::padding_box, &Clip::padding_box) => {
-                                try!(origin.to_css(dest));
-                            },
-                            (&Origin::border_box, &Clip::border_box) => {
-                                try!(origin.to_css(dest));
-                            },
-                            (&Origin::content_box, &Clip::content_box) => {
-                                try!(origin.to_css(dest));
-                            },
-                            _ => {
-                                try!(origin.to_css(dest));
-                                try!(write!(dest, " "));
-                                try!(clip.to_css(dest));
-                            }
-                        }
-                    },
-                    _ => {}
-                };
-
-                try!(write!(dest, " "));
-
-                if let Some(composite) = composite {
-                    try!(composite.to_css(dest));
-                } else {
-                    try!(write!(dest, "add"));
-                }
+                dest.write_str(" ")?;
+                composite.to_css(dest)?;
             }
 
             Ok(())
         }
     }
 </%helpers:shorthand>
 
 <%helpers:shorthand name="mask-position" products="gecko" extra_prefixes="webkit"
@@ -263,55 +212,24 @@
         }
 
         Ok(Longhands {
             mask_position_x: position_x,
             mask_position_y: position_y,
         })
     }
 
-    impl<'a> LonghandsToSerialize<'a>  {
-        fn to_css_declared<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
-            // mako doesn't like ampersands following `<`
-            fn extract_value<T>(x: &DeclaredValue<T>) -> Option< &T> {
-                match *x {
-                    DeclaredValue::Value(ref val) => Some(val),
-                    _ => None,
-                }
-            }
-            use std::cmp;
-            let mut len = 0;
-            % for name in "x y".split():
-                len = cmp::max(len, extract_value(self.mask_position_${name})
-                                                      .map(|i| i.0.len())
-                                                      .unwrap_or(0));
-            % endfor
-
-            // There should be at least one declared value
-            if len == 0 {
-                return dest.write_str("")
+    impl<'a> ToCss for LonghandsToSerialize<'a>  {
+        fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
+            let len = self.mask_position_x.0.len();
+            if len == 0 || self.mask_position_y.0.len() != len {
+                return Ok(());
             }
 
             for i in 0..len {
-                % for name in "x y".split():
-                    let position_${name} = if let DeclaredValue::Value(ref arr) =
-                                           *self.mask_position_${name} {
-                        arr.0.get(i % arr.0.len())
-                    } else {
-                        None
-                    };
-                % endfor
-
-                try!(position_x.unwrap_or(&mask_position_x::single_value
-                                                                ::get_initial_position_value())
-                               .to_css(dest));
-
-                try!(write!(dest, " "));
-
-                try!(position_y.unwrap_or(&mask_position_y::single_value
-                                                                ::get_initial_position_value())
-                               .to_css(dest));
+                self.mask_position_x.0[i].to_css(dest)?;
+                self.mask_position_y.0[i].to_css(dest)?;
             }
 
             Ok(())
         }
     }
 </%helpers:shorthand>
--- a/servo/components/style/properties/shorthand/outline.mako.rs
+++ b/servo/components/style/properties/shorthand/outline.mako.rs
@@ -46,33 +46,23 @@
                 outline_style: unwrap_or_initial!(outline_style, style),
                 outline_width: unwrap_or_initial!(outline_width, width),
             })
         } else {
             Err(())
         }
     }
 
-    impl<'a> LonghandsToSerialize<'a>  {
-        fn to_css_declared<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
+    impl<'a> ToCss for LonghandsToSerialize<'a>  {
+        fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
             try!(self.outline_width.to_css(dest));
             try!(write!(dest, " "));
-
-            match *self.outline_style {
-                DeclaredValue::Initial => try!(write!(dest, "none")),
-                _ => try!(self.outline_style.to_css(dest))
-            };
-
-            match *self.outline_color {
-                DeclaredValue::Initial => Ok(()),
-                _ => {
-                    try!(write!(dest, " "));
-                    self.outline_color.to_css(dest)
-                }
-            }
+            try!(self.outline_style.to_css(dest));
+            try!(write!(dest, " "));
+            self.outline_color.to_css(dest)
         }
     }
 </%helpers:shorthand>
 
 // The -moz-outline-radius shorthand is non-standard and not on a standards track.
 <%helpers:shorthand name="-moz-outline-radius" sub_properties="${' '.join(
     '-moz-outline-radius-%s' % corner
     for corner in ['topleft', 'topright', 'bottomright', 'bottomleft']
@@ -86,18 +76,18 @@
                 % for corner in ["top_left", "top_right", "bottom_right", "bottom_left"]:
                 _moz_outline_radius_${corner.replace("_", "")}: longhands.border_${corner}_radius,
                 % endfor
             }
         })
     }
 
     // TODO: Border radius for the radius shorthand is not implemented correctly yet
-    impl<'a> LonghandsToSerialize<'a>  {
-        fn to_css_declared<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
+    impl<'a> ToCss for LonghandsToSerialize<'a>  {
+        fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
             try!(self._moz_outline_radius_topleft.to_css(dest));
             try!(write!(dest, " "));
 
             try!(self._moz_outline_radius_topright.to_css(dest));
             try!(write!(dest, " "));
 
             try!(self._moz_outline_radius_bottomright.to_css(dest));
             try!(write!(dest, " "));
--- a/servo/components/style/properties/shorthand/position.mako.rs
+++ b/servo/components/style/properties/shorthand/position.mako.rs
@@ -32,29 +32,21 @@
         }
         Ok(Longhands {
             flex_direction: unwrap_or_initial!(flex_direction, direction),
             flex_wrap: unwrap_or_initial!(flex_wrap, wrap),
         })
     }
 
 
-    impl<'a> LonghandsToSerialize<'a>  {
-        fn to_css_declared<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
-            match *self.flex_direction {
-                DeclaredValue::Initial => try!(write!(dest, "row")),
-                _ => try!(self.flex_direction.to_css(dest))
-            };
-
-            try!(write!(dest, " "));
-
-            match *self.flex_wrap {
-                DeclaredValue::Initial => write!(dest, "nowrap"),
-                _ => self.flex_wrap.to_css(dest)
-            }
+    impl<'a> ToCss for LonghandsToSerialize<'a>  {
+        fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
+            self.flex_direction.to_css(dest)?;
+            dest.write_str(" ")?;
+            self.flex_wrap.to_css(dest)
         }
     }
 </%helpers:shorthand>
 
 <%helpers:shorthand name="flex" sub_properties="flex-grow flex-shrink flex-basis" extra_prefixes="webkit"
                     spec="https://drafts.csswg.org/css-flexbox/#flex-property">
     use parser::Parse;
     use values::specified::{Number, NoCalcLength, LengthOrPercentageOrAutoOrContent};
@@ -100,18 +92,18 @@
         }
         Ok(Longhands {
             flex_grow: grow.unwrap_or(Number(1.0)),
             flex_shrink: shrink.unwrap_or(Number(1.0)),
             flex_basis: basis.unwrap_or(LengthOrPercentageOrAutoOrContent::Length(NoCalcLength::zero()))
         })
     }
 
-    impl<'a> LonghandsToSerialize<'a>  {
-        fn to_css_declared<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
+    impl<'a> ToCss for LonghandsToSerialize<'a>  {
+        fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
             try!(self.flex_grow.to_css(dest));
             try!(write!(dest, " "));
 
             try!(self.flex_shrink.to_css(dest));
             try!(write!(dest, " "));
 
             self.flex_basis.to_css(dest)
         }
--- a/servo/components/style/properties/shorthand/serialize.mako.rs
+++ b/servo/components/style/properties/shorthand/serialize.mako.rs
@@ -1,14 +1,13 @@
 /* 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 cssparser::Color;
-use properties::DeclaredValue;
 use style_traits::ToCss;
 use values::specified::{BorderStyle, CSSColor};
 use std::fmt;
 
 #[allow(missing_docs)]
 pub fn serialize_four_sides<W, I>(dest: &mut W,
                                   top: &I,
                                   right: &I,
@@ -55,40 +54,21 @@ pub fn serialize_four_sides<W, I>(dest: 
 
         try!(left.to_css(dest));
     }
 
     Ok(())
 }
 
 fn serialize_directional_border<W, I,>(dest: &mut W,
-                                                width: &DeclaredValue<I>,
-                                                style: &DeclaredValue<BorderStyle>,
-                                                color: &DeclaredValue<CSSColor>)
-                                                -> fmt::Result where W: fmt::Write, I: ToCss {
-    match *width {
-        DeclaredValue::Value(ref width) => {
-            try!(width.to_css(dest));
-        },
-        _ => {
-            try!(write!(dest, "medium"));
-        }
-    };
-
-    try!(write!(dest, " "));
-
-    match *style {
-        DeclaredValue::Value(ref style) => {
-            try!(style.to_css(dest));
-        },
-        _ => {
-            try!(write!(dest, "none"));
-        }
-    };
-
-    match *color {
-        DeclaredValue::Value(ref color) if color.parsed != Color::CurrentColor => {
-            try!(write!(dest, " "));
-            color.to_css(dest)
-        },
-        _ => Ok(())
+                                       width: &I,
+                                       style: &BorderStyle,
+                                       color: &CSSColor)
+    -> fmt::Result where W: fmt::Write, I: ToCss {
+    width.to_css(dest)?;
+    dest.write_str(" ")?;
+    style.to_css(dest)?;
+    if color.parsed != Color::CurrentColor {
+        dest.write_str(" ")?;
+        color.to_css(dest)?;
     }
+    Ok(())
 }
--- a/servo/components/style/properties/shorthand/text.mako.rs
+++ b/servo/components/style/properties/shorthand/text.mako.rs
@@ -41,37 +41,21 @@
 
         Ok(Longhands {
             text_decoration_color: unwrap_or_initial!(text_decoration_color, color),
             text_decoration_line: unwrap_or_initial!(text_decoration_line, line),
             text_decoration_style: unwrap_or_initial!(text_decoration_style, style),
         })
     }
 
-    impl<'a> LonghandsToSerialize<'a>  {
-        fn to_css_declared<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
-            match *self.text_decoration_line {
-                DeclaredValue::Value(ref line) => {
-                    try!(line.to_css(dest));
-                },
-                _ => {
-                    try!(write!(dest, "none"));
-                }
-            };
-
-            if let DeclaredValue::Value(ref style) = *self.text_decoration_style {
-                if *style != text_decoration_style::computed_value::T::solid {
-                    try!(write!(dest, " "));
-                    try!(style.to_css(dest));
-                }
+    impl<'a> ToCss for LonghandsToSerialize<'a>  {
+        fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
+            self.text_decoration_line.to_css(dest)?;
+            dest.write_str(" ")?;
+            self.text_decoration_style.to_css(dest)?;
+            if self.text_decoration_color.parsed != CSSParserColor::CurrentColor {
+                dest.write_str(" ")?;
+                self.text_decoration_color.to_css(dest)?;
             }
-
-            if let DeclaredValue::Value(ref color) = *self.text_decoration_color {
-                if color.parsed != CSSParserColor::CurrentColor {
-                    try!(write!(dest, " "));
-                    try!(color.to_css(dest));
-                }
-            }
-
             Ok(())
         }
     }
 </%helpers:shorthand>
--- a/servo/tests/unit/style/properties/serialization.rs
+++ b/servo/tests/unit/style/properties/serialization.rs
@@ -2,17 +2,18 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 use cssparser::Parser;
 use media_queries::CSSErrorReporterTest;
 use servo_url::ServoUrl;
 use style::computed_values::display::T::inline_block;
 use style::parser::ParserContext;
-use style::properties::{DeclaredValue, PropertyDeclaration, PropertyDeclarationBlock, Importance, PropertyId};
+use style::properties::{DeclaredValue, PropertyDeclaration};
+use style::properties::{PropertyDeclarationBlock, Importance, PropertyId};
 use style::properties::longhands::outline_color::computed_value::T as ComputedColor;
 use style::properties::parse_property_declaration_list;
 use style::stylesheets::Origin;
 use style::values::{RGBA, Auto};
 use style::values::specified::{BorderStyle, BorderWidth, CSSColor, Length, NoCalcLength};
 use style::values::specified::{LengthOrPercentage, LengthOrPercentageOrAuto, LengthOrPercentageOrAutoOrContent};
 use style::values::specified::url::SpecifiedUrl;
 use style_traits::ToCss;
@@ -366,134 +367,92 @@ mod shorthand_serialization {
             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 {
-                parsed: ComputedColor::RGBA(RGBA::new(255, 0, 0, 255)),
-                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);");
+        fn get_border_property_values() -> (DeclaredValue<BorderWidth>,
+                                            DeclaredValue<BorderStyle>,
+                                            DeclaredValue<CSSColor>) {
+            (DeclaredValue::Value(BorderWidth::from_length(Length::from_px(4f32))),
+             DeclaredValue::Value(BorderStyle::solid),
+             DeclaredValue::Value(CSSColor::currentcolor()))
         }
 
         #[test]
-        fn directional_border_with_no_specified_color_will_not_show_color() {
+        fn border_top_should_serialize_correctly() {
             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::Initial;
-
+            let (width, style, color) = get_border_property_values();
             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;");
         }
 
         #[test]
         fn border_right_should_serialize_correctly() {
             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::Initial;
-
+            let (width, style, color) = get_border_property_values();
             properties.push(PropertyDeclaration::BorderRightWidth(width));
             properties.push(PropertyDeclaration::BorderRightStyle(style));
             properties.push(PropertyDeclaration::BorderRightColor(color));
 
             let serialization = shorthand_properties_to_string(properties);
             assert_eq!(serialization, "border-right: 4px solid;");
         }
 
         #[test]
         fn border_bottom_should_serialize_correctly() {
             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::Initial;
-
+            let (width, style, color) = get_border_property_values();
             properties.push(PropertyDeclaration::BorderBottomWidth(width));
             properties.push(PropertyDeclaration::BorderBottomStyle(style));
             properties.push(PropertyDeclaration::BorderBottomColor(color));
 
             let serialization = shorthand_properties_to_string(properties);
             assert_eq!(serialization, "border-bottom: 4px solid;");
         }
 
         #[test]
         fn border_left_should_serialize_correctly() {
             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::Initial;
-
+            let (width, style, color) = get_border_property_values();
             properties.push(PropertyDeclaration::BorderLeftWidth(width));
             properties.push(PropertyDeclaration::BorderLeftStyle(style));
             properties.push(PropertyDeclaration::BorderLeftColor(color));
 
             let serialization = shorthand_properties_to_string(properties);
             assert_eq!(serialization, "border-left: 4px solid;");
         }
 
         #[test]
         fn border_should_serialize_correctly() {
             let mut properties = Vec::new();
-
-            let top_width = DeclaredValue::Value(BorderWidth::from_length(Length::from_px(4f32)));
-            let top_style = DeclaredValue::Value(BorderStyle::solid);
-            let top_color = DeclaredValue::Initial;
+            let (width, style, color) = get_border_property_values();
 
-            properties.push(PropertyDeclaration::BorderTopWidth(top_width));
-            properties.push(PropertyDeclaration::BorderTopStyle(top_style));
-            properties.push(PropertyDeclaration::BorderTopColor(top_color));
-
-            let right_width = DeclaredValue::Value(BorderWidth::from_length(Length::from_px(4f32)));
-            let right_style = DeclaredValue::Value(BorderStyle::solid);
-            let right_color = DeclaredValue::Initial;
+            properties.push(PropertyDeclaration::BorderTopWidth(width.clone()));
+            properties.push(PropertyDeclaration::BorderTopStyle(style.clone()));
+            properties.push(PropertyDeclaration::BorderTopColor(color.clone()));
 
-            properties.push(PropertyDeclaration::BorderRightWidth(right_width));
-            properties.push(PropertyDeclaration::BorderRightStyle(right_style));
-            properties.push(PropertyDeclaration::BorderRightColor(right_color));
-
-            let bottom_width = DeclaredValue::Value(BorderWidth::from_length(Length::from_px(4f32)));
-            let bottom_style = DeclaredValue::Value(BorderStyle::solid);
-            let bottom_color = DeclaredValue::Initial;
+            properties.push(PropertyDeclaration::BorderRightWidth(width.clone()));
+            properties.push(PropertyDeclaration::BorderRightStyle(style.clone()));
+            properties.push(PropertyDeclaration::BorderRightColor(color.clone()));
 
-            properties.push(PropertyDeclaration::BorderBottomWidth(bottom_width));
-            properties.push(PropertyDeclaration::BorderBottomStyle(bottom_style));
-            properties.push(PropertyDeclaration::BorderBottomColor(bottom_color));
+            properties.push(PropertyDeclaration::BorderBottomWidth(width.clone()));
+            properties.push(PropertyDeclaration::BorderBottomStyle(style.clone()));
+            properties.push(PropertyDeclaration::BorderBottomColor(color.clone()));
 
-            let left_width = DeclaredValue::Value(BorderWidth::from_length(Length::from_px(4f32)));
-            let left_style = DeclaredValue::Value(BorderStyle::solid);
-            let left_color = DeclaredValue::Initial;
-
-            properties.push(PropertyDeclaration::BorderLeftWidth(left_width));
-            properties.push(PropertyDeclaration::BorderLeftStyle(left_style));
-            properties.push(PropertyDeclaration::BorderLeftColor(left_color));
+            properties.push(PropertyDeclaration::BorderLeftWidth(width.clone()));
+            properties.push(PropertyDeclaration::BorderLeftStyle(style.clone()));
+            properties.push(PropertyDeclaration::BorderLeftColor(color.clone()));
 
             let serialization = shorthand_properties_to_string(properties);
             assert_eq!(serialization, "border: 4px solid;");
         }
     }
 
     mod list_style {
         use style::properties::longhands::list_style_position::SpecifiedValue as ListStylePosition;
@@ -512,32 +471,16 @@ mod shorthand_serialization {
 
             properties.push(PropertyDeclaration::ListStylePosition(position));
             properties.push(PropertyDeclaration::ListStyleImage(image));
             properties.push(PropertyDeclaration::ListStyleType(style_type));
 
             let serialization = shorthand_properties_to_string(properties);
             assert_eq!(serialization, "list-style: inside url(\"http://servo/test.png\") disc;");
         }
-
-        #[test]
-        fn list_style_should_show_all_properties_even_if_only_one_is_set() {
-            let mut properties = Vec::new();
-
-            let position = DeclaredValue::Initial;
-            let image = DeclaredValue::Initial;
-            let style_type = DeclaredValue::Value(ListStyleType::disc);
-
-            properties.push(PropertyDeclaration::ListStylePosition(position));
-            properties.push(PropertyDeclaration::ListStyleImage(image));
-            properties.push(PropertyDeclaration::ListStyleType(style_type));
-
-            let serialization = shorthand_properties_to_string(properties);
-            assert_eq!(serialization, "list-style: outside none disc;");
-        }
     }
 
     mod outline {
         use style::properties::longhands::outline_width::SpecifiedValue as WidthContainer;
         use style::values::Either;
         use super::*;
 
         #[test]
@@ -555,50 +498,16 @@ mod shorthand_serialization {
             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);");
         }
 
         #[test]
-        fn outline_should_not_show_color_if_not_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::Initial;
-
-            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;");
-        }
-
-        #[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 {
-                parsed: ComputedColor::RGBA(RGBA::new(255, 0, 0, 255)),
-                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 {
                 parsed: ComputedColor::RGBA(RGBA::new(255, 0, 0, 255)),
                 authored: None
--- a/servo/tests/unit/style/stylesheets.rs
+++ b/servo/tests/unit/style/stylesheets.rs
@@ -12,17 +12,18 @@ use servo_url::ServoUrl;
 use std::borrow::ToOwned;
 use std::sync::Arc;
 use std::sync::Mutex;
 use std::sync::atomic::AtomicBool;
 use style::error_reporting::ParseErrorReporter;
 use style::keyframes::{Keyframe, KeyframeSelector, KeyframePercentage};
 use style::parser::ParserContextExtraData;
 use style::properties::Importance;
-use style::properties::{PropertyDeclaration, PropertyDeclarationBlock, DeclaredValue, longhands};
+use style::properties::{CSSWideKeyword, PropertyDeclaration, PropertyDeclarationBlock};
+use style::properties::{DeclaredValue, longhands};
 use style::properties::longhands::animation_play_state;
 use style::stylesheets::{Origin, Namespaces};
 use style::stylesheets::{Stylesheet, NamespaceRule, CssRule, CssRules, StyleRule, KeyframesRule};
 use style::values::specified::{LengthOrPercentageOrAuto, Percentage};
 
 #[test]
 fn test_parse_stylesheet() {
     let css = r"
@@ -97,17 +98,18 @@ fn test_parse_stylesheet() {
                         specificity: (0 << 20) + (1 << 10) + (1 << 0),
                     },
                 ]),
                 block: Arc::new(RwLock::new(PropertyDeclarationBlock {
                     declarations: vec![
                         (PropertyDeclaration::Display(DeclaredValue::Value(
                             longhands::display::SpecifiedValue::none)),
                          Importance::Important),
-                        (PropertyDeclaration::Custom(Atom::from("a"), DeclaredValue::Inherit),
+                        (PropertyDeclaration::Custom(Atom::from("a"),
+                         DeclaredValue::CSSWideKeyword(CSSWideKeyword::Inherit)),
                          Importance::Important),
                     ],
                     important_count: 2,
                 })),
             }))),
             CssRule::Style(Arc::new(RwLock::new(StyleRule {
                 selectors: SelectorList(vec![
                     Selector {