Skip to content
Browse files

EventWorker -> ServiceWorker

  • Loading branch information...
1 parent 3c629b7 commit 3865e9a0b07088f1bf553961c2c0d2e0cbf11566 @slightlyoff committed Sep 17, 2013
Showing with 202 additions and 183 deletions.
  1. +1 −1 Makefile
  2. +24 −24 advanced_topics.md
  3. +11 −7 caching.md
  4. +134 −119 explainer.md
  5. +2 −2 package.json
  6. +14 −14 event_worker.js → service_worker.js
  7. +16 −16 event_worker.ts → service_worker.ts
View
2 Makefile
@@ -1,6 +1,6 @@
all: build
build:
- node_modules/typescript/bin/tsc --target ES5 event_worker.ts
+ node_modules/typescript/bin/tsc --target ES5 service_worker.ts
.PHONY: build
View
48 advanced_topics.md
@@ -1,33 +1,33 @@
-<h2>EventWorkers Explained -- Advanced Topics</h2>
+<h2>ServiceWorkers Explained -- Advanced Topics</h2>
-So you've read the [Explainer](explainer.md) for EventWorkers but you've still got questions -- great! We've got (more) answers.
+So you've read the [Explainer](explainer.md) for ServiceWorkers but you've still got questions -- great! We've got (more) answers.
-## Caching of EventWorker scripts
+## Caching of ServiceWorker scripts
The script that you register, as well as any additional scripts that
are imported during initial load, are persistently cached with a separate policy from normal web content, or any other web storage mechanisms.
-This allows the browser to start up the EventWorker at any point, generally in response to
+This allows the browser to start up the ServiceWorker at any point, generally in response to document loading.
## Offline
How does this handle offline, or more specifically, how does this replace AppCache?
The fetch event is simply the gateway through which all network access for a given is managed. By intercepting all fetch events and optionally routing them through a cache, you can control access to the network, possibly avoiding it altogether.
-To do this you're going to need an actual Cache. EventWorkers (and eventually other contexts) have access to a separate Cache API which allows storage of arbitrary data that can be used to respond to fetch events.
+To do this you're going to need an actual Cache. ServiceWorkers (and eventually other contexts) have access to a separate Cache API which allows storage of arbitrary data that can be used to respond to fetch events.
-## Understanding EventWorker script Caching
+## Understanding ServiceWorker script Caching
-It's important to keep in mind that EventWorkers are a type of [Shared Worker](http://www.w3.org/TR/workers/#shared-workers-and-the-sharedworker-interface) -- uniquely imbued with additional APIs for access to cache objects and such -- but in general, what you can do in a Shared Worker, you can do in a EventWorker. That includes calling [`importScripts()`](https://developer.mozilla.org/en-US/docs/DOM/Using_web_workers#Importing_scripts_and_libraries) to include libraries.
+It's important to keep in mind that ServiceWorkers are a type of [Shared Worker](http://www.w3.org/TR/workers/#shared-workers-and-the-sharedworker-interface) -- uniquely imbued with additional APIs for access to cache objects and such -- but in general, what you can do in a Shared Worker, you can do in a ServiceWorker. That includes calling [`importScripts()`](https://developer.mozilla.org/en-US/docs/DOM/Using_web_workers#Importing_scripts_and_libraries) to include libraries.
-`importScripts()` is a dynamic call, much like the addition of a `<script>` element to document at runtime, so from the perspective of the browser, there's no way to know what resources the EventWorker script itself will depend on until it is executed. As you've guessed by now, this has implications for what gets fetched and cached when the browser downloads and attempts to install an EventWorker. Remember also that initial requests for EventWorker scripts (and their sub-resources) happen *against the native HTTP cache* (without heuristic caching).
+`importScripts()` is a dynamic call, much like the addition of a `<script>` element to document at runtime, so from the perspective of the browser, there's no way to know what resources the ServiceWorker script itself will depend on until it is executed. As you've guessed by now, this has implications for what gets fetched and cached when the browser downloads and attempts to install an ServiceWorker. Remember also that initial requests for ServiceWorker scripts (and their sub-resources) happen *against the native HTTP cache* (without heuristic caching).
-But browsers surely must cache EventWorker scripts (else how would EventWorkers run when disconnected?)...so what guarantees do we have about what will be cached and when?
+But browsers surely must cache ServiceWorker scripts (else how would ServiceWorkers run when disconnected?)...so what guarantees do we have about what will be cached and when?
To repeat: if you `importScripts()` for all of the resources you will need by the time your `oninstall` callback finishes, those resources are going to be part of the implicit cache that the browser maintains.
-Here's a super simple example that imports all the libraries the script will need at every possible point into the global scope. It brings in both EventWorker scripts themselves, as well as resources that will be used when offline:
+Here's a super simple example that imports all the libraries the script will need at every possible point into the global scope. It brings in both ServiceWorker scripts themselves, as well as resources that will be used when offline:
```js
// caching.js
@@ -50,44 +50,44 @@ route(
);
```
-Neat stuff. The `route` and `cache` functions provided by `router.js` and `cacheManger.js` make it much simpler to write your EventWorker, and since they're imported at the top-level, we know they'll be cached along with `caching.js`.
+Neat stuff. The `route` and `cache` functions provided by `router.js` and `cacheManger.js` make it much simpler to write your ServiceWorker, and since they're imported at the top-level, we know they'll be cached along with `caching.js`.
-Many versions of the basic pattern presented here are possible, including calling `importScripts()` to include libraries authored by third parties to help manage sensitive resources that they would like to manage. In these cases, the ability for the EventWorker to register multiple `oninstall` and `onfetch` handlers proves to be invaluable: each imported script can set up handlers to manage resources, caches, and the like for the bits of the world they need to be in control of.
+Many versions of the basic pattern presented here are possible, including calling `importScripts()` to include libraries authored by third parties to help manage sensitive resources that they would like to manage. In these cases, the ability for the ServiceWorker to register multiple `oninstall` and `onfetch` handlers proves to be invaluable: each imported script can set up handlers to manage resources, caches, and the like for the bits of the world they need to be in control of.
-_*NOTE: Be mindful that these are global imports running in the context of your app's origin. Like cross-origin scripts included in your app, scripts imported into your EventWorker run with full authority to do everything your EventWorker can -- which is pretty much everything. `importScripts()` only from those you trust!*_
+_*NOTE: Be mindful that these are global imports running in the context of your app's origin. Like cross-origin scripts included in your app, scripts imported into your ServiceWorker run with full authority to do everything your ServiceWorker can -- which is pretty much everything. `importScripts()` only from those you trust!*_
-It's also good to know what counts as an "update" to the EventWorker script: when the browser re-fetches the main script, it ignores HTTP heuristic caching and goes all the way to the network, requesting the EventWorker script directly from the server and bypassing HTTP caches. Upon getting a new response, the returned script it checked to see if it is byte-for-byte identical. Only when not byte-for-byte identical is the EventWorker script considered "updated". Scripts required by `importScripts()` are fetched and validated in the same way, at the same time, but updates to them are not considered to trigger the "upgrade dance" the same way that an update to the main EventWorker script does.
+It's also good to know what counts as an "update" to the ServiceWorker script: when the browser re-fetches the main script, it ignores HTTP heuristic caching and goes all the way to the network, requesting the ServiceWorker script directly from the server and bypassing HTTP caches. Upon getting a new response, the returned script it checked to see if it is byte-for-byte identical. Only when not byte-for-byte identical is the ServiceWorker script considered "updated". Scripts required by `importScripts()` are fetched and validated in the same way, at the same time, but updates to them are not considered to trigger the "upgrade dance" the same way that an update to the main ServiceWorker script does.
-The rule then is that if you'd like to update the behavior of EventWorker, you should update some of the contents of the EventWorker script itself -- even if it's just a small increment to the version number.
+The rule then is that if you'd like to update the behavior of ServiceWorker, you should update some of the contents of the ServiceWorker script itself -- even if it's just a small increment to the version number.
### If You Liked It, You Should Have Put Some SSL On It
-EventWorkers are "in effect" all the time, but really come into their own when offline. But "offline" is incredibly hard to define, it turns out. Think of the last time you were in a hotel lobby, coffee shop, or airport where some sign advertised "Free WiFi!", only to present you with a captive portal demanding an email address (all the better to spam you with!) or worse, some form of payment to do anything but view some marketing site for the place *you're already in* (but starting to want to leave).
+ServiceWorkers are "in effect" all the time, but really come into their own when offline. But "offline" is incredibly hard to define, it turns out. Think of the last time you were in a hotel lobby, coffee shop, or airport where some sign advertised "Free WiFi!", only to present you with a captive portal demanding an email address (all the better to spam you with!) or worse, some form of payment to do anything but view some marketing site for the place *you're already in* (but starting to want to leave).
-Now imagine that the browser is running and thinks "cool, we're connected to the Internet and DNS is resolving, let's fetch some updated EventWorkers!" Pain and heartache are about to befall installed apps, particularly if you *do* connect, but the service provider is running an aggressive and badly-behaved proxy. Such things are more common than they should be.
+Now imagine that the browser is running and thinks "cool, we're connected to the Internet and DNS is resolving, let's fetch some updated ServiceWorkers!" Pain and heartache are about to befall installed apps, particularly if you *do* connect, but the service provider is running an aggressive and badly-behaved proxy. Such things are more common than they should be.
-Good news and bad news: the good news is that *most* proxies will respect you serving your EventWorker scripts with `Cache-Control: no-cache` or `Cache-Control: private`. Between that and the browser turning off heuristic caching for EventWorker script resources, most requests for updated EventWorkers will get to the right places. But not if something is really wonky and/or DNS is compromised. To prevent your app getting pwn'd by terrible proxies and captive portals, you'll need to serve the script (and most of the resources) in such a way that they payloads _can't_ be inspected (and therefore cached). Yes, that means SSL.
+Good news and bad news: the good news is that *most* proxies will respect you serving your ServiceWorker scripts with `Cache-Control: no-cache` or `Cache-Control: private`. Between that and the browser turning off heuristic caching for ServiceWorker script resources, most requests for updated ServiceWorkers will get to the right places. But not if something is really wonky and/or DNS is compromised. To prevent your app getting pwn'd by terrible proxies and captive portals, you'll need to serve the script (and most of the resources) in such a way that they payloads _can't_ be inspected (and therefore cached). Yes, that means SSL.
Oh, don't give me that look. You knew you were going to have to do it.
-### Cache Quotas and Eviction Events
+### Cache Quotas and Eviction Services
TODO(slightlyoff)
### CSP
-TODO(slightlyoff): what happens if an EventWorker script matches the page's CSP policy but an importScript()'d resource doesn't?
+TODO(slightlyoff): what happens if an ServiceWorker script matches the page's CSP policy but an importScript()'d resource doesn't?
## Dude, Where Are My Synchronous APIs?
They're gone. Just gone.
-With the exception of `importScripts()` (covered at length above), EventWorkers expose no synchronous APIs. No sync XHR, no sync IDB, nothing.
+With the exception of `importScripts()` (covered at length above), ServiceWorkers expose no synchronous APIs. No sync XHR, no sync IDB, nothing.
Why?
-Glad you asked: EventWorker scripts live and die by their ability to respond quickly to requests. In fact, if a EventWorker takes too long to do _anything_, browsers can just simply kill them. If your `importScript()` call takes too long, dead. If your event handlers take too long, _dead_. Them's the breaks when you're in the fast-path for content fetching.
+Glad you asked: ServiceWorker scripts live and die by their ability to respond quickly to requests. In fact, if a ServiceWorker takes too long to do _anything_, browsers can just simply kill them. If your `importScript()` call takes too long, dead. If your event handlers take too long, _dead_. Them's the breaks when you're in the fast-path for content fetching.
-Remember that there's only one EventWorker running at a time, meaning that if many resources need to be handled, the EventWorker needs to be free to start making decisions about them, and _that_ means getting out of the way and giving some other request a chance. To help enable good performance behavior, all of the APIs that might otherwise lock up the EventWorker have been taken away and all of the APIs that you respond with content to deal with `Promises` to enable you to do work asynchronously.
+Remember that there's only one ServiceWorker running at a time, meaning that if many resources need to be handled, the ServiceWorker needs to be free to start making decisions about them, and _that_ means getting out of the way and giving some other request a chance. To help enable good performance behavior, all of the APIs that might otherwise lock up the ServiceWorker have been taken away and all of the APIs that you respond with content to deal with `Promises` to enable you to do work asynchronously.
-Keeping your EventWorkers responsive is your job. Making that easier than not is the job of the spec authors, and removing synchronous guns pointed straight at the feet of your application is one way they've done that.
+Keeping your ServiceWorkers responsive is your job. Making that easier than not is the job of the spec authors, and removing synchronous guns pointed straight at the feet of your application is one way they've done that.
View
18 caching.md
@@ -1,19 +1,19 @@
<h2>Caching</h2>
-The Cache API is the easiest mechanism to take an application offline, but it is by no means the only one. Other browser storage mechanisms (such as IndexedDB, the FileSystem API are fine candidates as well. (Local storage is out: no synchronous APIs) The Cache API is primarily an API sugared specifically for responding to `fetch` events in EventWorkers.
+The Cache API is the easiest mechanism to take an application offline, but it is by no means the only one. Other browser storage mechanisms (such as IndexedDB, the FileSystem API are fine candidates as well. (Local storage is out: no synchronous APIs) The Cache API is primarily an API sugared specifically for responding to `fetch` events in ServiceWorkers.
Much like any other web storage technology, they are _not_ shared
across domains, and they are completely isolated from the browser's HTTP cache.
-A domain can have multiple, named `Cache` objects, whose contents are entirely under the control of scripts. The zen of understanding `Cache` instances is that they _are not part of your browser's HTTP cache_. Forget what you know about HTTP cache eviction, expires headers, and all the rest. None of that matters here -- your `Cache` objects are exactly that, _your_ caches. They don't get updated unless you ask for them to be, they don't expire (unless you delete the entries), and they don't disappear just because you upgrade your EventWorker script.
+A domain can have multiple, named `Cache` objects, whose contents are entirely under the control of scripts. The zen of understanding `Cache` instances is that they _are not part of your browser's HTTP cache_. Forget what you know about HTTP cache eviction, expires headers, and all the rest. None of that matters here -- your `Cache` objects are exactly that, _your_ caches. They don't get updated unless you ask for them to be, they don't expire (unless you delete the entries), and they don't disappear just because you upgrade your ServiceWorker script.
-This has huge ramifications for good long-term offline use of EventWorkers.
+This has huge ramifications for good long-term offline use of ServiceWorkers.
-The first implication is that _you should version your caches by name_. Add the major version of your EventWorker to the cache name and make sure you are only using caches that your version of the EventWorker knows it can safely operate on.
+The first implication is that _you should version your caches by name_. Add the major version of your ServiceWorker to the cache name and make sure you are only using caches that your version of the ServiceWorker knows it can safely operate on.
-So what about old caches? Old EventWorker scripts don't get an extra chance to run, so it's always up to the _replacing_ EventWorker to do housekeeping.
+So what about old caches? Old ServiceWorker scripts don't get an extra chance to run, so it's always up to the _replacing_ ServiceWorker to do housekeeping.
-The [Explainer](explainer.md) talked heavily about `oninstall`, but wisely didn't mention it's cousin `onactivate`: there's good reason for this: `onactivate` is called *after* the previous EventWorker script is replaced but before the new script handles any resources, and no new requests will be sent to the new EventWorker until it finishes. That makes `onactivate` the ideal place to do work like IndexedDB schema upgrades and legacy cache removal. It also makes a dangerous place, since doing too much work or doing work that takes too long can have the effective appearance of hosing an app entirely. There won't be an old version to use and the new version is assumed to be doing important work to get ready to handle new requests.
+The [Explainer](explainer.md) talked heavily about `oninstall`, but wisely didn't mention it's cousin `onactivate`: there's good reason for this: `onactivate` is called *after* the previous ServiceWorker script is replaced but before the new script handles any resources, and no new requests will be sent to the new ServiceWorker until it finishes. That makes `onactivate` the ideal place to do work like IndexedDB schema upgrades and legacy cache removal. It also makes a dangerous place, since doing too much work or doing work that takes too long can have the effective appearance of hosing an app entirely. There won't be an old version to use and the new version is assumed to be doing important work to get ready to handle new requests.
Tread very, _very_ lightly when writing `onactivate` handlers.
@@ -32,6 +32,8 @@ var contentCacheName = "content";
var currentCaches = [ shellCacheName, contentCacheName ];
this.addEventListener("install", function(e) {
+ e.services = ["fetch"];
+
// Create a cache of resources. Begins the process of fetching them.
var shellResources = new Cache(
assetBase + "/base.css",
@@ -60,7 +62,7 @@ So that's cache "garbage collection" then: it's manual and your app should be mi
But what about in-place updates of `Cache` objects?
-Remember, `Cache` entries do not update themselves. Whatever versions of content they receive when they are successfully filled are the versions they keep until the developer requests an update. That means it's possible for an EventWorker to be updated regularly, use caches across versions, but still find itself using legacy content in caches...assuming content at the same URL has been updated, a pretty-clear anti-pattern: better to put version #'s in URLs than to update cacheable content at stable URL).
+Remember, `Cache` entries do not update themselves. Whatever versions of content they receive when they are successfully filled are the versions they keep until the developer requests an update. That means it's possible for an ServiceWorker to be updated regularly, use caches across versions, but still find itself using legacy content in caches...assuming content at the same URL has been updated, a pretty-clear anti-pattern: better to put version #'s in URLs than to update cacheable content at stable URL).
To re-iterate: caches aren't updated automatically. Updates must be manually managed. How? With `.update()`.
@@ -75,6 +77,8 @@ var contentCacheName = "content";
var currentCaches = [ shellCacheName, contentCacheName ];
this.addEventListener("install", function(e) {
+ e.services = ["fetch"];
+
// Update the existing caches that we'll eventually keep.
caches.forEach(function(cacheName, cache) {
if (currentCaches.indexOf(cacheName) >= 0) {
View
253 explainer.md
@@ -1,19 +1,19 @@
<!-- TODO(slightlyoff)
- Cover cache updating and script caching
-->
-<h2>EventWorkers Explained</h2>
+<h2>ServiceWorkers Explained</h2>
<!-- patterned after:
https://dvcs.w3.org/hg/webcomponents/raw-file/d7d1b718de45/explainer/index.html
-->
## What's All This Then?
-EventWorkers are a new feature for the web platform that lets a script persistently cache resources and handle all resource requests for an application -- even when the network isn't available. Putting it all together, EventWorkers give you a way to build applications that work offline.
+ServiceWorkers are a new feature for the web platform that lets a script persistently cache resources and handle all resource requests for an application -- even when the network isn't available. Putting it all together, ServiceWorkers give you a way to build applications that work offline.
-You might now be thinking "yeah, but what about the [HTML5 Application Cache (aka "AppCache")](http://www.whatwg.org/specs/web-apps/current-work/multipage/offline.html)"...didn't it solve this? Good question. AppCache is declarative -- you give the browser a manifest and magic happens. This has [well-documented limitations](http://alistapart.com/article/application-cache-is-a-douchebag) that EventWorkers work around by giving developers the lower-level primitives that AppCache might be described in terms of. EventWorkers, then, are the explanation for the magic of AppCache.
+You might now be thinking "yeah, but what about the [HTML5 Application Cache (aka "AppCache")](http://www.whatwg.org/specs/web-apps/current-work/multipage/offline.html)"...didn't it solve this? Good question. AppCache is declarative -- you give the browser a manifest and magic happens. This has [well-documented limitations](http://alistapart.com/article/application-cache-is-a-douchebag) that ServiceWorkers work around by giving developers the lower-level primitives that AppCache might be described in terms of. ServiceWorkers, then, are the explanation for the magic of AppCache.
-This document is designed to help you understand the basic concepts of EventWorkers, how they interact, and how to start thinking about building apps with them in mind.
+This document is designed to help you understand the basic concepts of ServiceWorkers, how they interact, and how to start thinking about building apps with them in mind.
## From Pages to Apps
@@ -25,27 +25,27 @@ Turns out the same story is repeated in nearly every sort of application you mig
Legacy offline solutions for HTML haven't made building applications in this model natural, URL-friendly, or scalable. Yet these are the qualities that developers demand of a productive platform.
-Enter the EventWorker.
+Enter the ServiceWorker.
-An EventWorker is a bit of script that can listen for network events, (such as resource requests) manage content caches, and thus decide what content to display when a URL is requested.
+An ServiceWorker is a bit of script that can listen for network events, (such as resource requests) manage content caches, and thus decide what content to display when a URL is requested.
-In our video example, one cache might be built/managed to help make sure that the shell of the application is available offline. Another might be built to represent the downloaded videos. Yet another might be built to keep a local inventory of ads or trailers to show before movies play. Each of these caches are effectively independent bits of content, joined at runtime by the application -- and EventWorkers mediate how applications come into being.
+In our video example, one cache might be built/managed to help make sure that the shell of the application is available offline. Another might be built to represent the downloaded videos. Yet another might be built to keep a local inventory of ads or trailers to show before movies play. Each of these caches are effectively independent bits of content, joined at runtime by the application -- and ServiceWorkers mediate how applications come into being.
-## Bootstrapping With an EventWorker
+## Bootstrapping With an ServiceWorker
-EventWorkers are installed by web pages. A user must visit a page or app for the process to start. Let's assume our page is `http://videos.example.com/index.html`. From there, script on that page might install an EventWorker with code like this:
+ServiceWorkers are installed by web pages. A user must visit a page or app for the process to start. Let's assume our page is `http://videos.example.com/index.html`. From there, script on that page might install an ServiceWorker with code like this:
```html
<!DOCTYPE html>
<!-- http://videos.example.com/index.html -->
<html>
<head>
<script>
- navigator.registerEventWorker("/*", "/assets/v1/ctrl.js").then(
- function(eventWorker) {
+ navigator.registerServiceWorker("/*", "/assets/v1/ctrl.js").then(
+ function(serviceWorker) {
console.log("success!");
- eventWorker.postMessage("Howdy from your installing page.");
- // To use the eventWorker immediately, you might call window.location.reload()
+ serviceWorker.postMessage("Howdy from your installing page.");
+ // To use the serviceWorker immediately, you might call window.location.reload()
},
function(why) {
console.error("Installing the worker failed!:", why);
@@ -61,31 +61,31 @@ EventWorkers are installed by web pages. A user must visit a page or app for the
</html>
```
-The EventWorker itself is a bit of JavaScript that runs in a context that's very much like a [shared worker](http://www.whatwg.org/specs/web-apps/current-work/multipage/workers.html#shared-workers "HTML5 Shared Workers").
+The ServiceWorker itself is a bit of JavaScript that runs in a context that's very much like a [shared worker](http://www.whatwg.org/specs/web-apps/current-work/multipage/workers.html#shared-workers "HTML5 Shared Workers").
The browser now attempts to download and "install" `ctrl.js`; a process covered later in this document. Once it is successfully installed, our `success!` message will be sent to the console and, crucially, the next time the user visits `index.html` or any other page located at `http://videos.example.com/`, `ctrl.js` will be consulted about what to do and what content to load -- even if the device has no internet connection. On pages that are "controlled" in this way, other resources (like the image in the body) are also requested first from `ctrl.js` before the normal browser cache is consulted for them.
### Controlled & Uncontrolled Documents
-The first time `http://videos.example.com/index.html` is loaded, all the resources it requests will come from the network. That means that even if the browser runs the install snippet for `ctrl.js`, fetches it, and finishes installing it before it begins fetching `logo.png`, the new EventWorker script won't be consulted about loading `logo.png`. This is down to the first rule of EventWorkers:
+The first time `http://videos.example.com/index.html` is loaded, all the resources it requests will come from the network. That means that even if the browser runs the install snippet for `ctrl.js`, fetches it, and finishes installing it before it begins fetching `logo.png`, the new ServiceWorker script won't be consulted about loading `logo.png`. This is down to the first rule of ServiceWorkers:
-> Documents live out their whole lives using the EventWorker they start with.
+> Documents live out their whole lives using the ServiceWorker they start with.
-This means that if a document starts life _without_ an EventWorker, even if one is installed for a matching bit of URL space, it won't suddenly get an EventWorker later in life. Same goes for documents that are loaded with an EventWorker which might later call `navigator.eventWorker.unregister("/*")`. Put another way, `register()` and `unregister()` only affects the *next* document(s).
+This means that if a document starts life _without_ an ServiceWorker, even if one is installed for a matching bit of URL space, it won't suddenly get an ServiceWorker later in life. Same goes for documents that are loaded with an ServiceWorker which might later call `navigator.serviceWorker.unregister("/*")`. Put another way, `register()` and `unregister()` only affects the *next* document(s).
This is good for a couple of important reasons:
- - Graceful fallback. Browsers that don't yet understand EventWorkers will still understand these pages.
- - Related: [good URLs are forever](http://www.w3.org/Provider/Style/URI). Apps that respect some URLs with an EventWorker should do sane things without one when users navigate to those locations. This is key to "URL-friendly" apps that exhibit the ur-social behaviors that make the web so good for collaboration, sharing, and all the rest.
+ - Graceful fallback. Browsers that don't yet understand ServiceWorkers will still understand these pages.
+ - Related: [good URLs are forever](http://www.w3.org/Provider/Style/URI). Apps that respect some URLs with an ServiceWorker should do sane things without one when users navigate to those locations. This is key to "URL-friendly" apps that exhibit the ur-social behaviors that make the web so good for collaboration, sharing, and all the rest.
- It forces you to have URLs! Some modern apps platforms have forsaken this core principle of the web and suffer for it. The web should never make the same mistake.
- - Developers are less likely to paint themselves into a corner by relying on EventWorkers when they shouldn't. If it doesn't work without the EventWorker, it'll be obvious the first time a new page is loaded or by unregistering the EventWorker. Not ideal for testing, but it beats AppCache and can be made better with tools over time.
- - Reasoning about a page that gets an EventWorker halfway through its lifetime -- or worse, loses its EventWorker -- is incredibly painful. If an uncontrolled page could become controlled, there's a natural tendency to stuff core app behavior into the EventWorker and then try to "block" until the EventWorker is installed. This isn't webby and it's not a good user experience. And given that there's no obvious way to synchronize on EventWorker installation gracefully, the patterns that would emerge are ghastly even to think about.
+ - Developers are less likely to paint themselves into a corner by relying on ServiceWorkers when they shouldn't. If it doesn't work without the ServiceWorker, it'll be obvious the first time a new page is loaded or by unregistering the ServiceWorker. Not ideal for testing, but it beats AppCache and can be made better with tools over time.
+ - Reasoning about a page that gets an ServiceWorker halfway through its lifetime -- or worse, loses its ServiceWorker -- is incredibly painful. If an uncontrolled page could become controlled, there's a natural tendency to stuff core app behavior into the ServiceWorker and then try to "block" until the ServiceWorker is installed. This isn't webby and it's not a good user experience. And given that there's no obvious way to synchronize on ServiceWorker installation gracefully, the patterns that would emerge are ghastly even to think about.
## A Quick Game of `onfetch`
-EventWorkers, once installed, can choose to handle resource loading. Before going to the network to fetch a document that matches the EventWorker's scope, the worker is consulted, including when fetching the document payload itself.
+ServiceWorkers, once installed, can choose to handle resource loading. Before going to the network to fetch a document that matches the ServiceWorker's scope, the worker is consulted, including when fetching the document payload itself.
-Here's an example of an EventWorker that only handles a single resource (`/services/inventory/data.json`) but which logs out requests for all resources it is consulted for:
+Here's an example of an ServiceWorker that only handles a single resource (`/services/inventory/data.json`) but which logs out requests for all resources it is consulted for:
```js
// hosted at: /assets/v1/ctrl.js
@@ -94,6 +94,11 @@ this.version = 1;
var base = "http://videos.example.com";
var inventory = new URL("/services/inventory/data.json", base);
+this.addEventListener("install", function(e) {
+ // Tell the system that this service worker can handle fetch events.
+ e.services = ["fetch"];
+});
+
this.addEventListener("fetch", function(e) {
var url = e.request.url;
console.log(url);
@@ -118,34 +123,38 @@ This simple example will always produce the following output at the console when
> http://videos.example.com/assets/v1/logo.png
```
-The contents of all but the inventory will be handled by the normal browser resource fetching system because the `onfetch` event handler didn't call `respondWith` when invoked with their requests. The first time the app is loaded (before the EventWorker is installed), `data.json` will also be fetched from the network. Thereafter it'll be computed by the EventWorker instead. The important thing to remember here is that _normal resource loading is the fallback behavior for fetch events_.
+The contents of all but the inventory will be handled by the normal browser resource fetching system because the `onfetch` event handler didn't call `respondWith` when invoked with their requests. The first time the app is loaded (before the ServiceWorker is installed), `data.json` will also be fetched from the network. Thereafter it'll be computed by the ServiceWorker instead. The important thing to remember here is that _normal resource loading is the fallback behavior for fetch events_.
-When combined with access to [IndexedDB](https://developer.mozilla.org/en-US/docs/IndexedDB) and a new form of Cache (covered below), the ability to respond with arbitrary content is incredibly powerful. Since installed EventWorkers are invoked even when offline, EventWorkers enable apps that are "offline by default" once installed.
+When combined with access to [IndexedDB](https://developer.mozilla.org/en-US/docs/IndexedDB) and a new form of Cache (covered below), the ability to respond with arbitrary content is incredibly powerful. Since installed ServiceWorkers are invoked even when offline, ServiceWorkers enable apps that are "offline by default" once installed.
## Mental Notes
-Before we get into the nitty-gritty of EventWorkers, a few things to keep in mind. First, the second rule of EventWorkers:
+Before we get into the nitty-gritty of ServiceWorkers, a few things to keep in mind. First, the second rule of ServiceWorkers:
-> EventWorkers may be killed at any time.
+> ServiceWorkers may be killed at any time.
-That's right, the browser might unceremoniously kill your EventWorker if it's idle, or even stop it mid-work and re-issue the request to a different instance of the worker. There are no guarantees about how long an EventWorker will run. EventWorkers should be written to avoid holding global state. This can't be stressed enough: _write your workers as though they will die after every request_.
+That's right, the browser might unceremoniously kill your ServiceWorker if it's idle, or even stop it mid-work and re-issue the request to a different instance of the worker. There are no guarantees about how long an ServiceWorker will run. ServiceWorkers should be written to avoid holding global state. This can't be stressed enough: _write your workers as though they will die after every request_.
-Also remember that _Event Workers are shared resources_. A single worker might be servicing requests from multiple tabs or documents. Never assume that only one document is talking to a given EventWorker. If you care about where a request is coming from or going to, use the `.window` property of the `onfetch` event; but don't create state that you care about without serializing it somewhere like [IndexedDB](https://developer.mozilla.org/en-US/docs/IndexedDB).
+Also remember that _Service Workers are shared resources_. A single worker might be servicing requests from multiple tabs or documents. Never assume that only one document is talking to a given ServiceWorker. If you care about where a request is coming from or going to, use the `.window` property of the `onfetch` event; but don't create state that you care about without serializing it somewhere like [IndexedDB](https://developer.mozilla.org/en-US/docs/IndexedDB).
This should be familiar if you've developed servers using Django, Rails, Java, Node etc. A single instance handles connections from many clients (documents in our case) but data persistence is handled by something else, typically a database.
-Lastly, exceptions or syntax errors that prevent running an EventWorker will ensure that the worker won't be considered successfully installed and won't be used on subsequent navigations. It pays to test.
+Lastly, exceptions or syntax errors that prevent running an ServiceWorker will ensure that the worker won't be considered successfully installed and won't be used on subsequent navigations. It pays to test.
### Resources & Navigations
Since loading documents and apps on the web boils down to an [HTTP request](http://shop.oreilly.com/product/9781565925090.do) the same way that any other sort of resource loading does, an interesting question arises: how do we distinguish loading a document from loading, say, an image or a CSS file that's a sub-resource for a document? And how can we distinguish between a top-level document and an `<iframe>`?
-A few properties are made available on `onfetch` event to help with this. Since the browser itself needs to understand the difference between these types of resource requests -- for example, to help it determine when to add something to the back/forward lists -- exposing it to an EventWorker is only natural.
+A few properties are made available on `onfetch` event to help with this. Since the browser itself needs to understand the difference between these types of resource requests -- for example, to help it determine when to add something to the back/forward lists -- exposing it to an ServiceWorker is only natural.
-Let's say we want an EventWorker that only handles top-level document navigations; that is to say, doesn't handle any `<iframes>` or requests for sub-resources like scripts, images, stylesheets or any of the rest. Here's how the most minimal version would look:
+Let's say we want an ServiceWorker that only handles top-level document navigations; that is to say, doesn't handle any `<iframes>` or requests for sub-resources like scripts, images, stylesheets or any of the rest. Here's how the most minimal version would look:
```js
-// top-level-only-event-worker.js
+// top-level-only-service-worker.js
+this.addEventListener("install", function(e) {
+ e.services = ["fetch"];
+});
+
this.addEventListener("fetch", function(e) {
if (e.type == "navigate" && e.isTopLevel == true) {
e.respondWith( /* ... */ );
@@ -155,11 +164,11 @@ this.addEventListener("fetch", function(e) {
### URLs, Domains, and Registrations
-Now that we've started to talk about `<iframe>`s, another question comes up: what if a controlled document from `video.example.com` loads an iframe from `www.example.net` which has previously registered an EventWorker using `navigator.registerEventWorker("/*", "/ctrl.js")`?
+Now that we've started to talk about `<iframe>`s, another question comes up: what if a controlled document from `video.example.com` loads an iframe from `www.example.net` which has previously registered an ServiceWorker using `navigator.registerServiceWorker("/*", "/ctrl.js")`?
-`video.example.com` and `www.example.net` are clearly different domains...should the EventWorker for `video.example.com` (registered with the path `/*`) get a crack at it? Because the web's same-origin security model guarantees that documents from different domains will be isolated from each other, it would be a huge error to allow `video.example.com` to return content that would run in the context of `www.example.net`. Code on that page could read cookies and databases, abuse sessions, and do all manner of malicious stuff.
+`video.example.com` and `www.example.net` are clearly different domains...should the ServiceWorker for `video.example.com` (registered with the path `/*`) get a crack at it? Because the web's same-origin security model guarantees that documents from different domains will be isolated from each other, it would be a huge error to allow `video.example.com` to return content that would run in the context of `www.example.net`. Code on that page could read cookies and databases, abuse sessions, and do all manner of malicious stuff.
-What happens instead in the scenario is that all navigations -- top level or not -- for `www.example.net` are handled by the EventWorker located at `http://www.example.net/ctrl.js`. The EventWorker on `video.example.com` won't get an `onfetch` event for this iframe, but it would if the iframe's `src` property were set to `http://video.example.com/subcontent.html` or any other page on `http://video.example.com`.
+What happens instead in the scenario is that all navigations -- top level or not -- for `www.example.net` are handled by the ServiceWorker located at `http://www.example.net/ctrl.js`. The ServiceWorker on `video.example.com` won't get an `onfetch` event for this iframe, but it would if the iframe's `src` property were set to `http://video.example.com/subcontent.html` or any other page on `http://video.example.com`.
Another interesting question: what happens if there are two registrations that might match?
@@ -171,7 +180,7 @@ For instance, what if `http://www.example.com/foo.html` contains:
<html>
<head>
<script>
- navigator.registerEventWorker("/foo*", "/fooEventWorker.js");
+ navigator.registerServiceWorker("/foo*", "/fooServiceWorker.js");
</script>
</head>
</html>
@@ -185,31 +194,31 @@ While `http://www.example.com/foo/bar.html` contains:
<html>
<head>
<script>
- navigator.registerEventWorker("/foo/bar*", "/foo/barEventWorker.js");
+ navigator.registerServiceWorker("/foo/bar*", "/foo/barServiceWorker.js");
</script>
</head>
</html>
```
-Turns out this is allowed, largely to prevent EventWorker scripts from becoming a point of contention across teams. If it were only possible to have one EventWorker per domain, sites with many different code-bases cooperating under one umbrella might find it very difficult to coordinate if they hadn't started by putting all apps on separate sub-domains.
+Turns out this is allowed, largely to prevent ServiceWorker scripts from becoming a point of contention across teams. If it were only possible to have one ServiceWorker per domain, sites with many different code-bases cooperating under one umbrella might find it very difficult to coordinate if they hadn't started by putting all apps on separate sub-domains.
#### Longest-Prefix Matching
-To break what might otherwise be ties when matching URLs, navigations are mapped to EventWorkers by longest-prefix-match. Note that the `*` can only occur _at the end_ of a matching rule, so attempts to register `/foo/*/bar` or `*bar` will throw exceptions. Similarly, registering a pattern that includes a "?" or "#" will also throw exceptions.
+To break what might otherwise be ties when matching URLs, navigations are mapped to ServiceWorkers by longest-prefix-match. Note that the `*` can only occur _at the end_ of a matching rule, so attempts to register `/foo/*/bar` or `*bar` will throw exceptions. Similarly, registering a pattern that includes a "?" or "#" will also throw exceptions.
In the above example with registrations for `/foo*` and `/foo/bar*`, the following matches would be made when navigating to the following URLs under `http://www.example.com`:
```
-/foo -> /fooEventWorker.js
-/foo?blarg -> /fooEventWorker.js
-/foo/ -> /fooEventWorker.js
-/foo/thinger.html -> /fooEventWorker.js
-/foobar.html -> /fooEventWorker.js
-/foo/other/thinger.html -> /fooEventWorker.js
-/foo/bar -> /foo/barEventWorker.js
-/foo/bar/ -> /foo/barEventWorker.js
-/foo/bar/thinger.html -> /foo/barEventWorker.js
-/foo/bar/baz/thinger.html -> /foo/barEventWorker.js
+/foo -> /fooServiceWorker.js
+/foo?blarg -> /fooServiceWorker.js
+/foo/ -> /fooServiceWorker.js
+/foo/thinger.html -> /fooServiceWorker.js
+/foobar.html -> /fooServiceWorker.js
+/foo/other/thinger.html -> /fooServiceWorker.js
+/foo/bar -> /foo/barServiceWorker.js
+/foo/bar/ -> /foo/barServiceWorker.js
+/foo/bar/thinger.html -> /foo/barServiceWorker.js
+/foo/bar/baz/thinger.html -> /foo/barServiceWorker.js
/index.html -> <fallback to native>
/whatevs/index.html -> <fallback to native>
```
@@ -221,15 +230,15 @@ In the above example with registrations for `/foo*` and `/foo/bar*`, the followi
when you then browse to "/foo"?
-->
-"fallback to native" is the browser's built-in behavior for fetching resources -- the thing the Fetch Event defers to when it doesn't handle a fetch with `e.respondWith()`.
+"fallback to native" is the browser's built-in behavior for fetching resources -- the thing the Fetch Service defers to when it doesn't handle a fetch with `e.respondWith()`.
-Note: if `e.respondWith()` isn't called when handling a connection in `/foo/barEventWorker.js`, it does not cascade to `/fooEventWorker.js`, it falls back to the browser's built-in network behavior.
+Note: if `e.respondWith()` isn't called when handling a connection in `/foo/barServiceWorker.js`, it does not cascade to `/fooServiceWorker.js`, it falls back to the browser's built-in network behavior.
-One more note: Last-registration wins. If two pages on a site are visited in order and both register an EventWorker for `"/*"` (or any other identical path), the second page visited will have its EventWorker installed. Only when the specified EventWorker scripts are identical byte-for-byte will there appear not to have been any change. In all other cases, the upgrade dance is performed (see below) and the last registration is now the effective one.
+One more note: Last-registration wins. If two pages on a site are visited in order and both register an ServiceWorker for `"/*"` (or any other identical path), the second page visited will have its ServiceWorker installed. Only when the specified ServiceWorker scripts are identical byte-for-byte will there appear not to have been any change. In all other cases, the upgrade dance is performed (see below) and the last registration is now the effective one.
#### Registrations Map Navigations, Documents Map Fetches
-It's important to understand that `navigator.registerEventWorker()` _only affects navigations_. Let's imagine for just a minute that we have a server that will hand back HTML or JSON for a given URL depending on whether the query parameter `?json=1` is included. Let's say this resource is hosted at `http://www.example.com/services/data`.
+It's important to understand that `navigator.registerServiceWorker()` _only affects navigations_. Let's imagine for just a minute that we have a server that will hand back HTML or JSON for a given URL depending on whether the query parameter `?json=1` is included. Let's say this resource is hosted at `http://www.example.com/services/data`.
Now, let's assume the page served by browsing to that URL is:
@@ -239,7 +248,7 @@ Now, let's assume the page served by browsing to that URL is:
<html>
<head>
<script>
- navigator.registerEventWorker("/services/data", "/services/data/ctrl.js");
+ navigator.registerServiceWorker("/services/data", "/services/data/ctrl.js");
</script>
</head>
</html>
@@ -253,7 +262,7 @@ What happens when we visit `http://www.example.com/index.html` that includes:
<html>
<head>
<script>
- navigator.registerEventWorker("/*", "/ctrl.js");
+ navigator.registerServiceWorker("/*", "/ctrl.js");
</script>
<script src="/services/data?json=1"></script>
</head>
@@ -262,57 +271,59 @@ What happens when we visit `http://www.example.com/index.html` that includes:
</html>
```
-Assuming a user visits them in order and both EventWorker install successfully, what happens the next time that user visits `/index.html`? What EventWorker is used for `/services/data?json=1`?
+Assuming a user visits them in order and both ServiceWorker install successfully, what happens the next time that user visits `/index.html`? What ServiceWorker is used for `/services/data?json=1`?
-The answer hinges on how requests map to EventWorkers. The third rule of EventWorkers is:
+The answer hinges on how requests map to ServiceWorkers. The third rule of ServiceWorkers is:
> All _resource requests_ from a controlled document are sent to _that
-> document's_ EventWorker.
+> document's_ ServiceWorker.
Looking back at our `index.html`, we see two different request types: a navigation for an `<iframe>` and a resource request for a script. Since iframe loading is a navigation and not a "naked" resource request, it matches the rules for longest-prefix, an instance of `/services/data/ctrl.js` is started and a single `onfetch` is dispatched ot it. The script loading, on the other hand, is a sub-resource request and not a navigation, so it's send to the instance of `/ctrl.js` that was started when the user initially navigated to `http://www.example.com/index.html`, either by typing it into the address bar or clicking on a link that took them there.
-Since resource requests (not navigations) are always sent to the EventWorker for the document it is issued from, and since documents always map to the EventWorkers they're born with, our script request will be send to `/ctrl.js` and not `/services/data/ctrl.js`.
+Since resource requests (not navigations) are always sent to the ServiceWorker for the document it is issued from, and since documents always map to the ServiceWorkers they're born with, our script request will be send to `/ctrl.js` and not `/services/data/ctrl.js`.
<!-- FIXME(slightlyoff):
Add a graphic here to explain the fetching/matching
-->
-#### EventWorkers Do Not Control Requests For EventWorkers
+#### ServiceWorkers Do Not Control Requests For ServiceWorkers
-At this point it might seem as though a bit of script executing a registration from a page that is itself controlled might generate a sub-resource request for an EventWorker that might be satisfied by the current EventWorker! Luckily the system explicitly prevents such an Inception-like event from ever happening by treating all fetches and resource loads for EventWorkers and their sub-resources as "naked" fetches against the browser's default HTTP behavior.
+At this point it might seem as though a bit of script executing a registration from a page that is itself controlled might generate a sub-resource request for an ServiceWorker that might be satisfied by the current ServiceWorker! Luckily the system explicitly prevents such an Inception-like event from ever happening by treating all fetches and resource loads for ServiceWorkers and their sub-resources as "naked" fetches against the browser's default HTTP behavior.
-A minor caveat is that EventWorker scripts are never [heuristically cached](http://www-archive.mozilla.org/projects/netlib/http/http-caching-faq.html) and when updated are assumed stale if last fetched over 24 hours ago. But those features only ensure that apps can't screw themselves over with one ill-placed `Expires` header. If the browser checks for an updated version and doesn't find anything different (i.e., they're the same bytes) or can't fetch it at all for some reason (an HTTP error code), nothing happens. If an updated version is found, the upgrade process is started (see below). All of this happens outside of the "controlled" world, for better and for worse.
+A minor caveat is that ServiceWorker scripts are never [heuristically cached](http://www-archive.mozilla.org/projects/netlib/http/http-caching-faq.html) and when updated are assumed stale if last fetched over 24 hours ago. But those features only ensure that apps can't screw themselves over with one ill-placed `Expires` header. If the browser checks for an updated version and doesn't find anything different (i.e., they're the same bytes) or can't fetch it at all for some reason (an HTTP error code), nothing happens. If an updated version is found, the upgrade process is started (see below). All of this happens outside of the "controlled" world, for better and for worse.
#### Last-Registration-Wins
The registration system is also last-registration-wins. That means if two pages on `www.example.com` set a registration to control `/*`, the one a user visits second (assuming the first doesn't interfere) will be installed and over-write the previous registration.
-This makes sense because registration is the same as replacement. That is to say, if you have content that wants to replace the existing EventWorker with one at a different URL (perhaps a heavy-handed form of "new version"), registering the new URL is the way that you indicate that the old registration is no longer the preferred one.
+This makes sense because registration is the same as replacement. That is to say, if you have content that wants to replace the existing ServiceWorker with one at a different URL (perhaps a heavy-handed form of "new version"), registering the new URL is the way that you indicate that the old registration is no longer the preferred one.
### Caching
So far we've only seen responses that are generated by code. This is an interesting, but not likely common case. Most often web apps are built as sets of resources in a directory or on a disk, wired together with html and re-constituted as a while by the browser at runtime. It's a good model that has served us well for more than a decade, allowing near endless flexibility in application architecture and the ability to scale services massively.
REST and good URL design have particularly stood the test of time as patterns that we abandon at our own risk. As a result, modern frameworks and thoughtful developers expend great care when working to compose HTML, CSS, and scripts that can be distributed to CDNs and perform well.
-A major challenge for developers attempting to bring web apps to the offline world has been the unfriendliness of existing solutions to the workflow of "put things on disk, visit URL, hit ctrl-r". EventWorkers, in contrast, enable a straightforward model that gives developers explicit control over what/when/how to cache resources without adding layers of indirection which they cannot control.
+A major challenge for developers attempting to bring web apps to the offline world has been the unfriendliness of existing solutions to the workflow of "put things on disk, visit URL, hit ctrl-r". ServiceWorkers, in contrast, enable a straightforward model that gives developers explicit control over what/when/how to cache resources without adding layers of indirection which they cannot control.
-In fact, our first example EventWorker, coupled with [IndexedDB](https://developer.mozilla.org/en-US/docs/IndexedDB) and XHR might be all that's *technically* necessary to build a programmatic offline solution. It would, however, be a royal pain in the ass to use -- either because developers would need to make or find large-ish libraries to managed fetching/storing/retrieving resources or because XHR doesn't provide all the power that's strictly necessary.
+In fact, our first example ServiceWorker, coupled with [IndexedDB](https://developer.mozilla.org/en-US/docs/IndexedDB) and XHR might be all that's *technically* necessary to build a programmatic offline solution. It would, however, be a royal pain in the ass to use -- either because developers would need to make or find large-ish libraries to managed fetching/storing/retrieving resources or because XHR doesn't provide all the power that's strictly necessary.
-This is where the global `caches` map comes in. Each EventWorker has a global `caches` Map which holds instances of `Cache`. A `Cache` is just what it sounds like: a repository of stored `Response` objects; or in this case, `Promise`s which represent `Response`s which may or may not yet be available from the network.
+This is where the global `caches` map comes in. Each ServiceWorker has a global `caches` Map which holds instances of `Cache`. A `Cache` is just what it sounds like: a repository of stored `Response` objects; or in this case, `Promise`s which represent `Response`s which may or may not yet be available from the network.
_NOTE: You might know "Promise" by the name "Future". If not, see the [case for Promises in DOM](https://github.com/slightlyoff/DOMPromise/blob/master/README.md#Promises-promises-i-dont-speak-your-crazy-moon-language) or an explanation [here](http://www.xanthir.com/b4PY0)._
-Using `Cache`s is perhaps simpler than talking about them, so here's some tiny example code that implements the `oninstall` event, starts populating a single `Cache` with content, and tells the system that the EventWorker is ready if-and- only-if all the there resources in the cache are downloaded.
+Using `Cache`s is perhaps simpler than talking about them, so here's some tiny example code that implements the `oninstall` event, starts populating a single `Cache` with content, and tells the system that the ServiceWorker is ready if-and- only-if all the there resources in the cache are downloaded.
```js
// caching.js
this.version = 1;
var base = "http://videos.example.com";
this.addEventListener("install", function(e) {
+ e.services = ["fetch"];
+
// Create a cache of resources. Begins the process of fetching them.
- // URLs are relative to the EventWorker
+ // URLs are relative to the ServiceWorker
var shellResources = new Cache(
base + "/assets/v1/base.css",
base + "/assets/v1/app.js",
@@ -328,19 +339,21 @@ this.addEventListener("install", function(e) {
});
```
-`Cache` objects contain an `items` map which contains `Promise`s for each of the resources, keyed by their absolute URL. When all of the resources added to a cache are downloaded successfully, the `Promise` vended by `.ready()` completes successfully. Our example wires that up to the resolution to the completion of installation, meaning this EventWorker won't be "activated" until at least that set of resources is cached and ready. Pretty neat.
+`Cache` objects contain an `items` map which contains `Promise`s for each of the resources, keyed by their absolute URL. When all of the resources added to a cache are downloaded successfully, the `Promise` vended by `.ready()` completes successfully. Our example wires that up to the resolution to the completion of installation, meaning this ServiceWorker won't be "activated" until at least that set of resources is cached and ready. Pretty neat.
### Serving Cached Resources
Now that we've got some resources in a cache, what can we do with 'em?
-Most of the EventWorker interfaces that can take `Response` instances are designed to also work with `Promise`s that wrap `Response`s. Here's an expanded version of `caching.js` that adds an `onfetch` handler to serve the URLs in question:
+Most of the ServiceWorker interfaces that can take `Response` instances are designed to also work with `Promise`s that wrap `Response`s. Here's an expanded version of `caching.js` that adds an `onfetch` handler to serve the URLs in question:
```js
// caching.js
this.version = 1;
this.addEventListener("install", function(e) {
+ e.services = ["fetch"];
+
// Create a cache of resources. Begins the process of fetching them.
var shellResources = new Cache(
"/app.html",
@@ -358,7 +371,7 @@ this.addEventListener("install", function(e) {
});
this.addEventListener("fetch", function(e) {
- // No "onfetch" events are dispatched to the EventWorker until it successfully
+ // No "onfetch" events are dispatched to the ServiceWorker until it successfully
// installs.
var shellResources = this.caches.get("shell-v1");
@@ -369,7 +382,7 @@ this.addEventListener("fetch", function(e) {
});
```
-The behavior of `respondWith()` is conditional: if the cache returns a valid `Response`, that is what is sent back to the requesting document. If the `Promise` generated by `match()` returns anything else or resolves as an error, the request is then routed to the browser's HTTP stack (as would happen without the EventWorker).
+The behavior of `respondWith()` is conditional: if the cache returns a valid `Response`, that is what is sent back to the requesting document. If the `Promise` generated by `match()` returns anything else or resolves as an error, the request is then routed to the browser's HTTP stack (as would happen without the ServiceWorker).
The `this.caches.get()/.match()` dance is a bit wordy, so to cut this short there's a `match` convenience method on the global `caches` object to make our `onfetch` handler shorter but instead of taking one parameter (the URL), it takes two (the cache name and the URL):
@@ -386,7 +399,7 @@ Handy!
HTTP redirects happen whenever a browser receives a `3xx` status code, most often [`302`](http://en.wikipedia.org/wiki/HTTP_302).
-Redirection is a fact of life in modern networks, so EventWorkers must have something intelligent to say about them. To enable this, a `forwardTo()` method is made available as a convenience in the `onfetch` event. It's functionally the same as creating a `SameOriginResponse`, setting the `.statusCode` to 302, providing a `Location: ...` header, and responding with that. Both work fine, but in most cases `e.forwardTo(urlOrString)` is easier:
+Redirection is a fact of life in modern networks, so ServiceWorkers must have something intelligent to say about them. To enable this, a `forwardTo()` method is made available as a convenience in the `onfetch` event. It's functionally the same as creating a `SameOriginResponse`, setting the `.statusCode` to 302, providing a `Location: ...` header, and responding with that. Both work fine, but in most cases `e.forwardTo(urlOrString)` is easier:
```js
this.addEventListener("fetch", function(e) {
@@ -397,7 +410,7 @@ this.addEventListener("fetch", function(e) {
});
```
-The important thing to note is that redirects behave the way they would as if a server had responded with a redirect: the browser will fetch the second resource directly as though it were creating a new request from the new URL. That is to say, if it's a top-level navigation and an EventWorker redirects to a different domain (or a bit of the same domain that it doesn't control), it won't get another chance to provide content for the eventual URL. In the case of same-domain & scope navigations and _all_ sub-resource redirects, the new request will be sent back through the `fetch` event listener again.
+The important thing to note is that redirects behave the way they would as if a server had responded with a redirect: the browser will fetch the second resource directly as though it were creating a new request from the new URL. That is to say, if it's a top-level navigation and an ServiceWorker redirects to a different domain (or a bit of the same domain that it doesn't control), it won't get another chance to provide content for the eventual URL. In the case of same-domain & scope navigations and _all_ sub-resource redirects, the new request will be sent back through the `fetch` event listener again.
But wait, doesn't this open up the potential for a loop? It does, but this is a case browsers already detect and handle by terminating the loop after some number of iterations. The same will happen to your requests should you create a loop.
@@ -412,7 +425,7 @@ So now we've got a mechanism to cache things and a way to serve up those cached
_Indeed_
-EventWorkers get first crack at requests, so if an app caches its "shell" (the stuff needed to bootstrap enough UI for navigating content), it's possible to make offline apps using EventWorkers. More excitingly still, offline support just sort of falls out of the system naturally. Building EventWorker-based apps inverts the model: apps don't have to care about "offline" independently of "just building the app". Cached resources can be used instead of going to the network _all_ the time.
+ServiceWorkers get first crack at requests, so if an app caches its "shell" (the stuff needed to bootstrap enough UI for navigating content), it's possible to make offline apps using ServiceWorkers. More excitingly still, offline support just sort of falls out of the system naturally. Building ServiceWorker-based apps inverts the model: apps don't have to care about "offline" independently of "just building the app". Cached resources can be used instead of going to the network _all_ the time.
But what to do when some content isn't available? Assume for a second that the video app is loaded and the user tries to visit a library of videos for sale or download -- a list that's far too big and dynamic to reasonably cache client-side, to say nothing of the videos themselves. How do we fallback gracefully and provide a "library not available when offline" message to users?
@@ -447,52 +460,52 @@ That might take a bit of explaining, particularly if you don't use Promises (aka
And _that_ is how we try-online-with-a-fallback! Variations of this might be better for when in flaky network scenarios where a low-ish timeout might be the right way to break the tie. If the browser is offline, our network fetch will either come from from the HTTP cache (depending on what's in there and the expiration behaviors) or fail immediately. Either way, the event listener gives the user a useful result. Huzzah!
-## EventWorker Installation & Upgrade
+## ServiceWorker Installation & Upgrade
A couple of examples of installation have been presented so far:
- - EventWorkers that don't handle the `oninstall` event at all (in which case they're assume to have succeeded).
- - EventWorkers that create new Caches and delay declaring success for their installation until those Caches are populated.
+ - ServiceWorkers that don't handle the `oninstall` event at all (in which case they're assume to have succeeded).
+ - ServiceWorkers that create new Caches and delay declaring success for their installation until those Caches are populated.
-The biggest scenario that hasn't been touched on yet is upgrades. Recall that browsers check for updated versions of EventWorker scripts roughly once a day. What happens if they find a new version?
+The biggest scenario that hasn't been touched on yet is upgrades. Recall that browsers check for updated versions of ServiceWorker scripts roughly once a day. What happens if they find a new version?
-For the new version (we'll call it "2"), nothing much changes about the process. `oninstall` is dispatched (which it can handle or not) and, if no error occurs, it's the new EventWorker-in-waiting.
+For the new version (we'll call it "2"), nothing much changes about the process. `oninstall` is dispatched (which it can handle or not) and, if no error occurs, it's the new ServiceWorker-in-waiting.
-Wait, "EventWorker-in-waiting"?
+Wait, "ServiceWorker-in-waiting"?
-Yep: recall the first rule of EventWorkers: _"Documents live out their whole lives using the EventWorker they start with."_
+Yep: recall the first rule of ServiceWorkers: _"Documents live out their whole lives using the ServiceWorker they start with."_
-Assume the browser found and installed v2 while a tab that had been born under EventWorker v1 was still running. Sure, the EventWorker itself might be killed at any time, but any new resource request generated from that window will re- instantiate that version of the EventWorker and expect it to service the request.
+Assume the browser found and installed v2 while a tab that had been born under ServiceWorker v1 was still running. Sure, the ServiceWorker itself might be killed at any time, but any new resource request generated from that window will re- instantiate that version of the ServiceWorker and expect it to service the request.
-So what if a new tab is created? Which EventWorker does it get, v1 or v2?
+So what if a new tab is created? Which ServiceWorker does it get, v1 or v2?
### Wait-For-Restart
-The default policy is that this new tab will be controlled by v1. This is done to prevent the crazy-town scenario of multiple EventWorker versions running at the same time, possibly creating conflicts for IndexedDB schemas, content caches, and the like. Yes, there's a small window during `oninstall` when v2 will be running at the same time as v1, but they won't both be serving content. The advice then is: _don't do irreversible things during `oninstall`_. It's a good place to get a jump on populating caches (with unique names if the new content is reliant on the new EventWorker), but a bad place to do things like schema and model upgrades for your app.
+The default policy is that this new tab will be controlled by v1. This is done to prevent the crazy-town scenario of multiple ServiceWorker versions running at the same time, possibly creating conflicts for IndexedDB schemas, content caches, and the like. Yes, there's a small window during `oninstall` when v2 will be running at the same time as v1, but they won't both be serving content. The advice then is: _don't do irreversible things during `oninstall`_. It's a good place to get a jump on populating caches (with unique names if the new content is reliant on the new ServiceWorker), but a bad place to do things like schema and model upgrades for your app.
-The alternative scenario is one in which the new version of your EventWorker is discovered and installed and no documents are running against v1. This could happen because:
+The alternative scenario is one in which the new version of your ServiceWorker is discovered and installed and no documents are running against v1. This could happen because:
- - v1 was installed by a page that was loaded "naked", but which was never reloaded so as to start under the EventWorker.
+ - v1 was installed by a page that was loaded "naked", but which was never reloaded so as to start under the ServiceWorker.
- The browser fetched an update of it's own volition. It's allowed to do that!
- - Between the time `oninstall` started for the v2 EventWorker and when `waitUntil()` was finally satisfied, all of the app's windows were closed.
+ - Between the time `oninstall` started for the v2 ServiceWorker and when `waitUntil()` was finally satisfied, all of the app's windows were closed.
-When this happens, v2 instantly becomes the active EventWorker, so the next time you navigate to a URL controlled by the registration, v2 would get first crack at it.
+When this happens, v2 instantly becomes the active ServiceWorker, so the next time you navigate to a URL controlled by the registration, v2 would get first crack at it.
-Indeed, v2 will become the active EventWorker _just as soon as all v1 documents are closed_.
+Indeed, v2 will become the active ServiceWorker _just as soon as all v1 documents are closed_.
-When v2 *does* become the active EventWorker, another event -- `onactivate` -- is sent to v2. This happens _before any fetches are dispatched_. This is the ideal time to upgrade database schemas and the like, but be careful not to do too much work. Applications will be blocked from loading while `onactivate` is being serviced (including any extensions asked for via `e.waitUntil()`, which is also available to `onactivate` handlers). Treat `onactivate` as a time to stake your claim as the new version but beware doing more than that lest you make your app unavailable!
+When v2 *does* become the active ServiceWorker, another event -- `onactivate` -- is sent to v2. This happens _before any fetches are dispatched_. This is the ideal time to upgrade database schemas and the like, but be careful not to do too much work. Applications will be blocked from loading while `onactivate` is being serviced (including any extensions asked for via `e.waitUntil()`, which is also available to `onactivate` handlers). Treat `onactivate` as a time to stake your claim as the new version but beware doing more than that lest you make your app unavailable!
<!-- FIXME(slightlyoff):
Add a graphic here to explain the wait-until-restart lifetime
-->
### Replacement
-An alternative policy is available for the daring: a new EventWorker can choose to cut-in and replace an existing one. And before you ask, yes, this does break the first rule. But not much.
+An alternative policy is available for the daring: a new ServiceWorker can choose to cut-in and replace an existing one. And before you ask, yes, this does break the first rule. But not much.
-To replace an existing EventWorker, use the `.replace()` method of the `oninstall` event during the event dispatch. In fact, you can even call `.replace()` on the very first install of an EventWorker, which will now make your EventWorker the proud owner of all windows/tabs whose URLs match the registration origin and scope -- including the page that registered it.
+To replace an existing ServiceWorker, use the `.replace()` method of the `oninstall` event during the event dispatch. In fact, you can even call `.replace()` on the very first install of an ServiceWorker, which will now make your ServiceWorker the proud owner of all windows/tabs whose URLs match the registration origin and scope -- including the page that registered it.
-let's clarify with an example: here we'll also compare the versions to ensure that they aren't so far apart that stepping in would break things; leaving the old EventWorker in place if the version skew is too great and taking over if it's a difference our new version is confident it can handle. Consider v1.3 vs. v1.0:
+let's clarify with an example: here we'll also compare the versions to ensure that they aren't so far apart that stepping in would break things; leaving the old ServiceWorker in place if the version skew is too great and taking over if it's a difference our new version is confident it can handle. Consider v1.3 vs. v1.0:
```js
// caching.js
@@ -503,6 +516,8 @@ var shellCacheName = "shell-v" + parseInt(this.version);
var contentCacheName = "content";
this.addEventListener("install", function(e) {
+ e.services = ["fetch"];
+
// Create a cache of resources. Begins the process of fetching them.
var shellResources = new Cache(
assetBase + "/base.css",
@@ -534,54 +549,54 @@ this.addEventListener("install", function(e) {
// ...onfetch, etc...
```
-The `previousVersion` field of the event is filled in using a [structured clone](https://developer.mozilla.org/en-US/docs/DOM/The_structured_clone_algorithm) of the global `version` property set by the last execution of the previous EventWorker. It's a good idea both to always set a `version` and, sort of obviously, not to make it something that can't be cloned or which varies.
+The `previousVersion` field of the event is filled in using a [structured clone](https://developer.mozilla.org/en-US/docs/DOM/The_structured_clone_algorithm) of the global `version` property set by the last execution of the previous ServiceWorker. It's a good idea both to always set a `version` and, sort of obviously, not to make it something that can't be cloned or which varies.
-What of the old EventWorker? What happens to it?
+What of the old ServiceWorker? What happens to it?
<!--
-The upgrade dance isn't unilateral. In most cases it's a good idea for the old version to do whatever cleanup it might want to do before handing the reigns to the new whippersnapper. Since replacement is a bit more hairy than wait-for-restart, a separate `onreplaced` event is sent to EventWorkers that are about to be replaced.
+The upgrade dance isn't unilateral. In most cases it's a good idea for the old version to do whatever cleanup it might want to do before handing the reigns to the new whippersnapper. Since replacement is a bit more hairy than wait-for-restart, a separate `onreplaced` event is sent to ServiceWorkers that are about to be replaced.
-->
-The replacing EventWorker can send a message to the old EventWorker in `oninstalled` using `e.previous.postMessage()`. This can blossom into a bi-directional discussion if both sides [have registered `onmessage` handlers](https://developer.mozilla.org/en-US/docs/DOM/window.postMessage), but that's out of the scope of this document for now.
+The replacing ServiceWorker can send a message to the old ServiceWorker in `oninstalled` using `e.previous.postMessage()`. This can blossom into a bi-directional discussion if both sides [have registered `onmessage` handlers](https://developer.mozilla.org/en-US/docs/DOM/window.postMessage), but that's out of the scope of this document for now.
### On Sane Versioning
-There's no universally "right" solution to versioning your EventWorkers, but a couple of Do's and Dont's can help keep your EventWorkers out of trouble and your app in ship-shape:
+There's no universally "right" solution to versioning your ServiceWorkers, but a couple of Do's and Dont's can help keep your ServiceWorkers out of trouble and your app in ship-shape:
#### _Do_:
- - Always set a global `version` at the top of your EventWorkers. A simple number or string is a good bet.
+ - Always set a global `version` at the top of your ServiceWorkers. A simple number or string is a good bet.
- Put cache initialization and loading into your `oninstall`, not `onfetch`.
- Version your assets and URL endpoints _in the URL_, not a part of the query string.
#### _Don't_:
- - Keep state in global variables inside EventWorkers.
+ - Keep state in global variables inside ServiceWorkers.
- Call `.replace()` in `oninstall` unless you're darned sure you know what you're doing. It's most often best to let wait-for-restart do its thing.
<!--
## Who's On First?
FIXME(slightlyoff): cover messaging:
- - window to EventWorker
- - EventWorker to window
- - new-to-old EventWorker and vice versa
+ - window to ServiceWorker
+ - ServiceWorker to window
+ - new-to-old ServiceWorker and vice versa
- a simple example of "please upgrade now"
-->
-## Cross-Origin EventWorkers?
+## Cross-Origin ServiceWorkers?
-Understanding fetches, caches, installation and upgrades are most of what you'll need to successfully use EventWorkers to enrich your apps. The performance implications might already be dawning on you, and they can be absolutely profound. And that's before you get to being able to architect for offline-first and provide a seamless experience based around synchronization (not 404 vs. working).
+Understanding fetches, caches, installation and upgrades are most of what you'll need to successfully use ServiceWorkers to enrich your apps. The performance implications might already be dawning on you, and they can be absolutely profound. And that's before you get to being able to architect for offline-first and provide a seamless experience based around synchronization (not 404 vs. working).
-One of the first advanced concerns that major apps hit is "how do I host things from a CDN?" By definition, these are servers in other places, often on other domains, that your content references. Can EventWorkers be hosted on CDNs?
+One of the first advanced concerns that major apps hit is "how do I host things from a CDN?" By definition, these are servers in other places, often on other domains, that your content references. Can ServiceWorkers be hosted on CDNs?
No, sorry. But they can include resources (via `importScripts()`) that are.
-The reasons for this restriction is that EventWorkers create the opportunity for a bad actor to turn a bad day into a bad eternity. Imagine an XSS vulnerability anywhere on a site. An attacker that can run a bit of JS can now request a new EventWorker be installed. If that EventWorker is registered from different origin (say, `evil.com`), the EventWorker itself can prevent updates to content which might dislodge it. Worse, the original application wouldn't be able to help the users who have been stranded.
+The reasons for this restriction is that ServiceWorkers create the opportunity for a bad actor to turn a bad day into a bad eternity. Imagine an XSS vulnerability anywhere on a site. An attacker that can run a bit of JS can now request a new ServiceWorker be installed. If that ServiceWorker is registered from different origin (say, `evil.com`), the ServiceWorker itself can prevent updates to content which might dislodge it. Worse, the original application wouldn't be able to help the users who have been stranded.
-By mandating same-origin restrictions for the EventWorker script, it's possible for an attacked application to help those users. Their browsers will request EventWorker updates from the source origin no less frequently than once a day, meaning an intermittent XSS is a hole that can still be closed.
+By mandating same-origin restrictions for the ServiceWorker script, it's possible for an attacked application to help those users. Their browsers will request ServiceWorker updates from the source origin no less frequently than once a day, meaning an intermittent XSS is a hole that can still be closed.
-It may some day be possible to loosen this policy via a new CSP directive, but for now, the best mental model for hosting EventWorkers is that the script you pass to `navigator.registerEventWorker()` must live on the same domain as the document itself. But EventWorkers can use `importScripts()` to include other scripts that are hosted elsewhere, e.g. on a CDN.
+It may some day be possible to loosen this policy via a new CSP directive, but for now, the best mental model for hosting ServiceWorkers is that the script you pass to `navigator.registerServiceWorker()` must live on the same domain as the document itself. But ServiceWorkers can use `importScripts()` to include other scripts that are hosted elsewhere, e.g. on a CDN.
### `importScripts()` & 3rd-party Routers
@@ -593,34 +608,34 @@ In `onfetch`, `e.respondWith()` and `e.forwardTo()` behave as though [`e.stopImm
In `oninstalled` and `onactivate`, multiple calls to `e.waitUntil()` will ensure that the overall operation isn't considered a success until _all_ the passed `Promise`s are resolved successfully.
-This all becomes more relevant when you consider that EventWorkers support the general Web Worker API [`importScripts()`](https://developer.mozilla.org/en-US/docs/DOM/Worker/Functions_available_to_workers#Worker-specific_functions). It's important to note that _only the scripts that have been imported the first time the worker is run will be cached along side it by the browser_. The upside is that imported scripts _will_ be downloaded and cached alongside the main EventWorker script.
+This all becomes more relevant when you consider that ServiceWorkers support the general Web Worker API [`importScripts()`](https://developer.mozilla.org/en-US/docs/DOM/Worker/Functions_available_to_workers#Worker-specific_functions). It's important to note that _only the scripts that have been imported the first time the worker is run will be cached along side it by the browser_. The upside is that imported scripts _will_ be downloaded and cached alongside the main ServiceWorker script.
-What does that imply? Lots of good stuff. First, EventWorkers can import libraries from elsewhere, including other origins and CDNs. Next, Since these scripts can register their own handlers, they can manage bits of the world that they are written to. For instance, an EventWorker can include the EventWorker bit for a third-party set of widgets or analytics without worrying about the details of the URLs they manage or need.
+What does that imply? Lots of good stuff. First, ServiceWorkers can import libraries from elsewhere, including other origins and CDNs. Next, Since these scripts can register their own handlers, they can manage bits of the world that they are written to. For instance, an ServiceWorker can include the ServiceWorker bit for a third-party set of widgets or analytics without worrying about the details of the URLs they manage or need.
## Cross-Origin Resources
What if an app wants to cache items that come from a CDN or other domain? It's possible to request many of them directly using `<script>`, `<img>`, `<video>` and `<link>` elements. It would be hugely limiting if this sort of runtime collaboration broke when offline. Similarly, it's possible to XHR many sorts of off-domain resources when appropriate [CORS headers](https://developer.mozilla.org/en-US/docs/HTTP/Access_control_CORS) are set.
-EventWorkers enable this by allowing `Cache`s to fetch and cache off-origin items. Some restrictions apply, however. First, unlike same-origin resources which are managed in the `Cache` as `Promise`s for `SameOriginResponse` instances, the objects stored are `Promise`s for `CrossOriginResponse` instances. `CrossOriginResponse` provides a much less expressive API than `SameOriginResponse`; the bodies and headers cannot be read or set, nor many of the other aspects of their content inspected. They can be passed to `respondWith()` and `forwardTo()` in the same manner as `SameOriginResponse`s, but can't be meaningfully created programmatically. These limitations are necessary to preserve the security invariants of the platform. Allowing `Cache`s to store them allows applications to avoid re-architecting in most cases.
+ServiceWorkers enable this by allowing `Cache`s to fetch and cache off-origin items. Some restrictions apply, however. First, unlike same-origin resources which are managed in the `Cache` as `Promise`s for `SameOriginResponse` instances, the objects stored are `Promise`s for `CrossOriginResponse` instances. `CrossOriginResponse` provides a much less expressive API than `SameOriginResponse`; the bodies and headers cannot be read or set, nor many of the other aspects of their content inspected. They can be passed to `respondWith()` and `forwardTo()` in the same manner as `SameOriginResponse`s, but can't be meaningfully created programmatically. These limitations are necessary to preserve the security invariants of the platform. Allowing `Cache`s to store them allows applications to avoid re-architecting in most cases.
Note that CORS plays an important role in the cross-origin story for many resource types: fonts, images, XHR requests. All cross-origin resources that are fetched by `Cache`s succeed when fetched, but may not display/run correctly when their CORS headers are replayed to the document fetching them.
A few things to keep in mind regarding cross-origin resources that you may cache or request via `fetch()`:
- * You can mix origins, but it might redirect. Consider a request from `example.com/index.html` to `example.com/assets/v1/script.js`. A `fetch` event listener that calls `e.respondWith(caches.match('cdn content', 'http://cdn.com/script.js'))` may upset some expectations. From the perspective of the page, this response will be treated as a redirect to whatever the original URL of the response body was. Scripts that interrogate the final state of the page wil see the redirected URL as the `src`, not the original one. The reason for this is that it would otherwise be possible for a page to co-operate with an EventWorker to defeat cross-origin restrictions, leaking data that other origins were counting on the browser to protect.
+ * You can mix origins, but it might redirect. Consider a request from `example.com/index.html` to `example.com/assets/v1/script.js`. A `fetch` event listener that calls `e.respondWith(caches.match('cdn content', 'http://cdn.com/script.js'))` may upset some expectations. From the perspective of the page, this response will be treated as a redirect to whatever the original URL of the response body was. Scripts that interrogate the final state of the page wil see the redirected URL as the `src`, not the original one. The reason for this is that it would otherwise be possible for a page to co-operate with an ServiceWorker to defeat cross-origin restrictions, leaking data that other origins were counting on the browser to protect.
* CORS does what CORS does. The body of a cross-origin response served with CORS headers won't be readable from a `fetch` (this restriction might be lifted later), but when sent to a document, the CORS headers will be replayed and the document will be able to do anything CORS would have allowed with the content.
* There's no harm in responding to a cross-origin request with a `new SameOriginResponse()` that you create out of thin air. Since the document in question is the thing that's at risk, and since the other APIs available to you won't allow you undue access to cross-origin response bodies, you can pretend you're any other origin -- so long as the only person you're fooling is yourself.
## Conclusions
-This document only scratches the surface of what EventWorkers enable,
+This document only scratches the surface of what ServiceWorkers enable,
and aren't an exhaustive list of all of the available APIs available
-to controlled pages or EventWorker instances. If you have more
+to controlled pages or ServiceWorker instances. If you have more
questions, they might be answered in the [Advanced Topics
Explainer](advanced_topics.md). Nor does it cover emergent practices
for authoring, composing, and upgrading applications architected to
-use EventWorkers. It is, hopefully, a guide to understanding the
-promise of EventWorkers and the rich Promise of offline-by-default
+use ServiceWorkers. It is, hopefully, a guide to understanding the
+promise of ServiceWorkers and the rich Promise of offline-by-default
web applications that are URL friendly and scalable.
## Acknowledgments
View
4 package.json
@@ -1,10 +1,10 @@
{
- "name": "EventWorker",
+ "name": "ServiceWorker",
"version": "0.0.0",
"description": "Spec for controlling and caching requests in the browser",
"repository": {
"type": "git",
- "url": "git://github.com/slightlyoff/EventWorker.git"
+ "url": "git://github.com/slightlyoff/ServiceWorker.git"
},
"license": "Apache 2.0",
"readmeFilename": "README.md",
View
28 event_worker.js → service_worker.js
@@ -32,7 +32,7 @@ var InstallPhaseEvent = (function (_super) {
this.previousVersion = 0;
}
// Delay treating the installing worker until the passed Promise resolves
- // successfully. This is primarily used to ensure that an EventWorker is not
+ // successfully. This is primarily used to ensure that an ServiceWorker is not
// active until all of the "core" Caches it depends on are populated.
// TODO: what does the returned promise do differently to the one passed in?
InstallPhaseEvent.prototype.waitUntil = function (f) {
@@ -41,16 +41,16 @@ var InstallPhaseEvent = (function (_super) {
return InstallPhaseEvent;
})(_Event);
-var InstalledEvent = (function (_super) {
- __extends(InstalledEvent, _super);
- function InstalledEvent() {
+var InstallEvent = (function (_super) {
+ __extends(InstallEvent, _super);
+ function InstallEvent() {
_super.apply(this, arguments);
this.previous = null;
}
// Ensures that the worker is used in place of existing workers for
// the currently controlled set of window instances.
// TODO: how does this interact with waitUntil? Does it automatically wait?
- InstalledEvent.prototype.replace = function () {
+ InstallEvent.prototype.replace = function () {
};
// Assists in restarting all windows with the new worker.
@@ -82,36 +82,36 @@ var InstalledEvent = (function (_super) {
// Activate the new worker
// Reload all windows asynchronously
// Resolve promise
- InstalledEvent.prototype.reloadAll = function () {
+ InstallEvent.prototype.reloadAll = function () {
return new Promise(function () {
});
};
- return InstalledEvent;
+ return InstallEvent;
})(InstallPhaseEvent);
// The scope in which worker code is executed
-var EventWorkerScope = (function (_super) {
- __extends(EventWorkerScope, _super);
- function EventWorkerScope() {
+var ServiceWorkerScope = (function (_super) {
+ __extends(ServiceWorkerScope, _super);
+ function ServiceWorkerScope() {
_super.apply(this, arguments);
// Set by the worker and used to communicate to newer versions what they
- // are replaceing (see InstalledEvent::previousVersion)
+ // are replaceing (see InstallEvent::previousVersion)
this.version = 0;
}
- Object.defineProperty(EventWorkerScope.prototype, "windows", {
+ Object.defineProperty(ServiceWorkerScope.prototype, "windows", {
get: function () {
return new WindowList();
},
enumerable: true,
configurable: true
});
- EventWorkerScope.prototype.fetch = function (request) {
+ ServiceWorkerScope.prototype.fetch = function (request) {
return new Promise(function (r) {
r.resolve(_defaultToBrowserHTTP(request));
});
};
- return EventWorkerScope;
+ return ServiceWorkerScope;
})(SharedWorker);
///////////////////////////////////////////////////////////////////////////////
View
32 event_worker.ts → service_worker.ts
@@ -10,11 +10,11 @@
////////////////////////////////////////////////////////////////////////////////
// extensions to window.navigator
-interface NavigatorEventWorker {
+interface NavigatorServiceWorker {
// null if page has no activated worker
- eventWorker: SharedEventWorker;
+ eventWorker: SharedServiceWorker;
- registerEventWorker(scope: string/* or URL */, url: string/* or URL */): Promise;
+ registerServiceWorker(scope: string/* or URL */, url: string/* or URL */): Promise;
// If an event worker is in-waiting, and its url & scope matches both
// url & scope
// - resolve the promise
@@ -36,18 +36,18 @@ interface NavigatorEventWorker {
//
// Resolves once the install event is triggered without unhandled exceptions
- unregisterEventWorker(scope: string): Promise;
+ unregisterServiceWorker(scope: string): Promise;
// TODO: if we have a worker-in-waiting & an active worker,
// what happens? Both removed?
// TODO: does removal happen immediately or using the same pattern as
// a worker update?
// called when a new worker becomes in-waiting
- oneventworkerinstalled: (ev: Event) => any;
+ oneventworkerinstall: (ev: Event) => any;
// TODO: needs custom event type?
// TODO: is this actually useful? Can't simply reload due to other tabs
- // called when a new worker takes over via InstalledEvent#replace
+ // called when a new worker takes over via InstallEvent#replace
oneventworkerreplaced: (ev: Event) => any;
// TODO: needs custom event type?
// TODO: is this actually useful? Might want to force a reload at this point
@@ -56,7 +56,7 @@ interface NavigatorEventWorker {
}
interface Navigator extends
- NavigatorEventWorker,
+ NavigatorServiceWorker,
EventTarget,
// the rest is just stuff from the default ts definition
NavigatorID,
@@ -67,9 +67,9 @@ interface Navigator extends
// MSNavigatorAbilities
{ }
-interface SharedEventWorker extends Worker {}
-declare var SharedEventWorker: {
- prototype: SharedEventWorker;
+interface SharedServiceWorker extends Worker {}
+declare var SharedServiceWorker: {
+ prototype: SharedServiceWorker;
}
class ReloadPageEvent extends _Event {
@@ -85,13 +85,13 @@ class InstallPhaseEvent extends _Event {
previousVersion: any = 0;
// Delay treating the installing worker until the passed Promise resolves
- // successfully. This is primarily used to ensure that an EventWorker is not
+ // successfully. This is primarily used to ensure that an ServiceWorker is not
// active until all of the "core" Caches it depends on are populated.
// TODO: what does the returned promise do differently to the one passed in?
waitUntil(f: Promise): Promise { return accepted(); }
}
-class InstalledEvent extends InstallPhaseEvent {
+class InstallEvent extends InstallPhaseEvent {
previous: MessagePort = null;
// Ensures that the worker is used in place of existing workers for
@@ -135,7 +135,7 @@ class InstalledEvent extends InstallPhaseEvent {
}
}
-interface InstalledEventHandler { (e:InstalledEvent); }
+interface InstallEventHandler { (e:InstallEvent); }
interface ActivateEventHandler { (e:InstallPhaseEvent); }
interface FetchEventHandler { (e:FetchEvent); }
@@ -147,7 +147,7 @@ interface OnlineEventHandler { (e:_Event); }
interface OfflineEventHandler { (e:_Event); }
// The scope in which worker code is executed
-class EventWorkerScope extends SharedWorker {
+class ServiceWorkerScope extends SharedWorker {
// Mirrors navigator.onLine. We also get network status change events
// (ononline, etc.). The proposed ping() API must be made available here as
// well.
@@ -158,7 +158,7 @@ class EventWorkerScope extends SharedWorker {
}
// Set by the worker and used to communicate to newer versions what they
- // are replaceing (see InstalledEvent::previousVersion)
+ // are replaceing (see InstallEvent::previousVersion)
version: any = 0; // NOTE: versions must be structured-cloneable!
//
@@ -175,7 +175,7 @@ class EventWorkerScope extends SharedWorker {
// Called when a worker is downloaded and being setup to handle
// events (navigations, alerts, etc.)
- oninstalled: InstalledEventHandler;
+ oninstall: InstallEventHandler;
// Called when a worker becomes the active event worker for a mapping
onactivate: ActivateEventHandler;

0 comments on commit 3865e9a

Please sign in to comment.
Something went wrong with that request. Please try again.