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, <>
Jake Archibald, Google, <>

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

This section is non-normative.

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 has an associated id (an opaque string), which uniquely identifies itself during the lifetime of its containing service worker registration.

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

A service worker has an associated script resource, which represents its own script resource. It is initially set to null.

A service worker has an associated script resource map which is a List of the Record {[[key]], [[value]]} where [[key]] is a URL and [[value]] is a script resource.

A service worker has an associated skip waiting flag. Unless stated otherwise it is unset.

A service worker has an associated imported scripts updated flag. It is initially unset.

A service worker has an associated set of event types to handle whose element type is an event listener's event type. It is initially set to null.

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 or detects abnormal operation such as infinite loops and tasks exceeding imposed time limits, if any, while handling the events.

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. The user agents may enable many service worker registrations at a single origin so long as the scope url of the service worker registration differs. A 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.

A service worker registration has an associated last update check time. It is initially set to null.

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

A service worker registration has one or more task queues that back up the tasks, whose task source is either the handle fetch task source or the handle functional event task source, from its active worker's event loop's corresponding task queues when the user agent terminates the active worker and re-queue those tasks to the active worker's event loop's corresponding task queues when the active worker spins off. Unlike the task queues owned by event loops, the service worker registration's task queues are not processed by any event loops in and of itself.

Lifetime

The user agents must persistently keep a list of registered service worker registrations unless otherwise they are explicitly unregistered. The user agent has a scope to registration map that stores the entries of the tuple of service worker registration's scope url and the corresponding service worker registration. 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.

Service Worker Client

A service worker client is an environment settings object that specifies various settings for its JavaScript global environment.

A service worker client has an associated active worker (an active worker) which currently controls it. It is initially set to null.

A service worker client has an associated id (an opaque string), which uniquely identifies itself during its lifetime. It is initially set to a new unique value when the corresponding environment settings object that it represents is created.

A service worker client has an associated frame type, which is one of auxiliary, top-level, nested, and none. Unless stated otherwise it is none.

A window client is a service worker client whose global object is a Window object.

A dedicated worker client is a service worker client whose global object is a DedicatedWorkerGlobalObject object.

A shared worker client is a service worker client whose global object is a SharedWorkerGlobalObject object.

A worker client is either a dedicated worker client or a shared worker client.

Selection and Use

A service worker client independently selects and uses a service worker registration for its own loading and its subresources. The selection of a service worker registration, upon a non-subresource request, is a process of either matching a service worker registration from scope to registration map or inheriting an existing service worker registration from its parent or owner context depending on the request's url.

When the request's url is not local, a service worker client matches a service worker registration from scope to registration map. That is, the service worker client attempts to consult a service worker registration whose scope url matches its creation url.

When the request's url is local, if the service worker client's responsible browsing context is a nested browsing context or the service worker client is a worker client, the service worker client inherits the service worker registration from its parent browsing context's environment or one of the worker's Documents' environment, respectively, if it exists.

If the selection was successful, the selected service worker registration's active worker starts to control the service worker client. Otherwise, the flow returns to fetch where it falls back to the default behavior. When a service worker client is controlled by an active worker, it is considered the service worker client is using the active worker's containing service worker registration.

Task sources

The following additional task sources are used by service workers.

The handle fetch task source
This task source is used for dispatching fetch events to service workers.
The handle functional event task source
This task source is used for features that dispatch other functional events, e.g. push events, to service workers.

The user agents may use a separate task source for each functional event type in order to avoid a head-of-line blocking phenomenon for certain functional events. For instance, the user agents may use a different task source for task events from other task sources.

Client Context

Example: Bootstrapping with a ServiceWorker

// scope defaults to the path the script sits in // "/" in this example navigator.serviceWorker.register("/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); });

ServiceWorker

[Exposed=(Window,Worker)] interface ServiceWorker : EventTarget { readonly attribute USVString scriptURL; readonly attribute ServiceWorkerState state; void postMessage(any message, optional sequence<Transferable> transfer); // event attribute EventHandler onstatechange; }; ServiceWorker implements AbstractWorker; 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 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.

A ServiceWorker object has an associated service worker client (a service worker client).

scriptURL

The scriptURL attribute must return the 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 the 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, or the current service worker is being discarded due to an install failure.

postMessage(message, transfer)

The postMessage(message, transfer) method must run these steps or their equivalent:

  1. If the state attribute value of the context object is "redundant", throw an "InvalidStateError" exception and abort these steps.
  2. Let newPorts be an empty array.
  3. Let transferMap be an empty association list of Transferable objects to placeholder objects.
  4. If the method was invoked with a second argument transfer, run these substeps:
    1. If any object is listed in transfer more than once, or any of the Transferable objects listed in transfer are marked as neutered, then throw a "DataCloneError" exception and abort these steps.
    2. For each object x in transfer in turn, add a mapping from x to a new unique placeholder object created for x to transferMap, and if x is a MessagePort object, also append the placeholder object to newPorts.
  5. Let clonedMessage be a structured clone of message with transferMap as the transferMap. If this throws an exception, rethrow that exception and abort these steps.
  6. Let serviceWorker be the service worker represented by the context object.
  7. Invoke Run Service Worker algorithm with serviceWorker as the arguement.
  8. Let destination be the ServiceWorkerGlobalScope object associated with serviceWorker.
  9. If the method was invoked with a second argument transfer, run these substeps:
    1. Let newOwner be the destination's environment settings object.
    2. For each object x in transfer in turn, obtain a new object y by transferring the object x to newOwner, and replace the placeholder object that was created for the object x by the new object y wherever the placeholder exists (i.e. in clonedMessage and in newPorts).
  10. Make newPorts into a read only array.
  11. Queue a task that runs the following steps:
    1. Create an event e that uses the ExtendableMessageEvent interface, with the event type message, which does not bubble, is not cancelable, and has no default action.
    2. Let the data attribute of e be initialized to clonedMessage.
    3. Let the origin attribute of e be initialized to the Unicode serialisation of the origin specified by the incumbent settings object.
    4. If the global object globalObject specified by the incumbent settings object is a ServiceWorkerGlobalScope object, let the source attribute of e be initialized to a new ServiceWorker object that represents globalObject's service worker.
    5. Else if globalObject is a Window object, let the source attribute of e be initialized to a new WindowClient object that represents globalObject's browsing context.
    6. Else, let it be initialized to a new Client object that represents globalObject's worker environment.
    7. Let the ports attribute of e be initialized to newPorts.
    8. Dispatch e at destination.

    The task must use the DOM manipulation task source.

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

[Exposed=(Window,Worker)] interface ServiceWorkerRegistration : EventTarget { [Unforgeable, SameObject] readonly attribute ServiceWorker? installing; [Unforgeable, SameObject] readonly attribute ServiceWorker? waiting; [Unforgeable, SameObject] readonly attribute ServiceWorker? active; readonly attribute USVString scope; [NewObject] Promise<void> update(); [NewObject] 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 (a service worker registration). Multiple separate objects implementing the ServiceWorkerRegistration interface across document environments and worker environments can all be associated with the same service worker registration simultaneously.

A ServiceWorkerRegistration object has an associated service worker client (a service worker client).

installing

installing attribute must run these steps or their equivalent:

  1. Let installingWorker be a ServiceWorker object that represents the service worker registration's installing worker.
  2. Set installingWorker's service worker client to the service worker client.
  3. Return installingWorker.

waiting

waiting attribute must run these steps or their equivalent:

  1. Let waitingWorker be a ServiceWorker object that represents the service worker registration's waiting worker.
  2. Set waitingWorker's service worker client to the service worker client.
  3. Return waitingWorker.

active

active attribute must run these steps or their equivalent:

  1. Let activeWorker be a ServiceWorker object that represents the service worker registration's active worker.
  2. Set activeWorker's service worker client to the service worker client.
  3. Return activeWorker.

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/".

update()

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

  1. Let registration be the service worker registration.
  2. If registration's uninstalling flag is set, abort these steps.
  3. Let newestWorker be the result of running Get Newest Worker algorithm passing registration as its argument.
  4. If newestWorker is null, return a promise rejected with an "InvalidStateError" exception.
  5. Set registration's registering script url to newestWorker's script url.
  6. Let p be the result of running Update algorithm, or its equivalent, passing the service worker client client and registration as the arguments.
  7. Return the result of transforming p with:
    1. A fulfillment handler that returns undefined.
    2. A rejection handler that, when called with argument e, throw e.

unregister()

The unregister() method unregisters the service worker registration. It is important to note that the currently controlled service worker client's active worker's containing service worker registration is effective until all the service worker clients (including itself) using this service worker registration unload. That is, the unregister() method only affects subsequent navigations.

unregister() method must return the result of running these steps or their equivalent:

  1. Let scopeURL be the scope url of the service worker registration.
  2. Return the result of running the Unregister algorithm, or its equivalent, passing the service worker client client, and scopeURL 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 { [SameObject] readonly attribute ServiceWorkerContainer serviceWorker; }; partial interface WorkerNavigator { [SameObject] readonly attribute ServiceWorkerContainer serviceWorker; };

The serviceWorker attribute must return the ServiceWorkerContainer object that is associated with the context object.

ServiceWorkerContainer

[Exposed=(Window,Worker)] interface ServiceWorkerContainer : EventTarget { [Unforgeable, SameObject] readonly attribute ServiceWorker? controller; [SameObject] readonly attribute Promise<ServiceWorkerRegistration> ready; [NewObject] Promise<ServiceWorkerRegistration> register(USVString scriptURL, optional RegistrationOptions options); [NewObject] Promise<ServiceWorkerRegistration> getRegistration(optional USVString clientURL = ""); [NewObject] Promise<sequence<ServiceWorkerRegistration>> getRegistrations(); // events attribute EventHandler oncontrollerchange; attribute EventHandler onerror; attribute EventHandler onmessage; // event.source of message events is ServiceWorker object }; dictionary RegistrationOptions { USVString scope; };

The user agents must create a ServiceWorkerContainer object when a Navigator object or a WorkerNavigator is created and associate it with that object.

A ServiceWorkerContainer provides capabilities to register, unregister, and update the service worker registrations, and provides access to the state of the service worker registrations and their associated service workers.

A ServiceWorkerContainer has an associated service worker client, which is a service worker client whose global object is associated with the Navigator object or the WorkerNavigator object that the ServiceWorkerContainer is retrieved from.

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 context object's service worker client's active worker.

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 clientURL be the context object's service worker client's creation url.
  5. Run the following substeps in parallel:
    1. CheckRegistration: If the result of running Match Service Worker Registration algorithm, or its equivalent, with clientURL as its argument is not null, then:
      1. Set registration to the result value.
    2. Else:
      1. Wait until scope to registration map has a new entry.
      2. Jump to the step labeled CheckRegistration.
    3. If registration's active worker is null, then:
      1. Wait until registration's active worker changes.

      Implementers should consider this condition is met when the corresponding registration request gets to the step 10 of Activate algorithm.

    4. Resolve context object's ready promise with a ServiceWorkerRegistration object, setting its service worker client to service worker client, which represents 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 scriptURL be the result of parsing scriptURL with entry settings object's API base URL.
  2. If scriptURL is failure, return a promise rejected with a TypeError.
  3. If any of the strings in scriptURL's path contains either ASCII case-insensitive "%2f" or ASCII case-insensitive "%5c", return a promise rejected with a TypeError.
  4. Let scopeURL be null.
  5. If the optional argument options is omitted or options.scope is not present, set scopeURL to the result of parsing a string "./" with scriptURL.

    The scope url for the registration is set to the location of the service worker script by default.

  6. Else, set scopeURL to the result of parsing options.scope with entry settings object's API base URL.
  7. If scopeURL is failure, return a promise rejected with a TypeError.
  8. If any of the strings in scopeURL's path contains either ASCII case-insensitive "%2f" or ASCII case-insensitive "%5c", return a promise rejected with a TypeError.
  9. Return the result of running the Register algorithm, or its equivalent, passing the service worker client client, scriptURL, scopeURL as the arguments.

getRegistration(clientURL)

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

  1. Let clientURL be the result of parsing clientURL with entry settings object's API base URL.
  2. If clientURL is failure, return a promise rejected with a TypeError.
  3. If the origin of clientURL is not the context object's service worker client's origin, return a promise with a "SecurityError" exception.
  4. Let promise be a new promise.
  5. Run the following substeps in parallel:
    1. Let registration be the result of running Match Service Worker Registration algorithm, or its equivalent, with clientURL as its argument.
    2. If registration is not null, then:
      1. Resolve promise with the ServiceWorkerRegistration object, setting its service worker client to service worker client, which represents registration.
    3. Else:
      1. Resolve promise with undefined.
  6. 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. For each Record {[[key]], [[value]]} entry of its scope to registration map:
      1. If the origin of the result of parsing entry.[[key]] is its service worker client's origin, then:
        1. Add the ServiceWorkerRegistration object, setting its service worker client to service worker client, associated with entry.[[value]] to the array.
    3. 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
onmessage message

ServiceWorkerMessageEvent

[Constructor(DOMString type, optional ServiceWorkerMessageEventInit eventInitDict), Exposed=(Window,Worker)] interface ServiceWorkerMessageEvent : Event { readonly attribute any data; readonly attribute DOMString origin; readonly attribute DOMString lastEventId; [SameObject] readonly attribute (ServiceWorker or MessagePort)? source; [SameObject] readonly attribute MessagePort[]? ports; }; dictionary ServiceWorkerMessageEventInit : EventInit { any data; DOMString origin; DOMString lastEventId; (ServiceWorker or MessagePort)? source; sequence<MessagePort> ports; };

Service workers define the message event that extends the message event defined in HTML to allow setting a ServiceWorker object as the source of the message. For the message event, service workers use the ServiceWorkerMessageEvent interface.

event.data

The data attribute must return the value it was initialized to. When the object is created, this attribute must be initialized to null. It represents the message being sent.

event.origin

The origin attribute must return the value it was initialized to. When the object is created, this attribute must be initialized to the empty string. It represents the origin of the service worker's environment settings object from which the message is sent.

event.lastEventId

The lastEventId attribute must return the value it was initialized to. When the object is created, this attribute must be initialized to the empty string.

event.source

The source attribute must return the value it was initialized to. When the object is created, this attribute must be initialized to null. It represents the ServiceWorker object whose associated service worker the message is sent from.

When navigator.connect() API extends the service worker communication model, this attribute may also represent ServicePort object from which the message is sent.

event.ports

The ports attribute must return the value it was initialized to. When the object is created, this attribute must be initialized to null. It represents the MessagePort array being sent, if any.

Events

The following event is dispatched on ServiceWorker object:

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

The following event is dispatched on ServiceWorkerRegistration object:

Event name Interface Dispatched when…
updatefound Event The service worker registration's installing worker changes. (See step 8 of the Install algorithm).

The following events are dispatched on ServiceWorkerContainer object:

Event name Interface Dispatched when…
controllerchange Event The service worker client's active worker changes. (See step 12.2 of the Activate algorithm. The skip waiting flag of a service worker causes activation of the service worker registration to occur while service worker clients are using the service worker registration, navigator.serviceWorker.controller immediately reflects the active worker as the service worker that controls the service worker client.)
message ServiceWorkerMessageEvent When it receives a message.
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( // Open a cache of resources. caches.open("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 service worker until it // successfully installs and activates. // 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 || fetch(e.request); }).catch(function() { return caches.match("/fallback.html"); }) ); });

ServiceWorkerGlobalScope

[Global=(Worker,ServiceWorker), Exposed=ServiceWorker] interface ServiceWorkerGlobalScope : WorkerGlobalScope { // A container for a list of Client objects that correspond to // browsing contexts (or shared workers) that are on the origin of this SW [SameObject] readonly attribute Clients clients; [SameObject] readonly attribute ServiceWorkerRegistration registration; [NewObject] Promise<void> skipWaiting(); attribute EventHandler oninstall; attribute EventHandler onactivate; attribute EventHandler onfetch; // event attribute EventHandler onmessage; // event.source of the message events is Client object // close() method inherited from WorkerGlobalScope should not be accessible. };

A ServiceWorkerGlobalScope object represents the global execution context of a service worker. A ServiceWorkerGlobalScope object has an associated service worker (a service worker). A ServiceWorkerGlobalScope object has an associated importscripts allowed flag. It is initially unset.

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.

clients

clients attribute must return the Clients object that is associated with the context object.

registration

The registration attribute must return the ServiceWorkerRegistration object that represents the service worker's containing service worker registration.

skipWaiting()

The skipWaiting() method allows this service worker to progress from the registration's waiting position to active even while service worker clients are using the registration.

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

  1. Let promise be a new promise.
  2. Run the following substeps in parallel:
    1. Set service worker's skip waiting flag
    2. If service worker's state is installed, then:
      1. Run Activate algorithm, or its equivalent, passing service worker's registration as the argument.
    3. Resolve promise with undefined.
  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 ServiceWorkerGlobalScope interface:

event handler event handler event type
oninstall install
onactivate activate
onfetch fetch
onmessage message

Client

[Exposed=ServiceWorker] interface Client { readonly attribute USVString url; readonly attribute FrameType frameType; readonly attribute DOMString id; void postMessage(any message, optional sequence<Transferable> transfer); }; [Exposed=ServiceWorker] interface WindowClient : Client { readonly attribute VisibilityState visibilityState; readonly attribute boolean focused; [NewObject] Promise<WindowClient> focus(); [NewObject] Promise<WindowClient> navigate(USVString url); }; enum FrameType { "auxiliary", "top-level", "nested", "none" };

A Client object has an associated service worker client (a service worker client).

A WindowClient object has an associated visibility state, which is one of visibilityState attribute value.

A WindowClient object has an associated focus state, which is either true or false. (Initially false).

url

The url attribute must return the context object's associated service worker client's serialized creation url.

frameType

The frameType attribute must return the value (in FrameType enumeration) corresponding to the first matching statement, switching on service worker client's frame type:

auxiliary
"auxiliary"

The window client's global object's browsing context is an auxiliary browsing context.

top-level
"top-level"

The window client's global object's browsing context is a top-level browsing context.

nested
"nested"

The window client's global object's browsing context is a nested browsing context.

none
"none"

id

The id attribute must return its associated service worker client's id.

postMessage(message, transfer)

The postMessage(message, transfer) method must run these steps or their equivalent:

  1. Let newPorts be an empty array.
  2. Let transferMap be an empty association list of Transferable objects to placeholder objects.
  3. If the method was invoked with a second argument transfer, run these substeps:
    1. If any object is listed in transfer more than once, or any of the Transferable objects listed in transfer are marked as neutered, then throw a "DataCloneError" exception and abort these steps.
    2. For each object x in transfer in turn, add a mapping from x to a new unique placeholder object created for x to transferMap, and if x is a MessagePort object, also append the placeholder object to newPorts.
  4. Let clonedMessage be a structured clone of message with transferMap as the transferMap. If this throws an exception, rethrow that exception and abort these steps.
  5. Let destination be the ServiceWorkerContainer object whose service worker client is the context object's service worker client.
  6. If destination is null, throw an "InvalidStateError" exception.
  7. If the method was invoked with a second argument transfer, run these substeps:
    1. Let newOwner be the destination's service worker client.
    2. For each object x in transfer in turn, obtain a new object y by transferring the object x to newOwner, and replace the placeholder object that was created for the object x by the new object y wherever the placeholder exists (i.e. in clonedMessage and in newPorts).
  8. Make newPorts into a read only array.
  9. Queue a task that runs the following steps:
    1. Create an event e that uses the ServiceWorkerMessageEvent interface, with the event type message, which does not bubble, is not cancelable, and has no default action.
    2. Let the data attribute of e be initialized to clonedMessage.
    3. Let the origin attribute of e be initialized to the Unicode serialisation of the origin specified by the incumbent settings object.
    4. Let the source attribute of e be initialized to a ServiceWorker object, setting its service worker client to destination's service worker client, which represents the service worker associated with the global object specified by the incumbent settings object.
    5. Let the ports attribute of e be initialized to newPorts.
    6. Dispatch e at destination.

    The task must use the DOM manipulation task source, and, for those where the event loop specified by the target ServiceWorkerContainer object's service worker client is a browsing context event loop, must be associated with the responsible document specified by that target ServiceWorkerContainer object's service worker client.

visibilityState

The visibilityState attribute must return the context object's visibility state.

focused

The focused attribute must return the context object's focus state.

focus()

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

  1. If this algorithm is not allowed to show a popup, return a promise rejected with an "InvalidAccessError" exception.
  2. Let promise be a new promise.
  3. Run these steps in parallel:
    1. Let browsingContext be the context object's associated service worker client's global object's browsing context.
    2. Queue a task to run the following substeps on the context object's associated service worker client's responsible event loop using the user interaction task source:
      1. Run the focusing steps with browsingContext.
      2. Let windowClient be the result of running Capture Window Client algorithm, or its equivalent, with browsingContext as the argument.
      3. If windowClient's focus state is true, resolve promise with windowClient.
      4. Else, reject promise with a TypeError.
  4. Return promise.

navigate(url)

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

  1. Let url be the result of parsing url with entry settings object's API base URL.
  2. If url is failure, return a promise rejected with a TypeError.
  3. If url is about:blank, return a promise rejected with a TypeError.
  4. If the context object's associated service worker client's active worker is not the incumbent settings object's global object's service worker, return a promise rejected with a TypeError.
  5. Let promise be a new promise.
  6. Run these steps in parallel:
    1. Let browsingContext be the context object's associated service worker client's global object's browsing context.
    2. If browsingContext has discarded its Document, reject promise with a TypeError and abort these steps.
    3. Queue a task to run the following substeps on the context object's associated service worker client's responsible event loop using the user interaction task source:
      1. HandleNavigate: Navigate browsingContext to url with replacement enabled and exceptions enabled. The source browsing context must be browsingContext.
      2. If the algorithm steps invoked in the step labeled HandleNavigate navigateSteps throws an exception, reject promise with that exception and abort these steps.
      3. After navigateSteps has run to completion (including its asynchronous steps), if browsingContext's Window object's environment settings object's creation url's origin is not the same as the service worker's origin, then:
        1. Resolve promise with null.
        2. Abort these steps.
      4. Let client be the result of running Capture Window Client algorithm, or its equivalent, with browsingContext as the argument.
      5. Resolve promise with client.
  7. Return promise.

Clients

[Exposed=ServiceWorker] interface Clients { // The objects returned will be new instances every time [NewObject] Promise<sequence<Client>> matchAll(optional ClientQueryOptions options); [NewObject] Promise<WindowClient?> openWindow(USVString url); [NewObject] Promise<void> claim(); }; dictionary ClientQueryOptions { boolean includeUncontrolled = false; ClientType type = "window"; }; enum ClientType { "window", "worker", "sharedworker", "all" };

The user agents must create a Clients object when a ServiceWorkerGlobalScope object is created and associate it with that object.

matchAll(options)

The matchAll(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 omitted, then:
      1. For each service worker client client whose active worker is the associated service worker, in the most recently focused order for window clients:
        1. If client is a window client, then:
          1. Let windowClient be the result of running Capture Window Client algorithm, or its equivalent, with client's responsible browsing context as the argument.
          2. Add windowClient to clients.
      2. Resolve promise with clients.
    3. Else:
      1. Let targetClients be an empty array.
      2. For each service worker client client whose origin is the same as the associated service worker's origin:
        1. If options.includeUncontrolled is false, then:
          1. If client's active worker is the associated service worker, add client to targetClients.
        2. Else:
          1. Add client to targetClients.
      3. Let matchedClients be an empty array.
      4. For each service worker client client in targetClients, in the most recently focused order for window clients:
        1. If options.type is "window", and client is a window client, then:
          1. Let windowClient be the result of running Capture Window Client algorithm, or its equivalent, with client's responsible browsing context as the argument.
          2. Add windowClient to matchedClients.
        2. Else if options.type is "worker" and client is a dedicated worker client, add a new Client object that represents client to matchedClients.
        3. Else if options.type is "sharedworker" and client is a shared worker client, add a new Client object that represents client to matchedClients.
        4. Else if options.type is "all", then:
          1. If client is a window client, then:
            1. Let windowClient be the result of running Capture Window Client algorithm, or its equivalent, with client's responsible browsing context as the argument.
            2. Add windowClient to matchedClients.
          2. Else, add a new Client object that represents client to matchedClients.
      5. Resolve promise with matchedClients.
  3. Return promise.

openWindow(url)

The openWindow(url) method must run these steps or their equivalent:

  1. Let url be the result of parsing url with entry settings object's API base URL.
  2. If url is failure, return a promise rejected with a TypeError.
  3. If url is about:blank, return a promise rejected with a TypeError.
  4. If this algorithm is not allowed to show a popup, return a promise rejected with an "InvalidAccessError" exception.
  5. Let promise be a new promise.
  6. Run these steps in parallel:
    1. Let newContext be a new top level browsing context.
    2. Queue a task to run the following substeps on newContext's Window object's environment settings object's responsible event loop using the user interaction task source:
      1. HandleNavigate: Navigate newContext to url, with exceptions enabled and replacement enabled.
      2. If the algorithm steps invoked in the step labeled HandleNavigate navigateSteps throws an exception, reject promise with that exception and abort these steps.
      3. After navigateSteps has run to completion (including its asynchronous steps), if newContext's Window object's environment settings object's creation url's origin is not the same as the service worker's origin, then:
        1. Resolve promise with null.
        2. Abort these steps.
      4. Let client be the result of running Capture Window Client algorithm, or its equivalent, with newContext as the argument.
      5. Resolve promise with client.
  7. Return promise.

claim()

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

  1. If the service worker is not an active worker, return a promise rejected with an "InvalidStateError" exception.
  2. Let promise be a new promise.
  3. Run the following substeps in parallel:
    1. For each service worker client client whose origin is the same as the service worker's origin:
      1. Let registration be the result of running Match Service Worker Registration algorithm passing client's creation url as the argument.
      2. If registration is not the service worker's containing service worker registration, continue to the next iteration of the loop.
      3. If client's active worker is not the service worker, then:
        1. Invoke Handle Service Worker Client Unload with client as the argument.
        2. Set client's active worker to service worker.
        3. Invoke Notify Controller Change algorithm with client as the argument.
    2. Resolve promise with undefined.
  4. Return promise.

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 };

An ExtendableEvent object has an associated extend lifetime promises (an array of promises). It is initially set to null.

Service workers have two lifecycle events, install and activate. Service workers use the ExtendableEvent interface for activate event and install event.

Service worker extensions that define event handlers may also use or extend the ExtendableEvent interface.

event.waitUntil(f)

waitUntil(f) method extends the lifetime of the event.

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

  1. If the dispatch flag is unset, then:
    1. Throw an "InvalidStateError" exception.
    2. Abort these steps.
  2. Add f to extend lifetime promises.

In the task task in which the steps of waitUntil(f) is running, the user agent must run these steps or their equivalent:

  1. Let extendLifetimePromises be an empty array.
  2. For each event listener invoked:
    1. Let eventObject be the first argument passed to this event listener.
    2. Append eventObject's extend lifetime promises to extendLifetimePromises.
  3. Do not terminate the service worker whose responsible event loop is running task until waiting for all of extendLifetimePromises settles.

However, the user agent may impose a time limit to this lifetime extension.

Service workers and extensions that define event handlers may define their own behaviours, allowing the extend lifetime promises to suggest operation length, and the rejected state of any of the promise in extend lifetime promises to suggest operation failure.

Service workers define the following behaviours for install event and activate event:

FetchEvent

[Constructor(DOMString type, optional FetchEventInit eventInitDict), Exposed=ServiceWorker] interface FetchEvent : ExtendableEvent { [SameObject] readonly attribute Request request; [SameObject] readonly attribute Client client; readonly attribute boolean isReload; void respondWith(Promise<Response> r); }; dictionary FetchEventInit : ExtendableEventInit { Request request; Client client; boolean isReload = false; };

Service workers have an essential functional event fetch. For fetch event, service workers use the FetchEvent interface which extends the ExtendableEvent interface.

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

event.request

request attribute must return the value it was initialized to.

event.client

client attribute must return the value it was initialized to.

event.isReload

isReload attribute must return the value it was initialized to. When an event is created the attribute must be initialized to false.

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.

event.respondWith(r)

Developers can set the argument r with either a promise that resolves with a Reponse object or a Reponse object (which is automatically cast to a promise). Otherwise, a network error is returned to Fetch. Renderer-side security checks about tainting for cross-origin content are tied to the types of filtered responses defined in Fetch.

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

  1. If the dispatch flag is unset, then:
    1. Throw an "InvalidStateError" exception.
    2. Abort these steps.
  2. If the respond-with entered flag is set, then:
    1. Throw an "InvalidStateError" exception.
    2. Abort these steps.
  3. Set the stop propagation flag and stop immediate propagation flag.
  4. Set the respond-with entered flag.
  5. Set the wait to respond flag.
  6. 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 a Response object, then:
        1. If response's body is non-null, then:
          1. If response's used flag is set, set the respond-with error flag.
          2. Set response's used flag.
      2. Else:
        1. Set the respond-with error flag.

        If the respond-with error flag is set, a network error is returned to Fetch through Handle Fetch algorithm. (See the step 21.1.) Otherwise, the value response is returned to Fetch through Handle Fetch algorithm. (See the step 22.1.)

    4. Unset the wait to respond flag.

ExtendableMessageEvent

[Constructor(DOMString type, optional ExtendableMessageEventInit eventInitDict), Exposed=ServiceWorker] interface ExtendableMessageEvent : ExtendableEvent { readonly attribute any data; readonly attribute DOMString origin; readonly attribute DOMString lastEventId; [SameObject] readonly attribute (Client or ServiceWorker or MessagePort)? source; [SameObject] readonly attribute MessagePort[]? ports; }; dictionary ExtendableMessageEventInit : ExtendableEventInit { any data; DOMString origin; DOMString lastEventId; (Client or ServiceWorker or MessagePort)? source; sequence<MessagePort> ports; };

Service workers define the extendable message event that extends the message event defined in HTML to allow extending the lifetime of the event. For the message event, service workers use the ExtendableMessageEvent interface which extends the ExtendableEvent interface.

event.data

The data attribute must return the value it was initialized to. When the object is created, this attribute must be initialized to null. It represents the message being sent.

event.origin

The origin attribute must return the value it was initialized to. When the object is created, this attribute must be initialized to the empty string. It represents the origin of the service worker client that sent the message.

event.lastEventId

The lastEventId attribute must return the value it was initialized to. When the object is created, this attribute must be initialized to the empty string.

event.source

The source attribute must return the value it was initialized to. When the object is created, this attribute must be initialized to null. It represents the Client object from which the message is sent.

When navigator.connect() API extends the communication model between cross origin service workers, this attribute may also represent ServicePort object from which the message is sent.

event.ports

The ports attribute must return the value it was initialized to. When the object is created, this attribute must be initialized to null. It represents the MessagePort array being sent, if any.

Events

The following events are dispatched on ServiceWorkerGlobalScope object:

Event name Interface Dispatched when…
install ExtendableEvent [Lifecycle event] The service worker's containing service worker registration's installing worker changes. (See step 11.2 of the Install algorithm.)
activate ExtendableEvent [Lifecycle event] The service worker's containing service worker registration's active worker changes. (See step 15.2 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. (A custom Response object can be another option.)
message ExtendableMessageEvent When it receives a message.

Caches

To allow authors to fully manage their content caches for offline use, the Window and the WorkerGlobalScope provide the asynchronous caching methods that open and manipulate Cache objects. An origin can have multiple, named Cache objects, whose contents are entirely under the control of scripts. Caches are not shared across origins, and they are completely isolated from the browser's HTTP cache.

Constructs

A fetching record is a Record {[[key]], [[value]]} where [[key]] is a Request and [[value]] is a Response.

A fetching record has an associated incumbent record (a fetching record). It is initially set to null.

A request to response map is a List of fetching records.

A name to cache map is a List of the Record {[[key]], [[value]]} where [[key]] is a string that represents a name of the Cache object and [[value]] is a Cache object.

Each origin has an associated name to cache map.

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 service worker that can safely operate on.

self.caches

partial interface Window { [SameObject] readonly attribute CacheStorage caches; }; partial interface WorkerGlobalScope { [SameObject] readonly attribute CacheStorage caches; };

caches

caches attribute must return the CacheStorage object that is associated with the context object.

Cache

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

A Cache object represents a request to response map. Multiple separate objects implementing the Cache interface across document environments and worker environments can all be associated with the same request to response map simultaneously.

Cache objects are always enumerable via self.caches in insertion order (per ECMAScript 6 Map objects.)

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 fetching record entry of its request to response map, in key insertion order:
        1. Add a copy of entry.[[value]] to responseArray.
      2. Resolve promise with responseArray.
      3. Abort these steps.
    3. Else:
      1. Let r be null.
      2. If request is a Request object, then:
        1. Set r to request's request.
        2. If r's method is neither `GET` nor `HEAD` and options.ignoreMethod is false, resolve promise with an empty array.
      3. Else if request is a string, 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.
      4. Set r's url's fragment to null.
      5. Let entries be the result of running Query Cache algorithm passing a Request object associated with r and options as the arguments.
      6. For each entry of entries:
        1. Let response be null.
        2. If the incumbent record incumbentRecord of the corresponding fetching record fetchingRecord in request to response map is not null, set response to a copy of incumbentRecord.[[value]].
        3. Else, set response to a copy of entry[1].
        4. If r's method is `HEAD` and options.ignoreMethod is false, set response's body to null.
        5. Add response to responseArray.
      7. 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. Return the result of transforming responseArrayPromise with a fulfillment handler that returns undefined.

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 whose type is Request in requests:
    1. Let r be request's request.
    2. If r's url's scheme is not one of "http" and "https", or r's method is not `GET`, return a promise rejected with a TypeError.
  4. For each request in requests:
    1. Let r be 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.
    2. If r's url's scheme is not one of "http" and "https", then:
      1. Terminate all the ongoing fetches initiated by requests with reason fatal.
      2. Break the loop.
    3. Set r's url's fragment to null.
    4. Set r's initiator to "fetch" and destination to "subresource".
    5. Add a Request object associated with r to requestArray.
    6. Let responsePromise be a new promise.
    7. Run the following in parallel:
      • Fetch r.
      • To process response for response, run these substeps:
        1. If response's type is error, reject responsePromise with a TypeError.
        2. Else if response's header list contains a header named `Vary`, then:
          1. Let varyHeaders be the array containing the elements corresponding to the field-values of the Vary header.
          2. Let matchAsterisk be false.
          3. For each f in varyHeaders:
            1. If f matches "*", set matchAsterisk to true and break the loop.
          4. If matchAsterisk is true, reject responsePromise with a TypeError.
          5. Else, resolve responsePromise with a new Response object associated with response.
        3. Else, resolve responsePromise with a new Response object associated with response.

        This step ensures that the promise for this fetch resolves as soon as the response's headers become available.

      • To process response body for response, do nothing.
      • To process response end-of-file for response, do nothing.
    8. Add responsePromise to responsePromiseArray.
  5. Let p be waiting for all of responsePromiseArray.
  6. Return the result of transforming p with a fulfillment handler that, when called with argument responseArray, performs the following substeps in parallel:
    1. Let operations be an empty array.
    2. For each response in responseArray with the index index:
      1. If response's body is non-null, then:
        1. If response's used flag is set, throw a TypeError.
        2. Set response's used flag.
      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 requestArray[index].
      5. Set the response dictionary member of o to response.
      6. Add o to operations.
    3. Let resultPromise be the result of running Batch Cache Operations algorithm passing operations as the argument.
    4. Return the result of transforming resultPromise with a fulfillment handler that, when called with argument responses, performs the following substeps in parallel:
      1. Let responseBodyPromiseArray be an empty array.
      2. For each response in responses:
        1. Let responseBodyPromise be a new promise.
        2. Run the following substeps in parallel:
          1. Wait for either end-of-file to have been pushed to response's associated response r's body or for r to have a termination reason.
          2. If r had a termination reason, then:
            1. If the incumbent record incumbentRecord of the corresponding fetching record fetchingRecord in request to response map is not null, then:
              1. Set fetchingRecord in request to response map to the copy of incumbentRecord.
            2. Else:
              1. Delete fetchingRecord from request to response map.
            3. Reject responseBodyPromise with a TypeError.
          3. Else:
            1. Set the incumbent record of the corresponding fetching record fetchingRecord in request to response map to the copy of fetchingRecord.
            2. Let invalidRecords be the result of running Query Cache algorithm passing fetchingRecord.[[key]] as the argument.
            3. For each invalidRecord in invalidRecords:
              1. If invalidRecord is not fetchingRecord, delete it from request to response map.
            4. Resolve responseBodyPromise with response.
        3. Add responseBodyPromise to responseBodyPromiseArray.
      3. Let q be waiting for all of responseBodyPromiseArray.
      4. Return the result of transforming q with a fulfillment handler that returns undefined.

put(request, response)

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

  1. Let r be null.
  2. If request is a Request object, then:
    1. Set r to request's request.
    2. If r's url's scheme is not one of "http" and "https", or r's method is not `GET`, return a promise rejected with a TypeError.
  3. Else if request is a string, 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.
    2. If r's url's scheme is not one of "http" and "https", return a promise rejected with a TypeError.
  4. Set r's url's fragment to null.
  5. If response's associated response's header list contains a header named `Vary`, then:
    1. Let varyHeaders be the array containing the elements corresponding to the field-values of the Vary header.
    2. For each f in varyHeaders:
      1. If f matches "*", return a promise rejected with a TypeError.
  6. If response's body is non-null, then:
    1. If response's used flag is set, return a promise rejected with a TypeError.
    2. Set response's used flag.
  7. Let operations be an empty array.
  8. Let o be an empty object representing a CacheBatchOperation dictionary.
  9. Set the type dictionary member of o to "put".
  10. Set the request dictionary member of o to a Request object associated with r.
  11. Set the response dictionary member of o to response.
  12. Add o to operations.
  13. Let resultPromise be the result of running Batch Cache Operations passing operations as the argument.
  14. Return the result of transforming resultPromise with a fulfillment handler that, when called with argument responses, performs the following substeps in parallel:
    1. Wait for either end-of-file to have been pushed to responses[0]'s associated response r's body or for r to have a termination reason.
    2. If r had a termination reason, then:
      1. If the incumbent record incumbentRecord of the corresponding fetching record fetchingRecord in request to response map is not null, then:
        1. Set fetchingRecord in request to response map to the copy of incumbentRecord.
      2. Else:
        1. Delete fetchingRecord from request to response map.
      3. Throw a TypeError.
    3. Else:
      1. Set the incumbent record of the corresponding fetching record fetchingRecord in request to response map to the copy of fetchingRecord.
      2. Let invalidRecords be the result of running Query Cache algorithm passing fetchingRecord.[[key]] as the argument.
      3. For each invalidRecord in invalidRecords:
        1. If invalidRecord is not fetchingRecord, delete it from request to response map.
      4. Return undefined.

delete(request, options)

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

  1. Let r be null.
  2. If request is a Request object, then:
    1. Set r to request's request.
    2. If r's method is neither `GET` nor `HEAD` and options.ignoreMethod is false, return a promise resolved with false.
  3. Else if request is a string, 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.
  4. Set r's url's fragment to null.
  5. Let operations be an empty array.
  6. Let o be an empty object representing a CacheBatchOperation dictionary.
  7. Set the type dictionary member of o to "delete".
  8. Set the request dictionary member of o to a Request object associated with r.
  9. Set the options dictionary member of o to options.
  10. Add o to operations.
  11. Let resultPromise be the result of running Batch Cache Operations passing operations as the argument.
  12. Return the result of transforming resultPromise with a fulfillment handler, when called with argument responseArray, performs the following substeps in parallel:
    1. If responseArray is not null, return true.
    2. Else, return false.

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 fetching record entry of its request to response map, in key insertion order:
        1. Add entry.[[key]] to resultArray.
    3. Else:
      1. Let r be null.
      2. If request is a Request object, then:
        1. Set r to request's request.
        2. If r's method is neither `GET` nor `HEAD` and options.ignoreMethod is false, resolve promise with an empty array.
      3. Else if request is a string, 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.
      4. Set r's url's fragment to null.
      5. Let requestResponseArray be the result of running Query Cache algorithm passing a Request object that represents r and options as the arguments.
      6. For each requestResponse in requestResponseArray:
        1. Add requestResponse[0] to resultArray.
    4. Resolve promise with resultArray.
  3. Return promise.

CacheStorage

[Exposed=(Window,Worker)] interface CacheStorage { [NewObject] Promise<Response> match(RequestInfo request, optional CacheQueryOptions options); [NewObject] Promise<boolean> has(DOMString cacheName); [NewObject] Promise<Cache> open(DOMString cacheName); [NewObject] Promise<boolean> delete(DOMString cacheName); [NewObject] 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 user agents must create a CacheStorage object when a Window object or a WorkerGlobalScope object is created and associate it with that object.

A CacheStorage object represents a name to cache map of its associated global object's environment settings object's origin. Multiple separate objects implementing the CacheStorage interface across document environments and worker environments can all be associated with the same name to cache map simultaneously.

match(request, options)

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

  1. If the result of running is settings object a secure context with the incumbent settings object is Not Secure, return a new promise rejected with a "SecurityError" exception.
  2. Let cacheName be null.
  3. If the optional argument options is not omitted, then:
    1. Set cacheName to options.cacheName.
  4. If cacheName is not null, then:
    1. Return a new promise p and run the following substeps in parallel:
      1. For each Record {[[key]], [[value]]} entry of its name to cache map, 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 a "NotFoundError" exception.
  5. Else:
    1. Let p be a promise resolved with undefined.
    2. For each Record {[[key]], [[value]]} entry of its name to cache map, in key insertion order:
      1. Set p to the result of transforming itself with a fulfillment handler that, when called with argument v, performs the following substeps in parallel:
        1. If v is not undefined, return v.
        2. Return 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).)
    3. Return p.

has(cacheName)

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

  1. If the result of running is settings object a secure context with the incumbent settings object is Not Secure, return a new promise rejected with a "SecurityError" exception.
  2. Return a promise p resolved with the result of running the following substeps:
    1. For each Record {[[key]], [[value]]} entry of its name to cache map, 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. If the result of running is settings object a secure context with the incumbent settings object is Not Secure, return a new promise rejected with a "SecurityError" exception.
  2. Let p be a new promise.
  3. Run the following substeps:
    1. For each Record {[[key]], [[value]]} entry of its name to cache map, in key insertion order:
      1. If cacheName matches entry.[[key]], then:
        1. Resolve p with a new Cache object which is a copy of 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 name to cache map. If this cache write operation failed due to exceeding the granted quota limit, reject p with a "QuotaExceededError" exception and abort these steps.
    4. Resolve p with cache.
  4. Return p.

delete(cacheName)

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

  1. If the result of running is settings object a secure context with the incumbent settings object is Not Secure, return a new promise rejected with a "SecurityError" exception.
  2. Let p be the result of running the algorithm specified in has(cacheName) method with cacheName as the argument.
  3. Return the result of transforming p with a fulfillment handler that, when called with argument cacheExists, performs the following substeps in parallel:
    1. If cacheExists is true, then:
      1. Delete a Record {[[key]], [[value]]} entry of its name to cache map where cacheName matches entry.[[key]].
      2. Return true.
      3. Abort these steps.

      After this step, the existing DOM objects (i.e. the currently referenced Cache, Request, and Response objects) should remain functional.

    2. Else:
      1. Return false.

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. If the result of running is settings object a secure context with the incumbent settings object is Not Secure, return a new promise rejected with a "SecurityError" exception.
  2. Let resultArray be an empty array.
  3. Return a promise p resolved with the result of running the following substeps:
    1. For each Record {[[key]], [[value]]} entry of its name to cache map, in key insertion order:
      1. Add entry.[[key]] to resultArray.
    2. Return resultArray.

Security Considerations

Secure Context

Service workers should execute in secure contexts, and the controlled service worker clients should also be secure contexts. This effectively means that service workers and their service worker clients should be hosted over HTTPS. The user agents may allow localhost, 127.0.0.0/8, and ::1/128 for development purpose. (Note that they are still secure contexts.) The primary reason for this restriction is to protect users from the risks associated with insecure contexts.

Content Security Policy

Whenever a user agent invokes Run Service Worker algorithm with a service worker serviceWorker:

The primary reason for this restriction is to mitigate a broad class of content injection vulnerabilities, such as cross-site scripting (XSS).

Origin Relativity

Origin restriction

This section is non-normative.

A Service worker executes in the registering service worker client's origin. 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 origins. 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.

importScripts(urls)

When the importScripts(urls) method is called on a ServiceWorkerGlobalScope object, the user agent must import scripts into worker global scope, with the following options:

To validate the state, the user agent must run the following step:

  1. If the settings object's global object's importscripts allowed flag is unset, throw an "InvalidStateError" exception and abort these steps.

To get a fetch result, the user agent must run the following steps:

  1. Let serviceWorker be the settings object's global object's service worker.
  2. If serviceWorker's imported scripts updated flag is unset, then:
    1. Attempt to fetch each resource identified by the resulting absolute URLs, from the origin specified by settings object, using the referrer source specified by settings object, and with the blocking flag set.
  3. Else:
    1. If there exists a corresponding Record record for url in serviceWorker's script resource map, set the script resource to record.[[value]].
    2. Else, set the script resource to null.

To postprocess the fetch result, the user agent must run the following steps:

  1. If serviceWorker's imported scripts updated flag is unset, then:
    1. If the fetching attempt failed (e.g. the server returned a 4xx or 5xx status code or equivalent, or there was a DNS error), throw a "NetworkError" exception and abort all these steps.
    2. Else:
      1. If there exists a corresponding Record record for the resulting absolute URL url in serviceWorker's script resource map, set record.[[value]] to the fetched script resource.
      2. Else, set a newly-created Record {[[key]]: url, [[value]]: the fetched script resource} to serviceWorker's script resource map.
  2. Else, if the script resource is null, throw a "NetworkError" exception and abort all these steps.

Cross-Origin Resources and CORS

This section is non-normative.

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

Service workers 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 whose corresponding responses are basic filtered response, the objects stored are Response objects whose corresponding responses are either CORS filtered responses or opaque filtered responses. They can be passed to event.respondWith(r) method in the same manner as the Response objects whose corresponding responses are basic filtered responses, but cannot be meaningfully created programmatically. These limitations are necessary to preserve the security invariants of the platform. Allowing Caches to store them allows applications to avoid re-architecting in most cases.

Implementer Concerns

This section is non-normative.

The implementers are encouraged to note:

Privacy

Service workers introduce new persistent storage features including scope to registration map (for service worker registrations and their service workers), request to response map and name to cache map (for caches), and script resource map (for script resources). In order to protect users from any potential unsanctioned tracking threat, these persistent storages should be cleared when users intend to clear them and should maintain and interoperate with existing user controls e.g. purging all existing persistent storages.

Storage Considerations

Service workers should take a dependency on Quota Management API that extends the ServiceWorkerGlobalScope with the event listeners onbeforeevicted and onevicted to detect a storage pressure and give pre-eviction information to the application.

The cache write operations in service workers when failed due to exceeding the granted quota limit should throw "QuotaExceededError" exception.

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, or its equivalent, 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 globalObject (a ServiceWorkerGlobalScope object) at which it may fire its functional events. This algorithm is called on a task queued by Handle Functional Event algorithm.

See an example hook defined in Notifications API.

Appendix A: Algorithms

The following definitions are the user agent's internal data structures used throughout the specification.

A scope to registration map is a List of the Record {[[key]], [[value]]} where [[key]] is a string that represents a scope url and [[value]] is a service worker registration.

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.

A registration queue 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.

An installation queue 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.

An installation result handle queue 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.

An activation queue 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
client, a service worker client
scriptURL, a URL
scopeURL, a URL
Output
promise, a promise
  1. If the result of running Is origin potentially trustworthy with the origin of scriptURL as the argument is Not Trusted, then:
    1. Return a promise rejected with a "SecurityError" exception.
  2. If the origin of scriptURL is not client's origin, then:
    1. Return a promise rejected with a "SecurityError" exception.
  3. If the origin of scopeURL is not client's origin, then:
    1. Return a promise rejected with a "SecurityError" exception.
  4. Run the following substeps atomically:
    1. Let registration be the result of running the Get Registration algorithm passing scopeURL 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 equals newestWorker's script url, and scriptURL equals 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 the ServiceWorkerRegistration object, setting its service worker client to client, which represents registration.
    3. Else:
      1. Set registration to the result of running Set Registration algorithm passing scopeURL 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 client and registration as the argument.

Update

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

Input
client, a service worker client
registration, a service worker registration
force bypass cache flag, an optional flag unset by default
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 registration queue, installation queue, and installation result handle queue.
  4. Run the following substeps in parallel:
    1. CheckPriority: If the value of the top element of registration queue is not timeStamp, then:
      1. Wait until the top element of registration queue is popped.
      2. Jump to the step labeled CheckPriority.

      Wait is a blocking wait. Implementers may use a condition variable or its equivalent synchronization primitive.

    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. Let r be the associated request of the result of invoking the initial value of Request as constructor with registration's serialized registering script url. If this throws an exception, then:
      1. Reject p with the exception.
      2. Pop the top element from registration queue, installation queue and installation result handle queue.
      3. If the result of running Get Newest Worker algorithm passing registration as the argument is null, invoke Clear Registration algorithm passing registration as its argument.
      4. Abort these steps.
    4. Set r's initiator to "" and destination to "serviceworker".
    5. Set r's client to client.
    6. Append `Service-Worker`/`script` to r's header list.
    7. Set r's skip service worker flag, r's synchronous flag, and r's redirect mode to "manual".
    8. Let newestWorker be null.
    9. Set newestWorker to the result of running Get Newest Worker algorithm passing registration as the argument.
    10. If newestWorker is not null and registration's last update check time is not null, then:
      1. If the time difference in seconds calculated by the current time minus registration's last update check time is greater than 86400, or force bypass cache flag is set, set r's cache mode to "reload".

      Even if the cache mode is not set to "reload", the user agents obey Cache-Control header's max-age value in the network layer to determine if it should bypass the browser cache.

    11. Let response be the result of running fetch using r.
    12. If response is a network error or response's status is not in the range 200 to 299, then:
      1. Reject p with a TypeError.
      2. Pop the top element from registration queue, installation queue and installation result handle queue.
      3. If the result of running Get Newest Worker algorithm passing registration as the argument is null, invoke Clear Registration algorithm passing registration as its argument.
      4. Abort these steps.
    13. If response's cache state is not "local", set registration's last update check time to the current time.
    14. 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, and application/javascript, then:
      1. Reject p with a "SecurityError" exception.
      2. Pop the top element from registration queue, installation queue and installation result handle queue.
      3. If the result of running Get Newest Worker algorithm passing registration as the argument is null, invoke Clear Registration algorithm passing registration as its argument.
      4. Abort these steps.
    15. Let serviceWorkerAllowed be the result of parsing `Service-Worker-Allowed` in response's header list.
    16. If serviceWorkerAllowed is failure, then:
      1. Reject p with a TypeError.
      2. Pop the top element from registration queue, installation queue and installation result handle queue.
      3. If the result of running Get Newest Worker algorithm passing registration as the argument is null, invoke Clear Registration algorithm passing registration as its argument.
      4. Abort these steps.
    17. Let scopeURL be registration's scope url.
    18. Let maxScopeString be null.
    19. If serviceWorkerAllowed is null, then:
      1. Set maxScopeString to "/" concatenated with the strings, except the last string that denotes the script's file name, in registration's registering script url's path (including empty strings), separated from each other by "/".
    20. Else:
      1. Let maxScope be the result of parsing serviceWorkerAllowed with registration's registering script url.
      2. Set maxScopeString to "/" concatenated with the strings in maxScope's path (including empty strings), separated from each other by "/".
    21. Let scopeString be "/" concatenated with the strings in scopeURL's path (including empty strings), separated from each other by "/".
    22. If scopeString starts with maxScopeString, do nothing.
    23. Else:
      1. Reject p with a "SecurityError" exception.
      2. Pop the top element from registration queue, installation queue and installation result handle queue.
      3. If the result of running Get Newest Worker algorithm passing registration as the argument is null, invoke Clear Registration algorithm passing registration as its argument.
      4. Abort these steps.
    24. Set newestWorker to the result of running the Get Newest Worker algorithm passing registration as the argument.
    25. If newestWorker is not null, newestWorker's script url equals registration's registering script url with the exclude fragments flag set, and response is a byte-for-byte match with the script resource of newestWorker, then:
      1. Resolve p with the ServiceWorkerRegistration object, setting its service worker client to client, which represents registration.
      2. Abort these steps.
    26. Else:
      1. Let worker be a new service worker.
      2. Generate a unique opaque string and set worker's id to the value.
      3. Set worker's script url to registration's registering script url, worker's script resource to the script resource retrieved from the fetched response.
      4. Invoke Run Service Worker algorithm with worker as the arguement.
      5. If an uncaught runtime script error occurs during the above step, then:
        1. Reject p with the error.
        2. Pop the top element from registration queue, installation queue and installation result handle queue.
        3. If the result of running Get Newest Worker algorithm passing registration as its argument is null, invoke Clear Registration algorithm passing registration as its argument.
        4. Abort these steps.
    27. Pop the top element from registration queue.
    28. Invoke Install algorithm, or its equivalent, with client, 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
force bypass cache flag, an optional flag unset by default
Output
None
  1. Let client be null.
  2. If registration's uninstalling flag is set, abort these steps.
  3. If registration's installing worker is not null, abort these steps.
  4. Let newestWorker be the result of running Get Newest Worker algorithm passing registration as its argument.
  5. If newestWorker is null, abort these steps.
  6. Set registration's registering script url to newestWorker's script url.
  7. Invoke Update algorithm, or its equivalent, passing client and registration as its argument with force bypass cache flag set only if its own force bypass cache flag is set.

Install

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

Input
client, a service worker client
registration, a service worker registration
worker, a service worker
registrationPromise, a promise
timeStamp, a timestamp
Output
none
  1. Let installFailed be false.
  2. CheckPriority: If the value of the top element of installation queue is not timeStamp, then:
    1. Wait until the top element of installation queue is popped.
    2. Jump to the step labeled CheckPriority.

    Wait is a blocking wait. Implementers may use a condition variable or its equivalent synchronization primitive.

  3. 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.
  4. Set registration's installing worker to worker.
  5. Run the Update State algorithm passing registration's installing worker and installing as the arguments.
  6. Assert: registrationPromise is not null.
  7. Resolve registrationPromise with the ServiceWorkerRegistration object, setting its service worker client to client, which represents registration.
  8. Queue a task to fire a simple event named updatefound at all the ServiceWorkerRegistration objects that represent registration for all the service worker clients whose creation url matches registration's scope url.
  9. Let installingWorker be registration's installing worker.
  10. Invoke Run Service Worker algorithm with installingWorker as the arguement.
  11. Queue a task task to run the following substeps:
    1. Create a trusted event e that uses the ExtendableEvent interface, with the event type install, which does not bubble, is not cancelable, and has no default action.
    2. Dispatch e at installingWorker's environment settings object's global object globalObject. During the execution of dispatch algorithm, set globalObject's importscripts allowed flag when e's dispatch flag is set, and unset globalObject's importscripts allowed flag when e's dispatch flag is unset.
    3. Let extendLifetimePromises be an empty array.
    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 installation queue and installation result handle queue.
        5. If the result of running Get Newest Worker algorithm is null, then:
          1. Invoke Clear Registration algorithm passing registration as its argument.
        6. Abort these steps.
      2. Let eventObject be the first argument passed to this event listener.
      3. Append eventObject's extend lifetime promises to extendLifetimePromises.
    5. Let p be waiting for all of extendLifetimePromises.
    6. Run the following substeps in parallel:
      1. Wait until p settles.
      2. If p rejected, then:
        1. Set installFailed to true.
      3. Else if p resolved with a value, then:
        1. Do nothing.
  12. Pop the top element from installation queue.
  13. CheckResultHandlePriority: If the value of the top element of installation result handle queue is not timeStamp, then:
    1. Wait until the top element of installation result handle queue is popped.
    2. Jump to the step labeled CheckResultHandlePriority.

    Wait is a blocking wait. Implementers may use a condition variable or its equivalent synchronization primitive.

    If task is discarded or the script has been aborted by the termination of installingWorker, set installFailed to true.

  14. Wait for task to have executed or been discarded.
  15. If registration's installing worker is null, then:
    1. Abort these steps.
  16. 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. If the result of running Get Newest Worker algorithm is null, then:
      1. Invoke Clear Registration algorithm passing registration as its argument.
    4. Abort these steps.
  17. Set registration's installing worker's imported scripts updated flag.
  18. 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.
  19. Set registration's waiting worker to registration's installing worker.
  20. Set registration's installing worker to null.
  21. Run the Update State algorithm passing registration's waiting worker and installed as the arguments.
  22. If registration's uninstalling flag is set, then:
    1. If it was set before timeStamp, unset registration's uninstalling flag.
    2. Else, abort these steps.
  23. If registration's waiting worker's skip waiting flag is set, then:
    1. For each service worker client serviceWorkerClient whose creation url matches registration's scope url:
      1. Let exitingWorker be the active worker that controls serviceWorkerClient.
      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.
  24. Pop the top element from installation result handle queue.
  25. Wait until no service worker client is using registration or registration's waiting worker's skip waiting flag is set.
  26. If registration's waiting worker waitingWorker is not null and waitingWorker's skip waiting flag is not set, invoke Activate algorithm, or its equivalent, with registration as its argument.

Activate

Input
registration, a service worker registration
Output
None
  1. Generate a timestamp and let timeStamp be the result value.
  2. Push timeStamp to activation queue.
  3. CheckPriority: If the value of the top element of activation queue is not timeStamp, then:
    1. Wait until the top element of activation queue is popped.
    2. Jump to the step labeled CheckPriority.

    Wait is a blocking wait. Implementers may use a condition variable or its equivalent synchronization primitive.

  4. Let activatingWorker be registration's waiting worker.
  5. Let exitingWorker be registration's active worker.
  6. If activatingWorker is null, then:
    1. Pop the top element from activation queue.
    2. Abort these steps.
  7. 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.
  8. Set registration's active worker to activatingWorker.
  9. Set registration's waiting worker to null.
  10. Run the Update State algorithm passing registration's active worker and activating as the arguments.
  11. For each service worker client client whose creation url matches registration's scope url:
    1. If client is a window client, then:
      1. Unassociate client's responsible document from its application cache, if it has one.
    2. Else if client is a shared worker client, then:
      1. Unassociate client's global object from its application cache, if it has one.

    Resources will now use the service worker registration instead of the existing application cache.

  12. For each service worker client client who is using registration:
    1. Set client's active worker to registration's active worker.
    2. Invoke Notify Controller Change algorithm with client as the argument.
  13. Let activeWorker be registration's active worker.
  14. Invoke Run Service Worker algorithm with activeWorker as the arguement.
  15. Queue a task task to run the following substeps:
    1. Create a trusted event e that uses the ExtendableEvent interface, with the event type activate, which does not bubble, is not cancelable, and has no default action.
    2. Dispatch e at activeWorker's environment settings object's global object.
    3. Let extendLifetimePromises be an empty array.
    4. For each event listener invoked:
      1. If any uncaught runtime script error occurs, report the error for the script per the runtime script errors handling.
      2. Let eventObject be the first argument passed to this event listener.
      3. Append eventObject's extend lifetime promises to extendLifetimePromises.
    5. Let p be waiting for all of extendLifetimePromises.
  16. Wait for task to have executed and p defined in task has settled, or task to have been discarded or the script to have been aborted by the termination of activeWorker.
  17. Run the Update State algorithm passing registration's active worker and activated as the arguments.
  18. Pop the top element from activation queue.

Run Service Worker

Input
serviceWorker, a service worker
Output
None
  1. Assert: serviceWorker has the script resource successfully fetched against its script url.
  2. If serviceWorker is already running, abort these steps.
  3. Let workerGlobalScope be a new ServiceWorkerGlobalScope object.
  4. Let workerEventLoop be a newly created event loop.
  5. If serviceWorker is an active worker, and there are any tasks queued in serviceWorker's containing service worker registration's task queues, queue them to serviceWorker's event loop's task queues in the same order using their original task sources.
  6. Let settingsObject be a new environment settings object whose algorithms are defined as follows:
    The script execution environments
    When the environment settings object is created, for each language supported by the user agent, create an appropriate execution environment as defined by the relevant specification.
    When a script execution environment is needed, return the appropriate one from those created when the environment settings object was created.
    The global object
    Return workerGlobalScope.
    The responsible event loop
    Return workerEventLoop.
    The referrer source
    Return serviceWorker's script url.
    The API URL character encoding
    Return UTF-8.
    The API base URL
    Return serviceWorker's script url.
    The origin and effective script origin
    Return its registering service worker client's origin.
  7. Create a separate parallel execution environment (i.e. a separate thread or process or equivalent construct), and run the rest of these steps in that context.
  8. Let source be the result of running the UTF-8 decode algorithm on the script resource of serviceWorker.
  9. Let language be JavaScript.
  10. In the newly created execution environment, create a JavaScript global environment whose global object is workerGlobalScope. (The JavaScript global environment whose global object is a ServiceWorkerGlobalScope object is defined as the service worker environment, which is a type of worker environments.)
  11. Let script be a new script.
  12. Obtain the appropriate script execution environment for the scripting language language from settingsObject.
  13. Parse/compile/initialize source using that script execution environment, as appropriate for language, and thus obtain a code entry-point. If the script was not compiled successfully, let the code entry-point be a no-op script, and act as if a corresponding uncaught script error had occurred.
  14. Let script's settings object be settingsObject.
  15. Jump to the script's code entry-point, and let that run until it either returns, fails to catch an exception, or gets aborted by the kill a worker or Terminate Service Worker algorithms.
  16. Set workerGlobalScope's associated service worker's set of event types to handle to the set of event types created from settingsObject's global object's associated list of event listeners' event types.

    If the global object's associated list of event listeners does not have any event listener added at this moment, the service worker's set of event types to handle is set to an empty set. The user agents are encouraged to show a warning that the event listeners must be added on the initial evaluation of worker script.

  17. Run the responsible event loop specified by settingsObject until it is destroyed.
  18. Empty workerGlobalScope's list of active timers.

Terminate Service Worker

Input
serviceWorker, a service worker
Output
None
  1. If serviceWorker is not running, abort these steps.
  2. Let serviceWorkerGlobalScope be serviceWorker environment settings object's global object.
  3. Set serviceWorkerGlobalScope's closing flag to true.
  4. If there are any tasks, whose task source is either the handle fetch task source or the handle functional event task source, queued in serviceWorkerGlobalScope's event loop's task queues, queue them to serviceWorker's containing service worker registration's corresponding task queues in the same order using their original task sources, and discard all the tasks (including tasks whose task source is neither the handle fetch task source nor the handle functional event task source) from serviceWorkerGlobalScope's event loop's task queues without processing them.

    This effectively means that the fetch events and the other functional events such as push events are backed up by the registration's task queues while the other tasks including message events are discarded.

  5. Abort the script currently running in serviceWorker.

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 eventCanceled be false.
  4. Let r be a new Request object associated with request.
  5. Let headersObject be r's headers attribute value.
  6. Set headersObject's guard to immutable.
  7. Let response be null.
  8. Let registration be null.
  9. Let client be request's client.
  10. Assert: request's destination is not "serviceworker".
  11. If request is a potential-navigation-or-subresource request, then:
    1. Return null.
  12. Else if request is a non-subresource request, then:

    If the non-subresource request is under the scope of a service worker registration, appplication cache is completely bypassed regardless of whether the non-subresource request uses the service worker registration.

    This behavior requires changing the algorithm steps in HTML: Issue 27482.

    1. If request is a navigation request and the navigation triggering it was initiated with a shift+reload or equivalent, return null.
    2. Set registration to the result of running Match Service Worker Registration algorithm, or its equivalent, passing request's url as the argument.
    3. If registration is null or registration's active worker is null, return null.
    4. Set client's active worker to registration's active worker.

    From this point, the service worker client starts to use its active worker's containing service worker registration.

  13. Else if request is a subresource request, then:
    1. If client's active worker is non-null, set registration to client's active worker's containing service worker registration.
    2. Else, return null.
  14. Let activeWorker be registration's active worker.
  15. If activeWorker's set of event types to handle does not contain fetch, return null.

    To avoid unnecessary delays, the Handle Fetch enforces early return when no event listeners have been deterministically added in the service worker's global during the initial script execution.

  16. If activeWorker's state is activating, then:
    1. Wait for activeWorker's state to become activated.
  17. Invoke Run Service Worker algorithm with activeWorker as the arguement.
  18. Queue a task task to run the following substeps:
    1. Create a trusted event e that uses the FetchEvent interface, with the event type fetch, which does not bubble and has no default action.
    2. Let the request attribute of e be initialized to r.
    3. Let c be null.
    4. If request's client is a window client, set c to the result of running Capture Window Client algorithm, or its equivalent, with request's client's responsible browsing context as the argument.
    5. Else, set c to a Client object that represents request's client.
    6. Let the client attribute of e be initialized to c.
    7. Let the isReload attribute of e be initialized to true if request's client is a window client and the event was dispatched with the user's intention for the page reload, and false otherwise.
    8. Dispatch e at activeWorker's environment settings object's global object.
    9. 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. If the argument passed into the event's respondWith(r) method arg is a Response object, then:
            1. Set response to arg.
          2. Else:
            1. Set response to the value which arg resolved with.
      5. If event's canceled flag is set, then:
        1. Set eventCanceled to true.

    If task is discarded or the script has been aborted by the termination of activeWorker, set handleFetchFailed to true.

    The task must use activeWorker's event loop and the handle fetch task source.

  19. Wait for task to have executed or been discarded.
  20. If respondWithEntered is false, then:
    1. If eventCanceled is true, then:
      1. Return a network error and run the following substeps in parallel.
    2. Else:
      1. Return null and run the following substeps in parallel.
    3. If request is a non-subresource request, then:
      1. Invoke Soft Update algorithm, or its equivalent, with registration.
    4. Abort these steps.
  21. If handleFetchFailed is true, then:
    1. Return a network error and run the following substeps in parallel.
    2. If request is a non-subresource request, then:
      1. Invoke Soft Update algorithm, or its equivalent, with registration.
  22. Else:
    1. Return a response represented by response and run the following substeps in parallel.
    2. If request is a non-subresource request, 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. Assert: a Record with the [[value]] equals to registration is contained in scope to registration map.
  2. Assert: registration's active worker is not null.
  3. Let activeWorker be registration's active worker.
  4. Invoke Run Service Worker algorithm with activeWorker as the arguement.
  5. Queue a task task to invoke callbackSteps with activeWorker's environment settings object's global object as its argument.

    The task must use activeWorker's event loop and the handle functional event task source.

  6. Wait for task to have executed or been discarded.
  7. If the time difference in seconds calculated by the current time minus registration's last update check time is greater than 86400, invoke Soft Update algorithm, or its equivalent, with registration.

Handle Service Worker Client Unload

The user agent must run these steps, or their equivalent, when a service worker client unloads by unloading, being killed, or terminating.

Input
client, a service worker client
Output
None
  1. Run the following steps atomically.
  2. Let registration be the service worker registration used by client.
  3. If registration is null, then:
    1. Abort these steps.
  4. If any other service worker client is using 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's waiting worker is not null:
    1. Run Activate algorithm, or its equivalent, with registration at the argument.

Unregister

Input
client, a service worker client
scope, a URL
Output
promise, a promise
  1. Let promise be a new promise.
  2. Run the following substeps in parallel:
    1. If the origin of scope is not client'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 service worker client is using 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.
  3. Return promise.

Set Registration

Input
scope, a URL
Output
registration, a service worker registration
  1. Run the following steps atomically.
  2. Let scopeString be serialized scope with the exclude fragment flag set.
  3. Let registration be a new service worker registration whose scope url is set to scope.
  4. Set a newly-created Record {[[key]]: scopeString, [[value]]: registration} to scope to registration map.
  5. 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 scope to registration map where registration's scope url is the result of parsing 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.
  3. For each serviceWorker in serviceWorkers:
    1. Queue a task to fire a simple event named statechange at serviceWorker.

    The task must use serviceWorker's service worker client's responsible event loop and the DOM manipulation task source.

Notify Controller Change

Input
client, a service worker client
Output
None
  1. Assert: client is not null.
  2. Queue a task to fire a simple event named controllerchange at the ServiceWorkerContainer object client is associated with.

The task must use client's responsible event loop and the DOM manipulation task source.

Match Service Worker Registration

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

Get Registration

Input
scope, a URL
Output
registration, a service worker registration
  1. Run the following steps atomically.
  2. Let registration be null.
  3. Let scopeString be serialized scope with the exclude fragment flag set.
  4. For each Record {[[key]], [[value]]} entry of its scope to registration map:
    1. If scopeString matches entry.[[key]], then:
      1. Set registration to entry.[[value]].
  5. 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.

Capture Window Client

Input
browsingContext, a browsing context
Output
windowClient, a WindowClient object
  1. Let windowClient be a new WindowClient object that represents browsingContext.
  2. Set windowClient's visibility state to browsingContext's active document's visibilityState attribute value.
  3. Set windowClient's focus state to the result of running browsingContext's active document's hasFocus() method.
  4. Return windowClient.

Query Cache

Input
request, a Request object
options, a CacheQueryOptions object, optional
targetStorage, an array that has [Request, Response] pairs as its elements, 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 options.ignoreMethod is false and request.method is neither "GET" nor "HEAD", then:
    1. Return resultArray.
  5. Let cachedURL and requestURL be null.
  6. Let serializedCachedURL and serializedRequestURL be null.
  7. If the optional argument targetStorage is omitted, then:
    1. For each fetching record entry of its request to response map, in key insertion order:
      1. Set cachedURL to entry.[[key]]'s associated request's url.
      2. Set requestURL to 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. Set serializedCachedURL to serialized cachedURL.
      5. Set serializedRequestURL to serialized requestURL.
      6. If serializedCachedURL matches serializedRequestURL, then:
        1. Add a copy of entry.[[key]] to requestArray.
        2. Add a copy of entry.[[value]] to responseArray.
  8. Else:
    1. For each record in targetStorage:
      1. Set cachedURL to record[0]'s associated request's url.
      2. Set requestURL to 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. Set serializedCachedURL to serialized cachedURL.
      5. Set serializedRequestURL to serialized requestURL.
      6. If serializedCachedURL matches serializedRequestURL, then:
        1. Add record[0] to requestArray.
        2. Add record[1] to responseArray.
  9. For each cachedResponse in responseArray with the index index:
    1. Let cachedRequest be the indexth element in requestArray.
    2. If cachedResponse's response's header list contains no header named `Vary`, 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 "*", or 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.
  10. Return resultArray.

Batch Cache Operations

Input
operations, an array of CacheBatchOperation dictionary objects
Output
promise, a promise resolves with an array of Response objects.
  1. Let p be a promise resolved with no value.
  2. Return the result of transforming p with a fulfillment handler that performs the following substeps in parallel:
    1. Let itemsCopy be a new request to response map that is a copy of its context object's request to response map.
    2. Let addedRecords 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 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. If the result of running Query Cache algorithm passing operation.request, operation.options, and addedRecords as the arguments is not an empty array, then:
          1. Throw an "InvalidStateError" exception.
        4. Let requestResponseArray be the result of running Query Cache algorithm passing operation.request and operation.options as the arguments.
        5. For each requestResponse in requestResponseArray:
          1. If operation.type matches "delete", remove the corresponding fetching record from request to response map.
        6. 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. If there exists a corresponding fetching record fetchingRecord for operation.request and operation.response in request to response map, set fetchingRecord.[[value]] to operation.response.
          7. Else, set a newly-created fetching record {[[key]]: operation.request, [[value]]: operation.response} to request to response map.

            The cache commit is allowed as long as the response's headers are available.

          8. If the cache write operation in the previous two steps failed due to exceeding the granted quota limit, throw a "QuotaExceededError" exception.
          9. Add an array [operation.request, operation.response] to addedRecords.
        7. Add operation.response to resultArray.
      3. Return resultArray.
    4. And then, if an exception was thrown, then:
      1. Set the context object's request to response map to itemsCopy.
      2. Throw the exception

Acknowledgements

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, Gavin Peters, Ben Kelly, Hiroki Nakagawa and Jake Archibald.

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