tor-browser

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

link-multiple-load-events.html (4941B)


      1 <!DOCTYPE html>
      2 <link rel="author" title="Dom Farolino" href="mailto:dom@chromium.org">
      3 <link rel="help" href="https://html.spec.whatwg.org/multipage/#the-link-element">
      4 <script src="/resources/testharness.js"></script>
      5 <script src="/resources/testharnessreport.js"></script>
      6 <script src="/common/utils.js"></script>
      7 <script>
      8 function getTimeoutPromise(t, msg) {
      9  return new Promise((resolve, reject) => {
     10    t.step_timeout(() => reject(`Timeout reached: ${msg}`), 2000);
     11  });
     12 }
     13 
     14 // The tests generated from this loop are important to ensure that a link's load
     15 // or error event handler never incurs infinite recursion by virtue of
     16 // re-setting a link element's attribute to the value it already has. This is
     17 // what thwarted the first attempt at fixing https://crbug.com/41436016 in
     18 // Chromium.
     19 const attributes_to_test = ['rel', 'href', 'type', 'media'];
     20 for (const attribute of attributes_to_test) {
     21  promise_test(async t => {
     22    const id = token();
     23    const link = document.createElement('link');
     24    let count = 0;
     25    let response = null;
     26 
     27    link.onerror = t.unreached_func('Sheet should load successfully');
     28    const firstLoadPromise = new Promise(resolve => {
     29      link.onload = resolve;
     30    });
     31 
     32    // Set all attributes to a sensible default, so when we re-assign one of
     33    // them (`attribute`) idempotently later, the value doesn't change.
     34    link.rel = 'stylesheet';
     35    link.media = 'all';
     36    link.type = 'text/css';
     37    link.href = new URL(`stylesheet.py?id=${id}`, location.href);
     38 
     39    document.head.append(link);
     40    t.add_cleanup(() => {
     41      link.remove();
     42    });
     43 
     44    await Promise.race([firstLoadPromise, getTimeoutPromise(t, 'first resource')]);
     45    response = await fetch(`stylesheet.py?id=${id}&count=foo`);
     46    count = await response.text();
     47    assert_equals(count, '1', "server sees first style sheet request");
     48 
     49    const secondLoadPromise = new Promise((resolve, reject) => {
     50      link.onload = () => reject('second load event unexpectedly fired');
     51    });
     52    const expectedTimeoutPromise = new Promise(resolve => {
     53      t.step_timeout(resolve, 2000);
     54    });
     55 
     56    link[attribute] = link[attribute];
     57    await Promise.race([expectedTimeoutPromise, secondLoadPromise]);
     58    response = await fetch(`stylesheet.py?id=${id}&count=foo`);
     59    count = await response.text();
     60    assert_equals(count, '0',
     61        "server does not see second request to the same style sheet");
     62  }, `<link> cannot request the same resource twice by touching the ` +
     63     `'${attribute}' attribute, if its value never changes`);
     64 }
     65 
     66 
     67 promise_test(async t => {
     68  const id = token();
     69  const link = document.createElement('link');
     70  link.rel = 'preload';
     71  link.as = 'style';
     72  document.head.append(link);
     73  t.add_cleanup(() => {
     74    link.remove();
     75  });
     76 
     77  let count = 0;
     78  let response = null;
     79 
     80  link.onerror = t.unreached_func('Sheet should load successfully');
     81  const firstLoadPromise = new Promise(resolve => {
     82    link.onload = resolve;
     83  });
     84 
     85  link.href = `stylesheet.py?id=${id}`;
     86  await Promise.race([firstLoadPromise, getTimeoutPromise(t, 'first resource')]);
     87  response = await fetch(`stylesheet.py?id=${id}&count=foo`);
     88  count = await response.text();
     89  assert_equals(count, '1', "server sees preload request");
     90 
     91  const secondLoadPromise = new Promise(resolve => {
     92    link.onload = resolve;
     93  });
     94 
     95  // Switching from `rel=preload` => `rel=stylesheet` triggers the stylesheet
     96  // processing model. The resource loads from the preload cache and never
     97  // touches the network, but the load event does fire again.
     98  link.rel = 'stylesheet';
     99  await Promise.race([secondLoadPromise,
    100      getTimeoutPromise(t, 'rel=stylesheet using preload cache')]);
    101  response = await fetch(`stylesheet.py?id=${id}&count=foo`);
    102  count = await response.text();
    103  assert_equals(count, '0', "server does not see second request to the same style sheet");
    104 }, "<link> load event can fire twice for the same href resource, based on " +
    105   "'rel' attribute mutations");
    106 
    107 promise_test(async t => {
    108  const link = document.createElement('link');
    109  link.rel = 'stylesheet';
    110  document.head.append(link);
    111  t.add_cleanup(() => {
    112    link.remove();
    113  });
    114 
    115  link.onerror = t.unreached_func('Sheet should load successfully');
    116  const firstLoadPromise = new Promise(resolve => {
    117    link.onload = resolve;
    118  });
    119 
    120  link.href = 'style.css?first';
    121  await Promise.race([firstLoadPromise, getTimeoutPromise(t, 'first resource')]);
    122 
    123  const secondLoadPromise = new Promise(resolve => {
    124    link.onload = resolve;
    125  });
    126 
    127  link.href = 'style.css?second';
    128  await Promise.race([secondLoadPromise, getTimeoutPromise(t, 'second resource')]);
    129 
    130  const thirdLoadPromise = new Promise(resolve => {
    131    link.onload = resolve;
    132  });
    133 
    134  link.href = 'style.css?third';
    135  await Promise.race([thirdLoadPromise, getTimeoutPromise(t, 'third resource')]);
    136 }, "<link> load event fires for each DIFFERENT stylesheet it loads");
    137 </script>
    138 </head>
    139 </html>