Copyright © 2025 the Contributors to the Web Serial API Specification, published by the Web Platform Incubator Community Group under the W3C Community Contributor License Agreement (CLA). A human-readable summary is available.
本规范由 Web 平台孵化社区组发布。它不是 W3C 标准,也不在 W3C 标准轨道上。 请注意,根据 W3C 社区贡献者许可协议 (CLA) 有有限的选择退出选项,并且适用其他条件。 了解更多关于 W3C 社区与商务组的信息。
该规范仍在不断完善中。欢迎所有贡献。讨论本规范优先使用GitHub Issues。
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 = {});
};
requestPort() 方法的执行步骤如下:
serial" 的策略控制功能,
则用 "SecurityError"
DOMException 拒绝
promise 并返回。
SecurityError"
DOMException
拒绝 promise,并返回。
filters"] 存在,
对于 options["filters"] 中的每个
filter 执行以下步骤:
bluetoothServiceClassId"]
存在:
usbVendorId"]
存在,
用 TypeError
拒绝 promise 并返回。
usbProductId"]
存在,
用 TypeError
拒绝 promise 并返回。
usbVendorId"] 不存在,用
TypeError
拒绝 promise并返回。
BluetoothServiceUUID
uuid:
allowedBluetoothServiceClassIds"]
存在且包含
uuid:
SerialPort
。
SerialPort。
filters"]
存在)或全部内容。
NotFoundError"
DOMException,
并中止后续步骤。
SerialPort 的一个实例。
串口在下列情况下被认为是可用:当它是有线串口并且物理连接至系统,或它是无线串口但对应的无线设备已注册到系统。
WebIDLdictionary SerialPortRequestOptions {
sequence<SerialPortFilter> filters;
sequence<BluetoothServiceUUID> allowedBluetoothServiceClassIds;
};
filters 成员
allowedBluetoothServiceClassIds 成员
BluetoothServiceUUID
的值组成的列表,代表蓝牙服务类 ID。带自定义服务类 ID 的蓝牙端口,除非在该列表中,否则不会出现在
用户可选端口列表里。
WebIDLdictionary SerialPortFilter {
unsigned short usbVendorId;
unsigned short usbProductId;
BluetoothServiceUUID bluetoothServiceClassId;
};
usbVendorId 成员
usbProductId 成员
bluetoothServiceClassId 成员
若下列步骤返回 true,则串口 port 匹配筛选条件
filter:
getInfo() 的结果。
bluetoothServiceClassId"]
存在:
false。
bluetoothServiceClassId"]
等于
info["bluetoothServiceClassId"],
返回
true。
false。
usbVendorId"] 不存在,
返回 true。
false。
usbVendorId"] 不等于
filter["usbVendorId"],返回
false。
usbProductId"] 不存在,返回
true。
usbProductId"] 不等于
filter["usbProductId"],返回
false。
true。
port 在某组
SerialPortFilter 内 匹配任意筛选条件,步骤如下:
true。
false。
getPorts() 方法步骤如下:
"serial" 的策略控制功能,
用 "SecurityError"
DOMException
拒绝 promise 并返回。
requestPort() 实现)的
可用
串口序列。
SerialPorts,
表示 availablePorts 所包含的端口。
onconnect 是
事件处理
IDL 属性,对应于 connect 事件类型。
ondisconnect 是
事件处理
IDL 属性,对应于 disconnect 事件类型。
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();
};
该接口的方法通常为异步完成,会在串口任务源上排队执行。
SerialPort 的 get 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 用于等待
readable 和
writable 关闭
|
onconnect 是
事件处理
IDL 属性,用于 connect 事件类型。
当用户此前已授权站点访问的串口,通过 requestPort() 方法,变为
逻辑连接 状态时,执行以下步骤:
SerialPort。
[[connected]] 设为 true。
connect 的事件,目标为 port,
并将 bubbles 属性初始化为
true。
当一个串口是逻辑连接 状态时,即为有线串口且物理连接至系统,或为无线串口且系统与该无线设备保持活跃连接(如已打开蓝牙 L2CAP 通道)。
ondisconnect 是
事件处理
IDL 属性,用于 disconnect 事件类型。
当用户此前已授权站点访问的串口,通过 requestPort(),不再
逻辑连接,则执行以下步骤:
SerialPort。
[[connected]] 设为 false。
disconnect 的事件,目标为 port,并将
bubbles 属性初始化为
true。
getInfo() 方法步骤如下:
usbVendorId"] 设为
设备的厂商 ID。
usbProductId"] 设为
设备的产品 ID。
bluetoothServiceClassId"]
设为蓝牙服务的服务类 UUID。WebIDLdictionary SerialPortInfo {
unsigned short usbVendorId;
unsigned short usbProductId;
BluetoothServiceUUID bluetoothServiceClassId;
};
usbVendorId 成员
undefined。
usbProductId 成员
undefined。
bluetoothServiceClassId 成员
BluetoothServiceUUID
,包含服务类 UUID。否则为 undefined。
open() 方法执行步骤如下:
[[state]] 非 "closed",则以 "InvalidStateError"
DOMException 拒绝
promise 并返回。
dataBits"] 不是 7 或
8,
则以 TypeError 拒绝
promise 并返回。
stopBits"] 不是 1 或
2,
则以 TypeError 拒绝
promise 并返回。
bufferSize"] 为
0,则以 TypeError 拒绝
promise 并返回。
bufferSize"]
大于实现所能支持的上限,则以 TypeError 拒绝
promise 并返回。
[[state]] 设为 "opening"。
NetworkError"
DOMException
并终止执行。
[[state]] 设为 "opened"。
[[bufferSize]] 设为
options["bufferSize"]。
undefined。
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 成员
stopBits 成员
parity 成员
bufferSize
成员
flowControl 成员
WebIDLenum ParityType {
"none",
"even",
"odd"
};
none
even
odd
WebIDLenum FlowControlType {
"none",
"hardware"
};
none
hardware
connected 获取操作如下:
[[connected]]。
readable 获取步骤如下:
[[readable]] 不为 null,返回
this.[[readable]]。
[[state]] 非 "opened",返回
null。
[[readFatal]] 为 true,返回
null。
ReadableStream。
[[readable]]。
[[readable]] 的
当前 BYOB
请求视图
非空,则将 desiredSize 设为 this.[[readable]] 的
当前 BYOB
请求视图的
字节长度。
[[readable]] 的
当前
BYOB 请求视图
非空,则将 bytes 写入 this.[[readable]] 的
当前
BYOB 请求视图,
并将 view 设为 this.[[readable]] 的
当前
BYOB 请求视图。
Uint8Array,在
this 的 相关
Realm 中创建。
[[readable]]。
[[readable]],错误为
"BufferOverrunError" DOMException,
并调用关闭 readable
流处理。
[[readable]],错误为
"BreakError" DOMException,
并调用关闭 readable
流处理。
[[readable]],错误为
"FramingError" DOMException,
并调用
关闭 readable
流处理。
[[readable]],错误为
"ParityError" DOMException,
并调用
关闭 readable
流处理。
[[readable]],错误为 "UnknownError"
DOMException,
并调用 关闭 readable
流处理。
[[readFatal]] 设为
true,
[[readable]],错误为
"NetworkError"
DOMException。
undefined 解析的 promise。
undefined。
[[bufferSize]]。
[[readable]] 设为 stream。
[[readable]] 设为 null。
[[writable]] 为 null 且
this.[[pendingClosePromise]] 非
null,
解析 this.[[pendingClosePromise]],值为
undefined。
writable 获取的步骤如下:
[[writable]] 不为 null,返回
this.[[writable]]。
[[state]] 非 "opened",返回
null。
[[writeFatal]] 为 true,返回
null。
WritableStream。
BufferSource
类型的 IDL 值,则用 TypeError
拒绝 promise 并返回。否则,转化结果保存到
source。
undefined。
UnknownError"
DOMException。
[[writeFatal]] 设为
true。
NetworkError"
DOMException。
undefined。
undefined。
[[bufferSize]],
sizeAlgorithm 设为字节计数算法。
[[writable]] 设为 stream。
[[writable]] 设为 null。
[[readable]] 为 null 且
this.[[pendingClosePromise]] 不为
null,
解析 this.[[pendingClosePromise]],值为
undefined。
setSignals() 方法的步骤如下:
[[state]] 不为 "opened",则以 "InvalidStateError"
DOMException 拒绝
promise 并返回。
TypeError 拒绝
promise 并返回。
dataTerminalReady"]
存在,
则调用操作系统在串口上置位(为 true 时)或清除(为 false 时)“数据终端就绪”(DTR)信号。
requestToSend"]
存在,
则调用操作系统在串口上置位(为 true 时)或清除(为 false 时)“请求发送”(RTS)信号。
break"] 存在,
则调用操作系统在串口上置位(为 true 时)或清除(为 false 时)“break”信号。
NetworkError"
DOMException。
undefined。
WebIDLdictionary SerialOutputSignals {
boolean dataTerminalReady;
boolean requestToSend;
boolean break;
};
dataTerminalReady
requestToSend
break
getSignals() 方法步骤如下:
[[state]] 不为 "opened",以 "InvalidStateError"
DOMException 拒绝
promise 并返回。
NetworkError"
DOMException,并中止步骤。
true,否则为
false。
true,否则为 false。
true,否则为 false。
true,否则为 false。
dataCarrierDetect"
→ dataCarrierDetect,
"clearToSend" →
clearToSend,
"ringIndicator" →
ringIndicator,
"dataSetReady" →
dataSetReady ]»。
WebIDLdictionary SerialInputSignals {
required boolean dataCarrierDetect;
required boolean clearToSend;
required boolean ringIndicator;
required boolean dataSetReady;
};
dataCarrierDetect
成员
clearToSend
成员
ringIndicator
成员
dataSetReady
成员
close() 方法的执行步骤如下:
[[state]] 非 "opened",
则用 "InvalidStateError"
DOMException
拒绝 promise 并返回。
[[readable]]
或 以
undefined 解析的 promise(如 this.[[readable]] 为 null)。
[[writable]]
或 以
undefined 解析的 promise(如 this.[[writable]] 为 null)。
[[readable]] 和
this.[[writable]] 均为 null,解析
pendingClosePromise,值为 undefined。
[[pendingClosePromise]] 设为
pendingClosePromise。
[[state]] 设为 "closing"。
[[state]]
设为 "closed"。
[[readFatal]] 和
this.[[writeFatal]] 设为
false。
[[pendingClosePromise]]
设为 null。
undefined。
[[pendingClosePromise]]
设为 null。
forget() 方法的步骤如下:
undefined 解析的 promise。
[[state]] 设为 "forgetting"。
requestPort()
授权访问的串口序列中移除。
[[state]] 设为 "forgotten"。
undefined。
本规范依赖于 https://github.com/WICG/serial 仓库中的 blocklist 文件,用于限制网站可访问的端口集合。
在 URL url 解析蓝牙服务类 ID 阻止列表的结果,是一个列表,包含代表自定义服务 ID 的 UUID 值。
串口配置文件服务类 ID 是一个
BluetoothServiceUUID
,其值为
"00001101-0000-1000-8000-00805f9b34fb"。
当 {{BluetoothServiceUUID} serviceUuid 满足下列步骤时,
被阻止的蓝牙服务类 UUID 返回 true:
BluetoothUUID.getService()
,参数为 serviceUuid 的结果。
true。
false。
-0000-1000-8000-00805f9b34fb",
返回 true。
false。
本规范定义了一个功能,用于控制 serial 属性在
Navigator
对象上暴露的方法是否可用。
本功能的特性名称为 "serial"。
本功能的默认允许列表
是 'self'。
本节为非规范性内容。
本 API 与[WEB-BLUETOOTH]和 [WEBUSB]有类似的安全风险,因此前者的经验同样适用。主要威胁有:requestPort() 模式,
该模式要求用户交互且只支持一次授权一个设备访问。这防止了“途经攻击”,因为站点无法遍历所有已连接设备来判断是否有易受攻击的设备,必须由用户明确操作授权。实现还可以提供
可视化指示,提示当前站点正在与设备通信,并允许用户随时撤销授权。
本规范要求站点必须部署在 安全上下文中,以防止基于 网络的攻击者注入恶意代码。这样在用户做权限决策时,显示的站点身份才是可信的。本规范还要求顶级文档必须通过 [PERMISSIONS-POLICY] 显式声明,跨域 iframe 才能使用本 API。结合 [CSP3] 可进一步防护代码注入攻击。
剩下的问题是,钓鱼攻击可能说服用户授予恶意网站访问设备的权限。这类攻击可以操纵设备的正常功能,也可以刷写恶意固件后攻击主机。主机端软件很可能没有正确校验设备输入而受攻击。安全研究建议 软件厂商将外部设备视为不可信。
没有什么机制可以完全防止这类攻击,因为从页面发给设备的数据是字节流。尝试阻止特定类型数据的做法,很可能被设备厂商以规避手段绕过。
用户代理可以实现额外的管理机制来进一步控制设备访问权限:
requestPort(),
除非站点被加入明确允许列表。
系统管理员可以通过企业策略全局设置。也可以让管理员自动允许部分站点访问特定设备,其它全部拒绝。
此外,维护脆弱设备列表在 USB 和蓝牙场景下有效,因为这些协议可以带外获取设备元数据。这样可以识别厂商和型号,即使设备对主机表现为虚拟串口。但还有大量 USB/蓝牙转串口适配器和那些用 DB-25、DE-9 或 RJ-45 连接的“真”串口。这些场景下没有可读元数据,无法得知设备身份,所以无法屏蔽这类访问。
本节为非规范性内容。
串口及其设备涉及两类敏感信息。当串口为 USB 或蓝牙设备时,包含厂商 ID、产品 ID(标识型号)及序列号或 MAC 地址。本身也可能有自有的标识,或通过串口命令等获得。设备内还可能存储其他隐私信息,有些也许能指向身份。为方便权限管理,实际实现很可能会把 USB 厂商 ID、产品 ID、序列号等存储在用户偏好文件中,作为用户授权访问设备的稳定标识。这些不会直接分享给网站,并且在权限撤销或站点数据整体清除时会被清除。
页面在获得设备访问权限后,可以通过命令访问设备已存储的其他敏感信息。参见 7. 安全性注意事项,不宜试图通过技术手段限制页面访问这些信息。
实现应该让用户完全掌握站点对哪些设备的访问权限,并且在无用户操作时不得自动授权设备访问。requestPort()
的设计正是如此。这样可以阻止网站静默遍历和采集所有已连接设备的信息,有点类似文件选择器。站点对文件系统一无所知,只能访问用户指定的文件/目录。实现也可以在标签页或地址栏上显示图标,指示站点正在使用该权限。
支持“隐私模式”或“无痕模式”的实现,应保证普通用户配置的权限不会带入隐私会话,并且该会话下授予的权限在结束后不被保留。实现可在隐私会话授权设备访问时提醒用户——如同手动输入身份证一样,这些设备标识和先前提到的唯一特性跨会话追踪用户的风险也必须说明。
如果用户不了解本 API 能带来的能力,可能会被授权后突破传统 Web 安全模型的隔离边界感到惊讶。安全 UI 和文档应该明确说明,授权网站访问设备可能让其完全控制设备及设备内数据。
除标记为非规范性内容的章节外,本规范中的所有创作指导、图示、示例和说明均为非规范性内容。其余内容均为规范性。
Referenced in:
Referenced in:
Referenced in:
Referenced in:
Referenced in:
Referenced in:
Referenced in:
Referenced in:
Referenced in:
Referenced in:
Referenced in:
Referenced in:
Referenced in:
Referenced in:
Referenced in:
Referenced in:
Referenced in:
Referenced in:
Referenced in:
Referenced in:
Referenced in:
Referenced in:
Referenced in:
Referenced in:
Referenced in:
Referenced in:
Referenced in:
Referenced in:
Referenced in:
Referenced in:
Referenced in:
Referenced in:
Referenced in: