tor-browser

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

test_abrupt_completion.html (4815B)


      1 <!doctype html>
      2 <meta charset=utf-8>
      3 <title></title>
      4 <script src="/tests/SimpleTest/SimpleTest.js"></script>
      5 <script>
      6 
      7 // Tests a _registered_ ServiceWorker whose script evaluation results in an
      8 // "abrupt completion", e.g. threw an uncaught exception. Such a ServiceWorker's
      9 // first script evaluation must result in a "normal completion", however, for
     10 // the Update algorithm to not abort in its step 18 when registering:
     11 //
     12 // 18. If runResult is failure or an abrupt completion, then: [...]
     13 
     14 const script = "./abrupt_completion_worker.js";
     15 const scope = "./empty.html";
     16 const expectedMessage = "handler-before-throw";
     17 let registration = null;
     18 
     19 // Should only be called once registration.active is non-null. Uses
     20 // implementation details by zero-ing the "idle timeout"s and then sending  an
     21 // event to the ServiceWorker, which should immediately cause its termination.
     22 // The idle timeouts are restored after the ServiceWorker is terminated.
     23 async function startAndStopServiceWorker() {
     24  SpecialPowers.registerObservers("service-worker-shutdown");
     25 
     26  const spTopic = "specialpowers-service-worker-shutdown";
     27 
     28  const origIdleTimeout =
     29    SpecialPowers.getIntPref("dom.serviceWorkers.idle_timeout");
     30 
     31  const origIdleExtendedTimeout =
     32    SpecialPowers.getIntPref("dom.serviceWorkers.idle_extended_timeout");
     33 
     34  await new Promise(resolve => {
     35    const observer = {
     36      async observe(subject, topic, data) {
     37        if (topic !== spTopic) {
     38          return;
     39        }
     40 
     41        SpecialPowers.removeObserver(observer, spTopic);
     42 
     43        await SpecialPowers.pushPrefEnv({
     44          set: [
     45              ["dom.serviceWorkers.idle_timeout", origIdleTimeout],
     46              ["dom.serviceWorkers.idle_extended_timeout", origIdleExtendedTimeout]
     47            ]
     48        });
     49 
     50        resolve();
     51      },
     52    };
     53 
     54    // Speed things up.
     55    SpecialPowers.pushPrefEnv({
     56      set: [
     57          ["dom.serviceWorkers.idle_timeout", 0],
     58          ["dom.serviceWorkers.idle_extended_timeout", 0]
     59        ]
     60    }).then(() => {
     61      SpecialPowers.addObserver(observer, spTopic);
     62 
     63      registration.active.postMessage("");
     64    });
     65  });
     66 }
     67 
     68 // eslint-disable-next-line mozilla/no-addtask-setup
     69 add_task(async function setup() {
     70  await SpecialPowers.pushPrefEnv({
     71    set: [
     72        ["dom.serviceWorkers.enabled", true],
     73        ["dom.serviceWorkers.testing.enabled", true]
     74      ]
     75  });
     76 
     77  registration = await navigator.serviceWorker.register(script, { scope });
     78  SimpleTest.registerCleanupFunction(async function unregisterRegistration() {
     79    await registration.unregister();
     80  });
     81 
     82  await new Promise(resolve => {
     83    const serviceWorker = registration.installing;
     84 
     85    serviceWorker.onstatechange = () => {
     86      if (serviceWorker.state === "activated") {
     87        resolve();
     88      }
     89    };
     90  });
     91 
     92  ok(registration.active instanceof ServiceWorker, "ServiceWorker is activated");
     93 });
     94 
     95 // We expect that the restarted SW that experiences an abrupt completion at
     96 // startup after adding its message handler 1) will be active in order to
     97 // respond to our postMessage and 2) will respond with the global value set
     98 // prior to the importScripts call that throws (and not the global value that
     99 // would have been assigned after the importScripts call if it didn't throw).
    100 add_task(async function testMessageHandler() {
    101  await startAndStopServiceWorker();
    102 
    103  await new Promise(resolve => {
    104    navigator.serviceWorker.onmessage = e => {
    105      is(e.data, expectedMessage, "Correct message handler");
    106      resolve();
    107    };
    108    registration.active.postMessage("");
    109  });
    110 });
    111 
    112 // We expect that the restarted SW that experiences an abrupt completion at
    113 // startup before adding its "fetch" listener will 1) successfully dispatch the
    114 // event and 2) it will not be handled (respondWith() will not be called) so
    115 // interception will be reset and the response will contain the contents of
    116 // empty.html. Before the fix in bug 1603484 the SW would fail to properly start
    117 // up and the fetch event would result in a NetworkError, breaking the
    118 // controlled page.
    119 add_task(async function testFetchHandler() {
    120  await startAndStopServiceWorker();
    121 
    122  const iframe = document.createElement("iframe");
    123  SimpleTest.registerCleanupFunction(function removeIframe() {
    124    iframe.remove();
    125  });
    126 
    127  await new Promise(resolve => {
    128    iframe.src = scope;
    129    iframe.onload = resolve;
    130    document.body.appendChild(iframe);
    131  });
    132 
    133  const response = await iframe.contentWindow.fetch(scope);
    134 
    135  // NetworkError will have a status of 0, which is not "ok", and this is
    136  // a stronger guarantee that should be true instead of just checking if there
    137  // isn't a NetworkError.
    138  ok(response.ok, "Fetch succeeded and didn't result in a NetworkError");
    139 
    140  const text = await response.text();
    141  is(text, "", "Correct response text");
    142 });
    143 
    144 </script>