diff --git a/browser/base/content/test/general/browser.ini b/browser/base/content/test/general/browser.ini index 6e53cee197f6..aa9dbab827f7 100644 --- a/browser/base/content/test/general/browser.ini +++ b/browser/base/content/test/general/browser.ini @@ -432,6 +432,7 @@ support-files = support-files = permissions.html temporary_permissions_subframe.html + ../webrtc/get_user_media.html [browser_temporary_permissions_navigation.js] [browser_temporary_permissions_tabs.js] [browser_testOpenNewRemoteTabsFromNonRemoteBrowsers.js] diff --git a/browser/base/content/test/general/browser_temporary_permissions.js b/browser/base/content/test/general/browser_temporary_permissions.js index f926fd109b29..fbf67ccd8033 100644 --- a/browser/base/content/test/general/browser_temporary_permissions.js +++ b/browser/base/content/test/general/browser_temporary_permissions.js @@ -6,11 +6,16 @@ Cu.import("resource:///modules/SitePermissions.jsm", this); Cu.import("resource:///modules/E10SUtils.jsm"); -const SUBFRAME_PAGE = "https://example.com/browser/browser/base/content/test/general/temporary_permissions_subframe.html"; +const ORIGIN = "https://example.com"; +const PERMISSIONS_PAGE = getRootDirectory(gTestPath).replace("chrome://mochitests/content", ORIGIN) + "permissions.html" +const SUBFRAME_PAGE = getRootDirectory(gTestPath).replace("chrome://mochitests/content", ORIGIN) + "temporary_permissions_subframe.html" + +const EXPIRE_TIME_MS = 100; +const TIMEOUT_MS = 500; // Test that setting temp permissions triggers a change in the identity block. add_task(function* testTempPermissionChangeEvents() { - let uri = NetUtil.newURI("https://example.com"); + let uri = NetUtil.newURI(ORIGIN); let id = "geo"; yield BrowserTestUtils.withNewTab(uri.spec, function*(browser) { @@ -31,9 +36,61 @@ add_task(function* testTempPermissionChangeEvents() { }); }); +// Test that temporary permissions can be re-requested after they expired +// and that the identity block is updated accordingly. +// TODO (blocked by bug 1334421): Write a check for webrtc permissions, +// because they use a different code path. +add_task(function* testTempPermissionRequestAfterExpiry() { + yield SpecialPowers.pushPrefEnv({set: [ + ["privacy.temporary_permission_expire_time_ms", EXPIRE_TIME_MS], + ]}); + + let uri = NetUtil.newURI(ORIGIN); + let id = "geo"; + + yield BrowserTestUtils.withNewTab(PERMISSIONS_PAGE, function*(browser) { + let blockedIcon = gIdentityHandler._identityBox + .querySelector(`.blocked-permission-icon[data-permission-id='${id}']`); + + SitePermissions.set(uri, id, SitePermissions.BLOCK, SitePermissions.SCOPE_TEMPORARY, browser); + + Assert.deepEqual(SitePermissions.get(uri, id, browser), { + state: SitePermissions.BLOCK, + scope: SitePermissions.SCOPE_TEMPORARY, + }); + + ok(blockedIcon.hasAttribute("showing"), "blocked permission icon is shown"); + + yield new Promise((c) => setTimeout(c, TIMEOUT_MS)); + + Assert.deepEqual(SitePermissions.get(uri, id, browser), { + state: SitePermissions.UNKNOWN, + scope: SitePermissions.SCOPE_PERSISTENT, + }); + + let popupshown = BrowserTestUtils.waitForEvent(PopupNotifications.panel, "popupshown"); + + // Request a permission; + yield BrowserTestUtils.synthesizeMouseAtCenter(`#${id}`, {}, browser); + + yield popupshown; + + ok(!blockedIcon.hasAttribute("showing"), "blocked permission icon is not shown"); + + let popuphidden = BrowserTestUtils.waitForEvent(PopupNotifications.panel, "popuphidden"); + + let notification = PopupNotifications.panel.firstChild; + EventUtils.synthesizeMouseAtCenter(notification.secondaryButton, {}); + + yield popuphidden; + + SitePermissions.remove(uri, id, browser); + }); +}); + // Test that temp blocked permissions requested by subframes (with a different URI) affect the whole page. add_task(function* testTempPermissionSubframes() { - let uri = NetUtil.newURI("https://example.com"); + let uri = NetUtil.newURI(ORIGIN); let id = "geo"; yield BrowserTestUtils.withNewTab(SUBFRAME_PAGE, function*(browser) { diff --git a/browser/modules/PermissionUI.jsm b/browser/modules/PermissionUI.jsm index 341b2aa2197d..c4ccf02f9d04 100644 --- a/browser/modules/PermissionUI.jsm +++ b/browser/modules/PermissionUI.jsm @@ -279,6 +279,11 @@ this.PermissionPromptPrototype = { this.allow(); return; } + + // Tell the browser to refresh the identity block display in case there + // are expired permission states. + this.browser.dispatchEvent(new this.browser.ownerGlobal + .CustomEvent("PermissionStateChange")); } // Transform the PermissionPrompt actions into PopupNotification actions. diff --git a/browser/modules/SitePermissions.jsm b/browser/modules/SitePermissions.jsm index 95a992448c66..9648792439d3 100644 --- a/browser/modules/SitePermissions.jsm +++ b/browser/modules/SitePermissions.jsm @@ -129,6 +129,13 @@ const TemporaryBlockedPermissions = { }, }; +/** + * A module to manage permanent and temporary permissions + * by URI and browser. + * + * Some methods have the side effect of dispatching a "PermissionStateChange" + * event on changes to temporary permissions, as mentioned in the respective docs. + */ this.SitePermissions = { // Permission states. UNKNOWN: Services.perms.UNKNOWN_ACTION, @@ -296,6 +303,9 @@ this.SitePermissions = { /** * Returns the state and scope of a particular permission for a given URI. * + * This method will NOT dispatch a "PermissionStateChange" event on the specified + * browser if a temporary permission was removed because it has expired. + * * @param {nsIURI} uri * The URI to check. * @param {String} permissionID @@ -344,6 +354,8 @@ this.SitePermissions = { /** * Sets the state of a particular permission for a given URI or browser. + * This method will dispatch a "PermissionStateChange" event on the specified + * browser if a temporary permission was set * * @param {nsIURI} uri * The URI to set the permission for. @@ -402,6 +414,8 @@ this.SitePermissions = { /** * Removes the saved state of a particular permission for a given URI and/or browser. + * This method will dispatch a "PermissionStateChange" event on the specified + * browser if a temporary permission was removed. * * @param {nsIURI} uri * The URI to remove the permission for. diff --git a/browser/modules/test/browser_SitePermissions_expiry.js b/browser/modules/test/browser_SitePermissions_expiry.js index cc321426029a..e5914edbd72f 100644 --- a/browser/modules/test/browser_SitePermissions_expiry.js +++ b/browser/modules/test/browser_SitePermissions_expiry.js @@ -6,10 +6,13 @@ Cu.import("resource:///modules/SitePermissions.jsm", this); +const EXPIRE_TIME_MS = 100; +const TIMEOUT_MS = 500; + // This tests the time delay to expire temporary permission entries. add_task(function* testTemporaryPermissionExpiry() { SpecialPowers.pushPrefEnv({set: [ - ["privacy.temporary_permission_expire_time_ms", 100], + ["privacy.temporary_permission_expire_time_ms", EXPIRE_TIME_MS], ]}); let uri = Services.io.newURI("https://example.com") @@ -23,7 +26,7 @@ add_task(function* testTemporaryPermissionExpiry() { scope: SitePermissions.SCOPE_TEMPORARY, }); - yield new Promise((c) => setTimeout(c, 500)); + yield new Promise((c) => setTimeout(c, TIMEOUT_MS)); Assert.deepEqual(SitePermissions.get(uri, id, browser), { state: SitePermissions.UNKNOWN, diff --git a/browser/modules/webrtcUI.jsm b/browser/modules/webrtcUI.jsm index 4bf2bbe3a34c..0b8890336879 100644 --- a/browser/modules/webrtcUI.jsm +++ b/browser/modules/webrtcUI.jsm @@ -373,6 +373,11 @@ function prompt(aBrowser, aRequest) { return; } + // Tell the browser to refresh the identity block display in case there + // are expired permission states. + aBrowser.dispatchEvent(new aBrowser.ownerGlobal + .CustomEvent("PermissionStateChange")); + let uri = Services.io.newURI(aRequest.documentURI); let host = getHost(uri); let chromeDoc = aBrowser.ownerDocument;