servo: Merge #15627 - stylo: Finish all SVG properties (from Manishearth:stylo-svg); r=heycam
authorManish Goregaokar <manishearth@gmail.com>
Sat, 18 Feb 2017 04:13:59 -0800
changeset 372723 868399420c8d5ff213724b5f27780047e4ac9c13
parent 372722 f508a675e8dc2780bff1aa99d2299494288a9ae1
child 372724 beaf2fc55a7da3177dbce2762d9b05e10be7a6a9
push id10863
push userjlorenzo@mozilla.com
push dateMon, 06 Mar 2017 23:02:23 +0000
treeherdermozilla-aurora@0931190cd725 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersheycam
milestone54.0a1
servo: Merge #15627 - stylo: Finish all SVG properties (from Manishearth:stylo-svg); r=heycam reviewed in bug https://bugzilla.mozilla.org/show_bug.cgi?id=1338388 Source-Repo: https://github.com/servo/servo Source-Revision: d3ba09e5884b8fb2513dd3fb7d9842de53fc3309
servo/components/style/build_gecko.rs
servo/components/style/gecko_bindings/bindings.rs
servo/components/style/gecko_bindings/structs_debug.rs
servo/components/style/gecko_bindings/structs_release.rs
servo/components/style/gecko_bindings/sugar/refptr.rs
servo/components/style/parser.rs
servo/components/style/properties/gecko.mako.rs
servo/components/style/properties/helpers.mako.rs
servo/components/style/properties/longhand/box.mako.rs
servo/components/style/properties/longhand/effects.mako.rs
servo/components/style/properties/longhand/inherited_svg.mako.rs
servo/components/style/properties/shorthand/inherited_svg.mako.rs
servo/components/style/values/computed/length.rs
servo/components/style/values/computed/mod.rs
servo/components/style/values/specified/length.rs
servo/components/style/values/specified/mod.rs
servo/components/style/values/specified/url.rs
--- a/servo/components/style/build_gecko.rs
+++ b/servo/components/style/build_gecko.rs
@@ -360,16 +360,17 @@ mod bindings {
             "nsStyleImage",
             "nsStyleImageLayers",
             "nsStyleList",
             "nsStyleMargin",
             "nsStyleOutline",
             "nsStylePadding",
             "nsStylePosition",
             "nsStyleSVG",
+            "nsStyleSVGPaint",
             "nsStyleSVGReset",
             "nsStyleTable",
             "nsStyleTableBorder",
             "nsStyleText",
             "nsStyleTextReset",
             "nsStyleUIReset",
             "nsStyleUnion",
             "nsStyleUnit",
@@ -378,16 +379,17 @@ mod bindings {
             "nsStyleVisibility",
             "nsStyleXUL",
             "nsTArray",
             "nsTArrayHeader",
             "Position",
             "PropertyValuePair",
             "Runnable",
             "ServoAttrSnapshot",
+            "ServoBundledURI",
             "ServoElementSnapshot",
             "SheetParsingMode",
             "StaticRefPtr",
             "StyleAnimation",
             "StyleBasicShape",
             "StyleBasicShapeType",
             "StyleClipPath",
             "StyleGeometryBox",
@@ -501,16 +503,17 @@ mod bindings {
             .hide_type("nsACString_internal")
             .hide_type("nsAString_internal")
             .raw_line("pub use nsstring::{nsACString, nsAString};")
             .raw_line("type nsACString_internal = nsACString;")
             .raw_line("type nsAString_internal = nsAString;")
             .whitelisted_function("Servo_.*")
             .whitelisted_function("Gecko_.*");
         let structs_types = [
+            "mozilla::css::URLValue",
             "RawGeckoDocument",
             "RawGeckoElement",
             "RawGeckoKeyframeList",
             "RawGeckoNode",
             "RawGeckoAnimationValueList",
             "RawServoAnimationValue",
             "RawServoDeclarationBlock",
             "RawGeckoPresContext",
@@ -518,16 +521,17 @@ mod bindings {
             "RefPtr",
             "ThreadSafeURIHolder",
             "ThreadSafePrincipalHolder",
             "CSSPseudoClassType",
             "TraversalRootBehavior",
             "FontFamilyList",
             "FontFamilyType",
             "Keyframe",
+            "ServoBundledURI",
             "ServoElementSnapshot",
             "SheetParsingMode",
             "StyleBasicShape",
             "StyleBasicShapeType",
             "StyleClipPath",
             "nsCSSKeyword",
             "nsCSSPropertyID",
             "nsCSSShadowArray",
@@ -545,31 +549,33 @@ mod bindings {
             "nsStyleColumn",
             "nsStyleContent",
             "nsStyleContext",
             "nsStyleCoord",
             "nsStyleCoord_Calc",
             "nsStyleCoord_CalcValue",
             "nsStyleDisplay",
             "nsStyleEffects",
+            "nsStyleFilter",
             "nsStyleFont",
             "nsStyleGradient",
             "nsStyleGradientStop",
             "nsStyleImage",
             "nsStyleImageLayers",
             "nsStyleImageLayers_Layer",
             "nsStyleImageLayers_LayerType",
             "nsStyleImageRequest",
             "nsStyleList",
             "nsStyleMargin",
             "nsStyleOutline",
             "nsStylePadding",
             "nsStylePosition",
             "nsStyleQuoteValues",
             "nsStyleSVG",
+            "nsStyleSVGPaint",
             "nsStyleSVGReset",
             "nsStyleTable",
             "nsStyleTableBorder",
             "nsStyleText",
             "nsStyleTextReset",
             "nsStyleUIReset",
             "nsStyleUnion",
             "nsStyleUnit",
--- a/servo/components/style/gecko_bindings/bindings.rs
+++ b/servo/components/style/gecko_bindings/bindings.rs
@@ -1,13 +1,14 @@
 /* automatically generated by rust-bindgen */
 
 pub use nsstring::{nsACString, nsAString};
 type nsACString_internal = nsACString;
 type nsAString_internal = nsAString;
+use gecko_bindings::structs::mozilla::css::URLValue;
 use gecko_bindings::structs::RawGeckoDocument;
 use gecko_bindings::structs::RawGeckoElement;
 use gecko_bindings::structs::RawGeckoKeyframeList;
 use gecko_bindings::structs::RawGeckoNode;
 use gecko_bindings::structs::RawGeckoAnimationValueList;
 use gecko_bindings::structs::RawServoAnimationValue;
 use gecko_bindings::structs::RawServoDeclarationBlock;
 use gecko_bindings::structs::RawGeckoPresContext;
@@ -15,16 +16,17 @@ use gecko_bindings::structs::RawGeckoPre
 use gecko_bindings::structs::RefPtr;
 use gecko_bindings::structs::ThreadSafeURIHolder;
 use gecko_bindings::structs::ThreadSafePrincipalHolder;
 use gecko_bindings::structs::CSSPseudoClassType;
 use gecko_bindings::structs::TraversalRootBehavior;
 use gecko_bindings::structs::FontFamilyList;
 use gecko_bindings::structs::FontFamilyType;
 use gecko_bindings::structs::Keyframe;
+use gecko_bindings::structs::ServoBundledURI;
 use gecko_bindings::structs::ServoElementSnapshot;
 use gecko_bindings::structs::SheetParsingMode;
 use gecko_bindings::structs::StyleBasicShape;
 use gecko_bindings::structs::StyleBasicShapeType;
 use gecko_bindings::structs::StyleClipPath;
 use gecko_bindings::structs::nsCSSKeyword;
 use gecko_bindings::structs::nsCSSPropertyID;
 use gecko_bindings::structs::nsCSSShadowArray;
@@ -64,16 +66,19 @@ use gecko_bindings::structs::nsStyleCoor
 unsafe impl Send for nsStyleCoord_CalcValue {}
 unsafe impl Sync for nsStyleCoord_CalcValue {}
 use gecko_bindings::structs::nsStyleDisplay;
 unsafe impl Send for nsStyleDisplay {}
 unsafe impl Sync for nsStyleDisplay {}
 use gecko_bindings::structs::nsStyleEffects;
 unsafe impl Send for nsStyleEffects {}
 unsafe impl Sync for nsStyleEffects {}
+use gecko_bindings::structs::nsStyleFilter;
+unsafe impl Send for nsStyleFilter {}
+unsafe impl Sync for nsStyleFilter {}
 use gecko_bindings::structs::nsStyleFont;
 unsafe impl Send for nsStyleFont {}
 unsafe impl Sync for nsStyleFont {}
 use gecko_bindings::structs::nsStyleGradient;
 unsafe impl Send for nsStyleGradient {}
 unsafe impl Sync for nsStyleGradient {}
 use gecko_bindings::structs::nsStyleGradientStop;
 unsafe impl Send for nsStyleGradientStop {}
@@ -109,16 +114,19 @@ use gecko_bindings::structs::nsStylePosi
 unsafe impl Send for nsStylePosition {}
 unsafe impl Sync for nsStylePosition {}
 use gecko_bindings::structs::nsStyleQuoteValues;
 unsafe impl Send for nsStyleQuoteValues {}
 unsafe impl Sync for nsStyleQuoteValues {}
 use gecko_bindings::structs::nsStyleSVG;
 unsafe impl Send for nsStyleSVG {}
 unsafe impl Sync for nsStyleSVG {}
+use gecko_bindings::structs::nsStyleSVGPaint;
+unsafe impl Send for nsStyleSVGPaint {}
+unsafe impl Sync for nsStyleSVGPaint {}
 use gecko_bindings::structs::nsStyleSVGReset;
 unsafe impl Send for nsStyleSVGReset {}
 unsafe impl Sync for nsStyleSVGReset {}
 use gecko_bindings::structs::nsStyleTable;
 unsafe impl Send for nsStyleTable {}
 unsafe impl Sync for nsStyleTable {}
 use gecko_bindings::structs::nsStyleTableBorder;
 unsafe impl Send for nsStyleTableBorder {}
@@ -585,21 +593,17 @@ extern "C" {
                                 legacy_syntax: bool, stops: u32)
      -> *mut nsStyleGradient;
 }
 extern "C" {
     pub fn Gecko_SetListStyleImageNone(style_struct: *mut nsStyleList);
 }
 extern "C" {
     pub fn Gecko_SetListStyleImage(style_struct: *mut nsStyleList,
-                                   string_bytes: *const u8,
-                                   string_length: u32,
-                                   base_uri: *mut ThreadSafeURIHolder,
-                                   referrer: *mut ThreadSafeURIHolder,
-                                   principal: *mut ThreadSafePrincipalHolder);
+                                   uri: ServoBundledURI);
 }
 extern "C" {
     pub fn Gecko_CopyListStyleImageFrom(dest: *mut nsStyleList,
                                         src: *const nsStyleList);
 }
 extern "C" {
     pub fn Gecko_SetCursorArrayLength(ui: *mut nsStyleUserInterface,
                                       len: usize);
@@ -611,27 +615,16 @@ extern "C" {
                                 referrer: *mut ThreadSafeURIHolder,
                                 principal: *mut ThreadSafePrincipalHolder);
 }
 extern "C" {
     pub fn Gecko_CopyCursorArrayFrom(dest: *mut nsStyleUserInterface,
                                      src: *const nsStyleUserInterface);
 }
 extern "C" {
-    pub fn Gecko_SetMozBinding(style_struct: *mut nsStyleDisplay,
-                               string_bytes: *const u8, string_length: u32,
-                               base_uri: *mut ThreadSafeURIHolder,
-                               referrer: *mut ThreadSafeURIHolder,
-                               principal: *mut ThreadSafePrincipalHolder);
-}
-extern "C" {
-    pub fn Gecko_CopyMozBindingFrom(des: *mut nsStyleDisplay,
-                                    src: *const nsStyleDisplay);
-}
-extern "C" {
     pub fn Gecko_GetNodeFlags(node: RawGeckoNodeBorrowed) -> u32;
 }
 extern "C" {
     pub fn Gecko_SetNodeFlags(node: RawGeckoNodeBorrowed, flags: u32);
 }
 extern "C" {
     pub fn Gecko_UnsetNodeFlags(node: RawGeckoNodeBorrowed, flags: u32);
 }
@@ -703,23 +696,59 @@ extern "C" {
 extern "C" {
     pub fn Gecko_DestroyClipPath(clip: *mut StyleClipPath);
 }
 extern "C" {
     pub fn Gecko_NewBasicShape(type_: StyleBasicShapeType)
      -> *mut StyleBasicShape;
 }
 extern "C" {
+    pub fn Gecko_StyleClipPath_SetURLValue(clip: *mut StyleClipPath,
+                                           uri: ServoBundledURI);
+}
+extern "C" {
     pub fn Gecko_ResetFilters(effects: *mut nsStyleEffects, new_len: usize);
 }
 extern "C" {
     pub fn Gecko_CopyFiltersFrom(aSrc: *mut nsStyleEffects,
                                  aDest: *mut nsStyleEffects);
 }
 extern "C" {
+    pub fn Gecko_nsStyleFilter_SetURLValue(effects: *mut nsStyleFilter,
+                                           uri: ServoBundledURI);
+}
+extern "C" {
+    pub fn Gecko_nsStyleSVGPaint_CopyFrom(dest: *mut nsStyleSVGPaint,
+                                          src: *const nsStyleSVGPaint);
+}
+extern "C" {
+    pub fn Gecko_nsStyleSVGPaint_SetURLValue(paint: *mut nsStyleSVGPaint,
+                                             uri: ServoBundledURI);
+}
+extern "C" {
+    pub fn Gecko_nsStyleSVGPaint_Reset(paint: *mut nsStyleSVGPaint);
+}
+extern "C" {
+    pub fn Gecko_nsStyleSVG_SetDashArrayLength(svg: *mut nsStyleSVG,
+                                               len: u32);
+}
+extern "C" {
+    pub fn Gecko_nsStyleSVG_CopyDashArray(dst: *mut nsStyleSVG,
+                                          src: *const nsStyleSVG);
+}
+extern "C" {
+    pub fn Gecko_NewURLValue(uri: ServoBundledURI) -> *mut URLValue;
+}
+extern "C" {
+    pub fn Gecko_AddRefCSSURLValueArbitraryThread(aPtr: *mut URLValue);
+}
+extern "C" {
+    pub fn Gecko_ReleaseCSSURLValueArbitraryThread(aPtr: *mut URLValue);
+}
+extern "C" {
     pub fn Gecko_FillAllBackgroundLists(layers: *mut nsStyleImageLayers,
                                         max_len: u32);
 }
 extern "C" {
     pub fn Gecko_FillAllMaskLists(layers: *mut nsStyleImageLayers,
                                   max_len: u32);
 }
 extern "C" {
--- a/servo/components/style/gecko_bindings/structs_debug.rs
+++ b/servo/components/style/gecko_bindings/structs_debug.rs
@@ -25358,16 +25358,33 @@ pub mod root {
                     "::" , stringify ! ( mGetter ) ));
     }
     impl Clone for nsMediaFeature {
         fn clone(&self) -> Self { *self }
     }
     pub type ThreadSafePrincipalHolder =
         root::nsMainThreadPtrHolder<root::nsIPrincipal>;
     pub type ThreadSafeURIHolder = root::nsMainThreadPtrHolder<root::nsIURI>;
+    #[repr(C)]
+    #[derive(Debug, Copy)]
+    pub struct ServoBundledURI {
+        pub mURLString: *const u8,
+        pub mURLStringLength: u32,
+        pub mBaseURI: *mut root::ThreadSafeURIHolder,
+        pub mReferrer: *mut root::ThreadSafeURIHolder,
+        pub mPrincipal: *mut root::ThreadSafePrincipalHolder,
+    }
+    #[test]
+    fn bindgen_test_layout_ServoBundledURI() {
+        assert_eq!(::std::mem::size_of::<ServoBundledURI>() , 40usize);
+        assert_eq!(::std::mem::align_of::<ServoBundledURI>() , 8usize);
+    }
+    impl Clone for ServoBundledURI {
+        fn clone(&self) -> Self { *self }
+    }
     pub type nsMediaFeatureValueGetter =
         ::std::option::Option<unsafe extern "C" fn(aPresContext:
                                                        *mut root::nsPresContext,
                                                    aFeature:
                                                        *const root::nsMediaFeature,
                                                    aResult:
                                                        *mut root::nsCSSValue)>;
     #[repr(C)]
--- a/servo/components/style/gecko_bindings/structs_release.rs
+++ b/servo/components/style/gecko_bindings/structs_release.rs
@@ -24714,16 +24714,33 @@ pub mod root {
                     "::" , stringify ! ( mGetter ) ));
     }
     impl Clone for nsMediaFeature {
         fn clone(&self) -> Self { *self }
     }
     pub type ThreadSafePrincipalHolder =
         root::nsMainThreadPtrHolder<root::nsIPrincipal>;
     pub type ThreadSafeURIHolder = root::nsMainThreadPtrHolder<root::nsIURI>;
+    #[repr(C)]
+    #[derive(Debug, Copy)]
+    pub struct ServoBundledURI {
+        pub mURLString: *const u8,
+        pub mURLStringLength: u32,
+        pub mBaseURI: *mut root::ThreadSafeURIHolder,
+        pub mReferrer: *mut root::ThreadSafeURIHolder,
+        pub mPrincipal: *mut root::ThreadSafePrincipalHolder,
+    }
+    #[test]
+    fn bindgen_test_layout_ServoBundledURI() {
+        assert_eq!(::std::mem::size_of::<ServoBundledURI>() , 40usize);
+        assert_eq!(::std::mem::align_of::<ServoBundledURI>() , 8usize);
+    }
+    impl Clone for ServoBundledURI {
+        fn clone(&self) -> Self { *self }
+    }
     pub type nsMediaFeatureValueGetter =
         ::std::option::Option<unsafe extern "C" fn(aPresContext:
                                                        *mut root::nsPresContext,
                                                    aFeature:
                                                        *const root::nsMediaFeature,
                                                    aResult:
                                                        *mut root::nsCSSValue)>;
     #[repr(C)]
--- a/servo/components/style/gecko_bindings/sugar/refptr.rs
+++ b/servo/components/style/gecko_bindings/sugar/refptr.rs
@@ -259,15 +259,18 @@ impl_threadsafe_refcount!(::gecko_bindin
                           Gecko_AddRefURIArbitraryThread,
                           Gecko_ReleaseURIArbitraryThread);
 impl_threadsafe_refcount!(::gecko_bindings::structs::nsStyleQuoteValues,
                           Gecko_AddRefQuoteValuesArbitraryThread,
                           Gecko_ReleaseQuoteValuesArbitraryThread);
 impl_threadsafe_refcount!(::gecko_bindings::structs::nsCSSValueSharedList,
                           Gecko_AddRefCSSValueSharedListArbitraryThread,
                           Gecko_ReleaseCSSValueSharedListArbitraryThread);
+impl_threadsafe_refcount!(::gecko_bindings::structs::mozilla::css::URLValue,
+                          Gecko_AddRefCSSURLValueArbitraryThread,
+                          Gecko_ReleaseCSSURLValueArbitraryThread);
 /// A Gecko `ThreadSafePrincipalHolder` wrapped in a safe refcounted pointer, to
 /// use during stylesheet parsing and style computation.
 pub type GeckoArcPrincipal = RefPtr<::gecko_bindings::structs::ThreadSafePrincipalHolder>;
 
 /// A Gecko `ThreadSafeURIHolder` wrapped in a safe refcounted pointer, to use
 /// during stylesheet parsing and style computation.
 pub type GeckoArcURI = RefPtr<::gecko_bindings::structs::ThreadSafeURIHolder>;
--- a/servo/components/style/parser.rs
+++ b/servo/components/style/parser.rs
@@ -105,13 +105,29 @@ pub trait Parse : Sized {
 }
 
 impl<T> Parse for Vec<T> where T: Parse + OneOrMoreCommaSeparated {
     fn parse(context: &ParserContext, input: &mut Parser) -> Result<Self, ()> {
         input.parse_comma_separated(|input| T::parse(context, input))
     }
 }
 
+/// Parse a non-empty space-separated or comma-separated list of objects parsed by parse_one
+pub fn parse_space_or_comma_separated<F, T>(input: &mut Parser, mut parse_one: F)
+        -> Result<Vec<T>, ()>
+        where F: FnMut(&mut Parser) -> Result<T, ()> {
+    let first = parse_one(input)?;
+    let mut vec = vec![first];
+    loop {
+        let _ = input.try(|i| i.expect_comma());
+        if let Ok(val) = input.try(|i| parse_one(i)) {
+            vec.push(val)
+        } else {
+            break
+        }
+    }
+    Ok(vec)
+}
 impl Parse for UnicodeRange {
     fn parse(_context: &ParserContext, input: &mut Parser) -> Result<Self, ()> {
         UnicodeRange::parse(input)
     }
 }
--- a/servo/components/style/properties/gecko.mako.rs
+++ b/servo/components/style/properties/gecko.mako.rs
@@ -6,42 +6,41 @@
 
 <%!
     from data import to_rust_ident, to_camel_case
     from data import Keyword
 %>
 <%namespace name="helpers" file="/helpers.mako.rs" />
 
 use app_units::Au;
+use cssparser::Color;
 use custom_properties::ComputedValuesMap;
 use gecko_bindings::bindings;
 % for style_struct in data.style_structs:
 use gecko_bindings::structs::${style_struct.gecko_ffi_name};
 use gecko_bindings::bindings::Gecko_Construct_Default_${style_struct.gecko_ffi_name};
 use gecko_bindings::bindings::Gecko_CopyConstruct_${style_struct.gecko_ffi_name};
 use gecko_bindings::bindings::Gecko_Destroy_${style_struct.gecko_ffi_name};
 % endfor
 use gecko_bindings::bindings::Gecko_Construct_nsStyleVariables;
 use gecko_bindings::bindings::Gecko_CopyCursorArrayFrom;
 use gecko_bindings::bindings::Gecko_CopyFontFamilyFrom;
 use gecko_bindings::bindings::Gecko_CopyImageValueFrom;
 use gecko_bindings::bindings::Gecko_CopyListStyleImageFrom;
 use gecko_bindings::bindings::Gecko_CopyListStyleTypeFrom;
-use gecko_bindings::bindings::Gecko_CopyMozBindingFrom;
 use gecko_bindings::bindings::Gecko_EnsureImageLayersLength;
 use gecko_bindings::bindings::Gecko_FontFamilyList_AppendGeneric;
 use gecko_bindings::bindings::Gecko_FontFamilyList_AppendNamed;
 use gecko_bindings::bindings::Gecko_FontFamilyList_Clear;
 use gecko_bindings::bindings::Gecko_SetCursorArrayLength;
 use gecko_bindings::bindings::Gecko_SetCursorImage;
 use gecko_bindings::bindings::Gecko_NewCSSShadowArray;
 use gecko_bindings::bindings::Gecko_SetListStyleImage;
 use gecko_bindings::bindings::Gecko_SetListStyleImageNone;
 use gecko_bindings::bindings::Gecko_SetListStyleType;
-use gecko_bindings::bindings::Gecko_SetMozBinding;
 use gecko_bindings::bindings::Gecko_SetNullImageValue;
 use gecko_bindings::bindings::ServoComputedValuesBorrowedOrNull;
 use gecko_bindings::bindings::{Gecko_ResetFilters, Gecko_CopyFiltersFrom};
 use gecko_bindings::bindings::RawGeckoPresContextBorrowed;
 use gecko_bindings::structs;
 use gecko_bindings::structs::nsStyleVariables;
 use gecko_bindings::sugar::ns_style_coord::{CoordDataValue, CoordData, CoordDataMut};
 use gecko_bindings::sugar::ownership::HasArcFFI;
@@ -269,29 +268,37 @@ def set_gecko_property(ffi_name, expr):
             % endfor
             % if keyword.gecko_inexhaustive:
             x => panic!("Found unexpected value in style struct for ${ident} property: {:?}", x),
             % endif
         }
     }
 </%def>
 
+
+/// Convert a Servo color into an nscolor; with currentColor as 0
+///
+/// Call sites will need to be updated after https://bugzilla.mozilla.org/show_bug.cgi?id=760345
+fn color_to_nscolor_zero_currentcolor(color: Color) -> structs::nscolor {
+    match color {
+        Color::RGBA(rgba) => {
+            convert_rgba_to_nscolor(&rgba)
+        },
+        Color::CurrentColor => 0,
+    }
+}
+
 <%def name="impl_color_setter(ident, gecko_ffi_name, complex_color=True)">
     #[allow(unreachable_code)]
     #[allow(non_snake_case)]
     pub fn set_${ident}(&mut self, v: longhands::${ident}::computed_value::T) {
         % if complex_color:
             let result = v.into();
         % else:
-            use cssparser::Color;
-            let result = match v {
-                Color::RGBA(rgba) => convert_rgba_to_nscolor(&rgba),
-                // FIXME #13547
-                Color::CurrentColor => 0,
-            };
+            let result = color_to_nscolor_zero_currentcolor(v);
         % endif
         ${set_gecko_property(gecko_ffi_name, "result")}
     }
 </%def>
 
 <%def name="impl_color_copy(ident, gecko_ffi_name, complex_color=True)">
     #[allow(non_snake_case)]
     pub fn copy_${ident}_from(&mut self, other: &Self) {
@@ -301,17 +308,16 @@ def set_gecko_property(ffi_name, expr):
 </%def>
 
 <%def name="impl_color_clone(ident, gecko_ffi_name, complex_color=True)">
     #[allow(non_snake_case)]
     pub fn clone_${ident}(&self) -> longhands::${ident}::computed_value::T {
         % if complex_color:
             ${get_gecko_property(gecko_ffi_name)}.into()
         % else:
-            use cssparser::Color;
             Color::RGBA(convert_nscolor_to_rgba(${get_gecko_property(gecko_ffi_name)}))
         % endif
     }
 </%def>
 
 <%def name="impl_keyword(ident, gecko_ffi_name, keyword, need_clone, **kwargs)">
 <%call expr="impl_keyword_setter(ident, gecko_ffi_name, keyword, **kwargs)"></%call>
 <%call expr="impl_simple_copy(ident, gecko_ffi_name)"></%call>
@@ -364,16 +370,68 @@ def set_gecko_property(ffi_name, expr):
 <%def name="impl_color(ident, gecko_ffi_name, need_clone=False, complex_color=True)">
 <%call expr="impl_color_setter(ident, gecko_ffi_name, complex_color)"></%call>
 <%call expr="impl_color_copy(ident, gecko_ffi_name, complex_color)"></%call>
 % if need_clone:
     <%call expr="impl_color_clone(ident, gecko_ffi_name, complex_color)"></%call>
 % endif
 </%def>
 
+<%def name="impl_svg_paint(ident, gecko_ffi_name, need_clone=False, complex_color=True)">
+    #[allow(non_snake_case)]
+    pub fn set_${ident}(&mut self, mut v: longhands::${ident}::computed_value::T) {
+        use values::computed::SVGPaintKind;
+        use self::structs::nsStyleSVGPaintType;
+
+        let ref mut paint = ${get_gecko_property(gecko_ffi_name)};
+        unsafe {
+            bindings::Gecko_nsStyleSVGPaint_Reset(paint);
+        }
+        let fallback = v.fallback.take();
+        match v.kind {
+            SVGPaintKind::None => return,
+            SVGPaintKind::ContextFill => {
+                paint.mType = nsStyleSVGPaintType::eStyleSVGPaintType_ContextFill;
+            }
+            SVGPaintKind::ContextStroke => {
+                paint.mType = nsStyleSVGPaintType::eStyleSVGPaintType_ContextStroke;
+            }
+            SVGPaintKind::PaintServer(url) => {
+                unsafe {
+                    if let Some(ffi) = url.for_ffi() {
+                        bindings::Gecko_nsStyleSVGPaint_SetURLValue(paint, ffi);
+                    } else {
+                        return;
+                    }
+                }
+            }
+            SVGPaintKind::Color(color) => {
+                paint.mType = nsStyleSVGPaintType::eStyleSVGPaintType_Color;
+                unsafe {
+                    *paint.mPaint.mColor.as_mut() = color_to_nscolor_zero_currentcolor(color);
+                }
+            }
+        }
+
+        if let Some(fallback) = fallback {
+            paint.mFallbackColor = color_to_nscolor_zero_currentcolor(fallback);
+        }
+    }
+
+    #[allow(non_snake_case)]
+    pub fn copy_${ident}_from(&mut self, other: &Self) {
+        unsafe {
+            bindings::Gecko_nsStyleSVGPaint_CopyFrom(
+                &mut ${get_gecko_property(gecko_ffi_name)},
+                & ${get_gecko_property(gecko_ffi_name, "other")}
+            );
+        }
+    }
+</%def>
+
 <%def name="impl_app_units(ident, gecko_ffi_name, need_clone, round_to_pixels=False)">
     #[allow(non_snake_case)]
     pub fn set_${ident}(&mut self, v: longhands::${ident}::computed_value::T) {
         % if round_to_pixels:
         let au_per_device_px = Au(self.gecko.mTwipsPerPixel);
         self.gecko.${gecko_ffi_name} = round_border_to_device_pixels(v, au_per_device_px).0;
         % else:
         self.gecko.${gecko_ffi_name} = v.0;
@@ -450,16 +508,51 @@ def set_gecko_property(ffi_name, expr):
             let height = GeckoStyleCoordConvertible::from_gecko_style_coord(
                             &self.gecko.${gecko_ffi_name}.data_at(${y_index}))
                             .expect("Failed to clone ${ident}");
             T(Size2D::new(width, height))
         }
     % endif
 </%def>
 
+<%def name="impl_css_url(ident, gecko_ffi_name, need_clone=False)">
+    #[allow(non_snake_case)]
+    pub fn set_${ident}(&mut self, v: longhands::${ident}::computed_value::T) {
+        use gecko_bindings::sugar::refptr::RefPtr;
+        match v {
+            Either::First(url) => {
+                let refptr = unsafe {
+                    if let Some(ffi) = url.for_ffi() {
+                        let ptr = bindings::Gecko_NewURLValue(ffi);
+                        RefPtr::from_addrefed(ptr)
+                    } else {
+                        self.gecko.${gecko_ffi_name}.clear();
+                        return;
+                    }
+                };
+                self.gecko.${gecko_ffi_name}.set_move(refptr)
+            }
+            Either::Second(_none) => {
+                unsafe {
+                    self.gecko.${gecko_ffi_name}.clear();
+                }
+            }
+        }
+    }
+    #[allow(non_snake_case)]
+    pub fn copy_${ident}_from(&mut self, other: &Self) {
+        unsafe {
+            self.gecko.${gecko_ffi_name}.set(&other.gecko.${gecko_ffi_name});
+        }
+    }
+    % if need_clone:
+        <% raise Exception("Do not know how to handle clone ") %>
+    % endif
+</%def>
+
 <%def name="impl_logical(name, need_clone=False, **kwargs)">
     ${helpers.logical_setter(name, need_clone)}
 </%def>
 
 <%def name="impl_style_struct(style_struct)">
 impl ${style_struct.gecko_struct_name} {
     #[allow(dead_code, unused_variables)]
     pub fn default(pres_context: RawGeckoPresContextBorrowed) -> Arc<Self> {
@@ -538,16 +631,18 @@ impl Debug for ${style_struct.gecko_stru
         "Position": impl_position,
         "LengthOrPercentage": impl_style_coord,
         "LengthOrPercentageOrAuto": impl_style_coord,
         "LengthOrPercentageOrNone": impl_style_coord,
         "LengthOrNone": impl_style_coord,
         "Number": impl_simple,
         "Opacity": impl_simple,
         "CSSColor": impl_color,
+        "SVGPaint": impl_svg_paint,
+        "UrlOrNone": impl_css_url,
     }
 
     def longhand_method(longhand):
         args = dict(ident=longhand.ident, gecko_ffi_name=longhand.gecko_ffi_name,
                     need_clone=longhand.need_clone)
 
         # get the method and pass additional keyword or type-specific arguments
         if longhand.logical:
@@ -1216,17 +1311,17 @@ fn static_assert() {
     ${impl_animation_count(ident, gecko_ffi_name)}
     ${impl_copy_animation_value(ident, gecko_ffi_name)}
 </%def>
 
 <% skip_box_longhands= """display overflow-y vertical-align
                           animation-name animation-delay animation-duration
                           animation-direction animation-fill-mode animation-play-state
                           animation-iteration-count animation-timing-function
-                          -moz-binding page-break-before page-break-after
+                          page-break-before page-break-after
                           scroll-snap-points-x scroll-snap-points-y transform
                           scroll-snap-type-y scroll-snap-coordinate
                           perspective-origin transform-origin""" %>
 <%self:impl_trait style_struct_name="Box" skip_longhands="${skip_box_longhands}">
 
     // We manually-implement the |display| property until we get general
     // infrastructure for preffing certain values.
     <% display_keyword = Keyword("display", "inline block inline-block table inline-table table-row-group " +
@@ -1311,43 +1406,16 @@ fn static_assert() {
                         .expect("Expected length or percentage for vertical-align");
                     T::LengthOrPercentage(v)
                 }
         }
     }
 
     <%call expr="impl_coord_copy('vertical_align', 'mVerticalAlign')"></%call>
 
-    #[allow(non_snake_case)]
-    pub fn set__moz_binding(&mut self, v: longhands::_moz_binding::computed_value::T) {
-        use values::Either;
-        match v {
-            Either::Second(_none) => debug_assert!(self.gecko.mBinding.mRawPtr.is_null()),
-            Either::First(ref url) => {
-                let extra_data = url.extra_data();
-                let (ptr, len) = match url.as_slice_components() {
-                    Ok(value) => value,
-                    Err(_) => (ptr::null(), 0),
-                };
-                unsafe {
-                    Gecko_SetMozBinding(&mut self.gecko,
-                                        ptr,
-                                        len as u32,
-                                        extra_data.base.get(),
-                                        extra_data.referrer.get(),
-                                        extra_data.principal.get());
-                }
-            }
-        }
-    }
-    #[allow(non_snake_case)]
-    pub fn copy__moz_binding_from(&mut self, other: &Self) {
-        unsafe { Gecko_CopyMozBindingFrom(&mut self.gecko, &other.gecko); }
-    }
-
     // Temp fix for Bugzilla bug 24000.
     // Map 'auto' and 'avoid' to false, and 'always', 'left', and 'right' to true.
     // "A conforming user agent may interpret the values 'left' and 'right'
     // as 'always'." - CSS2.1, section 13.3.1
     pub fn set_page_break_before(&mut self, v: longhands::page_break_before::computed_value::T) {
         use computed_values::page_break_before::T;
         let result = match v {
             T::auto   => false,
@@ -2061,27 +2129,23 @@ fn static_assert() {
         use values::Either;
         match image {
             Either::Second(_none) => {
                 unsafe {
                     Gecko_SetListStyleImageNone(&mut self.gecko);
                 }
             }
             Either::First(ref url) => {
-                let (ptr, len) = match url.as_slice_components() {
-                    Ok(value) | Err(value) => value
-                };
-                let extra_data = url.extra_data();
                 unsafe {
-                    Gecko_SetListStyleImage(&mut self.gecko,
-                                            ptr,
-                                            len as u32,
-                                            extra_data.base.get(),
-                                            extra_data.referrer.get(),
-                                            extra_data.principal.get());
+                    if let Some(ffi) = url.for_ffi() {
+                        Gecko_SetListStyleImage(&mut self.gecko,
+                                            ffi);
+                    } else {
+                        Gecko_SetListStyleImageNone(&mut self.gecko);
+                    }
                 }
                 // We don't need to record this struct as uncacheable, like when setting
                 // background-image to a url() value, since only properties in reset structs
                 // are re-used from the applicable declaration cache, and the List struct
                 // is an inherited struct.
             }
         }
     }
@@ -2142,17 +2206,16 @@ fn static_assert() {
 
     ${impl_simple_copy('_moz_image_region', 'mImageRegion')}
 
 </%self:impl_trait>
 
 <%self:impl_trait style_struct_name="Effects"
                   skip_longhands="box-shadow filter">
     pub fn set_box_shadow(&mut self, v: longhands::box_shadow::computed_value::T) {
-        use cssparser::Color;
 
         self.gecko.mBoxShadow.replace_with_new(v.0.len() as u32);
 
         for (servo, gecko_shadow) in v.0.into_iter()
                                       .zip(self.gecko.mBoxShadow.iter_mut()) {
 
             gecko_shadow.mXOffset = servo.offset_x.0;
             gecko_shadow.mYOffset = servo.offset_y.0;
@@ -2173,33 +2236,30 @@ fn static_assert() {
         }
     }
 
     pub fn copy_box_shadow_from(&mut self, other: &Self) {
         self.gecko.mBoxShadow.copy_from(&other.gecko.mBoxShadow);
     }
 
     pub fn clone_box_shadow(&self) -> longhands::box_shadow::computed_value::T {
-        use cssparser::Color;
-
         let buf = self.gecko.mBoxShadow.iter().map(|shadow| {
             longhands::box_shadow::single_value::computed_value::T {
                 offset_x: Au(shadow.mXOffset),
                 offset_y: Au(shadow.mYOffset),
                 blur_radius: Au(shadow.mRadius),
                 spread_radius: Au(shadow.mSpread),
                 inset: shadow.mInset,
                 color: Color::RGBA(convert_nscolor_to_rgba(shadow.mColor)),
             }
         }).collect();
         longhands::box_shadow::computed_value::T(buf)
     }
 
     pub fn set_filter(&mut self, v: longhands::filter::computed_value::T) {
-        use cssparser::Color;
         use properties::longhands::filter::computed_value::Filter::*;
         use gecko_bindings::structs::nsCSSShadowArray;
         use gecko_bindings::structs::nsStyleFilter;
         use gecko_bindings::structs::NS_STYLE_FILTER_BLUR;
         use gecko_bindings::structs::NS_STYLE_FILTER_BRIGHTNESS;
         use gecko_bindings::structs::NS_STYLE_FILTER_CONTRAST;
         use gecko_bindings::structs::NS_STYLE_FILTER_GRAYSCALE;
         use gecko_bindings::structs::NS_STYLE_FILTER_INVERT;
@@ -2274,16 +2334,23 @@ fn static_assert() {
                             gecko_shadow.mArray[0].mHasColor = true;
                             convert_rgba_to_nscolor(&rgba)
                         },
                         // TODO handle currentColor
                         // https://bugzilla.mozilla.org/show_bug.cgi?id=760345
                         Color::CurrentColor => 0,
                     };
                 }
+                Url(ref url) => {
+                    unsafe {
+                        if let Some(ffi) = url.for_ffi() {
+                            bindings::Gecko_nsStyleFilter_SetURLValue(gecko_filter, ffi);
+                        }
+                    }
+                }
             }
         }
     }
 
     pub fn copy_filter_from(&mut self, other: &Self) {
         unsafe {
             Gecko_CopyFiltersFrom(&other.gecko as *const _ as *mut _, &mut self.gecko);
         }
@@ -2311,17 +2378,16 @@ fn static_assert() {
                   skip_longhands="text-align text-emphasis-style text-shadow line-height letter-spacing word-spacing
                                   -webkit-text-stroke-width text-emphasis-position -moz-tab-size">
 
     <% text_align_keyword = Keyword("text-align", "start end left right center justify -moz-center -moz-left " +
                                                   "-moz-right match-parent") %>
     ${impl_keyword('text_align', 'mTextAlign', text_align_keyword, need_clone=False)}
 
     pub fn set_text_shadow(&mut self, v: longhands::text_shadow::computed_value::T) {
-        use cssparser::Color;
         self.gecko.mTextShadow.replace_with_new(v.0.len() as u32);
 
         for (servo, gecko_shadow) in v.0.into_iter()
                                       .zip(self.gecko.mTextShadow.iter_mut()) {
 
             gecko_shadow.mXOffset = servo.offset_x.0;
             gecko_shadow.mYOffset = servo.offset_y.0;
             gecko_shadow.mRadius = servo.blur_radius.0;
@@ -2339,17 +2405,16 @@ fn static_assert() {
         }
     }
 
     pub fn copy_text_shadow_from(&mut self, other: &Self) {
         self.gecko.mTextShadow.copy_from(&other.gecko.mTextShadow);
     }
 
     pub fn clone_text_shadow(&self) -> longhands::text_shadow::computed_value::T {
-        use cssparser::Color;
 
         let buf = self.gecko.mTextShadow.iter().map(|shadow| {
             longhands::text_shadow::computed_value::TextShadow {
                 offset_x: Au(shadow.mXOffset),
                 offset_y: Au(shadow.mYOffset),
                 blur_radius: Au(shadow.mRadius),
                 color: Color::RGBA(convert_nscolor_to_rgba(shadow.mColor)),
             }
@@ -2631,17 +2696,23 @@ clip-path
         use values::computed::basic_shape::*;
         let ref mut clip_path = self.gecko.mClipPath;
         // clean up existing struct
         unsafe { Gecko_DestroyClipPath(clip_path) };
 
         clip_path.mType = StyleShapeSourceType::None;
 
         match v {
-            ShapeSource::Url(..) => warn!("stylo: clip-path: url() not yet implemented"),
+            ShapeSource::Url(ref url) => {
+                unsafe {
+                    if let Some(ffi) = url.for_ffi() {
+                       bindings::Gecko_StyleClipPath_SetURLValue(clip_path, ffi);
+                    }
+                }
+            }
             ShapeSource::None => {} // don't change the type
             ShapeSource::Box(reference) => {
                 clip_path.mReferenceBox = reference.into();
                 clip_path.mType = StyleShapeSourceType::Box;
             }
             ShapeSource::Shape(servo_shape, maybe_box) => {
                 clip_path.mReferenceBox = maybe_box.map(Into::into)
                                                    .unwrap_or(StyleGeometryBox::NoBox);
@@ -2721,42 +2792,61 @@ clip-path
     }
 
     pub fn copy_clip_path_from(&mut self, other: &Self) {
         use gecko_bindings::bindings::Gecko_CopyClipPathValueFrom;
         unsafe {
             Gecko_CopyClipPathValueFrom(&mut self.gecko.mClipPath, &other.gecko.mClipPath);
         }
     }
-
-    pub fn clone_clip_path(&self) -> longhands::clip_path::computed_value::T {
-        use gecko_bindings::structs::StyleShapeSourceType;
-        use gecko_bindings::structs::StyleGeometryBox;
-        use values::computed::basic_shape::*;
-        let ref clip_path = self.gecko.mClipPath;
-
-        match clip_path.mType {
-            StyleShapeSourceType::None => ShapeSource::None,
-            StyleShapeSourceType::Box => {
-                ShapeSource::Box(clip_path.mReferenceBox.into())
+</%self:impl_trait>
+
+<%self:impl_trait style_struct_name="InheritedSVG"
+                  skip_longhands="paint-order stroke-dasharray"
+                  skip_additionals="*">
+    pub fn set_paint_order(&mut self, v: longhands::paint_order::computed_value::T) {
+        use self::longhands::paint_order;
+
+        if v.0 == 0 {
+            self.gecko.mPaintOrder = structs::NS_STYLE_PAINT_ORDER_NORMAL as u8;
+        } else {
+            let mut order = 0;
+
+            for pos in 0..3 {
+                let geckoval = match v.bits_at(pos) {
+                    paint_order::FILL => structs::NS_STYLE_PAINT_ORDER_FILL as u8,
+                    paint_order::STROKE => structs::NS_STYLE_PAINT_ORDER_STROKE as u8,
+                    paint_order::MARKERS => structs::NS_STYLE_PAINT_ORDER_MARKERS as u8,
+                    _ => unreachable!(),
+                };
+                order |= geckoval << (pos * structs::NS_STYLE_PAINT_ORDER_BITWIDTH as u8);
             }
-            StyleShapeSourceType::URL => {
-                warn!("stylo: clip-path: url() not implemented yet");
-                Default::default()
+
+            self.gecko.mPaintOrder = order;
+        }
+    }
+
+    ${impl_simple_copy('paint_order', 'mPaintOrder')}
+
+    pub fn set_stroke_dasharray(&mut self, v: longhands::stroke_dasharray::computed_value::T) {
+        unsafe {
+            bindings::Gecko_nsStyleSVG_SetDashArrayLength(&mut self.gecko, v.0.len() as u32);
+        }
+
+        for (mut gecko, servo) in self.gecko.mStrokeDasharray.iter_mut().zip(v.0.into_iter()) {
+            match servo {
+                Either::First(lop) => gecko.set(lop),
+                Either::Second(number) => gecko.set_value(CoordDataValue::Factor(number)),
             }
-            StyleShapeSourceType::Shape => {
-                let reference = if let StyleGeometryBox::NoBox = clip_path.mReferenceBox {
-                    None
-                } else {
-                    Some(clip_path.mReferenceBox.into())
-                };
-                let union = clip_path.__bindgen_anon_1;
-                let shape = unsafe { &**union.mBasicShape.as_ref() };
-                ShapeSource::Shape(shape.into(), reference)
-            }
+        }
+    }
+
+    pub fn copy_stroke_dasharray_from(&mut self, other: &Self) {
+        unsafe {
+            bindings::Gecko_nsStyleSVG_CopyDashArray(&mut self.gecko, &other.gecko);
         }
     }
 </%self:impl_trait>
 
 <%self:impl_trait style_struct_name="Color"
                   skip_longhands="*">
     pub fn set_color(&mut self, v: longhands::color::computed_value::T) {
         let result = convert_rgba_to_nscolor(&v);
--- a/servo/components/style/properties/helpers.mako.rs
+++ b/servo/components/style/properties/helpers.mako.rs
@@ -61,17 +61,18 @@
     to True for cases where Servo takes a single value
     and Stylo supports vector values.
 
     Setting allow_empty to False allows for cases where the vector
     is empty. The grammar for these is usually "none | <thing> [ , <thing> ]*".
     We assume that the default/initial value is an empty vector for these.
     `initial_value` need not be defined for these.
 </%doc>
-<%def name="vector_longhand(name, gecko_only=False, allow_empty=False, delegate_animate=False, **kwargs)">
+<%def name="vector_longhand(name, gecko_only=False, allow_empty=False,
+            delegate_animate=False, space_separated_allowed=False, **kwargs)">
     <%call expr="longhand(name, **kwargs)">
         % if not gecko_only:
             use std::fmt;
             use values::HasViewportPercentage;
             use style_traits::ToCss;
 
             impl HasViewportPercentage for SpecifiedValue {
                 fn has_viewport_percentage(&self) -> bool {
@@ -81,16 +82,17 @@
             }
 
             pub mod single_value {
                 use cssparser::Parser;
                 use parser::{Parse, ParserContext, ParserContextExtraData};
                 use properties::{CSSWideKeyword, DeclaredValue, ShorthandId};
                 use values::computed::{Context, ToComputedValue};
                 use values::{computed, specified};
+                use values::{Auto, Either, None_, Normal};
                 ${caller.body()}
             }
 
             /// The definition of the computed value for ${name}.
             pub mod computed_value {
                 pub use super::single_value::computed_value as single_value;
                 pub use self::single_value::T as SingleComputedValue;
                 /// The computed value, effectively a list of single values.
@@ -161,26 +163,33 @@
                 % if allow_empty:
                     computed_value::T(vec![])
                 % else:
                     computed_value::T(vec![single_value::get_initial_value()])
                 % endif
             }
 
             pub fn parse(context: &ParserContext, input: &mut Parser) -> Result<SpecifiedValue, ()> {
+                use parser::parse_space_or_comma_separated;
+
+                <%
+                    parse_func = "Parser::parse_comma_separated"
+                    if space_separated_allowed:
+                        parse_func = "parse_space_or_comma_separated"
+                %>
                 % if allow_empty:
                     if input.try(|input| input.expect_ident_matching("none")).is_ok() {
                         Ok(SpecifiedValue(Vec::new()))
                     } else {
-                        input.parse_comma_separated(|parser| {
+                        ${parse_func}(input, |parser| {
                             single_value::parse(context, parser)
                         }).map(SpecifiedValue)
                     }
                 % else:
-                    input.parse_comma_separated(|parser| {
+                    ${parse_func}(input, |parser| {
                         single_value::parse(context, parser)
                     }).map(SpecifiedValue)
                 % endif
             }
 
             pub use self::single_value::SpecifiedValue as SingleSpecifiedValue;
 
             impl ToComputedValue for SpecifiedValue {
--- a/servo/components/style/properties/longhand/box.mako.rs
+++ b/servo/components/style/properties/longhand/box.mako.rs
@@ -1917,16 +1917,17 @@
                          gecko_constant_prefix="NS_THEME",
                          products="gecko",
                          spec="Nonstandard (https://developer.mozilla.org/en-US/docs/Web/CSS/-moz-appearance)",
                          animatable=False)}
 
 ${helpers.predefined_type("-moz-binding", "UrlOrNone", "Either::Second(None_)",
                           products="gecko",
                           animatable="False",
+                          gecko_ffi_name="mBinding",
                           spec="Nonstandard (https://developer.mozilla.org/en-US/docs/Web/CSS/-moz-binding)",
                           disable_when_testing="True",
                           boxed=True)}
 
 ${helpers.single_keyword("-moz-orient",
                           "inline block horizontal vertical",
                           products="gecko",
                           gecko_ffi_name="mOrient",
--- a/servo/components/style/properties/longhand/effects.mako.rs
+++ b/servo/components/style/properties/longhand/effects.mako.rs
@@ -89,16 +89,17 @@
 <%helpers:longhand name="filter" animatable="False" extra_prefixes="webkit"
                    spec="https://drafts.fxtf.org/filters/#propdef-filter">
     //pub use self::computed_value::T as SpecifiedValue;
     use cssparser;
     use std::fmt;
     use style_traits::{self, ToCss};
     use values::{CSSFloat, HasViewportPercentage};
     use values::specified::{Angle, CSSColor, Length, Shadow};
+    use values::specified::url::SpecifiedUrl;
 
     impl HasViewportPercentage for SpecifiedValue {
         fn has_viewport_percentage(&self) -> bool {
             self.0.iter().any(|ref x| x.has_viewport_percentage())
         }
     }
 
     #[derive(Debug, Clone, PartialEq)]
@@ -124,39 +125,42 @@
         Grayscale(CSSFloat),
         HueRotate(Angle),
         Invert(CSSFloat),
         Opacity(CSSFloat),
         Saturate(CSSFloat),
         Sepia(CSSFloat),
         % if product == "gecko":
         DropShadow(Shadow),
+        Url(SpecifiedUrl),
         % endif
     }
 
     pub mod computed_value {
         use app_units::Au;
         use values::CSSFloat;
         use values::computed::{CSSColor, Shadow};
-        use values::specified::{Angle};
+        use values::specified::Angle;
+        use values::specified::url::SpecifiedUrl;
 
         #[derive(Clone, PartialEq, Debug)]
         #[cfg_attr(feature = "servo", derive(HeapSizeOf, Deserialize, Serialize))]
         pub enum Filter {
             Blur(Au),
             Brightness(CSSFloat),
             Contrast(CSSFloat),
             Grayscale(CSSFloat),
             HueRotate(Angle),
             Invert(CSSFloat),
             Opacity(CSSFloat),
             Saturate(CSSFloat),
             Sepia(CSSFloat),
             % if product == "gecko":
             DropShadow(Shadow),
+            Url(SpecifiedUrl),
             % endif
         }
 
         #[derive(Clone, PartialEq, Debug)]
         #[cfg_attr(feature = "servo", derive(HeapSizeOf, Deserialize, Serialize))]
         pub struct T { pub filters: Vec<Filter> }
 
         impl T {
@@ -257,16 +261,21 @@
                     try!(dest.write_str(" "));
                     try!(shadow.offset_y.to_css(dest));
                     try!(dest.write_str(" "));
                     try!(shadow.blur_radius.to_css(dest));
                     try!(dest.write_str(" "));
                     try!(shadow.color.to_css(dest));
                     try!(dest.write_str(")"));
                 }
+                computed_value::Filter::Url(ref url) => {
+                    dest.write_str("url(")?;
+                    url.to_css(dest)?;
+                    dest.write_str(")")?;
+                }
                 % endif
             }
             Ok(())
         }
     }
 
     impl ToCss for SpecifiedFilter {
         fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
@@ -297,16 +306,21 @@
                     try!(dest.write_str(" "));
                     try!(shadow.blur_radius.to_css(dest));
                     if let Some(ref color) = shadow.color {
                         try!(dest.write_str(" "));
                         try!(color.to_css(dest));
                     }
                     try!(dest.write_str(")"));
                 }
+                SpecifiedFilter::Url(ref url) => {
+                    dest.write_str("url(")?;
+                    url.to_css(dest)?;
+                    dest.write_str(")")?;
+                }
                 % endif
             }
             Ok(())
         }
     }
 
     #[inline]
     pub fn get_initial_value() -> computed_value::T {
@@ -314,16 +328,21 @@
     }
 
     pub fn parse(context: &ParserContext, input: &mut Parser) -> Result<SpecifiedValue, ()> {
         let mut filters = Vec::new();
         if input.try(|input| input.expect_ident_matching("none")).is_ok() {
             return Ok(SpecifiedValue(filters))
         }
         loop {
+            % if product == "gecko":
+                if let Ok(url) = input.try(|i| SpecifiedUrl::parse(context, i)) {
+                    filters.push(SpecifiedFilter::Url(url));
+                } else
+            % endif
             if let Ok(function_name) = input.try(|input| input.expect_function()) {
                 filters.push(try!(input.parse_nested_block(|input| {
                     match_ignore_ascii_case! { function_name,
                         "blur" => specified::Length::parse_non_negative(input).map(SpecifiedFilter::Blur),
                         "brightness" => parse_factor(input).map(SpecifiedFilter::Brightness),
                         "contrast" => parse_factor(input).map(SpecifiedFilter::Contrast),
                         "grayscale" => parse_factor(input).map(SpecifiedFilter::Grayscale),
                         "hue-rotate" => Angle::parse(context, input).map(SpecifiedFilter::HueRotate),
@@ -370,16 +389,19 @@
                     SpecifiedFilter::Invert(factor) => computed_value::Filter::Invert(factor),
                     SpecifiedFilter::Opacity(factor) => computed_value::Filter::Opacity(factor),
                     SpecifiedFilter::Saturate(factor) => computed_value::Filter::Saturate(factor),
                     SpecifiedFilter::Sepia(factor) => computed_value::Filter::Sepia(factor),
                     % if product == "gecko":
                     SpecifiedFilter::DropShadow(ref shadow) => {
                         computed_value::Filter::DropShadow(shadow.to_computed_value(context))
                     },
+                    SpecifiedFilter::Url(ref url) => {
+                        computed_value::Filter::Url(url.to_computed_value(context))
+                    }
                     % endif
                 }
             }).collect() }
         }
 
         fn from_computed_value(computed: &computed_value::T) -> Self {
             SpecifiedValue(computed.filters.iter().map(|value| {
                 match *value {
@@ -389,19 +411,24 @@
                     computed_value::Filter::Contrast(factor) => SpecifiedFilter::Contrast(factor),
                     computed_value::Filter::Grayscale(factor) => SpecifiedFilter::Grayscale(factor),
                     computed_value::Filter::HueRotate(factor) => SpecifiedFilter::HueRotate(factor),
                     computed_value::Filter::Invert(factor) => SpecifiedFilter::Invert(factor),
                     computed_value::Filter::Opacity(factor) => SpecifiedFilter::Opacity(factor),
                     computed_value::Filter::Saturate(factor) => SpecifiedFilter::Saturate(factor),
                     computed_value::Filter::Sepia(factor) => SpecifiedFilter::Sepia(factor),
                     % if product == "gecko":
-                    computed_value::Filter::DropShadow(shadow) => {
+                    computed_value::Filter::DropShadow(ref shadow) => {
                         SpecifiedFilter::DropShadow(
-                            ToComputedValue::from_computed_value(&shadow),
+                            ToComputedValue::from_computed_value(shadow),
+                        )
+                    }
+                    computed_value::Filter::Url(ref url) => {
+                        SpecifiedFilter::Url(
+                            ToComputedValue::from_computed_value(url),
                         )
                     }
                     % endif
                 }
             }).collect())
         }
     }
 </%helpers:longhand>
--- a/servo/components/style/properties/longhand/inherited_svg.mako.rs
+++ b/servo/components/style/properties/longhand/inherited_svg.mako.rs
@@ -28,32 +28,57 @@
                          spec="https://www.w3.org/TR/SVG11/painting.html#ColorInterpolationProperty")}
 
 ${helpers.single_keyword("color-interpolation-filters", "auto sRGB linearRGB",
                          products="gecko",
                          gecko_constant_prefix="NS_STYLE_COLOR_INTERPOLATION",
                          animatable=False,
                          spec="https://www.w3.org/TR/SVG11/painting.html#ColorInterpolationFiltersProperty")}
 
+${helpers.predefined_type(
+    "fill", "SVGPaint",
+    "::values::computed::SVGPaint::black()",
+    products="gecko",
+    animatable=False,
+    boxed=True,
+    spec="https://www.w3.org/TR/SVG2/painting.html#SpecifyingFillPaint")}
+
 ${helpers.predefined_type("fill-opacity", "Opacity", "1.0",
                           products="gecko", animatable=False,
                           spec="https://www.w3.org/TR/SVG11/painting.html#FillOpacityProperty")}
 
 ${helpers.single_keyword("fill-rule", "nonzero evenodd",
                          gecko_enum_prefix="StyleFillRule",
                          gecko_inexhaustive=True,
                          products="gecko", animatable=False,
                          spec="https://www.w3.org/TR/SVG11/painting.html#FillRuleProperty")}
 
 ${helpers.single_keyword("shape-rendering",
                          "auto optimizeSpeed crispEdges geometricPrecision",
                          products="gecko",
                          animatable=False,
                          spec="https://www.w3.org/TR/SVG11/painting.html#ShapeRenderingProperty")}
 
+${helpers.predefined_type(
+    "stroke", "SVGPaint",
+    "Default::default()",
+    products="gecko",
+    animatable=False,
+    boxed=True,
+    spec="https://www.w3.org/TR/SVG2/painting.html#SpecifyingStrokePaint")}
+
+${helpers.predefined_type(
+    "stroke-width", "LengthOrPercentage",
+    "computed::LengthOrPercentage::one()",
+    "parse_numbers_are_pixels_non_negative",
+    products="gecko",
+    animatable=True,
+    needs_context=False,
+    spec="https://www.w3.org/TR/SVG2/painting.html#StrokeWidth")}
+
 ${helpers.single_keyword("stroke-linecap", "butt round square",
                          products="gecko", animatable=False,
                          spec="https://www.w3.org/TR/SVG11/painting.html#StrokeLinecapProperty")}
 
 ${helpers.single_keyword("stroke-linejoin", "miter round bevel",
                          products="gecko", animatable=False,
                          spec="https://www.w3.org/TR/SVG11/painting.html#StrokeLinejoinProperty")}
 
@@ -62,15 +87,182 @@
                           needs_context=False,
                           animatable=False,
                           spec="https://www.w3.org/TR/SVG11/painting.html#StrokeMiterlimitProperty")}
 
 ${helpers.predefined_type("stroke-opacity", "Opacity", "1.0",
                           products="gecko", animatable=False,
                           spec="https://www.w3.org/TR/SVG11/painting.html#StrokeOpacityProperty")}
 
+${helpers.predefined_type("stroke-dasharray", "LoPOrNumber", "Either::Second(0.0)",
+                          "parse_non_negative",
+                          vector="True",
+                          products="gecko",
+                          animatable="False",
+                          space_separated_allowed="True",
+                          spec="https://www.w3.org/TR/SVG2/painting.html#StrokeDashing")}
+
+${helpers.predefined_type(
+    "stroke-dashoffset", "LengthOrPercentage",
+    "computed::LengthOrPercentage::zero()",
+    "parse_numbers_are_pixels",
+    products="gecko",
+    animatable=True,
+    needs_context=False,
+    spec="https://www.w3.org/TR/SVG2/painting.html#StrokeDashing")}
+
 // Section 14 - Clipping, Masking and Compositing
 ${helpers.single_keyword("clip-rule", "nonzero evenodd",
                          products="gecko",
                          gecko_enum_prefix="StyleFillRule",
                          gecko_inexhaustive=True,
                          animatable=False,
                          spec="https://www.w3.org/TR/SVG11/masking.html#ClipRuleProperty")}
+
+${helpers.predefined_type("marker-start", "UrlOrNone", "Either::Second(None_)",
+                          products="gecko",
+                          animatable="False",
+                          spec="https://www.w3.org/TR/SVG2/painting.html#VertexMarkerProperties")}
+
+${helpers.predefined_type("marker-mid", "UrlOrNone", "Either::Second(None_)",
+                          products="gecko",
+                          animatable="False",
+                          spec="https://www.w3.org/TR/SVG2/painting.html#VertexMarkerProperties")}
+
+${helpers.predefined_type("marker-end", "UrlOrNone", "Either::Second(None_)",
+                          products="gecko",
+                          animatable="False",
+                          spec="https://www.w3.org/TR/SVG2/painting.html#VertexMarkerProperties")}
+
+<%helpers:longhand name="paint-order"
+                   animatable="False"
+                   products="gecko"
+                   spec="https://www.w3.org/TR/SVG2/painting.html#PaintOrder">
+
+    use values::computed::ComputedValueAsSpecified;
+    use std::fmt;
+    use style_traits::ToCss;
+    use values::HasViewportPercentage;
+
+    pub const NORMAL: u8 = 0;
+    pub const FILL: u8 = 1;
+    pub const STROKE: u8 = 2;
+    pub const MARKERS: u8 = 3;
+
+    // number of bits for each component
+    pub const SHIFT: u8 = 2;
+    // mask with above bits set
+    pub const MASK: u8 = 0b11;
+    // number of non-normal keyword values
+    pub const COUNT: u8 = 3;
+    // all keywords
+    pub const ALL: [u8; 3] = [FILL, STROKE, MARKERS];
+
+    /// Represented as a six-bit field, of 3 two-bit pairs
+    ///
+    /// Each pair can be set to FILL, STROKE, or MARKERS
+    /// Lowest significant bit pairs are highest priority.
+    ///  `normal` is the empty bitfield. The three pairs are
+    /// never zero in any case other than `normal`.
+    ///
+    /// Higher priority values, i.e. the values specified first,
+    /// will be painted first (and may be covered by paintings of lower priority)
+    #[derive(PartialEq, Clone, Copy, Debug)]
+    #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
+    pub struct SpecifiedValue(pub u8);
+
+    pub mod computed_value {
+        pub use super::SpecifiedValue as T;
+    }
+
+    pub fn get_initial_value() -> SpecifiedValue {
+      SpecifiedValue(NORMAL)
+    }
+
+    impl SpecifiedValue {
+        pub fn bits_at(&self, pos: u8) -> u8 {
+            (self.0 >> pos * SHIFT) & MASK
+        }
+    }
+
+    pub fn parse(_context: &ParserContext, input: &mut Parser) -> Result<SpecifiedValue,()> {
+        if let Ok(()) = input.try(|i| i.expect_ident_matching("normal")) {
+            Ok(SpecifiedValue(0))
+        } else {
+            let mut value = 0;
+            // bitfield representing what we've seen so far
+            // bit 1 is fill, bit 2 is stroke, bit 3 is markers
+            let mut seen = 0;
+            let mut pos = 0;
+
+            loop {
+
+                let result = input.try(|i| {
+                    match_ignore_ascii_case! { i.expect_ident()?,
+                        "fill" => Ok(FILL),
+                        "stroke" => Ok(STROKE),
+                        "markers" => Ok(MARKERS),
+                        _ => Err(())
+                    }
+                });
+
+                match result {
+                    Ok(val) => {
+                        if (seen & (1 << val)) != 0 {
+                            // don't parse the same ident twice
+                            return Err(())
+                        } else {
+                            value |= val << (pos * SHIFT);
+                            seen |= 1 << val;
+                            pos += 1;
+                        }
+                    }
+                    Err(()) => break,
+                }
+            }
+
+            if value == 0 {
+                // couldn't find any keyword
+                Err(())
+            } else {
+                // fill in rest
+                for i in pos..COUNT {
+                    for paint in &ALL {
+                        // if not seen, set bit at position, mark as seen
+                        if (seen & (1 << paint)) == 0 {
+                            seen |= 1 << paint;
+                            value |= paint << (i * SHIFT);
+                            break;
+                        }
+                    }
+                }
+
+                Ok(SpecifiedValue(value))
+            }
+        }
+    }
+
+    impl ToCss for SpecifiedValue {
+        fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
+            if self.0 == 0 {
+                return dest.write_str("normal")
+            }
+
+            for pos in 0..COUNT {
+                if pos != 0 {
+                    dest.write_str(" ")?
+                }
+                match self.bits_at(pos) {
+                    FILL => dest.write_str("fill")?,
+                    STROKE => dest.write_str("stroke")?,
+                    MARKERS => dest.write_str("markers")?,
+                    _ => unreachable!(),
+                }
+            }
+            Ok(())
+        }
+    }
+
+    no_viewport_percentage!(SpecifiedValue);
+
+    impl ComputedValueAsSpecified for SpecifiedValue { }
+</%helpers:longhand>
+
new file mode 100644
--- /dev/null
+++ b/servo/components/style/properties/shorthand/inherited_svg.mako.rs
@@ -0,0 +1,36 @@
+/* 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/. */
+
+<%namespace name="helpers" file="/helpers.mako.rs" />
+
+<%helpers:shorthand name="marker" products="gecko"
+    sub_properties="marker-start marker-end marker-mid"
+    spec="https://www.w3.org/TR/SVG2/painting.html#MarkerShorthand">
+    use values::specified::UrlOrNone;
+
+    pub fn parse_value(context: &ParserContext, input: &mut Parser) -> Result<Longhands, ()> {
+        let url = UrlOrNone::parse(context, input)?;
+
+        Ok(Longhands {
+            marker_start: url.clone(),
+            marker_mid: url.clone(),
+            marker_end: url,
+        })
+    }
+
+    impl<'a> LonghandsToSerialize<'a>  {
+        fn to_css_declared<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
+            if let DeclaredValue::Value(ref start) = *self.marker_start {
+                if let DeclaredValue::Value(ref mid) = *self.marker_mid {
+                    if let DeclaredValue::Value(ref end) = *self.marker_end {
+                        if start == mid && mid == end {
+                            start.to_css(dest)?;
+                        }
+                    }
+                }
+            }
+            Ok(())
+        }
+    }
+</%helpers:shorthand>
--- a/servo/components/style/values/computed/length.rs
+++ b/servo/components/style/values/computed/length.rs
@@ -1,15 +1,15 @@
 /* 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/. */
 
 //! `<length>` computed values, and related ones.
 
-use app_units::Au;
+use app_units::{Au, AU_PER_PX};
 use ordered_float::NotNaN;
 use std::fmt;
 use style_traits::ToCss;
 use super::{Number, ToComputedValue, Context};
 use values::{Auto, CSSFloat, Either, None_, Normal, specified};
 use values::specified::length::{FontRelativeLength, ViewportPercentageLength};
 
 pub use cssparser::Color as CSSColor;
@@ -190,16 +190,22 @@ pub enum LengthOrPercentage {
 
 impl LengthOrPercentage {
     #[inline]
     #[allow(missing_docs)]
     pub fn zero() -> LengthOrPercentage {
         LengthOrPercentage::Length(Au(0))
     }
 
+    #[inline]
+    /// 1px length value for SVG defaults
+    pub fn one() -> LengthOrPercentage {
+        LengthOrPercentage::Length(Au(AU_PER_PX))
+    }
+
     /// Returns true if the computed value is absolute 0 or 0%.
     ///
     /// (Returns false for calc() values, even if ones that may resolve to zero.)
     #[inline]
     pub fn is_definitely_zero(&self) -> bool {
         use self::LengthOrPercentage::*;
         match *self {
             Length(Au(0)) | Percentage(0.0) => true,
--- a/servo/components/style/values/computed/mod.rs
+++ b/servo/components/style/values/computed/mod.rs
@@ -5,26 +5,26 @@
 //! Computed values.
 
 use app_units::Au;
 use euclid::size::Size2D;
 use font_metrics::FontMetricsProvider;
 use properties::ComputedValues;
 use std::fmt;
 use style_traits::ToCss;
-use super::{CSSFloat, specified};
+use super::{CSSFloat, RGBA, specified};
 
 pub use cssparser::Color as CSSColor;
 pub use self::image::{AngleOrCorner, EndingShape as GradientShape, Gradient, GradientKind, Image};
 pub use self::image::{LengthOrKeyword, LengthOrPercentageOrKeyword};
 pub use super::{Auto, Either, None_};
 #[cfg(feature = "gecko")]
 pub use super::specified::{AlignJustifyContent, AlignJustifySelf};
 pub use super::specified::{Angle, BorderStyle, GridLine, Time, UrlOrNone};
-pub use super::specified::url::UrlExtraData;
+pub use super::specified::url::{SpecifiedUrl, UrlExtraData};
 pub use self::length::{CalcLengthOrPercentage, Length, LengthOrNumber, LengthOrPercentage, LengthOrPercentageOrAuto};
 pub use self::length::{LengthOrPercentageOrAutoOrContent, LengthOrPercentageOrNone, LengthOrNone};
 pub use self::position::Position;
 
 pub mod basic_shape;
 pub mod image;
 pub mod length;
 pub mod position;
@@ -180,16 +180,93 @@ pub struct Shadow {
 
 /// A `<number>` value.
 pub type Number = CSSFloat;
 
 /// A type used for opacity.
 pub type Opacity = CSSFloat;
 
 
+/// An SVG paint value
+///
+/// https://www.w3.org/TR/SVG2/painting.html#SpecifyingPaint
+#[derive(Debug, Clone, PartialEq)]
+#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
+pub struct SVGPaint {
+    /// The paint source
+    pub kind: SVGPaintKind,
+    /// The fallback color
+    pub fallback: Option<CSSColor>,
+}
+
+impl Default for SVGPaint {
+    fn default() -> Self {
+        SVGPaint {
+            kind: SVGPaintKind::None,
+            fallback: None,
+        }
+    }
+}
+
+impl SVGPaint {
+    /// Opaque black color
+    pub fn black() -> Self {
+        let rgba = RGBA::from_floats(0., 0., 0., 1.);
+        SVGPaint {
+            kind: SVGPaintKind::Color(CSSColor::RGBA(rgba)),
+            fallback: None,
+        }
+    }
+}
+
+/// An SVG paint value without the fallback
+///
+/// Whereas the spec only allows PaintServer
+/// to have a fallback, Gecko lets the context
+/// properties have a fallback as well.
+#[derive(Debug, Clone, PartialEq)]
+#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
+pub enum SVGPaintKind {
+    /// `none`
+    None,
+    /// `<color>`
+    Color(CSSColor),
+    /// `url(...)`
+    PaintServer(SpecifiedUrl),
+    /// `context-fill`
+    ContextFill,
+    /// `context-stroke`
+    ContextStroke,
+}
+
+impl ToCss for SVGPaintKind {
+    fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
+        match *self {
+            SVGPaintKind::None => dest.write_str("none"),
+            SVGPaintKind::ContextStroke => dest.write_str("context-stroke"),
+            SVGPaintKind::ContextFill => dest.write_str("context-fill"),
+            SVGPaintKind::Color(ref color) => color.to_css(dest),
+            SVGPaintKind::PaintServer(ref server) => server.to_css(dest),
+        }
+    }
+}
+
+impl ToCss for SVGPaint {
+    fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
+        self.kind.to_css(dest)?;
+        if let Some(ref fallback) = self.fallback {
+            fallback.to_css(dest)?;
+        }
+        Ok(())
+    }
+}
+
+/// <length> | <percentage> | <number>
+pub type LoPOrNumber = Either<LengthOrPercentage, Number>;
+
 #[derive(Clone, PartialEq, Eq, Copy, Debug)]
 #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
 #[allow(missing_docs)]
 /// A computed cliprect for clip and image-region
 pub struct ClipRect {
     pub top: Au,
     pub right: Option<Au>,
     pub bottom: Option<Au>,
--- a/servo/components/style/values/specified/length.rs
+++ b/servo/components/style/values/specified/length.rs
@@ -982,16 +982,44 @@ impl LengthOrPercentage {
     }
 
     /// Parse a non-negative length.
     #[inline]
     pub fn parse_non_negative(input: &mut Parser) -> Result<LengthOrPercentage, ()> {
         LengthOrPercentage::parse_internal(input, AllowedNumericType::NonNegative)
     }
 
+    /// Parse a length, treating dimensionless numbers as pixels
+    ///
+    /// https://www.w3.org/TR/SVG2/types.html#presentation-attribute-css-value
+    pub fn parse_numbers_are_pixels(input: &mut Parser) -> Result<LengthOrPercentage, ()> {
+        if let Ok(lop) = input.try(|i| Self::parse_internal(i, AllowedNumericType::All)) {
+            Ok(lop)
+        } else {
+            let num = input.expect_number()?;
+            Ok(LengthOrPercentage::Length(NoCalcLength::Absolute(Au((AU_PER_PX * num) as i32))))
+        }
+    }
+
+    /// Parse a non-negative length, treating dimensionless numbers as pixels
+    ///
+    /// This is nonstandard behavior used by Firefox for SVG
+    pub fn parse_numbers_are_pixels_non_negative(input: &mut Parser) -> Result<LengthOrPercentage, ()> {
+        if let Ok(lop) = input.try(|i| Self::parse_internal(i, AllowedNumericType::NonNegative)) {
+            Ok(lop)
+        } else {
+            let num = input.expect_number()?;
+            if num >= 0. {
+                Ok(LengthOrPercentage::Length(NoCalcLength::Absolute(Au((AU_PER_PX * num) as i32))))
+            } else {
+                Err(())
+            }
+        }
+    }
+
     /// Extract value from ref without a clone, replacing it with a 0 Au
     ///
     /// Use when you need to move out of a length array without cloning
     #[inline]
     pub fn take(&mut self) -> Self {
         mem::replace(self, LengthOrPercentage::zero())
     }
 }
--- a/servo/components/style/values/specified/mod.rs
+++ b/servo/components/style/values/specified/mod.rs
@@ -665,16 +665,185 @@ impl Shadow {
             blur_radius: lengths[2].take(),
             spread_radius: if disable_spread_and_inset { Length::zero() } else { lengths[3].take() },
             color: color,
             inset: inset,
         })
     }
 }
 
+no_viewport_percentage!(SVGPaint);
+
+/// An SVG paint value
+///
+/// https://www.w3.org/TR/SVG2/painting.html#SpecifyingPaint
+#[derive(Debug, Clone, PartialEq)]
+#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
+pub struct SVGPaint {
+    /// The paint source
+    pub kind: SVGPaintKind,
+    /// The fallback color
+    pub fallback: Option<CSSColor>,
+}
+
+/// An SVG paint value without the fallback
+///
+/// Whereas the spec only allows PaintServer
+/// to have a fallback, Gecko lets the context
+/// properties have a fallback as well.
+#[derive(Debug, Clone, PartialEq)]
+#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
+pub enum SVGPaintKind {
+    /// `none`
+    None,
+    /// `<color>`
+    Color(CSSColor),
+    /// `url(...)`
+    PaintServer(SpecifiedUrl),
+    /// `context-fill`
+    ContextFill,
+    /// `context-stroke`
+    ContextStroke,
+}
+
+impl SVGPaintKind {
+    fn parse_ident(input: &mut Parser) -> Result<Self, ()> {
+        Ok(match_ignore_ascii_case! { input.expect_ident()?,
+            "none" => SVGPaintKind::None,
+            "context-fill" => SVGPaintKind::ContextFill,
+            "context-stroke" => SVGPaintKind::ContextStroke,
+            _ => return Err(())
+        })
+    }
+}
+
+impl Parse for SVGPaint {
+    fn parse(context: &ParserContext, input: &mut Parser) -> Result<Self, ()> {
+        if let Ok(url) = input.try(|i| SpecifiedUrl::parse(context, i)) {
+            let fallback = input.try(|i| CSSColor::parse(context, i));
+            Ok(SVGPaint {
+                kind: SVGPaintKind::PaintServer(url),
+                fallback: fallback.ok(),
+            })
+        } else if let Ok(kind) = input.try(SVGPaintKind::parse_ident) {
+            if kind == SVGPaintKind::None {
+                Ok(SVGPaint {
+                    kind: kind,
+                    fallback: None,
+                })
+            } else {
+                let fallback = input.try(|i| CSSColor::parse(context, i));
+                Ok(SVGPaint {
+                    kind: kind,
+                    fallback: fallback.ok(),
+                })
+            }
+        } else if let Ok(color) = input.try(|i| CSSColor::parse(context, i)) {
+            Ok(SVGPaint {
+                kind: SVGPaintKind::Color(color),
+                fallback: None,
+            })
+        } else {
+            Err(())
+        }
+    }
+}
+
+impl ToCss for SVGPaintKind {
+    fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
+        match *self {
+            SVGPaintKind::None => dest.write_str("none"),
+            SVGPaintKind::ContextStroke => dest.write_str("context-stroke"),
+            SVGPaintKind::ContextFill => dest.write_str("context-fill"),
+            SVGPaintKind::Color(ref color) => color.to_css(dest),
+            SVGPaintKind::PaintServer(ref server) => server.to_css(dest),
+        }
+    }
+}
+
+impl ToCss for SVGPaint {
+    fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
+        self.kind.to_css(dest)?;
+        if let Some(ref fallback) = self.fallback {
+            fallback.to_css(dest)?;
+        }
+        Ok(())
+    }
+}
+
+
+impl ToComputedValue for SVGPaint {
+    type ComputedValue = super::computed::SVGPaint;
+
+    #[inline]
+    fn to_computed_value(&self, context: &Context) -> Self::ComputedValue {
+        super::computed::SVGPaint {
+            kind: self.kind.to_computed_value(context),
+            fallback: self.fallback.as_ref().map(|f| f.to_computed_value(context))
+        }
+    }
+
+    #[inline]
+    fn from_computed_value(computed: &Self::ComputedValue) -> Self {
+        SVGPaint {
+            kind: ToComputedValue::from_computed_value(&computed.kind),
+            fallback: computed.fallback.as_ref().map(ToComputedValue::from_computed_value)
+        }
+    }
+}
+
+impl ToComputedValue for SVGPaintKind {
+    type ComputedValue = super::computed::SVGPaintKind;
+
+    #[inline]
+    fn to_computed_value(&self, context: &Context) -> Self::ComputedValue {
+        match *self {
+            SVGPaintKind::None => super::computed::SVGPaintKind::None,
+            SVGPaintKind::ContextStroke => super::computed::SVGPaintKind::ContextStroke,
+            SVGPaintKind::ContextFill => super::computed::SVGPaintKind::ContextFill,
+            SVGPaintKind::Color(ref color) => {
+                super::computed::SVGPaintKind::Color(color.to_computed_value(context))
+            }
+            SVGPaintKind::PaintServer(ref server) => {
+                super::computed::SVGPaintKind::PaintServer(server.to_computed_value(context))
+            }
+        }
+    }
+
+    #[inline]
+    fn from_computed_value(computed: &Self::ComputedValue) -> Self {
+        match *computed {
+            super::computed::SVGPaintKind::None => SVGPaintKind::None,
+            super::computed::SVGPaintKind::ContextStroke => SVGPaintKind::ContextStroke,
+            super::computed::SVGPaintKind::ContextFill => SVGPaintKind::ContextFill,
+            super::computed::SVGPaintKind::Color(ref color) => {
+                SVGPaintKind::Color(ToComputedValue::from_computed_value(color))
+            }
+            super::computed::SVGPaintKind::PaintServer(ref server) => {
+                SVGPaintKind::PaintServer(ToComputedValue::from_computed_value(server))
+            }
+        }
+    }
+}
+
+/// <length> | <percentage> | <number>
+pub type LoPOrNumber = Either<LengthOrPercentage, Number>;
+
+impl LoPOrNumber {
+    /// parse a <length-percentage> | <number> enforcing that the contents aren't negative
+    pub fn parse_non_negative(_: &ParserContext, input: &mut Parser) -> Result<Self, ()> {
+        if let Ok(lop) = input.try(LengthOrPercentage::parse_non_negative) {
+            Ok(Either::First(lop))
+        } else if let Ok(num) = input.try(Number::parse_non_negative) {
+            Ok(Either::Second(num))
+        } else {
+            Err(())
+        }
+    }
+}
 
 impl HasViewportPercentage for ClipRect {
     fn has_viewport_percentage(&self) -> bool {
         self.top.has_viewport_percentage() ||
         self.right.as_ref().map_or(false, |x| x.has_viewport_percentage()) ||
         self.bottom.as_ref().map_or(false, |x| x.has_viewport_percentage()) ||
         self.left.has_viewport_percentage()
     }
--- a/servo/components/style/values/specified/url.rs
+++ b/servo/components/style/values/specified/url.rs
@@ -1,16 +1,18 @@
 /* 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/. */
 
 //! Common handling for the specified value CSS url() values.
 
 use cssparser::{CssStringWriter, Parser};
 #[cfg(feature = "gecko")]
+use gecko_bindings::structs::ServoBundledURI;
+#[cfg(feature = "gecko")]
 use gecko_bindings::sugar::refptr::{GeckoArcPrincipal, GeckoArcURI};
 use parser::{Parse, ParserContext};
 #[cfg(feature = "gecko")]
 use parser::ParserContextExtraData;
 use servo_url::ServoUrl;
 use std::borrow::Cow;
 use std::fmt::{self, Write};
 use std::sync::Arc;
@@ -162,16 +164,34 @@ impl SpecifiedUrl {
     #[cfg(feature = "servo")]
     pub fn new_for_testing(url: &str) -> Self {
         SpecifiedUrl {
             original: Some(Arc::new(url.into())),
             resolved: ServoUrl::parse(url).ok(),
             extra_data: UrlExtraData {}
         }
     }
+
+    /// Create a bundled URI suitable for sending to Gecko
+    /// to be constructed into a css::URLValue
+    #[cfg(feature = "gecko")]
+    pub fn for_ffi(&self) -> Option<ServoBundledURI> {
+        let extra_data = self.extra_data();
+        let (ptr, len) = match self.as_slice_components() {
+            Ok(value) => value,
+            Err(_) => return None,
+        };
+        Some(ServoBundledURI {
+            mURLString: ptr,
+            mURLStringLength: len as u32,
+            mBaseURI: extra_data.base.get(),
+            mReferrer: extra_data.referrer.get(),
+            mPrincipal: extra_data.principal.get(),
+        })
+    }
 }
 
 impl PartialEq for SpecifiedUrl {
     fn eq(&self, other: &Self) -> bool {
         // TODO(emilio): maybe we care about equality of the specified values if
         // present? Seems not.
         self.resolved == other.resolved &&
             self.extra_data == other.extra_data