IndexedDB: Attributes for a cursor "run past the end" should be undefined.
authorcommit-queue@webkit.org <commit-queue@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Thu, 12 Apr 2012 20:01:22 +0000 (20:01 +0000)
committercommit-queue@webkit.org <commit-queue@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Thu, 12 Apr 2012 20:01:22 +0000 (20:01 +0000)
https://bugs.webkit.org/show_bug.cgi?id=83492

Patch by Alec Flett <alecflett@chromium.org> on 2012-04-12
Reviewed by Ojan Vafai.

Source/WebCore:

This brings us up to spec with respect to when it's ok to call
cursor.continue() and the various behaviors around it:

If you call it more than once, continue() throws an exception.

The values of key/primaryKey/value are consistent, whether or not
continue has been called, meaning that if continue() results in
data being loaded from the backing store, that data does not
affect the cursor until the success callback has completed.

Calls to update() and delete() can happen before continue(), but
not afterwards.

Test: storage/indexeddb/cursor-continue-validity.html

* Modules/indexeddb/IDBCursor.cpp:
(WebCore::IDBCursor::IDBCursor):
(WebCore::IDBCursor::key):
(WebCore::IDBCursor::primaryKey):
(WebCore::IDBCursor::value):
(WebCore::IDBCursor::update):
(WebCore::IDBCursor::continueFunction):
(WebCore::IDBCursor::deleteFunction):
(WebCore::IDBCursor::setGotValue):
(WebCore):
* Modules/indexeddb/IDBCursor.h:
(IDBCursor):
* Modules/indexeddb/IDBRequest.cpp:
(WebCore::IDBRequest::onSuccess):
(WebCore::IDBRequest::dispatchEvent):

LayoutTests:

* storage/indexeddb/cursor-continue-validity-expected.txt: Added.
* storage/indexeddb/cursor-continue-validity.html: Added.
* storage/indexeddb/resources/cursor-continue-validity.js: Added.
* storage/indexeddb/resources/mozilla/cursors-expected.txt:
* storage/indexeddb/resources/shared.js:

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

LayoutTests/ChangeLog
LayoutTests/storage/indexeddb/cursor-continue-validity-expected.txt [new file with mode: 0644]
LayoutTests/storage/indexeddb/cursor-continue-validity.html [new file with mode: 0644]
LayoutTests/storage/indexeddb/mozilla/cursors-expected.txt
LayoutTests/storage/indexeddb/mozilla/resources/cursors.js
LayoutTests/storage/indexeddb/resources/cursor-continue-validity.js [new file with mode: 0644]
LayoutTests/storage/indexeddb/resources/shared.js
Source/WebCore/ChangeLog
Source/WebCore/Modules/indexeddb/IDBCursor.cpp
Source/WebCore/Modules/indexeddb/IDBCursor.h
Source/WebCore/Modules/indexeddb/IDBRequest.cpp

index 06e4c07..c85c753 100644 (file)
@@ -1,3 +1,16 @@
+2012-04-12  Alec Flett  <alecflett@chromium.org>
+
+        IndexedDB: Attributes for a cursor "run past the end" should be undefined.
+        https://bugs.webkit.org/show_bug.cgi?id=83492
+
+        Reviewed by Ojan Vafai.
+
+        * storage/indexeddb/cursor-continue-validity-expected.txt: Added.
+        * storage/indexeddb/cursor-continue-validity.html: Added.
+        * storage/indexeddb/resources/cursor-continue-validity.js: Added.
+        * storage/indexeddb/resources/mozilla/cursors-expected.txt:
+        * storage/indexeddb/resources/shared.js:
+
 2012-04-12  Eric Carlson  <eric.carlson@apple.com>
 
         Media engine should not be told to prepare for playback if media loading is not allowed
diff --git a/LayoutTests/storage/indexeddb/cursor-continue-validity-expected.txt b/LayoutTests/storage/indexeddb/cursor-continue-validity-expected.txt
new file mode 100644 (file)
index 0000000..5d423df
--- /dev/null
@@ -0,0 +1,886 @@
+Test IndexedDB's IDBCursor.continue() behavior when called beyond normal scope.
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+indexedDB = self.indexedDB || self.webkitIndexedDB || self.mozIndexedDB || self.msIndexedDB || self.OIndexedDB;
+
+indexedDB.open('cursor-continue')
+db = event.target.result
+db.setVersion('new version')
+trans = event.target.result
+PASS trans !== null is true
+Deleted all object stores.
+db.createObjectStore('someObjectStore')
+objectStore.createIndex('someIndex', 'x')
+objectStore.add({'x': 0 }, 0)
+objectStore.add({'x': 1 }, 1)
+objectStore.add({'x': 2 }, 2)
+objectStore.add({'x': 3 }, 3)
+objectStore.add({'x': 4 }, 4)
+objectStore.add({'x': 5 }, 5)
+objectStore.add({'x': 6 }, 6)
+objectStore.add({'x': 7 }, 7)
+objectStore.add({'x': 8 }, 8)
+objectStore.add({'x': 9 }, 9)
+objectStore.add({'x': 10 }, 10)
+objectStore.add({'x': 11 }, 11)
+objectStore.add({'x': 12 }, 12)
+objectStore.add({'x': 13 }, 13)
+objectStore.add({'x': 14 }, 14)
+objectStore.add({'x': 15 }, 15)
+objectStore.add({'x': 16 }, 16)
+objectStore.add({'x': 17 }, 17)
+objectStore.add({'x': 18 }, 18)
+objectStore.add({'x': 19 }, 19)
+objectStore.add({'x': 20 }, 20)
+objectStore.add({'x': 21 }, 21)
+objectStore.add({'x': 22 }, 22)
+objectStore.add({'x': 23 }, 23)
+objectStore.add({'x': 24 }, 24)
+continueTest()
+
+Checking objectStore
+====================
+indexObject.openCursor(null, IDBCursor.NEXT)
+self.continueValue = 0
+doubleContinueCallback()
+cursor = event.target.result
+Checking value at 0
+PASS cursor.key is 0
+PASS cursor.value.x is 0
+cursor.continue()
+PASS cursor.key is 0
+PASS cursor.value.x is 0
+Expecting exception from cursor.continue()
+PASS Exception was thrown.
+PASS code is DOMException.INVALID_STATE_ERR
+PASS cursor.key is 0
+PASS cursor.value.x is 0
+self.continueValue++;
+doubleContinueCallback()
+cursor = event.target.result
+Checking value at 1
+PASS cursor.key is 1
+PASS cursor.value.x is 1
+cursor.continue()
+PASS cursor.key is 1
+PASS cursor.value.x is 1
+Expecting exception from cursor.continue()
+PASS Exception was thrown.
+PASS code is DOMException.INVALID_STATE_ERR
+PASS cursor.key is 1
+PASS cursor.value.x is 1
+self.continueValue++;
+doubleContinueCallback()
+cursor = event.target.result
+Checking value at 2
+PASS cursor.key is 2
+PASS cursor.value.x is 2
+cursor.continue()
+PASS cursor.key is 2
+PASS cursor.value.x is 2
+Expecting exception from cursor.continue()
+PASS Exception was thrown.
+PASS code is DOMException.INVALID_STATE_ERR
+PASS cursor.key is 2
+PASS cursor.value.x is 2
+self.continueValue++;
+doubleContinueCallback()
+cursor = event.target.result
+Checking value at 3
+PASS cursor.key is 3
+PASS cursor.value.x is 3
+cursor.continue()
+PASS cursor.key is 3
+PASS cursor.value.x is 3
+Expecting exception from cursor.continue()
+PASS Exception was thrown.
+PASS code is DOMException.INVALID_STATE_ERR
+PASS cursor.key is 3
+PASS cursor.value.x is 3
+self.continueValue++;
+doubleContinueCallback()
+cursor = event.target.result
+Checking value at 4
+PASS cursor.key is 4
+PASS cursor.value.x is 4
+cursor.continue()
+PASS cursor.key is 4
+PASS cursor.value.x is 4
+Expecting exception from cursor.continue()
+PASS Exception was thrown.
+PASS code is DOMException.INVALID_STATE_ERR
+PASS cursor.key is 4
+PASS cursor.value.x is 4
+self.continueValue++;
+doubleContinueCallback()
+cursor = event.target.result
+Checking value at 5
+PASS cursor.key is 5
+PASS cursor.value.x is 5
+cursor.continue()
+PASS cursor.key is 5
+PASS cursor.value.x is 5
+Expecting exception from cursor.continue()
+PASS Exception was thrown.
+PASS code is DOMException.INVALID_STATE_ERR
+PASS cursor.key is 5
+PASS cursor.value.x is 5
+self.continueValue++;
+doubleContinueCallback()
+cursor = event.target.result
+Checking value at 6
+PASS cursor.key is 6
+PASS cursor.value.x is 6
+cursor.continue()
+PASS cursor.key is 6
+PASS cursor.value.x is 6
+Expecting exception from cursor.continue()
+PASS Exception was thrown.
+PASS code is DOMException.INVALID_STATE_ERR
+PASS cursor.key is 6
+PASS cursor.value.x is 6
+self.continueValue++;
+doubleContinueCallback()
+cursor = event.target.result
+Checking value at 7
+PASS cursor.key is 7
+PASS cursor.value.x is 7
+cursor.continue()
+PASS cursor.key is 7
+PASS cursor.value.x is 7
+Expecting exception from cursor.continue()
+PASS Exception was thrown.
+PASS code is DOMException.INVALID_STATE_ERR
+PASS cursor.key is 7
+PASS cursor.value.x is 7
+self.continueValue++;
+doubleContinueCallback()
+cursor = event.target.result
+Checking value at 8
+PASS cursor.key is 8
+PASS cursor.value.x is 8
+cursor.continue()
+PASS cursor.key is 8
+PASS cursor.value.x is 8
+Expecting exception from cursor.continue()
+PASS Exception was thrown.
+PASS code is DOMException.INVALID_STATE_ERR
+PASS cursor.key is 8
+PASS cursor.value.x is 8
+self.continueValue++;
+doubleContinueCallback()
+cursor = event.target.result
+Checking value at 9
+PASS cursor.key is 9
+PASS cursor.value.x is 9
+cursor.continue()
+PASS cursor.key is 9
+PASS cursor.value.x is 9
+Expecting exception from cursor.continue()
+PASS Exception was thrown.
+PASS code is DOMException.INVALID_STATE_ERR
+PASS cursor.key is 9
+PASS cursor.value.x is 9
+self.continueValue++;
+doubleContinueCallback()
+cursor = event.target.result
+Checking value at 10
+PASS cursor.key is 10
+PASS cursor.value.x is 10
+cursor.continue()
+PASS cursor.key is 10
+PASS cursor.value.x is 10
+Expecting exception from cursor.continue()
+PASS Exception was thrown.
+PASS code is DOMException.INVALID_STATE_ERR
+PASS cursor.key is 10
+PASS cursor.value.x is 10
+self.continueValue++;
+doubleContinueCallback()
+cursor = event.target.result
+Checking value at 11
+PASS cursor.key is 11
+PASS cursor.value.x is 11
+cursor.continue()
+PASS cursor.key is 11
+PASS cursor.value.x is 11
+Expecting exception from cursor.continue()
+PASS Exception was thrown.
+PASS code is DOMException.INVALID_STATE_ERR
+PASS cursor.key is 11
+PASS cursor.value.x is 11
+self.continueValue++;
+doubleContinueCallback()
+cursor = event.target.result
+Checking value at 12
+PASS cursor.key is 12
+PASS cursor.value.x is 12
+cursor.continue()
+PASS cursor.key is 12
+PASS cursor.value.x is 12
+Expecting exception from cursor.continue()
+PASS Exception was thrown.
+PASS code is DOMException.INVALID_STATE_ERR
+PASS cursor.key is 12
+PASS cursor.value.x is 12
+self.continueValue++;
+doubleContinueCallback()
+cursor = event.target.result
+Checking value at 13
+PASS cursor.key is 13
+PASS cursor.value.x is 13
+cursor.continue()
+PASS cursor.key is 13
+PASS cursor.value.x is 13
+Expecting exception from cursor.continue()
+PASS Exception was thrown.
+PASS code is DOMException.INVALID_STATE_ERR
+PASS cursor.key is 13
+PASS cursor.value.x is 13
+self.continueValue++;
+doubleContinueCallback()
+cursor = event.target.result
+Checking value at 14
+PASS cursor.key is 14
+PASS cursor.value.x is 14
+cursor.continue()
+PASS cursor.key is 14
+PASS cursor.value.x is 14
+Expecting exception from cursor.continue()
+PASS Exception was thrown.
+PASS code is DOMException.INVALID_STATE_ERR
+PASS cursor.key is 14
+PASS cursor.value.x is 14
+self.continueValue++;
+doubleContinueCallback()
+cursor = event.target.result
+Checking value at 15
+PASS cursor.key is 15
+PASS cursor.value.x is 15
+cursor.continue()
+PASS cursor.key is 15
+PASS cursor.value.x is 15
+Expecting exception from cursor.continue()
+PASS Exception was thrown.
+PASS code is DOMException.INVALID_STATE_ERR
+PASS cursor.key is 15
+PASS cursor.value.x is 15
+self.continueValue++;
+doubleContinueCallback()
+cursor = event.target.result
+Checking value at 16
+PASS cursor.key is 16
+PASS cursor.value.x is 16
+cursor.continue()
+PASS cursor.key is 16
+PASS cursor.value.x is 16
+Expecting exception from cursor.continue()
+PASS Exception was thrown.
+PASS code is DOMException.INVALID_STATE_ERR
+PASS cursor.key is 16
+PASS cursor.value.x is 16
+self.continueValue++;
+doubleContinueCallback()
+cursor = event.target.result
+Checking value at 17
+PASS cursor.key is 17
+PASS cursor.value.x is 17
+cursor.continue()
+PASS cursor.key is 17
+PASS cursor.value.x is 17
+Expecting exception from cursor.continue()
+PASS Exception was thrown.
+PASS code is DOMException.INVALID_STATE_ERR
+PASS cursor.key is 17
+PASS cursor.value.x is 17
+self.continueValue++;
+doubleContinueCallback()
+cursor = event.target.result
+Checking value at 18
+PASS cursor.key is 18
+PASS cursor.value.x is 18
+cursor.continue()
+PASS cursor.key is 18
+PASS cursor.value.x is 18
+Expecting exception from cursor.continue()
+PASS Exception was thrown.
+PASS code is DOMException.INVALID_STATE_ERR
+PASS cursor.key is 18
+PASS cursor.value.x is 18
+self.continueValue++;
+doubleContinueCallback()
+cursor = event.target.result
+Checking value at 19
+PASS cursor.key is 19
+PASS cursor.value.x is 19
+cursor.continue()
+PASS cursor.key is 19
+PASS cursor.value.x is 19
+Expecting exception from cursor.continue()
+PASS Exception was thrown.
+PASS code is DOMException.INVALID_STATE_ERR
+PASS cursor.key is 19
+PASS cursor.value.x is 19
+self.continueValue++;
+doubleContinueCallback()
+cursor = event.target.result
+Checking value at 20
+PASS cursor.key is 20
+PASS cursor.value.x is 20
+cursor.continue()
+PASS cursor.key is 20
+PASS cursor.value.x is 20
+Expecting exception from cursor.continue()
+PASS Exception was thrown.
+PASS code is DOMException.INVALID_STATE_ERR
+PASS cursor.key is 20
+PASS cursor.value.x is 20
+self.continueValue++;
+doubleContinueCallback()
+cursor = event.target.result
+Checking value at 21
+PASS cursor.key is 21
+PASS cursor.value.x is 21
+cursor.continue()
+PASS cursor.key is 21
+PASS cursor.value.x is 21
+Expecting exception from cursor.continue()
+PASS Exception was thrown.
+PASS code is DOMException.INVALID_STATE_ERR
+PASS cursor.key is 21
+PASS cursor.value.x is 21
+self.continueValue++;
+doubleContinueCallback()
+cursor = event.target.result
+Checking value at 22
+PASS cursor.key is 22
+PASS cursor.value.x is 22
+cursor.continue()
+PASS cursor.key is 22
+PASS cursor.value.x is 22
+Expecting exception from cursor.continue()
+PASS Exception was thrown.
+PASS code is DOMException.INVALID_STATE_ERR
+PASS cursor.key is 22
+PASS cursor.value.x is 22
+self.continueValue++;
+doubleContinueCallback()
+cursor = event.target.result
+Checking value at 23
+PASS cursor.key is 23
+PASS cursor.value.x is 23
+cursor.continue()
+PASS cursor.key is 23
+PASS cursor.value.x is 23
+Expecting exception from cursor.continue()
+PASS Exception was thrown.
+PASS code is DOMException.INVALID_STATE_ERR
+PASS cursor.key is 23
+PASS cursor.value.x is 23
+self.continueValue++;
+doubleContinueCallback()
+cursor = event.target.result
+Checking value at 24
+PASS cursor.key is 24
+PASS cursor.value.x is 24
+cursor.continue()
+PASS cursor.key is 24
+PASS cursor.value.x is 24
+Expecting exception from cursor.continue()
+PASS Exception was thrown.
+PASS code is DOMException.INVALID_STATE_ERR
+PASS cursor.key is 24
+PASS cursor.value.x is 24
+self.continueValue++;
+doubleContinueCallback()
+cursor = event.target.result
+continueIndexTest()
+
+Checking index
+==============
+indexObject.openCursor(null, IDBCursor.NEXT)
+self.continueValue = 0
+cursor = event.target.result
+Checking value at 0
+PASS cursor.key is 0
+PASS cursor.value.x is 0
+cursor.continue()
+PASS cursor.key is 0
+PASS cursor.value.x is 0
+Expecting exception from cursor.continue()
+PASS Exception was thrown.
+PASS code is DOMException.INVALID_STATE_ERR
+PASS cursor.key is 0
+PASS cursor.value.x is 0
+self.continueValue++;
+cursor = event.target.result
+Checking value at 1
+PASS cursor.key is 1
+PASS cursor.value.x is 1
+cursor.continue()
+PASS cursor.key is 1
+PASS cursor.value.x is 1
+Expecting exception from cursor.continue()
+PASS Exception was thrown.
+PASS code is DOMException.INVALID_STATE_ERR
+PASS cursor.key is 1
+PASS cursor.value.x is 1
+self.continueValue++;
+cursor = event.target.result
+Checking value at 2
+PASS cursor.key is 2
+PASS cursor.value.x is 2
+cursor.continue()
+PASS cursor.key is 2
+PASS cursor.value.x is 2
+Expecting exception from cursor.continue()
+PASS Exception was thrown.
+PASS code is DOMException.INVALID_STATE_ERR
+PASS cursor.key is 2
+PASS cursor.value.x is 2
+self.continueValue++;
+cursor = event.target.result
+Checking value at 3
+PASS cursor.key is 3
+PASS cursor.value.x is 3
+cursor.continue()
+PASS cursor.key is 3
+PASS cursor.value.x is 3
+Expecting exception from cursor.continue()
+PASS Exception was thrown.
+PASS code is DOMException.INVALID_STATE_ERR
+PASS cursor.key is 3
+PASS cursor.value.x is 3
+self.continueValue++;
+cursor = event.target.result
+Checking value at 4
+PASS cursor.key is 4
+PASS cursor.value.x is 4
+cursor.continue()
+PASS cursor.key is 4
+PASS cursor.value.x is 4
+Expecting exception from cursor.continue()
+PASS Exception was thrown.
+PASS code is DOMException.INVALID_STATE_ERR
+PASS cursor.key is 4
+PASS cursor.value.x is 4
+self.continueValue++;
+cursor = event.target.result
+Checking value at 5
+PASS cursor.key is 5
+PASS cursor.value.x is 5
+cursor.continue()
+PASS cursor.key is 5
+PASS cursor.value.x is 5
+Expecting exception from cursor.continue()
+PASS Exception was thrown.
+PASS code is DOMException.INVALID_STATE_ERR
+PASS cursor.key is 5
+PASS cursor.value.x is 5
+self.continueValue++;
+cursor = event.target.result
+Checking value at 6
+PASS cursor.key is 6
+PASS cursor.value.x is 6
+cursor.continue()
+PASS cursor.key is 6
+PASS cursor.value.x is 6
+Expecting exception from cursor.continue()
+PASS Exception was thrown.
+PASS code is DOMException.INVALID_STATE_ERR
+PASS cursor.key is 6
+PASS cursor.value.x is 6
+self.continueValue++;
+cursor = event.target.result
+Checking value at 7
+PASS cursor.key is 7
+PASS cursor.value.x is 7
+cursor.continue()
+PASS cursor.key is 7
+PASS cursor.value.x is 7
+Expecting exception from cursor.continue()
+PASS Exception was thrown.
+PASS code is DOMException.INVALID_STATE_ERR
+PASS cursor.key is 7
+PASS cursor.value.x is 7
+self.continueValue++;
+cursor = event.target.result
+Checking value at 8
+PASS cursor.key is 8
+PASS cursor.value.x is 8
+cursor.continue()
+PASS cursor.key is 8
+PASS cursor.value.x is 8
+Expecting exception from cursor.continue()
+PASS Exception was thrown.
+PASS code is DOMException.INVALID_STATE_ERR
+PASS cursor.key is 8
+PASS cursor.value.x is 8
+self.continueValue++;
+cursor = event.target.result
+Checking value at 9
+PASS cursor.key is 9
+PASS cursor.value.x is 9
+cursor.continue()
+PASS cursor.key is 9
+PASS cursor.value.x is 9
+Expecting exception from cursor.continue()
+PASS Exception was thrown.
+PASS code is DOMException.INVALID_STATE_ERR
+PASS cursor.key is 9
+PASS cursor.value.x is 9
+self.continueValue++;
+cursor = event.target.result
+Checking value at 10
+PASS cursor.key is 10
+PASS cursor.value.x is 10
+cursor.continue()
+PASS cursor.key is 10
+PASS cursor.value.x is 10
+Expecting exception from cursor.continue()
+PASS Exception was thrown.
+PASS code is DOMException.INVALID_STATE_ERR
+PASS cursor.key is 10
+PASS cursor.value.x is 10
+self.continueValue++;
+cursor = event.target.result
+Checking value at 11
+PASS cursor.key is 11
+PASS cursor.value.x is 11
+cursor.continue()
+PASS cursor.key is 11
+PASS cursor.value.x is 11
+Expecting exception from cursor.continue()
+PASS Exception was thrown.
+PASS code is DOMException.INVALID_STATE_ERR
+PASS cursor.key is 11
+PASS cursor.value.x is 11
+self.continueValue++;
+cursor = event.target.result
+Checking value at 12
+PASS cursor.key is 12
+PASS cursor.value.x is 12
+cursor.continue()
+PASS cursor.key is 12
+PASS cursor.value.x is 12
+Expecting exception from cursor.continue()
+PASS Exception was thrown.
+PASS code is DOMException.INVALID_STATE_ERR
+PASS cursor.key is 12
+PASS cursor.value.x is 12
+self.continueValue++;
+cursor = event.target.result
+Checking value at 13
+PASS cursor.key is 13
+PASS cursor.value.x is 13
+cursor.continue()
+PASS cursor.key is 13
+PASS cursor.value.x is 13
+Expecting exception from cursor.continue()
+PASS Exception was thrown.
+PASS code is DOMException.INVALID_STATE_ERR
+PASS cursor.key is 13
+PASS cursor.value.x is 13
+self.continueValue++;
+cursor = event.target.result
+Checking value at 14
+PASS cursor.key is 14
+PASS cursor.value.x is 14
+cursor.continue()
+PASS cursor.key is 14
+PASS cursor.value.x is 14
+Expecting exception from cursor.continue()
+PASS Exception was thrown.
+PASS code is DOMException.INVALID_STATE_ERR
+PASS cursor.key is 14
+PASS cursor.value.x is 14
+self.continueValue++;
+cursor = event.target.result
+Checking value at 15
+PASS cursor.key is 15
+PASS cursor.value.x is 15
+cursor.continue()
+PASS cursor.key is 15
+PASS cursor.value.x is 15
+Expecting exception from cursor.continue()
+PASS Exception was thrown.
+PASS code is DOMException.INVALID_STATE_ERR
+PASS cursor.key is 15
+PASS cursor.value.x is 15
+self.continueValue++;
+cursor = event.target.result
+Checking value at 16
+PASS cursor.key is 16
+PASS cursor.value.x is 16
+cursor.continue()
+PASS cursor.key is 16
+PASS cursor.value.x is 16
+Expecting exception from cursor.continue()
+PASS Exception was thrown.
+PASS code is DOMException.INVALID_STATE_ERR
+PASS cursor.key is 16
+PASS cursor.value.x is 16
+self.continueValue++;
+cursor = event.target.result
+Checking value at 17
+PASS cursor.key is 17
+PASS cursor.value.x is 17
+cursor.continue()
+PASS cursor.key is 17
+PASS cursor.value.x is 17
+Expecting exception from cursor.continue()
+PASS Exception was thrown.
+PASS code is DOMException.INVALID_STATE_ERR
+PASS cursor.key is 17
+PASS cursor.value.x is 17
+self.continueValue++;
+cursor = event.target.result
+Checking value at 18
+PASS cursor.key is 18
+PASS cursor.value.x is 18
+cursor.continue()
+PASS cursor.key is 18
+PASS cursor.value.x is 18
+Expecting exception from cursor.continue()
+PASS Exception was thrown.
+PASS code is DOMException.INVALID_STATE_ERR
+PASS cursor.key is 18
+PASS cursor.value.x is 18
+self.continueValue++;
+cursor = event.target.result
+Checking value at 19
+PASS cursor.key is 19
+PASS cursor.value.x is 19
+cursor.continue()
+PASS cursor.key is 19
+PASS cursor.value.x is 19
+Expecting exception from cursor.continue()
+PASS Exception was thrown.
+PASS code is DOMException.INVALID_STATE_ERR
+PASS cursor.key is 19
+PASS cursor.value.x is 19
+self.continueValue++;
+cursor = event.target.result
+Checking value at 20
+PASS cursor.key is 20
+PASS cursor.value.x is 20
+cursor.continue()
+PASS cursor.key is 20
+PASS cursor.value.x is 20
+Expecting exception from cursor.continue()
+PASS Exception was thrown.
+PASS code is DOMException.INVALID_STATE_ERR
+PASS cursor.key is 20
+PASS cursor.value.x is 20
+self.continueValue++;
+cursor = event.target.result
+Checking value at 21
+PASS cursor.key is 21
+PASS cursor.value.x is 21
+cursor.continue()
+PASS cursor.key is 21
+PASS cursor.value.x is 21
+Expecting exception from cursor.continue()
+PASS Exception was thrown.
+PASS code is DOMException.INVALID_STATE_ERR
+PASS cursor.key is 21
+PASS cursor.value.x is 21
+self.continueValue++;
+cursor = event.target.result
+Checking value at 22
+PASS cursor.key is 22
+PASS cursor.value.x is 22
+cursor.continue()
+PASS cursor.key is 22
+PASS cursor.value.x is 22
+Expecting exception from cursor.continue()
+PASS Exception was thrown.
+PASS code is DOMException.INVALID_STATE_ERR
+PASS cursor.key is 22
+PASS cursor.value.x is 22
+self.continueValue++;
+cursor = event.target.result
+Checking value at 23
+PASS cursor.key is 23
+PASS cursor.value.x is 23
+cursor.continue()
+PASS cursor.key is 23
+PASS cursor.value.x is 23
+Expecting exception from cursor.continue()
+PASS Exception was thrown.
+PASS code is DOMException.INVALID_STATE_ERR
+PASS cursor.key is 23
+PASS cursor.value.x is 23
+self.continueValue++;
+cursor = event.target.result
+Checking value at 24
+PASS cursor.key is 24
+PASS cursor.value.x is 24
+cursor.continue()
+PASS cursor.key is 24
+PASS cursor.value.x is 24
+Expecting exception from cursor.continue()
+PASS Exception was thrown.
+PASS code is DOMException.INVALID_STATE_ERR
+PASS cursor.key is 24
+PASS cursor.value.x is 24
+self.continueValue++;
+cursor = event.target.result
+testModifyContinueOrder()
+
+Checking modification
+=====================
+indexObject.openCursor(null, IDBCursor.NEXT)
+self.continueValue = 0
+cursor = event.target.result
+cursor.continue()
+cursor = event.target.result
+cursor.update({ x: 100 + self.continueValue })
+cursor.continue()
+cursor = event.target.result
+cursor.delete()
+cursor.continue()
+cursor = event.target.result
+cursor.update({ x: 100 + self.continueValue })
+cursor.continue()
+cursor = event.target.result
+cursor.continue()
+Expecting exception from cursor.update({ x: 100 + self.continueValue})
+PASS Exception was thrown.
+PASS code is DOMException.INVALID_STATE_ERR
+cursor = event.target.result
+cursor.update({ x: 100 + self.continueValue })
+cursor.continue()
+cursor = event.target.result
+cursor.continue()
+Expecting exception from cursor.delete()
+PASS Exception was thrown.
+PASS code is DOMException.INVALID_STATE_ERR
+cursor = event.target.result
+cursor.update({ x: 100 + self.continueValue })
+cursor.continue()
+cursor = event.target.result
+cursor.delete()
+cursor.continue()
+cursor = event.target.result
+cursor.update({ x: 100 + self.continueValue })
+cursor.continue()
+cursor = event.target.result
+cursor.continue()
+cursor = event.target.result
+cursor.update({ x: 100 + self.continueValue })
+cursor.continue()
+cursor = event.target.result
+cursor.continue()
+cursor = event.target.result
+cursor.update({ x: 100 + self.continueValue })
+cursor.continue()
+cursor = event.target.result
+cursor.delete()
+cursor.continue()
+cursor = event.target.result
+cursor.update({ x: 100 + self.continueValue })
+cursor.continue()
+cursor = event.target.result
+cursor.continue()
+cursor = event.target.result
+cursor.update({ x: 100 + self.continueValue })
+cursor.continue()
+cursor = event.target.result
+cursor.continue()
+cursor = event.target.result
+cursor.update({ x: 100 + self.continueValue })
+cursor.continue()
+cursor = event.target.result
+cursor.delete()
+cursor.continue()
+cursor = event.target.result
+cursor.update({ x: 100 + self.continueValue })
+cursor.continue()
+cursor = event.target.result
+cursor.continue()
+cursor = event.target.result
+cursor.update({ x: 100 + self.continueValue })
+cursor.continue()
+cursor = event.target.result
+cursor.continue()
+Expecting exception from cursor.update({ x: 100 + self.continueValue})
+PASS Exception was thrown.
+PASS code is DOMException.INVALID_STATE_ERR
+cursor = event.target.result
+cursor.update({ x: 100 + self.continueValue })
+cursor.continue()
+cursor = event.target.result
+cursor.delete()
+cursor.continue()
+cursor = event.target.result
+cursor.update({ x: 100 + self.continueValue })
+cursor.continue()
+cursor = event.target.result
+cursor.continue()
+cursor = event.target.result
+cursor.update({ x: 100 + self.continueValue })
+cursor.continue()
+cursor = event.target.result
+cursor.continue()
+cursor = event.target.result
+cursor.update({ x: 100 + self.continueValue })
+cursor.continue()
+cursor = event.target.result
+cursor.delete()
+cursor.continue()
+cursor = event.target.result
+cursor.update({ x: 100 + self.continueValue })
+cursor.continue()
+cursor = event.target.result
+cursor.continue()
+Expecting exception from cursor.update({ x: 100 + self.continueValue})
+PASS Exception was thrown.
+PASS code is DOMException.INVALID_STATE_ERR
+cursor = event.target.result
+cursor.update({ x: 100 + self.continueValue })
+cursor.continue()
+cursor = event.target.result
+cursor.continue()
+cursor = event.target.result
+cursor.update({ x: 100 + self.continueValue })
+cursor.continue()
+cursor = event.target.result
+cursor.delete()
+cursor.continue()
+cursor = event.target.result
+cursor.update({ x: 100 + self.continueValue })
+cursor.continue()
+cursor = event.target.result
+cursor.continue()
+cursor = event.target.result
+cursor.update({ x: 100 + self.continueValue })
+cursor.continue()
+cursor = event.target.result
+cursor.continue()
+cursor = event.target.result
+cursor.update({ x: 100 + self.continueValue })
+cursor.continue()
+cursor = event.target.result
+cursor.delete()
+cursor.continue()
+cursor = event.target.result
+cursor.update({ x: 100 + self.continueValue })
+cursor.continue()
+cursor = event.target.result
+cursor.continue()
+cursor = event.target.result
+cursor.update({ x: 100 + self.continueValue })
+cursor.continue()
+cursor = event.target.result
+cursor.continue()
+Expecting exception from cursor.delete()
+PASS Exception was thrown.
+PASS code is DOMException.INVALID_STATE_ERR
+cursor = event.target.result
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
diff --git a/LayoutTests/storage/indexeddb/cursor-continue-validity.html b/LayoutTests/storage/indexeddb/cursor-continue-validity.html
new file mode 100644 (file)
index 0000000..c758382
--- /dev/null
@@ -0,0 +1,11 @@
+<!DOCTYPE html>
+<html>
+<head>
+<script src="../../fast/js/resources/js-test-pre.js"></script>
+<script src="resources/shared.js"></script>
+</head>
+<body>
+<script src="resources/cursor-continue-validity.js"></script>
+<script src="../../fast/js/resources/js-test-post.js"></script>
+</body>
+</html>
index 05681c6..66e8ae7 100644 (file)
@@ -42,7 +42,7 @@ PASS cursor.value is 'foo'
 cursor.continue();
 Expecting exception from cursor.continue();
 PASS Exception was thrown.
-PASS code is IDBDatabaseException.NOT_ALLOWED_ERR
+PASS code is DOMException.INVALID_STATE_ERR
 PASS cursor.key is sortedKeys[keyIndex]
 PASS cursor.primaryKey is sortedKeys[keyIndex]
 PASS cursor.value is 'foo'
@@ -54,7 +54,7 @@ PASS cursor.value is 'foo'
 cursor.continue();
 Expecting exception from cursor.continue();
 PASS Exception was thrown.
-PASS code is IDBDatabaseException.NOT_ALLOWED_ERR
+PASS code is DOMException.INVALID_STATE_ERR
 PASS cursor.key is sortedKeys[keyIndex]
 PASS cursor.primaryKey is sortedKeys[keyIndex]
 PASS cursor.value is 'foo'
@@ -66,7 +66,7 @@ PASS cursor.value is 'foo'
 cursor.continue();
 Expecting exception from cursor.continue();
 PASS Exception was thrown.
-PASS code is IDBDatabaseException.NOT_ALLOWED_ERR
+PASS code is DOMException.INVALID_STATE_ERR
 PASS cursor.key is sortedKeys[keyIndex]
 PASS cursor.primaryKey is sortedKeys[keyIndex]
 PASS cursor.value is 'foo'
@@ -78,7 +78,7 @@ PASS cursor.value is 'foo'
 cursor.continue();
 Expecting exception from cursor.continue();
 PASS Exception was thrown.
-PASS code is IDBDatabaseException.NOT_ALLOWED_ERR
+PASS code is DOMException.INVALID_STATE_ERR
 PASS cursor.key is sortedKeys[keyIndex]
 PASS cursor.primaryKey is sortedKeys[keyIndex]
 PASS cursor.value is 'foo'
@@ -90,7 +90,7 @@ PASS cursor.value is 'foo'
 cursor.continue();
 Expecting exception from cursor.continue();
 PASS Exception was thrown.
-PASS code is IDBDatabaseException.NOT_ALLOWED_ERR
+PASS code is DOMException.INVALID_STATE_ERR
 PASS cursor.key is sortedKeys[keyIndex]
 PASS cursor.primaryKey is sortedKeys[keyIndex]
 PASS cursor.value is 'foo'
@@ -102,7 +102,7 @@ PASS cursor.value is 'foo'
 cursor.continue();
 Expecting exception from cursor.continue();
 PASS Exception was thrown.
-PASS code is IDBDatabaseException.NOT_ALLOWED_ERR
+PASS code is DOMException.INVALID_STATE_ERR
 PASS cursor.key is sortedKeys[keyIndex]
 PASS cursor.primaryKey is sortedKeys[keyIndex]
 PASS cursor.value is 'foo'
@@ -114,7 +114,7 @@ PASS cursor.value is 'foo'
 cursor.continue();
 Expecting exception from cursor.continue();
 PASS Exception was thrown.
-PASS code is IDBDatabaseException.NOT_ALLOWED_ERR
+PASS code is DOMException.INVALID_STATE_ERR
 PASS cursor.key is sortedKeys[keyIndex]
 PASS cursor.primaryKey is sortedKeys[keyIndex]
 PASS cursor.value is 'foo'
@@ -126,7 +126,7 @@ PASS cursor.value is 'foo'
 cursor.continue();
 Expecting exception from cursor.continue();
 PASS Exception was thrown.
-PASS code is IDBDatabaseException.NOT_ALLOWED_ERR
+PASS code is DOMException.INVALID_STATE_ERR
 PASS cursor.key is sortedKeys[keyIndex]
 PASS cursor.primaryKey is sortedKeys[keyIndex]
 PASS cursor.value is 'foo'
@@ -138,7 +138,7 @@ PASS cursor.value is 'foo'
 cursor.continue();
 Expecting exception from cursor.continue();
 PASS Exception was thrown.
-PASS code is IDBDatabaseException.NOT_ALLOWED_ERR
+PASS code is DOMException.INVALID_STATE_ERR
 PASS cursor.key is sortedKeys[keyIndex]
 PASS cursor.primaryKey is sortedKeys[keyIndex]
 PASS cursor.value is 'foo'
@@ -150,7 +150,7 @@ PASS cursor.value is 'foo'
 cursor.continue();
 Expecting exception from cursor.continue();
 PASS Exception was thrown.
-PASS code is IDBDatabaseException.NOT_ALLOWED_ERR
+PASS code is DOMException.INVALID_STATE_ERR
 PASS cursor.key is sortedKeys[keyIndex]
 PASS cursor.primaryKey is sortedKeys[keyIndex]
 PASS cursor.value is 'foo'
index e1de374..fd25382 100644 (file)
@@ -113,7 +113,7 @@ function testGroup6()
         shouldBe("cursor.value", "'foo'");
 
         evalAndLog("cursor.continue();");
-        evalAndExpectException("cursor.continue();", "IDBDatabaseException.NOT_ALLOWED_ERR");
+        evalAndExpectException("cursor.continue();", "DOMException.INVALID_STATE_ERR");
 
         shouldBe("cursor.key", "sortedKeys[keyIndex]");
         shouldBe("cursor.primaryKey", "sortedKeys[keyIndex]");
diff --git a/LayoutTests/storage/indexeddb/resources/cursor-continue-validity.js b/LayoutTests/storage/indexeddb/resources/cursor-continue-validity.js
new file mode 100644 (file)
index 0000000..279095d
--- /dev/null
@@ -0,0 +1,180 @@
+if (this.importScripts) {
+    importScripts('../../../fast/js/resources/js-test-pre.js');
+    importScripts('shared.js');
+}
+
+description("Test IndexedDB's IDBCursor.continue() behavior when called beyond normal scope.");
+
+var date = new Date();
+
+// we set this pretty high because we get different behavior depending
+// if we're in a pre-fetched state or not
+self.testLength = 25;
+
+function test()
+{
+    removeVendorPrefixes();
+    openDatabase();
+}
+
+function openDatabase()
+{
+    result = evalAndLog("indexedDB.open('cursor-continue')");
+    result.onsuccess = setVersion;
+    result.onerror = unexpectedErrorCallback;
+}
+
+function setVersion()
+{
+    self.db = evalAndLog("db = event.target.result");
+
+    result = evalAndLog("db.setVersion('new version')");
+    result.onsuccess = deleteExisting;
+    result.onerror = unexpectedErrorCallback;
+}
+
+function deleteExisting()
+{
+    self.trans = evalAndLog("trans = event.target.result");
+    shouldBeTrue("trans !== null");
+    trans.onabort = unexpectedAbortCallback;
+
+    deleteAllObjectStores(db);
+
+    self.objectStore = evalAndLog("db.createObjectStore('someObjectStore')");
+    self.indexObject = evalAndLog("objectStore.createIndex('someIndex', 'x')");
+    self.nextToAdd = 0;
+    addData();
+}
+
+function addData()
+{
+    for (var i=0; i<self.testLength; i++) {
+        evalAndLog("objectStore.add({'x': " + i + " }, " + i + ")");
+    }
+    evalAndLog("continueTest()");
+}
+
+function continueTest()
+{
+    debug("");
+    debug("Checking objectStore");
+    debug("====================");
+    var request = evalAndLog("indexObject.openCursor(null, IDBCursor.NEXT)");
+    evalAndLog("self.continueValue = 0");
+    request.onsuccess = evalAndLogCallback("doubleContinueCallback()");
+    request.onerror = unexpectedErrorCallback;
+    self.stage = 0;
+}
+
+function doubleContinueCallback()
+{
+    evalAndLog("cursor = event.target.result");
+    if (cursor) {
+        debug("Checking value at " + self.continueValue);
+        // data should be valid before calling continue()
+        shouldBe("cursor.key", "" + self.continueValue);
+        shouldBe("cursor.value.x", "" + self.continueValue);
+
+        // Data should not change during iteration, even if continue() is called and extra time.
+        evalAndLog("cursor.continue()");
+        shouldBe("cursor.key", "" + self.continueValue);
+        shouldBe("cursor.value.x", "" + self.continueValue);
+
+        // Even if continue is called more than once, the value still shouldn't change.
+        evalAndExpectException("cursor.continue()", "DOMException.INVALID_STATE_ERR");
+        shouldBe("cursor.key", "" + self.continueValue);
+        shouldBe("cursor.value.x", "" + self.continueValue);
+        evalAndLog("self.continueValue++;");
+    } else {
+        evalAndLog("continueIndexTest()");
+    }
+}
+
+function continueIndexTest()
+{
+    debug("");
+    debug("Checking index");
+    debug("==============");
+    var request = evalAndLog("indexObject.openCursor(null, IDBCursor.NEXT)");
+    evalAndLog("self.continueValue = 0");
+    request.onsuccess = doubleContinueIndexCallback;
+    request.onerror = unexpectedErrorCallback;
+    self.stage = 0;
+}
+
+function doubleContinueIndexCallback()
+{
+    evalAndLog("cursor = event.target.result");
+    if (cursor) {
+        debug("Checking value at " + self.continueValue);
+        // data should be valid before calling continue()
+        shouldBe("cursor.key", "" + self.continueValue);
+        shouldBe("cursor.value.x", "" + self.continueValue);
+
+        // Data should not change during iteration, even if continue() is called and extra time.
+        evalAndLog("cursor.continue()");
+        shouldBe("cursor.key", "" + self.continueValue);
+        shouldBe("cursor.value.x", "" + self.continueValue);
+
+        // Even if continue is called more than once, the value still shouldn't change.
+        evalAndExpectException("cursor.continue()", "DOMException.INVALID_STATE_ERR");
+        shouldBe("cursor.key", "" + self.continueValue);
+        shouldBe("cursor.value.x", "" + self.continueValue);
+        evalAndLog("self.continueValue++;");
+    } else {
+        evalAndLog("testModifyContinueOrder()");
+    }
+
+}
+
+// Note: This mutates the data
+function testModifyContinueOrder()
+{
+    debug("");
+    debug("Checking modification");
+    debug("=====================");
+    var request = evalAndLog("indexObject.openCursor(null, IDBCursor.NEXT)");
+    evalAndLog("self.continueValue = 0");
+    request.onsuccess = modifyContinueOrderCallback;
+    request.onerror = unexpectedErrorCallback;
+    self.stage = 0;
+}
+
+function modifyContinueOrderCallback()
+{
+    cursor = evalAndLog("cursor = event.target.result");
+
+    self.continueValue++;
+    if (cursor) {
+        // we sprinkle these checks across the dataset, to sample
+        // behavior against pre-fetching. Make sure to use prime
+        // numbers for these checks to avoid overlap.
+        if (self.continueValue % 2 == 0) {
+            // it's ok to call update() and then continue..
+            evalAndLog("cursor.update({ x: 100 + self.continueValue })");
+            evalAndLog("cursor.continue()");
+        } else if (self.continueValue % 3 == 0) {
+            // it's ok to call delete() and then continue
+            evalAndLog("cursor.delete()");
+            evalAndLog("cursor.continue()");
+        } else if (self.continueValue % 5 == 0) {
+            // it's NOT ok to call continue and then update
+            evalAndLog("cursor.continue()");
+            evalAndExpectException("cursor.update({ x: 100 + self.continueValue})",
+                                   "DOMException.INVALID_STATE_ERR");
+        } else if (self.continueValue % 7 == 0) {
+            // it's NOT ok to call continue and then delete
+            evalAndLog("cursor.continue()");
+            evalAndExpectException("cursor.delete()",
+                                   "DOMException.INVALID_STATE_ERR");
+        } else {
+            evalAndLog("cursor.continue()");
+        }
+
+    } else {
+        finishJSTest();
+    }
+}
+
+test();
index afb2fb8..e4b8d3f 100644 (file)
@@ -99,6 +99,13 @@ function evalAndExpectExceptionClass(cmd, expected)
     }
 }
 
+function evalAndLogCallback(cmd) {
+  function callback() {
+    evalAndLog(cmd);
+  }
+  return callback;
+}
+
 function deleteAllObjectStores(db)
 {
     while (db.objectStoreNames.length)
index 1b9c3a5..df9d73f 100644 (file)
@@ -1,3 +1,41 @@
+2012-04-12  Alec Flett  <alecflett@chromium.org>
+
+        IndexedDB: Attributes for a cursor "run past the end" should be undefined.
+        https://bugs.webkit.org/show_bug.cgi?id=83492
+
+        Reviewed by Ojan Vafai.
+
+        This brings us up to spec with respect to when it's ok to call
+        cursor.continue() and the various behaviors around it:
+
+        If you call it more than once, continue() throws an exception.
+
+        The values of key/primaryKey/value are consistent, whether or not
+        continue has been called, meaning that if continue() results in
+        data being loaded from the backing store, that data does not
+        affect the cursor until the success callback has completed.
+
+        Calls to update() and delete() can happen before continue(), but
+        not afterwards.
+
+        Test: storage/indexeddb/cursor-continue-validity.html
+
+        * Modules/indexeddb/IDBCursor.cpp:
+        (WebCore::IDBCursor::IDBCursor):
+        (WebCore::IDBCursor::key):
+        (WebCore::IDBCursor::primaryKey):
+        (WebCore::IDBCursor::value):
+        (WebCore::IDBCursor::update):
+        (WebCore::IDBCursor::continueFunction):
+        (WebCore::IDBCursor::deleteFunction):
+        (WebCore::IDBCursor::setGotValue):
+        (WebCore):
+        * Modules/indexeddb/IDBCursor.h:
+        (IDBCursor):
+        * Modules/indexeddb/IDBRequest.cpp:
+        (WebCore::IDBRequest::onSuccess):
+        (WebCore::IDBRequest::dispatchEvent):
+
 2012-04-12  Eric Carlson  <eric.carlson@apple.com>
 
         Media engine should not be told to prepare for playback if media loading is not allowed
index 274f995..7387e1b 100644 (file)
@@ -51,6 +51,7 @@ IDBCursor::IDBCursor(PassRefPtr<IDBCursorBackendInterface> backend, IDBRequest*
     , m_source(source)
     , m_transaction(transaction)
     , m_transactionNotifier(transaction, this)
+    , m_gotValue(false)
 {
     ASSERT(m_backend);
     ASSERT(m_request);
@@ -71,19 +72,19 @@ unsigned short IDBCursor::direction() const
 PassRefPtr<IDBKey> IDBCursor::key() const
 {
     IDB_TRACE("IDBCursor::key");
-    return m_backend->key();
+    return m_currentKey;
 }
 
 PassRefPtr<IDBKey> IDBCursor::primaryKey() const
 {
     IDB_TRACE("IDBCursor::primaryKey");
-    return m_backend->primaryKey();
+    return m_currentPrimaryKey;
 }
 
 PassRefPtr<IDBAny> IDBCursor::value() const
 {
     IDB_TRACE("IDBCursor::value");
-    return IDBAny::create(m_backend->value());
+    return m_currentValue;
 }
 
 IDBAny* IDBCursor::source() const
@@ -95,6 +96,10 @@ PassRefPtr<IDBRequest> IDBCursor::update(ScriptExecutionContext* context, PassRe
 {
     IDB_TRACE("IDBCursor::update");
 
+    if (!m_gotValue) {
+        ec = INVALID_STATE_ERR;
+        return 0;
+    }
     RefPtr<SerializedScriptValue> value = prpValue;
     if (value->blobURLs().size() > 0) {
         // FIXME: Add Blob/File/FileList support
@@ -123,10 +128,17 @@ void IDBCursor::continueFunction(PassRefPtr<IDBKey> key, ExceptionCode& ec)
         ec = IDBDatabaseException::TRANSACTION_INACTIVE_ERR;
         return;
     }
+
+    if (!m_gotValue) {
+        ec = INVALID_STATE_ERR;
+        return;
+    }
+
     // FIXME: We're not using the context from when continue was called, which means the callback
     //        will be on the original context openCursor was called on. Is this right?
     if (m_request->resetReadyState(m_transaction.get())) {
         m_request->setCursor(this);
+        m_gotValue = false;
         m_backend->continueFunction(key, m_request, ec);
     } else
         ec = IDBDatabaseException::NOT_ALLOWED_ERR;
@@ -135,6 +147,10 @@ void IDBCursor::continueFunction(PassRefPtr<IDBKey> key, ExceptionCode& ec)
 PassRefPtr<IDBRequest> IDBCursor::deleteFunction(ScriptExecutionContext* context, ExceptionCode& ec)
 {
     IDB_TRACE("IDBCursor::delete");
+    if (!m_gotValue) {
+        ec = INVALID_STATE_ERR;
+        return 0;
+    }
     RefPtr<IDBRequest> request = IDBRequest::create(context, IDBAny::create(this), m_transaction.get());
     m_backend->deleteFunction(request, ec);
     if (ec) {
@@ -156,6 +172,14 @@ void IDBCursor::close()
     m_request.clear();
 }
 
+void IDBCursor::setValueReady()
+{
+    m_currentKey = m_backend->key();
+    m_currentPrimaryKey = m_backend->primaryKey();
+    m_currentValue = IDBAny::create(m_backend->value());
+    m_gotValue = true;
+}
+
 } // namespace WebCore
 
 #endif // ENABLE(INDEXED_DATABASE)
index eb76706..237f272 100644 (file)
@@ -72,6 +72,7 @@ public:
 
     void postSuccessHandlerCallback();
     void close();
+    void setValueReady();
 
 protected:
     IDBCursor(PassRefPtr<IDBCursorBackendInterface>, IDBRequest*, IDBAny* source, IDBTransaction*);
@@ -82,6 +83,12 @@ private:
     RefPtr<IDBAny> m_source;
     RefPtr<IDBTransaction> m_transaction;
     IDBTransaction::OpenCursorNotifier m_transactionNotifier;
+    bool m_gotValue;
+    // These values are held because m_backend may advance while they
+    // are still valid for the current success handlers.
+    RefPtr<IDBKey> m_currentKey;
+    RefPtr<IDBKey> m_currentPrimaryKey;
+    RefPtr<IDBAny> m_currentValue;
 };
 
 } // namespace WebCore
index ec2209a..6dc2a21 100644 (file)
@@ -222,7 +222,6 @@ void IDBRequest::onSuccess(PassRefPtr<IDBCursorBackendInterface> backend)
         cursor = IDBCursor::create(backend, this, m_source.get(), m_transaction.get());
     else
         cursor = IDBCursorWithValue::create(backend, this, m_source.get(), m_transaction.get());
-
     setResultCursor(cursor, m_cursorType);
 
     enqueueEvent(createSuccessEvent());
@@ -365,6 +364,8 @@ bool IDBRequest::dispatchEvent(PassRefPtr<Event> event)
             cursorToNotify = m_result->idbCursor();
         else if (m_result->type() == IDBAny::IDBCursorWithValueType)
             cursorToNotify = m_result->idbCursorWithValue();
+        if (cursorToNotify)
+            cursorToNotify->setValueReady();
     }
 
     // FIXME: When we allow custom event dispatching, this will probably need to change.