blob: d07c5225a867d22a38447568c09e4815705ce8f2 [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 "OriginStorageManager.h"
#include "FileSystemStorageHandleRegistry.h"
#include "FileSystemStorageManager.h"
#include "LocalStorageManager.h"
#include "MemoryStorageArea.h"
#include "SessionStorageManager.h"
#include "StorageAreaRegistry.h"
#include "WebsiteDataType.h"
#include <WebCore/SQLiteFileSystem.h>
#include <wtf/FileSystem.h>
namespace WebKit {
static constexpr auto originFileName = "origin"_s;
enum class OriginStorageManager::StorageBucketMode : bool { BestEffort, Persistent };
class OriginStorageManager::StorageBucket {
WTF_MAKE_FAST_ALLOCATED;
public:
StorageBucket(const String& rootPath, const String& identifier, const String& localStoragePath)
: m_rootPath(rootPath)
, m_identifier(identifier)
, m_localStoragePath(localStoragePath)
{
}
StorageBucketMode mode() const { return m_mode; }
void setMode(StorageBucketMode mode) { m_mode = mode; }
void connectionClosed(IPC::Connection::UniqueID connection)
{
if (m_fileSystemStorageManager)
m_fileSystemStorageManager->connectionClosed(connection);
}
enum class StorageType : uint8_t {
FileSystem,
LocalStorage,
SessionStorage,
};
static std::optional<WebsiteDataType> toWebsiteDataType(const String& storageIdentifier)
{
if (storageIdentifier == "FileSystem"_s)
return WebsiteDataType::FileSystem;
if (storageIdentifier == "LocalStorage"_s)
return WebsiteDataType::LocalStorage;
if (storageIdentifier == "SessionStorage"_s)
return WebsiteDataType::SessionStorage;
return std::nullopt;
}
static String toStorageIdentifier(StorageType type)
{
switch (type) {
case StorageType::FileSystem:
return "FileSystem"_s;
case StorageType::LocalStorage:
return "LocalStorage"_s;
case StorageType::SessionStorage:
return "SessionStorage"_s;
default:
break;
}
ASSERT_NOT_REACHED();
return ""_s;
}
String typeStoragePath(StorageType type) const
{
auto storageIdentifier = toStorageIdentifier(type);
if (m_rootPath.isEmpty() || storageIdentifier.isEmpty())
return emptyString();
return FileSystem::pathByAppendingComponent(m_rootPath, storageIdentifier);
}
FileSystemStorageManager& fileSystemStorageManager(FileSystemStorageHandleRegistry& registry)
{
if (!m_fileSystemStorageManager)
m_fileSystemStorageManager = makeUnique<FileSystemStorageManager>(typeStoragePath(StorageType::FileSystem), registry);
return *m_fileSystemStorageManager;
}
LocalStorageManager& localStorageManager(StorageAreaRegistry& registry)
{
if (!m_localStorageManager)
m_localStorageManager = makeUnique<LocalStorageManager>(m_localStoragePath, registry);
return *m_localStorageManager;
}
LocalStorageManager* existingLocalStorageManager()
{
return m_localStorageManager.get();
}
SessionStorageManager& sessionStorageManager(StorageAreaRegistry& registry)
{
if (!m_sessionStorageManager)
m_sessionStorageManager = makeUnique<SessionStorageManager>(registry);
return *m_sessionStorageManager;
}
SessionStorageManager* existingSessionStorageManager()
{
return m_sessionStorageManager.get();
}
bool isActive() const
{
return m_fileSystemStorageManager || m_localStorageManager || m_sessionStorageManager;
}
bool isEmpty() const
{
auto files = FileSystem::listDirectory(m_rootPath);
auto hasValidFile = WTF::anyOf(files, [&](auto file) {
bool isInvalidFile = (file == originFileName);
#if PLATFORM(COCOA)
isInvalidFile |= (file == ".DS_Store");
#endif
return !isInvalidFile;
});
if (hasValidFile)
return false;
return !FileSystem::fileExists(m_localStoragePath);
}
OptionSet<WebsiteDataType> fetchDataTypesInList(OptionSet<WebsiteDataType> types)
{
auto result = fetchDataTypesInListFromMemory(types);
result.add(fetchDataTypesInListFromDisk(types));
return result;
}
void deleteData(OptionSet<WebsiteDataType> types, WallTime modifiedSinceTime)
{
if (types.contains(WebsiteDataType::FileSystem))
deleteFileSystemStorageData(modifiedSinceTime);
if (types.contains(WebsiteDataType::LocalStorage))
deleteLocalStorageData(modifiedSinceTime);
if (types.contains(WebsiteDataType::SessionStorage) && modifiedSinceTime < WallTime::now())
deleteSessionStorageData();
}
void moveData(const String& path, const String& localStoragePath)
{
m_fileSystemStorageManager = nullptr;
if (m_localStorageManager)
m_localStorageManager->close();
FileSystem::makeAllDirectories(FileSystem::parentPath(path));
FileSystem::moveFile(m_rootPath, path);
if (!m_localStoragePath.isEmpty() && !localStoragePath.isEmpty()) {
FileSystem::makeAllDirectories(FileSystem::parentPath(localStoragePath));
WebCore::SQLiteFileSystem::moveDatabaseFile(m_localStoragePath, localStoragePath);
}
}
private:
OptionSet<WebsiteDataType> fetchDataTypesInListFromMemory(OptionSet<WebsiteDataType> types)
{
OptionSet<WebsiteDataType> result;
if (types.contains(WebsiteDataType::LocalStorage)) {
if (m_localStorageManager && m_localStorageManager->hasDataInMemory())
result.add(WebsiteDataType::LocalStorage);
}
if (types.contains(WebsiteDataType::SessionStorage)) {
if (m_sessionStorageManager && m_sessionStorageManager->hasDataInMemory())
result.add(WebsiteDataType::SessionStorage);
}
return result;
}
OptionSet<WebsiteDataType> fetchDataTypesInListFromDisk(OptionSet<WebsiteDataType> types)
{
OptionSet<WebsiteDataType> result;
for (auto& storageType : FileSystem::listDirectory(m_rootPath)) {
if (auto type = toWebsiteDataType(storageType); type && types.contains(*type))
result.add(*type);
}
if (types.contains(WebsiteDataType::LocalStorage) && !result.contains(WebsiteDataType::LocalStorage)) {
if (FileSystem::fileExists(m_localStoragePath))
result.add(WebsiteDataType::LocalStorage);
}
return result;
}
void deleteFileSystemStorageData(WallTime modifiedSinceTime)
{
m_fileSystemStorageManager = nullptr;
auto fileSystemStoragePath = typeStoragePath(StorageType::FileSystem);
FileSystem::deleteAllFilesModifiedSince(fileSystemStoragePath, modifiedSinceTime);
}
void deleteLocalStorageData(WallTime time)
{
if (auto modificationTime = FileSystem::fileModificationTime(m_localStoragePath); *modificationTime >= time) {
if (m_localStorageManager)
m_localStorageManager->clearDataOnDisk();
WebCore::SQLiteFileSystem::deleteDatabaseFile(m_localStoragePath);
}
if (!m_localStorageManager)
return;
m_localStorageManager->clearDataInMemory();
if (!m_localStorageManager->isActive())
m_localStorageManager = nullptr;
}
void deleteSessionStorageData()
{
if (!m_sessionStorageManager)
return;
m_sessionStorageManager->clearData();
if (!m_sessionStorageManager->isActive())
m_sessionStorageManager = nullptr;
}
String m_rootPath;
String m_identifier;
StorageBucketMode m_mode { StorageBucketMode::BestEffort };
std::unique_ptr<FileSystemStorageManager> m_fileSystemStorageManager;
std::unique_ptr<LocalStorageManager> m_localStorageManager;
String m_localStoragePath;
std::unique_ptr<SessionStorageManager> m_sessionStorageManager;
};
OriginStorageManager::OriginStorageManager(String&& path, String&& localStoragePath)
: m_path(WTFMove(path))
, m_localStoragePath(WTFMove(localStoragePath))
{
ASSERT(!RunLoop::isMain());
}
OriginStorageManager::~OriginStorageManager() = default;
void OriginStorageManager::connectionClosed(IPC::Connection::UniqueID connection)
{
if (m_defaultBucket)
m_defaultBucket->connectionClosed(connection);
}
OriginStorageManager::StorageBucket& OriginStorageManager::defaultBucket()
{
if (!m_defaultBucket)
m_defaultBucket = makeUnique<StorageBucket>(m_path, "default"_s, m_localStoragePath);
return *m_defaultBucket;
}
FileSystemStorageManager& OriginStorageManager::fileSystemStorageManager(FileSystemStorageHandleRegistry& registry)
{
return defaultBucket().fileSystemStorageManager(registry);
}
LocalStorageManager& OriginStorageManager::localStorageManager(StorageAreaRegistry& registry)
{
return defaultBucket().localStorageManager(registry);
}
LocalStorageManager* OriginStorageManager::existingLocalStorageManager()
{
return defaultBucket().existingLocalStorageManager();
}
SessionStorageManager& OriginStorageManager::sessionStorageManager(StorageAreaRegistry& registry)
{
return defaultBucket().sessionStorageManager(registry);
}
SessionStorageManager* OriginStorageManager::existingSessionStorageManager()
{
return defaultBucket().existingSessionStorageManager();
}
bool OriginStorageManager::isActive()
{
return defaultBucket().isActive();
}
bool OriginStorageManager::isEmpty()
{
return defaultBucket().isEmpty();
}
void OriginStorageManager::setPersisted(bool value)
{
ASSERT(!RunLoop::isMain());
m_persisted = value;
defaultBucket().setMode(value ? StorageBucketMode::Persistent : StorageBucketMode::BestEffort);
}
OptionSet<WebsiteDataType> OriginStorageManager::fetchDataTypesInList(OptionSet<WebsiteDataType> types)
{
ASSERT(!RunLoop::isMain());
return defaultBucket().fetchDataTypesInList(types);
}
void OriginStorageManager::deleteData(OptionSet<WebsiteDataType> types, WallTime modifiedSince)
{
ASSERT(!RunLoop::isMain());
defaultBucket().deleteData(types, modifiedSince);
}
void OriginStorageManager::moveData(const String& newPath, const String& localStoragePath)
{
ASSERT(!RunLoop::isMain());
defaultBucket().moveData(newPath, localStoragePath);
}
} // namespace WebKit