The cookie behavior was changed in bug 1918342. Lets update the constant name to reflect the new behavior. This adds new string constants for enterprise policy while keeping the old constant for backwards compatibilty. "reject-tracker-and-partition-foreign" is now deprecated and aliases to the new "partition-foreign" Addons API keeps the current "reject_trackers_and_partition_foreign"[1] string constant for compatibility. [1]: https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/API/privacy/websites#cookieconfig Differential Revision: https://phabricator.services.mozilla.com/D300794
627 lines
18 KiB
JavaScript
627 lines
18 KiB
JavaScript
//
|
|
// 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);
|
|
ifr.src = url;
|
|
content.document.body.appendChild(ifr);
|
|
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(
|
|
"https://tracking.example.org"
|
|
);
|
|
|
|
// 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(
|
|
"https://tracking.example.org/"
|
|
);
|
|
} 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_PARTITION_FOREIGN,
|
|
],
|
|
[
|
|
"network.cookie.cookieBehavior.pbmode",
|
|
Ci.nsICookieService.BEHAVIOR_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(
|
|
"https://not-tracking.example.com"
|
|
);
|
|
|
|
// 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(
|
|
"https://not-tracking.example.com/"
|
|
);
|
|
} 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(
|
|
"https://tracking.example.org"
|
|
);
|
|
} 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(
|
|
"https://tracking.example.org",
|
|
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(
|
|
"https://tracking.example.org"
|
|
);
|
|
} 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);
|
|
});
|