Browser-Scoped Permissions

Browser-scoped permissions are temporary, per-tab permissions managed directly by the permission manager. They are never persisted to disk and are automatically cleared when the tab is closed.

These permissions were previously managed by SitePermissions.sys.mjs as “temporary permissions” and have been moved into the C++ permission manager for better integration with the Permissions API and content processes. Most consumers (geolocation, notifications, permission UI, etc.) should continue to go through SitePermissions.sys.mjs rather than calling the ForBrowser methods directly. SitePermissions provides higher-level helpers for combining browser-scoped and persistent permissions and is the expected interface for browser UI code.

Overview

Browser-scoped permissions are keyed by a browser ID (the unique identifier of a tab’s <xul:browser> element). They are stored in an in-memory hash table (mBrowserPermissionTable) and are not written to the permissions database.

Permissions created through this mechanism use the EXPIRE_SESSION_TAB expiration type and fire the "browser-perm-changed" observer notification (as opposed to "perm-changed" for regular permissions).

Keying: Origin vs. Site

Browser-scoped permissions use different scoping depending on the permission action:

  • Deny (DENY_ACTION) permissions are site-scoped (keyed by base domain). When a user blocks a permission, the block applies to the entire site (e.g., all subdomains of example.com). This prevents sites from nagging users with repeated permission requests from different subdomains.

  • All other actions (ALLOW_ACTION, PROMPT_ACTION, etc.) are origin-scoped. This means https://sub.example.com and https://example.com are treated as separate origins, each with their own permission.

When a permission changes between deny and a non-deny action for the same type, the old entry under the other key style is automatically removed.

Lifetime

Browser-scoped permissions have two lifetime mechanisms:

  1. Tab lifetime: All browser-scoped permissions for a tab are automatically removed when the tab’s browsing context is discarded (observed via the "browsing-context-discarded" notification). Browsing context replacements (e.g., process switches) where the browser ID transfers to a new context are not treated as discards.

  2. Timer-based expiry: Permissions can optionally be given a duration in milliseconds. A timer is scheduled in the parent process to automatically remove the permission after the specified duration. The expireTimeMS parameter to addFromPrincipalForBrowser specifies the duration relative to the current time, not an absolute timestamp.

If no expiry duration is specified, the permission lives until the tab is closed.

Interfacing with Browser-Scoped Permissions

Browser-scoped permissions can be accessed through the nsIPermissionManager interface using the ForBrowser family of methods.

addFromPrincipalForBrowser

Adds a browser-scoped permission for a given principal, type, action, and browser ID. Optionally specify an expiry duration in milliseconds.

let principal = gBrowser.selectedBrowser.contentPrincipal;
let browserId = gBrowser.selectedBrowser.browserId;

// Allow geolocation for this tab (no expiry)
Services.perms.addFromPrincipalForBrowser(
  principal,
  "geo",
  Services.perms.ALLOW_ACTION,
  browserId
);

// Block camera for this tab, expires in 1 hour
Services.perms.addFromPrincipalForBrowser(
  principal,
  "camera",
  Services.perms.DENY_ACTION,
  browserId,
  1000 * 60 * 60
);

testForBrowser

Returns the permission action for a given principal, type, and browser ID. Returns UNKNOWN_ACTION if no browser-scoped permission is set. Origin-scoped permissions are checked before site-scoped ones.

let perm = Services.perms.testForBrowser(
  principal,
  "geo",
  browserId
);
if (perm == Services.perms.ALLOW_ACTION) {
  // Geolocation is temporarily allowed for this tab
}

removeFromPrincipalForBrowser

Removes a browser-scoped permission. Both origin-scoped and site-scoped keys are tried.

Services.perms.removeFromPrincipalForBrowser(principal, "geo", browserId);

removeAllForBrowser

Removes all browser-scoped permissions for a given browser ID.

Services.perms.removeAllForBrowser(browserId);

removeByActionForBrowser

Removes all browser-scoped permissions with a specific action for a given browser ID. Useful for clearing only blocks or only allows.

// Remove all deny permissions for this tab
Services.perms.removeByActionForBrowser(browserId, Services.perms.DENY_ACTION);

getForBrowser

Returns the browser-scoped permission as an nsIPermission object, or null if not set.

let perm = Services.perms.getForBrowser(principal, "geo", browserId);
if (perm) {
  // perm.capability, perm.expireTime, perm.browserId are available
}

getAllForBrowser

Returns all browser-scoped permissions for a given principal and browser ID as an array of nsIPermission objects.

let perms = Services.perms.getAllForBrowser(principal, browserId);

copyBrowserPermissions

Copies all browser-scoped permissions from one browser to another. Used during tab-swapping operations.

Services.perms.copyBrowserPermissions(srcBrowserId, destBrowserId);

Observer Notifications

Browser-scoped permission changes fire the "browser-perm-changed" notification (not "perm-changed"). The subject is an nsIPermission object with the browserId attribute set, and the data string is one of "added", "changed", or "deleted".

Interaction with Regular Permissions

When both a browser-scoped permission and a regular (persistent or session-scoped) permission exist for the same principal and type, the browser-scoped permission takes precedence for that tab. Other tabs without a browser-scoped permission will use the regular permission as usual.

The SitePermissions.sys.mjs module provides a higher-level interface that combines browser-scoped and regular permissions for UI display purposes.