Modern IDB: When a transaction is aborted, call onerror handlers for all in-progress...
authorbeidson@apple.com <beidson@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Mon, 23 Nov 2015 19:48:50 +0000 (19:48 +0000)
committerbeidson@apple.com <beidson@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Mon, 23 Nov 2015 19:48:50 +0000 (19:48 +0000)
https://bugs.webkit.org/show_bug.cgi?id=151550

Reviewed by Alex Christensen.

Source/WebCore:

Test: storage/indexeddb/modern/abort-requests-cancelled.html
      storage/indexeddb/modern/idbtransaction-objectstore-failures.html (with changes)
      storage/indexeddb/modern/index-5.html (with changes)
      Various (currently skipped) legacy IDB tests.

* Modules/indexeddb/client/IDBDatabaseImpl.cpp:
(WebCore::IDBClient::IDBDatabase::transaction):
(WebCore::IDBClient::IDBDatabase::startVersionChangeTransaction):
(WebCore::IDBClient::IDBDatabase::didStartTransaction):
(WebCore::IDBClient::IDBDatabase::willCommitTransaction):
(WebCore::IDBClient::IDBDatabase::didCommitTransaction):
(WebCore::IDBClient::IDBDatabase::willAbortTransaction):
(WebCore::IDBClient::IDBDatabase::didAbortTransaction):
(WebCore::IDBClient::IDBDatabase::didCommitOrAbortTransaction):

* Modules/indexeddb/client/IDBTransactionImpl.cpp:
(WebCore::IDBClient::IDBTransaction::abort):
(WebCore::IDBClient::IDBTransaction::abortOnServerAndCancelRequests):
(WebCore::IDBClient::IDBTransaction::didCreateObjectStoreOnServer):
(WebCore::IDBClient::IDBTransaction::didCreateIndexOnServer):
(WebCore::IDBClient::IDBTransaction::didGetRecordOnServer):
(WebCore::IDBClient::IDBTransaction::didDeleteObjectStoreOnServer):
(WebCore::IDBClient::IDBTransaction::didDeleteIndexOnServer):
(WebCore::IDBClient::IDBTransaction::immediateAbort): Deleted.
(WebCore::IDBClient::IDBTransaction::abortOnServer): Deleted.
* Modules/indexeddb/client/IDBTransactionImpl.h:

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

* Modules/indexeddb/shared/IDBError.cpp:
(WebCore::idbErrorName):
(WebCore::idbErrorDescription):
* Modules/indexeddb/shared/IDBError.h:

* Modules/indexeddb/shared/IDBResourceIdentifier.cpp:
(WebCore::IDBResourceIdentifier::loggingString):
* Modules/indexeddb/shared/IDBResourceIdentifier.h:

LayoutTests:

* storage/indexeddb/modern/abort-requests-cancelled-expected.txt: Added.
* storage/indexeddb/modern/abort-requests-cancelled.html: Added.
* storage/indexeddb/modern/idbtransaction-objectstore-failures-expected.txt:
* storage/indexeddb/modern/idbtransaction-objectstore-failures.html:
* storage/indexeddb/modern/index-5-expected.txt:
* storage/indexeddb/modern/index-5.html:

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

16 files changed:
LayoutTests/ChangeLog
LayoutTests/storage/indexeddb/modern/abort-requests-cancelled-expected.txt [new file with mode: 0644]
LayoutTests/storage/indexeddb/modern/abort-requests-cancelled.html [new file with mode: 0644]
LayoutTests/storage/indexeddb/modern/idbtransaction-objectstore-failures-expected.txt
LayoutTests/storage/indexeddb/modern/idbtransaction-objectstore-failures.html
LayoutTests/storage/indexeddb/modern/index-5-expected.txt
LayoutTests/storage/indexeddb/modern/index-5.html
Source/WebCore/ChangeLog
Source/WebCore/Modules/indexeddb/client/IDBDatabaseImpl.cpp
Source/WebCore/Modules/indexeddb/client/IDBTransactionImpl.cpp
Source/WebCore/Modules/indexeddb/client/IDBTransactionImpl.h
Source/WebCore/Modules/indexeddb/server/MemoryIDBBackingStore.cpp
Source/WebCore/Modules/indexeddb/shared/IDBError.cpp
Source/WebCore/Modules/indexeddb/shared/IDBError.h
Source/WebCore/Modules/indexeddb/shared/IDBResourceIdentifier.cpp
Source/WebCore/Modules/indexeddb/shared/IDBResourceIdentifier.h

index 57bad36..96e9b2d 100644 (file)
@@ -1,5 +1,19 @@
 2015-11-23  Brady Eidson  <beidson@apple.com>
 
+        Modern IDB: When a transaction is aborted, call onerror handlers for all in-progress requests.
+        https://bugs.webkit.org/show_bug.cgi?id=151550
+
+        Reviewed by Alex Christensen.
+
+        * storage/indexeddb/modern/abort-requests-cancelled-expected.txt: Added.
+        * storage/indexeddb/modern/abort-requests-cancelled.html: Added.
+        * storage/indexeddb/modern/idbtransaction-objectstore-failures-expected.txt:
+        * storage/indexeddb/modern/idbtransaction-objectstore-failures.html:
+        * storage/indexeddb/modern/index-5-expected.txt:
+        * storage/indexeddb/modern/index-5.html:
+
+2015-11-23  Brady Eidson  <beidson@apple.com>
+
         Modern IDB: Unskip storage/indexeddb/mozilla/global-data.html.
         https://bugs.webkit.org/show_bug.cgi?id=151557
 
diff --git a/LayoutTests/storage/indexeddb/modern/abort-requests-cancelled-expected.txt b/LayoutTests/storage/indexeddb/modern/abort-requests-cancelled-expected.txt
new file mode 100644 (file)
index 0000000..89c0c77
--- /dev/null
@@ -0,0 +1,24 @@
+This test makes sure that un-handled requests in a transaction receive onerror callbacks when the transaction is aborted.
+Initial upgrade needed: Old version - 0 New version - 1
+Error handling: "objectStore.put({ bar: 'A' }, 1);" (error)
+Error handling: "objectStore.put({ bar: 'B' }, 2);" (error)
+Error handling: "objectStore.put({ bar: 'C' }, 3);" (error)
+Initial upgrade versionchange transaction aborted
+Second upgrade needed: Old version - 0 New version - 1
+Success handling: "objectStore.put({ bar: 'A' }, 1);"
+Success handling: "objectStore.put({ bar: 'B' }, 2);"
+Success handling: "objectStore.put({ bar: 'C' }, 3);"
+Second upgrade versionchange transaction completed
+Error handling: "objectStore.get(1);" (error)
+Error handling: "objectStore.get(2);" (error)
+Error handling: "objectStore.get(3);" (error)
+Error handling: "objectStore.put({ bar: 'D' }, 4);" (error)
+Error handling: "objectStore.put({ bar: 'E' }, 5);" (error)
+Error handling: "objectStore.put({ bar: 'F' }, 6);" (error)
+readwrite transaction aborted
+Error handling: "objectStore.get(1);" (error)
+Error handling: "objectStore.get(2);" (error)
+Error handling: "objectStore.get(3);" (error)
+readonly transaction aborted
+Done
+
diff --git a/LayoutTests/storage/indexeddb/modern/abort-requests-cancelled.html b/LayoutTests/storage/indexeddb/modern/abort-requests-cancelled.html
new file mode 100644 (file)
index 0000000..e153b68
--- /dev/null
@@ -0,0 +1,157 @@
+This test makes sure that un-handled requests in a transaction receive onerror callbacks when the transaction is aborted.<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 database;
+var objectStore;
+
+function setupRequest(code)
+{
+    var request = eval(code);
+    request.onsuccess = function()
+    {
+        log ("Success handling: \"" + code + "\"");
+    }
+    request.onerror = function(e)
+    {
+        log ("Error handling: \"" + code + "\" (" + e.type + ")");
+        e.stopPropagation();
+    }   
+}
+
+startTest();
+
+function startTest() {
+    var createRequest = window.indexedDB.open("AbortRequestsCancelledDatabase", 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");
+        setupRequest("objectStore.put({ bar: 'A' }, 1);");
+        setupRequest("objectStore.put({ bar: 'B' }, 2);");
+        setupRequest("objectStore.put({ bar: 'C' }, 3);");
+
+        versionTransaction.abort();
+
+        versionTransaction.onabort = function(event) {
+            log("Initial upgrade versionchange transaction aborted");
+            database.close();
+            continueTest1();
+        }
+
+        versionTransaction.oncomplete = function(event) {
+            log("Initial upgrade versionchange transaction unexpected complete");
+            done();
+        }
+
+        versionTransaction.onerror = function(event) {
+            log("Initial upgrade versionchange transaction unexpected error" + event);
+            done();
+        }
+    }
+}
+
+function continueTest1() {
+    var createRequest = window.indexedDB.open("AbortRequestsCancelledDatabase", 1);
+    createRequest.onupgradeneeded = function(event) {
+        log("Second upgrade needed: Old version - " + event.oldVersion + " New version - " + event.newVersion);
+
+        var versionTransaction = createRequest.transaction;
+        database = event.target.result;
+        objectStore = database.createObjectStore("TestObjectStore");
+    
+        setupRequest("objectStore.put({ bar: 'A' }, 1);");
+        setupRequest("objectStore.put({ bar: 'B' }, 2);");
+        setupRequest("objectStore.put({ bar: 'C' }, 3);");
+    
+        versionTransaction.onabort = function(event) {
+            log("Second upgrade versionchange transaction unexpected abort");
+            done();
+        }
+
+        versionTransaction.oncomplete = function(event) {
+            log("Second upgrade versionchange transaction completed");
+            continueTest2();
+        }
+
+        versionTransaction.onerror = function(event) {
+            log("Second upgrade versionchange transaction unexpected error" + event);
+            done();
+        }
+    }
+}
+
+function continueTest2() {
+    var transaction = database.transaction("TestObjectStore", "readwrite");
+    objectStore = transaction.objectStore("TestObjectStore");
+
+    setupRequest("objectStore.get(1);");
+    setupRequest("objectStore.get(2);");
+    setupRequest("objectStore.get(3);");
+    setupRequest("objectStore.put({ bar: 'D' }, 4);");
+    setupRequest("objectStore.put({ bar: 'E' }, 5);");
+    setupRequest("objectStore.put({ bar: 'F' }, 6);");
+
+    transaction.abort();
+
+    transaction.onabort = function(event) {
+        log("readwrite transaction aborted");
+        continueTest3();
+    }
+
+    transaction.oncomplete = function(event) {
+        log("readwrite transaction unexpected complete");
+        done();
+    }
+
+    transaction.onerror = function(event) {
+        log("readwrite transaction unexpected error" + event);
+        done();
+    }
+}
+
+function continueTest3() {
+    var transaction = database.transaction("TestObjectStore", "readonly");
+    objectStore = transaction.objectStore("TestObjectStore");
+
+    setupRequest("objectStore.get(1);");
+    setupRequest("objectStore.get(2);");
+    setupRequest("objectStore.get(3);");
+
+    transaction.abort();
+
+    transaction.onabort = function(event) {
+        log("readonly transaction aborted");
+        done();
+    }
+
+    transaction.oncomplete = function(event) {
+        log("readwrite transaction unexpected complete");
+        done();
+    }
+
+    transaction.onerror = function(event) {
+        log("readwrite transaction unexpected error" + event);
+        done();
+    }
+}
+</script>
index 939b38a..7c3e03d 100644 (file)
@@ -5,7 +5,8 @@ ALERT: Caught attempt to access empty-named object store on the transaction
 ALERT: Caught attempt to access null-named object store on the transaction
 ALERT: Caught attempt to access non-existant object store on the transaction
 ALERT: Caught attempt to access valid object store on a transaction that is already finishing
-ALERT: First version change transaction abort
+ALERT: put failed (because transaction was aborted)
+ALERT: First version change transaction unexpected error - [object Event]
 ALERT: Done
 This tests some obvious failures that can happen while calling transaction.objectStore()
 
index 92eaf5a..c22cde3 100644 (file)
@@ -30,9 +30,8 @@ request.onupgradeneeded = function(event)
 
     var putRequest = os1.put("bar", "foo");
     
-    putRequest.onerror = function(event) {
-        alert("put unexpectedly failed - " + event);
-        done();
+    putRequest.onerror = function() {
+        alert("put failed (because transaction was aborted)");
     }
     
     try {
index 94488c3..3b3a08d 100644 (file)
@@ -1,6 +1,8 @@
 This tests creating an index on an object store that already has records, and those records would violate the unique constraint of the index.
 (The index creation should fail).
 Initial upgrade needed: Old version - 0 New version - 1
+Error getting cursor count
+Error opening or iterating cursor
 Initial upgrade versionchange transaction aborted (expected because index creation failed, and that should've caused transaction abort)
 Done
 
index dee988f..fd683fb 100644 (file)
@@ -29,6 +29,10 @@ function checkIndexValues()
     countRequest.onsuccess = function() {
         log("Count is: " + countRequest.result);
     }
+    countRequest.onerror = function(e) {
+        log("Error getting cursor count");
+        e.stopPropagation();
+    }
 
     var cursorRequest = index.openCursor();
     cursorRequest.onsuccess = function() {
@@ -41,9 +45,8 @@ function checkIndexValues()
             done();
     }
     cursorRequest.onerror = function(e) {
-        log("Unexpected error opening or iterating cursor");
-        logCursor(cursorRequest.result);
-        done();
+        log("Error opening or iterating cursor");
+        e.stopPropagation();
     } 
 }
 
index acf6bd5..ef5c4ae 100644 (file)
@@ -1,5 +1,52 @@
 2015-11-23  Brady Eidson  <beidson@apple.com>
 
+        Modern IDB: When a transaction is aborted, call onerror handlers for all in-progress requests.
+        https://bugs.webkit.org/show_bug.cgi?id=151550
+
+        Reviewed by Alex Christensen.
+
+        Test: storage/indexeddb/modern/abort-requests-cancelled.html
+              storage/indexeddb/modern/idbtransaction-objectstore-failures.html (with changes)
+              storage/indexeddb/modern/index-5.html (with changes)
+              Various (currently skipped) legacy IDB tests.
+
+        * Modules/indexeddb/client/IDBDatabaseImpl.cpp:
+        (WebCore::IDBClient::IDBDatabase::transaction):
+        (WebCore::IDBClient::IDBDatabase::startVersionChangeTransaction):
+        (WebCore::IDBClient::IDBDatabase::didStartTransaction):
+        (WebCore::IDBClient::IDBDatabase::willCommitTransaction):
+        (WebCore::IDBClient::IDBDatabase::didCommitTransaction):
+        (WebCore::IDBClient::IDBDatabase::willAbortTransaction):
+        (WebCore::IDBClient::IDBDatabase::didAbortTransaction):
+        (WebCore::IDBClient::IDBDatabase::didCommitOrAbortTransaction):
+        
+        * Modules/indexeddb/client/IDBTransactionImpl.cpp:
+        (WebCore::IDBClient::IDBTransaction::abort):
+        (WebCore::IDBClient::IDBTransaction::abortOnServerAndCancelRequests):
+        (WebCore::IDBClient::IDBTransaction::didCreateObjectStoreOnServer):
+        (WebCore::IDBClient::IDBTransaction::didCreateIndexOnServer):
+        (WebCore::IDBClient::IDBTransaction::didGetRecordOnServer):
+        (WebCore::IDBClient::IDBTransaction::didDeleteObjectStoreOnServer):
+        (WebCore::IDBClient::IDBTransaction::didDeleteIndexOnServer):
+        (WebCore::IDBClient::IDBTransaction::immediateAbort): Deleted.
+        (WebCore::IDBClient::IDBTransaction::abortOnServer): Deleted.
+        * Modules/indexeddb/client/IDBTransactionImpl.h:
+        
+        * Modules/indexeddb/server/MemoryIDBBackingStore.cpp:
+        (WebCore::IDBServer::MemoryIDBBackingStore::abortTransaction):
+        (WebCore::IDBServer::MemoryIDBBackingStore::commitTransaction):
+        
+        * Modules/indexeddb/shared/IDBError.cpp:
+        (WebCore::idbErrorName):
+        (WebCore::idbErrorDescription):
+        * Modules/indexeddb/shared/IDBError.h:
+        
+        * Modules/indexeddb/shared/IDBResourceIdentifier.cpp:
+        (WebCore::IDBResourceIdentifier::loggingString):
+        * Modules/indexeddb/shared/IDBResourceIdentifier.h:
+
+2015-11-23  Brady Eidson  <beidson@apple.com>
+
         Modern IDB: Unskip storage/indexeddb/mozilla/global-data.html.
         https://bugs.webkit.org/show_bug.cgi?id=151557
 
index ad7ac30..89d43e9 100644 (file)
@@ -171,6 +171,8 @@ RefPtr<WebCore::IDBTransaction> IDBDatabase::transaction(ScriptExecutionContext*
     auto info = IDBTransactionInfo::clientTransaction(m_serverConnection.get(), objectStores, mode);
     auto transaction = IDBTransaction::create(*this, info);
 
+    LOG(IndexedDB, "IDBDatabase::transaction - Added active transaction %s", info.identifier().loggingString().utf8().data());
+
     m_activeTransactions.set(info.identifier(), &transaction.get());
 
     return adoptRef(&transaction.leakRef());
@@ -241,7 +243,7 @@ bool IDBDatabase::canSuspendForPageCache() const
 
 Ref<IDBTransaction> IDBDatabase::startVersionChangeTransaction(const IDBTransactionInfo& info, IDBOpenDBRequest& request)
 {
-    LOG(IndexedDB, "IDBDatabase::startVersionChangeTransaction");
+    LOG(IndexedDB, "IDBDatabase::startVersionChangeTransaction %s", info.identifier().loggingString().utf8().data());
 
     ASSERT(!m_versionChangeTransaction);
     ASSERT(info.mode() == IndexedDB::TransactionMode::VersionChange);
@@ -256,15 +258,19 @@ Ref<IDBTransaction> IDBDatabase::startVersionChangeTransaction(const IDBTransact
 
 void IDBDatabase::didStartTransaction(IDBTransaction& transaction)
 {
-    LOG(IndexedDB, "IDBDatabase::didStartTransaction");
+    LOG(IndexedDB, "IDBDatabase::didStartTransaction %s", transaction.info().identifier().loggingString().utf8().data());
     ASSERT(!m_versionChangeTransaction);
 
+    // It is possible for the client to have aborted a transaction before the server replies back that it has started.
+    if (m_abortingTransactions.contains(transaction.info().identifier()))
+        return;
+
     m_activeTransactions.set(transaction.info().identifier(), &transaction);
 }
 
 void IDBDatabase::willCommitTransaction(IDBTransaction& transaction)
 {
-    LOG(IndexedDB, "IDBDatabase::willCommitTransaction");
+    LOG(IndexedDB, "IDBDatabase::willCommitTransaction %s", transaction.info().identifier().loggingString().utf8().data());
 
     auto refTransaction = m_activeTransactions.take(transaction.info().identifier());
     ASSERT(refTransaction);
@@ -273,7 +279,7 @@ void IDBDatabase::willCommitTransaction(IDBTransaction& transaction)
 
 void IDBDatabase::didCommitTransaction(IDBTransaction& transaction)
 {
-    LOG(IndexedDB, "IDBDatabase::didCommitTransaction");
+    LOG(IndexedDB, "IDBDatabase::didCommitTransaction %s", transaction.info().identifier().loggingString().utf8().data());
 
     if (m_versionChangeTransaction == &transaction)
         m_info.setVersion(transaction.info().newVersion());
@@ -283,7 +289,7 @@ void IDBDatabase::didCommitTransaction(IDBTransaction& transaction)
 
 void IDBDatabase::willAbortTransaction(IDBTransaction& transaction)
 {
-    LOG(IndexedDB, "IDBDatabase::willAbortTransaction");
+    LOG(IndexedDB, "IDBDatabase::willAbortTransaction %s", transaction.info().identifier().loggingString().utf8().data());
 
     auto refTransaction = m_activeTransactions.take(transaction.info().identifier());
     ASSERT(refTransaction);
@@ -292,7 +298,7 @@ void IDBDatabase::willAbortTransaction(IDBTransaction& transaction)
 
 void IDBDatabase::didAbortTransaction(IDBTransaction& transaction)
 {
-    LOG(IndexedDB, "IDBDatabase::didAbortTransaction");
+    LOG(IndexedDB, "IDBDatabase::didAbortTransaction %s", transaction.info().identifier().loggingString().utf8().data());
 
     if (transaction.isVersionChange()) {
         ASSERT(transaction.originalDatabaseInfo());
@@ -304,7 +310,7 @@ void IDBDatabase::didAbortTransaction(IDBTransaction& transaction)
 
 void IDBDatabase::didCommitOrAbortTransaction(IDBTransaction& transaction)
 {
-    LOG(IndexedDB, "IDBDatabase::didCommitOrAbortTransaction");
+    LOG(IndexedDB, "IDBDatabase::didCommitOrAbortTransaction %s", transaction.info().identifier().loggingString().utf8().data());
 
     if (m_versionChangeTransaction == &transaction)
         m_versionChangeTransaction = nullptr;
index e17baca..b2e9f50 100644 (file)
@@ -165,20 +165,6 @@ RefPtr<WebCore::IDBObjectStore> IDBTransaction::objectStore(const String& object
     return adoptRef(&objectStore.leakRef());
 }
 
-void IDBTransaction::immediateAbort()
-{
-    LOG(IndexedDB, "IDBTransaction::immediateAbort");
-
-    if (isFinishedOrFinishing())
-        return;
-
-    m_state = IndexedDB::TransactionState::Aborting;
-    m_database->willAbortTransaction(*this);
-
-    auto operation = createTransactionOperation(*this, nullptr, &IDBTransaction::abortOnServer);
-    abortOnServer(*operation);
-}
-
 void IDBTransaction::abort(ExceptionCode& ec)
 {
     LOG(IndexedDB, "IDBTransaction::abort");
@@ -191,14 +177,28 @@ void IDBTransaction::abort(ExceptionCode& ec)
     m_state = IndexedDB::TransactionState::Aborting;
     m_database->willAbortTransaction(*this);
 
-    auto operation = createTransactionOperation(*this, nullptr, &IDBTransaction::abortOnServer);
+    m_abortQueue.swap(m_transactionOperationQueue);
+
+    auto operation = createTransactionOperation(*this, nullptr, &IDBTransaction::abortOnServerAndCancelRequests);
     scheduleOperation(WTF::move(operation));
 }
 
-void IDBTransaction::abortOnServer(TransactionOperation&)
+void IDBTransaction::abortOnServerAndCancelRequests(TransactionOperation&)
 {
-    LOG(IndexedDB, "IDBTransaction::abortOnServer");
+    LOG(IndexedDB, "IDBTransaction::abortOnServerAndCancelRequests");
+
+    ASSERT(m_transactionOperationQueue.isEmpty());
+
     serverConnection().abortTransaction(*this);
+
+    IDBError error(IDBExceptionCode::AbortError);
+    for (auto& operation : m_abortQueue)
+        operation->completed(IDBResultData::error(operation->identifier(), error));
+
+    // Since we're aborting, this abortOnServerAndCancelRequests() operation should be the only
+    // in-progress operation, and it should be impossible to have queued any further operations.
+    ASSERT(m_transactionOperationMap.size() == 1);
+    ASSERT(m_transactionOperationQueue.isEmpty());
 }
 
 const char* IDBTransaction::activeDOMObjectName() const
@@ -426,7 +426,7 @@ void IDBTransaction::didCreateObjectStoreOnServer(const IDBResultData& resultDat
 {
     LOG(IndexedDB, "IDBTransaction::didCreateObjectStoreOnServer");
 
-    ASSERT_UNUSED(resultData, resultData.type() == IDBResultType::CreateObjectStoreSuccess);
+    ASSERT_UNUSED(resultData, resultData.type() == IDBResultType::CreateObjectStoreSuccess || resultData.type() == IDBResultType::Error);
 }
 
 Ref<IDBIndex> IDBTransaction::createIndex(IDBObjectStore& objectStore, const IDBIndexInfo& info)
@@ -458,8 +458,15 @@ void IDBTransaction::didCreateIndexOnServer(const IDBResultData& resultData)
     if (resultData.type() == IDBResultType::CreateIndexSuccess)
         return;
 
-    // If index creation failed, the transaction is aborted.
-    immediateAbort();
+    ASSERT(resultData.type() == IDBResultType::Error);
+
+    // This operation might have failed because the transaction is already aborting.
+    if (m_state == IndexedDB::TransactionState::Aborting)
+        return;
+
+    // Otherwise, failure to create an index forced abortion of the transaction.
+    ExceptionCode ec;
+    abort(ec);
 }
 
 Ref<IDBRequest> IDBTransaction::requestOpenCursor(ScriptExecutionContext& context, IDBObjectStore& objectStore, const IDBCursorInfo& info)
@@ -585,6 +592,13 @@ void IDBTransaction::didGetRecordOnServer(IDBRequest& request, const IDBResultDa
 {
     LOG(IndexedDB, "IDBTransaction::didGetRecordOnServer");
 
+    if (resultData.type() == IDBResultType::Error) {
+        request.requestCompleted(resultData);
+        return;
+    }
+
+    ASSERT(resultData.type() == IDBResultType::GetRecordSuccess);
+
     const IDBGetResult& result = resultData.getResult();
 
     if (request.sourceIndexIdentifier() && request.requestedIndexRecordType() == IndexedDB::IndexRecordType::Key) {
@@ -760,7 +774,7 @@ void IDBTransaction::deleteObjectStoreOnServer(TransactionOperation& operation,
 void IDBTransaction::didDeleteObjectStoreOnServer(const IDBResultData& resultData)
 {
     LOG(IndexedDB, "IDBTransaction::didDeleteObjectStoreOnServer");
-    ASSERT_UNUSED(resultData, resultData.type() == IDBResultType::DeleteObjectStoreSuccess);
+    ASSERT_UNUSED(resultData, resultData.type() == IDBResultType::DeleteObjectStoreSuccess || resultData.type() == IDBResultType::Error);
 }
 
 void IDBTransaction::deleteIndex(uint64_t objectStoreIdentifier, const String& indexName)
@@ -784,7 +798,7 @@ void IDBTransaction::deleteIndexOnServer(TransactionOperation& operation, const
 void IDBTransaction::didDeleteIndexOnServer(const IDBResultData& resultData)
 {
     LOG(IndexedDB, "IDBTransaction::didDeleteIndexOnServer");
-    ASSERT_UNUSED(resultData, resultData.type() == IDBResultType::DeleteIndexSuccess);
+    ASSERT_UNUSED(resultData, resultData.type() == IDBResultType::DeleteIndexSuccess || resultData.type() == IDBResultType::Error);
 }
 
 void IDBTransaction::operationDidComplete(TransactionOperation& operation)
index 8928185..142447b 100644 (file)
@@ -142,7 +142,7 @@ private:
     Ref<IDBRequest> requestIndexRecord(ScriptExecutionContext&, IDBIndex&, IndexedDB::IndexRecordType, const IDBKeyRangeData&);
 
     void commitOnServer(TransactionOperation&);
-    void abortOnServer(TransactionOperation&);
+    void abortOnServerAndCancelRequests(TransactionOperation&);
 
     void createObjectStoreOnServer(TransactionOperation&, const IDBObjectStoreInfo&);
     void didCreateObjectStoreOnServer(const IDBResultData&);
@@ -182,8 +182,6 @@ private:
 
     void scheduleOperationTimer();
 
-    void immediateAbort();
-
     Ref<IDBDatabase> m_database;
     IDBTransactionInfo m_info;
     std::unique_ptr<IDBDatabaseInfo> m_originalDatabaseInfo;
@@ -199,6 +197,7 @@ private:
     RefPtr<IDBOpenDBRequest> m_openDBRequest;
 
     Deque<RefPtr<TransactionOperation>> m_transactionOperationQueue;
+    Deque<RefPtr<TransactionOperation>> m_abortQueue;
     HashMap<IDBResourceIdentifier, RefPtr<TransactionOperation>> m_transactionOperationMap;
 
     HashMap<String, RefPtr<IDBObjectStore>> m_referencedObjectStores;
index d8632b1..b5c33de 100644 (file)
@@ -95,7 +95,7 @@ IDBError MemoryIDBBackingStore::beginTransaction(const IDBTransactionInfo& info)
 
 IDBError MemoryIDBBackingStore::abortTransaction(const IDBResourceIdentifier& transactionIdentifier)
 {
-    LOG(IndexedDB, "MemoryIDBBackingStore::abortTransaction");
+    LOG(IndexedDB, "MemoryIDBBackingStore::abortTransaction - %s", transactionIdentifier.loggingString().utf8().data());
 
     auto transaction = m_transactions.take(transactionIdentifier);
     if (!transaction)
@@ -108,7 +108,7 @@ IDBError MemoryIDBBackingStore::abortTransaction(const IDBResourceIdentifier& tr
 
 IDBError MemoryIDBBackingStore::commitTransaction(const IDBResourceIdentifier& transactionIdentifier)
 {
-    LOG(IndexedDB, "MemoryIDBBackingStore::commitTransaction");
+    LOG(IndexedDB, "MemoryIDBBackingStore::commitTransaction - %s", transactionIdentifier.loggingString().utf8().data());
 
     auto transaction = m_transactions.take(transactionIdentifier);
     if (!transaction)
index 452a470..75ec38f 100644 (file)
@@ -67,6 +67,10 @@ static const String& idbErrorName(IDBExceptionCode code)
         static NeverDestroyed<String> entry = ASCIILiteral("DataCloneError");
         return entry;
     }
+    case IDBExceptionCode::AbortError: {
+        static NeverDestroyed<String> entry = ASCIILiteral("AbortError");
+        return entry;
+    }
     case IDBExceptionCode::None:
         RELEASE_ASSERT_NOT_REACHED();
     }
@@ -109,6 +113,10 @@ static const String& idbErrorDescription(IDBExceptionCode code)
         static NeverDestroyed<String> entry = ASCIILiteral("Data being stored could not be cloned by the structured cloning algorithm.");
         return entry;
     }
+    case IDBExceptionCode::AbortError: {
+        static NeverDestroyed<String> entry = ASCIILiteral("Transaction was aborted");
+        return entry;
+    }
     case IDBExceptionCode::None:
         RELEASE_ASSERT_NOT_REACHED();
     }
index 2e74f4e..6f61016 100644 (file)
@@ -44,6 +44,7 @@ enum class IDBExceptionCode {
     // Indexed DB existing exception codes with IDB-specific error messages:
     InvalidStateError,
     DataCloneError,
+    AbortError,
 };
 
 class IDBError {
index 17e0302..0b1b46b 100644 (file)
@@ -94,6 +94,12 @@ bool IDBResourceIdentifier::isHashTableDeletedValue() const
         && m_resourceNumber == std::numeric_limits<uint64_t>::max();
 }
 
+#ifndef NDEBUG
+String IDBResourceIdentifier::loggingString() const
+{
+    return String::format("<%" PRIu64", %" PRIu64">", m_idbConnectionIdentifier, m_resourceNumber);
+}
+#endif
 } // namespace WebCore
 
 #endif // ENABLE(INDEXED_DATABASE)
index 9354624..7a62970 100644 (file)
@@ -72,6 +72,10 @@ public:
 
     IDBResourceIdentifier isolatedCopy() const;
 
+#ifndef NDEBUG
+    String loggingString() const;
+#endif
+
 private:
     IDBResourceIdentifier() = delete;
     IDBResourceIdentifier(uint64_t connectionIdentifier, uint64_t resourceIdentifier);