Modern IDB:Make in-memory Index cursors work.
authorbeidson@apple.com <beidson@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Thu, 19 Nov 2015 04:36:00 +0000 (04:36 +0000)
committerbeidson@apple.com <beidson@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Thu, 19 Nov 2015 04:36:00 +0000 (04:36 +0000)
https://bugs.webkit.org/show_bug.cgi?id=151278

Reviewed by Alex Christensen.

Source/WebCore:

Tests: storage/indexeddb/modern/index-cursor-1.html
       storage/indexeddb/modern/index-cursor-2.html
       storage/indexeddb/modern/index-cursor-3.html

* CMakeLists.txt:
* WebCore.xcodeproj/project.pbxproj:

* Modules/indexeddb/client/IDBIndexImpl.cpp:
(WebCore::IDBClient::IDBIndex::openCursor):
(WebCore::IDBClient::IDBIndex::openKeyCursor):

* Modules/indexeddb/server/IndexValueEntry.cpp:
(WebCore::IDBServer::IndexValueEntry::removeKey):
(WebCore::IDBServer::IndexValueEntry::Iterator::Iterator):
(WebCore::IDBServer::IndexValueEntry::Iterator::key):
(WebCore::IDBServer::IndexValueEntry::Iterator::isValid):
(WebCore::IDBServer::IndexValueEntry::Iterator::invalidate):
(WebCore::IDBServer::IndexValueEntry::Iterator::operator++):
(WebCore::IDBServer::IndexValueEntry::begin):
(WebCore::IDBServer::IndexValueEntry::reverseBegin):
(WebCore::IDBServer::IndexValueEntry::find):
(WebCore::IDBServer::IndexValueEntry::reverseFind):
* Modules/indexeddb/server/IndexValueEntry.h:
(WebCore::IDBServer::IndexValueEntry::Iterator::Iterator):
(WebCore::IDBServer::IndexValueEntry::unique):

* Modules/indexeddb/server/IndexValueStore.cpp:
(WebCore::IDBServer::IndexValueStore::removeEntriesWithValueKey):
(WebCore::IDBServer::IndexValueStore::lowestKeyWithRecordInRange):
(WebCore::IDBServer::IndexValueStore::lowestIteratorInRange):
(WebCore::IDBServer::IndexValueStore::highestReverseIteratorInRange):
(WebCore::IDBServer::IndexValueStore::find):
(WebCore::IDBServer::IndexValueStore::reverseFind):
(WebCore::IDBServer::IndexValueStore::Iterator::Iterator):
(WebCore::IDBServer::IndexValueStore::Iterator::nextIndexEntry):
(WebCore::IDBServer::IndexValueStore::Iterator::operator++):
(WebCore::IDBServer::IndexValueStore::Iterator::invalidate):
(WebCore::IDBServer::IndexValueStore::Iterator::isValid):
(WebCore::IDBServer::IndexValueStore::Iterator::key):
(WebCore::IDBServer::IndexValueStore::Iterator::primaryKey):
* Modules/indexeddb/server/IndexValueStore.h:
(WebCore::IDBServer::IndexValueStore::Iterator::Iterator):

* Modules/indexeddb/server/MemoryIDBBackingStore.cpp:
(WebCore::IDBServer::MemoryIDBBackingStore::openCursor):

* Modules/indexeddb/server/MemoryIndex.cpp:
(WebCore::IDBServer::MemoryIndex::cursorDidBecomeClean):
(WebCore::IDBServer::MemoryIndex::cursorDidBecomeDirty):
(WebCore::IDBServer::MemoryIndex::objectStoreCleared):
(WebCore::IDBServer::MemoryIndex::notifyCursorsOfValueChange):
(WebCore::IDBServer::MemoryIndex::notifyCursorsOfAllRecordsChanged):
(WebCore::IDBServer::MemoryIndex::putIndexKey):
(WebCore::IDBServer::MemoryIndex::removeRecord):
(WebCore::IDBServer::MemoryIndex::removeEntriesWithValueKey):
(WebCore::IDBServer::MemoryIndex::maybeOpenCursor):
* Modules/indexeddb/server/MemoryIndex.h:
(WebCore::IDBServer::MemoryIndex::valueStore):
(WebCore::IDBServer::MemoryIndex::objectStore):

* Modules/indexeddb/server/MemoryIndexCursor.cpp: Added.
(WebCore::IDBServer::MemoryIndexCursor::MemoryIndexCursor):
(WebCore::IDBServer::MemoryIndexCursor::~MemoryIndexCursor):
(WebCore::IDBServer::MemoryIndexCursor::currentData):
(WebCore::IDBServer::MemoryIndexCursor::iterate):
(WebCore::IDBServer::MemoryIndexCursor::indexRecordsAllChanged):
(WebCore::IDBServer::MemoryIndexCursor::indexValueChanged):
* Modules/indexeddb/server/MemoryIndexCursor.h:

* Modules/indexeddb/server/MemoryObjectStore.cpp:
(WebCore::IDBServer::MemoryObjectStore::indexForIdentifier):
(WebCore::IDBServer::MemoryObjectStore::valueForKey):
* Modules/indexeddb/server/MemoryObjectStore.h:

* Modules/indexeddb/shared/IDBCursorInfo.cpp:
(WebCore::IDBCursorInfo::objectStoreCursor):
(WebCore::IDBCursorInfo::indexCursor):
(WebCore::IDBCursorInfo::IDBCursorInfo):
(WebCore::IDBCursorInfo::isDirectionNoDuplicate):
(WebCore::IDBCursorInfo::isolatedCopy):
* Modules/indexeddb/shared/IDBCursorInfo.h:
(WebCore::IDBCursorInfo::objectStoreIdentifier):

LayoutTests:

* storage/indexeddb/modern/cursor-1-expected.txt:
* storage/indexeddb/modern/index-cursor-1-expected.txt: Added.
* storage/indexeddb/modern/index-cursor-1.html: Added.
* storage/indexeddb/modern/index-cursor-2-expected.txt: Added.
* storage/indexeddb/modern/index-cursor-2.html: Added.
* storage/indexeddb/modern/index-cursor-3-expected.txt: Added.
* storage/indexeddb/modern/index-cursor-3.html: Added.

git-svn-id: http://svn.webkit.org/repository/webkit/trunk@192610 268f45cc-cd09-0410-ab3c-d52691b4dbfc

25 files changed:
LayoutTests/ChangeLog
LayoutTests/storage/indexeddb/modern/cursor-1-expected.txt
LayoutTests/storage/indexeddb/modern/index-cursor-1-expected.txt [new file with mode: 0644]
LayoutTests/storage/indexeddb/modern/index-cursor-1.html [new file with mode: 0644]
LayoutTests/storage/indexeddb/modern/index-cursor-2-expected.txt [new file with mode: 0644]
LayoutTests/storage/indexeddb/modern/index-cursor-2.html [new file with mode: 0644]
LayoutTests/storage/indexeddb/modern/index-cursor-3-expected.txt [new file with mode: 0644]
LayoutTests/storage/indexeddb/modern/index-cursor-3.html [new file with mode: 0644]
Source/WebCore/CMakeLists.txt
Source/WebCore/ChangeLog
Source/WebCore/Modules/indexeddb/client/IDBIndexImpl.cpp
Source/WebCore/Modules/indexeddb/server/IndexValueEntry.cpp
Source/WebCore/Modules/indexeddb/server/IndexValueEntry.h
Source/WebCore/Modules/indexeddb/server/IndexValueStore.cpp
Source/WebCore/Modules/indexeddb/server/IndexValueStore.h
Source/WebCore/Modules/indexeddb/server/MemoryIDBBackingStore.cpp
Source/WebCore/Modules/indexeddb/server/MemoryIndex.cpp
Source/WebCore/Modules/indexeddb/server/MemoryIndex.h
Source/WebCore/Modules/indexeddb/server/MemoryIndexCursor.cpp [new file with mode: 0644]
Source/WebCore/Modules/indexeddb/server/MemoryIndexCursor.h [new file with mode: 0644]
Source/WebCore/Modules/indexeddb/server/MemoryObjectStore.cpp
Source/WebCore/Modules/indexeddb/server/MemoryObjectStore.h
Source/WebCore/Modules/indexeddb/shared/IDBCursorInfo.cpp
Source/WebCore/Modules/indexeddb/shared/IDBCursorInfo.h
Source/WebCore/WebCore.xcodeproj/project.pbxproj

index ab64f15..59c0d8d 100644 (file)
@@ -1,3 +1,18 @@
+2015-11-18  Brady Eidson  <beidson@apple.com>
+
+        Modern IDB:Make in-memory Index cursors work.
+        https://bugs.webkit.org/show_bug.cgi?id=151278
+
+        Reviewed by Alex Christensen.
+
+        * storage/indexeddb/modern/cursor-1-expected.txt:
+        * storage/indexeddb/modern/index-cursor-1-expected.txt: Added.
+        * storage/indexeddb/modern/index-cursor-1.html: Added.
+        * storage/indexeddb/modern/index-cursor-2-expected.txt: Added.
+        * storage/indexeddb/modern/index-cursor-2.html: Added.
+        * storage/indexeddb/modern/index-cursor-3-expected.txt: Added.
+        * storage/indexeddb/modern/index-cursor-3.html: Added.
+
 2015-11-18  Antti Koivisto  <antti@apple.com>
 
         Assertion failure in RenderTreePosition::computeNextSibling
index f58471c..db18cd3 100644 (file)
@@ -6,72 +6,72 @@ Cursor direction is: next
 Cursor source is: [object IDBObjectStore] (TestObjectStore)
 Cursor key is: 0
 Cursor primary key is: 0
-Error opening cursor (expected for now)
+Success opening cursor
 Cursor is: [object IDBCursorWithValue]
 Cursor direction is: next
 Cursor source is: [object IDBIndex] (TestIndex1)
-Cursor key is: null
-Cursor primary key is: null
-Error opening cursor (expected for now)
+Cursor key is: Hello
+Cursor primary key is: foo
+Success opening cursor
 Cursor is: [object IDBCursor]
 Cursor direction is: next
 Cursor source is: [object IDBIndex] (TestIndex1)
-Cursor key is: null
-Cursor primary key is: null
+Cursor key is: Hello
+Cursor primary key is: foo
 Success opening cursor
 Cursor is: [object IDBCursorWithValue]
 Cursor direction is: nextunique
 Cursor source is: [object IDBObjectStore] (TestObjectStore)
 Cursor key is: 0
 Cursor primary key is: 0
-Error opening cursor (expected for now)
+Success opening cursor
 Cursor is: [object IDBCursorWithValue]
 Cursor direction is: nextunique
 Cursor source is: [object IDBIndex] (TestIndex1)
-Cursor key is: null
-Cursor primary key is: null
-Error opening cursor (expected for now)
+Cursor key is: Hello
+Cursor primary key is: foo
+Success opening cursor
 Cursor is: [object IDBCursor]
 Cursor direction is: nextunique
 Cursor source is: [object IDBIndex] (TestIndex1)
-Cursor key is: null
-Cursor primary key is: null
+Cursor key is: Hello
+Cursor primary key is: foo
 Success opening cursor
 Cursor is: [object IDBCursorWithValue]
 Cursor direction is: prev
 Cursor source is: [object IDBObjectStore] (TestObjectStore)
 Cursor key is: foo
 Cursor primary key is: foo
-Error opening cursor (expected for now)
+Success opening cursor
 Cursor is: [object IDBCursorWithValue]
 Cursor direction is: prev
 Cursor source is: [object IDBIndex] (TestIndex1)
-Cursor key is: null
-Cursor primary key is: null
-Error opening cursor (expected for now)
+Cursor key is: Hello
+Cursor primary key is: foo
+Success opening cursor
 Cursor is: [object IDBCursor]
 Cursor direction is: prev
 Cursor source is: [object IDBIndex] (TestIndex1)
-Cursor key is: null
-Cursor primary key is: null
+Cursor key is: Hello
+Cursor primary key is: foo
 Success opening cursor
 Cursor is: [object IDBCursorWithValue]
 Cursor direction is: prevunique
 Cursor source is: [object IDBObjectStore] (TestObjectStore)
 Cursor key is: foo
 Cursor primary key is: foo
-Error opening cursor (expected for now)
+Success opening cursor
 Cursor is: [object IDBCursorWithValue]
 Cursor direction is: prevunique
 Cursor source is: [object IDBIndex] (TestIndex1)
-Cursor key is: null
-Cursor primary key is: null
-Error opening cursor (expected for now)
+Cursor key is: Hello
+Cursor primary key is: foo
+Success opening cursor
 Cursor is: [object IDBCursor]
 Cursor direction is: prevunique
 Cursor source is: [object IDBIndex] (TestIndex1)
-Cursor key is: null
-Cursor primary key is: null
+Cursor key is: Hello
+Cursor primary key is: foo
 Initial upgrade versionchange transaction complete
 Done
 
diff --git a/LayoutTests/storage/indexeddb/modern/index-cursor-1-expected.txt b/LayoutTests/storage/indexeddb/modern/index-cursor-1-expected.txt
new file mode 100644 (file)
index 0000000..2e495dd
--- /dev/null
@@ -0,0 +1,454 @@
+This tests cursors that iterate over entire indexes.
+Initial upgrade needed: Old version - 0 New version - 1
+
+Starting a new cursor: testCursorDirection(index1, 'next')
+TestIndex1 count is: 12
+TestIndex2 count is: 12
+Success opening or iterating cursor
+Cursor direction is: next
+Cursor source is: TestIndex1
+Cursor key is: A
+Cursor primary key is: 1
+Cursor value is: [object Object]
+Success opening or iterating cursor
+Cursor direction is: next
+Cursor source is: TestIndex1
+Cursor key is: A
+Cursor primary key is: 2
+Cursor value is: [object Object]
+Success opening or iterating cursor
+Cursor direction is: next
+Cursor source is: TestIndex1
+Cursor key is: A
+Cursor primary key is: 3
+Cursor value is: [object Object]
+Success opening or iterating cursor
+Cursor direction is: next
+Cursor source is: TestIndex1
+Cursor key is: B
+Cursor primary key is: 4
+Cursor value is: [object Object]
+Success opening or iterating cursor
+Cursor direction is: next
+Cursor source is: TestIndex1
+Cursor key is: B
+Cursor primary key is: 5
+Cursor value is: [object Object]
+Success opening or iterating cursor
+Cursor direction is: next
+Cursor source is: TestIndex1
+Cursor key is: B
+Cursor primary key is: 6
+Cursor value is: [object Object]
+Success opening or iterating cursor
+Cursor direction is: next
+Cursor source is: TestIndex1
+Cursor key is: C
+Cursor primary key is: 7
+Cursor value is: [object Object]
+Success opening or iterating cursor
+Cursor direction is: next
+Cursor source is: TestIndex1
+Cursor key is: C
+Cursor primary key is: 8
+Cursor value is: [object Object]
+Success opening or iterating cursor
+Cursor direction is: next
+Cursor source is: TestIndex1
+Cursor key is: C
+Cursor primary key is: 9
+Cursor value is: [object Object]
+Success opening or iterating cursor
+Cursor direction is: next
+Cursor source is: TestIndex1
+Cursor key is: D
+Cursor primary key is: 10
+Cursor value is: [object Object]
+Success opening or iterating cursor
+Cursor direction is: next
+Cursor source is: TestIndex1
+Cursor key is: D
+Cursor primary key is: 11
+Cursor value is: [object Object]
+Success opening or iterating cursor
+Cursor direction is: next
+Cursor source is: TestIndex1
+Cursor key is: D
+Cursor primary key is: 12
+Cursor value is: [object Object]
+Success opening or iterating cursor
+Cursor direction is: next
+Cursor source is: TestIndex1
+Cursor key is: undefined
+Cursor primary key is: undefined
+Cursor value is: undefined
+
+Starting a new cursor: testCursorDirection(index2, 'next')
+Success opening or iterating cursor
+Cursor direction is: next
+Cursor source is: TestIndex2
+Cursor key is: a
+Cursor primary key is: 1
+Cursor value is: [object Object]
+Success opening or iterating cursor
+Cursor direction is: next
+Cursor source is: TestIndex2
+Cursor key is: a
+Cursor primary key is: 2
+Cursor value is: [object Object]
+Success opening or iterating cursor
+Cursor direction is: next
+Cursor source is: TestIndex2
+Cursor key is: a
+Cursor primary key is: 3
+Cursor value is: [object Object]
+Success opening or iterating cursor
+Cursor direction is: next
+Cursor source is: TestIndex2
+Cursor key is: b
+Cursor primary key is: 4
+Cursor value is: [object Object]
+Success opening or iterating cursor
+Cursor direction is: next
+Cursor source is: TestIndex2
+Cursor key is: b
+Cursor primary key is: 5
+Cursor value is: [object Object]
+Success opening or iterating cursor
+Cursor direction is: next
+Cursor source is: TestIndex2
+Cursor key is: b
+Cursor primary key is: 6
+Cursor value is: [object Object]
+Success opening or iterating cursor
+Cursor direction is: next
+Cursor source is: TestIndex2
+Cursor key is: c
+Cursor primary key is: 7
+Cursor value is: [object Object]
+Success opening or iterating cursor
+Cursor direction is: next
+Cursor source is: TestIndex2
+Cursor key is: c
+Cursor primary key is: 8
+Cursor value is: [object Object]
+Success opening or iterating cursor
+Cursor direction is: next
+Cursor source is: TestIndex2
+Cursor key is: c
+Cursor primary key is: 9
+Cursor value is: [object Object]
+Success opening or iterating cursor
+Cursor direction is: next
+Cursor source is: TestIndex2
+Cursor key is: d
+Cursor primary key is: 10
+Cursor value is: [object Object]
+Success opening or iterating cursor
+Cursor direction is: next
+Cursor source is: TestIndex2
+Cursor key is: d
+Cursor primary key is: 11
+Cursor value is: [object Object]
+Success opening or iterating cursor
+Cursor direction is: next
+Cursor source is: TestIndex2
+Cursor key is: d
+Cursor primary key is: 12
+Cursor value is: [object Object]
+Success opening or iterating cursor
+Cursor direction is: next
+Cursor source is: TestIndex2
+Cursor key is: undefined
+Cursor primary key is: undefined
+Cursor value is: undefined
+
+Starting a new cursor: testCursorDirection(index1, 'nextunique')
+Success opening or iterating cursor
+Cursor direction is: nextunique
+Cursor source is: TestIndex1
+Cursor key is: A
+Cursor primary key is: 1
+Cursor value is: [object Object]
+Success opening or iterating cursor
+Cursor direction is: nextunique
+Cursor source is: TestIndex1
+Cursor key is: B
+Cursor primary key is: 4
+Cursor value is: [object Object]
+Success opening or iterating cursor
+Cursor direction is: nextunique
+Cursor source is: TestIndex1
+Cursor key is: C
+Cursor primary key is: 7
+Cursor value is: [object Object]
+Success opening or iterating cursor
+Cursor direction is: nextunique
+Cursor source is: TestIndex1
+Cursor key is: D
+Cursor primary key is: 10
+Cursor value is: [object Object]
+Success opening or iterating cursor
+Cursor direction is: nextunique
+Cursor source is: TestIndex1
+Cursor key is: undefined
+Cursor primary key is: undefined
+Cursor value is: undefined
+
+Starting a new cursor: testCursorDirection(index2, 'nextunique')
+Success opening or iterating cursor
+Cursor direction is: nextunique
+Cursor source is: TestIndex2
+Cursor key is: a
+Cursor primary key is: 1
+Cursor value is: [object Object]
+Success opening or iterating cursor
+Cursor direction is: nextunique
+Cursor source is: TestIndex2
+Cursor key is: b
+Cursor primary key is: 4
+Cursor value is: [object Object]
+Success opening or iterating cursor
+Cursor direction is: nextunique
+Cursor source is: TestIndex2
+Cursor key is: c
+Cursor primary key is: 7
+Cursor value is: [object Object]
+Success opening or iterating cursor
+Cursor direction is: nextunique
+Cursor source is: TestIndex2
+Cursor key is: d
+Cursor primary key is: 10
+Cursor value is: [object Object]
+Success opening or iterating cursor
+Cursor direction is: nextunique
+Cursor source is: TestIndex2
+Cursor key is: undefined
+Cursor primary key is: undefined
+Cursor value is: undefined
+
+Starting a new cursor: testCursorDirection(index1, 'prev')
+Success opening or iterating cursor
+Cursor direction is: prev
+Cursor source is: TestIndex1
+Cursor key is: D
+Cursor primary key is: 12
+Cursor value is: [object Object]
+Success opening or iterating cursor
+Cursor direction is: prev
+Cursor source is: TestIndex1
+Cursor key is: D
+Cursor primary key is: 11
+Cursor value is: [object Object]
+Success opening or iterating cursor
+Cursor direction is: prev
+Cursor source is: TestIndex1
+Cursor key is: D
+Cursor primary key is: 10
+Cursor value is: [object Object]
+Success opening or iterating cursor
+Cursor direction is: prev
+Cursor source is: TestIndex1
+Cursor key is: C
+Cursor primary key is: 9
+Cursor value is: [object Object]
+Success opening or iterating cursor
+Cursor direction is: prev
+Cursor source is: TestIndex1
+Cursor key is: C
+Cursor primary key is: 8
+Cursor value is: [object Object]
+Success opening or iterating cursor
+Cursor direction is: prev
+Cursor source is: TestIndex1
+Cursor key is: C
+Cursor primary key is: 7
+Cursor value is: [object Object]
+Success opening or iterating cursor
+Cursor direction is: prev
+Cursor source is: TestIndex1
+Cursor key is: B
+Cursor primary key is: 6
+Cursor value is: [object Object]
+Success opening or iterating cursor
+Cursor direction is: prev
+Cursor source is: TestIndex1
+Cursor key is: B
+Cursor primary key is: 5
+Cursor value is: [object Object]
+Success opening or iterating cursor
+Cursor direction is: prev
+Cursor source is: TestIndex1
+Cursor key is: B
+Cursor primary key is: 4
+Cursor value is: [object Object]
+Success opening or iterating cursor
+Cursor direction is: prev
+Cursor source is: TestIndex1
+Cursor key is: A
+Cursor primary key is: 3
+Cursor value is: [object Object]
+Success opening or iterating cursor
+Cursor direction is: prev
+Cursor source is: TestIndex1
+Cursor key is: A
+Cursor primary key is: 2
+Cursor value is: [object Object]
+Success opening or iterating cursor
+Cursor direction is: prev
+Cursor source is: TestIndex1
+Cursor key is: A
+Cursor primary key is: 1
+Cursor value is: [object Object]
+Success opening or iterating cursor
+Cursor direction is: prev
+Cursor source is: TestIndex1
+Cursor key is: undefined
+Cursor primary key is: undefined
+Cursor value is: undefined
+
+Starting a new cursor: testCursorDirection(index2, 'prev')
+Success opening or iterating cursor
+Cursor direction is: prev
+Cursor source is: TestIndex2
+Cursor key is: d
+Cursor primary key is: 12
+Cursor value is: [object Object]
+Success opening or iterating cursor
+Cursor direction is: prev
+Cursor source is: TestIndex2
+Cursor key is: d
+Cursor primary key is: 11
+Cursor value is: [object Object]
+Success opening or iterating cursor
+Cursor direction is: prev
+Cursor source is: TestIndex2
+Cursor key is: d
+Cursor primary key is: 10
+Cursor value is: [object Object]
+Success opening or iterating cursor
+Cursor direction is: prev
+Cursor source is: TestIndex2
+Cursor key is: c
+Cursor primary key is: 9
+Cursor value is: [object Object]
+Success opening or iterating cursor
+Cursor direction is: prev
+Cursor source is: TestIndex2
+Cursor key is: c
+Cursor primary key is: 8
+Cursor value is: [object Object]
+Success opening or iterating cursor
+Cursor direction is: prev
+Cursor source is: TestIndex2
+Cursor key is: c
+Cursor primary key is: 7
+Cursor value is: [object Object]
+Success opening or iterating cursor
+Cursor direction is: prev
+Cursor source is: TestIndex2
+Cursor key is: b
+Cursor primary key is: 6
+Cursor value is: [object Object]
+Success opening or iterating cursor
+Cursor direction is: prev
+Cursor source is: TestIndex2
+Cursor key is: b
+Cursor primary key is: 5
+Cursor value is: [object Object]
+Success opening or iterating cursor
+Cursor direction is: prev
+Cursor source is: TestIndex2
+Cursor key is: b
+Cursor primary key is: 4
+Cursor value is: [object Object]
+Success opening or iterating cursor
+Cursor direction is: prev
+Cursor source is: TestIndex2
+Cursor key is: a
+Cursor primary key is: 3
+Cursor value is: [object Object]
+Success opening or iterating cursor
+Cursor direction is: prev
+Cursor source is: TestIndex2
+Cursor key is: a
+Cursor primary key is: 2
+Cursor value is: [object Object]
+Success opening or iterating cursor
+Cursor direction is: prev
+Cursor source is: TestIndex2
+Cursor key is: a
+Cursor primary key is: 1
+Cursor value is: [object Object]
+Success opening or iterating cursor
+Cursor direction is: prev
+Cursor source is: TestIndex2
+Cursor key is: undefined
+Cursor primary key is: undefined
+Cursor value is: undefined
+
+Starting a new cursor: testCursorDirection(index1, 'prevunique')
+Success opening or iterating cursor
+Cursor direction is: prevunique
+Cursor source is: TestIndex1
+Cursor key is: D
+Cursor primary key is: 12
+Cursor value is: [object Object]
+Success opening or iterating cursor
+Cursor direction is: prevunique
+Cursor source is: TestIndex1
+Cursor key is: C
+Cursor primary key is: 9
+Cursor value is: [object Object]
+Success opening or iterating cursor
+Cursor direction is: prevunique
+Cursor source is: TestIndex1
+Cursor key is: B
+Cursor primary key is: 6
+Cursor value is: [object Object]
+Success opening or iterating cursor
+Cursor direction is: prevunique
+Cursor source is: TestIndex1
+Cursor key is: A
+Cursor primary key is: 3
+Cursor value is: [object Object]
+Success opening or iterating cursor
+Cursor direction is: prevunique
+Cursor source is: TestIndex1
+Cursor key is: undefined
+Cursor primary key is: undefined
+Cursor value is: undefined
+
+Starting a new cursor: testCursorDirection(index2, 'prevunique')
+Success opening or iterating cursor
+Cursor direction is: prevunique
+Cursor source is: TestIndex2
+Cursor key is: d
+Cursor primary key is: 12
+Cursor value is: [object Object]
+Success opening or iterating cursor
+Cursor direction is: prevunique
+Cursor source is: TestIndex2
+Cursor key is: c
+Cursor primary key is: 9
+Cursor value is: [object Object]
+Success opening or iterating cursor
+Cursor direction is: prevunique
+Cursor source is: TestIndex2
+Cursor key is: b
+Cursor primary key is: 6
+Cursor value is: [object Object]
+Success opening or iterating cursor
+Cursor direction is: prevunique
+Cursor source is: TestIndex2
+Cursor key is: a
+Cursor primary key is: 3
+Cursor value is: [object Object]
+Success opening or iterating cursor
+Cursor direction is: prevunique
+Cursor source is: TestIndex2
+Cursor key is: undefined
+Cursor primary key is: undefined
+Cursor value is: undefined
+Done
+
diff --git a/LayoutTests/storage/indexeddb/modern/index-cursor-1.html b/LayoutTests/storage/indexeddb/modern/index-cursor-1.html
new file mode 100644 (file)
index 0000000..6f45efa
--- /dev/null
@@ -0,0 +1,133 @@
+This tests cursors that iterate over entire indexes.<br>
+<div id="logger"></div>
+<script>
+
+if (window.testRunner) {
+    testRunner.waitUntilDone();
+    testRunner.dumpAsText();
+}
+
+function done()
+{
+    log("Done");
+    if (window.testRunner)
+        testRunner.notifyDone();
+}
+
+function log(message)
+{
+    document.getElementById("logger").innerHTML += message + "<br>";
+}
+
+function logCursor(cursor)
+{
+    log("Cursor direction is: " + cursor.direction);
+    log("Cursor source is: " + cursor.source.name);    
+    log("Cursor key is: " + cursor.key);    
+    log("Cursor primary key is: " + cursor.primaryKey);
+    log("Cursor value is: " + cursor.value);  
+}
+
+function setupRequest(request)
+{
+    request.onsuccess = function() {
+        log("Success opening or iterating cursor");
+        logCursor(request.result);  
+        if (request.result.key != undefined)
+            request.result.continue();
+        else
+            startNextCursor();
+    }
+    request.onerror = function(e) {
+        log("Unexpected error opening or iterating cursor");
+        logCursor(request.result);
+        done();
+    } 
+}
+
+function testCursorDirection(index, direction)
+{
+    var range = IDBKeyRange.lowerBound(-Infinity);
+    var request = index.openCursor(range, direction);
+    setupRequest(request);
+}
+
+var index1;
+var index2;
+
+var cursorCommands = [
+    "testCursorDirection(index2, 'prevunique')",
+    "testCursorDirection(index1, 'prevunique')",
+    "testCursorDirection(index2, 'prev')",
+    "testCursorDirection(index1, 'prev')",
+    "testCursorDirection(index2, 'nextunique')",
+    "testCursorDirection(index1, 'nextunique')",
+    "testCursorDirection(index2, 'next')",
+    "testCursorDirection(index1, 'next')",
+];
+
+function startNextCursor()
+{
+    if (!cursorCommands.length) {
+        done();
+        return;
+    }
+    
+    var command = cursorCommands.pop();
+    log ("");
+    log("Starting a new cursor: " + command);
+    eval(command);
+}
+    
+var createRequest = window.indexedDB.open("IndexCursor1Database", 1);
+createRequest.onupgradeneeded = function(event) {
+    log("Initial upgrade needed: Old version - " + event.oldVersion + " New version - " + event.newVersion);
+
+    var versionTransaction = createRequest.transaction;
+    var database = event.target.result;
+    var objectStore = database.createObjectStore("TestObjectStore");
+    index1 = objectStore.createIndex("TestIndex1", "bar");
+    index2 = objectStore.createIndex("TestIndex2", "baz");
+
+    objectStore.put({ bar: "A", baz: "a" }, 1);
+    objectStore.put({ bar: "A", baz: "a" }, 3);
+    objectStore.put({ bar: "A", baz: "a" }, 2);
+    objectStore.put({ bar: "B", baz: "b" }, 5);
+    objectStore.put({ bar: "B", baz: "b" }, 6);
+    objectStore.put({ bar: "B", baz: "b" }, 4);
+    objectStore.put({ bar: "C", baz: "c" }, 7);
+    objectStore.put({ bar: "C", baz: "c" }, 9);
+    objectStore.put({ bar: "C", baz: "c" }, 8);
+    objectStore.put({ bar: "D", baz: "d" }, 11);
+    objectStore.put({ bar: "D", baz: "d" }, 12);
+    objectStore.put({ bar: "D", baz: "d" }, 10);
+
+    var req1 = index1.count();
+    req1.onsuccess = function() {
+        log("TestIndex1 count is: " + req1.result);
+    }
+
+    var req2 = index2.count();
+    req2.onsuccess = function() {
+        log("TestIndex2 count is: " + req2.result);
+    }
+    
+    startNextCursor();
+    
+    versionTransaction.onabort = function(event) {
+        log("Initial upgrade versionchange transaction unexpected aborted");
+        done();
+    }
+
+    versionTransaction.oncomplete = function(event) {
+        log("Initial upgrade versionchange transaction complete");
+        done();
+    }
+
+    versionTransaction.onerror = function(event) {
+        log("Initial upgrade versionchange transaction unexpected error" + event);
+        done();
+    }
+}
+
+</script>
diff --git a/LayoutTests/storage/indexeddb/modern/index-cursor-2-expected.txt b/LayoutTests/storage/indexeddb/modern/index-cursor-2-expected.txt
new file mode 100644 (file)
index 0000000..c5ffc94
--- /dev/null
@@ -0,0 +1,549 @@
+This tests cursors that iterate over parts of indexes.
+Initial upgrade needed: Old version - 0 New version - 1
+
+Starting a new cursor: testCursorDirection('next', IDBKeyRange.bound('B', 'D', true, true))
+TestIndex1 count is: 12
+Success opening or iterating cursor
+Cursor direction is: next
+Cursor source is: TestIndex1
+Cursor key is: C
+Cursor primary key is: 7
+Cursor value is: [object Object]
+Success opening or iterating cursor
+Cursor direction is: next
+Cursor source is: TestIndex1
+Cursor key is: C
+Cursor primary key is: 8
+Cursor value is: [object Object]
+Success opening or iterating cursor
+Cursor direction is: next
+Cursor source is: TestIndex1
+Cursor key is: C
+Cursor primary key is: 9
+Cursor value is: [object Object]
+Success opening or iterating cursor
+Cursor direction is: next
+Cursor source is: TestIndex1
+Cursor key is: undefined
+Cursor primary key is: undefined
+Cursor value is: undefined
+
+Starting a new cursor: testCursorDirection('nextunique', IDBKeyRange.bound('B', 'D', true, true))
+Success opening or iterating cursor
+Cursor direction is: nextunique
+Cursor source is: TestIndex1
+Cursor key is: C
+Cursor primary key is: 7
+Cursor value is: [object Object]
+Success opening or iterating cursor
+Cursor direction is: nextunique
+Cursor source is: TestIndex1
+Cursor key is: undefined
+Cursor primary key is: undefined
+Cursor value is: undefined
+
+Starting a new cursor: testCursorDirection('prev', IDBKeyRange.bound('B', 'D', true, true))
+Success opening or iterating cursor
+Cursor direction is: prev
+Cursor source is: TestIndex1
+Cursor key is: C
+Cursor primary key is: 9
+Cursor value is: [object Object]
+Success opening or iterating cursor
+Cursor direction is: prev
+Cursor source is: TestIndex1
+Cursor key is: C
+Cursor primary key is: 8
+Cursor value is: [object Object]
+Success opening or iterating cursor
+Cursor direction is: prev
+Cursor source is: TestIndex1
+Cursor key is: C
+Cursor primary key is: 7
+Cursor value is: [object Object]
+Success opening or iterating cursor
+Cursor direction is: prev
+Cursor source is: TestIndex1
+Cursor key is: undefined
+Cursor primary key is: undefined
+Cursor value is: undefined
+
+Starting a new cursor: testCursorDirection('prevunique', IDBKeyRange.bound('B', 'D', true, true))
+Success opening or iterating cursor
+Cursor direction is: prevunique
+Cursor source is: TestIndex1
+Cursor key is: C
+Cursor primary key is: 9
+Cursor value is: [object Object]
+Success opening or iterating cursor
+Cursor direction is: prevunique
+Cursor source is: TestIndex1
+Cursor key is: undefined
+Cursor primary key is: undefined
+Cursor value is: undefined
+
+Starting a new cursor: testCursorDirection('next', IDBKeyRange.bound('B', 'C'))
+Success opening or iterating cursor
+Cursor direction is: next
+Cursor source is: TestIndex1
+Cursor key is: B
+Cursor primary key is: 4
+Cursor value is: [object Object]
+Success opening or iterating cursor
+Cursor direction is: next
+Cursor source is: TestIndex1
+Cursor key is: B
+Cursor primary key is: 5
+Cursor value is: [object Object]
+Success opening or iterating cursor
+Cursor direction is: next
+Cursor source is: TestIndex1
+Cursor key is: B
+Cursor primary key is: 6
+Cursor value is: [object Object]
+Success opening or iterating cursor
+Cursor direction is: next
+Cursor source is: TestIndex1
+Cursor key is: C
+Cursor primary key is: 7
+Cursor value is: [object Object]
+Success opening or iterating cursor
+Cursor direction is: next
+Cursor source is: TestIndex1
+Cursor key is: C
+Cursor primary key is: 8
+Cursor value is: [object Object]
+Success opening or iterating cursor
+Cursor direction is: next
+Cursor source is: TestIndex1
+Cursor key is: C
+Cursor primary key is: 9
+Cursor value is: [object Object]
+Success opening or iterating cursor
+Cursor direction is: next
+Cursor source is: TestIndex1
+Cursor key is: undefined
+Cursor primary key is: undefined
+Cursor value is: undefined
+
+Starting a new cursor: testCursorDirection('nextunique', IDBKeyRange.bound('B', 'C'))
+Success opening or iterating cursor
+Cursor direction is: nextunique
+Cursor source is: TestIndex1
+Cursor key is: B
+Cursor primary key is: 4
+Cursor value is: [object Object]
+Success opening or iterating cursor
+Cursor direction is: nextunique
+Cursor source is: TestIndex1
+Cursor key is: C
+Cursor primary key is: 7
+Cursor value is: [object Object]
+Success opening or iterating cursor
+Cursor direction is: nextunique
+Cursor source is: TestIndex1
+Cursor key is: undefined
+Cursor primary key is: undefined
+Cursor value is: undefined
+
+Starting a new cursor: testCursorDirection('prev', IDBKeyRange.bound('B', 'C'))
+Success opening or iterating cursor
+Cursor direction is: prev
+Cursor source is: TestIndex1
+Cursor key is: C
+Cursor primary key is: 9
+Cursor value is: [object Object]
+Success opening or iterating cursor
+Cursor direction is: prev
+Cursor source is: TestIndex1
+Cursor key is: C
+Cursor primary key is: 8
+Cursor value is: [object Object]
+Success opening or iterating cursor
+Cursor direction is: prev
+Cursor source is: TestIndex1
+Cursor key is: C
+Cursor primary key is: 7
+Cursor value is: [object Object]
+Success opening or iterating cursor
+Cursor direction is: prev
+Cursor source is: TestIndex1
+Cursor key is: B
+Cursor primary key is: 6
+Cursor value is: [object Object]
+Success opening or iterating cursor
+Cursor direction is: prev
+Cursor source is: TestIndex1
+Cursor key is: B
+Cursor primary key is: 5
+Cursor value is: [object Object]
+Success opening or iterating cursor
+Cursor direction is: prev
+Cursor source is: TestIndex1
+Cursor key is: B
+Cursor primary key is: 4
+Cursor value is: [object Object]
+Success opening or iterating cursor
+Cursor direction is: prev
+Cursor source is: TestIndex1
+Cursor key is: undefined
+Cursor primary key is: undefined
+Cursor value is: undefined
+
+Starting a new cursor: testCursorDirection('prevunique', IDBKeyRange.bound('B', 'C'))
+Success opening or iterating cursor
+Cursor direction is: prevunique
+Cursor source is: TestIndex1
+Cursor key is: C
+Cursor primary key is: 9
+Cursor value is: [object Object]
+Success opening or iterating cursor
+Cursor direction is: prevunique
+Cursor source is: TestIndex1
+Cursor key is: B
+Cursor primary key is: 6
+Cursor value is: [object Object]
+Success opening or iterating cursor
+Cursor direction is: prevunique
+Cursor source is: TestIndex1
+Cursor key is: undefined
+Cursor primary key is: undefined
+Cursor value is: undefined
+
+Starting a new cursor: testCursorDirection('next', IDBKeyRange.upperBound('B'))
+Success opening or iterating cursor
+Cursor direction is: next
+Cursor source is: TestIndex1
+Cursor key is: A
+Cursor primary key is: 1
+Cursor value is: [object Object]
+Success opening or iterating cursor
+Cursor direction is: next
+Cursor source is: TestIndex1
+Cursor key is: A
+Cursor primary key is: 2
+Cursor value is: [object Object]
+Success opening or iterating cursor
+Cursor direction is: next
+Cursor source is: TestIndex1
+Cursor key is: A
+Cursor primary key is: 3
+Cursor value is: [object Object]
+Success opening or iterating cursor
+Cursor direction is: next
+Cursor source is: TestIndex1
+Cursor key is: B
+Cursor primary key is: 4
+Cursor value is: [object Object]
+Success opening or iterating cursor
+Cursor direction is: next
+Cursor source is: TestIndex1
+Cursor key is: B
+Cursor primary key is: 5
+Cursor value is: [object Object]
+Success opening or iterating cursor
+Cursor direction is: next
+Cursor source is: TestIndex1
+Cursor key is: B
+Cursor primary key is: 6
+Cursor value is: [object Object]
+Success opening or iterating cursor
+Cursor direction is: next
+Cursor source is: TestIndex1
+Cursor key is: undefined
+Cursor primary key is: undefined
+Cursor value is: undefined
+
+Starting a new cursor: testCursorDirection('nextunique', IDBKeyRange.upperBound('B'))
+Success opening or iterating cursor
+Cursor direction is: nextunique
+Cursor source is: TestIndex1
+Cursor key is: A
+Cursor primary key is: 1
+Cursor value is: [object Object]
+Success opening or iterating cursor
+Cursor direction is: nextunique
+Cursor source is: TestIndex1
+Cursor key is: B
+Cursor primary key is: 4
+Cursor value is: [object Object]
+Success opening or iterating cursor
+Cursor direction is: nextunique
+Cursor source is: TestIndex1
+Cursor key is: undefined
+Cursor primary key is: undefined
+Cursor value is: undefined
+
+Starting a new cursor: testCursorDirection('prev', IDBKeyRange.upperBound('B'))
+Success opening or iterating cursor
+Cursor direction is: prev
+Cursor source is: TestIndex1
+Cursor key is: B
+Cursor primary key is: 6
+Cursor value is: [object Object]
+Success opening or iterating cursor
+Cursor direction is: prev
+Cursor source is: TestIndex1
+Cursor key is: B
+Cursor primary key is: 5
+Cursor value is: [object Object]
+Success opening or iterating cursor
+Cursor direction is: prev
+Cursor source is: TestIndex1
+Cursor key is: B
+Cursor primary key is: 4
+Cursor value is: [object Object]
+Success opening or iterating cursor
+Cursor direction is: prev
+Cursor source is: TestIndex1
+Cursor key is: A
+Cursor primary key is: 3
+Cursor value is: [object Object]
+Success opening or iterating cursor
+Cursor direction is: prev
+Cursor source is: TestIndex1
+Cursor key is: A
+Cursor primary key is: 2
+Cursor value is: [object Object]
+Success opening or iterating cursor
+Cursor direction is: prev
+Cursor source is: TestIndex1
+Cursor key is: A
+Cursor primary key is: 1
+Cursor value is: [object Object]
+Success opening or iterating cursor
+Cursor direction is: prev
+Cursor source is: TestIndex1
+Cursor key is: undefined
+Cursor primary key is: undefined
+Cursor value is: undefined
+
+Starting a new cursor: testCursorDirection('prevunique', IDBKeyRange.upperBound('B'))
+Success opening or iterating cursor
+Cursor direction is: prevunique
+Cursor source is: TestIndex1
+Cursor key is: B
+Cursor primary key is: 6
+Cursor value is: [object Object]
+Success opening or iterating cursor
+Cursor direction is: prevunique
+Cursor source is: TestIndex1
+Cursor key is: A
+Cursor primary key is: 3
+Cursor value is: [object Object]
+Success opening or iterating cursor
+Cursor direction is: prevunique
+Cursor source is: TestIndex1
+Cursor key is: undefined
+Cursor primary key is: undefined
+Cursor value is: undefined
+
+Starting a new cursor: testCursorDirection('next', IDBKeyRange.lowerBound('C'))
+Success opening or iterating cursor
+Cursor direction is: next
+Cursor source is: TestIndex1
+Cursor key is: C
+Cursor primary key is: 7
+Cursor value is: [object Object]
+Success opening or iterating cursor
+Cursor direction is: next
+Cursor source is: TestIndex1
+Cursor key is: C
+Cursor primary key is: 8
+Cursor value is: [object Object]
+Success opening or iterating cursor
+Cursor direction is: next
+Cursor source is: TestIndex1
+Cursor key is: C
+Cursor primary key is: 9
+Cursor value is: [object Object]
+Success opening or iterating cursor
+Cursor direction is: next
+Cursor source is: TestIndex1
+Cursor key is: D
+Cursor primary key is: 10
+Cursor value is: [object Object]
+Success opening or iterating cursor
+Cursor direction is: next
+Cursor source is: TestIndex1
+Cursor key is: D
+Cursor primary key is: 11
+Cursor value is: [object Object]
+Success opening or iterating cursor
+Cursor direction is: next
+Cursor source is: TestIndex1
+Cursor key is: D
+Cursor primary key is: 12
+Cursor value is: [object Object]
+Success opening or iterating cursor
+Cursor direction is: next
+Cursor source is: TestIndex1
+Cursor key is: undefined
+Cursor primary key is: undefined
+Cursor value is: undefined
+
+Starting a new cursor: testCursorDirection('nextunique', IDBKeyRange.lowerBound('C'))
+Success opening or iterating cursor
+Cursor direction is: nextunique
+Cursor source is: TestIndex1
+Cursor key is: C
+Cursor primary key is: 7
+Cursor value is: [object Object]
+Success opening or iterating cursor
+Cursor direction is: nextunique
+Cursor source is: TestIndex1
+Cursor key is: D
+Cursor primary key is: 10
+Cursor value is: [object Object]
+Success opening or iterating cursor
+Cursor direction is: nextunique
+Cursor source is: TestIndex1
+Cursor key is: undefined
+Cursor primary key is: undefined
+Cursor value is: undefined
+
+Starting a new cursor: testCursorDirection('prev', IDBKeyRange.lowerBound('C'))
+Success opening or iterating cursor
+Cursor direction is: prev
+Cursor source is: TestIndex1
+Cursor key is: D
+Cursor primary key is: 12
+Cursor value is: [object Object]
+Success opening or iterating cursor
+Cursor direction is: prev
+Cursor source is: TestIndex1
+Cursor key is: D
+Cursor primary key is: 11
+Cursor value is: [object Object]
+Success opening or iterating cursor
+Cursor direction is: prev
+Cursor source is: TestIndex1
+Cursor key is: D
+Cursor primary key is: 10
+Cursor value is: [object Object]
+Success opening or iterating cursor
+Cursor direction is: prev
+Cursor source is: TestIndex1
+Cursor key is: C
+Cursor primary key is: 9
+Cursor value is: [object Object]
+Success opening or iterating cursor
+Cursor direction is: prev
+Cursor source is: TestIndex1
+Cursor key is: C
+Cursor primary key is: 8
+Cursor value is: [object Object]
+Success opening or iterating cursor
+Cursor direction is: prev
+Cursor source is: TestIndex1
+Cursor key is: C
+Cursor primary key is: 7
+Cursor value is: [object Object]
+Success opening or iterating cursor
+Cursor direction is: prev
+Cursor source is: TestIndex1
+Cursor key is: undefined
+Cursor primary key is: undefined
+Cursor value is: undefined
+
+Starting a new cursor: testCursorDirection('prevunique', IDBKeyRange.lowerBound('C'))
+Success opening or iterating cursor
+Cursor direction is: prevunique
+Cursor source is: TestIndex1
+Cursor key is: D
+Cursor primary key is: 12
+Cursor value is: [object Object]
+Success opening or iterating cursor
+Cursor direction is: prevunique
+Cursor source is: TestIndex1
+Cursor key is: C
+Cursor primary key is: 9
+Cursor value is: [object Object]
+Success opening or iterating cursor
+Cursor direction is: prevunique
+Cursor source is: TestIndex1
+Cursor key is: undefined
+Cursor primary key is: undefined
+Cursor value is: undefined
+
+Starting a new cursor: testCursorDirection('next', IDBKeyRange.only('B'))
+Success opening or iterating cursor
+Cursor direction is: next
+Cursor source is: TestIndex1
+Cursor key is: B
+Cursor primary key is: 4
+Cursor value is: [object Object]
+Success opening or iterating cursor
+Cursor direction is: next
+Cursor source is: TestIndex1
+Cursor key is: B
+Cursor primary key is: 5
+Cursor value is: [object Object]
+Success opening or iterating cursor
+Cursor direction is: next
+Cursor source is: TestIndex1
+Cursor key is: B
+Cursor primary key is: 6
+Cursor value is: [object Object]
+Success opening or iterating cursor
+Cursor direction is: next
+Cursor source is: TestIndex1
+Cursor key is: undefined
+Cursor primary key is: undefined
+Cursor value is: undefined
+
+Starting a new cursor: testCursorDirection('nextunique', IDBKeyRange.only('B'))
+Success opening or iterating cursor
+Cursor direction is: nextunique
+Cursor source is: TestIndex1
+Cursor key is: B
+Cursor primary key is: 4
+Cursor value is: [object Object]
+Success opening or iterating cursor
+Cursor direction is: nextunique
+Cursor source is: TestIndex1
+Cursor key is: undefined
+Cursor primary key is: undefined
+Cursor value is: undefined
+
+Starting a new cursor: testCursorDirection('prev', IDBKeyRange.only('B'))
+Success opening or iterating cursor
+Cursor direction is: prev
+Cursor source is: TestIndex1
+Cursor key is: B
+Cursor primary key is: 6
+Cursor value is: [object Object]
+Success opening or iterating cursor
+Cursor direction is: prev
+Cursor source is: TestIndex1
+Cursor key is: B
+Cursor primary key is: 5
+Cursor value is: [object Object]
+Success opening or iterating cursor
+Cursor direction is: prev
+Cursor source is: TestIndex1
+Cursor key is: B
+Cursor primary key is: 4
+Cursor value is: [object Object]
+Success opening or iterating cursor
+Cursor direction is: prev
+Cursor source is: TestIndex1
+Cursor key is: undefined
+Cursor primary key is: undefined
+Cursor value is: undefined
+
+Starting a new cursor: testCursorDirection('prevunique', IDBKeyRange.only('B'))
+Success opening or iterating cursor
+Cursor direction is: prevunique
+Cursor source is: TestIndex1
+Cursor key is: B
+Cursor primary key is: 6
+Cursor value is: [object Object]
+Success opening or iterating cursor
+Cursor direction is: prevunique
+Cursor source is: TestIndex1
+Cursor key is: undefined
+Cursor primary key is: undefined
+Cursor value is: undefined
+Done
+
diff --git a/LayoutTests/storage/indexeddb/modern/index-cursor-2.html b/LayoutTests/storage/indexeddb/modern/index-cursor-2.html
new file mode 100644 (file)
index 0000000..59252c7
--- /dev/null
@@ -0,0 +1,137 @@
+This tests cursors that iterate over parts of indexes.<br>
+<div id="logger"></div>
+<script>
+
+if (window.testRunner) {
+    testRunner.waitUntilDone();
+    testRunner.dumpAsText();
+}
+
+function done()
+{
+    log("Done");
+    if (window.testRunner)
+        testRunner.notifyDone();
+}
+
+function log(message)
+{
+    document.getElementById("logger").innerHTML += message + "<br>";
+}
+
+function logCursor(cursor)
+{
+    log("Cursor direction is: " + cursor.direction);
+    log("Cursor source is: " + cursor.source.name);    
+    log("Cursor key is: " + cursor.key);    
+    log("Cursor primary key is: " + cursor.primaryKey);
+    log("Cursor value is: " + cursor.value);  
+}
+
+function setupRequest(request)
+{
+    request.onsuccess = function() {
+        log("Success opening or iterating cursor");
+        logCursor(request.result);  
+        if (request.result.key != undefined)
+            request.result.continue();
+        else
+            startNextCursor();
+    }
+    request.onerror = function(e) {
+        log("Unexpected error opening or iterating cursor");
+        logCursor(request.result);
+        done();
+    } 
+}
+
+var index;
+
+function testCursorDirection(direction, range)
+{
+    var request = index.openCursor(range, direction);
+    setupRequest(request);
+}
+
+var cursorCommands = [
+    "testCursorDirection('prevunique', IDBKeyRange.only('B'))",
+    "testCursorDirection('prev', IDBKeyRange.only('B'))",
+    "testCursorDirection('nextunique', IDBKeyRange.only('B'))",
+    "testCursorDirection('next', IDBKeyRange.only('B'))",
+    "testCursorDirection('prevunique', IDBKeyRange.lowerBound('C'))",
+    "testCursorDirection('prev', IDBKeyRange.lowerBound('C'))",
+    "testCursorDirection('nextunique', IDBKeyRange.lowerBound('C'))",
+    "testCursorDirection('next', IDBKeyRange.lowerBound('C'))",
+    "testCursorDirection('prevunique', IDBKeyRange.upperBound('B'))",
+    "testCursorDirection('prev', IDBKeyRange.upperBound('B'))",
+    "testCursorDirection('nextunique', IDBKeyRange.upperBound('B'))",
+    "testCursorDirection('next', IDBKeyRange.upperBound('B'))",
+    "testCursorDirection('prevunique', IDBKeyRange.bound('B', 'C'))",
+    "testCursorDirection('prev', IDBKeyRange.bound('B', 'C'))",
+    "testCursorDirection('nextunique', IDBKeyRange.bound('B', 'C'))",
+    "testCursorDirection('next', IDBKeyRange.bound('B', 'C'))",
+    "testCursorDirection('prevunique', IDBKeyRange.bound('B', 'D', true, true))",
+    "testCursorDirection('prev', IDBKeyRange.bound('B', 'D', true, true))",
+    "testCursorDirection('nextunique', IDBKeyRange.bound('B', 'D', true, true))",
+    "testCursorDirection('next', IDBKeyRange.bound('B', 'D', true, true))",
+];
+
+function startNextCursor()
+{
+    if (!cursorCommands.length) {
+        done();
+        return;
+    }
+
+    var command = cursorCommands.pop();
+    log ("");
+    log("Starting a new cursor: " + command);
+    eval(command);
+}
+    
+var createRequest = window.indexedDB.open("IndexCursor2Database", 1);
+createRequest.onupgradeneeded = function(event) {
+    log("Initial upgrade needed: Old version - " + event.oldVersion + " New version - " + event.newVersion);
+
+    var versionTransaction = createRequest.transaction;
+    var database = event.target.result;
+    var objectStore = database.createObjectStore("TestObjectStore");
+    index = objectStore.createIndex("TestIndex1", "bar");
+
+    objectStore.put({ bar: "A", baz: "a" }, 1);
+    objectStore.put({ bar: "A", baz: "a" }, 3);
+    objectStore.put({ bar: "A", baz: "a" }, 2);
+    objectStore.put({ bar: "B", baz: "b" }, 5);
+    objectStore.put({ bar: "B", baz: "b" }, 6);
+    objectStore.put({ bar: "B", baz: "b" }, 4);
+    objectStore.put({ bar: "C", baz: "c" }, 7);
+    objectStore.put({ bar: "C", baz: "c" }, 9);
+    objectStore.put({ bar: "C", baz: "c" }, 8);
+    objectStore.put({ bar: "D", baz: "d" }, 11);
+    objectStore.put({ bar: "D", baz: "d" }, 12);
+    objectStore.put({ bar: "D", baz: "d" }, 10);
+
+    var req = index.count();
+    req.onsuccess = function() {
+        log("TestIndex1 count is: " + req.result);
+    }
+
+    startNextCursor();
+    
+    versionTransaction.onabort = function(event) {
+        log("Initial upgrade versionchange transaction unexpected aborted");
+        done();
+    }
+
+    versionTransaction.oncomplete = function(event) {
+        log("Initial upgrade versionchange transaction complete");
+        done();
+    }
+
+    versionTransaction.onerror = function(event) {
+        log("Initial upgrade versionchange transaction unexpected error" + event);
+        done();
+    }
+}
+
+</script>
diff --git a/LayoutTests/storage/indexeddb/modern/index-cursor-3-expected.txt b/LayoutTests/storage/indexeddb/modern/index-cursor-3-expected.txt
new file mode 100644 (file)
index 0000000..4895718
--- /dev/null
@@ -0,0 +1,319 @@
+This tests that index cursors properly handle changing indexes.
+Initial upgrade needed: Old version - 0 New version - 1
+
+Starting a new cursor: testCursorDirection(index, 'next')
+
+TestIndex1 count is: 18
+
+Success opening or iterating cursor
+Cursor direction is: next
+Cursor source is: TestIndex1
+Cursor key is: A
+Cursor primary key is: 1
+Cursor value is: [object Object]
+Success opening or iterating cursor
+Cursor direction is: next
+Cursor source is: TestIndex1
+Cursor key is: A
+Cursor primary key is: 2
+Cursor value is: [object Object]
+Deleted key 2 from object store
+Deleted key 3 from object store
+Success opening or iterating cursor
+Cursor direction is: next
+Cursor source is: TestIndex1
+Cursor key is: B
+Cursor primary key is: 4
+Cursor value is: [object Object]
+Deleted key 4 from object store
+Deleted key 5 from object store
+Success opening or iterating cursor
+Cursor direction is: next
+Cursor source is: TestIndex1
+Cursor key is: C
+Cursor primary key is: 6
+Cursor value is: [object Object]
+Deleted key 6 from object store
+Deleted key 7 from object store
+Success opening or iterating cursor
+Cursor direction is: next
+Cursor source is: TestIndex1
+Cursor key is: D
+Cursor primary key is: 8
+Cursor value is: [object Object]
+Deleted key 8 from object store
+Deleted key 9 from object store
+Success opening or iterating cursor
+Cursor direction is: next
+Cursor source is: TestIndex1
+Cursor key is: E
+Cursor primary key is: 10
+Cursor value is: [object Object]
+Deleted key 10 from object store
+Deleted key 11 from object store
+Success opening or iterating cursor
+Cursor direction is: next
+Cursor source is: TestIndex1
+Cursor key is: F
+Cursor primary key is: 12
+Cursor value is: [object Object]
+Deleted key 12 from object store
+Deleted key 13 from object store
+Success opening or iterating cursor
+Cursor direction is: next
+Cursor source is: TestIndex1
+Cursor key is: G
+Cursor primary key is: 14
+Cursor value is: [object Object]
+Deleted key 14 from object store
+Deleted key 15 from object store
+Success opening or iterating cursor
+Cursor direction is: next
+Cursor source is: TestIndex1
+Cursor key is: H
+Cursor primary key is: 16
+Cursor value is: [object Object]
+Deleted key 16 from object store
+Deleted key 17 from object store
+Success opening or iterating cursor
+Cursor direction is: next
+Cursor source is: TestIndex1
+Cursor key is: I
+Cursor primary key is: 18
+Cursor value is: [object Object]
+Deleted key 18 from object store
+Success opening or iterating cursor
+Cursor direction is: next
+Cursor source is: TestIndex1
+Cursor key is: undefined
+Cursor primary key is: undefined
+Cursor value is: undefined
+
+Starting a new cursor: testCursorDirection(index, 'prev')
+
+TestIndex1 count is: 18
+
+Success opening or iterating cursor
+Cursor direction is: prev
+Cursor source is: TestIndex1
+Cursor key is: I
+Cursor primary key is: 18
+Cursor value is: [object Object]
+Success opening or iterating cursor
+Cursor direction is: prev
+Cursor source is: TestIndex1
+Cursor key is: I
+Cursor primary key is: 17
+Cursor value is: [object Object]
+Deleted key 17 from object store
+Deleted key 16 from object store
+Success opening or iterating cursor
+Cursor direction is: prev
+Cursor source is: TestIndex1
+Cursor key is: H
+Cursor primary key is: 15
+Cursor value is: [object Object]
+Deleted key 15 from object store
+Deleted key 14 from object store
+Success opening or iterating cursor
+Cursor direction is: prev
+Cursor source is: TestIndex1
+Cursor key is: G
+Cursor primary key is: 13
+Cursor value is: [object Object]
+Deleted key 13 from object store
+Deleted key 12 from object store
+Success opening or iterating cursor
+Cursor direction is: prev
+Cursor source is: TestIndex1
+Cursor key is: F
+Cursor primary key is: 11
+Cursor value is: [object Object]
+Deleted key 11 from object store
+Deleted key 10 from object store
+Success opening or iterating cursor
+Cursor direction is: prev
+Cursor source is: TestIndex1
+Cursor key is: E
+Cursor primary key is: 9
+Cursor value is: [object Object]
+Deleted key 9 from object store
+Deleted key 8 from object store
+Success opening or iterating cursor
+Cursor direction is: prev
+Cursor source is: TestIndex1
+Cursor key is: D
+Cursor primary key is: 7
+Cursor value is: [object Object]
+Deleted key 7 from object store
+Deleted key 6 from object store
+Success opening or iterating cursor
+Cursor direction is: prev
+Cursor source is: TestIndex1
+Cursor key is: C
+Cursor primary key is: 5
+Cursor value is: [object Object]
+Deleted key 5 from object store
+Deleted key 4 from object store
+Success opening or iterating cursor
+Cursor direction is: prev
+Cursor source is: TestIndex1
+Cursor key is: B
+Cursor primary key is: 3
+Cursor value is: [object Object]
+Deleted key 3 from object store
+Deleted key 2 from object store
+Success opening or iterating cursor
+Cursor direction is: prev
+Cursor source is: TestIndex1
+Cursor key is: A
+Cursor primary key is: 1
+Cursor value is: [object Object]
+Deleted key 1 from object store
+Success opening or iterating cursor
+Cursor direction is: prev
+Cursor source is: TestIndex1
+Cursor key is: undefined
+Cursor primary key is: undefined
+Cursor value is: undefined
+
+Starting a new cursor: testCursorDirection(index, 'nextunique')
+
+TestIndex1 count is: 18
+
+Success opening or iterating cursor
+Cursor direction is: nextunique
+Cursor source is: TestIndex1
+Cursor key is: A
+Cursor primary key is: 1
+Cursor value is: [object Object]
+Success opening or iterating cursor
+Cursor direction is: nextunique
+Cursor source is: TestIndex1
+Cursor key is: B
+Cursor primary key is: 3
+Cursor value is: [object Object]
+Deleted key 3 from object store
+Deleted key 4 from object store
+Deleted key 5 from object store
+Success opening or iterating cursor
+Cursor direction is: nextunique
+Cursor source is: TestIndex1
+Cursor key is: C
+Cursor primary key is: 6
+Cursor value is: [object Object]
+Deleted key 6 from object store
+Deleted key 7 from object store
+Deleted key 8 from object store
+Success opening or iterating cursor
+Cursor direction is: nextunique
+Cursor source is: TestIndex1
+Cursor key is: E
+Cursor primary key is: 9
+Cursor value is: [object Object]
+Deleted key 9 from object store
+Deleted key 10 from object store
+Deleted key 11 from object store
+Success opening or iterating cursor
+Cursor direction is: nextunique
+Cursor source is: TestIndex1
+Cursor key is: F
+Cursor primary key is: 12
+Cursor value is: [object Object]
+Deleted key 12 from object store
+Deleted key 13 from object store
+Deleted key 14 from object store
+Success opening or iterating cursor
+Cursor direction is: nextunique
+Cursor source is: TestIndex1
+Cursor key is: H
+Cursor primary key is: 15
+Cursor value is: [object Object]
+Deleted key 15 from object store
+Deleted key 16 from object store
+Deleted key 17 from object store
+Success opening or iterating cursor
+Cursor direction is: nextunique
+Cursor source is: TestIndex1
+Cursor key is: I
+Cursor primary key is: 18
+Cursor value is: [object Object]
+Deleted key 18 from object store
+Deleted key 1 from object store
+Success opening or iterating cursor
+Cursor direction is: nextunique
+Cursor source is: TestIndex1
+Cursor key is: undefined
+Cursor primary key is: undefined
+Cursor value is: undefined
+
+Starting a new cursor: testCursorDirection(index, 'prevunique')
+
+TestIndex1 count is: 18
+
+Success opening or iterating cursor
+Cursor direction is: prevunique
+Cursor source is: TestIndex1
+Cursor key is: I
+Cursor primary key is: 18
+Cursor value is: [object Object]
+Success opening or iterating cursor
+Cursor direction is: prevunique
+Cursor source is: TestIndex1
+Cursor key is: H
+Cursor primary key is: 16
+Cursor value is: [object Object]
+Deleted key 16 from object store
+Deleted key 15 from object store
+Deleted key 14 from object store
+Success opening or iterating cursor
+Cursor direction is: prevunique
+Cursor source is: TestIndex1
+Cursor key is: G
+Cursor primary key is: 13
+Cursor value is: [object Object]
+Deleted key 13 from object store
+Deleted key 12 from object store
+Deleted key 11 from object store
+Success opening or iterating cursor
+Cursor direction is: prevunique
+Cursor source is: TestIndex1
+Cursor key is: E
+Cursor primary key is: 10
+Cursor value is: [object Object]
+Deleted key 10 from object store
+Deleted key 9 from object store
+Deleted key 8 from object store
+Success opening or iterating cursor
+Cursor direction is: prevunique
+Cursor source is: TestIndex1
+Cursor key is: D
+Cursor primary key is: 7
+Cursor value is: [object Object]
+Deleted key 7 from object store
+Deleted key 6 from object store
+Deleted key 5 from object store
+Success opening or iterating cursor
+Cursor direction is: prevunique
+Cursor source is: TestIndex1
+Cursor key is: B
+Cursor primary key is: 4
+Cursor value is: [object Object]
+Deleted key 4 from object store
+Deleted key 3 from object store
+Deleted key 2 from object store
+Success opening or iterating cursor
+Cursor direction is: prevunique
+Cursor source is: TestIndex1
+Cursor key is: A
+Cursor primary key is: 1
+Cursor value is: [object Object]
+Deleted key 1 from object store
+Success opening or iterating cursor
+Cursor direction is: prevunique
+Cursor source is: TestIndex1
+Cursor key is: undefined
+Cursor primary key is: undefined
+Cursor value is: undefined
+Done
+
diff --git a/LayoutTests/storage/indexeddb/modern/index-cursor-3.html b/LayoutTests/storage/indexeddb/modern/index-cursor-3.html
new file mode 100644 (file)
index 0000000..59d6300
--- /dev/null
@@ -0,0 +1,176 @@
+This tests that index cursors properly handle changing indexes.<br>
+<div id="logger"></div>
+<script>
+
+if (window.testRunner) {
+    testRunner.waitUntilDone();
+    testRunner.dumpAsText();
+}
+
+function done()
+{
+    log("Done");
+    if (window.testRunner)
+        testRunner.notifyDone();
+}
+
+function log(message)
+{
+    document.getElementById("logger").innerHTML += message + "<br>";
+}
+
+var index;
+var objectStore;
+
+function logCursor(cursor)
+{
+    log("Cursor direction is: " + cursor.direction);
+    log("Cursor source is: " + cursor.source.name);    
+    log("Cursor key is: " + cursor.key);    
+    log("Cursor primary key is: " + cursor.primaryKey);
+    log("Cursor value is: " + cursor.value);  
+}
+
+function setupRequest(request)
+{
+    request.onsuccess = function() {
+        log("Success opening or iterating cursor");
+        logCursor(request.result);
+
+        if (request.iteratedOnce) {
+            var primaryKey = request.result.primaryKey;
+            if (primaryKey) {
+                objectStore.delete(primaryKey).onsuccess = function() {
+                    log("Deleted key " + primaryKey + " from object store");
+                }
+                var nextPrimaryKey = primaryKey;
+                if (request.result.direction.startsWith("next")) {
+                    nextPrimaryKey++;
+                    if (nextPrimaryKey > 18)
+                        nextPrimaryKey = 0;
+                } else
+                    nextPrimaryKey--;
+
+                if (nextPrimaryKey > 0) {
+                    objectStore.delete(nextPrimaryKey).onsuccess = function() {
+                        log("Deleted key " + nextPrimaryKey + " from object store");
+                    }
+                }
+                
+                // Delete an additional item for unique cursors to make sure they iterate deeper into the sets
+                // of primary keys and/or skip some index keys altogether.
+                if (request.result.direction.endsWith("unique")) {                
+                    var nextNextPrimaryKey = nextPrimaryKey;
+                    if (request.result.direction.startsWith("next")) {
+                        nextNextPrimaryKey++;
+                        if (nextNextPrimaryKey > 18)
+                            nextNextPrimaryKey = 0;
+                    } else
+                        nextNextPrimaryKey--;
+
+                    if (nextNextPrimaryKey > 0) {
+                        objectStore.delete(nextNextPrimaryKey).onsuccess = function() {
+                            log("Deleted key " + nextNextPrimaryKey + " from object store");
+                        }
+                    }
+                }
+            }
+        }
+     
+        request.iteratedOnce = true;
+
+        if (request.result.key != undefined)
+            request.result.continue();
+        else
+            startNextCursor();
+    }
+    request.onerror = function(e) {
+        log("Unexpected error opening or iterating cursor");
+        logCursor(request.result);
+        done();
+    } 
+}
+
+function testCursorDirection(index, direction)
+{
+    var range = IDBKeyRange.lowerBound(-Infinity);
+    var request = index.openCursor(range, direction);
+    setupRequest(request);
+}
+
+var cursorCommands = [
+    "testCursorDirection(index, 'prevunique')",
+    "testCursorDirection(index, 'nextunique')",
+    "testCursorDirection(index, 'prev')",
+    "testCursorDirection(index, 'next')",
+];
+
+function startNextCursor()
+{
+    if (!cursorCommands.length) {
+        done();
+        return;
+    }
+    
+    populateObjectStore();
+
+    var command = cursorCommands.pop();
+    log("<br>Starting a new cursor: " + command);
+    var req = index.count();
+    req.onsuccess = function() {
+        log("<br>TestIndex1 count is: " + req.result + "<br>");
+    }
+    
+    eval(command);
+}
+
+function populateObjectStore()
+{
+    objectStore.put({ bar: "A" }, 1);
+    objectStore.put({ bar: "A" }, 2);
+    objectStore.put({ bar: "B" }, 3);
+    objectStore.put({ bar: "B" }, 4);
+    objectStore.put({ bar: "C" }, 5);
+    objectStore.put({ bar: "C" }, 6);
+    objectStore.put({ bar: "D" }, 7);
+    objectStore.put({ bar: "D" }, 8);
+    objectStore.put({ bar: "E" }, 9);
+    objectStore.put({ bar: "E" }, 10);
+    objectStore.put({ bar: "F" }, 11);
+    objectStore.put({ bar: "F" }, 12);
+    objectStore.put({ bar: "G" }, 13);
+    objectStore.put({ bar: "G" }, 14);    
+    objectStore.put({ bar: "H" }, 15);
+    objectStore.put({ bar: "H" }, 16);  
+    objectStore.put({ bar: "I" }, 17);
+    objectStore.put({ bar: "I" }, 18);  
+}
+
+var createRequest = window.indexedDB.open("IndexCursor3Database", 1);
+createRequest.onupgradeneeded = function(event) {
+    log("Initial upgrade needed: Old version - " + event.oldVersion + " New version - " + event.newVersion);
+
+    var versionTransaction = createRequest.transaction;
+    var database = event.target.result;
+    objectStore = database.createObjectStore("TestObjectStore");
+    index = objectStore.createIndex("TestIndex1", "bar");
+    
+    startNextCursor();
+    
+    versionTransaction.onabort = function(event) {
+        log("Initial upgrade versionchange transaction unexpected aborted");
+        done();
+    }
+
+    versionTransaction.oncomplete = function(event) {
+        log("Initial upgrade versionchange transaction complete");
+        done();
+    }
+
+    versionTransaction.onerror = function(event) {
+        log("Initial upgrade versionchange transaction unexpected error" + event);
+        done();
+    }
+}
+
+</script>
index 36f9701..e7a96da 100644 (file)
@@ -909,6 +909,7 @@ set(WebCore_SOURCES
     Modules/indexeddb/server/MemoryCursor.cpp
     Modules/indexeddb/server/MemoryIDBBackingStore.cpp
     Modules/indexeddb/server/MemoryIndex.cpp
+    Modules/indexeddb/server/MemoryIndexCursor.cpp
     Modules/indexeddb/server/MemoryObjectStore.cpp
     Modules/indexeddb/server/MemoryObjectStoreCursor.cpp
     Modules/indexeddb/server/UniqueIDBDatabase.cpp
index 487903f..2bd693d 100644 (file)
@@ -1,3 +1,93 @@
+2015-11-18  Brady Eidson  <beidson@apple.com>
+
+        Modern IDB:Make in-memory Index cursors work.
+        https://bugs.webkit.org/show_bug.cgi?id=151278
+
+        Reviewed by Alex Christensen.
+
+        Tests: storage/indexeddb/modern/index-cursor-1.html
+               storage/indexeddb/modern/index-cursor-2.html
+               storage/indexeddb/modern/index-cursor-3.html
+
+        * CMakeLists.txt:
+        * WebCore.xcodeproj/project.pbxproj:
+
+        * Modules/indexeddb/client/IDBIndexImpl.cpp:
+        (WebCore::IDBClient::IDBIndex::openCursor):
+        (WebCore::IDBClient::IDBIndex::openKeyCursor):
+        
+        * Modules/indexeddb/server/IndexValueEntry.cpp:
+        (WebCore::IDBServer::IndexValueEntry::removeKey):
+        (WebCore::IDBServer::IndexValueEntry::Iterator::Iterator):
+        (WebCore::IDBServer::IndexValueEntry::Iterator::key):
+        (WebCore::IDBServer::IndexValueEntry::Iterator::isValid):
+        (WebCore::IDBServer::IndexValueEntry::Iterator::invalidate):
+        (WebCore::IDBServer::IndexValueEntry::Iterator::operator++):
+        (WebCore::IDBServer::IndexValueEntry::begin):
+        (WebCore::IDBServer::IndexValueEntry::reverseBegin):
+        (WebCore::IDBServer::IndexValueEntry::find):
+        (WebCore::IDBServer::IndexValueEntry::reverseFind):
+        * Modules/indexeddb/server/IndexValueEntry.h:
+        (WebCore::IDBServer::IndexValueEntry::Iterator::Iterator):
+        (WebCore::IDBServer::IndexValueEntry::unique):
+        
+        * Modules/indexeddb/server/IndexValueStore.cpp:
+        (WebCore::IDBServer::IndexValueStore::removeEntriesWithValueKey):
+        (WebCore::IDBServer::IndexValueStore::lowestKeyWithRecordInRange):
+        (WebCore::IDBServer::IndexValueStore::lowestIteratorInRange):
+        (WebCore::IDBServer::IndexValueStore::highestReverseIteratorInRange):
+        (WebCore::IDBServer::IndexValueStore::find):
+        (WebCore::IDBServer::IndexValueStore::reverseFind):
+        (WebCore::IDBServer::IndexValueStore::Iterator::Iterator):
+        (WebCore::IDBServer::IndexValueStore::Iterator::nextIndexEntry):
+        (WebCore::IDBServer::IndexValueStore::Iterator::operator++):
+        (WebCore::IDBServer::IndexValueStore::Iterator::invalidate):
+        (WebCore::IDBServer::IndexValueStore::Iterator::isValid):
+        (WebCore::IDBServer::IndexValueStore::Iterator::key):
+        (WebCore::IDBServer::IndexValueStore::Iterator::primaryKey):
+        * Modules/indexeddb/server/IndexValueStore.h:
+        (WebCore::IDBServer::IndexValueStore::Iterator::Iterator):
+        
+        * Modules/indexeddb/server/MemoryIDBBackingStore.cpp:
+        (WebCore::IDBServer::MemoryIDBBackingStore::openCursor):
+        
+        * Modules/indexeddb/server/MemoryIndex.cpp:
+        (WebCore::IDBServer::MemoryIndex::cursorDidBecomeClean):
+        (WebCore::IDBServer::MemoryIndex::cursorDidBecomeDirty):
+        (WebCore::IDBServer::MemoryIndex::objectStoreCleared):
+        (WebCore::IDBServer::MemoryIndex::notifyCursorsOfValueChange):
+        (WebCore::IDBServer::MemoryIndex::notifyCursorsOfAllRecordsChanged):
+        (WebCore::IDBServer::MemoryIndex::putIndexKey):
+        (WebCore::IDBServer::MemoryIndex::removeRecord):
+        (WebCore::IDBServer::MemoryIndex::removeEntriesWithValueKey):
+        (WebCore::IDBServer::MemoryIndex::maybeOpenCursor):
+        * Modules/indexeddb/server/MemoryIndex.h:
+        (WebCore::IDBServer::MemoryIndex::valueStore):
+        (WebCore::IDBServer::MemoryIndex::objectStore):
+        
+        * Modules/indexeddb/server/MemoryIndexCursor.cpp: Added.
+        (WebCore::IDBServer::MemoryIndexCursor::MemoryIndexCursor):
+        (WebCore::IDBServer::MemoryIndexCursor::~MemoryIndexCursor):
+        (WebCore::IDBServer::MemoryIndexCursor::currentData):
+        (WebCore::IDBServer::MemoryIndexCursor::iterate):
+        (WebCore::IDBServer::MemoryIndexCursor::indexRecordsAllChanged):
+        (WebCore::IDBServer::MemoryIndexCursor::indexValueChanged):
+        * Modules/indexeddb/server/MemoryIndexCursor.h:
+        
+        * Modules/indexeddb/server/MemoryObjectStore.cpp:
+        (WebCore::IDBServer::MemoryObjectStore::indexForIdentifier):
+        (WebCore::IDBServer::MemoryObjectStore::valueForKey):
+        * Modules/indexeddb/server/MemoryObjectStore.h:
+        
+        * Modules/indexeddb/shared/IDBCursorInfo.cpp:
+        (WebCore::IDBCursorInfo::objectStoreCursor):
+        (WebCore::IDBCursorInfo::indexCursor):
+        (WebCore::IDBCursorInfo::IDBCursorInfo):
+        (WebCore::IDBCursorInfo::isDirectionNoDuplicate):
+        (WebCore::IDBCursorInfo::isolatedCopy):
+        * Modules/indexeddb/shared/IDBCursorInfo.h:
+        (WebCore::IDBCursorInfo::objectStoreIdentifier):
+
 2015-11-18  Antti Koivisto  <antti@apple.com>
 
         Assertion failure in RenderTreePosition::computeNextSibling
index f092580..24ced82 100644 (file)
@@ -104,7 +104,13 @@ RefPtr<WebCore::IDBRequest> IDBIndex::openCursor(ScriptExecutionContext* context
     if (ec)
         return nullptr;
 
-    auto info = IDBCursorInfo::indexCursor(m_objectStore->modernTransaction(), m_info.identifier(), range, direction, IndexedDB::CursorType::KeyAndValue);
+    IDBKeyRangeData rangeData = range;
+    if (rangeData.lowerKey.isNull())
+        rangeData.lowerKey = IDBKeyData::minimum();
+    if (rangeData.upperKey.isNull())
+        rangeData.upperKey = IDBKeyData::maximum();
+
+    auto info = IDBCursorInfo::indexCursor(m_objectStore->modernTransaction(), m_objectStore->info().identifier(), m_info.identifier(), rangeData, direction, IndexedDB::CursorType::KeyAndValue);
     Ref<IDBRequest> request = m_objectStore->modernTransaction().requestOpenCursor(*context, *this, info);
     return WTF::move(request);
 }
@@ -202,7 +208,7 @@ RefPtr<WebCore::IDBRequest> IDBIndex::openKeyCursor(ScriptExecutionContext* cont
     if (ec)
         return nullptr;
 
-    auto info = IDBCursorInfo::indexCursor(m_objectStore->modernTransaction(), m_info.identifier(), range, direction, IndexedDB::CursorType::KeyOnly);
+    auto info = IDBCursorInfo::indexCursor(m_objectStore->modernTransaction(), m_objectStore->info().identifier(), m_info.identifier(), range, direction, IndexedDB::CursorType::KeyOnly);
     Ref<IDBRequest> request = m_objectStore->modernTransaction().requestOpenCursor(*context, *this, info);
     return WTF::move(request);
 }
index 1f084e4..e1c91b0 100644 (file)
@@ -61,14 +61,17 @@ void IndexValueEntry::addKey(const IDBKeyData& key)
 
 bool IndexValueEntry::removeKey(const IDBKeyData& key)
 {
-    if (m_unique && m_key && *m_key == key) {
-        delete m_key;
-        m_key = nullptr;
-        return true;
+    if (m_unique) {
+        if (m_key && *m_key == key) {
+            delete m_key;
+            m_key = nullptr;
+            return true;
+        }
+
+        return false;
     }
 
-    m_orderedKeys->erase(key);
-    return m_orderedKeys->empty();
+    return m_orderedKeys->erase(key);
 }
 
 const IDBKeyData* IndexValueEntry::getLowest() const
@@ -90,6 +93,131 @@ uint64_t IndexValueEntry::getCount() const
     return m_orderedKeys->size();
 }
 
+IndexValueEntry::Iterator::Iterator(IndexValueEntry& entry)
+    : m_entry(&entry)
+{
+    ASSERT(m_entry->m_key);
+}
+
+IndexValueEntry::Iterator::Iterator(IndexValueEntry& entry, std::set<IDBKeyData>::iterator iterator)
+    : m_entry(&entry)
+    , m_forwardIterator(iterator)
+{
+}
+
+IndexValueEntry::Iterator::Iterator(IndexValueEntry& entry, std::set<IDBKeyData>::reverse_iterator iterator)
+    : m_entry(&entry)
+    , m_forward(false)
+    , m_reverseIterator(iterator)
+{
+}
+
+const IDBKeyData& IndexValueEntry::Iterator::key() const
+{
+    ASSERT(isValid());
+    if (m_entry->unique()) {
+        ASSERT(m_entry->m_key);
+        return *m_entry->m_key;
+    }
+
+    return m_forward ? *m_forwardIterator : *m_reverseIterator;
+}
+
+bool IndexValueEntry::Iterator::isValid() const
+{
+#ifndef NDEBUG
+    if (m_entry) {
+        if (m_entry->m_unique)
+            ASSERT(m_entry->m_key);
+        else
+            ASSERT(m_entry->m_orderedKeys);
+    }
+#endif
+
+    return m_entry;
+}
+
+void IndexValueEntry::Iterator::invalidate()
+{
+    m_entry = nullptr;
+}
+
+IndexValueEntry::Iterator& IndexValueEntry::Iterator::operator++()
+{
+    if (!isValid())
+        return *this;
+
+    if (m_entry->m_unique) {
+        invalidate();
+        return *this;
+    }
+
+    if (m_forward) {
+        ++m_forwardIterator;
+        if (m_forwardIterator == m_entry->m_orderedKeys->end())
+            invalidate();
+    } else {
+        ++m_reverseIterator;
+        if (m_reverseIterator == m_entry->m_orderedKeys->rend())
+            invalidate();
+    }
+
+    return *this;
+}
+
+IndexValueEntry::Iterator IndexValueEntry::begin()
+{
+    if (m_unique) {
+        ASSERT(m_key);
+        return { *this };
+    }
+
+    ASSERT(m_orderedKeys);
+    return { *this, m_orderedKeys->begin() };
+}
+
+IndexValueEntry::Iterator IndexValueEntry::reverseBegin()
+{
+    if (m_unique) {
+        ASSERT(m_key);
+        return { *this };
+    }
+
+    ASSERT(m_orderedKeys);
+    return { *this, m_orderedKeys->rbegin() };
+}
+
+IndexValueEntry::Iterator IndexValueEntry::find(const IDBKeyData& key)
+{
+    if (m_unique) {
+        ASSERT(m_key);
+        return *m_key == key ? IndexValueEntry::Iterator(*this) : IndexValueEntry::Iterator();
+    }
+
+    ASSERT(m_orderedKeys);
+    auto iterator = m_orderedKeys->lower_bound(key);
+    if (iterator == m_orderedKeys->end())
+        return { };
+
+    return { *this, iterator };
+}
+
+IndexValueEntry::Iterator IndexValueEntry::reverseFind(const IDBKeyData& key)
+{
+    if (m_unique) {
+        ASSERT(m_key);
+        return *m_key == key ? IndexValueEntry::Iterator(*this) : IndexValueEntry::Iterator();
+    }
+
+    ASSERT(m_orderedKeys);
+    auto iterator = std::set<IDBKeyData>::reverse_iterator(m_orderedKeys->upper_bound(key));
+    if (iterator == m_orderedKeys->rend())
+        return { };
+
+    return { *this, iterator };
+}
+
+
 } // namespace IDBServer
 } // namespace WebCore
 
index eb9bac0..1afd75f 100644 (file)
@@ -32,6 +32,9 @@
 #include <set>
 
 namespace WebCore {
+
+class ThreadSafeDataBuffer;
+
 namespace IDBServer {
 
 class IndexValueEntry {
@@ -41,13 +44,48 @@ public:
 
     void addKey(const IDBKeyData&);
 
-    // Returns true if the IndexValueEntry is empty after removing the key;
+    // Returns true if a key was actually removed.
     bool removeKey(const IDBKeyData&);
 
     const IDBKeyData* getLowest() const;
 
     uint64_t getCount() const;
 
+    class Iterator {
+    public:
+        Iterator()
+        {
+        }
+
+        Iterator(IndexValueEntry&);
+        Iterator(IndexValueEntry&, std::set<IDBKeyData>::iterator);
+        Iterator(IndexValueEntry&, std::set<IDBKeyData>::reverse_iterator);
+
+        bool isValid() const;
+        void invalidate();
+
+        const IDBKeyData& key() const;
+        const ThreadSafeDataBuffer& value() const;
+
+        Iterator& operator++();
+
+    private:
+        IndexValueEntry* m_entry { nullptr };
+        bool m_forward { true };
+        std::set<IDBKeyData>::iterator m_forwardIterator;
+        std::set<IDBKeyData>::reverse_iterator m_reverseIterator;
+    };
+
+    Iterator begin();
+    Iterator reverseBegin();
+
+    // Finds the key, or the next higher record after the key.
+    Iterator find(const IDBKeyData&);
+    // Finds the key, or the next lowest record before the key.
+    Iterator reverseFind(const IDBKeyData&);
+
+    bool unique() const { return m_unique; }
+
 private:
     union {
         std::set<IDBKeyData>* m_orderedKeys;
index 7579724..58245ac 100644 (file)
@@ -30,6 +30,8 @@
 
 #include "IDBError.h"
 #include "IDBKeyRangeData.h"
+#include "Logging.h"
+#include "MemoryIndex.h"
 
 namespace WebCore {
 namespace IDBServer {
@@ -94,12 +96,14 @@ void IndexValueStore::removeRecord(const IDBKeyData& indexKey, const IDBKeyData&
         m_records.remove(iterator);
 }
 
-void IndexValueStore::removeEntriesWithValueKey(const IDBKeyData& valueKey)
+void IndexValueStore::removeEntriesWithValueKey(MemoryIndex& index, const IDBKeyData& valueKey)
 {
     HashSet<IDBKeyData*> entryKeysToRemove;
 
     for (auto& entry : m_records) {
         if (entry.value->removeKey(valueKey))
+            index.notifyCursorsOfValueChange(entry.key, valueKey);
+        if (!entry.value->getCount())
             entryKeysToRemove.add(&entry.key);
     }
 
@@ -111,28 +115,262 @@ void IndexValueStore::removeEntriesWithValueKey(const IDBKeyData& valueKey)
 
 IDBKeyData IndexValueStore::lowestKeyWithRecordInRange(const IDBKeyRangeData& range) const
 {
+    LOG(IndexedDB, "IndexValueStore::lowestKeyWithRecordInRange - %s", range.loggingString().utf8().data());
+
     if (range.isExactlyOneKey())
         return m_records.contains(range.lowerKey) ? range.lowerKey : IDBKeyData();
 
+    auto iterator = lowestIteratorInRange(range);
+    if (iterator == m_orderedKeys.end())
+        return { };
+
+    return *iterator;
+}
+
+std::set<IDBKeyData>::iterator IndexValueStore::lowestIteratorInRange(const IDBKeyRangeData& range) const
+{
     auto lowestInRange = m_orderedKeys.lower_bound(range.lowerKey);
 
     if (lowestInRange == m_orderedKeys.end())
-        return { };
+        return lowestInRange;
 
-    if (range.lowerOpen && *lowestInRange == range.lowerKey)
+    if (range.lowerOpen && *lowestInRange == range.lowerKey) {
         ++lowestInRange;
 
-    if (lowestInRange == m_orderedKeys.end())
-        return { };
+        if (lowestInRange == m_orderedKeys.end())
+            return lowestInRange;
+    }
 
     if (!range.upperKey.isNull()) {
         if (lowestInRange->compare(range.upperKey) > 0)
-            return { };
+            return m_orderedKeys.end();
         if (range.upperOpen && *lowestInRange == range.upperKey)
-            return { };
+            return m_orderedKeys.end();
+    }
+
+    return lowestInRange;
+}
+
+std::set<IDBKeyData>::reverse_iterator IndexValueStore::highestReverseIteratorInRange(const IDBKeyRangeData& range) const
+{
+    auto highestInRange = std::set<IDBKeyData>::reverse_iterator(m_orderedKeys.upper_bound(range.upperKey));
+
+    if (highestInRange == m_orderedKeys.rend())
+        return highestInRange;
+
+    if (range.upperOpen && *highestInRange == range.upperKey) {
+        ++highestInRange;
+
+        if (highestInRange == m_orderedKeys.rend())
+            return highestInRange;
     }
 
-    return *lowestInRange;
+    if (!range.lowerKey.isNull()) {
+        if (highestInRange->compare(range.lowerKey) < 0)
+            return m_orderedKeys.rend();
+        if (range.lowerOpen && *highestInRange == range.lowerKey)
+            return m_orderedKeys.rend();
+    }
+
+    return highestInRange;
+}
+
+IndexValueStore::Iterator IndexValueStore::find(const IDBKeyData& key, bool open)
+{
+    IDBKeyRangeData range;
+    if (!key.isNull())
+        range.lowerKey = key;
+    else
+        range.lowerKey = IDBKeyData::minimum();
+    range.lowerOpen = open;
+
+    auto iterator = lowestIteratorInRange(range);
+    if (iterator == m_orderedKeys.end())
+        return { };
+
+    auto record = m_records.get(*iterator);
+    ASSERT(record);
+
+    auto primaryIterator = record->begin();
+    ASSERT(primaryIterator.isValid());
+    return { *this, iterator, primaryIterator };
+}
+
+IndexValueStore::Iterator IndexValueStore::find(const IDBKeyData& key, const IDBKeyData& primaryKey)
+{
+    ASSERT(!key.isNull());
+    ASSERT(!primaryKey.isNull());
+
+    IDBKeyRangeData range;
+    range.lowerKey = key;
+    range.lowerOpen = false;
+
+    auto iterator = lowestIteratorInRange(range);
+    if (iterator == m_orderedKeys.end())
+        return { };
+
+    auto record = m_records.get(*iterator);
+    ASSERT(record);
+
+    auto primaryIterator = record->find(primaryKey);
+    if (primaryIterator.isValid())
+        return { *this, iterator, primaryIterator };
+
+    // If we didn't find a primary key iterator in this entry,
+    // we need to move on to start of the next record.
+    iterator++;
+    if (iterator == m_orderedKeys.end())
+        return { };
+
+    record = m_records.get(*iterator);
+    ASSERT(record);
+
+    primaryIterator = record->begin();
+    ASSERT(primaryIterator.isValid());
+
+    return { *this, iterator, primaryIterator };
+}
+
+IndexValueStore::Iterator IndexValueStore::reverseFind(const IDBKeyData& key, bool open)
+{
+    IDBKeyRangeData range;
+    if (!key.isNull())
+        range.upperKey = key;
+    else
+        range.upperKey = IDBKeyData::maximum();
+    range.upperOpen = open;
+
+    auto iterator = highestReverseIteratorInRange(range);
+    if (iterator == m_orderedKeys.rend())
+        return { };
+
+    auto record = m_records.get(*iterator);
+    ASSERT(record);
+
+    auto primaryIterator = record->reverseBegin();
+    ASSERT(primaryIterator.isValid());
+    return { *this, iterator, primaryIterator };
+}
+
+IndexValueStore::Iterator IndexValueStore::reverseFind(const IDBKeyData& key, const IDBKeyData& primaryKey)
+{
+    ASSERT(!key.isNull());
+    ASSERT(!primaryKey.isNull());
+
+    IDBKeyRangeData range;
+    range.upperKey = key;
+    range.upperOpen = false;
+
+    auto iterator = highestReverseIteratorInRange(range);
+    if (iterator == m_orderedKeys.rend())
+        return { };
+
+    auto record = m_records.get(*iterator);
+    ASSERT(record);
+
+    auto primaryIterator = record->reverseFind(primaryKey);
+    if (primaryIterator.isValid())
+        return { *this, iterator, primaryIterator };
+
+    // If we didn't find a primary key iterator in this entry,
+    // we need to move on to start of the next record.
+    iterator++;
+    if (iterator == m_orderedKeys.rend())
+        return { };
+
+    record = m_records.get(*iterator);
+    ASSERT(record);
+
+    primaryIterator = record->reverseBegin();
+    ASSERT(primaryIterator.isValid());
+
+    return { *this, iterator, primaryIterator };
+}
+
+
+IndexValueStore::Iterator::Iterator(IndexValueStore& store, std::set<IDBKeyData>::iterator iterator, IndexValueEntry::Iterator primaryIterator)
+    : m_store(&store)
+    , m_forwardIterator(iterator)
+    , m_primaryKeyIterator(primaryIterator)
+{
+}
+
+IndexValueStore::Iterator::Iterator(IndexValueStore& store, std::set<IDBKeyData>::reverse_iterator iterator, IndexValueEntry::Iterator primaryIterator)
+    : m_store(&store)
+    , m_forward(false)
+    , m_reverseIterator(iterator)
+    , m_primaryKeyIterator(primaryIterator)
+{
+}
+
+IndexValueStore::Iterator& IndexValueStore::Iterator::nextIndexEntry()
+{
+    if (!m_store)
+        return *this;
+
+    if (m_forward) {
+        ++m_forwardIterator;
+        if (m_forwardIterator == m_store->m_orderedKeys.end()) {
+            invalidate();
+            return *this;
+        }
+
+        auto* entry = m_store->m_records.get(*m_forwardIterator);
+        ASSERT(entry);
+
+        m_primaryKeyIterator = entry->begin();
+        ASSERT(m_primaryKeyIterator.isValid());
+    } else {
+        ++m_reverseIterator;
+        if (m_reverseIterator == m_store->m_orderedKeys.rend()) {
+            invalidate();
+            return *this;
+        }
+
+        auto* entry = m_store->m_records.get(*m_reverseIterator);
+        ASSERT(entry);
+
+        m_primaryKeyIterator = entry->reverseBegin();
+        ASSERT(m_primaryKeyIterator.isValid());
+    }
+    
+    return *this;
+}
+
+IndexValueStore::Iterator& IndexValueStore::Iterator::operator++()
+{
+    if (!isValid())
+        return *this;
+
+    ++m_primaryKeyIterator;
+    if (m_primaryKeyIterator.isValid())
+        return *this;
+
+    // Ran out of primary key records, so move the main index iterator.
+    return nextIndexEntry();
+}
+
+void IndexValueStore::Iterator::invalidate()
+{
+    m_store = nullptr;
+    m_primaryKeyIterator.invalidate();
+}
+
+bool IndexValueStore::Iterator::isValid()
+{
+    return m_store && m_primaryKeyIterator.isValid();
+}
+
+const IDBKeyData& IndexValueStore::Iterator::key()
+{
+    ASSERT(isValid());
+    return m_forward ? *m_forwardIterator : *m_reverseIterator;
+}
+
+const IDBKeyData& IndexValueStore::Iterator::primaryKey()
+{
+    ASSERT(isValid());
+    return m_primaryKeyIterator.key();
 }
 
 } // namespace IDBServer
index 99144eb..fccdf83 100644 (file)
@@ -41,6 +41,8 @@ struct IDBKeyRangeData;
 
 namespace IDBServer {
 
+class MemoryIndex;
+
 typedef HashMap<IDBKeyData, std::unique_ptr<IndexValueEntry>, IDBKeyDataHash, IDBKeyDataHashTraits> IndexKeyValueMap;
 
 class IndexValueStore {
@@ -55,9 +57,49 @@ public:
     IDBError addRecord(const IDBKeyData& indexKey, const IDBKeyData& valueKey);
     void removeRecord(const IDBKeyData& indexKey, const IDBKeyData& valueKey);
 
-    void removeEntriesWithValueKey(const IDBKeyData& valueKey);
+    void removeEntriesWithValueKey(MemoryIndex&, const IDBKeyData& valueKey);
+
+    class Iterator {
+        friend class IndexValueStore;
+    public:
+        Iterator()
+        {
+        }
+
+        Iterator(IndexValueStore&, std::set<IDBKeyData>::iterator, IndexValueEntry::Iterator);
+        Iterator(IndexValueStore&, std::set<IDBKeyData>::reverse_iterator, IndexValueEntry::Iterator);
+
+        void invalidate();
+        bool isValid();
+
+        const IDBKeyData& key();
+        const IDBKeyData& primaryKey();
+        const ThreadSafeDataBuffer& value();
+
+        Iterator& operator++();
+        Iterator& nextIndexEntry();
+
+    private:
+        IndexValueStore* m_store { nullptr };
+        bool m_forward { true };
+        std::set<IDBKeyData>::iterator m_forwardIterator;
+        std::set<IDBKeyData>::reverse_iterator m_reverseIterator;
+
+        IndexValueEntry::Iterator m_primaryKeyIterator;
+    };
+
+    // Returns an iterator pointing to the first primaryKey record in the requested key, or the next key if it doesn't exist.
+    Iterator find(const IDBKeyData&, bool open = false);
+    Iterator reverseFind(const IDBKeyData&, bool open = false);
+
+    // Returns an iterator pointing to the key/primaryKey record, or the next one after it if it doesn't exist.
+    Iterator find(const IDBKeyData&, const IDBKeyData& primaryKey);
+    Iterator reverseFind(const IDBKeyData&, const IDBKeyData& primaryKey);
 
 private:
+    std::set<IDBKeyData>::iterator lowestIteratorInRange(const IDBKeyRangeData&) const;
+    std::set<IDBKeyData>::reverse_iterator highestReverseIteratorInRange(const IDBKeyRangeData&) const;
+
     IndexKeyValueMap m_records;
     std::set<IDBKeyData> m_orderedKeys;
     
index c835e96..9cb98c6 100644 (file)
@@ -354,7 +354,20 @@ IDBError MemoryIDBBackingStore::openCursor(const IDBResourceIdentifier& transact
         break;
     }
     case IndexedDB::CursorSource::Index:
-        return IDBError(IDBExceptionCode::Unknown, ASCIILiteral("Index cursors not yet supported"));
+        auto* objectStore = m_objectStoresByIdentifier.get(info.objectStoreIdentifier());
+        if (!objectStore)
+            return IDBError(IDBExceptionCode::Unknown, ASCIILiteral("No backing store object store found"));
+
+        auto* index = objectStore->indexForIdentifier(info.sourceIdentifier());
+        if (!index)
+            return IDBError(IDBExceptionCode::Unknown, ASCIILiteral("No backing store index found"));
+
+        MemoryCursor* cursor = index->maybeOpenCursor(info);
+        if (!cursor)
+            return IDBError(IDBExceptionCode::Unknown, ASCIILiteral("Could not create index cursor in backing store"));
+
+        cursor->currentData(outData);
+        break;
     }
 
     return { };
index 23d684d..516651a 100644 (file)
@@ -55,12 +55,42 @@ MemoryIndex::~MemoryIndex()
 {
 }
 
+void MemoryIndex::cursorDidBecomeClean(MemoryIndexCursor& cursor)
+{
+    m_cleanCursors.add(&cursor);
+}
+
+void MemoryIndex::cursorDidBecomeDirty(MemoryIndexCursor& cursor)
+{
+    m_cleanCursors.remove(&cursor);
+}
+
 void MemoryIndex::objectStoreCleared()
 {
     auto transaction = m_objectStore.writeTransaction();
     ASSERT(transaction);
 
     transaction->indexCleared(*this, WTF::move(m_records));
+
+    notifyCursorsOfAllRecordsChanged();
+}
+
+void MemoryIndex::notifyCursorsOfValueChange(const IDBKeyData& indexKey, const IDBKeyData& primaryKey)
+{
+    Vector<MemoryIndexCursor*> cursors;
+    copyToVector(m_cleanCursors, cursors);
+    for (auto* cursor : cursors)
+        cursor->indexValueChanged(indexKey, primaryKey);
+}
+
+void MemoryIndex::notifyCursorsOfAllRecordsChanged()
+{
+    Vector<MemoryIndexCursor*> cursors;
+    copyToVector(m_cleanCursors, cursors);
+    for (auto* cursor : cursors)
+        cursor->indexRecordsAllChanged();
+
+    ASSERT(m_cleanCursors.isEmpty());
 }
 
 void MemoryIndex::replaceIndexValueStore(std::unique_ptr<IndexValueStore>&& valueStore)
@@ -122,12 +152,16 @@ IDBError MemoryIndex::putIndexKey(const IDBKeyData& valueKey, const IndexKey& in
 {
     LOG(IndexedDB, "MemoryIndex::provisionalPutIndexKey");
 
-    if (!m_records)
+    if (!m_records) {
         m_records = std::make_unique<IndexValueStore>(m_info.unique());
+        notifyCursorsOfAllRecordsChanged();
+    }
 
     if (!m_info.multiEntry()) {
         IDBKeyData key = indexKey.asOneKey();
-        return m_records->addRecord(key, valueKey);
+        IDBError result = m_records->addRecord(key, valueKey);
+        notifyCursorsOfValueChange(key, valueKey);
+        return result;
     }
 
     Vector<IDBKeyData> keys = indexKey.multiEntry();
@@ -142,6 +176,7 @@ IDBError MemoryIndex::putIndexKey(const IDBKeyData& valueKey, const IndexKey& in
     for (auto& key : keys) {
         auto error = m_records->addRecord(key, valueKey);
         ASSERT_UNUSED(error, error.isNull());
+        notifyCursorsOfValueChange(key, valueKey);
     }
 
     return { };
@@ -156,12 +191,15 @@ void MemoryIndex::removeRecord(const IDBKeyData& valueKey, const IndexKey& index
     if (!m_info.multiEntry()) {
         IDBKeyData key = indexKey.asOneKey();
         m_records->removeRecord(key, valueKey);
+        notifyCursorsOfValueChange(key, valueKey);
         return;
     }
 
     Vector<IDBKeyData> keys = indexKey.multiEntry();
-    for (auto& key : keys)
+    for (auto& key : keys) {
         m_records->removeRecord(key, valueKey);
+        notifyCursorsOfValueChange(key, valueKey);
+    }
 }
 
 void MemoryIndex::removeEntriesWithValueKey(const IDBKeyData& valueKey)
@@ -171,7 +209,17 @@ void MemoryIndex::removeEntriesWithValueKey(const IDBKeyData& valueKey)
     if (!m_records)
         return;
 
-    m_records->removeEntriesWithValueKey(valueKey);
+    m_records->removeEntriesWithValueKey(*this, valueKey);
+}
+
+MemoryIndexCursor* MemoryIndex::maybeOpenCursor(const IDBCursorInfo& info)
+{
+    auto result = m_cursors.add(info.identifier(), nullptr);
+    if (!result.isNewEntry)
+        return nullptr;
+
+    result.iterator->value = std::make_unique<MemoryIndexCursor>(*this, info);
+    return result.iterator->value.get();
 }
 
 } // namespace IDBServer
index 29bb252..a8924b1 100644 (file)
@@ -32,6 +32,7 @@
 #include "IDBIndexInfo.h"
 #include "IDBKeyData.h"
 #include "IndexValueStore.h"
+#include "MemoryIndexCursor.h"
 #include <set>
 #include <wtf/HashMap.h>
 
@@ -72,15 +73,31 @@ public:
     void objectStoreCleared();
     void replaceIndexValueStore(std::unique_ptr<IndexValueStore>&&);
 
+    MemoryIndexCursor* maybeOpenCursor(const IDBCursorInfo&);
+
+    IndexValueStore* valueStore() { return m_records.get(); }
+
+    MemoryObjectStore& objectStore() { return m_objectStore; }
+
+    void cursorDidBecomeClean(MemoryIndexCursor&);
+    void cursorDidBecomeDirty(MemoryIndexCursor&);
+
+    void notifyCursorsOfValueChange(const IDBKeyData& indexKey, const IDBKeyData& primaryKey);
+
 private:
     MemoryIndex(const IDBIndexInfo&, MemoryObjectStore&);
 
     uint64_t recordCountForKey(const IDBKeyData&) const;
-    
+
+    void notifyCursorsOfAllRecordsChanged();
+
     IDBIndexInfo m_info;
     MemoryObjectStore& m_objectStore;
 
     std::unique_ptr<IndexValueStore> m_records;
+
+    HashMap<IDBResourceIdentifier, std::unique_ptr<MemoryIndexCursor>> m_cursors;
+    HashSet<MemoryIndexCursor*> m_cleanCursors;
 };
 
 } // namespace IDBServer
diff --git a/Source/WebCore/Modules/indexeddb/server/MemoryIndexCursor.cpp b/Source/WebCore/Modules/indexeddb/server/MemoryIndexCursor.cpp
new file mode 100644 (file)
index 0000000..b31464a
--- /dev/null
@@ -0,0 +1,205 @@
+/*
+ * Copyright (C) 2015 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+#include "MemoryIndexCursor.h"
+
+#if ENABLE(INDEXED_DATABASE)
+
+#include "IDBCursorInfo.h"
+#include "IndexValueStore.h"
+#include "Logging.h"
+#include "MemoryCursor.h"
+#include "MemoryIndex.h"
+#include "MemoryObjectStore.h"
+
+namespace WebCore {
+namespace IDBServer {
+
+MemoryIndexCursor::MemoryIndexCursor(MemoryIndex& index, const IDBCursorInfo& info)
+    : MemoryCursor(info)
+    , m_index(index)
+{
+    auto* valueStore = m_index.valueStore();
+    if (!valueStore)
+        return;
+
+    IndexValueStore::Iterator iterator;
+    if (m_info.isDirectionForward())
+        iterator = valueStore->find(m_info.range().lowerKey, m_info.range().lowerOpen);
+    else
+        iterator = valueStore->reverseFind(m_info.range().upperKey, m_info.range().upperOpen);
+
+    if (iterator.isValid()) {
+        m_currentKey = iterator.key();
+        m_currentPrimaryKey = iterator.primaryKey();
+        m_index.cursorDidBecomeClean(*this);
+    }
+}
+
+MemoryIndexCursor::~MemoryIndexCursor()
+{
+}
+
+void MemoryIndexCursor::currentData(IDBGetResult& getResult)
+{
+    getResult.keyData = m_currentKey;
+    getResult.primaryKeyData = m_currentPrimaryKey;
+    if (m_info.cursorType() == IndexedDB::CursorType::KeyOnly)
+        return;
+
+    getResult.valueBuffer = m_index.objectStore().valueForKey(m_currentPrimaryKey);
+}
+
+void MemoryIndexCursor::iterate(const IDBKeyData& key, uint32_t count, IDBGetResult& getResult)
+{
+    LOG(IndexedDB, "MemoryIndexCursor::iterate to key %s, %u count", key.loggingString().utf8().data(), count);
+
+    if (key.isValid()) {
+        // Cannot iterator by both a count and to a key
+        ASSERT(!count);
+
+        auto* valueStore = m_index.valueStore();
+        if (!valueStore) {
+            m_currentKey = { };
+            m_currentPrimaryKey = { };
+            getResult = { };
+            return;
+        }
+
+        if (m_info.isDirectionForward())
+            m_currentIterator = valueStore->find(m_currentKey);
+        else
+            m_currentIterator = valueStore->reverseFind(m_currentKey);
+
+        if (!m_currentIterator.isValid()) {
+            m_currentKey = { };
+            m_currentPrimaryKey = { };
+            getResult = { };
+            return;
+        }
+
+        m_index.cursorDidBecomeClean(*this);
+
+        m_currentKey = m_currentIterator.key();
+        m_currentPrimaryKey = m_currentIterator.primaryKey();
+        currentData(getResult);
+
+        return;
+    }
+
+    // If there was not a valid key argument and no positive count argument
+    // that means the default iteration count of "1"
+    if (!count)
+        count = 1;
+
+    if (!m_currentIterator.isValid()) {
+        auto* valueStore = m_index.valueStore();
+        if (!valueStore) {
+            m_currentKey = { };
+            m_currentPrimaryKey = { };
+            getResult = { };
+            return;
+        }
+
+        switch (m_info.cursorDirection()) {
+        case IndexedDB::CursorDirection::Next:
+            m_currentIterator = valueStore->find(m_currentKey, m_currentPrimaryKey);
+            break;
+        case IndexedDB::CursorDirection::NextNoDuplicate:
+            m_currentIterator = valueStore->find(m_currentKey, true);
+            break;
+        case IndexedDB::CursorDirection::Prev:
+            m_currentIterator = valueStore->reverseFind(m_currentKey, m_currentPrimaryKey);
+            break;
+        case IndexedDB::CursorDirection::PrevNoDuplicate:
+            m_currentIterator = valueStore->reverseFind(m_currentKey, true);
+            break;
+        }
+
+        if (!m_currentIterator.isValid()) {
+            m_currentKey = { };
+            m_currentPrimaryKey = { };
+            getResult = { };
+            return;
+        }
+
+        m_index.cursorDidBecomeClean(*this);
+
+        // If we restored the current iterator and it does *not* match the current key/primaryKey,
+        // then it is the next record in line and we should consider that an iteration.
+        if (m_currentKey != m_currentIterator.key() || m_currentPrimaryKey != m_currentIterator.primaryKey())
+            --count;
+    }
+
+    ASSERT(m_currentIterator.isValid());
+
+    while (count) {
+        if (m_info.isDirectionNoDuplicate())
+            m_currentIterator.nextIndexEntry();
+        else
+            ++m_currentIterator;
+
+        if (!m_currentIterator.isValid())
+            break;
+
+        --count;
+    }
+
+    if (m_currentIterator.isValid() && !m_info.range().containsKey(m_currentIterator.key()))
+        m_currentIterator.invalidate();
+
+    // Not having a valid iterator after finishing any iteration means we've reached the end of the cursor.
+    if (!m_currentIterator.isValid()) {
+        m_currentKey = { };
+        m_currentPrimaryKey = { };
+        getResult = { };
+        return;
+    }
+
+    m_currentKey = m_currentIterator.key();
+    m_currentPrimaryKey = m_currentIterator.primaryKey();
+    currentData(getResult);
+}
+
+void MemoryIndexCursor::indexRecordsAllChanged()
+{
+    m_currentIterator.invalidate();
+    m_index.cursorDidBecomeDirty(*this);
+}
+
+void MemoryIndexCursor::indexValueChanged(const IDBKeyData& key, const IDBKeyData& primaryKey)
+{
+    if (m_currentKey != key || m_currentPrimaryKey != primaryKey)
+        return;
+
+    m_currentIterator.invalidate();
+    m_index.cursorDidBecomeDirty(*this);
+}
+
+} // namespace IDBServer
+} // namespace WebCore
+
+#endif // ENABLE(INDEXED_DATABASE)
diff --git a/Source/WebCore/Modules/indexeddb/server/MemoryIndexCursor.h b/Source/WebCore/Modules/indexeddb/server/MemoryIndexCursor.h
new file mode 100644 (file)
index 0000000..455f23a
--- /dev/null
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2015 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef MemoryIndexCursor_h
+#define MemoryIndexCursor_h
+
+#if ENABLE(INDEXED_DATABASE)
+
+#include "IDBCursorInfo.h"
+#include "IndexValueStore.h"
+#include "MemoryCursor.h"
+
+namespace WebCore {
+namespace IDBServer {
+
+class MemoryIndex;
+
+class MemoryIndexCursor : public MemoryCursor {
+public:
+    MemoryIndexCursor(MemoryIndex&, const IDBCursorInfo&);
+    virtual ~MemoryIndexCursor();
+
+    void indexRecordsAllChanged();
+    void indexValueChanged(const IDBKeyData& indexKey, const IDBKeyData& primaryKey);
+
+private:
+    virtual void currentData(IDBGetResult&) override final;
+    virtual void iterate(const IDBKeyData&, uint32_t count, IDBGetResult&) override final;
+
+    MemoryIndex& m_index;
+
+    IndexValueStore::Iterator m_currentIterator;
+    IDBKeyData m_currentKey;
+    IDBKeyData m_currentPrimaryKey;
+};
+
+} // namespace IDBServer
+} // namespace WebCore
+
+#endif // ENABLE(INDEXED_DATABASE)
+#endif // MemoryIndexCursor_h
index 7eca093..fc1f74d 100644 (file)
@@ -58,6 +58,12 @@ MemoryObjectStore::~MemoryObjectStore()
     ASSERT(!m_writeTransaction);
 }
 
+MemoryIndex* MemoryObjectStore::indexForIdentifier(uint64_t identifier)
+{
+    ASSERT(identifier);
+    return m_indexesByIdentifier.get(identifier);
+}
+
 void MemoryObjectStore::writeTransactionStarted(MemoryBackingStoreTransaction& transaction)
 {
     LOG(IndexedDB, "MemoryObjectStore::writeTransactionStarted");
@@ -305,6 +311,14 @@ uint64_t MemoryObjectStore::countForKeyRange(uint64_t indexIdentifier, const IDB
     return count;
 }
 
+ThreadSafeDataBuffer MemoryObjectStore::valueForKey(const IDBKeyData& key) const
+{
+    if (!m_keyValueStore)
+        return { };
+
+    return m_keyValueStore->get(key);
+}
+
 ThreadSafeDataBuffer MemoryObjectStore::valueForKeyRange(const IDBKeyRangeData& keyRangeData) const
 {
     LOG(IndexedDB, "MemoryObjectStore::valueForKey");
index f85b92d..58b099e 100644 (file)
@@ -78,6 +78,7 @@ public:
     void clear();
     void replaceKeyValueStore(std::unique_ptr<KeyValueMap>&&, std::unique_ptr<std::set<IDBKeyData>>&&);
 
+    ThreadSafeDataBuffer valueForKey(const IDBKeyData&) const;
     ThreadSafeDataBuffer valueForKeyRange(const IDBKeyRangeData&) const;
     IDBGetResult indexValueForKeyRange(uint64_t indexIdentifier, IndexedDB::IndexRecordType, const IDBKeyRangeData&) const;
     uint64_t countForKeyRange(uint64_t indexIdentifier, const IDBKeyRangeData&) const;
@@ -88,6 +89,8 @@ public:
 
     std::set<IDBKeyData>* orderedKeys() { return m_orderedKeys.get(); }
 
+    MemoryIndex* indexForIdentifier(uint64_t);
+
 private:
     MemoryObjectStore(const IDBObjectStoreInfo&);
 
index 0384601..90eb691 100644 (file)
@@ -35,28 +35,42 @@ namespace WebCore {
 
 IDBCursorInfo IDBCursorInfo::objectStoreCursor(IDBClient::IDBTransaction& transaction, uint64_t objectStoreIdentifier, const IDBKeyRangeData& range, IndexedDB::CursorDirection direction)
 {
-    return { transaction, IndexedDB::CursorSource::ObjectStore, objectStoreIdentifier, range, direction, IndexedDB::CursorType::KeyAndValue };
+    return { transaction, objectStoreIdentifier, range, direction, IndexedDB::CursorType::KeyAndValue };
 }
 
-IDBCursorInfo IDBCursorInfo::indexCursor(IDBClient::IDBTransaction& transaction, uint64_t indexIdentifier, const IDBKeyRangeData& range, IndexedDB::CursorDirection direction, IndexedDB::CursorType type)
+IDBCursorInfo IDBCursorInfo::indexCursor(IDBClient::IDBTransaction& transaction, uint64_t objectStoreIdentifier, uint64_t indexIdentifier, const IDBKeyRangeData& range, IndexedDB::CursorDirection direction, IndexedDB::CursorType type)
 {
-    return { transaction, IndexedDB::CursorSource::Index, indexIdentifier, range, direction, type };
+    return { transaction, objectStoreIdentifier, indexIdentifier, range, direction, type };
 }
 
-IDBCursorInfo::IDBCursorInfo(IDBClient::IDBTransaction& transaction, IndexedDB::CursorSource source, uint64_t sourceIdentifier, const IDBKeyRangeData& range, IndexedDB::CursorDirection direction, IndexedDB::CursorType type)
+IDBCursorInfo::IDBCursorInfo(IDBClient::IDBTransaction& transaction, uint64_t objectStoreIdentifier, const IDBKeyRangeData& range, IndexedDB::CursorDirection direction, IndexedDB::CursorType type)
     : m_cursorIdentifier(transaction.serverConnection())
     , m_transactionIdentifier(transaction.info().identifier())
-    , m_sourceIdentifier(sourceIdentifier)
+    , m_objectStoreIdentifier(objectStoreIdentifier)
+    , m_sourceIdentifier(objectStoreIdentifier)
     , m_range(range)
-    , m_source(source)
+    , m_source(IndexedDB::CursorSource::ObjectStore)
     , m_direction(direction)
     , m_type(type)
 {
 }
 
-IDBCursorInfo::IDBCursorInfo(const IDBResourceIdentifier& cursorIdentifier, const IDBResourceIdentifier& transactionIdentifier, uint64_t sourceIdentifier, const IDBKeyRangeData& range, IndexedDB::CursorSource source, IndexedDB::CursorDirection direction, IndexedDB::CursorType type)
+IDBCursorInfo::IDBCursorInfo(IDBClient::IDBTransaction& transaction, uint64_t objectStoreIdentifier, uint64_t indexIdentifier, const IDBKeyRangeData& range, IndexedDB::CursorDirection direction, IndexedDB::CursorType type)
+    : m_cursorIdentifier(transaction.serverConnection())
+    , m_transactionIdentifier(transaction.info().identifier())
+    , m_objectStoreIdentifier(objectStoreIdentifier)
+    , m_sourceIdentifier(indexIdentifier)
+    , m_range(range)
+    , m_source(IndexedDB::CursorSource::Index)
+    , m_direction(direction)
+    , m_type(type)
+{
+}
+
+IDBCursorInfo::IDBCursorInfo(const IDBResourceIdentifier& cursorIdentifier, const IDBResourceIdentifier& transactionIdentifier, uint64_t objectStoreIdentifier, uint64_t sourceIdentifier, const IDBKeyRangeData& range, IndexedDB::CursorSource source, IndexedDB::CursorDirection direction, IndexedDB::CursorType type)
     : m_cursorIdentifier(cursorIdentifier)
     , m_transactionIdentifier(transactionIdentifier)
+    , m_objectStoreIdentifier(objectStoreIdentifier)
     , m_sourceIdentifier(sourceIdentifier)
     , m_range(range)
     , m_source(source)
@@ -70,9 +84,14 @@ bool IDBCursorInfo::isDirectionForward() const
     return m_direction == IndexedDB::CursorDirection::Next || m_direction == IndexedDB::CursorDirection::NextNoDuplicate;
 }
 
+bool IDBCursorInfo::isDirectionNoDuplicate() const
+{
+    return m_direction == IndexedDB::CursorDirection::NextNoDuplicate || m_direction == IndexedDB::CursorDirection::PrevNoDuplicate;
+}
+
 IDBCursorInfo IDBCursorInfo::isolatedCopy() const
 {
-    return { m_cursorIdentifier.isolatedCopy(), m_transactionIdentifier.isolatedCopy(), m_sourceIdentifier, m_range.isolatedCopy(), m_source, m_direction, m_type };
+    return { m_cursorIdentifier.isolatedCopy(), m_transactionIdentifier.isolatedCopy(), m_objectStoreIdentifier, m_sourceIdentifier, m_range.isolatedCopy(), m_source, m_direction, m_type };
 }
 
 } // namespace WebCore
index 8649a0a..ca71e37 100644 (file)
@@ -48,10 +48,11 @@ struct IDBKeyRangeData;
 class IDBCursorInfo {
 public:
     static IDBCursorInfo objectStoreCursor(IDBClient::IDBTransaction&, uint64_t objectStoreIdentifier, const IDBKeyRangeData&, IndexedDB::CursorDirection);
-    static IDBCursorInfo indexCursor(IDBClient::IDBTransaction&, uint64_t indexIdentifier, const IDBKeyRangeData&, IndexedDB::CursorDirection, IndexedDB::CursorType);
+    static IDBCursorInfo indexCursor(IDBClient::IDBTransaction&, uint64_t objectStoreIdentifier, uint64_t indexIdentifier, const IDBKeyRangeData&, IndexedDB::CursorDirection, IndexedDB::CursorType);
 
     IDBResourceIdentifier identifier() const { return m_cursorIdentifier; }
     uint64_t sourceIdentifier() const { return m_sourceIdentifier; }
+    uint64_t objectStoreIdentifier() const { return m_objectStoreIdentifier; }
 
     IndexedDB::CursorSource cursorSource() const { return m_source; }
     IndexedDB::CursorDirection cursorDirection() const { return m_direction; }
@@ -59,15 +60,19 @@ public:
     const IDBKeyRangeData& range() const { return m_range; }
 
     bool isDirectionForward() const;
+    bool isDirectionNoDuplicate() const;
 
     IDBCursorInfo isolatedCopy() const;
 
 private:
-    IDBCursorInfo(IDBClient::IDBTransaction&, IndexedDB::CursorSource, uint64_t sourceIdentifier, const IDBKeyRangeData&, IndexedDB::CursorDirection, IndexedDB::CursorType);
-    IDBCursorInfo(const IDBResourceIdentifier&, const IDBResourceIdentifier&, uint64_t, const IDBKeyRangeData&, IndexedDB::CursorSource, IndexedDB::CursorDirection, IndexedDB::CursorType);
+    IDBCursorInfo(IDBClient::IDBTransaction&, uint64_t objectStoreIdentifier, const IDBKeyRangeData&, IndexedDB::CursorDirection, IndexedDB::CursorType);
+    IDBCursorInfo(IDBClient::IDBTransaction&, uint64_t objectStoreIdentifier, uint64_t indexIdentifier, const IDBKeyRangeData&, IndexedDB::CursorDirection, IndexedDB::CursorType);
+
+    IDBCursorInfo(const IDBResourceIdentifier&, const IDBResourceIdentifier&, uint64_t, uint64_t, const IDBKeyRangeData&, IndexedDB::CursorSource, IndexedDB::CursorDirection, IndexedDB::CursorType);
 
     IDBResourceIdentifier m_cursorIdentifier;
     IDBResourceIdentifier m_transactionIdentifier;
+    uint64_t m_objectStoreIdentifier;
     uint64_t m_sourceIdentifier;
 
     IDBKeyRangeData m_range;
index 5c4a8bd..09dd304 100644 (file)
                071E49701AD5AB5E008A50B4 /* MediaPlaybackTargetMac.h in Headers */ = {isa = PBXBuildFile; fileRef = 071E496F1AD5AB5E008A50B4 /* MediaPlaybackTargetMac.h */; settings = {ATTRIBUTES = (Private, ); }; };
                0720B0A014D3323500642955 /* GenericEventQueue.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0720B09E14D3323500642955 /* GenericEventQueue.cpp */; };
                0720B0A114D3323500642955 /* GenericEventQueue.h in Headers */ = {isa = PBXBuildFile; fileRef = 0720B09F14D3323500642955 /* GenericEventQueue.h */; settings = {ATTRIBUTES = (Private, ); }; };
-               072560FB1BFC2482004F9359 /* JSMediaTrackSupportedConstraintsCustom.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 07C1C0E61BFB90A700BD2256 /* JSMediaTrackSupportedConstraintsCustom.cpp */; settings = {ASSET_TAGS = (); }; };
+               072560FB1BFC2482004F9359 /* JSMediaTrackSupportedConstraintsCustom.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 07C1C0E61BFB90A700BD2256 /* JSMediaTrackSupportedConstraintsCustom.cpp */; };
                07277E4C17D018CC0015534D /* JSMediaStream.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 07277E4017D018CC0015534D /* JSMediaStream.cpp */; };
                07277E4D17D018CC0015534D /* JSMediaStream.h in Headers */ = {isa = PBXBuildFile; fileRef = 07277E4117D018CC0015534D /* JSMediaStream.h */; };
                07277E4E17D018CC0015534D /* JSMediaStreamAudioDestinationNode.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 07277E4217D018CC0015534D /* JSMediaStreamAudioDestinationNode.cpp */; };
                07846342145B151A00A58DF1 /* JSTrackEvent.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 07846340145B151A00A58DF1 /* JSTrackEvent.cpp */; };
                07846343145B151A00A58DF1 /* JSTrackEvent.h in Headers */ = {isa = PBXBuildFile; fileRef = 07846341145B151A00A58DF1 /* JSTrackEvent.h */; };
                07846385145B1B8E00A58DF1 /* JSTrackCustom.h in Headers */ = {isa = PBXBuildFile; fileRef = 07846384145B1B8E00A58DF1 /* JSTrackCustom.h */; };
-               0787C4691BFBDF6F006DCD7F /* JSMediaTrackSupportedConstraints.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0787C4671BFBDF6F006DCD7F /* JSMediaTrackSupportedConstraints.cpp */; settings = {ASSET_TAGS = (); }; };
-               0787C46A1BFBDF6F006DCD7F /* JSMediaTrackSupportedConstraints.h in Headers */ = {isa = PBXBuildFile; fileRef = 0787C4681BFBDF6F006DCD7F /* JSMediaTrackSupportedConstraints.h */; settings = {ASSET_TAGS = (); }; };
+               0787C4691BFBDF6F006DCD7F /* JSMediaTrackSupportedConstraints.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0787C4671BFBDF6F006DCD7F /* JSMediaTrackSupportedConstraints.cpp */; };
+               0787C46A1BFBDF6F006DCD7F /* JSMediaTrackSupportedConstraints.h in Headers */ = {isa = PBXBuildFile; fileRef = 0787C4681BFBDF6F006DCD7F /* JSMediaTrackSupportedConstraints.h */; };
                078E08FE17D14CEE00420AA1 /* MediaConstraintsImpl.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 07221B4A17CEC32700848E51 /* MediaConstraintsImpl.cpp */; };
                078E08FF17D14CEE00420AA1 /* MediaStream.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 07221B4C17CEC32700848E51 /* MediaStream.cpp */; };
                078E090017D14CEE00420AA1 /* MediaStreamEvent.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 07221B4F17CEC32700848E51 /* MediaStreamEvent.cpp */; };
                07B5A2DC1464320A00A81ECE /* JSTextTrackList.h in Headers */ = {isa = PBXBuildFile; fileRef = 07B5A2DA1464320A00A81ECE /* JSTextTrackList.h */; };
                07B5A30D14687D7100A81ECE /* JSTextTrackListCustom.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 07B5A30C14687D7100A81ECE /* JSTextTrackListCustom.cpp */; };
                07BDD6EC1469B4C2009C9F85 /* JSTrackEventCustom.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 07B5A30A14687B8400A81ECE /* JSTrackEventCustom.cpp */; };
-               07C1C0E21BFB600100BD2256 /* MediaTrackSupportedConstraints.h in Headers */ = {isa = PBXBuildFile; fileRef = 07C1C0E01BFB600100BD2256 /* MediaTrackSupportedConstraints.h */; settings = {ASSET_TAGS = (); }; };
+               07C1C0E21BFB600100BD2256 /* MediaTrackSupportedConstraints.h in Headers */ = {isa = PBXBuildFile; fileRef = 07C1C0E01BFB600100BD2256 /* MediaTrackSupportedConstraints.h */; };
                07C1C0E51BFB60ED00BD2256 /* RealtimeMediaSourceSupportedConstraints.h in Headers */ = {isa = PBXBuildFile; fileRef = 07C1C0E41BFB60ED00BD2256 /* RealtimeMediaSourceSupportedConstraints.h */; settings = {ATTRIBUTES = (Private, ); }; };
                07C59B6817F784BA000FBCBB /* MediaSourceStates.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 07C59B6517F784BA000FBCBB /* MediaSourceStates.cpp */; };
                07C59B6917F784BA000FBCBB /* MediaSourceStates.h in Headers */ = {isa = PBXBuildFile; fileRef = 07C59B6617F784BA000FBCBB /* MediaSourceStates.h */; };
                518F5004194CAC3A0081BAAE /* JSGamepadButton.h in Headers */ = {isa = PBXBuildFile; fileRef = 518F5000194CAC3A0081BAAE /* JSGamepadButton.h */; };
                518F97021BE94C630023187C /* MemoryIndex.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 518F97001BE94C5B0023187C /* MemoryIndex.cpp */; };
                518F97031BE94C630023187C /* MemoryIndex.h in Headers */ = {isa = PBXBuildFile; fileRef = 518F97011BE94C5B0023187C /* MemoryIndex.h */; };
+               519755F91BFD7DC3003DE980 /* MemoryIndexCursor.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 519755F71BFD7DBC003DE980 /* MemoryIndexCursor.cpp */; };
+               519755FA1BFD7DC3003DE980 /* MemoryIndexCursor.h in Headers */ = {isa = PBXBuildFile; fileRef = 519755F81BFD7DBC003DE980 /* MemoryIndexCursor.h */; };
                5198F7A41BBDB79300E2CC5F /* UniqueIDBDatabaseConnection.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 5198F7A21BBDAA2900E2CC5F /* UniqueIDBDatabaseConnection.cpp */; };
                5198F7A51BBDB79300E2CC5F /* UniqueIDBDatabaseConnection.h in Headers */ = {isa = PBXBuildFile; fileRef = 5198F7A31BBDAA2900E2CC5F /* UniqueIDBDatabaseConnection.h */; settings = {ATTRIBUTES = (Private, ); }; };
                5198F7A81BBDD38500E2CC5F /* UniqueIDBDatabaseTransaction.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 5198F7A61BBDD38100E2CC5F /* UniqueIDBDatabaseTransaction.cpp */; };
                5DFE8F560D16477B0076E937 /* ScheduledAction.cpp in Sources */ = {isa = PBXBuildFile; fileRef = BCA378BA0D15F64200B793D6 /* ScheduledAction.cpp */; };
                5DFE8F570D16477C0076E937 /* ScheduledAction.h in Headers */ = {isa = PBXBuildFile; fileRef = BCA378BB0D15F64200B793D6 /* ScheduledAction.h */; settings = {ATTRIBUTES = (Private, ); }; };
                5DFEBAB718592B6D00C75BEB /* WebKitAvailability.h in Headers */ = {isa = PBXBuildFile; fileRef = 5DFEBAB618592B6D00C75BEB /* WebKitAvailability.h */; settings = {ATTRIBUTES = (Private, ); }; };
-               5E16A2E41BFA650B0029A21E /* MediaEndpointPeerConnection.h in Headers */ = {isa = PBXBuildFile; fileRef = 5E16A2E31BFA64FB0029A21E /* MediaEndpointPeerConnection.h */; settings = {ASSET_TAGS = (); }; };
-               5E16A2E51BFA650F0029A21E /* MediaEndpointPeerConnection.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 5E16A2E21BFA64FB0029A21E /* MediaEndpointPeerConnection.cpp */; settings = {ASSET_TAGS = (); }; };
-               5E2C43511BCEE2F60001E2BC /* PeerConnectionBackend.h in Headers */ = {isa = PBXBuildFile; fileRef = 5E2C434D1BCEE2E50001E2BC /* PeerConnectionBackend.h */; settings = {ASSET_TAGS = (); }; };
-               5E2C43521BCEE2F60001E2BC /* PeerConnectionStates.h in Headers */ = {isa = PBXBuildFile; fileRef = 5E2C434E1BCEE2E50001E2BC /* PeerConnectionStates.h */; settings = {ASSET_TAGS = (); }; };
-               5E2C43531BCEE2F60001E2BC /* RTCConfiguration.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 5E2C434F1BCEE2E50001E2BC /* RTCConfiguration.cpp */; settings = {ASSET_TAGS = (); }; };
-               5E2C435F1BCEE31E0001E2BC /* RTCRtpSenderReceiverBase.h in Headers */ = {isa = PBXBuildFile; fileRef = 5E2C435D1BCEE30D0001E2BC /* RTCRtpSenderReceiverBase.h */; settings = {ASSET_TAGS = (); }; };
-               5E2C43601BCEE3230001E2BC /* RTCRtpSender.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 5E2C43591BCEE30D0001E2BC /* RTCRtpSender.cpp */; settings = {ASSET_TAGS = (); }; };
-               5E2C43611BCEE3230001E2BC /* RTCRtpSender.h in Headers */ = {isa = PBXBuildFile; fileRef = 5E2C435A1BCEE30D0001E2BC /* RTCRtpSender.h */; settings = {ASSET_TAGS = (); }; };
-               5E2C43621BCEE32B0001E2BC /* RTCRtpReceiver.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 5E2C43561BCEE30D0001E2BC /* RTCRtpReceiver.cpp */; settings = {ASSET_TAGS = (); }; };
-               5E2C43631BCEE32B0001E2BC /* RTCRtpReceiver.h in Headers */ = {isa = PBXBuildFile; fileRef = 5E2C43571BCEE30D0001E2BC /* RTCRtpReceiver.h */; settings = {ASSET_TAGS = (); }; };
-               5E2C43671BCEE3770001E2BC /* RTCTrackEvent.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 5E2C43641BCEE3720001E2BC /* RTCTrackEvent.cpp */; settings = {ASSET_TAGS = (); }; };
-               5E2C43681BCEE3770001E2BC /* RTCTrackEvent.h in Headers */ = {isa = PBXBuildFile; fileRef = 5E2C43651BCEE3720001E2BC /* RTCTrackEvent.h */; settings = {ASSET_TAGS = (); }; };
-               5E2C436B1BCF071E0001E2BC /* JSRTCTrackEvent.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 5E2C43691BCF05C80001E2BC /* JSRTCTrackEvent.cpp */; settings = {ASSET_TAGS = (); }; };
-               5E2C436C1BCF071E0001E2BC /* JSRTCTrackEvent.h in Headers */ = {isa = PBXBuildFile; fileRef = 5E2C436A1BCF05C80001E2BC /* JSRTCTrackEvent.h */; settings = {ASSET_TAGS = (); }; };
-               5E2C43711BCF0D750001E2BC /* JSRTCRtpReceiver.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 5E2C436D1BCF0D690001E2BC /* JSRTCRtpReceiver.cpp */; settings = {ASSET_TAGS = (); }; };
-               5E2C43721BCF0D750001E2BC /* JSRTCRtpReceiver.h in Headers */ = {isa = PBXBuildFile; fileRef = 5E2C436E1BCF0D690001E2BC /* JSRTCRtpReceiver.h */; settings = {ASSET_TAGS = (); }; };
-               5E2C43731BCF0D750001E2BC /* JSRTCRtpSender.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 5E2C436F1BCF0D690001E2BC /* JSRTCRtpSender.cpp */; settings = {ASSET_TAGS = (); }; };
-               5E2C43741BCF0D750001E2BC /* JSRTCRtpSender.h in Headers */ = {isa = PBXBuildFile; fileRef = 5E2C43701BCF0D690001E2BC /* JSRTCRtpSender.h */; settings = {ASSET_TAGS = (); }; };
+               5E16A2E41BFA650B0029A21E /* MediaEndpointPeerConnection.h in Headers */ = {isa = PBXBuildFile; fileRef = 5E16A2E31BFA64FB0029A21E /* MediaEndpointPeerConnection.h */; };
+               5E16A2E51BFA650F0029A21E /* MediaEndpointPeerConnection.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 5E16A2E21BFA64FB0029A21E /* MediaEndpointPeerConnection.cpp */; };
+               5E2C43511BCEE2F60001E2BC /* PeerConnectionBackend.h in Headers */ = {isa = PBXBuildFile; fileRef = 5E2C434D1BCEE2E50001E2BC /* PeerConnectionBackend.h */; };
+               5E2C43521BCEE2F60001E2BC /* PeerConnectionStates.h in Headers */ = {isa = PBXBuildFile; fileRef = 5E2C434E1BCEE2E50001E2BC /* PeerConnectionStates.h */; };
+               5E2C43531BCEE2F60001E2BC /* RTCConfiguration.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 5E2C434F1BCEE2E50001E2BC /* RTCConfiguration.cpp */; };
+               5E2C435F1BCEE31E0001E2BC /* RTCRtpSenderReceiverBase.h in Headers */ = {isa = PBXBuildFile; fileRef = 5E2C435D1BCEE30D0001E2BC /* RTCRtpSenderReceiverBase.h */; };
+               5E2C43601BCEE3230001E2BC /* RTCRtpSender.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 5E2C43591BCEE30D0001E2BC /* RTCRtpSender.cpp */; };
+               5E2C43611BCEE3230001E2BC /* RTCRtpSender.h in Headers */ = {isa = PBXBuildFile; fileRef = 5E2C435A1BCEE30D0001E2BC /* RTCRtpSender.h */; };
+               5E2C43621BCEE32B0001E2BC /* RTCRtpReceiver.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 5E2C43561BCEE30D0001E2BC /* RTCRtpReceiver.cpp */; };
+               5E2C43631BCEE32B0001E2BC /* RTCRtpReceiver.h in Headers */ = {isa = PBXBuildFile; fileRef = 5E2C43571BCEE30D0001E2BC /* RTCRtpReceiver.h */; };
+               5E2C43671BCEE3770001E2BC /* RTCTrackEvent.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 5E2C43641BCEE3720001E2BC /* RTCTrackEvent.cpp */; };
+               5E2C43681BCEE3770001E2BC /* RTCTrackEvent.h in Headers */ = {isa = PBXBuildFile; fileRef = 5E2C43651BCEE3720001E2BC /* RTCTrackEvent.h */; };
+               5E2C436B1BCF071E0001E2BC /* JSRTCTrackEvent.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 5E2C43691BCF05C80001E2BC /* JSRTCTrackEvent.cpp */; };
+               5E2C436C1BCF071E0001E2BC /* JSRTCTrackEvent.h in Headers */ = {isa = PBXBuildFile; fileRef = 5E2C436A1BCF05C80001E2BC /* JSRTCTrackEvent.h */; };
+               5E2C43711BCF0D750001E2BC /* JSRTCRtpReceiver.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 5E2C436D1BCF0D690001E2BC /* JSRTCRtpReceiver.cpp */; };
+               5E2C43721BCF0D750001E2BC /* JSRTCRtpReceiver.h in Headers */ = {isa = PBXBuildFile; fileRef = 5E2C436E1BCF0D690001E2BC /* JSRTCRtpReceiver.h */; };
+               5E2C43731BCF0D750001E2BC /* JSRTCRtpSender.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 5E2C436F1BCF0D690001E2BC /* JSRTCRtpSender.cpp */; };
+               5E2C43741BCF0D750001E2BC /* JSRTCRtpSender.h in Headers */ = {isa = PBXBuildFile; fileRef = 5E2C43701BCF0D690001E2BC /* JSRTCRtpSender.h */; };
                5E2C437B1BCF9A570001E2BC /* RTCPeerConnectionBuiltins.h in Headers */ = {isa = PBXBuildFile; fileRef = 5E2C43761BCF9A0B0001E2BC /* RTCPeerConnectionBuiltins.h */; settings = {ATTRIBUTES = (Private, ); }; };
                5E2C437C1BCF9A840001E2BC /* RTCPeerConnectionInternalsBuiltins.h in Headers */ = {isa = PBXBuildFile; fileRef = 5E2C43791BCF9A0B0001E2BC /* RTCPeerConnectionInternalsBuiltins.h */; settings = {ATTRIBUTES = (Private, ); }; };
                5EA725D21ACABD4700EAD17B /* MediaDevices.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 5EA725CD1ACABCD900EAD17B /* MediaDevices.cpp */; };
                518F5000194CAC3A0081BAAE /* JSGamepadButton.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JSGamepadButton.h; sourceTree = "<group>"; };
                518F97001BE94C5B0023187C /* MemoryIndex.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = MemoryIndex.cpp; sourceTree = "<group>"; };
                518F97011BE94C5B0023187C /* MemoryIndex.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MemoryIndex.h; sourceTree = "<group>"; };
+               519755F71BFD7DBC003DE980 /* MemoryIndexCursor.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = MemoryIndexCursor.cpp; sourceTree = "<group>"; };
+               519755F81BFD7DBC003DE980 /* MemoryIndexCursor.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MemoryIndexCursor.h; sourceTree = "<group>"; };
                5198F7A21BBDAA2900E2CC5F /* UniqueIDBDatabaseConnection.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = UniqueIDBDatabaseConnection.cpp; sourceTree = "<group>"; };
                5198F7A31BBDAA2900E2CC5F /* UniqueIDBDatabaseConnection.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = UniqueIDBDatabaseConnection.h; sourceTree = "<group>"; };
                5198F7A61BBDD38100E2CC5F /* UniqueIDBDatabaseTransaction.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = UniqueIDBDatabaseTransaction.cpp; sourceTree = "<group>"; };
                                51BA4AC91BBC5B9E00DF3D6D /* MemoryIDBBackingStore.h */,
                                518F97001BE94C5B0023187C /* MemoryIndex.cpp */,
                                518F97011BE94C5B0023187C /* MemoryIndex.h */,
+                               519755F71BFD7DBC003DE980 /* MemoryIndexCursor.cpp */,
+                               519755F81BFD7DBC003DE980 /* MemoryIndexCursor.h */,
                                51771DFC1BDB475600CAE8E4 /* MemoryObjectStore.cpp */,
                                51771DFD1BDB475600CAE8E4 /* MemoryObjectStore.h */,
                                517139031BF64DE3000D5F01 /* MemoryObjectStoreCursor.cpp */,
                                0707568C142262D600414161 /* HTMLTrackElement.h in Headers */,
                                977B37261228721700B81FF8 /* HTMLTreeBuilder.h in Headers */,
                                A8EA79F20A1916DF00A8EF5F /* HTMLUListElement.h in Headers */,
+                               519755FA1BFD7DC3003DE980 /* MemoryIndexCursor.h in Headers */,
                                AD49914318F0815100BF0092 /* HTMLUnknownElement.h in Headers */,
                                E44613AB0CD6331000FADA75 /* HTMLVideoElement.h in Headers */,
                                839AAFED1A0C0C8D00605F99 /* HTMLWBRElement.h in Headers */,
                                51ABAE1F103C1913008C5260 /* SocketStreamHandleCFNet.cpp in Sources */,
                                E45390470EAFD637003695C8 /* SoundIOS.mm in Sources */,
                                4B3043C90AE0371D00A82647 /* SoundMac.mm in Sources */,
+                               519755F91BFD7DC3003DE980 /* MemoryIndexCursor.cpp in Sources */,
                                84A81F3D0FC7DFF000955300 /* SourceAlpha.cpp in Sources */,
                                CD3A496117A9D01B00274E42 /* SourceBuffer.cpp in Sources */,
                                CD3A496417A9D01B00274E42 /* SourceBufferList.cpp in Sources */,