From 08710869a78115812f4c3eea0b12e17b7b46748a Mon Sep 17 00:00:00 2001 From: Paul Zuehlcke Date: Fri, 12 Mar 2021 10:06:15 +0000 Subject: [PATCH] Bug 1693621 - Tests for new SitePermissions expiry behavior. r=johannh Differential Revision: https://phabricator.services.mozilla.com/D106160 --- .../browser_temporary_permissions_expiry.js | 99 +++ .../browser_temporary_permissions_tabs.js | 46 ++ browser/modules/SitePermissions.jsm | 3 + .../test/browser/browser_SitePermissions.js | 21 - .../unit/test_SitePermissions_temporary.js | 698 ++++++++++++++++++ browser/modules/test/unit/xpcshell.ini | 1 + 6 files changed, 847 insertions(+), 21 deletions(-) create mode 100644 browser/modules/test/unit/test_SitePermissions_temporary.js diff --git a/browser/base/content/test/permissions/browser_temporary_permissions_expiry.js b/browser/base/content/test/permissions/browser_temporary_permissions_expiry.js index 7af986c15e80..993fc2f37b67 100644 --- a/browser/base/content/test/permissions/browser_temporary_permissions_expiry.js +++ b/browser/base/content/test/permissions/browser_temporary_permissions_expiry.js @@ -18,6 +18,9 @@ PromiseTestUtils.allowMatchingRejectionsGlobally(/The request is not allowed/); const EXPIRE_TIME_MS = 100; const TIMEOUT_MS = 500; +const EXPIRE_TIME_CUSTOM_MS = 1000; +const TIMEOUT_CUSTOM_MS = 1500; + const kVREnabled = SpecialPowers.getBoolPref("dom.vr.enabled"); // Test that temporary permissions can be re-requested after they expired @@ -108,3 +111,99 @@ add_task(async function testTempPermissionRequestAfterExpiry() { }); } }); + +/** + * Test whether the identity UI shows the permission granted state. + * @param {boolean} state - true = Shows permission granted, false otherwise. + */ +async function testIdentityPermissionGrantedState(state) { + let hasAttribute; + let msg = `Identity permission box ${ + state ? "shows" : "does not show" + } granted permissions.`; + await TestUtils.waitForCondition(() => { + hasAttribute = gPermissionPanel._identityPermissionBox.hasAttribute( + "hasPermissions" + ); + return hasAttribute == state; + }, msg); + is(hasAttribute, state, msg); +} + +// Test that temporary permissions can have custom expiry time and the identity +// block is updated correctly on expiry. +add_task(async function testTempPermissionCustomExpiry() { + const TEST_ID = "geo"; + // Set a default expiry time which is lower than the custom one we'll set. + await SpecialPowers.pushPrefEnv({ + set: [["privacy.temporary_permission_expire_time_ms", EXPIRE_TIME_MS]], + }); + + await BrowserTestUtils.withNewTab(PERMISSIONS_PAGE, async browser => { + Assert.deepEqual( + SitePermissions.getForPrincipal(null, TEST_ID, browser), + { + state: SitePermissions.UNKNOWN, + scope: SitePermissions.SCOPE_PERSISTENT, + }, + "Permission not set initially" + ); + + await testIdentityPermissionGrantedState(false); + + // Set permission with custom expiry time. + SitePermissions.setForPrincipal( + null, + "geo", + SitePermissions.ALLOW, + SitePermissions.SCOPE_TEMPORARY, + browser, + EXPIRE_TIME_CUSTOM_MS + ); + + await testIdentityPermissionGrantedState(true); + + // We've set the permission, start the timer promise. + let timeout = new Promise(resolve => + setTimeout(resolve, TIMEOUT_CUSTOM_MS) + ); + + Assert.deepEqual( + SitePermissions.getForPrincipal(null, TEST_ID, browser), + { + state: SitePermissions.ALLOW, + scope: SitePermissions.SCOPE_TEMPORARY, + }, + "We should see the temporary permission we just set." + ); + + // Wait for half of the expiry time. + await new Promise(resolve => + setTimeout(resolve, EXPIRE_TIME_CUSTOM_MS / 2) + ); + Assert.deepEqual( + SitePermissions.getForPrincipal(null, TEST_ID, browser), + { + state: SitePermissions.ALLOW, + scope: SitePermissions.SCOPE_TEMPORARY, + }, + "Temporary permission should not have expired yet." + ); + + // Wait until permission expiry. + await timeout; + + // Identity permission section should have updated by now. It should do this + // without relying on side-effects of the SitePermissions getter. + await testIdentityPermissionGrantedState(false); + + Assert.deepEqual( + SitePermissions.getForPrincipal(null, TEST_ID, browser), + { + state: SitePermissions.UNKNOWN, + scope: SitePermissions.SCOPE_PERSISTENT, + }, + "Permission should have expired" + ); + }); +}); diff --git a/browser/base/content/test/permissions/browser_temporary_permissions_tabs.js b/browser/base/content/test/permissions/browser_temporary_permissions_tabs.js index 513b98481d90..f3dd104be6ca 100644 --- a/browser/base/content/test/permissions/browser_temporary_permissions_tabs.js +++ b/browser/base/content/test/permissions/browser_temporary_permissions_tabs.js @@ -103,3 +103,49 @@ add_task(async function testTempPermissionMultipleTabs() { BrowserTestUtils.removeTab(tab1); BrowserTestUtils.removeTab(tab2); }); + +// Test that temp permissions are cleared when closing tabs. +add_task(async function testTempPermissionOnTabClose() { + let origin = "https://example.com/"; + let principal = Services.scriptSecurityManager.createContentPrincipalFromOrigin( + origin + ); + let id = "geo"; + + ok( + !SitePermissions._temporaryPermissions._stateByBrowser.size, + "Temporary permission map should be empty initially." + ); + + let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, origin); + + SitePermissions.setForPrincipal( + principal, + id, + SitePermissions.BLOCK, + SitePermissions.SCOPE_TEMPORARY, + tab.linkedBrowser + ); + + Assert.deepEqual( + SitePermissions.getForPrincipal(principal, id, tab.linkedBrowser), + { + state: SitePermissions.BLOCK, + scope: SitePermissions.SCOPE_TEMPORARY, + } + ); + + ok( + SitePermissions._temporaryPermissions._stateByBrowser.has( + tab.linkedBrowser + ), + "Temporary permission map should have an entry for the browser." + ); + + BrowserTestUtils.removeTab(tab); + + ok( + !SitePermissions._temporaryPermissions._stateByBrowser.size, + "Temporary permission map should be empty after closing the tab." + ); +}); diff --git a/browser/modules/SitePermissions.jsm b/browser/modules/SitePermissions.jsm index 17af0fc7195c..bf79d20e4d0b 100644 --- a/browser/modules/SitePermissions.jsm +++ b/browser/modules/SitePermissions.jsm @@ -423,6 +423,9 @@ var SitePermissions = { _permissionsArray: null, _defaultPrefBranch: Services.prefs.getBranch("permissions.default."), + // For testing use only. + _temporaryPermissions: TemporaryPermissions, + /** * Gets all custom permissions for a given principal. * Install addon permission is excluded, check bug 1303108. diff --git a/browser/modules/test/browser/browser_SitePermissions.js b/browser/modules/test/browser/browser_SitePermissions.js index e5d608060bbe..3c415e6c3b08 100644 --- a/browser/modules/test/browser/browser_SitePermissions.js +++ b/browser/modules/test/browser/browser_SitePermissions.js @@ -8,27 +8,6 @@ const { SitePermissions } = ChromeUtils.import( "resource:///modules/SitePermissions.jsm" ); -// This asserts that SitePermissions.set can not save ALLOW permissions -// temporarily on a tab. -add_task(async function testTempAllowThrows() { - let principal = Services.scriptSecurityManager.createContentPrincipalFromOrigin( - "https://example.com" - ); - let id = "notifications"; - - await BrowserTestUtils.withNewTab(principal.spec, function(browser) { - Assert.throws(function() { - SitePermissions.setForPrincipal( - principal, - id, - SitePermissions.ALLOW, - SitePermissions.SCOPE_TEMPORARY, - browser - ); - }, /'Block' is the only permission we can save temporarily on a browser/); - }); -}); - // This tests the SitePermissions.getAllPermissionDetailsForBrowser function. add_task(async function testGetAllPermissionDetailsForBrowser() { let principal = Services.scriptSecurityManager.createContentPrincipalFromOrigin( 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 000000000000..fa766272c1aa --- /dev/null +++ b/browser/modules/test/unit/test_SitePermissions_temporary.js @@ -0,0 +1,698 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ + */ +"use strict"; + +const { SitePermissions } = ChromeUtils.import( + "resource:///modules/SitePermissions.jsm" +); +const { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm"); + +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; + +let principal = Services.scriptSecurityManager.createContentPrincipalFromOrigin( + "https://example.com" +); + +function createDummyBrowser(spec) { + let uri = Services.io.newURI(spec); + return { + currentURI: uri, + contentPrincipal: Services.scriptSecurityManager.createContentPrincipal( + uri, + {} + ), + dispatchEvent: () => {}, + ownerGlobal: { + CustomEvent: class CustomEvent {}, + }, + }; +} + +/** + * 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.currentURI.prePath]; + let permissionsB = + uriToPerm[Services.eTLD.getBaseDomain(BROWSER_A.currentURI)]; + + ok(permissionsA, "Allow should be keyed under prePath"); + 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 URI prePath 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.currentURI.prePath]; + permissionB = permissionsB[PERM_B]; + Assert.ok( + permissionsB && permissionB, + "Overwritten permission should be keyed under prePath" + ); + 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, + BROWSER_A.currentURI, + resolve + ); + }); + let promiseExpireB = new Promise(resolve => { + TemporaryPermissions.set( + BROWSER_B, + PERM_A, + SitePermissions.BLOCK, + 100, + BROWSER_B.currentURI, + 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, + BROWSER_A.currentURI, + 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 prepath. + */ +add_task(async function testTemporaryPermissionScope() { + let states = { + strict: { + same: [ + "https://example.com", + "https://example.com/sub/path", + "https://example.com:443", + ], + different: [ + "https://example.com", + "https://name:password@example.com", + "https://test1.example.com", + "http://example.com", + "http://example.org", + ], + }, + 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 + ); + + for (let otherUri of list) { + if (uri == otherUri) { + continue; + } + browser.currentURI = Services.io.newURI(otherUri); + + 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 URI 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( + null, + PERM_A, + SitePermissions.ALLOW, + SitePermissions.SCOPE_TEMPORARY, + testBrowser, + EXPIRY_MS_A, + overrideURI + ); + + 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 + testBrowser.currentURI = 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( + null, + PERM_A, + SitePermissions.ALLOW, + SitePermissions.SCOPE_TEMPORARY, + BROWSER_A, + EXPIRY_MS_B, + aboutURI + ); + 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. + browser.currentURI = 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); +}); diff --git a/browser/modules/test/unit/xpcshell.ini b/browser/modules/test/unit/xpcshell.ini index 8c4899bab304..e013ca260c2d 100644 --- a/browser/modules/test/unit/xpcshell.ini +++ b/browser/modules/test/unit/xpcshell.ini @@ -8,6 +8,7 @@ skip-if = toolkit == 'android' [test_HomePage_ignore.js] [test_Sanitizer_interrupted.js] [test_SitePermissions.js] +[test_SitePermissions_temporary.js] [test_SiteDataManager.js] [test_TabUnloader.js] [test_LaterRun.js]