Web 串行 API

持续更新文档

社区小组报告(草案)

最近发布的版本:
最新编辑草稿:
https://wicg.github.io/serial/
编辑:
在 GH 查看贡献者
反馈:
GitHub wicg/serial (拉取请求, 新 issue, 打开的问题)

摘要

串口 API为网站提供了一种通过脚本读取和写入串口设备的方式。这样的 API 将 web 与物理世界连接起来,使文档能够与如微控制器、3D 打印机及其他串口设备通信。相关还有一份说明文档

文档状态

本规范由 Web 平台孵化社区组发布。它不是 W3C 标准,也不在 W3C 标准轨道上。 请注意,根据 W3C 社区贡献者许可协议 (CLA) 有有限的选择退出选项,并且适用其他条件。 了解更多关于 W3C 社区与商务组的信息。

该规范仍在不断完善中。欢迎所有贡献

讨论本规范优先使用GitHub Issues

1. Navigator 接口的扩展

WebIDL[Exposed=Window, SecureContext]
partial interface Navigator {
  [SameObject] readonly attribute Serial serial;
};

1.1 serial 属性

获取时,serial 属性总是返回同一个 Serial 对象实例。

2. WorkerNavigator 接口的扩展

WebIDL[Exposed=DedicatedWorker, SecureContext]
partial interface WorkerNavigator {
  [SameObject] readonly attribute Serial serial;
};

2.1 serial 属性

获取时,serial 属性总是 返回同一个 Serial 对象实例。

3. Serial 接口

WebIDL[Exposed=(DedicatedWorker, Window), SecureContext]
interface Serial : EventTarget {
  attribute EventHandler onconnect;
  attribute EventHandler ondisconnect;
  Promise<sequence<SerialPort>> getPorts();
  [Exposed=Window] Promise<SerialPort> requestPort(optional SerialPortRequestOptions options = {});
};

3.1 requestPort() 方法

requestPort() 方法的执行步骤如下:

  1. promise新 Promise
  2. 如果 this相关全局对象关联文档 不被允许使用名为 "serial" 的策略控制功能, 则用 "SecurityError" DOMException 拒绝 promise 并返回。
  3. 如果 相关全局对象 没有 瞬时激活, 用 "SecurityError" DOMException 拒绝 promise,并返回。
  4. 如果 options["filters"] 存在, 对于 options["filters"] 中的每个 filter 执行以下步骤:
    1. 如果 filter["bluetoothServiceClassId"] 存在:
      1. 如果 filter["usbVendorId"] 存在, 用 TypeError 拒绝 promise 并返回。
      2. 如果 filter["usbProductId"] 存在, 用 TypeError 拒绝 promise 并返回。
    2. 如果 filter["usbVendorId"] 不存在,用 TypeError 拒绝 promise并返回。
      注意
      此检查实现了组合规则:SerialPortFilter 不能为空,且如果 usbProductId 指定,则 usbVendorId 也必须被指定。
  5. 并行执行下列步骤:
    1. allPorts 为一个空 列表
    2. 对系统注册的每个蓝牙设备:
      1. 对于设备支持的每个 BluetoothServiceUUID uuid
        1. 如果 uuid 不是 受限 蓝牙服务类 UUID
    3. 对每一个可用的 非蓝牙串口:
      1. port 为表示该端口的 SerialPort
      2. 追加 portallPorts
    4. 提示用户授权站点访问某个串口,展示 allPorts匹配任意筛选条件 (若 options["filters"] 存在)或全部内容。
    5. 如果用户未选择端口,在相关全局对象 上、使用 serial port task source 拒绝 promise,错误为 "NotFoundError" DOMException, 并中止后续步骤。
    6. port 为用户选定的端口, 其为 SerialPort 的一个实例。
    7. 在相关全局对象 上、使用 serial port task source 解析 promise,值为 port
  6. 返回 promise

串口在下列情况下被认为是可用:当它是有线串口并且物理连接至系统,或它是无线串口但对应的无线设备已注册到系统。

3.1.1 SerialPortRequestOptions 字典

WebIDLdictionary SerialPortRequestOptions {
  sequence<SerialPortFilter> filters;
  sequence<BluetoothServiceUUID> allowedBluetoothServiceClassIds;
};
filters 成员
串口筛选器
allowedBluetoothServiceClassIds 成员
BluetoothServiceUUID 的值组成的列表,代表蓝牙服务类 ID。带自定义服务类 ID 的蓝牙端口,除非在该列表中,否则不会出现在 用户可选端口列表里。

3.1.2 SerialPortFilter 字典

WebIDLdictionary SerialPortFilter {
  unsigned short usbVendorId;
  unsigned short usbProductId;
  BluetoothServiceUUID bluetoothServiceClassId;
};
usbVendorId 成员
USB 厂商 ID
usbProductId 成员
USB 产品 ID
bluetoothServiceClassId 成员
蓝牙服务类 ID

若下列步骤返回 true,则串口 port 匹配筛选条件 filter

  1. info 等于调用 port.getInfo() 的结果。
  2. 如果 filter["bluetoothServiceClassId"] 存在:
    1. 如该串口不是蓝牙设备的一部分,返回 false
    2. filter["bluetoothServiceClassId"] 等于 info["bluetoothServiceClassId"], 返回 true
    3. 否则,返回 false
  3. filter["usbVendorId"] 不存在, 返回 true
  4. 如该串口不是 USB 设备一部分,返回 false
  5. info["usbVendorId"] 不等于 filter["usbVendorId"],返回 false
  6. filter["usbProductId"] 不存在,返回 true
  7. info["usbProductId"] 不等于 filter["usbProductId"],返回 false
  8. 否则,返回 true

port 在某组 SerialPortFilter匹配任意筛选条件,步骤如下:

  1. 对序列中的每个 filter,执行下面的子步骤:
    1. 如果 port 匹配筛选条件 filter,返回 true
  2. 返回 false

3.2 getPorts() 方法

getPorts() 方法步骤如下:

  1. promise新 Promise
  2. 如果 this相关全局对象关联文档 不被允许使用名为 "serial"策略控制功能, 用 "SecurityError" DOMException 拒绝 promise 并返回。
  3. 并行执行下列步骤:
    1. availablePorts 为用户允许站点访问(通过前次调用 requestPort() 实现)的 可用 串口序列。
    2. ports 为序列 SerialPorts, 表示 availablePorts 所包含的端口。
    3. 在相关全局对象 上、使用 serial port task source 解析 promise,值为 ports
  4. 返回 promise

3.3 onconnect 属性

onconnect事件处理 IDL 属性,对应于 connect 事件类型。

3.4 ondisconnect 属性

ondisconnect事件处理 IDL 属性,对应于 disconnect 事件类型。

4. SerialPort 接口

WebIDL[Exposed=(DedicatedWorker,Window), SecureContext]
interface SerialPort : EventTarget {
  attribute EventHandler onconnect;
  attribute EventHandler ondisconnect;
  readonly attribute boolean connected;
  readonly attribute ReadableStream readable;
  readonly attribute WritableStream writable;

  SerialPortInfo getInfo();

  Promise<undefined> open(SerialOptions options);
  Promise<undefined> setSignals(optional SerialOutputSignals signals = {});
  Promise<SerialInputSignals> getSignals();
  Promise<undefined> close();
  Promise<undefined> forget();
};

该接口的方法通常为异步完成,会在串口任务源上排队执行。

SerialPortget the parent 算法会返回同一个 Serial 实例,该实例由 Navigator 对象的 serial getter 返回。

SerialPort 实例包含下表所述的内部插槽:

内部插槽 初始值 说明(非规范性)
[[state]] "closed" 跟踪SerialPort的活动状态
[[bufferSize]] undefined 数据收发缓冲区的大小
[[connected]] false 标志串口逻辑连接状态
[[readable]] null 一个 ReadableStream 用于接收串口数据
[[readFatal]] false 标志端口遇到严重读取错误
[[writable]] null 一个 WritableStream 向端口发送数据
[[writeFatal]] false 标志端口遇到严重写入错误
[[pendingClosePromise]] null 一个 Promise 用于等待 readablewritable 关闭

4.1 onconnect 属性

onconnect事件处理 IDL 属性,用于 connect 事件类型。

当用户此前已授权站点访问的串口,通过 requestPort() 方法,变为 逻辑连接 状态时,执行以下步骤:

  1. port 为表示该端口的 SerialPort
  2. port.[[connected]] 设为 true
  3. 触发名为 connect 的事件,目标为 port, 并将 bubbles 属性初始化为 true

当一个串口是逻辑连接 状态时,即为有线串口且物理连接至系统,或为无线串口且系统与该无线设备保持活跃连接(如已打开蓝牙 L2CAP 通道)。

4.2 ondisconnect 属性

ondisconnect事件处理 IDL 属性,用于 disconnect 事件类型。

当用户此前已授权站点访问的串口,通过 requestPort(),不再 逻辑连接,则执行以下步骤:

  1. port 为表示该端口的 SerialPort
  2. port.[[connected]] 设为 false
  3. 触发名为 disconnect 的事件,目标为 port,并将 bubbles 属性初始化为 true

4.3 getInfo() 方法

getInfo() 方法步骤如下:
  1. info 为一个空的有序映射
  2. 如果端口属于 USB 设备,执行下列步骤:
    1. info["usbVendorId"] 设为 设备的厂商 ID。
    2. info["usbProductId"] 设为 设备的产品 ID。
  3. 如果端口是蓝牙设备的某项服务,执行以下步骤:
    1. info["bluetoothServiceClassId"] 设为蓝牙服务的服务类 UUID。
  4. 返回 info

4.3.1 SerialPortInfo 字典

WebIDLdictionary SerialPortInfo {
  unsigned short usbVendorId;
  unsigned short usbProductId;
  BluetoothServiceUUID bluetoothServiceClassId;
};
usbVendorId 成员
如果端口属于 USB 设备则为该设备的16位厂商 ID,否则为 undefined
usbProductId 成员
如果端口属于 USB 设备则为该设备的16位产品 ID,否则为 undefined
bluetoothServiceClassId 成员
如果端口是蓝牙设备的某项服务,则为 BluetoothServiceUUID ,包含服务类 UUID。否则为 undefined

4.4 open() 方法

open() 方法执行步骤如下:

  1. promise新 Promise
  2. 如果 this.[[state]]"closed",则以 "InvalidStateError" DOMException 拒绝 promise 并返回。
  3. 如果 options["dataBits"] 不是 7 或 8, 则以 TypeError 拒绝 promise 并返回。
  4. 如果 options["stopBits"] 不是 1 或 2, 则以 TypeError 拒绝 promise 并返回。
  5. 如果 options["bufferSize"] 为 0,则以 TypeError 拒绝 promise 并返回。
  6. 可选地,如 options["bufferSize"] 大于实现所能支持的上限,则以 TypeError 拒绝 promise 并返回。
  7. this.[[state]] 设为 "opening"
  8. 如下并行执行:
    1. 调用操作系统,使用 options 中指定(或默认)的参数打开串口。
    2. 如因任何原因失败,在相关全局对象 上、使用 serial port task source 拒绝 promise,错误为 "NetworkError" DOMException 并终止执行。
    3. this.[[state]] 设为 "opened"
    4. this.[[bufferSize]] 设为 options["bufferSize"]。
    5. 在相关全局对象 上、使用 serial port task source 解析 promise,值为 undefined
  9. 返回 promise

4.4.1 SerialOptions 字典

WebIDLdictionary SerialOptions {
  [EnforceRange] required unsigned long baudRate;
  [EnforceRange] octet dataBits = 8;
  [EnforceRange] octet stopBits = 1;
  ParityType parity = "none";
  [EnforceRange] unsigned long bufferSize = 255;
  FlowControlType flowControl = "none";
};
baudRate 成员
一个正整数且不为零,表示串口通信应使用的波特率。
注意
baudRate 是该字典唯一必需成员。 虽然其它连接参数有通用默认值,但开发者应查阅拟连接设备的文档来确定正确的取值。没有统一标准的波特率,要求明确该参数可减少规范默认值带来的困惑。
dataBits 成员
每帧数据位数,取值为 7 或 8。
stopBits 成员
每帧停止位数,取值为 1 或 2。
parity 成员
奇偶校验模式。
bufferSize 成员
指定应建读写缓冲区的正整数大小且不为零。
flowControl 成员
流控模式。
4.4.1.1 ParityType 枚举
WebIDLenum ParityType {
  "none",
  "even",
  "odd"
};
none
每个数据字不发送校验位。
even
数据字与校验位的总数为偶校验。
odd
数据字与校验位的总数为奇校验。
4.4.1.2 FlowControlType 枚举
WebIDLenum FlowControlType {
  "none",
  "hardware"
};
none
不启用流控。
hardware
启用基于RTS和CTS信号的硬件流控。

4.5 connected 属性

connected 获取操作如下:

  1. 返回 this.[[connected]]

4.6 readable 属性

readable 获取步骤如下:

  1. 如果 this.[[readable]] 不为 null,返回 this.[[readable]]
  2. 如果 this.[[state]]"opened",返回 null
  3. 如果 this.[[readFatal]]true,返回 null
  4. stream 为一个新的 ReadableStream
  5. pullAlgorithm 为如下步骤:
    1. desiredSize填满高水位线所需大小, 针对 this.[[readable]]
    2. 如果 this.[[readable]]当前 BYOB 请求视图 非空,则将 desiredSize 设为 this.[[readable]]当前 BYOB 请求视图字节长度
    3. 执行如下并行步骤:
      1. 调用操作系统,从端口读取至多 desiredSize 个字节,结果保存在 字节序列 bytes 中。
        注意
        this.[[state]] 变为 "forgotten" 时,要按端口断开处理。
      2. 在相关全局对象 上、使用 serial port task source 执行:
        1. 如果未遇到错误,则:
          1. 如果 this.[[readable]]当前 BYOB 请求视图 非空,则将 bytes 写入 this.[[readable]]当前 BYOB 请求视图, 并将 view 设为 this.[[readable]]当前 BYOB 请求视图
          2. 否则,将 view 设为以 bytes 创建的 Uint8Array,在 this相关 Realm 中创建。
          3. 入队 viewthis.[[readable]]
        2. 如遇 buffer overrun,调用 errorthis.[[readable]],错误为 "BufferOverrunError" DOMException, 并调用关闭 readable 流处理
        3. 如遇 break condition,调用 errorthis.[[readable]],错误为 "BreakError" DOMException, 并调用关闭 readable 流处理
        4. 如遇 framing error,调用 errorthis.[[readable]],错误为 "FramingError" DOMException, 并调用 关闭 readable 流处理
        5. 如遇 parity error,调用 errorthis.[[readable]],错误为 "ParityError" DOMException, 并调用 关闭 readable 流处理
        6. 如遇操作系统错误,调用 errorthis.[[readable]],错误为 "UnknownError" DOMException, 并调用 关闭 readable 流处理
        7. 端口已断开,执行如下:
          1. this.[[readFatal]] 设为 true
          2. 调用 errorthis.[[readable]],错误为 "NetworkError" DOMException
          3. 调用 关闭 readable 流处理
    4. 返回undefined 解析的 promise。
    注意

    本算法返回的Promise会立即被 resolve,不会阻塞流的取消。[STREAMS] 规定该算法不会在 chunk 未入队时被再次调用。

  6. cancelAlgorithm 如下:
    1. promise新 Promise
    2. 执行如下并行步骤。
      1. 让操作系统丢弃端口相关的所有软硬件接收缓冲区内容。
      2. 在相关全局对象 上、使用 serial port task source 执行:
        1. 调用 关闭 readable 流处理
        2. 解析 promise,值为 undefined
    3. 返回 promise
  7. 用字节读取支持设置 stream , 其 pullAlgorithm 设为 pullAlgorithmcancelAlgorithm 设为 cancelAlgorithmhighWaterMark 设为 this.[[bufferSize]]
  8. this.[[readable]] 设为 stream
  9. 返回 stream
关闭 readable 流处理,执行以下步骤:
  1. this.[[readable]] 设为 null
  2. 如果 this.[[writable]]nullthis.[[pendingClosePromise]]null解析 this.[[pendingClosePromise]],值为 undefined

4.7 writable 属性

writable 获取的步骤如下:

  1. 如果 this.[[writable]] 不为 null,返回 this.[[writable]]
  2. 如果 this.[[state]]"opened",返回 null
  3. 如果 this.[[writeFatal]]true,返回 null
  4. stream 为一个新的 WritableStream
  5. signalstreamsignal
  6. writeAlgorithm 如下,对于给定的 chunk
    1. promise新 Promise
    2. 断言:signal 未被中止
    3. 如果 chunk 无法 转为 BufferSource 类型的 IDL 值,则用 TypeError 拒绝 promise 并返回。否则,转化结果保存到 source
    4. 获取缓冲区副本 source,结果保存到 bytes
    5. 并行执行以下步骤:
      1. 调用操作系统将 bytes 写入端口。或先缓存在本地以便合并。
        注意
        操作系统可能在 bytes 被排队等待发送后就返回, 而不是等数据实际发送完毕。
        注意
        this.[[state]] 变为 "forgotten" 时要视为端口已断开。
      2. 在相关全局对象 上,使用 serial port task source,执行:
        1. 如果数据块已被成功写入或已缓存合并,resolve promise,值为 undefined
          注意
          [STREAMS] 规定 writeAlgorithm 只会在上一次返回的 Promise 解析之后再被调用。 为了效率,实现允许提前 resolve,这样可将多个排队的 chunk 合并为一次系统调用。
        2. 如遇操作系统错误, reject promise,错误为 "UnknownError" DOMException
        3. 端口已断开,执行以下步骤:
          1. this.[[writeFatal]] 设为 true
          2. Reject promise,错误为 "NetworkError" DOMException
          3. 调用 关闭 writable 流处理
        4. signal中止,则 reject promise,错误为 signal中止原因
    6. 返回 promise
  7. abortAlgorithm 如下:
    1. promise新 Promise
    2. 执行如下并行步骤。
      1. 让操作系统丢弃端口所有软硬件发送缓冲内容。
      2. 在相关全局对象 上、使用 serial port task source 执行:
        1. 调用 关闭 writable 流处理
        2. 解析 promise,值为 undefined
    3. 返回 promise
  8. closeAlgorithm 如下:
    1. promise新 Promise
    2. 执行如下并行步骤。
      1. 让操作系统刷新该端口的所有软硬件发送缓冲内容。
      2. 在相关全局对象 上、使用 serial port task source 执行:
        1. 调用 关闭 writable 流处理
        2. signal中止reject promise,错误为 signal中止原因
        3. 否则,resolve promise,值为 undefined
    3. 返回 promise
  9. 设置 stream,其 writeAlgorithm 设为 writeAlgorithm abortAlgorithm 设为 abortAlgorithm closeAlgorithm 设为 closeAlgorithm highWaterMark 设为 this.[[bufferSize]] sizeAlgorithm 设为字节计数算法。
  10. 添加下述中止操作到 signal
    1. 使任何针对端口的写操作系统调用立刻返回,无论数据是否实际写入。
  11. this.[[writable]] 设为 stream
  12. 返回 stream
关闭 writable 流处理,执行以下步骤:
  1. this.[[writable]] 设为 null
  2. 如果 this.[[readable]]nullthis.[[pendingClosePromise]] 不为 null解析 this.[[pendingClosePromise]],值为 undefined

4.8 setSignals() 方法

setSignals() 方法的步骤如下:

  1. promise新的 Promise
  2. 如果 this.[[state]] 不为 "opened",则以 "InvalidStateError" DOMException 拒绝 promise 并返回。
  3. 如果 signals 所有指定成员均不存在,则以 TypeError 拒绝 promise 并返回。
  4. 并行执行以下步骤:
    注意
    理想情况下,signals 中指定的各项更改应原子性应用,但无论是 POSIX 还是 Windows API 都不支持。所以下述顺序很可能被应用所依赖。
    1. signals["dataTerminalReady"] 存在, 则调用操作系统在串口上置位(为 true 时)或清除(为 false 时)“数据终端就绪”(DTR)信号。
    2. signals["requestToSend"] 存在, 则调用操作系统在串口上置位(为 true 时)或清除(为 false 时)“请求发送”(RTS)信号。
    3. signals["break"] 存在, 则调用操作系统在串口上置位(为 true 时)或清除(为 false 时)“break”信号。
      注意
      “break” 信号通常通过保持发送线在“mark”电平实现,在assert状态下不能发送数据。
    4. 如操作系统因任何原因未能改变任一信号状态,在相关全局对象 上、使用 serial port task source 拒绝 promise,错误为 "NetworkError" DOMException
    5. 在相关全局对象上、 使用 serial port task source 解析 promise,值为 undefined
  5. 返回 promise

4.8.1 SerialOutputSignals 字典

WebIDLdictionary SerialOutputSignals {
  boolean dataTerminalReady;
  boolean requestToSend;
  boolean break;
};
dataTerminalReady
数据终端就绪(DTR)
requestToSend
请求发送(RTS)
break
Break(断开信号)

4.9 getSignals() 方法

getSignals() 方法步骤如下:
  1. promise新 Promise
  2. 如果 this.[[state]] 不为 "opened",以 "InvalidStateError" DOMException 拒绝 promise 并返回。
  3. 并行执行以下步骤:
    1. 查询操作系统,获取串口连接设备可置位的控制信号状态。
    2. 如操作系统因任何原因未能判断信号状态,在相关全局对象 上、使用 serial port task source 拒绝 promise,错误为 "NetworkError" DOMException,并中止步骤。
    3. 如“数据载波检测”(DCD)信号已被设备置位,令 dataCarrierDetecttrue,否则为 false
    4. 如“清除发送”(CTS)信号已被设备置位,令 clearToSendtrue,否则为 false
    5. 如“振铃指示”(RI)信号已被设备置位,令 ringIndicatortrue,否则为 false
    6. 如“数据集就绪”(DSR)信号已被设备置位,令 dataSetReadytrue,否则为 false
    7. signals有序映射 «[ "dataCarrierDetect" → dataCarrierDetect, "clearToSend" → clearToSend, "ringIndicator" → ringIndicator, "dataSetReady" → dataSetReady ]»。
    8. 在相关全局对象上、 使用 serial port task source 解析 promise,值为 signals
  4. 返回 promise

4.9.1 SerialInputSignals 字典

WebIDLdictionary SerialInputSignals {
  required boolean dataCarrierDetect;
  required boolean clearToSend;
  required boolean ringIndicator;
  required boolean dataSetReady;
};
dataCarrierDetect 成员
数据载波检测(DCD)
clearToSend 成员
清除发送(CTS)
ringIndicator 成员
振铃指示(RI)
dataSetReady 成员
数据集就绪(DSR)

4.10 close() 方法

close() 方法的执行步骤如下:

  1. promise新 Promise
  2. 如果 this.[[state]]"opened", 则用 "InvalidStateError" DOMException 拒绝 promise 并返回。
  3. cancelPromise 为调用 cancelthis.[[readable]] undefined 解析的 promise(如 this.[[readable]]null)。
  4. abortPromise 为调用 abortthis.[[writable]] undefined 解析的 promise(如 this.[[writable]]null)。
  5. pendingClosePromise新 Promise
  6. 如果 this.[[readable]]this.[[writable]] 均为 null解析 pendingClosePromise,值为 undefined
  7. this.[[pendingClosePromise]] 设为 pendingClosePromise
  8. combinedPromise等待全部 «cancelPromise, abortPromise, pendingClosePromise»。
  9. this.[[state]] 设为 "closing"
  10. combinedPromise 做出反应。
  11. 返回 promise

4.11 forget() 方法

forget() 方法的步骤如下:

  1. 如果用户代理无法执行(如权限为管理员策略授予),则返回 undefined 解析的 promise。
  2. 并行执行以下步骤:
    1. this.[[state]] 设为 "forgetting"
    2. this 从 用户此前通过 requestPort() 授权访问的串口序列中移除。
    3. this.[[state]] 设为 "forgotten"
    4. 在相关全局对象上 ,使用 serial port task source 解析 promise,值为 undefined
  3. 返回 promise

5. 阻止列表

本规范依赖于 https://github.com/WICG/serial 仓库中的 blocklist 文件,用于限制网站可访问的端口集合。

在 URL url 解析蓝牙服务类 ID 阻止列表的结果,是一个列表,包含代表自定义服务 ID 的 UUID 值。

串口配置文件服务类 ID 是一个 BluetoothServiceUUID ,其值为 "00001101-0000-1000-8000-00805f9b34fb"。

当 {{BluetoothServiceUUID} serviceUuid 满足下列步骤时, 被阻止的蓝牙服务类 UUID 返回 true

  1. uuid 为调用 BluetoothUUID.getService() ,参数为 serviceUuid 的结果。
  2. blocklist解析蓝牙服务类 ID 阻止列表 ,URL 为 https://github.com/WICG/serial/blob/main/blocklist.txt 的结果。
  3. 如果 blocklist 包含 uuid, 返回 true
  4. 如果 uuid 等于 串口配置文件服务类 ID, 返回 false
  5. 如果 uuid 结尾为 "-0000-1000-8000-00805f9b34fb", 返回 true
  6. 否则,返回 false

6. 集成

6.1 权限策略

本规范定义了一个功能,用于控制 serial 属性在 Navigator 对象上暴露的方法是否可用。

本功能的特性名称为 "serial"。

本功能的默认允许列表'self'

7. 安全性注意事项

本节为非规范性内容。

本 API 与[WEB-BLUETOOTH]和 [WEBUSB]有类似的安全风险,因此前者的经验同样适用。主要威胁有: 主要的缓解措施是 requestPort() 模式, 该模式要求用户交互且只支持一次授权一个设备访问。这防止了“途经攻击”,因为站点无法遍历所有已连接设备来判断是否有易受攻击的设备,必须由用户明确操作授权。实现还可以提供 可视化指示,提示当前站点正在与设备通信,并允许用户随时撤销授权。

本规范要求站点必须部署在 安全上下文中,以防止基于 网络的攻击者注入恶意代码。这样在用户做权限决策时,显示的站点身份才是可信的。本规范还要求顶级文档必须通过 [PERMISSIONS-POLICY] 显式声明,跨域 iframe 才能使用本 API。结合 [CSP3] 可进一步防护代码注入攻击。

剩下的问题是,钓鱼攻击可能说服用户授予恶意网站访问设备的权限。这类攻击可以操纵设备的正常功能,也可以刷写恶意固件后攻击主机。主机端软件很可能没有正确校验设备输入而受攻击。安全研究建议 软件厂商将外部设备视为不可信。

没有什么机制可以完全防止这类攻击,因为从页面发给设备的数据是字节流。尝试阻止特定类型数据的做法,很可能被设备厂商以规避手段绕过。

用户代理可以实现额外的管理机制来进一步控制设备访问权限:

[WEB-BLUETOOTH]和[WEBUSB] 的实现都尝试过这些缓解措施,但这些措施有效性有限。首先,很难准确界定设备是否易受攻击。例如,本 API 允许站点将固件上传到微控制器开发板。这是本 API 面向教育和爱好市场的关键用例。这些开发板通常不校验固件签名,很容易被变成恶意设备。这类开发板显然可被利用但不应被屏蔽。

此外,维护脆弱设备列表在 USB 和蓝牙场景下有效,因为这些协议可以带外获取设备元数据。这样可以识别厂商和型号,即使设备对主机表现为虚拟串口。但还有大量 USB/蓝牙转串口适配器和那些用 DB-25、DE-9 或 RJ-45 连接的“真”串口。这些场景下没有可读元数据,无法得知设备身份,所以无法屏蔽这类访问。

8. 隐私注意事项

本节为非规范性内容。

串口及其设备涉及两类敏感信息。当串口为 USB 或蓝牙设备时,包含厂商 ID、产品 ID(标识型号)及序列号或 MAC 地址。本身也可能有自有的标识,或通过串口命令等获得。设备内还可能存储其他隐私信息,有些也许能指向身份。

为方便权限管理,实际实现很可能会把 USB 厂商 ID、产品 ID、序列号等存储在用户偏好文件中,作为用户授权访问设备的稳定标识。这些不会直接分享给网站,并且在权限撤销或站点数据整体清除时会被清除。

页面在获得设备访问权限后,可以通过命令访问设备已存储的其他敏感信息。参见 7. 安全性注意事项,不宜试图通过技术手段限制页面访问这些信息。

实现应该让用户完全掌握站点对哪些设备的访问权限,并且在无用户操作时不得自动授权设备访问。requestPort() 的设计正是如此。这样可以阻止网站静默遍历和采集所有已连接设备的信息,有点类似文件选择器。站点对文件系统一无所知,只能访问用户指定的文件/目录。实现也可以在标签页或地址栏上显示图标,指示站点正在使用该权限。

支持“隐私模式”或“无痕模式”的实现,应保证普通用户配置的权限不会带入隐私会话,并且该会话下授予的权限在结束后不被保留。实现可在隐私会话授权设备访问时提醒用户——如同手动输入身份证一样,这些设备标识和先前提到的唯一特性跨会话追踪用户的风险也必须说明。

如果用户不了解本 API 能带来的能力,可能会被授权后突破传统 Web 安全模型的隔离边界感到惊讶。安全 UI 和文档应该明确说明,授权网站访问设备可能让其完全控制设备及设备内数据。

9. 一致性

除标记为非规范性内容的章节外,本规范中的所有创作指导、图示、示例和说明均为非规范性内容。其余内容均为规范性。

A. 致谢

下列人员为本规范的制定做出贡献:

B. 参考文献

B.1 规范性引用

[dom]
DOM 标准. Anne van Kesteren. WHATWG. 实时标准. URL: https://dom.spec.whatwg.org/
[html]
HTML 标准. Anne van Kesteren; Domenic Denicola; Dominic Farolino; Ian Hickson; Philip Jägenstedt; Simon Pieters. WHATWG. 实时标准. URL: https://html.spec.whatwg.org/multipage/
[infra]
Infra 标准. Anne van Kesteren; Domenic Denicola. WHATWG. 实时标准. URL: https://infra.spec.whatwg.org/
[PERMISSIONS-POLICY]
权限策略. Ian Clelland. W3C. 2025年7月18日. W3C 工作草案. URL: https://www.w3.org/TR/permissions-policy-1/
[STREAMS]
流标准. Adam Rice; Domenic Denicola; Mattias Buelens; 吉野剛史 (Takeshi Yoshino). WHATWG. 实时标准. URL: https://streams.spec.whatwg.org/
[WEB-BLUETOOTH]
Web Bluetooth. Jeffrey Yasskin. W3C Web Bluetooth Community Group. 草案社区报告. URL: https://webbluetoothcg.github.io/web-bluetooth/
[WEBIDL]
Web IDL 标准. Edgar Chen; Timothy Gu. WHATWG. 实时标准. URL: https://webidl.spec.whatwg.org/

B.2 非规范性引用

[CSP3]
内容安全策略3. Mike West; Antonio Sartori. W3C. 2025年7月11日. W3C 工作草案. URL: https://www.w3.org/TR/CSP3/
[WEBUSB]
WebUSB API. W3C. 草案社区报告 . URL: https://wicg.github.io/webusb/