CARVIEW |
This specification defines conformance criteria that apply to a single product: the user agent that implements the interfaces that it contains.
Implementations that use ECMAScript to expose the APIs defined in this specification MUST implement them in a manner consistent with the ECMAScript Bindings defined in the Web IDL specification [[!WEBIDL]].
Dependencies
The following concepts and interfaces are defined in [[!HTML]]:
- origin of a Document and a worker.
- queue a task
- fire a simple event
- event handler
- event handler event type
- Window
- global object
Promise objects are defined in [[!ECMASCRIPT]].
Scope of this document
This document's goal is to specify an API that will help developers to handle permissions on the Web platform. Web APIs have different ways to deal with permissions. The [[notifications]] API allows developers to request a permission and check the permission status explicitly. Others might only expose the status to web pages when they try to use the API, like the [[geolocation-API]] which fails if the permission was not granted without allowing the developer to check beforehand.
Being able to know whether an API call is going to prompt is useful in order to provide a good user experience. Unfortunately, more often than not, those prompts can't be controlled by developers.
The API specified in this document is meant to provide the tools so that web applications can improve their user experience when permissions are involved.
The solution described in this document is meant to be extensible but isn't meant to be applicable to all the current and future permissions available in the web platform. If you are working on a specification that has a permission model that wouldn't fit in the model described in this document, please contact the editors or file an issue. We would love to hear about it.
The initial intent of this document was to allow web applications to request and revoke permissions explicitly in addition to querying the permission status. This is an aspect of the specification that was controversial thus removed from the current document in a spirit of incremental changes: settling on a small API that can be improved.
Privacy considerations
Permission states can be used as an element of fingerprinting by websites. Usually websites could already have access to the information but often through actually using the API which could lead to a permission request UI if the permission was not already granted. Thus, even though this API doesn't expose new fingerprinting data to websites, it makes it easier for them to have discreet access to it. Therefore, implementations are encouraged to have an option for users to block (globally or selectively) the querying of permission states.
Permission descriptor
- required PermissionName name
A permission is defined by a name and other properties that depend on the name. The simplest permissions require only a name, but some others will require more information, in which case, they should have an associated PermissionDescriptor dictionary that inherits from PermissionDescriptor.
Permission Registry
- geolocation
- notifications
- push
- midi
- camera
- microphone
- speaker
- device-info
The PermissionName
enum defines the list of known
permission names.
Geolocation
The geolocation
permission is the permission associated with the usage of the
[[geolocation-API]].
Notifications
The notifications
permission is the permission associated with the usage of the
[[notifications]] API.
Push
The push
permission is the permission associated with the usage of the
[[push-api]]. It has an associated PermissionDescriptor,
PushPermissionDescriptor:
- boolean userVisibleOnly = false
Midi
The midi
permission is the permission associated with the usage of
[[webmidi]]. It has an associated PermissionDescriptor,
MidiPermissionDescriptor:
- boolean sysex = false
Media Devices
The camera
, microphone
, and
speaker
permissions are associated with permission to use media devices as
specified in [[GETUSERMEDIA]] and [[audio-output]].
These three permissions have an associated PermissionDescriptor:
- DOMString deviceId
A permission covers access to the device given in the associated descriptor.
If the descriptor does not have a deviceId, its semantic is that it queries for access to any device of that class.
If the deviceId is the special string "*", it queries for access to all devices of that class, whether they exist at the moment or are attached at a later time. When such a permission has been granted, queries for permission to any specific device MUST return "granted" as long as the deviceId is the ID of a connected device of the given class.
The device-info
permission controls access to names and capabilities of input and
output devices.
The permission storage identifier for permissions in this section MUST include the top level context's origin and the embedding status.
A successful call to the getUserMedia
function of
[[GETUSERMEDIA]] MUST cause permission to be granted for the returned
devices, and MAY cause other permissions to be granted.
Stopping a MediaStreamTrack MAY cause permission to be revoked for the associated device.
Permission Store
User agents MAY use a form of storage to keep
track of web site permissions. When they do, they MUST have a
permission storage identifier which is linked to a permission
storage entry. The permission storage identifier MUST
contain the website's origin and MAY contain other information like the
embedding status or the embedder's origin. The permission storage
entry MUST be a PermissionState or undefined
.
The steps to retrieve a permission storage entry of a permission storage identifier are as follows:
- If the user agent has a permission storage entry associated with the permission storage identifier in its permission store, it MUST return the permission storage entry.
- Otherwise, it MUST return
undefined
.
The steps to create a permission storage entry for a permission storage identifier are as follows:
- If the user agent has a permission storage entry associated with the permission storage identifier in its permission store, it MUST overwrite it to the given permission storage entry.
- Otherwise, it MUST write the new permission storage entry to its permission store.
The steps to delete a permission storage entry of a permission storage identifier are as follows:
- If the user agent has a permission storage entry associated with the permission storage identifier in its permission store, it MUST remove it.
Status of a permission
- granted
- denied
- prompt
The granted
state represents that the caller will be able
to successfuly access the feature without having the user agent
asking the user's permission.
The denied
state represents that the caller will not be
able to access the feature.
The prompt
state represents that the user agent will
be asking the user's permission if the caller tries to access the
feature. The user might grant, deny or dismiss the request.
The steps to retrieve the permission state of an origin
and global object for a given permission
are as
follows:
- Let identifier be the permission storage identifier
associated with the origin, global object and
permission
. - Run the steps to retrieve a permission storage entry.
- If the result of those steps are not
undefined
, return it and abort these steps. - Otherwise, the user agent MUST return a default value based
on user agent's defined heuristics. For example,
prompt
can be a default value but it can also be based on frequency of visits.
- readonly attribute PermissionState state
- attribute EventHandler onchange
A PermissionStatus
instance has an associated
permission that is a PermissionDescriptor.
The steps to update the state of a
PermissionStatus
instance are as follows:
- Let status be the
PermissionStatus
instance being updated. - Run the steps to retrieve the permission state using the
Document/Worker's origin, status'
global object and status' associated
permission then set the result of those steps to the
state
attribute.
The steps to create a PermissionStatus for a given
permission
are as follow:
- Let status be a
PermissionStatus
instance. - Set the given
permission
parameter as the status' associated permission. - Run the steps to update the state on status.
- Return status.
The state
attribute MUST return the latest value that was set while running the
update the state steps on the current instance.
The onchange
attribute is an
event handler whose corresponding event handler event
type is change
.
Whenever the user agent is aware that the state
of
a PermissionStatus
instance has changed, it MUST
asynchronously run the following steps:
- Let status be the
PermissionStatus
for which thestate
has changed. - Run the steps to update the state of status.
-
Queue a task on the permission task source to
fire a simple event named
change
at status.
Navigator and WorkerNavigator extension
A Permissions instance is exposed on the navigator
object for Window
and Worker
contexts.
- readonly attribute Permissions permissions;
- readonly attribute Permissions permissions;
Permissions interface
[Exposed=(Window,Worker)] interface Permissions { Promise<PermissionStatus> query(PermissionDescriptor permission); Promise<PermissionStatus> request((PermissionDescriptor or sequence<PermissionDescriptor>) permissions); Promise<PermissionStatus> revoke(PermissionDescriptor permission); };
When the query()
method is invoked, the user agent MUST run the following steps:
- If permission.name has an associated
PermissionDescriptor, convert the underlying ECMAScript object to
the associated PermissionDescriptor dictionary as described in
[[!WEBIDL]], then:
- If that operation failed, return a Promise rejected with
a
TypeError
and abort these steps. - Otherwise, set permission to the result of the operation.
- If that operation failed, return a Promise rejected with
a
- Let promise be a newly-created Promise.
- Return promise and continue the following steps asynchronously.
- Run the steps to create a PermissionStatus using permission and resolve promise with the result of those steps.
Promise.all()
. An example can be
found in the Examples section.
When the request()
method is invoked, the user agent MUST run the following steps:
- TODO
When the revoke()
method is invoked, the user agent MUST run the following steps:
- If permission.name has an associated
PermissionDescriptor, convert the underlying ECMAScript object to
the associated PermissionDescriptor dictionary as described in
[[!WEBIDL]], then:
- If that operation failed, return a Promise rejected with
a
TypeError
and abort these steps. - Otherwise, set permission to the result of the operation.
- If that operation failed, return a Promise rejected with
a
- Let promise be a newly-created Promise.
- Return promise and continue the following steps asynchronously.
- Let identifier be the permission storage identifier associated with the origin, global object and permission.
- Run the steps to delete a permission storage entry using identifier.
- Run the steps to create a PermissionStatus using permission and resolve promise with the result of those steps.
Examples
This example uses the Permissions API to decide whether local news should be shown using the Geolocation API or with a button offering to add the feature.
<script> navigator.permissions.query({name:'geolocation'}).then(function(result) { if (result.state == 'granted') { showLocalNewsWithGeolocation(); } else if (result.state == 'prompt') { showButtonToEnableLocalNews(); } // Don't do anything if the permission was denied. }); </script>
This example is using the notifications
permission for a
chat application to show a notification button depending on the
permission state.
<script> function updateNotificationButton(state) { document.getElementById('chat-notification-button').disabled = (state == 'denied'); } navigator.permissions.query({name:'notifications'}).then(function(result) { updateNotificationButton(result.state); result.addEventListener('change', function() { updateNotificationButton(this.state); }); }); </script>
This example is checking whether the page has the
geolocation
and the notifications
permissions
using Promise.all
.
<script> Promise.all([navigator.permissions.query({name:'geolocation'}), navigator.permissions.query({name:'notifications'})]) .then(function(result) { console.log('Geolocation permission state is ' + result[0].state); console.log('Notifications permission state is ' + result[1].state); }); </script>
This example is checking whether the page has the camera
permission for the first device in the list. Error checking omitted.
<script> navigator.mediaDevices.enumerateDevices() .then(function(devices) { return navigator.permissions.query({name:'camera', deviceId: devices[0].deviceId}) }) .then(function(result) { console.log('Camera permission to first camera is ' + result.state); } .catch(err => console.log('Bad things happened: ' + err.name)); </script>
Acknowledgments
The editors would like to thank Adrienne Porter Felt, Anne van Kesteren, Domenic Denicola, Jake Archibald and Wendy Seltzer for their help with the API design and editorial work.