tor-browser

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

worker-termination-aborts-upgrade.window.js (3174B)


      1 // META: title=Worker Termination Aborts a Pending Upgrade
      2 // META: script=resources/support-promises.js
      3 'use strict';
      4 
      5 // This test verifies that if a Worker's shutdown races an IndexedDB
      6 // versionchange transaction that is creating a database that the next attempt
      7 // to open the database results in a versionchange from version 0 and that
      8 // nothing was in the database.
      9 //
     10 // Care has been taken to make this test's behavior well-defined relative to the
     11 // spec to avoid intermittent failures.  In particular
     12 // `DedicatedWorkerGlobalScope.close()` is used on the worker after issuing the
     13 // `IDBFactory.open()` call.  This precludes any further tasks running on the
     14 // worker by spec, although implementations may potentially have "zones of
     15 // danger" in the time between the worker transitioning and when any state
     16 // machines on the parent thread realize what's going on.
     17 
     18 async function runAsyncFunctionInWorkerThenClose(funcToStringify) {
     19  const script = `// This script was created by runAsyncFunctionInWorkerThenClose
     20 let testFunc = ${funcToStringify.toString()};
     21 setTimeout(async () => {
     22  await testFunc();
     23  postMessage("ran");
     24  self.close();
     25 }, 0);
     26 `;
     27  const scriptBlob = new Blob([script]);
     28  const url = URL.createObjectURL(scriptBlob);
     29  const w = new Worker(url);
     30  await new Promise((resolve) => {
     31    w.onmessage = (evt) => {
     32      if (evt.data === "ran") {
     33        resolve();
     34      }
     35    };
     36  });
     37  URL.revokeObjectURL(url);
     38 }
     39 
     40 promise_test(async t => {
     41  await runAsyncFunctionInWorkerThenClose(async function() {
     42    // Note that this code will actually run on the worker, so anything
     43    // lexically captured will be coming from the worker's global scope.
     44    const openReq = indexedDB.open("aborted-upgrade-db", 1);
     45 
     46    openReq.onupgradeneeded = (event) => {
     47      const db = event.target.result;
     48      db.createObjectStore("should-not-be-created");
     49    }
     50  });
     51 
     52  // At this point we know that the open request was issued on the worker
     53  // worker thread.  An ordering concern at this point is that IDB only
     54  // specifies that the connection opening algorithm is run in parallel and
     55  // we are not guaranteed that when we go "in parallel" here that our operation
     56  // won't run first.  As such, it may be necessary to add some kind of
     57  // arbitrary delay in the future if implementations do not effectively
     58  // maintain sequential ordering of IPC requests within a process.
     59  //
     60  // Note that we must NOT use `createNamedDatabase` here because it will
     61  // issue a blind call to `deleteDatabase`.  Because the migrate helper does
     62  // not perform cleanup, we must add the cleanup deletion now, though.
     63  t.add_cleanup(() => { indexedDB.deleteDatabase("aborted-upgrade-db"); });
     64  let createdDB = await migrateNamedDatabase(t, "aborted-upgrade-db", 1, (db) => {
     65    assert_equals(db.objectStoreNames.length, 0, "DB should have been empty");
     66    // Let's make sure the database is not permanently broken / corrupted.
     67    db.createObjectStore("should-be-created");
     68  });
     69 
     70  assert_equals(createdDB.objectStoreNames.length, 1, "created object store correctly");
     71  assert_equals(createdDB.objectStoreNames.item(0), "should-be-created");
     72 });