head.js (6287B)
1 async function newFocusedWindow(trigger, isInitialBlank = false) { 2 let winPromise = BrowserTestUtils.domWindowOpenedAndLoaded(); 3 let delayedStartupPromise = BrowserTestUtils.waitForNewWindow(); 4 5 await trigger(); 6 7 let win = await winPromise; 8 // New windows get focused after the first paint, see bug 1262946, 9 // but this is racy for the initial about:blank 10 if (!isInitialBlank) { 11 await BrowserTestUtils.waitForContentEvent( 12 win.gBrowser.selectedBrowser, 13 "MozAfterPaint" 14 ); 15 } else { 16 await new Promise(res => 17 win.requestAnimationFrame(() => win.requestAnimationFrame(res)) 18 ); 19 } 20 await delayedStartupPromise; 21 return win; 22 } 23 24 const JS_CACHE_BASE_URL = "http://mochi.test:8888/browser/dom/base/test/"; 25 26 function ev(event, file, hasElement = !!file) { 27 return { 28 event, 29 url: file ? JS_CACHE_BASE_URL + file : undefined, 30 hasElement, 31 }; 32 } 33 34 function unordered(list) { 35 return { 36 unordered: list, 37 }; 38 } 39 40 function optional_ev(...args) { 41 const event = ev(...args); 42 event.optional = true; 43 return event; 44 } 45 46 async function jsCacheContentTask(test, item) { 47 const defaultSkippedEvents = test.skippedEvents ?? [ 48 // The compilation is not target of this test. 49 "compile:main thread", 50 // This is triggered by 'invalidateMemory' below, but we don't have to 51 // track it. 52 "memorycache:invalidate", 53 // The disk cache handling for the in-memory cache can happen multiple 54 // times depending on the scheduling and speed 55 // (e.g. debug vs opt, verify mode). 56 "diskcache:noschedule", 57 ]; 58 const nonSkippedEvents = test.nonSkippedEvents ?? []; 59 const skippedEvents = defaultSkippedEvents.filter( 60 ev => !nonSkippedEvents.includes(ev) 61 ); 62 63 function match(param, event) { 64 if (event.event !== param.event) { 65 return false; 66 } 67 68 if (param.url && event.url !== param.url) { 69 return false; 70 } 71 72 if (event.hasElement) { 73 if (param.id !== "watchme") { 74 return false; 75 } 76 } else { 77 if (param.id) { 78 return false; 79 } 80 } 81 82 return true; 83 } 84 85 function consumeIfMatched(param, events) { 86 while ("optional" in events[0]) { 87 if (match(param, events[0])) { 88 events.shift(); 89 return true; 90 } 91 dump("@@@ Skip optional event: " + events[0].event + "\n"); 92 events.shift(); 93 } 94 95 if ("unordered" in events[0]) { 96 const unordered = events[0].unordered; 97 for (let i = 0; i < unordered.length; i++) { 98 if (match(param, unordered[i])) { 99 unordered.splice(i, 1); 100 if (!unordered.length) { 101 events.shift(); 102 } 103 return true; 104 } 105 } 106 107 return false; 108 } 109 110 if (match(param, events[0])) { 111 events.shift(); 112 return true; 113 } 114 115 return false; 116 } 117 118 let result = true; 119 120 const { promise, resolve, reject } = Promise.withResolvers(); 121 const observer = function (subject, topic, data) { 122 const param = {}; 123 for (const line of data.split("\n")) { 124 const m = line.match(/^([^:]+):(.*)/); 125 param[m[1]] = m[2]; 126 } 127 128 if (consumeIfMatched(param, item.events)) { 129 dump("@@@ Got expected event: " + data + "\n"); 130 if (item.events.length === 0) { 131 resolve(); 132 } 133 } else if (skippedEvents.includes(param.event)) { 134 dump("@@@ Ignoring: " + data + "\n"); 135 } else { 136 dump("@@@ Got unexpected event: " + data + "\n"); 137 dump("@@@ Expected: " + JSON.stringify(item.events[0]) + "\n"); 138 result = false; 139 } 140 }; 141 Services.obs.addObserver(observer, "ScriptLoaderTest"); 142 143 const script = content.document.createElement("script"); 144 script.id = "watchme"; 145 if (test.module || item.module) { 146 script.type = "module"; 147 } 148 if (item.sri) { 149 script.integrity = item.sri; 150 } 151 script.src = item.file; 152 content.document.body.appendChild(script); 153 154 await promise; 155 156 Services.obs.removeObserver(observer, "ScriptLoaderTest"); 157 158 return result; 159 } 160 161 async function runJSCacheTests(tests) { 162 await BrowserTestUtils.withNewTab( 163 JS_CACHE_BASE_URL + "empty.html", 164 async browser => { 165 const tab = gBrowser.getTabForBrowser(browser); 166 167 for (const test of tests) { 168 ChromeUtils.clearResourceCache(); 169 Services.cache2.clear(); 170 171 if (test.useServiceWorker) { 172 await SpecialPowers.spawn(browser, [], async () => { 173 const registration = await content.navigator.serviceWorker.register( 174 "file_js_cache_sw.js", 175 { scope: "./" } 176 ); 177 178 const sw = registration.installing || registration.active; 179 180 await new Promise(resolve => { 181 function onStateChange() { 182 if (sw.state === "activated") { 183 sw.removeEventListener("statechange", onStateChange); 184 resolve(); 185 } 186 } 187 sw.addEventListener("statechange", onStateChange); 188 onStateChange(); 189 }); 190 }); 191 } 192 193 for (let i = 0; i < test.items.length; i++) { 194 const item = test.items[i]; 195 info(`start: ${test.title} (item ${i})`); 196 197 if (!test.skipReload) { 198 // Make sure the test starts in clean document. 199 await BrowserTestUtils.reloadTab(tab); 200 } 201 202 if (item.clearMemory) { 203 info("clear memory cache"); 204 ChromeUtils.clearResourceCache(); 205 } 206 if (item.invalidateMemory) { 207 info("invalidate memory cache"); 208 ChromeUtils.invalidateResourceCache(); 209 } 210 if (item.clearDisk) { 211 info("clear disk cache"); 212 Services.cache2.clear(); 213 } 214 const result = await SpecialPowers.spawn( 215 browser, 216 [test, item], 217 jsCacheContentTask 218 ); 219 ok(result, "Received expected events"); 220 } 221 222 if (test.useServiceWorker) { 223 await SpecialPowers.spawn(browser, [], async () => { 224 const registration = 225 await content.navigator.serviceWorker.getRegistration(); 226 registration.unregister(); 227 }); 228 } 229 230 ok(true, "end: " + test.title); 231 } 232 } 233 ); 234 235 ok(true, "Finished all tests"); 236 }