servo: Merge #17402 - Use generics for the filter property (from servo:derive-all-the-things); r=emilio
authorAnthony Ramine <n.oxyde@gmail.com>
Tue, 20 Jun 2017 06:23:17 -0700
changeset 365068 034c0bb6fb56fe265f3929dbad480c8d8476eebf
parent 365067 e5fb1c36d5d6fc4420d4d9afa324e2b33cf6dd08
child 365069 6731e174280419faf890edb613d553b5c6642089
push id91680
push userkwierso@gmail.com
push dateWed, 21 Jun 2017 01:32:01 +0000
treeherdermozilla-inbound@f7b9dc31956c [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersemilio
milestone56.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 #17402 - Use generics for the filter property (from servo:derive-all-the-things); r=emilio Source-Repo: https://github.com/servo/servo Source-Revision: fc2c5b7ef4bd7f36b6aa4e0540adc5b8ef3882c0
servo/components/gfx/display_list/mod.rs
servo/components/layout/display_list_builder.rs
servo/components/layout/fragment.rs
servo/components/layout/webrender_helpers.rs
servo/components/style/gecko_bindings/sugar/ns_css_shadow_item.rs
servo/components/style/properties/gecko.mako.rs
servo/components/style/properties/helpers/animated_properties.mako.rs
servo/components/style/properties/longhand/effects.mako.rs
servo/components/style/properties/properties.mako.rs
servo/components/style/values/animated/effects.rs
servo/components/style/values/animated/mod.rs
servo/components/style/values/computed/effects.rs
servo/components/style/values/computed/mod.rs
servo/components/style/values/generics/counters.rs
servo/components/style/values/generics/effects.rs
servo/components/style/values/generics/mod.rs
servo/components/style/values/mod.rs
servo/components/style/values/specified/effects.rs
servo/components/style/values/specified/length.rs
servo/components/style/values/specified/mod.rs
servo/components/style_traits/viewport.rs
--- a/servo/components/gfx/display_list/mod.rs
+++ b/servo/components/gfx/display_list/mod.rs
@@ -474,17 +474,17 @@ impl StackingContext {
 
     #[inline]
     pub fn root(pipeline_id: PipelineId) -> StackingContext {
         StackingContext::new(StackingContextId::root(),
                              StackingContextType::Real,
                              &Rect::zero(),
                              &Rect::zero(),
                              0,
-                             filter::T::new(Vec::new()),
+                             filter::T::none(),
                              MixBlendMode::Normal,
                              None,
                              TransformStyle::Flat,
                              None,
                              ScrollPolicy::Scrollable,
                              pipeline_id.root_scroll_node())
     }
 
--- a/servo/components/layout/display_list_builder.rs
+++ b/servo/components/layout/display_list_builder.rs
@@ -45,27 +45,27 @@ use servo_url::ServoUrl;
 use std::{cmp, f32};
 use std::collections::HashMap;
 use std::default::Default;
 use std::mem;
 use std::sync::Arc;
 use style::computed_values::{background_attachment, background_clip, background_origin};
 use style::computed_values::{background_repeat, border_style, cursor};
 use style::computed_values::{image_rendering, overflow_x, pointer_events, position, visibility};
-use style::computed_values::filter::Filter;
 use style::logical_geometry::{LogicalPoint, LogicalRect, LogicalSize, WritingMode};
 use style::properties::{self, ServoComputedValues};
 use style::properties::longhands::border_image_repeat::computed_value::RepeatKeyword;
 use style::properties::style_structs;
 use style::servo::restyle_damage::REPAINT;
 use style::values::{Either, RGBA};
 use style::values::computed::{Gradient, GradientItem, LengthOrPercentage};
 use style::values::computed::{LengthOrPercentageOrAuto, NumberOrPercentage, Position, Shadow};
 use style::values::computed::image::{EndingShape, LineDirection};
 use style::values::generics::background::BackgroundSize;
+use style::values::generics::effects::Filter;
 use style::values::generics::image::{Circle, Ellipse, EndingShape as GenericEndingShape};
 use style::values::generics::image::{GradientItem as GenericGradientItem, GradientKind};
 use style::values::generics::image::{Image, ShapeExtent};
 use style::values::generics::image::PaintWorklet;
 use style::values::specified::length::Percentage;
 use style::values::specified::position::{X, Y};
 use style_traits::CSSPixel;
 use style_traits::cursor::Cursor;
@@ -2001,33 +2001,33 @@ impl FragmentDisplayListBuilding for Fra
         // from our flow origin, since that is what `BaseFlow::overflow` is relative to.
         let border_box_offset =
             border_box.translate(&-base_flow.stacking_relative_position).origin;
         // Then, using that, compute our overflow region relative to our border box.
         let overflow = base_flow.overflow.paint.translate(&-border_box_offset.to_vector());
 
         // Create the filter pipeline.
         let effects = self.style().get_effects();
-        let mut filters = effects.filter.clone();
+        let mut filters = effects.filter.clone().0.into_vec();
         if effects.opacity != 1.0 {
             filters.push(Filter::Opacity(effects.opacity))
         }
 
         let context_type = match mode {
             StackingContextCreationMode::PseudoFloat => StackingContextType::PseudoFloat,
             StackingContextCreationMode::PseudoPositioned => StackingContextType::PseudoPositioned,
             _ => StackingContextType::Real,
         };
 
         StackingContext::new(id,
                              context_type,
                              &border_box,
                              &overflow,
                              self.effective_z_index(),
-                             filters,
+                             filters.into(),
                              self.style().get_effects().mix_blend_mode.to_mix_blend_mode(),
                              self.transform_matrix(&border_box),
                              self.style().get_used_transform_style().to_transform_style(),
                              self.perspective_matrix(&border_box),
                              scroll_policy,
                              parent_scroll_id)
     }
 
--- a/servo/components/layout/fragment.rs
+++ b/servo/components/layout/fragment.rs
@@ -2462,17 +2462,17 @@ impl Fragment {
             SpecificFragmentInfo::ScannedText(_) |
             SpecificFragmentInfo::UnscannedText(_) => return false,
             _ => {}
         }
 
         if self.style().get_effects().opacity != 1.0 {
             return true
         }
-        if !self.style().get_effects().filter.is_empty() {
+        if !self.style().get_effects().filter.0.is_empty() {
             return true
         }
         if self.style().get_effects().mix_blend_mode != mix_blend_mode::T::normal {
             return true
         }
 
         if self.style().get_box().transform.0.is_some() ||
            self.style().get_box().transform_style == transform_style::T::preserve_3d ||
--- a/servo/components/layout/webrender_helpers.rs
+++ b/servo/components/layout/webrender_helpers.rs
@@ -8,18 +8,19 @@
 //           completely converting layout to directly generate WebRender display lists, for example.
 
 use app_units::Au;
 use euclid::{Point2D, Vector2D, Rect, SideOffsets2D, Size2D};
 use gfx::display_list::{BorderDetails, BorderRadii, BoxShadowClipMode, ClippingRegion};
 use gfx::display_list::{DisplayItem, DisplayList, DisplayListTraversal, StackingContextType};
 use msg::constellation_msg::PipelineId;
 use style::computed_values::{image_rendering, mix_blend_mode, transform_style};
-use style::computed_values::filter::{self, Filter};
+use style::computed_values::filter;
 use style::values::computed::BorderStyle;
+use style::values::generics::effects::Filter;
 use webrender_traits::{self, DisplayListBuilder, ExtendMode};
 use webrender_traits::{LayoutTransform, ClipId, ClipRegionToken};
 
 pub trait WebRenderDisplayListConverter {
     fn convert_to_webrender(&self, pipeline_id: PipelineId) -> DisplayListBuilder;
 }
 
 trait WebRenderDisplayItemConverter {
@@ -198,28 +199,29 @@ impl ToImageRendering for image_renderin
 }
 
 trait ToFilterOps {
     fn to_filter_ops(&self) -> Vec<webrender_traits::FilterOp>;
 }
 
 impl ToFilterOps for filter::T {
     fn to_filter_ops(&self) -> Vec<webrender_traits::FilterOp> {
-        let mut result = Vec::with_capacity(self.filters.len());
-        for filter in self.filters.iter() {
+        let mut result = Vec::with_capacity(self.0.len());
+        for filter in self.0.iter() {
             match *filter {
                 Filter::Blur(radius) => result.push(webrender_traits::FilterOp::Blur(radius)),
                 Filter::Brightness(amount) => result.push(webrender_traits::FilterOp::Brightness(amount)),
                 Filter::Contrast(amount) => result.push(webrender_traits::FilterOp::Contrast(amount)),
                 Filter::Grayscale(amount) => result.push(webrender_traits::FilterOp::Grayscale(amount)),
                 Filter::HueRotate(angle) => result.push(webrender_traits::FilterOp::HueRotate(angle.radians())),
                 Filter::Invert(amount) => result.push(webrender_traits::FilterOp::Invert(amount)),
                 Filter::Opacity(amount) => result.push(webrender_traits::FilterOp::Opacity(amount.into())),
                 Filter::Saturate(amount) => result.push(webrender_traits::FilterOp::Saturate(amount)),
                 Filter::Sepia(amount) => result.push(webrender_traits::FilterOp::Sepia(amount)),
+                Filter::DropShadow(ref shadow) => match *shadow {},
             }
         }
         result
     }
 }
 
 pub trait ToTransformStyle {
     fn to_transform_style(&self) -> webrender_traits::TransformStyle;
--- a/servo/components/style/gecko_bindings/sugar/ns_css_shadow_item.rs
+++ b/servo/components/style/gecko_bindings/sugar/ns_css_shadow_item.rs
@@ -3,16 +3,17 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 //! Rust helpers for Gecko's `nsCSSShadowItem`.
 
 use app_units::Au;
 use gecko::values::{convert_rgba_to_nscolor, convert_nscolor_to_rgba};
 use gecko_bindings::structs::nsCSSShadowItem;
 use values::computed::{Color, Shadow};
+use values::computed::effects::DropShadow;
 
 impl nsCSSShadowItem {
     /// Set this item to the given shadow value.
     pub fn set_from_shadow(&mut self, other: Shadow) {
         self.mXOffset = other.offset_x.0;
         self.mYOffset = other.offset_y.0;
         self.mRadius = other.blur_radius.0;
         self.mSpread = other.spread_radius.0;
@@ -34,9 +35,41 @@ impl nsCSSShadowItem {
             offset_x: Au(self.mXOffset),
             offset_y: Au(self.mYOffset),
             blur_radius: Au(self.mRadius),
             spread_radius: Au(self.mSpread),
             inset: self.mInset,
             color: Color::rgba(convert_nscolor_to_rgba(self.mColor)),
         }
     }
+
+    /// Sets this item from the given drop shadow.
+    #[inline]
+    pub fn set_from_drop_shadow(&mut self, shadow: DropShadow) {
+        self.mXOffset = shadow.horizontal.0;
+        self.mYOffset = shadow.vertical.0;
+        self.mRadius = shadow.blur.0;
+        self.mSpread = 0;
+        self.mInset = false;
+        if shadow.color.is_currentcolor() {
+            // TODO handle currentColor
+            // https://bugzilla.mozilla.org/show_bug.cgi?id=760345
+            self.mHasColor = false;
+            self.mColor = 0;
+        } else {
+            self.mHasColor = true;
+            self.mColor = convert_rgba_to_nscolor(&shadow.color.color);
+        }
+    }
+
+    /// Returns this item as a drop shadow.
+    #[inline]
+    pub fn to_drop_shadow(&self) -> DropShadow {
+        debug_assert_eq!(self.mSpread, 0);
+        debug_assert_eq!(self.mInset, false);
+        DropShadow {
+            color: Color::rgba(convert_nscolor_to_rgba(self.mColor)),
+            horizontal: Au(self.mXOffset),
+            vertical: Au(self.mYOffset),
+            blur: Au(self.mRadius),
+        }
+    }
 }
--- a/servo/components/style/properties/gecko.mako.rs
+++ b/servo/components/style/properties/gecko.mako.rs
@@ -3439,17 +3439,17 @@ fn static_assert() {
     # number value for function of clone / set.
     # The setting / cloning process of other function(e.g. Blur / HueRotate) is
     # different from these function. So this array don't include such function.
     FILTER_FUNCTIONS = [ 'Brightness', 'Contrast', 'Grayscale', 'Invert',
                          'Opacity', 'Saturate', 'Sepia' ]
      %>
 
     pub fn set_filter(&mut self, v: longhands::filter::computed_value::T) {
-        use properties::longhands::filter::computed_value::Filter::*;
+        use values::generics::effects::Filter::*;
         use gecko_bindings::structs::nsCSSShadowArray;
         use gecko_bindings::structs::nsStyleFilter;
         use gecko_bindings::structs::NS_STYLE_FILTER_BLUR;
         use gecko_bindings::structs::NS_STYLE_FILTER_BRIGHTNESS;
         use gecko_bindings::structs::NS_STYLE_FILTER_CONTRAST;
         use gecko_bindings::structs::NS_STYLE_FILTER_GRAYSCALE;
         use gecko_bindings::structs::NS_STYLE_FILTER_INVERT;
         use gecko_bindings::structs::NS_STYLE_FILTER_OPACITY;
@@ -3459,21 +3459,21 @@ fn static_assert() {
         use gecko_bindings::structs::NS_STYLE_FILTER_DROP_SHADOW;
 
         fn fill_filter(m_type: u32, value: CoordDataValue, gecko_filter: &mut nsStyleFilter){
             gecko_filter.mType = m_type;
             gecko_filter.mFilterParameter.set_value(value);
         }
 
         unsafe {
-            Gecko_ResetFilters(&mut self.gecko, v.filters.len());
+            Gecko_ResetFilters(&mut self.gecko, v.0.len());
         }
-        debug_assert!(v.filters.len() == self.gecko.mFilters.len());
-
-        for (servo, gecko_filter) in v.filters.into_iter().zip(self.gecko.mFilters.iter_mut()) {
+        debug_assert!(v.0.len() == self.gecko.mFilters.len());
+
+        for (servo, gecko_filter) in v.0.into_vec().into_iter().zip(self.gecko.mFilters.iter_mut()) {
             match servo {
                 % for func in FILTER_FUNCTIONS:
                 ${func}(factor) => fill_filter(NS_STYLE_FILTER_${func.upper()},
                                                CoordDataValue::Factor(factor),
                                                gecko_filter),
                 % endfor
                 Blur(length) => fill_filter(NS_STYLE_FILTER_BLUR,
                                             CoordDataValue::Coord(length.0),
@@ -3492,17 +3492,17 @@ fn static_assert() {
                             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].set_from_shadow(shadow);
+                    gecko_shadow.mArray[0].set_from_drop_shadow(shadow);
                 },
                 Url(ref url) => {
                     unsafe {
                         bindings::Gecko_nsStyleFilter_SetURLValue(gecko_filter, url.for_ffi());
                     }
                 },
             }
         }
@@ -3510,17 +3510,17 @@ fn static_assert() {
 
     pub fn copy_filter_from(&mut self, other: &Self) {
         unsafe {
             Gecko_CopyFiltersFrom(&other.gecko as *const _ as *mut _, &mut self.gecko);
         }
     }
 
     pub fn clone_filter(&self) -> longhands::filter::computed_value::T {
-        use properties::longhands::filter::computed_value::Filter::*;
+        use values::generics::effects::{Filter, FilterList};
         use values::specified::url::SpecifiedUrl;
         use gecko_bindings::structs::NS_STYLE_FILTER_BLUR;
         use gecko_bindings::structs::NS_STYLE_FILTER_BRIGHTNESS;
         use gecko_bindings::structs::NS_STYLE_FILTER_CONTRAST;
         use gecko_bindings::structs::NS_STYLE_FILTER_GRAYSCALE;
         use gecko_bindings::structs::NS_STYLE_FILTER_INVERT;
         use gecko_bindings::structs::NS_STYLE_FILTER_OPACITY;
         use gecko_bindings::structs::NS_STYLE_FILTER_SATURATE;
@@ -3529,45 +3529,46 @@ fn static_assert() {
         use gecko_bindings::structs::NS_STYLE_FILTER_DROP_SHADOW;
         use gecko_bindings::structs::NS_STYLE_FILTER_URL;
 
         let mut filters = Vec::new();
         for filter in self.gecko.mFilters.iter(){
             match filter.mType {
                 % for func in FILTER_FUNCTIONS:
                 NS_STYLE_FILTER_${func.upper()} => {
-                    filters.push(${func}(
+                    filters.push(Filter::${func}(
                         GeckoStyleCoordConvertible::from_gecko_style_coord(
                             &filter.mFilterParameter).unwrap()));
                 },
                 % endfor
                 NS_STYLE_FILTER_BLUR => {
-                    filters.push(Blur(Au::from_gecko_style_coord(
+                    filters.push(Filter::Blur(Au::from_gecko_style_coord(
                         &filter.mFilterParameter).unwrap()));
                 },
                 NS_STYLE_FILTER_HUE_ROTATE => {
-                    filters.push(HueRotate(
+                    filters.push(Filter::HueRotate(
                         GeckoStyleCoordConvertible::from_gecko_style_coord(
                             &filter.mFilterParameter).unwrap()));
                 },
                 NS_STYLE_FILTER_DROP_SHADOW => {
                     filters.push(unsafe {
-                        DropShadow((**filter.__bindgen_anon_1.mDropShadow.as_ref()).mArray[0].to_shadow())
+                        Filter::DropShadow((**filter.__bindgen_anon_1.mDropShadow.as_ref()).mArray[0].to_drop_shadow())
                     });
                 },
                 NS_STYLE_FILTER_URL => {
                     filters.push(unsafe {
-                        (Url(SpecifiedUrl::from_url_value_data(
-                            &(**filter.__bindgen_anon_1.mURL.as_ref())._base).unwrap()))
+                        Filter::Url(
+                            SpecifiedUrl::from_url_value_data(&(**filter.__bindgen_anon_1.mURL.as_ref())._base).unwrap()
+                        )
                     });
                 }
                 _ => {},
             }
         }
-        longhands::filter::computed_value::T::new(filters)
+        FilterList(filters.into_boxed_slice())
     }
 
 </%self:impl_trait>
 
 <%self:impl_trait style_struct_name="InheritedBox"
                   skip_longhands="image-orientation">
     // FIXME: Gecko uses a tricky way to store computed value of image-orientation
     //        within an u8. We could inline following glue codes by implementing all
--- a/servo/components/style/properties/helpers/animated_properties.mako.rs
+++ b/servo/components/style/properties/helpers/animated_properties.mako.rs
@@ -9,22 +9,19 @@
 use app_units::Au;
 use cssparser::{Parser, RGBA};
 use euclid::{Point2D, Size2D};
 #[cfg(feature = "gecko")] use gecko_bindings::bindings::RawServoAnimationValueMap;
 #[cfg(feature = "gecko")] use gecko_bindings::structs::RawGeckoGfxMatrix4x4;
 #[cfg(feature = "gecko")] use gecko_bindings::structs::nsCSSPropertyID;
 #[cfg(feature = "gecko")] use gecko_bindings::sugar::ownership::{HasFFI, HasSimpleFFI};
 #[cfg(feature = "gecko")] use gecko_string_cache::Atom;
-#[cfg(feature = "gecko")] use gecko::url::SpecifiedUrl;
 use properties::{CSSWideKeyword, PropertyDeclaration};
 use properties::longhands;
 use properties::longhands::background_size::computed_value::T as BackgroundSizeList;
-use properties::longhands::filter::computed_value::Filter;
-use properties::longhands::filter::computed_value::T as Filters;
 use properties::longhands::font_weight::computed_value::T as FontWeight;
 use properties::longhands::font_stretch::computed_value::T as FontStretch;
 use properties::longhands::text_shadow::computed_value::T as TextShadowList;
 use properties::longhands::box_shadow::computed_value::T as BoxShadowList;
 use properties::longhands::transform::computed_value::ComputedMatrix;
 use properties::longhands::transform::computed_value::ComputedOperation as TransformOperation;
 use properties::longhands::transform::computed_value::T as TransformList;
 use properties::longhands::vertical_align::computed_value::T as VerticalAlign;
@@ -32,22 +29,24 @@ use properties::longhands::visibility::c
 #[cfg(feature = "gecko")] use properties::{PropertyDeclarationId, LonghandId};
 use selectors::parser::SelectorParseError;
 use smallvec::SmallVec;
 use std::cmp;
 #[cfg(feature = "gecko")] use std::collections::HashMap;
 use style_traits::ParseError;
 use super::ComputedValues;
 use values::{Auto, CSSFloat, CustomIdent, Either};
+use values::animated::effects::{Filter as AnimatedFilter, FilterList as AnimatedFilterList};
 use values::computed::{Angle, LengthOrPercentageOrAuto, LengthOrPercentageOrNone};
 use values::computed::{BorderCornerRadius, ClipRect};
 use values::computed::{CalcLengthOrPercentage, Color, Context, ComputedValueAsSpecified};
 use values::computed::{LengthOrPercentage, MaxLength, MozLength, Shadow, ToComputedValue};
 use values::generics::{SVGPaint, SVGPaintKind};
 use values::generics::border::BorderCornerRadius as GenericBorderCornerRadius;
+use values::generics::effects::Filter;
 use values::generics::position as generic_position;
 use values::specified::length::Percentage;
 
 
 /// A longhand property whose animation type is not "none".
 ///
 /// NOTE: This includes the 'display' property since it is animatable from SMIL even though it is
 /// not animatable from CSS animations or Web Animations. CSS transitions also does not allow
@@ -3191,251 +3190,172 @@ impl Animatable for IntermediateShadowLi
 
         result.extend(self.0.iter().cloned());
         result.extend(other.0.iter().cloned());
 
         Ok(IntermediateShadowList(result))
     }
 }
 
-/// Intermediate type for filter property.
-/// The difference from normal filter type is that this structure uses
-/// IntermediateColor into DropShadow's value.
-pub type IntermediateFilters = Vec<IntermediateFilter>;
-
-#[derive(Clone, PartialEq, Debug)]
-#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
-#[allow(missing_docs)]
-pub enum IntermediateFilter {
-    Blur(Au),
-    Brightness(CSSFloat),
-    Contrast(CSSFloat),
-    Grayscale(CSSFloat),
-    HueRotate(Angle),
-    Invert(CSSFloat),
-    Opacity(CSSFloat),
-    Saturate(CSSFloat),
-    Sepia(CSSFloat),
-    % if product == "gecko":
-    DropShadow(IntermediateShadow),
-    Url(SpecifiedUrl),
-    % endif
-}
-
-impl From<Filters> for IntermediateFilters {
-    fn from(filters: Filters) -> IntermediateFilters {
-        filters.filters.into_iter().map(|f| f.into()).collect()
-    }
-}
-
-impl From<IntermediateFilters> for Filters {
-    fn from(filters: IntermediateFilters) -> Filters {
-        Filters::new(filters.into_iter().map(|f| f.into()).collect())
-    }
-}
-
 <%
     FILTER_FUNCTIONS = [ 'Blur', 'Brightness', 'Contrast', 'Grayscale',
                          'HueRotate', 'Invert', 'Opacity', 'Saturate',
                          'Sepia' ]
 %>
 
-impl From<Filter> for IntermediateFilter {
-    fn from(filter: Filter) -> IntermediateFilter {
-        use properties::longhands::filter::computed_value::Filter::*;
-        match filter {
-            % for func in FILTER_FUNCTIONS:
-                ${func}(val) => IntermediateFilter::${func}(val),
-            % endfor
-            % if product == "gecko":
-                DropShadow(shadow) => {
-                    IntermediateFilter::DropShadow(shadow.into())
-                },
-                Url(ref url) => {
-                    IntermediateFilter::Url(url.clone())
-                },
-            % endif
-        }
-    }
-}
-
-impl From<IntermediateFilter> for Filter {
-    fn from(filter: IntermediateFilter) -> Filter {
-        match filter {
-            % for func in FILTER_FUNCTIONS:
-                IntermediateFilter::${func}(val) => Filter::${func}(val),
-            % endfor
-            % if product == "gecko":
-                IntermediateFilter::DropShadow(shadow) => {
-                    Filter::DropShadow(shadow.into())
-                },
-                IntermediateFilter::Url(ref url) => {
-                    Filter::Url(url.clone())
-                },
-            % endif
-        }
-    }
-}
-
 /// https://drafts.fxtf.org/filters/#animation-of-filters
-fn add_weighted_filter_function_impl(from: &IntermediateFilter,
-                                     to: &IntermediateFilter,
+fn add_weighted_filter_function_impl(from: &AnimatedFilter,
+                                     to: &AnimatedFilter,
                                      self_portion: f64,
                                      other_portion: f64)
-                                     -> Result<IntermediateFilter, ()> {
+                                     -> Result<AnimatedFilter, ()> {
     match (from, to) {
         % for func in [ 'Blur', 'HueRotate' ]:
-            (&IntermediateFilter::${func}(from_value),
-             &IntermediateFilter::${func}(to_value)) => {
-                Ok(IntermediateFilter::${func}(
-                    try!(from_value.add_weighted(&to_value,
-                                                 self_portion,
-                                                 other_portion))))
+            (&Filter::${func}(from_value), &Filter::${func}(to_value)) => {
+                Ok(Filter::${func}(from_value.add_weighted(
+                    &to_value,
+                    self_portion,
+                    other_portion,
+                )?))
            },
         % endfor
         % for func in [ 'Grayscale', 'Invert', 'Sepia' ]:
-            (&IntermediateFilter::${func}(from_value),
-             &IntermediateFilter::${func}(to_value)) => {
-                Ok(IntermediateFilter::${func}(try!(
-                    add_weighted_with_initial_val(&from_value,
-                                                  &to_value,
-                                                  self_portion,
-                                                  other_portion,
-                                                  &0.0))))
+            (&Filter::${func}(from_value), &Filter::${func}(to_value)) => {
+                Ok(Filter::${func}(add_weighted_with_initial_val(
+                    &from_value,
+                    &to_value,
+                    self_portion,
+                    other_portion,
+                    &0.0,
+                )?))
             },
         % endfor
         % for func in [ 'Brightness', 'Contrast', 'Opacity', 'Saturate' ]:
-            (&IntermediateFilter::${func}(from_value),
-             &IntermediateFilter::${func}(to_value)) => {
-                Ok(IntermediateFilter::${func}(try!(
-                    add_weighted_with_initial_val(&from_value,
-                                                  &to_value,
-                                                  self_portion,
-                                                  other_portion,
-                                                  &1.0))))
+            (&Filter::${func}(from_value), &Filter::${func}(to_value)) => {
+                Ok(Filter::${func}(add_weighted_with_initial_val(
+                    &from_value,
+                    &to_value,
+                    self_portion,
+                    other_portion,
+                    &1.0,
+                )?))
                 },
         % endfor
         % if product == "gecko":
-            (&IntermediateFilter::DropShadow(from_value),
-             &IntermediateFilter::DropShadow(to_value)) => {
-                Ok(IntermediateFilter::DropShadow(try!(
-                    from_value.add_weighted(&to_value,
-                                            self_portion,
-                                            other_portion))))
-            },
-            (&IntermediateFilter::Url(_),
-             &IntermediateFilter::Url(_)) => {
-                Err(())
-            },
+        (&Filter::DropShadow(ref from_value), &Filter::DropShadow(ref to_value)) => {
+            Ok(Filter::DropShadow(from_value.add_weighted(
+                &to_value,
+                self_portion,
+                other_portion,
+            )?))
+        },
+        (&Filter::Url(_), &Filter::Url(_)) => {
+            Err(())
+        },
         % endif
         _ => {
             // If specified the different filter functions,
             // we will need to interpolate as discreate.
             Err(())
         },
     }
 }
 
 /// https://drafts.fxtf.org/filters/#animation-of-filters
-fn add_weighted_filter_function(from: Option<<&IntermediateFilter>,
-                                to: Option<<&IntermediateFilter>,
+fn add_weighted_filter_function(from: Option<<&AnimatedFilter>,
+                                to: Option<<&AnimatedFilter>,
                                 self_portion: f64,
-                                other_portion: f64) -> Result<IntermediateFilter, ()> {
+                                other_portion: f64) -> Result<AnimatedFilter, ()> {
     match (from, to) {
         (Some(f), Some(t)) => {
             add_weighted_filter_function_impl(f, t, self_portion, other_portion)
         },
         (Some(f), None) => {
             add_weighted_filter_function_impl(f, f, self_portion, 0.0)
         },
         (None, Some(t)) => {
             add_weighted_filter_function_impl(t, t, other_portion, 0.0)
         },
         _ => { Err(()) }
     }
 }
 
-fn compute_filter_square_distance(from: &IntermediateFilter,
-                                  to: &IntermediateFilter)
+fn compute_filter_square_distance(from: &AnimatedFilter,
+                                  to: &AnimatedFilter)
                                   -> Result<f64, ()> {
     match (from, to) {
         % for func in FILTER_FUNCTIONS :
-            (&IntermediateFilter::${func}(f),
-             &IntermediateFilter::${func}(t)) => {
+            (&Filter::${func}(f),
+             &Filter::${func}(t)) => {
                 Ok(try!(f.compute_squared_distance(&t)))
             },
         % endfor
         % if product == "gecko":
-            (&IntermediateFilter::DropShadow(f),
-             &IntermediateFilter::DropShadow(t)) => {
+            (&Filter::DropShadow(ref f), &Filter::DropShadow(ref t)) => {
                 Ok(try!(f.compute_squared_distance(&t)))
             },
         % endif
         _ => {
             Err(())
         }
     }
 }
 
-impl Animatable for IntermediateFilters {
+impl Animatable for AnimatedFilterList {
     #[inline]
     fn add_weighted(&self, other: &Self,
                     self_portion: f64, other_portion: f64) -> Result<Self, ()> {
-        let mut filters: IntermediateFilters = Vec::new();
-        let mut from_iter = self.iter();
-        let mut to_iter = (&other).iter();
+        let mut filters = vec![];
+        let mut from_iter = self.0.iter();
+        let mut to_iter = other.0.iter();
 
         let mut from = from_iter.next();
         let mut to = to_iter.next();
-        while (from,to) != (None, None) {
+        while from.is_some() || to.is_some() {
             filters.push(try!(add_weighted_filter_function(from,
                                                            to,
                                                            self_portion,
                                                            other_portion)));
-            if from != None {
+            if from.is_some() {
                 from = from_iter.next();
             }
-            if to != None {
+            if to.is_some() {
                 to = to_iter.next();
             }
         }
 
-        Ok(filters)
+        Ok(filters.into())
     }
 
     fn add(&self, other: &Self) -> Result<Self, ()> {
-        let from_list = &self;
-        let to_list = &other;
-        let filters: IntermediateFilters =
-            vec![&from_list[..], &to_list[..]].concat();
-        Ok(filters)
+        Ok(self.0.iter().chain(other.0.iter()).cloned().collect::<Vec<_>>().into())
     }
 
     #[inline]
     fn compute_distance(&self, other: &Self) -> Result<f64, ()> {
         self.compute_squared_distance(other).map(|sd| sd.sqrt())
     }
 
     #[inline]
     fn compute_squared_distance(&self, other: &Self) -> Result<f64, ()> {
         let mut square_distance: f64 = 0.0;
-        let mut from_iter = self.iter();
-        let mut to_iter = (&other).iter();
+        let mut from_iter = self.0.iter();
+        let mut to_iter = other.0.iter();
 
         let mut from = from_iter.next();
         let mut to = to_iter.next();
-        while (from,to) != (None, None) {
+        while from.is_some() || to.is_some() {
             let current_square_distance: f64 ;
-            if from == None {
+            if from.is_none() {
                 let none = try!(add_weighted_filter_function(to, to, 0.0, 0.0));
                 current_square_distance =
                     compute_filter_square_distance(&none, &(to.unwrap())).unwrap();
 
                 to = to_iter.next();
-            } else if to == None {
+            } else if to.is_none() {
                 let none = try!(add_weighted_filter_function(from, from, 0.0, 0.0));
                 current_square_distance =
                     compute_filter_square_distance(&none, &(from.unwrap())).unwrap();
 
                 from = from_iter.next();
             } else {
                 current_square_distance =
                     compute_filter_square_distance(&(from.unwrap()),
--- a/servo/components/style/properties/longhand/effects.mako.rs
+++ b/servo/components/style/properties/longhand/effects.mako.rs
@@ -36,349 +36,25 @@
 ${helpers.predefined_type("clip",
                           "ClipRectOrAuto",
                           "computed::ClipRectOrAuto::auto()",
                           animation_value_type="ComputedValue",
                           boxed="True",
                           allow_quirks=True,
                           spec="https://drafts.fxtf.org/css-masking/#clip-property")}
 
-<%helpers:longhand name="filter" animation_value_type="IntermediateFilters" extra_prefixes="webkit"
-                   flags="CREATES_STACKING_CONTEXT FIXPOS_CB"
-                   spec="https://drafts.fxtf.org/filters/#propdef-filter">
-    //pub use self::computed_value::T as SpecifiedValue;
-    use std::fmt;
-    use style_traits::{HasViewportPercentage, ToCss};
-    use values::CSSFloat;
-    use values::specified::{Angle, Length};
-    #[cfg(feature = "gecko")]
-    use values::specified::Shadow;
-    #[cfg(feature = "gecko")]
-    use values::specified::url::SpecifiedUrl;
-
-    #[derive(Clone, Debug, HasViewportPercentage, PartialEq)]
-    #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
-    pub struct SpecifiedValue(pub Vec<SpecifiedFilter>);
-
-    impl HasViewportPercentage for SpecifiedFilter {
-        fn has_viewport_percentage(&self) -> bool {
-            match *self {
-                SpecifiedFilter::Blur(ref length) => length.has_viewport_percentage(),
-                _ => false
-            }
-        }
-    }
-
-    // TODO(pcwalton): `drop-shadow`
-    #[derive(Clone, PartialEq, Debug)]
-    #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
-    pub enum SpecifiedFilter {
-        Blur(Length),
-        Brightness(CSSFloat),
-        Contrast(CSSFloat),
-        Grayscale(CSSFloat),
-        HueRotate(Angle),
-        Invert(CSSFloat),
-        Opacity(CSSFloat),
-        Saturate(CSSFloat),
-        Sepia(CSSFloat),
-        % if product == "gecko":
-        DropShadow(Shadow),
-        Url(SpecifiedUrl),
-        % endif
-    }
-
-    pub mod computed_value {
-        use app_units::Au;
-        use values::CSSFloat;
-        #[cfg(feature = "gecko")]
-        use values::computed::Shadow;
-        use values::computed::Angle;
-        #[cfg(feature = "gecko")]
-        use values::specified::url::SpecifiedUrl;
-
-        #[derive(Clone, PartialEq, Debug)]
-        #[cfg_attr(feature = "servo", derive(HeapSizeOf, Deserialize, Serialize))]
-        pub enum Filter {
-            Blur(Au),
-            Brightness(CSSFloat),
-            Contrast(CSSFloat),
-            Grayscale(CSSFloat),
-            HueRotate(Angle),
-            Invert(CSSFloat),
-            Opacity(CSSFloat),
-            Saturate(CSSFloat),
-            Sepia(CSSFloat),
-            % if product == "gecko":
-            DropShadow(Shadow),
-            Url(SpecifiedUrl),
-            % endif
-        }
-
-        #[derive(Clone, PartialEq, Debug)]
-        #[cfg_attr(feature = "servo", derive(HeapSizeOf, Deserialize, Serialize))]
-        pub struct T { pub filters: Vec<Filter> }
-
-        impl T {
-            /// Creates a new filter pipeline.
-            #[inline]
-            pub fn new(filters: Vec<Filter>) -> T {
-                T
-                {
-                   filters: filters,
-                }
-            }
-
-            /// Adds a new filter to the filter pipeline.
-            #[inline]
-            pub fn push(&mut self, filter: Filter) {
-                self.filters.push(filter)
-            }
-
-            /// Returns true if this filter pipeline is empty and false otherwise.
-            #[inline]
-            pub fn is_empty(&self) -> bool {
-                self.filters.is_empty()
-            }
-
-            /// Returns the resulting opacity of this filter pipeline.
-            #[inline]
-            pub fn opacity(&self) -> CSSFloat {
-                let mut opacity = 1.0;
-
-                for filter in &self.filters {
-                    if let Filter::Opacity(ref opacity_value) = *filter {
-                        opacity *= *opacity_value
-                    }
-                }
-                opacity
-            }
-        }
-    }
-
-    impl ToCss for computed_value::T {
-        fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
-            let mut iter = self.filters.iter();
-            if let Some(filter) = iter.next() {
-                filter.to_css(dest)?;
-            } else {
-                dest.write_str("none")?;
-                return Ok(())
-            }
-            for filter in iter {
-                dest.write_str(" ")?;
-                filter.to_css(dest)?;
-            }
-            Ok(())
-        }
-    }
-
-    impl ToCss for SpecifiedValue {
-        fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
-            let mut iter = self.0.iter();
-            if let Some(filter) = iter.next() {
-                filter.to_css(dest)?;
-            } else {
-                dest.write_str("none")?;
-                return Ok(())
-            }
-            for filter in iter {
-                dest.write_str(" ")?;
-                filter.to_css(dest)?;
-            }
-            Ok(())
-        }
-    }
-
-    impl ToCss for computed_value::Filter {
-        fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
-            match *self {
-                computed_value::Filter::Blur(ref value) => {
-                    dest.write_str("blur(")?;
-                    value.to_css(dest)?;
-                    dest.write_str(")")?;
-                }
-                computed_value::Filter::Brightness(value) => write!(dest, "brightness({})", value)?,
-                computed_value::Filter::Contrast(value) => write!(dest, "contrast({})", value)?,
-                computed_value::Filter::Grayscale(value) => write!(dest, "grayscale({})", value)?,
-                computed_value::Filter::HueRotate(value) => {
-                    dest.write_str("hue-rotate(")?;
-                    value.to_css(dest)?;
-                    dest.write_str(")")?;
-                }
-                computed_value::Filter::Invert(value) => write!(dest, "invert({})", value)?,
-                computed_value::Filter::Opacity(value) => write!(dest, "opacity({})", value)?,
-                computed_value::Filter::Saturate(value) => write!(dest, "saturate({})", value)?,
-                computed_value::Filter::Sepia(value) => write!(dest, "sepia({})", value)?,
-                % if product == "gecko":
-                computed_value::Filter::DropShadow(shadow) => {
-                    dest.write_str("drop-shadow(")?;
-                    shadow.to_css(dest)?;
-                    dest.write_str(")")?;
-                }
-                computed_value::Filter::Url(ref url) => {
-                    url.to_css(dest)?;
-                }
-                % endif
-            }
-            Ok(())
-        }
-    }
-
-    impl ToCss for SpecifiedFilter {
-        fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
-            match *self {
-                SpecifiedFilter::Blur(ref value) => {
-                    dest.write_str("blur(")?;
-                    value.to_css(dest)?;
-                    dest.write_str(")")?;
-                }
-                SpecifiedFilter::Brightness(value) => write!(dest, "brightness({})", value)?,
-                SpecifiedFilter::Contrast(value) => write!(dest, "contrast({})", value)?,
-                SpecifiedFilter::Grayscale(value) => write!(dest, "grayscale({})", value)?,
-                SpecifiedFilter::HueRotate(value) => {
-                    dest.write_str("hue-rotate(")?;
-                    value.to_css(dest)?;
-                    dest.write_str(")")?;
-                }
-                SpecifiedFilter::Invert(value) => write!(dest, "invert({})", value)?,
-                SpecifiedFilter::Opacity(value) => write!(dest, "opacity({})", value)?,
-                SpecifiedFilter::Saturate(value) => write!(dest, "saturate({})", value)?,
-                SpecifiedFilter::Sepia(value) => write!(dest, "sepia({})", value)?,
-                % if product == "gecko":
-                SpecifiedFilter::DropShadow(ref shadow) => {
-                    dest.write_str("drop-shadow(")?;
-                    shadow.to_css(dest)?;
-                    dest.write_str(")")?;
-                }
-                SpecifiedFilter::Url(ref url) => {
-                    url.to_css(dest)?;
-                }
-                % endif
-            }
-            Ok(())
-        }
-    }
-
-    #[inline]
-    pub fn get_initial_value() -> computed_value::T {
-        computed_value::T::new(Vec::new())
-    }
-
-    pub fn parse<'i, 't>(context: &ParserContext, input: &mut Parser<'i, 't>)
-                         -> Result<SpecifiedValue, ParseError<'i>> {
-        let mut filters = Vec::new();
-        if input.try(|input| input.expect_ident_matching("none")).is_ok() {
-            return Ok(SpecifiedValue(filters))
-        }
-        loop {
-            % if product == "gecko":
-                if let Ok(url) = input.try(|i| SpecifiedUrl::parse(context, i)) {
-                    filters.push(SpecifiedFilter::Url(url));
-                } else
-            % endif
-            if let Ok(function_name) = input.try(|input| input.expect_function()) {
-                filters.push(input.parse_nested_block(|input| {
-                    match_ignore_ascii_case! { &function_name,
-                        "blur" => specified::Length::parse_non_negative(context, input).map(SpecifiedFilter::Blur),
-                        "brightness" => parse_factor(input).map(SpecifiedFilter::Brightness),
-                        "contrast" => parse_factor(input).map(SpecifiedFilter::Contrast),
-                        "grayscale" => parse_factor(input).map(SpecifiedFilter::Grayscale),
-                        "hue-rotate" => Angle::parse(context, input).map(SpecifiedFilter::HueRotate),
-                        "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" => specified::Shadow::parse(context, input, true)
-                                             .map(SpecifiedFilter::DropShadow),
-                        % endif
-                        _ => Err(StyleParseError::UnexpectedFunction(function_name.clone()).into())
-                    }
-                })?);
-            } else if filters.is_empty() {
-                return Err(StyleParseError::UnspecifiedError.into())
-            } else {
-                return Ok(SpecifiedValue(filters))
-            }
-        }
-    }
-
-    fn parse_factor<'i, 't>(input: &mut Parser<'i, 't>) -> Result<::values::CSSFloat, ParseError<'i>> {
-        use cssparser::Token;
-        match input.next() {
-            Ok(Token::Number { value, .. }) if value.is_sign_positive() => Ok(value),
-            Ok(Token::Percentage { unit_value, .. }) if unit_value.is_sign_positive() => Ok(unit_value),
-            Ok(t) => Err(BasicParseError::UnexpectedToken(t).into()),
-            Err(e) => Err(e.into())
-        }
-    }
-
-    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(ref factor) =>
-                        computed_value::Filter::Blur(factor.to_computed_value(context)),
-                    SpecifiedFilter::Brightness(factor) => computed_value::Filter::Brightness(factor),
-                    SpecifiedFilter::Contrast(factor) => computed_value::Filter::Contrast(factor),
-                    SpecifiedFilter::Grayscale(factor) => computed_value::Filter::Grayscale(factor),
-                    SpecifiedFilter::HueRotate(ref factor) => {
-                        computed_value::Filter::HueRotate(factor.to_computed_value(context))
-                    },
-                    SpecifiedFilter::Invert(factor) => computed_value::Filter::Invert(factor),
-                    SpecifiedFilter::Opacity(factor) => computed_value::Filter::Opacity(factor),
-                    SpecifiedFilter::Saturate(factor) => computed_value::Filter::Saturate(factor),
-                    SpecifiedFilter::Sepia(factor) => computed_value::Filter::Sepia(factor),
-                    % if product == "gecko":
-                    SpecifiedFilter::DropShadow(ref shadow) => {
-                        computed_value::Filter::DropShadow(shadow.to_computed_value(context))
-                    },
-                    SpecifiedFilter::Url(ref url) => {
-                        computed_value::Filter::Url(url.to_computed_value(context))
-                    }
-                    % endif
-                }
-            }).collect() }
-        }
-
-        fn from_computed_value(computed: &computed_value::T) -> Self {
-            SpecifiedValue(computed.filters.iter().map(|value| {
-                match *value {
-                    computed_value::Filter::Blur(factor) =>
-                        SpecifiedFilter::Blur(ToComputedValue::from_computed_value(&factor)),
-                    computed_value::Filter::Brightness(factor) => SpecifiedFilter::Brightness(factor),
-                    computed_value::Filter::Contrast(factor) => SpecifiedFilter::Contrast(factor),
-                    computed_value::Filter::Grayscale(factor) => SpecifiedFilter::Grayscale(factor),
-                    computed_value::Filter::HueRotate(ref factor) => {
-                        SpecifiedFilter::HueRotate(ToComputedValue::from_computed_value(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(ref shadow) => {
-                        SpecifiedFilter::DropShadow(
-                            ToComputedValue::from_computed_value(shadow),
-                        )
-                    }
-                    computed_value::Filter::Url(ref url) => {
-                        SpecifiedFilter::Url(
-                            ToComputedValue::from_computed_value(url),
-                        )
-                    }
-                    % endif
-                }
-            }).collect())
-        }
-    }
-</%helpers:longhand>
+${helpers.predefined_type(
+    "filter",
+    "FilterList",
+    "computed::FilterList::none()",
+    animation_value_type="AnimatedFilterList",
+    extra_prefixes="webkit",
+    flags="CREATES_STACKING_CONTEXT FIXPOS_CB",
+    spec="https://drafts.fxtf.org/filters/#propdef-filter",
+)}
 
 ${helpers.single_keyword("mix-blend-mode",
                          """normal multiply screen overlay darken lighten color-dodge
                             color-burn hard-light soft-light difference exclusion hue
                             saturation color luminosity""", gecko_constant_prefix="NS_STYLE_BLEND",
                          animation_value_type="discrete",
                          flags="CREATES_STACKING_CONTEXT",
                          spec="https://drafts.fxtf.org/compositing/#propdef-mix-blend-mode")}
--- a/servo/components/style/properties/properties.mako.rs
+++ b/servo/components/style/properties/properties.mako.rs
@@ -2059,17 +2059,17 @@ impl ComputedValues {
 
     /// Return true if the effects force the transform style to be Flat
     pub fn overrides_transform_style(&self) -> bool {
         use computed_values::mix_blend_mode;
 
         let effects = self.get_effects();
         // TODO(gw): Add clip-path, isolation, mask-image, mask-border-source when supported.
         effects.opacity < 1.0 ||
-           !effects.filter.is_empty() ||
+           !effects.filter.0.is_empty() ||
            !effects.clip.is_auto() ||
            effects.mix_blend_mode != mix_blend_mode::T::normal
     }
 
     /// https://drafts.csswg.org/css-transforms/#grouping-property-values
     pub fn get_used_transform_style(&self) -> computed_values::transform_style::T {
         use computed_values::transform_style;
 
new file mode 100644
--- /dev/null
+++ b/servo/components/style/values/animated/effects.rs
@@ -0,0 +1,202 @@
+/* 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/. */
+
+//! Animated types for CSS values related to effects.
+
+use properties::animated_properties::Animatable;
+#[cfg(feature = "gecko")]
+use properties::animated_properties::IntermediateColor;
+use values::computed::{Angle, Number};
+use values::computed::effects::DropShadow as ComputedDropShadow;
+use values::computed::effects::Filter as ComputedFilter;
+use values::computed::effects::FilterList as ComputedFilterList;
+use values::computed::length::Length;
+use values::generics::effects::Filter as GenericFilter;
+use values::generics::effects::FilterList as GenericFilterList;
+
+/// An animated value for the `filter` property.
+pub type FilterList = GenericFilterList<Filter>;
+
+/// An animated value for a single `filter`.
+pub type Filter = GenericFilter<
+    Angle,
+    // FIXME: Should be `NumberOrPercentage`.
+    Number,
+    Length,
+    DropShadow
+>;
+
+/// An animated value for the `drop-shadow()` filter.
+///
+/// Currently unsupported outside of Gecko.
+#[cfg(not(feature = "gecko"))]
+#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
+#[derive(Clone, Debug, PartialEq)]
+pub enum DropShadow {}
+
+/// An animated value for the `drop-shadow()` filter.
+///
+/// Contrary to the canonical order from the spec, the color is serialised
+/// first, like in Gecko and Webkit.
+#[cfg(feature = "gecko")]
+#[derive(Clone, Debug, PartialEq)]
+pub struct DropShadow {
+    /// Color.
+    pub color: IntermediateColor,
+    /// Horizontal radius.
+    pub horizontal: Length,
+    /// Vertical radius.
+    pub vertical: Length,
+    /// Blur radius.
+    pub blur: Length,
+}
+
+impl From<ComputedFilterList> for FilterList {
+    #[inline]
+    fn from(filters: ComputedFilterList) -> Self {
+        filters.0.into_vec().into_iter().map(|f| f.into()).collect::<Vec<_>>().into()
+    }
+}
+
+impl From<FilterList> for ComputedFilterList {
+    #[inline]
+    fn from(filters: FilterList) -> Self {
+        filters.0.into_vec().into_iter().map(|f| f.into()).collect::<Vec<_>>().into()
+    }
+}
+
+impl From<ComputedFilter> for Filter {
+    #[inline]
+    fn from(filter: ComputedFilter) -> Self {
+        match filter {
+            GenericFilter::Blur(angle) => GenericFilter::Blur(angle),
+            GenericFilter::Brightness(factor) => GenericFilter::Brightness(factor),
+            GenericFilter::Contrast(factor) => GenericFilter::Contrast(factor),
+            GenericFilter::Grayscale(factor) => GenericFilter::Grayscale(factor),
+            GenericFilter::HueRotate(factor) => GenericFilter::HueRotate(factor),
+            GenericFilter::Invert(factor) => GenericFilter::Invert(factor),
+            GenericFilter::Opacity(factor) => GenericFilter::Opacity(factor),
+            GenericFilter::Saturate(factor) => GenericFilter::Saturate(factor),
+            GenericFilter::Sepia(factor) => GenericFilter::Sepia(factor),
+            GenericFilter::DropShadow(shadow) => {
+                GenericFilter::DropShadow(shadow.into())
+            },
+            #[cfg(feature = "gecko")]
+            GenericFilter::Url(url) => GenericFilter::Url(url),
+        }
+    }
+}
+
+impl From<Filter> for ComputedFilter {
+    #[inline]
+    fn from(filter: Filter) -> Self {
+        match filter {
+            GenericFilter::Blur(angle) => GenericFilter::Blur(angle),
+            GenericFilter::Brightness(factor) => GenericFilter::Brightness(factor),
+            GenericFilter::Contrast(factor) => GenericFilter::Contrast(factor),
+            GenericFilter::Grayscale(factor) => GenericFilter::Grayscale(factor),
+            GenericFilter::HueRotate(factor) => GenericFilter::HueRotate(factor),
+            GenericFilter::Invert(factor) => GenericFilter::Invert(factor),
+            GenericFilter::Opacity(factor) => GenericFilter::Opacity(factor),
+            GenericFilter::Saturate(factor) => GenericFilter::Saturate(factor),
+            GenericFilter::Sepia(factor) => GenericFilter::Sepia(factor),
+            GenericFilter::DropShadow(shadow) => {
+                GenericFilter::DropShadow(shadow.into())
+            },
+            #[cfg(feature = "gecko")]
+            GenericFilter::Url(url) => GenericFilter::Url(url.clone())
+        }
+    }
+}
+
+impl From<ComputedDropShadow> for DropShadow {
+    #[cfg(not(feature = "gecko"))]
+    #[inline]
+    fn from(shadow: ComputedDropShadow) -> Self {
+        match shadow {}
+    }
+
+    #[cfg(feature = "gecko")]
+    #[inline]
+    fn from(shadow: ComputedDropShadow) -> Self {
+        DropShadow {
+            color: shadow.color.into(),
+            horizontal: shadow.horizontal,
+            vertical: shadow.vertical,
+            blur: shadow.blur,
+        }
+    }
+}
+
+impl From<DropShadow> for ComputedDropShadow {
+    #[cfg(not(feature = "gecko"))]
+    #[inline]
+    fn from(shadow: DropShadow) -> Self {
+        match shadow {}
+    }
+
+    #[cfg(feature = "gecko")]
+    #[inline]
+    fn from(shadow: DropShadow) -> Self {
+        ComputedDropShadow {
+            color: shadow.color.into(),
+            horizontal: shadow.horizontal,
+            vertical: shadow.vertical,
+            blur: shadow.blur,
+        }
+    }
+}
+
+impl Animatable for DropShadow {
+    #[cfg(not(feature = "gecko"))]
+    #[inline]
+    fn add_weighted(&self, _other: &Self, _self_portion: f64, _other_portion: f64) -> Result<Self, ()> {
+        match *self {}
+    }
+
+    #[cfg(feature = "gecko")]
+    #[inline]
+    fn add_weighted(&self, other: &Self, self_portion: f64, other_portion: f64) -> Result<Self, ()> {
+        let color = self.color.add_weighted(&other.color, self_portion, other_portion)?;
+        let horizontal = self.horizontal.add_weighted(&other.horizontal, self_portion, other_portion)?;
+        let vertical = self.vertical.add_weighted(&other.vertical, self_portion, other_portion)?;
+        let blur = self.blur.add_weighted(&other.blur, self_portion, other_portion)?;
+
+        Ok(DropShadow {
+            color: color,
+            horizontal: horizontal,
+            vertical: vertical,
+            blur: blur,
+        })
+    }
+
+    #[cfg(not(feature = "gecko"))]
+    #[inline]
+    fn compute_distance(&self, _other: &Self) -> Result<f64, ()> {
+        match *self {}
+    }
+
+    #[cfg(feature = "gecko")]
+    #[inline]
+    fn compute_distance(&self, other: &Self) -> Result<f64, ()> {
+        self.compute_squared_distance(other).map(|sd| sd.sqrt())
+    }
+
+    #[cfg(not(feature = "gecko"))]
+    #[inline]
+    fn compute_squared_distance(&self, _other: &Self) -> Result<f64, ()> {
+        match *self {}
+    }
+
+    #[cfg(feature = "gecko")]
+    #[inline]
+    fn compute_squared_distance(&self, other: &Self) -> Result<f64, ()> {
+        Ok(
+            self.color.compute_squared_distance(&other.color)? +
+            self.horizontal.compute_squared_distance(&other.horizontal)? +
+            self.vertical.compute_squared_distance(&other.vertical)? +
+            self.blur.compute_squared_distance(&other.blur)?
+        )
+    }
+}
new file mode 100644
--- /dev/null
+++ b/servo/components/style/values/animated/mod.rs
@@ -0,0 +1,11 @@
+/* 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/. */
+
+//! Animated values.
+//!
+//! Some values, notably colors, cannot be interpolated directly with their
+//! computed values and need yet another intermediate representation. This
+//! module's raison d'ĂȘtre is to ultimately contain all these types.
+
+pub mod effects;
new file mode 100644
--- /dev/null
+++ b/servo/components/style/values/computed/effects.rs
@@ -0,0 +1,62 @@
+/* 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 CSS values related to effects.
+
+use values::computed::{Angle, Number};
+#[cfg(feature = "gecko")]
+use values::computed::color::Color;
+use values::computed::length::Length;
+use values::generics::effects::Filter as GenericFilter;
+use values::generics::effects::FilterList as GenericFilterList;
+
+/// A computed value for the `filter` property.
+pub type FilterList = GenericFilterList<Filter>;
+
+/// A computed value for a single `filter`.
+pub type Filter = GenericFilter<
+    Angle,
+    // FIXME: Should be `NumberOrPercentage`.
+    Number,
+    Length,
+    DropShadow,
+>;
+
+/// A computed value for the `drop-shadow()` filter.
+///
+/// Currently unsupported outside of Gecko.
+#[cfg(not(feature = "gecko"))]
+#[cfg_attr(feature = "servo", derive(Deserialize, HeapSizeOf, Serialize))]
+#[derive(Clone, Debug, PartialEq, ToCss)]
+pub enum DropShadow {}
+
+/// A computed value for the `drop-shadow()` filter.
+///
+/// Contrary to the canonical order from the spec, the color is serialised
+/// first, like in Gecko and Webkit.
+#[cfg(feature = "gecko")]
+#[derive(Clone, Debug, PartialEq, ToCss)]
+pub struct DropShadow {
+    /// Color.
+    pub color: Color,
+    /// Horizontal radius.
+    pub horizontal: Length,
+    /// Vertical radius.
+    pub vertical: Length,
+    /// Blur radius.
+    pub blur: Length,
+}
+
+impl FilterList {
+    /// Returns the resulting opacity of this filter pipeline.
+    pub fn opacity(&self) -> Number {
+        let mut opacity = 0.;
+        for filter in &*self.0 {
+            if let GenericFilter::Opacity(factor) = *filter {
+                opacity *= factor
+            }
+        }
+        opacity
+    }
+}
--- a/servo/components/style/values/computed/mod.rs
+++ b/servo/components/style/values/computed/mod.rs
@@ -23,16 +23,17 @@ use super::generics::grid::TrackList as 
 use super::specified;
 
 pub use app_units::Au;
 pub use properties::animated_properties::TransitionProperty;
 pub use self::background::BackgroundSize;
 pub use self::border::{BorderImageSlice, BorderImageWidth, BorderImageSideWidth};
 pub use self::border::{BorderRadius, BorderCornerRadius};
 pub use self::color::{Color, RGBAColor};
+pub use self::effects::FilterList;
 pub use self::flex::FlexBasis;
 pub use self::image::{Gradient, GradientItem, ImageLayer, LineDirection, Image, ImageRect};
 #[cfg(feature = "gecko")]
 pub use self::gecko::ScrollSnapPoint;
 pub use self::rect::LengthOrNumberRect;
 pub use super::{Auto, Either, None_};
 #[cfg(feature = "gecko")]
 pub use super::specified::{AlignItems, AlignJustifyContent, AlignJustifySelf, JustifyItems};
@@ -44,16 +45,17 @@ pub use self::length::{LengthOrPercentag
 pub use self::position::Position;
 pub use self::text::{InitialLetter, LetterSpacing, LineHeight, WordSpacing};
 pub use self::transform::{TimingFunction, TransformOrigin};
 
 pub mod background;
 pub mod basic_shape;
 pub mod border;
 pub mod color;
+pub mod effects;
 pub mod flex;
 pub mod image;
 #[cfg(feature = "gecko")]
 pub mod gecko;
 pub mod length;
 pub mod position;
 pub mod rect;
 pub mod text;
@@ -244,16 +246,32 @@ impl<T> ToComputedValue for Vec<T>
     }
 
     #[inline]
     fn from_computed_value(computed: &Self::ComputedValue) -> Self {
         computed.iter().map(T::from_computed_value).collect()
     }
 }
 
+impl<T> ToComputedValue for Box<[T]>
+    where T: ToComputedValue
+{
+    type ComputedValue = Box<[<T as ToComputedValue>::ComputedValue]>;
+
+    #[inline]
+    fn to_computed_value(&self, context: &Context) -> Self::ComputedValue {
+        self.iter().map(|item| item.to_computed_value(context)).collect::<Vec<_>>().into_boxed_slice()
+    }
+
+    #[inline]
+    fn from_computed_value(computed: &Self::ComputedValue) -> Self {
+        computed.iter().map(T::from_computed_value).collect::<Vec<_>>().into_boxed_slice()
+    }
+}
+
 /// A marker trait to represent that the specified value is also the computed
 /// value.
 pub trait ComputedValueAsSpecified {}
 
 impl<T> ToComputedValue for T
     where T: ComputedValueAsSpecified + Clone,
 {
     type ComputedValue = T;
new file mode 100644
--- /dev/null
+++ b/servo/components/style/values/generics/counters.rs
@@ -0,0 +1,51 @@
+/* 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 counters-related CSS values.
+
+use std::fmt;
+use style_traits::{HasViewportPercentage, ToCss};
+use values::CustomIdent;
+
+/// A generic value for the `counter-increment` property.
+///
+/// Keyword `none` is represented by an empty slice.
+#[derive(Clone, Debug, PartialEq, ToComputedValue)]
+pub struct CounterIncrement<Integer>(Box<[(CustomIdent, Integer)]);
+
+impl<I> CounterIncrement<I> {
+    /// Returns `none`.
+    #[inline]
+    pub fn none() -> Self {
+        CounterIncrement(vec![].into_boxed_slice())
+    }
+}
+
+impl<I> HasViewportPercentage for CounterIncrement<I> {
+    #[inline] fn has_viewport_percentage(&self) -> bool { false }
+}
+
+impl<I> ToCss for CounterIncrement<I>
+where
+    I: ToCss,
+{
+    #[inline]
+    fn to_css<W>(&self, dest: &mut W) -> fmt::Result
+    where
+        W: fmt::Write,
+    {
+        if self.0.is_empty() {
+            return dest.write_str("none");
+        }
+        for (&(ref name, ref value), i) in self.0.iter().enumerate() {
+            if i != 0 {
+                dest.write_str(" ")?;
+            }
+            name.to_css(dest)?;
+            dest.write_str(" ")?;
+            value.to_css(dest)?;
+        }
+        Ok(())
+    }
+}
new file mode 100644
--- /dev/null
+++ b/servo/components/style/values/generics/effects.rs
@@ -0,0 +1,92 @@
+/* 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 CSS values related to effects.
+
+use std::fmt;
+use style_traits::ToCss;
+#[cfg(feature = "gecko")]
+use values::specified::url::SpecifiedUrl;
+
+/// A generic value for the `filter` property.
+///
+/// Keyword `none` is represented by an empty slice.
+#[cfg_attr(feature = "servo", derive(Deserialize, HeapSizeOf, Serialize))]
+#[derive(Clone, Debug, HasViewportPercentage, PartialEq, ToComputedValue)]
+pub struct FilterList<Filter>(pub Box<[Filter]>);
+
+/// A generic value for a single `filter`.
+#[cfg_attr(feature = "servo", derive(Deserialize, HeapSizeOf, Serialize))]
+#[derive(Clone, Debug, HasViewportPercentage, PartialEq, ToComputedValue, ToCss)]
+pub enum Filter<Angle, Factor, Length, DropShadow> {
+    /// `blur(<length>)`
+    #[css(function)]
+    Blur(Length),
+    /// `brightness(<factor>)`
+    #[css(function)]
+    Brightness(Factor),
+    /// `contrast(<factor>)`
+    #[css(function)]
+    Contrast(Factor),
+    /// `grayscale(<factor>)`
+    #[css(function)]
+    Grayscale(Factor),
+    /// `hue-rotate(<angle>)`
+    #[css(function)]
+    HueRotate(Angle),
+    /// `invert(<factor>)`
+    #[css(function)]
+    Invert(Factor),
+    /// `opacity(<factor>)`
+    #[css(function)]
+    Opacity(Factor),
+    /// `saturate(<factor>)`
+    #[css(function)]
+    Saturate(Factor),
+    /// `sepia(<factor>)`
+    #[css(function)]
+    Sepia(Factor),
+    /// `drop-shadow(...)`
+    #[css(function)]
+    DropShadow(DropShadow),
+    /// `<url>`
+    #[cfg(feature = "gecko")]
+    Url(SpecifiedUrl),
+}
+
+impl<F> FilterList<F> {
+    /// Returns `none`.
+    #[inline]
+    pub fn none() -> Self {
+        FilterList(vec![].into_boxed_slice())
+    }
+}
+
+impl<F> From<Vec<F>> for FilterList<F> {
+    #[inline]
+    fn from(vec: Vec<F>) -> Self {
+        FilterList(vec.into_boxed_slice())
+    }
+}
+
+impl<F> ToCss for FilterList<F>
+where
+    F: ToCss,
+{
+    fn to_css<W>(&self, dest: &mut W) -> fmt::Result
+    where
+        W: fmt::Write
+    {
+        if let Some((first, rest)) = self.0.split_first() {
+            first.to_css(dest)?;
+            for filter in rest {
+                dest.write_str(" ")?;
+                filter.to_css(dest)?;
+            }
+            Ok(())
+        } else {
+            dest.write_str("none")
+        }
+    }
+}
--- a/servo/components/style/values/generics/mod.rs
+++ b/servo/components/style/values/generics/mod.rs
@@ -11,16 +11,17 @@ use parser::{Parse, ParserContext};
 use std::fmt;
 use style_traits::{OneOrMoreSeparated, CommaSeparator, ToCss, ParseError, StyleParseError};
 use super::CustomIdent;
 use values::specified::url::SpecifiedUrl;
 
 pub mod background;
 pub mod basic_shape;
 pub mod border;
+pub mod effects;
 pub mod flex;
 #[cfg(feature = "gecko")]
 pub mod gecko;
 pub mod grid;
 pub mod image;
 pub mod position;
 pub mod rect;
 pub mod text;
--- a/servo/components/style/values/mod.rs
+++ b/servo/components/style/values/mod.rs
@@ -13,16 +13,17 @@ pub use cssparser::{RGBA, Token, Parser,
 use parser::{Parse, ParserContext};
 use selectors::parser::SelectorParseError;
 use std::ascii::AsciiExt;
 use std::borrow::Cow;
 use std::fmt::{self, Debug};
 use std::hash;
 use style_traits::{ToCss, ParseError, StyleParseError};
 
+pub mod animated;
 pub mod computed;
 pub mod generics;
 pub mod specified;
 
 /// A CSS float value.
 pub type CSSFloat = f32;
 
 /// A CSS integer value.
new file mode 100644
--- /dev/null
+++ b/servo/components/style/values/specified/effects.rs
@@ -0,0 +1,244 @@
+/* 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/. */
+
+//! Specified types for CSS values related to effects.
+
+use cssparser::{BasicParseError, Parser, Token};
+use parser::{Parse, ParserContext};
+#[cfg(feature = "gecko")]
+use std::fmt;
+use style_traits::ParseError;
+#[cfg(not(feature = "gecko"))]
+use style_traits::StyleParseError;
+#[cfg(feature = "gecko")]
+use style_traits::ToCss;
+use values::computed::{Context, Number as ComputedNumber, ToComputedValue};
+use values::computed::effects::DropShadow as ComputedDropShadow;
+use values::generics::effects::Filter as GenericFilter;
+use values::generics::effects::FilterList as GenericFilterList;
+use values::specified::{Angle, Percentage};
+#[cfg(feature = "gecko")]
+use values::specified::color::Color;
+use values::specified::length::Length;
+#[cfg(feature = "gecko")]
+use values::specified::url::SpecifiedUrl;
+
+/// A specified value for the `filter` property.
+pub type FilterList = GenericFilterList<Filter>;
+
+/// A specified value for a single `filter`.
+pub type Filter = GenericFilter<Angle, Factor, Length, DropShadow>;
+
+/// A value for the `<factor>` parts in `Filter`.
+///
+/// FIXME: Should be `NumberOrPercentage`, but Gecko doesn't support that yet.
+#[cfg_attr(feature = "servo", derive(Deserialize, HeapSizeOf, Serialize))]
+#[derive(Clone, Debug, HasViewportPercentage, PartialEq, ToCss)]
+pub enum Factor {
+    /// Literal number.
+    Number(ComputedNumber),
+    /// Literal percentage.
+    Percentage(Percentage),
+}
+
+/// A specified value for the `drop-shadow()` filter.
+///
+/// Currently unsupported outside of Gecko.
+#[cfg(not(feature = "gecko"))]
+#[cfg_attr(feature = "servo", derive(Deserialize, HeapSizeOf, Serialize))]
+#[derive(Clone, Debug, HasViewportPercentage, PartialEq, ToCss)]
+pub enum DropShadow {}
+
+/// A specified value for the `drop-shadow()` filter.
+///
+/// Contrary to the canonical order from the spec, the color is serialised
+/// first, like in Gecko's computed values and in all Webkit's values.
+#[cfg(feature = "gecko")]
+#[derive(Clone, Debug, HasViewportPercentage, PartialEq)]
+pub struct DropShadow {
+    /// Color.
+    pub color: Option<Color>,
+    /// Horizontal radius.
+    pub horizontal: Length,
+    /// Vertical radius.
+    pub vertical: Length,
+    /// Blur radius.
+    pub blur: Option<Length>,
+}
+
+impl Parse for FilterList {
+    #[inline]
+    fn parse<'i, 't>(
+        context: &ParserContext,
+        input: &mut Parser<'i, 't>
+    ) -> Result<Self, ParseError<'i>> {
+        let mut filters = vec![];
+        while let Ok(filter) = input.try(|i| Filter::parse(context, i)) {
+            filters.push(filter);
+        }
+        if filters.is_empty() {
+            input.expect_ident_matching("none")?;
+        }
+        Ok(GenericFilterList(filters.into_boxed_slice()))
+    }
+}
+
+impl Parse for Filter {
+    #[inline]
+    fn parse<'i, 't>(
+        context: &ParserContext,
+        input: &mut Parser<'i, 't>
+    ) -> Result<Self, ParseError<'i>> {
+        #[cfg(feature = "gecko")]
+        {
+            if let Ok(url) = input.try(|i| SpecifiedUrl::parse(context, i)) {
+                return Ok(GenericFilter::Url(url));
+            }
+        }
+        let function = input.expect_function()?;
+        input.parse_nested_block(|i| {
+            try_match_ident_ignore_ascii_case! { function,
+                "blur" => Ok(GenericFilter::Blur(Length::parse_non_negative(context, i)?)),
+                "brightness" => Ok(GenericFilter::Brightness(Factor::parse(context, i)?)),
+                "contrast" => Ok(GenericFilter::Contrast(Factor::parse(context, i)?)),
+                "grayscale" => Ok(GenericFilter::Grayscale(Factor::parse(context, i)?)),
+                "hue-rotate" => Ok(GenericFilter::HueRotate(Angle::parse(context, i)?)),
+                "invert" => Ok(GenericFilter::Invert(Factor::parse(context, i)?)),
+                "opacity" => Ok(GenericFilter::Opacity(Factor::parse(context, i)?)),
+                "saturate" => Ok(GenericFilter::Saturate(Factor::parse(context, i)?)),
+                "sepia" => Ok(GenericFilter::Sepia(Factor::parse(context, i)?)),
+                "drop-shadow" => Ok(GenericFilter::DropShadow(DropShadow::parse(context, i)?)),
+            }
+        })
+    }
+}
+
+impl Parse for Factor {
+    #[inline]
+    fn parse<'i, 't>(
+        _context: &ParserContext,
+        input: &mut Parser<'i, 't>
+    ) -> Result<Self, ParseError<'i>> {
+        match input.next()? {
+            Token::Number { value, .. } if value.is_sign_positive() => {
+                Ok(Factor::Number(value))
+            },
+            Token::Percentage { unit_value, .. } if unit_value.is_sign_positive() => {
+                Ok(Factor::Percentage(Percentage(unit_value)))
+            },
+            other => Err(BasicParseError::UnexpectedToken(other).into()),
+        }
+    }
+}
+
+impl ToComputedValue for Factor {
+    /// This should actually be `ComputedNumberOrPercentage`, but layout uses
+    /// `computed::effects::FilterList` directly in `StackingContext`.
+    type ComputedValue = ComputedNumber;
+
+    #[inline]
+    fn to_computed_value(&self, _context: &Context) -> Self::ComputedValue {
+        match *self {
+            Factor::Number(number) => number,
+            Factor::Percentage(percentage) => percentage.0,
+        }
+    }
+
+    #[inline]
+    fn from_computed_value(computed: &Self::ComputedValue) -> Self {
+        Factor::Number(*computed)
+    }
+}
+
+impl Parse for DropShadow {
+    #[cfg(not(feature = "gecko"))]
+    #[inline]
+    fn parse<'i, 't>(
+        _context: &ParserContext,
+        _input: &mut Parser<'i, 't>
+    ) -> Result<Self, ParseError<'i>> {
+        Err(StyleParseError::UnspecifiedError.into())
+    }
+
+    #[cfg(feature = "gecko")]
+    #[inline]
+    fn parse<'i, 't>(
+        context: &ParserContext,
+        input: &mut Parser<'i, 't>
+    ) -> Result<Self, ParseError<'i>> {
+        let color = input.try(|i| Color::parse(context, i)).ok();
+        let horizontal = Length::parse(context, input)?;
+        let vertical = Length::parse(context, input)?;
+        let blur = input.try(|i| Length::parse_non_negative(context, i)).ok();
+        let color = color.or_else(|| input.try(|i| Color::parse(context, i)).ok());
+        Ok(DropShadow {
+            color: color,
+            horizontal: horizontal,
+            vertical: vertical,
+            blur: blur,
+        })
+    }
+}
+
+impl ToComputedValue for DropShadow {
+    type ComputedValue = ComputedDropShadow;
+
+    #[cfg(not(feature = "gecko"))]
+    #[inline]
+    fn to_computed_value(&self, _context: &Context) -> Self::ComputedValue {
+        match *self {}
+    }
+
+    #[cfg(feature = "gecko")]
+    #[inline]
+    fn to_computed_value(&self, context: &Context) -> Self::ComputedValue {
+        ComputedDropShadow {
+            color:
+                self.color.as_ref().unwrap_or(&Color::CurrentColor).to_computed_value(context),
+            horizontal: self.horizontal.to_computed_value(context),
+            vertical: self.vertical.to_computed_value(context),
+            blur:
+                self.blur.as_ref().unwrap_or(&Length::zero()).to_computed_value(context),
+        }
+    }
+
+    #[cfg(not(feature = "gecko"))]
+    #[inline]
+    fn from_computed_value(computed: &Self::ComputedValue) -> Self {
+        match *computed {}
+    }
+
+    #[cfg(feature = "gecko")]
+    #[inline]
+    fn from_computed_value(computed: &Self::ComputedValue) -> Self {
+        DropShadow {
+            color: Some(ToComputedValue::from_computed_value(&computed.color)),
+            horizontal: ToComputedValue::from_computed_value(&computed.horizontal),
+            vertical: ToComputedValue::from_computed_value(&computed.vertical),
+            blur: Some(ToComputedValue::from_computed_value(&computed.blur)),
+        }
+    }
+}
+
+#[cfg(feature = "gecko")]
+impl ToCss for DropShadow {
+    #[inline]
+    fn to_css<W>(&self, dest: &mut W) -> fmt::Result
+    where
+        W: fmt::Write,
+    {
+        if let Some(ref color) = self.color {
+            color.to_css(dest)?;
+            dest.write_str(" ")?;
+        }
+        self.horizontal.to_css(dest)?;
+        dest.write_str(" ")?;
+        self.vertical.to_css(dest)?;
+        if let Some(ref blur) = self.blur {
+            dest.write_str(" ")?;
+            blur.to_css(dest)?;
+        }
+        Ok(())
+    }
+}
--- a/servo/components/style/values/specified/length.rs
+++ b/servo/components/style/values/specified/length.rs
@@ -699,17 +699,17 @@ impl<T: Parse> Either<Length, T> {
 /// to add special handling for `calc()` and percentages in here in the same way
 /// as for `Angle` and `Time`, but the lack of this this is otherwise
 /// undistinguishable (we handle it correctly from `CalcLengthOrPercentage`).
 ///
 /// As of today, only `-moz-image-rect` supports percentages without length.
 /// This is not a regression, and that's a non-standard extension anyway, so I'm
 /// not implementing it for now.
 #[derive(Clone, Copy, Debug, Default, HasViewportPercentage, PartialEq)]
-#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
+#[cfg_attr(feature = "servo", derive(Deserialize, HeapSizeOf, Serialize))]
 pub struct Percentage(pub CSSFloat);
 
 impl ToCss for Percentage {
     fn to_css<W>(&self, dest: &mut W) -> fmt::Result
         where W: fmt::Write,
     {
         write!(dest, "{}%", self.0 * 100.)
     }
--- a/servo/components/style/values/specified/mod.rs
+++ b/servo/components/style/values/specified/mod.rs
@@ -28,16 +28,17 @@ use values::specified::calc::CalcNode;
 
 pub use properties::animated_properties::TransitionProperty;
 #[cfg(feature = "gecko")]
 pub use self::align::{AlignItems, AlignJustifyContent, AlignJustifySelf, JustifyItems};
 pub use self::background::BackgroundSize;
 pub use self::border::{BorderCornerRadius, BorderImageSlice, BorderImageWidth};
 pub use self::border::{BorderImageSideWidth, BorderRadius, BorderSideWidth};
 pub use self::color::{Color, RGBAColor};
+pub use self::effects::FilterList;
 pub use self::flex::FlexBasis;
 #[cfg(feature = "gecko")]
 pub use self::gecko::ScrollSnapPoint;
 pub use self::image::{ColorStop, EndingShape as GradientEndingShape, Gradient};
 pub use self::image::{GradientItem, GradientKind, Image, ImageRect, ImageLayer};
 pub use self::length::{AbsoluteLength, CalcLengthOrPercentage, CharacterWidth};
 pub use self::length::{FontRelativeLength, Length, LengthOrNone, LengthOrNumber};
 pub use self::length::{LengthOrPercentage, LengthOrPercentageOrAuto};
@@ -51,16 +52,17 @@ pub use super::generics::grid::GridLine;
 
 #[cfg(feature = "gecko")]
 pub mod align;
 pub mod background;
 pub mod basic_shape;
 pub mod border;
 pub mod calc;
 pub mod color;
+pub mod effects;
 pub mod flex;
 #[cfg(feature = "gecko")]
 pub mod gecko;
 pub mod grid;
 pub mod image;
 pub mod length;
 pub mod position;
 pub mod rect;
--- a/servo/components/style_traits/viewport.rs
+++ b/servo/components/style_traits/viewport.rs
@@ -36,17 +36,20 @@ macro_rules! no_viewport_percentage {
                 false
             }
         })+
     };
 }
 
 no_viewport_percentage!(bool, f32);
 
-impl<T: HasViewportPercentage> HasViewportPercentage for Box<T> {
+impl<T> HasViewportPercentage for Box<T>
+where
+    T: ?Sized + HasViewportPercentage
+{
     #[inline]
     fn has_viewport_percentage(&self) -> bool {
         (**self).has_viewport_percentage()
     }
 }
 
 impl<T: HasViewportPercentage> HasViewportPercentage for Option<T> {
     #[inline]
@@ -64,16 +67,23 @@ impl<T: HasViewportPercentage, U> HasVie
 
 impl<T: HasViewportPercentage> HasViewportPercentage for Vec<T> {
     #[inline]
     fn has_viewport_percentage(&self) -> bool {
         self.iter().any(T::has_viewport_percentage)
     }
 }
 
+impl<T: HasViewportPercentage> HasViewportPercentage for [T] {
+    #[inline]
+    fn has_viewport_percentage(&self) -> bool {
+        self.iter().any(T::has_viewport_percentage)
+    }
+}
+
 /// A set of viewport descriptors:
 ///
 /// https://drafts.csswg.org/css-device-adapt/#viewport-desc
 #[derive(Clone, Debug, PartialEq)]
 #[cfg_attr(feature = "servo", derive(Deserialize, Serialize, HeapSizeOf))]
 pub struct ViewportConstraints {
     /// Width and height:
     ///  * https://drafts.csswg.org/css-device-adapt/#width-desc