Implement origin-clean algorithm for ImageBitmap.

This resolves #973.
This commit is contained in:
wolfbeast 2019-02-14 11:46:21 +01:00 • committed by Roy Tam
commit 1cc92006b6
10 changed files with 143 additions and 60 deletions

View file

@ -1105,6 +1105,7 @@ CanvasRenderingContext2D::CanvasRenderingContext2D(layers::LayersBackend aCompos
, mIsCapturedFrameInvalid(false) , mIsCapturedFrameInvalid(false)
, mPathTransformWillUpdate(false) , mPathTransformWillUpdate(false)
, mInvalidateCount(0) , mInvalidateCount(0)
, mWriteOnly(false) // == !origin-clean
{ {
sNumLivingContexts++; sNumLivingContexts++;
@ -2562,7 +2563,8 @@ CanvasRenderingContext2D::CreatePattern(const CanvasImageSource& aSource,
// nullptr and set CORSUsed to true for passing the security check in // nullptr and set CORSUsed to true for passing the security check in
// CanvasUtils::DoDrawImageSecurityCheck(). // CanvasUtils::DoDrawImageSecurityCheck().
RefPtr<CanvasPattern> pat = RefPtr<CanvasPattern> pat =
new CanvasPattern(this, srcSurf, repeatMode, nullptr, false, true); new CanvasPattern(this, srcSurf, repeatMode, nullptr,
imgBitmap.IsWriteOnly(), true);
return pat.forget(); return pat.forget();
} }
@ -4952,6 +4954,10 @@ CanvasRenderingContext2D::DrawImage(const CanvasImageSource& aImage,
aError.Throw(NS_ERROR_DOM_INVALID_STATE_ERR); aError.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
return; return;
} }
if (canvas->IsWriteOnly()) {
SetWriteOnly();
}
} else if (aImage.IsImageBitmap()) { } else if (aImage.IsImageBitmap()) {
ImageBitmap& imageBitmap = aImage.GetAsImageBitmap(); ImageBitmap& imageBitmap = aImage.GetAsImageBitmap();
srcSurf = imageBitmap.PrepareForDrawTarget(mTarget); srcSurf = imageBitmap.PrepareForDrawTarget(mTarget);
@ -4960,6 +4966,10 @@ CanvasRenderingContext2D::DrawImage(const CanvasImageSource& aImage,
return; return;
} }
if (imageBitmap.IsWriteOnly()) {
SetWriteOnly();
}
imgSize = gfx::IntSize(imageBitmap.Width(), imageBitmap.Height()); imgSize = gfx::IntSize(imageBitmap.Width(), imageBitmap.Height());
} }
else { else {
@ -5674,9 +5684,8 @@ CanvasRenderingContext2D::GetImageData(JSContext* aCx, double aSx,
// Check only if we have a canvas element; if we were created with a docshell, // Check only if we have a canvas element; if we were created with a docshell,
// then it's special internal use. // then it's special internal use.
if (mCanvasElement && mCanvasElement->IsWriteOnly() && if (IsWriteOnly() ||
!nsContentUtils::IsCallerChrome()) (mCanvasElement && mCanvasElement->IsWriteOnly() && !nsContentUtils::IsCallerChrome())) {
{
// XXX ERRMSG we need to report an error to developers here! (bug 329026) // XXX ERRMSG we need to report an error to developers here! (bug 329026)
aError.Throw(NS_ERROR_DOM_SECURITY_ERR); aError.Throw(NS_ERROR_DOM_SECURITY_ERR);
return nullptr; return nullptr;

View file

@ -40,6 +40,7 @@ class SourceSurface;
namespace dom { namespace dom {
class HTMLImageElementOrSVGImageElementOrHTMLCanvasElementOrHTMLVideoElementOrImageBitmap; class HTMLImageElementOrSVGImageElementOrHTMLCanvasElementOrHTMLVideoElementOrImageBitmap;
typedef HTMLImageElementOrSVGImageElementOrHTMLCanvasElementOrHTMLVideoElementOrImageBitmap CanvasImageSource; typedef HTMLImageElementOrSVGImageElementOrHTMLCanvasElementOrHTMLVideoElementOrImageBitmap CanvasImageSource;
class ImageBitmap;
class ImageData; class ImageData;
class StringOrCanvasGradientOrCanvasPattern; class StringOrCanvasGradientOrCanvasPattern;
class OwningStringOrCanvasGradientOrCanvasPattern; class OwningStringOrCanvasGradientOrCanvasPattern;
@ -1151,6 +1152,19 @@ protected:
friend struct CanvasBidiProcessor; friend struct CanvasBidiProcessor;
friend class CanvasDrawObserver; friend class CanvasDrawObserver;
friend class ImageBitmap;
// For the origin-clean algorithm (mWriteOnly == !origin-clean)
// See https://html.spec.whatwg.org/multipage/imagebitmap-and-animations.html
void SetWriteOnly() {
mWriteOnly = true;
}
bool IsWriteOnly() const {
return mWriteOnly;
}
bool mWriteOnly;
}; };
} // namespace dom } // namespace dom

View file

@ -351,29 +351,27 @@ CheckSecurityForHTMLElements(const nsLayoutUtils::SurfaceFromElementResult& aRes
*/ */
template<class HTMLElementType> template<class HTMLElementType>
static already_AddRefed<SourceSurface> static already_AddRefed<SourceSurface>
GetSurfaceFromElement(nsIGlobalObject* aGlobal, HTMLElementType& aElement, ErrorResult& aRv) GetSurfaceFromElement(nsIGlobalObject* aGlobal, HTMLElementType& aElement,
bool* aWriteOnly, ErrorResult& aRv)
{ {
nsLayoutUtils::SurfaceFromElementResult res = nsLayoutUtils::SurfaceFromElementResult res =
nsLayoutUtils::SurfaceFromElement(&aElement, nsLayoutUtils::SFE_WANT_FIRST_FRAME); nsLayoutUtils::SurfaceFromElement(&aElement, nsLayoutUtils::SFE_WANT_FIRST_FRAME);
// check origin-clean
if (!CheckSecurityForHTMLElements(res)) {
aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
return nullptr;
}
RefPtr<SourceSurface> surface = res.GetSourceSurface(); RefPtr<SourceSurface> surface = res.GetSourceSurface();
if (NS_WARN_IF(!surface)) { if (NS_WARN_IF(!surface)) {
aRv.Throw(NS_ERROR_NOT_AVAILABLE); aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
return nullptr; return nullptr;
} }
// Check origin-clean and pass back
*aWriteOnly = !CheckSecurityForHTMLElements(res);
return surface.forget(); return surface.forget();
} }
/* /*
* The specification doesn't allow to create an ImegeBitmap from a vector image. * The specification doesn't allow to create an ImageBitmap from a vector image.
* This function is used to check if the given HTMLImageElement contains a * This function is used to check if the given HTMLImageElement contains a
* raster image. * raster image.
*/ */
@ -398,7 +396,7 @@ HasRasterImage(HTMLImageElement& aImageEl)
} }
ImageBitmap::ImageBitmap(nsIGlobalObject* aGlobal, layers::Image* aData, ImageBitmap::ImageBitmap(nsIGlobalObject* aGlobal, layers::Image* aData,
bool aIsPremultipliedAlpha /* = true */) bool aWriteOnly, bool aIsPremultipliedAlpha /* = true */)
: mParent(aGlobal) : mParent(aGlobal)
, mData(aData) , mData(aData)
, mSurface(nullptr) , mSurface(nullptr)
@ -406,6 +404,7 @@ ImageBitmap::ImageBitmap(nsIGlobalObject* aGlobal, layers::Image* aData,
, mPictureRect(0, 0, aData->GetSize().width, aData->GetSize().height) , mPictureRect(0, 0, aData->GetSize().width, aData->GetSize().height)
, mIsPremultipliedAlpha(aIsPremultipliedAlpha) , mIsPremultipliedAlpha(aIsPremultipliedAlpha)
, mIsCroppingAreaOutSideOfSourceImage(false) , mIsCroppingAreaOutSideOfSourceImage(false)
, mWriteOnly(aWriteOnly)
{ {
MOZ_ASSERT(aData, "aData is null in ImageBitmap constructor."); MOZ_ASSERT(aData, "aData is null in ImageBitmap constructor.");
} }
@ -698,6 +697,7 @@ ImageBitmap::ToCloneData() const
RefPtr<SourceSurface> surface = mData->GetAsSourceSurface(); RefPtr<SourceSurface> surface = mData->GetAsSourceSurface();
result->mSurface = surface->GetDataSurface(); result->mSurface = surface->GetDataSurface();
MOZ_ASSERT(result->mSurface); MOZ_ASSERT(result->mSurface);
result->mWriteOnly = mWriteOnly;
return Move(result); return Move(result);
} }
@ -708,7 +708,7 @@ ImageBitmap::CreateFromCloneData(nsIGlobalObject* aGlobal,
{ {
RefPtr<layers::Image> data = CreateImageFromSurface(aData->mSurface); RefPtr<layers::Image> data = CreateImageFromSurface(aData->mSurface);
RefPtr<ImageBitmap> ret = new ImageBitmap(aGlobal, data, RefPtr<ImageBitmap> ret = new ImageBitmap(aGlobal, data, aData->mWriteOnly,
aData->mIsPremultipliedAlpha); aData->mIsPremultipliedAlpha);
ret->mIsCroppingAreaOutSideOfSourceImage = ret->mIsCroppingAreaOutSideOfSourceImage =
@ -724,11 +724,8 @@ ImageBitmap::CreateFromOffscreenCanvas(nsIGlobalObject* aGlobal,
OffscreenCanvas& aOffscreenCanvas, OffscreenCanvas& aOffscreenCanvas,
ErrorResult& aRv) ErrorResult& aRv)
{ {
// Check origin-clean. // Check origin-clean
if (aOffscreenCanvas.IsWriteOnly()) { bool writeOnly = aOffscreenCanvas.IsWriteOnly();
aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
return nullptr;
}
nsLayoutUtils::SurfaceFromElementResult res = nsLayoutUtils::SurfaceFromElementResult res =
nsLayoutUtils::SurfaceFromOffscreenCanvas(&aOffscreenCanvas, nsLayoutUtils::SurfaceFromOffscreenCanvas(&aOffscreenCanvas,
@ -744,7 +741,7 @@ ImageBitmap::CreateFromOffscreenCanvas(nsIGlobalObject* aGlobal,
RefPtr<layers::Image> data = RefPtr<layers::Image> data =
CreateImageFromSurface(surface); CreateImageFromSurface(surface);
RefPtr<ImageBitmap> ret = new ImageBitmap(aGlobal, data); RefPtr<ImageBitmap> ret = new ImageBitmap(aGlobal, data, writeOnly);
return ret.forget(); return ret.forget();
} }
@ -757,16 +754,19 @@ ImageBitmap::CreateInternal(nsIGlobalObject* aGlobal, HTMLImageElement& aImageEl
aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR); aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
return nullptr; return nullptr;
} }
// Check if the image element is a bitmap (e.g. it's a vector graphic) or not. // Check if the image element is a bitmap (e.g. it's a vector graphic) or not.
if (!HasRasterImage(aImageEl)) { if (!HasRasterImage(aImageEl)) {
aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR); aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
return nullptr; return nullptr;
} }
bool writeOnly = true;
// Get the SourceSurface out from the image element and then do security // Get the SourceSurface out from the image element and then do security
// checking. // checking.
RefPtr<SourceSurface> surface = GetSurfaceFromElement(aGlobal, aImageEl, aRv); RefPtr<SourceSurface> surface = GetSurfaceFromElement(aGlobal, aImageEl,
&writeOnly, aRv);
if (NS_WARN_IF(aRv.Failed())) { if (NS_WARN_IF(aRv.Failed())) {
return nullptr; return nullptr;
@ -780,7 +780,7 @@ ImageBitmap::CreateInternal(nsIGlobalObject* aGlobal, HTMLImageElement& aImageEl
return nullptr; return nullptr;
} }
RefPtr<ImageBitmap> ret = new ImageBitmap(aGlobal, data); RefPtr<ImageBitmap> ret = new ImageBitmap(aGlobal, data, writeOnly);
// Set the picture rectangle. // Set the picture rectangle.
if (ret && aCropRect.isSome()) { if (ret && aCropRect.isSome()) {
@ -812,13 +812,13 @@ ImageBitmap::CreateInternal(nsIGlobalObject* aGlobal, HTMLVideoElement& aVideoEl
return nullptr; return nullptr;
} }
bool writeOnly = true;
// Check security. // Check security.
nsCOMPtr<nsIPrincipal> principal = aVideoEl.GetCurrentVideoPrincipal(); nsCOMPtr<nsIPrincipal> principal = aVideoEl.GetCurrentVideoPrincipal();
bool CORSUsed = aVideoEl.GetCORSMode() != CORS_NONE; bool CORSUsed = aVideoEl.GetCORSMode() != CORS_NONE;
if (!CheckSecurityForHTMLElements(false, CORSUsed, principal)) {
aRv.Throw(NS_ERROR_DOM_SECURITY_ERR); writeOnly = !CheckSecurityForHTMLElements(false, CORSUsed, principal);
return nullptr;
}
// Create ImageBitmap. // Create ImageBitmap.
ImageContainer *container = aVideoEl.GetImageContainer(); ImageContainer *container = aVideoEl.GetImageContainer();
@ -834,7 +834,7 @@ ImageBitmap::CreateInternal(nsIGlobalObject* aGlobal, HTMLVideoElement& aVideoEl
aRv.Throw(NS_ERROR_NOT_AVAILABLE); aRv.Throw(NS_ERROR_NOT_AVAILABLE);
return nullptr; return nullptr;
} }
RefPtr<ImageBitmap> ret = new ImageBitmap(aGlobal, data); RefPtr<ImageBitmap> ret = new ImageBitmap(aGlobal, data, writeOnly);
// Set the picture rectangle. // Set the picture rectangle.
if (ret && aCropRect.isSome()) { if (ret && aCropRect.isSome()) {
@ -856,12 +856,18 @@ ImageBitmap::CreateInternal(nsIGlobalObject* aGlobal, HTMLCanvasElement& aCanvas
return nullptr; 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())) { if (NS_WARN_IF(aRv.Failed())) {
return nullptr; return nullptr;
} }
if (!writeOnly) {
writeOnly = aCanvasEl.IsWriteOnly();
}
// Crop the source surface if needed. // Crop the source surface if needed.
RefPtr<SourceSurface> croppedSurface; RefPtr<SourceSurface> croppedSurface;
IntRect cropRect = aCropRect.valueOr(IntRect()); IntRect cropRect = aCropRect.valueOr(IntRect());
@ -874,8 +880,7 @@ ImageBitmap::CreateInternal(nsIGlobalObject* aGlobal, HTMLCanvasElement& aCanvas
aCropRect.isSome()) { aCropRect.isSome()) {
// The _surface_ must be a DataSourceSurface. // The _surface_ must be a DataSourceSurface.
MOZ_ASSERT(surface->GetType() == SurfaceType::DATA, MOZ_ASSERT(surface->GetType() == SurfaceType::DATA,
"The snapshot SourceSurface from WebGL rendering contest is not \ "The snapshot SourceSurface from WebGL rendering contest is not DataSourceSurface.");
DataSourceSurface.");
RefPtr<DataSourceSurface> dataSurface = surface->GetDataSurface(); RefPtr<DataSourceSurface> dataSurface = surface->GetDataSurface();
croppedSurface = CropAndCopyDataSourceSurface(dataSurface, cropRect); croppedSurface = CropAndCopyDataSourceSurface(dataSurface, cropRect);
cropRect.MoveTo(0, 0); cropRect.MoveTo(0, 0);
@ -897,7 +902,7 @@ ImageBitmap::CreateInternal(nsIGlobalObject* aGlobal, HTMLCanvasElement& aCanvas
return nullptr; return nullptr;
} }
RefPtr<ImageBitmap> ret = new ImageBitmap(aGlobal, data); RefPtr<ImageBitmap> ret = new ImageBitmap(aGlobal, data, writeOnly);
// Set the picture rectangle. // Set the picture rectangle.
if (ret && aCropRect.isSome()) { if (ret && aCropRect.isSome()) {
@ -958,9 +963,12 @@ ImageBitmap::CreateInternal(nsIGlobalObject* aGlobal, ImageData& aImageData,
return nullptr; return nullptr;
} }
// Create an ImageBimtap. // Create an ImageBitmap.
// ImageData's underlying data is not alpha-premultiplied. // ImageData's underlying data is not alpha-premultiplied.
RefPtr<ImageBitmap> ret = new ImageBitmap(aGlobal, data, false); RefPtr<ImageBitmap> ret = new ImageBitmap(aGlobal,
data,
false /* write-only */,
false /* alpha-premult */);
// The cropping information has been handled in the CreateImageFromRawData() // The cropping information has been handled in the CreateImageFromRawData()
// function. // function.
@ -975,11 +983,8 @@ ImageBitmap::CreateInternal(nsIGlobalObject* aGlobal, ImageData& aImageData,
ImageBitmap::CreateInternal(nsIGlobalObject* aGlobal, CanvasRenderingContext2D& aCanvasCtx, ImageBitmap::CreateInternal(nsIGlobalObject* aGlobal, CanvasRenderingContext2D& aCanvasCtx,
const Maybe<IntRect>& aCropRect, ErrorResult& aRv) const Maybe<IntRect>& aCropRect, ErrorResult& aRv)
{ {
// Check origin-clean. // Check origin-clean
if (aCanvasCtx.GetCanvas()->IsWriteOnly()) { bool writeOnly = aCanvasCtx.GetCanvas()->IsWriteOnly() || aCanvasCtx.IsWriteOnly();
aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
return nullptr;
}
RefPtr<SourceSurface> surface = aCanvasCtx.GetSurfaceSnapshot(); RefPtr<SourceSurface> surface = aCanvasCtx.GetSurfaceSnapshot();
@ -1001,7 +1006,7 @@ ImageBitmap::CreateInternal(nsIGlobalObject* aGlobal, CanvasRenderingContext2D&
return nullptr; return nullptr;
} }
RefPtr<ImageBitmap> ret = new ImageBitmap(aGlobal, data); RefPtr<ImageBitmap> ret = new ImageBitmap(aGlobal, data, writeOnly);
// Set the picture rectangle. // Set the picture rectangle.
if (ret && aCropRect.isSome()) { if (ret && aCropRect.isSome()) {
@ -1024,7 +1029,10 @@ ImageBitmap::CreateInternal(nsIGlobalObject* aGlobal, ImageBitmap& aImageBitmap,
} }
RefPtr<layers::Image> data = aImageBitmap.mData; RefPtr<layers::Image> data = aImageBitmap.mData;
RefPtr<ImageBitmap> ret = new ImageBitmap(aGlobal, data, aImageBitmap.mIsPremultipliedAlpha); RefPtr<ImageBitmap> ret = new ImageBitmap(aGlobal,
data,
aImageBitmap.mWriteOnly,
aImageBitmap.mIsPremultipliedAlpha);
// Set the picture rectangle. // Set the picture rectangle.
if (ret && aCropRect.isSome()) { if (ret && aCropRect.isSome()) {
@ -1295,7 +1303,7 @@ private:
} }
// Create ImageBitmap object. // Create ImageBitmap object.
RefPtr<ImageBitmap> imageBitmap = new ImageBitmap(mGlobalObject, data); RefPtr<ImageBitmap> imageBitmap = new ImageBitmap(mGlobalObject, data, false /* write-only */);
// Set mIsCroppingAreaOutSideOfSourceImage. // Set mIsCroppingAreaOutSideOfSourceImage.
imageBitmap->SetIsCroppingAreaOutSideOfSourceImage(sourceSize, originalCropRect); imageBitmap->SetIsCroppingAreaOutSideOfSourceImage(sourceSize, originalCropRect);
@ -1391,7 +1399,7 @@ private:
} }
// Create ImageBitmap object. // Create ImageBitmap object.
RefPtr<ImageBitmap> imageBitmap = new ImageBitmap(mGlobalObject, data); RefPtr<ImageBitmap> imageBitmap = new ImageBitmap(mGlobalObject, data, false /* write-only */);
// Set mIsCroppingAreaOutSideOfSourceImage. // Set mIsCroppingAreaOutSideOfSourceImage.
imageBitmap->SetIsCroppingAreaOutSideOfSourceImage(sourceSize, originalCropRect); imageBitmap->SetIsCroppingAreaOutSideOfSourceImage(sourceSize, originalCropRect);
@ -1486,14 +1494,19 @@ ImageBitmap::ReadStructuredClone(JSContext* aCx,
uint32_t picRectHeight_; uint32_t picRectHeight_;
uint32_t isPremultipliedAlpha_; uint32_t isPremultipliedAlpha_;
uint32_t isCroppingAreaOutSideOfSourceImage_; uint32_t isCroppingAreaOutSideOfSourceImage_;
uint32_t writeOnly;
uint32_t dummy;
if (!JS_ReadUint32Pair(aReader, &picRectX_, &picRectY_) || if (!JS_ReadUint32Pair(aReader, &picRectX_, &picRectY_) ||
!JS_ReadUint32Pair(aReader, &picRectWidth_, &picRectHeight_) || !JS_ReadUint32Pair(aReader, &picRectWidth_, &picRectHeight_) ||
!JS_ReadUint32Pair(aReader, &isPremultipliedAlpha_, !JS_ReadUint32Pair(aReader, &isPremultipliedAlpha_,
&isCroppingAreaOutSideOfSourceImage_)) { &isCroppingAreaOutSideOfSourceImage_) ||
!JS_ReadUint32Pair(aReader, &writeOnly, &dummy)) {
return nullptr; return nullptr;
} }
MOZ_ASSERT(dummy == 0);
int32_t picRectX = BitwiseCast<int32_t>(picRectX_); int32_t picRectX = BitwiseCast<int32_t>(picRectX_);
int32_t picRectY = BitwiseCast<int32_t>(picRectY_); int32_t picRectY = BitwiseCast<int32_t>(picRectY_);
int32_t picRectWidth = BitwiseCast<int32_t>(picRectWidth_); int32_t picRectWidth = BitwiseCast<int32_t>(picRectWidth_);
@ -1512,7 +1525,7 @@ ImageBitmap::ReadStructuredClone(JSContext* aCx,
{ {
RefPtr<layers::Image> img = CreateImageFromSurface(aClonedSurfaces[aIndex]); RefPtr<layers::Image> img = CreateImageFromSurface(aClonedSurfaces[aIndex]);
RefPtr<ImageBitmap> imageBitmap = RefPtr<ImageBitmap> imageBitmap =
new ImageBitmap(aParent, img, isPremultipliedAlpha_); new ImageBitmap(aParent, img, !!writeOnly, isPremultipliedAlpha_);
imageBitmap->mIsCroppingAreaOutSideOfSourceImage = imageBitmap->mIsCroppingAreaOutSideOfSourceImage =
isCroppingAreaOutSideOfSourceImage_; isCroppingAreaOutSideOfSourceImage_;
@ -1547,6 +1560,7 @@ ImageBitmap::WriteStructuredClone(JSStructuredCloneWriter* aWriter,
const uint32_t picRectHeight = BitwiseCast<uint32_t>(aImageBitmap->mPictureRect.height); const uint32_t picRectHeight = BitwiseCast<uint32_t>(aImageBitmap->mPictureRect.height);
const uint32_t isPremultipliedAlpha = aImageBitmap->mIsPremultipliedAlpha ? 1 : 0; const uint32_t isPremultipliedAlpha = aImageBitmap->mIsPremultipliedAlpha ? 1 : 0;
const uint32_t isCroppingAreaOutSideOfSourceImage = aImageBitmap->mIsCroppingAreaOutSideOfSourceImage ? 1 : 0; const uint32_t isCroppingAreaOutSideOfSourceImage = aImageBitmap->mIsCroppingAreaOutSideOfSourceImage ? 1 : 0;
const uint32_t isWriteOnly = aImageBitmap->mWriteOnly ? 1 : 0;
// Indexing the cloned surfaces and send the index to the receiver. // Indexing the cloned surfaces and send the index to the receiver.
uint32_t index = aClonedSurfaces.Length(); uint32_t index = aClonedSurfaces.Length();
@ -1555,7 +1569,8 @@ ImageBitmap::WriteStructuredClone(JSStructuredCloneWriter* aWriter,
NS_WARN_IF(!JS_WriteUint32Pair(aWriter, picRectX, picRectY)) || 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, isPremultipliedAlpha, NS_WARN_IF(!JS_WriteUint32Pair(aWriter, isPremultipliedAlpha,
isCroppingAreaOutSideOfSourceImage))) { isCroppingAreaOutSideOfSourceImage)) ||
NS_WARN_IF(!JS_WriteUint32Pair(aWriter, isWriteOnly, 0))) {
return false; return false;
} }

View file

@ -65,6 +65,7 @@ struct ImageBitmapCloneData final
gfx::IntRect mPictureRect; gfx::IntRect mPictureRect;
bool mIsPremultipliedAlpha; bool mIsPremultipliedAlpha;
bool mIsCroppingAreaOutSideOfSourceImage; bool mIsCroppingAreaOutSideOfSourceImage;
bool mWriteOnly;
}; };
/* /*
@ -161,6 +162,10 @@ public:
template<typename T> template<typename T>
friend class MapDataIntoBufferSource; friend class MapDataIntoBufferSource;
bool IsWriteOnly() const {
return mWriteOnly;
}
// Mozilla Extensions // Mozilla Extensions
ImageBitmapFormat ImageBitmapFormat
FindOptimalFormat(const Optional<Sequence<ImageBitmapFormat>>& aPossibleFormats, FindOptimalFormat(const Optional<Sequence<ImageBitmapFormat>>& aPossibleFormats,
@ -197,6 +202,7 @@ protected:
* CreateInternal(from ImageData) method. * CreateInternal(from ImageData) method.
*/ */
ImageBitmap(nsIGlobalObject* aGlobal, layers::Image* aData, ImageBitmap(nsIGlobalObject* aGlobal, layers::Image* aData,
bool aWriteOnly,
bool aIsPremultipliedAlpha = true); bool aIsPremultipliedAlpha = true);
virtual ~ImageBitmap(); virtual ~ImageBitmap();
@ -280,6 +286,12 @@ protected:
*/ */
bool mIsCroppingAreaOutSideOfSourceImage; bool mIsCroppingAreaOutSideOfSourceImage;
/*
* 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 } // namespace dom

View file

@ -63,6 +63,11 @@ ImageBitmapRenderingContext::TransferFromImageBitmap(ImageBitmap& aImageBitmap)
if (!mImage) { if (!mImage) {
return; return;
} }
// Check if ImageBitmap is tainted, and if so flag the canvas tainted too.
if (aImageBitmap.IsWriteOnly() && mCanvasElement) {
mCanvasElement->SetWriteOnly();
}
Redraw(gfxRect(0, 0, mWidth, mHeight)); Redraw(gfxRect(0, 0, mWidth, mHeight));
} }

View file

@ -275,8 +275,9 @@ struct TexImageSourceAdapter final : public TexImageSource
mPboOffset = pboOffset; mPboOffset = pboOffset;
} }
TexImageSourceAdapter(const dom::ImageBitmap* imageBitmap, ErrorResult*) { TexImageSourceAdapter(const dom::ImageBitmap* imageBitmap, ErrorResult* out_error) {
mImageBitmap = imageBitmap; mImageBitmap = imageBitmap;
mOut_error = out_error;
} }
TexImageSourceAdapter(const dom::ImageData* imageData, ErrorResult*) { TexImageSourceAdapter(const dom::ImageData* imageData, ErrorResult*) {

View file

@ -12,6 +12,7 @@
#include "GLBlitHelper.h" #include "GLBlitHelper.h"
#include "GLContext.h" #include "GLContext.h"
#include "mozilla/gfx/2D.h" #include "mozilla/gfx/2D.h"
#include "mozilla/dom/HTMLCanvasElement.h"
#include "mozilla/dom/HTMLVideoElement.h" #include "mozilla/dom/HTMLVideoElement.h"
#include "mozilla/dom/ImageBitmap.h" #include "mozilla/dom/ImageBitmap.h"
#include "mozilla/dom/ImageData.h" #include "mozilla/dom/ImageData.h"
@ -214,9 +215,18 @@ FromPboOffset(WebGLContext* webgl, const char* funcName, TexImageTarget target,
static UniquePtr<webgl::TexUnpackBlob> static UniquePtr<webgl::TexUnpackBlob>
FromImageBitmap(WebGLContext* webgl, const char* funcName, TexImageTarget target, FromImageBitmap(WebGLContext* webgl, const char* funcName, TexImageTarget target,
uint32_t width, uint32_t height, uint32_t depth, 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()); UniquePtr<dom::ImageBitmapCloneData> cloneData = Move(imageBitmap.ToCloneData());
if (!cloneData) {
return nullptr;
}
const RefPtr<gfx::DataSourceSurface> surf = cloneData->mSurface; const RefPtr<gfx::DataSourceSurface> surf = cloneData->mSurface;
//// ////
@ -293,6 +303,14 @@ WebGLContext::FromDomElem(const char* funcName, TexImageTarget target, uint32_t
uint32_t height, uint32_t depth, const dom::Element& elem, uint32_t height, uint32_t depth, const dom::Element& elem,
ErrorResult* const out_error) 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;
}
}
uint32_t flags = nsLayoutUtils::SFE_WANT_IMAGE_SURFACE | uint32_t flags = nsLayoutUtils::SFE_WANT_IMAGE_SURFACE |
nsLayoutUtils::SFE_USE_ELEMENT_SIZE_IF_VECTOR; nsLayoutUtils::SFE_USE_ELEMENT_SIZE_IF_VECTOR;
@ -412,7 +430,7 @@ WebGLContext::From(const char* funcName, TexImageTarget target, GLsizei rawWidth
if (src.mImageBitmap) { if (src.mImageBitmap) {
return FromImageBitmap(this, funcName, target, width, height, depth, return FromImageBitmap(this, funcName, target, width, height, depth,
*(src.mImageBitmap)); *(src.mImageBitmap), src.mOut_error);
} }
if (src.mImageData) { if (src.mImageData) {

View file

@ -270,13 +270,22 @@ function testSecurityErrors() {
} }
function checkPromiseFailedWithSecurityError(p) { function checkPromiseFailedWithSecurityError(p) {
return p.then( function(reason) { ok(false, "Did not get SecurityError with unclean source. ImageBitmap was created successfully."); }, return p.then(imageBitmap => {
function(reason) { if (reason == "SecurityError: The operation is insecure.") { ok(!!imageBitmap, "ImageBitmaps are always created");
ok(true, reason); const context = document.createElement("canvas").getContext("2d");
} context.drawImage(imageBitmap, 0, 0);
else { try {
ok(false, "Did not get SecurityError with unclean source. Error Message: " + reason); 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([ return Promise.all([

View file

@ -1000,7 +1000,7 @@ HTMLCanvasElement::GetSize()
} }
bool bool
HTMLCanvasElement::IsWriteOnly() HTMLCanvasElement::IsWriteOnly() const
{ {
return mWriteOnly; return mWriteOnly;
} }

View file

@ -224,9 +224,9 @@ public:
nsIntSize GetSize(); nsIntSize GetSize();
/** /**
* Determine whether the canvas is write-only. * Determine whether the canvas is write-only (tainted).
*/ */
bool IsWriteOnly(); bool IsWriteOnly() const;
/** /**
* Force the canvas to be write-only. * Force the canvas to be write-only.