Bug 1979782. r=ahale a=diannaS
Original Revision: https://phabricator.services.mozilla.com/D260529 Differential Revision: https://phabricator.services.mozilla.com/D260566
This commit is contained in:
committed by
dsmith@mozilla.com
parent
2925957e93
commit
8d43e5f486
@@ -7,7 +7,6 @@
|
|||||||
#include "GLContext.h"
|
#include "GLContext.h"
|
||||||
#include "ImageBitmapRenderingContext.h"
|
#include "ImageBitmapRenderingContext.h"
|
||||||
#include "ImageEncoder.h"
|
#include "ImageEncoder.h"
|
||||||
#include "mozilla/dom/BlobImpl.h"
|
|
||||||
#include "mozilla/dom/CanvasRenderingContext2D.h"
|
#include "mozilla/dom/CanvasRenderingContext2D.h"
|
||||||
#include "mozilla/dom/OffscreenCanvasRenderingContext2D.h"
|
#include "mozilla/dom/OffscreenCanvasRenderingContext2D.h"
|
||||||
#include "mozilla/GfxMessageUtils.h"
|
#include "mozilla/GfxMessageUtils.h"
|
||||||
@@ -26,55 +25,6 @@ namespace mozilla::dom {
|
|||||||
CanvasRenderingContextHelper::CanvasRenderingContextHelper()
|
CanvasRenderingContextHelper::CanvasRenderingContextHelper()
|
||||||
: mCurrentContextType(CanvasContextType::NoContext) {}
|
: mCurrentContextType(CanvasContextType::NoContext) {}
|
||||||
|
|
||||||
void CanvasRenderingContextHelper::ToBlob(
|
|
||||||
JSContext* aCx, nsIGlobalObject* aGlobal, BlobCallback& aCallback,
|
|
||||||
const nsAString& aType, JS::Handle<JS::Value> aParams, bool aUsePlaceholder,
|
|
||||||
ErrorResult& aRv) {
|
|
||||||
// Encoder callback when encoding is complete.
|
|
||||||
class EncodeCallback : public EncodeCompleteCallback {
|
|
||||||
public:
|
|
||||||
EncodeCallback(nsIGlobalObject* aGlobal, BlobCallback* aCallback)
|
|
||||||
: mGlobal(aGlobal), mBlobCallback(aCallback) {}
|
|
||||||
|
|
||||||
// This is called on main thread.
|
|
||||||
MOZ_CAN_RUN_SCRIPT
|
|
||||||
nsresult ReceiveBlobImpl(already_AddRefed<BlobImpl> aBlobImpl) override {
|
|
||||||
MOZ_ASSERT(NS_IsMainThread());
|
|
||||||
|
|
||||||
RefPtr<BlobImpl> blobImpl = aBlobImpl;
|
|
||||||
|
|
||||||
RefPtr<Blob> blob;
|
|
||||||
|
|
||||||
if (blobImpl) {
|
|
||||||
blob = Blob::Create(mGlobal, blobImpl);
|
|
||||||
}
|
|
||||||
|
|
||||||
RefPtr<BlobCallback> callback(std::move(mBlobCallback));
|
|
||||||
ErrorResult rv;
|
|
||||||
|
|
||||||
callback->Call(blob, rv);
|
|
||||||
|
|
||||||
mGlobal = nullptr;
|
|
||||||
MOZ_ASSERT(!mBlobCallback);
|
|
||||||
|
|
||||||
return rv.StealNSResult();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool CanBeDeletedOnAnyThread() override {
|
|
||||||
// EncodeCallback is used from the main thread only.
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
nsCOMPtr<nsIGlobalObject> mGlobal;
|
|
||||||
RefPtr<BlobCallback> mBlobCallback;
|
|
||||||
};
|
|
||||||
|
|
||||||
RefPtr<EncodeCompleteCallback> callback =
|
|
||||||
new EncodeCallback(aGlobal, &aCallback);
|
|
||||||
|
|
||||||
ToBlob(aCx, callback, aType, aParams, aUsePlaceholder, aRv);
|
|
||||||
}
|
|
||||||
|
|
||||||
void CanvasRenderingContextHelper::ToBlob(
|
void CanvasRenderingContextHelper::ToBlob(
|
||||||
JSContext* aCx, EncodeCompleteCallback* aCallback, const nsAString& aType,
|
JSContext* aCx, EncodeCompleteCallback* aCallback, const nsAString& aType,
|
||||||
JS::Handle<JS::Value> aParams, bool aUsePlaceholder, ErrorResult& aRv) {
|
JS::Handle<JS::Value> aParams, bool aUsePlaceholder, ErrorResult& aRv) {
|
||||||
|
|||||||
@@ -58,10 +58,6 @@ class CanvasRenderingContextHelper {
|
|||||||
nsAString& outParams,
|
nsAString& outParams,
|
||||||
bool* const outCustomParseOptions);
|
bool* const outCustomParseOptions);
|
||||||
|
|
||||||
void ToBlob(JSContext* aCx, nsIGlobalObject* global, BlobCallback& aCallback,
|
|
||||||
const nsAString& aType, JS::Handle<JS::Value> aParams,
|
|
||||||
bool aUsePlaceholder, ErrorResult& aRv);
|
|
||||||
|
|
||||||
void ToBlob(JSContext* aCx, EncodeCompleteCallback* aCallback,
|
void ToBlob(JSContext* aCx, EncodeCompleteCallback* aCallback,
|
||||||
const nsAString& aType, JS::Handle<JS::Value> aParams,
|
const nsAString& aType, JS::Handle<JS::Value> aParams,
|
||||||
bool aUsePlaceholder, ErrorResult& aRv);
|
bool aUsePlaceholder, ErrorResult& aRv);
|
||||||
|
|||||||
@@ -573,6 +573,10 @@ void OffscreenCanvas::SetWriteOnly(RefPtr<nsIPrincipal>&& aExpandedReader) {
|
|||||||
mExpandedReader.forget());
|
mExpandedReader.forget());
|
||||||
mExpandedReader = std::move(aExpandedReader);
|
mExpandedReader = std::move(aExpandedReader);
|
||||||
mIsWriteOnly = true;
|
mIsWriteOnly = true;
|
||||||
|
|
||||||
|
if (mDisplay) {
|
||||||
|
mDisplay->SetWriteOnly(mExpandedReader);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool OffscreenCanvas::CallerCanRead(nsIPrincipal& aPrincipal) const {
|
bool OffscreenCanvas::CallerCanRead(nsIPrincipal& aPrincipal) const {
|
||||||
|
|||||||
@@ -32,7 +32,11 @@ OffscreenCanvasDisplayHelper::OffscreenCanvasDisplayHelper(
|
|||||||
mData.mSize.height = aHeight;
|
mData.mSize.height = aHeight;
|
||||||
}
|
}
|
||||||
|
|
||||||
OffscreenCanvasDisplayHelper::~OffscreenCanvasDisplayHelper() = default;
|
OffscreenCanvasDisplayHelper::~OffscreenCanvasDisplayHelper() {
|
||||||
|
MutexAutoLock lock(mMutex);
|
||||||
|
NS_ReleaseOnMainThread("OffscreenCanvas::mExpandedReader",
|
||||||
|
mExpandedReader.forget());
|
||||||
|
}
|
||||||
|
|
||||||
void OffscreenCanvasDisplayHelper::DestroyElement() {
|
void OffscreenCanvasDisplayHelper::DestroyElement() {
|
||||||
MOZ_ASSERT(NS_IsMainThread());
|
MOZ_ASSERT(NS_IsMainThread());
|
||||||
@@ -61,6 +65,32 @@ void OffscreenCanvasDisplayHelper::DestroyCanvas() {
|
|||||||
mWorkerRef = nullptr;
|
mWorkerRef = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void OffscreenCanvasDisplayHelper::SetWriteOnly(nsIPrincipal* aExpandedReader) {
|
||||||
|
MutexAutoLock lock(mMutex);
|
||||||
|
NS_ReleaseOnMainThread("OffscreenCanvasDisplayHelper::mExpandedReader",
|
||||||
|
mExpandedReader.forget());
|
||||||
|
mExpandedReader = aExpandedReader;
|
||||||
|
mIsWriteOnly = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool OffscreenCanvasDisplayHelper::CallerCanRead(
|
||||||
|
nsIPrincipal& aPrincipal) const {
|
||||||
|
MutexAutoLock lock(mMutex);
|
||||||
|
if (!mIsWriteOnly) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If mExpandedReader is set, this canvas was tainted only by
|
||||||
|
// mExpandedReader's resources. So allow reading if the subject
|
||||||
|
// principal subsumes mExpandedReader.
|
||||||
|
if (mExpandedReader && aPrincipal.Subsumes(mExpandedReader)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return nsContentUtils::PrincipalHasPermission(aPrincipal,
|
||||||
|
nsGkAtoms::all_urlsPermission);
|
||||||
|
}
|
||||||
|
|
||||||
bool OffscreenCanvasDisplayHelper::CanElementCaptureStream() const {
|
bool OffscreenCanvasDisplayHelper::CanElementCaptureStream() const {
|
||||||
MutexAutoLock lock(mMutex);
|
MutexAutoLock lock(mMutex);
|
||||||
return !!mWorkerRef;
|
return !!mWorkerRef;
|
||||||
|
|||||||
@@ -57,6 +57,19 @@ class OffscreenCanvasDisplayHelper final {
|
|||||||
void DestroyCanvas();
|
void DestroyCanvas();
|
||||||
void DestroyElement();
|
void DestroyElement();
|
||||||
|
|
||||||
|
bool IsWriteOnly() const {
|
||||||
|
MutexAutoLock lock(mMutex);
|
||||||
|
return mIsWriteOnly;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool HasWorkerRef() const {
|
||||||
|
MutexAutoLock lock(mMutex);
|
||||||
|
return !!mWorkerRef;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SetWriteOnly(nsIPrincipal* aExpandedReader = nullptr);
|
||||||
|
bool CallerCanRead(nsIPrincipal& aPrincipal) const;
|
||||||
|
|
||||||
bool CanElementCaptureStream() const;
|
bool CanElementCaptureStream() const;
|
||||||
bool UsingElementCaptureStream() const;
|
bool UsingElementCaptureStream() const;
|
||||||
|
|
||||||
@@ -90,6 +103,8 @@ class OffscreenCanvasDisplayHelper final {
|
|||||||
mozilla::layers::ImageContainer::FrameID mLastFrameID MOZ_GUARDED_BY(mMutex) =
|
mozilla::layers::ImageContainer::FrameID mLastFrameID MOZ_GUARDED_BY(mMutex) =
|
||||||
0;
|
0;
|
||||||
bool mPendingInvalidate MOZ_GUARDED_BY(mMutex) = false;
|
bool mPendingInvalidate MOZ_GUARDED_BY(mMutex) = false;
|
||||||
|
bool mIsWriteOnly MOZ_GUARDED_BY(mMutex) = false;
|
||||||
|
RefPtr<nsIPrincipal> mExpandedReader MOZ_GUARDED_BY(mMutex);
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace mozilla::dom
|
} // namespace mozilla::dom
|
||||||
|
|||||||
@@ -15,6 +15,7 @@
|
|||||||
#include "mozilla/BasePrincipal.h"
|
#include "mozilla/BasePrincipal.h"
|
||||||
#include "mozilla/CheckedInt.h"
|
#include "mozilla/CheckedInt.h"
|
||||||
#include "mozilla/PresShell.h"
|
#include "mozilla/PresShell.h"
|
||||||
|
#include "mozilla/dom/BlobImpl.h"
|
||||||
#include "mozilla/dom/CanvasCaptureMediaStream.h"
|
#include "mozilla/dom/CanvasCaptureMediaStream.h"
|
||||||
#include "mozilla/dom/CanvasRenderingContext2D.h"
|
#include "mozilla/dom/CanvasRenderingContext2D.h"
|
||||||
#include "mozilla/dom/Document.h"
|
#include "mozilla/dom/Document.h"
|
||||||
@@ -767,16 +768,26 @@ void HTMLCanvasElement::ToDataURL(JSContext* aCx, const nsAString& aType,
|
|||||||
nsAString& aDataURL,
|
nsAString& aDataURL,
|
||||||
nsIPrincipal& aSubjectPrincipal,
|
nsIPrincipal& aSubjectPrincipal,
|
||||||
ErrorResult& aRv) {
|
ErrorResult& aRv) {
|
||||||
// mWriteOnly check is redundant, but optimizes for the common case.
|
bool recheckCanRead = mOffscreenDisplay && mOffscreenDisplay->HasWorkerRef();
|
||||||
if (mWriteOnly && !CallerCanRead(aSubjectPrincipal)) {
|
|
||||||
|
if (!CallerCanRead(aSubjectPrincipal)) {
|
||||||
aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
|
aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
nsresult rv = ToDataURLImpl(aCx, aSubjectPrincipal, aType, aParams, aDataURL);
|
nsString dataURL;
|
||||||
if (NS_FAILED(rv)) {
|
nsresult rv = ToDataURLImpl(aCx, aSubjectPrincipal, aType, aParams, dataURL);
|
||||||
aDataURL.AssignLiteral("data:,");
|
if (recheckCanRead && !CallerCanRead(aSubjectPrincipal)) {
|
||||||
|
aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (NS_FAILED(rv)) {
|
||||||
|
aDataURL.Assign(u"data:,"_ns);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
aDataURL = std::move(dataURL);
|
||||||
}
|
}
|
||||||
|
|
||||||
void HTMLCanvasElement::SetMozPrintCallback(PrintCallback* aCallback) {
|
void HTMLCanvasElement::SetMozPrintCallback(PrintCallback* aCallback) {
|
||||||
@@ -992,8 +1003,9 @@ void HTMLCanvasElement::ToBlob(JSContext* aCx, BlobCallback& aCallback,
|
|||||||
JS::Handle<JS::Value> aParams,
|
JS::Handle<JS::Value> aParams,
|
||||||
nsIPrincipal& aSubjectPrincipal,
|
nsIPrincipal& aSubjectPrincipal,
|
||||||
ErrorResult& aRv) {
|
ErrorResult& aRv) {
|
||||||
// mWriteOnly check is redundant, but optimizes for the common case.
|
bool recheckCanRead = mOffscreenDisplay && mOffscreenDisplay->HasWorkerRef();
|
||||||
if (mWriteOnly && !CallerCanRead(aSubjectPrincipal)) {
|
|
||||||
|
if (!CallerCanRead(aSubjectPrincipal)) {
|
||||||
aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
|
aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -1018,7 +1030,59 @@ void HTMLCanvasElement::ToBlob(JSContext* aCx, BlobCallback& aCallback,
|
|||||||
// If no permission, return all-white, opaque image data.
|
// If no permission, return all-white, opaque image data.
|
||||||
bool usePlaceholder = !CanvasUtils::IsImageExtractionAllowed(
|
bool usePlaceholder = !CanvasUtils::IsImageExtractionAllowed(
|
||||||
OwnerDoc(), aCx, aSubjectPrincipal);
|
OwnerDoc(), aCx, aSubjectPrincipal);
|
||||||
CanvasRenderingContextHelper::ToBlob(aCx, global, aCallback, aType, aParams,
|
|
||||||
|
// Encoder callback when encoding is complete.
|
||||||
|
class EncodeCallback : public EncodeCompleteCallback {
|
||||||
|
public:
|
||||||
|
EncodeCallback(nsIGlobalObject* aGlobal, BlobCallback* aCallback,
|
||||||
|
OffscreenCanvasDisplayHelper* aOffscreenDisplay,
|
||||||
|
nsIPrincipal* aSubjectPrincipal)
|
||||||
|
: mGlobal(aGlobal),
|
||||||
|
mBlobCallback(aCallback),
|
||||||
|
mOffscreenDisplay(aOffscreenDisplay),
|
||||||
|
mSubjectPrincipal(aSubjectPrincipal) {}
|
||||||
|
|
||||||
|
// This is called on main thread.
|
||||||
|
MOZ_CAN_RUN_SCRIPT
|
||||||
|
nsresult ReceiveBlobImpl(already_AddRefed<BlobImpl> aBlobImpl) override {
|
||||||
|
MOZ_ASSERT(NS_IsMainThread());
|
||||||
|
|
||||||
|
RefPtr<BlobImpl> blobImpl = aBlobImpl;
|
||||||
|
|
||||||
|
RefPtr<Blob> blob;
|
||||||
|
|
||||||
|
if (blobImpl && (!mOffscreenDisplay ||
|
||||||
|
mOffscreenDisplay->CallerCanRead(*mSubjectPrincipal))) {
|
||||||
|
blob = Blob::Create(mGlobal, blobImpl);
|
||||||
|
}
|
||||||
|
|
||||||
|
RefPtr<BlobCallback> callback(std::move(mBlobCallback));
|
||||||
|
ErrorResult rv;
|
||||||
|
|
||||||
|
callback->Call(blob, rv);
|
||||||
|
|
||||||
|
mGlobal = nullptr;
|
||||||
|
MOZ_ASSERT(!mBlobCallback);
|
||||||
|
|
||||||
|
return rv.StealNSResult();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CanBeDeletedOnAnyThread() override {
|
||||||
|
// EncodeCallback is used from the main thread only.
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
nsCOMPtr<nsIGlobalObject> mGlobal;
|
||||||
|
RefPtr<BlobCallback> mBlobCallback;
|
||||||
|
RefPtr<OffscreenCanvasDisplayHelper> mOffscreenDisplay;
|
||||||
|
RefPtr<nsIPrincipal> mSubjectPrincipal;
|
||||||
|
};
|
||||||
|
|
||||||
|
RefPtr<EncodeCompleteCallback> callback = new EncodeCallback(
|
||||||
|
global, &aCallback, recheckCanRead ? mOffscreenDisplay.get() : nullptr,
|
||||||
|
recheckCanRead ? &aSubjectPrincipal : nullptr);
|
||||||
|
|
||||||
|
CanvasRenderingContextHelper::ToBlob(aCx, callback, aType, aParams,
|
||||||
usePlaceholder, aRv);
|
usePlaceholder, aRv);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1086,7 +1150,12 @@ already_AddRefed<nsISupports> HTMLCanvasElement::GetContext(
|
|||||||
|
|
||||||
CSSIntSize HTMLCanvasElement::GetSize() { return GetWidthHeight(); }
|
CSSIntSize HTMLCanvasElement::GetSize() { return GetWidthHeight(); }
|
||||||
|
|
||||||
bool HTMLCanvasElement::IsWriteOnly() const { return mWriteOnly; }
|
bool HTMLCanvasElement::IsWriteOnly() const {
|
||||||
|
if (mOffscreenDisplay && mOffscreenDisplay->IsWriteOnly()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return mWriteOnly;
|
||||||
|
}
|
||||||
|
|
||||||
void HTMLCanvasElement::SetWriteOnly(
|
void HTMLCanvasElement::SetWriteOnly(
|
||||||
nsIPrincipal* aExpandedReader /* = nullptr */) {
|
nsIPrincipal* aExpandedReader /* = nullptr */) {
|
||||||
@@ -1098,6 +1167,10 @@ void HTMLCanvasElement::SetWriteOnly(
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool HTMLCanvasElement::CallerCanRead(nsIPrincipal& aPrincipal) const {
|
bool HTMLCanvasElement::CallerCanRead(nsIPrincipal& aPrincipal) const {
|
||||||
|
if (mOffscreenDisplay && !mOffscreenDisplay->CallerCanRead(aPrincipal)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
if (!mWriteOnly) {
|
if (!mWriteOnly) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -373,13 +373,13 @@ class HTMLCanvasElement final : public nsGenericHTMLElement,
|
|||||||
RefPtr<layers::ImageContainer> mImageContainer;
|
RefPtr<layers::ImageContainer> mImageContainer;
|
||||||
RefPtr<HTMLCanvasElementObserver> mContextObserver;
|
RefPtr<HTMLCanvasElementObserver> mContextObserver;
|
||||||
|
|
||||||
public:
|
|
||||||
// Record whether this canvas should be write-only or not.
|
// Record whether this canvas should be write-only or not.
|
||||||
// We set this when script paints an image from a different origin.
|
// We set this when script paints an image from a different origin.
|
||||||
// We also transitively set it when script paints a canvas which
|
// We also transitively set it when script paints a canvas which
|
||||||
// is itself write-only.
|
// is itself write-only.
|
||||||
bool mWriteOnly;
|
bool mWriteOnly;
|
||||||
|
|
||||||
|
public:
|
||||||
// When this canvas is (only) tainted by an image from an extension
|
// When this canvas is (only) tainted by an image from an extension
|
||||||
// content script, allow reads from the same extension afterwards.
|
// content script, allow reads from the same extension afterwards.
|
||||||
RefPtr<nsIPrincipal> mExpandedReader;
|
RefPtr<nsIPrincipal> mExpandedReader;
|
||||||
|
|||||||
Reference in New Issue
Block a user