commit 988dc26f437bd875ce8417a485cb50b550c86876
parent 88e897fd97c61714e4f03d19ef23b6de43bfcb1c
Author: Tom Ritter <tom@mozilla.com>
Date: Wed, 17 Dec 2025 17:44:39 +0000
Bug 1976287: Add an RFPTarget for adding a random suffix to the WebGL Vendor r=timhuang
Differential Revision: https://phabricator.services.mozilla.com/D276243
Diffstat:
6 files changed, 197 insertions(+), 0 deletions(-)
diff --git a/dom/canvas/ClientWebGLContext.cpp b/dom/canvas/ClientWebGLContext.cpp
@@ -20,7 +20,9 @@
#include "gfxCrashReporterUtils.h"
#include "js/PropertyAndElement.h" // JS_DefineElement
#include "js/ScalarType.h" // js::Scalar::Type
+#include "mozilla/Base64.h"
#include "mozilla/EnumeratedRange.h"
+#include "mozilla/RandomNum.h"
#include "mozilla/ResultVariant.h"
#include "mozilla/ScopeExit.h"
#include "mozilla/StaticPrefs_webgl.h"
@@ -2458,6 +2460,24 @@ void ClientWebGLContext::GetParameter(JSContext* cx, GLenum pname,
if (ShouldResistFingerprinting(RFPTarget::WebGLRenderInfo)) {
ret = Some("Mozilla"_ns);
} else if (ShouldResistFingerprinting(
+ RFPTarget::WebGLVendorRandomize)) {
+ // Generate "Mozilla <Base64(uint64)>"
+ auto randomValue = RandomUint64();
+ if (randomValue.isSome()) {
+ uint64_t value = randomValue.value();
+ nsCString base64;
+ nsresult rv =
+ Base64Encode(reinterpret_cast<const char*>(&value),
+ sizeof(value), base64);
+ if (NS_SUCCEEDED(rv)) {
+ ret = Some(std::string("Mozilla ") + base64.get());
+ } else {
+ ret = Some("Mozilla"_ns);
+ }
+ } else {
+ ret = Some("Mozilla"_ns);
+ }
+ } else if (ShouldResistFingerprinting(
RFPTarget::WebGLVendorConstant)) {
ret = Some("Mozilla"_ns);
} else {
diff --git a/dom/canvas/test/webgl-mochitest/mochitest.toml b/dom/canvas/test/webgl-mochitest/mochitest.toml
@@ -285,6 +285,12 @@ skip-if = [
"os == 'android'", # bug 865443- seperate suite - the non_conf* tests pass except for one on armv6 tests
]
+["test_webgl_vendor_randomize_fpp.html"]
+
+["test_webgl_vendor_randomize_fpp_explicit.html"]
+
+["test_webgl_vendor_randomize_rfp.html"]
+
["test_webgl_vendor_sanitize_fpp.html"]
["test_webgl_vendor_sanitize_fpp_explicit.html"]
diff --git a/dom/canvas/test/webgl-mochitest/test_webgl_vendor_randomize_fpp.html b/dom/canvas/test/webgl-mochitest/test_webgl_vendor_randomize_fpp.html
@@ -0,0 +1,59 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>WebGL Vendor Randomize with FPP - Vendor should be randomized</title>
+<script src="/tests/SimpleTest/SimpleTest.js"></script>
+<script>
+/* global SimpleTest SpecialPowers */
+SimpleTest.waitForExplicitFinish();
+document.addEventListener("DOMContentLoaded", async function() {
+ // Enable FPP with WebGLVendorRandomize
+ await SpecialPowers.pushPrefEnv({
+ set: [
+ ["privacy.fingerprintingProtection", true],
+ ["privacy.fingerprintingProtection.overrides", "+WebGLVendorRandomize"],
+ ["privacy.resistFingerprinting", false]
+ ]
+ });
+
+ let canvas = document.body.appendChild(document.createElement("canvas"));
+ if (!canvas) {
+ SimpleTest.ok(false, "Cannot create canvas");
+ SimpleTest.finish();
+ return;
+ }
+
+ let gl = canvas.getContext("webgl");
+ if (!gl) {
+ SimpleTest.ok(false, "Cannot get WebGL context");
+ SimpleTest.finish();
+ return;
+ }
+
+ // Try to get the WEBGL_debug_renderer_info extension
+ let ext = gl.getExtension("WEBGL_debug_renderer_info");
+ if (!ext) {
+ SimpleTest.ok(false, "WEBGL_debug_renderer_info extension should be available with FPP");
+ SimpleTest.finish();
+ return;
+ }
+
+ // With FPP and WebGLVendorRandomize enabled, the vendor should be "Mozilla <Base64>"
+ let vendor = gl.getParameter(ext.UNMASKED_VENDOR_WEBGL);
+
+ // Verify it starts with "Mozilla "
+ SimpleTest.ok(vendor.startsWith("Mozilla "),
+ "UNMASKED_VENDOR_WEBGL should start with 'Mozilla ' with WebGLVendorRandomize enabled. Got: " + vendor);
+
+ // Verify it has additional content (the Base64 part)
+ SimpleTest.ok(vendor.length > "Mozilla ".length,
+ "UNMASKED_VENDOR_WEBGL should have Base64 suffix. Got: " + vendor);
+
+ // Verify the Base64 part looks like Base64 (alphanumeric and =)
+ let base64Part = vendor.substring("Mozilla ".length);
+ let base64Regex = /^[A-Za-z0-9+/=]+$/;
+ SimpleTest.ok(base64Regex.test(base64Part),
+ "Base64 part should be valid Base64. Got: " + base64Part);
+
+ SimpleTest.finish();
+});
+</script>
diff --git a/dom/canvas/test/webgl-mochitest/test_webgl_vendor_randomize_fpp_explicit.html b/dom/canvas/test/webgl-mochitest/test_webgl_vendor_randomize_fpp_explicit.html
@@ -0,0 +1,60 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>WebGL Vendor Randomize with FPP (explicit) - Vendor should be randomized</title>
+<script src="/tests/SimpleTest/SimpleTest.js"></script>
+<script>
+/* global SimpleTest SpecialPowers */
+SimpleTest.waitForExplicitFinish();
+document.addEventListener("DOMContentLoaded", async function() {
+ // Enable FPP with explicit -AllTargets,+WebGLVendorRandomize
+ // This ensures only WebGLVendorRandomize is active, nothing else
+ await SpecialPowers.pushPrefEnv({
+ set: [
+ ["privacy.fingerprintingProtection", true],
+ ["privacy.fingerprintingProtection.overrides", "-AllTargets,+WebGLVendorRandomize"],
+ ["privacy.resistFingerprinting", false]
+ ]
+ });
+
+ let canvas = document.body.appendChild(document.createElement("canvas"));
+ if (!canvas) {
+ SimpleTest.ok(false, "Cannot create canvas");
+ SimpleTest.finish();
+ return;
+ }
+
+ let gl = canvas.getContext("webgl");
+ if (!gl) {
+ SimpleTest.ok(false, "Cannot get WebGL context");
+ SimpleTest.finish();
+ return;
+ }
+
+ // Try to get the WEBGL_debug_renderer_info extension
+ let ext = gl.getExtension("WEBGL_debug_renderer_info");
+ if (!ext) {
+ SimpleTest.ok(false, "WEBGL_debug_renderer_info extension should be available with FPP");
+ SimpleTest.finish();
+ return;
+ }
+
+ // With FPP and WebGLVendorRandomize enabled, the vendor should be "Mozilla <Base64>"
+ let vendor = gl.getParameter(ext.UNMASKED_VENDOR_WEBGL);
+
+ // Verify it starts with "Mozilla "
+ SimpleTest.ok(vendor.startsWith("Mozilla "),
+ "UNMASKED_VENDOR_WEBGL should start with 'Mozilla ' with WebGLVendorRandomize enabled. Got: " + vendor);
+
+ // Verify it has additional content (the Base64 part)
+ SimpleTest.ok(vendor.length > "Mozilla ".length,
+ "UNMASKED_VENDOR_WEBGL should have Base64 suffix. Got: " + vendor);
+
+ // Verify the Base64 part looks like Base64 (alphanumeric and =)
+ let base64Part = vendor.substring("Mozilla ".length);
+ let base64Regex = /^[A-Za-z0-9+/=]+$/;
+ SimpleTest.ok(base64Regex.test(base64Part),
+ "Base64 part should be valid Base64. Got: " + base64Part);
+
+ SimpleTest.finish();
+});
+</script>
diff --git a/dom/canvas/test/webgl-mochitest/test_webgl_vendor_randomize_rfp.html b/dom/canvas/test/webgl-mochitest/test_webgl_vendor_randomize_rfp.html
@@ -0,0 +1,51 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>WebGL Vendor Randomize with RFP - WebGLRenderInfo takes precedence</title>
+<script src="/tests/SimpleTest/SimpleTest.js"></script>
+<script>
+/* global SimpleTest SpecialPowers */
+SimpleTest.waitForExplicitFinish();
+document.addEventListener("DOMContentLoaded", async function() {
+ // Enable RFP - WebGLRenderInfo should take precedence over WebGLVendorRandomize
+ await SpecialPowers.pushPrefEnv({
+ set: [
+ ["privacy.resistFingerprinting", true],
+ ["privacy.fingerprintingProtection.overrides", "+WebGLVendorRandomize"],
+ ["privacy.fingerprintingProtection", true],
+ ]
+ });
+
+ let canvas = document.body.appendChild(document.createElement("canvas"));
+ if (!canvas) {
+ SimpleTest.ok(false, "Cannot create canvas");
+ SimpleTest.finish();
+ return;
+ }
+
+ let gl = canvas.getContext("webgl");
+ if (!gl) {
+ SimpleTest.ok(false, "Cannot get WebGL context");
+ SimpleTest.finish();
+ return;
+ }
+
+ // Try to get the WEBGL_debug_renderer_info extension
+ let ext = gl.getExtension("WEBGL_debug_renderer_info");
+ if (!ext) {
+ SimpleTest.ok(false, "WEBGL_debug_renderer_info extension should be available in RFP");
+ SimpleTest.finish();
+ return;
+ }
+
+ // With RFP enabled, WebGLRenderInfo takes precedence over WebGLVendorRandomize
+ // The vendor should be "Mozilla", NOT "Mozilla <Base64>"
+ let vendor = gl.getParameter(ext.UNMASKED_VENDOR_WEBGL);
+ SimpleTest.is(vendor, "Mozilla", "UNMASKED_VENDOR_WEBGL should be 'Mozilla' with RFP enabled (WebGLRenderInfo precedence)");
+
+ // Also check renderer for completeness
+ let renderer = gl.getParameter(ext.UNMASKED_RENDERER_WEBGL);
+ SimpleTest.is(renderer, "Mozilla", "UNMASKED_RENDERER_WEBGL should be 'Mozilla' with RFP enabled");
+
+ SimpleTest.finish();
+});
+</script>
diff --git a/toolkit/components/resistfingerprinting/RFPTargets.inc b/toolkit/components/resistfingerprinting/RFPTargets.inc
@@ -111,6 +111,7 @@ ITEM_VALUE(WebGLRandomization, 75)
ITEM_VALUE(EfficientCanvasRandomization, 76)
ITEM_VALUE(WebGLVendorSanitize, 77)
ITEM_VALUE(WebGLVendorConstant, 78)
+ITEM_VALUE(WebGLVendorRandomize, 79)
// !!! Adding a new target? Rename PointerId and repurpose it.