Diff
Modified: trunk/LayoutTests/ChangeLog (114463 => 114464)
--- trunk/LayoutTests/ChangeLog 2012-04-17 23:20:45 UTC (rev 114463)
+++ trunk/LayoutTests/ChangeLog 2012-04-17 23:26:02 UTC (rev 114464)
@@ -1,3 +1,31 @@
+2012-04-17 Alec Flett <alecfl...@chromium.org>
+
+ IndexedDB chooses wrong record on PREV_NO_DUPLICATE index cursor
+ https://bugs.webkit.org/show_bug.cgi?id=60746
+
+ Reviewed by Ojan Vafai.
+
+ Test for PREV_NO_DUPLICATE.
+
+ * storage/indexeddb/cursor-prev-no-duplicate.html: Added.
+ * storage/indexeddb/cursor-prev-no-duplicate.txt: Added.
+ * storage/indexeddb/mozilla/index-prev-no-duplicate-expected.txt: Added.
+ * storage/indexeddb/mozilla/index-prev-no-duplicate.html: Added.
+ * storage/indexeddb/resources/cursor-prev-no-duplicate.js: Added.
+ (prepareDatabase.openreq.onsuccess.verreq.onsuccess):
+ (prepareDatabase.openreq.onsuccess):
+ (prepareDatabase):
+ (populateStore):
+ (runAllTests):
+ (waitFor):
+ (complete.fireCallback):
+ (complete):
+ (testFarRangeCursor):
+ (makeOpenKeyCursor):
+ (runTest.trans.oncomplete):
+ (runTest.testFunction):
+ (runTest):
+
2012-04-17 Yuta Kitamura <yu...@chromium.org>
Unreviewed, unskip some WebSocket worker tests that should not be skipped.
Added: trunk/LayoutTests/storage/indexeddb/cursor-prev-no-duplicate-expected.txt (0 => 114464)
--- trunk/LayoutTests/storage/indexeddb/cursor-prev-no-duplicate-expected.txt (rev 0)
+++ trunk/LayoutTests/storage/indexeddb/cursor-prev-no-duplicate-expected.txt 2012-04-17 23:26:02 UTC (rev 114464)
@@ -0,0 +1,125 @@
+Test IndexedDB behavior when iterating backwards with and without NO_DUPLICATE
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+indexedDB = self.indexedDB || self.webkitIndexedDB || self.mozIndexedDB || self.msIndexedDB || self.OIndexedDB;
+
+
+openreq = indexedDB.open('cursor-prev-no-duplicate')
+db = openreq.result
+verreq = db.setVersion('1')
+Deleted all object stores.
+store = db.createObjectStore('store')
+store.createIndex('index', 'sorted')
+
+populating store...
+trans = db.transaction('store', IDBTransaction.READ_WRITE)
+store = trans.objectStore('store');
+store.put({sorted: 3, value: 111}, 1)
+store.put({sorted: 2, value: 222}, 2)
+store.put({sorted: 1, value: 333}, 3)
+store.put({sorted: 10, value: 444}, 17)
+store.put({sorted: 10, value: 555}, 16)
+store.put({sorted: 10, value: 666}, 15)
+testFarRangeCursor: upper bound is well out of range, results always the same, whether open or closed
+storeReq = store.openCursor(IDBKeyRange.upperBound(7, false), 2)
+
+Testing: store.openCursor(IDBKeyRange.upperBound(7, false), 2)
+PASS cursor.key is 3
+PASS cursor.value.value is 333
+storeReq = store.openCursor(IDBKeyRange.upperBound(7, true), 2)
+DONE
+
+Testing: store.openCursor(IDBKeyRange.upperBound(7, true), 2)
+PASS cursor.key is 3
+PASS cursor.value.value is 333
+storeReq = index.openCursor(IDBKeyRange.upperBound(7, false), 2)
+DONE
+
+Testing: index.openCursor(IDBKeyRange.upperBound(7, false), 2)
+PASS cursor.key is 3
+PASS cursor.value.value is 111
+PASS cursor.primaryKey is 1
+storeReq = index.openCursor(IDBKeyRange.upperBound(7, true), 2)
+DONE
+
+Testing: index.openCursor(IDBKeyRange.upperBound(7, true), 2)
+PASS cursor.key is 3
+PASS cursor.value.value is 111
+PASS cursor.primaryKey is 1
+storeReq = index.openKeyCursor(IDBKeyRange.upperBound(7, false), 2)
+DONE
+
+Testing: index.openKeyCursor(IDBKeyRange.upperBound(7, false), 2)
+PASS cursor.key is 3
+PASS cursor.primaryKey is 1
+storeReq = index.openKeyCursor(IDBKeyRange.upperBound(7, true), 2)
+DONE
+
+Testing: index.openKeyCursor(IDBKeyRange.upperBound(7, true), 2)
+PASS cursor.key is 3
+PASS cursor.primaryKey is 1
+storeReq = store.openCursor(IDBKeyRange.upperBound(3, false), 2)
+DONE
+
+Testing: store.openCursor(IDBKeyRange.upperBound(3, false), 2)
+PASS cursor.key is 3
+PASS cursor.value.value is 333
+storeReq = store.openCursor(IDBKeyRange.upperBound(3, true), 2)
+DONE
+
+Testing: store.openCursor(IDBKeyRange.upperBound(3, true), 2)
+PASS cursor.key is 2
+PASS cursor.value.value is 222
+storeReq = index.openCursor(IDBKeyRange.upperBound(3, false), 2)
+DONE
+
+Testing: index.openCursor(IDBKeyRange.upperBound(3, false), 2)
+PASS cursor.key is 3
+PASS cursor.value.value is 111
+PASS cursor.primaryKey is 1
+storeReq = index.openCursor(IDBKeyRange.upperBound(3, true), 2)
+DONE
+
+Testing: index.openCursor(IDBKeyRange.upperBound(3, true), 2)
+PASS cursor.key is 2
+PASS cursor.value.value is 222
+PASS cursor.primaryKey is 2
+storeReq = index.openKeyCursor(IDBKeyRange.upperBound(3, false), 2)
+DONE
+
+Testing: index.openKeyCursor(IDBKeyRange.upperBound(3, false), 2)
+PASS cursor.key is 3
+PASS cursor.primaryKey is 1
+storeReq = index.openKeyCursor(IDBKeyRange.upperBound(3, true), 2)
+DONE
+
+Testing: index.openKeyCursor(IDBKeyRange.upperBound(3, true), 2)
+PASS cursor.key is 2
+PASS cursor.primaryKey is 2
+testNoDuplicate: there are 3 values, but we should return always the first one
+storeReq = store.openCursor(IDBKeyRange.upperBound(15, false), 3)
+DONE
+
+Testing: store.openCursor(IDBKeyRange.upperBound(15, false), 3)
+PASS cursor.key is 15
+PASS cursor.value.value is 666
+PASS cursor.primaryKey is 15
+storeReq = index.openCursor(IDBKeyRange.upperBound(15, false), 3)
+DONE
+
+Testing: index.openCursor(IDBKeyRange.upperBound(15, false), 3)
+PASS cursor.key is 10
+PASS cursor.value.value is 666
+PASS cursor.primaryKey is 15
+storeReq = index.openKeyCursor(IDBKeyRange.upperBound(15, false), 3)
+DONE
+
+Testing: index.openKeyCursor(IDBKeyRange.upperBound(15, false), 3)
+PASS cursor.key is 10
+PASS cursor.primaryKey is 15
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
Added: trunk/LayoutTests/storage/indexeddb/cursor-prev-no-duplicate.html (0 => 114464)
--- trunk/LayoutTests/storage/indexeddb/cursor-prev-no-duplicate.html (rev 0)
+++ trunk/LayoutTests/storage/indexeddb/cursor-prev-no-duplicate.html 2012-04-17 23:26:02 UTC (rev 114464)
@@ -0,0 +1,10 @@
+<html>
+<head>
+<script src=""
+<script src=""
+</head>
+<body>
+<script src=""
+<script src=""
+</body>
+</html>
Added: trunk/LayoutTests/storage/indexeddb/mozilla/index-prev-no-duplicate-expected.txt (0 => 114464)
--- trunk/LayoutTests/storage/indexeddb/mozilla/index-prev-no-duplicate-expected.txt (rev 0)
+++ trunk/LayoutTests/storage/indexeddb/mozilla/index-prev-no-duplicate-expected.txt 2012-04-17 23:26:02 UTC (rev 114464)
@@ -0,0 +1,183 @@
+Test IndexedDB: iterating backwards through an index, skipping duplicates
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+indexedDB = window.indexedDB || window.webkitIndexedDB || window.mozIndexedDB;
+PASS indexedDB == null is false
+IDBDatabaseException = window.IDBDatabaseException || window.webkitIDBDatabaseException;
+PASS IDBDatabaseException == null is false
+IDBTransaction = window.IDBTransaction || window.webkitIDBTransaction;
+PASS IDBTransaction == null is false
+IDBCursor = window.IDBCursor || window.webkitIDBCursor;
+PASS IDBCursor == null is false
+IDBKeyRange = window.IDBKeyRange || window.webkitIDBKeyRange;
+PASS IDBKeyRange == null is false
+indexedDB.open(name, description)
+openSuccess():
+db = event.target.result
+request = db.setVersion('1')
+Deleted all object stores.
+objectStore = db.createObjectStore(objectStoreName);
+First, add all our data to the object store.
+request = objectStore.add(objectStoreData[i].value, objectStoreData[i].key);
+request = objectStore.add(objectStoreData[i].value, objectStoreData[i].key);
+request = objectStore.add(objectStoreData[i].value, objectStoreData[i].key);
+request = objectStore.add(objectStoreData[i].value, objectStoreData[i].key);
+request = objectStore.add(objectStoreData[i].value, objectStoreData[i].key);
+request = objectStore.add(objectStoreData[i].value, objectStoreData[i].key);
+request = objectStore.add(objectStoreData[i].value, objectStoreData[i].key);
+request = objectStore.add(objectStoreData[i].value, objectStoreData[i].key);
+request = objectStore.add(objectStoreData[i].value, objectStoreData[i].key);
+Now create the indexes.
+objectStore.createIndex(indexData[i].name, indexData[i].keyPath, indexData[i].options);
+objectStore.createIndex(indexData[i].name, indexData[i].keyPath, indexData[i].options);
+objectStore.createIndex(indexData[i].name, indexData[i].keyPath, indexData[i].options);
+testPrev()
+trans = db.transaction(objectStoreName)
+objectStore = trans.objectStore(objectStoreName);
+keyIndex = 8;
+request = objectStore.index('height').openCursor(null, IDBCursor.PREV);
+cursor = event.target.result;
+PASS cursor.key is 73
+PASS cursor.primaryKey is '237-23-7740'
+PASS cursor.value.name is 'Sam'
+PASS cursor.value.height is 73
+PASS cursor.value.weight is 110
+cursor.continue();
+keyIndex--;
+ => 7
+cursor = event.target.result;
+PASS cursor.key is 73
+PASS cursor.primaryKey is '237-23-7734'
+PASS cursor.value.name is 'Ron'
+PASS cursor.value.height is 73
+PASS cursor.value.weight is 180
+cursor.continue();
+keyIndex--;
+ => 6
+cursor = event.target.result;
+PASS cursor.key is 65
+PASS cursor.primaryKey is '237-23-7739'
+PASS cursor.value.name is 'Jef'
+PASS cursor.value.height is 65
+PASS cursor.value.weight is 120
+cursor.continue();
+keyIndex--;
+ => 5
+cursor = event.target.result;
+PASS cursor.key is 65
+PASS cursor.primaryKey is '237-23-7738'
+PASS cursor.value.name is 'Leo'
+PASS cursor.value.height is 65
+PASS cursor.value.weight is 180
+cursor.continue();
+keyIndex--;
+ => 4
+cursor = event.target.result;
+PASS cursor.key is 65
+PASS cursor.primaryKey is '237-23-7737'
+PASS cursor.value.name is 'Pat'
+PASS cursor.value.height is 65
+PASS cursor.value.weight is 100
+cursor.continue();
+keyIndex--;
+ => 3
+cursor = event.target.result;
+PASS cursor.key is 65
+PASS cursor.primaryKey is '237-23-7736'
+PASS cursor.value.name is 'Joe'
+PASS cursor.value.height is 65
+PASS cursor.value.weight is 150
+cursor.continue();
+keyIndex--;
+ => 2
+cursor = event.target.result;
+PASS cursor.key is 60
+PASS cursor.primaryKey is '237-23-7732'
+PASS cursor.value.name is 'Bob'
+PASS cursor.value.height is 60
+PASS cursor.value.weight is 120
+cursor.continue();
+keyIndex--;
+ => 1
+cursor = event.target.result;
+PASS cursor.key is 58
+PASS cursor.primaryKey is '237-23-7735'
+PASS cursor.value.name is 'Sue'
+PASS cursor.value.height is 58
+PASS cursor.value.weight is 130
+cursor.continue();
+keyIndex--;
+ => 0
+cursor = event.target.result;
+PASS cursor.key is 52
+PASS cursor.primaryKey is '237-23-7733'
+PASS cursor.value.name is 'Ann'
+PASS cursor.value.height is 52
+PASS cursor.value.weight is 110
+cursor.continue();
+keyIndex--;
+ => -1
+cursor = event.target.result;
+No cursor: null
+PASS keyIndex is -1
+testPrevNoDuplicate()
+objectStore = db.transaction(objectStoreName).objectStore(objectStoreName);
+keyIndex = 8;
+request = objectStore.index('height').openCursor(null, IDBCursor.PREV_NO_DUPLICATE);
+cursor = event.target.result;
+keyIndex -= 1
+ => Entering with keyIndex = 7
+PASS cursor.key is 73
+PASS cursor.primaryKey is '237-23-7734'
+PASS cursor.value.name is 'Ron'
+PASS cursor.value.height is 73
+PASS cursor.value.weight is 180
+cursor.continue();
+keyIndex--;
+cursor = event.target.result;
+keyIndex -= 3;
+ => Entering with keyIndex = 3
+PASS cursor.key is 65
+PASS cursor.primaryKey is '237-23-7736'
+PASS cursor.value.name is 'Joe'
+PASS cursor.value.height is 65
+PASS cursor.value.weight is 150
+cursor.continue();
+keyIndex--;
+cursor = event.target.result;
+ => Entering with keyIndex = 2
+PASS cursor.key is 60
+PASS cursor.primaryKey is '237-23-7732'
+PASS cursor.value.name is 'Bob'
+PASS cursor.value.height is 60
+PASS cursor.value.weight is 120
+cursor.continue();
+keyIndex--;
+cursor = event.target.result;
+ => Entering with keyIndex = 1
+PASS cursor.key is 58
+PASS cursor.primaryKey is '237-23-7735'
+PASS cursor.value.name is 'Sue'
+PASS cursor.value.height is 58
+PASS cursor.value.weight is 130
+cursor.continue();
+keyIndex--;
+cursor = event.target.result;
+ => Entering with keyIndex = 0
+PASS cursor.key is 52
+PASS cursor.primaryKey is '237-23-7733'
+PASS cursor.value.name is 'Ann'
+PASS cursor.value.height is 52
+PASS cursor.value.weight is 110
+cursor.continue();
+keyIndex--;
+cursor = event.target.result;
+ => Entering with keyIndex = -1
+No cursor: null
+PASS keyIndex is -1
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
Added: trunk/LayoutTests/storage/indexeddb/mozilla/index-prev-no-duplicate.html (0 => 114464)
--- trunk/LayoutTests/storage/indexeddb/mozilla/index-prev-no-duplicate.html (rev 0)
+++ trunk/LayoutTests/storage/indexeddb/mozilla/index-prev-no-duplicate.html 2012-04-17 23:26:02 UTC (rev 114464)
@@ -0,0 +1,193 @@
+<!DOCTYPE html>
+<!--
+ original test: http://mxr.mozilla.org/mozilla2.0/source/dom/indexedDB/test/test_indexes.html?force=1
+ license of original test:
+ " Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ "
+-->
+<html>
+<head>
+<script src=""
+<script>
+ var jsTestIsAsync = true;
+</script>
+<script src=""
+</head>
+<body>
+<script>
+
+description("Test IndexedDB: iterating backwards through an index, skipping duplicates");
+if (window.layoutTestController)
+ layoutTestController.waitUntilDone();
+
+function test()
+{
+ indexedDB = evalAndLog("indexedDB = window.indexedDB || window.webkitIndexedDB || window.mozIndexedDB;");
+ shouldBeFalse("indexedDB == null");
+ IDBDatabaseException = evalAndLog("IDBDatabaseException = window.IDBDatabaseException || window.webkitIDBDatabaseException;");
+ shouldBeFalse("IDBDatabaseException == null");
+ IDBTransaction = evalAndLog("IDBTransaction = window.IDBTransaction || window.webkitIDBTransaction;");
+ shouldBeFalse("IDBTransaction == null");
+ IDBCursor = evalAndLog("IDBCursor = window.IDBCursor || window.webkitIDBCursor;");
+ shouldBeFalse("IDBCursor == null");
+ IDBKeyRange = evalAndLog("IDBKeyRange = window.IDBKeyRange || window.webkitIDBKeyRange;");
+ shouldBeFalse("IDBKeyRange == null");
+
+ name = window.location.pathname;
+ description = "My Test Database";
+ request = evalAndLog("indexedDB.open(name, description)");
+ request._onsuccess_ = openSuccess;
+ request._onerror_ = unexpectedErrorCallback;
+}
+
+function openSuccess()
+{
+ debug("openSuccess():");
+ db = evalAndLog("db = event.target.result");
+
+ objectStoreName = "People";
+
+ objectStoreData = [
+ { key: "237-23-7732", value: { name: "Bob", height: 60, weight: 120 } },
+ { key: "237-23-7733", value: { name: "Ann", height: 52, weight: 110 } },
+ { key: "237-23-7734", value: { name: "Ron", height: 73, weight: 180 } },
+ { key: "237-23-7735", value: { name: "Sue", height: 58, weight: 130 } },
+ { key: "237-23-7736", value: { name: "Joe", height: 65, weight: 150 } },
+ { key: "237-23-7737", value: { name: "Pat", height: 65, weight: 100 } },
+ { key: "237-23-7738", value: { name: "Leo", height: 65, weight: 180 } },
+ { key: "237-23-7739", value: { name: "Jef", height: 65, weight: 120 } },
+ { key: "237-23-7740", value: { name: "Sam", height: 73, weight: 110 } },
+ ];
+
+ indexData = [
+ { name: "name", keyPath: "name", options: { unique: true } },
+ { name: "height", keyPath: "height", options: { } },
+ { name: "weight", keyPath: "weight", options: { unique: false } }
+ ];
+
+ objectStoreDataHeightSort = [
+ { key: "237-23-7733", value: { name: "Ann", height: 52, weight: 110 } },
+ { key: "237-23-7735", value: { name: "Sue", height: 58, weight: 130 } },
+ { key: "237-23-7732", value: { name: "Bob", height: 60, weight: 120 } },
+ { key: "237-23-7736", value: { name: "Joe", height: 65, weight: 150 } },
+ { key: "237-23-7737", value: { name: "Pat", height: 65, weight: 100 } },
+ { key: "237-23-7738", value: { name: "Leo", height: 65, weight: 180 } },
+ { key: "237-23-7739", value: { name: "Jef", height: 65, weight: 120 } },
+ { key: "237-23-7734", value: { name: "Ron", height: 73, weight: 180 } },
+ { key: "237-23-7740", value: { name: "Sam", height: 73, weight: 110 } },
+ ];
+
+ request = evalAndLog("request = db.setVersion('1')");
+ request._onsuccess_ = createAndPopulateObjectStore;
+ request._onerror_ = unexpectedErrorCallback;
+}
+
+function createAndPopulateObjectStore()
+{
+ deleteAllObjectStores(db);
+
+ objectStore = evalAndLog("objectStore = db.createObjectStore(objectStoreName);");
+
+ debug("First, add all our data to the object store.");
+ addedData = 0;
+ for (i in objectStoreData) {
+ request = evalAndLog("request = objectStore.add(objectStoreData[i].value, objectStoreData[i].key);");
+ request._onerror_ = unexpectedErrorCallback;
+ request._onsuccess_ = function(event)
+ {
+ if (++addedData == objectStoreData.length) {
+ createIndexes();
+ }
+ }
+ }
+}
+
+function createIndexes()
+{
+ debug("Now create the indexes.");
+ for (i in indexData) {
+ evalAndLog("objectStore.createIndex(indexData[i].name, indexData[i].keyPath, indexData[i].options);");
+ }
+
+
+ testPrev();
+}
+
+function testPrev()
+{
+ debug("testPrev()");
+ trans = evalAndLog("trans = db.transaction(objectStoreName)");
+ objectStore = evalAndLog("objectStore = trans.objectStore(objectStoreName);");
+ keyIndex = evalAndLog("keyIndex = 8;");
+ // first try with just PREV
+ request = evalAndLog("request = objectStore.index('height').openCursor(null, IDBCursor.PREV);");
+ request._onerror_ = unexpectedErrorCallback;
+ request._onsuccess_ = function(event)
+ {
+ cursor = evalAndLog("cursor = event.target.result;");
+ if (cursor) {
+ shouldBe("cursor.key", "" + objectStoreDataHeightSort[keyIndex].value.height);
+ shouldBe("cursor.primaryKey", "'" + objectStoreDataHeightSort[keyIndex].key + "'");
+ shouldBe("cursor.value.name", "'" + objectStoreDataHeightSort[keyIndex].value.name + "'");
+ shouldBe("cursor.value.height", "" + objectStoreDataHeightSort[keyIndex].value.height);
+ if ('weight' in cursor.value) {
+ shouldBe("cursor.value.weight", "" + objectStoreDataHeightSort[keyIndex].value.weight);
+ }
+
+ evalAndLog("cursor.continue();");
+ evalAndLog("keyIndex--;");
+ debug(" => " + keyIndex);
+ }
+ else {
+ debug("No cursor: " + cursor);
+ shouldBe("keyIndex", "-1");
+ }
+ };
+ trans._oncomplete_ = testPrevNoDuplicate;
+}
+
+function testPrevNoDuplicate()
+{
+ debug("testPrevNoDuplicate()");
+ objectStore = evalAndLog("objectStore = db.transaction(objectStoreName).objectStore(objectStoreName);");
+ keyIndex = evalAndLog("keyIndex = 8;");
+ request = evalAndLog("request = objectStore.index('height').openCursor(null, IDBCursor.PREV_NO_DUPLICATE);");
+ request._onerror_ = unexpectedErrorCallback;
+ request._onsuccess_ = function (event)
+ {
+ cursor = evalAndLog("cursor = event.target.result;");
+ if (keyIndex == 8) {
+ evalAndLog("keyIndex -= 1");
+ }
+ if (keyIndex == 6) {
+ evalAndLog("keyIndex -= 3;");
+ }
+ debug(" => Entering with keyIndex = " + keyIndex);
+ if (cursor) {
+ shouldBe("cursor.key", "" + objectStoreDataHeightSort[keyIndex].value.height);
+ shouldBe("cursor.primaryKey", "'" + objectStoreDataHeightSort[keyIndex].key + "'");
+ shouldBe("cursor.value.name", "'" + objectStoreDataHeightSort[keyIndex].value.name + "'");
+ shouldBe("cursor.value.height", "" + objectStoreDataHeightSort[keyIndex].value.height);
+ if ('weight' in cursor.value) {
+ shouldBe("cursor.value.weight", "" + objectStoreDataHeightSort[keyIndex].value.weight);
+ }
+
+ evalAndLog("cursor.continue();");
+ evalAndLog("keyIndex--;");
+ }
+ else {
+ debug("No cursor: " + cursor);
+ shouldBe("keyIndex", "-1");
+ finishJSTest();
+ }
+ }
+}
+
+var successfullyParsed = true;
+
+test();
+
+</script>
+<script src=""
+</body>
+</html>
Added: trunk/LayoutTests/storage/indexeddb/resources/cursor-prev-no-duplicate.js (0 => 114464)
--- trunk/LayoutTests/storage/indexeddb/resources/cursor-prev-no-duplicate.js (rev 0)
+++ trunk/LayoutTests/storage/indexeddb/resources/cursor-prev-no-duplicate.js 2012-04-17 23:26:02 UTC (rev 114464)
@@ -0,0 +1,236 @@
+if (this.importScripts) {
+ importScripts('../../../fast/js/resources/js-test-pre.js');
+ importScripts('shared.js');
+}
+
+description("Test IndexedDB behavior when iterating backwards with and without NO_DUPLICATE");
+
+function test()
+{
+ removeVendorPrefixes();
+
+ prepareDatabase();
+}
+
+function prepareDatabase()
+{
+ debug("");
+ evalAndLog("openreq = indexedDB.open('cursor-prev-no-duplicate')");
+ openreq._onerror_ = unexpectedErrorCallback;
+ openreq._onsuccess_ = function()
+ {
+ evalAndLog("db = openreq.result");
+ evalAndLog("verreq = db.setVersion('1')");
+ verreq._onerror_ = unexpectedErrorCallback;
+ verreq._onsuccess_ = function()
+ {
+ deleteAllObjectStores(db);
+ store = evalAndLog("store = db.createObjectStore('store')");
+ evalAndLog("store.createIndex('index', 'sorted')");
+ verreq.result._oncomplete_ = populateStore;
+ };
+ };
+}
+
+function populateStore()
+{
+ debug("");
+ debug("populating store...");
+ evalAndLog("trans = db.transaction('store', IDBTransaction.READ_WRITE)");
+ evalAndLog("store = trans.objectStore('store');");
+ trans._onerror_ = unexpectedErrorCallback;
+ trans._onabort_ = unexpectedAbortCallback;
+
+ evalAndLog("store.put({sorted: 3, value: 111}, 1)");
+ evalAndLog("store.put({sorted: 2, value: 222}, 2)");
+ evalAndLog("store.put({sorted: 1, value: 333}, 3)");
+ evalAndLog("store.put({sorted: 10, value: 444}, 17)");
+ evalAndLog("store.put({sorted: 10, value: 555}, 16)");
+ evalAndLog("store.put({sorted: 10, value: 666}, 15)");
+ trans._oncomplete_ = testFarRangeCursor_closed;
+}
+
+
+function testFarRangeCursor_closed()
+{
+ debug("testFarRangeCursor: upper bound is well out of range, results always the same, whether open or closed");
+
+ runTest(makeOpenCursor("store", 7, false, IDBCursor.PREV),
+ { expectedValue: 333, expectedKey: 3})._onsuccess_ =
+ testFarRangeCursor_open;
+}
+
+function testFarRangeCursor_open()
+{
+ runTest(makeOpenCursor("store", 7, true, IDBCursor.PREV),
+ { expectedValue: 333, expectedKey: 3})._onsuccess_ =
+ testFarRangeCursor_indexClosed;
+}
+
+function testFarRangeCursor_indexClosed()
+{
+ // here '7' refers to the 'sorted' value
+ runTest(makeOpenCursor("index", 7, false, IDBCursor.PREV),
+ { expectedValue: 111, expectedKey: 3, expectedPrimaryKey: 1})._onsuccess_ =
+ testFarRangeCursor_indexOpen;
+}
+function testFarRangeCursor_indexOpen()
+{
+ runTest(makeOpenCursor("index", 7, true, IDBCursor.PREV),
+ { expectedValue: 111, expectedKey: 3, expectedPrimaryKey: 1})._onsuccess_ =
+ testFarRangeCursor_indexKeyOpen;
+}
+
+function testFarRangeCursor_indexKeyOpen()
+{
+ // here '7' refers to the sorted value
+ runTest(makeOpenKeyCursor("index", 7, false, IDBCursor.PREV),
+ { expectedKey: 3, expectedPrimaryKey: 1})._onsuccess_ =
+ testFarRangeCursor_indexKeyClosed;
+}
+
+function testFarRangeCursor_indexKeyClosed()
+{
+ runTest(makeOpenKeyCursor("index", 7, true, IDBCursor.PREV),
+ { expectedKey: 3, expectedPrimaryKey: 1})._onsuccess_ =
+ testBoundaryCursor_closed;
+}
+
+function testBoundaryCursor_closed()
+{
+ runTest(makeOpenCursor("store", 3, false, IDBCursor.PREV),
+ { expectedValue: 333, expectedKey: 3})._onsuccess_ =
+ testBoundaryCursor_open;
+};
+
+function testBoundaryCursor_open()
+{
+ runTest(makeOpenCursor("store", 3, true, IDBCursor.PREV),
+ { expectedValue: 222, expectedKey: 2})._onsuccess_ =
+ testBoundaryCursor_indexClosed;
+}
+
+function testBoundaryCursor_indexClosed()
+{
+ // by index sort order, we should return them in a different order
+ runTest(makeOpenCursor("index", 3, false, IDBCursor.PREV),
+ { expectedValue: 111, expectedKey: 3, expectedPrimaryKey: 1})._onsuccess_ =
+ testBoundaryCursor_indexOpen;
+}
+
+function testBoundaryCursor_indexOpen()
+{
+ runTest(makeOpenCursor("index", 3, true, IDBCursor.PREV),
+ { expectedValue: 222, expectedKey: 2, expectedPrimaryKey: 2})._onsuccess_ =
+ testBoundaryCursor_indexKeyClosed;
+}
+
+function testBoundaryCursor_indexKeyClosed()
+{
+
+ // now the value doesn't matter, just the primary key
+ runTest(makeOpenKeyCursor("index", 3, false, IDBCursor.PREV),
+ { expectedKey: 3, expectedPrimaryKey: 1})._onsuccess_ =
+ testBoundaryCursor_indexKeyOpen;
+}
+
+function testBoundaryCursor_indexKeyOpen()
+{
+ runTest(makeOpenKeyCursor("index", 3, true, IDBCursor.PREV),
+ { expectedKey: 2, expectedPrimaryKey: 2})._onsuccess_ =
+ testNoDuplicate_closed;
+}
+
+function testNoDuplicate_closed()
+{
+ debug("testNoDuplicate: there are 3 values, but we should return always the first one");
+
+ // PREV_NO_DUPLICATE doesn't really affect non-indexed
+ // cursors, but we should make sure we get the right one
+ // anyway
+ runTest(makeOpenCursor("store", 15, false, IDBCursor.PREV_NO_DUPLICATE),
+ { expectedValue: 666, expectedKey: 15, expectedPrimaryKey: 15 })
+ ._onsuccess_ = testNoDuplicate_open;
+}
+
+function testNoDuplicate_open()
+{
+ // still three values, but now the index says we should return the
+ // second one
+ runTest(makeOpenCursor("index", 15, false, IDBCursor.PREV_NO_DUPLICATE),
+ { expectedValue: 666, expectedKey: 10, expectedPrimaryKey: 15})._onsuccess_ = testNoDuplicate_indexKeyClosed;
+}
+
+
+function testNoDuplicate_indexKeyClosed()
+{
+ // same behavior as above, without a value
+ runTest(makeOpenKeyCursor("index", 15, false, IDBCursor.PREV_NO_DUPLICATE),
+ { expectedKey: 10, expectedPrimaryKey: 15})._onsuccess_ =
+ finishJSTest;
+}
+
+
+function makeOpenCursor(obj, upperBound, open, direction)
+{
+ return obj + ".openCursor(IDBKeyRange.upperBound(" + upperBound + ", " +
+ open + "), " +
+ direction + ")";
+}
+
+function makeOpenKeyCursor(obj, upperBound, open, direction)
+{
+ return obj + ".openKeyCursor(IDBKeyRange.upperBound(" + upperBound + ", " +
+ open + "), " +
+ direction + ")";
+}
+
+function runTest(openCursor, exp)
+{
+ trans = db.transaction('store', IDBTransaction.READ_ONLY);
+
+ // expose these for code in openCursor
+ store = trans.objectStore('store');
+ index = store.index('index');
+ trans._onerror_ = unexpectedErrorCallback;
+ trans._onabort_ = unexpectedAbortCallback;
+ trans._oncomplete_ = function()
+ {
+ debug("DONE");
+ };
+
+ var result = {};
+
+ var testFunction = function()
+ {
+ // expose for evalAndLog
+ expectation = exp;
+
+ debug("\nTesting: " + openCursor);
+ cursor = event.target.result;
+ if (cursor === null) {
+ testFailed("null cursor");
+ return;
+ }
+
+ shouldBe("cursor.key", JSON.stringify(expectation.expectedKey));
+ if ("value" in cursor) {
+ shouldBe("cursor.value.value", JSON.stringify(expectation.expectedValue));
+ }
+ else if ("expectedValue" in expectation)
+ testFailed("Test broken: shouldn't have expectedValue");
+
+ if ("expectedPrimaryKey" in expectation) {
+ shouldBe("cursor.primaryKey", JSON.stringify(expectation.expectedPrimaryKey));
+ }
+
+ result.onsuccess();
+ };
+
+ storeReq = evalAndLog("storeReq = " + openCursor);
+ storeReq._onsuccess_ = testFunction;
+
+ return result;
+}
+
+test();
\ No newline at end of file
Modified: trunk/Source/WebCore/ChangeLog (114463 => 114464)
--- trunk/Source/WebCore/ChangeLog 2012-04-17 23:20:45 UTC (rev 114463)
+++ trunk/Source/WebCore/ChangeLog 2012-04-17 23:26:02 UTC (rev 114464)
@@ -1,3 +1,19 @@
+2012-04-17 Alec Flett <alecfl...@chromium.org>
+
+ IndexedDB chooses wrong record on PREV_NO_DUPLICATE index cursor
+ https://bugs.webkit.org/show_bug.cgi?id=60746
+
+ Reviewed by Ojan Vafai.
+
+ When iterating backwards with PREV_NO_DUPLICATE, keep walking past
+ the 'prev' key until it changes, then walk forward one time. This
+ covers the object store and index cases.
+
+ Test: storage/indexeddb/mozilla/index-prev-no-duplicate.html
+
+ * Modules/indexeddb/IDBLevelDBBackingStore.cpp:
+ (WebCore):
+
2012-04-17 Yi Shen <yi.4.s...@nokia.com>
Caret is not rendered properly inside an input element with text-indent
Modified: trunk/Source/WebCore/Modules/indexeddb/IDBLevelDBBackingStore.cpp (114463 => 114464)
--- trunk/Source/WebCore/Modules/indexeddb/IDBLevelDBBackingStore.cpp 2012-04-17 23:20:45 UTC (rev 114463)
+++ trunk/Source/WebCore/Modules/indexeddb/IDBLevelDBBackingStore.cpp 2012-04-17 23:26:02 UTC (rev 114464)
@@ -1039,24 +1039,48 @@
{
RefPtr<IDBKey> previousKey = m_currentKey;
+ // When iterating with PREV_NO_DUPLICATE, spec requires that the
+ // value we yield for each key is the first duplicate in forwards
+ // order.
+ RefPtr<IDBKey> lastDuplicateKey;
+
+ bool forward = m_cursorOptions.forward;
+
for (;;) {
if (nextState == Seek) {
- if (m_cursorOptions.forward)
+ if (forward)
m_iterator->next();
else
m_iterator->prev();
} else
nextState = Seek; // for subsequent iterations
- if (!m_iterator->isValid())
+ if (!m_iterator->isValid()) {
+ if (!forward && lastDuplicateKey.get()) {
+ // We need to walk forward because we hit the end of
+ // the data.
+ forward = true;
+ continue;
+ }
+
return false;
+ }
+ // The iterator will contain values deleted during iteration.
Vector<char> trash;
if (!m_transaction->get(m_iterator->key(), trash))
- continue;
+ continue;
- if (isPastBounds())
+ if (isPastBounds()) {
+ if (!forward && lastDuplicateKey.get()) {
+ // We need to walk forward because now we're beyond the
+ // bounds defined by the cursor.
+ forward = true;
+ continue;
+ }
+
return false;
+ }
if (!haveEnteredRange())
continue;
@@ -1067,7 +1091,7 @@
continue;
if (key) {
- if (m_cursorOptions.forward) {
+ if (forward) {
if (m_currentKey->isLessThan(key))
continue;
} else {
@@ -1076,12 +1100,35 @@
}
}
- if (m_cursorOptions.unique && m_currentKey->isEqual(previousKey.get()))
- continue;
+ if (m_cursorOptions.unique) {
+ if (m_currentKey->isEqual(previousKey.get())) {
+ // We should never be able to walk forward all the way
+ // to the previous key.
+ ASSERT(!lastDuplicateKey.get());
+ continue;
+ }
+
+ if (!forward) {
+ if (!lastDuplicateKey.get()) {
+ lastDuplicateKey = m_currentKey;
+ continue;
+ }
+
+ // We need to walk forward because we hit the boundary
+ // between key ranges.
+ if (!lastDuplicateKey->isEqual(m_currentKey.get())) {
+ forward = true;
+ continue;
+ }
+
+ continue;
+ }
+ }
break;
}
+ ASSERT(!lastDuplicateKey.get() || (forward && lastDuplicateKey->isEqual(m_currentKey.get())));
return true;
}