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