| <!DOCTYPE html> |
| <title>Subresource loading with link rel="webbundle"</title> |
| <link rel="help" href="https://github.com/WICG/webpackage/blob/master/explainers/subresource-loading.md" /> |
| <script src="/resources/testharness.js"></script> |
| <script src="/resources/testharnessreport.js"></script> |
| |
| <body> |
| <link id="link-web-bundle" rel="webbundle" href="../resources/wbn/subresource.wbn" resources="https://web-platform.test:8444/web-bundle/resources/wbn/root.js |
| https://web-platform.test:8444/web-bundle/resources/wbn/submodule.js" /> |
| <script> |
| promise_test(async () => { |
| const module = await import('https://web-platform.test:8444/web-bundle/resources/wbn/root.js'); |
| assert_equals(module.result, 'OK'); |
| }, "Subresource loading with WebBundle"); |
| |
| promise_test(async () => { |
| const response = await fetch('https://web-platform.test:8444/web-bundle/resources/wbn/root.js'); |
| const text = await response.text(); |
| assert_equals(text, "export * from './submodule.js';\n"); |
| }, "Subresource loading with WebBundle (Fetch API)"); |
| |
| promise_test(t => { |
| const url = |
| '/common/redirect.py?location=https://web-platform.test:8444/web-bundle/resources/wbn/root.js'; |
| return promise_rejects_js(t, TypeError, import(url)); |
| }, "Subresource loading with WebBundle shouldn't affect redirect"); |
| |
| promise_test(async () => { |
| const link = document.createElement("link"); |
| link.rel = "webbundle"; |
| link.href = "../resources/wbn/dynamic1.wbn"; |
| link.resources.add('https://web-platform.test:8444/web-bundle/resources/wbn/dynamic/resource1.js', |
| 'https://web-platform.test:8444/web-bundle/resources/wbn/dynamic/resource2.js', |
| 'https://web-platform.test:8444/web-bundle/resources/wbn/dynamic/resource4.js'); |
| document.body.appendChild(link); |
| |
| const module = await import('https://web-platform.test:8444/web-bundle/resources/wbn/dynamic/resource1.js'); |
| assert_equals(module.result, 'resource1 from dynamic1.wbn'); |
| |
| link.href = "../resources/wbn/dynamic2.wbn"; |
| const module2 = await import('https://web-platform.test:8444/web-bundle/resources/wbn/dynamic/resource2.js'); |
| assert_equals(module2.result, 'resource2 from dynamic2.wbn'); |
| |
| // A resource not specified in the resources attribute, but in the bundle. |
| const module3 = await import('https://web-platform.test:8444/web-bundle/resources/wbn/dynamic/resource3.js'); |
| assert_equals(module3.result, 'resource3 from network'); |
| |
| document.body.removeChild(link); |
| const module4 = await import('https://web-platform.test:8444/web-bundle/resources/wbn/dynamic/resource4.js'); |
| assert_equals(module4.result, 'resource4 from network'); |
| |
| // Module scripts are stored to the Document's module map once loaded. |
| // So import()ing the same module script will reuse the previously loaded |
| // script. |
| const module_second = await import('https://web-platform.test:8444/web-bundle/resources/wbn/dynamic/resource1.js'); |
| assert_equals(module_second.result, 'resource1 from dynamic1.wbn'); |
| }, 'Dynamically adding / updating / removing "<link rel=webbundle>"'); |
| |
| promise_test(async () => { |
| const classic_script_url = 'https://web-platform.test:8444/web-bundle/resources/wbn/dynamic/classic_script.js'; |
| const link = document.createElement("link"); |
| link.rel = "webbundle"; |
| link.href = "../resources/wbn/dynamic1.wbn"; |
| link.resources.add(classic_script_url); |
| document.body.appendChild(link); |
| assert_equals( |
| await loadScriptAndWaitReport(classic_script_url), |
| 'classic script from dynamic1.wbn'); |
| link.href = "../resources/wbn/dynamic2.wbn"; |
| // Loading the classic script should not reuse the previously loaded |
| // script. So in this case, the script must be loaded from dynamic2.wbn. |
| assert_equals( |
| await loadScriptAndWaitReport(classic_script_url), |
| 'classic script from dynamic2.wbn'); |
| document.body.removeChild(link); |
| // And in this case, the script must be loaded from network. |
| assert_equals( |
| await loadScriptAndWaitReport(classic_script_url), |
| 'classic script from network'); |
| }, 'Dynamically loading classic script from web bundle with link.resources'); |
| |
| promise_test(async () => { |
| const classic_script_url = 'https://web-platform.test:8444/web-bundle/resources/wbn/dynamic/classic_script.js'; |
| const scope = 'https://web-platform.test:8444/web-bundle/resources/wbn/dynamic/'; |
| const link = document.createElement("link"); |
| link.rel = "webbundle"; |
| link.href = "../resources/wbn/dynamic1.wbn"; |
| link.scopes.add(scope); |
| document.body.appendChild(link); |
| assert_equals( |
| await loadScriptAndWaitReport(classic_script_url), |
| 'classic script from dynamic1.wbn'); |
| link.href = "../resources/wbn/dynamic2.wbn"; |
| // Loading the classic script should not reuse the previously loaded |
| // script. So in this case, the script must be loaded from dynamic2.wbn. |
| assert_equals( |
| await loadScriptAndWaitReport(classic_script_url), |
| 'classic script from dynamic2.wbn'); |
| // Changes the scope not to hit the classic_script.js. |
| link.scopes = scope + 'dummy'; |
| // And in this case, the script must be loaded from network. |
| assert_equals( |
| await loadScriptAndWaitReport(classic_script_url), |
| 'classic script from network'); |
| // Adds the scope to hit the classic_script.js. |
| link.scopes.add(scope + 'classic_'); |
| assert_equals( |
| await loadScriptAndWaitReport(classic_script_url), |
| 'classic script from dynamic2.wbn'); |
| document.body.removeChild(link); |
| // And in this case, the script must be loaded from network. |
| assert_equals( |
| await loadScriptAndWaitReport(classic_script_url), |
| 'classic script from network'); |
| }, 'Dynamically loading classic script from web bundle with link.scopes'); |
| |
| promise_test(() => { |
| return addLinkAndWaitForLoad("../resources/wbn/dynamic1.wbn?test-event"); |
| }, '<link rel="webbundle"> fires a load event on load success'); |
| |
| promise_test((t) => { |
| return addLinkAndWaitForError("../resources/wbn/nonexistent.wbn"); |
| }, '<link rel="webbundle"> fires an error event on load failure'); |
| |
| promise_test(async () => { |
| const link = document.createElement('link'); |
| link.rel = 'webbundle'; |
| link.href = '../resources/wbn/dynamic1-crossorigin.wbn'; |
| link.resources = 'https://www1.web-platform.test:8444/web-bundle/resources/wbn/dynamic/resource1.js'; |
| document.body.appendChild(link); |
| const module = await import(link.resources); |
| assert_equals(module.result, 'resource1 from network'); |
| }, 'Subresource URL must be same-origin with bundle URL'); |
| |
| promise_test(async () => { |
| const url = 'urn:uuid:020111b3-437a-4c5c-ae07-adb6bbffb720'; |
| const link = document.createElement('link'); |
| link.rel = 'webbundle'; |
| link.href = '../resources/wbn/urn-uuid.wbn'; |
| link.resources = url; |
| document.body.appendChild(link); |
| assert_equals(await loadScriptAndWaitReport(url), 'OK'); |
| document.body.removeChild(link); |
| }, 'Subresource loading with urn:uuid: URL with link.resources'); |
| |
| promise_test(async () => { |
| const url = 'urn:uuid:020111b3-437a-4c5c-ae07-adb6bbffb720'; |
| const link = document.createElement('link'); |
| link.rel = 'webbundle'; |
| link.href = '../resources/wbn/urn-uuid.wbn'; |
| link.scopes = 'urn:uuid:'; |
| document.body.appendChild(link); |
| assert_equals(await loadScriptAndWaitReport(url), 'OK'); |
| document.body.removeChild(link); |
| }, 'Subresource loading with urn:uuid: URL with link.scopes'); |
| |
| promise_test(async () => { |
| const wbn_url = 'https://web-platform.test:8444/web-bundle/resources/wbn/subresource.wbn?test-resources-update'; |
| const resource_url = 'https://web-platform.test:8444/web-bundle/resources/wbn/submodule.js'; |
| const link = await addLinkAndWaitForLoad(wbn_url); |
| link.resources.add(resource_url); |
| const resp = await fetch(resource_url, { cache: 'no-store' }); |
| assert_true(resp.ok); |
| assert_equals(performance.getEntriesByName(wbn_url).length, 1); |
| }, 'Updating resource= attribute should not reload the bundle'); |
| |
| function addLinkAndWaitForLoad(url) { |
| return new Promise((resolve, reject) => { |
| const link = document.createElement("link"); |
| link.rel = "webbundle"; |
| link.href = url; |
| link.onload = () => resolve(link); |
| link.onerror = reject; |
| document.body.appendChild(link); |
| }); |
| } |
| |
| function addLinkAndWaitForError(url) { |
| return new Promise((resolve, reject) => { |
| const link = document.createElement("link"); |
| link.rel = "webbundle"; |
| link.href = url; |
| link.onload = reject; |
| link.onerror = () => resolve(link); |
| document.body.appendChild(link); |
| }); |
| } |
| |
| async function loadScriptAndWaitReport(script_url) { |
| const result_promise = new Promise((resolve) => { |
| // This function will be called from script.js |
| window.report_result = resolve; |
| }); |
| |
| const script = document.createElement('script'); |
| script.src = script_url; |
| document.body.appendChild(script); |
| return result_promise; |
| } |
| </script> |
| </body> |