modulepreload.html (15046B)
1 <!DOCTYPE html> 2 <script src="/resources/testharness.js"></script> 3 <script src="/resources/testharnessreport.js"></script> 4 <script src="/common/get-host-info.sub.js"></script> 5 <body> 6 <script> 7 host_info = get_host_info(); 8 9 function verifyNumberOfDownloads(url, number, allowTransferSizeOfZero = false) { 10 var numDownloads = 0; 11 let absoluteURL = new URL(url, location.href).href; 12 performance.getEntriesByName(absoluteURL).forEach(entry => { 13 if (entry.transferSize > 0 || allowTransferSizeOfZero) { 14 numDownloads++; 15 } 16 }); 17 assert_equals(numDownloads, number, url); 18 } 19 20 function attachAndWaitForLoad(element) { 21 return new Promise((resolve, reject) => { 22 element.onload = resolve; 23 element.onerror = reject; 24 document.body.appendChild(element); 25 }); 26 } 27 28 function attachAndWaitForError(element) { 29 return new Promise((resolve, reject) => { 30 element.onload = reject; 31 element.onerror = resolve; 32 document.body.appendChild(element); 33 }); 34 } 35 36 function attachAndWaitForTimeout(element, t) { 37 return new Promise((resolve, reject) => { 38 element.onload = reject; 39 element.onerror = reject; 40 t.step_timeout(resolve, 1000); 41 document.body.appendChild(element); 42 }); 43 } 44 45 promise_test(function(t) { 46 var link = document.createElement('link'); 47 link.rel = 'modulepreload'; 48 link.href = 'resources/dummy.js?unique'; 49 return attachAndWaitForLoad(link).then(() => { 50 verifyNumberOfDownloads('resources/dummy.js?unique', 1); 51 52 // Verify that <script> doesn't fetch the module again. 53 var script = document.createElement('script'); 54 script.type = 'module'; 55 script.src = 'resources/dummy.js?unique'; 56 return attachAndWaitForLoad(script); 57 }).then(() => { 58 verifyNumberOfDownloads('resources/dummy.js?unique', 1); 59 }); 60 }, 'link rel=modulepreload'); 61 62 /** 63 * Begin tests to ensure crossorigin value behaves the same on 64 * link rel=modulepreload as it does script elements. 65 */ 66 promise_test(function(t) { 67 document.cookie = 'same=1'; 68 var link = document.createElement('link'); 69 link.rel = 'modulepreload'; 70 link.crossOrigin = 'anonymous'; 71 link.href = 'resources/dummy.js?sameOriginAnonymous'; 72 return attachAndWaitForLoad(link).then(() => { 73 verifyNumberOfDownloads('resources/dummy.js?sameOriginAnonymous', 1); 74 75 // Verify that <script> doesn't fetch the module again. 76 var script = document.createElement('script'); 77 script.type = 'module'; 78 script.crossOrigin = 'anonymous'; 79 script.src = 'resources/dummy.js?sameOriginAnonymous'; 80 return attachAndWaitForLoad(script); 81 }).then(() => { 82 verifyNumberOfDownloads('resources/dummy.js?sameOriginAnonymous', 1); 83 }); 84 }, 'same-origin link rel=modulepreload crossorigin=anonymous'); 85 86 promise_test(function(t) { 87 var link = document.createElement('link'); 88 link.rel = 'modulepreload'; 89 link.crossOrigin = 'use-credentials'; 90 link.href = 'resources/dummy.js?sameOriginUseCredentials'; 91 return attachAndWaitForLoad(link).then(() => { 92 verifyNumberOfDownloads('resources/dummy.js?sameOriginUseCredentials', 1); 93 94 // Verify that <script> doesn't fetch the module again. 95 var script = document.createElement('script'); 96 script.type = 'module'; 97 script.crossOrigin = 'use-credentials'; 98 script.src = 'resources/dummy.js?sameOriginUseCredentials'; 99 return attachAndWaitForLoad(script); 100 }).then(() => { 101 verifyNumberOfDownloads('resources/dummy.js?sameOriginUseCredentials', 1); 102 }); 103 }, 'same-origin link rel=modulepreload crossorigin=use-credentials'); 104 105 promise_test(function(t) { 106 const setCookiePromise = fetch( 107 `${host_info.HTTP_REMOTE_ORIGIN}/cookies/resources/set-cookie.py?name=cross&path=/preload/`, 108 { 109 mode: 'no-cors', 110 credentials: 'include', 111 }); 112 113 return setCookiePromise.then(() => { 114 var link = document.createElement('link'); 115 link.rel = 'modulepreload'; 116 link.href = `${host_info.HTTP_REMOTE_ORIGIN}/preload/resources/cross-origin-module.py?crossOriginNone`; 117 return attachAndWaitForLoad(link); 118 }).then(() => { 119 verifyNumberOfDownloads(`${host_info.HTTP_REMOTE_ORIGIN}/preload/resources/cross-origin-module.py?crossOriginNone`, 1, true); 120 121 // Verify that <script> doesn't fetch the module again. 122 var script = document.createElement('script'); 123 script.type = 'module'; 124 script.src = `${host_info.HTTP_REMOTE_ORIGIN}/preload/resources/cross-origin-module.py?crossOriginNone`; 125 return attachAndWaitForLoad(script); 126 }).then(() => { 127 verifyNumberOfDownloads(`${host_info.HTTP_REMOTE_ORIGIN}/preload/resources/cross-origin-module.py?crossOriginNone`, 1, true); 128 }); 129 }, 'cross-origin link rel=modulepreload'); 130 131 promise_test(function(t) { 132 var link = document.createElement('link'); 133 link.rel = 'modulepreload'; 134 link.crossOrigin = 'anonymous'; 135 link.href = `${host_info.HTTP_REMOTE_ORIGIN}/preload/resources/cross-origin-module.py?crossOriginAnonymous`; 136 return attachAndWaitForLoad(link).then(() => { 137 verifyNumberOfDownloads(`${host_info.HTTP_REMOTE_ORIGIN}/preload/resources/cross-origin-module.py?crossOriginAnonymous`, 1, true); 138 139 // Verify that <script> doesn't fetch the module again. 140 var script = document.createElement('script'); 141 script.type = 'module'; 142 script.crossOrigin = 'anonymous'; 143 script.src = `${host_info.HTTP_REMOTE_ORIGIN}/preload/resources/cross-origin-module.py?crossOriginAnonymous`; 144 return attachAndWaitForLoad(script); 145 }).then(() => { 146 verifyNumberOfDownloads(`${host_info.HTTP_REMOTE_ORIGIN}/preload/resources/cross-origin-module.py?crossOriginAnonymous`, 1, true); 147 }); 148 }, 'cross-origin link rel=modulepreload crossorigin=anonymous'); 149 150 promise_test(function(t) { 151 var link = document.createElement('link'); 152 link.rel = 'modulepreload'; 153 link.crossOrigin = 'use-credentials'; 154 link.href = `${host_info.HTTP_REMOTE_ORIGIN}/preload/resources/cross-origin-module.py?crossOriginUseCredentials`; 155 return attachAndWaitForLoad(link).then(() => { 156 verifyNumberOfDownloads(`${host_info.HTTP_REMOTE_ORIGIN}/preload/resources/cross-origin-module.py?crossOriginUseCredentials`, 1, true); 157 158 // Verify that <script> doesn't fetch the module again. 159 var script = document.createElement('script'); 160 script.type = 'module'; 161 script.crossOrigin = 'use-credentials'; 162 script.src = `${host_info.HTTP_REMOTE_ORIGIN}/preload/resources/cross-origin-module.py?crossOriginUseCredentials`; 163 return attachAndWaitForLoad(script); 164 }).then(() => { 165 verifyNumberOfDownloads(`${host_info.HTTP_REMOTE_ORIGIN}/preload/resources/cross-origin-module.py?crossOriginUseCredentials`, 1, true); 166 }); 167 }, 'cross-origin link rel=modulepreload crossorigin=use-credentials'); 168 /** 169 * End link rel=modulepreload crossorigin attribute tests. 170 */ 171 172 promise_test(function(t) { 173 var link = document.createElement('link'); 174 link.rel = 'modulepreload'; 175 link.href = 'resources/module1.js?submodule'; 176 return attachAndWaitForLoad(link).then(() => { 177 verifyNumberOfDownloads('resources/module1.js?submodule', 1); 178 // The load event fires before (optional) submodules fetch. 179 verifyNumberOfDownloads('resources/module2.js', 0); 180 181 var script = document.createElement('script'); 182 script.type = 'module'; 183 script.src = 'resources/module1.js?submodule'; 184 return attachAndWaitForLoad(script); 185 }).then(() => { 186 verifyNumberOfDownloads('resources/module1.js?submodule', 1); 187 verifyNumberOfDownloads('resources/module2.js', 1); 188 }); 189 }, 'link rel=modulepreload with submodules'); 190 191 promise_test(function(t) { 192 var link = document.createElement('link'); 193 link.rel = 'modulepreload'; 194 link.href = 'resources/syntax-error.js'; 195 return attachAndWaitForLoad(link); 196 }, 'link rel=modulepreload for a module with syntax error'); 197 198 promise_test(function(t) { 199 var link = document.createElement('link'); 200 link.rel = 'modulepreload'; 201 link.href = 'resources/not-exist.js'; 202 return attachAndWaitForError(link); 203 }, 'link rel=modulepreload for a module with network error'); 204 205 promise_test(function(t) { 206 var link = document.createElement('link'); 207 link.rel = 'modulepreload'; 208 link.href = null; 209 return attachAndWaitForError(link); 210 }, 'link rel=modulepreload with bad href attribute'); 211 212 promise_test(function(t) { 213 var link = document.createElement('link'); 214 link.rel = 'modulepreload'; 215 link.href = 'resources/module1.js?as-script'; 216 link.as = 'script' 217 return attachAndWaitForLoad(link); 218 }, 'link rel=modulepreload as=script'); 219 220 promise_test(function(t) { 221 var link = document.createElement('link'); 222 link.rel = 'modulepreload'; 223 link.href = 'resources/module1.js?as-image'; 224 link.as = 'image' 225 return attachAndWaitForError(link); 226 }, 'link rel=modulepreload with non-script-like as= value (image)'); 227 228 promise_test(function(t) { 229 var link = document.createElement('link'); 230 link.rel = 'modulepreload'; 231 link.href = 'resources/module1.js?as-xslt'; 232 link.as = 'xslt' 233 return attachAndWaitForError(link); 234 }, 'link rel=modulepreload with non-script-like as= value (xslt)'); 235 236 promise_test(function(t) { 237 var link = document.createElement('link'); 238 link.rel = 'modulepreload'; 239 link.href = 'resources/module1.js?integrity-match'; 240 link.integrity = 'sha256-+Ks3iNIiTq2ujlWhvB056cmXobrCFpU9hd60xZ1WCaA=' 241 return attachAndWaitForLoad(link); 242 }, 'link rel=modulepreload with integrity match'); 243 244 promise_test(function(t) { 245 var link = document.createElement('link'); 246 link.rel = 'modulepreload'; 247 link.href = 'resources/module1.mjs?integrity-match'; 248 link.integrity = 'sha256-+Ks3iNIiTq2ujlWhvB056cmXobrCFpU9hd60xZ1WCaA=' 249 return attachAndWaitForLoad(link); 250 }, 'link rel=modulepreload with integrity match2'); 251 252 promise_test(function(t) { 253 var link = document.createElement('link'); 254 link.rel = 'modulepreload'; 255 link.href = 'resources/module1.js?integrity-doesnotmatch'; 256 link.integrity = 'sha384-doesnotmatch' 257 return attachAndWaitForError(link); 258 }, 'link rel=modulepreload with integrity mismatch'); 259 260 promise_test(function(t) { 261 var link = document.createElement('link'); 262 link.rel = 'modulepreload'; 263 link.href = 'resources/module1.mjs?integrity-doesnotmatch'; 264 link.integrity = 'sha256-dOxReWMnMSPfUvxEbBqIrjNh8ZN8n05j7h3JmhF8gQc=' 265 return attachAndWaitForError(link); 266 }, 'link rel=modulepreload with integrity mismatch2'); 267 268 promise_test(function(t) { 269 var link = document.createElement('link'); 270 link.rel = 'modulepreload'; 271 link.href = 'resources/module1.mjs?integrity-invalid'; 272 link.integrity = 'sha256-dOxReWMnMSPfUvxEbBqIrjNh8ZN8n05j7h3JmhF8gQc=%' 273 return attachAndWaitForError(link); 274 }, 'link rel=modulepreload with integrity mismatch3'); 275 276 promise_test(function(t) { 277 var link1 = document.createElement('link'); 278 var link2 = document.createElement('link'); 279 link1.rel = 'modulepreload'; 280 link2.rel = 'modulepreload'; 281 link1.href = 'resources/module1.js?same-url'; 282 link2.href = 'resources/module1.js?same-url'; 283 return Promise.all([ 284 attachAndWaitForLoad(link1), 285 attachAndWaitForLoad(link2), 286 ]); 287 }, 'multiple link rel=modulepreload with same href'); 288 289 promise_test(function(t) { 290 var link1 = document.createElement('link'); 291 var link2 = document.createElement('link'); 292 link1.rel = 'modulepreload'; 293 link2.rel = 'modulepreload'; 294 link1.href = 'resources/module2.js?child-before'; 295 link2.href = 'resources/module1.js?child-before'; 296 return attachAndWaitForLoad(link1) 297 .then(() => attachAndWaitForLoad(link2)) 298 .then(() => new Promise(r => t.step_timeout(r, 1000))) 299 .then(() => { 300 verifyNumberOfDownloads('resources/module2.js?child-before', 1); 301 }); 302 303 }, 'multiple link rel=modulepreload with child module before parent'); 304 305 promise_test(function(t) { 306 var link = document.createElement('link'); 307 link.rel = 'modulepreload'; 308 link.href = 'resources/module1.mjs?matching-media'; 309 link.media = 'all'; 310 return attachAndWaitForLoad(link); 311 }, 'link rel=modulepreload with matching media'); 312 313 promise_test(function(t) { 314 var link = document.createElement('link'); 315 link.rel = 'modulepreload'; 316 link.href = 'resources/module1.mjs?non-matching-media'; 317 link.media = 'not all'; 318 return attachAndWaitForTimeout(link, t); 319 }, 'link rel=modulepreload with non-matching media'); 320 321 promise_test(function(t) { 322 var link = document.createElement('link'); 323 link.rel = 'modulepreload'; 324 link.href = 'resources/module1.mjs?empty-media'; 325 link.media = ''; 326 return attachAndWaitForLoad(link); 327 }, 'link rel=modulepreload with empty media'); 328 329 promise_test(function(t) { 330 var link = document.createElement('link'); 331 link.rel = 'modulepreload'; 332 link.href = ''; 333 return attachAndWaitForTimeout(link, t); 334 }, 'link rel=modulepreload with empty href'); 335 336 promise_test(function(t) { 337 var link = document.createElement('link'); 338 link.rel = 'modulepreload'; 339 link.href = ''; 340 link.as = 'fetch'; 341 return attachAndWaitForTimeout(link, t); 342 }, 'link rel=modulepreload with empty href and invalid as= value'); 343 344 promise_test(function(t) { 345 var link = document.createElement('link'); 346 var script = document.createElement('script'); 347 link.rel = 'modulepreload'; 348 script.type = 'module'; 349 link.href = 'resources/module1.mjs?non-matching-crossorigin'; 350 script.src = link.href; 351 script.crossOrigin = 'anonymous'; 352 document.body.append(link); 353 return attachAndWaitForLoad(script); 354 }, 'link rel=modulepreload and script with non-matching crossorigin values'); 355 356 promise_test(function(t) { 357 var link = document.createElement('link'); 358 var script = document.createElement('script'); 359 link.rel = 'modulepreload'; 360 script.type = 'module'; 361 link.href = 'resources/module1.mjs?non-matching-crossorigin'; 362 script.src = link.href; 363 link.crossOrigin = 'anonymous'; 364 script.crossOrigin = 'use-credentials'; 365 document.body.append(link); 366 return attachAndWaitForLoad(script); 367 }, 'link rel=modulepreload and script with non-matching crossorigin values2'); 368 369 promise_test(function(t) { 370 var link = document.createElement('link'); 371 var moduleScript = document.createElement('script'); 372 var classicScript = document.createElement('script'); 373 link.rel = 'modulepreload'; 374 moduleScript.type = 'module'; 375 link.href = 'resources/dummy.js?non-module script'; 376 classicScript.src = link.href; 377 moduleScript.src = link.href; 378 document.body.append(link); 379 document.body.append(classicScript); 380 return attachAndWaitForLoad(moduleScript); 381 }, 'link rel=modulepreload and non-module script'); 382 </script> 383 </body>