WebHID API

repository·main·Indexed 41 days ago

https://github.com/wicg/webhid

The WebHID API allows web applications to communicate directly with Human Interface Devices (HID), such as gamepads, custom controllers, or medical devices. It provides methods for requesting device permissions via navigator.hid.requestDevice(), managing connections, and exchanging data through input, output, and feature reports. Available in Chromium 89 and later, the API requires secure contexts (HTTPS) and explicit user consent to access hardware.

Tokens
2.7K
Snippets
4
Records
13
Agent score
50%

What's inside WebHID

  1. WebHID API Overview

    main
    The WebHID API provides a way for web applications to interact with Human Interface Devices (HID) directly. It brings the capabilities of the native HID API to the web platform, allowing developers to communicate with specialized hardware like gamepads, custom controllers, or medical devices that use the HID protocol.
  2. Security and privacy mitigations in WebHID

    main

    WebHID implements several security and privacy protections to prevent unauthorized data access and fingerprinting:

    • Secure Contexts Only: Access to the WebHID API is restricted to secure contexts (HTTPS).
    • Explicit User Consent: No device information is exposed to a script by default. A user must explicitly grant access to a specific device by selecting it from a device chooser list. Information about devices not selected by the user is not exposed.
    • High-Value Data Protection: To prevent credential theft, FIDO devices are excluded from the device chooser. While keyboards and pointer devices (mice, touchscreens) may appear in the chooser, reports containing keyboard or pointer information are blocked and not delivered to the script.
    • Incognito Mode: Device permissions granted during an incognito session must not persist once the session is closed.
  3. Privacy implications of WebHID device access

    main

    Developers should be aware of the following privacy considerations when using WebHID:

    • Fingerprinting: Information about connected peripherals can potentially be used to fingerprint a user. This is mitigated by the requirement for explicit user selection via a chooser.
    • Sensor Access: Many sensors are implemented as HID peripherals; WebHID allows access to these sensors if the user grants permission.
    • Local Environment Exposure: WebHID allows an origin to see a list of devices for which it has already been granted access and to send/receive data from them, which may reveal aspects of the user's local computing environment.
    • Cross-Origin Communication: Device access is not exclusive. If multiple origins are granted access to the same device, they can potentially use that device to communicate across origins.
  4. Access WebHID in Extension Service Workers

    main

    In browser extensions, the navigator.hid interface is exposed within the Service Worker context. This allows extension developers to manage HID devices from a service worker rather than relying solely on a background page. Note that navigator.hid is only exposed to Service Workers when running within a browser extension context.

    [Exposed=(Window,ServiceWorker), SecureContext]
    interface HID : EventTarget {
        attribute EventHandler onconnect;
        attribute EventHandler ondisconnect;
        Promise<sequence<HIDDevice>> getDevices();
        Promise<sequence<HIDDevice>> requestDevice(
            HIDDeviceRequestOptions options);
    };
  5. Understand HID terminology

    main

    To work with WebHID, it is helpful to understand these core concepts:

    • HID (Human Interface Device): A standard protocol for bi-directional communication between a host and a device (e.g., USB or Bluetooth).
    • HID Report: A binary data packet. Input reports go from device to host; Output reports go from host to device; Feature reports can go in either direction.
    • HID Report Descriptor: A hierarchical structure requested during enumeration that describes the binary format of the reports supported by the device.
    • HID Usage: A numeric value (organized into Usage Pages) that describes the purpose of a device or a specific field within a report (e.g., a button or a joystick axis).
  6. Request HID device permissions from a page

    main

    The requestDevice() method cannot be called directly from a Service Worker because it requires displaying a permission dialog to the user, and service workers cannot show UI elements.

    To request device permissions, you must call requestDevice() from a Window object (a web page). If you attempt to call requestDevice() from a Service Worker context, the method will throw a NotSupportedError.

  7. How to request and connect to HID devices

    main

    To interact with a HID device, you must first request permission from the user via navigator.hid.requestDevice(). This method displays a chooser dialog. Once the user selects a device, the returned HIDDevice must be explicitly opened using .open() before any data can be exchanged.

    You can also listen for global connection and disconnection events on navigator.hid to manage devices being plugged in or removed from the system.

    let deviceFilter = { vendorId: 0x1234, productId: 0xabcd };
    let requestParams = { filters: [deviceFilter] };
    
    // 1. Listen for connection events
    navigator.hid.addEventListener("connect", (e) => {
      console.log("Device connected: " + e.device.productName);
    });
    
    // 2. Request a device (requires user gesture)
    navigator.hid.requestDevice(requestParams).then((devices) => {
      if (devices.length == 0) return;
      const device = devices[0];
    
      // 3. Open the device
      device.open().then(() => {
        console.log("Opened device: " + device.productName);
        
        // 4. Listen for input reports
        device.addEventListener("inputreport", (e) => {
          console.log(e.device.productName + ": got input report " + e.reportId);
          console.log(new Uint8Array(e.data.buffer));
        });
    
        // 5. Send an output report
        const outputReportId = 0x01;
        const outputReport = new Uint8Array([42]);
        device.sendReport(outputReportId, outputReport).then(() => {
          console.log("Sent output report " + outputReportId);
        });
      });
    });
  8. Inspect HID device collections and report structures

    main

    The HIDDevice.collections attribute provides a hierarchical description of the device's report formats via HIDCollectionInfo. This allows applications to understand the device's capabilities (like which buttons or axes are available) without manual trial-and-error.

    Each collection contains:

    • usagePage and usage: Identifying the type of control.
    • inputReports, outputReports, and featureReports: Arrays of HIDReportInfo describing the available report formats.
    • children: Nested HIDCollectionInfo objects.
  9. Use the HID interface in Service Workers

    main

    Once permission has been granted via a page, you can interact with HID devices in the Service Worker using the navigator.hid interface. The interface provides the following methods and events:

    • getDevices(): Returns a Promise that resolves to a list of HIDDevice objects that the user has already granted permission for.
    • onconnect: An event handler triggered when a device is connected.
    • ondisconnect: An event handler triggered when a device is disconnected.
  10. Use the HID interface via navigator.hid

    main

    The navigator.hid object is the entry point for the WebHID API. It is used to manage device permissions and listen for connection changes.

    Key methods:

    • getDevices(): Returns a Promise resolving to an array of HIDDevice objects that the user has previously granted permission for.
    • requestDevice(options): Displays a browser UI to let the user select a device. Returns a Promise resolving to a sequence<HIDDevice>.

    Events:

    • connect: Fired when a device the page has permission for is connected.
    • disconnect: Fired when a device the page has permission for is disconnected.
  11. Communicate with an open HIDDevice

    main

    Once an HIDDevice is opened via .open(), you can perform bi-directional communication using reports:

    • Input Reports: Listen for data sent from the device using the inputreport event. The event provides the reportId and a DataView containing the data.
    • Output Reports: Send data to the device using .sendReport(reportId, data). The data must be a BufferSource.
    • Feature Reports: Use .sendFeatureReport(reportId, data) to send data or .receiveFeatureReport(reportId) to request data from the device. Both return Promises.
    // Example of sending an output report
    await device.sendReport(0x01, new Uint8Array([0x42]));
    
    // Example of listening for input reports
    device.addEventListener("inputreport", (event) => {
      const { reportId, data } = event;
      // data is a DataView
    });
    
    // Example of feature reports
    await device.sendFeatureReport(0x02, new Uint8Array([0x01]));
    const featureData = await device.receiveFeatureReport(0x02);
  12. Configure HID device filters

    main

    When calling requestDevice(), you can provide HIDDeviceRequestOptions to narrow down the devices shown to the user. This is done using filters (required) and exclusionFilters (optional).

    A HIDDeviceFilter can match devices based on:

    • vendorId: The USB vendor ID.
    • productId: The USB product ID.
    • usagePage: The HID usage page.
    • usage: The specific HID usage.
    // Example of a filter object
    {
      "filters": [
        {
          "vendorId": 0x1234,
          "productId": 0xabcd
        }
      ]
    }