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
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
|
<!DOCTYPE html>
<title>Subresource Integrity for font
</title>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="/preload/resources/preload_helper.js"></script>
<script src="/common/utils.js"></script>
<script src="/common/get-host-info.sub.js"></script>
<body>
<script>
const integrities = {
sha256: 'sha256-xkrni1nquuAzPoWieTZ22i9RONF4y11sJyWgYQDVlxE=',
sha384: 'sha384-Vif8vpq+J5UhnTqtncDDyol01dZx9nurRqQcSGtlCf0L1G8P+YeTyUYyZn4LMGrl',
sha512: 'sha512-CVkJJeS4/8zBdqBHmpzMvbI987MEWpTVd1Y/w20UFU0+NWlJAQpl1d3lIyCF97CQ/N+t/gn4IkWP4pjuWWrg6A==',
incorrect_sha256: 'sha256-wrongwrongwrongwrongwrongwrongwrongvalue====',
incorrect_sha512: 'sha512-wrongwrongwrongwrongwrongwrongwrongwrongwrongwrongwrongwrongwrongwrongwrongwrongwrong===',
unknown_algo: 'foo666-8aBiAJl3ukQwSJ6eTs5wl6hGjnOtyXjcTRdAf89uIfY='
};
const run_test = (preload_success, main_load_success, name,
resource_url, extra_attributes, number_of_requests) => {
const test = async_test(name);
const link = document.createElement('link');
link.rel = 'preload';
link.as = 'font';
link.href = resource_url;
for (const attribute_name in extra_attributes) {
link[attribute_name] = extra_attributes[attribute_name];
}
const valid_preload_failed = test.step_func(() => {
assert_unreached('Valid preload fired error handler.');
});
const invalid_preload_succeeded = test.step_func(() => {
assert_unreached('Invalid preload load succeeded.');
});
const valid_main_load_failed = test.step_func(() => {
assert_unreached('Valid main load fired error handler.');
});
const invalid_main_load_succeeded = test.step_func(() => {
assert_unreached('Invalid main load succeeded.');
});
const main_load_pass = test.step_func(() => {
verifyNumberOfResourceTimingEntries(resource_url, number_of_requests);
test.done();
});
const preload_pass = test.step_func(async () => {
try {
await new FontFace('CanvasTest', `url("${resource_url}")`).load();
} catch (error) {
if (main_load_success) {
valid_main_load_failed();
} else {
main_load_pass();
}
}
if (main_load_success) {
main_load_pass();
} else {
invalid_main_load_succeeded();
}
});
if (preload_success) {
link.onload = preload_pass;
link.onerror = valid_preload_failed;
} else {
link.onload = invalid_preload_succeeded;
link.onerror = preload_pass;
}
document.body.appendChild(link);
};
verifyPreloadAndRTSupport();
const anonymous = '&pipe=header(Access-Control-Allow-Origin,*)';
const use_credentials = '&pipe=header(Access-Control-Allow-Credentials,true)|' +
'header(Access-Control-Allow-Origin,' + location.origin + ')';
const cross_origin_prefix = get_host_info().REMOTE_ORIGIN;
const file_path = '/fonts/CanvasTest.ttf';
// Note: About preload + font + CORS
//
// The CSS Font spec defines that font files always have to be fetched using
// anonymous-mode CORS.
//
// https://developer.mozilla.org/en-US/docs/Web/HTML/Link_types/preload#cors-enabled_fetches
// https://www.w3.org/TR/css-fonts-3/#font-fetching-requirements
//
// So that font loading (@font-face in CSS and FontFace.load()) always
// sends requests with anonymous-mode CORS. The crossOrigin attribute of
// <link rel="preload" as="font"> should be set as anonymout mode,
// too, even for same origin fetch. Otherwise, main font loading
// doesn't match the corresponding preloading due to credentials
// mode mismatch and the main font loading invokes another request.
// Needs CORS request even for same origin preload.
run_test(true, true, '<crossorigin="anonymous"> Same-origin with correct sha256 hash.',
file_path + '?' + token(),
{integrity: integrities.sha256, crossOrigin: 'anonymous'}, 1);
run_test(true, true, '<crossorigin="anonymous"> Same-origin with correct sha384 hash.',
file_path + '?' + token(),
{integrity: integrities.sha384, crossOrigin: 'anonymous'}, 1);
run_test(true, true, '<crossorigin="anonymous"> Same-origin with correct sha512 hash.',
file_path + '?' + token(),
{integrity: integrities.sha512, crossOrigin: 'anonymous'}, 1);
run_test(true, true, '<crossorigin="anonymous"> Same-origin with empty integrity.',
file_path + '?' + token(),
{integrity: '', crossOrigin: 'anonymous'}, 1);
run_test(true, true, '<crossorigin="anonymous"> Same-origin with no integrity.',
file_path + '?' + token(),
{crossOrigin: 'anonymous'}, 1);
run_test(false, false, '<crossorigin="anonymous"> Same-origin with incorrect hash.',
file_path + '?' + token(),
{integrity: integrities.incorrect_sha256, crossOrigin: 'anonymous'}, 1);
run_test(true, true, '<crossorigin="anonymous"> Same-origin with correct sha256 hash, options.',
file_path + '?' + token(),
{integrity: `${integrities.sha256}?foo=bar?spam=eggs`, crossOrigin: 'anonymous'}, 1);
run_test(true, true, '<crossorigin="anonymous"> Same-origin with unknown algorithm only.',
file_path + '?' + token(),
{integrity: integrities.unknown_algo, crossOrigin: 'anonymous'}, 1);
run_test(true, true, '<crossorigin="anonymous"> Same-origin with multiple sha256 hashes, including correct.',
file_path + '?' + token(),
{integrity: `${integrities.sha256} ${integrities.incorrect_sha256}`, crossOrigin: 'anonymous'}, 1);
run_test(true, true, '<crossorigin="anonymous"> Same-origin with multiple sha256 hashes, including unknown algorithm.',
file_path + '?' + token(),
{integrity: `${integrities.sha256} ${integrities.unknown_algo}`, crossOrigin: 'anonymous'}, 1);
run_test(true, true, '<crossorigin="anonymous"> Same-origin with sha256 mismatch, sha512 match.',
file_path + '?' + token(),
{integrity: `${integrities.incorrect_sha256} ${integrities.sha512}`, crossOrigin: 'anonymous'}, 1);
run_test(false, false, '<crossorigin="anonymous"> Same-origin with sha256 match, sha512 mismatch.',
file_path + '?' + token(),
{integrity: `${integrities.sha256} ${integrities.incorrect_sha512}`, crossOrigin: 'anonymous'}, 1);
// Main loading shouldn't match preloading due to credentials mode mismatch
// so the number of requests should be two.
run_test(true, true, 'Same-origin, not CORS request, with correct sha256 hash.',
file_path + '?' + token(),
{integrity: integrities.sha256}, 2);
// Main loading shouldn't match preloading due to credentials mode mismatch
// and the main loading should invoke another request. The main font loading
// always sends CORS request and doesn't support SRI by itself, so it should succeed.
run_test(false, true, 'Same-origin, not CORS request, with incorrect sha256 hash.',
file_path + '?' + token(),
{integrity: integrities.incorrect_sha256}, 2);
run_test(true, true, '<crossorigin="anonymous"> Cross-origin with correct sha256 hash, ACAO: *.',
cross_origin_prefix + file_path + '?' + token() + anonymous,
{integrity: integrities.sha256, crossOrigin: 'anonymous'}, 1);
run_test(false, false, '<crossorigin="anonymous"> Cross-origin with incorrect sha256 hash, ACAO: *.',
cross_origin_prefix + file_path + '?' + token() + anonymous,
{integrity: integrities.incorrect_sha256, crossOrigin: 'anonymous'}, 1);
run_test(false, false, '<crossorigin="anonymous"> Cross-origin with correct sha256 hash, with CORS-ineligible resource.',
cross_origin_prefix + file_path + '?' + token(),
{integrity: integrities.sha256, crossOrigin: 'anonymous'}, 1);
run_test(false, true, 'Cross-origin, not CORS request, with correct sha256.',
cross_origin_prefix + file_path + '?' + token() + anonymous,
{integrity: integrities.sha256}, 2);
run_test(false, true, 'Cross-origin, not CORS request, with incorrect sha256.',
cross_origin_prefix + file_path + '?' + token() + anonymous,
{integrity: integrities.incorrect_sha256}, 2);
run_test(true, true, '<crossorigin="anonymous"> Cross-origin with empty integrity.',
cross_origin_prefix + file_path + '?' + token() + anonymous,
{integrity: '', crossOrigin: 'anonymous'}, 1);
run_test(true, true, 'Cross-origin, not CORS request, with empty integrity.',
cross_origin_prefix + file_path + '?' + token() + anonymous,
{integrity: ''}, 2);
// Non-anonymous mode CORS preload request should mismatch the main load.
run_test(true, true, '<crossorigin="use-credentials"> Cross-origin with correct sha256 hash, CORS-eligible.',
cross_origin_prefix + file_path + '?' + token() + use_credentials,
{integrity: integrities.sha256, crossOrigin: 'use-credentials'}, 2);
run_test(false, true, '<crossorigin="use-credentials"> Cross-origin with incorrect sha256 hash, CORS-eligible.',
cross_origin_prefix + file_path + '?' + token() + use_credentials,
{integrity: integrities.incorrect_sha256, crossOrigin: 'use-credentials'}, 2);
</script>
</body>
</html>
|