通用传感器 API

W3C 候选推荐草案

关于本文档的更多信息
本版本:
https://www.w3.org/TR/2024/CRD-generic-sensor-20240222/
最新发布版本:
https://www.w3.org/TR/generic-sensor/
编辑草稿:
https://w3c.github.io/sensors/
以往版本:
历史记录:
https://www.w3.org/standards/history/generic-sensor/
反馈:
public-device-apis@w3.org 主题行请使用 “[generic-sensor] … message topic …”(邮件列表存档
GitHub
实现报告:
https://www.w3.org/wiki/DAS/Implementations
编辑:
Rick Waldron受邀专家,前 Bocoup 和 JS Foundation 代表
前任编辑:
Mikhail Pozdnyakov英特尔公司
Alexander Shalamov英特尔公司
Tobie LangelCodespeaks,前英特尔公司代表
其它:
测试套件最新版本历史历史版本

摘要

本规范定义了一个框架,用于以一致的方式将传感器数据暴露给开放 Web 平台。 该规范通过为具体传感器的规范编写提供蓝本,并定义一个可扩展以适应不同传感器类型的抽象 Sensor 接口来实现这一目标。

本文档状态

本节描述本文档在发布时的状态。W3C 当前发布列表及本技术报告的最新修订版本可在 W3C 技术报告索引 https://www.w3.org/TR/ 查阅。

本文档由 设备与传感器工作组 作为候选推荐草案(Candidate Recommendation Draft)使用推荐流程发布。该文档预计成为 W3C 推荐标准。

如需对本文档提出意见,请发送至 public-device-apis@w3.org订阅存档)。 发送邮件时, 请在主题中注明“generic-sensor”,建议格式如下: “[generic-sensor] …评论摘要…”。 欢迎提出任何意见。

作为候选推荐草案的发布并不表示 W3C 及其成员的认可。候选推荐草案合并了前一版本中工作组拟纳入下一步候选推荐快照的更改。此为草案文档,可能随时更新、替换或废止。除作为开发中内容外,不应引用此文档。

本文档进入建议推荐阶段的准入标准 是至少有两个独立且可互操作的用户代理实现本规范的所有功能,并通过工作组制定的测试套件中的测试。工作组将准备实施报告以跟踪进展。

本文档由遵循 W3C 专利政策 的组织产生。 W3C 保持一份与本组输出相关的公开专利披露列表; 页面中还包括披露专利的相关说明。 任何知晓拥有其认为包含必要声明的专利的个人 必须按照W3C 专利政策第 6 条中的规定披露相关信息。

本文档适用 2023年11月3日 W3C 流程文档 管辖。

目前正在收集更多关于 权限请求算法 的实现经验,基于这些经验推动的规范澄清正在 GitHub issue #397 进行讨论。 工作组预计在 [PERMISSIONS-REQUEST] 进入更高阶段前不会推进本规范超过候选推荐阶段。 本文档可随时维护和更新,部分内容仍在持续完善中。

1. 简介

传感器数据在应用开发中的使用日益增多,为诸如地理定位、计步或头部跟踪等新用例提供了支持。 这在移动设备上尤为明显,新型传感器被不断引入。

将传感器数据暴露到 Web 目前一直进展缓慢且零散。 目前可被 Web 获取的传感器很少。 即使可用,往往以限制其用例的方式暴露 (例如,仅暴露过于高层的抽象,且性能不佳)。 各传感器的 API 设计也大相径庭, 增加了 Web 应用开发者的认知负担, 并减缓了开发速度。

通用传感器 API 的目标是 促进各类传感器 API 之间的一致性, 通过高性能的低层API支持高级用例, 并通过简化规范和实现流程, 加快新传感器在 Web 上的开放速度。

基于通用传感器 API 的具体传感器列表、适用用例及代码示例详见 [GENERIC-SENSOR-USECASES][MOTION-SENSORS] 说明文档。

2. 范围

本节为非规范性内容

本规范目前的范围仅限于定义用于暴露来自设备传感器数据的基础原语。

暴露远程传感器 或个人局域网(如蓝牙)中的传感器 不在当前规范范围之内。 随着相关领域工作的推进, 可能可以找到通用、较低层的原语, 届时本规范会相应更新。 不过,这对具体实现几乎没有影响。

此外,本规范目前尚未提供 传感器发现 API。 由于用户代理当前可用的传感器数量有限, 无需提供类似 API。 可依靠 §3 关于硬件特性检测的说明 所述的特性检测方法, 目前已足够。 未来规范版本可能会定义发现 API, 当前 API 的设计也已为此预留空间。

3. 关于硬件特性检测的说明

本节为非规范性内容。

特性检测是 Web 开发中公认的最佳实践。 相关资料非常丰富, 本节不再赘述, 仅着重将其置于检测硬件相关特性的背景下。

请参考以下特性检测示例:

这个简单示例说明如何检测用户代理是否暴露了某一特定传感器类型的接口。如需稳健的错误处理方式,请参考 此示例
if (typeof Gyroscope === "function") {
    // run in circles...
}

if ("ProximitySensor" in window) {
    // watch out!
}

if (window.AmbientLightSensor) {
    // go dark...
}

// etc.

以上代码都能反映出某 API 的存在 及其部分特征, 但无法判断该 API 是否与真实硬件传感器相连, 传感器本身是否正常工作, 是否依然连接, 甚至用户是否允许你访问该传感器。 关于后者,可借助 Permissions API [PERMISSIONS] 进行检测。

理想情况下,关于底层状态的信息 能被事先获得。 然而现实中存在两方面问题: 首先,从硬件获取这些信息代价较大, 既影响性能也耗电, 且会处于关键路径上。 其次,底层硬件状态随时可能变化。 用户可能撤销权限、传感器连接中断、 操作系统也可能因电量临界而限制传感器使用等。

因此,有效策略为结合特性检测 (检测是否存在目标传感器对应的 API), 并辅以防御性编程,包括:

  1. 实例化Sensor对象时捕获异常,

  2. 监听该对象发出的错误事件,

  3. 优雅处理以上情形,让用户因使用传感器体验更佳,而非因缺失变差。

下例展示了加速度计传感器的稳健创建方式。Web 应用可选用不同的错误处理策略,例如显示通知、切换传感器类型或 回退至其他 API。
let accelerometer = null;
try {
    accelerometer = new Accelerometer({ frequency: 10 });
    accelerometer.addEventListener('error', event => {
        // Handle runtime errors.
        if (event.error.name === 'NotAllowedError') {
            console.log('Permission to access sensor was denied.');
        } else if (event.error.name === 'NotReadableError' ) {
            console.log('Cannot connect to the sensor.');
        }
    });
    accelerometer.addEventListener('reading', () => reloadOnShake(accelerometer));
    accelerometer.start();
} catch (error) {
    // Handle construction errors.
    if (error.name === 'SecurityError') {
        console.log('Sensor construction was blocked by the Permissions Policy.');
    } else if (error.name === 'ReferenceError') {
        console.log('Sensor is not supported by the User Agent.');
    } else {
        throw error;
    }
}

4. 安全与隐私注意事项

如何向用户传达已知的威胁由实现者决定,但缓解措施须强制执行。

传感器读数属于敏感数据,可能被恶意网页用于各类攻击。在讨论缓解策略前, 我们简要列出传感器的主要隐私与安全威胁类型。 [MOBILESENSORS] 将主要威胁分为 位置跟踪窃听按键监控设备指纹 以及 用户识别。 这一分类与本规范完全契合。

多个传感器联合使用,或与其他功能结合使用,或长期持续使用时, 攻击成功风险会提升,尤其体现在数据相关性和通过指纹识别用户的风险。 采用这些 JavaScript API 的 Web 应用开发者应考虑信息可能被如何整合,以及潜在的隐私风险。 还应考虑长期收集此类数据所带来的风险。

传感器读数及事件触发频率 都有可能导致用户被指纹识别。 用户代理可通过 限制开发者可用的事件频率 来降低风险。

降低传感器读数精度 通常能减少指纹识别风险。 用户代理不应输出过于详细的传感器数据。 不同传感器类型应分别评估。

若同一 API 的 JavaScript 代码可 在一台设备的多个窗口上下文中并发执行, 代码间或可关联追踪用户, 带来非预期的跟踪机制。

用户代理应考虑向用户 提示 传感器的使用情况, 并允许用户禁用之。 另外,用户代理也可考虑 让用户查看过去和当前的传感器使用模式。

使用传感器的 Web 应用开发者应 对应用执行隐私影响评估, 全面考虑所有相关因素。

检测设备上全部可用传感器的能力可能形成唯一标识,进而用于指纹识别。

选定传感器的组合甚至可能作为设备间的隐式通信信道。

传感器还可能被用于跨设备的关联与跟踪。

4.1. 隐私与安全威胁类型

本节为非规范性内容。

4.1.1. 位置跟踪

在此类威胁下,攻击者可利用传感器读数定位设备, 而无需 GPS 或其他定位传感器。例如,可通过加速度计数据用统计模型估算轨迹, 进而通过地图匹配算法获取预测位置(精度可达 200 米半径以内)[MOBILESENSORS]

4.1.2. 窃听

从陀螺仪读数恢复语音属于窃听攻击的一例。 参见 [GYROSPEECHRECOGNITION]

4.1.3. 按键监控

许多用户输入都可由传感器读数推测,包括利用运动传感器攻击用户 PIN、密码、解锁模式 (甚至点击、滑动、缩放等触控操作)。 这些攻击通常训练机器学习算法来获取用户的此类信息。参见 [STEALINGPINSVIASENSORS]

4.1.4. 设备指纹

传感器可泄露可用于唯一识别设备的信息。 每一型号的具体传感器在制造上都有微小差异,这些差异可被用于指纹识别设备 [ACCELPRINT] [MOBILESENSORS]

4.1.5. 用户识别

传感器读数能用于识别用户,例如通过分析智能手机或可穿戴设备运动传感器的数据推断个人步态。

4.2. 缓解策略

本节为非规范性内容。

本节简要介绍一些在规范性章节中指定的缓解策略。

4.2.1. 安全上下文

传感器读数被《安全上下文》规范 [POWERFUL-FEATURES] 明确列为网络攻击者的高价值目标。 因此本规范 以及扩展规范定义的所有接口均只在安全上下文中使用。

4.2.2. 权限策略

为避免向用户不熟悉的上下文泄露传感器读数传感器读数 仅对允许使用相应策略控制特性传感器类型的文档可用。详见 [PERMISSIONS-POLICY]

4.2.3. 聚焦区域

传感器读数仅对活跃文档可用, 前提是其通过了焦点与来源检查

此举旨在缓解对可导航对象包含有获得焦点元素的扒窃攻击的风险, 例如用户在 iframe 内进行游戏内购买,启用第三方支付服务时。

4.2.4. 可见性状态

传感器读数仅对活跃文档可见, 且其可见性状态为 "visible"。

4.2.5. Permissions API

访问传感器读数 受到 Permissions API [PERMISSIONS] 控制。

4.3. 针对具体情况应用缓解策略

每种传感器类型都需单独评估, 考虑其启用的用例与威胁情况。 下列缓解策略适用于部分传感器, 但有时也会妨碍或完全阻止某些用例。

注:这些缓解措施可持续应用,也可临时启用, 如用户执行特定操作时、 或调用其他可能提升威胁等级的 API 时等。

4.3.1. 限制最大采样频率

用户代理及扩展规范可通过定义传感器类型最大采样频率, 来缓解部分威胁。

上限选择取决于传感器类型、 所要防范的威胁、 以及攻击者的预期能力等因素。

最大采样频率的限制将妨碍 依赖低延迟或高数据密度的用例。

4.3.2. 彻底停止传感器

这当然是最后的手段, 但在短暂场景下极其有效, 如当用户在不同域名([rfc6454]) 或不同应用中输入凭据时,可阻止密码扒取尝试。

4.3.3. 限制推送读数数量

限制最大采样频率相比, 另一做法是将传给 Web 开发者的传感器读数数量限制, 无论采样频率为何。 这样一来,具备低延迟需求的用例 可以提升采样频率而不会获得更多数据。

丢弃中间结果会影响某些用例, 比如依赖特定滤波的数据处理。

4.3.4. 降低精度

降低传感器读数读数时间戳的精度也有助于缓解威胁, 用户代理不应输出过于详细的传感器数据。

具体传感器实现可定义阈值检查算法,当新读数与最新读数差异不大时丢弃。

具体传感器实现可定义读数量化算法,减少来自设备传感器传感器读数精度。

注:这两种缓解措施通常可互补。如果仅执行阈值检查算法,则读数依然过于精细;若仅量化读数,则可能暴露真实精度。

注:传感器读数 的进一步操作、或据时间戳计算的时间差,将进一步放大误差, 因此此缓解措施会影响某些用例。

注:虽然给传感器读数加入随机偏差亦可达到相似效果, 但实际并不可取,因为很容易被滤波掉噪声。

4.3.5. 让用户知晓 API 的使用

用户代理可选择向用户告知 当前及历史 API 使用情况。

注:这并不意味着要记录实际的传感器读数,否则会引发其它隐私问题。

5. 概念

5.1. 传感器

设备传感器 指的是设备底层的物理传感器实例。

设备传感器 用于测量物理量,并提供相应的 传感器读数,它是环境信息的来源。

每个 传感器读数 都由 设备传感器 在时间点 tn 测得的物理量的值组成,该时间点被称为 读数时间戳

如果 设备传感器 用于空间测量(如加速度、角速度),则必须在 本地坐标系 中进行解析,此坐标系代表 设备传感器传感器读数的参考系。 能够提供此类 传感器读数设备传感器 称为 空间型传感器

空间型传感器 根据其可同时测量的正交轴的数量,可以是单轴、双轴或三轴传感器。

标量物理量(如温度)不需要 本地坐标系 来解析。

移动设备中常用的 本地坐标系 通常是直角坐标系,其相对设备屏幕定义,使得 X、Y 轴平行于屏幕,Z 轴垂直于屏幕表面。

平台传感器 指的是平台接口,用户代理通过它们获取针对单一 传感器类型、 来源自一个或多个 设备传感器传感器读数

平台传感器 可以由底层平台(如本地传感器框架)定义,也可以由用户代理定义,如果其可直接访问 设备传感器

从实现角度看,平台传感器 可以被视为相应 设备传感器 的软件代理。如果底层平台支持,可以同时有多个 平台传感器 与同一 设备传感器 交互。

在简单情况下,平台传感器仅对应一个 设备传感器, 如果所提供的传感器读数是通过软件进行 传感器融合 得到的,那么 平台传感器 对应于参与 传感器融合 的一组 设备传感器

传感器读数 和实际被测物理量之间的偏差可通过 校准 消除,该过程可发生在制造阶段,有些传感器还需动态校准以补偿未知误差。

注: 通过 传感器融合 创建的 平台传感器 有时也称为虚拟或合成传感器,但规范对此并未做出实际区分。

5.2. 传感器类型

不同的传感器类型 用于测量不同的物理量,比如温度、气压、心率或亮度。

在本规范中,我们区分 高层低层 传感器类型

以实现方式为特征的 传感器类型 被称为 低层 传感器。例如,陀螺仪就是一种 低层 传感器类型

按其 读数 命名的传感器, 无论实现方式如何, 称为 高层 传感器。 例如,地理位置传感器用于提供用户位置的信息,但其数据来源是刻意不透明的 (数据可能来源于 GPS 芯片、蜂窝网络定位、WiFi 等,或组合), 并依赖于实现相关的启发式方法。高层传感器往往是对 低层传感器的算法处理结果——例如计步器只需陀螺仪数据即可实现——或者通过传感器融合得到。

需要指出的是,高层低层传感器类型的划分是一种约定,界限常常模糊。 例如,压力计用于测量气压,多数情况下被归为低层,尽管其读数来自于压电式或热敏电阻等多组件的传感器融合,而对这些底层组件的暴露并无实际意义。 高度计一般也如此,但如果高度计数据既可来自压力计,也可来自 GPS,则这类“泛指”高度计显然应归入高层传感器类型

鉴于这种界限的模糊性,扩展规范(见 §10 可扩展性)鼓励为各自关注的传感器类型单独定义高层低层传感器。

不同传感器类型传感器读数可通过一种称为 传感器融合 的过程进行结合。 这样能产生更高层或更精确的数据(通常以牺牲延迟为代价)。 例如,三轴磁力计的读数常需与加速度计的读数结合,才能得到正确的方位。

智能传感器和传感器集线器有内置的计算能力,可在硬件层实施 校准传感器融合,降低 CPU 负担与电量消耗。

若无法在硬件层实施 传感器融合,或需特定的融合算法时,也可在软件层实现 传感器融合

5.3. 默认传感器

通用传感器 API 设计旨在让最常见用例实现简单,同时支持更复杂的场景。

目前大多数设备并不含有多于一个可提供同类型传感器读数设备传感器。需要相同类型 设备传感器 集合的用例,比 较少,通常限于特定 传感器类型,如 2 合 1 笔记本中的多个加速度计。

因此,API 让开发者能直接实例化对应 Sensor 子类,便捷地访问设备对于每种类型的默认(通常也是唯一的)传感器

确实,如果没有针对给定类型的 传感器 的确切标识信息,则由用户代理选定 默认传感器

若底层平台可用于查找 默认传感器,用户代理须选用平台指定的传感器,否则自行确定设备上哪一个 传感器 作为 默认传感器

监听默认加速度计变化:
let sensor = new Accelerometer({ frequency: 30 });

sensor.onreading = () => { ... }
sensor.start();

注: 若某些扩展规范下定义 默认传感器 并无意义,则可不做定义。例如,地理位置 传感器类型 不需要默认 传感器,因为其接口实现可用多个后端。

若一个设备中存在多个对应同一类型的 设备传感器, 则扩展规范须定义区分各自身份的方法。

例如:检测左后轮胎的压力:
var sensor = new DirectTirePressureSensor({ position: "rear", side: "left" });
sensor.onreading = _ => console.log(sensor.pressure);
sensor.start();

5.4. 采样频率与上报频率

本规范中,平台传感器采样频率平台传感器 从底层 设备传感器 采集 传感器读数 的频率。具体获取方式为 实现自定义

平台传感器采样频率 未必等于 设备传感器 实际采样速率,后者对本规范来说是不可见的。

注: 系统层面的传感器读数API及底层硬件接口,可能基于轮询或事件。对于轮询型 设备传感器平台传感器采样频率 是请求新读数的速率,而事件型 设备传感器则由 平台传感器向系统申请采样频率,并按指定频率(或更低)生成事件。若读数未变化,可能不会生成事件。

设备传感器 可为其可接受的 平台传感器 采样频率范围提供 最小采样频率最大采样频率平台传感器采样频率 不得低于 设备传感器最小采样频率,也不得高于其 最大采样频率

平台传感器采样频率 依据其关联 集合中的激活传感器对象 [[frequency]] 值决定。具体逻辑为 实现自定义,但值必须落入 平台传感器传感器类型 最小和最大采样频率,以及 设备传感器最小和最大采样频率的范围内。

注: 例如,用户代理可取一组 [[frequency]] 的最小公倍数(Least Common Denominator, LCD)作为采样频率,并受限于底层平台给定的采样频率范围。

上报频率指具体 Sensor 对象上 "reading" 事件的触发频率。

Sensor 对象获取新 读数 的速率不可能高于用户代理自平台所获得的速率,因此上报频率不会超过平台传感器采样频率,后者同样不超过设备传感器(如有定义)的最大采样频率

上报频率Sensor对象的[[frequency]]属性的值有以下差异情形:

5.5. 暴露传感器读数的条件

仅当以下所有条件成立时,用户代理 可以向 Document document 暴露传感器读数

注: 除上述条件外,还需要注意,各 Sensor 子类会在构造器中调用 检查传感器策略控制特性 操作,且 §7.1.7 Sensor.start() 会调用 请求传感器访问。这些检查共同组成了 §4.2 缓解策略 中描述的防护措施。

注: 为释放硬件资源,用户代理可请求底层 平台传感器 在不允许暴露读数之前暂停上报。

6. 模型

6.1. 传感器类型

一个传感器类型必须具有关联的数据如下:

最小采样频率不得大于最大采样频率

一个传感器类型还可以有如下关联数据:

6.2. 传感器

当前浏览上下文平台传感器应具有以下内容:

每当为某平台传感器得到新的传感器读数且 用户代理可以向当前可导航对象活动文档暴露传感器读数时, 用户代理应调用更新最新读数,传入该平台传感器传感器读数为参数。

最新读数映射 包含一个条目,其为 “timestamp”,其为高精度时间戳,表示以毫秒为单位的读数时间戳, 由 不安全当前时间估算获得。

最新读数["timestamp"] 初始值为null,除非最新读数映射缓存了之前的读数

该映射的其它条目则保存了 平台传感器所测量各个量的数值。 这些条目的必须与属性标识符相匹配, 这些属性由传感器类型关联的扩展传感器接口定义。 当属性的getter方法被调用时, 可以通过调用从最新读数获取值, 传递实现扩展传感器接口的对象及其属性标识符作为参数即可获得其值。

所有最新读数条目 初始都为null。

获取平台传感器的采样边界,给定一个平台传感器platformSensor
  1. minimumFrequency等于platformSensor传感器类型最小采样频率

  2. 如果platformSensor所连接的设备传感器最小采样频率,则将minimumFrequency设置为两者中的最大值。

  3. maximumFrequency等于platformSensor传感器类型最大采样频率

  4. 如果platformSensor所连接的设备传感器最大采样频率,则将maximumFrequency设置为两者中的最小值。

  5. 返回元组(minimumFrequency, maximumFrequency)。

此示例展示了上述模型的一种可能实现方式。

在下图中,多个来自两个不同浏览上下文激活的 Sensor 对象与同一个设备传感器交互。

Generic Sensor Model

"idle" 状态下的Sensor 对象不属于平台传感器激活的传感器对象,因此不会与设备传感器交互。

本例中,每个平台传感器实例对应一个浏览上下文

最新读数映射在同一上下文下由多个Sensor对象共享,并以对应采样频率的速率进行更新。

7. API

7.1. Sensor 接口

[SecureContext, Exposed=(DedicatedWorker, Window)]
interface Sensor : EventTarget {
  readonly attribute boolean activated;
  readonly attribute boolean hasReading;
  readonly attribute DOMHighResTimeStamp? timestamp;
  undefined start();
  undefined stop();
  attribute EventHandler onreading;
  attribute EventHandler onactivate;
  attribute EventHandler onerror;
};

dictionary SensorOptions {
  double frequency;
};

Sensor对象有一个关联的平台传感器

具体的 Sensor 对象还关联一个传感器类型,即拥有将其接口包含在其扩展传感器接口集合中的传感器类型

任务源为本规范中提到的任务传感器任务源

下面示例中,首先检查用户代理是否有权限访问传感器读数,然后构造加速度计传感器并添加事件监听器,分别监听事件平台传感器激活、错误情况、以及新读数到来的通知。该示例测量并记录设备所产生的最大合加速度。

相关事件处理器事件类型以及 Sensor 接口对应的事件处理器属性定义请参见事件处理器章节。

navigator.permissions.query({ name: 'accelerometer' }).then(result => {
    if (result.state === 'denied') {
        console.log('Permission to use accelerometer sensor is denied.');
        return;
    }

    let acl = new Accelerometer({frequency: 30});
    let max_magnitude = 0;
    acl.addEventListener('activate', () => console.log('Ready to measure.'));
    acl.addEventListener('error', error => console.log(`Error: ${error.name}`));
    acl.addEventListener('reading', () => {
        let magnitude = Math.hypot(acl.x, acl.y, acl.z);
        if (magnitude > max_magnitude) {
            max_magnitude = magnitude;
            console.log(`Max magnitude: ${max_magnitude} m/s2`);
        }
    });
    acl.start();
});

7.1.1. 传感器生命周期

Sensor lifecycle idle idle activating activating idle->activating start() start() activating->idle onerror activated activated activating->activated activated->idle stop() / onerror start start->idle construct

注意:上图中的节点代表Sensor 对象的状态,切勿与底层平台传感器设备传感器的可能状态混淆。

7.1.2. 传感器垃圾回收

Sensor对象,其[[state]] 为"activating"时,只要注册了“activated”、“reading”或“error”事件监听器,则不得被垃圾回收。

Sensor对象,其[[state]] 为"activated"时,只要注册了“reading”或“error”事件监听器,则不得被垃圾回收。

Sensor 对象,其[[state]] 为"activated"或"activating"被垃圾回收时,用户代理必须用此对象作为参数调用停用传感器对象

7.1.3. 传感器内部插槽

Sensor 的实例创建时包含以下表格里描述的内部插槽:

内部插槽 描述(非规范性)
[[state]] Sensor 对象的当前状态,可为"idle"、"activating"或"activated"之一。 初始为"idle"。
[[frequency]] 用于计算关联平台传感器采样频率的Hz(双精度浮点数), 并作为本Sensor 对象的报告频率上限。初始值为null。
[[lastEventFiredAt]] 最近一次为观察者发出的传感器读数的高精度时间戳, 单位为自时间原点起的毫秒数。 初始为null。
[[pendingReadingNotification]] 布尔值,表示新的传感器读数上报后, 是否需要通知观察者。初始为false。

7.1.4. Sensor.activated

activated getter 步骤如下:

  1. 如果 this.[[state]] 为"activated", 返回true。

  2. 否则,返回false。

7.1.5. Sensor.hasReading

hasReading getter 步骤如下:

  1. timestamp为调用从最新读数获取值, 以this和"timestamp"为参数的返回结果。

  2. 如果timestamp不为null,返回true。

  3. 否则,返回false。

7.1.6. Sensor.timestamp

timestamp getter 步骤如下:
  1. globalthis相关全局对象

  2. unsafeTimestamp为调用从最新读数获取值, 以this和"timestamp"为参数的结果。

  3. unsafeTimestamp为null,返回null。

  4. 否则,返回相对高精度时间,参数为unsafeTimestampglobal

7.1.7. Sensor.start()

start() 方法步骤如下:

  1. this.[[state]] 已为"activating"或"activated",则直接返回。

  2. this.[[state]] 设为"activating"。

  3. 并行运行以下子步骤:

    1. permissionState为调用请求传感器访问,以this为参数。

    2. permissionState为"denied",则:

      1. e创建一个"NotAllowedError" DOMException

      2. 排入任务队列,调用通知错误,以thise作为参数

      3. 返回。

    3. connected为调用连接传感器,参数为 thisthis相关全局对象

    4. connected为false,则

      1. e创建一个"NotReadableError" DOMException

      2. 排入任务队列,调用通知错误,以thise作为参数

      3. 返回。

    5. 调用激活传感器对象, 以this为参数。

7.1.8. Sensor.stop()

stop() 方法如下:

  1. this.[[state]] 为"idle",则返回。

  2. this.[[state]] 设为"idle"。

  3. 并行运行以下子步骤:

    1. 调用停用传感器对象,以this为参数。

7.1.9. Sensor.onreading

onreadingEventHandler ,用于通知有新的读数可用。

7.1.10. Sensor.onactivate

onactivateEventHandler ,当this.[[state]] 从"activating"转变为"activated"时被调用。

7.1.11. Sensor.onerror

onerrorEventHandler ,每当抽象操作或IDL操作发生无法同步处理的错误时调用。

7.1.12. 事件处理器

下面列出了对象实现Sensor接口时必须支持的事件处理器(及其对应事件类型):

事件处理器 事件类型
onreading reading
onactivate activate
onerror error

7.2. SensorErrorEvent 接口

[SecureContext, Exposed=(DedicatedWorker, Window)]
interface SensorErrorEvent : Event {
  constructor(DOMString type, SensorErrorEventInit errorEventInitDict);
  readonly attribute DOMException error;
};

dictionary SensorErrorEventInit : EventInit {
  required DOMException error;
};

SensorErrorEvent 实例的构造步骤遵循 Event构造器步骤。

error 属性必须返回初始化时设置的值。它表示传递给 SensorErrorEventInitDOMException 对象。

8. 抽象操作

8.1. 初始化传感器对象

输入

sensor_instance,一个 Sensor 对象。

options,一个 SensorOptions 字典实例。

输出

  1. options 的每一项 keyvalue 执行如下操作

    1. 如果对应的支持的传感器选项 不包含 key

      1. 抛出 "NotSupportedError" DOMException

  2. 如果 options["frequency"] 存在,则

    1. sensor_instance.[[frequency]] 设置为 options["frequency"]。

    注意: 不保证能够满足请求的 options["frequency"]。 有关可能适用的约束可参见 § 5.4 采样频率与报告频率

8.2. 检查传感器策略控制特性

输入

sensor_type,一个传感器类型

输出

仅当所有关联的传感器功能名均被允许使用时返回 true,否则返回 false。

  1. feature_namessensor_type 关联的 传感器功能名

  2. 对于 feature_names 中的每个 feature_name

    1. 如果活动文档未被允许使用名为 feature_name策略控制特性,则:

      1. 返回 false。

  3. 返回 true。

8.3. 连接传感器

输入

sensor,一个 Sensor 对象。

global,一个 全局对象

输出

sensor 与某个平台传感器关联则为 true,否则为 false。

  1. platformSensor 为 null。

  2. typesensor 关联的传感器类型

  3. virtualSensorTypesensor 关联的虚拟传感器类型,若未设置则为 null。

  4. topLevelTraversableglobalnavigable顶层可遍历对象

  5. 如果 virtualSensorType 不为 null 且 topLevelTraversable虚拟传感器映射 包含 virtualSensorType

    1. virtualSensortopLevelTraversable虚拟传感器映射[virtualSensorType]。

    2. 如果 virtualSensor可供读数标志为 true,则将 platformSensor 设置为 与 virtualSensor 对应的平台传感器

      注意: 如果可供读数标志为 false,platformSensor 仍为 null, 本算法将返回 false。

  6. 否则:

    1. 如果设备有唯一设备传感器能为 type 提供读数,则

      1. platformSensor 设为与此设备传感器对应的平台传感器

    2. 如果设备有多个设备传感器能为 type 提供读数,则

      1. 如果 type 有关联的默认传感器,则

        1. platformSensor 设为与此默认设备传感器对应的平台传感器

  7. 如果 platformSensor 为 null,返回 false。

  8. bounds 为调用 获取平台传感器的采样边界 (platformSensor) 得到的结果。

  9. 如果 sensor.[[frequency]] 为 null,则将其设为根据 type 实现定义的值。

  10. 如果 sensor.[[frequency]] 小于 bounds[0],则设为 bounds[0]。

  11. 如果 sensor.[[frequency]] 大于 bounds[1],则设为 bounds[1]。

  12. 返回 true。

8.4. 激活传感器对象

输入

sensor_instance,一个 Sensor 对象。

输出

  1. sensor 为与 sensor_instance 关联的 平台传感器

  2. sensor_instance 添加到 sensor激活的传感器对象集合中。

  3. 调用 设置传感器配置,以 sensor 为参数。

  4. 排入任务队列,调用 通知激活状态,参数为 sensor_instance

8.5. 停用传感器对象

输入

sensor_instance,一个 Sensor 对象。

输出

  1. 从与 sensor_instance 相关的任务队列以及 传感器任务源 中移除所有与 sensor_instance 有关的任务

  2. sensor 为与 sensor_instance 关联的 平台传感器

  3. sensor激活的传感器对象集合包含 sensor_instance,则:

    1. sensor_instancesensor激活的传感器对象集合中移除。

    2. 调用 设置传感器配置,以 sensor 为参数。

    3. sensor_instance.[[pendingReadingNotification]] 设为 false。

    4. sensor_instance.[[lastEventFiredAt]] 设为 null。

8.6. 通用传感器权限撤销算法

输入

permissionName,一个强大功能名称

输出

  1. 对于当前领域内的每个 Sensor 实例 sensor

    1. 如果 sensor.[[state]] 为 "idle",则继续

    2. 如果 sensor传感器类型传感器权限名 包含 permissionName

      1. 调用 停用传感器对象,参数为 sensor

      2. exception创建的 "NotAllowedError" DOMException

      3. 排入任务队列,调用 通知错误,参数为 sensorexception

8.7. 设置传感器配置

输入

platformSensor,一个 平台传感器

输出

  1. 如果 platformSensor激活的传感器对象集合为空

    1. platformSensor采样频率设为 null。

    2. platformSensor最新读数每一项 keyvalue 执行:

      1. platformSensor最新读数[key] 设为 null。

    3. 以实现定义的方式更新 传感器读数 获取逻辑,使其从 platformSensor 不再提供 读数

    4. 返回。

  2. platformSensor采样频率设为其激活的传感器对象集合 中各项 [[frequency]] 值实现定义的方式计算出。

  3. bounds 为调用 获取平台传感器的采样边界 (platformSensor) 得到的结果。

  4. 断言platformSensor采样频率 不小于 bounds[0] 且不大于 bounds[1]。

8.8. 更新最新读数

输入

sensor,一个平台传感器

reading,一个传感器读数

输出

  1. typesensor 关联的 传感器类型

  2. type 定义了 阈值检查算法

    1. result 为调用 type阈值检查算法, 参数为 readingsensor最新读数

    2. result 为 false,则中止后续步骤。

  3. reading["timestamp"] 存在

    1. reading["timestamp"] 转换为同一个单调时钟 使用的不安全当前时间(以实现定义的方式),时间原点共享此时钟。

      注意: 这一步是为了确保原本属于不同时间原点的时间戳, 能转为可在此单调时钟下参与计算的值,参考 [HR-TIME]

  4. 否则,设定 reading["timestamp"] 为 不安全共享当前时间

    注意: 无论如何,不安全当前时间 都不会向脚本暴露。 Sensor.timestamp 始终返回与时间原点相关的粗化时刻。

  5. 最新读数中的每一项 keyvalue,执行:

    1. 最新读数[key] 设为 reading 中的对应值。

  6. activated_sensorssensor 关联的激活的传感器对象集合。

  7. 并行运行以下子步骤:

    1. 对于 activated_sensors 中的每个 s

      1. 调用 报告最新读数已更新,参数为 s

8.9. 报告最新读数已更新

输入

sensor_instance,一个 Sensor 对象。

输出

  1. sensor_instance.[[pendingReadingNotification]] 为 true,

    1. 返回。

  2. sensor_instance.[[pendingReadingNotification]] 设为 true。

  3. lastReportedTimestampsensor_instance.[[lastEventFiredAt]] 的值。

  4. lastReportedTimestamp 未设值

    1. 排入任务队列,调用 通知新读数,参数为 sensor_instance

    2. 返回。

  5. 断言sensor_instance.[[frequency]] 不为 null。

  6. 断言sensor_instance.[[frequency]] 大于 0。

  7. reportingInterval = 1 / sensor_instance.[[frequency]]

  8. timestampDelta = 最新读数["timestamp"] − lastReportedTimestamp

  9. timestampDelta 大于或等于 reportingInterval

    1. 排入任务队列,调用 通知新读数,参数为 sensor_instance

    2. 返回。

  10. deferUpdateTime = reportingIntervaltimestampDelta

  11. 循环事件循环,持续时间为 deferUpdateTime

  12. sensor_instance.[[pendingReadingNotification]] 仍为 true,

    1. 排入任务队列,调用 通知新读数,参数为 sensor_instance

8.10. 通知新读数

输入

sensor_instance,一个 Sensor 对象。

输出

  1. sensor_instance.[[pendingReadingNotification]] 设为 false。

  2. sensor_instance.[[lastEventFiredAt]] 设为 最新读数["timestamp"]。

  3. 触发一个名为 "reading" 的事件,目标为 sensor_instance

8.11. 通知激活状态

输入

sensor_instance,一个 Sensor 对象。

输出

  1. sensor_instance.[[state]] 设为 "activated"。

  2. 触发一个名为 "activate" 的事件,目标为 sensor_instance

  3. sensor 是与 sensor_instance 关联的 平台传感器

  4. 如果 sensor最新读数["timestamp"] 不为 null,

    1. 排入任务队列,运行 通知新读数,参数为 sensor_instance

8.12. 通知错误

输入

sensor_instance,一个 Sensor 对象。

error,一个 DOMException

输出

  1. sensor_instance.[[state]] 设为 "idle"。

  2. 触发一个名为 "error" 的事件,目标为 sensor_instance,事件类型为 SensorErrorEvent 并将其 error 属性初始化为 error

8.13. 从最新读数获取值

输入

sensor_instance,一个 Sensor 对象。

key,一个表示值名称的字符串。

输出

一个传感器读数值或 null。

  1. sensor_instance.[[state]] 为 "activated",

    1. readingssensor_instance 关联的 平台传感器最新读数

    2. typesensor_instance 关联的 传感器类型

    3. type 定义了读数量化算法,则:

      1. readings 设置为调用 type读数量化算法,参数为 readings

    4. 如果扩展规范sensor_instance 定义了局部坐标系

      1. readings 的值重映射到局部坐标系(见 [COORDINATES-TRANSFORMATION])。

    5. 返回 readings[key]。

  2. 否则,返回 null。

8.14. 请求传感器访问

输入

sensor_instance,一个 Sensor 对象。

输出

一个权限状态

  1. sensor_typesensor_instance 关联的 传感器类型

  2. sensor_permissionssensor_type 关联的 传感器权限名

  3. sensor_permissions 中的每个 permission_name 执行:

    1. state请求使用权限 permission_name 的结果。

    2. 如果 state 为 "denied"

      1. 返回 "denied"。

  4. 返回 "granted"。

8.15. 焦点与源检查

输入

document,一个 Document

输出

布尔值。

  1. origindocument相关设置对象origin

  2. focusedDocumentdocument节点导航对象顶层可遍历对象当前聚焦区域DOM 锚点节点文档

  3. focusedOriginfocusedDocument相关设置对象origin

  4. originfocusedOrigin 同源则返回 true,否则返回 false。

9. 自动化

通用传感器 API 及其扩展规范给测试作者带来了挑战,因为要完整地测试这些接口需要可预测响应的物理硬件设备。 为了解决这一问题,本规范定义了一些[WEBDRIVER2] 扩展命令,允许定义和控制行为类似于设备传感器虚拟传感器。这些虚拟传感器代表具有特定属性的设备,其读数可完全由用户定义。

9.1. 虚拟传感器

虚拟传感器 以受控方式模拟设备传感器的行为。它会向零个或多个与之连接的平台传感器上报传感器读数

虚拟传感器有以下关联数据:

虚拟传感器类型 是表示某一类型传感器的字符串。

按类型虚拟传感器元数据有序映射,将虚拟传感器类型 映射到虚拟传感器元数据。其初始值为空,扩展规范应为其定义一个或多个映射项,对应其定义的传感器类型。

虚拟传感器元数据 是一个结构体,其项目如下:

每个顶层可遍历对象都拥有一个虚拟传感器映射, 其为有序映射 ,将虚拟传感器类型映射到 虚拟传感器

注意: 虚拟传感器映射 结构体 保存了同一类型所有虚拟传感器共有的数据。虚拟传感器自身的数据在每次创建和使用时都可变化。

注意: 虚拟传感器映射绑定于顶层可遍历对象,而非任何导航对象, 因为在同一顶层可遍历对象下的所有平台传感器 (同一导航对象内的传感器类型)都应连接至同一个虚拟传感器。 这样更贴近现实世界的情况,即同一个硬件(或融合)传感器会为不同导航对象提供读数。

注意: 该机制还便于通过web-platform-tests测试本规范, 因为所有 WebDriver 通信皆由承载测试工具的帧发出

9.2. 扩展命令

9.2.1. 创建虚拟传感器

HTTP 方法 URI 模板
POST /session/{session id}/sensor

扩展命令用于创建某种传感器类型的新虚拟传感器。对于同一传感器类型Sensor 实例,调用 start() 时会让该 虚拟传感器 作为其底层 设备传感器,直到执行 § 9.2.4 删除虚拟传感器

注意:扩展命令 的工作方式允许同类型的 Sensor 实例 共存且可拥有不同的 设备传感器。在调用本扩展命令之前,一个 Sensor sensor 可能已经被创建且连接到真实硬件传感器。它会继续从硬件传感器获取读数,只有在再次调用 connect to sensor 时, 才会切换到从虚拟传感器接收读数。

该算法所用 parameters 参数的属性
参数名 值类型 是否必需
"type" String
"connected" Boolean
"maxSamplingFrequency" Number
"minSamplingFrequency" Number
远端步骤如下:
  1. virtualSensorType 为调用获取属性 "type" 从 parameters 获得的值。

  2. 如果 virtualSensorType 不是 String, 则返回 错误,WebDriver 错误码为 invalid argument

  3. 如果 按类型虚拟传感器元数据包含 virtualSensorType,则返回 错误,WebDriver 错误码为 invalid argument

  4. topLevelVirtualSensorMapping当前浏览上下文顶层可遍历对象虚拟传感器映射

  5. 如果 topLevelVirtualSensorMapping 包含 virtualSensorType,则返回错误,WebDriver 错误码为 invalid argument

  6. connected 为调用带默认值获取属性,参数 "connected" 和 true,作用于 parameters

  7. maxSamplingFrequency 为调用带默认值获取属性,参数为 "maxSamplingFrequency" 和实现定义的值,作用于 parameters

  8. 如果 maxSamplingFrequency 不是 Number 或值为 NaN、+∞ 或 −∞,则返回 错误,WebDriver 错误码为 invalid argument

  9. minSamplingFrequency 为调用带默认值获取属性,参数为 "minSamplingFrequency" 和实现定义的值,作用于 parameters

  10. 如果 minSamplingFrequency 不是 Number 或值为 NaN、+∞ 或 −∞,则返回 错误,WebDriver 错误码为 invalid argument

  11. 如果 minSamplingFrequency 大于 maxSamplingFrequency,则返回 错误,WebDriver 错误码为 invalid argument

  12. 新建 virtualSensor,类型为虚拟传感器

  13. virtualSensor可供读数标志设置为 connected

  14. virtualSensor最小采样频率设置为minSamplingFrequency

  15. virtualSensor最大采样频率设置为maxSamplingFrequency

  16. virtualSensor 赋值给 topLevelVirtualSensorMapping[virtualSensorType]。

  17. 返回 成功,数据为 null

若要在 会话ID为23的 当前浏览上下文中创建一个“ambient-light”类型虚拟传感器, 本地端需要向/session/23/sensor 发送 POST,内容如下:
{
  "type": "ambient-light",
  "maxSamplingFrequency": 60,
  "minSamplingFrequency": 5
}

注意:在同一个传感器类型下, 顶层可遍历对象只能创建一个虚拟传感器,否则将返回invalid argument WebDriver 错误码。

9.2.2. 获取虚拟传感器信息

HTTP 方法 URI 模板
GET /session/{session id}/sensor/{type}

扩展命令用于获取通过§ 9.2.1 创建虚拟传感器创建的 某个虚拟传感器的信息。

成功时,success返回的数据对象类型为 Object, 属性如下:

属性名 值类型 描述(非规范性)
"requestedSamplingFrequency" Number 虚拟传感器请求的采样频率

注意:有关采样频率的更多约束和请求的采样频率为何是实现定义的值的说明, 请见 § 5.4 采样频率与报告频率§ 9.1 虚拟传感器。一般情况下,只能保证该值处于虚拟传感器最小采样频率最大采样频率之间。

远端步骤如下:
  1. virtualSensorTypetype url 变量的值。

  2. topLevelVirtualSensorMapping当前浏览上下文顶层可遍历对象虚拟传感器映射

  3. 如果 topLevelVirtualSensorMapping包含 virtualSensorType,则返回错误,WebDriver 错误码为 invalid argument

  4. virtualSensortopLevelVirtualSensorMapping[virtualSensorType]。

  5. 新建 info,类型为 Object

  6. 调用设置属性,在 info 上设 "requestedSamplingFrequency" 属性值为 virtualSensor请求的采样频率

  7. 返回 success,数据为 info

9.2.3. 更新虚拟传感器读数

HTTP 方法 URI 模板
POST /session/{session id}/sensor/{type}

扩展命令用于向平台传感器提供新的传感器读数

注意: 虚拟传感器的行为类似于设备传感器,所以这里产生的传感器读数还需经过平台传感器的处理,例如可能会因传感器类型阈值检查算法是否可暴露传感器读数的结果而被丢弃。

该算法所用 parameters 参数的属性
参数名 值类型 是否必需
"reading" Object
远端步骤如下:
  1. reading 为调用获取属性 "reading" 从 parameters 获得的值。

  2. 如果 reading 不是 Object 类型,则返回 错误,WebDriver 错误码为 invalid argument

  3. virtualSensorTypetype url 变量的值。

  4. 如果按类型虚拟传感器元数据包含virtualSensorType,则返回错误,WebDriver 错误码为invalid argument

  5. metadata按类型虚拟传感器元数据[virtualSensorType]。

  6. topLevelVirtualSensorMapping当前浏览上下文顶层可遍历对象虚拟传感器映射

  7. 如果 topLevelVirtualSensorMapping包含 virtualSensorType,则返回错误,WebDriver 错误码为 invalid argument

  8. virtualSensortopLevelVirtualSensorMapping[virtualSensorType]。

  9. parsedReading 为调用 metadata读数解析算法,以 reading 为参数的结果。

  10. parsedReadingundefined,则返回 错误,WebDriver 错误码为 invalid argument

  11. 以实现定义的方式,让 parsedReading 可被连接到 virtualSensor平台传感器获取。

  12. 返回 success,数据为 null

9.2.3.1. 解析读数的算法

本规范定义了一些可供扩展规范在为虚拟传感器元数据 (用于按类型虚拟传感器元数据)定义时使用的算法。

9.2.3.1.1. 解析单值数字读数
输入

parameters,一个 JSON Object

valueName,一个字符串

输出

一个传感器读数undefined

  1. value 为调用获取属性,从 parametersvalueName 获得的结果。

  2. 如果 value 不是 Number 或其值是 NaN、+∞ 或 −∞,返回 undefined

  3. reading为新建的传感器读数

  4. 设置 reading[valueName] = value

  5. 返回 reading

9.2.3.1.2. 解析 XYZ 读数
输入

parameters,一个 JSON Object

输出

一个传感器读数undefined

  1. x 为调用获取属性,从parameters取"x"的结果。

  2. 如果 x 不是 Number 或其值为 NaN、+∞ 或 −∞,则返回 undefined

  3. y 为调用获取属性,从parameters取"y"的结果。

  4. 如果 y 不是 Number 或其值为 NaN、+∞ 或 −∞,则返回 undefined

  5. z 为调用获取属性,从parameters取"z"的结果。

  6. 如果 z 不是 Number 或其值为 NaN、+∞ 或 −∞,则返回 undefined

  7. reading为新建的传感器读数

  8. 设置 reading["x"] = x

  9. 设置 reading["y"] = y

  10. 设置 reading["z"] = z

  11. 返回 reading

9.2.4. 删除虚拟传感器

HTTP 方法 URI 模板
DELETE /session/{session id}/sensor/{type}

扩展命令用于删除指定类型的虚拟传感器

远端步骤如下:
  1. virtualSensorTypetype url 变量的值。

  2. 如果按类型虚拟传感器元数据包含virtualSensorType,则返回 错误,WebDriver 错误码为 invalid argument

  3. topLevelVirtualSensorMapping当前浏览上下文顶层可遍历对象虚拟传感器映射

  4. 移除 topLevelVirtualSensorMapping[virtualSensorType]。

  5. 返回 success,数据为 null

注意:当一个正在使用的设备传感器不可用(例如被物理断开, 或因非用户代理的原因停止)时,平台传感器Sensor 实例的行为未指定。实现可以选择保持现有Sensor实例不变 (它们将不会再上报新读数),停用传感器对象, 或让平台传感器上报错误, 最终触发通知错误

10. 可扩展性

本节为非规范性内容。

注意:本节及其子节使用规范性语言为扩展规范作者提供指导。对于实现者来说,本节及其子节均视为非规范性内容。

本节描述了如何扩展本规范以为不同的传感器类型指定 API。

此类扩展规范鼓励聚焦于单一传感器类型, 并按需暴露级别能力。

扩展规范 鼓励定义关联传感器读数是否需经过校准流程。

扩展规范 可以显式为对应传感器类型定义 局部坐标系,或使其在每个Sensor对象上可配置。

要获取 扩展规范的最新列表,请参阅 [GENERIC-SENSOR-USECASES][MOTION-SENSORS] 文档。

10.1. 安全与隐私

扩展规范 应当:

10.2. 命名

面向低级传感器的 Sensor 接口应以 其所关联平台传感器命名。 例如,与陀螺仪关联的接口就应命名为 Gyroscope。 面向高级传感器的 Sensor 接口 应以该平台传感器测量的物理量名加 Sensor 后缀命名。 例如,测量距离的 平台传感器 相关接口可命名为 ProximitySensor

Sensor 子类中保存传感器读数值的属性名 应以其值的完整含义命名。例如 Thermometer 接口应包含 传感器读数值的 temperature 属性(而不是 valuetemp)。 可参考 QUDT [QUDT] 的命名实践。

10.3. 单位

扩展规范 必须明确定义传感器读数的单位。

根据 TAG API 设计原则 [API-DESIGN-PRINCIPLES] ,所有时间测量均应采用毫秒为单位。 其它单位应优先按以下顺序采用(除了温度推荐用摄氏而非开尔文): 国际单位制(SI)、SI 派生单位、SI 可接受的非SI单位。参见 SI Brochure [SI]

10.4. 暴露高级与低级传感器

截至目前,暴露传感器给网页平台的各种规范 主要侧重于高级传感器 API。参见 [GEOLOCATION-API][ORIENTATION-EVENT]

之所以采用此做法,原因包括: 高级传感器:

但越来越多的场景(如虚拟现实、增强现实) 出于性能等考虑需要低级别的传感器访问能力。

提供低级别访问权 有助于 Web 应用开发者结合领域属性进行更高性能系统设计。

根据 Extensible Web Manifesto 的理念 [EXTENNNNSIBLE]扩展规范 应重点暴露低级传感器 API; 但如果有明显优势,也应暴露高级API。

10.5. 不该开启同类型多传感器的情形?

不建议为同一传感器类型 用完全相同参数创建多个Sensor实例, 否则会消耗不必要的硬件资源。

如果有多个观察者希望接收新传感器读数的通知,可以在一个 Sensor 实例上添加事件监听器, 而无需为同一传感器类型创建多个实例并使用简单的 onreading 事件处理器。

反过来,如须用不同设置(如 frequency、 精度等,由扩展规范定义的设置), 可以为同类传感器类型创建多个 Sensors 实例。

10.6. 定义要求

扩展规范必须定义 § 6.1 传感器类型中列明的所有关联数据。

本节给出扩展规范 需指明的部分关联数据详细说明。

扩展规范可为每个 传感器类型 另定义如下内容:

10.7. 自动化

为了支持用户代理自动化与应用测试,扩展规范鼓励:

距离传感器扩展规范 (见§ 10.10 示例 WebIDL)可以包含如下内容:
距离传感器 是一个传感器类型,只关联一个 扩展传感器接口 ProximitySensor。其关联的虚拟传感器类型是 "proximity"。

[...]

距离读数解析算法,给定一个 JSON Object parameters,必须调用解析单值数字读数,参数为 parameters 和 "distance"。

按类型虚拟传感器元数据 映射必须包含一个键为"proximity"的条目, 其值是一个虚拟传感器元数据,其读数解析算法距离读数解析算法

10.8. 扩展 Permission API

每个 Sensor 接口对应的 传感器类型 必须用其关联的 读数 来保护相应的 名称PermissionDescriptor。 低级 sensor 可以直接用接口名作为 名称,例如 "gyroscope" 或 "accelerometer"。 融合传感器必须请求访问用作融合源的所有传感器的权限

即使从融合数据重建 低级 传感器读数较为困难,但部分原始信息仍可能被推断。 例如,若使用绝对或地磁朝向传感器,很容易推断用户在空间中的朝向,因此这类传感器必须 请求使用 magnetometer 权限, 因为其暴露了设备与地磁场的相对位置。而相对朝向传感器则不会暴露这类信息,因此不需 请求使用 magnetometer。

PermissionDescriptor 也可用于设置最大精度或采样频率等限制。下例为加速度计权限扩展的一个可能实现:

dictionary AccelerometerPermissionDescriptor : PermissionDescriptor {
    boolean highAccuracy = false;
    boolean highFrequency = false;
};

10.9. 扩展 Permissions Policy API

每个 Sensor 接口对应的 传感器类型 (若未进行传感器融合时为1个,否则为多个) 需对应一个或多个策略控制功能,用于控制是否允许文档使用该接口。

这些功能默认允许列表'self'

注意: 默认允许列表设置为 'self', 允许同源嵌套框架使用 Sensor 接口, 但禁止第三方内容访问 传感器读数

传感器功能名 集合 必须包含所有关联策略控制功能的令牌。

低级 sensor 可以用接口名作策略控制功能令牌, 如 "gyroscope" 或 "accelerometer"。 若扩展规范 未作修改,传感器功能名等于其类型对应权限名

为第三方源有选择性地开启加速度计功能,可通过在框架元素上添加 allow 属性实现:
<iframe src="https://third-party.com" allow="accelerometer"/></iframe>
可在 HTTP 响应头中设定权限策略完全禁用传感器使用:
Permissions-Policy: accelerometer=()

融合传感器必须使用其作为融合源的传感器的 传感器功能名

允许第三方源使用加速度计、磁力计和陀螺仪功能, 以支持绝对方向传感器。
<iframe src="https://third-party.com" allow="accelerometer; magnetometer; gyroscope"/>

10.10. 示例 WebIDL

以下是本规范针对距离传感器扩展的 WebIDL 示例:

[SecureContext, Exposed=Window]
interface ProximitySensor : Sensor {
    constructor(optional ProximitySensorOptions proximitySensorOptions = {});
    readonly attribute double? distance;
};

dictionary ProximitySensorOptions : SensorOptions {
    double min;
    double max;
    ProximitySensorPosition position;
    ProximitySensorDirection direction;
};

enum ProximitySensorPosition {
    "top-left",
    "top",
    "top-right",
    "middle-left",
    "middle",
    "middle-right",
    "bottom-left",
    "bottom",
    "bottom-right"
};

enum ProximitySensorDirection {
    "front",
    "rear",
    "left",
    "right",
    "top",
    "bottom"
};

11. 致谢

首先,衷心感谢 Anssi Kostiainen 在本规范开发过程中持续地投入和支持, 还要感谢 Mikhail Pozdnyakov、Alexander Shalamov、Rijubrata Bhaumik 和 Kenneth Rohde Christiansen 在实现反馈、建议与研究上的宝贵贡献,这些都极大推动了规范的完善。

特别感谢 Rick Waldron,他推动了 Web 通用传感器 API 设计的讨论, 勾画了本规范的最初 API,并基于他在 Johnny-Five 项目的工作不断反馈实现建议, 在整个规范开发过程中持续贡献观点。

特别感谢 Boris Smus、Tim Volodine 与 Rich Tibbett 早期将传感器以一致方式暴露给 Web 所做的工作。

感谢 Anne van Kesteren 不仅面对面也通过 IRC 持续地热心协助。

感谢 Domenic Denicola 和 Jake Archibald 的帮助。

还要感谢 Frederick Hirsch 和 Dominique Hazaël-Massieux(通过 HTML5Apps 项目) 在管理和技术层面对本规范的支持。

感谢 Tab Atkins 制作 Bikeshed 并细致讲解其细节。

感谢 Lukasz Olejnik 和 Maryam Mehrnezhad 为隐私与安全相关内容贡献力量。

以下各位在 GitHub 上反复讨论、深度参与,本规范极大受益于他们的贡献: Anssi Kostiainen、 Boris Smus、 chaals、 Claes Nilsson、 Dave Raggett、 David Mark Clements、 Domenic Denicola、 Dominique Hazaël-Massieux(通过 HTML5Apps 项目)、 Francesco Iovine、 Frederick Hirsch、 gmandyam、 Jafar Husain、 Johannes Hund、 Kris Kowal、 Lukasz Olejnik、 Marcos Caceres、 Marijn Kruisselbrink、 Mark Foltz、 Mats Wichmann、 Matthew Podwysocki、 Olli Pettay、 pablochacin、 Remy Sharp、 Rich Tibbett、 Rick Waldron、 Rijubrata Bhaumik、 robman、 Sean T. McBeth、 Tab Atkins Jr.、 Virginie Galindo、 zenparsing、 以及 Zoltan Kis。

我们还要感谢 Anssi Kostiainen、 Dominique Hazaël-Massieux、 Erik Wilde、 和 Michael[tm] Smith 对编辑工作提供的建议和帮助。

一致性

文档约定

一致性要求通过描述性断言和 RFC 2119 术语组合表达。 在本规范的规范性部分中,关键字 “MUST”、“MUST NOT”、“REQUIRED”、“SHALL”、“SHALL NOT”、“SHOULD”、“SHOULD NOT”、“RECOMMENDED”、“MAY” 和 “OPTIONAL” 的解释请参考 RFC 2119。 为了可读性,这些词在本规范中并未全大写。

除非明确标为非规范性的内容、示例和注释,否则本规范的所有文本都是规范性的。[RFC2119]

本规范中的示例会以“例如”引入,或通过 class="example" 标记与规范性文本区分开,如下所示:

这是一个说明性示例。

说明性注释以“注意”开头,并通过 class="note" 标记与规范性文本区分开,例如:

注意:这是一个说明性注释。

一致性算法

以祈使句表达在算法中的一致性要求(如“去除任何前导空格字符”或“返回 false 并中止这些步骤”), 应按照在引入算法时使用的关键字(“must”、“should”、“may”等)的含义来解释。

以算法或具体步骤表述的一致性要求,可以用任何方式实现,只要最终结果等价即可。 尤其是,本规范规定的算法旨在易于理解,并不追求高性能。鼓励实现者进行优化。

索引

本规范定义的术语

参考文件定义的术语

参考文献

规范性引用

[DOM]
Anne van Kesteren. DOM 标准. 现行标准. URL: https://dom.spec.whatwg.org/
[ECMASCRIPT]
ECMAScript 语言规范. URL: https://tc39.es/ecma262/multipage/
[HR-TIME]
Yoav Weiss. 高精度时间. URL: https://w3c.github.io/hr-time/
[HTML]
Anne van Kesteren 等. HTML 标准. 现行标准. URL: https://html.spec.whatwg.org/multipage/
[INFRA]
Anne van Kesteren; Domenic Denicola. Infra 标准. 现行标准. URL: https://infra.spec.whatwg.org/
[PERMISSIONS]
Marcos Caceres; Mike Taylor. Permissions. URL: https://w3c.github.io/permissions/
[PERMISSIONS-POLICY]
Ian Clelland. Permissions Policy. URL: https://w3c.github.io/webappsec-permissions-policy/
[PERMISSIONS-REQUEST]
Requesting Permissions. cg-draft. URL: https://wicg.github.io/permissions-request/
[RFC2119]
S. Bradner. 在 RFC 中用于指示需求级别的关键字. 1997 年 3 月. 最佳当前实践. URL: https://datatracker.ietf.org/doc/html/rfc2119
[WEBDRIVER2]
Simon Stewart; David Burns. WebDriver. URL: https://w3c.github.io/webdriver/
[WEBIDL]
Edgar Chen; Timothy Gu. Web IDL 标准. 现行标准. URL: https://webidl.spec.whatwg.org/

说明性引用

[ACCELPRINT]
Dey, Sanorita, 等. AccelPrint: Accelerometers 的缺陷让智能手机可被跟踪. 2014. 信息性. URL: http://synrg.csl.illinois.edu/papers/AccelPrint_NDSS14.pdf
[API-DESIGN-PRINCIPLES]
Sangwhan Moon; Lea Verou. Web 平台设计原则. URL: https://w3ctag.github.io/design-principles/
[COORDINATES-TRANSFORMATION]
George W. Collins, II. 天体力学基础. 2004. 信息性. URL: http://ads.harvard.edu/books/1989fcm..book/Chapter2.pdf
[EXTENNNNSIBLE]
Extensible Web 宣言. 2013 年 6 月 10 日. URL: https://extensiblewebmanifesto.org/
[GENERIC-SENSOR-USECASES]
Rick Waldron, Mikhail Pozdnyakov, Alexander Shalamov. 传感器用例. 2017. 说明. URL: https://w3c.github.io/sensors/usecases
[GEOLOCATION-API]
Andrei Popescu. 地理定位 API 规范 第二版. URL: https://w3c.github.io/geolocation-api/
[GYROSPEECHRECOGNITION]
Michalevsky, Y., Boneh, D. 和 Nakibly, G.. Gyrophone: 利用陀螺仪信号识别语音. 2014. 信息性. URL: https://www.usenix.org/system/files/conference/usenixsecurity14/sec14-paper-michalevsky.pdf
[MOBILESENSORS]
Manish J. Gajjar. 移动传感器与上下文感知计算. 2017. 信息性.
[MOTION-SENSORS]
Kenneth Christiansen; Alexander Shalamov. 运动传感器解说. URL: https://w3c.github.io/motion-sensors/
[ORIENTATION-EVENT]
Reilly Grant; Raphael Kubo da Costa. DeviceOrientation 事件规范. URL: https://w3c.github.io/deviceorientation/
[POWERFUL-FEATURES]
Mike West. 安全上下文. URL: https://w3c.github.io/webappsec-secure-contexts/
[QUDT]
Ralph Hodgson 等. QUDT - 量、单位、维度及数据类型本体. 2014 年 3 月 18 日. URL: http://www.qudt.org/
[RFC6454]
A. Barth. Web 源概念. 2011 年 12 月. 标准草案. URL: https://www.rfc-editor.org/rfc/rfc6454
[SECURITY-PRIVACY-QUESTIONNAIRE]
Theresa O'Connor; Peter Snyder. 安全与隐私自查问卷. URL: https://w3ctag.github.io/security-questionnaire/
[SI]
国际单位制 SI 手册,第八版. 2014. URL: http://www.bipm.org/en/publications/si-brochure/
[STEALINGPINSVIASENSORS]
Maryam Mehrnezhad, Ehsan Toreini, Siamak F. Shahandashti, Feng Hao. 通过移动传感器窃取 PIN:实际风险与用户感知. 2017. 信息性. URL: https://rd.springer.com/article/10.1007/s10207-017-0369-x?wt_mc=Internal.Event.1.SEM.ArticleAuthorOnlineFirst

IDL 索引

[SecureContext, Exposed=(DedicatedWorker, Window)]
interface Sensor : EventTarget {
  readonly attribute boolean activated;
  readonly attribute boolean hasReading;
  readonly attribute DOMHighResTimeStamp? timestamp;
  undefined start();
  undefined stop();
  attribute EventHandler onreading;
  attribute EventHandler onactivate;
  attribute EventHandler onerror;
};

dictionary SensorOptions {
  double frequency;
};

[SecureContext, Exposed=(DedicatedWorker, Window)]
interface SensorErrorEvent : Event {
  constructor(DOMString type, SensorErrorEventInit errorEventInitDict);
  readonly attribute DOMException error;
};

dictionary SensorErrorEventInit : EventInit {
  required DOMException error;
};

MDN

Sensor/activate_event

In only one current engine.

FirefoxNoneSafariNoneChrome67+
Opera?Edge79+
Edge (Legacy)?IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?
MDN

Sensor/activated

In only one current engine.

FirefoxNoneSafariNoneChrome67+
Opera?Edge79+
Edge (Legacy)?IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?
MDN

Sensor/error_event

In only one current engine.

FirefoxNoneSafariNoneChrome67+
Opera?Edge79+
Edge (Legacy)?IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?
MDN

Sensor/hasReading

In only one current engine.

FirefoxNoneSafariNoneChrome67+
Opera?Edge79+
Edge (Legacy)?IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?
MDN

Sensor/reading_event

In only one current engine.

FirefoxNoneSafariNoneChrome67+
Opera?Edge79+
Edge (Legacy)?IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?
MDN

Sensor/start

In only one current engine.

FirefoxNoneSafariNoneChrome67+
Opera?Edge79+
Edge (Legacy)?IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?
MDN

Sensor/stop

In only one current engine.

FirefoxNoneSafariNoneChrome67+
Opera?Edge79+
Edge (Legacy)?IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?
MDN

Sensor/timestamp

In only one current engine.

FirefoxNoneSafariNoneChrome67+
Opera?Edge79+
Edge (Legacy)?IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?
MDN

Sensor

In only one current engine.

FirefoxNoneSafariNoneChrome67+
Opera?Edge79+
Edge (Legacy)?IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?
MDN

SensorErrorEvent/SensorErrorEvent

In only one current engine.

FirefoxNoneSafariNoneChrome67+
Opera?Edge79+
Edge (Legacy)?IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?
MDN

SensorErrorEvent/error

In only one current engine.

FirefoxNoneSafariNoneChrome67+
Opera?Edge79+
Edge (Legacy)?IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?
MDN

SensorErrorEvent

In only one current engine.

FirefoxNoneSafariNoneChrome67+
Opera?Edge79+
Edge (Legacy)?IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?
MDN

Headers/Permissions-Policy/accelerometer

In only one current engine.

FirefoxNoneSafariNoneChrome88+
Opera?Edge88+
Edge (Legacy)?IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?

Headers/Permissions-Policy/ambient-light-sensor

In only one current engine.

FirefoxNoneSafariNoneChrome88+
Opera?Edge88+
Edge (Legacy)?IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?

Headers/Permissions-Policy/gyroscope

In only one current engine.

FirefoxNoneSafariNoneChrome88+
Opera?Edge88+
Edge (Legacy)?IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?

Headers/Permissions-Policy/magnetometer

In only one current engine.

FirefoxNoneSafariNoneChrome88+
Opera?Edge88+
Edge (Legacy)?IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebViewNoneSamsung Internet?Opera Mobile?