From 643e1870529b8219eb887ed6ffc0165cbc00e22b Mon Sep 17 00:00:00 2001 From: Josh Karlin Date: Thu, 8 Oct 2015 13:47:50 -0400 Subject: [PATCH 1/3] Remove periodic from the explainer The spec is one-shot only. We can either bring back periodic when we're ready to spec it or move it to a new api. --- explainer.md | 90 +++++----------------------------------------------- 1 file changed, 8 insertions(+), 82 deletions(-) diff --git a/explainer.md b/explainer.md index e0d323a..0b86da3 100644 --- a/explainer.md +++ b/explainer.md @@ -1,12 +1,10 @@ # Background synchronization explained -This is a specification that brings both one-off and periodic synchronization to the web, in the form of [Service Workers](https://github.com/slightlyoff/ServiceWorker) events. +This is a specification that brings background synchronization to the web, in the form of a [Service Workers](https://github.com/slightlyoff/ServiceWorker) event. -## One-off synchronization +If you write an email, instant message, or simply favourite a tweet, the application needs to communicate that data to the server. If that fails, either due to user connectivity, service availability, or anything in-between, the app can store that action in some kind of 'outbox' for retry later. -If you write an email, instant message, or simply favourite a tweet, the application needs to communicate that data to the server. If that fails, either due to user connectivity, service availability or anything in-between, the app can store that action in some kind of 'outbox' for retry later. - -Unfortunately, on the web, that outbox can only be processed while the site is displayed in a browsing context. This is particularly problematic on mobile, where browsing contexts are frequently shut down to free memory. +Unfortunately, on the web, that outbox can only be processed while the site is displayed in a browsing context. If the user navigates away, closes the tab, or closes the browser the outbox can't be synced until the page is visited again. This is particularly problematic on mobile, where browsing contexts are frequently shut down to free memory. Native application platforms provide [job scheduling](https://developer.android.com/reference/android/app/job/JobScheduler.html) APIs that enable developers to collaborate with the system to ensure low power usage and background-driven processing. The web platform needs capabilities like this too. @@ -52,70 +50,11 @@ The promise passed to `waitUntil` is a signal to the UA that the sync event is o The UA may coalesce synchronizations to reduce the number of times the device, radio and browser need to wake up. The coalescing can be across origins, and even coalesced across the OS with native synchronizations. Although the event timings are coalesced, you still get an event per pending sync registration. -## Periodic synchronization - -Opening a news or social media app to find content you hadn't seen before - without going to the network, is a user experience currently limited to native apps. - -[The push API](https://w3c.github.io/push-api/) allows the server to dictate when the service worker should wake up and seek updates, but these are not sensitive to connection and charging state. Also, some sites update too frequently to warrant a push message per update (think Twitter, or a news site). - -Periodic syncs are simple to set up, don't require any server configuration, and allow the UA to optimize when they fire to be most-helpful and least-disruptive to the user. E.g. if the UA knows the user has a morning alarm set, it may run synchronizations shortly beforehand, giving the user quick and up-to-date information from their favourite sites. - -### The API - -**To request a periodic sync:** - -```js -navigator.serviceWorker.ready.then(function(registration) { - registration.periodicSync.register({ - tag: 'get-latest-news', // default: '' - minPeriod: 12 * 60 * 60 * 1000, // default: 0 - powerState: 'avoid-draining', // default: 'auto' - networkState: 'avoid-cellular' // default: 'online' - }).then(function(periodicSyncReg) { - // success - }, function() { - // failure - }) -}); -``` - -* `tag`: This operates like a notification's tag. If you register a sync and an existing sync with the same tag is pending, it returns the existing registration and updates it with the options provided. **Note:** one-off and periodic sync tags have separate namespaces.p -* `minPeriod`: The minimum time between successful sync events. A value of 0 (the default) means the UI may fire the event as frequently as it wishes. This value is a suggestion to prevent over-syncing. Syncing may be less frequent depending on heuristics such as visit frequency & device status. If timing is critical, [the push API](https://w3c.github.io/push-api/) may better suit your requirements. -* `powerState`: Either "auto" (default) or "avoid-draining". "avoid-draining" will delay syncs on battery-powered devices while that battery isn't charging. "auto" allows syncs to occur during battery-drain, although the UA may choose to avoid this depending on global device status (such as battery-saving mode) or user preferences. -* `networkState`: One of "online" (default), "avoid-cellular", or "any". "avoid-cellular" will delay syncs if the device is on a [cellular connection](https://w3c.github.io/netinfo/#idl-def-ConnectionType.cellular) - but be aware that some users may never use another connection type. "online" will delay syncs if the device is online, although the UA may choose to avoid particular connection types depending on global device status (such as roaming) or user preferences. "any" is similar to "online", except syncs may happen while the device is offline. - -**To respond to a periodic sync:** - -Over in the service worker: - -```js -self.addEventListener('periodicsync', function(event) { - if (event.registration.tag == 'get-latest-news') { - event.waitUntil(fetchAndCacheLatestNews()); - } - else { - // unknown sync, may be old, best to unregister - event.registration.unregister(); - } -}); -``` - -Like one-off syncs, the promise passed to `waitUntil` is a signal to the UA that the sync event is ongoing and that it should keep the SW alive if possible. Rejection of the event signals to the UA that the sync failed. Upon rejection the UA should reschedule (likely with a UA-determined backoff). `minPeriod` may be ignored for rescheduling. - -Also like one-off syncs, the UA may coalesce synchronizations to reduce the number of times the device, radio and browser need to wake up. In fact, the coalescing is more extreme for periodic syncs, as the result is perceived to be "beneficial" as opposed to "critical". - - -### What periodic sync is not - -Periodic sync is specifically not an exact alarm API. The scheduling granularity is in milliseconds but events may be delayed from firing for several hours depending on usage frequency and device state (battery, connection, location). - -The results of a sync running should be "beneficial" not "critical". If your use-case is critical, one-off syncs or [the push API](https://w3c.github.io/push-api/) may serve your requirements. - ## Getting pending sync details -As seen in the previous code examples, `sync.register()` and `syncEvent.registration` expose a sync registration object. You can also fetch them using `sync.getRegistration`, `sync.getRegistrations`, and `periodicSync.getRegistration`, `periodicSync.getRegistrations`. +As seen in the previous code examples, `sync.register()` and `syncEvent.registration` expose a sync registration object. You can also fetch them using `sync.getRegistration`, and `sync.getRegistrations`. -For example, to unregister a single one-off sync: +For example, to unregister a single sync: ```js navigator.serviceWorker.ready.then(function(registration) { @@ -125,11 +64,11 @@ navigator.serviceWorker.ready.then(function(registration) { }); ``` -To unregister all periodic syncs, except "get-latest-news": +To unregister all syncs, except "get-latest-news": ```js navigator.serviceWorker.ready.then(function(registration) { - registration.periodicSync.getRegistrations().then(function(syncRegs) { + registration.sync.getRegistrations().then(function(syncRegs) { syncRegs.filter(function(reg) { return reg.tag != 'get-latest-news'; }).forEach(function(reg) { @@ -139,21 +78,8 @@ navigator.serviceWorker.ready.then(function(registration) { }); ``` -## Checking for Permission - -Permissions for `sync` and `periodicSync` are entirely separate, and `periodicSync` is expected to be more difficult to obtain permission for. - -```js -navigator.serviceWorker.ready.then(function(registration) { - registration.periodicSync.permissionState().then(function(state) { - if (state == 'prompt') showSyncRegisterUI(); - }); -}); -``` - ## Notes * Since Service Workers are a requirement for sync, and since Service Workers are limited to HTTPS origins, that restriction applies here too. * All fetches during sync events must be HTTPS. HTTP fetches will be rejected. -* Sync may not be available to all web applications, not even all apps served over SSL. Browsers may choose to limit the set of applications which can register for synchronization based on quality signals that aren't a part of the visible API. This is especially true of periodic sync. -* Like all ServiceWorker events, 'sync' and 'periodicsync' may be terminated if they're taking an unreasonable amount of time or CPU. This is not a tool for distributed bitcoin mining :) \ No newline at end of file +* Like all ServiceWorker events, 'sync' may be terminated if they're taking an unreasonable amount of time or CPU. This is not a tool for distributed bitcoin mining :) From 17a96230fb1722f44296b57803e70d1c615743df Mon Sep 17 00:00:00 2001 From: Josh Karlin Date: Thu, 8 Oct 2015 14:00:45 -0400 Subject: [PATCH 2/3] Add in idl --- explainer.md | 14 ++++++++++++-- idl.md | 49 +------------------------------------------------ 2 files changed, 13 insertions(+), 50 deletions(-) diff --git a/explainer.md b/explainer.md index 0b86da3..1cee9ee 100644 --- a/explainer.md +++ b/explainer.md @@ -1,10 +1,10 @@ # Background synchronization explained -This is a specification that brings background synchronization to the web, in the form of a [Service Workers](https://github.com/slightlyoff/ServiceWorker) event. +This is a specification that brings background synchronization to the web, in the form of a [Service Worker](https://github.com/slightlyoff/ServiceWorker) event. If you write an email, instant message, or simply favourite a tweet, the application needs to communicate that data to the server. If that fails, either due to user connectivity, service availability, or anything in-between, the app can store that action in some kind of 'outbox' for retry later. -Unfortunately, on the web, that outbox can only be processed while the site is displayed in a browsing context. If the user navigates away, closes the tab, or closes the browser the outbox can't be synced until the page is visited again. This is particularly problematic on mobile, where browsing contexts are frequently shut down to free memory. +Unfortunately, on the web, that outbox can only be processed while the site is displayed in a browsing context. If the user navigates away, closes the tab, or closes the browser, the outbox can't be synced until the page is visited again. This is particularly problematic on mobile, where browsing contexts are frequently shut down to free memory. Native application platforms provide [job scheduling](https://developer.android.com/reference/android/app/job/JobScheduler.html) APIs that enable developers to collaborate with the system to ensure low power usage and background-driven processing. The web platform needs capabilities like this too. @@ -78,6 +78,16 @@ navigator.serviceWorker.ready.then(function(registration) { }); ``` +## Checking for Permission + +```js +navigator.serviceWorker.ready.then(function(registration) { + registration.sync.permissionState().then(function(state) { + if (state == 'prompt') showSyncRegisterUI(); + }); +}); +``` + ## Notes * Since Service Workers are a requirement for sync, and since Service Workers are limited to HTTPS origins, that restriction applies here too. diff --git a/idl.md b/idl.md index e34497d..727235a 100644 --- a/idl.md +++ b/idl.md @@ -3,7 +3,6 @@ ```js partial interface ServiceWorkerRegistration { readonly attribute SyncManager sync; - readonly attribute PeriodicSyncManager periodicSync;b }; interface SyncManager { @@ -16,7 +15,7 @@ interface SyncManager { interface SyncRegistration { readonly attribute DOMString tag; readonly attribute Promise done; - + Promise unregister(); }; @@ -30,45 +29,8 @@ enum SyncPermissionState { "granted" }; -enum SyncNetworkState { - "any", - "avoid-cellular", - "online" -}; - -enum SyncPowerState { - "auto", - "avoid-draining" -}; - -interface PeriodicSyncManager { - Promise register(optional PeriodicSyncRegistrationOptions options); - Promise getRegistration(DOMString tag); - Promise> getRegistrations(); - Promise permissionState(); - - readonly attribute unsigned long minPossiblePeriod; -}; - -interface PeriodicSyncRegistration { - readonly attribute DOMString tag; - readonly attribute unsigned long minPeriod; - readonly attribute SyncNetworkState networkState; - readonly attribute SyncPowerState powerState; - - Promise unregister(); -}; - -dictionary PeriodicSyncRegistrationOptions { - DOMString tag = ""; - unsigned long minPeriod = 0; - SyncNetworkType networkState = "online"; - SyncPowerState powerState = "auto"; -}; - partial interface ServiceWorkerGlobalScope { attribute EventHandler onsync; - attribute EventHandler onperiodicsync; }; [Constructor(DOMString type, SyncEventInit eventInitDict), Exposed=ServiceWorker] @@ -79,13 +41,4 @@ interface SyncEvent : ExtendableEvent { dictionary SyncEventInit : EventInit { required SyncRegistration registration; }; - -[Constructor(DOMString type, PeriodicSyncEventInit eventInitDict), Exposed=ServiceWorker] -interface PeriodicSyncEvent : ExtendableEvent { - readonly attribute PeriodicSyncRegistration registration; -}; - -dictionary PeriodicSyncEventInit : EventInit { - required PeriodicSyncRegistration registration; -}; ``` From 448896758db758d43df83e9854796b1a3a7f58be Mon Sep 17 00:00:00 2001 From: Josh Karlin Date: Thu, 8 Oct 2015 14:30:51 -0400 Subject: [PATCH 3/3] update use-cases.md --- use-cases.md | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/use-cases.md b/use-cases.md index 1f22877..4e0a342 100644 --- a/use-cases.md +++ b/use-cases.md @@ -16,19 +16,6 @@ The above are from actual customers, but not naming names until they agree to be * If sync fails, reschedule it - `event.waitUntil` can be used to extend lifetime and indicate failure. We’ll want some kind of back-off for reschedules. * Multiple sync requests for the same name are coalesced into one sync event in the SW - allows multiple independent systems to add to an idb “outbox” and request an an outbox sync. -## Approximately regular sync - -### Use-cases - -* **News site** - fetching daily news for quick display in the morning -* **Social media** - periodic updates so initial display is content user hasn't seen, even if offline -* **Blog updates** - Updated blog content without having to set up a push server -* **RSS reader** - Check for updates across multiple origins - -These are either "I don't want to / can't set up push" or "updates are so frequent push doesn't make sense". - -Exact-time syncs are out-of scope, but may be investigated in the context of an alarms API in the future. - # Concerns * **Location tracking** - an interval sync or failing one-off sync could lead to user tracking via IP