Service Workers

W3C Editor's Draft

This version
https://slightlyoff.github.io/ServiceWorker/spec/service_worker/
Latest editor's draft
https://slightlyoff.github.io/ServiceWorker/spec/service_worker/
Previous version
http://www.w3.org/TR/service-workers/
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 an Editor's 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 an Editor's 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.

Introduction

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.

Dependencies

This document relies on the following specifications:

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).

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 character "*". Ending a path expression with "*" enables longest-prefix wildcard matching.

A registration may have an associated installing worker when the registration has entered and is running the steps in the installation process. A registration may have an an associated active worker when the registration has entered (or may have completed) the activation process.

A document is controlled if an active 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. When a document is controlled by a Service Worker, it is considered the document is using the Service Worker's associated registration.

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(scriptURL, options) 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 Workers 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 an installing worker completes the steps in the installation process (i.e. successfully installed), it is now considered the worker in waiting. There may be only one active worker, one worker in waiting and one installing worker 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.

Document Context

Example: Bootstrapping with a ServiceWorker

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

ServiceWorker

[Constructor] // no-op constructor interface ServiceWorker : Worker { readonly attribute ScalarValueString scriptURL; 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 the attributes installing, waiting, active of ServiceWorkerRegistration interface and navigator.serviceWorker.controller) will provide meaningful functionality.

scriptURL

The scriptURL of a ServiceWorker object reflects the script's URL of the associated Service Worker.

The scriptURL attribute must return the result of running these steps or their equivalent:

  1. Let scriptURL be the URL of the script of the Service Worker identifed by its URL scope.
  2. Set scriptURL to the result of running the URL serializer with scriptURL.
  3. Return scriptURL.

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.controller.scriptURL will be "https://example.com/service_worker.js".

state

A ServiceWorker object can be in several states.

The state attribute must return the current state, which must be one of the following values defined in the ServiceWorkerState enumeration:

"installing"
The Service Worker represented by the ServiceWorker object has entered and is running the steps in the installation process. During this state, event.waitUntil(f) can be called inside the oninstall event handler of the associated ServiceWorkerGlobalScope object to extend the life of the installing worker until the passed promise resolves successfully. This is primarily used to ensure that the Service Worker is not active until all of the core caches are populated.
"installed"
The Service Worker represented by the ServiceWorker object has completed the steps in the installation process. The Service Worker in this state is considered the worker in waiting.
"activating"
The Service Worker represented by the ServiceWorker object has entered and is running the steps in the activation process. During this state, event.waitUntil(f) can be called inside the onactivate event handler of the associated ServiceWorkerGlobalScope object to extend the life of the activating active worker until the passed promise resolves successfully. Note that no functional events are dispatched until the state becomes "activated".
"activated"
The Service Worker represented by the ServiceWorker object has completed the steps in the activation process. The Service Worker in this state is considered the active worker ready to control the documents in matching scope upon subsequent navigation.
"redundant"
A newly created Service Worker registration is replacing the current registration of the Service Worker.

Event handler

The following is the event handler (and its corresponding event handler event type) that must be supported, as event handler IDL attributes, by all objects implementing ServiceWorker interface:

event handler event handler event type
onstatechange statechange

ServiceWorkerRegistration

[Constructor] // no-op constructor interface ServiceWorkerRegistration : EventTarget { [Unforgeable] readonly attribute ServiceWorker? installing; [Unforgeable] readonly attribute ServiceWorker? waiting; [Unforgeable] readonly attribute ServiceWorker? active; readonly attribute ScalarValueString scope; Promise<any> unregister(); // event attribute EventHandler onupdatefound; };

The ServiceWorkerRegistration interface represents a registration of a Service Worker. This object provides a no-op constructor. Callers should note that only ServiceWorkerRegistration objects created by the user agent will provide meaningful functionality. A ServiceWorkerRegistration has scope, a string that represents a URL scope, installing, a ServiceWorker object representing an installing worker, waiting, a ServiceWorker object representing a worker in wating and active, a ServiceWorker object representing an active worker.

A ServiceWorkerRegistration has internal properties [[ScriptURL]], a string that represents the URL of the script for which the registration process ([[Register]] algorithm) started, [[UpdatePromise]], a promise that resolves with a ServiceWorkerRegistration object, and [[Uninstalling]], a boolean value that indicates whether a registration is currently undergoing the unregistration process. ([[Unregister]] algorithm.)

installing

installing attribute must return a ServiceWorker object representing the installing worker that is currently undergoing the installation process (from step 3 to step 12 of the [[Install]] algorithm) for the given URL scope in which the document may be controlled when the Service Worker becomes the active worker. navigator.serviceWorker.installing returns null if no Service Worker registration is in the installation process.

waiting

waiting attribute must return a ServiceWorker object representing the waiting Service Worker that is considered the worker in waiting for the document. navigator.serviceWorker.waiting returns null if there is no worker in waiting for the document.

active

active attribute must return a ServiceWorker object representing the active worker that is currently undergoing or completed the activation process (from step 5 to step 12 of the [[Activate]] algorithm) for the given URL scope in which the document is controlled (or to be controlled). navigator.serviceWorker.active returns null if no Service Worker registration is in the activation process.

scope

The scope of a ServiceWorkerRegistration object reflects the URL scope of the associated Service Worker's registration.

The scope attribute must return the result of running these steps or their equivalent:

  1. Let scope be the URL representing the URL scope of the associated ServiceWorkerRegistration object.
  2. Set scope to the result of running the URL serializer with scope.
  3. Return scope.

In the example in section 3.1.1, the value of registration.scope, obtained from navigator.serviceWorker.ready.then(function(registration) { console.log(registration.scope); }) for example, will be "https://example.com/*".

unregister()

The unregister() method unregisters the registration for the given URL scope. It is important to note that the currently controlled document's registration is effective until all the documents (including itself) using this registration unload. That is, unregister() method only affects subsequent navigations.

unregister() method must return the result of running the [[Unregister]] algorithm, or their equivalent, passing scope, which represents its URL scope, as the argument.

Event handler

The following is the event handler (and its corresponding event handler event type) that must be supported, as event handler IDL attributes, by all objects implementing ServiceWorker interface:

event handler event handler event type
onupdatefound updatefound

navigator.serviceWorker

partial interface Navigator { readonly attribute ServiceWorkerContainer serviceWorker; };

The serviceWorker attribute of the Navigator interface must return an instance of the ServiceWorkerContainer interface, which provides access to registration, removal, upgrade, and communication with Service Workers that are (or will become) active for the current document. Communication with these workers is provided via standard HTML5 messaging APIs, and messaging occurs as per usual with Web Workers.

ServiceWorkerContainer

interface ServiceWorkerContainer : EventTarget { [Unforgeable] readonly attribute ServiceWorker? controller; readonly attribute Promise<ServiceWorkerRegistration> ready; Promise<ServiceWorkerRegistration> register(ScalarValueString scriptURL, optional RegistrationOptionList options); Promise<ServiceWorkerRegistration> getRegistration(optional ScalarValueString documentURL = ""); Promise<sequence<ServiceWorkerRegistration>?> getRegistrations(); // events attribute EventHandler oncontrollerchange; attribute EventHandler onreloadpage; attribute EventHandler onerror; }; dictionary RegistrationOptionList { ScalarValueString scope = "/*"; }; [Constructor(DOMString type, optional EventInit eventInitDict)] interface ReloadPageEvent : Event { void waitUntil(Promise<any> f); };

controller

controller attribute must return a ServiceWorker object representing the active worker that currently handles resource requests for the document. navigator.serviceWorker.controller returns null if the current document was not created under a Service Worker (See step 13-1 of [[HandleFetch]] algorithm) or the request is a force refresh (shift+refresh).

ready

ready attribute must return the result of running these steps or their equivalent:

  1. Let promise be a new promise.
  2. Run the following steps asynchronously:
    1. Let registration be null.
    2. Let documentURL be the result of running the URL serializer on the document's URL.
    3. Set registration to the result of running [[MatchScope]] algorithm, or its equivalent, with documentURL as its argument.
    4. If registration is null, then:
      1. Wait for the document to have a matching registration.
      2. Set registration to the corresponding ServiceWorkerRegistration object.
    5. If registration.active is null, then:
      1. Wait until the registration, represented by registration, acquires an active worker through a new activation process.
      2. Resolve promise with registration.
  3. Return promise.

The ready attribute is designed in a way that the returned promise will never reject. Instead, it waits until the promise resolves with an active worker. Hence, the state of the acquired ServiceWorker object is either "activating" or "activated".

The algorithm for ready attribute will be refined.

register(scriptURL, options)

The register(scriptURL, options) method creates or updates a registration for some amount of URL scope. If successful, a registration ties the provided scriptURL to a URL scope, which is subsequently used for navigation matching.

register(scriptURL, options) method must return the result of running the [[Register]] algorithm, or their equivalent, passing scriptURL and options.scope as the argument.

getRegistration(documentURL)

getRegistration(documentURL) method must run these steps or their equivalent:

  1. Let promise be a new promise.
  2. Run the following steps asynchronously:
    1. Let documentURL be the result of running the URL parser on documentURL with entry settings object's API base URL.
    2. Set documentURL to the result of running the URL serializer on documentURL.
    3. If the origin of documentURL does not match the document's origin, then:
      1. Reject promise with a "SecurityError" exception.
      2. Abort these steps.
    4. Let registration be the result of running [[MatchScope]] algorithm, or its equivalent, with documentURL as its argument.
    5. If registration is not null, then:
      1. Resolve promise with registration.
    6. Else:
      1. Reject promise with a "NotFoundError" exception.
  3. Return promise.

getRegistrations()

getRegistrations() method must run these steps or their equivalent:

  1. Let promise be a new promise.
  2. Run the following steps asynchronously:
    1. Let array be an empty array.
    2. Let origin be the unicode serialization of the document's origin.
    3. For each Record {[[key]], [[value]]} entry of its [[ScopeToRegistrationMap]]:
      1. If the origin of entry.[[key]] matches origin, then:
        1. Add entry.[[value]] to the array.
    4. Resolve promise with array.
  3. Return promise.

Event handlers

The following are the event handlers (and its corresponding event handler event types) that must be supported, as event handler IDL attributes, by all objects implementing the ServiceWorkerContainer interface:

event handler event handler event type
oncontrollerchange controllerchange
onreloadpage reloadpage
onerror error

Events

The following events are dispatched on ServiceWorker object:

Event name Interface Dispatched when…
statechange Event The state attribute of the ServiceWorker object is changed.

The following events are dispatched on ServiceWorkerContainer object:

Event name Interface Dispatched when…
updatefound Event The document's associated registration enters the installation process such that navigator.serviceWorker.installing becomes the new installing worker (See step 6 of the [[Install]] algorithm).
controllerchange Event The document's associated registration enters the activation process. (See step 8 of the [[Activate]] algorithm. When the activation process is triggered by replace() method call within the event handler of the install event, navigator.serviceWorker.controller immediately reflects the active worker as the Service Worker that controls the document.)
reloadpage ReloadPageEvent The page reload is triggered by the self.clients.reloadAll() method call from the active worker, represented by its associated ServiceWorkerGlobalScope object, for the document.
error ErrorEvent Any error occurred from the associated Service Workers.

Execution Context

Example: Serving Cached Resources

// caching.js this.addEventListener("install", function(e) { // Create a cache of resources. var shellResources = new Cache(); // Begins the process of fetching them. // The coast is only clear when all the resources are ready. e.waitUntil(shellResources.add( "/app.html", "/assets/v1/base.css", "/assets/v1/app.js", "/assets/v1/logo.png", "/assets/v1/intro_video.webm", )); // 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"); }) ); });

ServiceWorkerGlobalScope

[Global] interface ServiceWorkerGlobalScope : WorkerGlobalScope { readonly attribute CacheStorage 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 ScalarValueString scope; // fetch(input, init) method is defined in Fetch // Promise<Response> fetch(RequestInfo input, optional RequestInit init); 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.

caches

caches attribute must return the CacheStorage object that is the global asynchronous 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.)

clients

clients attribute 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.

scope

The scope attribute of a ServiceWorkerGlobalScope object reflects the URL scope of the associated Service Worker registration. The scope attribute must return the serialization of the URL representing the URL scope of the associated Service Worker registration.

fetch(input, init)

fetch(input, init) method is defined on the GlobalFetch interface implemented by the WorkerGlobalScope interface that the ServiceWorkerGlobalScope inherits from. When a script invokes the fetch(input, init) method on a ServiceWorkerGlobalScope object, the UA must run the steps defined in the fetch(input, init) method.

update()

update() pings the server for an updated version of this script without consulting caches.

update() method must run these steps or their equivalent:

  1. Let workerGlobalScope be the script settings object's global object.
  2. Let scope be an absolute URL, serialized, representing the URL scope of the Service Worker associated with workerGlobalScope.
  3. Let registration be the result of running [[GetRegistration]] algorithm passing scope as the argument.
  4. Invoke [[SoftUpdate]] algorithm, or its equivalent, passing registration as the argument.

This is conceptually the same operation that UA does maximum once per every 24 hours.

unregister()

The unregister() method unregisters the registration for its URL scope. It is important to note that this registration is effective until all the documents (including itself) using this registration unload. That is, unregister() method only affects subsequent navigations.

unregister() method must run these steps or their equivalent:

  1. Let workerGlobalScope be the script settings object's global object.
  2. Let scope be a string representing the path expression of URL scope of the Service Worker associated with workerGlobalScope.
  3. Invoke [[Unregister]] algorithm, or its equivalent, passing scope as the argument.

Event handlers

The following are the event handlers (and its corresponding event handler event types) that must be supported, as event handler IDL attributes, by all objects implementing the ServiceWorkerGlobalScope interface:

event handler event handler event type
oninstall install
onactivate activate
onfetch fetch
onbeforeevicted beforeevicted
onevicted evicted
onmessage message

For onmessage event handler,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.

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 attribute 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.

ServiceWorkerClients

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

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

getServiced()

The getServiced() method must return a promise that will resolve with a list of Client objects that are controlled by this Service Worker.

reloadAll()

reloadAll() method 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 event.waitUntil(f)), or cancel the collective reload by calling event.preventDefault().

The algorithm will be updated in a more formal language.

Request Objects

Request objects model HTTP requests. The Request interface is defined in Fetch. A request is an input to fetch, and accordingly a Request object is used as the first argument for the fetch(input, init) method. A Request object is a key for the Cache object.

The section will be updated.

Response Objects

Response objects model HTTP responses. The Response interface is defined in Fetch. A response is an output as a result of performing a fetch, and accordingly a Response object is the value the returned promise from fetch(input, init) method resolves with. A Response object is a value for the Cache object.

The section will be updated.

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.

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.

Cache

[Constructor] interface Cache { Promise<Response> match((Request or ScalarValueString) request, optional QueryParams params); Promise<sequence<Response>> matchAll(optional (Request or ScalarValueString) request, optional QueryParams params); Promise<any> add((Request or ScalarValueString)... requests); Promise<any> put((Request or ScalarValueString) request, Response response); Promise<any> delete((Request or ScalarValueString) request, optional QueryParams params); Promise<any> keys(optional (Request or ScalarValueString) request, optional QueryParams params); Promise<any> batch(sequence<CacheBatchOperation> operations); }; dictionary QueryParams { boolean ignoreSearch; boolean ignoreMethod; boolean ignoreVary; boolean prefixMatch; }; dictionary CacheBatchOperation { DOMString type; Request request; Response response; QueryParams matchParams; };

The Cache() constructor, when invoked, must return a newly created Cache object. A Cache object has a [[RequestToResponseMap]] object.

match(request, params)

match(request, params) method must run these steps or their equivalent:

  1. Let promise be a new promise.
  2. Run these steps asynchronously:
    1. Let p be the result of running the algorithm specified in matchAll(request, params) method with request and params as the arguments.
    2. Wait until p settles.
    3. If p rejects with an exception, then:
      1. Reject promise with that exception.
    4. Else if p resolves with an array, responseArray, then:
      1. If responseArray is an empty array, then:
        1. Reject promise with an "NotFoundError" exception.
      2. Else:
        1. Resolve promise with the first element of responseArray.
  3. Return promise.

matchAll(request, params)

matchAll(request, params) method must run these steps or their equivalent:

  1. Let promise be a new promise.
  2. Run these steps asynchronously:
    1. Let responseArray be an empty array.
    2. If the optional argument request is omitted, then:
      1. For each Record {[[key]], [[value]]} entry of its [[RequestToResponseMap]]:
        1. Add entry.[[value]] to responseArray.
      2. Resolve promise with responseArray.
      3. Abort these steps.
    3. Else:
      1. Let entries be the result of running [[QueryCache]] algorithm passing request and params as the arguments.
      2. For each entry of entries:
        1. Add entry[1] to responseArray.
      3. Resolve promise with responseArray.
  3. Return promise.

add(...requests)

add(...requests) method must run these steps or their equivalent:

  1. If there are no arguments, then:
    1. Return a promise rejected with a TypeError.
  2. Let requestArray be an array containing the elements corresponding to the variadic argument requests.
  3. Let responsePromiseArray be an empty array.
  4. For each request in requestArray:
    1. Run the algorithm specified in fetch(input, init) with request and add the returned value to responsePromiseArray.
  5. Let p be waiting for all of responsePromiseArray.
  6. Let q be transforming p with onFulfilled and onRejected.
  7. Upon fulfillment of p with value responseArray, perform the following substeps, onFulfilled, asynchronously:
    1. For each response in responseArray:
      1. If the status attribute of response is not 200, then:
        1. Reject q with a "NetworkError" exception.
        2. Abort these steps.
    2. Let operations be an empty array.
    3. For each response in responseArray with the index index:
      1. Let o be an empty object representing a CacheBatchOperation dictionary.
      2. Set the type dictionary member of o to "put".
      3. Set the request dictionary member of o to requestArray[index].
      4. Set the response dictionary member of o to response.
      5. Add o to operations.
    4. Set resultPromiseArray to the result of running the algorithm specified in batch(operations) passing operations as the argument.
    5. Resolve q with resultPromiseArray.
  8. Upon rejection of p with reason r, perform the following substeps, onRejected, asynchronously:
    1. Reject q with r.
  9. Return q.

put(request, response)

put(request, response) method must run these steps or their equivalent:

  1. Let operations be an empty array.
  2. Let o be an empty object representing a CacheBatchOperation dictionary.
  3. Set the type dictionary member of o to "put".
  4. Set the request dictionary member of o to request.
  5. Set the response dictionary member of o to response.
  6. Add o to operations.
  7. Set resultPromiseArray to the result of running the algorithm specified in batch(operations) passing operations as the argument.
  8. Let p be transforming resultPromiseArray with onFulfilled.
  9. Upon fulfillment of p with responseArray, perform the following substeps, onFulfilled, asynchronously:
    1. Resolve p with responseArray[0].
  10. Return p.

delete(request, params)

delete(request, params) method must run these steps or their equivalent:

  1. Let operations be an empty array.
  2. Let o be an empty object representing a CacheBatchOperation dictionary.
  3. Set the type dictionary member of o to "delete".
  4. Set the request dictionary member of o to request.
  5. Set the matchParams dictionary member of o to params.
  6. Add o to operations.
  7. Set resultPromiseArray to the result of running the algorithm specified in batch(operations) passing operations as the argument.
  8. Let p be transforming resultPromiseArray with onFulfilled.
  9. Upon fulfillment of p with responseArray, perform the following substeps, onFulfilled, asynchronously:
    1. Resolve p with responseArray[0].
  10. Return p.

keys(request, params)

keys(request, params) method must run these steps or their equivalent:

  1. Let promise be a new promise.
  2. Run these steps asynchronously:
    1. Let resultArray be an empty array.
    2. If the optional argument request is omitted, then:
      1. For each Record {[[key]], [[value]]} entry of its [[RequestToResponseMap]]:
        1. Add entry.[[key]] to resultArray.
    3. Else:
      1. Let requestResponseArray be the result of running [[QueryCache]] algorithm passing request and params as the arguments.
      2. For each requestResponse in requestResponseArray:
        1. Add requestResponse[0] to resultArray.
    4. Resolve promise with resultArray.
  3. Return promise.

batch(operations)

batch(operations) method must run these steps or their equivalent:

  1. Let p be a promise resolved with no value.
  2. Let q be transforming p with onFulfilled.
  3. Upon fulfillment of p with value v, perform the following steps, onFulfilled, asynchronously:
    1. Let itemsCopy be a new [[RequestToResponseMap]] object that is a copy of its context object's [[RequestToResponseMap]] object.
    2. Let addedRequests be an empty array.
    3. Try running the following substeps atomically:
      1. Let resultArray be an empty array.
      2. For each operation in operations with the index index:
        1. If opeartion.type matches neither "delete" nor "put", then:
          1. Throw a TypeError.
        2. If opeartion.type matches "delete" and opeartion.response is not null, then:
          1. Throw a TypeError.
        3. Let request be the result of invoking the initial value of Request as constructor with operation.request as the argument.
        4. Let requestResponseArray be the result of running [[QueryCache]] algorithm passing request and opeation.matchParams as the arguments.
        5. For each requestResponse in requestResponseArray:
          1. If any of the values in addedRequests matches requestResponse[0], then:
            1. Throw a "InvalidStateError" exception.
          2. For each Record {[[key]], [[value]]} entry of its [[RequestToResponseMap]]:
            1. If requestResponse[0] matches entry.[[key]], then:
              1. Delete entry.
        6. If opeartion.type matches "put", then:
          1. If opeartion.response is null, then:
            1. Throw a TypeError.
          2. If request.method does not match "GET", then:
            1. Throw a TypeError.
          3. If operation.matchParams is not null, then:
            1. Throw a TypeError.
          4. Add request to addedRequests.
          5. Set a newly-created Record {[[key]]: request, [[value]]: operation.response} to [[RequestToResponseMap]].
        7. Add operation.response to resultArray.
      3. Resolve q with resultArray.
    4. And then, if an exception was thrown, then:
      1. Set the context object's [[RequestToResponseMap]] to itemsCopy.
      2. Rethrow the exception
  4. Return q.

CacheStorage

[Constructor(sequence<any> iterable)] interface CacheStorage { Promise<any> match((Request or ScalarValueString) request, optional DOMString cacheName); Promise<Cache> get(DOMString cacheName); Promise<boolean> has(DOMString cacheName); Promise<any> create(DOMString cacheName); Promise<any> delete(DOMString cacheName); Promise<sequence<DOMString>> keys(); };

CacheStorage interface is designed to largely conform to ECMAScript 6 Map objects but entirely async, and with additional convenience methods. The methods, clear, forEach, entries and values, are intentionally excluded from the scope of the first version resorting to the ongoing discussion about the async iteration by TC39.

InstallPhaseEvent

[Constructor(DOMString type, optional EventInit eventInitDict)] 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.

Each event using InstallPhaseEvent interface or a derived interface has the following associated flags that are all initially unset:

event.waitUntil(f)

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.

waitUntil(f) method must run these steps or their equivalent:

  1. Let promise be a new promise.
  2. Run the following steps asynchronously:
    1. Set the extend lifetime flag.
    2. Wait until f settles.
    3. If f rejects with an exception, then:
      1. Reject promise with that exception.
      2. Set the wait-until reject flag.
    4. Else if f resolves with a value, then:
      1. Resolve promise with that value.
    5. Unset the extend lifetime flag.
  3. Return promise.

InstallEvent

[Constructor(DOMString type, optional InstallEventInit eventInitDict)] interface InstallEvent : InstallPhaseEvent { readonly attribute ServiceWorker? activeWorker; void replace(); }; dictionary InstallEventInit : EventInit { ServiceWorker activeWorker; };

Each event using InstallEvent interface has the following associated flag that is initially unset:

event.replace()

event.replace() method interacts with event.waitUntil(f) method. Successful installation can be delayed by event.waitUntil(f). Replacement only happens upon successful installation. That is, replacement of the active worker (if any) by invoking event.replace() method is not immediate, but it may occur as soon as the event's event handler completes its execution which is extended by event.waitUntil(f).

replace() method must set the activate immediate flag.

FetchEvent

[Constructor(DOMString type, optional FetchEventInit eventInitDict)] interface FetchEvent : Event { readonly attribute Request request; readonly attribute Client client; // The window issuing the request. readonly attribute Context context; readonly attribute boolean isReload; void respondWith(Promise<Response> r); Promise<any> forwardTo(ScalarValueString url); Promise<any> default(); }; dictionary FetchEventInit : EventInit { Request request; Client client; Context context; boolean isReload; }; enum Context { "connect", "font", "img", "object", "script", "style", "worker", "popup", "child", "navigate" };

Each event using FetchEvent interface has the following associated flag that is initially unset:

event.respondWith(r)

When event.respondWith(r) method is invoked, the argument, r, must resolve with a Response, else a NetworkError is thrown. If the request is a top-level navigation and the return value is a Response whose type attribute is "opaque" (i.e., 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 cross-origin content are tied to the transparency (or opacity) of the Response body, not URLs.

respondWith(r) method must run these steps or their equivalent:

  1. If the wait to respond flag is set, then:
    1. Throw an "InvalidStateError" exception.
    2. Abort these steps.
  2. Set the wait to respond flag.
  3. Wait until r settles.
  4. If r rejected, then:
    1. Set the respond-with error flag.
  5. If r resolves to response, an instance of Response interface whose type attribute is "opaque", and the context attribute is "navigate", then:
    1. Set the respond-with error flag
  6. If r resolves to response, an instance of Response interface, then:
    1. Do nothing.
  7. Unset the wait to respond flag.

event.default()

The invocation of event.default() method performs a fetch using event.request. event.request represents the original request from the controlled document. During the execution, the original request is not altered (except the skip service worker flag), and thus event.default() fetches the original request through the UA's HTTP stack. event.default() returns a promise, which resolves with a Response object, that can be an argument to the event.respondWith(r) method.

default() method must run these steps or their equivalent:

  1. Let promise be a new promise.
  2. Run the following steps asynchronously:
    1. Let request be event's request.
    2. Set request's skip service worker flag.
    3. Let response be the result of running fetch algorithm with request as its argument.
    4. If response is a network error, then:
      1. Reject promise with a TypeError.
    5. Else,
      1. Resolve promise with a new Response object associated with response.
  3. Return promise.

event.isReload

isReload attribute must return 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.

Events

The following events are dispatched on ServiceWorkerGlobalScope object:

Event name Interface Dispatched when…
install InstallEvent [Lifecycle event] A registration's installing Service Worker enters the installation process. (See step 7-1 of the [[Install]] algorithm.)
activate InstallPhaseEvent [Lifecycle event] A registration's activating Service Worker enters the activation process. (See step 9-1 of the [[Activate]] algorithm.)
fetch FetchEvent [Functional event] Fetch invokes handle a fetch with request. As a result of performing handle a fetch, the Service Woker returns a response to Fetch. The response, represented by a Response object, can be retrieved from a Cache object or directly from network using self.fetch(input, init) method or event.default() method. (A custom Response object can be another option.)

Custom event types for beforeevicted and evicted should be added.

Security Considerations

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

The section will be updated.

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.

The section will be updated.

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 Response objects with the type attribute set to "basic", the objects stored are Response objects with the type attribute set to "opaque". Responses typed "opaque" provide a much less expressive API than Responses typed "basic"; the bodies and headers cannot be read or set, nor many of the other aspects of their content inspected. They can be passed to event.respondWith(r) method and event.forwardTo(url) method in the same manner as the Responses typed "basic", 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.

The section will be updated.

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.

The section will be updated.

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.

The section will be updated.

Appendix A: Algorithms

The double square bracket notation ([[name of method or name of property]]) is used throughout the specification to indicate user agent's internal methods and internal properties.

[[ScopeToRegistrationMap]] represents an internal map structure that stores the entries of the Record {[[key]], [[value]]} where [[key]] is a string that represents a URL scope and [[value]] is a ServiceWorkerRegistration object.

[[RequestToResponseMap]] represents an internal map structure that stores the entries of the Record {[[key]], [[value]]} where [[key]] is a Request and [[value]] is a Response.

[[Register]]

Input
scriptURL, a string that represents the URL of the script
scope, a string that represents a path expression or a URL scope
Output
promise, a promise
  1. Let promise be a new promise.
  2. Run the following steps asynchronously:
    1. Let scope be the result of running the URL parser on scope with entry settings object's API base URL.
    2. Set scope to the result of running the URL serializer on scope with the exclude fragment flag set.
    3. Let scriptURL be the result of running the URL parser on scriptURL with entry settings object's API base URL.
    4. If the scheme of scriptURL is not "https", then: // Browsers may ignore this rule for development purposes only.
      1. Reject promise with a "SecurityError" exception.
      2. Abort these steps.
    5. If the origin of scriptURL does not match the document's origin, then:
      1. Reject promise with a "SecurityError" exception.
      2. Abort these steps.
    6. If the origin of scope does not match the document's origin, then:
      1. Reject promise with a "SecurityError" exception.
      2. Abort these steps.
    7. Let registration be the result of running the [[GetRegistration]] algorithm passing scope as the argument.
    8. If registration is not null, then:
      1. Set registration.[[Uninstalling]] to false.
      2. If scriptURL is equal to registration.[[ScriptURL]], then:
        1. If registration.[[UpdatePromise]] is not null, then:
          1. Resolve promise with registration.[[UpdatePromise]].
          2. Abort these steps.
      3. Let newestWorker be the result of running the [[GetNewestWorker]] algorithm passing registration as the argument.
      4. If newestWorker is not null, and scriptURL is equal to newestWorker.scriptURL, then:
        1. Resolve promise with registration.
        2. Abort these steps.
    9. Else:
      1. Let registration be a new ServiceWorkerRegistration object.
      2. Set a newly-created Record {[[key]]: scope, [[value]]: registration} to [[ScopeToRegistrationMap]].
      3. Set registration.scope to scope.
      4. Set registration.[[Uninstalling]] to false.
    10. Set registration.[[ScriptURL]] to scriptURL.
    11. Let p be the result of running the [[Update]] algorithm, or its equivalent, passing registration as the argument.
    12. Resolve promise with p.
  3. Return promise.

[[Update]]

The [[Update]] algorithm, run by the user agent, upgrades an active worker to a new version for the same URL scope. If successful, the newly installed Service Worker becomes the worker in waiting which later becomes the active 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
registration, a ServiceWorkerRegistration object
Output
promise, a promise
  1. If registration.[[UpdatePromise]] is not null, then:
    1. Reject registration.[[UpdatePromise]] with an "AbortError" exception.
    2. The user agent may abort in-flight requests, parsing or worker execution relating to registration.[[UpdatePromise]].
  2. If registration.installing is not null, then:
    1. Terminate registration.installing.
    2. Run the [[UpdateState]] algorithm passing registration.installing and "redundant" as the arguments.
    3. Set registration.installing to null.
    4. The user agent may abort any in-flight requests triggered by registration.installing.
  3. Let promise be a new promise.
  4. Set registration.[[UpdatePromise]] to promise.
  5. Run the following steps asynchronously:
    1. Perform a fetch of registration.[[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 registration.[[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 "NetworkError" exception.
      2. Set registration.[[UpdatePromise]] to null.
      3. Abort these steps.
    4. Else if the server returned a redirect, then:
      1. Reject promise with a "SecurityError" exception.
      2. Set registration.[[UpdatePromise]] to null.
      3. Abort these steps.
    5. Let fetchedScript be the fetched script.
    6. Let newestWorker be the result of running the [[GetNewestWorker]] algorithm passing registration as the argument.
    7. If newestWorker is not null, and newestWorker.scriptURL is equal to registration.[[ScriptURL]] and fetchedScript is a byte-for-byte match with the script of newestWorker, then:
      1. Resolve promise with registration.
      2. Set registration.[[UpdatePromise]] to null.
      3. Abort these steps.
    8. Else,
      1. Let worker be a new ServiceWorker object, using fetchedScript.
      2. If promise has been rejected (e.g, another registration has aborted it), then:
        1. Set registration.[[UpdatePromise]] to null.
        2. Abort these steps.
      3. If worker fails to start up, due to parse errors or uncaught errors, then:
        1. Reject promise with the error.
        2. Set registration.[[UpdatePromise]] to null.
        3. Abort these steps.
      4. Invoke [[Install]] algorithm, or its equivalent, with registration and worker as its arguments.
  6. Return promise.

[[SoftUpdate]]

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

Input
registration, a ServiceWorkerRegistration object
Output
None
  1. If registration.[[Uninstalling]] is true, then:
    1. Abort these steps.
  2. If registration.[[UpdatePromise]] is not null, then:
    1. Abort these steps.
  3. If registration.installing is not null, then:
    1. Abort these steps.
  4. Invoke [[Update]] algorithm, or its equivalent, with registration as its argument.

Inspect whether the promise returned from [[Update]] algorithm should be returned to the caller (either UA internal context or self.update()).

[[Install]]

Input
registration, a ServiceWorkerRegistration object
worker, a ServiceWorker object
Output
None
  1. Let installFailed be false.
  2. Let activateImmediate be false.
  3. Set registration.installing to worker.
  4. Run the [[UpdateState]] algorithm passing registration.installing and "installing" as the arguments.
  5. If registration.[[UpdatePromise]] is not null, and not settled, then:
    1. Resolve registration.[[UpdatePromise]] with registration.
    2. Set registration.[[UpdatePromise]] to null.
  6. Queue a task to fire a simple event named updatefound on navigator.serviceWorker for all documents which match registration.scope.
  7. Queue a task to run the following substeps:
    1. Fire an event named install using InstallEvent interface on the associated ServiceWorkerGlobalScope object.
    2. For each event listener invoked:
      1. If any uncaught runtime script error occurs, then:
        1. Report the error for the script per the runtime script errors handling.
        2. Run the [[UpdateState]] algorithm passing registration.installing and "redundant" as the arguments.
        3. Set registration.installing to null.
        4. Abort these steps.
      2. Let event be the event for which this event listener was invoked.
      3. If event's extend lifetime flag is set, then:
        1. Wait until event's extend lifetime flag is unset.
        2. If event's wait-until reject flag is set, then:
          1. Set installFailed to true.
      4. If event's activate immediate flag is set, then:
        1. Set activateImmediate to true.
  8. Wait for the task queued by the previous step to have executed.
  9. If registration.installing is null, then:
    1. Abort these steps.
  10. If installFailed is true, then:
    1. Run the [[UpdateState]] algorithm passing registration.installing and "redundant" as the arguments.
    2. Set registration.installing to null.
    3. Abort these steps.
  11. If registration.waiting is not null, then:
    1. Run the [[UpdateState]] algorithm passing registration.waiting and "redundant" as the arguments.
  12. Set registration.waiting to registration.installing.
  13. Set registration.installing to null.
  14. Run the [[UpdateState]] algorithm passing registration.waiting and "installed" as the arguments.
  15. If activateImmediate is true, then:
    1. For each document matching registration.scope:
      1. Set registration to the document's Service Worker registration.
    2. Run [[Activate]] algorithm, or its equivalent, passing registration as the argument.
    3. Abort these steps.
  16. Wait until no document is using registration as their Service Worker registration.
  17. Invoke [[Activate]] algorithm, or its equivalent, with registration as its argument.

[[Activate]]

Input
registration, a ServiceWorkerRegistration object
Output
None
  1. Let activateFailed be false.
  2. Let activatingWorker be registration.waiting.
  3. Let exitingWorker be registration.active.
  4. If exitingWorker is not null, then:
    1. Wait for exitingWorker to finish handling any in-progress requests.
    2. Terminate exitingWorker.
    3. Run the [[UpdateState]] algorithm passing exitingWorker and "redundant" as the arguments.
  5. Set registration.active to activatingWorker.
  6. Set registration.waiting to null.
  7. Run the [[UpdateState]] algorithm passing registration.active and "activating" as the arguments.
  8. Queue a task to fire a simple event named controllerchange on navigator.serviceWorker for all documents that have used registration as its Service Worker registration.
  9. Queue a task to run the following substeps:
    1. Fire an event named activate using InstallPhaseEvent interface on the associated ServiceWorkerGlobalScope object.
    2. For each event listener invoked:
      1. If any uncaught runtime script error occurs, then:
        1. Report the error for the script per the runtime script errors handling.
        2. Run the [[UpdateState]] algorithm passing registration.active and "redundant" as the arguments.
        3. Set registration.active to null.
        4. Abort these steps.
      2. Let event be the event for which this event listener was invoked.
      3. If event's extend lifetime flag is set, then:
        1. Wait until event's extend lifetime flag is unset.
        2. If event's wait-until reject flag is set, then:
          1. Set activateFailed to true.
  10. Wait for the task queued by the previous step to have executed.
  11. If activateFailed is true, then:
    1. Run the [[UpdateState]] algorithm passing registration.active and "redundant" as the arguments.
    2. Set registration.active to null.
    3. Abort these steps.
  12. Run the [[UpdateState]] algorithm passing registration.active and "activated" as the arguments.

[[HandleFetch]]

The [[HandleFetch]] algorithm is the entry point for the fetch handling handed to the Service Worker context. The invocation of handle a fetch with request must act as if the caller immediately invoked [[HandleFetch]] algorithm with request.

Input
request, a request
Output
response, a response
  1. Let handleFetchFailed be false.
  2. Let respondWithEntered be false.
  3. Let r be a new Request object associated with request.
  4. Let response be null.
  5. Let registration be null.
  6. Let document be the responsible document specified by the relevant settings object for request's client.
  7. If request's context is navigate, then:
    1. If the navigation triggering request was initiated with a shift+reload or equivalent, then:
      1. Return null.
    2. Set registration to the result of running [[MatchScope]] algorithm, or its equivalent, passing r.url as the argument.
  8. Else,
    1. Set registration to the ServiceWorkerRegistration object used by document.
  9. If registration is null, then:
    1. Return null.
  10. If registration.active is null, then:
    1. Return null.
  11. Let matchedWorker be registration.active.
  12. If request's context is navigate, then:
    1. Let document use registration as its Service Worker registration.
  13. If matchedWorker.state is "activating", then:
    1. Wait for matchedWorker.state to become "activated".
    2. If matchedWorker's activation fails, then:
      1. Return null.
  14. Queue a task to run the following substeps:
    1. Fire an event named fetch, using FetchEvent interface, with request attribute initialized to r, client attribute initialized to client of the request, in the form of Client object, context attribute initialized to context of the request in string, and isReload initialized to true if event was dispatched with the user's intention for the page reload, and false otherwise, on the associated ServiceWorkerGlobalScope object.
    2. For each event listener invoked:
      1. If any uncaught runtime script error occurs, then:
        1. Report the error for the script per the runtime script errors handling.
        2. Run the [[UpdateState]] algorithm passing registration.installing and "redundant" as the arguments.
        3. Set registration.installing to null.
        4. Abort these steps.
      2. Let event be the event for which this event listener was invoked.
      3. If event's wait to respond flag is set, then:
        1. Set respondWithEntered to true.
        2. Wait until event's wait to respond flag is unset.
        3. If event's respond-with error flag is set, then:
          1. Set handleFetchFailed to true.
        4. Else:
          1. Set response to the value, which the argument passed into the event's respondWith(r) method resolved with.
  15. Wait for the task queued by the previous step to have executed.
  16. If respondWithEntered is false, then:
    1. Return null and run the following steps asynchronously.
    2. If request's context is navigate, then:
      1. Invoke [[SoftUpdate]] algorithm, or its equivalent, with registration.
    3. Abort these steps.
  17. If handleFetchFailed is true, then:
    1. Return a network error.
  18. Else:
    1. Return a response represented by response and run the following steps asynchronously.
    2. If request's context is navigate, then:
      1. Invoke [[SoftUpdate]] algorithm, or its equivalent, with registration.

[[HandleDocumentUnload]]

The user agent must run these steps, or their equivalent, when it unloads a document.

Input
document, a document using the given ServiceWorkerRegistration object as its registration
Output
None
  1. Let registration be the Service Worker registration used by document.
  2. If registration is null, then:
    1. Abort these steps.
  3. If any other document is using registration as their Service Worker registration, then:
    1. Abort these steps.
  4. If registration.[[Uninstalling]] is true, then:
    1. Invoke [[ClearRegistration]] algorithm passing registration as its argument.
    2. Abort these steps.
  5. If registration.[[UpdatePromise]] is null and registration.installing is null and registration.waiting is null and registration.active is null, then:
    1. Invoke [[ClearRegistration]] algorithm passing registration as its argument.
    2. Abort these steps.
  6. If registration.waiting is not null:
    1. Run [[Activate]] algorithm, or its equivalent, with registration at the argument.

[[Unregister]]

Input
scope, a string that represents a path expression or a URL scope
Output
promise, a promise
  1. Let promise be a new promise.
  2. Run the following steps asynchronously:
    1. Let scope be the result of running the URL parser on scope with entry settings object's API base URL.
    2. Set scope to the result of running the URL serializer on scope with the exclude fragment flag set.
    3. If the origin of scope does not match the document's origin, then:
      1. Reject promise with a "SecurityError" exception.
      2. Abort these steps.
    4. Let registration be the result of running [[GetRegistration]] algorithm passing scope as the argument.
    5. If registration is null, then:
      1. Resolve promise.
      2. Abort these steps.
    6. Set registration.[[Uninstalling]] to true.
    7. If registration.[[UpdatePromise]] is not null, then:
      1. Reject registration.[[UpdatePromise]] with an "AbortError" exception.
      2. The user agent may abort in-flight requests, parsing or worker execution relating to registration.[[UpdatePromise]].
    8. Resolve promise.
    9. If no document is using registration as their Service Worker registration, then:
      1. If registration.[[Uninstalling]] is false, then:
        1. Abort these steps.
      2. Invoke [[ClearRegistration]] algorithm passing registration as its argument.
  3. Return promise.

[[ClearRegistration]]

Input
registration, a ServiceWorkerRegistration object
Output
None
  1. If registration.installing is not null, then:
    1. Terminate registration.installing.
    2. Run the [[UpdateState]] algorithm passing registration.installing and "redundant" as the arguments.
    3. Set registration.installing to null.
    4. The user agent may abort in-flight requests triggered by registration.installing.
  2. If registration.waiting is not null, then:
    1. Terminate registration.waiting.
    2. Run the [[UpdateState]] algorithm passing registration.waiting and "redundant" as the arguments.
    3. Set registration.waiting to null.
    4. The user agent may abort in-flight requests triggered by registration.waiting.
  3. If registration.active is not null, then:
    1. Terminate registration.active.
    2. Run the [[UpdateState]] algorithm passing registration.active and "redundant" as the arguments.
    3. Set registration.active to null.
    4. The user agent may abort in-flight requests triggered by registration.active.
  4. Delete a Record {[[key]], [[value]]} entry of its [[ScopeToRegistrationMap]] where registration.scope matches entry.[[key]].

[[UpdateState]]

Input
worker, a ServiceWorker object
state, a state
Output
None
  1. Queue a task to run these steps:
    1. Set worker.state to state.
    2. Fire a simple event named statechange at worker.

[[MatchScope]]

Input
scope, a string that represents a URL scope
Output
registration, a ServiceWorkerRegistration object
  1. Let matchingScope be the longest [[key]] in [[ScopeToRegistrationMap]] that glob-matches scope.
  2. Let registration be the result of running [[GetRegistration]] algorithm passing matchingScope as the argument.
  3. If registration is not null and registration.[[Uninstalling]] is true, then:
    1. Return null.
  4. Return registration.

[[GetRegistration]]

Input
scope, a string that represents a URL scope
Output
registration, a ServiceWorkerRegistration object
  1. Let registration be null.
  2. For each Record {[[key]], [[value]]} entry of its [[ScopeToRegistrationMap]]:
    1. If scope matches entry.[[key]], then:
      1. Set registration to entry.[[value]].
  3. Return registration.

[[GetNewestWorker]]

Input
registration, a ServiceWorkerRegistration object
Output
worker, a ServiceWorker object
  1. Let newestWorker be null.
  2. If registration.installing is not null, then:
    1. Set newestWorker to registration.installing.
  3. Else if registration.waiting is not null, then:
    1. Set newestWorker to registration.waiting.
  4. Else if registration.active is not null, then:
    1. Set newestWorker to registration.active.
  5. Return newestWorker.

[[QueryCache]]

Input
request, a Request object
params, a QueryParams object, optional
Output
resultArray, an array that has [Request, Response] pairs as its elements
  1. Let requestArray be an empty array.
  2. Let responseArray be an empty array.
  3. Let request be the result of invoking the initial value of Request as constructor with request as the argument.
  4. If this throws an exception, then:
    1. Rethrow that exception.
    2. Abort these steps.
  5. If params.ignoreMethod is false and request.method is neither "GET" nor "HEAD", then:
    1. Return responseArray.
  6. For each Record {[[key]], [[value]]} entry of its [[RequestToResponseMap]]:
    1. Let cachedURL be entry.[[key]].url.
    2. Let requestURL be request.url.
    3. If params.ignoreSearch is true, then:
      1. Set cachedURL.search to the empty string.
      2. Set requestURL.search to the empty string.
    4. If params.prefixMatch is true, then:
      1. Set cachedURL.href to the substring of itself from the start, with the length of requestURL.href.
    5. If cachedURL.href matches requestURL.href, then:
      1. Add entry.[[key]] to requestArray.
      2. Add entry.[[value]] to responseArray.
  7. Let resultArray be an empty array.
  8. For each cachedResponse in responseArray with the index index:
    1. Let cachedRequest be the indexth element in requestArray.
    2. If the result of running cachedResponse.headers object's has(name) method with "Vary" as the argument is false, or params.ignoreVary is true, then:
      1. Add an array [cachedRequest, cachedResponse] to resultArray.
    3. Let varyHeaders be the array containing the elements corresponding to the field-values of the Vary header.
    4. For each f in varyHeaders:
      1. If f matches "*", then:
        1. Continue to the next iteration of the loop.
      2. If the result of running cachedRequest.headers object's get(name) method with f as the argument does not match the result of running request.headers object's get(name) method with f as the argument, then:
        1. Break the loop.
      3. Add an array [cachedRequest, cachedResponse] to resultArray.
  9. Return resultArray.

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 throughout 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.