Web Periodic Background Synchronization

Draft Community Group Report,

This version:
https://wicg.github.io/periodic-background-sync/index.html
Issue Tracking:
GitHub
Editors:
(Google)
(Google)

Abstract

This specification describes a method that enables web applications to periodically synchronize data and content in the background.

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 often run in environments with unreliable networks (e.g., mobile phones) and unknown lifetimes (the browser might be killed or the user might navigate away). This makes it difficult for web apps to keep their content and state in sync with servers.

This API is intended to reduce the time between content creation and content synchronization between the servers and the web app. It does so by letting the web app register an intent to periodically synchronize state and data, with a minimum interval it wishes to do so at. Through a service worker event, the user agent then periodically lets the web app download network resources and update state.

As this API relies on service workers, functionality provided by this API is only available in a secure context.

1.1. Example

Registering periodic background sync at a mininimum interval of one day from a browsing context:
async function registerPeriodicNewsCheck() {
  const registration = await navigator.serviceWorker.ready;
  try {
    await registration.periodicSync.register('fetch-news', {
      minInterval: 24 * 60 * 60 * 1000,
    });
  } catch {
    console.log('Periodic Sync could not be registered!');
  }
}

Reacting to a periodicsync event within a service worker:

self.addEventListener('periodicsync', event => {
  event.waitUntil(fetchAndCacheLatestNews());
});

In the above example fetchAndCacheLatestNews is a developer-defined function is a developer-defined function that fetches the latest news articles from a server and stores them locally, for example using the Cache API, for offline consumption.

2. Concepts

When a periodicsync event is fired for a periodic sync registration registration, it is considered to run in the background if no service worker clients whose frame type is "top-level", "auxiliary" or "nested" exist for the origin of the service worker registration associated with registration.

3. Extensions to service worker registration

A service worker registration additionally has:

4. Constructs

4.1. Periodic sync registration

A periodic sync registration consists of:
A service worker registration, which is a service worker registration.

A tag, which is a DOMString.

Note: Periodic Background Sync doesn’t share namespace with Background Sync, so an origin can have registrations of both types with the same tag.

minimum interval (a long long), which is used to specify the minimum interval, in milliseconds, at which the periodic synchronization should happen. minimum interval is a suggestion to the user agent.

Note: The actual interval at which periodicsync events are fired MUST be greater than or equal to this.

An anchor time (a timestamp), the previous time a periodicsync event fired for this periodic sync registration, or the time of initial registration.

A state, which is one of "pending", "firing", "suspended" or "reregistered-while-firing". It is initially set to "pending".

4.2. Periodic Sync Scheduler

The periodic sync scheduler is responsible for scheduling firing of periodicsync events. In response to these triggers, the scheduler either schedules delayed processing to fire a periodicsync event at the appropriate time in the future, or cancels such scheduling.
The scheduler maintains:

The effective minimum sync interval for origin origin (an origin), is the minimum periodic sync interval for any origin + some user agent defined amount for the origin.

Note: The user agent defined amount could be based off the amount of engagement the user has with the origin. This value can be different each time effective minimum sync interval for origin is called.

The scheduler processes periodic sync registrations.

Note: Browsers may suspend this processing loop when there are no active periodic sync registrations to conserve resources.

4.3. Constants

As recommended in § 5 Privacy Considerations and § 6 Resource Usage, the user agent SHOULD also define:

minimum periodic sync interval across origins MUST be greater than or equal to minimum periodic sync interval for any origin. If undefined, these are set to 43200000, which is twelve hours in milliseconds.

Note: Two caps on frequency are needed because conforming to the cap enforced by minimum periodic sync interval for any origin for each origin can still cause the browser to fire periodicsync events very frequently. This can happen, for instance, when there are many periodic sync registrations for different origins. minimum periodic sync interval across origins ensures there’s a global cap on how often these events are fired.

The user agent MAY define a maximum number of retries, a number, allowed for each periodicsync event. In choosing this, the user agent SHOULD ensure that the time needed to attempt the maximum number of retries is an order of magnitude smaller than the minimum periodic sync interval for any origin. If undefined, this number is zero.

5. Privacy Considerations

5.1. Permission

Periodic Background Sync is only available if the PermissionState for a PermissionDescriptor with name "periodic-background-sync" is granted. In addition, user agents SHOULD offer a way for the user to disable Periodic Background Sync. When Periodic Background Sync is disabled, periodicsync events MUST NOT be dispatched for the periodic sync registrations affected by this permission. (See § 7.2 Respond to permission revocation).

5.2. Location Tracking

Fetch requests within the periodicsync event while in the background may reveal the client’s IP address to the server after the user has left the page. The user agent SHOULD limit tracking by capping the number of retries and duration of periodicsync events, to reduce the amount of time the user’s location can be tracked by the website. Further, the user agent SHOULD limit persistent location tracking by capping the frequency of periodicsync events, both for an origin, and across origins.

5.3. History Leaking

Fetch requests within the periodicsync event while in the background may reveal something about the client’s navigation history to middleboxes on networks different from the one used to create the periodic sync registration. For instance, the client might visit site https://example.com, which registers a periodicsync event, but based on the implementation, might not fire until after the user has navigated away from the page and changed networks. Middleboxes on the new network may see the fetch requests that the periodicsync event makes. The fetch requests are HTTPS so the request contents will not be leaked but the destination of the fetch request and domain may be (via DNS lookups and IP address of the request). To prevent this leakage of browsing history, the user agent MAY choose to only fire periodicsync events on the network the periodic sync registration was made on, with the understanding that it will reduce usability by not allowing synchronization opportunistically.

6. Resource Usage

This section is non-normative.

A website will most likely download resources from the network when processing a periodicsync event. The underlying operating system may launch the user agent to dispatch these events, and may keep it awake for a pre-defined duration to allow processing of the events. Both cause battery drain. The user agent should cap the duration and frequency of these events to limit resource usage by websites when the user has navigated away.

Large resources should be downloaded by registering a background fetch via the BackgroundFetchManager interface.

In addition, the user agent should consider other factors such as user engagement with the origin, and any user indications to temporarily reduce data consumption, such as a Data Saving Mode, to adjust the frequency of periodicsync events.

7. Algorithms

7.1. Process periodic sync registrations

When the user agent starts, run the following steps in parallel:
  1. While true:

    1. Wait for minimum periodic sync interval across origins.

    2. Let firedPeriodicSync be false.

    3. While firedPeriodicSync is false:

      1. Wait for some user agent defined amount of time.

        Note: This can be used to group synchronization for different periodic sync registrations into a single device wake-up.

      2. Wait until online.

      3. For each service worker registration registration that is not unregistered, enqueue the following steps to registration’s periodic sync processing queue:

        1. Let origin be the origin associated with periodicSyncRegistration’s service worker registration.

        2. If time of last fire[origin] + the effective minimum sync interval for origin origin is greater than now, continue.

        3. For each periodic sync registration periodicSyncRegistration in registration’s active periodic sync registrations:

          1. If periodicSyncRegistration’s state is not "pending", continue.

          2. If periodicSyncRegistration’s anchor time + periodicSyncRegistration’s minimum interval is greater than now, continue.

          3. Set firedPeriodicSync to true.

          4. Fire a periodicsync event for periodicSyncRegistration.

7.2. Respond to permission revocation

To respond to revocation of the permission with name "periodic-background-sync" for origin origin, the user agent MUST enqueue the following steps to the periodic sync processing queue:
  1. For each periodic sync registration registration in active periodic sync registrations whose service worker registration is associated with the same origin as origin:

    1. Remove registration from active periodic sync registrations.

8. API Description

8.1. Extensions to the ServiceWorkerGlobalScope interface

partial interface ServiceWorkerGlobalScope {
    attribute EventHandler onperiodicsync;
};

8.2. Extensions to the ServiceWorkerRegistration interface

[Exposed=(Window,Worker)]
partial interface ServiceWorkerRegistration {
  readonly attribute PeriodicSyncManager periodicSync;
};
A ServiceWorkerRegistration has a periodic sync manager (a PeriodicSyncManager).

The periodicSync attribute’s getter must return the context object's periodic sync manager, initially a new PeriodicSyncManager whose service worker registration is the context object's service worker registration.

8.3. PeriodicSyncManager interface

[Exposed=(Window,Worker)]
interface PeriodicSyncManager {
    Promise<void> register(DOMString tag, optional BackgroundSyncOptions options);
    Promise<sequence<DOMString>> getTags();
    Promise<void> unregister(DOMString tag);
};

dictionary BackgroundSyncOptions {
    [EnforceRange] unsigned long long minInterval = 0;
};
A PeriodicSyncManager has a service worker registration (a service worker registration).
The register(tag, options) method, when invoked, MUST return a new promise promise and enqueue the following steps to the periodic sync processing queue:
  1. Let serviceWorkerRegistration be the service worker registration associated with the context object's PeriodicSyncManager.

  2. If serviceWorkerRegistration’s active worker is null, reject promise with an InvalidStateError and abort these steps.

  3. If the PermissionState for a PermissionDescriptor with name "periodic-background-sync" is not granted, reject promise with a NotAllowedError and abort these steps.

  4. Let isBackground, a boolean, be true.

  5. For each client in the service worker clients for the serviceWorkerRegistration’s origin:

    1. If client’s frame type is "top-level" or "auxiliary", set isBackground to false.

  6. If isBackground is true, reject promise with an InvalidAccessError and abort these steps.

  7. Let currentRegistration be the periodic sync registration in serviceWorkerRegistration’s active periodic sync registrations whose tag equals tag if it exists, else null.

  8. If currentRegistration is null:

    1. Let newRegistration be a new periodic sync registration.

    2. Set newRegistration’s tag to tag.

    3. Set newRegistration’s minimum interval to optionsminInterval member.

    4. Set newRegistration’s state to "pending".

    5. Set newRegistration’s service worker registration to serviceWorkerRegistration.

    6. Set newRegistration’s anchor time to a timestamp representing now.

    7. Add newRegistration to serviceWorkerRegistration’s active periodic sync registrations.

    8. Resolve promise.

  9. Else:

    1. If currentRegistration’s minimum interval is different to optionsminInterval member:

      1. Set currentRegistration’s minimum interval to optionsminInterval member.

    2. Else, if currentRegistration’s state is "firing", set serviceWorkerRegistration’s state to "reregistered-while-firing".

    3. Resolve promise.

The getTags() method when invoked, MUST return a new promise promise and enqueue the following steps to the periodic sync processing queue:
  1. Let serviceWorkerRegistration be the service worker registration associated with the context object's PeriodicSyncManager.

  2. Let currentTags be a new list.

  3. For each registration of serviceWorkerRegistration’s active periodic sync registrations, append registration’s tag to currentTags.

  4. Resolve promise with currentTags.

The unregister(tag) method when invoked, MUST return a new promise promise and enqueue the following steps to the periodic sync processing queue:
  1. Let serviceWorkerRegistration be the service worker registration associated with the context object's PeriodicSyncManager.

    1. Let currentRegistration be the periodic sync registration in serviceWorkerRegistration’s active periodic sync registrations whose tag equals tag if it exists, else null.

    2. If currentRegistration is not null, remove currentRegistration from serviceWorkerRegistration’s active periodic sync registrations.

    3. Resolve promise.

8.4. The periodicsync event

dictionary PeriodicSyncEventInit : ExtendableEventInit {
    required DOMString tag;
};

[
    Constructor(DOMString type, PeriodicSyncEventInit init),
    Exposed=ServiceWorker
] interface PeriodicSyncEvent : ExtendableEvent {
    readonly attribute DOMString tag;
  };
A PeriodicSyncEvent has a tag (a tag). The tag attribute must return the value it was initialized to.

8.4.1. Fire a periodicsync event

Note: A user agent MAY impose a time limit on the lifetime extension and execution time of a PeriodicSyncEvent which is stricter than the time limit imposed for ExtendableEvents in general. In particular, any retries of the PeriodicSyncEvent MAY have a significantly shortened time limit.
To fire a periodicsync event for a periodic sync registration registration, the user agent MUST run the following steps:
  1. Let serviceWorkerRegistration be registration’s service worker registration.

  2. If registration is no longer part of serviceWorkerRegistration’s active periodic sync registrations, abort these steps.

  3. Assert: registration’s state is "pending".

  4. Let retryCount be 0.

  5. Set registration’s state to "firing".

  6. While true:

    1. Let continue be false.

    2. Let success be false.

    3. Fire functional event "periodicsync" using PeriodicSyncEvent on serviceWorkerRegistration with tag set to registration’s tag. Let dispatchedEvent, an ExtendableEvent, represent the dispatched periodicsync event and run the following steps with dispatchedEvent:

    4. Let waitUntilPromise be the result of waiting for all of dispatchedEvent’s extend lifetime promises.

    5. React to the fulfillment of waitUntilPromise with the following steps:

      1. Set success to true.

      2. Set continue to true.

    6. React to rejection rejection of waitUntilPromise with the following steps:

      1. Set continue to true.

    7. In parallel:

      1. Wait for continue to be true.

      2. Let origin be the origin associated with registration’s service worker registration.

      3. If success is true, set time of last fire for key origin to the current time.

      4. If success is true or retryCount is greater than maximum number of retries or registration’s state is "reregistered-while-firing", then:

        1. Set registration’s state to "pending".

        2. Set registration’s anchor time to a timestamp representing now.

        3. Abort these steps.

    8. Increment retryCount.

    9. Wait for a small back-off time based on retryCount.

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

[BACKGROUND-FETCH]
Background Fetch. cg-draft. URL: https://wicg.github.io/background-fetch/
[DOM]
Anne van Kesteren. DOM Standard. Living Standard. URL: https://dom.spec.whatwg.org/
[HTML]
Anne van Kesteren; et al. HTML Standard. Living Standard. URL: https://html.spec.whatwg.org/multipage/
[INFRA]
Anne van Kesteren; Domenic Denicola. Infra Standard. Living Standard. URL: https://infra.spec.whatwg.org/
[PERMISSIONS]
Mounir Lamouri; Marcos Caceres; Jeffrey Yasskin. Permissions. 25 September 2017. WD. URL: https://www.w3.org/TR/permissions/
[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. 15 September 2016. CR. URL: https://www.w3.org/TR/secure-contexts/
[SERVICE-WORKERS-1]
Alex Russell; et al. Service Workers 1. 19 November 2019. CR. URL: https://www.w3.org/TR/service-workers-1/
[WEB-BACKGROUND-SYNC]
Web Background Synchronization. cg-draft. URL: https://wicg.github.io/BackgroundSync/spec/
[WebIDL]
Boris Zbarsky. Web IDL. 15 December 2016. ED. URL: https://heycam.github.io/webidl/

IDL Index

partial interface ServiceWorkerGlobalScope {
    attribute EventHandler onperiodicsync;
};

[Exposed=(Window,Worker)]
partial interface ServiceWorkerRegistration {
  readonly attribute PeriodicSyncManager periodicSync;
};

[Exposed=(Window,Worker)]
interface PeriodicSyncManager {
    Promise<void> register(DOMString tag, optional BackgroundSyncOptions options);
    Promise<sequence<DOMString>> getTags();
    Promise<void> unregister(DOMString tag);
};

dictionary BackgroundSyncOptions {
    [EnforceRange] unsigned long long minInterval = 0;
};

dictionary PeriodicSyncEventInit : ExtendableEventInit {
    required DOMString tag;
};

[
    Constructor(DOMString type, PeriodicSyncEventInit init),
    Exposed=ServiceWorker
] interface PeriodicSyncEvent : ExtendableEvent {
    readonly attribute DOMString tag;
  };