tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

preload-resource-match.https.html (6754B)


      1 <!doctype html>
      2 <script src="/resources/testharness.js"></script>
      3 <script src="/resources/testharnessreport.js"></script>
      4 <script src="/common/utils.js"></script>
      5 <script src="/preload/resources/preload_helper.js"></script>
      6 <script src="/common/get-host-info.sub.js"></script>
      7 <script>
      8 
      9 const {HTTPS_REMOTE_ORIGIN} = get_host_info();
     10 
     11 function createEchoURL(text, type) {
     12    return `/preload/resources/echo-with-cors.py?type=${
     13        encodeURIComponent(type)}&content=${
     14        encodeURIComponent(text)}&uid=${token()}`
     15 }
     16 const urls = {
     17    image: createEchoURL('<svg xmlns="http://www.w3.org/2000/svg" width="2" height="2" />', 'image/svg+xml'),
     18    font: '/preload/resources/font.ttf?x',
     19    text: createEchoURL('hello', 'text/plain'),
     20    script: createEchoURL('function dummy() { }', 'application/javascript'),
     21    style: createEchoURL('.cls { }', 'text/css'),
     22 }
     23 
     24 const resourceTypes = {
     25    image: {url: urls.image, as: 'image'},
     26    font: {url: urls.font, as: 'font', config: 'anonymous'},
     27    backgroundImage: {url: urls.image, as: 'image', config: 'no-cors'},
     28    fetch: {url: urls.text, as: 'fetch'},
     29    script: {url: urls.script, as: 'script'},
     30    module: {url: urls.script, as: 'script'},
     31    style: {url: urls.style, as: 'style'}
     32 }
     33 
     34 const configs = {
     35    // The requested URL is from the same origin
     36    'same-origin': {crossOrigin: false, attributes: {}},
     37 
     38    // The requested URL is from a remote origin, without CORS
     39    'no-cors': {crossOrigin: true, attributes: {}},
     40 
     41    // The requested URL is from a remote origin, with CORS (anonymous)
     42    'anonymous': {crossOrigin: true, attributes: {crossOrigin: 'anonymous'}},
     43 
     44    // The requested URL is from a remote origin, with CORS (including credentials)
     45    'use-credentials': {crossOrigin: true, attributes: {crossOrigin: 'use-credentials'}},
     46 }
     47 
     48 function preload(attributes, t) {
     49    const link = document.createElement('link');
     50    link.rel = "preload";
     51    Object.entries(attributes).forEach(([key, value]) => {
     52        if (value)
     53            link[key] = value;
     54    });
     55 
     56    document.head.appendChild(link);
     57    t.add_cleanup(() => link.remove());
     58    return new Promise(resolve => link.addEventListener('load', resolve));
     59 }
     60 
     61 const loaders = {
     62    image: (href, attr, t) => {
     63        const img = document.createElement('img');
     64        Object.entries(attr).forEach(([key, value]) => {
     65            img[key] = value;
     66        });
     67 
     68        img.src = href
     69 
     70        document.body.appendChild(img);
     71        t.add_cleanup(() => img.remove());
     72        return new Promise(resolve => {
     73            img.addEventListener('load', resolve);
     74            img.addEventListener('error', resolve);
     75        });
     76    },
     77    font: async (href, attr, t) => {
     78        const style = document.createElement('style');
     79        style.innerHTML = `@font-face {
     80            font-family: 'MyFont';
     81            src: url('${href}');
     82        }`;
     83 
     84        document.head.appendChild(style);
     85        t.add_cleanup(() => style.remove());
     86        const p = document.createElement('p');
     87        p.style.fontFamily = 'MyFont';
     88        document.body.appendChild(p);
     89        t.add_cleanup(() => p.remove());
     90        await document.fonts.ready;
     91    },
     92    shape: (href, attr, t) => {
     93        const div = document.createElement('div');
     94        div.style.shapeOutside = `url(${href})`;
     95        document.body.appendChild(div);
     96        t.add_cleanup(() => div.remove());
     97    },
     98    backgroundImage: (href, attr, t) => {
     99        const div = document.createElement('div');
    100        div.style.background = `url(${href})`;
    101        document.body.appendChild(div);
    102        t.add_cleanup(() => div.remove());
    103    },
    104    fetch: async (href, attr, t) => {
    105        const options = {mode: attr.crossOrigin ? 'cors' : 'no-cors',
    106             credentials: attr.crossOrigin === 'anonymous' ? 'same-origin' : 'include'}
    107 
    108        const response = await fetch(href, options)
    109        await response.text();
    110    },
    111    script: async (href, attr, t) => {
    112        const script = document.createElement('script');
    113        t.add_cleanup(() => script.remove());
    114        if (attr.crossOrigin)
    115            script.setAttribute('crossorigin', attr.crossOrigin);
    116        script.src = href;
    117        document.body.appendChild(script);
    118        await new Promise(resolve => { script.onload = resolve });
    119    },
    120    module: async (href, attr, t) => {
    121        const script = document.createElement('script');
    122        script.type = 'module';
    123        t.add_cleanup(() => script.remove());
    124        if (attr.crossOrigin)
    125            script.setAttribute('crossorigin', attr.crossOrigin);
    126        script.src = href;
    127        document.body.appendChild(script);
    128        await new Promise(resolve => { script.onload = resolve });
    129    },
    130    style: async (href, attr, t) => {
    131        const style = document.createElement('link');
    132        style.rel = 'stylesheet';
    133        style.href = href;
    134        t.add_cleanup(() => style.remove());
    135        if (attr.crossOrigin)
    136            style.setAttribute('crossorigin', attr.crossOrigin);
    137        document.body.appendChild(style);
    138        await new Promise(resolve => style.addEventListener('load', resolve));
    139    }
    140 }
    141 
    142 function preload_reuse_test(type, as, url, preloadConfig, resourceConfig) {
    143    const expected = (preloadConfig === resourceConfig) ? "reuse" : "discard";
    144    const key = token();
    145    const href = getAbsoluteURL(`${
    146        (configs[resourceConfig].crossOrigin ? HTTPS_REMOTE_ORIGIN : '') + url
    147    }&${token()}`)
    148    promise_test(async t => {
    149        await preload({href, as, ...configs[preloadConfig].attributes}, t);
    150        await loaders[as](href, configs[resourceConfig].attributes, t);
    151        const expectedEntries = expected === "reuse" ? 1 : 2;
    152 
    153        if (numberOfResourceTimingEntries(href) < expectedEntries)
    154            await new Promise(resolve => t.step_timeout(resolve, 300));
    155        verifyNumberOfResourceTimingEntries(href, expectedEntries);
    156    }, `Loading ${type} (${resourceConfig}) with link (${preloadConfig}) should ${expected} the preloaded response`);
    157 }
    158 
    159 for (const [resourceTypeName, resourceInfo] of Object.entries(resourceTypes)) {
    160    const configNames = resourceInfo.config ? [resourceInfo.config, 'same-origin'] : Object.keys(configs)
    161    for (const resourceConfigName of configNames) {
    162        for (const preloadConfigName in configs) {
    163            // Same-origin requests ignore their CORS attributes, so no need to match all of them.
    164            if ((resourceConfigName === 'same-origin' && preloadConfigName === 'same-origin') ||
    165                (resourceConfigName !== 'same-origin' && preloadConfigName !== 'same-origin'))
    166            preload_reuse_test(resourceTypeName, resourceInfo.as, resourceInfo.url, preloadConfigName, resourceConfigName);
    167        }
    168    }
    169 
    170 }
    171 </script>