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 ofexample.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 meanshttps://sub.example.comandhttps://example.comare 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.
In both cases the principal is that of the tab’s top-level document.
Callers must not key a browser-scoped permission to a subframe principal: such
an entry is not cleared when the subframe goes away, can be read by that origin
again under a different first party in the same tab, and is invisible to
getAllForBrowser and therefore to the permission panel. Where a permission
belongs to an embedded third party, encode that third party in the permission
type and store it under the top-level principal, as 3rdPartyStorage^<origin>
does.
Lifetime
Browser-scoped permissions have two lifetime mechanisms:
Tab lifetime: All browser-scoped permissions for a tab are automatically removed when the tab’s top-level browsing context is discarded (observed via the
"browsing-context-discarded"notification). Subframe discards are ignored: child browsing contexts share their top-level browser’s browser ID, so treating them as tab teardown would clear unrelated grants for the whole tab. Browsing context replacements (e.g., process switches) where the browser ID transfers to a new context are not treated as discards either.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
expireTimeMSparameter toaddFromPrincipalForBrowserspecifies 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"). For "added", "changed" and
"deleted" the subject is an nsIPermission object with the browserId
attribute set.
A bulk clear (removeAllForBrowser, removeByActionForBrowser) has no single
permission to report, so it uses the data string "cleared" and passes an
nsISupportsPRUint64 carrying the browser ID instead. Observers must
discriminate on the data string before casting the subject.
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.