1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
|
<!--
Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/
-->
<!DOCTYPE HTML>
<html>
<head>
<title>Test install event being GC'd before waitUntil fulfills</title>
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
</head>
<body>
<script class="testbody" type="text/javascript">
var script = 'blocking_install_event_worker.js';
var scope = 'sw_clients/simple.html?install-event-gc';
var registration;
function register() {
return navigator.serviceWorker.register(script, { scope: scope })
.then(swr => registration = swr);
}
function unregister() {
if (!registration) {
return;
}
return registration.unregister();
}
function waitForInstallEvent() {
return new Promise((resolve, reject) => {
navigator.serviceWorker.addEventListener('message', evt => {
if (evt.data.type === 'INSTALL_EVENT') {
resolve();
}
});
});
}
function gcWorker() {
return new Promise(function(resolve, reject) {
// We are able to trigger asynchronous garbage collection and cycle
// collection by emitting "child-cc-request" and "child-gc-request"
// observer notifications. The worker RuntimeService will translate
// these notifications into the appropriate operation on all known
// worker threads.
//
// In the failure case where GC/CC causes us to abort the installation,
// we will know something happened from the statechange event.
const statechangeHandler = evt => {
// Reject rather than resolving to avoid the possibility of us seeing
// an unrelated racing statechange somehow. Since in the success case we
// will still see a state change on termination, we do explicitly need to
// be removed on the success path.
ok(registration.installing, 'service worker is still installing?');
reject();
};
registration.installing.addEventListener('statechange', statechangeHandler);
// In the success case since the service worker installation is effectively
// hung, we instead depend on sending a 'ping' message to the service worker
// and hearing it 'pong' back. Since we issue our postMessage after we
// trigger the GC/CC, our 'ping' will only be processed after the GC/CC and
// therefore the pong will also strictly occur after the cycle collection.
navigator.serviceWorker.addEventListener('message', evt => {
if (evt.data.type === 'pong') {
registration.installing.removeEventListener(
'statechange', statechangeHandler);
resolve();
}
});
// At the current time, the service worker will exist in our same process
// and notifyObservers is synchronous. However, in the future, service
// workers may end up in a separate process and in that case it will be
// appropriate to use notifyObserversInParentProcess or something like it.
// (notifyObserversInParentProcess is a synchronous IPC call to the parent
// process's main thread. IPDL PContent::CycleCollect is an async message.
// Ordering will be maintained if the postMessage goes via PContent as well,
// but that seems unlikely.)
SpecialPowers.notifyObservers(null, 'child-gc-request', null);
SpecialPowers.notifyObservers(null, 'child-cc-request', null);
SpecialPowers.notifyObservers(null, 'child-gc-request', null);
// (Only send the ping after we set the gc/cc/gc in motion.)
registration.installing.postMessage({ type: 'ping' });
});
}
function terminateWorker() {
return SpecialPowers.pushPrefEnv({
set: [
["dom.serviceWorkers.idle_timeout", 0],
["dom.serviceWorkers.idle_extended_timeout", 0]
]
}).then(_ => {
registration.installing.postMessage({ type: 'RESET_TIMER' });
});
}
function runTest() {
Promise.all([
waitForInstallEvent(),
register()
]).then(_ => ok(registration.installing, 'service worker is installing'))
.then(gcWorker)
.then(_ => ok(registration.installing, 'service worker is still installing'))
.then(terminateWorker)
.catch(e => ok(false, e))
.then(unregister)
.then(SimpleTest.finish);
}
SimpleTest.waitForExplicitFinish();
SpecialPowers.pushPrefEnv({"set": [
["dom.serviceWorkers.exemptFromPerDomainMax", true],
["dom.serviceWorkers.enabled", true],
["dom.serviceWorkers.testing.enabled", true],
["dom.caches.enabled", true],
]}, runTest);
</script>
</pre>
</body>
</html>
|