/* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ "use strict"; ChromeUtils.defineESModuleGetters(this, { ContentBlockingAllowList: "resource://gre/modules/ContentBlockingAllowList.sys.mjs", }); const TEST_PATH = "http://example.net/browser/browser/" + "components/resistfingerprinting/test/browser/"; const PERFORMANCE_TIMINGS = [ "navigationStart", "unloadEventStart", "unloadEventEnd", "redirectStart", "redirectEnd", "fetchStart", "domainLookupStart", "domainLookupEnd", "connectStart", "connectEnd", "secureConnectionStart", "requestStart", "responseStart", "responseEnd", "domLoading", "domInteractive", "domContentLoadedEventStart", "domContentLoadedEventEnd", "domComplete", "loadEventStart", "loadEventEnd", ]; /** * Sets up tests for making sure that performance APIs have been correctly * spoofed or disabled. */ let setupPerformanceAPISpoofAndDisableTest = async function ( resistFingerprinting, reduceTimerPrecision, crossOriginIsolated, expectedPrecision, runTests, workerCall ) { await SpecialPowers.pushPrefEnv({ set: [ ["privacy.resistFingerprinting", resistFingerprinting], ["privacy.reduceTimerPrecision", reduceTimerPrecision], [ "privacy.resistFingerprinting.reduceTimerPrecision.microseconds", expectedPrecision * 1000, ], ["browser.tabs.remote.useCrossOriginOpenerPolicy", crossOriginIsolated], ["browser.tabs.remote.useCrossOriginEmbedderPolicy", crossOriginIsolated], ], }); let url = crossOriginIsolated ? `https://example.com/browser/browser/components/resistfingerprinting` + `/test/browser/coop_header.sjs?crossOriginIsolated=${crossOriginIsolated}` : TEST_PATH + "file_dummy.html"; let win = await BrowserTestUtils.openNewBrowserWindow(); let tab = await BrowserTestUtils.openNewForegroundTab(win.gBrowser, url); // No matter what we set the precision to, if we're in ResistFingerprinting // mode we use the larger of the precision pref and the RFP time-atom constant if (resistFingerprinting) { const RFP_TIME_ATOM_MS = 16.667; expectedPrecision = Math.max(RFP_TIME_ATOM_MS, expectedPrecision); } await SpecialPowers.spawn( tab.linkedBrowser, [ { list: PERFORMANCE_TIMINGS, resistFingerprinting, precision: expectedPrecision, isRoundedFunc: isTimeValueRounded.toString(), workerCall, }, ], runTests ); if (crossOriginIsolated) { let remoteType = tab.linkedBrowser.remoteType; ok( remoteType.startsWith(E10SUtils.WEB_REMOTE_COOP_COEP_TYPE_PREFIX), `${remoteType} expected to be coop+coep` ); } await BrowserTestUtils.closeWindow(win); }; let isTimeValueRounded = (x, expectedPrecision, console) => { const nearestExpected = Math.round(x / expectedPrecision) * expectedPrecision; // First we do the perfectly normal check that should work just fine if (x === nearestExpected) { return true; } // When we're dividing by non-whole numbers, we may not get perfect // multiplication/division because of floating points. // When dealing with ms since epoch, a double's precision is on the order // of 1/5 of a microsecond, so we use a value a little higher than that as // our epsilon. // To be clear, this error is introduced in our re-calculation of 'rounded' // above in JavaScript. const error = Math.abs(x - nearestExpected); if (console) { console.log( "Additional Debugging Info: Expected Precision: " + expectedPrecision + " Measured Value: " + x + " Nearest Expected Vaue: " + nearestExpected + " Error: " + error ); } if (Math.abs(error) < 0.0005) { return true; } // Then we handle the case where you're sub-millisecond and the timer is not // We check that the timer is not sub-millisecond by assuming it is not if it // returns an even number of milliseconds if ( Math.round(expectedPrecision) != expectedPrecision && Math.round(x) == x ) { let acceptableIntRounding = false; acceptableIntRounding |= Math.floor(nearestExpected) == x; acceptableIntRounding |= Math.ceil(nearestExpected) == x; if (acceptableIntRounding) { return true; } } return false; }; let setupAndRunCrossOriginIsolatedTest = async function ( options, expectedPrecision, runTests, workerCall ) { let prefsToSet = [ [ "privacy.resistFingerprinting.reduceTimerPrecision.microseconds", expectedPrecision * 1000, ], ]; if (options.resistFingerprinting !== undefined) { prefsToSet = prefsToSet.concat([ ["privacy.resistFingerprinting", options.resistFingerprinting], ]); } else { options.resistFingerprinting = false; } if (options.resistFingerprintingPBMOnly !== undefined) { prefsToSet = prefsToSet.concat([ [ "privacy.resistFingerprinting.pbmode", options.resistFingerprintingPBMOnly, ], ]); } else { options.resistFingerprintingPBMOnly = false; } if (options.reduceTimerPrecision !== undefined) { prefsToSet = prefsToSet.concat([ ["privacy.reduceTimerPrecision", options.reduceTimerPrecision], ]); } else { options.reduceTimerPrecision = false; } if (options.crossOriginIsolated !== undefined) { prefsToSet = prefsToSet.concat([ [ "browser.tabs.remote.useCrossOriginOpenerPolicy", options.crossOriginIsolated, ], [ "browser.tabs.remote.useCrossOriginEmbedderPolicy", options.crossOriginIsolated, ], ]); } else { options.crossOriginIsolated = false; } if (options.openPrivateWindow === undefined) { options.openPrivateWindow = false; } if (options.shouldBeRounded === undefined) { options.shouldBeRounded = true; } console.log(prefsToSet); await SpecialPowers.pushPrefEnv({ set: prefsToSet }); let win = await BrowserTestUtils.openNewBrowserWindow({ private: options.openPrivateWindow, }); let tab = await BrowserTestUtils.openNewForegroundTab( win.gBrowser, `https://example.com/browser/browser/components/resistfingerprinting` + `/test/browser/coop_header.sjs?crossOriginIsolated=${options.crossOriginIsolated}` ); // No matter what we set the precision to, if we're in ResistFingerprinting // mode we use the larger of the precision pref and the RFP time-atom constant if ( options.resistFingerprinting || (options.resistFingerprintingPBMOnly && options.openPrivateWindow) ) { const RFP_TIME_ATOM_MS = 16.667; expectedPrecision = Math.max(RFP_TIME_ATOM_MS, expectedPrecision); } await SpecialPowers.spawn( tab.linkedBrowser, [ { precision: expectedPrecision, isRoundedFunc: isTimeValueRounded.toString(), workerCall, options, }, ], runTests ); if (options.crossOriginIsolated) { let remoteType = tab.linkedBrowser.remoteType; ok( remoteType.startsWith(E10SUtils.WEB_REMOTE_COOP_COEP_TYPE_PREFIX), `${remoteType} expected to be coop+coep` ); } await BrowserTestUtils.closeWindow(win); await SpecialPowers.popPrefEnv(); }; // This function calculates the maximum available window dimensions and returns // them as an object. async function calcMaximumAvailSize(aChromeWidth, aChromeHeight) { let chromeUIWidth; let chromeUIHeight; let testPath = "http://example.net/browser/browser/" + "components/resistfingerprinting/test/browser/"; // If the chrome UI dimensions is not given, we will calculate it. if (!aChromeWidth || !aChromeHeight) { let win = await BrowserTestUtils.openNewBrowserWindow(); let tab = await BrowserTestUtils.openNewForegroundTab( win.gBrowser, testPath + "file_dummy.html" ); let contentSize = await SpecialPowers.spawn( tab.linkedBrowser, [], async function () { let result = { width: content.innerWidth, height: content.innerHeight, }; return result; } ); // Calculate the maximum available window size which is depending on the // available screen space. chromeUIWidth = win.outerWidth - contentSize.width; chromeUIHeight = win.outerHeight - contentSize.height; BrowserTestUtils.removeTab(tab); await BrowserTestUtils.closeWindow(win); } else { chromeUIWidth = aChromeWidth; chromeUIHeight = aChromeHeight; } let availWidth = window.screen.availWidth; let availHeight = window.screen.availHeight; // Ideally, we would round the window size as 1000x1000. But the available // screen space might not suffice. So, we decide the size according to the // available screen size. let availContentWidth = Math.min(1000, availWidth - chromeUIWidth); let availContentHeight; // If it is GTK window, we would consider the system decorations when we // calculating avail content height since the system decorations won't be // reported when we get available screen dimensions. if (AppConstants.MOZ_WIDGET_GTK) { availContentHeight = Math.min(1000, -40 + availHeight - chromeUIHeight); } else { availContentHeight = Math.min(1000, availHeight - chromeUIHeight); } // Rounded the desire size to the nearest 200x100. let maxAvailWidth = availContentWidth - (availContentWidth % 200); let maxAvailHeight = availContentHeight - (availContentHeight % 100); return { maxAvailWidth, maxAvailHeight }; } async function calcPopUpWindowChromeUISize() { let testPath = "http://example.net/browser/browser/" + "components/resistFingerprinting/test/browser/"; // open a popup window to acquire the chrome UI size of it. let tab = await BrowserTestUtils.openNewForegroundTab( gBrowser, testPath + "file_dummy.html" ); let result = await SpecialPowers.spawn( tab.linkedBrowser, [], async function () { let win; await new Promise(resolve => { win = content.open("about:blank", "", "width=1000,height=1000"); win.onload = () => resolve(); }); let res = { chromeWidth: win.outerWidth - win.innerWidth, chromeHeight: win.outerHeight - win.innerHeight, }; win.close(); return res; } ); BrowserTestUtils.removeTab(tab); return result; } async function testWindowOpen( aBrowser, aSettingWidth, aSettingHeight, aTargetWidth, aTargetHeight, aMaxAvailWidth, aMaxAvailHeight, aPopupChromeUIWidth, aPopupChromeUIHeight ) { // If the target size is greater than the maximum available content size, // we set the target size to it. if (aTargetWidth > aMaxAvailWidth) { aTargetWidth = aMaxAvailWidth; } if (aTargetHeight > aMaxAvailHeight) { aTargetHeight = aMaxAvailHeight; } // Create the testing window features. let winFeatures = "width=" + aSettingWidth + ",height=" + aSettingHeight; let testParams = { winFeatures, targetWidth: aTargetWidth, targetHeight: aTargetHeight, }; await SpecialPowers.spawn(aBrowser, [testParams], async function (input) { // Call window.open() with window features. await new Promise(resolve => { let win = content.open("http://example.net/", "", input.winFeatures); win.onload = () => { is( win.screen.width, input.targetWidth, "The screen.width has a correct rounded value" ); is( win.screen.height, input.targetHeight, "The screen.height has a correct rounded value" ); is( win.innerWidth, input.targetWidth, "The window.innerWidth has a correct rounded value" ); is( win.innerHeight, input.targetHeight, "The window.innerHeight has a correct rounded value" ); win.close(); resolve(); }; }); }); } async function testWindowSizeSetting( aBrowser, aSettingWidth, aSettingHeight, aTargetWidth, aTargetHeight, aInitWidth, aInitHeight, aTestOuter, aMaxAvailWidth, aMaxAvailHeight, aPopupChromeUIWidth, aPopupChromeUIHeight ) { // If the target size is greater than the maximum available content size, // we set the target size to it. if (aTargetWidth > aMaxAvailWidth) { aTargetWidth = aMaxAvailWidth; } if (aTargetHeight > aMaxAvailHeight) { aTargetHeight = aMaxAvailHeight; } let testParams = { initWidth: aInitWidth, initHeight: aInitHeight, settingWidth: aSettingWidth + (aTestOuter ? aPopupChromeUIWidth : 0), settingHeight: aSettingHeight + (aTestOuter ? aPopupChromeUIHeight : 0), targetWidth: aTargetWidth, targetHeight: aTargetHeight, testOuter: aTestOuter, }; await SpecialPowers.spawn(aBrowser, [testParams], async function (input) { let win; // Open a new window and wait until it loads. await new Promise(resolve => { // Given a initial window size which should be different from target // size. We need this to trigger 'onresize' event. let initWinFeatures = "width=" + input.initWidth + ",height=" + input.initHeight; win = content.open("http://example.net/", "", initWinFeatures); win.onload = () => resolve(); }); // Test inner/outerWidth. await new Promise(resolve => { win.addEventListener( "resize", () => { is( win.screen.width, input.targetWidth, "The screen.width has a correct rounded value" ); is( win.innerWidth, input.targetWidth, "The window.innerWidth has a correct rounded value" ); resolve(); }, { once: true } ); if (input.testOuter) { win.outerWidth = input.settingWidth; } else { win.innerWidth = input.settingWidth; } }); win.close(); // Open a new window and wait until it loads. await new Promise(resolve => { // Given a initial window size which should be different from target // size. We need this to trigger 'onresize' event. let initWinFeatures = "width=" + input.initWidth + ",height=" + input.initHeight; win = content.open("http://example.net/", "", initWinFeatures); win.onload = () => resolve(); }); // Test inner/outerHeight. await new Promise(resolve => { win.addEventListener( "resize", () => { is( win.screen.height, input.targetHeight, "The screen.height has a correct rounded value" ); is( win.innerHeight, input.targetHeight, "The window.innerHeight has a correct rounded value" ); resolve(); }, { once: true } ); if (input.testOuter) { win.outerHeight = input.settingHeight; } else { win.innerHeight = input.settingHeight; } }); win.close(); }); } class RoundedWindowTest { // testOuter is optional. run() can be invoked with only 1 parameter. static run(testCases, testOuter) { // "this" is the calling class itself. // e.g. when invoked by RoundedWindowTest.run(), "this" is "class RoundedWindowTest". let test = new this(testCases); add_task(async () => test.setup()); add_task(async () => { if (testOuter == undefined) { // If testOuter is not given, do tests for both inner and outer. await test.doTests(false); await test.doTests(true); } else { await test.doTests(testOuter); } }); } constructor(testCases) { this.testCases = testCases; } async setup() { await SpecialPowers.pushPrefEnv({ set: [["privacy.resistFingerprinting", true]], }); // Calculate the popup window's chrome UI size for tests of outerWidth/Height. let popUpChromeUISize = await calcPopUpWindowChromeUISize(); this.popupChromeUIWidth = popUpChromeUISize.chromeWidth; this.popupChromeUIHeight = popUpChromeUISize.chromeHeight; // Calculate the maximum available size. let maxAvailSize = await calcMaximumAvailSize( this.popupChromeUIWidth, this.popupChromeUIHeight ); this.maxAvailWidth = maxAvailSize.maxAvailWidth; this.maxAvailHeight = maxAvailSize.maxAvailHeight; } async doTests(testOuter) { // Open a tab to test. this.tab = await BrowserTestUtils.openNewForegroundTab( gBrowser, TEST_PATH + "file_dummy.html" ); for (let test of this.testCases) { await this.doTest(test, testOuter); } BrowserTestUtils.removeTab(this.tab); } async doTest() { throw new Error("RoundedWindowTest.doTest must be overridden."); } } class WindowSettingTest extends RoundedWindowTest { async doTest(test, testOuter) { await testWindowSizeSetting( this.tab.linkedBrowser, test.settingWidth, test.settingHeight, test.targetWidth, test.targetHeight, test.initWidth, test.initHeight, testOuter, this.maxAvailWidth, this.maxAvailHeight, this.popupChromeUIWidth, this.popupChromeUIHeight ); } } class OpenTest extends RoundedWindowTest { async doTest(test) { await testWindowOpen( this.tab.linkedBrowser, test.settingWidth, test.settingHeight, test.targetWidth, test.targetHeight, this.maxAvailWidth, this.maxAvailHeight, this.popupChromeUIWidth, this.popupChromeUIHeight ); } } // ============================================================ const FRAMER_DOMAIN = "example.com"; const IFRAME_DOMAIN = "example.org"; const CROSS_ORIGIN_DOMAIN = "example.net"; async function runActualTest(uri, testFunction, expectedResults, extraData) { let browserWin = gBrowser; let openedWin = null; if ("private_window" in extraData) { openedWin = await BrowserTestUtils.openNewBrowserWindow({ private: true, }); browserWin = openedWin.gBrowser; } let tab = await BrowserTestUtils.openNewForegroundTab(browserWin, uri); if ("etp_reload" in extraData) { ContentBlockingAllowList.add(tab.linkedBrowser); await BrowserTestUtils.reloadTab(tab); } /* * We expect that `runTheTest` is going to be able to communicate with the iframe * or tab that it opens, but if it cannot (because we are using noopener), we kind * of hack around and get the data directly. */ if ("noopener" in extraData) { var popupTabPromise = BrowserTestUtils.waitForNewTab( browserWin, extraData.await_uri ); } // In SpecialPowers.spawn, extraData goes through a structuredClone, which cannot clone // functions. await_uri is sometimes a function. This filters out keys that are used by // this function (runActualTest) and not by runTheTest or testFunction. It avoids the // cloning issue, and avoids polluting the object in those called functions. let filterExtraData = function (x) { let banned_keys = ["private_window", "etp_reload", "noopener", "await_uri"]; return Object.fromEntries( Object.entries(x).filter(([k, v]) => !banned_keys.includes(k)) ); }; let result = await SpecialPowers.spawn( tab.linkedBrowser, [IFRAME_DOMAIN, CROSS_ORIGIN_DOMAIN, filterExtraData(extraData)], async function (iframe_domain_, cross_origin_domain_, extraData_) { return content.wrappedJSObject.runTheTest( iframe_domain_, cross_origin_domain_, extraData_ ); } ); if ("noopener" in extraData) { await popupTabPromise; if (Services.appinfo.OS === "WINNT") { await new Promise(r => setTimeout(r, 1000)); } let popup_tab = browserWin.tabs[browserWin.tabs.length - 1]; result = await SpecialPowers.spawn( popup_tab.linkedBrowser, [], async function () { let r = content.wrappedJSObject.give_result(); return r; } ); BrowserTestUtils.removeTab(popup_tab); } testFunction(result, expectedResults, extraData); if ("etp_reload" in extraData) { ContentBlockingAllowList.remove(tab.linkedBrowser); } BrowserTestUtils.removeTab(tab); if ("private_window" in extraData) { await BrowserTestUtils.closeWindow(openedWin); } } async function defaultsTest( uri, testFunction, expectedResults, extraData, extraPrefs ) { if (extraData == undefined) { extraData = {}; } extraData.testDesc = extraData.testDesc || "default"; 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, expectedResults, extraData, extraPrefs ) { if (extraData == undefined) { extraData = {}; } extraData.testDesc = extraData.testDesc || "simple RFP enabled"; expectedResults.shouldRFPApply = true; await SpecialPowers.pushPrefEnv({ set: [["privacy.resistFingerprinting", true]].concat(extraPrefs || []), }); await runActualTest(uri, testFunction, expectedResults, extraData); await SpecialPowers.popPrefEnv(); } async function simplePBMRFPTest( uri, testFunction, expectedResults, extraData, extraPrefs ) { if (extraData == undefined) { extraData = {}; } extraData.private_window = true; extraData.testDesc = extraData.testDesc || "simple RFP in PBM enabled"; expectedResults.shouldRFPApply = true; await SpecialPowers.pushPrefEnv({ set: [["privacy.resistFingerprinting.pbmode", true]].concat( extraPrefs || [] ), }); await runActualTest(uri, testFunction, expectedResults, extraData); await SpecialPowers.popPrefEnv(); } async function simpleFPPTest( uri, testFunction, expectedResults, extraData, extraPrefs ) { if (extraData == undefined) { extraData = {}; } extraData.testDesc = extraData.testDesc || "simple FPP enabled"; expectedResults.shouldRFPApply = true; await SpecialPowers.pushPrefEnv({ set: [ ["privacy.fingerprintingProtection", true], ["privacy.fingerprintingProtection.overrides", "+NavigatorHWConcurrency"], ].concat(extraPrefs || []), }); await runActualTest(uri, testFunction, expectedResults, extraData); await SpecialPowers.popPrefEnv(); } async function simplePBMFPPTest( uri, testFunction, expectedResults, extraData, extraPrefs ) { if (extraData == undefined) { extraData = {}; } extraData.private_window = true; extraData.testDesc = extraData.testDesc || "simple FPP in PBM enabled"; expectedResults.shouldRFPApply = true; await SpecialPowers.pushPrefEnv({ set: [ ["privacy.fingerprintingProtection.pbmode", true], ["privacy.fingerprintingProtection.overrides", "+HardwareConcurrency"], ].concat(extraPrefs || []), }); await runActualTest(uri, testFunction, expectedResults, extraData); await SpecialPowers.popPrefEnv(); } // (A) RFP is exempted on the framer and framee and (if needed) on another cross-origin domain async function testA( uri, testFunction, expectedResults, extraData, extraPrefs ) { if (extraData == undefined) { extraData = {}; } extraData.testDesc = extraData.testDesc || "test (A)"; expectedResults.shouldRFPApply = false; await SpecialPowers.pushPrefEnv({ set: [ ["privacy.resistFingerprinting", true], [ "privacy.resistFingerprinting.exemptedDomains", `${FRAMER_DOMAIN}, ${IFRAME_DOMAIN}, ${CROSS_ORIGIN_DOMAIN}`, ], ].concat(extraPrefs || []), }); await runActualTest(uri, testFunction, expectedResults, extraData); await SpecialPowers.popPrefEnv(); } // (B) RFP is exempted on the framer and framee but is not on another (if needed) cross-origin domain async function testB( uri, testFunction, expectedResults, extraData, extraPrefs ) { if (extraData == undefined) { extraData = {}; } extraData.testDesc = extraData.testDesc || "test (B)"; expectedResults.shouldRFPApply = false; await SpecialPowers.pushPrefEnv({ set: [ ["privacy.resistFingerprinting", true], [ "privacy.resistFingerprinting.exemptedDomains", `${FRAMER_DOMAIN}, ${IFRAME_DOMAIN}`, ], ].concat(extraPrefs || []), }); await runActualTest(uri, testFunction, expectedResults, extraData); await SpecialPowers.popPrefEnv(); } // (C) RFP is exempted on the framer and (if needed) on another cross-origin domain, but not the framee async function testC( uri, testFunction, expectedResults, extraData, extraPrefs ) { if (extraData == undefined) { extraData = {}; } extraData.testDesc = extraData.testDesc || "test (C)"; expectedResults.shouldRFPApply = true; await SpecialPowers.pushPrefEnv({ set: [ ["privacy.resistFingerprinting", true], [ "privacy.resistFingerprinting.exemptedDomains", `${FRAMER_DOMAIN}, ${CROSS_ORIGIN_DOMAIN}`, ], ].concat(extraPrefs || []), }); await runActualTest(uri, testFunction, expectedResults, extraData); await SpecialPowers.popPrefEnv(); } // (D) RFP is exempted on the framer but not the framee nor another (if needed) cross-origin domain async function testD( uri, testFunction, expectedResults, extraData, extraPrefs ) { if (extraData == undefined) { extraData = {}; } extraData.testDesc = extraData.testDesc || "test (D)"; expectedResults.shouldRFPApply = true; await SpecialPowers.pushPrefEnv({ set: [ ["privacy.resistFingerprinting", true], ["privacy.resistFingerprinting.exemptedDomains", `${FRAMER_DOMAIN}`], ].concat(extraPrefs || []), }); await runActualTest(uri, testFunction, expectedResults, extraData); await SpecialPowers.popPrefEnv(); } // (E) RFP is not exempted on the framer nor the framee but (if needed) is exempted on another cross-origin domain async function testE( uri, testFunction, expectedResults, extraData, extraPrefs ) { if (extraData == undefined) { extraData = {}; } extraData.testDesc = extraData.testDesc || "test (E)"; expectedResults.shouldRFPApply = true; await SpecialPowers.pushPrefEnv({ set: [ ["privacy.resistFingerprinting", true], [ "privacy.resistFingerprinting.exemptedDomains", `${CROSS_ORIGIN_DOMAIN}`, ], ].concat(extraPrefs || []), }); await runActualTest(uri, testFunction, expectedResults, extraData); await SpecialPowers.popPrefEnv(); } // (F) RFP is not exempted on the framer nor the framee nor another (if needed) cross-origin domain async function testF( uri, testFunction, expectedResults, extraData, extraPrefs ) { if (extraData == undefined) { extraData = {}; } extraData.testDesc = extraData.testDesc || "test (F)"; expectedResults.shouldRFPApply = true; await SpecialPowers.pushPrefEnv({ set: [ ["privacy.resistFingerprinting", true], ["privacy.resistFingerprinting.exemptedDomains", ""], ].concat(extraPrefs || []), }); await runActualTest(uri, testFunction, expectedResults, extraData); await SpecialPowers.popPrefEnv(); } // (G) RFP is not exempted on the framer but is on the framee and (if needed) on another cross-origin domain async function testG( uri, testFunction, expectedResults, extraData, extraPrefs ) { if (extraData == undefined) { extraData = {}; } extraData.testDesc = extraData.testDesc || "test (G)"; expectedResults.shouldRFPApply = true; await SpecialPowers.pushPrefEnv({ set: [ ["privacy.resistFingerprinting", true], [ "privacy.resistFingerprinting.exemptedDomains", `${IFRAME_DOMAIN}, ${CROSS_ORIGIN_DOMAIN}`, ], ].concat(extraPrefs || []), }); await runActualTest(uri, testFunction, expectedResults, extraData); await SpecialPowers.popPrefEnv(); } // (H) RFP is not exempted on the framer nor another (if needed) cross-origin domain but is on the framee async function testH( uri, testFunction, expectedResults, extraData, extraPrefs ) { if (extraData == undefined) { extraData = {}; } extraData.testDesc = extraData.testDesc || "test (H)"; expectedResults.shouldRFPApply = true; await SpecialPowers.pushPrefEnv({ set: [ ["privacy.resistFingerprinting", true], ["privacy.resistFingerprinting.exemptedDomains", `${IFRAME_DOMAIN}`], ].concat(extraPrefs || []), }); await runActualTest(uri, testFunction, expectedResults, extraData); await SpecialPowers.popPrefEnv(); }