diff options
Diffstat (limited to 'toolkit/components/resistfingerprinting/tests/browser/browser_fingerprintingWebCompat.js')
-rw-r--r-- | toolkit/components/resistfingerprinting/tests/browser/browser_fingerprintingWebCompat.js | 559 |
1 files changed, 559 insertions, 0 deletions
diff --git a/toolkit/components/resistfingerprinting/tests/browser/browser_fingerprintingWebCompat.js b/toolkit/components/resistfingerprinting/tests/browser/browser_fingerprintingWebCompat.js new file mode 100644 index 0000000000..1a882fc63d --- /dev/null +++ b/toolkit/components/resistfingerprinting/tests/browser/browser_fingerprintingWebCompat.js @@ -0,0 +1,559 @@ +/* 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"; + +const { RemoteSettings } = ChromeUtils.importESModule( + "resource://services-settings/remote-settings.sys.mjs" +); + +const COLLECTION_NAME = "fingerprinting-protection-overrides"; + +const TOP_DOMAIN = "https://example.com"; +const THIRD_PARTY_DOMAIN = "https://example.org"; + +const SPOOFED_HW_CONCURRENCY = 2; +const DEFAULT_HW_CONCURRENCY = navigator.hardwareConcurrency; + +const TEST_TOP_PAGE = + getRootDirectory(gTestPath).replace( + "chrome://mochitests/content", + TOP_DOMAIN + ) + "empty.html"; +const TEST_THIRD_PARTY_PAGE = + getRootDirectory(gTestPath).replace( + "chrome://mochitests/content", + THIRD_PARTY_DOMAIN + ) + "empty.html"; + +const TEST_CASES = [ + // Test the wildcard entry. + { + entires: [ + { + id: "1", + last_modified: 1000000000000001, + overrides: "+WindowOuterSize", + firstPartyDomain: "*", + }, + ], + expects: { + windowOuter: { + top: true, + firstParty: true, + thirdParty: true, + }, + hwConcurrency: { + top: false, + firstParty: false, + thirdParty: false, + }, + }, + }, + // Test the entry that only applies to the first-party context. + { + entires: [ + { + id: "1", + last_modified: 1000000000000001, + overrides: "+WindowOuterSize", + firstPartyDomain: "example.com", + }, + ], + expects: { + windowOuter: { + top: true, + firstParty: true, + thirdParty: false, + }, + hwConcurrency: { + top: false, + firstParty: false, + thirdParty: false, + }, + }, + }, + // Test the entry that applies to every context under the first-party domain. + { + entires: [ + { + id: "1", + last_modified: 1000000000000001, + overrides: "+WindowOuterSize", + firstPartyDomain: "example.com", + thirdPartyDomain: "*", + }, + ], + expects: { + windowOuter: { + top: true, + firstParty: true, + thirdParty: true, + }, + hwConcurrency: { + top: false, + firstParty: false, + thirdParty: false, + }, + }, + }, + // Test the entry that applies to the specific third-party domain under the first-party domain. + { + entires: [ + { + id: "1", + last_modified: 1000000000000001, + overrides: "+WindowOuterSize", + firstPartyDomain: "example.com", + thirdPartyDomain: "example.org", + }, + ], + expects: { + windowOuter: { + top: false, + firstParty: false, + thirdParty: true, + }, + hwConcurrency: { + top: false, + firstParty: false, + thirdParty: false, + }, + }, + }, + // Test the entry that applies to the every third-party domain under any first-party domains. + { + entires: [ + { + id: "1", + last_modified: 1000000000000001, + overrides: "+WindowOuterSize", + firstPartyDomain: "*", + thirdPartyDomain: "example.org", + }, + ], + expects: { + windowOuter: { + top: false, + firstParty: false, + thirdParty: true, + }, + hwConcurrency: { + top: false, + firstParty: false, + thirdParty: false, + }, + }, + }, + // Test a entry that doesn't apply to the current contexts. The first-party + // domain is different. + { + entires: [ + { + id: "1", + last_modified: 1000000000000001, + overrides: "+WindowOuterSize", + firstPartyDomain: "example.net", + }, + ], + expects: { + windowOuter: { + top: false, + firstParty: false, + thirdParty: false, + }, + hwConcurrency: { + top: false, + firstParty: false, + thirdParty: false, + }, + }, + }, + // Test a entry that doesn't apply to the current contexts. The first-party + // domain is different. + { + entires: [ + { + id: "1", + last_modified: 1000000000000001, + overrides: "+WindowOuterSize", + firstPartyDomain: "example.net", + thirdPartyDomain: "*", + }, + ], + expects: { + windowOuter: { + top: false, + firstParty: false, + thirdParty: false, + }, + hwConcurrency: { + top: false, + firstParty: false, + thirdParty: false, + }, + }, + }, + // Test a entry that doesn't apply to a the current contexts. Both first-party + // and third-party domains are different. + { + entires: [ + { + id: "1", + last_modified: 1000000000000001, + overrides: "+WindowOuterSize", + firstPartyDomain: "example.net", + thirdPartyDomain: "example.com", + }, + ], + expects: { + windowOuter: { + top: false, + firstParty: false, + thirdParty: false, + }, + hwConcurrency: { + top: false, + firstParty: false, + thirdParty: false, + }, + }, + }, + // Test multiple entries that enable HW concurrency in the first-party context + // and WindowOuter in the third-party context. + { + entires: [ + { + id: "1", + last_modified: 1000000000000001, + overrides: "+WindowOuterSize", + firstPartyDomain: "example.com", + }, + { + id: "2", + last_modified: 1000000000000001, + overrides: "+NavigatorHWConcurrency", + firstPartyDomain: "example.com", + thirdPartyDomain: "example.org", + }, + ], + expects: { + windowOuter: { + top: true, + firstParty: true, + thirdParty: false, + }, + hwConcurrency: { + top: false, + firstParty: false, + thirdParty: true, + }, + }, + }, +]; + +async function openAndSetupTestPage() { + let tab = await BrowserTestUtils.openNewForegroundTab( + gBrowser, + TEST_TOP_PAGE + ); + + // Create a first-party and a third-party iframe in the tab. + let { firstPartyBC, thirdPartyBC } = await SpecialPowers.spawn( + tab.linkedBrowser, + [TEST_TOP_PAGE, TEST_THIRD_PARTY_PAGE], + async (firstPartySrc, thirdPartySrc) => { + let firstPartyFrame = content.document.createElement("iframe"); + let loading = new content.Promise(resolve => { + firstPartyFrame.onload = resolve; + }); + content.document.body.appendChild(firstPartyFrame); + firstPartyFrame.src = firstPartySrc; + await loading; + + let thirdPartyFrame = content.document.createElement("iframe"); + loading = new content.Promise(resolve => { + thirdPartyFrame.onload = resolve; + }); + content.document.body.appendChild(thirdPartyFrame); + thirdPartyFrame.src = thirdPartySrc; + await loading; + + return { + firstPartyBC: firstPartyFrame.browsingContext, + thirdPartyBC: thirdPartyFrame.browsingContext, + }; + } + ); + + return { tab, firstPartyBC, thirdPartyBC }; +} + +async function openAndSetupTestPageForPopup() { + let tab = await BrowserTestUtils.openNewForegroundTab( + gBrowser, + TEST_TOP_PAGE + ); + + // Create a third-party iframe and a pop-up from this iframe. + let { thirdPartyFrameBC, popupBC } = await SpecialPowers.spawn( + tab.linkedBrowser, + [TEST_THIRD_PARTY_PAGE], + async thirdPartySrc => { + let thirdPartyFrame = content.document.createElement("iframe"); + let loading = new content.Promise(resolve => { + thirdPartyFrame.onload = resolve; + }); + content.document.body.appendChild(thirdPartyFrame); + thirdPartyFrame.src = thirdPartySrc; + await loading; + + let popupBC = await SpecialPowers.spawn( + thirdPartyFrame, + [thirdPartySrc], + async src => { + let win; + let loading = new content.Promise(resolve => { + win = content.open(src); + win.onload = resolve; + }); + await loading; + + return win.browsingContext; + } + ); + + return { + thirdPartyFrameBC: thirdPartyFrame.browsingContext, + popupBC, + }; + } + ); + + return { tab, thirdPartyFrameBC, popupBC }; +} + +async function verifyResultInTab(tab, firstPartyBC, thirdPartyBC, expected) { + let testWindowOuter = enabled => { + if (enabled) { + ok( + content.wrappedJSObject.outerHeight == + content.wrappedJSObject.innerHeight && + content.wrappedJSObject.outerWidth == + content.wrappedJSObject.innerWidth, + "Fingerprinting target WindowOuterSize is enabled for WindowOuterSize." + ); + } else { + ok( + content.wrappedJSObject.outerHeight != + content.wrappedJSObject.innerHeight || + content.wrappedJSObject.outerWidth != + content.wrappedJSObject.innerWidth, + "Fingerprinting target WindowOuterSize is not enabled for WindowOuterSize." + ); + } + }; + + let testHWConcurrency = expected => { + is( + content.wrappedJSObject.navigator.hardwareConcurrency, + expected, + "The hardware concurrency is expected." + ); + }; + + info( + `Verify top-level context with fingerprinting protection is ${ + expected.top ? "enabled" : "not enabled" + }.` + ); + await SpecialPowers.spawn( + tab.linkedBrowser, + [expected.windowOuter.top], + testWindowOuter + ); + let expectHWConcurrencyTop = expected.hwConcurrency.top + ? SPOOFED_HW_CONCURRENCY + : DEFAULT_HW_CONCURRENCY; + await SpecialPowers.spawn( + tab.linkedBrowser, + [expectHWConcurrencyTop], + testHWConcurrency + ); + + let workerTopResult = await runFunctionInWorker( + tab.linkedBrowser, + async _ => { + return navigator.hardwareConcurrency; + } + ); + is( + workerTopResult, + expectHWConcurrencyTop, + "The top worker reports the expected HW concurrency." + ); + + info( + `Verify first-party context with fingerprinting protection is ${ + expected.firstParty ? "enabled" : "not enabled" + }.` + ); + await SpecialPowers.spawn( + firstPartyBC, + [expected.windowOuter.firstParty], + testWindowOuter + ); + let expectHWConcurrencyFirstParty = expected.hwConcurrency.firstParty + ? SPOOFED_HW_CONCURRENCY + : DEFAULT_HW_CONCURRENCY; + await SpecialPowers.spawn( + firstPartyBC, + [expectHWConcurrencyFirstParty], + testHWConcurrency + ); + + let workerFirstPartyResult = await runFunctionInWorker( + firstPartyBC, + async _ => { + return navigator.hardwareConcurrency; + } + ); + is( + workerFirstPartyResult, + expectHWConcurrencyFirstParty, + "The first-party worker reports the expected HW concurrency." + ); + + info( + `Verify third-party context with fingerprinting protection is ${ + expected.thirdParty ? "enabled" : "not enabled" + }.` + ); + await SpecialPowers.spawn( + thirdPartyBC, + [expected.windowOuter.thirdParty], + testWindowOuter + ); + let expectHWConcurrencyThirdParty = expected.hwConcurrency.thirdParty + ? SPOOFED_HW_CONCURRENCY + : DEFAULT_HW_CONCURRENCY; + await SpecialPowers.spawn( + thirdPartyBC, + [expectHWConcurrencyThirdParty], + testHWConcurrency + ); + + let workerThirdPartyResult = await runFunctionInWorker( + thirdPartyBC, + async _ => { + return navigator.hardwareConcurrency; + } + ); + is( + workerThirdPartyResult, + expectHWConcurrencyThirdParty, + "The third-party worker reports the expected HW concurrency." + ); +} + +add_setup(async function () { + await SpecialPowers.pushPrefEnv({ + set: [ + ["privacy.fingerprintingProtection.remoteOverrides.testing", true], + ["privacy.fingerprintingProtection", true], + ["privacy.fingerprintingProtection.pbmode", true], + ], + }); + + registerCleanupFunction(() => { + Services.rfp.cleanAllOverrides(); + }); +}); + +add_task(async function () { + // Add initial empty record. + let db = RemoteSettings(COLLECTION_NAME).db; + await db.importChanges({}, Date.now(), []); + + for (let test of TEST_CASES) { + // Create a promise for waiting the overrides get updated. + let promise = promiseObserver("fpp-test:set-overrides-finishes"); + + // Trigger the fingerprinting overrides update by a remote settings sync. + await RemoteSettings(COLLECTION_NAME).emit("sync", { + data: { + current: test.entires, + }, + }); + await promise; + + ok(true, "Got overrides update"); + + let { tab, firstPartyBC, thirdPartyBC } = await openAndSetupTestPage(); + + await verifyResultInTab(tab, firstPartyBC, thirdPartyBC, test.expects); + + BrowserTestUtils.removeTab(tab); + } + + db.clear(); +}); + +// A test to verify that a pop-up inherits overrides from the third-party iframe +// that opens it. +add_task(async function test_popup_inheritance() { + // Add initial empty record. + let db = RemoteSettings(COLLECTION_NAME).db; + await db.importChanges({}, Date.now(), []); + + // Create a promise for waiting the overrides get updated. + let promise = promiseObserver("fpp-test:set-overrides-finishes"); + + // Trigger the fingerprinting overrides update by a remote settings sync. + await RemoteSettings(COLLECTION_NAME).emit("sync", { + data: { + current: [ + { + id: "1", + last_modified: 1000000000000001, + overrides: "+WindowOuterSize", + firstPartyDomain: "example.com", + thirdPartyDomain: "example.org", + }, + ], + }, + }); + await promise; + + let { tab, thirdPartyFrameBC, popupBC } = + await openAndSetupTestPageForPopup(); + + // Ensure the third-party iframe has the correct overrides. + await SpecialPowers.spawn(thirdPartyFrameBC, [], _ => { + ok( + content.wrappedJSObject.outerHeight == + content.wrappedJSObject.innerHeight && + content.wrappedJSObject.outerWidth == + content.wrappedJSObject.innerWidth, + "Fingerprinting target WindowOuterSize is enabled for third-party iframe." + ); + }); + + // Verify the popup inherits overrides from the opener. + await SpecialPowers.spawn(popupBC, [], _ => { + ok( + content.wrappedJSObject.outerHeight == + content.wrappedJSObject.innerHeight && + content.wrappedJSObject.outerWidth == + content.wrappedJSObject.innerWidth, + "Fingerprinting target WindowOuterSize is enabled for the pop-up." + ); + + content.close(); + }); + + BrowserTestUtils.removeTab(tab); + + db.clear(); +}); |