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
"use strict";
const { RemoteSettings } = ChromeUtils.importESModule(
);
const COLLECTION_NAME = "fingerprinting-protection-overrides";
// The javascript bitwise operator only support 32bits. So, we only test
// RFPTargets that is under low 32 bits.
const TARGET_DEFAULT = extractLow32Bits(
Services.rfp.enabledFingerprintingProtections
);
const TARGET_PointerEvents = 0x00000002;
const TARGET_CanvasRandomization = 0x000000100;
const TARGET_WindowOuterSize = 0x002000000;
const TARGET_Gamepad = 0x00800000;
const TEST_PAGE =
getRootDirectory(gTestPath).replace(
) + "empty.html";
const TEST_ANOTHER_PAGE =
getRootDirectory(gTestPath).replace(
) + "empty.html";
// A helper function to filter high 32 bits.
function extractLow32Bits(value) {
return value & 0xffffffff;
}
const TEST_CASES = [
// Test simple addition.
{
entires: [
{
id: "1",
last_modified: 1000000000000001,
overrides: "+WindowOuterSize",
firstPartyDomain: "example.org",
},
],
expects: [
{
domain: "example.org",
overrides: TARGET_DEFAULT | TARGET_WindowOuterSize,
},
{ domain: "example.com", noEntry: true },
],
},
// Test simple subtraction
{
entires: [
{
id: "1",
last_modified: 1000000000000001,
overrides: "-WindowOuterSize",
firstPartyDomain: "example.org",
},
],
expects: [
{
domain: "example.org",
overrides: TARGET_DEFAULT & ~TARGET_WindowOuterSize,
},
{ domain: "example.com", noEntry: true },
],
},
// Test simple subtraction for default targets.
{
entires: [
{
id: "1",
last_modified: 1000000000000001,
overrides: "-CanvasRandomization",
firstPartyDomain: "example.org",
},
],
expects: [
{
domain: "example.org",
overrides: TARGET_DEFAULT & ~TARGET_CanvasRandomization,
},
{ domain: "example.com", noEntry: true },
],
},
// Test multiple targets
{
entires: [
{
id: "1",
last_modified: 1000000000000001,
overrides: "+WindowOuterSize,+PointerEvents,-Gamepad",
firstPartyDomain: "example.org",
},
],
expects: [
{
domain: "example.org",
overrides:
(TARGET_DEFAULT | TARGET_WindowOuterSize | TARGET_PointerEvents) &
~TARGET_Gamepad,
},
{ domain: "example.com", noEntry: true },
],
},
// Test multiple entries
{
entires: [
{
id: "1",
last_modified: 1000000000000001,
overrides: "+WindowOuterSize,+PointerEvents,-Gamepad",
firstPartyDomain: "example.org",
},
{
id: "2",
last_modified: 1000000000000001,
overrides: "+Gamepad",
firstPartyDomain: "example.com",
},
],
expects: [
{
domain: "example.org",
overrides:
(TARGET_DEFAULT | TARGET_WindowOuterSize | TARGET_PointerEvents) &
~TARGET_Gamepad,
},
{
domain: "example.com",
overrides: TARGET_DEFAULT | TARGET_Gamepad,
},
],
},
// Test an entry with both first-party and third-party domains.
{
entires: [
{
id: "1",
last_modified: 1000000000000001,
overrides: "+WindowOuterSize,+PointerEvents,-Gamepad",
firstPartyDomain: "example.org",
thirdPartyDomain: "example.com",
},
],
expects: [
{
domain: "example.org,example.com",
overrides:
(TARGET_DEFAULT | TARGET_WindowOuterSize | TARGET_PointerEvents) &
~TARGET_Gamepad,
},
],
},
// Test an entry with the first-party set to a wildcard.
{
entires: [
{
id: "1",
last_modified: 1000000000000001,
overrides: "+WindowOuterSize,+PointerEvents,-Gamepad",
firstPartyDomain: "*",
},
],
expects: [
{
domain: "*",
overrides:
(TARGET_DEFAULT | TARGET_WindowOuterSize | TARGET_PointerEvents) &
~TARGET_Gamepad,
},
],
},
// Test a entry with the first-party set to a wildcard and the third-party set
// to a domain.
{
entires: [
{
id: "1",
last_modified: 1000000000000001,
overrides: "+WindowOuterSize,+PointerEvents,-Gamepad",
firstPartyDomain: "*",
thirdPartyDomain: "example.com",
},
],
expects: [
{
domain: "*,example.com",
overrides:
(TARGET_DEFAULT | TARGET_WindowOuterSize | TARGET_PointerEvents) &
~TARGET_Gamepad,
},
],
},
// Test an entry with all targets disabled using '-AllTargets' but only enable one target.
{
entires: [
{
id: "1",
last_modified: 1000000000000001,
overrides: "-AllTargets,+WindowOuterSize",
firstPartyDomain: "*",
thirdPartyDomain: "example.com",
},
],
expects: [
{
domain: "*,example.com",
overrides: TARGET_WindowOuterSize,
},
],
},
// Test an entry with all targets disabled using '-AllTargets' but enable some targets.
{
entires: [
{
id: "1",
last_modified: 1000000000000001,
overrides: "-AllTargets,+WindowOuterSize,+Gamepad",
firstPartyDomain: "*",
thirdPartyDomain: "example.com",
},
],
expects: [
{
domain: "*,example.com",
overrides: TARGET_WindowOuterSize | TARGET_Gamepad,
},
],
},
// Test an invalid entry with only third party domain.
{
entires: [
{
id: "1",
last_modified: 1000000000000001,
overrides: "+WindowOuterSize,+PointerEvents,-Gamepad",
thirdPartyDomain: "example.com",
},
],
expects: [
{
domain: "example.com",
noEntry: true,
},
],
},
// Test an invalid entry with both wildcards.
{
entires: [
{
id: "1",
last_modified: 1000000000000001,
overrides: "+WindowOuterSize,+PointerEvents,-Gamepad",
firstPartyDomain: "*",
thirdPartyDomain: "*",
},
],
expects: [
{
domain: "example.org",
noEntry: true,
},
],
},
];
add_setup(async function () {
await SpecialPowers.pushPrefEnv({
set: [["privacy.fingerprintingProtection.remoteOverrides.testing", true]],
});
registerCleanupFunction(() => {
Services.rfp.cleanAllOverrides();
});
});
add_task(async function test_remote_settings() {
// Add initial empty record.
let db = RemoteSettings(COLLECTION_NAME).db;
await db.importChanges({}, Date.now(), []);
for (let test of TEST_CASES) {
info(`Testing with entry ${JSON.stringify(test.entires)}`);
// 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");
for (let expect of test.expects) {
// Get the addition and subtraction flags for the domain.
try {
let overrides = extractLow32Bits(
Services.rfp.getFingerprintingOverrides(expect.domain)
);
// Verify if the flags are matching to expected values.
is(overrides, expect.overrides, "The override value is correct.");
} catch (e) {
ok(expect.noEntry, "The override entry doesn't exist.");
}
}
}
db.clear();
});
add_task(async function test_remote_settings_pref() {
// Add initial empty record.
let db = RemoteSettings(COLLECTION_NAME).db;
await db.importChanges({}, Date.now(), []);
for (let test of TEST_CASES) {
info(`Testing with entry ${JSON.stringify(test.entires)}`);
// Disable remote overrides
await SpecialPowers.pushPrefEnv({
set: [
["privacy.fingerprintingProtection.remoteOverrides.enabled", false],
],
});
// 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");
for (let expect of test.expects) {
try {
// Check for the existance of RFP overrides
Services.rfp.getFingerprintingOverrides(expect.domain);
ok(
false,
"This line should never run as the override should not exist and the previous line would throw an exception"
);
} catch (e) {
ok(true, "Received an exception as expected");
}
}
}
db.clear();
});
add_task(async function test_pref() {
for (let test of TEST_CASES) {
info(`Testing with entry ${JSON.stringify(test.entires)}`);
// Create a promise for waiting the overrides get updated.
let promise = promiseObserver("fpp-test:set-overrides-finishes");
// Trigger the fingerprinting overrides update by setting the pref.
await SpecialPowers.pushPrefEnv({
set: [
[
"privacy.fingerprintingProtection.granularOverrides",
JSON.stringify(test.entires),
],
],
});
await promise;
ok(true, "Got overrides update");
for (let expect of test.expects) {
try {
// Get the addition and subtraction flags for the domain.
let overrides = extractLow32Bits(
Services.rfp.getFingerprintingOverrides(expect.domain)
);
// Verify if the flags are matching to expected values.
is(overrides, expect.overrides, "The override value is correct.");
} catch (e) {
ok(expect.noEntry, "The override entry doesn't exist.");
}
}
}
});
// Verify if the pref overrides the remote settings.
add_task(async function test_pref_override_remote_settings() {
// Add initial empty record.
let db = RemoteSettings(COLLECTION_NAME).db;
await db.importChanges({}, Date.now(), []);
// Trigger a remote settings sync.
let promise = promiseObserver("fpp-test:set-overrides-finishes");
await RemoteSettings(COLLECTION_NAME).emit("sync", {
data: {
current: [
{
id: "1",
last_modified: 1000000000000001,
overrides: "+WindowOuterSize",
firstPartyDomain: "example.org",
},
],
},
});
await promise;
// Then, setting the pref.
promise = promiseObserver("fpp-test:set-overrides-finishes");
await SpecialPowers.pushPrefEnv({
set: [
[
"privacy.fingerprintingProtection.granularOverrides",
JSON.stringify([
{
id: "1",
last_modified: 1000000000000001,
overrides: "+PointerEvents,-WindowOuterSize,-Gamepad",
firstPartyDomain: "example.org",
},
]),
],
],
});
await promise;
// Get the addition and subtraction flags for the domain.
let overrides = extractLow32Bits(
Services.rfp.getFingerprintingOverrides("example.org")
);
// Verify if the flags are matching to the pref settings.
is(
overrides,
(TARGET_DEFAULT | TARGET_PointerEvents) &
~TARGET_Gamepad &
~TARGET_WindowOuterSize,
"The override addition value is correct."
);
db.clear();
});
// assertion in nsRFPService::GetOverriddenFingerprintingSettingsForChannel().
add_task(async function test_beacon_request() {
// Open an empty page.
let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, TEST_PAGE);
await SpecialPowers.spawn(
tab.linkedBrowser,
[TEST_ANOTHER_PAGE],
async url => {
// Create a third-party iframe
let ifr = content.document.createElement("iframe");
await new content.Promise(resolve => {
ifr.onload = resolve;
content.document.body.appendChild(ifr);
ifr.src = url;
});
await SpecialPowers.spawn(ifr, [url], url => {
// Sending the beacon request right before the tab navigates away.
content.addEventListener("unload", _ => {
let value = ["text"];
let blob = new Blob(value, {
type: "application/x-www-form-urlencoded",
});
content.navigator.sendBeacon(url, blob);
});
});
// Navigate the tab to another page.
content.location = url;
}
);
await BrowserTestUtils.browserLoaded(
tab.linkedBrowser,
false,
TEST_ANOTHER_PAGE
);
ok(true, "Successfully navigates away.");
BrowserTestUtils.removeTab(tab);
});