idbcursor_continue_delete_objectstore.any.js (3843B)
1 // META: title=IDBObjectStore.delete() and IDBCursor.continue() 2 // META: global=window,worker 3 // META: script=resources/support.js 4 5 'use strict'; 6 7 async_test(t => { 8 /* The goal here is to test that any prefetching of cursor values performs 9 * correct invalidation of prefetched data. This test is motivated by the 10 * particularities of the Firefox implementation of preloading, and is 11 * specifically motivated by an edge case when prefetching prefetches at 12 * least 2 extra records and at most determines whether a mutation is 13 * potentially relevant based on current cursor position and direction and 14 * does not test for key equivalence. Future implementations may want to 15 * help refine this test if their cursors are more clever. 16 * 17 * Step-wise we: 18 * - Open a cursor, returning key 0. 19 * - When the cursor request completes, without yielding control: 20 * - Issue a delete() call that won't actually delete anything but looks 21 * relevant. This should purge prefetched records 1 and 2. 22 * - Issue a continue() which should result in record 1 being fetched 23 * again and record 2 being prefetched again. 24 * - Delete record 2. Unless there's a synchronously available source 25 * of truth, the data from continue() above will not be present and 26 * we'll expect the implementation to need to set a flag to invalidate 27 * the prefetched data when it arrives. 28 * - When the cursor request completes, validate we got record 1 and issue 29 * a continue. 30 * - When the request completes, we should have a null cursor result value 31 * because 2 was deleted. 32 */ 33 let db; 34 let count = 0; 35 const records = 36 [{pKey: 'primaryKey_0'}, {pKey: 'primaryKey_1'}, {pKey: 'primaryKey_2'}]; 37 38 // This is a key that is not present in the database, but that is known to 39 // be relevant to a forward iteration of the above keys by comparing to be 40 // greater than all of them. 41 const plausibleFutureKey = 'primaryKey_9'; 42 43 let open_rq = createdb(t); 44 open_rq.onupgradeneeded = function(e) { 45 db = e.target.result; 46 47 let objStore = db.createObjectStore('test', {keyPath: 'pKey'}); 48 49 for (let i = 0; i < records.length; i++) 50 objStore.add(records[i]); 51 }; 52 53 open_rq.onsuccess = t.step_func(CursorDeleteRecord); 54 55 56 function CursorDeleteRecord(e) { 57 let txn = db.transaction('test', 'readwrite'); 58 let object_store = txn.objectStore('test'); 59 let cursor_rq = object_store.openCursor(); 60 let iteration = 0; 61 62 cursor_rq.onsuccess = t.step_func(function(e) { 63 let cursor = e.target.result; 64 65 switch (iteration) { 66 case 0: 67 object_store.delete(plausibleFutureKey); 68 assert_true(cursor != null, 'cursor valid'); 69 assert_equals(cursor.value.pKey, records[iteration].pKey); 70 cursor.continue(); 71 object_store.delete(records[2].pKey); 72 break; 73 case 1: 74 assert_true(cursor != null, 'cursor valid'); 75 assert_equals(cursor.value.pKey, records[iteration].pKey); 76 cursor.continue(); 77 break; 78 case 2: 79 assert_equals(cursor, null, 'cursor no longer valid'); 80 break; 81 }; 82 iteration++; 83 }); 84 85 txn.oncomplete = t.step_func(VerifyRecordWasDeleted); 86 } 87 88 89 function VerifyRecordWasDeleted(e) { 90 let cursor_rq = db.transaction('test', 'readonly') 91 .objectStore('test') 92 .openCursor(); 93 94 cursor_rq.onsuccess = t.step_func(function(e) { 95 let cursor = e.target.result; 96 97 if (!cursor) { 98 assert_equals(count, 2, 'count'); 99 t.done(); 100 } 101 102 assert_equals(cursor.value.pKey, records[count].pKey); 103 count++; 104 cursor.continue(); 105 }); 106 } 107 }, 'Object store - remove a record from the object store while iterating cursor');