Bug 1522021. Propagate input size to CreateSimilarDrawTarget for filters. r=mstange
authorJeff Muizelaar <jrmuizel@gmail.com>
Sun, 27 Jan 2019 23:36:04 +0000
changeset 455591 3c9634352cd66e1debc3beeedd53130d16474db2
parent 455590 747303cab27a597bbfdffd2c1db157f5d65c4f0f
child 455592 2bad68e1de18761ea1c256781269f98c931b3a80
push id76867
push userjmuizelaar@mozilla.com
push dateSun, 27 Jan 2019 23:37:22 +0000
treeherderautoland@3c9634352cd6 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmstange
bugs1522021
milestone66.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 1522021. Propagate input size to CreateSimilarDrawTarget for filters. r=mstange This lets us avoid drawing the complete input for ever tile when drawing filters into a tile. Differential Revision: https://phabricator.services.mozilla.com/D17686
gfx/2d/2D.h
gfx/2d/DrawTargetRecording.cpp
gfx/2d/DrawTargetRecording.h
gfx/2d/FilterNodeSoftware.cpp
gfx/2d/FilterNodeSoftware.h
gfx/2d/Filters.h
gfx/2d/RecordedEvent.h
gfx/2d/RecordedEventImpl.h
gfx/src/FilterSupport.cpp
gfx/src/FilterSupport.h
layout/reftests/svg/filters/reftest.list
layout/svg/nsFilterInstance.cpp
layout/svg/nsFilterInstance.h
--- a/gfx/2d/2D.h
+++ b/gfx/2d/2D.h
@@ -1306,16 +1306,30 @@ class DrawTarget : public external::Atom
   /**
    * Create a DrawTarget whose snapshot is optimized for use with this
    * DrawTarget.
    */
   virtual already_AddRefed<DrawTarget> CreateSimilarDrawTarget(
       const IntSize &aSize, SurfaceFormat aFormat) const = 0;
 
   /**
+   * Create a DrawTarget whose snapshot is optimized for use with this
+   * DrawTarget and aFilter.
+   * @param aSource is the FilterNode that that will be attached to this
+   * surface.
+   * @param aSourceRect is the source rect that will be passed to DrawFilter
+   * @param aDestPoint is the dest point that will be passed to DrawFilter.
+   */
+  virtual already_AddRefed<DrawTarget> CreateSimilarDrawTargetForFilter(
+      const IntSize &aSize, SurfaceFormat aFormat, FilterNode *aFilter,
+      FilterNode *aSource, const Rect &aSourceRect, const Point &aDestPoint) {
+    return CreateSimilarDrawTarget(aSize, aFormat);
+  }
+
+  /**
    * Returns false if CreateSimilarDrawTarget would return null with the same
    * parameters. May return true even in cases where CreateSimilarDrawTarget
    * return null (i.e. this function returning false has meaning, but returning
    * true doesn't guarantee anything).
    */
   virtual bool CanCreateSimilarDrawTarget(const IntSize &aSize,
                                           SurfaceFormat aFormat) const {
     return true;
--- a/gfx/2d/DrawTargetRecording.cpp
+++ b/gfx/2d/DrawTargetRecording.cpp
@@ -549,16 +549,35 @@ RefPtr<DrawTarget> DrawTargetRecording::
     // See CreateSimilarDrawTarget
     MOZ_CRASH(
         "Content-process DrawTargetRecording can't create requested clipped "
         "drawtarget");
   }
   return similarDT;
 }
 
+already_AddRefed<DrawTarget>
+DrawTargetRecording::CreateSimilarDrawTargetForFilter(
+    const IntSize &aMaxSize, SurfaceFormat aFormat, FilterNode *aFilter,
+    FilterNode *aSource, const Rect &aSourceRect, const Point &aDestPoint) {
+  RefPtr<DrawTarget> similarDT;
+  if (mFinalDT->CanCreateSimilarDrawTarget(aMaxSize, aFormat)) {
+    similarDT = new DrawTargetRecording(this, aMaxSize, aFormat);
+    mRecorder->RecordEvent(RecordedCreateDrawTargetForFilter(
+        this, similarDT.get(), aMaxSize, aFormat, aFilter, aSource, aSourceRect,
+        aDestPoint));
+  } else if (XRE_IsContentProcess()) {
+    // See CreateSimilarDrawTarget
+    MOZ_CRASH(
+        "Content-process DrawTargetRecording can't create requested clipped "
+        "drawtarget");
+  }
+  return similarDT.forget();
+}
+
 already_AddRefed<PathBuilder> DrawTargetRecording::CreatePathBuilder(
     FillRule aFillRule) const {
   RefPtr<PathBuilder> builder = mFinalDT->CreatePathBuilder(aFillRule);
   return MakeAndAddRef<PathBuilderRecording>(builder, aFillRule);
 }
 
 already_AddRefed<GradientStops> DrawTargetRecording::CreateGradientStops(
     GradientStop *aStops, uint32_t aNumStops, ExtendMode aExtendMode) const {
--- a/gfx/2d/DrawTargetRecording.h
+++ b/gfx/2d/DrawTargetRecording.h
@@ -299,16 +299,20 @@ class DrawTargetRecording : public DrawT
   /**
    * Create a similar DrawTarget whose requested size may be clipped based
    * on this DrawTarget's rect transformed to the new target's space.
    */
   virtual RefPtr<DrawTarget> CreateClippedDrawTarget(
       const IntSize &aMaxSize, const Matrix &aTransform,
       SurfaceFormat aFormat) const override;
 
+  virtual already_AddRefed<DrawTarget> CreateSimilarDrawTargetForFilter(
+      const IntSize &aSize, SurfaceFormat aFormat, FilterNode *aFilter,
+      FilterNode *aSource, const Rect &aSourceRect,
+      const Point &aDestPoint) override;
   /*
    * Create a path builder with the specified fillmode.
    *
    * We need the fill mode up front because of Direct2D.
    * ID2D1SimplifiedGeometrySink requires the fill mode
    * to be set before calling BeginFigure().
    */
   virtual already_AddRefed<PathBuilder> CreatePathBuilder(
--- a/gfx/2d/FilterNodeSoftware.cpp
+++ b/gfx/2d/FilterNodeSoftware.cpp
@@ -660,16 +660,43 @@ void FilterNodeSoftware::RequestRect(con
     // in the number of primitives, e.g. if each primitive takes the
     // previous primitive as its two inputs.
     return;
   }
   mRequestedRect = mRequestedRect.Union(aRect);
   RequestFromInputsForRect(aRect);
 }
 
+IntRect FilterNodeSoftware::MapInputRectToSource(uint32_t aInputEnumIndex,
+                                                 const IntRect &aRect,
+                                                 const IntRect &aMax,
+                                                 FilterNode *aSourceNode) {
+  int32_t inputIndex = InputIndex(aInputEnumIndex);
+  if (inputIndex < 0) {
+    gfxDevCrash(LogReason::FilterInputError)
+        << "Invalid input " << inputIndex << " vs. " << NumberOfSetInputs();
+    return aMax;
+  }
+  if ((uint32_t)inputIndex < NumberOfSetInputs()) {
+    RefPtr<FilterNodeSoftware> filter = mInputFilters[inputIndex];
+    // If we have any input filters call into them to do the mapping,
+    // otherwise we can assume an input surface will be used
+    // and just return aRect.
+    if (filter) {
+      return filter->MapRectToSource(aRect, aMax, aSourceNode);
+    }
+  }
+  // We have an input surface instead of a filter
+  // so check if we're the target node.
+  if (this == aSourceNode) {
+    return aRect;
+  }
+  return IntRect();
+}
+
 void FilterNodeSoftware::RequestInputRect(uint32_t aInputEnumIndex,
                                           const IntRect &aRect) {
   if (aRect.Overflows()) {
     return;
   }
 
   int32_t inputIndex = InputIndex(aInputEnumIndex);
   if (inputIndex < 0 || (uint32_t)inputIndex >= NumberOfSetInputs()) {
@@ -1046,16 +1073,24 @@ already_AddRefed<DataSourceSurface> Filt
   return target.forget();
 }
 
 void FilterNodeBlendSoftware::RequestFromInputsForRect(const IntRect &aRect) {
   RequestInputRect(IN_BLEND_IN, aRect);
   RequestInputRect(IN_BLEND_IN2, aRect);
 }
 
+IntRect FilterNodeBlendSoftware::MapRectToSource(const IntRect &aRect,
+                                                 const IntRect &aMax,
+                                                 FilterNode *aSourceNode) {
+  IntRect result = MapInputRectToSource(IN_BLEND_IN, aRect, aMax, aSourceNode);
+  result.OrWith(MapInputRectToSource(IN_BLEND_IN2, aRect, aMax, aSourceNode));
+  return result;
+}
+
 IntRect FilterNodeBlendSoftware::GetOutputRectInRect(const IntRect &aRect) {
   return GetInputRectInRect(IN_BLEND_IN, aRect)
       .Union(GetInputRectInRect(IN_BLEND_IN2, aRect))
       .Intersect(aRect);
 }
 
 FilterNodeTransformSoftware::FilterNodeTransformSoftware()
     : mSamplingFilter(SamplingFilter::GOOD) {}
@@ -1098,16 +1133,38 @@ IntRect FilterNodeTransformSoftware::Sou
   neededRect.RoundOut();
   IntRect neededIntRect;
   if (!neededRect.ToIntRect(&neededIntRect)) {
     return IntRect();
   }
   return GetInputRectInRect(IN_TRANSFORM_IN, neededIntRect);
 }
 
+IntRect FilterNodeTransformSoftware::MapRectToSource(const IntRect &aRect,
+                                                     const IntRect &aMax,
+                                                     FilterNode *aSourceNode) {
+  if (aRect.IsEmpty()) {
+    return IntRect();
+  }
+
+  Matrix inverted(mMatrix);
+  if (!inverted.Invert()) {
+    return aMax;
+  }
+
+  Rect neededRect = inverted.TransformBounds(Rect(aRect));
+  neededRect.RoundOut();
+  IntRect neededIntRect;
+  if (!neededRect.ToIntRect(&neededIntRect)) {
+    return aMax;
+  }
+  return MapInputRectToSource(IN_TRANSFORM_IN, neededIntRect, aMax,
+                              aSourceNode);
+}
+
 already_AddRefed<DataSourceSurface> FilterNodeTransformSoftware::Render(
     const IntRect &aRect) {
   IntRect srcRect = SourceRectForOutputRect(aRect);
 
   RefPtr<DataSourceSurface> input =
       GetInputDataSourceSurface(IN_TRANSFORM_IN, srcRect);
 
   if (!input) {
@@ -1453,16 +1510,21 @@ already_AddRefed<DataSourceSurface> Filt
   return result.forget();
 }
 
 void FilterNodeColorMatrixSoftware::RequestFromInputsForRect(
     const IntRect &aRect) {
   RequestInputRect(IN_COLOR_MATRIX_IN, aRect);
 }
 
+IntRect FilterNodeColorMatrixSoftware::MapRectToSource(
+    const IntRect &aRect, const IntRect &aMax, FilterNode *aSourceNode) {
+  return MapInputRectToSource(IN_COLOR_MATRIX_IN, aRect, aMax, aSourceNode);
+}
+
 IntRect FilterNodeColorMatrixSoftware::GetOutputRectInRect(
     const IntRect &aRect) {
   if (mMatrix._54 > 0.0f) {
     return aRect;
   }
   return GetInputRectInRect(IN_COLOR_MATRIX_IN, aRect);
 }
 
@@ -1541,16 +1603,22 @@ already_AddRefed<DataSourceSurface> Filt
 
 // Override GetOutput to get around caching. Rendering simple floods is
 // comparatively fast.
 already_AddRefed<DataSourceSurface> FilterNodeFloodSoftware::GetOutput(
     const IntRect &aRect) {
   return Render(aRect);
 }
 
+IntRect FilterNodeFloodSoftware::MapRectToSource(const IntRect &aRect,
+                                                 const IntRect &aMax,
+                                                 FilterNode *aSourceNode) {
+  return IntRect();
+}
+
 IntRect FilterNodeFloodSoftware::GetOutputRectInRect(const IntRect &aRect) {
   if (mColor.a == 0.0f) {
     return IntRect();
   }
   return aRect;
 }
 
 int32_t FilterNodeTileSoftware::InputIndex(uint32_t aInputEnumIndex) {
@@ -1811,16 +1879,21 @@ already_AddRefed<DataSourceSurface> Filt
   return target.forget();
 }
 
 void FilterNodeComponentTransferSoftware::RequestFromInputsForRect(
     const IntRect &aRect) {
   RequestInputRect(IN_TRANSFER_IN, aRect);
 }
 
+IntRect FilterNodeComponentTransferSoftware::MapRectToSource(
+    const IntRect &aRect, const IntRect &aMax, FilterNode *aSourceNode) {
+  return MapInputRectToSource(IN_TRANSFER_IN, aRect, aMax, aSourceNode);
+}
+
 IntRect FilterNodeComponentTransferSoftware::GetOutputRectInRect(
     const IntRect &aRect) {
   if (mDisableA) {
     return GetInputRectInRect(IN_TRANSFER_IN, aRect);
   }
   return aRect;
 }
 
@@ -2456,16 +2529,22 @@ already_AddRefed<DataSourceSurface> Filt
   return target.forget();
 }
 
 void FilterNodeConvolveMatrixSoftware::RequestFromInputsForRect(
     const IntRect &aRect) {
   RequestInputRect(IN_CONVOLVE_MATRIX_IN, InflatedSourceRect(aRect));
 }
 
+IntRect FilterNodeConvolveMatrixSoftware::MapRectToSource(
+    const IntRect &aRect, const IntRect &aMax, FilterNode *aSourceNode) {
+  return MapInputRectToSource(IN_CONVOLVE_MATRIX_IN, InflatedSourceRect(aRect),
+                              aMax, aSourceNode);
+}
+
 IntRect FilterNodeConvolveMatrixSoftware::InflatedSourceRect(
     const IntRect &aDestRect) {
   if (aDestRect.IsEmpty()) {
     return IntRect();
   }
 
   IntMargin margin;
   margin.left = ceil(mTarget.x * mKernelUnitLength.width);
@@ -2606,16 +2685,26 @@ already_AddRefed<DataSourceSurface> Filt
 }
 
 void FilterNodeDisplacementMapSoftware::RequestFromInputsForRect(
     const IntRect &aRect) {
   RequestInputRect(IN_DISPLACEMENT_MAP_IN, InflatedSourceOrDestRect(aRect));
   RequestInputRect(IN_DISPLACEMENT_MAP_IN2, aRect);
 }
 
+IntRect FilterNodeDisplacementMapSoftware::MapRectToSource(
+    const IntRect &aRect, const IntRect &aMax, FilterNode *aSourceNode) {
+  IntRect result =
+      MapInputRectToSource(IN_DISPLACEMENT_MAP_IN,
+                           InflatedSourceOrDestRect(aRect), aMax, aSourceNode);
+  result.OrWith(
+      MapInputRectToSource(IN_DISPLACEMENT_MAP_IN2, aRect, aMax, aSourceNode));
+  return result;
+}
+
 IntRect FilterNodeDisplacementMapSoftware::InflatedSourceOrDestRect(
     const IntRect &aDestOrSourceRect) {
   IntRect sourceOrDestRect = aDestOrSourceRect;
   sourceOrDestRect.Inflate(ceil(fabs(mScale) / 2));
   return sourceOrDestRect;
 }
 
 IntRect FilterNodeDisplacementMapSoftware::GetOutputRectInRect(
@@ -2694,16 +2783,22 @@ already_AddRefed<DataSourceSurface> Filt
       mStitchable, Rect(mRenderRect));
 }
 
 IntRect FilterNodeTurbulenceSoftware::GetOutputRectInRect(
     const IntRect &aRect) {
   return aRect.Intersect(mRenderRect);
 }
 
+IntRect FilterNodeTurbulenceSoftware::MapRectToSource(const IntRect &aRect,
+                                                      const IntRect &aMax,
+                                                      FilterNode *aSourceNode) {
+  return IntRect();
+}
+
 FilterNodeArithmeticCombineSoftware::FilterNodeArithmeticCombineSoftware()
     : mK1(0), mK2(0), mK3(0), mK4(0) {}
 
 int32_t FilterNodeArithmeticCombineSoftware::InputIndex(
     uint32_t aInputEnumIndex) {
   switch (aInputEnumIndex) {
     case IN_ARITHMETIC_COMBINE_IN:
       return 0;
@@ -2757,16 +2852,25 @@ already_AddRefed<DataSourceSurface> Filt
 }
 
 void FilterNodeArithmeticCombineSoftware::RequestFromInputsForRect(
     const IntRect &aRect) {
   RequestInputRect(IN_ARITHMETIC_COMBINE_IN, aRect);
   RequestInputRect(IN_ARITHMETIC_COMBINE_IN2, aRect);
 }
 
+IntRect FilterNodeArithmeticCombineSoftware::MapRectToSource(
+    const IntRect &aRect, const IntRect &aMax, FilterNode *aSourceNode) {
+  IntRect result =
+      MapInputRectToSource(IN_ARITHMETIC_COMBINE_IN, aRect, aMax, aSourceNode);
+  result.OrWith(MapInputRectToSource(IN_ARITHMETIC_COMBINE_IN2, aRect, aMax,
+                                     aSourceNode));
+  return result;
+}
+
 IntRect FilterNodeArithmeticCombineSoftware::GetOutputRectInRect(
     const IntRect &aRect) {
   if (mK4 > 0.0f) {
     return aRect;
   }
   IntRect rectFrom1 =
       GetInputRectInRect(IN_ARITHMETIC_COMBINE_IN, aRect).Intersect(aRect);
   IntRect rectFrom2 =
@@ -2843,16 +2947,27 @@ already_AddRefed<DataSourceSurface> Filt
 
 void FilterNodeCompositeSoftware::RequestFromInputsForRect(
     const IntRect &aRect) {
   for (size_t inputIndex = 0; inputIndex < NumberOfSetInputs(); inputIndex++) {
     RequestInputRect(IN_COMPOSITE_IN_START + inputIndex, aRect);
   }
 }
 
+IntRect FilterNodeCompositeSoftware::MapRectToSource(const IntRect &aRect,
+                                                     const IntRect &aMax,
+                                                     FilterNode *aSourceNode) {
+  IntRect result;
+  for (size_t inputIndex = 0; inputIndex < NumberOfSetInputs(); inputIndex++) {
+    result.OrWith(MapInputRectToSource(IN_COMPOSITE_IN_START + inputIndex,
+                                       aRect, aMax, aSourceNode));
+  }
+  return result;
+}
+
 IntRect FilterNodeCompositeSoftware::GetOutputRectInRect(const IntRect &aRect) {
   IntRect rect;
   for (size_t inputIndex = 0; inputIndex < NumberOfSetInputs(); inputIndex++) {
     IntRect inputRect =
         GetInputRectInRect(IN_COMPOSITE_IN_START + inputIndex, aRect);
     if (mOperator == COMPOSITE_OPERATOR_IN && inputIndex > 0) {
       rect = rect.Intersect(inputRect);
     } else {
@@ -2940,16 +3055,23 @@ already_AddRefed<DataSourceSurface> Filt
 
   return GetDataSurfaceInRect(target, srcRect, aRect, EDGE_MODE_NONE);
 }
 
 void FilterNodeBlurXYSoftware::RequestFromInputsForRect(const IntRect &aRect) {
   RequestInputRect(IN_GAUSSIAN_BLUR_IN, InflatedSourceOrDestRect(aRect));
 }
 
+IntRect FilterNodeBlurXYSoftware::MapRectToSource(const IntRect &aRect,
+                                                  const IntRect &aMax,
+                                                  FilterNode *aSourceNode) {
+  return MapInputRectToSource(
+      IN_GAUSSIAN_BLUR_IN, InflatedSourceOrDestRect(aRect), aMax, aSourceNode);
+}
+
 IntRect FilterNodeBlurXYSoftware::InflatedSourceOrDestRect(
     const IntRect &aDestRect) {
   Size sigmaXY = StdDeviationXY();
   IntSize d =
       AlphaBoxBlur::CalculateBlurRadius(Point(sigmaXY.width, sigmaXY.height));
   IntRect srcRect = aDestRect;
   srcRect.Inflate(d);
   return srcRect;
@@ -3042,16 +3164,23 @@ already_AddRefed<DataSourceSurface> Filt
     const IntRect &aRect) {
   return GetInputDataSourceSurface(IN_CROP_IN, aRect.Intersect(mCropRect));
 }
 
 void FilterNodeCropSoftware::RequestFromInputsForRect(const IntRect &aRect) {
   RequestInputRect(IN_CROP_IN, aRect.Intersect(mCropRect));
 }
 
+IntRect FilterNodeCropSoftware::MapRectToSource(const IntRect &aRect,
+                                                const IntRect &aMax,
+                                                FilterNode *aSourceNode) {
+  return MapInputRectToSource(IN_CROP_IN, aRect.Intersect(mCropRect), aMax,
+                              aSourceNode);
+}
+
 IntRect FilterNodeCropSoftware::GetOutputRectInRect(const IntRect &aRect) {
   return GetInputRectInRect(IN_CROP_IN, aRect).Intersect(mCropRect);
 }
 
 int32_t FilterNodePremultiplySoftware::InputIndex(uint32_t aInputEnumIndex) {
   switch (aInputEnumIndex) {
     case IN_PREMULTIPLY_IN:
       return 0;
@@ -3067,16 +3196,21 @@ already_AddRefed<DataSourceSurface> Filt
   return input ? Premultiply(input) : nullptr;
 }
 
 void FilterNodePremultiplySoftware::RequestFromInputsForRect(
     const IntRect &aRect) {
   RequestInputRect(IN_PREMULTIPLY_IN, aRect);
 }
 
+IntRect FilterNodePremultiplySoftware::MapRectToSource(
+    const IntRect &aRect, const IntRect &aMax, FilterNode *aSourceNode) {
+  return MapInputRectToSource(IN_PREMULTIPLY_IN, aRect, aMax, aSourceNode);
+}
+
 IntRect FilterNodePremultiplySoftware::GetOutputRectInRect(
     const IntRect &aRect) {
   return GetInputRectInRect(IN_PREMULTIPLY_IN, aRect);
 }
 
 int32_t FilterNodeUnpremultiplySoftware::InputIndex(uint32_t aInputEnumIndex) {
   switch (aInputEnumIndex) {
     case IN_UNPREMULTIPLY_IN:
@@ -3093,16 +3227,21 @@ already_AddRefed<DataSourceSurface> Filt
   return input ? Unpremultiply(input) : nullptr;
 }
 
 void FilterNodeUnpremultiplySoftware::RequestFromInputsForRect(
     const IntRect &aRect) {
   RequestInputRect(IN_UNPREMULTIPLY_IN, aRect);
 }
 
+IntRect FilterNodeUnpremultiplySoftware::MapRectToSource(
+    const IntRect &aRect, const IntRect &aMax, FilterNode *aSourceNode) {
+  return MapInputRectToSource(IN_UNPREMULTIPLY_IN, aRect, aMax, aSourceNode);
+}
+
 IntRect FilterNodeUnpremultiplySoftware::GetOutputRectInRect(
     const IntRect &aRect) {
   return GetInputRectInRect(IN_UNPREMULTIPLY_IN, aRect);
 }
 
 void FilterNodeOpacitySoftware::SetAttribute(uint32_t aIndex, Float aValue) {
   MOZ_ASSERT(aIndex == ATT_OPACITY_VALUE);
   mValue = aValue;
@@ -3124,16 +3263,22 @@ already_AddRefed<DataSourceSurface> Filt
       GetInputDataSourceSurface(IN_OPACITY_IN, aRect);
   return input ? Opacity(input, mValue) : nullptr;
 }
 
 void FilterNodeOpacitySoftware::RequestFromInputsForRect(const IntRect &aRect) {
   RequestInputRect(IN_OPACITY_IN, aRect);
 }
 
+IntRect FilterNodeOpacitySoftware::MapRectToSource(const IntRect &aRect,
+                                                   const IntRect &aMax,
+                                                   FilterNode *aSourceNode) {
+  return MapInputRectToSource(IN_OPACITY_IN, aRect, aMax, aSourceNode);
+}
+
 IntRect FilterNodeOpacitySoftware::GetOutputRectInRect(const IntRect &aRect) {
   return GetInputRectInRect(IN_OPACITY_IN, aRect);
 }
 
 bool PointLightSoftware::SetAttribute(uint32_t aIndex, const Point3D &aPoint) {
   switch (aIndex) {
     case ATT_POINT_LIGHT_POSITION:
       mPosition = aPoint;
@@ -3408,16 +3553,25 @@ void FilterNodeLightingSoftware<
     LightType, LightingType>::RequestFromInputsForRect(const IntRect &aRect) {
   IntRect srcRect = aRect;
   srcRect.Inflate(ceil(mKernelUnitLength.width),
                   ceil(mKernelUnitLength.height));
   RequestInputRect(IN_LIGHTING_IN, srcRect);
 }
 
 template <typename LightType, typename LightingType>
+IntRect FilterNodeLightingSoftware<LightType, LightingType>::MapRectToSource(
+    const IntRect &aRect, const IntRect &aMax, FilterNode *aSourceNode) {
+  IntRect srcRect = aRect;
+  srcRect.Inflate(ceil(mKernelUnitLength.width),
+                  ceil(mKernelUnitLength.height));
+  return MapInputRectToSource(IN_LIGHTING_IN, srcRect, aMax, aSourceNode);
+}
+
+template <typename LightType, typename LightingType>
 template <typename CoordType>
 already_AddRefed<DataSourceSurface>
 FilterNodeLightingSoftware<LightType, LightingType>::DoRender(
     const IntRect &aRect, CoordType aKernelUnitLengthX,
     CoordType aKernelUnitLengthY) {
   MOZ_ASSERT(aKernelUnitLengthX > 0,
              "aKernelUnitLengthX can be a negative or zero value");
   MOZ_ASSERT(aKernelUnitLengthY > 0,
--- a/gfx/2d/FilterNodeSoftware.h
+++ b/gfx/2d/FilterNodeSoftware.h
@@ -144,22 +144,28 @@ class FilterNodeSoftware : public Filter
       FormatHint aFormatHint = CAN_HANDLE_A8,
       ConvolveMatrixEdgeMode aEdgeMode = EDGE_MODE_NONE,
       const IntRect* aTransparencyPaddedSourceRect = nullptr);
 
   /**
    * Returns the intersection of the input filter's or surface's output rect
    * with aInRect.
    */
-  IntRect GetInputRectInRect(uint32_t aInputEnumIndex, const IntRect& aInRect);
+  IntRect GetInputRectInRect(uint32_t aInputEnumIndex, const IntRect &aInRect);
 
   /**
    * Calls RequestRect on the specified input, if it's a filter.
    */
-  void RequestInputRect(uint32_t aInputEnumIndex, const IntRect& aRect);
+  void RequestInputRect(uint32_t aInputEnumIndex, const IntRect &aRect);
+
+  /**
+   * Calls MapRectToSource on the specified input, if it's a filter.
+   */
+  IntRect MapInputRectToSource(uint32_t aInputEnumIndex, const IntRect &aRect,
+                               const IntRect &aMax, FilterNode *aSourceNode);
 
   /**
    * Returns the number of set input filters or surfaces. Needed for filters
    * which can have an arbitrary number of inputs.
    */
   size_t NumberOfSetInputs();
 
   /**
@@ -220,151 +226,161 @@ class FilterNodeSoftware : public Filter
 };
 
 // Subclasses for specific filters.
 
 class FilterNodeTransformSoftware : public FilterNodeSoftware {
  public:
   MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(FilterNodeTransformSoftware, override)
   FilterNodeTransformSoftware();
-  virtual const char* GetName() override { return "Transform"; }
+  virtual const char *GetName() override { return "Transform"; }
   using FilterNodeSoftware::SetAttribute;
   virtual void SetAttribute(uint32_t aIndex, uint32_t aGraphicsFilter) override;
-  virtual void SetAttribute(uint32_t aIndex, const Matrix& aMatrix) override;
+  virtual void SetAttribute(uint32_t aIndex, const Matrix &aMatrix) override;
+  virtual IntRect MapRectToSource(const IntRect &aRect, const IntRect &aMax,
+                                  FilterNode *aSourceNode) override;
 
  protected:
   virtual already_AddRefed<DataSourceSurface> Render(
-      const IntRect& aRect) override;
-  virtual IntRect GetOutputRectInRect(const IntRect& aRect) override;
+      const IntRect &aRect) override;
+  virtual IntRect GetOutputRectInRect(const IntRect &aRect) override;
   virtual int32_t InputIndex(uint32_t aInputEnumIndex) override;
-  virtual void RequestFromInputsForRect(const IntRect& aRect) override;
-  IntRect SourceRectForOutputRect(const IntRect& aRect);
+  virtual void RequestFromInputsForRect(const IntRect &aRect) override;
+  IntRect SourceRectForOutputRect(const IntRect &aRect);
 
  private:
   Matrix mMatrix;
   SamplingFilter mSamplingFilter;
 };
 
 class FilterNodeBlendSoftware : public FilterNodeSoftware {
  public:
   MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(FilterNodeBlendSoftware, override)
   FilterNodeBlendSoftware();
-  virtual const char* GetName() override { return "Blend"; }
+  virtual const char *GetName() override { return "Blend"; }
   using FilterNodeSoftware::SetAttribute;
   virtual void SetAttribute(uint32_t aIndex, uint32_t aBlendMode) override;
+  virtual IntRect MapRectToSource(const IntRect &aRect, const IntRect &aMax,
+                                  FilterNode *aSourceNode) override;
 
  protected:
   virtual already_AddRefed<DataSourceSurface> Render(
-      const IntRect& aRect) override;
-  virtual IntRect GetOutputRectInRect(const IntRect& aRect) override;
+      const IntRect &aRect) override;
+  virtual IntRect GetOutputRectInRect(const IntRect &aRect) override;
   virtual int32_t InputIndex(uint32_t aInputEnumIndex) override;
-  virtual void RequestFromInputsForRect(const IntRect& aRect) override;
+  virtual void RequestFromInputsForRect(const IntRect &aRect) override;
 
  private:
   BlendMode mBlendMode;
 };
 
 class FilterNodeMorphologySoftware : public FilterNodeSoftware {
  public:
   MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(FilterNodeMorphologySoftware,
                                           override)
   FilterNodeMorphologySoftware();
-  virtual const char* GetName() override { return "Morphology"; }
+  virtual const char *GetName() override { return "Morphology"; }
   using FilterNodeSoftware::SetAttribute;
-  virtual void SetAttribute(uint32_t aIndex, const IntSize& aRadii) override;
+  virtual void SetAttribute(uint32_t aIndex, const IntSize &aRadii) override;
   virtual void SetAttribute(uint32_t aIndex, uint32_t aOperator) override;
 
  protected:
   virtual already_AddRefed<DataSourceSurface> Render(
-      const IntRect& aRect) override;
-  virtual IntRect GetOutputRectInRect(const IntRect& aRect) override;
+      const IntRect &aRect) override;
+  virtual IntRect GetOutputRectInRect(const IntRect &aRect) override;
   virtual int32_t InputIndex(uint32_t aInputEnumIndex) override;
-  virtual void RequestFromInputsForRect(const IntRect& aRect) override;
+  virtual void RequestFromInputsForRect(const IntRect &aRect) override;
 
  private:
   IntSize mRadii;
   MorphologyOperator mOperator;
 };
 
 class FilterNodeColorMatrixSoftware : public FilterNodeSoftware {
  public:
   MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(FilterNodeColorMatrixSoftware,
                                           override)
-  virtual const char* GetName() override { return "ColorMatrix"; }
+  virtual const char *GetName() override { return "ColorMatrix"; }
   using FilterNodeSoftware::SetAttribute;
-  virtual void SetAttribute(uint32_t aIndex, const Matrix5x4& aMatrix) override;
+  virtual void SetAttribute(uint32_t aIndex, const Matrix5x4 &aMatrix) override;
   virtual void SetAttribute(uint32_t aIndex, uint32_t aAlphaMode) override;
 
  protected:
   virtual already_AddRefed<DataSourceSurface> Render(
-      const IntRect& aRect) override;
-  virtual IntRect GetOutputRectInRect(const IntRect& aRect) override;
+      const IntRect &aRect) override;
+  virtual IntRect GetOutputRectInRect(const IntRect &aRect) override;
   virtual int32_t InputIndex(uint32_t aInputEnumIndex) override;
-  virtual void RequestFromInputsForRect(const IntRect& aRect) override;
+  virtual void RequestFromInputsForRect(const IntRect &aRect) override;
+  virtual IntRect MapRectToSource(const IntRect &aRect, const IntRect &aMax,
+                                  FilterNode *aSourceNode) override;
 
  private:
   Matrix5x4 mMatrix;
   AlphaMode mAlphaMode;
 };
 
 class FilterNodeFloodSoftware : public FilterNodeSoftware {
  public:
   MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(FilterNodeFloodSoftware, override)
-  virtual const char* GetName() override { return "Flood"; }
+  virtual const char *GetName() override { return "Flood"; }
   using FilterNodeSoftware::SetAttribute;
-  virtual void SetAttribute(uint32_t aIndex, const Color& aColor) override;
+  virtual void SetAttribute(uint32_t aIndex, const Color &aColor) override;
+  virtual IntRect MapRectToSource(const IntRect &aRect, const IntRect &aMax,
+                                  FilterNode *aSourceNode) override;
 
  protected:
   virtual already_AddRefed<DataSourceSurface> GetOutput(
-      const IntRect& aRect) override;
+      const IntRect &aRect) override;
   virtual already_AddRefed<DataSourceSurface> Render(
-      const IntRect& aRect) override;
-  virtual IntRect GetOutputRectInRect(const IntRect& aRect) override;
+      const IntRect &aRect) override;
+  virtual IntRect GetOutputRectInRect(const IntRect &aRect) override;
 
  private:
   Color mColor;
 };
 
 class FilterNodeTileSoftware : public FilterNodeSoftware {
  public:
   MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(FilterNodeTileSoftware, override)
-  virtual const char* GetName() override { return "Tile"; }
+  virtual const char *GetName() override { return "Tile"; }
   using FilterNodeSoftware::SetAttribute;
   virtual void SetAttribute(uint32_t aIndex,
-                            const IntRect& aSourceRect) override;
+                            const IntRect &aSourceRect) override;
 
  protected:
   virtual already_AddRefed<DataSourceSurface> Render(
-      const IntRect& aRect) override;
-  virtual IntRect GetOutputRectInRect(const IntRect& aRect) override;
+      const IntRect &aRect) override;
+  virtual IntRect GetOutputRectInRect(const IntRect &aRect) override;
   virtual int32_t InputIndex(uint32_t aInputEnumIndex) override;
-  virtual void RequestFromInputsForRect(const IntRect& aRect) override;
+  virtual void RequestFromInputsForRect(const IntRect &aRect) override;
 
  private:
   IntRect mSourceRect;
 };
 
 /**
  * Baseclass for the four different component transfer filters.
  */
 class FilterNodeComponentTransferSoftware : public FilterNodeSoftware {
  public:
   MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(FilterNodeComponentTransferSoftware,
                                           override)
   FilterNodeComponentTransferSoftware();
 
   using FilterNodeSoftware::SetAttribute;
   virtual void SetAttribute(uint32_t aIndex, bool aDisable) override;
+  virtual IntRect MapRectToSource(const IntRect &aRect, const IntRect &aMax,
+                                  FilterNode *aSourceNode) override;
 
  protected:
   virtual already_AddRefed<DataSourceSurface> Render(
-      const IntRect& aRect) override;
-  virtual IntRect GetOutputRectInRect(const IntRect& aRect) override;
+      const IntRect &aRect) override;
+  virtual IntRect GetOutputRectInRect(const IntRect &aRect) override;
   virtual int32_t InputIndex(uint32_t aInputEnumIndex) override;
-  virtual void RequestFromInputsForRect(const IntRect& aRect) override;
+  virtual void RequestFromInputsForRect(const IntRect &aRect) override;
   virtual void GenerateLookupTable(ptrdiff_t aComponent,
                                    uint8_t aTables[4][256], bool aDisabled);
   virtual void FillLookupTable(ptrdiff_t aComponent, uint8_t aTable[256]) = 0;
 
   bool mDisableR;
   bool mDisableG;
   bool mDisableB;
   bool mDisableA;
@@ -477,46 +493,48 @@ class FilterNodeGammaTransferSoftware
   Float mOffsetA;
 };
 
 class FilterNodeConvolveMatrixSoftware : public FilterNodeSoftware {
  public:
   MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(FilterNodeConvolveMatrixSoftware,
                                           override)
   FilterNodeConvolveMatrixSoftware();
-  virtual const char* GetName() override { return "ConvolveMatrix"; }
+  virtual const char *GetName() override { return "ConvolveMatrix"; }
   using FilterNodeSoftware::SetAttribute;
   virtual void SetAttribute(uint32_t aIndex,
-                            const IntSize& aKernelSize) override;
-  virtual void SetAttribute(uint32_t aIndex, const Float* aMatrix,
+                            const IntSize &aKernelSize) override;
+  virtual void SetAttribute(uint32_t aIndex, const Float *aMatrix,
                             uint32_t aSize) override;
   virtual void SetAttribute(uint32_t aIndex, Float aValue) override;
   virtual void SetAttribute(uint32_t aIndex,
-                            const Size& aKernelUnitLength) override;
+                            const Size &aKernelUnitLength) override;
   virtual void SetAttribute(uint32_t aIndex,
-                            const IntRect& aSourceRect) override;
-  virtual void SetAttribute(uint32_t aIndex, const IntPoint& aTarget) override;
+                            const IntRect &aSourceRect) override;
+  virtual void SetAttribute(uint32_t aIndex, const IntPoint &aTarget) override;
   virtual void SetAttribute(uint32_t aIndex, uint32_t aEdgeMode) override;
   virtual void SetAttribute(uint32_t aIndex, bool aPreserveAlpha) override;
+  virtual IntRect MapRectToSource(const IntRect &aRect, const IntRect &aMax,
+                                  FilterNode *aSourceNode) override;
 
  protected:
   virtual already_AddRefed<DataSourceSurface> Render(
-      const IntRect& aRect) override;
-  virtual IntRect GetOutputRectInRect(const IntRect& aRect) override;
+      const IntRect &aRect) override;
+  virtual IntRect GetOutputRectInRect(const IntRect &aRect) override;
   virtual int32_t InputIndex(uint32_t aInputEnumIndex) override;
-  virtual void RequestFromInputsForRect(const IntRect& aRect) override;
+  virtual void RequestFromInputsForRect(const IntRect &aRect) override;
 
  private:
   template <typename CoordType>
-  already_AddRefed<DataSourceSurface> DoRender(const IntRect& aRect,
+  already_AddRefed<DataSourceSurface> DoRender(const IntRect &aRect,
                                                CoordType aKernelUnitLengthX,
                                                CoordType aKernelUnitLengthY);
 
-  IntRect InflatedSourceRect(const IntRect& aDestRect);
-  IntRect InflatedDestRect(const IntRect& aSourceRect);
+  IntRect InflatedSourceRect(const IntRect &aDestRect);
+  IntRect InflatedDestRect(const IntRect &aSourceRect);
 
   IntSize mKernelSize;
   std::vector<Float> mKernelMatrix;
   Float mDivisor;
   Float mBias;
   IntPoint mTarget;
   IntRect mSourceRect;
   ConvolveMatrixEdgeMode mEdgeMode;
@@ -524,259 +542,279 @@ class FilterNodeConvolveMatrixSoftware :
   bool mPreserveAlpha;
 };
 
 class FilterNodeDisplacementMapSoftware : public FilterNodeSoftware {
  public:
   MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(FilterNodeDisplacementMapSoftware,
                                           override)
   FilterNodeDisplacementMapSoftware();
-  virtual const char* GetName() override { return "DisplacementMap"; }
+  virtual const char *GetName() override { return "DisplacementMap"; }
   using FilterNodeSoftware::SetAttribute;
   virtual void SetAttribute(uint32_t aIndex, Float aScale) override;
   virtual void SetAttribute(uint32_t aIndex, uint32_t aValue) override;
+  virtual IntRect MapRectToSource(const IntRect &aRect, const IntRect &aMax,
+                                  FilterNode *aSourceNode) override;
 
  protected:
   virtual already_AddRefed<DataSourceSurface> Render(
-      const IntRect& aRect) override;
-  virtual IntRect GetOutputRectInRect(const IntRect& aRect) override;
+      const IntRect &aRect) override;
+  virtual IntRect GetOutputRectInRect(const IntRect &aRect) override;
   virtual int32_t InputIndex(uint32_t aInputEnumIndex) override;
-  virtual void RequestFromInputsForRect(const IntRect& aRect) override;
+  virtual void RequestFromInputsForRect(const IntRect &aRect) override;
 
  private:
-  IntRect InflatedSourceOrDestRect(const IntRect& aDestOrSourceRect);
+  IntRect InflatedSourceOrDestRect(const IntRect &aDestOrSourceRect);
 
   Float mScale;
   ColorChannel mChannelX;
   ColorChannel mChannelY;
 };
 
 class FilterNodeTurbulenceSoftware : public FilterNodeSoftware {
  public:
   MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(FilterNodeTurbulenceSoftware,
                                           override)
   FilterNodeTurbulenceSoftware();
-  virtual const char* GetName() override { return "Turbulence"; }
+  virtual const char *GetName() override { return "Turbulence"; }
   using FilterNodeSoftware::SetAttribute;
-  virtual void SetAttribute(uint32_t aIndex, const Size& aSize) override;
+  virtual void SetAttribute(uint32_t aIndex, const Size &aSize) override;
   virtual void SetAttribute(uint32_t aIndex,
-                            const IntRect& aRenderRect) override;
+                            const IntRect &aRenderRect) override;
   virtual void SetAttribute(uint32_t aIndex, bool aStitchable) override;
   virtual void SetAttribute(uint32_t aIndex, uint32_t aValue) override;
+  virtual IntRect MapRectToSource(const IntRect &aRect, const IntRect &aMax,
+                                  FilterNode *aSourceNode) override;
 
  protected:
   virtual already_AddRefed<DataSourceSurface> Render(
-      const IntRect& aRect) override;
-  virtual IntRect GetOutputRectInRect(const IntRect& aRect) override;
+      const IntRect &aRect) override;
+  virtual IntRect GetOutputRectInRect(const IntRect &aRect) override;
   virtual int32_t InputIndex(uint32_t aInputEnumIndex) override;
 
  private:
   IntRect mRenderRect;
   Size mBaseFrequency;
   uint32_t mNumOctaves;
   uint32_t mSeed;
   bool mStitchable;
   TurbulenceType mType;
 };
 
 class FilterNodeArithmeticCombineSoftware : public FilterNodeSoftware {
  public:
   MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(FilterNodeArithmeticCombineSoftware,
                                           override)
   FilterNodeArithmeticCombineSoftware();
-  virtual const char* GetName() override { return "ArithmeticCombine"; }
+  virtual const char *GetName() override { return "ArithmeticCombine"; }
   using FilterNodeSoftware::SetAttribute;
-  virtual void SetAttribute(uint32_t aIndex, const Float* aFloat,
+  virtual void SetAttribute(uint32_t aIndex, const Float *aFloat,
                             uint32_t aSize) override;
+  virtual IntRect MapRectToSource(const IntRect &aRect, const IntRect &aMax,
+                                  FilterNode *aSourceNode) override;
 
  protected:
   virtual already_AddRefed<DataSourceSurface> Render(
-      const IntRect& aRect) override;
-  virtual IntRect GetOutputRectInRect(const IntRect& aRect) override;
+      const IntRect &aRect) override;
+  virtual IntRect GetOutputRectInRect(const IntRect &aRect) override;
   virtual int32_t InputIndex(uint32_t aInputEnumIndex) override;
-  virtual void RequestFromInputsForRect(const IntRect& aRect) override;
+  virtual void RequestFromInputsForRect(const IntRect &aRect) override;
 
  private:
   Float mK1;
   Float mK2;
   Float mK3;
   Float mK4;
 };
 
 class FilterNodeCompositeSoftware : public FilterNodeSoftware {
  public:
   MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(FilterNodeCompositeSoftware, override)
   FilterNodeCompositeSoftware();
-  virtual const char* GetName() override { return "Composite"; }
+  virtual const char *GetName() override { return "Composite"; }
   using FilterNodeSoftware::SetAttribute;
   virtual void SetAttribute(uint32_t aIndex, uint32_t aOperator) override;
 
  protected:
   virtual already_AddRefed<DataSourceSurface> Render(
-      const IntRect& aRect) override;
-  virtual IntRect GetOutputRectInRect(const IntRect& aRect) override;
+      const IntRect &aRect) override;
+  virtual IntRect GetOutputRectInRect(const IntRect &aRect) override;
   virtual int32_t InputIndex(uint32_t aInputEnumIndex) override;
-  virtual void RequestFromInputsForRect(const IntRect& aRect) override;
+  virtual void RequestFromInputsForRect(const IntRect &aRect) override;
+  virtual IntRect MapRectToSource(const IntRect &aRect, const IntRect &aMax,
+                                  FilterNode *aSourceNode) override;
 
  private:
   CompositeOperator mOperator;
 };
 
 // Base class for FilterNodeGaussianBlurSoftware and
 // FilterNodeDirectionalBlurSoftware.
 class FilterNodeBlurXYSoftware : public FilterNodeSoftware {
  public:
   MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(FilterNodeBlurXYSoftware, override)
  protected:
   virtual already_AddRefed<DataSourceSurface> Render(
-      const IntRect& aRect) override;
-  virtual IntRect GetOutputRectInRect(const IntRect& aRect) override;
+      const IntRect &aRect) override;
+  virtual IntRect GetOutputRectInRect(const IntRect &aRect) override;
   virtual int32_t InputIndex(uint32_t aInputEnumIndex) override;
-  IntRect InflatedSourceOrDestRect(const IntRect& aDestRect);
-  virtual void RequestFromInputsForRect(const IntRect& aRect) override;
+  IntRect InflatedSourceOrDestRect(const IntRect &aDestRect);
+  virtual void RequestFromInputsForRect(const IntRect &aRect) override;
+  virtual IntRect MapRectToSource(const IntRect &aRect, const IntRect &aMax,
+                                  FilterNode *aSourceNode) override;
 
   // Implemented by subclasses.
   virtual Size StdDeviationXY() = 0;
 };
 
 class FilterNodeGaussianBlurSoftware : public FilterNodeBlurXYSoftware {
  public:
   MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(FilterNodeGaussianBlurSoftware,
                                           override)
   FilterNodeGaussianBlurSoftware();
-  virtual const char* GetName() override { return "GaussianBlur"; }
+  virtual const char *GetName() override { return "GaussianBlur"; }
   using FilterNodeSoftware::SetAttribute;
   virtual void SetAttribute(uint32_t aIndex, Float aStdDeviation) override;
 
  protected:
   virtual Size StdDeviationXY() override;
 
  private:
   Float mStdDeviation;
 };
 
 class FilterNodeDirectionalBlurSoftware : public FilterNodeBlurXYSoftware {
  public:
   MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(FilterNodeDirectionalBlurSoftware,
                                           override)
   FilterNodeDirectionalBlurSoftware();
-  virtual const char* GetName() override { return "DirectionalBlur"; }
+  virtual const char *GetName() override { return "DirectionalBlur"; }
   using FilterNodeSoftware::SetAttribute;
   virtual void SetAttribute(uint32_t aIndex, Float aStdDeviation) override;
   virtual void SetAttribute(uint32_t aIndex, uint32_t aBlurDirection) override;
 
  protected:
   virtual Size StdDeviationXY() override;
 
  private:
   Float mStdDeviation;
   BlurDirection mBlurDirection;
 };
 
 class FilterNodeCropSoftware : public FilterNodeSoftware {
  public:
   MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(FilterNodeCropSoftware, override)
-  virtual const char* GetName() override { return "Crop"; }
+  virtual const char *GetName() override { return "Crop"; }
   using FilterNodeSoftware::SetAttribute;
-  virtual void SetAttribute(uint32_t aIndex, const Rect& aSourceRect) override;
+  virtual void SetAttribute(uint32_t aIndex, const Rect &aSourceRect) override;
 
  protected:
   virtual already_AddRefed<DataSourceSurface> Render(
-      const IntRect& aRect) override;
-  virtual IntRect GetOutputRectInRect(const IntRect& aRect) override;
+      const IntRect &aRect) override;
+  virtual IntRect GetOutputRectInRect(const IntRect &aRect) override;
   virtual int32_t InputIndex(uint32_t aInputEnumIndex) override;
-  virtual void RequestFromInputsForRect(const IntRect& aRect) override;
+  virtual void RequestFromInputsForRect(const IntRect &aRect) override;
+  virtual IntRect MapRectToSource(const IntRect &aRect, const IntRect &aMax,
+                                  FilterNode *aSourceNode) override;
 
  private:
   IntRect mCropRect;
 };
 
 class FilterNodePremultiplySoftware : public FilterNodeSoftware {
  public:
   MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(FilterNodePremultiplySoftware,
                                           override)
-  virtual const char* GetName() override { return "Premultiply"; }
+  virtual const char *GetName() override { return "Premultiply"; }
 
  protected:
   virtual already_AddRefed<DataSourceSurface> Render(
-      const IntRect& aRect) override;
-  virtual IntRect GetOutputRectInRect(const IntRect& aRect) override;
+      const IntRect &aRect) override;
+  virtual IntRect GetOutputRectInRect(const IntRect &aRect) override;
   virtual int32_t InputIndex(uint32_t aInputEnumIndex) override;
-  virtual void RequestFromInputsForRect(const IntRect& aRect) override;
+  virtual void RequestFromInputsForRect(const IntRect &aRect) override;
+  virtual IntRect MapRectToSource(const IntRect &aRect, const IntRect &aMax,
+                                  FilterNode *aSourceNode) override;
 };
 
 class FilterNodeUnpremultiplySoftware : public FilterNodeSoftware {
  public:
   MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(FilterNodeUnpremultiplySoftware,
                                           override)
-  virtual const char* GetName() override { return "Unpremultiply"; }
+  virtual const char *GetName() override { return "Unpremultiply"; }
 
  protected:
   virtual already_AddRefed<DataSourceSurface> Render(
-      const IntRect& aRect) override;
-  virtual IntRect GetOutputRectInRect(const IntRect& aRect) override;
+      const IntRect &aRect) override;
+  virtual IntRect GetOutputRectInRect(const IntRect &aRect) override;
   virtual int32_t InputIndex(uint32_t aInputEnumIndex) override;
-  virtual void RequestFromInputsForRect(const IntRect& aRect) override;
+  virtual void RequestFromInputsForRect(const IntRect &aRect) override;
+  virtual IntRect MapRectToSource(const IntRect &aRect, const IntRect &aMax,
+                                  FilterNode *aSourceNode) override;
 };
 
 class FilterNodeOpacitySoftware : public FilterNodeSoftware {
  public:
   MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(FilterNodeOpacitySoftware, override)
-  virtual const char* GetName() override { return "Opacity"; }
+  virtual const char *GetName() override { return "Opacity"; }
   using FilterNodeSoftware::SetAttribute;
   virtual void SetAttribute(uint32_t aIndex, Float aValue) override;
+  virtual IntRect MapRectToSource(const IntRect &aRect, const IntRect &aMax,
+                                  FilterNode *aSourceNode) override;
 
  protected:
   virtual already_AddRefed<DataSourceSurface> Render(
-      const IntRect& aRect) override;
-  virtual IntRect GetOutputRectInRect(const IntRect& aRect) override;
+      const IntRect &aRect) override;
+  virtual IntRect GetOutputRectInRect(const IntRect &aRect) override;
   virtual int32_t InputIndex(uint32_t aInputEnumIndex) override;
-  virtual void RequestFromInputsForRect(const IntRect& aRect) override;
+  virtual void RequestFromInputsForRect(const IntRect &aRect) override;
 
   Float mValue = 1.0f;
 };
 
 template <typename LightType, typename LightingType>
 class FilterNodeLightingSoftware : public FilterNodeSoftware {
  public:
 #if defined(MOZILLA_INTERNAL_API) && \
     (defined(DEBUG) || defined(FORCE_BUILD_REFCNT_LOGGING))
   // Helpers for refcounted
-  virtual const char* typeName() const override { return mTypeName; }
+  virtual const char *typeName() const override { return mTypeName; }
   virtual size_t typeSize() const override { return sizeof(*this); }
 #endif
-  explicit FilterNodeLightingSoftware(const char* aTypeName);
-  virtual const char* GetName() override { return "Lighting"; }
+  explicit FilterNodeLightingSoftware(const char *aTypeName);
+  virtual const char *GetName() override { return "Lighting"; }
   using FilterNodeSoftware::SetAttribute;
   virtual void SetAttribute(uint32_t aIndex, Float) override;
-  virtual void SetAttribute(uint32_t aIndex, const Size&) override;
-  virtual void SetAttribute(uint32_t aIndex, const Point3D&) override;
-  virtual void SetAttribute(uint32_t aIndex, const Color&) override;
+  virtual void SetAttribute(uint32_t aIndex, const Size &) override;
+  virtual void SetAttribute(uint32_t aIndex, const Point3D &) override;
+  virtual void SetAttribute(uint32_t aIndex, const Color &) override;
+  virtual IntRect MapRectToSource(const IntRect &aRect, const IntRect &aMax,
+                                  FilterNode *aSourceNode) override;
 
  protected:
   virtual already_AddRefed<DataSourceSurface> Render(
-      const IntRect& aRect) override;
-  virtual IntRect GetOutputRectInRect(const IntRect& aRect) override;
+      const IntRect &aRect) override;
+  virtual IntRect GetOutputRectInRect(const IntRect &aRect) override;
   virtual int32_t InputIndex(uint32_t aInputEnumIndex) override;
-  virtual void RequestFromInputsForRect(const IntRect& aRect) override;
+  virtual void RequestFromInputsForRect(const IntRect &aRect) override;
 
  private:
   template <typename CoordType>
-  already_AddRefed<DataSourceSurface> DoRender(const IntRect& aRect,
+  already_AddRefed<DataSourceSurface> DoRender(const IntRect &aRect,
                                                CoordType aKernelUnitLengthX,
                                                CoordType aKernelUnitLengthY);
 
   Mutex mLock;
   LightType mLight;
   LightingType mLighting;
   Float mSurfaceScale;
   Size mKernelUnitLength;
   Color mColor;
 #if defined(MOZILLA_INTERNAL_API) && \
     (defined(DEBUG) || defined(FORCE_BUILD_REFCNT_LOGGING))
-  const char* mTypeName;
+  const char *mTypeName;
 #endif
 };
 
 }  // namespace gfx
 }  // namespace mozilla
 
 #endif  // _MOZILLA_GFX_FILTERNODESOFTWARE_H_
--- a/gfx/2d/Filters.h
+++ b/gfx/2d/Filters.h
@@ -420,16 +420,24 @@ class FilterNode : public external::Atom
   virtual void SetAttribute(uint32_t aIndex, const Color &) {
     MOZ_CRASH("GFX: FilterNode");
   }
   virtual void SetAttribute(uint32_t aIndex, const Float *aFloat,
                             uint32_t aSize) {
     MOZ_CRASH("GFX: FilterNode");
   }
 
+  /** Maps a rectangle in filter space back to a rectangle in the space of
+   * aSourceNode's first input. aSourceNode should not have an input
+   * assigned when calling this function. */
+  virtual IntRect MapRectToSource(const IntRect &aRect, const IntRect &aMax,
+                                  FilterNode *aSourceNode) {
+    return aMax;
+  }
+
  protected:
   friend class Factory;
 
   FilterNode() {}
 };
 
 }  // namespace gfx
 }  // namespace mozilla
--- a/gfx/2d/RecordedEvent.h
+++ b/gfx/2d/RecordedEvent.h
@@ -234,16 +234,17 @@ class RecordedEvent {
     MASKSURFACE,
     FILTERNODECREATION,
     FILTERNODEDESTRUCTION,
     DRAWFILTER,
     FILTERNODESETATTRIBUTE,
     FILTERNODESETINPUT,
     CREATESIMILARDRAWTARGET,
     CREATECLIPPEDDRAWTARGET,
+    CREATEDRAWTARGETFORFILTER,
     FONTDATA,
     FONTDESC,
     PUSHLAYER,
     PUSHLAYERWITHBLEND,
     POPLAYER,
     UNSCALEDFONTCREATION,
     UNSCALEDFONTDESTRUCTION,
     INTOLUMINANCE,
@@ -252,22 +253,25 @@ class RecordedEvent {
   static const uint32_t kTotalEventTypes =
       RecordedEvent::FILTERNODESETINPUT + 1;
 
   virtual ~RecordedEvent() {}
 
   static std::string GetEventName(EventType aType);
 
   /**
-   * Play back this event using the translator. Note that derived classes should
-   * only return false when there is a fatal error, as it will probably mean the
+   * Play back this event using the translator. Note that derived classes
+   * should
+   * only return false when there is a fatal error, as it will probably mean
+   * the
    * translation will abort.
    * @param aTranslator Translator to be used for retrieving other referenced
    *                    objects and making playback decisions.
-   * @return true unless a fatal problem has occurred and playback should abort.
+   * @return true unless a fatal problem has occurred and playback should
+   * abort.
    */
   virtual bool PlayEvent(Translator *aTranslator) const { return true; }
 
   virtual void RecordToStream(std::ostream &aStream) const = 0;
   virtual void RecordToStream(EventStream &aStream) const = 0;
   virtual void RecordToStream(MemStream &aStream) const = 0;
 
   virtual void OutputSimpleEventInfo(std::stringstream &aStringStream) const {}
--- a/gfx/2d/RecordedEventImpl.h
+++ b/gfx/2d/RecordedEventImpl.h
@@ -193,16 +193,61 @@ class RecordedCreateClippedDrawTarget
 
  private:
   friend class RecordedEvent;
 
   template <class S>
   MOZ_IMPLICIT RecordedCreateClippedDrawTarget(S &aStream);
 };
 
+class RecordedCreateDrawTargetForFilter
+    : public RecordedDrawingEvent<RecordedCreateDrawTargetForFilter> {
+ public:
+  RecordedCreateDrawTargetForFilter(DrawTarget *aDT, ReferencePtr aRefPtr,
+                                    const IntSize &aMaxSize,
+                                    SurfaceFormat aFormat, FilterNode *aFilter,
+                                    FilterNode *aSource,
+                                    const Rect &aSourceRect,
+                                    const Point &aDestPoint)
+      : RecordedDrawingEvent(CREATEDRAWTARGETFORFILTER, aDT),
+        mRefPtr(aRefPtr),
+        mMaxSize(aMaxSize),
+        mFormat(aFormat),
+        mFilter(aFilter),
+        mSource(aSource),
+        mSourceRect(aSourceRect),
+        mDestPoint(aDestPoint) {}
+
+  virtual bool PlayEvent(Translator *aTranslator) const override;
+
+  template <class S>
+  void Record(S &aStream) const;
+  virtual void OutputSimpleEventInfo(
+      std::stringstream &aStringStream) const override;
+
+  virtual std::string GetName() const override {
+    return "CreateSimilarDrawTargetForFilter";
+  }
+  virtual ReferencePtr GetObjectRef() const override { return mRefPtr; }
+
+  ReferencePtr mRefPtr;
+  IntSize mMaxSize;
+  SurfaceFormat mFormat;
+  ReferencePtr mFilter;
+  ReferencePtr mSource;
+  Rect mSourceRect;
+  Point mDestPoint;
+
+ private:
+  friend class RecordedEvent;
+
+  template <class S>
+  MOZ_IMPLICIT RecordedCreateDrawTargetForFilter(S &aStream);
+};
+
 class RecordedFillRect : public RecordedDrawingEvent<RecordedFillRect> {
  public:
   RecordedFillRect(DrawTarget *aDT, const Rect &aRect, const Pattern &aPattern,
                    const DrawOptions &aOptions)
       : RecordedDrawingEvent(FILLRECT, aDT),
         mRect(aRect),
         mPattern(),
         mOptions(aOptions) {
@@ -1890,16 +1935,72 @@ RecordedCreateSimilarDrawTarget::Recorde
 
 inline void RecordedCreateSimilarDrawTarget::OutputSimpleEventInfo(
     std::stringstream &aStringStream) const {
   aStringStream << "[" << mRefPtr
                 << "] CreateSimilarDrawTarget (Size: " << mSize.width << "x"
                 << mSize.height << ")";
 }
 
+inline bool RecordedCreateDrawTargetForFilter::PlayEvent(
+    Translator *aTranslator) const {
+  IntRect baseRect = aTranslator->LookupDrawTarget(mDT)->GetRect();
+
+  auto maxRect = IntRect(IntPoint(0, 0), mMaxSize);
+
+  auto clone = aTranslator->LookupDrawTarget(mDT)->GetTransform();
+  bool invertible = clone.Invert();
+  // mSourceRect is in filter space. The filter outputs from mSourceRect need
+  // to be drawn at mDestPoint in user space.
+  Rect userSpaceSource = Rect(mDestPoint, mSourceRect.Size());
+  if (invertible) {
+    // Try to reduce the source rect so that it's not much bigger
+    // than the draw target. The result is not minimal. Examples
+    // are left as an exercise for the reader.
+    auto destRect = IntRectToRect(baseRect);
+    Rect userSpaceBounds = clone.TransformBounds(destRect);
+    userSpaceSource = userSpaceSource.Intersect(userSpaceBounds);
+  }
+
+  // Compute how much we moved the top-left of the source rect by, and use that
+  // to compute the new dest point, and move our intersected source rect back
+  // into the (new) filter space.
+  Point shift = userSpaceSource.TopLeft() - mDestPoint;
+  Rect filterSpaceSource =
+      Rect(mSourceRect.TopLeft() + shift, userSpaceSource.Size());
+
+  baseRect = RoundedOut(filterSpaceSource);
+  IntRect transformedRect =
+      aTranslator->LookupFilterNode(mFilter)->MapRectToSource(
+          baseRect, maxRect, aTranslator->LookupFilterNode(mSource));
+
+  // Intersect with maxRect to make sure we didn't end up with something bigger
+  transformedRect = transformedRect.Intersect(maxRect);
+
+  // If we end up with an empty rect make it 1x1 so that things don't break.
+  if (transformedRect.IsEmpty()) {
+    transformedRect = IntRect(0, 0, 1, 1);
+  }
+
+  RefPtr<DrawTarget> newDT =
+      aTranslator->GetReferenceDrawTarget()->CreateSimilarDrawTarget(
+          transformedRect.Size(), mFormat);
+  newDT =
+      gfx::Factory::CreateOffsetDrawTarget(newDT, transformedRect.TopLeft());
+
+  // If we couldn't create a DrawTarget this will probably cause us to crash
+  // with nullptr later in the playback, so return false to abort.
+  if (!newDT) {
+    return false;
+  }
+
+  aTranslator->AddDrawTarget(mRefPtr, newDT);
+  return true;
+}
+
 inline bool RecordedCreateClippedDrawTarget::PlayEvent(
     Translator *aTranslator) const {
   const IntRect baseRect = aTranslator->GetReferenceDrawTarget()->GetRect();
   const IntRect transformedRect = RoundedToInt(
       mTransform.Inverse().TransformBounds(IntRectToRect(baseRect)));
   const IntRect intersection =
       IntRect(IntPoint(0, 0), mMaxSize).Intersect(transformedRect);
 
@@ -1943,16 +2044,45 @@ RecordedCreateClippedDrawTarget::Recorde
   ReadElement(aStream, mFormat);
 }
 
 inline void RecordedCreateClippedDrawTarget::OutputSimpleEventInfo(
     std::stringstream &aStringStream) const {
   aStringStream << "[" << mRefPtr << "] CreateClippedDrawTarget ()";
 }
 
+template <class S>
+void RecordedCreateDrawTargetForFilter::Record(S &aStream) const {
+  RecordedDrawingEvent::Record(aStream);
+  WriteElement(aStream, mRefPtr);
+  WriteElement(aStream, mMaxSize);
+  WriteElement(aStream, mFormat);
+  WriteElement(aStream, mFilter);
+  WriteElement(aStream, mSource);
+  WriteElement(aStream, mSourceRect);
+  WriteElement(aStream, mDestPoint);
+}
+
+template <class S>
+RecordedCreateDrawTargetForFilter::RecordedCreateDrawTargetForFilter(S &aStream)
+    : RecordedDrawingEvent(CREATEDRAWTARGETFORFILTER, aStream) {
+  ReadElement(aStream, mRefPtr);
+  ReadElement(aStream, mMaxSize);
+  ReadElement(aStream, mFormat);
+  ReadElement(aStream, mFilter);
+  ReadElement(aStream, mSource);
+  ReadElement(aStream, mSourceRect);
+  ReadElement(aStream, mDestPoint);
+}
+
+inline void RecordedCreateDrawTargetForFilter::OutputSimpleEventInfo(
+    std::stringstream &aStringStream) const {
+  aStringStream << "[" << mRefPtr << "] CreateDrawTargetForFilter ()";
+}
+
 struct GenericPattern {
   GenericPattern(const PatternStorage &aStorage, Translator *aTranslator)
       : mPattern(nullptr), mTranslator(aTranslator) {
     mStorage = const_cast<PatternStorage *>(&aStorage);
   }
 
   ~GenericPattern() {
     if (mPattern) {
@@ -3414,60 +3544,61 @@ inline void RecordedFilterNodeSetInput::
 
   aStringStream << ")";
 }
 
 #define LOAD_EVENT_TYPE(_typeenum, _class) \
   case _typeenum:                          \
     return new _class(aStream)
 
-#define FOR_EACH_EVENT(f)                                        \
-  f(DRAWTARGETCREATION, RecordedDrawTargetCreation);             \
-  f(DRAWTARGETDESTRUCTION, RecordedDrawTargetDestruction);       \
-  f(FILLRECT, RecordedFillRect);                                 \
-  f(STROKERECT, RecordedStrokeRect);                             \
-  f(STROKELINE, RecordedStrokeLine);                             \
-  f(CLEARRECT, RecordedClearRect);                               \
-  f(COPYSURFACE, RecordedCopySurface);                           \
-  f(SETTRANSFORM, RecordedSetTransform);                         \
-  f(PUSHCLIPRECT, RecordedPushClipRect);                         \
-  f(PUSHCLIP, RecordedPushClip);                                 \
-  f(POPCLIP, RecordedPopClip);                                   \
-  f(FILL, RecordedFill);                                         \
-  f(FILLGLYPHS, RecordedFillGlyphs);                             \
-  f(MASK, RecordedMask);                                         \
-  f(STROKE, RecordedStroke);                                     \
-  f(DRAWSURFACE, RecordedDrawSurface);                           \
-  f(DRAWDEPENDENTSURFACE, RecordedDrawDependentSurface);         \
-  f(DRAWSURFACEWITHSHADOW, RecordedDrawSurfaceWithShadow);       \
-  f(DRAWFILTER, RecordedDrawFilter);                             \
-  f(PATHCREATION, RecordedPathCreation);                         \
-  f(PATHDESTRUCTION, RecordedPathDestruction);                   \
-  f(SOURCESURFACECREATION, RecordedSourceSurfaceCreation);       \
-  f(SOURCESURFACEDESTRUCTION, RecordedSourceSurfaceDestruction); \
-  f(FILTERNODECREATION, RecordedFilterNodeCreation);             \
-  f(FILTERNODEDESTRUCTION, RecordedFilterNodeDestruction);       \
-  f(GRADIENTSTOPSCREATION, RecordedGradientStopsCreation);       \
-  f(GRADIENTSTOPSDESTRUCTION, RecordedGradientStopsDestruction); \
-  f(SNAPSHOT, RecordedSnapshot);                                 \
-  f(SCALEDFONTCREATION, RecordedScaledFontCreation);             \
-  f(SCALEDFONTDESTRUCTION, RecordedScaledFontDestruction);       \
-  f(MASKSURFACE, RecordedMaskSurface);                           \
-  f(FILTERNODESETATTRIBUTE, RecordedFilterNodeSetAttribute);     \
-  f(FILTERNODESETINPUT, RecordedFilterNodeSetInput);             \
-  f(CREATESIMILARDRAWTARGET, RecordedCreateSimilarDrawTarget);   \
-  f(CREATECLIPPEDDRAWTARGET, RecordedCreateClippedDrawTarget);   \
-  f(FONTDATA, RecordedFontData);                                 \
-  f(FONTDESC, RecordedFontDescriptor);                           \
-  f(PUSHLAYER, RecordedPushLayer);                               \
-  f(PUSHLAYERWITHBLEND, RecordedPushLayerWithBlend);             \
-  f(POPLAYER, RecordedPopLayer);                                 \
-  f(UNSCALEDFONTCREATION, RecordedUnscaledFontCreation);         \
-  f(UNSCALEDFONTDESTRUCTION, RecordedUnscaledFontDestruction);   \
-  f(INTOLUMINANCE, RecordedIntoLuminanceSource);                 \
+#define FOR_EACH_EVENT(f)                                          \
+  f(DRAWTARGETCREATION, RecordedDrawTargetCreation);               \
+  f(DRAWTARGETDESTRUCTION, RecordedDrawTargetDestruction);         \
+  f(FILLRECT, RecordedFillRect);                                   \
+  f(STROKERECT, RecordedStrokeRect);                               \
+  f(STROKELINE, RecordedStrokeLine);                               \
+  f(CLEARRECT, RecordedClearRect);                                 \
+  f(COPYSURFACE, RecordedCopySurface);                             \
+  f(SETTRANSFORM, RecordedSetTransform);                           \
+  f(PUSHCLIPRECT, RecordedPushClipRect);                           \
+  f(PUSHCLIP, RecordedPushClip);                                   \
+  f(POPCLIP, RecordedPopClip);                                     \
+  f(FILL, RecordedFill);                                           \
+  f(FILLGLYPHS, RecordedFillGlyphs);                               \
+  f(MASK, RecordedMask);                                           \
+  f(STROKE, RecordedStroke);                                       \
+  f(DRAWSURFACE, RecordedDrawSurface);                             \
+  f(DRAWDEPENDENTSURFACE, RecordedDrawDependentSurface);           \
+  f(DRAWSURFACEWITHSHADOW, RecordedDrawSurfaceWithShadow);         \
+  f(DRAWFILTER, RecordedDrawFilter);                               \
+  f(PATHCREATION, RecordedPathCreation);                           \
+  f(PATHDESTRUCTION, RecordedPathDestruction);                     \
+  f(SOURCESURFACECREATION, RecordedSourceSurfaceCreation);         \
+  f(SOURCESURFACEDESTRUCTION, RecordedSourceSurfaceDestruction);   \
+  f(FILTERNODECREATION, RecordedFilterNodeCreation);               \
+  f(FILTERNODEDESTRUCTION, RecordedFilterNodeDestruction);         \
+  f(GRADIENTSTOPSCREATION, RecordedGradientStopsCreation);         \
+  f(GRADIENTSTOPSDESTRUCTION, RecordedGradientStopsDestruction);   \
+  f(SNAPSHOT, RecordedSnapshot);                                   \
+  f(SCALEDFONTCREATION, RecordedScaledFontCreation);               \
+  f(SCALEDFONTDESTRUCTION, RecordedScaledFontDestruction);         \
+  f(MASKSURFACE, RecordedMaskSurface);                             \
+  f(FILTERNODESETATTRIBUTE, RecordedFilterNodeSetAttribute);       \
+  f(FILTERNODESETINPUT, RecordedFilterNodeSetInput);               \
+  f(CREATESIMILARDRAWTARGET, RecordedCreateSimilarDrawTarget);     \
+  f(CREATECLIPPEDDRAWTARGET, RecordedCreateClippedDrawTarget);     \
+  f(CREATEDRAWTARGETFORFILTER, RecordedCreateDrawTargetForFilter); \
+  f(FONTDATA, RecordedFontData);                                   \
+  f(FONTDESC, RecordedFontDescriptor);                             \
+  f(PUSHLAYER, RecordedPushLayer);                                 \
+  f(PUSHLAYERWITHBLEND, RecordedPushLayerWithBlend);               \
+  f(POPLAYER, RecordedPopLayer);                                   \
+  f(UNSCALEDFONTCREATION, RecordedUnscaledFontCreation);           \
+  f(UNSCALEDFONTDESTRUCTION, RecordedUnscaledFontDestruction);     \
+  f(INTOLUMINANCE, RecordedIntoLuminanceSource);                   \
   f(EXTERNALSURFACECREATION, RecordedExternalSurfaceCreation);
 
 template <class S>
 RecordedEvent *RecordedEvent::LoadEvent(S &aStream, EventType aType) {
   switch (aType) {
     FOR_EACH_EVENT(LOAD_EVENT_TYPE)
     default:
       return nullptr;
--- a/gfx/src/FilterSupport.cpp
+++ b/gfx/src/FilterSupport.cpp
@@ -204,28 +204,28 @@ static already_AddRefed<FilterNode> Gaus
     filterV->SetAttribute(ATT_DIRECTIONAL_BLUR_STD_DEVIATION, stdY);
     filterH->SetInput(IN_DIRECTIONAL_BLUR_IN, aInputFilter);
     filterV->SetInput(IN_DIRECTIONAL_BLUR_IN, filterH);
     return filterV.forget();
   }
   return nullptr;
 }
 
-static already_AddRefed<FilterNode> Clear(DrawTarget* aDT) {
+already_AddRefed<FilterNode> Clear(DrawTarget* aDT) {
   RefPtr<FilterNode> filter = aDT->CreateFilter(FilterType::FLOOD);
   if (filter) {
     filter->SetAttribute(ATT_FLOOD_COLOR, Color(0, 0, 0, 0));
     return filter.forget();
   }
   return nullptr;
 }
 
-static already_AddRefed<FilterNode> ForSurface(
-    DrawTarget* aDT, SourceSurface* aSurface,
-    const IntPoint& aSurfacePosition) {
+already_AddRefed<FilterNode> ForSurface(DrawTarget* aDT,
+                                        SourceSurface* aSurface,
+                                        const IntPoint& aSurfacePosition) {
   RefPtr<FilterNode> filter = aDT->CreateFilter(FilterType::TRANSFORM);
   if (filter) {
     filter->SetAttribute(
         ATT_TRANSFORM_MATRIX,
         Matrix::Translation(aSurfacePosition.x, aSurfacePosition.y));
     filter->SetInput(IN_TRANSFORM_IN, aSurface);
     return filter.forget();
   }
@@ -1199,22 +1199,21 @@ static AlphaModel OutputAlphaModelForPri
     return InputAlphaModelForPrimitive(aDescr, 0, aInputAlphaModels[0]);
   }
 
   // All filters without inputs produce premultiplied alpha.
   return AlphaModel::Premultiplied;
 }
 
 // Returns the output FilterNode, in premultiplied sRGB space.
-static already_AddRefed<FilterNode> FilterNodeGraphFromDescription(
+already_AddRefed<FilterNode> FilterNodeGraphFromDescription(
     DrawTarget* aDT, const FilterDescription& aFilter,
-    const Rect& aResultNeededRect, SourceSurface* aSourceGraphic,
-    const IntRect& aSourceGraphicRect, SourceSurface* aFillPaint,
-    const IntRect& aFillPaintRect, SourceSurface* aStrokePaint,
-    const IntRect& aStrokePaintRect,
+    const Rect& aResultNeededRect, FilterNode* aSourceGraphic,
+    const IntRect& aSourceGraphicRect, FilterNode* aFillPaint,
+    FilterNode* aStrokePaint,
     nsTArray<RefPtr<SourceSurface>>& aAdditionalImages) {
   const nsTArray<FilterPrimitiveDescription>& primitives = aFilter.mPrimitives;
   MOZ_RELEASE_ASSERT(!primitives.IsEmpty());
 
   RefPtr<FilterCachedColorModels> sourceFilters[4];
   nsTArray<RefPtr<FilterCachedColorModels>> primitiveFilters;
 
   for (size_t i = 0; i < primitives.Length(); ++i) {
@@ -1244,27 +1243,22 @@ static already_AddRefed<FilterNode> Filt
       } else {
         int32_t sourceIndex = -inputIndex - 1;
         MOZ_ASSERT(sourceIndex >= 0, "invalid source index");
         MOZ_ASSERT(sourceIndex < 4, "invalid source index");
         inputFilter = sourceFilters[sourceIndex];
         if (!inputFilter) {
           RefPtr<FilterNode> sourceFilterNode;
 
-          nsTArray<SourceSurface*> primitiveSurfaces;
-          nsTArray<IntRect> primitiveSurfaceRects;
-          RefPtr<SourceSurface> surf =
-              ElementForIndex(inputIndex, primitiveSurfaces, aSourceGraphic,
+          nsTArray<FilterNode*> primitiveFilters;
+          RefPtr<FilterNode> filt =
+              ElementForIndex(inputIndex, primitiveFilters, aSourceGraphic,
                               aFillPaint, aStrokePaint);
-          IntRect surfaceRect = ElementForIndex(
-              inputIndex, primitiveSurfaceRects, aSourceGraphicRect,
-              aFillPaintRect, aStrokePaintRect);
-          if (surf) {
-            IntPoint offset = surfaceRect.TopLeft();
-            sourceFilterNode = FilterWrappers::ForSurface(aDT, surf, offset);
+          if (filt) {
+            sourceFilterNode = filt;
 
             // Clip the original SourceGraphic to the first filter region if the
             // surface isn't already sized appropriately.
             if ((inputIndex ==
                      FilterPrimitiveDescription::kPrimitiveIndexSourceGraphic ||
                  inputIndex ==
                      FilterPrimitiveDescription::kPrimitiveIndexSourceAlpha) &&
                 !descr.FilterSpaceBounds().Contains(aSourceGraphicRect)) {
@@ -1319,19 +1313,32 @@ static already_AddRefed<FilterNode> Filt
 
 void FilterSupport::RenderFilterDescription(
     DrawTarget* aDT, const FilterDescription& aFilter, const Rect& aRenderRect,
     SourceSurface* aSourceGraphic, const IntRect& aSourceGraphicRect,
     SourceSurface* aFillPaint, const IntRect& aFillPaintRect,
     SourceSurface* aStrokePaint, const IntRect& aStrokePaintRect,
     nsTArray<RefPtr<SourceSurface>>& aAdditionalImages, const Point& aDestPoint,
     const DrawOptions& aOptions) {
+  RefPtr<FilterNode> sourceGraphic, fillPaint, strokePaint;
+  if (aSourceGraphic) {
+    sourceGraphic = FilterWrappers::ForSurface(aDT, aSourceGraphic,
+                                               aSourceGraphicRect.TopLeft());
+  }
+  if (aFillPaint) {
+    fillPaint =
+        FilterWrappers::ForSurface(aDT, aFillPaint, aFillPaintRect.TopLeft());
+  }
+  if (aStrokePaint) {
+    strokePaint = FilterWrappers::ForSurface(aDT, aStrokePaint,
+                                             aStrokePaintRect.TopLeft());
+  }
   RefPtr<FilterNode> resultFilter = FilterNodeGraphFromDescription(
-      aDT, aFilter, aRenderRect, aSourceGraphic, aSourceGraphicRect, aFillPaint,
-      aFillPaintRect, aStrokePaint, aStrokePaintRect, aAdditionalImages);
+      aDT, aFilter, aRenderRect, sourceGraphic, aSourceGraphicRect, fillPaint,
+      strokePaint, aAdditionalImages);
   if (!resultFilter) {
     gfxWarning() << "Filter is NULL.";
     return;
   }
   aDT->DrawFilter(resultFilter, aRenderRect, aDestPoint, aOptions);
 }
 
 static nsIntRegion UnionOfRegions(const nsTArray<nsIntRegion>& aRegions) {
--- a/gfx/src/FilterSupport.h
+++ b/gfx/src/FilterSupport.h
@@ -4,35 +4,40 @@
  * 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 __FilterSupport_h
 #define __FilterSupport_h
 
 #include "mozilla/Attributes.h"
 #include "mozilla/RefPtr.h"
-#include "mozilla/gfx/Rect.h"
+#include "mozilla/gfx/2D.h"
 #include "mozilla/gfx/Matrix.h"
-#include "mozilla/gfx/2D.h"
+#include "mozilla/gfx/Rect.h"
 #include "nsClassHashtable.h"
+#include "nsRegion.h"
 #include "nsTArray.h"
-#include "nsRegion.h"
 
 namespace mozilla {
 namespace gfx {
 class FilterPrimitiveDescription;
 }  // namespace gfx
 }  // namespace mozilla
 
 DECLARE_USE_COPY_CONSTRUCTORS(mozilla::gfx::FilterPrimitiveDescription)
 
 extern const float gsRGBToLinearRGBMap[256];
 
 namespace mozilla {
 namespace gfx {
+namespace FilterWrappers {
+extern already_AddRefed<FilterNode> Clear(DrawTarget* aDT);
+extern already_AddRefed<FilterNode> ForSurface(
+    DrawTarget* aDT, SourceSurface* aSurface, const IntPoint& aSurfacePosition);
+}  // namespace FilterWrappers
 
 // Morphology Operators
 const unsigned short SVG_OPERATOR_UNKNOWN = 0;
 const unsigned short SVG_OPERATOR_ERODE = 1;
 const unsigned short SVG_OPERATOR_DILATE = 2;
 
 // ColorMatrix types
 const unsigned short SVG_FECOLORMATRIX_TYPE_UNKNOWN = 0;
@@ -494,16 +499,23 @@ struct FilterDescription final {
   bool operator==(const FilterDescription& aOther) const;
   bool operator!=(const FilterDescription& aOther) const {
     return !(*this == aOther);
   }
 
   nsTArray<FilterPrimitiveDescription> mPrimitives;
 };
 
+already_AddRefed<FilterNode> FilterNodeGraphFromDescription(
+    DrawTarget* aDT, const FilterDescription& aFilter,
+    const Rect& aResultNeededRect, FilterNode* aSourceGraphic,
+    const IntRect& aSourceGraphicRect, FilterNode* aFillPaint,
+    FilterNode* aStrokePaint,
+    nsTArray<RefPtr<SourceSurface>>& aAdditionalImages);
+
 /**
  * The methods of this class are not on FilterDescription because
  * FilterDescription is designed as a simple value holder that can be used
  * on any thread.
  */
 class FilterSupport {
  public:
   /**
--- a/layout/reftests/svg/filters/reftest.list
+++ b/layout/reftests/svg/filters/reftest.list
@@ -99,17 +99,17 @@ fails == filter-marked-line-01.svg pass.
 fuzzy(0-1,0-26732) == feComposite-paint-01.svg feComposite-paint-01-ref.svg
 fuzzy(0-1,0-10000) == feConvolveMatrix-bias-01.svg feConvolveMatrix-bias-01-ref.svg
 == feConvolveMatrix-order-01.svg feConvolveMatrix-order-01-ref.svg
 
 fuzzy-if(skiaContent,0-1,0-400) == feDisplacementMap-alpha-01.svg pass.svg
 fuzzy(0-2,0-500) == feDisplacementMap-colour-01.svg feDisplacementMap-colour-01-ref.svg
 == feDisplacementMap-scale-01.svg pass.svg
 
-fuzzy-if(/^Windows\x20NT\x2010\.0/.test(http.oscpu),0-2,0-25) fuzzy-if(webrender,16-16,14033-15367) == feDropShadow-01.svg feDropShadow-01-ref.svg
+fuzzy-if(/^Windows\x20NT\x2010\.0/.test(http.oscpu),0-2,0-25) fuzzy-if(webrender,56-57,14033-15368) == feDropShadow-01.svg feDropShadow-01-ref.svg
 
 == feFlood-color-01.svg pass.svg
 
 fuzzy-if(webrender,20-21,5624-5646) == feGaussianBlur-alpha-01.svg feGaussianBlur-alpha-01-ref.svg
 
 == feMorphology-radius-negative-01.svg pass.svg
 == feMorphology-radius-negative-02.svg pass.svg
 == feMorphology-radius-zero-01.svg pass.svg
--- a/layout/svg/nsFilterInstance.cpp
+++ b/layout/svg/nsFilterInstance.cpp
@@ -6,32 +6,33 @@
 
 // Main header first:
 #include "nsFilterInstance.h"
 
 // MFBT headers next:
 #include "mozilla/UniquePtr.h"
 
 // Keep others in (case-insensitive) order:
+#include "FilterSupport.h"
 #include "ImgDrawResult.h"
+#include "SVGContentUtils.h"
 #include "gfx2DGlue.h"
 #include "gfxContext.h"
 #include "gfxPlatform.h"
 #include "gfxPrefs.h"
 #include "gfxUtils.h"
+#include "mozilla/Unused.h"
+#include "mozilla/gfx/Filters.h"
 #include "mozilla/gfx/Helpers.h"
 #include "mozilla/gfx/PatternHelpers.h"
+#include "nsCSSFilterInstance.h"
 #include "nsSVGDisplayableFrame.h"
-#include "nsCSSFilterInstance.h"
 #include "nsSVGFilterInstance.h"
 #include "nsSVGFilterPaintCallback.h"
 #include "nsSVGUtils.h"
-#include "SVGContentUtils.h"
-#include "FilterSupport.h"
-#include "mozilla/Unused.h"
 
 using namespace mozilla;
 using namespace mozilla::dom;
 using namespace mozilla::gfx;
 using namespace mozilla::image;
 
 FilterDescription nsFilterInstance::GetFilterDescription(
     nsIContent* aFilteredElement, const nsTArray<nsStyleFilter>& aFilterChain,
@@ -609,26 +610,30 @@ void nsFilterInstance::BuildSourcePaints
   }
 
   if (!mStrokePaint.mNeededBounds.IsEmpty()) {
     BuildSourcePaint(&mStrokePaint, aImgParams);
   }
 }
 
 void nsFilterInstance::BuildSourceImage(DrawTarget* aDest,
-                                        imgDrawingParams& aImgParams) {
+                                        imgDrawingParams& aImgParams,
+                                        FilterNode* aFilter,
+                                        FilterNode* aSource,
+                                        const Rect& aSourceRect) {
   MOZ_ASSERT(mTargetFrame);
 
   nsIntRect neededRect = mSourceGraphic.mNeededBounds;
   if (neededRect.IsEmpty()) {
     return;
   }
 
-  RefPtr<DrawTarget> offscreenDT = aDest->CreateSimilarDrawTarget(
-      neededRect.Size(), SurfaceFormat::B8G8R8A8);
+  RefPtr<DrawTarget> offscreenDT = aDest->CreateSimilarDrawTargetForFilter(
+      neededRect.Size(), SurfaceFormat::B8G8R8A8, aFilter, aSource, aSourceRect,
+      Point(0, 0));
   if (!offscreenDT || !offscreenDT->IsValid()) {
     return;
   }
 
   gfxRect r = FilterSpaceToUserSpace(ThebesRect(neededRect));
   r.RoundOut();
   nsIntRect dirty;
   if (!gfxUtils::GfxRectToIntRect(r, &dirty)) {
@@ -677,25 +682,63 @@ void nsFilterInstance::Render(gfxContext
   }
 
   gfxContextMatrixAutoSaveRestore autoSR(aCtx);
   aCtx->SetMatrix(
       aCtx->CurrentMatrix().PreTranslate(filterRect.x, filterRect.y));
 
   ComputeNeededBoxes();
 
-  BuildSourceImage(aCtx->GetDrawTarget(), aImgParams);
+  Rect renderRect = IntRectToRect(filterRect);
+  RefPtr<DrawTarget> dt = aCtx->GetDrawTarget();
+
   BuildSourcePaints(aImgParams);
+  RefPtr<FilterNode> sourceGraphic, fillPaint, strokePaint;
+  if (mFillPaint.mSourceSurface) {
+    fillPaint = FilterWrappers::ForSurface(dt, mFillPaint.mSourceSurface,
+                                           mFillPaint.mSurfaceRect.TopLeft());
+  }
+  if (mStrokePaint.mSourceSurface) {
+    strokePaint = FilterWrappers::ForSurface(
+        dt, mStrokePaint.mSourceSurface, mStrokePaint.mSurfaceRect.TopLeft());
+  }
 
-  FilterSupport::RenderFilterDescription(
-      aCtx->GetDrawTarget(), mFilterDescription, IntRectToRect(filterRect),
-      mSourceGraphic.mSourceSurface, mSourceGraphic.mSurfaceRect,
-      mFillPaint.mSourceSurface, mFillPaint.mSurfaceRect,
-      mStrokePaint.mSourceSurface, mStrokePaint.mSurfaceRect, mInputImages,
-      Point(0, 0), DrawOptions(aOpacity));
+  // We make the sourceGraphic filter but don't set its inputs until after so
+  // that we can make the sourceGraphic size depend on the filter chain
+  sourceGraphic = dt->CreateFilter(FilterType::TRANSFORM);
+  if (sourceGraphic) {
+    // Make sure we set the translation before calling BuildSourceImage
+    // so that CreateSimilarDrawTargetForFilter works properly
+    IntPoint offset = mSourceGraphic.mNeededBounds.TopLeft();
+    sourceGraphic->SetAttribute(ATT_TRANSFORM_MATRIX,
+                                Matrix::Translation(offset.x, offset.y));
+  }
+
+  RefPtr<FilterNode> resultFilter = FilterNodeGraphFromDescription(
+      aCtx->GetDrawTarget(), mFilterDescription, renderRect, sourceGraphic,
+      mSourceGraphic.mSurfaceRect, fillPaint, strokePaint, mInputImages);
+
+  if (!resultFilter) {
+    gfxWarning() << "Filter is NULL.";
+    return;
+  }
+
+  BuildSourceImage(aCtx->GetDrawTarget(), aImgParams, resultFilter,
+                   sourceGraphic, renderRect);
+  if (sourceGraphic) {
+    if (mSourceGraphic.mSourceSurface) {
+      sourceGraphic->SetInput(IN_TRANSFORM_IN, mSourceGraphic.mSourceSurface);
+    } else {
+      RefPtr<FilterNode> clear = FilterWrappers::Clear(aCtx->GetDrawTarget());
+      sourceGraphic->SetInput(IN_TRANSFORM_IN, clear);
+    }
+  }
+
+  aCtx->GetDrawTarget()->DrawFilter(resultFilter, renderRect, Point(0, 0),
+                                    DrawOptions(aOpacity));
 }
 
 nsRegion nsFilterInstance::ComputePostFilterDirtyRegion() {
   if (mPreFilterDirtyRegion.IsEmpty() ||
       mFilterDescription.mPrimitives.IsEmpty()) {
     return nsRegion();
   }
 
--- a/layout/svg/nsFilterInstance.h
+++ b/layout/svg/nsFilterInstance.h
@@ -235,17 +235,20 @@ class nsFilterInstance {
    * mStrokePaint.mSourceSurface respectively.
    */
   void BuildSourcePaints(imgDrawingParams& aImgParams);
 
   /**
    * Creates the SourceSurface for the SourceGraphic graph node, paints its
    * contents, and assigns it to mSourceGraphic.mSourceSurface.
    */
-  void BuildSourceImage(DrawTarget* aDest, imgDrawingParams& aImgParams);
+  void BuildSourceImage(DrawTarget* aDest, imgDrawingParams& aImgParams,
+                        mozilla::gfx::FilterNode* aFilter,
+                        mozilla::gfx::FilterNode* aSource,
+                        const mozilla::gfx::Rect& aSourceRect);
 
   /**
    * Build the list of FilterPrimitiveDescriptions that describes the filter's
    * filter primitives and their connections. This populates
    * mPrimitiveDescriptions and mInputImages. aFilterInputIsTainted describes
    * whether the SourceGraphic is tainted.
    */
   nsresult BuildPrimitives(const nsTArray<nsStyleFilter>& aFilterChain,