diff --git a/dom/canvas/CanvasRenderingContext2D.cpp b/dom/canvas/CanvasRenderingContext2D.cpp index 5339c8d68..1974e2b36 100644 --- a/dom/canvas/CanvasRenderingContext2D.cpp +++ b/dom/canvas/CanvasRenderingContext2D.cpp @@ -966,6 +966,7 @@ CanvasRenderingContext2D::CanvasRenderingContext2D() , mIsCapturedFrameInvalid(false) , mPathTransformWillUpdate(false) , mInvalidateCount(0) + , mWriteOnly(false) { sNumLivingContexts++; @@ -1998,7 +1999,11 @@ CanvasRenderingContext2D::SetStyleFromUnion(const StringOrCanvasGradientOrCanvas } if (value.IsCanvasPattern()) { - SetStyleFromPattern(value.GetAsCanvasPattern(), whichStyle); + CanvasPattern& pattern = value.GetAsCanvasPattern(); + SetStyleFromPattern(pattern, whichStyle); + if (pattern.mForceWriteOnly) { + SetWriteOnly(); + } return; } @@ -2121,7 +2126,7 @@ CanvasRenderingContext2D::CreatePattern(const CanvasImageSource& source, // nullptr and set CORSUsed to true for passing the security check in // CanvasUtils::DoDrawImageSecurityCheck(). RefPtr pat = - new CanvasPattern(this, srcSurf, repeatMode, nullptr, false, true); + new CanvasPattern(this, srcSurf, repeatMode, nullptr, imgBitmap.IsWriteOnly(), true); return pat.forget(); } @@ -2134,12 +2139,13 @@ CanvasRenderingContext2D::CreatePattern(const CanvasImageSource& source, nsLayoutUtils::SurfaceFromElement(htmlElement, nsLayoutUtils::SFE_WANT_FIRST_FRAME, mTarget); - if (!res.GetSourceSurface()) { + RefPtr surface = res.GetSourceSurface(); + if (!surface) { error.Throw(NS_ERROR_NOT_AVAILABLE); return nullptr; } - RefPtr pat = new CanvasPattern(this, res.GetSourceSurface(), repeatMode, + RefPtr pat = new CanvasPattern(this, surface, repeatMode, res.mPrincipal, res.mIsWriteOnly, res.mCORSUsed); return pat.forget(); @@ -4370,8 +4376,8 @@ CanvasRenderingContext2D::CachedSurfaceFromElement(Element* aElement) res.mSize = res.mSourceSurface->GetSize(); res.mPrincipal = principal.forget(); - res.mIsWriteOnly = false; res.mImageRequest = imgRequest.forget(); + res.mIsWriteOnly = CheckWriteOnlySecurity(res.mCORSUsed, res.mPrincipal); return res; } @@ -4446,6 +4452,10 @@ CanvasRenderingContext2D::DrawImage(const CanvasImageSource& image, error.Throw(NS_ERROR_DOM_INVALID_STATE_ERR); return; } + + if (canvas->IsWriteOnly()) { + SetWriteOnly(); + } } else if (image.IsImageBitmap()) { ImageBitmap& imageBitmap = image.GetAsImageBitmap(); srcSurf = imageBitmap.PrepareForDrawTarget(mTarget); @@ -4454,6 +4464,10 @@ CanvasRenderingContext2D::DrawImage(const CanvasImageSource& image, return; } + if (imageBitmap.IsWriteOnly()) { + SetWriteOnly(); + } + imgSize = gfx::IntSize(imageBitmap.Width(), imageBitmap.Height()); } else { @@ -5146,9 +5160,8 @@ CanvasRenderingContext2D::GetImageData(JSContext* aCx, double aSx, // Check only if we have a canvas element; if we were created with a docshell, // then it's special internal use. - if (mCanvasElement && mCanvasElement->IsWriteOnly() && - !nsContentUtils::IsCallerChrome()) - { + if (IsWriteOnly() || + (mCanvasElement && !mCanvasElement->CallerCanRead(aCx))) { // XXX ERRMSG we need to report an error to developers here! (bug 329026) error.Throw(NS_ERROR_DOM_SECURITY_ERR); return nullptr; @@ -5754,6 +5767,13 @@ CanvasRenderingContext2D::ShouldForceInactiveLayer(LayerManager *aManager) return !aManager->CanUseCanvasLayerForSize(IntSize(mWidth, mHeight)); } +void CanvasRenderingContext2D::SetWriteOnly() { + mWriteOnly = true; + if (mCanvasElement) { + mCanvasElement->SetWriteOnly(); + } +} + NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(CanvasPath, AddRef) NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(CanvasPath, Release) diff --git a/dom/canvas/CanvasRenderingContext2D.h b/dom/canvas/CanvasRenderingContext2D.h index 23d991d1a..c7a5dd1be 100644 --- a/dom/canvas/CanvasRenderingContext2D.h +++ b/dom/canvas/CanvasRenderingContext2D.h @@ -41,6 +41,7 @@ class SourceSurface; namespace dom { class HTMLImageElementOrHTMLCanvasElementOrHTMLVideoElementOrImageBitmap; typedef HTMLImageElementOrHTMLCanvasElementOrHTMLVideoElementOrImageBitmap CanvasImageSource; +class ImageBitmap; class ImageData; class StringOrCanvasGradientOrCanvasPattern; class OwningStringOrCanvasGradientOrCanvasPattern; @@ -1081,6 +1082,16 @@ protected: friend struct CanvasBidiProcessor; friend class CanvasDrawObserver; + friend class ImageBitmap; + + void SetWriteOnly(); + + bool IsWriteOnly() const + { + return mWriteOnly; + } + + bool mWriteOnly; }; } // namespace dom diff --git a/dom/canvas/CanvasUtils.cpp b/dom/canvas/CanvasUtils.cpp index 9d258c01f..8494c9a95 100644 --- a/dom/canvas/CanvasUtils.cpp +++ b/dom/canvas/CanvasUtils.cpp @@ -83,8 +83,9 @@ DoDrawImageSecurityCheck(dom::HTMLCanvasElement *aCanvasElement, return; } - if (aCanvasElement->IsWriteOnly()) + if (aCanvasElement->IsWriteOnly() && !aCanvasElement->mExpandedReader) { return; + } // If we explicitly set WriteOnly just do it and get out if (forceWriteOnly) { @@ -103,6 +104,11 @@ DoDrawImageSecurityCheck(dom::HTMLCanvasElement *aCanvasElement, return; } + // XXX: Bug 1502799 implements an expanded reader flag to allow extensions + // to look at canvas data under defined conditions. However, we don't + // really have a need for this because our old XUL add-ons are chrome + // anyway, or do we? See the check above against mExpandedReader. + aCanvasElement->SetWriteOnly(); } @@ -121,5 +127,25 @@ CoerceDouble(JS::Value v, double* d) return true; } +bool CheckWriteOnlySecurity(bool aCORSUsed, nsIPrincipal* aPrincipal) { + if (!aPrincipal) { + return true; + } + + if (!aCORSUsed) { + nsIGlobalObject* incumbentSettingsObject = dom::GetIncumbentGlobal(); + if (NS_WARN_IF(!incumbentSettingsObject)) { + return true; + } + + nsIPrincipal* principal = incumbentSettingsObject->PrincipalOrNull(); + if (NS_WARN_IF(!principal) || !(principal->Subsumes(aPrincipal))) { + return true; + } + } + + return false; +} + } // namespace CanvasUtils } // namespace mozilla diff --git a/dom/canvas/CanvasUtils.h b/dom/canvas/CanvasUtils.h index b1237b69e..e047a37c4 100644 --- a/dom/canvas/CanvasUtils.h +++ b/dom/canvas/CanvasUtils.h @@ -10,6 +10,7 @@ #include "mozilla/dom/ToJSValue.h" #include "jsapi.h" #include "mozilla/FloatingPoint.h" +#include "nsLayoutUtils.h" class nsIPrincipal; @@ -155,6 +156,10 @@ DashArrayToJSVal(nsTArray& dashes, } } +// returns true if write-only mode must used for this principal based on +// the incumbent global. +bool CheckWriteOnlySecurity(bool aCORSUsed, nsIPrincipal* aPrincipal); + } // namespace CanvasUtils } // namespace mozilla diff --git a/dom/canvas/ImageBitmap.cpp b/dom/canvas/ImageBitmap.cpp index 1896e5bb1..d42dd3b7a 100644 --- a/dom/canvas/ImageBitmap.cpp +++ b/dom/canvas/ImageBitmap.cpp @@ -312,53 +312,18 @@ private: const Maybe& mCropRect; }; -static bool -CheckSecurityForHTMLElements(bool aIsWriteOnly, bool aCORSUsed, nsIPrincipal* aPrincipal) -{ - MOZ_ASSERT(aPrincipal); - - if (aIsWriteOnly) { - return false; - } - - if (!aCORSUsed) { - nsIGlobalObject* incumbentSettingsObject = GetIncumbentGlobal(); - if (NS_WARN_IF(!incumbentSettingsObject)) { - return false; - } - - nsIPrincipal* principal = incumbentSettingsObject->PrincipalOrNull(); - if (NS_WARN_IF(!principal) || !(principal->Subsumes(aPrincipal))) { - return false; - } - } - - return true; -} - -static bool -CheckSecurityForHTMLElements(const nsLayoutUtils::SurfaceFromElementResult& aRes) -{ - return CheckSecurityForHTMLElements(aRes.mIsWriteOnly, aRes.mCORSUsed, aRes.mPrincipal); -} - /* * A wrapper to the nsLayoutUtils::SurfaceFromElement() function followed by the * security checking. */ -template +template static already_AddRefed -GetSurfaceFromElement(nsIGlobalObject* aGlobal, HTMLElementType& aElement, ErrorResult& aRv) +GetSurfaceFromElement(nsIGlobalObject* aGlobal, ElementType& aElement, + bool* aWriteOnly, ErrorResult& aRv) { nsLayoutUtils::SurfaceFromElementResult res = nsLayoutUtils::SurfaceFromElement(&aElement, nsLayoutUtils::SFE_WANT_FIRST_FRAME); - // check origin-clean - if (!CheckSecurityForHTMLElements(res)) { - aRv.Throw(NS_ERROR_DOM_SECURITY_ERR); - return nullptr; - } - RefPtr surface = res.GetSourceSurface(); if (NS_WARN_IF(!surface)) { @@ -366,6 +331,8 @@ GetSurfaceFromElement(nsIGlobalObject* aGlobal, HTMLElementType& aElement, Error return nullptr; } + *aWriteOnly = res.mIsWriteOnly; + return surface.forget(); } @@ -394,11 +361,13 @@ HasRasterImage(HTMLImageElement& aImageEl) return false; } -ImageBitmap::ImageBitmap(nsIGlobalObject* aGlobal, layers::Image* aData) +ImageBitmap::ImageBitmap(nsIGlobalObject* aGlobal, layers::Image* aData, + bool aWriteOnly) : mParent(aGlobal) , mData(aData) , mSurface(nullptr) , mPictureRect(0, 0, aData->GetSize().width, aData->GetSize().height) + , mWriteOnly(aWriteOnly) { MOZ_ASSERT(aData, "aData is null in ImageBitmap constructor."); } @@ -514,9 +483,12 @@ ImageBitmap::CreateInternal(nsIGlobalObject* aGlobal, HTMLImageElement& aImageEl return nullptr; } + bool writeOnly = true; + // Get the SourceSurface out from the image element and then do security // checking. - RefPtr surface = GetSurfaceFromElement(aGlobal, aImageEl, aRv); + RefPtr surface = GetSurfaceFromElement(aGlobal, aImageEl, + &writeOnly, aRv); if (NS_WARN_IF(aRv.Failed())) { return nullptr; @@ -530,7 +502,7 @@ ImageBitmap::CreateInternal(nsIGlobalObject* aGlobal, HTMLImageElement& aImageEl return nullptr; } - RefPtr ret = new ImageBitmap(aGlobal, data); + RefPtr ret = new ImageBitmap(aGlobal, data, writeOnly); // Set the picture rectangle. if (ret && aCropRect.isSome()) { @@ -560,10 +532,7 @@ ImageBitmap::CreateInternal(nsIGlobalObject* aGlobal, HTMLVideoElement& aVideoEl // Check security. nsCOMPtr principal = aVideoEl.GetCurrentPrincipal(); bool CORSUsed = aVideoEl.GetCORSMode() != CORS_NONE; - if (!CheckSecurityForHTMLElements(false, CORSUsed, principal)) { - aRv.Throw(NS_ERROR_DOM_SECURITY_ERR); - return nullptr; - } + bool writeOnly = CheckWriteOnlySecurity(CORSUsed, principal); // Create ImageBitmap. ImageContainer *container = aVideoEl.GetImageContainer(); @@ -575,7 +544,7 @@ ImageBitmap::CreateInternal(nsIGlobalObject* aGlobal, HTMLVideoElement& aVideoEl AutoLockImage lockImage(container); layers::Image* data = lockImage.GetImage(); - RefPtr ret = new ImageBitmap(aGlobal, data); + RefPtr ret = new ImageBitmap(aGlobal, data, writeOnly); // Set the picture rectangle. if (ret && aCropRect.isSome()) { @@ -594,12 +563,18 @@ ImageBitmap::CreateInternal(nsIGlobalObject* aGlobal, HTMLCanvasElement& aCanvas return nullptr; } - RefPtr surface = GetSurfaceFromElement(aGlobal, aCanvasEl, aRv); + bool writeOnly = true; + RefPtr surface = GetSurfaceFromElement(aGlobal, aCanvasEl, + &writeOnly, aRv); if (NS_WARN_IF(aRv.Failed())) { return nullptr; } + if (!writeOnly) { + writeOnly = aCanvasEl.IsWriteOnly(); + } + // Crop the source surface if needed. RefPtr croppedSurface; IntRect cropRect = aCropRect.valueOr(IntRect()); @@ -635,7 +610,7 @@ ImageBitmap::CreateInternal(nsIGlobalObject* aGlobal, HTMLCanvasElement& aCanvas return nullptr; } - RefPtr ret = new ImageBitmap(aGlobal, data); + RefPtr ret = new ImageBitmap(aGlobal, data, writeOnly); // Set the picture rectangle. if (ret && aCropRect.isSome()) { @@ -694,7 +669,7 @@ ImageBitmap::CreateInternal(nsIGlobalObject* aGlobal, ImageData& aImageData, } // Create an ImageBimtap. - RefPtr ret = new ImageBitmap(aGlobal, data); + RefPtr ret = new ImageBitmap(aGlobal, data, false /* write-only */); // The cropping information has been handled in the CreateImageFromRawData() // function. @@ -706,11 +681,9 @@ ImageBitmap::CreateInternal(nsIGlobalObject* aGlobal, ImageData& aImageData, ImageBitmap::CreateInternal(nsIGlobalObject* aGlobal, CanvasRenderingContext2D& aCanvasCtx, const Maybe& aCropRect, ErrorResult& aRv) { - // Check origin-clean. - if (aCanvasCtx.GetCanvas()->IsWriteOnly()) { - aRv.Throw(NS_ERROR_DOM_SECURITY_ERR); - return nullptr; - } + // Check write-only mode. + bool writeOnly = + aCanvasCtx.GetCanvas()->IsWriteOnly() || aCanvasCtx.IsWriteOnly(); RefPtr surface = aCanvasCtx.GetSurfaceSnapshot(); @@ -732,7 +705,7 @@ ImageBitmap::CreateInternal(nsIGlobalObject* aGlobal, CanvasRenderingContext2D& return nullptr; } - RefPtr ret = new ImageBitmap(aGlobal, data); + RefPtr ret = new ImageBitmap(aGlobal, data, writeOnly); // Set the picture rectangle. if (ret && aCropRect.isSome()) { @@ -752,7 +725,7 @@ ImageBitmap::CreateInternal(nsIGlobalObject* aGlobal, ImageBitmap& aImageBitmap, } RefPtr data = aImageBitmap.mData; - RefPtr ret = new ImageBitmap(aGlobal, data); + RefPtr ret = new ImageBitmap(aGlobal, data, aImageBitmap.mWriteOnly); // Set the picture rectangle. if (ret && aCropRect.isSome()) { @@ -1001,7 +974,9 @@ private: } // Create ImageBitmap object. - RefPtr imageBitmap = new ImageBitmap(mGlobalObject, data); + // Ass-U-me that if this is a blob object, the promise to resolve it + // would not have been kicked off without a permissions check. + RefPtr imageBitmap = new ImageBitmap(mGlobalObject, data, false /* write-only */); return imageBitmap.forget(); } }; @@ -1081,7 +1056,8 @@ private: } // Create ImageBitmap object. - RefPtr imageBitmap = new ImageBitmap(mGlobalObject, data); + RefPtr imageBitmap = new ImageBitmap(mGlobalObject, data, + false /* write-only */); return imageBitmap.forget(); } @@ -1170,12 +1146,17 @@ ImageBitmap::ReadStructuredClone(JSContext* aCx, uint32_t picRectY_; uint32_t picRectWidth_; uint32_t picRectHeight_; + uint32_t writeOnly; + uint32_t dummy; if (!JS_ReadUint32Pair(aReader, &picRectX_, &picRectY_) || - !JS_ReadUint32Pair(aReader, &picRectWidth_, &picRectHeight_)) { + !JS_ReadUint32Pair(aReader, &picRectWidth_, &picRectHeight_) || + !JS_ReadUint32Pair(aReader, &writeOnly, &dummy)) { return nullptr; } + MOZ_ASSERT(dummy == 0); + int32_t picRectX = BitwiseCast(picRectX_); int32_t picRectY = BitwiseCast(picRectY_); int32_t picRectWidth = BitwiseCast(picRectWidth_); @@ -1193,7 +1174,7 @@ ImageBitmap::ReadStructuredClone(JSContext* aCx, JS::Rooted value(aCx); { RefPtr imageBitmap = - new ImageBitmap(aParent, aClonedImages[aIndex]); + new ImageBitmap(aParent, aClonedImages[aIndex], !!writeOnly); ErrorResult error; imageBitmap->SetPictureRect(IntRect(picRectX, picRectY, @@ -1229,7 +1210,8 @@ ImageBitmap::WriteStructuredClone(JSStructuredCloneWriter* aWriter, if (NS_WARN_IF(!JS_WriteUint32Pair(aWriter, SCTAG_DOM_IMAGEBITMAP, index)) || NS_WARN_IF(!JS_WriteUint32Pair(aWriter, picRectX, picRectY)) || - NS_WARN_IF(!JS_WriteUint32Pair(aWriter, picRectWidth, picRectHeight))) { + NS_WARN_IF(!JS_WriteUint32Pair(aWriter, picRectWidth, picRectHeight)) || + NS_WARN_IF(!JS_WriteUint32Pair(aWriter, aImageBitmap->mWriteOnly, 0))) { return false; } diff --git a/dom/canvas/ImageBitmap.h b/dom/canvas/ImageBitmap.h index ac849770a..58584a2a1 100644 --- a/dom/canvas/ImageBitmap.h +++ b/dom/canvas/ImageBitmap.h @@ -110,9 +110,14 @@ public: friend CreateImageBitmapFromBlobTask; friend CreateImageBitmapFromBlobWorkerTask; + bool IsWriteOnly() const + { + return mWriteOnly; + } + protected: - ImageBitmap(nsIGlobalObject* aGlobal, layers::Image* aData); + ImageBitmap(nsIGlobalObject* aGlobal, layers::Image* aData, bool aWriteOnly); virtual ~ImageBitmap(); @@ -173,6 +178,13 @@ protected: * to draw this ImageBitmap into a HTMLCanvasElement. */ gfx::IntRect mPictureRect; + + /* + * Write-Only flag is set to true if this image has been generated from a + * cross-origin source. This is the opposite of what is called 'origin-clean' + * in the spec. + */ + bool mWriteOnly; }; } // namespace dom diff --git a/dom/html/HTMLCanvasElement.cpp b/dom/html/HTMLCanvasElement.cpp index 7e767ef27..8de85ed2a 100644 --- a/dom/html/HTMLCanvasElement.cpp +++ b/dom/html/HTMLCanvasElement.cpp @@ -621,8 +621,8 @@ NS_IMETHODIMP HTMLCanvasElement::ToDataURL(const nsAString& aType, JS::Handle aParams, JSContext* aCx, nsAString& aDataURL) { - // do a trust check if this is a write-only canvas - if (mWriteOnly && !nsContentUtils::IsCallerChrome()) { + // mWriteOnly check is redundant, but optimizes for the common case. + if (mWriteOnly && !CallerCanRead(aCx)) { return NS_ERROR_DOM_SECURITY_ERR; } @@ -751,8 +751,8 @@ HTMLCanvasElement::ToBlob(JSContext* aCx, JS::Handle aParams, ErrorResult& aRv) { - // do a trust check if this is a write-only canvas - if (mWriteOnly && !nsContentUtils::IsCallerChrome()) { + // mWriteOnly check is redundant, but optimizes for the common case. + if (mWriteOnly && !CallerCanRead(aCx)) { aRv.Throw(NS_ERROR_DOM_SECURITY_ERR); return; } @@ -935,17 +935,47 @@ HTMLCanvasElement::GetSize() } bool -HTMLCanvasElement::IsWriteOnly() -{ - return mWriteOnly; -} +HTMLCanvasElement::IsWriteOnly() const { return mWriteOnly; } void HTMLCanvasElement::SetWriteOnly() { + mExpandedReader = nullptr; mWriteOnly = true; } +void +HTMLCanvasElement::SetWriteOnly(nsIPrincipal* aExpandedReader) +{ + mExpandedReader = aExpandedReader; + mWriteOnly = true; +} + +bool +HTMLCanvasElement::CallerCanRead(JSContext* aCx) +{ + if (!mWriteOnly) { + return true; + } + +// XXX: We don't implement expanded readers yet, and may not need them. +#if(0) + nsIPrincipal* prin = nsContentUtils::SubjectPrincipal(aCx); + + // If mExpandedReader is set, this canvas was tainted only by + // mExpandedReader's resources. So allow reading if the subject + // principal subsumes mExpandedReader. + if (mExpandedReader && prin->Subsumes(mExpandedReader)) { + return true; + } + + return nsContentUtils::PrincipalHasPermission(prin, nsGkAtoms::all_urlsPermission); +#else + // This is essentially what we did before, modulo the write-only check. + return nsContentUtils::IsCallerChrome(); +#endif +} + void HTMLCanvasElement::InvalidateCanvasContent(const gfx::Rect* damageRect) { diff --git a/dom/html/HTMLCanvasElement.h b/dom/html/HTMLCanvasElement.h index 1d11a2770..1f4607944 100644 --- a/dom/html/HTMLCanvasElement.h +++ b/dom/html/HTMLCanvasElement.h @@ -222,13 +222,19 @@ public: /** * Determine whether the canvas is write-only. */ - bool IsWriteOnly(); + bool IsWriteOnly() const; /** * Force the canvas to be write-only. */ void SetWriteOnly(); + /** + * Force the canvas to be write-only, except for readers from + * a specific extension's content script expanded principal. + */ + void SetWriteOnly(nsIPrincipal* aExpandedReader); + /** * Notify that some canvas content has changed and the window may * need to be updated. aDamageRect is in canvas coordinates. @@ -381,6 +387,13 @@ public: // is itself write-only. bool mWriteOnly; + // When this canvas is (only) tainted by an image from an extension + // content script, allow reads from the same extension afterwards. + RefPtr mExpandedReader; + + // Determines if the caller should be able to read the content. + bool CallerCanRead(JSContext* aCx); + bool IsPrintCallbackDone(); void HandlePrintCallback(nsPresContext::nsPresContextType aType); diff --git a/layout/base/nsLayoutUtils.cpp b/layout/base/nsLayoutUtils.cpp index a4ae8c109..f5a89763b 100644 --- a/layout/base/nsLayoutUtils.cpp +++ b/layout/base/nsLayoutUtils.cpp @@ -8,6 +8,7 @@ #include "mozilla/ArrayUtils.h" #include "mozilla/BasicEvents.h" +#include "mozilla/dom/CanvasUtils.h" #include "mozilla/ClearOnShutdown.h" #include "mozilla/EffectCompositor.h" #include "mozilla/EffectSet.h" @@ -7147,9 +7148,9 @@ nsLayoutUtils::SurfaceFromElement(nsIImageLoadingContent* aElement, result.mSize = IntSize(imgWidth, imgHeight); result.mPrincipal = principal.forget(); - // no images, including SVG images, can load content from another domain. - result.mIsWriteOnly = false; result.mImageRequest = imgRequest.forget(); + result.mIsWriteOnly = + CanvasUtils::CheckWriteOnlySecurity(result.mCORSUsed, result.mPrincipal); return result; } @@ -7261,7 +7262,8 @@ nsLayoutUtils::SurfaceFromElement(HTMLVideoElement* aElement, result.mHasSize = true; result.mSize = result.mLayersImage->GetSize(); result.mPrincipal = principal.forget(); - result.mIsWriteOnly = false; + result.mIsWriteOnly = + CanvasUtils::CheckWriteOnlySecurity(result.mCORSUsed, result.mPrincipal); return result; }