blob: 55b7e9d85fae34951238caa33a3b8b0e95a801d1 [file] [log] [blame]
<!DOCTYPE html>
<title>script type="webbundle" reuses webbundle resources</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>
window.TEST_WEB_BUNDLE_ELEMENT_TYPE = "script";
setup(() => {
assert_true(HTMLScriptElement.supports("webbundle"));
});
const wbn_url = "../resources/wbn/subresource.wbn";
const resource1 = "root.js";
const resource2 = "submodule.js";
const resource1_url = `../resources/wbn/${resource1}`;
const resource2_url = `../resources/wbn/${resource2}`;
let script1;
let script2;
function cleanUp() {
if (script1) {
script1.remove();
}
if (script2) {
script2.remove();
}
}
async function assertResource1CanBeFetched() {
const response = await fetch(resource1_url);
const text = await response.text();
assert_equals(text, "export * from './submodule.js';\n");
}
async function assertResource1CanNotBeFetched() {
const response = await fetch(resource1_url);
assert_equals(response.status, 404);
}
async function assertResource2CanBeFetched() {
const response = await fetch(resource2_url);
const text = await response.text();
assert_equals(text, "export const result = 'OK';\n");
}
function createScriptWebBundle1() {
return createWebBundleElement(wbn_url, /*resources=*/ [resource1]);
}
function createScriptWebBundle2(options) {
return createWebBundleElement(wbn_url, /*resources=*/ [resource2], /*options=*/ options);
}
async function appendScriptWebBundle1AndFetchResource1() {
clearWebBundleFetchCount();
script1 = createScriptWebBundle1();
document.body.append(script1);
await assertResource1CanBeFetched();
assert_equals(webBundleFetchCount(), 1);
}
function clearWebBundleFetchCount() {
performance.clearResourceTimings();
}
function webBundleFetchCount() {
return performance
.getEntriesByType("resource")
.filter((e) => e.name.endsWith("subresource.wbn")).length;
}
promise_test(async (t) => {
t.add_cleanup(cleanUp);
await appendScriptWebBundle1AndFetchResource1();
clearWebBundleFetchCount();
// Append script2 without removing script1.
// script2 should fetch the wbn again.
script2 = createScriptWebBundle2();
document.body.appendChild(script2);
await assertResource1CanBeFetched()
await assertResource2CanBeFetched();
assert_equals(webBundleFetchCount(), 1);
}, "A webbundle should be fetched again when new script element is appended.");
promise_test(async (t) => {
t.add_cleanup(cleanUp);
await appendScriptWebBundle1AndFetchResource1();
clearWebBundleFetchCount();
// Remove script1, then append script2
// script2 should reuse webbundle resources.
script1.remove();
script2 = createScriptWebBundle2();
document.body.append(script2);
await assertResource1CanNotBeFetched();
await assertResource2CanBeFetched();
assert_equals(webBundleFetchCount(), 0);
}, "'remove(), then append()' should reuse webbundle resources");
promise_test(async (t) => {
t.add_cleanup(cleanUp);
await appendScriptWebBundle1AndFetchResource1();
clearWebBundleFetchCount();
// Remove script1, then append script2 with an explicit 'same-origin' credentials mode.
script1.remove();
script2 = createScriptWebBundle2({ credentials: "same-origin" });
document.body.append(script2);
await assertResource1CanNotBeFetched();
await assertResource2CanBeFetched();
assert_equals(webBundleFetchCount(), 0);
}, "Should reuse webbundle resources if a credential mode is same");
promise_test(async (t) => {
t.add_cleanup(cleanUp);
await appendScriptWebBundle1AndFetchResource1();
clearWebBundleFetchCount();
// Remove script1, then append script2 with a different credentials mode.
script1.remove();
script2 = createScriptWebBundle2({ credentials: "omit" });
document.body.append(script2);
await assertResource1CanNotBeFetched();
await assertResource2CanBeFetched();
assert_equals(webBundleFetchCount(), 1);
}, "Should not reuse webbundle resources if a credentials mode is different (same-origin vs omit)");
promise_test(async (t) => {
t.add_cleanup(cleanUp);
await appendScriptWebBundle1AndFetchResource1();
clearWebBundleFetchCount();
// Remove script1, then append script2 with a different credentials mode.
script1.remove();
script2 = createScriptWebBundle2({ credentials: "include" });
document.body.append(script2);
await assertResource1CanNotBeFetched();
await assertResource2CanBeFetched();
assert_equals(webBundleFetchCount(), 1);
}, "Should not reuse webbundle resources if a credential mode is different (same-origin vs include");
promise_test(async (t) => {
t.add_cleanup(cleanUp);
await appendScriptWebBundle1AndFetchResource1();
clearWebBundleFetchCount();
// Remove script1, then append the removed one.
script1.remove();
document.body.append(script1);
await assertResource1CanNotBeFetched();
assert_equals(webBundleFetchCount(), 0);
}, "'remove(), then append()' for the same element should reuse webbundle resources");
promise_test(async (t) => {
t.add_cleanup(cleanUp);
await appendScriptWebBundle1AndFetchResource1();
clearWebBundleFetchCount();
// Multiple 'remove(), then append()' for the same element.
script1.remove();
document.body.append(script1);
script1.remove();
document.body.append(script1);
await assertResource1CanNotBeFetched();
assert_equals(webBundleFetchCount(), 0);
}, "Multiple 'remove(), then append()' for the same element should reuse webbundle resources");
promise_test(async (t) => {
t.add_cleanup(cleanUp);
await appendScriptWebBundle1AndFetchResource1();
clearWebBundleFetchCount();
// Remove script1.
script1.remove();
// Then append script2 in a separet task.
await new Promise(resolve => t.step_timeout(resolve, 0));
script2 = createScriptWebBundle2();
document.body.append(script2);
await assertResource1CanNotBeFetched();
await assertResource2CanBeFetched();
assert_equals(webBundleFetchCount(), 1);
}, "'remove(), then append() in a separate task' should not reuse webbundle resources");
promise_test(async (t) => {
t.add_cleanup(cleanUp);
await appendScriptWebBundle1AndFetchResource1();
clearWebBundleFetchCount();
// Use replaceWith() to replace script1 with script2.
// script2 should reuse webbundle resources.
script2 = createScriptWebBundle2();
script1.replaceWith(script2);
await assertResource1CanNotBeFetched();
await assertResource2CanBeFetched();
assert_equals(webBundleFetchCount(), 0);
}, "replaceWith() should reuse webbundle resources.");
promise_test(async (t) => {
t.add_cleanup(cleanUp);
await appendScriptWebBundle1AndFetchResource1();
clearWebBundleFetchCount();
// Move script1 to another document. Then append script2.
// script2 should reuse webbundle resources.
const another_document = new Document();
another_document.append(script1);
script2 = createScriptWebBundle2();
document.body.append(script2);
await assertResource1CanNotBeFetched();
await assertResource2CanBeFetched();
assert_equals(webBundleFetchCount(), 0);
// TODO: Test the following cases:
// - The resources are not loaded from the webbundle in the new Document
// (Probably better to use a <iframe>.contentDocument)
// - Even if we move the script element back to the original Document,
// even immediately, the resources are not loaded from the webbundle in the
// original Document.
}, "append() should reuse webbundle resoruces even if the old script was moved to another document.");
</script>
</body>