Skip to content

Commit 77a55e2

Browse files
noamrmoz-wptsync-bot
authored andcommitted
Bug 1810933 [wpt PR 35707] - Add multiple tests for prefetch behavior, a=testonly
Automatic update from web-platform-tests Add multiple tests for prefetch behavior See whatwg/html#8111 (comment) Tests that: * preload/prefetch-cache.html No 5 minute rule (cache headers must be respected) * preload/prefetch-document.html No special treatment of as=document Whether Accept is left as its default value. Storage partitioning is applied, including for documents * preload/prefetch-load-event.html Does not block the load event * preload/prefetch-headers.html Whether any additional headers (Sec-Purpose, Purpose, X-moz, X-Purpose) are sent (depending on what we put in the spec) * preload/prefetch-types.html Always uses prefetch destination, and ignores as="" -- Add tests for load/error events -- Add cross-origin event tests -- Test for proprietary headers -- Add test cases for cross-origin error events -- Update preload/prefetch-cache.html Co-authored-by: Domenic Denicola <d@domenic.me> -- Fix CR comments -- Clean up doc test -- Retries in cache test -- Use script instead of fetch to de-flake firefox -- wpt-commits: 56746bfcf1b8eae23f81f69c144d54bb7441b9c6, 9e12c604be29a47a730ecdb3c1382e678eb9eca4, a221f4091d67ca56f9218929ab019b4b9c2f2302, 5fe9b213f64e80cddb5de0aa8debec29f5ab20ad, 67113e16693e30945f2d2b71a8a4b3f556fe4353, c03154bde9e293f82a631b80f841908c25927493, 18faf32573d6d83c1932167e23bb8f6c37df3eb1, 8f2af79a66aeaece84373ce5e24023edb2c55f08, 8ae300754a3dd226cd58b3fd53e270cf141c28fc, 1423a3236683cc4856158c54e9f8f29eef3d2871 wpt-pr: 35707
1 parent 223f95b commit 77a55e2

7 files changed

+359
-0
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
<!DOCTYPE html>
2+
<title>Ensures that prefetch respects HTTP cache semantics</title>
3+
<script src="/resources/testharness.js"></script>
4+
<script src="/resources/testharnessreport.js"></script>
5+
<script src="/common/utils.js"></script>
6+
<script src="resources/prefetch-helper.js"></script>
7+
<body>
8+
<script>
9+
10+
async function prefetch_and_count(cacheControl, t) {
11+
const {href} = await prefetch({
12+
"cache-control": cacheControl,
13+
"type": "application/javascript",
14+
content: "/**/"}, t);
15+
const script = document.createElement("script");
16+
script.src = href;
17+
t.add_cleanup(() => script.remove());
18+
const loaded = new Promise(resolve => script.addEventListener("load", resolve));
19+
document.body.appendChild(script);
20+
await loaded;
21+
const info = await get_prefetch_info(href);
22+
return info.length;
23+
}
24+
25+
promise_test(async t => {
26+
const result = await prefetch_and_count("max-age=604800", t);
27+
assert_equals(result, 1);
28+
}, "Prefetch should populate the HTTP cache");
29+
30+
for (const cacheControl of ["no-cache", "no-store", "max-age=0"]) {
31+
promise_test(async t => {
32+
const result = await prefetch_and_count(cacheControl, t);
33+
assert_equals(result, 2);
34+
}, `Prefetch should respect cache-control: ${cacheControl}`);
35+
}
36+
</script>
37+
</body>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
<!DOCTYPE html>
2+
<title>Ensures that prefetch works with documents</title>
3+
<script src="/resources/testharness.js"></script>
4+
<script src="/resources/testharnessreport.js"></script>
5+
<script src="/common/utils.js"></script>
6+
<script src="/common/get-host-info.sub.js"></script>
7+
<script src="/common/dispatcher/dispatcher.js"></script>
8+
<script src="resources/prefetch-helper.js"></script>
9+
<body>
10+
<script>
11+
12+
const {ORIGIN, REMOTE_ORIGIN, HTTP_NOTSAMESITE_ORIGIN} = get_host_info();
13+
const loaders = {
14+
image: {
15+
file: 'square.png',
16+
type: 'image/png',
17+
load: href => {
18+
const image = document.createElement('img');
19+
image.src = href;
20+
document.body.appendChild(image);
21+
return new Promise(resolve => image.addEventListener('load', resolve));
22+
}
23+
},
24+
script: {
25+
file: 'dummy.js',
26+
type: 'application/javascript',
27+
load: href => {
28+
const script = document.createElement('script');
29+
script.src = href;
30+
document.body.appendChild(script);
31+
return new Promise(resolve => script.addEventListener('load', resolve));
32+
}
33+
},
34+
style: {
35+
file: 'dummy.css',
36+
type: 'text/css',
37+
load: href => {
38+
const link = document.createElement('link');
39+
link.href = href;
40+
link.rel = "stylesheet";
41+
document.body.appendChild(link);
42+
return new Promise(resolve => link.addEventListener('load', resolve));
43+
}
44+
},
45+
document: {
46+
file: 'empty.html',
47+
type: 'text/html',
48+
load: href => {
49+
const iframe = document.createElement("iframe");
50+
iframe.src = href;
51+
document.body.appendChild(iframe);
52+
return new Promise(resolve => iframe.addEventListener("load", resolve));
53+
}
54+
}
55+
};
56+
57+
async function prefetch_document_and_count_fetches(options, t) {
58+
const {href, uid} = await prefetch({
59+
file: "prefetch-exec.html",
60+
type: "text/html",
61+
corssOrigin: "anonymous",
62+
...options});
63+
const popup = window.open(href);
64+
const remoteContext = new RemoteContext(uid);
65+
t.add_cleanup(() => popup.close());
66+
const result = await remoteContext.execute_script(() => "OK");
67+
assert_equals(result, "OK");
68+
const requests = await get_prefetch_info(href);
69+
return requests.length;
70+
}
71+
72+
promise_test(async t => {
73+
assert_equals(await prefetch_document_and_count_fetches({origin: ORIGIN}, t), 1);
74+
}, "same origin document prefetch without 'as' should be consumed");
75+
76+
promise_test(async t => {
77+
assert_equals(await prefetch_document_and_count_fetches({origin: REMOTE_ORIGIN}, t), 1);
78+
}, "same-site different-origin document prefetch without 'as' should be consumed");
79+
80+
promise_test(async t => {
81+
assert_equals(await prefetch_document_and_count_fetches({origin: HTTP_NOTSAMESITE_ORIGIN}, t), 2);
82+
}, "different-site document prefetch without 'as' should not be consumed");
83+
84+
promise_test(async t => {
85+
assert_equals(await prefetch_document_and_count_fetches({origin: HTTP_NOTSAMESITE_ORIGIN, as: "document"}, t), 2);
86+
}, "different-site document prefetch with 'as=document' should not be consumed");
87+
88+
promise_test(async t => {
89+
const {href, uid} = await prefetch({
90+
file: "prefetch-exec.html",
91+
type: "text/html",
92+
corssOrigin: "anonymous",
93+
origin: ORIGIN});
94+
const popup = window.open(href + "&cache_bust=" + token());
95+
const remoteContext = new RemoteContext(uid);
96+
t.add_cleanup(() => popup.close());
97+
await remoteContext.execute_script(() => "OK");
98+
const results = await get_prefetch_info(href);
99+
assert_equals(results.length, 2);
100+
assert_equals(results[0].headers.accept, results[1].headers.accept);
101+
}, "Document prefetch should send the exact Accept header as navigation")
102+
</script>
103+
</body>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
<!DOCTYPE html>
2+
<title>Ensures that prefetch respects HTTP cache semantics</title>
3+
<meta name="timeout" content="long">
4+
<script src="/resources/testharness.js"></script>
5+
<script src="/resources/testharnessreport.js"></script>
6+
<script src="/common/utils.js"></script>
7+
<script src="resources/prefetch-helper.js"></script>
8+
<script src="/common/get-host-info.sub.js"></script>
9+
<body>
10+
<script>
11+
const {REMOTE_ORIGIN} = get_host_info();
12+
async function prefetch(link, uid, t) {
13+
link.rel = "prefetch";
14+
document.head.appendChild(link);
15+
const event = new Promise(resolve => {
16+
link.addEventListener("error", () => resolve("error"));
17+
link.addEventListener("load", () => resolve("load"));
18+
t.step_timeout(() => resolve("timeout"), 1000);
19+
});
20+
return await event;
21+
}
22+
23+
promise_test(async t => {
24+
const uid = token();
25+
const link = document.createElement("link");
26+
link.href = `/preload/resources/prefetch-info.py?key=${uid}`;
27+
const event = await prefetch(link, uid, t);
28+
assert_equals(event, "load");
29+
}, "Prefetch should fire the load event");
30+
31+
promise_test(async t => {
32+
const uid = token();
33+
const link = document.createElement("link");
34+
link.href = `${REMOTE_ORIGIN}/preload/resources/prefetch-info.py?key=${uid}`;
35+
const event = await prefetch(link, uid, t);
36+
assert_equals(event, "load");
37+
}, "Cross-origin prefetch should fire the load event");
38+
39+
promise_test(async t => {
40+
const uid = token();
41+
const link = document.createElement("link");
42+
link.href = `/preload/resources/prefetch-info.py?key=${uid}&status=404`;
43+
const event = await prefetch(link, uid, t);
44+
assert_equals(event, "load");
45+
}, "Prefetch should fire the load event for 404");
46+
47+
promise_test(async t => {
48+
const uid = token();
49+
const link = document.createElement("link");
50+
link.href = `${REMOTE_ORIGIN}/preload/resources/prefetch-info.py?key=${uid}&status=404`;
51+
const event = await prefetch(link, uid, t);
52+
assert_equals(event, "load");
53+
}, "Prefetch should fire the load event for 404 (cross-origin)");
54+
55+
promise_test(async t => {
56+
const uid = token();
57+
const link = document.createElement("link");
58+
link.href = `/preload/resources/prefetch-info.py?key=${uid}&status=500`;
59+
const event = await prefetch(link, uid, t);
60+
assert_equals(event, "load");
61+
}, "Prefetch should fire the load event for 500");
62+
63+
promise_test(async t => {
64+
const uid = token();
65+
const link = document.createElement("link");
66+
link.href = `${REMOTE_ORIGIN}/preload/resources/prefetch-info.py?key=${uid}&status=500`;
67+
const event = await prefetch(link, uid, t);
68+
assert_equals(event, "load");
69+
}, "Cross-origin prefetch should fire the load event for 500");
70+
71+
promise_test(async t => {
72+
const uid = token();
73+
const link = document.createElement("link");
74+
link.crossOrigin = "anonymous";
75+
link.href = `${REMOTE_ORIGIN}/preload/resources/prefetch-info.py?key=${uid}&cors=false`;
76+
const event = await prefetch(link, uid, t);
77+
assert_equals(event, "error");
78+
}, "Prefetch should fire the error event for network errors");
79+
80+
promise_test(async t => {
81+
const uid = token();
82+
const link = document.createElement("link");
83+
link.crossOrigin = "anonymous";
84+
const event = await prefetch(link, uid, t);
85+
assert_equals(event, "timeout");
86+
}, "Prefetch should do nothing with an empty href");
87+
88+
promise_test(async t => {
89+
const uid = token();
90+
const link = document.createElement("link");
91+
link.href = "https://example.com\u0000mozilla.org";
92+
link.crossOrigin = "anonymous";
93+
const event = await prefetch(link, uid, t);
94+
assert_equals(event, "timeout");
95+
}, "Prefetch should do nothing with an invalid href");
96+
</script>
97+
</body>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
<!DOCTYPE html>
2+
<title>Ensures that prefetch sends headers as per-spec</title>
3+
<script src="/resources/testharness.js"></script>
4+
<script src="/resources/testharnessreport.js"></script>
5+
<script src="/common/utils.js"></script>
6+
<script src="resources/prefetch-helper.js"></script>
7+
<body>
8+
<script>
9+
10+
promise_test(async t => {
11+
const {href} = await prefetch({"type": "image/png", file: "../../images/green.png"}, t);
12+
const [info] = await get_prefetch_info(href);
13+
const {headers} = info;
14+
assert_equals(headers["sec-fetch-dest"], "empty");
15+
assert_equals(headers["sec-purpose"], "prefetch");
16+
assert_false("origin" in headers);
17+
}, "Prefetch should include Sec-Purpose=prefetch and Sec-Fetch-Dest=empty headers");
18+
19+
promise_test(async t => {
20+
const {href} = await prefetch({"type": "image/png", file: "../../images/green.png"}, t);
21+
const [info] = await get_prefetch_info(href);
22+
const {headers} = info;
23+
assert_false("purpose" in headers);
24+
assert_false("x-moz" in headers);
25+
}, "Prefetch should not include proprietary headers (X-moz/Purpose)");
26+
27+
promise_test(async t => {
28+
const {href} = await prefetch({"type": "image/png", file: "../../images/green.png", crossOrigin: "anonymous"}, t);
29+
const [info] = await get_prefetch_info(href);
30+
const {headers} = info;
31+
assert_equals(headers["origin"], document.origin);
32+
}, "Prefetch should respect CORS mode");
33+
34+
</script>
35+
</body>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
<!DOCTYPE html>
2+
<script src="/resources/testharness.js"></script>
3+
<script src="/resources/testharnessreport.js"></script>
4+
<script src="/common/utils.js"></script>
5+
<link rel="prefetch" href="/xhr/resources/delay.py?ms=100000">
6+
<body>
7+
<script>
8+
9+
promise_test(() => new Promise(resolve => window.addEventListener("load", resolve)),
10+
"Prefetch should not block the load event");
11+
12+
</script>
13+
</body>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
<!DOCTYPE html>
2+
<title>Ensures that prefetch is not specific to resource types</title>
3+
<script src="/resources/testharness.js"></script>
4+
<script src="/resources/testharnessreport.js"></script>
5+
<script src="/common/utils.js"></script>
6+
<script src="/common/get-host-info.sub.js"></script>
7+
<script src="resources/prefetch-helper.js"></script>
8+
<body>
9+
<script>
10+
const host_info = get_host_info();
11+
const loaders = {
12+
"": {
13+
file: "../../common/dummy.xml",
14+
type: "text/xml",
15+
load: fetch
16+
},
17+
image: {
18+
file: '../../images/green.png',
19+
type: 'image/png',
20+
load: href => {
21+
const image = document.createElement('img');
22+
image.src = href;
23+
document.body.appendChild(image);
24+
return new Promise(resolve => image.addEventListener('load', resolve));
25+
}
26+
},
27+
script: {
28+
file: 'dummy.js',
29+
type: 'application/javascript',
30+
load: href => {
31+
const script = document.createElement('script');
32+
script.src = href;
33+
document.body.appendChild(script);
34+
return new Promise(resolve => script.addEventListener('load', resolve));
35+
}
36+
},
37+
style: {
38+
file: 'dummy.css',
39+
type: 'text/css',
40+
load: href => {
41+
const link = document.createElement('link');
42+
link.href = href;
43+
link.rel = "stylesheet";
44+
document.body.appendChild(link);
45+
return new Promise(resolve => link.addEventListener('load', resolve));
46+
}
47+
},
48+
document: {
49+
file: 'empty.html',
50+
type: 'text/html',
51+
load: href => {
52+
const iframe = document.createElement("iframe");
53+
iframe.src = href;
54+
document.body.appendChild(iframe);
55+
return new Promise(resolve => iframe.addEventListener("load", resolve));
56+
}
57+
}
58+
};
59+
60+
for (const as in loaders) {
61+
for (const consumer in loaders) {
62+
const {file, type, load} = loaders[as]
63+
promise_test(async t => {
64+
const {href} = await prefetch({file, type, as, origin: host_info[origin]});
65+
const requests = await get_prefetch_info(href);
66+
assert_equals(requests.length, 1);
67+
assert_equals(requests[0].headers["sec-fetch-dest"], "empty");
68+
}, `Prefetch as=${as} should work when consumed as ${consumer} (${origin})`);
69+
}
70+
}
71+
72+
</script>
73+
</body>

testing/web-platform/tests/preload/resources/prefetch-helper.js

+1
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ async function get_prefetch_info(href) {
66
async function prefetch(p = {}, t) {
77
const link = document.createElement("link");
88
link.rel = "prefetch";
9+
link.as = p.as;
910
if (p.crossOrigin)
1011
link.setAttribute("crossorigin", p.crossOrigin);
1112
const uid = token();

0 commit comments

Comments
 (0)