Compute Pressure Level 1

W3C Working Draft

More details about this document
This version:
https://www.w3.org/TR/2024/WD-compute-pressure-20240409/
Latest published version:
https://www.w3.org/TR/compute-pressure/
Latest editor's draft:
https://w3c.github.io/compute-pressure/
History:
https://www.w3.org/standards/history/compute-pressure/
Commit history
Test suite:
https://github.com/web-platform-tests/wpt/labels/compute-pressure
Editors:
Kenneth Rohde Christiansen (Intel Corporation)
Arnaud Mandy (Intel Corporation)
Former editors:
Olivier Yiptong (Google Inc.)
Victor Costan (Google Inc.)
Feedback:
GitHub w3c/compute-pressure (pull requests, new issue, open issues)

Abstract

The Compute Pressure API provides a way for websites to react to changes in the CPU pressure of the target device, such that websites can trade off resources for an improved user experience.

Status of This Document

This section describes the status of this document at the time of its publication. A list of current W3C publications and the latest revision of this technical report can be found in the W3C technical reports index at https://www.w3.org/TR/.

This document was published by the Devices and Sensors Working Group as a Working Draft using the Recommendation track.

Publication as a Working Draft does not imply endorsement by W3C and its Members.

This is a draft document and may be updated, replaced or obsoleted by other documents at any time. It is inappropriate to cite this document as other than work in progress.

This document was produced by a group operating under the W3C Patent Policy. W3C maintains a public list of any patent disclosures made in connection with the deliverables of the group; that page also includes instructions for disclosing a patent. An individual who has actual knowledge of a patent which the individual believes contains Essential Claim(s) must disclose the information in accordance with section 6 of the W3C Patent Policy.

This document is governed by the 03 November 2023 W3C Process Document.

1. Introduction

This section is non-normative.

Modern applications often need to balance the trade offs and advantages of fully utilizing the system's computing resources, in order to provide a modern and delightful user experience.

As an example, many applications can render video effects with varying degrees of sophistication. These applications aim to provide the best user experience, while avoiding driving the user's device into a high pressure regime.

Utilization of processing units close to and often reaching 100% can lead to a bad user experience, as different tasks are fighting for the processing time. This can lead to slowless, which is especially noticeable with input delay. Further, a prolonged utilization close 100% can cause the processing units to heat up due to prolonged boosting, which can lead to throttling, resulting in an even worse user experience.

As a result of thermal limits, many smartphones, tablets and laptops can become uncomfortably hot to the touch. The fans in laptops and desktops can become so loud that they disrupt conversations or the users’ ability to focus.

In many cases, a device under high pressure appears to be unresponsive, as the operating system may fail to schedule the threads advancing the task that the user is waiting for. See also Use Cases.

2. A Note on Feature Detection

This section is non-normative.

Feature detection is an established web development best practice. Resources on the topic are plentiful on- and offline and the purpose of this section is not to discuss it further, but rather to put it in the context of detecting hardware-dependent features.

Consider the below feature detection examples:

Note

3. Concepts

This specification defines the following concepts:

3.1 Processing Units

Computing devices consist of a multitude of different processing units such as the Central Processing Unit (CPU), the Graphics Processing Unit (GPU) and many specialized processing units. The latter are becoming popular such as ones designed to accelerate specific tasks like machine learning or computer vision.

3.2 Supported sources

The specification currently defines the supported source types as global system thermals and the central processing unit, also know as the CPU. Future levels of this specification MAY introduce additional source types.

WebIDLenum PressureSource { "thermals", "cpu" };

The PressureSource enum represents the supported source types:

Note

3.3 Sampling and Reporting Rate

The requested sampling interval represents the desired interval between samples to be obtained from the hardware, expressed in milliseconds.

Interval and frequency are inverses of each other, so the requested sampling interval can also be expressed as a requested sampling rate in Hertz (cycles per second) by dividing 1000 by the requested sampling interval value.

The sampling rate for a platform collector is defined as a rate at which the user agent obtains telemetry readings from the underlying platform, and it might differ from the pressure observers' requested sampling rates. The rate is measured in Hertz (cycles per second).

The reporting rate for a pressure observer is the rate at which it runs the data delivery steps, and it will never exceed the sampling rate.

The sampling rate differs from the requested sampling rate when the requested sampling rate exceeds upper or lower sampling rate bounds supported or accepted by the underlying platform and user agent.

It is recommended that the user agent limits the reporting rate as outlined in 11.2.2 Rate-limiting change notifications.

In case the user didn't request a sampling rate, the sampling rate is implementation-defined.

Note

4. Platform primitives

The platform collector refers to a platform interface, with which the user agent interacts to obtain the telemetry readings required by this specification.

A platform collector can be defined by the underlying platform (e.g. in a native telemetry framework) or by the user agent, if it has a direct access to hardware counters.

A platform collector can support telemetry for different source types of computing devices defined by PressureSource, or there can be multiple platform collectors.

From the implementation perspective platform collector can be treated as a software proxy for the corresponding hardware counters. It is possible to have multiple platform collector simultaneously interacting with the same underlying hardware if the underlying platform supports it.

In simple cases, a platform collector represents individual hardware counters, but if the provided counter readings are a product of data fusion performed in software, the platform collector represents the results of the data fusion process. This may happen in user space or in kernel space.

As collecting telemetry data often means polling hardware counters, it is not a free operation and thus, it should not happen if there are no one observing the data. See 10.5 Life-cycle and garbage collection for more information.

A platform collector samples data at a specific rate. A user agent may modify this rate (if possible) for privacy reasons, or ignore and fuse certain readings.

5. User notifications

It is RECOMMENDED that a user agent show some form of user-visible notification that informs the user when a pressure observer is active, as well as provides the user with the means to block the ongoing operation, or simply dismiss the notification.

6. Policy control

The Compute Pressure API defines a policy-controlled feature identified by the token "compute-pressure". Its default allowlist is 'self'.

Workers (dedicated and shared) adhere to the permission policy set by their owning document(s).

Shared workers often have multiple owning documents as they can be obtained by other documents with the same origin. In this case, all owning documents must be allowed to use the policy-controlled feature defined by this specification.

Dedicated workers can be created from other workers, in which case the permission policy of the first owning document (or owning documents, in case of a shared worker) up the owner chain will be used.

Note
Note

7. Internal Slot Definitions

Each global object has:

A registered observer consists of an observer (a PressureObserver object).

The user agent additionally has a max queued records integer, which is set to an implementation-defined value, greater than 0.

A constructed PressureObserver object has the following internal slots:

For the rate obfuscation mitigation the constructed PressureObserver object additionally has the following internal slots:

8. Pressure States

Pressure states represents the minimal set of useful states that allows websites to react to changes in compute and system pressure with minimal degration in quality or service, or user experience.

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

The PressureState enum represents the pressure state with the following states:

9. Contributing Factors

Contributing factors represent the underlying hardware metrics contributing to the current pressure state and can be implementation-defined.

The adjusted pressure state is a pressure state determined by an implementation-defined algorithm that takes as input source type and any other implementation-defined data from contributing factors. This algorithm MUST not be deterministic to ensure break calibration mitigation effectiveness.

The change in contributing factors is substantial steps are as follows:

  1. If implementation-defined low-level hardware metrics that contribute to the current pressure state drop below or exceed an, per metric, implementation-defined threshold for the current pressure state, return true.
  2. Return false.
Note

10. Pressure Observer

The Compute Pressure API enables developers to understand the pressure of system resources such as the CPU.

10.1 The PressureUpdateCallback callback

WebIDLcallback PressureUpdateCallback = undefined (
  sequence<PressureRecord> changes,
  PressureObserver observer
);
This callback will be invoked when the pressure state changes.

10.2 The PressureObserver object

The PressureObserver can be used to observe changes in the pressure states.

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> supportedSources;
};

The PressureObserver interface represents a PressureObserver.

10.2.1 The constructor() method

The new PressureObserver(callback) constructor steps are:

  1. Set this.[[Callback]] to callback.

10.2.2 The observe() method

The observe(source, options) method steps are:

  1. Let relevantGlobal be this's relevant global object.
  2. For each document in relevantGlobal's owning document set:
    1. If document is not allowed to use the policy-controlled feature token "compute-pressure", return a promise rejected with NotAllowedError.
    Issue 1
  3. Set this.[[SampleIntervalMap]][source] to options's sampleInterval.
  4. Let promise be a new promise.
  5. Let pendingPromiseTuple be (source, promise).
  6. Append pendingPromiseTuple to this.[[PendingObservePromises]].
  7. React to promise:
  8. Run the following steps in parallel:
    1. If source is not a supported source type, queue a global task on the PressureObserver task source given relevantGlobal to reject promise NotSupportedError and abort these steps.
    2. Activate data delivery of source data to relevantGlobal.
    3. Queue a global task on the PressureObserver task source given relevantGlobal to run these steps:
      1. If promise was rejected, run the following substeps:
        1. If relevantGlobal's registered observer list for source is empty, deactivate data delivery of source data to relevantGlobal.
        2. Return.
      2. Append a new registered observer whose observer is this to relevantGlobal's registered observer list for source.
      3. Resolve promise.
  9. Return promise.

10.2.3 The unobserve() method

The unobserve(source) method steps are:

  1. If source is not a supported source type, throw "NotSupportedError".
  2. Remove from this.[[QueuedRecords]] all records associated with source.
  3. Remove this.[[SampleIntervalMap]][source].
  4. Remove this.[[LastRecordMap]][source].
  5. Remove this.[[AfterPenaltyRecordMap]][source].
  6. For each (promiseSource, pendingPromise) of this.[[PendingObservePromises]], if source is equal to promiseSource, reject pendingPromise with an AbortError.
  7. Let relevantGlobal be this's relevant global object.
  8. Remove any registered observer from relevantGlobal's registered observer list for source for which this is the registered observer.
  9. If the above registered observer list is empty, deactivate data delivery of source data to relevantGlobal.

10.2.4 The disconnect() method

The disconnect() method steps are:

  1. Empty observer.[[QueuedRecords]].
  2. Clear this.[[SampleIntervalMap]].
  3. Clear this.[[LastRecordMap]].
  4. Clear this.[[AfterPenaltyRecordMap]].
  5. For each (promiseSource, pendingPromise) of this.[[PendingObservePromises]], reject pendingPromise with an AbortError.
  6. Let relevantGlobal be this's relevant global object.
  7. Remove any registered observer from relevantGlobal's' registered observer list for all supported source types for which this is the observer.
  8. If the above registered observer list is empty, deactivate data delivery of source data to relevantGlobal.

10.2.5 The takeRecords() method

Note

The takeRecords() method steps are:

  1. Let records be a clone of observer.[[QueuedRecords]].
  2. Empty observer.[[QueuedRecords]].
  3. Return records.

10.2.6 The supportedSources attribute

The supportedSources attribute is informing on the supported source type by the platform collector.

The supportedSources getter steps are:

  1. Let sources be a list of source.
  2. Return observer's frozen array of supported source types.

Note

10.3 The PressureRecord interface

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

A constructed PressureRecord object has the following internal slots:

10.3.1 The source attribute

The source getter steps are to return its [[Source]] internal slot.

10.3.2 The state attribute

The state getter steps are to return its [[State]] internal slot.

10.3.3 The time attribute

The time getter steps are to return its [[Time]] internal slot.

10.3.4 The toJSON member

When PressureRecord.toJSON is called, run Web IDL Standard's default toJSON steps.

10.4 The PressureObserverOptions dictionary

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

10.4.1 The sampleInterval member

The sampleInterval member represents the requested sampling interval expressed in milliseconds.

Note

10.5 Life-cycle and garbage collection

Each global object has a strong reference to registered observers in their registered observer list (one per source).

Note

10.6 Processing Model

This section outlines the steps the user agent must take when implementing the specification.

10.6.1 Supporting algorithms

The reset observation window steps given the argument observer, are as follows:

Run the reset observation window steps and start a timer to re-run the steps when the observer.[[ObservationWindow]] time has passed, using different randomized values.

Note

To determine the owning document set for a relevant global object relevantGlobal:

  1. Let owningDocumentSet be an empty set.
  2. If relevantGlobal is Window, then append relevantGlobal's associated document to owningDocumentSet.
  3. Otherwise, for each owner in WorkerGlobalScope relevantGlobal's owner set:
    1. If owner is a Document, then append owner to owningDocumentSet.
    2. If owner is a WorkerGlobalScope, set owningDocumentSet to the union of owningDocumentSet and owner's owning document set.
  4. Return owningDocumentSet.

The document has implicit focus steps given the argument document, are as follows:

  1. If document is not fully active, return false.
  2. Let relevantGlobal be document's relevant global object.
  3. For each origin in initiators of active Picture-in-Picture sessions:
    1. If relevantGlobal's relevant settings object's origin is same origin with origin, return true.
  4. If relevantGlobal's browsing context is capturing, return true.
  5. Let topLevelBC be relevantGlobal's browsing context's top-level browsing context.
  6. If topLevelBC does not have system focus, return false.
  7. Let focusedDocument be the topLevelBC's currently focused area's node document.
  8. If relevantGlobal's relevant settings object's origin is same origin with focusedDocument's origin, return true.
  9. Otherwise, return false.

The may receive data steps given the argument observer are as follows:

  1. Let relevantGlobal be observer's relevant global object.
  2. If relevantGlobal is a Window object:
    1. Return the result of running document has implicit focus with relevantGlobal's associated Document.
  3. If relevantGlobal is a WorkerGlobalScope object:
    1. Let owningDocuments be relevantGlobal's owning document set.
    2. For each document in owningDocuments:
      1. If the result of running document has implicit focus with document is true, return true.
      2. Otherwise, continue.
  4. Return false.
Note

The passes rate test steps given the argument observer, source and timestamp, are as follows:
  1. If observer.[[LastRecordMap]][source] does not exist, return true.
  2. Let record be observer.[[LastRecordMap]][source].
  3. Let sampleInterval be observer.[[SampleIntervalMap]][source].
  4. Let timeDeltaMilliseconds = timestamp - record.[[Time]].
  5. If timeDeltaMillisecondssampleInterval, return true, otherwise return false.
The has change in data steps given the argument observer, source, state, are as follows:
  1. If observer.[[LastRecordMap]][source] does not exist, return true.
  2. Let record be observer.[[LastRecordMap]][source].
  3. If record.[[State]] is not equal to state and change in contributing factors is substantial returns true, return true.
  4. Return false.
The passes rate obfuscation test steps given the argument observer, source, are as follows:
  1. Increment observer.[[ChangesCountMap]][source].
  2. Return observer.[[ChangesCountMap]][source] ≤ observer.[[MaxChangesThreshold]].

10.6.2 Data delivery

Data delivery from a platform collector can be activate and deactivated in an implementation-defined manner per source type and global object.

Note

The data delivery steps that are run when an implementation-defined data sample of source type source is obtained from global object relevantGlobal's platform collector, are as follows:

  1. Let source be the source type of the data sample.
  2. Let state be an adjusted pressure state given data and source.
  3. Let timestamp be a timestamp representing the time the data was obtained from the relevantGlobal's platform collector.
    Note
  4. For each observer in relevantGlobal's registered observer list for source:
    1. If running may receive data with observer returns false, continue.
    2. If running passes rate test with observer, source and timestamp returns false, continue.
    3. If running has change in data with observer, source and state returns false, continue.
    4. Let record be a new PressureRecord object with its [[Source]] set to source, [[State]] set to state and [[Time]] set to timestamp.
    5. If observer.[[AfterPenaltyRecordMap]][source] exists:
      1. Set observer.[[AfterPenaltyRecordMap]][source] to record.
      2. Continue.
    6. If running passes rate obfuscation test with observer and source returns false:
      1. Set observer.[[AfterPenaltyRecordMap]][source] to record.
      2. Set observer.[[ChangesCountMap]][source] to 0.
      3. Create timer of observer.[[PenaltyDuration]] duration with the following callback:
        1. If observer.[[AfterPenaltyRecordMap]][source] exists:
          1. Let record be observer.[[AfterPenaltyRecordMap]][source].
          2. Remove observer.[[AfterPenaltyRecordMap]][source].
          3. Run queue a record with observer, source, record.
      4. Continue.
    7. Run queue a record with observer, source, record.

10.6.3 Queue a PressureRecord

To queue a record given the arguments observer, source, record, run these steps:

  1. If size of observer.[[QueuedRecords]] is greater than max queued records, then remove the first item.
  2. Append record to observer.[[QueuedRecords]].
  3. Set observer.[[LastRecordMap]][source] to record.
  4. Queue a pressure observer task with observer's relevant global object.

10.6.4 Queue a Pressure Observer Task

The PressureObserver task source is a task source used for scheduling tasks to 10.6.5 Notify Pressure Observers.

To queue a pressure observer task given relevantGlobal as input, run these steps:

  1. If the relevantGlobal's pressure observer task queued is true, then return.
  2. Set the relevantGlobal's pressure observer task queued to true.
  3. Queue a global task on PressureObserver task source with relevantGlobal to notify pressure observers.

10.6.5 Notify Pressure Observers

To notify pressure observers given relevantGlobal as input, run these steps:

  1. Set relevantGlobal's pressure observer task queued to false.
  2. Let notifySet be a new set of all observers in relevantGlobal’s registered observer lists.
  3. For each observer of notifySet:
    1. Let records be a clone of observer.[[QueuedRecords]].
    2. Empty observer.[[QueuedRecords]].
    3. If records is not empty, then invoke observer.[[Callback]] with records and observer. If this throws an exception, catch it, and report the exception.

10.6.6 Handling change of fully active

When a Document document is no longer fully active, deactivate data delivery of data of all supported source types to document's relevant global object.

When a worker with associated WorkerGlobalScope relevantGlobal is no longer an active needed workers, deactivate data delivery of data of all supported source types to relevantGlobal.

When a Document document becomes fully active, for each non-empty registered observer list associated the source type source, activate data delivery of source data to document's relevant global object.

When a worker with associated WorkerGlobalScope relevantGlobal becomes an active needed workers, for each non-empty registered observer list associated the source type source, activate data delivery of source data to document's relevant global object.

Note

10.6.7 Handle unloading document and closing of workers

When a worker with associated WorkerGlobalScope relevantGlobal, once relevantGlobal's closing flag is set to true, deactivate data delivery for all supported source types to relevantGlobal.

As one of the unloading document cleanup steps given Document document, deactivate data delivery for all supported source types to document's relevant global object.

11. Security and privacy considerations

11.1 Types of privacy and security threats

Note
The Working Group will list any known attack vectors, both theoretical and real-world, in this section.

11.1.1 Timing attacks

It may be possible to identify users across non-same origin sites if unique or very precise values can be accessed at the same time by sites not sharing origin. This attack is mitigated by 11.2.1 Data minimization, 11.2.2 Rate-limiting change notifications, and 11.2.8 Same-origin restriction.

11.1.2 Cross-site covert channel

In computer security a covert channel creates a capability to transfer information between processes that are not supposed to be allowed to communicate. In modern multi-process web engines in the generic case each window or tab resides in its own process (documents that have the same origin or sites that have the same site typically share the same process). Using this API it may be possible to create a cross-site covert channel C where a site A on one tab first broadcasts to the channel C after having manipulated the state of the CPU. Next a site B (that is not same site with site A) on another tab reads the broadcasted data from the channel C by using this API to learn when the state of the CPU has changed. This process is repeated as long as the scripts run on both the sites A and B.

This attack is mitigated by 11.2.2 Rate-limiting change notifications, 11.2.3 Rate obfuscation and 11.2.6 Break calibration. Implementers are advised to consider all these mitigations for long-running scripts.

Note
The longer the scripts run the more information can be transmitted using the proposed cross-site covert channel. For example, if a user is on a video conferencing site and another long-running site that allows for more information to be transferred compared to a regular browsing scenario. On the other hand, a workload such as a video conferencing session will typically exert sustained pressure on the CPU that makes it harder to manipulate the pressure state in a predictive manner.

11.1.3 Targeted de-anonymization attacks

Targeted de-anonymization attacks constitute a critical class of threats that jeopardize a user's anonymity. These attacks allow a malicious or partially compromised website (referred to as the “malicious site”) to ascertain whether a website visitor possesses a specific public identifier, such as an email address or a social media handle.

While anonymity may be a luxury for some, for certain individuals, it is far more than that—it is a matter of survival. Consider for instance those who engage in political protests, work as journalists covering sensitive topics, etc.

As an example, an attacker can privately share a resource with the target for instance using a public resource sharing service (“victim site”), and then measure side-effects (indicating successful access) on loading the resource via side-channels. If the logged in visitor can access the embedded resource successfully, that indicates that the current visit is indeed the intended target.

Specifically, exposing reliable information about the total CPU pressure can let an attacking site understand if a target of a cross-origin navigation (e.g. an iframe or pop-up window from another site) performed a CPU-intensive operation.

Techniques such as pop-under and tab-under can be used to hide the loading from the user.

One possible attack is that the malicious website opens e.g., a popup to a resource on a victim site to which the user is logged in (e.g. a video streaming site or online document editor) pointing to a resource shared with specific users.

Assuming that loading the resource puts increased pressure on the CPU, this would create a side-channel reveals to the attacking site if the user is logged into an account with access to the resource, deanonymizing the user.

Given that modern CPUs recover quickly from high pressure, one possible mitigation strategy could be to temporarily disable readings for a few seconds after loading popup and iframe content.

11.2 Mitigation strategies

Note
This section gives a high-level view into mitigation strategies applicable to this specification. The normative definitions of these mitigations are integrated into the respective algorithms of this specification. Implementers are advised to consider the TAG guidance on private browsing modes when implementing the mitigations defined in this specification.

11.2.1 Data minimization

This specification adheres to the generic data minimization principles to limit exposure of data related to low-level details of the underlying platform to the minimum required to address its high-value use cases. This includes consideration for limiting exposure of identifying information about devices.

The specific application of data minimization principles in the context of this specification are discussed in 11.2.2 Rate-limiting change notifications and 11.2.8 Same-origin restriction.

11.2.2 Rate-limiting change notifications

By rate-limiting the delivery of the pressure state information we remove the attacker's ability to observe the precise time when a value transitions between two states.

More precisely, once the pressure observer is activated, it will be called once with initial values, and then is called when the values change. The subsequent calls will be rate-limited. When the callback is called, the most recent value is reported.

The specification will recommend a rate limit of at most one call per second for the active window, and one call per 10 seconds for all other windows. We will also recommend that the call timings are jittered across origins.

These measures benefit the user's privacy, by reducing the risk of identifying a device across multiple origins. The rate-limiting also benefits the user's security, by making it difficult to use this API for timing attacks. Last, rate-limiting change callbacks places an upper bound on the performance overhead of this API.

Rate limiting can be implemented in the user agent, but it might also be possible to simply change the polling/sampling rate of the underlying hardware counters, if not accessed via a higher level framework.

11.2.3 Rate obfuscation

The specification requires implementing the rate obfuscation mitigation to keep track of the number of pressure changes over an implementation-defined sliding observation window and set a flag if an implementation-defined threshold for the number of pressure changes is exceeded. Similarly, it is also recommended for the implementation to observe any abnormal activity such as a high number of pressure state changes spanning across multiple states, and set this flag similarly.

If this flag is set, the implementation is recommended to give the pressure observer a penalty during which it will not be able to inform scripts of changes in its pressure state as it normally would. The duration of this penalty is implementation-defined and it is recommended to be randomized. When notify pressure observers resumes operation after the penalty, it only reports the latest pressure state and disregards any interim state information received from the platform collector during this penalty.

11.2.4 Rate obfuscation normative parameters

Based on implementation experience, implementers must use:

  • a range in between 50 and 100 changes for PressureObserver's [[MaxChangesThreshold]] internal slot.
  • a range in between 5000 milliseconds and 10000 milliseconds for PressureObserver's [[PenaltyDuration]] internal slot.
Note

11.2.5 Rate obfuscation non-normative parameters

This section is non-normative.

Based on implementation experience, implementers are advised to use:

  • a range in between 300000 milliseconds (5 minutes) and 600000 milliseconds (10 minutes) for PressureObserver's [[ObservationWindow]] internal slot.
Note

11.2.6 Break calibration

In a calibration process an attacker tries to manipulate the CPU so that this API would report a transition into a certain pressure state with the highest probability in response to the pressure exerted by the fabricated workload. This break calibration mitigation solution can slow down or prevent this calibration process from succeeding by slightly changing at runtime the implementation-defined low-level hardware metrics that contribute to these pressure state transitions. Even if the initial calibration would succeed, its results will be invalidated at runtime when this mitigation is running continuously. Any attempts to recalibrate will similarly be mitigated against.

Note

11.2.7 Break calibration parameters

This section is non-normative.

Based on implementation experience, implementers are advised to apply the mitigation to a randomized time value within a range between 120000 milliseconds (2 minutes) and 240000 milliseconds (4 minutes).

Note

11.2.8 Same-origin restriction

By default data delivery is restricted to documents served from the same-origin as an initiator of an active picture-in-picture-session, documents capturing or the document with system focus, if any.

The documents qualifying for data delivery, under the above rules, can delegate it to documents in child navigables.

The feature can be extended to third-party contexts such as iframes only by a declared policy.

Shared workers can be shared across documents, such as top level document and those associated iframes. If one of the documents in the owner set passes the above data delivery requirements, the shared worker will qualify for data delivery. This means that the embedded iframe is able to pass along the data to the embedding document.

12. Accessibility considerations

The Compute Pressure API is focused on improving the user experience. There are two ways in which applications that build on the API can positively impact accessibility.

  1. Considering users' access needs when making decisions based on information gathered using the API.
  2. Designing and making user interfaces based on information gained from the API with accessibility in mind.

As a consumer of the API, it's important to consider both of these opportunities. Here are some examples:

13. Examples

This section is non-normative.

Example 2: How to access observer from callback
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");

In the following example we want to lower the number of concurrent video streams when the pressure becomes critical. For the sake of simplicity we only consider this one state.

As lowering the amount of streams might not result in exiting the critical state, or at least not immediately, we use a strategy where we lower one stream at the time every 30 seconds while still in the critical state.

We accomplish this by making sure the callback is called at least once every 30 seconds, or when the state actually changes. When the state changes we reset the interval timer.

Example 3: How to adjust the number of video feeds based on CPU pressure
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");

In the following example, we want to demonstrate the usage of takeRecords(), by retrieving the remaining records accumulated since the the callback was last invoked.

It is recommended to do so before disconnect(), otherwise disconnect() will clear them and they will be lost forever.

For example, we might want to measure the pressure during a benchmarking workload, and thus want pressure telemetry for the exact duration of the workload. This means disconnecting all observers immediately when the task is completed, and manually requesting any pending pressure telemetry up to this point that might not have been delivered yet as part of the event loop cycle.

Example 4: How to handle all state changes right up until disconnect
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();

In the following example, we show how to tell the observer to stop watching a specific source by invoking unobserve() with source.

Note
Example 5: How to tell the observer to stop watching for state changes for a specific source
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'.

In the following example, we show how to tell the observer to stop watching for any state changes by calling disconnect(). Calling disconnect() will stop observing all sources observed by previous observe() calls.

Additionally it will clear all pending records collected since the last callback was invoked.

Example 6: how to tell the observer to stop watching for any state changes
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();

14. Conformance

As well as sections marked as non-normative, all authoring guidelines, diagrams, examples, and notes in this specification are non-normative. Everything else in this specification is normative.

The key words MAY, MUST, and RECOMMENDED in this document are to be interpreted as described in BCP 14 [RFC2119] [RFC8174] when, and only when, they appear in all capitals, as shown here.

This specification defines conformance criteria for a single product: a user agent that implements the interfaces that it contains.

A. Acknowledgments

This section is non-normative.

Many thanks for valuable feedback and advice from Anssi Kostiainen, Asaf Yaffe, Chen Xing, Evan Shrubsole, 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, Raphael Kubo da Costa, Reilly Grant, Ulan Degenbaev, Victor Miura, Wei Wang, and Zhenyao Mo

Thanks to the W3C Privacy Interest Group (PING) and especially Peter Snyder for the privacy review, feedback and the proposed cross-site covert channel attack and its mitigations. Similarly thanks to Ehsan Toreini for his work on the privacy of private browsing and related contributions to this specification.

Special thanks to Amanda Zhao, Fidel Tian, Zhiliang Wang and others from the Zoom engineering team for the feedback and hands-on experiments that have helped improve this API in real-world scenarios.

B. IDL Index

WebIDLenum PressureSource { "thermals", "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> supportedSources;
};

[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. References

C.1 Normative references

[dom]
DOM Standard. Anne van Kesteren. WHATWG. Living Standard. URL: https://dom.spec.whatwg.org/
[hr-time]
High Resolution Time. Yoav Weiss. W3C. 19 July 2023. W3C Working Draft. URL: https://www.w3.org/TR/hr-time-3/
[html]
HTML Standard. Anne van Kesteren; Domenic Denicola; Ian Hickson; Philip Jägenstedt; Simon Pieters. WHATWG. Living Standard. URL: https://html.spec.whatwg.org/multipage/
[infra]
Infra Standard. Anne van Kesteren; Domenic Denicola. WHATWG. Living Standard. URL: https://infra.spec.whatwg.org/
[mediacapture-streams]
Media Capture and Streams. Cullen Jennings; Bernard Aboba; Jan-Ivar Bruaroey; Henrik Boström; youenn fablet. W3C. 7 March 2024. W3C Candidate Recommendation. URL: https://www.w3.org/TR/mediacapture-streams/
[PERMISSIONS-POLICY]
Permissions Policy. Ian Clelland. W3C. 18 December 2023. W3C Working Draft. URL: https://www.w3.org/TR/permissions-policy-1/
[RFC2119]
Key words for use in RFCs to Indicate Requirement Levels. S. Bradner. IETF. March 1997. Best Current Practice. URL: https://www.rfc-editor.org/rfc/rfc2119
[RFC8174]
Ambiguity of Uppercase vs Lowercase in RFC 2119 Key Words. B. Leiba. IETF. May 2017. Best Current Practice. URL: https://www.rfc-editor.org/rfc/rfc8174
[WEBIDL]
Web IDL Standard. Edgar Chen; Timothy Gu. WHATWG. Living Standard. URL: https://webidl.spec.whatwg.org/

C.2 Informative references

[ECMAScript]
ECMAScript Language Specification. Ecma International. URL: https://tc39.es/ecma262/multipage/