Bug 948265 - Split out rendering code from nsSVGFilterInstance into nsFilterInstance. r=roc
authorMax Vujovic <mvujovic@adobe.com>
Mon, 24 Feb 2014 10:22:58 -0500
changeset 170233 926a5f6d263c5eff796dec916ed65a1ba6e17a34
parent 170232 7913b3e61acb873a51669b01fb86f1d8dc8ce709
child 170234 d22ac63d67f134fbaf49686ecd5c6754c58078f9
push id26283
push userkwierso@gmail.com
push dateTue, 25 Feb 2014 01:45:31 +0000
treeherdermozilla-central@e3daaa4c73dd [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersroc
bugs948265
milestone30.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 948265 - Split out rendering code from nsSVGFilterInstance into nsFilterInstance. r=roc
layout/svg/moz.build
layout/svg/nsFilterInstance.cpp
layout/svg/nsFilterInstance.h
layout/svg/nsSVGFilterInstance.cpp
layout/svg/nsSVGFilterInstance.h
layout/svg/nsSVGIntegrationUtils.cpp
layout/svg/nsSVGUtils.cpp
--- a/layout/svg/moz.build
+++ b/layout/svg/moz.build
@@ -1,24 +1,26 @@
 # -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
 # vim: set filetype=python:
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 EXPORTS += [
+    'nsFilterInstance.h',
     'nsSVGEffects.h',
     'nsSVGFilterInstance.h',
     'nsSVGForeignObjectFrame.h',
     'nsSVGIntegrationUtils.h',
     'nsSVGUtils.h',
     'SVGImageContext.h',
 ]
 
 UNIFIED_SOURCES += [
+    'nsFilterInstance.cpp',
     'nsSVGAFrame.cpp',
     'nsSVGClipPathFrame.cpp',
     'nsSVGContainerFrame.cpp',
     'nsSVGEffects.cpp',
     'nsSVGFilterFrame.cpp',
     'nsSVGFilterInstance.cpp',
     'nsSVGForeignObjectFrame.cpp',
     'nsSVGGenericContainerFrame.cpp',
copy from layout/svg/nsSVGFilterInstance.cpp
copy to layout/svg/nsFilterInstance.cpp
--- a/layout/svg/nsSVGFilterInstance.cpp
+++ b/layout/svg/nsFilterInstance.cpp
@@ -1,235 +1,142 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 // Main header first:
-#include "nsSVGFilterInstance.h"
+#include "nsFilterInstance.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 "nsSVGFilterInstance.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(nsRenderingContext *aContext,
-                                        nsIFrame *aFilteredFrame,
-                                        nsSVGFilterPaintCallback *aPaintCallback,
-                                        const nsRect *aDirtyArea,
-                                        nsIFrame* aTransformRoot)
+nsFilterInstance::PaintFilteredFrame(nsRenderingContext *aContext,
+                                     nsIFrame *aFilteredFrame,
+                                     nsSVGFilterPaintCallback *aPaintCallback,
+                                     const nsRect *aDirtyArea,
+                                     nsIFrame* aTransformRoot)
 {
-  nsSVGFilterInstance instance(aFilteredFrame, aPaintCallback, aDirtyArea,
-                               nullptr, nullptr, nullptr,
-                               aTransformRoot);
+  nsFilterInstance instance(aFilteredFrame, aPaintCallback, aDirtyArea,
+                            nullptr, nullptr, nullptr,
+                            aTransformRoot);
   if (!instance.IsInitialized()) {
     return NS_OK;
   }
   return instance.Render(aContext->ThebesContext());
 }
 
 nsRect
-nsSVGFilterInstance::GetPostFilterDirtyArea(nsIFrame *aFilteredFrame,
-                                            const nsRect& aPreFilterDirtyRect)
+nsFilterInstance::GetPostFilterDirtyArea(nsIFrame *aFilteredFrame,
+                                         const nsRect& aPreFilterDirtyRect)
 {
   if (aPreFilterDirtyRect.IsEmpty()) {
     return nsRect();
   }
 
-  nsSVGFilterInstance instance(aFilteredFrame, nullptr, nullptr,
-                               &aPreFilterDirtyRect);
+  nsFilterInstance 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(nsIFrame *aFilteredFrame,
-                                            const nsRect& aPostFilterDirtyRect)
+nsFilterInstance::GetPreFilterNeededArea(nsIFrame *aFilteredFrame,
+                                         const nsRect& aPostFilterDirtyRect)
 {
-  nsSVGFilterInstance instance(aFilteredFrame, nullptr, &aPostFilterDirtyRect);
+  nsFilterInstance 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(nsIFrame *aFilteredFrame,
-                                         const gfxRect *aOverrideBBox,
-                                         const nsRect *aPreFilterBounds)
+nsFilterInstance::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, nullptr, nullptr,
-                               aPreFilterBounds, aPreFilterBounds,
-                               aOverrideBBox);
+  nsFilterInstance 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,
-                                         nsSVGFilterPaintCallback *aPaintCallback,
-                                         const nsRect *aPostFilterDirtyRect,
-                                         const nsRect *aPreFilterDirtyRect,
-                                         const nsRect *aPreFilterVisualOverflowRectOverride,
-                                         const gfxRect *aOverrideBBox,
-                                         nsIFrame* aTransformRoot) :
+nsFilterInstance::nsFilterInstance(nsIFrame *aTargetFrame,
+                                   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) {
 
-  // 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 =
-    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
-  // with units. This is a common mistake and can result in filterRes being
-  // *massive* below (because we ignore the units and interpret the number as
-  // a factor of the bbox width/height). We should also send a warning if the
-  // user uses a number without units (a future SVG spec should really
-  // deprecate that, since it's too confusing for a bare number to be sometimes
-  // 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] = *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 =
-    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.
+  nsresult rv = BuildPrimitives();
+  if (NS_FAILED(rv)) {
     return;
   }
 
-  // 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 =
-    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;
-    }
-
-    mFilterRegion.Scale(filterResX, filterResY);
-    mFilterRegion.RoundOut();
-    mFilterRegion.Scale(1.0 / filterResX, 1.0 / filterResY);
-    // We don't care if this overflows, because we can handle upscaling/
-    // downscaling to filterRes
-    bool overflow;
-    filterRes =
-      nsSVGUtils::ConvertToSurfaceSize(gfxSize(filterResX, filterResY),
-                                       &overflow);
-    // XXX we could send a warning to the error console if the author specified
-    // filterRes doesn't align well with our outer 'svg' device space.
-  } else {
-    // Match filterRes as closely as possible to the pixel density of the nearest
-    // outer 'svg' device space:
-    gfxMatrix canvasTM =
-      nsSVGUtils::GetCanvasTM(mTargetFrame, nsISVGChildFrame::FOR_OUTERSVG_TM);
-    if (canvasTM.IsSingular()) {
-      // nothing to draw
-      return;
-    }
-
-    gfxSize scale = canvasTM.ScaleFactors(true);
-    mFilterRegion.Scale(scale.width, scale.height);
-    mFilterRegion.RoundOut();
-    // We don't care if this overflows, because we can handle upscaling/
-    // downscaling to filterRes
-    bool overflow;
-    filterRes = nsSVGUtils::ConvertToSurfaceSize(mFilterRegion.Size(),
-                                                 &overflow);
-    mFilterRegion.Scale(1.0 / scale.width, 1.0 / scale.height);
+  if (mPrimitiveDescriptions.IsEmpty()) {
+    // Nothing should be rendered.
+    return;
   }
 
-  mFilterSpaceBounds.SetRect(nsIntPoint(0, 0), filterRes);
-
   // Get various transforms:
 
-  gfxMatrix filterToUserSpace(mFilterRegion.Width() / filterRes.width, 0.0f,
-                              0.0f, mFilterRegion.Height() / filterRes.height,
+  gfxMatrix filterToUserSpace(mFilterRegion.Width() / mFilterSpaceBounds.width, 0.0f,
+                              0.0f, mFilterRegion.Height() / mFilterSpaceBounds.height,
                               mFilterRegion.X(), mFilterRegion.Y());
 
   // Only used (so only set) when we paint:
   if (mPaintCallback) {
     mFilterSpaceToDeviceSpaceTransform = filterToUserSpace *
               nsSVGUtils::GetCanvasTM(mTargetFrame, nsISVGChildFrame::FOR_PAINTING);
   }
 
@@ -252,307 +159,76 @@ 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;
-  if (mPrimitiveUnits == SVG_UNIT_TYPE_OBJECTBOUNDINGBOX) {
-    value = nsSVGUtils::ObjectSpace(mTargetBBox, &val);
-  } else {
-    value = nsSVGUtils::UserSpace(mTargetFrame, &val);
-  }
-
-  switch (aCtxType) {
-  case SVGContentUtils::X:
-    return value * mFilterSpaceBounds.width / mFilterRegion.Width();
-  case SVGContentUtils::Y:
-    return value * mFilterSpaceBounds.height / mFilterRegion.Height();
-  case SVGContentUtils::XY:
-  default:
-    return value * SVGContentUtils::ComputeNormalizedHypotenuse(
-                     mFilterSpaceBounds.width / mFilterRegion.Width(),
-                     mFilterSpaceBounds.height / mFilterRegion.Height());
-  }
-}
-
-Point3D
-nsSVGFilterInstance::ConvertLocation(const Point3D& aPoint) const
-{
-  nsSVGLength2 val[4];
-  val[0].Init(SVGContentUtils::X, 0xff, aPoint.x,
-              nsIDOMSVGLength::SVG_LENGTHTYPE_NUMBER);
-  val[1].Init(SVGContentUtils::Y, 0xff, aPoint.y,
-              nsIDOMSVGLength::SVG_LENGTHTYPE_NUMBER);
-  // Dummy width/height values
-  val[2].Init(SVGContentUtils::X, 0xff, 0,
-              nsIDOMSVGLength::SVG_LENGTHTYPE_NUMBER);
-  val[3].Init(SVGContentUtils::Y, 0xff, 0,
-              nsIDOMSVGLength::SVG_LENGTHTYPE_NUMBER);
-
-  gfxRect feArea = nsSVGUtils::GetRelativeRect(mPrimitiveUnits,
-    val, mTargetBBox, mTargetFrame);
-  gfxRect r = UserSpaceToFilterSpace(feArea);
-  return Point3D(r.x, r.y, GetPrimitiveNumber(SVGContentUtils::XY, aPoint.z));
-}
-
 gfxRect
-nsSVGFilterInstance::UserSpaceToFilterSpace(const gfxRect& aRect) const
+nsFilterInstance::UserSpaceToFilterSpace(const gfxRect& aRect) const
 {
   gfxRect r = aRect - mFilterRegion.TopLeft();
   r.Scale(mFilterSpaceBounds.width / mFilterRegion.Width(),
           mFilterSpaceBounds.height / mFilterRegion.Height());
   return r;
 }
 
-gfxPoint
-nsSVGFilterInstance::FilterSpaceToUserSpace(const gfxPoint& aPt) const
-{
-  return gfxPoint(aPt.x * mFilterRegion.Width() / mFilterSpaceBounds.width + mFilterRegion.X(),
-                  aPt.y * mFilterRegion.Height() / mFilterSpaceBounds.height + mFilterRegion.Y());
-}
-
 gfxMatrix
-nsSVGFilterInstance::GetUserSpaceToFilterSpaceTransform() const
+nsFilterInstance::GetUserSpaceToFilterSpaceTransform() const
 {
   gfxFloat widthScale = mFilterSpaceBounds.width / mFilterRegion.Width();
   gfxFloat heightScale = mFilterSpaceBounds.height / mFilterRegion.Height();
   return gfxMatrix(widthScale, 0.0f,
                    0.0f, heightScale,
                    -mFilterRegion.X() * widthScale, -mFilterRegion.Y() * heightScale);
 }
 
-IntRect
-nsSVGFilterInstance::ComputeFilterPrimitiveSubregion(nsSVGFE* aFilterElement,
-                                                     const nsTArray<int32_t>& aInputIndices)
-{
-  nsSVGFE* fE = aFilterElement;
-
-  IntRect defaultFilterSubregion(0,0,0,0);
-  if (fE->SubregionIsUnionOfRegions()) {
-    for (uint32_t i = 0; i < aInputIndices.Length(); ++i) {
-      int32_t inputIndex = aInputIndices[i];
-      IntRect inputSubregion = inputIndex >= 0 ?
-        mPrimitiveDescriptions[inputIndex].PrimitiveSubregion() :
-        ToIntRect(mFilterSpaceBounds);
-
-      defaultFilterSubregion = defaultFilterSubregion.Union(inputSubregion);
-    }
-  } else {
-    defaultFilterSubregion = ToIntRect(mFilterSpaceBounds);
-  }
-
-  gfxRect feArea = nsSVGUtils::GetRelativeRect(mPrimitiveUnits,
-    &fE->mLengthAttributes[nsSVGFE::ATTR_X], mTargetBBox, mTargetFrame);
-  Rect region = ToRect(UserSpaceToFilterSpace(feArea));
-
-  if (!fE->mLengthAttributes[nsSVGFE::ATTR_X].IsExplicitlySet())
-    region.x = defaultFilterSubregion.X();
-  if (!fE->mLengthAttributes[nsSVGFE::ATTR_Y].IsExplicitlySet())
-    region.y = defaultFilterSubregion.Y();
-  if (!fE->mLengthAttributes[nsSVGFE::ATTR_WIDTH].IsExplicitlySet())
-    region.width = defaultFilterSubregion.Width();
-  if (!fE->mLengthAttributes[nsSVGFE::ATTR_HEIGHT].IsExplicitlySet())
-    region.height = defaultFilterSubregion.Height();
-
-  // We currently require filter primitive subregions to be pixel-aligned.
-  // Following the spec, any pixel partially in the region is included
-  // in the region.
-  region.RoundOut();
-
-  return RoundedToInt(region);
-}
-
-void
-nsSVGFilterInstance::GetInputsAreTainted(const nsTArray<int32_t>& aInputIndices,
-                                         nsTArray<bool>& aOutInputsAreTainted)
+nsresult
+nsFilterInstance::BuildPrimitives()
 {
-  for (uint32_t i = 0; i < aInputIndices.Length(); i++) {
-    int32_t inputIndex = aInputIndices[i];
-    if (inputIndex < 0) {
-      // SourceGraphic, SourceAlpha, FillPaint and StrokePaint are tainted.
-      aOutInputsAreTainted.AppendElement(true);
-    } else {
-      aOutInputsAreTainted.AppendElement(mPrimitiveDescriptions[inputIndex].IsTainted());
-    }
-  }
-}
-
-static nsresult
-GetSourceIndices(nsSVGFE* aFilterElement,
-                 int32_t aCurrentIndex,
-                 const nsDataHashtable<nsStringHashKey, int32_t>& aImageTable,
-                 nsTArray<int32_t>& aSourceIndices)
-{
-  nsAutoTArray<nsSVGStringInfo,2> sources;
-  aFilterElement->GetSourceImageNames(sources);
-
-  for (uint32_t j = 0; j < sources.Length(); j++) {
-    nsAutoString str;
-    sources[j].mString->GetAnimValue(str, sources[j].mElement);
+  NS_ASSERTION(!mPrimitiveDescriptions.Length(),
+               "expected to start building primitives from scratch");
 
-    int32_t sourceIndex = 0;
-    if (str.EqualsLiteral("SourceGraphic")) {
-      sourceIndex = FilterPrimitiveDescription::kPrimitiveIndexSourceGraphic;
-    } else if (str.EqualsLiteral("SourceAlpha")) {
-      sourceIndex = FilterPrimitiveDescription::kPrimitiveIndexSourceAlpha;
-    } else if (str.EqualsLiteral("FillPaint")) {
-      sourceIndex = FilterPrimitiveDescription::kPrimitiveIndexFillPaint;
-    } else if (str.EqualsLiteral("StrokePaint")) {
-      sourceIndex = FilterPrimitiveDescription::kPrimitiveIndexStrokePaint;
-    } else if (str.EqualsLiteral("BackgroundImage") ||
-               str.EqualsLiteral("BackgroundAlpha")) {
-      return NS_ERROR_NOT_IMPLEMENTED;
-    } else if (str.EqualsLiteral("")) {
-      sourceIndex = aCurrentIndex == 0 ?
-        FilterPrimitiveDescription::kPrimitiveIndexSourceGraphic :
-        aCurrentIndex - 1;
-    } else {
-      bool inputExists = aImageTable.Get(str, &sourceIndex);
-      if (!inputExists)
-        return NS_ERROR_FAILURE;
+  const nsTArray<nsStyleFilter>& filters = mTargetFrame->StyleSVGReset()->mFilters;
+  for (uint32_t i = 0; i < filters.Length(); i++) {
+    nsresult rv = BuildPrimitivesForFilter(filters[i]);
+    if (NS_FAILED(rv)) {
+      return rv;
     }
-
-    MOZ_ASSERT(sourceIndex < aCurrentIndex);
-    aSourceIndices.AppendElement(sourceIndex);
   }
   return NS_OK;
 }
 
 nsresult
-nsSVGFilterInstance::BuildPrimitives()
+nsFilterInstance::BuildPrimitivesForFilter(const nsStyleFilter& aFilter)
 {
-  nsTArray<nsRefPtr<nsSVGFE> > primitives;
-  for (nsIContent* child = mFilterElement->nsINode::GetFirstChild();
-       child;
-       child = child->GetNextSibling()) {
-    nsRefPtr<nsSVGFE> primitive;
-    CallQueryInterface(child, (nsSVGFE**)getter_AddRefs(primitive));
-    if (primitive) {
-      primitives.AppendElement(primitive);
-    }
-  }
-
-  // Maps source image name to source index.
-  nsDataHashtable<nsStringHashKey, int32_t> imageTable(10);
-
-  // The principal that we check principals of any loaded images against.
-  nsCOMPtr<nsIPrincipal> principal = mTargetFrame->GetContent()->NodePrincipal();
-
-  for (uint32_t i = 0; i < primitives.Length(); ++i) {
-    nsSVGFE* filter = primitives[i];
-
-    nsAutoTArray<int32_t,2> sourceIndices;
-    nsresult rv = GetSourceIndices(filter, i, imageTable, sourceIndices);
-    if (NS_FAILED(rv)) {
-      return rv;
+  if (aFilter.GetType() == NS_STYLE_FILTER_URL) {
+    // Build primitives for an SVG filter.
+    nsSVGFilterInstance svgFilterInstance(aFilter, mTargetFrame, mTargetBBox);
+    if (!svgFilterInstance.IsInitialized()) {
+      return NS_ERROR_FAILURE;
     }
 
-    IntRect primitiveSubregion =
-      ComputeFilterPrimitiveSubregion(filter, sourceIndices);
-
-    nsTArray<bool> sourcesAreTainted;
-    GetInputsAreTainted(sourceIndices, sourcesAreTainted);
-
-    FilterPrimitiveDescription descr =
-      filter->GetPrimitiveDescription(this, primitiveSubregion, sourcesAreTainted, mInputImages);
-
-    descr.SetIsTainted(filter->OutputIsTainted(sourcesAreTainted, principal));
-    descr.SetPrimitiveSubregion(primitiveSubregion);
+    // For now, we use the last SVG filter region as the overall filter region
+    // for the filter chain. Eventually, we will compute the overall filter
+    // using all of the generated FilterPrimitiveDescriptions.
+    mFilterRegion = svgFilterInstance.GetFilterRegion();
+    mFilterSpaceBounds = svgFilterInstance.GetFilterSpaceBounds();
 
-    for (uint32_t j = 0; j < sourceIndices.Length(); j++) {
-      int32_t inputIndex = sourceIndices[j];
-      descr.SetInputPrimitive(j, inputIndex);
-      ColorSpace inputColorSpace =
-        inputIndex < 0 ? SRGB : mPrimitiveDescriptions[inputIndex].OutputColorSpace();
-      ColorSpace desiredInputColorSpace = filter->GetInputColorSpace(j, inputColorSpace);
-      descr.SetInputColorSpace(j, desiredInputColorSpace);
-      if (j == 0) {
-        // the output color space is whatever in1 is if there is an in1
-        descr.SetOutputColorSpace(desiredInputColorSpace);
-      }
-    }
-
-    if (sourceIndices.Length() == 0) {
-      descr.SetOutputColorSpace(filter->GetOutputColorSpace());
-    }
-
-    mPrimitiveDescriptions.AppendElement(descr);
-
-    nsAutoString str;
-    filter->GetResultImageName().GetAnimValue(str, filter);
-    imageTable.Put(str, i);
+    return svgFilterInstance.BuildPrimitives(mPrimitiveDescriptions, mInputImages);
   }
 
-  return NS_OK;
+  // Eventually, we will build primitives for CSS filters, too.
+  return NS_ERROR_FAILURE;
 }
 
 void
-nsSVGFilterInstance::ComputeNeededBoxes()
+nsFilterInstance::ComputeNeededBoxes()
 {
   if (mPrimitiveDescriptions.IsEmpty())
     return;
 
   nsIntRegion sourceGraphicNeededRegion;
   nsIntRegion fillPaintNeededRegion;
   nsIntRegion strokePaintNeededRegion;
 
@@ -572,19 +248,19 @@ nsSVGFilterInstance::ComputeNeededBoxes(
   sourceGraphicNeededRegion.And(sourceGraphicNeededRegion, sourceBoundsInt);
 
   mSourceGraphic.mNeededBounds = sourceGraphicNeededRegion.GetBounds();
   mFillPaint.mNeededBounds = fillPaintNeededRegion.GetBounds();
   mStrokePaint.mNeededBounds = strokePaintNeededRegion.GetBounds();
 }
 
 nsresult
-nsSVGFilterInstance::BuildSourcePaint(SourceInfo *aSource,
-                                      gfxASurface* aTargetSurface,
-                                      DrawTarget* aTargetDT)
+nsFilterInstance::BuildSourcePaint(SourceInfo *aSource,
+                                   gfxASurface* aTargetSurface,
+                                   DrawTarget* aTargetDT)
 {
   nsIntRect neededRect = aSource->mNeededBounds;
 
   RefPtr<DrawTarget> offscreenDT;
   nsRefPtr<gfxASurface> offscreenSurface;
   nsRefPtr<gfxContext> ctx;
   if (aTargetSurface) {
     offscreenSurface = gfxPlatform::GetPlatform()->CreateOffscreenSurface(
@@ -639,36 +315,36 @@ nsSVGFilterInstance::BuildSourcePaint(So
     aSource->mSourceSurface = offscreenDT->Snapshot();
   }
   aSource->mSurfaceRect = ToIntRect(neededRect);
 
   return NS_OK;
 }
 
 nsresult
-nsSVGFilterInstance::BuildSourcePaints(gfxASurface* aTargetSurface,
-                                       DrawTarget* aTargetDT)
+nsFilterInstance::BuildSourcePaints(gfxASurface* aTargetSurface,
+                                    DrawTarget* aTargetDT)
 {
   nsresult rv = NS_OK;
 
   if (!mFillPaint.mNeededBounds.IsEmpty()) {
     rv = BuildSourcePaint(&mFillPaint, aTargetSurface, aTargetDT);
     NS_ENSURE_SUCCESS(rv, rv);
   }
 
   if (!mStrokePaint.mNeededBounds.IsEmpty()) {
     rv = BuildSourcePaint(&mStrokePaint, aTargetSurface, aTargetDT);
     NS_ENSURE_SUCCESS(rv, rv);
   }
   return  rv;
 }
 
 nsresult
-nsSVGFilterInstance::BuildSourceImage(gfxASurface* aTargetSurface,
-                                      DrawTarget* aTargetDT)
+nsFilterInstance::BuildSourceImage(gfxASurface* aTargetSurface,
+                                   DrawTarget* aTargetDT)
 {
   nsIntRect neededRect = mSourceGraphic.mNeededBounds;
   if (neededRect.IsEmpty()) {
     return NS_OK;
   }
 
   RefPtr<DrawTarget> offscreenDT;
   nsRefPtr<gfxASurface> offscreenSurface;
@@ -728,27 +404,18 @@ nsSVGFilterInstance::BuildSourceImage(gf
 
   mSourceGraphic.mSourceSurface = sourceGraphicSource;
   mSourceGraphic.mSurfaceRect = ToIntRect(neededRect);
    
   return NS_OK;
 }
 
 nsresult
-nsSVGFilterInstance::Render(gfxContext* aContext)
+nsFilterInstance::Render(gfxContext* aContext)
 {
-  nsresult rv = BuildPrimitives();
-  if (NS_FAILED(rv))
-    return rv;
-
-  if (mPrimitiveDescriptions.IsEmpty()) {
-    // Nothing should be rendered.
-    return NS_OK;
-  }
-
   nsIntRect filterRect = mPostFilterDirtyRect.Intersect(mFilterSpaceBounds);
   gfxMatrix ctm = GetFilterSpaceToDeviceSpaceTransform();
 
   if (filterRect.IsEmpty() || ctm.IsSingular()) {
     return NS_OK;
   }
 
   Matrix oldDTMatrix;
@@ -771,17 +438,17 @@ nsSVGFilterInstance::Render(gfxContext* 
     oldDTMatrix = dt->GetTransform();
     Matrix matrix = ToMatrix(ctm);
     matrix.Translate(filterRect.x, filterRect.y);
     dt->SetTransform(matrix * oldDTMatrix);
   }
 
   ComputeNeededBoxes();
 
-  rv = BuildSourceImage(resultImage, dt);
+  nsresult rv = BuildSourceImage(resultImage, dt);
   if (NS_FAILED(rv))
     return rv;
   rv = BuildSourcePaints(resultImage, dt);
   if (NS_FAILED(rv))
     return rv;
 
   IntRect filterSpaceBounds = ToIntRect(mFilterSpaceBounds);
   FilterDescription filter(mPrimitiveDescriptions, filterSpaceBounds);
@@ -803,55 +470,38 @@ nsSVGFilterInstance::Render(gfxContext* 
   } else {
     dt->SetTransform(oldDTMatrix);
   }
 
   return NS_OK;
 }
 
 nsresult
-nsSVGFilterInstance::ComputePostFilterDirtyRect(nsRect* aPostFilterDirtyRect)
+nsFilterInstance::ComputePostFilterDirtyRect(nsRect* aPostFilterDirtyRect)
 {
   *aPostFilterDirtyRect = nsRect();
   if (mPreFilterDirtyRect.IsEmpty()) {
     return NS_OK;
   }
 
-  nsresult rv = BuildPrimitives();
-  if (NS_FAILED(rv))
-    return rv;
-
-  if (mPrimitiveDescriptions.IsEmpty()) {
-    // Nothing should be rendered, so nothing can be dirty.
-    return NS_OK;
-  }
-
   IntRect filterSpaceBounds = ToIntRect(mFilterSpaceBounds);
   FilterDescription filter(mPrimitiveDescriptions, filterSpaceBounds);
   nsIntRegion resultChangeRegion =
     FilterSupport::ComputeResultChangeRegion(filter,
       mPreFilterDirtyRect, nsIntRegion(), nsIntRegion());
   *aPostFilterDirtyRect =
     FilterSpaceToFrameSpace(resultChangeRegion.GetBounds());
   return NS_OK;
 }
 
 nsresult
-nsSVGFilterInstance::ComputePostFilterExtents(nsRect* aPostFilterExtents)
+nsFilterInstance::ComputePostFilterExtents(nsRect* aPostFilterExtents)
 {
   *aPostFilterExtents = nsRect();
 
-  nsresult rv = BuildPrimitives();
-  if (NS_FAILED(rv))
-    return rv;
-
-  if (mPrimitiveDescriptions.IsEmpty()) {
-    return NS_OK;
-  }
-
   nsIntRect sourceBoundsInt;
   gfxRect sourceBounds = UserSpaceToFilterSpace(mTargetBBox);
   sourceBounds.RoundOut();
   // Detect possible float->int overflow
   if (!gfxUtils::GfxRectToIntRect(sourceBounds, &sourceBoundsInt))
     return NS_ERROR_FAILURE;
   sourceBoundsInt.UnionRect(sourceBoundsInt, mTargetBounds);
 
@@ -859,35 +509,26 @@ nsSVGFilterInstance::ComputePostFilterEx
   FilterDescription filter(mPrimitiveDescriptions, filterSpaceBounds);
   nsIntRegion postFilterExtents =
     FilterSupport::ComputePostFilterExtents(filter, sourceBoundsInt);
   *aPostFilterExtents = FilterSpaceToFrameSpace(postFilterExtents.GetBounds());
   return NS_OK;
 }
 
 nsresult
-nsSVGFilterInstance::ComputeSourceNeededRect(nsRect* aDirty)
+nsFilterInstance::ComputeSourceNeededRect(nsRect* aDirty)
 {
-  nsresult rv = BuildPrimitives();
-  if (NS_FAILED(rv))
-    return rv;
-
-  if (mPrimitiveDescriptions.IsEmpty()) {
-    // Nothing should be rendered, so nothing is needed.
-    return NS_OK;
-  }
-
   ComputeNeededBoxes();
   *aDirty = FilterSpaceToFrameSpace(mSourceGraphic.mNeededBounds);
 
   return NS_OK;
 }
 
 nsIntRect
-nsSVGFilterInstance::FrameSpaceToFilterSpace(const nsRect* aRect) const
+nsFilterInstance::FrameSpaceToFilterSpace(const nsRect* aRect) const
 {
   nsIntRect rect = mFilterSpaceBounds;
   if (aRect) {
     if (aRect->IsEmpty()) {
       return nsIntRect();
     }
     gfxRect rectInCSSPx =
       nsLayoutUtils::RectToGfxRect(*aRect, mAppUnitsPerCSSPx);
@@ -898,28 +539,28 @@ nsSVGFilterInstance::FrameSpaceToFilterS
     if (gfxUtils::GfxRectToIntRect(rectInFilterSpace, &intRect)) {
       rect = intRect;
     }
   }
   return rect;
 }
 
 nsRect
-nsSVGFilterInstance::FilterSpaceToFrameSpace(const nsIntRect& aRect) const
+nsFilterInstance::FilterSpaceToFrameSpace(const nsIntRect& aRect) const
 {
   if (aRect.IsEmpty()) {
     return nsRect();
   }
   gfxRect r(aRect.x, aRect.y, aRect.width, aRect.height);
   r = mFilterSpaceToFrameSpaceInCSSPxTransform.TransformBounds(r);
   return nsLayoutUtils::RoundGfxRectToAppRect(r, mAppUnitsPerCSSPx);
 }
 
 gfxMatrix
-nsSVGFilterInstance::GetUserSpaceToFrameSpaceInCSSPxTransform() const
+nsFilterInstance::GetUserSpaceToFrameSpaceInCSSPxTransform() const
 {
   gfxMatrix userToFrameSpaceInCSSPx;
 
   if ((mTargetFrame->GetStateBits() & NS_FRAME_SVG_LAYOUT)) {
     // As currently implemented by Mozilla for the purposes of filters, user
     // space is the coordinate system established by GetCanvasTM(), since
     // that's what we use to set filterToDeviceSpace above. In other words,
     // for SVG, user space is actually the coordinate system aTarget
copy from layout/svg/nsSVGFilterInstance.h
copy to layout/svg/nsFilterInstance.h
--- a/layout/svg/nsSVGFilterInstance.h
+++ b/layout/svg/nsFilterInstance.h
@@ -1,15 +1,15 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
-#ifndef __NS_SVGFILTERINSTANCE_H__
-#define __NS_SVGFILTERINSTANCE_H__
+#ifndef __NS_FILTERINSTANCE_H__
+#define __NS_FILTERINSTANCE_H__
 
 #include "gfxMatrix.h"
 #include "gfxPoint.h"
 #include "gfxRect.h"
 #include "nsCOMPtr.h"
 #include "nsHashKeys.h"
 #include "nsPoint.h"
 #include "nsRect.h"
@@ -17,47 +17,37 @@
 #include "nsSVGFilters.h"
 #include "nsSVGNumber2.h"
 #include "nsSVGNumberPair.h"
 #include "nsTArray.h"
 #include "nsIFrame.h"
 #include "mozilla/gfx/2D.h"
 
 class gfxASurface;
-class gfxImageSurface;
 class nsIFrame;
-class nsSVGFilterFrame;
 class nsSVGFilterPaintCallback;
 
-namespace mozilla {
-namespace dom {
-class SVGFilterElement;
-}
-}
-
 /**
  * This class performs all filter processing.
  * 
  * We build a graph of the filter image data flow, essentially
  * converting the filter graph to SSA. This lets us easily propagate
  * analysis data (such as bounding-boxes) over the filter primitive graph.
  *
  * Definition of "filter space": filter space is a coordinate system that is
  * aligned with the user space of the filtered element, with its origin located
- * at the top left of the filter region (as specified by our ctor's
- * aFilterRegion, and returned by our GetFilterRegion, specifically), and with
- * one unit equal in size to one pixel of the offscreen surface into which the
- * filter output would/will be painted.
+ * 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 nsSVGFilterInstance
+class nsFilterInstance
 {
-  typedef mozilla::gfx::Point3D Point3D;
   typedef mozilla::gfx::IntRect IntRect;
   typedef mozilla::gfx::SourceSurface SourceSurface;
   typedef mozilla::gfx::DrawTarget DrawTarget;
   typedef mozilla::gfx::FilterPrimitiveDescription FilterPrimitiveDescription;
 
 public:
   /**
    * Paint the given filtered frame.
@@ -98,77 +88,57 @@ public:
    *   aFilteredFrame, if non-null.
    */
   static nsRect GetPostFilterBounds(nsIFrame *aFilteredFrame,
                                     const gfxRect *aOverrideBBox = nullptr,
                                     const nsRect *aPreFilterBounds = nullptr);
 
   /**
    * @param aTargetFrame The frame of the filtered element under consideration.
-   * @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,
-                      nsSVGFilterPaintCallback *aPaintCallback,
-                      const nsRect *aPostFilterDirtyRect = nullptr,
-                      const nsRect *aPreFilterDirtyRect = nullptr,
-                      const nsRect *aOverridePreFilterVisualOverflowRect = nullptr,
-                      const gfxRect *aOverrideBBox = nullptr,
-                      nsIFrame* aTransformRoot = nullptr);
+  nsFilterInstance(nsIFrame *aTargetFrame,
+                   nsSVGFilterPaintCallback *aPaintCallback,
+                   const nsRect *aPostFilterDirtyRect = nullptr,
+                   const nsRect *aPreFilterDirtyRect = nullptr,
+                   const nsRect *aOverridePreFilterVisualOverflowRect = nullptr,
+                   const gfxRect *aOverrideBBox = nullptr,
+                   nsIFrame* aTransformRoot = nullptr);
 
   /**
    * Returns true if the filter instance was created successfully.
    */
   bool IsInitialized() const { return mInitialized; }
 
   /**
-   * Returns the user specified "filter region", in the filtered element's user
-   * space, after it has been adjusted out (if necessary) so that its edges
-   * coincide with pixel boundaries of the offscreen surface into which the
-   * filtered output would/will be painted.
-   */
-  gfxRect GetFilterRegion() const { return mFilterRegion; }
-
-  /**
-   * Returns the size of the user specified "filter region", in filter space.
-   * The size will be {filterRes.x by filterRes.y}, whether the user specified
-   * the filter's filterRes attribute explicitly, or the implementation chose
-   * the filterRes values. (The top-left of the filter region is the origin of
-   * filter space, which is why this method returns an nsIntSize and not an
-   * nsIntRect.)
-   */
-  uint32_t GetFilterResX() const { return mFilterSpaceBounds.width; }
-  uint32_t GetFilterResY() const { return mFilterSpaceBounds.height; }
-
-  /**
    * Draws the filter output into aContext. The area that
    * needs to be painted must have been specified before calling this method
    * by passing it as the aPostFilterDirtyRect argument to the
-   * nsSVGFilterInstance constructor.
+   * nsFilterInstance constructor.
    */
   nsresult Render(gfxContext* aContext);
 
   /**
    * Sets the aPostFilterDirtyRect outparam to the post-filter bounds in frame
    * space of the area that would be dirtied by mTargetFrame when a given
    * pre-filter area of mTargetFrame is dirtied. The pre-filter area must have
    * been specified before calling this method by passing it as the
-   * aPreFilterDirtyRect argument to the nsSVGFilterInstance constructor.
+   * aPreFilterDirtyRect argument to the nsFilterInstance constructor.
    */
   nsresult ComputePostFilterDirtyRect(nsRect* aPostFilterDirtyRect);
 
   /**
    * Sets the aPostFilterExtents outparam to the post-filter bounds in frame
    * space for the whole filter output. This is not necessarily equivalent to
    * the area that would be dirtied in the result when the entire pre-filter
    * area is dirtied, because some filter primitives can generate output
@@ -176,65 +146,33 @@ public:
    */
   nsresult ComputePostFilterExtents(nsRect* aPostFilterExtents);
 
   /**
    * Sets the aDirty outparam to the pre-filter bounds in frame space of the
    * area of mTargetFrame that is needed in order to paint the filtered output
    * for a given post-filter dirtied area. The post-filter area must have been
    * specified before calling this method by passing it as the aPostFilterDirtyRect
-   * argument to the nsSVGFilterInstance constructor.
+   * argument to the nsFilterInstance constructor.
    */
   nsresult ComputeSourceNeededRect(nsRect* aDirty);
 
-  float GetPrimitiveNumber(uint8_t aCtxType, const nsSVGNumber2 *aNumber) const
-  {
-    return GetPrimitiveNumber(aCtxType, aNumber->GetAnimValue());
-  }
-  float GetPrimitiveNumber(uint8_t aCtxType, const nsSVGNumberPair *aNumberPair,
-                           nsSVGNumberPair::PairIndex aIndex) const
-  {
-    return GetPrimitiveNumber(aCtxType, aNumberPair->GetAnimValue(aIndex));
-  }
-
-  /**
-   * Converts a userSpaceOnUse/objectBoundingBoxUnits unitless point
-   * into filter space, depending on the value of mPrimitiveUnits. (For
-   * objectBoundingBoxUnits, the bounding box offset is applied to the point.)
-   */
-  Point3D ConvertLocation(const Point3D& aPoint) const;
-
   /**
    * Returns the transform from the filtered element's user space to filter
    * space. This will be a simple translation and/or scale.
    */
   gfxMatrix GetUserSpaceToFilterSpaceTransform() const;
 
   /**
    * Returns the transform from filter space to outer-<svg> device space.
    */
   gfxMatrix GetFilterSpaceToDeviceSpaceTransform() const {
     return mFilterSpaceToDeviceSpaceTransform;
   }
 
-  gfxPoint FilterSpaceToUserSpace(const gfxPoint& aPt) const;
-
-  /**
-   * Returns the transform from filter space to frame space, in CSS px. This
-   * transform does not transform to frame space in its normal app units, since
-   * app units are ints, requiring appropriate rounding which can't be done by
-   * a transform matrix. Callers have to do that themselves as appropriate for
-   * their needs.
-   */
-  gfxMatrix GetFilterSpaceToFrameSpaceInCSSPxTransform() const {
-    return mFilterSpaceToFrameSpaceInCSSPxTransform;
-  }
-
-  int32_t AppUnitsPerCSSPixel() const { return mAppUnitsPerCSSPx; }
-
 private:
   struct SourceInfo {
     // Specifies which parts of the source need to be rendered.
     // Set by ComputeNeededBoxes().
     nsIntRect mNeededBounds;
 
     // The surface that contains the input rendering.
     // Set by BuildSourceImage / BuildSourcePaint.
@@ -264,52 +202,35 @@ 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();
 
   /**
+   * Add to the list of FilterPrimitiveDescriptions for a particular SVG
+   * reference filter or CSS filter. This populates mPrimitiveDescrs and
+   * mInputImages.
+   */
+  nsresult BuildPrimitivesForFilter(const nsStyleFilter& aFilter);
+
+  /**
    * Computes the filter space bounds of the areas that we actually *need* from
    * the filter sources, based on the value of mPostFilterDirtyRect.
    * This sets mNeededBounds on the corresponding SourceInfo structs.
    */
-   void ComputeNeededBoxes();
-
-  /**
-   * Computes the filter primitive subregion for the given primitive.
-   */
-  IntRect ComputeFilterPrimitiveSubregion(nsSVGFE* aFilterElement,
-                                          const nsTArray<int32_t>& aInputIndices);
-
-  /**
-   * Takes the input indices of a filter primitive and returns for each input
-   * whether the input's output is tainted.
-   */
-  void GetInputsAreTainted(const nsTArray<int32_t>& aInputIndices,
-                           nsTArray<bool>& aOutInputsAreTainted);
-
-  /**
-   * Scales a numeric filter primitive length in the X, Y or "XY" directions
-   * into a length in filter space (no offset is applied).
-   */
-  float GetPrimitiveNumber(uint8_t aCtxType, float aValue) const;
+  void ComputeNeededBoxes();
 
   gfxRect UserSpaceToFilterSpace(const gfxRect& aUserSpace) const;
 
   /**
    * Converts an nsRect that is relative to a filtered frame's origin (i.e. the
    * top-left corner of its border box) into filter space.
    * Returns the entire filter region if aRect is null, or if the result is too
    * large to be stored in an nsIntRect.
@@ -324,34 +245,19 @@ 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;
@@ -384,21 +290,16 @@ private:
   /**
    * If set, this is the filter space bounds of the outer-<svg> device bounds
    * of the pre-filter area of the filtered element that changed. (As
    * bounds-of-bounds, this may be a fair bit bigger than we actually need,
    * unfortunately.)
    */
   nsIntRect               mPreFilterDirtyRect;
 
-  /**
-   * The 'primitiveUnits' attribute value (objectBoundingBox or userSpaceOnUse).
-   */
-  uint16_t                mPrimitiveUnits;
-
   SourceInfo              mSourceGraphic;
   SourceInfo              mFillPaint;
   SourceInfo              mStrokePaint;
   nsIFrame*               mTransformRoot;
   nsTArray<mozilla::RefPtr<SourceSurface>> mInputImages;
   nsTArray<FilterPrimitiveDescription> mPrimitiveDescriptions;
   int32_t                 mAppUnitsPerCSSPx;
   bool                    mInitialized;
--- a/layout/svg/nsSVGFilterInstance.cpp
+++ b/layout/svg/nsSVGFilterInstance.cpp
@@ -19,108 +19,22 @@
 #include "SVGContentUtils.h"
 #include "FilterSupport.h"
 #include "gfx2DGlue.h"
 
 using namespace mozilla;
 using namespace mozilla::dom;
 using namespace mozilla::gfx;
 
-nsresult
-nsSVGFilterInstance::PaintFilteredFrame(nsRenderingContext *aContext,
-                                        nsIFrame *aFilteredFrame,
-                                        nsSVGFilterPaintCallback *aPaintCallback,
-                                        const nsRect *aDirtyArea,
-                                        nsIFrame* aTransformRoot)
-{
-  nsSVGFilterInstance instance(aFilteredFrame, aPaintCallback, aDirtyArea,
-                               nullptr, nullptr, nullptr,
-                               aTransformRoot);
-  if (!instance.IsInitialized()) {
-    return NS_OK;
-  }
-  return instance.Render(aContext->ThebesContext());
-}
-
-nsRect
-nsSVGFilterInstance::GetPostFilterDirtyArea(nsIFrame *aFilteredFrame,
-                                            const nsRect& aPreFilterDirtyRect)
-{
-  if (aPreFilterDirtyRect.IsEmpty()) {
-    return nsRect();
-  }
-
-  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(nsIFrame *aFilteredFrame,
-                                            const nsRect& 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(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, 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,
-                                         nsSVGFilterPaintCallback *aPaintCallback,
-                                         const nsRect *aPostFilterDirtyRect,
-                                         const nsRect *aPreFilterDirtyRect,
-                                         const nsRect *aPreFilterVisualOverflowRectOverride,
-                                         const gfxRect *aOverrideBBox,
-                                         nsIFrame* aTransformRoot) :
+nsSVGFilterInstance::nsSVGFilterInstance(const nsStyleFilter& aFilter,
+                                         nsIFrame *aTargetFrame,
+                                         const gfxRect& aTargetBBox) :
+  mFilter(aFilter),
   mTargetFrame(aTargetFrame),
-  mFilters(aTargetFrame->StyleSVGReset()->mFilters),
-  mPaintCallback(aPaintCallback),
-  mTransformRoot(aTransformRoot),
+  mTargetBBox(aTargetBBox),
   mInitialized(false) {
 
   // Get the filter frame.
   mFilterFrame = GetFilterFrame();
   if (!mFilterFrame) {
     return;
   }
 
@@ -129,19 +43,16 @@ nsSVGFilterInstance::nsSVGFilterInstance
   if (!mFilterElement) {
     NS_NOTREACHED("filter frame should have a related element");
     return;
   }
 
   mPrimitiveUnits =
     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
   // with units. This is a common mistake and can result in filterRes being
   // *massive* below (because we ignore the units and interpret the number as
   // a factor of the bbox width/height). We should also send a warning if the
   // user uses a number without units (a future SVG spec should really
@@ -216,70 +127,28 @@ nsSVGFilterInstance::nsSVGFilterInstance
     bool overflow;
     filterRes = nsSVGUtils::ConvertToSurfaceSize(mFilterRegion.Size(),
                                                  &overflow);
     mFilterRegion.Scale(1.0 / scale.width, 1.0 / scale.height);
   }
 
   mFilterSpaceBounds.SetRect(nsIntPoint(0, 0), filterRes);
 
-  // Get various transforms:
-
-  gfxMatrix filterToUserSpace(mFilterRegion.Width() / filterRes.width, 0.0f,
-                              0.0f, mFilterRegion.Height() / filterRes.height,
-                              mFilterRegion.X(), mFilterRegion.Y());
-
-  // Only used (so only set) when we paint:
-  if (mPaintCallback) {
-    mFilterSpaceToDeviceSpaceTransform = filterToUserSpace *
-              nsSVGUtils::GetCanvasTM(mTargetFrame, nsISVGChildFrame::FOR_PAINTING);
-  }
-
-  // Convert the passed in rects from frame to filter space:
-
-  mAppUnitsPerCSSPx = mTargetFrame->PresContext()->AppUnitsPerCSSPixel();
-
-  mFilterSpaceToFrameSpaceInCSSPxTransform =
-    filterToUserSpace * GetUserSpaceToFrameSpaceInCSSPxTransform();
-  // mFilterSpaceToFrameSpaceInCSSPxTransform is always invertible
-  mFrameSpaceInCSSPxToFilterSpaceTransform =
-    mFilterSpaceToFrameSpaceInCSSPxTransform;
-  mFrameSpaceInCSSPxToFilterSpaceTransform.Invert();
-
-  mPostFilterDirtyRect = FrameSpaceToFilterSpace(aPostFilterDirtyRect);
-  mPreFilterDirtyRect = FrameSpaceToFilterSpace(aPreFilterDirtyRect);
-  if (aPreFilterVisualOverflowRectOverride) {
-    mTargetBounds = 
-      FrameSpaceToFilterSpace(aPreFilterVisualOverflowRectOverride);
-  } 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) {
+  if (mFilter.GetType() != NS_STYLE_FILTER_URL) {
     // The filter is not an SVG reference filter.
     return nullptr;
   }
 
-  nsIURI* url = filter.GetURL();
+  nsIURI* url = mFilter.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();
@@ -359,45 +228,39 @@ gfxRect
 nsSVGFilterInstance::UserSpaceToFilterSpace(const gfxRect& aRect) const
 {
   gfxRect r = aRect - mFilterRegion.TopLeft();
   r.Scale(mFilterSpaceBounds.width / mFilterRegion.Width(),
           mFilterSpaceBounds.height / mFilterRegion.Height());
   return r;
 }
 
-gfxPoint
-nsSVGFilterInstance::FilterSpaceToUserSpace(const gfxPoint& aPt) const
-{
-  return gfxPoint(aPt.x * mFilterRegion.Width() / mFilterSpaceBounds.width + mFilterRegion.X(),
-                  aPt.y * mFilterRegion.Height() / mFilterSpaceBounds.height + mFilterRegion.Y());
-}
-
 gfxMatrix
 nsSVGFilterInstance::GetUserSpaceToFilterSpaceTransform() const
 {
   gfxFloat widthScale = mFilterSpaceBounds.width / mFilterRegion.Width();
   gfxFloat heightScale = mFilterSpaceBounds.height / mFilterRegion.Height();
   return gfxMatrix(widthScale, 0.0f,
                    0.0f, heightScale,
                    -mFilterRegion.X() * widthScale, -mFilterRegion.Y() * heightScale);
 }
 
 IntRect
 nsSVGFilterInstance::ComputeFilterPrimitiveSubregion(nsSVGFE* aFilterElement,
+                                                     const nsTArray<FilterPrimitiveDescription>& aPrimitiveDescrs,
                                                      const nsTArray<int32_t>& aInputIndices)
 {
   nsSVGFE* fE = aFilterElement;
 
   IntRect defaultFilterSubregion(0,0,0,0);
   if (fE->SubregionIsUnionOfRegions()) {
     for (uint32_t i = 0; i < aInputIndices.Length(); ++i) {
       int32_t inputIndex = aInputIndices[i];
       IntRect inputSubregion = inputIndex >= 0 ?
-        mPrimitiveDescriptions[inputIndex].PrimitiveSubregion() :
+        aPrimitiveDescrs[inputIndex].PrimitiveSubregion() :
         ToIntRect(mFilterSpaceBounds);
 
       defaultFilterSubregion = defaultFilterSubregion.Union(inputSubregion);
     }
   } else {
     defaultFilterSubregion = ToIntRect(mFilterSpaceBounds);
   }
 
@@ -418,26 +281,27 @@ nsSVGFilterInstance::ComputeFilterPrimit
   // Following the spec, any pixel partially in the region is included
   // in the region.
   region.RoundOut();
 
   return RoundedToInt(region);
 }
 
 void
-nsSVGFilterInstance::GetInputsAreTainted(const nsTArray<int32_t>& aInputIndices,
+nsSVGFilterInstance::GetInputsAreTainted(const nsTArray<FilterPrimitiveDescription>& aPrimitiveDescrs,
+                                         const nsTArray<int32_t>& aInputIndices,
                                          nsTArray<bool>& aOutInputsAreTainted)
 {
   for (uint32_t i = 0; i < aInputIndices.Length(); i++) {
     int32_t inputIndex = aInputIndices[i];
     if (inputIndex < 0) {
       // SourceGraphic, SourceAlpha, FillPaint and StrokePaint are tainted.
       aOutInputsAreTainted.AppendElement(true);
     } else {
-      aOutInputsAreTainted.AppendElement(mPrimitiveDescriptions[inputIndex].IsTainted());
+      aOutInputsAreTainted.AppendElement(aPrimitiveDescrs[inputIndex].IsTainted());
     }
   }
 }
 
 static nsresult
 GetSourceIndices(nsSVGFE* aFilterElement,
                  int32_t aCurrentIndex,
                  const nsDataHashtable<nsStringHashKey, int32_t>& aImageTable,
@@ -474,17 +338,18 @@ GetSourceIndices(nsSVGFE* aFilterElement
 
     MOZ_ASSERT(sourceIndex < aCurrentIndex);
     aSourceIndices.AppendElement(sourceIndex);
   }
   return NS_OK;
 }
 
 nsresult
-nsSVGFilterInstance::BuildPrimitives()
+nsSVGFilterInstance::BuildPrimitives(nsTArray<FilterPrimitiveDescription>& aPrimitiveDescrs,
+                                     nsTArray<mozilla::RefPtr<SourceSurface>>& aInputImages)
 {
   nsTArray<nsRefPtr<nsSVGFE> > primitives;
   for (nsIContent* child = mFilterElement->nsINode::GetFirstChild();
        child;
        child = child->GetNextSibling()) {
     nsRefPtr<nsSVGFE> primitive;
     CallQueryInterface(child, (nsSVGFE**)getter_AddRefs(primitive));
     if (primitive) {
@@ -503,443 +368,45 @@ nsSVGFilterInstance::BuildPrimitives()
 
     nsAutoTArray<int32_t,2> sourceIndices;
     nsresult rv = GetSourceIndices(filter, i, imageTable, sourceIndices);
     if (NS_FAILED(rv)) {
       return rv;
     }
 
     IntRect primitiveSubregion =
-      ComputeFilterPrimitiveSubregion(filter, sourceIndices);
+      ComputeFilterPrimitiveSubregion(filter, aPrimitiveDescrs, sourceIndices);
 
     nsTArray<bool> sourcesAreTainted;
-    GetInputsAreTainted(sourceIndices, sourcesAreTainted);
+    GetInputsAreTainted(aPrimitiveDescrs, sourceIndices, sourcesAreTainted);
 
     FilterPrimitiveDescription descr =
-      filter->GetPrimitiveDescription(this, primitiveSubregion, sourcesAreTainted, mInputImages);
+      filter->GetPrimitiveDescription(this, primitiveSubregion, sourcesAreTainted, aInputImages);
 
     descr.SetIsTainted(filter->OutputIsTainted(sourcesAreTainted, principal));
     descr.SetPrimitiveSubregion(primitiveSubregion);
 
     for (uint32_t j = 0; j < sourceIndices.Length(); j++) {
       int32_t inputIndex = sourceIndices[j];
       descr.SetInputPrimitive(j, inputIndex);
       ColorSpace inputColorSpace =
-        inputIndex < 0 ? SRGB : mPrimitiveDescriptions[inputIndex].OutputColorSpace();
+        inputIndex < 0 ? SRGB : aPrimitiveDescrs[inputIndex].OutputColorSpace();
       ColorSpace desiredInputColorSpace = filter->GetInputColorSpace(j, inputColorSpace);
       descr.SetInputColorSpace(j, desiredInputColorSpace);
       if (j == 0) {
         // the output color space is whatever in1 is if there is an in1
         descr.SetOutputColorSpace(desiredInputColorSpace);
       }
     }
 
     if (sourceIndices.Length() == 0) {
       descr.SetOutputColorSpace(filter->GetOutputColorSpace());
     }
 
-    mPrimitiveDescriptions.AppendElement(descr);
+    aPrimitiveDescrs.AppendElement(descr);
 
     nsAutoString str;
     filter->GetResultImageName().GetAnimValue(str, filter);
     imageTable.Put(str, i);
   }
 
   return NS_OK;
 }
-
-void
-nsSVGFilterInstance::ComputeNeededBoxes()
-{
-  if (mPrimitiveDescriptions.IsEmpty())
-    return;
-
-  nsIntRegion sourceGraphicNeededRegion;
-  nsIntRegion fillPaintNeededRegion;
-  nsIntRegion strokePaintNeededRegion;
-
-  FilterDescription filter(mPrimitiveDescriptions, ToIntRect(mFilterSpaceBounds));
-  FilterSupport::ComputeSourceNeededRegions(
-    filter, mPostFilterDirtyRect,
-    sourceGraphicNeededRegion, fillPaintNeededRegion, strokePaintNeededRegion);
-
-  nsIntRect sourceBoundsInt;
-  gfxRect sourceBounds = UserSpaceToFilterSpace(mTargetBBox);
-  sourceBounds.RoundOut();
-  // Detect possible float->int overflow
-  if (!gfxUtils::GfxRectToIntRect(sourceBounds, &sourceBoundsInt))
-    return;
-  sourceBoundsInt.UnionRect(sourceBoundsInt, mTargetBounds);
-
-  sourceGraphicNeededRegion.And(sourceGraphicNeededRegion, sourceBoundsInt);
-
-  mSourceGraphic.mNeededBounds = sourceGraphicNeededRegion.GetBounds();
-  mFillPaint.mNeededBounds = fillPaintNeededRegion.GetBounds();
-  mStrokePaint.mNeededBounds = strokePaintNeededRegion.GetBounds();
-}
-
-nsresult
-nsSVGFilterInstance::BuildSourcePaint(SourceInfo *aSource,
-                                      gfxASurface* aTargetSurface,
-                                      DrawTarget* aTargetDT)
-{
-  nsIntRect neededRect = aSource->mNeededBounds;
-
-  RefPtr<DrawTarget> offscreenDT;
-  nsRefPtr<gfxASurface> offscreenSurface;
-  nsRefPtr<gfxContext> ctx;
-  if (aTargetSurface) {
-    offscreenSurface = gfxPlatform::GetPlatform()->CreateOffscreenSurface(
-      neededRect.Size().ToIntSize(), gfxContentType::COLOR_ALPHA);
-    if (!offscreenSurface || offscreenSurface->CairoStatus()) {
-      return NS_ERROR_OUT_OF_MEMORY;
-    }
-    ctx = new gfxContext(offscreenSurface);
-  } else {
-    offscreenDT = gfxPlatform::GetPlatform()->CreateOffscreenContentDrawTarget(
-      ToIntSize(neededRect.Size()), SurfaceFormat::B8G8R8A8);
-    if (!offscreenDT) {
-      return NS_ERROR_OUT_OF_MEMORY;
-    }
-    ctx = new gfxContext(offscreenDT);
-  }
-
-  ctx->Translate(-neededRect.TopLeft());
-
-  nsRenderingContext tmpCtx;
-  tmpCtx.Init(mTargetFrame->PresContext()->DeviceContext(), ctx);
-
-  gfxMatrix m = GetUserSpaceToFilterSpaceTransform();
-  m.Invert();
-  gfxRect r = m.TransformBounds(mFilterSpaceBounds);
-
-  gfxMatrix deviceToFilterSpace = GetFilterSpaceToDeviceSpaceTransform().Invert();
-  gfxContext *gfx = tmpCtx.ThebesContext();
-  gfx->Multiply(deviceToFilterSpace);
-
-  gfx->Save();
-
-  gfxMatrix matrix =
-    nsSVGUtils::GetCanvasTM(mTargetFrame, nsISVGChildFrame::FOR_PAINTING,
-                            mTransformRoot);
-  if (!matrix.IsSingular()) {
-    gfx->Multiply(matrix);
-    gfx->Rectangle(r);
-    if ((aSource == &mFillPaint && 
-         nsSVGUtils::SetupCairoFillPaint(mTargetFrame, gfx)) ||
-        (aSource == &mStrokePaint &&
-         nsSVGUtils::SetupCairoStrokePaint(mTargetFrame, gfx))) {
-      gfx->Fill();
-    }
-  }
-  gfx->Restore();
-
-  if (offscreenSurface) {
-    aSource->mSourceSurface =
-      gfxPlatform::GetPlatform()->GetSourceSurfaceForSurface(aTargetDT, offscreenSurface);
-  } else {
-    aSource->mSourceSurface = offscreenDT->Snapshot();
-  }
-  aSource->mSurfaceRect = ToIntRect(neededRect);
-
-  return NS_OK;
-}
-
-nsresult
-nsSVGFilterInstance::BuildSourcePaints(gfxASurface* aTargetSurface,
-                                       DrawTarget* aTargetDT)
-{
-  nsresult rv = NS_OK;
-
-  if (!mFillPaint.mNeededBounds.IsEmpty()) {
-    rv = BuildSourcePaint(&mFillPaint, aTargetSurface, aTargetDT);
-    NS_ENSURE_SUCCESS(rv, rv);
-  }
-
-  if (!mStrokePaint.mNeededBounds.IsEmpty()) {
-    rv = BuildSourcePaint(&mStrokePaint, aTargetSurface, aTargetDT);
-    NS_ENSURE_SUCCESS(rv, rv);
-  }
-  return  rv;
-}
-
-nsresult
-nsSVGFilterInstance::BuildSourceImage(gfxASurface* aTargetSurface,
-                                      DrawTarget* aTargetDT)
-{
-  nsIntRect neededRect = mSourceGraphic.mNeededBounds;
-  if (neededRect.IsEmpty()) {
-    return NS_OK;
-  }
-
-  RefPtr<DrawTarget> offscreenDT;
-  nsRefPtr<gfxASurface> offscreenSurface;
-  nsRefPtr<gfxContext> ctx;
-  if (aTargetSurface) {
-    offscreenSurface = gfxPlatform::GetPlatform()->CreateOffscreenSurface(
-      neededRect.Size().ToIntSize(), gfxContentType::COLOR_ALPHA);
-    if (!offscreenSurface || offscreenSurface->CairoStatus()) {
-      return NS_ERROR_OUT_OF_MEMORY;
-    }
-    ctx = new gfxContext(offscreenSurface);
-  } else {
-    offscreenDT = gfxPlatform::GetPlatform()->CreateOffscreenContentDrawTarget(
-      ToIntSize(neededRect.Size()), SurfaceFormat::B8G8R8A8);
-    if (!offscreenDT) {
-      return NS_ERROR_OUT_OF_MEMORY;
-    }
-    ctx = new gfxContext(offscreenDT);
-  }
-
-  ctx->Translate(-neededRect.TopLeft());
-
-  nsRenderingContext tmpCtx;
-  tmpCtx.Init(mTargetFrame->PresContext()->DeviceContext(), ctx);
-
-  gfxMatrix m = GetUserSpaceToFilterSpaceTransform();
-  m.Invert();
-  gfxRect r = m.TransformBounds(neededRect);
-  r.RoundOut();
-  nsIntRect dirty;
-  if (!gfxUtils::GfxRectToIntRect(r, &dirty))
-    return NS_ERROR_FAILURE;
-
-  // SVG graphics paint to device space, so we need to set an initial device
-  // space to filter space transform on the gfxContext that SourceGraphic
-  // and SourceAlpha will paint to.
-  //
-  // (In theory it would be better to minimize error by having filtered SVG
-  // graphics temporarily paint to user space when painting the sources and
-  // only set a user space to filter space transform on the gfxContext
-  // (since that would eliminate the transform multiplications from user
-  // space to device space and back again). However, that would make the
-  // code more complex while being hard to get right without introducing
-  // subtle bugs, and in practice it probably makes no real difference.)
-  gfxMatrix deviceToFilterSpace = GetFilterSpaceToDeviceSpaceTransform().Invert();
-  tmpCtx.ThebesContext()->Multiply(deviceToFilterSpace);
-  mPaintCallback->Paint(&tmpCtx, mTargetFrame, &dirty, mTransformRoot);
-
-  RefPtr<SourceSurface> sourceGraphicSource;
-
-  if (offscreenSurface) {
-    sourceGraphicSource =
-      gfxPlatform::GetPlatform()->GetSourceSurfaceForSurface(aTargetDT, offscreenSurface);
-  } else {
-    sourceGraphicSource = offscreenDT->Snapshot();
-  }
-
-  mSourceGraphic.mSourceSurface = sourceGraphicSource;
-  mSourceGraphic.mSurfaceRect = ToIntRect(neededRect);
-   
-  return NS_OK;
-}
-
-nsresult
-nsSVGFilterInstance::Render(gfxContext* aContext)
-{
-  nsresult rv = BuildPrimitives();
-  if (NS_FAILED(rv))
-    return rv;
-
-  if (mPrimitiveDescriptions.IsEmpty()) {
-    // Nothing should be rendered.
-    return NS_OK;
-  }
-
-  nsIntRect filterRect = mPostFilterDirtyRect.Intersect(mFilterSpaceBounds);
-  gfxMatrix ctm = GetFilterSpaceToDeviceSpaceTransform();
-
-  if (filterRect.IsEmpty() || ctm.IsSingular()) {
-    return NS_OK;
-  }
-
-  Matrix oldDTMatrix;
-  nsRefPtr<gfxASurface> resultImage;
-  RefPtr<DrawTarget> dt;
-  if (aContext->IsCairo()) {
-    resultImage =
-      gfxPlatform::GetPlatform()->CreateOffscreenSurface(filterRect.Size().ToIntSize(),
-                                                         gfxContentType::COLOR_ALPHA);
-    if (!resultImage || resultImage->CairoStatus())
-      return NS_ERROR_OUT_OF_MEMORY;
-
-    // Create a Cairo DrawTarget around resultImage.
-    dt = gfxPlatform::GetPlatform()->CreateDrawTargetForSurface(
-           resultImage, ToIntSize(filterRect.Size()));
-  } else {
-    // When we have a DrawTarget-backed context, we can call DrawFilter
-    // directly on the target DrawTarget and don't need a temporary DT.
-    dt = aContext->GetDrawTarget();
-    oldDTMatrix = dt->GetTransform();
-    Matrix matrix = ToMatrix(ctm);
-    matrix.Translate(filterRect.x, filterRect.y);
-    dt->SetTransform(matrix * oldDTMatrix);
-  }
-
-  ComputeNeededBoxes();
-
-  rv = BuildSourceImage(resultImage, dt);
-  if (NS_FAILED(rv))
-    return rv;
-  rv = BuildSourcePaints(resultImage, dt);
-  if (NS_FAILED(rv))
-    return rv;
-
-  IntRect filterSpaceBounds = ToIntRect(mFilterSpaceBounds);
-  FilterDescription filter(mPrimitiveDescriptions, filterSpaceBounds);
-
-  FilterSupport::RenderFilterDescription(
-    dt, filter, ToRect(filterRect),
-    mSourceGraphic.mSourceSurface, mSourceGraphic.mSurfaceRect,
-    mFillPaint.mSourceSurface, mFillPaint.mSurfaceRect,
-    mStrokePaint.mSourceSurface, mStrokePaint.mSurfaceRect,
-    mInputImages);
-
-  if (resultImage) {
-    aContext->Save();
-    aContext->Multiply(ctm);
-    aContext->Translate(filterRect.TopLeft());
-    aContext->SetSource(resultImage);
-    aContext->Paint();
-    aContext->Restore();
-  } else {
-    dt->SetTransform(oldDTMatrix);
-  }
-
-  return NS_OK;
-}
-
-nsresult
-nsSVGFilterInstance::ComputePostFilterDirtyRect(nsRect* aPostFilterDirtyRect)
-{
-  *aPostFilterDirtyRect = nsRect();
-  if (mPreFilterDirtyRect.IsEmpty()) {
-    return NS_OK;
-  }
-
-  nsresult rv = BuildPrimitives();
-  if (NS_FAILED(rv))
-    return rv;
-
-  if (mPrimitiveDescriptions.IsEmpty()) {
-    // Nothing should be rendered, so nothing can be dirty.
-    return NS_OK;
-  }
-
-  IntRect filterSpaceBounds = ToIntRect(mFilterSpaceBounds);
-  FilterDescription filter(mPrimitiveDescriptions, filterSpaceBounds);
-  nsIntRegion resultChangeRegion =
-    FilterSupport::ComputeResultChangeRegion(filter,
-      mPreFilterDirtyRect, nsIntRegion(), nsIntRegion());
-  *aPostFilterDirtyRect =
-    FilterSpaceToFrameSpace(resultChangeRegion.GetBounds());
-  return NS_OK;
-}
-
-nsresult
-nsSVGFilterInstance::ComputePostFilterExtents(nsRect* aPostFilterExtents)
-{
-  *aPostFilterExtents = nsRect();
-
-  nsresult rv = BuildPrimitives();
-  if (NS_FAILED(rv))
-    return rv;
-
-  if (mPrimitiveDescriptions.IsEmpty()) {
-    return NS_OK;
-  }
-
-  nsIntRect sourceBoundsInt;
-  gfxRect sourceBounds = UserSpaceToFilterSpace(mTargetBBox);
-  sourceBounds.RoundOut();
-  // Detect possible float->int overflow
-  if (!gfxUtils::GfxRectToIntRect(sourceBounds, &sourceBoundsInt))
-    return NS_ERROR_FAILURE;
-  sourceBoundsInt.UnionRect(sourceBoundsInt, mTargetBounds);
-
-  IntRect filterSpaceBounds = ToIntRect(mFilterSpaceBounds);
-  FilterDescription filter(mPrimitiveDescriptions, filterSpaceBounds);
-  nsIntRegion postFilterExtents =
-    FilterSupport::ComputePostFilterExtents(filter, sourceBoundsInt);
-  *aPostFilterExtents = FilterSpaceToFrameSpace(postFilterExtents.GetBounds());
-  return NS_OK;
-}
-
-nsresult
-nsSVGFilterInstance::ComputeSourceNeededRect(nsRect* aDirty)
-{
-  nsresult rv = BuildPrimitives();
-  if (NS_FAILED(rv))
-    return rv;
-
-  if (mPrimitiveDescriptions.IsEmpty()) {
-    // Nothing should be rendered, so nothing is needed.
-    return NS_OK;
-  }
-
-  ComputeNeededBoxes();
-  *aDirty = FilterSpaceToFrameSpace(mSourceGraphic.mNeededBounds);
-
-  return NS_OK;
-}
-
-nsIntRect
-nsSVGFilterInstance::FrameSpaceToFilterSpace(const nsRect* aRect) const
-{
-  nsIntRect rect = mFilterSpaceBounds;
-  if (aRect) {
-    if (aRect->IsEmpty()) {
-      return nsIntRect();
-    }
-    gfxRect rectInCSSPx =
-      nsLayoutUtils::RectToGfxRect(*aRect, mAppUnitsPerCSSPx);
-    gfxRect rectInFilterSpace =
-      mFrameSpaceInCSSPxToFilterSpaceTransform.TransformBounds(rectInCSSPx);
-    rectInFilterSpace.RoundOut();
-    nsIntRect intRect;
-    if (gfxUtils::GfxRectToIntRect(rectInFilterSpace, &intRect)) {
-      rect = intRect;
-    }
-  }
-  return rect;
-}
-
-nsRect
-nsSVGFilterInstance::FilterSpaceToFrameSpace(const nsIntRect& aRect) const
-{
-  if (aRect.IsEmpty()) {
-    return nsRect();
-  }
-  gfxRect r(aRect.x, aRect.y, aRect.width, aRect.height);
-  r = mFilterSpaceToFrameSpaceInCSSPxTransform.TransformBounds(r);
-  return nsLayoutUtils::RoundGfxRectToAppRect(r, mAppUnitsPerCSSPx);
-}
-
-gfxMatrix
-nsSVGFilterInstance::GetUserSpaceToFrameSpaceInCSSPxTransform() const
-{
-  gfxMatrix userToFrameSpaceInCSSPx;
-
-  if ((mTargetFrame->GetStateBits() & NS_FRAME_SVG_LAYOUT)) {
-    // As currently implemented by Mozilla for the purposes of filters, user
-    // space is the coordinate system established by GetCanvasTM(), since
-    // that's what we use to set filterToDeviceSpace above. In other words,
-    // for SVG, user space is actually the coordinate system aTarget
-    // establishes for _its_ children (i.e. after taking account of any x/y
-    // and viewBox attributes), not the coordinate system that is established
-    // for it by its 'transform' attribute (or by its _parent_) as it's
-    // normally defined. (XXX We should think about fixing this.) The only
-    // frame type for which these extra transforms are not simply an x/y
-    // translation is nsSVGInnerSVGFrame, hence we treat it specially here.
-    if (mTargetFrame->GetType() == nsGkAtoms::svgInnerSVGFrame) {
-      userToFrameSpaceInCSSPx =
-        static_cast<nsSVGElement*>(mTargetFrame->GetContent())->
-          PrependLocalTransformsTo(gfxMatrix());
-    } else {
-      gfxPoint targetsUserSpaceOffset =
-        nsLayoutUtils::RectToGfxRect(mTargetFrame->GetRect(),
-                                     mAppUnitsPerCSSPx).TopLeft();
-      userToFrameSpaceInCSSPx.Translate(-targetsUserSpaceOffset);
-    }
-  }
-  // else, for all other frames, leave as the identity matrix
-  return userToFrameSpaceInCSSPx;
-}
--- a/layout/svg/nsSVGFilterInstance.h
+++ b/layout/svg/nsSVGFilterInstance.h
@@ -2,193 +2,88 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef __NS_SVGFILTERINSTANCE_H__
 #define __NS_SVGFILTERINSTANCE_H__
 
 #include "gfxMatrix.h"
-#include "gfxPoint.h"
 #include "gfxRect.h"
-#include "nsCOMPtr.h"
-#include "nsHashKeys.h"
-#include "nsPoint.h"
-#include "nsRect.h"
-#include "nsSize.h"
 #include "nsSVGFilters.h"
 #include "nsSVGNumber2.h"
 #include "nsSVGNumberPair.h"
 #include "nsTArray.h"
 #include "nsIFrame.h"
-#include "mozilla/gfx/2D.h"
 
-class gfxASurface;
-class gfxImageSurface;
 class nsIFrame;
 class nsSVGFilterFrame;
 class nsSVGFilterPaintCallback;
 
 namespace mozilla {
 namespace dom {
 class SVGFilterElement;
 }
 }
 
 /**
- * This class performs all filter processing.
- * 
- * We build a graph of the filter image data flow, essentially
- * converting the filter graph to SSA. This lets us easily propagate
- * analysis data (such as bounding-boxes) over the filter primitive graph.
+ * This class helps nsFilterInstance build its filter graph by processing a
+ * single SVG reference filter.
  *
- * Definition of "filter space": filter space is a coordinate system that is
- * aligned with the user space of the filtered element, with its origin located
- * at the top left of the filter region (as specified by our ctor's
- * aFilterRegion, and returned by our GetFilterRegion, specifically), 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
+ * In BuildPrimitives, this class iterates through the referenced <filter>
+ * element's primitive elements, creating a FilterPrimitiveDescription for
+ * each one.
  */
 class nsSVGFilterInstance
 {
   typedef mozilla::gfx::Point3D Point3D;
   typedef mozilla::gfx::IntRect IntRect;
   typedef mozilla::gfx::SourceSurface SourceSurface;
-  typedef mozilla::gfx::DrawTarget DrawTarget;
   typedef mozilla::gfx::FilterPrimitiveDescription FilterPrimitiveDescription;
 
 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(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(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.
+   * @param aFilter The SVG reference filter to process.
+   * @param aTargetFrame The frame of the filtered element under consideration.
+   * @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.
    */
-  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(nsIFrame *aFilteredFrame,
-                                    const gfxRect *aOverrideBBox = nullptr,
-                                    const nsRect *aPreFilterBounds = nullptr);
-
-  /**
-   * @param aTargetFrame The frame of the filtered element under consideration.
-   * @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,
-                      nsSVGFilterPaintCallback *aPaintCallback,
-                      const nsRect *aPostFilterDirtyRect = nullptr,
-                      const nsRect *aPreFilterDirtyRect = nullptr,
-                      const nsRect *aOverridePreFilterVisualOverflowRect = nullptr,
-                      const gfxRect *aOverrideBBox = nullptr,
-                      nsIFrame* aTransformRoot = nullptr);
+  nsSVGFilterInstance(const nsStyleFilter& aFilter,
+                      nsIFrame *aTargetFrame,
+                      const gfxRect& aTargetBBox);
 
   /**
    * Returns true if the filter instance was created successfully.
    */
   bool IsInitialized() const { return mInitialized; }
 
   /**
+   * Iterates through the <filter> element's primitive elements, creating a
+   * FilterPrimitiveDescription for each one. Appends the new
+   * FilterPrimitiveDescription(s) to the aPrimitiveDescrs list. Also, appends
+   * new images from feImage filter primitive elements to the aInputImages list.
+   */
+  nsresult BuildPrimitives(nsTArray<FilterPrimitiveDescription>& aPrimitiveDescrs,
+                           nsTArray<mozilla::RefPtr<SourceSurface>>& aInputImages);
+
+  /**
    * Returns the user specified "filter region", in the filtered element's user
    * space, after it has been adjusted out (if necessary) so that its edges
    * coincide with pixel boundaries of the offscreen surface into which the
    * filtered output would/will be painted.
    */
   gfxRect GetFilterRegion() const { return mFilterRegion; }
 
   /**
    * Returns the size of the user specified "filter region", in filter space.
    * The size will be {filterRes.x by filterRes.y}, whether the user specified
    * the filter's filterRes attribute explicitly, or the implementation chose
-   * the filterRes values. (The top-left of the filter region is the origin of
-   * filter space, which is why this method returns an nsIntSize and not an
-   * nsIntRect.)
-   */
-  uint32_t GetFilterResX() const { return mFilterSpaceBounds.width; }
-  uint32_t GetFilterResY() const { return mFilterSpaceBounds.height; }
-
-  /**
-   * Draws the filter output into aContext. The area that
-   * needs to be painted must have been specified before calling this method
-   * by passing it as the aPostFilterDirtyRect argument to the
-   * nsSVGFilterInstance constructor.
+   * the filterRes values.
    */
-  nsresult Render(gfxContext* aContext);
-
-  /**
-   * Sets the aPostFilterDirtyRect outparam to the post-filter bounds in frame
-   * space of the area that would be dirtied by mTargetFrame when a given
-   * pre-filter area of mTargetFrame is dirtied. The pre-filter area must have
-   * been specified before calling this method by passing it as the
-   * aPreFilterDirtyRect argument to the nsSVGFilterInstance constructor.
-   */
-  nsresult ComputePostFilterDirtyRect(nsRect* aPostFilterDirtyRect);
-
-  /**
-   * Sets the aPostFilterExtents outparam to the post-filter bounds in frame
-   * space for the whole filter output. This is not necessarily equivalent to
-   * the area that would be dirtied in the result when the entire pre-filter
-   * area is dirtied, because some filter primitives can generate output
-   * without any input.
-   */
-  nsresult ComputePostFilterExtents(nsRect* aPostFilterExtents);
-
-  /**
-   * Sets the aDirty outparam to the pre-filter bounds in frame space of the
-   * area of mTargetFrame that is needed in order to paint the filtered output
-   * for a given post-filter dirtied area. The post-filter area must have been
-   * specified before calling this method by passing it as the aPostFilterDirtyRect
-   * argument to the nsSVGFilterInstance constructor.
-   */
-  nsresult ComputeSourceNeededRect(nsRect* aDirty);
+  nsIntRect GetFilterSpaceBounds() const { return mFilterSpaceBounds; }
 
   float GetPrimitiveNumber(uint8_t aCtxType, const nsSVGNumber2 *aNumber) const
   {
     return GetPrimitiveNumber(aCtxType, aNumber->GetAnimValue());
   }
   float GetPrimitiveNumber(uint8_t aCtxType, const nsSVGNumberPair *aNumberPair,
                            nsSVGNumberPair::PairIndex aIndex) const
   {
@@ -203,205 +98,84 @@ public:
   Point3D ConvertLocation(const Point3D& aPoint) const;
 
   /**
    * Returns the transform from the filtered element's user space to filter
    * space. This will be a simple translation and/or scale.
    */
   gfxMatrix GetUserSpaceToFilterSpaceTransform() const;
 
-  /**
-   * Returns the transform from filter space to outer-<svg> device space.
-   */
-  gfxMatrix GetFilterSpaceToDeviceSpaceTransform() const {
-    return mFilterSpaceToDeviceSpaceTransform;
-  }
-
-  gfxPoint FilterSpaceToUserSpace(const gfxPoint& aPt) const;
-
-  /**
-   * Returns the transform from filter space to frame space, in CSS px. This
-   * transform does not transform to frame space in its normal app units, since
-   * app units are ints, requiring appropriate rounding which can't be done by
-   * a transform matrix. Callers have to do that themselves as appropriate for
-   * their needs.
-   */
-  gfxMatrix GetFilterSpaceToFrameSpaceInCSSPxTransform() const {
-    return mFilterSpaceToFrameSpaceInCSSPxTransform;
-  }
-
-  int32_t AppUnitsPerCSSPixel() const { return mAppUnitsPerCSSPx; }
-
 private:
-  struct SourceInfo {
-    // Specifies which parts of the source need to be rendered.
-    // Set by ComputeNeededBoxes().
-    nsIntRect mNeededBounds;
-
-    // The surface that contains the input rendering.
-    // Set by BuildSourceImage / BuildSourcePaint.
-    mozilla::RefPtr<SourceSurface> mSourceSurface;
-
-    // The position and size of mSourceSurface in filter space.
-    // Set by BuildSourceImage / BuildSourcePaint.
-    IntRect mSurfaceRect;
-  };
-
-  /**
-   * Creates a SourceSurface for either the FillPaint or StrokePaint graph
-   * nodes
-   */
-  nsresult BuildSourcePaint(SourceInfo *aPrimitive,
-                            gfxASurface* aTargetSurface,
-                            DrawTarget* aTargetDT);
-
-  /**
-   * Creates a SourceSurface for either the FillPaint and StrokePaint graph
-   * nodes, fills its contents and assigns it to mFillPaint.mSourceSurface and
-   * mStrokePaint.mSourceSurface respectively.
-   */
-  nsresult BuildSourcePaints(gfxASurface* aTargetSurface,
-                             DrawTarget* aTargetDT);
-
-  /**
-   * 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
-   * the filter sources, based on the value of mPostFilterDirtyRect.
-   * This sets mNeededBounds on the corresponding SourceInfo structs.
-   */
-   void ComputeNeededBoxes();
-
-  /**
    * Computes the filter primitive subregion for the given primitive.
    */
   IntRect ComputeFilterPrimitiveSubregion(nsSVGFE* aFilterElement,
+                                          const nsTArray<FilterPrimitiveDescription>& aPrimitiveDescrs,
                                           const nsTArray<int32_t>& aInputIndices);
 
   /**
    * Takes the input indices of a filter primitive and returns for each input
    * whether the input's output is tainted.
    */
-  void GetInputsAreTainted(const nsTArray<int32_t>& aInputIndices,
+  void GetInputsAreTainted(const nsTArray<FilterPrimitiveDescription>& aPrimitiveDescrs,
+                           const nsTArray<int32_t>& aInputIndices,
                            nsTArray<bool>& aOutInputsAreTainted);
 
   /**
    * Scales a numeric filter primitive length in the X, Y or "XY" directions
    * into a length in filter space (no offset is applied).
    */
   float GetPrimitiveNumber(uint8_t aCtxType, float aValue) const;
 
   gfxRect UserSpaceToFilterSpace(const gfxRect& aUserSpace) const;
 
   /**
-   * Converts an nsRect that is relative to a filtered frame's origin (i.e. the
-   * top-left corner of its border box) into filter space.
-   * Returns the entire filter region if aRect is null, or if the result is too
-   * large to be stored in an nsIntRect.
-   */
-  nsIntRect FrameSpaceToFilterSpace(const nsRect* aRect) const;
-  nsRect FilterSpaceToFrameSpace(const nsIntRect& aRect) const;
-
-  /**
    * Returns the transform from frame space to the coordinate space that
    * GetCanvasTM transforms to. "Frame space" is the origin of a frame, aka the
    * top-left corner of its border box, aka the top left corner of its mRect.
    */
   gfxMatrix GetUserSpaceToFrameSpaceInCSSPxTransform() const;
 
   /**
+   * The SVG reference filter originally from the style system.
+   */
+  const nsStyleFilter mFilter;
+
+  /**
    * 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;
-
-  /**
-   * Transform rects between filter space and frame space in CSS pixels.
-   */
-  gfxMatrix               mFilterSpaceToFrameSpaceInCSSPxTransform;
-  gfxMatrix               mFrameSpaceInCSSPxToFilterSpaceTransform;
-
-  /**
    * The "filter region", in the filtered element's user space.
    */
   gfxRect                 mFilterRegion;
   nsIntRect               mFilterSpaceBounds;
 
   /**
-   * Pre-filter paint bounds of the element that is being filtered, in filter
-   * space.
-   */
-  nsIntRect               mTargetBounds;
-
-  /**
-   * If set, this is the filter space bounds of the outer-<svg> device space
-   * bounds of the dirty area that needs to be repainted. (As bounds-of-bounds,
-   * this may be a fair bit bigger than we actually need, unfortunately.)
-   */
-  nsIntRect               mPostFilterDirtyRect;
-
-  /**
-   * If set, this is the filter space bounds of the outer-<svg> device bounds
-   * of the pre-filter area of the filtered element that changed. (As
-   * bounds-of-bounds, this may be a fair bit bigger than we actually need,
-   * unfortunately.)
-   */
-  nsIntRect               mPreFilterDirtyRect;
-
-  /**
    * The 'primitiveUnits' attribute value (objectBoundingBox or userSpaceOnUse).
    */
   uint16_t                mPrimitiveUnits;
 
-  SourceInfo              mSourceGraphic;
-  SourceInfo              mFillPaint;
-  SourceInfo              mStrokePaint;
-  nsIFrame*               mTransformRoot;
-  nsTArray<mozilla::RefPtr<SourceSurface>> mInputImages;
-  nsTArray<FilterPrimitiveDescription> mPrimitiveDescriptions;
-  int32_t                 mAppUnitsPerCSSPx;
   bool                    mInitialized;
 };
 
 #endif
--- a/layout/svg/nsSVGIntegrationUtils.cpp
+++ b/layout/svg/nsSVGIntegrationUtils.cpp
@@ -5,22 +5,22 @@
 
 // Main header first:
 #include "nsSVGIntegrationUtils.h"
 
 // Keep others in (case-insensitive) order:
 #include "gfxDrawable.h"
 #include "nsCSSAnonBoxes.h"
 #include "nsDisplayList.h"
+#include "nsFilterInstance.h"
 #include "nsLayoutUtils.h"
 #include "nsRenderingContext.h"
 #include "nsSVGClipPathFrame.h"
 #include "nsSVGEffects.h"
 #include "nsSVGElement.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"
 
@@ -270,17 +270,17 @@ nsRect
     nsLayoutUtils::RectToGfxRect(
       GetPreEffectsVisualOverflowUnion(firstFrame, aFrame,
                                        aPreEffectsOverflowRect,
                                        firstFrameToUserSpace),
       aFrame->PresContext()->AppUnitsPerCSSPixel());
   overrideBBox.RoundOut();
 
   nsRect overflowRect =
-    nsSVGFilterInstance::GetPostFilterBounds(firstFrame, &overrideBBox);
+    nsFilterInstance::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,
@@ -315,17 +315,17 @@ 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(firstFrame,
+  nsRect result = nsFilterInstance::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)
@@ -340,18 +340,18 @@ nsSVGIntegrationUtils::GetRequiredSource
   }
   
   // 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(firstFrame,
-    postEffectsRect) - toUserSpace;
+  return nsFilterInstance::GetPreFilterNeededArea(firstFrame, postEffectsRect)
+    - toUserSpace;
 }
 
 bool
 nsSVGIntegrationUtils::HitTestFrameForEffects(nsIFrame* aFrame, const nsPoint& aPt)
 {
   nsIFrame* firstFrame =
     nsLayoutUtils::FirstContinuationOrIBSplitSibling(aFrame);
   // Convert aPt to user space:
@@ -505,17 +505,17 @@ nsSVGIntegrationUtils::PaintFramesWithEf
     clipPathFrame->ClipPaint(aCtx, aFrame, cssPxToDevPxMatrix);
   }
 
   /* Paint the child */
   if (effectProperties.HasValidFilter()) {
     RegularFramePaintCallback callback(aBuilder, aLayerManager,
                                        offsetWithoutSVGGeomFramePos);
     nsRect dirtyRect = aDirtyRect - offset;
-    nsSVGFilterInstance::PaintFilteredFrame(aCtx, aFrame, &callback, &dirtyRect);
+    nsFilterInstance::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
@@ -15,32 +15,32 @@
 #include "gfxMatrix.h"
 #include "gfxPlatform.h"
 #include "gfxRect.h"
 #include "gfxUtils.h"
 #include "mozilla/gfx/2D.h"
 #include "mozilla/Preferences.h"
 #include "nsCSSFrameConstructor.h"
 #include "nsDisplayList.h"
+#include "nsFilterInstance.h"
 #include "nsFrameList.h"
 #include "nsGkAtoms.h"
 #include "nsIContent.h"
 #include "nsIDocument.h"
 #include "nsIFrame.h"
 #include "nsINameSpaceManager.h"
 #include "nsIPresShell.h"
 #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 "nsSVGFilterInstance.h"
 #include "nsSVGFilterPaintCallback.h"
 #include "nsSVGForeignObjectFrame.h"
 #include "gfxSVGGlyphs.h"
 #include "nsSVGInnerSVGFrame.h"
 #include "nsSVGIntegrationUtils.h"
 #include "nsSVGLength2.h"
 #include "nsSVGMaskFrame.h"
 #include "nsSVGOuterSVGFrame.h"
@@ -159,17 +159,17 @@ nsSVGUtils::GetPostFilterVisualOverflowR
   NS_ABORT_IF_FALSE(aFrame->GetStateBits() & NS_FRAME_SVG_LAYOUT,
                     "Called on invalid frame type");
 
   nsSVGFilterProperty *property = nsSVGEffects::GetFilterProperty(aFrame);
   if (!property || !property->ReferencesValidResources()) {
     return aPreFilterRect;
   }
 
-  return nsSVGFilterInstance::GetPostFilterBounds(aFrame, nullptr, &aPreFilterRect);
+  return nsFilterInstance::GetPostFilterBounds(aFrame, nullptr, &aPreFilterRect);
 }
 
 bool
 nsSVGUtils::OuterSVGIsCallingReflowSVG(nsIFrame *aFrame)
 {
   return GetOuterSVGFrame(aFrame)->IsCallingReflowSVG();
 }
 
@@ -616,18 +616,18 @@ nsSVGUtils::PaintFrameWithEffects(nsRend
                                       aDirtyRect->width, aDirtyRect->height));
       tmpDirtyRect =
         nsLayoutUtils::RoundGfxRectToAppRect(
           dirtyBounds, aFrame->PresContext()->AppUnitsPerCSSPixel()) -
         aFrame->GetPosition();
       dirtyRect = &tmpDirtyRect;
     }
     SVGPaintCallback paintCallback;
-    nsSVGFilterInstance::PaintFilteredFrame(aContext, aFrame, &paintCallback,
-                                            dirtyRect, aTransformRoot);
+    nsFilterInstance::PaintFilteredFrame(aContext, aFrame, &paintCallback,
+                                         dirtyRect, aTransformRoot);
   } else {
     svgChildFrame->PaintSVG(aContext, aDirtyRect, aTransformRoot);
   }
 
   if (clipPathFrame && isTrivialClip) {
     gfx->Restore();
   }