diff --git a/dom/canvas/CanvasRenderingContext2D.cpp b/dom/canvas/CanvasRenderingContext2D.cpp index 4879bbb298c9..0e3541c38f2c 100644 --- a/dom/canvas/CanvasRenderingContext2D.cpp +++ b/dom/canvas/CanvasRenderingContext2D.cpp @@ -2144,8 +2144,9 @@ UniquePtr CanvasRenderingContext2D::GetImageBuffer( if (ret && ShouldResistFingerprinting(RFPTarget::CanvasRandomization)) { nsRFPService::RandomizePixels( - GetCookieJarSettings(), ret.get(), out_imageSize->width, - out_imageSize->height, out_imageSize->width * out_imageSize->height * 4, + GetCookieJarSettings(), PrincipalOrNull(), ret.get(), + out_imageSize->width, out_imageSize->height, + out_imageSize->width * out_imageSize->height * 4, SurfaceFormat::A8R8G8B8_UINT32); } @@ -6407,9 +6408,10 @@ nsresult CanvasRenderingContext2D::GetImageDataArray( // holder. const IntSize size = readback->GetSize(); - nsRFPService::RandomizePixels( - GetCookieJarSettings(), rawData.mData, size.width, size.height, - size.height * size.width * 4, SurfaceFormat::A8R8G8B8_UINT32); + nsRFPService::RandomizePixels(GetCookieJarSettings(), PrincipalOrNull(), + rawData.mData, size.width, size.height, + size.height * size.width * 4, + SurfaceFormat::A8R8G8B8_UINT32); } JS::AutoCheckCannotGC nogc; diff --git a/dom/canvas/CanvasUtils.cpp b/dom/canvas/CanvasUtils.cpp index 83647d8066ae..b27fd570fd57 100644 --- a/dom/canvas/CanvasUtils.cpp +++ b/dom/canvas/CanvasUtils.cpp @@ -322,10 +322,6 @@ ImageExtraction ImageExtractionResult(dom::HTMLCanvasElement* aCanvasElement, } if (ownerDoc->ShouldResistFingerprinting(RFPTarget::CanvasRandomization)) { - if (GetCanvasExtractDataPermission(aPrincipal) == - nsIPermissionManager::ALLOW_ACTION) { - return ImageExtraction::Unrestricted; - } return ImageExtraction::Randomize; } diff --git a/dom/canvas/CanvasUtils.h b/dom/canvas/CanvasUtils.h index 84f06d176a25..3d9bd72598e9 100644 --- a/dom/canvas/CanvasUtils.h +++ b/dom/canvas/CanvasUtils.h @@ -59,10 +59,16 @@ bool HasDrawWindowPrivilege(JSContext* aCx, JSObject* aObj); // Check if the context has permission to use OffscreenCanvas. bool IsOffscreenCanvasEnabled(JSContext* aCx, JSObject* aObj); +// Check if the principal has "canvas" permission. +uint32_t GetCanvasExtractDataPermission(nsIPrincipal& aPrincipal); + // Check site-specific permission and display prompt if appropriate. bool IsImageExtractionAllowed(dom::Document* aDocument, JSContext* aCx, nsIPrincipal& aPrincipal); +bool IsImageExtractionAllowed(dom::OffscreenCanvas* aOffscreenCanvas, + JSContext* aCx, nsIPrincipal& aPrincipal); + uint32_t GetCanvasExtractDataPermission(nsIPrincipal& aPrincipal); enum class ImageExtraction { diff --git a/dom/canvas/ClientWebGLContext.cpp b/dom/canvas/ClientWebGLContext.cpp index ba79bca2c3b1..341608daa132 100644 --- a/dom/canvas/ClientWebGLContext.cpp +++ b/dom/canvas/ClientWebGLContext.cpp @@ -13,6 +13,7 @@ #include "HostWebGLContext.h" #include "js/PropertyAndElement.h" // JS_DefineElement #include "js/ScalarType.h" // js::Scalar::Type +#include "mozilla/dom/CanvasUtils.h" #include "mozilla/dom/Document.h" #include "mozilla/dom/ToJSValue.h" #include "mozilla/dom/TypedArray.h" @@ -35,6 +36,7 @@ #include "mozilla/StaticPrefs_webgl.h" #include "nsContentUtils.h" #include "nsDisplayList.h" +#include "nsIPermissionManager.h" #include "TexUnpackBlob.h" #include "WebGLFormats.h" #include "WebGLMethodDispatcher.h" @@ -1344,7 +1346,8 @@ UniquePtr ClientWebGLContext::GetImageBuffer( if (ShouldResistFingerprinting(RFPTarget::CanvasRandomization)) { return gfxUtils::GetImageBufferWithRandomNoise( - dataSurface, premultAlpha, GetCookieJarSettings(), out_format); + dataSurface, premultAlpha, GetCookieJarSettings(), PrincipalOrNull(), + out_format); } return gfxUtils::GetImageBuffer(dataSurface, premultAlpha, out_format); @@ -1365,7 +1368,7 @@ ClientWebGLContext::GetInputStream(const char* mimeType, if (ShouldResistFingerprinting(RFPTarget::CanvasRandomization)) { return gfxUtils::GetInputStreamWithRandomNoise( dataSurface, premultAlpha, mimeType, encoderOptions, - GetCookieJarSettings(), out_stream); + GetCookieJarSettings(), PrincipalOrNull(), out_stream); } return gfxUtils::GetInputStream(dataSurface, premultAlpha, mimeType, diff --git a/dom/canvas/ImageBitmapRenderingContext.cpp b/dom/canvas/ImageBitmapRenderingContext.cpp index 8abe32150b3f..8987ed3ba6f0 100644 --- a/dom/canvas/ImageBitmapRenderingContext.cpp +++ b/dom/canvas/ImageBitmapRenderingContext.cpp @@ -195,8 +195,8 @@ mozilla::UniquePtr ImageBitmapRenderingContext::GetImageBuffer( if (ret && ShouldResistFingerprinting(RFPTarget::CanvasRandomization)) { nsRFPService::RandomizePixels( - GetCookieJarSettings(), ret.get(), data->GetSize().width, - data->GetSize().height, + GetCookieJarSettings(), PrincipalOrNull(), ret.get(), + data->GetSize().width, data->GetSize().height, data->GetSize().width * data->GetSize().height * 4, gfx::SurfaceFormat::A8R8G8B8_UINT32); } diff --git a/dom/canvas/OffscreenCanvas.cpp b/dom/canvas/OffscreenCanvas.cpp index 8e0167133b9a..b6c6a9eb1a3f 100644 --- a/dom/canvas/OffscreenCanvas.cpp +++ b/dom/canvas/OffscreenCanvas.cpp @@ -26,6 +26,7 @@ #include "ImageBitmap.h" #include "ImageBitmapRenderingContext.h" #include "nsContentUtils.h" +#include "nsIPermissionManager.h" #include "nsProxyRelease.h" #include "WebGLChild.h" @@ -503,8 +504,11 @@ already_AddRefed OffscreenCanvas::ConvertToBlob( RefPtr callback = CreateEncodeCompleteCallback(promise); - bool usePlaceholder = - ShouldResistFingerprinting(RFPTarget::CanvasImageExtractionPrompt); + + bool usePlaceholder = mCurrentContext && mCurrentContext->PrincipalOrNull() && + !CanvasUtils::IsImageExtractionAllowed( + this, nsContentUtils::GetCurrentJSContext(), + *mCurrentContext->PrincipalOrNull()); CanvasRenderingContextHelper::ToBlob(callback, type, encodeOptions, /* aUsingCustomOptions */ false, usePlaceholder, aRv); @@ -545,8 +549,10 @@ already_AddRefed OffscreenCanvas::ToBlob(JSContext* aCx, RefPtr callback = CreateEncodeCompleteCallback(promise); - bool usePlaceholder = - ShouldResistFingerprinting(RFPTarget::CanvasImageExtractionPrompt); + bool usePlaceholder = mCurrentContext && mCurrentContext->PrincipalOrNull() && + !CanvasUtils::IsImageExtractionAllowed( + this, nsContentUtils::GetCurrentJSContext(), + *mCurrentContext->PrincipalOrNull()); CanvasRenderingContextHelper::ToBlob(aCx, callback, aType, aParams, usePlaceholder, aRv); diff --git a/dom/canvas/OffscreenCanvasDisplayHelper.cpp b/dom/canvas/OffscreenCanvasDisplayHelper.cpp index d23421ef153d..ec3e8f66791e 100644 --- a/dom/canvas/OffscreenCanvasDisplayHelper.cpp +++ b/dom/canvas/OffscreenCanvasDisplayHelper.cpp @@ -577,9 +577,21 @@ UniquePtr OffscreenCanvasDisplayHelper::GetImageBuffer( } if (resistFingerprinting) { + nsIPrincipal* principal; + { + MutexAutoLock lock(mMutex); + if (mCanvasElement) { + principal = mCanvasElement->NodePrincipal(); + } + if (mOffscreenCanvas) { + principal = mOffscreenCanvas->GetParentObject() + ? mOffscreenCanvas->GetParentObject()->PrincipalOrNull() + : nullptr; + } + } nsRFPService::RandomizePixels( - cookieJarSettings, imageBuffer.get(), dataSurface->GetSize().width, - dataSurface->GetSize().height, + cookieJarSettings, principal, imageBuffer.get(), + dataSurface->GetSize().width, dataSurface->GetSize().height, dataSurface->GetSize().width * dataSurface->GetSize().height * 4, gfx::SurfaceFormat::A8R8G8B8_UINT32); } diff --git a/dom/webgpu/CanvasContext.cpp b/dom/webgpu/CanvasContext.cpp index 7d3da86482ff..999f0b0cccc5 100644 --- a/dom/webgpu/CanvasContext.cpp +++ b/dom/webgpu/CanvasContext.cpp @@ -326,9 +326,10 @@ mozilla::UniquePtr CanvasContext::GetImageBuffer( *out_imageSize = dataSurface->GetSize(); if (ShouldResistFingerprinting(RFPTarget::CanvasRandomization)) { - gfxUtils::GetImageBufferWithRandomNoise( - dataSurface, - /* aIsAlphaPremultiplied */ true, GetCookieJarSettings(), &*out_format); + gfxUtils::GetImageBufferWithRandomNoise(dataSurface, + /* aIsAlphaPremultiplied */ true, + GetCookieJarSettings(), + PrincipalOrNull(), &*out_format); } return gfxUtils::GetImageBuffer(dataSurface, /* aIsAlphaPremultiplied */ true, @@ -347,9 +348,9 @@ NS_IMETHODIMP CanvasContext::GetInputStream(const char* aMimeType, RefPtr dataSurface = snapshot->GetDataSurface(); if (ShouldResistFingerprinting(RFPTarget::CanvasRandomization)) { - gfxUtils::GetInputStreamWithRandomNoise( + return gfxUtils::GetInputStreamWithRandomNoise( dataSurface, /* aIsAlphaPremultiplied */ true, aMimeType, - aEncoderOptions, GetCookieJarSettings(), aStream); + aEncoderOptions, GetCookieJarSettings(), PrincipalOrNull(), aStream); } return gfxUtils::GetInputStream(dataSurface, /* aIsAlphaPremultiplied */ true, diff --git a/gfx/thebes/gfxUtils.cpp b/gfx/thebes/gfxUtils.cpp index c76bd12da008..1950d9e54005 100644 --- a/gfx/thebes/gfxUtils.cpp +++ b/gfx/thebes/gfxUtils.cpp @@ -1613,13 +1613,14 @@ UniquePtr gfxUtils::GetImageBuffer(gfx::DataSourceSurface* aSurface, /* static */ UniquePtr gfxUtils::GetImageBufferWithRandomNoise( gfx::DataSourceSurface* aSurface, bool aIsAlphaPremultiplied, - nsICookieJarSettings* aCookieJarSettings, int32_t* outFormat) { + nsICookieJarSettings* aCookieJarSettings, nsIPrincipal* aPrincipal, + int32_t* outFormat) { UniquePtr imageBuffer = GetImageBuffer(aSurface, aIsAlphaPremultiplied, outFormat); nsRFPService::RandomizePixels( - aCookieJarSettings, imageBuffer.get(), aSurface->GetSize().width, - aSurface->GetSize().height, + aCookieJarSettings, aPrincipal, imageBuffer.get(), + aSurface->GetSize().width, aSurface->GetSize().height, aSurface->GetSize().width * aSurface->GetSize().height * 4, SurfaceFormat::A8R8G8B8_UINT32); @@ -1651,7 +1652,8 @@ nsresult gfxUtils::GetInputStream(gfx::DataSourceSurface* aSurface, nsresult gfxUtils::GetInputStreamWithRandomNoise( gfx::DataSourceSurface* aSurface, bool aIsAlphaPremultiplied, const char* aMimeType, const nsAString& aEncoderOptions, - nsICookieJarSettings* aCookieJarSettings, nsIInputStream** outStream) { + nsICookieJarSettings* aCookieJarSettings, nsIPrincipal* aPrincipal, + nsIInputStream** outStream) { nsCString enccid("@mozilla.org/image/encoder;2?type="); enccid += aMimeType; nsCOMPtr encoder = do_CreateInstance(enccid.get()); @@ -1667,8 +1669,8 @@ nsresult gfxUtils::GetInputStreamWithRandomNoise( } nsRFPService::RandomizePixels( - aCookieJarSettings, imageBuffer.get(), aSurface->GetSize().width, - aSurface->GetSize().height, + aCookieJarSettings, aPrincipal, imageBuffer.get(), + aSurface->GetSize().width, aSurface->GetSize().height, aSurface->GetSize().width * aSurface->GetSize().height * 4, SurfaceFormat::A8R8G8B8_UINT32); diff --git a/gfx/thebes/gfxUtils.h b/gfx/thebes/gfxUtils.h index a7b9f067ebbf..1544a634bece 100644 --- a/gfx/thebes/gfxUtils.h +++ b/gfx/thebes/gfxUtils.h @@ -400,7 +400,8 @@ class gfxUtils { static mozilla::UniquePtr GetImageBufferWithRandomNoise( DataSourceSurface* aSurface, bool aIsAlphaPremultiplied, - nsICookieJarSettings* aCookieJarSettings, int32_t* outFormat); + nsICookieJarSettings* aCookieJarSettings, nsIPrincipal* aPrincipal, + int32_t* outFormat); static nsresult GetInputStream(DataSourceSurface* aSurface, bool aIsAlphaPremultiplied, @@ -411,7 +412,8 @@ class gfxUtils { static nsresult GetInputStreamWithRandomNoise( DataSourceSurface* aSurface, bool aIsAlphaPremultiplied, const char* aMimeType, const nsAString& aEncoderOptions, - nsICookieJarSettings* aCookieJarSettings, nsIInputStream** outStream); + nsICookieJarSettings* aCookieJarSettings, nsIPrincipal* aPrincipal, + nsIInputStream** outStream); static void RemoveShaderCacheFromDiskIfNecessary(); diff --git a/toolkit/components/resistfingerprinting/nsRFPService.cpp b/toolkit/components/resistfingerprinting/nsRFPService.cpp index 0cc48347017a..4622d6f53ba5 100644 --- a/toolkit/components/resistfingerprinting/nsRFPService.cpp +++ b/toolkit/components/resistfingerprinting/nsRFPService.cpp @@ -54,6 +54,7 @@ #include "mozilla/dom/MediaDeviceInfoBinding.h" #include "mozilla/fallible.h" #include "mozilla/XorShift128PlusRNG.h" +#include "mozilla/dom/CanvasUtils.h" #include "nsAboutProtocolUtils.h" #include "nsBaseHashtable.h" @@ -1708,8 +1709,9 @@ nsresult nsRFPService::GenerateCanvasKeyFromImageData( // static nsresult nsRFPService::RandomizePixels(nsICookieJarSettings* aCookieJarSettings, - uint8_t* aData, uint32_t aWidth, - uint32_t aHeight, uint32_t aSize, + nsIPrincipal* aPrincipal, uint8_t* aData, + uint32_t aWidth, uint32_t aHeight, + uint32_t aSize, gfx::SurfaceFormat aSurfaceFormat) { NS_ENSURE_ARG_POINTER(aData); @@ -1721,6 +1723,11 @@ nsresult nsRFPService::RandomizePixels(nsICookieJarSettings* aCookieJarSettings, return NS_OK; } + if (aPrincipal && CanvasUtils::GetCanvasExtractDataPermission(*aPrincipal) == + nsIPermissionManager::ALLOW_ACTION) { + return NS_OK; + } + // Don't randomize if all pixels are uniform. static constexpr size_t bytesPerPixel = 4; MOZ_ASSERT(aSize == aWidth * aHeight * bytesPerPixel, diff --git a/toolkit/components/resistfingerprinting/nsRFPService.h b/toolkit/components/resistfingerprinting/nsRFPService.h index 3d4a238e1bcf..bead92c4ac1f 100644 --- a/toolkit/components/resistfingerprinting/nsRFPService.h +++ b/toolkit/components/resistfingerprinting/nsRFPService.h @@ -342,8 +342,9 @@ class nsRFPService final : public nsIObserver, public nsIRFPService { // The method to add random noises to the image data based on the random key // of the given cookieJarSettings. static nsresult RandomizePixels(nsICookieJarSettings* aCookieJarSettings, - uint8_t* aData, uint32_t aWidth, - uint32_t aHeight, uint32_t aSize, + nsIPrincipal* aPrincipal, uint8_t* aData, + uint32_t aWidth, uint32_t aHeight, + uint32_t aSize, mozilla::gfx::SurfaceFormat aSurfaceFormat); // --------------------------------------------------------------------------