test_console_serviceworker.html (7346B)
1 <!DOCTYPE HTML> 2 <html lang="en"> 3 <head> 4 <meta charset="utf8"> 5 <title>Test for the Console API and Service Workers</title> 6 <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> 7 <script type="text/javascript" src="common.js"></script> 8 <!-- Any copyright is dedicated to the Public Domain. 9 - http://creativecommons.org/publicdomain/zero/1.0/ --> 10 </head> 11 <body> 12 <p>Test for the Console API and Service Workers</p> 13 14 <script class="testbody" type="text/javascript"> 15 "use strict"; 16 17 SimpleTest.waitForExplicitFinish(); 18 19 // Utils functions 20 function withFrame(url) { 21 return new Promise(resolve => { 22 const iframe = document.createElement("iframe"); 23 iframe.onload = function () { 24 resolve(iframe); 25 }; 26 iframe.src = url; 27 document.body.appendChild(iframe); 28 }); 29 } 30 31 function navigateFrame(iframe, url) { 32 return new Promise(resolve => { 33 iframe.onload = function () { 34 resolve(iframe); 35 }; 36 iframe.src = url; 37 }); 38 } 39 40 function forceReloadFrame(iframe) { 41 return new Promise(resolve => { 42 iframe.onload = function () { 43 resolve(iframe); 44 }; 45 iframe.contentWindow.location.reload(true); 46 }); 47 } 48 49 function messageServiceWorker(win, scope, message) { 50 return win.navigator.serviceWorker.getRegistration(scope).then(swr => { 51 return new Promise(resolve => { 52 win.navigator.serviceWorker.onmessage = () => { 53 resolve(); 54 }; 55 const sw = swr.active || swr.waiting || swr.installing; 56 sw.postMessage({ type: "PING", message }); 57 }); 58 }); 59 } 60 61 function unregisterServiceWorker(win) { 62 return win.navigator.serviceWorker.ready.then(swr => { 63 return swr.unregister(); 64 }); 65 } 66 67 // Test 68 const BASE_URL = "https://example.com/chrome/devtools/shared/webconsole/test/chrome/"; 69 const SERVICE_WORKER_URL = BASE_URL + "helper_serviceworker.js"; 70 const SCOPE = BASE_URL + "foo/"; 71 const NONSCOPE_FRAME_URL = BASE_URL + "sandboxed_iframe.html"; 72 const SCOPE_FRAME_URL = SCOPE + "fake.html"; 73 const SCOPE_FRAME_URL2 = SCOPE + "whatsit.html"; 74 const MESSAGE = 'Tic Tock'; 75 76 const expectedConsoleCalls = [ 77 { 78 level: "log", 79 filename: /helper_serviceworker/, 80 arguments: ['script evaluation'], 81 }, 82 { 83 level: "log", 84 filename: /helper_serviceworker/, 85 // Note that the second argument isn't a SharedArrayBuffer, but a DevTools "Object Front" instance. 86 arguments: ['Here is a SAB', { class : "SharedArrayBuffer" }], 87 }, 88 { 89 level: "log", 90 filename: /helper_serviceworker/, 91 arguments: ['install event'], 92 }, 93 { 94 level: "log", 95 filename: /helper_serviceworker/, 96 arguments: ['activate event'], 97 }, 98 { 99 level: "log", 100 filename: /helper_serviceworker/, 101 arguments: ['fetch event: ' + SCOPE_FRAME_URL], 102 }, 103 { 104 level: "log", 105 filename: /helper_serviceworker/, 106 arguments: ['fetch event: ' + SCOPE_FRAME_URL2], 107 }, 108 ]; 109 let consoleCalls = []; 110 111 const startTest = async function () { 112 removeEventListener("load", startTest); 113 114 await new Promise(resolve => { 115 SpecialPowers.pushPrefEnv({"set": [ 116 ["dom.serviceWorkers.enabled", true], 117 ["devtools.webconsole.filter.serviceworkers", true], 118 [ 119 "dom.postMessage.sharedArrayBuffer.bypassCOOP_COEP.insecure.enabled", 120 true 121 ], 122 ]}, resolve); 123 }); 124 125 const {state, response} = await attachConsoleToTab(["ConsoleAPI"]); 126 onAttach(state, response); 127 }; 128 addEventListener("load", startTest); 129 130 const onAttach = async function (state) { 131 onConsoleAPICall = onConsoleAPICall.bind(null, state); 132 state.webConsoleFront.on("consoleAPICall", onConsoleAPICall); 133 134 let currentFrame; 135 try { 136 // First, we need a frame from which to register our script. This 137 // will not trigger any console calls. 138 info("Loading a non-scope frame from which to register a service worker."); 139 currentFrame = await withFrame(NONSCOPE_FRAME_URL); 140 141 // Now register the service worker and wait for it to become 142 // activate. This should trigger 3 console calls; 1 for script 143 // evaluation, 1 for the install event, and 1 for the activate 144 // event. These console calls are received because we called 145 // register(), not because we are in scope for the worker. 146 info("Registering the service worker"); 147 await withActiveServiceWorker(currentFrame.contentWindow, 148 SERVICE_WORKER_URL, SCOPE); 149 ok(!currentFrame.contentWindow.navigator.serviceWorker.controller, 150 'current frame should not be controlled'); 151 152 // Now that the service worker is activate, lets navigate our frame. 153 // This will trigger 1 more console call for the fetch event. 154 info("Service worker registered. Navigating frame."); 155 await navigateFrame(currentFrame, SCOPE_FRAME_URL); 156 ok(currentFrame.contentWindow.navigator.serviceWorker.controller, 157 'navigated frame should be controlled'); 158 159 // We now have a controlled frame. Lets perform a non-navigation fetch. 160 // This should produce another console call for the fetch event. 161 info("Frame navigated. Calling fetch()."); 162 await currentFrame.contentWindow.fetch(SCOPE_FRAME_URL2); 163 164 // Now force refresh our controlled frame. This will cause the frame 165 // to bypass the service worker and become an uncontrolled frame. It 166 // also happens to make the frame display a 404 message because the URL 167 // does not resolve to a real resource. This is ok, as we really only 168 // care about the frame being non-controlled, but still having a location 169 // that matches our service worker scope so we can provide its not 170 // incorrectly getting console calls. 171 info("Completed fetch(). Force refreshing to get uncontrolled frame."); 172 await forceReloadFrame(currentFrame); 173 ok(!currentFrame.contentWindow.navigator.serviceWorker.controller, 174 'current frame should not be controlled after force refresh'); 175 is(currentFrame.contentWindow.location.toString(), SCOPE_FRAME_URL, 176 'current frame should still have in-scope location URL even though it got 404'); 177 178 // Now postMessage() the service worker to trigger its message event 179 // handler. This will generate 1 or 2 to console.log() statements 180 // depending on if the worker thread needs to spin up again. In either 181 // case, though, we should not get any console calls because we don't 182 // have a controlled or registering document. 183 info("Completed force refresh. Messaging service worker."); 184 await messageServiceWorker(currentFrame.contentWindow, SCOPE, MESSAGE); 185 186 info("Done messaging service worker. Unregistering service worker."); 187 await unregisterServiceWorker(currentFrame.contentWindow); 188 189 info('Service worker unregistered. Checking console calls.'); 190 state.webConsoleFront.off("consoleAPICall", onConsoleAPICall); 191 checkConsoleAPICalls(consoleCalls, expectedConsoleCalls); 192 } catch(error) { 193 ok(false, 'unexpected error: ' + error); 194 } finally { 195 if (currentFrame) { 196 currentFrame.remove(); 197 currentFrame = null; 198 } 199 consoleCalls = []; 200 closeDebugger(state, function() { 201 SimpleTest.finish(); 202 }); 203 } 204 }; 205 206 function onConsoleAPICall(state, packet) { 207 info("received message level: " + packet.message.level); 208 consoleCalls.push(packet.message); 209 } 210 </script> 211 </body> 212 </html>