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

Model

Service Worker

A service worker is a type of web worker. A service worker executes in the registering service worker client's origin.

A service worker has an associated state, which is one of parsed, installing, installed, activating, activated, and redundant. (Initially parsed).

A service worker has an associated script url (a URL).

A service worker has an associated containing service worker registration (a service worker registration), which contains itself.

A service worker is dispatched a set of lifecycle events, install and activate, and functional events including fetch.

Lifetime

The lifetime of a service worker is tied to the execution lifetime of events, not references held by service worker clients to the ServiceWorker object. The user agent may terminate service workers at any time it has no event to handle.

Service Worker Registration

A service worker registration is a tuple of a scope url and a set of service workers, an installing worker, a waiting worker, and an active worker. User agents may enable many service worker registrations at a single origin so long as the scope url of the service worker registration differs. service worker registration of an identical scope url when one already exists in the user agent causes the existing service worker registration to be replaced.

A service worker registration has an associated scope url (a URL).

A service worker registration has an associated registering script url (a URL).

A service worker registration has an associated installing worker (a service worker) whose state is installing. It is initially set to null.

A service worker registration has an associated waiting worker (a service worker) whose state is installed. It is initially set to null.

A service worker registration has an associated active worker (a service worker) whose state is either activating or activated. It is initially set to null. An active worker controls a service worker client if the active worker's service worker registration's scope url matches the service worker client's client url upon navigation. When a service worker client is controlled by an active worker, it is considered the service worker client is using the active worker's service worker registration.

A service worker registration has an associated uninstalling flag. It is initially unset.

Lifetime

The user agents must persistently keep a list of registered service worker registrations unless otherwise they are explicitly unregistered. The lifetime of service worker registrations is beyond that of the ServiceWorkerRegistration objects which represent them within the lifetime of their corresponding service worker clients. [[ScopeToRegistrationMap]] represents an internal map structure that stores the entries of the tuple of service worker registration's scope url and the corresponding service worker registration.

Service Worker Client

A service worker client is either a document in a browsing context or a shared worker, which is controlled by an active worker. A service worker client independently selects and uses a service worker for its own loading and its subresources. Unless specified otherwise, a service worker client is a document.

A service worker client has an associated service worker registration (a service worker registration) it uses.

A service worker client has an associated hidden flag. Initially set to false.

A service worker client has an associated focused flag. Initially set to false.

A service worker client has an associated client url, which is one of the URL of itself when it is a document of a browsing context and environment settings object's API base URL when it is a shared worker.

A service worker client has an associated frame type, which is one of the values of context frame type.

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."); } }, function(why) { console.error("Installing the worker failed!:", why); });

Interface ServiceWorker

[Exposed=(Window,ServiceWorker)] interface ServiceWorker : Worker { readonly attribute USVString scriptURL; readonly attribute ServiceWorkerState state; // event attribute EventHandler onstatechange; // terminate() method inherited from Worker should not be accessible. }; enum ServiceWorkerState { "installing", "installed", "activating", "activated", "redundant" };

A ServiceWorker object represents a service worker. Each ServiceWorker object is associated with a service worker. Multiple separate objects implementing the ServiceWorker interface across document environments and service worker environments can all be associated with the same service worker simultaneously.

A ServiceWorker object has an associated ServiceWorkerState object which is itself associated with service worker's state.

The terminate() method inherited from Worker, when called on the context object, should throw an "InvalidAccessError" exception.

The postMessage(message, transfer) method inherited from Worker, when called on the context object, should throw an "InvalidStateError" exception if the state attribute of the context object is "redundant". Otherwise, it must run as defined on the Worker interface.

scriptURL

The scriptURL attribute must return service worker's serialized script url.

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

The state attribute must return the value (in ServiceWorkerState enumeration) corresponding to the first matching statement, switching on service worker's state:

installing
"installing"

The service worker in this state is considered an installing worker. During this state, event.waitUntil(f) can be called inside the oninstall event handler 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
"installed"

The service worker in this state is considered a waiting worker.

activating
"activating"

The service worker in this state is considered an active worker. During this state, event.waitUntil(f) can be called inside the onactivate event handler to extend the life of the active worker until the passed promise resolves successfully. No functional events are dispatched until the state becomes activated.

activated
"activated"

The service worker in this state is considered an active worker ready to handle functional events.

redundant
"redundant"

A new service worker is replacing the current 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

Interface ServiceWorkerRegistration

[Exposed=Window] interface ServiceWorkerRegistration : EventTarget { [Unforgeable] readonly attribute ServiceWorker? installing; [Unforgeable] readonly attribute ServiceWorker? waiting; [Unforgeable] readonly attribute ServiceWorker? active; readonly attribute USVString scope; Promise<boolean> unregister(); // event attribute EventHandler onupdatefound; };

A ServiceWorkerRegistration object represents a service worker registration. Each ServiceWorkerRegistration object is associated with a service worker registration. Multiple separate objects implementing the ServiceWorkerRegistration interface across document environments can all be associated with the same service worker registration simultaneously.

installing

installing attribute must return a ServiceWorker object that represents the installing worker.

waiting

waiting attribute must return a ServiceWorker object that represents the waiting worker.

active

active attribute must return a ServiceWorker object that represents the active worker.

scope

The scope attribute must return service worker registration's serialized scope url.

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 service worker registration. It is important to note that the currently controlled service worker client's service worker registration is effective until all the service worker clients (including itself) using this service worker 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 document, the context object's associated document, and scope, the scope url of the service worker registration, as the arguments.

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 ServiceWorkerRegistration 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 a ServiceWorkerContainer object, which provides access to registration, removal, upgrade, and communication with service workers for the associated document.

Communication with these workers is provided via standard HTML5 messaging APIs, and messaging occurs as per usual with Web Workers.

ServiceWorkerContainer

[Exposed=Window] interface ServiceWorkerContainer : EventTarget { [Unforgeable] readonly attribute ServiceWorker? controller; readonly attribute Promise<ServiceWorkerRegistration> ready; Promise<ServiceWorkerRegistration> register(USVString scriptURL, optional RegistrationOptions options); Promise<ServiceWorkerRegistration> getRegistration(optional USVString documentURL = ""); Promise<sequence<ServiceWorkerRegistration>?> getRegistrations(); // events attribute EventHandler oncontrollerchange; attribute EventHandler onerror; }; dictionary RegistrationOptions { USVString scope = "/"; };

A ServiceWorkerContainer's associated Window is the associated Window of the Navigator object that the ServiceWorkerContainer is retrieved from. A ServiceWorkerContainer's associated Document is the newest Document object of its associated Window.

A ServiceWorkerContainer object has an associated ready promise (a promise). It is initially set to null.

controller

controller attribute must return a ServiceWorker object that represents the active worker for the context object's associated document.

navigator.serviceWorker.controller returns null if the request is a force refresh (shift+refresh).

ready

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

  1. If the context object's ready promise is null, then:
    1. Set the context object's ready promise to a new promise.
  2. If the context object's ready promise is settled, then:
    1. Return the context object's ready promise.
  3. Let registration be null.
  4. Let documentURL be the result of running the URL serializer on the context object's associated document's URL.
  5. Run the following substeps in parallel:
    1. CheckRegistration: If the result of running Match Scope algorithm, or its equivalent, with documentURL as its argument is not null, then:
      1. Set registration to the result value.
    2. Else:
      1. Wait until [[ScopeToRegistrationMap]] has a new entry.
      2. Jump to the step labeled CheckRegistration.
    3. If registration's active worker is null, then:
      1. Wait until registration acquires an active worker.
      2. Implementers should consider this condition is met when the corresponding registration request gets to the step 1.8 of Activate algorithm.

    4. Resolve context object's ready promise with registration.
  6. Return context object's ready promise.

The ready attribute is designed in a way that the returned promise will never reject. Instead, it waits until the promise resolves with a service worker registration that has an active worker.

register(scriptURL, options)

The register(scriptURL, options) method creates or updates a service worker registration for the given scope url. If successful, a service worker registration ties the provided script url to a scope url, which is subsequently used for navigation matching.

register(scriptURL, options) method must return the result of running these steps or their equivalent:

  1. Let p be the result of running the Register algorithm, or their equivalent, passing the context object's associated document document, scriptURL, options.scope, and entry settings object's API base URL as the arguments.
  2. Return p.

getRegistration(documentURL)

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

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

getRegistrations()

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

  1. Let promise be a new promise.
  2. Run the following substeps in parallel:
    1. Let array be an empty array.
    2. Let origin be the unicode serialization of the context object's associated 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 a ServiceWorkerRegistration object associated with 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
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 A service worker registration acquires a new installing worker (See step 5.7 of the Install algorithm).
controllerchange Event The document's associated service worker registration acquires a new active worker. (See step 1.8 of the Activate algorithm. When the activation of the service worker registration 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.)
error ErrorEvent Any error occurred from the associated service workers.

Execution Context

Example: Serving Cached Resources

// caching.js this.addEventListener("install", function(e) { e.waitUntil( // Create a cache of resources. caches.create("shell-v1").then(function(cache) { // Begins the process of fetching them. // The coast is only clear when all the resources are ready. return cache.addAll([ "/app.html", "/assets/v1/base.css", "/assets/v1/app.js", "/assets/v1/logo.png", "/assets/v1/intro_video.webm" ]) }) ); }); this.addEventListener("fetch", function(e) { // No "fetch" 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).then(function(response) { return response || e.default(); }).catch(function() { return caches.match("/fallback.html"); }) ); });

ServiceWorkerGlobalScope

[Global=(Worker,ServiceWorker), Exposed=ServiceWorker] interface ServiceWorkerGlobalScope : WorkerGlobalScope { readonly attribute Cache scriptCache; readonly attribute CacheStorage caches; // A container for a list of ServiceWorkerClient objects that correspond to // browsing contexts (or shared workers) that are on the origin of this SW readonly attribute ServiceWorkerClients clients; [Unforgeable] readonly attribute USVString scope; void update(); Promise<boolean> 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 ServiceWorkerClient attribute EventHandler onmessage; // close() method inherited from WorkerGlobalScope should not be accessible. };

A ServiceWorkerGlobalScope object 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 service worker clients. Any type of synchronous requests must not be initiated inside of a service worker.

The close() method inherited from WorkerGlobalScope, when called on the context object, should throw an "InvalidAccessError" exception.

scriptCache

scriptCache must return a Cache object that represents the storage for scripts that are cached as part of the service worker installation.

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 Cache objects. Cache objects are always enumerable via self.caches in insertion order (per ECMAScript 6 Map objects.)

clients

clients attribute must return the ServiceWorkerClients object.

scope

The scope attribute must return the serialized scope url of the service worker's associated service worker registration.

update()

update() pings the server for an updated version of this script without consulting caches. This is conceptually the same operation that UA does maximum once per every 24 hours.

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

  1. Let scope be the scope url of the service worker's associated service worker registration.
  2. Let registration be the result of running Get Registration algorithm passing scope as the argument.
  3. Invoke Soft Update algorithm, or its equivalent, passing registration as the argument.

unregister()

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

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

  1. Let scope be the scope url of the service worker's containing service worker registration.
  2. Return the result of running Unregister algorithm, or its equivalent, passing document, the context object's associated document, and 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 ServiceWorkerClient.

ServiceWorkerClient

[Constructor(USVString url), Exposed=ServiceWorker] interface ServiceWorkerClient { readonly attribute Promise ready; readonly attribute boolean hidden; readonly attribute boolean focused; readonly attribute USVString url; readonly attribute ContextFrameType frameType; void postMessage(any message, optional sequence<Transferable> transfer); Promise focus(); };

The ServiceWorkerClient object has an associated service worker client (a service worker client).

This section will be updated as per SW #414, SW #423.

ServiceWorkerClients

[Exposed=ServiceWorker] interface ServiceWorkerClients { // The objects returned will be new instances every time Promise<sequence<ServiceWorkerClient>?> getAll(optional ServiceWorkerClientQueryOptions options); }; dictionary ServiceWorkerClientQueryOptions { boolean includeUncontrolled = false; };

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

getAll(options)

The getAll(options) method must run these steps or their equivalent:

  1. Let promise be a new promise.
  2. Run these steps in parallel:
    1. Let clients be an empty array.
    2. If the optional argument options is present and options.includeUncontrolled is true, then:
      1. For each service worker client client whose origin is the same as the associated service worker's origin:
        1. Add a ServiceWorkerClient object that represent client to clients.
      2. Resolve promise with clients.
    3. Else:
      1. For each service worker client client which are controlled by the associated service worker:
        1. Add a ServiceWorkerClient object that represent client to clients.
      2. Resolve promise with clients.
  3. Return promise.

This section will be updated as per SW #414.

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

[Exposed=ServiceWorker] interface Cache { Promise<Response> match(RequestInfo request, optional CacheQueryOptions options); Promise<sequence<Response>> matchAll(optional RequestInfo request, optional CacheQueryOptions options); Promise<Response> add(RequestInfo request); Promise<sequence<Response>> addAll(sequence<RequestInfo> requests); Promise<Response> put(RequestInfo request, Response response); Promise<boolean> delete(RequestInfo request, optional CacheQueryOptions options); Promise<sequence<Request>> keys(optional RequestInfo request, optional CacheQueryOptions options); }; dictionary CacheQueryOptions { boolean ignoreSearch = false; boolean ignoreMethod = false; boolean ignoreVary = false; boolean prefixMatch = false; DOMString cacheName; }; dictionary CacheBatchOperation { DOMString type; Request request; Response response; CacheQueryOptions options; };

The Cache interface represents the storage for the pairs of a Request object and a Response object that are cached as part of the service worker installation. A Cache object has a [[RequestToResponseMap]] object.

Each Cache object is associated with a [[RequestToResponseMap]]. Multiple separate objects implementing the Cache interface across service worker environments can all be associated with the same [[RequestToResponseMap]] simultaneously.

match(request, options)

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

  1. Let promise be a new promise.
  2. Run these steps in parallel:
    1. Let p be the result of running the algorithm specified in matchAll(request, options) method with request and options 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. Resolve promise with undefined.
      2. Else:
        1. Resolve promise with the first element of responseArray.
  3. Return promise.

matchAll(request, options)

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

  1. Let promise be a new promise.
  2. Run these steps in parallel:
    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]], in key insertion order:
        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 Query Cache algorithm passing request and options as the arguments.
      2. For each entry of entries:
        1. Add entry[1] to responseArray.
      3. Resolve promise with responseArray.
  3. Return promise.

add(request)

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

  1. Let requests be an array containing only request.
  2. Set responseArrayPromise to the result of running the algorithm specified in addAll(requests) passing requests as the argument.
  3. Let p be transforming responseArrayPromise with onFulfilled.
  4. Upon fulfillment of p with value responseArray, perform the following substeps, onFulfilled, in parallel:
    1. Resolve p with undefined.
  5. Return p.

addAll(requests)

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

  1. Let responsePromiseArray be an empty array.
  2. Let requestArray be an empty array.
  3. For each request in requests:
    1. Let r be null.
    2. If request is a USVString, then:
      1. Set r to the associated request of the result of invoking the initial value of Request as constructor with request as its argument. If this throws an exception, return a promise rejected with that exception.
    3. Else:
      1. Set r to request's associated request.
    4. Add a Request object that represents r to requestArray.
    5. If r's url's scheme is not one of "http" and "https", then:
      1. Add a promise rejected with a "NetworkError" exception to responsePromiseArray.
      2. Continue to the next iteration of the loop.
    6. Run the algorithm specified in fetch(input, init) with r and add the returned value to responsePromiseArray.
  4. Let p be waiting for all of responsePromiseArray.
  5. Let q be transforming p with onFulfilled.
  6. Upon fulfillment of p with value responseArray, perform the following substeps, onFulfilled, in parallel:
    1. Let operations be an empty array.
    2. 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.
    3. Let resultPromise to the result of running Batch Cache Operations algorithm passing operations as the argument.
    4. Resolve q with undefined.
  7. 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. Let resultPromise to the result of running Batch Cache Operations passing operations as the argument.
  8. Let p be transforming resultPromise with onFulfilled.
  9. Upon fulfillment of p with responseArray, perform the following substeps, onFulfilled, in parallel:
    1. Resolve p with undefined.
  10. Return p.

delete(request, options)

delete(request, options) 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 options dictionary member of o to options.
  6. Add o to operations.
  7. Let resultPromise to the result of running Batch Cache Operations passing operations as the argument.
  8. Let p be transforming resultPromise with onFulfilled.
  9. Upon fulfillment of p with responseArray, perform the following substeps, onFulfilled, in parallel:
    1. If responseArray is not null, then:
      1. Resolve p with true.
    2. Else:
      1. Resolve p with false.
  10. Return p.

keys(request, options)

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

  1. Let promise be a new promise.
  2. Run these steps in parallel:
    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]], in key insertion order:
        1. Add entry.[[key]] to resultArray.
    3. Else:
      1. Let requestResponseArray be the result of running Query Cache algorithm passing request and options as the arguments.
      2. For each requestResponse in requestResponseArray:
        1. Add requestResponse[0] to resultArray.
    4. Resolve promise with resultArray.
  3. Return promise.

CacheStorage

[Exposed=ServiceWorker] interface CacheStorage { Promise<Response> match(RequestInfo request, optional CacheQueryOptions options); Promise<boolean> has(DOMString cacheName); Promise<Cache> open(DOMString cacheName); Promise<boolean> 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.

The CacheStorage interface represents the storage for Cache objects. A CacheStorage object has a [[NameToCacheMap]] object.

match(request, options)

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

  1. Let cacheName be null.
  2. If the optional argument options is not omitted, then:
    1. Set cacheName to options.cacheName.
  3. If cacheName is not null, then:
    1. Return a promise, p, resolved with the result of running the following substeps:
      1. For each Record {[[key]], [[value]]} entry of its [[NameToCacheMap]], in key insertion order:
        1. If cacheName matches entry.[[key]], then:
          1. Resolve p with the result of running the algorithm specified in match(request, options) method of Cache interface with request and options as the arguments (providing entry.[[value]] as thisArgument to the [[Call]] internal method of match(request, options).)
          2. Abort these steps.
      2. Reject p with an "NotFoundError" exception.
  4. Else:
    1. Set p to transforming the result of running the algorithm specified in keys() method, with onFulfilled.
    2. Upon fulfillment of p with value keys, perform the following substeps, onFulfilled, in parallel:
      1. For each key in keys:
        1. Let q be the result of running the algorithm specified in match(request, options) method of Cache interface with request and options as the arguments (providing key as thisArgument to the [[Call]] internal method of match(request, options).)
        2. Upon fulfillment of q with value matchedResponse:
          1. If matchedResponse is not undefined, then:
            1. Resolve p with matchedResponse.
            2. Abort these steps.
        3. Upon rejection of q with value err:
          1. Reject p with err.
          2. Abort these steps.
      2. Resolve p with undefined.
    3. Return p.

has(cacheName)

has(cacheName) method must run these steps or their equivalent:

  1. Return a promise, p, resolved with the result of running the following substeps:
    1. For each Record {[[key]], [[value]]} entry of its [[NameToCacheMap]], in key insertion order:
      1. If cacheName matches entry.[[key]], then:
        1. Return true.
    2. Return false.

open(cacheName)

open(cacheName) method must run these steps or their equivalent:

  1. Return a promise, p, resolved with the result of running the following substeps:
    1. For each Record {[[key]], [[value]]} entry of its [[NameToCacheMap]], in key insertion order:
      1. If cacheName matches entry.[[key]], then:
        1. Return entry.[[value]].
        2. Abort these steps.
    2. Let cache be a new Cache object.
    3. Set a newly-created Record {[[key]]: cacheName, [[value]]: cache} to [[NameToCacheMap]].
    4. Return cache.

delete(cacheName)

delete(cacheName) method must run these steps or their equivalent:

  1. Let p be the result of running the algorithm specified in has(cacheName) method with cacheName as the argument.
  2. Let q be transforming p with onFulfilled.
  3. Upon fulfillment of p with value cacheExists, perform the following substeps, onFulfilled, in parallel:
    1. If cacheExists is true, then:
      1. Delete a Record {[[key]], [[value]]} entry of its [[NameToCacheMap]] where cacheName matches entry.[[key]].
      2. Resolve q with true.
      3. Abort these steps.
    2. Else:
      1. Resolve q with flase.
  4. Return q.

keys()

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

The promise returned from this method resolves with the sequence of keys, cache names in DOMString, in insertion order.

  1. Let resultArray be an empty array.
  2. Return a promise, p, resolved with the result of running the following substeps:
    1. For each Record {[[key]], [[value]]} entry of its [[NameToCacheMap]], in key insertion order:
      1. Add entry.[[key]] to resultArray.
    2. Return resultArray.

ExtendableEvent

[Constructor(DOMString type, optional ExtendableEventInit eventInitDict), Exposed=ServiceWorker] interface ExtendableEvent : Event { void waitUntil(Promise<any> f); }; dictionary ExtendableEventInit : EventInit { // Defined for the forward compatibility across the derived events };

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

Each event using ExtendableEvent 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. Set the extend lifetime flag.
  2. Run the following substeps in parallel:
    1. Wait until f settles.
    2. If f rejected, then:
      1. Set the wait-until reject flag.
    3. Else if f resolved with a value, then:
      1. Do nothing.
    4. Unset the extend lifetime flag.

InstallEvent

[Constructor(DOMString type, optional InstallEventInit eventInitDict), Exposed=ServiceWorker] interface InstallEvent : ExtendableEvent { 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), Exposed=ServiceWorker] interface FetchEvent : Event { readonly attribute Request request; readonly attribute ServiceWorkerClient client; readonly attribute boolean isReload; void respondWith((Response or Promise<Response>) r); Promise<Response> forwardTo(USVString url); Promise<Response> default(); }; dictionary FetchEventInit : EventInit { Request request; ServiceWorkerClient client; boolean isReload; };

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 network error is returned to Fetch. 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 network error is returned to Fetch. 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 respond-with entered flag is set, then:
    1. Throw an "InvalidStateError" exception.
    2. Abort these steps.
  2. Set the respond-with entered flag.
  3. Set the wait to respond flag.
  4. Run the following substeps in parallel:
    1. Wait until r settles.
    2. If r rejected, then:
      1. Set the respond-with error flag.
    3. If r resolved with response, then:
      1. If response is an instance of Response interface, then:
        1. Let request be the value that the context object's request attribute's getter returns
        2. If response.type is "opaque", and request's associated request's context is one of frame, hyperlink, iframe, location, and sharedworker, then:
          1. Set the respond-with error flag.
      2. Else:
        1. Set the respond-with error flag.
    4. If the respond-with error flag is set, a network error is returned to Fetch through Handle Fetch algorithm. (See the step 23.1.) Otherwise, the value response is returned to Fetch through Handle Fetch algorithm. (See the step 24.1.)

    5. 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 service worker client. 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. If event's dispatch flag is unset, then:
    1. Return a promise rejected with a "InvalidStateError" exception.
  2. Let promise be a new promise.
  3. Run the following substeps in parallel:
    1. Let r be the value that the context object's request attribute's getter returns.
    2. Let request be r's associated request.
    3. Set request's skip service worker flag and request's synchronous flag.
    4. Let response be the result of running fetch with request as its argument.
    5. If response is a network error, then:
      1. Reject promise with a TypeError.
    6. Else:
      1. Resolve promise with a new Response object associated with response.
  4. 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 service worker registration acquires a new installing worker. (See step 5.8.1 of the Install algorithm.)
activate ExtendableEvent [Lifecycle event] A service worker registration acquires a new activating worker. (See step 1.9.1 of the Activate algorithm.)
fetch FetchEvent [Functional event] Fetch invokes Handle Fetch with request. As a result of performing Handle 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

Service workers are extensible from other specifications.

Define API bound to Service Worker Registration

Specifications may define an API tied to a service worker registration by using partial interface definition to the ServiceWorkerRegistration interface where it may define the specification specific attributes and methods:

partial interface ServiceWorkerRegistration { // e.g. define an API namespace readonly attribute APISpaceType APISpace; // e.g. define a method Promise<T> methodName(list of arguments); };

Define Functional Event

Specifications may define a functional event by extending ExtendableEvent interface:

// e.g. define FunctionalEvent interface interface FunctionalEvent : ExtendableEvent { // add a functional event's own attributes and methods };

Define Event Handler

Specifications may define an event handler attribute for the corresponding functional event using partial interface definition to the ServiceWorkerGlobalScope interface:

partial interface ServiceWorkerGlobalScope { attribute EventHandler onfunctionalevent; };

Request Functional Event Dispatch

To request a functional event dispatch to a service worker, specifications may invoke Handle Functional Event algorithm with its service worker registration registration and the algorithm callbackSteps as the arguments.

Specifications may define an algorithm callbackSteps where the corresponding functional event can be created and fired with specification specific objects. The algorithm is passed global object (a ServiceWorkerGlobalScope object) at which it may fire its functional events. This algorithm is called on a task run by Handle Functional Event On Scheduled Task algorithm which is queued by Handle Functional Event algorithm.

See an example hook defined in Notifications API.

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 data structures.

[[ScopeToRegistrationMap]] represents an internal map structure that stores the entries of the Record {[[key]], [[value]]} where [[key]] is a string that represents a scope url and [[value]] is a service worker registration.

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

[[NameToCacheMap]] represents an internal map structure that stores the entries of the Record {[[key]], [[value]]} where [[key]] is a string that represents a name of the Cache object and [[value]] is a Cache object.

An algorithm thread queue is a thread safe queue used to synchronize the set of concurrent entries of algorithm steps. The queue contains timestamps (with the assumptions), gained by algorithms, as its elements. The queue should satisfy the general properties of FIFO queue.

[[RegistrationQueue]] is an algorithm thread queue for synchronizing the set of concurrent registration requests. The user agent must maintain a separate queue for each service worker registration keyed by its scope url. The queue is initially empty.

[[InstallationQueue]] is an algorithm thread queue for synchronizing the set of concurrent installation jobs. The user agent must maintain a separate queue for each service worker registration keyed by its scope url. The queue is initially empty.

[[InstallationResultHandleQueue]] is an algorithm thread queue for synchronizing the set of concurrent installation jobs. The user agent must maintain a separate queue for each service worker registration keyed by its scope url. The queue is initially empty.

Register

Input
document, a Document
scriptURL, a string that represents the URL of the script
scope, a relative URL that represents a path prefix of a scope url
base, a base URL, optional
Output
promise, a promise
  1. Let scope be the result of running the URL parser on scope with base.
  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 base.
  4. If the result of running Is origin authenticated origin with the origin of scriptURL as the argument is unauthenticated, then:
    1. Return a promise rejected with a "SecurityError" exception.
  5. If the origin of scriptURL does not match document's origin, then:
    1. Return a promise rejected with a "SecurityError" exception.
  6. If the origin of scope does not match document's origin, then:
    1. Return a promise rejected with a "SecurityError" exception.
  7. Run the following substeps atomically:
    1. Let registration be the result of running the Get Registration algorithm passing scope as the argument.
    2. If registration is not null, then:
      1. Let newestWorker be the result of running the Get Newest Worker algorithm passing registration as the argument.
      2. If newestWorker is not null, scriptURL is equal to newestWorker's script url, and scriptURL is equal to registration's registering script url, then:
        1. If newestWorker is an active worker, then:
          1. If registration's uninstalling flag is set, unset it.
          2. Return a promise resolved with a ServiceWorkerRegistration object that represents registration.
    3. Else:
      1. Set registration to the result of running Set Registration algorithm passing scope as its argument.
    4. Set registration's registering script url to scriptURL.
    5. Return the result of running the Update algorithm, or its equivalent, passing registration as the argument.

Update

The algorithm uses [[RegistrationQueue]] to synchronize the set of multiple registration requests. Implementers may use it or other synchronization primitives and methods to satisfy this requirement.

Input
registration, a service worker registration
Output
promise, a promise
  1. Let p be a new promise.
  2. Generate a timestamp and let timeStamp be the result value.
  3. Push timeStamp to [[RegistrationQueue]], [[InstallationQueue]], and [[InstallationResultHandleQueue]].
  4. Run the following substeps in parallel:
    1. CheckPriority: If the value of the top element of [[RegistrationQueue]] matches timeStamp, then:
      1. Pop the top element from [[RegistrationQueue]].
    2. Else:
      1. Wait until the top element of [[RegistrationQueue]] is popped.
      2. Wait is a blocking wait. Implementers may use a condition variable or its equivalent synchronization primitive.

      3. Jump to the step labeled CheckProirity.
    3. Run the following substeps atomically:
      1. If registration's installing worker is not null, then:
        1. Terminate registration's installing worker.
        2. Run the Update State algorithm passing registration's installing worker and redundant as the arguments.
        3. Set registration's installing worker to null.
        4. The user agent may abort in-flight requests triggered by registration's installing worker.
      2. Let r be the associated request of the result of invoking the initial value of Request as constructor with registration's registering script url. If this throws an exception, then:
        1. Reject p with the exception.
        2. Pop the top element from [[InstallationQueue]] and [[InstallationResultHandleQueue]].
        3. Abort these steps.
      3. Set r's context to serviceworker.
      4. Append `Service-Worker`/`script` to r's header list.
      5. Set r's skip service worker flag and r's synchronous flag.
      6. Let response be the result of running fetch using r, forcing a network fetch if cached entry is greater than 1 day old.
      7. If response is a network error, then:
        1. If r's redirect count is not zero, then:
          1. Reject p with a "SecurityError" exception.
          2. Pop the top element from [[InstallationQueue]] and [[InstallationResultHandleQueue]].
          3. Abort these steps.
        2. Else:
          1. Reject p with a "NetworkError" exception.
          2. Pop the top element from [[InstallationQueue]] and [[InstallationResultHandleQueue]].
          3. Abort these steps.
      8. Extract a MIME type from the response's header list. If this MIME type (ignoring parameters) is not one of text/javascript, application/x-javascript, or application/javascript, then:
        1. Reject p with a "SecurityError" exception.
        2. Pop the top element from [[InstallationQueue]] and [[InstallationResultHandleQueue]].
        3. Abort these steps.
      9. Let newestWorker be the result of running the Get Newest Worker algorithm passing registration as the argument.
      10. If newestWorker is not null, and newestWorker's script url is equal to registration's registering script url and response is a byte-for-byte match with the script of newestWorker, then:
        1. Resolve p with registration.
        2. Abort these steps.
      11. Else:
        1. Let worker be a new service worker for the script with registration's registering script url.
        2. If worker fails to start up, due to parse errors or uncaught errors, then:
          1. Reject p with the error.
          2. Pop the top element from [[InstallationQueue]] and [[InstallationResultHandleQueue]].
          3. Abort these steps.
    4. Invoke Install algorithm, or its equivalent, with registration, worker, p, and timeStamp as its arguments.
  5. Return p.

Soft Update

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

Input
registration, a service worker registration
Output
None
  1. If registration's uninstalling flag is set, then:
    1. Abort these steps.
  2. If registration's installing worker is not null, then:
    1. Abort these steps.
  3. 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

The algorithm uses [[InstallationQueue]] to synchronize the set of multiple installation jobs. Implementers may use it or other synchronization primitives and methods to satisfy this requirement.

Input
registration, a service worker registration
worker, a service worker
registrationPromise, a promise
timeStamp, a timestamp
Output
none
  1. Let installFailed be false.
  2. Let activateImmediate be false.
  3. CheckPriority: If the value of the top element of [[InstallationQueue]] matches timeStamp, then:
    1. Pop the top element from [[InstallationQueue]].
  4. Else:
    1. Wait until the top element of [[InstallationQueue]] is popped.
    2. Wait is a blocking wait. Implementers may use a condition variable or its equivalent synchronization primitive.

    3. Jump to the step labeled CheckProirity.
  5. Run the following substeps atomically:
    1. If registration's installing worker is not null, then:
      1. Terminate registration's installing worker.
      2. Run the Update State algorithm passing registration's installing worker and redundant as the arguments.
      3. The user agent may abort any in-flight requests triggered by registration's installing worker.
    2. Set registration's installing worker to worker.
    3. Run the Update State algorithm passing registration's installing worker and installing as the arguments.
    4. Assert: registrationPromise is not null.
    5. Resolve registrationPromise with registration.
    6. Queue a task to fire a simple event named updatefound at all the ServiceWorkerRegistration objects associated with registration for all the service worker clients which use registration.
    7. Queue a task to run the following substeps:
      1. Let installingWorker be registration's installing worker.
      2. Run a worker, if not running, for a script with installingWorker's script url and installingWorker's environment settings object.
      3. Fire an event named install using InstallEvent interface at installingWorker's environment settings object's global object.
      4. 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 Update State algorithm passing registration's installing worker and redundant as the arguments.
          3. Set registration's installing worker to null.
          4. Pop the top element from [[InstallationResultHandleQueue]].
          5. 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.
  6. CheckResultHandlePriority: If the value of the top element of [[InstallationResultHandleQueue]] matches timeStamp, then:
    1. Pop the top element from [[InstallationResultHandleQueue]].
  7. Else:
    1. Wait until the top element of [[InstallationResultHandleQueue]] is popped.
    2. Wait is a blocking wait. Implementers may use a condition variable or its equivalent synchronization primitive.

    3. Jump to the step labeled CheckResultHandlePriority.
  8. Run the following substeps atomically:
    1. Wait for the task queued by the step 5.8 to have executed.
    2. If registration's installing worker is null, then:
      1. Abort these steps.
    3. If installFailed is true, then:
      1. Run the Update State algorithm passing registration's installing worker and redundant as the arguments.
      2. Set registration's installing worker to null.
      3. Abort these steps.
    4. If registration's waiting worker is not null, then:
      1. Terminate registration's waiting worker.
      2. Run the Update State algorithm passing registration's waiting worker and redundant as the arguments.
      3. The user agent may abort in-flight requests triggered by registration's waiting worker.
    5. Set registration's waiting worker to registration's installing worker.
    6. Set registration's installing worker to null.
    7. Run the Update State algorithm passing registration's waiting worker and installed as the arguments.
    8. If registration's uninstalling flag is set before timeStamp, then:
      1. Wait until a Record {[[key]], [[value]]} entry of its [[ScopeToRegistrationMap]] where registration's scope url matches entry.[[key]] is deleted.
      2. Set a newly-created Record {[[key]]: registration's scope url, [[value]]: registration} to [[ScopeToRegistrationMap]].
      3. Unset registration's uninstalling flag.
    9. If activateImmediate is true, then:
      1. For each service worker client client whose client url matches registration's scope url:
        1. Let exitingWorker be the active worker that controls client.
        2. If exitingWorker is not null, then:
          1. Wait for exitingWorker to finish handling any in-progress requests.
          2. Terminate exitingWorker.
          3. Run the Update State algorithm passing exitingWorker and redundant as the arguments.
      2. Run Activate algorithm, or its equivalent, passing registration as the argument.
      3. Abort these steps.
  9. Wait until no service worker client is using registration as their service worker registration.
  10. Invoke Activate algorithm, or its equivalent, with registration as its argument.

Activate

Input
registration, a service worker registration
Output
None
  1. Run the following substeps atomically:
    1. Let activateFailed be false.
    2. Let activatingWorker be registration's waiting worker.
    3. Let exitingWorker be registration's active worker.
    4. If activatingWorker is null, then:
      1. Abort these steps.
    5. If exitingWorker is not null, then:
      1. Wait for exitingWorker to finish handling any in-progress requests.
      2. Terminate exitingWorker.
      3. Run the Update State algorithm passing exitingWorker and redundant as the arguments.
    6. Set registration's active worker to activatingWorker.
    7. Set registration's waiting worker to null.
    8. Run the Update State algorithm passing registration's active worker and activating as the arguments.
    9. Queue a task to fire a simple event named controllerchange at all the ServiceWorkerContainer objects for all the service worker clients which use registration.
    10. Queue a task to run the following substeps:
      1. Let activeWorker be registration's active worker.
      2. Run a worker, if not running, for a script with activeWorker's script url and activeWorker's environment settings object.
      3. Fire an event named activate using ExtendableEvent interface at activeWorker's environment settings object's global object.
      4. 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 Update State algorithm passing registration's active worker and redundant as the arguments.
          3. Set registration's active worker 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.
    11. Wait for the task queued by the previous step to have executed.
    12. If activateFailed is true, then:
      1. Run the Update State algorithm passing registration's active worker and redundant as the arguments.
      2. Set registration's active worker to null.
      3. Abort these steps.
    13. Run the Update State algorithm passing registration's active worker and activated as the arguments.

Handle Fetch

The Handle Fetch algorithm is the entry point for the fetch handling handed to the service worker context.

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 client be null.
  7. Assert: request's client's global object is either a Window object or a SharedWorkerGlobalScope object.
  8. If request's client's global object is a Window object, then:
    1. Set client to the associated document of request's client's global object.
  9. Else if request's client's global object is a SharedWorkerGlobalScope object, then:
    1. Set client to the shared worker represented by request's client's global object.
  10. Assert: request's context is not serviceworker.
  11. If request's context is either embed or object, then:
    1. Return null.
  12. Else if request's context is one of frame, hyperlink, iframe, location, and sharedworker, 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 Match Scope algorithm, or its equivalent, passing r.url as the argument.
  13. Else:
    1. If client is a service worker client, then:
      1. Set registration to client's service worker registration.
  14. If registration is null, then:
    1. Return null.
  15. If registration's active worker is null, then:
    1. Return null.
  16. Let matchedWorker be registration's active worker.
  17. Assert: request's context is not serviceworker, embed, or object.
  18. If request's context is one of frame, hyperlink, iframe, location, and sharedworker, then:
    1. Let client use registration as its service worker registration.
  19. If matchedWorker's state is activating, then:
    1. Wait for matchedWorker's state to become activated.
    2. If matchedWorker's activation fails, then:
      1. Return null.
  20. Queue a task to run the following substeps:
    1. Let activeWorker be registration's active worker.
    2. Run a worker, if not running, for a script with activeWorker's script url and activeWorker's environment settings object.
    3. 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 ServiceWorkerClient object, and isReload initialized to true if event was dispatched with the user's intention for the page reload, and false otherwise, at activeWorker's environment settings object's global object.
    4. 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. Abort these steps.
      2. Let event be the event for which this event listener was invoked.
      3. If event's respond-with entered flag is set, then:
        1. Set respondWithEntered to true.
      4. If event's wait to respond flag is set, then:
        1. Wait until event's wait to respond flag is unset.
        2. If event's respond-with error flag is set, then:
          1. Set handleFetchFailed to true.
        3. Else:
          1. Set response to the value, which the argument passed into the event's respondWith(r) method resolved with.
  21. Wait for the task queued by the previous step to have executed.
  22. If respondWithEntered is false, then:
    1. Return null and run the following substeps in parallel.
    2. If request's context is one of frame, hyperlink, iframe, location, and sharedworker, then:
      1. Invoke Soft Update algorithm, or its equivalent, with registration.
    3. Abort these steps.
  23. If handleFetchFailed is true, then:
    1. Return a network error.
  24. Else:
    1. Return a response represented by response and run the following substeps in parallel.
    2. If request's context is one of frame, hyperlink, iframe, location, and sharedworker, then:
      1. Invoke Soft Update algorithm, or its equivalent, with registration.

Handle Functional Event

Input
registration, a service worker registration
callbackSteps, an algorithm
Output
None
  1. Queue a task to run Handle Functional Event On Scheduled Task algorithm with registration and callbackSteps.

Event loop and task queuing model for this algorithm will be specified.

Handle Functional Event On Scheduled Task

Input
registration, a service worker registration
callbackSteps, an algorithm
Output
None
  1. Assert: a Record with the [[value]] equals to registration is contained in [[ScopeToRegistrationMap]].
  2. Assert: registration's active worker is not null.
  3. Let activeWorker be registration's active worker.
  4. Run a worker, if not running, for a script with activeWorker's script url and activeWorker's environment settings object.
  5. Invoke callbackSteps, defined by the caller of the algorithm which queued this task, with activeWorker's environment settings object's global object as its argument.
  6. Return.

Handle Document Unload

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

Input
document, a document using the service worker registration
Output
None
  1. Run the following steps atomically.
  2. Let registration be the service worker registration used by document.
  3. If registration is null, then:
    1. Abort these steps.
  4. If any other document is using registration as their service worker registration, then:
    1. Abort these steps.
  5. If registration's uninstalling flag is true, then:
    1. Invoke Clear Registration 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
document, a Document
scope, a scope url
Output
promise, a promise
  1. Let promise be a new promise.
  2. Let scope to the result of running the URL serializer on scope with the exclude fragment flag set.
  3. Run the following substeps in parallel:
    1. If the origin of scope does not match document's origin, then:
      1. Reject promise with a "SecurityError" exception.
      2. Abort these steps.
    2. Let registration be the result of running Get Registration algorithm passing scope as the argument.
    3. If registration is null, then:
      1. Resolve promise with false.
      2. Abort these steps.
    4. Set registration's uninstalling flag.
    5. Resolve promise with true.
    6. If no document is using registration as their service worker registration, then:
      1. If registration's uninstalling flag is unset, then:
        1. Abort these steps.
      2. Invoke Clear Registration algorithm passing registration as its argument.
  4. Return promise.

Set Registration

Input
scope, a scope url
Output
registration, a service worker registration
  1. Run the following steps atomically.
  2. Let registration be a new service worker registration whose scope url is set to scope.
  3. Set a newly-created Record {[[key]]: scope, [[value]]: registration} to [[ScopeToRegistrationMap]].
  4. Return registration.

Clear Registration

Input
registration, a service worker registration
Output
None
  1. Run the following steps atomically.
  2. If registration's installing worker is not null, then:
    1. Terminate registration's installing worker.
    2. Run the Update State algorithm passing registration's installing worker and redundant as the arguments.
    3. Set registration's installing worker to null.
    4. The user agent may abort in-flight requests triggered by registration's installing worker.
  3. If registration's waiting worker is not null, then:
    1. Terminate registration's waiting worker.
    2. Run the Update State algorithm passing registration's waiting worker and redundant as the arguments.
    3. Set registration's waiting worker to null.
    4. The user agent may abort in-flight requests triggered by registration's waiting worker.
  4. If registration's active worker is not null, then:
    1. Terminate registration's active worker.
    2. Run the Update State algorithm passing registration's active worker and redundant as the arguments.
    3. Set registration's active worker to null.
    4. The user agent may abort in-flight requests triggered by registration's active worker.
  5. Delete a Record {[[key]], [[value]]} entry of its [[ScopeToRegistrationMap]] where registration's scope url matches entry.[[key]].

Update State

Input
worker, a service worker
state, a service worker's state
Output
None
  1. Set worker's state to state.
  2. Let serviceWorkers be an array containing all the ServiceWorker objects associated with worker for all the browsing contexts' Document objects and the ServiceWorkerGlobalScope object represented by worker.
  3. For each serviceWorker in serviceWorkers:
    1. Queue a task to fire a simple event named statechange at serviceWorker.

Match Scope

Input
scope, a scope url
Output
registration, a service worker registration
  1. Run the following steps atomically.
  2. Let matchingScope be the longest [[key]] in [[ScopeToRegistrationMap]] starting with the value of scope.
  3. Let registration be the result of running Get Registration algorithm passing matchingScope as the argument.
  4. If registration is not null and registration's uninstalling flag is set, then:
    1. Return null.
  5. Return registration.

Get Registration

Input
scope, a scope url
Output
registration, a service worker registration
  1. Run the following steps atomically.
  2. Let registration be null.
  3. For each Record {[[key]], [[value]]} entry of its [[ScopeToRegistrationMap]]:
    1. If scope matches entry.[[key]], then:
      1. Set registration to entry.[[value]].
  4. Return registration.

Get Newest Worker

Input
registration, a service worker registration
Output
worker, a service worker
  1. Run the following steps atomically.
  2. Let newestWorker be null.
  3. If registration's installing worker is not null, then:
    1. Set newestWorker to registration's installing worker.
  4. Else if registration's waiting worker is not null, then:
    1. Set newestWorker to registration's waiting worker.
  5. Else if registration's active worker is not null, then:
    1. Set newestWorker to registration's active worker.
  6. Return newestWorker.

Query Cache

Input
request, a Request object
options, a CacheQueryOptions 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 resultArray be an empty array.
  4. If this throws an exception, then:
    1. Rethrow that exception.
    2. Abort these steps.
  5. If options.ignoreMethod is false and request.method is neither "GET" nor "HEAD", then:
    1. Return resultArray.
  6. For each Record {[[key]], [[value]]} entry of its [[RequestToResponseMap]], in key insertion order:
    1. Let cachedURL be entry.[[key]]'s associated request's url.
    2. Let requestURL be request's associated request's url.
    3. If options.ignoreSearch is true, then:
      1. Set cachedURL's query to the empty string.
      2. Set requestURL's query to the empty string.
    4. Let serializedCachedURL be serialized cachedURL.
    5. Let serializedRequestURL be serialized requestURL.
    6. If options.prefixMatch is true, then:
      1. Set serializedCachedURL to the substring of itself from the start, with the length of serializedRequestURL.
    7. If serializedCachedURL matches serializedRequestURL, then:
      1. Add entry.[[key]] to requestArray.
      2. Add entry.[[value]] to responseArray.
  7. 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 options.ignoreVary is true, then:
      1. Add an array [cachedRequest, cachedResponse] to resultArray.
      2. Continue to the next iteration of the loop.
    3. Let varyHeaders be the array containing the elements corresponding to the field-values of the Vary header.
    4. Let matchFailed be false.
    5. 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. Set matchFailed to true.
        2. Break the loop.
    6. If matchFailed is false, then:
      1. Add an array [cachedRequest, cachedResponse] to resultArray.
  8. Return resultArray.

Batch Cache Operations

Input
operations, an array of CacheBatchOperation dictionary objects
Output
q, a promise resolves with an array of Response objects.
  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 substeps, onFulfilled, in parallel:
    1. Let itemsCopy be a new [[RequestToResponseMap]] object that is a copy of its context object's [[RequestToResponseMap]] object.
    2. 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 operation.type matches neither "delete" nor "put", then:
          1. Throw a TypeError.
        2. If operation.type matches "delete" and operation.response is not null, then:
          1. Throw a TypeError.
        3. Let requestResponseArray be the result of running Query Cache algorithm passing operation.request and operation.options as the arguments.
        4. For each requestResponse in requestResponseArray:
          1. If the result of running Query Cache algorithm passing requestResponse[0] as the argument is not an empty array, then:
            1. Throw an "InvalidStateError" exception.
          2. For each Record {[[key]], [[value]]} entry of its [[RequestToResponseMap]], in key insertion order:
            1. If requestResponse[0] matches entry.[[key]], then:
              1. Delete entry.
        5. If operation.type matches "put", then:
          1. If operation.response is null, then:
            1. Throw a TypeError.
          2. Let r be operation.request's associated request.
          3. If r's url's scheme is not one of "http" and "https", then:
            1. Throw a TypeError.
          4. If r's method is not `GET`, then:
            1. Throw a TypeError.
          5. If operation.options is not null, then:
            1. Throw a TypeError.
          6. Set a newly-created Record {[[key]]: request, [[value]]: operation.response} to [[RequestToResponseMap]].
        6. Add operation.response to resultArray.
      3. Resolve q with resultArray.
    3. And then, if an exception was thrown, then:
      1. Set the context object's [[RequestToResponseMap]] to itemsCopy.
      2. Reject q with the exception
  4. Return q.

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, Devdatta Akhawe, Dominic Cooney, Jeffrey Yasskin, Joshua Bell, Boris Zbarsky, Matt Falkenhagen, Tobie Langel, and Gavin Peters.

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, Alex Komoroske, and Wonsuk Lee for their considerable professional support.