/* * Copyright (C) 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 * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF * THE POSSIBILITY OF SUCH DAMAGE. */ #include "config.h" #include "IDBConnectionProxy.h" #if ENABLE(INDEXED_DATABASE) #include "IDBCursorInfo.h" #include "IDBDatabase.h" #include "IDBGetRecordData.h" #include "IDBIterateCursorData.h" #include "IDBKeyRangeData.h" #include "IDBOpenDBRequest.h" #include "IDBRequestData.h" #include "IDBResultData.h" #include "ScriptExecutionContext.h" #include "SecurityOrigin.h" #include namespace WebCore { namespace IDBClient { IDBConnectionProxy::IDBConnectionProxy(IDBConnectionToServer& connection) : m_connectionToServer(connection) , m_serverConnectionIdentifier(connection.identifier()) { ASSERT(isMainThread()); } void IDBConnectionProxy::ref() { m_connectionToServer.ref(); } void IDBConnectionProxy::deref() { m_connectionToServer.deref(); } Ref IDBConnectionProxy::openDatabase(ScriptExecutionContext& context, const IDBDatabaseIdentifier& databaseIdentifier, uint64_t version) { RefPtr request; { Locker locker(m_openDBRequestMapLock); request = IDBOpenDBRequest::createOpenRequest(context, *this, databaseIdentifier, version); ASSERT(!m_openDBRequestMap.contains(request->resourceIdentifier())); m_openDBRequestMap.set(request->resourceIdentifier(), request.get()); } callConnectionOnMainThread(&IDBConnectionToServer::openDatabase, IDBRequestData(*this, *request)); return request.releaseNonNull(); } Ref IDBConnectionProxy::deleteDatabase(ScriptExecutionContext& context, const IDBDatabaseIdentifier& databaseIdentifier) { RefPtr request; { Locker locker(m_openDBRequestMapLock); request = IDBOpenDBRequest::createDeleteRequest(context, *this, databaseIdentifier); ASSERT(!m_openDBRequestMap.contains(request->resourceIdentifier())); m_openDBRequestMap.set(request->resourceIdentifier(), request.get()); } callConnectionOnMainThread(&IDBConnectionToServer::deleteDatabase, IDBRequestData(*this, *request)); return request.releaseNonNull(); } void IDBConnectionProxy::didOpenDatabase(const IDBResultData& resultData) { completeOpenDBRequest(resultData); } void IDBConnectionProxy::didDeleteDatabase(const IDBResultData& resultData) { completeOpenDBRequest(resultData); } void IDBConnectionProxy::completeOpenDBRequest(const IDBResultData& resultData) { ASSERT(isMainThread()); RefPtr request; { Locker locker(m_openDBRequestMapLock); request = m_openDBRequestMap.take(resultData.requestIdentifier()); } if (!request) return; request->performCallbackOnOriginThread(*request, &IDBOpenDBRequest::requestCompleted, resultData); } void IDBConnectionProxy::createObjectStore(TransactionOperation& operation, const IDBObjectStoreInfo& info) { const IDBRequestData requestData { operation }; saveOperation(operation); callConnectionOnMainThread(&IDBConnectionToServer::createObjectStore, requestData, info); } void IDBConnectionProxy::renameObjectStore(TransactionOperation& operation, uint64_t objectStoreIdentifier, const String& newName) { const IDBRequestData requestData { operation }; saveOperation(operation); callConnectionOnMainThread(&IDBConnectionToServer::renameObjectStore, requestData, objectStoreIdentifier, newName); } void IDBConnectionProxy::renameIndex(TransactionOperation& operation, uint64_t objectStoreIdentifier, uint64_t indexIdentifier, const String& newName) { const IDBRequestData requestData { operation }; saveOperation(operation); callConnectionOnMainThread(&IDBConnectionToServer::renameIndex, requestData, objectStoreIdentifier, indexIdentifier, newName); } void IDBConnectionProxy::deleteObjectStore(TransactionOperation& operation, const String& objectStoreName) { const IDBRequestData requestData { operation }; saveOperation(operation); callConnectionOnMainThread(&IDBConnectionToServer::deleteObjectStore, requestData, objectStoreName); } void IDBConnectionProxy::clearObjectStore(TransactionOperation& operation, uint64_t objectStoreIdentifier) { const IDBRequestData requestData { operation }; saveOperation(operation); callConnectionOnMainThread(&IDBConnectionToServer::clearObjectStore, requestData, objectStoreIdentifier); } void IDBConnectionProxy::createIndex(TransactionOperation& operation, const IDBIndexInfo& info) { const IDBRequestData requestData { operation }; saveOperation(operation); callConnectionOnMainThread(&IDBConnectionToServer::createIndex, requestData, info); } void IDBConnectionProxy::deleteIndex(TransactionOperation& operation, uint64_t objectStoreIdentifier, const String& indexName) { const IDBRequestData requestData { operation }; saveOperation(operation); callConnectionOnMainThread(&IDBConnectionToServer::deleteIndex, requestData, WTFMove(objectStoreIdentifier), indexName); } void IDBConnectionProxy::putOrAdd(TransactionOperation& operation, IDBKeyData&& keyData, const IDBValue& value, const IndexedDB::ObjectStoreOverwriteMode mode) { const IDBRequestData requestData { operation }; saveOperation(operation); callConnectionOnMainThread(&IDBConnectionToServer::putOrAdd, requestData, keyData, value, mode); } void IDBConnectionProxy::getRecord(TransactionOperation& operation, const IDBGetRecordData& getRecordData) { const IDBRequestData requestData { operation }; saveOperation(operation); callConnectionOnMainThread(&IDBConnectionToServer::getRecord, requestData, getRecordData); } void IDBConnectionProxy::getAllRecords(TransactionOperation& operation, const IDBGetAllRecordsData& getAllRecordsData) { const IDBRequestData requestData { operation }; saveOperation(operation); callConnectionOnMainThread(&IDBConnectionToServer::getAllRecords, requestData, getAllRecordsData); } void IDBConnectionProxy::getCount(TransactionOperation& operation, const IDBKeyRangeData& keyRange) { const IDBRequestData requestData { operation }; saveOperation(operation); callConnectionOnMainThread(&IDBConnectionToServer::getCount, requestData, keyRange); } void IDBConnectionProxy::deleteRecord(TransactionOperation& operation, const IDBKeyRangeData& keyRange) { const IDBRequestData requestData { operation }; saveOperation(operation); callConnectionOnMainThread(&IDBConnectionToServer::deleteRecord, requestData, keyRange); } void IDBConnectionProxy::openCursor(TransactionOperation& operation, const IDBCursorInfo& info) { const IDBRequestData requestData { operation }; saveOperation(operation); callConnectionOnMainThread(&IDBConnectionToServer::openCursor, requestData, info); } void IDBConnectionProxy::iterateCursor(TransactionOperation& operation, const IDBIterateCursorData& data) { const IDBRequestData requestData { operation }; saveOperation(operation); callConnectionOnMainThread(&IDBConnectionToServer::iterateCursor, requestData, data); } void IDBConnectionProxy::saveOperation(TransactionOperation& operation) { Locker locker(m_transactionOperationLock); ASSERT(!m_activeOperations.contains(operation.identifier())); m_activeOperations.set(operation.identifier(), &operation); } void IDBConnectionProxy::completeOperation(const IDBResultData& resultData) { RefPtr operation; { Locker locker(m_transactionOperationLock); operation = m_activeOperations.take(resultData.requestIdentifier()); } if (!operation) return; operation->transitionToComplete(resultData, WTFMove(operation)); } void IDBConnectionProxy::abortOpenAndUpgradeNeeded(uint64_t databaseConnectionIdentifier, const IDBResourceIdentifier& transactionIdentifier) { callConnectionOnMainThread(&IDBConnectionToServer::abortOpenAndUpgradeNeeded, databaseConnectionIdentifier, transactionIdentifier); } void IDBConnectionProxy::fireVersionChangeEvent(uint64_t databaseConnectionIdentifier, const IDBResourceIdentifier& requestIdentifier, uint64_t requestedVersion) { RefPtr database; { Locker locker(m_databaseConnectionMapLock); database = m_databaseConnectionMap.get(databaseConnectionIdentifier); } if (!database) return; database->performCallbackOnOriginThread(*database, &IDBDatabase::fireVersionChangeEvent, requestIdentifier, requestedVersion); } void IDBConnectionProxy::didFireVersionChangeEvent(uint64_t databaseConnectionIdentifier, const IDBResourceIdentifier& requestIdentifier) { callConnectionOnMainThread(&IDBConnectionToServer::didFireVersionChangeEvent, databaseConnectionIdentifier, requestIdentifier); } void IDBConnectionProxy::notifyOpenDBRequestBlocked(const IDBResourceIdentifier& requestIdentifier, uint64_t oldVersion, uint64_t newVersion) { ASSERT(isMainThread()); RefPtr request; { Locker locker(m_openDBRequestMapLock); request = m_openDBRequestMap.get(requestIdentifier); } if (!request) return; request->performCallbackOnOriginThread(*request, &IDBOpenDBRequest::requestBlocked, oldVersion, newVersion); } void IDBConnectionProxy::openDBRequestCancelled(const IDBRequestData& requestData) { callConnectionOnMainThread(&IDBConnectionToServer::openDBRequestCancelled, requestData); } void IDBConnectionProxy::establishTransaction(IDBTransaction& transaction) { { Locker locker(m_transactionMapLock); ASSERT(!hasRecordOfTransaction(transaction)); m_pendingTransactions.set(transaction.info().identifier(), &transaction); } callConnectionOnMainThread(&IDBConnectionToServer::establishTransaction, transaction.database().databaseConnectionIdentifier(), transaction.info()); } void IDBConnectionProxy::didStartTransaction(const IDBResourceIdentifier& transactionIdentifier, const IDBError& error) { RefPtr transaction; { Locker locker(m_transactionMapLock); transaction = m_pendingTransactions.take(transactionIdentifier); } ASSERT(transaction); transaction->performCallbackOnOriginThread(*transaction, &IDBTransaction::didStart, error); } void IDBConnectionProxy::commitTransaction(IDBTransaction& transaction) { { Locker locker(m_transactionMapLock); ASSERT(!m_committingTransactions.contains(transaction.info().identifier())); m_committingTransactions.set(transaction.info().identifier(), &transaction); } callConnectionOnMainThread(&IDBConnectionToServer::commitTransaction, transaction.info().identifier()); } void IDBConnectionProxy::didCommitTransaction(const IDBResourceIdentifier& transactionIdentifier, const IDBError& error) { RefPtr transaction; { Locker locker(m_transactionMapLock); transaction = m_committingTransactions.take(transactionIdentifier); } if (!transaction) return; transaction->performCallbackOnOriginThread(*transaction, &IDBTransaction::didCommit, error); } void IDBConnectionProxy::abortTransaction(IDBTransaction& transaction) { { Locker locker(m_transactionMapLock); ASSERT(!m_abortingTransactions.contains(transaction.info().identifier())); m_abortingTransactions.set(transaction.info().identifier(), &transaction); } callConnectionOnMainThread(&IDBConnectionToServer::abortTransaction, transaction.info().identifier()); } void IDBConnectionProxy::didAbortTransaction(const IDBResourceIdentifier& transactionIdentifier, const IDBError& error) { RefPtr transaction; { Locker locker(m_transactionMapLock); transaction = m_abortingTransactions.take(transactionIdentifier); } if (!transaction) return; transaction->performCallbackOnOriginThread(*transaction, &IDBTransaction::didAbort, error); } bool IDBConnectionProxy::hasRecordOfTransaction(const IDBTransaction& transaction) const { ASSERT(m_transactionMapLock.isLocked()); auto identifier = transaction.info().identifier(); return m_pendingTransactions.contains(identifier) || m_committingTransactions.contains(identifier) || m_abortingTransactions.contains(identifier); } void IDBConnectionProxy::didFinishHandlingVersionChangeTransaction(uint64_t databaseConnectionIdentifier, IDBTransaction& transaction) { callConnectionOnMainThread(&IDBConnectionToServer::didFinishHandlingVersionChangeTransaction, databaseConnectionIdentifier, transaction.info().identifier()); } void IDBConnectionProxy::databaseConnectionPendingClose(IDBDatabase& database) { callConnectionOnMainThread(&IDBConnectionToServer::databaseConnectionPendingClose, database.databaseConnectionIdentifier()); } void IDBConnectionProxy::databaseConnectionClosed(IDBDatabase& database) { callConnectionOnMainThread(&IDBConnectionToServer::databaseConnectionClosed, database.databaseConnectionIdentifier()); } void IDBConnectionProxy::didCloseFromServer(uint64_t databaseConnectionIdentifier, const IDBError& error) { RefPtr database; { Locker locker(m_databaseConnectionMapLock); database = m_databaseConnectionMap.get(databaseConnectionIdentifier); } // If the IDBDatabase object is gone, message back to the server so it doesn't hang // waiting for a reply that will never come. if (!database) { m_connectionToServer.confirmDidCloseFromServer(databaseConnectionIdentifier); return; } database->performCallbackOnOriginThread(*database, &IDBDatabase::didCloseFromServer, error); } void IDBConnectionProxy::confirmDidCloseFromServer(IDBDatabase& database) { callConnectionOnMainThread(&IDBConnectionToServer::confirmDidCloseFromServer, database.databaseConnectionIdentifier()); } void IDBConnectionProxy::connectionToServerLost(const IDBError& error) { Vector databaseConnectionIdentifiers; { Locker locker(m_databaseConnectionMapLock); copyKeysToVector(m_databaseConnectionMap, databaseConnectionIdentifiers); } for (auto connectionIdentifier : databaseConnectionIdentifiers) { RefPtr database; { Locker locker(m_databaseConnectionMapLock); database = m_databaseConnectionMap.get(connectionIdentifier); } if (!database) continue; database->performCallbackOnOriginThread(*database, &IDBDatabase::connectionToServerLost, error); } Vector openDBRequestIdentifiers; { Locker locker(m_openDBRequestMapLock); copyKeysToVector(m_openDBRequestMap, openDBRequestIdentifiers); } for (auto& requestIdentifier : openDBRequestIdentifiers) { RefPtr request; { Locker locker(m_openDBRequestMapLock); request = m_openDBRequestMap.get(requestIdentifier); } if (!request) continue; auto result = IDBResultData::error(requestIdentifier, error); request->performCallbackOnOriginThread(*request, &IDBOpenDBRequest::requestCompleted, result); } } void IDBConnectionProxy::scheduleMainThreadTasks() { Locker locker(m_mainThreadTaskLock); if (m_mainThreadProtector) return; m_mainThreadProtector = &m_connectionToServer; callOnMainThread([this] { handleMainThreadTasks(); }); } void IDBConnectionProxy::handleMainThreadTasks() { RefPtr protector; { Locker locker(m_mainThreadTaskLock); ASSERT(m_mainThreadProtector); protector = WTFMove(m_mainThreadProtector); } while (auto task = m_mainThreadQueue.tryGetMessage()) task->performTask(); } void IDBConnectionProxy::getAllDatabaseNames(const SecurityOrigin& mainFrameOrigin, const SecurityOrigin& openingOrigin, std::function&)> callback) { // This method is only meant to be called by the web inspector on the main thread. RELEASE_ASSERT(isMainThread()); m_connectionToServer.getAllDatabaseNames(mainFrameOrigin, openingOrigin, callback); } void IDBConnectionProxy::registerDatabaseConnection(IDBDatabase& database) { Locker locker(m_databaseConnectionMapLock); ASSERT(!m_databaseConnectionMap.contains(database.databaseConnectionIdentifier())); m_databaseConnectionMap.set(database.databaseConnectionIdentifier(), &database); } void IDBConnectionProxy::unregisterDatabaseConnection(IDBDatabase& database) { Locker locker(m_databaseConnectionMapLock); ASSERT(!m_databaseConnectionMap.contains(database.databaseConnectionIdentifier()) || m_databaseConnectionMap.get(database.databaseConnectionIdentifier()) == &database); m_databaseConnectionMap.remove(database.databaseConnectionIdentifier()); } void IDBConnectionProxy::forgetActiveOperations(const Vector>& operations) { Locker locker(m_transactionOperationLock); for (auto& operation : operations) m_activeOperations.remove(operation->identifier()); } template void removeItemsMatchingCurrentThread(HashMap& map) { auto currentThreadID = currentThread(); Vector keys; keys.reserveInitialCapacity(map.size()); for (auto& iterator : map) { if (iterator.value->originThreadID() == currentThreadID) keys.uncheckedAppend(iterator.key); } for (auto& key : keys) map.remove(key); } void IDBConnectionProxy::forgetActivityForCurrentThread() { ASSERT(!isMainThread()); { Locker lock(m_databaseConnectionMapLock); removeItemsMatchingCurrentThread(m_databaseConnectionMap); } { Locker lock(m_openDBRequestMapLock); removeItemsMatchingCurrentThread(m_openDBRequestMap); } { Locker lock(m_transactionMapLock); removeItemsMatchingCurrentThread(m_pendingTransactions); removeItemsMatchingCurrentThread(m_committingTransactions); removeItemsMatchingCurrentThread(m_abortingTransactions); } { Locker lock(m_transactionOperationLock); removeItemsMatchingCurrentThread(m_activeOperations); } } } // namesapce IDBClient } // namespace WebCore #endif // ENABLE(INDEXED_DATABASE)