Source code

Revision control

Copy as Markdown

Other Tools

# 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
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.
```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.