servo: Merge #20482 - Implement a URL-generic type for ListStyleImage (from brainlessdeveloper:list-style-image-computed); r=emilio
authorFausto Núñez Alberro <fausto.nunez@mailbox.org>
Tue, 03 Apr 2018 18:12:13 -0400
changeset 777153 f04ea50a6c1390dc9c5342dc78eca432cdb76d95
parent 777152 99de9f5450d819c78ceab58d3502c82efaffd231
child 777154 d112cf7b2b60e6244099dc3b599a2444ba0d1da3
push id105088
push userjdescottes@mozilla.com
push dateWed, 04 Apr 2018 10:12:11 +0000
reviewersemilio
milestone61.0a1
servo: Merge #20482 - Implement a URL-generic type for ListStyleImage (from brainlessdeveloper:list-style-image-computed); r=emilio <!-- Please describe your changes on the following line: --> This should fix the following two "expected to fail" tests described in https://github.com/servo/servo/issues/18015: - getComputedStyle(elem) for url() listStyleImage uses the resolved URL and elem.style uses the original URL - getComputedStyle(elem) for url() listStyle uses the resolved URL and elem.style uses the original URL I updated the test failure expectations by removing the corresponding `.ini` file. --- <!-- 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 - [x] These changes fix #18015 (github issue number if applicable). <!-- Either: --> - [x] 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: d744e35d38ce84f7209eb1fc41d2d9f38545d0de
servo/components/layout/construct.rs
servo/components/style/gecko/conversions.rs
servo/components/style/properties/gecko.mako.rs
servo/components/style/properties/helpers/animated_properties.mako.rs
servo/components/style/properties/longhand/box.mako.rs
servo/components/style/properties/longhand/inherited_svg.mako.rs
servo/components/style/properties/longhand/list.mako.rs
servo/components/style/properties/shorthand/inherited_svg.mako.rs
servo/components/style/properties/shorthand/list.mako.rs
servo/components/style/values/animated/mod.rs
servo/components/style/values/computed/basic_shape.rs
servo/components/style/values/computed/image.rs
servo/components/style/values/computed/length.rs
servo/components/style/values/computed/list.rs
servo/components/style/values/computed/mod.rs
servo/components/style/values/computed/svg.rs
servo/components/style/values/computed/url.rs
servo/components/style/values/generics/mod.rs
servo/components/style/values/generics/url.rs
servo/components/style/values/specified/list.rs
servo/components/style/values/specified/mod.rs
servo/components/style/values/specified/svg.rs
servo/components/style/values/specified/url.rs
servo/tests/unit/style/properties/serialization.rs
--- a/servo/components/layout/construct.rs
+++ b/servo/components/layout/construct.rs
@@ -47,21 +47,20 @@ use style::computed_values::display::T a
 use style::computed_values::empty_cells::T as EmptyCells;
 use style::computed_values::float::T as Float;
 use style::computed_values::list_style_position::T as ListStylePosition;
 use style::computed_values::position::T as Position;
 use style::context::SharedStyleContext;
 use style::dom::TElement;
 use style::logical_geometry::Direction;
 use style::properties::ComputedValues;
-use style::properties::longhands::list_style_image;
 use style::selector_parser::{PseudoElement, RestyleDamage};
 use style::servo::restyle_damage::ServoRestyleDamage;
-use style::values::Either;
 use style::values::computed::counters::ContentItem;
+use style::values::generics::url::UrlOrNone as ImageUrlOrNone;
 use table::TableFlow;
 use table_caption::TableCaptionFlow;
 use table_cell::TableCellFlow;
 use table_colgroup::TableColGroupFlow;
 use table_row::TableRowFlow;
 use table_rowgroup::TableRowGroupFlow;
 use table_wrapper::TableWrapperFlow;
 use text::TextRunScanner;
@@ -1273,23 +1272,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)
                                 -> ConstructionResult {
         let flotation = FloatKind::from_property(flotation);
         let marker_fragments = match node.style(self.style_context()).get_list().list_style_image {
-            list_style_image::computed_value::T(Either::First(ref url_value)) => {
+            ImageUrlOrNone::Url(ref url_value) => {
                 let image_info = Box::new(ImageFragmentInfo::new(
                     url_value.url().map(|u| u.clone()), node, &self.layout_context
                 ));
                 vec![Fragment::new(node, SpecificFragmentInfo::Image(image_info), self.layout_context)]
             }
-            list_style_image::computed_value::T(Either::Second(_none)) => {
+            ImageUrlOrNone::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
@@ -12,18 +12,19 @@ use app_units::Au;
 use gecko::values::{convert_rgba_to_nscolor, GeckoStyleCoordConvertible};
 use gecko_bindings::bindings::{Gecko_CreateGradient, Gecko_SetGradientImageValue, Gecko_SetLayerImageImageValue};
 use gecko_bindings::bindings::{Gecko_InitializeImageCropRect, Gecko_SetImageElement};
 use gecko_bindings::structs::{self, nsCSSUnit, nsStyleCoord_CalcValue};
 use gecko_bindings::structs::{nsStyleImage, nsresult, SheetType};
 use gecko_bindings::sugar::ns_style_coord::{CoordDataValue, CoordData, CoordDataMut};
 use std::f32::consts::PI;
 use stylesheets::{Origin, RulesMutateError};
-use values::computed::{Angle, CalcLengthOrPercentage, ComputedImageUrl, Gradient, Image};
+use values::computed::{Angle, CalcLengthOrPercentage, Gradient, Image};
 use values::computed::{Integer, LengthOrPercentage, LengthOrPercentageOrAuto, Percentage, TextAlign};
+use values::computed::url::ComputedImageUrl;
 use values::generics::box_::VerticalAlign;
 use values::generics::grid::{TrackListValue, TrackSize};
 use values::generics::image::{CompatMode, Image as GenericImage, GradientItem};
 use values::generics::rect::Rect;
 
 impl From<CalcLengthOrPercentage> for nsStyleCoord_CalcValue {
     fn from(other: CalcLengthOrPercentage) -> nsStyleCoord_CalcValue {
         let has_percentage = other.percentage.is_some();
@@ -588,21 +589,21 @@ pub mod basic_shape {
 
     use gecko::values::GeckoStyleCoordConvertible;
     use gecko_bindings::structs;
     use gecko_bindings::structs::{StyleBasicShape, StyleBasicShapeType, StyleFillRule};
     use gecko_bindings::structs::{StyleGeometryBox, StyleShapeSource, StyleShapeSourceType};
     use gecko_bindings::structs::{nsStyleCoord, nsStyleCorners};
     use gecko_bindings::sugar::ns_style_coord::{CoordDataMut, CoordDataValue};
     use std::borrow::Borrow;
-    use values::computed::ComputedUrl;
     use values::computed::basic_shape::{BasicShape, ClippingShape, FloatAreaShape, ShapeRadius};
     use values::computed::border::{BorderCornerRadius, BorderRadius};
     use values::computed::length::LengthOrPercentage;
     use values::computed::position;
+    use values::computed::url::ComputedUrl;
     use values::generics::basic_shape::{BasicShape as GenericBasicShape, InsetRect, Polygon};
     use values::generics::basic_shape::{Circle, Ellipse, FillRule};
     use values::generics::basic_shape::{GeometryBox, ShapeBox, ShapeSource};
     use values::generics::border::BorderRadius as GenericBorderRadius;
     use values::generics::rect::Rect;
 
     impl StyleShapeSource {
         /// Convert StyleShapeSource to ShapeSource except URL and Image
--- a/servo/components/style/properties/gecko.mako.rs
+++ b/servo/components/style/properties/gecko.mako.rs
@@ -61,16 +61,17 @@ use values::{self, CustomIdent, Either, 
 use values::computed::{NonNegativeLength, ToComputedValue, Percentage};
 use values::computed::font::{FontSize, SingleFontFamily};
 use values::computed::effects::{BoxShadow, Filter, SimpleShadow};
 use values::computed::outline::OutlineStyle;
 use values::generics::column::ColumnCount;
 use values::generics::position::ZIndex;
 use values::generics::text::MozTabSize;
 use values::generics::transform::TransformStyle;
+use values::generics::url::UrlOrNone;
 use computed_values::border_style;
 
 pub mod style_structs {
     % for style_struct in data.style_structs:
     pub use super::${style_struct.gecko_struct_name} as ${style_struct.name};
     % endfor
 }
 
@@ -932,20 +933,20 @@ def set_gecko_property(ffi_name, expr):
         BorderCornerRadius::new(width, height)
     }
 </%def>
 
 <%def name="impl_css_url(ident, gecko_ffi_name)">
     #[allow(non_snake_case)]
     pub fn set_${ident}(&mut self, v: longhands::${ident}::computed_value::T) {
         match v {
-            Either::First(url) => {
+            UrlOrNone::Url(ref url) => {
                 self.gecko.${gecko_ffi_name}.set_move(url.url_value.clone())
             }
-            Either::Second(_none) => {
+            UrlOrNone::None => {
                 unsafe {
                     self.gecko.${gecko_ffi_name}.clear();
                 }
             }
         }
     }
     #[allow(non_snake_case)]
     pub fn copy_${ident}_from(&mut self, other: &Self) {
@@ -956,25 +957,24 @@ def set_gecko_property(ffi_name, expr):
     #[allow(non_snake_case)]
     pub fn reset_${ident}(&mut self, other: &Self) {
         self.copy_${ident}_from(other)
     }
 
     #[allow(non_snake_case)]
     pub fn clone_${ident}(&self) -> longhands::${ident}::computed_value::T {
         use values::specified::url::SpecifiedUrl;
-        use values::None_;
 
         if self.gecko.${gecko_ffi_name}.mRawPtr.is_null() {
-            Either::Second(None_)
+            UrlOrNone::none()
         } else {
             unsafe {
                 let ref gecko_url_value = *self.gecko.${gecko_ffi_name}.mRawPtr;
-                Either::First(SpecifiedUrl::from_url_value_data(&gecko_url_value._base)
-                        .expect("${gecko_ffi_name} could not convert to SpecifiedUrl"))
+                UrlOrNone::Url(SpecifiedUrl::from_url_value_data(&gecko_url_value._base)
+                               .expect("${gecko_ffi_name} could not convert to SpecifiedUrl"))
             }
         }
     }
 </%def>
 
 <%
 transform_functions = [
     ("Matrix3D", "matrix3d", ["number"] * 16),
@@ -1464,17 +1464,17 @@ impl Clone for ${style_struct.gecko_stru
         "Position": impl_position,
         "RGBAColor": impl_rgba_color,
         "SVGLength": impl_svg_length,
         "SVGOpacity": impl_svg_opacity,
         "SVGPaint": impl_svg_paint,
         "SVGWidth": impl_svg_length,
         "Transform": impl_transform,
         "TransformOrigin": impl_transform_origin,
-        "UrlOrNone": impl_css_url,
+        "url::UrlOrNone": impl_css_url,
     }
 
     def longhand_method(longhand):
         args = dict(ident=longhand.ident, gecko_ffi_name=longhand.gecko_ffi_name)
 
         # get the method and pass additional keyword or type-specific arguments
         if longhand.logical:
             method = impl_logical
@@ -4056,24 +4056,23 @@ fn static_assert() {
     <% impl_simple_image_array_property("attachment", "background", "mImage", "mAttachment", "Background") %>
     <% impl_simple_image_array_property("blend_mode", "background", "mImage", "mBlendMode", "Background") %>
 </%self:impl_trait>
 
 <%self:impl_trait style_struct_name="List"
                   skip_longhands="list-style-image list-style-type quotes -moz-image-region">
 
     pub fn set_list_style_image(&mut self, image: longhands::list_style_image::computed_value::T) {
-        use values::Either;
         match image {
-            longhands::list_style_image::computed_value::T(Either::Second(_none)) => {
+            UrlOrNone::None => {
                 unsafe {
                     Gecko_SetListStyleImageNone(&mut self.gecko);
                 }
             }
-            longhands::list_style_image::computed_value::T(Either::First(ref url)) => {
+            UrlOrNone::Url(ref url) => {
                 unsafe {
                     Gecko_SetListStyleImageImageValue(&mut self.gecko, url.image_value.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.
             }
@@ -4085,30 +4084,26 @@ fn static_assert() {
     }
 
     pub fn reset_list_style_image(&mut self, other: &Self) {
         self.copy_list_style_image_from(other)
     }
 
     pub fn clone_list_style_image(&self) -> longhands::list_style_image::computed_value::T {
         use values::specified::url::SpecifiedImageUrl;
-        use values::{Either, None_};
-
-        longhands::list_style_image::computed_value::T(
-            match self.gecko.mListStyleImage.mRawPtr.is_null() {
-                true => Either::Second(None_),
-                false => {
-                    unsafe {
-                        let ref gecko_image_request = *self.gecko.mListStyleImage.mRawPtr;
-                        Either::First(SpecifiedImageUrl::from_image_request(gecko_image_request)
-                                      .expect("mListStyleImage could not convert to SpecifiedImageUrl"))
-                    }
-                }
-            }
-        )
+
+        if self.gecko.mListStyleImage.mRawPtr.is_null() {
+            return UrlOrNone::None;
+        }
+
+        unsafe {
+            let ref gecko_image_request = *self.gecko.mListStyleImage.mRawPtr;
+            UrlOrNone::Url(SpecifiedImageUrl::from_image_request(gecko_image_request)
+                           .expect("mListStyleImage could not convert to SpecifiedImageUrl"))
+        }
     }
 
     pub fn set_list_style_type(&mut self, v: longhands::list_style_type::computed_value::T, device: &Device) {
         use gecko_bindings::bindings::Gecko_SetCounterStyleToString;
         use nsstring::{nsACString, nsCStr};
         use self::longhands::list_style_type::computed_value::T;
         match v {
             T::CounterStyle(s) => s.to_gecko_value(&mut self.gecko.mCounterStyle, device),
--- a/servo/components/style/properties/helpers/animated_properties.mako.rs
+++ b/servo/components/style/properties/helpers/animated_properties.mako.rs
@@ -31,28 +31,29 @@ use std::mem::{self, ManuallyDrop};
 use style_traits::ParseError;
 use super::ComputedValues;
 use values::{CSSFloat, CustomIdent, Either};
 use values::animated::{Animate, Procedure, ToAnimatedValue, ToAnimatedZero};
 use values::animated::color::RGBA as AnimatedRGBA;
 use values::animated::effects::Filter as AnimatedFilter;
 use values::animated::effects::FilterList as AnimatedFilterList;
 use values::computed::{Angle, CalcLengthOrPercentage};
-use values::computed::{ClipRect, Context, ComputedUrl};
+use values::computed::{ClipRect, Context};
 use values::computed::{Length, LengthOrPercentage, LengthOrPercentageOrAuto};
 use values::computed::{LengthOrPercentageOrNone, MaxLength};
 use values::computed::{NonNegativeNumber, Number, NumberOrPercentage, Percentage};
 use values::computed::length::NonNegativeLengthOrPercentage;
 use values::computed::ToComputedValue;
 use values::computed::transform::{DirectionVector, Matrix, Matrix3D};
 use values::computed::transform::TransformOperation as ComputedTransformOperation;
 use values::computed::transform::Transform as ComputedTransform;
 use values::computed::transform::Rotate as ComputedRotate;
 use values::computed::transform::Translate as ComputedTranslate;
 use values::computed::transform::Scale as ComputedScale;
+use values::computed::url::ComputedUrl;
 use values::generics::transform::{self, Rotate, Translate, Scale, Transform, TransformOperation};
 use values::distance::{ComputeSquaredDistance, SquaredDistance};
 use values::generics::font::{FontSettings as GenericFontSettings, FontTag, VariationValue};
 use values::computed::font::FontVariationSettings;
 use values::generics::effects::Filter;
 use values::generics::position as generic_position;
 use values::generics::svg::{SVGLength,  SvgLengthOrPercentageOrNumber, SVGPaint};
 use values::generics::svg::{SVGPaintKind, SVGStrokeDashArray, SVGOpacity};
--- a/servo/components/style/properties/longhand/box.mako.rs
+++ b/servo/components/style/properties/longhand/box.mako.rs
@@ -608,17 +608,17 @@
                             -moz-window-titlebar-maximized
                          """,
                          gecko_ffi_name="mAppearance",
                          gecko_constant_prefix="ThemeWidgetType_NS_THEME",
                          products="gecko",
                          spec="Nonstandard (https://developer.mozilla.org/en-US/docs/Web/CSS/-moz-appearance)",
                          animation_value_type="discrete")}
 
-${helpers.predefined_type("-moz-binding", "UrlOrNone", "Either::Second(None_)",
+${helpers.predefined_type("-moz-binding", "url::UrlOrNone", "computed::url::UrlOrNone::none()",
                           products="gecko",
                           animation_value_type="none",
                           gecko_ffi_name="mBinding",
                           spec="Nonstandard (https://developer.mozilla.org/en-US/docs/Web/CSS/-moz-binding)")}
 
 ${helpers.single_keyword("-moz-orient",
                           "inline block horizontal vertical",
                           products="gecko",
--- a/servo/components/style/properties/longhand/inherited_svg.mako.rs
+++ b/servo/components/style/properties/longhand/inherited_svg.mako.rs
@@ -107,27 +107,27 @@
 
 // Section 14 - Clipping, Masking and Compositing
 ${helpers.single_keyword("clip-rule", "nonzero evenodd",
                          products="gecko",
                          gecko_enum_prefix="StyleFillRule",
                          animation_value_type="discrete",
                          spec="https://www.w3.org/TR/SVG11/masking.html#ClipRuleProperty")}
 
-${helpers.predefined_type("marker-start", "UrlOrNone", "Either::Second(None_)",
+${helpers.predefined_type("marker-start", "url::UrlOrNone", "computed::url::UrlOrNone::none()",
                           products="gecko",
                           animation_value_type="discrete",
                           spec="https://www.w3.org/TR/SVG2/painting.html#VertexMarkerProperties")}
 
-${helpers.predefined_type("marker-mid", "UrlOrNone", "Either::Second(None_)",
+${helpers.predefined_type("marker-mid", "url::UrlOrNone", "computed::url::UrlOrNone::none()",
                           products="gecko",
                           animation_value_type="discrete",
                           spec="https://www.w3.org/TR/SVG2/painting.html#VertexMarkerProperties")}
 
-${helpers.predefined_type("marker-end", "UrlOrNone", "Either::Second(None_)",
+${helpers.predefined_type("marker-end", "url::UrlOrNone", "computed::url::UrlOrNone::none()",
                           products="gecko",
                           animation_value_type="discrete",
                           spec="https://www.w3.org/TR/SVG2/painting.html#VertexMarkerProperties")}
 
 ${helpers.predefined_type("paint-order", "SVGPaintOrder", "computed::SVGPaintOrder::normal()",
                           products="gecko",
                           animation_value_type="discrete",
                           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
@@ -36,19 +36,19 @@
         animation_value_type="discrete",
         boxed=True,
         spec="https://drafts.csswg.org/css-lists/#propdef-list-style-type",
         servo_restyle_damage="rebuild_and_reflow",
     )}
 % endif
 
 ${helpers.predefined_type("list-style-image",
-                          "ListStyleImage",
-                          initial_value="specified::ListStyleImage::none()",
-                          initial_specified_value="specified::ListStyleImage::none()",
+                          "url::ImageUrlOrNone",
+                          initial_value="computed::url::ImageUrlOrNone::none()",
+                          initial_specified_value="specified::url::ImageUrlOrNone::none()",
                           animation_value_type="discrete",
                           spec="https://drafts.csswg.org/css-lists/#propdef-list-style-image",
                           servo_restyle_damage="rebuild_and_reflow")}
 
 ${helpers.predefined_type("quotes",
                           "Quotes",
                           "computed::Quotes::get_initial_value()",
                           animation_value_type="discrete",
--- a/servo/components/style/properties/shorthand/inherited_svg.mako.rs
+++ b/servo/components/style/properties/shorthand/inherited_svg.mako.rs
@@ -2,17 +2,17 @@
  * 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;
+    use values::specified::url::UrlOrNone;
 
     pub fn parse_value<'i, 't>(
         context: &ParserContext,
         input: &mut Parser<'i, 't>,
     ) -> Result<Longhands, ParseError<'i>> {
         use parser::Parse;
         let url = UrlOrNone::parse(context, input)?;
 
--- a/servo/components/style/properties/shorthand/list.mako.rs
+++ b/servo/components/style/properties/shorthand/list.mako.rs
@@ -4,17 +4,17 @@
 
 <%namespace name="helpers" file="/helpers.mako.rs" />
 
 <%helpers:shorthand name="list-style"
                     sub_properties="list-style-position list-style-image list-style-type"
                     derive_serialize="True"
                     spec="https://drafts.csswg.org/css-lists/#propdef-list-style">
     use properties::longhands::{list_style_image, list_style_position, list_style_type};
-    use values::{Either, None_};
+    use values::specified::url::ImageUrlOrNone;
 
     pub fn parse_value<'i, 't>(
         context: &ParserContext,
         input: &mut Parser<'i, 't>,
     ) -> Result<Longhands, ParseError<'i>> {
         // `none` is ambiguous until we've finished parsing the shorthands, so we count the number
         // of times we see it.
         let mut nones = 0u8;
@@ -71,38 +71,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(expanded! {
                     list_style_position: position,
-                    list_style_image: list_style_image::SpecifiedValue(Either::Second(None_)),
+                    list_style_image: ImageUrlOrNone::none(),
                     list_style_type: list_style_type_none(),
                 })
             }
             (true, 1, None, Some(image)) => {
                 Ok(expanded! {
                     list_style_position: position,
                     list_style_image: image,
                     list_style_type: list_style_type_none(),
                 })
             }
             (true, 1, Some(list_style_type), None) => {
                 Ok(expanded! {
                     list_style_position: position,
-                    list_style_image: list_style_image::SpecifiedValue(Either::Second(None_)),
+                    list_style_image: ImageUrlOrNone::none(),
                     list_style_type: list_style_type,
                 })
             }
             (true, 1, None, None) => {
                 Ok(expanded! {
                     list_style_position: position,
-                    list_style_image: list_style_image::SpecifiedValue(Either::Second(None_)),
+                    list_style_image: ImageUrlOrNone::none(),
                     list_style_type: list_style_type_none(),
                 })
             }
             (true, 0, list_style_type, image) => {
                 Ok(expanded! {
                     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/animated/mod.rs
+++ b/servo/components/style/values/animated/mod.rs
@@ -8,20 +8,20 @@
 //! computed values and need yet another intermediate representation. This
 //! module's raison d'ĂȘtre is to ultimately contain all these types.
 
 use app_units::Au;
 use euclid::{Point2D, Size2D};
 use smallvec::SmallVec;
 use values::computed::Angle as ComputedAngle;
 use values::computed::BorderCornerRadius as ComputedBorderCornerRadius;
-#[cfg(feature = "servo")]
-use values::computed::ComputedUrl;
 use values::computed::MaxLength as ComputedMaxLength;
 use values::computed::MozLength as ComputedMozLength;
+#[cfg(feature = "servo")]
+use values::computed::url::ComputedUrl;
 use values::specified::url::SpecifiedUrl;
 
 pub mod color;
 pub mod effects;
 
 /// Animate from one value to another.
 ///
 /// This trait is derivable with `#[derive(Animate)]`. The derived
--- a/servo/components/style/values/computed/basic_shape.rs
+++ b/servo/components/style/values/computed/basic_shape.rs
@@ -4,17 +4,18 @@
 
 //! CSS handling for the computed value of
 //! [`basic-shape`][basic-shape]s
 //!
 //! [basic-shape]: https://drafts.csswg.org/css-shapes/#typedef-basic-shape
 
 use std::fmt::{self, Write};
 use style_traits::{CssWriter, ToCss};
-use values::computed::{LengthOrPercentage, ComputedUrl, Image};
+use values::computed::{LengthOrPercentage, Image};
+use values::computed::url::ComputedUrl;
 use values::generics::basic_shape::{BasicShape as GenericBasicShape};
 use values::generics::basic_shape::{Circle as GenericCircle, ClippingShape as GenericClippingShape};
 use values::generics::basic_shape::{Ellipse as GenericEllipse, FloatAreaShape as GenericFloatAreaShape};
 use values::generics::basic_shape::{InsetRect as GenericInsetRect, ShapeRadius as GenericShapeRadius};
 
 /// A computed clipping shape.
 pub type ClippingShape = GenericClippingShape<BasicShape, ComputedUrl>;
 
--- a/servo/components/style/values/computed/image.rs
+++ b/servo/components/style/values/computed/image.rs
@@ -7,21 +7,22 @@
 //!
 //! [image]: https://drafts.csswg.org/css-images/#image-values
 
 use cssparser::RGBA;
 use std::f32::consts::PI;
 use std::fmt::{self, Write};
 use style_traits::{CssWriter, ToCss};
 use values::{Either, None_};
-use values::computed::{Angle, ComputedImageUrl, Context};
+use values::computed::{Angle, Context};
 use values::computed::{Length, LengthOrPercentage, NumberOrPercentage, ToComputedValue};
 #[cfg(feature = "gecko")]
 use values::computed::Percentage;
 use values::computed::position::Position;
+use values::computed::url::ComputedImageUrl;
 use values::generics::image::{CompatMode, ColorStop as GenericColorStop, EndingShape as GenericEndingShape};
 use values::generics::image::{Gradient as GenericGradient, GradientItem as GenericGradientItem};
 use values::generics::image::{Image as GenericImage, GradientKind as GenericGradientKind};
 use values::generics::image::{LineDirection as GenericLineDirection, MozImageRect as GenericMozImageRect};
 use values::specified::image::LineDirection as SpecifiedLineDirection;
 use values::specified::position::{X, Y};
 
 /// A computed image layer.
--- a/servo/components/style/values/computed/length.rs
+++ b/servo/components/style/values/computed/length.rs
@@ -16,17 +16,18 @@ use super::{Number, ToComputedValue, Con
 use values::{Auto, CSSFloat, Either, Normal, specified};
 use values::animated::{Animate, Procedure, ToAnimatedValue, ToAnimatedZero};
 use values::distance::{ComputeSquaredDistance, SquaredDistance};
 use values::generics::NonNegative;
 use values::specified::length::{AbsoluteLength, FontBaseSize, FontRelativeLength};
 use values::specified::length::ViewportPercentageLength;
 
 pub use super::image::Image;
-pub use values::specified::{Angle, BorderStyle, Time, UrlOrNone};
+pub use values::specified::url::UrlOrNone;
+pub use values::specified::{Angle, BorderStyle, Time};
 
 impl ToComputedValue for specified::NoCalcLength {
     type ComputedValue = CSSPixelLength;
 
     #[inline]
     fn to_computed_value(&self, context: &Context) -> Self::ComputedValue {
         match *self {
             specified::NoCalcLength::Absolute(length) =>
--- a/servo/components/style/values/computed/list.rs
+++ b/servo/components/style/values/computed/list.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/. */
 
 //! `list` computed values.
 
-pub use values::specified::list::{ListStyleImage, Quotes};
+pub use values::specified::list::Quotes;
 #[cfg(feature = "gecko")]
 pub use values::specified::list::ListStyleType;
 
 impl Quotes {
     /// Initial value for `quotes`.
     ///
     /// FIXME(emilio): This should ideally not allocate.
     #[inline]
--- a/servo/components/style/values/computed/mod.rs
+++ b/servo/components/style/values/computed/mod.rs
@@ -57,17 +57,17 @@ pub use self::inherited_box::{Orientatio
 pub use self::gecko::ScrollSnapPoint;
 pub use self::rect::LengthOrNumberRect;
 pub use super::{Auto, Either, None_};
 pub use super::specified::{BorderStyle, TextDecorationLine};
 pub use self::length::{CalcLengthOrPercentage, Length, LengthOrNumber, LengthOrPercentage};
 pub use self::length::{LengthOrPercentageOrAuto, LengthOrPercentageOrNone, MaxLength, MozLength};
 pub use self::length::{CSSPixelLength, ExtremumLength, NonNegativeLength};
 pub use self::length::{NonNegativeLengthOrPercentage, NonNegativeLengthOrPercentageOrAuto};
-pub use self::list::{ListStyleImage, Quotes};
+pub use self::list::Quotes;
 #[cfg(feature = "gecko")]
 pub use self::list::ListStyleType;
 pub use self::outline::OutlineStyle;
 pub use self::percentage::Percentage;
 pub use self::pointing::{CaretColor, Cursor};
 #[cfg(feature = "gecko")]
 pub use self::pointing::CursorImage;
 pub use self::position::{GridAutoFlow, GridTemplateAreas, Position, ZIndex};
@@ -76,17 +76,16 @@ pub use self::svg::{SVGPaintOrder, SVGSt
 pub use self::svg::MozContextProperties;
 pub use self::table::XSpan;
 pub use self::text::{InitialLetter, LetterSpacing, LineHeight, MozTabSize};
 pub use self::text::{TextAlign, TextEmphasisPosition, TextEmphasisStyle, TextOverflow, WordSpacing};
 pub use self::time::Time;
 pub use self::transform::{Rotate, Scale, TimingFunction, Transform, TransformOperation};
 pub use self::transform::{TransformOrigin, TransformStyle, Translate};
 pub use self::ui::MozForceBrokenImageIcon;
-pub use self::url::{ComputedUrl, ComputedImageUrl};
 
 #[cfg(feature = "gecko")]
 pub mod align;
 pub mod angle;
 pub mod background;
 pub mod basic_shape;
 pub mod border;
 #[path = "box.rs"]
@@ -109,24 +108,17 @@ pub mod pointing;
 pub mod position;
 pub mod rect;
 pub mod svg;
 pub mod table;
 pub mod text;
 pub mod time;
 pub mod transform;
 pub mod ui;
-
-/// Common handling for the computed value CSS url() values.
-pub mod url {
-#[cfg(feature = "servo")]
-pub use ::servo::url::{ComputedUrl, ComputedImageUrl};
-#[cfg(feature = "gecko")]
-pub use ::gecko::url::{ComputedUrl, ComputedImageUrl};
-}
+pub mod url;
 
 /// A `Context` is all the data a specified value could ever need to compute
 /// itself and be transformed to a computed value.
 pub struct Context<'a> {
     /// Whether the current element is the root element.
     pub is_root_element: bool,
 
     /// Values accessed through this need to be in the properties "computed
@@ -634,14 +626,8 @@ impl ClipRectOrAuto {
     /// Check if it is auto
     pub fn is_auto(&self) -> bool {
         match *self {
             Either::Second(_) => true,
             _ => false
         }
     }
 }
-
-/// <url> | <none>
-pub type UrlOrNone = Either<ComputedUrl, None_>;
-
-/// <url> | <none> for image
-pub type ImageUrlOrNone = Either<ComputedImageUrl, None_>;
--- a/servo/components/style/values/computed/svg.rs
+++ b/servo/components/style/values/computed/svg.rs
@@ -1,19 +1,20 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 //! Computed types for SVG properties.
 
 use app_units::Au;
 use values::RGBA;
-use values::computed::{ComputedUrl, LengthOrPercentage, NonNegativeLength};
+use values::computed::{LengthOrPercentage, NonNegativeLength};
 use values::computed::{NonNegativeNumber, NonNegativeLengthOrPercentage, Number};
 use values::computed::Opacity;
+use values::computed::url::ComputedUrl;
 use values::generics::svg as generic;
 
 pub use values::specified::SVGPaintOrder;
 
 pub use values::specified::MozContextProperties;
 
 /// Computed SVG Paint value
 pub type SVGPaint = generic::SVGPaint<RGBA, ComputedUrl>;
new file mode 100644
--- /dev/null
+++ b/servo/components/style/values/computed/url.rs
@@ -0,0 +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 computed value CSS url() values.
+
+use values::generics::url::UrlOrNone as GenericUrlOrNone;
+
+#[cfg(feature = "servo")]
+pub use ::servo::url::{ComputedUrl, ComputedImageUrl};
+#[cfg(feature = "gecko")]
+pub use ::gecko::url::{ComputedUrl, ComputedImageUrl};
+
+/// Computed <url> | <none>
+pub type UrlOrNone = GenericUrlOrNone<ComputedUrl>;
+
+/// Computed image <url> | <none>
+pub type ImageUrlOrNone = GenericUrlOrNone<ComputedImageUrl>;
--- a/servo/components/style/values/generics/mod.rs
+++ b/servo/components/style/values/generics/mod.rs
@@ -27,16 +27,17 @@ pub mod grid;
 pub mod image;
 pub mod pointing;
 pub mod position;
 pub mod rect;
 pub mod size;
 pub mod svg;
 pub mod text;
 pub mod transform;
+pub mod url;
 
 // https://drafts.csswg.org/css-counter-styles/#typedef-symbols-type
 #[allow(missing_docs)]
 #[cfg_attr(feature = "servo", derive(Deserialize, Serialize))]
 #[derive(Clone, Copy, Debug, Eq, MallocSizeOf, Parse, PartialEq)]
 #[derive(ToComputedValue, ToCss)]
 pub enum SymbolsType {
     Cyclic,
new file mode 100644
--- /dev/null
+++ b/servo/components/style/values/generics/url.rs
@@ -0,0 +1,39 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+//! Generic types for url properties.
+
+use cssparser::Parser;
+use parser::{Parse, ParserContext};
+use style_traits::ParseError;
+
+/// An image url or none, used for example in list-style-image
+#[derive(Animate, Clone, ComputeSquaredDistance, Debug, MallocSizeOf)]
+#[derive(PartialEq, ToAnimatedValue, ToAnimatedZero, ToComputedValue, ToCss)]
+pub enum UrlOrNone<Url> {
+    /// `none`
+    None,
+    /// `A URL`
+    Url(Url),
+}
+
+impl<Url> UrlOrNone<Url> {
+    /// Initial "none" value for properties such as `list-style-image`
+    pub fn none() -> Self {
+        UrlOrNone::None
+    }
+}
+
+impl<Url> Parse for UrlOrNone<Url> where Url: Parse {
+    fn parse<'i, 't>(
+        context: &ParserContext,
+        input: &mut Parser<'i, 't>,
+    ) -> Result<UrlOrNone<Url>, ParseError<'i>> {
+        if let Ok(url) = input.try(|input| Url::parse(context, input)) {
+            return Ok(UrlOrNone::Url(url));
+        }
+        input.expect_ident_matching("none")?;
+        Ok(UrlOrNone::None)
+    }
+}
--- a/servo/components/style/values/specified/list.rs
+++ b/servo/components/style/values/specified/list.rs
@@ -3,22 +3,20 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 //! `list` specified values.
 
 use cssparser::{Parser, Token};
 use parser::{Parse, ParserContext};
 use std::fmt::{self, Write};
 use style_traits::{CssWriter, ParseError, StyleParseErrorKind, ToCss};
-use values::{Either, None_};
 #[cfg(feature = "gecko")]
 use values::CustomIdent;
 #[cfg(feature = "gecko")]
 use values::generics::CounterStyleOrNone;
-use values::specified::ImageUrlOrNone;
 
 /// Specified and computed `list-style-type` property.
 #[cfg(feature = "gecko")]
 #[derive(Clone, Debug, Eq, MallocSizeOf, PartialEq, ToComputedValue, ToCss)]
 pub enum ListStyleType {
     /// <counter-style> | none
     CounterStyle(CounterStyleOrNone),
     /// <string>
@@ -68,41 +66,16 @@ impl Parse for ListStyleType {
         if let Ok(style) = input.try(|i| CounterStyleOrNone::parse(context, i)) {
             return Ok(ListStyleType::CounterStyle(style))
         }
 
         Ok(ListStyleType::String(input.expect_string()?.as_ref().to_owned()))
     }
 }
 
-/// Specified and computed `list-style-image` property.
-#[derive(Clone, Debug, MallocSizeOf, PartialEq, ToCss)]
-pub struct ListStyleImage(pub ImageUrlOrNone);
-
-// FIXME(nox): This is wrong, there are different types for specified
-// and computed URLs in Servo.
-trivial_to_computed_value!(ListStyleImage);
-
-impl ListStyleImage {
-    /// Initial specified value for `list-style-image`.
-    #[inline]
-    pub fn none() -> ListStyleImage {
-        ListStyleImage(Either::Second(None_))
-    }
-}
-
-impl Parse for ListStyleImage {
-    fn parse<'i, 't>(
-        context: &ParserContext,
-        input: &mut Parser<'i, 't>,
-    ) -> Result<ListStyleImage, ParseError<'i>> {
-        ImageUrlOrNone::parse(context, input).map(ListStyleImage)
-    }
-}
-
 /// Specified and computed `quote` property.
 ///
 /// FIXME(emilio): It's a shame that this allocates all the time it's computed,
 /// probably should just be refcounted.
 #[derive(Clone, Debug, MallocSizeOf, PartialEq, ToComputedValue)]
 pub struct Quotes(pub Box<[(Box<str>, Box<str>)]>);
 
 impl ToCss for Quotes {
--- a/servo/components/style/values/specified/mod.rs
+++ b/servo/components/style/values/specified/mod.rs
@@ -6,22 +6,21 @@
 //!
 //! TODO(emilio): Enhance docs.
 
 use Prefix;
 use context::QuirksMode;
 use cssparser::{Parser, Token, serialize_identifier};
 use num_traits::One;
 use parser::{ParserContext, Parse};
-use self::url::{SpecifiedImageUrl, SpecifiedUrl};
 use std::f32;
 use std::fmt::{self, Write};
 use style_traits::{CssWriter, ParseError, StyleParseErrorKind, ToCss};
 use style_traits::values::specified::AllowedNumericType;
-use super::{Auto, CSSFloat, CSSInteger, Either, None_};
+use super::{Auto, CSSFloat, CSSInteger, Either};
 use super::computed::{Context, ToComputedValue};
 use super::generics::{GreaterThanOrEqualToOne, NonNegative};
 use super::generics::grid::{GridLine as GenericGridLine, TrackBreadth as GenericTrackBreadth};
 use super::generics::grid::{TrackSize as GenericTrackSize, TrackList as GenericTrackList};
 use values::serialize_atom_identifier;
 use values::specified::calc::CalcNode;
 
 pub use properties::animated_properties::TransitionProperty;
@@ -51,17 +50,17 @@ pub use self::image::{ColorStop, EndingS
 pub use self::image::{GradientItem, GradientKind, Image, ImageLayer, MozImageRect};
 pub use self::inherited_box::ImageOrientation;
 pub use self::length::{AbsoluteLength, CalcLengthOrPercentage, CharacterWidth};
 pub use self::length::{FontRelativeLength, Length, LengthOrNumber};
 pub use self::length::{LengthOrPercentage, LengthOrPercentageOrAuto};
 pub use self::length::{LengthOrPercentageOrNone, MaxLength, MozLength};
 pub use self::length::{NoCalcLength, ViewportPercentageLength};
 pub use self::length::{NonNegativeLengthOrPercentage, NonNegativeLengthOrPercentageOrAuto};
-pub use self::list::{ListStyleImage, Quotes};
+pub use self::list::Quotes;
 #[cfg(feature = "gecko")]
 pub use self::list::ListStyleType;
 pub use self::outline::OutlineStyle;
 pub use self::rect::LengthOrNumberRect;
 pub use self::percentage::Percentage;
 pub use self::pointing::{CaretColor, Cursor};
 #[cfg(feature = "gecko")]
 pub use self::pointing::CursorImage;
@@ -109,24 +108,17 @@ pub mod position;
 pub mod rect;
 pub mod source_size_list;
 pub mod svg;
 pub mod table;
 pub mod text;
 pub mod time;
 pub mod transform;
 pub mod ui;
-
-/// Common handling for the specified value CSS url() values.
-pub mod url {
-#[cfg(feature = "servo")]
-pub use ::servo::url::{SpecifiedUrl, SpecifiedImageUrl};
-#[cfg(feature = "gecko")]
-pub use ::gecko::url::{SpecifiedUrl, SpecifiedImageUrl};
-}
+pub mod url;
 
 /// Parse a `<number>` value, with a given clamping mode.
 fn parse_number_with_clamping_mode<'i, 't>(
     context: &ParserContext,
     input: &mut Parser<'i, 't>,
     clamping_mode: AllowedNumericType,
 ) -> Result<Number, ParseError<'i>> {
     let location = input.current_source_location();
@@ -514,22 +506,16 @@ pub type PositiveInteger = GreaterThanOr
 
 impl Parse for PositiveInteger {
     #[inline]
     fn parse<'i, 't>(context: &ParserContext, input: &mut Parser<'i, 't>) -> Result<Self, ParseError<'i>> {
         Integer::parse_positive(context, input).map(GreaterThanOrEqualToOne::<Integer>)
     }
 }
 
-#[allow(missing_docs)]
-pub type UrlOrNone = Either<SpecifiedUrl, None_>;
-
-/// The specified value of a `<url>` for image or `none`.
-pub type ImageUrlOrNone = Either<SpecifiedImageUrl, None_>;
-
 /// The specified value of a grid `<track-breadth>`
 pub type TrackBreadth = GenericTrackBreadth<LengthOrPercentage>;
 
 /// The specified value of a grid `<track-size>`
 pub type TrackSize = GenericTrackSize<LengthOrPercentage>;
 
 /// The specified value of a grid `<track-list>`
 /// (could also be `<auto-track-list>` or `<explicit-track-list>`)
--- a/servo/components/style/values/specified/svg.rs
+++ b/servo/components/style/values/specified/svg.rs
@@ -7,18 +7,19 @@
 use cssparser::Parser;
 use parser::{Parse, ParserContext};
 use std::fmt::{self, Write};
 use style_traits::{CommaWithSpace, CssWriter, ParseError, Separator};
 use style_traits::{StyleParseErrorKind, ToCss};
 use values::CustomIdent;
 use values::generics::svg as generic;
 use values::specified::{LengthOrPercentage, NonNegativeLengthOrPercentage, NonNegativeNumber};
-use values::specified::{Number, Opacity, SpecifiedUrl};
+use values::specified::{Number, Opacity};
 use values::specified::color::RGBAColor;
+use values::specified::url::SpecifiedUrl;
 
 /// Specified SVG Paint value
 pub type SVGPaint = generic::SVGPaint<RGBAColor, SpecifiedUrl>;
 
 
 /// Specified SVG Paint Kind value
 pub type SVGPaintKind = generic::SVGPaintKind<RGBAColor, SpecifiedUrl>;
 
new file mode 100644
--- /dev/null
+++ b/servo/components/style/values/specified/url.rs
@@ -0,0 +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 values::generics::url::UrlOrNone as GenericUrlOrNone;
+
+#[cfg(feature = "servo")]
+pub use ::servo::url::{SpecifiedUrl, SpecifiedImageUrl};
+#[cfg(feature = "gecko")]
+pub use ::gecko::url::{SpecifiedUrl, SpecifiedImageUrl};
+
+/// Specified <url> | <none>
+pub type UrlOrNone = GenericUrlOrNone<SpecifiedUrl>;
+
+/// Specified image <url> | <none>
+pub type ImageUrlOrNone = GenericUrlOrNone<SpecifiedImageUrl>;
--- a/servo/tests/unit/style/properties/serialization.rs
+++ b/servo/tests/unit/style/properties/serialization.rs
@@ -488,29 +488,27 @@ 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 style::values::generics::url::UrlOrNone as ImageUrlOrNone;
         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 =
-                ListStyleImage(Either::First(SpecifiedUrl::new_for_testing("http://servo/test.png")));
+            let image = ImageUrlOrNone::Url(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));