Source code

Revision control

Copy as Markdown

Other Tools

Test Info:

<!DOCTYPE html>
<title>Subresource loading with script type="webbundle"</title>
<link
rel="help"
/>
<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": [
]
}
</script>
<script>
setup(() => {
assert_true(HTMLScriptElement.supports("webbundle"));
});
promise_test(async () => {
const module = await import(
);
assert_equals(module.result, "OK");
}, "Subresource loading with WebBundle");
promise_test(async () => {
const response = await fetch(
);
const text = await response.text();
assert_equals(text, "export * from './submodule.js';\n");
}, "Subresource loading with WebBundle (Fetch API)");
promise_test((t) => {
const url =
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", [
]);
document.body.appendChild(element);
const module = await import(
);
assert_equals(module.result, "resource1 from dynamic1.wbn");
const new_element = removeAndAppendNewElementWithUpdatedRule(element, {
url: "../resources/wbn/dynamic2.wbn",
});
const module2 = await import(
);
assert_equals(module2.result, "resource2 from dynamic2.wbn");
// A resource not specified in the resources attribute, but in the bundle.
const module3 = await import(
);
assert_equals(module3.result, "resource3 from network");
document.body.removeChild(new_element);
const module4 = await import(
);
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(
);
assert_equals(module_second.result, "resource1 from dynamic1.wbn");
}, "Dynamically adding / updating / removing the webbundle element.");
promise_test(async () => {
const classic_script_url =
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 =
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 =
const scope =
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 =
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>