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 to synchronize client data from web apps (such as photo uploads, document changes, or composed emails) with servers. If the browser closes or the user navigates away before synchronization can complete, the app must wait until the user revisits the page to try again. This specification provides a new onsync service worker event which can fire in the background so that synchronization attempts can continue despite adverse conditions when initially requested. This API is intended to reduce the time between content creation and content synchronization with the server.
As this API relies on service workers, functionality provided by this API is only available in a secure context.
function sendChatMessage( message) { return addChatMessageToOutbox( message). then(() => { // Wait for the scoped service worker registration to get a // service worker with an active state return navigator. serviceWorker. ready; }). then( reg=> { return reg. sync. register( 'send-chats' ); }). then(() => { console. log( 'Sync registered!' ); }). catch (() => { console. log( 'Sync registration failed :(' ); }); }
In the above example addChatMessageToOutbox
is a developer-defined function.
Reacting to a sync event within a service worker:
self. addEventListener( 'sync' , event=> { if ( event. tag== 'send-chats' ) { event. waitUntil( getMessagesFromOutbox(). then( messages=> { // Post the messages to the server return fetch( '/send' , { method: 'POST' , body: JSON. stringify( messages), headers: { 'Content-Type' : 'application/json' } }). then(() => { // Success! Remove them from the outbox return removeMessagesFromOutbox( messages); }); }). then(() => { // Tell pages of your success so they can update UI return clients. matchAll({ includeUncontrolled: true }); }). then( clients=> { clients. forEach( client=> client. postMessage( 'outbox-processed' )) }) ); } });
In the above example getMessagesFromOutbox
and removeMessagesFromOutbox
are developer-defined functions.
2. Concepts
The sync event is considered to run in the background if no service worker clients whose frame type is top-level or auxiliary exist for the origin of the corresponding service worker registration.
The user agent is considered to be online if the user agent has established a network connection. A user agent MAY use a stricter definition of being online. Such a stricter definition MAY take into account the particular service worker or origin a sync registration is associated with.
3. Constructs
A service worker registration has an associated list of sync registrations whose element type is a sync registration.A sync registration is a tuple consisting of a tag and a state.
A sync registration has an associated tag, a DOMString.
A sync registration has an associated registration state, which is one of pending, waiting, firing, or reregisteredWhileFiring. It is initially set to pending.
A sync registration has an associated service worker registration. It is initially set to null.
Within one list of sync registrations each sync registration MUST have a unique tag.
4. Permissions Integration
The Web Background Synchronization API is a default powerful feature that is identified by the name "background-sync".5. Privacy Considerations
5.1. Permission
User agents MAY offer a way for the user to disable background sync.Note: Background sync SHOULD be enabled by default. Having the permission denied is considered an exceptional case.
5.2. Location Tracking
Fetch requests within the onsync event while in the background 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 number of retries and duration of sync events.5.3. History Leaking
Fetch requests within the onsync event while in the background may reveal something about the client’s navigation history to passive eavesdroppers. For instance, the client might visit site https://example.com, which registers a sync event, but doesn’t fire until after the user has navigated away from the page and changed networks. Passive eavesdroppers on the new network may see the fetch requests that the onsync event makes. The fetch requests are HTTPS so the request contents will not be leaked but the domain may be (via DNS lookups and IP address of the request).6. API Description
6.1. Extensions to the ServiceWorkerRegistration
interface
ServiceWorkerRegistration/sync
In only one current engine.
Opera36+Edge79+
Edge (Legacy)NoneIENone
Firefox for AndroidNoneiOS SafariNoneChrome for Android49+Android WebView49+Samsung Internet4.0+Opera Mobile36+
partial interface ServiceWorkerRegistration {readonly attribute SyncManager ; };
sync
The
attribute exposes a sync
SyncManager
, which has an associated service worker registration represented by the ServiceWorkerRegistration
on which the attribute is exposed.
6.2. SyncManager
interface
In only one current engine.
OperaNoneEdge79+
Edge (Legacy)NoneIENone
Firefox for AndroidNoneiOS SafariNoneChrome for Android49+Android WebView49+Samsung Internet5.0+Opera MobileNone
[Exposed =(Window ,Worker )]interface {
SyncManager Promise <undefined >register (DOMString );
tag Promise <sequence <DOMString >>getTags (); };
In only one current engine.
OperaNoneEdge79+
Edge (Legacy)NoneIENone
Firefox for AndroidNoneiOS SafariNoneChrome for Android49+Android WebView49+Samsung Internet5.0+Opera MobileNone
The
method, when invoked, MUST return a new promise promise and run the following steps in parallel:register(tag)
- Let serviceWorkerRegistration be the
SyncManager
's associated service worker registration. - If serviceWorkerRegistration’s active worker is null, reject promise with an
InvalidStateError
and abort these steps. - If the user has disabled background sync, reject promise with an
NotAllowedError
and abort these steps. - Let isBackground be true.
-
For each client in the service worker clients for the serviceWorkerRegistration’s origin:
- If client’s frame type is top-level or auxiliary, set isBackground to false.
- If isBackground is true, reject promise with an
InvalidAccessError
and abort these steps. - Let currentRegistration be the registration in serviceWorkerRegistration’s list of sync registrations whose tag equals tag if it exists, else null.
-
If currentRegistration is not null:
- If currentRegistration’s registration state is waiting, set currentRegistration’s registration state to pending.
- If currentRegistration’s registration state is firing, set currentRegistration’s registration state to reregisteredWhileFiring.
- Resolve promise.
- If the user agent is currently online and currentRegistration’s registration state is pending, fire a sync event for currentRegistration.
-
Else:
- Let newRegistration be a new sync registration.
- Set newRegistration’s associated tag to tag.
- Set newRegistration’s associated service worker registration to serviceWorkerRegistration.
- Add newRegistration to serviceWorkerRegistration’s list of sync registrations.
- Resolve promise.
- If the user agent is currently online, fire a sync event for newRegistration.
In only one current engine.
OperaNoneEdge79+
Edge (Legacy)NoneIENone
Firefox for AndroidNoneiOS SafariNoneChrome for Android49+Android WebView49+Samsung Internet5.0+Opera MobileNone
The
method when invoked, MUST return a new promise promise and run the following steps in parallel:getTags()
- Let serviceWorkerRegistration be the
SyncManager
's associated service worker registration. - Let currentTags be a new
sequence
. - For each registration in serviceWorkerRegistration’s list of sync registrations, add registration’s associated tag to currentTags.
- Resolve promise with currentTags.
6.3. The sync event
In only one current engine.
Opera?Edge79+
Edge (Legacy)NoneIENone
Firefox for AndroidNoneiOS SafariNoneChrome for Android49+Android WebViewNoneSamsung Internet5.0+Opera Mobile?
ServiceWorkerGlobalScope/onsync
In all current engines.
Opera24+Edge79+
Edge (Legacy)NoneIENone
Firefox for Android44+iOS Safari11.3+Chrome for Android49+Android WebView49+Samsung Internet5.0+Opera Mobile24+
In only one current engine.
Opera?Edge79+
Edge (Legacy)NoneIENone
Firefox for AndroidNoneiOS SafariNoneChrome for Android49+Android WebViewNoneSamsung Internet5.0+Opera Mobile?
partial interface ServiceWorkerGlobalScope {attribute EventHandler ; }; [
onsync Exposed =ServiceWorker ]interface :
SyncEvent ExtendableEvent {(
constructor DOMString ,
type SyncEventInit );
init readonly attribute DOMString ;
tag readonly attribute boolean ; };
lastChance dictionary :
SyncEventInit ExtendableEventInit {required DOMString ;
tag boolean =
lastChance false ; };
Note: The SyncEvent
interface represents a firing sync registration. If the page (or worker) that registered the event is running, the user agent will fire the sync event as soon as network connectivity is available. Otherwise, the user agent should run the event at the soonest convenience. If a sync event fails, the user agent may decide to retry it at a time of its choosing. The lastChance
attribute is true if the user agent will not make further attempts to try this sync after the current attempt.
lastChance
:
self. addEventListener( 'sync' , event=> { if ( event. tag== 'important-thing' ) { event. waitUntil( doImportantThing(). catch ( err=> { if ( event. lastChance) { self. registration. showNotification( "Important thing failed" ); } throw err; }) ); } });
The above example reacts to lastChance
by showing a notification to the user. This requires the origin to have permission to show notifications.
In the above example doImportantThing
is a developer-defined function.
Whenever the user agent changes to online, the user agent SHOULD fire a sync event for each sync registration whose registration state is pending. The events may be fired in any order.
To fire a sync event for a sync registration registration, the user agent MUST run the following steps:
- Assert: registration’s registration state is pending.
- Let serviceWorkerRegistration be the service worker registration associated with registration.
- Assert: registration exists in the list of sync registrations associated with serviceWorkerRegistration.
- Set registration’s registration state to firing.
-
Fire Functional Event "
sync
" usingSyncEvent
on serviceWorkerRegistration with the following properties:-
In only one current engine.
FirefoxNoneSafariNoneChrome49+
Opera?Edge79+
Edge (Legacy)NoneIENone
Firefox for AndroidNoneiOS SafariNoneChrome for Android49+Android WebViewNoneSamsung Internet5.0+Opera Mobile?tag
- The tag associated with registration
-
In only one current engine.
FirefoxNoneSafariNoneChrome49+
Opera?Edge79+
Edge (Legacy)NoneIENone
Firefox for AndroidNoneiOS SafariNoneChrome for Android49+Android WebViewNoneSamsung Internet5.0+Opera Mobile?lastChance
- False if the user agent will retry this sync event if it fails, or true if no further attempts will be made after the current attempt.
Then run the following steps with dispatchedEvent:
- Let waitUntilPromise be the result of waiting for all of dispatchedEvent’s extended lifetime promises.
-
Upon fulfillment of waitUntilPromise, perform the following steps atomically:
-
If registration’s state is reregisteredWhileFiring:
- Set registration’s state to pending.
- If the user agent is currently online, fire a sync event for registration.
- Abort the rest of these steps.
- Assert: registration’s registration state is firing.
- Remove registration from serviceWorkerRegistration’s list of sync registration.
-
If registration’s state is reregisteredWhileFiring:
-
Upon rejection of waitUntilPromise, or if the script has been aborted by the termination of the service worker, perform the following steps atomically:
-
If registration’s state is reregisteredWhileFiring:
- Set registration’s state to pending.
- If the user agent is currently online, fire a sync event for registration.
- Abort the rest of these steps.
-
If the
lastChance
attribute of dispatchedEvent is false, set registration’s registration state to waiting, and perform the following steps in parallel:- Wait a user agent defined length of time.
- If registration’s registration state is not waiting, abort these substeps.
- Set registration’s registration state to pending.
- If the user agent is currently online, fire a sync event for registration.
- Else remove registration from serviceWorkerRegistration’s list of sync registrations.
-
If registration’s state is reregisteredWhileFiring:
-
A user agent MAY impose a time limit on the lifetime extension and execution time of a SyncEvent
which is stricter than the time limit imposed for ExtendableEvent
s in general. In particular an event for which lastChance
is true MAY have a significantly shortened time limit.
A user agent will retry a sync event based on some user agent defined heuristics.