tor-browser

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

browser_storage_recovery.js (5430B)


      1 "use strict";
      2 
      3 // This test registers a SW for a scope that will never control a document
      4 // and therefore never trigger a "fetch" functional event that would
      5 // automatically attempt to update the registration.  The overlap of the
      6 // PAGE_URI and SCOPE is incidental.  checkForUpdate is the only thing that
      7 // will trigger an update of the registration and so there is no need to
      8 // worry about Schedule Job races to coalesce an update job.
      9 
     10 const BASE_URI = "http://mochi.test:8888/browser/dom/serviceworkers/test/";
     11 const PAGE_URI = BASE_URI + "empty.html";
     12 const SCOPE = PAGE_URI + "?storage_recovery";
     13 const SW_SCRIPT = BASE_URI + "storage_recovery_worker.sjs";
     14 
     15 async function checkForUpdate(browser) {
     16  return SpecialPowers.spawn(browser, [SCOPE], async function (uri) {
     17    let reg = await content.navigator.serviceWorker.getRegistration(uri);
     18    await reg.update();
     19    return !!reg.installing;
     20  });
     21 }
     22 
     23 // Delete all of our chrome-namespace Caches for this origin, leaving any
     24 // content-owned caches in place.  This is exclusively for simulating loss
     25 // of the origin's storage without loss of the registration and without
     26 // having to worry that future enhancements to QuotaClients/ServiceWorkerRegistrar
     27 // will break this test.  If you want to wipe storage for an origin, use
     28 // QuotaManager APIs
     29 async function wipeStorage(u) {
     30  let uri = Services.io.newURI(u);
     31  let principal = Services.scriptSecurityManager.createContentPrincipal(
     32    uri,
     33    {}
     34  );
     35  let caches = new CacheStorage("chrome", principal);
     36  let list = await caches.keys();
     37  return Promise.all(list.map(c => caches.delete(c)));
     38 }
     39 
     40 add_setup(async function () {
     41  await SpecialPowers.pushPrefEnv({
     42    set: [
     43      ["dom.serviceWorkers.enabled", true],
     44      ["dom.serviceWorkers.testing.enabled", true],
     45      ["dom.serviceWorkers.idle_timeout", 0],
     46    ],
     47  });
     48 
     49  // Configure the server script to not redirect.
     50  await fetch(SW_SCRIPT + "?clear-redirect");
     51 
     52  let tab = BrowserTestUtils.addTab(gBrowser, PAGE_URI);
     53  let browser = gBrowser.getBrowserForTab(tab);
     54  await BrowserTestUtils.browserLoaded(browser);
     55 
     56  await SpecialPowers.spawn(
     57    browser,
     58    [{ script: SW_SCRIPT, scope: SCOPE }],
     59    async function (opts) {
     60      let reg = await content.navigator.serviceWorker.register(opts.script, {
     61        scope: opts.scope,
     62      });
     63      let worker = reg.installing || reg.waiting || reg.active;
     64      await new Promise(resolve => {
     65        if (worker.state === "activated") {
     66          resolve();
     67          return;
     68        }
     69        worker.addEventListener("statechange", function onStateChange() {
     70          if (worker.state === "activated") {
     71            worker.removeEventListener("statechange", onStateChange);
     72            resolve();
     73          }
     74        });
     75      });
     76    }
     77  );
     78 
     79  BrowserTestUtils.removeTab(tab);
     80 });
     81 
     82 // Verify that our service worker doesn't update normally.
     83 add_task(async function normal_update_check() {
     84  let tab = BrowserTestUtils.addTab(gBrowser, PAGE_URI);
     85  let browser = gBrowser.getBrowserForTab(tab);
     86  await BrowserTestUtils.browserLoaded(browser);
     87 
     88  let updated = await checkForUpdate(browser);
     89  ok(!updated, "normal update check should not trigger an update");
     90 
     91  BrowserTestUtils.removeTab(tab);
     92 });
     93 
     94 // Test what happens when we wipe the service worker scripts
     95 // out from under the site before triggering the update.  This
     96 // should cause an update to occur.
     97 add_task(async function wiped_update_check() {
     98  // Wipe the backing cache storage, but leave the SW registered.
     99  await wipeStorage(PAGE_URI);
    100 
    101  let tab = BrowserTestUtils.addTab(gBrowser, PAGE_URI);
    102  let browser = gBrowser.getBrowserForTab(tab);
    103  await BrowserTestUtils.browserLoaded(browser);
    104 
    105  let updated = await checkForUpdate(browser);
    106  ok(updated, "wiping the service worker scripts should trigger an update");
    107 
    108  BrowserTestUtils.removeTab(tab);
    109 });
    110 
    111 // Test what happens when we wipe the service worker scripts
    112 // out from under the site before triggering the update.  This
    113 // should cause an update to occur.
    114 add_task(async function wiped_and_failed_update_check() {
    115  // Wipe the backing cache storage, but leave the SW registered.
    116  await wipeStorage(PAGE_URI);
    117 
    118  // Configure the service worker script to redirect.  This will
    119  // prevent the update from completing successfully.
    120  await fetch(SW_SCRIPT + "?set-redirect");
    121 
    122  let tab = BrowserTestUtils.addTab(gBrowser, PAGE_URI);
    123  let browser = gBrowser.getBrowserForTab(tab);
    124  await BrowserTestUtils.browserLoaded(browser);
    125 
    126  // Attempt to update the service worker.  This should throw
    127  // an error because the script is now redirecting.
    128  let updateFailed = false;
    129  try {
    130    await checkForUpdate(browser);
    131  } catch (e) {
    132    updateFailed = true;
    133  }
    134  ok(updateFailed, "redirecting service worker script should fail to update");
    135 
    136  // Also, since the existing service worker's scripts are broken
    137  // we should also remove the registration completely when the
    138  // update fails.
    139  let exists = await SpecialPowers.spawn(
    140    browser,
    141    [SCOPE],
    142    async function (uri) {
    143      let reg = await content.navigator.serviceWorker.getRegistration(uri);
    144      return !!reg;
    145    }
    146  );
    147  ok(
    148    !exists,
    149    "registration should be removed after scripts are wiped and update fails"
    150  );
    151 
    152  // Note, we don't have to clean up the service worker registration
    153  // since its effectively been force-removed here.
    154 
    155  BrowserTestUtils.removeTab(tab);
    156 });