1. Introduction
This section is non-normative.
A HID (Human Interface Device) is a type of device that takes input from or provides output to humans. It also refers to the HID protocol, a standard for bi-directional communication between a host and a device that is designed to simplify the installation procedure. The HID protocol was originally developed for USB devices but has since been implemented over many other protocols, including Bluetooth.
The web platform already supports input from many HID devices. Keyboards, pointing devices, and gamepads are all typically implemented using the HID protocol. However, this support relies on the operating system’s HID drivers that transform the HID input into high-level input APIs. Devices that are not well supported by the host’s HID driver are often inaccessible to web pages. Similarly, the outputs on most devices that are not supported by the host’s HID driver are inaccessible.
See also the related Explainer document.
2. Motivating Applications
This section is non-normative.
2.1. Niche devices
The most common classes of HID input devices are already
well-supported on the web platform through existing high-level input
APIs. For instance, mouse input is accessible through
PointerEvent and keyboard input is accessible through
KeyboardEvent. Input from these devices is handled using the
host’s native HID driver and typically does not require
device-specific drivers or configuration to work correctly. WebHID is
not intended for devices like these that are already well-supported
through high-level input APIs.
For some classes of HID devices, the web platform supports some features of the device but limits access to other features. For instance, [GAMEPAD] supports the input capabilities of most game controllers but does not support less common capabilities like LED indicators or audio. These features are often not well-supported by host APIs and adding support within the user agent can lead to significant complexity. WebHID would give applications an alternative when the functionality provided by the high-level API is incomplete.
Many HID devices are not supported through any web platform API. The HID specification describes a wide array of devices that could be supported through HID, including virtual reality controls, flight simulators, medical equipment, and more. WebHID would allow these devices to be used without requiring additional drivers or modification to the user agent.
2.2. Prototyping, hobbyists, and educational devices
HID is attractive for prototyping and hobbyist applications because it allows devices to use the host’s generic HID driver instead of requiring a driver for each host where the device will be used. The simplified installation procedure also makes it easier for educators to deploy a device to a classroom of students without modifying the system configuration on each host. Providing access to HID devices through the web platform would further reduce installation requirements, particularly for devices that are currently only supported through host-specific applications.
3. Security and Privacy Considerations
This section is non-normative.
3.1. Abusing Access to a Device
HID peripherals may expose powerful functionality that should not be made accessible to the page without explicit consent from the user. For instance, a HID peripheral may have sensors that allow it to collect information about its surroundings. A device may store private information that should not be revealed or overwritten. Many devices expose functionality that allow the device firmware to be upgraded. Operating systems typically do not restrict access to HID devices from applications, and this access can occasionally be abused to damage the device or corrupt the data stored on it.
In some cases, a device may expose functionality that should not be accessible at all from a web page. Specific devices may be blocked by recognizing the device by its vendor ID and product ID and hiding such devices during enumeration. The report descriptor allows a device to describe its own capabilities, and this information can be used to block classes of devices even when the vendor ID and product ID cannot be known in advance. This can be accomplished by inspecting the HID usage values assigned to collections within the report descriptor; for instance, access to keyboard-like devices can be denied by denying access to devices that include a top-level collection with the HID usage for a keyboard.
To mitigate abuse, a device will not be made available to the page until the user has granted explicit access. Access must first be requested by the page and will be granted once the user has selected the device from a chooser displaying all available devices. Once a page has been granted access, an indicator will be displayed to indicate the device is in use.
3.2. Attacking a Device
The HID protocol is extremely versatile, and a wide variety of devices have been designed that take advantage of this versatility. As a result, there are many possibilities for how access to a device could allow a malicious page to damage the device itself. It is relatively common for HID peripherals to allow firmware updates to be sent using custom HID reports. Device manufacturers must take care to design the firmware upgrade mechanism to verify that the firmware binary is authentic, and should also protect against downgrades that could reduce the security of a device. Untrusted upgrades could be used to add new capabilities to a device or cause it to masquerade as an entirely different type of device.
Some devices may be damaged when inputs are provided outside of the expected range. For instance, the rumble feature on a gamepad could become damaged if a page sends an invalid rumble command.
In general, these types of attacks are device-specific and cannot be mitigated at the API level.
3.3. Attacking the Host
If a malicious page gains access to a device, in some cases this access can be used to attack the host. A major concern is whether the device can be used to generate trusted input events. These events serve as a proxy for user intent and can be used to access more powerful web platform features.
Mice and keyboards are typically implemented as HID peripherals, and are also capable of generating trusted input. A HID keyboard or mouse may contain advanced features like programmable macros, which store a sequence of inputs and allow the sequence to be played back at a later time. Device manufacturers must take care to design such features in a way that would prevent a malicious app from reprogramming the device with an unexpected input sequence, and must also protect against triggering macro playback without the user’s explicit consent.
3.4. Mitigation
To mitigate security and privacy risks, it is recommended that user agents implement a chooser-based flow for requesting device access. When a page requests access to a HID device, the user agent should display a dialog that informs the user that the page wants to access a device. The dialog should display a list of connected HID devices and ask the user to select a device that will be exposed to the page. To reduce the possibility of accidental clicks, the dialog should require two separate interactions: one interaction to select the device from the list and a second interaction to confirm the selection and dismiss the dialog.
4. Extensions to the Navigator interface
[SecureContext ]partial interface Navigator { [SameObject ]readonly attribute HID hid ; };
4.1. hid attribute
When getting, the hid attribute always returns the same
instance of the HID object.
5. Extensions to the WorkerNavigator interface
[Exposed =(DedicatedWorker ,ServiceWorker ),SecureContext ]partial interface WorkerNavigator { [SameObject ]readonly attribute HID hid ; };
5.1. hid attribute
When getting, the hid attribute always returns the same
instance of the HID object.
6. HID interface
[Exposed =(DedicatedWorker ,ServiceWorker ,Window ),SecureContext ]interface HID :EventTarget {attribute EventHandler onconnect ;attribute EventHandler ondisconnect ;Promise <sequence <HIDDevice >>getDevices (); [Exposed =Window ]Promise <sequence <HIDDevice >>requestDevice (HIDDeviceRequestOptions ); };options
Note: The user agent’s implement may choose to conditionally expose
hid attribute of the Navigator when the relevant global object
of the Navigator is ServiceWorkerGlobalScope. For example,
in the Browser extension contexts of [browserext].
Instances of HID are created with the internal slots described in
the following table:
| Internal slot | Initial value | Description (non-normative) |
|---|---|---|
[[devices]]
| An empty ordered set |
An ordered set of HIDDevice instances representing the
HID interfaces currently available on the system
|
onconnectattribute, of type EventHandler-
onconnectis an event handler IDL attribute for theconnectevent type. ondisconnectattribute, of type EventHandler-
ondisconnectis an event handler IDL attribute for thedisconnectevent type.
For example, USB devices are implemented as a collection of interfaces, each of which may specify an interface class. When a device has multiple USB interfaces that use the HID class, each interface is enumerated as a separate HID interface.
-
Let device be a
HIDDevice. -
Set device.
[[vendorId]]to the vendor ID of the device that exposes the interface. -
Set device.
[[productId]]to the product ID of the device that exposes the interface. -
Set device.
[[productName]]to the product name of the device that exposes the interface. -
Set device.
[[collections]]to the result of parsing the Report descriptor. -
Let hid be the result of calling device’s relevant global object’s
Navigatorobject’shidgetter if device’s relevant global object is aWindowobject, otherwise be the result of calling device’s relevant global object’sWorkerNavigatorobject’shidgetter. -
Append device to hid.
[[devices]]. -
If the user has allowed the site to access device as the result of a previous call to
requestDevice(), then queue a global task on the relevant global object of hid using the HID device task source to fire an event namedconnectat hid usingHIDConnectionEventwith itsdeviceattribute initialized to device.
The report descriptor format is described in [USBIF-HID-CLASS] section 6.2.2. The algorithm below is intended to implement the same parsing algorithm described in that section.
The report descriptor contains three classes of items. Main items are used to define and group data fields. Global items and Local items describe the data fields. Local items define characteristics that only apply to the next Main item, while Global items define characteristics that affect all subsequently defined Main items.
Each item has an item data field containing 0, 1, 2, or 4 bytes of data associated with the item and a bSize field that describes how many bytes are used to store the item data field. Each item also has a tag that identifies its type.
-
Let items be the result of converting the report descriptor byte sequence into a list of Main items, Global items, and Local items.
-
Initialize collections to be an empty sequence of
HIDCollectionInfo. -
Initialize ancestors to be an empty list.
-
Initialize localState to be an empty ordered map.
-
Initialize usages to be an empty sequence of
unsigned long. -
Initialize stringIndices to be an empty list.
-
Initialize globalStack to be an empty stack.
-
Initialize initialGlobalState to be an empty ordered map.
-
Initialize reportId to 0.
-
Set initialGlobalState["
unitSystem"] to"none". -
Set initialGlobalState["
unitExponent"] to 0. -
Set initialGlobalState["
unitFactorLengthExponent"] to 0. -
Set initialGlobalState["
unitFactorMassExponent"] to 0. -
Set initialGlobalState["
unitFactorTimeExponent"] to 0. -
Set initialGlobalState["
unitFactorTemperatureExponent"] to 0. -
Set initialGlobalState["
unitFactorCurrentExponent"] to 0. -
Set initialGlobalState["
unitFactorLuminousIntensityExponent"] to 0. -
Set initialGlobalState["
logicalMinimum"] to 0. -
Set initialGlobalState["
logicalMaximum"] to 0. -
Set initialGlobalState["
physicalMinimum"] to 0. -
Set initialGlobalState["
physicalMaximum"] to 0. -
Push initialGlobalState onto globalStack.
-
For each item of items:
Note: Main items are described in [USBIF-HID-CLASS] sections 6.2.2.4 through 6.2.2.6. An item with the Input tag, Output tag, or Feature tag defines a field within an input, output, or feature report. The item data field of a Main item with an Input tag, Output tag, or Feature tag is a bit field containing several boolean parameters describing the data field. An item with the Collection tag defines a new collection nested within the current collection, or a new top-level collection if there is no current collection. The item data field of a Main item with a Collection tag contains the collection type. An item with the End Collection tag closes the current collection.
-
If item is a Main item:
-
If item has the Input tag, Output tag, or Feature tag:
-
Let globalState be the last item of globalStack.
-
Let reportId be globalState["
reportId"]. -
Let reportItem be the result of creating a HID report item with item, localState, globalState, usages, and strings.
-
For each collection of ancestors:
-
Let reports be:
-
collection["
inputReports"] if item has the Input tag, -
collection["
outputReports"] if item has the Output tag, or -
collection["
featureReports"] if item has the Feature tag.
-
-
If reports contains an item with its
reportIdattribute equal to reportId, then let report be that item.Otherwise:
-
Let report be a new
HIDReportInfodictionary. -
Set report["
reportId"] to reportId. -
Set report["
items"] to an empty sequence ofHIDReportItem. -
Append report to reports.
-
-
-
-
If item has the Collection tag:
-
Let collection be a new
HIDCollectionInfodictionary. -
Let usage be the first item of usages.
-
If the size of usages is greater than 1, remove the first item of usages.
-
Set collection["
usage"] to usage. -
Set collection["
type"] to the value representing the collection’s type. -
Set collection["
children"] to an empty sequence ofHIDCollectionInfo. -
Set collection["
inputReports"] to an empty sequence ofHIDReportInfo. -
Set collection["
outputReports"] to an empty sequence ofHIDReportInfo. -
Set collection["
featureReports"] to an empty sequence ofHIDReportInfo. -
If ancestors is empty, append collection to collections.
Otherwise:
-
Append collection to ancestors.
-
-
If item has the End Collection tag, then remove the last item of ancestors.
-
Set localState to be an empty ordered map.
-
Set usages to be an empty sequence of
unsigned long.Note: Global items are described in section 6.2.2.7. An item with the Usage Page tag, Logical Minimum tag, Logical Maximum tag, Physical Minimum tag, Physical Maximum tag, Unit Exponent tag, Unit tag, Report Size tag, Report ID tag, or Report Count tag modifies the respective field of the global item state table. An item with the Push tag pushes a copy of the current global item state table onto the item state stack. An item with the Pop tag replaces the global item state table with the top of the item state stack. The global Report ID value is not affected when manipulating the item state stack.
-
If item is a Global item:
-
Let globalState be the last item of globalStack.
-
Let value be the item data field interpreted as an
unsigned long. -
If item has the Usage Page tag, then set globalState["
usagePage"] to value. -
If item has the Logical Minimum tag, then set globalState["
logicalMinimum"] to value. -
If item has the Logical Maximum tag, then set globalState["
logicalMaximum"] to value. -
If item has the Physical Minimum tag, then set globalState["
physicalMinimum"] to value. -
If item has the Physical Maximum tag, then set globalState["
physicalMaximum"] to value. -
If item has the Report Size tag, then set globalState["
reportSize"] to value. -
If item has the Report ID tag, then set globalState["
reportId"] to value. -
If item has the Report Count tag, then set globalState["
reportCount"] to value. -
If item has the Unit tag:
-
Let nibbles be the result of interpreting value as a sequence of eight 4-bit signed two’s complement integers starting from the low-order bits.
-
If nibbles[0] is:
-
0, then set globalState["
unitSystem"] to"none" -
1, then set globalState["
unitSystem"] to"si-linear" -
2, then set globalState["
unitSystem"] to"si-rotation" -
3, then set globalState["
unitSystem"] to"english-linear" -
4, then set globalState["
unitSystem"] to"english-rotation" -
-1, then set globalState["
unitSystem"] to"vendor-defined" -
Otherwise, set globalState["
unitSystem"] to"reserved"
-
-
Set globalState["
unitFactorLengthExponent"] to nibbles[1]. -
Set globalState["
unitFactorMassExponent"] to nibbles[2]. -
Set globalState["
unitFactorTimeExponent"] to nibbles[3]. -
Set globalState["
unitFactorTemperatureExponent"] to nibbles[4]. -
Set globalState["
unitFactorCurrentExponent"] to nibbles[5]. -
Set globalState["
unitFactorLuminousIntensityExponent"] to nibbles[6].
-
-
If item has the Unit Exponent tag, then set globalState["
unitExponent"] to the result of interpreting the lowest 4 bits of the item data field as a signed two’s complement integer. -
If item has the Push tag, then push a copy of globalState onto globalStack.
-
Note: Local items are described in section 6.2.2.8. An item with the Usage tag, Usage Minimum tag, Usage Maximum tag, String Index tag, String Minimum tag, or String Maximum tag modifies the respective field of the local item state table. The local item state is cleared after each Main item.
-
-
If item is a Local item:
-
Let value be the item data field interpreted as an
unsigned long. -
If item has the Usage tag, Usage Minimum tag, or Usage Maximum tag, and item’s bSize field is 1 or 2:
-
If item has the Usage Minimum tag, then set localState["
usageMinimum"] to value. -
If item has the Usage Maximum tag, then set localState["
usageMaximum"] to value. -
If item has the String Index tag, then append value to stringIndices.
-
If item has the String Minimum tag, then set localState[
"stringMinimum"] to value. -
If item has the String Maximum tag, then set localState[
"stringMaximum"] to value.
-
-
-
Let reportItem be a new
HIDReportItemdictionary. -
Let value be the item data field interpreted as an
unsigned long. -
Let bitfield be a list of
booleanvalues representing each of the bits of value, starting from the low-order bits. -
Set reportItem["
isConstant"] to bitfield[0]. -
Set reportItem["
isArray"] to !bitfield[1]. -
Set reportItem["
isAbsolute"] to !bitfield[2]. -
Set reportItem["
wrap"] to bitfield[3]. -
Set reportItem["
isLinear"] to !bitfield[4]. -
Set reportItem["
hasPreferredState"] to !bitfield[5]. -
Set reportItem["
hasNull"] to bitfield[6]. -
Set reportItem["
isVolatile"] to bitfield[7]. -
Set reportItem["
isBufferedBytes"] to bitfield[8]. -
Set reportItem["
usages"] to usages. -
Set reportItem["
reportSize"] to globalState["reportSize"]. -
Set reportItem["
reportCount"] to globalState["reportCount"]. -
Set reportItem["
unitExponent"] to globalState["unitExponent"]. -
Set reportItem["
unitSystem"] to globalState["unitSystem"]. -
Set reportItem["
unitFactorLengthExponent"] to globalState["unitFactorLengthExponent"]. -
Set reportItem["
unitFactorMassExponent"] to globalState["unitFactorMassExponent"]. -
Set reportItem["
unitFactorTimeExponent"] to globalState["unitFactorTimeExponent"]. -
Set reportItem["
unitFactorTemperatureExponent"] to globalState["unitFactorTemperatureExponent"]. -
Set reportItem["
unitFactorCurrentExponent"] to globalState["unitFactorCurrentExponent"]. -
Set reportItem["
unitFactorLuminousIntensityExponent"] to globalState["unitFactorLuminousIntensityExponent"]. -
Set reportItem["
logicalMinimum"] to globalState["logicalMinimum"]. -
Set reportItem["
logicalMaximum"] to globalState["logicalMaximum"]. -
Set reportItem["
physicalMinimum"] to globalState["physicalMinimum"]. -
Set reportItem["
physicalMaximum"] to globalState["physicalMaximum"]. -
Set reportItem["
isRange"] totrueif both "usageMinimum" and "usageMaximum" are in localState and localState["usageMinimum"] is less than or equal to localState["usageMaximum"], or false otherwise. -
If reportItem["
isRange"] istrue, set reportItem["usageMinimum"] to localState["usageMinimum"]. -
If reportItem["
isRange"] istrue, set reportItem["usageMaximum"] to localState["usageMaximum"]. -
If
"stringMinimum"is in localState and"stringMaximum"is in localState: -
For each stringIndex of stringIndices:
-
Return reportItem.
-
Let device be the
HIDDevicein hid.[[devices]]representing the interface. -
Let hid be the result of calling device’s relevant global object’s
Navigatorobject’shidgetter if device’s relevant global object is aWindowobject, otherwise be the result of calling device’s relevant global object’sWorkerNavigatorobject’shidgetter. -
Remove device from hid.
[[devices]]. -
If the user has allowed the site to access device as the result of a previous call to
requestDevice(), then queue a global task on the relevant global object of hid using the HID device task source to fire an event nameddisconnectat hid usingHIDConnectionEventwith itsdeviceattribute initialized to device.
6.1. getDevices() method
getDevices() method steps are:
-
Let promise be a new promise.
-
Let document be
null. -
If this’s relevant global object is a
DedicatedWorkerGlobalScopeorwindowobject, set document to this’s relevant global object’s associated Document. -
If this’s relevant global object is a
ServiceWorkerGlobalScopeobject and the associated service worker client is window client, set document to the associated Document of the associated service worker client’s global object. -
If document is
nullor document is not allowed to use the policy-controlled feature named "hid", reject promise with a "SecurityError"DOMExceptionand return promise. -
Run the following steps in parallel:
-
For each device of this.
[[devices]]:-
If the user has allowed the site to access device as the result of a previous call to
requestDevice()and device.[[state]]is not"forgotten", then append device to devices.
-
-
Queue a global task on the relevant global object of this using the HID device task source to resolve promise with devices.
-
Return promise.
6.2. requestDevice() method
requestDevice() permission dialog presents a list of
connected devices and asks the user to select one.
If a device has multiple HID interfaces, the dialog may present only a single item that represents all the interfaces. When the user selects an item that represents multiple HID interfaces, the user agent must grant access to all the HID interfaces exposed by the device.
requestDevice() method steps are:
-
Let promise be a new promise.
-
If this’s relevant global object’s associated Document is not allowed to use the policy-controlled feature named "hid", reject promise with a "
SecurityError"DOMExceptionand return promise. -
If the relevant global object of this is not
window, reject promise with a "NotSupportedError"DOMExceptionand return promise. -
If the relevant global object of this does not have transient activation, reject promise with a "
SecurityError"DOMExceptionand return promise. -
If options["
filters"] is present, then run the following steps for each filter of options["filters"]:-
If filter is not a valid filter, then reject promise with a
TypeErrorand return promise.
-
-
If options["
exclusionFilters"] is present, then run the following steps:-
If options["
exclusionFilters"] is empty, then reject promise with aTypeErrorand return promise. -
For each exclusionFilter of options["
exclusionFilters"]:-
If exclusionFilter is not a valid filter, then reject promise with a
TypeErrorand return promise.
-
-
-
Run the following steps in parallel:
-
Initialize availableDevices to be an empty list.
-
For each device of this.
[[devices]]:-
If device matches any filter in options["
filters"] and device does not match any filter in options["exclusionFilters"], then append device to availableDevices.
-
-
Prompt the user to grant the site access to a HID device by presenting them with the list of devices in availableDevices.
Note: The user agent may modify the device list before presenting it to the user. For example, devices with multiple HID interfaces should be represented by a single item such that selecting the item grants access to all interfaces exposed by the device. Whether a device is implemented as a single interface or a collection of interfaces is an implementation detail that may not be known by the user and should not be considered relevant for device access permissions.
-
If the user does not choose a device, queue a global task on the relevant global object of this using the HID device task source to resolve promise with devices and abort these steps.
-
For each device of this.
[[devices]]:-
If device represents a HID interface exposed by the chosen device, then set device.
[[state]]to"closed"and append device to devices.
-
-
Queue a global task on the relevant global object of device using the HID device task source to resolve promise with devices.
-
-
Return promise.
6.2.1. HIDDeviceRequestOptions dictionary
dictionary HIDDeviceRequestOptions {required sequence <HIDDeviceFilter >filters ;sequence <HIDDeviceFilter >exclusionFilters ; };
filtersmember, of type sequence<HIDDeviceFilter>- Filters for HID devices.
exclusionFiltersmember, of type sequence<HIDDeviceFilter>- Exclusion filters for HID devices.
6.2.2. HIDDeviceFilter dictionary
dictionary HIDDeviceFilter {unsigned short vendorId ;unsigned short productId ;unsigned short usagePage ;unsigned short usage ; };
vendorIdmember, of type unsigned short- The vendor ID to match.
productIdmember, of type unsigned short-
The product ID to match. If the
vendorIdmember is not present,productIdis ignored. usagePagemember, of type unsigned short- The top-level collection usage page to match.
usagemember, of type unsigned short-
The top-level collection usage ID to match. If the
usagePagemember is not present,usageis ignored.
requestDevice(). When no filters are provided, all
connected devices matching no exclusion filters are included.
When one or more filters are provided, a device is included if
any filter matches and no exclusion filters matches.
All HIDDeviceFilter members are optional. Each member
specifies a rule. To match a HIDDeviceFilter, every rule must
be satisfied. An empty HIDDeviceFilter matches any device.
Device ID rules are used to select devices by their vendor ID
and product ID. This is useful when an application is only
designed to work with a specific device, or otherwise relies on
vendor-specific functionality. Including a
vendorId rule causes the filter to only match
devices with the specified vendor ID. If a
productId rule is also included, the filter
will only match devices with the specified vendor ID and
product ID. productId is ignored if it is
specified without a vendorId rule.
Usage rules are used to select devices by the HID usages
assigned to the top-level collections of the device. This is
useful when an application is designed to work with a standard
class of device. To match a device, at least one of the device’s
collections must match every usage rule. Including a
usagePage rule causes the filter to only
match if the device contains a collection with the specified
usage page. If a usage rule is also
included, the filter will only match devices with the specified
usage page and usage ID. usage is
ignored if it is specified without a
usagePage rule.
Device ID rules and usage rules may be combined. For instance, an application may want to match devices from a particular vendor, but only devices that implement a specific device class.
The exclusionFilters option allows an
application to exclude some devices presented to the user from
the chooser dialog created by requestDevice() that are
known to not work as expected, even though they implement a
specific device class for instance.
HIDDeviceFilter filter is a valid
filter if these steps return true:
HIDDevice device matches any filter in a sequence
filters of HIDDeviceFilter if
these steps return true:
-
If filters is empty, then return
true. -
For each filter of filters:
-
If filter matches the device IDs of device and filter matches any collection of device, then return
true.
-
-
Return
false.
HIDDeviceFilter filter matches the
device IDs of device if these steps return
true:
HIDDeviceFilter filter matches any
collection of device if these steps return
true:
-
For each collection of device.
collections:-
If filter matches the collection collection then return
true.
-
-
Return
false.
HIDDeviceFilter filter matches the
collection collection if these steps
return true:
7. HIDDevice interface
[Exposed =(DedicatedWorker ,ServiceWorker ,Window ),SecureContext ]interface HIDDevice :EventTarget {attribute EventHandler oninputreport ;readonly attribute boolean opened ;readonly attribute unsigned short vendorId ;readonly attribute unsigned short productId ;readonly attribute DOMString productName ;readonly attribute FrozenArray <HIDCollectionInfo >collections ;Promise <undefined >open ();Promise <undefined >close ();Promise <undefined >forget ();Promise <undefined >sendReport ([EnforceRange ]octet ,reportId BufferSource );data Promise <undefined >sendFeatureReport ( [EnforceRange ]octet ,reportId BufferSource );data Promise <DataView >receiveFeatureReport ([EnforceRange ]octet ); };reportId
Methods on this interface typically complete asynchronously, queuing work on the HID device task source.
Instances of HIDDevice are created with the internal slots
described in the following table:
| Internal slot | Initial value | Description (non-normative) |
|---|---|---|
[[state]]
| "closed"
| Tracks the active state of the HIDDevice
|
[[vendorId]]
| 0 | The vendor ID of the device |
[[productId]]
| 0 | The product ID of the device |
[[productName]]
| ""
| The product name of the device |
[[collections]]
| An empty sequence of HIDCollectionInfo
| The top-level collections created by parsing the report descriptor |
[[pendingSendReportPromises]]
| An empty list | Pending sendReport() Promises
|
[[pendingSendFeatureReportPromises]]
| An empty list | Pending sendFeatureReport() Promises
|
[[pendingReceiveFeatureReportPromises]]
| An empty list | Pending receiveFeatureReport() Promises
|
oninputreportattribute, of type EventHandler-
oninputreportis an event handler IDL attribute for theinputreportevent type. openedattribute, of type boolean, readonly-
A flag indicating that the
HIDDeviceis ready to transfer data. vendorIdattribute, of type unsigned short, readonly-
A vendor ID is an
unsigned shortvalue assigned by a vendor ID source that identifies the manufacturer of a device.The
vendorIdattribute is the vendor ID of the device. If the device has no vendor ID or the vendor ID cannot be accessed, the attribute MUST be 0.ThevendorIdgetter steps are:-
Return this.
[[vendorId]].
-
productIdattribute, of type unsigned short, readonly-
A product ID is an
unsigned shortvalue assigned by the manufacturer of the device that identifies the product.The
productIdattribute is the product ID of the device. If the device has no product ID or the product ID cannot be accessed, the attribute MUST be 0.TheproductIdgetter steps are:-
Return this.
[[productId]].
-
productNameattribute, of type DOMString, readonly-
A string identifying the product.
For USB HID devices, the product name SHOULD contain the value of the string descriptor at the index
iProductspecified in the Device Descriptor. For Bluetooth HID devices, the product name SHOULD contain the value of the Device Name characteristic. If the device has no product name or the product name is empty or cannot be accessed, the attribute MUST return an empty string.The
productNamestring SHOULD NOT contain any identifiers like a serial number or Bluetooth device address that could be used to uniquely identify the device.TheproductNamegetter steps are:-
Return this.
[[productName]].
-
collectionsattribute, of type FrozenArray<HIDCollectionInfo>, readonly-
A sequence of
HIDCollectionInforepresenting the top-level collections in the report descriptor.Thecollectionsgetter steps are:-
Return this.
[[collections]].
Note: Top-level collections group items that serve similar purposes within the device. Applications look at the HID usage applied to top-level collections to identify devices. An application may not recognize a device if it does not follow the conventions for a particular type of device. Refer to [USBIF-HID-USAGE] for more information about the conventional behavior for standard HID devices.
-
-
If the input report is a blocked report, abort these steps.
-
Let reportId be the report ID for this report, or 0 if the HID interface does not use report IDs.
-
Let data be a
DataViewcreated over the byte sequence representing the input report. If the HID interface uses report IDs, the byte sequence MUST NOT include the report ID byte. -
Queue a global task on the relevant global object of this using the HID device task source to fire an event named
inputreportat device usingHIDInputReportEventwith itsdeviceattribute initialized to device, itsreportIdattribute initialized to reportId, and itsdataattribute initialized to data.
7.1. open() method
open() method steps are:
-
Let promise be a new promise.
-
If this.
[[state]]is not"closed", then reject promise with a "InvalidStateError"DOMExceptionand return promise. -
Perform the following steps in parallel:
-
Invoke the operating system to open the HID device.
-
If this fails for any reason, queue a global task on the relevant global object of this using the HID device task source to reject promise with a "
NetworkError"DOMExceptionand abort these steps. -
Queue a global task on the relevant global object of this using the HID device task source to resolve promise with
undefined.
-
-
Return promise.
7.2. close() method
close() method steps are:
-
Let promise be a new promise.
-
If this.
[[state]]is one of"forgotten","forgetting","opening", or"closing", then reject promise with a "InvalidStateError"DOMExceptionand return promise. -
For each pendingPromise of this.
[[pendingSendReportPromises]]:-
Reject pendingPromise with an "
AbortError"DOMException.
-
-
For each pendingPromise of this.
[[pendingSendFeatureReportPromises]]:-
Reject pendingPromise with an "
AbortError"DOMException.
-
-
For each pendingPromise of this.
[[pendingReceiveFeatureReportPromises]]:-
Reject pendingPromise with an "
AbortError"DOMException.
-
-
Perform the following steps in parallel:
-
Invoke the operating system to close the HID device and release any associated resources.
-
Queue a global task on the relevant global object of this using the HID device task source to resolve promise with
undefined.
-
-
Return promise.
7.3. forget() method
Note: The user agent may decide to combine permissions across APIs, for instance tracking WebHID + WebUSB device access under a unified low-level device access permission. This is why this method may also revoke additional (unspecified yet) permissions in the future.
forget() method steps are:
-
Let promise be a new promise.
-
If this’s relevant global object’s associated Document is not fully active, then:
-
Reject promise with an "
InvalidStateError"DOMException. -
Return promise.
-
-
Perform the following steps in parallel:
-
For each device of this.
[[devices]]:-
If device does not share device access permissions with the HIDDevice, then continue to the next device.
-
Set device.
[[state]]to"forgetting". -
For each pendingPromise of device.
[[pendingSendReportPromises]]:-
Reject pendingPromise with an "
AbortError"DOMException.
-
-
Empty device.
[[pendingSendReportPromises]]. -
For each pendingPromise of device.
[[pendingSendFeatureReportPromises]]:-
Reject pendingPromise with an "
AbortError"DOMException.
-
-
For each pendingPromise of device.
[[pendingReceiveFeatureReportPromises]]:-
Reject pendingPromise with an "
AbortError"DOMException.
-
-
Invoke the user agent to revoke access to all the HID interfaces exposed by device.
-
Set device.
[[state]]to"forgotten".
-
-
Queue a global task on the relevant global object of this using the HID device task source to resolve promise with
undefined.
-
-
Return promise.
7.4. sendReport() method
sendReport() method is used to send an output
report with the specified reportId and data.
If the HID interface does not use report IDs, pass 0
instead of the report ID.
For HID interfaces that use report IDs, the report ID value 0 is reserved and should not be used.
The sendReport() method steps are:
-
Let promise be a new promise.
-
If this.
[[state]]is not"opened", then reject promise with a "InvalidStateError"DOMExceptionand return promise. -
If reportId is 0 and the HID interface represented by this uses report IDs, or if reportId is not 0 and the interface does not use report IDs, then reject promise with a "
TypeError"DOMExceptionand return promise. -
Append promise to this.
[[pendingSendReportPromises]]. -
Perform the following steps in parallel:
-
If this report is a blocked report, queue a global task on the relevant global object of this using the HID device task source to reject promise with a "
NotAllowedError"DOMExceptionand abort these steps. -
Invoke the operating system to send an output report with the specified reportId and data.
-
If this fails for any reason, queue a global task on the relevant global object of this using the HID device task source to reject promise with a "
NetworkError"DOMExceptionand abort these steps. -
Queue a global task on the relevant global object of this using the HID device task source to resolve promise with
undefined.
-
-
Return promise.
7.5. sendFeatureReport() method
sendFeatureReport() method is used to send a
feature report with the specified reportId and
data. If the HID interface does not use report IDs, pass 0 instead of the report ID.
For HID interfaces that use report IDs, the report ID value 0 is reserved and should not be used.
The sendFeatureReport() method steps are:
-
Let promise be a new promise.
-
If this.
[[state]]is not"opened", then reject promise with a "InvalidStateError"DOMExceptionand return promise. -
If reportId is 0 and the HID interface represented by this uses report IDs, or if reportId is not 0 and the interface does not use report IDs, then reject promise with a "
TypeError"DOMExceptionand return promise. -
Append promise to this.
[[pendingSendFeatureReportPromises]]. -
Perform the following steps in parallel:
-
If this report is a blocked report, queue a global task on the relevant global object of this using the HID device task source to reject promise with a "
NotAllowedError"DOMExceptionand abort these steps. -
Invoke the operating system to send a feature report with the specified reportId and data.
-
If this fails for any reason, queue a global task on the relevant global object of this using the HID device task source to reject promise with a "
NetworkError"DOMExceptionand abort these steps. -
Queue a global task on the relevant global object of this using the HID device task source to resolve promise with
undefined.
-
-
Return promise.
7.6. receiveFeatureReport() method
receiveFeatureReport() method is used to request
a feature report with the specified reportId. If the HID interface does not use report IDs, pass 0 instead of the
report ID.
For HID interfaces that use report IDs, the report ID value 0 is reserved and should not be used.
The receiveFeatureReport() method steps are:
-
Let promise be a new promise.
-
If this.
[[state]]is not"opened", then reject promise with a "InvalidStateError"DOMExceptionand return promise. -
If reportId is 0 and the HID interface represented by this uses report IDs, or if reportId is not 0 and the interface does not use report IDs, then reject promise with a "
TypeError"DOMExceptionand return promise. -
Append promise to this.
[[pendingReceiveFeatureReportPromises]]. -
Perform the following steps in parallel:
-
If this report is a blocked report, queue a global task on the relevant global object of this using the HID device task source to reject promise with a "
NotAllowedError"DOMExceptionand abort these steps. -
Let data be a
DataViewcreated over the byte sequence representing the result of invoking the operating system to read a feature report with the specified reportId.Note: data will contain whatever report data was received from the operating system. If the device uses report IDs the first byte may be the report ID. This byte is included because the device may respond with other data in place of the report ID.
-
If reading the feature report fails for any reason, queue a global task on the relevant global object of this using the HID device task source to reject promise with a "
NetworkError"DOMExceptionand abort these steps. -
Queue a global task on the relevant global object of this using the HID device task source to resolve promise with data.
-
-
Return promise.
8. HIDConnectionEvent interface
[Exposed =(DedicatedWorker ,ServiceWorker ,Window ),SecureContext ]interface HIDConnectionEvent :Event {(constructor DOMString ,type HIDConnectionEventInit ); [eventInitDict SameObject ]readonly attribute HIDDevice device ; };
deviceattribute, of type HIDDevice, readonly-
The
HIDDeviceinstance that represents the connected or disconnected device.
8.1. HIDConnectionEventInit dictionary
dictionary HIDConnectionEventInit :EventInit {required HIDDevice device ; };
devicemember, of type HIDDevice- The device associated with the event.
9. HIDInputReportEvent interface
[Exposed =(DedicatedWorker ,ServiceWorker ,Window ),SecureContext ]interface HIDInputReportEvent :Event {(constructor DOMString ,type HIDInputReportEventInit ); [eventInitDict SameObject ]readonly attribute HIDDevice device ;readonly attribute octet reportId ;readonly attribute DataView data ; };
deviceattribute, of type HIDDevice, readonly-
The
HIDDeviceinstance that represents the HID interface that sent the input report. reportIdattribute, of type octet, readonly- The one-byte identification prefix for this report, or 0 if the HID interface does not use report IDs.
dataattribute, of type DataView, readonly-
A
DataViewcontaining the data from the input report, excluding thereportIdbyte if the HID interface uses report IDs.
9.1. HIDInputReportEventInit dictionary
dictionary HIDInputReportEventInit :EventInit {required HIDDevice device ;required octet reportId ;required DataView data ; };
devicemember, of type HIDDevice- The device associated with the event.
reportIdmember, of type octet- The report ID for the input report.
datamember, of type DataView- The data for the input report.
10. HIDCollectionInfo dictionary
dictionary HIDCollectionInfo {unsigned short usagePage ;unsigned short usage ;octet type ;sequence <HIDCollectionInfo >children ;sequence <HIDReportInfo >inputReports ;sequence <HIDReportInfo >outputReports ;sequence <HIDReportInfo >featureReports ; };
When a device is first connected to the system, the system must retrieve the report descriptor for each HID interface exposed by the device. The Report descriptor is a byte sequence that can be expanded into a hierarchical data structure describing the layout of each report supported by the device. The elements of this data structure are called items. Groups of items that share a relationship are called collections.
A HIDCollectionInfo object represents a single collection within
the report descriptor. When a collection contains nested
collections, the nested collections are included as elements of
children.
The inputReports,
outputReports, and
featureReports attributes contain sequences
of HIDReportInfo that give information about each report described
within this collection. For a top-level collection, the
HIDReportInfo represents a flattened view of all items that compose
the report. The flattened view interleaves items contained in nested
collections with items contained within the top-level collection.
For a nested collection, the HIDReportInfo includes only the items
contained within the collection and its nested collections.
usagePagemember, of type unsigned short-
The usage page component of the HID usage associated with
this collection.
HID usages are constants that can be interpreted by an application to identify the purpose and meaning of an item or collection. A HID usage is an
unsigned longvalue composed of anunsigned shortusage page in the high order bits and anunsigned shortusage ID in the low order bits. Standard HID usage values are described in [USBIF-HID-CLASS] and other documents published by the USB Implementers Forum.The HID usages of the top-level collections exposed by the device is used to identify the general device type.
usagemember, of type unsigned short-
The usage ID component of the HID usage associated with
this collection.
A usage ID is used to select an individual HID usage on a usage page. By convention, usage IDs 0x01 through 0x1F are reserved for top-level collections.
typemember, of type octet-
A value representing the collection type.
Collection type Value Physical 0x00 Application 0x01 Logical 0x02 Report 0x03 Named Array 0x04 Usage Switch 0x05 Usage Modified 0x06 Reserved for future use 0x07 to 0x7F Vendor-defined 0x80 to 0xFF Each collection type describes a different type of relationship among the grouped items. Refer to [USBIF-HID-CLASS] section 6.2.2.6 for more information about collection types.
childrenmember, of type sequence<HIDCollectionInfo>-
A sequence of
HIDCollectionInforepresenting the collections nested within this collection. inputReportsmember, of type sequence<HIDReportInfo>-
A sequence of
HIDReportInforepresenting the input reports described within this collection. outputReportsmember, of type sequence<HIDReportInfo>-
A sequence of
HIDReportInforepresenting the output reports described within this collection. featureReportsmember, of type sequence<HIDReportInfo>-
A sequence of
HIDReportInforepresenting the feature reports described within this collection.
11. HIDReportInfo dictionary
dictionary HIDReportInfo {octet reportId ;sequence <HIDReportItem >items ; };
A HIDReportInfo represents one input, output, or feature report
within the report descriptor.
reportIdmember, of type octet-
A HID interface uses report
IDs if it prepends a one-byte identification prefix to each
report transfer. The
reportIdmember is the prefix for this report if the interface uses report IDs, or 0 otherwise. itemsmember, of type sequence<HIDReportItem>-
A sequence of
HIDReportItemrepresenting the fields of this report.
12. HIDReportItem dictionary
dictionary HIDReportItem {boolean isAbsolute ;boolean isArray ;boolean isBufferedBytes ;boolean isConstant ;boolean isLinear ;boolean isRange ;boolean isVolatile ;boolean hasNull ;boolean hasPreferredState ;boolean wrap ;sequence <unsigned long >usages ;unsigned long usageMinimum ;unsigned long usageMaximum ;unsigned short reportSize ;unsigned short reportCount ;byte unitExponent ;HIDUnitSystem unitSystem ;byte unitFactorLengthExponent ;byte unitFactorMassExponent ;byte unitFactorTimeExponent ;byte unitFactorTemperatureExponent ;byte unitFactorCurrentExponent ;byte unitFactorLuminousIntensityExponent ;long logicalMinimum ;long logicalMaximum ;long physicalMinimum ;long physicalMaximum ;sequence <DOMString >strings ; };
isAbsolutemember, of type boolean-
trueif the data is absolute (based on a fixed origin), orfalseif the data is relative (indicating the change in value from the last report). isArraymember, of type boolean-
trueif the item creates array data fields in reports where each data field contains an index corresponding to a pressed button or key, orfalseif the item creates variable data fields in reports where each data field contains a value. isBufferedBytesmember, of type boolean-
trueif the item emits a fixed-sized stream of bytes, orfalseif the item is a numeric quantity. isConstantmember, of type boolean-
trueif the item is a static read-only field and cannot be modified, orfalseif the item defines report fields that contain modifiable device data. isLinearmember, of type boolean-
trueif the item represents a linear relationship between what is measured and what is reported, orfalseif the data has been processed in some way. isRangemember, of type boolean-
trueif the item assigns usages from a HID usage range defined byusageMinimumandusageMaximum, orfalseif the item has a sequence of HID usage values inusages. isVolatilemember, of type boolean-
trueif the item value can change without host interaction, orfalseif the item value should only be changed by the host.Only used for Feature and Output items.
hasNullmember, of type boolean-
trueif the item has a null state in which it is not sending meaningful data, orfalseif it does not have a null state. When in a null state, the item will report a value outside of the specifiedlogicalMinimumandlogicalMaximum. hasPreferredStatemember, of type boolean-
trueif the item has a preferred state to which it will return when the user is not physically interacting with the control, orfalseif the item does not have a preferred state. wrapmember, of type boolean-
trueif the item value rolls over when reaching either the extreme high or low value, orfalseif the item value does not roll over. usagesmember, of typesequence<unsigned long>-
If
isRangeistrueor this item has no associated HID usage values, thenusagesMUST be undefined.If
isRangeisfalsethenusagesis a sequence of HID usage values associated with this item. usageMinimummember, of type unsigned long-
If
isRangeistruethenusageMinimumcontains the minimum HID usage value in the usage range associated with this item.If
isRangeisfalsethenusageMinimumMUST be undefined. usageMaximummember, of type unsigned long-
If
isRangeistruethenusageMaximumcontains the maximum HID usage value in the usage range associated with this item.If
isRangeisfalsethenusageMaximumMUST be undefined. reportSizemember, of type unsigned short-
The size of each report data field in bits. The
reportSizeMUST be greater than 0. reportCountmember, of type unsigned short-
The number of fields included in the report for this item. The
reportCountMUST be greater than 0. unitExponentmember, of type byte- The value of the unit exponent, or 0 if the item has no unit definition.
unitSystemmember, of type HIDUnitSystem-
A
HIDUnitSystemenum value specifying the unit system for the unit definition, or"none"if the item has no unit definition. unitFactorLengthExponentmember, of type byte- The value of the exponent for the length unit (centimeters, radians, inch, or degrees) in the unit definition, or 0 if the item has no unit definition.
unitFactorMassExponentmember, of type byte- The value of the exponent for the mass unit (gram or slug) in the unit definition, or 0 if the item has no unit definition.
unitFactorTimeExponentmember, of type byte- The value of the exponent for the time unit (seconds) in the unit definition, or 0 if the item has no unit definition.
unitFactorTemperatureExponentmember, of type byte- The value of the exponent for the temperature unit (Kelvin or degrees Fahrenheit) in the unit definition, or 0 if the item has no unit definition.
unitFactorCurrentExponentmember, of type byte- The value of the exponent for the electrical current unit (Ampere) in the unit definition, or 0 if the item has no unit definition.
unitFactorLuminousIntensityExponentmember, of type byte- The value of the exponent for the luminous intensity unit (Candela) in the unit definition, or 0 if the item has no unit definition.
logicalMinimummember, of type long- The minimum extent of this item in logical units. This is the minimum value that a variable or array item will report.
logicalMaximummember, of type long- The maximum extent of this item in logical units. This is the maximum value that a variable or array item wll report.
physicalMinimummember, of type long-
The minimum value for the physical extent of this item. This
represents the
logicalMinimumwith units applied to it. physicalMaximummember, of type long-
The maximum value for the physical extent of this item. This
represents the
logicalMaximumwith units applied to it. stringsmember, of type sequence<DOMString>- The strings associated with this item, or an empty sequence if no strings are associated with this item.
13. HIDUnitSystem enum
[USBIF-HID-CLASS] section 6.2.2.7 defines four standard unit systems: SI Linear, SI Rotation, English Linear, and English Rotation. Each item uses one of these four unit systems, a vendor-defined unit system, or no unit system.
enum HIDUnitSystem {"none" ,"si-linear" ,"si-rotation" ,"english-linear" ,"english-rotation" ,"vendor-defined" ,"reserved" };
"none"- No unit system. Indicates that the item does not have a unit definition.
"si-linear"- The SI Linear unit system uses centimeter, gram, second, Kelvin, Ampere, Candela.
"si-rotation"- The SI Rotation unit system uses radian, gram, second, Kelvin, Ampere, Candela.
"english-linear"- The English Linear unit system uses inch, slug, second, degree Fahrenheit, Ampere, Candela.
"english-rotation"- The English Rotation unit system uses degree, slug, second, degree Fahrenheit, Ampere, Candela.
"vendor-defined"- A vendor-defined unit system.
"reserved"- Indicates that the device used a reserved value for its unit system.
14. Usage Examples
document. addEventListener( 'DOMContentLoaded' , async () => { let devices= await navigator. hid. getDevices(); devices. forEach( device=> { console. log( `HID: ${ device. productName} ` ); }); });
navigator. hid. addEventListener( 'connect' , ({ device}) => { console. log( `HID connected: ${ device. productName} ` ); }); navigator. hid. addEventListener( 'disconnect' , ({ device}) => { console. log( `HID disconnected: ${ device. productName} ` ); });
getDevices() and will not
generate connection events until permission has been granted to
access the device. The page may request permission using
requestDevice(). In this example, the page requests access to
a device with vendor ID 0xABCD. The device must also have a
collection with usage page Consumer (0x000C) and usage ID
Consumer Control (0x0001). The device with product ID 0x1234
has been reported as malfunctioning.
let requestButton= document. getElementById( "request-hid-device" ); requestButton. addEventListener( "click" , async () => { let device; try { const devices= await navigator. hid. requestDevice({ filters: [ { vendorId: 0xabcd , usagePage: 0x000c , usage: 0x0001 , }, ], exclusionFilters: [ { vendorId: 0xabcd , productId: 0x1234 , } ], }); device= devices[ 0 ]; } catch ( error) { console. log( "An error occurred." ); } if ( ! device) { console. log( "No device was selected." ); } else { console. log( `HID: ${ device. productName} ` ); } });
device. oninputreport= ({ device, reportId, data}) => { console. log( `Input report ${ reportId} from ${ device. productName} :` , new Uint8Array( data. buffer)); }; if ( ! device. opened) await device. open();
try { if ( ! device. opened) await device. open(); const outputReportData= new Uint8Array([ 0x12 , 0x34 , 0x56 , 0x78 ]); await device. sendReport( 0x05 , outputReportData); console. log( "Output report sent" ); } catch ( error) { console. log( "An error occurred." ) }
try { if ( ! device. opened) await device. open(); const oldFeatureData= await device. receiveFeatureReport( 0x80 ); console. log( "Feature report received:" , new Uint8Array( oldFeatureData. buffer)); const newFeatureData= new Uint8Array([ 0x12 , 0x34 , 0x56 , 0x78 ]); await device. sendFeatureReport( 0x80 , newFeatureData); console. log( "Feature report sent" ); } catch ( error) { console. log( "An error occurred." ) }
15. The HID Blocklist
This specification relies on a blocklist file in the https://github.com/whatwg/hid repository to restrict the set of HID devices a website can access.
The HID blocklist is the result of parsing the blocklist at https://github.com/whatwg/hid/blob/main/blocklist.txt
true:
-
For each rule of the HID blocklist:
-
If the report is blocked by a blocklist rule rule then return
true.
-
-
Return
false.
true:
-
Let reportId be the report ID of the report, or 0 if the HID interface does not use report IDs.
-
Let reportType be
"input"if the report is an input report,"output"if the report is an output report, or"feature"if the report is a feature report. -
Let collection be the top-level collection that contains the report.
-
If rule contains
"vendor"and rule["vendor"] is not device.vendorId, returnfalse. -
If rule contains
"product"and rule["product"] is not device.productId, returnfalse. -
If rule contains
"reportId"and rule["reportId"] is not reportId, returnfalse. -
If rule contains
"reportType"and rule["reportType"] is not reportType, returnfalse. -
If rule contains
"usagePage"and rule["usagePage"] is not collection["usagePage"], returnfalse. -
If rule contains
"usage"and rule.["usage"] is not collection["usage"], returnfalse. -
Return
true.
16. Integrations
16.1. Permissions Policy
This specification defines a feature that controls whether the
hid attribute is exposed on the Navigator object.
The feature name for this feature is "hid".
The default allowlist for this feature
is 'self'.
Acknowledgments
Thanks to Feras Aldahlawi, François Beaufort, Jack Hsieh, Jeffrey Yasskin, Jonas Due Vesterheden, Kagami Sascha Rosylight, Marcos Cáceres, Peter Høy Andersen, Reilly Grant, Rob Pitkin, Ted Mielczarek, Thomas Steiner, Tim Steiner, and Vincent Scheib for being awesome.
This standard is written by Matt Reynolds (Google, mattreynolds@google.com).
Intellectual property rights
Copyright © WHATWG (Apple, Google, Mozilla, Microsoft). This work is licensed under a Creative Commons Attribution 4.0 International License. To the extent portions of it are incorporated into source code, such portions in the source code are licensed under the BSD 3-Clause License instead.