servo: Merge #16862 - stylo: Pass Cached ImageValue from stylo back to gecko (from CJKu:bug-1310885-part-6); r=heycam
authorcku <cku@mozilla.com>
Mon, 15 May 2017 18:04:59 -0500
changeset 358546 e608fc261bfb1effc1136a2676c719bb6a21a983
parent 358545 a3309e99de1573ef7e7cddbf9c0bcc23721e62ca
child 358547 424daa127a04c8295dee7ca320417ad8f79dfcbc
push id90352
push usercbook@mozilla.com
push dateTue, 16 May 2017 13:09:14 +0000
treeherdermozilla-inbound@8f89d291e303 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersheycam
bugs1310885
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 #16862 - stylo: Pass Cached ImageValue from stylo back to gecko (from CJKu:bug-1310885-part-6); r=heycam stylo: Pass Cached ImageValue from stylo back to gecko <!-- Please describe your changes on the following line: --> This is part 6 ~ part 9 patch in bug 1310885 gecko bug link: https://bugzilla.mozilla.org/show_bug.cgi?id=1310885 --- <!-- Thank you for contributing to Servo! Please replace each `[ ]` by `[X]` when the step is complete, and replace `__` with appropriate data: --> - [X] `./mach build -d` does not report any errors - [X] `./mach test-tidy` does not report any errors - [ ] These changes fix #__ (github issue number if applicable). <!-- Either: --> - [ ] There are tests for these changes OR - [ ] These changes do not require tests because _____ <!-- Also, please make sure that "Allow edits from maintainers" checkbox is checked, so that we can help you if you get stuck somewhere along the way.--> <!-- Pull requests that do not address these steps are welcome, but they will require additional verification as part of the review process. --> Source-Repo: https://github.com/servo/servo Source-Revision: 5bd6b92494d6b9527c1639c358eea3c4822bed84
servo/components/layout/construct.rs
servo/components/style/gecko/conversions.rs
servo/components/style/gecko/generated/bindings.rs
servo/components/style/gecko/url.rs
servo/components/style/properties/gecko.mako.rs
servo/components/style/properties/longhand/box.mako.rs
servo/components/style/properties/longhand/counters.mako.rs
servo/components/style/properties/longhand/inherited_svg.mako.rs
servo/components/style/properties/longhand/list.mako.rs
servo/components/style/properties/longhand/pointing.mako.rs
servo/components/style/properties/shorthand/list.mako.rs
servo/components/style/values/specified/image.rs
servo/ports/geckolib/glue.rs
servo/tests/unit/style/properties/serialization.rs
--- a/servo/components/layout/construct.rs
+++ b/servo/components/layout/construct.rs
@@ -48,16 +48,17 @@ use std::mem;
 use std::sync::Arc;
 use std::sync::atomic::Ordering;
 use style::computed_values::{caption_side, display, empty_cells, float, list_style_position};
 use style::computed_values::content::ContentItem;
 use style::computed_values::position;
 use style::context::SharedStyleContext;
 use style::logical_geometry::Direction;
 use style::properties::ServoComputedValues;
+use style::properties::longhands::list_style_image;
 use style::selector_parser::{PseudoElement, RestyleDamage};
 use style::servo::restyle_damage::{BUBBLE_ISIZES, RECONSTRUCT_FLOW};
 use style::values::Either;
 use table::TableFlow;
 use table_caption::TableCaptionFlow;
 use table_cell::TableCellFlow;
 use table_colgroup::TableColGroupFlow;
 use table_row::TableRowFlow;
@@ -1201,23 +1202,23 @@ impl<'a, ConcreteThreadSafeLayoutNode: T
     /// Builds a flow for a node with `display: list-item`. This yields a `ListItemFlow` with
     /// possibly other `BlockFlow`s or `InlineFlow`s underneath it.
     fn build_flow_for_list_item(&mut self,
                                 node: &ConcreteThreadSafeLayoutNode,
                                 flotation: float::T)
                                 -> ConstructionResult {
         let flotation = FloatKind::from_property(flotation);
         let marker_fragments = match node.style(self.style_context()).get_list().list_style_image {
-            Either::First(ref url_value) => {
+            list_style_image::computed_value::T(Either::First(ref url_value)) => {
                 let image_info = box ImageFragmentInfo::new(url_value.url().map(|u| u.clone()),
                                                             node,
                                                             &self.layout_context);
                 vec![Fragment::new(node, SpecificFragmentInfo::Image(image_info), self.layout_context)]
             }
-            Either::Second(_none) => {
+            list_style_image::computed_value::T(Either::Second(_none)) => {
                 match ListStyleTypeContent::from_list_style_type(node.style(self.style_context())
                                                                      .get_list()
                                                                      .list_style_type) {
                     ListStyleTypeContent::None => Vec::new(),
                     ListStyleTypeContent::StaticText(ch) => {
                         let text = format!("{}\u{a0}", ch);
                         let mut unscanned_marker_fragments = LinkedList::new();
                         unscanned_marker_fragments.push_back(Fragment::new(
--- a/servo/components/style/gecko/conversions.rs
+++ b/servo/components/style/gecko/conversions.rs
@@ -5,17 +5,17 @@
 //! This module contains conversion helpers between Servo and Gecko types
 //! Ideally, it would be in geckolib itself, but coherence
 //! forces us to keep the traits and implementations here
 
 #![allow(unsafe_code)]
 
 use app_units::Au;
 use gecko::values::{convert_rgba_to_nscolor, GeckoStyleCoordConvertible};
-use gecko_bindings::bindings::{Gecko_CreateGradient, Gecko_SetGradientImageValue, Gecko_SetUrlImageValue};
+use gecko_bindings::bindings::{Gecko_CreateGradient, Gecko_SetGradientImageValue, Gecko_SetLayerImageImageValue};
 use gecko_bindings::bindings::{Gecko_InitializeImageCropRect, Gecko_SetImageElement};
 use gecko_bindings::structs::{nsCSSUnit, nsStyleCoord_CalcValue, nsStyleImage};
 use gecko_bindings::structs::{nsresult, SheetType};
 use gecko_bindings::sugar::ns_style_coord::{CoordDataValue, CoordDataMut};
 use stylesheets::{Origin, RulesMutateError};
 use values::computed::{Angle, CalcLengthOrPercentage, Gradient, Image};
 use values::computed::{LengthOrPercentage, LengthOrPercentageOrAuto};
 use values::generics::image::{CompatMode, Image as GenericImage, GradientItem};
@@ -139,30 +139,30 @@ impl nsStyleImage {
     /// Set a given Servo `Image` value into this `nsStyleImage`.
     pub fn set(&mut self, image: Image, cacheable: &mut bool) {
         match image {
             GenericImage::Gradient(gradient) => {
                 self.set_gradient(gradient)
             },
             GenericImage::Url(ref url) => {
                 unsafe {
-                    Gecko_SetUrlImageValue(self, url.for_ffi());
+                    Gecko_SetLayerImageImageValue(self, url.image_value.clone().unwrap().get());
                     // We unfortunately must make any url() value uncacheable, since
                     // the applicable declarations cache is not per document, but
                     // global, and the imgRequestProxy objects we store in the style
                     // structs don't like to be tracked by more than one document.
                     //
                     // FIXME(emilio): With the scoped TLS thing this is no longer
                     // true, remove this line in a follow-up!
                     *cacheable = false;
                 }
             },
             GenericImage::Rect(ref image_rect) => {
                 unsafe {
-                    Gecko_SetUrlImageValue(self, image_rect.url.for_ffi());
+                    Gecko_SetLayerImageImageValue(self, image_rect.url.image_value.clone().unwrap().get());
                     Gecko_InitializeImageCropRect(self);
 
                     // We unfortunately must make any url() value uncacheable, since
                     // the applicable declarations cache is not per document, but
                     // global, and the imgRequestProxy objects we store in the style
                     // structs don't like to be tracked by more than one document.
                     //
                     // FIXME(emilio): With the scoped TLS thing this is no longer
--- a/servo/components/style/gecko/generated/bindings.rs
+++ b/servo/components/style/gecko/generated/bindings.rs
@@ -827,18 +827,21 @@ extern "C" {
 }
 extern "C" {
     pub fn Gecko_AddRefImageValueArbitraryThread(aPtr: *mut ImageValue);
 }
 extern "C" {
     pub fn Gecko_ReleaseImageValueArbitraryThread(aPtr: *mut ImageValue);
 }
 extern "C" {
-    pub fn Gecko_SetUrlImageValue(image: *mut nsStyleImage,
-                                  uri: ServoBundledURI);
+    pub fn Gecko_ImageValue_Create(uri: ServoBundledURI) -> *mut ImageValue;
+}
+extern "C" {
+    pub fn Gecko_SetLayerImageImageValue(image: *mut nsStyleImage,
+                                         imageValue: *mut ImageValue);
 }
 extern "C" {
     pub fn Gecko_SetImageElement(image: *mut nsStyleImage,
                                  atom: *mut nsIAtom);
 }
 extern "C" {
     pub fn Gecko_CopyImageValueFrom(image: *mut nsStyleImage,
                                     other: *const nsStyleImage);
@@ -850,38 +853,38 @@ extern "C" {
     pub fn Gecko_CreateGradient(shape: u8, size: u8, repeating: bool,
                                 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,
-                                   uri: ServoBundledURI);
+    pub fn Gecko_SetListStyleImageImageValue(style_struct: *mut nsStyleList,
+    	                                     imageValue: *mut ImageValue);
 }
 extern "C" {
     pub fn Gecko_CopyListStyleImageFrom(dest: *mut nsStyleList,
                                         src: *const nsStyleList);
 }
 extern "C" {
     pub fn Gecko_SetCursorArrayLength(ui: *mut nsStyleUserInterface,
                                       len: usize);
 }
 extern "C" {
-    pub fn Gecko_SetCursorImage(cursor: *mut nsCursorImage,
-                                uri: ServoBundledURI);
+    pub fn Gecko_SetCursorImageValue(cursor: *mut nsCursorImage,
+                                     imageValue: *mut ImageValue);
 }
 extern "C" {
     pub fn Gecko_CopyCursorArrayFrom(dest: *mut nsStyleUserInterface,
                                      src: *const nsStyleUserInterface);
 }
 extern "C" {
-    pub fn Gecko_SetContentDataImage(content_data: *mut nsStyleContentData,
-                                     uri: ServoBundledURI);
+    pub fn Gecko_SetContentDataImageValue(content_data: *mut nsStyleContentData,
+                                    imageValue: *mut ImageValue);
 }
 extern "C" {
     pub fn Gecko_SetContentDataArray(content_data: *mut nsStyleContentData,
                                      type_: nsStyleContentType, len: u32);
 }
 extern "C" {
     pub fn Gecko_GetNodeFlags(node: RawGeckoNodeBorrowed) -> u32;
 }
--- a/servo/components/style/gecko/url.rs
+++ b/servo/components/style/gecko/url.rs
@@ -1,16 +1,17 @@
 /* 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;
 use gecko_bindings::structs::{ServoBundledURI, URLExtraData};
+use gecko_bindings::structs::root::mozilla::css::ImageValue;
 use gecko_bindings::sugar::refptr::RefPtr;
 use parser::ParserContext;
 use std::borrow::Cow;
 use std::fmt::{self, Write};
 use style_traits::ToCss;
 use stylearc::Arc;
 
 /// A specified url() value for gecko. Gecko does not eagerly resolve SpecifiedUrls.
@@ -19,29 +20,34 @@ pub struct SpecifiedUrl {
     /// The URL in unresolved string form.
     ///
     /// Refcounted since cloning this should be cheap and data: uris can be
     /// really large.
     serialization: Arc<String>,
 
     /// The URL extra data.
     pub extra_data: RefPtr<URLExtraData>,
+
+    /// Cache ImageValue, if any, so that we can reuse it while rematching a
+    /// a property with this specified url value.
+    pub image_value: Option<RefPtr<ImageValue>>,
 }
 
 impl SpecifiedUrl {
     /// Try to parse a URL from a string value that is a valid CSS token for a
     /// URL.
     ///
     /// Returns `Err` in the case that extra_data is incomplete.
     pub fn parse_from_string<'a>(url: Cow<'a, str>,
                                  context: &ParserContext)
                                  -> Result<Self, ()> {
         Ok(SpecifiedUrl {
             serialization: Arc::new(url.into_owned()),
             extra_data: context.url_data.clone(),
+            image_value: None,
         })
     }
 
     /// Returns true if the URL is definitely invalid. We don't eagerly resolve
     /// URLs in gecko, so we just return false here.
     /// use its |resolved| status.
     pub fn is_invalid(&self) -> bool {
         false
@@ -71,16 +77,31 @@ impl SpecifiedUrl {
     pub fn for_ffi(&self) -> ServoBundledURI {
         let (ptr, len) = self.as_slice_components();
         ServoBundledURI {
             mURLString: ptr,
             mURLStringLength: len as u32,
             mExtraData: self.extra_data.get(),
         }
     }
+
+    /// Build and carry an image value on request.
+    pub fn build_image_value(&mut self) {
+        use gecko_bindings::bindings::Gecko_ImageValue_Create;
+
+        debug_assert_eq!(self.image_value, None);
+        self.image_value = {
+            unsafe {
+                let ptr = Gecko_ImageValue_Create(self.for_ffi());
+                // We do not expect Gecko_ImageValue_Create returns null.
+                debug_assert!(!ptr.is_null());
+                Some(RefPtr::from_addrefed(ptr))
+            }
+        }
+    }
 }
 
 impl ToCss for SpecifiedUrl {
     fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
         try!(dest.write_str("url(\""));
         try!(CssStringWriter::new(dest).write_str(&*self.serialization));
         dest.write_str("\")")
     }
--- a/servo/components/style/properties/gecko.mako.rs
+++ b/servo/components/style/properties/gecko.mako.rs
@@ -27,23 +27,23 @@ use gecko_bindings::bindings::Gecko_Copy
 use gecko_bindings::bindings::Gecko_CopyListStyleImageFrom;
 use gecko_bindings::bindings::Gecko_CopyListStyleTypeFrom;
 use gecko_bindings::bindings::Gecko_Destroy_nsStyleVariables;
 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_SetCursorImageValue;
 use gecko_bindings::bindings::Gecko_StyleTransition_SetUnsupportedProperty;
 use gecko_bindings::bindings::Gecko_NewCSSShadowArray;
 use gecko_bindings::bindings::Gecko_nsStyleFont_SetLang;
 use gecko_bindings::bindings::Gecko_nsStyleFont_CopyLangFrom;
-use gecko_bindings::bindings::Gecko_SetListStyleImage;
 use gecko_bindings::bindings::Gecko_SetListStyleImageNone;
+use gecko_bindings::bindings::Gecko_SetListStyleImageImageValue;
 use gecko_bindings::bindings::Gecko_SetListStyleType;
 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::{self, StyleComplexColor};
 use gecko_bindings::structs::nsCSSPropertyID;
 use gecko_bindings::structs::nsStyleVariables;
@@ -2959,25 +2959,25 @@ fn static_assert() {
 
 <%self:impl_trait style_struct_name="List"
                   skip_longhands="list-style-image list-style-type quotes -moz-image-region"
                   skip_additionals="*">
 
     pub fn set_list_style_image(&mut self, image: longhands::list_style_image::computed_value::T) {
         use values::Either;
         match image {
-            Either::Second(_none) => {
+            longhands::list_style_image::computed_value::T(Either::Second(_none)) => {
                 unsafe {
                     Gecko_SetListStyleImageNone(&mut self.gecko);
                 }
             }
-            Either::First(ref url) => {
+            longhands::list_style_image::computed_value::T(Either::First(ref url)) => {
                 unsafe {
-                    Gecko_SetListStyleImage(&mut self.gecko,
-                                            url.for_ffi());
+                    Gecko_SetListStyleImageImageValue(&mut self.gecko,
+                                                      url.image_value.clone().unwrap().get());
                 }
                 // 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.
             }
         }
     }
@@ -3990,20 +3990,21 @@ clip-path
                 Cursor::MozZoomOut => structs::NS_STYLE_CURSOR_ZOOM_OUT,
             }
         } as u8;
 
         unsafe {
             Gecko_SetCursorArrayLength(&mut self.gecko, v.images.len());
         }
         for i in 0..v.images.len() {
-            let image = &v.images[i];
             unsafe {
-                Gecko_SetCursorImage(&mut self.gecko.mCursorImages[i], image.url.for_ffi());
+                Gecko_SetCursorImageValue(&mut self.gecko.mCursorImages[i],
+                                          v.images[i].url.clone().image_value.unwrap().get());
             }
+
             // 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 Pointing struct
             // is an inherited struct.
         }
     }
 
     pub fn copy_cursor_from(&mut self, other: &Self) {
@@ -4155,18 +4156,21 @@ clip-path
                                                                     eStyleContentType_Counters, 3)
                             }
                             let mut array = unsafe { &mut **self.gecko.mContents[i].mContent.mCounters.as_mut() };
                             array[0].set_string(&name);
                             array[1].set_string(&sep);
                             // When we support <custom-ident> values for list-style-type this will need to be updated
                             array[2].set_atom_ident(style.to_css_string().into());
                         }
-                        ContentItem::Url(url) => {
-                            unsafe { bindings::Gecko_SetContentDataImage(&mut self.gecko.mContents[i], url.for_ffi()) }
+                        ContentItem::Url(ref url) => {
+                            unsafe {
+                                bindings::Gecko_SetContentDataImageValue(&mut self.gecko.mContents[i],
+                                    url.image_value.clone().unwrap().get())
+                            }
                         }
                     }
                 }
             }
         }
     }
 
     pub fn copy_content_from(&mut self, other: &Self) {
--- a/servo/components/style/properties/longhand/box.mako.rs
+++ b/servo/components/style/properties/longhand/box.mako.rs
@@ -2436,16 +2436,17 @@
                          gecko_ffi_name="mMozAppearance",
                          gecko_constant_prefix="NS_THEME",
                          products="gecko",
                          spec="Nonstandard (https://developer.mozilla.org/en-US/docs/Web/CSS/-moz-appearance)",
                          animation_value_type="none")}
 
 ${helpers.predefined_type("-moz-binding", "UrlOrNone", "Either::Second(None_)",
                           products="gecko",
+                          boxed="True" if product == "gecko" else "False",
                           animation_value_type="none",
                           gecko_ffi_name="mBinding",
                           spec="Nonstandard (https://developer.mozilla.org/en-US/docs/Web/CSS/-moz-binding)",
                           disable_when_testing="True")}
 
 ${helpers.single_keyword("-moz-orient",
                           "inline block horizontal vertical",
                           products="gecko",
--- a/servo/components/style/properties/longhand/counters.mako.rs
+++ b/servo/components/style/properties/longhand/counters.mako.rs
@@ -143,17 +143,18 @@
             return Ok(SpecifiedValue::normal)
         }
         if input.try(|input| input.expect_ident_matching("none")).is_ok() {
             return Ok(SpecifiedValue::none)
         }
         let mut content = vec![];
         loop {
             % if product == "gecko":
-                if let Ok(url) = input.try(|i| SpecifiedUrl::parse(context, i)) {
+                if let Ok(mut url) = input.try(|i| SpecifiedUrl::parse(context, i)) {
+                    url.build_image_value();
                     content.push(ContentItem::Url(url));
                     continue;
                 }
             % endif
             match input.next() {
                 Ok(Token::QuotedString(value)) => {
                     content.push(ContentItem::String(value.into_owned()))
                 }
--- a/servo/components/style/properties/longhand/inherited_svg.mako.rs
+++ b/servo/components/style/properties/longhand/inherited_svg.mako.rs
@@ -115,26 +115,29 @@
                          products="gecko",
                          gecko_enum_prefix="StyleFillRule",
                          gecko_inexhaustive=True,
                          animation_value_type="none",
                          spec="https://www.w3.org/TR/SVG11/masking.html#ClipRuleProperty")}
 
 ${helpers.predefined_type("marker-start", "UrlOrNone", "Either::Second(None_)",
                           products="gecko",
+                          boxed="True" if product == "gecko" else "False",
                           animation_value_type="none",
                           spec="https://www.w3.org/TR/SVG2/painting.html#VertexMarkerProperties")}
 
 ${helpers.predefined_type("marker-mid", "UrlOrNone", "Either::Second(None_)",
                           products="gecko",
+                          boxed="True" if product == "gecko" else "False",
                           animation_value_type="none",
                           spec="https://www.w3.org/TR/SVG2/painting.html#VertexMarkerProperties")}
 
 ${helpers.predefined_type("marker-end", "UrlOrNone", "Either::Second(None_)",
                           products="gecko",
+                          boxed="True" if product == "gecko" else "False",
                           animation_value_type="none",
                           spec="https://www.w3.org/TR/SVG2/painting.html#VertexMarkerProperties")}
 
 <%helpers:longhand name="paint-order"
                    animation_value_type="none"
                    products="gecko"
                    spec="https://www.w3.org/TR/SVG2/painting.html#PaintOrder">
 
--- a/servo/components/style/properties/longhand/list.mako.rs
+++ b/servo/components/style/properties/longhand/list.mako.rs
@@ -32,19 +32,65 @@
     korean-hanja-formal korean-hanja-informal simp-chinese-informal simp-chinese-formal
     trad-chinese-informal trad-chinese-formal ethiopic-numeric upper-roman lower-roman
     """,
     gecko_constant_prefix="NS_STYLE_LIST_STYLE",
     needs_conversion="True",
     animation_value_type="none",
     spec="https://drafts.csswg.org/css-lists/#propdef-list-style-type")}
 
-${helpers.predefined_type("list-style-image", "UrlOrNone", "Either::Second(None_)",
-                          initial_specified_value="Either::Second(None_)", animation_value_type="none",
-                          spec="https://drafts.csswg.org/css-lists/#propdef-list-style-image")}
+<%helpers:longhand name="list-style-image" animation_value_type="none"
+                   boxed="${product == 'gecko'}"
+                   spec="https://drafts.csswg.org/css-lists/#propdef-list-style-image">
+    use std::fmt;
+    use values::HasViewportPercentage;
+    use values::computed::ComputedValueAsSpecified;
+    use values::specified::UrlOrNone;
+    pub use self::computed_value::T as SpecifiedValue;
+    use style_traits::ToCss;
+
+    pub mod computed_value {
+        use values::specified::UrlOrNone;
+
+        #[derive(Debug, Clone, PartialEq)]
+        #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
+        pub struct T(pub UrlOrNone);
+    }
+
+
+    impl ComputedValueAsSpecified for SpecifiedValue {}
+    no_viewport_percentage!(SpecifiedValue);
+
+    impl ToCss for SpecifiedValue {
+        fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
+            self.0.to_css(dest)
+        }
+    }
+
+    #[inline]
+    pub fn get_initial_value() -> computed_value::T {
+        computed_value::T(Either::Second(None_))
+    }
+    #[inline]
+    pub fn get_initial_specified_value() -> SpecifiedValue {
+        SpecifiedValue(Either::Second(None_))
+    }
+    pub fn parse(context: &ParserContext, input: &mut Parser) -> Result<SpecifiedValue,()> {
+        % if product == "gecko":
+        let mut value = input.try(|input| UrlOrNone::parse(context, input))?;
+        if let Either::First(ref mut url) = value {
+            url.build_image_value();
+        }
+        % else :
+        let value = input.try(|input| UrlOrNone::parse(context, input))?;
+        % endif
+
+        return Ok(SpecifiedValue(value));
+    }
+</%helpers:longhand>
 
 <%helpers:longhand name="quotes" animation_value_type="none"
                    spec="https://drafts.csswg.org/css-content/#propdef-quotes">
     use cssparser::Token;
     use std::borrow::Cow;
     use std::fmt;
     use style_traits::ToCss;
     use values::computed::ComputedValueAsSpecified;
--- a/servo/components/style/properties/longhand/pointing.mako.rs
+++ b/servo/components/style/properties/longhand/pointing.mako.rs
@@ -126,17 +126,20 @@
     }
 
     /// cursor: [<url> [<number> <number>]?]# [auto | default | ...]
     #[cfg(feature = "gecko")]
     pub fn parse(context: &ParserContext, input: &mut Parser) -> Result<SpecifiedValue, ()> {
         let mut images = vec![];
         loop {
             match input.try(|input| parse_image(context, input)) {
-                Ok(image) => images.push(image),
+                Ok(mut image) => {
+                    image.url.build_image_value();
+                    images.push(image)
+                }
                 Err(()) => break,
             }
             try!(input.expect_comma());
         }
 
         Ok(computed_value::T {
             images: images,
             keyword: try!(computed_value::Keyword::parse(context, input)),
--- a/servo/components/style/properties/shorthand/list.mako.rs
+++ b/servo/components/style/properties/shorthand/list.mako.rs
@@ -55,38 +55,38 @@
 
         // If there are two `none`s, then we can't have a type or image; if there is one `none`,
         // then we can't have both a type *and* an image; if there is no `none` then we're fine as
         // long as we parsed something.
         match (any, nones, list_style_type, image) {
             (true, 2, None, None) => {
                 Ok(Longhands {
                     list_style_position: position,
-                    list_style_image: Either::Second(None_),
+                    list_style_image: list_style_image::SpecifiedValue(Either::Second(None_)),
                     list_style_type: list_style_type::SpecifiedValue::none,
                 })
             }
             (true, 1, None, Some(image)) => {
                 Ok(Longhands {
                     list_style_position: position,
                     list_style_image: image,
                     list_style_type: list_style_type::SpecifiedValue::none,
                 })
             }
             (true, 1, Some(list_style_type), None) => {
                 Ok(Longhands {
                     list_style_position: position,
-                    list_style_image: Either::Second(None_),
+                    list_style_image: list_style_image::SpecifiedValue(Either::Second(None_)),
                     list_style_type: list_style_type,
                 })
             }
             (true, 1, None, None) => {
                 Ok(Longhands {
                     list_style_position: position,
-                    list_style_image: Either::Second(None_),
+                    list_style_image: list_style_image::SpecifiedValue(Either::Second(None_)),
                     list_style_type: list_style_type::SpecifiedValue::none,
                 })
             }
             (true, 0, list_style_type, image) => {
                 Ok(Longhands {
                     list_style_position: position,
                     list_style_image: unwrap_or_initial!(list_style_image, image),
                     list_style_type: unwrap_or_initial!(list_style_type),
--- a/servo/components/style/values/specified/image.rs
+++ b/servo/components/style/values/specified/image.rs
@@ -77,24 +77,44 @@ pub type GradientItem = GenericGradientI
 pub type ColorStop = GenericColorStop<CSSColor, LengthOrPercentage>;
 
 /// Specified values for `moz-image-rect`
 /// -moz-image-rect(<uri>, top, right, bottom, left);
 pub type ImageRect = GenericImageRect<NumberOrPercentage>;
 
 impl Parse for Image {
     fn parse(context: &ParserContext, input: &mut Parser) -> Result<Image, ()> {
-        if let Ok(url) = input.try(|input| SpecifiedUrl::parse(context, input)) {
-            return Ok(GenericImage::Url(url));
+        #[cfg(feature = "gecko")]
+        {
+          if let Ok(mut url) = input.try(|input| SpecifiedUrl::parse(context, input)) {
+              url.build_image_value();
+              return Ok(GenericImage::Url(url));
+          }
+        }
+        #[cfg(feature = "servo")]
+        {
+          if let Ok(url) = input.try(|input| SpecifiedUrl::parse(context, input)) {
+              return Ok(GenericImage::Url(url));
+          }
         }
         if let Ok(gradient) = input.try(|i| Gradient::parse(context, i)) {
             return Ok(GenericImage::Gradient(gradient));
         }
-        if let Ok(image_rect) = input.try(|input| ImageRect::parse(context, input)) {
-            return Ok(GenericImage::Rect(image_rect));
+        #[cfg(feature = "gecko")]
+        {
+            if let Ok(mut image_rect) = input.try(|input| ImageRect::parse(context, input)) {
+                image_rect.url.build_image_value();
+                return Ok(GenericImage::Rect(image_rect));
+            }
+        }
+        #[cfg(feature = "servo")]
+        {
+            if let Ok(image_rect) = input.try(|input| ImageRect::parse(context, input)) {
+                return Ok(GenericImage::Rect(image_rect));
+            }
         }
 
         Ok(GenericImage::Element(Image::parse_element(input)?))
     }
 }
 
 impl Image {
     /// Creates an already specified image value from an already resolved URL
--- a/servo/ports/geckolib/glue.rs
+++ b/servo/ports/geckolib/glue.rs
@@ -1840,17 +1840,18 @@ pub extern "C" fn Servo_DeclarationBlock
     use style::values::specified::url::SpecifiedUrl;
 
     let url_data = unsafe { RefPtr::from_ptr_ref(&raw_extra_data) };
     let string = unsafe { (*value).to_string() };
     let error_reporter = RustLogReporter;
     let context = ParserContext::new(Origin::Author, url_data, &error_reporter,
                                      Some(CssRuleType::Style), PARSING_MODE_DEFAULT,
                                      QuirksMode::NoQuirks);
-    if let Ok(url) = SpecifiedUrl::parse_from_string(string.into(), &context) {
+    if let Ok(mut url) = SpecifiedUrl::parse_from_string(string.into(), &context) {
+        url.build_image_value();
         let decl = PropertyDeclaration::BackgroundImage(BackgroundImage(
             vec![Either::Second(Image::Url(url))]
         ));
         write_locked_arc(declarations, |decls: &mut PropertyDeclarationBlock| {
             decls.push(decl, Importance::Normal);
         })
     }
 }
--- a/servo/tests/unit/style/properties/serialization.rs
+++ b/servo/tests/unit/style/properties/serialization.rs
@@ -488,32 +488,38 @@ mod shorthand_serialization {
 
             let serialization = block.to_css_string();
 
             assert_eq!(serialization, "border: 4px solid;");
         }
     }
 
     mod list_style {
+        use style::properties::longhands::list_style_image::SpecifiedValue as ListStyleImage;
         use style::properties::longhands::list_style_position::SpecifiedValue as ListStylePosition;
         use style::properties::longhands::list_style_type::SpecifiedValue as ListStyleType;
         use style::values::Either;
         use super::*;
 
         #[test]
         fn list_style_should_show_all_properties_when_values_are_set() {
             let mut properties = Vec::new();
 
             let position = ListStylePosition::inside;
-            let image = Either::First(
-                SpecifiedUrl::new_for_testing("http://servo/test.png"));
+            let image =
+                ListStyleImage(Either::First(SpecifiedUrl::new_for_testing("http://servo/test.png")));
             let style_type = ListStyleType::disc;
 
             properties.push(PropertyDeclaration::ListStylePosition(position));
+
+            #[cfg(feature = "gecko")]
+            properties.push(PropertyDeclaration::ListStyleImage(Box::new(image)));
+            #[cfg(not(feature = "gecko"))]
             properties.push(PropertyDeclaration::ListStyleImage(image));
+
             properties.push(PropertyDeclaration::ListStyleType(style_type));
 
             let serialization = shorthand_properties_to_string(properties);
             assert_eq!(serialization, "list-style: inside url(\"http://servo/test.png\") disc;");
         }
     }
 
     mod outline {