294 lines
11 KiB
HTML
294 lines
11 KiB
HTML
<!DOCTYPE html>
|
|
<title>Subresource loading with script type="webbundle"</title>
|
|
<link
|
|
rel="help"
|
|
href="https://github.com/WICG/webpackage/blob/main/explainers/subresource-loading.md"
|
|
/>
|
|
<script src="/resources/testharness.js"></script>
|
|
<script src="/resources/testharnessreport.js"></script>
|
|
<script src="../resources/test-helpers.js"></script>
|
|
<body>
|
|
<script type="webbundle">
|
|
{
|
|
"source": "../resources/wbn/subresource.wbn",
|
|
"resources": [
|
|
"https://{{domains[]}}:{{ports[https][0]}}/web-bundle/resources/wbn/root.js",
|
|
"https://{{domains[]}}:{{ports[https][0]}}/web-bundle/resources/wbn/submodule.js"
|
|
]
|
|
}
|
|
</script>
|
|
<script>
|
|
setup(() => {
|
|
assert_true(HTMLScriptElement.supports("webbundle"));
|
|
});
|
|
|
|
promise_test(async () => {
|
|
const module = await import(
|
|
"https://{{domains[]}}:{{ports[https][0]}}/web-bundle/resources/wbn/root.js"
|
|
);
|
|
assert_equals(module.result, "OK");
|
|
}, "Subresource loading with WebBundle");
|
|
|
|
promise_test(async () => {
|
|
const response = await fetch(
|
|
"https://{{domains[]}}:{{ports[https][0]}}/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://{{domains[]}}:{{ports[https][0]}}/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 element = createWebBundleElement("../resources/wbn/dynamic1.wbn", [
|
|
"https://{{domains[]}}:{{ports[https][0]}}/web-bundle/resources/wbn/dynamic/resource1.js",
|
|
"https://{{domains[]}}:{{ports[https][0]}}/web-bundle/resources/wbn/dynamic/resource2.js",
|
|
"https://{{domains[]}}:{{ports[https][0]}}/web-bundle/resources/wbn/dynamic/resource4.js",
|
|
]);
|
|
document.body.appendChild(element);
|
|
|
|
const module = await import(
|
|
"https://{{domains[]}}:{{ports[https][0]}}/web-bundle/resources/wbn/dynamic/resource1.js"
|
|
);
|
|
assert_equals(module.result, "resource1 from dynamic1.wbn");
|
|
|
|
const new_element = removeAndAppendNewElementWithUpdatedRule(element, {
|
|
url: "../resources/wbn/dynamic2.wbn",
|
|
});
|
|
const module2 = await import(
|
|
"https://{{domains[]}}:{{ports[https][0]}}/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://{{domains[]}}:{{ports[https][0]}}/web-bundle/resources/wbn/dynamic/resource3.js"
|
|
);
|
|
assert_equals(module3.result, "resource3 from network");
|
|
|
|
document.body.removeChild(new_element);
|
|
const module4 = await import(
|
|
"https://{{domains[]}}:{{ports[https][0]}}/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://{{domains[]}}:{{ports[https][0]}}/web-bundle/resources/wbn/dynamic/resource1.js"
|
|
);
|
|
assert_equals(module_second.result, "resource1 from dynamic1.wbn");
|
|
}, "Dynamically adding / updating / removing the webbundle element.");
|
|
|
|
promise_test(async () => {
|
|
const classic_script_url =
|
|
"https://{{domains[]}}:{{ports[https][0]}}/web-bundle/resources/wbn/dynamic/classic_script.js";
|
|
const element = createWebBundleElement("../resources/wbn/dynamic1.wbn", [
|
|
classic_script_url,
|
|
]);
|
|
document.body.appendChild(element);
|
|
assert_equals(
|
|
await loadScriptAndWaitReport(classic_script_url),
|
|
"classic script from dynamic1.wbn"
|
|
);
|
|
const new_element = removeAndAppendNewElementWithUpdatedRule(element, {
|
|
url: "../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(new_element);
|
|
// 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");
|
|
|
|
promise_test(async (t) => {
|
|
// To avoid caching mechanism, this test is using fetch() API with
|
|
// { cache: 'no-store' } to load the resource.
|
|
const classic_script_url =
|
|
"https://{{domains[]}}:{{ports[https][0]}}/web-bundle/resources/wbn/dynamic/classic_script.js";
|
|
|
|
assert_equals(
|
|
await (await fetch(classic_script_url)).text(),
|
|
"window.report_result('classic script from network');\n"
|
|
);
|
|
|
|
const element1 = createWebBundleElement("../resources/wbn/dynamic1.wbn", [
|
|
classic_script_url,
|
|
]);
|
|
document.body.appendChild(element1);
|
|
t.add_cleanup(() => {
|
|
if (element1.parentElement)
|
|
element1.parentElement.removeChild(element1);
|
|
});
|
|
|
|
assert_equals(
|
|
await (await fetch(classic_script_url, { cache: "no-store" })).text(),
|
|
"window.report_result('classic script from dynamic1.wbn');\n"
|
|
);
|
|
|
|
const element2 = createWebBundleElement("../resources/wbn/dynamic2.wbn", [
|
|
classic_script_url,
|
|
]);
|
|
document.body.appendChild(element2);
|
|
t.add_cleanup(() => {
|
|
if (element2.parentElement)
|
|
element2.parentElement.removeChild(element2);
|
|
});
|
|
|
|
assert_equals(
|
|
await (await fetch(classic_script_url, { cache: "no-store" })).text(),
|
|
"window.report_result('classic script from dynamic2.wbn');\n"
|
|
);
|
|
|
|
document.body.removeChild(element2);
|
|
|
|
assert_equals(
|
|
await (await fetch(classic_script_url, { cache: "no-store" })).text(),
|
|
"window.report_result('classic script from dynamic1.wbn');\n"
|
|
);
|
|
|
|
document.body.removeChild(element1);
|
|
|
|
assert_equals(
|
|
await (await fetch(classic_script_url, { cache: "no-store" })).text(),
|
|
"window.report_result('classic script from network');\n"
|
|
);
|
|
}, "Multiple web bundle elements. The last added element must be refered.");
|
|
|
|
promise_test(async () => {
|
|
const classic_script_url =
|
|
"https://{{domains[]}}:{{ports[https][0]}}/web-bundle/resources/wbn/dynamic/classic_script.js";
|
|
const scope =
|
|
"https://{{domains[]}}:{{ports[https][0]}}/web-bundle/resources/wbn/dynamic/";
|
|
const element = createWebBundleElement(
|
|
"../resources/wbn/dynamic1.wbn",
|
|
[],
|
|
{ scopes: [scope] }
|
|
);
|
|
document.body.appendChild(element);
|
|
assert_equals(
|
|
await loadScriptAndWaitReport(classic_script_url),
|
|
"classic script from dynamic1.wbn"
|
|
);
|
|
const new_element = removeAndAppendNewElementWithUpdatedRule(element, {
|
|
url: "../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.
|
|
const new_element2 = removeAndAppendNewElementWithUpdatedRule(
|
|
new_element,
|
|
{ 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.
|
|
const new_element3 = removeAndAppendNewElementWithUpdatedRule(
|
|
new_element2,
|
|
{ scopes: [scope + "dummy", scope + "classic_"] }
|
|
);
|
|
assert_equals(
|
|
await loadScriptAndWaitReport(classic_script_url),
|
|
"classic script from dynamic2.wbn"
|
|
);
|
|
document.body.removeChild(new_element3);
|
|
// 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 scopes");
|
|
|
|
promise_test(() => {
|
|
return addWebBundleElementAndWaitForLoad(
|
|
"../resources/wbn/dynamic1.wbn?test-event",
|
|
/*resources=*/ [],
|
|
{ crossOrigin: undefined }
|
|
);
|
|
}, "The webbundle element fires a load event on load success");
|
|
|
|
promise_test((t) => {
|
|
return addWebBundleElementAndWaitForError(
|
|
"../resources/wbn/nonexistent.wbn",
|
|
/*resources=*/ [],
|
|
{ crossOrigin: undefined }
|
|
);
|
|
}, "The webbundle element fires an error event on load failure");
|
|
|
|
promise_test(async () => {
|
|
const module_script_url =
|
|
"https://www1.{{domains[]}}:{{ports[https][0]}}/web-bundle/resources/wbn/dynamic/resource1.js";
|
|
const element = createWebBundleElement(
|
|
"../resources/wbn/dynamic1-crossorigin.wbn",
|
|
[module_script_url]
|
|
);
|
|
document.body.appendChild(element);
|
|
const module = await import(module_script_url);
|
|
assert_equals(module.result, "resource1 from network");
|
|
}, "Subresource URL must be same-origin with bundle URL");
|
|
|
|
promise_test(async () => {
|
|
const url = "uuid-in-package:020111b3-437a-4c5c-ae07-adb6bbffb720";
|
|
const element = createWebBundleElement(
|
|
"../resources/wbn/uuid-in-package.wbn",
|
|
[url]
|
|
);
|
|
document.body.appendChild(element);
|
|
assert_equals(await loadScriptAndWaitReport(url), "OK");
|
|
document.body.removeChild(element);
|
|
}, "Subresource loading with uuid-in-package: URL with resources attribute");
|
|
|
|
promise_test(async () => {
|
|
const url = "uuid-in-package:020111b3-437a-4c5c-ae07-adb6bbffb720";
|
|
const element = createWebBundleElement(
|
|
"../resources/wbn/uuid-in-package.wbn",
|
|
[],
|
|
{ scopes: ["uuid-in-package:"] }
|
|
);
|
|
document.body.appendChild(element);
|
|
assert_equals(await loadScriptAndWaitReport(url), "OK");
|
|
document.body.removeChild(element);
|
|
}, "Subresource loading with uuid-in-package: URL with scopes attribute");
|
|
|
|
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;
|
|
}
|
|
|
|
function removeAndAppendNewElementWithUpdatedRule(element, new_rule) {
|
|
const new_element = createNewWebBundleElementWithUpdatedRule(
|
|
element,
|
|
new_rule
|
|
);
|
|
element.remove();
|
|
document.body.appendChild(new_element);
|
|
return new_element;
|
|
}
|
|
</script>
|
|
</body>
|