tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

commit c83fe48d8f341c62e0f20eebf1e5909d7ce78157
parent 39accc7ebd036a744d2bf375aeda31b841685ed3
Author: Tom Ritter <tom@mozilla.com>
Date:   Mon,  8 Dec 2025 16:31:02 +0000

Bug 1873716: Adding more recording sites r=timhuang

SKIP_BMO_CHECK

Differential Revision: https://phabricator.services.mozilla.com/D273665

Diffstat:
Mdom/canvas/CanvasRenderingContext2D.cpp | 3+++
Mdom/canvas/ClientWebGLContext.cpp | 44++++++++++++++++++++++++--------------------
Mdom/canvas/OffscreenCanvas.cpp | 11+++++++++++
Mdom/canvas/OffscreenCanvas.h | 3+++
Mdom/canvas/nsICanvasRenderingContextInternal.cpp | 86+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mdom/canvas/nsICanvasRenderingContextInternal.h | 3+++
6 files changed, 130 insertions(+), 20 deletions(-)

diff --git a/dom/canvas/CanvasRenderingContext2D.cpp b/dom/canvas/CanvasRenderingContext2D.cpp @@ -71,6 +71,7 @@ #include "mozilla/dom/ToJSValue.h" #include "mozilla/dom/TypedArray.h" #include "mozilla/dom/VideoFrame.h" +#include "mozilla/dom/WorkerPrivate.h" #include "mozilla/gfx/2D.h" #include "mozilla/gfx/CanvasShutdownManager.h" #include "mozilla/gfx/DataSurfaceHelpers.h" @@ -6511,6 +6512,8 @@ already_AddRefed<ImageData> CanvasRenderingContext2D::GetImageData( h = 1; } + RecordCanvasUsage(CanvasExtractionAPI::GetImageData, CSSIntSize(w, h)); + JS::Rooted<JSObject*> array(aCx); aError = GetImageDataArray(aCx, aSx, aSy, w, h, aSubjectPrincipal, array.address()); diff --git a/dom/canvas/ClientWebGLContext.cpp b/dom/canvas/ClientWebGLContext.cpp @@ -5354,26 +5354,30 @@ void ClientWebGLContext::ReadPixels(GLint x, GLint y, GLsizei width, if (extraction == CanvasUtils::ImageExtraction::Placeholder) { dom::GeneratePlaceholderCanvasData(range->size(), range->Elements()); - } else if (extraction == CanvasUtils::ImageExtraction::Randomize) { - const auto pii = webgl::PackingInfoInfo::For(desc.pi); - // DoReadPixels() requres pii to be Some(). - MOZ_ASSERT(pii.isSome()); - - // With WebGL, the alpha channel is always the last element (if it - // exists) in the pixel. With nsRFPService::RandomizeElements, we do - // random % (pii->elementsPerPixel - 1) + offset to get the channel - // we want to randomize. With the offset being 0, we avoid the last - // element, which is the alpha channel. - // If WebGL had ARGB or some other format where the alpha channel - // was not the last element, we would need to adjust the offset. - constexpr uint8_t alphaChannelOffset = 0; - bool hasAlphaChannel = - format == LOCAL_GL_SRGB_ALPHA || format == LOCAL_GL_RGBA || - format == LOCAL_GL_BGRA || format == LOCAL_GL_LUMINANCE_ALPHA; - nsRFPService::RandomizeElements( - GetCookieJarSettings(), PrincipalOrNull(), range->data(), - range->size_bytes(), pii->elementsPerPixel, pii->bytesPerElement, - alphaChannelOffset, hasAlphaChannel); + } else { + RecordCanvasUsage(CanvasExtractionAPI::ReadPixels, + CSSIntSize(width, height)); + if (extraction == CanvasUtils::ImageExtraction::Randomize) { + const auto pii = webgl::PackingInfoInfo::For(desc.pi); + // DoReadPixels() requres pii to be Some(). + MOZ_ASSERT(pii.isSome()); + + // With WebGL, the alpha channel is always the last element (if it + // exists) in the pixel. With nsRFPService::RandomizeElements, we do + // random % (pii->elementsPerPixel - 1) + offset to get the channel + // we want to randomize. With the offset being 0, we avoid the last + // element, which is the alpha channel. + // If WebGL had ARGB or some other format where the alpha channel + // was not the last element, we would need to adjust the offset. + constexpr uint8_t alphaChannelOffset = 0; + bool hasAlphaChannel = + format == LOCAL_GL_SRGB_ALPHA || format == LOCAL_GL_RGBA || + format == LOCAL_GL_BGRA || format == LOCAL_GL_LUMINANCE_ALPHA; + nsRFPService::RandomizeElements( + GetCookieJarSettings(), PrincipalOrNull(), range->data(), + range->size_bytes(), pii->elementsPerPixel, pii->bytesPerElement, + alphaChannelOffset, hasAlphaChannel); + } } } }); diff --git a/dom/canvas/OffscreenCanvas.cpp b/dom/canvas/OffscreenCanvas.cpp @@ -515,9 +515,14 @@ already_AddRefed<Promise> OffscreenCanvas::ConvertToBlob( this, nsContentUtils::GetCurrentJSContext(), mCurrentContext ? mCurrentContext->PrincipalOrNull() : nullptr); + if (extractionBehaviour != CanvasUtils::ImageExtraction::Placeholder) { + GetContext()->RecordCanvasUsage(CanvasExtractionAPI::ToBlob, + GetWidthHeight()); + } CanvasRenderingContextHelper::ToBlob(callback, type, encodeOptions, /* aUsingCustomOptions */ false, extractionBehaviour, aRv); + if (aRv.Failed()) { promise->MaybeReject(std::move(aRv)); } @@ -559,6 +564,12 @@ already_AddRefed<Promise> OffscreenCanvas::ToBlob(JSContext* aCx, CanvasUtils::ImageExtractionResult( this, aCx, mCurrentContext ? mCurrentContext->PrincipalOrNull() : nullptr); + + if (extractionBehaviour != CanvasUtils::ImageExtraction::Placeholder) { + GetContext()->RecordCanvasUsage(CanvasExtractionAPI::ToBlob, + GetWidthHeight()); + } + CanvasRenderingContextHelper::ToBlob(aCx, callback, aType, aParams, extractionBehaviour, aRv); diff --git a/dom/canvas/OffscreenCanvas.h b/dom/canvas/OffscreenCanvas.h @@ -175,6 +175,9 @@ class OffscreenCanvas final : public DOMEventTargetHelper, private: ~OffscreenCanvas(); + void RecordCanvasUsage(CanvasExtractionAPI aExtractionAPI, + CanvasUtils::ImageExtraction aExtractionBehaviour); + already_AddRefed<EncodeCompleteCallback> CreateEncodeCompleteCallback( Promise* aPromise); diff --git a/dom/canvas/nsICanvasRenderingContextInternal.cpp b/dom/canvas/nsICanvasRenderingContextInternal.cpp @@ -12,10 +12,15 @@ #include "mozilla/dom/Event.h" #include "mozilla/dom/WorkerCommon.h" #include "mozilla/dom/WorkerPrivate.h" +#include "mozilla/dom/WorkerRunnable.h" #include "mozilla/gfx/DrawTargetRecording.h" #include "nsContentUtils.h" #include "nsPIDOMWindow.h" +#include "nsRFPService.h" #include "nsRefreshDriver.h" +#include "nsThreadUtils.h" + +static mozilla::LazyLogModule gFingerprinterDetection("FingerprinterDetection"); nsICanvasRenderingContextInternal::nsICanvasRenderingContextInternal() = default; @@ -40,6 +45,87 @@ nsIGlobalObject* nsICanvasRenderingContextInternal::GetParentObject() const { return nullptr; } +class RecordCanvasUsageRunnable final + : public mozilla::dom::WorkerMainThreadRunnable { + public: + RecordCanvasUsageRunnable(mozilla::dom::WorkerPrivate* aWorkerPrivate, + const mozilla::CanvasUsage& aUsage) + : WorkerMainThreadRunnable(aWorkerPrivate, + "RecordCanvasUsageRunnable"_ns), + mUsage(aUsage) { + MOZ_ASSERT(aWorkerPrivate); + aWorkerPrivate->AssertIsOnWorkerThread(); + } + + protected: + MOZ_CAN_RUN_SCRIPT_BOUNDARY bool MainThreadRun() override { + mozilla::AssertIsOnMainThread(); + RefPtr<mozilla::dom::Document> doc; + if (!mWorkerRef) { + MOZ_LOG(gFingerprinterDetection, mozilla::LogLevel::Error, + ("RecordCanvasUsageRunnable::MainThreadRun - null mWorkerRef")); + return false; + } + auto* priv = mWorkerRef->Private(); + if (!priv) { + MOZ_LOG( + gFingerprinterDetection, mozilla::LogLevel::Error, + ("RecordCanvasUsageRunnable::MainThreadRun - null worker private")); + return false; + } + doc = priv->GetDocument(); + if (!doc) { + MOZ_LOG(gFingerprinterDetection, mozilla::LogLevel::Error, + ("RecordCanvasUsageRunnable::MainThreadRun - null document")); + return false; + } + doc->RecordCanvasUsage(mUsage); + return true; + } + + private: + mozilla::CanvasUsage mUsage; +}; + +void nsICanvasRenderingContextInternal::RecordCanvasUsage( + mozilla::CanvasExtractionAPI aAPI, mozilla::CSSIntSize size) const { + mozilla::dom::CanvasContextType contextType; + if (mCanvasElement) { + contextType = mCanvasElement->GetCurrentContextType(); + auto usage = + mozilla::CanvasUsage::CreateUsage(false, contextType, aAPI, size, this); + mCanvasElement->OwnerDoc()->RecordCanvasUsage(usage); + } + if (mOffscreenCanvas) { + contextType = mOffscreenCanvas->GetContextType(); + auto usage = + mozilla::CanvasUsage::CreateUsage(true, contextType, aAPI, size, this); + if (NS_IsMainThread()) { + if (nsPIDOMWindowInner* inner = + mOffscreenCanvas->GetOwnerGlobal()->GetAsInnerWindow()) { + if (mozilla::dom::Document* doc = inner->GetExtantDoc()) { + doc->RecordCanvasUsage(usage); + } + } + } else { + mozilla::dom::WorkerPrivate* workerPrivate = + mozilla::dom::GetCurrentThreadWorkerPrivate(); + if (workerPrivate) { + RefPtr<RecordCanvasUsageRunnable> runnable = + new RecordCanvasUsageRunnable(workerPrivate, usage); + mozilla::ErrorResult rv; + runnable->Dispatch(workerPrivate, mozilla::dom::WorkerStatus::Canceling, + rv); + if (rv.Failed()) { + rv.SuppressException(); + MOZ_LOG(gFingerprinterDetection, mozilla::LogLevel::Error, + ("RecordCanvasUsageRunnable dispatch failed")); + } + } + } + } +} + nsIPrincipal* nsICanvasRenderingContextInternal::PrincipalOrNull() const { if (mCanvasElement) { return mCanvasElement->NodePrincipal(); diff --git a/dom/canvas/nsICanvasRenderingContextInternal.h b/dom/canvas/nsICanvasRenderingContextInternal.h @@ -231,6 +231,9 @@ class nsICanvasRenderingContextInternal : public nsISupports, bool DispatchEvent(const nsAString& eventName, mozilla::CanBubble aCanBubble, mozilla::Cancelable aIsCancelable) const; + void RecordCanvasUsage(mozilla::CanvasExtractionAPI aAPI, + mozilla::CSSIntSize size) const; + protected: RefPtr<mozilla::dom::HTMLCanvasElement> mCanvasElement; RefPtr<mozilla::dom::OffscreenCanvas> mOffscreenCanvas;