Bug 948265 - Pass an nsStyleFilter chain instead of an nsSVGFilterFrame into nsSVGFilterInstance. r=roc
authorMax Vujovic <mvujovic@adobe.com>
Mon, 24 Feb 2014 10:22:58 -0500
changeset 170550 7913b3e61acb873a51669b01fb86f1d8dc8ce709
parent 170549 c9de8e0ce7450bc27797523a96022a5b9d84b8ea
child 170551 926a5f6d263c5eff796dec916ed65a1ba6e17a34
push id270
push userpvanderbeken@mozilla.com
push dateThu, 06 Mar 2014 09:24:21 +0000
reviewersroc
bugs948265
milestone30.0a1
Bug 948265 - Pass an nsStyleFilter chain instead of an nsSVGFilterFrame into nsSVGFilterInstance. r=roc
layout/base/nsDisplayList.cpp
layout/svg/nsSVGEffects.cpp
layout/svg/nsSVGEffects.h
layout/svg/nsSVGFilterInstance.cpp
layout/svg/nsSVGFilterInstance.h
layout/svg/nsSVGIntegrationUtils.cpp
layout/svg/nsSVGUtils.cpp
--- a/layout/base/nsDisplayList.cpp
+++ b/layout/base/nsDisplayList.cpp
@@ -4888,27 +4888,26 @@ nsDisplaySVGEffects::BuildLayer(nsDispla
   if (opacity == 0.0f)
     return nullptr;
 
   nsIFrame* firstFrame =
     nsLayoutUtils::FirstContinuationOrIBSplitSibling(mFrame);
   nsSVGEffects::EffectProperties effectProperties =
     nsSVGEffects::GetEffectProperties(firstFrame);
 
-  bool isOK = true;
+  bool isOK = effectProperties.HasNoFilterOrHasValidFilter();
   effectProperties.GetClipPathFrame(&isOK);
   effectProperties.GetMaskFrame(&isOK);
-  bool hasFilter = effectProperties.GetFilterFrame(&isOK) != nullptr;
 
   if (!isOK) {
     return nullptr;
   }
 
   ContainerLayerParameters newContainerParameters = aContainerParameters;
-  if (hasFilter) {
+  if (effectProperties.HasValidFilter()) {
     newContainerParameters.mDisableSubpixelAntialiasingInDescendants = true;
   }
 
   nsRefPtr<ContainerLayer> container = aManager->GetLayerBuilder()->
     BuildContainerLayerFor(aBuilder, aManager, mFrame, this, mList,
                            newContainerParameters, nullptr);
 
   return container.forget();
@@ -4967,17 +4966,17 @@ nsDisplaySVGEffects::PrintEffects(nsACSt
   }
   if (clipPathFrame) {
     if (!first) {
       aTo += ", ";
     }
     aTo += nsPrintfCString("clip(%s)", clipPathFrame->IsTrivial() ? "trivial" : "non-trivial");
     first = false;
   }
-  if (effectProperties.GetFilterFrame(&isOK)) {
+  if (effectProperties.HasValidFilter()) {
     if (!first) {
       aTo += ", ";
     }
     aTo += "filter";
     first = false;
   }
   if (effectProperties.GetMaskFrame(&isOK)) {
     if (!first) {
--- a/layout/svg/nsSVGEffects.cpp
+++ b/layout/svg/nsSVGEffects.cpp
@@ -261,24 +261,16 @@ nsSVGFilterProperty::IsInObserverLists()
 void
 nsSVGFilterProperty::Invalidate()
 {
   for (uint32_t i = 0; i < mReferences.Length(); i++) {
     mReferences[i]->Invalidate();
   }
 }
 
-nsSVGFilterFrame *
-nsSVGFilterProperty::GetFilterFrame()
-{
-  // Eventually, callers will ask nsSVGFilterProperty for an nsStyleFilter
-  // chain, not a single nsSVGFilterFrame, and this function will go away.
-  return mReferences.Length() > 0 ? mReferences[0]->GetFilterFrame() : nullptr;
-}
-
 NS_IMPL_ISUPPORTS_INHERITED1(nsSVGFilterReference,
                              nsSVGIDRenderingObserver,
                              nsISVGFilterReference);
 
 nsSVGFilterFrame *
 nsSVGFilterReference::GetFilterFrame()
 {
   return static_cast<nsSVGFilterFrame *>
--- a/layout/svg/nsSVGEffects.h
+++ b/layout/svg/nsSVGEffects.h
@@ -202,21 +202,16 @@ public:
                       nsIFrame *aFilteredFrame);
   virtual ~nsSVGFilterProperty();
 
   const nsTArray<nsStyleFilter>& GetFilters() { return mFilters; }
   bool ReferencesValidResources();
   bool IsInObserverLists() const;
   void Invalidate();
 
-  /**
-   * @return the filter frame, or null if there is no filter frame
-   */
-  nsSVGFilterFrame *GetFilterFrame();
-
   // nsISupports
   NS_DECL_ISUPPORTS
 
 private:
   nsTArray<nsSVGFilterReference*> mReferences;
   nsTArray<nsStyleFilter> mFilters;
 };
 
@@ -369,30 +364,23 @@ public:
     nsSVGClipPathFrame *GetClipPathFrame(bool *aOK);
     /**
      * @return the mask frame, or null if there is no mask frame
      * @param aOK if a mask was specified and the designated element
      * exists but is an element of the wrong type, *aOK is set to false.
      * Otherwise *aOK is untouched.
      */
     nsSVGMaskFrame *GetMaskFrame(bool *aOK);
-    /**
-     * @return the filter frame, or null if there is no filter frame
-     * @param aOK if a filter was specified but the designated element
-     * does not exist or is an element of the wrong type, *aOK is set
-     * to false. Otherwise *aOK is untouched.
-     */
-    nsSVGFilterFrame *GetFilterFrame(bool *aOK) {
-      if (!mFilter)
-        return nullptr;
-      nsSVGFilterFrame *filter = mFilter->GetFilterFrame();
-      if (!filter) {
-        *aOK = false;
-      }
-      return filter;
+
+    bool HasValidFilter() {
+      return mFilter && mFilter->ReferencesValidResources();
+    }
+
+    bool HasNoFilterOrHasValidFilter() {
+      return !mFilter || mFilter->ReferencesValidResources();
     }
   };
 
   /**
    * @param aFrame should be the first continuation
    */
   static EffectProperties GetEffectProperties(nsIFrame *aFrame);
 
@@ -410,20 +398,16 @@ public:
    * XXX rename to something more meaningful like RefreshResourceReferences?
    */
   static void UpdateEffects(nsIFrame *aFrame);
 
   /**
    * @param aFrame should be the first continuation
    */
   static nsSVGFilterProperty *GetFilterProperty(nsIFrame *aFrame);
-  static nsSVGFilterFrame *GetFilterFrame(nsIFrame *aFrame) {
-    nsSVGFilterProperty *prop = GetFilterProperty(aFrame);
-    return prop ? prop->GetFilterFrame() : nullptr;
-  }
 
   /**
    * @param aFrame must be a first-continuation.
    */
   static void AddRenderingObserver(Element *aElement, nsSVGRenderingObserver *aObserver);
   /**
    * @param aFrame must be a first-continuation.
    */
--- a/layout/svg/nsSVGFilterInstance.cpp
+++ b/layout/svg/nsSVGFilterInstance.cpp
@@ -7,130 +7,137 @@
 #include "nsSVGFilterInstance.h"
 
 // Keep others in (case-insensitive) order:
 #include "gfxPlatform.h"
 #include "gfxUtils.h"
 #include "nsISVGChildFrame.h"
 #include "nsRenderingContext.h"
 #include "mozilla/dom/SVGFilterElement.h"
+#include "nsReferencedElement.h"
 #include "nsSVGFilterFrame.h"
 #include "nsSVGFilterPaintCallback.h"
 #include "nsSVGUtils.h"
 #include "SVGContentUtils.h"
 #include "FilterSupport.h"
 #include "gfx2DGlue.h"
 
 using namespace mozilla;
 using namespace mozilla::dom;
 using namespace mozilla::gfx;
 
 nsresult
-nsSVGFilterInstance::PaintFilteredFrame(nsSVGFilterFrame* aFilterFrame,
-                                        nsRenderingContext *aContext,
+nsSVGFilterInstance::PaintFilteredFrame(nsRenderingContext *aContext,
                                         nsIFrame *aFilteredFrame,
                                         nsSVGFilterPaintCallback *aPaintCallback,
                                         const nsRect *aDirtyArea,
                                         nsIFrame* aTransformRoot)
 {
-  nsSVGFilterInstance instance(aFilteredFrame, aFilterFrame, aPaintCallback,
-                               aDirtyArea, nullptr, nullptr, nullptr,
+  nsSVGFilterInstance instance(aFilteredFrame, aPaintCallback, aDirtyArea,
+                               nullptr, nullptr, nullptr,
                                aTransformRoot);
   if (!instance.IsInitialized()) {
     return NS_OK;
   }
   return instance.Render(aContext->ThebesContext());
 }
 
 nsRect
-nsSVGFilterInstance::GetPostFilterDirtyArea(nsSVGFilterFrame* aFilterFrame,
-                                            nsIFrame *aFilteredFrame,
+nsSVGFilterInstance::GetPostFilterDirtyArea(nsIFrame *aFilteredFrame,
                                             const nsRect& aPreFilterDirtyRect)
 {
   if (aPreFilterDirtyRect.IsEmpty()) {
     return nsRect();
   }
 
-  nsSVGFilterInstance instance(aFilteredFrame, aFilterFrame, nullptr, nullptr,
+  nsSVGFilterInstance instance(aFilteredFrame, nullptr, nullptr,
                                &aPreFilterDirtyRect);
   if (!instance.IsInitialized()) {
     return nsRect();
   }
   // We've passed in the source's dirty area so the instance knows about it.
   // Now we can ask the instance to compute the area of the filter output
   // that's dirty.
   nsRect dirtyRect;
   nsresult rv = instance.ComputePostFilterDirtyRect(&dirtyRect);
   if (NS_SUCCEEDED(rv)) {
     return dirtyRect;
   }
   return nsRect();
 }
 
 nsRect
-nsSVGFilterInstance::GetPreFilterNeededArea(nsSVGFilterFrame* aFilterFrame,
-                                            nsIFrame *aFilteredFrame,
+nsSVGFilterInstance::GetPreFilterNeededArea(nsIFrame *aFilteredFrame,
                                             const nsRect& aPostFilterDirtyRect)
 {
-  nsSVGFilterInstance instance(aFilteredFrame, aFilterFrame, nullptr,
-                               &aPostFilterDirtyRect);
+  nsSVGFilterInstance instance(aFilteredFrame, nullptr, &aPostFilterDirtyRect);
   if (!instance.IsInitialized()) {
     return nsRect();
   }
   // Now we can ask the instance to compute the area of the source
   // that's needed.
   nsRect neededRect;
   nsresult rv = instance.ComputeSourceNeededRect(&neededRect);
   if (NS_SUCCEEDED(rv)) {
     return neededRect;
   }
   return nsRect();
 }
 
 nsRect
-nsSVGFilterInstance::GetPostFilterBounds(nsSVGFilterFrame* aFilterFrame,
-                                         nsIFrame *aFilteredFrame,
+nsSVGFilterInstance::GetPostFilterBounds(nsIFrame *aFilteredFrame,
                                          const gfxRect *aOverrideBBox,
                                          const nsRect *aPreFilterBounds)
 {
   MOZ_ASSERT(!(aFilteredFrame->GetStateBits() & NS_FRAME_SVG_LAYOUT) ||
              !(aFilteredFrame->GetStateBits() & NS_FRAME_IS_NONDISPLAY),
              "Non-display SVG do not maintain visual overflow rects");
 
-  nsSVGFilterInstance instance(aFilteredFrame, aFilterFrame, nullptr, nullptr,
+  nsSVGFilterInstance instance(aFilteredFrame, nullptr, nullptr,
                                aPreFilterBounds, aPreFilterBounds,
                                aOverrideBBox);
   if (!instance.IsInitialized()) {
     return nsRect();
   }
   nsRect bbox;
   nsresult rv = instance.ComputePostFilterExtents(&bbox);
   if (NS_SUCCEEDED(rv)) {
     return bbox;
   }
   return nsRect();
 }
 
 nsSVGFilterInstance::nsSVGFilterInstance(nsIFrame *aTargetFrame,
-                                         nsSVGFilterFrame *aFilterFrame,
                                          nsSVGFilterPaintCallback *aPaintCallback,
                                          const nsRect *aPostFilterDirtyRect,
                                          const nsRect *aPreFilterDirtyRect,
                                          const nsRect *aPreFilterVisualOverflowRectOverride,
                                          const gfxRect *aOverrideBBox,
                                          nsIFrame* aTransformRoot) :
   mTargetFrame(aTargetFrame),
+  mFilters(aTargetFrame->StyleSVGReset()->mFilters),
   mPaintCallback(aPaintCallback),
   mTransformRoot(aTransformRoot),
   mInitialized(false) {
 
-  mFilterElement =  aFilterFrame->GetFilterContent();
+  // Get the filter frame.
+  mFilterFrame = GetFilterFrame();
+  if (!mFilterFrame) {
+    return;
+  }
+
+  // Get the filter element.
+  mFilterElement = mFilterFrame->GetFilterContent();
+  if (!mFilterElement) {
+    NS_NOTREACHED("filter frame should have a related element");
+    return;
+  }
 
   mPrimitiveUnits =
-    aFilterFrame->GetEnumValue(SVGFilterElement::PRIMITIVEUNITS);
+    mFilterFrame->GetEnumValue(SVGFilterElement::PRIMITIVEUNITS);
 
   mTargetBBox = aOverrideBBox ?
     *aOverrideBBox : nsSVGUtils::GetBBox(mTargetFrame);
 
   // Get the filter region (in the filtered element's user space):
 
   // XXX if filterUnits is set (or has defaulted) to objectBoundingBox, we
   // should send a warning to the error console if the author has used lengths
@@ -142,22 +149,22 @@ nsSVGFilterInstance::nsSVGFilterInstance
   // interpreted as a fraction of the bounding box and sometimes as user-space
   // units). So really only percentage values should be used in this case.
   
   nsSVGLength2 XYWH[4];
   NS_ABORT_IF_FALSE(sizeof(mFilterElement->mLengthAttributes) == sizeof(XYWH),
                     "XYWH size incorrect");
   memcpy(XYWH, mFilterElement->mLengthAttributes, 
     sizeof(mFilterElement->mLengthAttributes));
-  XYWH[0] = *aFilterFrame->GetLengthValue(SVGFilterElement::ATTR_X);
-  XYWH[1] = *aFilterFrame->GetLengthValue(SVGFilterElement::ATTR_Y);
-  XYWH[2] = *aFilterFrame->GetLengthValue(SVGFilterElement::ATTR_WIDTH);
-  XYWH[3] = *aFilterFrame->GetLengthValue(SVGFilterElement::ATTR_HEIGHT);
+  XYWH[0] = *mFilterFrame->GetLengthValue(SVGFilterElement::ATTR_X);
+  XYWH[1] = *mFilterFrame->GetLengthValue(SVGFilterElement::ATTR_Y);
+  XYWH[2] = *mFilterFrame->GetLengthValue(SVGFilterElement::ATTR_WIDTH);
+  XYWH[3] = *mFilterFrame->GetLengthValue(SVGFilterElement::ATTR_HEIGHT);
   uint16_t filterUnits =
-    aFilterFrame->GetEnumValue(SVGFilterElement::FILTERUNITS);
+    mFilterFrame->GetEnumValue(SVGFilterElement::FILTERUNITS);
   // The filter region in user space, in user units:
   mFilterRegion = nsSVGUtils::GetRelativeRect(filterUnits,
     XYWH, mTargetBBox, mTargetFrame);
 
   if (mFilterRegion.Width() <= 0 || mFilterRegion.Height() <= 0) {
     // 0 disables rendering, < 0 is error. dispatch error console warning
     // or error as appropriate.
     return;
@@ -166,17 +173,17 @@ nsSVGFilterInstance::nsSVGFilterInstance
   // Calculate filterRes (the width and height of the pixel buffer of the
   // temporary offscreen surface that we would/will create to paint into when
   // painting the entire filtered element) and, if necessary, adjust
   // mFilterRegion out slightly so that it aligns with pixel boundaries of this
   // buffer:
 
   gfxIntSize filterRes;
   const nsSVGIntegerPair* filterResAttrs =
-    aFilterFrame->GetIntegerPairValue(SVGFilterElement::FILTERRES);
+    mFilterFrame->GetIntegerPairValue(SVGFilterElement::FILTERRES);
   if (filterResAttrs->IsExplicitlySet()) {
     int32_t filterResX = filterResAttrs->GetAnimValue(nsSVGIntegerPair::eFirst);
     int32_t filterResY = filterResAttrs->GetAnimValue(nsSVGIntegerPair::eSecond);
     if (filterResX <= 0 || filterResY <= 0) {
       // 0 disables rendering, < 0 is error. dispatch error console warning?
       return;
     }
 
@@ -245,16 +252,67 @@ nsSVGFilterInstance::nsSVGFilterInstance
   } else {
     nsRect preFilterVOR = mTargetFrame->GetPreEffectsVisualOverflowRect();
     mTargetBounds = FrameSpaceToFilterSpace(&preFilterVOR);
   }
 
   mInitialized = true;
 }
 
+nsSVGFilterFrame*
+nsSVGFilterInstance::GetFilterFrame()
+{
+  if (mFilters.Length() <= 0) {
+    // There are no filters.
+    return nullptr;
+  }
+
+  // For now, use the first filter in the chain. Later, nsSVGFilterInstance will
+  // receive the exact filter that it should use, instead of choosing one from a
+  // chain of filters.
+  const nsStyleFilter& filter = mFilters[0];
+  if (filter.GetType() != NS_STYLE_FILTER_URL) {
+    // The filter is not an SVG reference filter.
+    return nullptr;
+  }
+
+  nsIURI* url = filter.GetURL();
+  if (!url) {
+    NS_NOTREACHED("an nsStyleFilter of type URL should have a non-null URL");
+    return nullptr;
+  }
+
+  // Get the target element to use as a point of reference for looking up the
+  // filter element.
+  nsIContent* targetElement = mTargetFrame->GetContent();
+  if (!targetElement) {
+    // There is no element associated with the target frame.
+    return nullptr;
+  }
+
+  // Look up the filter element by URL.
+  nsReferencedElement filterElement;
+  bool watch = false;
+  filterElement.Reset(targetElement, url, watch);
+  Element* element = filterElement.get();
+  if (!element) {
+    // The URL points to no element.
+    return nullptr;
+  }
+
+  // Get the frame of the filter element.
+  nsIFrame* frame = element->GetPrimaryFrame();
+  if (frame->GetType() != nsGkAtoms::svgFilterFrame) {
+    // The URL points to an element that's not an SVG filter element.
+    return nullptr;
+  }
+
+  return static_cast<nsSVGFilterFrame*>(frame);
+}
+
 float
 nsSVGFilterInstance::GetPrimitiveNumber(uint8_t aCtxType, float aValue) const
 {
   nsSVGLength2 val;
   val.Init(aCtxType, 0xff, aValue,
            nsIDOMSVGLength::SVG_LENGTHTYPE_NUMBER);
 
   float value;
--- a/layout/svg/nsSVGFilterInstance.h
+++ b/layout/svg/nsSVGFilterInstance.h
@@ -60,75 +60,70 @@ class nsSVGFilterInstance
 
 public:
   /**
    * 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
    *   border box).
    */
-  static nsresult PaintFilteredFrame(nsSVGFilterFrame* aFilterFrame,
-                                     nsRenderingContext *aContext,
+  static nsresult PaintFilteredFrame(nsRenderingContext *aContext,
                                      nsIFrame *aFilteredFrame,
                                      nsSVGFilterPaintCallback *aPaintCallback,
                                      const nsRect* aDirtyArea,
                                      nsIFrame* aTransformRoot = nullptr);
 
   /**
    * Returns the post-filter area that could be dirtied when the given
    * pre-filter area of aFilteredFrame changes.
    * @param aPreFilterDirtyRect The pre-filter area of aFilteredFrame that has
    *   changed, relative to aFilteredFrame, in app units.
    */
-  static nsRect GetPostFilterDirtyArea(nsSVGFilterFrame* aFilterFrame,
-                                       nsIFrame *aFilteredFrame,
+  static nsRect GetPostFilterDirtyArea(nsIFrame *aFilteredFrame,
                                        const nsRect& aPreFilterDirtyRect);
 
   /**
    * Returns the pre-filter area that is needed from aFilteredFrame when the
    * given post-filter area needs to be repainted.
    * @param aPostFilterDirtyRect The post-filter area that is dirty, relative
    *   to aFilteredFrame, in app units.
    */
-  static nsRect GetPreFilterNeededArea(nsSVGFilterFrame* aFilterFrame,
-                                       nsIFrame *aFilteredFrame,
+  static nsRect GetPreFilterNeededArea(nsIFrame *aFilteredFrame,
                                        const nsRect& aPostFilterDirtyRect);
 
   /**
    * Returns the post-filter visual overflow rect (paint bounds) of
    * aFilteredFrame.
    * @param aOverrideBBox A user space rect, in user units, that should be used
    *   as aFilteredFrame's bbox ('bbox' is a specific SVG term), if non-null.
    * @param aPreFilterBounds The pre-filter visual overflow rect of
    *   aFilteredFrame, if non-null.
    */
-  static nsRect GetPostFilterBounds(nsSVGFilterFrame* aFilterFrame,
-                                    nsIFrame *aFilteredFrame,
+  static nsRect GetPostFilterBounds(nsIFrame *aFilteredFrame,
                                     const gfxRect *aOverrideBBox = nullptr,
                                     const nsRect *aPreFilterBounds = nullptr);
 
   /**
    * @param aTargetFrame The frame of the filtered element under consideration.
-   * @param aFilterFrame The frame of the SVG filter element.
+   * @param aFilters The CSS and SVG filter chain from the style system.
    * @param aPaintCallback [optional] The callback that Render() should use to
    *   paint. Only required if you will call Render().
    * @param aPostFilterDirtyRect [optional] The bounds of the post-filter area
    *   that has to be repainted, in filter space. Only required if you will
    *   call ComputeSourceNeededRect() or Render().
    * @param aPreFilterDirtyRect [optional] The bounds of the pre-filter area of
    *   the filtered element that changed, in filter space. Only required if you
    *   will call ComputePostFilterDirtyRect().
    * @param aOverridePreFilterVisualOverflowRect [optional] Use a different
    *   visual overflow rect for the target element.
    * @param aOverrideBBox [optional] Use a different SVG bbox for the target
    *   element.
    * @param aTransformRoot [optional] The transform root frame for painting.
    */
   nsSVGFilterInstance(nsIFrame *aTargetFrame,
-                      nsSVGFilterFrame *aFilterFrame,
                       nsSVGFilterPaintCallback *aPaintCallback,
                       const nsRect *aPostFilterDirtyRect = nullptr,
                       const nsRect *aPreFilterDirtyRect = nullptr,
                       const nsRect *aOverridePreFilterVisualOverflowRect = nullptr,
                       const gfxRect *aOverrideBBox = nullptr,
                       nsIFrame* aTransformRoot = nullptr);
 
   /**
@@ -269,16 +264,21 @@ private:
   /**
    * Creates the SourceSurface for the SourceGraphic graph node, paints its
    * contents, and assigns it to mSourceGraphic.mSourceSurface.
    */
   nsresult BuildSourceImage(gfxASurface* aTargetSurface,
                             DrawTarget* aTargetDT);
 
   /**
+   * Finds the filter frame associated with this SVG filter.
+   */
+  nsSVGFilterFrame* GetFilterFrame();
+
+  /**
    * Build the list of FilterPrimitiveDescriptions that describes the filter's
    * filter primitives and their connections. This populates
    * mPrimitiveDescriptions and mInputImages.
    */
   nsresult BuildPrimitives();
 
   /**
    * Computes the filter space bounds of the areas that we actually *need* from
@@ -324,24 +324,34 @@ private:
    */
   gfxMatrix GetUserSpaceToFrameSpaceInCSSPxTransform() const;
 
   /**
    * The frame for the element that is currently being filtered.
    */
   nsIFrame*               mTargetFrame;
 
+  /**
+   * The CSS and SVG filter chain from the style system.
+   */
+  const nsTArray<nsStyleFilter> mFilters;
+
   nsSVGFilterPaintCallback* mPaintCallback;
 
   /**
    * The filter element referenced by mTargetFrame's element.
    */
   const mozilla::dom::SVGFilterElement* mFilterElement;
 
   /**
+   * The frame for the SVG filter element.
+   */
+  nsSVGFilterFrame* mFilterFrame;
+
+  /**
    * The SVG bbox of the element that is being filtered, in user space.
    */
   gfxRect                 mTargetBBox;
 
   /**
    * The transform from filter space to outer-<svg> device space.
    */
   gfxMatrix               mFilterSpaceToDeviceSpaceTransform;
--- a/layout/svg/nsSVGIntegrationUtils.cpp
+++ b/layout/svg/nsSVGIntegrationUtils.cpp
@@ -10,17 +10,16 @@
 #include "gfxDrawable.h"
 #include "nsCSSAnonBoxes.h"
 #include "nsDisplayList.h"
 #include "nsLayoutUtils.h"
 #include "nsRenderingContext.h"
 #include "nsSVGClipPathFrame.h"
 #include "nsSVGEffects.h"
 #include "nsSVGElement.h"
-#include "nsSVGFilterFrame.h"
 #include "nsSVGFilterInstance.h"
 #include "nsSVGFilterPaintCallback.h"
 #include "nsSVGMaskFrame.h"
 #include "nsSVGPaintServerFrame.h"
 #include "nsSVGUtils.h"
 #include "FrameLayerBuilder.h"
 #include "BasicLayers.h"
 #include "mozilla/gfx/Point.h"
@@ -253,36 +252,35 @@ nsRect
 {
   NS_ASSERTION(!(aFrame->GetStateBits() & NS_FRAME_SVG_LAYOUT),
                  "Don't call this on SVG child frames");
 
   nsIFrame* firstFrame =
     nsLayoutUtils::FirstContinuationOrIBSplitSibling(aFrame);
   nsSVGEffects::EffectProperties effectProperties =
     nsSVGEffects::GetEffectProperties(firstFrame);
-  nsSVGFilterFrame *filterFrame = effectProperties.mFilter ?
-    effectProperties.mFilter->GetFilterFrame() : nullptr;
-  if (!filterFrame)
+  if (!effectProperties.HasValidFilter()) {
     return aPreEffectsOverflowRect;
+  }
 
   // Create an override bbox - see comment above:
   nsPoint firstFrameToUserSpace = GetOffsetToUserSpace(firstFrame);
   // overrideBBox is in "user space", in _CSS_ pixels:
   // XXX Why are we rounding out to pixel boundaries? We don't do that in
   // GetSVGBBoxForNonSVGFrame, and it doesn't appear to be necessary.
   gfxRect overrideBBox =
     nsLayoutUtils::RectToGfxRect(
       GetPreEffectsVisualOverflowUnion(firstFrame, aFrame,
                                        aPreEffectsOverflowRect,
                                        firstFrameToUserSpace),
       aFrame->PresContext()->AppUnitsPerCSSPixel());
   overrideBBox.RoundOut();
 
   nsRect overflowRect =
-    nsSVGFilterInstance::GetPostFilterBounds(filterFrame, firstFrame, &overrideBBox);
+    nsSVGFilterInstance::GetPostFilterBounds(firstFrame, &overrideBBox);
 
   // Return overflowRect relative to aFrame, rather than "user space":
   return overflowRect - (aFrame->GetOffsetTo(firstFrame) + firstFrameToUserSpace);
 }
 
 nsIntRect
 nsSVGIntegrationUtils::AdjustInvalidAreaForSVGEffects(nsIFrame* aFrame,
                                                       const nsPoint& aToReferenceFrame,
@@ -291,30 +289,24 @@ nsSVGIntegrationUtils::AdjustInvalidArea
   if (aInvalidRect.IsEmpty()) {
     return nsIntRect();
   }
 
   // Don't bother calling GetEffectProperties; the filter property should
   // already have been set up during reflow/ComputeFrameEffectsRect
   nsIFrame* firstFrame =
     nsLayoutUtils::FirstContinuationOrIBSplitSibling(aFrame);
-  nsSVGEffects::EffectProperties effectProperties =
-    nsSVGEffects::GetEffectProperties(firstFrame);
-  if (!effectProperties.mFilter)
-    return aInvalidRect;
-
   nsSVGFilterProperty *prop = nsSVGEffects::GetFilterProperty(firstFrame);
   if (!prop || !prop->IsInObserverLists()) {
     return aInvalidRect;
   }
 
   int32_t appUnitsPerDevPixel = aFrame->PresContext()->AppUnitsPerDevPixel();
 
-  nsSVGFilterFrame* filterFrame = prop->GetFilterFrame();
-  if (!filterFrame) {
+  if (!prop || !prop->ReferencesValidResources()) {
     // The frame is either not there or not currently available,
     // perhaps because we're in the middle of tearing stuff down.
     // Be conservative, return our visual overflow rect relative
     // to the reference frame.
     nsRect overflow = aFrame->GetVisualOverflowRect() + aToReferenceFrame;
     return overflow.ToOutsidePixels(appUnitsPerDevPixel);
   }
 
@@ -323,42 +315,42 @@ nsSVGIntegrationUtils::AdjustInvalidArea
     aFrame->GetOffsetTo(firstFrame) + GetOffsetToUserSpace(firstFrame);
   // The initial rect was relative to the reference frame, so we need to
   // remove that offset to get a rect relative to the current frame.
   toUserSpace -= aToReferenceFrame;
   nsRect preEffectsRect = aInvalidRect.ToAppUnits(appUnitsPerDevPixel) + toUserSpace;
 
   // Adjust the dirty area for effects, and shift it back to being relative to
   // the reference frame.
-  nsRect result = nsSVGFilterInstance::GetPostFilterDirtyArea(filterFrame,
-    firstFrame, preEffectsRect) - toUserSpace;
+  nsRect result = nsSVGFilterInstance::GetPostFilterDirtyArea(firstFrame,
+    preEffectsRect) - toUserSpace;
   // Return the result, in pixels relative to the reference frame.
   return result.ToOutsidePixels(appUnitsPerDevPixel);
 }
 
 nsRect
 nsSVGIntegrationUtils::GetRequiredSourceForInvalidArea(nsIFrame* aFrame,
                                                        const nsRect& aDirtyRect)
 {
   // Don't bother calling GetEffectProperties; the filter property should
   // already have been set up during reflow/ComputeFrameEffectsRect
   nsIFrame* firstFrame =
     nsLayoutUtils::FirstContinuationOrIBSplitSibling(aFrame);
-  nsSVGFilterFrame* filterFrame =
-    nsSVGEffects::GetFilterFrame(firstFrame);
-  if (!filterFrame)
+  nsSVGFilterProperty *prop = nsSVGEffects::GetFilterProperty(firstFrame);
+  if (!prop || !prop->ReferencesValidResources()) {
     return aDirtyRect;
+  }
   
   // Convert aDirtyRect into "user space" in app units:
   nsPoint toUserSpace =
     aFrame->GetOffsetTo(firstFrame) + GetOffsetToUserSpace(firstFrame);
   nsRect postEffectsRect = aDirtyRect + toUserSpace;
 
   // Return ther result, relative to aFrame, not in user space:
-  return nsSVGFilterInstance::GetPreFilterNeededArea(filterFrame, firstFrame,
+  return nsSVGFilterInstance::GetPreFilterNeededArea(firstFrame,
     postEffectsRect) - toUserSpace;
 }
 
 bool
 nsSVGIntegrationUtils::HitTestFrameForEffects(nsIFrame* aFrame, const nsPoint& aPt)
 {
   nsIFrame* firstFrame =
     nsLayoutUtils::FirstContinuationOrIBSplitSibling(aFrame);
@@ -451,19 +443,18 @@ nsSVGIntegrationUtils::PaintFramesWithEf
 
   /* Properties are added lazily and may have been removed by a restyle,
      so make sure all applicable ones are set again. */
   nsIFrame* firstFrame =
     nsLayoutUtils::FirstContinuationOrIBSplitSibling(aFrame);
   nsSVGEffects::EffectProperties effectProperties =
     nsSVGEffects::GetEffectProperties(firstFrame);
 
-  bool isOK = true;
+  bool isOK = effectProperties.HasNoFilterOrHasValidFilter();
   nsSVGClipPathFrame *clipPathFrame = effectProperties.GetClipPathFrame(&isOK);
-  nsSVGFilterFrame *filterFrame = effectProperties.GetFilterFrame(&isOK);
   nsSVGMaskFrame *maskFrame = effectProperties.GetMaskFrame(&isOK);
   if (!isOK) {
     return; // Some resource is missing. We shouldn't paint anything.
   }
 
   bool isTrivialClip = clipPathFrame ? clipPathFrame->IsTrivial() : true;
 
   gfxContext* gfx = aCtx->ThebesContext();
@@ -510,22 +501,21 @@ nsSVGIntegrationUtils::PaintFramesWithEf
    * we can just do normal painting and get it clipped appropriately.
    */
   if (clipPathFrame && isTrivialClip) {
     gfx->Save();
     clipPathFrame->ClipPaint(aCtx, aFrame, cssPxToDevPxMatrix);
   }
 
   /* Paint the child */
-  if (filterFrame) {
+  if (effectProperties.HasValidFilter()) {
     RegularFramePaintCallback callback(aBuilder, aLayerManager,
                                        offsetWithoutSVGGeomFramePos);
     nsRect dirtyRect = aDirtyRect - offset;
-    nsSVGFilterInstance::PaintFilteredFrame(filterFrame, aCtx, aFrame,
-                                            &callback, &dirtyRect);
+    nsSVGFilterInstance::PaintFilteredFrame(aCtx, aFrame, &callback, &dirtyRect);
   } else {
     gfx->SetMatrix(matrixAutoSaveRestore.Matrix());
     aLayerManager->EndTransaction(FrameLayerBuilder::DrawThebesLayer, aBuilder);
     aCtx->Translate(offsetWithoutSVGGeomFramePos);
   }
 
   if (clipPathFrame && isTrivialClip) {
     gfx->Restore();
--- a/layout/svg/nsSVGUtils.cpp
+++ b/layout/svg/nsSVGUtils.cpp
@@ -30,17 +30,16 @@
 #include "nsISVGChildFrame.h"
 #include "nsPresContext.h"
 #include "nsRenderingContext.h"
 #include "nsStyleCoord.h"
 #include "nsStyleStruct.h"
 #include "nsSVGClipPathFrame.h"
 #include "nsSVGContainerFrame.h"
 #include "nsSVGEffects.h"
-#include "nsSVGFilterFrame.h"
 #include "nsSVGFilterInstance.h"
 #include "nsSVGFilterPaintCallback.h"
 #include "nsSVGForeignObjectFrame.h"
 #include "gfxSVGGlyphs.h"
 #include "nsSVGInnerSVGFrame.h"
 #include "nsSVGIntegrationUtils.h"
 #include "nsSVGLength2.h"
 #include "nsSVGMaskFrame.h"
@@ -155,23 +154,22 @@ nsSVGUtils::GetNearestSVGViewport(nsIFra
 
 nsRect
 nsSVGUtils::GetPostFilterVisualOverflowRect(nsIFrame *aFrame,
                                             const nsRect &aPreFilterRect)
 {
   NS_ABORT_IF_FALSE(aFrame->GetStateBits() & NS_FRAME_SVG_LAYOUT,
                     "Called on invalid frame type");
 
-  nsSVGFilterFrame *filterFrame = nsSVGEffects::GetFilterFrame(aFrame);
-  if (!filterFrame) {
+  nsSVGFilterProperty *property = nsSVGEffects::GetFilterProperty(aFrame);
+  if (!property || !property->ReferencesValidResources()) {
     return aPreFilterRect;
   }
 
-  return nsSVGFilterInstance::GetPostFilterBounds(filterFrame, aFrame, nullptr,
-                                                  &aPreFilterRect);
+  return nsSVGFilterInstance::GetPostFilterBounds(aFrame, nullptr, &aPreFilterRect);
 }
 
 bool
 nsSVGUtils::OuterSVGIsCallingReflowSVG(nsIFrame *aFrame)
 {
   return GetOuterSVGFrame(aFrame)->IsCallingReflowSVG();
 }
 
@@ -494,18 +492,17 @@ nsSVGUtils::PaintFrameWithEffects(nsRend
   }
 
   /* Properties are added lazily and may have been removed by a restyle,
      so make sure all applicable ones are set again. */
 
   nsSVGEffects::EffectProperties effectProperties =
     nsSVGEffects::GetEffectProperties(aFrame);
 
-  bool isOK = true;
-  nsSVGFilterFrame *filterFrame = effectProperties.GetFilterFrame(&isOK);
+  bool isOK = effectProperties.HasNoFilterOrHasValidFilter();
 
   if (aDirtyRect &&
       !(aFrame->GetStateBits() & NS_FRAME_IS_NONDISPLAY)) {
     // Here we convert aFrame's paint bounds to outer-<svg> device space,
     // compare it to aDirtyRect, and return early if they don't intersect.
     // We don't do this optimization for nondisplay SVG since nondisplay
     // SVG doesn't maintain bounds/overflow rects.
     nsRect overflowRect = aFrame->GetVisualOverflowRectRelativeToSelf();
@@ -596,17 +593,17 @@ nsSVGUtils::PaintFrameWithEffects(nsRend
    * we can just do normal painting and get it clipped appropriately.
    */
   if (clipPathFrame && isTrivialClip) {
     gfx->Save();
     clipPathFrame->ClipPaint(aContext, aFrame, matrix);
   }
 
   /* Paint the child */
-  if (filterFrame) {
+  if (effectProperties.HasValidFilter()) {
     nsRect* dirtyRect = nullptr;
     nsRect tmpDirtyRect;
     if (aDirtyRect) {
       // aDirtyRect is in outer-<svg> device pixels, but the filter code needs
       // it in frame space.
       gfxMatrix userToDeviceSpace =
         GetUserToCanvasTM(aFrame, nsISVGChildFrame::FOR_OUTERSVG_TM);
       if (userToDeviceSpace.IsSingular()) {
@@ -619,19 +616,18 @@ nsSVGUtils::PaintFrameWithEffects(nsRend
                                       aDirtyRect->width, aDirtyRect->height));
       tmpDirtyRect =
         nsLayoutUtils::RoundGfxRectToAppRect(
           dirtyBounds, aFrame->PresContext()->AppUnitsPerCSSPixel()) -
         aFrame->GetPosition();
       dirtyRect = &tmpDirtyRect;
     }
     SVGPaintCallback paintCallback;
-    nsSVGFilterInstance::PaintFilteredFrame(filterFrame, aContext, aFrame,
-                                            &paintCallback, dirtyRect,
-                                            aTransformRoot);
+    nsSVGFilterInstance::PaintFilteredFrame(aContext, aFrame, &paintCallback,
+                                            dirtyRect, aTransformRoot);
   } else {
     svgChildFrame->PaintSVG(aContext, aDirtyRect, aTransformRoot);
   }
 
   if (clipPathFrame && isTrivialClip) {
     gfx->Restore();
   }