tor-browser

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

commit d4cf73ac9411060a035affe325c5761057bba570
parent 992c24e4cf666613a8853213d2a36dcf1feaf35d
Author: Tom Ritter <tom@mozilla.com>
Date:   Fri,  3 Oct 2025 16:03:18 +0000

Bug 1980264: Add a test r=timhuang

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

Diffstat:
Mbrowser/components/resistfingerprinting/test/browser/browser.toml | 4++++
Abrowser/components/resistfingerprinting/test/browser/browser_efficientcanvascompare_iframes.js | 247+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Abrowser/components/resistfingerprinting/test/browser/file_efficientcanvascompare_iframee.html | 41+++++++++++++++++++++++++++++++++++++++++
Abrowser/components/resistfingerprinting/test/browser/file_efficientcanvascompare_iframer.html | 75+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
4 files changed, 367 insertions(+), 0 deletions(-)

diff --git a/browser/components/resistfingerprinting/test/browser/browser.toml b/browser/components/resistfingerprinting/test/browser/browser.toml @@ -11,6 +11,8 @@ support-files = [ "file_workerNetInfo.js", "file_workerPerformance.js", "head.js", + "file_efficientcanvascompare_iframer.html", + "file_efficientcanvascompare_iframee.html", "file_canvascompare_aboutblank_iframee.html", "file_canvascompare_aboutblank_iframer.html", "file_canvascompare_aboutblank_popupmaker.html", @@ -97,6 +99,8 @@ skip-if = [ "os == 'mac' && os_version == '15.30' && arch == 'aarch64' && opt", # Bug 1775698 ] +["browser_efficientcanvascompare_iframes.js"] + ["browser_exslt_time_precision.js"] ["browser_exslt_timezone_load.js"] diff --git a/browser/components/resistfingerprinting/test/browser/browser_efficientcanvascompare_iframes.js b/browser/components/resistfingerprinting/test/browser/browser_efficientcanvascompare_iframes.js @@ -0,0 +1,247 @@ +/** + * 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"; + +// ============================================================================================= + +async function testCanvasRandomization(result, expectedResults, extraData) { + let testDesc = extraData.testDesc; + + let parent = result.mine; + let child = result.theirs; + let unmodified = UNMODIFIED_CANVAS_DATA; + + if (expectedResults.shouldBeRandom) { + if (expectedResults.shouldRFPApply) { + Assert.notEqual( + parent, + child, + `Checking ${testDesc} for RFP canvas randomization parent != child` + + ` is ${parent != child}` + ); + Assert.notEqual( + parent, + unmodified, + `Checking ${testDesc} for RFP canvas randomization, parent != unmodified` + + ` is ${parent != unmodified}` + ); + } else { + Assert.equal( + parent, + child, + `Checking ${testDesc} for canvas randomization parent == child` + + ` is ${parent == child}` + ); + Assert.notEqual( + parent, + unmodified, + `Checking ${testDesc} for canvas randomization, parent != unmodified` + + ` is ${parent != unmodified}` + ); + } + } else { + Assert.equal( + parent, + child, + `Checking ${testDesc} for no canvas randomization, parent == child` + + ` is ${parent == child}` + ); + Assert.equal( + parent, + unmodified, + `Checking ${testDesc} for no canvas randomization, parent == unmodified` + + ` is ${parent == unmodified}` + ); + } +} + +requestLongerTimeout(2); + +var UNMODIFIED_CANVAS_DATA = undefined; + +add_setup(async function () { + // Disable the fingerprinting randomization. + await SpecialPowers.pushPrefEnv({ + set: [ + [ + "privacy.fingerprintingProtection.overrides", + "+EfficientCanvasRandomization,-CanvasRandomization", + ], + ], + }); + + await SpecialPowers.pushPrefEnv({ + set: [ + ["privacy.fingerprintingProtection", false], + ["privacy.fingerprintingProtection.pbmode", false], + ["privacy.resistFingerprinting", false], + ], + }); + + function runExtractCanvasData(tab) { + return SpecialPowers.spawn(tab.linkedBrowser, [], async () => { + const canvas = content.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 + content.document.body.appendChild(canvas); + + let url = canvas.toDataURL(); + return url; + }); + } + + 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(); +}); + +// Keep the test simpler, we do pixel tests in other tests. + +// 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_efficientcanvascompare_iframer.html?mode=iframe`; + +let shouldBeRandom = { + shouldBeRandom: true, +}; +let noRandom = { + shouldBeRandom: false, +}; +let expectedResults = undefined; + +expectedResults = structuredClone(noRandom); +add_task( + defaultsTest.bind(null, uri, testCanvasRandomization, expectedResults) +); + +expectedResults = structuredClone(shouldBeRandom); +add_task( + defaultsPBMTest.bind(null, uri, testCanvasRandomization, expectedResults) +); + +expectedResults = structuredClone(shouldBeRandom); +add_task( + simpleRFPTest.bind(null, uri, testCanvasRandomization, expectedResults) +); + +// Test a private window with RFP enabled in PBMode +expectedResults = structuredClone(shouldBeRandom); +add_task( + simplePBMRFPTest.bind(null, uri, testCanvasRandomization, expectedResults) +); + +expectedResults = structuredClone(shouldBeRandom); +add_task( + simpleFPPTest.bind(null, uri, testCanvasRandomization, expectedResults) +); + +// Test a Private Window with FPP Enabled in PBM +expectedResults = structuredClone(shouldBeRandom); +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(shouldBeRandom); +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_efficientcanvascompare_iframer.html?mode=iframe`; + +expectedResults = structuredClone(noRandom); +add_task( + defaultsTest.bind(null, uri, testCanvasRandomization, expectedResults) +); + +expectedResults = structuredClone(shouldBeRandom); +add_task( + simpleRFPTest.bind(null, uri, testCanvasRandomization, expectedResults) +); + +// Test a private window with RFP enabled in PBMode +expectedResults = structuredClone(shouldBeRandom); +add_task( + simplePBMRFPTest.bind(null, uri, testCanvasRandomization, expectedResults) +); + +expectedResults = structuredClone(shouldBeRandom); +add_task( + simpleFPPTest.bind(null, uri, testCanvasRandomization, expectedResults) +); + +// Test a Private Window with FPP Enabled in PBM +expectedResults = structuredClone(shouldBeRandom); +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(shouldBeRandom); +add_task( + RFPPBMFPP_NormalMode_ProtectionsTest.bind( + null, + uri, + testCanvasRandomization, + expectedResults + ) +); diff --git a/browser/components/resistfingerprinting/test/browser/file_efficientcanvascompare_iframee.html b/browser/components/resistfingerprinting/test/browser/file_efficientcanvascompare_iframee.html @@ -0,0 +1,41 @@ +<!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); + + return canvas.toDataURL(); +} +</script> +<output id="result"></output> diff --git a/browser/components/resistfingerprinting/test/browser/file_efficientcanvascompare_iframer.html b/browser/components/resistfingerprinting/test/browser/file_efficientcanvascompare_iframer.html @@ -0,0 +1,75 @@ +<!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_efficientcanvascompare_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); + + return canvas.toDataURL(); + } + 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>