1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
|
"use strict";
const SAME_ORIGIN = "https://{{host}}:{{ports[h2][0]}}";
const CROSS_ORIGIN = "https://{{hosts[alt][www]}}:{{ports[h2][0]}}";
const RESOURCES_PATH = "/loading/early-hints/resources";
const SAME_ORIGIN_RESOURCES_URL = SAME_ORIGIN + RESOURCES_PATH;
const CROSS_ORIGIN_RESOURCES_URL = CROSS_ORIGIN + RESOURCES_PATH;
/**
* Navigate to a test page with an Early Hints response.
*
* @typedef {Object} Preload
* @property {string} url - A URL to preload. Note: This is relative to the
* `test_url` parameter of `navigateToTestWithEarlyHints()`.
* @property {string} as_attr - `as` attribute of this preload.
*
* @param {string} test_url - URL of a test after the Early Hints response.
* @param {Array<Preload>} preloads - Preloads included in the Early Hints response.
*/
function navigateToTestWithEarlyHints(test_url, preloads) {
const params = new URLSearchParams();
params.set("test_url", test_url);
for (const preload of preloads) {
params.append("preloads", JSON.stringify(preload));
}
const url = "resources/early-hints-test-loader.h2.py?" + params.toString();
window.location.replace(new URL(url, window.location));
}
/**
* Parses the query string of the current window location and returns preloads
* in the Early Hints response sent via `navigateToTestWithEarlyHints()`.
*
* @returns {Array<Preload>}
*/
function getPreloadsFromSearchParams() {
const params = new URLSearchParams(window.location.search);
const encoded_preloads = params.getAll("preloads");
const preloads = [];
for (const encoded of encoded_preloads) {
preloads.push(JSON.parse(encoded));
}
return preloads;
}
/**
* Fetches a script or an image.
*
* @param {string} element - "script" or "img".
* @param {string} url - URL of the resource.
*/
async function fetchResource(element, url) {
return new Promise((resolve, reject) => {
const el = document.createElement(element);
el.src = url;
el.onload = resolve;
el.onerror = _ => reject(new Error("Failed to fetch resource: " + url));
document.body.appendChild(el);
});
}
/**
* Fetches a script.
*
* @param {string} url
*/
async function fetchScript(url) {
return fetchResource("script", url);
}
/**
* Fetches an image.
*
* @param {string} url
*/
async function fetchImage(url) {
return fetchResource("img", url);
}
/**
* Returns true when the resource is preloaded via Early Hints.
*
* @param {string} url
* @returns {boolean}
*/
function isPreloadedByEarlyHints(url) {
const entries = performance.getEntriesByName(url);
if (entries.length === 0) {
return false;
}
assert_equals(entries.length, 1);
return entries[0].initiatorType === "early-hints";
}
/**
* Navigate to the referrer policy test page.
*
* @param {string} referrer_policy - A value of Referrer-Policy to test.
*/
function testReferrerPolicy(referrer_policy) {
const params = new URLSearchParams();
params.set("referrer-policy", referrer_policy);
const same_origin_preload_url = SAME_ORIGIN_RESOURCES_URL + "/fetch-and-record-js.h2.py?id=" + token();
params.set("same-origin-preload-url", same_origin_preload_url);
const cross_origin_preload_url = CROSS_ORIGIN_RESOURCES_URL + "/fetch-and-record-js.h2.py?id=" + token();
params.set("cross-origin-preload-url", cross_origin_preload_url);
const path = "resources/referrer-policy-test-loader.h2.py?" + params.toString();
const url = new URL(path, window.location);
window.location.replace(url);
}
/**
* Navigate to the content security policy basic test. The test page sends an
* Early Hints response with a cross origin resource preload. CSP headers are
* configured based on the given policies. A policy should be one of the
* followings:
* "absent" - Do not send Content-Security-Policy header
* "allowed" - Set Content-Security-Policy to allow the cross origin preload
* "disallowed" - Set Content-Security-Policy to disallow the cross origin preload
*
* @param {string} early_hints_policy - The policy for the Early Hints response
* @param {string} final_policy - The policy for the final response
*/
function navigateToContentSecurityPolicyBasicTest(
early_hints_policy, final_policy) {
const params = new URLSearchParams();
params.set("resource-origin", CROSS_ORIGIN);
params.set("resource-url",
CROSS_ORIGIN_RESOURCES_URL + "/empty.js?" + token());
params.set("early-hints-policy", early_hints_policy);
params.set("final-policy", final_policy);
const url = "resources/csp-basic-loader.h2.py?" + params.toString();
window.location.replace(new URL(url, window.location));
}
/**
* Navigate to a test page which sends an Early Hints containing a cross origin
* preload link with/without Content-Security-Policy header. The CSP header is
* configured based on the given policy. The test page disallows the preload
* while the preload is in-flight. The policy should be one of the followings:
* "absent" - Do not send Content-Security-Policy header
* "allowed" - Set Content-Security-Policy to allow the cross origin preload
*
* @param {string} early_hints_policy
*/
function navigateToContentSecurityPolicyDocumentDisallowTest(early_hints_policy) {
const resource_id = token();
const params = new URLSearchParams();
params.set("resource-origin", CROSS_ORIGIN);
params.set("resource-url",
CROSS_ORIGIN_RESOURCES_URL + "/delayed-js.h2.py?id=" + resource_id);
params.set("resume-url",
CROSS_ORIGIN_RESOURCES_URL + "/resume-delayed-js.h2.py?id=" + resource_id);
params.set("early-hints-policy", early_hints_policy);
const url = "resources/csp-document-disallow-loader.h2.py?" + params.toString();
window.location.replace(new URL(url, window.location));
}
/**
* Navigate to a test page which sends different Cross-Origin-Embedder-Policy
* values in an Early Hints response and the final response.
*
* @param {string} early_hints_policy - The policy for the Early Hints response
* @param {string} final_policy - The policy for the final response
*/
function navigateToCrossOriginEmbedderPolicyMismatchTest(
early_hints_policy, final_policy) {
const params = new URLSearchParams();
params.set("resource-url",
CROSS_ORIGIN_RESOURCES_URL + "/empty-corp-absent.js?" + token());
params.set("early-hints-policy", early_hints_policy);
params.set("final-policy", final_policy);
const url = "resources/coep-mismatch.h2.py?" + params.toString();
window.location.replace(new URL(url, window.location));
}
|