servo: Merge #19002 - style: Move background-repeat and mask-repeat outside of mako (from emilio:kill-more-mako); r=jdm
authorEmilio Cobos Álvarez <emilio@crisal.io>
Tue, 24 Oct 2017 12:02:05 -0500
changeset 388037 b3dcf61084abe620b3f29df70055fdeeeb36f0d0
parent 388036 dab1c5badddbf3dafd34b52700c3e1129f76d632
child 388038 127d2762b087b5e5d5b6c32683e91fe483963302
push id96545
push useracraciun@mozilla.com
push dateWed, 25 Oct 2017 09:37:39 +0000
treeherdermozilla-inbound@060e68fcb4aa [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjdm
bugs19002
milestone58.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 #19002 - style: Move background-repeat and mask-repeat outside of mako (from emilio:kill-more-mako); r=jdm style: Move background-repeat and mask-repeat outside of mako. Source-Repo: https://github.com/servo/servo Source-Revision: 347176df257978bdb1c413f632c9fc870de4ff76
servo/components/layout/display_list_builder.rs
servo/components/style/properties/gecko.mako.rs
servo/components/style/properties/helpers.mako.rs
servo/components/style/properties/longhand/background.mako.rs
servo/components/style/properties/longhand/border.mako.rs
servo/components/style/properties/longhand/inherited_text.mako.rs
servo/components/style/properties/longhand/svg.mako.rs
servo/components/style/stylesheets/viewport_rule.rs
servo/components/style/values/computed/background.rs
servo/components/style/values/computed/mod.rs
servo/components/style/values/generics/size.rs
servo/components/style/values/specified/background.rs
servo/components/style/values/specified/color.rs
servo/components/style/values/specified/mod.rs
servo/components/style_traits/values.rs
servo/components/style_traits/viewport.rs
--- a/servo/components/layout/display_list_builder.rs
+++ b/servo/components/layout/display_list_builder.rs
@@ -43,17 +43,17 @@ use range::Range;
 use script_layout_interface::wrapper_traits::PseudoElementType;
 use servo_config::opts;
 use servo_geometry::max_rect;
 use std::{cmp, f32};
 use std::default::Default;
 use std::mem;
 use std::sync::Arc;
 use style::computed_values::{background_attachment, background_clip, background_origin};
-use style::computed_values::{background_repeat, border_style, cursor};
+use style::computed_values::{border_style, cursor};
 use style::computed_values::{image_rendering, overflow_x, pointer_events, position, visibility};
 use style::logical_geometry::{LogicalMargin, LogicalPoint, LogicalRect, LogicalSize, WritingMode};
 use style::properties::ComputedValues;
 use style::properties::longhands::border_image_repeat::computed_value::RepeatKeyword;
 use style::properties::style_structs;
 use style::servo::restyle_damage::REPAINT;
 use style::values::{Either, RGBA};
 use style::values::computed::{Angle, Gradient, GradientItem, LengthOrPercentage, Percentage};
@@ -61,16 +61,17 @@ use style::values::computed::{LengthOrPe
 use style::values::computed::effects::SimpleShadow;
 use style::values::computed::image::{EndingShape, LineDirection};
 use style::values::generics::background::BackgroundSize;
 use style::values::generics::effects::Filter;
 use style::values::generics::image::{Circle, Ellipse, EndingShape as GenericEndingShape};
 use style::values::generics::image::{GradientItem as GenericGradientItem, GradientKind};
 use style::values::generics::image::{Image, ShapeExtent};
 use style::values::generics::image::PaintWorklet;
+use style::values::specified::background::RepeatKeyword as BackgroundRepeatKeyword;
 use style::values::specified::position::{X, Y};
 use style_traits::CSSPixel;
 use style_traits::ToCss;
 use style_traits::cursor::Cursor;
 use table_cell::CollapsedBordersForCell;
 use webrender_api::{ClipId, ClipMode, ColorF, ComplexClipRegion, GradientStop, LineStyle};
 use webrender_api::{LocalClip, RepeatMode, ScrollPolicy, ScrollSensitivity, StickyFrameInfo};
 use webrender_api::StickySideConstraint;
@@ -1181,61 +1182,61 @@ impl FragmentDisplayListBuilding for Fra
         let anchor_origin_y = border.top + virtual_origin_y + origin_y + vertical_position;
 
         let mut tile_spacing = Size2D::zero();
         let mut stretch_size = image_size;
 
         // Adjust origin and size based on background-repeat
         let background_repeat = get_cyclic(&background.background_repeat.0, index);
         match background_repeat.0 {
-            background_repeat::single_value::RepeatKeyword::NoRepeat => {
+            BackgroundRepeatKeyword::NoRepeat => {
                 bounds.origin.x = anchor_origin_x;
                 bounds.size.width = image_size.width;
             }
-            background_repeat::single_value::RepeatKeyword::Repeat => {
+            BackgroundRepeatKeyword::Repeat => {
                 ImageFragmentInfo::tile_image(&mut bounds.origin.x,
                                               &mut bounds.size.width,
                                               anchor_origin_x,
                                               image_size.width);
             }
-            background_repeat::single_value::RepeatKeyword::Space => {
+            BackgroundRepeatKeyword::Space => {
                 ImageFragmentInfo::tile_image_spaced(&mut bounds.origin.x,
                                                      &mut bounds.size.width,
                                                      &mut tile_spacing.width,
                                                      anchor_origin_x,
                                                      image_size.width);
 
             }
-            background_repeat::single_value::RepeatKeyword::Round => {
+            BackgroundRepeatKeyword::Round => {
                 ImageFragmentInfo::tile_image_round(&mut bounds.origin.x,
                                                     &mut bounds.size.width,
                                                     anchor_origin_x,
                                                     &mut stretch_size.width);
             }
         };
         match background_repeat.1 {
-            background_repeat::single_value::RepeatKeyword::NoRepeat => {
+            BackgroundRepeatKeyword::NoRepeat => {
                 bounds.origin.y = anchor_origin_y;
                 bounds.size.height = image_size.height;
             }
-            background_repeat::single_value::RepeatKeyword::Repeat => {
+            BackgroundRepeatKeyword::Repeat => {
                 ImageFragmentInfo::tile_image(&mut bounds.origin.y,
                                               &mut bounds.size.height,
                                               anchor_origin_y,
                                               image_size.height);
             }
-            background_repeat::single_value::RepeatKeyword::Space => {
+            BackgroundRepeatKeyword::Space => {
                 ImageFragmentInfo::tile_image_spaced(&mut bounds.origin.y,
                                                      &mut bounds.size.height,
                                                      &mut tile_spacing.height,
                                                      anchor_origin_y,
                                                      image_size.height);
 
             }
-            background_repeat::single_value::RepeatKeyword::Round => {
+            BackgroundRepeatKeyword::Round => {
                 ImageFragmentInfo::tile_image_round(&mut bounds.origin.y,
                                                     &mut bounds.size.height,
                                                     anchor_origin_y,
                                                     &mut stretch_size.height);
             }
         };
 
         // Create the image display item.
--- a/servo/components/style/properties/gecko.mako.rs
+++ b/servo/components/style/properties/gecko.mako.rs
@@ -3654,17 +3654,17 @@ fn static_assert() {
             image_layers_field = "mImage"
             struct_name = "Background"
         else:
             image_layers_field = "mMask"
             struct_name = "SVG"
     %>
 
     <%self:simple_image_array_property name="repeat" shorthand="${shorthand}" field_name="mRepeat">
-        use properties::longhands::${shorthand}_repeat::single_value::computed_value::RepeatKeyword;
+        use values::specified::background::RepeatKeyword;
         use gecko_bindings::structs::nsStyleImageLayers_Repeat;
         use gecko_bindings::structs::StyleImageLayerRepeat;
 
         fn to_ns(repeat: RepeatKeyword) -> StyleImageLayerRepeat {
             match repeat {
                 RepeatKeyword::Repeat => StyleImageLayerRepeat::Repeat,
                 RepeatKeyword::Space => StyleImageLayerRepeat::Space,
                 RepeatKeyword::Round => StyleImageLayerRepeat::Round,
@@ -3677,17 +3677,17 @@ fn static_assert() {
         nsStyleImageLayers_Repeat {
               mXRepeat: repeat_x,
               mYRepeat: repeat_y,
         }
     </%self:simple_image_array_property>
 
     pub fn clone_${shorthand}_repeat(&self) -> longhands::${shorthand}_repeat::computed_value::T {
         use properties::longhands::${shorthand}_repeat::single_value::computed_value::T;
-        use properties::longhands::${shorthand}_repeat::single_value::computed_value::RepeatKeyword;
+        use values::specified::background::RepeatKeyword;
         use gecko_bindings::structs::StyleImageLayerRepeat;
 
         fn to_servo(repeat: StyleImageLayerRepeat) -> RepeatKeyword {
             match repeat {
                 StyleImageLayerRepeat::Repeat => RepeatKeyword::Repeat,
                 StyleImageLayerRepeat::Space => RepeatKeyword::Space,
                 StyleImageLayerRepeat::Round => RepeatKeyword::Round,
                 StyleImageLayerRepeat::NoRepeat => RepeatKeyword::NoRepeat,
--- a/servo/components/style/properties/helpers.mako.rs
+++ b/servo/components/style/properties/helpers.mako.rs
@@ -434,17 +434,17 @@
     %>
     <%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::{ToCss, ParseError};
+            use style_traits::ParseError;
             define_css_keyword_enum! { T:
                 % for value in keyword.values_for(product):
                     "${value}" => ${to_rust_ident(value)},
                 % endfor
             }
 
             impl Parse for T {
                 fn parse<'i, 't>(_: &ParserContext, input: &mut Parser<'i, 't>) -> Result<Self, ParseError<'i>> {
@@ -609,34 +609,32 @@
             '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):
-            use style_traits::ToCss;
             define_css_keyword_enum! { SpecifiedValue:
                 values {
                     % for value in keyword.values_for(product) + (extra_specified or "").split():
                         "${value}" => ${to_rust_ident(value)},
                     % endfor
                 }
                 aliases {
                     % for alias, value in keyword.aliases_for(product).iteritems():
                         "${alias}" => ${to_rust_ident(value)},
                     % endfor
                 }
             }
         % else:
             pub use self::computed_value::T as SpecifiedValue;
         % endif
         pub mod computed_value {
-            use style_traits::ToCss;
             define_css_keyword_enum! { T:
                 % for value in data.longhands_by_name[name].keyword.values_for(product):
                     "${value}" => ${to_rust_ident(value)},
                 % endfor
             }
         }
         #[inline]
         pub fn get_initial_value() -> computed_value::T {
--- a/servo/components/style/properties/longhand/background.mako.rs
+++ b/servo/components/style/properties/longhand/background.mako.rs
@@ -35,111 +35,26 @@
         initial_specified_value="SpecifiedValue::initial_specified_value()",
         spec="https://drafts.csswg.org/css-backgrounds-4/#propdef-background-position-" + axis,
         animation_value_type="ComputedValue",
         vector=True,
         flags="APPLIES_TO_FIRST_LETTER APPLIES_TO_FIRST_LINE APPLIES_TO_PLACEHOLDER",
     )}
 % endfor
 
-<%helpers:vector_longhand name="background-repeat" animation_value_type="discrete"
-                          spec="https://drafts.csswg.org/css-backgrounds/#the-background-repeat"
-                          flags="APPLIES_TO_FIRST_LETTER APPLIES_TO_FIRST_LINE APPLIES_TO_PLACEHOLDER">
-    use std::fmt;
-    use style_traits::ToCss;
-
-    define_css_keyword_enum!(RepeatKeyword:
-                             "repeat" => Repeat,
-                             "space" => Space,
-                             "round" => Round,
-                             "no-repeat" => NoRepeat);
-
-    #[derive(Clone, Debug, MallocSizeOf, PartialEq, ToCss)]
-    pub enum SpecifiedValue {
-        RepeatX,
-        RepeatY,
-        Other(RepeatKeyword, Option<RepeatKeyword>),
-    }
-
-    pub mod computed_value {
-        pub use super::RepeatKeyword;
-
-        #[derive(Clone, Debug, MallocSizeOf, PartialEq)]
-        pub struct T(pub RepeatKeyword, pub RepeatKeyword);
-    }
-
-
-    impl ToCss for computed_value::T {
-        fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
-            match (self.0, self.1) {
-                (RepeatKeyword::Repeat, RepeatKeyword::NoRepeat) => dest.write_str("repeat-x"),
-                (RepeatKeyword::NoRepeat, RepeatKeyword::Repeat) => dest.write_str("repeat-y"),
-                (horizontal, vertical) => {
-                    horizontal.to_css(dest)?;
-                    if horizontal != vertical {
-                        dest.write_str(" ")?;
-                        vertical.to_css(dest)?;
-                    }
-                    Ok(())
-                },
-            }
-        }
-    }
-
-    #[inline]
-    pub fn get_initial_value() -> computed_value::T {
-        computed_value::T(RepeatKeyword::Repeat, RepeatKeyword::Repeat)
-    }
-
-    #[inline]
-    pub fn get_initial_specified_value() -> SpecifiedValue {
-        SpecifiedValue::Other(RepeatKeyword::Repeat, None)
-    }
-
-    impl ToComputedValue for SpecifiedValue {
-        type ComputedValue = computed_value::T;
-
-        #[inline]
-        fn to_computed_value(&self, _context: &Context) -> computed_value::T {
-            match *self {
-                SpecifiedValue::RepeatX =>
-                    computed_value::T(RepeatKeyword::Repeat, RepeatKeyword::NoRepeat),
-                SpecifiedValue::RepeatY =>
-                    computed_value::T(RepeatKeyword::NoRepeat, RepeatKeyword::Repeat),
-                SpecifiedValue::Other(horizontal, vertical) =>
-                    computed_value::T(horizontal, vertical.unwrap_or(horizontal))
-            }
-        }
-
-        #[inline]
-        fn from_computed_value(computed: &computed_value::T) -> Self {
-            match (computed.0, computed.1) {
-                (RepeatKeyword::Repeat, RepeatKeyword::NoRepeat) => SpecifiedValue::RepeatX,
-                (RepeatKeyword::NoRepeat, RepeatKeyword::Repeat) => SpecifiedValue::RepeatY,
-                (horizontal, vertical) => SpecifiedValue::Other(horizontal, Some(vertical)),
-            }
-        }
-    }
-
-    pub fn parse<'i, 't>(_context: &ParserContext, input: &mut Parser<'i, 't>)
-                         -> Result<SpecifiedValue, ParseError<'i>> {
-        let ident = input.expect_ident_cloned()?;
-        (match_ignore_ascii_case! { &ident,
-            "repeat-x" => Ok(SpecifiedValue::RepeatX),
-            "repeat-y" => Ok(SpecifiedValue::RepeatY),
-            _ => Err(()),
-        }).or_else(|()| {
-            let horizontal: Result<_, ParseError> = RepeatKeyword::from_ident(&ident)
-                .map_err(|()| input.new_custom_error(SelectorParseErrorKind::UnexpectedIdent(ident.clone())));
-            let horizontal = horizontal?;
-            let vertical = input.try(RepeatKeyword::parse).ok();
-            Ok(SpecifiedValue::Other(horizontal, vertical))
-        })
-    }
-</%helpers:vector_longhand>
+${helpers.predefined_type(
+    "background-repeat",
+    "BackgroundRepeat",
+    "computed::BackgroundRepeat::repeat()",
+    initial_specified_value="specified::BackgroundRepeat::repeat()",
+    animation_value_type="discrete",
+    vector=True,
+    spec="https://drafts.csswg.org/css-backgrounds/#the-background-repeat",
+    flags="APPLIES_TO_FIRST_LETTER APPLIES_TO_FIRST_LINE APPLIES_TO_PLACEHOLDER",
+)}
 
 ${helpers.single_keyword("background-attachment",
                          "scroll fixed" + (" local" if product == "gecko" else ""),
                          vector=True,
                          gecko_constant_prefix="NS_STYLE_IMAGELAYER_ATTACHMENT",
                          spec="https://drafts.csswg.org/css-backgrounds/#the-background-attachment",
                          animation_value_type="discrete",
                          flags="APPLIES_TO_FIRST_LETTER APPLIES_TO_FIRST_LINE APPLIES_TO_PLACEHOLDER")}
--- a/servo/components/style/properties/longhand/border.mako.rs
+++ b/servo/components/style/properties/longhand/border.mako.rs
@@ -217,19 +217,16 @@
     spec="https://drafts.csswg.org/css-backgrounds/#border-image-outset",
     animation_value_type="discrete",
     flags="APPLIES_TO_FIRST_LETTER",
     boxed=True)}
 
 <%helpers:longhand name="border-image-repeat" animation_value_type="discrete"
                    flags="APPLIES_TO_FIRST_LETTER"
                    spec="https://drafts.csswg.org/css-backgrounds/#border-image-repeat">
-    use style_traits::ToCss;
-
-
     pub mod computed_value {
         pub use super::RepeatKeyword;
 
         #[derive(Clone, Debug, MallocSizeOf, PartialEq, ToCss)]
         pub struct T(pub RepeatKeyword, pub RepeatKeyword);
     }
 
     #[derive(Clone, Debug, MallocSizeOf, PartialEq, ToCss)]
--- a/servo/components/style/properties/longhand/inherited_text.mako.rs
+++ b/servo/components/style/properties/longhand/inherited_text.mako.rs
@@ -107,17 +107,16 @@
                          animation_value_type="discrete",
                          spec="https://drafts.csswg.org/css-text/#propdef-text-align-last")}
 
 // TODO make this a shorthand and implement text-align-last/text-align-all
 <%helpers:longhand name="text-align" animation_value_type="discrete"
                    flags="APPLIES_TO_PLACEHOLDER"
                    spec="https://drafts.csswg.org/css-text/#propdef-text-align">
     pub mod computed_value {
-        use style_traits::ToCss;
         macro_rules! define_text_align {
             ( $( $name: ident ( $string: expr ) => $discriminant: expr, )+ ) => {
                 define_css_keyword_enum! { T:
                     $(
                         $string => $name,
                     )+
                 }
                 impl T {
@@ -579,18 +578,16 @@
             _ => 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">
-    use style_traits::ToCss;
-
     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);
--- a/servo/components/style/properties/longhand/svg.mako.rs
+++ b/servo/components/style/properties/longhand/svg.mako.rs
@@ -76,33 +76,27 @@
 
 ${helpers.single_keyword("mask-mode",
                          "match-source alpha luminance",
                          vector=True,
                          products="gecko",
                          animation_value_type="discrete",
                          spec="https://drafts.fxtf.org/css-masking/#propdef-mask-mode")}
 
-<%helpers:vector_longhand name="mask-repeat" products="gecko" animation_value_type="discrete" extra_prefixes="webkit"
-                          spec="https://drafts.fxtf.org/css-masking/#propdef-mask-repeat">
-    pub use properties::longhands::background_repeat::single_value::parse;
-    pub use properties::longhands::background_repeat::single_value::SpecifiedValue;
-    pub use properties::longhands::background_repeat::single_value::computed_value;
-    pub use properties::longhands::background_repeat::single_value::RepeatKeyword;
-
-    #[inline]
-    pub fn get_initial_value() -> computed_value::T {
-        computed_value::T(RepeatKeyword::Repeat, RepeatKeyword::Repeat)
-    }
-
-    #[inline]
-    pub fn get_initial_specified_value() -> SpecifiedValue {
-        SpecifiedValue::Other(RepeatKeyword::Repeat, None)
-    }
-</%helpers:vector_longhand>
+${helpers.predefined_type(
+    "mask-repeat",
+    "BackgroundRepeat",
+    "computed::BackgroundRepeat::repeat()",
+    initial_specified_value="specified::BackgroundRepeat::repeat()",
+    products="gecko",
+    extra_prefixes="webkit",
+    animation_value_type="discrete",
+    spec="https://drafts.fxtf.org/css-masking/#propdef-mask-repeat",
+    vector=True,
+)}
 
 % for (axis, direction) in [("x", "Horizontal"), ("y", "Vertical")]:
     ${helpers.predefined_type(
         "mask-position-" + axis,
         "position::" + direction + "Position",
         products="gecko",
         extra_prefixes="webkit",
         initial_value="computed::LengthOrPercentage::zero()",
--- a/servo/components/style/stylesheets/viewport_rule.rs
+++ b/servo/components/style/stylesheets/viewport_rule.rs
@@ -5,17 +5,17 @@
 //! The [`@viewport`][at] at-rule and [`meta`][meta] element.
 //!
 //! [at]: https://drafts.csswg.org/css-device-adapt/#atviewport-rule
 //! [meta]: https://drafts.csswg.org/css-device-adapt/#viewport-meta
 
 use app_units::Au;
 use context::QuirksMode;
 use cssparser::{AtRuleParser, DeclarationListParser, DeclarationParser, Parser, parse_important};
-use cssparser::{CowRcStr, ToCss as ParserToCss};
+use cssparser::CowRcStr;
 use error_reporting::{ContextualParseError, ParseErrorReporter};
 use euclid::TypedSize2D;
 use font_metrics::get_metrics_provider_for_product;
 use media_queries::Device;
 use parser::{ParserContext, ParserErrorContext};
 use properties::StyleBuilder;
 use rule_cache::RuleCacheConditions;
 use selectors::parser::SelectorParseErrorKind;
--- a/servo/components/style/values/computed/background.rs
+++ b/servo/components/style/values/computed/background.rs
@@ -1,19 +1,23 @@
 /* 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/. */
 
 //! Computed types for CSS values related to backgrounds.
 
 use properties::animated_properties::RepeatableListAnimatable;
 use properties::longhands::background_size::computed_value::T as BackgroundSizeList;
+use std::fmt;
+use style_traits::ToCss;
 use values::animated::{ToAnimatedValue, ToAnimatedZero};
+use values::computed::{Context, ToComputedValue};
 use values::computed::length::LengthOrPercentageOrAuto;
 use values::generics::background::BackgroundSize as GenericBackgroundSize;
+use values::specified::background::{BackgroundRepeat as SpecifiedBackgroundRepeat, RepeatKeyword};
 
 /// A computed value for the `background-size` property.
 pub type BackgroundSize = GenericBackgroundSize<LengthOrPercentageOrAuto>;
 
 impl BackgroundSize {
     /// Returns `auto auto`.
     pub fn auto() -> Self {
         GenericBackgroundSize::Explicit {
@@ -72,8 +76,77 @@ impl ToAnimatedValue for BackgroundSizeL
         self
     }
 
     #[inline]
     fn from_animated_value(animated: Self::AnimatedValue) -> Self {
         BackgroundSizeList(ToAnimatedValue::from_animated_value(animated.0))
     }
 }
+
+/// The computed value of the `background-repeat` property:
+///
+/// https://drafts.csswg.org/css-backgrounds/#the-background-repeat
+#[derive(Clone, Debug, MallocSizeOf, PartialEq)]
+pub struct BackgroundRepeat(pub RepeatKeyword, pub RepeatKeyword);
+
+impl BackgroundRepeat {
+    /// Returns the `repeat repeat` value.
+    pub fn repeat() -> Self {
+        BackgroundRepeat(RepeatKeyword::Repeat, RepeatKeyword::Repeat)
+    }
+}
+
+impl ToCss for BackgroundRepeat {
+    fn to_css<W>(&self, dest: &mut W) -> fmt::Result
+    where
+        W: fmt::Write,
+    {
+        match (self.0, self.1) {
+            (RepeatKeyword::Repeat, RepeatKeyword::NoRepeat) => dest.write_str("repeat-x"),
+            (RepeatKeyword::NoRepeat, RepeatKeyword::Repeat) => dest.write_str("repeat-y"),
+            (horizontal, vertical) => {
+                horizontal.to_css(dest)?;
+                if horizontal != vertical {
+                    dest.write_str(" ")?;
+                    vertical.to_css(dest)?;
+                }
+                Ok(())
+            },
+        }
+    }
+}
+
+impl ToComputedValue for SpecifiedBackgroundRepeat {
+    type ComputedValue = BackgroundRepeat;
+
+    #[inline]
+    fn to_computed_value(&self, _: &Context) -> Self::ComputedValue {
+        match *self {
+            SpecifiedBackgroundRepeat::RepeatX => {
+                BackgroundRepeat(RepeatKeyword::Repeat, RepeatKeyword::NoRepeat)
+            }
+            SpecifiedBackgroundRepeat::RepeatY => {
+                BackgroundRepeat(RepeatKeyword::NoRepeat, RepeatKeyword::Repeat)
+            }
+            SpecifiedBackgroundRepeat::Keywords(horizontal, vertical) => {
+                BackgroundRepeat(horizontal, vertical.unwrap_or(horizontal))
+            }
+        }
+    }
+
+    #[inline]
+    fn from_computed_value(computed: &Self::ComputedValue) -> Self {
+        // FIXME(emilio): Why can't this just be:
+        //   SpecifiedBackgroundRepeat::Keywords(computed.0, computed.1)
+        match (computed.0, computed.1) {
+            (RepeatKeyword::Repeat, RepeatKeyword::NoRepeat) => {
+                SpecifiedBackgroundRepeat::RepeatX
+            }
+            (RepeatKeyword::NoRepeat, RepeatKeyword::Repeat) => {
+                SpecifiedBackgroundRepeat::RepeatY
+            }
+            (horizontal, vertical) => {
+                SpecifiedBackgroundRepeat::Keywords(horizontal, Some(vertical))
+            }
+        }
+    }
+}
--- a/servo/components/style/values/computed/mod.rs
+++ b/servo/components/style/values/computed/mod.rs
@@ -28,17 +28,17 @@ use super::generics::grid::{TrackSize as
 use super::generics::grid::GridTemplateComponent as GenericGridTemplateComponent;
 use super::specified;
 
 pub use app_units::Au;
 pub use properties::animated_properties::TransitionProperty;
 #[cfg(feature = "gecko")]
 pub use self::align::{AlignItems, AlignJustifyContent, AlignJustifySelf, JustifyItems};
 pub use self::angle::Angle;
-pub use self::background::BackgroundSize;
+pub use self::background::{BackgroundSize, BackgroundRepeat};
 pub use self::border::{BorderImageSlice, BorderImageWidth, BorderImageSideWidth};
 pub use self::border::{BorderRadius, BorderCornerRadius, BorderSpacing};
 pub use self::box_::VerticalAlign;
 pub use self::color::{Color, ColorPropertyValue, RGBAColor};
 pub use self::effects::{BoxShadow, Filter, SimpleShadow};
 pub use self::flex::FlexBasis;
 pub use self::image::{Gradient, GradientItem, Image, ImageLayer, LineDirection, MozImageRect};
 #[cfg(feature = "gecko")]
--- a/servo/components/style/values/generics/size.rs
+++ b/servo/components/style/values/generics/size.rs
@@ -47,22 +47,16 @@ impl<L> Size<L> {
         let first = parse_one(context, input)?;
         let second = input
             .try(|i| parse_one(context, i))
             .unwrap_or_else(|_| first.clone());
         Ok(Self::new(first, second))
     }
 }
 
-impl<L: Clone> From<L> for Size<L> {
-    fn from(size: L) -> Self {
-        Self::new(size.clone(), size)
-    }
-}
-
 impl<L> ToCss for Size<L>
 where L:
     ToCss + PartialEq,
 {
     fn to_css<W>(&self, dest: &mut W) -> fmt::Result
     where W:
         fmt::Write
     {
--- a/servo/components/style/values/specified/background.rs
+++ b/servo/components/style/values/specified/background.rs
@@ -10,17 +10,20 @@ use selectors::parser::SelectorParseErro
 use style_traits::ParseError;
 use values::generics::background::BackgroundSize as GenericBackgroundSize;
 use values::specified::length::LengthOrPercentageOrAuto;
 
 /// A specified value for the `background-size` property.
 pub type BackgroundSize = GenericBackgroundSize<LengthOrPercentageOrAuto>;
 
 impl Parse for BackgroundSize {
-    fn parse<'i, 't>(context: &ParserContext, input: &mut Parser<'i, 't>) -> Result<Self, ParseError<'i>> {
+    fn parse<'i, 't>(
+        context: &ParserContext,
+        input: &mut Parser<'i, 't>,
+    ) -> Result<Self, ParseError<'i>> {
         if let Ok(width) = input.try(|i| LengthOrPercentageOrAuto::parse_non_negative(context, i)) {
             let height = input
                 .try(|i| LengthOrPercentageOrAuto::parse_non_negative(context, i))
                 .unwrap_or(LengthOrPercentageOrAuto::Auto);
             return Ok(GenericBackgroundSize::Explicit { width, height });
         }
         let location = input.current_source_location();
         let ident = input.expect_ident()?;
@@ -36,8 +39,64 @@ impl BackgroundSize {
     /// Returns `auto auto`.
     pub fn auto() -> Self {
         GenericBackgroundSize::Explicit {
             width: LengthOrPercentageOrAuto::Auto,
             height: LengthOrPercentageOrAuto::Auto,
         }
     }
 }
+
+/// One of the keywords for `background-repeat`.
+define_css_keyword_enum! { RepeatKeyword:
+    "repeat" => Repeat,
+    "space" => Space,
+    "round" => Round,
+    "no-repeat" => NoRepeat
+}
+
+/// The specified value for the `background-repeat` property.
+///
+/// https://drafts.csswg.org/css-backgrounds/#the-background-repeat
+#[derive(Clone, Debug, MallocSizeOf, PartialEq, ToCss)]
+pub enum BackgroundRepeat {
+    /// `repeat-x`
+    RepeatX,
+    /// `repeat-y`
+    RepeatY,
+    /// `[repeat | space | round | no-repeat]{1,2}`
+    Keywords(RepeatKeyword, Option<RepeatKeyword>),
+}
+
+impl BackgroundRepeat {
+    /// Returns the `repeat` value.
+    #[inline]
+    pub fn repeat() -> Self {
+        BackgroundRepeat::Keywords(RepeatKeyword::Repeat, None)
+    }
+}
+
+impl Parse for BackgroundRepeat {
+    fn parse<'i, 't>(
+        _context: &ParserContext,
+        input: &mut Parser<'i, 't>,
+    ) -> Result<Self, ParseError<'i>> {
+        let ident = input.expect_ident_cloned()?;
+
+        match_ignore_ascii_case! { &ident,
+            "repeat-x" => return Ok(BackgroundRepeat::RepeatX),
+            "repeat-y" => return Ok(BackgroundRepeat::RepeatY),
+            _ => {},
+        }
+
+        let horizontal = match RepeatKeyword::from_ident(&ident) {
+            Ok(h) => h,
+            Err(()) => {
+                return Err(input.new_custom_error(
+                    SelectorParseErrorKind::UnexpectedIdent(ident.clone())
+                ));
+            }
+        };
+
+        let vertical = input.try(RepeatKeyword::parse).ok();
+        Ok(BackgroundRepeat::Keywords(horizontal, vertical))
+    }
+}
--- a/servo/components/style/values/specified/color.rs
+++ b/servo/components/style/values/specified/color.rs
@@ -41,18 +41,16 @@ pub enum Color {
     /// Quirksmode-only rule for inheriting color from the body
     #[cfg(feature = "gecko")]
     InheritFromBodyQuirk,
 }
 
 
 #[cfg(feature = "gecko")]
 mod gecko {
-    use style_traits::ToCss;
-
     define_css_keyword_enum! { SpecialColorKeyword:
         "-moz-default-color" => MozDefaultColor,
         "-moz-default-background-color" => MozDefaultBackgroundColor,
         "-moz-hyperlinktext" => MozHyperlinktext,
         "-moz-activehyperlinktext" => MozActiveHyperlinktext,
         "-moz-visitedhyperlinktext" => MozVisitedHyperlinktext,
     }
 }
--- a/servo/components/style/values/specified/mod.rs
+++ b/servo/components/style/values/specified/mod.rs
@@ -22,17 +22,17 @@ use super::generics::{GreaterThanOrEqual
 use super::generics::grid::{GridLine as GenericGridLine, TrackBreadth as GenericTrackBreadth};
 use super::generics::grid::{TrackSize as GenericTrackSize, TrackList as GenericTrackList};
 use values::specified::calc::CalcNode;
 
 pub use properties::animated_properties::TransitionProperty;
 pub use self::angle::Angle;
 #[cfg(feature = "gecko")]
 pub use self::align::{AlignItems, AlignJustifyContent, AlignJustifySelf, JustifyItems};
-pub use self::background::BackgroundSize;
+pub use self::background::{BackgroundRepeat, BackgroundSize};
 pub use self::border::{BorderCornerRadius, BorderImageSlice, BorderImageWidth};
 pub use self::border::{BorderImageSideWidth, BorderRadius, BorderSideWidth, BorderSpacing};
 pub use self::box_::VerticalAlign;
 pub use self::color::{Color, ColorPropertyValue, RGBAColor};
 pub use self::effects::{BoxShadow, Filter, SimpleShadow};
 pub use self::flex::FlexBasis;
 #[cfg(feature = "gecko")]
 pub use self::gecko::ScrollSnapPoint;
--- a/servo/components/style_traits/values.rs
+++ b/servo/components/style_traits/values.rs
@@ -467,17 +467,17 @@ macro_rules! __define_css_keyword_enum__
                 match_ignore_ascii_case! { ident,
                                            $( $css => Ok($name::$variant), )+
                                            $( $alias => Ok($name::$alias_variant), )*
                                            _ => Err(())
                 }
             }
         }
 
-        impl ToCss for $name {
+        impl $crate::ToCss for $name {
             fn to_css<W>(&self, dest: &mut W) -> ::std::fmt::Result
                 where W: ::std::fmt::Write
             {
                 match *self {
                     $( $name::$variant => dest.write_str($css) ),+
                 }
             }
         }
--- a/servo/components/style_traits/viewport.rs
+++ b/servo/components/style_traits/viewport.rs
@@ -1,16 +1,16 @@
 /* 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/. */
 
 //! Helper types for the `@viewport` rule.
 
-use {CSSPixel, PinchZoomFactor, ParseError};
-use cssparser::{Parser, ToCss};
+use {CSSPixel, PinchZoomFactor, ParseError, ToCss};
+use cssparser::Parser;
 use euclid::TypedSize2D;
 use std::ascii::AsciiExt;
 use std::fmt;
 
 define_css_keyword_enum!(UserZoom:
                          "zoom" => Zoom,
                          "fixed" => Fixed);