merge mozilla-central to autoland. r=merge a=merge
authorSebastian Hengst <archaeopteryx@coole-files.de>
Sun, 04 Jun 2017 20:10:24 +0200
changeset 412693 3a48edeff5afbad125b56683e95392c5b18d0936
parent 412692 1df048c139de6254b0de95c95f7ba59a0c0118c1 (diff)
parent 412688 8a3aa1701537ea6b8334f432cd030d260d492fa3 (current diff)
child 412694 eb8b2638f96a3f73340a5b436de8013ef0d0613d
push id1490
push usermtabara@mozilla.com
push dateMon, 31 Jul 2017 14:08:16 +0000
treeherdermozilla-release@70e32e6bf15e [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge, merge
milestone55.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
merge mozilla-central to autoland. r=merge a=merge
--- a/browser/base/content/browser-sets.inc
+++ b/browser/base/content/browser-sets.inc
@@ -354,16 +354,22 @@
     <key id="key_sanitize_mac" command="Tools:Sanitize" keycode="VK_BACK" modifiers="accel,shift"/>
 #endif
     <key id="key_quitApplication" key="&quitApplicationCmd.key;"
 #ifdef XP_WIN
          modifiers="accel,shift"
 #else
          modifiers="accel"
 #endif
+# On OS X, dark voodoo magic invokes the quit code for this key.
+# So we're not adding the attribute on OSX because of backwards/add-on compat.
+# See bug 1369909 for background on this.
+#ifndef XP_MACOSX
+         command="cmd_quitApplication"
+#endif
          reserved="true"/>
 
 #ifdef FULL_BROWSER_WINDOW
     <key id="key_undoCloseTab" command="History:UndoCloseTab" key="&tabCmd.commandkey;" modifiers="accel,shift"/>
 #endif
     <key id="key_undoCloseWindow" command="History:UndoCloseWindow" key="&newNavigatorCmd.key;" modifiers="accel,shift"/>
 
 #ifdef XP_GNOME
--- a/layout/reftests/w3c-css/received/reftest.list
+++ b/layout/reftests/w3c-css/received/reftest.list
@@ -235,17 +235,17 @@ fuzzy-if(OSX,40,6) == css-values-3/ch-un
 == css-values-3/initial-background-color.html css-values-3/reference/all-green.html
 == css-values-3/vh-calc-support-pct.html css-values-3/reference/all-green.html
 == css-values-3/vh-calc-support.html css-values-3/reference/all-green.html
 == css-values-3/vh-em-inherit.html css-values-3/reference/all-green.html
 == css-values-3/vh-inherit.html css-values-3/reference/all-green.html
 == css-values-3/vh-interpolate-pct.html css-values-3/reference/all-green.html
 == css-values-3/vh-interpolate-px.html css-values-3/reference/all-green.html
 == css-values-3/vh-interpolate-vh.html css-values-3/reference/all-green.html
-fails-if(styloVsGecko||stylo) == css-values-3/vh-support-atviewport.html css-values-3/reference/all-green.html
+== css-values-3/vh-support-atviewport.html css-values-3/reference/all-green.html
 == css-values-3/vh-support-margin.html css-values-3/reference/all-green.html
 skip == css-values-3/vh-support-transform-origin.html css-values-3/reference/all-green.html
 skip == css-values-3/vh-support-transform-translate.html css-values-3/reference/all-green.html
 == css-values-3/vh-support.html css-values-3/reference/all-green.html
 == css-values-3/vh-zero-support.html css-values-3/reference/all-green.html
 skip == css-values-3/vh_not_refreshing_on_chrome.html css-values-3/reference/vh_not_refreshing_on_chrome-ref.html
 skip == css-values-3/vh_not_refreshing_on_chrome_iframe.html css-values-3/reference/vh_not_refreshing_on_chrome-ref.html
 == css-writing-modes-3/abs-pos-non-replaced-icb-vlr-003.xht css21/reference/ref-filled-green-100px-square.xht
--- a/servo/components/style/properties/helpers.mako.rs
+++ b/servo/components/style/properties/helpers.mako.rs
@@ -1069,29 +1069,27 @@
 
 // Define property that supports prefixed intrinsic size keyword values for gecko.
 // E.g. -moz-max-content, -moz-min-content, etc.
 <%def name="gecko_size_type(name, length_type, initial_value, logical, **kwargs)">
     <%call expr="longhand(name,
                           predefined_type=length_type,
                           logical=logical,
                           **kwargs)">
-        use std::fmt;
-        use style_traits::ToCss;
         % if not logical:
             use values::specified::AllowQuirks;
         % endif
         use values::specified::${length_type};
 
         pub mod computed_value {
             pub type T = ::values::computed::${length_type};
         }
 
-        #[derive(Clone, Debug, HasViewportPercentage, PartialEq)]
         #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
+        #[derive(Clone, Debug, HasViewportPercentage, PartialEq, ToCss)]
         pub struct SpecifiedValue(pub ${length_type});
 
         % if length_type == "MozLength":
         impl SpecifiedValue {
             /// Returns the `auto` value.
             pub fn auto() -> Self {
                 use values::specified::length::LengthOrPercentageOrAuto;
                 SpecifiedValue(MozLength::LengthOrPercentageOrAuto(LengthOrPercentageOrAuto::Auto))
@@ -1126,22 +1124,16 @@
             % if "block" in name:
                 if let Ok(${length_type}::ExtremumLength(..)) = ret {
                     return Err(())
                 }
             % endif
             ret.map(SpecifiedValue)
         }
 
-        impl ToCss for SpecifiedValue {
-            fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
-                self.0.to_css(dest)
-            }
-        }
-
         impl ToComputedValue for SpecifiedValue {
             type ComputedValue = computed_value::T;
             #[inline]
             fn to_computed_value(&self, context: &Context) -> computed_value::T {
                 % if not logical or "block" in name:
                     use values::computed::${length_type};
                 % endif
                 let computed = self.0.to_computed_value(context);
--- a/servo/components/style/properties/longhand/border.mako.rs
+++ b/servo/components/style/properties/longhand/border.mako.rs
@@ -217,40 +217,32 @@
     use std::fmt;
     use style_traits::ToCss;
 
     no_viewport_percentage!(SpecifiedValue);
 
     pub mod computed_value {
         pub use super::RepeatKeyword;
 
-        #[derive(Debug, Clone, PartialEq)]
         #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
+        #[derive(Debug, Clone, PartialEq, ToCss)]
         pub struct T(pub RepeatKeyword, pub RepeatKeyword);
     }
 
     #[derive(Debug, Clone, PartialEq)]
     #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
     pub struct SpecifiedValue(pub RepeatKeyword,
                               pub Option<RepeatKeyword>);
 
     define_css_keyword_enum!(RepeatKeyword:
                              "stretch" => Stretch,
                              "repeat" => Repeat,
                              "round" => Round,
                              "space" => Space);
 
-
-    impl ToCss for computed_value::T {
-        fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
-            try!(self.0.to_css(dest));
-            try!(dest.write_str(" "));
-            self.1.to_css(dest)
-        }
-    }
     impl ToCss for SpecifiedValue {
         fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
             try!(self.0.to_css(dest));
             if let Some(second) = self.1 {
                 try!(dest.write_str(" "));
                 try!(second.to_css(dest));
             }
             Ok(())
--- a/servo/components/style/properties/longhand/color.mako.rs
+++ b/servo/components/style/properties/longhand/color.mako.rs
@@ -8,45 +8,37 @@
 
 <% from data import to_rust_ident %>
 
 <%helpers:longhand name="color" need_clone="True"
                    animation_value_type="IntermediateRGBA"
                    ignored_when_colors_disabled="True"
                    spec="https://drafts.csswg.org/css-color/#color">
     use cssparser::RGBA;
-    use std::fmt;
-    use style_traits::ToCss;
     use values::specified::{AllowQuirks, Color, CSSColor};
 
     impl ToComputedValue for SpecifiedValue {
         type ComputedValue = computed_value::T;
 
         #[inline]
         fn to_computed_value(&self, context: &Context) -> computed_value::T {
             self.0.parsed.to_computed_value(context)
         }
 
         #[inline]
         fn from_computed_value(computed: &computed_value::T) -> Self {
             SpecifiedValue(Color::RGBA(*computed).into())
         }
     }
 
-    #[derive(Clone, PartialEq, Debug)]
     #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
+    #[derive(Clone, Debug, PartialEq, ToCss)]
     pub struct SpecifiedValue(pub CSSColor);
     no_viewport_percentage!(SpecifiedValue);
 
-    impl ToCss for SpecifiedValue {
-        fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
-            self.0.to_css(dest)
-        }
-    }
-
     pub mod computed_value {
         use cssparser;
         pub type T = cssparser::RGBA;
     }
     #[inline]
     pub fn get_initial_value() -> computed_value::T {
         RGBA::new(0, 0, 0, 255) // black
     }
@@ -86,16 +78,19 @@
                               IMERawInputForeground IMERawInputUnderline IMESelectedRawTextBackground
                               IMESelectedRawTextForeground IMESelectedRawTextUnderline
                               IMEConvertedTextBackground IMEConvertedTextForeground IMEConvertedTextUnderline
                               IMESelectedConvertedTextBackground IMESelectedConvertedTextForeground
                               IMESelectedConvertedTextUnderline SpellCheckerUnderline""".split()
         %>
         use gecko_bindings::bindings::Gecko_GetLookAndFeelSystemColor;
         use gecko_bindings::structs::root::mozilla::LookAndFeel_ColorID;
+        use std::fmt;
+        use style_traits::ToCss;
+
         pub type SystemColor = LookAndFeel_ColorID;
 
         impl ToCss for SystemColor {
             fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
                 let s = match *self {
                     % for color in system_colors + extra_colors:
                         LookAndFeel_ColorID::eColorID_${to_rust_ident(color)} => "${color}",
                     % endfor
--- a/servo/components/style/properties/longhand/font.mako.rs
+++ b/servo/components/style/properties/longhand/font.mako.rs
@@ -2144,28 +2144,25 @@ https://drafts.csswg.org/css-fonts-4/#lo
                          needs_conversion=True)}
 
 <%helpers:longhand name="-moz-script-min-size" products="gecko" animation_value_type="none"
                    predefined_type="Length" gecko_ffi_name="mScriptMinSize"
                    spec="Internal (not web-exposed)"
                    internal="True" disable_when_testing="True">
     use app_units::Au;
     use gecko_bindings::structs::NS_MATHML_DEFAULT_SCRIPT_MIN_SIZE_PT;
-    use std::fmt;
-    use style_traits::ToCss;
     use values::specified::length::{AU_PER_PT, FontBaseSize, NoCalcLength};
 
-    #[derive(Clone, Debug, HasViewportPercentage, PartialEq)]
+    #[derive(Clone, Debug, HasViewportPercentage, PartialEq, ToCss)]
     pub struct SpecifiedValue(pub NoCalcLength);
 
     pub mod computed_value {
         pub type T = super::Au;
     }
 
-
     impl ToComputedValue for SpecifiedValue {
         type ComputedValue = computed_value::T;
 
         fn to_computed_value(&self, cx: &Context) -> Au {
             // this value is used in the computation of font-size, so
             // we use the parent size
             let base_size = FontBaseSize::InheritedStyle;
             match self.0 {
@@ -2180,22 +2177,16 @@ https://drafts.csswg.org/css-fonts-4/#lo
                 }
             }
         }
         fn from_computed_value(other: &computed_value::T) -> Self {
             SpecifiedValue(ToComputedValue::from_computed_value(other))
         }
     }
 
-    impl ToCss for SpecifiedValue {
-        fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
-            self.0.to_css(dest)
-        }
-    }
-
     #[inline]
     pub fn get_initial_value() -> computed_value::T {
         Au((NS_MATHML_DEFAULT_SCRIPT_MIN_SIZE_PT as f32 * AU_PER_PT) as i32)
     }
 
     pub fn parse(_context: &ParserContext, _input: &mut Parser) -> Result<SpecifiedValue, ()> {
         debug_assert!(false, "Should be set directly by presentation attributes only.");
         Err(())
--- a/servo/components/style/properties/longhand/inherited_table.mako.rs
+++ b/servo/components/style/properties/longhand/inherited_table.mako.rs
@@ -26,18 +26,18 @@
     use std::fmt;
     use style_traits::ToCss;
     use values::specified::{AllowQuirks, Length};
 
     pub mod computed_value {
         use app_units::Au;
         use properties::animated_properties::Animatable;
 
-        #[derive(Clone, Copy, Debug, PartialEq)]
         #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
+        #[derive(Clone, Copy, Debug, PartialEq, ToCss)]
         pub struct T {
             pub horizontal: Au,
             pub vertical: Au,
         }
 
         /// https://drafts.csswg.org/css-transitions/#animtype-simple-list
         impl Animatable for T {
             #[inline]
@@ -87,24 +87,16 @@
             if let Some(vertical) = self.vertical.as_ref() {
                 try!(dest.write_str(" "));
                 vertical.to_css(dest)?;
             }
             Ok(())
         }
     }
 
-    impl ToCss for computed_value::T {
-        fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
-            try!(self.horizontal.to_css(dest));
-            try!(dest.write_str(" "));
-            self.vertical.to_css(dest)
-        }
-    }
-
     impl ToComputedValue for SpecifiedValue {
         type ComputedValue = computed_value::T;
 
         #[inline]
         fn to_computed_value(&self, context: &Context) -> computed_value::T {
             let horizontal = self.horizontal.to_computed_value(context);
             computed_value::T {
                 horizontal: horizontal,
--- a/servo/components/style/properties/longhand/inherited_text.mako.rs
+++ b/servo/components/style/properties/longhand/inherited_text.mako.rs
@@ -438,18 +438,18 @@
         use app_units::Au;
         use cssparser::Color;
         use smallvec::SmallVec;
 
         #[derive(Clone, PartialEq, Debug)]
         #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
         pub struct T(pub SmallVec<[TextShadow; 1]>);
 
-        #[derive(Clone, PartialEq, Debug)]
         #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
+        #[derive(Clone, PartialEq, Debug, ToCss)]
         pub struct TextShadow {
             pub offset_x: Au,
             pub offset_y: Au,
             pub blur_radius: Au,
             pub color: Color,
         }
     }
 
@@ -463,28 +463,16 @@
             for shadow in iter {
                 dest.write_str(", ")?;
                 shadow.to_css(dest)?;
             }
             Ok(())
         }
     }
 
-    impl ToCss for computed_value::TextShadow {
-        fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
-            self.offset_x.to_css(dest)?;
-            dest.write_str(" ")?;
-            self.offset_y.to_css(dest)?;
-            dest.write_str(" ")?;
-            self.blur_radius.to_css(dest)?;
-            dest.write_str(" ")?;
-            self.color.to_css(dest)
-        }
-    }
-
     impl ToCss for SpecifiedValue {
         fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
             let mut iter = self.0.iter();
             match iter.next() {
                 Some(shadow) => shadow.to_css(dest)?,
                 None => return dest.write_str("none"),
             }
             for shadow in iter {
@@ -762,29 +750,28 @@
             _ => return Err(()),
         };
         Ok(SpecifiedValue::Keyword(keyword_value))
     }
 </%helpers:longhand>
 
 <%helpers:longhand name="text-emphasis-position" animation_value_type="none" products="gecko"
                    spec="https://drafts.csswg.org/css-text-decor/#propdef-text-emphasis-position">
-    use std::fmt;
     use values::computed::ComputedValueAsSpecified;
     use style_traits::ToCss;
 
     define_css_keyword_enum!(HorizontalWritingModeValue:
                              "over" => Over,
                              "under" => Under);
     define_css_keyword_enum!(VerticalWritingModeValue:
                              "right" => Right,
                              "left" => Left);
 
-    #[derive(Debug, Clone, PartialEq)]
     #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
+    #[derive(Debug, Clone, PartialEq, ToCss)]
     pub struct SpecifiedValue(pub HorizontalWritingModeValue, pub VerticalWritingModeValue);
 
     pub mod computed_value {
         pub type T = super::SpecifiedValue;
     }
 
     impl ComputedValueAsSpecified for SpecifiedValue {}
     no_viewport_percentage!(SpecifiedValue);
@@ -799,25 +786,16 @@
             Ok(SpecifiedValue(horizontal, vertical))
         } else {
             let vertical = try!(VerticalWritingModeValue::parse(input));
             let horizontal = try!(HorizontalWritingModeValue::parse(input));
             Ok(SpecifiedValue(horizontal, vertical))
         }
     }
 
-    impl ToCss for SpecifiedValue {
-        fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
-            let SpecifiedValue(horizontal, vertical) = *self;
-            try!(horizontal.to_css(dest));
-            try!(dest.write_char(' '));
-            vertical.to_css(dest)
-        }
-    }
-
     % if product == "gecko":
         impl SpecifiedValue {
             pub fn from_gecko_keyword(kw: u32) -> Self {
                 use gecko_bindings::structs;
 
                 let vert = if kw & structs::NS_STYLE_TEXT_EMPHASIS_POSITION_RIGHT != 0 {
                     VerticalWritingModeValue::Right
                 } else {
--- a/servo/components/style/properties/longhand/list.mako.rs
+++ b/servo/components/style/properties/longhand/list.mako.rs
@@ -109,40 +109,32 @@
             })
         }
     </%helpers:longhand>
 % endif
 
 <%helpers:longhand name="list-style-image" animation_value_type="none"
                    boxed="${product == 'gecko'}"
                    spec="https://drafts.csswg.org/css-lists/#propdef-list-style-image">
-    use std::fmt;
     use values::computed::ComputedValueAsSpecified;
     use values::specified::UrlOrNone;
     pub use self::computed_value::T as SpecifiedValue;
-    use style_traits::ToCss;
 
     pub mod computed_value {
         use values::specified::UrlOrNone;
 
-        #[derive(Debug, Clone, PartialEq)]
         #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
+        #[derive(Debug, Clone, PartialEq, ToCss)]
         pub struct T(pub UrlOrNone);
     }
 
 
     impl ComputedValueAsSpecified for SpecifiedValue {}
     no_viewport_percentage!(SpecifiedValue);
 
-    impl ToCss for SpecifiedValue {
-        fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
-            self.0.to_css(dest)
-        }
-    }
-
     #[inline]
     pub fn get_initial_value() -> computed_value::T {
         computed_value::T(Either::Second(None_))
     }
     #[inline]
     pub fn get_initial_specified_value() -> SpecifiedValue {
         SpecifiedValue(Either::Second(None_))
     }
--- a/servo/components/style/stylesheets.rs
+++ b/servo/components/style/stylesheets.rs
@@ -1754,17 +1754,17 @@ impl<'a, 'b> NestedRuleParser<'a, 'b> {
 
 #[cfg(feature = "servo")]
 fn is_viewport_enabled() -> bool {
     PREFS.get("layout.viewport.enabled").as_boolean().unwrap_or(false)
 }
 
 #[cfg(not(feature = "servo"))]
 fn is_viewport_enabled() -> bool {
-    true
+    false // Gecko doesn't support @viewport
 }
 
 impl<'a, 'b> AtRuleParser for NestedRuleParser<'a, 'b> {
     type Prelude = AtRulePrelude;
     type AtRule = CssRule;
 
     fn parse_prelude(
         &mut self,
--- a/servo/components/style/values/computed/length.rs
+++ b/servo/components/style/values/computed/length.rs
@@ -618,19 +618,19 @@ impl LengthOrNumber {
     }
 }
 
 /// Either a computed `<length>` or the `normal` keyword.
 pub type LengthOrNormal = Either<Length, Normal>;
 
 /// A value suitable for a `min-width`, `min-height`, `width` or `height` property.
 /// See specified/values/length.rs for more details.
-#[derive(Debug, Copy, Clone, PartialEq)]
+#[allow(missing_docs)]
 #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
-#[allow(missing_docs)]
+#[derive(Clone, Copy, Debug, PartialEq, ToCss)]
 pub enum MozLength {
     LengthOrPercentageOrAuto(LengthOrPercentageOrAuto),
     ExtremumLength(ExtremumLength),
 }
 
 impl MozLength {
     /// Returns the `auto` value.
     pub fn auto() -> Self {
@@ -660,32 +660,21 @@ impl ToComputedValue for specified::MozL
                 specified::MozLength::LengthOrPercentageOrAuto(
                     specified::LengthOrPercentageOrAuto::from_computed_value(&lopoa)),
             MozLength::ExtremumLength(ref ext) =>
                 specified::MozLength::ExtremumLength(ext.clone()),
         }
     }
 }
 
-impl ToCss for MozLength {
-    fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
-        match *self {
-            MozLength::LengthOrPercentageOrAuto(lopoa) =>
-                lopoa.to_css(dest),
-            MozLength::ExtremumLength(ext) =>
-                ext.to_css(dest),
-        }
-    }
-}
-
 /// A value suitable for a `max-width` or `max-height` property.
 /// See specified/values/length.rs for more details.
-#[derive(Debug, Copy, Clone, PartialEq)]
+#[allow(missing_docs)]
 #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
-#[allow(missing_docs)]
+#[derive(Clone, Copy, Debug, PartialEq, ToCss)]
 pub enum MaxLength {
     LengthOrPercentageOrNone(LengthOrPercentageOrNone),
     ExtremumLength(ExtremumLength),
 }
 
 impl MaxLength {
     /// Returns the `none` value.
     pub fn none() -> Self {
@@ -713,19 +702,8 @@ impl ToComputedValue for specified::MaxL
             MaxLength::LengthOrPercentageOrNone(ref lopon) =>
                 specified::MaxLength::LengthOrPercentageOrNone(
                     specified::LengthOrPercentageOrNone::from_computed_value(&lopon)),
             MaxLength::ExtremumLength(ref ext) =>
                 specified::MaxLength::ExtremumLength(ext.clone()),
         }
     }
 }
-
-impl ToCss for MaxLength {
-    fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
-        match *self {
-            MaxLength::LengthOrPercentageOrNone(lopon) =>
-                lopon.to_css(dest),
-            MaxLength::ExtremumLength(ext) =>
-                ext.to_css(dest),
-        }
-    }
-}
--- a/servo/components/style/values/computed/mod.rs
+++ b/servo/components/style/values/computed/mod.rs
@@ -476,19 +476,19 @@ pub struct Shadow {
     pub spread_radius: Au,
     pub color: CSSColor,
     pub inset: bool,
 }
 
 /// A `<number>` value.
 pub type Number = CSSFloat;
 
-#[derive(Debug, Copy, Clone, PartialEq)]
+#[allow(missing_docs)]
 #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
-#[allow(missing_docs)]
+#[derive(Clone, Copy, Debug, PartialEq, ToCss)]
 pub enum NumberOrPercentage {
     Percentage(Percentage),
     Number(Number),
 }
 
 impl ToComputedValue for specified::NumberOrPercentage {
     type ComputedValue = NumberOrPercentage;
 
@@ -507,25 +507,16 @@ impl ToComputedValue for specified::Numb
             NumberOrPercentage::Percentage(percentage) =>
                 specified::NumberOrPercentage::Percentage(ToComputedValue::from_computed_value(&percentage)),
             NumberOrPercentage::Number(number) =>
                 specified::NumberOrPercentage::Number(ToComputedValue::from_computed_value(&number)),
         }
     }
 }
 
-impl ToCss for NumberOrPercentage {
-    fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
-        match *self {
-            NumberOrPercentage::Percentage(percentage) => percentage.to_css(dest),
-            NumberOrPercentage::Number(number) => number.to_css(dest),
-        }
-    }
-}
-
 /// A type used for opacity.
 pub type Opacity = CSSFloat;
 
 /// A `<integer>` value.
 pub type Integer = CSSInteger;
 
 /// <integer> | auto
 pub type IntegerOrAuto = Either<CSSInteger, Auto>;
--- a/servo/components/style/values/generics/basic_shape.rs
+++ b/servo/components/style/values/generics/basic_shape.rs
@@ -48,17 +48,17 @@ pub enum ShapeSource<BasicShape, Referen
     Url(SpecifiedUrl),
     Shape(BasicShape, Option<ReferenceBox>),
     Box(ReferenceBox),
     None,
 }
 
 #[allow(missing_docs)]
 #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
-#[derive(Clone, Debug, PartialEq, ToComputedValue)]
+#[derive(Clone, Debug, PartialEq, ToComputedValue, ToCss)]
 pub enum BasicShape<H, V, LengthOrPercentage> {
     Inset(InsetRect<LengthOrPercentage>),
     Circle(Circle<H, V, LengthOrPercentage>),
     Ellipse(Ellipse<H, V, LengthOrPercentage>),
     Polygon(Polygon<LengthOrPercentage>),
 }
 
 /// https://drafts.csswg.org/css-shapes/#funcdef-inset
@@ -148,33 +148,16 @@ impl ToCss for GeometryBox {
             GeometryBox::FillBox => dest.write_str("fill-box"),
             GeometryBox::StrokeBox => dest.write_str("stroke-box"),
             GeometryBox::ViewBox => dest.write_str("view-box"),
             GeometryBox::ShapeBox(s) => s.to_css(dest),
         }
     }
 }
 
-impl<H, V, L> ToCss for BasicShape<H, V, L>
-    where H: ToCss,
-          V: ToCss,
-          L: PartialEq + ToCss,
-          Circle<H, V, L>: ToCss,
-          Ellipse<H, V, L>: ToCss,
-{
-    fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
-        match *self {
-            BasicShape::Inset(ref rect) => rect.to_css(dest),
-            BasicShape::Circle(ref circle) => circle.to_css(dest),
-            BasicShape::Ellipse(ref ellipse) => ellipse.to_css(dest),
-            BasicShape::Polygon(ref polygon) => polygon.to_css(dest),
-        }
-    }
-}
-
 impl<L> ToCss for InsetRect<L>
     where L: ToCss + PartialEq
 {
     fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
         dest.write_str("inset(")?;
         self.rect.to_css(dest)?;
         if let Some(ref radius) = self.round {
             dest.write_str(" round ")?;
--- a/servo/components/style/values/generics/image.rs
+++ b/servo/components/style/values/generics/image.rs
@@ -103,18 +103,18 @@ define_css_keyword_enum!(ShapeExtent:
     "contain" => Contain,
     "cover" => Cover
 );
 no_viewport_percentage!(ShapeExtent);
 impl ComputedValueAsSpecified for ShapeExtent {}
 
 /// A gradient item.
 /// https://drafts.csswg.org/css-images-4/#color-stop-syntax
-#[derive(Clone, Copy, Debug, HasViewportPercentage, PartialEq, ToComputedValue)]
 #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
+#[derive(Clone, Copy, Debug, HasViewportPercentage, PartialEq, ToComputedValue, ToCss)]
 pub enum GradientItem<Color, LengthOrPercentage> {
     /// A color stop.
     ColorStop(ColorStop<Color, LengthOrPercentage>),
     /// An interpolation hint.
     InterpolationHint(LengthOrPercentage),
 }
 
 /// A color stop.
@@ -283,27 +283,16 @@ impl<L, LoP> ToCss for EndingShape<L, Lo
                 x.to_css(dest)?;
                 dest.write_str(" ")?;
                 y.to_css(dest)
             },
         }
     }
 }
 
-impl<C, L> ToCss for GradientItem<C, L>
-    where C: ToCss, L: ToCss,
-{
-    fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
-        match *self {
-            GradientItem::ColorStop(ref stop) => stop.to_css(dest),
-            GradientItem::InterpolationHint(ref hint) => hint.to_css(dest),
-        }
-    }
-}
-
 impl<C, L> fmt::Debug for ColorStop<C, L>
     where C: fmt::Debug, L: fmt::Debug,
 {
     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
         write!(f, "{:?}", self.color)?;
         if let Some(ref pos) = self.position {
             write!(f, " {:?}", pos)?;
         }
--- a/servo/components/style/values/generics/transform.rs
+++ b/servo/components/style/values/generics/transform.rs
@@ -1,20 +1,17 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 //! Generic types for CSS values that are related to transformations.
 
-use std::fmt;
-use style_traits::ToCss;
-
 /// A generic transform origin.
 #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
-#[derive(Clone, Copy, Debug, HasViewportPercentage, PartialEq, ToComputedValue)]
+#[derive(Clone, Copy, Debug, HasViewportPercentage, PartialEq, ToComputedValue, ToCss)]
 pub struct TransformOrigin<H, V, Depth> {
     /// The horizontal origin.
     pub horizontal: H,
     /// The vertical origin.
     pub vertical: V,
     /// The depth.
     pub depth: Depth,
 }
@@ -24,22 +21,8 @@ impl<H, V, D> TransformOrigin<H, V, D> {
     pub fn new(horizontal: H, vertical: V, depth: D) -> Self {
         Self {
             horizontal: horizontal,
             vertical: vertical,
             depth: depth,
         }
     }
 }
-
-impl<H, V, D> ToCss for TransformOrigin<H, V, D>
-    where H: ToCss, V: ToCss, D: ToCss,
-{
-    fn to_css<W>(&self, dest: &mut W) -> fmt::Result
-        where W: fmt::Write,
-    {
-        self.horizontal.to_css(dest)?;
-        dest.write_str(" ")?;
-        self.vertical.to_css(dest)?;
-        dest.write_str(" ")?;
-        self.depth.to_css(dest)
-    }
-}
--- a/servo/components/style/values/mod.rs
+++ b/servo/components/style/values/mod.rs
@@ -29,44 +29,35 @@ pub type CSSInteger = i32;
 
 /// The default font size.
 pub const FONT_MEDIUM_PX: i32 = 16;
 
 define_keyword_type!(None_, "none");
 define_keyword_type!(Auto, "auto");
 define_keyword_type!(Normal, "normal");
 
-#[derive(Clone, Copy, HasViewportPercentage, PartialEq)]
+/// A struct representing one of two kinds of values.
 #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
-/// A struct representing one of two kinds of values.
+#[derive(Clone, Copy, HasViewportPercentage, PartialEq, ToCss)]
 pub enum Either<A, B> {
     /// The first value.
     First(A),
     /// The second kind of value.
     Second(B),
 }
 
 impl<A: Debug, B: Debug> Debug for Either<A, B> {
     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
         match *self {
             Either::First(ref v) => v.fmt(f),
             Either::Second(ref v) => v.fmt(f),
         }
     }
 }
 
-impl<A: ToCss, B: ToCss> ToCss for Either<A, B> {
-    fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
-        match *self {
-            Either::First(ref v) => v.to_css(dest),
-            Either::Second(ref v) => v.to_css(dest),
-        }
-    }
-}
-
 impl<A: Parse, B: Parse> Parse for Either<A, B> {
     fn parse(context: &ParserContext, input: &mut Parser) -> Result<Either<A, B>, ()> {
         if let Ok(v) = input.try(|i| A::parse(context, i)) {
             Ok(Either::First(v))
         } else {
             B::parse(context, input).map(Either::Second)
         }
     }
--- a/servo/components/style/values/specified/align.rs
+++ b/servo/components/style/values/specified/align.rs
@@ -204,17 +204,17 @@ impl Parse for AlignJustifyContent {
         }
         Err(())
     }
 }
 
 /// Value of the `align-self` or `justify-self` property.
 ///
 /// https://drafts.csswg.org/css-align/#self-alignment
-#[derive(Copy, Clone, Debug, Eq, PartialEq)]
+#[derive(Copy, Clone, Debug, Eq, PartialEq, ToCss)]
 pub struct AlignJustifySelf(pub AlignFlags);
 
 impl AlignJustifySelf {
     /// The initial value 'auto'
     #[inline]
     pub fn auto() -> Self {
         AlignJustifySelf(ALIGN_AUTO)
     }
@@ -223,22 +223,16 @@ impl AlignJustifySelf {
     #[inline]
     pub fn has_extra_flags(self) -> bool {
         self.0.intersects(ALIGN_FLAG_BITS)
     }
 }
 
 no_viewport_percentage!(AlignJustifySelf);
 
-impl ToCss for AlignJustifySelf {
-    fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
-        self.0.to_css(dest)
-    }
-}
-
 impl Parse for AlignJustifySelf {
     // auto | normal | stretch | <baseline-position> |
     // [ <overflow-position>? && <self-position> ]
     fn parse(_: &ParserContext, input: &mut Parser) -> Result<Self, ()> {
         // auto | normal | stretch | <baseline-position>
         if let Ok(value) = input.try(parse_auto_normal_stretch_baseline) {
             return Ok(AlignJustifySelf(value))
         }
@@ -248,17 +242,17 @@ impl Parse for AlignJustifySelf {
         }
         Err(())
     }
 }
 
 /// Value of the `align-items` property
 ///
 /// https://drafts.csswg.org/css-align/#self-alignment
-#[derive(Copy, Clone, Debug, Eq, PartialEq)]
+#[derive(Copy, Clone, Debug, Eq, PartialEq, ToCss)]
 pub struct AlignItems(pub AlignFlags);
 
 impl AlignItems {
     /// The initial value 'normal'
     #[inline]
     pub fn normal() -> Self {
         AlignItems(ALIGN_NORMAL)
     }
@@ -267,22 +261,16 @@ impl AlignItems {
     #[inline]
     pub fn has_extra_flags(self) -> bool {
         self.0.intersects(ALIGN_FLAG_BITS)
     }
 }
 
 no_viewport_percentage!(AlignItems);
 
-impl ToCss for AlignItems {
-    fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
-        self.0.to_css(dest)
-    }
-}
-
 impl Parse for AlignItems {
     // normal | stretch | <baseline-position> |
     // [ <overflow-position>? && <self-position> ]
     fn parse(_: &ParserContext, input: &mut Parser) -> Result<Self, ()> {
         // normal | stretch | <baseline-position>
         if let Ok(value) = input.try(parse_normal_stretch_baseline) {
             return Ok(AlignItems(value))
         }
@@ -292,17 +280,17 @@ impl Parse for AlignItems {
         }
         Err(())
     }
 }
 
 /// Value of the `justify-items` property
 ///
 /// https://drafts.csswg.org/css-align/#justify-items-property
-#[derive(Copy, Clone, Debug, Eq, PartialEq)]
+#[derive(Copy, Clone, Debug, Eq, PartialEq, ToCss)]
 pub struct JustifyItems(pub AlignFlags);
 
 impl JustifyItems {
     /// The initial value 'auto'
     #[inline]
     pub fn auto() -> Self {
         JustifyItems(ALIGN_AUTO)
     }
@@ -311,22 +299,16 @@ impl JustifyItems {
     #[inline]
     pub fn has_extra_flags(self) -> bool {
         self.0.intersects(ALIGN_FLAG_BITS)
     }
 }
 
 no_viewport_percentage!(JustifyItems);
 
-impl ToCss for JustifyItems {
-    fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
-        self.0.to_css(dest)
-    }
-}
-
 impl Parse for JustifyItems {
     // auto | normal | stretch | <baseline-position> |
     // [ <overflow-position>? && <self-position> ]
     // [ legacy && [ left | right | center ] ]
     fn parse(_: &ParserContext, input: &mut Parser) -> Result<Self, ()> {
         // auto | normal | stretch | <baseline-position>
         if let Ok(value) = input.try(parse_auto_normal_stretch_baseline) {
             return Ok(JustifyItems(value))
--- a/servo/components/style/values/specified/length.rs
+++ b/servo/components/style/values/specified/length.rs
@@ -526,43 +526,34 @@ impl NoCalcLength {
         NoCalcLength::Absolute(AbsoluteLength::Px(px_value))
     }
 }
 
 /// An extension to `NoCalcLength` to parse `calc` expressions.
 /// This is commonly used for the `<length>` values.
 ///
 /// https://drafts.csswg.org/css-values/#lengths
-#[derive(Clone, Debug, HasViewportPercentage, PartialEq)]
 #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
+#[derive(Clone, Debug, HasViewportPercentage, PartialEq, ToCss)]
 pub enum Length {
     /// The internal length type that cannot parse `calc`
     NoCalc(NoCalcLength),
     /// A calc expression.
     ///
     /// https://drafts.csswg.org/css-values/#calc-notation
     Calc(Box<CalcLengthOrPercentage>),
 }
 
 impl From<NoCalcLength> for Length {
     #[inline]
     fn from(len: NoCalcLength) -> Self {
         Length::NoCalc(len)
     }
 }
 
-impl ToCss for Length {
-    fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
-        match *self {
-            Length::NoCalc(ref inner) => inner.to_css(dest),
-            Length::Calc(ref calc) => calc.to_css(dest),
-        }
-    }
-}
-
 impl Mul<CSSFloat> for Length {
     type Output = Length;
 
     #[inline]
     fn mul(self, scalar: CSSFloat) -> Length {
         match self {
             Length::NoCalc(inner) => Length::NoCalc(inner * scalar),
             Length::Calc(..) => panic!("Can't multiply Calc!"),
@@ -753,19 +744,19 @@ impl Parse for Percentage {
     fn parse(_context: &ParserContext, input: &mut Parser) -> Result<Self, ()> {
         Self::parse_with_clamping_mode(input, AllowedNumericType::All)
     }
 }
 
 impl ComputedValueAsSpecified for Percentage {}
 
 /// A length or a percentage value.
-#[derive(Clone, Debug, HasViewportPercentage, PartialEq)]
+#[allow(missing_docs)]
 #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
-#[allow(missing_docs)]
+#[derive(Clone, Debug, HasViewportPercentage, PartialEq, ToCss)]
 pub enum LengthOrPercentage {
     Length(NoCalcLength),
     Percentage(Percentage),
     Calc(Box<CalcLengthOrPercentage>),
 }
 
 impl From<Length> for LengthOrPercentage {
     fn from(len: Length) -> LengthOrPercentage {
@@ -785,26 +776,16 @@ impl From<NoCalcLength> for LengthOrPerc
 
 impl From<Percentage> for LengthOrPercentage {
     #[inline]
     fn from(pc: Percentage) -> Self {
         LengthOrPercentage::Percentage(pc)
     }
 }
 
-impl ToCss for LengthOrPercentage {
-    fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
-        match *self {
-            LengthOrPercentage::Length(ref length) => length.to_css(dest),
-            LengthOrPercentage::Percentage(percentage) => percentage.to_css(dest),
-            LengthOrPercentage::Calc(ref calc) => calc.to_css(dest),
-        }
-    }
-}
-
 impl LengthOrPercentage {
     #[inline]
     /// Returns a `zero` length.
     pub fn zero() -> LengthOrPercentage {
         LengthOrPercentage::Length(NoCalcLength::zero())
     }
 
     fn parse_internal(context: &ParserContext,
@@ -1208,35 +1189,24 @@ impl LengthOrNumber {
     pub fn zero() -> Self {
         Either::Second(Number::new(0.))
     }
 }
 
 /// A value suitable for a `min-width` or `min-height` property.
 /// Unlike `max-width` or `max-height` properties, a MozLength can be
 /// `auto`, and cannot be `none`.
-#[derive(Clone, Debug, HasViewportPercentage, PartialEq)]
+#[allow(missing_docs)]
 #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
-#[allow(missing_docs)]
+#[derive(Clone, Debug, HasViewportPercentage, PartialEq, ToCss)]
 pub enum MozLength {
     LengthOrPercentageOrAuto(LengthOrPercentageOrAuto),
     ExtremumLength(ExtremumLength),
 }
 
-impl ToCss for MozLength {
-    fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
-        match *self {
-            MozLength::LengthOrPercentageOrAuto(ref lopoa) =>
-                lopoa.to_css(dest),
-            MozLength::ExtremumLength(ref ext) =>
-                ext.to_css(dest),
-        }
-    }
-}
-
 impl Parse for MozLength {
     fn parse(context: &ParserContext, input: &mut Parser) -> Result<Self, ()> {
         MozLength::parse_quirky(context, input, AllowQuirks::No)
     }
 }
 
 impl MozLength {
     /// Parses, with quirks.
@@ -1245,35 +1215,24 @@ impl MozLength {
                         allow_quirks: AllowQuirks) -> Result<Self, ()> {
         input.try(ExtremumLength::parse).map(MozLength::ExtremumLength)
             .or_else(|()| input.try(|i| LengthOrPercentageOrAuto::parse_non_negative_quirky(context, i, allow_quirks))
                                .map(MozLength::LengthOrPercentageOrAuto))
     }
 }
 
 /// A value suitable for a `max-width` or `max-height` property.
-#[derive(Clone, Debug, HasViewportPercentage, PartialEq)]
+#[allow(missing_docs)]
 #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
-#[allow(missing_docs)]
+#[derive(Clone, Debug, HasViewportPercentage, PartialEq, ToCss)]
 pub enum MaxLength {
     LengthOrPercentageOrNone(LengthOrPercentageOrNone),
     ExtremumLength(ExtremumLength),
 }
 
-impl ToCss for MaxLength {
-    fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
-        match *self {
-            MaxLength::LengthOrPercentageOrNone(ref lopon) =>
-                lopon.to_css(dest),
-            MaxLength::ExtremumLength(ref ext) =>
-                ext.to_css(dest),
-        }
-    }
-}
-
 impl Parse for MaxLength {
     fn parse(context: &ParserContext, input: &mut Parser) -> Result<Self, ()> {
         MaxLength::parse_quirky(context, input, AllowQuirks::No)
     }
 }
 
 impl MaxLength {
     /// Parses, with quirks.
--- a/servo/components/style/values/specified/mod.rs
+++ b/servo/components/style/values/specified/mod.rs
@@ -647,19 +647,19 @@ impl ToCss for Number {
             dest.write_str(")")?;
         }
         Ok(())
     }
 }
 
 /// <number-percentage>
 /// Accepts only non-negative numbers.
-#[derive(Clone, Copy, Debug, PartialEq)]
+#[allow(missing_docs)]
 #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
-#[allow(missing_docs)]
+#[derive(Clone, Copy, Debug, PartialEq, ToCss)]
 pub enum NumberOrPercentage {
     Percentage(Percentage),
     Number(Number),
 }
 
 no_viewport_percentage!(NumberOrPercentage);
 
 impl NumberOrPercentage {
@@ -681,28 +681,19 @@ impl NumberOrPercentage {
 }
 
 impl Parse for NumberOrPercentage {
     fn parse(context: &ParserContext, input: &mut Parser) -> Result<Self, ()> {
         Self::parse_with_clamping_mode(context, input, AllowedNumericType::All)
     }
 }
 
-impl ToCss for NumberOrPercentage {
-    fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
-        match *self {
-            NumberOrPercentage::Percentage(percentage) => percentage.to_css(dest),
-            NumberOrPercentage::Number(number) => number.to_css(dest),
-        }
-    }
-}
-
-#[derive(Clone, Copy, Debug, PartialEq, PartialOrd)]
+#[allow(missing_docs)]
 #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
-#[allow(missing_docs)]
+#[derive(Clone, Copy, Debug, PartialEq, PartialOrd, ToCss)]
 pub struct Opacity(Number);
 
 no_viewport_percentage!(Opacity);
 
 impl Parse for Opacity {
     fn parse(context: &ParserContext, input: &mut Parser) -> Result<Self, ()> {
         parse_number(context, input).map(Opacity)
     }
@@ -717,22 +708,16 @@ impl ToComputedValue for Opacity {
     }
 
     #[inline]
     fn from_computed_value(computed: &CSSFloat) -> Self {
         Opacity(Number::from_computed_value(computed))
     }
 }
 
-impl ToCss for Opacity {
-    fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
-        self.0.to_css(dest)
-    }
-}
-
 #[derive(Clone, Copy, Debug, PartialEq, PartialOrd)]
 #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
 #[allow(missing_docs)]
 pub struct Integer {
     value: CSSInteger,
     was_calc: bool,
 }
 
--- a/servo/components/style_derive/lib.rs
+++ b/servo/components/style_derive/lib.rs
@@ -6,20 +6,27 @@ extern crate proc_macro;
 #[macro_use] extern crate quote;
 extern crate syn;
 extern crate synstructure;
 
 use proc_macro::TokenStream;
 
 mod has_viewport_percentage;
 mod to_computed_value;
+mod to_css;
 
 #[proc_macro_derive(HasViewportPercentage)]
 pub fn derive_has_viewport_percentage(stream: TokenStream) -> TokenStream {
     let input = syn::parse_derive_input(&stream.to_string()).unwrap();
     has_viewport_percentage::derive(input).to_string().parse().unwrap()
 }
 
 #[proc_macro_derive(ToComputedValue)]
 pub fn derive_to_computed_value(stream: TokenStream) -> TokenStream {
     let input = syn::parse_derive_input(&stream.to_string()).unwrap();
     to_computed_value::derive(input).to_string().parse().unwrap()
 }
+
+#[proc_macro_derive(ToCss)]
+pub fn derive_to_css(stream: TokenStream) -> TokenStream {
+    let input = syn::parse_derive_input(&stream.to_string()).unwrap();
+    to_css::derive(input).to_string().parse().unwrap()
+}
new file mode 100644
--- /dev/null
+++ b/servo/components/style_derive/to_css.rs
@@ -0,0 +1,70 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+use quote;
+use syn;
+use synstructure;
+
+pub fn derive(input: syn::DeriveInput) -> quote::Tokens {
+    let name = &input.ident;
+    let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl();
+    let mut where_clause = where_clause.clone();
+    for param in &input.generics.ty_params {
+        where_clause.predicates.push(where_predicate(syn::Ty::Path(None, param.ident.clone().into())))
+    }
+
+    let style = synstructure::BindStyle::Ref.into();
+    let match_body = synstructure::each_variant(&input, &style, |bindings, _| {
+        if bindings.is_empty() {
+            panic!("unit variants are not yet supported");
+        }
+        let (first, rest) = bindings.split_first().expect("unit variants are not yet supported");
+        where_clause.predicates.push(where_predicate(first.field.ty.clone()));
+        let mut expr = quote! {
+            ::style_traits::ToCss::to_css(#first, dest)
+        };
+        for binding in rest {
+            where_clause.predicates.push(where_predicate(binding.field.ty.clone()));
+            expr = quote! {
+                #expr?;
+                dest.write_str(" ")?;
+                ::style_traits::ToCss::to_css(#binding, dest)
+            };
+        }
+        Some(expr)
+    });
+
+    quote! {
+        impl #impl_generics ::style_traits::ToCss for #name #ty_generics #where_clause {
+            #[allow(unused_variables, unused_imports)]
+            #[inline]
+            fn to_css<W>(&self, dest: &mut W) -> ::std::fmt::Result
+            where
+                W: ::std::fmt::Write
+            {
+                match *self {
+                    #match_body
+                }
+            }
+        }
+    }
+}
+
+/// `#ty: ::style_traits::ToCss`
+fn where_predicate(ty: syn::Ty) -> syn::WherePredicate {
+    syn::WherePredicate::BoundPredicate(syn::WhereBoundPredicate {
+        bound_lifetimes: vec![],
+        bounded_ty: ty,
+        bounds: vec![syn::TyParamBound::Trait(
+            syn::PolyTraitRef {
+                bound_lifetimes: vec![],
+                trait_ref: syn::Path {
+                    global: true,
+                    segments: vec!["style_traits".into(), "ToCss".into()],
+                },
+            },
+            syn::TraitBoundModifier::None
+        )],
+    })
+}