Title: Service Workers Nightly Status: ED ED: https://w3c.github.io/ServiceWorker/ TR: https://www.w3.org/TR/service-workers/ Shortname: service-workers Level: Editor: Alex Russell, Google, slightlyoff@chromium.org Editor: Jungkee Song, Samsung Electronics, jungkee.song@samsung.com Editor: Jake Archibald, Google, jakearchibald@chromium.org Editor: Marijn Kruisselbrink, Google, mek@chromium.org Repository: w3c/ServiceWorker Group: webplatform Status Text: This is a living document addressing new features including foreign fetch, header-based installation, navigation preload, etc. Readers need to be aware that this specification may include unstable features. Service Workers 1 is a version that is advancing toward a W3C Recommendation. 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. Abstract: Abstract: 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. Abstract: Abstract: The service worker is a generic entry point for event-driven background processing in the Web Platform that is extensible by other specifications. Markup Shorthands: css no, markdown yes
spec: html;
type: dfn; text: task queues; for: /
type: element; text: link
spec: dom;
type: interface; text: Document
spec: fetch;
type: dfn
for: /; text: fetch
text: empty
type: interface
text: ReadableStream
spec: infra;
type: dfn;
for: set; text: append
for: list; text: append
spec: push; urlPrefix: https://w3c.github.io/push-api/
type: event
text: push; url: h-the-push-event
spec: ecma-262; urlPrefix: http://tc39.github.io/ecma262/
type: dfn
text: Assert; url: sec-algorithm-conventions
text: [[Call]]; url: sec-ecmascript-function-objects-call-thisargument-argumentslist
text: promise; url: sec-promise-objects
text: DetachArrayBuffer; url: sec-detacharraybuffer
url: sec-list-and-record-specification-type
text: List
text: Record
text: map objects; url: sec-map-objects
text: InitializeHostDefinedRealm; url: sec-initializehostdefinedrealm
text: execution context; url: sec-execution-contexts
spec: page-visibility; urlPrefix: https://www.w3.org/TR/page-visibility/
type: enum; text: VisibilityState; url: VisibilityState
type: attribute; text: visibilityState; for: Document; url: dom-document-visibilitystate
spec: html; urlPrefix: https://html.spec.whatwg.org/multipage/
type: attribute
urlPrefix: comms.html
text: origin; for: MessageEvent; url: dom-messageevent-origin
text: source; for: MessageEvent; url: dom-messageevent-source
text: ports; for: MessageEvent; url: dom-messageevent-ports
text: data; for: MessageEvent; url: dom-messageevent-data
type: dfn
urlPrefix: browsers.html
text: ancestor origins list; for: Location; url: concept-location-ancestor-origins-list
urlPrefix: syntax.html
text: delay the load event; for: document; url: delay-the-load-event
spec: fetch; urlPrefix: https://fetch.spec.whatwg.org/
type: dfn
text: response; for: Response; url: concept-response-response
text: request; for: Request; url: concept-request-request
text: HTTP fetch; for: /; url: concept-http-fetch
spec: rfc5988; urlPrefix: https://tools.ietf.org/html/rfc5988
type: dfn
text: context IRI; url: section-5.2
text: target attribute; url: section-5.4
text: target attributes; url: section-5.4
text: target IRI; url: section-5.1
spec: rfc7230; urlPrefix: https://tools.ietf.org/html/rfc7230
type: dfn
text: field-value; for: http; url: section-3.2
spec: rfc7231; urlPrefix: https://tools.ietf.org/html/rfc7231
type: dfn
text: Vary; url: section-7.1.4
spec: webappsec-referrer-policy; urlPrefix: https://w3c.github.io/webappsec-referrer-policy/
type: dfn
text: Parse a referrer policy from a Referrer-Policy header; url: parse-referrer-policy-from-header
{
"unsanctioned-tracking": {
"href": "https://www.w3.org/2001/tag/doc/unsanctioned-tracking/",
"title": "Unsanctioned Web Tracking",
"date": "17 July 2015",
"status": "Finding of the W3C TAG",
"publisher": "W3C TAG"
}
}
classic" or "module". Unless stated otherwise, it is "classic".
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=] has an associated global object (a {{ServiceWorkerGlobalScope}} object or null).
A [=/service worker=] is dispatched a set of lifecycle events, {{install!!event}} and {{activate!!event}}, and functional events including {{fetch!!event}}.
A [=/service worker=] has an associated script resource (a script), which represents its own script resource. It is initially set to null.
A script resource has an associated has ever been evaluated flag. It is initially unset.
A script resource has an associated HTTPS state (an HTTPS state value). It is initially "none".
A script resource has an associated referrer policy (a [=/referrer policy=]). It is initially the empty string.
A [=/service worker=] has an associated script resource map which is an ordered map where the keys are [=/URLs=] and the values are [=/responses=].
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 (a [=ordered set|set=]) whose [=item=] is an event listener's event type. It is initially an empty set.
A [=/service worker=] has an associated set of extended events (a [=ordered set|set=]) whose [=item=] is an {{ExtendableEvent}}. It is initially an empty set.
A [=/service worker=] has an associated list of foreign fetch scopes whose element type is a [=/URL=]. It is initially empty.
A [=/service worker=] has an associated list of foreign fetch origins whose element type is a [=/URL=]. It is initially empty.
true\`.
// scope defaults to the path the script sits in
// "/" in this example
navigator.serviceWorker.register("/serviceworker.js").then(registration => {
console.log("success!");
if (registration.installing) {
registration.installing.postMessage("Howdy from your installing page.");
}
}, err => {
console.error("Installing the worker failed!", err);
});
[SecureContext, Exposed=(Window,Worker)]
interface ServiceWorker : EventTarget {
readonly attribute USVString scriptURL;
readonly attribute ServiceWorkerState state;
void postMessage(any message, optional sequence<object> 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 documents and workers 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.
scriptURL attribute *must* return the [=/service worker=]'s serialized [=service worker/script url=].
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");
The value of navigator.serviceWorker.controller.scriptURL will be "https://example.com/service_worker.js".
state attribute *must* return the value (in {{ServiceWorkerState}} enumeration) to which it was last set.
postMessage(|message|, |transfer|) method *must* run these steps:
1. If the {{ServiceWorker/state}} attribute value of the context object is {{"redundant"}}, throw an "{{InvalidStateError}}" exception and abort these steps.
1. Let |serviceWorker| be the [=/service worker=] represented by the context object.
1. Invoke Run Service Worker algorithm with |serviceWorker| as the argument.
1. Let |destination| be the {{ServiceWorkerGlobalScope}} object associated with |serviceWorker|.
1. Let |targetRealm| be |destination|'s [=global object/Realm=].
1. Let |incumbentSettings| be the incumbent settings object, and |incumbentGlobal| its [=environment settings object/global object=].
1. Let |cloneRecord| be StructuredCloneWithTransfer(|message|, |transfer|, |targetRealm|). If this throws an exception, rethrow that exception and abort these steps.
1. Let |clonedMessage| be |cloneRecord|.\[[Clone]].
1. Let |newPorts| be a new [=frozen array type|frozen array=] consisting of all {{MessagePort}} objects in |cloneRecord|.\[[TransferList]], if any, maintaining their relative order.
1. Queue a task that runs the following steps:
1. Create an event |e| that uses the {{ExtendableMessageEvent}} interface, with the event type {{message!!event}}, which does not bubble and is not cancelable.
1. Let the {{ExtendableMessageEvent/data}} attribute of |e| be initialized to |clonedMessage|.
1. Let the {{ExtendableMessageEvent/origin}} attribute of |e| be initialized to the Unicode serialization of |incumbentSettings|'s [=environment settings object/origin=].
1. If |incumbentGlobal| is a {{ServiceWorkerGlobalScope}} object, let the {{ExtendableMessageEvent/source}} attribute of |e| be initialized to a new {{ServiceWorker}} object that represents |incumbentGlobal|'s [=ServiceWorkerGlobalScope/service worker=].
1. Else if |incumbentGlobal| is a {{Window}} object, let the {{ExtendableMessageEvent/source}} attribute of |e| be initialized to a new {{WindowClient}} object that represents |incumbentGlobal|'s [=/environment settings object=].
1. Else, let it be initialized to a new {{Client}} object that represents the worker associated with |incumbentGlobal|.
1. Let the {{ExtendableMessageEvent/ports}} attribute of |e| be initialized to |newPorts|.
1. Dispatch |e| at |destination|.
1. Invoke [=Update Service Worker Extended Events Set=] with |serviceWorker| and |e|.
The task *must* use the DOM manipulation task source.
| event handler | event handler event type |
|---|---|
onstatechange |
{{ServiceWorker/statechange}} |
[SecureContext, Exposed=(Window,Worker)]
interface ServiceWorkerRegistration : EventTarget {
readonly attribute ServiceWorker? installing;
readonly attribute ServiceWorker? waiting;
readonly attribute ServiceWorker? active;
readonly attribute NavigationPreloadManager navigationPreload;
readonly attribute USVString scope;
readonly attribute boolean useCache;
[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 documents and workers can all be associated with the same [=/service worker registration=] simultaneously.
installing attribute *must* return the value to which it was last set.
Note: The {{ServiceWorker}} objects returned from this attribute getter that represent the same [=/service worker=] are the same objects.
waiting attribute *must* return the value to which it was last set.
Note: The {{ServiceWorker}} objects returned from this attribute getter that represent the same [=/service worker=] are the same objects.
active attribute *must* return the value to which it was last set.
Note: The {{ServiceWorker}} objects returned from this attribute getter that represent the same [=/service worker=] are the same objects.
navigationPreload attribute *must* return [=ServiceWorkerRegistration/service worker registration=]'s {{NavigationPreloadManager}} object.
scope attribute *must* return [=ServiceWorkerRegistration/service worker registration=]'s serialized [=service worker registration/scope url=].
registration.scope, obtained from navigator.serviceWorker.ready.then(registration => console.log(registration.scope)) for example, will be "https://example.com/".
useCache attribute *must* return [=ServiceWorkerRegistration/service worker registration=]'s [=service worker registration/use cache=].
update() method *must* run these steps:
1. Let |p| be a promise.
1. Let |registration| be the [=ServiceWorkerRegistration/service worker registration=].
1. Let |newestWorker| be the result of running Get Newest Worker algorithm passing |registration| as its argument.
1. If |newestWorker| is null, reject |p| with an "{{InvalidStateError}}" exception and abort these steps.
1. If the context object's relevant settings object's [=environment settings object/global object=] |globalObject| is a {{ServiceWorkerGlobalScope}} object, and |globalObject|'s associated [=ServiceWorkerGlobalScope/service worker=]'s state is *installing*, reject |p| with an "{{InvalidStateError}}" exception and abort these steps.
1. Let |job| be the result of running Create Job with *update*, |registration|'s [=service worker registration/scope url=], |newestWorker|'s [=service worker/script url=], |p|, and the context object's relevant settings object.
1. Set |job|'s worker type to |newestWorker|'s [=service worker/type=].
1. Invoke Schedule Job with |job|.
1. Return |p|.
unregister() method *must* run these steps:
1. Let |p| be a promise.
1. Let |job| be the result of running Create Job with *unregister*, the [=service worker registration/scope url=] of the [=ServiceWorkerRegistration/service worker registration=], null, |p|, and the context object's relevant settings object.
1. Invoke Schedule Job with |job|.
1. Return |p|.
| event handler | event handler event type |
|---|---|
onupdatefound |
{{updatefound!!event}} |
partial interface Navigator {
[SecureContext, SameObject] readonly attribute ServiceWorkerContainer serviceWorker;
};
partial interface WorkerNavigator {
[SecureContext, SameObject] readonly attribute ServiceWorkerContainer serviceWorker;
};
The serviceWorker attribute *must* return the {{ServiceWorkerContainer}} object that is associated with the context object.
[SecureContext, Exposed=(Window,Worker)]
interface ServiceWorkerContainer : EventTarget {
readonly attribute ServiceWorker? controller;
readonly attribute Promise<ServiceWorkerRegistration> ready;
[NewObject] Promise<ServiceWorkerRegistration> register(USVString scriptURL, optional RegistrationOptions options);
[NewObject] Promise<any> getRegistration(optional USVString clientURL = "");
[NewObject] Promise<sequence<ServiceWorkerRegistration>> getRegistrations();
void startMessages();
// events
attribute EventHandler oncontrollerchange;
attribute EventHandler onmessage; // event.source of message events is ServiceWorker object
};
dictionary RegistrationOptions {
USVString scope;
WorkerType type = "classic";
boolean useCache = false;
};
The user agent *must* create a {{ServiceWorkerContainer}} object when a {{Navigator}} object or a {{WorkerNavigator}} object 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 [=environment settings object/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 a new promise.
A {{ServiceWorkerContainer}} object has a task source called the client message queue, initially empty. A [=ServiceWorkerContainer/client message queue=] can be enabled or disabled, and is initially disabled. When a {{ServiceWorkerContainer}} object's [=ServiceWorkerContainer/client message queue=] is enabled, the event loop *must* use it as one of its task sources. When the {{ServiceWorkerContainer}} object's relevant global object is a {{Window}} object, all tasks queued on its [=ServiceWorkerContainer/client message queue=] *must* be associated with its relevant settings object's responsible document.
controller attribute *must* run these steps:
1. Let |client| be the context object's [=ServiceWorkerContainer/service worker client=].
1. Return the {{ServiceWorker}} object that represents |client|'s active service worker.
Note: {{ServiceWorkerContainer/controller|navigator.serviceWorker.controller}} returns null if the request is a force refresh (shift+refresh). The {{ServiceWorker}} objects returned from this attribute getter that represent the same [=/service worker=] are the same objects.
ready attribute *must* run these steps:
1. If the context object's [=ServiceWorkerContainer/ready promise=] is settled, return the context object's [=ServiceWorkerContainer/ready promise=].
1. Let |client| be the context object's [=ServiceWorkerContainer/service worker client=].
1. Let |registration| be null.
1. Let |clientURL| be |client|'s creation URL.
1. Run the following substeps in parallel:
1. *CheckRegistration*: If the result of running Match Service Worker Registration algorithm with |clientURL| as its argument is not null, then:
1. Set |registration| to the result value.
1. Else:
1. Wait until scope to registration map has a new entry.
1. Jump to the step labeled *CheckRegistration*.
1. If |registration|'s active worker is null, wait until |registration|'s active worker changes.
Note: Implementers should consider this condition is met when the corresponding registration request gets to the step 6 of Activate algorithm.
1. Resolve context object's [=ServiceWorkerContainer/ready promise=] with the {{ServiceWorkerRegistration}} object which represents |registration|.
1. Return context object's [=ServiceWorkerContainer/ready promise=].
Note: When the {{ServiceWorkerContainer/ready}} attribute is accessed, 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|) method *must* run these steps:
1. Let |p| be a promise.
1. Let |client| be the context object's [=ServiceWorkerContainer/service worker client=].
1. Let |scriptURL| be the result of parsing |scriptURL| with the context object's relevant settings object's API base URL.
1. Let |scopeURL| be null.
1. If |options|.{{RegistrationOptions/scope}} is present, set |scopeURL| to the result of parsing |options|.{{RegistrationOptions/scope}} with the context object's relevant settings object's API base URL.
1. Invoke [=Start Register=] with |scopeURL|, |scriptURL|, |p|, |client|, |client|'s creation URL, |options|.{{RegistrationOptions/type}}, and |options|.{{RegistrationOptions/useCache}}.
1. Return |p|.
getRegistration(|clientURL|) method *must* run these steps:
1. Let |client| be the context object's [=ServiceWorkerContainer/service worker client=].
1. Let |clientURL| be the result of parsing |clientURL| with the context object's relevant settings object's API base URL.
1. If |clientURL| is failure, return a promise rejected with a TypeError.
1. If the [=environment settings object/origin=] of |clientURL| is not |client|'s [=environment settings object/origin=], return a |promise| rejected with a "{{SecurityError}}" exception.
1. Let |promise| be a new promise.
1. Run the following substeps in parallel:
1. Let |registration| be the result of running Match Service Worker Registration algorithm with |clientURL| as its argument.
1. If |registration| is not null, then:
1. Resolve |promise| with the {{ServiceWorkerRegistration}} object which represents |registration|.
1. Else:
1. Resolve |promise| with undefined.
1. Return |promise|.
getRegistrations() method *must* run these steps:
1. Let |client| be the context object's [=ServiceWorkerContainer/service worker client=].
1. Let |promise| be a new promise.
1. Run the following substeps in parallel:
1. Let |array| be an empty array.
1. [=map/For each=] |key| → |value| of scope to registration map:
1. If the [=url/origin=] of the result of parsing |key| is the same as |client|'s [=environment settings object/origin=], and |value|'s uninstalling flag is unset, add the {{ServiceWorkerRegistration}} object associated with |value| to the |array|.
1. Resolve |promise| with |array|.
1. Return |promise|.
startMessages() method *must* enable the context object's [=ServiceWorkerContainer/client message queue=] if it is not enabled.
| event handler | event handler event type |
|---|---|
oncontrollerchange |
{{ServiceWorkerContainer/controllerchange!!event}} |
onmessage |
{{ServiceWorkerContainer/message!!event}} |
| Event name | Interface | Dispatched when… |
|---|---|---|
statechange |
{{Event}} | The {{ServiceWorker/state}} attribute of the {{ServiceWorker}} object is changed. |
| Event name | Interface | Dispatched when… |
|---|---|---|
updatefound |
{{Event}} | The [=ServiceWorkerRegistration/service worker registration=]'s installing worker changes. (See step 8 of the Install algorithm.) |
| Event name | Interface | Dispatched when… |
|---|---|---|
controllerchange |
{{Event}} | The [=ServiceWorkerContainer/service worker client=]'s active service worker changes. (See step 9.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=], {{ServiceWorkerContainer/controller|navigator.serviceWorker.controller}} immediately reflects the active worker as the [=/service worker=] that controls the [=/service worker client=].) |
[SecureContext, Exposed=(Window,Worker)]
interface NavigationPreloadManager {
Promise<void> enable();
Promise<void> disable();
Promise<void> setHeaderValue(ByteString value);
Promise<NavigationPreloadState> getState();
};
dictionary NavigationPreloadState {
boolean enabled = false;
ByteString headerValue;
};
enable() method, when invoked, *must* return a new [=promise=] |promise| and run the following steps [=in parallel=]:
1. Let |registration| be the [=context object=]'s associated [=/service worker registration=].
1. If |registration|'s [=active worker=] is null, [=reject=] |promise| with an "{{InvalidStateError}}" exception, and abort these steps.
1. Set |registration|'s [=navigation preload enabled flag=].
1. [=Resolve=] |promise| with undefined.
disable() method, when invoked, *must* return a new [=promise=] |promise| and run the following steps [=in parallel=]:
1. Let |registration| be the [=context object=]'s associated [=/service worker registration=].
1. If |registration|'s [=active worker=] is null, [=reject=] |promise| with an "{{InvalidStateError}}" exception, and abort these steps.
1. Unset |registration|'s [=navigation preload enabled flag=].
1. [=Resolve=] |promise| with undefined.
setHeaderValue(|value|) method, when invoked, *must* return [=a new promise=] |promise| and run the following steps [=in parallel=]:
1. Let |registration| be the [=context object=]'s associated [=/service worker registration=].
1. If |registration|'s [=active worker=] is null, [=reject=] |promise| with an "{{InvalidStateError}}" exception, and abort these steps.
1. Set |registration|'s [=navigation preload header value=] to |value|.
1. [=Resolve=] |promise| with undefined.
getState() method, when invoked, *must* return [=a new promise=] |promise| and run the following steps [=in parallel=]:
1. Let |registration| be the [=context object=]'s associated [=/service worker registration=].
1. Let |state| be a new {{NavigationPreloadState}} dictionary.
1. If |registration|'s [=navigation preload enabled flag=] is set, set |state|'s {{NavigationPreloadState/enabled}} dictionary member to true.
1. Set |state|'s {{NavigationPreloadState/headerValue}} dictionary member to the |registration|'s [=navigation preload header value=].
1. [=Resolve=] |promise| with |state|.
// caching.js
self.addEventListener("install", event => {
event.waitUntil(
// Open a cache of resources.
caches.open("shell-v1").then(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"
]);
})
);
});
self.addEventListener("fetch", event => {
// 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:
event.respondWith(
caches.match(e.request).then(response => {
return response || fetch(e.request);
}).catch(() => {
return caches.match("/fallback.html");
})
);
});
[Global=(Worker,ServiceWorker), Exposed=ServiceWorker]
interface ServiceWorkerGlobalScope : WorkerGlobalScope {
[SameObject] readonly attribute Clients clients;
[SameObject] readonly attribute ServiceWorkerRegistration registration;
[NewObject] Promise<void> skipWaiting();
attribute EventHandler oninstall;
attribute EventHandler onactivate;
attribute EventHandler onfetch;
attribute EventHandler onforeignfetch;
// event
attribute EventHandler onmessage; // event.source of the message events is Client object
};
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 force bypass cache for importscripts flag. It is initially unset.
Note: {{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=].
clients attribute *must* return the {{Clients}} object that is associated with the context object.
registration attribute *must* return the {{ServiceWorkerRegistration}} object that represents the [=ServiceWorkerGlobalScope/service worker=]'s containing service worker registration.
skipWaiting() method *must* run these steps:
1. Let |promise| be a new promise.
1. Run the following substeps in parallel:
1. Set [=ServiceWorkerGlobalScope/service worker=]'s skip waiting flag.
1. Invoke [=Try Activate=] with [=ServiceWorkerGlobalScope/service worker=]'s [=containing service worker registration=].
1. Resolve |promise| with undefined.
1. Return |promise|.
| event handler | event handler event type |
|---|---|
oninstall |
{{install!!event}} |
onactivate |
{{activate!!event}} |
onfetch |
{{fetch!!event}} |
onforeignfetch |
{{foreignfetch!!event}} |
onmessage |
{{message!!event}} |
[Exposed=ServiceWorker]
interface Client {
readonly attribute USVString url;
readonly attribute DOMString id;
readonly attribute ClientType type;
readonly attribute boolean reserved;
void postMessage(any message, optional sequence<object> transfer = []);
};
[Exposed=ServiceWorker]
interface WindowClient : Client {
readonly attribute VisibilityState visibilityState;
readonly attribute boolean focused;
[SameObject] readonly attribute FrozenArray<USVString> ancestorOrigins;
[NewObject] Promise<WindowClient> focus();
[NewObject] Promise<WindowClient> navigate(USVString url);
};
A {{Client}} object has an associated service worker client (a [=/service worker client=]).
A {{Client}} object has an associated reserved state, which is either true or false.
A {{WindowClient}} object has an associated visibility state, which is one of {{Document/visibilityState}} attribute value.
A {{WindowClient}} object has an associated focus state, which is either true or false (initially false).
A {{WindowClient}} object has an associated ancestor origins array.
url attribute *must* return the context object's associated [=Client/service worker client=]'s serialized creation URL.
id attribute *must* return its associated [=Client/service worker client=]'s [=environment/id=].
type attribute *must* run these steps:
1. Let |client| be [=context object=]'s [=Client/service worker client=].
1. If |client| is an [=environment settings object=], then:
1. If |client| is a [=window client=], return {{ClientType/"window"}}.
1. Else if |client| is a [=dedicated worker client=], return {{ClientType/"worker"}}.
1. Else if |client| is a [=shared worker client=], return {{ClientType/"sharedworker"}}.
1. Else:
1. Return {{ClientType/"window"}}.
reserved attribute *must* return the context object's associated [=Client/reserved state=].
postMessage(|message|, |transfer|) method *must* run these steps:
1. Let |sourceSettings| be the context object's relevant settings object.
1. Let |destination| be the {{ServiceWorkerContainer}} object whose [=ServiceWorkerContainer/service worker client=] is the context object's [=Client/service worker client=].
1. If |destination| is null, throw an "{{InvalidStateError}}" exception.
1. Let |targetRealm| be |destination|'s relevant Realm.
1. Let |cloneRecord| be StructuredCloneWithTransfer(|message|, |transfer|, |targetRealm|). If this throws an exception, rethrow that exception and abort these steps.
1. Let |clonedMessage| be |cloneRecord|.\[[Clone]].
1. Let |newPorts| be a new [=frozen array type|frozen array=] consisting of all {{MessagePort}} objects in |cloneRecord|.\[[TransferList]], if any, maintaining their relative order.
1. Add a task that runs the following steps to |destination|'s [=ServiceWorkerContainer/client message queue=]:
1. Create an event |e| that uses the {{MessageEvent}} interface, with the event type {{Window/message!!event}}, which does not bubble and is not cancelable.
1. Let the data attribute of |e| be initialized to |clonedMessage|.
1. Let the origin attribute of |e| be initialized to the Unicode serialization of |sourceSettings|'s [=environment settings object/origin=].
1. Let the source attribute of |e| be initialized to a {{ServiceWorker}} object, which represents the [=ServiceWorkerGlobalScope/service worker=] associated with |sourceSettings|'s [=environment settings object/global object=].
1. Let the ports attribute of |e| be initialized to |newPorts|.
1. Dispatch |e| at |destination|.
visibilityState attribute *must* return the context object's visibility state.
focused attribute *must* return the context object's focus state.
ancestorOrigins attribute *must* return the context object's associated [=WindowClient/ancestor origins array=].
focus() method *must* run these steps:
1. If this algorithm is not triggered by user activation, return a promise rejected with an "{{InvalidAccessError}}" exception.
1. Let |promise| be a new promise.
1. Run these substeps in parallel:
1. Let |browsingContext| be the context object's associated [=Client/service worker client=]'s [=environment settings object/global object=]'s [=/browsing context=].
1. Let |visibilityState| be null.
1. Let |focusState| be false.
1. Let |ancestorOriginsList| be the empty list.
1. Queue a task |task| to run the following substeps on the context object's associated [=Client/service worker client=]'s responsible event loop using the user interaction task source:
1. Run the focusing steps with |browsingContext|.
1. Set |visibilityState| to |browsingContext|'s active document's {{Document/visibilityState}} attribute value.
1. Set |focusState| to the result of running the has focus steps with |browsingContext|'s active document as the argument.
1. Set |ancestorOriginsList| to |browsingContext|'s active document's relevant global object's {{Location}} object's [=Location/ancestor origins list=]'s associated list.
1. Wait for |task| to have executed.
1. Let |windowClient| be the result of running Create Window Client algorithm with the context object's associated [=Client/service worker client=], |visibilityState|, |focusState|, and |ancestorOriginsList| as the arguments.
1. If |windowClient|'s focus state is true, resolve |promise| with |windowClient|.
1. Else, reject |promise| with a TypeError.
1. Return |promise|.
navigate() method *must* run these steps:
1. Let |url| be the result of parsing |url| with the context object's relevant settings object's API base URL.
1. If |url| is failure, return a promise rejected with a TypeError.
1. If |url| is about:blank, return a promise rejected with a TypeError.
1. If the context object's associated [=Client/service worker client=]'s active service worker is not the context object's relevant global object's [=ServiceWorkerGlobalScope/service worker=], return a promise rejected with a TypeError.
1. Let |promise| be a new promise.
1. Run these substeps in parallel:
1. Let |browsingContext| be the context object's associated [=Client/service worker client=]'s [=environment settings object/global object=]'s [=/browsing context=].
1. If |browsingContext| has discarded its {{Document}}, reject |promise| with a TypeError and abort these steps.
1. Let |navigateFailed| to false.
1. Let |visibilityState| be null.
1. Let |focusState| be false.
1. Let |ancestorOriginsList| be the empty list.
1. Queue a task |task| to run the following substeps on the context object's associated [=Client/service worker client=]'s responsible event loop using the user interaction task source:
1. *HandleNavigate*: Navigate |browsingContext| to |url| with exceptions enabled. The source browsing context must be |browsingContext|.
1. If the algorithm steps invoked in the step labeled *HandleNavigate* throws an exception, set |navigateFailed| to true.
1. Set |visibilityState| to |browsingContext|'s active document's {{Document/visibilityState}} attribute value.
1. Set |focusState| to the result of running the has focus steps with |browsingContext|'s active document as the argument.
1. Set |ancestorOriginsList| to |browsingContext|'s active document's relevant global object's {{Location}} object's [=Location/ancestor origins list=]'s associated list.
1. Wait for |task| to have executed (including its asynchronous steps).
1. If |navigateFailed| is true, reject |promise| with a TypeError and abort these steps.
1. If |browsingContext|'s {{Window}} object's environment settings object's creation URL's [=url/origin=] is not the same as the [=ServiceWorkerGlobalScope/service worker=]'s [=environment settings object/origin=], then:
1. Resolve |promise| with null.
1. Abort these steps.
1. Let |windowClient| be the result of running Create Window Client algorithm with |browsingContext|'s {{Window}} object's environment settings object, |visibilityState|, |focusState|, and |ancestorOriginsList| as the arguments.
1. Resolve |promise| with |windowClient|.
1. Return |promise|.
[Exposed=ServiceWorker]
interface Clients {
// The objects returned will be new instances every time
[NewObject] Promise<any> get(DOMString id);
[NewObject] Promise<sequence<Client>> matchAll(optional ClientQueryOptions options);
[NewObject] Promise<WindowClient?> openWindow(USVString url);
[NewObject] Promise<void> claim();
};
dictionary ClientQueryOptions {
boolean includeUncontrolled = false;
boolean includeReserved = false;
ClientType type = "window";
};
enum ClientType {
"window",
"worker",
"sharedworker",
"all"
};
The user agent *must* create a {{Clients}} object when a {{ServiceWorkerGlobalScope}} object is created and associate it with that object.
get(|id|) method *must* run these steps:
1. Let |promise| be a new promise.
1. Run these substeps in parallel:
1. For each [=/service worker client=] |client| whose [=service worker client/origin=] is the same as the associated [=ServiceWorkerGlobalScope/service worker=]'s [=environment settings object/origin=]:
1. If |client|'s [=environment/id=] is not |id|, continue to the next iteration of the loop.
1. If |client| is an [=environment settings object=], then:
1. If |client| is not a secure context, reject |promise| with a "{{SecurityError}}" exception and abort these steps.
1. Else:
1. If |client|’s creation URL is not a potentially trustworthy URL, reject |promise| with a "{{SecurityError}}" exception and abort these steps.
1. If |client| is an [=environment settings object=] and is not a [=window client=], then:
1. Let |clientObject| be the result of running Create Client algorithm with |client| as the argument.
1. Resolve |promise| with |clientObject| and abort these steps.
1. Else:
1. Let |browsingContext| be null.
1. Let |visibilityState| be null.
1. Let |focusState| be false.
1. Let |ancestorOriginsList| be the empty list.
1. If |client| is an [=environment settings object=], set |browsingContext| to |client|'s [=environment settings object/global object=]'s [=/browsing context=].
1. Else, set |browsingContext| to |client|’s [=environment/target browsing context=].
1. Queue a task |task| to run the following substeps on |browsingContext|'s event loop using the user interaction task source:
1. Set |visibilityState| to |browsingContext|'s active document's {{Document/visibilityState}} attribute value.
1. Set |focusState| to the result of running the has focus steps with |browsingContext|'s active document as the argument.
1. If |client| is a window client, set |ancestorOriginsList| to |browsingContext|'s active document's relevant global object's {{Location}} object's [=Location/ancestor origins list=]'s associated list.
1. Wait for |task| to have executed.
1. Let |windowClient| be the result of running Create Window Client algorithm with |client|, |visibilityState|, |focusState|, and |ancestorOriginsList| as the arguments.
1. Resolve |promise| with |windowClient| and abort these steps.
1. Resolve |promise| with undefined.
1. Return |promise|.
matchAll(|options|) method *must* run these steps:
1. Let |promise| be a new promise.
1. Run these substeps in parallel:
1. Let |targetClients| be an empty array.
1. For each [=/service worker client=] |client| whose [=service worker client/origin=] is the same as the associated [=ServiceWorkerGlobalScope/service worker=]'s [=environment settings object/origin=]:
1. If |client| is an [=environment settings object=], then:
1. If |client| is not a secure context, continue to the next iteration of the loop.
1. Else:
1. If |client|’s creation URL is not a potentially trustworthy URL, continue to the next iteration of the loop.
1. If |options|.{{ClientQueryOptions/includeUncontrolled}} is false, then:
1. If |client|'s active service worker is not the associated [=ServiceWorkerGlobalScope/service worker=], continue to the next iteration of the loop.
1. If |options|.{{ClientQueryOptions/includeReserved}} is false, then:
1. If |client|'s [=environment/execution ready flag=] is unset, continue to the next iteration of the loop.
1. Add |client| to |targetClients|.
1. Let |matchedClients| be an empty array.
1. For each [=/service worker client=] |client| in |targetClients|:
1. If |options|.{{ClientQueryOptions/type}} is {{ClientType/"window"}} or {{ClientType/"all"}}, and |client| is not an [=environment settings object=] or is a [=window client=], then:
1. Let |browsingContext| be null.
1. Let |isClientEnumerable| be true.
1. Let |visibilityState| be the empty string.
1. Let |focusState| be false.
1. Let |ancestorOriginsList| be the empty list.
1. If |client| is an [=environment settings object=], set |browsingContext| to |client|'s [=environment settings object/global object=]'s [=/browsing context=].
1. Else, set |browsingContext| to |client|’s [=environment/target browsing context=].
1. Queue a task |task| to run the following substeps on |browsingContext|'s event loop using the user interaction task source:
1. If |browsingContext| has been discarded, set |isClientEnumerable| to false and abort these steps.
1. If |client| is a window client and |client|'s responsible document is not |browsingContext|'s active document, set |isClientEnumerable| to false and abort these steps.
1. Set |visibilityState| to |browsingContext|'s active document's {{Document/visibilityState}} attribute value.
1. Set |focusState| to the result of running the has focus steps with |browsingContext|'s active document as the argument.
1. It |client| is a window client, set |ancestorOriginsList| to |browsingContext|'s active document's relevant global object's {{Location}} object's [=Location/ancestor origins list=]'s associated list.
1. Wait for |task| to have executed.
Note: Wait is a blocking wait, but implementers may run the iterations in parallel as long as the state is not broken.
1. If |isClientEnumerable| is true, then:
1. Let |windowClient| be the result of running Create Window Client algorithm with |client|, |visibilityState|, |focusState|, and |ancestorOriginsList| as the arguments.
1. Add |windowClient| to |matchedClients|.
1. Else if |options|.{{ClientQueryOptions/type}} is {{ClientType/"worker"}} or {{ClientType/"all"}} and |client| is a [=dedicated worker client=], or |options|.{{ClientQueryOptions/type}} is {{ClientType/"sharedworker"}} or {{ClientType/"all"}} and |client| is a [=shared worker client=], then:
1. Let |clientObject| be the result of running Create Client algorithm with |client| as the argument.
1. Add |clientObject| to |matchedClients|.
1. Sort |matchedClients| such that:
* {{WindowClient}} objects are always placed before {{Client}} objects whose associated [=Client/service worker clients=] are worker clients.
* {{WindowClient}} objects that have been focused are placed first sorted in the most recently focused order, and {{WindowClient}} objects that have never been focused are placed next sorted in their [=Client/service worker clients=]' creation order.
* {{Client}} objects whose associated [=Client/service worker clients=] are worker clients are placed next sorted in their [=Client/service worker clients=]' creation order.
1. Resolve |promise| with |matchedClients|.
1. Return |promise|.
openWindow(|url|) method *must* run these steps:
1. Let |url| be the result of parsing |url| with the context object's relevant settings object's API base URL.
1. If |url| is failure, return a promise rejected with a TypeError.
1. If |url| is about:blank, return a promise rejected with a TypeError.
1. If this algorithm is not triggered by user activation, return a promise rejected with an "{{InvalidAccessError}}" exception.
1. Let |promise| be a new promise.
1. Run these substeps in parallel:
1. Let |newContext| be a new top-level browsing context.
1. Let |openWindowFailed| to false.
1. Let |visibilityState| be null.
1. Let |focusState| be false.
1. Let |ancestorOriginsList| be the empty list.
1. Queue a task |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.
1. If the algorithm steps invoked in the step labeled *HandleNavigate* throws an exception, set |openWindowFailed| to true.
1. Set |visibilityState| to |newContext|'s active document's {{Document/visibilityState}} attribute value.
1. Set |focusState| to the result of running the has focus steps with |newContext|'s active document as the argument.
1. Set |ancestorOriginsList| to |newContext|'s active document's relevant global object's {{Location}} object's [=Location/ancestor origins list=]'s associated list.
1. Wait for |task| to have executed (including its asynchronous steps).
1. If |openWindowFailed| is true, reject |promise| with a TypeError and abort these steps.
1. If |newContext|'s {{Window}} object's environment settings object's creation URL's [=environment settings object/origin=] is not the same as the [=ServiceWorkerGlobalScope/service worker=]'s [=environment settings object/origin=], then:
1. Resolve |promise| with null.
1. Abort these steps.
1. Let |client| be the result of running Create Window Client algorithm with |newContext|'s {{Window}} object's environment settings object, |visibilityState|, |focusState|, and |ancestorOriginsList| as the arguments.
1. Resolve |promise| with |client|.
1. Return |promise|.
claim() method *must* run these steps:
1. If the [=ServiceWorkerGlobalScope/service worker=] is not an active worker, return a promise rejected with an "{{InvalidStateError}}" exception.
1. Let |promise| be a new promise.
1. Run the following substeps in parallel:
1. For each [=/service worker client=] |client| whose [=service worker client/origin=] is the same as the [=ServiceWorkerGlobalScope/service worker=]'s [=environment settings object/origin=]:
1. If |client| is an [=environment settings object=], then:
1. If |client| is not a secure context, continue to the next iteration of the loop.
1. Else:
1. If |client|’s creation URL is not a potentially trustworthy URL, continue to the next iteration of the loop.
1. Let |registration| be the result of running Match Service Worker Registration algorithm passing |client|'s creation URL as the argument.
1. If |registration| is not the [=ServiceWorkerGlobalScope/service worker=]'s containing service worker registration, continue to the next iteration of the loop.
1. If |client|'s active service worker is not the [=ServiceWorkerGlobalScope/service worker=], then:
1. Invoke Handle Service Worker Client Unload with |client| as the argument.
1. Set |client|'s active service worker to [=ServiceWorkerGlobalScope/service worker=].
1. Invoke Notify Controller Change algorithm with |client| as the argument.
1. Resolve |promise| with undefined.
1. Return |promise|.
[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 an empty array.
An {{ExtendableEvent}} object has an associated pending promises count (the number of pending promises in the [=ExtendableEvent/extend lifetime promises=]). It is initially set to zero.
[=/Service workers=] have two lifecycle events, {{install!!event}} and {{activate!!event}}. [=/Service workers=] use the {{ExtendableEvent}} interface for {{activate!!event}} event and {{install!!event}} event.
Service worker extensions that define event handlers *may* also use or extend the {{ExtendableEvent}} interface.
waitUntil(|f|) method *must* run these steps:
1. If the [=ExtendableEvent/pending promises count=] is zero and the [=dispatch flag=] is unset, then:
1. Throw an "{{InvalidStateError}}" exception.
1. Abort these steps.
Note: If no lifetime extension promise has been added in the task that called the event handlers), calling {{ExtendableEvent/waitUntil()}} in subsequent asynchronous tasks will throw.
1. Add |f| to the [=ExtendableEvent/extend lifetime promises=].
1. Increment the [=ExtendableEvent/pending promises count=] by one.
Note: The [=ExtendableEvent/pending promises count=] is incremented even if the given promise has already been settled. The corresponding count decrement is done in the microtask queued by the reaction to the promise.
1. Upon [=upon fulfillment|fulfillment=] or [=upon rejection|rejection=] of |f|, [=queue a microtask=] to run these substeps:
1. Decrement the [=ExtendableEvent/pending promises count=] by one.
1. Let |registration| be the [=context object=]'s [=relevant global object=]'s associated [=ServiceWorkerGlobalScope/service worker=]'s [=containing service worker registration=].
1. If |registration|'s [=uninstalling flag=] is set, invoke [=Try Clear Registration=] with |registration|.
1. If |registration| is not null, invoke [=Try Activate=] with |registration|.
The user agent *should not* terminate the [=/service worker=] associated with |event|'s relevant settings object's [=environment settings object/global object=] when |event|'s [=dispatch flag=] is set or |event|'s [=ExtendableEvent/pending promises count=] is not zero. However, the user agent *may* impose a time limit to this lifetime extension.
install event and activate event, respectively:
* Adding a promise |f| to the event's extend lifetime promises delays treating the installing worker as *installed* (i.e. a waiting worker) until all the promises in the extend lifetime promises resolve successfully. (See step 11.3.1 of Install algorithm.) If |f| rejects, the installation fails. This is primarily used to ensure that a [=/service worker=] is not considered *installed* (i.e. a waiting worker) until all of the core caches it depends on are populated.
* Adding a promise to the event's extend lifetime promises delays treating the active worker as *activated* until all the promises in the extend lifetime promises settle. (See step 12.3 of Activate algorithm.) This is primarily used to ensure that any functional events are not dispatched to the {{ServiceWorkerGlobalScope}} object that represents the [=/service worker=] until it upgrades database schemas and deletes the outdated cache entries.
[Constructor(DOMString type, optional ExtendableEventInit eventInitDict), Exposed=ServiceWorker]
interface InstallEvent : ExtendableEvent {
void registerForeignFetch(ForeignFetchOptions options);
};
dictionary ForeignFetchOptions {
required sequence<USVString> scopes;
required sequence<USVString> origins;
};
registerForeignFetch(|options|) method *must* run these steps:
1. If the dispatch flag is unset, then:
1. Throw an "{{InvalidStateError}}" exception.
1. Abort these steps.
1. If |options|.{{ForeignFetchOptions/origins}} is empty throw a TypeError and abort these steps.
1. Let |originURLs| be an empty list of [=/URLs=].
1. If the value of |options|.{{ForeignFetchOptions/origins}} is not a single string equal to a single U+002A ASTERISK character (*):
1. For each |origin| in |options|.origins:
1. If the value of |origin| is not an absolute-URL string, throw a TypeError and abort these steps.
1. Add the result of parsing |origin| to |originURLs|.
1. If |options|.{{ForeignFetchOptions/scopes}} is empty throw a TypeError and abort these steps.
1. Let |scopeString| be the context object's relevant global object's [=ServiceWorkerGlobalScope/service worker=]'s containing service worker registration's [=service worker registration/scope url=], serialized.
1. Let |subScopeURLs| be an empty list of [=/URLs=].
1. For each |subScope| in |options|.{{ForeignFetchOptions/scopes}}:
1. Let |subScopeURL| be the result of parsing |subScope| with context object's relevant settings object's API base URL.
1. If |subScopeURL| is failure, throw a TypeError and abort these steps.
1. Let |subScopeString| be the serialized |subScopeURL|.
1. If |subScopeString| does not start with |scopeString|, throw a TypeError and abort these steps.
1. Add |subScopeURL| to |subScopeURLs|.
1. Set this [=/service worker=]'s list of foreign fetch scopes to |subScopeURLs|.
1. Set this [=/service worker=]'s list of foreign fetch origins to |originURLs|.
[Constructor(DOMString type, FetchEventInit eventInitDict), Exposed=ServiceWorker]
interface FetchEvent : ExtendableEvent {
[SameObject] readonly attribute Request request;
readonly attribute Promise<any> preloadResponse;
readonly attribute DOMString clientId;
readonly attribute DOMString reservedClientId;
readonly attribute DOMString targetClientId;
void respondWith(Promise<Response> r);
};
dictionary FetchEventInit : ExtendableEventInit {
required Request request;
required Promise<any> preloadResponse;
DOMString clientId = "";
DOMString reservedClientId = "";
DOMString targetClientId = "";
};
[=/Service workers=] have an essential functional event {{fetch!!event}}. For {{fetch!!event}} event, [=/service workers=] use the {{FetchEvent}} interface which extends the {{ExtendableEvent}} interface.
Each event using {{FetchEvent}} interface has an associated potential response (a [=/response=]), initially set to null, and the following associated flags that are initially unset:
* wait to respond flag
* respond-with entered flag
* respond-with error flag
request attribute *must* return the value it was initialized to.
preloadResponse attribute *must* return the value it was initialized to.
clientId attribute *must* return the value it was initialized to. When an event is created the attribute *must* be initialized to the empty string.
reservedClientId attribute *must* return the value it was initialized to. When an event is created the attribute *must* be initialized to the empty string.
targetClientId attribute *must* return the value it was initialized to. When an event is created the attribute *must* be initialized to the empty string.
respondWith(|r|) method *must* run these steps:
1. If the dispatch flag is unset, then:
1. Throw an "{{InvalidStateError}}" exception.
1. Abort these steps.
1. If the [=FetchEvent/respond-with entered flag=] is set, then:
1. Throw an "{{InvalidStateError}}" exception.
1. Abort these steps.
1. Add |r| to the extend lifetime promises.
1. Increment the [=ExtendableEvent/pending promises count=] by one.
Note: The [=ExtendableEvent/pending promises count=] is incremented even if the given promise has already been settled. The corresponding count decrement is done in the microtask queued by the reaction to the promise.
1. Upon [=upon fulfillment|fulfillment=] or [=upon rejection|rejection=] of |r|, [=queue a microtask=] to run these substeps:
1. Decrement the [=ExtendableEvent/pending promises count=] by one.
1. Let |registration| be the [=context object=]'s [=relevant global object=]'s associated [=ServiceWorkerGlobalScope/service worker=]'s [=containing service worker registration=].
1. If |registration|'s [=uninstalling flag=] is set, invoke [=Try Clear Registration=] with |registration|.
1. If |registration| is not null, invoke [=Try Activate=] with |registration|.
Note: {{FetchEvent/respondWith(r)|event.respondWith(r)}} extends the lifetime of the event by default as if {{ExtendableEvent/waitUntil()|event.waitUntil(r)}} is called.
1. Set the stop propagation flag and stop immediate propagation flag.
1. Set the [=FetchEvent/respond-with entered flag=].
1. Set the [=FetchEvent/wait to respond flag=].
1. Let |targetRealm| be the relevant Realm of the context object.
1. Run the following substeps in parallel:
1. Wait until |r| settles.
1. If |r| rejected, then:
1. Set the [=FetchEvent/respond-with error flag=].
1. If |r| resolved with |response|, then:
1. If |response| is a {{Response}} object, then:
1. If |response| is [=Body/disturbed=] or [=Body/locked=], then:
1. Set the [=FetchEvent/respond-with error flag=].
1. Else:
1. Let |bytes| be an empty byte sequence.
1. Let |end-of-body| be false.
1. Let |done| be false.
1. Let |potentialResponse| be a copy of |response|'s associated [=Response/response=], except for its [=response/body=].
1. If |response|'s [=response/body=] is non-null, run these substeps:
1. Let |reader| be the result of getting a reader from |response|'s [=response/body=]'s stream.
1. Let |strategy| be an object created in |targetRealm|. The user agent may choose any object.
1. Let |pull| be an action that runs these subsubsteps:
1. Let |promise| be the result of reading a chunk from |response|'s [=response/body=]'s stream with |reader|.
1. When |promise| is fulfilled with an object whose done property is false and whose value property is a Uint8Array object, append the bytes represented by the value property to |bytes| and perform ! DetachArrayBuffer with the ArrayBuffer object wrapped by the value property.
1. When |promise| is fulfilled with an object whose done property is true, set |end-of-body| to true.
1. When |promise| is fulfilled with a value that matches with neither of the above patterns, or |promise| is rejected, [=ReadableStream/error=] |newStream| with a TypeError.
1. Let |cancel| be an action that [=ReadableStream/cancels=] |response|'s [=response/body=]'s stream with |reader|.
1. Let |newStream| be the result of [=ReadableStream/construct a ReadableStream object=] with |strategy|, |pull| and |cancel| in |targetRealm|.
1. Set |potentialResponse|'s [=response/body=] to a new [=/body=] whose stream is |newStream|.
1. Run these subsubsteps repeatedly in parallel while |done| is false:
1. If |newStream| is errored, then set |done| to true.
1. Otherwise, if |bytes| is empty and |end-of-body| is true, then [=ReadableStream/close=] |newStream| and set |done| to true.
1. Otherwise, if |bytes| is not empty, run these subsubsubsteps:
1. Let |chunk| be a subsequence of |bytes| starting from the beginning of |bytes|.
1. Remove |chunk| from |bytes|.
1. Let |buffer| be an ArrayBuffer object created in |targetRealm| and containing |chunk|.
1. [=ReadableStream/Enqueue=] a Uint8Array object created in |targetRealm| and wrapping |buffer| to |newStream|.
Note: These substeps are meant to produce the observable equivalent of "piping" |response|'s [=response/body=]'s stream into |potentialResponse|.
1. Set the [=FetchEvent/potential response=] to |potentialResponse|.
1. Else:
1. Set the [=FetchEvent/respond-with error flag=].
Note: If the [=FetchEvent/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.)
1. Unset the [=FetchEvent/wait to respond flag=].
[Constructor(DOMString type, ForeignFetchEventInit eventInitDict), Exposed=ServiceWorker]
interface ForeignFetchEvent : ExtendableEvent {
[SameObject] readonly attribute Request request;
readonly attribute USVString origin;
void respondWith(Promise<ForeignFetchResponse> r);
};
dictionary ForeignFetchEventInit : ExtendableEventInit {
required Request request;
USVString origin = "null";
};
dictionary ForeignFetchResponse {
required Response response;
USVString origin;
sequence<ByteString> headers;
};
[=/Service workers=] have a functional event {{foreignfetch!!event}}. For {{foreignfetch!!event}} events, [=/service workers=] use the {{ForeignFetchEvent}} interface which extends the {{ExtendableEvent}} interface.
Each event using {{ForeignFetchEvent}} interface has an associated potential response (a [=/response=]), initially set to null, an associated origin (a {{USVString}} or null), initially set to null, an associated list of exposed headers (whose element type is a byte string), initially set to an empty list, and the following associated flags that are initially unset:
* wait to respond flag
* respond-with entered flag
* respond-with error flag
request attribute *must* return the value it was initialized to.
origin attribute *must* return the value it was initialized to.
respondWith(|r|) method *must* run these steps:
done property is false and whose value property is a Uint8Array object, append the bytes represented by the value property to |bytes| and perform ! DetachArrayBuffer with the ArrayBuffer object wrapped by the value property.
1. When |promise| is fulfilled with an object whose done property is true, set |end-of-body| to true.
1. When |promise| is fulfilled with a value that matches with neither of the above patterns, or |promise| is rejected, [=ReadableStream/error=] |newStream| with a TypeError.
1. Let |cancel| be an action that [=ReadableStream/cancels=] |response|.{{ForeignFetchResponse/response}}'s [=response/body=]'s stream with |reader|.
1. Let |newStream| be the result of [=ReadableStream/construct a ReadableStream object=] with |strategy|, |pull| and |cancel| in |targetRealm|.
1. Set |potentialResponse|'s [=response/body=] to a new [=/body=] whose stream is |newStream|.
1. Run these subsubsteps repeatedly in parallel while |done| is false:
1. If |newStream| is errored, then set |done| to true.
1. Otherwise, if |bytes| is empty and |end-of-body| is true, then [=ReadableStream/close=] |newStream| and set |done| to true.
1. Otherwise, if |bytes| is not empty, run these subsubsubsteps:
1. Let |chunk| be a subsequence of |bytes| starting from the beginning of |bytes|.
1. Remove |chunk| from |bytes|.
1. Let |buffer| be an ArrayBuffer object created in |targetRealm| and containing |chunk|.
1. [=ReadableStream/Enqueue=] a Uint8Array object created in |targetRealm| and wrapping |buffer| to |newStream|.
1. Set the [=ForeignFetchEvent/potential response=] to |potentialResponse|.
1. Else:
1. Set the [=ForeignFetchEvent/respond-with error flag=].
Note: If the [=ForeignFetchEvent/respond-with error flag=] is set, a network error is returned to [=/Fetch=] through [=Handle Foreign Fetch=] algorithm. (See the step 19.1.) Otherwise, a filtered version of |response| is returned to [=/Fetch=] through [=Handle Foreign Fetch=] algorithm. (See the step 20.1.)
1. Unset the [=ForeignFetchEvent/wait to respond flag=].
[Constructor(DOMString type, optional ExtendableMessageEventInit eventInitDict), Exposed=ServiceWorker]
interface ExtendableMessageEvent : ExtendableEvent {
readonly attribute any data;
readonly attribute USVString origin;
readonly attribute DOMString lastEventId;
[SameObject] readonly attribute (Client or ServiceWorker or MessagePort)? source;
readonly attribute FrozenArray<MessagePort> ports;
};
dictionary ExtendableMessageEventInit : ExtendableEventInit {
any data = null;
USVString origin = "";
DOMString lastEventId = "";
(Client or ServiceWorker or MessagePort)? source = null;
sequence<MessagePort> ports = [];
};
[=/Service workers=] define the extendable {{message!!event}} event to allow extending the lifetime of the event. For the {{message!!event}} event, [=/service workers=] use the {{ExtendableMessageEvent}} interface which extends the {{ExtendableEvent}} interface.
| Event name | Interface | Dispatched when… |
|---|---|---|
install |
{{InstallEvent}} | [Lifecycle event] The [=ServiceWorkerGlobalScope/service worker=]'s containing service worker registration's installing worker changes. (See step 11.2 of the Install algorithm.) |
activate |
{{ExtendableEvent}} | [Lifecycle event] The [=ServiceWorkerGlobalScope/service worker=]'s containing service worker registration's active worker changes. (See step 12.2 of the Activate algorithm.) |
fetch |
{{FetchEvent}} | [Functional event] The [=/http fetch=] invokes Handle Fetch with |request|. As a result of performing Handle Fetch, the [=ServiceWorkerGlobalScope/service worker=] returns a [=/response=] to the [=/http fetch=]. The [=/response=], represented by a {{Response}} object, can be retrieved from a {{Cache}} object or directly from network using {{WindowOrWorkerGlobalScope/fetch(input, init)|self.fetch(input, init)}} method. (A custom {{Response}} object can be another option.) |
foreignfetch |
{{FetchEvent}} | [Functional event] The [=/http fetch=] invokes [=Handle Foreign Fetch=] with |request|. As a result of performing [=Handle Foreign Fetch=], the [=ServiceWorkerGlobalScope/service worker=] returns a [=/response=] to the [=/http fetch=]. The [=/response=], represented by a {{Response}} object, can be retrieved from a {{Cache}} object or directly from network using {{WindowOrWorkerGlobalScope/fetch(input, init)|self.fetch(input, init)}} method. (A custom {{Response}} object can be another option.) |
message |
{{ExtendableMessageEvent}} | When it receives a message. |
Link header
Link: <sw.js>; rel="serviceworker"; scope="/"; workertype="module"; updateviacache="all"
Link headerLink header that contains a serviceworker link, the user agent *should* run these steps:
1. If the Link header has an "anchor" parameter, abort these steps.
1. Let |contextURL| be the result of parsing the context IRI of the Link header.
1. If the result of running potentially trustworthy origin with the [=environment settings object/origin=] of |contextURL| is Not Trusted, abort these steps.
1. Let |request| be the [=/request=] for which this header was received in the response.
1. If |request|'s [=request/client=] is not a secure context, abort these steps.
1. Let |scriptURL| be the result of parsing the target IRI of the Link header.
1. Let |scopeURL| be null.
1. If the "scope" target attribute of the Link header is present, set |scopeURL| to the result of parsing the "scope" target attribute with |scriptURL|.
1. Let |workerType| be the "workertype" target attribute of the Link header, or "classic" if no such attribute is present.
1. If |workerType| is not a valid {{WorkerType}} value, abort these steps.
1. Let |useCache| be true if the Link header has a target attribute named "usecache", otherwise false.
1. Invoke [=Start Register=] with |scopeURL|, |scriptURL|, a new promise, null, |contextURL|, |workerType|, and |useCache|.
error at the <{link}> element, and abort these steps.
1. Let |scriptURL| be the result of parsing the <{link/href}> attribute with the <{link}> element's node document's document base URL.
1. Let |scopeURL| be null.
1. If the <{link/scope}> attribute is present, set |scopeURL| to the result of parsing the <{link/scope}> attribute with the <{link}> element's node document's document base URL.
1. Let |workerType| be the state of the <{link/workertype}> attribute.
1. Let |useCache| be true if the <{link}> element has a <{link/usecache}> attribute, otherwise false.
1. Let |promise| be a new promise.
1. Invoke [=Start Register=] with |scopeURL|, |scriptURL|, |promise|, |client|, |client|'s creation URL, |workerType|, and |useCache|.
1. Run the following substeps in parallel:
1. Wait until |promise| settles.
1. If |promise| rejected, queue a task to fire an event named error at the <{link}> element.
1. If |promise| resolved, queue a task to fire an event named load at the <{link}> element.
The serviceworker link element *must not* [=document/delay the load event=] of the element's node document.
Link: </js/sw.js>; rel="serviceworker"; scope="/"
has more or less the same effect as a document being loaded in a secure context with the following
link element:
<link rel="serviceworker" href="/js/sw.js" scope="/">
which is more or less equivalent to the page containing javascript code like:
navigator.serviceworker.register("/js/sw.js", { scope: "/" });
partial interface WindowOrWorkerGlobalScope {
[SecureContext, SameObject] readonly attribute CacheStorage caches;
};
caches attribute *must* return this object's associated {{CacheStorage}} object.
[SecureContext, Exposed=(Window,Worker)]
interface Cache {
[NewObject] Promise<any> 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 documents and workers can all be associated with the same request to response map simultaneously.
{{Cache}} objects are always enumerable via {{WindowOrWorkerGlobalScope/caches|self.caches}} in insertion order (per ECMAScript 6 Map objects).
match(|request|, |options|) method *must* run these steps:
1. Let |promise| be a new promise.
1. Run these substeps in parallel:
1. Let |p| be the result of running the algorithm specified in {{Cache/matchAll(request, options)}} method with |request| and |options| as the arguments.
1. Wait until |p| settles.
1. If |p| rejects with an exception, then:
1. Reject |promise| with that exception.
1. Else if |p| resolves with an array, |responseArray|, then:
1. If |responseArray| is an empty array, then:
1. Resolve |promise| with undefined.
1. Else:
1. Resolve |promise| with the first element of |responseArray|.
1. Return |promise|.
matchAll(|request|, |options|) method *must* run these steps:
1. Let |r| be null.
1. If the optional argument |request| is not omitted, then:
1. If |request| is a {{Request}} object, then:
1. Set |r| to |request|'s [=Request/request=].
1. If |r|'s [=request/method=] is not \`GET\` and |options|.ignoreMethod is false, return a promise resolved with an empty array.
1. Else if |request| is a string, then:
1. Set |r| to the associated [=Request/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.
1. Let |promise| be a new promise.
1. Run these substeps in parallel:
1. Let |responseArray| be an empty array.
1. 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|.
1. Resolve |promise| with |responseArray|.
1. Abort these steps.
1. Else:
1. Let |entries| be the result of running Query Cache algorithm passing a {{Request}} object associated with |r| and |options| as the arguments.
1. For each |entry| of |entries|:
1. Let |response| be null.
1. If the incumbent record |incumbentRecord| of the corresponding fetching record in request to response map is not null, set |response| to a copy of |incumbentRecord|.\[[value]].
1. Else, set |response| to a copy of |entry|[1].
1. Add |response| to |responseArray|.
1. Resolve |promise| with |responseArray|.
1. Return |promise|.
add(|request|) method *must* run these steps:
1. Let |requests| be an array containing only |request|.
1. Set |responseArrayPromise| to the result of running the algorithm specified in {{Cache/addAll(requests)}} passing |requests| as the argument.
1. Return the result of transforming |responseArrayPromise| with a fulfillment handler that returns undefined.
addAll(|requests|) method *must* run these steps:
1. Let |responsePromiseArray| be an empty array.
1. Let |requestArray| be an empty array.
1. For each |request| whose type is {{Request}} in |requests|:
1. Let |r| be |request|'s [=Request/request=].
1. If |r|'s [=request/url=]'s [=url/scheme=] is not one of "http" and "https", or |r|'s [=request/method=] is not \`GET\`, return a promise rejected with a TypeError.
1. For each |request| in |requests|:
1. Let |r| be the associated [=Request/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.
1. If |r|'s [=request/url=]'s [=url/scheme=] is not one of "http" and "https", then:
1. [=fetch/Terminate=] all the ongoing fetches initiated by |requests| with reason *fatal*.
1. Break the loop.
1. If |r|'s [=request/client=]'s [=environment settings object/global object=] is a {{ServiceWorkerGlobalScope}} object, set |request|'s [=service-workers mode=] to "`foreign`".
1. Set |r|'s [=request/initiator=] to "fetch" and [=request/destination=] to "subresource".
1. Add a {{Request}} object associated with |r| to |requestArray|.
1. Let |responsePromise| be a new promise.
1. Run the following substeps in parallel:
* [=/Fetch=] |r|.
* To process response for |response|, run these substeps:
1. If |response|'s [=response/type=] is "error", or |response|'s [=response/status=] is not an ok status, reject |responsePromise| with a TypeError.
1. Else if |response|'s [=response/header list=] contains a header [=header/named=] \`Vary\`, then:
1. Let |varyHeaders| be the array containing the elements corresponding to the [=http/field-values=] of the Vary header.
1. Let |matchAsterisk| be false.
1. For each |f| in |varyHeaders|:
1. If |f| matches "*", set |matchAsterisk| to true and break the loop.
1. If |matchAsterisk| is true, reject |responsePromise| with a TypeError.
1. Else, resolve |responsePromise| with a new {{Response}} object associated with |response| and a new {{Headers}} object whose guard is "immutable".
1. Else, resolve |responsePromise| with a new {{Response}} object associated with |response| and a new {{Headers}} object whose guard is "immutable".
Note: This step ensures that the promise for this fetch resolves as soon as the response's headers become available.
* To process response end-of-body for |response|, do nothing.
* To process response done for |response|, do nothing.
1. Add |responsePromise| to |responsePromiseArray|.
1. Let |p| be waiting for all of |responsePromiseArray|.
1. 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.
1. For each |response| in |responseArray| with the index |index|:
1. Let |o| be an empty object representing a {{CacheBatchOperation}} dictionary.
1. Set the {{CacheBatchOperation/type}} dictionary member of |o| to "put".
1. Set the {{CacheBatchOperation/request}} dictionary member of |o| to |requestArray|[|index|].
1. Set the {{CacheBatchOperation/response}} dictionary member of |o| to |response|.
1. Add |o| to |operations|.
1. Let |resultPromise| be the result of running Batch Cache Operations algorithm passing |operations| as the argument.
1. 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.
1. For each |response| in |responses|:
1. Let |responseBodyPromise| be a new promise.
1. Run the following substeps in parallel:
1. Wait for either end-of-file to have been pushed to |response|'s associated [=Response/response=] |r|'s [=response/body=] or for |r| to have a [=response/termination reason=].
1. If |r| had a [=response/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|.
1. Else:
1. Delete |fetchingRecord| from request to response map.
1. Reject |responseBodyPromise| with a TypeError.
1. Else:
1. Set the incumbent record of the corresponding fetching record |fetchingRecord| in request to response map to the copy of |fetchingRecord|.
1. Let |invalidRecords| be the result of running Query Cache algorithm passing |fetchingRecord|.\[[key]] as the argument.
1. For each |invalidRecord| in |invalidRecords|:
1. If |invalidRecord| is not |fetchingRecord|, delete it from request to response map.
1. Resolve |responseBodyPromise| with |response|.
1. Add |responseBodyPromise| to |responseBodyPromiseArray|.
1. Let |q| be waiting for all of |responseBodyPromiseArray|.
1. Return the result of transforming |q| with a fulfillment handler that returns undefined.
put(|request|, |response|) method *must* run these steps:
1. Let |r| be null.
1. If |request| is a {{Request}} object, then:
1. Set |r| to |request|'s [=Request/request=].
1. If |r|'s [=request/url=]'s [=url/scheme=] is not one of "http" and "https", or |r|'s [=request/method=] is not \`GET\`, return a promise rejected with a TypeError.
1. Else if |request| is a string, then:
1. Set |r| to the associated [=Request/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.
1. If |r|'s [=request/url=]'s [=url/scheme=] is not one of "http" and "https", return a promise rejected with a TypeError.
1. If |response|'s associated [=Response/response=]'s [=response/header list=] contains a header [=header/named=] \`Vary\`, then:
1. Let |varyHeaders| be the array containing the elements corresponding to the [=http/field-values=] of the Vary header.
1. For each |f| in |varyHeaders|:
1. If |f| matches "*", return a promise rejected with a TypeError.
1. If |response| is [=Body/disturbed=] or [=Body/locked=], return a promise rejected with a TypeError.
1. Let |newResponse| be a new {{Response}} object associated with |response|'s associated [=/response=] and a new {{Headers}} object whose guard is |response|'s {{Headers}}' guard.
1. If |response|'s body is non-null, run these substeps:
1. Let |dummyStream| be an empty {{ReadableStream}} object.
1. Set |response|'s [=response/body=] to a new [=/body=] whose stream is |dummyStream|.
1. Let |reader| be the result of getting a reader from |dummyStream|.
1. Read all bytes from |dummyStream| with |reader|.
1. Let |operations| be an empty array.
1. Let |o| be an empty object representing a {{CacheBatchOperation}} dictionary.
1. Set the {{CacheBatchOperation/type}} dictionary member of |o| to "put".
1. Set the {{CacheBatchOperation/request}} dictionary member of |o| to a {{Request}} object associated with |r|.
1. Set the {{CacheBatchOperation/response}} dictionary member of |o| to |newResponse|.
1. Add |o| to |operations|.
1. Let |resultPromise| be the result of running Batch Cache Operations passing |operations| as the argument.
1. 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/response=] |r|'s [=response/body=] or for |r| to have a [=response/termination reason=].
1. If |r| had a [=response/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|.
1. Else:
1. Delete |fetchingRecord| from request to response map.
1. Throw a TypeError.
1. Else:
1. Set the incumbent record of the corresponding fetching record |fetchingRecord| in request to response map to the copy of |fetchingRecord|.
1. Let |invalidRecords| be the result of running Query Cache algorithm passing |fetchingRecord|.\[[key]] as the argument.
1. For each |invalidRecord| in |invalidRecords|:
1. If |invalidRecord| is not |fetchingRecord|, delete it from request to response map.
1. Return undefined.
delete(|request|, |options|) method *must* run these steps:
1. Let |r| be null.
1. If |request| is a {{Request}} object, then:
1. Set |r| to |request|'s [=Request/request=].
1. If |r|'s [=request/method=] is not \`GET\` and |options|.ignoreMethod is false, return a promise resolved with false.
1. Else if |request| is a string, then:
1. Set |r| to the associated [=Request/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.
1. Let |operations| be an empty array.
1. Let |o| be an empty object representing a {{CacheBatchOperation}} dictionary.
1. Set the {{CacheBatchOperation/type}} dictionary member of |o| to "delete".
1. Set the {{CacheBatchOperation/request}} dictionary member of |o| to a {{Request}} object associated with |r|.
1. Set the {{CacheBatchOperation/options}} dictionary member of |o| to |options|.
1. Add |o| to |operations|.
1. Let |resultPromise| be the result of running Batch Cache Operations passing |operations| as the argument.
1. 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.
1. Else, return false.
keys(|request|, |options|) method *must* run these steps:
1. Let |promise| be a new promise.
1. Run these substeps in parallel:
1. Let |resultArray| be an empty array.
1. 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|.
1. Else:
1. Let |r| be null.
1. If |request| is a {{Request}} object, then:
1. Set |r| to |request|'s [=Request/request=].
1. If |r|'s [=request/method=] is not \`GET\` and |options|.ignoreMethod is false, resolve |promise| with an empty array.
1. Else if |request| is a string, then:
1. Set |r| to the associated [=Request/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.
1. Let |requestResponseArray| be the result of running Query Cache algorithm passing a {{Request}} object that represents |r| and |options| as the arguments.
1. For each |requestResponse| in |requestResponseArray|:
1. Add |requestResponse|[0] to |resultArray|.
1. Resolve |promise| with |resultArray|.
1. Return |promise|.
[SecureContext, Exposed=(Window,Worker)]
interface CacheStorage {
[NewObject] Promise<any> 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();
};
Note: {{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 agent *must* create a {{CacheStorage}} object when a {{Window}} object or a {{WorkerGlobalScope}} object is created and associate it with that global object.
A {{CacheStorage}} object represents a name to cache map of its associated [=CacheStorage/global object=]'s environment settings object's [=environment settings object/origin=]. Multiple separate objects implementing the {{CacheStorage}} interface across documents and workers can all be associated with the same name to cache map simultaneously.
match(|request|, |options|) method *must* run these steps:
1. If |options|.{{CacheQueryOptions/cacheName}} is present, then:
1. Return a new promise |p| and run the following substeps in parallel:
1. [=map/For each=] |cacheName| → |cache| of name to cache map:
1. If |options|.{{CacheQueryOptions/cacheName}} matches |cacheName|, then:
1. Resolve |p| with the result of running the algorithm specified in {{Cache/match(request, options)}} method of {{Cache}} interface with |request| and |options| as the arguments (providing |cache| as thisArgument to the \[[Call]] internal method of {{Cache/match(request, options)}}.)
1. Abort these steps.
1. Resolve |p| with undefined.
1. Else:
1. Let |p| be a promise resolved with undefined.
1. [=map/For each=] cacheName → |cache| of name to cache map:
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|.
1. Return the result of running the algorithm specified in {{Cache/match(request, options)}} method of {{Cache}} interface with |request| and |options| as the arguments (providing |cache|] as thisArgument to the \[[Call]] internal method of {{Cache/match(request, options)}}.)
1. Return |p|.
has(|cacheName|) method *must* run these steps:
1. Return a promise resolved with the result of running the following substeps:
1. [=map/For each=] |key| → value of name to cache map:
1. If |cacheName| matches |key|, then:
1. Return true.
1. Return false.
open(|cacheName|) method *must* run these steps:
1. Let |p| be a new promise.
1. Run the following substeps:
1. [=map/For each=] |key| → |value| of name to cache map:
1. If |cacheName| matches |key|, then:
1. Resolve |p| with a new {{Cache}} object which is a copy of |value|.
1. Abort these steps.
1. Let |cache| be a new {{Cache}} object.
1. [=map/Set=] name to cache map[|cacheName|] to |cache|. If this cache write operation failed due to exceeding the granted quota limit, reject |p| with a "{{QuotaExceededError}}" exception and abort these steps.
1. Resolve |p| with |cache|.
1. Return |p|.
delete(|cacheName|) method *must* run these steps:
1. Let |p| be the result of running the algorithm specified in {{CacheStorage/has(cacheName)}} method with |cacheName| as the argument.
1. 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 from its name to cache map where |cacheName| matches entry.\[[key]].
1. Return true.
1. Abort these steps.
Note: After this step, the existing DOM objects (i.e. the currently referenced Cache, Request, and Response objects) should remain functional.
1. Else:
1. Return false.
keys() method *must* run these steps:
Note: The promise returned from this method resolves with the sequence of keys, cache names in DOMString, in insertion order.
1. Let |cacheKeys| be the result of [=map/get the keys|getting the keys=] of name to cache map.
1. Return |cacheKeys|.
localhost, 127.0.0.0/8, and ::1/128 for development purpose. (Note that they may still be secure contexts.) The primary reason for this restriction is to protect users from the risks associated with insecure contexts.
Content-Security-Policy HTTP header containing the value |policy|, the user agent *must* enforce |policy| for |serviceWorker|.
* If |serviceWorker|'s script resource was delivered with a Content-Security-Policy-Report-Only HTTP header containing the value |policy|, the user agent *must* monitor |policy| for |serviceWorker|.
The primary reason for this restriction is to mitigate a broad class of content injection vulnerabilities, such as cross-site scripting (XSS).
importScripts(|urls|) method is called on a {{ServiceWorkerGlobalScope}} object, the user agent *must* import scripts into worker global scope, given this {{ServiceWorkerGlobalScope}} object and |urls|, and with the following steps to [=fetching scripts/perform the fetch=] given the [=/request=] |request|:
1. Let |serviceWorker| be |request|'s [=request/client=]'s [=environment settings object/global object=]'s [=ServiceWorkerGlobalScope/service worker=].
1. If |serviceWorker|'s imported scripts updated flag is unset, then:
1. Let |registration| be |serviceWorker|'s [=containing service worker registration=].
1. Set |request|'s [=service-workers mode=] to "`foreign`".
1. Set |request|'s [=request/cache mode=] to "no-cache" if any of the following are true:
* |registration|'s [=service worker registration/use cache=] is false.
* The [=current global object=]'s [=force bypass cache for importscripts flag=] is set.
* |registration|'s [=last update check time=] is not null and the time difference in seconds calculated by the current time minus |registration|’s [=last update check time=] is greater than 86400.
1. Let |response| be the result of fetching |request|.
1. If |response|’s cache state is not "local", set |registration|’s [=service worker registration/last update check time=] to the current time.
1. [=Extract a MIME type=] from the |response|'s [=unsafe response=]'s [=response/header list=]. If this MIME type (ignoring parameters) is not a [=JavaScript MIME type=], return a [=network error=].
1. If |response|'s unsafe response's [=response/type=] is not "error", and |response|'s [=response/status=] is an ok status, then:
1. [=map/Set=] script resource map[|request|'s [=request/url=]] to |response|.
1. Return |response|.
1. Else:
1. If script resource map[|url|] [=map/exists=], return script resource map[|url|].
1. Else, return a network error.
<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-[=environment settings object/origin=] resources when appropriate CORS headers are set.
[=/Service workers=] enable this by allowing {{Cache|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 [=Response/responses=] are basic filtered response, the objects stored are {{Response}} objects whose corresponding [=Response/responses=] are either CORS filtered responses or opaque filtered responses. They can be passed to {{FetchEvent/respondWith(r)|event.respondWith(r)}} method in the same manner as the {{Response}} objects whose corresponding [=Response/responses=] are basic filtered responses, but cannot be meaningfully created programmatically. These limitations are necessary to preserve the security invariants of the platform. Allowing {{Cache|Caches}} to store them allows applications to avoid re-architecting in most cases.
<embed> or <object>) immediately fallback to the network without dispatching {{fetch!!event}} event.
* Some of the legacy networking stack code may need to be carefully audited to understand the ramifications of interactions with [=/service workers=].
partial interface ServiceWorkerRegistration {
// e.g. define an API namespace
readonly attribute APISpaceType APISpace;
// e.g. define a method
Promise<T> methodName(/* list of arguments */);
};
// e.g. define FunctionalEvent interface
interface FunctionalEvent : ExtendableEvent {
// add a functional event's own attributes and methods
};
partial interface ServiceWorkerGlobalScope {
attribute EventHandler onfunctionalevent;
};
classic" or "module").
A job has a use cache (a boolean).
A job has a client (a [=/service worker client=]). It is initially null.
A job has a referrer (a [=/URL=] or null).
A job has a job promise (a promise). It is initially null.
A job has a list of equivalent jobs (a list of jobs). It is initially the empty list.
A job has a force bypass cache flag. It is initially unset.
TypeError and abort these steps.
1. If |scriptURL|'s [=url/scheme=] is not one of "http" and "https", reject |promise| with a TypeError and abort these steps.
1. If any of the strings in |scriptURL|'s [=url/path=] contains either ASCII case-insensitive "%2f" or ASCII case-insensitive "%5c", reject |promise| with a TypeError and abort these steps.
1. If |scopeURL| is null, set |scopeURL| to the result of parsing the string "./" with |scriptURL|.
Note: The scope url for the registration is set to the location of the service worker script by default.
1. If |scopeURL| is failure, reject |promise| with a TypeError and abort these steps.
1. If |scopeURL|'s [=url/scheme=] is not one of "http" and "https", reject |promise| with a TypeError and abort these steps.
1. If any of the strings in |scopeURL|'s [=url/path=] contains either ASCII case-insensitive "%2f" or ASCII case-insensitive "%5c", reject |promise| with a TypeError and abort these steps.
1. Let |job| be the result of running [=Create Job=] with *register*, |scopeURL|, |scriptURL|, |promise|, and |client|.
1. Set |job|'s [=job/worker type=] to |workerType|.
1. Set |job|'s [=job/use cache=] to |useCache|.
1. Set |job|'s [=job/referrer=] to |referrer|.
1. Invoke [=Schedule Job=] with |job|.
Not Trusted, then:
1. Invoke Reject Job Promise with |job| and a "{{SecurityError}}" exception.
1. Invoke Finish Job with |job| and abort these steps.
1. If the [=environment settings object/origin=] of |job|'s [=job/script url=] is not |job|'s [=job/referrer=]'s [=environment settings object/origin=], then:
1. Invoke Reject Job Promise with |job| and a "{{SecurityError}}" exception.
1. Invoke Finish Job with |job| and abort these steps.
1. If the [=environment settings object/origin=] of |job|'s [=job/scope url=] is not |job|'s [=job/referrer=]'s [=environment settings object/origin=], then:
1. Invoke Reject Job Promise with |job| and a "{{SecurityError}}" exception.
1. Invoke Finish Job with |job| and abort these steps.
1. Let |registration| be the result of running the Get Registration algorithm passing |job|'s [=job/scope url=] as the argument.
1. If |registration| is not null, then:
1. If |registration|'s uninstalling flag is set, unset it.
1. Let |newestWorker| be the result of running the Get Newest Worker algorithm passing |registration| as the argument.
1. If |newestWorker| is not null, |job|'s [=job/script url=] [=url/equals=] |newestWorker|'s [=service worker/script url=] with the *exclude fragments flag* set, and |job|'s [=job/use cache=]'s value equals |registration|'s [=service worker registration/use cache=]'s value, then:
1. Invoke Resolve Job Promise with |job| and the {{ServiceWorkerRegistration}} object which represents |registration|.
1. Invoke Finish Job with |job| and abort these steps.
1. Else:
1. Invoke Set Registration algorithm with |job|'s [=job/scope url=] and |job|'s [=job/use cache=].
1. Invoke Update algorithm passing |job| as the argument.
TypeError.
1. Invoke Finish Job with |job| and abort these steps.
1. Let |newestWorker| be the result of running Get Newest Worker algorithm passing |registration| as the argument.
1. If |job|'s job type is *update*, and |newestWorker|'s [=service worker/script url=] does not [=url/equal=] |job|'s [=job/script url=] with the *exclude fragments flag* set, then:
1. Invoke Reject Job Promise with |job| and a TypeError.
1. Invoke Finish Job with |job| and abort these steps.
1. Let |httpsState| be "none".
1. Let |referrerPolicy| be the empty string.
1. Switching on |job|'s worker type, run these substeps with the following options:
: "classic"
:: Fetch a classic worker script given |job|’s serialized [=job/script url=], |job|’s [=job/client=], "serviceworker", and the to-be-created environment settings object for this service worker.
: "module"
:: Fetch a module worker script graph given |job|’s serialized [=job/script url=], |job|’s [=job/client=], "serviceworker", "omit", and the to-be-created environment settings object for this service worker.
To [=fetching scripts/perform the fetch=] given |request|, run the following steps:
1. Append \`Service-Worker\`/\`script\` to |request|'s [=request/header list=].
Note: See the definition of the Service-Worker header in Appendix B: Extended HTTP headers.
1. Set |request|'s [=request/cache mode=] to "no-cache" if any of the following are true:
* |registration|'s [=service worker registration/use cache=] is false.
* |job|'s [=force bypass cache flag=] is set.
* |newestWorker| is not null, and |registration|'s [=last update check time=] is not null and the time difference in seconds calculated by the current time minus |registration|’s [=last update check time=] is greater than 86400.
Note: Even if the cache mode is not set to "no-cache", the user agent obeys Cache-Control header's max-age value in the network layer to determine if it should bypass the browser cache.
1. Set |request|'s [=service-workers mode=] to "`none`".
1. If the [=fetching scripts/is top-level=] flag is unset, then return the result of [=/fetching=] |request|.
1. Append \`Service-Worker\`/\`script\` to |request|'s [=request/header list=].
Note: See the definition of the Service-Worker header in Appendix B: Extended HTTP headers.
1. Set |request|'s [=request/redirect mode=] to "error".
1. [=/Fetch=] |request|, and asynchronously wait to run the remaining steps as part of fetch's process response for the [=/response=] |response|.
1. [=Extract a MIME type=] from the |response|'s [=response/header list=]. If this MIME type (ignoring parameters) is not a [=JavaScript MIME type=], then:
1. Invoke [=Reject Job Promise=] with |job| and a "{{SecurityError}}" exception.
1. Asynchronously complete these steps with a [=network error=].
1. Let |serviceWorkerAllowed| be the result of [=extracting header list values=] given \`Service-Worker-Allowed\` and |response|'s [=response/header list=].
Note: See the definition of the Service-Worker-Allowed header in Appendix B: Extended HTTP headers.
1. Set |httpsState| to |response|'s [=response/HTTPS state=].
1. Set |referrerPolicy| to the result of parse a referrer policy from a Referrer-Policy header of |response|.
1. If |serviceWorkerAllowed| is failure, then:
1. Asynchronously complete these steps with a network error.
1. Let |scopeURL| be |registration|'s [=service worker registration/scope url=].
1. Let |maxScopeString| be null.
1. 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 |job|'s [=job/script url=]'s [=url/path=] (including empty strings), separated from each other by "/".
1. Else:
1. Let |maxScope| be the result of parsing |serviceWorkerAllowed| with |job|'s [=job/script url=].
1. Set |maxScopeString| to "/" concatenated with the strings in |maxScope|'s [=url/path=] (including empty strings), separated from each other by "/".
1. Let |scopeString| be "/" concatenated with the strings in |scopeURL|'s [=url/path=] (including empty strings), separated from each other by "/".
1. If |scopeString| starts with |maxScopeString|, do nothing.
1. Else:
1. Invoke Reject Job Promise with |job| and a "{{SecurityError}}" exception.
1. Asynchronously complete these steps with a network error.
1. If |response|'s cache state is not "local", set |registration|'s last update check time to the current time.
Issue: The response's cache state concept had been removed from fetch. The fetch issue #376 tracks the request to restore the concept or add some similar way to check this state.
1. Return true.
If the algorithm asynchronously completes with null, then:
1. Invoke Reject Job Promise with |job| and a TypeError.
Note: This will do nothing if Reject Job Promise was previously invoked with a "{{SecurityError}}" exception.
1. If |newestWorker| is null, invoke Clear Registration algorithm passing |registration| as its argument.
1. Invoke Finish Job with |job| and abort these steps.
Else, continue the rest of these steps after the algorithm's asynchronous completion, with |script| being the asynchronous completion value.
1. If |newestWorker| is not null, |newestWorker|'s [=service worker/script url=] [=url/equals=] |job|'s [=job/script url=] with the *exclude fragments flag* set, and |script|'s [=source text=] is a byte-for-byte match with |newestWorker|'s [=script resource=]'s [=source text=], if |script| is a [=classic script=], and |script|'s [=module script/module record=]'s \[[ECMAScriptCode]] is a byte-for-byte match with |newestWorker|'s [=script resource=]'s [=module script/module record=]'s \[[ECMAScriptCode]] otherwise, then:
1. Invoke Resolve Job Promise with |job| and the {{ServiceWorkerRegistration}} object which represents |registration|.
1. Invoke Finish Job with |job| and abort these steps.
1. Else:
1. Let |worker| be a new [=/service worker=].
1. Generate a unique opaque string and set |worker|'s id to the value.
1. Set |worker|'s [=service worker/script url=] to |job|'s [=job/script url=], |worker|'s script resource to |script|, and |worker|'s type to |job|'s worker type.
1. Set |worker|'s script resource's HTTPS state to |httpsState|.
1. Set |worker|'s script resource's [=script resource/referrer policy=] to |referrerPolicy|.
1. Invoke Run Service Worker algorithm given |worker|, and with the *force bypass cache for importscripts flag* set if |job|'s [=job/force bypass cache flag=] is set.
1. If an uncaught runtime script error occurs during the above step, then:
1. Invoke Reject Job Promise with |job| and a TypeError.
1. If |newestWorker| is null, invoke Clear Registration algorithm passing |registration| as its argument.
1. Invoke Finish Job with |job| and abort these steps.
1. Invoke Install algorithm with |job|, |worker|, and |registration| as its arguments.
installing" and |worker| as the arguments.
1. Run the Update Worker State algorithm passing |registration|'s installing worker and *installing* as the arguments.
1. Assert: |job|'s [=job/job promise=] is not null.
1. Invoke Resolve Job Promise with |job| and the {{ServiceWorkerRegistration}} object which represents |registration|.
1. Queue a task to fire an event named updatefound at all the {{ServiceWorkerRegistration}} objects for all the [=/service worker clients=] whose creation URL matches |registration|'s [=service worker registration/scope url=] and all the [=/service workers=] whose containing service worker registration is |registration|.
1. Let |installingWorker| be |registration|'s installing worker.
1. Invoke Run Service Worker algorithm given |installingWorker|, and with the *force bypass cache for importscripts flag* set if |job|'s [=job/force bypass cache flag=] is set.
1. Queue a task |task| to run the following substeps:
1. Let |e| be the result of creating an event with {{InstallEvent}}.
1. Initialize |e|’s {{Event/type}} attribute to {{install!!event}}.
1. Dispatch |e| at |installingWorker|'s [=service worker/global object=].
1. *WaitForAsynchronousExtensions*: Run the following substeps in parallel:
1. Wait until |e|'s [=ExtendableEvent/pending promises count=] is zero.
1. If the result of waiting for all of |e|'s extend lifetime promises rejected, set |installFailed| to true.
If |task| is discarded or the script has been aborted by the termination of |installingWorker|, set |installFailed| to true.
1. Wait for |task| to have executed or been discarded.
1. Wait for the step labeled *WaitForAsynchronousExtensions* to complete.
1. If |installFailed| is true, then:
1. Run the Update Worker State algorithm passing |registration|'s [=installing worker=] and *redundant* as the arguments.
1. Run the Update Registration State algorithm passing |registration|, "installing" and null as the arguments.
1. If |newestWorker| is null, invoke Clear Registration algorithm passing |registration| as its argument.
1. Invoke Finish Job with |job| and abort these steps.
1. Set |registration|'s installing worker's imported scripts updated flag.
1. If |registration|'s waiting worker is not null, then:
1. [=Terminate Service Worker|Terminate=] |registration|'s [=waiting worker=].
1. Run the [=Update Worker State=] algorithm passing |registration|'s [=waiting worker=] and *redundant* as the arguments.
1. Run the Update Registration State algorithm passing |registration|, "waiting" and |registration|'s installing worker as the arguments.
1. Run the Update Registration State algorithm passing |registration|, "installing" and null as the arguments.
1. Run the Update Worker State algorithm passing |registration|'s waiting worker and *installed* as the arguments.
1. Invoke Finish Job with |job|.
1. Wait for all the tasks queued by Update Worker State invoked in this algorithm have executed.
1. Invoke [=Try Activate=] with |registration|.
Note: If [=Try Activate=] does not trigger [=Activate=] here, [=Activate=] is tried again when the last client controlled by the existing [=active worker=] is [=Handle Service Worker Client Unload|unloaded=], {{ServiceWorkerGlobalScope/skipWaiting()}} is asynchronously called, or the [=extend lifetime promises=] for the existing [=active worker=] settle.
active" and |registration|'s waiting worker as the arguments.
1. Run the Update Registration State algorithm passing |registration|, "waiting" and null as the arguments.
1. Run the Update Worker State algorithm passing |registration|'s active worker and *activating* as the arguments.
Note: Once an active worker is activating, neither a runtime script error nor a force termination of the active worker prevents the active worker from getting activated.
1. For each [=/service worker client=] |client| whose creation URL matches |registration|'s [=service worker registration/scope url=]:
1. If |client| is a window client, unassociate |client|'s responsible document from its application cache, if it has one.
1. Else if |client| is a shared worker client, unassociate |client|'s [=environment settings object/global object=] from its application cache, if it has one.
Note: Resources will now use the service worker registration instead of the existing application cache.
1. For each [=/service worker client=] |client| who is using |registration|:
1. Set |client|'s active worker to |registration|'s active worker.
1. Invoke Notify Controller Change algorithm with |client| as the argument.
1. Let |activeWorker| be |registration|'s active worker.
1. Invoke Run Service Worker algorithm with |activeWorker| as the argument.
1. Queue a task |task| to run the following substeps:
1. Let |e| be the result of creating an event with {{ExtendableEvent}}.
1. Initialize |e|’s {{Event/type}} attribute to {{activate!!event}}.
1. Dispatch |e| at |activeWorker|'s [=service worker/global object=].
1. *WaitForAsynchronousExtensions*: Wait, in parallel, until |e|'s [=ExtendableEvent/pending promises count=] is zero.
1. Wait for |task| to have executed or been discarded, or the script to have been aborted by the termination of |activeWorker|.
1. Wait for the step labeled *WaitForAsynchronousExtensions* to complete.
1. Run the Update Worker State algorithm passing |registration|'s active worker and *activated* as the arguments.
serviceworker".
1. If |request| is a potential-navigation-or-subresource request, then:
1. Return null.
1. Else if |request| is a non-subresource request, then:
Note: If the non-subresource request is under the scope of a service worker registration, application cache is completely bypassed regardless of whether the non-subresource request uses the service worker registration.
1. If |reservedClient| is not null and is an environment settings object, then:
1. If |reservedClient| is not a secure context, return null.
1. Else:
1. If |request|'s [=request/url=] is not a potentially trustworthy URL, return null.
1. If |request| is a navigation request and the navigation triggering it was initiated with a shift+reload or equivalent, return null.
1. Set |registration| to the result of running Match Service Worker Registration algorithm passing |request|'s [=request/url=] as the argument.
1. If |registration| is null or |registration|'s active worker is null, return null.
1. If |request|'s [=request/destination=] is not {{RequestDestination/"report"}}, set |reservedClient|'s active service worker to |registration|'s active worker.
1. If |request| is a [=navigation request=], |registration|'s [=navigation preload enabled flag=] is set, |request|'s [=request/method=] is \`GET\`, and |registration|'s [=active worker=]'s set of event types to handle [=set/contains=] fetch, then:
Note: If the above is true except |registration|'s [=active worker=]'s set of event types to handle **does not** contain fetch, then the user agent may wish to show a console warning, as the developer's intent isn't clear.
1. Let |preloadRequest| be the result of [=request/cloning=] the request |request|.
1. Let |preloadRequestHeaders| be |preloadRequest|'s [=request/header list=].
1. Let |preloadResponseObject| be a new {{Response}} object associated with a new {{Headers}} object whose [=guard=] is "`immutable`".
1. [=header list/Append=] to |preloadRequestHeaders| a new [=header=] whose [=header/name=] is \`Service-Worker-Navigation-Preload\` and [=header/value=] is |registration|'s [=navigation preload header value=].
1. Set |preloadRequest|'s [=service-workers mode=] to "`none`".
1. Run the following substeps [=in parallel=]:
1. [=Fetch=] |preloadRequest|.
To [=process response=] for |navigationPreloadResponse|, run these substeps:
1. If |navigationPreloadResponse|'s [=response/type=] is "`error`", reject |preloadResponse| with a `TypeError` and terminate these substeps.
1. Associate |preloadResponseObject| with |navigationPreloadResponse|.
1. Resolve |preloadResponse| with |navigationPreloadResponse|.
1. Else, resolve |preloadResponse| with undefined.
Note: From this point, the [=/service worker client=] starts to use its active service worker's containing service worker registration.
1. Else if |request| is a subresource request, then:
1. If |client|'s active service worker is non-null, set |registration| to |client|'s active service worker's containing service worker registration.
1. Else, return null.
1. Let |activeWorker| be |registration|'s active worker.
1. If |activeWorker|'s set of event types to handle does not [=set/contain=] fetch, then:
1. Return null and continue running these steps in parallel.
1. If |request| is a non-subresource request, or |request| is a subresource request and 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 with |registration|.
1. Abort these steps.
Note: 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 very first script execution.
1. If |activeWorker|'s state is *activating*, wait for |activeWorker|'s state to become *activated*.
1. Invoke Run Service Worker algorithm with |activeWorker| as the argument.
1. Queue a task |task| to run the following substeps:
1. Let |e| be the result of creating an event with {{FetchEvent}}.
1. Initialize |e|’s {{Event/type}} attribute to {{fetch!!event}}.
1. Initialize |e|’s {{Event/cancelable}} attribute to true.
1. Initialize |e|’s {{FetchEvent/request}} attribute to |r|.
1. Initialize |e|’s {{FetchEvent/preloadResponse}} to |preloadResponse|.
1. Initialize |e|'s {{FetchEvent/clientId}} attribute to |client|'s [=environment/id=].
1. If |request| is a non-subresource request and |request|'s [=request/destination=] is not {{RequestDestination/"report"}}, initialize |e|'s {{FetchEvent/reservedClientId}} attribute to |reservedClient|'s [=environment/id=], and to the empty string otherwise.
1. If |request| is a navigation request, initialize |e|'s {{FetchEvent/targetClientId}} attribute to |request|'s [=request/target client id=], and to the empty string otherwise.
1. Dispatch |e| at |activeWorker|'s [=service worker/global object=].
1. Invoke [=Update Service Worker Extended Events Set=] with |activeWorker| and |e|.
1. If |e|'s [=FetchEvent/respond-with entered flag=] is set, set |respondWithEntered| to true.
1. If |e|'s [=FetchEvent/wait to respond flag=] is set, then:
1. Wait until |e|'s [=FetchEvent/wait to respond flag=] is unset.
1. If |e|'s [=FetchEvent/respond-with error flag=] is set, set |handleFetchFailed| to true.
1. Else, set |response| to |e|'s [=FetchEvent/potential response=].
1. If |e|'s canceled flag is set, 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.
1. Wait for |task| to have executed or been discarded.
1. If |respondWithEntered| is false, then:
1. If |eventCanceled| is true, return a network error and continue running these steps in parallel.
1. Else, return null and continue running these steps in parallel.
1. If |request| is a non-subresource request, or |request| is a subresource request and 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 with |registration|.
1. Abort these steps.
1. If |handleFetchFailed| is true, then:
1. Return a network error and continue running these steps in parallel.
1. If |request| is a non-subresource request, or |request| is a subresource request and 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 with |registration|.
1. Else:
1. Return |response| and continue running these steps in parallel.
1. If |request| is a non-subresource request, or |request| is a subresource request and 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 with |registration|.
"opaque", set |response| to an opaque filtered response of |internalResponse|.
1. Else:
1. Let |headers| be |e|'s list of exposed headers.
1. If |response| is a CORS filtered response, remove from |internalResponse|'s CORS-exposed header-name list all values not in |headers|.
1. Else set |internalResponse|'s CORS-exposed header-name list to |headers|.
1. Set |response| to a CORS filtered response of |internalResponse|.
1. If |e|'s canceled flag is set, 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.
1. Wait for |task| to have executed or been discarded.
1. If |respondWithEntered| is false, then:
1. If |eventCanceled| is true, then:
1. Return a network error.
1. Else:
1. Return null.
1. If |handleFetchFailed| is true, then:
1. Return a network error.
1. Else:
1. Return |response|.
installing" and null as the arguments.
1. If |registration|'s waiting worker is not null, then:
1. [=Terminate Service Worker|Terminate=] |registration|'s [=waiting worker=].
1. Run the Update Worker State algorithm passing |registration|'s [=waiting worker=] and *redundant* as the arguments.
1. Run the Update Registration State algorithm passing |registration|, "waiting" and null as the arguments.
1. If |registration|'s active worker is not null, then:
1. [=Terminate Service Worker|Terminate=] |registration|'s [=active worker=].
1. Run the Update Worker State algorithm passing |registration|'s [=active worker=] and *redundant* as the arguments.
1. Run the Update Registration State algorithm passing |registration|, "active" and null as the arguments.
1. Let |scopeString| be serialized |registration|'s [=service worker registration/scope url=] with the *exclude fragment flag* set.
1. [=map/Remove=] scope to registration map[|scopeString|].
installing", "waiting", and "active")
:: |source|, a [=/service worker=] or null
: Output
:: None
1. Let |registrationObjects| be an array containing all the {{ServiceWorkerRegistration}} objects associated with |registration|.
1. If |target| is "installing", then:
1. Set |registration|'s installing worker to |source|.
1. For each |registrationObject| in |registrationObjects|:
1. Queue a task to set the {{ServiceWorkerRegistration/installing}} attribute of |registrationObject| to the {{ServiceWorker}} object that represents |registration|’s installing worker, or null if |registration|’s installing worker is null.
1. Else if |target| is "waiting", then:
1. Set |registration|'s waiting worker to |source|.
1. For each |registrationObject| in |registrationObjects|:
1. Queue a task to set the {{ServiceWorkerRegistration/waiting}} attribute of |registrationObject| to the {{ServiceWorker}} object that represents |registration|’s waiting worker, or null if |registration|’s waiting worker is null.
1. Else if |target| is "active", then:
1. Set |registration|'s [=service worker registration/active worker=] to |source|.
1. For each |registrationObject| in |registrationObjects|:
1. Queue a task to set the {{ServiceWorkerRegistration/active}} attribute of |registrationObject| to the {{ServiceWorker}} object that represents |registration|’s [=service worker registration/active worker=], or null if |registration|’s [=service worker registration/active worker=] is null.
The task *must* use |registrationObject|'s relevant settings object's responsible event loop and the DOM manipulation task source.
statechange at |workerObject|.
The task *must* use |workerObject|'s relevant settings object's responsible event loop and the DOM manipulation task source.
controllerchange at the {{ServiceWorkerContainer}} object that |client| is [=ServiceWorkerContainer/service worker client|associated=] with.
The task *must* use |client|'s responsible event loop and the DOM manipulation task source.
GET\`, return |resultArray|.
1. Let |cachedURL| and |requestURL| be null.
1. 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/request=]'s [=request/url=].
1. Set |requestURL| to |request|'s associated [=Request/request=]'s [=request/url=].
1. If |options|.ignoreSearch is true, then:
1. Set |cachedURL|'s [=url/query=] to the empty string.
1. Set |requestURL|'s [=url/query=] to the empty string.
1. If |cachedURL| [=url/equals=] |requestURL| with the *exclude fragments flag* set, then:
1. Add a copy of |entry|.\[[key]] to |requestArray|.
1. Add a copy of |entry|.\[[value]] to |responseArray|.
1. Else:
1. For each |record| in |targetStorage|:
1. Set |cachedURL| to |record|[0]'s associated [=Request/request=]'s [=request/url=].
1. Set |requestURL| to |request|'s associated [=Request/request=]'s [=request/url=].
1. If |options|.{{CacheQueryOptions/ignoreSearch}} is true, then:
1. Set |cachedURL|'s [=url/query=] to the empty string.
1. Set |requestURL|'s [=url/query=] to the empty string.
1. If |cachedURL| [=url/equals=] |requestURL| with the *exclude fragments flag* set, then:
1. Add |record|[0] to |requestArray|.
1. Add |record|[1] to |responseArray|.
1. For each |cachedResponse| in |responseArray| with the index |index|:
1. Let |cachedRequest| be the |index|th element in |requestArray|.
1. If |cachedResponse|'s [=Response/response=]'s [=response/header list=] contains no header [=header/named=] \`Vary\`, or |options|.{{CacheQueryOptions/ignoreVary}} is true, then:
1. Add an array [|cachedRequest|, |cachedResponse|] to |resultArray|.
1. Continue to the next iteration of the loop.
1. Let |varyHeaders| be the array containing the elements corresponding to the [=http/field-values=] of the Vary header.
1. Let |matchFailed| be false.
1. For each |f| in |varyHeaders|:
1. If |f| matches "*", or the result of running |cachedRequest|.{{Request/headers}} object's {{Headers/get(name)}} method with |f| as the argument does not match the result of running |request|.{{Request/headers}} object's {{Headers/get(name)}} method with |f| as the argument, then:
1. Set |matchFailed| to true.
1. Break the loop.
1. If |matchFailed| is false, add an array [|cachedRequest|, |cachedResponse|] to |resultArray|.
1. Return |resultArray|.
TypeError.
1. If |operation|.{{CacheBatchOperation/type}} matches "delete" and |operation|.{{CacheBatchOperation/response}} is not null, throw a TypeError.
1. If the result of running Query Cache algorithm passing |operation|.{{CacheBatchOperation/request}}, |operation|.{{CacheBatchOperation/options}}, and |addedRecords| as the arguments is not an empty array, throw an "{{InvalidStateError}}" exception.
1. Let |requestResponseArray| be the result of running Query Cache algorithm passing |operation|.{{CacheBatchOperation/request}} and |operation|.{{CacheBatchOperation/options}} as the arguments.
1. For each |requestResponse| in |requestResponseArray|:
1. If |operation|.{{CacheBatchOperation/type}} matches "delete", remove the corresponding fetching record from request to response map.
1. If |operation|.{{CacheBatchOperation/type}} matches "put", then:
1. If |operation|.{{CacheBatchOperation/response}} is null, throw a TypeError.
1. Let |r| be |operation|.{{CacheBatchOperation/request}}'s associated [=Request/request=].
1. If |r|'s [=request/url=]'s [=url/scheme=] is not one of "http" and "https", throw a TypeError.
1. If |r|'s [=request/method=] is not \`GET\`, throw a TypeError.
1. If |operation|.{{CacheBatchOperation/options}} is not null, throw a TypeError.
1. Set |requestResponseArray| to the result of running Query Cache algorithm passing |operation|.{{CacheBatchOperation/request}}.
1. If |requestResponseArray| is not an empty array, then:
1. Let |requestResponse| be |requestResponseArray|[0].
1. Let |fetchingRecord| be the corresponding fetching record for |requestResponse|[0] and |requestResponse|[1] in request to response map.
1. Set |fetchingRecord|.\[[key]] to |operation|.{{CacheBatchOperation/request}} and |fetchingRecord|.\[[value]] to |operation|.{{CacheBatchOperation/response}}.
1. Else:
1. Set a newly-created fetching record {\[[key]]: |operation|.{{CacheBatchOperation/request}}, \[[value]]: |operation|.{{CacheBatchOperation/response}}} to request to response map.
Note: The cache commit is allowed as long as the response's headers are available.
1. If the cache write operation in the previous two steps failed due to exceeding the granted quota limit, throw a "{{QuotaExceededError}}" exception.
1. Add an array [|operation|.request, |operation|.response] to |addedRecords|.
1. Add |operation|.response to |resultArray|.
1. Return |resultArray|.
1. And then, if an exception was thrown, then:
1. Set the context object's request to response map to |itemsCopy|.
1. Throw the exception
Service-Worker\`
:: Indicates this request is a [=/service worker=]'s script resource request.
Note: This header helps administrators log the requests and detect threats.
Service-Worker-Allowed\`
:: Indicates the user agent will override the path restriction, which limits the maximum allowed [=service worker registration/scope url=] that the script can control, to the given value.
Note: The value is a URL. If a relative URL is given, it is parsed against the script's URL.
// Maximum allowed scope defaults to the path the script sits in
// "/js" in this example
navigator.serviceWorker.register("/js/sw.js").then(() => {
console.log("Install succeeded with the default scope '/js'.");
});
// Set the scope to an upper path of the script location
// Response has no Service-Worker-Allowed header
navigator.serviceWorker.register("/js/sw.js", { scope: "/" }).catch(() => {
console.error("Install failed due to the path restriction violation.");
});
// Set the scope to an upper path of the script location
// Response included "Service-Worker-Allowed : /"
navigator.serviceWorker.register("/js/sw.js", { scope: "/" }).then(() => {
console.log("Install succeeded as the max allowed scope was overriden to '/'.");
});
// Set the scope to an upper path of the script location
// Response included "Service-Worker-Allowed : /foo"
navigator.serviceWorker.register("/foo/bar/sw.js", { scope: "/" }).catch(() => {
console.error("Install failed as the scope is still out of the overriden maximum allowed scope.");
});
Service-Worker = %x73.63.72.69.70.74 ; "script", case-sensitive
Note: The validation of the Service-Worker-Allowed header's values is done by URL parsing algorithm (in Update algorithm) instead of using ABNF.