| <!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> |