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 374515 1a3de0af9d95b3f3b8b0711c718fd175d8d7c83a
parent 374514 6846c1427f28a9baf6498ce06d424c87c2a8fc1f
child 374516 ab41a61ca37b4dd9ddfc234b1b99460c0fe92fb3
push id10863
push userjlorenzo@mozilla.com
push dateMon, 06 Mar 2017 23:02:23 +0000
treeherdermozilla-aurora@0931190cd725 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersemilio
bugs15779
milestone54.0a1
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 {