servo: Merge #17002 - Introduce style::values::generics::rect ▭ (from servo:derive-all-the-things); r=emilio
authorAnthony Ramine <n.oxyde@gmail.com>
Wed, 24 May 2017 02:18:01 -0500
changeset 360420 d083357507c234e33b56aa3fbe0fa4daa161c140
parent 360419 744e9a4706a08b8e5b6484355e9531706539e351
child 360421 3132da9afcb8cc40d4f7c71f5beee8869a4cba8b
push id90669
push userryanvm@gmail.com
push dateWed, 24 May 2017 20:33:41 +0000
treeherdermozilla-inbound@541bf10775c7 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersemilio
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 #17002 - Introduce style::values::generics::rect ▭ (from servo:derive-all-the-things); r=emilio Source-Repo: https://github.com/servo/servo Source-Revision: b4cebe1920ee9dc64f1a080fb940ea77743692a2
servo/components/layout/display_list_builder.rs
servo/components/style/properties/gecko.mako.rs
servo/components/style/properties/longhand/border.mako.rs
servo/components/style/values/computed/border.rs
servo/components/style/values/computed/length.rs
servo/components/style/values/computed/mod.rs
servo/components/style/values/computed/rect.rs
servo/components/style/values/generics/border.rs
servo/components/style/values/generics/mod.rs
servo/components/style/values/generics/rect.rs
servo/components/style/values/specified/border.rs
servo/components/style/values/specified/length.rs
servo/components/style/values/specified/mod.rs
servo/components/style/values/specified/rect.rs
--- a/servo/components/layout/display_list_builder.rs
+++ b/servo/components/layout/display_list_builder.rs
@@ -1424,29 +1424,28 @@ impl FragmentDisplayListBuilding for Fra
             }
             Either::Second(Image::Url(ref image_url)) => {
                 if let Some(url) = image_url.url() {
                     let webrender_image = state.layout_context
                                                .get_webrender_image_for_url(self.node,
                                                                             url.clone(),
                                                                             UsePlaceholder::No);
                     if let Some(webrender_image) = webrender_image {
-                        // The corners array is guaranteed to be len=4 by the css parser.
-                        let corners = &border_style_struct.border_image_slice.corners;
+                        let corners = &border_style_struct.border_image_slice.offsets;
 
                         state.add_display_item(DisplayItem::Border(box BorderDisplayItem {
                             base: base,
                             border_widths: border.to_physical(style.writing_mode),
                             details: BorderDetails::Image(ImageBorder {
                                 image: webrender_image,
                                 fill: border_style_struct.border_image_slice.fill,
-                                slice: SideOffsets2D::new(corners[0].resolve(webrender_image.height),
-                                                          corners[1].resolve(webrender_image.width),
-                                                          corners[2].resolve(webrender_image.height),
-                                                          corners[3].resolve(webrender_image.width)),
+                                slice: SideOffsets2D::new(corners.top.resolve(webrender_image.height),
+                                                          corners.right.resolve(webrender_image.width),
+                                                          corners.bottom.resolve(webrender_image.height),
+                                                          corners.left.resolve(webrender_image.width)),
                                 // TODO(gw): Support border-image-outset
                                 outset: SideOffsets2D::zero(),
                                 repeat_horizontal: convert_repeat_mode(border_style_struct.border_image_repeat.0),
                                 repeat_vertical: convert_repeat_mode(border_style_struct.border_image_repeat.1),
                             }),
                         }));
                     }
                 }
--- a/servo/components/style/properties/gecko.mako.rs
+++ b/servo/components/style/properties/gecko.mako.rs
@@ -954,18 +954,17 @@ fn static_assert() {
         unsafe {
             Gecko_CopyImageValueFrom(&mut self.gecko.mBorderImageSource,
                                      &other.gecko.mBorderImageSource);
         }
     }
 
     pub fn set_border_image_outset(&mut self, v: longhands::border_image_outset::computed_value::T) {
         % for side in SIDES:
-            v.${side.index}.to_gecko_style_coord(&mut self.gecko.mBorderImageOutset
-                                                          .data_at_mut(${side.index}));
+        v.${side.ident}.to_gecko_style_coord(&mut self.gecko.mBorderImageOutset.data_at_mut(${side.index}));
         % endfor
     }
 
     pub fn copy_border_image_outset_from(&mut self, other: &Self) {
         % for side in SIDES:
             self.gecko.mBorderImageOutset.data_at_mut(${side.index})
                 .copy_from(&other.gecko.mBorderImageOutset.data_at(${side.index}));
         % endfor
@@ -989,46 +988,46 @@ fn static_assert() {
     }
 
     pub fn copy_border_image_repeat_from(&mut self, other: &Self) {
         self.gecko.mBorderImageRepeatH = other.gecko.mBorderImageRepeatH;
         self.gecko.mBorderImageRepeatV = other.gecko.mBorderImageRepeatV;
     }
 
     pub fn set_border_image_width(&mut self, v: longhands::border_image_width::computed_value::T) {
-        use properties::longhands::border_image_width::computed_value::SingleComputedValue;
+        use values::generics::border::BorderImageWidthSide;
 
         % for side in SIDES:
-        match v.${side.index} {
-            SingleComputedValue::Auto => {
+        match v.${side.ident} {
+            BorderImageWidthSide::Auto => {
                 self.gecko.mBorderImageWidth.data_at_mut(${side.index}).set_value(CoordDataValue::Auto)
             },
-            SingleComputedValue::LengthOrPercentage(l) => {
+            BorderImageWidthSide::Length(l) => {
                 l.to_gecko_style_coord(&mut self.gecko.mBorderImageWidth.data_at_mut(${side.index}))
             },
-            SingleComputedValue::Number(n) => {
+            BorderImageWidthSide::Number(n) => {
                 self.gecko.mBorderImageWidth.data_at_mut(${side.index}).set_value(CoordDataValue::Factor(n))
             },
         }
         % endfor
     }
 
     pub fn copy_border_image_width_from(&mut self, other: &Self) {
         % for side in SIDES:
             self.gecko.mBorderImageWidth.data_at_mut(${side.index})
                 .copy_from(&other.gecko.mBorderImageWidth.data_at(${side.index}));
         % endfor
     }
 
     pub fn set_border_image_slice(&mut self, v: longhands::border_image_slice::computed_value::T) {
         use gecko_bindings::structs::{NS_STYLE_BORDER_IMAGE_SLICE_NOFILL, NS_STYLE_BORDER_IMAGE_SLICE_FILL};
 
-        for (i, corner) in v.corners.iter().enumerate() {
-            corner.to_gecko_style_coord(&mut self.gecko.mBorderImageSlice.data_at_mut(i));
-        }
+        % for side in SIDES:
+        v.offsets.${side.ident}.to_gecko_style_coord(&mut self.gecko.mBorderImageSlice.data_at_mut(${side.index}));
+        % endfor
 
         let fill = if v.fill {
             NS_STYLE_BORDER_IMAGE_SLICE_FILL
         } else {
             NS_STYLE_BORDER_IMAGE_SLICE_NOFILL
         };
         self.gecko.mBorderImageFill = fill as u8;
     }
--- a/servo/components/style/properties/longhand/border.mako.rs
+++ b/servo/components/style/properties/longhand/border.mako.rs
@@ -195,119 +195,23 @@
     initial_value="Either::First(None_)",
     initial_specified_value="Either::First(None_)",
     spec="https://drafts.csswg.org/css-backgrounds/#the-background-image",
     vector=False,
     animation_value_type="none",
     has_uncacheable_values=False,
     boxed="True")}
 
-<%helpers:longhand name="border-image-outset" animation_value_type="none"
-                   spec="https://drafts.csswg.org/css-backgrounds/#border-image-outset">
-    use std::fmt;
-    use style_traits::ToCss;
-    use values::specified::{LengthOrNumber, Number};
-
-    pub mod computed_value {
-        use values::computed::LengthOrNumber;
-        #[derive(Debug, Clone, PartialEq)]
-        #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
-        pub struct T(pub LengthOrNumber, pub LengthOrNumber,
-                     pub LengthOrNumber, pub LengthOrNumber);
-    }
-
-    #[derive(Clone, Debug, HasViewportPercentage, PartialEq)]
-    #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
-    pub struct SpecifiedValue(pub Vec<LengthOrNumber>);
-
-    impl ToCss for computed_value::T {
-        fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
-            try!(self.0.to_css(dest));
-            try!(dest.write_str(" "));
-            try!(self.1.to_css(dest));
-            try!(dest.write_str(" "));
-            try!(self.2.to_css(dest));
-            try!(dest.write_str(" "));
-            self.3.to_css(dest)
-        }
-    }
-    impl ToCss for SpecifiedValue {
-        fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
-            try!(self.0[0].to_css(dest));
-            for value in self.0.iter().skip(1) {
-                try!(dest.write_str(" "));
-                try!(value.to_css(dest));
-            }
-            Ok(())
-        }
-    }
-
-    #[inline]
-    pub fn get_initial_value() -> computed_value::T {
-        computed_value::T(Either::Second(0.0), Either::Second(0.0),
-                          Either::Second(0.0), Either::Second(0.0))
-    }
-
-    #[inline]
-    pub fn get_initial_specified_value() -> SpecifiedValue {
-        SpecifiedValue(vec![Either::Second(Number::new(0.0))])
-    }
-
-    impl ToComputedValue for SpecifiedValue {
-        type ComputedValue = computed_value::T;
-
-        #[inline]
-        fn to_computed_value(&self, context: &Context) -> computed_value::T {
-            let length = self.0.len();
-            match length {
-                4 => computed_value::T(self.0[0].to_computed_value(context),
-                                       self.0[1].to_computed_value(context),
-                                       self.0[2].to_computed_value(context),
-                                       self.0[3].to_computed_value(context)),
-                3 => computed_value::T(self.0[0].to_computed_value(context),
-                                       self.0[1].to_computed_value(context),
-                                       self.0[2].to_computed_value(context),
-                                       self.0[1].to_computed_value(context)),
-                2 => computed_value::T(self.0[0].to_computed_value(context),
-                                       self.0[1].to_computed_value(context),
-                                       self.0[0].to_computed_value(context),
-                                       self.0[1].to_computed_value(context)),
-                1 => computed_value::T(self.0[0].to_computed_value(context),
-                                       self.0[0].to_computed_value(context),
-                                       self.0[0].to_computed_value(context),
-                                       self.0[0].to_computed_value(context)),
-                _ => unreachable!(),
-            }
-        }
-        #[inline]
-        fn from_computed_value(computed: &computed_value::T) -> Self {
-            SpecifiedValue(vec![ToComputedValue::from_computed_value(&computed.0),
-                                ToComputedValue::from_computed_value(&computed.1),
-                                ToComputedValue::from_computed_value(&computed.2),
-                                ToComputedValue::from_computed_value(&computed.3)])
-        }
-    }
-
-    pub fn parse(context: &ParserContext, input: &mut Parser) -> Result<SpecifiedValue, ()> {
-        let mut values = vec![];
-        for _ in 0..4 {
-            let value = input.try(|input| LengthOrNumber::parse_non_negative(context, input));
-            match value {
-                Ok(val) => values.push(val),
-                Err(_) => break,
-            }
-        }
-
-        if values.len() > 0 {
-            Ok(SpecifiedValue(values))
-        } else {
-            Err(())
-        }
-    }
-</%helpers:longhand>
+${helpers.predefined_type("border-image-outset", "LengthOrNumberRect",
+    parse_method="parse_non_negative",
+    initial_value="computed::LengthOrNumber::zero().into()",
+    initial_specified_value="specified::LengthOrNumber::zero().into()",
+    spec="https://drafts.csswg.org/css-backgrounds/#border-image-outset",
+    animation_value_type="none",
+    boxed=True)}
 
 <%helpers:longhand name="border-image-repeat" animation_value_type="none"
                    spec="https://drafts.csswg.org/css-backgrounds/#border-image-repeat">
     use std::fmt;
     use style_traits::ToCss;
 
     no_viewport_percentage!(SpecifiedValue);
 
@@ -375,337 +279,21 @@
     pub fn parse(_context: &ParserContext, input: &mut Parser) -> Result<SpecifiedValue, ()> {
         let first = try!(RepeatKeyword::parse(input));
         let second = input.try(RepeatKeyword::parse).ok();
 
         Ok(SpecifiedValue(first, second))
     }
 </%helpers:longhand>
 
-<%helpers:longhand name="border-image-width" animation_value_type="none"
-                   spec="https://drafts.csswg.org/css-backgrounds/#border-image-width">
-    use std::fmt;
-    use style_traits::ToCss;
-    use values::specified::{LengthOrPercentage, Number};
-
-    pub mod computed_value {
-        use values::computed::{LengthOrPercentage, Number};
-        #[derive(Debug, Clone, PartialEq)]
-        #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
-        pub struct T(pub SingleComputedValue, pub SingleComputedValue,
-                     pub SingleComputedValue, pub SingleComputedValue);
-
-        #[derive(Debug, Clone, PartialEq)]
-        #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
-        pub enum SingleComputedValue {
-            LengthOrPercentage(LengthOrPercentage),
-            Number(Number),
-            Auto,
-        }
-    }
-
-    #[derive(Clone, Debug, HasViewportPercentage, PartialEq)]
-    #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
-    pub struct SpecifiedValue(pub Vec<SingleSpecifiedValue>);
-
-    impl ToCss for computed_value::T {
-        fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
-            try!(self.0.to_css(dest));
-            try!(dest.write_str(" "));
-            try!(self.1.to_css(dest));
-            try!(dest.write_str(" "));
-            try!(self.2.to_css(dest));
-            try!(dest.write_str(" "));
-            self.3.to_css(dest)
-        }
-    }
-    impl ToCss for SpecifiedValue {
-        fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
-            try!(self.0[0].to_css(dest));
-            for value in self.0.iter().skip(1) {
-                try!(dest.write_str(" "));
-                try!(value.to_css(dest));
-            }
-            Ok(())
-        }
-    }
-
-    #[derive(Clone, Debug, HasViewportPercentage, PartialEq)]
-    #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
-    pub enum SingleSpecifiedValue {
-        LengthOrPercentage(LengthOrPercentage),
-        Number(Number),
-        Auto,
-    }
-
-    impl ToCss for computed_value::SingleComputedValue {
-        fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
-            match *self {
-                computed_value::SingleComputedValue::LengthOrPercentage(ref len) => len.to_css(dest),
-                computed_value::SingleComputedValue::Number(number) => number.to_css(dest),
-                computed_value::SingleComputedValue::Auto => dest.write_str("auto"),
-            }
-        }
-    }
-    impl ToCss for SingleSpecifiedValue {
-        fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
-            match *self {
-                SingleSpecifiedValue::LengthOrPercentage(ref len) => len.to_css(dest),
-                SingleSpecifiedValue::Number(number) => number.to_css(dest),
-                SingleSpecifiedValue::Auto => dest.write_str("auto"),
-            }
-        }
-    }
-
-    impl ToComputedValue for SingleSpecifiedValue {
-        type ComputedValue = computed_value::SingleComputedValue;
-
-        #[inline]
-        fn to_computed_value(&self, context: &Context) -> computed_value::SingleComputedValue {
-            match *self {
-                SingleSpecifiedValue::LengthOrPercentage(ref len) => {
-                    computed_value::SingleComputedValue::LengthOrPercentage(
-                        len.to_computed_value(context))
-                },
-                SingleSpecifiedValue::Number(number) =>
-                    computed_value::SingleComputedValue::Number(number.to_computed_value(context)),
-                SingleSpecifiedValue::Auto => computed_value::SingleComputedValue::Auto,
-            }
-        }
-        #[inline]
-        fn from_computed_value(computed: &computed_value::SingleComputedValue) -> Self {
-            match *computed {
-                computed_value::SingleComputedValue::LengthOrPercentage(len) => {
-                    SingleSpecifiedValue::LengthOrPercentage(
-                        ToComputedValue::from_computed_value(&len))
-                },
-                computed_value::SingleComputedValue::Number(number) =>
-                    SingleSpecifiedValue::Number(ToComputedValue::from_computed_value(&number)),
-                computed_value::SingleComputedValue::Auto => SingleSpecifiedValue::Auto,
-            }
-        }
-    }
-
-    #[inline]
-    pub fn get_initial_value() -> computed_value::T {
-        computed_value::T(computed_value::SingleComputedValue::Number(1.0),
-                          computed_value::SingleComputedValue::Number(1.0),
-                          computed_value::SingleComputedValue::Number(1.0),
-                          computed_value::SingleComputedValue::Number(1.0))
-    }
-
-    #[inline]
-    pub fn get_initial_specified_value() -> SpecifiedValue {
-        SpecifiedValue(vec![SingleSpecifiedValue::Number(Number::new(1.0))])
-    }
-
-    impl ToComputedValue for SpecifiedValue {
-        type ComputedValue = computed_value::T;
-
-        #[inline]
-        fn to_computed_value(&self, context: &Context) -> computed_value::T {
-            let length = self.0.len();
-            match length {
-                4 => computed_value::T(self.0[0].to_computed_value(context),
-                                       self.0[1].to_computed_value(context),
-                                       self.0[2].to_computed_value(context),
-                                       self.0[3].to_computed_value(context)),
-                3 => computed_value::T(self.0[0].to_computed_value(context),
-                                       self.0[1].to_computed_value(context),
-                                       self.0[2].to_computed_value(context),
-                                       self.0[1].to_computed_value(context)),
-                2 => computed_value::T(self.0[0].to_computed_value(context),
-                                       self.0[1].to_computed_value(context),
-                                       self.0[0].to_computed_value(context),
-                                       self.0[1].to_computed_value(context)),
-                1 => computed_value::T(self.0[0].to_computed_value(context),
-                                       self.0[0].to_computed_value(context),
-                                       self.0[0].to_computed_value(context),
-                                       self.0[0].to_computed_value(context)),
-                _ => unreachable!(),
-            }
-        }
-        #[inline]
-        fn from_computed_value(computed: &computed_value::T) -> Self {
-            SpecifiedValue(vec![ToComputedValue::from_computed_value(&computed.0),
-                                ToComputedValue::from_computed_value(&computed.1),
-                                ToComputedValue::from_computed_value(&computed.2),
-                                ToComputedValue::from_computed_value(&computed.3)])
-        }
-    }
-
-    impl Parse for SingleSpecifiedValue {
-        fn parse(context: &ParserContext, input: &mut Parser) -> Result<Self, ()> {
-            if input.try(|input| input.expect_ident_matching("auto")).is_ok() {
-                return Ok(SingleSpecifiedValue::Auto);
-            }
-
-            if let Ok(len) = input.try(|input| LengthOrPercentage::parse_non_negative(context, input)) {
-                return Ok(SingleSpecifiedValue::LengthOrPercentage(len));
-            }
+${helpers.predefined_type("border-image-width", "BorderImageWidth",
+    initial_value="computed::BorderImageWidthSide::one().into()",
+    initial_specified_value="specified::BorderImageWidthSide::one().into()",
+    spec="https://drafts.csswg.org/css-backgrounds/#border-image-width",
+    animation_value_type="none",
+    boxed=True)}
 
-            let num = try!(Number::parse_non_negative(context, input));
-            Ok(SingleSpecifiedValue::Number(num))
-        }
-    }
-
-    pub fn parse(context: &ParserContext, input: &mut Parser) -> Result<SpecifiedValue, ()> {
-        let mut values = vec![];
-        for _ in 0..4 {
-            let value = input.try(|input| SingleSpecifiedValue::parse(context, input));
-            match value {
-                Ok(val) => values.push(val),
-                Err(_) => break,
-            }
-        }
-
-        if values.len() > 0 {
-            Ok(SpecifiedValue(values))
-        } else {
-            Err(())
-        }
-    }
-</%helpers:longhand>
-
-<%helpers:longhand name="border-image-slice" boxed="True" animation_value_type="none"
-                   spec="https://drafts.csswg.org/css-backgrounds/#border-image-slice">
-    use std::fmt;
-    use style_traits::ToCss;
-    use values::computed::NumberOrPercentage as ComputedNumberOrPercentage;
-    use values::specified::{NumberOrPercentage, Percentage};
-
-    no_viewport_percentage!(SpecifiedValue);
-
-    pub mod computed_value {
-        use values::computed::NumberOrPercentage;
-        #[derive(Debug, Clone, PartialEq)]
-        #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
-        pub struct T {
-            pub corners: [NumberOrPercentage; 4],
-            pub fill: bool,
-        }
-    }
-
-    #[derive(Debug, Clone, PartialEq)]
-    #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
-    pub struct SpecifiedValue {
-        pub corners: Vec<NumberOrPercentage>,
-        pub fill: bool,
-    }
-
-    impl ToCss for computed_value::T {
-        fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
-            try!(self.corners[0].to_css(dest));
-            try!(dest.write_str(" "));
-            try!(self.corners[1].to_css(dest));
-            try!(dest.write_str(" "));
-            try!(self.corners[2].to_css(dest));
-            try!(dest.write_str(" "));
-            try!(self.corners[3].to_css(dest));
-
-            if self.fill {
-                try!(dest.write_str(" fill"));
-            }
-            Ok(())
-        }
-    }
-    impl ToCss for SpecifiedValue {
-        fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
-            try!(self.corners[0].to_css(dest));
-            for value in self.corners.iter().skip(1) {
-                try!(dest.write_str(" "));
-                try!(value.to_css(dest));
-            }
-
-            if self.fill {
-                try!(dest.write_str(" fill"));
-            }
-            Ok(())
-        }
-    }
-
-    #[inline]
-    pub fn get_initial_value() -> computed_value::T {
-        computed_value::T {
-            corners: [ComputedNumberOrPercentage::Percentage(Percentage(1.0)),
-                      ComputedNumberOrPercentage::Percentage(Percentage(1.0)),
-                      ComputedNumberOrPercentage::Percentage(Percentage(1.0)),
-                      ComputedNumberOrPercentage::Percentage(Percentage(1.0))],
-            fill: false,
-        }
-    }
-
-    #[inline]
-    pub fn get_initial_specified_value() -> SpecifiedValue {
-        SpecifiedValue {
-            corners: vec![NumberOrPercentage::Percentage(Percentage(1.0))],
-            fill: false,
-        }
-    }
-
-    impl ToComputedValue for SpecifiedValue {
-        type ComputedValue = computed_value::T;
-
-        #[inline]
-        fn to_computed_value(&self, context: &Context) -> computed_value::T {
-            let length = self.corners.len();
-            let corners = match length {
-                4 => [self.corners[0].to_computed_value(context),
-                      self.corners[1].to_computed_value(context),
-                      self.corners[2].to_computed_value(context),
-                      self.corners[3].to_computed_value(context)],
-                3 => [self.corners[0].to_computed_value(context),
-                      self.corners[1].to_computed_value(context),
-                      self.corners[2].to_computed_value(context),
-                      self.corners[1].to_computed_value(context)],
-                2 => [self.corners[0].to_computed_value(context),
-                      self.corners[1].to_computed_value(context),
-                      self.corners[0].to_computed_value(context),
-                      self.corners[1].to_computed_value(context)],
-                1 => [self.corners[0].to_computed_value(context),
-                      self.corners[0].to_computed_value(context),
-                      self.corners[0].to_computed_value(context),
-                      self.corners[0].to_computed_value(context)],
-                _ => unreachable!(),
-            };
-            computed_value::T {
-                corners: corners,
-                fill: self.fill,
-            }
-        }
-        #[inline]
-        fn from_computed_value(computed: &computed_value::T) -> Self {
-            SpecifiedValue {
-                corners: vec![ToComputedValue::from_computed_value(&computed.corners[0]),
-                              ToComputedValue::from_computed_value(&computed.corners[1]),
-                              ToComputedValue::from_computed_value(&computed.corners[2]),
-                              ToComputedValue::from_computed_value(&computed.corners[3])],
-                fill: computed.fill,
-            }
-        }
-    }
-
-    pub fn parse(context: &ParserContext, input: &mut Parser) -> Result<SpecifiedValue, ()> {
-        let mut fill = input.try(|input| input.expect_ident_matching("fill")).is_ok();
-
-        let mut values = vec![];
-        for _ in 0..4 {
-            let value = input.try(|input| NumberOrPercentage::parse_non_negative(context, input));
-            match value {
-                Ok(val) => values.push(val),
-                Err(_) => break,
-            }
-        }
-
-        if !fill {
-            fill = input.try(|input| input.expect_ident_matching("fill")).is_ok();
-        }
-
-        if !values.is_empty() {
-            Ok(SpecifiedValue {
-                corners: values,
-                fill: fill
-            })
-        } else {
-            Err(())
-        }
-    }
-</%helpers:longhand>
+${helpers.predefined_type("border-image-slice", "BorderImageSlice",
+    initial_value="computed::NumberOrPercentage::Percentage(computed::Percentage(1.)).into()",
+    initial_specified_value="specified::NumberOrPercentage::Percentage(specified::Percentage(1.)).into()",
+    spec="https://drafts.csswg.org/css-backgrounds/#border-image-slice",
+    animation_value_type="none",
+    boxed=True)}
new file mode 100644
--- /dev/null
+++ b/servo/components/style/values/computed/border.rs
@@ -0,0 +1,28 @@
+/* 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 borders.
+
+use values::computed::{Number, NumberOrPercentage};
+use values::computed::length::LengthOrPercentage;
+use values::generics::border::BorderImageSlice as GenericBorderImageSlice;
+use values::generics::border::BorderImageWidthSide as GenericBorderImageWidthSide;
+use values::generics::rect::Rect;
+
+/// A computed value for the `border-image-width` property.
+pub type BorderImageWidth = Rect<BorderImageWidthSide>;
+
+/// A computed value for a single side of a `border-image-width` property.
+pub type BorderImageWidthSide = GenericBorderImageWidthSide<LengthOrPercentage, Number>;
+
+/// A computed value for the `border-image-slice` property.
+pub type BorderImageSlice = GenericBorderImageSlice<NumberOrPercentage>;
+
+impl BorderImageWidthSide {
+    /// Returns `1`.
+    #[inline]
+    pub fn one() -> Self {
+        GenericBorderImageWidthSide::Number(1.)
+    }
+}
--- a/servo/components/style/values/computed/length.rs
+++ b/servo/components/style/values/computed/length.rs
@@ -598,16 +598,24 @@ pub type Length = Au;
 pub type LengthOrNone = Either<Length, None_>;
 
 /// Either a computed `<length>` or the `auto` keyword.
 pub type LengthOrAuto = Either<Length, Auto>;
 
 /// Either a computed `<length>` or a `<number>` value.
 pub type LengthOrNumber = Either<Length, Number>;
 
+impl LengthOrNumber {
+    /// Returns `0`.
+    #[inline]
+    pub fn zero() -> Self {
+        Either::Second(0.)
+    }
+}
+
 /// Either a computed `<length>` or the `normal` keyword.
 pub type LengthOrNormal = Either<Length, Normal>;
 
 /// A value suitable for a `min-width`, `min-height`, `width` or `height` property.
 /// See specified/values/length.rs for more details.
 #[derive(Debug, Copy, Clone, PartialEq)]
 #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
 #[allow(missing_docs)]
--- a/servo/components/style/values/computed/mod.rs
+++ b/servo/components/style/values/computed/mod.rs
@@ -19,32 +19,36 @@ 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::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 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
 /// itself and be transformed to a computed value.
 pub struct Context<'a> {
     /// Whether the current element is the root element.
     pub is_root_element: bool,
 
     /// The Device holds the viewport and other external state.
new file mode 100644
--- /dev/null
+++ b/servo/components/style/values/computed/rect.rs
@@ -0,0 +1,11 @@
+/* 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 borders.
+
+use values::computed::length::LengthOrNumber;
+use values::generics::rect::Rect;
+
+/// A specified rectangle made of four `<length-or-number>` values.
+pub type LengthOrNumberRect = Rect<LengthOrNumber>;
new file mode 100644
--- /dev/null
+++ b/servo/components/style/values/generics/border.rs
@@ -0,0 +1,71 @@
+/* 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 borders.
+
+use std::fmt;
+use style_traits::ToCss;
+use values::generics::rect::Rect;
+
+/// A generic value for a single side of a `border-image-width` property.
+#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
+#[derive(Clone, Copy, Debug, HasViewportPercentage, PartialEq, ToComputedValue)]
+pub enum BorderImageWidthSide<LengthOrPercentage, Number> {
+    /// `<length-or-percentage>`
+    Length(LengthOrPercentage),
+    /// `<number>`
+    Number(Number),
+    /// `auto`
+    Auto,
+}
+
+/// A generic value for the `border-image-slice` property.
+#[derive(Clone, Copy, Debug, HasViewportPercentage, PartialEq, ToComputedValue)]
+#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
+pub struct BorderImageSlice<NumberOrPercentage> {
+    /// The offsets.
+    pub offsets: Rect<NumberOrPercentage>,
+    /// Whether to fill the middle part.
+    pub fill: bool,
+}
+
+impl<L, N> ToCss for BorderImageWidthSide<L, N>
+    where L: ToCss, N: ToCss,
+{
+    fn to_css<W>(&self, dest: &mut W) -> fmt::Result
+        where W: fmt::Write
+    {
+        match *self {
+            BorderImageWidthSide::Length(ref length) => length.to_css(dest),
+            BorderImageWidthSide::Number(ref number) => number.to_css(dest),
+            BorderImageWidthSide::Auto => dest.write_str("auto"),
+        }
+    }
+}
+
+impl<N> From<N> for BorderImageSlice<N>
+    where N: Clone,
+{
+    #[inline]
+    fn from(value: N) -> Self {
+        Self {
+            offsets: value.into(),
+            fill: false,
+        }
+    }
+}
+
+impl<N> ToCss for BorderImageSlice<N>
+    where N: PartialEq + ToCss,
+{
+    fn to_css<W>(&self, dest: &mut W) -> fmt::Result
+        where W: fmt::Write
+    {
+        self.offsets.to_css(dest)?;
+        if self.fill {
+            dest.write_str(" fill")?;
+        }
+        Ok(())
+    }
+}
--- a/servo/components/style/values/generics/mod.rs
+++ b/servo/components/style/values/generics/mod.rs
@@ -11,19 +11,21 @@ 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 basic_shape;
+pub mod border;
 pub mod grid;
 pub mod image;
 pub mod position;
+pub mod rect;
 
 #[derive(Clone, Debug, PartialEq, ToComputedValue)]
 #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
 /// A type for representing CSS `width` and `height` values.
 pub struct BorderRadiusSize<L>(pub Size2D<L>);
 
 impl<L> HasViewportPercentage for BorderRadiusSize<L> {
     #[inline]
new file mode 100644
--- /dev/null
+++ b/servo/components/style/values/generics/rect.rs
@@ -0,0 +1,110 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+//! Generic types for CSS values that are composed of four sides.
+
+use cssparser::Parser;
+use parser::{Parse, ParserContext};
+use std::fmt;
+use style_traits::ToCss;
+
+/// A CSS value made of four sides: top, right, bottom, and left.
+#[derive(Clone, Copy, Debug, HasViewportPercentage, PartialEq, ToComputedValue)]
+#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
+pub struct Rect<T> {
+    /// Top
+    pub top: T,
+    /// Right.
+    pub right: T,
+    /// Bottom.
+    pub bottom: T,
+    /// Left.
+    pub left: T,
+}
+
+impl<T> Rect<T> {
+    /// Returns a new `Rect<T>` value.
+    pub fn new(top: T, right: T, bottom: T, left: T) -> Self {
+        Rect {
+            top: top,
+            right: right,
+            bottom: bottom,
+            left: left,
+        }
+    }
+}
+
+impl<T> Rect<T>
+    where T: Clone
+{
+    /// Parses a new `Rect<T>` value with the given parse function.
+    pub fn parse_with<Parse>(
+        context: &ParserContext,
+        input: &mut Parser,
+        parse: Parse)
+        -> Result<Self, ()>
+        where Parse: Fn(&ParserContext, &mut Parser) -> Result<T, ()>
+    {
+        let top = parse(context, input)?;
+        let right = if let Ok(right) = input.try(|i| parse(context, i)) { right } else {
+            // <top>
+            return Ok(Self::new(top.clone(), top.clone(), top.clone(), top));
+        };
+        let bottom = if let Ok(bottom) = input.try(|i| parse(context, i)) { bottom } else {
+            // <top> <right>
+            return Ok(Self::new(top.clone(), right.clone(), top, right));
+        };
+        let left = if let Ok(left) = input.try(|i| parse(context, i)) { left } else {
+            // <top> <right> <bottom>
+            return Ok(Self::new(top, right.clone(), bottom, right));
+        };
+        // <top> <right> <bottom> <left>
+        Ok(Self::new(top, right, bottom, left))
+    }
+}
+
+impl<T> From<T> for Rect<T>
+    where T: Clone
+{
+    #[inline]
+    fn from(value: T) -> Self {
+        Self::new(value.clone(), value.clone(), value.clone(), value)
+    }
+}
+
+impl<T> Parse for Rect<T>
+    where T: Clone + Parse
+{
+    #[inline]
+    fn parse(context: &ParserContext, input: &mut Parser) -> Result<Self, ()> {
+        Self::parse_with(context, input, T::parse)
+    }
+}
+
+impl<T> ToCss for Rect<T>
+    where T: PartialEq + ToCss
+{
+    fn to_css<W>(&self, dest: &mut W) -> fmt::Result
+        where W: fmt::Write,
+    {
+        self.top.to_css(dest)?;
+        let same_vertical = self.top == self.bottom;
+        let same_horizontal = self.right == self.left;
+        if same_vertical && same_horizontal && self.top == self.right {
+            return Ok(());
+        }
+        dest.write_str(" ")?;
+        self.right.to_css(dest)?;
+        if same_vertical && same_horizontal {
+            return Ok(());
+        }
+        dest.write_str(" ")?;
+        self.bottom.to_css(dest)?;
+        if same_horizontal {
+            return Ok(());
+        }
+        dest.write_str(" ")?;
+        self.left.to_css(dest)
+    }
+}
new file mode 100644
--- /dev/null
+++ b/servo/components/style/values/specified/border.rs
@@ -0,0 +1,59 @@
+/* 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 borders.
+
+use cssparser::Parser;
+use parser::{Parse, ParserContext};
+use values::generics::border::BorderImageSlice as GenericBorderImageSlice;
+use values::generics::border::BorderImageWidthSide as GenericBorderImageWidthSide;
+use values::generics::rect::Rect;
+use values::specified::{Number, NumberOrPercentage};
+use values::specified::length::LengthOrPercentage;
+
+/// A specified value for the `border-image-width` property.
+pub type BorderImageWidth = Rect<BorderImageWidthSide>;
+
+/// A specified value for a single side of a `border-image-width` property.
+pub type BorderImageWidthSide = GenericBorderImageWidthSide<LengthOrPercentage, Number>;
+
+/// A specified value for the `border-image-slice` property.
+pub type BorderImageSlice = GenericBorderImageSlice<NumberOrPercentage>;
+
+impl BorderImageWidthSide {
+    /// Returns `1`.
+    #[inline]
+    pub fn one() -> Self {
+        GenericBorderImageWidthSide::Number(Number::new(1.))
+    }
+}
+
+impl Parse for BorderImageWidthSide {
+    fn parse(context: &ParserContext, input: &mut Parser) -> Result<Self, ()> {
+        if input.try(|i| i.expect_ident_matching("auto")).is_ok() {
+            return Ok(GenericBorderImageWidthSide::Auto);
+        }
+
+        if let Ok(len) = input.try(|i| LengthOrPercentage::parse_non_negative(context, i)) {
+            return Ok(GenericBorderImageWidthSide::Length(len));
+        }
+
+        let num = Number::parse_non_negative(context, input)?;
+        Ok(GenericBorderImageWidthSide::Number(num))
+    }
+}
+
+impl Parse for BorderImageSlice {
+    fn parse(context: &ParserContext, input: &mut Parser) -> Result<Self, ()> {
+        let mut fill = input.try(|i| i.expect_ident_matching("fill")).is_ok();
+        let offsets = Rect::parse_with(context, input, NumberOrPercentage::parse_non_negative)?;
+        if !fill {
+            fill = input.try(|i| i.expect_ident_matching("fill")).is_ok();
+        }
+        Ok(GenericBorderImageSlice {
+            offsets: offsets,
+            fill: fill,
+        })
+    }
+}
--- a/servo/components/style/values/specified/length.rs
+++ b/servo/components/style/values/specified/length.rs
@@ -1175,16 +1175,22 @@ impl LengthOrNumber {
         // LengthOrNumber, we want "0" to be parsed as a plain Number rather
         // than a Length (0px); this matches the behaviour of all major browsers
         if let Ok(v) = input.try(|i| Number::parse_non_negative(context, i)) {
             return Ok(Either::Second(v))
         }
 
         Length::parse_non_negative(context, input).map(Either::First)
     }
+
+    /// Returns `0`.
+    #[inline]
+    pub fn zero() -> Self {
+        Either::Second(Number::new(0.))
+    }
 }
 
 /// A value suitable for a `min-width` or `min-height` property.
 /// Unlike `max-width` or `max-height` properties, a MozLength can be
 /// `auto`, and cannot be `none`.
 #[derive(Clone, Debug, HasViewportPercentage, PartialEq)]
 #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
 #[allow(missing_docs)]
--- a/servo/components/style/values/specified/mod.rs
+++ b/servo/components/style/values/specified/mod.rs
@@ -25,36 +25,40 @@ 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::border::{BorderImageSlice, BorderImageWidth, BorderImageWidthSide};
 pub use self::color::Color;
 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 basic_shape;
+pub mod border;
 pub mod calc;
 pub mod color;
 pub mod grid;
 pub mod image;
 pub mod length;
 pub mod position;
+pub mod rect;
 
 /// Common handling for the specified value CSS url() values.
 pub mod url {
 use cssparser::Parser;
 use parser::{Parse, ParserContext};
 use values::computed::ComputedValueAsSpecified;
 
 #[cfg(feature = "servo")]
new file mode 100644
--- /dev/null
+++ b/servo/components/style/values/specified/rect.rs
@@ -0,0 +1,21 @@
+/* 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 borders.
+
+use cssparser::Parser;
+use parser::ParserContext;
+use values::generics::rect::Rect;
+use values::specified::length::LengthOrNumber;
+
+/// A specified rectangle made of four `<length-or-number>` values.
+pub type LengthOrNumberRect = Rect<LengthOrNumber>;
+
+impl LengthOrNumberRect {
+    /// Parses a `LengthOrNumberRect`, rejecting negative values.
+    #[inline]
+    pub fn parse_non_negative(context: &ParserContext, input: &mut Parser) -> Result<Self, ()> {
+        Rect::parse_with(context, input, LengthOrNumber::parse_non_negative)
+    }
+}