171 lines
No EOL
6.6 KiB
HTML
171 lines
No EOL
6.6 KiB
HTML
<!doctype html>
|
|
<script src="/resources/testharness.js"></script>
|
|
<script src="/resources/testharnessreport.js"></script>
|
|
<script src="/common/utils.js"></script>
|
|
<script src="/preload/resources/preload_helper.js"></script>
|
|
<script src="/common/get-host-info.sub.js"></script>
|
|
<script>
|
|
|
|
const {HTTPS_REMOTE_ORIGIN} = get_host_info();
|
|
|
|
function createEchoURL(text, type) {
|
|
return `/preload/resources/echo-with-cors.py?type=${
|
|
encodeURIComponent(type)}&content=${
|
|
encodeURIComponent(text)}&uid=${token()}`
|
|
}
|
|
const urls = {
|
|
image: createEchoURL('<svg xmlns="http://www.w3.org/2000/svg" width="2" height="2" />', 'image/svg+xml'),
|
|
font: '/preload/resources/font.ttf?x',
|
|
text: createEchoURL('hello', 'text/plain'),
|
|
script: createEchoURL('function dummy() { }', 'application/javascript'),
|
|
style: createEchoURL('.cls { }', 'text/css'),
|
|
}
|
|
|
|
const resourceTypes = {
|
|
image: {url: urls.image, as: 'image'},
|
|
font: {url: urls.font, as: 'font', config: 'anonymous'},
|
|
backgroundImage: {url: urls.image, as: 'image', config: 'no-cors'},
|
|
fetch: {url: urls.text, as: 'fetch'},
|
|
script: {url: urls.script, as: 'script'},
|
|
module: {url: urls.script, as: 'script'},
|
|
style: {url: urls.style, as: 'style'}
|
|
}
|
|
|
|
const configs = {
|
|
// The requested URL is from the same origin
|
|
'same-origin': {crossOrigin: false, attributes: {}},
|
|
|
|
// The requested URL is from a remote origin, without CORS
|
|
'no-cors': {crossOrigin: true, attributes: {}},
|
|
|
|
// The requested URL is from a remote origin, with CORS (anonymous)
|
|
'anonymous': {crossOrigin: true, attributes: {crossOrigin: 'anonymous'}},
|
|
|
|
// The requested URL is from a remote origin, with CORS (including credentials)
|
|
'use-credentials': {crossOrigin: true, attributes: {crossOrigin: 'use-credentials'}},
|
|
}
|
|
|
|
function preload(attributes, t) {
|
|
const link = document.createElement('link');
|
|
link.rel = "preload";
|
|
Object.entries(attributes).forEach(([key, value]) => {
|
|
if (value)
|
|
link[key] = value;
|
|
});
|
|
|
|
document.head.appendChild(link);
|
|
t.add_cleanup(() => link.remove());
|
|
return new Promise(resolve => link.addEventListener('load', resolve));
|
|
}
|
|
|
|
const loaders = {
|
|
image: (href, attr, t) => {
|
|
const img = document.createElement('img');
|
|
Object.entries(attr).forEach(([key, value]) => {
|
|
img[key] = value;
|
|
});
|
|
|
|
img.src = href
|
|
|
|
document.body.appendChild(img);
|
|
t.add_cleanup(() => img.remove());
|
|
return new Promise(resolve => {
|
|
img.addEventListener('load', resolve);
|
|
img.addEventListener('error', resolve);
|
|
});
|
|
},
|
|
font: async (href, attr, t) => {
|
|
const style = document.createElement('style');
|
|
style.innerHTML = `@font-face {
|
|
font-family: 'MyFont';
|
|
src: url('${href}');
|
|
}`;
|
|
|
|
document.head.appendChild(style);
|
|
t.add_cleanup(() => style.remove());
|
|
const p = document.createElement('p');
|
|
p.style.fontFamily = 'MyFont';
|
|
document.body.appendChild(p);
|
|
t.add_cleanup(() => p.remove());
|
|
await document.fonts.ready;
|
|
},
|
|
shape: (href, attr, t) => {
|
|
const div = document.createElement('div');
|
|
div.style.shapeOutside = `url(${href})`;
|
|
document.body.appendChild(div);
|
|
t.add_cleanup(() => div.remove());
|
|
},
|
|
backgroundImage: (href, attr, t) => {
|
|
const div = document.createElement('div');
|
|
div.style.background = `url(${href})`;
|
|
document.body.appendChild(div);
|
|
t.add_cleanup(() => div.remove());
|
|
},
|
|
fetch: async (href, attr, t) => {
|
|
const options = {mode: attr.crossOrigin ? 'cors' : 'no-cors',
|
|
credentials: !attr.crossOrigin || attr.crossOrigin === 'anonymous' ? 'omit' : 'include'}
|
|
|
|
const response = await fetch(href, options)
|
|
await response.text();
|
|
},
|
|
script: async (href, attr, t) => {
|
|
const script = document.createElement('script');
|
|
t.add_cleanup(() => script.remove());
|
|
if (attr.crossOrigin)
|
|
script.setAttribute('crossorigin', attr.crossOrigin);
|
|
script.src = href;
|
|
document.body.appendChild(script);
|
|
await new Promise(resolve => { script.onload = resolve });
|
|
},
|
|
module: async (href, attr, t) => {
|
|
const script = document.createElement('script');
|
|
script.type = 'module';
|
|
t.add_cleanup(() => script.remove());
|
|
if (attr.crossOrigin)
|
|
script.setAttribute('crossorigin', attr.crossOrigin);
|
|
script.src = href;
|
|
document.body.appendChild(script);
|
|
await new Promise(resolve => { script.onload = resolve });
|
|
},
|
|
style: async (href, attr, t) => {
|
|
const style = document.createElement('link');
|
|
style.rel = 'stylesheet';
|
|
style.href = href;
|
|
t.add_cleanup(() => style.remove());
|
|
if (attr.crossOrigin)
|
|
style.setAttribute('crossorigin', attr.crossOrigin);
|
|
document.body.appendChild(style);
|
|
await new Promise(resolve => style.addEventListener('load', resolve));
|
|
}
|
|
}
|
|
|
|
function preload_reuse_test(type, as, url, preloadConfig, resourceConfig) {
|
|
const expected = (preloadConfig === resourceConfig) ? "reuse" : "discard";
|
|
const key = token();
|
|
const href = getAbsoluteURL(`${
|
|
(configs[resourceConfig].crossOrigin ? HTTPS_REMOTE_ORIGIN : '') + url
|
|
}&${token()}`)
|
|
promise_test(async t => {
|
|
await preload({href, as, ...configs[preloadConfig].attributes}, t);
|
|
await loaders[as](href, configs[resourceConfig].attributes, t);
|
|
const expectedEntries = expected === "reuse" ? 1 : 2;
|
|
|
|
if (numberOfResourceTimingEntries(href) < expectedEntries)
|
|
await new Promise(resolve => t.step_timeout(resolve, 300));
|
|
verifyNumberOfResourceTimingEntries(href, expectedEntries);
|
|
}, `Loading ${type} (${resourceConfig}) with link (${preloadConfig}) should ${expected} the preloaded response`);
|
|
}
|
|
|
|
for (const [resourceTypeName, resourceInfo] of Object.entries(resourceTypes)) {
|
|
const configNames = resourceInfo.config ? [resourceInfo.config, 'same-origin'] : Object.keys(configs)
|
|
for (const resourceConfigName of configNames) {
|
|
for (const preloadConfigName in configs) {
|
|
// Same-origin requests ignore their CORS attributes, so no need to match all of them.
|
|
if ((resourceConfigName === 'same-origin' && preloadConfigName === 'same-origin') ||
|
|
(resourceConfigName !== 'same-origin' && preloadConfigName !== 'same-origin'))
|
|
preload_reuse_test(resourceTypeName, resourceInfo.as, resourceInfo.url, preloadConfigName, resourceConfigName);
|
|
}
|
|
}
|
|
|
|
}
|
|
</script> |