summaryrefslogtreecommitdiffstats
path: root/dom/canvas
diff options
context:
space:
mode:
Diffstat (limited to 'dom/canvas')
-rw-r--r--dom/canvas/CanvasRenderingContext2D.cpp44
-rw-r--r--dom/canvas/CanvasRenderingContext2D.h11
-rw-r--r--dom/canvas/CanvasUtils.cpp43
-rw-r--r--dom/canvas/CanvasUtils.h5
-rw-r--r--dom/canvas/ImageBitmap.cpp118
-rw-r--r--dom/canvas/ImageBitmap.h14
-rw-r--r--dom/canvas/ImageBitmapRenderingContext.cpp4
-rw-r--r--dom/canvas/WebGLBuffer.cpp2
-rw-r--r--dom/canvas/WebGLContext.h3
-rw-r--r--dom/canvas/WebGLTextureUpload.cpp18
-rw-r--r--dom/canvas/test/test_imagebitmap.html23
11 files changed, 192 insertions, 93 deletions
diff --git a/dom/canvas/CanvasRenderingContext2D.cpp b/dom/canvas/CanvasRenderingContext2D.cpp
index eee73e5..73cd347 100644
--- a/dom/canvas/CanvasRenderingContext2D.cpp
+++ b/dom/canvas/CanvasRenderingContext2D.cpp
@@ -1080,7 +1080,8 @@ CanvasRenderingContext2D::CanvasRenderingContext2D(
mPredictManyRedrawCalls(false),
mIsCapturedFrameInvalid(false),
mPathTransformWillUpdate(false),
- mInvalidateCount(0) {
+ mInvalidateCount(0),
+ mWriteOnly(false) {
if (!sMaxContextsInitialized) {
sMaxContexts = gfxPrefs::CanvasAzureAcceleratedLimit();
sMaxContextsInitialized = true;
@@ -2346,7 +2347,11 @@ void CanvasRenderingContext2D::SetStyleFromUnion(
}
if (aValue.IsCanvasPattern()) {
- SetStyleFromPattern(aValue.GetAsCanvasPattern(), aWhichStyle);
+ CanvasPattern& pattern = aValue.GetAsCanvasPattern();
+ SetStyleFromPattern(pattern, aWhichStyle);
+ if (pattern.mForceWriteOnly) {
+ SetWriteOnly();
+ }
return;
}
@@ -2497,7 +2502,7 @@ already_AddRefed<CanvasPattern> CanvasRenderingContext2D::CreatePattern(
// nullptr and set CORSUsed to true for passing the security check in
// CanvasUtils::DoDrawImageSecurityCheck().
RefPtr<CanvasPattern> pat =
- new CanvasPattern(this, srcSurf, repeatMode, nullptr, false, true);
+ new CanvasPattern(this, srcSurf, repeatMode, nullptr, imgBitmap.IsWriteOnly(), true);
return pat.forget();
}
@@ -2514,13 +2519,14 @@ already_AddRefed<CanvasPattern> CanvasRenderingContext2D::CreatePattern(
nsLayoutUtils::SurfaceFromElement(
element, nsLayoutUtils::SFE_WANT_FIRST_FRAME_IF_IMAGE, mTarget);
- if (!res.GetSourceSurface()) {
+ RefPtr<SourceSurface> surface = res.GetSourceSurface();
+ if (!surface) {
return nullptr;
}
RefPtr<CanvasPattern> pat =
- new CanvasPattern(this, res.GetSourceSurface(), repeatMode,
- res.mPrincipal, res.mIsWriteOnly, res.mCORSUsed);
+ new CanvasPattern(this, surface, repeatMode, res.mPrincipal,
+ res.mIsWriteOnly, res.mCORSUsed);
return pat.forget();
}
@@ -4848,8 +4854,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;
}
@@ -4908,6 +4914,10 @@ void CanvasRenderingContext2D::DrawImage(const CanvasImageSource& aImage,
aError.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
return;
}
+
+ if (canvas->IsWriteOnly()) {
+ SetWriteOnly();
+ }
} else if (aImage.IsImageBitmap()) {
ImageBitmap& imageBitmap = aImage.GetAsImageBitmap();
srcSurf = imageBitmap.PrepareForDrawTarget(mTarget);
@@ -4916,6 +4926,10 @@ void CanvasRenderingContext2D::DrawImage(const CanvasImageSource& aImage,
return;
}
+ if (imageBitmap.IsWriteOnly()) {
+ SetWriteOnly();
+ }
+
imgSize = gfx::IntSize(imageBitmap.Width(), imageBitmap.Height());
} else {
if (aImage.IsHTMLImageElement()) {
@@ -5421,13 +5435,8 @@ already_AddRefed<ImageData> CanvasRenderingContext2D::GetImageData(
// 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() &&
- // We could ask bindings for the caller type, but they already hand us a
- // JSContext, and we're at least _somewhat_ perf-sensitive (so may not
- // want to compute the caller type in the common non-write-only case), so
- // let's just use what we have.
- !nsContentUtils::CallerHasPermission(aCx,
- nsGkAtoms::all_urlsPermission)) {
+ if (IsWriteOnly() ||
+ (mCanvasElement && !mCanvasElement->CallerCanRead(aCx))) {
// XXX ERRMSG we need to report an error to developers here! (bug 329026)
aError.Throw(NS_ERROR_DOM_SECURITY_ERR);
return nullptr;
@@ -6029,6 +6038,13 @@ bool CanvasRenderingContext2D::ShouldForceInactiveLayer(
return !aManager->CanUseCanvasLayerForSize(GetSize());
}
+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 c436d36..693daf2 100644
--- a/dom/canvas/CanvasRenderingContext2D.h
+++ b/dom/canvas/CanvasRenderingContext2D.h
@@ -43,6 +43,7 @@ class
HTMLImageElementOrSVGImageElementOrHTMLCanvasElementOrHTMLVideoElementOrImageBitmap;
typedef HTMLImageElementOrSVGImageElementOrHTMLCanvasElementOrHTMLVideoElementOrImageBitmap
CanvasImageSource;
+class ImageBitmap;
class ImageData;
class StringOrCanvasGradientOrCanvasPattern;
class OwningStringOrCanvasGradientOrCanvasPattern;
@@ -1091,6 +1092,16 @@ class CanvasRenderingContext2D final : public nsICanvasRenderingContextInternal,
friend struct CanvasBidiProcessor;
friend class CanvasDrawObserver;
+ friend class ImageBitmap;
+
+ void SetWriteOnly();
+
+ bool IsWriteOnly() const
+ {
+ return mWriteOnly;
+ }
+
+ bool mWriteOnly;
};
size_t BindingJSObjectMallocBytes(CanvasRenderingContext2D* aContext);
diff --git a/dom/canvas/CanvasUtils.cpp b/dom/canvas/CanvasUtils.cpp
index 5ab2aa7..a76885b 100644
--- a/dom/canvas/CanvasUtils.cpp
+++ b/dom/canvas/CanvasUtils.cpp
@@ -236,7 +236,9 @@ void DoDrawImageSecurityCheck(dom::HTMLCanvasElement* aCanvasElement,
return;
}
- if (aCanvasElement->IsWriteOnly()) return;
+ if (aCanvasElement->IsWriteOnly() && !aCanvasElement->mExpandedReader) {
+ return;
+ }
// If we explicitly set WriteOnly just do it and get out
if (forceWriteOnly) {
@@ -254,6 +256,25 @@ void DoDrawImageSecurityCheck(dom::HTMLCanvasElement* aCanvasElement,
return;
}
+ if (BasePrincipal::Cast(aPrincipal)->AddonPolicy()) {
+ // This is a resource from an extension content script principal.
+
+ if (aCanvasElement->mExpandedReader &&
+ aCanvasElement->mExpandedReader->Subsumes(aPrincipal)) {
+ // This canvas already allows reading from this principal.
+ return;
+ }
+
+ if (!aCanvasElement->mExpandedReader) {
+ // Allow future reads from this same princial only.
+ aCanvasElement->SetWriteOnly(aPrincipal);
+ return;
+ }
+
+ // If we got here, this must be the *second* extension tainting
+ // the canvas. Fall through to mark it WriteOnly for everyone.
+ }
+
aCanvasElement->SetWriteOnly();
}
@@ -275,5 +296,25 @@ bool HasDrawWindowPrivilege(JSContext* aCx, JSObject* /* unused */) {
nsGkAtoms::all_urlsPermission);
}
+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 f87a5af..fa5c497 100644
--- a/dom/canvas/CanvasUtils.h
+++ b/dom/canvas/CanvasUtils.h
@@ -11,6 +11,7 @@
#include "mozilla/dom/ToJSValue.h"
#include "jsapi.h"
#include "mozilla/FloatingPoint.h"
+#include "nsLayoutUtils.h"
class nsIPrincipal;
@@ -172,6 +173,10 @@ void DashArrayToJSVal(nsTArray<T>& dashes, JSContext* cx,
}
}
+// 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 e74df2e..6e579dc 100644
--- a/dom/canvas/ImageBitmap.cpp
+++ b/dom/canvas/ImageBitmap.cpp
@@ -407,50 +407,18 @@ class CreateImageFromRawDataInMainThreadSyncTask final
const Maybe<IntRect>& mCropRect;
};
-static bool CheckSecurityForHTMLElements(bool aIsWriteOnly, bool aCORSUsed,
- nsIPrincipal* aPrincipal) {
- if (aIsWriteOnly || !aPrincipal) {
- 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 <class HTMLElementType>
+template <class ElementType>
static already_AddRefed<SourceSurface> GetSurfaceFromElement(
- nsIGlobalObject* aGlobal, HTMLElementType& aElement, ErrorResult& aRv) {
+ nsIGlobalObject* aGlobal, ElementType& aElement, bool* aWriteOnly,
+ ErrorResult& aRv) {
nsLayoutUtils::SurfaceFromElementResult res =
nsLayoutUtils::SurfaceFromElement(
&aElement, nsLayoutUtils::SFE_WANT_FIRST_FRAME_IF_IMAGE);
- // check origin-clean
- if (!CheckSecurityForHTMLElements(res)) {
- aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
- return nullptr;
- }
-
RefPtr<SourceSurface> surface = res.GetSourceSurface();
if (NS_WARN_IF(!surface)) {
@@ -458,6 +426,8 @@ static already_AddRefed<SourceSurface> GetSurfaceFromElement(
return nullptr;
}
+ *aWriteOnly = res.mIsWriteOnly;
+
return surface.forget();
}
@@ -485,7 +455,7 @@ static bool HasRasterImage(HTMLImageElement& aImageEl) {
}
ImageBitmap::ImageBitmap(nsIGlobalObject* aGlobal, layers::Image* aData,
- gfxAlphaType aAlphaType)
+ bool aWriteOnly, gfxAlphaType aAlphaType)
: mParent(aGlobal),
mData(aData),
mSurface(nullptr),
@@ -493,7 +463,8 @@ ImageBitmap::ImageBitmap(nsIGlobalObject* aGlobal, layers::Image* aData,
mPictureRect(0, 0, aData->GetSize().width, aData->GetSize().height),
mAlphaType(aAlphaType),
mIsCroppingAreaOutSideOfSourceImage(false),
- mAllocatedImageData(false) {
+ mAllocatedImageData(false),
+ mWriteOnly(aWriteOnly) {
MOZ_ASSERT(aData, "aData is null in ImageBitmap constructor.");
mShutdownObserver = new ImageBitmapShutdownObserver(this);
@@ -751,6 +722,7 @@ UniquePtr<ImageBitmapCloneData> ImageBitmap::ToCloneData() const {
RefPtr<SourceSurface> surface = mData->GetAsSourceSurface();
result->mSurface = surface->GetDataSurface();
MOZ_ASSERT(result->mSurface);
+ result->mWriteOnly = mWriteOnly;
return Move(result);
}
@@ -759,7 +731,8 @@ UniquePtr<ImageBitmapCloneData> ImageBitmap::ToCloneData() const {
nsIGlobalObject* aGlobal, ImageBitmapCloneData* aData) {
RefPtr<layers::Image> data = CreateImageFromSurface(aData->mSurface);
- RefPtr<ImageBitmap> ret = new ImageBitmap(aGlobal, data, aData->mAlphaType);
+ RefPtr<ImageBitmap> ret =
+ new ImageBitmap(aGlobal, data, aData->mWriteOnly, aData->mAlphaType);
ret->mAllocatedImageData = true;
@@ -775,11 +748,8 @@ UniquePtr<ImageBitmapCloneData> ImageBitmap::ToCloneData() const {
ImageBitmap::CreateFromOffscreenCanvas(nsIGlobalObject* aGlobal,
OffscreenCanvas& aOffscreenCanvas,
ErrorResult& aRv) {
- // Check origin-clean.
- if (aOffscreenCanvas.IsWriteOnly()) {
- aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
- return nullptr;
- }
+ // Check write-only mode.
+ bool writeOnly = aOffscreenCanvas.IsWriteOnly();
nsLayoutUtils::SurfaceFromElementResult res =
nsLayoutUtils::SurfaceFromOffscreenCanvas(
@@ -794,7 +764,7 @@ ImageBitmap::CreateFromOffscreenCanvas(nsIGlobalObject* aGlobal,
RefPtr<layers::Image> data = CreateImageFromSurface(surface);
- RefPtr<ImageBitmap> ret = new ImageBitmap(aGlobal, data);
+ RefPtr<ImageBitmap> ret = new ImageBitmap(aGlobal, data, writeOnly);
ret->mAllocatedImageData = true;
@@ -816,9 +786,12 @@ ImageBitmap::CreateFromOffscreenCanvas(nsIGlobalObject* aGlobal,
return nullptr;
}
+ bool writeOnly = true;
+
// Get the SourceSurface out from the image element and then do security
// checking.
- RefPtr<SourceSurface> surface = GetSurfaceFromElement(aGlobal, aImageEl, aRv);
+ RefPtr<SourceSurface> surface = GetSurfaceFromElement(aGlobal, aImageEl,
+ &writeOnly, aRv);
if (NS_WARN_IF(aRv.Failed())) {
return nullptr;
@@ -832,7 +805,7 @@ ImageBitmap::CreateFromOffscreenCanvas(nsIGlobalObject* aGlobal,
return nullptr;
}
- RefPtr<ImageBitmap> ret = new ImageBitmap(aGlobal, data);
+ RefPtr<ImageBitmap> ret = new ImageBitmap(aGlobal, data, writeOnly);
// Set the picture rectangle.
if (ret && aCropRect.isSome()) {
@@ -868,10 +841,7 @@ ImageBitmap::CreateFromOffscreenCanvas(nsIGlobalObject* aGlobal,
// Check security.
nsCOMPtr<nsIPrincipal> principal = aVideoEl.GetCurrentVideoPrincipal();
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.
RefPtr<layers::Image> data = aVideoEl.GetCurrentImage();
@@ -879,7 +849,7 @@ ImageBitmap::CreateFromOffscreenCanvas(nsIGlobalObject* aGlobal,
aRv.Throw(NS_ERROR_NOT_AVAILABLE);
return nullptr;
}
- RefPtr<ImageBitmap> ret = new ImageBitmap(aGlobal, data);
+ RefPtr<ImageBitmap> ret = new ImageBitmap(aGlobal, data, writeOnly);
// Set the picture rectangle.
if (ret && aCropRect.isSome()) {
@@ -900,13 +870,18 @@ ImageBitmap::CreateFromOffscreenCanvas(nsIGlobalObject* aGlobal,
return nullptr;
}
- RefPtr<SourceSurface> surface =
- GetSurfaceFromElement(aGlobal, aCanvasEl, aRv);
+ bool writeOnly = true;
+ RefPtr<SourceSurface> 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<SourceSurface> croppedSurface;
IntRect cropRect = aCropRect.valueOr(IntRect());
@@ -939,7 +914,7 @@ ImageBitmap::CreateFromOffscreenCanvas(nsIGlobalObject* aGlobal,
return nullptr;
}
- RefPtr<ImageBitmap> ret = new ImageBitmap(aGlobal, data);
+ RefPtr<ImageBitmap> ret = new ImageBitmap(aGlobal, data, writeOnly);
if (needToReportMemoryAllocation) {
ret->mAllocatedImageData = true;
@@ -1001,7 +976,8 @@ ImageBitmap::CreateFromOffscreenCanvas(nsIGlobalObject* aGlobal,
}
// Create an ImageBimtap.
- RefPtr<ImageBitmap> ret = new ImageBitmap(aGlobal, data, alphaType);
+ RefPtr<ImageBitmap> ret =
+ new ImageBitmap(aGlobal, data, false /* write-only */, alphaType);
ret->mAllocatedImageData = true;
@@ -1017,11 +993,9 @@ ImageBitmap::CreateFromOffscreenCanvas(nsIGlobalObject* aGlobal,
/* static */ already_AddRefed<ImageBitmap> ImageBitmap::CreateInternal(
nsIGlobalObject* aGlobal, CanvasRenderingContext2D& aCanvasCtx,
const Maybe<IntRect>& 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<SourceSurface> surface = aCanvasCtx.GetSurfaceSnapshot();
@@ -1043,7 +1017,8 @@ ImageBitmap::CreateFromOffscreenCanvas(nsIGlobalObject* aGlobal,
return nullptr;
}
- RefPtr<ImageBitmap> ret = new ImageBitmap(aGlobal, data);
+ RefPtr<ImageBitmap> ret =
+ new ImageBitmap(aGlobal, data, writeOnly);
ret->mAllocatedImageData = true;
@@ -1068,7 +1043,8 @@ ImageBitmap::CreateFromOffscreenCanvas(nsIGlobalObject* aGlobal,
RefPtr<layers::Image> data = aImageBitmap.mData;
RefPtr<ImageBitmap> ret =
- new ImageBitmap(aGlobal, data, aImageBitmap.mAlphaType);
+ new ImageBitmap(aGlobal, data, aImageBitmap.mWriteOnly,
+ aImageBitmap.mAlphaType);
// Set the picture rectangle.
if (ret && aCropRect.isSome()) {
@@ -1384,14 +1360,19 @@ static void AsyncCreateImageBitmapFromBlob(Promise* aPromise,
uint32_t picRectHeight_;
uint32_t alphaType_;
uint32_t isCroppingAreaOutSideOfSourceImage_;
+ uint32_t writeOnly;
+ uint32_t dummy;
if (!JS_ReadUint32Pair(aReader, &picRectX_, &picRectY_) ||
!JS_ReadUint32Pair(aReader, &picRectWidth_, &picRectHeight_) ||
!JS_ReadUint32Pair(aReader, &alphaType_,
- &isCroppingAreaOutSideOfSourceImage_)) {
+ &isCroppingAreaOutSideOfSourceImage_) ||
+ !JS_ReadUint32Pair(aReader, &writeOnly, &dummy)) {
return nullptr;
}
+ MOZ_ASSERT(dummy == 0);
+
int32_t picRectX = BitwiseCast<int32_t>(picRectX_);
int32_t picRectY = BitwiseCast<int32_t>(picRectY_);
int32_t picRectWidth = BitwiseCast<int32_t>(picRectWidth_);
@@ -1415,7 +1396,7 @@ static void AsyncCreateImageBitmapFromBlob(Promise* aPromise,
}
#endif
RefPtr<layers::Image> img = CreateImageFromSurface(aClonedSurfaces[aIndex]);
- RefPtr<ImageBitmap> imageBitmap = new ImageBitmap(aParent, img, alphaType);
+ RefPtr<ImageBitmap> imageBitmap = new ImageBitmap(aParent, img, !!writeOnly, alphaType);
imageBitmap->mIsCroppingAreaOutSideOfSourceImage =
isCroppingAreaOutSideOfSourceImage_;
@@ -1462,7 +1443,8 @@ static void AsyncCreateImageBitmapFromBlob(Promise* aPromise,
NS_WARN_IF(!JS_WriteUint32Pair(aWriter, picRectX, picRectY)) ||
NS_WARN_IF(!JS_WriteUint32Pair(aWriter, picRectWidth, picRectHeight)) ||
NS_WARN_IF(!JS_WriteUint32Pair(aWriter, alphaType,
- isCroppingAreaOutSideOfSourceImage))) {
+ isCroppingAreaOutSideOfSourceImage)) ||
+ NS_WARN_IF(!JS_WriteUint32Pair(aWriter, aImageBitmap->mWriteOnly, 0))) {
return false;
}
@@ -2003,7 +1985,8 @@ class CreateImageFromBufferSourceRawDataInMainThreadSyncTask final
// Create an ImageBimtap.
// Assume the data from an external buffer is not alpha-premultiplied.
RefPtr<ImageBitmap> imageBitmap =
- new ImageBitmap(aGlobal, data, gfxAlphaType::NonPremult);
+ new ImageBitmap(aGlobal, data, false /* write-only */,
+ gfxAlphaType::NonPremult);
imageBitmap->mAllocatedImageData = true;
@@ -2239,7 +2222,8 @@ void CreateImageBitmapFromBlob::DecodeAndCropBlobCompletedOwningThread(
}
// Create ImageBitmap object.
- RefPtr<ImageBitmap> imageBitmap = new ImageBitmap(mGlobalObject, aImage);
+ RefPtr<ImageBitmap> imageBitmap = new ImageBitmap(mGlobalObject, aImage,
+ false /* write-only */);
// Set mIsCroppingAreaOutSideOfSourceImage.
imageBitmap->SetIsCroppingAreaOutSideOfSourceImage(mSourceSize,
diff --git a/dom/canvas/ImageBitmap.h b/dom/canvas/ImageBitmap.h
index 1612296..8b4bd12 100644
--- a/dom/canvas/ImageBitmap.h
+++ b/dom/canvas/ImageBitmap.h
@@ -63,6 +63,7 @@ struct ImageBitmapCloneData final {
gfx::IntRect mPictureRect;
gfxAlphaType mAlphaType;
bool mIsCroppingAreaOutSideOfSourceImage;
+ bool mWriteOnly;
};
/*
@@ -160,6 +161,11 @@ class ImageBitmap final : public nsISupports, public nsWrapperCache {
void OnShutdown();
+ bool IsWriteOnly() const
+ {
+ return mWriteOnly;
+ }
+
protected:
/*
* The default value of aIsPremultipliedAlpha is TRUE because that the
@@ -181,6 +187,7 @@ class ImageBitmap final : public nsISupports, public nsWrapperCache {
* CreateInternal(from ImageData) method.
*/
ImageBitmap(nsIGlobalObject* aGlobal, layers::Image* aData,
+ bool aWriteOnly,
gfxAlphaType aAlphaType = gfxAlphaType::Premult);
virtual ~ImageBitmap();
@@ -271,6 +278,13 @@ class ImageBitmap final : public nsISupports, public nsWrapperCache {
* Whether this object allocated allocated and owns the image data.
*/
bool mAllocatedImageData;
+
+ /*
+ * 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/canvas/ImageBitmapRenderingContext.cpp b/dom/canvas/ImageBitmapRenderingContext.cpp
index 01ce541..1761acc 100644
--- a/dom/canvas/ImageBitmapRenderingContext.cpp
+++ b/dom/canvas/ImageBitmapRenderingContext.cpp
@@ -60,6 +60,10 @@ void ImageBitmapRenderingContext::TransferFromImageBitmap(
return;
}
+ if (aImageBitmap.IsWriteOnly() && mCanvasElement) {
+ mCanvasElement->SetWriteOnly();
+ }
+
Redraw(gfxRect(0, 0, mWidth, mHeight));
}
diff --git a/dom/canvas/WebGLBuffer.cpp b/dom/canvas/WebGLBuffer.cpp
index 657ad78..63dbbc0 100644
--- a/dom/canvas/WebGLBuffer.cpp
+++ b/dom/canvas/WebGLBuffer.cpp
@@ -96,7 +96,7 @@ void WebGLBuffer::BufferData(GLenum target, size_t size, const void* data,
if (!ValidateBufferUsageEnum(mContext, funcName, usage)) return;
-#ifdef XP_MACOSX
+#if defined(XP_MACOSX) || defined(MOZ_WIDGET_GTK)
// bug 790879
if (mContext->gl->WorkAroundDriverBugs() && size > INT32_MAX) {
mContext->ErrorOutOfMemory("%s: Allocation size too large.", funcName);
diff --git a/dom/canvas/WebGLContext.h b/dom/canvas/WebGLContext.h
index 1bfde2f..bc2cab7 100644
--- a/dom/canvas/WebGLContext.h
+++ b/dom/canvas/WebGLContext.h
@@ -241,8 +241,9 @@ struct TexImageSourceAdapter final : public TexImageSource {
mPboOffset = pboOffset;
}
- TexImageSourceAdapter(const dom::ImageBitmap* imageBitmap, ErrorResult*) {
+ TexImageSourceAdapter(const dom::ImageBitmap* imageBitmap, ErrorResult* out_error) {
mImageBitmap = imageBitmap;
+ mOut_error = out_error;
}
TexImageSourceAdapter(const dom::ImageData* imageData, ErrorResult*) {
diff --git a/dom/canvas/WebGLTextureUpload.cpp b/dom/canvas/WebGLTextureUpload.cpp
index ef29d18..7170fc1 100644
--- a/dom/canvas/WebGLTextureUpload.cpp
+++ b/dom/canvas/WebGLTextureUpload.cpp
@@ -12,6 +12,7 @@
#include "GLBlitHelper.h"
#include "GLContext.h"
#include "mozilla/gfx/2D.h"
+#include "mozilla/dom/HTMLCanvasElement.h"
#include "mozilla/dom/HTMLVideoElement.h"
#include "mozilla/dom/ImageBitmap.h"
#include "mozilla/dom/ImageData.h"
@@ -222,7 +223,12 @@ static UniquePtr<webgl::TexUnpackBytes> FromPboOffset(
static UniquePtr<webgl::TexUnpackBlob> FromImageBitmap(
WebGLContext* webgl, const char* funcName, TexImageTarget target,
uint32_t width, uint32_t height, uint32_t depth,
- const dom::ImageBitmap& imageBitmap) {
+ const dom::ImageBitmap& imageBitmap, ErrorResult* aRv) {
+ if (imageBitmap.IsWriteOnly()) {
+ aRv->Throw(NS_ERROR_DOM_SECURITY_ERR);
+ return nullptr;
+ }
+
UniquePtr<dom::ImageBitmapCloneData> cloneData =
Move(imageBitmap.ToCloneData());
const RefPtr<gfx::DataSourceSurface> surf = cloneData->mSurface;
@@ -294,6 +300,14 @@ UniquePtr<webgl::TexUnpackBlob> WebGLContext::FromDomElem(
const char* funcName, TexImageTarget target, uint32_t width,
uint32_t height, uint32_t depth, const dom::Element& elem,
ErrorResult* const out_error) {
+ if (elem.IsHTMLElement(nsGkAtoms::canvas)) {
+ const dom::HTMLCanvasElement* canvas = static_cast<const dom::HTMLCanvasElement*>(&elem);
+ if (canvas->IsWriteOnly()) {
+ out_error->Throw(NS_ERROR_DOM_SECURITY_ERR);
+ return nullptr;
+ }
+ }
+
// The canvas spec says that drawImage should draw the first frame of
// animated images. The webgl spec doesn't mention the issue, so we do the
// same as drawImage.
@@ -414,7 +428,7 @@ UniquePtr<webgl::TexUnpackBlob> WebGLContext::From(
if (src.mImageBitmap) {
return FromImageBitmap(this, funcName, target, width, height, depth,
- *(src.mImageBitmap));
+ *(src.mImageBitmap), src.mOut_error);
}
if (src.mImageData) {
diff --git a/dom/canvas/test/test_imagebitmap.html b/dom/canvas/test/test_imagebitmap.html
index 17f050e..81360d2 100644
--- a/dom/canvas/test/test_imagebitmap.html
+++ b/dom/canvas/test/test_imagebitmap.html
@@ -270,13 +270,22 @@ function testSecurityErrors() {
}
function checkPromiseFailedWithSecurityError(p) {
- return p.then( function(reason) { ok(false, "Did not get SecurityError with unclean source. ImageBitmap was created successfully."); },
- function(reason) { if (reason == "SecurityError: The operation is insecure.") {
- ok(true, reason);
- }
- else {
- ok(false, "Did not get SecurityError with unclean source. Error Message: " + reason);
- }});
+ return p.then(imageBitmap => {
+ ok(!!imageBitmap, "ImageBitmaps are always created");
+ const context = document.createElement("canvas").getContext("2d");
+ context.drawImage(imageBitmap, 0, 0);
+ try {
+ context.getImageData(0, 0, 1, 1);
+ ok(false, "Did not get SecurityError with unclean source. ImageBitmap was created successfully.");
+ } catch (ex) {
+ if (ex == "SecurityError: The operation is insecure.") {
+ ok(true, ex);
+ }
+ else {
+ ok(false, "Did not get SecurityError with unclean source. Error Message: " + ex);
+ }
+ }
+ });
}
return Promise.all([