# WebUSB API：从网页连接非标准 USB 设备

> navigator.usb.requestDevice() 如何与标准设备类之外的 USB 设备配对、瞬态激活与安全上下文要求、权限/选择器模型，以及可选的着陆页机制。

**一句话：** WebUSB 让网页可以请求访问操作系统标准设备类（键盘、鼠标等）之外的 USB
设备——这些设备原本需要原生驱动或原生应用——方式是向用户展示一个权限提示，与特定设备
配对，无需下载任何驱动。它是一份 WICG 规范，既不是 W3C 标准，也不在 W3C 标准化轨道
上。

## 规范状态

WebUSB 由 Web 平台孵化社区组（WICG）以社区组草案报告的形式发布；规范声明它"既不是
W3C 标准，也不在 W3C 标准化轨道上"。MDN 将该 API 标记为**实验性**且**非 Baseline**，
"因为它在部分最广泛使用的浏览器中不可用"。

## WebUSB 存在的原因

许多 USB 设备属于有操作系统驱动支持的标准化设备类，也可以通过 WebHID API 从网页访
问。而那些不属于标准类的设备，历来需要原生代码才能访问，这"使得这些设备无法被网页使
用"。WebUSB 让网页可以直接访问这些非标准化的设备服务。

## 安全上下文与瞬态激活

WebUSB 仅在安全上下文中可用。调用 `requestDevice()` 时，调用脚本必须处于**瞬态激
活**状态（即近期发生过用户手势）；若在没有用户手势的情况下调用，返回的 Promise 会以
`SecurityError` 被拒绝。

## 请求与复用设备

```js
navigator.usb
  .requestDevice({ filters: [{ vendorId: 0x2341 }] })
  .then((device) => {
    console.log(device.productName); // "Arduino Micro"
    console.log(device.manufacturerName); // "Arduino LLC"
  })
  .catch((error) => {
    console.error(error);
  });
```

`requestDevice()` 会对候选设备进行过滤（例如按 `vendorId`），根据规范，"UA 可能在
调用此函数时显示权限提示"。设备一旦获得授权后，`navigator.usb.getDevices()` 便可返
回此前已获授权的设备，而不会再次弹出提示：

```js
navigator.usb.getDevices().then((devices) => {
  devices.forEach((device) => {
    console.log(device.productName);
  });
});
```

UA "也可能在设备连接处于活动状态时显示一个指示器"。

## 权限模型

WebUSB 的安全模型依赖于 `requestDevice()` 的权限提示，并结合权限策略
（Permissions Policy）集成，而非采用设备端的允许列表（规范中说明，曾考虑过类似
CORS 的方案但未被采纳）。此外，规范定义的 **USB 屏蔽列表（Blocklist）** 会限制对特定
设备的访问，识别依据是 `vendorId`、`productId` 与 `bcdDevice`。另外，受保护的接口类
无论是否在屏蔽列表中，都会被单独限制访问 WebUSB。

同一时刻只能有一个执行上下文声明（claim）某个 USB 接口——若多个执行上下文尝试声明
同一接口，`claimInterface()` 会失败。

## 设备结构

`USBDevice` 提供已配对设备的元数据以及用于控制它的方法。设备被组织为
`USBConfiguration` → `USBInterface` → `USBAlternateInterface` → `USBEndpoint` 的层
级结构，每一层都提供该层级的信息。传输结果由 `USBInTransferResult`/
`USBOutTransferResult`（以及用于流式端点的等时传输变体）表示。

## 着陆页

设备可以通过 WebUSB 平台能力描述符（Platform Capability Descriptor）的
`iLandingPage` 字段声明一个着陆页——"设备制造商希望用户访问以控制其设备的着陆页"。
UA 的角色仅是建议性的：它"可以（MAY）在设备连接时建议用户导航到该 URL"。浏览器通过
一个规范定义的 `GET_URL` 设备请求获取该地址，该请求返回一个 URL 描述符（包含协议前
缀与 UTF-8 编码的 URL 字符串）。

## 在 Worker 中的可用性

MDN 指出该特性在 Web Worker 中可用（`WorkerNavigator.usb`），此外主线程上还有
`Navigator.usb`。

## 实践清单

- [ ] 在调用任何 WebUSB 方法之前先进行特性检测：`navigator.usb`。
- [ ] 通过 HTTPS 提供页面——WebUSB 仅在安全上下文中可用。
- [ ] 仅在具有瞬态激活（用户手势）的脚本中调用 `requestDevice()`；否则调用会以
      `SecurityError` 被拒绝。
- [ ] 使用 `filters`（例如 `vendorId`）将选择器范围限定到目标设备。
- [ ] 在后续访问中调用 `getDevices()` 以复用此前已获授权的设备，而无需再次弹出提
      示。
- [ ] 上线前检查当前浏览器兼容性——MDN 将 WebUSB 标记为实验性且非 Baseline。
- [ ] 如果你的设备声明了 `iLandingPage`，请记住浏览器的导航建议是可选的（MAY），并
      非保证行为。