Source/WebCore: IndexedDB: fix cursor prefetch crash
authordgrogan@chromium.org <dgrogan@chromium.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Fri, 6 Jan 2012 01:15:29 +0000 (01:15 +0000)
committerdgrogan@chromium.org <dgrogan@chromium.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Fri, 6 Jan 2012 01:15:29 +0000 (01:15 +0000)
http://crbug.com/108071
https://bugs.webkit.org/show_bug.cgi?id=75596

Reviewed by Tony Chang.

Test: storage/indexeddb/prefetch-bugfix-108071.html
Note: DumpRenderTree doesn't exercise the bug, it only occurs in
multi-process chromium.  The layout test will soon be run as a
chromium ui test: http://codereview.chromium.org/9108004

* storage/IDBCursorBackendImpl.cpp:
(WebCore::IDBCursorBackendImpl::IDBCursorBackendImpl):
(WebCore::IDBCursorBackendImpl::~IDBCursorBackendImpl): Destroy
cursors before their objectstores.
(WebCore::IDBCursorBackendImpl::prefetchReset): Don't run continue if
the cursor is closed.
(WebCore::IDBCursorBackendImpl::close): Set a closed flag.
* storage/IDBCursorBackendImpl.h:

LayoutTests: IndexedDB: fix cursor prefetch crash
https://bugs.webkit.org/show_bug.cgi?id=75596

Reviewed by Tony Chang.

* storage/indexeddb/prefetch-bugfix-108071-expected.txt: Added.
* storage/indexeddb/prefetch-bugfix-108071.html: Added.

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

LayoutTests/ChangeLog
LayoutTests/storage/indexeddb/prefetch-bugfix-108071-expected.txt [new file with mode: 0644]
LayoutTests/storage/indexeddb/prefetch-bugfix-108071.html [new file with mode: 0644]
Source/WebCore/ChangeLog
Source/WebCore/storage/IDBCursorBackendImpl.cpp
Source/WebCore/storage/IDBCursorBackendImpl.h

index 737ac22..cd74be3 100755 (executable)
@@ -1,3 +1,13 @@
+2012-01-05  David Grogan  <dgrogan@chromium.org>
+
+        IndexedDB: fix cursor prefetch crash
+        https://bugs.webkit.org/show_bug.cgi?id=75596
+
+        Reviewed by Tony Chang.
+
+        * storage/indexeddb/prefetch-bugfix-108071-expected.txt: Added.
+        * storage/indexeddb/prefetch-bugfix-108071.html: Added.
+
 2012-01-05  Gavin Barraclough  <barraclough@apple.com>
 
         Date parsing is too restrictive.
diff --git a/LayoutTests/storage/indexeddb/prefetch-bugfix-108071-expected.txt b/LayoutTests/storage/indexeddb/prefetch-bugfix-108071-expected.txt
new file mode 100644 (file)
index 0000000..5d81a29
--- /dev/null
@@ -0,0 +1,54 @@
+Test for crbug.com/108071
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+indexedDB = window.indexedDB || window.webkitIndexedDB || window.mozIndexedDB;
+PASS indexedDB == null is false
+indexedDB.deleteDatabase(DBNAME)
+indexedDB.open(DBNAME)
+db = event.target.result
+db.setVersion('new version')
+setVersionSuccess():
+trans = event.target.result
+PASS trans !== null is true
+objectStore = db.createObjectStore('store', {keyPath: 'id'})
+
+resetObjectStore():
+objectStore.clear()
+objectStore.add({id: 0, name: "Alpha"})
+objectStore.add({id: 1, name: "Bravo"})
+objectStore.add({id: 2, name: "Charlie"})
+objectStore.add({id: 3, name: "Delta"})
+objectStore.add({id: 4, name: "Echo"})
+
+iterateAndDeleteFirstElement():
+trans = db.transaction(['store'], webkitIDBTransaction.READ_WRITE)
+trans.objectStore('store').openCursor()
+0: Alpha
+trans.objectStore('store').delete(0)
+1: Bravo
+2: Charlie
+3: Delta
+4: Echo
+
+resetObjectStore():
+objectStore.clear()
+objectStore.add({id: 0, name: "Alpha"})
+objectStore.add({id: 1, name: "Bravo"})
+objectStore.add({id: 2, name: "Charlie"})
+objectStore.add({id: 3, name: "Delta"})
+objectStore.add({id: 4, name: "Echo"})
+
+prefetchAndAbort():
+trans.objectStore('store').openCursor()
+0: Alpha
+1: Bravo
+2: Charlie
+3: Delta
+trans.abort()
+PASS Transaction aborted as expected
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
diff --git a/LayoutTests/storage/indexeddb/prefetch-bugfix-108071.html b/LayoutTests/storage/indexeddb/prefetch-bugfix-108071.html
new file mode 100644 (file)
index 0000000..2e2e35c
--- /dev/null
@@ -0,0 +1,130 @@
+<html>
+<head>
+<script src="../../fast/js/resources/js-test-pre.js"></script>
+<script src="resources/shared.js"></script>
+</head>
+<body>
+<p id="description"></p>
+<div id="console"></div>
+<script>
+
+description("Test for crbug.com/108071");
+
+// Have to be at least 5 here: 1 initial, 3 continues to trigger prefetch and 1
+// post-abort outstanding continue.
+var names = ['Alpha', 'Bravo', 'Charlie', 'Delta', 'Echo'];
+
+function test()
+{
+    indexedDB = evalAndLog("indexedDB = window.indexedDB || window.webkitIndexedDB || window.mozIndexedDB;");
+    shouldBeFalse("indexedDB == null");
+    DBNAME = 'prefetch-bugfix-108071';
+    request = evalAndLog("indexedDB.deleteDatabase(DBNAME)");
+    request.onerror = unexpectedErrorCallback;
+    request.onblocked = unexpectedBlockedCallback;
+    request.onsuccess = function (e) {
+        request = evalAndLog("indexedDB.open(DBNAME)");
+        request.onsuccess = openSuccess;
+        request.onerror = unexpectedErrorCallback;
+    };
+}
+
+function openSuccess()
+{
+    var db = evalAndLog("db = event.target.result");
+
+    request = evalAndLog("db.setVersion('new version')");
+    request.onsuccess = setVersionSuccess;
+    request.onerror = unexpectedErrorCallback;
+}
+
+function setVersionSuccess()
+{
+    debug("setVersionSuccess():");
+    window.trans = evalAndLog("trans = event.target.result");
+    shouldBeTrue("trans !== null");
+    trans.onabort = unexpectedAbortCallback;
+    trans.oncomplete = iterateAndDeleteFirstElement;
+
+    var objectStore = evalAndLog("objectStore = db.createObjectStore('store', {keyPath: 'id'})");
+    resetObjectStore();
+}
+
+function resetObjectStore()
+{
+    debug("\nresetObjectStore():");
+
+    objectStore = trans.objectStore('store');
+    evalAndLog("objectStore.clear()");
+    for (i = 0; i < names.length; i++) {
+        request = evalAndLog("objectStore.add({id: " + i + ", name: \"" + names[i] + "\"})");
+        request.onerror = unexpectedErrorCallback;
+    }
+
+    debug("");
+}
+
+function iterateAndDeleteFirstElement()
+{
+    debug("iterateAndDeleteFirstElement():");
+
+    evalAndLog("trans = db.transaction(['store'], webkitIDBTransaction.READ_WRITE)");
+    trans.onabort = transactionAborted;
+    trans.oncomplete = unexpectedCompleteCallback;
+
+    // Create the cursor and iterate over the whole thing.
+    request = evalAndLog("trans.objectStore('store').openCursor()");
+    request.onerror = unexpectedErrorCallback;
+    request.onsuccess = function () {
+        var cursor = event.target.result;
+        if (cursor == null) {
+            resetObjectStore();
+            prefetchAndAbort();
+            return;
+        }
+
+        debug(cursor.value.id + ": " + cursor.value.name);
+
+        if (cursor.value.id == 0) {
+            // Delete the first element when we see it.
+            request = evalAndLog("trans.objectStore('store').delete(0)");
+            request.onerror = unexpectedErrorCallback;
+            request.onsuccess = function() { cursor.continue(); };
+        } else {
+            cursor.continue();
+        }
+    }
+}
+
+function prefetchAndAbort()
+{
+    debug("prefetchAndAbort():");
+
+    request = evalAndLog("trans.objectStore('store').openCursor()");
+    request.onerror = unexpectedErrorCallback;
+    request.onsuccess = function () {
+        var cursor = event.target.result;
+        debug(cursor.value.id + ": " + cursor.value.name);
+        // Have to iterate to 3 in order to prefetch, but can't iterate past 3
+        // so that there will be a continue remaining.
+        if (cursor.value.id == 3) {
+            evalAndLog("trans.abort()");
+        } else {
+            cursor.continue();
+        }
+    }
+}
+
+function transactionAborted()
+{
+    testPassed("Transaction aborted as expected");
+    finishJSTest();
+}
+
+var jsTestIsAsync = true;
+test();
+
+</script>
+<script src="../../fast/js/resources/js-test-post.js"></script>
+</body>
+</html>
index 4633f51..bdb6af4 100755 (executable)
@@ -1,3 +1,25 @@
+2012-01-05  David Grogan  <dgrogan@chromium.org>
+
+        IndexedDB: fix cursor prefetch crash
+        http://crbug.com/108071
+        https://bugs.webkit.org/show_bug.cgi?id=75596
+
+        Reviewed by Tony Chang.
+
+        Test: storage/indexeddb/prefetch-bugfix-108071.html
+        Note: DumpRenderTree doesn't exercise the bug, it only occurs in
+        multi-process chromium.  The layout test will soon be run as a
+        chromium ui test: http://codereview.chromium.org/9108004
+
+        * storage/IDBCursorBackendImpl.cpp:
+        (WebCore::IDBCursorBackendImpl::IDBCursorBackendImpl):
+        (WebCore::IDBCursorBackendImpl::~IDBCursorBackendImpl): Destroy
+        cursors before their objectstores.
+        (WebCore::IDBCursorBackendImpl::prefetchReset): Don't run continue if
+        the cursor is closed.
+        (WebCore::IDBCursorBackendImpl::close): Set a closed flag.
+        * storage/IDBCursorBackendImpl.h:
+
 2012-01-04  James Robinson  <jamesr@chromium.org>
 
         [chromium] Route all animate calls through CCLayerTreeHost in composited mode to simplify rate limiting logic
index aa4dcf1..ad96c5f 100644 (file)
@@ -47,6 +47,7 @@ IDBCursorBackendImpl::IDBCursorBackendImpl(PassRefPtr<IDBBackingStore::Cursor> c
     , m_cursorType(cursorType)
     , m_transaction(transaction)
     , m_objectStore(objectStore)
+    , m_closed(false)
 {
     m_transaction->registerOpenCursor(this);
 }
@@ -54,6 +55,11 @@ IDBCursorBackendImpl::IDBCursorBackendImpl(PassRefPtr<IDBBackingStore::Cursor> c
 IDBCursorBackendImpl::~IDBCursorBackendImpl()
 {
     m_transaction->unregisterOpenCursor(this);
+    // Order is important, the cursors have to be destructed before the objectStore.
+    m_cursor.clear();
+    m_savedCursor.clear();
+
+    m_objectStore.clear();
 }
 
 unsigned short IDBCursorBackendImpl::direction() const
@@ -179,6 +185,8 @@ void IDBCursorBackendImpl::prefetchReset(int usedPrefetches, int unusedPrefetche
     m_cursor = m_savedCursor;
     m_savedCursor = 0;
 
+    if (m_closed)
+        return;
     if (m_cursor) {
         for (int i = 0; i < usedPrefetches; ++i) {
             bool ok = m_cursor->continueFunction();
@@ -189,6 +197,7 @@ void IDBCursorBackendImpl::prefetchReset(int usedPrefetches, int unusedPrefetche
 
 void IDBCursorBackendImpl::close()
 {
+    m_closed = true;
     if (m_cursor)
         m_cursor->close();
 }
index 0962415..4ee5ac4 100644 (file)
@@ -78,6 +78,8 @@ private:
     CursorType m_cursorType;
     RefPtr<IDBTransactionBackendInterface> m_transaction;
     RefPtr<IDBObjectStoreBackendInterface> m_objectStore;
+    
+    bool m_closed;
 };
 
 } // namespace WebCore