#include "config.h"
#include "WebIDBServer.h"
#include "WebIDBConnectionToClient.h"
#include "WebIDBServerMessages.h"
#include <WebCore/SQLiteDatabaseTracker.h>
#include <WebCore/StorageQuotaManager.h>
#include <wtf/threads/BinarySemaphore.h>
#if ENABLE(INDEXED_DATABASE)
namespace WebKit {
Ref<WebIDBServer> WebIDBServer::create(PAL::SessionID sessionID, const String& directory, WebCore::IDBServer::IDBServer::StorageQuotaManagerSpaceRequester&& spaceRequester, CompletionHandler<void()>&& closeCallback)
{
return adoptRef(*new WebIDBServer(sessionID, directory, WTFMove(spaceRequester), WTFMove(closeCallback)));
}
WebIDBServer::WebIDBServer(PAL::SessionID sessionID, const String& directory, WebCore::IDBServer::IDBServer::StorageQuotaManagerSpaceRequester&& spaceRequester, CompletionHandler<void()>&& closeCallback)
: CrossThreadTaskHandler("com.apple.WebKit.IndexedDBServer", WTF::CrossThreadTaskHandler::AutodrainedPoolForRunLoop::Use)
, m_dataTaskCounter([this](RefCounterEvent) { tryClose(); })
, m_closeCallback(WTFMove(closeCallback))
{
ASSERT(RunLoop::isMain());
BinarySemaphore semaphore;
postTask([this, protectedThis = makeRef(*this), &semaphore, sessionID, directory = directory.isolatedCopy(), spaceRequester = WTFMove(spaceRequester)] () mutable {
m_server = makeUnique<WebCore::IDBServer::IDBServer>(sessionID, directory, WTFMove(spaceRequester));
semaphore.signal();
});
semaphore.wait();
}
WebIDBServer::~WebIDBServer()
{
ASSERT(RunLoop::isMain());
ASSERT(!m_closeCallback);
}
void WebIDBServer::getOrigins(CompletionHandler<void(HashSet<WebCore::SecurityOriginData>&&)>&& callback)
{
ASSERT(RunLoop::isMain());
postTask([this, protectedThis = makeRef(*this), callback = WTFMove(callback), token = m_dataTaskCounter.count()]() mutable {
ASSERT(!RunLoop::isMain());
LockHolder locker(m_server->lock());
postTaskReply(CrossThreadTask([callback = WTFMove(callback), token = WTFMove(token), origins = crossThreadCopy(m_server->getOrigins())]() mutable {
callback(WTFMove(origins));
}));
});
}
void WebIDBServer::closeAndDeleteDatabasesModifiedSince(WallTime modificationTime, CompletionHandler<void()>&& callback)
{
ASSERT(RunLoop::isMain());
postTask([this, protectedThis = makeRef(*this), modificationTime, callback = WTFMove(callback), token = m_dataTaskCounter.count()]() mutable {
ASSERT(!RunLoop::isMain());
LockHolder locker(m_server->lock());
m_server->closeAndDeleteDatabasesModifiedSince(modificationTime);
postTaskReply(CrossThreadTask([callback = WTFMove(callback), token = WTFMove(token)]() mutable {
callback();
}));
});
}
void WebIDBServer::closeAndDeleteDatabasesForOrigins(const Vector<WebCore::SecurityOriginData>& originDatas, CompletionHandler<void()>&& callback)
{
ASSERT(RunLoop::isMain());
postTask([this, protectedThis = makeRef(*this), originDatas = originDatas.isolatedCopy(), callback = WTFMove(callback), token = m_dataTaskCounter.count()] () mutable {
ASSERT(!RunLoop::isMain());
LockHolder locker(m_server->lock());
m_server->closeAndDeleteDatabasesForOrigins(originDatas);
postTaskReply(CrossThreadTask([callback = WTFMove(callback), token = WTFMove(token)]() mutable {
callback();
}));
});
}
void WebIDBServer::renameOrigin(const WebCore::SecurityOriginData& oldOrigin, const WebCore::SecurityOriginData& newOrigin, CompletionHandler<void()>&& callback)
{
ASSERT(RunLoop::isMain());
postTask([this, protectedThis = makeRef(*this), oldOrigin = oldOrigin.isolatedCopy(), newOrigin = newOrigin.isolatedCopy(), callback = WTFMove(callback), token = m_dataTaskCounter.count()] () mutable {
ASSERT(!RunLoop::isMain());
LockHolder locker(m_server->lock());
m_server->renameOrigin(oldOrigin, newOrigin);
postTaskReply(CrossThreadTask([callback = WTFMove(callback), token = WTFMove(token)]() mutable {
callback();
}));
});
}
void WebIDBServer::suspend()
{
ASSERT(RunLoop::isMain());
if (m_isSuspended)
return;
m_isSuspended = true;
m_server->lock().lock();
m_server->stopDatabaseActivitiesOnMainThread();
}
void WebIDBServer::resume()
{
ASSERT(RunLoop::isMain());
if (!m_isSuspended)
return;
m_isSuspended = false;
m_server->lock().unlock();
}
void WebIDBServer::openDatabase(const WebCore::IDBRequestData& requestData)
{
ASSERT(!RunLoop::isMain());
LockHolder locker(m_server->lock());
m_server->openDatabase(requestData);
}
void WebIDBServer::deleteDatabase(const WebCore::IDBRequestData& requestData)
{
ASSERT(!RunLoop::isMain());
LockHolder locker(m_server->lock());
m_server->deleteDatabase(requestData);
}
void WebIDBServer::abortTransaction(const WebCore::IDBResourceIdentifier& transactionIdentifier)
{
ASSERT(!RunLoop::isMain());
LockHolder locker(m_server->lock());
m_server->abortTransaction(transactionIdentifier);
}
void WebIDBServer::commitTransaction(const WebCore::IDBResourceIdentifier& transactionIdentifier)
{
ASSERT(!RunLoop::isMain());
LockHolder locker(m_server->lock());
m_server->commitTransaction(transactionIdentifier);
}
void WebIDBServer::didFinishHandlingVersionChangeTransaction(uint64_t databaseConnectionIdentifier, const WebCore::IDBResourceIdentifier& transactionIdentifier)
{
ASSERT(!RunLoop::isMain());
LockHolder locker(m_server->lock());
m_server->didFinishHandlingVersionChangeTransaction(databaseConnectionIdentifier, transactionIdentifier);
}
void WebIDBServer::createObjectStore(const WebCore::IDBRequestData& requestData, const WebCore::IDBObjectStoreInfo& objectStoreInfo)
{
ASSERT(!RunLoop::isMain());
LockHolder locker(m_server->lock());
m_server->createObjectStore(requestData, objectStoreInfo);
}
void WebIDBServer::deleteObjectStore(const WebCore::IDBRequestData& requestData, const String& objectStoreName)
{
ASSERT(!RunLoop::isMain());
LockHolder locker(m_server->lock());
m_server->deleteObjectStore(requestData, objectStoreName);
}
void WebIDBServer::renameObjectStore(const WebCore::IDBRequestData& requestData, uint64_t objectStoreIdentifier, const String& newName)
{
ASSERT(!RunLoop::isMain());
LockHolder locker(m_server->lock());
m_server->renameObjectStore(requestData, objectStoreIdentifier, newName);
}
void WebIDBServer::clearObjectStore(const WebCore::IDBRequestData& requestData, uint64_t objectStoreIdentifier)
{
ASSERT(!RunLoop::isMain());
LockHolder locker(m_server->lock());
m_server->clearObjectStore(requestData, objectStoreIdentifier);
}
void WebIDBServer::createIndex(const WebCore::IDBRequestData& requestData, const WebCore::IDBIndexInfo& indexInfo)
{
ASSERT(!RunLoop::isMain());
LockHolder locker(m_server->lock());
m_server->createIndex(requestData, indexInfo);
}
void WebIDBServer::deleteIndex(const WebCore::IDBRequestData& requestData, uint64_t objectStoreIdentifier, const String& indexName)
{
ASSERT(!RunLoop::isMain());
LockHolder locker(m_server->lock());
m_server->deleteIndex(requestData, objectStoreIdentifier, indexName);
}
void WebIDBServer::renameIndex(const WebCore::IDBRequestData& requestData, uint64_t objectStoreIdentifier, uint64_t indexIdentifier, const String& newName)
{
ASSERT(!RunLoop::isMain());
LockHolder locker(m_server->lock());
m_server->renameIndex(requestData, objectStoreIdentifier, indexIdentifier, newName);
}
void WebIDBServer::putOrAdd(const WebCore::IDBRequestData& requestData, const WebCore::IDBKeyData& keyData, const WebCore::IDBValue& value, WebCore::IndexedDB::ObjectStoreOverwriteMode overWriteMode)
{
ASSERT(!RunLoop::isMain());
LockHolder locker(m_server->lock());
m_server->putOrAdd(requestData, keyData, value, overWriteMode);
}
void WebIDBServer::getRecord(const WebCore::IDBRequestData& requestData, const WebCore::IDBGetRecordData& getRecordData)
{
ASSERT(!RunLoop::isMain());
LockHolder locker(m_server->lock());
m_server->getRecord(requestData, getRecordData);
}
void WebIDBServer::getAllRecords(const WebCore::IDBRequestData& requestData, const WebCore::IDBGetAllRecordsData& getAllRecordsData)
{
ASSERT(!RunLoop::isMain());
LockHolder locker(m_server->lock());
m_server->getAllRecords(requestData, getAllRecordsData);
}
void WebIDBServer::getCount(const WebCore::IDBRequestData& requestData, const WebCore::IDBKeyRangeData& keyRangeData)
{
ASSERT(!RunLoop::isMain());
LockHolder locker(m_server->lock());
m_server->getCount(requestData, keyRangeData);
}
void WebIDBServer::deleteRecord(const WebCore::IDBRequestData& requestData, const WebCore::IDBKeyRangeData& keyRangeData)
{
ASSERT(!RunLoop::isMain());
LockHolder locker(m_server->lock());
m_server->deleteRecord(requestData, keyRangeData);
}
void WebIDBServer::openCursor(const WebCore::IDBRequestData& requestData, const WebCore::IDBCursorInfo& cursorInfo)
{
ASSERT(!RunLoop::isMain());
LockHolder locker(m_server->lock());
m_server->openCursor(requestData, cursorInfo);
}
void WebIDBServer::iterateCursor(const WebCore::IDBRequestData& requestData, const WebCore::IDBIterateCursorData& iterateCursorData)
{
ASSERT(!RunLoop::isMain());
LockHolder locker(m_server->lock());
m_server->iterateCursor(requestData, iterateCursorData);
}
void WebIDBServer::establishTransaction(uint64_t databaseConnectionIdentifier, const WebCore::IDBTransactionInfo& transactionInfo)
{
ASSERT(!RunLoop::isMain());
LockHolder locker(m_server->lock());
m_server->establishTransaction(databaseConnectionIdentifier, transactionInfo);
}
void WebIDBServer::databaseConnectionPendingClose(uint64_t databaseConnectionIdentifier)
{
ASSERT(!RunLoop::isMain());
LockHolder locker(m_server->lock());
m_server->databaseConnectionPendingClose(databaseConnectionIdentifier);
}
void WebIDBServer::databaseConnectionClosed(uint64_t databaseConnectionIdentifier)
{
ASSERT(!RunLoop::isMain());
LockHolder locker(m_server->lock());
m_server->databaseConnectionClosed(databaseConnectionIdentifier);
}
void WebIDBServer::abortOpenAndUpgradeNeeded(uint64_t databaseConnectionIdentifier, const WebCore::IDBResourceIdentifier& transactionIdentifier)
{
ASSERT(!RunLoop::isMain());
LockHolder locker(m_server->lock());
m_server->abortOpenAndUpgradeNeeded(databaseConnectionIdentifier, transactionIdentifier);
}
void WebIDBServer::didFireVersionChangeEvent(uint64_t databaseConnectionIdentifier, const WebCore::IDBResourceIdentifier& requestIdentifier, WebCore::IndexedDB::ConnectionClosedOnBehalfOfServer connectionClosed)
{
ASSERT(!RunLoop::isMain());
LockHolder locker(m_server->lock());
m_server->didFireVersionChangeEvent(databaseConnectionIdentifier, requestIdentifier, connectionClosed);
}
void WebIDBServer::openDBRequestCancelled(const WebCore::IDBRequestData& requestData)
{
ASSERT(!RunLoop::isMain());
LockHolder locker(m_server->lock());
m_server->openDBRequestCancelled(requestData);
}
void WebIDBServer::getAllDatabaseNamesAndVersions(IPC::Connection& connection, const WebCore::IDBResourceIdentifier& requestIdentifier, const WebCore::ClientOrigin& origin)
{
ASSERT(!RunLoop::isMain());
auto* webIDBConnection = m_connectionMap.get(connection.uniqueID());
ASSERT(webIDBConnection);
LockHolder locker(m_server->lock());
m_server->getAllDatabaseNamesAndVersions(webIDBConnection->identifier(), requestIdentifier, origin);
}
void WebIDBServer::addConnection(IPC::Connection& connection, WebCore::ProcessIdentifier processIdentifier)
{
ASSERT(RunLoop::isMain());
postTask([this, protectedThis = makeRef(*this), protectedConnection = makeRefPtr(connection), processIdentifier] {
auto[iter, isNewEntry] = m_connectionMap.ensure(protectedConnection->uniqueID(), [&] {
return makeUnique<WebIDBConnectionToClient>(*protectedConnection, processIdentifier);
});
ASSERT_UNUSED(isNewEntry, isNewEntry);
LockHolder locker(m_server->lock());
m_server->registerConnection(iter->value->connectionToClient());
});
m_connections.add(&connection);
connection.addThreadMessageReceiver(Messages::WebIDBServer::messageReceiverName(), this);
}
void WebIDBServer::removeConnection(IPC::Connection& connection)
{
ASSERT(RunLoop::isMain());
auto* takenConnection = m_connections.take(&connection);
if (!takenConnection)
return;
takenConnection->removeThreadMessageReceiver(Messages::WebIDBServer::messageReceiverName());
postTask([this, protectedThis = makeRef(*this), connectionID = connection.uniqueID()] {
auto connection = m_connectionMap.take(connectionID);
ASSERT(connection);
LockHolder locker(m_server->lock());
m_server->unregisterConnection(connection->connectionToClient());
});
tryClose();
}
void WebIDBServer::postTask(Function<void()>&& task)
{
ASSERT(RunLoop::isMain());
CrossThreadTaskHandler::postTask(CrossThreadTask(WTFMove(task)));
}
void WebIDBServer::dispatchToThread(Function<void()>&& task)
{
CrossThreadTaskHandler::postTask(CrossThreadTask(WTFMove(task)));
}
void WebIDBServer::close()
{
ASSERT(RunLoop::isMain());
if (!m_closeCallback)
return;
for (auto* connection : m_connections)
connection->removeThreadMessageReceiver(Messages::WebIDBServer::messageReceiverName());
CrossThreadTaskHandler::setCompletionCallback([protectedThis = makeRef(*this)]() mutable {
ASSERT(!RunLoop::isMain());
callOnMainRunLoop([protectedThis = WTFMove(protectedThis)]() mutable { });
});
postTask([this]() mutable {
m_connectionMap.clear();
m_server = nullptr;
CrossThreadTaskHandler::kill();
});
m_closeCallback();
}
void WebIDBServer::tryClose()
{
if (!m_connections.isEmpty() || m_dataTaskCounter.value())
return;
close();
}
} #endif