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