summaryrefslogtreecommitdiffstats
path: root/testing/web-platform/tests/IndexedDB/worker-termination-aborts-upgrade.window.js
blob: e84ca2c2a644abe87f16aaa789eab40bb2c6fd64 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
// META: title=Worker Termination Aborts a Pending Upgrade
// META: script=resources/support-promises.js

// This test verifies that if a Worker's shutdown races an IndexedDB
// versionchange transaction that is creating a database that the next attempt
// to open the database results in a versionchange from version 0 and that
// nothing was in the database.
//
// Care has been taken to make this test's behavior well-defined relative to the
// spec to avoid intermittent failures.  In particular
// `DedicatedWorkerGlobalScope.close()` is used on the worker after issuing the
// `IDBFactory.open()` call.  This precludes any further tasks running on the
// worker by spec, although implementations may potentially have "zones of
// danger" in the time between the worker transitioning and when any state
// machines on the parent thread realize what's going on.

async function runAsyncFunctionInWorkerThenClose(funcToStringify) {
  const script = `// This script was created by runAsyncFunctionInWorkerThenClose
let testFunc = ${funcToStringify.toString()};
setTimeout(async () => {
  await testFunc();
  postMessage("ran");
  self.close();
}, 0);
`;
  const scriptBlob = new Blob([script]);
  const url = URL.createObjectURL(scriptBlob);
  const w = new Worker(url);
  await new Promise((resolve) => {
    w.onmessage = (evt) => {
      if (evt.data === "ran") {
        resolve();
      }
    };
  });
  URL.revokeObjectURL(url);
}

promise_test(async t => {
  await runAsyncFunctionInWorkerThenClose(async function() {
    // Note that this code will actually run on the worker, so anything
    // lexically captured will be coming from the worker's global scope.
    const openReq = indexedDB.open("aborted-upgrade-db", 1);

    openReq.onupgradeneeded = (event) => {
      const db = event.target.result;
      db.createObjectStore("should-not-be-created");
    }
  });

  // At this point we know that the open request was issued on the worker
  // worker thread.  An ordering concern at this point is that IDB only
  // specifies that the the connection opening algorithm is run in parallel and
  // we are not guaranteed that when we go "in parallel" here that our operation
  // won't run first.  As such, it may be necessary to add some kind of
  // arbitrary delay in the future if implementations do not effectively
  // maintain sequential ordering of IPC requests within a process.
  //
  // Note that we must NOT use `createNamedDatabase` here because it will
  // issue a blind call to `deleteDatabase`.  Because the migrate helper does
  // not perform cleanup, we must add the cleanup deletion now, though.
  t.add_cleanup(() => { indexedDB.deleteDatabase("aborted-upgrade-db"); });
  let createdDB = await migrateNamedDatabase(t, "aborted-upgrade-db", 1, (db) => {
    assert_equals(db.objectStoreNames.length, 0, "DB should have been empty");
    // Let's make sure the database is not permanently broken / corrupted.
    db.createObjectStore("should-be-created");
  });

  assert_equals(createdDB.objectStoreNames.length, 1, "created object store correctly");
  assert_equals(createdDB.objectStoreNames.item(0), "should-be-created");
});