servo: Merge #14359 - Reuse box-shadow code for drop-shadow filter (from shinglyu:dropshadow-refactor); r=Manishearth
authorShing Lyu <shing.lyu@gmail.com>
Tue, 13 Dec 2016 18:50:30 -0800
changeset 340329 e07acb058a3141b0470a51bf9f6dff1a962747b0
parent 340328 92646471f722ff59c5664c03324f17a40c6fc6b1
child 340330 6f7d4c8793785affeae2b8764059111ddded5a3e
push id31307
push usergszorc@mozilla.com
push dateSat, 04 Feb 2017 00:59:06 +0000
treeherdermozilla-central@94079d43835f [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersManishearth
servo: Merge #14359 - Reuse box-shadow code for drop-shadow filter (from shinglyu:dropshadow-refactor); r=Manishearth <!-- Please describe your changes on the following line: --> Followup for #14218. Extracted the `Shadow` struct and methods into `values::*`, and make `box-shadow` and `drop-shadow` share the new `Shadow` type. The `ToCss` trait is not reused because they behave different in the two properties. --- <!-- 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 #14219 (github issue number if applicable). <!-- Either: --> - [x] There are tests for these changes OR - [ ] These changes do not require tests because _____ <!-- 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: cd1d5f1cf359051eb76aa81ec3de48b84b0dab81
servo/components/style/properties/gecko.mako.rs
servo/components/style/properties/longhand/effects.mako.rs
servo/components/style/values/computed/mod.rs
servo/components/style/values/specified/mod.rs
--- a/servo/components/style/properties/gecko.mako.rs
+++ b/servo/components/style/properties/gecko.mako.rs
@@ -1804,37 +1804,37 @@ fn static_assert() {
                                                   CoordDataValue::Factor(factor),
                                                   gecko_filter),
                 Saturate(factor)   => fill_filter(NS_STYLE_FILTER_SATURATE,
                                                   CoordDataValue::Factor(factor),
                                                   gecko_filter),
                 Sepia(factor)      => fill_filter(NS_STYLE_FILTER_SEPIA,
                                                   CoordDataValue::Factor(factor),
                                                   gecko_filter),
-                DropShadow(offset_x, offset_y, blur_radius, ref color) => {
+                DropShadow(shadow) => {
                     gecko_filter.mType = NS_STYLE_FILTER_DROP_SHADOW;
 
                     fn init_shadow(filter: &mut nsStyleFilter) -> &mut nsCSSShadowArray {
                         unsafe {
                             let ref mut union = filter.__bindgen_anon_1;
                             let mut shadow_array: &mut *mut nsCSSShadowArray = union.mDropShadow.as_mut();
                             *shadow_array = Gecko_NewCSSShadowArray(1);
 
                             &mut **shadow_array
                         }
                     }
 
                     let mut gecko_shadow = init_shadow(gecko_filter);
-                    gecko_shadow.mArray[0].mXOffset = offset_x.0;
-                    gecko_shadow.mArray[0].mYOffset = offset_y.0;
-                    gecko_shadow.mArray[0].mRadius = blur_radius.0;
+                    gecko_shadow.mArray[0].mXOffset = shadow.offset_x.0;
+                    gecko_shadow.mArray[0].mYOffset = shadow.offset_y.0;
+                    gecko_shadow.mArray[0].mRadius = shadow.blur_radius.0;
                     // mSpread is not supported in the spec, so we leave it as 0
                     gecko_shadow.mArray[0].mInset = false; // Not supported in spec level 1
 
-                    gecko_shadow.mArray[0].mColor = match *color {
+                    gecko_shadow.mArray[0].mColor = match shadow.color {
                         Color::RGBA(rgba) => {
                             gecko_shadow.mArray[0].mHasColor = true;
                             convert_rgba_to_nscolor(&rgba)
                         },
                         // TODO handle currentColor
                         // https://bugzilla.mozilla.org/show_bug.cgi?id=760345
                         Color::CurrentColor => 0,
                     };
--- a/servo/components/style/properties/longhand/effects.mako.rs
+++ b/servo/components/style/properties/longhand/effects.mako.rs
@@ -13,35 +13,17 @@
                           animatable=True)}
 
 <%helpers:vector_longhand name="box-shadow" allow_empty="True" animatable="True">
     use cssparser;
     use std::fmt;
     use style_traits::ToCss;
     use values::HasViewportPercentage;
 
-    #[derive(Debug, Clone, PartialEq)]
-    #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
-    pub struct SpecifiedValue {
-        pub offset_x: specified::Length,
-        pub offset_y: specified::Length,
-        pub blur_radius: specified::Length,
-        pub spread_radius: specified::Length,
-        pub color: Option<specified::CSSColor>,
-        pub inset: bool,
-    }
-
-    impl HasViewportPercentage for SpecifiedValue {
-        fn has_viewport_percentage(&self) -> bool {
-            self.offset_x.has_viewport_percentage() ||
-            self.offset_y.has_viewport_percentage() ||
-            self.blur_radius.has_viewport_percentage() ||
-            self.spread_radius.has_viewport_percentage()
-        }
-    }
+    pub type SpecifiedValue = specified::Shadow;
 
     impl ToCss for SpecifiedValue {
         fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
             if self.inset {
                 try!(dest.write_str("inset "));
             }
             try!(self.blur_radius.to_css(dest));
             try!(dest.write_str(" "));
@@ -58,27 +40,19 @@
             Ok(())
         }
     }
 
     pub mod computed_value {
         use app_units::Au;
         use std::fmt;
         use values::computed;
+        use values::computed::Shadow;
 
-        #[derive(Clone, PartialEq, Copy, Debug)]
-        #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
-        pub struct T {
-            pub offset_x: Au,
-            pub offset_y: Au,
-            pub blur_radius: Au,
-            pub spread_radius: Au,
-            pub color: computed::CSSColor,
-            pub inset: bool,
-        }
+        pub type T = Shadow;
     }
 
     impl ToCss for computed_value::T {
         fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
             if self.inset {
                 try!(dest.write_str("inset "));
             }
             try!(self.blur_radius.to_css(dest));
@@ -89,105 +63,18 @@
             try!(dest.write_str(" "));
             try!(self.offset_y.to_css(dest));
             try!(dest.write_str(" "));
             try!(self.color.to_css(dest));
             Ok(())
         }
     }
 
-    impl ToComputedValue for SpecifiedValue {
-        type ComputedValue = computed_value::T;
-
-        #[inline]
-        fn to_computed_value(&self, context: &Context) -> computed_value::T {
-            computed_value::T {
-                offset_x: self.offset_x.to_computed_value(context),
-                offset_y: self.offset_y.to_computed_value(context),
-                blur_radius: self.blur_radius.to_computed_value(context),
-                spread_radius: self.spread_radius.to_computed_value(context),
-                color: self.color
-                            .as_ref()
-                            .map(|color| color.parsed)
-                            .unwrap_or(cssparser::Color::CurrentColor),
-                inset: self.inset,
-            }
-        }
-
-        #[inline]
-        fn from_computed_value(computed: &computed_value::T) -> Self {
-            SpecifiedValue {
-                offset_x: ToComputedValue::from_computed_value(&computed.offset_x),
-                offset_y: ToComputedValue::from_computed_value(&computed.offset_y),
-                blur_radius: ToComputedValue::from_computed_value(&computed.blur_radius),
-                spread_radius: ToComputedValue::from_computed_value(&computed.spread_radius),
-                color: Some(ToComputedValue::from_computed_value(&computed.color)),
-                inset: computed.inset,
-            }
-        }
-    }
-
-    pub fn parse(context: &ParserContext, input: &mut Parser) -> Result<SpecifiedValue, ()> {
-        use app_units::Au;
-        let mut lengths = [specified::Length::Absolute(Au(0)); 4];
-        let mut lengths_parsed = false;
-        let mut color = None;
-        let mut inset = false;
-
-        loop {
-            if !inset {
-                if input.try(|input| input.expect_ident_matching("inset")).is_ok() {
-                    inset = true;
-                    continue
-                }
-            }
-            if !lengths_parsed {
-                if let Ok(value) = input.try(|i| specified::Length::parse(context, i)) {
-                    lengths[0] = value;
-                    let mut length_parsed_count = 1;
-                    while length_parsed_count < 4 {
-                        if let Ok(value) = input.try(|i| specified::Length::parse(context, i)) {
-                            lengths[length_parsed_count] = value
-                        } else {
-                            break
-                        }
-                        length_parsed_count += 1;
-                    }
-
-                    // The first two lengths must be specified.
-                    if length_parsed_count < 2 {
-                        return Err(())
-                    }
-
-                    lengths_parsed = true;
-                    continue
-                }
-            }
-            if color.is_none() {
-                if let Ok(value) = input.try(|i| specified::CSSColor::parse(context, i)) {
-                    color = Some(value);
-                    continue
-                }
-            }
-            break
-        }
-
-        // Lengths must be specified.
-        if !lengths_parsed {
-            return Err(())
-        }
-
-        Ok(SpecifiedValue {
-            offset_x: lengths[0],
-            offset_y: lengths[1],
-            blur_radius: lengths[2],
-            spread_radius: lengths[3],
-            color: color,
-            inset: inset,
-        })
+    pub fn parse(context: &ParserContext, input: &mut Parser) -> Result<specified::Shadow, ()> {
+        specified::Shadow::parse(context, input, false)
     }
 </%helpers:vector_longhand>
 
 // FIXME: This prop should be animatable
 <%helpers:longhand name="clip" products="servo" animatable="False">
     use std::fmt;
     use style_traits::ToCss;
     use values::HasViewportPercentage;
@@ -401,17 +288,17 @@
 
 // FIXME: This prop should be animatable
 <%helpers:longhand name="filter" animatable="False">
     //pub use self::computed_value::T as SpecifiedValue;
     use cssparser;
     use std::fmt;
     use style_traits::{self, ToCss};
     use values::{CSSFloat, HasViewportPercentage};
-    use values::specified::{Angle, CSSColor, Length};
+    use values::specified::{Angle, CSSColor, Length, Shadow};
 
     impl HasViewportPercentage for SpecifiedValue {
         fn has_viewport_percentage(&self) -> bool {
             let &SpecifiedValue(ref vec) = self;
             vec.iter().any(|ref x| x.has_viewport_percentage())
         }
     }
 
@@ -437,40 +324,40 @@
         Contrast(CSSFloat),
         Grayscale(CSSFloat),
         HueRotate(Angle),
         Invert(CSSFloat),
         Opacity(CSSFloat),
         Saturate(CSSFloat),
         Sepia(CSSFloat),
         % if product == "gecko":
-        DropShadow(Length, Length, Length, Option<CSSColor>),
+        DropShadow(Shadow),
         % endif
     }
 
     pub mod computed_value {
         use app_units::Au;
         use values::CSSFloat;
-        use values::computed::CSSColor;
+        use values::computed::{CSSColor, Shadow};
         use values::specified::{Angle};
 
         #[derive(Clone, PartialEq, Debug)]
         #[cfg_attr(feature = "servo", derive(HeapSizeOf, Deserialize, Serialize))]
         pub enum Filter {
             Blur(Au),
             Brightness(CSSFloat),
             Contrast(CSSFloat),
             Grayscale(CSSFloat),
             HueRotate(Angle),
             Invert(CSSFloat),
             Opacity(CSSFloat),
             Saturate(CSSFloat),
             Sepia(CSSFloat),
             % if product == "gecko":
-            DropShadow(Au, Au, Au, CSSColor),
+            DropShadow(Shadow),
             % endif
         }
 
         #[derive(Clone, PartialEq, Debug)]
         #[cfg_attr(feature = "servo", derive(HeapSizeOf, Deserialize, Serialize))]
         pub struct T { pub filters: Vec<Filter> }
 
         impl T {
@@ -560,25 +447,25 @@
                     try!(value.to_css(dest));
                     try!(dest.write_str(")"));
                 }
                 computed_value::Filter::Invert(value) => try!(write!(dest, "invert({})", value)),
                 computed_value::Filter::Opacity(value) => try!(write!(dest, "opacity({})", value)),
                 computed_value::Filter::Saturate(value) => try!(write!(dest, "saturate({})", value)),
                 computed_value::Filter::Sepia(value) => try!(write!(dest, "sepia({})", value)),
                 % if product == "gecko":
-                computed_value::Filter::DropShadow(offset_x, offset_y, blur_radius, ref color) => {
+                computed_value::Filter::DropShadow(shadow) => {
                     try!(dest.write_str("drop-shadow("));
-                    try!(offset_x.to_css(dest));
+                    try!(shadow.offset_x.to_css(dest));
                     try!(dest.write_str(", "));
-                    try!(offset_y.to_css(dest));
+                    try!(shadow.offset_y.to_css(dest));
                     try!(dest.write_str(", "));
-                    try!(blur_radius.to_css(dest));
+                    try!(shadow.blur_radius.to_css(dest));
                     try!(dest.write_str(", "));
-                    try!(color.to_css(dest));
+                    try!(shadow.color.to_css(dest));
                     try!(dest.write_str(")"));
                 }
                 % endif
             }
             Ok(())
         }
     }
 
@@ -598,24 +485,24 @@
                     try!(value.to_css(dest));
                     try!(dest.write_str(")"));
                 }
                 SpecifiedFilter::Invert(value) => try!(write!(dest, "invert({})", value)),
                 SpecifiedFilter::Opacity(value) => try!(write!(dest, "opacity({})", value)),
                 SpecifiedFilter::Saturate(value) => try!(write!(dest, "saturate({})", value)),
                 SpecifiedFilter::Sepia(value) => try!(write!(dest, "sepia({})", value)),
                 % if product == "gecko":
-                SpecifiedFilter::DropShadow(offset_x, offset_y, blur_radius, ref color) => {
+                SpecifiedFilter::DropShadow(ref shadow) => {
                     try!(dest.write_str("drop-shadow("));
-                    try!(offset_x.to_css(dest));
+                    try!(shadow.offset_x.to_css(dest));
                     try!(dest.write_str(", "));
-                    try!(offset_y.to_css(dest));
+                    try!(shadow.offset_y.to_css(dest));
                     try!(dest.write_str(", "));
-                    try!(blur_radius.to_css(dest));
-                    if let &Some(ref color) = color {
+                    try!(shadow.blur_radius.to_css(dest));
+                    if let Some(ref color) = shadow.color {
                         try!(dest.write_str(", "));
                         try!(color.to_css(dest));
                     }
                     try!(dest.write_str(")"));
                 }
                 % endif
             }
             Ok(())
@@ -641,17 +528,18 @@
                         "contrast" => parse_factor(input).map(SpecifiedFilter::Contrast),
                         "grayscale" => parse_factor(input).map(SpecifiedFilter::Grayscale),
                         "hue-rotate" => Angle::parse(context, input).map(SpecifiedFilter::HueRotate),
                         "invert" => parse_factor(input).map(SpecifiedFilter::Invert),
                         "opacity" => parse_factor(input).map(SpecifiedFilter::Opacity),
                         "saturate" => parse_factor(input).map(SpecifiedFilter::Saturate),
                         "sepia" => parse_factor(input).map(SpecifiedFilter::Sepia),
                         % if product == "gecko":
-                        "drop-shadow" => parse_drop_shadow(context, input),
+                        "drop-shadow" => specified::Shadow::parse(context, input, true)
+                                             .map(SpecifiedFilter::DropShadow),
                         % endif
                         _ => Err(())
                     }
                 })));
             } else if filters.is_empty() {
                 return Err(())
             } else {
                 return Ok(SpecifiedValue(filters))
@@ -663,27 +551,16 @@
         use cssparser::Token;
         match input.next() {
             Ok(Token::Number(value)) => Ok(value.value),
             Ok(Token::Percentage(value)) => Ok(value.unit_value),
             _ => Err(())
         }
     }
 
-    % if product == "gecko":
-    fn parse_drop_shadow(context: &ParserContext, input: &mut Parser) -> Result<SpecifiedFilter, ()> {
-        let offset_x = try!(specified::Length::parse(context, input));
-        let offset_y = try!(specified::Length::parse(context, input));
-        let blur_radius = input.try(|i| specified::Length::parse(context, i))
-                               .unwrap_or(specified::Length::from_px(0.0));
-        let color = input.try(|i| specified::CSSColor::parse(context, i)).ok();
-        Ok(SpecifiedFilter::DropShadow(offset_x, offset_y, blur_radius, color))
-    }
-    % endif
-
     impl ToComputedValue for SpecifiedValue {
         type ComputedValue = computed_value::T;
 
         fn to_computed_value(&self, context: &Context) -> computed_value::T {
             computed_value::T{ filters: self.0.iter().map(|value| {
                 match *value {
                     SpecifiedFilter::Blur(factor) =>
                         computed_value::Filter::Blur(factor.to_computed_value(context)),
@@ -691,26 +568,19 @@
                     SpecifiedFilter::Contrast(factor) => computed_value::Filter::Contrast(factor),
                     SpecifiedFilter::Grayscale(factor) => computed_value::Filter::Grayscale(factor),
                     SpecifiedFilter::HueRotate(factor) => computed_value::Filter::HueRotate(factor),
                     SpecifiedFilter::Invert(factor) => computed_value::Filter::Invert(factor),
                     SpecifiedFilter::Opacity(factor) => computed_value::Filter::Opacity(factor),
                     SpecifiedFilter::Saturate(factor) => computed_value::Filter::Saturate(factor),
                     SpecifiedFilter::Sepia(factor) => computed_value::Filter::Sepia(factor),
                     % if product == "gecko":
-                    SpecifiedFilter::DropShadow(offset_x, offset_y, blur_radius, ref color) => {
-                        computed_value::Filter::DropShadow(
-                            offset_x.to_computed_value(context),
-                            offset_y.to_computed_value(context),
-                            blur_radius.to_computed_value(context),
-                            color.as_ref()
-                                 .map(|color| color.parsed)
-                                 .unwrap_or(cssparser::Color::CurrentColor),
-                        )
-                    }
+                    SpecifiedFilter::DropShadow(ref shadow) => {
+                        computed_value::Filter::DropShadow(shadow.to_computed_value(context))
+                    },
                     % endif
                 }
             }).collect() }
         }
 
         fn from_computed_value(computed: &computed_value::T) -> Self {
             SpecifiedValue(computed.filters.iter().map(|value| {
                 match *value {
@@ -720,22 +590,19 @@
                     computed_value::Filter::Contrast(factor) => SpecifiedFilter::Contrast(factor),
                     computed_value::Filter::Grayscale(factor) => SpecifiedFilter::Grayscale(factor),
                     computed_value::Filter::HueRotate(factor) => SpecifiedFilter::HueRotate(factor),
                     computed_value::Filter::Invert(factor) => SpecifiedFilter::Invert(factor),
                     computed_value::Filter::Opacity(factor) => SpecifiedFilter::Opacity(factor),
                     computed_value::Filter::Saturate(factor) => SpecifiedFilter::Saturate(factor),
                     computed_value::Filter::Sepia(factor) => SpecifiedFilter::Sepia(factor),
                     % if product == "gecko":
-                    computed_value::Filter::DropShadow(offset_x, offset_y, blur_radius, color) => {
+                    computed_value::Filter::DropShadow(shadow) => {
                         SpecifiedFilter::DropShadow(
-                            ToComputedValue::from_computed_value(&offset_x),
-                            ToComputedValue::from_computed_value(&offset_y),
-                            ToComputedValue::from_computed_value(&blur_radius),
-                            Some(ToComputedValue::from_computed_value(&color)),
+                            ToComputedValue::from_computed_value(&shadow),
                         )
                     }
                     % endif
                 }
             }).collect())
         }
     }
 </%helpers:longhand>
--- a/servo/components/style/values/computed/mod.rs
+++ b/servo/components/style/values/computed/mod.rs
@@ -162,10 +162,21 @@ impl ToComputedValue for specified::Bord
 impl ToCss for BorderRadiusSize {
     fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
         try!(self.0.width.to_css(dest));
         try!(dest.write_str("/"));
         self.0.height.to_css(dest)
     }
 }
 
+#[derive(Debug, PartialEq, Clone, Copy)]
+#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
+pub struct Shadow {
+    pub offset_x: Au,
+    pub offset_y: Au,
+    pub blur_radius: Au,
+    pub spread_radius: Au,
+    pub color: CSSColor,
+    pub inset: bool,
+}
+
 pub type Number = CSSFloat;
 pub type Opacity = CSSFloat;
--- a/servo/components/style/values/specified/mod.rs
+++ b/servo/components/style/values/specified/mod.rs
@@ -9,16 +9,17 @@ use parser::{ParserContext, Parse};
 use self::url::SpecifiedUrl;
 use std::ascii::AsciiExt;
 use std::f32::consts::PI;
 use std::fmt;
 use std::ops::Mul;
 use style_traits::ToCss;
 use super::{CSSFloat, HasViewportPercentage, NoViewportPercentage, Either, None_};
 use super::computed::{ComputedValueAsSpecified, Context, ToComputedValue};
+use super::computed::Shadow as ComputedShadow;
 
 pub use self::image::{AngleOrCorner, ColorStop, EndingShape as GradientEndingShape, Gradient};
 pub use self::image::{GradientKind, HorizontalDirection, Image, LengthOrKeyword, LengthOrPercentageOrKeyword};
 pub use self::image::{SizeKeyword, VerticalDirection};
 pub use self::length::{FontRelativeLength, ViewportPercentageLength, CharacterWidth, Length, CalcLengthOrPercentage};
 pub use self::length::{Percentage, LengthOrNone, LengthOrNumber, LengthOrPercentage, LengthOrPercentageOrAuto};
 pub use self::length::{LengthOrPercentageOrNone, LengthOrPercentageOrAutoOrContent, CalcUnit};
 
@@ -512,8 +513,122 @@ impl ToComputedValue for Opacity {
 impl ToCss for Opacity {
     fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
         self.0.to_css(dest)
     }
 }
 
 pub type UrlOrNone = Either<SpecifiedUrl, None_>;
 
+#[derive(Debug, Clone, PartialEq)]
+#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
+pub struct Shadow {
+    pub offset_x: Length,
+    pub offset_y: Length,
+    pub blur_radius: Length,
+    pub spread_radius: Length,
+    pub color: Option<CSSColor>,
+    pub inset: bool,
+}
+
+impl HasViewportPercentage for Shadow {
+    fn has_viewport_percentage(&self) -> bool {
+        self.offset_x.has_viewport_percentage() ||
+        self.offset_y.has_viewport_percentage() ||
+        self.blur_radius.has_viewport_percentage() ||
+        self.spread_radius.has_viewport_percentage()
+    }
+}
+
+impl ToComputedValue for Shadow {
+    type ComputedValue = ComputedShadow;
+
+    #[inline]
+    fn to_computed_value(&self, context: &Context) -> Self::ComputedValue {
+        ComputedShadow {
+            offset_x: self.offset_x.to_computed_value(context),
+            offset_y: self.offset_y.to_computed_value(context),
+            blur_radius: self.blur_radius.to_computed_value(context),
+            spread_radius: self.spread_radius.to_computed_value(context),
+            color: self.color
+                        .as_ref()
+                        .map(|color| color.parsed)
+                        .unwrap_or(cssparser::Color::CurrentColor),
+            inset: self.inset,
+        }
+    }
+
+    #[inline]
+    fn from_computed_value(computed: &ComputedShadow) -> Self {
+        Shadow {
+            offset_x: ToComputedValue::from_computed_value(&computed.offset_x),
+            offset_y: ToComputedValue::from_computed_value(&computed.offset_y),
+            blur_radius: ToComputedValue::from_computed_value(&computed.blur_radius),
+            spread_radius: ToComputedValue::from_computed_value(&computed.spread_radius),
+            color: Some(ToComputedValue::from_computed_value(&computed.color)),
+            inset: computed.inset,
+        }
+    }
+}
+
+impl Shadow {
+    // disable_spread_and_inset is for filter: drop-shadow(...)
+    pub fn parse(context:  &ParserContext, input: &mut Parser, disable_spread_and_inset: bool) -> Result<Shadow, ()> {
+        use app_units::Au;
+        let length_count = if disable_spread_and_inset { 3 } else { 4 };
+        let mut lengths = [Length::Absolute(Au(0)); 4];
+        let mut lengths_parsed = false;
+        let mut color = None;
+        let mut inset = false;
+
+        loop {
+            if !inset && !disable_spread_and_inset {
+                if input.try(|input| input.expect_ident_matching("inset")).is_ok() {
+                    inset = true;
+                    continue
+                }
+            }
+            if !lengths_parsed {
+                if let Ok(value) = input.try(|i| Length::parse(context, i)) {
+                    lengths[0] = value;
+                    let mut length_parsed_count = 1;
+                    while length_parsed_count < length_count {
+                        if let Ok(value) = input.try(|i| Length::parse(context, i)) {
+                            lengths[length_parsed_count] = value
+                        } else {
+                            break
+                        }
+                        length_parsed_count += 1;
+                    }
+
+                    // The first two lengths must be specified.
+                    if length_parsed_count < 2 {
+                        return Err(())
+                    }
+
+                    lengths_parsed = true;
+                    continue
+                }
+            }
+            if color.is_none() {
+                if let Ok(value) = input.try(|i| CSSColor::parse(context, i)) {
+                    color = Some(value);
+                    continue
+                }
+            }
+            break
+        }
+
+        // Lengths must be specified.
+        if !lengths_parsed {
+            return Err(())
+        }
+
+        Ok(Shadow {
+            offset_x: lengths[0],
+            offset_y: lengths[1],
+            blur_radius: lengths[2],
+            spread_radius: if disable_spread_and_inset { Length::Absolute(Au(0)) } else { lengths[3] },
+            color: color,
+            inset: inset,
+        })
+    }
+}