servo: Merge #19903 - Derive more Parse implementations (fixes #19827) (from servo:derive-all-the-things); r=emilio
authorAnthony Ramine <n.oxyde@gmail.com>
Thu, 01 Feb 2018 05:52:08 -0600
changeset 402054 9352e0738ccf95cb541b0926d11a896024a0183b
parent 402053 b4a42010118a987173f6b82dfb569c743f795d4c
child 402055 05d667efdfb8639bf21a4b93b9f641ab672b5b78
push id33361
push userncsoregi@mozilla.com
push dateThu, 01 Feb 2018 18:09:52 +0000
treeherdermozilla-central@946b943acc04 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersemilio
bugs19903, 19827
milestone60.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
servo: Merge #19903 - Derive more Parse implementations (fixes #19827) (from servo:derive-all-the-things); r=emilio Fixes #19827. Source-Repo: https://github.com/servo/servo Source-Revision: b4339ab5c8fc31d5eeb7e1fae55cab05e400ec9e
servo/components/style/font_face.rs
servo/components/style/gecko/conversions.rs
servo/components/style/gecko/values.rs
servo/components/style/lib.rs
servo/components/style/macros.rs
servo/components/style/properties/gecko.mako.rs
servo/components/style/properties/helpers.mako.rs
servo/components/style/properties/longhand/border.mako.rs
servo/components/style/properties/longhand/box.mako.rs
servo/components/style/properties/longhand/inherited_text.mako.rs
servo/components/style/properties/shorthand/border.mako.rs
servo/components/style/values/computed/mod.rs
servo/components/style/values/computed/transform.rs
servo/components/style/values/generics/basic_shape.rs
servo/components/style/values/generics/grid.rs
servo/components/style/values/generics/image.rs
servo/components/style/values/generics/mod.rs
servo/components/style/values/generics/transform.rs
servo/components/style/values/mod.rs
servo/components/style/values/specified/box.rs
servo/components/style/values/specified/color.rs
servo/components/style/values/specified/mod.rs
servo/components/style/values/specified/outline.rs
servo/components/style/values/specified/transform.rs
servo/components/style_derive/lib.rs
servo/components/style_derive/parse.rs
servo/components/style_derive/to_css.rs
servo/components/style_traits/values.rs
servo/components/style_traits/viewport.rs
servo/tests/unit/style/parsing/border.rs
--- a/servo/components/style/font_face.rs
+++ b/servo/components/style/font_face.rs
@@ -62,23 +62,27 @@ impl ToCss for UrlSource {
     {
         self.url.to_css(dest)
     }
 }
 
 /// A font-display value for a @font-face rule.
 /// The font-display descriptor determines how a font face is displayed based
 /// on whether and when it is downloaded and ready to use.
-define_css_keyword_enum!(FontDisplay:
-                         "auto" => Auto,
-                         "block" => Block,
-                         "swap" => Swap,
-                         "fallback" => Fallback,
-                         "optional" => Optional);
-add_impls_for_keyword_enum!(FontDisplay);
+#[allow(missing_docs)]
+#[cfg_attr(feature = "servo", derive(Deserialize, Serialize))]
+#[derive(Clone, Copy, Debug, Eq, MallocSizeOf, Parse, PartialEq)]
+#[derive(ToComputedValue, ToCss)]
+pub enum FontDisplay {
+    Auto,
+    Block,
+    Swap,
+    Fallback,
+    Optional,
+}
 
 /// A font-weight value for a @font-face rule.
 /// The font-weight CSS property specifies the weight or boldness of the font.
 #[cfg(feature = "gecko")]
 #[derive(Clone, Debug, Eq, PartialEq, ToCss)]
 pub enum FontWeight {
     /// Numeric font weights for fonts that provide more than just normal and bold.
     Weight(font_weight::T),
--- a/servo/components/style/gecko/conversions.rs
+++ b/servo/components/style/gecko/conversions.rs
@@ -702,19 +702,19 @@ pub mod basic_shape {
                     GenericBasicShape::Ellipse(Ellipse {
                         semiaxis_x: (&other.mCoordinates[0]).into(),
                         semiaxis_y: (&other.mCoordinates[1]).into(),
                         position: (&other.mPosition).into()
                     })
                 }
                 StyleBasicShapeType::Polygon => {
                     let fill_rule = if other.mFillRule == StyleFillRule::Evenodd {
-                        FillRule::EvenOdd
+                        FillRule::Evenodd
                     } else {
-                        FillRule::NonZero
+                        FillRule::Nonzero
                     };
                     let mut coords = Vec::with_capacity(other.mCoordinates.len() / 2);
                     for i in 0..(other.mCoordinates.len() / 2) {
                         let x = 2 * i;
                         let y = x + 1;
                         coords.push((LengthOrPercentage::from_gecko_style_coord(&other.mCoordinates[x])
                                     .expect("polygon() coordinate should be a length, percentage, or calc value"),
                                 LengthOrPercentage::from_gecko_style_coord(&other.mCoordinates[y])
--- a/servo/components/style/gecko/values.rs
+++ b/servo/components/style/gecko/values.rs
@@ -343,35 +343,35 @@ impl GeckoStyleCoordConvertible for Norm
 }
 
 impl GeckoStyleCoordConvertible for ExtremumLength {
     fn to_gecko_style_coord<T: CoordDataMut>(&self, coord: &mut T) {
         use gecko_bindings::structs::{NS_STYLE_WIDTH_AVAILABLE, NS_STYLE_WIDTH_FIT_CONTENT};
         use gecko_bindings::structs::{NS_STYLE_WIDTH_MAX_CONTENT, NS_STYLE_WIDTH_MIN_CONTENT};
         coord.set_value(CoordDataValue::Enumerated(
             match *self {
-                ExtremumLength::MaxContent => NS_STYLE_WIDTH_MAX_CONTENT,
-                ExtremumLength::MinContent => NS_STYLE_WIDTH_MIN_CONTENT,
-                ExtremumLength::FitContent => NS_STYLE_WIDTH_FIT_CONTENT,
-                ExtremumLength::FillAvailable => NS_STYLE_WIDTH_AVAILABLE,
+                ExtremumLength::MozMaxContent => NS_STYLE_WIDTH_MAX_CONTENT,
+                ExtremumLength::MozMinContent => NS_STYLE_WIDTH_MIN_CONTENT,
+                ExtremumLength::MozFitContent => NS_STYLE_WIDTH_FIT_CONTENT,
+                ExtremumLength::MozAvailable => NS_STYLE_WIDTH_AVAILABLE,
             }
         ))
     }
 
     fn from_gecko_style_coord<T: CoordData>(coord: &T) -> Option<Self> {
         use gecko_bindings::structs::{NS_STYLE_WIDTH_AVAILABLE, NS_STYLE_WIDTH_FIT_CONTENT};
         use gecko_bindings::structs::{NS_STYLE_WIDTH_MAX_CONTENT, NS_STYLE_WIDTH_MIN_CONTENT};
         match coord.as_value() {
             CoordDataValue::Enumerated(NS_STYLE_WIDTH_MAX_CONTENT) =>
-                Some(ExtremumLength::MaxContent),
+                Some(ExtremumLength::MozMaxContent),
             CoordDataValue::Enumerated(NS_STYLE_WIDTH_MIN_CONTENT) =>
-                Some(ExtremumLength::MinContent),
+                Some(ExtremumLength::MozMinContent),
             CoordDataValue::Enumerated(NS_STYLE_WIDTH_FIT_CONTENT) =>
-                Some(ExtremumLength::FitContent),
-            CoordDataValue::Enumerated(NS_STYLE_WIDTH_AVAILABLE) => Some(ExtremumLength::FillAvailable),
+                Some(ExtremumLength::MozFitContent),
+            CoordDataValue::Enumerated(NS_STYLE_WIDTH_AVAILABLE) => Some(ExtremumLength::MozAvailable),
             _ => None,
         }
     }
 }
 
 impl GeckoStyleCoordConvertible for MozLength {
     fn to_gecko_style_coord<T: CoordDataMut>(&self, coord: &mut T) {
         match *self {
--- a/servo/components/style/lib.rs
+++ b/servo/components/style/lib.rs
@@ -20,18 +20,16 @@
 //! Major dependencies are the [cssparser][cssparser] and [selectors][selectors]
 //! crates.
 //!
 //! [cssparser]: ../cssparser/index.html
 //! [selectors]: ../selectors/index.html
 
 #![deny(missing_docs)]
 
-#![recursion_limit = "500"]  // For define_css_keyword_enum! in -moz-appearance
-
 extern crate app_units;
 extern crate arrayvec;
 extern crate atomic_refcell;
 #[macro_use]
 extern crate bitflags;
 #[allow(unused_extern_crates)] extern crate byteorder;
 #[cfg(feature = "gecko")] #[macro_use] #[no_link] extern crate cfg_if;
 #[macro_use] extern crate cssparser;
@@ -67,17 +65,16 @@ extern crate selectors;
 pub extern crate servo_arc;
 #[cfg(feature = "servo")] #[macro_use] extern crate servo_atoms;
 #[cfg(feature = "servo")] extern crate servo_config;
 #[cfg(feature = "servo")] extern crate servo_url;
 extern crate smallbitvec;
 extern crate smallvec;
 #[macro_use]
 extern crate style_derive;
-#[macro_use]
 extern crate style_traits;
 extern crate time;
 extern crate uluru;
 extern crate unicode_bidi;
 #[allow(unused_extern_crates)]
 extern crate unicode_segmentation;
 
 #[macro_use]
--- a/servo/components/style/macros.rs
+++ b/servo/components/style/macros.rs
@@ -60,77 +60,16 @@ macro_rules! try_match_ident_ignore_asci
             $( $match_body )*
             _ => return Err(location.new_custom_error(
                 ::selectors::parser::SelectorParseErrorKind::UnexpectedIdent(ident.clone())
             ))
         }
     }}
 }
 
-macro_rules! define_numbered_css_keyword_enum {
-    ($name: ident: $( $css: expr => $variant: ident = $value: expr ),+,) => {
-        define_numbered_css_keyword_enum!($name: $( $css => $variant = $value ),+);
-    };
-    ($name: ident: $( $css: expr => $variant: ident = $value: expr ),+) => {
-        #[allow(non_camel_case_types, missing_docs)]
-        #[derive(Clone, Copy, Debug, Eq, MallocSizeOf, Ord, PartialEq, PartialOrd)]
-        #[cfg_attr(feature = "servo", derive(Deserialize, Serialize))]
-        pub enum $name {
-            $( $variant = $value ),+
-        }
-
-        impl $crate::parser::Parse for $name {
-            fn parse<'i, 't>(
-                _context: &$crate::parser::ParserContext,
-                input: &mut ::cssparser::Parser<'i, 't>,
-            ) -> Result<$name, ::style_traits::ParseError<'i>> {
-                try_match_ident_ignore_ascii_case! { input,
-                    $( $css => Ok($name::$variant), )+
-                }
-            }
-        }
-
-        impl ::style_traits::ToCss for $name {
-            fn to_css<W>(
-                &self,
-                dest: &mut ::style_traits::CssWriter<W>,
-            ) -> ::std::fmt::Result
-            where
-                W: ::std::fmt::Write,
-            {
-                match *self {
-                    $( $name::$variant => dest.write_str($css) ),+
-                }
-            }
-        }
-    }
-}
-
-/// A macro for implementing `ToComputedValue`, and `Parse` traits for
-/// the enums defined using `define_css_keyword_enum` macro.
-///
-/// NOTE: We should either move `Parse` trait to `style_traits`
-/// or `define_css_keyword_enum` macro to this crate, but that
-/// may involve significant cleanup in both the crates.
-macro_rules! add_impls_for_keyword_enum {
-    ($name:ident) => {
-        impl $crate::parser::Parse for $name {
-            #[inline]
-            fn parse<'i, 't>(
-                _context: &$crate::parser::ParserContext,
-                input: &mut ::cssparser::Parser<'i, 't>,
-            ) -> Result<Self, ::style_traits::ParseError<'i>> {
-                $name::parse(input)
-            }
-        }
-
-        trivial_to_computed_value!($name);
-    };
-}
-
 macro_rules! define_keyword_type {
     ($name: ident, $css: expr) => {
         #[allow(missing_docs)]
         #[derive(Animate, Clone, ComputeSquaredDistance, Copy, MallocSizeOf, PartialEq)]
         #[derive(ToAnimatedZero, ToComputedValue, ToCss)]
         pub struct $name;
 
         impl fmt::Debug for $name {
--- a/servo/components/style/properties/gecko.mako.rs
+++ b/servo/components/style/properties/gecko.mako.rs
@@ -57,16 +57,17 @@ use selector_parser::PseudoElement;
 use servo_arc::{Arc, RawOffsetArc};
 use std::mem::{forget, uninitialized, transmute, zeroed};
 use std::{cmp, ops, ptr};
 use values::{self, Auto, CustomIdent, Either, KeyframesName, None_};
 use values::computed::{NonNegativeLength, ToComputedValue, Percentage};
 use values::computed::font::{FontSize, SingleFontFamily};
 use values::computed::effects::{BoxShadow, Filter, SimpleShadow};
 use values::computed::outline::OutlineStyle;
+use values::generics::transform::TransformStyle;
 use computed_values::border_style;
 
 pub mod style_structs {
     % for style_struct in data.style_structs:
     pub use super::${style_struct.gecko_struct_name} as ${style_struct.name};
     % endfor
 }
 
@@ -3340,16 +3341,35 @@ fn static_assert() {
             }
         }
     }
 
     pub fn reset_transition_property(&mut self, other: &Self) {
         self.copy_transition_property_from(other)
     }
 
+    // Hand-written because the Mako helpers transform `Preserve3d` into `PRESERVE3D`.
+    pub fn set_transform_style(&mut self, v: TransformStyle) {
+        self.gecko.mTransformStyle = match v {
+            TransformStyle::Flat => structs::NS_STYLE_TRANSFORM_STYLE_FLAT as u8,
+            TransformStyle::Preserve3d => structs::NS_STYLE_TRANSFORM_STYLE_PRESERVE_3D as u8,
+        };
+    }
+
+    // Hand-written because the Mako helpers transform `Preserve3d` into `PRESERVE3D`.
+    pub fn clone_transform_style(&self) -> TransformStyle {
+        match self.gecko.mTransformStyle as u32 {
+            structs::NS_STYLE_TRANSFORM_STYLE_FLAT => TransformStyle::Flat,
+            structs::NS_STYLE_TRANSFORM_STYLE_PRESERVE_3D => TransformStyle::Preserve3d,
+            _ => panic!("illegal transform style"),
+        }
+    }
+
+    ${impl_simple_copy('transform_style', 'mTransformStyle')}
+
     ${impl_transition_count('property', 'Property')}
 
     pub fn animations_equals(&self, other: &Self) -> bool {
         return self.gecko.mAnimationNameCount == other.gecko.mAnimationNameCount
             && self.gecko.mAnimationDelayCount == other.gecko.mAnimationDelayCount
             && self.gecko.mAnimationDirectionCount == other.gecko.mAnimationDirectionCount
             && self.gecko.mAnimationDurationCount == other.gecko.mAnimationDurationCount
             && self.gecko.mAnimationFillModeCount == other.gecko.mAnimationFillModeCount
@@ -5076,17 +5096,17 @@ fn static_assert() {
                             shape.mCoordinates.set_len(poly.coordinates.len() as u32 * 2);
                         }
                         for (i, coord) in poly.coordinates.iter().enumerate() {
                             shape.mCoordinates[2 * i].leaky_set_null();
                             shape.mCoordinates[2 * i + 1].leaky_set_null();
                             coord.0.to_gecko_style_coord(&mut shape.mCoordinates[2 * i]);
                             coord.1.to_gecko_style_coord(&mut shape.mCoordinates[2 * i + 1]);
                         }
-                        shape.mFillRule = if poly.fill == FillRule::EvenOdd {
+                        shape.mFillRule = if poly.fill == FillRule::Evenodd {
                             StyleFillRule::Evenodd
                         } else {
                             StyleFillRule::Nonzero
                         };
                     }
                 }
 
                 ${ident}.mReferenceBox = maybe_box.map(Into::into)
--- a/servo/components/style/properties/helpers.mako.rs
+++ b/servo/components/style/properties/helpers.mako.rs
@@ -419,30 +419,23 @@
             'custom_consts', 'gecko_inexhaustive',
         ]}
         keyword = keyword=Keyword(name, values, **keyword_kwargs)
     %>
     <%call expr="longhand(name, keyword=Keyword(name, values, **keyword_kwargs), **kwargs)">
         use properties::longhands::system_font::SystemFont;
 
         pub mod computed_value {
-            use cssparser::Parser;
-            use parser::{Parse, ParserContext};
-
-            use style_traits::ParseError;
-            define_css_keyword_enum! { T:
-                % for value in keyword.values_for(product):
-                    "${value}" => ${to_camel_case(value)},
-                % endfor
-            }
-
-            impl Parse for T {
-                fn parse<'i, 't>(_: &ParserContext, input: &mut Parser<'i, 't>) -> Result<Self, ParseError<'i>> {
-                    T::parse(input)
-                }
+            #[cfg_attr(feature = "servo", derive(Deserialize, Serialize))]
+            #[derive(Clone, Copy, Debug, Eq, Hash, MallocSizeOf, Parse)]
+            #[derive(PartialEq, ToCss)]
+            pub enum T {
+            % for value in keyword.values_for(product):
+                ${to_camel_case(value)},
+            % endfor
             }
 
             ${gecko_keyword_conversion(keyword, keyword.values_for(product), type="T", cast_to="i32")}
         }
 
         #[cfg_attr(feature = "gecko", derive(MallocSizeOf))]
         #[derive(Clone, Copy, Debug, Eq, PartialEq, ToCss)]
         pub enum SpecifiedValue {
@@ -599,58 +592,57 @@
             'extra_gecko_values', 'extra_servo_values',
             'aliases', 'extra_gecko_aliases', 'custom_consts',
             'gecko_inexhaustive', 'gecko_strip_moz_prefix',
         ]}
     %>
 
     <%def name="inner_body(keyword, extra_specified=None, needs_conversion=False)">
         % if extra_specified or keyword.aliases_for(product):
-            define_css_keyword_enum! { SpecifiedValue:
-                values {
-                    % for value in keyword.values_for(product) + (extra_specified or "").split():
-                        "${value}" => ${to_camel_case(value)},
-                    % endfor
-                }
-                aliases {
-                    % for alias, value in keyword.aliases_for(product).iteritems():
-                        "${alias}" => ${to_camel_case(value)},
-                    % endfor
-                }
+            #[cfg_attr(feature = "servo", derive(Deserialize, Serialize))]
+            #[derive(Clone, Copy, Debug, Eq, MallocSizeOf, Parse, PartialEq, ToCss)]
+            pub enum SpecifiedValue {
+                % for value in keyword.values_for(product) + (extra_specified or "").split():
+                <%
+                    aliases = []
+                    for alias, v in keyword.aliases_for(product).iteritems():
+                        if value == v:
+                            aliases.append(alias)
+                %>
+                % if aliases:
+                #[css(aliases = "${','.join(aliases)}")]
+                % endif
+                ${to_camel_case(value)},
+                % endfor
             }
         % else:
             pub use self::computed_value::T as SpecifiedValue;
         % endif
         pub mod computed_value {
-            define_css_keyword_enum! { T:
-                % for value in data.longhands_by_name[name].keyword.values_for(product):
-                    "${value}" => ${to_camel_case(value)},
-                % endfor
+            #[cfg_attr(feature = "servo", derive(Deserialize, Serialize))]
+            #[derive(Clone, Copy, Debug, Eq, MallocSizeOf, Parse, PartialEq, ToCss)]
+            pub enum T {
+            % for value in data.longhands_by_name[name].keyword.values_for(product):
+                ${to_camel_case(value)},
+            % endfor
             }
         }
         #[inline]
         pub fn get_initial_value() -> computed_value::T {
             computed_value::T::${to_camel_case(values.split()[0])}
         }
         #[inline]
         pub fn get_initial_specified_value() -> SpecifiedValue {
             SpecifiedValue::${to_camel_case(values.split()[0])}
         }
         #[inline]
         pub fn parse<'i, 't>(_context: &ParserContext, input: &mut Parser<'i, 't>)
                              -> Result<SpecifiedValue, ParseError<'i>> {
             SpecifiedValue::parse(input)
         }
-        impl Parse for SpecifiedValue {
-            #[inline]
-            fn parse<'i, 't>(_context: &ParserContext, input: &mut Parser<'i, 't>)
-                             -> Result<SpecifiedValue, ParseError<'i>> {
-                SpecifiedValue::parse(input)
-            }
-        }
 
         % if needs_conversion:
             <%
                 conversion_values = keyword.values_for(product)
                 if extra_specified:
                     conversion_values += extra_specified.split()
                 conversion_values += keyword.aliases_for(product).keys()
             %>
--- a/servo/components/style/properties/longhand/border.mako.rs
+++ b/servo/components/style/properties/longhand/border.mako.rs
@@ -27,23 +27,26 @@
         spec=maybe_logical_spec(side, "color"),
         animation_value_type="AnimatedColor",
         logical=is_logical,
         allow_quirks=not is_logical,
         flags="APPLIES_TO_FIRST_LETTER",
         ignored_when_colors_disabled=True,
     )}
 
-    ${helpers.predefined_type("border-%s-style" % side_name, "BorderStyle",
-                              "specified::BorderStyle::None",
-                              alias=maybe_moz_logical_alias(product, side, "-moz-border-%s-style"),
-                              spec=maybe_logical_spec(side, "style"),
-                              flags="APPLIES_TO_FIRST_LETTER",
-                              animation_value_type="discrete" if not is_logical else "none",
-                              logical=is_logical)}
+    ${helpers.predefined_type(
+        "border-%s-style" % side_name, "BorderStyle",
+        "specified::BorderStyle::None",
+        alias=maybe_moz_logical_alias(product, side, "-moz-border-%s-style"),
+        spec=maybe_logical_spec(side, "style"),
+        flags="APPLIES_TO_FIRST_LETTER",
+        animation_value_type="discrete" if not is_logical else "none",
+        logical=is_logical,
+        needs_context=False,
+    )}
 
     ${helpers.predefined_type("border-%s-width" % side_name,
                               "BorderSideWidth",
                               "::values::computed::NonNegativeLength::new(3.)",
                               computed_type="::values::computed::NonNegativeLength",
                               alias=maybe_moz_logical_alias(product, side, "-moz-border-%s-width"),
                               spec=maybe_logical_spec(side, "width"),
                               animation_value_type="NonNegativeLength",
@@ -107,21 +110,24 @@
         #[derive(Clone, Debug, MallocSizeOf, PartialEq, ToCss)]
         pub struct T(pub RepeatKeyword, pub RepeatKeyword);
     }
 
     #[derive(Clone, Debug, MallocSizeOf, PartialEq, ToCss)]
     pub struct SpecifiedValue(pub RepeatKeyword,
                               pub Option<RepeatKeyword>);
 
-    define_css_keyword_enum!(RepeatKeyword:
-                             "stretch" => Stretch,
-                             "repeat" => Repeat,
-                             "round" => Round,
-                             "space" => Space);
+    #[cfg_attr(feature = "servo", derive(Deserialize, Serialize))]
+    #[derive(Clone, Copy, Debug, Eq, MallocSizeOf, Parse, PartialEq, ToCss)]
+    pub enum RepeatKeyword {
+        Stretch,
+        Repeat,
+        Round,
+        Space,
+    }
 
     #[inline]
     pub fn get_initial_value() -> computed_value::T {
         computed_value::T(RepeatKeyword::Stretch, RepeatKeyword::Stretch)
     }
 
     #[inline]
     pub fn get_initial_specified_value() -> SpecifiedValue {
--- a/servo/components/style/properties/longhand/box.mako.rs
+++ b/servo/components/style/properties/longhand/box.mako.rs
@@ -521,24 +521,26 @@
                          "border-box fill-box view-box",
                          gecko_enum_prefix="StyleGeometryBox",
                          products="gecko",
                          gecko_pref="svg.transform-box.enabled",
                          spec="https://drafts.csswg.org/css-transforms/#transform-box",
                          gecko_inexhaustive="True",
                          animation_value_type="discrete")}
 
-// `auto` keyword is not supported in gecko yet.
-${helpers.single_keyword("transform-style",
-                         "auto flat preserve-3d" if product == "servo" else
-                         "flat preserve-3d",
-                         spec="https://drafts.csswg.org/css-transforms/#transform-style-property",
-                         extra_prefixes="moz webkit",
-                         flags="CREATES_STACKING_CONTEXT FIXPOS_CB",
-                         animation_value_type="discrete")}
+${helpers.predefined_type(
+    "transform-style",
+    "TransformStyle",
+    "computed::TransformStyle::" + ("Auto" if product == "servo" else "Flat"),
+    spec="https://drafts.csswg.org/css-transforms-2/#transform-style-property",
+    needs_context=False,
+    extra_prefixes="moz webkit",
+    flags="CREATES_STACKING_CONTEXT FIXPOS_CB",
+    animation_value_type="discrete",
+)}
 
 ${helpers.predefined_type("transform-origin",
                           "TransformOrigin",
                           "computed::TransformOrigin::initial_value()",
                           animation_value_type="ComputedValue",
                           extra_prefixes="moz webkit",
                           gecko_ffi_name="mTransformOrigin",
                           boxed=True,
--- a/servo/components/style/properties/longhand/inherited_text.mako.rs
+++ b/servo/components/style/properties/longhand/inherited_text.mako.rs
@@ -274,22 +274,24 @@
             match *self {
                 KeywordValue::Shape(shape) |
                 KeywordValue::FillAndShape(_, shape) => Some(shape),
                 _ => None,
             }
         }
     }
 
-    define_css_keyword_enum!(ShapeKeyword:
-                             "dot" => Dot,
-                             "circle" => Circle,
-                             "double-circle" => DoubleCircle,
-                             "triangle" => Triangle,
-                             "sesame" => Sesame);
+    #[derive(Clone, Copy, Debug, Eq, MallocSizeOf, Parse, PartialEq, ToCss)]
+    pub enum ShapeKeyword {
+        Dot,
+        Circle,
+        DoubleCircle,
+        Triangle,
+        Sesame,
+    }
 
     impl ShapeKeyword {
         pub fn char(&self, fill: bool) -> &str {
             match *self {
                 ShapeKeyword::Dot => if fill { "\u{2022}" } else { "\u{25e6}" },
                 ShapeKeyword::Circle =>  if fill { "\u{25cf}" } else { "\u{25cb}" },
                 ShapeKeyword::DoubleCircle =>  if fill { "\u{25c9}" } else { "\u{25ce}" },
                 ShapeKeyword::Triangle =>  if fill { "\u{25b2}" } else { "\u{25b3}" },
@@ -376,24 +378,29 @@
             _ => return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError)),
         };
         Ok(SpecifiedValue::Keyword(keyword_value))
     }
 </%helpers:longhand>
 
 <%helpers:longhand name="text-emphasis-position" animation_value_type="discrete" products="gecko"
                    spec="https://drafts.csswg.org/css-text-decor/#propdef-text-emphasis-position">
-    define_css_keyword_enum!(HorizontalWritingModeValue:
-                             "over" => Over,
-                             "under" => Under);
-    add_impls_for_keyword_enum!(VerticalWritingModeValue);
-    define_css_keyword_enum!(VerticalWritingModeValue:
-                             "right" => Right,
-                             "left" => Left);
-    add_impls_for_keyword_enum!(HorizontalWritingModeValue);
+    #[derive(Clone, Copy, Debug, Eq, MallocSizeOf, Parse, PartialEq)]
+    #[derive(ToComputedValue, ToCss)]
+    pub enum HorizontalWritingModeValue {
+        Over,
+        Under,
+    }
+
+    #[derive(Clone, Copy, Debug, Eq, MallocSizeOf, Parse, PartialEq)]
+    #[derive(ToComputedValue, ToCss)]
+    pub enum VerticalWritingModeValue {
+        Right,
+        Left,
+    }
 
     #[derive(Clone, Debug, MallocSizeOf, PartialEq, ToComputedValue, ToCss)]
     pub struct SpecifiedValue(pub HorizontalWritingModeValue, pub VerticalWritingModeValue);
 
     pub mod computed_value {
         pub type T = super::SpecifiedValue;
     }
 
--- a/servo/components/style/properties/shorthand/border.mako.rs
+++ b/servo/components/style/properties/shorthand/border.mako.rs
@@ -4,19 +4,23 @@
 
 <%namespace name="helpers" file="/helpers.mako.rs" />
 <% from data import to_rust_ident, ALL_SIDES, PHYSICAL_SIDES, maybe_moz_logical_alias %>
 
 ${helpers.four_sides_shorthand("border-color", "border-%s-color", "specified::Color::parse",
                                spec="https://drafts.csswg.org/css-backgrounds/#border-color",
                                allow_quirks=True)}
 
-${helpers.four_sides_shorthand("border-style", "border-%s-style",
-                               "specified::BorderStyle::parse",
-                               spec="https://drafts.csswg.org/css-backgrounds/#border-style")}
+${helpers.four_sides_shorthand(
+    "border-style",
+    "border-%s-style",
+    "specified::BorderStyle::parse",
+    needs_context=False,
+    spec="https://drafts.csswg.org/css-backgrounds/#border-style",
+)}
 
 <%helpers:shorthand name="border-width" sub_properties="${
         ' '.join('border-%s-width' % side
                  for side in PHYSICAL_SIDES)}"
     spec="https://drafts.csswg.org/css-backgrounds/#border-width">
     use values::generics::rect::Rect;
     use values::specified::{AllowQuirks, BorderSideWidth};
 
@@ -58,17 +62,17 @@ pub fn parse_border<'i, 't>(context: &Pa
         if color.is_none() {
             if let Ok(value) = input.try(|i| Color::parse(context, i)) {
                 color = Some(value);
                 any = true;
                 continue
             }
         }
         if style.is_none() {
-            if let Ok(value) = input.try(|i| BorderStyle::parse(context, i)) {
+            if let Ok(value) = input.try(BorderStyle::parse) {
                 style = Some(value);
                 any = true;
                 continue
             }
         }
         if width.is_none() {
             if let Ok(value) = input.try(|i| BorderSideWidth::parse(context, i)) {
                 width = Some(value);
--- a/servo/components/style/values/computed/mod.rs
+++ b/servo/components/style/values/computed/mod.rs
@@ -68,17 +68,18 @@ pub use self::pointing::Cursor;
 #[cfg(feature = "gecko")]
 pub use self::pointing::CursorImage;
 pub use self::svg::{SVGLength, SVGOpacity, SVGPaint, SVGPaintKind};
 pub use self::svg::{SVGPaintOrder, SVGStrokeDashArray, SVGWidth};
 pub use self::svg::MozContextProperties;
 pub use self::table::XSpan;
 pub use self::text::{InitialLetter, LetterSpacing, LineHeight, TextAlign, TextOverflow, WordSpacing};
 pub use self::time::Time;
-pub use self::transform::{TimingFunction, Transform, TransformOperation, TransformOrigin, Rotate, Translate, Scale};
+pub use self::transform::{Rotate, Scale, TimingFunction, Transform, TransformOperation};
+pub use self::transform::{TransformOrigin, TransformStyle, Translate};
 pub use self::ui::MozForceBrokenImageIcon;
 
 #[cfg(feature = "gecko")]
 pub mod align;
 pub mod angle;
 pub mod background;
 pub mod basic_shape;
 pub mod border;
--- a/servo/components/style/values/computed/transform.rs
+++ b/servo/components/style/values/computed/transform.rs
@@ -13,16 +13,18 @@ use values::computed::{LengthOrNumber, L
 use values::generics::transform::{self, Matrix as GenericMatrix, Matrix3D as GenericMatrix3D};
 use values::generics::transform::{Transform as GenericTransform, TransformOperation as GenericTransformOperation};
 use values::generics::transform::Rotate as GenericRotate;
 use values::generics::transform::Scale as GenericScale;
 use values::generics::transform::TimingFunction as GenericTimingFunction;
 use values::generics::transform::TransformOrigin as GenericTransformOrigin;
 use values::generics::transform::Translate as GenericTranslate;
 
+pub use values::generics::transform::TransformStyle;
+
 /// A single operation in a computed CSS `transform`
 pub type TransformOperation = GenericTransformOperation<
     Angle,
     Number,
     Length,
     Integer,
     LengthOrNumber,
     LengthOrPercentage,
--- a/servo/components/style/values/generics/basic_shape.rs
+++ b/servo/components/style/values/generics/basic_shape.rs
@@ -25,23 +25,26 @@ pub enum GeometryBox {
     ViewBox,
     ShapeBox(ShapeBox),
 }
 
 /// A float area shape, for `shape-outside`.
 pub type FloatAreaShape<BasicShape, Image> = ShapeSource<BasicShape, ShapeBox, Image>;
 
 // https://drafts.csswg.org/css-shapes-1/#typedef-shape-box
-define_css_keyword_enum!(ShapeBox:
-    "margin-box" => MarginBox,
-    "border-box" => BorderBox,
-    "padding-box" => PaddingBox,
-    "content-box" => ContentBox
-);
-add_impls_for_keyword_enum!(ShapeBox);
+#[allow(missing_docs)]
+#[cfg_attr(feature = "servo", derive(Deserialize, Serialize))]
+#[derive(Clone, Copy, Debug, Eq, MallocSizeOf, Parse, PartialEq)]
+#[derive(ToComputedValue, ToCss)]
+pub enum ShapeBox {
+    MarginBox,
+    BorderBox,
+    PaddingBox,
+    ContentBox,
+}
 
 /// A shape source, for some reference box.
 #[allow(missing_docs)]
 #[derive(Animate, Clone, Debug, MallocSizeOf, PartialEq, ToComputedValue, ToCss)]
 pub enum ShapeSource<BasicShape, ReferenceBox, ImageOrUrl> {
     #[animation(error)]
     ImageOrUrl(ImageOrUrl),
     Shape(
@@ -112,21 +115,24 @@ pub struct Polygon<LengthOrPercentage> {
     /// A collection of (x, y) coordinates to draw the polygon.
     pub coordinates: Vec<(LengthOrPercentage, LengthOrPercentage)>,
 }
 
 // https://drafts.csswg.org/css-shapes/#typedef-fill-rule
 // NOTE: Basic shapes spec says that these are the only two values, however
 // https://www.w3.org/TR/SVG/painting.html#FillRuleProperty
 // says that it can also be `inherit`
-define_css_keyword_enum!(FillRule:
-    "nonzero" => NonZero,
-    "evenodd" => EvenOdd
-);
-add_impls_for_keyword_enum!(FillRule);
+#[allow(missing_docs)]
+#[cfg_attr(feature = "servo", derive(Deserialize, Serialize))]
+#[derive(Clone, Copy, Debug, Eq, MallocSizeOf, Parse, PartialEq)]
+#[derive(ToComputedValue, ToCss)]
+pub enum FillRule {
+    Nonzero,
+    Evenodd,
+}
 
 // FIXME(nox): Implement ComputeSquaredDistance for T types and stop
 // using PartialEq here, this will let us derive this impl.
 impl<B, T, U> ComputeSquaredDistance for ShapeSource<B, T, U>
 where
     B: ComputeSquaredDistance,
     T: PartialEq,
 {
@@ -234,10 +240,10 @@ impl<L: ToCss> ToCss for Polygon<L> {
         }
 
         dest.write_str(")")
     }
 }
 
 impl Default for FillRule {
     #[inline]
-    fn default() -> Self { FillRule::NonZero }
+    fn default() -> Self { FillRule::Nonzero }
 }
--- a/servo/components/style/values/generics/grid.rs
+++ b/servo/components/style/values/generics/grid.rs
@@ -135,22 +135,25 @@ impl Parse for GridLine<specified::Integ
                 return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
             }
         }
 
         Ok(grid_line)
     }
 }
 
-define_css_keyword_enum!{ TrackKeyword:
-    "auto" => Auto,
-    "max-content" => MaxContent,
-    "min-content" => MinContent
+#[allow(missing_docs)]
+#[cfg_attr(feature = "servo", derive(Deserialize, Serialize))]
+#[derive(Clone, Copy, Debug, Eq, MallocSizeOf, Parse, PartialEq)]
+#[derive(ToComputedValue, ToCss)]
+pub enum TrackKeyword {
+    Auto,
+    MaxContent,
+    MinContent,
 }
-add_impls_for_keyword_enum!(TrackKeyword);
 
 /// A track breadth for explicit grid track sizing. It's generic solely to
 /// avoid re-implementing it for the computed type.
 ///
 /// <https://drafts.csswg.org/css-grid/#typedef-track-breadth>
 #[derive(Clone, Debug, MallocSizeOf, PartialEq, ToComputedValue, ToCss)]
 pub enum TrackBreadth<L> {
     /// The generic type is almost always a non-negative `<length-percentage>`
--- a/servo/components/style/values/generics/image.rs
+++ b/servo/components/style/values/generics/image.rs
@@ -92,25 +92,28 @@ pub enum Circle<Length> {
 pub enum Ellipse<LengthOrPercentage> {
     /// An ellipse pair of radii.
     Radii(LengthOrPercentage, LengthOrPercentage),
     /// An ellipse extent.
     Extent(ShapeExtent),
 }
 
 /// <https://drafts.csswg.org/css-images/#typedef-extent-keyword>
-define_css_keyword_enum!(ShapeExtent:
-    "closest-side" => ClosestSide,
-    "farthest-side" => FarthestSide,
-    "closest-corner" => ClosestCorner,
-    "farthest-corner" => FarthestCorner,
-    "contain" => Contain,
-    "cover" => Cover
-);
-add_impls_for_keyword_enum!(ShapeExtent);
+#[allow(missing_docs)]
+#[cfg_attr(feature = "servo", derive(Deserialize, Serialize))]
+#[derive(Clone, Copy, Debug, Eq, MallocSizeOf, Parse, PartialEq)]
+#[derive(ToComputedValue, ToCss)]
+pub enum ShapeExtent {
+    ClosestSide,
+    FarthestSide,
+    ClosestCorner,
+    FarthestCorner,
+    Contain,
+    Cover,
+}
 
 /// A gradient item.
 /// <https://drafts.csswg.org/css-images-4/#color-stop-syntax>
 #[derive(Clone, Copy, Debug, MallocSizeOf, PartialEq, ToComputedValue, ToCss)]
 pub enum GradientItem<Color, LengthOrPercentage> {
     /// A color stop.
     ColorStop(ColorStop<Color, LengthOrPercentage>),
     /// An interpolation hint.
--- a/servo/components/style/values/generics/mod.rs
+++ b/servo/components/style/values/generics/mod.rs
@@ -27,24 +27,27 @@ pub mod image;
 pub mod position;
 pub mod rect;
 pub mod size;
 pub mod svg;
 pub mod text;
 pub mod transform;
 
 // https://drafts.csswg.org/css-counter-styles/#typedef-symbols-type
-define_css_keyword_enum! { SymbolsType:
-    "cyclic" => Cyclic,
-    "numeric" => Numeric,
-    "alphabetic" => Alphabetic,
-    "symbolic" => Symbolic,
-    "fixed" => Fixed,
+#[allow(missing_docs)]
+#[cfg_attr(feature = "servo", derive(Deserialize, Serialize))]
+#[derive(Clone, Copy, Debug, Eq, MallocSizeOf, Parse, PartialEq)]
+#[derive(ToComputedValue, ToCss)]
+pub enum SymbolsType {
+    Cyclic,
+    Numeric,
+    Alphabetic,
+    Symbolic,
+    Fixed,
 }
-add_impls_for_keyword_enum!(SymbolsType);
 
 #[cfg(feature = "gecko")]
 impl SymbolsType {
     /// Convert symbols type to their corresponding Gecko values.
     pub fn to_gecko_keyword(self) -> u8 {
         use gecko_bindings::structs;
         match self {
             SymbolsType::Cyclic => structs::NS_STYLE_COUNTER_SYSTEM_CYCLIC as u8,
--- a/servo/components/style/values/generics/transform.rs
+++ b/servo/components/style/values/generics/transform.rs
@@ -93,30 +93,36 @@ pub enum TimingFunction<Integer, Number>
         y2: Number,
     },
     /// `step-start | step-end | steps(<integer>, [ start | end ]?)`
     Steps(Integer, StepPosition),
     /// `frames(<integer>)`
     Frames(Integer),
 }
 
-define_css_keyword_enum! { TimingKeyword:
-    "linear" => Linear,
-    "ease" => Ease,
-    "ease-in" => EaseIn,
-    "ease-out" => EaseOut,
-    "ease-in-out" => EaseInOut,
+#[allow(missing_docs)]
+#[cfg_attr(feature = "servo", derive(Deserialize, Serialize))]
+#[derive(Clone, Copy, Debug, Eq, MallocSizeOf, Parse, PartialEq)]
+#[derive(ToComputedValue, ToCss)]
+pub enum TimingKeyword {
+    Linear,
+    Ease,
+    EaseIn,
+    EaseOut,
+    EaseInOut,
 }
-add_impls_for_keyword_enum!(TimingKeyword);
 
-define_css_keyword_enum! { StepPosition:
-    "start" => Start,
-    "end" => End,
+#[allow(missing_docs)]
+#[cfg_attr(feature = "servo", derive(Deserialize, Serialize))]
+#[derive(Clone, Copy, Debug, Eq, MallocSizeOf, Parse, PartialEq)]
+#[derive(ToComputedValue, ToCss)]
+pub enum StepPosition {
+    Start,
+    End,
 }
-add_impls_for_keyword_enum!(StepPosition);
 
 impl<H, V, D> TransformOrigin<H, V, D> {
     /// Returns a new transform origin.
     pub fn new(horizontal: H, vertical: V, depth: D) -> Self {
         Self {
             horizontal: horizontal,
             vertical: vertical,
             depth: depth,
@@ -709,8 +715,18 @@ pub enum Translate<LengthOrPercentage, L
     None,
     /// '<length-percentage>'
     TranslateX(LengthOrPercentage),
     /// '<length-percentage> <length-percentage>'
     Translate(LengthOrPercentage, LengthOrPercentage),
     /// '<length-percentage> <length-percentage> <length>'
     Translate3D(LengthOrPercentage, LengthOrPercentage, Length),
 }
+
+#[allow(missing_docs)]
+#[derive(Clone, Copy, Debug, MallocSizeOf, Parse, PartialEq, ToComputedValue, ToCss)]
+pub enum TransformStyle {
+    #[cfg(feature = "servo")]
+    Auto,
+    Flat,
+    #[css(keyword = "preserve-3d")]
+    Preserve3d,
+}
--- a/servo/components/style/values/mod.rs
+++ b/servo/components/style/values/mod.rs
@@ -190,15 +190,19 @@ impl ToCss for KeyframesName {
     {
         match *self {
             KeyframesName::Ident(ref ident) => ident.to_css(dest),
             KeyframesName::QuotedString(ref atom) => atom.to_string().to_css(dest),
         }
     }
 }
 
-// A type for possible values for min- and max- flavors of width,
-// height, block-size, and inline-size.
-define_css_keyword_enum!(ExtremumLength:
-                         "-moz-max-content" => MaxContent,
-                         "-moz-min-content" => MinContent,
-                         "-moz-fit-content" => FitContent,
-                         "-moz-available" => FillAvailable);
+/// A type for possible values for min- and max- flavors of width,
+/// height, block-size, and inline-size.
+#[allow(missing_docs)]
+#[cfg_attr(feature = "servo", derive(Deserialize, Serialize))]
+#[derive(Clone, Copy, Debug, Eq, MallocSizeOf, Parse, PartialEq, ToCss)]
+pub enum ExtremumLength {
+    MozMaxContent,
+    MozMinContent,
+    MozFitContent,
+    MozAvailable,
+}
--- a/servo/components/style/values/specified/box.rs
+++ b/servo/components/style/values/specified/box.rs
@@ -23,19 +23,19 @@ use values::specified::length::LengthOrP
 /// Defines an element’s display type, which consists of
 /// the two basic qualities of how an element generates boxes
 /// <https://drafts.csswg.org/css-display/#propdef-display>
 pub enum Display {
     Inline, Block, InlineBlock,
     Table, InlineTable, TableRowGroup, TableHeaderGroup,
     TableFooterGroup, TableRow, TableColumnGroup,
     TableColumn, TableCell, TableCaption, ListItem, None,
-    #[parse(aliases = "-webkit-flex")]
+    #[css(aliases = "-webkit-flex")]
     Flex,
-    #[parse(aliases = "-webkit-inline-flex")]
+    #[css(aliases = "-webkit-inline-flex")]
     InlineFlex,
     #[cfg(feature = "gecko")]
     Grid,
     #[cfg(feature = "gecko")]
     InlineGrid,
     #[cfg(feature = "gecko")]
     Ruby,
     #[cfg(feature = "gecko")]
@@ -315,35 +315,44 @@ impl Parse for AnimationName {
             return Ok(AnimationName(Some(name)));
         }
 
         input.expect_ident_matching("none")?;
         Ok(AnimationName(None))
     }
 }
 
-define_css_keyword_enum! { ScrollSnapType:
-    "none" => None,
-    "mandatory" => Mandatory,
-    "proximity" => Proximity,
+#[allow(missing_docs)]
+#[cfg_attr(feature = "servo", derive(Deserialize, Serialize))]
+#[derive(Clone, Copy, Debug, Eq, MallocSizeOf, Parse, PartialEq)]
+#[derive(ToComputedValue, ToCss)]
+pub enum ScrollSnapType {
+    None,
+    Mandatory,
+    Proximity,
 }
-add_impls_for_keyword_enum!(ScrollSnapType);
 
-define_css_keyword_enum! { OverscrollBehavior:
-    "auto" => Auto,
-    "contain" => Contain,
-    "none" => None,
+#[allow(missing_docs)]
+#[cfg_attr(feature = "servo", derive(Deserialize, Serialize))]
+#[derive(Clone, Copy, Debug, Eq, MallocSizeOf, Parse, PartialEq)]
+#[derive(ToComputedValue, ToCss)]
+pub enum OverscrollBehavior {
+    Auto,
+    Contain,
+    None,
 }
-add_impls_for_keyword_enum!(OverscrollBehavior);
 
-define_css_keyword_enum! { OverflowClipBox:
-    "padding-box" => PaddingBox,
-    "content-box" => ContentBox,
+#[allow(missing_docs)]
+#[cfg_attr(feature = "servo", derive(Deserialize, Serialize))]
+#[derive(Clone, Copy, Debug, Eq, MallocSizeOf, Parse, PartialEq)]
+#[derive(ToComputedValue, ToCss)]
+pub enum OverflowClipBox {
+    PaddingBox,
+    ContentBox,
 }
-add_impls_for_keyword_enum!(OverflowClipBox);
 
 #[derive(Clone, Debug, MallocSizeOf, PartialEq, ToComputedValue, ToCss)]
 /// Provides a rendering hint to the user agent,
 /// stating what kinds of changes the author expects
 /// to perform on the element
 ///
 /// <https://drafts.csswg.org/css-will-change/#will-change>
 pub enum WillChange {
--- a/servo/components/style/values/specified/color.rs
+++ b/servo/components/style/values/specified/color.rs
@@ -42,22 +42,23 @@ pub enum Color {
     Special(gecko::SpecialColorKeyword),
     /// Quirksmode-only rule for inheriting color from the body
     #[cfg(feature = "gecko")]
     InheritFromBodyQuirk,
 }
 
 #[cfg(feature = "gecko")]
 mod gecko {
-    define_css_keyword_enum! { SpecialColorKeyword:
-        "-moz-default-color" => MozDefaultColor,
-        "-moz-default-background-color" => MozDefaultBackgroundColor,
-        "-moz-hyperlinktext" => MozHyperlinktext,
-        "-moz-activehyperlinktext" => MozActiveHyperlinktext,
-        "-moz-visitedhyperlinktext" => MozVisitedHyperlinktext,
+    #[derive(Clone, Copy, Debug, Eq, Hash, MallocSizeOf, Parse, PartialEq, ToCss)]
+    pub enum SpecialColorKeyword {
+        MozDefaultColor,
+        MozDefaultBackgroundColor,
+        MozHyperlinktext,
+        MozActiveHyperlinktext,
+        MozVisitedHyperlinktext,
     }
 }
 
 impl From<RGBA> for Color {
     fn from(value: RGBA) -> Self {
         Color::rgba(value)
     }
 }
--- a/servo/components/style/values/specified/mod.rs
+++ b/servo/components/style/values/specified/mod.rs
@@ -64,17 +64,18 @@ pub use self::pointing::Cursor;
 pub use self::pointing::CursorImage;
 pub use self::svg::{SVGLength, SVGOpacity, SVGPaint, SVGPaintKind};
 pub use self::svg::{SVGPaintOrder, SVGStrokeDashArray, SVGWidth};
 pub use self::svg::MozContextProperties;
 pub use self::table::XSpan;
 pub use self::text::{InitialLetter, LetterSpacing, LineHeight, TextDecorationLine};
 pub use self::text::{TextAlign, TextAlignKeyword, TextOverflow, WordSpacing};
 pub use self::time::Time;
-pub use self::transform::{TimingFunction, Transform, TransformOrigin, Rotate, Translate, Scale};
+pub use self::transform::{Rotate, Scale, TimingFunction, Transform};
+pub use self::transform::{TransformOrigin, TransformStyle, Translate};
 pub use self::ui::MozForceBrokenImageIcon;
 pub use super::generics::grid::GridTemplateComponent as GenericGridTemplateComponent;
 
 #[cfg(feature = "gecko")]
 pub mod align;
 pub mod angle;
 pub mod background;
 pub mod basic_shape;
@@ -155,30 +156,33 @@ fn parse_number_with_clamping_mode<'i, '
         calc_clamping_mode: Some(clamping_mode),
     })
 }
 
 // The integer values here correspond to the border conflict resolution rules in CSS 2.1 §
 // 17.6.2.1. Higher values override lower values.
 //
 // FIXME(emilio): Should move to border.rs
-define_numbered_css_keyword_enum! { BorderStyle:
-    "none" => None = -1,
-    "solid" => Solid = 6,
-    "double" => Double = 7,
-    "dotted" => Dotted = 4,
-    "dashed" => Dashed = 5,
-    "hidden" => Hidden = -2,
-    "groove" => Groove = 1,
-    "ridge" => Ridge = 3,
-    "inset" => Inset = 0,
-    "outset" => Outset = 2,
+#[allow(missing_docs)]
+#[cfg_attr(feature = "servo", derive(Deserialize, Serialize))]
+#[derive(Clone, Copy, Debug, Eq, MallocSizeOf, Ord, Parse, PartialEq)]
+#[derive(PartialOrd, ToCss)]
+pub enum BorderStyle {
+    None = -1,
+    Solid = 6,
+    Double = 7,
+    Dotted = 4,
+    Dashed = 5,
+    Hidden = -2,
+    Groove = 1,
+    Ridge = 3,
+    Inset = 0,
+    Outset = 2,
 }
 
-
 impl BorderStyle {
     /// Whether this border style is either none or hidden.
     pub fn none_or_hidden(&self) -> bool {
         matches!(*self, BorderStyle::None | BorderStyle::Hidden)
     }
 }
 
 /// A CSS `<number>` specified value.
--- a/servo/components/style/values/specified/outline.rs
+++ b/servo/components/style/values/specified/outline.rs
@@ -34,20 +34,20 @@ impl OutlineStyle {
             OutlineStyle::Auto => false,
             OutlineStyle::Other(ref border_style) => border_style.none_or_hidden()
         }
     }
 }
 
 impl Parse for OutlineStyle {
     fn parse<'i, 't>(
-        context: &ParserContext,
+        _context: &ParserContext,
         input: &mut Parser<'i, 't>
     ) -> Result<OutlineStyle, ParseError<'i>> {
-        if let Ok(border_style) = input.try(|i| BorderStyle::parse(context, i)) {
+        if let Ok(border_style) = input.try(BorderStyle::parse) {
             if let BorderStyle::Hidden = border_style {
                 return Err(input.new_custom_error(SelectorParseErrorKind::UnexpectedIdent("hidden".into())));
             }
 
             return Ok(OutlineStyle::Other(border_style));
         }
 
         input.expect_ident_matching("auto")?;
--- a/servo/components/style/values/specified/transform.rs
+++ b/servo/components/style/values/specified/transform.rs
@@ -17,16 +17,18 @@ use values::generics::transform::{Timing
 use values::generics::transform::Rotate as GenericRotate;
 use values::generics::transform::Scale as GenericScale;
 use values::generics::transform::TransformOperation as GenericTransformOperation;
 use values::generics::transform::Translate as GenericTranslate;
 use values::specified::{self, Angle, Number, Length, Integer};
 use values::specified::{LengthOrNumber, LengthOrPercentage, LengthOrPercentageOrNumber};
 use values::specified::position::{Side, X, Y};
 
+pub use values::generics::transform::TransformStyle;
+
 /// A single operation in a specified CSS `transform`
 pub type TransformOperation = GenericTransformOperation<
     Angle,
     Number,
     Length,
     Integer,
     LengthOrNumber,
     LengthOrPercentage,
--- a/servo/components/style_derive/lib.rs
+++ b/servo/components/style_derive/lib.rs
@@ -34,17 +34,17 @@ pub fn derive_compute_squared_distance(s
 }
 
 #[proc_macro_derive(ToAnimatedValue)]
 pub fn derive_to_animated_value(stream: TokenStream) -> TokenStream {
     let input = syn::parse_derive_input(&stream.to_string()).unwrap();
     to_animated_value::derive(input).to_string().parse().unwrap()
 }
 
-#[proc_macro_derive(Parse, attributes(parse))]
+#[proc_macro_derive(Parse, attributes(css))]
 pub fn derive_parse(stream: TokenStream) -> TokenStream {
     let input = syn::parse_derive_input(&stream.to_string()).unwrap();
     parse::derive(input).to_string().parse().unwrap()
 }
 
 #[proc_macro_derive(ToAnimatedZero, attributes(animation))]
 pub fn derive_to_animated_zero(stream: TokenStream) -> TokenStream {
     let input = syn::parse_derive_input(&stream.to_string()).unwrap();
--- a/servo/components/style_derive/parse.rs
+++ b/servo/components/style_derive/parse.rs
@@ -1,31 +1,34 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 use cg;
 use quote::Tokens;
 use syn::DeriveInput;
 use synstructure;
+use to_css::CssVariantAttrs;
 
 pub fn derive(input: DeriveInput) -> Tokens {
     let name = &input.ident;
 
     let mut match_body = quote! {};
 
     let style = synstructure::BindStyle::Ref.into();
     synstructure::each_variant(&input, &style, |bindings, variant| {
         assert!(
             bindings.is_empty(),
             "Parse is only supported for single-variant enums for now"
         );
 
-        let variant_attrs = cg::parse_variant_attrs::<ParseVariantAttrs>(variant);
-        let identifier = cg::to_css_identifier(variant.ident.as_ref());
+        let variant_attrs = cg::parse_variant_attrs::<CssVariantAttrs>(variant);
+        let identifier = cg::to_css_identifier(
+            &variant_attrs.keyword.as_ref().unwrap_or(&variant.ident).as_ref(),
+        );
         let ident = &variant.ident;
 
         match_body = quote! {
             #match_body
             #identifier => Ok(#name::#ident),
         };
 
         let aliases = match variant_attrs.aliases {
@@ -82,16 +85,8 @@ pub fn derive(input: DeriveInput) -> Tok
         }
     };
 
     quote! {
         #parse_trait_impl
         #methods_impl
     }
 }
-
-#[darling(attributes(parse), default)]
-#[derive(Default, FromVariant)]
-struct ParseVariantAttrs {
-    /// The comma-separated list of aliases this variant should be aliased to at
-    /// parse time.
-    aliases: Option<String>,
-}
--- a/servo/components/style_derive/to_css.rs
+++ b/servo/components/style_derive/to_css.rs
@@ -18,20 +18,29 @@ pub fn derive(input: DeriveInput) -> Tok
     let style = synstructure::BindStyle::Ref.into();
     let match_body = synstructure::each_variant(&input, &style, |bindings, variant| {
         let identifier = cg::to_css_identifier(variant.ident.as_ref());
         let variant_attrs = cg::parse_variant_attrs::<CssVariantAttrs>(variant);
         let separator = if variant_attrs.comma { ", " } else { " " };
 
         if variant_attrs.dimension {
             assert_eq!(bindings.len(), 1);
-            assert!(variant_attrs.function.is_none(), "That makes no sense");
+            assert!(
+                variant_attrs.function.is_none() && variant_attrs.keyword.is_none(),
+                "That makes no sense"
+            );
         }
 
-        let mut expr = if !bindings.is_empty() {
+        let mut expr = if let Some(keyword) = variant_attrs.keyword {
+            assert!(bindings.is_empty());
+            let keyword = keyword.to_string();
+            quote! {
+                ::std::fmt::Write::write_str(dest, #keyword)
+            }
+        } else if !bindings.is_empty() {
             let mut expr = quote! {};
             if variant_attrs.iterable {
                 assert_eq!(bindings.len(), 1);
                 let binding = &bindings[0];
                 expr = quote! {
                     #expr
 
                     for item in #binding.iter() {
@@ -118,30 +127,32 @@ pub fn derive(input: DeriveInput) -> Tok
 struct CssInputAttrs {
     derive_debug: bool,
     function: Option<Function>,
     comma: bool,
 }
 
 #[darling(attributes(css), default)]
 #[derive(Default, FromVariant)]
-struct CssVariantAttrs {
-    function: Option<Function>,
-    iterable: bool,
-    comma: bool,
-    dimension: bool,
+pub struct CssVariantAttrs {
+    pub function: Option<Function>,
+    pub iterable: bool,
+    pub comma: bool,
+    pub dimension: bool,
+    pub keyword: Option<Ident>,
+    pub aliases: Option<String>,
 }
 
 #[darling(attributes(css), default)]
 #[derive(Default, FromField)]
 struct CssFieldAttrs {
     ignore_bound: bool,
 }
 
-struct Function {
+pub struct Function {
     name: Option<Ident>,
 }
 
 impl FromMetaItem for Function {
     fn from_word() -> Result<Self, Error> {
         Ok(Self { name: None })
     }
 
--- a/servo/components/style_traits/values.rs
+++ b/servo/components/style_traits/values.rs
@@ -379,117 +379,67 @@ impl_to_css_for_predefined_type!(u16);
 impl_to_css_for_predefined_type!(u32);
 impl_to_css_for_predefined_type!(::cssparser::Token<'a>);
 impl_to_css_for_predefined_type!(::cssparser::RGBA);
 impl_to_css_for_predefined_type!(::cssparser::Color);
 impl_to_css_for_predefined_type!(::cssparser::UnicodeRange);
 
 #[macro_export]
 macro_rules! define_css_keyword_enum {
-    ($name: ident: values { $( $css: expr => $variant: ident),+, }
-                   aliases { $( $alias: expr => $alias_variant: ident ),+, }) => {
-        __define_css_keyword_enum__add_optional_traits!($name [ $( $css => $variant ),+ ]
-                                                              [ $( $alias => $alias_variant ),+ ]);
-    };
-    ($name: ident: values { $( $css: expr => $variant: ident),+, }
-                   aliases { $( $alias: expr => $alias_variant: ident ),* }) => {
-        __define_css_keyword_enum__add_optional_traits!($name [ $( $css => $variant ),+ ]
-                                                              [ $( $alias => $alias_variant ),* ]);
-    };
-    ($name: ident: values { $( $css: expr => $variant: ident),+ }
-                   aliases { $( $alias: expr => $alias_variant: ident ),+, }) => {
-        __define_css_keyword_enum__add_optional_traits!($name [ $( $css => $variant ),+ ]
-                                                              [ $( $alias => $alias_variant ),+ ]);
-    };
-    ($name: ident: values { $( $css: expr => $variant: ident),+ }
-                   aliases { $( $alias: expr => $alias_variant: ident ),* }) => {
-        __define_css_keyword_enum__add_optional_traits!($name [ $( $css => $variant ),+ ]
-                                                              [ $( $alias => $alias_variant ),* ]);
-    };
-    ($name: ident: $( $css: expr => $variant: ident ),+,) => {
-        __define_css_keyword_enum__add_optional_traits!($name [ $( $css => $variant ),+ ] []);
-    };
-    ($name: ident: $( $css: expr => $variant: ident ),+) => {
-        __define_css_keyword_enum__add_optional_traits!($name [ $( $css => $variant ),+ ] []);
-    };
-}
-
-#[cfg(feature = "servo")]
-#[macro_export]
-macro_rules! __define_css_keyword_enum__add_optional_traits {
-    ($name: ident [ $( $css: expr => $variant: ident ),+ ]
-                  [ $( $alias: expr => $alias_variant: ident),* ]) => {
-        __define_css_keyword_enum__actual! {
-            $name [ Deserialize, Serialize, MallocSizeOf ]
-                  [ $( $css => $variant ),+ ]
-                  [ $( $alias => $alias_variant ),* ]
-        }
-    };
-}
-
-#[cfg(not(feature = "servo"))]
-#[macro_export]
-macro_rules! __define_css_keyword_enum__add_optional_traits {
-    ($name: ident [ $( $css: expr => $variant: ident ),+ ]
-                  [ $( $alias: expr => $alias_variant: ident),* ]) => {
-        __define_css_keyword_enum__actual! {
-            $name [ MallocSizeOf ]
-                  [ $( $css => $variant ),+ ]
-                  [ $( $alias => $alias_variant ),* ]
-        }
-    };
-}
-
-#[macro_export]
-macro_rules! __define_css_keyword_enum__actual {
-    ($name: ident [ $( $derived_trait: ident),* ]
-                  [ $( $css: expr => $variant: ident ),+ ]
-                  [ $( $alias: expr => $alias_variant: ident ),* ]) => {
-        #[allow(non_camel_case_types, missing_docs)]
-        #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq$(, $derived_trait )* )]
+    (pub enum $name:ident { $($variant:ident = $css:expr,)+ }) => {
+        #[allow(missing_docs)]
+        #[cfg_attr(feature = "servo", derive(Deserialize, Serialize))]
+        #[derive(Clone, Copy, Debug, Eq, Hash, MallocSizeOf, PartialEq)]
         pub enum $name {
-            $( $variant ),+
+            $($variant),+
         }
 
         impl $name {
             /// Parse this property from a CSS input stream.
             pub fn parse<'i, 't>(input: &mut ::cssparser::Parser<'i, 't>)
                                  -> Result<$name, $crate::ParseError<'i>> {
                 use cssparser::Token;
                 let location = input.current_source_location();
                 match *input.next()? {
                     Token::Ident(ref ident) => {
                         Self::from_ident(ident).map_err(|()| {
-                            location.new_unexpected_token_error(Token::Ident(ident.clone()))
+                            location.new_unexpected_token_error(
+                                Token::Ident(ident.clone()),
+                            )
                         })
                     }
-                    ref token => Err(location.new_unexpected_token_error(token.clone()))
+                    ref token => {
+                        Err(location.new_unexpected_token_error(token.clone()))
+                    }
                 }
             }
 
             /// Parse this property from an already-tokenized identifier.
             pub fn from_ident(ident: &str) -> Result<$name, ()> {
                 match_ignore_ascii_case! { ident,
-                                           $( $css => Ok($name::$variant), )+
-                                           $( $alias => Ok($name::$alias_variant), )*
-                                           _ => Err(())
+                    $($css => Ok($name::$variant),)+
+                    _ => Err(())
                 }
             }
         }
 
         impl $crate::ToCss for $name {
-            fn to_css<W>(&self, dest: &mut $crate::CssWriter<W>) -> ::std::fmt::Result
-                where W: ::std::fmt::Write
+            fn to_css<W>(
+                &self,
+                dest: &mut $crate::CssWriter<W>,
+            ) -> ::std::fmt::Result
+            where
+                W: ::std::fmt::Write,
             {
                 match *self {
                     $( $name::$variant => ::std::fmt::Write::write_str(dest, $css) ),+
                 }
             }
         }
-    }
+    };
 }
 
 /// Helper types for the handling of specified values.
 pub mod specified {
     use ParsingMode;
 
     /// Whether to allow negative lengths or not.
     #[repr(u8)]
--- a/servo/components/style_traits/viewport.rs
+++ b/servo/components/style_traits/viewport.rs
@@ -5,24 +5,30 @@
 //! Helper types for the `@viewport` rule.
 
 use {CSSPixel, CssWriter, ParseError, PinchZoomFactor, ToCss};
 use cssparser::Parser;
 use euclid::TypedSize2D;
 #[allow(unused_imports)] use std::ascii::AsciiExt;
 use std::fmt::{self, Write};
 
-define_css_keyword_enum!(UserZoom:
-                         "zoom" => Zoom,
-                         "fixed" => Fixed);
+define_css_keyword_enum! {
+    pub enum UserZoom {
+        Zoom = "zoom",
+        Fixed = "fixed",
+    }
+}
 
-define_css_keyword_enum!(Orientation:
-                         "auto" => Auto,
-                         "portrait" => Portrait,
-                         "landscape" => Landscape);
+define_css_keyword_enum! {
+    pub enum Orientation {
+        Auto = "auto",
+        Portrait = "portrait",
+        Landscape = "landscape",
+    }
+}
 
 /// A set of viewport descriptors:
 ///
 /// <https://drafts.csswg.org/css-device-adapt/#viewport-desc>
 #[derive(Clone, Debug, PartialEq)]
 #[cfg_attr(feature = "servo", derive(Deserialize, Serialize, MallocSizeOf))]
 pub struct ViewportConstraints {
     /// Width and height:
--- a/servo/tests/unit/style/parsing/border.rs
+++ b/servo/tests/unit/style/parsing/border.rs
@@ -163,26 +163,26 @@ fn border_image_outset_should_return_len
     let result = parse(border_image_outset::parse, "0em");
     assert_eq!(result.unwrap(), parse_longhand!(border_image_outset, "0em"));
 }
 
 #[test]
 fn test_border_style() {
     use style::values::specified::BorderStyle;
 
-    assert_roundtrip_with_context!(BorderStyle::parse, r#"none"#);
-    assert_roundtrip_with_context!(BorderStyle::parse, r#"hidden"#);
-    assert_roundtrip_with_context!(BorderStyle::parse, r#"solid"#);
-    assert_roundtrip_with_context!(BorderStyle::parse, r#"double"#);
-    assert_roundtrip_with_context!(BorderStyle::parse, r#"dotted"#);
-    assert_roundtrip_with_context!(BorderStyle::parse, r#"dashed"#);
-    assert_roundtrip_with_context!(BorderStyle::parse, r#"groove"#);
-    assert_roundtrip_with_context!(BorderStyle::parse, r#"ridge"#);
-    assert_roundtrip_with_context!(BorderStyle::parse, r#"inset"#);
-    assert_roundtrip_with_context!(BorderStyle::parse, r#"outset"#);
+    assert_roundtrip_with_context!(<BorderStyle as Parse>::parse, r#"none"#);
+    assert_roundtrip_with_context!(<BorderStyle as Parse>::parse, r#"hidden"#);
+    assert_roundtrip_with_context!(<BorderStyle as Parse>::parse, r#"solid"#);
+    assert_roundtrip_with_context!(<BorderStyle as Parse>::parse, r#"double"#);
+    assert_roundtrip_with_context!(<BorderStyle as Parse>::parse, r#"dotted"#);
+    assert_roundtrip_with_context!(<BorderStyle as Parse>::parse, r#"dashed"#);
+    assert_roundtrip_with_context!(<BorderStyle as Parse>::parse, r#"groove"#);
+    assert_roundtrip_with_context!(<BorderStyle as Parse>::parse, r#"ridge"#);
+    assert_roundtrip_with_context!(<BorderStyle as Parse>::parse, r#"inset"#);
+    assert_roundtrip_with_context!(<BorderStyle as Parse>::parse, r#"outset"#);
 }
 
 #[test]
 fn test_border_spacing() {
     use style::properties::longhands::border_spacing;
 
     assert_parser_exhausted!(border_spacing::parse, "1px rubbish", false);
     assert_parser_exhausted!(border_spacing::parse, "1px", true);