summaryrefslogtreecommitdiffstats
path: root/browser/components/resistfingerprinting/test
diff options
context:
space:
mode:
Diffstat (limited to 'browser/components/resistfingerprinting/test')
-rw-r--r--browser/components/resistfingerprinting/test/browser/browser.toml74
-rw-r--r--browser/components/resistfingerprinting/test/browser/browser_canvas_iframes.js217
-rw-r--r--browser/components/resistfingerprinting/test/browser/browser_canvas_popups.js198
-rw-r--r--browser/components/resistfingerprinting/test/browser/browser_canvascompare_iframes.js263
-rw-r--r--browser/components/resistfingerprinting/test/browser/browser_canvascompare_iframes_aboutblank.js266
-rw-r--r--browser/components/resistfingerprinting/test/browser/browser_canvascompare_iframes_blob.js266
-rw-r--r--browser/components/resistfingerprinting/test/browser/browser_canvascompare_iframes_data.js271
-rw-r--r--browser/components/resistfingerprinting/test/browser/browser_canvascompare_popups.js268
-rw-r--r--browser/components/resistfingerprinting/test/browser/browser_canvascompare_popups_aboutblank.js269
-rw-r--r--browser/components/resistfingerprinting/test/browser/browser_canvascompare_popups_blob.js208
-rw-r--r--browser/components/resistfingerprinting/test/browser/browser_canvascompare_popups_data.js208
-rw-r--r--browser/components/resistfingerprinting/test/browser/browser_hwconcurrency_etp_iframes.js2
-rw-r--r--browser/components/resistfingerprinting/test/browser/browser_hwconcurrency_iframes.js19
-rw-r--r--browser/components/resistfingerprinting/test/browser/browser_hwconcurrency_iframes_aboutblank.js15
-rw-r--r--browser/components/resistfingerprinting/test/browser/browser_hwconcurrency_iframes_aboutsrcdoc.js15
-rw-r--r--browser/components/resistfingerprinting/test/browser/browser_hwconcurrency_iframes_blob.js15
-rw-r--r--browser/components/resistfingerprinting/test/browser/browser_hwconcurrency_iframes_blobcrossorigin.js15
-rw-r--r--browser/components/resistfingerprinting/test/browser/browser_hwconcurrency_iframes_data.js15
-rw-r--r--browser/components/resistfingerprinting/test/browser/browser_hwconcurrency_iframes_sandboxediframe.js15
-rw-r--r--browser/components/resistfingerprinting/test/browser/browser_hwconcurrency_popups.js15
-rw-r--r--browser/components/resistfingerprinting/test/browser/browser_hwconcurrency_popups_aboutblank.js15
-rw-r--r--browser/components/resistfingerprinting/test/browser/browser_hwconcurrency_popups_blob.js15
-rw-r--r--browser/components/resistfingerprinting/test/browser/browser_hwconcurrency_popups_blob_noopener.js26
-rw-r--r--browser/components/resistfingerprinting/test/browser/browser_hwconcurrency_popups_data.js15
-rw-r--r--browser/components/resistfingerprinting/test/browser/browser_hwconcurrency_popups_data_noopener.js26
-rw-r--r--browser/components/resistfingerprinting/test/browser/browser_hwconcurrency_popups_noopener.js26
-rw-r--r--browser/components/resistfingerprinting/test/browser/file_canvas_iframee.html43
-rw-r--r--browser/components/resistfingerprinting/test/browser/file_canvas_iframer.html55
-rw-r--r--browser/components/resistfingerprinting/test/browser/file_canvascompare_aboutblank_iframee.html71
-rw-r--r--browser/components/resistfingerprinting/test/browser/file_canvascompare_aboutblank_iframer.html31
-rw-r--r--browser/components/resistfingerprinting/test/browser/file_canvascompare_aboutblank_popupmaker.html110
-rw-r--r--browser/components/resistfingerprinting/test/browser/file_canvascompare_blob_iframee.html75
-rw-r--r--browser/components/resistfingerprinting/test/browser/file_canvascompare_blob_iframer.html31
-rw-r--r--browser/components/resistfingerprinting/test/browser/file_canvascompare_blob_popupmaker.html92
-rw-r--r--browser/components/resistfingerprinting/test/browser/file_canvascompare_data_iframee.html76
-rw-r--r--browser/components/resistfingerprinting/test/browser/file_canvascompare_data_iframer.html31
-rw-r--r--browser/components/resistfingerprinting/test/browser/file_canvascompare_data_popupmaker.html91
-rw-r--r--browser/components/resistfingerprinting/test/browser/file_canvascompare_iframee.html43
-rw-r--r--browser/components/resistfingerprinting/test/browser/file_canvascompare_iframer.html77
-rw-r--r--browser/components/resistfingerprinting/test/browser/file_hwconcurrency_data_popupmaker.html4
-rw-r--r--browser/components/resistfingerprinting/test/browser/head.js75
41 files changed, 3637 insertions, 25 deletions
diff --git a/browser/components/resistfingerprinting/test/browser/browser.toml b/browser/components/resistfingerprinting/test/browser/browser.toml
index 8fbd7b5b2f..dd60e383cb 100644
--- a/browser/components/resistfingerprinting/test/browser/browser.toml
+++ b/browser/components/resistfingerprinting/test/browser/browser.toml
@@ -10,6 +10,19 @@ support-files = [
"file_workerNetInfo.js",
"file_workerPerformance.js",
"head.js",
+ "file_canvascompare_aboutblank_iframee.html",
+ "file_canvascompare_aboutblank_iframer.html",
+ "file_canvascompare_aboutblank_popupmaker.html",
+ "file_canvascompare_blob_iframee.html",
+ "file_canvascompare_blob_iframer.html",
+ "file_canvascompare_blob_popupmaker.html",
+ "file_canvascompare_data_iframee.html",
+ "file_canvascompare_data_iframer.html",
+ "file_canvascompare_data_popupmaker.html",
+ "file_canvascompare_iframer.html",
+ "file_canvascompare_iframee.html",
+ "file_canvas_iframer.html",
+ "file_canvas_iframee.html",
"file_navigator_header.sjs",
"file_navigator_iframer.html",
"file_navigator_iframee.html",
@@ -41,17 +54,48 @@ support-files = [
]
["browser_animationapi_iframes.js"]
+lineno = "48"
["browser_block_mozAddonManager.js"]
+lineno = "51"
["browser_bug1369357_site_specific_zoom_level.js"]
https_first_disabled = true
+lineno = "54"
+
+["browser_canvas_iframes.js"]
+lineno = "58"
+
+["browser_canvas_popups.js"]
+lineno = "61"
+
+["browser_canvascompare_iframes.js"]
+lineno = "64"
+
+["browser_canvascompare_iframes_aboutblank.js"]
+
+["browser_canvascompare_iframes_blob.js"]
+
+["browser_canvascompare_iframes_data.js"]
+
+["browser_canvascompare_popups.js"]
+lineno = "66"
+
+["browser_canvascompare_popups_aboutblank.js"]
+lineno = "68"
+
+["browser_canvascompare_popups_blob.js"]
+
+["browser_canvascompare_popups_data.js"]
["browser_cross_origin_isolated_animation_api.js"]
+lineno = "70"
["browser_cross_origin_isolated_performance_api.js"]
+lineno = "73"
["browser_cross_origin_isolated_reduce_time_precision.js"]
+lineno = "76"
["browser_dynamical_window_rounding.js"]
https_first_disabled = true
@@ -59,66 +103,96 @@ skip-if = [
"os == 'mac'", # Bug 1570812
"os == 'linux'", # Bug 1570812, Bug 1775698
]
+lineno = "79"
["browser_hwconcurrency_etp_iframes.js"]
+lineno = "87"
["browser_hwconcurrency_iframes.js"]
+lineno = "90"
["browser_hwconcurrency_iframes_aboutblank.js"]
+lineno = "93"
["browser_hwconcurrency_iframes_aboutsrcdoc.js"]
+lineno = "96"
["browser_hwconcurrency_iframes_blob.js"]
+lineno = "99"
["browser_hwconcurrency_iframes_blobcrossorigin.js"]
+lineno = "102"
["browser_hwconcurrency_iframes_data.js"]
+lineno = "105"
["browser_hwconcurrency_iframes_sandboxediframe.js"]
+lineno = "108"
["browser_hwconcurrency_popups.js"]
+lineno = "111"
["browser_hwconcurrency_popups_aboutblank.js"]
+lineno = "114"
["browser_hwconcurrency_popups_blob.js"]
+lineno = "117"
["browser_hwconcurrency_popups_blob_noopener.js"]
+lineno = "120"
["browser_hwconcurrency_popups_data.js"]
+lineno = "123"
["browser_hwconcurrency_popups_data_noopener.js"]
+lineno = "126"
["browser_hwconcurrency_popups_noopener.js"]
+lineno = "129"
["browser_math.js"]
+lineno = "132"
["browser_navigator.js"]
https_first_disabled = true
+lineno = "135"
["browser_navigator_iframes.js"]
https_first_disabled = true
+lineno = "139"
["browser_netInfo.js"]
https_first_disabled = true
+lineno = "143"
["browser_performanceAPI.js"]
+lineno = "147"
["browser_performanceAPIWorkers.js"]
+lineno = "150"
["browser_reduceTimePrecision_iframes.js"]
https_first_disabled = true
+lineno = "153"
["browser_roundedWindow_dialogWindow.js"]
+lineno = "157"
["browser_roundedWindow_newWindow.js"]
+lineno = "160"
["browser_roundedWindow_open_max_inner.js"]
+lineno = "163"
["browser_roundedWindow_open_mid_inner.js"]
+lineno = "166"
["browser_roundedWindow_open_min_inner.js"]
+lineno = "169"
["browser_spoofing_keyboard_event.js"]
skip-if = ["(debug || asan) && os == 'linux' && bits == 64"] #Bug 1518179
+lineno = "172"
["browser_timezone.js"]
+lineno = "176"
diff --git a/browser/components/resistfingerprinting/test/browser/browser_canvas_iframes.js b/browser/components/resistfingerprinting/test/browser/browser_canvas_iframes.js
new file mode 100644
index 0000000000..2b6497cc85
--- /dev/null
+++ b/browser/components/resistfingerprinting/test/browser/browser_canvas_iframes.js
@@ -0,0 +1,217 @@
+/**
+ * This tests that the canvas is correctly randomized on the iframe (not the framer)
+ *
+ * Covers the following cases:
+ * - RFP/FPP is disabled entirely
+ * - RFP is enabled entirely, and only in PBM
+ * - FPP is enabled entirely, and only in PBM
+ * - A normal window when FPP is enabled globally and RFP is enabled in PBM, Protections Enabled and Disabled
+ *
+ * - (A) RFP is exempted on the framer and framee and (if needed) on another cross-origin domain
+ * - (B) RFP is exempted on the framer and framee but is not on another (if needed) cross-origin domain
+ * - (C) RFP is exempted on the framer and (if needed) on another cross-origin domain, but not the framee
+ * - (D) RFP is exempted on the framer but not the framee nor another (if needed) cross-origin domain
+ * - (E) RFP is not exempted on the framer nor the framee but (if needed) is exempted on another cross-origin domain
+ * - (F) RFP is not exempted on the framer nor the framee nor another (if needed) cross-origin domain
+ * - (G) RFP is not exempted on the framer but is on the framee and (if needed) on another cross-origin domain
+ * - (H) RFP is not exempted on the framer nor another (if needed) cross-origin domain but is on the framee
+ *
+ */
+
+"use strict";
+
+// =============================================================================================
+
+/**
+ * Compares two Uint8Arrays and returns the number of bits that are different.
+ *
+ * @param {Uint8ClampedArray} arr1 - The first Uint8ClampedArray to compare.
+ * @param {Uint8ClampedArray} arr2 - The second Uint8ClampedArray to compare.
+ * @returns {number} - The number of bits that are different between the two
+ * arrays.
+ */
+function countDifferencesInUint8Arrays(arr1, arr2) {
+ let count = 0;
+ for (let i = 0; i < arr1.length; i++) {
+ let diff = arr1[i] ^ arr2[i];
+ while (diff > 0) {
+ count += diff & 1;
+ diff >>= 1;
+ }
+ }
+ return count;
+}
+
+// =============================================================================================
+
+async function testCanvasRandomization(result, expectedResults, extraData) {
+ let testDesc = extraData.testDesc;
+ let differences = countDifferencesInUint8Arrays(
+ result,
+ UNMODIFIED_CANVAS_DATA
+ );
+
+ Assert.greaterOrEqual(
+ differences,
+ expectedResults[0],
+ `Checking ${testDesc} for canvas randomization - did not see enough random pixels.`
+ );
+ Assert.lessOrEqual(
+ differences,
+ expectedResults[1],
+ `Checking ${testDesc} for canvas randomization - saw too many random pixels.`
+ );
+}
+
+requestLongerTimeout(2);
+
+let expectedResults = {};
+var UNMODIFIED_CANVAS_DATA = undefined;
+
+add_setup(async function () {
+ // Disable the fingerprinting randomization.
+ await SpecialPowers.pushPrefEnv({
+ set: [
+ ["privacy.fingerprintingProtection", false],
+ ["privacy.fingerprintingProtection.pbmode", false],
+ ["privacy.resistFingerprinting", false],
+ ],
+ });
+
+ let extractCanvasData = function () {
+ let offscreenCanvas = new OffscreenCanvas(100, 100);
+
+ const context = offscreenCanvas.getContext("2d");
+
+ // Draw a red rectangle
+ context.fillStyle = "#EE2222";
+ context.fillRect(0, 0, 100, 100);
+ context.fillStyle = "#2222EE";
+ context.fillRect(20, 20, 100, 100);
+
+ const imageData = context.getImageData(0, 0, 100, 100);
+ return imageData.data;
+ };
+
+ function runExtractCanvasData(tab) {
+ let code = extractCanvasData.toString();
+ return SpecialPowers.spawn(tab.linkedBrowser, [code], async funccode => {
+ await content.eval(`var extractCanvasData = ${funccode}`);
+ let result = await content.eval(`extractCanvasData()`);
+ return result;
+ });
+ }
+
+ const emptyPage =
+ getRootDirectory(gTestPath).replace(
+ "chrome://mochitests/content",
+ "https://example.com"
+ ) + "empty.html";
+
+ // Open a tab for extracting the canvas data.
+ const tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, emptyPage);
+
+ let data = await runExtractCanvasData(tab);
+ UNMODIFIED_CANVAS_DATA = data;
+
+ BrowserTestUtils.removeTab(tab);
+ await SpecialPowers.popPrefEnv();
+});
+
+// Be sure to always use `let expectedResults = structuredClone(rfpFullyRandomized)` to do a
+// deep copy and avoiding corrupting the original 'const' object
+// The first value represents the minimum number of random pixels we should see
+// The second, the maximum number of random pixels
+const rfpFullyRandomized = [10000, 999999999];
+const fppRandomized = [1, 260];
+const noRandom = [0, 0];
+
+// Note that the starting page and the iframe will be cross-domain from each other, but this test does not check that we inherit the randomizationkey,
+// only that the iframe is randomized.
+const uri = `https://${FRAMER_DOMAIN}/browser/browser/components/resistfingerprinting/test/browser/file_canvas_iframer.html?mode=iframe`;
+
+expectedResults = structuredClone(noRandom);
+add_task(
+ defaultsTest.bind(null, uri, testCanvasRandomization, expectedResults)
+);
+
+expectedResults = structuredClone(fppRandomized);
+add_task(
+ defaultsPBMTest.bind(null, uri, testCanvasRandomization, expectedResults)
+);
+
+expectedResults = structuredClone(rfpFullyRandomized);
+add_task(
+ simpleRFPTest.bind(null, uri, testCanvasRandomization, expectedResults)
+);
+
+// Test a private window with RFP enabled in PBMode
+expectedResults = structuredClone(rfpFullyRandomized);
+add_task(
+ simplePBMRFPTest.bind(null, uri, testCanvasRandomization, expectedResults)
+);
+
+expectedResults = structuredClone(fppRandomized);
+add_task(
+ simpleFPPTest.bind(null, uri, testCanvasRandomization, expectedResults)
+);
+
+// Test a Private Window with FPP Enabled in PBM
+expectedResults = structuredClone(fppRandomized);
+add_task(
+ simplePBMFPPTest.bind(null, uri, testCanvasRandomization, expectedResults)
+);
+
+// Test RFP Enabled in PBM and FPP enabled in Normal Browsing Mode, No Protections
+expectedResults = structuredClone(noRandom);
+add_task(
+ RFPPBMFPP_NormalMode_NoProtectionsTest.bind(
+ null,
+ uri,
+ testCanvasRandomization,
+ expectedResults
+ )
+);
+
+// Test RFP Enabled in PBM and FPP enabled in Normal Browsing Mode, Protections Enabled
+expectedResults = structuredClone(fppRandomized);
+add_task(
+ RFPPBMFPP_NormalMode_ProtectionsTest.bind(
+ null,
+ uri,
+ testCanvasRandomization,
+ expectedResults
+ )
+);
+
+// (A) RFP is exempted on the framer and framee and (if needed) on another cross-origin domain
+expectedResults = structuredClone(noRandom);
+add_task(testA.bind(null, uri, testCanvasRandomization, expectedResults));
+
+// (B) RFP is exempted on the framer and framee but is not on another (if needed) cross-origin domain
+expectedResults = structuredClone(noRandom);
+add_task(testB.bind(null, uri, testCanvasRandomization, expectedResults));
+
+// (C) RFP is exempted on the framer and (if needed) on another cross-origin domain, but not the framee
+expectedResults = structuredClone(rfpFullyRandomized);
+add_task(testC.bind(null, uri, testCanvasRandomization, expectedResults));
+
+// (D) RFP is exempted on the framer but not the framee nor another (if needed) cross-origin domain
+expectedResults = structuredClone(rfpFullyRandomized);
+add_task(testD.bind(null, uri, testCanvasRandomization, expectedResults));
+
+// (E) RFP is not exempted on the framer nor the framee but (if needed) is exempted on another cross-origin domain
+expectedResults = structuredClone(rfpFullyRandomized);
+add_task(testE.bind(null, uri, testCanvasRandomization, expectedResults));
+
+// (F) RFP is not exempted on the framer nor the framee nor another (if needed) cross-origin domain
+expectedResults = structuredClone(rfpFullyRandomized);
+add_task(testF.bind(null, uri, testCanvasRandomization, expectedResults));
+
+// (G) RFP is not exempted on the framer but is on the framee and (if needed) on another cross-origin domain
+expectedResults = structuredClone(rfpFullyRandomized);
+add_task(testG.bind(null, uri, testCanvasRandomization, expectedResults));
+
+// (H) RFP is not exempted on the framer nor another (if needed) cross-origin domain but is on the framee
+expectedResults = structuredClone(rfpFullyRandomized);
+add_task(testH.bind(null, uri, testCanvasRandomization, expectedResults));
diff --git a/browser/components/resistfingerprinting/test/browser/browser_canvas_popups.js b/browser/components/resistfingerprinting/test/browser/browser_canvas_popups.js
new file mode 100644
index 0000000000..234529b988
--- /dev/null
+++ b/browser/components/resistfingerprinting/test/browser/browser_canvas_popups.js
@@ -0,0 +1,198 @@
+/**
+ * This tests that the canvas is correctly randomized on a popup (not the starting page)
+ *
+ * Covers the following cases:
+ * - RFP/FPP is disabled entirely
+ * - RFP is enabled entirely, and only in PBM
+ * - FPP is enabled entirely, and only in PBM
+ * - A normal window when FPP is enabled globally and RFP is enabled in PBM, Protections Enabled and Disabled
+
+ *
+ * - (A) RFP is exempted on the maker and popup
+ * - (C) RFP is exempted on the maker but not the popup
+ * - (E) RFP is not exempted on the maker nor the popup
+ * - (G) RFP is not exempted on the maker but is on the popup
+ *
+ */
+
+"use strict";
+
+// =============================================================================================
+
+/**
+ * Compares two Uint8Arrays and returns the number of bits that are different.
+ *
+ * @param {Uint8ClampedArray} arr1 - The first Uint8ClampedArray to compare.
+ * @param {Uint8ClampedArray} arr2 - The second Uint8ClampedArray to compare.
+ * @returns {number} - The number of bits that are different between the two
+ * arrays.
+ */
+function countDifferencesInUint8Arrays(arr1, arr2) {
+ let count = 0;
+ for (let i = 0; i < arr1.length; i++) {
+ let diff = arr1[i] ^ arr2[i];
+ while (diff > 0) {
+ count += diff & 1;
+ diff >>= 1;
+ }
+ }
+ return count;
+}
+
+// =============================================================================================
+
+async function testCanvasRandomization(result, expectedResults, extraData) {
+ let testDesc = extraData.testDesc;
+ let differences = countDifferencesInUint8Arrays(
+ result,
+ UNMODIFIED_CANVAS_DATA
+ );
+
+ Assert.greaterOrEqual(
+ differences,
+ expectedResults[0],
+ `Checking ${testDesc} for canvas randomization - did not see enough random pixels.`
+ );
+ Assert.lessOrEqual(
+ differences,
+ expectedResults[1],
+ `Checking ${testDesc} for canvas randomization - saw too many random pixels.`
+ );
+}
+
+requestLongerTimeout(2);
+
+let expectedResults = {};
+var UNMODIFIED_CANVAS_DATA = undefined;
+
+add_setup(async function () {
+ // Disable the fingerprinting randomization.
+ await SpecialPowers.pushPrefEnv({
+ set: [
+ ["privacy.fingerprintingProtection", false],
+ ["privacy.fingerprintingProtection.pbmode", false],
+ ["privacy.resistFingerprinting", false],
+ ],
+ });
+
+ let extractCanvasData = function () {
+ let offscreenCanvas = new OffscreenCanvas(100, 100);
+
+ const context = offscreenCanvas.getContext("2d");
+
+ // Draw a red rectangle
+ context.fillStyle = "#EE2222";
+ context.fillRect(0, 0, 100, 100);
+ context.fillStyle = "#2222EE";
+ context.fillRect(20, 20, 100, 100);
+
+ const imageData = context.getImageData(0, 0, 100, 100);
+ return imageData.data;
+ };
+
+ function runExtractCanvasData(tab) {
+ let code = extractCanvasData.toString();
+ return SpecialPowers.spawn(tab.linkedBrowser, [code], async funccode => {
+ await content.eval(`var extractCanvasData = ${funccode}`);
+ let result = await content.eval(`extractCanvasData()`);
+ return result;
+ });
+ }
+
+ const emptyPage =
+ getRootDirectory(gTestPath).replace(
+ "chrome://mochitests/content",
+ "https://example.com"
+ ) + "empty.html";
+
+ // Open a tab for extracting the canvas data.
+ const tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, emptyPage);
+
+ let data = await runExtractCanvasData(tab);
+ UNMODIFIED_CANVAS_DATA = data;
+
+ BrowserTestUtils.removeTab(tab);
+ await SpecialPowers.popPrefEnv();
+});
+
+// Be sure to always use `let expectedResults = structuredClone(rfpFullyRandomized)` to do a
+// deep copy and avoiding corrupting the original 'const' object
+// The first value represents the minimum number of random pixels we should see
+// The second, the maximum number of random pixels
+const rfpFullyRandomized = [10000, 999999999];
+const fppRandomized = [1, 260];
+const noRandom = [0, 0];
+
+// Note that the starting page and the popup will be cross-domain from each other, but this test does not check that we inherit the randomizationkey,
+// only that the popup is randomized.
+const uri = `https://${FRAMER_DOMAIN}/browser/browser/components/resistfingerprinting/test/browser/file_canvas_iframer.html?mode=popup`;
+
+expectedResults = structuredClone(noRandom);
+add_task(
+ defaultsTest.bind(null, uri, testCanvasRandomization, expectedResults)
+);
+
+expectedResults = structuredClone(fppRandomized);
+add_task(
+ defaultsPBMTest.bind(null, uri, testCanvasRandomization, expectedResults)
+);
+
+expectedResults = structuredClone(rfpFullyRandomized);
+add_task(
+ simpleRFPTest.bind(null, uri, testCanvasRandomization, expectedResults)
+);
+
+// Test a private window with RFP enabled in PBMode
+expectedResults = structuredClone(rfpFullyRandomized);
+add_task(
+ simplePBMRFPTest.bind(null, uri, testCanvasRandomization, expectedResults)
+);
+
+expectedResults = structuredClone(fppRandomized);
+add_task(
+ simpleFPPTest.bind(null, uri, testCanvasRandomization, expectedResults)
+);
+
+// Test a Private Window with FPP Enabled in PBM
+expectedResults = structuredClone(fppRandomized);
+add_task(
+ simplePBMFPPTest.bind(null, uri, testCanvasRandomization, expectedResults)
+);
+
+// Test RFP Enabled in PBM and FPP enabled in Normal Browsing Mode, No Protections
+expectedResults = structuredClone(noRandom);
+add_task(
+ RFPPBMFPP_NormalMode_NoProtectionsTest.bind(
+ null,
+ uri,
+ testCanvasRandomization,
+ expectedResults
+ )
+);
+
+// Test RFP Enabled in PBM and FPP enabled in Normal Browsing Mode, Protections Enabled
+expectedResults = structuredClone(fppRandomized);
+add_task(
+ RFPPBMFPP_NormalMode_ProtectionsTest.bind(
+ null,
+ uri,
+ testCanvasRandomization,
+ expectedResults
+ )
+);
+
+// (A) RFP is exempted on the opener and openee
+expectedResults = structuredClone(noRandom);
+add_task(testA.bind(null, uri, testCanvasRandomization, expectedResults));
+
+// (C) RFP is exempted on the opener but not the openee
+expectedResults = structuredClone(rfpFullyRandomized);
+add_task(testC.bind(null, uri, testCanvasRandomization, expectedResults));
+
+// (E) RFP is not exempted on the opener nor the openee
+expectedResults = structuredClone(rfpFullyRandomized);
+add_task(testE.bind(null, uri, testCanvasRandomization, expectedResults));
+
+// (G) RFP is not exempted on the opener but is on the openee
+expectedResults = structuredClone(rfpFullyRandomized);
+add_task(testG.bind(null, uri, testCanvasRandomization, expectedResults));
diff --git a/browser/components/resistfingerprinting/test/browser/browser_canvascompare_iframes.js b/browser/components/resistfingerprinting/test/browser/browser_canvascompare_iframes.js
new file mode 100644
index 0000000000..a4da3aa9ec
--- /dev/null
+++ b/browser/components/resistfingerprinting/test/browser/browser_canvascompare_iframes.js
@@ -0,0 +1,263 @@
+/**
+ * This test compares canvas randomization on a parent and an iframe, and ensures that the canvas randomization key
+ * is inherited correctly. (e.g. that the canvases have the same random value)
+ *
+ * It runs all the tests twice - once for when the iframe is cross-domain, and once when it is same-domain
+ *
+ * Covers the following cases:
+ * - RFP/FPP is disabled entirely
+ * - RFP is enabled entirely, and only in PBM
+ * - FPP is enabled entirely, and only in PBM
+ * - A normal window when FPP is enabled globally and RFP is enabled in PBM, Protections Enabled and Disabled
+ *
+ */
+
+"use strict";
+
+// =============================================================================================
+
+/**
+ * Compares two Uint8Arrays and returns the number of bits that are different.
+ *
+ * @param {Uint8ClampedArray} arr1 - The first Uint8ClampedArray to compare.
+ * @param {Uint8ClampedArray} arr2 - The second Uint8ClampedArray to compare.
+ * @returns {number} - The number of bits that are different between the two
+ * arrays.
+ */
+function countDifferencesInUint8Arrays(arr1, arr2) {
+ let count = 0;
+ for (let i = 0; i < arr1.length; i++) {
+ let diff = arr1[i] ^ arr2[i];
+ while (diff > 0) {
+ count += diff & 1;
+ diff >>= 1;
+ }
+ }
+ return count;
+}
+
+// =============================================================================================
+
+async function testCanvasRandomization(result, expectedResults, extraData) {
+ let testDesc = extraData.testDesc;
+
+ let parent = result.mine;
+ let child = result.theirs;
+
+ let differencesInRandom = countDifferencesInUint8Arrays(parent, child);
+ let differencesFromUnmodifiedParent = countDifferencesInUint8Arrays(
+ UNMODIFIED_CANVAS_DATA,
+ parent
+ );
+ let differencesFromUnmodifiedChild = countDifferencesInUint8Arrays(
+ UNMODIFIED_CANVAS_DATA,
+ child
+ );
+
+ Assert.greaterOrEqual(
+ differencesFromUnmodifiedParent,
+ expectedResults[0],
+ `Checking ${testDesc} for canvas randomization, comparing parent - lower bound for random pixels.`
+ );
+ Assert.lessOrEqual(
+ differencesFromUnmodifiedParent,
+ expectedResults[1],
+ `Checking ${testDesc} for canvas randomization, comparing parent - upper bound for random pixels.`
+ );
+
+ Assert.greaterOrEqual(
+ differencesFromUnmodifiedChild,
+ expectedResults[0],
+ `Checking ${testDesc} for canvas randomization, comparing child - lower bound for random pixels.`
+ );
+ Assert.lessOrEqual(
+ differencesFromUnmodifiedChild,
+ expectedResults[1],
+ `Checking ${testDesc} for canvas randomization, comparing child - upper bound for random pixels.`
+ );
+
+ Assert.greaterOrEqual(
+ differencesInRandom,
+ expectedResults[2],
+ `Checking ${testDesc} and comparing randomization - lower bound for different random pixels.`
+ );
+ Assert.lessOrEqual(
+ differencesInRandom,
+ expectedResults[3],
+ `Checking ${testDesc} and comparing randomization - upper bound for different random pixels.`
+ );
+}
+
+requestLongerTimeout(2);
+
+let expectedResults = {};
+var UNMODIFIED_CANVAS_DATA = undefined;
+
+add_setup(async function () {
+ // Disable the fingerprinting randomization.
+ await SpecialPowers.pushPrefEnv({
+ set: [
+ ["privacy.fingerprintingProtection", false],
+ ["privacy.fingerprintingProtection.pbmode", false],
+ ["privacy.resistFingerprinting", false],
+ ],
+ });
+
+ let extractCanvasData = function () {
+ let offscreenCanvas = new OffscreenCanvas(100, 100);
+
+ const context = offscreenCanvas.getContext("2d");
+
+ // Draw a red rectangle
+ context.fillStyle = "#EE2222";
+ context.fillRect(0, 0, 100, 100);
+ context.fillStyle = "#2222EE";
+ context.fillRect(20, 20, 100, 100);
+
+ const imageData = context.getImageData(0, 0, 100, 100);
+ return imageData.data;
+ };
+
+ function runExtractCanvasData(tab) {
+ let code = extractCanvasData.toString();
+ return SpecialPowers.spawn(tab.linkedBrowser, [code], async funccode => {
+ await content.eval(`var extractCanvasData = ${funccode}`);
+ let result = await content.eval(`extractCanvasData()`);
+ return result;
+ });
+ }
+
+ const emptyPage =
+ getRootDirectory(gTestPath).replace(
+ "chrome://mochitests/content",
+ "https://example.com"
+ ) + "empty.html";
+
+ // Open a tab for extracting the canvas data.
+ const tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, emptyPage);
+
+ let data = await runExtractCanvasData(tab);
+ UNMODIFIED_CANVAS_DATA = data;
+
+ BrowserTestUtils.removeTab(tab);
+ await SpecialPowers.popPrefEnv();
+});
+
+// Be sure to always use `let expectedResults = structuredClone(allNotSpoofed)` to do a
+// deep copy and avoiding corrupting the original 'const' object
+// The first value represents the minimum number of random pixels we should see
+// The second, the maximum number of random pixels
+// The third, the minimum number of differences between the canvases of the parent and child
+// The fourth, the maximum number of differences between the canvases of the parent and child
+const rfpFullyRandomized = [10000, 999999999, 20000, 999999999];
+const fppRandomized = [1, 260, 0, 0];
+const noRandom = [0, 0, 0, 0];
+
+// Note that we are inheriting the randomization key ACROSS top-level domains that are cross-domain, because the iframe is a 3rd party domain
+let uri = `https://${FRAMER_DOMAIN}/browser/browser/components/resistfingerprinting/test/browser/file_canvascompare_iframer.html?mode=iframe`;
+
+expectedResults = structuredClone(noRandom);
+add_task(
+ defaultsTest.bind(null, uri, testCanvasRandomization, expectedResults)
+);
+
+expectedResults = structuredClone(fppRandomized);
+add_task(
+ defaultsPBMTest.bind(null, uri, testCanvasRandomization, expectedResults)
+);
+
+expectedResults = structuredClone(rfpFullyRandomized);
+add_task(
+ simpleRFPTest.bind(null, uri, testCanvasRandomization, expectedResults)
+);
+
+// Test a private window with RFP enabled in PBMode
+expectedResults = structuredClone(rfpFullyRandomized);
+add_task(
+ simplePBMRFPTest.bind(null, uri, testCanvasRandomization, expectedResults)
+);
+
+expectedResults = structuredClone(fppRandomized);
+add_task(
+ simpleFPPTest.bind(null, uri, testCanvasRandomization, expectedResults)
+);
+
+// Test a Private Window with FPP Enabled in PBM
+expectedResults = structuredClone(fppRandomized);
+add_task(
+ simplePBMFPPTest.bind(null, uri, testCanvasRandomization, expectedResults)
+);
+
+// Test RFP Enabled in PBM and FPP enabled in Normal Browsing Mode, No Protections
+expectedResults = structuredClone(noRandom);
+add_task(
+ RFPPBMFPP_NormalMode_NoProtectionsTest.bind(
+ null,
+ uri,
+ testCanvasRandomization,
+ expectedResults
+ )
+);
+
+// Test RFP Enabled in PBM and FPP enabled in Normal Browsing Mode, Protections Enabled
+expectedResults = structuredClone(fppRandomized);
+add_task(
+ RFPPBMFPP_NormalMode_ProtectionsTest.bind(
+ null,
+ uri,
+ testCanvasRandomization,
+ expectedResults
+ )
+);
+
+// And here the we are inheriting the randomization key into an iframe that is same-domain to the parent
+uri = `https://${IFRAME_DOMAIN}/browser/browser/components/resistfingerprinting/test/browser/file_canvascompare_iframer.html?mode=iframe`;
+
+expectedResults = structuredClone(noRandom);
+add_task(
+ defaultsTest.bind(null, uri, testCanvasRandomization, expectedResults)
+);
+
+expectedResults = structuredClone(rfpFullyRandomized);
+add_task(
+ simpleRFPTest.bind(null, uri, testCanvasRandomization, expectedResults)
+);
+
+// Test a private window with RFP enabled in PBMode
+expectedResults = structuredClone(rfpFullyRandomized);
+add_task(
+ simplePBMRFPTest.bind(null, uri, testCanvasRandomization, expectedResults)
+);
+
+expectedResults = structuredClone(fppRandomized);
+add_task(
+ simpleFPPTest.bind(null, uri, testCanvasRandomization, expectedResults)
+);
+
+// Test a Private Window with FPP Enabled in PBM
+expectedResults = structuredClone(fppRandomized);
+add_task(
+ simplePBMFPPTest.bind(null, uri, testCanvasRandomization, expectedResults)
+);
+
+// Test RFP Enabled in PBM and FPP enabled in Normal Browsing Mode, No Protections
+expectedResults = structuredClone(noRandom);
+add_task(
+ RFPPBMFPP_NormalMode_NoProtectionsTest.bind(
+ null,
+ uri,
+ testCanvasRandomization,
+ expectedResults
+ )
+);
+
+// Test RFP Enabled in PBM and FPP enabled in Normal Browsing Mode, Protections Enabled
+expectedResults = structuredClone(fppRandomized);
+add_task(
+ RFPPBMFPP_NormalMode_ProtectionsTest.bind(
+ null,
+ uri,
+ testCanvasRandomization,
+ expectedResults
+ )
+);
diff --git a/browser/components/resistfingerprinting/test/browser/browser_canvascompare_iframes_aboutblank.js b/browser/components/resistfingerprinting/test/browser/browser_canvascompare_iframes_aboutblank.js
new file mode 100644
index 0000000000..1fd9ac2150
--- /dev/null
+++ b/browser/components/resistfingerprinting/test/browser/browser_canvascompare_iframes_aboutblank.js
@@ -0,0 +1,266 @@
+/**
+ * This test compares canvas randomization on an iframe and an iframe of about:blank within that iframe, and
+ * ensures that the canvas randomization key is inherited correctly. (e.g. that the canvases have the same
+ * random value.) There's three pages at play here: the parent frame, the iframe, and the about:blank iframe
+ * within the iframe. We only compare the inner-most two, we don't measure the outer one.
+ *
+ * It runs all the tests twice - once for when the iframe is cross-domain from the parent, and once when it is
+ * same-domain. But in both cases the about:blank iframe is same-domain to its parent.
+ *
+ * Covers the following cases:
+ * - RFP/FPP is disabled entirely
+ * - RFP is enabled entirely, and only in PBM
+ * - FPP is enabled entirely, and only in PBM
+ * - A normal window when FPP is enabled globally and RFP is enabled in PBM, Protections Enabled and Disabled
+ *
+ */
+
+"use strict";
+
+// =============================================================================================
+
+/**
+ * Compares two Uint8Arrays and returns the number of bits that are different.
+ *
+ * @param {Uint8ClampedArray} arr1 - The first Uint8ClampedArray to compare.
+ * @param {Uint8ClampedArray} arr2 - The second Uint8ClampedArray to compare.
+ * @returns {number} - The number of bits that are different between the two
+ * arrays.
+ */
+function countDifferencesInUint8Arrays(arr1, arr2) {
+ let count = 0;
+ for (let i = 0; i < arr1.length; i++) {
+ let diff = arr1[i] ^ arr2[i];
+ while (diff > 0) {
+ count += diff & 1;
+ diff >>= 1;
+ }
+ }
+ return count;
+}
+
+// =============================================================================================
+
+async function testCanvasRandomization(result, expectedResults, extraData) {
+ let testDesc = extraData.testDesc;
+
+ let parent = result.mine;
+ let child = result.theirs;
+
+ let differencesInRandom = countDifferencesInUint8Arrays(parent, child);
+ let differencesFromUnmodifiedParent = countDifferencesInUint8Arrays(
+ UNMODIFIED_CANVAS_DATA,
+ parent
+ );
+ let differencesFromUnmodifiedChild = countDifferencesInUint8Arrays(
+ UNMODIFIED_CANVAS_DATA,
+ child
+ );
+
+ Assert.greaterOrEqual(
+ differencesFromUnmodifiedParent,
+ expectedResults[0],
+ `Checking ${testDesc} for canvas randomization, comparing parent - lower bound for random pixels.`
+ );
+ Assert.lessOrEqual(
+ differencesFromUnmodifiedParent,
+ expectedResults[1],
+ `Checking ${testDesc} for canvas randomization, comparing parent - upper bound for random pixels.`
+ );
+
+ Assert.greaterOrEqual(
+ differencesFromUnmodifiedChild,
+ expectedResults[0],
+ `Checking ${testDesc} for canvas randomization, comparing child - lower bound for random pixels.`
+ );
+ Assert.lessOrEqual(
+ differencesFromUnmodifiedChild,
+ expectedResults[1],
+ `Checking ${testDesc} for canvas randomization, comparing child - upper bound for random pixels.`
+ );
+
+ Assert.greaterOrEqual(
+ differencesInRandom,
+ expectedResults[2],
+ `Checking ${testDesc} and comparing randomization - lower bound for different random pixels.`
+ );
+ Assert.lessOrEqual(
+ differencesInRandom,
+ expectedResults[3],
+ `Checking ${testDesc} and comparing randomization - upper bound for different random pixels.`
+ );
+}
+
+requestLongerTimeout(2);
+
+let expectedResults = {};
+var UNMODIFIED_CANVAS_DATA = undefined;
+
+add_setup(async function () {
+ // Disable the fingerprinting randomization.
+ await SpecialPowers.pushPrefEnv({
+ set: [
+ ["privacy.fingerprintingProtection", false],
+ ["privacy.fingerprintingProtection.pbmode", false],
+ ["privacy.resistFingerprinting", false],
+ ],
+ });
+
+ let extractCanvasData = function () {
+ let offscreenCanvas = new OffscreenCanvas(100, 100);
+
+ const context = offscreenCanvas.getContext("2d");
+
+ // Draw a red rectangle
+ context.fillStyle = "#EE2222";
+ context.fillRect(0, 0, 100, 100);
+ context.fillStyle = "#2222EE";
+ context.fillRect(20, 20, 100, 100);
+
+ const imageData = context.getImageData(0, 0, 100, 100);
+ return imageData.data;
+ };
+
+ function runExtractCanvasData(tab) {
+ let code = extractCanvasData.toString();
+ return SpecialPowers.spawn(tab.linkedBrowser, [code], async funccode => {
+ await content.eval(`var extractCanvasData = ${funccode}`);
+ let result = await content.eval(`extractCanvasData()`);
+ return result;
+ });
+ }
+
+ const emptyPage =
+ getRootDirectory(gTestPath).replace(
+ "chrome://mochitests/content",
+ "https://example.com"
+ ) + "empty.html";
+
+ // Open a tab for extracting the canvas data.
+ const tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, emptyPage);
+
+ let data = await runExtractCanvasData(tab);
+ UNMODIFIED_CANVAS_DATA = data;
+
+ BrowserTestUtils.removeTab(tab);
+ await SpecialPowers.popPrefEnv();
+});
+
+// Be sure to always use `let expectedResults = structuredClone(allNotSpoofed)` to do a
+// deep copy and avoiding corrupting the original 'const' object
+// The first value represents the minimum number of random pixels we should see
+// The second, the maximum number of random pixels
+// The third, the minimum number of differences between the canvases of the parent and child
+// The fourth, the maximum number of differences between the canvases of the parent and child
+const rfpFullyRandomized = [10000, 999999999, 20000, 999999999];
+const fppRandomizedSameDomain = [1, 260, 0, 0];
+const noRandom = [0, 0, 0, 0];
+
+// Note that we are inheriting the randomization key ACROSS top-level domains that are cross-domain, because the iframe is a 3rd party domain
+let uri = `https://${FRAMER_DOMAIN}/browser/browser/components/resistfingerprinting/test/browser/file_canvascompare_aboutblank_iframer.html`;
+
+expectedResults = structuredClone(noRandom);
+add_task(
+ defaultsTest.bind(null, uri, testCanvasRandomization, expectedResults)
+);
+
+expectedResults = structuredClone(fppRandomizedSameDomain);
+add_task(
+ defaultsPBMTest.bind(null, uri, testCanvasRandomization, expectedResults)
+);
+
+expectedResults = structuredClone(rfpFullyRandomized);
+add_task(
+ simpleRFPTest.bind(null, uri, testCanvasRandomization, expectedResults)
+);
+
+// Test a private window with RFP enabled in PBMode
+expectedResults = structuredClone(rfpFullyRandomized);
+add_task(
+ simplePBMRFPTest.bind(null, uri, testCanvasRandomization, expectedResults)
+);
+
+expectedResults = structuredClone(fppRandomizedSameDomain);
+add_task(
+ simpleFPPTest.bind(null, uri, testCanvasRandomization, expectedResults)
+);
+
+// Test a Private Window with FPP Enabled in PBM
+expectedResults = structuredClone(fppRandomizedSameDomain);
+add_task(
+ simplePBMFPPTest.bind(null, uri, testCanvasRandomization, expectedResults)
+);
+
+// Test RFP Enabled in PBM and FPP enabled in Normal Browsing Mode, No Protections
+expectedResults = structuredClone(noRandom);
+add_task(
+ RFPPBMFPP_NormalMode_NoProtectionsTest.bind(
+ null,
+ uri,
+ testCanvasRandomization,
+ expectedResults
+ )
+);
+
+// Test RFP Enabled in PBM and FPP enabled in Normal Browsing Mode, Protections Enabled
+expectedResults = structuredClone(fppRandomizedSameDomain);
+add_task(
+ RFPPBMFPP_NormalMode_ProtectionsTest.bind(
+ null,
+ uri,
+ testCanvasRandomization,
+ expectedResults
+ )
+);
+
+// And here the we are inheriting the randomization key into an iframe that is same-domain to the parent
+uri = `https://${IFRAME_DOMAIN}/browser/browser/components/resistfingerprinting/test/browser/file_canvascompare_aboutblank_iframer.html`;
+
+expectedResults = structuredClone(noRandom);
+add_task(
+ defaultsTest.bind(null, uri, testCanvasRandomization, expectedResults)
+);
+
+expectedResults = structuredClone(rfpFullyRandomized);
+add_task(
+ simpleRFPTest.bind(null, uri, testCanvasRandomization, expectedResults)
+);
+
+// Test a private window with RFP enabled in PBMode
+expectedResults = structuredClone(rfpFullyRandomized);
+add_task(
+ simplePBMRFPTest.bind(null, uri, testCanvasRandomization, expectedResults)
+);
+
+expectedResults = structuredClone(fppRandomizedSameDomain);
+add_task(
+ simpleFPPTest.bind(null, uri, testCanvasRandomization, expectedResults)
+);
+
+// Test a Private Window with FPP Enabled in PBM
+expectedResults = structuredClone(fppRandomizedSameDomain);
+add_task(
+ simplePBMFPPTest.bind(null, uri, testCanvasRandomization, expectedResults)
+);
+
+// Test RFP Enabled in PBM and FPP enabled in Normal Browsing Mode, No Protections
+expectedResults = structuredClone(noRandom);
+add_task(
+ RFPPBMFPP_NormalMode_NoProtectionsTest.bind(
+ null,
+ uri,
+ testCanvasRandomization,
+ expectedResults
+ )
+);
+
+// Test RFP Enabled in PBM and FPP enabled in Normal Browsing Mode, Protections Enabled
+expectedResults = structuredClone(fppRandomizedSameDomain);
+add_task(
+ RFPPBMFPP_NormalMode_ProtectionsTest.bind(
+ null,
+ uri,
+ testCanvasRandomization,
+ expectedResults
+ )
+);
diff --git a/browser/components/resistfingerprinting/test/browser/browser_canvascompare_iframes_blob.js b/browser/components/resistfingerprinting/test/browser/browser_canvascompare_iframes_blob.js
new file mode 100644
index 0000000000..be7aedb174
--- /dev/null
+++ b/browser/components/resistfingerprinting/test/browser/browser_canvascompare_iframes_blob.js
@@ -0,0 +1,266 @@
+/**
+ * This test compares canvas randomization on an iframe and a blob: iframe within that iframe, and
+ * ensures that the canvas randomization key is inherited correctly. (e.g. that the canvases have the same
+ * random value.) There's three pages at play here: the parent frame, the iframe, and the blob iframe
+ * within the iframe. We only compare the inner-most two, we don't measure the outer one.
+ *
+ * It runs all the tests twice - once for when the iframe is cross-domain from the parent, and once when it is
+ * same-domain. But in both cases the blob iframe is same-domain to its parent.
+ *
+ * Covers the following cases:
+ * - RFP/FPP is disabled entirely
+ * - RFP is enabled entirely, and only in PBM
+ * - FPP is enabled entirely, and only in PBM
+ * - A normal window when FPP is enabled globally and RFP is enabled in PBM, Protections Enabled and Disabled
+ *
+ */
+
+"use strict";
+
+// =============================================================================================
+
+/**
+ * Compares two Uint8Arrays and returns the number of bits that are different.
+ *
+ * @param {Uint8ClampedArray} arr1 - The first Uint8ClampedArray to compare.
+ * @param {Uint8ClampedArray} arr2 - The second Uint8ClampedArray to compare.
+ * @returns {number} - The number of bits that are different between the two
+ * arrays.
+ */
+function countDifferencesInUint8Arrays(arr1, arr2) {
+ let count = 0;
+ for (let i = 0; i < arr1.length; i++) {
+ let diff = arr1[i] ^ arr2[i];
+ while (diff > 0) {
+ count += diff & 1;
+ diff >>= 1;
+ }
+ }
+ return count;
+}
+
+// =============================================================================================
+
+async function testCanvasRandomization(result, expectedResults, extraData) {
+ let testDesc = extraData.testDesc;
+
+ let parent = result.mine;
+ let child = result.theirs;
+
+ let differencesInRandom = countDifferencesInUint8Arrays(parent, child);
+ let differencesFromUnmodifiedParent = countDifferencesInUint8Arrays(
+ UNMODIFIED_CANVAS_DATA,
+ parent
+ );
+ let differencesFromUnmodifiedChild = countDifferencesInUint8Arrays(
+ UNMODIFIED_CANVAS_DATA,
+ child
+ );
+
+ Assert.greaterOrEqual(
+ differencesFromUnmodifiedParent,
+ expectedResults[0],
+ `Checking ${testDesc} for canvas randomization, comparing parent - lower bound for random pixels.`
+ );
+ Assert.lessOrEqual(
+ differencesFromUnmodifiedParent,
+ expectedResults[1],
+ `Checking ${testDesc} for canvas randomization, comparing parent - upper bound for random pixels.`
+ );
+
+ Assert.greaterOrEqual(
+ differencesFromUnmodifiedChild,
+ expectedResults[0],
+ `Checking ${testDesc} for canvas randomization, comparing child - lower bound for random pixels.`
+ );
+ Assert.lessOrEqual(
+ differencesFromUnmodifiedChild,
+ expectedResults[1],
+ `Checking ${testDesc} for canvas randomization, comparing child - upper bound for random pixels.`
+ );
+
+ Assert.greaterOrEqual(
+ differencesInRandom,
+ expectedResults[2],
+ `Checking ${testDesc} and comparing randomization - lower bound for different random pixels.`
+ );
+ Assert.lessOrEqual(
+ differencesInRandom,
+ expectedResults[3],
+ `Checking ${testDesc} and comparing randomization - upper bound for different random pixels.`
+ );
+}
+
+requestLongerTimeout(2);
+
+let expectedResults = {};
+var UNMODIFIED_CANVAS_DATA = undefined;
+
+add_setup(async function () {
+ // Disable the fingerprinting randomization.
+ await SpecialPowers.pushPrefEnv({
+ set: [
+ ["privacy.fingerprintingProtection", false],
+ ["privacy.fingerprintingProtection.pbmode", false],
+ ["privacy.resistFingerprinting", false],
+ ],
+ });
+
+ let extractCanvasData = function () {
+ let offscreenCanvas = new OffscreenCanvas(100, 100);
+
+ const context = offscreenCanvas.getContext("2d");
+
+ // Draw a red rectangle
+ context.fillStyle = "#EE2222";
+ context.fillRect(0, 0, 100, 100);
+ context.fillStyle = "#2222EE";
+ context.fillRect(20, 20, 100, 100);
+
+ const imageData = context.getImageData(0, 0, 100, 100);
+ return imageData.data;
+ };
+
+ function runExtractCanvasData(tab) {
+ let code = extractCanvasData.toString();
+ return SpecialPowers.spawn(tab.linkedBrowser, [code], async funccode => {
+ await content.eval(`var extractCanvasData = ${funccode}`);
+ let result = await content.eval(`extractCanvasData()`);
+ return result;
+ });
+ }
+
+ const emptyPage =
+ getRootDirectory(gTestPath).replace(
+ "chrome://mochitests/content",
+ "https://example.com"
+ ) + "empty.html";
+
+ // Open a tab for extracting the canvas data.
+ const tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, emptyPage);
+
+ let data = await runExtractCanvasData(tab);
+ UNMODIFIED_CANVAS_DATA = data;
+
+ BrowserTestUtils.removeTab(tab);
+ await SpecialPowers.popPrefEnv();
+});
+
+// Be sure to always use `let expectedResults = structuredClone(allNotSpoofed)` to do a
+// deep copy and avoiding corrupting the original 'const' object
+// The first value represents the minimum number of random pixels we should see
+// The second, the maximum number of random pixels
+// The third, the minimum number of differences between the canvases of the parent and child
+// The fourth, the maximum number of differences between the canvases of the parent and child
+const rfpFullyRandomized = [10000, 999999999, 20000, 999999999];
+const fppRandomizedSameDomain = [1, 260, 0, 0];
+const noRandom = [0, 0, 0, 0];
+
+// Note that we are inheriting the randomization key ACROSS top-level domains that are cross-domain, because the iframe is a 3rd party domain
+let uri = `https://${FRAMER_DOMAIN}/browser/browser/components/resistfingerprinting/test/browser/file_canvascompare_blob_iframer.html`;
+
+expectedResults = structuredClone(noRandom);
+add_task(
+ defaultsTest.bind(null, uri, testCanvasRandomization, expectedResults)
+);
+
+expectedResults = structuredClone(fppRandomizedSameDomain);
+add_task(
+ defaultsPBMTest.bind(null, uri, testCanvasRandomization, expectedResults)
+);
+
+expectedResults = structuredClone(rfpFullyRandomized);
+add_task(
+ simpleRFPTest.bind(null, uri, testCanvasRandomization, expectedResults)
+);
+
+// Test a private window with RFP enabled in PBMode
+expectedResults = structuredClone(rfpFullyRandomized);
+add_task(
+ simplePBMRFPTest.bind(null, uri, testCanvasRandomization, expectedResults)
+);
+
+expectedResults = structuredClone(fppRandomizedSameDomain);
+add_task(
+ simpleFPPTest.bind(null, uri, testCanvasRandomization, expectedResults)
+);
+
+// Test a Private Window with FPP Enabled in PBM
+expectedResults = structuredClone(fppRandomizedSameDomain);
+add_task(
+ simplePBMFPPTest.bind(null, uri, testCanvasRandomization, expectedResults)
+);
+
+// Test RFP Enabled in PBM and FPP enabled in Normal Browsing Mode, No Protections
+expectedResults = structuredClone(noRandom);
+add_task(
+ RFPPBMFPP_NormalMode_NoProtectionsTest.bind(
+ null,
+ uri,
+ testCanvasRandomization,
+ expectedResults
+ )
+);
+
+// Test RFP Enabled in PBM and FPP enabled in Normal Browsing Mode, Protections Enabled
+expectedResults = structuredClone(fppRandomizedSameDomain);
+add_task(
+ RFPPBMFPP_NormalMode_ProtectionsTest.bind(
+ null,
+ uri,
+ testCanvasRandomization,
+ expectedResults
+ )
+);
+
+// And here the we are inheriting the randomization key into an iframe that is same-domain to the parent
+uri = `https://${IFRAME_DOMAIN}/browser/browser/components/resistfingerprinting/test/browser/file_canvascompare_blob_iframer.html`;
+
+expectedResults = structuredClone(noRandom);
+add_task(
+ defaultsTest.bind(null, uri, testCanvasRandomization, expectedResults)
+);
+
+expectedResults = structuredClone(rfpFullyRandomized);
+add_task(
+ simpleRFPTest.bind(null, uri, testCanvasRandomization, expectedResults)
+);
+
+// Test a private window with RFP enabled in PBMode
+expectedResults = structuredClone(rfpFullyRandomized);
+add_task(
+ simplePBMRFPTest.bind(null, uri, testCanvasRandomization, expectedResults)
+);
+
+expectedResults = structuredClone(fppRandomizedSameDomain);
+add_task(
+ simpleFPPTest.bind(null, uri, testCanvasRandomization, expectedResults)
+);
+
+// Test a Private Window with FPP Enabled in PBM
+expectedResults = structuredClone(fppRandomizedSameDomain);
+add_task(
+ simplePBMFPPTest.bind(null, uri, testCanvasRandomization, expectedResults)
+);
+
+// Test RFP Enabled in PBM and FPP enabled in Normal Browsing Mode, No Protections
+expectedResults = structuredClone(noRandom);
+add_task(
+ RFPPBMFPP_NormalMode_NoProtectionsTest.bind(
+ null,
+ uri,
+ testCanvasRandomization,
+ expectedResults
+ )
+);
+
+// Test RFP Enabled in PBM and FPP enabled in Normal Browsing Mode, Protections Enabled
+expectedResults = structuredClone(fppRandomizedSameDomain);
+add_task(
+ RFPPBMFPP_NormalMode_ProtectionsTest.bind(
+ null,
+ uri,
+ testCanvasRandomization,
+ expectedResults
+ )
+);
diff --git a/browser/components/resistfingerprinting/test/browser/browser_canvascompare_iframes_data.js b/browser/components/resistfingerprinting/test/browser/browser_canvascompare_iframes_data.js
new file mode 100644
index 0000000000..dcf10564e0
--- /dev/null
+++ b/browser/components/resistfingerprinting/test/browser/browser_canvascompare_iframes_data.js
@@ -0,0 +1,271 @@
+/**
+ * This test compares canvas randomization on an iframe and a data iframe within that iframe, and
+ * ensures that the canvas randomization key is inherited correctly. (e.g. that the canvases have the same
+ * random value.) There's three pages at play here: the parent frame, the iframe, and the data: iframe
+ * within the iframe. We only compare the inner-most two, we don't measure the outer one.
+ *
+ * It runs all the tests twice - once for when the iframe is cross-domain from the parent, and once when it is
+ * same-domain. But in both cases the data: iframe is same-domain to its parent.
+ *
+ * Covers the following cases:
+ * - RFP/FPP is disabled entirely
+ * - RFP is enabled entirely, and only in PBM
+ * - FPP is enabled entirely, and only in PBM
+ * - A normal window when FPP is enabled globally and RFP is enabled in PBM, Protections Enabled and Disabled
+ *
+ */
+
+"use strict";
+
+// =============================================================================================
+
+/**
+ * Compares two Uint8Arrays and returns the number of bits that are different.
+ *
+ * @param {Uint8ClampedArray} arr1 - The first Uint8ClampedArray to compare.
+ * @param {Uint8ClampedArray} arr2 - The second Uint8ClampedArray to compare.
+ * @returns {number} - The number of bits that are different between the two
+ * arrays.
+ */
+function countDifferencesInUint8Arrays(arr1, arr2) {
+ let count = 0;
+ for (let i = 0; i < arr1.length; i++) {
+ let diff = arr1[i] ^ arr2[i];
+ while (diff > 0) {
+ count += diff & 1;
+ diff >>= 1;
+ }
+ }
+ return count;
+}
+
+// =============================================================================================
+
+async function testCanvasRandomization(result, expectedResults, extraData) {
+ let testDesc = extraData.testDesc;
+
+ let parent = result.mine;
+ let child = result.theirs;
+
+ let differencesInRandom = countDifferencesInUint8Arrays(parent, child);
+ let differencesFromUnmodifiedParent = countDifferencesInUint8Arrays(
+ UNMODIFIED_CANVAS_DATA,
+ parent
+ );
+ let differencesFromUnmodifiedChild = countDifferencesInUint8Arrays(
+ UNMODIFIED_CANVAS_DATA,
+ child
+ );
+
+ Assert.greaterOrEqual(
+ differencesFromUnmodifiedParent,
+ expectedResults[0],
+ `Checking ${testDesc} for canvas randomization, comparing parent - lower bound for random pixels.`
+ );
+ Assert.lessOrEqual(
+ differencesFromUnmodifiedParent,
+ expectedResults[1],
+ `Checking ${testDesc} for canvas randomization, comparing parent - upper bound for random pixels.`
+ );
+
+ Assert.greaterOrEqual(
+ differencesFromUnmodifiedChild,
+ expectedResults[0],
+ `Checking ${testDesc} for canvas randomization, comparing child - lower bound for random pixels.`
+ );
+ Assert.lessOrEqual(
+ differencesFromUnmodifiedChild,
+ expectedResults[1],
+ `Checking ${testDesc} for canvas randomization, comparing child - upper bound for random pixels.`
+ );
+
+ Assert.greaterOrEqual(
+ differencesInRandom,
+ expectedResults[2],
+ `Checking ${testDesc} and comparing randomization - lower bound for different random pixels.`
+ );
+ Assert.lessOrEqual(
+ differencesInRandom,
+ expectedResults[3],
+ `Checking ${testDesc} and comparing randomization - upper bound for different random pixels.`
+ );
+}
+
+requestLongerTimeout(2);
+
+let expectedResults = {};
+var UNMODIFIED_CANVAS_DATA = undefined;
+
+add_setup(async function () {
+ // Disable the fingerprinting randomization.
+ await SpecialPowers.pushPrefEnv({
+ set: [
+ ["privacy.fingerprintingProtection", false],
+ ["privacy.fingerprintingProtection.pbmode", false],
+ ["privacy.resistFingerprinting", false],
+ ],
+ });
+
+ let extractCanvasData = function () {
+ let offscreenCanvas = new OffscreenCanvas(100, 100);
+
+ const context = offscreenCanvas.getContext("2d");
+
+ // Draw a red rectangle
+ context.fillStyle = "#EE2222";
+ context.fillRect(0, 0, 100, 100);
+ context.fillStyle = "#2222EE";
+ context.fillRect(20, 20, 100, 100);
+
+ const imageData = context.getImageData(0, 0, 100, 100);
+ return imageData.data;
+ };
+
+ function runExtractCanvasData(tab) {
+ let code = extractCanvasData.toString();
+ return SpecialPowers.spawn(tab.linkedBrowser, [code], async funccode => {
+ await content.eval(`var extractCanvasData = ${funccode}`);
+ let result = await content.eval(`extractCanvasData()`);
+ return result;
+ });
+ }
+
+ const emptyPage =
+ getRootDirectory(gTestPath).replace(
+ "chrome://mochitests/content",
+ "https://example.com"
+ ) + "empty.html";
+
+ // Open a tab for extracting the canvas data.
+ const tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, emptyPage);
+
+ let data = await runExtractCanvasData(tab);
+ UNMODIFIED_CANVAS_DATA = data;
+
+ BrowserTestUtils.removeTab(tab);
+ await SpecialPowers.popPrefEnv();
+});
+
+// Be sure to always use `let expectedResults = structuredClone(allNotSpoofed)` to do a
+// deep copy and avoiding corrupting the original 'const' object
+// The first value represents the minimum number of random pixels we should see
+// The second, the maximum number of random pixels
+// The third, the minimum number of differences between the canvases of the parent and child
+// The fourth, the maximum number of differences between the canvases of the parent and child
+const rfpFullyRandomized = [10000, 999999999, 20000, 999999999];
+const fppRandomizedSameDomain = [1, 260, 0, 0];
+const noRandom = [0, 0, 0, 0];
+
+// Note that we are inheriting the randomization key ACROSS top-level domains that are cross-domain, because the iframe is a 3rd party domain
+let uri = `https://${FRAMER_DOMAIN}/browser/browser/components/resistfingerprinting/test/browser/file_canvascompare_data_iframer.html`;
+
+expectedResults = structuredClone(noRandom);
+add_task(
+ defaultsTest.bind(null, uri, testCanvasRandomization, expectedResults)
+);
+
+expectedResults = structuredClone(fppRandomizedSameDomain);
+add_task(
+ defaultsPBMTest.bind(null, uri, testCanvasRandomization, expectedResults)
+);
+
+expectedResults = structuredClone(rfpFullyRandomized);
+add_task(
+ simpleRFPTest.bind(null, uri, testCanvasRandomization, expectedResults)
+);
+
+// Test a private window with RFP enabled in PBMode
+expectedResults = structuredClone(rfpFullyRandomized);
+add_task(
+ simplePBMRFPTest.bind(null, uri, testCanvasRandomization, expectedResults)
+);
+
+expectedResults = structuredClone(fppRandomizedSameDomain);
+add_task(
+ simpleFPPTest.bind(null, uri, testCanvasRandomization, expectedResults)
+);
+
+// Test a Private Window with FPP Enabled in PBM
+expectedResults = structuredClone(fppRandomizedSameDomain);
+add_task(
+ simplePBMFPPTest.bind(null, uri, testCanvasRandomization, expectedResults)
+);
+
+// Test RFP Enabled in PBM and FPP enabled in Normal Browsing Mode, No Protections
+expectedResults = structuredClone(noRandom);
+add_task(
+ RFPPBMFPP_NormalMode_NoProtectionsTest.bind(
+ null,
+ uri,
+ testCanvasRandomization,
+ expectedResults
+ )
+);
+
+// Test RFP Enabled in PBM and FPP enabled in Normal Browsing Mode, Protections Enabled
+expectedResults = structuredClone(fppRandomizedSameDomain);
+add_task(
+ RFPPBMFPP_NormalMode_ProtectionsTest.bind(
+ null,
+ uri,
+ testCanvasRandomization,
+ expectedResults
+ )
+);
+
+// And here the we are inheriting the randomization key into an iframe that is same-domain to the parent
+uri = `https://${IFRAME_DOMAIN}/browser/browser/components/resistfingerprinting/test/browser/file_canvascompare_data_iframer.html`;
+
+expectedResults = structuredClone(noRandom);
+add_task(
+ defaultsTest.bind(null, uri, testCanvasRandomization, expectedResults)
+);
+
+expectedResults = structuredClone(fppRandomizedSameDomain);
+add_task(
+ defaultsPBMTest.bind(null, uri, testCanvasRandomization, expectedResults)
+);
+
+expectedResults = structuredClone(rfpFullyRandomized);
+add_task(
+ simpleRFPTest.bind(null, uri, testCanvasRandomization, expectedResults)
+);
+
+// Test a private window with RFP enabled in PBMode
+expectedResults = structuredClone(rfpFullyRandomized);
+add_task(
+ simplePBMRFPTest.bind(null, uri, testCanvasRandomization, expectedResults)
+);
+
+expectedResults = structuredClone(fppRandomizedSameDomain);
+add_task(
+ simpleFPPTest.bind(null, uri, testCanvasRandomization, expectedResults)
+);
+
+// Test a Private Window with FPP Enabled in PBM
+expectedResults = structuredClone(fppRandomizedSameDomain);
+add_task(
+ simplePBMFPPTest.bind(null, uri, testCanvasRandomization, expectedResults)
+);
+
+// Test RFP Enabled in PBM and FPP enabled in Normal Browsing Mode, No Protections
+expectedResults = structuredClone(noRandom);
+add_task(
+ RFPPBMFPP_NormalMode_NoProtectionsTest.bind(
+ null,
+ uri,
+ testCanvasRandomization,
+ expectedResults
+ )
+);
+
+// Test RFP Enabled in PBM and FPP enabled in Normal Browsing Mode, Protections Enabled
+expectedResults = structuredClone(fppRandomizedSameDomain);
+add_task(
+ RFPPBMFPP_NormalMode_ProtectionsTest.bind(
+ null,
+ uri,
+ testCanvasRandomization,
+ expectedResults
+ )
+);
diff --git a/browser/components/resistfingerprinting/test/browser/browser_canvascompare_popups.js b/browser/components/resistfingerprinting/test/browser/browser_canvascompare_popups.js
new file mode 100644
index 0000000000..b1b6f5e9d8
--- /dev/null
+++ b/browser/components/resistfingerprinting/test/browser/browser_canvascompare_popups.js
@@ -0,0 +1,268 @@
+/**
+ * This test compares canvas randomization on a parent and a popup, and ensures that the canvas randomization key
+ * is inherited correctly. (e.g. that the canvases have the same random value)
+ *
+ * It runs all the tests twice - once for when the popup is cross-domain, and once when it is same-domain
+ * We DO NOT inherit a randomization key across cross-domain popups, but we do for same-domain
+ *
+ * Covers the following cases:
+ * - RFP/FPP is disabled entirely
+ * - RFP is enabled entirely, and only in PBM
+ * - FPP is enabled entirely, and only in PBM
+ * - A normal window when FPP is enabled globally and RFP is enabled in PBM, Protections Enabled and Disabled
+ *
+ */
+
+"use strict";
+
+// =============================================================================================
+
+/**
+ * Compares two Uint8Arrays and returns the number of bits that are different.
+ *
+ * @param {Uint8ClampedArray} arr1 - The first Uint8ClampedArray to compare.
+ * @param {Uint8ClampedArray} arr2 - The second Uint8ClampedArray to compare.
+ * @returns {number} - The number of bits that are different between the two
+ * arrays.
+ */
+function countDifferencesInUint8Arrays(arr1, arr2) {
+ let count = 0;
+ for (let i = 0; i < arr1.length; i++) {
+ let diff = arr1[i] ^ arr2[i];
+ while (diff > 0) {
+ count += diff & 1;
+ diff >>= 1;
+ }
+ }
+ return count;
+}
+
+// =============================================================================================
+
+async function testCanvasRandomization(result, expectedResults, extraData) {
+ let testDesc = extraData.testDesc;
+
+ let parent = result.mine;
+ let child = result.theirs;
+
+ let differencesInRandom = countDifferencesInUint8Arrays(parent, child);
+ let differencesFromUnmodifiedParent = countDifferencesInUint8Arrays(
+ UNMODIFIED_CANVAS_DATA,
+ parent
+ );
+ let differencesFromUnmodifiedChild = countDifferencesInUint8Arrays(
+ UNMODIFIED_CANVAS_DATA,
+ child
+ );
+
+ Assert.greaterOrEqual(
+ differencesFromUnmodifiedParent,
+ expectedResults[0],
+ `Checking ${testDesc} for canvas randomization, comparing parent - lower bound for random pixels.`
+ );
+ Assert.lessOrEqual(
+ differencesFromUnmodifiedParent,
+ expectedResults[1],
+ `Checking ${testDesc} for canvas randomization, comparing parent - upper bound for random pixels.`
+ );
+
+ Assert.greaterOrEqual(
+ differencesFromUnmodifiedChild,
+ expectedResults[0],
+ `Checking ${testDesc} for canvas randomization, comparing child - lower bound for random pixels.`
+ );
+ Assert.lessOrEqual(
+ differencesFromUnmodifiedChild,
+ expectedResults[1],
+ `Checking ${testDesc} for canvas randomization, comparing child - upper bound for random pixels.`
+ );
+
+ Assert.greaterOrEqual(
+ differencesInRandom,
+ expectedResults[2],
+ `Checking ${testDesc} and comparing randomization - lower bound for different random pixels.`
+ );
+ Assert.lessOrEqual(
+ differencesInRandom,
+ expectedResults[3],
+ `Checking ${testDesc} and comparing randomization - upper bound for different random pixels.`
+ );
+}
+
+requestLongerTimeout(2);
+
+let expectedResults = {};
+var UNMODIFIED_CANVAS_DATA = undefined;
+
+add_setup(async function () {
+ // Disable the fingerprinting randomization.
+ await SpecialPowers.pushPrefEnv({
+ set: [
+ ["privacy.fingerprintingProtection", false],
+ ["privacy.fingerprintingProtection.pbmode", false],
+ ["privacy.resistFingerprinting", false],
+ ],
+ });
+
+ let extractCanvasData = function () {
+ let offscreenCanvas = new OffscreenCanvas(100, 100);
+
+ const context = offscreenCanvas.getContext("2d");
+
+ // Draw a red rectangle
+ context.fillStyle = "#EE2222";
+ context.fillRect(0, 0, 100, 100);
+ context.fillStyle = "#2222EE";
+ context.fillRect(20, 20, 100, 100);
+
+ const imageData = context.getImageData(0, 0, 100, 100);
+ return imageData.data;
+ };
+
+ function runExtractCanvasData(tab) {
+ let code = extractCanvasData.toString();
+ return SpecialPowers.spawn(tab.linkedBrowser, [code], async funccode => {
+ await content.eval(`var extractCanvasData = ${funccode}`);
+ let result = await content.eval(`extractCanvasData()`);
+ return result;
+ });
+ }
+
+ const emptyPage =
+ getRootDirectory(gTestPath).replace(
+ "chrome://mochitests/content",
+ "https://example.com"
+ ) + "empty.html";
+
+ // Open a tab for extracting the canvas data.
+ const tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, emptyPage);
+
+ let data = await runExtractCanvasData(tab);
+ UNMODIFIED_CANVAS_DATA = data;
+
+ BrowserTestUtils.removeTab(tab);
+ await SpecialPowers.popPrefEnv();
+});
+
+// The following are convenience objects that allow you to quickly see what is
+// and is not modified from a logical set of values.
+// Be sure to always use `let expectedResults = structuredClone(allNotSpoofed)` to do a
+// deep copy and avoiding corrupting the original 'const' object
+// The first value represents the minimum number of random pixels we should see
+// The second, the maximum number of random pixels
+// The third, the minimum number of differences between the canvases of the parent and child
+// The fourth, the maximum number of differences between the canvases of the parent and child
+const rfpFullyRandomized = [10000, 999999999, 20000, 999999999];
+const fppRandomizedSameDomain = [1, 260, 0, 0];
+const fppRandomizedCrossDomain = [1, 260, 2, 520];
+const noRandom = [0, 0, 0, 0];
+
+// Note that we will be doing two sets of tests - one where the popup is on a cross-domain
+// and one where it is on the same domain. First, we do same domain
+let uri = `https://${IFRAME_DOMAIN}/browser/browser/components/resistfingerprinting/test/browser/file_canvascompare_iframer.html?mode=popup`;
+
+expectedResults = structuredClone(noRandom);
+add_task(
+ defaultsTest.bind(null, uri, testCanvasRandomization, expectedResults)
+);
+
+expectedResults = structuredClone(fppRandomizedSameDomain);
+add_task(
+ defaultsPBMTest.bind(null, uri, testCanvasRandomization, expectedResults)
+);
+
+expectedResults = structuredClone(rfpFullyRandomized);
+add_task(
+ simpleRFPTest.bind(null, uri, testCanvasRandomization, expectedResults)
+);
+
+// Test a private window with RFP enabled in PBMode
+expectedResults = structuredClone(rfpFullyRandomized);
+add_task(
+ simplePBMRFPTest.bind(null, uri, testCanvasRandomization, expectedResults)
+);
+
+expectedResults = structuredClone(fppRandomizedSameDomain);
+add_task(
+ simpleFPPTest.bind(null, uri, testCanvasRandomization, expectedResults)
+);
+
+// Test a Private Window with FPP Enabled in PBM
+expectedResults = structuredClone(fppRandomizedSameDomain);
+add_task(
+ simplePBMFPPTest.bind(null, uri, testCanvasRandomization, expectedResults)
+);
+
+// Test RFP Enabled in PBM and FPP enabled in Normal Browsing Mode, No Protections
+expectedResults = structuredClone(noRandom);
+add_task(
+ RFPPBMFPP_NormalMode_NoProtectionsTest.bind(
+ null,
+ uri,
+ testCanvasRandomization,
+ expectedResults
+ )
+);
+
+// Test RFP Enabled in PBM and FPP enabled in Normal Browsing Mode, Protections Enabled
+expectedResults = structuredClone(fppRandomizedSameDomain);
+add_task(
+ RFPPBMFPP_NormalMode_ProtectionsTest.bind(
+ null,
+ uri,
+ testCanvasRandomization,
+ expectedResults
+ )
+);
+
+// Now, cross-domain.
+uri = `https://${FRAMER_DOMAIN}/browser/browser/components/resistfingerprinting/test/browser/file_canvascompare_iframer.html?mode=popup`;
+
+expectedResults = structuredClone(noRandom);
+add_task(
+ defaultsTest.bind(null, uri, testCanvasRandomization, expectedResults)
+);
+
+expectedResults = structuredClone(rfpFullyRandomized);
+add_task(
+ simpleRFPTest.bind(null, uri, testCanvasRandomization, expectedResults)
+);
+
+// Test a private window with RFP enabled in PBMode
+expectedResults = structuredClone(rfpFullyRandomized);
+add_task(
+ simplePBMRFPTest.bind(null, uri, testCanvasRandomization, expectedResults)
+);
+
+expectedResults = structuredClone(fppRandomizedCrossDomain);
+add_task(
+ simpleFPPTest.bind(null, uri, testCanvasRandomization, expectedResults)
+);
+
+// Test a Private Window with FPP Enabled in PBM
+expectedResults = structuredClone(fppRandomizedCrossDomain);
+add_task(
+ simplePBMFPPTest.bind(null, uri, testCanvasRandomization, expectedResults)
+);
+
+// Test RFP Enabled in PBM and FPP enabled in Normal Browsing Mode, No Protections
+expectedResults = structuredClone(noRandom);
+add_task(
+ RFPPBMFPP_NormalMode_NoProtectionsTest.bind(
+ null,
+ uri,
+ testCanvasRandomization,
+ expectedResults
+ )
+);
+
+// Test RFP Enabled in PBM and FPP enabled in Normal Browsing Mode, Protections Enabled
+expectedResults = structuredClone(fppRandomizedCrossDomain);
+add_task(
+ RFPPBMFPP_NormalMode_ProtectionsTest.bind(
+ null,
+ uri,
+ testCanvasRandomization,
+ expectedResults
+ )
+);
diff --git a/browser/components/resistfingerprinting/test/browser/browser_canvascompare_popups_aboutblank.js b/browser/components/resistfingerprinting/test/browser/browser_canvascompare_popups_aboutblank.js
new file mode 100644
index 0000000000..5897aba7e5
--- /dev/null
+++ b/browser/components/resistfingerprinting/test/browser/browser_canvascompare_popups_aboutblank.js
@@ -0,0 +1,269 @@
+/**
+ * This test compares canvas randomization on a parent and an about:blank popup, and ensures that the canvas randomization key
+ * is inherited correctly. (e.g. that the canvases have the same random value)
+ *
+ * It runs all the tests twice. Development showed that there were two different code paths that might get taken for
+ * about:blank popup creation depending on when in the page lifecycle the popup is created. I don't understand why
+ * that is, but at time of writing, the subtle difference in the test will hit both code paths.
+ *
+ * Covers the following cases:
+ * - RFP/FPP is disabled entirely
+ * - RFP is enabled entirely, and only in PBM
+ * - FPP is enabled entirely, and only in PBM
+ * - A normal window when FPP is enabled globally and RFP is enabled in PBM, Protections Enabled and Disabled
+ *
+ */
+
+"use strict";
+
+// =============================================================================================
+
+/**
+ * Compares two Uint8Arrays and returns the number of bits that are different.
+ *
+ * @param {Uint8ClampedArray} arr1 - The first Uint8ClampedArray to compare.
+ * @param {Uint8ClampedArray} arr2 - The second Uint8ClampedArray to compare.
+ * @returns {number} - The number of bits that are different between the two
+ * arrays.
+ */
+function countDifferencesInUint8Arrays(arr1, arr2) {
+ let count = 0;
+ for (let i = 0; i < arr1.length; i++) {
+ let diff = arr1[i] ^ arr2[i];
+ while (diff > 0) {
+ count += diff & 1;
+ diff >>= 1;
+ }
+ }
+ return count;
+}
+
+// =============================================================================================
+
+async function testCanvasRandomization(result, expectedResults, extraData) {
+ let testDesc = extraData.testDesc;
+
+ let parent = result.mine;
+ let child = result.theirs;
+
+ let differencesInRandom = countDifferencesInUint8Arrays(parent, child);
+ let differencesFromUnmodifiedParent = countDifferencesInUint8Arrays(
+ UNMODIFIED_CANVAS_DATA,
+ parent
+ );
+ let differencesFromUnmodifiedChild = countDifferencesInUint8Arrays(
+ UNMODIFIED_CANVAS_DATA,
+ child
+ );
+
+ Assert.greaterOrEqual(
+ differencesFromUnmodifiedParent,
+ expectedResults[0],
+ `Checking ${testDesc} for canvas randomization, comparing parent - lower bound for random pixels.`
+ );
+ Assert.lessOrEqual(
+ differencesFromUnmodifiedParent,
+ expectedResults[1],
+ `Checking ${testDesc} for canvas randomization, comparing parent - upper bound for random pixels.`
+ );
+
+ Assert.greaterOrEqual(
+ differencesFromUnmodifiedChild,
+ expectedResults[0],
+ `Checking ${testDesc} for canvas randomization, comparing child - lower bound for random pixels.`
+ );
+ Assert.lessOrEqual(
+ differencesFromUnmodifiedChild,
+ expectedResults[1],
+ `Checking ${testDesc} for canvas randomization, comparing child - upper bound for random pixels.`
+ );
+
+ Assert.greaterOrEqual(
+ differencesInRandom,
+ expectedResults[2],
+ `Checking ${testDesc} and comparing randomization - lower bound for different random pixels.`
+ );
+ Assert.lessOrEqual(
+ differencesInRandom,
+ expectedResults[3],
+ `Checking ${testDesc} and comparing randomization - upper bound for different random pixels.`
+ );
+}
+
+requestLongerTimeout(2);
+
+let expectedResults = {};
+var UNMODIFIED_CANVAS_DATA = undefined;
+
+add_setup(async function () {
+ // Disable the fingerprinting randomization.
+ await SpecialPowers.pushPrefEnv({
+ set: [
+ ["privacy.fingerprintingProtection", false],
+ ["privacy.fingerprintingProtection.pbmode", false],
+ ["privacy.resistFingerprinting", false],
+ ],
+ });
+
+ let extractCanvasData = function () {
+ let offscreenCanvas = new OffscreenCanvas(100, 100);
+
+ const context = offscreenCanvas.getContext("2d");
+
+ // Draw a red rectangle
+ context.fillStyle = "#EE2222";
+ context.fillRect(0, 0, 100, 100);
+ context.fillStyle = "#2222EE";
+ context.fillRect(20, 20, 100, 100);
+
+ const imageData = context.getImageData(0, 0, 100, 100);
+ return imageData.data;
+ };
+
+ function runExtractCanvasData(tab) {
+ let code = extractCanvasData.toString();
+ return SpecialPowers.spawn(tab.linkedBrowser, [code], async funccode => {
+ await content.eval(`var extractCanvasData = ${funccode}`);
+ let result = await content.eval(`extractCanvasData()`);
+ return result;
+ });
+ }
+
+ const emptyPage =
+ getRootDirectory(gTestPath).replace(
+ "chrome://mochitests/content",
+ "https://example.com"
+ ) + "empty.html";
+
+ // Open a tab for extracting the canvas data.
+ const tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, emptyPage);
+
+ let data = await runExtractCanvasData(tab);
+ UNMODIFIED_CANVAS_DATA = data;
+
+ BrowserTestUtils.removeTab(tab);
+ await SpecialPowers.popPrefEnv();
+});
+
+// The following are convenience objects that allow you to quickly see what is
+// and is not modified from a logical set of values.
+// Be sure to always use `let expectedResults = structuredClone(allNotSpoofed)` to do a
+// deep copy and avoiding corrupting the original 'const' object
+// The first value represents the minimum number of random pixels we should see
+// The second, the maximum number of random pixels
+// The third, the minimum number of differences between the canvases of the parent and child
+// The fourth, the maximum number of differences between the canvases of the parent and child
+const rfpFullyRandomized = [10000, 999999999, 20000, 999999999];
+const fppRandomizedSameDomain = [1, 260, 0, 0];
+const noRandom = [0, 0, 0, 0];
+
+// As detailed in file_canvascompare_aboutblank_popupmaker.html - there is a difference in code
+// paths for propagating information via LoadInfo when the popup is opened during onLoad vs
+// later in the page. The two modes switch between these situations.
+let uri = `https://${IFRAME_DOMAIN}/browser/browser/components/resistfingerprinting/test/browser/file_canvascompare_aboutblank_popupmaker.html?mode=addOnLoadCallback`;
+
+expectedResults = structuredClone(noRandom);
+add_task(
+ defaultsTest.bind(null, uri, testCanvasRandomization, expectedResults)
+);
+
+expectedResults = structuredClone(fppRandomizedSameDomain);
+add_task(
+ defaultsPBMTest.bind(null, uri, testCanvasRandomization, expectedResults)
+);
+
+expectedResults = structuredClone(rfpFullyRandomized);
+add_task(
+ simpleRFPTest.bind(null, uri, testCanvasRandomization, expectedResults)
+);
+
+// Test a private window with RFP enabled in PBMode
+expectedResults = structuredClone(rfpFullyRandomized);
+add_task(
+ simplePBMRFPTest.bind(null, uri, testCanvasRandomization, expectedResults)
+);
+
+expectedResults = structuredClone(fppRandomizedSameDomain);
+add_task(
+ simpleFPPTest.bind(null, uri, testCanvasRandomization, expectedResults)
+);
+
+// Test a Private Window with FPP Enabled in PBM
+expectedResults = structuredClone(fppRandomizedSameDomain);
+add_task(
+ simplePBMFPPTest.bind(null, uri, testCanvasRandomization, expectedResults)
+);
+
+// Test RFP Enabled in PBM and FPP enabled in Normal Browsing Mode, No Protections
+expectedResults = structuredClone(noRandom);
+add_task(
+ RFPPBMFPP_NormalMode_NoProtectionsTest.bind(
+ null,
+ uri,
+ testCanvasRandomization,
+ expectedResults
+ )
+);
+
+// Test RFP Enabled in PBM and FPP enabled in Normal Browsing Mode, Protections Enabled
+expectedResults = structuredClone(fppRandomizedSameDomain);
+add_task(
+ RFPPBMFPP_NormalMode_ProtectionsTest.bind(
+ null,
+ uri,
+ testCanvasRandomization,
+ expectedResults
+ )
+);
+
+// Technically mode=addOnLoadCallback adds the one relevant callback; so mode=<anything else> will omit the callback and result in the other scenario
+uri = `https://${IFRAME_DOMAIN}/browser/browser/components/resistfingerprinting/test/browser/file_canvascompare_aboutblank_popupmaker.html?mode=skipOnLoadCallback`;
+
+expectedResults = structuredClone(noRandom);
+add_task(
+ defaultsTest.bind(null, uri, testCanvasRandomization, expectedResults)
+);
+
+expectedResults = structuredClone(rfpFullyRandomized);
+add_task(
+ simpleRFPTest.bind(null, uri, testCanvasRandomization, expectedResults)
+);
+
+// Test a private window with RFP enabled in PBMode
+expectedResults = structuredClone(rfpFullyRandomized);
+add_task(
+ simplePBMRFPTest.bind(null, uri, testCanvasRandomization, expectedResults)
+);
+
+expectedResults = structuredClone(fppRandomizedSameDomain);
+add_task(
+ simpleFPPTest.bind(null, uri, testCanvasRandomization, expectedResults)
+);
+
+// Test a Private Window with FPP Enabled in PBM
+expectedResults = structuredClone(fppRandomizedSameDomain);
+add_task(
+ simplePBMFPPTest.bind(null, uri, testCanvasRandomization, expectedResults)
+);
+
+// Test RFP Enabled in PBM and FPP enabled in Normal Browsing Mode, No Protections
+expectedResults = structuredClone(noRandom);
+add_task(
+ RFPPBMFPP_NormalMode_NoProtectionsTest.bind(
+ null,
+ uri,
+ testCanvasRandomization,
+ expectedResults
+ )
+);
+
+// Test RFP Enabled in PBM and FPP enabled in Normal Browsing Mode, Protections Enabled
+expectedResults = structuredClone(fppRandomizedSameDomain);
+add_task(
+ RFPPBMFPP_NormalMode_ProtectionsTest.bind(
+ null,
+ uri,
+ testCanvasRandomization,
+ expectedResults
+ )
+);
diff --git a/browser/components/resistfingerprinting/test/browser/browser_canvascompare_popups_blob.js b/browser/components/resistfingerprinting/test/browser/browser_canvascompare_popups_blob.js
new file mode 100644
index 0000000000..739aaf07b7
--- /dev/null
+++ b/browser/components/resistfingerprinting/test/browser/browser_canvascompare_popups_blob.js
@@ -0,0 +1,208 @@
+/**
+ * This test compares canvas randomization on a parent and a blob popup, and ensures that the canvas randomization key
+ * is inherited correctly. (e.g. that the canvases have the same random value)
+ *
+ * Covers the following cases:
+ * - RFP/FPP is disabled entirely
+ * - RFP is enabled entirely, and only in PBM
+ * - FPP is enabled entirely, and only in PBM
+ * - A normal window when FPP is enabled globally and RFP is enabled in PBM, Protections Enabled and Disabled
+ *
+ */
+
+"use strict";
+
+// =============================================================================================
+
+/**
+ * Compares two Uint8Arrays and returns the number of bits that are different.
+ *
+ * @param {Uint8ClampedArray} arr1 - The first Uint8ClampedArray to compare.
+ * @param {Uint8ClampedArray} arr2 - The second Uint8ClampedArray to compare.
+ * @returns {number} - The number of bits that are different between the two
+ * arrays.
+ */
+function countDifferencesInUint8Arrays(arr1, arr2) {
+ let count = 0;
+ for (let i = 0; i < arr1.length; i++) {
+ let diff = arr1[i] ^ arr2[i];
+ while (diff > 0) {
+ count += diff & 1;
+ diff >>= 1;
+ }
+ }
+ return count;
+}
+
+// =============================================================================================
+
+async function testCanvasRandomization(result, expectedResults, extraData) {
+ let testDesc = extraData.testDesc;
+
+ let parent = result.mine;
+ let child = result.theirs;
+
+ let differencesInRandom = countDifferencesInUint8Arrays(parent, child);
+ let differencesFromUnmodifiedParent = countDifferencesInUint8Arrays(
+ UNMODIFIED_CANVAS_DATA,
+ parent
+ );
+ let differencesFromUnmodifiedChild = countDifferencesInUint8Arrays(
+ UNMODIFIED_CANVAS_DATA,
+ child
+ );
+
+ Assert.greaterOrEqual(
+ differencesFromUnmodifiedParent,
+ expectedResults[0],
+ `Checking ${testDesc} for canvas randomization, comparing parent - lower bound for random pixels.`
+ );
+ Assert.lessOrEqual(
+ differencesFromUnmodifiedParent,
+ expectedResults[1],
+ `Checking ${testDesc} for canvas randomization, comparing parent - upper bound for random pixels.`
+ );
+
+ Assert.greaterOrEqual(
+ differencesFromUnmodifiedChild,
+ expectedResults[0],
+ `Checking ${testDesc} for canvas randomization, comparing child - lower bound for random pixels.`
+ );
+ Assert.lessOrEqual(
+ differencesFromUnmodifiedChild,
+ expectedResults[1],
+ `Checking ${testDesc} for canvas randomization, comparing child - upper bound for random pixels.`
+ );
+
+ Assert.greaterOrEqual(
+ differencesInRandom,
+ expectedResults[2],
+ `Checking ${testDesc} and comparing randomization - lower bound for different random pixels.`
+ );
+ Assert.lessOrEqual(
+ differencesInRandom,
+ expectedResults[3],
+ `Checking ${testDesc} and comparing randomization - upper bound for different random pixels.`
+ );
+}
+
+requestLongerTimeout(2);
+
+let expectedResults = {};
+var UNMODIFIED_CANVAS_DATA = undefined;
+
+add_setup(async function () {
+ // Disable the fingerprinting randomization.
+ await SpecialPowers.pushPrefEnv({
+ set: [
+ ["privacy.fingerprintingProtection", false],
+ ["privacy.fingerprintingProtection.pbmode", false],
+ ["privacy.resistFingerprinting", false],
+ ],
+ });
+
+ let extractCanvasData = function () {
+ let offscreenCanvas = new OffscreenCanvas(100, 100);
+
+ const context = offscreenCanvas.getContext("2d");
+
+ // Draw a red rectangle
+ context.fillStyle = "#EE2222";
+ context.fillRect(0, 0, 100, 100);
+ context.fillStyle = "#2222EE";
+ context.fillRect(20, 20, 100, 100);
+
+ const imageData = context.getImageData(0, 0, 100, 100);
+ return imageData.data;
+ };
+
+ function runExtractCanvasData(tab) {
+ let code = extractCanvasData.toString();
+ return SpecialPowers.spawn(tab.linkedBrowser, [code], async funccode => {
+ await content.eval(`var extractCanvasData = ${funccode}`);
+ let result = await content.eval(`extractCanvasData()`);
+ return result;
+ });
+ }
+
+ const emptyPage =
+ getRootDirectory(gTestPath).replace(
+ "chrome://mochitests/content",
+ "https://example.com"
+ ) + "empty.html";
+
+ // Open a tab for extracting the canvas data.
+ const tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, emptyPage);
+
+ let data = await runExtractCanvasData(tab);
+ UNMODIFIED_CANVAS_DATA = data;
+
+ BrowserTestUtils.removeTab(tab);
+ await SpecialPowers.popPrefEnv();
+});
+
+// Be sure to always use `let expectedResults = structuredClone(allNotSpoofed)` to do a
+// deep copy and avoiding corrupting the original 'const' object
+// The first value represents the minimum number of random pixels we should see
+// The second, the maximum number of random pixels
+// The third, the minimum number of differences between the canvases of the parent and child
+// The fourth, the maximum number of differences between the canvases of the parent and child
+const rfpFullyRandomized = [10000, 999999999, 20000, 999999999];
+const fppRandomizedSameDomain = [1, 260, 0, 0];
+const noRandom = [0, 0, 0, 0];
+
+let uri = `https://${IFRAME_DOMAIN}/browser/browser/components/resistfingerprinting/test/browser/file_canvascompare_blob_popupmaker.html`;
+
+expectedResults = structuredClone(noRandom);
+add_task(
+ defaultsTest.bind(null, uri, testCanvasRandomization, expectedResults)
+);
+
+expectedResults = structuredClone(fppRandomizedSameDomain);
+add_task(
+ defaultsPBMTest.bind(null, uri, testCanvasRandomization, expectedResults)
+);
+
+expectedResults = structuredClone(rfpFullyRandomized);
+add_task(
+ simpleRFPTest.bind(null, uri, testCanvasRandomization, expectedResults)
+);
+
+// Test a private window with RFP enabled in PBMode
+expectedResults = structuredClone(rfpFullyRandomized);
+add_task(
+ simplePBMRFPTest.bind(null, uri, testCanvasRandomization, expectedResults)
+);
+
+expectedResults = structuredClone(fppRandomizedSameDomain);
+add_task(
+ simpleFPPTest.bind(null, uri, testCanvasRandomization, expectedResults)
+);
+
+// Test a Private Window with FPP Enabled in PBM
+expectedResults = structuredClone(fppRandomizedSameDomain);
+add_task(
+ simplePBMFPPTest.bind(null, uri, testCanvasRandomization, expectedResults)
+);
+
+// Test RFP Enabled in PBM and FPP enabled in Normal Browsing Mode, No Protections
+expectedResults = structuredClone(noRandom);
+add_task(
+ RFPPBMFPP_NormalMode_NoProtectionsTest.bind(
+ null,
+ uri,
+ testCanvasRandomization,
+ expectedResults
+ )
+);
+
+// Test RFP Enabled in PBM and FPP enabled in Normal Browsing Mode, Protections Enabled
+expectedResults = structuredClone(fppRandomizedSameDomain);
+add_task(
+ RFPPBMFPP_NormalMode_ProtectionsTest.bind(
+ null,
+ uri,
+ testCanvasRandomization,
+ expectedResults
+ )
+);
diff --git a/browser/components/resistfingerprinting/test/browser/browser_canvascompare_popups_data.js b/browser/components/resistfingerprinting/test/browser/browser_canvascompare_popups_data.js
new file mode 100644
index 0000000000..caca6e0ff9
--- /dev/null
+++ b/browser/components/resistfingerprinting/test/browser/browser_canvascompare_popups_data.js
@@ -0,0 +1,208 @@
+/**
+ * This test compares canvas randomization on a parent and a data popup, and ensures that the canvas randomization key
+ * is inherited correctly. (e.g. that the canvases have the same random value)
+ *
+ * Covers the following cases:
+ * - RFP/FPP is disabled entirely
+ * - RFP is enabled entirely, and only in PBM
+ * - FPP is enabled entirely, and only in PBM
+ * - A normal window when FPP is enabled globally and RFP is enabled in PBM, Protections Enabled and Disabled
+ *
+ */
+
+"use strict";
+
+// =============================================================================================
+
+/**
+ * Compares two Uint8Arrays and returns the number of bits that are different.
+ *
+ * @param {Uint8ClampedArray} arr1 - The first Uint8ClampedArray to compare.
+ * @param {Uint8ClampedArray} arr2 - The second Uint8ClampedArray to compare.
+ * @returns {number} - The number of bits that are different between the two
+ * arrays.
+ */
+function countDifferencesInUint8Arrays(arr1, arr2) {
+ let count = 0;
+ for (let i = 0; i < arr1.length; i++) {
+ let diff = arr1[i] ^ arr2[i];
+ while (diff > 0) {
+ count += diff & 1;
+ diff >>= 1;
+ }
+ }
+ return count;
+}
+
+// =============================================================================================
+
+async function testCanvasRandomization(result, expectedResults, extraData) {
+ let testDesc = extraData.testDesc;
+
+ let parent = result.mine;
+ let child = result.theirs;
+
+ let differencesInRandom = countDifferencesInUint8Arrays(parent, child);
+ let differencesFromUnmodifiedParent = countDifferencesInUint8Arrays(
+ UNMODIFIED_CANVAS_DATA,
+ parent
+ );
+ let differencesFromUnmodifiedChild = countDifferencesInUint8Arrays(
+ UNMODIFIED_CANVAS_DATA,
+ child
+ );
+
+ Assert.greaterOrEqual(
+ differencesFromUnmodifiedParent,
+ expectedResults[0],
+ `Checking ${testDesc} for canvas randomization, comparing parent - lower bound for random pixels.`
+ );
+ Assert.lessOrEqual(
+ differencesFromUnmodifiedParent,
+ expectedResults[1],
+ `Checking ${testDesc} for canvas randomization, comparing parent - upper bound for random pixels.`
+ );
+
+ Assert.greaterOrEqual(
+ differencesFromUnmodifiedChild,
+ expectedResults[0],
+ `Checking ${testDesc} for canvas randomization, comparing child - lower bound for random pixels.`
+ );
+ Assert.lessOrEqual(
+ differencesFromUnmodifiedChild,
+ expectedResults[1],
+ `Checking ${testDesc} for canvas randomization, comparing child - upper bound for random pixels.`
+ );
+
+ Assert.greaterOrEqual(
+ differencesInRandom,
+ expectedResults[2],
+ `Checking ${testDesc} and comparing randomization - lower bound for different random pixels.`
+ );
+ Assert.lessOrEqual(
+ differencesInRandom,
+ expectedResults[3],
+ `Checking ${testDesc} and comparing randomization - upper bound for different random pixels.`
+ );
+}
+
+requestLongerTimeout(2);
+
+let expectedResults = {};
+var UNMODIFIED_CANVAS_DATA = undefined;
+
+add_setup(async function () {
+ // Disable the fingerprinting randomization.
+ await SpecialPowers.pushPrefEnv({
+ set: [
+ ["privacy.fingerprintingProtection", false],
+ ["privacy.fingerprintingProtection.pbmode", false],
+ ["privacy.resistFingerprinting", false],
+ ],
+ });
+
+ let extractCanvasData = function () {
+ let offscreenCanvas = new OffscreenCanvas(100, 100);
+
+ const context = offscreenCanvas.getContext("2d");
+
+ // Draw a red rectangle
+ context.fillStyle = "#EE2222";
+ context.fillRect(0, 0, 100, 100);
+ context.fillStyle = "#2222EE";
+ context.fillRect(20, 20, 100, 100);
+
+ const imageData = context.getImageData(0, 0, 100, 100);
+ return imageData.data;
+ };
+
+ function runExtractCanvasData(tab) {
+ let code = extractCanvasData.toString();
+ return SpecialPowers.spawn(tab.linkedBrowser, [code], async funccode => {
+ await content.eval(`var extractCanvasData = ${funccode}`);
+ let result = await content.eval(`extractCanvasData()`);
+ return result;
+ });
+ }
+
+ const emptyPage =
+ getRootDirectory(gTestPath).replace(
+ "chrome://mochitests/content",
+ "https://example.com"
+ ) + "empty.html";
+
+ // Open a tab for extracting the canvas data.
+ const tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, emptyPage);
+
+ let data = await runExtractCanvasData(tab);
+ UNMODIFIED_CANVAS_DATA = data;
+
+ BrowserTestUtils.removeTab(tab);
+ await SpecialPowers.popPrefEnv();
+});
+
+// Be sure to always use `let expectedResults = structuredClone(allNotSpoofed)` to do a
+// deep copy and avoiding corrupting the original 'const' object
+// The first value represents the minimum number of random pixels we should see
+// The second, the maximum number of random pixels
+// The third, the minimum number of differences between the canvases of the parent and child
+// The fourth, the maximum number of differences between the canvases of the parent and child
+const rfpFullyRandomized = [10000, 999999999, 20000, 999999999];
+const fppRandomizedSameDomain = [1, 260, 0, 0];
+const noRandom = [0, 0, 0, 0];
+
+let uri = `https://${IFRAME_DOMAIN}/browser/browser/components/resistfingerprinting/test/browser/file_canvascompare_data_popupmaker.html`;
+
+expectedResults = structuredClone(noRandom);
+add_task(
+ defaultsTest.bind(null, uri, testCanvasRandomization, expectedResults)
+);
+
+expectedResults = structuredClone(fppRandomizedSameDomain);
+add_task(
+ defaultsPBMTest.bind(null, uri, testCanvasRandomization, expectedResults)
+);
+
+expectedResults = structuredClone(rfpFullyRandomized);
+add_task(
+ simpleRFPTest.bind(null, uri, testCanvasRandomization, expectedResults)
+);
+
+// Test a private window with RFP enabled in PBMode
+expectedResults = structuredClone(rfpFullyRandomized);
+add_task(
+ simplePBMRFPTest.bind(null, uri, testCanvasRandomization, expectedResults)
+);
+
+expectedResults = structuredClone(fppRandomizedSameDomain);
+add_task(
+ simpleFPPTest.bind(null, uri, testCanvasRandomization, expectedResults)
+);
+
+// Test a Private Window with FPP Enabled in PBM
+expectedResults = structuredClone(fppRandomizedSameDomain);
+add_task(
+ simplePBMFPPTest.bind(null, uri, testCanvasRandomization, expectedResults)
+);
+
+// Test RFP Enabled in PBM and FPP enabled in Normal Browsing Mode, No Protections
+expectedResults = structuredClone(noRandom);
+add_task(
+ RFPPBMFPP_NormalMode_NoProtectionsTest.bind(
+ null,
+ uri,
+ testCanvasRandomization,
+ expectedResults
+ )
+);
+
+// Test RFP Enabled in PBM and FPP enabled in Normal Browsing Mode, Protections Enabled
+expectedResults = structuredClone(fppRandomizedSameDomain);
+add_task(
+ RFPPBMFPP_NormalMode_ProtectionsTest.bind(
+ null,
+ uri,
+ testCanvasRandomization,
+ expectedResults
+ )
+);
diff --git a/browser/components/resistfingerprinting/test/browser/browser_hwconcurrency_etp_iframes.js b/browser/components/resistfingerprinting/test/browser/browser_hwconcurrency_etp_iframes.js
index 47914e098e..7c66c511a5 100644
--- a/browser/components/resistfingerprinting/test/browser/browser_hwconcurrency_etp_iframes.js
+++ b/browser/components/resistfingerprinting/test/browser/browser_hwconcurrency_etp_iframes.js
@@ -91,7 +91,7 @@ add_task(
let extraPrefs = [
["privacy.resistFingerprinting.pbmode", true],
["privacy.fingerprintingProtection", true],
- ["privacy.fingerprintingProtection.overrides", "+HardwareConcurrency"],
+ ["privacy.fingerprintingProtection.overrides", "+NavigatorHWConcurrency"],
];
let this_extraData = structuredClone(extraData);
diff --git a/browser/components/resistfingerprinting/test/browser/browser_hwconcurrency_iframes.js b/browser/components/resistfingerprinting/test/browser/browser_hwconcurrency_iframes.js
index 1b89556f61..e9f7175909 100644
--- a/browser/components/resistfingerprinting/test/browser/browser_hwconcurrency_iframes.js
+++ b/browser/components/resistfingerprinting/test/browser/browser_hwconcurrency_iframes.js
@@ -5,7 +5,7 @@
* - RFP is disabled entirely
* - RFP is enabled entirely
* - FPP is enabled entirely
-
+ * - RFP is enabled in PBM, FPP is enabled globally, testing in a Normal Window
*
* - (A) RFP is exempted on the framer and framee and (if needed) on another cross-origin domain
* - (B) RFP is exempted on the framer and framee but is not on another (if needed) cross-origin domain
@@ -60,9 +60,17 @@ add_task(defaultsTest.bind(null, uri, testHWConcurrency, expectedResults));
expectedResults = structuredClone(allSpoofed);
add_task(simpleRFPTest.bind(null, uri, testHWConcurrency, expectedResults));
+// Test a private window with RFP enabled in PBMode
+expectedResults = structuredClone(allSpoofed);
+add_task(simplePBMRFPTest.bind(null, uri, testHWConcurrency, expectedResults));
+
expectedResults = structuredClone(allSpoofed);
add_task(simpleFPPTest.bind(null, uri, testHWConcurrency, expectedResults));
+// Test a Private Window with FPP Enabled in PBM
+expectedResults = structuredClone(allSpoofed);
+add_task(simplePBMFPPTest.bind(null, uri, testHWConcurrency, expectedResults));
+
// (A) RFP is exempted on the framer and framee and (if needed) on another cross-origin domain
expectedResults = structuredClone(allNotSpoofed);
add_task(testA.bind(null, uri, testHWConcurrency, expectedResults));
@@ -95,8 +103,13 @@ add_task(testG.bind(null, uri, testHWConcurrency, expectedResults));
expectedResults = structuredClone(allSpoofed);
add_task(testH.bind(null, uri, testHWConcurrency, expectedResults));
-// Test RFP Enabled in PBM and FPP enabled in Normal Browsing Mode
+// Test a Normal Window with RFP Enabled in PBM and FPP enabled in Normal Browsing Mode - but FPP has no No Protections enabled in it (via .overrides pref)
expectedResults = structuredClone(allNotSpoofed);
add_task(
- simpleRFPPBMFPPTest.bind(null, uri, testHWConcurrency, expectedResults)
+ RFPPBMFPP_NormalMode_NoProtectionsTest.bind(
+ null,
+ uri,
+ testHWConcurrency,
+ expectedResults
+ )
);
diff --git a/browser/components/resistfingerprinting/test/browser/browser_hwconcurrency_iframes_aboutblank.js b/browser/components/resistfingerprinting/test/browser/browser_hwconcurrency_iframes_aboutblank.js
index 1c78cc997f..d48baf3b7d 100644
--- a/browser/components/resistfingerprinting/test/browser/browser_hwconcurrency_iframes_aboutblank.js
+++ b/browser/components/resistfingerprinting/test/browser/browser_hwconcurrency_iframes_aboutblank.js
@@ -60,9 +60,17 @@ add_task(defaultsTest.bind(null, uri, testHWConcurrency, expectedResults));
expectedResults = structuredClone(allSpoofed);
add_task(simpleRFPTest.bind(null, uri, testHWConcurrency, expectedResults));
+// Test a private window with RFP enabled in PBMode
+expectedResults = structuredClone(allSpoofed);
+add_task(simplePBMRFPTest.bind(null, uri, testHWConcurrency, expectedResults));
+
expectedResults = structuredClone(allSpoofed);
add_task(simpleFPPTest.bind(null, uri, testHWConcurrency, expectedResults));
+// Test a Private Window with FPP Enabled in PBM
+expectedResults = structuredClone(allSpoofed);
+add_task(simplePBMFPPTest.bind(null, uri, testHWConcurrency, expectedResults));
+
// (A) RFP is exempted on the framer and framee and (if needed) on another cross-origin domain
expectedResults = structuredClone(allNotSpoofed);
add_task(testA.bind(null, uri, testHWConcurrency, expectedResults));
@@ -98,5 +106,10 @@ add_task(testH.bind(null, uri, testHWConcurrency, expectedResults));
// Test RFP Enabled in PBM and FPP enabled in Normal Browsing Mode
expectedResults = structuredClone(allNotSpoofed);
add_task(
- simpleRFPPBMFPPTest.bind(null, uri, testHWConcurrency, expectedResults)
+ RFPPBMFPP_NormalMode_NoProtectionsTest.bind(
+ null,
+ uri,
+ testHWConcurrency,
+ expectedResults
+ )
);
diff --git a/browser/components/resistfingerprinting/test/browser/browser_hwconcurrency_iframes_aboutsrcdoc.js b/browser/components/resistfingerprinting/test/browser/browser_hwconcurrency_iframes_aboutsrcdoc.js
index 6a650256a5..fcaba5b5c1 100644
--- a/browser/components/resistfingerprinting/test/browser/browser_hwconcurrency_iframes_aboutsrcdoc.js
+++ b/browser/components/resistfingerprinting/test/browser/browser_hwconcurrency_iframes_aboutsrcdoc.js
@@ -60,9 +60,17 @@ add_task(defaultsTest.bind(null, uri, testHWConcurrency, expectedResults));
expectedResults = structuredClone(allSpoofed);
add_task(simpleRFPTest.bind(null, uri, testHWConcurrency, expectedResults));
+// Test a private window with RFP enabled in PBMode
+expectedResults = structuredClone(allSpoofed);
+add_task(simplePBMRFPTest.bind(null, uri, testHWConcurrency, expectedResults));
+
expectedResults = structuredClone(allSpoofed);
add_task(simpleFPPTest.bind(null, uri, testHWConcurrency, expectedResults));
+// Test a Private Window with FPP Enabled in PBM
+expectedResults = structuredClone(allSpoofed);
+add_task(simplePBMFPPTest.bind(null, uri, testHWConcurrency, expectedResults));
+
// (A) RFP is exempted on the framer and framee and (if needed) on another cross-origin domain
expectedResults = structuredClone(allNotSpoofed);
add_task(testA.bind(null, uri, testHWConcurrency, expectedResults));
@@ -98,5 +106,10 @@ add_task(testH.bind(null, uri, testHWConcurrency, expectedResults));
// Test RFP Enabled in PBM and FPP enabled in Normal Browsing Mode
expectedResults = structuredClone(allNotSpoofed);
add_task(
- simpleRFPPBMFPPTest.bind(null, uri, testHWConcurrency, expectedResults)
+ RFPPBMFPP_NormalMode_NoProtectionsTest.bind(
+ null,
+ uri,
+ testHWConcurrency,
+ expectedResults
+ )
);
diff --git a/browser/components/resistfingerprinting/test/browser/browser_hwconcurrency_iframes_blob.js b/browser/components/resistfingerprinting/test/browser/browser_hwconcurrency_iframes_blob.js
index adedb8b96b..9bafe0b43d 100644
--- a/browser/components/resistfingerprinting/test/browser/browser_hwconcurrency_iframes_blob.js
+++ b/browser/components/resistfingerprinting/test/browser/browser_hwconcurrency_iframes_blob.js
@@ -60,9 +60,17 @@ add_task(defaultsTest.bind(null, uri, testHWConcurrency, expectedResults));
expectedResults = structuredClone(allSpoofed);
add_task(simpleRFPTest.bind(null, uri, testHWConcurrency, expectedResults));
+// Test a private window with RFP enabled in PBMode
+expectedResults = structuredClone(allSpoofed);
+add_task(simplePBMRFPTest.bind(null, uri, testHWConcurrency, expectedResults));
+
expectedResults = structuredClone(allSpoofed);
add_task(simpleFPPTest.bind(null, uri, testHWConcurrency, expectedResults));
+// Test a Private Window with FPP Enabled in PBM
+expectedResults = structuredClone(allSpoofed);
+add_task(simplePBMFPPTest.bind(null, uri, testHWConcurrency, expectedResults));
+
// (A) RFP is exempted on the framer and framee and (if needed) on another cross-origin domain
expectedResults = structuredClone(allNotSpoofed);
add_task(testA.bind(null, uri, testHWConcurrency, expectedResults));
@@ -98,5 +106,10 @@ add_task(testH.bind(null, uri, testHWConcurrency, expectedResults));
// Test RFP Enabled in PBM and FPP enabled in Normal Browsing Mode
expectedResults = structuredClone(allNotSpoofed);
add_task(
- simpleRFPPBMFPPTest.bind(null, uri, testHWConcurrency, expectedResults)
+ RFPPBMFPP_NormalMode_NoProtectionsTest.bind(
+ null,
+ uri,
+ testHWConcurrency,
+ expectedResults
+ )
);
diff --git a/browser/components/resistfingerprinting/test/browser/browser_hwconcurrency_iframes_blobcrossorigin.js b/browser/components/resistfingerprinting/test/browser/browser_hwconcurrency_iframes_blobcrossorigin.js
index 1bb7f268a9..be3a55cfb1 100644
--- a/browser/components/resistfingerprinting/test/browser/browser_hwconcurrency_iframes_blobcrossorigin.js
+++ b/browser/components/resistfingerprinting/test/browser/browser_hwconcurrency_iframes_blobcrossorigin.js
@@ -63,9 +63,17 @@ add_task(defaultsTest.bind(null, uri, testHWConcurrency, expectedResults));
expectedResults = structuredClone(allSpoofed);
add_task(simpleRFPTest.bind(null, uri, testHWConcurrency, expectedResults));
+// Test a private window with RFP enabled in PBMode
+expectedResults = structuredClone(allSpoofed);
+add_task(simplePBMRFPTest.bind(null, uri, testHWConcurrency, expectedResults));
+
expectedResults = structuredClone(allSpoofed);
add_task(simpleFPPTest.bind(null, uri, testHWConcurrency, expectedResults));
+// Test a Private Window with FPP Enabled in PBM
+expectedResults = structuredClone(allSpoofed);
+add_task(simplePBMFPPTest.bind(null, uri, testHWConcurrency, expectedResults));
+
// (A) RFP is exempted on the framer and framee and (if needed) on another cross-origin domain
// In theory this should be Not Spoofed, however, in this test there is a blob: document that
// has a content principal and a reference to the iframe's parent (when Fission is disabled anyway.)
@@ -112,5 +120,10 @@ add_task(testH.bind(null, uri, testHWConcurrency, expectedResults));
// Test RFP Enabled in PBM and FPP enabled in Normal Browsing Mode
expectedResults = structuredClone(allNotSpoofed);
add_task(
- simpleRFPPBMFPPTest.bind(null, uri, testHWConcurrency, expectedResults)
+ RFPPBMFPP_NormalMode_NoProtectionsTest.bind(
+ null,
+ uri,
+ testHWConcurrency,
+ expectedResults
+ )
);
diff --git a/browser/components/resistfingerprinting/test/browser/browser_hwconcurrency_iframes_data.js b/browser/components/resistfingerprinting/test/browser/browser_hwconcurrency_iframes_data.js
index 01690bce49..11d4e0ec87 100644
--- a/browser/components/resistfingerprinting/test/browser/browser_hwconcurrency_iframes_data.js
+++ b/browser/components/resistfingerprinting/test/browser/browser_hwconcurrency_iframes_data.js
@@ -60,9 +60,17 @@ add_task(defaultsTest.bind(null, uri, testHWConcurrency, expectedResults));
expectedResults = structuredClone(allSpoofed);
add_task(simpleRFPTest.bind(null, uri, testHWConcurrency, expectedResults));
+// Test a private window with RFP enabled in PBMode
+expectedResults = structuredClone(allSpoofed);
+add_task(simplePBMRFPTest.bind(null, uri, testHWConcurrency, expectedResults));
+
expectedResults = structuredClone(allSpoofed);
add_task(simpleFPPTest.bind(null, uri, testHWConcurrency, expectedResults));
+// Test a Private Window with FPP Enabled in PBM
+expectedResults = structuredClone(allSpoofed);
+add_task(simplePBMFPPTest.bind(null, uri, testHWConcurrency, expectedResults));
+
// (A) RFP is exempted on the framer and framee and (if needed) on another cross-origin domain
expectedResults = structuredClone(allNotSpoofed);
add_task(testA.bind(null, uri, testHWConcurrency, expectedResults));
@@ -98,5 +106,10 @@ add_task(testH.bind(null, uri, testHWConcurrency, expectedResults));
// Test RFP Enabled in PBM and FPP enabled in Normal Browsing Mode
expectedResults = structuredClone(allNotSpoofed);
add_task(
- simpleRFPPBMFPPTest.bind(null, uri, testHWConcurrency, expectedResults)
+ RFPPBMFPP_NormalMode_NoProtectionsTest.bind(
+ null,
+ uri,
+ testHWConcurrency,
+ expectedResults
+ )
);
diff --git a/browser/components/resistfingerprinting/test/browser/browser_hwconcurrency_iframes_sandboxediframe.js b/browser/components/resistfingerprinting/test/browser/browser_hwconcurrency_iframes_sandboxediframe.js
index 05c2b33feb..f783937501 100644
--- a/browser/components/resistfingerprinting/test/browser/browser_hwconcurrency_iframes_sandboxediframe.js
+++ b/browser/components/resistfingerprinting/test/browser/browser_hwconcurrency_iframes_sandboxediframe.js
@@ -60,9 +60,17 @@ add_task(defaultsTest.bind(null, uri, testHWConcurrency, expectedResults));
expectedResults = structuredClone(allSpoofed);
add_task(simpleRFPTest.bind(null, uri, testHWConcurrency, expectedResults));
+// Test a private window with RFP enabled in PBMode
+expectedResults = structuredClone(allSpoofed);
+add_task(simplePBMRFPTest.bind(null, uri, testHWConcurrency, expectedResults));
+
expectedResults = structuredClone(allSpoofed);
add_task(simpleFPPTest.bind(null, uri, testHWConcurrency, expectedResults));
+// Test a Private Window with FPP Enabled in PBM
+expectedResults = structuredClone(allSpoofed);
+add_task(simplePBMFPPTest.bind(null, uri, testHWConcurrency, expectedResults));
+
// (A) RFP is exempted on the framer and framee and (if needed) on another cross-origin domain
expectedResults = structuredClone(allNotSpoofed);
add_task(testA.bind(null, uri, testHWConcurrency, expectedResults));
@@ -98,5 +106,10 @@ add_task(testH.bind(null, uri, testHWConcurrency, expectedResults));
// Test RFP Enabled in PBM and FPP enabled in Normal Browsing Mode
expectedResults = structuredClone(allNotSpoofed);
add_task(
- simpleRFPPBMFPPTest.bind(null, uri, testHWConcurrency, expectedResults)
+ RFPPBMFPP_NormalMode_NoProtectionsTest.bind(
+ null,
+ uri,
+ testHWConcurrency,
+ expectedResults
+ )
);
diff --git a/browser/components/resistfingerprinting/test/browser/browser_hwconcurrency_popups.js b/browser/components/resistfingerprinting/test/browser/browser_hwconcurrency_popups.js
index a9e3591b62..5515da2a7a 100644
--- a/browser/components/resistfingerprinting/test/browser/browser_hwconcurrency_popups.js
+++ b/browser/components/resistfingerprinting/test/browser/browser_hwconcurrency_popups.js
@@ -56,9 +56,17 @@ add_task(defaultsTest.bind(null, uri, testHWConcurrency, expectedResults));
expectedResults = structuredClone(allSpoofed);
add_task(simpleRFPTest.bind(null, uri, testHWConcurrency, expectedResults));
+// Test a private window with RFP enabled in PBMode
+expectedResults = structuredClone(allSpoofed);
+add_task(simplePBMRFPTest.bind(null, uri, testHWConcurrency, expectedResults));
+
expectedResults = structuredClone(allSpoofed);
add_task(simpleFPPTest.bind(null, uri, testHWConcurrency, expectedResults));
+// Test a Private Window with FPP Enabled in PBM
+expectedResults = structuredClone(allSpoofed);
+add_task(simplePBMFPPTest.bind(null, uri, testHWConcurrency, expectedResults));
+
// (A) RFP is exempted on the maker and popup
expectedResults = structuredClone(allNotSpoofed);
add_task(testA.bind(null, uri, testHWConcurrency, expectedResults));
@@ -78,5 +86,10 @@ add_task(testG.bind(null, uri, testHWConcurrency, expectedResults));
// Test RFP Enabled in PBM and FPP enabled in Normal Browsing Mode
expectedResults = structuredClone(allNotSpoofed);
add_task(
- simpleRFPPBMFPPTest.bind(null, uri, testHWConcurrency, expectedResults)
+ RFPPBMFPP_NormalMode_NoProtectionsTest.bind(
+ null,
+ uri,
+ testHWConcurrency,
+ expectedResults
+ )
);
diff --git a/browser/components/resistfingerprinting/test/browser/browser_hwconcurrency_popups_aboutblank.js b/browser/components/resistfingerprinting/test/browser/browser_hwconcurrency_popups_aboutblank.js
index 17f2960b62..7f99d52635 100644
--- a/browser/components/resistfingerprinting/test/browser/browser_hwconcurrency_popups_aboutblank.js
+++ b/browser/components/resistfingerprinting/test/browser/browser_hwconcurrency_popups_aboutblank.js
@@ -55,9 +55,17 @@ add_task(defaultsTest.bind(null, uri, testHWConcurrency, expectedResults));
expectedResults = structuredClone(allSpoofed);
add_task(simpleRFPTest.bind(null, uri, testHWConcurrency, expectedResults));
+// Test a private window with RFP enabled in PBMode
+expectedResults = structuredClone(allSpoofed);
+add_task(simplePBMRFPTest.bind(null, uri, testHWConcurrency, expectedResults));
+
expectedResults = structuredClone(allSpoofed);
add_task(simpleFPPTest.bind(null, uri, testHWConcurrency, expectedResults));
+// Test a Private Window with FPP Enabled in PBM
+expectedResults = structuredClone(allSpoofed);
+add_task(simplePBMFPPTest.bind(null, uri, testHWConcurrency, expectedResults));
+
// (A) RFP is exempted on the popup maker
expectedResults = structuredClone(allNotSpoofed);
add_task(testA.bind(null, uri, testHWConcurrency, expectedResults));
@@ -69,5 +77,10 @@ add_task(testE.bind(null, uri, testHWConcurrency, expectedResults));
// Test RFP Enabled in PBM and FPP enabled in Normal Browsing Mode
expectedResults = structuredClone(allNotSpoofed);
add_task(
- simpleRFPPBMFPPTest.bind(null, uri, testHWConcurrency, expectedResults)
+ RFPPBMFPP_NormalMode_NoProtectionsTest.bind(
+ null,
+ uri,
+ testHWConcurrency,
+ expectedResults
+ )
);
diff --git a/browser/components/resistfingerprinting/test/browser/browser_hwconcurrency_popups_blob.js b/browser/components/resistfingerprinting/test/browser/browser_hwconcurrency_popups_blob.js
index b2f3ebf863..9487df372c 100644
--- a/browser/components/resistfingerprinting/test/browser/browser_hwconcurrency_popups_blob.js
+++ b/browser/components/resistfingerprinting/test/browser/browser_hwconcurrency_popups_blob.js
@@ -55,9 +55,17 @@ add_task(defaultsTest.bind(null, uri, testHWConcurrency, expectedResults));
expectedResults = structuredClone(allSpoofed);
add_task(simpleRFPTest.bind(null, uri, testHWConcurrency, expectedResults));
+// Test a private window with RFP enabled in PBMode
+expectedResults = structuredClone(allSpoofed);
+add_task(simplePBMRFPTest.bind(null, uri, testHWConcurrency, expectedResults));
+
expectedResults = structuredClone(allSpoofed);
add_task(simpleFPPTest.bind(null, uri, testHWConcurrency, expectedResults));
+// Test a Private Window with FPP Enabled in PBM
+expectedResults = structuredClone(allSpoofed);
+add_task(simplePBMFPPTest.bind(null, uri, testHWConcurrency, expectedResults));
+
// (A) RFP is exempted on the popup maker
expectedResults = structuredClone(allNotSpoofed);
add_task(testA.bind(null, uri, testHWConcurrency, expectedResults));
@@ -69,5 +77,10 @@ add_task(testE.bind(null, uri, testHWConcurrency, expectedResults));
// Test RFP Enabled in PBM and FPP enabled in Normal Browsing Mode
expectedResults = structuredClone(allNotSpoofed);
add_task(
- simpleRFPPBMFPPTest.bind(null, uri, testHWConcurrency, expectedResults)
+ RFPPBMFPP_NormalMode_NoProtectionsTest.bind(
+ null,
+ uri,
+ testHWConcurrency,
+ expectedResults
+ )
);
diff --git a/browser/components/resistfingerprinting/test/browser/browser_hwconcurrency_popups_blob_noopener.js b/browser/components/resistfingerprinting/test/browser/browser_hwconcurrency_popups_blob_noopener.js
index 06e4166a4d..a4c5871a22 100644
--- a/browser/components/resistfingerprinting/test/browser/browser_hwconcurrency_popups_blob_noopener.js
+++ b/browser/components/resistfingerprinting/test/browser/browser_hwconcurrency_popups_blob_noopener.js
@@ -65,11 +65,35 @@ add_task(
simpleRFPTest.bind(null, uri, testHWConcurrency, expectedResults, extraData)
);
+// Test a private window with RFP enabled in PBMode
+expectedResults = structuredClone(allSpoofed);
+add_task(
+ simplePBMRFPTest.bind(
+ null,
+ uri,
+ testHWConcurrency,
+ expectedResults,
+ extraData
+ )
+);
+
expectedResults = structuredClone(allSpoofed);
add_task(
simpleFPPTest.bind(null, uri, testHWConcurrency, expectedResults, extraData)
);
+// Test a Private Window with FPP Enabled in PBM
+expectedResults = structuredClone(allSpoofed);
+add_task(
+ simplePBMFPPTest.bind(
+ null,
+ uri,
+ testHWConcurrency,
+ expectedResults,
+ extraData
+ )
+);
+
// (A) RFP is exempted on the popup maker
// Ordinarily, RFP would be exempted, however because the opener relationship is severed
// there is nothing to grant it an exemption, so it is not exempted.
@@ -83,7 +107,7 @@ add_task(testE.bind(null, uri, testHWConcurrency, expectedResults, extraData));
// Test RFP Enabled in PBM and FPP enabled in Normal Browsing Mode
expectedResults = structuredClone(allNotSpoofed);
add_task(
- simpleRFPPBMFPPTest.bind(
+ RFPPBMFPP_NormalMode_NoProtectionsTest.bind(
null,
uri,
testHWConcurrency,
diff --git a/browser/components/resistfingerprinting/test/browser/browser_hwconcurrency_popups_data.js b/browser/components/resistfingerprinting/test/browser/browser_hwconcurrency_popups_data.js
index 7499c55303..1a9353bbf4 100644
--- a/browser/components/resistfingerprinting/test/browser/browser_hwconcurrency_popups_data.js
+++ b/browser/components/resistfingerprinting/test/browser/browser_hwconcurrency_popups_data.js
@@ -55,9 +55,17 @@ add_task(defaultsTest.bind(null, uri, testHWConcurrency, expectedResults));
expectedResults = structuredClone(allSpoofed);
add_task(simpleRFPTest.bind(null, uri, testHWConcurrency, expectedResults));
+// Test a private window with RFP enabled in PBMode
+expectedResults = structuredClone(allSpoofed);
+add_task(simplePBMRFPTest.bind(null, uri, testHWConcurrency, expectedResults));
+
expectedResults = structuredClone(allSpoofed);
add_task(simpleFPPTest.bind(null, uri, testHWConcurrency, expectedResults));
+// Test a Private Window with FPP Enabled in PBM
+expectedResults = structuredClone(allSpoofed);
+add_task(simplePBMFPPTest.bind(null, uri, testHWConcurrency, expectedResults));
+
// (A) RFP is exempted on the popup maker
expectedResults = structuredClone(allNotSpoofed);
add_task(testA.bind(null, uri, testHWConcurrency, expectedResults));
@@ -69,5 +77,10 @@ add_task(testE.bind(null, uri, testHWConcurrency, expectedResults));
// Test RFP Enabled in PBM and FPP enabled in Normal Browsing Mode
expectedResults = structuredClone(allNotSpoofed);
add_task(
- simpleRFPPBMFPPTest.bind(null, uri, testHWConcurrency, expectedResults)
+ RFPPBMFPP_NormalMode_NoProtectionsTest.bind(
+ null,
+ uri,
+ testHWConcurrency,
+ expectedResults
+ )
);
diff --git a/browser/components/resistfingerprinting/test/browser/browser_hwconcurrency_popups_data_noopener.js b/browser/components/resistfingerprinting/test/browser/browser_hwconcurrency_popups_data_noopener.js
index 75f79ba10c..3d90fb61ff 100644
--- a/browser/components/resistfingerprinting/test/browser/browser_hwconcurrency_popups_data_noopener.js
+++ b/browser/components/resistfingerprinting/test/browser/browser_hwconcurrency_popups_data_noopener.js
@@ -65,11 +65,35 @@ add_task(
simpleRFPTest.bind(null, uri, testHWConcurrency, expectedResults, extraData)
);
+// Test a private window with RFP enabled in PBMode
+expectedResults = structuredClone(allSpoofed);
+add_task(
+ simplePBMRFPTest.bind(
+ null,
+ uri,
+ testHWConcurrency,
+ expectedResults,
+ extraData
+ )
+);
+
expectedResults = structuredClone(allSpoofed);
add_task(
simpleFPPTest.bind(null, uri, testHWConcurrency, expectedResults, extraData)
);
+// Test a Private Window with FPP Enabled in PBM
+expectedResults = structuredClone(allSpoofed);
+add_task(
+ simplePBMFPPTest.bind(
+ null,
+ uri,
+ testHWConcurrency,
+ expectedResults,
+ extraData
+ )
+);
+
// (A) RFP is exempted on the popup maker
// Ordinarily, RFP would be exempted, however because the opener relationship is severed
// there is nothing to grant it an exemption, so it is not exempted.
@@ -83,7 +107,7 @@ add_task(testE.bind(null, uri, testHWConcurrency, expectedResults, extraData));
// Test RFP Enabled in PBM and FPP enabled in Normal Browsing Mode
expectedResults = structuredClone(allNotSpoofed);
add_task(
- simpleRFPPBMFPPTest.bind(
+ RFPPBMFPP_NormalMode_NoProtectionsTest.bind(
null,
uri,
testHWConcurrency,
diff --git a/browser/components/resistfingerprinting/test/browser/browser_hwconcurrency_popups_noopener.js b/browser/components/resistfingerprinting/test/browser/browser_hwconcurrency_popups_noopener.js
index 96125c5e20..b9160eb245 100644
--- a/browser/components/resistfingerprinting/test/browser/browser_hwconcurrency_popups_noopener.js
+++ b/browser/components/resistfingerprinting/test/browser/browser_hwconcurrency_popups_noopener.js
@@ -65,11 +65,35 @@ add_task(
simpleRFPTest.bind(null, uri, testHWConcurrency, expectedResults, extraData)
);
+// Test a private window with RFP enabled in PBMode
+expectedResults = structuredClone(allSpoofed);
+add_task(
+ simplePBMRFPTest.bind(
+ null,
+ uri,
+ testHWConcurrency,
+ expectedResults,
+ extraData
+ )
+);
+
expectedResults = structuredClone(allSpoofed);
add_task(
simpleFPPTest.bind(null, uri, testHWConcurrency, expectedResults, extraData)
);
+// Test a Private Window with FPP Enabled in PBM
+expectedResults = structuredClone(allSpoofed);
+add_task(
+ simplePBMFPPTest.bind(
+ null,
+ uri,
+ testHWConcurrency,
+ expectedResults,
+ extraData
+ )
+);
+
// (A) RFP is exempted on the maker and popup
expectedResults = structuredClone(allNotSpoofed);
add_task(testA.bind(null, uri, testHWConcurrency, expectedResults, extraData));
@@ -91,7 +115,7 @@ add_task(testG.bind(null, uri, testHWConcurrency, expectedResults, extraData));
// Test RFP Enabled in PBM and FPP enabled in Normal Browsing Mode
expectedResults = structuredClone(allNotSpoofed);
add_task(
- simpleRFPPBMFPPTest.bind(
+ RFPPBMFPP_NormalMode_NoProtectionsTest.bind(
null,
uri,
testHWConcurrency,
diff --git a/browser/components/resistfingerprinting/test/browser/file_canvas_iframee.html b/browser/components/resistfingerprinting/test/browser/file_canvas_iframee.html
new file mode 100644
index 0000000000..811eb7ee46
--- /dev/null
+++ b/browser/components/resistfingerprinting/test/browser/file_canvas_iframee.html
@@ -0,0 +1,43 @@
+<!DOCTYPE html>
+<meta charset="utf8">
+<script>
+var parent_window;
+let params = new URLSearchParams(document.location.search);
+if (params.get("mode") == "popup") {
+ parent_window = window.opener;
+} else {
+ parent_window = window.parent;
+}
+
+window.onload = async () => {
+ parent_window.postMessage("ready", "*");
+}
+
+window.addEventListener("message", async function listener(event) {
+ if (event.data[0] == "gimme") {
+ let result = give_result();
+ parent_window.postMessage(result, "*")
+ }
+});
+
+function give_result() {
+ const canvas = document.createElement("canvas");
+ canvas.width = 100;
+ canvas.height = 100;
+
+ const context = canvas.getContext("2d");
+
+ context.fillStyle = "#EE2222";
+ context.fillRect(0, 0, 100, 100);
+ context.fillStyle = "#2222EE";
+ context.fillRect(20, 20, 100, 100);
+
+ // Add the canvas element to the document
+ document.body.appendChild(canvas);
+
+ const imageData = context.getImageData(0, 0, 100, 100);
+
+ return imageData.data;
+}
+</script>
+<output id="result"></output>
diff --git a/browser/components/resistfingerprinting/test/browser/file_canvas_iframer.html b/browser/components/resistfingerprinting/test/browser/file_canvas_iframer.html
new file mode 100644
index 0000000000..3c9f2b65a7
--- /dev/null
+++ b/browser/components/resistfingerprinting/test/browser/file_canvas_iframer.html
@@ -0,0 +1,55 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8">
+<title></title>
+<script src="shared_test_funcs.js"></script>
+<script>
+async function runTheTest(iframe_domain, cross_origin_domain) {
+ var child_reference;
+ let url = `https://${iframe_domain}/browser/browser/components/resistfingerprinting/test/browser/file_canvas_iframee.html?mode=`
+ let params = new URLSearchParams(document.location.search);
+
+ if (params.get("mode") == 'iframe') {
+ const iframes = document.querySelectorAll("iframe");
+ iframes[0].src = url + 'iframe';
+ child_reference = iframes[0].contentWindow;
+ } else if (params.get("mode") == "popup") {
+ let options = "";
+ if (params.get("submode") == "noopener") {
+ options = "noopener";
+ }
+ const popup = window.open(url + 'popup', '', options);
+ if (params.get("submode") == "noopener") {
+ return {};
+ }
+ child_reference = popup;
+ } else {
+ throw new Error("Unknown page mode specified");
+ }
+
+ await waitForMessage("ready", `https://${iframe_domain}`);
+
+ const promiseForRFPTest = new Promise(resolve => {
+ window.addEventListener("message", event => {
+ if(event.origin != `https://${iframe_domain}`) {
+ throw new Error(`origin should be ${iframe_domain}`);
+ }
+ resolve(event.data);
+ }, { once: true });
+ });
+ child_reference.postMessage(["gimme", cross_origin_domain], "*");
+ var result = await promiseForRFPTest;
+
+ if (params.get("mode") == "popup") {
+ child_reference.close();
+ }
+
+ return result;
+}
+</script>
+</head>
+<body>
+<iframe width=100></iframe>
+</body>
+</html>
diff --git a/browser/components/resistfingerprinting/test/browser/file_canvascompare_aboutblank_iframee.html b/browser/components/resistfingerprinting/test/browser/file_canvascompare_aboutblank_iframee.html
new file mode 100644
index 0000000000..c123ecc3e9
--- /dev/null
+++ b/browser/components/resistfingerprinting/test/browser/file_canvascompare_aboutblank_iframee.html
@@ -0,0 +1,71 @@
+<!DOCTYPE html>
+<meta charset="utf8">
+<body>
+<output id="result"></output>
+<script>
+window.onload = async () => {
+ parent.postMessage("ready", "*");
+}
+
+window.addEventListener("message", async function listener(event) {
+ if (event.data[0] == "gimme") {
+ var iframe = document.createElement("iframe");
+ iframe.src = "about:blank?foo";
+ document.body.append(iframe);
+
+ function test() {
+ function give_inner_result() {
+ const canvas = document.createElement("canvas");
+ canvas.width = 100;
+ canvas.height = 100;
+
+ const context = canvas.getContext("2d");
+
+ context.fillStyle = "#EE2222";
+ context.fillRect(0, 0, 100, 100);
+ context.fillStyle = "#2222EE";
+ context.fillRect(20, 20, 100, 100);
+
+ // Add the canvas element to the document
+ document.body.appendChild(canvas);
+
+ const imageData = context.getImageData(0, 0, 100, 100);
+
+ return imageData.data;
+ }
+ window.parent.document.querySelector("#result").textContent = JSON.stringify(give_inner_result());
+ }
+
+ iframe.contentWindow.eval(`(${test})()`);
+
+
+ function give_result() {
+ const canvas = document.createElement("canvas");
+ canvas.width = 100;
+ canvas.height = 100;
+
+ const context = canvas.getContext("2d");
+
+ context.fillStyle = "#EE2222";
+ context.fillRect(0, 0, 100, 100);
+ context.fillStyle = "#2222EE";
+ context.fillRect(20, 20, 100, 100);
+
+ // Add the canvas element to the document
+ document.body.appendChild(canvas);
+
+ const imageData = context.getImageData(0, 0, 100, 100);
+
+ return imageData.data;
+ }
+ let myResult = give_result();
+
+ parent.postMessage({mine: myResult, theirs: JSON.parse(document.querySelector("#result").textContent)}, "*")
+
+ // Fun fact - without clearing the text content of the element, the test will hang on shutdown
+ // Guess how many hours it took to figure _that_ out?
+ document.querySelector("#result").textContent = '';
+ }
+});
+</script>
+</body>
diff --git a/browser/components/resistfingerprinting/test/browser/file_canvascompare_aboutblank_iframer.html b/browser/components/resistfingerprinting/test/browser/file_canvascompare_aboutblank_iframer.html
new file mode 100644
index 0000000000..71a27e6098
--- /dev/null
+++ b/browser/components/resistfingerprinting/test/browser/file_canvascompare_aboutblank_iframer.html
@@ -0,0 +1,31 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8">
+<title></title>
+<script src="shared_test_funcs.js"></script>
+<script>
+async function runTheTest(iframe_domain, cross_origin_domain) {
+ const iframes = document.querySelectorAll("iframe");
+ iframes[0].src = `https://${iframe_domain}/browser/browser/components/resistfingerprinting/test/browser/file_canvascompare_aboutblank_iframee.html`;
+ await waitForMessage("ready", `https://${iframe_domain}`);
+
+ const promiseForRFPTest = new Promise(resolve => {
+ window.addEventListener("message", event => {
+ if(event.origin != `https://${iframe_domain}`) {
+ throw new Error(`origin should be ${iframe_domain}`);
+ }
+ resolve(event.data);
+ }, { once: true });
+ });
+ iframes[0].contentWindow.postMessage(["gimme", cross_origin_domain], "*");
+ var result = await promiseForRFPTest;
+
+ return result;
+}
+</script>
+</head>
+<body>
+<iframe width=100></iframe>
+</body>
+</html>
diff --git a/browser/components/resistfingerprinting/test/browser/file_canvascompare_aboutblank_popupmaker.html b/browser/components/resistfingerprinting/test/browser/file_canvascompare_aboutblank_popupmaker.html
new file mode 100644
index 0000000000..74e54ffdf2
--- /dev/null
+++ b/browser/components/resistfingerprinting/test/browser/file_canvascompare_aboutblank_popupmaker.html
@@ -0,0 +1,110 @@
+<!DOCTYPE html>
+<meta charset="utf8">
+<script src="shared_test_funcs.js"></script>
+<script>
+var popup = undefined;
+function createPopup() {
+ if(popup === undefined) {
+ let s = `
+ function give_result() {
+ const canvas = document.createElement("canvas");
+ canvas.width = 100;
+ canvas.height = 100;
+
+ const context = canvas.getContext("2d");
+
+ context.fillStyle = "#EE2222";
+ context.fillRect(0, 0, 100, 100);
+ context.fillStyle = "#2222EE";
+ context.fillRect(20, 20, 100, 100);
+
+ // Add the canvas element to the document
+ document.body.appendChild(canvas);
+
+ const imageData = context.getImageData(0, 0, 100, 100);
+
+ return imageData.data;
+ }
+
+ window.addEventListener('message', async function listener(event) {
+ if (event.data[0] == 'popup_is_ready') {
+ window.opener.postMessage(["popup_ready"], "*");
+ } else if (event.data[0] == 'popup_request') {
+ window.opener.postMessage(['popup_response', give_result()], '*');
+ window.close();
+ }
+ });
+ setInterval(function() {
+ if(!window.opener || window.opener.closed) {
+ window.close();
+ }
+ }, 50);`;
+
+ popup = window.open("about:blank", "");
+ popup.eval(s);
+ }
+}
+
+/*
+ * Believe it or not, when the popup is created alters the code paths for
+ * how the RandomKey is populated on the CJS of the popup. It's a pretty
+ * drastic change, and the two changes in the substative (non-test) patch
+ * of this bug are the two different locations. I'll also note that it took
+ * probably 20 hours or more of work to figure out the LoadInfo ctor one,
+ * so I want to have test coverage of both paths, even if I don't understand
+ * _why_ there are two paths.
+ */
+let params = new URLSearchParams(document.location.search);
+if (params.get("mode") == 'addOnLoadCallback') {
+ window.addEventListener("load", createPopup);
+}
+
+async function runTheTest(iframe_domain, cross_origin_domain) {
+ await new Promise(r => setTimeout(r, 2000));
+
+ if (document.readyState !== 'complete') {
+ createPopup();
+ } else if(popup === undefined) {
+ createPopup();
+ }
+
+ function give_result() {
+ const canvas = document.createElement("canvas");
+ canvas.width = 100;
+ canvas.height = 100;
+
+ const context = canvas.getContext("2d");
+
+ context.fillStyle = "#EE2222";
+ context.fillRect(0, 0, 100, 100);
+ context.fillStyle = "#2222EE";
+ context.fillRect(20, 20, 100, 100);
+
+ // Add the canvas element to the document
+ document.body.appendChild(canvas);
+
+ const imageData = context.getImageData(0, 0, 100, 100);
+
+ return imageData.data;
+ }
+ let myResult = give_result();
+
+ popup.postMessage(["popup_is_ready", cross_origin_domain], "*");
+ await waitForMessage("popup_ready", `*`);
+
+ const promiseForRFPTest = new Promise(resolve => {
+ window.addEventListener("message", event => {
+ resolve({mine: myResult, theirs: event.data[1]});
+ }, { once: true });
+ });
+
+ popup.postMessage(["popup_request", cross_origin_domain], "*");
+ var result = await promiseForRFPTest;
+
+ popup.close();
+
+ return result;
+}
+
+</script>
+<output id="result"></output>
diff --git a/browser/components/resistfingerprinting/test/browser/file_canvascompare_blob_iframee.html b/browser/components/resistfingerprinting/test/browser/file_canvascompare_blob_iframee.html
new file mode 100644
index 0000000000..84e785777e
--- /dev/null
+++ b/browser/components/resistfingerprinting/test/browser/file_canvascompare_blob_iframee.html
@@ -0,0 +1,75 @@
+<!DOCTYPE html>
+<meta charset="utf8">
+<script type="text/javascript">
+window.onload = async () => {
+ parent.postMessage("ready", "*");
+}
+
+function give_result() {
+ const canvas = document.createElement("canvas");
+ canvas.width = 100;
+ canvas.height = 100;
+
+ const context = canvas.getContext("2d");
+
+ context.fillStyle = "#EE2222";
+ context.fillRect(0, 0, 100, 100);
+ context.fillStyle = "#2222EE";
+ context.fillRect(20, 20, 100, 100);
+
+ // Add the canvas element to the document
+ document.body.appendChild(canvas);
+
+ const imageData = context.getImageData(0, 0, 100, 100);
+
+ return imageData.data;
+}
+
+window.addEventListener("message", async function listener(event) {
+//window.addEventListener("load", async function listener(event) {
+ if (event.data[0] == "gimme") {
+ // eslint-disable-next-line
+ var s = `<html><body><script>
+ function give_result() {
+ const canvas = document.createElement("canvas");
+ canvas.width = 100;
+ canvas.height = 100;
+
+ const context = canvas.getContext("2d");
+
+ context.fillStyle = "#EE2222";
+ context.fillRect(0, 0, 100, 100);
+ context.fillStyle = "#2222EE";
+ context.fillRect(20, 20, 100, 100);
+
+ // Add the canvas element to the document
+ document.body.appendChild(canvas);
+
+ const imageData = context.getImageData(0, 0, 100, 100);
+
+ return imageData.data;
+ }
+ window.parent.document.querySelector('#result').textContent = JSON.stringify(give_result());
+ window.parent.postMessage(["frame_response"], "*");`;
+ // eslint-disable-next-line
+ s += `</` + `script></body></html>`;
+
+ let b = new Blob([s], { type: "text/html" });
+ let url = URL.createObjectURL(b);
+
+ var iframe = document.createElement("iframe");
+ iframe.src = url;
+ document.body.append(iframe);
+ } else if (event.data[0] == "frame_response") {
+ let myResult = give_result();
+ console.log("myResult", myResult)
+
+ let result = JSON.parse(document.querySelector("#result").textContent);
+ console.log("theirResult", result)
+ parent.postMessage({mine: myResult, theirs: result}, "*")
+ }
+});
+</script>
+<body>
+<output id="result" style="display:none"></output>
+</body>
diff --git a/browser/components/resistfingerprinting/test/browser/file_canvascompare_blob_iframer.html b/browser/components/resistfingerprinting/test/browser/file_canvascompare_blob_iframer.html
new file mode 100644
index 0000000000..ac64101600
--- /dev/null
+++ b/browser/components/resistfingerprinting/test/browser/file_canvascompare_blob_iframer.html
@@ -0,0 +1,31 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8">
+<title></title>
+<script src="shared_test_funcs.js"></script>
+<script>
+async function runTheTest(iframe_domain, cross_origin_domain) {
+ const iframes = document.querySelectorAll("iframe");
+ iframes[0].src = `https://${iframe_domain}/browser/browser/components/resistfingerprinting/test/browser/file_canvascompare_blob_iframee.html`;
+ await waitForMessage("ready", `https://${iframe_domain}`);
+
+ const promiseForRFPTest = new Promise(resolve => {
+ window.addEventListener("message", event => {
+ if(event.origin != `https://${iframe_domain}`) {
+ throw new Error(`origin should be ${iframe_domain}`);
+ }
+ resolve(event.data);
+ }, { once: true });
+ });
+ iframes[0].contentWindow.postMessage(["gimme", cross_origin_domain], "*");
+ var result = await promiseForRFPTest;
+
+ return result;
+}
+</script>
+</head>
+<body>
+<iframe width=100></iframe>
+</body>
+</html>
diff --git a/browser/components/resistfingerprinting/test/browser/file_canvascompare_blob_popupmaker.html b/browser/components/resistfingerprinting/test/browser/file_canvascompare_blob_popupmaker.html
new file mode 100644
index 0000000000..454ecb0a7f
--- /dev/null
+++ b/browser/components/resistfingerprinting/test/browser/file_canvascompare_blob_popupmaker.html
@@ -0,0 +1,92 @@
+<!DOCTYPE html>
+<meta charset="utf8">
+<script src="shared_test_funcs.js"></script>
+<script type="text/javascript">
+var popup;
+async function runTheTest(iframe_domain, cross_origin_domain) {
+ let s = `<html><script>
+ function give_result() {
+ const canvas = document.createElement("canvas");
+ canvas.width = 100;
+ canvas.height = 100;
+
+ const context = canvas.getContext("2d");
+
+ context.fillStyle = "#EE2222";
+ context.fillRect(0, 0, 100, 100);
+ context.fillStyle = "#2222EE";
+ context.fillRect(20, 20, 100, 100);
+
+ // Add the canvas element to the document
+ document.body.appendChild(canvas);
+
+ const imageData = context.getImageData(0, 0, 100, 100);
+
+ return imageData.data;
+ }
+ window.addEventListener('load', async function listener(event) {
+ window.opener.postMessage(["popup_ready"], "*");
+ });
+ window.addEventListener('message', async function listener(event) {
+ if (event.data[0] == 'popup_request') {
+ window.opener.postMessage(['popup_response', give_result()], '*');
+ window.close();
+ }
+ });`;
+ // eslint-disable-next-line
+ s += `</` + `script></html>`;
+
+ let params = new URLSearchParams(document.location.search);
+ let options = "";
+ if (params.get("submode") == "noopener") {
+ options = "noopener";
+ }
+
+ let b = new Blob([s], { type: "text/html" });
+ let url = URL.createObjectURL(b);
+ popup = window.open(url, "", options);
+
+ if (params.get("submode") == "noopener") {
+ return {};
+ }
+
+ await waitForMessage("popup_ready", `*`);
+
+ function give_result() {
+ const canvas = document.createElement("canvas");
+ canvas.width = 100;
+ canvas.height = 100;
+
+ const context = canvas.getContext("2d");
+
+ context.fillStyle = "#EE2222";
+ context.fillRect(0, 0, 100, 100);
+ context.fillStyle = "#2222EE";
+ context.fillRect(20, 20, 100, 100);
+
+ // Add the canvas element to the document
+ document.body.appendChild(canvas);
+
+ const imageData = context.getImageData(0, 0, 100, 100);
+
+ return imageData.data;
+ }
+ let myResult = give_result();
+
+ const promiseForRFPTest = new Promise(resolve => {
+ window.addEventListener("message", event => {
+ resolve({mine: myResult, theirs: event.data[1]});
+ }, { once: true });
+ });
+
+ popup.postMessage(["popup_request", cross_origin_domain], "*");
+ var result = await promiseForRFPTest;
+
+ popup.close();
+
+ return result;
+}
+</script>
+<body>
+<output id="result"></output>
+</body>
diff --git a/browser/components/resistfingerprinting/test/browser/file_canvascompare_data_iframee.html b/browser/components/resistfingerprinting/test/browser/file_canvascompare_data_iframee.html
new file mode 100644
index 0000000000..856dc8b33d
--- /dev/null
+++ b/browser/components/resistfingerprinting/test/browser/file_canvascompare_data_iframee.html
@@ -0,0 +1,76 @@
+<!DOCTYPE html>
+<meta charset="utf8">
+<script type="text/javascript">
+window.onload = async () => {
+ parent.postMessage("ready", "*");
+}
+
+window.addEventListener("message", async function listener(event) {
+ if (event.data[0] == "gimme") {
+ var s = `<html><script>
+ function give_result() {
+ const canvas = document.createElement("canvas");
+ canvas.width = 100;
+ canvas.height = 100;
+
+ const context = canvas.getContext("2d");
+
+ context.fillStyle = "#EE2222";
+ context.fillRect(0, 0, 100, 100);
+ context.fillStyle = "#2222EE";
+ context.fillRect(20, 20, 100, 100);
+
+ // Add the canvas element to the document
+ document.body.appendChild(canvas);
+
+ const imageData = context.getImageData(0, 0, 100, 100);
+
+ return imageData.data;
+ }
+ window.addEventListener("load", async function listener(event) {
+ parent.postMessage(["frame_ready"], "*");
+ });
+ window.addEventListener('message', async function listener(event) {
+ if (event.data[0] == 'frame_request') {
+
+ parent.postMessage(['frame_response', give_result()], '*');
+ }
+ });`;
+ // eslint-disable-next-line
+ s += `</` + `script></html>`;
+
+ let iframe = document.createElement("iframe");
+ iframe.src = "data:text/html;base64," + btoa(s);
+ document.body.append(iframe);
+ } else if (event.data[0] == "frame_ready") {
+ let iframe = document.getElementsByTagName("iframe")[0];
+ iframe.contentWindow.postMessage(["frame_request"], "*");
+ } else if (event.data[0] == "frame_response") {
+ function give_result() {
+ const canvas = document.createElement("canvas");
+ canvas.width = 100;
+ canvas.height = 100;
+
+ const context = canvas.getContext("2d");
+
+ context.fillStyle = "#EE2222";
+ context.fillRect(0, 0, 100, 100);
+ context.fillStyle = "#2222EE";
+ context.fillRect(20, 20, 100, 100);
+
+ // Add the canvas element to the document
+ document.body.appendChild(canvas);
+
+ const imageData = context.getImageData(0, 0, 100, 100);
+
+ return imageData.data;
+ }
+ let myResult = give_result();
+
+ parent.postMessage({mine: myResult, theirs: event.data[1]}, "*")
+ }
+});
+</script>
+<body>
+<output id="result"></output>
+</body>
diff --git a/browser/components/resistfingerprinting/test/browser/file_canvascompare_data_iframer.html b/browser/components/resistfingerprinting/test/browser/file_canvascompare_data_iframer.html
new file mode 100644
index 0000000000..c62e5367cb
--- /dev/null
+++ b/browser/components/resistfingerprinting/test/browser/file_canvascompare_data_iframer.html
@@ -0,0 +1,31 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8">
+<title></title>
+<script src="shared_test_funcs.js"></script>
+<script>
+async function runTheTest(iframe_domain, cross_origin_domain) {
+ const iframes = document.querySelectorAll("iframe");
+ iframes[0].src = `https://${iframe_domain}/browser/browser/components/resistfingerprinting/test/browser/file_canvascompare_data_iframee.html`;
+ await waitForMessage("ready", `https://${iframe_domain}`);
+
+ const promiseForRFPTest = new Promise(resolve => {
+ window.addEventListener("message", event => {
+ if(event.origin != `https://${iframe_domain}`) {
+ throw new Error(`origin should be ${iframe_domain}`);
+ }
+ resolve(event.data);
+ }, { once: true });
+ });
+ iframes[0].contentWindow.postMessage(["gimme", cross_origin_domain], "*");
+ var result = await promiseForRFPTest;
+
+ return result;
+}
+</script>
+</head>
+<body>
+<iframe width=100></iframe>
+</body>
+</html>
diff --git a/browser/components/resistfingerprinting/test/browser/file_canvascompare_data_popupmaker.html b/browser/components/resistfingerprinting/test/browser/file_canvascompare_data_popupmaker.html
new file mode 100644
index 0000000000..b9cefeb197
--- /dev/null
+++ b/browser/components/resistfingerprinting/test/browser/file_canvascompare_data_popupmaker.html
@@ -0,0 +1,91 @@
+<!DOCTYPE html>
+<meta charset="utf8">
+<script src="shared_test_funcs.js"></script>
+<script type="text/javascript">
+var popup;
+async function runTheTest(iframe_domain, cross_origin_domain) {
+ let s = `<!DOCTYPE html><html><script>
+ function give_result() {
+ const canvas = document.createElement("canvas");
+ canvas.width = 100;
+ canvas.height = 100;
+
+ const context = canvas.getContext("2d");
+
+ context.fillStyle = "#EE2222";
+ context.fillRect(0, 0, 100, 100);
+ context.fillStyle = "#2222EE";
+ context.fillRect(20, 20, 100, 100);
+
+ // Add the canvas element to the document
+ document.body.appendChild(canvas);
+
+ const imageData = context.getImageData(0, 0, 100, 100);
+
+ return imageData.data;
+ }
+ window.addEventListener('load', async function listener(event) {
+ window.opener.postMessage(["popup_ready"], "*");
+ });
+ window.addEventListener('message', async function listener(event) {
+ if (event.data[0] == 'popup_request') {
+ window.opener.postMessage(['popup_response', give_result()], '*');
+ window.close();
+ }
+ });`;
+ // eslint-disable-next-line
+ s += `</` + `script></html>`;
+
+ let params = new URLSearchParams(document.location.search);
+ let options = "";
+ if (params.get("submode") == "noopener") {
+ options = "noopener";
+ }
+
+ let url = "data:text/html;base64," + btoa(s);
+ popup = window.open(url, "", options);
+
+ if (params.get("submode") == "noopener") {
+ return {};
+ }
+
+ await waitForMessage("popup_ready", `*`);
+
+ function give_result() {
+ const canvas = document.createElement("canvas");
+ canvas.width = 100;
+ canvas.height = 100;
+
+ const context = canvas.getContext("2d");
+
+ context.fillStyle = "#EE2222";
+ context.fillRect(0, 0, 100, 100);
+ context.fillStyle = "#2222EE";
+ context.fillRect(20, 20, 100, 100);
+
+ // Add the canvas element to the document
+ document.body.appendChild(canvas);
+
+ const imageData = context.getImageData(0, 0, 100, 100);
+
+ return imageData.data;
+ }
+ let myResult = give_result();
+
+ const promiseForRFPTest = new Promise(resolve => {
+ window.addEventListener("message", event => {
+ resolve({mine: myResult, theirs: event.data[1]});
+ }, { once: true });
+ });
+
+ popup.postMessage(["popup_request", cross_origin_domain], "*");
+ var result = await promiseForRFPTest;
+
+ popup.close();
+
+ return result;
+}
+</script>
+<body>
+<output id="result"></output>
+</body>
diff --git a/browser/components/resistfingerprinting/test/browser/file_canvascompare_iframee.html b/browser/components/resistfingerprinting/test/browser/file_canvascompare_iframee.html
new file mode 100644
index 0000000000..811eb7ee46
--- /dev/null
+++ b/browser/components/resistfingerprinting/test/browser/file_canvascompare_iframee.html
@@ -0,0 +1,43 @@
+<!DOCTYPE html>
+<meta charset="utf8">
+<script>
+var parent_window;
+let params = new URLSearchParams(document.location.search);
+if (params.get("mode") == "popup") {
+ parent_window = window.opener;
+} else {
+ parent_window = window.parent;
+}
+
+window.onload = async () => {
+ parent_window.postMessage("ready", "*");
+}
+
+window.addEventListener("message", async function listener(event) {
+ if (event.data[0] == "gimme") {
+ let result = give_result();
+ parent_window.postMessage(result, "*")
+ }
+});
+
+function give_result() {
+ const canvas = document.createElement("canvas");
+ canvas.width = 100;
+ canvas.height = 100;
+
+ const context = canvas.getContext("2d");
+
+ context.fillStyle = "#EE2222";
+ context.fillRect(0, 0, 100, 100);
+ context.fillStyle = "#2222EE";
+ context.fillRect(20, 20, 100, 100);
+
+ // Add the canvas element to the document
+ document.body.appendChild(canvas);
+
+ const imageData = context.getImageData(0, 0, 100, 100);
+
+ return imageData.data;
+}
+</script>
+<output id="result"></output>
diff --git a/browser/components/resistfingerprinting/test/browser/file_canvascompare_iframer.html b/browser/components/resistfingerprinting/test/browser/file_canvascompare_iframer.html
new file mode 100644
index 0000000000..e164ad21be
--- /dev/null
+++ b/browser/components/resistfingerprinting/test/browser/file_canvascompare_iframer.html
@@ -0,0 +1,77 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8">
+<title></title>
+<script src="shared_test_funcs.js"></script>
+<script>
+async function runTheTest(iframe_domain, cross_origin_domain) {
+ var child_reference;
+ let url = `https://${iframe_domain}/browser/browser/components/resistfingerprinting/test/browser/file_canvascompare_iframee.html?mode=`
+ let params = new URLSearchParams(document.location.search);
+
+ if (params.get("mode") == 'iframe') {
+ const iframes = document.querySelectorAll("iframe");
+ iframes[0].src = url + 'iframe';
+ child_reference = iframes[0].contentWindow;
+ } else if (params.get("mode") == "popup") {
+ let options = "";
+ if (params.get("submode") == "noopener") {
+ options = "noopener";
+ }
+ const popup = window.open(url + 'popup', '', options);
+ if (params.get("submode") == "noopener") {
+ return {};
+ }
+ child_reference = popup;
+ } else {
+ throw new Error("Unknown page mode specified");
+ }
+
+ function give_result() {
+ const canvas = document.createElement("canvas");
+ canvas.width = 100;
+ canvas.height = 100;
+
+ const context = canvas.getContext("2d");
+
+ context.fillStyle = "#EE2222";
+ context.fillRect(0, 0, 100, 100);
+ context.fillStyle = "#2222EE";
+ context.fillRect(20, 20, 100, 100);
+
+ // Add the canvas element to the document
+ document.body.appendChild(canvas);
+
+ const imageData = context.getImageData(0, 0, 100, 100);
+
+ return imageData.data;
+ }
+ let myResult = give_result();
+
+ await waitForMessage("ready", `https://${iframe_domain}`);
+
+ const promiseForRFPTest = new Promise(resolve => {
+ window.addEventListener("message", event => {
+ if(event.origin != `https://${iframe_domain}`) {
+ throw new Error(`origin should be ${iframe_domain}`);
+ }
+
+ resolve({mine: myResult, theirs: event.data});
+ }, { once: true });
+ });
+ child_reference.postMessage(["gimme", cross_origin_domain], "*");
+ var result = await promiseForRFPTest;
+
+ if (params.get("mode") == "popup") {
+ child_reference.close();
+ }
+
+ return result;
+}
+</script>
+</head>
+<body>
+<iframe width=100></iframe>
+</body>
+</html>
diff --git a/browser/components/resistfingerprinting/test/browser/file_hwconcurrency_data_popupmaker.html b/browser/components/resistfingerprinting/test/browser/file_hwconcurrency_data_popupmaker.html
index 75ae15313b..26e9656398 100644
--- a/browser/components/resistfingerprinting/test/browser/file_hwconcurrency_data_popupmaker.html
+++ b/browser/components/resistfingerprinting/test/browser/file_hwconcurrency_data_popupmaker.html
@@ -14,9 +14,7 @@ async function runTheTest(iframe_domain, cross_origin_domain) {
window.opener.postMessage(["popup_ready"], "*");
});
window.addEventListener('message', async function listener(event) {
- if (event.data[0] == 'popup_is_ready') {
- window.opener.postMessage(["popup_ready"], "*");
- } else if (event.data[0] == 'popup_request') {
+ if (event.data[0] == 'popup_request') {
let result = give_result();
window.opener.postMessage(['popup_response', result], '*');
}
diff --git a/browser/components/resistfingerprinting/test/browser/head.js b/browser/components/resistfingerprinting/test/browser/head.js
index 8973839220..18a96994c7 100644
--- a/browser/components/resistfingerprinting/test/browser/head.js
+++ b/browser/components/resistfingerprinting/test/browser/head.js
@@ -753,6 +753,30 @@ async function defaultsTest(
}
}
+async function defaultsPBMTest(
+ uri,
+ testFunction,
+ expectedResults,
+ extraData,
+ extraPrefs
+) {
+ if (extraData == undefined) {
+ extraData = {};
+ }
+ extraData.private_window = true;
+ extraData.testDesc = extraData.testDesc || "default PBM window";
+ expectedResults.shouldRFPApply = false;
+ if (extraPrefs != undefined) {
+ await SpecialPowers.pushPrefEnv({
+ set: extraPrefs,
+ });
+ }
+ await runActualTest(uri, testFunction, expectedResults, extraData);
+ if (extraPrefs != undefined) {
+ await SpecialPowers.popPrefEnv();
+ }
+}
+
async function simpleRFPTest(
uri,
testFunction,
@@ -813,7 +837,10 @@ async function simpleFPPTest(
await SpecialPowers.pushPrefEnv({
set: [
["privacy.fingerprintingProtection", true],
- ["privacy.fingerprintingProtection.overrides", "+NavigatorHWConcurrency"],
+ [
+ "privacy.fingerprintingProtection.overrides",
+ "+NavigatorHWConcurrency,+CanvasRandomization",
+ ],
].concat(extraPrefs || []),
});
@@ -838,7 +865,42 @@ async function simplePBMFPPTest(
await SpecialPowers.pushPrefEnv({
set: [
["privacy.fingerprintingProtection.pbmode", true],
- ["privacy.fingerprintingProtection.overrides", "+HardwareConcurrency"],
+ [
+ "privacy.fingerprintingProtection.overrides",
+ "+NavigatorHWConcurrency,+CanvasRandomization",
+ ],
+ ].concat(extraPrefs || []),
+ });
+
+ await runActualTest(uri, testFunction, expectedResults, extraData);
+
+ await SpecialPowers.popPrefEnv();
+}
+
+async function RFPPBMFPP_NormalMode_NoProtectionsTest(
+ uri,
+ testFunction,
+ expectedResults,
+ extraData,
+ extraPrefs
+) {
+ if (extraData == undefined) {
+ extraData = {};
+ }
+ extraData.private_window = false;
+ extraData.testDesc =
+ extraData.testDesc ||
+ "RFP Enabled in PBM and FPP enabled in Normal Browsing Mode, Protections Disabled";
+ expectedResults.shouldRFPApply = false;
+ await SpecialPowers.pushPrefEnv({
+ set: [
+ ["privacy.resistFingerprinting", false],
+ ["privacy.resistFingerprinting.pbmode", true],
+ ["privacy.fingerprintingProtection", true],
+ [
+ "privacy.fingerprintingProtection.overrides",
+ "-NavigatorHWConcurrency,-CanvasRandomization",
+ ],
].concat(extraPrefs || []),
});
@@ -847,7 +909,7 @@ async function simplePBMFPPTest(
await SpecialPowers.popPrefEnv();
}
-async function simpleRFPPBMFPPTest(
+async function RFPPBMFPP_NormalMode_ProtectionsTest(
uri,
testFunction,
expectedResults,
@@ -860,14 +922,17 @@ async function simpleRFPPBMFPPTest(
extraData.private_window = false;
extraData.testDesc =
extraData.testDesc ||
- "RFP Enabled in PBM and FPP enabled in Normal Browsing Mode";
+ "RFP Enabled in PBM and FPP enabled in Normal Browsing Mode, Protections Enabled";
expectedResults.shouldRFPApply = false;
await SpecialPowers.pushPrefEnv({
set: [
["privacy.resistFingerprinting", false],
["privacy.resistFingerprinting.pbmode", true],
["privacy.fingerprintingProtection", true],
- ["privacy.fingerprintingProtection.overrides", "-HardwareConcurrency"],
+ [
+ "privacy.fingerprintingProtection.overrides",
+ "+NavigatorHWConcurrency,+CanvasRandomization",
+ ],
].concat(extraPrefs || []),
});