StorageManager.cpp [plain text]
#include "config.h"
#include "StorageManager.h"
#include "LocalStorageDatabase.h"
#include "LocalStorageDatabaseTracker.h"
#include "StorageAreaMapMessages.h"
#include "StorageManagerMessages.h"
#include "WebProcessProxy.h"
#include <WebCore/SecurityOriginData.h>
#include <WebCore/SecurityOriginHash.h>
#include <WebCore/StorageMap.h>
#include <WebCore/TextEncoding.h>
#include <memory>
#include <wtf/WorkQueue.h>
#include <wtf/threads/BinarySemaphore.h>
namespace WebKit {
using namespace WebCore;
class StorageManager::StorageArea : public ThreadSafeRefCounted<StorageManager::StorageArea> {
public:
static Ref<StorageArea> create(LocalStorageNamespace*, const SecurityOriginData&, unsigned quotaInBytes);
~StorageArea();
const WebCore::SecurityOriginData& securityOrigin() const { return m_securityOrigin; }
void addListener(IPC::Connection::UniqueID, uint64_t storageMapID);
void removeListener(IPC::Connection::UniqueID, uint64_t storageMapID);
bool hasListener(IPC::Connection::UniqueID, uint64_t storageMapID) const;
Ref<StorageArea> clone() const;
void setItem(IPC::Connection::UniqueID sourceConnection, uint64_t sourceStorageAreaID, const String& key, const String& value, const String& urlString, bool& quotaException);
void removeItem(IPC::Connection::UniqueID sourceConnection, uint64_t sourceStorageAreaID, const String& key, const String& urlString);
void clear(IPC::Connection::UniqueID sourceConnection, uint64_t sourceStorageAreaID, const String& urlString);
const HashMap<String, String>& items() const;
void clear();
bool isSessionStorage() const { return !m_localStorageNamespace; }
private:
explicit StorageArea(LocalStorageNamespace*, const SecurityOriginData&, unsigned quotaInBytes);
void openDatabaseAndImportItemsIfNeeded() const;
void dispatchEvents(IPC::Connection::UniqueID sourceConnection, uint64_t sourceStorageAreaID, const String& key, const String& oldValue, const String& newValue, const String& urlString) const;
LocalStorageNamespace* m_localStorageNamespace;
mutable RefPtr<LocalStorageDatabase> m_localStorageDatabase;
mutable bool m_didImportItemsFromDatabase { false };
SecurityOriginData m_securityOrigin;
unsigned m_quotaInBytes;
RefPtr<StorageMap> m_storageMap;
HashSet<std::pair<IPC::Connection::UniqueID, uint64_t>> m_eventListeners;
};
class StorageManager::LocalStorageNamespace : public ThreadSafeRefCounted<LocalStorageNamespace> {
public:
static Ref<LocalStorageNamespace> create(StorageManager*, uint64_t storageManagerID);
~LocalStorageNamespace();
StorageManager* storageManager() const { return m_storageManager; }
Ref<StorageArea> getOrCreateStorageArea(SecurityOriginData&&);
void didDestroyStorageArea(StorageArea*);
void clearStorageAreasMatchingOrigin(const SecurityOriginData&);
void clearAllStorageAreas();
private:
explicit LocalStorageNamespace(StorageManager*, uint64_t storageManagerID);
StorageManager* m_storageManager;
uint64_t m_storageNamespaceID;
unsigned m_quotaInBytes;
HashMap<SecurityOriginData, StorageArea*> m_storageAreaMap;
};
const unsigned localStorageDatabaseQuotaInBytes = 5 * 1024 * 1024;
class StorageManager::TransientLocalStorageNamespace : public ThreadSafeRefCounted<TransientLocalStorageNamespace> {
public:
static Ref<TransientLocalStorageNamespace> create()
{
return adoptRef(*new TransientLocalStorageNamespace());
}
~TransientLocalStorageNamespace()
{
}
Ref<StorageArea> getOrCreateStorageArea(SecurityOriginData&& securityOrigin)
{
return *m_storageAreaMap.ensure(securityOrigin, [this, securityOrigin]() mutable {
return StorageArea::create(nullptr, WTFMove(securityOrigin), m_quotaInBytes);
}).iterator->value.copyRef();
}
Vector<SecurityOriginData> origins() const
{
Vector<SecurityOriginData> origins;
for (const auto& storageArea : m_storageAreaMap.values()) {
if (!storageArea->items().isEmpty())
origins.append(storageArea->securityOrigin());
}
return origins;
}
void clearStorageAreasMatchingOrigin(const SecurityOriginData& securityOrigin)
{
for (auto& storageArea : m_storageAreaMap.values()) {
if (storageArea->securityOrigin() == securityOrigin)
storageArea->clear();
}
}
void clearAllStorageAreas()
{
for (auto& storageArea : m_storageAreaMap.values())
storageArea->clear();
}
private:
explicit TransientLocalStorageNamespace()
{
}
const unsigned m_quotaInBytes = localStorageDatabaseQuotaInBytes;
HashMap<SecurityOriginData, RefPtr<StorageArea>> m_storageAreaMap;
};
auto StorageManager::StorageArea::create(LocalStorageNamespace* localStorageNamespace, const SecurityOriginData& securityOrigin, unsigned quotaInBytes) -> Ref<StorageManager::StorageArea>
{
return adoptRef(*new StorageArea(localStorageNamespace, securityOrigin, quotaInBytes));
}
StorageManager::StorageArea::StorageArea(LocalStorageNamespace* localStorageNamespace, const SecurityOriginData& securityOrigin, unsigned quotaInBytes)
: m_localStorageNamespace(localStorageNamespace)
, m_securityOrigin(securityOrigin)
, m_quotaInBytes(quotaInBytes)
, m_storageMap(StorageMap::create(m_quotaInBytes))
{
}
StorageManager::StorageArea::~StorageArea()
{
ASSERT(m_eventListeners.isEmpty());
if (m_localStorageDatabase)
m_localStorageDatabase->close();
if (m_localStorageNamespace)
m_localStorageNamespace->didDestroyStorageArea(this);
}
void StorageManager::StorageArea::addListener(IPC::Connection::UniqueID connectionID, uint64_t storageMapID)
{
ASSERT(!m_eventListeners.contains(std::make_pair(connectionID, storageMapID)));
m_eventListeners.add(std::make_pair(connectionID, storageMapID));
}
void StorageManager::StorageArea::removeListener(IPC::Connection::UniqueID connectionID, uint64_t storageMapID)
{
ASSERT(isSessionStorage() || m_eventListeners.contains(std::make_pair(connectionID, storageMapID)));
m_eventListeners.remove(std::make_pair(connectionID, storageMapID));
}
bool StorageManager::StorageArea::hasListener(IPC::Connection::UniqueID connectionID, uint64_t storageMapID) const
{
return m_eventListeners.contains(std::make_pair(connectionID, storageMapID));
}
Ref<StorageManager::StorageArea> StorageManager::StorageArea::clone() const
{
ASSERT(!m_localStorageNamespace);
auto storageArea = StorageArea::create(nullptr, m_securityOrigin, m_quotaInBytes);
storageArea->m_storageMap = m_storageMap;
return storageArea;
}
void StorageManager::StorageArea::setItem(IPC::Connection::UniqueID sourceConnection, uint64_t sourceStorageAreaID, const String& key, const String& value, const String& urlString, bool& quotaException)
{
openDatabaseAndImportItemsIfNeeded();
String oldValue;
auto newStorageMap = m_storageMap->setItem(key, value, oldValue, quotaException);
if (newStorageMap)
m_storageMap = WTFMove(newStorageMap);
if (quotaException)
return;
if (m_localStorageDatabase)
m_localStorageDatabase->setItem(key, value);
dispatchEvents(sourceConnection, sourceStorageAreaID, key, oldValue, value, urlString);
}
void StorageManager::StorageArea::removeItem(IPC::Connection::UniqueID sourceConnection, uint64_t sourceStorageAreaID, const String& key, const String& urlString)
{
openDatabaseAndImportItemsIfNeeded();
String oldValue;
auto newStorageMap = m_storageMap->removeItem(key, oldValue);
if (newStorageMap)
m_storageMap = WTFMove(newStorageMap);
if (oldValue.isNull())
return;
if (m_localStorageDatabase)
m_localStorageDatabase->removeItem(key);
dispatchEvents(sourceConnection, sourceStorageAreaID, key, oldValue, String(), urlString);
}
void StorageManager::StorageArea::clear(IPC::Connection::UniqueID sourceConnection, uint64_t sourceStorageAreaID, const String& urlString)
{
openDatabaseAndImportItemsIfNeeded();
if (!m_storageMap->length())
return;
m_storageMap = StorageMap::create(m_quotaInBytes);
if (m_localStorageDatabase)
m_localStorageDatabase->clear();
dispatchEvents(sourceConnection, sourceStorageAreaID, String(), String(), String(), urlString);
}
const HashMap<String, String>& StorageManager::StorageArea::items() const
{
openDatabaseAndImportItemsIfNeeded();
return m_storageMap->items();
}
void StorageManager::StorageArea::clear()
{
m_storageMap = StorageMap::create(m_quotaInBytes);
if (m_localStorageDatabase) {
m_localStorageDatabase->close();
m_localStorageDatabase = nullptr;
}
for (auto it = m_eventListeners.begin(), end = m_eventListeners.end(); it != end; ++it) {
RunLoop::main().dispatch([connectionID = it->first, destinationStorageAreaID = it->second] {
if (auto* connection = IPC::Connection::connection(connectionID))
connection->send(Messages::StorageAreaMap::ClearCache(), destinationStorageAreaID);
});
}
}
void StorageManager::StorageArea::openDatabaseAndImportItemsIfNeeded() const
{
if (!m_localStorageNamespace)
return;
if (!m_localStorageDatabase)
m_localStorageDatabase = LocalStorageDatabase::create(m_localStorageNamespace->storageManager()->m_queue.copyRef(), m_localStorageNamespace->storageManager()->m_localStorageDatabaseTracker.copyRef(), m_securityOrigin);
if (m_didImportItemsFromDatabase)
return;
m_localStorageDatabase->importItems(*m_storageMap);
m_didImportItemsFromDatabase = true;
}
void StorageManager::StorageArea::dispatchEvents(IPC::Connection::UniqueID sourceConnection, uint64_t sourceStorageAreaID, const String& key, const String& oldValue, const String& newValue, const String& urlString) const
{
for (auto it = m_eventListeners.begin(), end = m_eventListeners.end(); it != end; ++it) {
sourceStorageAreaID = it->first == sourceConnection ? sourceStorageAreaID : 0;
RunLoop::main().dispatch([connectionID = it->first, sourceStorageAreaID, destinationStorageAreaID = it->second, key = key.isolatedCopy(), oldValue = oldValue.isolatedCopy(), newValue = newValue.isolatedCopy(), urlString = urlString.isolatedCopy()] {
if (auto* connection = IPC::Connection::connection(connectionID))
connection->send(Messages::StorageAreaMap::DispatchStorageEvent(sourceStorageAreaID, key, oldValue, newValue, urlString), destinationStorageAreaID);
});
}
}
Ref<StorageManager::LocalStorageNamespace> StorageManager::LocalStorageNamespace::create(StorageManager* storageManager, uint64_t storageNamespaceID)
{
return adoptRef(*new LocalStorageNamespace(storageManager, storageNamespaceID));
}
StorageManager::LocalStorageNamespace::LocalStorageNamespace(StorageManager* storageManager, uint64_t storageNamespaceID)
: m_storageManager(storageManager)
, m_storageNamespaceID(storageNamespaceID)
, m_quotaInBytes(localStorageDatabaseQuotaInBytes)
{
}
StorageManager::LocalStorageNamespace::~LocalStorageNamespace()
{
ASSERT(m_storageAreaMap.isEmpty());
}
auto StorageManager::LocalStorageNamespace::getOrCreateStorageArea(SecurityOriginData&& securityOrigin) -> Ref<StorageArea>
{
auto& slot = m_storageAreaMap.add(securityOrigin, nullptr).iterator->value;
if (slot)
return *slot;
auto storageArea = StorageArea::create(this, WTFMove(securityOrigin), m_quotaInBytes);
slot = &storageArea.get();
return storageArea;
}
void StorageManager::LocalStorageNamespace::didDestroyStorageArea(StorageArea* storageArea)
{
ASSERT(m_storageAreaMap.contains(storageArea->securityOrigin()));
m_storageAreaMap.remove(storageArea->securityOrigin());
if (!m_storageAreaMap.isEmpty())
return;
ASSERT(m_storageManager->m_localStorageNamespaces.contains(m_storageNamespaceID));
m_storageManager->m_localStorageNamespaces.remove(m_storageNamespaceID);
}
void StorageManager::LocalStorageNamespace::clearStorageAreasMatchingOrigin(const SecurityOriginData& securityOrigin)
{
for (const auto& originAndStorageArea : m_storageAreaMap) {
if (originAndStorageArea.key == securityOrigin)
originAndStorageArea.value->clear();
}
}
void StorageManager::LocalStorageNamespace::clearAllStorageAreas()
{
for (auto* storageArea : m_storageAreaMap.values())
storageArea->clear();
}
class StorageManager::SessionStorageNamespace : public ThreadSafeRefCounted<SessionStorageNamespace> {
public:
static Ref<SessionStorageNamespace> create(unsigned quotaInBytes);
~SessionStorageNamespace();
bool isEmpty() const { return m_storageAreaMap.isEmpty(); }
IPC::Connection::UniqueID allowedConnection() const { return m_allowedConnection; }
void setAllowedConnection(IPC::Connection::UniqueID);
Ref<StorageArea> getOrCreateStorageArea(SecurityOriginData&&);
void cloneTo(SessionStorageNamespace& newSessionStorageNamespace);
Vector<SecurityOriginData> origins() const
{
Vector<SecurityOriginData> origins;
for (const auto& storageArea : m_storageAreaMap.values()) {
if (!storageArea->items().isEmpty())
origins.append(storageArea->securityOrigin());
}
return origins;
}
void clearStorageAreasMatchingOrigin(const SecurityOriginData& securityOrigin)
{
for (auto& storageArea : m_storageAreaMap.values()) {
if (storageArea->securityOrigin() == securityOrigin)
storageArea->clear();
}
}
void clearAllStorageAreas()
{
for (auto& storageArea : m_storageAreaMap.values())
storageArea->clear();
}
private:
explicit SessionStorageNamespace(unsigned quotaInBytes);
IPC::Connection::UniqueID m_allowedConnection;
unsigned m_quotaInBytes;
HashMap<SecurityOriginData, RefPtr<StorageArea>> m_storageAreaMap;
};
Ref<StorageManager::SessionStorageNamespace> StorageManager::SessionStorageNamespace::create(unsigned quotaInBytes)
{
return adoptRef(*new SessionStorageNamespace(quotaInBytes));
}
StorageManager::SessionStorageNamespace::SessionStorageNamespace(unsigned quotaInBytes)
: m_quotaInBytes(quotaInBytes)
{
}
StorageManager::SessionStorageNamespace::~SessionStorageNamespace()
{
}
void StorageManager::SessionStorageNamespace::setAllowedConnection(IPC::Connection::UniqueID allowedConnection)
{
m_allowedConnection = allowedConnection;
}
auto StorageManager::SessionStorageNamespace::getOrCreateStorageArea(SecurityOriginData&& securityOrigin) -> Ref<StorageArea>
{
return *m_storageAreaMap.ensure(securityOrigin, [this, securityOrigin]() mutable {
return StorageArea::create(nullptr, WTFMove(securityOrigin), m_quotaInBytes);
}).iterator->value.copyRef();
}
void StorageManager::SessionStorageNamespace::cloneTo(SessionStorageNamespace& newSessionStorageNamespace)
{
ASSERT_UNUSED(newSessionStorageNamespace, newSessionStorageNamespace.isEmpty());
for (auto& pair : m_storageAreaMap)
newSessionStorageNamespace.m_storageAreaMap.add(pair.key, pair.value->clone());
}
Ref<StorageManager> StorageManager::create(const String& localStorageDirectory)
{
return adoptRef(*new StorageManager(localStorageDirectory));
}
StorageManager::StorageManager(const String& localStorageDirectory)
: m_queue(WorkQueue::create("com.apple.WebKit.StorageManager"))
, m_localStorageDatabaseTracker(LocalStorageDatabaseTracker::create(m_queue.copyRef(), localStorageDirectory))
, m_isEphemeral(localStorageDirectory.isNull())
{
UTF8Encoding();
}
StorageManager::~StorageManager()
{
}
void StorageManager::createSessionStorageNamespace(uint64_t storageNamespaceID, unsigned quotaInBytes)
{
m_queue->dispatch([this, protectedThis = makeRef(*this), storageNamespaceID, quotaInBytes]() mutable {
ASSERT(!m_sessionStorageNamespaces.contains(storageNamespaceID));
m_sessionStorageNamespaces.set(storageNamespaceID, SessionStorageNamespace::create(quotaInBytes));
});
}
void StorageManager::destroySessionStorageNamespace(uint64_t storageNamespaceID)
{
m_queue->dispatch([this, protectedThis = makeRef(*this), storageNamespaceID] {
ASSERT(m_sessionStorageNamespaces.contains(storageNamespaceID));
m_sessionStorageNamespaces.remove(storageNamespaceID);
});
}
void StorageManager::setAllowedSessionStorageNamespaceConnection(uint64_t storageNamespaceID, IPC::Connection* allowedConnection)
{
auto allowedConnectionID = allowedConnection ? allowedConnection->uniqueID() : IPC::Connection::UniqueID { };
m_queue->dispatch([this, protectedThis = makeRef(*this), allowedConnectionID, storageNamespaceID]() mutable {
ASSERT(m_sessionStorageNamespaces.contains(storageNamespaceID));
m_sessionStorageNamespaces.get(storageNamespaceID)->setAllowedConnection(allowedConnectionID);
});
}
void StorageManager::cloneSessionStorageNamespace(uint64_t storageNamespaceID, uint64_t newStorageNamespaceID)
{
m_queue->dispatch([this, protectedThis = makeRef(*this), storageNamespaceID, newStorageNamespaceID] {
SessionStorageNamespace* sessionStorageNamespace = m_sessionStorageNamespaces.get(storageNamespaceID);
if (!sessionStorageNamespace) {
return;
}
SessionStorageNamespace* newSessionStorageNamespace = m_sessionStorageNamespaces.get(newStorageNamespaceID);
ASSERT(newSessionStorageNamespace);
sessionStorageNamespace->cloneTo(*newSessionStorageNamespace);
});
}
void StorageManager::processWillOpenConnection(WebProcessProxy&, IPC::Connection& connection)
{
connection.addWorkQueueMessageReceiver(Messages::StorageManager::messageReceiverName(), m_queue.get(), this);
}
void StorageManager::processDidCloseConnection(WebProcessProxy&, IPC::Connection& connection)
{
connection.removeWorkQueueMessageReceiver(Messages::StorageManager::messageReceiverName());
m_queue->dispatch([this, protectedThis = makeRef(*this), connectionID = connection.uniqueID()]() mutable {
Vector<std::pair<IPC::Connection::UniqueID, uint64_t>> connectionAndStorageMapIDPairsToRemove;
for (auto& storageArea : m_storageAreasByConnection) {
if (storageArea.key.first != connectionID)
continue;
storageArea.value->removeListener(storageArea.key.first, storageArea.key.second);
connectionAndStorageMapIDPairsToRemove.append(storageArea.key);
}
for (auto& pair : connectionAndStorageMapIDPairsToRemove)
m_storageAreasByConnection.remove(pair);
});
}
void StorageManager::getSessionStorageOrigins(Function<void(HashSet<WebCore::SecurityOriginData>&&)>&& completionHandler)
{
m_queue->dispatch([this, protectedThis = makeRef(*this), completionHandler = WTFMove(completionHandler)]() mutable {
HashSet<SecurityOriginData> origins;
for (const auto& sessionStorageNamespace : m_sessionStorageNamespaces.values()) {
for (auto& origin : sessionStorageNamespace->origins())
origins.add(origin);
}
RunLoop::main().dispatch([origins = WTFMove(origins), completionHandler = WTFMove(completionHandler)]() mutable {
completionHandler(WTFMove(origins));
});
});
}
void StorageManager::deleteSessionStorageOrigins(Function<void()>&& completionHandler)
{
m_queue->dispatch([this, protectedThis = makeRef(*this), completionHandler = WTFMove(completionHandler)]() mutable {
for (auto& sessionStorageNamespace : m_sessionStorageNamespaces.values())
sessionStorageNamespace->clearAllStorageAreas();
RunLoop::main().dispatch(WTFMove(completionHandler));
});
}
void StorageManager::deleteSessionStorageEntriesForOrigins(const Vector<WebCore::SecurityOriginData>& origins, Function<void()>&& completionHandler)
{
Vector<WebCore::SecurityOriginData> copiedOrigins;
copiedOrigins.reserveInitialCapacity(origins.size());
for (auto& origin : origins)
copiedOrigins.uncheckedAppend(origin.isolatedCopy());
m_queue->dispatch([this, protectedThis = makeRef(*this), copiedOrigins = WTFMove(copiedOrigins), completionHandler = WTFMove(completionHandler)]() mutable {
for (auto& origin : copiedOrigins) {
for (auto& sessionStorageNamespace : m_sessionStorageNamespaces.values())
sessionStorageNamespace->clearStorageAreasMatchingOrigin(origin);
}
RunLoop::main().dispatch(WTFMove(completionHandler));
});
}
void StorageManager::getLocalStorageOrigins(Function<void(HashSet<WebCore::SecurityOriginData>&&)>&& completionHandler)
{
m_queue->dispatch([this, protectedThis = makeRef(*this), completionHandler = WTFMove(completionHandler)]() mutable {
HashSet<SecurityOriginData> origins;
for (auto& origin : m_localStorageDatabaseTracker->origins())
origins.add(origin);
for (auto& transientLocalStorageNamespace : m_transientLocalStorageNamespaces.values()) {
for (auto& origin : transientLocalStorageNamespace->origins())
origins.add(origin);
}
RunLoop::main().dispatch([origins = WTFMove(origins), completionHandler = WTFMove(completionHandler)]() mutable {
completionHandler(WTFMove(origins));
});
});
}
void StorageManager::getLocalStorageOriginDetails(Function<void (Vector<LocalStorageDatabaseTracker::OriginDetails>)>&& completionHandler)
{
m_queue->dispatch([this, protectedThis = makeRef(*this), completionHandler = WTFMove(completionHandler)]() mutable {
auto originDetails = m_localStorageDatabaseTracker->originDetails();
RunLoop::main().dispatch([originDetails = WTFMove(originDetails), completionHandler = WTFMove(completionHandler)]() mutable {
completionHandler(WTFMove(originDetails));
});
});
}
void StorageManager::deleteLocalStorageEntriesForOrigin(const SecurityOriginData& securityOrigin)
{
m_queue->dispatch([this, protectedThis = makeRef(*this), copiedOrigin = securityOrigin.isolatedCopy()]() mutable {
for (auto& localStorageNamespace : m_localStorageNamespaces.values())
localStorageNamespace->clearStorageAreasMatchingOrigin(copiedOrigin);
for (auto& transientLocalStorageNamespace : m_transientLocalStorageNamespaces.values())
transientLocalStorageNamespace->clearStorageAreasMatchingOrigin(copiedOrigin);
m_localStorageDatabaseTracker->deleteDatabaseWithOrigin(copiedOrigin);
});
}
void StorageManager::deleteLocalStorageOriginsModifiedSince(WallTime time, Function<void()>&& completionHandler)
{
m_queue->dispatch([this, protectedThis = makeRef(*this), time, completionHandler = WTFMove(completionHandler)]() mutable {
auto originsToDelete = m_localStorageDatabaseTracker->databasesModifiedSince(time);
for (auto& transientLocalStorageNamespace : m_transientLocalStorageNamespaces.values())
transientLocalStorageNamespace->clearAllStorageAreas();
for (const auto& origin : originsToDelete) {
for (auto& localStorageNamespace : m_localStorageNamespaces.values())
localStorageNamespace->clearStorageAreasMatchingOrigin(origin);
m_localStorageDatabaseTracker->deleteDatabaseWithOrigin(origin);
}
RunLoop::main().dispatch(WTFMove(completionHandler));
});
}
void StorageManager::deleteLocalStorageEntriesForOrigins(const Vector<WebCore::SecurityOriginData>& origins, Function<void()>&& completionHandler)
{
Vector<SecurityOriginData> copiedOrigins;
copiedOrigins.reserveInitialCapacity(origins.size());
for (auto& origin : origins)
copiedOrigins.uncheckedAppend(origin.isolatedCopy());
m_queue->dispatch([this, protectedThis = makeRef(*this), copiedOrigins = WTFMove(copiedOrigins), completionHandler = WTFMove(completionHandler)]() mutable {
for (auto& origin : copiedOrigins) {
for (auto& localStorageNamespace : m_localStorageNamespaces.values())
localStorageNamespace->clearStorageAreasMatchingOrigin(origin);
for (auto& transientLocalStorageNamespace : m_transientLocalStorageNamespaces.values())
transientLocalStorageNamespace->clearStorageAreasMatchingOrigin(origin);
m_localStorageDatabaseTracker->deleteDatabaseWithOrigin(origin);
}
RunLoop::main().dispatch(WTFMove(completionHandler));
});
}
void StorageManager::createLocalStorageMap(IPC::Connection& connection, uint64_t storageMapID, uint64_t storageNamespaceID, SecurityOriginData&& securityOriginData)
{
ASSERT(!m_isEphemeral);
std::pair<IPC::Connection::UniqueID, uint64_t> connectionAndStorageMapIDPair(connection.uniqueID(), storageMapID);
ASSERT((HashMap<std::pair<IPC::Connection::UniqueID, uint64_t>, RefPtr<StorageArea>>::isValidKey(connectionAndStorageMapIDPair)));
auto result = m_storageAreasByConnection.add(connectionAndStorageMapIDPair, nullptr);
ASSERT(result.isNewEntry);
ASSERT((HashMap<uint64_t, RefPtr<LocalStorageNamespace>>::isValidKey(storageNamespaceID)));
LocalStorageNamespace* localStorageNamespace = getOrCreateLocalStorageNamespace(storageNamespaceID);
ASSERT(localStorageNamespace);
auto storageArea = localStorageNamespace->getOrCreateStorageArea(WTFMove(securityOriginData));
storageArea->addListener(connection.uniqueID(), storageMapID);
result.iterator->value = WTFMove(storageArea);
}
void StorageManager::createTransientLocalStorageMap(IPC::Connection& connection, uint64_t storageMapID, uint64_t storageNamespaceID, SecurityOriginData&& topLevelOriginData, SecurityOriginData&& origin)
{
ASSERT(m_storageAreasByConnection.isValidKey({ connection.uniqueID(), storageMapID }));
for (auto it = m_storageAreasByConnection.begin(), end = m_storageAreasByConnection.end(); it != end; ++it) {
if (it->key.first != connection.uniqueID())
continue;
Ref<StorageArea> area = *it->value;
if (!area->isSessionStorage())
continue;
if (!origin.securityOrigin()->isSameSchemeHostPort(area->securityOrigin().securityOrigin().get()))
continue;
area->addListener(connection.uniqueID(), storageMapID);
if (!area->hasListener(connection.uniqueID(), it->key.second))
m_storageAreasByConnection.remove(it);
m_storageAreasByConnection.add({ connection.uniqueID(), storageMapID }, WTFMove(area));
return;
}
auto& slot = m_storageAreasByConnection.add({ connection.uniqueID(), storageMapID }, nullptr).iterator->value;
ASSERT(!slot);
TransientLocalStorageNamespace* transientLocalStorageNamespace = getOrCreateTransientLocalStorageNamespace(storageNamespaceID, WTFMove(topLevelOriginData));
auto storageArea = transientLocalStorageNamespace->getOrCreateStorageArea(WTFMove(origin));
storageArea->addListener(connection.uniqueID(), storageMapID);
slot = WTFMove(storageArea);
}
void StorageManager::createSessionStorageMap(IPC::Connection& connection, uint64_t storageMapID, uint64_t storageNamespaceID, SecurityOriginData&& securityOriginData)
{
if (m_isEphemeral) {
m_ephemeralStorage.add(securityOriginData, WebCore::StorageMap::create(localStorageDatabaseQuotaInBytes));
return;
}
ASSERT(m_sessionStorageNamespaces.isValidKey(storageNamespaceID));
SessionStorageNamespace* sessionStorageNamespace = m_sessionStorageNamespaces.get(storageNamespaceID);
if (!sessionStorageNamespace) {
return;
}
ASSERT(m_storageAreasByConnection.isValidKey({ connection.uniqueID(), storageMapID }));
auto& slot = m_storageAreasByConnection.add({ connection.uniqueID(), storageMapID }, nullptr).iterator->value;
ASSERT(!slot);
ASSERT(connection.uniqueID() == sessionStorageNamespace->allowedConnection());
auto storageArea = sessionStorageNamespace->getOrCreateStorageArea(WTFMove(securityOriginData));
storageArea->addListener(connection.uniqueID(), storageMapID);
slot = WTFMove(storageArea);
}
void StorageManager::destroyStorageMap(IPC::Connection& connection, uint64_t storageMapID)
{
std::pair<IPC::Connection::UniqueID, uint64_t> connectionAndStorageMapIDPair(connection.uniqueID(), storageMapID);
ASSERT(m_storageAreasByConnection.isValidKey(connectionAndStorageMapIDPair));
auto it = m_storageAreasByConnection.find(connectionAndStorageMapIDPair);
if (it == m_storageAreasByConnection.end()) {
return;
}
it->value->removeListener(connection.uniqueID(), storageMapID);
if (it->value->isSessionStorage())
return;
m_storageAreasByConnection.remove(connectionAndStorageMapIDPair);
}
void StorageManager::getValues(IPC::Connection& connection, WebCore::SecurityOriginData&& securityOriginData, uint64_t storageMapID, uint64_t storageMapSeed, HashMap<String, String>& values)
{
StorageArea* storageArea = findStorageArea(connection, storageMapID);
if (!storageArea) {
if (m_isEphemeral) {
if (auto storageMap = m_ephemeralStorage.get(securityOriginData)) {
values = storageMap->items();
return;
}
}
return;
}
values = storageArea->items();
connection.send(Messages::StorageAreaMap::DidGetValues(storageMapSeed), storageMapID);
}
void StorageManager::setItem(IPC::Connection& connection, WebCore::SecurityOriginData&& securityOriginData, uint64_t storageMapID, uint64_t sourceStorageAreaID, uint64_t storageMapSeed, const String& key, const String& value, const String& urlString)
{
StorageArea* storageArea = findStorageArea(connection, storageMapID);
if (!storageArea) {
if (m_isEphemeral) {
if (auto storageMap = m_ephemeralStorage.get(securityOriginData)) {
String oldValue;
bool quotaException;
storageMap->setItem(key, value, oldValue, quotaException);
}
}
return;
}
bool quotaError;
storageArea->setItem(connection.uniqueID(), sourceStorageAreaID, key, value, urlString, quotaError);
connection.send(Messages::StorageAreaMap::DidSetItem(storageMapSeed, key, quotaError), storageMapID);
}
void StorageManager::removeItem(IPC::Connection& connection, WebCore::SecurityOriginData&& securityOriginData, uint64_t storageMapID, uint64_t sourceStorageAreaID, uint64_t storageMapSeed, const String& key, const String& urlString)
{
StorageArea* storageArea = findStorageArea(connection, storageMapID);
if (!storageArea) {
if (m_isEphemeral) {
if (auto storageMap = m_ephemeralStorage.get(securityOriginData)) {
String oldValue;
storageMap->removeItem(key, oldValue);
}
}
return;
}
storageArea->removeItem(connection.uniqueID(), sourceStorageAreaID, key, urlString);
connection.send(Messages::StorageAreaMap::DidRemoveItem(storageMapSeed, key), storageMapID);
}
void StorageManager::clear(IPC::Connection& connection, WebCore::SecurityOriginData&& securityOriginData, uint64_t storageMapID, uint64_t sourceStorageAreaID, uint64_t storageMapSeed, const String& urlString)
{
StorageArea* storageArea = findStorageArea(connection, storageMapID);
if (!storageArea) {
if (m_isEphemeral)
m_ephemeralStorage.remove(securityOriginData);
return;
}
storageArea->clear(connection.uniqueID(), sourceStorageAreaID, urlString);
connection.send(Messages::StorageAreaMap::DidClear(storageMapSeed), storageMapID);
}
void StorageManager::applicationWillTerminate()
{
BinarySemaphore semaphore;
m_queue->dispatch([this, &semaphore] {
Vector<std::pair<IPC::Connection::UniqueID, uint64_t>> connectionAndStorageMapIDPairsToRemove;
for (auto& connectionStorageAreaPair : m_storageAreasByConnection) {
connectionStorageAreaPair.value->removeListener(connectionStorageAreaPair.key.first, connectionStorageAreaPair.key.second);
connectionAndStorageMapIDPairsToRemove.append(connectionStorageAreaPair.key);
}
for (auto& connectionStorageAreaPair : connectionAndStorageMapIDPairsToRemove)
m_storageAreasByConnection.remove(connectionStorageAreaPair);
semaphore.signal();
});
semaphore.wait();
}
StorageManager::StorageArea* StorageManager::findStorageArea(IPC::Connection& connection, uint64_t storageMapID) const
{
std::pair<IPC::Connection::UniqueID, uint64_t> connectionAndStorageMapIDPair(connection.uniqueID(), storageMapID);
if (!m_storageAreasByConnection.isValidKey(connectionAndStorageMapIDPair))
return nullptr;
return m_storageAreasByConnection.get(connectionAndStorageMapIDPair);
}
StorageManager::LocalStorageNamespace* StorageManager::getOrCreateLocalStorageNamespace(uint64_t storageNamespaceID)
{
if (!m_localStorageNamespaces.isValidKey(storageNamespaceID))
return nullptr;
auto& slot = m_localStorageNamespaces.add(storageNamespaceID, nullptr).iterator->value;
if (!slot)
slot = LocalStorageNamespace::create(this, storageNamespaceID);
return slot.get();
}
StorageManager::TransientLocalStorageNamespace* StorageManager::getOrCreateTransientLocalStorageNamespace(uint64_t storageNamespaceID, WebCore::SecurityOriginData&& topLevelOrigin)
{
if (!m_transientLocalStorageNamespaces.isValidKey({ storageNamespaceID, topLevelOrigin }))
return nullptr;
auto& slot = m_transientLocalStorageNamespaces.add({ storageNamespaceID, WTFMove(topLevelOrigin) }, nullptr).iterator->value;
if (!slot)
slot = TransientLocalStorageNamespace::create();
return slot.get();
}
}