Bug 1552878 - Use cbindgen for filters. r=jwatt
authorEmilio Cobos Álvarez <emilio@crisal.io>
Mon, 27 May 2019 12:37:37 +0000
changeset 476113 5715b3107ce3c7d0e87f0781a2fb5a38eceb1355
parent 476112 388ab8151b4d5e797b6cde9a92a8be7e58836d2b
child 476114 de1986c0ec6176677f05237338304bdcbffc84a3
push id113250
push usershindli@mozilla.com
push dateThu, 30 May 2019 03:52:44 +0000
treeherdermozilla-inbound@cabf1bb20b97 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjwatt
bugs1552878
milestone69.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
Bug 1552878 - Use cbindgen for filters. r=jwatt Had to implement some OwnedSlice bits that the canvas code used. Differential Revision: https://phabricator.services.mozilla.com/D31799
dom/canvas/CanvasRenderingContext2D.cpp
dom/canvas/CanvasRenderingContext2D.h
layout/painting/nsDisplayList.cpp
layout/style/GeckoBindings.cpp
layout/style/GeckoBindings.h
layout/style/ServoBindings.toml
layout/style/ServoStyleConstsInlines.h
layout/style/nsCSSProps.cpp
layout/style/nsCSSProps.h
layout/style/nsComputedDOMStyle.h
layout/style/nsStyleConsts.h
layout/style/nsStyleStruct.cpp
layout/style/nsStyleStruct.h
layout/svg/SVGObserverUtils.cpp
layout/svg/SVGObserverUtils.h
layout/svg/nsCSSFilterInstance.cpp
layout/svg/nsCSSFilterInstance.h
layout/svg/nsFilterInstance.cpp
layout/svg/nsFilterInstance.h
layout/svg/nsSVGFilterInstance.cpp
layout/svg/nsSVGFilterInstance.h
servo/components/style/gecko/url.rs
servo/components/style/properties/gecko.mako.rs
servo/components/style/properties/helpers/animated_properties.mako.rs
servo/components/style/properties/longhands/effects.mako.rs
servo/components/style/values/animated/effects.rs
servo/components/style/values/computed/effects.rs
servo/components/style/values/generics/effects.rs
servo/components/style/values/specified/effects.rs
servo/ports/geckolib/cbindgen.toml
--- a/dom/canvas/CanvasRenderingContext2D.cpp
+++ b/dom/canvas/CanvasRenderingContext2D.cpp
@@ -2262,17 +2262,17 @@ static already_AddRefed<ComputedStyle> R
   ServoStyleSet* styleSet = aPresShell->StyleSet();
   RefPtr<ComputedStyle> computedValues =
       styleSet->ResolveForDeclarations(aParentStyle, declarations);
 
   return computedValues.forget();
 }
 
 bool CanvasRenderingContext2D::ParseFilter(
-    const nsAString& aString, nsTArray<nsStyleFilter>& aFilterChain,
+    const nsAString& aString, StyleOwnedSlice<StyleFilter>& aFilterChain,
     ErrorResult& aError) {
   if (!mCanvasElement && !mDocShell) {
     NS_WARNING(
         "Canvas element must be non-null or a docshell must be provided");
     aError.Throw(NS_ERROR_FAILURE);
     return false;
   }
 
@@ -2297,24 +2297,24 @@ bool CanvasRenderingContext2D::ParseFilt
   }
 
   aFilterChain = style->StyleEffects()->mFilters;
   return true;
 }
 
 void CanvasRenderingContext2D::SetFilter(const nsAString& aFilter,
                                          ErrorResult& aError) {
-  nsTArray<nsStyleFilter> filterChain;
+  StyleOwnedSlice<StyleFilter> filterChain;
   if (ParseFilter(aFilter, filterChain, aError)) {
     CurrentState().filterString = aFilter;
-    filterChain.SwapElements(CurrentState().filterChain);
+    CurrentState().filterChain = std::move(filterChain);
     if (mCanvasElement) {
       CurrentState().autoSVGFiltersObserver =
           SVGObserverUtils::ObserveFiltersForCanvasContext(
-              this, mCanvasElement, CurrentState().filterChain);
+              this, mCanvasElement, CurrentState().filterChain.AsSpan());
       UpdateFilter();
     }
   }
 }
 
 class CanvasUserSpaceMetrics : public UserSpaceMetricsWithSize {
  public:
   CanvasUserSpaceMetrics(const gfx::IntSize& aSize, const nsFont& aFont,
@@ -2371,17 +2371,18 @@ void CanvasRenderingContext2D::UpdateFil
   if (MOZ_UNLIKELY(presShell->IsDestroying())) {
     return;
   }
 
   bool sourceGraphicIsTainted =
       (mCanvasElement && mCanvasElement->IsWriteOnly());
 
   CurrentState().filter = nsFilterInstance::GetFilterDescription(
-      mCanvasElement, CurrentState().filterChain, sourceGraphicIsTainted,
+      mCanvasElement, CurrentState().filterChain.AsSpan(),
+      sourceGraphicIsTainted,
       CanvasUserSpaceMetrics(
           GetSize(), CurrentState().fontFont, CurrentState().fontLanguage,
           CurrentState().fontExplicitLanguage, presShell->GetPresContext()),
       gfxRect(0, 0, mWidth, mHeight), CurrentState().filterAdditionalImages);
   CurrentState().filterSourceGraphicTainted = sourceGraphicIsTainted;
 }
 
 //
--- a/dom/canvas/CanvasRenderingContext2D.h
+++ b/dom/canvas/CanvasRenderingContext2D.h
@@ -561,17 +561,18 @@ class CanvasRenderingContext2D final : p
 
   // Returns whether a color was successfully parsed.
   bool ParseColor(const nsAString& aString, nscolor* aColor);
 
   static void StyleColorToString(const nscolor& aColor, nsAString& aStr);
 
   // Returns whether a filter was successfully parsed.
   bool ParseFilter(const nsAString& aString,
-                   nsTArray<nsStyleFilter>& aFilterChain, ErrorResult& aError);
+                   StyleOwnedSlice<StyleFilter>& aFilterChain,
+                   ErrorResult& aError);
 
   // Returns whether the font was successfully updated.
   bool SetFontInternal(const nsAString& aFont, mozilla::ErrorResult& aError);
 
   // Clears the target and updates mOpaque based on mOpaqueAttrValue and
   // mContextAttributesHasAlpha.
   void UpdateIsOpaque();
 
@@ -961,17 +962,17 @@ class CanvasRenderingContext2D final : p
     mozilla::gfx::Float dashOffset = 0.0f;
 
     mozilla::gfx::CompositionOp op = mozilla::gfx::CompositionOp::OP_OVER;
     mozilla::gfx::FillRule fillRule = mozilla::gfx::FillRule::FILL_WINDING;
     mozilla::gfx::CapStyle lineCap = mozilla::gfx::CapStyle::BUTT;
     mozilla::gfx::JoinStyle lineJoin = mozilla::gfx::JoinStyle::MITER_OR_BEVEL;
 
     nsString filterString = nsString(u"none");
-    nsTArray<nsStyleFilter> filterChain;
+    StyleOwnedSlice<StyleFilter> filterChain;
     // RAII object that we obtain when we start to observer SVG filter elements
     // for rendering changes.  When released we stop observing the SVG elements.
     nsCOMPtr<nsISupports> autoSVGFiltersObserver;
     mozilla::gfx::FilterDescription filter;
     nsTArray<RefPtr<mozilla::gfx::SourceSurface>> filterAdditionalImages;
 
     // This keeps track of whether the canvas was "tainted" or not when
     // we last used a filter. This is a security measure, whereby the
--- a/layout/painting/nsDisplayList.cpp
+++ b/layout/painting/nsDisplayList.cpp
@@ -10017,94 +10017,86 @@ void nsDisplayFilters::PaintAsLayer(nsDi
   nsDisplayFiltersGeometry::UpdateDrawResult(this, imgParams.result);
 }
 
 static float ClampStdDeviation(float aStdDeviation) {
   // Cap software blur radius for performance reasons.
   return std::min(std::max(0.0f, aStdDeviation), 100.0f);
 }
 
-bool nsDisplayFilters::CreateWebRenderCSSFilters(WrFiltersHolder& wrFilters) {
+bool nsDisplayFilters::CreateWebRenderCSSFilters(WrFiltersHolder& aWrFilters) {
   // All CSS filters are supported by WebRender. SVG filters are not fully
   // supported, those use NS_STYLE_FILTER_URL and are handled separately.
-  const nsTArray<nsStyleFilter>& filters = mFrame->StyleEffects()->mFilters;
+  Span<const StyleFilter> filters = mFrame->StyleEffects()->mFilters.AsSpan();
 
   // If there are too many filters to render, then just pretend that we
   // succeeded, and don't render any of them.
   if (filters.Length() > StaticPrefs::WebRenderMaxFilterOpsPerChain()) {
     return true;
   }
-  wrFilters.filters.SetCapacity(filters.Length());
-
-  for (const nsStyleFilter& filter : filters) {
-    switch (filter.GetType()) {
-      case NS_STYLE_FILTER_BRIGHTNESS:
-        wrFilters.filters.AppendElement(wr::FilterOp::Brightness(
-            filter.GetFilterParameter().GetFactorOrPercentValue()));
-        break;
-      case NS_STYLE_FILTER_CONTRAST:
-        wrFilters.filters.AppendElement(wr::FilterOp::Contrast(
-            filter.GetFilterParameter().GetFactorOrPercentValue()));
+  aWrFilters.filters.SetCapacity(filters.Length());
+  auto& wrFilters = aWrFilters.filters;
+  for (const StyleFilter& filter : filters) {
+    switch (filter.tag) {
+      case StyleFilter::Tag::Brightness:
+        wrFilters.AppendElement(
+            wr::FilterOp::Brightness(filter.AsBrightness()));
         break;
-      case NS_STYLE_FILTER_GRAYSCALE:
-        wrFilters.filters.AppendElement(wr::FilterOp::Grayscale(
-            filter.GetFilterParameter().GetFactorOrPercentValue()));
+      case StyleFilter::Tag::Contrast:
+        wrFilters.AppendElement(wr::FilterOp::Contrast(filter.AsContrast()));
+        break;
+      case StyleFilter::Tag::Grayscale:
+        wrFilters.AppendElement(wr::FilterOp::Grayscale(filter.AsGrayscale()));
         break;
-      case NS_STYLE_FILTER_INVERT:
-        wrFilters.filters.AppendElement(wr::FilterOp::Invert(
-            filter.GetFilterParameter().GetFactorOrPercentValue()));
+      case StyleFilter::Tag::Invert:
+        wrFilters.AppendElement(wr::FilterOp::Invert(filter.AsInvert()));
         break;
-      case NS_STYLE_FILTER_OPACITY: {
-        float opacity = filter.GetFilterParameter().GetFactorOrPercentValue();
-        wrFilters.filters.AppendElement(wr::FilterOp::Opacity(
+      case StyleFilter::Tag::Opacity: {
+        float opacity = filter.AsOpacity();
+        wrFilters.AppendElement(wr::FilterOp::Opacity(
             wr::PropertyBinding<float>::Value(opacity), opacity));
         break;
       }
-      case NS_STYLE_FILTER_SATURATE:
-        wrFilters.filters.AppendElement(wr::FilterOp::Saturate(
-            filter.GetFilterParameter().GetFactorOrPercentValue()));
+      case StyleFilter::Tag::Saturate:
+        wrFilters.AppendElement(wr::FilterOp::Saturate(filter.AsSaturate()));
         break;
-      case NS_STYLE_FILTER_SEPIA: {
-        wrFilters.filters.AppendElement(wr::FilterOp::Sepia(
-            filter.GetFilterParameter().GetFactorOrPercentValue()));
+      case StyleFilter::Tag::Sepia:
+        wrFilters.AppendElement(wr::FilterOp::Sepia(filter.AsSepia()));
         break;
-      }
-      case NS_STYLE_FILTER_HUE_ROTATE: {
-        wrFilters.filters.AppendElement(wr::FilterOp::HueRotate(
-            (float)filter.GetFilterParameter().GetAngleValueInDegrees()));
+      case StyleFilter::Tag::HueRotate: {
+        wrFilters.AppendElement(
+            wr::FilterOp::HueRotate(filter.AsHueRotate().ToDegrees()));
         break;
       }
-      case NS_STYLE_FILTER_BLUR: {
+      case StyleFilter::Tag::Blur: {
+        // TODO(emilio): we should go directly from css pixels -> device pixels.
         float appUnitsPerDevPixel =
             mFrame->PresContext()->AppUnitsPerDevPixel();
-        wrFilters.filters.AppendElement(mozilla::wr::FilterOp::Blur(
+        wrFilters.AppendElement(mozilla::wr::FilterOp::Blur(
             ClampStdDeviation(NSAppUnitsToFloatPixels(
-                filter.GetFilterParameter().GetCoordValue(),
-                appUnitsPerDevPixel))));
+                filter.AsBlur().ToAppUnits(), appUnitsPerDevPixel))));
         break;
       }
-      case NS_STYLE_FILTER_DROP_SHADOW: {
+      case StyleFilter::Tag::DropShadow: {
         float appUnitsPerDevPixel =
             mFrame->PresContext()->AppUnitsPerDevPixel();
-        const StyleSimpleShadow& shadow = filter.GetDropShadow();
+        const StyleSimpleShadow& shadow = filter.AsDropShadow();
         nscolor color = shadow.color.CalcColor(mFrame);
 
         wr::Shadow wrShadow;
         wrShadow.offset = {
             NSAppUnitsToFloatPixels(shadow.horizontal.ToAppUnits(),
                                     appUnitsPerDevPixel),
             NSAppUnitsToFloatPixels(shadow.vertical.ToAppUnits(),
                                     appUnitsPerDevPixel)};
         wrShadow.blur_radius = NSAppUnitsToFloatPixels(shadow.blur.ToAppUnits(),
                                                        appUnitsPerDevPixel);
         wrShadow.color = {NS_GET_R(color) / 255.0f, NS_GET_G(color) / 255.0f,
                           NS_GET_B(color) / 255.0f, NS_GET_A(color) / 255.0f};
-        auto filterOp = wr::FilterOp::DropShadow(wrShadow);
-
-        wrFilters.filters.AppendElement(filterOp);
+        wrFilters.AppendElement(wr::FilterOp::DropShadow(wrShadow));
         break;
       }
       default:
         return false;
     }
   }
 
   return true;
--- a/layout/style/GeckoBindings.cpp
+++ b/layout/style/GeckoBindings.cpp
@@ -1546,30 +1546,16 @@ void Gecko_NewShapeImage(StyleShapeSourc
 
 void Gecko_SetToSVGPath(StyleShapeSource* aShape,
                         StyleForgottenArcSlicePtr<StylePathCommand> aCommands,
                         StyleFillRule aFill) {
   MOZ_ASSERT(aShape);
   aShape->SetPath(MakeUnique<StyleSVGPath>(aCommands, aFill));
 }
 
-void Gecko_ResetFilters(nsStyleEffects* effects, size_t new_len) {
-  effects->mFilters.Clear();
-  effects->mFilters.SetLength(new_len);
-}
-
-void Gecko_CopyFiltersFrom(nsStyleEffects* aSrc, nsStyleEffects* aDest) {
-  aDest->mFilters = aSrc->mFilters;
-}
-
-void Gecko_nsStyleFilter_SetURLValue(nsStyleFilter* aEffects,
-                                     const StyleComputedUrl* aURL) {
-  aEffects->SetURL(*aURL);
-}
-
 void Gecko_nsStyleSVGPaint_CopyFrom(nsStyleSVGPaint* aDest,
                                     const nsStyleSVGPaint* aSrc) {
   *aDest = *aSrc;
 }
 
 void Gecko_nsStyleSVGPaint_SetURLValue(nsStyleSVGPaint* aPaint,
                                        const StyleComputedUrl* aURL) {
   aPaint->SetPaintServer(*aURL);
--- a/layout/style/GeckoBindings.h
+++ b/layout/style/GeckoBindings.h
@@ -531,19 +531,16 @@ void Gecko_SetToSVGPath(
     mozilla::StyleShapeSource* shape,
     mozilla::StyleForgottenArcSlicePtr<mozilla::StylePathCommand>,
     mozilla::StyleFillRule);
 
 void Gecko_ResetFilters(nsStyleEffects* effects, size_t new_len);
 
 void Gecko_CopyFiltersFrom(nsStyleEffects* aSrc, nsStyleEffects* aDest);
 
-void Gecko_nsStyleFilter_SetURLValue(nsStyleFilter* effects,
-                                     const mozilla::StyleComputedUrl* url);
-
 void Gecko_nsStyleSVGPaint_CopyFrom(nsStyleSVGPaint* dest,
                                     const nsStyleSVGPaint* src);
 
 void Gecko_nsStyleSVGPaint_SetURLValue(nsStyleSVGPaint* paint,
                                        const mozilla::StyleComputedUrl* url);
 
 void Gecko_nsStyleSVGPaint_Reset(nsStyleSVGPaint* paint);
 
--- a/layout/style/ServoBindings.toml
+++ b/layout/style/ServoBindings.toml
@@ -502,16 +502,17 @@ cbindgen-types = [
     { gecko = "StyleGenericBorderImageSideWidth", servo = "values::generics::border::BorderImageSideWidth" },
     { gecko = "StyleGenericUrlOrNone", servo = "values::generics::url::UrlOrNone" },
     { gecko = "StyleCssUrl", servo = "gecko::url::CssUrl" },
     { gecko = "StyleSpecifiedUrl", servo = "gecko::url::SpecifiedUrl" },
     { gecko = "StyleSpecifiedImageUrl", servo = "gecko::url::SpecifiedImageUrl" },
     { gecko = "StyleComputedUrl", servo = "gecko::url::ComputedUrl" },
     { gecko = "StyleComputedImageUrl", servo = "gecko::url::ComputedImageUrl" },
     { gecko = "StyleLoadData", servo = "gecko::url::LoadData" },
+    { gecko = "StyleGenericFilter", servo = "values::generics::effects::Filter" },
 ]
 
 mapped-generic-types = [
     { generic = true, gecko = "mozilla::RustCell", servo = "::std::cell::Cell" },
     { generic = false, gecko = "ServoNodeData", servo = "AtomicRefCell<ElementData>" },
     { generic = false, gecko = "mozilla::ServoWritingMode", servo = "::logical_geometry::WritingMode" },
     { generic = false, gecko = "mozilla::ServoCustomPropertiesMap", servo = "Option<::servo_arc::Arc<::custom_properties::CustomPropertiesMap>>" },
     { generic = false, gecko = "mozilla::ServoRuleNode", servo = "Option<::rule_tree::StrongRuleNode>" },
--- a/layout/style/ServoStyleConstsInlines.h
+++ b/layout/style/ServoStyleConstsInlines.h
@@ -18,48 +18,74 @@
 #include <new>
 
 // TODO(emilio): there are quite a few other implementations scattered around
 // that should move here.
 
 namespace mozilla {
 
 template <typename T>
-inline StyleOwnedSlice<T>::StyleOwnedSlice(const StyleOwnedSlice& aOther) {
+inline void StyleOwnedSlice<T>::Clear() {
+  if (!len) {
+    return;
+  }
+  for (size_t i : IntegerRange(len)) {
+    ptr[i].~T();
+  }
+  free(ptr);
+  ptr = (T*)alignof(T);
+  len = 0;
+}
+
+template <typename T>
+inline void StyleOwnedSlice<T>::CopyFrom(const StyleOwnedSlice& aOther) {
+  Clear();
   len = aOther.len;
   if (!len) {
     ptr = (T*)alignof(T);
   } else {
     ptr = (T*)malloc(len * sizeof(T));
     size_t i = 0;
     for (const T& elem : aOther.AsSpan()) {
       new (ptr + i++) T(elem);
     }
   }
 }
 
 template <typename T>
-inline StyleOwnedSlice<T>::StyleOwnedSlice(StyleOwnedSlice&& aOther) {
-  len = aOther.len;
-  ptr = aOther.ptr;
-  aOther.ptr = (T*)alignof(T);
-  aOther.len = 0;
+inline void StyleOwnedSlice<T>::SwapElements(StyleOwnedSlice& aOther) {
+  std::swap(ptr, aOther.ptr);
+  std::swap(len, aOther.len);
+}
+
+template <typename T>
+inline StyleOwnedSlice<T>::StyleOwnedSlice(const StyleOwnedSlice& aOther)
+    : StyleOwnedSlice() {
+  CopyFrom(aOther);
 }
 
 template <typename T>
-inline void StyleOwnedSlice<T>::Clear() {
-  if (!len) {
-    return;
-  }
-  for (size_t i : IntegerRange(len)) {
-    ptr[i].~T();
-  }
-  free(ptr);
-  ptr = (T*)alignof(T);
-  len = 0;
+inline StyleOwnedSlice<T>::StyleOwnedSlice(StyleOwnedSlice&& aOther)
+    : StyleOwnedSlice() {
+  SwapElements(aOther);
+}
+
+template <typename T>
+inline StyleOwnedSlice<T>& StyleOwnedSlice<T>::operator=(
+    const StyleOwnedSlice& aOther) {
+  CopyFrom(aOther);
+  return *this;
+}
+
+template <typename T>
+inline StyleOwnedSlice<T>& StyleOwnedSlice<T>::operator=(
+    StyleOwnedSlice&& aOther) {
+  Clear();
+  SwapElements(aOther);
+  return *this;
 }
 
 template <typename T>
 inline StyleOwnedSlice<T>::~StyleOwnedSlice() {
   Clear();
 }
 
 // This code is basically a C++ port of the Arc::clone() implementation in
--- a/layout/style/nsCSSProps.cpp
+++ b/layout/style/nsCSSProps.cpp
@@ -371,31 +371,16 @@ const KTableEntry nsCSSProps::kTextDecor
 const KTableEntry nsCSSProps::kTextEmphasisStyleShapeKTable[] = {
     {eCSSKeyword_dot, NS_STYLE_TEXT_EMPHASIS_STYLE_DOT},
     {eCSSKeyword_circle, NS_STYLE_TEXT_EMPHASIS_STYLE_CIRCLE},
     {eCSSKeyword_double_circle, NS_STYLE_TEXT_EMPHASIS_STYLE_DOUBLE_CIRCLE},
     {eCSSKeyword_triangle, NS_STYLE_TEXT_EMPHASIS_STYLE_TRIANGLE},
     {eCSSKeyword_sesame, NS_STYLE_TEXT_EMPHASIS_STYLE_SESAME},
     {eCSSKeyword_UNKNOWN, -1}};
 
-// keyword tables for SVG properties
-
-const KTableEntry nsCSSProps::kFilterFunctionKTable[] = {
-    {eCSSKeyword_blur, NS_STYLE_FILTER_BLUR},
-    {eCSSKeyword_brightness, NS_STYLE_FILTER_BRIGHTNESS},
-    {eCSSKeyword_contrast, NS_STYLE_FILTER_CONTRAST},
-    {eCSSKeyword_grayscale, NS_STYLE_FILTER_GRAYSCALE},
-    {eCSSKeyword_invert, NS_STYLE_FILTER_INVERT},
-    {eCSSKeyword_opacity, NS_STYLE_FILTER_OPACITY},
-    {eCSSKeyword_saturate, NS_STYLE_FILTER_SATURATE},
-    {eCSSKeyword_sepia, NS_STYLE_FILTER_SEPIA},
-    {eCSSKeyword_hue_rotate, NS_STYLE_FILTER_HUE_ROTATE},
-    {eCSSKeyword_drop_shadow, NS_STYLE_FILTER_DROP_SHADOW},
-    {eCSSKeyword_UNKNOWN, -1}};
-
 int32_t nsCSSProps::FindIndexOfKeyword(nsCSSKeyword aKeyword,
                                        const KTableEntry aTable[]) {
   if (eCSSKeyword_UNKNOWN == aKeyword) {
     // NOTE: we can have keyword tables where eCSSKeyword_UNKNOWN is used
     // not only for the sentinel, but also in the middle of the table to
     // knock out values that have been disabled by prefs, e.g. kDisplayKTable.
     // So we deal with eCSSKeyword_UNKNOWN up front to avoid returning a valid
     // index in the loop below.
--- a/layout/style/nsCSSProps.h
+++ b/layout/style/nsCSSProps.h
@@ -285,17 +285,16 @@ class nsCSSProps {
           es_ = (nsCSSPropertyID)((enabledstate_) | CSSEnabledState(0)); \
        *it_ != eCSSProperty_UNKNOWN; ++it_)                              \
     if (nsCSSProps::IsEnabled(*it_, (mozilla::CSSEnabledState)es_))
 
   // Keyword/Enum value tables
   // Not const because we modify its entries when the pref
   // "layout.css.background-clip.text" changes:
   static const KTableEntry kShapeRadiusKTable[];
-  static const KTableEntry kFilterFunctionKTable[];
   static const KTableEntry kBoxShadowTypeKTable[];
   static const KTableEntry kCursorKTable[];
   // Not const because we modify its entries when various
   // "layout.css.*.enabled" prefs changes:
   static KTableEntry kDisplayKTable[];
   // clang-format off
   // -- tables for auto-completion of the {align,justify}-{content,items,self} properties --
   static const KTableEntry kAutoCompletionAlignJustifySelf[];
--- a/layout/style/nsComputedDOMStyle.h
+++ b/layout/style/nsComputedDOMStyle.h
@@ -36,17 +36,16 @@ struct ComputedGridTrackInfo;
 
 struct ComputedStyleMap;
 struct nsCSSKTableEntry;
 class nsIFrame;
 class nsDOMCSSValueList;
 struct nsMargin;
 class nsROCSSPrimitiveValue;
 class nsStyleCoord;
-struct nsStyleFilter;
 class nsStyleGradient;
 struct nsStyleImage;
 class nsStyleSides;
 
 class nsComputedDOMStyle final : public nsDOMCSSDeclaration,
                                  public nsStubMutationObserver {
  private:
   // Convenience typedefs:
--- a/layout/style/nsStyleConsts.h
+++ b/layout/style/nsStyleConsts.h
@@ -359,30 +359,16 @@ enum class StyleFlexDirection : uint8_t 
 #define NS_STYLE_FLEX_WRAP_WRAP 1
 #define NS_STYLE_FLEX_WRAP_WRAP_REVERSE 2
 
 // See nsStylePosition
 // NOTE: This is the initial value of the integer-valued 'order' property
 // (rather than an internal numerical representation of some keyword).
 #define NS_STYLE_ORDER_INITIAL 0
 
-// See nsStyleFilter
-#define NS_STYLE_FILTER_NONE 0
-#define NS_STYLE_FILTER_URL 1
-#define NS_STYLE_FILTER_BLUR 2
-#define NS_STYLE_FILTER_BRIGHTNESS 3
-#define NS_STYLE_FILTER_CONTRAST 4
-#define NS_STYLE_FILTER_GRAYSCALE 5
-#define NS_STYLE_FILTER_INVERT 6
-#define NS_STYLE_FILTER_OPACITY 7
-#define NS_STYLE_FILTER_SATURATE 8
-#define NS_STYLE_FILTER_SEPIA 9
-#define NS_STYLE_FILTER_HUE_ROTATE 10
-#define NS_STYLE_FILTER_DROP_SHADOW 11
-
 // See nsStyleFont
 #define NS_STYLE_FONT_SIZE_XXSMALL 0
 #define NS_STYLE_FONT_SIZE_XSMALL 1
 #define NS_STYLE_FONT_SIZE_SMALL 2
 #define NS_STYLE_FONT_SIZE_MEDIUM 3
 #define NS_STYLE_FONT_SIZE_LARGE 4
 #define NS_STYLE_FONT_SIZE_XLARGE 5
 #define NS_STYLE_FONT_SIZE_XXLARGE 6
--- a/layout/style/nsStyleStruct.cpp
+++ b/layout/style/nsStyleStruct.cpp
@@ -1006,103 +1006,16 @@ void StyleShapeSource::DoDestroy() {
     case StyleShapeSourceType::Box:
       // Not a union type, so do nothing.
       break;
   }
   mType = StyleShapeSourceType::None;
 }
 
 // --------------------
-// nsStyleFilter
-//
-nsStyleFilter::nsStyleFilter() : mType(NS_STYLE_FILTER_NONE) {
-  MOZ_COUNT_CTOR(nsStyleFilter);
-}
-
-nsStyleFilter::nsStyleFilter(const nsStyleFilter& aSource)
-    : mType(NS_STYLE_FILTER_NONE) {
-  MOZ_COUNT_CTOR(nsStyleFilter);
-  if (aSource.mType == NS_STYLE_FILTER_URL) {
-    SetURL(aSource.mURL);
-  } else if (aSource.mType == NS_STYLE_FILTER_DROP_SHADOW) {
-    SetDropShadow(aSource.mDropShadow);
-  } else if (aSource.mType != NS_STYLE_FILTER_NONE) {
-    SetFilterParameter(aSource.mFilterParameter, aSource.mType);
-  }
-}
-
-nsStyleFilter::~nsStyleFilter() {
-  ReleaseRef();
-  MOZ_COUNT_DTOR(nsStyleFilter);
-}
-
-nsStyleFilter& nsStyleFilter::operator=(const nsStyleFilter& aOther) {
-  if (this == &aOther) {
-    return *this;
-  }
-
-  if (aOther.mType == NS_STYLE_FILTER_URL) {
-    SetURL(aOther.mURL);
-  } else if (aOther.mType == NS_STYLE_FILTER_DROP_SHADOW) {
-    SetDropShadow(aOther.mDropShadow);
-  } else if (aOther.mType != NS_STYLE_FILTER_NONE) {
-    SetFilterParameter(aOther.mFilterParameter, aOther.mType);
-  } else {
-    ReleaseRef();
-    mType = NS_STYLE_FILTER_NONE;
-  }
-
-  return *this;
-}
-
-bool nsStyleFilter::operator==(const nsStyleFilter& aOther) const {
-  if (mType != aOther.mType) {
-    return false;
-  }
-
-  if (mType == NS_STYLE_FILTER_URL) {
-    return mURL == aOther.mURL;
-  } else if (mType == NS_STYLE_FILTER_DROP_SHADOW) {
-    return mDropShadow == aOther.mDropShadow;
-  } else if (mType != NS_STYLE_FILTER_NONE) {
-    return mFilterParameter == aOther.mFilterParameter;
-  }
-
-  return true;
-}
-
-void nsStyleFilter::ReleaseRef() {
-  if (mType == NS_STYLE_FILTER_DROP_SHADOW) {
-    mDropShadow.~StyleSimpleShadow();
-  } else if (mType == NS_STYLE_FILTER_URL) {
-    mURL.~StyleComputedUrl();
-  }
-}
-
-void nsStyleFilter::SetFilterParameter(const nsStyleCoord& aFilterParameter,
-                                       int32_t aType) {
-  ReleaseRef();
-  mFilterParameter = aFilterParameter;
-  mType = aType;
-}
-
-bool nsStyleFilter::SetURL(const StyleComputedUrl& aUrl) {
-  ReleaseRef();
-  new (&mURL) StyleComputedUrl(aUrl);
-  mType = NS_STYLE_FILTER_URL;
-  return true;
-}
-
-void nsStyleFilter::SetDropShadow(const StyleSimpleShadow& aSrc) {
-  ReleaseRef();
-  new (&mDropShadow) StyleSimpleShadow(aSrc);
-  mType = NS_STYLE_FILTER_DROP_SHADOW;
-}
-
-// --------------------
 // nsStyleSVGReset
 //
 nsStyleSVGReset::nsStyleSVGReset(const Document& aDocument)
     : mX(LengthPercentage::Zero()),
       mY(LengthPercentage::Zero()),
       mCx(LengthPercentage::Zero()),
       mCy(LengthPercentage::Zero()),
       mRx(NonNegativeLengthPercentageOrAuto::Auto()),
--- a/layout/style/nsStyleStruct.h
+++ b/layout/style/nsStyleStruct.h
@@ -2541,70 +2541,16 @@ struct MOZ_NEEDS_MEMMOVABLE_MEMBERS nsSt
   // stroke-width: context-value
   static const uint8_t STROKE_WIDTH_CONTEXT = 0x40;
   static const uint8_t FILL_OPACITY_SOURCE_SHIFT = 0;
   static const uint8_t STROKE_OPACITY_SOURCE_SHIFT = 2;
 
   uint8_t mContextFlags;
 };
 
-struct nsStyleFilter {
-  nsStyleFilter();
-  nsStyleFilter(const nsStyleFilter& aSource);
-  ~nsStyleFilter();
-  void TriggerImageLoads(mozilla::dom::Document&, const nsStyleFilter*) {}
-  const static bool kHasTriggerImageLoads = false;
-
-  nsStyleFilter& operator=(const nsStyleFilter& aOther);
-
-  bool operator==(const nsStyleFilter& aOther) const;
-  bool operator!=(const nsStyleFilter& aOther) const {
-    return !(*this == aOther);
-  }
-
-  uint32_t GetType() const { return mType; }
-
-  const nsStyleCoord& GetFilterParameter() const {
-    NS_ASSERTION(mType != NS_STYLE_FILTER_DROP_SHADOW &&
-                     mType != NS_STYLE_FILTER_URL &&
-                     mType != NS_STYLE_FILTER_NONE,
-                 "wrong filter type");
-    return mFilterParameter;
-  }
-  void SetFilterParameter(const nsStyleCoord& aFilterParameter, int32_t aType);
-
-  const mozilla::StyleComputedUrl& GetURL() const {
-    MOZ_ASSERT(mType == NS_STYLE_FILTER_URL, "wrong filter type");
-    return mURL;
-  }
-
-  bool SetURL(const mozilla::StyleComputedUrl& aUrl);
-
-  const mozilla::StyleSimpleShadow& GetDropShadow() const {
-    NS_ASSERTION(mType == NS_STYLE_FILTER_DROP_SHADOW, "wrong filter type");
-    return mDropShadow;
-  }
-  void SetDropShadow(const mozilla::StyleSimpleShadow&);
-
- private:
-  void ReleaseRef();
-
-  uint32_t mType;                 // NS_STYLE_FILTER_*
-  nsStyleCoord mFilterParameter;  // coord, percent, factor, angle
-  union {
-    mozilla::StyleComputedUrl mURL;
-    mozilla::StyleSimpleShadow mDropShadow;
-  };
-};
-
-template <>
-struct nsTArray_CopyChooser<nsStyleFilter> {
-  typedef nsTArray_CopyWithConstructors<nsStyleFilter> Type;
-};
-
 struct MOZ_NEEDS_MEMMOVABLE_MEMBERS nsStyleSVGReset {
   explicit nsStyleSVGReset(const mozilla::dom::Document&);
   nsStyleSVGReset(const nsStyleSVGReset& aSource);
   ~nsStyleSVGReset();
 
   // Resolves and tracks the images in mMask.  Only called with a Servo-backed
   // style system, where those images must be resolved later than the OMT
   // nsStyleSVGReset constructor call.
@@ -2661,17 +2607,17 @@ struct MOZ_NEEDS_MEMMOVABLE_MEMBERS nsSt
     for (auto& shadow : mBoxShadow.AsSpan()) {
       if (shadow.inset == aInset) {
         return true;
       }
     }
     return false;
   }
 
-  nsTArray<nsStyleFilter> mFilters;
+  mozilla::StyleOwnedSlice<mozilla::StyleFilter> mFilters;
   mozilla::StyleOwnedSlice<mozilla::StyleBoxShadow> mBoxShadow;
   nsRect mClip;  // offsets from UL border edge
   float mOpacity;
   uint8_t mClipFlags;     // bitfield of NS_STYLE_CLIP_* values
   uint8_t mMixBlendMode;  // NS_STYLE_BLEND_*
 };
 
 #define STATIC_ASSERT_TYPE_LAYOUTS_MATCH(T1, T2)           \
--- a/layout/svg/SVGObserverUtils.cpp
+++ b/layout/svg/SVGObserverUtils.cpp
@@ -623,17 +623,17 @@ nsSVGFilterFrame* SVGFilterObserver::Get
  * In the above example, the SVGFilterObserverList will manage two
  * SVGFilterObservers, one for each of the references to SVG filters.  CSS
  * filters like "blur(10px)" don't reference filter elements, so they don't
  * need an SVGFilterObserver.  The style system invalidates changes to CSS
  * filters.
  */
 class SVGFilterObserverList : public nsISupports {
  public:
-  SVGFilterObserverList(const nsTArray<nsStyleFilter>& aFilters,
+  SVGFilterObserverList(Span<const StyleFilter> aFilters,
                         nsIContent* aFilteredElement,
                         nsIFrame* aFilteredFrame = nullptr);
 
   bool ReferencesValidResources();
   void Invalidate() { OnRenderingChange(); }
 
   const nsTArray<RefPtr<SVGFilterObserver>>& GetObservers() const {
     return mObservers;
@@ -679,35 +679,35 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(SV
   tmp->DetachObservers();
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mObservers);
 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(SVGFilterObserverList)
   NS_INTERFACE_MAP_ENTRY(nsISupports)
 NS_INTERFACE_MAP_END
 
-SVGFilterObserverList::SVGFilterObserverList(
-    const nsTArray<nsStyleFilter>& aFilters, nsIContent* aFilteredElement,
-    nsIFrame* aFilteredFrame) {
-  for (uint32_t i = 0; i < aFilters.Length(); i++) {
-    if (aFilters[i].GetType() != NS_STYLE_FILTER_URL) {
+SVGFilterObserverList::SVGFilterObserverList(Span<const StyleFilter> aFilters,
+                                             nsIContent* aFilteredElement,
+                                             nsIFrame* aFilteredFrame) {
+  for (const auto& filter : aFilters) {
+    if (!filter.IsUrl()) {
       continue;
     }
 
+    const auto& url = filter.AsUrl();
+
     // aFilteredFrame can be null if this filter belongs to a
     // CanvasRenderingContext2D.
     RefPtr<URLAndReferrerInfo> filterURL;
     if (aFilteredFrame) {
-      filterURL = ResolveURLUsingLocalRef(aFilteredFrame, aFilters[i].GetURL());
+      filterURL = ResolveURLUsingLocalRef(aFilteredFrame, url);
     } else {
-      nsCOMPtr<nsIURI> resolvedURI =
-          aFilters[i].GetURL().ResolveLocalRef(aFilteredElement);
+      nsCOMPtr<nsIURI> resolvedURI = url.ResolveLocalRef(aFilteredElement);
       if (resolvedURI) {
-        filterURL = new URLAndReferrerInfo(resolvedURI,
-                                           aFilters[i].GetURL().ExtraData());
+        filterURL = new URLAndReferrerInfo(resolvedURI, url.ExtraData());
       }
     }
 
     RefPtr<SVGFilterObserver> observer =
         new SVGFilterObserver(filterURL, aFilteredElement, this);
     mObservers.AppendElement(observer);
   }
 }
@@ -720,17 +720,17 @@ bool SVGFilterObserverList::ReferencesVa
       return false;
     }
   }
   return true;
 }
 
 class SVGFilterObserverListForCSSProp final : public SVGFilterObserverList {
  public:
-  SVGFilterObserverListForCSSProp(const nsTArray<nsStyleFilter>& aFilters,
+  SVGFilterObserverListForCSSProp(Span<const StyleFilter> aFilters,
                                   nsIFrame* aFilteredFrame)
       : SVGFilterObserverList(aFilters, aFilteredFrame->GetContent(),
                               aFilteredFrame),
         mFrameReference(aFilteredFrame) {}
 
   void DetachFromFrame() { mFrameReference.Detach(); }
 
  protected:
@@ -764,17 +764,17 @@ void SVGFilterObserverListForCSSProp::On
       frame->GetContent()->AsElement(), RestyleHint{0}, changeHint);
 }
 
 class SVGFilterObserverListForCanvasContext final
     : public SVGFilterObserverList {
  public:
   SVGFilterObserverListForCanvasContext(CanvasRenderingContext2D* aContext,
                                         Element* aCanvasElement,
-                                        nsTArray<nsStyleFilter>& aFilters)
+                                        Span<const StyleFilter> aFilters)
       : SVGFilterObserverList(aFilters, aCanvasElement), mContext(aContext) {}
 
   void OnRenderingChange() override;
   void DetachFromContext() { mContext = nullptr; }
 
  private:
   CanvasRenderingContext2D* mContext;
 };
@@ -1163,17 +1163,18 @@ static SVGFilterObserverListForCSSProp* 
 
   bool found;
   SVGFilterObserverListForCSSProp* observers =
       aFrame->GetProperty(FilterProperty(), &found);
   if (found) {
     MOZ_ASSERT(observers, "this property should only store non-null values");
     return observers;
   }
-  observers = new SVGFilterObserverListForCSSProp(effects->mFilters, aFrame);
+  observers =
+      new SVGFilterObserverListForCSSProp(effects->mFilters.AsSpan(), aFrame);
   NS_ADDREF(observers);
   aFrame->AddProperty(FilterProperty(), observers);
   return observers;
 }
 
 static SVGObserverUtils::ReferenceState GetAndObserveFilters(
     SVGFilterObserverListForCSSProp* aObserverList,
     nsTArray<nsSVGFilterFrame*>* aFilterFrames) {
@@ -1214,17 +1215,17 @@ SVGObserverUtils::ReferenceState SVGObse
     nsIFrame* aFilteredFrame, nsTArray<nsSVGFilterFrame*>* aFilterFrames) {
   SVGFilterObserverListForCSSProp* observerList =
       aFilteredFrame->GetProperty(FilterProperty());
   return ::GetAndObserveFilters(observerList, aFilterFrames);
 }
 
 already_AddRefed<nsISupports> SVGObserverUtils::ObserveFiltersForCanvasContext(
     CanvasRenderingContext2D* aContext, Element* aCanvasElement,
-    nsTArray<nsStyleFilter>& aFilters) {
+    const Span<const StyleFilter> aFilters) {
   return do_AddRef(new SVGFilterObserverListForCanvasContext(
       aContext, aCanvasElement, aFilters));
 }
 
 void SVGObserverUtils::DetachFromCanvasContext(nsISupports* aAutoObserver) {
   static_cast<SVGFilterObserverListForCanvasContext*>(aAutoObserver)
       ->DetachFromContext();
 }
@@ -1675,16 +1676,15 @@ already_AddRefed<nsIURI> SVGObserverUtil
       return originalURI.forget();
     }
   }
 
   return baseURI.forget();
 }
 
 already_AddRefed<URLAndReferrerInfo> SVGObserverUtils::GetFilterURI(
-    nsIFrame* aFrame, const nsStyleFilter& aFilter) {
-  MOZ_ASSERT(aFrame->StyleEffects()->mFilters.Length());
-  MOZ_ASSERT(aFilter.GetType() == NS_STYLE_FILTER_URL);
-
-  return ResolveURLUsingLocalRef(aFrame, aFilter.GetURL());
+    nsIFrame* aFrame, const StyleFilter& aFilter) {
+  MOZ_ASSERT(!aFrame->StyleEffects()->mFilters.IsEmpty());
+  MOZ_ASSERT(aFilter.IsUrl());
+  return ResolveURLUsingLocalRef(aFrame, aFilter.AsUrl());
 }
 
 }  // namespace mozilla
--- a/layout/svg/SVGObserverUtils.h
+++ b/layout/svg/SVGObserverUtils.h
@@ -291,23 +291,23 @@ class SVGObserverUtils {
    * Starts observing filters for a <canvas> element's CanvasRenderingContext2D.
    *
    * Returns a RAII object that the caller should make sure is released once
    * the CanvasRenderingContext2D is no longer using them (that is, when the
    * CanvasRenderingContext2D "drawing style state" on which the filters were
    * set is destroyed or has its filter style reset).
    *
    * XXXjwatt: It's a bit unfortunate that both we and
-   * CanvasRenderingContext2D::UpdateFilter process the list of nsStyleFilter
+   * CanvasRenderingContext2D::UpdateFilter process the list of StyleFilter
    * objects separately.  It would be better to refactor things so that we only
    * do that work once.
    */
   static already_AddRefed<nsISupports> ObserveFiltersForCanvasContext(
       CanvasRenderingContext2D* aContext, Element* aCanvasElement,
-      nsTArray<nsStyleFilter>& aFilters);
+      Span<const StyleFilter> aFilters);
 
   /**
    * Called when cycle collecting CanvasRenderingContext2D, and requires the
    * RAII object returned from ObserveFiltersForCanvasContext to be passed in.
    *
    * XXXjwatt: I don't think this is doing anything useful.  All we do under
    * this function is clear a raw C-style (i.e. not strong) pointer.  That's
    * clearly not helping in breaking any cycles.  The fact that we MOZ_CRASH
@@ -398,17 +398,17 @@ class SVGObserverUtils {
    * invalidation changes for background-clip:text.
    */
   static Element* GetAndObserveBackgroundClip(nsIFrame* aFrame);
 
   /**
    * A helper function to resolve filter URL.
    */
   static already_AddRefed<URLAndReferrerInfo> GetFilterURI(
-      nsIFrame* aFrame, const nsStyleFilter& aFilter);
+      nsIFrame* aFrame, const StyleFilter& aFilter);
 
   /**
    * Return a baseURL for resolving a local-ref URL.
    *
    * @param aContent an element which uses a local-ref property. Here are some
    *                 examples:
    *                   <rect fill=url(#foo)>
    *                   <circle clip-path=url(#foo)>
--- a/layout/svg/nsCSSFilterInstance.cpp
+++ b/layout/svg/nsCSSFilterInstance.cpp
@@ -25,60 +25,60 @@ static float ClampFactor(float aFactor) 
     MOZ_ASSERT_UNREACHABLE("A negative value should not have been parsed.");
     return 0;
   }
 
   return aFactor;
 }
 
 nsCSSFilterInstance::nsCSSFilterInstance(
-    const nsStyleFilter& aFilter, nscolor aShadowFallbackColor,
+    const StyleFilter& aFilter, nscolor aShadowFallbackColor,
     const nsIntRect& aTargetBoundsInFilterSpace,
     const gfxMatrix& aFrameSpaceInCSSPxToFilterSpaceTransform)
     : mFilter(aFilter),
       mShadowFallbackColor(aShadowFallbackColor),
       mTargetBoundsInFilterSpace(aTargetBoundsInFilterSpace),
       mFrameSpaceInCSSPxToFilterSpaceTransform(
           aFrameSpaceInCSSPxToFilterSpaceTransform) {}
 
 nsresult nsCSSFilterInstance::BuildPrimitives(
     nsTArray<FilterPrimitiveDescription>& aPrimitiveDescrs,
     bool aInputIsTainted) {
   FilterPrimitiveDescription descr =
       CreatePrimitiveDescription(aPrimitiveDescrs, aInputIsTainted);
   nsresult result;
-  switch (mFilter.GetType()) {
-    case NS_STYLE_FILTER_BLUR:
+  switch (mFilter.tag) {
+    case StyleFilter::Tag::Blur:
       result = SetAttributesForBlur(descr);
       break;
-    case NS_STYLE_FILTER_BRIGHTNESS:
+    case StyleFilter::Tag::Brightness:
       result = SetAttributesForBrightness(descr);
       break;
-    case NS_STYLE_FILTER_CONTRAST:
+    case StyleFilter::Tag::Contrast:
       result = SetAttributesForContrast(descr);
       break;
-    case NS_STYLE_FILTER_DROP_SHADOW:
+    case StyleFilter::Tag::DropShadow:
       result = SetAttributesForDropShadow(descr);
       break;
-    case NS_STYLE_FILTER_GRAYSCALE:
+    case StyleFilter::Tag::Grayscale:
       result = SetAttributesForGrayscale(descr);
       break;
-    case NS_STYLE_FILTER_HUE_ROTATE:
+    case StyleFilter::Tag::HueRotate:
       result = SetAttributesForHueRotate(descr);
       break;
-    case NS_STYLE_FILTER_INVERT:
+    case StyleFilter::Tag::Invert:
       result = SetAttributesForInvert(descr);
       break;
-    case NS_STYLE_FILTER_OPACITY:
+    case StyleFilter::Tag::Opacity:
       result = SetAttributesForOpacity(descr);
       break;
-    case NS_STYLE_FILTER_SATURATE:
+    case StyleFilter::Tag::Saturate:
       result = SetAttributesForSaturate(descr);
       break;
-    case NS_STYLE_FILTER_SEPIA:
+    case StyleFilter::Tag::Sepia:
       result = SetAttributesForSepia(descr);
       break;
     default:
       MOZ_ASSERT_UNREACHABLE("not a valid CSS filter type");
       return NS_ERROR_FAILURE;
   }
 
   if (NS_FAILED(result)) {
@@ -104,34 +104,28 @@ FilterPrimitiveDescription nsCSSFilterIn
                                     : aPrimitiveDescrs[inputIndex].IsTainted());
   descr.SetInputColorSpace(0, ColorSpace::SRGB);
   descr.SetOutputColorSpace(ColorSpace::SRGB);
   return descr;
 }
 
 nsresult nsCSSFilterInstance::SetAttributesForBlur(
     FilterPrimitiveDescription& aDescr) {
-  const nsStyleCoord& radiusInFrameSpace = mFilter.GetFilterParameter();
-  if (radiusInFrameSpace.GetUnit() != eStyleUnit_Coord) {
-    MOZ_ASSERT_UNREACHABLE("unexpected unit");
-    return NS_ERROR_FAILURE;
-  }
-
+  const Length& radiusInFrameSpace = mFilter.AsBlur();
   Size radiusInFilterSpace =
-      BlurRadiusToFilterSpace(radiusInFrameSpace.GetCoordValue());
+      BlurRadiusToFilterSpace(radiusInFrameSpace.ToAppUnits());
   GaussianBlurAttributes atts;
   atts.mStdDeviation = radiusInFilterSpace;
   aDescr.Attributes() = AsVariant(atts);
   return NS_OK;
 }
 
 nsresult nsCSSFilterInstance::SetAttributesForBrightness(
     FilterPrimitiveDescription& aDescr) {
-  const nsStyleCoord& styleValue = mFilter.GetFilterParameter();
-  float value = styleValue.GetFactorOrPercentValue();
+  float value = mFilter.AsBrightness();
   float intercept = 0.0f;
   ComponentTransferAttributes atts;
 
   // Set transfer functions for RGB.
   atts.mTypes[kChannelROrRGB] = (uint8_t)SVG_FECOMPONENTTRANSFER_TYPE_LINEAR;
   atts.mTypes[kChannelG] = (uint8_t)SVG_FECOMPONENTTRANSFER_TYPE_UNKNOWN;
   atts.mTypes[kChannelB] = (uint8_t)SVG_FECOMPONENTTRANSFER_TYPE_UNKNOWN;
   float slopeIntercept[2];
@@ -142,18 +136,17 @@ nsresult nsCSSFilterInstance::SetAttribu
   atts.mTypes[kChannelA] = (uint8_t)SVG_FECOMPONENTTRANSFER_TYPE_IDENTITY;
 
   aDescr.Attributes() = AsVariant(std::move(atts));
   return NS_OK;
 }
 
 nsresult nsCSSFilterInstance::SetAttributesForContrast(
     FilterPrimitiveDescription& aDescr) {
-  const nsStyleCoord& styleValue = mFilter.GetFilterParameter();
-  float value = styleValue.GetFactorOrPercentValue();
+  float value = mFilter.AsContrast();
   float intercept = -(0.5 * value) + 0.5;
   ComponentTransferAttributes atts;
 
   // Set transfer functions for RGB.
   atts.mTypes[kChannelROrRGB] = (uint8_t)SVG_FECOMPONENTTRANSFER_TYPE_LINEAR;
   atts.mTypes[kChannelG] = (uint8_t)SVG_FECOMPONENTTRANSFER_TYPE_UNKNOWN;
   atts.mTypes[kChannelB] = (uint8_t)SVG_FECOMPONENTTRANSFER_TYPE_UNKNOWN;
   float slopeIntercept[2];
@@ -164,17 +157,17 @@ nsresult nsCSSFilterInstance::SetAttribu
   atts.mTypes[kChannelA] = (uint8_t)SVG_FECOMPONENTTRANSFER_TYPE_IDENTITY;
 
   aDescr.Attributes() = AsVariant(std::move(atts));
   return NS_OK;
 }
 
 nsresult nsCSSFilterInstance::SetAttributesForDropShadow(
     FilterPrimitiveDescription& aDescr) {
-  const auto& shadow = mFilter.GetDropShadow();
+  const auto& shadow = mFilter.AsDropShadow();
 
   DropShadowAttributes atts;
 
   // Set drop shadow blur radius.
   Size radiusInFilterSpace = BlurRadiusToFilterSpace(shadow.blur.ToAppUnits());
   atts.mStdDeviation = radiusInFilterSpace;
 
   // Set offset.
@@ -192,44 +185,41 @@ nsresult nsCSSFilterInstance::SetAttribu
 
 nsresult nsCSSFilterInstance::SetAttributesForGrayscale(
     FilterPrimitiveDescription& aDescr) {
   ColorMatrixAttributes atts;
   // Set color matrix type.
   atts.mType = (uint32_t)SVG_FECOLORMATRIX_TYPE_SATURATE;
 
   // Set color matrix values.
-  const nsStyleCoord& styleValue = mFilter.GetFilterParameter();
-  float value = 1 - ClampFactor(styleValue.GetFactorOrPercentValue());
+  float value = 1 - ClampFactor(mFilter.AsGrayscale());
   atts.mValues.AppendElements(&value, 1);
 
   aDescr.Attributes() = AsVariant(std::move(atts));
   return NS_OK;
 }
 
 nsresult nsCSSFilterInstance::SetAttributesForHueRotate(
     FilterPrimitiveDescription& aDescr) {
   ColorMatrixAttributes atts;
   // Set color matrix type.
   atts.mType = (uint32_t)SVG_FECOLORMATRIX_TYPE_HUE_ROTATE;
 
   // Set color matrix values.
-  const nsStyleCoord& styleValue = mFilter.GetFilterParameter();
-  float value = styleValue.GetAngleValueInDegrees();
+  float value = mFilter.AsHueRotate().ToDegrees();
   atts.mValues.AppendElements(&value, 1);
 
   aDescr.Attributes() = AsVariant(std::move(atts));
   return NS_OK;
 }
 
 nsresult nsCSSFilterInstance::SetAttributesForInvert(
     FilterPrimitiveDescription& aDescr) {
   ComponentTransferAttributes atts;
-  const nsStyleCoord& styleValue = mFilter.GetFilterParameter();
-  float value = ClampFactor(styleValue.GetFactorOrPercentValue());
+  float value = ClampFactor(mFilter.AsInvert());
 
   // Set transfer functions for RGB.
   float invertTableValues[2];
   invertTableValues[0] = value;
   invertTableValues[1] = 1 - value;
 
   // Set transfer functions for RGB.
   atts.mTypes[kChannelROrRGB] = (uint8_t)SVG_FECOMPONENTTRANSFER_TYPE_TABLE;
@@ -241,48 +231,45 @@ nsresult nsCSSFilterInstance::SetAttribu
 
   aDescr.Attributes() = AsVariant(std::move(atts));
   return NS_OK;
 }
 
 nsresult nsCSSFilterInstance::SetAttributesForOpacity(
     FilterPrimitiveDescription& aDescr) {
   OpacityAttributes atts;
-  const nsStyleCoord& styleValue = mFilter.GetFilterParameter();
-  float value = ClampFactor(styleValue.GetFactorOrPercentValue());
+  float value = ClampFactor(mFilter.AsOpacity());
 
   atts.mOpacity = value;
   aDescr.Attributes() = AsVariant(std::move(atts));
   return NS_OK;
 }
 
 nsresult nsCSSFilterInstance::SetAttributesForSaturate(
     FilterPrimitiveDescription& aDescr) {
   ColorMatrixAttributes atts;
   // Set color matrix type.
   atts.mType = (uint32_t)SVG_FECOLORMATRIX_TYPE_SATURATE;
 
   // Set color matrix values.
-  const nsStyleCoord& styleValue = mFilter.GetFilterParameter();
-  float value = styleValue.GetFactorOrPercentValue();
+  float value = mFilter.AsSaturate();
   atts.mValues.AppendElements(&value, 1);
 
   aDescr.Attributes() = AsVariant(std::move(atts));
   return NS_OK;
 }
 
 nsresult nsCSSFilterInstance::SetAttributesForSepia(
     FilterPrimitiveDescription& aDescr) {
   ColorMatrixAttributes atts;
   // Set color matrix type.
   atts.mType = (uint32_t)SVG_FECOLORMATRIX_TYPE_SEPIA;
 
   // Set color matrix values.
-  const nsStyleCoord& styleValue = mFilter.GetFilterParameter();
-  float value = ClampFactor(styleValue.GetFactorOrPercentValue());
+  float value = ClampFactor(mFilter.AsSepia());
   atts.mValues.AppendElements(&value, 1);
 
   aDescr.Attributes() = AsVariant(std::move(atts));
   return NS_OK;
 }
 
 Size nsCSSFilterInstance::BlurRadiusToFilterSpace(nscoord aRadiusInFrameSpace) {
   float radiusInFrameSpaceInCSSPx =
--- a/layout/svg/nsCSSFilterInstance.h
+++ b/layout/svg/nsCSSFilterInstance.h
@@ -8,26 +8,25 @@
 #define __NS_CSSFILTERINSTANCE_H__
 
 #include "FilterSupport.h"
 #include "gfxMatrix.h"
 #include "gfxRect.h"
 #include "mozilla/gfx/Point.h"
 #include "mozilla/gfx/Types.h"
 #include "nsColor.h"
-#include "nsTArrayForwardDeclare.h"
-
-struct nsStyleFilter;
+#include "mozilla/ServoStyleConsts.h"
 
 /**
  * This class helps nsFilterInstance build its filter graph. It turns a CSS
  * filter function (e.g. blur(3px)) from the style system into a
  * FilterPrimitiveDescription connected to the filter graph.
  */
 class nsCSSFilterInstance {
+  using StyleFilter = mozilla::StyleFilter;
   typedef mozilla::gfx::Color Color;
   typedef mozilla::gfx::FilterPrimitiveDescription FilterPrimitiveDescription;
   typedef mozilla::gfx::IntPoint IntPoint;
   typedef mozilla::gfx::Size Size;
 
  public:
   /**
    * @param aFilter The CSS filter from the style system. This class stores
@@ -36,17 +35,17 @@ class nsCSSFilterInstance {
    * @param aShadowFallbackColor The color that should be used for
    *   drop-shadow() filters that don't specify a shadow color.
    * @param aTargetBoundsInFilterSpace The pre-filter visual overflow rect of
    *   the frame being filtered, in filter space.
    * @param aFrameSpaceInCSSPxToFilterSpaceTransform The transformation from
    *   the filtered element's frame space in CSS pixels to filter space.
    */
   nsCSSFilterInstance(
-      const nsStyleFilter& aFilter, nscolor aShadowFallbackColor,
+      const StyleFilter& aFilter, nscolor aShadowFallbackColor,
       const nsIntRect& aTargetBoundsInFilterSpace,
       const gfxMatrix& aFrameSpaceInCSSPxToFilterSpaceTransform);
 
   /**
    * Creates at least one new FilterPrimitiveDescription based on the filter
    * from the style system. Appends the new FilterPrimitiveDescription(s) to the
    * aPrimitiveDescrs list.
    * aInputIsTainted describes whether the input to this filter is tainted, i.e.
@@ -116,17 +115,17 @@ class nsCSSFilterInstance {
    * space to filter space.
    */
   IntPoint OffsetToFilterSpace(nscoord aXOffsetInFrameSpace,
                                nscoord aYOffsetInFrameSpace);
 
   /**
    * The CSS filter originally from the style system.
    */
-  const nsStyleFilter& mFilter;
+  const StyleFilter& mFilter;
 
   /**
    * The color that should be used for drop-shadow() filters that don't
    * specify a shadow color.
    */
   nscolor mShadowFallbackColor;
 
   /**
--- a/layout/svg/nsFilterInstance.cpp
+++ b/layout/svg/nsFilterInstance.cpp
@@ -30,17 +30,17 @@
 #include "nsSVGUtils.h"
 
 using namespace mozilla;
 using namespace mozilla::dom;
 using namespace mozilla::gfx;
 using namespace mozilla::image;
 
 FilterDescription nsFilterInstance::GetFilterDescription(
-    nsIContent* aFilteredElement, const nsTArray<nsStyleFilter>& aFilterChain,
+    nsIContent* aFilteredElement, Span<const StyleFilter> aFilterChain,
     bool aFilterInputIsTainted, const UserSpaceMetrics& aMetrics,
     const gfxRect& aBBox,
     nsTArray<RefPtr<SourceSurface>>& aOutAdditionalImages) {
   gfxMatrix identity;
   nsFilterInstance instance(nullptr, aFilteredElement, aMetrics, aFilterChain,
                             aFilterInputIsTainted, nullptr, identity, nullptr,
                             nullptr, nullptr, &aBBox);
   if (!instance.IsInitialized()) {
@@ -56,17 +56,17 @@ static UniquePtr<UserSpaceMetrics> UserS
   }
   return MakeUnique<NonSVGFrameUserSpaceMetrics>(aFrame);
 }
 
 void nsFilterInstance::PaintFilteredFrame(
     nsIFrame* aFilteredFrame, gfxContext* aCtx,
     nsSVGFilterPaintCallback* aPaintCallback, const nsRegion* aDirtyArea,
     imgDrawingParams& aImgParams, float aOpacity) {
-  auto& filterChain = aFilteredFrame->StyleEffects()->mFilters;
+  auto filterChain = aFilteredFrame->StyleEffects()->mFilters.AsSpan();
   UniquePtr<UserSpaceMetrics> metrics =
       UserSpaceMetricsForFrame(aFilteredFrame);
 
   gfxContextMatrixAutoSaveRestore autoSR(aCtx);
   gfxSize scaleFactors = aCtx->CurrentMatrixDouble().ScaleFactors(true);
   if (scaleFactors.IsEmpty()) {
     return;
   }
@@ -116,17 +116,17 @@ static mozilla::wr::ComponentTransferFun
 
 bool nsFilterInstance::BuildWebRenderFilters(nsIFrame* aFilteredFrame,
                                              WrFiltersHolder& aWrFilters,
                                              Maybe<nsRect>& aPostFilterClip) {
   aWrFilters.filters.Clear();
   aWrFilters.filter_datas.Clear();
   aWrFilters.values.Clear();
 
-  auto& filterChain = aFilteredFrame->StyleEffects()->mFilters;
+  auto filterChain = aFilteredFrame->StyleEffects()->mFilters.AsSpan();
   UniquePtr<UserSpaceMetrics> metrics =
       UserSpaceMetricsForFrame(aFilteredFrame);
 
   // TODO: simply using an identity matrix here, was pulling the scale from a
   // gfx context for the non-wr path.
   gfxMatrix scaleMatrix;
   gfxMatrix scaleMatrixInDevUnits =
       scaleMatrix * nsSVGUtils::GetCSSPxToDevPxMatrix(aFilteredFrame);
@@ -370,17 +370,17 @@ bool nsFilterInstance::BuildWebRenderFil
 
 nsRegion nsFilterInstance::GetPostFilterDirtyArea(
     nsIFrame* aFilteredFrame, const nsRegion& aPreFilterDirtyRegion) {
   if (aPreFilterDirtyRegion.IsEmpty()) {
     return nsRegion();
   }
 
   gfxMatrix tm = nsSVGUtils::GetCanvasTM(aFilteredFrame);
-  auto& filterChain = aFilteredFrame->StyleEffects()->mFilters;
+  auto filterChain = aFilteredFrame->StyleEffects()->mFilters.AsSpan();
   UniquePtr<UserSpaceMetrics> metrics =
       UserSpaceMetricsForFrame(aFilteredFrame);
   // Hardcode InputIsTainted to true because we don't want JS to be able to
   // read the rendered contents of aFilteredFrame.
   nsFilterInstance instance(aFilteredFrame, aFilteredFrame->GetContent(),
                             *metrics, filterChain, /* InputIsTainted */ true,
                             nullptr, tm, nullptr, &aPreFilterDirtyRegion);
   if (!instance.IsInitialized()) {
@@ -391,17 +391,17 @@ nsRegion nsFilterInstance::GetPostFilter
   // Now we can ask the instance to compute the area of the filter output
   // that's dirty.
   return instance.ComputePostFilterDirtyRegion();
 }
 
 nsRegion nsFilterInstance::GetPreFilterNeededArea(
     nsIFrame* aFilteredFrame, const nsRegion& aPostFilterDirtyRegion) {
   gfxMatrix tm = nsSVGUtils::GetCanvasTM(aFilteredFrame);
-  auto& filterChain = aFilteredFrame->StyleEffects()->mFilters;
+  auto filterChain = aFilteredFrame->StyleEffects()->mFilters.AsSpan();
   UniquePtr<UserSpaceMetrics> metrics =
       UserSpaceMetricsForFrame(aFilteredFrame);
   // Hardcode InputIsTainted to true because we don't want JS to be able to
   // read the rendered contents of aFilteredFrame.
   nsFilterInstance instance(aFilteredFrame, aFilteredFrame->GetContent(),
                             *metrics, filterChain, /* InputIsTainted */ true,
                             nullptr, tm, &aPostFilterDirtyRegion);
   if (!instance.IsInitialized()) {
@@ -423,17 +423,17 @@ nsRect nsFilterInstance::GetPostFilterBo
   nsRegion preFilterRegion;
   nsRegion* preFilterRegionPtr = nullptr;
   if (aPreFilterBounds) {
     preFilterRegion = *aPreFilterBounds;
     preFilterRegionPtr = &preFilterRegion;
   }
 
   gfxMatrix tm = nsSVGUtils::GetCanvasTM(aFilteredFrame);
-  auto& filterChain = aFilteredFrame->StyleEffects()->mFilters;
+  auto filterChain = aFilteredFrame->StyleEffects()->mFilters.AsSpan();
   UniquePtr<UserSpaceMetrics> metrics =
       UserSpaceMetricsForFrame(aFilteredFrame);
   // Hardcode InputIsTainted to true because we don't want JS to be able to
   // read the rendered contents of aFilteredFrame.
   nsFilterInstance instance(aFilteredFrame, aFilteredFrame->GetContent(),
                             *metrics, filterChain, /* InputIsTainted */ true,
                             nullptr, tm, nullptr, preFilterRegionPtr,
                             aPreFilterBounds, aOverrideBBox);
@@ -441,20 +441,19 @@ nsRect nsFilterInstance::GetPostFilterBo
     return nsRect();
   }
 
   return instance.ComputePostFilterExtents();
 }
 
 nsFilterInstance::nsFilterInstance(
     nsIFrame* aTargetFrame, nsIContent* aTargetContent,
-    const UserSpaceMetrics& aMetrics,
-    const nsTArray<nsStyleFilter>& aFilterChain, bool aFilterInputIsTainted,
-    nsSVGFilterPaintCallback* aPaintCallback, const gfxMatrix& aPaintTransform,
-    const nsRegion* aPostFilterDirtyRegion,
+    const UserSpaceMetrics& aMetrics, Span<const StyleFilter> aFilterChain,
+    bool aFilterInputIsTainted, nsSVGFilterPaintCallback* aPaintCallback,
+    const gfxMatrix& aPaintTransform, const nsRegion* aPostFilterDirtyRegion,
     const nsRegion* aPreFilterDirtyRegion,
     const nsRect* aPreFilterVisualOverflowRectOverride,
     const gfxRect* aOverrideBBox)
     : mTargetFrame(aTargetFrame),
       mTargetContent(aTargetContent),
       mMetrics(aMetrics),
       mPaintCallback(aPaintCallback),
       mPaintTransform(aPaintTransform),
@@ -550,19 +549,19 @@ gfxRect nsFilterInstance::UserSpaceToFil
 gfxRect nsFilterInstance::FilterSpaceToUserSpace(
     const gfxRect& aFilterSpaceRect) const {
   gfxRect userSpaceRect = aFilterSpaceRect;
   userSpaceRect.Scale(mFilterSpaceToUserSpaceScale.width,
                       mFilterSpaceToUserSpaceScale.height);
   return userSpaceRect;
 }
 
-nsresult nsFilterInstance::BuildPrimitives(
-    const nsTArray<nsStyleFilter>& aFilterChain, nsIFrame* aTargetFrame,
-    bool aFilterInputIsTainted) {
+nsresult nsFilterInstance::BuildPrimitives(Span<const StyleFilter> aFilterChain,
+                                           nsIFrame* aTargetFrame,
+                                           bool aFilterInputIsTainted) {
   nsTArray<FilterPrimitiveDescription> primitiveDescriptions;
 
   for (uint32_t i = 0; i < aFilterChain.Length(); i++) {
     bool inputIsTainted = primitiveDescriptions.IsEmpty()
                               ? aFilterInputIsTainted
                               : primitiveDescriptions.LastElement().IsTainted();
     nsresult rv = BuildPrimitivesForFilter(
         aFilterChain[i], aTargetFrame, inputIsTainted, primitiveDescriptions);
@@ -572,23 +571,23 @@ nsresult nsFilterInstance::BuildPrimitiv
   }
 
   mFilterDescription = FilterDescription(std::move(primitiveDescriptions));
 
   return NS_OK;
 }
 
 nsresult nsFilterInstance::BuildPrimitivesForFilter(
-    const nsStyleFilter& aFilter, nsIFrame* aTargetFrame, bool aInputIsTainted,
+    const StyleFilter& aFilter, nsIFrame* aTargetFrame, bool aInputIsTainted,
     nsTArray<FilterPrimitiveDescription>& aPrimitiveDescriptions) {
   NS_ASSERTION(mUserSpaceToFilterSpaceScale.width > 0.0f &&
                    mFilterSpaceToUserSpaceScale.height > 0.0f,
                "scale factors between spaces should be positive values");
 
-  if (aFilter.GetType() == NS_STYLE_FILTER_URL) {
+  if (aFilter.IsUrl()) {
     // Build primitives for an SVG filter.
     nsSVGFilterInstance svgFilterInstance(aFilter, aTargetFrame, mTargetContent,
                                           mMetrics, mTargetBBox,
                                           mUserSpaceToFilterSpaceScale);
     if (!svgFilterInstance.IsInitialized()) {
       return NS_ERROR_FAILURE;
     }
 
--- a/layout/svg/nsFilterInstance.h
+++ b/layout/svg/nsFilterInstance.h
@@ -43,16 +43,20 @@ class UserSpaceMetrics;
  * at the top left of the filter region, and with one unit equal in size to one
  * pixel of the offscreen surface into which the filter output would/will be
  * painted.
  *
  * The definition of "filter region" can be found here:
  * http://www.w3.org/TR/SVG11/filters.html#FilterEffectsRegion
  */
 class nsFilterInstance {
+  template <typename T>
+  using Span = mozilla::Span<T>;
+  using StyleFilter = mozilla::StyleFilter;
+
   typedef mozilla::gfx::IntRect IntRect;
   typedef mozilla::gfx::SourceSurface SourceSurface;
   typedef mozilla::gfx::DrawTarget DrawTarget;
   typedef mozilla::gfx::FilterPrimitiveDescription FilterPrimitiveDescription;
   typedef mozilla::gfx::FilterDescription FilterDescription;
   typedef mozilla::dom::UserSpaceMetrics UserSpaceMetrics;
   typedef mozilla::image::imgDrawingParams imgDrawingParams;
 
@@ -66,17 +70,17 @@ class nsFilterInstance {
    *   IsTainted() state on the filter primitives in the FilterDescription.
    *   "Tainted" is a term from the filters spec and means security-sensitive
    *   content, i.e. pixels that JS should not be able to read in any way.
    * @param aOutAdditionalImages Will contain additional images needed to
    *   render the filter (from feImage primitives).
    * @return A FilterDescription describing the filter.
    */
   static FilterDescription GetFilterDescription(
-      nsIContent* aFilteredElement, const nsTArray<nsStyleFilter>& aFilterChain,
+      nsIContent* aFilteredElement, Span<const StyleFilter> aFilterChain,
       bool aFilterInputIsTainted, const UserSpaceMetrics& aMetrics,
       const gfxRect& aBBox,
       nsTArray<RefPtr<SourceSurface>>& aOutAdditionalImages);
 
   /**
    * Paint the given filtered frame.
    * @param aDirtyArea The area than needs to be painted, in aFilteredFrame's
    *   frame space (i.e. relative to its origin, the top-left corner of its
@@ -148,17 +152,17 @@ class nsFilterInstance {
    *   will call ComputePostFilterDirtyRegion().
    * @param aPreFilterVisualOverflowRectOverride [optional] Use a different
    *   visual overflow rect for the target element.
    * @param aOverrideBBox [optional] Use a different SVG bbox for the target
    *   element. Must be non-null if aTargetFrame is null.
    */
   nsFilterInstance(nsIFrame* aTargetFrame, nsIContent* aTargetContent,
                    const UserSpaceMetrics& aMetrics,
-                   const nsTArray<nsStyleFilter>& aFilterChain,
+                   Span<const StyleFilter> aFilterChain,
                    bool aFilterInputIsTainted,
                    nsSVGFilterPaintCallback* aPaintCallback,
                    const gfxMatrix& aPaintTransform,
                    const nsRegion* aPostFilterDirtyRegion = nullptr,
                    const nsRegion* aPreFilterDirtyRegion = nullptr,
                    const nsRect* aPreFilterVisualOverflowRectOverride = nullptr,
                    const gfxRect* aOverrideBBox = nullptr);
 
@@ -246,28 +250,27 @@ class nsFilterInstance {
                         const mozilla::gfx::Rect& aSourceRect);
 
   /**
    * Build the list of FilterPrimitiveDescriptions that describes the filter's
    * filter primitives and their connections. This populates
    * mPrimitiveDescriptions and mInputImages. aFilterInputIsTainted describes
    * whether the SourceGraphic is tainted.
    */
-  nsresult BuildPrimitives(const nsTArray<nsStyleFilter>& aFilterChain,
+  nsresult BuildPrimitives(Span<const StyleFilter> aFilterChain,
                            nsIFrame* aTargetFrame, bool aFilterInputIsTainted);
 
   /**
    * Add to the list of FilterPrimitiveDescriptions for a particular SVG
    * reference filter or CSS filter. This populates mPrimitiveDescriptions and
    * mInputImages. aInputIsTainted describes whether the input to aFilter is
    * tainted.
    */
   nsresult BuildPrimitivesForFilter(
-      const nsStyleFilter& aFilter, nsIFrame* aTargetFrame,
-      bool aInputIsTainted,
+      const StyleFilter& aFilter, nsIFrame* aTargetFrame, bool aInputIsTainted,
       nsTArray<FilterPrimitiveDescription>& aPrimitiveDescriptions);
 
   /**
    * Computes the filter space bounds of the areas that we actually *need* from
    * the filter sources, based on the value of mPostFilterDirtyRegion.
    * This sets mNeededBounds on the corresponding SourceInfo structs.
    */
   void ComputeNeededBoxes();
--- a/layout/svg/nsSVGFilterInstance.cpp
+++ b/layout/svg/nsSVGFilterInstance.cpp
@@ -24,17 +24,17 @@
 #include "gfx2DGlue.h"
 
 using namespace mozilla;
 using namespace mozilla::dom;
 using namespace mozilla::dom::SVGUnitTypes_Binding;
 using namespace mozilla::gfx;
 
 nsSVGFilterInstance::nsSVGFilterInstance(
-    const nsStyleFilter& aFilter, nsIFrame* aTargetFrame,
+    const StyleFilter& aFilter, nsIFrame* aTargetFrame,
     nsIContent* aTargetContent, const UserSpaceMetrics& aMetrics,
     const gfxRect& aTargetBBox, const gfxSize& aUserSpaceToFilterSpaceScale)
     : mFilter(aFilter),
       mTargetContent(aTargetContent),
       mMetrics(aMetrics),
       mTargetBBox(aTargetBBox),
       mUserSpaceToFilterSpaceScale(aUserSpaceToFilterSpaceScale),
       mSourceAlphaAvailable(false),
@@ -104,17 +104,17 @@ bool nsSVGFilterInstance::ComputeBounds(
     // The filter region is way too big if there is float -> int overflow.
     return false;
   }
 
   return true;
 }
 
 nsSVGFilterFrame* nsSVGFilterInstance::GetFilterFrame(nsIFrame* aTargetFrame) {
-  if (mFilter.GetType() != NS_STYLE_FILTER_URL) {
+  if (!mFilter.IsUrl()) {
     // The filter is not an SVG reference filter.
     return nullptr;
   }
 
   // Get the target element to use as a point of reference for looking up the
   // filter element.
   if (!mTargetContent) {
     return nullptr;
@@ -129,31 +129,31 @@ nsSVGFilterFrame* nsSVGFilterInstance::G
 
     // urlExtraReferrer might be null when mFilter has an invalid url
     if (!urlExtraReferrer) {
       return nullptr;
     }
 
     url = urlExtraReferrer->GetURI();
   } else {
-    url = mFilter.GetURL().ResolveLocalRef(mTargetContent);
+    url = mFilter.AsUrl().ResolveLocalRef(mTargetContent);
   }
 
   if (!url) {
     MOZ_ASSERT_UNREACHABLE(
-        "an nsStyleFilter of type URL should have a non-null URL");
+        "an StyleFilter of type URL should have a non-null URL");
     return nullptr;
   }
 
   // Look up the filter element by URL.
   IDTracker idTracker;
   bool watch = false;
   idTracker.ResetToURIFragmentID(
-      mTargetContent, url, mFilter.GetURL().ExtraData().GetReferrer(),
-      mFilter.GetURL().ExtraData().GetReferrerPolicy(), watch);
+      mTargetContent, url, mFilter.AsUrl().ExtraData().GetReferrer(),
+      mFilter.AsUrl().ExtraData().GetReferrerPolicy(), watch);
   Element* element = idTracker.get();
   if (!element) {
     // The URL points to no element.
     return nullptr;
   }
 
   // Get the frame of the filter element.
   nsIFrame* frame = element->GetPrimaryFrame();
--- a/layout/svg/nsSVGFilterInstance.h
+++ b/layout/svg/nsSVGFilterInstance.h
@@ -7,20 +7,19 @@
 #ifndef __NS_SVGFILTERINSTANCE_H__
 #define __NS_SVGFILTERINSTANCE_H__
 
 #include "gfxMatrix.h"
 #include "gfxRect.h"
 #include "SVGAnimatedNumber.h"
 #include "SVGAnimatedNumberPair.h"
 #include "SVGFilters.h"
-#include "nsTArray.h"
+#include "mozilla/ServoStyleConsts.h"
 
 class nsSVGFilterFrame;
-struct nsStyleFilter;
 
 namespace mozilla {
 namespace dom {
 class SVGFilterElement;
 }  // namespace dom
 }  // namespace mozilla
 
 /**
@@ -60,16 +59,17 @@ class SVGFilterElement;
  * div element's border box.
  *
  * Now, let's transform the point from user space to filter space:
  *   "filter space point" = "user space point" * "device pixels per CSS pixel"
  *   "filter space point" = (10, 10) * 2
  *   "filter space point" = (20, 20)
  */
 class nsSVGFilterInstance {
+  using StyleFilter = mozilla::StyleFilter;
   typedef mozilla::SVGAnimatedNumber SVGAnimatedNumber;
   typedef mozilla::SVGAnimatedNumberPair SVGAnimatedNumberPair;
   typedef mozilla::gfx::Point3D Point3D;
   typedef mozilla::gfx::IntRect IntRect;
   typedef mozilla::gfx::SourceSurface SourceSurface;
   typedef mozilla::gfx::FilterPrimitiveDescription FilterPrimitiveDescription;
   typedef mozilla::dom::SVGFE SVGFE;
   typedef mozilla::dom::UserSpaceMetrics UserSpaceMetrics;
@@ -78,17 +78,17 @@ class nsSVGFilterInstance {
   /**
    * @param aFilter The SVG filter reference from the style system. This class
    *   stores aFilter by reference, so callers should avoid modifying or
    *   deleting aFilter during the lifetime of nsSVGFilterInstance.
    * @param aTargetContent The filtered element.
    * @param aTargetBBox The SVG bbox to use for the target frame, computed by
    *   the caller. The caller may decide to override the actual SVG bbox.
    */
-  nsSVGFilterInstance(const nsStyleFilter& aFilter, nsIFrame* aTargetFrame,
+  nsSVGFilterInstance(const StyleFilter& aFilter, nsIFrame* aTargetFrame,
                       nsIContent* aTargetContent,
                       const UserSpaceMetrics& aMetrics,
                       const gfxRect& aTargetBBox,
                       const gfxSize& aUserSpaceToFilterSpaceScale);
 
   /**
    * Returns true if the filter instance was created successfully.
    */
@@ -197,17 +197,17 @@ class nsSVGFilterInstance {
    * Compute the filter region in user space, filter space, and filter
    * space.
    */
   bool ComputeBounds();
 
   /**
    * The SVG reference filter originally from the style system.
    */
-  const nsStyleFilter& mFilter;
+  const StyleFilter& mFilter;
 
   /**
    * The filtered element.
    */
   nsIContent* mTargetContent;
 
   /**
    * The SVG user space metrics that SVG lengths are resolved against.
--- a/servo/components/style/gecko/url.rs
+++ b/servo/components/style/gecko/url.rs
@@ -251,17 +251,16 @@ impl ToComputedValue for SpecifiedUrl {
     #[inline]
     fn from_computed_value(computed: &Self::ComputedValue) -> Self {
         computed.0.clone()
     }
 }
 
 /// A specified image `url()` value.
 #[derive(Clone, Debug, Eq, MallocSizeOf, PartialEq, SpecifiedValueInfo, ToCss, ToShmem)]
-#[repr(C)]
 pub struct SpecifiedImageUrl(pub SpecifiedUrl);
 
 impl SpecifiedImageUrl {
     /// Parse a URL from a string value that is a valid CSS token for a URL.
     pub fn parse_from_string(url: String, context: &ParserContext) -> Self {
         SpecifiedImageUrl(SpecifiedUrl::parse_from_string(url, context, CorsMode::None))
     }
 
@@ -335,16 +334,17 @@ impl ToCss for ComputedUrl {
         W: Write,
     {
         self.serialize_with(bindings::Gecko_GetComputedURLSpec, dest)
     }
 }
 
 /// The computed value of a CSS image `url()`.
 #[derive(Clone, Debug, Eq, MallocSizeOf, PartialEq)]
+#[repr(transparent)]
 pub struct ComputedImageUrl(pub ComputedUrl);
 
 impl ComputedImageUrl {
     /// Convert from nsStyleImageRequest to ComputedImageUrl.
     pub unsafe fn from_image_request(image_request: &nsStyleImageRequest) -> Self {
         image_request.mImageURL.clone()
     }
 }
--- a/servo/components/style/properties/gecko.mako.rs
+++ b/servo/components/style/properties/gecko.mako.rs
@@ -28,40 +28,37 @@ use crate::gecko_bindings::bindings::Gec
 use crate::gecko_bindings::bindings::Gecko_EnsureImageLayersLength;
 use crate::gecko_bindings::bindings::Gecko_SetCursorArrayLength;
 use crate::gecko_bindings::bindings::Gecko_SetCursorImageValue;
 use crate::gecko_bindings::bindings::Gecko_nsStyleFont_SetLang;
 use crate::gecko_bindings::bindings::Gecko_nsStyleFont_CopyLangFrom;
 use crate::gecko_bindings::bindings::Gecko_SetListStyleImageNone;
 use crate::gecko_bindings::bindings::Gecko_SetListStyleImageImageValue;
 use crate::gecko_bindings::bindings::Gecko_SetNullImageValue;
-use crate::gecko_bindings::bindings::{Gecko_ResetFilters, Gecko_CopyFiltersFrom};
 use crate::gecko_bindings::structs;
 use crate::gecko_bindings::structs::nsCSSPropertyID;
 use crate::gecko_bindings::structs::mozilla::PseudoStyleType;
-use crate::gecko_bindings::sugar::ns_style_coord::{CoordDataValue, CoordDataMut};
+use crate::gecko_bindings::sugar::ns_style_coord::CoordDataMut;
 use crate::gecko_bindings::sugar::refptr::RefPtr;
-use crate::gecko::values::GeckoStyleCoordConvertible;
 use crate::gecko::values::round_border_to_device_pixels;
 use crate::logical_geometry::WritingMode;
 use crate::media_queries::Device;
 use crate::properties::computed_value_flags::*;
 use crate::properties::longhands;
 use crate::rule_tree::StrongRuleNode;
 use crate::selector_parser::PseudoElement;
 use servo_arc::{Arc, RawOffsetArc};
 use std::marker::PhantomData;
 use std::mem::{forget, uninitialized, zeroed, ManuallyDrop};
 use std::{cmp, ops, ptr};
 use crate::values::{self, CustomIdent, Either, KeyframesName, None_};
-use crate::values::computed::{NonNegativeLength, Percentage, TransitionProperty};
+use crate::values::computed::{Percentage, TransitionProperty};
 use crate::values::computed::url::ComputedImageUrl;
 use crate::values::computed::BorderStyle;
 use crate::values::computed::font::FontSize;
-use crate::values::computed::effects::Filter;
 use crate::values::generics::column::ColumnCount;
 use crate::values::generics::transform::TransformStyle;
 use crate::values::generics::url::UrlOrNone;
 
 
 pub mod style_structs {
     % for style_struct in data.style_structs:
     pub use super::${style_struct.gecko_struct_name} as ${style_struct.name};
@@ -2157,16 +2154,17 @@ fn static_assert() {
 
     <% clear_keyword = Keyword(
         "clear",
         "Left Right None Both",
         gecko_enum_prefix="StyleClear",
         gecko_inexhaustive=True,
     ) %>
     ${impl_keyword('clear', 'mBreakType', clear_keyword)}
+
     ${impl_transition_time_value('delay', 'Delay')}
     ${impl_transition_time_value('duration', 'Duration')}
     ${impl_transition_timing_function()}
 
     pub fn transition_combined_duration_at(&self, index: usize) -> f32 {
         // https://drafts.csswg.org/css-transitions/#transition-combined-duration
         self.gecko.mTransitions[index % self.gecko.mTransitionDurationCount as usize].mDuration.max(0.0)
             + self.gecko.mTransitions[index % self.gecko.mTransitionDelayCount as usize].mDelay
@@ -2921,18 +2919,17 @@ fn static_assert() {
         longhands::_x_span::computed_value::T(
             self.gecko.mSpan
         )
     }
 
     ${impl_simple_copy('_x_span', 'mSpan')}
 </%self:impl_trait>
 
-<%self:impl_trait style_struct_name="Effects"
-                  skip_longhands="clip filter">
+<%self:impl_trait style_struct_name="Effects" skip_longhands="clip">
     pub fn set_clip(&mut self, v: longhands::clip::computed_value::T) {
         use crate::gecko_bindings::structs::NS_STYLE_CLIP_AUTO;
         use crate::gecko_bindings::structs::NS_STYLE_CLIP_RECT;
         use crate::gecko_bindings::structs::NS_STYLE_CLIP_LEFT_AUTO;
         use crate::gecko_bindings::structs::NS_STYLE_CLIP_TOP_AUTO;
         use crate::gecko_bindings::structs::NS_STYLE_CLIP_RIGHT_AUTO;
         use crate::gecko_bindings::structs::NS_STYLE_CLIP_BOTTOM_AUTO;
         use crate::values::generics::length::LengthPercentageOrAuto::*;
@@ -3030,146 +3027,16 @@ fn static_assert() {
             debug_assert_eq!(self.gecko.mClip.width, 1 << 30); // NS_MAXSIZE
             Auto
         } else {
             LengthPercentage(Au(self.gecko.mClip.x + self.gecko.mClip.width).into())
         };
 
         Either::First(ClipRect { top, right, bottom, left })
     }
-
-    <%
-    # This array is several filter function which has percentage or
-    # 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<I>(&mut self, v: I)
-    where
-        I: IntoIterator<Item = Filter>,
-        I::IntoIter: ExactSizeIterator,
-    {
-        use crate::values::generics::effects::Filter::*;
-        use crate::gecko_bindings::structs::nsStyleFilter;
-        use crate::gecko_bindings::structs::NS_STYLE_FILTER_BLUR;
-        use crate::gecko_bindings::structs::NS_STYLE_FILTER_BRIGHTNESS;
-        use crate::gecko_bindings::structs::NS_STYLE_FILTER_CONTRAST;
-        use crate::gecko_bindings::structs::NS_STYLE_FILTER_GRAYSCALE;
-        use crate::gecko_bindings::structs::NS_STYLE_FILTER_INVERT;
-        use crate::gecko_bindings::structs::NS_STYLE_FILTER_OPACITY;
-        use crate::gecko_bindings::structs::NS_STYLE_FILTER_SATURATE;
-        use crate::gecko_bindings::structs::NS_STYLE_FILTER_SEPIA;
-        use crate::gecko_bindings::structs::NS_STYLE_FILTER_HUE_ROTATE;
-        use crate::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);
-        }
-
-        let v = v.into_iter();
-        unsafe {
-            Gecko_ResetFilters(&mut *self.gecko, v.len());
-        }
-        debug_assert_eq!(v.len(), self.gecko.mFilters.len());
-
-        for (servo, gecko_filter) in v.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.0),
-                                               gecko_filter),
-                % endfor
-                Blur(length) => fill_filter(NS_STYLE_FILTER_BLUR,
-                                            CoordDataValue::Coord(length.0.to_i32_au()),
-                                            gecko_filter),
-
-                HueRotate(angle) => fill_filter(NS_STYLE_FILTER_HUE_ROTATE,
-                                                CoordDataValue::from(angle),
-                                                gecko_filter),
-
-                DropShadow(shadow) => {
-                    gecko_filter.mType = NS_STYLE_FILTER_DROP_SHADOW;
-                    unsafe {
-                        let ref mut union = gecko_filter.__bindgen_anon_1;
-                        ptr::write(union.mDropShadow.as_mut(), shadow);
-                    }
-                },
-                Url(ref url) => {
-                    unsafe {
-                        bindings::Gecko_nsStyleFilter_SetURLValue(gecko_filter, url);
-                    }
-                },
-            }
-        }
-    }
-
-    pub fn copy_filter_from(&mut self, other: &Self) {
-        unsafe {
-            Gecko_CopyFiltersFrom(&other.gecko as *const _ as *mut _, &mut *self.gecko);
-        }
-    }
-
-    pub fn reset_filter(&mut self, other: &Self) {
-        self.copy_filter_from(other)
-    }
-
-    pub fn clone_filter(&self) -> longhands::filter::computed_value::T {
-        use crate::values::generics::effects::Filter;
-        use crate::gecko_bindings::structs::NS_STYLE_FILTER_BLUR;
-        use crate::gecko_bindings::structs::NS_STYLE_FILTER_BRIGHTNESS;
-        use crate::gecko_bindings::structs::NS_STYLE_FILTER_CONTRAST;
-        use crate::gecko_bindings::structs::NS_STYLE_FILTER_GRAYSCALE;
-        use crate::gecko_bindings::structs::NS_STYLE_FILTER_INVERT;
-        use crate::gecko_bindings::structs::NS_STYLE_FILTER_OPACITY;
-        use crate::gecko_bindings::structs::NS_STYLE_FILTER_SATURATE;
-        use crate::gecko_bindings::structs::NS_STYLE_FILTER_SEPIA;
-        use crate::gecko_bindings::structs::NS_STYLE_FILTER_HUE_ROTATE;
-        use crate::gecko_bindings::structs::NS_STYLE_FILTER_DROP_SHADOW;
-        use crate::gecko_bindings::structs::NS_STYLE_FILTER_URL;
-
-        longhands::filter::computed_value::List(self.gecko.mFilters.iter().map(|filter| {
-            match filter.mType {
-                % for func in FILTER_FUNCTIONS:
-                NS_STYLE_FILTER_${func.upper()} => {
-                    Filter::${func}(
-                        GeckoStyleCoordConvertible::from_gecko_style_coord(
-                            &filter.mFilterParameter
-                        ).unwrap()
-                    )
-                },
-                % endfor
-                NS_STYLE_FILTER_BLUR => {
-                    Filter::Blur(NonNegativeLength::from_gecko_style_coord(
-                        &filter.mFilterParameter
-                    ).unwrap())
-                },
-                NS_STYLE_FILTER_HUE_ROTATE => {
-                    Filter::HueRotate(GeckoStyleCoordConvertible::from_gecko_style_coord(
-                        &filter.mFilterParameter,
-                    ).unwrap())
-                },
-                NS_STYLE_FILTER_DROP_SHADOW => {
-                    Filter::DropShadow(unsafe {
-                        (*filter.__bindgen_anon_1.mDropShadow.as_ref()).clone()
-                    })
-                },
-                NS_STYLE_FILTER_URL => {
-                    Filter::Url(unsafe {
-                        filter.__bindgen_anon_1.mURL.as_ref().clone()
-                    })
-                }
-                _ => unreachable!("Unknown filter function?"),
-            }
-        }).collect())
-    }
-
 </%self:impl_trait>
 
 <%self:impl_trait style_struct_name="InheritedBox">
 </%self:impl_trait>
 
 <%self:impl_trait style_struct_name="InheritedTable"
                   skip_longhands="border-spacing">
 
--- a/servo/components/style/properties/helpers/animated_properties.mako.rs
+++ b/servo/components/style/properties/helpers/animated_properties.mako.rs
@@ -17,17 +17,17 @@ use crate::properties::longhands::visibi
 use crate::properties::LonghandId;
 use servo_arc::Arc;
 use smallvec::SmallVec;
 use std::ptr;
 use std::mem::{self, ManuallyDrop};
 use crate::hash::FxHashMap;
 use super::ComputedValues;
 use crate::values::animated::{Animate, Procedure, ToAnimatedValue, ToAnimatedZero};
-use crate::values::animated::effects::Filter as AnimatedFilter;
+use crate::values::animated::effects::AnimatedFilter;
 #[cfg(feature = "gecko")] use crate::values::computed::TransitionProperty;
 use crate::values::computed::{ClipRect, Context};
 use crate::values::computed::ToComputedValue;
 use crate::values::distance::{ComputeSquaredDistance, SquaredDistance};
 use crate::values::generics::effects::Filter;
 use void::{self, Void};
 
 /// Convert nsCSSPropertyID to TransitionProperty
--- a/servo/components/style/properties/longhands/effects.mako.rs
+++ b/servo/components/style/properties/longhands/effects.mako.rs
@@ -42,16 +42,18 @@
     spec="https://drafts.fxtf.org/css-masking/#clip-property",
 )}
 
 ${helpers.predefined_type(
     "filter",
     "Filter",
     None,
     vector=True,
+    simple_vector_bindings=True,
+    gecko_ffi_name="mFilters",
     separator="Space",
     animation_value_type="AnimatedFilterList",
     vector_animation_type="with_zero",
     extra_prefixes="webkit",
     flags="CREATES_STACKING_CONTEXT FIXPOS_CB",
     spec="https://drafts.fxtf.org/filters/#propdef-filter",
 )}
 
--- a/servo/components/style/values/animated/effects.rs
+++ b/servo/components/style/values/animated/effects.rs
@@ -14,13 +14,13 @@ use crate::values::generics::effects::Si
 #[cfg(not(feature = "gecko"))]
 use crate::values::Impossible;
 
 /// An animated value for the `drop-shadow()` filter.
 pub type AnimatedSimpleShadow = GenericSimpleShadow<Color, Length, Length>;
 
 /// An animated value for a single `filter`.
 #[cfg(feature = "gecko")]
-pub type Filter = GenericFilter<Angle, Number, Length, AnimatedSimpleShadow, ComputedUrl>;
+pub type AnimatedFilter = GenericFilter<Angle, Number, Length, AnimatedSimpleShadow, ComputedUrl>;
 
 /// An animated value for a single `filter`.
 #[cfg(not(feature = "gecko"))]
-pub type Filter = GenericFilter<Angle, Number, Length, Impossible, Impossible>;
+pub type AnimatedFilter = GenericFilter<Angle, Number, Length, Impossible, Impossible>;
--- a/servo/components/style/values/computed/effects.rs
+++ b/servo/components/style/values/computed/effects.rs
@@ -19,14 +19,14 @@ use crate::values::Impossible;
 pub type BoxShadow = GenericBoxShadow<Color, Length, NonNegativeLength, Length>;
 
 /// A computed value for a single `filter`.
 #[cfg(feature = "gecko")]
 pub type Filter =
     GenericFilter<Angle, NonNegativeNumber, NonNegativeLength, SimpleShadow, ComputedUrl>;
 
 /// A computed value for a single `filter`.
-#[cfg(not(feature = "gecko"))]
+#[cfg(feature = "servo")]
 pub type Filter =
     GenericFilter<Angle, NonNegativeNumber, NonNegativeLength, Impossible, Impossible>;
 
 /// A computed value for the `drop-shadow()` filter.
 pub type SimpleShadow = GenericSimpleShadow<Color, Length, NonNegativeLength>;
--- a/servo/components/style/values/generics/effects.rs
+++ b/servo/components/style/values/generics/effects.rs
@@ -29,32 +29,35 @@ pub struct GenericBoxShadow<Color, SizeL
     #[animation(constant)]
     #[css(represents_keyword)]
     pub inset: bool,
 }
 
 pub use self::GenericBoxShadow as BoxShadow;
 
 /// A generic value for a single `filter`.
+///
+/// cbindgen:derive-tagged-enum-copy-constructor=true
 #[cfg_attr(feature = "servo", derive(Deserialize, Serialize))]
-#[animation(no_bound(Url))]
+#[animation(no_bound(U))]
 #[derive(
     Clone,
     ComputeSquaredDistance,
     Debug,
     MallocSizeOf,
     PartialEq,
     SpecifiedValueInfo,
     ToAnimatedValue,
     ToComputedValue,
     ToCss,
     ToResolvedValue,
     ToShmem,
 )]
-pub enum Filter<Angle, Factor, Length, DropShadow, Url> {
+#[repr(C, u8)]
+pub enum GenericFilter<Angle, Factor, Length, Shadow, U> {
     /// `blur(<length>)`
     #[css(function)]
     Blur(Length),
     /// `brightness(<factor>)`
     #[css(function)]
     Brightness(Factor),
     /// `contrast(<factor>)`
     #[css(function)]
@@ -74,22 +77,24 @@ pub enum Filter<Angle, Factor, Length, D
     /// `saturate(<factor>)`
     #[css(function)]
     Saturate(Factor),
     /// `sepia(<factor>)`
     #[css(function)]
     Sepia(Factor),
     /// `drop-shadow(...)`
     #[css(function)]
-    DropShadow(DropShadow),
+    DropShadow(Shadow),
     /// `<url>`
     #[animation(error)]
-    Url(Url),
+    Url(U),
 }
 
+pub use self::GenericFilter as Filter;
+
 /// A generic value for the `drop-shadow()` filter and the `text-shadow` property.
 ///
 /// Contrary to the canonical order from the spec, the color is serialised
 /// first, like in Gecko and Webkit.
 #[derive(
     Animate,
     Clone,
     ComputeSquaredDistance,
--- a/servo/components/style/values/specified/effects.rs
+++ b/servo/components/style/values/specified/effects.rs
@@ -13,33 +13,35 @@ use crate::values::generics::effects::Bo
 use crate::values::generics::effects::Filter as GenericFilter;
 use crate::values::generics::effects::SimpleShadow as GenericSimpleShadow;
 use crate::values::generics::NonNegative;
 use crate::values::specified::color::Color;
 use crate::values::specified::length::{Length, NonNegativeLength};
 #[cfg(feature = "gecko")]
 use crate::values::specified::url::SpecifiedUrl;
 use crate::values::specified::{Angle, Number, NumberOrPercentage};
-#[cfg(not(feature = "gecko"))]
+#[cfg(feature = "servo")]
 use crate::values::Impossible;
 use crate::Zero;
 use cssparser::{self, BasicParseErrorKind, Parser, Token};
 use style_traits::{ParseError, StyleParseErrorKind, ValueParseErrorKind};
 
 /// A specified value for a single shadow of the `box-shadow` property.
 pub type BoxShadow =
     GenericBoxShadow<Option<Color>, Length, Option<NonNegativeLength>, Option<Length>>;
 
 /// A specified value for a single `filter`.
 #[cfg(feature = "gecko")]
-pub type Filter = GenericFilter<Angle, Factor, NonNegativeLength, SimpleShadow, SpecifiedUrl>;
+pub type SpecifiedFilter = GenericFilter<Angle, Factor, NonNegativeLength, SimpleShadow, SpecifiedUrl>;
 
 /// A specified value for a single `filter`.
-#[cfg(not(feature = "gecko"))]
-pub type Filter = GenericFilter<Angle, Factor, NonNegativeLength, Impossible, Impossible>;
+#[cfg(feature = "servo")]
+pub type SpecifiedFilter = GenericFilter<Angle, Factor, NonNegativeLength, Impossible, Impossible>;
+
+pub use self::SpecifiedFilter as Filter;
 
 /// A value for the `<factor>` parts in `Filter`.
 #[derive(Clone, Debug, MallocSizeOf, PartialEq, SpecifiedValueInfo, ToCss, ToShmem)]
 pub struct Factor(NumberOrPercentage);
 
 impl Factor {
     /// Parse this factor but clamp to one if the value is over 100%.
     #[inline]
--- a/servo/ports/geckolib/cbindgen.toml
+++ b/servo/ports/geckolib/cbindgen.toml
@@ -30,16 +30,21 @@ includes = ["mozilla/ServoStyleConstsFor
 [parse]
 parse_deps = true
 include = ["style", "cssparser", "style_traits", "servo_arc"]
 
 [struct]
 derive_eq = true
 derive_neq = true
 
+[defines]
+# This will actually never be defined, but is handy to avoid cbindgen
+# generating Servo-only types.
+"feature = servo" = "CBINDGEN_IS_SERVO"
+
 [macro_expansion]
 bitflags = true
 
 [enum]
 derive_helper_methods = true
 derive_const_casts = true
 derive_tagged_enum_destructor = true
 cast_assert_name = "MOZ_ASSERT"
@@ -138,16 +143,17 @@ include = [
   "Transform",
   "Rotate",
   "Scale",
   "Translate",
   "BorderImageWidth",
   "ComputedUrl",
   "ComputedImageUrl",
   "UrlOrNone",
+  "Filter",
 ]
 item_types = ["enums", "structs", "typedefs", "functions"]
 renaming_overrides_prefixing = true
 
 # Prevent some renaming for Gecko types that cbindgen doesn't otherwise understand.
 [export.rename]
 "nscolor" = "nscolor"
 "nsAtom" = "nsAtom"
@@ -359,25 +365,26 @@ renaming_overrides_prefixing = true
   inline nscolor ToColor() const;
 """
 
 "OwnedSlice" = """
   StyleOwnedSlice() :
     ptr((T*)alignof(T)),
     len(0) {}
 
-  // Should be easily implementable if wanted, but the default implementation would leak.
-  StyleOwnedSlice& operator=(const StyleOwnedSlice&) = delete;
-  StyleOwnedSlice& operator=(StyleOwnedSlice&&) = delete;
+  inline void Clear();
+  inline void CopyFrom(const StyleOwnedSlice&);
+  inline void SwapElements(StyleOwnedSlice&);
+
+  StyleOwnedSlice& operator=(const StyleOwnedSlice&);
+  StyleOwnedSlice& operator=(StyleOwnedSlice&&);
 
   inline StyleOwnedSlice(const StyleOwnedSlice&);
   inline StyleOwnedSlice(StyleOwnedSlice&&);
 
-  inline void Clear();
-
   inline ~StyleOwnedSlice();
 
   Span<const T> AsSpan() const {
     return MakeSpan(ptr, len);
   }
 
   size_t Length() const {
     return AsSpan().Length();
@@ -452,16 +459,19 @@ renaming_overrides_prefixing = true
 "Atom" = """
   StyleAtom(size_t) = delete;
   StyleAtom() = delete;
 
   // NOTE(emilio): For now we don't need to expose anything else, but it'd be trivial if we wanted to.
   inline bool IsStatic() const;
   inline nsAtom* AsAtom() const;
 
+  // Could be implemented if wanted.
+  StyleAtom& operator=(const StyleAtom&) = delete;
+
   inline StyleAtom(const StyleAtom& aOther);
   inline ~StyleAtom();
 """
 
 "OwnedStr" = """
   inline nsDependentCSubstring AsString() const;
 """