Bug 895182 - [CSS Filters] Implement parsing for blur, brightness, contrast, grayscale, invert, opacity, saturate, sepia. Co-authored with Dirk Schulze (krit). r=heycam
authorMax Vujovic <mvujovic@adobe.com>
Mon, 22 Jul 2013 15:08:33 -0700
changeset 152163 0f7620a5047a753f2f8b200d8a0e9979d215387d
parent 152162 ff3b2131de1289d1609dc69f14a569c839205815
child 152164 fe1213d6035d8a48ad630a73072c1f9523e837fc
push id2859
push userakeybl@mozilla.com
push dateMon, 16 Sep 2013 19:14:59 +0000
treeherdermozilla-beta@87d3c51cd2bf [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersheycam
bugs895182
milestone25.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 895182 - [CSS Filters] Implement parsing for blur, brightness, contrast, grayscale, invert, opacity, saturate, sepia. Co-authored with Dirk Schulze (krit). r=heycam
dom/locales/en-US/chrome/layout/css.properties
layout/base/nsLayoutUtils.cpp
layout/base/nsLayoutUtils.h
layout/generic/nsFrame.cpp
layout/style/nsCSSKeywordList.h
layout/style/nsCSSParser.cpp
layout/style/nsCSSPropList.h
layout/style/nsCSSProps.h
layout/style/nsComputedDOMStyle.cpp
layout/style/nsComputedDOMStyle.h
layout/style/nsRuleNode.cpp
layout/style/nsStyleStruct.cpp
layout/style/nsStyleStruct.h
layout/style/test/property_database.js
layout/svg/nsSVGEffects.cpp
layout/svg/nsSVGIntegrationUtils.cpp
layout/svg/nsSVGUtils.cpp
modules/libpref/src/init/all.js
--- a/dom/locales/en-US/chrome/layout/css.properties
+++ b/dom/locales/en-US/chrome/layout/css.properties
@@ -132,8 +132,13 @@ PESupportsConditionStartEOF2='not', '(',
 PESupportsConditionInParensEOF=')'
 PESupportsConditionNotEOF='not'
 PESupportsWhitespaceRequired=Expected whitespace after 'not', 'and', or 'or'.
 PESupportsConditionExpectedOpenParenOrFunction=Expected '(' or function while parsing supports condition but found '%1$S'.
 PESupportsConditionExpectedCloseParen=Expected ')' while parsing supports condition but found '%1$S'.
 PESupportsConditionExpectedStart2=Expected 'not', '(', or function while parsing supports condition but found '%1$S'.
 PESupportsConditionExpectedNot=Expected 'not' while parsing supports condition but found '%1$S'.
 PESupportsGroupRuleStart=Expected '{' to begin @supports rule but found '%1$S'.
+PEFilterEOF=filter
+PEExpectedNoneOrURL=Expected 'none' or URL but found '%1$S'.
+PEExpectedNoneOrURLOrFilterFunction=Expected 'none', URL, or filter function but found '%1$S'.
+PEExpectedNonnegativeNP=Expected non-negative number or percentage.
+PEFilterFunctionArgumentsParsingError=Error in parsing arguments for filter function.
--- a/layout/base/nsLayoutUtils.cpp
+++ b/layout/base/nsLayoutUtils.cpp
@@ -379,16 +379,32 @@ nsLayoutUtils::AnimatedImageLayersEnable
     Preferences::AddBoolVarCache(&sAnimatedImageLayersEnabled,
                                  "layout.animated-image-layers.enabled",
                                  false);
   }
 
   return sAnimatedImageLayersEnabled;
 }
 
+bool
+nsLayoutUtils::CSSFiltersEnabled()
+{
+  static bool sCSSFiltersEnabled;
+  static bool sCSSFiltersPrefCached = false;
+
+  if (!sCSSFiltersPrefCached) {
+    sCSSFiltersPrefCached = true;
+    Preferences::AddBoolVarCache(&sCSSFiltersEnabled,
+                                 "layout.css.filters.enabled",
+                                 false);
+  }
+
+  return sCSSFiltersEnabled;
+}
+
 void
 nsLayoutUtils::UnionChildOverflow(nsIFrame* aFrame,
                                   nsOverflowAreas& aOverflowAreas)
 {
   // Iterate over all children except pop-ups.
   const nsIFrame::ChildListIDs skip(nsIFrame::kPopupList |
                                     nsIFrame::kSelectPopupList);
   for (nsIFrame::ChildListIterator childLists(aFrame);
--- a/layout/base/nsLayoutUtils.h
+++ b/layout/base/nsLayoutUtils.h
@@ -1618,16 +1618,21 @@ public:
   static bool GPUImageScalingEnabled();
 
   /**
    * Checks whether we want to layerize animated images whenever possible.
    */
   static bool AnimatedImageLayersEnabled();
 
   /**
+   * Checks if we should enable parsing for CSS Filters.
+   */
+  static bool CSSFiltersEnabled();
+
+  /**
    * Unions the overflow areas of all non-popup children of aFrame with
    * aOverflowAreas.
    */
   static void UnionChildOverflow(nsIFrame* aFrame,
                                  nsOverflowAreas& aOverflowAreas);
 
   /**
    * Return whether this is a frame whose width is used when computing
--- a/layout/generic/nsFrame.cpp
+++ b/layout/generic/nsFrame.cpp
@@ -4959,17 +4959,17 @@ ComputeOutlineAndEffectsRect(nsIFrame* a
                              const nsSize& aNewSize,
                              bool aStoreRectProperties) {
   nsRect r = aOverflowRect;
 
   if (aFrame->GetStateBits() & NS_FRAME_SVG_LAYOUT) {
     // For SVG frames, we only need to account for filters.
     // TODO: We could also take account of clipPath and mask to reduce the
     // visual overflow, but that's not essential.
-    if (aFrame->StyleSVGReset()->mFilter) {
+    if (aFrame->StyleSVGReset()->SingleFilter()) {
       if (aStoreRectProperties) {
         aFrame->Properties().
           Set(nsIFrame::PreEffectsBBoxProperty(), new nsRect(r));
       }
       r = nsSVGUtils::GetPostFilterVisualOverflowRect(aFrame, aOverflowRect);
     }
     return r;
   }
--- a/layout/style/nsCSSKeywordList.h
+++ b/layout/style/nsCSSKeywordList.h
@@ -177,25 +177,27 @@ CSS_KEY(avoid, avoid)
 CSS_KEY(background, background)
 CSS_KEY(backwards, backwards)
 CSS_KEY(balance, balance)
 CSS_KEY(baseline, baseline)
 CSS_KEY(bidi-override, bidi_override)
 CSS_KEY(blink, blink)
 CSS_KEY(block, block)
 CSS_KEY(block-axis, block_axis)
+CSS_KEY(blur, blur)
 CSS_KEY(bold, bold)
 CSS_KEY(bolder, bolder)
 CSS_KEY(border-box, border_box)
 CSS_KEY(both, both)
 CSS_KEY(bottom, bottom)
 CSS_KEY(bottom-outside, bottom_outside)
 CSS_KEY(bounding-box, bounding_box)
 CSS_KEY(break-all, break_all)
 CSS_KEY(break-word, break_word)
+CSS_KEY(brightness, brightness)
 CSS_KEY(button, button)
 CSS_KEY(buttonface, buttonface)
 CSS_KEY(buttonhighlight, buttonhighlight)
 CSS_KEY(buttonshadow, buttonshadow)
 CSS_KEY(buttontext, buttontext)
 CSS_KEY(capitalize, capitalize)
 CSS_KEY(caption, caption)
 CSS_KEY(captiontext, captiontext)
@@ -215,16 +217,17 @@ CSS_KEY(collapse, collapse)
 CSS_KEY(common-ligatures, common_ligatures)
 CSS_KEY(column, column)
 CSS_KEY(column-reverse, column_reverse)
 CSS_KEY(condensed, condensed)
 CSS_KEY(contain, contain)
 CSS_KEY(content-box, content_box)
 CSS_KEY(context-menu, context_menu)
 CSS_KEY(continuous, continuous)
+CSS_KEY(contrast, contrast)
 CSS_KEY(copy, copy)
 CSS_KEY(contextual, contextual)
 CSS_KEY(cover, cover)
 CSS_KEY(crop, crop)
 CSS_KEY(cross, cross)
 CSS_KEY(crosshair, crosshair)
 CSS_KEY(currentcolor, currentcolor)
 CSS_KEY(dashed, dashed)
@@ -265,16 +268,17 @@ CSS_KEY(fixed, fixed)
 CSS_KEY(flat, flat)
 CSS_KEY(flex, flex)
 CSS_KEY(flex-end, flex_end)
 CSS_KEY(flex-start, flex_start)
 CSS_KEY(forwards, forwards)
 CSS_KEY(full-width, full_width)
 CSS_KEY(georgian, georgian)
 CSS_KEY(grad, grad)
+CSS_KEY(grayscale, grayscale)
 CSS_KEY(graytext, graytext)
 CSS_KEY(groove, groove)
 CSS_KEY(hebrew, hebrew)
 CSS_KEY(help, help)
 CSS_KEY(hidden, hidden)
 CSS_KEY(hide, hide)
 CSS_KEY(highlight, highlight)
 CSS_KEY(highlighttext, highlighttext)
@@ -301,16 +305,17 @@ CSS_KEY(initial, initial)
 CSS_KEY(inline, inline)
 CSS_KEY(inline-axis, inline_axis)
 CSS_KEY(inline-block, inline_block)
 CSS_KEY(inline-flex, inline_flex)
 CSS_KEY(inline-table, inline_table)
 CSS_KEY(inset, inset)
 CSS_KEY(inside, inside)
 CSS_KEY(interpolatematrix, interpolatematrix)
+CSS_KEY(invert, invert)
 CSS_KEY(italic, italic)
 CSS_KEY(jis78, jis78)
 CSS_KEY(jis83, jis83)
 CSS_KEY(jis90, jis90)
 CSS_KEY(jis04, jis04)
 CSS_KEY(justify, justify)
 CSS_KEY(katakana, katakana)
 CSS_KEY(katakana-iroha, katakana_iroha)
@@ -364,16 +369,17 @@ CSS_KEY(none, none)
 CSS_KEY(normal, normal)
 CSS_KEY(not-allowed, not_allowed)
 CSS_KEY(nowrap, nowrap)
 CSS_KEY(ns-resize, ns_resize)
 CSS_KEY(nw-resize, nw_resize)
 CSS_KEY(nwse-resize, nwse_resize)
 CSS_KEY(oblique, oblique)
 CSS_KEY(oldstyle-nums, oldstyle_nums)
+CSS_KEY(opacity, opacity)
 CSS_KEY(open-quote, open_quote)
 CSS_KEY(ordinal, ordinal)
 CSS_KEY(ornaments, ornaments)
 CSS_KEY(outset, outset)
 CSS_KEY(outside, outside)
 CSS_KEY(overline, overline)
 CSS_KEY(padding-box, padding_box)
 CSS_KEY(painted, painted)
@@ -413,16 +419,17 @@ CSS_KEY(round, round)
 CSS_KEY(row, row)
 CSS_KEY(row-resize, row_resize)
 CSS_KEY(row-reverse, row_reverse)
 CSS_KEY(rtl, rtl)
 CSS_KEY(ruby, ruby)
 CSS_KEY(running, running)
 CSS_KEY(s, s)
 CSS_KEY(s-resize, s_resize)
+CSS_KEY(saturate, saturate)
 CSS_KEY(scale, scale)
 CSS_KEY(scale3d, scale3d)
 CSS_KEY(scalex, scalex)
 CSS_KEY(scaley, scaley)
 CSS_KEY(scalez, scalez)
 CSS_KEY(scroll, scroll)
 CSS_KEY(scrollbar, scrollbar)
 CSS_KEY(scrollbar-small, scrollbar_small)
@@ -430,16 +437,17 @@ CSS_KEY(se-resize, se_resize)
 CSS_KEY(select-after, select_after)
 CSS_KEY(select-all, select_all)
 CSS_KEY(select-before, select_before)
 CSS_KEY(select-menu, select_menu)
 CSS_KEY(select-same, select_same)
 CSS_KEY(semi-condensed, semi_condensed)
 CSS_KEY(semi-expanded, semi_expanded)
 CSS_KEY(separate, separate)
+CSS_KEY(sepia, sepia)
 CSS_KEY(show, show)
 CSS_KEY(simplified, simplified)
 CSS_KEY(skew, skew)
 CSS_KEY(skewx, skewx)
 CSS_KEY(skewy, skewy)
 CSS_KEY(slashed-zero, slashed_zero)
 CSS_KEY(small, small)
 CSS_KEY(small-caps, small_caps)
--- a/layout/style/nsCSSParser.cpp
+++ b/layout/style/nsCSSParser.cpp
@@ -599,16 +599,20 @@ protected:
                               int32_t aVariantMaskAll,
                               uint16_t aMinElems,
                               uint16_t aMaxElems,
                               InfallibleTArray<nsCSSValue>& aOutput);
 
   /* Functions for transform-origin/perspective-origin Parsing */
   bool ParseTransformOrigin(bool aPerspective);
 
+  /* Functions for filter parsing */
+  bool ParseFilter();
+  bool ParseSingleFilter(nsCSSValue* aValue);
+
   /* Find and return the namespace ID associated with aPrefix.
      If aPrefix has not been declared in an @namespace rule, returns
      kNameSpaceID_Unknown. */
   int32_t GetNamespaceIdForPrefix(const nsString& aPrefix);
 
   /* Find the correct default namespace, and set it on aSelector. */
   void SetDefaultNamespaceOnSelector(nsCSSSelector& aSelector);
 
@@ -6465,16 +6469,18 @@ CSSParserImpl::ParsePropertyByFunction(n
     return ParseBorderSide(kColumnRuleIDs, false);
   case eCSSProperty_content:
     return ParseContent();
   case eCSSProperty_counter_increment:
   case eCSSProperty_counter_reset:
     return ParseCounterData(aPropID);
   case eCSSProperty_cursor:
     return ParseCursor();
+  case eCSSProperty_filter:
+    return ParseFilter();
   case eCSSProperty_flex:
     return ParseFlex();
   case eCSSProperty_font:
     return ParseFont();
   case eCSSProperty_image_region:
     return ParseRect(eCSSProperty_image_region);
   case eCSSProperty_list_style:
     return ParseListStyle();
@@ -10027,16 +10033,154 @@ bool CSSParserImpl::ParseTransformOrigin
       value.SetTripletValue(position.mXValue, position.mYValue, depth);
     }
 
     AppendValue(prop, value);
   }
   return true;
 }
 
+/**
+ * Reads a single url or filter function from the tokenizer stream, reporting an
+ * error if something goes wrong.
+ */
+bool
+CSSParserImpl::ParseSingleFilter(nsCSSValue* aValue)
+{
+  if (ParseVariant(*aValue, VARIANT_URL, nullptr)) {
+    return true;
+  }
+
+  if (!nsLayoutUtils::CSSFiltersEnabled()) {
+    // With CSS Filters disabled, we should only accept an SVG reference filter.
+    REPORT_UNEXPECTED_TOKEN(PEExpectedNoneOrURL);
+    return false;
+  }
+
+  if (!GetToken(true)) {
+    REPORT_UNEXPECTED_EOF(PEFilterEOF);
+    return false;
+  }
+
+  if (mToken.mType != eCSSToken_Function) {
+    REPORT_UNEXPECTED_TOKEN(PEExpectedNoneOrURLOrFilterFunction);
+    return false;
+  }
+
+  // Set up the parsing rules based on the filter function.
+  int32_t variantMask = VARIANT_PN;
+  bool rejectNegativeArgument = true;
+  bool clampArgumentToOne = false;
+  nsCSSKeyword functionName = nsCSSKeywords::LookupKeyword(mToken.mIdent);
+  switch (functionName) {
+    case eCSSKeyword_blur:
+      variantMask = VARIANT_LCALC | VARIANT_NONNEGATIVE_DIMENSION;
+      // VARIANT_NONNEGATIVE_DIMENSION will already reject negative lengths.
+      rejectNegativeArgument = false;
+      break;
+    case eCSSKeyword_grayscale:
+    case eCSSKeyword_invert:
+    case eCSSKeyword_sepia:
+    case eCSSKeyword_opacity:
+      clampArgumentToOne = true;
+      break;
+    case eCSSKeyword_brightness:
+    case eCSSKeyword_contrast:
+    case eCSSKeyword_saturate:
+      break;
+    default:
+      // Unrecognized filter function.
+      REPORT_UNEXPECTED_TOKEN(PEExpectedNoneOrURLOrFilterFunction);
+      SkipUntil(')');
+      return false;
+  }
+
+  // Parse the function.
+  uint16_t minElems = 1U;
+  uint16_t maxElems = 1U;
+  uint32_t allVariants = 0;
+  if (!ParseFunction(functionName, &variantMask, allVariants,
+                     minElems, maxElems, *aValue)) {
+    REPORT_UNEXPECTED(PEFilterFunctionArgumentsParsingError);
+    return false;
+  }
+
+  // Get the first and only argument to the filter function.
+  NS_ABORT_IF_FALSE(aValue->GetUnit() == eCSSUnit_Function,
+                    "expected a filter function");
+  NS_ABORT_IF_FALSE(aValue->UnitHasArrayValue(),
+                    "filter function should be an array");
+  NS_ABORT_IF_FALSE(aValue->GetArrayValue()->Count() == 2,
+                    "filter function should have exactly one argument");
+  nsCSSValue& arg = aValue->GetArrayValue()->Item(1);
+
+  if (rejectNegativeArgument &&
+      ((arg.GetUnit() == eCSSUnit_Percent && arg.GetPercentValue() < 0.0f) ||
+       (arg.GetUnit() == eCSSUnit_Number && arg.GetFloatValue() < 0.0f))) {
+    REPORT_UNEXPECTED(PEExpectedNonnegativeNP);
+    return false;
+  }
+
+  if (clampArgumentToOne) {
+    if (arg.GetUnit() == eCSSUnit_Number &&
+        arg.GetFloatValue() > 1.0f) {
+      arg.SetFloatValue(1.0f, arg.GetUnit());
+    } else if (arg.GetUnit() == eCSSUnit_Percent &&
+               arg.GetPercentValue() > 1.0f) {
+      arg.SetPercentValue(1.0f);
+    }
+  }
+
+  return true;
+}
+
+/**
+ * Parses a filter property value by continuously reading in urls and/or filter
+ * functions and constructing a list.
+ *
+ * When CSS Filters are enabled, the filter property accepts one or more SVG
+ * reference filters and/or CSS filter functions.
+ * e.g. filter: url(#my-filter-1) blur(3px) url(#my-filter-2) grayscale(50%);
+ *
+ * When CSS Filters are disabled, the filter property only accepts one SVG
+ * reference filter.
+ * e.g. filter: url(#my-filter);
+ */
+bool
+CSSParserImpl::ParseFilter()
+{
+  nsCSSValue value;
+  if (ParseVariant(value, VARIANT_INHERIT | VARIANT_NONE, nullptr)) {
+    // 'inherit', 'initial', and 'none' must be alone
+    if (!ExpectEndProperty()) {
+      return false;
+    }
+  } else {
+    nsCSSValueList* cur = value.SetListValue();
+    while (cur) {
+      if (!ParseSingleFilter(&cur->mValue)) {
+        return false;
+      }
+      if (CheckEndProperty()) {
+        break;
+      }
+      if (!nsLayoutUtils::CSSFiltersEnabled()) {
+        // With CSS Filters disabled, we should only accept one SVG reference
+        // filter.
+        REPORT_UNEXPECTED_TOKEN(PEExpectEndValue);
+        return false;
+      }
+      cur->mNext = new nsCSSValueList;
+      cur = cur->mNext;
+    }
+  }
+  AppendValue(eCSSProperty_filter, value);
+  return true;
+}
+
 bool
 CSSParserImpl::ParseTransitionProperty()
 {
   nsCSSValue value;
   if (ParseVariant(value, VARIANT_INHERIT | VARIANT_NONE, nullptr)) {
     // 'inherit', 'initial', and 'none' must be alone
     if (!ExpectEndProperty()) {
       return false;
--- a/layout/style/nsCSSPropList.h
+++ b/layout/style/nsCSSPropList.h
@@ -3322,19 +3322,19 @@ CSS_PROP_SVG(
     VARIANT_HK,
     kFillRuleKTable,
     offsetof(nsStyleSVG, mFillRule),
     eStyleAnimType_EnumU8)
 CSS_PROP_SVGRESET(
     filter,
     filter,
     Filter,
-    CSS_PROPERTY_PARSE_VALUE,
+    CSS_PROPERTY_PARSE_FUNCTION,
     "",
-    VARIANT_HUO,
+    0,
     nullptr,
     CSS_PROP_NO_OFFSET,
     eStyleAnimType_None)
 CSS_PROP_SVGRESET(
     flood-color,
     flood_color,
     FloodColor,
     CSS_PROPERTY_PARSE_VALUE,
--- a/layout/style/nsCSSProps.h
+++ b/layout/style/nsCSSProps.h
@@ -84,18 +84,19 @@
 #define VARIANT_ALPN (VARIANT_AL | VARIANT_PN)
 #define VARIANT_HN   (VARIANT_INHERIT | VARIANT_NUMBER)
 #define VARIANT_HON  (VARIANT_HN | VARIANT_NONE)
 #define VARIANT_HOS  (VARIANT_INHERIT | VARIANT_NONE | VARIANT_STRING)
 #define VARIANT_LPN  (VARIANT_LP | VARIANT_NUMBER)
 #define VARIANT_UK   (VARIANT_URL | VARIANT_KEYWORD)
 #define VARIANT_UO   (VARIANT_URL | VARIANT_NONE)
 #define VARIANT_ANGLE_OR_ZERO (VARIANT_ANGLE | VARIANT_ZERO_ANGLE)
-#define VARIANT_LPCALC  (VARIANT_LENGTH | VARIANT_CALC | VARIANT_PERCENT)
-#define VARIANT_LNCALC  (VARIANT_LENGTH | VARIANT_CALC | VARIANT_NUMBER)
+#define VARIANT_LCALC  (VARIANT_LENGTH | VARIANT_CALC)
+#define VARIANT_LPCALC (VARIANT_LCALC | VARIANT_PERCENT)
+#define VARIANT_LNCALC (VARIANT_LCALC | VARIANT_NUMBER)
 #define VARIANT_LPNCALC (VARIANT_LNCALC | VARIANT_PERCENT)
 #define VARIANT_IMAGE   (VARIANT_URL | VARIANT_NONE | VARIANT_GRADIENT | \
                         VARIANT_IMAGE_RECT | VARIANT_ELEMENT)
 
 // Flags for the kFlagsTable bitfield (flags_ in nsCSSPropList.h)
 
 // A property that is a *-ltr-source or *-rtl-source property for one of
 // the directional pseudo-shorthand properties.
--- a/layout/style/nsComputedDOMStyle.cpp
+++ b/layout/style/nsComputedDOMStyle.cpp
@@ -4463,29 +4463,106 @@ nsComputedDOMStyle::DoGetClipPath()
   if (svg->mClipPath)
     val->SetURI(svg->mClipPath);
   else
     val->SetIdent(eCSSKeyword_none);
 
   return val;
 }
 
+void
+nsComputedDOMStyle::SetCssTextToCoord(nsAString& aCssText,
+                                      const nsStyleCoord& aCoord)
+{
+  nsROCSSPrimitiveValue* value = new nsROCSSPrimitiveValue;
+  bool clampNegativeCalc = true;
+  SetValueToCoord(value, aCoord, clampNegativeCalc);
+  value->GetCssText(aCssText);
+  delete value;
+}
+
+static void
+GetFilterFunctionName(nsAString& aString, nsStyleFilter::Type mType)
+{
+  switch (mType) {
+    case nsStyleFilter::Type::eBlur:
+      aString.AssignLiteral("blur(");
+      break;
+    case nsStyleFilter::Type::eBrightness:
+      aString.AssignLiteral("brightness(");
+      break;
+    case nsStyleFilter::Type::eContrast:
+      aString.AssignLiteral("contrast(");
+      break;
+    case nsStyleFilter::Type::eGrayscale:
+      aString.AssignLiteral("grayscale(");
+      break;
+    case nsStyleFilter::Type::eInvert:
+      aString.AssignLiteral("invert(");
+      break;
+    case nsStyleFilter::Type::eOpacity:
+      aString.AssignLiteral("opacity(");
+      break;
+    case nsStyleFilter::Type::eSaturate:
+      aString.AssignLiteral("saturate(");
+      break;
+    case nsStyleFilter::Type::eSepia:
+      aString.AssignLiteral("sepia(");
+      break;
+    default:
+      NS_NOTREACHED("unrecognized filter type");
+  }
+}
+
+nsROCSSPrimitiveValue*
+nsComputedDOMStyle::CreatePrimitiveValueForStyleFilter(
+  const nsStyleFilter& aStyleFilter)
+{
+  nsROCSSPrimitiveValue* value = new nsROCSSPrimitiveValue;
+
+  // Handle url().
+  if (nsStyleFilter::Type::eURL == aStyleFilter.mType) {
+    value->SetURI(aStyleFilter.mURL);
+    return value;
+  }
+
+  // Filter function name and opening parenthesis.
+  nsAutoString filterFunctionString;
+  GetFilterFunctionName(filterFunctionString, aStyleFilter.mType);
+
+  // Filter function argument.
+  nsAutoString argumentString;
+  SetCssTextToCoord(argumentString, aStyleFilter.mCoord);
+  filterFunctionString.Append(argumentString);
+
+  // Filter function closing parenthesis.
+  filterFunctionString.AppendLiteral(")");
+
+  value->SetString(filterFunctionString);
+  return value;
+}
+
 CSSValue*
 nsComputedDOMStyle::DoGetFilter()
 {
-  nsROCSSPrimitiveValue* val = new nsROCSSPrimitiveValue;
-
-  const nsStyleSVGReset* svg = StyleSVGReset();
-
-  if (svg->mFilter)
-    val->SetURI(svg->mFilter);
-  else
-    val->SetIdent(eCSSKeyword_none);
-
-  return val;
+  const nsTArray<nsStyleFilter>& filters = StyleSVGReset()->mFilters;
+
+  if (filters.IsEmpty()) {
+    nsROCSSPrimitiveValue* value = new nsROCSSPrimitiveValue;
+    value->SetIdent(eCSSKeyword_none);
+    return value;
+  }
+
+  nsDOMCSSValueList* valueList = GetROCSSValueList(false);
+  for(uint32_t i = 0; i < filters.Length(); i++) {
+    nsROCSSPrimitiveValue* value =
+      CreatePrimitiveValueForStyleFilter(filters[i]);
+    valueList->AppendCSSValue(value);
+  }
+  return valueList;
 }
 
 CSSValue*
 nsComputedDOMStyle::DoGetMask()
 {
   nsROCSSPrimitiveValue* val = new nsROCSSPrimitiveValue;
 
   const nsStyleSVGReset* svg = StyleSVGReset();
--- a/layout/style/nsComputedDOMStyle.h
+++ b/layout/style/nsComputedDOMStyle.h
@@ -486,16 +486,21 @@ private:
 
   bool GetCBContentWidth(nscoord& aWidth);
   bool GetCBContentHeight(nscoord& aWidth);
   bool GetFrameBoundsWidthForTransform(nscoord &aWidth);
   bool GetFrameBoundsHeightForTransform(nscoord &aHeight);
   bool GetFrameBorderRectWidth(nscoord& aWidth);
   bool GetFrameBorderRectHeight(nscoord& aHeight);
 
+  /* Helper functions for computing the filter property style. */
+  void SetCssTextToCoord(nsAString& aCssText, const nsStyleCoord& aCoord);
+  nsROCSSPrimitiveValue* CreatePrimitiveValueForStyleFilter(
+    const nsStyleFilter& aStyleFilter);
+
   struct ComputedStyleMapEntry
   {
     // Create a pointer-to-member-function type.
     typedef mozilla::dom::CSSValue* (nsComputedDOMStyle::*ComputeMethod)();
 
     nsCSSProperty mProperty;
     ComputeMethod mGetter;
     bool mNeedsLayoutFlush;
--- a/layout/style/nsRuleNode.cpp
+++ b/layout/style/nsRuleNode.cpp
@@ -7702,16 +7702,78 @@ nsRuleNode::ComputeSVGData(void* aStartS
   SetDiscrete(*aRuleData->ValueForTextRendering(),
               svg->mTextRendering, canStoreInRuleTree,
               SETDSC_ENUMERATED, parentSVG->mTextRendering,
               NS_STYLE_TEXT_RENDERING_AUTO, 0, 0, 0, 0);
 
   COMPUTE_END_INHERITED(SVG, svg)
 }
 
+static nsStyleFilter::Type
+StyleFilterTypeForFunctionName(nsCSSKeyword functionName)
+{
+  switch (functionName) {
+    case eCSSKeyword_blur:
+      return nsStyleFilter::Type::eBlur;
+    case eCSSKeyword_brightness:
+      return nsStyleFilter::Type::eBrightness;
+    case eCSSKeyword_contrast:
+      return nsStyleFilter::Type::eContrast;
+    case eCSSKeyword_grayscale:
+      return nsStyleFilter::Type::eGrayscale;
+    case eCSSKeyword_invert:
+      return nsStyleFilter::Type::eInvert;
+    case eCSSKeyword_opacity:
+      return nsStyleFilter::Type::eOpacity;
+    case eCSSKeyword_saturate:
+      return nsStyleFilter::Type::eSaturate;
+    case eCSSKeyword_sepia:
+      return nsStyleFilter::Type::eSepia;
+    default:
+      NS_NOTREACHED("Unknown filter type.");
+      return nsStyleFilter::Type::eNull;
+  }
+}
+
+static void
+SetStyleFilterToCSSValue(nsStyleFilter* aStyleFilter,
+                         const nsCSSValue& aValue,
+                         nsStyleContext* aStyleContext,
+                         nsPresContext* aPresContext,
+                         bool& aCanStoreInRuleTree)
+{
+  nsCSSUnit unit = aValue.GetUnit();
+  if (unit == eCSSUnit_URL) {
+    aStyleFilter->mType = nsStyleFilter::Type::eURL;
+    aStyleFilter->mURL = aValue.GetURLValue();
+    return;
+  }
+
+  NS_ABORT_IF_FALSE(unit == eCSSUnit_Function, "expected a filter function");
+
+  nsCSSValue::Array* filterFunction = aValue.GetArrayValue();
+  nsCSSKeyword functionName =
+    (nsCSSKeyword)filterFunction->Item(0).GetIntValue();
+  aStyleFilter->mType = StyleFilterTypeForFunctionName(functionName);
+
+  int32_t mask = SETCOORD_PERCENT | SETCOORD_FACTOR;
+  if (aStyleFilter->mType == nsStyleFilter::Type::eBlur)
+    mask = SETCOORD_LENGTH | SETCOORD_STORE_CALC;
+
+  NS_ABORT_IF_FALSE(filterFunction->Count() == 2,
+                    "all filter functions except drop-shadow should have "
+                    "exactly one argument");
+
+  nsCSSValue& arg = filterFunction->Item(1);
+  DebugOnly<bool> success = SetCoord(arg, aStyleFilter->mCoord, nsStyleCoord(),
+                                     mask, aStyleContext, aPresContext,
+                                     aCanStoreInRuleTree);
+  NS_ABORT_IF_FALSE(success, "unexpected unit");
+}
+
 const void*
 nsRuleNode::ComputeSVGResetData(void* aStartStruct,
                                 const nsRuleData* aRuleData,
                                 nsStyleContext* aContext,
                                 nsRuleNode* aHighestNode,
                                 const RuleDetail aRuleDetail,
                                 const bool aCanStoreInRuleTree)
 {
@@ -7778,24 +7840,44 @@ nsRuleNode::ComputeSVGResetData(void* aS
   SetDiscrete(*aRuleData->ValueForVectorEffect(),
               svgReset->mVectorEffect,
               canStoreInRuleTree, SETDSC_ENUMERATED,
               parentSVGReset->mVectorEffect,
               NS_STYLE_VECTOR_EFFECT_NONE, 0, 0, 0, 0);
 
   // filter: url, none, inherit
   const nsCSSValue* filterValue = aRuleData->ValueForFilter();
-  if (eCSSUnit_URL == filterValue->GetUnit()) {
-    svgReset->mFilter = filterValue->GetURLValue();
-  } else if (eCSSUnit_None == filterValue->GetUnit() ||
-             eCSSUnit_Initial == filterValue->GetUnit()) {
-    svgReset->mFilter = nullptr;
-  } else if (eCSSUnit_Inherit == filterValue->GetUnit()) {
-    canStoreInRuleTree = false;
-    svgReset->mFilter = parentSVGReset->mFilter;
+  switch (filterValue->GetUnit()) {
+    case eCSSUnit_Null:
+      break;
+    case eCSSUnit_None:
+    case eCSSUnit_Initial:
+      svgReset->mFilters.Clear();
+      break;
+    case eCSSUnit_Inherit:
+      canStoreInRuleTree = false;
+      svgReset->mFilters = parentSVGReset->mFilters;
+      break;
+    case eCSSUnit_List:
+    case eCSSUnit_ListDep: {
+      svgReset->mFilters.Clear();
+      const nsCSSValueList* cur = filterValue->GetListValue();
+      while(cur) {
+        nsStyleFilter styleFilter;
+        SetStyleFilterToCSSValue(&styleFilter, cur->mValue, aContext,
+                                 mPresContext, canStoreInRuleTree);
+        NS_ABORT_IF_FALSE(styleFilter.mType != nsStyleFilter::Type::eNull,
+                          "filter should be set");
+        svgReset->mFilters.AppendElement(styleFilter);
+        cur = cur->mNext;
+      }
+      break;
+    }
+    default:
+      NS_NOTREACHED("unexpected unit");
   }
 
   // mask: url, none, inherit
   const nsCSSValue* maskValue = aRuleData->ValueForMask();
   if (eCSSUnit_URL == maskValue->GetUnit()) {
     svgReset->mMask = maskValue->GetURLValue();
   } else if (eCSSUnit_None == maskValue->GetUnit() ||
              eCSSUnit_Initial == maskValue->GetUnit()) {
--- a/layout/style/nsStyleStruct.cpp
+++ b/layout/style/nsStyleStruct.cpp
@@ -996,26 +996,67 @@ nsChangeHint nsStyleSVG::CalcDifference(
       NS_UpdateHint(hint, nsChangeHint_RepaintFrame);
       return hint;
     }
 
   return hint;
 }
 
 // --------------------
+// nsStyleFilter
+//
+nsStyleFilter::nsStyleFilter()
+  : mType(eNull)
+{
+  MOZ_COUNT_CTOR(nsStyleFilter);
+}
+
+nsStyleFilter::nsStyleFilter(const nsStyleFilter& aSource)
+  : mType(aSource.mType)
+{
+  MOZ_COUNT_CTOR(nsStyleFilter);
+
+  if (mType == eURL) {
+    mURL = aSource.mURL;
+  } else if (mType != eNull) {
+    mCoord = aSource.mCoord;
+  }
+}
+
+nsStyleFilter::~nsStyleFilter()
+{
+  MOZ_COUNT_DTOR(nsStyleFilter);
+}
+
+bool
+nsStyleFilter::operator==(const nsStyleFilter& aOther) const
+{
+  if (mType != aOther.mType) {
+      return false;
+  }
+
+  if (mType == eURL) {
+    return EqualURIs(mURL, aOther.mURL);
+  } else if (mType != eNull) {
+    return mCoord == aOther.mCoord;
+  }
+
+  return true;
+}
+
+// --------------------
 // nsStyleSVGReset
 //
 nsStyleSVGReset::nsStyleSVGReset() 
 {
     MOZ_COUNT_CTOR(nsStyleSVGReset);
     mStopColor               = NS_RGB(0,0,0);
     mFloodColor              = NS_RGB(0,0,0);
     mLightingColor           = NS_RGB(255,255,255);
     mClipPath                = nullptr;
-    mFilter                  = nullptr;
     mMask                    = nullptr;
     mStopOpacity             = 1.0f;
     mFloodOpacity            = 1.0f;
     mDominantBaseline        = NS_STYLE_DOMINANT_BASELINE_AUTO;
     mVectorEffect            = NS_STYLE_VECTOR_EFFECT_NONE;
     mMaskType                = NS_STYLE_MASK_TYPE_LUMINANCE;
 }
 
@@ -1026,32 +1067,32 @@ nsStyleSVGReset::~nsStyleSVGReset()
 
 nsStyleSVGReset::nsStyleSVGReset(const nsStyleSVGReset& aSource)
 {
   MOZ_COUNT_CTOR(nsStyleSVGReset);
   mStopColor = aSource.mStopColor;
   mFloodColor = aSource.mFloodColor;
   mLightingColor = aSource.mLightingColor;
   mClipPath = aSource.mClipPath;
-  mFilter = aSource.mFilter;
+  mFilters = aSource.mFilters;
   mMask = aSource.mMask;
   mStopOpacity = aSource.mStopOpacity;
   mFloodOpacity = aSource.mFloodOpacity;
   mDominantBaseline = aSource.mDominantBaseline;
   mVectorEffect = aSource.mVectorEffect;
   mMaskType = aSource.mMaskType;
 }
 
 nsChangeHint nsStyleSVGReset::CalcDifference(const nsStyleSVGReset& aOther) const
 {
   nsChangeHint hint = nsChangeHint(0);
 
   if (!EqualURIs(mClipPath, aOther.mClipPath) ||
-      !EqualURIs(mFilter, aOther.mFilter)     ||
-      !EqualURIs(mMask, aOther.mMask)) {
+      !EqualURIs(mMask, aOther.mMask) ||
+      mFilters != aOther.mFilters) {
     NS_UpdateHint(hint, nsChangeHint_UpdateEffects);
     NS_UpdateHint(hint, nsChangeHint_RepaintFrame);
   }
 
   if (mDominantBaseline != aOther.mDominantBaseline) {
     // XXXjwatt: why NS_STYLE_HINT_REFLOW? Isn't that excessive?
     NS_UpdateHint(hint, NS_STYLE_HINT_REFLOW);
   } else if (mVectorEffect  != aOther.mVectorEffect) {
--- a/layout/style/nsStyleStruct.h
+++ b/layout/style/nsStyleStruct.h
@@ -2263,16 +2263,42 @@ struct nsStyleSVG {
   bool mStrokeDashoffsetFromObject  : 1;
   bool mStrokeWidthFromObject       : 1;
 
   bool HasMarker() const {
     return mMarkerStart || mMarkerMid || mMarkerEnd;
   }
 };
 
+struct nsStyleFilter {
+  nsStyleFilter();
+  nsStyleFilter(const nsStyleFilter& aSource);
+  ~nsStyleFilter();
+
+  bool operator==(const nsStyleFilter& aOther) const;
+
+  enum Type {
+    eNull,
+    eURL,
+    eBlur,
+    eBrightness,
+    eContrast,
+    eInvert,
+    eOpacity,
+    eGrayscale,
+    eSaturate,
+    eSepia,
+  };
+
+  Type mType;
+  nsIURI* mURL;
+  nsStyleCoord mCoord;
+  // FIXME: Add a nsCSSShadowItem when we implement drop shadow.
+};
+
 struct nsStyleSVGReset {
   nsStyleSVGReset();
   nsStyleSVGReset(const nsStyleSVGReset& aSource);
   ~nsStyleSVGReset();
 
   void* operator new(size_t sz, nsPresContext* aContext) CPP_THROW_NEW {
     return aContext->AllocateFromShell(sz);
   }
@@ -2281,18 +2307,27 @@ struct nsStyleSVGReset {
     aContext->FreeToShell(sizeof(nsStyleSVGReset), this);
   }
 
   nsChangeHint CalcDifference(const nsStyleSVGReset& aOther) const;
   static nsChangeHint MaxDifference() {
     return NS_CombineHint(nsChangeHint_UpdateEffects, NS_STYLE_HINT_REFLOW);
   }
 
+  // The backend only supports one SVG reference right now.
+  // Eventually, it will support multiple chained SVG reference filters and CSS
+  // filter functions.
+  nsIURI* SingleFilter() const {
+    return (mFilters.Length() == 1 &&
+            mFilters[0].mType == nsStyleFilter::Type::eURL) ?
+            mFilters[0].mURL : nullptr;
+  }
+
   nsCOMPtr<nsIURI> mClipPath;         // [reset]
-  nsCOMPtr<nsIURI> mFilter;           // [reset]
+  nsTArray<nsStyleFilter> mFilters;   // [reset]
   nsCOMPtr<nsIURI> mMask;             // [reset]
   nscolor          mStopColor;        // [reset]
   nscolor          mFloodColor;       // [reset]
   nscolor          mLightingColor;    // [reset]
 
   float            mStopOpacity;      // [reset]
   float            mFloodOpacity;     // [reset]
 
--- a/layout/style/test/property_database.js
+++ b/layout/style/test/property_database.js
@@ -4406,8 +4406,185 @@ if (SpecialPowers.getBoolPref("svg.paint
     domProp: "paintOrder",
     inherited: true,
     type: CSS_TYPE_LONGHAND,
     initial_values: [ "normal" ],
     other_values: [ "fill", "fill stroke", "fill stroke markers", "stroke markers fill" ],
     invalid_values: [ "fill stroke markers fill", "fill normal" ]
   };
 }
+
+if (SpecialPowers.getBoolPref("layout.css.filters.enabled")) {
+  	gCSSProperties["filter"] = {
+		domProp: "filter",
+		inherited: false,
+		type: CSS_TYPE_LONGHAND,
+		initial_values: [ "none" ],
+		other_values: [
+			// SVG reference filters
+			"url(#my-filter)",
+			"url(#my-filter-1) url(#my-filter-2)",
+
+			// Filter functions
+			"opacity(50%) saturate(1.0)",
+			"invert(50%) sepia(0.1) brightness(90%)",
+
+			// Mixed SVG reference filters and filter functions
+			"grayscale(1) url(#my-filter-1)",
+			"url(#my-filter-1) brightness(50%) contrast(0.9)",
+
+			"blur(0)",
+			"blur(0px)",
+			"blur(0.5px)",
+			"blur(3px)",
+			"blur(100px)",
+			"blur(0.1em)",
+			"blur(calc(-1px))", // Parses and becomes blur(0px).
+			"blur(calc(0px))",
+			"blur(calc(5px))",
+			"blur(calc(2 * 5px))",
+
+			"brightness(0)",
+			"brightness(50%)",
+			"brightness(1)",
+			"brightness(1.0)",
+			"brightness(2)",
+			"brightness(350%)",
+			"brightness(4.567)",
+
+			"contrast(0)",
+			"contrast(50%)",
+			"contrast(1)",
+			"contrast(1.0)",
+			"contrast(2)",
+			"contrast(350%)",
+			"contrast(4.567)",
+
+			"grayscale(0)",
+			"grayscale(50%)",
+			"grayscale(1)",
+			"grayscale(1.0)",
+			"grayscale(2)",
+			"grayscale(350%)",
+			"grayscale(4.567)",
+
+			"invert(0)",
+			"invert(50%)",
+			"invert(1)",
+			"invert(1.0)",
+			"invert(2)",
+			"invert(350%)",
+			"invert(4.567)",
+
+			"opacity(0)",
+			"opacity(50%)",
+			"opacity(1)",
+			"opacity(1.0)",
+			"opacity(2)",
+			"opacity(350%)",
+			"opacity(4.567)",
+
+			"saturate(0)",
+			"saturate(50%)",
+			"saturate(1)",
+			"saturate(1.0)",
+			"saturate(2)",
+			"saturate(350%)",
+			"saturate(4.567)",
+
+			"sepia(0)",
+			"sepia(50%)",
+			"sepia(1)",
+			"sepia(1.0)",
+			"sepia(2)",
+			"sepia(350%)",
+			"sepia(4.567)",
+		],
+		invalid_values: [
+			// none
+			"none none",
+			"url(#my-filter) none",
+			"none url(#my-filter)",
+			"blur(2px) none url(#my-filter)",
+
+			// Nested filters
+			"grayscale(invert(1.0))",
+
+			// Comma delimited filters
+			"url(#my-filter),",
+			"invert(50%), url(#my-filter), brightness(90%)",
+
+			// Test the following situations for each filter function:
+			// - Invalid number of arguments
+			// - Comma delimited arguments
+			// - Wrong argument type
+			// - Argument value out of range
+			"blur()",
+			"blur(3px 5px)",
+			"blur(3px,)",
+			"blur(3px, 5px)",
+			"blur(#my-filter)",
+			"blur(0.5)",
+			"blur(50%)",
+			"blur(calc(0))", // Unitless zero in calc is not a valid length.
+			"blur(calc(0.1))",
+			"blur(calc(10%))",
+			"blur(calc(20px - 5%))",
+			"blur(-3px)",
+
+			"brightness()",
+			"brightness(0.5 0.5)",
+			"brightness(0.5,)",
+			"brightness(0.5, 0.5)",
+			"brightness(#my-filter)",
+			"brightness(10px)",
+			"brightness(-1)",
+
+			"contrast()",
+			"contrast(0.5 0.5)",
+			"contrast(0.5,)",
+			"contrast(0.5, 0.5)",
+			"contrast(#my-filter)",
+			"contrast(10px)",
+			"contrast(-1)",
+
+			"grayscale()",
+			"grayscale(0.5 0.5)",
+			"grayscale(0.5,)",
+			"grayscale(0.5, 0.5)",
+			"grayscale(#my-filter)",
+			"grayscale(10px)",
+			"grayscale(-1)",
+
+			"invert()",
+			"invert(0.5 0.5)",
+			"invert(0.5,)",
+			"invert(0.5, 0.5)",
+			"invert(#my-filter)",
+			"invert(10px)",
+			"invert(-1)",
+
+			"opacity()",
+			"opacity(0.5 0.5)",
+			"opacity(0.5,)",
+			"opacity(0.5, 0.5)",
+			"opacity(#my-filter)",
+			"opacity(10px)",
+			"opacity(-1)",
+
+			"saturate()",
+			"saturate(0.5 0.5)",
+			"saturate(0.5,)",
+			"saturate(0.5, 0.5)",
+			"saturate(#my-filter)",
+			"saturate(10px)",
+			"saturate(-1)",
+
+			"sepia()",
+			"sepia(0.5 0.5)",
+			"sepia(0.5,)",
+			"sepia(0.5, 0.5)",
+			"sepia(#my-filter)",
+			"sepia(10px)",
+			"sepia(-1)",
+		]
+	};
+}
--- a/layout/svg/nsSVGEffects.cpp
+++ b/layout/svg/nsSVGEffects.cpp
@@ -445,17 +445,17 @@ nsSVGEffects::GetPaintingPropertyForURI(
 nsSVGEffects::EffectProperties
 nsSVGEffects::GetEffectProperties(nsIFrame *aFrame)
 {
   NS_ASSERTION(!aFrame->GetPrevContinuation(), "aFrame should be first continuation");
 
   EffectProperties result;
   const nsStyleSVGReset *style = aFrame->StyleSVGReset();
   result.mFilter = static_cast<nsSVGFilterProperty*>
-    (GetEffectProperty(style->mFilter, aFrame, FilterProperty(),
+    (GetEffectProperty(style->SingleFilter(), aFrame, FilterProperty(),
                        CreateFilterProperty));
   result.mClipPath =
     GetPaintingProperty(style->mClipPath, aFrame, ClipPathProperty());
   result.mMask =
     GetPaintingProperty(style->mMask, aFrame, MaskProperty());
   return result;
 }
 
@@ -521,17 +521,17 @@ nsSVGEffects::UpdateEffects(nsIFrame *aF
   props.Delete(MarkerMiddleProperty());
   props.Delete(MarkerEndProperty());
   props.Delete(FillProperty());
   props.Delete(StrokeProperty());
   props.Delete(BackgroundImageProperty());
 
   // Ensure that the filter is repainted correctly
   // We can't do that in DoUpdate as the referenced frame may not be valid
-  GetEffectProperty(aFrame->StyleSVGReset()->mFilter,
+  GetEffectProperty(aFrame->StyleSVGReset()->SingleFilter(),
                     aFrame, FilterProperty(), CreateFilterProperty);
 
   if (aFrame->GetType() == nsGkAtoms::svgPathGeometryFrame &&
       static_cast<nsSVGPathGeometryElement*>(aFrame->GetContent())->IsMarkable()) {
     // Set marker properties here to avoid reference loops
     const nsStyleSVG *style = aFrame->StyleSVG();
     GetEffectProperty(style->mMarkerStart, aFrame, MarkerBeginProperty(),
                       CreateMarkerProperty);
@@ -542,17 +542,17 @@ nsSVGEffects::UpdateEffects(nsIFrame *aF
   }
 }
 
 nsSVGFilterProperty *
 nsSVGEffects::GetFilterProperty(nsIFrame *aFrame)
 {
   NS_ASSERTION(!aFrame->GetPrevContinuation(), "aFrame should be first continuation");
 
-  if (!aFrame->StyleSVGReset()->mFilter)
+  if (!aFrame->StyleSVGReset()->SingleFilter())
     return nullptr;
 
   return static_cast<nsSVGFilterProperty *>
     (aFrame->Properties().Get(FilterProperty()));
 }
 
 static PLDHashOperator
 GatherEnumerator(nsPtrHashKey<nsSVGRenderingObserver>* aEntry, void* aArg)
--- a/layout/svg/nsSVGIntegrationUtils.cpp
+++ b/layout/svg/nsSVGIntegrationUtils.cpp
@@ -145,17 +145,17 @@ GetPreEffectsVisualOverflowUnion(nsIFram
 bool
 nsSVGIntegrationUtils::UsingEffectsForFrame(const nsIFrame* aFrame)
 {
   // Even when SVG display lists are disabled, returning true for SVG frames
   // does not adversely affect any of our callers. Therefore we don't bother
   // checking the SDL prefs here, since we don't know if we're being called for
   // painting or hit-testing anyway.
   const nsStyleSVGReset *style = aFrame->StyleSVGReset();
-  return (style->mFilter || style->mClipPath || style->mMask);
+  return (style->SingleFilter() || style->mClipPath || style->mMask);
 }
 
 /* static */ nsPoint
 nsSVGIntegrationUtils::GetOffsetToUserSpace(nsIFrame* aFrame)
 {
   if ((aFrame->GetStateBits() & NS_FRAME_SVG_LAYOUT)) {
     // Do NOT call GetAllInFlowRectsUnion for SVG - it will get the
     // covered region relative to the nsSVGOuterSVGFrame, which is absolutely
--- a/layout/svg/nsSVGUtils.cpp
+++ b/layout/svg/nsSVGUtils.cpp
@@ -1264,17 +1264,17 @@ nsSVGUtils::CanOptimizeOpacity(nsIFrame 
   if (!(aFrame->GetStateBits() & NS_FRAME_SVG_LAYOUT)) {
     return false;
   }
   nsIAtom *type = aFrame->GetType();
   if (type != nsGkAtoms::svgImageFrame &&
       type != nsGkAtoms::svgPathGeometryFrame) {
     return false;
   }
-  if (aFrame->StyleSVGReset()->mFilter) {
+  if (aFrame->StyleSVGReset()->SingleFilter()) {
     return false;
   }
   // XXX The SVG WG is intending to allow fill, stroke and markers on <image>
   if (type == nsGkAtoms::svgImageFrame) {
     return true;
   }
   const nsStyleSVG *style = aFrame->StyleSVG();
   if (style->HasMarker()) {
--- a/modules/libpref/src/init/all.js
+++ b/modules/libpref/src/init/all.js
@@ -1769,16 +1769,19 @@ pref("layout.css.devPixelsPerPx", "-1.0"
 pref("layout.css.masking.enabled", false);
 #else
 pref("layout.css.masking.enabled", true);
 #endif
 
 // Is support for the the @supports rule enabled?
 pref("layout.css.supports-rule.enabled", true);
 
+// Is support for CSS Filters enabled?
+pref("layout.css.filters.enabled", false);
+
 // Is support for CSS Flexbox enabled?
 pref("layout.css.flexbox.enabled", true);
 
 // Is support for CSS3 Fonts features enabled?
 // (includes font-variant-*, font-kerning, font-synthesis
 // and the @font-feature-values rule)
 // Note: with this enabled, font-feature-settings is aliased
 // to -moz-font-feature-settings.  When unprefixing, this should