servo: Merge #17036 - Use values::generics::rect::Rect some more (from servo:derive-all-the-things); r=emilio
authorAnthony Ramine <n.oxyde@gmail.com>
Sat, 27 May 2017 05:41:12 -0500
changeset 409103 59bbdf0d3ba2196e6cac27b38de450a8743df839
parent 409102 c2302acb2d2be8ea38fe7b19fe178194f63a420e
child 409104 d3e552f716987251e8b3b776a9c3759dfb8f6c0b
push id7391
push usermtabara@mozilla.com
push dateMon, 12 Jun 2017 13:08:53 +0000
treeherdermozilla-beta@2191d7f87e2e [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersemilio
bugs17036
milestone55.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
servo: Merge #17036 - Use values::generics::rect::Rect some more (from servo:derive-all-the-things); r=emilio Source-Repo: https://github.com/servo/servo Source-Revision: 7275f6598120266a0beaeff1294271a23cd40d4d
servo/components/layout/display_list_builder.rs
servo/components/style/gecko/conversions.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/properties.mako.rs
servo/components/style/properties/shorthand/border.mako.rs
servo/components/style/properties/shorthand/serialize.mako.rs
servo/components/style/values/computed/background.rs
servo/components/style/values/computed/mod.rs
servo/components/style/values/generics/background.rs
servo/components/style/values/generics/basic_shape.rs
servo/components/style/values/generics/mod.rs
servo/components/style/values/specified/background.rs
servo/components/style/values/specified/basic_shape.rs
servo/components/style/values/specified/mod.rs
servo/tests/unit/style/parsing/basic_shape.rs
servo/tests/unit/style/properties/serialization.rs
--- a/servo/components/layout/display_list_builder.rs
+++ b/servo/components/layout/display_list_builder.rs
@@ -43,29 +43,30 @@ use servo_config::opts;
 use servo_geometry::max_rect;
 use servo_url::ServoUrl;
 use std::{cmp, f32};
 use std::collections::HashMap;
 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, background_size, border_style, cursor};
+use style::computed_values::{background_repeat, border_style, cursor};
 use style::computed_values::{image_rendering, overflow_x, pointer_events, position, visibility};
 use style::computed_values::filter::Filter;
 use style::computed_values::text_shadow::TextShadow;
 use style::logical_geometry::{LogicalPoint, LogicalRect, LogicalSize, WritingMode};
 use style::properties::{self, ServoComputedValues};
 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::{Gradient, GradientItem, LengthOrPercentage};
 use style::values::computed::{LengthOrPercentageOrAuto, NumberOrPercentage, Position};
 use style::values::computed::image::{EndingShape, LineDirection};
+use style::values::generics::background::BackgroundSize;
 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::specified::position::{X, Y};
 use style_traits::CSSPixel;
 use style_traits::cursor::Cursor;
 use table_cell::CollapsedBordersForCell;
 use webrender_helpers::{ToMixBlendMode, ToTransformStyle};
@@ -912,53 +913,39 @@ impl FragmentDisplayListBuilding for Fra
         // If `image_aspect_ratio` < `bounds_aspect_ratio`, the image is tall; otherwise, it is
         // wide.
         let image_aspect_ratio = (image.width as f64) / (image.height as f64);
         let bounds_aspect_ratio = bounds.size.width.to_f64_px() / bounds.size.height.to_f64_px();
         let intrinsic_size = Size2D::new(Au::from_px(image.width as i32),
                                          Au::from_px(image.height as i32));
         let background_size = get_cyclic(&style.get_background().background_size.0, index).clone();
         match (background_size, image_aspect_ratio < bounds_aspect_ratio) {
-            (background_size::single_value::T::Contain, false) |
-            (background_size::single_value::T::Cover, true) => {
+            (BackgroundSize::Contain, false) | (BackgroundSize::Cover, true) => {
                 Size2D::new(bounds.size.width,
                             Au::from_f64_px(bounds.size.width.to_f64_px() / image_aspect_ratio))
             }
 
-            (background_size::single_value::T::Contain, true) |
-            (background_size::single_value::T::Cover, false) => {
+            (BackgroundSize::Contain, true) | (BackgroundSize::Cover, false) => {
                 Size2D::new(Au::from_f64_px(bounds.size.height.to_f64_px() * image_aspect_ratio),
                             bounds.size.height)
             }
 
-            (background_size::single_value::T::Explicit(background_size::single_value
-                                                                       ::ExplicitSize {
-                width,
-                height: LengthOrPercentageOrAuto::Auto,
-            }), _) => {
+            (BackgroundSize::Explicit { width, height: LengthOrPercentageOrAuto::Auto }, _) => {
                 let width = MaybeAuto::from_style(width, bounds.size.width)
                                       .specified_or_default(intrinsic_size.width);
                 Size2D::new(width, Au::from_f64_px(width.to_f64_px() / image_aspect_ratio))
             }
 
-            (background_size::single_value::T::Explicit(background_size::single_value
-                                                                       ::ExplicitSize {
-                width: LengthOrPercentageOrAuto::Auto,
-                height
-            }), _) => {
+            (BackgroundSize::Explicit { width: LengthOrPercentageOrAuto::Auto, height }, _) => {
                 let height = MaybeAuto::from_style(height, bounds.size.height)
                                        .specified_or_default(intrinsic_size.height);
                 Size2D::new(Au::from_f64_px(height.to_f64_px() * image_aspect_ratio), height)
             }
 
-            (background_size::single_value::T::Explicit(background_size::single_value
-                                                                       ::ExplicitSize {
-                width,
-                height
-            }), _) => {
+            (BackgroundSize::Explicit { width, height }, _) => {
                 Size2D::new(MaybeAuto::from_style(width, bounds.size.width)
                                  .specified_or_default(intrinsic_size.width),
                        MaybeAuto::from_style(height, bounds.size.height)
                                  .specified_or_default(intrinsic_size.height))
             }
         }
     }
 
--- a/servo/components/style/gecko/conversions.rs
+++ b/servo/components/style/gecko/conversions.rs
@@ -364,33 +364,37 @@ pub mod basic_shape {
     use std::borrow::Borrow;
     use values::computed::{BorderRadiusSize, LengthOrPercentage};
     use values::computed::basic_shape::{BasicShape, BorderRadius, ShapeRadius};
     use values::computed::position;
     use values::generics::BorderRadiusSize as GenericBorderRadiusSize;
     use values::generics::basic_shape::{BasicShape as GenericBasicShape, InsetRect, Polygon};
     use values::generics::basic_shape::{Circle, Ellipse, FillRule};
     use values::generics::basic_shape::{GeometryBox, ShapeBox};
+    use values::generics::rect::Rect;
 
     // using Borrow so that we can have a non-moving .into()
     impl<T: Borrow<StyleBasicShape>> From<T> for BasicShape {
         fn from(other: T) -> Self {
             let other = other.borrow();
             match other.mType {
                 StyleBasicShapeType::Inset => {
                     let t = LengthOrPercentage::from_gecko_style_coord(&other.mCoordinates[0]);
                     let r = LengthOrPercentage::from_gecko_style_coord(&other.mCoordinates[1]);
                     let b = LengthOrPercentage::from_gecko_style_coord(&other.mCoordinates[2]);
                     let l = LengthOrPercentage::from_gecko_style_coord(&other.mCoordinates[3]);
                     let round = (&other.mRadius).into();
+                    let rect = Rect::new(
+                        t.expect("inset() offset should be a length, percentage, or calc value"),
+                        r.expect("inset() offset should be a length, percentage, or calc value"),
+                        b.expect("inset() offset should be a length, percentage, or calc value"),
+                        l.expect("inset() offset should be a length, percentage, or calc value"),
+                    );
                     GenericBasicShape::Inset(InsetRect {
-                        top: t.expect("inset() offset should be a length, percentage, or calc value"),
-                        right: r.expect("inset() offset should be a length, percentage, or calc value"),
-                        bottom: b.expect("inset() offset should be a length, percentage, or calc value"),
-                        left: l.expect("inset() offset should be a length, percentage, or calc value"),
+                        rect: rect,
                         round: Some(round),
                     })
                 }
                 StyleBasicShapeType::Circle => {
                     GenericBasicShape::Circle(Circle {
                         radius: (&other.mCoordinates[0]).into(),
                         position: (&other.mPosition).into()
                     })
--- a/servo/components/style/properties/gecko.mako.rs
+++ b/servo/components/style/properties/gecko.mako.rs
@@ -2927,79 +2927,86 @@ fn static_assert() {
         }
     }
     % endfor
 
     <%self:simple_image_array_property name="size" shorthand="${shorthand}" field_name="mSize">
         use gecko_bindings::structs::nsStyleImageLayers_Size_Dimension;
         use gecko_bindings::structs::nsStyleImageLayers_Size_DimensionType;
         use gecko_bindings::structs::{nsStyleCoord_CalcValue, nsStyleImageLayers_Size};
-        use properties::longhands::background_size::single_value::computed_value::T;
+        use values::generics::background::BackgroundSize;
 
         let mut width = nsStyleCoord_CalcValue::new();
         let mut height = nsStyleCoord_CalcValue::new();
 
         let (w_type, h_type) = match servo {
-            T::Explicit(size) => {
+            BackgroundSize::Explicit { width: explicit_width, height: explicit_height } => {
                 let mut w_type = nsStyleImageLayers_Size_DimensionType::eAuto;
                 let mut h_type = nsStyleImageLayers_Size_DimensionType::eAuto;
-                if let Some(w) = size.width.to_calc_value() {
+                if let Some(w) = explicit_width.to_calc_value() {
                     width = w;
                     w_type = nsStyleImageLayers_Size_DimensionType::eLengthPercentage;
                 }
-                if let Some(h) = size.height.to_calc_value() {
+                if let Some(h) = explicit_height.to_calc_value() {
                     height = h;
                     h_type = nsStyleImageLayers_Size_DimensionType::eLengthPercentage;
                 }
                 (w_type, h_type)
             }
-            T::Cover => (nsStyleImageLayers_Size_DimensionType::eCover,
-                         nsStyleImageLayers_Size_DimensionType::eCover),
-            T::Contain => (nsStyleImageLayers_Size_DimensionType::eContain,
-                         nsStyleImageLayers_Size_DimensionType::eContain),
+            BackgroundSize::Cover => {
+                (
+                    nsStyleImageLayers_Size_DimensionType::eCover,
+                    nsStyleImageLayers_Size_DimensionType::eCover,
+                )
+            },
+            BackgroundSize::Contain => {
+                (
+                    nsStyleImageLayers_Size_DimensionType::eContain,
+                    nsStyleImageLayers_Size_DimensionType::eContain,
+                )
+            },
         };
 
         nsStyleImageLayers_Size {
             mWidth: nsStyleImageLayers_Size_Dimension { _base: width },
             mHeight: nsStyleImageLayers_Size_Dimension { _base: height },
             mWidthType: w_type as u8,
             mHeightType: h_type as u8,
         }
     </%self:simple_image_array_property>
 
     pub fn clone_${shorthand}_size(&self) -> longhands::background_size::computed_value::T {
         use gecko_bindings::structs::nsStyleCoord_CalcValue as CalcValue;
         use gecko_bindings::structs::nsStyleImageLayers_Size_DimensionType as DimensionType;
-        use properties::longhands::background_size::single_value::computed_value::{ExplicitSize, T};
         use values::computed::LengthOrPercentageOrAuto;
+        use values::generics::background::BackgroundSize;
 
         fn to_servo(value: CalcValue, ty: u8) -> LengthOrPercentageOrAuto {
             if ty == DimensionType::eAuto as u8 {
                 LengthOrPercentageOrAuto::Auto
             } else {
                 debug_assert!(ty == DimensionType::eLengthPercentage as u8);
                 LengthOrPercentageOrAuto::Calc(value.into())
             }
         }
 
         longhands::background_size::computed_value::T(
             self.gecko.${image_layers_field}.mLayers.iter().map(|ref layer| {
                 if DimensionType::eCover as u8 == layer.mSize.mWidthType {
                     debug_assert!(layer.mSize.mHeightType == DimensionType::eCover as u8);
-                    return T::Cover
+                    return BackgroundSize::Cover
                 }
                 if DimensionType::eContain as u8 == layer.mSize.mWidthType {
                     debug_assert!(layer.mSize.mHeightType == DimensionType::eContain as u8);
-                    return T::Contain
+                    return BackgroundSize::Contain
                 }
-
-                T::Explicit(ExplicitSize {
+                BackgroundSize::Explicit {
                     width: to_servo(layer.mSize.mWidth._base, layer.mSize.mWidthType),
                     height: to_servo(layer.mSize.mHeight._base, layer.mSize.mHeightType),
-                })
+                }
             }).collect()
         )
     }
 
 
     pub fn copy_${shorthand}_image_from(&mut self, other: &Self) {
         use gecko_bindings::structs::nsStyleImageLayers_LayerType as LayerType;
         unsafe {
@@ -3883,37 +3890,37 @@ fn static_assert() {
                         // We have to be very careful to avoid a copy here!
                         let ref mut union = ${ident}.__bindgen_anon_1;
                         let mut shape: &mut *mut StyleBasicShape = union.mBasicShape.as_mut();
                         *shape = Gecko_NewBasicShape(ty);
                         &mut **shape
                     }
                 }
                 match servo_shape {
-                    BasicShape::Inset(rect) => {
+                    BasicShape::Inset(inset) => {
                         let mut shape = init_shape(${ident}, StyleBasicShapeType::Inset);
                         unsafe { shape.mCoordinates.set_len(4) };
 
                         // set_len() can't call constructors, so the coordinates
                         // can contain any value. set_value() attempts to free
                         // allocated coordinates, so we don't want to feed it
                         // garbage values which it may misinterpret.
                         // Instead, we use leaky_set_value to blindly overwrite
                         // the garbage data without
                         // attempting to clean up.
                         shape.mCoordinates[0].leaky_set_null();
-                        rect.top.to_gecko_style_coord(&mut shape.mCoordinates[0]);
+                        inset.rect.top.to_gecko_style_coord(&mut shape.mCoordinates[0]);
                         shape.mCoordinates[1].leaky_set_null();
-                        rect.right.to_gecko_style_coord(&mut shape.mCoordinates[1]);
+                        inset.rect.right.to_gecko_style_coord(&mut shape.mCoordinates[1]);
                         shape.mCoordinates[2].leaky_set_null();
-                        rect.bottom.to_gecko_style_coord(&mut shape.mCoordinates[2]);
+                        inset.rect.bottom.to_gecko_style_coord(&mut shape.mCoordinates[2]);
                         shape.mCoordinates[3].leaky_set_null();
-                        rect.left.to_gecko_style_coord(&mut shape.mCoordinates[3]);
-
-                        set_corners_from_radius(rect.round, &mut shape.mRadius);
+                        inset.rect.left.to_gecko_style_coord(&mut shape.mCoordinates[3]);
+
+                        set_corners_from_radius(inset.round, &mut shape.mRadius);
                     }
                     BasicShape::Circle(circ) => {
                         let mut shape = init_shape(${ident}, StyleBasicShapeType::Circle);
                         unsafe { shape.mCoordinates.set_len(1) };
                         shape.mCoordinates[0].leaky_set_null();
                         circ.radius.to_gecko_style_coord(&mut shape.mCoordinates[0]);
 
                         shape.mPosition = circ.position.into();
--- a/servo/components/style/properties/helpers.mako.rs
+++ b/servo/components/style/properties/helpers.mako.rs
@@ -882,45 +882,44 @@
 </%def>
 
 <%def name="four_sides_shorthand(name, sub_property_pattern, parser_function,
                                  needs_context=True, allow_quirks=False, **kwargs)">
     <% sub_properties=' '.join(sub_property_pattern % side for side in ['top', 'right', 'bottom', 'left']) %>
     <%call expr="self.shorthand(name, sub_properties=sub_properties, **kwargs)">
         #[allow(unused_imports)]
         use parser::Parse;
-        use super::parse_four_sides;
+        use values::generics::rect::Rect;
         use values::specified;
 
         pub fn parse_value(context: &ParserContext, input: &mut Parser) -> Result<Longhands, ()> {
-            let (top, right, bottom, left) =
+            let rect = Rect::parse_with(context, input, |_c, i| {
             % if allow_quirks:
-                try!(parse_four_sides(input, |i| ${parser_function}_quirky(context, i, specified::AllowQuirks::Yes)));
+                ${parser_function}_quirky(_c, i, specified::AllowQuirks::Yes)
             % elif needs_context:
-                try!(parse_four_sides(input, |i| ${parser_function}(context, i)));
+                ${parser_function}(_c, i)
             % else:
-                try!(parse_four_sides(input, ${parser_function}));
-                let _unused = context;
+                ${parser_function}(i)
             % endif
+            })?;
             Ok(expanded! {
                 % for side in ["top", "right", "bottom", "left"]:
-                    ${to_rust_ident(sub_property_pattern % side)}: ${side},
+                    ${to_rust_ident(sub_property_pattern % side)}: rect.${side},
                 % endfor
             })
         }
 
         impl<'a> ToCss for LonghandsToSerialize<'a> {
             fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
-                super::serialize_four_sides(
-                    dest,
-                    self.${to_rust_ident(sub_property_pattern % 'top')},
-                    self.${to_rust_ident(sub_property_pattern % 'right')},
-                    self.${to_rust_ident(sub_property_pattern % 'bottom')},
-                    self.${to_rust_ident(sub_property_pattern % 'left')}
-                )
+                let rect = Rect::new(
+                    % for side in ["top", "right", "bottom", "left"]:
+                    &self.${to_rust_ident(sub_property_pattern % side)},
+                    % endfor
+                );
+                rect.to_css(dest)
             }
         }
     </%call>
 </%def>
 
 <%def name="logical_setter_helper(name)">
     <%
         side = None
--- a/servo/components/style/properties/longhand/background.mako.rs
+++ b/servo/components/style/properties/longhand/background.mako.rs
@@ -155,198 +155,23 @@
                          animation_value_type="none")}
 
 ${helpers.single_keyword("background-origin",
                          "padding-box border-box content-box",
                          vector=True, extra_prefixes="webkit",
                          spec="https://drafts.csswg.org/css-backgrounds/#the-background-origin",
                          animation_value_type="none")}
 
-<%helpers:vector_longhand name="background-size" animation_value_type="ComputedValue" extra_prefixes="webkit"
-                          spec="https://drafts.csswg.org/css-backgrounds/#the-background-size">
-    use std::fmt;
-    use style_traits::ToCss;
-
-    #[allow(missing_docs)]
-    pub mod computed_value {
-        use values::computed::LengthOrPercentageOrAuto;
-        use properties::animated_properties::{Animatable, RepeatableListAnimatable};
-
-        #[derive(PartialEq, Clone, Debug)]
-        #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
-        pub struct ExplicitSize {
-            pub width: LengthOrPercentageOrAuto,
-            pub height: LengthOrPercentageOrAuto,
-        }
-
-        #[derive(PartialEq, Clone, Debug)]
-        #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
-        pub enum T {
-            Explicit(ExplicitSize),
-            Cover,
-            Contain,
-        }
-
-        impl RepeatableListAnimatable for T {}
-
-        impl Animatable for T {
-            fn add_weighted(&self, other: &Self, self_portion: f64, other_portion: f64)
-                -> Result<Self, ()> {
-                use properties::longhands::background_size::single_value::computed_value::ExplicitSize;
-                match (self, other) {
-                    (&T::Explicit(ref me), &T::Explicit(ref other)) => {
-                        Ok(T::Explicit(ExplicitSize {
-                            width: try!(me.width.add_weighted(&other.width,
-                                                              self_portion, other_portion)),
-                            height: try!(me.height.add_weighted(&other.height,
-                                                                self_portion, other_portion)),
-                        }))
-                    }
-                    _ => Err(()),
-                }
-            }
-
-            #[inline]
-            fn compute_distance(&self, other: &Self) -> Result<f64, ()> {
-                self.compute_squared_distance(other).map(|sd| sd.sqrt())
-            }
-
-            #[inline]
-            fn compute_squared_distance(&self, other: &Self) -> Result<f64, ()> {
-                match (self, other) {
-                    (&T::Explicit(ref me), &T::Explicit(ref other)) => {
-                        Ok(try!(me.width.compute_squared_distance(&other.width)) +
-                           try!(me.height.compute_squared_distance(&other.height)))
-                    },
-                    _ => Err(())
-                }
-            }
-        }
-    }
-
-    impl ToCss for computed_value::T {
-        fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
-            match *self {
-                computed_value::T::Explicit(ref size) => size.to_css(dest),
-                computed_value::T::Cover => dest.write_str("cover"),
-                computed_value::T::Contain => dest.write_str("contain"),
-            }
-        }
-    }
-
-    #[derive(Clone, Debug, HasViewportPercentage, PartialEq)]
-    #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
-    #[allow(missing_docs)]
-    pub struct ExplicitSize {
-        pub width: specified::LengthOrPercentageOrAuto,
-        pub height: specified::LengthOrPercentageOrAuto,
-    }
-
-    impl ToCss for ExplicitSize {
-        fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
-            try!(self.width.to_css(dest));
-            try!(dest.write_str(" "));
-            self.height.to_css(dest)
-        }
-    }
-
-    impl ToCss for computed_value::ExplicitSize {
-        fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
-            try!(self.width.to_css(dest));
-            try!(dest.write_str(" "));
-            self.height.to_css(dest)
-        }
-    }
-
-    #[derive(Clone, Debug, HasViewportPercentage, PartialEq)]
-    #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
-    pub enum SpecifiedValue {
-        Explicit(ExplicitSize),
-        Cover,
-        Contain,
-    }
-
-    impl ToCss for SpecifiedValue {
-        fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
-            match *self {
-                SpecifiedValue::Explicit(ref size) => size.to_css(dest),
-                SpecifiedValue::Cover => dest.write_str("cover"),
-                SpecifiedValue::Contain => dest.write_str("contain"),
-            }
-        }
-    }
-
-    impl ToComputedValue for SpecifiedValue {
-        type ComputedValue = computed_value::T;
-
-        #[inline]
-        fn to_computed_value(&self, context: &Context) -> computed_value::T {
-            match *self {
-                SpecifiedValue::Explicit(ref size) => {
-                    computed_value::T::Explicit(computed_value::ExplicitSize {
-                        width: size.width.to_computed_value(context),
-                        height: size.height.to_computed_value(context),
-                    })
-                }
-                SpecifiedValue::Cover => computed_value::T::Cover,
-                SpecifiedValue::Contain => computed_value::T::Contain,
-            }
-        }
-        #[inline]
-        fn from_computed_value(computed: &computed_value::T) -> Self {
-            match *computed {
-                computed_value::T::Explicit(ref size) => {
-                    SpecifiedValue::Explicit(ExplicitSize {
-                        width: ToComputedValue::from_computed_value(&size.width),
-                        height: ToComputedValue::from_computed_value(&size.height),
-                    })
-                }
-                computed_value::T::Cover => SpecifiedValue::Cover,
-                computed_value::T::Contain => SpecifiedValue::Contain,
-            }
-        }
-    }
-
-    #[inline]
-    pub fn get_initial_value() -> computed_value::T {
-        computed_value::T::Explicit(computed_value::ExplicitSize {
-            width: computed::LengthOrPercentageOrAuto::Auto,
-            height: computed::LengthOrPercentageOrAuto::Auto,
-        })
-    }
-    #[inline]
-    pub fn get_initial_specified_value() -> SpecifiedValue {
-        SpecifiedValue::Explicit(ExplicitSize {
-            width: specified::LengthOrPercentageOrAuto::Auto,
-            height: specified::LengthOrPercentageOrAuto::Auto,
-        })
-    }
-
-    pub fn parse(context: &ParserContext, input: &mut Parser) -> Result<SpecifiedValue,()> {
-        if input.try(|input| input.expect_ident_matching("cover")).is_ok() {
-            return Ok(SpecifiedValue::Cover);
-        }
-
-        if input.try(|input| input.expect_ident_matching("contain")).is_ok() {
-            return Ok(SpecifiedValue::Contain);
-        }
-
-        let width =
-            try!(specified::LengthOrPercentageOrAuto::parse_non_negative(context, input));
-
-        let height = input.try(|input| {
-            specified::LengthOrPercentageOrAuto::parse_non_negative(context, input)
-        }).unwrap_or(specified::LengthOrPercentageOrAuto::Auto);
-
-        Ok(SpecifiedValue::Explicit(ExplicitSize {
-            width: width,
-            height: height,
-        }))
-    }
-</%helpers:vector_longhand>
+${helpers.predefined_type("background-size", "BackgroundSize",
+    initial_value="computed::LengthOrPercentageOrAuto::Auto.into()",
+    initial_specified_value="specified::LengthOrPercentageOrAuto::Auto.into()",
+    spec="https://drafts.csswg.org/css-backgrounds/#the-background-size",
+    vector=True,
+    animation_value_type="ComputedValue",
+    extra_prefixes="webkit")}
 
 // https://drafts.fxtf.org/compositing/#background-blend-mode
 ${helpers.single_keyword("background-blend-mode",
                          """normal multiply screen overlay darken lighten color-dodge
                             color-burn hard-light soft-light difference exclusion hue
                             saturation color luminosity""",
                          vector=True, products="gecko", animation_value_type="none",
                          spec="https://drafts.fxtf.org/compositing/#background-blend-mode")}
--- a/servo/components/style/properties/properties.mako.rs
+++ b/servo/components/style/properties/properties.mako.rs
@@ -167,65 +167,16 @@ macro_rules! unwrap_or_initial {
 /// A module with code for all the shorthand css properties, and a few
 /// serialization helpers.
 #[allow(missing_docs)]
 pub mod shorthands {
     use cssparser::Parser;
     use parser::{Parse, ParserContext};
     use values::specified;
 
-    /// Parses a property for four different sides per CSS syntax.
-    ///
-    ///  * Zero or more than four values is invalid.
-    ///  * One value sets them all
-    ///  * Two values set (top, bottom) and (left, right)
-    ///  * Three values set top, (left, right) and bottom
-    ///  * Four values set them in order
-    ///
-    /// returns the values in (top, right, bottom, left) order.
-    pub fn parse_four_sides<F, T>(input: &mut Parser, parse_one: F) -> Result<(T, T, T, T), ()>
-        where F: Fn(&mut Parser) -> Result<T, ()>,
-              T: Clone,
-    {
-        let top = try!(parse_one(input));
-        let right;
-        let bottom;
-        let left;
-        match input.try(|i| parse_one(i)) {
-            Err(()) => {
-                right = top.clone();
-                bottom = top.clone();
-                left = top.clone();
-            }
-            Ok(value) => {
-                right = value;
-                match input.try(|i| parse_one(i)) {
-                    Err(()) => {
-                        bottom = top.clone();
-                        left = right.clone();
-                    }
-                    Ok(value) => {
-                        bottom = value;
-                        match input.try(|i| parse_one(i)) {
-                            Err(()) => {
-                                left = right.clone();
-                            }
-                            Ok(value) => {
-                                left = value;
-                            }
-                        }
-
-                    }
-                }
-
-            }
-        }
-        Ok((top, right, bottom, left))
-    }
-
     <%include file="/shorthand/serialize.mako.rs" />
     <%include file="/shorthand/background.mako.rs" />
     <%include file="/shorthand/border.mako.rs" />
     <%include file="/shorthand/box.mako.rs" />
     <%include file="/shorthand/column.mako.rs" />
     <%include file="/shorthand/font.mako.rs" />
     <%include file="/shorthand/inherited_text.mako.rs" />
     <%include file="/shorthand/list.mako.rs" />
--- a/servo/components/style/properties/shorthand/border.mako.rs
+++ b/servo/components/style/properties/shorthand/border.mako.rs
@@ -12,37 +12,38 @@
 ${helpers.four_sides_shorthand("border-style", "border-%s-style",
                                "specified::BorderStyle::parse",
                                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 super::parse_four_sides;
+    use values::generics::rect::Rect;
     use values::specified::{AllowQuirks, BorderWidth};
 
     pub fn parse_value(context: &ParserContext, input: &mut Parser) -> Result<Longhands, ()> {
-        let (top, right, bottom, left) = try!(parse_four_sides(input, |i| {
+        let rect = Rect::parse_with(context, input, |_, i| {
             BorderWidth::parse_quirky(context, i, AllowQuirks::Yes)
-        }));
+        })?;
         Ok(expanded! {
             % for side in PHYSICAL_SIDES:
-                ${to_rust_ident('border-%s-width' % side)}: ${side},
+                ${to_rust_ident('border-%s-width' % side)}: rect.${side},
             % endfor
         })
     }
 
     impl<'a> ToCss for LonghandsToSerialize<'a>  {
         fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
-            % for side in PHYSICAL_SIDES:
-                let ${side} = self.border_${side}_width.clone();
-            % endfor
-
-            super::serialize_four_sides(dest, &top, &right, &bottom, &left)
+            let rect = Rect {
+                % for side in PHYSICAL_SIDES:
+                ${side}: &self.border_${side}_width,
+                % endfor
+            };
+            rect.to_css(dest)
         }
     }
 </%helpers:shorthand>
 
 
 pub fn parse_border(context: &ParserContext, input: &mut Parser)
                  -> Result<(specified::CSSColor,
                             specified::BorderStyle,
--- a/servo/components/style/properties/shorthand/serialize.mako.rs
+++ b/servo/components/style/properties/shorthand/serialize.mako.rs
@@ -1,67 +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/. */
 
 use style_traits::ToCss;
 use values::specified::{BorderStyle, Color, CSSColor};
 use std::fmt;
 
-#[allow(missing_docs)]
-pub fn serialize_four_sides<W, I>(dest: &mut W,
-                                  top: &I,
-                                  right: &I,
-                                  bottom: &I,
-                                  left: &I)
-                                  -> fmt::Result
-    where W: fmt::Write,
-          I: ToCss + PartialEq,
-{
-
-    if left == right {
-        let horizontal_value = left;
-
-        if top == bottom {
-            let vertical_value = top;
-
-            if horizontal_value == vertical_value {
-                let single_value = horizontal_value;
-                try!(single_value.to_css(dest));
-            } else {
-                try!(vertical_value.to_css(dest));
-                try!(write!(dest, " "));
-
-                try!(horizontal_value.to_css(dest));
-            }
-        } else {
-            try!(top.to_css(dest));
-            try!(write!(dest, " "));
-
-            try!(horizontal_value.to_css(dest));
-            try!(write!(dest, " "));
-
-            try!(bottom.to_css(dest));
-        }
-    } else {
-        try!(top.to_css(dest));
-        try!(write!(dest, " "));
-
-        try!(right.to_css(dest));
-        try!(write!(dest, " "));
-
-        try!(bottom.to_css(dest));
-        try!(write!(dest, " "));
-
-        try!(left.to_css(dest));
-    }
-
-    Ok(())
-}
-
 fn serialize_directional_border<W, I,>(dest: &mut W,
                                        width: &I,
                                        style: &BorderStyle,
                                        color: &CSSColor)
     -> fmt::Result where W: fmt::Write, I: ToCss {
     width.to_css(dest)?;
     dest.write_str(" ")?;
     style.to_css(dest)?;
new file mode 100644
--- /dev/null
+++ b/servo/components/style/values/computed/background.rs
@@ -0,0 +1,52 @@
+/* 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::{Animatable, RepeatableListAnimatable};
+use values::computed::length::LengthOrPercentageOrAuto;
+use values::generics::background::BackgroundSize as GenericBackgroundSize;
+
+/// A computed value for the `background-size` property.
+pub type BackgroundSize = GenericBackgroundSize<LengthOrPercentageOrAuto>;
+
+impl RepeatableListAnimatable for BackgroundSize {}
+
+impl Animatable for BackgroundSize {
+    fn add_weighted(&self, other: &Self, self_portion: f64, other_portion: f64) -> Result<Self, ()> {
+        match (self, other) {
+            (
+                &GenericBackgroundSize::Explicit { width: self_width, height: self_height },
+                &GenericBackgroundSize::Explicit { width: other_width, height: other_height },
+            ) => {
+                Ok(GenericBackgroundSize::Explicit {
+                    width: self_width.add_weighted(&other_width, self_portion, other_portion)?,
+                    height: self_height.add_weighted(&other_height, self_portion, other_portion)?,
+                })
+            }
+            _ => Err(()),
+        }
+    }
+
+    #[inline]
+    fn compute_distance(&self, other: &Self) -> Result<f64, ()> {
+        self.compute_squared_distance(other).map(|sd| sd.sqrt())
+    }
+
+    #[inline]
+    fn compute_squared_distance(&self, other: &Self) -> Result<f64, ()> {
+        match (self, other) {
+            (
+                &GenericBackgroundSize::Explicit { width: self_width, height: self_height },
+                &GenericBackgroundSize::Explicit { width: other_width, height: other_height },
+            ) => {
+                Ok(
+                    self_width.compute_squared_distance(&other_width)? +
+                    self_height.compute_squared_distance(&other_height)?
+                )
+            }
+            _ => Err(()),
+        }
+    }
+}
--- a/servo/components/style/values/computed/mod.rs
+++ b/servo/components/style/values/computed/mod.rs
@@ -19,30 +19,32 @@ use style_traits::ToCss;
 use super::{CSSFloat, CSSInteger, RGBA};
 use super::generics::BorderRadiusSize as GenericBorderRadiusSize;
 use super::generics::grid::{TrackBreadth as GenericTrackBreadth, TrackSize as GenericTrackSize};
 use super::generics::grid::TrackList as GenericTrackList;
 use super::specified;
 
 pub use app_units::Au;
 pub use cssparser::Color as CSSColor;
+pub use self::background::BackgroundSize;
 pub use self::border::{BorderImageSlice, BorderImageWidth, BorderImageWidthSide};
 pub use self::image::{Gradient, GradientItem, ImageLayer, LineDirection, Image, ImageRect};
 pub use self::rect::LengthOrNumberRect;
 pub use super::{Auto, Either, None_};
 #[cfg(feature = "gecko")]
 pub use super::specified::{AlignItems, AlignJustifyContent, AlignJustifySelf, JustifyItems};
 pub use super::specified::{BorderStyle, Percentage, UrlOrNone};
 pub use super::generics::grid::GridLine;
 pub use super::specified::url::SpecifiedUrl;
 pub use self::length::{CalcLengthOrPercentage, Length, LengthOrNumber, LengthOrPercentage, LengthOrPercentageOrAuto};
 pub use self::length::{LengthOrPercentageOrAutoOrContent, LengthOrPercentageOrNone, LengthOrNone};
 pub use self::length::{MaxLength, MozLength};
 pub use self::position::Position;
 
+pub mod background;
 pub mod basic_shape;
 pub mod border;
 pub mod image;
 pub mod length;
 pub mod position;
 pub mod rect;
 
 /// A `Context` is all the data a specified value could ever need to compute
new file mode 100644
--- /dev/null
+++ b/servo/components/style/values/generics/background.rs
@@ -0,0 +1,52 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+//! Generic types for CSS values related to backgrounds.
+
+use std::fmt;
+use style_traits::ToCss;
+
+/// A generic value for the `background-size` property.
+#[derive(Clone, Copy, Debug, HasViewportPercentage, PartialEq, ToComputedValue)]
+#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
+pub enum BackgroundSize<LengthOrPercentageOrAuto> {
+    /// `<width> <height>`
+    Explicit {
+        /// Explicit width.
+        width: LengthOrPercentageOrAuto,
+        /// Explicit height.
+        height: LengthOrPercentageOrAuto
+    },
+    /// `cover`
+    Cover,
+    /// `contain`
+    Contain,
+}
+
+impl<L> From<L> for BackgroundSize<L>
+    where L: Clone,
+{
+    #[inline]
+    fn from(value: L) -> Self {
+        BackgroundSize::Explicit { width: value.clone(), height: value }
+    }
+}
+
+impl<L> ToCss for BackgroundSize<L>
+    where L: ToCss
+{
+    fn to_css<W>(&self, dest: &mut W) -> fmt::Result
+        where W: fmt::Write
+    {
+        match *self {
+            BackgroundSize::Explicit { ref width, ref height } => {
+                width.to_css(dest)?;
+                dest.write_str(" ")?;
+                height.to_css(dest)
+            },
+            BackgroundSize::Cover => dest.write_str("cover"),
+            BackgroundSize::Contain => dest.write_str("contain"),
+        }
+    }
+}
--- a/servo/components/style/values/generics/basic_shape.rs
+++ b/servo/components/style/values/generics/basic_shape.rs
@@ -1,22 +1,22 @@
 /* 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/. */
 
 //! CSS handling for the [`basic-shape`](https://drafts.csswg.org/css-shapes/#typedef-basic-shape)
 //! types that are generic over their `ToCss` implementations.
 
 use euclid::size::Size2D;
-use properties::shorthands::serialize_four_sides;
 use std::fmt;
 use style_traits::{HasViewportPercentage, ToCss};
 use values::computed::ComputedValueAsSpecified;
 use values::generics::BorderRadiusSize;
 use values::generics::position::Position;
+use values::generics::rect::Rect;
 use values::specified::url::SpecifiedUrl;
 
 /// A clipping shape, for `clip-path`.
 pub type ClippingShape<BasicShape> = ShapeSource<BasicShape, GeometryBox>;
 
 /// https://drafts.fxtf.org/css-masking-1/#typedef-geometry-box
 #[allow(missing_docs)]
 #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
@@ -62,20 +62,17 @@ pub enum BasicShape<H, V, LengthOrPercen
     Polygon(Polygon<LengthOrPercentage>),
 }
 
 /// https://drafts.csswg.org/css-shapes/#funcdef-inset
 #[allow(missing_docs)]
 #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
 #[derive(Clone, Debug, PartialEq, ToComputedValue)]
 pub struct InsetRect<LengthOrPercentage> {
-    pub top: LengthOrPercentage,
-    pub right: LengthOrPercentage,
-    pub bottom: LengthOrPercentage,
-    pub left: LengthOrPercentage,
+    pub rect: Rect<LengthOrPercentage>,
     pub round: Option<BorderRadius<LengthOrPercentage>>,
 }
 
 /// A generic type used for `border-radius`, `outline-radius` and `inset()` values.
 ///
 /// https://drafts.csswg.org/css-backgrounds-3/#border-radius
 #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
 #[derive(Clone, Debug, PartialEq, ToComputedValue)]
@@ -185,32 +182,26 @@ impl<H, V, L> ToCss for BasicShape<H, V,
             BasicShape::Inset(ref rect) => rect.to_css(dest),
             BasicShape::Circle(ref circle) => circle.to_css(dest),
             BasicShape::Ellipse(ref ellipse) => ellipse.to_css(dest),
             BasicShape::Polygon(ref polygon) => polygon.to_css(dest),
         }
     }
 }
 
-impl<L: ToCss + PartialEq> ToCss for InsetRect<L> {
-    // XXXManishearth We should try to reduce the number of values printed here
+impl<L> ToCss for InsetRect<L>
+    where L: ToCss + PartialEq
+{
     fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
         dest.write_str("inset(")?;
-        self.top.to_css(dest)?;
-        dest.write_str(" ")?;
-        self.right.to_css(dest)?;
-        dest.write_str(" ")?;
-        self.bottom.to_css(dest)?;
-        dest.write_str(" ")?;
-        self.left.to_css(dest)?;
+        self.rect.to_css(dest)?;
         if let Some(ref radius) = self.round {
             dest.write_str(" round ")?;
             radius.to_css(dest)?;
         }
-
         dest.write_str(")")
     }
 }
 
 impl<L: ToCss + PartialEq> ToCss for BorderRadius<L> {
     #[inline]
     fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
         serialize_radius_values(dest, &self.top_left.0, &self.top_right.0,
@@ -219,27 +210,27 @@ impl<L: ToCss + PartialEq> ToCss for Bor
 }
 
 /// Serialization helper for types of longhands like `border-radius` and `outline-radius`
 pub fn serialize_radius_values<L, W>(dest: &mut W, top_left: &Size2D<L>,
                                      top_right: &Size2D<L>, bottom_right: &Size2D<L>,
                                      bottom_left: &Size2D<L>) -> fmt::Result
     where L: ToCss + PartialEq, W: fmt::Write
 {
-    if top_left.width == top_left.height && top_right.width == top_right.height &&
-       bottom_right.width == bottom_right.height && bottom_left.width == bottom_left.height {
-        serialize_four_sides(dest, &top_left.width, &top_right.width,
-                             &bottom_right.width, &bottom_left.width)
-    } else {
-        serialize_four_sides(dest, &top_left.width, &top_right.width,
-                             &bottom_right.width, &bottom_left.width)?;
+    Rect::new(&top_left.width, &top_right.width, &bottom_right.width, &bottom_left.width).to_css(dest)?;
+    if
+        top_left.width != top_left.height ||
+        top_right.width != top_right.height ||
+        bottom_right.width != bottom_right.height ||
+        bottom_left.width != bottom_left.height
+    {
         dest.write_str(" / ")?;
-        serialize_four_sides(dest, &top_left.height, &top_right.height,
-                             &bottom_right.height, &bottom_left.height)
+        Rect::new(&top_left.height, &top_right.height, &bottom_right.height, &bottom_left.height).to_css(dest)?;
     }
+    Ok(())
 }
 
 impl<L> Default for ShapeRadius<L> {
     #[inline]
     fn default() -> Self { ShapeRadius::ClosestSide }
 }
 
 impl<L: ToCss> ToCss for ShapeRadius<L> {
--- a/servo/components/style/values/generics/mod.rs
+++ b/servo/components/style/values/generics/mod.rs
@@ -10,16 +10,17 @@ use cssparser::Parser;
 use euclid::size::Size2D;
 use parser::{Parse, ParserContext};
 use std::fmt;
 use style_traits::{HasViewportPercentage, ToCss};
 use super::CustomIdent;
 
 pub use self::basic_shape::serialize_radius_values;
 
+pub mod background;
 pub mod basic_shape;
 pub mod border;
 pub mod grid;
 pub mod image;
 pub mod position;
 pub mod rect;
 
 #[derive(Clone, Debug, PartialEq, ToComputedValue)]
new file mode 100644
--- /dev/null
+++ b/servo/components/style/values/specified/background.rs
@@ -0,0 +1,29 @@
+/* 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/. */
+
+//! Specified types for CSS values related to backgrounds.
+
+use cssparser::Parser;
+use parser::{Parse, ParserContext};
+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(context: &ParserContext, input: &mut Parser) -> Result<Self, ()> {
+        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: width, height: height });
+        }
+        match_ignore_ascii_case! { &input.expect_ident()?,
+            "cover" => Ok(GenericBackgroundSize::Cover),
+            "contain" => Ok(GenericBackgroundSize::Contain),
+            _ => Err(()),
+        }
+    }
+}
--- a/servo/components/style/values/specified/basic_shape.rs
+++ b/servo/components/style/values/specified/basic_shape.rs
@@ -4,27 +4,27 @@
 
 //! CSS handling for the specified value of
 //! [`basic-shape`][basic-shape]s
 //!
 //! [basic-shape]: https://drafts.csswg.org/css-shapes/#typedef-basic-shape
 
 use cssparser::Parser;
 use parser::{Parse, ParserContext};
-use properties::shorthands::parse_four_sides;
 use std::borrow::Cow;
 use std::fmt;
 use style_traits::ToCss;
 use values::generics::BorderRadiusSize;
 use values::generics::basic_shape::{BorderRadius as GenericBorderRadius, Circle as GenericCircle};
 use values::generics::basic_shape::{ClippingShape as GenericClippingShape, Ellipse as GenericEllipse};
 use values::generics::basic_shape::{FillRule, BasicShape as GenericBasicShape};
 use values::generics::basic_shape::{FloatAreaShape as GenericFloatAreaShape, InsetRect as GenericInsetRect};
 use values::generics::basic_shape::{GeometryBox, ShapeBox, ShapeSource};
 use values::generics::basic_shape::{Polygon as GenericPolygon, ShapeRadius as GenericShapeRadius};
+use values::generics::rect::Rect;
 use values::specified::{LengthOrPercentage, Percentage};
 use values::specified::position::{HorizontalPosition, Position, PositionComponent, Side, VerticalPosition};
 use values::specified::url::SpecifiedUrl;
 
 /// A specified clipping shape.
 pub type ClippingShape = GenericClippingShape<BasicShape>;
 
 /// A specified float area shape.
@@ -122,28 +122,25 @@ impl Parse for InsetRect {
         input.expect_function_matching("inset")?;
         input.parse_nested_block(|i| Self::parse_function_arguments(context, i))
     }
 }
 
 impl InsetRect {
     /// Parse the inner function arguments of `inset()`
     pub fn parse_function_arguments(context: &ParserContext, input: &mut Parser) -> Result<Self, ()> {
-        let (t, r, b, l) = parse_four_sides(input, |i| LengthOrPercentage::parse(context, i))?;
-        let rect = if input.try(|i| i.expect_ident_matching("round")).is_ok() {
+        let rect = Rect::parse_with(context, input, LengthOrPercentage::parse)?;
+        let round = if input.try(|i| i.expect_ident_matching("round")).is_ok() {
             Some(BorderRadius::parse(context, input)?)
         } else {
             None
         };
         Ok(GenericInsetRect {
-            top: t,
-            right: r,
-            bottom: b,
-            left: l,
-            round: rect,
+            rect: rect,
+            round: round,
         })
     }
 }
 
 impl Parse for BorderRadius {
     fn parse(context: &ParserContext, input: &mut Parser) -> Result<Self, ()> {
         let mut widths = parse_one_set_of_border_values(context, input)?;
         let mut heights = if input.try(|input| input.expect_delim('/')).is_ok() {
--- a/servo/components/style/values/specified/mod.rs
+++ b/servo/components/style/values/specified/mod.rs
@@ -25,31 +25,33 @@ use super::computed::{self, Context};
 use super::computed::{Shadow as ComputedShadow, ToComputedValue};
 use super::generics::BorderRadiusSize as GenericBorderRadiusSize;
 use super::generics::grid::{TrackBreadth as GenericTrackBreadth, TrackSize as GenericTrackSize};
 use super::generics::grid::TrackList as GenericTrackList;
 use values::specified::calc::CalcNode;
 
 #[cfg(feature = "gecko")]
 pub use self::align::{AlignItems, AlignJustifyContent, AlignJustifySelf, JustifyItems};
-pub use self::rect::LengthOrNumberRect;
+pub use self::background::BackgroundSize;
 pub use self::border::{BorderImageSlice, BorderImageWidth, BorderImageWidthSide};
 pub use self::color::Color;
+pub use self::rect::LengthOrNumberRect;
 pub use super::generics::grid::GridLine;
 pub use self::image::{ColorStop, EndingShape as GradientEndingShape, Gradient};
 pub use self::image::{GradientItem, GradientKind, Image, ImageRect, ImageLayer};
 pub use self::length::AbsoluteLength;
 pub use self::length::{FontRelativeLength, ViewportPercentageLength, CharacterWidth, Length, CalcLengthOrPercentage};
 pub use self::length::{Percentage, LengthOrNone, LengthOrNumber, LengthOrPercentage, LengthOrPercentageOrAuto};
 pub use self::length::{LengthOrPercentageOrNone, LengthOrPercentageOrAutoOrContent, NoCalcLength};
 pub use self::length::{MaxLength, MozLength};
 pub use self::position::{Position, PositionComponent};
 
 #[cfg(feature = "gecko")]
 pub mod align;
+pub mod background;
 pub mod basic_shape;
 pub mod border;
 pub mod calc;
 pub mod color;
 pub mod grid;
 pub mod image;
 pub mod length;
 pub mod position;
--- a/servo/tests/unit/style/parsing/basic_shape.rs
+++ b/servo/tests/unit/style/parsing/basic_shape.rs
@@ -8,17 +8,20 @@ use style::values::specified::basic_shap
 use style_traits::ToCss;
 
 // Ensure that basic-shape sub-functions parse as both basic shapes
 // and their individual components
 macro_rules! assert_roundtrip_basicshape {
     ($fun:expr, $input:expr, $output:expr) => {
         assert_roundtrip_with_context!($fun, $input, $output);
         assert_roundtrip_with_context!(BasicShape::parse, $input, $output);
-    }
+    };
+    ($fun:expr, $input:expr) => {
+        assert_roundtrip_basicshape!($fun, $input, $input);
+    };
 }
 
 macro_rules! assert_border_radius_values {
     ($input:expr; $tlw:expr, $trw:expr, $brw:expr, $blw:expr ;
                   $tlh:expr, $trh:expr, $brh:expr, $blh:expr) => {
         let input = parse(BorderRadius::parse, $input)
                           .expect(&format!("Failed parsing {} as border radius",
                                   $input));
@@ -30,30 +33,22 @@ macro_rules! assert_border_radius_values
         assert_eq!(::style_traits::ToCss::to_css_string(&input.top_right.0.height), $trh);
         assert_eq!(::style_traits::ToCss::to_css_string(&input.bottom_right.0.height), $brh);
         assert_eq!(::style_traits::ToCss::to_css_string(&input.bottom_left.0.height), $blh);
     }
 }
 
 #[test]
 fn test_inset() {
-    // these are actually wrong, we should be serializing to the minimum possible result
-    // the advantage of being wrong is that the roundtrip test actually suffices
-    // for testing the intermediate state
-    assert_roundtrip_basicshape!(InsetRect::parse, "inset(10px)", "inset(10px 10px 10px 10px)");
-    assert_roundtrip_basicshape!(InsetRect::parse, "inset(10px 20%)", "inset(10px 20% 10px 20%)");
+    assert_roundtrip_basicshape!(InsetRect::parse, "inset(10px)");
+    assert_roundtrip_basicshape!(InsetRect::parse, "inset(10px 20%)");
 
-    assert_roundtrip_basicshape!(InsetRect::parse, "inset(10px round 10px)",
-                                                   "inset(10px 10px 10px 10px round 10px)");
-    assert_roundtrip_basicshape!(InsetRect::parse, "inset(10px round 10px 20px 30px 40px)",
-                                                   "inset(10px 10px 10px 10px round 10px 20px 30px 40px)");
-    assert_roundtrip_basicshape!(InsetRect::parse, "inset(10px 10px 10px 10px round 10px 20px 30px 40px \
-                                                    / 1px 2px 3px 4px)",
-                                                   "inset(10px 10px 10px 10px round 10px 20px 30px 40px \
-                                                    / 1px 2px 3px 4px)");
+    assert_roundtrip_basicshape!(InsetRect::parse, "inset(10px round 10px)");
+    assert_roundtrip_basicshape!(InsetRect::parse, "inset(10px round 10px 20px 30px 40px)");
+    assert_roundtrip_basicshape!(InsetRect::parse, "inset(10px round 10px 20px 30px 40px / 1px 2px 3px 4px)");
 }
 
 #[test]
 fn test_border_radius() {
     assert_border_radius_values!("10px";
                                  "10px", "10px", "10px", "10px" ;
                                  "10px", "10px", "10px", "10px");
     assert_border_radius_values!("10px 20px";
--- a/servo/tests/unit/style/properties/serialization.rs
+++ b/servo/tests/unit/style/properties/serialization.rs
@@ -801,16 +801,17 @@ mod shorthand_serialization {
         use style::properties::longhands::mask_image as image;
         use style::properties::longhands::mask_mode as mode;
         use style::properties::longhands::mask_origin as origin;
         use style::properties::longhands::mask_position_x as position_x;
         use style::properties::longhands::mask_position_y as position_y;
         use style::properties::longhands::mask_repeat as repeat;
         use style::properties::longhands::mask_size as size;
         use style::values::Either;
+        use style::values::generics::background::BackgroundSize;
         use style::values::generics::image::Image;
         use super::*;
 
         macro_rules! single_vec_value_typedef {
             ($name:ident, $path:expr) => {
                 $name::SpecifiedValue(
                     vec![$path]
                 )
@@ -847,23 +848,22 @@ mod shorthand_serialization {
             );
             let position_y = single_vec_value_typedef!(position_y,
                 PositionComponent::Side(
                     Y::Bottom,
                     Some(LengthOrPercentage::Length(NoCalcLength::from_px(4f32))),
                 )
             );
 
-            let size = single_vec_variant_value!(size,
-                size::single_value::SpecifiedValue::Explicit(
-                    size::single_value::ExplicitSize {
-                        width: LengthOrPercentageOrAuto::Length(NoCalcLength::from_px(70f32)),
-                        height: LengthOrPercentageOrAuto::Length(NoCalcLength::from_px(50f32))
-                    }
-                )
+            let size = single_vec_variant_value!(
+                size,
+                BackgroundSize::Explicit {
+                    width: LengthOrPercentageOrAuto::Length(NoCalcLength::from_px(70f32)),
+                    height: LengthOrPercentageOrAuto::Length(NoCalcLength::from_px(50f32)),
+                }
             );
 
             let repeat = single_vec_keyword_value!(repeat, RepeatX);
             let origin = single_vec_keyword_value!(origin, padding_box);
             let clip = single_vec_keyword_value!(clip, border_box);
             let composite = single_vec_keyword_value!(composite, subtract);
 
             properties.push(PropertyDeclaration::MaskImage(image));
@@ -898,23 +898,22 @@ mod shorthand_serialization {
             let position_x = single_vec_value_typedef!(position_x,
                 PositionComponent::Length(LengthOrPercentage::Length(NoCalcLength::from_px(7f32)))
             );
 
             let position_y = single_vec_value_typedef!(position_y,
                 PositionComponent::Length(LengthOrPercentage::Length(NoCalcLength::from_px(4f32)))
             );
 
-            let size = single_vec_variant_value!(size,
-                size::single_value::SpecifiedValue::Explicit(
-                    size::single_value::ExplicitSize {
-                        width: LengthOrPercentageOrAuto::Length(NoCalcLength::from_px(70f32)),
-                        height: LengthOrPercentageOrAuto::Length(NoCalcLength::from_px(50f32))
-                    }
-                )
+            let size = single_vec_variant_value!(
+                size,
+                BackgroundSize::Explicit {
+                    width: LengthOrPercentageOrAuto::Length(NoCalcLength::from_px(70f32)),
+                    height: LengthOrPercentageOrAuto::Length(NoCalcLength::from_px(50f32)),
+                }
             );
 
             let repeat = single_vec_keyword_value!(repeat, RepeatX);
             let origin = single_vec_keyword_value!(origin, padding_box);
             let clip = single_vec_keyword_value!(clip, padding_box);
             let composite = single_vec_keyword_value!(composite, subtract);
 
             properties.push(PropertyDeclaration::MaskImage(image));