Source code

Revision control

Copy as Markdown

Other Tools

Test Info:

/* 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 https://mozilla.org/MPL/2.0/. */
"use strict";
const { RemoteSettings } = ChromeUtils.importESModule(
);
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(
TOP_DOMAIN
) + "empty.html";
const TEST_THIRD_PARTY_PAGE =
getRootDirectory(gTestPath).replace(
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();
});