Bug 685518 part 1. Look at the image CORS mode for drawImage into a canvas 2d context. r=roc
authorBoris Zbarsky <bzbarsky@mit.edu>
Fri, 09 Sep 2011 17:58:35 -0400
changeset 76848 cb9eced21ea1b7aabfa358b700e34553c96334e5
parent 76847 09e96590b9de8f04dac3a154a231eae7dd20ba31
child 76849 5946c12881270d778e750dd76ba748c31218a02e
push id3
push userfelipc@gmail.com
push dateFri, 30 Sep 2011 20:09:13 +0000
reviewersroc
bugs685518
milestone9.0a1
Bug 685518 part 1. Look at the image CORS mode for drawImage into a canvas 2d context. r=roc
content/canvas/src/CanvasUtils.cpp
content/canvas/src/CanvasUtils.h
content/canvas/src/WebGLContextGL.cpp
content/canvas/src/nsCanvasRenderingContext2D.cpp
content/canvas/src/nsCanvasRenderingContext2DAzure.cpp
layout/base/nsLayoutUtils.cpp
layout/base/nsLayoutUtils.h
--- a/content/canvas/src/CanvasUtils.cpp
+++ b/content/canvas/src/CanvasUtils.cpp
@@ -60,17 +60,18 @@
 #include "mozilla/gfx/Matrix.h"
 
 namespace mozilla {
 namespace CanvasUtils {
 
 void
 DoDrawImageSecurityCheck(nsHTMLCanvasElement *aCanvasElement,
                          nsIPrincipal *aPrincipal,
-                         PRBool forceWriteOnly)
+                         PRBool forceWriteOnly,
+                         PRBool CORSUsed)
 {
     // Callers should ensure that mCanvasElement is non-null before calling this
     if (!aCanvasElement) {
         NS_WARNING("DoDrawImageSecurityCheck called without canvas element!");
         return;
     }
 
     if (aCanvasElement->IsWriteOnly())
@@ -80,16 +81,20 @@ DoDrawImageSecurityCheck(nsHTMLCanvasEle
     if (forceWriteOnly) {
         aCanvasElement->SetWriteOnly();
         return;
     }
 
     if (aPrincipal == nsnull)
         return;
 
+    // No need to do a security check if the image used CORS for the load
+    if (CORSUsed)
+        return;
+
     PRBool subsumes;
     nsresult rv =
         aCanvasElement->NodePrincipal()->Subsumes(aPrincipal, &subsumes);
 
     if (NS_SUCCEEDED(rv) && subsumes) {
         // This canvas has access to that image anyway
         return;
     }
--- a/content/canvas/src/CanvasUtils.h
+++ b/content/canvas/src/CanvasUtils.h
@@ -69,17 +69,18 @@ inline PRBool CheckSaneSubrectSize(PRInt
         checked_ymost.value() <= realHeight;
 }
 
 // Flag aCanvasElement as write-only if drawing an image with aPrincipal
 // onto it would make it such.
 
 void DoDrawImageSecurityCheck(nsHTMLCanvasElement *aCanvasElement,
                               nsIPrincipal *aPrincipal,
-                              PRBool forceWriteOnly);
+                              PRBool forceWriteOnly,
+                              PRBool CORSUsed);
 
 void LogMessage (const nsCString& errorString);
 void LogMessagef (const char *fmt, ...);
 
 // Make a double out of |v|, treating undefined values as 0.0 (for
 // the sake of sparse arrays).  Return true iff coercion
 // succeeded.
 bool CoerceDouble(jsval v, double* d);
--- a/content/canvas/src/WebGLContextGL.cpp
+++ b/content/canvas/src/WebGLContextGL.cpp
@@ -3579,29 +3579,23 @@ WebGLContext::DOMElementToImageSurface(n
     // To prevent a loophole where a Canvas2D would be used as a proxy to load
     // cross-domain textures, we also disallow loading textures from write-only
     // Canvas2D's.
 
     // part 1: check that the DOM element is same-origin, or has otherwise been
     // validated for cross-domain use.
     // if res.mPrincipal == null, no need for the origin check. See DoDrawImageSecurityCheck.
     // this case happens in the mochitest for images served from mochi.test:8888
-    if (res.mPrincipal) {
+    if (res.mPrincipal && !res.mCORSUsed) {
         PRBool subsumes;
         nsresult rv = HTMLCanvasElement()->NodePrincipal()->Subsumes(res.mPrincipal, &subsumes);
         if (NS_FAILED(rv) || !subsumes) {
-            PRInt32 corsmode;
-            if (!res.mImageRequest || NS_FAILED(res.mImageRequest->GetCORSMode(&corsmode))) {
-                corsmode = imgIRequest::CORS_NONE;
-            }
-            if (corsmode == imgIRequest::CORS_NONE) {
-                LogMessageIfVerbose("It is forbidden to load a WebGL texture from a cross-domain element that has not been validated with CORS. "
-                                    "See https://developer.mozilla.org/en/WebGL/Cross-Domain_Textures");
-                return NS_ERROR_DOM_SECURITY_ERR;
-            }
+            LogMessageIfVerbose("It is forbidden to load a WebGL texture from a cross-domain element that has not been validated with CORS. "
+                                "See https://developer.mozilla.org/en/WebGL/Cross-Domain_Textures");
+            return NS_ERROR_DOM_SECURITY_ERR;
         }
     }
 
     // part 2: if the DOM element is a canvas, check that it's not write-only. That would indicate a tainted canvas,
     // i.e. a canvas that could contain cross-domain image data.
     nsCOMPtr<nsIContent> maybeDOMCanvas = do_QueryInterface(imageOrCanvas);
     if (maybeDOMCanvas && maybeDOMCanvas->IsHTML(nsGkAtoms::canvas)) {
         nsHTMLCanvasElement *canvas = static_cast<nsHTMLCanvasElement*>(maybeDOMCanvas.get());
--- a/content/canvas/src/nsCanvasRenderingContext2D.cpp
+++ b/content/canvas/src/nsCanvasRenderingContext2D.cpp
@@ -241,36 +241,40 @@ NS_INTERFACE_MAP_END
     { 0xb85c6c8a, 0x0624, 0x4530, { 0xb8, 0xee, 0xff, 0xdf, 0x42, 0xe8, 0x21, 0x6d } }
 class nsCanvasPattern : public nsIDOMCanvasPattern
 {
 public:
     NS_DECLARE_STATIC_IID_ACCESSOR(NS_CANVASPATTERN_PRIVATE_IID)
 
     nsCanvasPattern(gfxPattern* pat,
                     nsIPrincipal* principalForSecurityCheck,
-                    PRBool forceWriteOnly)
+                    PRBool forceWriteOnly,
+                    PRBool CORSUsed)
         : mPattern(pat),
           mPrincipal(principalForSecurityCheck),
-          mForceWriteOnly(forceWriteOnly)
+          mForceWriteOnly(forceWriteOnly),
+          mCORSUsed(CORSUsed)
     {
     }
 
-    gfxPattern* GetPattern() {
+    gfxPattern* GetPattern() const {
         return mPattern;
     }
 
-    nsIPrincipal* Principal() { return mPrincipal; }
-    PRBool GetForceWriteOnly() { return mForceWriteOnly; }
+    nsIPrincipal* Principal() const { return mPrincipal; }
+    PRBool GetForceWriteOnly() const { return mForceWriteOnly; }
+    PRBool GetCORSUsed() const { return mCORSUsed; }
 
     NS_DECL_ISUPPORTS
 
 protected:
     nsRefPtr<gfxPattern> mPattern;
     nsCOMPtr<nsIPrincipal> mPrincipal;
-    PRPackedBool mForceWriteOnly;
+    const PRPackedBool mForceWriteOnly;
+    const PRPackedBool mCORSUsed;
 };
 
 NS_DEFINE_STATIC_IID_ACCESSOR(nsCanvasPattern, NS_CANVASPATTERN_PRIVATE_IID)
 
 NS_IMPL_ADDREF(nsCanvasPattern)
 NS_IMPL_RELEASE(nsCanvasPattern)
 
 DOMCI_DATA(CanvasPattern, nsCanvasPattern)
@@ -989,17 +993,18 @@ nsCanvasRenderingContext2D::ApplyStyle(S
         mDirtyStyle[aWhichStyle] = PR_FALSE;
     mLastStyle = aWhichStyle;
 
     nsCanvasPattern* pattern = CurrentState().patternStyles[aWhichStyle];
     if (pattern) {
         if (mCanvasElement)
             CanvasUtils::DoDrawImageSecurityCheck(HTMLCanvasElement(),
                                                   pattern->Principal(),
-                                                  pattern->GetForceWriteOnly());
+                                                  pattern->GetForceWriteOnly(),
+                                                  pattern->GetCORSUsed());
 
         gfxPattern* gpat = pattern->GetPattern();
 
         if (CurrentState().imageSmoothingEnabled)
             gpat->SetFilter(gfxPattern::FILTER_GOOD);
         else
             gpat->SetFilter(gfxPattern::FILTER_NEAREST);
 
@@ -1837,17 +1842,18 @@ nsCanvasRenderingContext2D::CreatePatter
     if (!res.mSurface)
         return NS_ERROR_NOT_AVAILABLE;
 
     nsRefPtr<gfxPattern> thebespat = new gfxPattern(res.mSurface);
 
     thebespat->SetExtend(extend);
 
     nsRefPtr<nsCanvasPattern> pat = new nsCanvasPattern(thebespat, res.mPrincipal,
-                                                        res.mIsWriteOnly);
+                                                        res.mIsWriteOnly,
+                                                        res.mCORSUsed);
     if (!pat)
         return NS_ERROR_OUT_OF_MEMORY;
 
     *_retval = pat.forget().get();
     return NS_OK;
 }
 
 //
@@ -3433,17 +3439,19 @@ nsCanvasRenderingContext2D::DrawImage(ns
                 return NS_ERROR_NOT_AVAILABLE;
         }
 
         imgsurf = res.mSurface.forget();
         imgSize = res.mSize;
 
         if (mCanvasElement) {
             CanvasUtils::DoDrawImageSecurityCheck(HTMLCanvasElement(),
-                                                  res.mPrincipal, res.mIsWriteOnly);
+                                                  res.mPrincipal,
+                                                  res.mIsWriteOnly,
+                                                  res.mCORSUsed);
         }
 
         if (res.mImageRequest) {
             CanvasImageCache::NotifyDrawImage(imgElt, HTMLCanvasElement(),
                                               res.mImageRequest, imgsurf, imgSize);
         }
     }
 
--- a/content/canvas/src/nsCanvasRenderingContext2DAzure.cpp
+++ b/content/canvas/src/nsCanvasRenderingContext2DAzure.cpp
@@ -301,30 +301,33 @@ public:
     REPEATX,
     REPEATY,
     NOREPEAT
   };
 
   nsCanvasPatternAzure(SourceSurface* aSurface,
                        RepeatMode aRepeat,
                        nsIPrincipal* principalForSecurityCheck,
-                       PRBool forceWriteOnly)
+                       PRBool forceWriteOnly,
+                       PRBool CORSUsed)
     : mSurface(aSurface)
     , mRepeat(aRepeat)
     , mPrincipal(principalForSecurityCheck)
     , mForceWriteOnly(forceWriteOnly)
+    , mCORSUsed(CORSUsed)
   {
   }
 
   NS_DECL_ISUPPORTS
 
   RefPtr<SourceSurface> mSurface;
-  RepeatMode mRepeat;
+  const RepeatMode mRepeat;
   nsCOMPtr<nsIPrincipal> mPrincipal;
-  PRPackedBool mForceWriteOnly;
+  const PRPackedBool mForceWriteOnly;
+  const PRPackedBool mCORSUsed;
 };
 
 NS_DEFINE_STATIC_IID_ACCESSOR(nsCanvasPatternAzure, NS_CANVASPATTERNAZURE_PRIVATE_IID)
 
 NS_IMPL_ADDREF(nsCanvasPatternAzure)
 NS_IMPL_RELEASE(nsCanvasPatternAzure)
 
 // XXX
@@ -793,17 +796,18 @@ protected:
 
         mPattern = new (mRadialGradientPattern.addr())
           RadialGradientPattern(gradient->mCenter1, gradient->mCenter2, gradient->mRadius1,
                                 gradient->mRadius2, gradient->GetGradientStopsForTarget(aRT));
       } else if (state.patternStyles[aStyle]) {
         if (aCtx->mCanvasElement) {
           CanvasUtils::DoDrawImageSecurityCheck(aCtx->HTMLCanvasElement(),
                                                 state.patternStyles[aStyle]->mPrincipal,
-                                                state.patternStyles[aStyle]->mForceWriteOnly);
+                                                state.patternStyles[aStyle]->mForceWriteOnly,
+                                                state.patternStyles[aStyle]->mCORSUsed);
         }
 
         ExtendMode mode;
         if (state.patternStyles[aStyle]->mRepeat == nsCanvasPatternAzure::NOREPEAT) {
           mode = EXTEND_CLAMP;
         } else {
           mode = EXTEND_WRAP;
         }
@@ -1907,27 +1911,26 @@ nsCanvasRenderingContext2DAzure::CreateP
   if (canvas) {
     nsIntSize size = canvas->GetSize();
     if (size.width == 0 || size.height == 0) {
       return NS_ERROR_DOM_INVALID_STATE_ERR;
     }
   }
 
   // Special case for Canvas, which could be an Azure canvas!
-  nsCOMPtr<nsINode> node = do_QueryInterface(image);
-  if (canvas && node) {
+  if (canvas) {
     if (canvas->CountContexts() == 1) {
       nsICanvasRenderingContextInternal *srcCanvas = canvas->GetContextAtIndex(0);
 
       // This might not be an Azure canvas!
       if (srcCanvas) {
         RefPtr<SourceSurface> srcSurf = srcCanvas->GetSurfaceSnapshot();
 
         nsRefPtr<nsCanvasPatternAzure> pat =
-          new nsCanvasPatternAzure(srcSurf, repeatMode, node->NodePrincipal(), canvas->IsWriteOnly());
+          new nsCanvasPatternAzure(srcSurf, repeatMode, content->NodePrincipal(), canvas->IsWriteOnly(), PR_FALSE);
 
         *_retval = pat.forget().get();
         return NS_OK;
       }
     }
   }
 
   // The canvas spec says that createPattern should use the first frame
@@ -1944,17 +1947,18 @@ nsCanvasRenderingContext2DAzure::CreateP
   if (!res.mSurface->CairoSurface()) {
     return NS_OK;
   }
 
   RefPtr<SourceSurface> srcSurf =
     gfxPlatform::GetPlatform()->GetSourceSurfaceForSurface(mTarget, res.mSurface);
 
   nsRefPtr<nsCanvasPatternAzure> pat =
-    new nsCanvasPatternAzure(srcSurf, repeatMode, res.mPrincipal, res.mIsWriteOnly);
+    new nsCanvasPatternAzure(srcSurf, repeatMode, res.mPrincipal,
+                             res.mIsWriteOnly, res.mCORSUsed);
 
   *_retval = pat.forget().get();
   return NS_OK;
 }
 
 //
 // shadows
 //
@@ -3695,17 +3699,18 @@ nsCanvasRenderingContext2DAzure::DrawIma
       return NS_OK;
     }
 
     imgsurf = res.mSurface.forget();
     imgSize = res.mSize;
 
     if (mCanvasElement) {
       CanvasUtils::DoDrawImageSecurityCheck(HTMLCanvasElement(),
-                                            res.mPrincipal, res.mIsWriteOnly);
+                                            res.mPrincipal, res.mIsWriteOnly,
+                                            res.mCORSUsed);
     }
 
     if (res.mImageRequest) {
       CanvasImageCache::NotifyDrawImage(imgElt, HTMLCanvasElement(),
                                         res.mImageRequest, imgsurf, imgSize);
     }
 
     srcSurf = gfxPlatform::GetPlatform()->GetSourceSurfaceForSurface(mTarget, imgsurf);
--- a/layout/base/nsLayoutUtils.cpp
+++ b/layout/base/nsLayoutUtils.cpp
@@ -4113,16 +4113,21 @@ nsLayoutUtils::SurfaceFromElement(nsIDOM
 
     nsRefPtr<gfxContext> ctx = new gfxContext(gfxsurf);
 
     ctx->SetOperator(gfxContext::OPERATOR_SOURCE);
     ctx->SetSource(framesurf);
     ctx->Paint();
   }
 
+  PRInt32 corsmode;
+  if (NS_SUCCEEDED(imgRequest->GetCORSMode(&corsmode))) {
+    result.mCORSUsed = (corsmode != imgIRequest::CORS_NONE);
+  }
+
   result.mSurface = gfxsurf;
   result.mSize = gfxIntSize(imgWidth, imgHeight);
   result.mPrincipal = principal.forget();
   // SVG images could have <foreignObject> and/or <image> elements that load
   // content from another domain.  For safety, they make the canvas write-only.
   // XXXdholbert We could probably be more permissive here if we check that our
   // helper SVG document has no elements that could load remote content.
   result.mIsWriteOnly = (imgContainer->GetType() == imgIContainer::TYPE_VECTOR);
--- a/layout/base/nsLayoutUtils.h
+++ b/layout/base/nsLayoutUtils.h
@@ -1344,31 +1344,35 @@ public:
     SFE_NO_COLORSPACE_CONVERSION = 1 << 3,
     /* Whether we should skip premultiplication -- the resulting
        image will always be an image surface, and must not be given to
        Thebes for compositing! */
     SFE_NO_PREMULTIPLY_ALPHA = 1 << 4
   };
 
   struct SurfaceFromElementResult {
-    SurfaceFromElementResult() : mIsWriteOnly(PR_TRUE), mIsStillLoading(PR_FALSE) {}
+    SurfaceFromElementResult() :
+      // Use safe default values here
+      mIsWriteOnly(PR_TRUE), mIsStillLoading(PR_FALSE), mCORSUsed(PR_FALSE) {}
 
     /* mSurface will contain the resulting surface, or will be NULL on error */
     nsRefPtr<gfxASurface> mSurface;
     /* The size of the surface */
     gfxIntSize mSize;
     /* The principal associated with the element whose surface was returned */
     nsCOMPtr<nsIPrincipal> mPrincipal;
     /* The image request, if the element is an nsIImageLoadingContent */
     nsCOMPtr<imgIRequest> mImageRequest;
     /* Whether the element was "write only", that is, the bits should not be exposed to content */
     PRPackedBool mIsWriteOnly;
     /* Whether the element was still loading.  Some consumers need to handle
        this case specially. */
     PRPackedBool mIsStillLoading;
+    /* Whether the element used CORS when loading. */
+    PRPackedBool mCORSUsed;
   };
 
   static SurfaceFromElementResult SurfaceFromElement(nsIDOMElement *aElement,
                                                      PRUint32 aSurfaceFlags = 0);
 
   /**
    * When the document is editable by contenteditable attribute of its root
    * content or body content.