From 433ab5062573e5841aa3fc7c8a77890ab4731682 Mon Sep 17 00:00:00 2001 From: Matt Falkenhagen Date: Thu, 6 Jun 2019 17:40:51 +0900 Subject: [PATCH] run service worker --- docs/index.bs | 147 +++++++++++++++++++++++++++++--------------------- 1 file changed, 85 insertions(+), 62 deletions(-) diff --git a/docs/index.bs b/docs/index.bs index 4db18b77..d2912f89 100644 --- a/docs/index.bs +++ b/docs/index.bs @@ -175,6 +175,8 @@ spec: webappsec-referrer-policy; urlPrefix: https://w3c.github.io/webappsec-refe A [=/service worker=] has an associated set of extended events (a [=ordered set|set=]) whose [=list/item=] is an {{ExtendableEvent}}. It is initially a new [=ordered set|set=]. + A [=/service worker=] is said to be running if its [=event loop=] is running and its [=service worker/global object=]'s [=WorkerGlobalScope/closing=] flag is false. +

Lifetime

@@ -377,32 +379,34 @@ spec: webappsec-referrer-policy; urlPrefix: https://w3c.github.io/webappsec-refe 1. If the {{ServiceWorker/state}} attribute value of the context object is {{"redundant"}}, [=throw=] an "{{InvalidStateError}}" {{DOMException}}. 1. Let |serviceWorker| be the [=/service worker=] represented by the context object. 1. If the result of running the [=Should Skip Event=] algorithm with "message" and |serviceWorker|, is true, then return. - 1. Invoke Run Service Worker algorithm with |serviceWorker| as the argument. - 1. Let |incumbentSettings| be the incumbent settings object, and |incumbentGlobal| its [=environment settings object/global object=]. - 1. Let |serializeWithTransferResult| be StructuredSerializeWithTransfer(|message|, |options|.transfer). Rethrow any exceptions. - 1. [=Queue a task=] on the [=DOM manipulation task source=] to run the following steps: - 1. Let |source| be determined by switching on the type of |incumbentGlobal|: -
-
{{ServiceWorkerGlobalScope}}
-
a new {{ServiceWorker}} object that represents |incumbentGlobal|'s [=ServiceWorkerGlobalScope/service worker=].
- -
{{Window}}
-
a new {{WindowClient}} object that represents |incumbentGlobal|'s [=relevant settings object=].
- -
Otherwise
-
a new {{Client}} object that represents |incumbentGlobal|'s associated worker -
- 1. Let |origin| be the [=Unicode serialization of an origin|Unicode serialization=] of |incumbentSettings|'s [=environment settings object/origin=]. - 1. Let |destination| be the {{ServiceWorkerGlobalScope}} object associated with |serviceWorker|. - 1. Let |deserializeRecord| be StructuredDeserializeWithTransfer(|serializeWithTransferResult|, |destination|'s [=global object/Realm=]). - - If this throws an exception, catch it, [=fire an event=] named {{messageerror!!event}} at |destination|, using {{MessageEvent}}, with the {{MessageEvent/origin}} attribute initialized to |origin| and the {{MessageEvent/source}} attribute initialized to |source|, and then abort these steps. - 1. Let |messageClone| be |deserializeRecord|.\[[Deserialized]]. - 1. Let |newPorts| be a new [=frozen array type|frozen array=] consisting of all {{MessagePort}} objects in |deserializeRecord|.\[[TransferredValues]], if any, maintaining their relative order. - 1. Let |e| be the result of [=creating an event=] named {{message!!event}}, using {{ExtendableMessageEvent}}, with the {{ExtendableMessageEvent/origin}} attribute initialized to |origin|, the {{ExtendableMessageEvent/source}} attribute initialized to |source|, the {{ExtendableMessageEvent/data}} attribute initialized to |messageClone|, and the {{ExtendableMessageEvent/ports}} attribute initialized to |newPorts|. - 1. [=Dispatch=] |e| at |destination|. - 1. Invoke [=Update Service Worker Extended Events Set=] with |serviceWorker| and |e|. -
+ 1. Run these substeps [=in parallel=]: + 1. Run the [=Run Service Worker=] algorithm with |serviceWorker|. + 1. If |serviceWorker| is not [=running=], return. + 1. Let |incumbentSettings| be the incumbent settings object, and |incumbentGlobal| its [=environment settings object/global object=]. + 1. Let |serializeWithTransferResult| be StructuredSerializeWithTransfer(|message|, |options|.transfer). Rethrow any exceptions. + 1. [=Queue a task=] on the [=DOM manipulation task source=] using |serviceWorker|'s [=event loop=] to run the following steps: + 1. Let |source| be determined by switching on the type of |incumbentGlobal|: +
+
{{ServiceWorkerGlobalScope}}
+
a new {{ServiceWorker}} object that represents |incumbentGlobal|'s [=ServiceWorkerGlobalScope/service worker=].
+ +
{{Window}}
+
a new {{WindowClient}} object that represents |incumbentGlobal|'s [=relevant settings object=].
+ +
Otherwise
+
a new {{Client}} object that represents |incumbentGlobal|'s associated worker +
+ 1. Let |origin| be the [=Unicode serialization of an origin|Unicode serialization=] of |incumbentSettings|'s [=environment settings object/origin=]. + 1. Let |destination| be the {{ServiceWorkerGlobalScope}} object associated with |serviceWorker|. + 1. Let |deserializeRecord| be StructuredDeserializeWithTransfer(|serializeWithTransferResult|, |destination|'s [=global object/Realm=]). + + If this throws an exception, catch it, [=fire an event=] named {{messageerror!!event}} at |destination|, using {{MessageEvent}}, with the {{MessageEvent/origin}} attribute initialized to |origin| and the {{MessageEvent/source}} attribute initialized to |source|, and then abort these steps. + 1. Let |messageClone| be |deserializeRecord|.\[[Deserialized]]. + 1. Let |newPorts| be a new [=frozen array type|frozen array=] consisting of all {{MessagePort}} objects in |deserializeRecord|.\[[TransferredValues]], if any, maintaining their relative order. + 1. Let |e| be the result of [=creating an event=] named {{message!!event}}, using {{ExtendableMessageEvent}}, with the {{ExtendableMessageEvent/origin}} attribute initialized to |origin|, the {{ExtendableMessageEvent/source}} attribute initialized to |source|, the {{ExtendableMessageEvent/data}} attribute initialized to |messageClone|, and the {{ExtendableMessageEvent/ports}} attribute initialized to |newPorts|. + 1. [=Dispatch=] |e| at |destination|. + 1. Invoke [=Update Service Worker Extended Events Set=] with |serviceWorker| and |e|. +

Event handler

@@ -2066,7 +2070,7 @@ spec: webappsec-referrer-policy; urlPrefix: https://w3c.github.io/webappsec-refe

Content Security Policy

- Whenever a user agent invokes Run Service Worker algorithm with a [=/service worker=] |serviceWorker|: + Whenever a user agent invokes the [=Run Service Worker=] algorithm with a [=/service worker=] |serviceWorker|: * If |serviceWorker|'s script resource was delivered with a 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|. @@ -2565,12 +2569,12 @@ spec: webappsec-referrer-policy; urlPrefix: https://w3c.github.io/webappsec-refe 1. Append |url| to |worker|'s [=set of used scripts=]. 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|, with the *force bypass cache for importscripts flag* set if |job|'s [=job/force bypass cache flag=] is set, and with the following callback steps given |evaluationStatus|: - 1. If |evaluationStatus| is an [=abrupt completion=] or |evaluationStatus|.\[[Value]] is empty, then: - 1. Invoke [=Reject Job Promise=] with |job| and `TypeError`. - 1. If |newestWorker| is null, invoke [=Clear Registration=] algorithm passing |registration| as its argument. - 1. Invoke [=Finish Job=] with |job|. - 1. Else, invoke [=Install=] algorithm with |job|, |worker|, and |registration| as its arguments. + 1. Set |evaluationStatus| to the result of running the [=Run Service Worker=] algorithm with |worker| and the *force bypass cache for importscripts flag* set if |job|'s [=job/force bypass cache flag=] is set. + 1. If |evaluationStatus| is an [=abrupt completion=] or |evaluationStatus|.\[[Value]] is empty, then: + 1. Invoke [=Reject Job Promise=] with |job| and `TypeError`. + 1. If |newestWorker| is null, invoke [=Clear Registration=] algorithm passing |registration| as its argument. + 1. Invoke [=Finish Job=] with |job|. + 1. Else, invoke [=Install=] algorithm with |job|, |worker|, and |registration| as its arguments.
@@ -2618,19 +2622,21 @@ spec: webappsec-referrer-policy; urlPrefix: https://w3c.github.io/webappsec-refe 1. For each |registrationObject| of |registrationObjects|, [=fire an event=] on |registrationObject| named `updatefound`. 1. Let |installingWorker| be |registration|'s installing worker. 1. If the result of running the [=Should Skip Event=] algorithm with |installingWorker| and "install" is false, then: - 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 {{ExtendableEvent}}. - 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| is not [=ExtendableEvent/active=]. - 1. If |e|'s [=ExtendableEvent/timed out flag=] is set, or 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. Run the [=Run Service Worker=] algorithm with |installingWorker| and the *force bypass cache for importscripts flag* set if |job|'s [=job/force bypass cache flag=] is set. + 1. If |installingWorker| is not [=running=], then set |installFailed| to true. + 1. Else: + 1. [=Queue a task=] |task| on |installingWorker|'s [=event loop=] using the [=DOM manipulation task source=] to run the following steps: + 1. Let |e| be the result of creating an event with {{ExtendableEvent}}. + 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| is not [=ExtendableEvent/active=]. + 1. If |e|'s [=ExtendableEvent/timed out flag=] is set, or 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. @@ -2686,14 +2692,15 @@ spec: webappsec-referrer-policy; urlPrefix: https://w3c.github.io/webappsec-refe 1. Invoke Notify Controller Change algorithm with |client| as the argument. 1. Let |activeWorker| be |registration|'s active worker. 1. If the result of running the [=Should Skip Event=] algorithm with |activeWorker| and "activate" is false, then: - 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| is not [=ExtendableEvent/active=]. - 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 [=Run Service Worker=] algorithm with |activeWorker|. + 1. If |activeWorker| is [=running=], then: + 1. [=Queue a task=] |task| on |activeWorker|'s [=event loop=] using the [=DOM manipulation task source=] to run the following steps: + 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| is not [=ExtendableEvent/active=]. + 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.
@@ -2721,12 +2728,25 @@ spec: webappsec-referrer-policy; urlPrefix: https://w3c.github.io/webappsec-refe : Input :: |serviceWorker|, a [=/service worker=] :: *force bypass cache for importscripts flag*, an optional flag unset by default - :: optional |callbackSteps|, an algorithm which takes a [=Completion=] + : Output + :: a [=Completion=] + +
+ Note: This algorithm typically blocks until initial script evaluation of the service worker finishes, and returns when the service worker starts its event loop. The return value may be: + * an evaluation status whose \[[Value]] is empty if the worker was not attempted to start at all because the worker was redundant or the [=run a classic script=] or [=run a module script=] algorithm determined not to start the worker. + * an evaluation status whose \[[Value]] is a "QuotaExceededError" DOMException if the script was [=abort a running script|aborted prematurely=] during evaluation. In this case, the worker's [=global object=]'s [=WorkerGlobalScope/closing=] flag is true. + * some other [=abrupt completion=] if, e.g., a uncaught exception occurred during evalution. + * a normal completion. + + This specification generally treats an [=abrupt completion=] due to an uncaught exception the same as a normal completion. That is, if the worker throws an exception during initial script evaluation, it is still considering running and can receive events. However, some callsites distinguish between them using the return value. Other callsites simply check if the service worker is [=running=] after this algorithm returns. +
1. Let |script| be |serviceWorker|'s [=service worker/script resource=]. 1. Assert: |script| is not null. - 1. If |serviceWorker| is already running, this algorithm must have been invoked previously. If |callbackSteps| is provided, run them with the same value as the previous time, and abort these steps. - 1. Create a separate parallel execution environment (i.e. a separate thread or process or equivalent construct), and run the following substeps in that context: + 1. If |serviceWorker| is already running, this algorithm must have been invoked previously. Return the same value as the previous time. + 1. If |serviceWorker|'s [=state=] is *redundant*, then return NormalCompletion(empty). + 1. Let |evaluationStatus| be null. + 1. Create a separate parallel execution environment (i.e. a separate thread or process or equivalent construct), and run the following substeps in that context [=in parallel=]: 1. Call the JavaScript [=InitializeHostDefinedRealm|InitializeHostDefinedRealm()=] abstract operation with the following customizations: * For the global object, create a new {{ServiceWorkerGlobalScope}} object. Let |workerGlobalScope| be the created object. * Let |realmExecutionContext| be the created [=execution context|JavaScript execution context=]. @@ -2761,9 +2781,6 @@ spec: webappsec-referrer-policy; urlPrefix: https://w3c.github.io/webappsec-refe 1. Create a new {{WorkerLocation}} object and associate it with |workerGlobalScope|. 1. If |serviceWorker| is an active worker, and there are any tasks queued in |serviceWorker|'s containing service worker registration's [=service worker registration/task queues=], queue them to |serviceWorker|'s event loop's [=/task queues=] in the same order using their original task sources. 1. Let |evaluationStatus| be the result of running the classic script |script| if |script| is a classic script, otherwise, the result of running the module script |script| if |script| is a [=module script=]. - - Note: In addition to the usual possibilities of returning a value or failing due to an exception, this could be prematurely aborted by the terminate service worker algorithm. - 1. If |callbackSteps| is provided, run them with |evaluationStatus| on the original thread that invoked this algorithm, while continuing in parallel with these steps. 1. If |script|'s has ever been evaluated flag is unset, then: 1. For each |eventType| of |settingsObject|'s [=environment settings object/global object=]'s associated list of event listeners' event types: 1. [=set/Append=] |eventType| to |workerGlobalScope|'s associated [=ServiceWorkerGlobalScope/service worker=]'s set of event types to handle. @@ -2773,6 +2790,8 @@ spec: webappsec-referrer-policy; urlPrefix: https://w3c.github.io/webappsec-refe 1. Set |script|'s has ever been evaluated flag. 1. Run the responsible event loop specified by |settingsObject| until it is destroyed. 1. Empty |workerGlobalScope|'s list of active timers. + 1. Wait for |serviceWorker|'s [=event loop=] to start running, or to be destroyed, whichever comes first. + 1. Return |evaluationStatus|.
@@ -2861,7 +2880,8 @@ spec: webappsec-referrer-policy; urlPrefix: https://w3c.github.io/webappsec-refe 1. If |shouldSoftUpdate| is true, then [=in parallel=] run the [=Soft Update=] algorithm with |registration|. 1. Return null. 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. Run the [=Run Service Worker=] algorithm with |activeWorker|. + 1. If |activeWorker| is not [=running=], then return null. 1. Queue a task |task| to run the following substeps: 1. Let |e| be the result of creating an event with {{FetchEvent}}. 1. Let |requestObject| be a new {{Request}} object associated with |request| and a new associated {{Headers}} object whose [=guard=] is "`immutable`". @@ -2925,7 +2945,10 @@ spec: webappsec-referrer-policy; urlPrefix: https://w3c.github.io/webappsec-refe 1. If |registration| is [=stale=], then [=in parallel=] run the [=Soft Update=] algorithm with |registration|. 2. Return. 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. Run the [=Run Service Worker=] algorithm with |activeWorker|. + 1. If |activeWorker| is not [=running=], then: + 1. If |registration| is [=stale=], then [=in parallel=] run the [=Soft Update=] algorithm with |registration|. + 2. Return. 1. [=Queue a task=] |task| to run these substeps: 1. Let |event| be the result of [=creating an event=] with |eventConstructor| and the [=relevant realm=] of |activeWorker|'s [=service worker/global object=]. 1. If |initialization| is not null, then initialize |event| with |initialization|.