closes #557: M1502799 (M1526218, M1528909, M1540221, partial M1484980 with future expansion

This commit is contained in:
Cameron Kaiser 2019-06-15 18:23:26 -07:00
parent 612f0f7c3c
commit 695f6a162d
9 changed files with 184 additions and 83 deletions

View File

@ -966,6 +966,7 @@ CanvasRenderingContext2D::CanvasRenderingContext2D()
, mIsCapturedFrameInvalid(false) , mIsCapturedFrameInvalid(false)
, mPathTransformWillUpdate(false) , mPathTransformWillUpdate(false)
, mInvalidateCount(0) , mInvalidateCount(0)
, mWriteOnly(false)
{ {
sNumLivingContexts++; sNumLivingContexts++;
@ -1998,7 +1999,11 @@ CanvasRenderingContext2D::SetStyleFromUnion(const StringOrCanvasGradientOrCanvas
} }
if (value.IsCanvasPattern()) { if (value.IsCanvasPattern()) {
SetStyleFromPattern(value.GetAsCanvasPattern(), whichStyle); CanvasPattern& pattern = value.GetAsCanvasPattern();
SetStyleFromPattern(pattern, whichStyle);
if (pattern.mForceWriteOnly) {
SetWriteOnly();
}
return; return;
} }
@ -2121,7 +2126,7 @@ CanvasRenderingContext2D::CreatePattern(const CanvasImageSource& source,
// 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();
} }
@ -2134,12 +2139,13 @@ CanvasRenderingContext2D::CreatePattern(const CanvasImageSource& source,
nsLayoutUtils::SurfaceFromElement(htmlElement, nsLayoutUtils::SurfaceFromElement(htmlElement,
nsLayoutUtils::SFE_WANT_FIRST_FRAME, mTarget); nsLayoutUtils::SFE_WANT_FIRST_FRAME, mTarget);
if (!res.GetSourceSurface()) { RefPtr<SourceSurface> surface = res.GetSourceSurface();
if (!surface) {
error.Throw(NS_ERROR_NOT_AVAILABLE); error.Throw(NS_ERROR_NOT_AVAILABLE);
return nullptr; return nullptr;
} }
RefPtr<CanvasPattern> pat = new CanvasPattern(this, res.GetSourceSurface(), repeatMode, RefPtr<CanvasPattern> pat = new CanvasPattern(this, surface, repeatMode,
res.mPrincipal, res.mIsWriteOnly, res.mPrincipal, res.mIsWriteOnly,
res.mCORSUsed); res.mCORSUsed);
return pat.forget(); return pat.forget();
@ -4370,8 +4376,8 @@ CanvasRenderingContext2D::CachedSurfaceFromElement(Element* aElement)
res.mSize = res.mSourceSurface->GetSize(); res.mSize = res.mSourceSurface->GetSize();
res.mPrincipal = principal.forget(); res.mPrincipal = principal.forget();
res.mIsWriteOnly = false;
res.mImageRequest = imgRequest.forget(); res.mImageRequest = imgRequest.forget();
res.mIsWriteOnly = CheckWriteOnlySecurity(res.mCORSUsed, res.mPrincipal);
return res; return res;
} }
@ -4446,6 +4452,10 @@ CanvasRenderingContext2D::DrawImage(const CanvasImageSource& image,
error.Throw(NS_ERROR_DOM_INVALID_STATE_ERR); error.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
return; return;
} }
if (canvas->IsWriteOnly()) {
SetWriteOnly();
}
} else if (image.IsImageBitmap()) { } else if (image.IsImageBitmap()) {
ImageBitmap& imageBitmap = image.GetAsImageBitmap(); ImageBitmap& imageBitmap = image.GetAsImageBitmap();
srcSurf = imageBitmap.PrepareForDrawTarget(mTarget); srcSurf = imageBitmap.PrepareForDrawTarget(mTarget);
@ -4454,6 +4464,10 @@ CanvasRenderingContext2D::DrawImage(const CanvasImageSource& image,
return; return;
} }
if (imageBitmap.IsWriteOnly()) {
SetWriteOnly();
}
imgSize = gfx::IntSize(imageBitmap.Width(), imageBitmap.Height()); imgSize = gfx::IntSize(imageBitmap.Width(), imageBitmap.Height());
} }
else { 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, // 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->CallerCanRead(aCx))) {
{
// 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)
error.Throw(NS_ERROR_DOM_SECURITY_ERR); error.Throw(NS_ERROR_DOM_SECURITY_ERR);
return nullptr; return nullptr;
@ -5754,6 +5767,13 @@ CanvasRenderingContext2D::ShouldForceInactiveLayer(LayerManager *aManager)
return !aManager->CanUseCanvasLayerForSize(IntSize(mWidth, mHeight)); 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_ROOT_NATIVE(CanvasPath, AddRef)
NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(CanvasPath, Release) NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(CanvasPath, Release)

View File

@ -41,6 +41,7 @@ class SourceSurface;
namespace dom { namespace dom {
class HTMLImageElementOrHTMLCanvasElementOrHTMLVideoElementOrImageBitmap; class HTMLImageElementOrHTMLCanvasElementOrHTMLVideoElementOrImageBitmap;
typedef HTMLImageElementOrHTMLCanvasElementOrHTMLVideoElementOrImageBitmap CanvasImageSource; typedef HTMLImageElementOrHTMLCanvasElementOrHTMLVideoElementOrImageBitmap CanvasImageSource;
class ImageBitmap;
class ImageData; class ImageData;
class StringOrCanvasGradientOrCanvasPattern; class StringOrCanvasGradientOrCanvasPattern;
class OwningStringOrCanvasGradientOrCanvasPattern; class OwningStringOrCanvasGradientOrCanvasPattern;
@ -1081,6 +1082,16 @@ protected:
friend struct CanvasBidiProcessor; friend struct CanvasBidiProcessor;
friend class CanvasDrawObserver; friend class CanvasDrawObserver;
friend class ImageBitmap;
void SetWriteOnly();
bool IsWriteOnly() const
{
return mWriteOnly;
}
bool mWriteOnly;
}; };
} // namespace dom } // namespace dom

View File

@ -83,8 +83,9 @@ DoDrawImageSecurityCheck(dom::HTMLCanvasElement *aCanvasElement,
return; return;
} }
if (aCanvasElement->IsWriteOnly()) if (aCanvasElement->IsWriteOnly() && !aCanvasElement->mExpandedReader) {
return; return;
}
// If we explicitly set WriteOnly just do it and get out // If we explicitly set WriteOnly just do it and get out
if (forceWriteOnly) { if (forceWriteOnly) {
@ -103,6 +104,11 @@ DoDrawImageSecurityCheck(dom::HTMLCanvasElement *aCanvasElement,
return; 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(); aCanvasElement->SetWriteOnly();
} }
@ -121,5 +127,25 @@ CoerceDouble(JS::Value v, double* d)
return true; 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 CanvasUtils
} // namespace mozilla } // namespace mozilla

View File

@ -10,6 +10,7 @@
#include "mozilla/dom/ToJSValue.h" #include "mozilla/dom/ToJSValue.h"
#include "jsapi.h" #include "jsapi.h"
#include "mozilla/FloatingPoint.h" #include "mozilla/FloatingPoint.h"
#include "nsLayoutUtils.h"
class nsIPrincipal; class nsIPrincipal;
@ -155,6 +156,10 @@ DashArrayToJSVal(nsTArray<T>& 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 CanvasUtils
} // namespace mozilla } // namespace mozilla

View File

@ -312,53 +312,18 @@ private:
const Maybe<IntRect>& mCropRect; const Maybe<IntRect>& 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 * A wrapper to the nsLayoutUtils::SurfaceFromElement() function followed by the
* security checking. * security checking.
*/ */
template<class HTMLElementType> template<class ElementType>
static already_AddRefed<SourceSurface> static already_AddRefed<SourceSurface>
GetSurfaceFromElement(nsIGlobalObject* aGlobal, HTMLElementType& aElement, ErrorResult& aRv) GetSurfaceFromElement(nsIGlobalObject* aGlobal, ElementType& 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)) {
@ -366,6 +331,8 @@ GetSurfaceFromElement(nsIGlobalObject* aGlobal, HTMLElementType& aElement, Error
return nullptr; return nullptr;
} }
*aWriteOnly = res.mIsWriteOnly;
return surface.forget(); return surface.forget();
} }
@ -394,11 +361,13 @@ HasRasterImage(HTMLImageElement& aImageEl)
return false; return false;
} }
ImageBitmap::ImageBitmap(nsIGlobalObject* aGlobal, layers::Image* aData) ImageBitmap::ImageBitmap(nsIGlobalObject* aGlobal, layers::Image* aData,
bool aWriteOnly)
: mParent(aGlobal) : mParent(aGlobal)
, mData(aData) , mData(aData)
, mSurface(nullptr) , mSurface(nullptr)
, mPictureRect(0, 0, aData->GetSize().width, aData->GetSize().height) , mPictureRect(0, 0, aData->GetSize().width, aData->GetSize().height)
, mWriteOnly(aWriteOnly)
{ {
MOZ_ASSERT(aData, "aData is null in ImageBitmap constructor."); MOZ_ASSERT(aData, "aData is null in ImageBitmap constructor.");
} }
@ -514,9 +483,12 @@ ImageBitmap::CreateInternal(nsIGlobalObject* aGlobal, HTMLImageElement& aImageEl
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;
@ -530,7 +502,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()) {
@ -560,10 +532,7 @@ ImageBitmap::CreateInternal(nsIGlobalObject* aGlobal, HTMLVideoElement& aVideoEl
// Check security. // Check security.
nsCOMPtr<nsIPrincipal> principal = aVideoEl.GetCurrentPrincipal(); nsCOMPtr<nsIPrincipal> principal = aVideoEl.GetCurrentPrincipal();
bool CORSUsed = aVideoEl.GetCORSMode() != CORS_NONE; bool CORSUsed = aVideoEl.GetCORSMode() != CORS_NONE;
if (!CheckSecurityForHTMLElements(false, CORSUsed, principal)) { bool writeOnly = CheckWriteOnlySecurity(CORSUsed, principal);
aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
return nullptr;
}
// Create ImageBitmap. // Create ImageBitmap.
ImageContainer *container = aVideoEl.GetImageContainer(); ImageContainer *container = aVideoEl.GetImageContainer();
@ -575,7 +544,7 @@ ImageBitmap::CreateInternal(nsIGlobalObject* aGlobal, HTMLVideoElement& aVideoEl
AutoLockImage lockImage(container); AutoLockImage lockImage(container);
layers::Image* data = lockImage.GetImage(); layers::Image* data = lockImage.GetImage();
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()) {
@ -594,12 +563,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());
@ -635,7 +610,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()) {
@ -694,7 +669,7 @@ ImageBitmap::CreateInternal(nsIGlobalObject* aGlobal, ImageData& aImageData,
} }
// Create an ImageBimtap. // Create an ImageBimtap.
RefPtr<ImageBitmap> ret = new ImageBitmap(aGlobal, data); RefPtr<ImageBitmap> ret = new ImageBitmap(aGlobal, data, false /* write-only */);
// The cropping information has been handled in the CreateImageFromRawData() // The cropping information has been handled in the CreateImageFromRawData()
// function. // function.
@ -706,11 +681,9 @@ 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 write-only mode.
if (aCanvasCtx.GetCanvas()->IsWriteOnly()) { bool writeOnly =
aRv.Throw(NS_ERROR_DOM_SECURITY_ERR); aCanvasCtx.GetCanvas()->IsWriteOnly() || aCanvasCtx.IsWriteOnly();
return nullptr;
}
RefPtr<SourceSurface> surface = aCanvasCtx.GetSurfaceSnapshot(); RefPtr<SourceSurface> surface = aCanvasCtx.GetSurfaceSnapshot();
@ -732,7 +705,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()) {
@ -752,7 +725,7 @@ 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); RefPtr<ImageBitmap> ret = new ImageBitmap(aGlobal, data, aImageBitmap.mWriteOnly);
// Set the picture rectangle. // Set the picture rectangle.
if (ret && aCropRect.isSome()) { if (ret && aCropRect.isSome()) {
@ -1001,7 +974,9 @@ private:
} }
// Create ImageBitmap object. // Create ImageBitmap object.
RefPtr<ImageBitmap> 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> imageBitmap = new ImageBitmap(mGlobalObject, data, false /* write-only */);
return imageBitmap.forget(); return imageBitmap.forget();
} }
}; };
@ -1081,7 +1056,8 @@ private:
} }
// Create ImageBitmap object. // Create ImageBitmap object.
RefPtr<ImageBitmap> imageBitmap = new ImageBitmap(mGlobalObject, data); RefPtr<ImageBitmap> imageBitmap = new ImageBitmap(mGlobalObject, data,
false /* write-only */);
return imageBitmap.forget(); return imageBitmap.forget();
} }
@ -1170,12 +1146,17 @@ ImageBitmap::ReadStructuredClone(JSContext* aCx,
uint32_t picRectY_; uint32_t picRectY_;
uint32_t picRectWidth_; uint32_t picRectWidth_;
uint32_t picRectHeight_; uint32_t picRectHeight_;
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, &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_);
@ -1193,7 +1174,7 @@ ImageBitmap::ReadStructuredClone(JSContext* aCx,
JS::Rooted<JS::Value> value(aCx); JS::Rooted<JS::Value> value(aCx);
{ {
RefPtr<ImageBitmap> imageBitmap = RefPtr<ImageBitmap> imageBitmap =
new ImageBitmap(aParent, aClonedImages[aIndex]); new ImageBitmap(aParent, aClonedImages[aIndex], !!writeOnly);
ErrorResult error; ErrorResult error;
imageBitmap->SetPictureRect(IntRect(picRectX, picRectY, imageBitmap->SetPictureRect(IntRect(picRectX, picRectY,
@ -1229,7 +1210,8 @@ ImageBitmap::WriteStructuredClone(JSStructuredCloneWriter* aWriter,
if (NS_WARN_IF(!JS_WriteUint32Pair(aWriter, SCTAG_DOM_IMAGEBITMAP, index)) || 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, 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; return false;
} }

View File

@ -110,9 +110,14 @@ public:
friend CreateImageBitmapFromBlobTask; friend CreateImageBitmapFromBlobTask;
friend CreateImageBitmapFromBlobWorkerTask; friend CreateImageBitmapFromBlobWorkerTask;
bool IsWriteOnly() const
{
return mWriteOnly;
}
protected: protected:
ImageBitmap(nsIGlobalObject* aGlobal, layers::Image* aData); ImageBitmap(nsIGlobalObject* aGlobal, layers::Image* aData, bool aWriteOnly);
virtual ~ImageBitmap(); virtual ~ImageBitmap();
@ -173,6 +178,13 @@ protected:
* to draw this ImageBitmap into a HTMLCanvasElement. * to draw this ImageBitmap into a HTMLCanvasElement.
*/ */
gfx::IntRect mPictureRect; 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 } // namespace dom

View File

@ -621,8 +621,8 @@ NS_IMETHODIMP
HTMLCanvasElement::ToDataURL(const nsAString& aType, JS::Handle<JS::Value> aParams, HTMLCanvasElement::ToDataURL(const nsAString& aType, JS::Handle<JS::Value> aParams,
JSContext* aCx, nsAString& aDataURL) JSContext* aCx, nsAString& aDataURL)
{ {
// do a trust check if this is a write-only canvas // mWriteOnly check is redundant, but optimizes for the common case.
if (mWriteOnly && !nsContentUtils::IsCallerChrome()) { if (mWriteOnly && !CallerCanRead(aCx)) {
return NS_ERROR_DOM_SECURITY_ERR; return NS_ERROR_DOM_SECURITY_ERR;
} }
@ -751,8 +751,8 @@ HTMLCanvasElement::ToBlob(JSContext* aCx,
JS::Handle<JS::Value> aParams, JS::Handle<JS::Value> aParams,
ErrorResult& aRv) ErrorResult& aRv)
{ {
// do a trust check if this is a write-only canvas // mWriteOnly check is redundant, but optimizes for the common case.
if (mWriteOnly && !nsContentUtils::IsCallerChrome()) { if (mWriteOnly && !CallerCanRead(aCx)) {
aRv.Throw(NS_ERROR_DOM_SECURITY_ERR); aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
return; return;
} }
@ -935,17 +935,47 @@ HTMLCanvasElement::GetSize()
} }
bool bool
HTMLCanvasElement::IsWriteOnly() HTMLCanvasElement::IsWriteOnly() const { return mWriteOnly; }
{
return mWriteOnly;
}
void void
HTMLCanvasElement::SetWriteOnly() HTMLCanvasElement::SetWriteOnly()
{ {
mExpandedReader = nullptr;
mWriteOnly = true; 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 void
HTMLCanvasElement::InvalidateCanvasContent(const gfx::Rect* damageRect) HTMLCanvasElement::InvalidateCanvasContent(const gfx::Rect* damageRect)
{ {

View File

@ -222,13 +222,19 @@ public:
/** /**
* Determine whether the canvas is write-only. * Determine whether the canvas is write-only.
*/ */
bool IsWriteOnly(); bool IsWriteOnly() const;
/** /**
* Force the canvas to be write-only. * Force the canvas to be write-only.
*/ */
void SetWriteOnly(); 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 * Notify that some canvas content has changed and the window may
* need to be updated. aDamageRect is in canvas coordinates. * need to be updated. aDamageRect is in canvas coordinates.
@ -381,6 +387,13 @@ public:
// is itself write-only. // is itself write-only.
bool mWriteOnly; bool mWriteOnly;
// When this canvas is (only) tainted by an image from an extension
// content script, allow reads from the same extension afterwards.
RefPtr<nsIPrincipal> mExpandedReader;
// Determines if the caller should be able to read the content.
bool CallerCanRead(JSContext* aCx);
bool IsPrintCallbackDone(); bool IsPrintCallbackDone();
void HandlePrintCallback(nsPresContext::nsPresContextType aType); void HandlePrintCallback(nsPresContext::nsPresContextType aType);

View File

@ -8,6 +8,7 @@
#include "mozilla/ArrayUtils.h" #include "mozilla/ArrayUtils.h"
#include "mozilla/BasicEvents.h" #include "mozilla/BasicEvents.h"
#include "mozilla/dom/CanvasUtils.h"
#include "mozilla/ClearOnShutdown.h" #include "mozilla/ClearOnShutdown.h"
#include "mozilla/EffectCompositor.h" #include "mozilla/EffectCompositor.h"
#include "mozilla/EffectSet.h" #include "mozilla/EffectSet.h"
@ -7147,9 +7148,9 @@ nsLayoutUtils::SurfaceFromElement(nsIImageLoadingContent* aElement,
result.mSize = IntSize(imgWidth, imgHeight); result.mSize = IntSize(imgWidth, imgHeight);
result.mPrincipal = principal.forget(); result.mPrincipal = principal.forget();
// no images, including SVG images, can load content from another domain.
result.mIsWriteOnly = false;
result.mImageRequest = imgRequest.forget(); result.mImageRequest = imgRequest.forget();
result.mIsWriteOnly =
CanvasUtils::CheckWriteOnlySecurity(result.mCORSUsed, result.mPrincipal);
return result; return result;
} }
@ -7261,7 +7262,8 @@ nsLayoutUtils::SurfaceFromElement(HTMLVideoElement* aElement,
result.mHasSize = true; result.mHasSize = true;
result.mSize = result.mLayersImage->GetSize(); result.mSize = result.mLayersImage->GetSize();
result.mPrincipal = principal.forget(); result.mPrincipal = principal.forget();
result.mIsWriteOnly = false; result.mIsWriteOnly =
CanvasUtils::CheckWriteOnlySecurity(result.mCORSUsed, result.mPrincipal);
return result; return result;
} }