Bug 1174278 - Make filter tainting and canvas tainting. r=jrmuizel
authorTobias Schneider <schneider@jancona.com>
Tue, 24 May 2016 21:50:02 -0700
changeset 299187 f43c78d7cc1393e2b88488e85d082d4dd440fb9b
parent 299186 d3bde9a513bb8b33a97bf6c2e5d451a133e59192
child 299188 a7d611896f386cf32e61b11a69a8f29ce76c866e
push id30291
push userryanvm@gmail.com
push dateFri, 27 May 2016 01:45:30 +0000
treeherdermozilla-central@4d63dde701b4 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjrmuizel
bugs1174278
milestone49.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 1174278 - Make filter tainting and canvas tainting. r=jrmuizel
dom/canvas/CanvasRenderingContext2D.cpp
dom/canvas/CanvasRenderingContext2D.h
layout/svg/nsSVGFilterInstance.cpp
layout/svg/nsSVGFilterInstance.h
--- a/dom/canvas/CanvasRenderingContext2D.cpp
+++ b/dom/canvas/CanvasRenderingContext2D.cpp
@@ -314,16 +314,21 @@ public:
     mFinalTarget = aFinalTarget;
     mPostFilterBounds = aPostFilterBounds;
     mOffset = aFilterSpaceToTargetOffset;
 
     nsIntRegion sourceGraphicNeededRegion;
     nsIntRegion fillPaintNeededRegion;
     nsIntRegion strokePaintNeededRegion;
 
+    if (aCtx->CurrentState().updateFilterOnWriteOnly) {
+      aCtx->UpdateFilter();
+      aCtx->CurrentState().updateFilterOnWriteOnly = false;
+    }
+
     FilterSupport::ComputeSourceNeededRegions(
       aCtx->CurrentState().filter, mPostFilterBounds,
       sourceGraphicNeededRegion, fillPaintNeededRegion, strokePaintNeededRegion);
 
     mSourceGraphicRect = sourceGraphicNeededRegion.GetBounds();
     mFillPaintRect = fillPaintNeededRegion.GetBounds();
     mStrokePaintRect = strokePaintNeededRegion.GetBounds();
 
@@ -400,16 +405,22 @@ public:
       mFinalTarget, mCtx->CurrentState().filter,
       gfx::Rect(mPostFilterBounds),
       snapshot, mSourceGraphicRect,
       fillPaint, mFillPaintRect,
       strokePaint, mStrokePaintRect,
       mCtx->CurrentState().filterAdditionalImages,
       mPostFilterBounds.TopLeft() - mOffset,
       DrawOptions(1.0f, mCompositionOp));
+
+    const gfx::FilterDescription& filter = mCtx->CurrentState().filter;
+    MOZ_ASSERT(!filter.mPrimitives.IsEmpty());
+    if (filter.mPrimitives.LastElement().IsTainted() && mCtx->mCanvasElement) {
+      mCtx->mCanvasElement->SetWriteOnly();
+    }
   }
 
   DrawTarget* DT()
   {
     return mTarget;
   }
 
 private:
@@ -2463,16 +2474,19 @@ CanvasRenderingContext2D::SetFilter(cons
     filterChain.SwapElements(CurrentState().filterChain);
     if (mCanvasElement) {
       CurrentState().filterChainObserver =
         new CanvasFilterChainObserver(CurrentState().filterChain,
                                       mCanvasElement, this);
       UpdateFilter();
     }
   }
+  if (mCanvasElement && !mCanvasElement->IsWriteOnly()) {
+    CurrentState().updateFilterOnWriteOnly = true;
+  }
 }
 
 class CanvasUserSpaceMetrics : public UserSpaceMetricsWithSize
 {
 public:
   CanvasUserSpaceMetrics(const gfx::IntSize& aSize, const nsFont& aFont,
                          nsIAtom* aFontLanguage, bool aExplicitLanguage,
                          nsPresContext* aPresContext)
--- a/dom/canvas/CanvasRenderingContext2D.h
+++ b/dom/canvas/CanvasRenderingContext2D.h
@@ -907,16 +907,17 @@ protected:
                      globalAlpha(1.0f),
                      shadowBlur(0.0),
                      dashOffset(0.0f),
                      op(mozilla::gfx::CompositionOp::OP_OVER),
                      fillRule(mozilla::gfx::FillRule::FILL_WINDING),
                      lineCap(mozilla::gfx::CapStyle::BUTT),
                      lineJoin(mozilla::gfx::JoinStyle::MITER_OR_BEVEL),
                      filterString(MOZ_UTF16("none")),
+                     updateFilterOnWriteOnly(false),
                      imageSmoothingEnabled(true),
                      fontExplicitLanguage(false)
     { }
 
     ContextState(const ContextState& aOther)
         : fontGroup(aOther.fontGroup),
           fontLanguage(aOther.fontLanguage),
           fontFont(aOther.fontFont),
@@ -939,16 +940,17 @@ protected:
           fillRule(aOther.fillRule),
           lineCap(aOther.lineCap),
           lineJoin(aOther.lineJoin),
           filterString(aOther.filterString),
           filterChain(aOther.filterChain),
           filterChainObserver(aOther.filterChainObserver),
           filter(aOther.filter),
           filterAdditionalImages(aOther.filterAdditionalImages),
+          updateFilterOnWriteOnly(aOther.updateFilterOnWriteOnly),
           imageSmoothingEnabled(aOther.imageSmoothingEnabled),
           fontExplicitLanguage(aOther.fontExplicitLanguage)
     { }
 
     void SetColorStyle(Style aWhichStyle, nscolor aColor)
     {
       colorStyles[aWhichStyle] = aColor;
       gradientStyles[aWhichStyle] = nullptr;
@@ -1016,16 +1018,17 @@ protected:
     mozilla::gfx::CapStyle lineCap;
     mozilla::gfx::JoinStyle lineJoin;
 
     nsString filterString;
     nsTArray<nsStyleFilter> filterChain;
     RefPtr<nsSVGFilterChainObserver> filterChainObserver;
     mozilla::gfx::FilterDescription filter;
     nsTArray<RefPtr<mozilla::gfx::SourceSurface>> filterAdditionalImages;
+    bool updateFilterOnWriteOnly;
 
     bool imageSmoothingEnabled;
     bool fontExplicitLanguage;
   };
 
   AutoTArray<ContextState, 3> mStyleStack;
 
   inline ContextState& CurrentState() {
--- a/layout/svg/nsSVGFilterInstance.cpp
+++ b/layout/svg/nsSVGFilterInstance.cpp
@@ -5,16 +5,17 @@
 
 // Main header first:
 #include "nsSVGFilterInstance.h"
 
 // Keep others in (case-insensitive) order:
 #include "gfxPlatform.h"
 #include "gfxUtils.h"
 #include "nsISVGChildFrame.h"
+#include "mozilla/dom/HTMLCanvasElement.h"
 #include "mozilla/dom/SVGFilterElement.h"
 #include "nsReferencedElement.h"
 #include "nsSVGFilterFrame.h"
 #include "nsSVGUtils.h"
 #include "SVGContentUtils.h"
 #include "FilterSupport.h"
 #include "gfx2DGlue.h"
 
@@ -257,23 +258,23 @@ nsSVGFilterInstance::ComputeFilterPrimit
   // in the region.
   region.RoundOut();
   return RoundedToInt(region);
 }
 
 void
 nsSVGFilterInstance::GetInputsAreTainted(const nsTArray<FilterPrimitiveDescription>& aPrimitiveDescrs,
                                          const nsTArray<int32_t>& aInputIndices,
+                                         bool aFilterInputIsTainted,
                                          nsTArray<bool>& aOutInputsAreTainted)
 {
   for (uint32_t i = 0; i < aInputIndices.Length(); i++) {
     int32_t inputIndex = aInputIndices[i];
     if (inputIndex < 0) {
-      // SourceGraphic, SourceAlpha, FillPaint and StrokePaint are tainted.
-      aOutInputsAreTainted.AppendElement(true);
+      aOutInputsAreTainted.AppendElement(aFilterInputIsTainted);
     } else {
       aOutInputsAreTainted.AppendElement(aPrimitiveDescrs[inputIndex].IsTainted());
     }
   }
 }
 
 static int32_t
 GetLastResultIndex(const nsTArray<FilterPrimitiveDescription>& aPrimitiveDescrs)
@@ -354,16 +355,30 @@ 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)
 {
   mSourceGraphicIndex = GetLastResultIndex(aPrimitiveDescrs);
 
   // Clip previous filter's output to this filter's filter region.
   if (mSourceGraphicIndex >= 0) {
@@ -384,32 +399,34 @@ 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, sourcesAreTainted);
+    GetInputsAreTainted(aPrimitiveDescrs, sourceIndices, filterInputIsTainted, 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
@@ -150,16 +150,17 @@ private:
                                           const nsTArray<int32_t>& aInputIndices);
 
   /**
    * Takes the input indices of a filter primitive and returns for each input
    * whether the input's output is tainted.
    */
   void GetInputsAreTainted(const nsTArray<FilterPrimitiveDescription>& aPrimitiveDescrs,
                            const nsTArray<int32_t>& aInputIndices,
+                           bool aFilterInputIsTainted,
                            nsTArray<bool>& aOutInputsAreTainted);
 
   /**
    * Scales a numeric filter primitive length in the X, Y or "XY" directions
    * into a length in filter space (no offset is applied).
    */
   float GetPrimitiveNumber(uint8_t aCtxType, float aValue) const;