计算压力等级 1

W3C 候选推荐草案

关于本文档的更多信息
本版本:
https://www.w3.org/TR/2025/CRD-compute-pressure-20250521/
最新发布版本:
https://www.w3.org/TR/compute-pressure/
最新编辑草案:
https://w3c.github.io/compute-pressure/
历史:
https://www.w3.org/standards/history/compute-pressure/
提交历史
测试套件:
https://github.com/web-platform-tests/wpt/labels/compute-pressure
实施报告:
https://wpt.fyi/results/compute-pressure
编辑:
Kenneth Rohde Christiansen (英特尔公司)
Arnaud Mandy (英特尔公司)
前编辑:
Raphael Kubo da Costa (英特尔公司)
反馈:
GitHub w3c/compute-pressure (拉取请求, 新问题, 开放问题)

摘要

计算压力 API 提供了一种让网站对目标设备 CPU 压力变化做出响应的方法,从而使网站能够在资源和优化用户体验之间进行权衡。

文档状态

本节描述了本文档在发布时的状态。当前 W3C 发布的文档列表和本技术报告的最新修订版可在 W3C 标准与草案索引(https://www.w3.org/TR/)找到。

本规范要退出 CR 阶段,规范中定义的每个功能至少需要有 2 个独立实现记录在实现报告中。

本文档由设备与传感器工作组推荐进程发布为候选推荐草案。

作为候选推荐发布并不代表 W3C 及其成员机构的认可。候选推荐草案整合了自上一版候选推荐以来工作组计划纳入后续候选推荐快照的更改。

本文件为草案,随时可能被更新、替换或废弃。将本文档作为最终成果引用是不合适的,仅可视为进行中的工作。

本文档由遵循 W3C 专利政策 的团队制定。 W3C 维护了与本工作组成果相关的 专利公开名单,该页面还包含如何公开专利的说明。任何个人如已知某专利可能包含 必要权利要求 ,须按 W3C专利政策第6节披露相关信息。

本文档受 2023年11月3日 W3C 流程文件 约束。

1. 介绍

本节为非规范性内容。

现代应用程序通常需要在充分利用系统计算资源的权衡和优势之间进行平衡,以提供现代且愉悦的用户体验。

例如,许多应用程序可以以不同复杂度渲染视频特效。这些应用旨在提供最佳用户体验,同时避免让用户设备进入高压力状态。

处理单元的利用率接近甚至经常达到100%可能会导致不佳的用户体验,因为不同任务会争夺处理时间。 这可能会导致设备变慢,输入延迟尤为明显。 此外,长时间接近100%的利用率会因为持续加速使处理单元发热,进而降频,进一步恶化用户体验。

由于热限制,许多智能手机、平板和笔记本电脑会变得烫手。笔记本和台式机的风扇噪声甚至可能扰乱谈话或用户集中注意力的能力。

在许多情况下,高压力下的设备表现为无响应,因为操作系统可能无法调度推进用户所等待任务的线程。参见 用例

2. 特性检测说明

本节为非规范性内容。

特性检测是 Web 开发中公认的最佳实践。关于这一主题的资源在网络上和线下都非常丰富,本节目的不是详细讨论,而是将其置于检测硬件相关特性的上下文下。

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

注意

3. 基本概念

本规范定义了以下概念:

3.1 处理单元

计算设备由多种不同的处理单元组成,如中央处理器 (CPU)、图形处理器 (GPU) 及许多专用处理单元。后者正在流行,如用于加速特定任务(如机器学习或计算机视觉)的单元。

3.2 压力来源

目前规范定义的有效源类型中央处理单元,即 CPU。 规范的未来版本可以引入其他源类型

PressureSource枚举表示有效源类型

注意

3.3 采样与报告速率

请求采样间隔表示期望的硬件采样间隔,单位为毫秒。

间隔与频率互为倒数,请求采样间隔也可表示为赫兹(每秒周期数)的 请求采样速率,计算方式为1000除以 请求采样间隔的值。

采样速率是指平台收集器从底层平台获取遥测数据的速率, 该速率可能与压力观察者的请求采样速率不同; 单位为赫兹(每秒周期数)。

报告速率是压力观察者运行 数据收集步骤的速率,且不会超过 采样速率

采样速率请求采样速率的区别在于当 请求采样速率超出了底层平台和 用户代理支持或允许的采样速率上下限时。

另外,本规范还将速率进行混淆,见 11.2.2 速率混淆

如果用户未请求采样速率采样速率实现定义

注意

4. 平台原语

压力源是一个抽象的、实现定义 的接口,连接到底层的硬件计数器或提供关于由PressureSource定义的源类型的遥测数据的框架。压力源 可融合其他来源的数据以获得更精确的结果。

压力源提供的遥测数据在本规范中表现为压力源样本,即一个结构体 ,包含以下

压力源有一个关联的最新样本,为一个压力源样本或 null,初始为 null。

平台收集器是一个负责从压力源获取遥测样本,转化为压力状态并提供给用户代理的抽象接口。

平台收集器有如下关联数据:

压力源提供的遥测数据格式,以及其保存在最新样本data的格式,都是实现定义的,以及平台收集器如何转化为压力状态的过程也是实现定义的。

就本规范而言,平台收集器全局对象 作用域,通过平台收集器映射实现。

为自动化目的,平台收集器必须能够连接虚拟压力源,并使用其模拟data 作为压力状态,而不是必须转化为调整后压力状态的原始平台数据。

由于采集遥测数据通常意味着轮询硬件计数器,因此这不是免费操作,没有数据观察者时不应采集。详细信息见10.5 生命周期与垃圾回收

平台收集器以特定速率采样数据。用户代理可(如可行)为隐私目的修改此速率,或忽略并融合某些读数。

5. 用户通知

建议用户代理以某种用户可见的通知形式,告知用户某个压力观察器处于活跃状态,并为用户提供阻止当前操作或直接关闭通知的方式。

6. 策略控制

Compute Pressure API 定义了一个由标记 "compute-pressure" 识别的策略控制特性。 其默认允许列表'self'

Worker(专用和共享)会遵循其所属文档设置的权限策略。

共享 Worker 往往由多个拥有文档,因为其它同源文档也可获取。 这种情况下,所有拥有文档都必须被允许使用本规范定义的策略控制特性

专用 worker 可由其它 worker 创建,此时会沿用最初拥有该 worker 的文档 (若是共享 worker,则为全部所属文档)在所有者链上的权限策略。

注意
注意

7. 内部插槽定义

每个全局对象拥有:

已注册观察者由一个观察者PressureObserver对象)组成。

用户代理具有:

已构造的PressureObserver对象拥有如下内部插槽:

针对速率混淆缓解措施,构造的PressureObserver 对象还包含以下内部插槽:

8. 压力状态

压力状态表示允许网站对计算与系统压力变化做出反应的最小有用状态集,从而实现用户体验、质量或服务的最小降级。

WebIDLenum PressureState { "nominal", "fair", "serious", "critical" };

PressureState 枚举用以下状态表示压力状态

注意

9. 影响因素

影响因素指影响当前压力状态的底层硬件和操作系统度量,可为实现定义内容。

调整后压力状态是一个压力状态,由以源类型和影响因素中的其它实现定义数据为输入的实现定义算法判定。该算法必须非确定性,以确保中断校准缓解措施有效。

影响因素变化显著的步骤如下:

  1. 如果低层硬件度量(实现定义)低于/超过了当前压力状态下为各度量定义的实现定义阈值,则返回 true。
  2. 返回 false。
注意

10. 压力观察器

Compute Pressure API 使开发者能够了解系统资源(如 CPU)的压力情况。
WebIDLcallback PressureUpdateCallback = undefined (
  sequence<PressureRecord> changes,
  PressureObserver observer
);
压力状态发生变化时,将调用此回调。

10.2 PressureObserver 对象

PressureObserver 可用于观察压力状态的变化。

WebIDL[Exposed=(DedicatedWorker,SharedWorker,Window), SecureContext]
interface PressureObserver {
  constructor(PressureUpdateCallback callback);

  Promise<undefined> observe(PressureSource source, optional PressureObserverOptions options = {});
  undefined unobserve(PressureSource source);
  undefined disconnect();
  sequence<PressureRecord> takeRecords();

  [SameObject] static readonly attribute FrozenArray<PressureSource> knownSources;
};

PressureObserver 接口表示一个 PressureObserver

10.2.1 constructor() 方法

new PressureObserver(callback) 构造器的步骤如下:

  1. this.[[Callback]] 设为 callback

10.2.2 observe() 方法

observe(source, options) 方法的步骤如下:

  1. relevantGlobalthis相关全局对象
  2. relevantGlobal所属文档集合中的每个document
    1. 如果 document 没有 被允许使用 策略控制特性 token "compute-pressure",则返回 NotAllowedError 拒绝的 promise。
    注意
  3. this.[[SampleIntervalMap]][source] 设为 optionssampleInterval
  4. promise一个新的 promise
  5. pendingPromiseTuple 为(sourcepromise)。
  6. pendingPromiseTuple 追加this.[[PendingObservePromises]]
  7. promise 进行反应
  8. 并行运行以下步骤:
    1. platformCollector 为 null。
    2. 如果 relevantGlobal平台收集器映射 包含 source
      1. platformCollector 设为 relevantGlobal平台收集器映射[source]。
    3. 否则:
      1. newCollector 为一个新的平台收集器,其关联压力源为 null。
      2. virtualPressureSource 为以 sourcerelevantGlobal 调用获取虚拟压力源的结果。
      3. 如果 virtualPressureSource 不为 null:
        1. 如果 virtualPressureSource可以提供样本为 true:
          1. newCollector关联压力源设为 virtualPressureSource
          2. newCollector 追加virtualPressureSource已连接平台收集器中。
      4. 否则:
        1. realPressureSource 为一个实现定义压力源,其提供关于 source 的遥测数据;若不存在则为 null。
        2. newCollector关联压力源设为 realPressureSource
      5. 如果 newCollector关联压力源 不为 null:
        1. platformCollector 设为 newCollector
        2. relevantGlobal平台收集器映射[source] 设为 platformCollector
    4. 如果 platformCollector 为 null,则在给定 relevantGlobal 的情况下,使用队列一个全局任务,在PressureObserver 任务源上以NotSupportedError 拒绝 promise 并中止这些步骤。
    5. 使用 sourcerelevantGlobal 调用激活数据收集
    6. 在给定 relevantGlobal 的情况下,在PressureObserver 任务源队列一个全局任务以运行以下步骤:
      1. 如果 promise 已被拒绝,则运行以下子步骤:
        1. 如果 relevantGlobal 针对 source已注册观察者列表,则以 sourcerelevantGlobal 调用停用数据收集
        2. 返回。
      2. 将一个新的已注册观察者(其观察者this追加relevantGlobal 针对 source已注册观察者列表中。
      3. 兑现 promise
  9. 返回 promise

10.2.3 unobserve() 方法

unobserve(source) 方法的步骤如下:

  1. 如果 source 不是一个支持的源类型,则抛出 "NotSupportedError"。
  2. this.[[QueuedRecords]]移除所有与 source 关联的 records
  3. 移除 this.[[SampleIntervalMap]][source]。
  4. 移除 this.[[LastRecordMap]][source]。
  5. 移除 this.[[AfterPenaltyRecordMap]][source]。
  6. this.[[PendingObservePromises]] 中的每个(promiseSourcependingPromise),若 source 等于 promiseSource,则以 AbortError 拒绝 pendingPromise
  7. relevantGlobalthis相关全局对象
  8. registeredObserverListrelevantGlobal 针对 source已注册观察者列表
  9. registeredObserverList移除任意已注册观察者,其观察者this
  10. 如果 registeredObserverList
    1. 使用 sourcerelevantGlobal 调用停用数据收集
    2. 移除 relevantGlobal平台收集器映射[source]。

10.2.4 disconnect() 方法

disconnect() 方法的步骤如下:

  1. 清空 observer.[[QueuedRecords]]
  2. 清除 this.[[SampleIntervalMap]]
  3. 清除 this.[[LastRecordMap]]
  4. 清除 this.[[AfterPenaltyRecordMap]]
  5. this.[[PendingObservePromises]] 中的每个(promiseSourcependingPromise),以AbortError 拒绝 pendingPromise
  6. relevantGlobalthis相关全局对象
  7. relevantGlobal已注册观察者列表有序映射中的每个 sourceregisteredObserverList
    1. registeredObserverList移除任意已注册观察者,其观察者this
    2. 如果 registeredObserverList
      1. 使用 sourcerelevantGlobal 调用停用数据收集
      2. 移除 relevantGlobal平台收集器映射[source]。

10.2.5 takeRecords() 方法

注意

takeRecords() 方法的步骤如下:

  1. recordsobserver.[[QueuedRecords]] 的一个克隆
  2. 清空 observer.[[QueuedRecords]]
  3. 返回 records

10.2.6 knownSources 属性

knownSources getter 的步骤如下:

  1. 以字母顺序返回用户代理支持的源类型

注意

10.3 PressureRecord 接口

WebIDL[Exposed=(DedicatedWorker,SharedWorker,Window), SecureContext]
interface PressureRecord {
  readonly attribute PressureSource source;
  readonly attribute PressureState state;
  readonly attribute DOMHighResTimeStamp time;
  [Default] object toJSON();
};

一个已构造的PressureRecord对象具有以下内部插槽:

10.3.1 source 属性

sourcegetter 步骤是返回其[[Source]]内部插槽。

10.3.2 state 属性

stategetter 步骤是返回其[[State]]内部插槽。

10.3.3 time 属性

timegetter 步骤是返回其[[Time]]内部插槽。

10.3.4 toJSON 成员

当调用 PressureRecord.toJSON 时,运行Web IDL Standard中的默认 toJSON 步骤

10.4 PressureObserverOptions 字典

WebIDLdictionary PressureObserverOptions {
  [EnforceRange] unsigned long sampleInterval = 0;
};

10.4.1 sampleInterval 成员

sampleInterval 成员表示以毫秒表示的请求采样间隔。当该值设为 0 时,系统仅会在PressureState发生变化时调用PressureUpdateCallback

注意

10.5 生命周期与垃圾回收

每个全局对象都会对其已注册观察者(每个 source 一个)所在的已注册观察者列表保持强引用。

注意

10.6 处理模型

本节概述了用户代理在实现本规范时必须执行的步骤。

10.6.1 支持算法

给定参数 observer重置观测窗口步骤如下:

运行重置观测窗口步骤,并启动一个定时器,在 observer.[[ObservationWindow]] 时间过去时重新运行这些步骤,并使用不同的随机值。

注意

要确定一个相关全局对象 relevantGlobal所属文档集合

  1. owningDocumentSet 为一个空的集合
  2. 如果 relevantGlobalWindow,则将 relevantGlobal关联文档追加owningDocumentSet
  3. 否则,对WorkerGlobalScope relevantGlobal拥有者集合中的每个 owner
    1. 如果 ownerDocument,则将 owner追加owningDocumentSet
    2. 如果 ownerWorkerGlobalScope, 则将 owningDocumentSet 设为 owningDocumentSetowner所属文档集合并集
  4. 返回 owningDocumentSet

给定参数 document文档具有隐式焦点步骤如下:

  1. 如果 document 不是完全激活的,返回 false。
  2. relevantGlobaldocument相关全局对象
  3. 活跃的画中画会话的发起方中的每个 origin
    1. 如果 relevantGlobal相关设置对象origin同源,返回 true。
  4. 如果 relevantGlobal浏览上下文正在捕获,返回 true。
  5. topLevelBCrelevantGlobal浏览上下文顶级浏览上下文
  6. 如果 topLevelBC 不具有系统焦点,返回 false。
  7. focusedDocumenttopLevelBC 当前获得焦点的区域节点文档
  8. 如果 relevantGlobal相关设置对象focusedDocument同源,返回 true。
  9. 否则,返回 false。

给定参数 observer可以接收数据步骤如下:

  1. relevantGlobalobserver相关全局对象
  2. 如果 relevantGlobalWindow 对象:
    1. 返回以 relevantGlobal关联 Document运行文档具有隐式焦点的结果。
  3. 如果 relevantGlobalWorkerGlobalScope 对象:
    1. owningDocumentsrelevantGlobal所属文档集合
    2. owningDocuments 中的每个document
      1. 如果以 document 运行文档具有隐式焦点的结果为 true, 则返回 true。
      2. 否则,继续
  4. 返回 false。
注意

通过速率测试步骤(给定参数 observersourcetimestamp)如下:
  1. 如果 observer.[[LastRecordMap]][source] 不存在,返回 true。
  2. recordobserver.[[LastRecordMap]][source]。
  3. sampleIntervalobserver.[[SampleIntervalMap]][source]。
  4. timeDeltaMilliseconds = timestamp - record.[[Time]]
  5. 如果 timeDeltaMillisecondssampleInterval, 返回 true,否则返回 false。
应当派发步骤(给定参数 observersourcestate)如下:
  1. 如果 observer.[[SampleIntervalMap]][source] > 0,返回 true。
  2. 如果 observer.[[LastRecordMap]][source] 不存在,返回 true。
  3. recordobserver.[[LastRecordMap]][source]。
  4. 如果 record.[[State]] 不等于 state,返回 true。
  5. 返回 false。
通过速率混淆测试步骤(给定参数 observersource)如下:
  1. 将 observer.[[ChangesCountMap]][source] 加 1。
  2. 返回 observer.[[ChangesCountMap]][source] ≤ observer.[[MaxChangesThreshold]]

给定源类型 sourcerelevantGlobal,要获取虚拟压力源,执行以下步骤。它们返回一个虚拟压力源或 null。

  1. topLevelTraversable 为 null。
  2. 如果 relevantGlobalWindow 对象:
    1. topLevelTraversable 设为 relevantGlobal可导航对象顶级可遍历对象
  3. 如果 relevantGlobalDedicatedWorkerGlobalScope 对象:
    1. owningDocumentsrelevantGlobal所属文档集合
    2. 如果 owningDocuments,返回 null。
    3. 断言owningDocuments大小为 1。
    4. topLevelTraversable 设为 owningDocuments[0] 的节点可导航对象顶级可遍历对象
  4. 如果 topLevelTraversable 为 null,返回 null。
  5. topLevelVirtualPressureSourceMappingtopLevelTraversable虚拟压力源映射
  6. virtualPressureSource 为 null。
  7. 如果 topLevelVirtualPressureSourceMapping 包含 source
    1. virtualPressureSource 设为 topLevelVirtualPressureSourceMapping[source]。
  8. 返回 virtualPressureSource

10.6.2 数据收集与传递

给定源类型 sourcerelevantGlobal,要激活数据收集,执行以下步骤:

  1. 如果 relevantGlobal平台收集器映射包含 source,中止这些步骤。
  2. platformCollectorrelevantGlobal平台收集器映射[source]。
  3. 如果 platformCollector已激活为 true, 中止这些步骤。
  4. platformCollector已激活设为 true。
  5. 实现定义的方式,开始使用 relevantGlobalsourceplatformCollector 运行数据收集步骤。
    注意

给定源类型 sourcerelevantGlobal,要停用数据收集,执行以下步骤:

  1. 如果 relevantGlobal平台收集器映射包含 source,中止这些步骤。
  2. platformCollectorrelevantGlobal平台收集器映射[source]。
  3. 如果 platformCollector已激活为 false, 中止这些步骤。
  4. 实现定义的方式,停止使用 relevantGlobalsourceplatformCollector 运行数据收集步骤。
  5. platformCollector已激活设为 false。
  6. 如果 platformCollector关联压力源是一个虚拟压力源
    1. 从其关联压力源已连接平台收集器移除 platformCollector
  7. 否则,执行任何实现定义步骤,以通知 platformCollector关联压力源停止检索遥测数据。

给定 relevantGlobalsourceplatformCollector数据收集步骤如下:

  1. pressureSourceplatformCollector关联压力源
  2. 如果 pressureSource 为 null,中止这些步骤。
  3. samplepressureSource最新样本
  4. 如果 sample 为 null,中止这些步骤。
  5. state 为 null。
  6. 如果 pressureSource 是一个虚拟压力源
    1. state 设为存储在 sampledata中的PressureState
  7. 否则:
    1. 如果影响因素变化显著为 false,中止这些步骤。
    2. state 设为由 sourcesampledata计算出的调整后压力状态
      注意
  8. 断言state 不为 null。
  9. rawTimestampsampletimestamp
  10. timeValue 为基于 rawTimestamprelevantGlobal相对高分辨率时间
  11. relevantGlobal 针对 source已注册观察者列表中的每个observer
    1. 如果以 observer 运行可以接收数据返回 false,继续
    2. 如果以 observersourcetimeValue 运行通过速率测试返回 false,继续
    3. 如果以 observersourcestate 运行应当派发 返回 false,继续
    4. record 为一个新的PressureRecord 对象,其[[Source]] 设为 source[[State]] 设为 state, 并将[[Time]] 设为 timeValue
    5. 如果 observer.[[AfterPenaltyRecordMap]][source] 存在
      1. observer.[[AfterPenaltyRecordMap]][source] 设为 record
      2. 继续
    6. 如果以 observersource 运行通过速率混淆测试返回 false:
      1. observer.[[AfterPenaltyRecordMap]][source] 设为 record
      2. observer.[[ChangesCountMap]][source] 设为 0。
      3. 创建一个持续 observer.[[PenaltyDuration]] 的定时器,回调如下:
        1. 如果 observer.[[AfterPenaltyRecordMap]][source] 存在
          1. recordobserver.[[AfterPenaltyRecordMap]][source]。
          2. 移除 observer.[[AfterPenaltyRecordMap]][source]。
          3. observersourcerecord 运行入队一条记录
      4. 继续
    7. observersourcerecord 运行入队一条记录

10.6.3 入队 PressureRecord

给定参数 observersourcerecord,要入队一条记录,运行以下步骤:

  1. 如果 observer.[[QueuedRecords]]大小大于最大排队记录数,则移除第一个
  2. record追加observer.[[QueuedRecords]]
  3. observer.[[LastRecordMap]][source] 设为 record
  4. 使用 observer相关全局对象运行入队一个压力观察任务

10.6.4 入队压力观察任务

PressureObserver 任务源是一个任务源,用于安排任务以执行10.6.5 通知压力观察者

给定 relevantGlobal 作为输入,要入队一个压力观察任务,运行以下步骤:

  1. 如果 relevantGlobal压力观察任务已入队为 true,则返回。
  2. relevantGlobal压力观察任务已入队设为 true。
  3. 在以 relevantGlobal 为目标的情况下,于PressureObserver 任务源队列一个全局任务通知压力观察者

10.6.5 通知压力观察者

给定 relevantGlobal 作为输入,要通知压力观察者,运行以下步骤:

  1. relevantGlobal压力观察任务已入队设为 false。
  2. notifySet 为一个新的集合,包含 relevantGlobal已注册观察者列表中的所有观察者
  3. notifySet 中的每个observer
    1. recordsobserver.[[QueuedRecords]] 的一个克隆
    2. 清空 observer.[[QueuedRecords]]
    3. 如果 records 不为,则调用 observer.[[Callback]] ,参数为 « recordsobserver »,以及 "report"。

10.6.6 处理完全激活状态的变化

本规范定义了给定Document document 的以下卸载文档清理步骤

  1. relevantGlobaldocument相关全局对象
  2. relevantGlobal已注册观察者列表有序映射的每个 source
    1. 使用 sourcerelevantGlobal 调用停用数据收集
    2. 移除 relevantGlobal平台收集器映射[source]。
Issue 275:正确确定 bfcache 集成策略

本规范此前包含了覆盖以下情况的步骤:一个Document再次变为完全激活(即与Documentreactivate步骤集成)时的情形。在讨论预期行为期间,这些步骤已被移除。

10.6.7 处理 worker 状态变化

注意

每当某个WorkerGlobalScope relevantGlobalclosing 标志被设为 true 时,执行以下步骤:

  1. relevantGlobal已注册观察者列表有序映射的每个 source
    1. 使用 sourcerelevantGlobal 调用停用数据收集
    2. 移除 relevantGlobal平台收集器映射[source]。

11. 安全与隐私注意事项

11.1 隐私与安全威胁类型

注意
本工作组将在本节列出任何已知的攻击向量,包括理论与现实世界的。

11.1.1 计时攻击

如果不共享同源的网站能够在同一时间访问到唯一或非常精确的数值,那么可能会在跨非同源站点识别用户。 此类攻击可通过11.2.1 数据最小化11.2.2 速率混淆, 以及11.2.7 同源限制进行缓解。

11.1.2 跨站隐蔽信道

在计算机安全中,隐蔽信道是一种在本不应被允许通信的进程之间传递信息的能力。在现代多进程 Web 引擎中,通用情况下每个窗口或标签页都驻留在自己的进程中(具有同源的文档或具有同站点的站点通常共享同一进程)。使用此 API,站点 A 可以在一个标签页中先操纵 CPU 的状态后向信道 C 进行广播,从而可能创建跨站隐蔽信道 C。接着另一个标签页中的站点 B(与站点 A 不同站点)通过使用此 API 读取来自信道 C 的广播数据,以获知 CPU 状态何时发生变化。只要站点 A 和 B 上的脚本都在运行,该过程就会重复。

此类攻击可通过11.2.2 速率混淆11.2.5 中断校准进行缓解。建议实现者对于长时间运行的脚本综合考虑这些缓解措施。

注意
脚本运行时间越长,利用所提议的跨站隐蔽信道能传输的信息就越多。例如,用户同时在视频会议站点与另一个长时间运行的站点上,相较于常规浏览情形,允许传输更多信息。另一方面,视频会议这类工作负载通常会对 CPU 施加持续压力,使得以可预测的方式操控压力状态更加困难。

11.1.3 定向去匿名化攻击

定向去匿名化攻击是危及用户匿名性的一类关键威胁。这类攻击允许一个恶意或部分被攻陷的网站(下称“恶意站点”)判断访客是否拥有某个特定的公共标识符,例如电子邮件地址或社交媒体账号。

虽然匿名对部分人而言或许是一种奢侈,但对某些人来说远不止于此——它关系到生存。例如参与政治抗议、报道敏感话题的记者等。

举例来说,攻击者可以将某个资源私下分享给目标(例如使用公共资源分享服务,“受害站点”),然后通过侧信道在加载该资源时测量副作用(表明访问成功)。如果已登录的访客能够成功访问嵌入的资源,这就表明当前访问确实是预期目标。

具体而言,若暴露关于整体 CPU 压力的可靠信息,攻击站点就能理解跨源导航的目标(例如来自另一个站点的 iframe 或弹出窗口)是否执行了 CPU 密集型操作。

下置弹窗(pop-under)以及 下置标签(tab-under)等技术可用于对用户隐藏加载过程。

一种可能的攻击是恶意网站打开一个指向受害站点资源的弹窗,例如视频流媒体站点或在线文档编辑器,其中该资源仅分享给特定用户,而用户当前已登录。

假设加载该资源会增加 CPU 压力,这将创建一个侧信道,向攻击站点揭示用户是否登录了具有访问该资源权限的账户,从而使用户去匿名化。

鉴于现代 CPU 能够快速从高压力状态恢复,一种可能的缓解策略是在加载弹窗和 iframe 内容后,临时禁用读数数秒。

11.2 缓解策略

注意
本节提供适用于本规范的缓解策略的高层视图。这些缓解的规范性定义已整合到本规范相应算法之中。实现者在落实本规范定义的缓解措施时,建议参考 TAG 关于隐私浏览模式的指导

11.2.1 数据最小化

本规范遵循通用的 数据最小化 原则,以将与底层平台低层细节相关的数据暴露限制在满足高价值用例所需的最小范围之内。这包括考虑限制暴露 设备识别信息

在本规范语境中应用数据最小化原则的具体做法,见11.2.2 速率混淆11.2.7 同源限制

11.2.2 速率混淆

本规范要求实现速率混淆缓解措施,用于在一个实现定义的滑动观测窗口内跟踪压力变更次数,并在超过关于压力变更次数的实现定义阈值时设置标志。同样,建议实现也观察任何异常活动,如跨多个状态的压力状态高频变更,并以类似方式设置该标志。

若该标志被设置,建议实现给予压力观察器一个惩罚期,在此期间它不能像正常那样向脚本通知其压力状态的变更。惩罚期的时长为实现定义,并建议进行随机化。当通知压力观察者在惩罚结束后恢复运行时,它仅报告最新的压力状态,忽略该惩罚期间从平台收集器接收到的任何中间状态信息。

11.2.3 速率混淆规范性参数

基于实现经验,实施者必须使用:

  • 对 PressureObserver 的 [[MaxChangesThreshold]] 内部插槽,使用 50 到 100 次变更之间的范围。
  • 对 PressureObserver 的 [[PenaltyDuration]] 内部插槽,使用 5000 毫秒到 10000 毫秒之间的范围。
注意

11.2.4 速率混淆非规范性参数

本节为非规范性。

基于实现经验,建议实施者使用:

  • 对 PressureObserver 的 [[ObservationWindow]] 内部插槽,使用 300000 毫秒(5 分钟)到 600000 毫秒(10 分钟)之间的范围。
注意

11.2.5 中断校准

在校准过程中,攻击者尝试操控 CPU,使得该 API 会以最高概率在其构造的工作负载施压后报告进入某个特定压力状态。此中断校准缓解方案可通过在运行时对促成这些压力状态转换的实现定义低层硬件度量进行轻微变动,从而减慢或阻止该校准过程成功。即便初始校准成功,在该缓解持续运行时其结果也会在运行时失效。任何重新校准的尝试也将被同样缓解。

注意

11.2.6 中断校准参数

本节为非规范性。

基于实现经验,建议实施者将缓解应用于一个随机化的时间值,其范围在 120000 毫秒(2 分钟)到 240000 毫秒(4 分钟)之间。

注意

11.2.7 同源限制

默认情况下,数据传递仅限于与 活跃画中画会话发起方同源的文档,处于捕获状态的文档,或具有系统焦点的文档(如有)。

符合上述规则的数据传递资格的文档,可将其数据传递委派给子可导航对象中的文档。

该功能仅可通过声明的策略扩展至第三方上下文,如 iframe。

共享 worker 可以在文档之间共享,例如顶层文档及其关联的 iframe。若拥有者集合中的某个文档满足上述数据传递要求,则该共享 worker 将具备数据传递资格。这意味着嵌入的 iframe 能够将数据传递给嵌入它的文档。

12. 可访问性 注意事项

Compute Pressure API 专注于改善用户体验。有两种方式,基于该 API 构建的应用可以积极影响可访问性。

  1. 在根据使用该 API 收集的信息做出决策时,考虑用户的访问需求。
  2. 在基于从该 API 获得的信息设计和构建用户界面时,将可访问性纳入考虑。

作为该 API 的使用者,考虑这两种机会都很重要。以下是一些示例:

13. 自动化

Compute Pressure API 对测试作者提出了挑战,因为要完全测试接口需要能以可预测方式响应的物理硬件设备。

为了解决这一挑战,本文件定义了 [WEBDRIVER2] 扩展命令,允许定义和控制行为像真实来源的虚拟压力源,这些虚拟压力源可具有特定属性,其读数可以由用户完全定义。

13.1 虚拟压力源

A virtual pressure source 是一种以可控方式模拟真实压力源行为的 pressure source。它会向连接到它的零个或多个 platform collectors 报告压力变化。

然而,与真实的 pressure source 相反,它直接报告 pressure state 值,而不是必须由 implementation-defined 值再由 platform collector 处理成 pressure states。换言之,virtual pressure sourcepressure source sampledata 是一个 PressureState

除了与所有 pressure sources 关联的数据(例如 pressure source sample)之外,每个 virtual pressure source 具有:

每个 top-level traversable 都有一个 virtual pressure source mapping,它是一个将 source types 映射到 virtual pressure source 的有序映射(ordered map)。

注意
注意

13.1.1 扩展命令

13.1.1.1 创建虚拟压力源
HTTP Method URI Template
POST /session/{session id}/pressuresource

扩展命令 创建一个指定 源类型 的新的 虚拟压力源。来自同一 源类型PressureObserver 实例调用 observe() 后,将使用此 虚拟压力源 作为其底层 压力源,直到执行 13.1.1.2 删除虚拟压力源

此算法使用的 parameters 参数的属性
参数名 值类型 是否必需
type String yes
supported Boolean no

给定 sessionURL variablesparameters远端步骤 为:

  1. virtualPressureSourceType 为从 parameters 中调用 get a property "type" 的结果。
  2. 如果 user agentsupported source types 不包含 virtualPressureSourceType,则返回带有 WebDriver 错误代码 invalid argument错误
  3. topLevelTraversable当前浏览上下文top-level traversable
  4. topLevelVirtualPressureSourceMappingtopLevelTraversablevirtual pressure source mapping
  5. 如果 topLevelVirtualPressureSourceMapping 包含 virtualPressureSourceType,则返回带有 WebDriver 错误代码 invalid argument错误
  6. supported 为从 parameters 使用 get a property with default 获取 "supported" 且默认值为 true 的结果。
  7. virtualPressureSource 为一个新的 virtual pressure source
  8. virtualPressureSourcecan provide samples 设置为 supported
  9. topLevelVirtualPressureSourceMapping[virtualPressureSourceType] 设置为 virtualPressureSource
  10. 返回 成功,数据为 null。
13.1.1.2 删除虚拟压力源
HTTP Method URI Template
DELETE /session/{session id}/pressuresource/{type}

扩展命令 删除给定的 虚拟压力源,这意味着如果可用,针对给定 source type 的数据将以常规方式由非虚拟手段传递。

给定 sessionURL variablesparameters远端步骤 为:

  1. virtualPressureSourceTypeURL variables["type"] 的值。
  2. 如果 user agentsupported source types 不包含 virtualPressureSourceType,则返回带有 WebDriver 错误代码 invalid argument错误
  3. topLevelTraversable当前浏览上下文top-level traversable
  4. topLevelVirtualPressureSourceMappingtopLevelTraversablevirtual pressure source mapping
  5. pressureSourcetopLevelVirtualPressureSourceMapping[virtualPressureSourceType]。
  6. 对于 pressureSourceconnected platform collectors 中的每个 platformCollector
    1. platformCollectorassociated pressure source 设为 null。
  7. 移除 topLevelVirtualPressureSourceMapping[virtualPressureSourceType]。
  8. 返回 成功,数据为 null。
13.1.1.3 更新虚拟压力源
HTTP Method URI Template
POST /session/{session id}/pressuresource/{type}

扩展命令 允许通过推送新的 pressure source sample 来更新 虚拟压力源 的状态。

注意
此算法使用的 parameters 参数的属性
参数名 值类型 是否必需
sample PressureState yes

给定 sessionURL variablesparameters远端步骤 为:

  1. virtualPressureSourceTypeURL variables["type"] 的值。
  2. 如果 user agentsupported source types 不包含 virtualPressureSourceType,则返回带有 WebDriver 错误代码 invalid argument错误
  3. topLevelTraversable当前浏览上下文top-level traversable
  4. topLevelVirtualPressureSourceMappingtopLevelTraversablevirtual pressure source mapping
  5. 如果 topLevelVirtualPressureSourceMapping 不包含 virtualPressureSource, 则返回带有 WebDriver 错误代码的 错误,错误类型为 unsupported operation
  6. virtualPressureSourcetopLevelVirtualPressureSourceMapping[virtualPressureSourceType]。
  7. sample 为从 parameters 中调用 get a property "sample" 的结果。
  8. 如果 sample 的类型不是 PressureState,则返回带有 WebDriver 错误代码 错误,错误类型为 invalid argument
  9. virtualPressureSourcelatest sample 设置为一个新的 pressure source sample,其 datatimestampunsafe shared current time
  10. 以实现定义的方式,使 virtualPressureSourcelatest samplevirtualPressureSourceconnected platform collectors 可用。
  11. 返回 成功,数据为 null。

14. 示例

本节为非规范性内容。

Example 2: 如何从回调访问 observer
const samples = [];

function pressureChange(records, observer) {
  for (const record of records) {
    samples.push(record.state);

    // We only want 20 samples.
    if (samples.length == 20) {
      observer.disconnect();
      return;
    }
  }
}

const observer = new PressureObserver(pressureChange);
observer.observe("cpu");

在下例中,当压力变为 critical 时,我们希望减少并发的视频流数量。为简化起见,我们只考虑这一种状态。

由于降低流数量可能不会立即使系统退出 critical 状态(或根本不能),我们采用一种策略:在仍处于 critical 状态时,每 30 秒逐次减少一个流。

我们通过确保回调至少每 30 秒被调用一次(或者在状态实际变化时被调用)来实现这一点。当状态变化时,我们会重置定时器。

Example 3: 根据 CPU 压力调整视频流数量
let timerId = -1;
function pressureChange(records) {
  // Clear timer every time we are called, either by an actual state change,
  // or when called by setTimeout (see below).
  if (timerId > 0) {
    clearTimeout(timerId);
  }

  // When entering critical state, we want to recheck every 30sec if we are
  // still in critical state and if so, further reduce our concurrent streams.
  // For this reason we create a timer for 30 seconds that will call us back
  // with the last result in there were no change.
  const lastRecordArray = [records.at(records.length - 1)];
  timerId = setTimeout(pressureChange.bind(this, lastRecordArray), 30_000);

  for (const record of records) {
    if (record.state == "critical") {
      let streamsCount = getStreamsCount();
      setStreamsCount(streamsCount--);
    }
  }
}

const observer = new PressureObserver(pressureChange);
observer.observe("cpu");

在下例中,我们演示如何使用 takeRecords(), 以检索自上次回调以来累积的剩余 records

建议在调用 disconnect() 之前这样做, 否则 disconnect() 会清除它们,从而永远丢失。

例如,我们可能想在基准测试工作负载期间测量压力,因此希望获得该工作负载的精确时长内的压力遥测数据。这意味着在任务完成时立即断开所有观察者,并手动请求此时点之前可能尚未作为事件循环一部分交付的任何待处理压力遥测。

Example 4: 在断开连接之前处理所有状态变化
function logWorkloadStatistics(records) {
  // do something with records.
}

const observer = new PressureObserver(logWorkloadStatistics);
observer.observe("cpu");

// Read pending state change records, otherwise they will be cleared
// when we disconnect.
const records = observer.takeRecords();
logWorkloadStatistics(records);

observer.disconnect();

在下例中,我们展示如何通过对带有 sourceunobserve() 的调用来告诉观察者停止监视特定源的状态变化。

注意
Example 5: 如何告诉观察者停止监视特定源的状态变化
const observer = new PressureObserver(records => { /* do something with records. */ });

observer.observe("cpu");
observer.observe("gpu");

// Callback now gets called whenever the pressure state changes for 'cpu' or 'gpu'.

observer.unobserve("gpu");

// Callback now only gets called whenever the pressure state changes for 'cpu'.

在下例中,我们展示如何通过调用 disconnect() 来告诉观察者停止监视任何状态变化。调用 disconnect() 将停止观察之前通过 observe() 调用所观察的所有源。

此外,它还会清除自上次回调被调用以来收集的所有待处理记录。

Example 6: 如何告诉观察者停止监视任何状态变化
const observer = new PressureObserver(records => { // do something with records. });
observer.observe("cpu");
observer.observe("gpu");

// some time later...

observer.disconnect();

// records will be an empty array, because of the previous disconnect().
const records = observer.takeRecords();

15. 符合性

除标记为非规范性的部分外,本规范中的所有作者指南、图表、示例和注释均为非规范性内容。本规范的其余部分为规范性内容。

文档中关键词 MAYMUSTRECOMMENDED 的含义按 BCP 14 [RFC2119] [RFC8174] 中的描述解释,仅当这些词以全大写形式出现时适用,如本文所示。

本规范为单一产品定义符合性标准:实现本文所包含接口的 用户代理

A. 致谢

本节为非规范性内容。

非常感谢以下人员提供的宝贵反馈和建议: Anssi Kostiainen, Asaf Yaffe, Benjamin VanderSloot, Chen Xing, Evan Shrubsole, Florian Scholz, François Beaufort, Jan Gora, Jesse Barnes, Joshua Bell, Kamila Hasanbega, Matt Menke, Moh Haghighat, Nicolás Peña Moreno, Opal Voravootivat, Paul Jensen, Peter Djeu, Reilly Grant, Ulan Degenbaev, Victor Miura, Wei Wang, 以及 Zhenyao Mo

感谢 W3C 隐私兴趣组 (PING) 的审阅,特别感谢 Peter Snyder 对隐私审查、反馈以及提议的跨站隐形通道攻击及其缓解措施的贡献。同样感谢 Ehsan Toreini 在私人浏览隐私方面的工作以及对本规范的相关贡献。

特别感谢 Amanda Zhao, Fidel Tian, Zhiliang Wang 以及 Zoom 工程团队的其他成员,感谢他们提供的反馈和实践实验,帮助在真实场景中改进此 API。

B. 实质性更改摘要

本节为非规范性内容。

B.1 首次公开工作草案 (2022/12/20) 以来的更改

B.2 索引

B.2.1 本规范定义的术语

B.2.2 引用中定义的术语

  • [DOM] 定义了以下项:
    • Document 接口
    • 文档
    • 节点
    • 节点文档(用于 Node
  • [ECMASCRIPT] 定义了以下项:
    • agent(针对 ECMAScript
    • globalThis 属性(针对 globalThis
  • [HR-TIME] 定义了以下项:
    • DOMHighResTimeStamp
    • 相对高分辨率时间
    • 不安全的当前时间(用于单调时钟)
    • 不安全的共享当前时间
  • [HTML] 定义了以下项:
    • 允许使用
    • 关联文档
    • 浏览上下文
    • 子可导航项
    • closing(针对 WorkerGlobalScope
    • DedicatedWorkerGlobalScope 接口
    • fully active(针对 Document
    • 全局对象
    • iframes 元素
    • 并行
    • 可导航(针对 Window
    • 节点可导航
    • 源(origin)
    • owner set(针对 WorkerGlobalScope
    • policy container(针对 WorkerGlobalScope
    • 排队一个全局任务
    • 重新激活(针对 Document
    • 相关全局对象
    • 相关设置对象
    • 同源(same origin)
    • 同站点(same site)
    • 系统焦点(针对顶层可遍历对象)
    • 任务来源
    • 时间原点(针对环境设置对象)
    • 顶层浏览上下文
    • 顶层可遍历对象
    • 顶层可遍历对象(针对浏览上下文)
    • 卸载文档清理步骤
    • Window 接口
    • WorkerGlobalScope 接口
  • [INFRA] 定义了以下项:
    • 追加(针对 list
    • 追加(针对 set
    • 断言(Assert)
    • 清除(针对 map
    • 克隆(针对 list
    • 包含(针对 list
    • 包含(针对 map
    • 继续(针对迭代)
    • 空(针对 list
    • 对每一项(针对 list
    • 对每一项(针对 map
    • 实现定义(implementation-defined)
    • 项(针对 list
    • 项(针对 struct
    • 键定(针对 map
    • 列表(list)
    • 有序映射(ordered map)
    • 队列(queue)
    • 移除(针对 list
    • 移除(针对 map
    • 集合(set)
    • 大小(针对 list
    • 结构体(struct)
    • 元组(tuples)
    • 并集(针对 set
    • 值(针对 map
  • [MEDIACAPTURE-STREAMS] 定义了以下项:
    • 上下文正在捕获
  • [PERMISSIONS-POLICY] 定义了以下项:
    • 默认允许列表(针对策略控制的功能)
    • 策略控制的功能
  • [WEBDRIVER2] 定义了以下项:
    • 当前浏览上下文
    • 错误
    • 错误代码
    • 扩展命令 URI 模板
    • 扩展命令
    • 获取属性
    • 获取具有默认值的属性
    • 无效参数
    • 远端步骤
    • 成功
    • 不受支持的操作
  • [WEBIDL] 定义了以下项:
    • 一个新的 Promise
    • 以某值被拒绝的 Promise
    • AbortError 异常
    • [Default] 扩展属性
    • 默认 toJSON 步骤
    • [EnforceRange] 扩展属性
    • [Exposed] 扩展属性
    • FrozenArray 接口
    • getter 步骤
    • 调用(invoke)
    • NotAllowedError 异常
    • NotSupportedError 异常
    • object 类型
    • Promise 接口
    • 在 Promise 定结后执行步骤(React)
    • 已拒绝
    • 已解析
    • [SameObject] 扩展属性
    • [SecureContext] 扩展属性
    • 序列(sequence)
    • this
    • TypeError 异常
    • undefined 类型
    • unsigned long 类型

B.3 IDL 索引

WebIDLenum PressureSource { "cpu" };

enum PressureState { "nominal", "fair", "serious", "critical" };

callback PressureUpdateCallback = undefined (
  sequence<PressureRecord> changes,
  PressureObserver observer
);

[Exposed=(DedicatedWorker,SharedWorker,Window), SecureContext]
interface PressureObserver {
  constructor(PressureUpdateCallback callback);

  Promise<undefined> observe(PressureSource source, optional PressureObserverOptions options = {});
  undefined unobserve(PressureSource source);
  undefined disconnect();
  sequence<PressureRecord> takeRecords();

  [SameObject] static readonly attribute FrozenArray<PressureSource> knownSources;
};

[Exposed=(DedicatedWorker,SharedWorker,Window), SecureContext]
interface PressureRecord {
  readonly attribute PressureSource source;
  readonly attribute PressureState state;
  readonly attribute DOMHighResTimeStamp time;
  [Default] object toJSON();
};

dictionary PressureObserverOptions {
  [EnforceRange] unsigned long sampleInterval = 0;
};

C. 参考文献

C.1 规范性参考文献

[dom]
DOM 标准. Anne van Kesteren. WHATWG. 现行标准。URL: https://dom.spec.whatwg.org/
[hr-time]
高精度时间(High Resolution Time). Yoav Weiss. W3C. 2024 年 11 月 7 日。W3C 工作草案。URL: https://www.w3.org/TR/hr-time-3/
[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/
[mediacapture-streams]
媒体捕获与流(Media Capture and Streams). Cullen Jennings; Jan-Ivar Bruaroey; Henrik Boström; youenn fablet. W3C. 2025 年 4 月 24 日。CRD。URL: https://www.w3.org/TR/mediacapture-streams/
[PERMISSIONS-POLICY]
权限策略(Permissions Policy). Ian Clelland. W3C. 2025 年 5 月 6 日。W3C 工作草案。URL: https://www.w3.org/TR/permissions-policy-1/
[RFC2119]
在 RFC 中表示要求级别的关键词. S. Bradner. IETF. 1997 年 3 月。最佳当前实践。URL: https://www.rfc-editor.org/rfc/rfc2119
[RFC8174]
RFC 2119 关键词中的大小写歧义. B. Leiba. IETF. 2017 年 5 月。最佳当前实践。URL: https://www.rfc-editor.org/rfc/rfc8174
[WEBDRIVER2]
WebDriver. Simon Stewart; David Burns. W3C. 2025 年 5 月 12 日。W3C 工作草案。URL: https://www.w3.org/TR/webdriver2/
[WEBIDL]
Web IDL 标准. Edgar Chen; Timothy Gu. WHATWG. 现行标准。URL: https://webidl.spec.whatwg.org/

C.2 信息性参考文献

[ECMAScript]
ECMAScript 语言规范. Ecma International. URL: https://tc39.es/ecma262/multipage/