1. The permissions API
The permissions API lets an extension check, request, and relinquish its own optional permissions and optional host permissions at runtime, instead of requiring every permission at install time.
An optional permission is a permission the extension may hold
without it being granted at install: one listed in optional_permissions,
or one listed in permissions that the user agent has withheld until the
extension requests it.
A named permission required by the extension’s manifest, or optional and granted to the extension at runtime, is part of the extension’s granted permissions.
1.1. Types
1.1.1. The Permissions dictionary
A Permissions dictionary’s permissions member is a list of named API permission strings,
excluding host permissions. Its origins member is a list of match patterns,
validated using the same match pattern grammar used for the host_permissions manifest key.
Which strings are valid entries in permissions is implementation-defined: each browser
recognizes its own set of permission names, and a name outside a given browser’s set is not a
valid entry for that browser. An entry in permissions or origins that the user agent does
not recognize is an error. No permission is granted or revoked, and the error is surfaced to
the caller.
-
activeTab -
alarms -
clipboardWrite -
contextMenus -
cookies -
declarativeNetRequest -
declarativeNetRequestFeedback -
declarativeNetRequestWithHostAccess -
nativeMessaging -
notifications -
scripting -
storage -
tabs -
unlimitedStorage -
webNavigation -
webRequest
A name outside this list is recognized by at most two of the three, so it is not portable.
-
Chrome: rejects the returned promise.
-
Firefox: throws synchronously.
-
Safari: throws synchronously for
contains(), but rejects the promise forrequest()andremove().
A synchronous throw escapes a .catch() attached to the returned promise, so
browser.permissions.contains(bad).then(ok).catch(err) behaves differently across browsers.
dictionary {Permissions sequence <DOMString >;permissions sequence <DOMString >; };origins
1.2. Methods
[Exposed =WebExtensions ]interface {permissions Promise <Permissions >getAll ();Promise <boolean >contains (optional Permissions );permissions Promise <boolean >request (optional Permissions );permissions Promise <undefined >remove (optional Permissions );permissions readonly attribute PermissionsEvent onAdded ;readonly attribute PermissionsEvent onRemoved ; };
1.2.1. getAll()
getAll() returns the extension’s complete current set of
permissions and match patterns: its granted permissions and granted host permissions.
-
Let result be a new
Permissions. -
Set result["permissions"] to the extension’s granted permissions.
-
Set result["origins"] to the extension’s granted host permissions.
-
Resolve with result.
May getAll() return an all-hosts pattern that no single granted
permission contains, when the granted patterns together cover all hosts?
Safari does, returning *://*/*. Chrome and Firefox return only what was
granted.
1.2.2. contains()
contains(permissions) checks whether the extension’s
granted permissions and granted host permissions include every permission and origin
named in permissions.
-
For each named permission in permissions["permissions"]:
-
If it is not in the extension’s granted permissions, resolve with
false.
-
-
For each match pattern in permissions["origins"]:
-
If it is not subsumed by the extension’s granted host permissions, resolve with
false.
-
-
Resolve with
true.
1.2.3. request()
request(permissions) asks the user to grant the extension
some or all of permissions that are not already part of its granted permissions or
granted host permissions.
-
If this call is not happening within a user activation, reject with an error.
-
If any named permission in permissions["permissions"] is not an optional permission of the extension, reject with an error.
-
If any match pattern in permissions["origins"] is not subsumed by an optional host permission of the extension, reject with an error.
-
If every named permission in permissions["permissions"] is in the extension’s granted permissions, and every match pattern in permissions["origins"] is subsumed by the extension’s granted host permissions, resolve with
true. -
Prompt the user to approve the permissions and origins in permissions that are not already part of the extension’s granted permissions or granted host permissions. Skip this prompt, and grant those permissions and origins automatically, if they carry no additional user-facing warnings beyond what the extension is already granted.
-
If the user does not approve the prompt, resolve with
false. -
Grant the extension the requested permissions and origins not already in its granted permissions or granted host permissions. This fires
onAddedfor the newly granted set. -
Resolve with
true.
What does request() reject with, for a missing user activation, an
unlisted permission, or an unlisted origin? Chrome rejects with an Error:
one fixed message for a missing user activation, and a second, shared
fixed message for an unlisted permission or an unlisted origin. Firefox
rejects with an ExtensionError. Safari reports a callback/promise error
with no named type.
What happens when no window is available to show the prompt in,
separate from the user-activation requirement? Chrome rejects, distinctly
from the no-activation rejection. Firefox has no window requirement and
prompts through whatever surface is available. Safari has no window
concept: the embedding application prompts, and the call resolves false
if it never responds.
Chrome and Safari exclude the entire permissions namespace from content scripts. Firefox
exposes request() alone to content scripts, keeping the rest of the namespace restricted to
background and extension-page contexts.
A user agent may decline a request without prompting the user, for example where administrative policy has already denied the permission.
Does a request declined by policy reject, or resolve false?
Chrome and Firefox reject. Safari resolves false without prompting.
1.2.4. remove()
remove(permissions) relinquishes some or all of the
extension’s optional granted permissions and granted host permissions.
-
If any named permission or match pattern in permissions is not an optional permission or optional host permission of the extension, reject with an error.
-
If any named permission or match pattern in permissions is not declared anywhere in the extension’s manifest, reject with an error.
-
Revoke each named permission in permissions["permissions"] that is in the extension’s granted permissions, and each match pattern in permissions["origins"] that is subsumed by the extension’s granted host permissions. Entries in permissions not granted are ignored rather than treated as an error. This fires
onRemovedfor the revoked set, if any. -
Resolve with
undefined.
remove() resolve with undefined here, when every shipping browser resolves true?
-
All three browsers currently resolve
trueon success; none has been found to resolvefalse. -
w3c/webextensions#1069 (open) argues a boolean that never varies carries no information, and proposes dropping it. The sibling case is
alarms.clearAll(), w3c/webextensions#1055.
request() and contains() are not affected: both resolve a boolean that carries real
information (whether the request was granted, whether the extension has the permission), so
neither changes.
1.3. Events
Should the event-listener pattern (addListener(), removeListener(), hasListener())
used by every event in this specification be defined once, as a shared events.Event type,
rather than duplicated as PermissionsEvent in each namespace that fires one?
PermissionsEvent below is a minimal local stand-in pending that decision.
callback =PermissionsListener undefined (optional Permissions );changed interface {PermissionsEvent undefined (addListener PermissionsListener );listener undefined (removeListener PermissionsListener );listener boolean (hasListener PermissionsListener ); };listener
1.3.1. onAdded
The onAdded event fires with a Permissions
dictionary listing the permissions and origins newly granted to the extension, whenever the
extension’s granted permissions or granted host permissions increase. This includes grants made through
request, and grants made through user agent UI outside of the permissions API,
for example the user agent’s own controls for granting a withheld host permission.
onAdded/onRemoved fire for a permission or host-permission change driven entirely by
administrative or managed policy?
-
Chrome does not fire either event for a policy-driven change.
-
Firefox and Safari fire the event for a policy-driven change, the same as for any other change.
1.3.2. onRemoved
The onRemoved event fires with a Permissions
dictionary listing the permissions and origins no longer granted to the extension, whenever the
extension’s granted permissions or granted host permissions decrease, whether through remove or through user
agent UI.