diff options
Diffstat (limited to 'browser/components/resistfingerprinting/test')
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 || []), }); |