Source code
Revision control
Copy as Markdown
Other Tools
Test Info:
"use strict";
const MIRROR_PREF = "privacy.trackingprotection.content.mirror.enabled";
const PROT_ENABLED = "privacy.trackingprotection.content.protection.enabled";
const PROT_ENGINES = "privacy.trackingprotection.content.protection.engines";
const PROT_ENGINES_PBM =
"privacy.trackingprotection.content.protection.engines.pbmode";
const ANNO_ENABLED = "privacy.trackingprotection.content.annotation.enabled";
const ANNO_ENGINES = "privacy.trackingprotection.content.annotation.engines";
const ANNO_ENGINES_PBM =
"privacy.trackingprotection.content.annotation.engines.pbmode";
// The content prefs the mirror writes. They are included in every pushPrefEnv
// that enables the mirror so popping the environment restores them - the mirror
// changes them out of band while enabled, and pop restores the pre-test state
// regardless of the placeholder values used here.
const CONTENT_PREF_RESET = [
[PROT_ENABLED, false],
[PROT_ENGINES, ""],
[PROT_ENGINES_PBM, ""],
[ANNO_ENABLED, false],
[ANNO_ENGINES, ""],
[ANNO_ENGINES_PBM, ""],
];
// Set to true as a side effect of changing ETP settings (it gates a one-time
// allow-list onboarding infobar; see UrlClassifierExceptionListService). It is
// not a mirror pref, but it gets written while the ETP prefs are toggled, so it
// is pinned once in add_setup() to keep popping from leaking it.
const HAS_INTERACTED_PREF =
"privacy.trackingprotection.allow_list.hasUserInteractedWithETPSettings";
// All ETP source prefs the mirror reads, defaulted off so each task's
// expected output is fully determined by its overrides.
const ETP_OFF = {
"privacy.trackingprotection.enabled": false,
"privacy.trackingprotection.pbmode.enabled": false,
"privacy.trackingprotection.annotate_channels": false,
"privacy.annotate_channels.strict_list.enabled": false,
"privacy.annotate_channels.strict_list.pbmode.enabled": false,
"privacy.trackingprotection.fingerprinting.enabled": false,
"privacy.trackingprotection.cryptomining.enabled": false,
"privacy.trackingprotection.socialtracking.enabled": false,
"privacy.trackingprotection.emailtracking.enabled": false,
"privacy.trackingprotection.emailtracking.pbmode.enabled": false,
};
// The mirror coalesces pref changes onto a runnable, so wait a turn of the
// main-thread event loop for the pending Sync() to run before asserting.
function flushMirror() {
return new Promise(resolve => Services.tm.dispatchToMainThread(resolve));
}
// Push an all-off ETP baseline with the given overrides and enable the mirror,
// then wait for the coalesced Sync(). Because the mirror recomputes from the
// final pref state once per turn, the order prefs are applied within the env
// does not matter.
async function enableMirror(overrides = {}) {
const etp = { ...ETP_OFF, ...overrides };
await SpecialPowers.pushPrefEnv({
set: [...Object.entries(etp), [MIRROR_PREF, true], ...CONTENT_PREF_RESET],
});
await flushMirror();
}
// The mirror only initializes from ContentClassifierService::Init, which runs
// lazily on the first real channel classification. Force that here so the
// singleton (and its pref callbacks) are live for every task.
add_setup(async function () {
// Pin the interaction flag for the whole file; the ETP prefs the tasks toggle
// would otherwise flip it and leave it changed past the suite.
await SpecialPowers.pushPrefEnv({ set: [[HAS_INTERACTED_PREF, false]] });
let tab = await BrowserTestUtils.openNewForegroundTab(
gBrowser,
TEST_TOP_PAGE
);
BrowserTestUtils.removeTab(tab);
});
// With the master pref off (the default), changing an ETP pref must NOT touch
// the content prefs.
add_task(async function test_mirror_off_leaves_content_prefs_untouched() {
// Make sure the mirror is disabled first.
await SpecialPowers.pushPrefEnv({ set: [[MIRROR_PREF, false]] });
// Then flip an ETP pref; with the mirror off it must not touch content prefs.
await SpecialPowers.pushPrefEnv({
set: [["privacy.trackingprotection.enabled", true]],
});
await flushMirror();
is(
Services.prefs.getStringPref(PROT_ENGINES),
"test_block",
"protection.engines stays at default"
);
is(
Services.prefs.getBoolPref(PROT_ENABLED),
false,
"protection stays disabled"
);
is(
Services.prefs.getStringPref(ANNO_ENGINES),
"test_annotate",
"annotation.engines stays at default"
);
});
// Each ETP feature toggle maps to its content classifier engine in the
// protection (blocking) list.
add_task(async function test_protection_per_feature_mapping() {
const cases = [
["privacy.trackingprotection.enabled", "trackers"],
["privacy.trackingprotection.fingerprinting.enabled", "fingerprinters"],
["privacy.trackingprotection.cryptomining.enabled", "cryptominers"],
["privacy.trackingprotection.socialtracking.enabled", "social-trackers"],
["privacy.trackingprotection.emailtracking.enabled", "email-trackers"],
];
for (const [etpPref, engine] of cases) {
await enableMirror({ [etpPref]: true });
is(
Services.prefs.getStringPref(PROT_ENGINES),
engine,
`${etpPref} -> protection ${engine}`
);
is(
Services.prefs.getBoolPref(PROT_ENABLED),
true,
`${etpPref} enables protection`
);
is(
Services.prefs.getStringPref(ANNO_ENGINES),
"",
`${etpPref} leaves annotation empty`
);
}
});
// Multiple feature toggles join into one comma-separated list, in the mirror's
// mapping order (trackers, fingerprinters, cryptominers, social, email).
add_task(async function test_protection_multiple_features_joined() {
await enableMirror({
"privacy.trackingprotection.enabled": true,
"privacy.trackingprotection.cryptomining.enabled": true,
"privacy.trackingprotection.emailtracking.enabled": true,
});
is(
Services.prefs.getStringPref(PROT_ENGINES),
"trackers,cryptominers,email-trackers",
"enabled features joined in mapping order"
);
});
// The private-browsing ETP prefs drive only the .engines.pbmode lists, and the
// normal prefs drive only the non-PBM lists.
add_task(async function test_pbm_prefs_drive_pbmode_list_only() {
await enableMirror({
"privacy.trackingprotection.pbmode.enabled": true,
"privacy.trackingprotection.emailtracking.pbmode.enabled": true,
});
is(
Services.prefs.getStringPref(PROT_ENGINES),
"",
"normal protection empty (only PBM prefs on)"
);
is(
Services.prefs.getStringPref(PROT_ENGINES_PBM),
"trackers,email-trackers",
"PBM prefs map to pbmode protection list"
);
is(
Services.prefs.getBoolPref(PROT_ENABLED),
true,
"protection enabled from PBM list alone"
);
});
// fingerprinting/cryptomining/socialtracking have no PBM-specific pref, so the
// single toggle applies to both the normal and PBM protection lists.
add_task(async function test_modeless_features_apply_to_both_modes() {
await enableMirror({
"privacy.trackingprotection.fingerprinting.enabled": true,
});
is(
Services.prefs.getStringPref(PROT_ENGINES),
"fingerprinters",
"normal list has fingerprinters"
);
is(
Services.prefs.getStringPref(PROT_ENGINES_PBM),
"fingerprinters",
"pbmode list has fingerprinters too"
);
});
// annotate_channels drives the annotation list (trackers, fingerprinters,
// cryptominers, social-trackers); it gates both normal and PBM annotation.
// trackers-content is gated separately by the strict-list pref and is absent
// here.
add_task(async function test_annotate_channels_drives_annotation_list() {
await enableMirror({
"privacy.trackingprotection.annotate_channels": true,
});
is(
Services.prefs.getStringPref(ANNO_ENGINES),
"trackers,fingerprinters,cryptominers,social-trackers",
"annotate_channels maps to annotation engines"
);
is(
Services.prefs.getStringPref(ANNO_ENGINES_PBM),
"trackers,fingerprinters,cryptominers,social-trackers",
"annotate_channels gates PBM annotation too"
);
is(Services.prefs.getBoolPref(ANNO_ENABLED), true, "annotation enabled");
is(
Services.prefs.getStringPref(PROT_ENGINES),
"",
"protection list untouched by annotate_channels"
);
is(Services.prefs.getBoolPref(PROT_ENABLED), false, "protection disabled");
});
// trackers-content (the level-2 content-tracker annotation) is gated by the
// strict-list prefs, independently of annotate_channels and per mode.
add_task(async function test_strict_list_drives_trackers_content_annotation() {
await enableMirror({
"privacy.annotate_channels.strict_list.enabled": true,
});
is(
Services.prefs.getStringPref(ANNO_ENGINES),
"trackers-content",
"strict_list.enabled adds trackers-content to normal annotation"
);
is(
Services.prefs.getStringPref(ANNO_ENGINES_PBM),
"",
"No PBM annotation when the PBM pref is off"
);
await enableMirror({
"privacy.annotate_channels.strict_list.pbmode.enabled": true,
});
is(
Services.prefs.getStringPref(ANNO_ENGINES),
"",
"Switch normal strict-list off will clear the pref for normal annotation"
);
is(
Services.prefs.getStringPref(ANNO_ENGINES_PBM),
"trackers-content",
"strict_list.pbmode.enabled adds trackers-content to PBM annotation"
);
});
// Turning the master pref off leaves the last derived content prefs in place;
// the mirror simply stops updating them.
add_task(async function test_disable_keeps_last_values() {
await enableMirror({ "privacy.trackingprotection.enabled": true });
is(
Services.prefs.getStringPref(PROT_ENGINES),
"trackers",
"mirror derived trackers while on"
);
is(
Services.prefs.getBoolPref(PROT_ENABLED),
true,
"protection enabled while on"
);
// Disable the mirror and verify the content prefs are kept.
await SpecialPowers.pushPrefEnv({ set: [[MIRROR_PREF, false]] });
await flushMirror();
is(
Services.prefs.getStringPref(PROT_ENGINES),
"trackers",
"protection.engines kept after disable"
);
is(
Services.prefs.getBoolPref(PROT_ENABLED),
true,
"protection still enabled after mirror off"
);
// Further ETP changes while disabled must not update the content prefs.
await SpecialPowers.pushPrefEnv({
set: [["privacy.trackingprotection.fingerprinting.enabled", true]],
});
await flushMirror();
is(
Services.prefs.getStringPref(PROT_ENGINES),
"trackers",
"ETP change ignored while mirror off"
);
});