From 6eb3e9c2c901e7ad58939656dc946276ee779114 Mon Sep 17 00:00:00 2001 From: Chung-Sheng Fu Date: Tue, 22 Aug 2017 14:23:41 +0800 Subject: [PATCH] Bug 967895 - Ask for placeholder data when image extraction is not allowed (Tor 6253). r=jrmuizel MozReview-Commit-ID: AJ5F6M5S83U --HG-- extra : rebase_source : 894b16575ebbccc26c5b639d7526cb473501d9d2 --- dom/bindings/Bindings.conf | 2 +- dom/canvas/CanvasRenderingContext2D.cpp | 43 +++++- dom/canvas/CanvasRenderingContext2D.h | 8 +- dom/canvas/CanvasRenderingContextHelper.cpp | 5 +- dom/canvas/CanvasRenderingContextHelper.h | 4 +- dom/canvas/CanvasUtils.cpp | 140 ++++++++++++++++++++ dom/canvas/CanvasUtils.h | 3 + dom/canvas/OffscreenCanvas.cpp | 9 +- dom/html/HTMLCanvasElement.cpp | 61 +++++++-- dom/html/HTMLCanvasElement.h | 6 +- dom/ipc/PBrowser.ipdl | 8 ++ dom/ipc/TabParent.cpp | 21 +++ dom/ipc/TabParent.h | 1 + dom/media/imagecapture/CaptureTask.cpp | 1 + 14 files changed, 281 insertions(+), 31 deletions(-) diff --git a/dom/bindings/Bindings.conf b/dom/bindings/Bindings.conf index 6bdde83cfcea..ea5006f73b47 100644 --- a/dom/bindings/Bindings.conf +++ b/dom/bindings/Bindings.conf @@ -124,7 +124,7 @@ DOMInterfaces = { 'CanvasRenderingContext2D': { 'implicitJSContext': [ - 'createImageData', 'getImageData' + 'createImageData', 'getImageData', 'isPointInPath', 'isPointInStroke' ], 'binaryNames': { 'mozImageSmoothingEnabled': 'imageSmoothingEnabled' diff --git a/dom/canvas/CanvasRenderingContext2D.cpp b/dom/canvas/CanvasRenderingContext2D.cpp index 70886176dcc1..0b7cfc26a960 100644 --- a/dom/canvas/CanvasRenderingContext2D.cpp +++ b/dom/canvas/CanvasRenderingContext2D.cpp @@ -4905,12 +4905,19 @@ CanvasRenderingContext2D::LineDashOffset() const { } bool -CanvasRenderingContext2D::IsPointInPath(double aX, double aY, const CanvasWindingRule& aWinding) +CanvasRenderingContext2D::IsPointInPath(JSContext* aCx, double aX, double aY, const CanvasWindingRule& aWinding) { if (!FloatValidate(aX, aY)) { return false; } + // Check for site-specific permission and return false if no permission. + if (mCanvasElement) { + nsCOMPtr ownerDoc = mCanvasElement->OwnerDoc(); + if (!ownerDoc || !CanvasUtils::IsImageExtractionAllowed(ownerDoc, aCx)) + return false; + } + EnsureUserSpacePath(aWinding); if (!mPath) { return false; @@ -4923,7 +4930,7 @@ CanvasRenderingContext2D::IsPointInPath(double aX, double aY, const CanvasWindin return mPath->ContainsPoint(Point(aX, aY), mTarget->GetTransform()); } -bool CanvasRenderingContext2D::IsPointInPath(const CanvasPath& aPath, double aX, double aY, const CanvasWindingRule& aWinding) +bool CanvasRenderingContext2D::IsPointInPath(JSContext* aCx, const CanvasPath& aPath, double aX, double aY, const CanvasWindingRule& aWinding) { if (!FloatValidate(aX, aY)) { return false; @@ -4940,12 +4947,19 @@ bool CanvasRenderingContext2D::IsPointInPath(const CanvasPath& aPath, double aX, } bool -CanvasRenderingContext2D::IsPointInStroke(double aX, double aY) +CanvasRenderingContext2D::IsPointInStroke(JSContext* aCx, double aX, double aY) { if (!FloatValidate(aX, aY)) { return false; } + // Check for site-specific permission and return false if no permission. + if (mCanvasElement) { + nsCOMPtr ownerDoc = mCanvasElement->OwnerDoc(); + if (!ownerDoc || !CanvasUtils::IsImageExtractionAllowed(ownerDoc, aCx)) + return false; + } + EnsureUserSpacePath(); if (!mPath) { return false; @@ -4967,7 +4981,7 @@ CanvasRenderingContext2D::IsPointInStroke(double aX, double aY) return mPath->StrokeContainsPoint(strokeOptions, Point(aX, aY), mTarget->GetTransform()); } -bool CanvasRenderingContext2D::IsPointInStroke(const CanvasPath& aPath, double aX, double aY) +bool CanvasRenderingContext2D::IsPointInStroke(JSContext* aCx, const CanvasPath& aPath, double aX, double aY) { if (!FloatValidate(aX, aY)) { return false; @@ -5808,7 +5822,17 @@ CanvasRenderingContext2D::GetImageDataArray(JSContext* aCx, IntRect dstWriteRect = srcReadRect; dstWriteRect.MoveBy(-aX, -aY); - { + // Check for site-specific permission. This check is not needed if the + // canvas was created with a docshell (that is only done for special + // internal uses). + bool usePlaceholder = false; + if (mCanvasElement) { + nsCOMPtr ownerDoc = mCanvasElement->OwnerDoc(); + usePlaceholder = !ownerDoc || + !CanvasUtils::IsImageExtractionAllowed(ownerDoc, aCx); + } + + do { JS::AutoCheckCannotGC nogc; bool isShared; uint8_t* data = JS_GetUint8ClampedArrayData(darray, &isShared, nogc); @@ -5816,6 +5840,13 @@ CanvasRenderingContext2D::GetImageDataArray(JSContext* aCx, uint32_t srcStride = rawData.mStride; uint8_t* src = rawData.mData + srcReadRect.y * srcStride + srcReadRect.x * 4; + + // Return all-white, opaque pixel data if no permission. + if (usePlaceholder) { + memset(data, 0xFF, len.value()); + break; + } + uint8_t* dst = data + dstWriteRect.y * (aWidth * 4) + dstWriteRect.x * 4; if (mOpaque) { @@ -5827,7 +5858,7 @@ CanvasRenderingContext2D::GetImageDataArray(JSContext* aCx, dst, aWidth * 4, SurfaceFormat::R8G8B8A8, dstWriteRect.Size()); } - } + } while (false); readback->Unmap(); *aRetval = darray; diff --git a/dom/canvas/CanvasRenderingContext2D.h b/dom/canvas/CanvasRenderingContext2D.h index 3eb2d5fb5418..b4c8562d355b 100644 --- a/dom/canvas/CanvasRenderingContext2D.h +++ b/dom/canvas/CanvasRenderingContext2D.h @@ -204,10 +204,10 @@ public: bool DrawCustomFocusRing(mozilla::dom::Element& aElement); void Clip(const CanvasWindingRule& aWinding); void Clip(const CanvasPath& aPath, const CanvasWindingRule& aWinding); - bool IsPointInPath(double aX, double aY, const CanvasWindingRule& aWinding); - bool IsPointInPath(const CanvasPath& aPath, double aX, double aY, const CanvasWindingRule& aWinding); - bool IsPointInStroke(double aX, double aY); - bool IsPointInStroke(const CanvasPath& aPath, double aX, double aY); + bool IsPointInPath(JSContext* aCx, double aX, double aY, const CanvasWindingRule& aWinding); + bool IsPointInPath(JSContext* aCx, const CanvasPath& aPath, double aX, double aY, const CanvasWindingRule& aWinding); + bool IsPointInStroke(JSContext* aCx, double aX, double aY); + bool IsPointInStroke(JSContext* aCx, const CanvasPath& aPath, double aX, double aY); void FillText(const nsAString& aText, double aX, double aY, const Optional& aMaxWidth, mozilla::ErrorResult& aError); diff --git a/dom/canvas/CanvasRenderingContextHelper.cpp b/dom/canvas/CanvasRenderingContextHelper.cpp index 370d6c3c5ad0..73af82a89456 100644 --- a/dom/canvas/CanvasRenderingContextHelper.cpp +++ b/dom/canvas/CanvasRenderingContextHelper.cpp @@ -25,6 +25,7 @@ CanvasRenderingContextHelper::ToBlob(JSContext* aCx, BlobCallback& aCallback, const nsAString& aType, JS::Handle aParams, + bool aUsePlaceholder, ErrorResult& aRv) { // Encoder callback when encoding is complete. @@ -58,7 +59,7 @@ CanvasRenderingContextHelper::ToBlob(JSContext* aCx, RefPtr callback = new EncodeCallback(aGlobal, &aCallback); - ToBlob(aCx, aGlobal, callback, aType, aParams, aRv); + ToBlob(aCx, aGlobal, callback, aType, aParams, aUsePlaceholder, aRv); } void @@ -67,6 +68,7 @@ CanvasRenderingContextHelper::ToBlob(JSContext* aCx, EncodeCompleteCallback* aCallback, const nsAString& aType, JS::Handle aParams, + bool aUsePlaceholder, ErrorResult& aRv) { nsAutoString type; @@ -107,6 +109,7 @@ CanvasRenderingContextHelper::ToBlob(JSContext* aCx, Move(imageBuffer), format, GetWidthHeight(), + aUsePlaceholder, callback); } diff --git a/dom/canvas/CanvasRenderingContextHelper.h b/dom/canvas/CanvasRenderingContextHelper.h index d28864f02971..8288217d3ac5 100644 --- a/dom/canvas/CanvasRenderingContextHelper.h +++ b/dom/canvas/CanvasRenderingContextHelper.h @@ -58,11 +58,11 @@ protected: void ToBlob(JSContext* aCx, nsIGlobalObject* global, BlobCallback& aCallback, const nsAString& aType, JS::Handle aParams, - ErrorResult& aRv); + bool aUsePlaceholder, ErrorResult& aRv); void ToBlob(JSContext* aCx, nsIGlobalObject* aGlobal, EncodeCompleteCallback* aCallback, const nsAString& aType, JS::Handle aParams, - ErrorResult& aRv); + bool aUsePlaceholder, ErrorResult& aRv); virtual already_AddRefed CreateContext(CanvasContextType aContextType); diff --git a/dom/canvas/CanvasUtils.cpp b/dom/canvas/CanvasUtils.cpp index 86e62a46693d..90641090e009 100644 --- a/dom/canvas/CanvasUtils.cpp +++ b/dom/canvas/CanvasUtils.cpp @@ -13,6 +13,7 @@ #include "nsICanvasRenderingContextInternal.h" #include "nsIHTMLCollection.h" #include "mozilla/dom/HTMLCanvasElement.h" +#include "mozilla/dom/TabChild.h" #include "nsIPrincipal.h" #include "nsGfxCIID.h" @@ -23,11 +24,150 @@ #include "mozilla/gfx/Matrix.h" #include "WebGL2Context.h" +#include "nsIScriptObjectPrincipal.h" +#include "nsIPermissionManager.h" +#include "nsIObserverService.h" +#include "mozilla/Services.h" +#include "mozIThirdPartyUtil.h" +#include "nsContentUtils.h" +#include "nsUnicharUtils.h" +#include "nsPrintfCString.h" +#include "nsIConsoleService.h" +#include "jsapi.h" + +#define TOPIC_CANVAS_PERMISSIONS_PROMPT "canvas-permissions-prompt" +#define PERMISSION_CANVAS_EXTRACT_DATA "canvas/extractData" + using namespace mozilla::gfx; namespace mozilla { namespace CanvasUtils { +bool IsImageExtractionAllowed(nsIDocument *aDocument, JSContext *aCx) +{ + // Do the rest of the checks only if privacy.resistFingerprinting is on. + if (!nsContentUtils::ShouldResistFingerprinting()) { + return true; + } + + // Don't proceed if we don't have a document or JavaScript context. + if (!aDocument || !aCx) { + return false; + } + + // Documents with system principal can always extract canvas data. + nsPIDOMWindowOuter *win = aDocument->GetWindow(); + nsCOMPtr sop(do_QueryInterface(win)); + if (sop && nsContentUtils::IsSystemPrincipal(sop->GetPrincipal())) { + return true; + } + + // Always give permission to chrome scripts (e.g. Page Inspector). + if (nsContentUtils::ThreadsafeIsCallerChrome()) { + return true; + } + + // Get the document URI and its spec. + nsIURI *docURI = aDocument->GetDocumentURI(); + nsCString docURISpec; + docURI->GetSpec(docURISpec); + + // Allow local files to extract canvas data. + bool isFileURL; + (void) docURI->SchemeIs("file", &isFileURL); + if (isFileURL) { + return true; + } + + // Get calling script file and line for logging. + JS::AutoFilename scriptFile; + unsigned scriptLine = 0; + bool isScriptKnown = false; + if (JS::DescribeScriptedCaller(aCx, &scriptFile, &scriptLine)) { + isScriptKnown = true; + // Don't show canvas prompt for PDF.js + if (scriptFile.get() && + strcmp(scriptFile.get(), "resource://pdf.js/build/pdf.js") == 0) { + return true; + } + } + + nsIDocument* topLevelDocument = aDocument->GetTopLevelContentDocument(); + nsIURI *topLevelDocURI = topLevelDocument ? topLevelDocument->GetDocumentURI() : nullptr; + nsCString topLevelDocURISpec; + if (topLevelDocURI) { + topLevelDocURI->GetSpec(topLevelDocURISpec); + } + + // Load Third Party Util service. + nsresult rv; + nsCOMPtr thirdPartyUtil = + do_GetService(THIRDPARTYUTIL_CONTRACTID, &rv); + NS_ENSURE_SUCCESS(rv, false); + + // Block all third-party attempts to extract canvas. + bool isThirdParty = true; + rv = thirdPartyUtil->IsThirdPartyURI(topLevelDocURI, docURI, &isThirdParty); + NS_ENSURE_SUCCESS(rv, false); + if (isThirdParty) { + nsAutoCString message; + message.AppendPrintf("Blocked third party %s in page %s from extracting canvas data.", + docURISpec.get(), topLevelDocURISpec.get()); + if (isScriptKnown) { + message.AppendPrintf(" %s:%u.", scriptFile.get(), scriptLine); + } + nsContentUtils::LogMessageToConsole(message.get()); + return false; + } + + // Load Permission Manager service. + nsCOMPtr permissionManager = + do_GetService(NS_PERMISSIONMANAGER_CONTRACTID, &rv); + NS_ENSURE_SUCCESS(rv, false); + + // Check if the site has permission to extract canvas data. + // Either permit or block extraction if a stored permission setting exists. + uint32_t permission; + rv = permissionManager->TestPermission(topLevelDocURI, + PERMISSION_CANVAS_EXTRACT_DATA, + &permission); + NS_ENSURE_SUCCESS(rv, false); + switch (permission) { + case nsIPermissionManager::ALLOW_ACTION: + return true; + case nsIPermissionManager::DENY_ACTION: + return false; + default: + break; + } + + // At this point, permission is unknown (nsIPermissionManager::UNKNOWN_ACTION). + nsAutoCString message; + message.AppendPrintf("Blocked %s in page %s from extracting canvas data.", + docURISpec.get(), topLevelDocURISpec.get()); + if (isScriptKnown) { + message.AppendPrintf(" %s:%u.", scriptFile.get(), scriptLine); + } + nsContentUtils::LogMessageToConsole(message.get()); + + // Prompt the user (asynchronous). + if (XRE_IsContentProcess()) { + TabChild* tabChild = TabChild::GetFrom(win); + if (tabChild) { + tabChild->SendShowCanvasPermissionPrompt(topLevelDocURISpec); + } + } else { + nsCOMPtr obs = mozilla::services::GetObserverService(); + if (obs) { + obs->NotifyObservers(win, TOPIC_CANVAS_PERMISSIONS_PROMPT, + NS_ConvertUTF8toUTF16(topLevelDocURISpec).get()); + } + } + + // We don't extract the image for now -- user may override at prompt. + return false; +} + bool GetCanvasContextType(const nsAString& str, dom::CanvasContextType* const out_type) { diff --git a/dom/canvas/CanvasUtils.h b/dom/canvas/CanvasUtils.h index 4e39ebffde63..af48de2cda9e 100644 --- a/dom/canvas/CanvasUtils.h +++ b/dom/canvas/CanvasUtils.h @@ -49,6 +49,9 @@ void DoDrawImageSecurityCheck(dom::HTMLCanvasElement *aCanvasElement, // Check if the context is chrome or has the permission to drawWindow bool HasDrawWindowPrivilege(JSContext* aCx, JSObject* aObj); +// Check site-specific permission and display prompt if appropriate. +bool IsImageExtractionAllowed(nsIDocument *aDocument, JSContext *aCx); + // Make a double out of |v|, treating undefined values as 0.0 (for // the sake of sparse arrays). Return true iff coercion // succeeded. diff --git a/dom/canvas/OffscreenCanvas.cpp b/dom/canvas/OffscreenCanvas.cpp index 0586b963368d..d17d6acae6e7 100644 --- a/dom/canvas/OffscreenCanvas.cpp +++ b/dom/canvas/OffscreenCanvas.cpp @@ -286,8 +286,13 @@ OffscreenCanvas::ToBlob(JSContext* aCx, RefPtr callback = new EncodeCallback(global, promise); - CanvasRenderingContextHelper::ToBlob(aCx, global, - callback, aType, aParams, aRv); + // TODO: Can we obtain the context and document here somehow + // so that we can decide when usePlaceholder should be true/false? + // See https://trac.torproject.org/18599 + // For now, we always return a placeholder if fingerprinting resistance is on. + bool usePlaceholder = nsContentUtils::ShouldResistFingerprinting(); + CanvasRenderingContextHelper::ToBlob(aCx, global, callback, aType, aParams, + usePlaceholder, aRv); return promise.forget(); } diff --git a/dom/html/HTMLCanvasElement.cpp b/dom/html/HTMLCanvasElement.cpp index da91cc9b7576..471940ad8365 100644 --- a/dom/html/HTMLCanvasElement.cpp +++ b/dom/html/HTMLCanvasElement.cpp @@ -44,6 +44,7 @@ #include "nsRefreshDriver.h" #include "nsStreamUtils.h" #include "ActiveLayerTracker.h" +#include "CanvasUtils.h" #include "VRManagerChild.h" #include "WebGL1Context.h" #include "WebGL2Context.h" @@ -62,8 +63,10 @@ class RequestedFrameRefreshObserver : public nsARefreshObserver public: RequestedFrameRefreshObserver(HTMLCanvasElement* const aOwningElement, - nsRefreshDriver* aRefreshDriver) + nsRefreshDriver* aRefreshDriver, + bool aReturnPlaceholderData) : mRegistered(false), + mReturnPlaceholderData(aReturnPlaceholderData), mOwningElement(aOwningElement), mRefreshDriver(aRefreshDriver) { @@ -71,7 +74,8 @@ public: } static already_AddRefed - CopySurface(const RefPtr& aSurface) + CopySurface(const RefPtr& aSurface, + bool aReturnPlaceholderData) { RefPtr data = aSurface->GetDataSurface(); if (!data) { @@ -100,12 +104,23 @@ public: MOZ_ASSERT(data->GetSize() == copy->GetSize()); MOZ_ASSERT(data->GetFormat() == copy->GetFormat()); - memcpy(write.GetData(), read.GetData(), - write.GetStride() * copy->GetSize().height); + if (aReturnPlaceholderData) { + // If returning placeholder data, fill the frame copy with white pixels. + memset(write.GetData(), 0xFF, + write.GetStride() * copy->GetSize().height); + } else { + memcpy(write.GetData(), read.GetData(), + write.GetStride() * copy->GetSize().height); + } return copy.forget(); } + void SetReturnPlaceholderData(bool aReturnPlaceholderData) + { + mReturnPlaceholderData = aReturnPlaceholderData; + } + void WillRefresh(TimeStamp aTime) override { MOZ_ASSERT(NS_IsMainThread()); @@ -144,7 +159,7 @@ public: { AUTO_PROFILER_LABEL( "RequestedFrameRefreshObserver::WillRefresh:CopySurface", OTHER); - copy = CopySurface(snapshot); + copy = CopySurface(snapshot, mReturnPlaceholderData); if (!copy) { return; } @@ -201,6 +216,7 @@ private: } bool mRegistered; + bool mReturnPlaceholderData; HTMLCanvasElement* const mOwningElement; RefPtr mRefreshDriver; }; @@ -754,7 +770,14 @@ HTMLCanvasElement::CaptureStream(const Optional& aFrameRate, new CanvasCaptureTrackSource(principal, stream)); stream->AddTrackInternal(track); - rv = RegisterFrameCaptureListener(stream->FrameCaptureListener()); + // Check site-specific permission and display prompt if appropriate. + // If no permission, arrange for the frame capture listener to return + // all-white, opaque image data. + bool usePlaceholder = !CanvasUtils::IsImageExtractionAllowed( + OwnerDoc(), + nsContentUtils::GetCurrentJSContext()); + + rv = RegisterFrameCaptureListener(stream->FrameCaptureListener(), usePlaceholder); if (NS_FAILED(rv)) { aRv.Throw(rv); return nullptr; @@ -764,13 +787,18 @@ HTMLCanvasElement::CaptureStream(const Optional& aFrameRate, } nsresult -HTMLCanvasElement::ExtractData(nsAString& aType, +HTMLCanvasElement::ExtractData(JSContext* aCx, + nsAString& aType, const nsAString& aOptions, nsIInputStream** aStream) { + // Check site-specific permission and display prompt if appropriate. + // If no permission, return all-white, opaque image data. + bool usePlaceholder = !CanvasUtils::IsImageExtractionAllowed(OwnerDoc(), aCx); return ImageEncoder::ExtractData(aType, aOptions, GetSize(), + usePlaceholder, mCurrentContext, mAsyncCanvasRenderer, aStream); @@ -800,12 +828,12 @@ HTMLCanvasElement::ToDataURLImpl(JSContext* aCx, } nsCOMPtr stream; - rv = ExtractData(type, params, getter_AddRefs(stream)); + rv = ExtractData(aCx, type, params, getter_AddRefs(stream)); // If there are unrecognized custom parse options, we should fall back to // the default values for the encoder without any options at all. if (rv == NS_ERROR_INVALID_ARG && usingCustomParseOptions) { - rv = ExtractData(type, EmptyString(), getter_AddRefs(stream)); + rv = ExtractData(aCx, type, EmptyString(), getter_AddRefs(stream)); } NS_ENSURE_SUCCESS(rv, rv); @@ -855,8 +883,11 @@ HTMLCanvasElement::ToBlob(JSContext* aCx, return; } + // Check site-specific permission and display prompt if appropriate. + // If no permission, return all-white, opaque image data. + bool usePlaceholder = !CanvasUtils::IsImageExtractionAllowed(OwnerDoc(), aCx); CanvasRenderingContextHelper::ToBlob(aCx, global, aCallback, aType, - aParams, aRv); + aParams, usePlaceholder, aRv); } @@ -925,7 +956,8 @@ HTMLCanvasElement::MozGetAsFileImpl(const nsAString& aName, { nsCOMPtr stream; nsAutoString type(aType); - nsresult rv = ExtractData(type, EmptyString(), getter_AddRefs(stream)); + nsresult rv = ExtractData(nsContentUtils::GetCurrentJSContext(), + type, EmptyString(), getter_AddRefs(stream)); NS_ENSURE_SUCCESS(rv, rv); uint64_t imgSize; @@ -1246,7 +1278,8 @@ HTMLCanvasElement::IsContextCleanForFrameCapture() } nsresult -HTMLCanvasElement::RegisterFrameCaptureListener(FrameCaptureListener* aListener) +HTMLCanvasElement::RegisterFrameCaptureListener(FrameCaptureListener* aListener, + bool aReturnPlaceholderData) { WeakPtr listener = aListener; @@ -1285,7 +1318,9 @@ HTMLCanvasElement::RegisterFrameCaptureListener(FrameCaptureListener* aListener) } mRequestedFrameRefreshObserver = - new RequestedFrameRefreshObserver(this, driver); + new RequestedFrameRefreshObserver(this, driver, aReturnPlaceholderData); + } else { + mRequestedFrameRefreshObserver->SetReturnPlaceholderData(aReturnPlaceholderData); } mRequestedFrameListeners.AppendElement(listener); diff --git a/dom/html/HTMLCanvasElement.h b/dom/html/HTMLCanvasElement.h index 9758d97b3651..859386a50f69 100644 --- a/dom/html/HTMLCanvasElement.h +++ b/dom/html/HTMLCanvasElement.h @@ -262,7 +262,8 @@ public: * caller's responsibility to keep them alive. Once a registered * FrameCaptureListener is destroyed it will be automatically deregistered. */ - nsresult RegisterFrameCaptureListener(FrameCaptureListener* aListener); + nsresult RegisterFrameCaptureListener(FrameCaptureListener* aListener, + bool aReturnPlaceholderData); /* * Returns true when there is at least one registered FrameCaptureListener @@ -348,7 +349,8 @@ protected: virtual already_AddRefed CreateContext(CanvasContextType aContextType) override; - nsresult ExtractData(nsAString& aType, + nsresult ExtractData(JSContext* aCx, + nsAString& aType, const nsAString& aOptions, nsIInputStream** aStream); nsresult ToDataURLImpl(JSContext* aCx, diff --git a/dom/ipc/PBrowser.ipdl b/dom/ipc/PBrowser.ipdl index 175e6337b2da..107a207c4fac 100644 --- a/dom/ipc/PBrowser.ipdl +++ b/dom/ipc/PBrowser.ipdl @@ -594,6 +594,14 @@ parent: */ async RequestCrossBrowserNavigation(uint32_t aGlobalIndex); + /** + * This function is used to notify the parent that it should display a + * canvas permission prompt. + * + * @param aFirstPartyURI first party of the tab that is requesting access. + */ + async ShowCanvasPermissionPrompt(nsCString aFirstPartyURI); + child: /** * Notify the remote browser that it has been Show()n on this diff --git a/dom/ipc/TabParent.cpp b/dom/ipc/TabParent.cpp index 195c6f1583ae..fa1224be442a 100644 --- a/dom/ipc/TabParent.cpp +++ b/dom/ipc/TabParent.cpp @@ -3604,6 +3604,27 @@ TabParent::RecvRequestCrossBrowserNavigation(const uint32_t& aGlobalIndex) return IPC_OK(); } +mozilla::ipc::IPCResult +TabParent::RecvShowCanvasPermissionPrompt(const nsCString& aFirstPartyURI) +{ + nsCOMPtr browser = do_QueryInterface(mFrameElement); + if (!browser) { + // If the tab is being closed, the browser may not be available. + // In this case we can ignore the request. + return IPC_OK(); + } + nsCOMPtr os = services::GetObserverService(); + if (!os) { + return IPC_FAIL_NO_REASON(this); + } + nsresult rv = os->NotifyObservers(browser, "canvas-permissions-prompt", + NS_ConvertUTF8toUTF16(aFirstPartyURI).get()); + if (NS_FAILED(rv)) { + return IPC_FAIL_NO_REASON(this); + } + return IPC_OK(); +} + void TabParent::LiveResizeStarted() { diff --git a/dom/ipc/TabParent.h b/dom/ipc/TabParent.h index 0cbaabafa302..88108b306a6c 100644 --- a/dom/ipc/TabParent.h +++ b/dom/ipc/TabParent.h @@ -648,6 +648,7 @@ protected: const bool& aTruncate) override; virtual mozilla::ipc::IPCResult RecvRequestCrossBrowserNavigation(const uint32_t& aGlobalIndex) override; + virtual mozilla::ipc::IPCResult RecvShowCanvasPermissionPrompt(const nsCString& aFirstPartyURI) override; ContentCacheInParent mContentCache; diff --git a/dom/media/imagecapture/CaptureTask.cpp b/dom/media/imagecapture/CaptureTask.cpp index a39472199bd3..70186cff9389 100644 --- a/dom/media/imagecapture/CaptureTask.cpp +++ b/dom/media/imagecapture/CaptureTask.cpp @@ -157,6 +157,7 @@ CaptureTask::SetCurrentFrames(const VideoSegment& aSegment) options, false, image, + false, new EncodeComplete(this)); if (NS_FAILED(rv)) { PostTrackEndEvent();