Skip to content

Commit 47f30fc

Browse files
hayatoitochromium-wpt-export-bot
authored andcommitted
Introduce <script type=webbundle>
Introduce <script>-based API for subresource loading with Web Bundles. See the design doc [1] for the motivation of switching from <link>-based API to <script>-based API. The explainer [2] was already updated to use <script>-based API. This feature is guarded by `SubresourceWebBundles` flag. We eventually drop the <link rel=webbundle> support and remove the <link>-based API code once we can confirm <script>-based API can be used as a replacement of <link>-based API. This CL should be considered as the first step to switch to <script>-based API. There are still gaps between <link>-based API and <script>-based API, which will be addressed later [3]. This CL intentionally adds a very minimum test for <script>-based API because there are already WPT tests for <script type=webbundle>. They all have been marked as [ SKIP ] in TestExpectations until now. Now some of them are passing after this CL. We'll make the remaining tests pass in follow-up CLs, and also add tests which are specific to <script>-based API as necessary. These efforts should be tracked by crbug.com/1245166. [1]: https://docs.google.com/document/d/1q_SodTcLuwya4cXt1gIRaVrkiaBfwWyPvkY1fqRKkgM/edit?usp=sharing&resourcekey=0-dqrFOGVCYsg8WRZ4RFgwuw [2]: https://github.com/WICG/webpackage/blob/main/explainers/subresource-loading.md [3]: WICG/webpackage#670 Bug: 1245166 Change-Id: I5109b6e692baf10fd1d8a31a31d93176d4dc4ad2 Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/3128843 Commit-Queue: Hayato Ito <hayato@chromium.org> Reviewed-by: Tsuyoshi Horo <horo@chromium.org> Reviewed-by: Kunihiko Sakamoto <ksakamoto@chromium.org> Reviewed-by: Hiroshige Hayashizaki <hiroshige@chromium.org> Reviewed-by: Kouhei Ueno <kouhei@chromium.org> Cr-Commit-Position: refs/heads/main@{#933346}
1 parent aea6e4f commit 47f30fc

File tree

2 files changed

+215
-0
lines changed

2 files changed

+215
-0
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,199 @@
1+
<!DOCTYPE html>
2+
<title>script type="webbundle" reuses webbundle resources</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+
11+
<body>
12+
<script>
13+
window.TEST_WEB_BUNDLE_ELEMENT_TYPE = "script";
14+
setup(() => {
15+
assert_true(HTMLScriptElement.supports("webbundle"));
16+
});
17+
18+
const wbn_url = "../resources/wbn/subresource.wbn";
19+
const resource1 = "../resources/wbn/root.js";
20+
const resource2 = "../resources/wbn/submodule.js";
21+
22+
let script1;
23+
let script2;
24+
25+
function cleanUp() {
26+
if (script1) {
27+
script1.remove();
28+
}
29+
if (script2) {
30+
script2.remove();
31+
}
32+
}
33+
34+
async function assertResource1CanBeFetched() {
35+
const response = await fetch(resource1);
36+
const text = await response.text();
37+
assert_equals(text, "export * from './submodule.js';\n");
38+
}
39+
40+
async function assertResource1CanNotBeFetched() {
41+
const response = await fetch(resource1);
42+
assert_equals(response.status, 404);
43+
}
44+
45+
async function assertResource2CanBeFetched() {
46+
const response = await fetch(resource2);
47+
const text = await response.text();
48+
assert_equals(text, "export const result = 'OK';\n");
49+
}
50+
51+
function createScriptWebBundle1() {
52+
return createWebBundleElement(wbn_url, /*resources=*/ [resource1]);
53+
}
54+
55+
function createScriptWebBundle2() {
56+
return createWebBundleElement(wbn_url, /*resources=*/ [resource2]);
57+
}
58+
59+
async function appendScriptWebBundle1AndFetchResource1() {
60+
clearWebBundleFetchCount();
61+
script1 = createScriptWebBundle1();
62+
document.body.append(script1);
63+
await assertResource1CanBeFetched();
64+
assert_equals(webBundleFetchCount(), 1);
65+
}
66+
67+
function clearWebBundleFetchCount() {
68+
performance.clearResourceTimings();
69+
}
70+
71+
function webBundleFetchCount() {
72+
return performance
73+
.getEntriesByType("resource")
74+
.filter((e) => e.name.endsWith("subresource.wbn")).length;
75+
}
76+
77+
promise_test(async (t) => {
78+
t.add_cleanup(cleanUp);
79+
await appendScriptWebBundle1AndFetchResource1();
80+
clearWebBundleFetchCount();
81+
82+
// Append script2 without removing script1.
83+
// script2 should fetch the wbn again.
84+
script2 = createScriptWebBundle2();
85+
document.body.appendChild(script2);
86+
87+
await assertResource1CanBeFetched()
88+
await assertResource2CanBeFetched();
89+
assert_equals(webBundleFetchCount(), 1);
90+
}, "A webbundle should be fetched again when new script element is appended.");
91+
92+
promise_test(async (t) => {
93+
t.add_cleanup(cleanUp);
94+
await appendScriptWebBundle1AndFetchResource1();
95+
clearWebBundleFetchCount();
96+
97+
// Remove script1, then append script2
98+
// script2 should reuse webbundle resources.
99+
script1.remove();
100+
script2 = createScriptWebBundle2();
101+
document.body.append(script2);
102+
103+
await assertResource1CanNotBeFetched();
104+
await assertResource2CanBeFetched();
105+
assert_equals(webBundleFetchCount(), 0);
106+
}, "'remove(), then append()' should reuse webbundle resources");
107+
108+
promise_test(async (t) => {
109+
t.add_cleanup(cleanUp);
110+
await appendScriptWebBundle1AndFetchResource1();
111+
clearWebBundleFetchCount();
112+
113+
// Remove script1, then append the removed one.
114+
script1.remove();
115+
document.body.append(script1);
116+
117+
await assertResource1CanNotBeFetched();
118+
assert_equals(webBundleFetchCount(), 0);
119+
}, "'remove(), then append()' for the same element should reuse webbundle resources");
120+
121+
promise_test(async (t) => {
122+
t.add_cleanup(cleanUp);
123+
await appendScriptWebBundle1AndFetchResource1();
124+
clearWebBundleFetchCount();
125+
126+
// Multiple 'remove(), then append()' for the same element.
127+
script1.remove();
128+
document.body.append(script1);
129+
130+
script1.remove();
131+
document.body.append(script1);
132+
133+
await assertResource1CanNotBeFetched();
134+
assert_equals(webBundleFetchCount(), 0);
135+
}, "Multiple 'remove(), then append()' for the same element should reuse webbundle resources");
136+
137+
138+
promise_test(async (t) => {
139+
t.add_cleanup(cleanUp);
140+
await appendScriptWebBundle1AndFetchResource1();
141+
clearWebBundleFetchCount();
142+
143+
// Remove script1.
144+
script1.remove();
145+
146+
// Then append script2 in a separet task.
147+
await new Promise(resolve => t.step_timeout(resolve, 0));
148+
script2 = createScriptWebBundle2();
149+
document.body.append(script2);
150+
151+
await assertResource1CanNotBeFetched();
152+
await assertResource2CanBeFetched();
153+
assert_equals(webBundleFetchCount(), 1);
154+
}, "'remove(), then append() in a separate task' should not reuse webbundle resources");
155+
156+
157+
promise_test(async (t) => {
158+
t.add_cleanup(cleanUp);
159+
await appendScriptWebBundle1AndFetchResource1();
160+
clearWebBundleFetchCount();
161+
162+
// Use replaceWith() to replace script1 with script2.
163+
// script2 should reuse webbundle resources.
164+
script2 = createScriptWebBundle2();
165+
script1.replaceWith(script2);
166+
167+
await assertResource1CanNotBeFetched();
168+
await assertResource2CanBeFetched();
169+
170+
assert_equals(webBundleFetchCount(), 0);
171+
}, "replaceWith() should reuse webbundle resources.");
172+
173+
promise_test(async (t) => {
174+
t.add_cleanup(cleanUp);
175+
await appendScriptWebBundle1AndFetchResource1();
176+
clearWebBundleFetchCount();
177+
178+
// Move script1 to another document. Then append script2.
179+
// script2 should reuse webbundle resources.
180+
const another_document = new Document();
181+
another_document.append(script1);
182+
script2 = createScriptWebBundle2();
183+
document.body.append(script2);
184+
185+
await assertResource1CanNotBeFetched();
186+
await assertResource2CanBeFetched();
187+
188+
assert_equals(webBundleFetchCount(), 0);
189+
190+
// TODO: Test the following cases:
191+
// - The resources are not loaded from the webbundle in the new Document
192+
// (Probably better to use a <iframe>.contentDocument)
193+
// - Even if we move the script element back to the original Document,
194+
// even immediately, the resources are not loaded from the webbundle in the
195+
// original Document.
196+
}, "append() should reuse webbundle resoruces even if the old script was moved to another document.");
197+
198+
</script>
199+
</body>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
<DOCTYPE html>
2+
<html>
3+
<title>HTMLScriptElement.supports webbundle</title>
4+
<script src="/resources/testharness.js"></script>
5+
<script src="/resources/testharnessreport.js"></script>
6+
<script>
7+
test(() => {
8+
assert_true(HTMLScriptElement.supports('webbundle'));
9+
}, 'HTMLScriptElement.supports returns true for \'webbundle\'');
10+
11+
test(() => {
12+
assert_false(HTMLScriptElement.supports(' webbundle'));
13+
assert_false(HTMLScriptElement.supports('webbundle '));
14+
assert_false(HTMLScriptElement.supports('WEBBUNDLE'));
15+
}, 'HTMLScriptElement.supports returns false for unsupported types');
16+
</script>

0 commit comments

Comments
 (0)