Modern IDB: storage/indexeddb/dont-wedge.html sometimes ASSERTs.
[WebKit-https.git] / Source / WebCore / Modules / indexeddb / server / UniqueIDBDatabase.cpp
index a00a0e2..73832a5 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2015 Apple Inc. All rights reserved.
+ * Copyright (C) 2015, 2016 Apple Inc. All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
@@ -34,6 +34,7 @@
 #include "IDBServer.h"
 #include "IDBTransactionInfo.h"
 #include "Logging.h"
+#include "ScopeGuard.h"
 #include "UniqueIDBDatabaseConnection.h"
 #include <wtf/MainThread.h>
 #include <wtf/NeverDestroyed.h>
@@ -68,8 +69,8 @@ const IDBDatabaseInfo& UniqueIDBDatabase::info() const
 
 void UniqueIDBDatabase::openDatabaseConnection(IDBConnectionToClient& connection, const IDBRequestData& requestData)
 {
-    auto operation = IDBServerOperation::create(connection, requestData);
-    m_pendingDatabaseOperations.append(WTF::move(operation));
+    auto operation = ServerOpenDBRequest::create(connection, requestData);
+    m_pendingOpenDBRequests.append(WTFMove(operation));
 
     // An open operation is already in progress, so we can't possibly handle this one yet.
     if (m_isOpeningBackingStore)
@@ -98,14 +99,17 @@ bool UniqueIDBDatabase::isVersionChangeInProgress()
 
 void UniqueIDBDatabase::performCurrentOpenOperation()
 {
-    LOG(IndexedDB, "(main) UniqueIDBDatabase::performCurrentOpenOperation");
+    LOG(IndexedDB, "(main) UniqueIDBDatabase::performCurrentOpenOperation (%p)", this);
 
-    ASSERT(m_currentOperation);
-    ASSERT(m_currentOperation->isOpenRequest());
+    ASSERT(m_currentOpenDBRequest);
+    ASSERT(m_currentOpenDBRequest->isOpenRequest());
 
     if (!m_databaseInfo) {
-        m_isOpeningBackingStore = true;
-        m_server.postDatabaseTask(createCrossThreadTask(*this, &UniqueIDBDatabase::openBackingStore, m_identifier));
+        if (!m_isOpeningBackingStore) {
+            m_isOpeningBackingStore = true;
+            m_server.postDatabaseTask(createCrossThreadTask(*this, &UniqueIDBDatabase::openBackingStore, m_identifier));
+        }
+
         return;
     }
 
@@ -120,29 +124,29 @@ void UniqueIDBDatabase::performCurrentOpenOperation()
     // 3.3.1 Opening a database
     // If requested version is undefined, then let requested version be 1 if db was created in the previous step,
     // or the current version of db otherwise.
-    uint64_t requestedVersion = m_currentOperation->requestData().requestedVersion();
+    uint64_t requestedVersion = m_currentOpenDBRequest->requestData().requestedVersion();
     if (!requestedVersion)
         requestedVersion = m_databaseInfo->version() ? m_databaseInfo->version() : 1;
 
     // 3.3.1 Opening a database
     // If the database version higher than the requested version, abort these steps and return a VersionError.
     if (requestedVersion < m_databaseInfo->version()) {
-        auto result = IDBResultData::error(m_currentOperation->requestData().requestIdentifier(), IDBError(IDBDatabaseException::VersionError));
-        m_currentOperation->connection().didOpenDatabase(result);
-        m_currentOperation = nullptr;
+        auto result = IDBResultData::error(m_currentOpenDBRequest->requestData().requestIdentifier(), IDBError(IDBDatabaseException::VersionError));
+        m_currentOpenDBRequest->connection().didOpenDatabase(result);
+        m_currentOpenDBRequest = nullptr;
 
         return;
     }
 
-    Ref<UniqueIDBDatabaseConnection> connection = UniqueIDBDatabaseConnection::create(*this, m_currentOperation->connection());
+    Ref<UniqueIDBDatabaseConnection> connection = UniqueIDBDatabaseConnection::create(*this, m_currentOpenDBRequest->connection());
     UniqueIDBDatabaseConnection* rawConnection = &connection.get();
 
     if (requestedVersion == m_databaseInfo->version()) {
-        addOpenDatabaseConnection(WTF::move(connection));
+        addOpenDatabaseConnection(WTFMove(connection));
 
-        auto result = IDBResultData::openDatabaseSuccess(m_currentOperation->requestData().requestIdentifier(), *rawConnection);
-        m_currentOperation->connection().didOpenDatabase(result);
-        m_currentOperation = nullptr;
+        auto result = IDBResultData::openDatabaseSuccess(m_currentOpenDBRequest->requestData().requestIdentifier(), *rawConnection);
+        m_currentOpenDBRequest->connection().didOpenDatabase(result);
+        m_currentOpenDBRequest = nullptr;
 
         return;
     }
@@ -158,44 +162,86 @@ void UniqueIDBDatabase::performCurrentOpenOperation()
     }
 
     // Otherwise we have to notify all those open connections and wait for them to close.
-    notifyConnectionsOfVersionChangeForUpgrade();
-
-    // And we notify this OpenDBRequest that it is blocked until those connections close.
-    m_versionChangeDatabaseConnection->connectionToClient().notifyOpenDBRequestBlocked(m_currentOperation->requestData().requestIdentifier(), m_databaseInfo->version(), requestedVersion);
+    maybeNotifyConnectionsOfVersionChange();
 }
 
 void UniqueIDBDatabase::performCurrentDeleteOperation()
 {
-    LOG(IndexedDB, "(main) UniqueIDBDatabase::performCurrentDeleteOperation");
+    ASSERT(isMainThread());
+    LOG(IndexedDB, "(main) UniqueIDBDatabase::performCurrentDeleteOperation - %s", m_identifier.debugString().utf8().data());
 
-    ASSERT(m_databaseInfo);
-    ASSERT(m_currentOperation);
-    ASSERT(m_currentOperation->isDeleteRequest());
+    ASSERT(m_currentOpenDBRequest);
+    ASSERT(m_currentOpenDBRequest->isDeleteRequest());
 
-    if (hasAnyOpenConnections()) {
-        // Exactly once, notify all open connections of the pending deletion.
-        if (!m_hasNotifiedConnectionsOfDelete) {
-            notifyConnectionsOfVersionChange(0);
-            m_hasNotifiedConnectionsOfDelete = true;
-        }
+    if (m_deleteBackingStoreInProgress)
+        return;
 
-        if (!m_currentOperation->hasNotifiedDeleteRequestBlocked())
-            m_currentOperation->notifyDeleteRequestBlocked(m_databaseInfo->version());
+    if (hasAnyOpenConnections()) {
+        maybeNotifyConnectionsOfVersionChange();
+        return;
+    }
 
+    // Even though we have no open database connections, we might have close-pending database connections
+    // that are waiting on transactions to complete.
+    if (!m_inProgressTransactions.isEmpty()) {
+        ASSERT(!m_closePendingDatabaseConnections.isEmpty());
         return;
     }
 
     ASSERT(!hasAnyPendingCallbacks());
+    ASSERT(m_pendingTransactions.isEmpty());
+    ASSERT(m_openDatabaseConnections.isEmpty());
+
+    // It's possible to have multiple delete requests queued up in a row.
+    // In that scenario only the first request will actually have to delete the database.
+    // Subsequent requests can immediately notify their completion.
+
+    if (m_databaseInfo) {
+        m_deleteBackingStoreInProgress = true;
+        m_server.postDatabaseTask(createCrossThreadTask(*this, &UniqueIDBDatabase::deleteBackingStore));
+    } else {
+        ASSERT(m_mostRecentDeletedDatabaseInfo);
+        didDeleteBackingStore();
+    }
+}
+
+void UniqueIDBDatabase::deleteBackingStore()
+{
+    ASSERT(!isMainThread());
+    LOG(IndexedDB, "(db) UniqueIDBDatabase::deleteBackingStore");
+
+    if (m_backingStore) {
+        m_backingStore->deleteBackingStore();
+        m_backingStore = nullptr;
+        m_backingStoreSupportsSimultaneousTransactions = false;
+    }
+
+    m_server.postDatabaseTaskReply(createCrossThreadTask(*this, &UniqueIDBDatabase::didDeleteBackingStore));
+}
+
+void UniqueIDBDatabase::didDeleteBackingStore()
+{
+    ASSERT(isMainThread());
+    LOG(IndexedDB, "(main) UniqueIDBDatabase::didDeleteBackingStore");
+
+    ASSERT(m_currentOpenDBRequest);
+    ASSERT(m_currentOpenDBRequest->isDeleteRequest());
+    ASSERT(!hasAnyPendingCallbacks());
     ASSERT(m_inProgressTransactions.isEmpty());
     ASSERT(m_pendingTransactions.isEmpty());
     ASSERT(m_openDatabaseConnections.isEmpty());
 
-    m_currentOperation->notifyDidDeleteDatabase(*m_databaseInfo);
-    m_currentOperation = nullptr;
-    m_hasNotifiedConnectionsOfDelete = false;
+    if (m_databaseInfo)
+        m_mostRecentDeletedDatabaseInfo = WTFMove(m_databaseInfo);
+
+    ASSERT(m_mostRecentDeletedDatabaseInfo);
+    m_currentOpenDBRequest->notifyDidDeleteDatabase(*m_mostRecentDeletedDatabaseInfo);
+    m_currentOpenDBRequest = nullptr;
+
     m_deletePending = false;
+    m_deleteBackingStoreInProgress = false;
 
-    if (m_pendingDatabaseOperations.isEmpty())
+    if (m_pendingOpenDBRequests.isEmpty())
         m_server.deleteUniqueIDBDatabase(*this);
     else
         invokeOperationAndTransactionTimer();
@@ -204,43 +250,43 @@ void UniqueIDBDatabase::performCurrentDeleteOperation()
 void UniqueIDBDatabase::handleDatabaseOperations()
 {
     ASSERT(isMainThread());
-    LOG(IndexedDB, "(main) UniqueIDBDatabase::handleDatabaseOperations - There are %zu pending", m_pendingDatabaseOperations.size());
+    LOG(IndexedDB, "(main) UniqueIDBDatabase::handleDatabaseOperations - There are %zu pending", m_pendingOpenDBRequests.size());
 
-    if (m_versionChangeDatabaseConnection || m_versionChangeTransaction || m_currentOperation) {
+    if (m_versionChangeDatabaseConnection || m_versionChangeTransaction || m_currentOpenDBRequest) {
         // We can't start any new open-database operations right now, but we might be able to start handling a delete operation.
-        if (!m_currentOperation && !m_pendingDatabaseOperations.isEmpty() && m_pendingDatabaseOperations.first()->isDeleteRequest())
-            m_currentOperation = m_pendingDatabaseOperations.takeFirst();
+        if (!m_currentOpenDBRequest && !m_pendingOpenDBRequests.isEmpty() && m_pendingOpenDBRequests.first()->isDeleteRequest())
+            m_currentOpenDBRequest = m_pendingOpenDBRequests.takeFirst();
 
         // Some operations (such as the first open operation after a delete) require multiple passes to completely handle
-        if (m_currentOperation)
+        if (m_currentOpenDBRequest)
             handleCurrentOperation();
 
         return;
     }
 
-    if (m_pendingDatabaseOperations.isEmpty())
+    if (m_pendingOpenDBRequests.isEmpty())
         return;
 
-    m_currentOperation = m_pendingDatabaseOperations.takeFirst();
-    LOG(IndexedDB, "UniqueIDBDatabase::handleDatabaseOperations - Popped an operation, now there are %zu pending", m_pendingDatabaseOperations.size());
+    m_currentOpenDBRequest = m_pendingOpenDBRequests.takeFirst();
+    LOG(IndexedDB, "UniqueIDBDatabase::handleDatabaseOperations - Popped an operation, now there are %zu pending", m_pendingOpenDBRequests.size());
 
     handleCurrentOperation();
 }
 
 void UniqueIDBDatabase::handleCurrentOperation()
 {
-    ASSERT(m_currentOperation);
+    ASSERT(m_currentOpenDBRequest);
 
     RefPtr<UniqueIDBDatabase> protector(this);
 
-    if (m_currentOperation->isOpenRequest())
+    if (m_currentOpenDBRequest->isOpenRequest())
         performCurrentOpenOperation();
-    else if (m_currentOperation->isDeleteRequest())
+    else if (m_currentOpenDBRequest->isDeleteRequest())
         performCurrentDeleteOperation();
     else
         ASSERT_NOT_REACHED();
 
-    if (!m_currentOperation)
+    if (!m_currentOpenDBRequest)
         invokeOperationAndTransactionTimer();
 }
 
@@ -292,7 +338,7 @@ void UniqueIDBDatabase::handleDelete(IDBConnectionToClient& connection, const ID
 {
     LOG(IndexedDB, "(main) UniqueIDBDatabase::handleDelete");
 
-    m_pendingDatabaseOperations.append(IDBServerOperation::create(connection, requestData));
+    m_pendingOpenDBRequests.append(ServerOpenDBRequest::create(connection, requestData));
     handleDatabaseOperations();
 }
 
@@ -301,11 +347,11 @@ void UniqueIDBDatabase::startVersionChangeTransaction()
     LOG(IndexedDB, "(main) UniqueIDBDatabase::startVersionChangeTransaction");
 
     ASSERT(!m_versionChangeTransaction);
-    ASSERT(m_currentOperation);
-    ASSERT(m_currentOperation->isOpenRequest());
+    ASSERT(m_currentOpenDBRequest);
+    ASSERT(m_currentOpenDBRequest->isOpenRequest());
     ASSERT(m_versionChangeDatabaseConnection);
 
-    auto operation = WTF::move(m_currentOperation);
+    auto operation = WTFMove(m_currentOpenDBRequest);
 
     uint64_t requestedVersion = operation->requestData().requestedVersion();
     if (!requestedVersion)
@@ -329,28 +375,67 @@ void UniqueIDBDatabase::beginTransactionInBackingStore(const IDBTransactionInfo&
     m_backingStore->beginTransaction(info);
 }
 
-void UniqueIDBDatabase::notifyConnectionsOfVersionChangeForUpgrade()
+void UniqueIDBDatabase::maybeNotifyConnectionsOfVersionChange()
 {
-    ASSERT(m_currentOperation);
-    ASSERT(m_currentOperation->isOpenRequest());
-    ASSERT(m_versionChangeDatabaseConnection);
+    ASSERT(m_currentOpenDBRequest);
 
-    notifyConnectionsOfVersionChange(m_currentOperation->requestData().requestedVersion());
-}
+    if (m_currentOpenDBRequest->hasNotifiedConnectionsOfVersionChange())
+        return;
 
-void UniqueIDBDatabase::notifyConnectionsOfVersionChange(uint64_t requestedVersion)
-{
-    LOG(IndexedDB, "(main) UniqueIDBDatabase::notifyConnectionsOfVersionChange - %" PRIu64, requestedVersion);
+    uint64_t newVersion = m_currentOpenDBRequest->isOpenRequest() ? m_currentOpenDBRequest->requestData().requestedVersion() : 0;
+    auto requestIdentifier = m_currentOpenDBRequest->requestData().requestIdentifier();
+
+    LOG(IndexedDB, "(main) UniqueIDBDatabase::notifyConnectionsOfVersionChange - %" PRIu64, newVersion);
 
     // 3.3.7 "versionchange" transaction steps
     // Fire a versionchange event at each connection in m_openDatabaseConnections that is open.
     // The event must not be fired on connections which has the closePending flag set.
+    HashSet<uint64_t> connectionIdentifiers;
     for (auto connection : m_openDatabaseConnections) {
         if (connection->closePending())
             continue;
 
-        connection->fireVersionChangeEvent(requestedVersion);
+        connection->fireVersionChangeEvent(requestIdentifier, newVersion);
+        connectionIdentifiers.add(connection->identifier());
+    }
+
+    m_currentOpenDBRequest->notifiedConnectionsOfVersionChange(WTFMove(connectionIdentifiers));
+}
+
+void UniqueIDBDatabase::notifyCurrentRequestConnectionClosedOrFiredVersionChangeEvent(uint64_t connectionIdentifier)
+{
+    LOG(IndexedDB, "UniqueIDBDatabase::notifyCurrentRequestConnectionClosedOrFiredVersionChangeEvent - %" PRIu64, connectionIdentifier);
+
+    ASSERT(m_currentOpenDBRequest);
+
+    m_currentOpenDBRequest->connectionClosedOrFiredVersionChangeEvent(connectionIdentifier);
+
+    if (m_currentOpenDBRequest->hasConnectionsPendingVersionChangeEvent())
+        return;
+
+    if (!hasAnyOpenConnections()) {
+        invokeOperationAndTransactionTimer();
+        return;
     }
+
+    if (m_currentOpenDBRequest->hasNotifiedBlocked())
+        return;
+
+    // Since all open connections have fired their version change events but not all of them have closed,
+    // this request is officially blocked.
+    m_currentOpenDBRequest->notifyRequestBlocked(m_databaseInfo->version());
+}
+
+void UniqueIDBDatabase::didFireVersionChangeEvent(UniqueIDBDatabaseConnection& connection, const IDBResourceIdentifier& requestIdentifier)
+{
+    LOG(IndexedDB, "UniqueIDBDatabase::didFireVersionChangeEvent");
+
+    if (!m_currentOpenDBRequest)
+        return;
+
+    ASSERT_UNUSED(requestIdentifier, m_currentOpenDBRequest->requestData().requestIdentifier() == requestIdentifier);
+
+    notifyCurrentRequestConnectionClosedOrFiredVersionChangeEvent(connection.identifier());
 }
 
 void UniqueIDBDatabase::addOpenDatabaseConnection(Ref<UniqueIDBDatabaseConnection>&& connection)
@@ -362,10 +447,11 @@ void UniqueIDBDatabase::addOpenDatabaseConnection(Ref<UniqueIDBDatabaseConnectio
 void UniqueIDBDatabase::openBackingStore(const IDBDatabaseIdentifier& identifier)
 {
     ASSERT(!isMainThread());
-    LOG(IndexedDB, "(db) UniqueIDBDatabase::openBackingStore");
+    LOG(IndexedDB, "(db) UniqueIDBDatabase::openBackingStore (%p)", this);
 
     ASSERT(!m_backingStore);
     m_backingStore = m_server.createBackingStore(identifier);
+    m_backingStoreSupportsSimultaneousTransactions = m_backingStore->supportsSimultaneousTransactions();
     auto databaseInfo = m_backingStore->getOrEstablishDatabaseInfo();
 
     m_server.postDatabaseTaskReply(createCrossThreadTask(*this, &UniqueIDBDatabase::didOpenBackingStore, databaseInfo));
@@ -422,28 +508,35 @@ void UniqueIDBDatabase::deleteObjectStore(UniqueIDBDatabaseTransaction& transact
     LOG(IndexedDB, "(main) UniqueIDBDatabase::deleteObjectStore");
 
     uint64_t callbackID = storeCallback(callback);
-    m_server.postDatabaseTask(createCrossThreadTask(*this, &UniqueIDBDatabase::performDeleteObjectStore, callbackID, transaction.info().identifier(), objectStoreName));
+
+    auto* info = m_databaseInfo->infoForExistingObjectStore(objectStoreName);
+    if (!info) {
+        performErrorCallback(callbackID, { IDBDatabaseException::UnknownError, ASCIILiteral("Attempt to delete non-existant object store") });
+        return;
+    }
+
+    m_server.postDatabaseTask(createCrossThreadTask(*this, &UniqueIDBDatabase::performDeleteObjectStore, callbackID, transaction.info().identifier(), info->identifier()));
 }
 
-void UniqueIDBDatabase::performDeleteObjectStore(uint64_t callbackIdentifier, const IDBResourceIdentifier& transactionIdentifier, const String& objectStoreName)
+void UniqueIDBDatabase::performDeleteObjectStore(uint64_t callbackIdentifier, const IDBResourceIdentifier& transactionIdentifier, uint64_t objectStoreIdentifier)
 {
     ASSERT(!isMainThread());
     LOG(IndexedDB, "(db) UniqueIDBDatabase::performDeleteObjectStore");
 
     ASSERT(m_backingStore);
-    m_backingStore->deleteObjectStore(transactionIdentifier, objectStoreName);
+    m_backingStore->deleteObjectStore(transactionIdentifier, objectStoreIdentifier);
 
     IDBError error;
-    m_server.postDatabaseTaskReply(createCrossThreadTask(*this, &UniqueIDBDatabase::didPerformDeleteObjectStore, callbackIdentifier, error, objectStoreName));
+    m_server.postDatabaseTaskReply(createCrossThreadTask(*this, &UniqueIDBDatabase::didPerformDeleteObjectStore, callbackIdentifier, error, objectStoreIdentifier));
 }
 
-void UniqueIDBDatabase::didPerformDeleteObjectStore(uint64_t callbackIdentifier, const IDBError& error, const String& objectStoreName)
+void UniqueIDBDatabase::didPerformDeleteObjectStore(uint64_t callbackIdentifier, const IDBError& error, uint64_t objectStoreIdentifier)
 {
     ASSERT(isMainThread());
     LOG(IndexedDB, "(main) UniqueIDBDatabase::didPerformDeleteObjectStore");
 
     if (error.isNull())
-        m_databaseInfo->deleteObjectStore(objectStoreName);
+        m_databaseInfo->deleteObjectStore(objectStoreIdentifier);
 
     performErrorCallback(callbackIdentifier, error);
 }
@@ -518,22 +611,35 @@ void UniqueIDBDatabase::deleteIndex(UniqueIDBDatabaseTransaction& transaction, u
     LOG(IndexedDB, "(main) UniqueIDBDatabase::deleteIndex");
 
     uint64_t callbackID = storeCallback(callback);
-    m_server.postDatabaseTask(createCrossThreadTask(*this, &UniqueIDBDatabase::performDeleteIndex, callbackID, transaction.info().identifier(), objectStoreIdentifier, indexName));
+
+    auto* objectStoreInfo = m_databaseInfo->infoForExistingObjectStore(objectStoreIdentifier);
+    if (!objectStoreInfo) {
+        performErrorCallback(callbackID, { IDBDatabaseException::UnknownError, ASCIILiteral("Attempt to delete index from non-existant object store") });
+        return;
+    }
+
+    auto* indexInfo = objectStoreInfo->infoForExistingIndex(indexName);
+    if (!indexInfo) {
+        performErrorCallback(callbackID, { IDBDatabaseException::UnknownError, ASCIILiteral("Attempt to delete non-existant index") });
+        return;
+    }
+
+    m_server.postDatabaseTask(createCrossThreadTask(*this, &UniqueIDBDatabase::performDeleteIndex, callbackID, transaction.info().identifier(), objectStoreIdentifier, indexInfo->identifier()));
 }
 
-void UniqueIDBDatabase::performDeleteIndex(uint64_t callbackIdentifier, const IDBResourceIdentifier& transactionIdentifier, uint64_t objectStoreIdentifier, const String& indexName)
+void UniqueIDBDatabase::performDeleteIndex(uint64_t callbackIdentifier, const IDBResourceIdentifier& transactionIdentifier, uint64_t objectStoreIdentifier, const uint64_t indexIdentifier)
 {
     ASSERT(!isMainThread());
     LOG(IndexedDB, "(db) UniqueIDBDatabase::performDeleteIndex");
 
     ASSERT(m_backingStore);
-    m_backingStore->deleteIndex(transactionIdentifier, objectStoreIdentifier, indexName);
+    m_backingStore->deleteIndex(transactionIdentifier, objectStoreIdentifier, indexIdentifier);
 
     IDBError error;
-    m_server.postDatabaseTaskReply(createCrossThreadTask(*this, &UniqueIDBDatabase::didPerformDeleteIndex, callbackIdentifier, error, objectStoreIdentifier, indexName));
+    m_server.postDatabaseTaskReply(createCrossThreadTask(*this, &UniqueIDBDatabase::didPerformDeleteIndex, callbackIdentifier, error, objectStoreIdentifier, indexIdentifier));
 }
 
-void UniqueIDBDatabase::didPerformDeleteIndex(uint64_t callbackIdentifier, const IDBError& error, uint64_t objectStoreIdentifier, const String& indexName)
+void UniqueIDBDatabase::didPerformDeleteIndex(uint64_t callbackIdentifier, const IDBError& error, uint64_t objectStoreIdentifier, uint64_t indexIdentifier)
 {
     ASSERT(isMainThread());
     LOG(IndexedDB, "(main) UniqueIDBDatabase::didPerformDeleteIndex");
@@ -541,7 +647,7 @@ void UniqueIDBDatabase::didPerformDeleteIndex(uint64_t callbackIdentifier, const
     if (error.isNull()) {
         auto* objectStoreInfo = m_databaseInfo->infoForExistingObjectStore(objectStoreIdentifier);
         if (objectStoreInfo)
-            objectStoreInfo->deleteIndex(indexName);
+            objectStoreInfo->deleteIndex(indexIdentifier);
     }
 
     performErrorCallback(callbackIdentifier, error);
@@ -592,6 +698,7 @@ void UniqueIDBDatabase::performPutOrAdd(uint64_t callbackIdentifier, const IDBRe
     }
 
     bool usedKeyIsGenerated = false;
+    ScopeGuard generatedKeyResetter;
     if (objectStoreInfo->autoIncrement() && !keyData.isValid()) {
         uint64_t keyNumber;
         error = m_backingStore->generateKeyNumber(transactionIdentifier, objectStoreIdentifier, keyNumber);
@@ -602,6 +709,9 @@ void UniqueIDBDatabase::performPutOrAdd(uint64_t callbackIdentifier, const IDBRe
         
         usedKey.setNumberValue(keyNumber);
         usedKeyIsGenerated = true;
+        generatedKeyResetter.enable([this, transactionIdentifier, objectStoreIdentifier, keyNumber]() {
+            m_backingStore->revertGeneratedKeyNumber(transactionIdentifier, objectStoreIdentifier, keyNumber);
+        });
     } else
         usedKey = keyData;
 
@@ -654,7 +764,7 @@ void UniqueIDBDatabase::performPutOrAdd(uint64_t callbackIdentifier, const IDBRe
         return;
     }
 
-    error = m_backingStore->addRecord(transactionIdentifier, objectStoreIdentifier, usedKey, injectedRecordValue.data() ? injectedRecordValue : originalRecordValue);
+    error = m_backingStore->addRecord(transactionIdentifier, *objectStoreInfo, usedKey, injectedRecordValue.data() ? injectedRecordValue : originalRecordValue);
     if (!error.isNull()) {
         m_server.postDatabaseTaskReply(createCrossThreadTask(*this, &UniqueIDBDatabase::didPerformPutOrAdd, callbackIdentifier, error, usedKey));
         return;
@@ -663,6 +773,7 @@ void UniqueIDBDatabase::performPutOrAdd(uint64_t callbackIdentifier, const IDBRe
     if (overwriteMode != IndexedDB::ObjectStoreOverwriteMode::OverwriteForCursor && objectStoreInfo->autoIncrement() && keyData.type() == IndexedDB::KeyType::Number)
         error = m_backingStore->maybeUpdateKeyGeneratorNumber(transactionIdentifier, objectStoreIdentifier, keyData.number());
 
+    generatedKeyResetter.disable();
     m_server.postDatabaseTaskReply(createCrossThreadTask(*this, &UniqueIDBDatabase::didPerformPutOrAdd, callbackIdentifier, error, usedKey));
 }
 
@@ -843,7 +954,7 @@ void UniqueIDBDatabase::commitTransaction(UniqueIDBDatabaseTransaction& transact
     ASSERT(&transaction.databaseConnection().database() == this);
 
     if (m_versionChangeTransaction == &transaction) {
-        ASSERT(&m_versionChangeTransaction->databaseConnection() == m_versionChangeDatabaseConnection);
+        ASSERT(!m_versionChangeDatabaseConnection || &m_versionChangeTransaction->databaseConnection() == m_versionChangeDatabaseConnection);
         ASSERT(m_databaseInfo->version() == transaction.info().newVersion());
 
         invokeOperationAndTransactionTimer();
@@ -912,7 +1023,7 @@ void UniqueIDBDatabase::didPerformAbortTransaction(uint64_t callbackIdentifier,
     LOG(IndexedDB, "(main) UniqueIDBDatabase::didPerformAbortTransaction");
 
     if (m_versionChangeTransaction && m_versionChangeTransaction->info().identifier() == transactionIdentifier) {
-        ASSERT(&m_versionChangeTransaction->databaseConnection() == m_versionChangeDatabaseConnection);
+        ASSERT(!m_versionChangeDatabaseConnection || &m_versionChangeTransaction->databaseConnection() == m_versionChangeDatabaseConnection);
         ASSERT(m_versionChangeTransaction->originalDatabaseInfo());
         m_databaseInfo = std::make_unique<IDBDatabaseInfo>(*m_versionChangeTransaction->originalDatabaseInfo());
     }
@@ -942,15 +1053,20 @@ void UniqueIDBDatabase::connectionClosedFromClient(UniqueIDBDatabaseConnection&
     while (!m_pendingTransactions.isEmpty()) {
         auto transaction = m_pendingTransactions.takeFirst();
         if (&transaction->databaseConnection() != &connection)
-            pendingTransactions.append(WTF::move(transaction));
+            pendingTransactions.append(WTFMove(transaction));
     }
 
     if (!pendingTransactions.isEmpty())
         m_pendingTransactions.swap(pendingTransactions);
 
-    auto removedConnection = m_openDatabaseConnections.take(&connection);
-    if (removedConnection->hasNonFinishedTransactions()) {
-        m_closePendingDatabaseConnections.add(WTF::move(removedConnection));
+    RefPtr<UniqueIDBDatabaseConnection> refConnection(&connection);
+    m_openDatabaseConnections.remove(&connection);
+
+    if (m_currentOpenDBRequest)
+        notifyCurrentRequestConnectionClosedOrFiredVersionChangeEvent(connection.identifier());
+
+    if (connection.hasNonFinishedTransactions()) {
+        m_closePendingDatabaseConnections.add(WTFMove(refConnection));
         return;
     }
 
@@ -960,11 +1076,11 @@ void UniqueIDBDatabase::connectionClosedFromClient(UniqueIDBDatabaseConnection&
 
 void UniqueIDBDatabase::enqueueTransaction(Ref<UniqueIDBDatabaseTransaction>&& transaction)
 {
-    LOG(IndexedDB, "UniqueIDBDatabase::enqueueTransaction");
+    LOG(IndexedDB, "UniqueIDBDatabase::enqueueTransaction - %s", transaction->info().loggingString().utf8().data());
 
     ASSERT(transaction->info().mode() != IndexedDB::TransactionMode::VersionChange);
 
-    m_pendingTransactions.append(WTF::move(transaction));
+    m_pendingTransactions.append(WTFMove(transaction));
 
     invokeOperationAndTransactionTimer();
 }
@@ -984,10 +1100,10 @@ void UniqueIDBDatabase::operationAndTransactionTimerFired()
 
     // The current operation might require multiple attempts to handle, so try to
     // make further progress on it now.
-    if (m_currentOperation)
+    if (m_currentOpenDBRequest)
         handleCurrentOperation();
 
-    if (!m_currentOperation)
+    if (!m_currentOpenDBRequest)
         handleDatabaseOperations();
 
     bool hadDeferredTransactions = false;
@@ -995,8 +1111,13 @@ void UniqueIDBDatabase::operationAndTransactionTimerFired()
 
     if (transaction) {
         m_inProgressTransactions.set(transaction->info().identifier(), transaction);
-        for (auto objectStore : transaction->objectStoreIdentifiers())
+        for (auto objectStore : transaction->objectStoreIdentifiers()) {
             m_objectStoreTransactionCounts.add(objectStore);
+            if (!transaction->isReadOnly()) {
+                m_objectStoreWriteTransactions.add(objectStore);
+                ASSERT(m_objectStoreTransactionCounts.count(objectStore) == 1);
+            }
+        }
 
         activateTransactionInBackingStore(*transaction);
 
@@ -1050,29 +1171,42 @@ template<typename T> bool scopesOverlap(const T& aScopes, const Vector<uint64_t>
 
 RefPtr<UniqueIDBDatabaseTransaction> UniqueIDBDatabase::takeNextRunnableTransaction(bool& hadDeferredTransactions)
 {
+    hadDeferredTransactions = false;
+    if (!m_backingStoreSupportsSimultaneousTransactions && !m_inProgressTransactions.isEmpty()) {
+        LOG(IndexedDB, "UniqueIDBDatabase::takeNextRunnableTransaction - Backing store only supports 1 transaction, and we already have 1");
+        return nullptr;
+    }
+
     Deque<RefPtr<UniqueIDBDatabaseTransaction>> deferredTransactions;
     RefPtr<UniqueIDBDatabaseTransaction> currentTransaction;
 
+    HashSet<uint64_t> deferredReadWriteScopes;
+
     while (!m_pendingTransactions.isEmpty()) {
         currentTransaction = m_pendingTransactions.takeFirst();
 
         switch (currentTransaction->info().mode()) {
-        case IndexedDB::TransactionMode::ReadOnly:
-            // If there are any deferred transactions, the first one is a read-write transaction we need to unblock.
-            // Therefore, skip this read-only transaction if its scope overlaps with that read-write transaction.
-            if (!deferredTransactions.isEmpty()) {
-                ASSERT(deferredTransactions.first()->info().mode() == IndexedDB::TransactionMode::ReadWrite);
-                if (scopesOverlap(deferredTransactions.first()->objectStoreIdentifiers(), currentTransaction->objectStoreIdentifiers()))
-                    deferredTransactions.append(WTF::move(currentTransaction));
-            }
+        case IndexedDB::TransactionMode::ReadOnly: {
+            bool hasOverlappingScopes = scopesOverlap(deferredReadWriteScopes, currentTransaction->objectStoreIdentifiers());
+            hasOverlappingScopes |= scopesOverlap(m_objectStoreWriteTransactions, currentTransaction->objectStoreIdentifiers());
+
+            if (hasOverlappingScopes)
+                deferredTransactions.append(WTFMove(currentTransaction));
 
             break;
-        case IndexedDB::TransactionMode::ReadWrite:
-            // If this read-write transaction's scope overlaps with running transactions, it must be deferred.
-            if (scopesOverlap(m_objectStoreTransactionCounts, currentTransaction->objectStoreIdentifiers()))
-                deferredTransactions.append(WTF::move(currentTransaction));
+        }
+        case IndexedDB::TransactionMode::ReadWrite: {
+            bool hasOverlappingScopes = scopesOverlap(m_objectStoreTransactionCounts, currentTransaction->objectStoreIdentifiers());
+            hasOverlappingScopes |= scopesOverlap(deferredReadWriteScopes, currentTransaction->objectStoreIdentifiers());
+
+            if (hasOverlappingScopes) {
+                for (auto objectStore : currentTransaction->objectStoreIdentifiers())
+                    deferredReadWriteScopes.add(objectStore);
+                deferredTransactions.append(WTFMove(currentTransaction));
+            }
 
             break;
+        }
         case IndexedDB::TransactionMode::VersionChange:
             // Version change transactions should never be scheduled in the traditional manner.
             RELEASE_ASSERT_NOT_REACHED();
@@ -1085,13 +1219,13 @@ RefPtr<UniqueIDBDatabaseTransaction> UniqueIDBDatabase::takeNextRunnableTransact
 
     hadDeferredTransactions = !deferredTransactions.isEmpty();
     if (!hadDeferredTransactions)
-        return WTF::move(currentTransaction);
+        return currentTransaction;
 
     // Prepend the deferred transactions back on the beginning of the deque for future scheduling passes.
     while (!deferredTransactions.isEmpty())
         m_pendingTransactions.prepend(deferredTransactions.takeLast());
 
-    return WTF::move(currentTransaction);
+    return currentTransaction;
 }
 
 void UniqueIDBDatabase::inProgressTransactionCompleted(const IDBResourceIdentifier& transactionIdentifier)
@@ -1099,8 +1233,13 @@ void UniqueIDBDatabase::inProgressTransactionCompleted(const IDBResourceIdentifi
     auto transaction = m_inProgressTransactions.take(transactionIdentifier);
     ASSERT(transaction);
 
-    for (auto objectStore : transaction->objectStoreIdentifiers())
+    for (auto objectStore : transaction->objectStoreIdentifiers()) {
+        if (!transaction->isReadOnly()) {
+            m_objectStoreWriteTransactions.remove(objectStore);
+            ASSERT(m_objectStoreTransactionCounts.count(objectStore) == 1);
+        }
         m_objectStoreTransactionCounts.remove(objectStore);
+    }
 
     if (!transaction->databaseConnection().hasNonFinishedTransactions())
         m_closePendingDatabaseConnections.remove(&transaction->databaseConnection());