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 411579 f04ea50a6c1390dc9c5342dc78eca432cdb76d95
parent 411578 99de9f5450d819c78ceab58d3502c82efaffd231
child 411580 d112cf7b2b60e6244099dc3b599a2444ba0d1da3
push id62110
push userservo-vcs-sync@mozilla.com
push dateTue, 03 Apr 2018 23:08:57 +0000
treeherderautoland@f04ea50a6c13 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersemilio
milestone61.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 #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));