subresource-load.https.tentative.sub.html (11500B)
1 <!DOCTYPE html> 2 <title>Subresource loading with script type="webbundle"</title> 3 <link 4 rel="help" 5 href="https://github.com/WICG/webpackage/blob/main/explainers/subresource-loading.md" 6 /> 7 <script src="/resources/testharness.js"></script> 8 <script src="/resources/testharnessreport.js"></script> 9 <script src="../resources/test-helpers.js"></script> 10 <body> 11 <script type="webbundle"> 12 { 13 "source": "../resources/wbn/subresource.wbn", 14 "resources": [ 15 "https://{{domains[]}}:{{ports[https][0]}}/web-bundle/resources/wbn/root.js", 16 "https://{{domains[]}}:{{ports[https][0]}}/web-bundle/resources/wbn/submodule.js" 17 ] 18 } 19 </script> 20 <script> 21 setup(() => { 22 assert_true(HTMLScriptElement.supports("webbundle")); 23 }); 24 25 promise_test(async () => { 26 const module = await import( 27 "https://{{domains[]}}:{{ports[https][0]}}/web-bundle/resources/wbn/root.js" 28 ); 29 assert_equals(module.result, "OK"); 30 }, "Subresource loading with WebBundle"); 31 32 promise_test(async () => { 33 const response = await fetch( 34 "https://{{domains[]}}:{{ports[https][0]}}/web-bundle/resources/wbn/root.js" 35 ); 36 const text = await response.text(); 37 assert_equals(text, "export * from './submodule.js';\n"); 38 }, "Subresource loading with WebBundle (Fetch API)"); 39 40 promise_test((t) => { 41 const url = 42 "/common/redirect.py?location=https://{{domains[]}}:{{ports[https][0]}}/web-bundle/resources/wbn/root.js"; 43 return promise_rejects_js(t, TypeError, import(url)); 44 }, "Subresource loading with WebBundle shouldn't affect redirect"); 45 46 promise_test(async () => { 47 const element = createWebBundleElement("../resources/wbn/dynamic1.wbn", [ 48 "https://{{domains[]}}:{{ports[https][0]}}/web-bundle/resources/wbn/dynamic/resource1.js", 49 "https://{{domains[]}}:{{ports[https][0]}}/web-bundle/resources/wbn/dynamic/resource2.js", 50 "https://{{domains[]}}:{{ports[https][0]}}/web-bundle/resources/wbn/dynamic/resource4.js", 51 ]); 52 document.body.appendChild(element); 53 54 const module = await import( 55 "https://{{domains[]}}:{{ports[https][0]}}/web-bundle/resources/wbn/dynamic/resource1.js" 56 ); 57 assert_equals(module.result, "resource1 from dynamic1.wbn"); 58 59 const new_element = removeAndAppendNewElementWithUpdatedRule(element, { 60 url: "../resources/wbn/dynamic2.wbn", 61 }); 62 const module2 = await import( 63 "https://{{domains[]}}:{{ports[https][0]}}/web-bundle/resources/wbn/dynamic/resource2.js" 64 ); 65 assert_equals(module2.result, "resource2 from dynamic2.wbn"); 66 67 // A resource not specified in the resources attribute, but in the bundle. 68 const module3 = await import( 69 "https://{{domains[]}}:{{ports[https][0]}}/web-bundle/resources/wbn/dynamic/resource3.js" 70 ); 71 assert_equals(module3.result, "resource3 from network"); 72 73 document.body.removeChild(new_element); 74 const module4 = await import( 75 "https://{{domains[]}}:{{ports[https][0]}}/web-bundle/resources/wbn/dynamic/resource4.js" 76 ); 77 assert_equals(module4.result, "resource4 from network"); 78 79 // Module scripts are stored to the Document's module map once loaded. 80 // So import()ing the same module script will reuse the previously loaded 81 // script. 82 const module_second = await import( 83 "https://{{domains[]}}:{{ports[https][0]}}/web-bundle/resources/wbn/dynamic/resource1.js" 84 ); 85 assert_equals(module_second.result, "resource1 from dynamic1.wbn"); 86 }, "Dynamically adding / updating / removing the webbundle element."); 87 88 promise_test(async () => { 89 const classic_script_url = 90 "https://{{domains[]}}:{{ports[https][0]}}/web-bundle/resources/wbn/dynamic/classic_script.js"; 91 const element = createWebBundleElement("../resources/wbn/dynamic1.wbn", [ 92 classic_script_url, 93 ]); 94 document.body.appendChild(element); 95 assert_equals( 96 await loadScriptAndWaitReport(classic_script_url), 97 "classic script from dynamic1.wbn" 98 ); 99 const new_element = removeAndAppendNewElementWithUpdatedRule(element, { 100 url: "../resources/wbn/dynamic2.wbn", 101 }); 102 // Loading the classic script should not reuse the previously loaded 103 // script. So in this case, the script must be loaded from dynamic2.wbn. 104 assert_equals( 105 await loadScriptAndWaitReport(classic_script_url), 106 "classic script from dynamic2.wbn" 107 ); 108 document.body.removeChild(new_element); 109 // And in this case, the script must be loaded from network. 110 assert_equals( 111 await loadScriptAndWaitReport(classic_script_url), 112 "classic script from network" 113 ); 114 }, "Dynamically loading classic script from web bundle"); 115 116 promise_test(async (t) => { 117 // To avoid caching mechanism, this test is using fetch() API with 118 // { cache: 'no-store' } to load the resource. 119 const classic_script_url = 120 "https://{{domains[]}}:{{ports[https][0]}}/web-bundle/resources/wbn/dynamic/classic_script.js"; 121 122 assert_equals( 123 await (await fetch(classic_script_url)).text(), 124 "window.report_result('classic script from network');\n" 125 ); 126 127 const element1 = createWebBundleElement("../resources/wbn/dynamic1.wbn", [ 128 classic_script_url, 129 ]); 130 document.body.appendChild(element1); 131 t.add_cleanup(() => { 132 if (element1.parentElement) 133 element1.parentElement.removeChild(element1); 134 }); 135 136 assert_equals( 137 await (await fetch(classic_script_url, { cache: "no-store" })).text(), 138 "window.report_result('classic script from dynamic1.wbn');\n" 139 ); 140 141 const element2 = createWebBundleElement("../resources/wbn/dynamic2.wbn", [ 142 classic_script_url, 143 ]); 144 document.body.appendChild(element2); 145 t.add_cleanup(() => { 146 if (element2.parentElement) 147 element2.parentElement.removeChild(element2); 148 }); 149 150 assert_equals( 151 await (await fetch(classic_script_url, { cache: "no-store" })).text(), 152 "window.report_result('classic script from dynamic2.wbn');\n" 153 ); 154 155 document.body.removeChild(element2); 156 157 assert_equals( 158 await (await fetch(classic_script_url, { cache: "no-store" })).text(), 159 "window.report_result('classic script from dynamic1.wbn');\n" 160 ); 161 162 document.body.removeChild(element1); 163 164 assert_equals( 165 await (await fetch(classic_script_url, { cache: "no-store" })).text(), 166 "window.report_result('classic script from network');\n" 167 ); 168 }, "Multiple web bundle elements. The last added element must be refered."); 169 170 promise_test(async () => { 171 const classic_script_url = 172 "https://{{domains[]}}:{{ports[https][0]}}/web-bundle/resources/wbn/dynamic/classic_script.js"; 173 const scope = 174 "https://{{domains[]}}:{{ports[https][0]}}/web-bundle/resources/wbn/dynamic/"; 175 const element = createWebBundleElement( 176 "../resources/wbn/dynamic1.wbn", 177 [], 178 { scopes: [scope] } 179 ); 180 document.body.appendChild(element); 181 assert_equals( 182 await loadScriptAndWaitReport(classic_script_url), 183 "classic script from dynamic1.wbn" 184 ); 185 const new_element = removeAndAppendNewElementWithUpdatedRule(element, { 186 url: "../resources/wbn/dynamic2.wbn", 187 }); 188 // Loading the classic script should not reuse the previously loaded 189 // script. So in this case, the script must be loaded from dynamic2.wbn. 190 assert_equals( 191 await loadScriptAndWaitReport(classic_script_url), 192 "classic script from dynamic2.wbn" 193 ); 194 // Changes the scope not to hit the classic_script.js. 195 const new_element2 = removeAndAppendNewElementWithUpdatedRule( 196 new_element, 197 { scopes: [scope + "dummy"] } 198 ); 199 // And in this case, the script must be loaded from network. 200 assert_equals( 201 await loadScriptAndWaitReport(classic_script_url), 202 "classic script from network" 203 ); 204 // Adds the scope to hit the classic_script.js. 205 const new_element3 = removeAndAppendNewElementWithUpdatedRule( 206 new_element2, 207 { scopes: [scope + "dummy", scope + "classic_"] } 208 ); 209 assert_equals( 210 await loadScriptAndWaitReport(classic_script_url), 211 "classic script from dynamic2.wbn" 212 ); 213 document.body.removeChild(new_element3); 214 // And in this case, the script must be loaded from network. 215 assert_equals( 216 await loadScriptAndWaitReport(classic_script_url), 217 "classic script from network" 218 ); 219 }, "Dynamically loading classic script from web bundle with scopes"); 220 221 promise_test(() => { 222 return addWebBundleElementAndWaitForLoad( 223 "../resources/wbn/dynamic1.wbn?test-event", 224 /*resources=*/ [], 225 { crossOrigin: undefined } 226 ); 227 }, "The webbundle element fires a load event on load success"); 228 229 promise_test((t) => { 230 return addWebBundleElementAndWaitForError( 231 "../resources/wbn/nonexistent.wbn", 232 /*resources=*/ [], 233 { crossOrigin: undefined } 234 ); 235 }, "The webbundle element fires an error event on load failure"); 236 237 promise_test(async () => { 238 const module_script_url = 239 "https://www1.{{domains[]}}:{{ports[https][0]}}/web-bundle/resources/wbn/dynamic/resource1.js"; 240 const element = createWebBundleElement( 241 "../resources/wbn/dynamic1-crossorigin.wbn", 242 [module_script_url] 243 ); 244 document.body.appendChild(element); 245 const module = await import(module_script_url); 246 assert_equals(module.result, "resource1 from network"); 247 }, "Subresource URL must be same-origin with bundle URL"); 248 249 promise_test(async () => { 250 const url = "uuid-in-package:020111b3-437a-4c5c-ae07-adb6bbffb720"; 251 const element = createWebBundleElement( 252 "../resources/wbn/uuid-in-package.wbn", 253 [url] 254 ); 255 document.body.appendChild(element); 256 assert_equals(await loadScriptAndWaitReport(url), "OK"); 257 document.body.removeChild(element); 258 }, "Subresource loading with uuid-in-package: URL with resources attribute"); 259 260 promise_test(async () => { 261 const url = "uuid-in-package:020111b3-437a-4c5c-ae07-adb6bbffb720"; 262 const element = createWebBundleElement( 263 "../resources/wbn/uuid-in-package.wbn", 264 [], 265 { scopes: ["uuid-in-package:"] } 266 ); 267 document.body.appendChild(element); 268 assert_equals(await loadScriptAndWaitReport(url), "OK"); 269 document.body.removeChild(element); 270 }, "Subresource loading with uuid-in-package: URL with scopes attribute"); 271 272 async function loadScriptAndWaitReport(script_url) { 273 const result_promise = new Promise((resolve) => { 274 // This function will be called from script.js 275 window.report_result = resolve; 276 }); 277 278 const script = document.createElement("script"); 279 script.src = script_url; 280 document.body.appendChild(script); 281 return result_promise; 282 } 283 284 function removeAndAppendNewElementWithUpdatedRule(element, new_rule) { 285 const new_element = createNewWebBundleElementWithUpdatedRule( 286 element, 287 new_rule 288 ); 289 element.remove(); 290 document.body.appendChild(new_element); 291 return new_element; 292 } 293 </script> 294 </body>