Web Budget API

Editor’s Draft,

This version:
https://wicg.github.io/budget-api/
Issue Tracking:
GitHub
Inline In Spec
Editor:
(Google)

Abstract

This specification describes an API that can be used to retrieve the amount of budget an origin has available for resource consuming background operations, as well as the cost associated with doing such an operation.

Status of this document

This specification was published by the Web Platform Incubator Community Group. It is not a W3C Standard nor is it on the W3C Standards Track. Please note that under the W3C Community Contributor License Agreement (CLA) there is a limited opt-out and other conditions apply. Learn more about W3C Community and Business Groups.

1. Introduction

This section is non-normative.

Web Applications have conventionally been able to execute code, make network requests and interact with the user by means of established interaction, usually through a browser tab. This has allowed users to associate the presence of a browser tab with the Web Application’s ability to do work on their behalf.

Following the introduction of the Push API [PUSH-API] and Web Background Synchronization [WEB-BACKGROUND-SYNC], this assumption no longer stands. Web Applications are now able to both trigger and schedule execution of code in the background, outside of the user’s control.

In an effort to mitigate risk to the user, user agents have implemented restrictions such as time limits on executing code in the background, or a requirement for the Web Application to use the Web Notification API [NOTIFICATIONS] to inform the user of the work they’ve done. Those restrictions are often unspecified and left up to the discretion of the user agent. In some cases, user agents will choose to not enforce these restrictions depending on the intensity of the user’s engagement with the Web Application.

This specification describes an API that exposes a budget that can be used by authors to determine their current budget for resource consuming background operations, as well as the cost associated with doing a certain background operation.

Because this API relates to the ability to do work in the background, which is considered a privilege, functionality provided by this API is only available in a secure context.

This specification does not define how user agents establish or store the amount of current budget. It aims to define an API that exposes sufficient information to make the budget useful for authors, while not restricting the implementation details and heuristics specific to a user agent.

1.1. Current budget

There are various use-cases for needing to know the currently available budget:

  • Deciding to not show a notification in response to a low priority push message whose primary purpose was to synchronize data.
  • Deciding whether the origin can schedule a precise timer using a hypothetical Job Scheduler API.

Determine whether a user visible interaction is required in response to a push message:

self.addEventListener('push', event => {
    // Execute the application-specific logic depending on the contents of the
    // received push message, for example by caching the latest content.

    event.waitUntil(
        navigator.budget.reserve('silent-push').then(reserved => {
            if (reserved)
                return;  // No need to show a notification.

            // Not enough budget is available, must show a notification.
            return registration.showNotification(...);
        })
    );
});

1.2. Expected budget

There are various use-cases for needing to know the budget in advance:

  • Deciding on the frequency of server-initiated cache updates of synchronized data.
  • Deciding on the server whether there is sufficient budget available to hide previously shown notifications when the user dismissed them on other devices.
  • Deciding to temporarily limit background operations if the budget could be used during an upcoming sporting event instead.

Add an example that demonstrates a one of these use-cases.

2. Concepts

The user engagement with an origin is defined by the intensity of their interaction with the application by means of navigation, interaction and retention signals.

A background operation is the ability for an origin to execute potentially resource consuming code in the background.

The background operation cost is a non-negative number that describes the cost of executing a background operation on the user’s device.

An origin has an associated budget, which is a non-negative number derived from the user engagement that describes how many background operations the origin is able to do, depending on the associated background operation costs.

An origin has an associated list of budget expectations. This starts with the origin’s currently available budget, followed by zero or more entries indicating the lower bound of available budget at known points in the future.

User agents are not required to maintain future-bound budget expectations, but doing so enables more use-cases for authors.

Part of an origin’s available budget can be reserved. This reduces the origin’s current budget by the given cost.

The reserved cost of certain background operations could be less than the cost indicated by getCost() when the user’s device is in favorable conditions, for example because it’s not on battery power.

3. Security and Privacy Considerations

3.1. Applicability

Applicability of the Budget API is limited to potentially resource consuming background operations— operations that are not sufficiently privacy sensitive to need express user permission for basic usage.

User agents MUST NOT use the Budget API as an alternative to obtaining express user permission for privacy-sensitive operations such as accurate location access [GEOLOCATION-API] and access to the user’s camera and/or microphone [WEBRTC].

Examples include Web Background Sync [WEB-BACKGROUND-SYNC], which may have execution time and retry limitations applied by the user agent, and the Push API [PUSH-API] in situations where the effects of a push message are not immediately visible to the user.

3.1.1. Location Tracking

Fetch requests within background operations may reveal the client’s IP address to the server after the user left the page. The user agent SHOULD limit tracking by capping the duration of background operations.

3.2. Permissions

The Budget API provides an alternative to obtaining express user permission where the user agent believes it can appropriately protect the user for strictly resource consuming background operations.

Both the APIs described in this document, as well as the specifications that depend on this document, MUST NOT limit the user agent’s ability to require express user permission in addition to budget requirements.

User agents that require express user permission for certain background operations MAY lower or eliminate the background operation cost of such an operation, because the user has explicitly allowed the Web Application to engage on their behalf.

4. API

[Exposed=Window]
partial interface Navigator {
    [SameObject] readonly attribute BudgetService budget;
};
[Exposed=Worker]
partial interface WorkerNavigator {
    [SameObject] readonly attribute BudgetService budget;
};

The budget attribute’s getter must return a BudgetService scoped to the entry settings object’s origin.

4.2. The BudgetService interface

The BudgetService interface represents the programmatic interface to the user agent’s budget service. It is available in both document and worker environments.

[Exposed=(Window,Worker)]
interface BudgetService {
    Promise<double> getCost(OperationType operation);
    Promise<sequence<BudgetState>> getBudget();

    Promise<boolean> reserve(OperationType operation);
};

The getCost() method returns a promise that will be resolved with the worst-case background operation cost of the indicated background operation.

When invoked, it MUST run the following steps:

  1. Let promise be a new promise.
  2. Let origin be the entry settings object’s origin.
  3. If the origin is not a secure context, reject promise with a SecurityError and terminate these steps.
  4. Return promise and run the following step in parallel:
    1. Resolve the promise with the worst-case background operation cost associated with the given operation.

The getBudget() method returns a promise that will be resolved with a sequence of BudgetState objects indicating the expected state of the budget at given times in the future.

When invoked, it MUST run the following steps:

  1. Let promise be a new promise.
  2. Let origin be the entry settings object’s origin.
  3. If the origin is not a secure context, reject promise with a SecurityError and terminate these steps.
  4. Return promise and run the following step in parallel:
    1. Let details be a new sequence.
    2. If there are entries in origin’s list of budget expectations, for each entry:
      1. Let state be a new BudgetState instance.
      2. Set state’s budgetAt attribute to entry’s budget value.
      3. Set state’s time attribute to the DOMTimeStamp representing the final date of entry’s validity in milliseconds since 00:00:00 UTC on 1 January 1970.
      4. Add state to details.

      Otherwise:

      1. Let state be a new BudgetState instance.
      2. Set state’s budgetAt attribute to 0.
      3. Set state’s time attribute to the DOMTimeStamp representing the current time in milliseconds since 00:00:00 UTC on 1 January 1970.
      4. Add state to details.
    3. Resolve the promise with details.

The reserve() method returns a promise that will be resolved with a boolean indicating whether the requested budget for operation could be reserved.

When invoked, it MUST run the following steps:

  1. Let promise be a new promise.
  2. Let origin be the entry settings object’s origin.
  3. If the origin is not a secure context, reject promise with a SecurityError and terminate these steps.
  4. Return promise and run the following step in parallel:
    1. Let budget be the amount of budget available to origin.
    2. Let cost be the background operation cost associated with operation.
    3. If cost is greater than budget, resolve the promise with the boolean false and abort these steps.
    4. Reserve the cost from origin’s budget and resolve the promise with the boolean true.

4.3. The BudgetState interface

The BudgetState interface represents the amount of budget available at a specific point in time. This enables authors to make near-term decisions about how to spend their budget.

[Exposed=(Window,Worker)]
interface BudgetState {
  readonly attribute double budgetAt;
  readonly attribute DOMTimeStamp time;
};

The budgetAt attribute’s getter must return the budget at the associated time.

The time attribute’s getter must return the timestamp representing the time, in milliseconds since 00:00:00 UTC on 1 January 1970, at which the budgetAt will be valid.

4.4. The OperationType enum

The OperationType enumeration describes the known set of background operations that the Web Budget API caters for. Authors can use this in combination with getCost() to interpret their available budget as a quantifiable set of background operations it can be used for.

enum OperationType {
  "silent-push"
};

The following OperationType values are defined:

  • The silent-push value represents a background operation in response to an incoming push message through the Push API that does not result in a user visible action. [PUSH-API]

Specifications are encouraged to extend the OperationType enumeration with their own values. Naming consistency with the Permission API [PERMISSIONS], where applicable, is recommended.

Conformance

Conformance requirements are expressed with a combination of descriptive assertions and RFC 2119 terminology. The key words “MUST”, “MUST NOT”, “REQUIRED”, “SHALL”, “SHALL NOT”, “SHOULD”, “SHOULD NOT”, “RECOMMENDED”, “MAY”, and “OPTIONAL” in the normative parts of this document are to be interpreted as described in RFC 2119. However, for readability, these words do not appear in all uppercase letters in this specification.

All of the text of this specification is normative except sections explicitly marked as non-normative, examples, and notes. [RFC2119]

Examples in this specification are introduced with the words “for example” or are set apart from the normative text with class="example", like this:

This is an example of an informative example.

Informative notes begin with the word “Note” and are set apart from the normative text with class="note", like this:

Note, this is an informative note.

Index

Terms defined by this specification

Terms defined by reference

References

Normative References

[HTML]
Anne van Kesteren; et al. HTML Standard. Living Standard. URL: https://html.spec.whatwg.org/multipage/
[PROMISES-GUIDE]
Domenic Denicola. Writing Promise-Using Specifications. 16 February 2016. Finding of the W3C TAG. URL: https://www.w3.org/2001/tag/doc/promises-guide
[PUSH-API]
Michael van Ouwerkerk; et al. Push API. URL: https://w3c.github.io/push-api/
[RFC2119]
S. Bradner. Key words for use in RFCs to Indicate Requirement Levels. March 1997. Best Current Practice. URL: https://tools.ietf.org/html/rfc2119
[SECURE-CONTEXTS]
Mike West. Secure Contexts. URL: https://w3c.github.io/webappsec-secure-contexts/
[WEB-BACKGROUND-SYNC]
Web Background Synchronization specification draft. Living Standard. URL: https://wicg.github.io/BackgroundSync/spec/
[WebIDL]
Cameron McCormack; Boris Zbarsky; Tobie Langel. Web IDL. URL: https://heycam.github.io/webidl/

Informative References

[GEOLOCATION-API]
Andrei Popescu. Geolocation API Specification 2nd Edition. URL: http://dev.w3.org/geo/api/spec-source.html
[NOTIFICATIONS]
Anne van Kesteren. Notifications API Standard. Living Standard. URL: https://notifications.spec.whatwg.org/
[PERMISSIONS]
Mounir Lamouri; Marcos Caceres. The Permissions API. URL: https://w3c.github.io/permissions/
[WEBRTC]
Adam Bergkvist; et al. WebRTC 1.0: Real-time Communication Between Browsers. URL: https://w3c.github.io/webrtc-pc/

IDL Index

[Exposed=Window]
partial interface Navigator {
    [SameObject] readonly attribute BudgetService budget;
};

[Exposed=Worker]
partial interface WorkerNavigator {
    [SameObject] readonly attribute BudgetService budget;
};

[Exposed=(Window,Worker)]
interface BudgetService {
    Promise<double> getCost(OperationType operation);
    Promise<sequence<BudgetState>> getBudget();

    Promise<boolean> reserve(OperationType operation);
};

[Exposed=(Window,Worker)]
interface BudgetState {
  readonly attribute double budgetAt;
  readonly attribute DOMTimeStamp time;
};

enum OperationType {
  "silent-push"
};

Issues Index

Add an example that demonstrates a one of these use-cases.