Bug 1307740 - Properly handle filter input tainting with CSS filters and multiple filters. r=jwatt, a=ritu
authorMarkus Stange <mstange@themasta.com>
Wed, 19 Oct 2016 11:32:02 -0400
changeset 356305 09a44149a83927ab6cd3ac482c96f5f1aa79bbed
parent 356304 5bf38444c8f688d3340d273e601bc63d034477c1
child 356306 05dbe33c8ef29493f139319cbcf73595f39de4da
push id6570
push userraliiev@mozilla.com
push dateMon, 14 Nov 2016 12:26:13 +0000
treeherdermozilla-beta@f455459b2ae5 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjwatt, ritu
bugs1307740
milestone51.0a2
Bug 1307740 - Properly handle filter input tainting with CSS filters and multiple filters. r=jwatt, a=ritu MozReview-Commit-ID: HwOJ8SFhkUq
dom/canvas/CanvasRenderingContext2D.cpp
layout/svg/nsCSSFilterInstance.cpp
layout/svg/nsCSSFilterInstance.h
layout/svg/nsFilterInstance.cpp
layout/svg/nsFilterInstance.h
layout/svg/nsSVGFilterInstance.cpp
layout/svg/nsSVGFilterInstance.h
--- a/dom/canvas/CanvasRenderingContext2D.cpp
+++ b/dom/canvas/CanvasRenderingContext2D.cpp
@@ -2847,28 +2847,31 @@ CanvasRenderingContext2D::UpdateFilter()
     return;
   }
 
   // The filter might reference an SVG filter that is declared inside this
   // document. Flush frames so that we'll have an nsSVGFilterFrame to work
   // with.
   presShell->FlushPendingNotifications(Flush_Frames);
 
+  bool sourceGraphicIsTainted =
+    (mCanvasElement && mCanvasElement->IsWriteOnly());
+
   CurrentState().filter =
     nsFilterInstance::GetFilterDescription(mCanvasElement,
       CurrentState().filterChain,
+      sourceGraphicIsTainted,
       CanvasUserSpaceMetrics(GetSize(),
                              CurrentState().fontFont,
                              CurrentState().fontLanguage,
                              CurrentState().fontExplicitLanguage,
                              presShell->GetPresContext()),
       gfxRect(0, 0, mWidth, mHeight),
       CurrentState().filterAdditionalImages);
-  CurrentState().filterSourceGraphicTainted =
-    (mCanvasElement && mCanvasElement->IsWriteOnly());
+  CurrentState().filterSourceGraphicTainted = sourceGraphicIsTainted;
 }
 
 //
 // rects
 //
 
 static bool
 ValidateRect(double& aX, double& aY, double& aWidth, double& aHeight, bool aIsZeroSizeValid)
--- a/layout/svg/nsCSSFilterInstance.cpp
+++ b/layout/svg/nsCSSFilterInstance.cpp
@@ -35,60 +35,81 @@ nsCSSFilterInstance::nsCSSFilterInstance
   : mFilter(aFilter)
   , mShadowFallbackColor(aShadowFallbackColor)
   , mTargetBoundsInFilterSpace(aTargetBoundsInFilterSpace)
   , mFrameSpaceInCSSPxToFilterSpaceTransform(aFrameSpaceInCSSPxToFilterSpaceTransform)
 {
 }
 
 nsresult
-nsCSSFilterInstance::BuildPrimitives(nsTArray<FilterPrimitiveDescription>& aPrimitiveDescrs)
+nsCSSFilterInstance::BuildPrimitives(nsTArray<FilterPrimitiveDescription>& aPrimitiveDescrs,
+                                     bool aInputIsTainted)
 {
   FilterPrimitiveDescription descr;
   nsresult result;
 
   switch(mFilter.GetType()) {
     case NS_STYLE_FILTER_BLUR:
-      descr = CreatePrimitiveDescription(PrimitiveType::GaussianBlur, aPrimitiveDescrs);
+      descr = CreatePrimitiveDescription(PrimitiveType::GaussianBlur,
+                                         aPrimitiveDescrs,
+                                         aInputIsTainted);
       result = SetAttributesForBlur(descr);
       break;
     case NS_STYLE_FILTER_BRIGHTNESS:
-      descr = CreatePrimitiveDescription(PrimitiveType::ComponentTransfer, aPrimitiveDescrs);
+      descr = CreatePrimitiveDescription(PrimitiveType::ComponentTransfer,
+                                         aPrimitiveDescrs,
+                                         aInputIsTainted);
       result = SetAttributesForBrightness(descr);
       break;
     case NS_STYLE_FILTER_CONTRAST:
-      descr = CreatePrimitiveDescription(PrimitiveType::ComponentTransfer, aPrimitiveDescrs);
+      descr = CreatePrimitiveDescription(PrimitiveType::ComponentTransfer,
+                                         aPrimitiveDescrs,
+                                         aInputIsTainted);
       result = SetAttributesForContrast(descr);
       break;
     case NS_STYLE_FILTER_DROP_SHADOW:
-      descr = CreatePrimitiveDescription(PrimitiveType::DropShadow, aPrimitiveDescrs);
+      descr = CreatePrimitiveDescription(PrimitiveType::DropShadow,
+                                         aPrimitiveDescrs,
+                                         aInputIsTainted);
       result = SetAttributesForDropShadow(descr);
       break;
     case NS_STYLE_FILTER_GRAYSCALE:
-      descr = CreatePrimitiveDescription(PrimitiveType::ColorMatrix, aPrimitiveDescrs);
+      descr = CreatePrimitiveDescription(PrimitiveType::ColorMatrix,
+                                         aPrimitiveDescrs,
+                                         aInputIsTainted);
       result = SetAttributesForGrayscale(descr);
       break;
     case NS_STYLE_FILTER_HUE_ROTATE:
-      descr = CreatePrimitiveDescription(PrimitiveType::ColorMatrix, aPrimitiveDescrs);
+      descr = CreatePrimitiveDescription(PrimitiveType::ColorMatrix,
+                                         aPrimitiveDescrs,
+                                         aInputIsTainted);
       result = SetAttributesForHueRotate(descr);
       break;
     case NS_STYLE_FILTER_INVERT:
-      descr = CreatePrimitiveDescription(PrimitiveType::ComponentTransfer, aPrimitiveDescrs);
+      descr = CreatePrimitiveDescription(PrimitiveType::ComponentTransfer,
+                                         aPrimitiveDescrs,
+                                         aInputIsTainted);
       result = SetAttributesForInvert(descr);
       break;
     case NS_STYLE_FILTER_OPACITY:
-      descr = CreatePrimitiveDescription(PrimitiveType::ComponentTransfer, aPrimitiveDescrs);
+      descr = CreatePrimitiveDescription(PrimitiveType::ComponentTransfer,
+                                         aPrimitiveDescrs,
+                                         aInputIsTainted);
       result = SetAttributesForOpacity(descr);
       break;
     case NS_STYLE_FILTER_SATURATE:
-      descr = CreatePrimitiveDescription(PrimitiveType::ColorMatrix, aPrimitiveDescrs);
+      descr = CreatePrimitiveDescription(PrimitiveType::ColorMatrix,
+                                         aPrimitiveDescrs,
+                                         aInputIsTainted);
       result = SetAttributesForSaturate(descr);
       break;
     case NS_STYLE_FILTER_SEPIA:
-      descr = CreatePrimitiveDescription(PrimitiveType::ColorMatrix, aPrimitiveDescrs);
+      descr = CreatePrimitiveDescription(PrimitiveType::ColorMatrix,
+                                         aPrimitiveDescrs,
+                                         aInputIsTainted);
       result = SetAttributesForSepia(descr);
       break;
     default:
       NS_NOTREACHED("not a valid CSS filter type");
       return NS_ERROR_FAILURE;
   }
 
   if (NS_FAILED(result)) {
@@ -101,21 +122,22 @@ nsCSSFilterInstance::BuildPrimitives(nsT
 
   // Add this primitive to the filter chain.
   aPrimitiveDescrs.AppendElement(descr);
   return NS_OK;
 }
 
 FilterPrimitiveDescription
 nsCSSFilterInstance::CreatePrimitiveDescription(PrimitiveType aType,
-                                                const nsTArray<FilterPrimitiveDescription>& aPrimitiveDescrs) {
+                                                const nsTArray<FilterPrimitiveDescription>& aPrimitiveDescrs,
+                                                bool aInputIsTainted) {
   FilterPrimitiveDescription descr(aType);
   int32_t inputIndex = GetLastResultIndex(aPrimitiveDescrs);
   descr.SetInputPrimitive(0, inputIndex);
-  descr.SetIsTainted(inputIndex < 0 ? true : aPrimitiveDescrs[inputIndex].IsTainted());
+  descr.SetIsTainted(inputIndex < 0 ? aInputIsTainted : aPrimitiveDescrs[inputIndex].IsTainted());
   descr.SetInputColorSpace(0, ColorSpace::SRGB);
   descr.SetOutputColorSpace(ColorSpace::SRGB);
   return descr;
 }
 
 nsresult
 nsCSSFilterInstance::SetAttributesForBlur(FilterPrimitiveDescription& aDescr)
 {
--- a/layout/svg/nsCSSFilterInstance.h
+++ b/layout/svg/nsCSSFilterInstance.h
@@ -45,25 +45,35 @@ public:
                       nscolor aShadowFallbackColor,
                       const nsIntRect& aTargetBoundsInFilterSpace,
                       const gfxMatrix& aFrameSpaceInCSSPxToFilterSpaceTransform);
 
   /**
    * Creates at least one new FilterPrimitiveDescription based on the filter
    * from the style system. Appends the new FilterPrimitiveDescription(s) to the
    * aPrimitiveDescrs list.
+   * aInputIsTainted describes whether the input to this filter is tainted, i.e.
+   * whether it contains security-sensitive content. This is needed to propagate
+   * taintedness to the FilterPrimitive that take tainted inputs. Something being
+   * tainted means that it contains security sensitive content.
+   * The input to this filter is the previous filter's output, i.e. the last
+   * element in aPrimitiveDescrs, or the SourceGraphic input if this is the first
+   * filter in the filter chain.
    */
-  nsresult BuildPrimitives(nsTArray<FilterPrimitiveDescription>& aPrimitiveDescrs);
+  nsresult BuildPrimitives(nsTArray<FilterPrimitiveDescription>& aPrimitiveDescrs,
+                           bool aInputIsTainted);
 
 private:
   /**
    * Returns a new FilterPrimitiveDescription with its basic properties set up.
+   * See the comment above BuildPrimitives for the meaning of aInputIsTainted.
    */
   FilterPrimitiveDescription CreatePrimitiveDescription(PrimitiveType aType,
-                                                        const nsTArray<FilterPrimitiveDescription>& aPrimitiveDescrs);
+                                                        const nsTArray<FilterPrimitiveDescription>& aPrimitiveDescrs,
+                                                        bool aInputIsTainted);
 
   /**
    * Sets aDescr's attributes using the style info in mFilter.
    */
   nsresult SetAttributesForBlur(FilterPrimitiveDescription& aDescr);
   nsresult SetAttributesForBrightness(FilterPrimitiveDescription& aDescr);
   nsresult SetAttributesForContrast(FilterPrimitiveDescription& aDescr);
   nsresult SetAttributesForDropShadow(FilterPrimitiveDescription& aDescr);
--- a/layout/svg/nsFilterInstance.cpp
+++ b/layout/svg/nsFilterInstance.cpp
@@ -27,24 +27,25 @@
 
 using namespace mozilla;
 using namespace mozilla::dom;
 using namespace mozilla::gfx;
 
 FilterDescription
 nsFilterInstance::GetFilterDescription(nsIContent* aFilteredElement,
                                        const nsTArray<nsStyleFilter>& aFilterChain,
+                                       bool aFilterInputIsTainted,
                                        const UserSpaceMetrics& aMetrics,
                                        const gfxRect& aBBox,
                                        nsTArray<RefPtr<SourceSurface>>& aOutAdditionalImages)
 {
   gfxMatrix unused; // aPaintTransform arg not used since we're not painting
   nsFilterInstance instance(nullptr, aFilteredElement, aMetrics,
-                            aFilterChain, nullptr, unused,
-                            nullptr, nullptr, nullptr, &aBBox);
+                            aFilterChain, aFilterInputIsTainted, nullptr,
+                            unused, nullptr, nullptr, nullptr, &aBBox);
   if (!instance.IsInitialized()) {
     return FilterDescription();
   }
   return instance.ExtractDescriptionAndAdditionalImages(aOutAdditionalImages);
 }
 
 static UniquePtr<UserSpaceMetrics>
 UserSpaceMetricsForFrame(nsIFrame* aFrame)
@@ -60,19 +61,21 @@ nsresult
 nsFilterInstance::PaintFilteredFrame(nsIFrame *aFilteredFrame,
                                      DrawTarget* aDrawTarget,
                                      const gfxMatrix& aTransform,
                                      nsSVGFilterPaintCallback *aPaintCallback,
                                      const nsRegion *aDirtyArea)
 {
   auto& filterChain = aFilteredFrame->StyleEffects()->mFilters;
   UniquePtr<UserSpaceMetrics> metrics = UserSpaceMetricsForFrame(aFilteredFrame);
+  // Hardcode InputIsTainted to true because we don't want JS to be able to
+  // read the rendered contents of aFilteredFrame.
   nsFilterInstance instance(aFilteredFrame, aFilteredFrame->GetContent(), *metrics,
-                            filterChain, aPaintCallback, aTransform,
-                            aDirtyArea, nullptr, nullptr, nullptr);
+                            filterChain, /* InputIsTainted */ true, aPaintCallback,
+                            aTransform, aDirtyArea, nullptr, nullptr, nullptr);
   if (!instance.IsInitialized()) {
     return NS_OK;
   }
   return instance.Render(aDrawTarget);
 }
 
 nsRegion
 nsFilterInstance::GetPostFilterDirtyArea(nsIFrame *aFilteredFrame,
@@ -80,19 +83,21 @@ nsFilterInstance::GetPostFilterDirtyArea
 {
   if (aPreFilterDirtyRegion.IsEmpty()) {
     return nsRegion();
   }
 
   gfxMatrix unused; // aPaintTransform arg not used since we're not painting
   auto& filterChain = aFilteredFrame->StyleEffects()->mFilters;
   UniquePtr<UserSpaceMetrics> metrics = UserSpaceMetricsForFrame(aFilteredFrame);
+  // Hardcode InputIsTainted to true because we don't want JS to be able to
+  // read the rendered contents of aFilteredFrame.
   nsFilterInstance instance(aFilteredFrame, aFilteredFrame->GetContent(), *metrics,
-                            filterChain, nullptr, unused, nullptr,
-                            &aPreFilterDirtyRegion);
+                            filterChain, /* InputIsTainted */ true, nullptr, unused,
+                            nullptr, &aPreFilterDirtyRegion);
   if (!instance.IsInitialized()) {
     return nsRegion();
   }
 
   // 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.
   return instance.ComputePostFilterDirtyRegion();
@@ -100,18 +105,20 @@ nsFilterInstance::GetPostFilterDirtyArea
 
 nsRegion
 nsFilterInstance::GetPreFilterNeededArea(nsIFrame *aFilteredFrame,
                                          const nsRegion& aPostFilterDirtyRegion)
 {
   gfxMatrix unused; // aPaintTransform arg not used since we're not painting
   auto& filterChain = aFilteredFrame->StyleEffects()->mFilters;
   UniquePtr<UserSpaceMetrics> metrics = UserSpaceMetricsForFrame(aFilteredFrame);
+  // Hardcode InputIsTainted to true because we don't want JS to be able to
+  // read the rendered contents of aFilteredFrame.
   nsFilterInstance instance(aFilteredFrame, aFilteredFrame->GetContent(), *metrics,
-                            filterChain, nullptr, unused,
+                            filterChain, /* InputIsTainted */ true, nullptr, unused,
                             &aPostFilterDirtyRegion);
   if (!instance.IsInitialized()) {
     return nsRect();
   }
 
   // Now we can ask the instance to compute the area of the source
   // that's needed.
   return instance.ComputeSourceNeededRect();
@@ -131,31 +138,34 @@ nsFilterInstance::GetPostFilterBounds(ns
   if (aPreFilterBounds) {
     preFilterRegion = *aPreFilterBounds;
     preFilterRegionPtr = &preFilterRegion;
   }
 
   gfxMatrix unused; // aPaintTransform arg not used since we're not painting
   auto& filterChain = aFilteredFrame->StyleEffects()->mFilters;
   UniquePtr<UserSpaceMetrics> metrics = UserSpaceMetricsForFrame(aFilteredFrame);
+  // Hardcode InputIsTainted to true because we don't want JS to be able to
+  // read the rendered contents of aFilteredFrame.
   nsFilterInstance instance(aFilteredFrame, aFilteredFrame->GetContent(), *metrics,
-                            filterChain, nullptr, unused, nullptr,
-                            preFilterRegionPtr, aPreFilterBounds,
+                            filterChain, /* InputIsTainted */ true, nullptr, unused,
+                            nullptr, preFilterRegionPtr, aPreFilterBounds,
                             aOverrideBBox);
   if (!instance.IsInitialized()) {
     return nsRect();
   }
 
   return instance.ComputePostFilterExtents();
 }
 
 nsFilterInstance::nsFilterInstance(nsIFrame *aTargetFrame,
                                    nsIContent* aTargetContent,
                                    const UserSpaceMetrics& aMetrics,
                                    const nsTArray<nsStyleFilter>& aFilterChain,
+                                   bool aFilterInputIsTainted,
                                    nsSVGFilterPaintCallback *aPaintCallback,
                                    const gfxMatrix& aPaintTransform,
                                    const nsRegion *aPostFilterDirtyRegion,
                                    const nsRegion *aPreFilterDirtyRegion,
                                    const nsRect *aPreFilterVisualOverflowRectOverride,
                                    const gfxRect *aOverrideBBox)
   : mTargetFrame(aTargetFrame)
   , mTargetContent(aTargetContent)
@@ -208,17 +218,17 @@ nsFilterInstance::nsFilterInstance(nsIFr
       FrameSpaceToFilterSpace(aPreFilterVisualOverflowRectOverride);
   } else if (mTargetFrame) {
     nsRect preFilterVOR = mTargetFrame->GetPreEffectsVisualOverflowRect();
     targetBounds = FrameSpaceToFilterSpace(&preFilterVOR);
   }
   mTargetBounds.UnionRect(mTargetBBoxInFilterSpace, targetBounds);
 
   // Build the filter graph.
-  rv = BuildPrimitives(aFilterChain, aTargetFrame);
+  rv = BuildPrimitives(aFilterChain, aTargetFrame, aFilterInputIsTainted);
   if (NS_FAILED(rv)) {
     return;
   }
 
   if (mPrimitiveDescriptions.IsEmpty()) {
     // Nothing should be rendered.
     return;
   }
@@ -269,66 +279,72 @@ nsFilterInstance::FilterSpaceToUserSpace
   gfxRect userSpaceRect = aFilterSpaceRect;
   userSpaceRect.Scale(mFilterSpaceToUserSpaceScale.width,
                       mFilterSpaceToUserSpaceScale.height);
   return userSpaceRect;
 }
 
 nsresult
 nsFilterInstance::BuildPrimitives(const nsTArray<nsStyleFilter>& aFilterChain,
-                                  nsIFrame* aTargetFrame)
+                                  nsIFrame* aTargetFrame,
+                                  bool aFilterInputIsTainted)
 {
   NS_ASSERTION(!mPrimitiveDescriptions.Length(),
                "expected to start building primitives from scratch");
 
   for (uint32_t i = 0; i < aFilterChain.Length(); i++) {
-    nsresult rv = BuildPrimitivesForFilter(aFilterChain[i], aTargetFrame);
+    bool inputIsTainted =
+      mPrimitiveDescriptions.IsEmpty() ? aFilterInputIsTainted :
+        mPrimitiveDescriptions.LastElement().IsTainted();
+    nsresult rv = BuildPrimitivesForFilter(aFilterChain[i], aTargetFrame, inputIsTainted);
     if (NS_FAILED(rv)) {
       return rv;
     }
   }
 
   mFilterDescription = FilterDescription(mPrimitiveDescriptions);
 
   return NS_OK;
 }
 
 nsresult
 nsFilterInstance::BuildPrimitivesForFilter(const nsStyleFilter& aFilter,
-                                           nsIFrame* aTargetFrame)
+                                           nsIFrame* aTargetFrame,
+                                           bool aInputIsTainted)
 {
   NS_ASSERTION(mUserSpaceToFilterSpaceScale.width > 0.0f &&
                mFilterSpaceToUserSpaceScale.height > 0.0f,
                "scale factors between spaces should be positive values");
 
   if (aFilter.GetType() == NS_STYLE_FILTER_URL) {
     // Build primitives for an SVG filter.
     nsSVGFilterInstance svgFilterInstance(aFilter, aTargetFrame,
                                           mTargetContent,
                                           mMetrics, mTargetBBox,
                                           mUserSpaceToFilterSpaceScale,
                                           mFilterSpaceToUserSpaceScale);
     if (!svgFilterInstance.IsInitialized()) {
       return NS_ERROR_FAILURE;
     }
 
-    return svgFilterInstance.BuildPrimitives(mPrimitiveDescriptions, mInputImages);
+    return svgFilterInstance.BuildPrimitives(mPrimitiveDescriptions, mInputImages,
+                                             aInputIsTainted);
   }
 
   // Build primitives for a CSS filter.
 
   // If we don't have a frame, use opaque black for shadows with unspecified
   // shadow colors.
   nscolor shadowFallbackColor =
     mTargetFrame ? mTargetFrame->StyleColor()->mColor : NS_RGB(0,0,0);
 
   nsCSSFilterInstance cssFilterInstance(aFilter, shadowFallbackColor,
                                         mTargetBounds,
                                         mFrameSpaceInCSSPxToFilterSpaceTransform);
-  return cssFilterInstance.BuildPrimitives(mPrimitiveDescriptions);
+  return cssFilterInstance.BuildPrimitives(mPrimitiveDescriptions, aInputIsTainted);
 }
 
 void
 nsFilterInstance::ComputeNeededBoxes()
 {
   if (mPrimitiveDescriptions.IsEmpty())
     return;
 
--- a/layout/svg/nsFilterInstance.h
+++ b/layout/svg/nsFilterInstance.h
@@ -55,22 +55,29 @@ class nsFilterInstance
   typedef mozilla::gfx::FilterPrimitiveDescription FilterPrimitiveDescription;
   typedef mozilla::gfx::FilterDescription FilterDescription;
   typedef mozilla::dom::UserSpaceMetrics UserSpaceMetrics;
 
 public:
   /**
    * Create a FilterDescription for the supplied filter. All coordinates in
    * the description are in filter space.
+   * @param aFilterInputIsTainted Describes whether the SourceImage / SourceAlpha
+   *   input is tainted. This affects whether feDisplacementMap will respect
+   *   the filter input as its map input, and it affects the IsTainted() state
+   *   on the filter primitives in the FilterDescription. "Tainted" is a term
+   *   from the filters spec and means security-sensitive content, i.e. pixels
+   *   that JS should not be able to read in any way.
    * @param aOutAdditionalImages Will contain additional images needed to
    *   render the filter (from feImage primitives).
    * @return A FilterDescription describing the filter.
    */
   static FilterDescription GetFilterDescription(nsIContent* aFilteredElement,
                                                 const nsTArray<nsStyleFilter>& aFilterChain,
+                                                bool aFilterInputIsTainted,
                                                 const UserSpaceMetrics& aMetrics,
                                                 const gfxRect& aBBox,
                                                 nsTArray<RefPtr<SourceSurface>>& aOutAdditionalImages);
 
   /**
    * Paint the given filtered frame.
    * @param aDirtyArea The area than needs to be painted, in aFilteredFrame's
    *   frame space (i.e. relative to its origin, the top-left corner of its
@@ -113,16 +120,19 @@ public:
                                     const nsRect *aPreFilterBounds = nullptr);
 
   /**
    * @param aTargetFrame The frame of the filtered element under consideration,
    *   may be null.
    * @param aTargetContent The filtered element itself.
    * @param aMetrics The metrics to resolve SVG lengths against.
    * @param aFilterChain The list of filters to apply.
+   * @param aFilterInputIsTainted Describes whether the SourceImage / SourceAlpha
+   *   input is tainted. This affects whether feDisplacementMap will respect
+   *   the filter input as its map input.
    * @param aPaintCallback [optional] The callback that Render() should use to
    *   paint. Only required if you will call Render().
    * @param aPaintTransform The transform to apply to convert to
    *   aTargetFrame's SVG user space. Only used when painting.
    * @param aPostFilterDirtyRegion [optional] The post-filter area
    *   that has to be repainted, in app units. Only required if you will
    *   call ComputeSourceNeededRect() or Render().
    * @param aPreFilterDirtyRegion [optional] The pre-filter area of
@@ -132,16 +142,17 @@ public:
    *   visual overflow rect for the target element.
    * @param aOverrideBBox [optional] Use a different SVG bbox for the target
    *   element. Must be non-null if aTargetFrame is null.
    */
   nsFilterInstance(nsIFrame *aTargetFrame,
                    nsIContent* aTargetContent,
                    const UserSpaceMetrics& aMetrics,
                    const nsTArray<nsStyleFilter>& aFilterChain,
+                   bool aFilterInputIsTainted,
                    nsSVGFilterPaintCallback *aPaintCallback,
                    const gfxMatrix& aPaintTransform,
                    const nsRegion *aPostFilterDirtyRegion = nullptr,
                    const nsRegion *aPreFilterDirtyRegion = nullptr,
                    const nsRect *aOverridePreFilterVisualOverflowRect = nullptr,
                    const gfxRect *aOverrideBBox = nullptr);
 
   /**
@@ -231,28 +242,32 @@ private:
    * Creates the SourceSurface for the SourceGraphic graph node, paints its
    * contents, and assigns it to mSourceGraphic.mSourceSurface.
    */
   nsresult BuildSourceImage(DrawTarget* aTargetDT);
 
   /**
    * Build the list of FilterPrimitiveDescriptions that describes the filter's
    * filter primitives and their connections. This populates
-   * mPrimitiveDescriptions and mInputImages.
+   * mPrimitiveDescriptions and mInputImages. aFilterInputIsTainted describes
+   * whether the SourceGraphic is tainted.
    */
   nsresult BuildPrimitives(const nsTArray<nsStyleFilter>& aFilterChain,
-                           nsIFrame* aTargetFrame);
+                           nsIFrame* aTargetFrame,
+                           bool aFilterInputIsTainted);
 
   /**
    * Add to the list of FilterPrimitiveDescriptions for a particular SVG
-   * reference filter or CSS filter. This populates mPrimitiveDescrs and
-   * mInputImages.
+   * reference filter or CSS filter. This populates mPrimitiveDescriptions and
+   * mInputImages. aInputIsTainted describes whether the input to aFilter is
+   * tainted.
    */
   nsresult BuildPrimitivesForFilter(const nsStyleFilter& aFilter,
-                                    nsIFrame* aTargetFrame);
+                                    nsIFrame* aTargetFrame,
+                                    bool aInputIsTainted);
 
   /**
    * Computes the filter space bounds of the areas that we actually *need* from
    * the filter sources, based on the value of mPostFilterDirtyRegion.
    * This sets mNeededBounds on the corresponding SourceInfo structs.
    */
   void ComputeNeededBoxes();
 
--- a/layout/svg/nsSVGFilterInstance.cpp
+++ b/layout/svg/nsSVGFilterInstance.cpp
@@ -361,33 +361,20 @@ nsSVGFilterInstance::GetSourceIndices(ns
         return NS_ERROR_FAILURE;
     }
 
     aSourceIndices.AppendElement(sourceIndex);
   }
   return NS_OK;
 }
 
-static bool
-IsFilterInputTainted(nsIContent* aElement)
-{
-  // When the filter is applied during canvas drawing, we might be allowed to
-  // read from the canvas.
-  if (HTMLCanvasElement* canvas =
-        HTMLCanvasElement::FromContentOrNull(aElement)) {
-    return canvas->IsWriteOnly();
-  }
-
-  // Always treat normal filtered elements as tainted.
-  return true;
-}
-
 nsresult
 nsSVGFilterInstance::BuildPrimitives(nsTArray<FilterPrimitiveDescription>& aPrimitiveDescrs,
-                                     nsTArray<RefPtr<SourceSurface>>& aInputImages)
+                                     nsTArray<RefPtr<SourceSurface>>& aInputImages,
+                                     bool aInputIsTainted)
 {
   mSourceGraphicIndex = GetLastResultIndex(aPrimitiveDescrs);
 
   // Clip previous filter's output to this filter's filter region.
   if (mSourceGraphicIndex >= 0) {
     FilterPrimitiveDescription& sourceDescr = aPrimitiveDescrs[mSourceGraphicIndex];
     sourceDescr.SetPrimitiveSubregion(sourceDescr.PrimitiveSubregion().Intersect(mFilterSpaceBounds));
   }
@@ -405,34 +392,32 @@ nsSVGFilterInstance::BuildPrimitives(nsT
   }
 
   // Maps source image name to source index.
   nsDataHashtable<nsStringHashKey, int32_t> imageTable(8);
 
   // The principal that we check principals of any loaded images against.
   nsCOMPtr<nsIPrincipal> principal = mTargetContent->NodePrincipal();
 
-  bool filterInputIsTainted = IsFilterInputTainted(mTargetContent);
-
   for (uint32_t primitiveElementIndex = 0;
        primitiveElementIndex < primitives.Length();
        ++primitiveElementIndex) {
     nsSVGFE* filter = primitives[primitiveElementIndex];
 
     AutoTArray<int32_t,2> sourceIndices;
     nsresult rv = GetSourceIndices(filter, aPrimitiveDescrs, imageTable, sourceIndices);
     if (NS_FAILED(rv)) {
       return rv;
     }
 
     IntRect primitiveSubregion =
       ComputeFilterPrimitiveSubregion(filter, aPrimitiveDescrs, sourceIndices);
 
     nsTArray<bool> sourcesAreTainted;
-    GetInputsAreTainted(aPrimitiveDescrs, sourceIndices, filterInputIsTainted, sourcesAreTainted);
+    GetInputsAreTainted(aPrimitiveDescrs, sourceIndices, aInputIsTainted, sourcesAreTainted);
 
     FilterPrimitiveDescription descr =
       filter->GetPrimitiveDescription(this, primitiveSubregion, sourcesAreTainted, aInputImages);
 
     descr.SetIsTainted(filter->OutputIsTainted(sourcesAreTainted, principal));
     descr.SetFilterSpaceBounds(mFilterSpaceBounds);
     descr.SetPrimitiveSubregion(primitiveSubregion.Intersect(descr.FilterSpaceBounds()));
 
--- a/layout/svg/nsSVGFilterInstance.h
+++ b/layout/svg/nsSVGFilterInstance.h
@@ -93,19 +93,27 @@ public:
    */
   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.
+   * aInputIsTainted describes whether the input to this filter is tainted, i.e.
+   * whether it contains security-sensitive content. This is needed to propagate
+   * taintedness to the FilterPrimitive that take tainted inputs. Something being
+   * tainted means that it contains security sensitive content.
+   * The input to this filter is the previous filter's output, i.e. the last
+   * element in aPrimitiveDescrs, or the SourceGraphic input if this is the first
+   * filter in the filter chain.
    */
   nsresult BuildPrimitives(nsTArray<FilterPrimitiveDescription>& aPrimitiveDescrs,
-                           nsTArray<RefPtr<SourceSurface>>& aInputImages);
+                           nsTArray<RefPtr<SourceSurface>>& aInputImages,
+                           bool aInputIsTainted);
 
   /**
    * 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 mUserSpaceBounds; }