blob: 14901c4a1ce43c4b68bc3c266a13c0c3776c6898 [file] [log] [blame]
/*
* Copyright (C) 2013 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 "UniqueIDBDatabase.h"
#if ENABLE(INDEXED_DATABASE) && ENABLE(DATABASE_PROCESS)
#include "AsyncRequest.h"
#include "DataReference.h"
#include "DatabaseProcess.h"
#include "DatabaseProcessIDBConnection.h"
#include "Logging.h"
#include "UniqueIDBDatabaseBackingStoreSQLite.h"
#include "WebCrossThreadCopier.h"
#include <WebCore/CrossThreadTask.h>
#include <WebCore/FileSystem.h>
#include <WebCore/IDBDatabaseMetadata.h>
#include <WebCore/IDBGetResult.h>
#include <WebCore/IDBKeyData.h>
#include <WebCore/IDBKeyRangeData.h>
#include <WebCore/SecurityOrigin.h>
#include <wtf/MainThread.h>
#include <wtf/text/WTFString.h>
using namespace WebCore;
namespace WebKit {
String UniqueIDBDatabase::calculateAbsoluteDatabaseFilename(const String& absoluteDatabaseDirectory)
{
return pathByAppendingComponent(absoluteDatabaseDirectory, "IndexedDB.sqlite3");
}
UniqueIDBDatabase::UniqueIDBDatabase(const UniqueIDBDatabaseIdentifier& identifier)
: m_identifier(identifier)
, m_acceptingNewRequests(true)
, m_didGetMetadataFromBackingStore(false)
{
m_inMemory = !canShareDatabases(identifier.openingOrigin(), identifier.mainFrameOrigin());
if (m_inMemory)
return;
// *********
// IMPORTANT: Do not change the directory structure for indexed databases on disk without first consulting a reviewer from Apple (<rdar://problem/17454712>)
// *********
// Each unique Indexed Database exists in a directory named for the database, which exists in a directory representing its opening origin.
m_databaseRelativeDirectory = pathByAppendingComponent(databaseFilenameIdentifier(identifier.openingOrigin()), filenameForDatabaseName());
DatabaseProcess::singleton().ensureIndexedDatabaseRelativePathExists(m_databaseRelativeDirectory);
}
UniqueIDBDatabase::~UniqueIDBDatabase()
{
ASSERT(!m_acceptingNewRequests);
ASSERT(m_pendingMetadataRequests.isEmpty());
}
String UniqueIDBDatabase::filenameForDatabaseName() const
{
ASSERT(!m_identifier.databaseName().isNull());
if (m_identifier.databaseName().isEmpty())
return "%00";
String filename = encodeForFileName(m_identifier.databaseName());
filename.replace('.', "%2E");
return filename;
}
String UniqueIDBDatabase::databaseFilenameIdentifier(const SecurityOriginData& originData) const
{
Ref<SecurityOrigin> securityOrigin(SecurityOrigin::create(originData.protocol, originData.host, originData.port));
return securityOrigin.get().databaseIdentifier();
}
bool UniqueIDBDatabase::canShareDatabases(const SecurityOriginData& openingOrigin, const SecurityOriginData& mainFrameOrigin) const
{
// For now, an origin's database access is predicated on equality with the other origin.
// We might need to make this more nuanced later.
return openingOrigin == mainFrameOrigin;
}
void UniqueIDBDatabase::registerConnection(DatabaseProcessIDBConnection& connection)
{
ASSERT(!m_connections.contains(&connection));
m_connections.add(&connection);
}
void UniqueIDBDatabase::unregisterConnection(DatabaseProcessIDBConnection& connection)
{
ASSERT(m_connections.contains(&connection));
resetAllTransactions(connection);
m_connections.remove(&connection);
if (m_connections.isEmpty() && m_pendingTransactionRollbacks.isEmpty()) {
shutdown(UniqueIDBDatabaseShutdownType::NormalShutdown);
DatabaseProcess::singleton().removeUniqueIDBDatabase(*this);
}
}
void UniqueIDBDatabase::shutdown(UniqueIDBDatabaseShutdownType type)
{
ASSERT(RunLoop::isMain());
if (!m_acceptingNewRequests)
return;
m_acceptingNewRequests = false;
// Balanced by an adoptRef in ::didShutdownBackingStore()
ref();
{
LockHolder locker(m_databaseTaskMutex);
m_databaseTasks.clear();
}
postDatabaseTask(createCrossThreadTask(*this, &UniqueIDBDatabase::shutdownBackingStore, type, absoluteDatabaseDirectory()), DatabaseTaskType::Shutdown);
}
void UniqueIDBDatabase::shutdownBackingStore(UniqueIDBDatabaseShutdownType type, const String& databaseDirectory)
{
ASSERT(!RunLoop::isMain());
m_backingStore = nullptr;
if (type == UniqueIDBDatabaseShutdownType::DeleteShutdown) {
String dbFilename = UniqueIDBDatabase::calculateAbsoluteDatabaseFilename(databaseDirectory);
LOG(IDB, "UniqueIDBDatabase::shutdownBackingStore deleting file '%s' on disk", dbFilename.utf8().data());
deleteFile(dbFilename);
deleteEmptyDirectory(databaseDirectory);
}
postMainThreadTask(createCrossThreadTask(*this, &UniqueIDBDatabase::didShutdownBackingStore, type), DatabaseTaskType::Shutdown);
}
void UniqueIDBDatabase::didShutdownBackingStore(UniqueIDBDatabaseShutdownType type)
{
ASSERT(RunLoop::isMain());
// Balanced by a ref in ::shutdown()
RefPtr<UniqueIDBDatabase> protector(adoptRef(this));
// Empty out remaining main thread tasks.
while (performNextMainThreadTask()) {
}
// No more requests will be handled, so abort all outstanding requests.
for (const auto& request : m_pendingMetadataRequests)
request->requestAborted();
for (const auto& request : m_pendingTransactionRequests.values())
request->requestAborted();
for (const auto& request : m_pendingDatabaseTasks.values())
request->requestAborted();
m_pendingMetadataRequests.clear();
m_pendingTransactionRequests.clear();
m_pendingDatabaseTasks.clear();
if (m_pendingShutdownTask)
m_pendingShutdownTask->completeRequest(type);
m_pendingShutdownTask = nullptr;
}
void UniqueIDBDatabase::deleteDatabase(std::function<void (bool)> successCallback)
{
ASSERT(RunLoop::isMain());
if (!m_acceptingNewRequests) {
// Someone else has already shutdown this database, so we can't request a delete.
callOnMainThread([successCallback] {
successCallback(false);
});
return;
}
RefPtr<UniqueIDBDatabase> protector(this);
m_pendingShutdownTask = AsyncRequestImpl<UniqueIDBDatabaseShutdownType>::create([this, protector, successCallback](UniqueIDBDatabaseShutdownType type) {
// If the shutdown just completed was a Delete shutdown then we succeeded.
// If not report failure instead of trying again.
successCallback(type == UniqueIDBDatabaseShutdownType::DeleteShutdown);
}, [this, protector, successCallback] {
successCallback(false);
});
shutdown(UniqueIDBDatabaseShutdownType::DeleteShutdown);
}
void UniqueIDBDatabase::getOrEstablishIDBDatabaseMetadata(std::function<void (bool, const IDBDatabaseMetadata&)> completionCallback)
{
ASSERT(RunLoop::isMain());
if (!m_acceptingNewRequests) {
completionCallback(false, IDBDatabaseMetadata());
return;
}
// If we've already retrieved metadata from the backing store, return it now.
if (m_didGetMetadataFromBackingStore) {
if (m_metadata)
completionCallback(true, *m_metadata);
else
completionCallback(false, IDBDatabaseMetadata());
return;
}
// If this is the first unanswered metadata request, then later we need to
// post a task to open the backing store and get metadata.
bool shouldOpenBackingStore = m_pendingMetadataRequests.isEmpty();
// Then remember this metadata request to be answered later.
RefPtr<AsyncRequest> request = AsyncRequestImpl<>::create([completionCallback, this] {
completionCallback(!!m_metadata, m_metadata ? *m_metadata : IDBDatabaseMetadata());
}, [completionCallback] {
// The boolean flag to the completion callback represents whether the
// attempt to get/establish metadata succeeded or failed.
// Since we're aborting the attempt, it failed, so we always pass in false.
completionCallback(false, IDBDatabaseMetadata());
});
m_pendingMetadataRequests.append(request.release());
if (shouldOpenBackingStore)
postDatabaseTask(createCrossThreadTask(*this, &UniqueIDBDatabase::openBackingStoreAndReadMetadata, m_identifier, absoluteDatabaseDirectory()));
}
void UniqueIDBDatabase::openBackingStoreAndReadMetadata(const UniqueIDBDatabaseIdentifier& identifier, const String& databaseDirectory)
{
ASSERT(!RunLoop::isMain());
ASSERT(!m_backingStore);
if (m_inMemory) {
LOG_ERROR("Support for in-memory databases not yet implemented");
return;
}
m_backingStore = UniqueIDBDatabaseBackingStoreSQLite::create(identifier, databaseDirectory);
std::unique_ptr<IDBDatabaseMetadata> metadata = m_backingStore->getOrEstablishMetadata();
postMainThreadTask(createCrossThreadTask(*this, &UniqueIDBDatabase::didOpenBackingStoreAndReadMetadata, metadata ? *metadata : IDBDatabaseMetadata(), !!metadata));
}
void UniqueIDBDatabase::didOpenBackingStoreAndReadMetadata(const IDBDatabaseMetadata& metadata, bool success)
{
ASSERT(RunLoop::isMain());
ASSERT(!m_metadata);
m_didGetMetadataFromBackingStore = true;
if (success)
m_metadata = std::make_unique<IDBDatabaseMetadata>(metadata);
while (!m_pendingMetadataRequests.isEmpty()) {
RefPtr<AsyncRequest> request = m_pendingMetadataRequests.takeFirst();
request->completeRequest();
}
}
void UniqueIDBDatabase::openTransaction(const IDBIdentifier& transactionIdentifier, const Vector<int64_t>& objectStoreIDs, IndexedDB::TransactionMode mode, std::function<void (bool)> successCallback)
{
postTransactionOperation(transactionIdentifier, createCrossThreadTask(*this, &UniqueIDBDatabase::openBackingStoreTransaction, transactionIdentifier, objectStoreIDs, mode), successCallback);
}
void UniqueIDBDatabase::beginTransaction(const IDBIdentifier& transactionIdentifier, std::function<void (bool)> successCallback)
{
postTransactionOperation(transactionIdentifier, createCrossThreadTask(*this, &UniqueIDBDatabase::beginBackingStoreTransaction, transactionIdentifier), successCallback);
}
void UniqueIDBDatabase::commitTransaction(const IDBIdentifier& transactionIdentifier, std::function<void (bool)> successCallback)
{
postTransactionOperation(transactionIdentifier, createCrossThreadTask(*this, &UniqueIDBDatabase::commitBackingStoreTransaction, transactionIdentifier), successCallback);
}
void UniqueIDBDatabase::resetTransaction(const IDBIdentifier& transactionIdentifier, std::function<void (bool)> successCallback)
{
postTransactionOperation(transactionIdentifier, createCrossThreadTask(*this, &UniqueIDBDatabase::resetBackingStoreTransaction, transactionIdentifier), successCallback);
}
void UniqueIDBDatabase::rollbackTransaction(const IDBIdentifier& transactionIdentifier, std::function<void (bool)> successCallback)
{
postTransactionOperation(transactionIdentifier, createCrossThreadTask(*this, &UniqueIDBDatabase::rollbackBackingStoreTransaction, transactionIdentifier), successCallback);
}
void UniqueIDBDatabase::postTransactionOperation(const IDBIdentifier& transactionIdentifier, std::unique_ptr<CrossThreadTask> task, std::function<void (bool)> successCallback)
{
ASSERT(RunLoop::isMain());
if (!m_acceptingNewRequests) {
successCallback(false);
return;
}
if (m_pendingTransactionRequests.contains(transactionIdentifier)) {
LOG_ERROR("Attempting to queue an operation for a transaction that already has an operation pending. Each transaction should only have one operation pending at a time.");
successCallback(false);
return;
}
postDatabaseTask(WTF::move(task));
RefPtr<AsyncRequest> request = AsyncRequestImpl<bool>::create([successCallback](bool success) {
successCallback(success);
}, [successCallback] {
successCallback(false);
});
m_pendingTransactionRequests.add(transactionIdentifier, request.release());
}
void UniqueIDBDatabase::didCompleteTransactionOperation(const IDBIdentifier& transactionIdentifier, bool success)
{
ASSERT(RunLoop::isMain());
RefPtr<AsyncRequest> request = m_pendingTransactionRequests.take(transactionIdentifier);
if (request)
request->completeRequest(success);
if (m_pendingTransactionRollbacks.contains(transactionIdentifier))
finalizeRollback(transactionIdentifier);
}
void UniqueIDBDatabase::changeDatabaseVersion(const IDBIdentifier& transactionIdentifier, uint64_t newVersion, std::function<void (bool)> successCallback)
{
ASSERT(RunLoop::isMain());
if (!m_acceptingNewRequests) {
successCallback(false);
return;
}
uint64_t oldVersion = m_metadata->version;
m_metadata->version = newVersion;
RefPtr<AsyncRequest> request = AsyncRequestImpl<bool>::create([this, oldVersion, successCallback](bool success) {
if (!success)
m_metadata->version = oldVersion;
successCallback(success);
}, [this, oldVersion, successCallback] {
m_metadata->version = oldVersion;
successCallback(false);
});
uint64_t requestID = request->requestID();
m_pendingDatabaseTasks.add(requestID, request.release());
postDatabaseTask(createCrossThreadTask(*this, &UniqueIDBDatabase::changeDatabaseVersionInBackingStore, requestID, transactionIdentifier, newVersion));
}
void UniqueIDBDatabase::didChangeDatabaseVersion(uint64_t requestID, bool success)
{
didCompleteBoolRequest(requestID, success);
}
void UniqueIDBDatabase::didCreateObjectStore(uint64_t requestID, bool success)
{
didCompleteBoolRequest(requestID, success);
}
void UniqueIDBDatabase::didDeleteObjectStore(uint64_t requestID, bool success)
{
didCompleteBoolRequest(requestID, success);
}
void UniqueIDBDatabase::didClearObjectStore(uint64_t requestID, bool success)
{
didCompleteBoolRequest(requestID, success);
}
void UniqueIDBDatabase::didCreateIndex(uint64_t requestID, bool success)
{
didCompleteBoolRequest(requestID, success);
}
void UniqueIDBDatabase::didDeleteIndex(uint64_t requestID, bool success)
{
didCompleteBoolRequest(requestID, success);
}
void UniqueIDBDatabase::didCompleteBoolRequest(uint64_t requestID, bool success)
{
m_pendingDatabaseTasks.take(requestID).get().completeRequest(success);
}
void UniqueIDBDatabase::createObjectStore(const IDBIdentifier& transactionIdentifier, const IDBObjectStoreMetadata& metadata, std::function<void (bool)> successCallback)
{
ASSERT(RunLoop::isMain());
if (!m_acceptingNewRequests) {
successCallback(false);
return;
}
ASSERT(!m_metadata->objectStores.contains(metadata.id));
m_metadata->objectStores.set(metadata.id, metadata);
int64_t addedObjectStoreID = metadata.id;
RefPtr<AsyncRequest> request = AsyncRequestImpl<bool>::create([this, addedObjectStoreID, successCallback](bool success) {
if (!success)
m_metadata->objectStores.remove(addedObjectStoreID);
successCallback(success);
}, [this, addedObjectStoreID, successCallback] {
m_metadata->objectStores.remove(addedObjectStoreID);
successCallback(false);
});
uint64_t requestID = request->requestID();
m_pendingDatabaseTasks.add(requestID, request.release());
postDatabaseTask(createCrossThreadTask(*this, &UniqueIDBDatabase::createObjectStoreInBackingStore, requestID, transactionIdentifier, metadata));
}
void UniqueIDBDatabase::deleteObjectStore(const IDBIdentifier& transactionIdentifier, int64_t objectStoreID, std::function<void (bool)> successCallback)
{
ASSERT(RunLoop::isMain());
if (!m_acceptingNewRequests) {
successCallback(false);
return;
}
ASSERT(m_metadata->objectStores.contains(objectStoreID));
IDBObjectStoreMetadata metadata = m_metadata->objectStores.take(objectStoreID);
RefPtr<AsyncRequest> request = AsyncRequestImpl<bool>::create([this, metadata, successCallback](bool success) {
if (!success)
m_metadata->objectStores.set(metadata.id, metadata);
successCallback(success);
}, [this, metadata, successCallback] {
m_metadata->objectStores.set(metadata.id, metadata);
successCallback(false);
});
uint64_t requestID = request->requestID();
m_pendingDatabaseTasks.add(requestID, request.release());
postDatabaseTask(createCrossThreadTask(*this, &UniqueIDBDatabase::deleteObjectStoreInBackingStore, requestID, transactionIdentifier, objectStoreID));
}
void UniqueIDBDatabase::clearObjectStore(const IDBIdentifier& transactionIdentifier, int64_t objectStoreID, std::function<void (bool)> successCallback)
{
ASSERT(RunLoop::isMain());
if (!m_acceptingNewRequests) {
successCallback(false);
return;
}
ASSERT(m_metadata->objectStores.contains(objectStoreID));
RefPtr<AsyncRequest> request = AsyncRequestImpl<bool>::create([this, successCallback](bool success) {
successCallback(success);
}, [this, successCallback] {
successCallback(false);
});
uint64_t requestID = request->requestID();
m_pendingDatabaseTasks.add(requestID, request.release());
postDatabaseTask(createCrossThreadTask(*this, &UniqueIDBDatabase::clearObjectStoreInBackingStore, requestID, transactionIdentifier, objectStoreID));
}
void UniqueIDBDatabase::createIndex(const IDBIdentifier& transactionIdentifier, int64_t objectStoreID, const IDBIndexMetadata& metadata, std::function<void (bool)> successCallback)
{
ASSERT(RunLoop::isMain());
if (!m_acceptingNewRequests) {
successCallback(false);
return;
}
ASSERT(m_metadata->objectStores.contains(objectStoreID));
ASSERT(!m_metadata->objectStores.get(objectStoreID).indexes.contains(metadata.id));
m_metadata->objectStores.get(objectStoreID).indexes.set(metadata.id, metadata);
int64_t addedIndexID = metadata.id;
RefPtr<AsyncRequest> request = AsyncRequestImpl<bool>::create([this, objectStoreID, addedIndexID, successCallback](bool success) {
if (!success) {
auto objectStoreFind = m_metadata->objectStores.find(objectStoreID);
if (objectStoreFind != m_metadata->objectStores.end())
objectStoreFind->value.indexes.remove(addedIndexID);
}
successCallback(success);
}, [this, objectStoreID, addedIndexID, successCallback] {
auto objectStoreFind = m_metadata->objectStores.find(objectStoreID);
if (objectStoreFind != m_metadata->objectStores.end())
objectStoreFind->value.indexes.remove(addedIndexID);
successCallback(false);
});
uint64_t requestID = request->requestID();
m_pendingDatabaseTasks.add(requestID, request.release());
postDatabaseTask(createCrossThreadTask(*this, &UniqueIDBDatabase::createIndexInBackingStore, requestID, transactionIdentifier, objectStoreID, metadata));
}
void UniqueIDBDatabase::deleteIndex(const IDBIdentifier& transactionIdentifier, int64_t objectStoreID, int64_t indexID, std::function<void (bool)> successCallback)
{
ASSERT(RunLoop::isMain());
if (!m_acceptingNewRequests) {
successCallback(false);
return;
}
ASSERT(m_metadata->objectStores.contains(objectStoreID));
ASSERT(m_metadata->objectStores.get(objectStoreID).indexes.contains(indexID));
IDBIndexMetadata metadata = m_metadata->objectStores.get(objectStoreID).indexes.take(indexID);
RefPtr<AsyncRequest> request = AsyncRequestImpl<bool>::create([this, objectStoreID, metadata, successCallback](bool success) {
if (!success) {
auto objectStoreFind = m_metadata->objectStores.find(objectStoreID);
if (objectStoreFind != m_metadata->objectStores.end())
objectStoreFind->value.indexes.set(metadata.id, metadata);
}
successCallback(success);
}, [this, objectStoreID, metadata, successCallback] {
auto objectStoreFind = m_metadata->objectStores.find(objectStoreID);
if (objectStoreFind != m_metadata->objectStores.end())
objectStoreFind->value.indexes.set(metadata.id, metadata);
successCallback(false);
});
uint64_t requestID = request->requestID();
m_pendingDatabaseTasks.add(requestID, request.release());
postDatabaseTask(createCrossThreadTask(*this, &UniqueIDBDatabase::deleteIndexInBackingStore, requestID, transactionIdentifier, objectStoreID, indexID));
}
void UniqueIDBDatabase::putRecord(const IDBIdentifier& transactionIdentifier, int64_t objectStoreID, const IDBKeyData& keyData, const IPC::DataReference& value, int64_t putMode, const Vector<int64_t>& indexIDs, const Vector<Vector<IDBKeyData>>& indexKeys, std::function<void (const IDBKeyData&, uint32_t, const String&)> callback)
{
ASSERT(RunLoop::isMain());
if (!m_acceptingNewRequests) {
callback(IDBKeyData(), INVALID_STATE_ERR, "Unable to put record into database because it has shut down");
return;
}
ASSERT(m_metadata->objectStores.contains(objectStoreID));
RefPtr<AsyncRequest> request = AsyncRequestImpl<IDBKeyData, uint32_t, String>::create([this, callback](const IDBKeyData& keyData, uint32_t errorCode, const String& errorMessage) {
callback(keyData, errorCode, errorMessage);
}, [this, callback] {
callback(IDBKeyData(), INVALID_STATE_ERR, "Unable to put record into database");
});
uint64_t requestID = request->requestID();
m_pendingDatabaseTasks.add(requestID, request.release());
postDatabaseTask(createCrossThreadTask(*this, &UniqueIDBDatabase::putRecordInBackingStore, requestID, transactionIdentifier, m_metadata->objectStores.get(objectStoreID), keyData, value.vector(), putMode, indexIDs, indexKeys));
}
void UniqueIDBDatabase::getRecord(const IDBIdentifier& transactionIdentifier, int64_t objectStoreID, int64_t indexID, const IDBKeyRangeData& keyRangeData, IndexedDB::CursorType cursorType, std::function<void (const IDBGetResult&, uint32_t, const String&)> callback)
{
ASSERT(RunLoop::isMain());
if (!m_acceptingNewRequests) {
callback(IDBGetResult(), INVALID_STATE_ERR, "Unable to get record from database because it has shut down");
return;
}
ASSERT(m_metadata->objectStores.contains(objectStoreID));
RefPtr<AsyncRequest> request = AsyncRequestImpl<IDBGetResult, uint32_t, String>::create([this, callback](const IDBGetResult& result, uint32_t errorCode, const String& errorMessage) {
callback(result, errorCode, errorMessage);
}, [this, callback] {
callback(IDBGetResult(), INVALID_STATE_ERR, "Unable to get record from database");
});
uint64_t requestID = request->requestID();
m_pendingDatabaseTasks.add(requestID, request.release());
postDatabaseTask(createCrossThreadTask(*this, &UniqueIDBDatabase::getRecordFromBackingStore, requestID, transactionIdentifier, m_metadata->objectStores.get(objectStoreID), indexID, keyRangeData, cursorType));
}
void UniqueIDBDatabase::openCursor(const IDBIdentifier& transactionIdentifier, int64_t objectStoreID, int64_t indexID, IndexedDB::CursorDirection cursorDirection, IndexedDB::CursorType cursorType, IDBDatabaseBackend::TaskType taskType, const IDBKeyRangeData& keyRangeData, std::function<void (int64_t, const IDBKeyData&, const IDBKeyData&, PassRefPtr<SharedBuffer>, uint32_t, const String&)> callback)
{
ASSERT(RunLoop::isMain());
if (!m_acceptingNewRequests) {
callback(0, nullptr, nullptr, nullptr, INVALID_STATE_ERR, "Unable to open cursor in database because it has shut down");
return;
}
ASSERT(m_metadata->objectStores.contains(objectStoreID));
RefPtr<AsyncRequest> request = AsyncRequestImpl<int64_t, IDBKeyData, IDBKeyData, PassRefPtr<SharedBuffer>, uint32_t, String>::create([this, callback](int64_t cursorID, const IDBKeyData& key, const IDBKeyData& primaryKey, PassRefPtr<SharedBuffer> value, uint32_t errorCode, const String& errorMessage) {
callback(cursorID, key, primaryKey, value, errorCode, errorMessage);
}, [this, callback] {
callback(0, nullptr, nullptr, nullptr, INVALID_STATE_ERR, "Unable to get record from database");
});
uint64_t requestID = request->requestID();
m_pendingDatabaseTasks.add(requestID, request.release());
postDatabaseTask(createCrossThreadTask(*this, &UniqueIDBDatabase::openCursorInBackingStore, requestID, transactionIdentifier, objectStoreID, indexID, cursorDirection, cursorType, taskType, keyRangeData));
}
void UniqueIDBDatabase::cursorAdvance(const IDBIdentifier& cursorIdentifier, uint64_t count, std::function<void (const IDBKeyData&, const IDBKeyData&, PassRefPtr<SharedBuffer>, uint32_t, const String&)> callback)
{
ASSERT(RunLoop::isMain());
if (!m_acceptingNewRequests) {
callback(nullptr, nullptr, nullptr, INVALID_STATE_ERR, "Unable to advance cursor in database because it has shut down");
return;
}
RefPtr<AsyncRequest> request = AsyncRequestImpl<IDBKeyData, IDBKeyData, PassRefPtr<SharedBuffer>, uint32_t, String>::create([this, callback](const IDBKeyData& key, const IDBKeyData& primaryKey, PassRefPtr<SharedBuffer> value, uint32_t errorCode, const String& errorMessage) {
callback(key, primaryKey, value, errorCode, errorMessage);
}, [this, callback] {
callback(nullptr, nullptr, nullptr, INVALID_STATE_ERR, "Unable to advance cursor in database");
});
uint64_t requestID = request->requestID();
m_pendingDatabaseTasks.add(requestID, request.release());
postDatabaseTask(createCrossThreadTask(*this, &UniqueIDBDatabase::advanceCursorInBackingStore, requestID, cursorIdentifier, count));
}
void UniqueIDBDatabase::cursorIterate(const IDBIdentifier& cursorIdentifier, const IDBKeyData& key, std::function<void (const IDBKeyData&, const IDBKeyData&, PassRefPtr<SharedBuffer>, uint32_t, const String&)> callback)
{
ASSERT(RunLoop::isMain());
if (!m_acceptingNewRequests) {
callback(nullptr, nullptr, nullptr, INVALID_STATE_ERR, "Unable to iterate cursor in database because it has shut down");
return;
}
RefPtr<AsyncRequest> request = AsyncRequestImpl<IDBKeyData, IDBKeyData, PassRefPtr<SharedBuffer>, uint32_t, String>::create([this, callback](const IDBKeyData& key, const IDBKeyData& primaryKey, PassRefPtr<SharedBuffer> value, uint32_t errorCode, const String& errorMessage) {
callback(key, primaryKey, value, errorCode, errorMessage);
}, [this, callback] {
callback(nullptr, nullptr, nullptr, INVALID_STATE_ERR, "Unable to iterate cursor in database");
});
uint64_t requestID = request->requestID();
m_pendingDatabaseTasks.add(requestID, request.release());
postDatabaseTask(createCrossThreadTask(*this, &UniqueIDBDatabase::iterateCursorInBackingStore, requestID, cursorIdentifier, key));
}
void UniqueIDBDatabase::count(const IDBIdentifier& transactionIdentifier, int64_t objectStoreID, int64_t indexID, const IDBKeyRangeData& keyRangeData, std::function<void (int64_t, uint32_t, const String&)> callback)
{
ASSERT(RunLoop::isMain());
if (!m_acceptingNewRequests) {
callback(0, INVALID_STATE_ERR, "Unable to get count from database because it has shut down");
return;
}
RefPtr<AsyncRequest> request = AsyncRequestImpl<int64_t, uint32_t, String>::create([this, callback](int64_t count, uint32_t errorCode, const String& errorMessage) {
callback(count, errorCode, errorMessage);
}, [this, callback] {
callback(0, INVALID_STATE_ERR, "Unable to get count from database");
});
uint64_t requestID = request->requestID();
m_pendingDatabaseTasks.add(requestID, request.release());
postDatabaseTask(createCrossThreadTask(*this, &UniqueIDBDatabase::countInBackingStore, requestID, transactionIdentifier, objectStoreID, indexID, keyRangeData));
}
void UniqueIDBDatabase::deleteRange(const IDBIdentifier& transactionIdentifier, int64_t objectStoreID, const IDBKeyRangeData& keyRangeData, std::function<void (uint32_t, const String&)> callback)
{
ASSERT(RunLoop::isMain());
if (!m_acceptingNewRequests) {
callback(INVALID_STATE_ERR, "Unable to deleteRange from database because it has shut down");
return;
}
RefPtr<AsyncRequest> request = AsyncRequestImpl<uint32_t, String>::create([callback](uint32_t errorCode, const String& errorMessage) {
callback(errorCode, errorMessage);
}, [callback] {
callback(INVALID_STATE_ERR, "Unable to deleteRange from database");
});
uint64_t requestID = request->requestID();
m_pendingDatabaseTasks.add(requestID, request.release());
postDatabaseTask(createCrossThreadTask(*this, &UniqueIDBDatabase::deleteRangeInBackingStore, requestID, transactionIdentifier, objectStoreID, keyRangeData));
}
void UniqueIDBDatabase::openBackingStoreTransaction(const IDBIdentifier& transactionIdentifier, const Vector<int64_t>& objectStoreIDs, IndexedDB::TransactionMode mode)
{
ASSERT(!RunLoop::isMain());
ASSERT(m_backingStore);
bool success = m_backingStore->establishTransaction(transactionIdentifier, objectStoreIDs, mode);
postMainThreadTask(createCrossThreadTask(*this, &UniqueIDBDatabase::didEstablishTransaction, transactionIdentifier, success));
postMainThreadTask(createCrossThreadTask(*this, &UniqueIDBDatabase::didCompleteTransactionOperation, transactionIdentifier, success));
}
void UniqueIDBDatabase::beginBackingStoreTransaction(const IDBIdentifier& transactionIdentifier)
{
ASSERT(!RunLoop::isMain());
ASSERT(m_backingStore);
bool success = m_backingStore->beginTransaction(transactionIdentifier);
postMainThreadTask(createCrossThreadTask(*this, &UniqueIDBDatabase::didCompleteTransactionOperation, transactionIdentifier, success));
}
void UniqueIDBDatabase::commitBackingStoreTransaction(const IDBIdentifier& transactionIdentifier)
{
ASSERT(!RunLoop::isMain());
ASSERT(m_backingStore);
bool success = m_backingStore->commitTransaction(transactionIdentifier);
postMainThreadTask(createCrossThreadTask(*this, &UniqueIDBDatabase::didCompleteTransactionOperation, transactionIdentifier, success));
}
void UniqueIDBDatabase::resetBackingStoreTransaction(const IDBIdentifier& transactionIdentifier)
{
ASSERT(!RunLoop::isMain());
ASSERT(m_backingStore);
bool success = m_backingStore->resetTransaction(transactionIdentifier);
postMainThreadTask(createCrossThreadTask(*this, &UniqueIDBDatabase::didResetTransaction, transactionIdentifier, success));
postMainThreadTask(createCrossThreadTask(*this, &UniqueIDBDatabase::didCompleteTransactionOperation, transactionIdentifier, success));
}
void UniqueIDBDatabase::rollbackBackingStoreTransaction(const IDBIdentifier& transactionIdentifier)
{
ASSERT(!RunLoop::isMain());
ASSERT(m_backingStore);
bool success = m_backingStore->rollbackTransaction(transactionIdentifier);
postMainThreadTask(createCrossThreadTask(*this, &UniqueIDBDatabase::didCompleteTransactionOperation, transactionIdentifier, success));
}
void UniqueIDBDatabase::changeDatabaseVersionInBackingStore(uint64_t requestID, const IDBIdentifier& transactionIdentifier, uint64_t newVersion)
{
ASSERT(!RunLoop::isMain());
ASSERT(m_backingStore);
bool success = m_backingStore->changeDatabaseVersion(transactionIdentifier, newVersion);
postMainThreadTask(createCrossThreadTask(*this, &UniqueIDBDatabase::didChangeDatabaseVersion, requestID, success));
}
void UniqueIDBDatabase::createObjectStoreInBackingStore(uint64_t requestID, const IDBIdentifier& transactionIdentifier, const IDBObjectStoreMetadata& metadata)
{
ASSERT(!RunLoop::isMain());
ASSERT(m_backingStore);
bool success = m_backingStore->createObjectStore(transactionIdentifier, metadata);
postMainThreadTask(createCrossThreadTask(*this, &UniqueIDBDatabase::didCreateObjectStore, requestID, success));
}
void UniqueIDBDatabase::deleteObjectStoreInBackingStore(uint64_t requestID, const IDBIdentifier& transactionIdentifier, int64_t objectStoreID)
{
ASSERT(!RunLoop::isMain());
ASSERT(m_backingStore);
bool success = m_backingStore->deleteObjectStore(transactionIdentifier, objectStoreID);
postMainThreadTask(createCrossThreadTask(*this, &UniqueIDBDatabase::didDeleteObjectStore, requestID, success));
}
void UniqueIDBDatabase::clearObjectStoreInBackingStore(uint64_t requestID, const IDBIdentifier& transactionIdentifier, int64_t objectStoreID)
{
ASSERT(!RunLoop::isMain());
ASSERT(m_backingStore);
bool success = m_backingStore->clearObjectStore(transactionIdentifier, objectStoreID);
postMainThreadTask(createCrossThreadTask(*this, &UniqueIDBDatabase::didClearObjectStore, requestID, success));
}
void UniqueIDBDatabase::createIndexInBackingStore(uint64_t requestID, const IDBIdentifier& transactionIdentifier, int64_t objectStoreID, const IDBIndexMetadata& metadata)
{
ASSERT(!RunLoop::isMain());
ASSERT(m_backingStore);
bool success = m_backingStore->createIndex(transactionIdentifier, objectStoreID, metadata);
postMainThreadTask(createCrossThreadTask(*this, &UniqueIDBDatabase::didCreateIndex, requestID, success));
}
void UniqueIDBDatabase::deleteIndexInBackingStore(uint64_t requestID, const IDBIdentifier& transactionIdentifier, int64_t objectStoreID, int64_t indexID)
{
ASSERT(!RunLoop::isMain());
ASSERT(m_backingStore);
bool success = m_backingStore->deleteIndex(transactionIdentifier, objectStoreID, indexID);
postMainThreadTask(createCrossThreadTask(*this, &UniqueIDBDatabase::didDeleteIndex, requestID, success));
}
void UniqueIDBDatabase::putRecordInBackingStore(uint64_t requestID, const IDBIdentifier& transaction, const IDBObjectStoreMetadata& objectStoreMetadata, const IDBKeyData& inputKeyData, const Vector<uint8_t>& value, int64_t putMode, const Vector<int64_t>& indexIDs, const Vector<Vector<IDBKeyData>>& indexKeys)
{
ASSERT(!RunLoop::isMain());
ASSERT(m_backingStore);
bool keyWasGenerated = false;
IDBKeyData key;
int64_t keyNumber = 0;
if (putMode != IDBDatabaseBackend::CursorUpdate && objectStoreMetadata.autoIncrement && inputKeyData.isNull()) {
if (!m_backingStore->generateKeyNumber(transaction, objectStoreMetadata.id, keyNumber)) {
postMainThreadTask(createCrossThreadTask(*this, &UniqueIDBDatabase::didPutRecordInBackingStore, requestID, IDBKeyData(), IDBDatabaseException::UnknownError, ASCIILiteral("Internal backing store error checking for key existence")));
return;
}
key.setNumberValue(keyNumber);
keyWasGenerated = true;
} else
key = inputKeyData;
if (putMode == IDBDatabaseBackend::AddOnly) {
bool keyExists;
if (!m_backingStore->keyExistsInObjectStore(transaction, objectStoreMetadata.id, key, keyExists)) {
postMainThreadTask(createCrossThreadTask(*this, &UniqueIDBDatabase::didPutRecordInBackingStore, requestID, IDBKeyData(), IDBDatabaseException::UnknownError, ASCIILiteral("Internal backing store error checking for key existence")));
return;
}
if (keyExists) {
postMainThreadTask(createCrossThreadTask(*this, &UniqueIDBDatabase::didPutRecordInBackingStore, requestID, IDBKeyData(), IDBDatabaseException::ConstraintError, ASCIILiteral("Key already exists in the object store")));
return;
}
}
// The spec says that even if we're about to overwrite the record, perform the steps to delete it first.
// This is important because formally deleting it from from the object store also removes it from the appropriate indexes.
if (!m_backingStore->deleteRecord(transaction, objectStoreMetadata.id, key)) {
postMainThreadTask(createCrossThreadTask(*this, &UniqueIDBDatabase::didPutRecordInBackingStore, requestID, IDBKeyData(), IDBDatabaseException::UnknownError, ASCIILiteral("Replacing an existing key in backing store, unable to delete previous record.")));
return;
}
if (!m_backingStore->putRecord(transaction, objectStoreMetadata.id, key, value.data(), value.size())) {
postMainThreadTask(createCrossThreadTask(*this, &UniqueIDBDatabase::didPutRecordInBackingStore, requestID, IDBKeyData(), IDBDatabaseException::UnknownError, ASCIILiteral("Internal backing store error putting a record")));
return;
}
ASSERT(indexIDs.size() == indexKeys.size());
for (size_t i = 0; i < indexIDs.size(); ++i) {
for (size_t j = 0; j < indexKeys[i].size(); ++j) {
if (!m_backingStore->putIndexRecord(transaction, objectStoreMetadata.id, indexIDs[i], key, indexKeys[i][j])) {
postMainThreadTask(createCrossThreadTask(*this, &UniqueIDBDatabase::didPutRecordInBackingStore, requestID, IDBKeyData(), IDBDatabaseException::UnknownError, ASCIILiteral("Internal backing store error writing index key")));
return;
}
}
}
m_backingStore->notifyCursorsOfChanges(transaction, objectStoreMetadata.id);
if (putMode != IDBDatabaseBackend::CursorUpdate && objectStoreMetadata.autoIncrement && key.type() == KeyType::Number) {
if (!m_backingStore->updateKeyGeneratorNumber(transaction, objectStoreMetadata.id, keyNumber, keyWasGenerated)) {
postMainThreadTask(createCrossThreadTask(*this, &UniqueIDBDatabase::didPutRecordInBackingStore, requestID, IDBKeyData(), IDBDatabaseException::UnknownError, ASCIILiteral("Internal backing store error updating key generator")));
return;
}
}
postMainThreadTask(createCrossThreadTask(*this, &UniqueIDBDatabase::didPutRecordInBackingStore, requestID, key, 0, String(StringImpl::empty())));
}
void UniqueIDBDatabase::didPutRecordInBackingStore(uint64_t requestID, const IDBKeyData& keyData, uint32_t errorCode, const String& errorMessage)
{
m_pendingDatabaseTasks.take(requestID).get().completeRequest(keyData, errorCode, errorMessage);
}
void UniqueIDBDatabase::getRecordFromBackingStore(uint64_t requestID, const IDBIdentifier& transaction, const IDBObjectStoreMetadata& objectStoreMetadata, int64_t indexID, const IDBKeyRangeData& keyRangeData, IndexedDB::CursorType cursorType)
{
ASSERT(!RunLoop::isMain());
ASSERT(m_backingStore);
RefPtr<IDBKeyRange> keyRange = keyRangeData.maybeCreateIDBKeyRange();
ASSERT(keyRange);
if (!keyRange) {
postMainThreadTask(createCrossThreadTask(*this, &UniqueIDBDatabase::didGetRecordFromBackingStore, requestID, IDBGetResult(), IDBDatabaseException::UnknownError, ASCIILiteral("Invalid IDBKeyRange requested from backing store")));
return;
}
if (indexID == IDBIndexMetadata::InvalidId) {
// IDBObjectStore get record
RefPtr<SharedBuffer> result;
if (keyRange->isOnlyKey()) {
if (!m_backingStore->getKeyRecordFromObjectStore(transaction, objectStoreMetadata.id, *keyRange->lower(), result))
postMainThreadTask(createCrossThreadTask(*this, &UniqueIDBDatabase::didGetRecordFromBackingStore, requestID, IDBGetResult(), IDBDatabaseException::UnknownError, ASCIILiteral("Failed to get key record from object store in backing store")));
else {
IDBGetResult getResult = result ? IDBGetResult(result.release(), keyRange->lower(), objectStoreMetadata.keyPath) : IDBGetResult();
postMainThreadTask(createCrossThreadTask(*this, &UniqueIDBDatabase::didGetRecordFromBackingStore, requestID, getResult, 0, String(StringImpl::empty())));
}
return;
}
RefPtr<IDBKey> resultKey;
if (!m_backingStore->getKeyRangeRecordFromObjectStore(transaction, objectStoreMetadata.id, *keyRange, result, resultKey))
postMainThreadTask(createCrossThreadTask(*this, &UniqueIDBDatabase::didGetRecordFromBackingStore, requestID, IDBGetResult(), IDBDatabaseException::UnknownError, ASCIILiteral("Failed to get key range record from object store in backing store")));
else {
IDBGetResult getResult = result ? IDBGetResult(result.release(), resultKey.release(), objectStoreMetadata.keyPath) : IDBGetResult();
postMainThreadTask(createCrossThreadTask(*this, &UniqueIDBDatabase::didGetRecordFromBackingStore, requestID, getResult, 0, String(StringImpl::empty())));
}
return;
}
// IDBIndex get record
IDBGetResult result;
if (!m_backingStore->getIndexRecord(transaction, objectStoreMetadata.id, indexID, keyRangeData, cursorType, result)) {
postMainThreadTask(createCrossThreadTask(*this, &UniqueIDBDatabase::didGetRecordFromBackingStore, requestID, IDBGetResult(), IDBDatabaseException::UnknownError, ASCIILiteral("Failed to get index record from backing store")));
return;
}
// We must return a key path to know how to inject the result key into the result value object.
result.setKeyPath(objectStoreMetadata.keyPath);
postMainThreadTask(createCrossThreadTask(*this, &UniqueIDBDatabase::didGetRecordFromBackingStore, requestID, result, 0, String(StringImpl::empty())));
}
void UniqueIDBDatabase::didGetRecordFromBackingStore(uint64_t requestID, const IDBGetResult& result, uint32_t errorCode, const String& errorMessage)
{
m_pendingDatabaseTasks.take(requestID).get().completeRequest(result, errorCode, errorMessage);
}
void UniqueIDBDatabase::openCursorInBackingStore(uint64_t requestID, const IDBIdentifier& transactionIdentifier, int64_t objectStoreID, int64_t indexID, IndexedDB::CursorDirection cursorDirection, IndexedDB::CursorType cursorType, IDBDatabaseBackend::TaskType taskType, const IDBKeyRangeData& keyRange)
{
ASSERT(!RunLoop::isMain());
ASSERT(m_backingStore);
int64_t cursorID = 0;
IDBKeyData key;
IDBKeyData primaryKey;
Vector<uint8_t> valueBuffer;
int32_t errorCode = 0;
String errorMessage;
bool success = m_backingStore->openCursor(transactionIdentifier, objectStoreID, indexID, cursorDirection, cursorType, taskType, keyRange, cursorID, key, primaryKey, valueBuffer);
if (!success) {
errorCode = IDBDatabaseException::UnknownError;
errorMessage = ASCIILiteral("Unknown error opening cursor in backing store");
}
postMainThreadTask(createCrossThreadTask(*this, &UniqueIDBDatabase::didOpenCursorInBackingStore, requestID, cursorID, key, primaryKey, valueBuffer, errorCode, errorMessage));
}
void UniqueIDBDatabase::didOpenCursorInBackingStore(uint64_t requestID, int64_t cursorID, const IDBKeyData& key, const IDBKeyData& primaryKey, const Vector<uint8_t>& valueBuffer, uint32_t errorCode, const String& errorMessage)
{
m_pendingDatabaseTasks.take(requestID).get().completeRequest(cursorID, key, primaryKey, SharedBuffer::create(valueBuffer.data(), valueBuffer.size()), errorCode, errorMessage);
}
void UniqueIDBDatabase::advanceCursorInBackingStore(uint64_t requestID, const IDBIdentifier& cursorIdentifier, uint64_t count)
{
IDBKeyData key;
IDBKeyData primaryKey;
Vector<uint8_t> valueBuffer;
int32_t errorCode = 0;
String errorMessage;
bool success = m_backingStore->advanceCursor(cursorIdentifier, count, key, primaryKey, valueBuffer);
if (!success) {
errorCode = IDBDatabaseException::UnknownError;
errorMessage = ASCIILiteral("Unknown error advancing cursor in backing store");
}
postMainThreadTask(createCrossThreadTask(*this, &UniqueIDBDatabase::didAdvanceCursorInBackingStore, requestID, key, primaryKey, valueBuffer, errorCode, errorMessage));
}
void UniqueIDBDatabase::didAdvanceCursorInBackingStore(uint64_t requestID, const IDBKeyData& key, const IDBKeyData& primaryKey, const Vector<uint8_t>& valueBuffer, uint32_t errorCode, const String& errorMessage)
{
m_pendingDatabaseTasks.take(requestID).get().completeRequest(key, primaryKey, SharedBuffer::create(valueBuffer.data(), valueBuffer.size()), errorCode, errorMessage);
}
void UniqueIDBDatabase::iterateCursorInBackingStore(uint64_t requestID, const IDBIdentifier& cursorIdentifier, const IDBKeyData& iterateKey)
{
IDBKeyData key;
IDBKeyData primaryKey;
Vector<uint8_t> valueBuffer;
int32_t errorCode = 0;
String errorMessage;
bool success = m_backingStore->iterateCursor(cursorIdentifier, iterateKey, key, primaryKey, valueBuffer);
if (!success) {
errorCode = IDBDatabaseException::UnknownError;
errorMessage = ASCIILiteral("Unknown error iterating cursor in backing store");
}
postMainThreadTask(createCrossThreadTask(*this, &UniqueIDBDatabase::didIterateCursorInBackingStore, requestID, key, primaryKey, valueBuffer, errorCode, errorMessage));
}
void UniqueIDBDatabase::didIterateCursorInBackingStore(uint64_t requestID, const IDBKeyData& key, const IDBKeyData& primaryKey, const Vector<uint8_t>& valueBuffer, uint32_t errorCode, const String& errorMessage)
{
m_pendingDatabaseTasks.take(requestID).get().completeRequest(key, primaryKey, SharedBuffer::create(valueBuffer.data(), valueBuffer.size()), errorCode, errorMessage);
}
void UniqueIDBDatabase::countInBackingStore(uint64_t requestID, const IDBIdentifier& transactionIdentifier, int64_t objectStoreID, int64_t indexID, const IDBKeyRangeData& keyRangeData)
{
int64_t count;
if (!m_backingStore->count(transactionIdentifier, objectStoreID, indexID, keyRangeData, count)) {
LOG_ERROR("Failed to get count from backing store.");
postMainThreadTask(createCrossThreadTask(*this, &UniqueIDBDatabase::didCountInBackingStore, requestID, 0, IDBDatabaseException::UnknownError, ASCIILiteral("Failed to get count from backing store")));
return;
}
postMainThreadTask(createCrossThreadTask(*this, &UniqueIDBDatabase::didCountInBackingStore, requestID, count, 0, String(StringImpl::empty())));
}
void UniqueIDBDatabase::didCountInBackingStore(uint64_t requestID, int64_t count, uint32_t errorCode, const String& errorMessage)
{
m_pendingDatabaseTasks.take(requestID).get().completeRequest(count, errorCode, errorMessage);
}
void UniqueIDBDatabase::deleteRangeInBackingStore(uint64_t requestID, const IDBIdentifier& transactionIdentifier, int64_t objectStoreID, const IDBKeyRangeData& keyRangeData)
{
if (!m_backingStore->deleteRange(transactionIdentifier, objectStoreID, keyRangeData)) {
LOG_ERROR("Failed to delete range from backing store.");
postMainThreadTask(createCrossThreadTask(*this, &UniqueIDBDatabase::didDeleteRangeInBackingStore, requestID, IDBDatabaseException::UnknownError, ASCIILiteral("Failed to get count from backing store")));
return;
}
m_backingStore->notifyCursorsOfChanges(transactionIdentifier, objectStoreID);
postMainThreadTask(createCrossThreadTask(*this, &UniqueIDBDatabase::didDeleteRangeInBackingStore, requestID, 0, String(StringImpl::empty())));
}
void UniqueIDBDatabase::didDeleteRangeInBackingStore(uint64_t requestID, uint32_t errorCode, const String& errorMessage)
{
m_pendingDatabaseTasks.take(requestID).get().completeRequest(errorCode, errorMessage);
}
void UniqueIDBDatabase::didEstablishTransaction(const IDBIdentifier& transactionIdentifier, bool success)
{
ASSERT(RunLoop::isMain());
if (!success)
return;
auto transactions = m_establishedTransactions.add(&transactionIdentifier.connection(), HashSet<IDBIdentifier>());
transactions.iterator->value.add(transactionIdentifier);
}
void UniqueIDBDatabase::didResetTransaction(const IDBIdentifier& transactionIdentifier, bool success)
{
ASSERT(RunLoop::isMain());
if (!success)
return;
auto transactions = m_establishedTransactions.find(&transactionIdentifier.connection());
if (transactions != m_establishedTransactions.end())
transactions.get()->value.remove(transactionIdentifier);
}
void UniqueIDBDatabase::resetAllTransactions(const DatabaseProcessIDBConnection& connection)
{
ASSERT(RunLoop::isMain());
auto transactions = m_establishedTransactions.find(&connection);
if (transactions == m_establishedTransactions.end() || !m_acceptingNewRequests)
return;
for (auto& transactionIdentifier : transactions.get()->value) {
m_pendingTransactionRollbacks.add(transactionIdentifier);
if (!m_pendingTransactionRequests.contains(transactionIdentifier))
finalizeRollback(transactionIdentifier);
}
}
void UniqueIDBDatabase::finalizeRollback(const WebKit::IDBIdentifier& transactionId)
{
ASSERT(RunLoop::isMain());
ASSERT(m_pendingTransactionRollbacks.contains(transactionId));
ASSERT(!m_pendingTransactionRequests.contains(transactionId));
rollbackTransaction(transactionId, [this, transactionId](bool) {
ASSERT(RunLoop::isMain());
if (m_pendingTransactionRequests.contains(transactionId))
return;
ASSERT(m_pendingTransactionRollbacks.contains(transactionId));
m_pendingTransactionRollbacks.remove(transactionId);
resetTransaction(transactionId, [this, transactionId](bool) {
if (m_acceptingNewRequests && m_connections.isEmpty() && m_pendingTransactionRollbacks.isEmpty()) {
shutdown(UniqueIDBDatabaseShutdownType::NormalShutdown);
DatabaseProcess::singleton().removeUniqueIDBDatabase(*this);
}
});
});
}
String UniqueIDBDatabase::absoluteDatabaseDirectory() const
{
ASSERT(RunLoop::isMain());
return DatabaseProcess::singleton().absoluteIndexedDatabasePathFromDatabaseRelativePath(m_databaseRelativeDirectory);
}
void UniqueIDBDatabase::postMainThreadTask(std::unique_ptr<CrossThreadTask> task, DatabaseTaskType taskType)
{
ASSERT(!RunLoop::isMain());
if (!m_acceptingNewRequests && taskType == DatabaseTaskType::Normal)
return;
LockHolder locker(m_mainThreadTaskMutex);
m_mainThreadTasks.append(WTF::move(task));
RefPtr<UniqueIDBDatabase> database(this);
RunLoop::main().dispatch([database] {
database->performNextMainThreadTask();
});
}
bool UniqueIDBDatabase::performNextMainThreadTask()
{
ASSERT(RunLoop::isMain());
bool moreTasks;
std::unique_ptr<CrossThreadTask> task;
{
LockHolder locker(m_mainThreadTaskMutex);
// This database might be shutting down, in which case the task queue might be empty.
if (m_mainThreadTasks.isEmpty())
return false;
task = m_mainThreadTasks.takeFirst();
moreTasks = !m_mainThreadTasks.isEmpty();
}
task->performTask();
return moreTasks;
}
void UniqueIDBDatabase::postDatabaseTask(std::unique_ptr<CrossThreadTask> task, DatabaseTaskType taskType)
{
ASSERT(RunLoop::isMain());
if (!m_acceptingNewRequests && taskType == DatabaseTaskType::Normal)
return;
LockHolder locker(m_databaseTaskMutex);
m_databaseTasks.append(WTF::move(task));
RefPtr<UniqueIDBDatabase> database(this);
DatabaseProcess::singleton().queue().dispatch([database] {
database->performNextDatabaseTask();
});
}
void UniqueIDBDatabase::performNextDatabaseTask()
{
ASSERT(!RunLoop::isMain());
// It is possible that this database might be shutting down on the main thread.
// In this case, immediately after releasing m_databaseTaskMutex, this database might get deleted.
// We take a ref() to make sure the database is still live while this last task is performed.
RefPtr<UniqueIDBDatabase> protector(this);
std::unique_ptr<CrossThreadTask> task;
{
LockHolder locker(m_databaseTaskMutex);
// This database might be shutting down on the main thread, in which case the task queue might be empty.
if (m_databaseTasks.isEmpty())
return;
task = m_databaseTasks.takeFirst();
}
task->performTask();
}
} // namespace WebKit
#endif // ENABLE(INDEXED_DATABASE) && ENABLE(DATABASE_PROCESS)