Service Workers

W3C First Public Working Draft 08 May 2014

This version
http://www.w3.org/TR/2014/WD-service-workers-20140508/
Latest version
http://www.w3.org/TR/service-workers/
Latest editor's draft
https://slightlyoff.github.io/ServiceWorker/spec/service_worker/
Revision history
https://github.com/slightlyoff/ServiceWorker/commits/master
Participate
Discuss on public-webapps@w3.org (Web Applications Working Group)
File bugs
Editors
Alex Russell, Google, <>
Jungkee Song, Samsung Electronics, <>

Abstract

This specification describes a method that enables applications to take advantage of persistent background processing, including hooks to enable bootstrapping of web applications while offline.

The core of this system is an event-driven Web Worker, which responds to events dispatched from documents and other sources. A system for managing installation, versions, and upgrades is provided.

The Service Worker is a generic entry point for event-driven background processing in the Web Platform that is extensible by other specifications.

Status of This Document

This section describes the status of this document at the time of its publication. Other documents may supersede this document. A list of current W3C publications and the latest revision of this technical report can be found in the W3C technical reports index at http://www.w3.org/TR/.

This document was published by the Web Applications Working Group as a First Public Working Draft. If you wish to make comments regarding this document, please send them to public-webapps@w3.org (subscribe, archives). All feedback is welcome.

Publication as a First Public Working Draft does not imply endorsement by the W3C Membership. This is a draft document and may be updated, replaced or obsoleted by other documents at any time. It is inappropriate to cite this document as other than work in progress.

This document was produced by a group operating under the 5 February 2004 W3C Patent Policy. W3C maintains a public list of any patent disclosures made in connection with the deliverables of the group; that page also includes instructions for disclosing a patent. An individual who has actual knowledge of a patent which the individual believes contains Essential Claim(s) must disclose the information in accordance with section 6 of the W3C Patent Policy.

1 Introduction

1.1 About this Document

All diagrams, examples, notes, are non-normative, as well as sections explicitly marked as non-normative. Everything else in this specification is normative.

The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in the normative parts of this document are to be interpreted as described in RFC2119. For readability, these words do not appear in all uppercase letters in this specification.

Any point, at which a conforming UA must make decisions about the state or reaction to the state of the conceptual model, is captured as algorithm. The algorithms are defined in terms of processing equivalence. The processing equivalence is a constraint imposed on the algorithm implementors, requiring the output of the both UA-implemented and the specified algorithm to be exactly the same for all inputs.

1.2 Dependencies

This document relies on the following specifications:

1.3 Motivations

Web Applications traditionally assume that the network is reachable. This assumption pervades the platform. HTML documents are loaded over HTTP and traditionally fetch all of their sub-resources via subsequent HTTP requests. This places web content at a disadvantage versus other technology stacks.

The Service Worker is designed first to redress this balance by providing a Web Worker context, which can be started by a runtime when navigations are about to occur. This event-driven worker is registered against an origin and a path (or pattern), meaning it can be consulted when navigations occur to that location. Events that correspond to network requests are dispatched to the worker and the responses generated by the worker may over-ride default network stack behavior. This puts the Service Worker, conceptually, between the network and a document renderer, allowing the Service Worker to provide content for documents, even while offline.

Web developers familiar with previous attempts to solve the offline problem have reported a deficit of flexibility in those solutions. As a result, the Service Worker is highly procedural, providing a maximum of flexibility at the price of additional complexity for developers. Part of this complexity arises from the need to keep Service Workers responsive in the face of a single-threaded execution model. As a result, APIs exposed by Service Workers are almost entirely asynchronous, a pattern familiar in other JavaScript contexts but accentuated here by the need to avoid blocking document and resource loading.

Developers using the HTML5 Application Cache have also reported that several attributes of the design contribute to unrecoverable errors. A key design principle of the Service Worker is that errors should always be recoverable. Many details of the update process of Service Workers are designed to avoid these hazards.

Service Workers are started and kept alive by their relationship to events, not documents. This design borrows heavily from developer and vendor experience with Shared Workers and Chrome Background Pages. A key lesson from these systems is the necessity to time-limit the execution of background processing contexts, both to conserve resources and to ensure that background context loss and restart is top-of-mind for developers. As a result, Service Workers bear more than a passing resemblance to Chrome Event Pages, the successor to Background Pages. Service Workers may be started by user agents without an attached document and may be killed by the user agent at nearly any time. Conceptually, Service Workers can be thought of as Shared Workers that can start, process events, and die without ever handling messages from documents. Developers are advised to keep in mind that Service Workers may be started and killed many times a second.

Service Workers are generic, event-driven, time-limited script contexts that run at an origin. These properties make them natural endpoints for a range of runtime services that may outlive the context of a particular document, e.g. handling push notifications, background data synchronization, responding to resource requests from other origins, or receiving centralized updates to expensive-to-calculate data (e.g., geolocation or gyroscope).

2 Concepts

A Service Worker is a type of Web Worker. Unlike other types of Web Worker, the lifetime of a Service Worker is tied to the execution lifetime of events, not references held by documents to the worker object. In practice, this means that Service Workers may begin, end, and restart execution many times over the life of documents which they logically control.

Service Workers are installed by user agents after being registered by authors from the context of a document. Service Workers execute in the registering document's origin.

Registration maps a Service Worker script to url scope, a tuple of origin and path expression. User agents may enable many registrations at a single origin so long as the path expression of the registration differs. Registration of an identical origin/path expression when one already exists in the user agent causes the existing registration to be replaced.

A path expression consists of a relative url which may, optionally, terminate with the charachter "*". Ending a path expression with "*" enables longest-prefix wildcard matching.

A document is "controlled" if an active Service Worker matches the document's URL upon navigation. Multiple documents may be concurrently controlled by a single Service Worker instance. That is, Service Workers have a one-to-many relationship with controlled documents.

The Lifecycle events of Service Workers are install and activate. Functional events are DOM Events that are dispatched in the Service Worker global context which are not lifecycle events of the Service Worker.

Registered Service Workers do not immediately begin to receive functional events for documents. Registration is the first step in installation, which proceeds through several phases:

  1. Fetch:
    The script URL provided by the author (via a call to navigator.serviceWorker.register([script URL], [registration option]) from a document) is fetched without heuristic caching. If the return status code of the fetch is not 2xx, installation aborts.
  2. Startup:
    If fetching the worker script is successful, it is executed in a ServiceWorkerGlobalScope. These scripts may call importScripts resulting in further fetches. Imported scripts are fetched, parsed and executed in turn, per the ECMA-262 and Web Worker specifications. All resources downloaded as part of the very first startup of a Service Worker are cached along with the worker script as described in "Worker Script Caching".
  3. oninstall:
    Once a Service Worker has been fetched and started, it is ready to process events. The first event sent to every Service Worker is install. Workers that handle this event are encouraged to use it as a way to prime the available storage mechanisms for supporting offline application use; perhaps by populating IndexedDB databases or Cache objects.

    Service Workers are not considered "installed" until the oninstall event handler completes. Given that many tasks, such as populating caches, may take a long time and are asynchronous, mechanisms are provided to let applications signal to the user agent when they consider themselves prepared to handle further events.

    If no oninstall event handler is registered, the Service Worker is considered to be successfully installed.

    If any oninstall handler throws an exception, or if any lifetime extension via event.waitUntil() fails (via Promise rejection), installation fails and activation is not carried out.

    Assuming a worker is successfully installed, it is now considered the worker in waiting. There may be only one active worker and one worker in waiting for a given url scope.
  4. onactivate:
    After successful installation and just prior to receiving functional events (e.g., fetch), the activate event is dispatched. The primary use of onactivate is for cleanup of resources used in previous versions of a Service Worker script.

    Like install event, this event may extend its lifetime using event.waitUntil(), however developers should note that activation is particularly performance sensitive. Performance sensitive events may be queued and will be delayed until successful completion of onactivate.

User Agents may request updated Service Worker scripts "in the background" while controlled documents for an existing Service Worker and url scope are active. Successful fetch, startup, and oninstall do not guarantee that the worker-in-waiting will begin to immediately handle functional events. An existing Service Worker script will continue to service documents it controls (and will continue to control new documents in the url scope) so long as any documents it controlled remain. API exists on the Service Worker to enable immediate activation but this is not the default behavior.

Once a service worker becomes active, the user agent may dispatch functional events. These events model various user-agent generated operations; for example the fetch event handling HTTP requests.

3 Document Context

Example: Bootstrapping with a ServiceWoker

// scope defaults to "/*"
navigator.serviceWorker.register("/assets/v1/serviceworker.js").then(
  function(serviceWorker) {
    console.log("success!");
    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);
  });

3.1 ServiceWorker

[Constructor()] // no-op constructor
interface ServiceWorker : Worker {
  readonly attribute DOMString scope;
  readonly attribute DOMString url;
  readonly attribute ServiceWorkerState state;

  // event
  attribute EventHandler onstatechange;
};

enum ServiceWorkerState {
  "installing",
  "installed",
  "activating",
  "activated",
  "redundant"
};

The ServiceWorker interface represents the document-side view of a Service Worker. This object provides a no-op constructor. Callers should note that only ServiceWorker objects created by the user agent (see navigator.serviceWorker.installing, navigator.serviceWorker.waiting and navigator.serviceWorker.current) will provide meaningful functionality.

3.1.1 scope

The scope attribute must return the serialization of the url representing the url scope of the ServiceWorker object.

The scope of a ServiceWorker reflects the scope of the ServiceWorker's registration. For example, consider a document created by a navigation to https://example.com/app.html which matches via the following registration call which has been previously executed:

// Script on the page https://example.com/app.html
navigator.serviceWorker.register("/service_worker.js", { scope: "/*" });

The value of navigator.serviceWorker.current.scope will be "https://example.com/*".

3.1.2 url

The url attribute must return the serialization of the url identifying the script resource that is running in the worker that the ServiceWorker object represents.

Similarly, in the above example, the value of navigator.serviceWorker.current.url will be "https://example.com/service_worker.js". The url property is always an absolute URL corresponding to the script file which the Service Worker evaluates.

3.1.3 onstatechange

onstatechange is the event handler that must be supported as attribute by the ServiceWorker object. A statechange event using the Event interface is dispatched on ServiceWorker object when the state attribute of the ServiceWorker object is changed.

4 Execution Context

Example: Serving Cached Resources

// caching.js
this.addEventListener("install", function(e) {
  // Create a cache of resources. Begins the process of fetching them.
  var shellResources = new Cache(
    "/app.html",
    "/assets/v1/base.css",
    "/assets/v1/app.js",
    "/assets/v1/logo.png",
    "/assets/v1/intro_video.webm",
  );

  // The coast is only clear when all the resources are ready.
  e.waitUntil(shellResources.ready());

  // Add Cache to the global so it can be used later during onfetch
  caches.set("shell-v1", shellResources);
});

this.addEventListener("fetch", function(e) {
  // No "onfetch" events are dispatched to the ServiceWorker until it
  // successfully installs.

  // All operations on caches are async, including matching URLs, so we use
  // Promises heavily. e.respondWith() even takes Promises to enable this:
  e.respondWith(
    caches.match(e.request).catch(function() {
      return e.default();
    }).catch(function() {
      return caches.match("/fallback.html");
    })
  );
});

4.1 ServiceWorkerGlobalScope

[Global]
interface ServiceWorkerGlobalScope : WorkerGlobalScope {
  readonly attribute CacheList caches;
  // A container for a list of window objects, identifiable by ID, that
  // correspond to windows (or workers) that are "controlled" by this SW
  readonly attribute ServiceWorkerClients clients;
  [Unforgeable] readonly attribute DOMString scope;

  ResponsePromise<any> fetch((Request or [EnsureUTF16] DOMString) request);

  void update();
  void unregister();

  attribute EventHandler oninstall;
  attribute EventHandler onactivate;
  attribute EventHandler onfetch;
  attribute EventHandler onbeforeevicted;
  attribute EventHandler onevicted;

  // The event.source of these MessageEvents are instances of Client
  attribute EventHandler onmessage;

  // close() method inherited from WorkerGlobalScope is not exposed.
};

The ServiceWorkerGlobalScope interface represents the global execution context of a Service Worker. ServiceWorkerGlobalScope object provides generic, event-driven, time-limited script execution contexts that run at an origin. Once successfully registered, a Service Worker is started, kept alive and killed by their relationship to events, not documents. Any type of synchronous requests MUST NOT be initiated inside of a Service Worker.

4.1.1 caches

self.caches must return the CacheList object that is the global asychronous map object for the ServiceWorkerGlobalScope execution context containing the cache objects keyed by the name of the caches. Caches are always enumerable via self.caches in insertion order (per ECMAScript 6 Map objects.)

4.1.2 clients

self.clients must return the ServiceWorkerClients object containing a list of client objects, identifiable by ID, that correspond to windows or workers that are controlled by this Service Worker.

4.1.3 scope

self.scope must return the pattern string for which this Service Worker instance was registered.

4.1.4 fetch(request)

self.fetch(request) method must run the fetch algorithm passing request as the argument.

4.1.5 update()

update() pings the server for an updated version of this script without consulting caches. self.update() method must run the _SoftUpdate algorithm passing its serviceWorkerRegistration object as the argument which is the result of running the _GetRegistration algorithm with self.scope as the argument. (This is conceptually the same operation that UA does maximum once per every 24 hours.)

4.1.6 unregister()

self.unregister() method must run the Unregistration algorithm implicitly passing self.scope as the argument.

4.1.7 onmessage

self.onmessage is the event handler that must be supported as attribute by the ServiceWorkerGlobalScope object. ServiceWorkerGlobalScope objects act as if they had an implicit MessagePort associated with them. This port is part of a channel that is set up when the worker is created, but it is not exposed. This object must never be garbage collected before the ServiceWorkerGlobalScope object.

All messages received by that port must immediately be retargeted at the ServiceWorkerGlobalScope object. That is, an event named message using the MessageEvent interface is dispatched on ServiceWorkerGlobalScope object. The event.source of these MessageEvents are instances of Client.

4.2 Client

[Constructor()] // no-op constructor
interface Client {
  readonly attribute unsigned long id;
  void postMessage(any message, DOMString targetOrigin,
                   optional sequence<Transferable> transfer);
};

The Client interface represents the window or the worker (defined as client) that is controlled by the Service Worker. This object provides a no-op constructor. Callers should note that only Client objects created by the user agent (see this.clients.getServiced()) will provide meaningful functionality.

The id of a Client identifies the specific client object from the list of client objects serviced by the Service Worker. The postMessage(message, targetOrigin, transfer) method of a Client, when called, causes a MessageEvent to be dispatched at the client object.

4.3 ServiceWorkerClients

interface ServiceWorkerClients {
  // A list of client objects, identifiable by ID, that correspond to windows
  // (or workers) that are "controlled" by this SW
  Promise<Client[]?> getServiced();
  Promise<any> reloadAll();
};

The ServiceWorkerClients interface represents a container for a list of Client objects.

4.3.1 getServiced()

The getServiced() method of a ServiceWorkerClients, when called, returns a Promise that will resolve with a list of Client objects that are controlled by this Service Worker.

4.3.2 reloadAll()

reloadAll() provides a mechanism for the worker to request synchronized re-fetch of all documents whose URLs match the registration's url scope. An event named reloadpage is dispatched on the navigator.serviceWorker object of each document. The in-document handlers may allow the event to continue, request an extension (via e.waitUntil()), or cancel the collective reload by calling e.preventDefault().

4.4 Request Objects


interface RequestPromise : Promise {
};

[Constructor(optional RequestInit init)]
interface Request {
  attribute unsigned long timeout;
  attribute DOMString url;
  attribute ByteString method;
  readonly attribute DOMString origin;
  readonly attribute Mode mode;
  attribute boolean synchronous;
  readonly attribute unsigned long redirectCount;
  attribute boolean forcePreflight;
  attribute boolean forceSameOrigin;
  attribute boolean omitCredentials;
  readonly attribute DOMString referrer;
  readonly attribute HeaderMap headers; // alternative: sequence<Header> headers;
  attribute any body;
};

dictionary RequestInit {
  unsigned long timeout = 0;
  DOMString url;
  boolean synchronous = false;
  boolean forcePreflight = false;
  boolean forceSameOrigin = false;
  boolean omitCredentials = false;
  ByteString method = "GET";
  HeaderMap headers;
  any body;
};

enum Mode {
  "same origin",
  "tainted x-origin",
  "CORS"
};

[MapClass(DOMString, DOMString)]
interface HeaderMap {
};

4.5 Response Objects

Resposne objects model HTTP responses.


interface ResponsePromise : Promise {
  Promise<Blob> toBlob();
};

[Constructor]
interface AbstractResponse {
};

interface OpaqueResponse : AbstractResponse {
  readonly attribute unsigned short status;
  readonly attribute ByteString statusText;
  readonly attribute ByteString method;
  // Returns a filtered list of headers. See prose for details.
  getter HeaderMap headers();
  // No setter for headers
  readonly attribute DOMString url;
};

interface CORSResponse : Response {
  getter HeaderMap headers();
};

[Constructor(optional ResponseInit responseInitDict)]
interface Response : AbstractResponse {
  attribute unsigned short status;
  attribute ByteString statusText;
  attribute ByteString method;
  getter HeaderMap headers();
  setter void headers(HeaderMap items);
  attribute DOMString url;
  Promise<Blob> toBlob();
};

dictionary ResponseInit {
  unsigned short status = 200;
  ByteString statusText = "OK";
  ByteString method;
  HeaderMap headers;
};

4.5.1 AbstractResponse

AbstractResponse is a superclass for all Resposne types. It should not be directly constructed (although, for compatibility with JavaScript, a constructor is provided).

4.5.2 Response

Response objects are mutable and constructable. They model HTTP responses. The fetch() API returns this type for same-origin responses.

It may be possible to set the Location header of a Response object to someplace not in the current origin but this is not a security issue. Cross-origin response bodies are opaque to script, and since only same-origin documents will encounter these responses, the only systems the Service Worker can "lie to" are same-origin (and therefore safe from the perspective of other origins).

4.5.4 OpaqueResponse

OpaqueResponse objects are immutable but constructable. The fetch() API returns this type for cross-origin responses.

Their role is to encapsulate the security properties of the web platform. As such, their body attribute will always be undefined and the list of readable headers is heavily filtered.

OpaqueResponse objects may be forwarded on to rendering documents in exactly the same way as mutable Response objects.

4.5.5 CORSResponse

4.6 Caches

To allow authors to fully manage their content caches for offline use, the ServiceWorkerGlobalScope execution context provides the caching methods largely conforming to ECMAScript 6 Map objects with additional convenience methods. A domain can have multiple, named Cache objects, whose contents are entirely under the control of scripts. Caches are not shared across domains, and they are completely isolated from the browser's HTTP cache.

4.6.1 Understanding Cache Lifetimes

The Cache instances are not part of the browser's HTTP cache. The Cache objects are exactly what authors have to manage themselves. The Cache objects do not get updated unless authors explicitly request them to be. The Cache objects do not expire unless authors delete the entries. The Cache objects do not disappear just because the Service Worker script is updated. That is, caches are not updated automatically. Updates must be manually managed. This implies that authors should version their caches by name and make sure to use the caches only from the version of the ServiceWorker that can safely operate on.

4.6.2 Cache


[Constructor((Request or Response or [EnsureUTF16] DOMString)... items)]
interface Cache {
  Promise<Response> match((Request or [EnsureUTF16] DOMString) request);
  Promise<Response[]> matchAll((Request or [EnsureUTF16] DOMString) request);
  Promise<Request[]> keys((Request or [EnsureUTF16] DOMString) filterRequest);
  Promise<Response[]> add((Request or Response or [EnsureUTF16] DOMString)... items);
  Promise<Response> set((Request or [EnsureUTF16] DOMString) request, Response response);
  Promise<any[]> delete((Request or [EnsureUTF16] DOMString) request);
  Promise<Response[]> ready();
};

4.6.3 CacheList


[MapClass(DOMString, Cache)]
interface CacheList {
  ResponsePromise<any> match([EnsureUTF16] DOMString url, optional DOMString cacheName);
  Promise<Cache> get(DOMString key);
  Promise<boolean> has(DOMString key);
  Promise<any> set(DOMString key, Cache val);
  Promise<any> clear();
  CacheList items();
  DOMString[] keys();
  Cache[] values();
  getter unsigned long size();
};

Issue: the following issues should be addressed in the WebIDL specification: the methods keys() and values() are not allowed to be declared as interface members of an interface declared with the [MapClass] extended attribute; the return type of get(key), has(key), set(key, val), clear(), delete(key) is meant to be declared as pre-defined IDL fragment. Namely, the interface declared with [MapClass] extended attribute cannot fully support creating asynchronous map objects.

4.7 Events

4.7.1 InstallPhaseEvent


interface InstallPhaseEvent : Event {
  Promise<any> waitUntil(Promise<any> f);
};

Service Workers have two Lifecycle events, install and activate. Service Workers use the InstallPhaseEvent interface for activate event and the InstallEvent interface, which inherits from the InstallPhaseEvent interface, for install event.

4.7.1.1 event.waitUntil(f)

event.waitUntil(f) method, when called in oninstall or onactivate, extends the lifetime of the event. When called in oninstall, it delays treating the installing worker until the passed Promise resolves successfully. This is primarily used to ensure that a ServiceWorker is not active until all of the core caches it depends on are populated. When called in onactivate, it delays treating the activating worker until the passed Promise resolves successfully. This is primarily used to ensure that any Functional events are not dispatched to the ServiceWorker until it upgrades database schemas and deletes the outdated cache entries.

4.7.2 install Event

An event named install using the InstallEvent interface is dispatched on ServiceWorkerGlobalScope object when the state of the associated ServiceWorker changes its value to installing. (See step 3 of _Installation algorithm)

4.7.2.1 InstallEvent

interface InstallEvent : InstallPhaseEvent {
  readonly attribute ServiceWorker activeWorker;
  void replace();
};

Service Workers use the InstallEvent interface for install event.

4.7.2.2 event.replace()

replace() interacts with waitUntil method in the following way:

  • Successful installation can be delayed by waitUntil, perhaps by subsequent event handlers.
  • Replacement only happens upon successful installation
  • Therefore, replacement of the active worker (if any) is not immediate, however it may occur as soon as the end of the current turn.

4.7.3 activate Event

An event named activate using the InstallPhaseEvent interface is dispatched on ServiceWorkerGlobalScope object when the state of the associated ServiceWorker changes its value to activating. (See step 7 of _Activation algorithm)

Service Workers use the InstallPhaseEvent interface for activate event.

4.7.4 fetch Event

4.7.4.1 FetchEvent

[Constructor]
interface FetchEvent : Event {
  readonly attribute Request request;
  readonly attribute Client client; // The window issuing the request.
  readonly attribute Purpose purpose;
  readonly attribute boolean isReload;

  void respondWith(Promise<AbstractResponse> r);
  Promise<any> forwardTo([EnsureUTF16] DOMString url);
  ResponsePromise<any> default();
};

enum Purpose {
  "connect",
  "font",
  "img",
  "object",
  "script",
  "style",
  "worker",
  "popup",
  "child",
  "navigate"
};

Service Workers use the FetchEvent interface for fetch event.

4.7.4.2 event.respondWith(r)

The r argument must resolve with a Response, else a NetworkError is thrown. If the request is a top-level navigation and the return value is a OpaqueResponse (an opaque response body), a NetworkError is thrown. The final URL of all successful (non network-error) responses is the requested URL. Renderer-side security checks about tainting for x-origin content are tied to the transparency (or opacity) of the Response body, not URLs.

4.7.4.3 event.isReload

Returns true if event was dispatched with the user's intention for the page reload, and false otherwise. Pressing the refresh button should be considered a reload while clicking a link and pressing the back button should not. The behavior of the Ctrl+l enter is left to the implementations of the user agents.

5 Security Considerations

Service Workers should be implemented to be HTTPS-only. The reasons for SSL-only support include:

5.1 Origin Relativity

One of the advanced concerns that major applications would encounter is whether they can be hosted from a CDN. By definition, these are servers in other places, often on other domains. Therefore, Service Workers cannot be hosted on CDNs. But they can include resources via importScripts(). The reason for this restriction is that Service Workers create the opportunity for a bad actor to turn a bad day into a bad eternity.

5.2 Cross-Origin Resources & CORS

Applications tend to cache items that come from a CDN or other domain. It is 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 is possible to XHR many sorts of off-domain resources when appropriate CORS headers are set.

ServiceWorkers enable this by allowing Caches to fetch and cache off-origin items. Some restrictions apply, however. First, unlike same-origin resources which are managed in the Cache as Promises for Response instances, the objects stored are Promises for OpaqueResponse instances. OpaqueResponse provides a much less expressive API than Response; 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 Responses, but cannot be meaningfully created programmatically. These limitations are necessary to preserve the security invariants of the platform. Allowing Caches to store them allows applications to avoid re-architecting in most cases.

6 Storage Considerations

Service Workers should take a dependency on Quota Management in preparation for an extension event that communicates storage pressure and pre-eviction information to the application.

7 Extensibility

The expectation is that service workers are meant to be extensible from other specifications. As of now, BackgroundSync event, TaskScheduler and Push API would be the prime candidates and more will come. That said, we want to make sure that the future works won't be adding synchronous cruft, etc. This section will provide template text for how to extend the service workers.

8 Appendix A: Algorithms

Underscored functions and attributes are UA-internal properties.

8.1 Registration

The Service Worker Registration Algorithm creates or updates a registration for some amount of url scope. If successful, a registration ties the provided scriptUrl to an origin + scope pair which is subsequently consulted by the user for navigation matching.

Input
scriptUrl, the URL of the script to register; a string
scope, a string representing a path expression of url scope
Output
promise, a promise whose resolution indicates the success or failure of the algorithm
  1. Let promise be a newly-created promise.
  2. Return promise.
  3. Run the following steps asynchronously:
    1. Let scope be scope resolved against the document url.
    2. Let scriptUrl be scriptUrl resolved against the document url.
    3. If the scheme of the document url is not https, then: // Browsers may ignore this rule for development purposes only.
      1. Reject promise with a new SecurityError.
      2. Abort these steps.
    4. If the origin of scriptUrl does not match the document's origin, then:
      1. Reject promise with a new SecurityError.
      2. Abort these steps.
    5. If the origin of scope does not match the document's origin, then:
      1. Reject promise with a new SecurityError.
      2. Abort these steps.
    6. Let serviceWorkerRegistration be the result of running the _GetRegistration algorithm passing scope as the argument.
    7. If serviceWorkerRegistration is not null, then:
      1. Set serviceWorkerRegistration.uninstalling to false.
      2. If scriptUrl is equal to serviceWorkerRegistration.scriptUrl, then:
        1. If serviceWorkerRegistration.updatePromise is not null, then:
          1. Resolve promise with serviceWorkerRegistration.updatePromise.
          2. Abort these steps.
        2. Let newestWorker be _GetNewestWorker(serviceWorkerRegistration).
        3. If newestWorker is not null, then:
          1. Resolve promise with newestWorker.
          2. Abort these steps.
    8. If serviceWorkerRegistration is null, then:
      1. Let serviceWorkerRegistration be a newly-created _ServiceWorkerRegistration object.
      2. Set serviceWorkerRegistration to the value of key scope in _ScopeToServiceWorkerRegistrationMap.
      3. Set serviceWorkerRegistration.scope to scope.
      4. Set serviceWorkerRegistration.uninstalling to false.
    9. Set serviceWorkerRegistration.scriptUrl to scriptUrl.
    10. Resolve promise with the result of running the _Update algorithm passing serviceWorkerRegistration as the argument.

8.2 _Update

The Service Worker _Update Algorithm, run by the user agent, upgrades an installed Service Worker to a new version for the same url scope. If successful, the newly installed Service Worker becomes the Service Worker in waiting which becomes the active Service Worker as soon as all the documents served by the previous Service Worker are closed. When scheduling a fetch of a new version, the user agent should honor the HTTP cache headers with the upper limit of 24 hours:

Input
serviceWorkerRegistration, a record of the internal map structure keyed by a relative URL pattern, scope, with the value of properties, scriptUrl, installingWorker, waitingWorker, currentWorker and updatePromise; a _ServiceWorkerRegistration object
Output
promise, a promise whose resolution indicates the success or failure of the algorithm
  1. If serviceWorkerRegistration.updatePromise is not null, then:
    1. Reject serviceWorkerRegistration.updatePromise with a new AbortError.
    2. The browser may abort in-flight requests, parsing or worker execution relating to serviceWorkerRegistration.updatePromise.
  2. If serviceWorkerRegistration.installingWorker is not null, then:
    1. Terminate serviceWorkerRegistration.installingWorker.
    2. Set serviceWorkerRegistration.installingWorker.state to redundant.
    3. Fire statechange event on serviceWorkerRegistration.installingWorker.
    4. Set serviceWorkerRegistration.installingWorker to null.
    5. The user agent may abort any in-flight requests triggered by serviceWorkerRegistration.installingWorker.
  3. Let promise be a newly-created promise.
  4. Set serviceWorkerRegistration.updatePromise to promise.
  5. Return promise.
  6. Run the following steps asynchronously:
    1. Perform a fetch of serviceWorkerRegistration.scriptUrl, forcing a network fetch if cached entry is greater than 1 day old.
    2. If promise has been rejected (eg, another registration has aborted it), then:
      1. Set serviceWorkerRegistration.updatePromise to null.
      2. Abort these steps.
    3. Else if fetching the script fails due to the server returning a 4xx response or a 5xx response, or there is a DNS error, or the connection times out, then:
      1. Reject promise with a new NetworkError.
      2. Set serviceWorkerRegistration.updatePromise to null.
      3. Abort these steps.
    4. Else if the server returned a redirect, then:
      1. Reject promise with a new SecurityError.
      2. Set serviceWorkerRegistration.updatePromise to null.
      3. Abort these steps.
    5. Let fetchedScript be the fetched script.
    6. Let newestWorker be _GetNewestWorker(serviceWorkerRegistration).
    7. If newestWorker is not null, and newestWorker.url is equal to serviceWorkerRegistration.scriptUrl and fetchedScript is a byte-for-byte match with the script of newestWorker, then:
      1. Resolve promise with newestWorker.
      2. Set serviceWorkerRegistration.updatePromise to null.
      3. Abort these steps.
    8. Else,
      1. Let serviceWorker be a newly-created ServiceWorker object, using fetchedScript.
      2. If promise has been rejected (e.g, another registration has aborted it), then:
        1. Set serviceWorkerRegistration.updatePromise to null.
        2. Abort these steps.
      3. If serviceWorker fails to start up, due to parse errors or uncaught errors, then:
        1. Reject promise with the error.
        2. Set serviceWorkerRegistration.updatePromise to null.
        3. Abort these steps.
      4. Resolve promise with serviceWorker.
      5. Set serviceWorkerRegistration.updatePromise to null.
      6. Queue a task to invoke _Installation algorithm with serviceWorkerRegistration and serviceWorker as its arguments.

8.3 _SoftUpdate

The browser may call this as often as it likes to check for updates.

Input
serviceWorkerRegistration, a record of the internal map structure keyed by a relative URL pattern, scope, with the value of properties, scriptUrl, installingWorker, waitingWorker, currentWorker and updatePromise; a _ServiceWorkerRegistration object
Output
None
  1. If serviceWorkerRegistration.uninstalling is true, then:
    1. Abort these steps.
  2. If serviceWorkerRegistration.updatePromise is not null, then:
    1. Abort these steps.
  3. If serviceWorkerRegistration.installingWorker is not null, then:
    1. Abort these steps.
  4. Queue a task to invoke _Update algorithm with serviceWorkerRegistration as its argument.

8.4 _Installation

Input
serviceWorkerRegistration, a record of the internal map structure keyed by a relative URL pattern, scope, with the value of properties, scriptUrl, installingWorker, waitingWorker, currentWorker and updatePromise; a _ServiceWorkerRegistration object
serviceWorker, the object representing the document-side view of a Service Worker; a ServiceWorker object
Output
None
  1. Set serviceWorkerRegistration.installingWorker to serviceWorker.
  2. Set serviceWorkerRegistration.installingWorker.state to installing.
  3. Fire install event on the associated ServiceWorkerGlobalScope object.
  4. Fire updatefound event on navigator.serviceWorker for all documents which match serviceWorkerRegistration.scope.
  5. If any handler called waitUntil(), then:
    1. Extend this process until the associated promises settle.
    2. If the resulting promise rejects, then:
      1. Set serviceWorkerRegistration.installingWorker.state to redundant.
      2. Fire statechange event on serviceWorkerRegistration.installingWorker.
      3. Set serviceWorkerRegistration.installingWorker to null.
      4. Abort these steps.
  6. If serviceWorkerRegistration.waitingWorker is not null, then:
    1. Set serviceWorkerRegistration.waitingWorker.state to redundant.
  7. Set serviceWorkerRegistration.waitingWorker to serviceWorkerRegistration.installingWorker
  8. Set serviceWorkerRegistration.installingWorker to null
  9. Set serviceWorkerRegistration.waitingWorker.state to installed.
  10. Fire statechange event on serviceWorkerRegistration.waitingWorker.
  11. If any handler called replace(), then:
    1. For each document matching serviceWorkerRegistration.scope:
      1. Set serviceWorkerRegistration as the document's service worker registration.
    2. Run _Activation algorithm passing serviceWorkerRegistration as the argument.
    3. Abort these steps.
  12. If no document is using serviceWorkerRegistration as their service worker registration, then:
    1. Queue a task to invoke _Activation algorithm with serviceWorkerRegistration as its argument.

8.5 _Activation

Input
serviceWorkerRegistration, a record of the internal map structure keyed by a relative URL pattern, scope, with the value of properties, scriptUrl, installingWorker, waitingWorker, currentWorker and updatePromise; a _ServiceWorkerRegistration object
Output
None
  1. Let activatingWorker be serviceWorkerRegistration.waitingWorker.
  2. Let exitingWorker be serviceWorkerRegistration.currentWorker.
  3. If exitingWorker is not null, then:
    1. Wait for exitingWorker to finish handling any in-progress requests.
    2. Terminate exitingWorker.
    3. Set exitingWorker.state to redundant.
    4. Fire statechange event on exitingWorker.
  4. Set serviceWorkerRegistration.currentWorker to activatingWorker.
  5. Set serviceWorkerRegistration.waitingWorker to null.
  6. Set serviceWorkerRegistration.currentWorker.state to activating.
  7. Fire statechange event on serviceWorkerRegistration.currentWorker.
  8. Fire currentchange event on navigator.serviceWorker for all documents that have selected serviceWorkerRegistration.
  9. Fire activate event on the associated ServiceWorkerGlobalScope object.
  10. If any handler calls waitUntil(), then:
    1. Extend this process until the associated promises settle.
  11. Set serviceWorkerRegistration.currentWorker.state to activated.
  12. Fire statechange event on serviceWorkerRegistration.currentWorker.

8.6 _OnNavigationRequest

Input
request, the object representing the request of fetching a resource; a Request object
Output
None
  1. If request is a force-refresh (shift+refresh), then:
    1. Fetch the resource normally and abort these steps.
  2. Let parsedUrl be the result of parsing request.url.
  3. Let serviceWorkerRegistration be the result of running _ScopeMatch algorithm passing parsedUrl as the argument.
  4. If serviceWorkerRegistration is null, then:
    1. Fetch the resource normally and abort these steps.
  5. Let matchedServiceWorker be serviceWorkerRegistration.currentWorker.
  6. If matchedServiceWorker is null, then:
    1. Fetch the resource normally and abort these steps.
  7. Document will now use serviceWorkerRegistration as its service worker registration.
  8. If matchedServiceWorker.state is activating, then:
    1. Wait for matchedServiceWorker.state to become activated.
  9. Fire fetch event on the associated ServiceWorkerGlobalScope object with a new FetchEvent object.
  10. If respondWith() was not called, then:
    1. Fetch the resource normally.
    2. Queue a task to invoke _SoftUpdate algorithm with serviceWorkerRegistrationas its argument.
    3. Abort these steps.
  11. Let responsePromise be value passed into respondWith() casted to a promise.
  12. Wait for responsePromise to settle.
  13. If responsePromise rejected, then:
    1. Fail the resource load as if there had been a generic network error and abort these steps.
  14. If responsePromise resolves to a OpaqueResponse, then:
    1. Fail the resource load as if there had been a generic network error and abort these steps.
  15. If responsePromise resolves to an AbstractResponse, then:
    1. Serve the response.
    2. Queue a task to invoke _SoftUpdate algorithm with serviceWorkerRegistration as the argument.
    3. Abort these steps.
  16. Fail the resource load as if there had been a generic network error and abort these steps.

8.7 _OnResourceRequest

Input
request, the object representing the request of fetching a resource; a Request object
Output
None
  1. Let serviceWorkerRegistration be the registration used by this document.
  2. If serviceWorkerRegistration is null, then:
    1. Fetch the resource normally and abort these steps.
  3. Let matchedServiceWorker be serviceWorkerRegistration.activeWorker.
  4. If matchedServiceWorker is null, then:
    1. Fetch the resource normally and abort these steps.
  5. If matchedServiceWorker.state is activating, then:
    1. Wait for matchedServiceWorker.state to become activated.
  6. Fire fetch event on the associated ServiceWorkerGlobalScope object with a new FetchEvent object.
  7. If respondWith() was not called, then:
    1. Fetch the resource normally and abort these steps.
  8. Let responsePromise be value passed into respondWith() casted to a promise.
  9. Wait for responsePromise to resolve.
  10. If responsePromise rejected, then:
    1. Fail the resource load as if there had been a generic network error and abort these steps.
  11. If responsePromise resolves to an AbstractResponse, then:
    1. Serve the response and abort these steps.
  12. Fail the resource load as if there had been a generic network error and abort these steps.

8.8 _OnDocumentUnload

Input
document, the document using the given _ServiceWorkerReistration object as its service worker registration; a Document object
Output
None
  1. Let serviceWorkerRegistration be the registration used by document.
  2. If serviceWorkerRegistration is null, then:
    1. Abort these steps.
  3. If any other document is using serviceWorkerRegistration as their service worker registration, then:
    1. Abort these steps.
  4. If serviceWorkerRegistration.uninstalling is true, then:
    1. Delete serviceWorkerRegistration.scope from _ScopeToServiceWorkerRegistrationMap.
    2. Abort these steps.
  5. If serviceWorkerRegistration.updatePromise is null and serviceWorkerRegistration.installingWorker is null and serviceWorkerRegistration.waitingWorker is null and serviceWorkerRegistration.currentWorker is null, then:
    1. Delete serviceWorkerRegistration.scope from _ScopeToServiceWorkerRegistrationMap.
    2. Abort these steps.
  6. If serviceWorkerRegistration.waitingWorker is not null:
    1. Run _Activatation algorithm with serviceWorkerRegistration at the argument.

8.9 Unregistration

Input
scope, a string representing a path expression of url scope
Output
promise, a promise whose resolution indicates the success or failure of the algorithm
  1. Let promise be a newly-created promise.
  2. Return promise.
  3. Run the following steps asynchronously:
    1. Let scope be scope resolved against the document url.
    2. If the origin of scope does not match the document's origin, then:
      1. Reject promise with a new SecurityError.
      2. Abort these steps.
    3. Let serviceWorkerRegistration be the result of running _GetRegistration algorithm passing scope as the argument.
    4. If serviceWorkerRegistration is null, then:
      1. Resolve promise.
      2. Abort these steps.
    5. Set serviceWorkerRegistration.uninstalling to true.
    6. If serviceWorkerRegistration.updatePromise is not null, then:
      1. Reject serviceWorkerRegistration.updatePromise with a new AbortError.
      2. The browser may abort in-flight requests, parsing or worker execution relating to serviceWorkerRegistration.updatePromise.
    7. If serviceWorkerRegistration.installingWorker is not null, then:
      1. Terminate serviceWorkerRegistration.installingWorker.
      2. Set serviceWorkerRegistration.installingWorker.state to redundant.
      3. Fire statechange event on serviceWorkerRegistration.installingWorker.
      4. Set serviceWorkerRegistration.installingWorker to null.
      5. The user agent may abort in-flight requests triggered by serviceWorkerRegistration.installingWorker.
    8. If serviceWorkerRegistration.waitingWorker is not null, then:
      1. Set serviceWorkerRegistration.waitingWorker.state to redundant.
      2. Fire statechange event on serviceWorkerRegistration.waitingWorker.
      3. Set serviceWorkerRegistration.waitingWorker to null.
    9. Resolve promise.
    10. If no document is using serviceWorkerRegistration as their service worker registration, then:
      1. Delete scope from _ScopeToServiceWorkerRegistrationMap

8.10 _ScopeMatch

Input
url, a string representing a relative URL pattern; a string
Output
serviceWorkerRegistration, a record of the internal map structure keyed by a relative URL pattern, scope, with the value of properties, scriptUrl, installingWorker, waitingWorker, currentWorker and updatePromise; a _ServiceWorkerRegistration object
  1. Let matchingScope be the longest key in _ScopeToServiceWorkerRegistrationMap that glob-matches url.
  2. Let serviceWorkerRegistration be the result of running _GetRegistration algorithm passing matchingScope as the argument.
  3. Return serviceWorkerRegistration.

8.11 _GetRegistration

Input
scope, a string representing a relative URL pattern; a string
Output
serviceWorkerRegistration, a record of the internal map structure keyed by a relative URL pattern, scope, with the value of properties, scriptUrl, installingWorker, waitingWorker, currentWorker and updatePromise; a _ServiceWorkerRegistration object
  1. If there is no record for scope in _ScopeToServiceWorkerRegistrationMap, then return null.
  2. Let serviceWorkerRegistration be the record for scope in _ScopeToServiceWorkerRegistrationMap.
  3. Return serviceWorkerRegistration.

8.12 _GetNewestWorker

Input
serviceWorkerRegistration, a record of the internal map structure keyed by a relative URL pattern, scope, with the value of properties, scriptUrl, installingWorker, waitingWorker, currentWorker and updatePromise; a _ServiceWorkerRegistration object
Output
serviceWorker, the object representing the document-side view of a Service Worker; a ServiceWorker object
  1. Let newestWorker be null.
  2. If serviceWorkerRegistration.installingWorker is not null, then:
    1. Set newestWorker to serviceWorkerRegistration.installingWorker.
  3. Else if serviceWorkerRegistration.waitingWorker is not null, then:
    1. Set newestWorker to serviceWorkerRegistration.waitingWorker.
  4. Else if serviceWorkerRegistration.currentWorker is not null, then:
    1. Set newestWorker to serviceWorkerRegistration.currentWorker.
  5. Return newestWorker.

9 Acknowledgements

Jake Archibald is a ghost-author of this document. The best instincts in the design are his. He similarly shaped many of the details through discussion and experimentation. The bits which are not his (but which are good) owe everything to his experience, persistence, and focus on enabling web developers. He embodies a hopeful example for developers in shaping browser efforts to more directly address real-world pain points. If Service Workers solve "offline for the web", the credit is due him.

Deep thanks go to Andrew Betts for organizing and hosting a small workshop of like-minded individuals including: Jake Archibald, Jackson Gabbard, Tobie Langel, Robin Berjon, Patrick Lauke, Christian Heilmann. From the clarity of the day's discussions and the use-cases outlined there, much has become possible. Further thanks to Andrew for raising consciousness about the offline problem. His organization of EdgeConf and inclusion of Offline as a persistent topic there has created many opportunities and connections that have enabled this work to progress.

Anne van Kesteren has generously lent his encyclopedic knowledge of Web Platform arcana and standards development experience throught the development of the Service Worker. This specification would be incomplete without his previous work in describing the real-world behavior of URLs, HTTP Fetch, Promises, and DOM. Similarly, this specification would not be possible without Ian Hickson's rigorous Web Worker spec. Much thanks to him.

In no particular order, deep gratitude for design guidance and discussion goes to: Jungkee Song, Alec Flett, David Barrett-Kahn, Aaron Boodman, Michael Nordman, Tom Ashworth, Kinuko Yasuda, Darin Fisher, Jonas Sicking, Jesús Leganés Combarro, Mark Christian, Dave Hermann, Yehuda Katz, François Remy, Ilya Grigorik, Will Chan, Domenic Denicola, Nikhil Marathe, Yves Lafon, Adam Barth, Greg Simon, and Devdatta Akhawe.

Jason Weber, Chris Wilson, Paul Kinlan, Ehsan Akhgari, and Daniel Austin have provided valuable, well-timed feedback on requirements and the standardization process.

The authors would also like to thank Dimitri Glazkov for his scripts and formatting tools which have been essential in the production of this specification. The authors are also grateful for his considerable guidance.

Thanks also to Vivian Cromwell, Greg Simon, and Alex Komoroske for their considerable professional support.