Source code

Revision control

Copy as Markdown

Other Tools

Test Info: Warnings

//
// Bug 1724376 - Tests for the privilege requestStorageAccessForOrigin API.
//
/* import-globals-from storageAccessAPIHelpers.js */
"use strict";
const TEST_ANOTHER_TRACKER_DOMAIN = "https://itisatracker.org/";
const TEST_ANOTHER_TRACKER_PAGE =
TEST_ANOTHER_TRACKER_DOMAIN + TEST_PATH + "3rdParty.html";
const TEST_ANOTHER_4TH_PARTY_DOMAIN = "https://test1.example.org/";
const TEST_ANOTHER_4TH_PARTY_PAGE =
TEST_ANOTHER_4TH_PARTY_DOMAIN + TEST_PATH + "3rdParty.html";
// Insert an iframe with the given id into the content.
async function insertSubFrame(browser, url, id) {
return SpecialPowers.spawn(browser, [url, id], async (url, id) => {
let ifr = content.document.createElement("iframe");
ifr.setAttribute("id", id);
let loaded = ContentTaskUtils.waitForEvent(ifr, "load", false);
content.document.body.appendChild(ifr);
ifr.src = url;
await loaded;
});
}
// Run the given script in the iframe with the given id.
function runScriptInSubFrame(browser, id, script) {
return SpecialPowers.spawn(
browser,
[{ callback: script.toString(), id }],
async obj => {
await new content.Promise(resolve => {
let ifr = content.document.getElementById(obj.id);
content.addEventListener("message", function msg(event) {
if (event.data.type == "finish") {
content.removeEventListener("message", msg);
resolve();
return;
}
if (event.data.type == "ok") {
ok(event.data.what, event.data.msg);
return;
}
if (event.data.type == "info") {
info(event.data.msg);
return;
}
ok(false, "Unknown message");
});
ifr.contentWindow.postMessage(obj.callback, "*");
});
}
);
}
function waitStoragePermission(trackingOrigin) {
return TestUtils.topicObserved("perm-changed", aSubject => {
let permission = aSubject.QueryInterface(Ci.nsIPermission);
let uri = Services.io.newURI(TEST_DOMAIN);
return (
permission.type == `3rdPartyStorage^${trackingOrigin}` &&
permission.principal.equalsURI(uri)
);
});
}
function clearStoragePermission(trackingOrigin) {
return SpecialPowers.removePermission(
`3rdPartyStorage^${trackingOrigin}`,
TEST_TOP_PAGE
);
}
function triggerCommand(button) {
let notifications = PopupNotifications.panel.children;
let notification = notifications[0];
EventUtils.synthesizeMouseAtCenter(notification[button], {});
}
function triggerMainCommand() {
triggerCommand("button");
}
function triggerSecondaryCommand() {
triggerCommand("secondaryButton");
}
add_setup(async function () {
await SpecialPowers.pushPrefEnv({
set: [
["dom.storage_access.auto_grants", true],
["dom.storage_access.auto_grants.delayed", false],
["dom.storage_access.enabled", true],
["dom.storage_access.prompt.testing", false],
["privacy.trackingprotection.enabled", false],
["privacy.trackingprotection.pbmode.enabled", false],
["privacy.trackingprotection.annotate_channels", true],
// Bug 1617611: Fix all the tests broken by "cookies SameSite=lax by default"
["network.cookie.sameSite.laxByDefault", false],
["network.cookie.CHIPS.enabled", true],
],
});
await UrlClassifierTestUtils.addTestTrackers();
registerCleanupFunction(() => {
SpecialPowers.clearUserPref("network.cookie.sameSite.laxByDefault");
UrlClassifierTestUtils.cleanupTestTrackers();
});
});
add_task(async function test_api_only_available_in_privilege_scope() {
let tab = await BrowserTestUtils.openNewForegroundTab(
gBrowser,
TEST_TOP_PAGE
);
let browser = tab.linkedBrowser;
await SpecialPowers.spawn(browser, [], async _ => {
ok(
content.document.requestStorageAccessForOrigin,
"The privilege API is available in system privilege code."
);
});
// Open an iframe and check if the privilege is not available in content code.
await insertSubFrame(browser, TEST_3RD_PARTY_PAGE, "test");
await runScriptInSubFrame(browser, "test", async function check() {
ok(
!document.requestStorageAccessForOrigin,
"The privilege API is not available in content code."
);
});
BrowserTestUtils.removeTab(tab);
});
add_task(async function test_privilege_api_with_reject_tracker() {
await SpecialPowers.pushPrefEnv({
set: [
[
"network.cookie.cookieBehavior",
Ci.nsICookieService.BEHAVIOR_REJECT_TRACKER,
],
[
"network.cookie.cookieBehavior.pbmode",
Ci.nsICookieService.BEHAVIOR_REJECT_TRACKER,
],
],
});
let tab = await BrowserTestUtils.openNewForegroundTab(
gBrowser,
TEST_TOP_PAGE
);
let browser = tab.linkedBrowser;
await insertSubFrame(browser, TEST_3RD_PARTY_PAGE, "test");
// Verify that the third party tracker doesn't have storage access at
// beginning.
await runScriptInSubFrame(browser, "test", async _ => {
await noStorageAccessInitially();
is(document.cookie, "", "No cookies for me");
document.cookie = "name=value";
is(document.cookie, "", "Setting cookie is blocked");
});
let storagePermissionPromise = waitStoragePermission(
);
// Verify if the prompt has been shown.
let shownPromise = BrowserTestUtils.waitForEvent(
PopupNotifications.panel,
"popupshown"
);
// Call the privilege API.
let callAPIPromise = SpecialPowers.spawn(browser, [], async _ => {
// The privilege API requires user activation. So, we set the user
// activation flag before we call the API.
content.document.notifyUserGestureActivation();
try {
await content.document.requestStorageAccessForOrigin(
);
} catch (e) {
ok(false, "The API shouldn't throw.");
}
content.document.clearUserGestureActivation();
});
await shownPromise;
// Accept the prompt
triggerMainCommand();
await callAPIPromise;
// Verify if the storage access permission is set correctly.
await storagePermissionPromise;
// Verify if the existing third-party tracker iframe gains the storage
// access.
await runScriptInSubFrame(browser, "test", async _ => {
await hasStorageAccessInitially();
is(document.cookie, "", "Still no cookies for me");
document.cookie = "name=value";
is(document.cookie, "name=value", "Successfully set cookies.");
});
// Insert another third-party tracker iframe and check if it has storage access.
await insertSubFrame(browser, TEST_3RD_PARTY_PAGE, "test2");
await runScriptInSubFrame(browser, "test2", async _ => {
await hasStorageAccessInitially();
is(document.cookie, "name=value", "Some cookies for me");
});
// Insert another iframe with different third-party tracker and check it has
// no storage access.
await insertSubFrame(browser, TEST_ANOTHER_TRACKER_PAGE, "test3");
await runScriptInSubFrame(browser, "test3", async _ => {
await noStorageAccessInitially();
is(document.cookie, "", "No cookies for me");
document.cookie = "name=value";
is(document.cookie, "", "Setting cookie is blocked for another tracker.");
});
await clearStoragePermission("https://tracking.example.org");
Services.cookies.removeAll();
BrowserTestUtils.removeTab(tab);
});
add_task(async function test_privilege_api_with_dFPI() {
await SpecialPowers.pushPrefEnv({
set: [
[
"network.cookie.cookieBehavior",
Ci.nsICookieService.BEHAVIOR_REJECT_TRACKER_AND_PARTITION_FOREIGN,
],
[
"network.cookie.cookieBehavior.pbmode",
Ci.nsICookieService.BEHAVIOR_REJECT_TRACKER_AND_PARTITION_FOREIGN,
],
],
});
let tab = await BrowserTestUtils.openNewForegroundTab(
gBrowser,
TEST_TOP_PAGE
);
let browser = tab.linkedBrowser;
await insertSubFrame(browser, TEST_4TH_PARTY_PAGE_HTTPS, "test");
// Verify that the third-party context doesn't have storage access at
// beginning.
await runScriptInSubFrame(browser, "test", async _ => {
await noStorageAccessInitially();
is(document.cookie, "", "No cookies for me");
document.cookie = "name=partitioned; SameSite=None; Secure; Partitioned;";
is(
document.cookie,
"name=partitioned",
"Setting cookie in partitioned context."
);
});
let storagePermissionPromise = waitStoragePermission(
);
// Verify if the prompt has been shown.
let shownPromise = BrowserTestUtils.waitForEvent(
PopupNotifications.panel,
"popupshown"
);
// Call the privilege API.
let callAPIPromise = SpecialPowers.spawn(browser, [], async _ => {
// The privilege API requires a user gesture. So, we set the user handling
// flag before we call the API.
content.document.notifyUserGestureActivation();
try {
await content.document.requestStorageAccessForOrigin(
);
} catch (e) {
ok(false, "The API shouldn't throw.");
}
content.document.clearUserGestureActivation();
});
await shownPromise;
// Accept the prompt
triggerMainCommand();
await callAPIPromise;
// Verify if the storage access permission is set correctly.
await storagePermissionPromise;
// Verify if the existing third-party iframe gains the storage access.
await runScriptInSubFrame(browser, "test", async _ => {
await hasStorageAccessInitially();
is(
document.cookie,
"name=partitioned",
"partitioned cookies should be available"
);
document.cookie = "name=unpartitioned";
is(
document.cookie,
"name=partitioned; name=unpartitioned",
"Successfully set cookies. Both partitioned and unpartitioned cookies should be available"
);
});
// Insert another third-party content iframe and check if it has storage access.
await insertSubFrame(browser, TEST_4TH_PARTY_PAGE_HTTPS, "test2");
await runScriptInSubFrame(browser, "test2", async _ => {
await hasStorageAccessInitially();
is(
document.cookie,
"name=partitioned; name=unpartitioned",
"Some cookies for unpartitioned context"
);
});
// Insert another iframe with different third-party content and check it has
// no storage access.
await insertSubFrame(browser, TEST_ANOTHER_4TH_PARTY_PAGE, "test3");
await runScriptInSubFrame(browser, "test3", async _ => {
await noStorageAccessInitially();
is(document.cookie, "", "No cookies for me");
document.cookie = "name=value; SameSite=None; Secure; Partitioned;";
is(document.cookie, "name=value", "Setting cookie to partitioned context.");
});
await clearStoragePermission("https://not-tracking.example.com");
Services.cookies.removeAll();
BrowserTestUtils.removeTab(tab);
});
add_task(async function test_prompt() {
await SpecialPowers.pushPrefEnv({
set: [
["dom.storage_access.auto_grants", false],
[
"network.cookie.cookieBehavior",
Ci.nsICookieService.BEHAVIOR_REJECT_TRACKER,
],
[
"network.cookie.cookieBehavior.pbmode",
Ci.nsICookieService.BEHAVIOR_REJECT_TRACKER,
],
],
});
for (const allow of [false, true]) {
let tab = await BrowserTestUtils.openNewForegroundTab(
gBrowser,
TEST_TOP_PAGE
);
let browser = tab.linkedBrowser;
// Verify if the prompt has been shown.
let shownPromise = BrowserTestUtils.waitForEvent(
PopupNotifications.panel,
"popupshown"
);
let hiddenPromise = BrowserTestUtils.waitForEvent(
PopupNotifications.panel,
"popuphidden"
);
// Call the privilege API.
let callAPIPromise = SpecialPowers.spawn(browser, [allow], async allow => {
// The privilege API requires a user gesture. So, we set the user handling
// flag before we call the API.
content.document.notifyUserGestureActivation();
let isThrown = false;
try {
await content.document.requestStorageAccessForOrigin(
);
} catch (e) {
isThrown = true;
}
is(isThrown, !allow, `The API ${allow ? "shouldn't" : "should"} throw.`);
content.document.clearUserGestureActivation();
});
await shownPromise;
let notification = await TestUtils.waitForCondition(_ =>
PopupNotifications.getNotification("storage-access", browser)
);
ok(notification, "Should have gotten the notification");
// Click the popup button.
if (allow) {
triggerMainCommand();
} else {
triggerSecondaryCommand();
}
// Wait until the popup disappears.
await hiddenPromise;
// Wait until the API finishes.
await callAPIPromise;
await insertSubFrame(browser, TEST_3RD_PARTY_PAGE, "test");
if (allow) {
await runScriptInSubFrame(browser, "test", async _ => {
await hasStorageAccessInitially();
is(document.cookie, "", "Still no cookies for me");
document.cookie = "name=value";
is(document.cookie, "name=value", "Successfully set cookies.");
});
} else {
await runScriptInSubFrame(browser, "test", async _ => {
await noStorageAccessInitially();
is(document.cookie, "", "Still no cookies for me");
document.cookie = "name=value";
is(document.cookie, "", "No cookie after setting.");
});
}
BrowserTestUtils.removeTab(tab);
}
await clearStoragePermission("https://tracking.example.org");
Services.cookies.removeAll();
});
// Tests that the priviledged rSA method should show a prompt when auto grants
// are enabled, but we don't have user activation. When requiring user
// activation, rSA should still reject.
add_task(async function test_prompt_no_user_activation() {
await SpecialPowers.pushPrefEnv({
set: [
["dom.storage_access.auto_grants", true],
[
"network.cookie.cookieBehavior",
Ci.nsICookieService.BEHAVIOR_REJECT_TRACKER,
],
[
"network.cookie.cookieBehavior.pbmode",
Ci.nsICookieService.BEHAVIOR_REJECT_TRACKER,
],
],
});
for (let requireUserActivation of [false, true]) {
let tab = await BrowserTestUtils.openNewForegroundTab(
gBrowser,
TEST_TOP_PAGE
);
let browser = tab.linkedBrowser;
let shownPromise, hiddenPromise;
// Verify if the prompt has been shown.
if (!requireUserActivation) {
shownPromise = BrowserTestUtils.waitForEvent(
PopupNotifications.panel,
"popupshown"
);
hiddenPromise = BrowserTestUtils.waitForEvent(
PopupNotifications.panel,
"popuphidden"
);
}
// Call the privilege API.
let callAPIPromise = SpecialPowers.spawn(
browser,
[requireUserActivation],
async requireUserActivation => {
let isThrown = false;
try {
await content.document.requestStorageAccessForOrigin(
requireUserActivation
);
} catch (e) {
isThrown = true;
}
is(
isThrown,
requireUserActivation,
`The API ${requireUserActivation ? "shouldn't" : "should"} throw.`
);
}
);
if (!requireUserActivation) {
await shownPromise;
let notification = await TestUtils.waitForCondition(_ =>
PopupNotifications.getNotification("storage-access", browser)
);
ok(notification, "Should have gotten the notification");
// Click the popup button.
triggerMainCommand();
// Wait until the popup disappears.
await hiddenPromise;
}
// Wait until the API finishes.
await callAPIPromise;
await insertSubFrame(browser, TEST_3RD_PARTY_PAGE, "test");
if (!requireUserActivation) {
await runScriptInSubFrame(browser, "test", async _ => {
await hasStorageAccessInitially();
is(document.cookie, "", "Still no cookies for me");
document.cookie = "name=value";
is(document.cookie, "name=value", "Successfully set cookies.");
});
} else {
await runScriptInSubFrame(browser, "test", async _ => {
await noStorageAccessInitially();
is(document.cookie, "", "Still no cookies for me");
document.cookie = "name=value";
is(document.cookie, "", "No cookie after setting.");
});
}
BrowserTestUtils.removeTab(tab);
await clearStoragePermission("https://tracking.example.org");
Services.cookies.removeAll();
}
});
add_task(async function test_invalid_input() {
let tab = await BrowserTestUtils.openNewForegroundTab(
gBrowser,
TEST_TOP_PAGE
);
let browser = tab.linkedBrowser;
await SpecialPowers.spawn(browser, [], async _ => {
let isThrown = false;
try {
await content.document.requestStorageAccessForOrigin(
);
} catch (e) {
isThrown = true;
}
ok(isThrown, "The API should throw without user gesture.");
content.document.notifyUserGestureActivation();
isThrown = false;
try {
await content.document.requestStorageAccessForOrigin();
} catch (e) {
isThrown = true;
}
ok(isThrown, "The API should throw with no input.");
content.document.notifyUserGestureActivation();
isThrown = false;
try {
await content.document.requestStorageAccessForOrigin("");
} catch (e) {
isThrown = true;
is(e.name, "NS_ERROR_MALFORMED_URI", "The input is not a valid url");
}
ok(isThrown, "The API should throw with empty string.");
content.document.notifyUserGestureActivation();
isThrown = false;
try {
await content.document.requestStorageAccessForOrigin("invalid url");
} catch (e) {
isThrown = true;
is(e.name, "NS_ERROR_MALFORMED_URI", "The input is not a valid url");
}
ok(isThrown, "The API should throw with invalid url.");
content.document.clearUserGestureActivation();
});
BrowserTestUtils.removeTab(tab);
});