From 7e132a872e89a79a387019516757153b185a3504 Mon Sep 17 00:00:00 2001 From: Mike Pennisi Date: Fri, 10 Mar 2017 16:59:00 -0500 Subject: [PATCH] Discourage race condition in Service Workers Invoke the ExtendableEvent's `waitUntil` method in order to discourage user agents from terminating service workers while asynchronous tests are running (which would interfere with test execution). Although the user agent is still free to terminate workers when execution "exceeds imposed time limits," this precaution should prevent interference for the most common cases. > # 2.1.1. Lifetime > > The lifetime of a service worker is tied to the execution lifetime of > events and not references held by service worker clients to the > ServiceWorker object. > > A user agent may terminate service workers at any time it: > > - Has no event to handle. > - Detects abnormal operation: such as infinite loops and tasks exceeding > imposed time limits (if any) while handling the events. https://w3c.github.io/ServiceWorker/#service-worker-lifetime --- testharness.js | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/testharness.js b/testharness.js index ee842e4..2599ee2 100644 --- a/testharness.js +++ b/testharness.js @@ -412,6 +412,8 @@ policies and contribution forms [3]. this.all_loaded = false; this.on_loaded_callback = null; var this_obj = this; + var releaseInstallHold = null; + self.addEventListener("message", function(event) { if (event.data.type && event.data.type === "connect") { @@ -439,11 +441,44 @@ policies and contribution forms [3]. // necessary to wait until the onactivate event. on_event(self, "install", function(event) { + + // The ServiceWorker specification allows user agents to + // terminate idle service workers (i.e. workers that have + // no active or pending event handlers) at any time. In + // order for this class to function as designed, the same + // object must be present in the runtime throughout the + // duration of the test. The user agent can be discouraged + // from terminating service workers if any event handlers + // have been extended with an unsettled promise. While + // extending the `install` lifecycle event in this way + // would serve this end, it would also prevent transition + // into subsequent phases, possibly interfering with the + // behavior under test. The `message` event does not suffer + // from this problem. + // + // By interleaving handlers for the two events, the service + // worker can avoid entering an idle state without altering + // its own lifecycle. + event.waitUntil(new Promise(function(resolve) { + releaseInstallHold = resolve; + self.registration.installing.postMessage("testharness.js:releaseInstallHold"); + })); + this_obj.all_loaded = true; if (this_obj.on_loaded_callback) { this_obj.on_loaded_callback(); } }); + + on_event(self, "message", + function(event) { + if (event.data === "testharness.js:releaseInstallHold") { + event.waitUntil(new Promise(function(resolve) { + add_completion_callback(resolve); + })); + releaseInstallHold(); + } + }); } ServiceWorkerTestEnvironment.prototype = Object.create(WorkerTestEnvironment.prototype);