let { ForgetAboutSite } = ChromeUtils.importESModule( "resource://gre/modules/ForgetAboutSite.sys.mjs" ); requestLongerTimeout(2); const TEST_DOMAIN = "https://example.com"; const TEST_DOMAIN_ANOTHER = "https://example.org"; const TEST_DOMAIN_THIRD = "https://example.net"; const TEST_PAGE = getRootDirectory(gTestPath).replace( "chrome://mochitests/content", TEST_DOMAIN ) + "testPage.html"; const TEST_DOMAIN_ANOTHER_PAGE = getRootDirectory(gTestPath).replace( "chrome://mochitests/content", TEST_DOMAIN_ANOTHER ) + "testPage.html"; const TEST_DOMAIN_THIRD_PAGE = getRootDirectory(gTestPath).replace( "chrome://mochitests/content", TEST_DOMAIN_THIRD ) + "testPage.html"; /** * A helper function to get the random key in a hex string format and test if * the random key works properly. * * @param {Browser} browser The browser element of the testing tab. * @param {string} firstPartyDomain The first-party domain loaded on the tab * @param {string} thirdPartyDomain The third-party domain to test * @returns {string} The random key hex string */ async function getRandomKeyHexFromBrowser( browser, firstPartyDomain, thirdPartyDomain ) { // Get the key from the cookieJarSettings of the browser element. let key = browser.cookieJarSettings.fingerprintingRandomizationKey; let keyHex = key.map(bytes => bytes.toString(16).padStart(2, "0")).join(""); // Get the key from the cookieJarSettings of the top-level document. let keyTop = await SpecialPowers.spawn(browser, [], _ => { return content.document.cookieJarSettings.fingerprintingRandomizationKey; }); let keyTopHex = keyTop .map(bytes => bytes.toString(16).padStart(2, "0")) .join(""); is( keyTopHex, keyHex, "The fingerprinting random key should match between the browser element and the top-level document." ); // Get the key from the cookieJarSettings of an about:blank iframe. let keyAboutBlank = await SpecialPowers.spawn(browser, [], async _ => { let ifr = content.document.createElement("iframe"); let loaded = new content.Promise(resolve => { ifr.onload = resolve; }); content.document.body.appendChild(ifr); ifr.src = "about:blank"; await loaded; return SpecialPowers.spawn(ifr, [], _ => { return content.document.cookieJarSettings.fingerprintingRandomizationKey; }); }); let keyAboutBlankHex = keyAboutBlank .map(bytes => bytes.toString(16).padStart(2, "0")) .join(""); is( keyAboutBlankHex, keyHex, "The fingerprinting random key should match between the browser element and the about:blank iframe document." ); // Get the key from the cookieJarSettings of the javascript URL iframe // document. let keyJavascriptURL = await SpecialPowers.spawn(browser, [], async _ => { let ifr = content.document.getElementById("testFrame"); return ifr.contentDocument.cookieJarSettings.fingerprintingRandomizationKey; }); let keyJavascriptURLHex = keyJavascriptURL .map(bytes => bytes.toString(16).padStart(2, "0")) .join(""); is( keyJavascriptURLHex, keyHex, "The fingerprinting random key should match between the browser element and the javascript URL iframe document." ); // Get the key from the cookieJarSettings of an first-party iframe. let keyFirstPartyFrame = await SpecialPowers.spawn( browser, [firstPartyDomain], async domain => { let ifr = content.document.createElement("iframe"); let loaded = new content.Promise(resolve => { ifr.onload = resolve; }); content.document.body.appendChild(ifr); ifr.src = domain; await loaded; return SpecialPowers.spawn(ifr, [], _ => { return content.document.cookieJarSettings .fingerprintingRandomizationKey; }); } ); let keyFirstPartyFrameHex = keyFirstPartyFrame .map(bytes => bytes.toString(16).padStart(2, "0")) .join(""); is( keyFirstPartyFrameHex, keyHex, "The fingerprinting random key should match between the browser element and the first-party iframe document." ); // Get the key from the cookieJarSettings of an third-party iframe let keyThirdPartyFrame = await SpecialPowers.spawn( browser, [thirdPartyDomain], async domain => { let ifr = content.document.createElement("iframe"); let loaded = new content.Promise(resolve => { ifr.onload = resolve; }); content.document.body.appendChild(ifr); ifr.src = domain; await loaded; return SpecialPowers.spawn(ifr, [], _ => { return content.document.cookieJarSettings .fingerprintingRandomizationKey; }); } ); let keyThirdPartyFrameHex = keyThirdPartyFrame .map(bytes => bytes.toString(16).padStart(2, "0")) .join(""); is( keyThirdPartyFrameHex, keyHex, "The fingerprinting random key should match between the browser element and the third-party iframe document." ); return keyHex; } // Test accessing the fingerprinting randomization key will throw if // fingerprinting resistance is disabled. add_task(async function test_randomization_disabled_with_rfp_disabled() { await SpecialPowers.pushPrefEnv({ set: [ ["privacy.resistFingerprinting", false], ["privacy.resistFingerprinting.pbmode", false], ["privacy.fingerprintingProtection", false], ["privacy.fingerprintingProtection.pbmode", false], ], }); // Ensure accessing the fingerprinting randomization key of the browser // element will throw if fingerprinting randomization is disabled. let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, TEST_PAGE); try { let key = tab.linkedBrowser.cookieJarSettings.fingerprintingRandomizationKey; ok( false, `Accessing the fingerprinting randomization key should throw when fingerprinting resistance is disabled. ${key}` ); } catch (e) { ok( true, "It should throw when getting the key when fingerprinting resistance is disabled." ); } // Ensure accessing the fingerprinting randomization key of the top-level // document will throw if fingerprinting randomization is disabled. try { await SpecialPowers.spawn(tab.linkedBrowser, [], _ => { return content.document.cookieJarSettings.fingerprintingRandomizationKey; }); } catch (e) { ok( true, "It should throw when getting the key when fingerprinting resistance is disabled." ); } BrowserTestUtils.removeTab(tab); }); // Test the fingerprinting randomization key generation. add_task(async function test_generate_randomization_key() { await SpecialPowers.pushPrefEnv({ set: [["privacy.resistFingerprinting", true]], }); for (let testPrivateWin of [true, false]) { let win = window; if (testPrivateWin) { win = await BrowserTestUtils.openNewBrowserWindow({ private: true, }); } let tabOne = await BrowserTestUtils.openNewForegroundTab( win.gBrowser, TEST_PAGE ); let keyHexOne; try { keyHexOne = await getRandomKeyHexFromBrowser( tabOne.linkedBrowser, TEST_PAGE, TEST_DOMAIN_THIRD_PAGE ); ok(true, `The fingerprinting random key: ${keyHexOne}`); } catch (e) { ok( false, "Shouldn't fail when getting the random key from the cookieJarSettings" ); } // Open the test domain again and check if the key remains the same. let tabTwo = await BrowserTestUtils.openNewForegroundTab( win.gBrowser, TEST_PAGE ); try { let keyHexTwo = await getRandomKeyHexFromBrowser( tabTwo.linkedBrowser, TEST_PAGE, TEST_DOMAIN_THIRD_PAGE ); is( keyHexTwo, keyHexOne, `The key should remain the same after reopening the tab.` ); } catch (e) { ok( false, "Shouldn't fail when getting the random key from the cookieJarSettings" ); } // Open a tab with a different domain to see if the key changes. let tabAnother = await BrowserTestUtils.openNewForegroundTab( win.gBrowser, TEST_DOMAIN_ANOTHER_PAGE ); try { let keyHexAnother = await getRandomKeyHexFromBrowser( tabAnother.linkedBrowser, TEST_DOMAIN_ANOTHER_PAGE, TEST_DOMAIN_THIRD_PAGE ); isnot( keyHexAnother, keyHexOne, `The key should be different when loading a different domain` ); } catch (e) { ok( false, "Shouldn't fail when getting the random key from the cookieJarSettings" ); } BrowserTestUtils.removeTab(tabOne); BrowserTestUtils.removeTab(tabTwo); BrowserTestUtils.removeTab(tabAnother); if (testPrivateWin) { await BrowserTestUtils.closeWindow(win); } } }); // Test the fingerprinting randomization key will change after private session // ends. add_task(async function test_reset_key_after_pbm_session_ends() { await SpecialPowers.pushPrefEnv({ set: [["privacy.resistFingerprinting", true]], }); let privateWin = await BrowserTestUtils.openNewBrowserWindow({ private: true, }); // Open a tab in the private window. let tab = await BrowserTestUtils.openNewForegroundTab( privateWin.gBrowser, TEST_PAGE ); let keyHex = await getRandomKeyHexFromBrowser( tab.linkedBrowser, TEST_PAGE, TEST_DOMAIN_THIRD_PAGE ); // Close the window and open another private window. BrowserTestUtils.removeTab(tab); let promisePBExit = TestUtils.topicObserved("last-pb-context-exited"); await BrowserTestUtils.closeWindow(privateWin); await promisePBExit; privateWin = await BrowserTestUtils.openNewBrowserWindow({ private: true, }); // Open a tab again in the new private window. tab = await BrowserTestUtils.openNewForegroundTab( privateWin.gBrowser, TEST_PAGE ); let keyHexNew = await getRandomKeyHexFromBrowser( tab.linkedBrowser, TEST_PAGE, TEST_DOMAIN_THIRD_PAGE ); // Ensure the keys are different. isnot(keyHexNew, keyHex, "Ensure the new key is different from the old one."); BrowserTestUtils.removeTab(tab); await BrowserTestUtils.closeWindow(privateWin); }); // Test accessing the fingerprinting randomization key will throw in normal // windows if we exempt fingerprinting protection in normal windows. add_task(async function test_randomization_with_exempted_normal_window() { await SpecialPowers.pushPrefEnv({ set: [ ["privacy.resistFingerprinting", false], ["privacy.resistFingerprinting.pbmode", true], ["privacy.fingerprintingProtection", false], ["privacy.fingerprintingProtection.pbmode", false], ], }); // Ensure accessing the fingerprinting randomization key of the browser // element will throw if fingerprinting randomization is exempted from normal // windows. let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, TEST_PAGE); try { let key = tab.linkedBrowser.cookieJarSettings.fingerprintingRandomizationKey; ok( false, `Accessing the fingerprinting randomization key should throw when fingerprinting resistance is exempted in normal windows. ${key}` ); } catch (e) { ok( true, "It should throw when getting the key when fingerprinting resistance is exempted in normal windows." ); } // Ensure accessing the fingerprinting randomization key of the top-level // document will throw if fingerprinting randomization is exempted from normal // windows. try { await SpecialPowers.spawn(tab.linkedBrowser, [], _ => { return content.document.cookieJarSettings.fingerprintingRandomizationKey; }); } catch (e) { ok( true, "It should throw when getting the key when fingerprinting resistance is exempted in normal windows." ); } BrowserTestUtils.removeTab(tab); // Open a private window and check the key can be accessed there. let privateWin = await BrowserTestUtils.openNewBrowserWindow({ private: true, }); // Open a tab in the private window. tab = await BrowserTestUtils.openNewForegroundTab( privateWin.gBrowser, TEST_PAGE ); // Access the key, this shouldn't throw an error. await getRandomKeyHexFromBrowser( tab.linkedBrowser, TEST_PAGE, TEST_DOMAIN_THIRD_PAGE ); BrowserTestUtils.removeTab(tab); await BrowserTestUtils.closeWindow(privateWin); }); // Test that the random key gets reset when the site data gets cleared. add_task(async function test_reset_random_key_when_clear_site_data() { // Enable fingerprinting randomization key generation. await SpecialPowers.pushPrefEnv({ set: [["privacy.resistFingerprinting", true]], }); // Open a tab and get randomization key from the test domain. let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, TEST_PAGE); let keyHex = await getRandomKeyHexFromBrowser( tab.linkedBrowser, TEST_PAGE, TEST_DOMAIN_THIRD_PAGE ); // Open another tab and get randomization key from another domain. let anotherTab = await BrowserTestUtils.openNewForegroundTab( gBrowser, TEST_DOMAIN_ANOTHER_PAGE ); let keyHexAnother = await getRandomKeyHexFromBrowser( anotherTab.linkedBrowser, TEST_PAGE, TEST_DOMAIN_THIRD_PAGE ); BrowserTestUtils.removeTab(tab); BrowserTestUtils.removeTab(anotherTab); // Call ForgetAboutSite for the test domain. await ForgetAboutSite.removeDataFromDomain("example.com"); // Open the tab for the test domain again and verify the key is reset. tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, TEST_PAGE); let keyHexNew = await getRandomKeyHexFromBrowser( tab.linkedBrowser, TEST_PAGE, TEST_DOMAIN_THIRD_PAGE ); // Open the tab for another domain again and verify the key is intact. anotherTab = await BrowserTestUtils.openNewForegroundTab( gBrowser, TEST_DOMAIN_ANOTHER_PAGE ); let keyHexAnotherNew = await getRandomKeyHexFromBrowser( anotherTab.linkedBrowser, TEST_PAGE, TEST_DOMAIN_THIRD_PAGE ); // Ensure the keys are different for the test domain. isnot(keyHexNew, keyHex, "Ensure the new key is different from the old one."); // Ensure the key for another domain isn't changed. is( keyHexAnother, keyHexAnotherNew, "Ensure the key of another domain isn't reset." ); BrowserTestUtils.removeTab(tab); BrowserTestUtils.removeTab(anotherTab); });