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 });