blob: 90fa1dfae8c869be5bf2c54ce2f1781c2728095d [file] [log] [blame]
/*
* Copyright (C) 2021 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 "NetworkStorageManager.h"
#include "FileSystemStorageHandleRegistry.h"
#include "FileSystemStorageManager.h"
#include "LocalStorageManager.h"
#include "Logging.h"
#include "NetworkStorageManagerMessages.h"
#include "OriginStorageManager.h"
#include "SessionStorageManager.h"
#include "StorageAreaBase.h"
#include "StorageAreaMapMessages.h"
#include "StorageAreaRegistry.h"
#include "WebsiteDataType.h"
#include <WebCore/SecurityOriginData.h>
#include <pal/crypto/CryptoDigest.h>
#include <wtf/Scope.h>
#include <wtf/SuspendableWorkQueue.h>
#include <wtf/persistence/PersistentCoders.h>
#include <wtf/text/Base64.h>
namespace WebKit {
static std::optional<WebCore::ClientOrigin> readOriginFromFile(const String& filePath)
{
ASSERT(!RunLoop::isMain());
if (filePath.isEmpty() || !FileSystem::fileExists(filePath))
return std::nullopt;
auto originFileHandle = FileSystem::openFile(filePath, FileSystem::FileOpenMode::Read);
auto closeFile = makeScopeExit([&] {
FileSystem::closeFile(originFileHandle);
});
if (!FileSystem::isHandleValid(originFileHandle))
return std::nullopt;
auto originContent = FileSystem::readEntireFile(originFileHandle);
if (!originContent)
return std::nullopt;
WTF::Persistence::Decoder decoder({ originContent->data(), originContent->size() });
std::optional<WebCore::ClientOrigin> origin;
decoder >> origin;
return origin;
}
static void writeOriginToFileIfNecessary(const String& filePath, const WebCore::ClientOrigin& origin)
{
if (filePath.isEmpty() || FileSystem::fileExists(filePath))
return;
FileSystem::makeAllDirectories(FileSystem::parentPath(filePath));
auto originFileHandle = FileSystem::openFile(filePath, FileSystem::FileOpenMode::ReadWrite);
auto closeFile = makeScopeExit([&] {
FileSystem::closeFile(originFileHandle);
});
if (!FileSystem::isHandleValid(originFileHandle)) {
LOG_ERROR("writeOriginToFileIfNecessary: Failed to open origin file '%s'", filePath.utf8().data());
return;
}
WTF::Persistence::Encoder encoder;
encoder << origin;
FileSystem::writeToFile(originFileHandle, encoder.buffer(), encoder.bufferSize());
}
static void deleteOriginFileIfNecessary(const String& filePath)
{
auto parentPath = FileSystem::parentPath(filePath);
auto children = FileSystem::listDirectory(parentPath);
if (children.size() == 1)
FileSystem::deleteFile(filePath);
}
Ref<NetworkStorageManager> NetworkStorageManager::create(PAL::SessionID sessionID, const String& path, const String& customLocalStoragePath)
{
return adoptRef(*new NetworkStorageManager(sessionID, path, customLocalStoragePath));
}
NetworkStorageManager::NetworkStorageManager(PAL::SessionID sessionID, const String& path, const String& customLocalStoragePath)
: m_sessionID(sessionID)
, m_queue(SuspendableWorkQueue::create("com.apple.WebKit.Storage"))
{
ASSERT(RunLoop::isMain());
m_queue->dispatch([this, protectedThis = Ref { *this }, path = path.isolatedCopy(), customLocalStoragePath = crossThreadCopy(customLocalStoragePath)]() mutable {
m_fileSystemStorageHandleRegistry = makeUnique<FileSystemStorageHandleRegistry>();
m_storageAreaRegistry = makeUnique<StorageAreaRegistry>();
m_path = path;
m_customLocalStoragePath = customLocalStoragePath;
if (!m_path.isEmpty()) {
auto saltPath = FileSystem::pathByAppendingComponent(m_path, "salt");
m_salt = valueOrDefault(FileSystem::readOrMakeSalt(saltPath));
}
});
}
NetworkStorageManager::~NetworkStorageManager()
{
ASSERT(RunLoop::isMain());
ASSERT(m_closed);
}
bool NetworkStorageManager::canHandleTypes(OptionSet<WebsiteDataType> types)
{
return types.contains(WebsiteDataType::LocalStorage) || types.contains(WebsiteDataType::SessionStorage) || types.contains(WebsiteDataType::FileSystem);
}
void NetworkStorageManager::close()
{
ASSERT(RunLoop::isMain());
if (m_closed)
return;
m_closed = true;
for (auto& connection : m_connections)
connection.removeWorkQueueMessageReceiver(Messages::NetworkStorageManager::messageReceiverName());
m_queue->dispatch([this, protectedThis = Ref { *this }]() mutable {
m_localOriginStorageManagers.clear();
m_fileSystemStorageHandleRegistry = nullptr;
RunLoop::main().dispatch([protectedThis = WTFMove(protectedThis)] { });
});
}
void NetworkStorageManager::startReceivingMessageFromConnection(IPC::Connection& connection)
{
ASSERT(RunLoop::isMain());
connection.addWorkQueueMessageReceiver(Messages::NetworkStorageManager::messageReceiverName(), m_queue.get(), this);
m_connections.add(connection);
}
void NetworkStorageManager::stopReceivingMessageFromConnection(IPC::Connection& connection)
{
ASSERT(RunLoop::isMain());
if (!m_connections.remove(connection))
return;
connection.removeWorkQueueMessageReceiver(Messages::NetworkStorageManager::messageReceiverName());
m_queue->dispatch([this, protectedThis = Ref { *this }, connection = connection.uniqueID()]() mutable {
for (auto& originStorageManager : m_localOriginStorageManagers.values())
originStorageManager->connectionClosed(connection);
RunLoop::main().dispatch([protectedThis = WTFMove(protectedThis)] { });
});
}
static String encode(const String& string, FileSystem::Salt salt)
{
auto crypto = PAL::CryptoDigest::create(PAL::CryptoDigest::Algorithm::SHA_256);
auto utf8String = string.utf8();
crypto->addBytes(utf8String.data(), utf8String.length());
crypto->addBytes(salt.data(), salt.size());
auto hash = crypto->computeHash();
return base64URLEncodeToString(hash.data(), hash.size());
}
static String originDirectoryPath(const String& rootPath, const WebCore::ClientOrigin& origin, FileSystem::Salt salt)
{
if (rootPath.isEmpty())
return String { };
auto encodedTopOrigin = encode(origin.topOrigin.toString(), salt);
auto encodedOpeningOrigin = encode(origin.clientOrigin.toString(), salt);
return FileSystem::pathByAppendingComponents(rootPath, { encodedTopOrigin, encodedOpeningOrigin });
}
static String originFilePath(const String& directory)
{
if (directory.isEmpty())
return String { };
return FileSystem::pathByAppendingComponent(directory, "origin"_s);
}
OriginStorageManager& NetworkStorageManager::localOriginStorageManager(const WebCore::ClientOrigin& origin)
{
ASSERT(!RunLoop::isMain());
return *m_localOriginStorageManagers.ensure(origin, [&] {
auto originDirectory = originDirectoryPath(m_path, origin, m_salt);
writeOriginToFileIfNecessary(originFilePath(originDirectory), origin);
return makeUnique<OriginStorageManager>(WTFMove(originDirectory), LocalStorageManager::localStorageFilePath(m_customLocalStoragePath, origin));
}).iterator->value;
}
bool NetworkStorageManager::removeOriginStorageManagerIfPossible(const WebCore::ClientOrigin& origin)
{
auto iterator = m_localOriginStorageManagers.find(origin);
if (iterator == m_localOriginStorageManagers.end())
return true;
if (iterator->value->isActive())
return false;
m_localOriginStorageManagers.remove(iterator);
return true;
}
void NetworkStorageManager::deleteOriginDirectoryIfPossible(const WebCore::ClientOrigin& origin)
{
bool isEmpty = localOriginStorageManager(origin).isEmpty();
bool removed = removeOriginStorageManagerIfPossible(origin);
if (!removed || !isEmpty)
return;
auto originDirectory = originDirectoryPath(m_path, origin, m_salt);
auto filePath = originFilePath(originDirectory);
deleteOriginFileIfNecessary(filePath);
FileSystem::deleteEmptyDirectory(originDirectory);
}
void NetworkStorageManager::persisted(const WebCore::ClientOrigin& origin, CompletionHandler<void(bool)>&& completionHandler)
{
ASSERT(!RunLoop::isMain());
completionHandler(localOriginStorageManager(origin).persisted());
}
void NetworkStorageManager::persist(const WebCore::ClientOrigin& origin, CompletionHandler<void(bool)>&& completionHandler)
{
ASSERT(!RunLoop::isMain());
localOriginStorageManager(origin).setPersisted(true);
completionHandler(true);
}
void NetworkStorageManager::clearStorageForTesting(CompletionHandler<void()>&& completionHandler)
{
ASSERT(RunLoop::isMain());
m_queue->dispatch([this, protectedThis = Ref { *this }, completionHandler = WTFMove(completionHandler)]() mutable {
// Reset persisted value.
for (auto& manager : m_localOriginStorageManagers.values())
manager->setPersisted(false);
RunLoop::main().dispatch([protectedThis = WTFMove(protectedThis), completionHandler = WTFMove(completionHandler)]() mutable {
completionHandler();
});
});
}
void NetworkStorageManager::fileSystemGetDirectory(IPC::Connection& connection, const WebCore::ClientOrigin& origin, CompletionHandler<void(Expected<WebCore::FileSystemHandleIdentifier, FileSystemStorageError>)>&& completionHandler)
{
ASSERT(!RunLoop::isMain());
completionHandler(localOriginStorageManager(origin).fileSystemStorageManager(*m_fileSystemStorageHandleRegistry).getDirectory(connection.uniqueID()));
}
void NetworkStorageManager::closeHandle(WebCore::FileSystemHandleIdentifier identifier)
{
ASSERT(!RunLoop::isMain());
if (auto handle = m_fileSystemStorageHandleRegistry->getHandle(identifier))
handle->close();
}
void NetworkStorageManager::isSameEntry(WebCore::FileSystemHandleIdentifier identifier, WebCore::FileSystemHandleIdentifier targetIdentifier, CompletionHandler<void(bool)>&& completionHandler)
{
ASSERT(!RunLoop::isMain());
auto handle = m_fileSystemStorageHandleRegistry->getHandle(identifier);
if (!handle)
return completionHandler(false);
completionHandler(handle->isSameEntry(targetIdentifier));
}
void NetworkStorageManager::move(WebCore::FileSystemHandleIdentifier identifier, WebCore::FileSystemHandleIdentifier destinationIdentifier, const String& newName, CompletionHandler<void(std::optional<FileSystemStorageError>)>&& completionHandler)
{
ASSERT(!RunLoop::isMain());
auto handle = m_fileSystemStorageHandleRegistry->getHandle(identifier);
if (!handle)
return completionHandler(FileSystemStorageError::Unknown);
completionHandler(handle->move(destinationIdentifier, newName));
}
void NetworkStorageManager::getFileHandle(IPC::Connection& connection, WebCore::FileSystemHandleIdentifier identifier, String&& name, bool createIfNecessary, CompletionHandler<void(Expected<WebCore::FileSystemHandleIdentifier, FileSystemStorageError>)>&& completionHandler)
{
ASSERT(!RunLoop::isMain());
auto handle = m_fileSystemStorageHandleRegistry->getHandle(identifier);
if (!handle)
return completionHandler(makeUnexpected(FileSystemStorageError::Unknown));
completionHandler(handle->getFileHandle(connection.uniqueID(), WTFMove(name), createIfNecessary));
}
void NetworkStorageManager::getDirectoryHandle(IPC::Connection& connection, WebCore::FileSystemHandleIdentifier identifier, String&& name, bool createIfNecessary, CompletionHandler<void(Expected<WebCore::FileSystemHandleIdentifier, FileSystemStorageError>)>&& completionHandler)
{
ASSERT(!RunLoop::isMain());
auto handle = m_fileSystemStorageHandleRegistry->getHandle(identifier);
if (!handle)
return completionHandler(makeUnexpected(FileSystemStorageError::Unknown));
completionHandler(handle->getDirectoryHandle(connection.uniqueID(), WTFMove(name), createIfNecessary));
}
void NetworkStorageManager::removeEntry(WebCore::FileSystemHandleIdentifier identifier, const String& name, bool deleteRecursively, CompletionHandler<void(std::optional<FileSystemStorageError>)>&& completionHandler)
{
ASSERT(!RunLoop::isMain());
auto handle = m_fileSystemStorageHandleRegistry->getHandle(identifier);
if (!handle)
return completionHandler(FileSystemStorageError::Unknown);
completionHandler(handle->removeEntry(name, deleteRecursively));
}
void NetworkStorageManager::resolve(WebCore::FileSystemHandleIdentifier identifier, WebCore::FileSystemHandleIdentifier targetIdentifier, CompletionHandler<void(Expected<Vector<String>, FileSystemStorageError>)>&& completionHandler)
{
ASSERT(!RunLoop::isMain());
auto handle = m_fileSystemStorageHandleRegistry->getHandle(identifier);
if (!handle)
return completionHandler(makeUnexpected(FileSystemStorageError::Unknown));
completionHandler(handle->resolve(targetIdentifier));
}
void NetworkStorageManager::getFile(WebCore::FileSystemHandleIdentifier identifier, CompletionHandler<void(Expected<String, FileSystemStorageError>)>&& completionHandler)
{
ASSERT(!RunLoop::isMain());
auto handle = m_fileSystemStorageHandleRegistry->getHandle(identifier);
if (!handle)
return completionHandler(makeUnexpected(FileSystemStorageError::Unknown));
completionHandler(handle->path());
}
void NetworkStorageManager::createSyncAccessHandle(WebCore::FileSystemHandleIdentifier identifier, CompletionHandler<void(Expected<AccessHandleInfo, FileSystemStorageError>)>&& completionHandler)
{
ASSERT(!RunLoop::isMain());
auto handle = m_fileSystemStorageHandleRegistry->getHandle(identifier);
if (!handle)
return completionHandler(makeUnexpected(FileSystemStorageError::Unknown));
completionHandler(handle->createSyncAccessHandle());
}
void NetworkStorageManager::closeSyncAccessHandle(WebCore::FileSystemHandleIdentifier identifier, WebCore::FileSystemSyncAccessHandleIdentifier accessHandleIdentifier, CompletionHandler<void(std::optional<FileSystemStorageError>)>&& completionHandler)
{
ASSERT(!RunLoop::isMain());
auto handle = m_fileSystemStorageHandleRegistry->getHandle(identifier);
if (!handle)
return completionHandler(FileSystemStorageError::Unknown);
completionHandler(handle->closeSyncAccessHandle(accessHandleIdentifier));
}
void NetworkStorageManager::getHandleNames(WebCore::FileSystemHandleIdentifier identifier, CompletionHandler<void(Expected<Vector<String>, FileSystemStorageError>)>&& completionHandler)
{
ASSERT(!RunLoop::isMain());
auto handle = m_fileSystemStorageHandleRegistry->getHandle(identifier);
if (!handle)
return completionHandler(makeUnexpected(FileSystemStorageError::Unknown));
completionHandler(handle->getHandleNames());
}
void NetworkStorageManager::getHandle(IPC::Connection& connection, WebCore::FileSystemHandleIdentifier identifier, String&& name, CompletionHandler<void(Expected<std::pair<WebCore::FileSystemHandleIdentifier, bool>, FileSystemStorageError>)>&& completionHandler)
{
ASSERT(!RunLoop::isMain());
auto handle = m_fileSystemStorageHandleRegistry->getHandle(identifier);
if (!handle)
return completionHandler(makeUnexpected(FileSystemStorageError::Unknown));
completionHandler(handle->getHandle(connection.uniqueID(), WTFMove(name)));
}
void NetworkStorageManager::forEachOriginDirectory(const Function<void(const String&)>& apply)
{
for (auto& topOrigin : FileSystem::listDirectory(m_path)) {
auto topOriginDirectory = FileSystem::pathByAppendingComponent(m_path, topOrigin);
auto openingOrigins = FileSystem::listDirectory(topOriginDirectory);
if (openingOrigins.isEmpty()) {
FileSystem::deleteEmptyDirectory(topOriginDirectory);
continue;
}
for (auto& openingOrigin : openingOrigins) {
if (openingOrigin.startsWith('.'))
continue;
auto openingOriginDirectory = FileSystem::pathByAppendingComponent(topOriginDirectory, openingOrigin);
apply(openingOriginDirectory);
}
}
}
HashSet<WebCore::ClientOrigin> NetworkStorageManager::getAllOrigins()
{
HashSet<WebCore::ClientOrigin> allOrigins;
for (auto& origin : m_localOriginStorageManagers.keys())
allOrigins.add(origin);
forEachOriginDirectory([&](auto directory) {
if (auto origin = readOriginFromFile(originFilePath(directory)))
allOrigins.add(*origin);
});
for (auto& origin : LocalStorageManager::originsOfLocalStorageData(m_customLocalStoragePath))
allOrigins.add(WebCore::ClientOrigin { origin, origin });
return allOrigins;
}
Vector<WebsiteData::Entry> NetworkStorageManager::fetchDataFromDisk(OptionSet<WebsiteDataType> targetTypes)
{
ASSERT(!RunLoop::isMain());
HashMap<WebCore::SecurityOriginData, OptionSet<WebsiteDataType>> originTypes;
for (auto& origin : getAllOrigins()) {
auto types = localOriginStorageManager(origin).fetchDataTypesInList(targetTypes);
originTypes.add(origin.clientOrigin, OptionSet<WebsiteDataType> { }).iterator->value.add(types);
originTypes.add(origin.topOrigin, OptionSet<WebsiteDataType> { }).iterator->value.add(types);
removeOriginStorageManagerIfPossible(origin);
}
Vector<WebsiteData::Entry> entries;
for (auto [origin, types] : originTypes) {
for (auto type : types)
entries.append({ WebsiteData::Entry { origin, type, 0 } });
}
return entries;
}
void NetworkStorageManager::fetchData(OptionSet<WebsiteDataType> types, CompletionHandler<void(Vector<WebsiteData::Entry>&&)>&& completionHandler)
{
ASSERT(RunLoop::isMain());
ASSERT(!m_closed);
m_queue->dispatch([this, protectedThis = Ref { *this }, types, completionHandler = WTFMove(completionHandler)]() mutable {
auto entries = fetchDataFromDisk(types);
RunLoop::main().dispatch([protectedThis = WTFMove(protectedThis), completionHandler = WTFMove(completionHandler), entries = crossThreadCopy(WTFMove(entries))]() mutable {
completionHandler(WTFMove(entries));
});
});
}
HashSet<WebCore::ClientOrigin> NetworkStorageManager::deleteDataOnDisk(OptionSet<WebsiteDataType> types, WallTime modifiedSinceTime, const Function<bool(const WebCore::ClientOrigin&)>& filter)
{
ASSERT(!RunLoop::isMain());
HashSet<WebCore::ClientOrigin> deletedOrigins;
for (auto& origin : getAllOrigins()) {
if (!filter(origin))
continue;
auto existingDataTypes = localOriginStorageManager(origin).fetchDataTypesInList(types);
if (!existingDataTypes.isEmpty()) {
deletedOrigins.add(origin);
localOriginStorageManager(origin).deleteData(types, modifiedSinceTime);
}
deleteOriginDirectoryIfPossible(origin);
}
return deletedOrigins;
}
void NetworkStorageManager::deleteData(OptionSet<WebsiteDataType> types, const Vector<WebCore::SecurityOriginData>& origins, CompletionHandler<void()>&& completionHandler)
{
ASSERT(RunLoop::isMain());
ASSERT(!m_closed);
m_queue->dispatch([this, protectedThis = Ref { *this }, types, origins = crossThreadCopy(origins), completionHandler = WTFMove(completionHandler)]() mutable {
HashSet<WebCore::SecurityOriginData> originSet;
originSet.reserveInitialCapacity(origins.size());
for (auto origin : origins)
originSet.add(WTFMove(origin));
deleteDataOnDisk(types, -WallTime::infinity(), [&originSet](auto origin) {
return originSet.contains(origin.topOrigin) || originSet.contains(origin.clientOrigin);
});
RunLoop::main().dispatch([protectedThis = WTFMove(protectedThis), completionHandler = WTFMove(completionHandler)]() mutable {
completionHandler();
});
});
}
void NetworkStorageManager::deleteDataModifiedSince(OptionSet<WebsiteDataType> types, WallTime modifiedSinceTime, CompletionHandler<void()>&& completionHandler)
{
ASSERT(RunLoop::isMain());
ASSERT(!m_closed);
m_queue->dispatch([this, protectedThis = Ref { *this }, types, modifiedSinceTime, completionHandler = WTFMove(completionHandler)]() mutable {
deleteDataOnDisk(types, modifiedSinceTime, [](auto&) {
return true;
});
RunLoop::main().dispatch([protectedThis = WTFMove(protectedThis), completionHandler = WTFMove(completionHandler)]() mutable {
completionHandler();
});
});
}
void NetworkStorageManager::deleteDataForRegistrableDomains(OptionSet<WebsiteDataType> types, const Vector<WebCore::RegistrableDomain>& domains, CompletionHandler<void(HashSet<WebCore::RegistrableDomain>&&)>&& completionHandler)
{
ASSERT(RunLoop::isMain());
ASSERT(!m_closed);
m_queue->dispatch([this, protectedThis = Ref { *this }, types, domains = crossThreadCopy(domains), completionHandler = WTFMove(completionHandler)]() mutable {
auto deletedOrigins = deleteDataOnDisk(types, -WallTime::infinity(), [&domains](auto& origin) {
auto domain = WebCore::RegistrableDomain::uncheckedCreateFromHost(origin.clientOrigin.host);
return domains.contains(domain);
});
HashSet<WebCore::RegistrableDomain> deletedDomains;
for (auto origin : deletedOrigins) {
auto domain = WebCore::RegistrableDomain::uncheckedCreateFromHost(origin.clientOrigin.host);
deletedDomains.add(domain);
}
RunLoop::main().dispatch([protectedThis = WTFMove(protectedThis), completionHandler = WTFMove(completionHandler), domains = crossThreadCopy(WTFMove(deletedDomains))]() mutable {
completionHandler(WTFMove(domains));
});
});
}
void NetworkStorageManager::moveData(const WebCore::SecurityOriginData& source, const WebCore::SecurityOriginData& target, CompletionHandler<void()>&& completionHandler)
{
ASSERT(RunLoop::isMain());
ASSERT(!m_closed);
m_queue->dispatch([this, protectedThis = Ref { *this }, source = crossThreadCopy(source), target = crossThreadCopy(target), completionHandler = WTFMove(completionHandler)]() mutable {
auto sourceOrigin = WebCore::ClientOrigin { source, source };
auto targetOrigin = WebCore::ClientOrigin { target, target };
// Clear existing data of target origin.
OptionSet<WebsiteDataType> types = { WebsiteDataType::FileSystem, WebsiteDataType::LocalStorage, WebsiteDataType::SessionStorage };
localOriginStorageManager(targetOrigin).deleteData(types, -WallTime::infinity());
removeOriginStorageManagerIfPossible(targetOrigin);
// Move data from source origin to target origin.
localOriginStorageManager(sourceOrigin).moveData(originDirectoryPath(m_path, targetOrigin, m_salt), LocalStorageManager::localStorageFilePath(m_customLocalStoragePath, targetOrigin));
removeOriginStorageManagerIfPossible(sourceOrigin);
RunLoop::main().dispatch(WTFMove(completionHandler));
});
}
void NetworkStorageManager::suspend(CompletionHandler<void()>&& completionHandler)
{
ASSERT(RunLoop::isMain());
m_queue->suspend([this, protectedThis = Ref { *this }] {
for (auto& manager : m_localOriginStorageManagers.values()) {
if (auto localStorageManager = manager->existingLocalStorageManager())
localStorageManager->syncLocalStorage();
}
}, WTFMove(completionHandler));
}
void NetworkStorageManager::resume()
{
ASSERT(RunLoop::isMain());
m_queue->resume();
}
void NetworkStorageManager::handleLowMemoryWarning()
{
ASSERT(RunLoop::isMain());
m_queue->dispatch([this, protectedThis = Ref { *this }] {
for (auto& manager : m_localOriginStorageManagers.values()) {
if (auto localStorageManager = manager->existingLocalStorageManager())
localStorageManager->handleLowMemoryWarning();
}
});
}
void NetworkStorageManager::syncLocalStorage(CompletionHandler<void()>&& completionHandler)
{
ASSERT(RunLoop::isMain());
m_queue->dispatch([this, protectedThis = Ref { *this }, completionHandler = WTFMove(completionHandler)]() mutable {
for (auto& manager : m_localOriginStorageManagers.values()) {
if (auto localStorageManager = manager->existingLocalStorageManager())
localStorageManager->syncLocalStorage();
}
RunLoop::main().dispatch(WTFMove(completionHandler));
});
}
void NetworkStorageManager::connectToStorageArea(IPC::Connection& connection, WebCore::StorageType type, StorageAreaMapIdentifier sourceIdentifier, StorageNamespaceIdentifier namespaceIdentifier, const WebCore::ClientOrigin& origin, CompletionHandler<void(StorageAreaIdentifier, HashMap<String, String>, uint64_t)>&& completionHandler)
{
ASSERT(!RunLoop::isMain());
auto connectionIdentifier = connection.uniqueID();
auto& originStorageManager = localOriginStorageManager(origin);
StorageAreaIdentifier resultIdentifier;
switch (type) {
case WebCore::StorageType::Local:
resultIdentifier = originStorageManager.localStorageManager(*m_storageAreaRegistry).connectToLocalStorageArea(connectionIdentifier, sourceIdentifier, origin, m_queue.copyRef());
break;
case WebCore::StorageType::TransientLocal:
resultIdentifier = originStorageManager.localStorageManager(*m_storageAreaRegistry).connectToTransientLocalStorageArea(connectionIdentifier, sourceIdentifier, origin);
break;
case WebCore::StorageType::Session:
resultIdentifier = originStorageManager.sessionStorageManager(*m_storageAreaRegistry).connectToSessionStorageArea(connectionIdentifier, sourceIdentifier, origin, namespaceIdentifier);
}
if (auto storageArea = m_storageAreaRegistry->getStorageArea(resultIdentifier))
return completionHandler(resultIdentifier, storageArea->allItems(), StorageAreaBase::nextMessageIdentifier());
return completionHandler(resultIdentifier, HashMap<String, String> { }, StorageAreaBase::nextMessageIdentifier());
}
void NetworkStorageManager::connectToStorageAreaSync(IPC::Connection& connection, WebCore::StorageType type, StorageAreaMapIdentifier sourceIdentifier, StorageNamespaceIdentifier namespaceIdentifier, const WebCore::ClientOrigin& origin, CompletionHandler<void(StorageAreaIdentifier, HashMap<String, String>, uint64_t)>&& completionHandler)
{
connectToStorageArea(connection, type, sourceIdentifier, namespaceIdentifier, origin, WTFMove(completionHandler));
}
void NetworkStorageManager::disconnectFromStorageArea(IPC::Connection& connection, StorageAreaIdentifier identifier)
{
ASSERT(!RunLoop::isMain());
auto storageArea = m_storageAreaRegistry->getStorageArea(identifier);
if (!storageArea)
return;
if (storageArea->storageType() == StorageAreaBase::StorageType::Local)
localOriginStorageManager(storageArea->origin()).localStorageManager(*m_storageAreaRegistry).disconnectFromStorageArea(connection.uniqueID(), identifier);
else
localOriginStorageManager(storageArea->origin()).sessionStorageManager(*m_storageAreaRegistry).disconnectFromStorageArea(connection.uniqueID(), identifier);
}
void NetworkStorageManager::cloneSessionStorageNamespace(IPC::Connection& connection, StorageNamespaceIdentifier fromIdentifier, StorageNamespaceIdentifier toIdentifier)
{
ASSERT(!RunLoop::isMain());
for (auto& manager : m_localOriginStorageManagers.values()) {
if (auto* sessionStorageManager = manager->existingSessionStorageManager())
sessionStorageManager->cloneStorageArea(connection.uniqueID(), fromIdentifier, toIdentifier);
}
}
void NetworkStorageManager::setItem(IPC::Connection& connection, StorageAreaIdentifier identifier, StorageAreaImplIdentifier implIdentifier, String&& key, String&& value, String&& urlString, CompletionHandler<void(bool)>&& completionHandler)
{
ASSERT(!RunLoop::isMain());
bool hasQuotaError = false;
if (auto storageArea = m_storageAreaRegistry->getStorageArea(identifier)) {
auto result = storageArea->setItem(connection.uniqueID(), implIdentifier, String { key }, WTFMove(value), WTFMove(urlString));
if (!result)
hasQuotaError = (result.error() == StorageError::QuotaExceeded);
}
completionHandler(hasQuotaError);
}
void NetworkStorageManager::removeItem(IPC::Connection& connection, StorageAreaIdentifier identifier, StorageAreaImplIdentifier implIdentifier, String&& key, String&& urlString, CompletionHandler<void()>&& completionHandler)
{
ASSERT(!RunLoop::isMain());
if (auto storageArea = m_storageAreaRegistry->getStorageArea(identifier))
storageArea->removeItem(connection.uniqueID(), implIdentifier, WTFMove(key), WTFMove(urlString));
completionHandler();
}
void NetworkStorageManager::clear(IPC::Connection& connection, StorageAreaIdentifier identifier, StorageAreaImplIdentifier implIdentifier, String&& urlString, CompletionHandler<void()>&& completionHandler)
{
ASSERT(!RunLoop::isMain());
if (auto storageArea = m_storageAreaRegistry->getStorageArea(identifier))
storageArea->clear(connection.uniqueID(), implIdentifier, WTFMove(urlString));
completionHandler();
}
} // namespace WebKit