1 <html> 2 <head> 3 <link rel="stylesheet" href="../../fast/js/resources/js-test-style.css"> 4 <script src="../../fast/js/resources/js-test-pre.js"></script> 5 <script src="../../fast/js/resources/js-test-post-function.js"></script> 6 <script src="resources/shared.js"></script> 7 </head> 8 <body> 9 <p id="description"></p> 10 <div id="console"></div> 11 <script> 12 13 description("Test IndexedDB's cursor skips deleted entries."); 14 if (window.layoutTestController) 15 layoutTestController.waitUntilDone(); 16 17 var names = ['Alpha', 'Bravo', 'Charlie', 'Delta', 'Echo', 'Foxtrot', 'Golf', 18 'Hotel', 'India', 'Juliet', 'Kilo', 'Lima', 'Mike', 'November', 19 'Oscar', 'Papa', 'Quebec', 'Romeo', 'Sierra', 'Tango', 'Uniform', 20 'Victor', 'Whiskey', 'X-ray', 'Yankee', 'Zulu']; 21 22 test(); 23 24 function test() 25 { 26 request = evalAndLog("webkitIndexedDB.open('cursor-skip-deleted')"); 27 request.onsuccess = openSuccess; 28 request.onerror = unexpectedErrorCallback; 29 } 30 31 function openSuccess() 32 { 33 var db = evalAndLog("db = event.target.result"); 34 35 request = evalAndLog("db.setVersion('new version')"); 36 request.onsuccess = setVersionSuccess; 37 request.onerror = unexpectedErrorCallback; 38 } 39 40 function setVersionSuccess() 41 { 42 debug("setVersionSuccess():"); 43 window.trans = evalAndLog("trans = event.target.result"); 44 shouldBeTrue("trans !== null"); 45 trans.onabort = unexpectedAbortCallback; 46 trans.oncomplete = basicCursorTest; 47 48 deleteAllObjectStores(db); 49 50 var objectStore = evalAndLog("objectStore = db.createObjectStore('store', {keyPath: 'id'})"); 51 evalAndLog("objectStore.createIndex('nameIndex', 'name')"); 52 resetObjectStore(function() {}); 53 } 54 55 var silentErrorHandler = function() { event.preventDefault(); } 56 57 function resetObjectStore(callback) 58 { 59 debug("\nresetObjectStore():"); 60 if (callback === undefined) 61 callback = function () {}; 62 63 var objectStore = trans.objectStore('store'); 64 for (var i = 0; i < names.length; i++) 65 objectStore.delete(i).onerror = silentErrorHandler; 66 for (var i = 0; i < names.length; i++) 67 objectStore.add({id: i, name: names[i]}).onerror = unexpectedErrorCallback; 68 69 debug(""); 70 callback(); 71 } 72 73 function contains(arr, obj) 74 { 75 for (var i = 0; i < arr.length; i++) { 76 if (arr[i] == obj) 77 return true; 78 } 79 return false; 80 } 81 82 var cursor; 83 var deleted; 84 var seen; 85 86 function testCursor(deleteList, createCursorCommand, callback) 87 { 88 debug("\ntestCursor():"); 89 deleted = []; 90 seen = []; 91 92 // Create the cursor. 93 request = evalAndLog(createCursorCommand); 94 95 request.onerror = unexpectedErrorCallback; 96 request.onsuccess = function () { 97 if (event.target.result == null) { 98 // Make sure we have seen every non-deleted item. 99 for (var i = 0; i < names.length; i++) { 100 if (contains(deleted, i)) 101 continue; 102 103 if (!contains(seen, i)) 104 testFailed("Cursor did not see item with id: " + i); 105 } 106 107 // Make sure we used every rule in |deleteList|. 108 for (var i = 0; i < deleteList.length; i++) { 109 if (!contains(seen, deleteList[i].id)) 110 testFailed("deleteList rule with id: " + deleteList[i].id + " was never used."); 111 } 112 113 debug(""); 114 callback(); 115 return; 116 } 117 118 cursor = event.target.result; 119 debug(event.target.result.value.id + ": " + event.target.result.value.name); 120 seen.push(event.target.result.value.id); 121 122 // Make sure we don't see any deleted items. 123 if (contains(deleted, event.target.result.value.id)) 124 testFailed("Cursor hit previously deleted element."); 125 126 for (var i = 0; i < deleteList.length; i++) { 127 if (event.target.result.value.id == deleteList[i].id) { 128 // Delete objects targeted by this id. 129 var targets = deleteList[i].targets; 130 for (var j = 0; j < targets.length; j++) { 131 deleted.push(targets[j]); 132 request = evalAndLog("request = trans.objectStore('store').delete(" + targets[j] + ")"); 133 request.onerror = unexpectedErrorCallback; 134 if (j == targets.length - 1) 135 request.onsuccess = function() { cursor.continue(); } 136 } 137 return; 138 } 139 } 140 141 cursor.continue(); 142 } 143 } 144 145 function basicCursorTest() 146 { 147 debug("basicCursorTest()"); 148 149 evalAndLog("trans = db.transaction([], webkitIDBTransaction.READ_WRITE)"); 150 trans.onabort = unexpectedAbortCallback; 151 trans.oncomplete = transactionComplete; 152 153 var deletes = [{id: 1, targets: [0]}, 154 {id: 2, targets: [names.length - 1]}, 155 {id: 3, targets: [5,6,7]}, 156 {id: 10, targets: [10]}, 157 {id: 12, targets: [13]}, 158 {id: 15, targets: [14]}, 159 {id: 20, targets: [17,18]} 160 ]; 161 162 testCursor(deletes, "trans.objectStore('store').openCursor(webkitIDBKeyRange.lowerBound(0))", function() { resetObjectStore(reverseCursorTest); }); 163 } 164 165 function reverseCursorTest() 166 { 167 debug("reverseCursorTest():"); 168 169 var deletes = [{id: 24, targets: [names.length - 1]}, 170 {id: 23, targets: [0]}, 171 {id: 22, targets: [20, 19, 18]}, 172 {id: 15, targets: [15]}, 173 {id: 13, targets: [12]}, 174 {id: 10, targets: [11]}, 175 {id: 5, targets: [7,8]} 176 ]; 177 178 179 testCursor(deletes, "trans.objectStore('store').openCursor(webkitIDBKeyRange.lowerBound(0), webkitIDBCursor.PREV)", function() { resetObjectStore(indexCursorTest); }); 180 } 181 182 function indexCursorTest() 183 { 184 debug("indexCursorTest():"); 185 186 var deletes = [{id: 1, targets: [0]}, 187 {id: 2, targets: [names.length - 1]}, 188 {id: 3, targets: [5,6,7]}, 189 {id: 10, targets: [10]}, 190 {id: 12, targets: [13]}, 191 {id: 15, targets: [14]}, 192 {id: 20, targets: [17,18]} 193 ]; 194 195 testCursor(deletes, "trans.objectStore('store').index('nameIndex').openCursor(webkitIDBKeyRange.lowerBound('Alpha'))", function() { }); 196 } 197 198 function transactionComplete() 199 { 200 debug("transactionComplete():"); 201 done(); 202 } 203 204 var successfullyParsed = true; 205 206 207