Source code
Revision control
Copy as Markdown
Other Tools
Test Info:
-
CI results over the last 21 days
- Manifest: toolkit/components/antitracking/test/browser/browser.toml
/* Any copyright is dedicated to the Public Domain.
"use strict";
// example.org (container 1) -> example.com (interaction) -> example.org
// -> example.com (interaction) -> test1.example.org (container 2, new tab)
//
// Navigating back to a site after interacting with another grants the latter
// storage access under the former (DynamicFpiNavigationHeuristic). The heuristic
// reads the session history and the user activation of the tab the load runs
// new tab in another container, which must not be granted access from those of
// the tab it is leaving.
const HOST_BOUND = "test1.example.org";
const OTHER_CONTAINER = 1;
const BOUND_CONTAINER = 2;
const GRANT = `3rdPartyStorage^${ORIGIN_INTERACTED}`;
function url(origin) {
return (
"page.html"
);
}
function isGranted(origin, userContextId) {
return (
Services.perms.testPermissionFromPrincipal(
Services.scriptSecurityManager.createContentPrincipal(
Services.io.newURI(origin),
{ userContextId }
),
GRANT
) == Services.perms.ALLOW_ACTION
);
}
// Navigates from the page without user activation, so the navigation continues
// the extended navigation of the tab, which holds the user activation the
// heuristic reads. Injected so the page, not the system principal of the
// content task, triggers the load.
async function navigateByScript(browser, targetURL) {
await SpecialPowers.spawn(browser, [targetURL], target => {
let script = content.document.createElement("script");
script.textContent = `location.href = ${JSON.stringify(target)};`;
content.document.body.appendChild(script);
});
}
async function interactAndLeave(browser, targetURL) {
let loaded = BrowserTestUtils.browserLoaded(
browser,
false,
url(ORIGIN_INTERACTED)
);
await navigateByScript(browser, url(ORIGIN_INTERACTED));
await loaded;
await SpecialPowers.spawn(browser, [], () => {
SpecialPowers.wrap(content.document).notifyUserGestureActivation();
content.document.userInteractionForTesting();
SpecialPowers.wrap(content.document).clearUserGestureActivation();
});
await navigateByScript(browser, targetURL);
}
add_setup(async function () {
await SpecialPowers.pushPrefEnv({
set: [
["privacy.antitracking.enableWebcompat", true],
["privacy.restrict3rdpartystorage.heuristic.navigation", true],
// The heuristic reads the user activation of the extended navigation
// which Bounce Tracking Protection records per tab.
[
"privacy.bounceTrackingProtection.mode",
Ci.nsIBounceTrackingProtection.MODE_ENABLED_DRY_RUN,
],
// Otherwise permissions, the storage access grant included, are shared
// across containers.
["permissions.isolateBy.userContext", true],
["privacy.userContext.enabled", true],
["privacy.containers.switchDuringNavigation.enabled", true],
],
});
ContextualIdentityService.setSiteAssociation(HOST_BOUND, BOUND_CONTAINER);
registerCleanupFunction(() => {
ContextualIdentityService.removeSiteAssociation(HOST_BOUND);
Services.perms.removeByType(GRANT);
});
});
add_task(async function test_retargeted_load_is_not_granted_storage_access() {
let tab = BrowserTestUtils.addTab(gBrowser, url(ORIGIN_RETURNED), {
userContextId: OTHER_CONTAINER,
});
gBrowser.selectedTab = tab;
let browser = tab.linkedBrowser;
await BrowserTestUtils.browserLoaded(browser, false, url(ORIGIN_RETURNED));
info("Without a container switch the heuristic grants access.");
let loaded = BrowserTestUtils.browserLoaded(
browser,
false,
url(ORIGIN_RETURNED)
);
await interactAndLeave(browser, url(ORIGIN_RETURNED));
await loaded;
ok(
isGranted(ORIGIN_RETURNED, OTHER_CONTAINER),
`${ORIGIN_INTERACTED} is granted storage access under ${ORIGIN_RETURNED} ` +
`in container ${OTHER_CONTAINER}`
);
info("Retargeted into another container it must not.");
let retargetedPromise = BrowserTestUtils.waitForNewTab(
gBrowser,
url(ORIGIN_BOUND),
true
);
await interactAndLeave(browser, url(ORIGIN_BOUND));
let retargeted = await retargetedPromise;
is(
retargeted.userContextId,
BOUND_CONTAINER,
"The bound host landed in the container it is bound to"
);
ok(
!isGranted(ORIGIN_BOUND, BOUND_CONTAINER),
`${ORIGIN_INTERACTED} is not granted storage access under ${ORIGIN_BOUND} ` +
`in container ${BOUND_CONTAINER}`
);
BrowserTestUtils.removeTab(retargeted);
BrowserTestUtils.removeTab(tab);
});