summaryrefslogtreecommitdiffstats
path: root/browser/modules/test/unit/test_SitePermissions_temporary.js
diff options
context:
space:
mode:
Diffstat (limited to 'browser/modules/test/unit/test_SitePermissions_temporary.js')
-rw-r--r--browser/modules/test/unit/test_SitePermissions_temporary.js710
1 files changed, 710 insertions, 0 deletions
diff --git a/browser/modules/test/unit/test_SitePermissions_temporary.js b/browser/modules/test/unit/test_SitePermissions_temporary.js
new file mode 100644
index 0000000000..a91b1b8bd8
--- /dev/null
+++ b/browser/modules/test/unit/test_SitePermissions_temporary.js
@@ -0,0 +1,710 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+"use strict";
+
+const { SitePermissions } = ChromeUtils.importESModule(
+ "resource:///modules/SitePermissions.sys.mjs"
+);
+
+const TemporaryPermissions = SitePermissions._temporaryPermissions;
+
+const PERM_A = "foo";
+const PERM_B = "bar";
+const PERM_C = "foobar";
+
+const BROWSER_A = createDummyBrowser("https://example.com/foo");
+const BROWSER_B = createDummyBrowser("https://example.org/foo");
+
+const EXPIRY_MS_A = 1000000;
+const EXPIRY_MS_B = 1000001;
+
+function createDummyBrowser(spec) {
+ let uri = Services.io.newURI(spec);
+ return {
+ currentURI: uri,
+ contentPrincipal: Services.scriptSecurityManager.createContentPrincipal(
+ uri,
+ {}
+ ),
+ dispatchEvent: () => {},
+ ownerGlobal: {
+ CustomEvent: class CustomEvent {},
+ },
+ };
+}
+
+function navigateDummyBrowser(browser, uri) {
+ // Callers may pass in either uri strings or nsIURI objects.
+ if (typeof uri == "string") {
+ uri = Services.io.newURI(uri);
+ }
+ browser.currentURI = uri;
+ browser.contentPrincipal =
+ Services.scriptSecurityManager.createContentPrincipal(
+ browser.currentURI,
+ {}
+ );
+}
+
+/**
+ * Tests that temporary permissions with different block states are stored
+ * (set, overwrite, delete) correctly.
+ */
+add_task(async function testAllowBlock() {
+ // Set two temporary permissions on the same browser.
+ SitePermissions.setForPrincipal(
+ null,
+ PERM_A,
+ SitePermissions.ALLOW,
+ SitePermissions.SCOPE_TEMPORARY,
+ BROWSER_A,
+ EXPIRY_MS_A
+ );
+
+ SitePermissions.setForPrincipal(
+ null,
+ PERM_B,
+ SitePermissions.BLOCK,
+ SitePermissions.SCOPE_TEMPORARY,
+ BROWSER_A,
+ EXPIRY_MS_A
+ );
+
+ // Test that the permissions have been set correctly.
+ Assert.deepEqual(
+ SitePermissions.getForPrincipal(null, PERM_A, BROWSER_A),
+ {
+ state: SitePermissions.ALLOW,
+ scope: SitePermissions.SCOPE_TEMPORARY,
+ },
+ "SitePermissions returns expected permission state for perm A."
+ );
+
+ Assert.deepEqual(
+ SitePermissions.getForPrincipal(null, PERM_B, BROWSER_A),
+ {
+ state: SitePermissions.BLOCK,
+ scope: SitePermissions.SCOPE_TEMPORARY,
+ },
+ "SitePermissions returns expected permission state for perm B."
+ );
+
+ Assert.deepEqual(
+ TemporaryPermissions.get(BROWSER_A, PERM_A),
+ {
+ id: PERM_A,
+ state: SitePermissions.ALLOW,
+ scope: SitePermissions.SCOPE_TEMPORARY,
+ },
+ "TemporaryPermissions returns expected permission state for perm A."
+ );
+
+ Assert.deepEqual(
+ TemporaryPermissions.get(BROWSER_A, PERM_B),
+ {
+ id: PERM_B,
+ state: SitePermissions.BLOCK,
+ scope: SitePermissions.SCOPE_TEMPORARY,
+ },
+ "TemporaryPermissions returns expected permission state for perm B."
+ );
+
+ // Test internal data structure of TemporaryPermissions.
+ let entry = TemporaryPermissions._stateByBrowser.get(BROWSER_A);
+ ok(entry, "Should have an entry for browser A");
+ ok(
+ !TemporaryPermissions._stateByBrowser.has(BROWSER_B),
+ "Should have no entry for browser B"
+ );
+
+ let { browser, uriToPerm } = entry;
+ Assert.equal(
+ browser?.get(),
+ BROWSER_A,
+ "Entry should have a weak reference to the browser."
+ );
+
+ ok(uriToPerm, "Entry should have uriToPerm object.");
+ Assert.equal(Object.keys(uriToPerm).length, 2, "uriToPerm has 2 entries.");
+
+ let permissionsA = uriToPerm[BROWSER_A.contentPrincipal.origin];
+ let permissionsB =
+ uriToPerm[Services.eTLD.getBaseDomain(BROWSER_A.currentURI)];
+
+ ok(permissionsA, "Allow should be keyed under origin");
+ ok(permissionsB, "Block should be keyed under baseDomain");
+
+ let permissionA = permissionsA[PERM_A];
+ let permissionB = permissionsB[PERM_B];
+
+ Assert.equal(
+ permissionA.state,
+ SitePermissions.ALLOW,
+ "Should have correct state"
+ );
+ let expireTimeoutA = permissionA.expireTimeout;
+ Assert.ok(
+ Number.isInteger(expireTimeoutA),
+ "Should have valid expire timeout"
+ );
+
+ Assert.equal(
+ permissionB.state,
+ SitePermissions.BLOCK,
+ "Should have correct state"
+ );
+ let expireTimeoutB = permissionB.expireTimeout;
+ Assert.ok(
+ Number.isInteger(expireTimeoutB),
+ "Should have valid expire timeout"
+ );
+
+ // Overwrite permission A.
+ SitePermissions.setForPrincipal(
+ null,
+ PERM_A,
+ SitePermissions.ALLOW,
+ SitePermissions.SCOPE_TEMPORARY,
+ BROWSER_A,
+ EXPIRY_MS_B
+ );
+
+ Assert.ok(
+ permissionsA[PERM_A].expireTimeout != expireTimeoutA,
+ "Overwritten permission A should have new timer"
+ );
+
+ // Overwrite permission B - this time with a non-block state which means it
+ // should be keyed by origin now.
+ SitePermissions.setForPrincipal(
+ null,
+ PERM_B,
+ SitePermissions.ALLOW,
+ SitePermissions.SCOPE_TEMPORARY,
+ BROWSER_A,
+ EXPIRY_MS_A
+ );
+
+ let baseDomainEntry =
+ uriToPerm[Services.eTLD.getBaseDomain(BROWSER_A.currentURI)];
+ Assert.ok(
+ !baseDomainEntry || !baseDomainEntry[PERM_B],
+ "Should not longer have baseDomain permission entry"
+ );
+
+ permissionsB = uriToPerm[BROWSER_A.contentPrincipal.origin];
+ permissionB = permissionsB[PERM_B];
+ Assert.ok(
+ permissionsB && permissionB,
+ "Overwritten permission should be keyed under origin"
+ );
+ Assert.equal(
+ permissionB.state,
+ SitePermissions.ALLOW,
+ "Should have correct updated state"
+ );
+ Assert.ok(
+ permissionB.expireTimeout != expireTimeoutB,
+ "Overwritten permission B should have new timer"
+ );
+
+ // Remove permissions
+ SitePermissions.removeFromPrincipal(null, PERM_A, BROWSER_A);
+ SitePermissions.removeFromPrincipal(null, PERM_B, BROWSER_A);
+
+ // Test that permissions have been removed correctly
+ Assert.deepEqual(
+ SitePermissions.getForPrincipal(null, PERM_A, BROWSER_A),
+ {
+ state: SitePermissions.UNKNOWN,
+ scope: SitePermissions.SCOPE_PERSISTENT,
+ },
+ "SitePermissions returns UNKNOWN state for A."
+ );
+
+ Assert.deepEqual(
+ SitePermissions.getForPrincipal(null, PERM_B, BROWSER_A),
+ {
+ state: SitePermissions.UNKNOWN,
+ scope: SitePermissions.SCOPE_PERSISTENT,
+ },
+ "SitePermissions returns UNKNOWN state for B."
+ );
+
+ Assert.equal(
+ TemporaryPermissions.get(BROWSER_A, PERM_A),
+ null,
+ "TemporaryPermissions returns null for perm A."
+ );
+
+ Assert.equal(
+ TemporaryPermissions.get(BROWSER_A, PERM_B),
+ null,
+ "TemporaryPermissions returns null for perm B."
+ );
+});
+
+/**
+ * Tests TemporaryPermissions#getAll.
+ */
+add_task(async function testGetAll() {
+ SitePermissions.setForPrincipal(
+ null,
+ PERM_A,
+ SitePermissions.ALLOW,
+ SitePermissions.SCOPE_TEMPORARY,
+ BROWSER_A,
+ EXPIRY_MS_A
+ );
+ SitePermissions.setForPrincipal(
+ null,
+ PERM_B,
+ SitePermissions.BLOCK,
+ SitePermissions.SCOPE_TEMPORARY,
+ BROWSER_B,
+ EXPIRY_MS_A
+ );
+ SitePermissions.setForPrincipal(
+ null,
+ PERM_C,
+ SitePermissions.PROMPT,
+ SitePermissions.SCOPE_TEMPORARY,
+ BROWSER_B,
+ EXPIRY_MS_A
+ );
+
+ Assert.deepEqual(TemporaryPermissions.getAll(BROWSER_A), [
+ {
+ id: PERM_A,
+ state: SitePermissions.ALLOW,
+ scope: SitePermissions.SCOPE_TEMPORARY,
+ },
+ ]);
+
+ let permsBrowserB = TemporaryPermissions.getAll(BROWSER_B);
+ Assert.equal(
+ permsBrowserB.length,
+ 2,
+ "There should be 2 permissions set for BROWSER_B"
+ );
+
+ let permB;
+ let permC;
+
+ if (permsBrowserB[0].id == PERM_B) {
+ permB = permsBrowserB[0];
+ permC = permsBrowserB[1];
+ } else {
+ permB = permsBrowserB[1];
+ permC = permsBrowserB[0];
+ }
+
+ Assert.deepEqual(permB, {
+ id: PERM_B,
+ state: SitePermissions.BLOCK,
+ scope: SitePermissions.SCOPE_TEMPORARY,
+ });
+ Assert.deepEqual(permC, {
+ id: PERM_C,
+ state: SitePermissions.PROMPT,
+ scope: SitePermissions.SCOPE_TEMPORARY,
+ });
+});
+
+/**
+ * Tests SitePermissions#clearTemporaryBlockPermissions and
+ * TemporaryPermissions#clear.
+ */
+add_task(async function testClear() {
+ SitePermissions.setForPrincipal(
+ null,
+ PERM_A,
+ SitePermissions.ALLOW,
+ SitePermissions.SCOPE_TEMPORARY,
+ BROWSER_A,
+ EXPIRY_MS_A
+ );
+ SitePermissions.setForPrincipal(
+ null,
+ PERM_B,
+ SitePermissions.BLOCK,
+ SitePermissions.SCOPE_TEMPORARY,
+ BROWSER_A,
+ EXPIRY_MS_A
+ );
+ SitePermissions.setForPrincipal(
+ null,
+ PERM_C,
+ SitePermissions.BLOCK,
+ SitePermissions.SCOPE_TEMPORARY,
+ BROWSER_B,
+ EXPIRY_MS_A
+ );
+
+ let stateByBrowser = SitePermissions._temporaryPermissions._stateByBrowser;
+
+ Assert.ok(stateByBrowser.has(BROWSER_A), "Browser map should have BROWSER_A");
+ Assert.ok(stateByBrowser.has(BROWSER_B), "Browser map should have BROWSER_B");
+
+ SitePermissions.clearTemporaryBlockPermissions(BROWSER_A);
+
+ // We only clear block permissions, so we should still see PERM_A.
+ Assert.deepEqual(
+ SitePermissions.getForPrincipal(null, PERM_A, BROWSER_A),
+ {
+ state: SitePermissions.ALLOW,
+ scope: SitePermissions.SCOPE_TEMPORARY,
+ },
+ "SitePermissions returns ALLOW state for PERM_A."
+ );
+ // We don't clear BROWSER_B so it should still be there.
+ Assert.ok(stateByBrowser.has(BROWSER_B), "Should still have BROWSER_B.");
+
+ // Now clear allow permissions for A explicitly.
+ SitePermissions._temporaryPermissions.clear(BROWSER_A, SitePermissions.ALLOW);
+
+ Assert.ok(!stateByBrowser.has(BROWSER_A), "Should no longer have BROWSER_A.");
+ let browser = stateByBrowser.get(BROWSER_B);
+ Assert.ok(browser, "Should still have BROWSER_B");
+
+ Assert.deepEqual(
+ SitePermissions.getForPrincipal(null, PERM_A, BROWSER_A),
+ {
+ state: SitePermissions.UNKNOWN,
+ scope: SitePermissions.SCOPE_PERSISTENT,
+ },
+ "SitePermissions returns UNKNOWN state for PERM_A."
+ );
+ Assert.deepEqual(
+ SitePermissions.getForPrincipal(null, PERM_B, BROWSER_A),
+ {
+ state: SitePermissions.UNKNOWN,
+ scope: SitePermissions.SCOPE_PERSISTENT,
+ },
+ "SitePermissions returns UNKNOWN state for PERM_B."
+ );
+ Assert.deepEqual(
+ SitePermissions.getForPrincipal(null, PERM_C, BROWSER_B),
+ {
+ state: SitePermissions.BLOCK,
+ scope: SitePermissions.SCOPE_TEMPORARY,
+ },
+ "SitePermissions returns BLOCK state for PERM_C."
+ );
+
+ SitePermissions._temporaryPermissions.clear(BROWSER_B);
+
+ Assert.ok(!stateByBrowser.has(BROWSER_B), "Should no longer have BROWSER_B.");
+ Assert.deepEqual(
+ SitePermissions.getForPrincipal(null, PERM_C, BROWSER_B),
+ {
+ state: SitePermissions.UNKNOWN,
+ scope: SitePermissions.SCOPE_PERSISTENT,
+ },
+ "SitePermissions returns UNKNOWN state for PERM_C."
+ );
+});
+
+/**
+ * Tests that the temporary permissions setter calls the callback on permission
+ * expire with the associated browser.
+ */
+add_task(async function testCallbackOnExpiry() {
+ let promiseExpireA = new Promise(resolve => {
+ TemporaryPermissions.set(
+ BROWSER_A,
+ PERM_A,
+ SitePermissions.BLOCK,
+ 100,
+ undefined,
+ resolve
+ );
+ });
+ let promiseExpireB = new Promise(resolve => {
+ TemporaryPermissions.set(
+ BROWSER_B,
+ PERM_A,
+ SitePermissions.BLOCK,
+ 100,
+ BROWSER_B.contentPrincipal,
+ resolve
+ );
+ });
+
+ let [browserA, browserB] = await Promise.all([
+ promiseExpireA,
+ promiseExpireB,
+ ]);
+ Assert.equal(
+ browserA,
+ BROWSER_A,
+ "Should get callback with browser on expiry for A"
+ );
+ Assert.equal(
+ browserB,
+ BROWSER_B,
+ "Should get callback with browser on expiry for B"
+ );
+});
+
+/**
+ * Tests that the temporary permissions setter calls the callback on permission
+ * expire with the associated browser if the browser associated browser has
+ * changed after setting the permission.
+ */
+add_task(async function testCallbackOnExpiryUpdatedBrowser() {
+ let promiseExpire = new Promise(resolve => {
+ TemporaryPermissions.set(
+ BROWSER_A,
+ PERM_A,
+ SitePermissions.BLOCK,
+ 200,
+ undefined,
+ resolve
+ );
+ });
+
+ TemporaryPermissions.copy(BROWSER_A, BROWSER_B);
+
+ let browser = await promiseExpire;
+ Assert.equal(
+ browser,
+ BROWSER_B,
+ "Should get callback with updated browser on expiry."
+ );
+});
+
+/**
+ * Tests that the permission setter throws an exception if an invalid expiry
+ * time is passed.
+ */
+add_task(async function testInvalidExpiryTime() {
+ let expectedError = /expireTime must be a positive integer/;
+ Assert.throws(() => {
+ SitePermissions.setForPrincipal(
+ null,
+ PERM_A,
+ SitePermissions.ALLOW,
+ SitePermissions.SCOPE_TEMPORARY,
+ BROWSER_A,
+ null
+ );
+ }, expectedError);
+ Assert.throws(() => {
+ SitePermissions.setForPrincipal(
+ null,
+ PERM_A,
+ SitePermissions.ALLOW,
+ SitePermissions.SCOPE_TEMPORARY,
+ BROWSER_A,
+ 0
+ );
+ }, expectedError);
+ Assert.throws(() => {
+ SitePermissions.setForPrincipal(
+ null,
+ PERM_A,
+ SitePermissions.ALLOW,
+ SitePermissions.SCOPE_TEMPORARY,
+ BROWSER_A,
+ -100
+ );
+ }, expectedError);
+});
+
+/**
+ * Tests that we block by base domain but allow by origin.
+ */
+add_task(async function testTemporaryPermissionScope() {
+ let states = {
+ strict: {
+ same: [
+ "https://example.com",
+ "https://example.com/sub/path",
+ "https://example.com:443",
+ "https://name:password@example.com",
+ ],
+ different: [
+ "https://example.com",
+ "https://test1.example.com",
+ "http://example.com",
+ "http://example.org",
+ "file:///tmp/localPageA.html",
+ "file:///tmp/localPageB.html",
+ ],
+ },
+ nonStrict: {
+ same: [
+ "https://example.com",
+ "https://example.com/sub/path",
+ "https://example.com:443",
+ "https://test1.example.com",
+ "http://test2.test1.example.com",
+ "https://name:password@example.com",
+ "http://example.com",
+ ],
+ different: [
+ "https://example.com",
+ "https://example.org",
+ "http://example.net",
+ ],
+ },
+ };
+
+ for (let state of [SitePermissions.BLOCK, SitePermissions.ALLOW]) {
+ let matchStrict = state != SitePermissions.BLOCK;
+
+ let lists = matchStrict ? states.strict : states.nonStrict;
+
+ Object.entries(lists).forEach(([type, list]) => {
+ let expectSet = type == "same";
+
+ for (let uri of list) {
+ let browser = createDummyBrowser(uri);
+ SitePermissions.setForPrincipal(
+ null,
+ PERM_A,
+ state,
+ SitePermissions.SCOPE_TEMPORARY,
+ browser,
+ EXPIRY_MS_A
+ );
+
+ ok(true, "origin:" + browser.contentPrincipal.origin);
+
+ for (let otherUri of list) {
+ if (uri == otherUri) {
+ continue;
+ }
+ navigateDummyBrowser(browser, otherUri);
+ ok(true, "new origin:" + browser.contentPrincipal.origin);
+
+ Assert.deepEqual(
+ SitePermissions.getForPrincipal(null, PERM_A, browser),
+ {
+ state: expectSet ? state : SitePermissions.UNKNOWN,
+ scope: expectSet
+ ? SitePermissions.SCOPE_TEMPORARY
+ : SitePermissions.SCOPE_PERSISTENT,
+ },
+ `${
+ state == SitePermissions.BLOCK ? "Block" : "Allow"
+ } Permission originally set for ${uri} should ${
+ expectSet ? "not" : "also"
+ } be set for ${otherUri}.`
+ );
+ }
+
+ SitePermissions._temporaryPermissions.clear(browser);
+ }
+ });
+ }
+});
+
+/**
+ * Tests that we can override the principal to use for keying temporary
+ * permissions.
+ */
+add_task(async function testOverrideBrowserURI() {
+ let testBrowser = createDummyBrowser("https://old.example.com/foo");
+ let overrideURI = Services.io.newURI("https://test.example.org/test/path");
+ SitePermissions.setForPrincipal(
+ Services.scriptSecurityManager.createContentPrincipal(overrideURI, {}),
+ PERM_A,
+ SitePermissions.ALLOW,
+ SitePermissions.SCOPE_TEMPORARY,
+ testBrowser,
+ EXPIRY_MS_A
+ );
+
+ Assert.deepEqual(
+ SitePermissions.getForPrincipal(null, PERM_A, testBrowser),
+ {
+ state: SitePermissions.UNKNOWN,
+ scope: SitePermissions.SCOPE_PERSISTENT,
+ },
+ "Permission should not be set for old URI."
+ );
+
+ // "Navigate" to new URI
+ navigateDummyBrowser(testBrowser, overrideURI);
+
+ Assert.deepEqual(
+ SitePermissions.getForPrincipal(null, PERM_A, testBrowser),
+ {
+ state: SitePermissions.ALLOW,
+ scope: SitePermissions.SCOPE_TEMPORARY,
+ },
+ "Permission should be set for new URI."
+ );
+
+ SitePermissions._temporaryPermissions.clear(testBrowser);
+});
+
+/**
+ * Tests that TemporaryPermissions does not throw for incompatible URI or
+ * browser.currentURI.
+ */
+add_task(async function testPermissionUnsupportedScheme() {
+ let aboutURI = Services.io.newURI("about:blank");
+
+ // Incompatible override URI should not throw or store any permissions.
+ SitePermissions.setForPrincipal(
+ Services.scriptSecurityManager.createContentPrincipal(aboutURI, {}),
+ PERM_A,
+ SitePermissions.ALLOW,
+ SitePermissions.SCOPE_TEMPORARY,
+ BROWSER_A,
+ EXPIRY_MS_B
+ );
+ Assert.ok(
+ SitePermissions._temporaryPermissions._stateByBrowser.has(BROWSER_A),
+ "Should not have stored permission for unsupported URI scheme."
+ );
+
+ let browser = createDummyBrowser("https://example.com/");
+ // Set a permission so we get an entry in the browser map.
+ SitePermissions.setForPrincipal(
+ null,
+ PERM_B,
+ SitePermissions.BLOCK,
+ SitePermissions.SCOPE_TEMPORARY,
+ browser
+ );
+
+ // Change browser URI to about:blank.
+ navigateDummyBrowser(browser, aboutURI);
+
+ // Setting permission for browser with unsupported URI should not throw.
+ SitePermissions.setForPrincipal(
+ null,
+ PERM_A,
+ SitePermissions.ALLOW,
+ SitePermissions.SCOPE_TEMPORARY,
+ browser
+ );
+ Assert.ok(true, "Set should not throw for unsupported URI");
+
+ SitePermissions.removeFromPrincipal(null, PERM_A, browser);
+ Assert.ok(true, "Remove should not throw for unsupported URI");
+
+ Assert.deepEqual(
+ SitePermissions.getForPrincipal(null, PERM_A, browser),
+ {
+ state: SitePermissions.UNKNOWN,
+ scope: SitePermissions.SCOPE_PERSISTENT,
+ },
+ "Should return no permission set for unsupported URI."
+ );
+ Assert.ok(true, "Get should not throw for unsupported URI");
+
+ // getAll should not throw, but return empty permissions array.
+ let permissions = SitePermissions.getAllForBrowser(browser);
+ Assert.ok(
+ Array.isArray(permissions) && !permissions.length,
+ "Should return empty array for browser on about:blank"
+ );
+
+ SitePermissions._temporaryPermissions.clear(browser);
+});