# 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](https://searchfox.org/mozilla-central/source/browser/modules/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 `` 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. ```js 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. ```js 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. ```js Services.perms.removeFromPrincipalForBrowser(principal, "geo", browserId); ``` ### `removeAllForBrowser` Removes all browser-scoped permissions for a given browser ID. ```js 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. ```js // 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. ```js 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. ```js let perms = Services.perms.getAllForBrowser(principal, browserId); ``` ### `copyBrowserPermissions` Copies all browser-scoped permissions from one browser to another. Used during tab-swapping operations. ```js 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.