diff options
author | Lorry Tar Creator <lorry-tar-importer@lorry> | 2017-06-27 06:07:23 +0000 |
---|---|---|
committer | Lorry Tar Creator <lorry-tar-importer@lorry> | 2017-06-27 06:07:23 +0000 |
commit | 1bf1084f2b10c3b47fd1a588d85d21ed0eb41d0c (patch) | |
tree | 46dcd36c86e7fbc6e5df36deb463b33e9967a6f7 /Source/WebKit2/UIProcess/WebsiteData | |
parent | 32761a6cee1d0dee366b885b7b9c777e67885688 (diff) | |
download | WebKitGtk-tarball-master.tar.gz |
webkitgtk-2.16.5HEADwebkitgtk-2.16.5master
Diffstat (limited to 'Source/WebKit2/UIProcess/WebsiteData')
5 files changed, 1560 insertions, 0 deletions
diff --git a/Source/WebKit2/UIProcess/WebsiteData/WebsiteDataRecord.cpp b/Source/WebKit2/UIProcess/WebsiteData/WebsiteDataRecord.cpp new file mode 100644 index 000000000..174a0b200 --- /dev/null +++ b/Source/WebKit2/UIProcess/WebsiteData/WebsiteDataRecord.cpp @@ -0,0 +1,110 @@ +/* + * Copyright (C) 2015 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 "WebsiteDataRecord.h" + +#include <WebCore/LocalizedStrings.h> +#include <WebCore/PublicSuffix.h> +#include <WebCore/SecurityOrigin.h> + +#if PLATFORM(COCOA) +#import <WebCore/CFNetworkSPI.h> +#endif + +static String displayNameForLocalFiles() +{ + return WEB_UI_STRING("Local documents on your computer", "'Website' name displayed when local documents have stored local data"); +} + +namespace WebKit { + +String WebsiteDataRecord::displayNameForCookieHostName(const String& hostName) +{ +#if PLATFORM(COCOA) + if (hostName == String(kCFHTTPCookieLocalFileDomain)) + return displayNameForLocalFiles(); +#else + if (hostName == "localhost") + return hostName; +#endif + +#if ENABLE(PUBLIC_SUFFIX_LIST) + return WebCore::topPrivatelyControlledDomain(hostName.startsWith('.') ? hostName.substring(1) : hostName); +#endif + + return String(); +} + +#if ENABLE(NETSCAPE_PLUGIN_API) +String WebsiteDataRecord::displayNameForPluginDataHostName(const String& hostName) +{ +#if ENABLE(PUBLIC_SUFFIX_LIST) + return WebCore::topPrivatelyControlledDomain(hostName); +#endif + + return String(); +} +#endif + +String WebsiteDataRecord::displayNameForOrigin(const WebCore::SecurityOriginData& securityOrigin) +{ + const auto& protocol = securityOrigin.protocol; + + if (protocol == "file") + return displayNameForLocalFiles(); + +#if ENABLE(PUBLIC_SUFFIX_LIST) + if (protocol == "http" || protocol == "https") + return WebCore::topPrivatelyControlledDomain(securityOrigin.host); +#endif + + return String(); +} + +void WebsiteDataRecord::add(WebsiteDataType type, const WebCore::SecurityOriginData& origin) +{ + types |= type; + + origins.add(origin); +} + +void WebsiteDataRecord::addCookieHostName(const String& hostName) +{ + types |= WebsiteDataType::Cookies; + + cookieHostNames.add(hostName); +} + +#if ENABLE(NETSCAPE_PLUGIN_API) +void WebsiteDataRecord::addPluginDataHostName(const String& hostName) +{ + types |= WebsiteDataType::PlugInData; + + pluginDataHostNames.add(hostName); +} +#endif + +} diff --git a/Source/WebKit2/UIProcess/WebsiteData/WebsiteDataRecord.h b/Source/WebKit2/UIProcess/WebsiteData/WebsiteDataRecord.h new file mode 100644 index 000000000..137fc1c31 --- /dev/null +++ b/Source/WebKit2/UIProcess/WebsiteData/WebsiteDataRecord.h @@ -0,0 +1,73 @@ +/* + * Copyright (C) 2015 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. + */ + +#pragma once + +#include "WebsiteDataType.h" +#include <WebCore/SecurityOriginData.h> +#include <WebCore/SecurityOriginHash.h> +#include <wtf/HashMap.h> +#include <wtf/HashSet.h> +#include <wtf/OptionSet.h> +#include <wtf/Optional.h> +#include <wtf/text/StringHash.h> +#include <wtf/text/WTFString.h> + +namespace WebCore { +class SecurityOrigin; +} + +namespace WebKit { + +struct WebsiteDataRecord { + static String displayNameForCookieHostName(const String& hostName); +#if ENABLE(NETSCAPE_PLUGIN_API) + static String displayNameForPluginDataHostName(const String& hostName); +#endif + static String displayNameForOrigin(const WebCore::SecurityOriginData&); + + void add(WebsiteDataType, const WebCore::SecurityOriginData&); + void addCookieHostName(const String& hostName); +#if ENABLE(NETSCAPE_PLUGIN_API) + void addPluginDataHostName(const String& hostName); +#endif + + String displayName; + OptionSet<WebsiteDataType> types; + + struct Size { + uint64_t totalSize; + HashMap<unsigned, uint64_t> typeSizes; + }; + std::optional<Size> size; + + HashSet<WebCore::SecurityOriginData> origins; + HashSet<String> cookieHostNames; +#if ENABLE(NETSCAPE_PLUGIN_API) + HashSet<String> pluginDataHostNames; +#endif +}; + +} diff --git a/Source/WebKit2/UIProcess/WebsiteData/WebsiteDataStore.cpp b/Source/WebKit2/UIProcess/WebsiteData/WebsiteDataStore.cpp new file mode 100644 index 000000000..8fec3e4e1 --- /dev/null +++ b/Source/WebKit2/UIProcess/WebsiteData/WebsiteDataStore.cpp @@ -0,0 +1,1207 @@ +/* + * Copyright (C) 2014-2017 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 "WebsiteDataStore.h" + +#include "APIProcessPoolConfiguration.h" +#include "APIWebsiteDataRecord.h" +#include "NetworkProcessMessages.h" +#include "StorageManager.h" +#include "WebProcessMessages.h" +#include "WebProcessPool.h" +#include "WebResourceLoadStatisticsStore.h" +#include "WebResourceLoadStatisticsStoreMessages.h" +#include "WebsiteData.h" +#include <WebCore/ApplicationCacheStorage.h> +#include <WebCore/DatabaseTracker.h> +#include <WebCore/HTMLMediaElement.h> +#include <WebCore/OriginLock.h> +#include <WebCore/SecurityOrigin.h> +#include <WebCore/SecurityOriginData.h> +#include <wtf/RunLoop.h> + +#if ENABLE(NETSCAPE_PLUGIN_API) +#include "PluginProcessManager.h" +#endif + +namespace WebKit { + +static WebCore::SessionID generateNonPersistentSessionID() +{ + // FIXME: We count backwards here to not conflict with API::Session. + static uint64_t sessionID = std::numeric_limits<uint64_t>::max(); + + return WebCore::SessionID(--sessionID); +} + +static uint64_t generateIdentifier() +{ + static uint64_t identifier; + + return ++identifier; +} + +Ref<WebsiteDataStore> WebsiteDataStore::createNonPersistent() +{ + return adoptRef(*new WebsiteDataStore(generateNonPersistentSessionID())); +} + +Ref<WebsiteDataStore> WebsiteDataStore::create(Configuration configuration) +{ + return adoptRef(*new WebsiteDataStore(WTFMove(configuration))); +} + +WebsiteDataStore::WebsiteDataStore(Configuration configuration) + : m_identifier(generateIdentifier()) + , m_sessionID(WebCore::SessionID::defaultSessionID()) + , m_configuration(WTFMove(configuration)) + , m_storageManager(StorageManager::create(m_configuration.localStorageDirectory)) + , m_resourceLoadStatistics(WebResourceLoadStatisticsStore::create(m_configuration.resourceLoadStatisticsDirectory)) + , m_queue(WorkQueue::create("com.apple.WebKit.WebsiteDataStore")) +{ + platformInitialize(); +} + +WebsiteDataStore::WebsiteDataStore(WebCore::SessionID sessionID) + : m_identifier(generateIdentifier()) + , m_sessionID(sessionID) + , m_configuration() + , m_queue(WorkQueue::create("com.apple.WebKit.WebsiteDataStore")) +{ + platformInitialize(); +} + +WebsiteDataStore::~WebsiteDataStore() +{ + platformDestroy(); + + if (m_sessionID.isEphemeral()) { + for (auto& processPool : WebProcessPool::allProcessPools()) + processPool->sendToNetworkingProcess(Messages::NetworkProcess::DestroyPrivateBrowsingSession(m_sessionID)); + } +} + +void WebsiteDataStore::cloneSessionData(WebPageProxy& sourcePage, WebPageProxy& newPage) +{ + auto& sourceDataStore = sourcePage.websiteDataStore(); + auto& newDataStore = newPage.websiteDataStore(); + + // FIXME: Handle this. + if (&sourceDataStore != &newDataStore) + return; + + if (!sourceDataStore.m_storageManager) + return; + + sourceDataStore.m_storageManager->cloneSessionStorageNamespace(sourcePage.pageID(), newPage.pageID()); +} + +enum class ProcessAccessType { + None, + OnlyIfLaunched, + Launch, +}; + +static ProcessAccessType computeNetworkProcessAccessTypeForDataFetch(OptionSet<WebsiteDataType> dataTypes, bool isNonPersistentStore) +{ + ProcessAccessType processAccessType = ProcessAccessType::None; + + if (dataTypes.contains(WebsiteDataType::Cookies)) { + if (isNonPersistentStore) + processAccessType = std::max(processAccessType, ProcessAccessType::OnlyIfLaunched); + else + processAccessType = std::max(processAccessType, ProcessAccessType::Launch); + } + + if (dataTypes.contains(WebsiteDataType::DiskCache) && !isNonPersistentStore) + processAccessType = std::max(processAccessType, ProcessAccessType::Launch); + + return processAccessType; +} + +static ProcessAccessType computeWebProcessAccessTypeForDataFetch(OptionSet<WebsiteDataType> dataTypes, bool isNonPersistentStore) +{ + UNUSED_PARAM(isNonPersistentStore); + + ProcessAccessType processAccessType = ProcessAccessType::None; + + if (dataTypes.contains(WebsiteDataType::MemoryCache)) + return ProcessAccessType::OnlyIfLaunched; + + return processAccessType; +} + +void WebsiteDataStore::fetchData(OptionSet<WebsiteDataType> dataTypes, OptionSet<WebsiteDataFetchOption> fetchOptions, std::function<void (Vector<WebsiteDataRecord>)> completionHandler) +{ + struct CallbackAggregator final : ThreadSafeRefCounted<CallbackAggregator> { + explicit CallbackAggregator(OptionSet<WebsiteDataFetchOption> fetchOptions, std::function<void (Vector<WebsiteDataRecord>)> completionHandler) + : fetchOptions(fetchOptions) + , completionHandler(WTFMove(completionHandler)) + { + } + + ~CallbackAggregator() + { + ASSERT(!pendingCallbacks); + } + + void addPendingCallback() + { + pendingCallbacks++; + } + + void removePendingCallback(WebsiteData websiteData) + { + ASSERT(pendingCallbacks); + --pendingCallbacks; + + for (auto& entry : websiteData.entries) { + auto displayName = WebsiteDataRecord::displayNameForOrigin(entry.origin); + if (!displayName) + continue; + + auto& record = m_websiteDataRecords.add(displayName, WebsiteDataRecord { }).iterator->value; + if (!record.displayName) + record.displayName = WTFMove(displayName); + + record.add(entry.type, entry.origin); + + if (fetchOptions.contains(WebsiteDataFetchOption::ComputeSizes)) { + if (!record.size) + record.size = WebsiteDataRecord::Size { 0, { } }; + + record.size->totalSize += entry.size; + record.size->typeSizes.add(static_cast<unsigned>(entry.type), 0).iterator->value += entry.size; + } + } + + for (auto& hostName : websiteData.hostNamesWithCookies) { + auto displayName = WebsiteDataRecord::displayNameForCookieHostName(hostName); + if (!displayName) + continue; + + auto& record = m_websiteDataRecords.add(displayName, WebsiteDataRecord { }).iterator->value; + if (!record.displayName) + record.displayName = WTFMove(displayName); + + record.addCookieHostName(hostName); + } + +#if ENABLE(NETSCAPE_PLUGIN_API) + for (auto& hostName : websiteData.hostNamesWithPluginData) { + auto displayName = WebsiteDataRecord::displayNameForPluginDataHostName(hostName); + if (!displayName) + continue; + + auto& record = m_websiteDataRecords.add(displayName, WebsiteDataRecord { }).iterator->value; + if (!record.displayName) + record.displayName = WTFMove(displayName); + + record.addPluginDataHostName(hostName); + } +#endif + + callIfNeeded(); + } + + void callIfNeeded() + { + if (pendingCallbacks) + return; + + RunLoop::main().dispatch([callbackAggregator = makeRef(*this)]() mutable { + + WTF::Vector<WebsiteDataRecord> records; + records.reserveInitialCapacity(callbackAggregator->m_websiteDataRecords.size()); + + for (auto& record : callbackAggregator->m_websiteDataRecords.values()) + records.uncheckedAppend(WTFMove(record)); + + callbackAggregator->completionHandler(WTFMove(records)); + }); + } + + const OptionSet<WebsiteDataFetchOption> fetchOptions; + + unsigned pendingCallbacks = 0; + std::function<void (Vector<WebsiteDataRecord>)> completionHandler; + + HashMap<String, WebsiteDataRecord> m_websiteDataRecords; + }; + + RefPtr<CallbackAggregator> callbackAggregator = adoptRef(new CallbackAggregator(fetchOptions, WTFMove(completionHandler))); + +#if ENABLE(VIDEO) + if (dataTypes.contains(WebsiteDataType::DiskCache)) { + callbackAggregator->addPendingCallback(); + m_queue->dispatch([fetchOptions, mediaCacheDirectory = m_configuration.mediaCacheDirectory.isolatedCopy(), callbackAggregator] { + // FIXME: Make HTMLMediaElement::originsInMediaCache return a collection of SecurityOriginDatas. + HashSet<RefPtr<WebCore::SecurityOrigin>> origins = WebCore::HTMLMediaElement::originsInMediaCache(mediaCacheDirectory); + WebsiteData websiteData; + + for (auto& origin : origins) { + WebsiteData::Entry entry { WebCore::SecurityOriginData::fromSecurityOrigin(*origin), WebsiteDataType::DiskCache, 0 }; + websiteData.entries.append(WTFMove(entry)); + } + + RunLoop::main().dispatch([callbackAggregator, origins = WTFMove(origins), websiteData = WTFMove(websiteData)]() mutable { + callbackAggregator->removePendingCallback(WTFMove(websiteData)); + }); + }); + } +#endif + + auto networkProcessAccessType = computeNetworkProcessAccessTypeForDataFetch(dataTypes, !isPersistent()); + if (networkProcessAccessType != ProcessAccessType::None) { + for (auto& processPool : processPools()) { + switch (networkProcessAccessType) { + case ProcessAccessType::OnlyIfLaunched: + if (!processPool->networkProcess()) + continue; + break; + + case ProcessAccessType::Launch: + processPool->ensureNetworkProcess(); + break; + + case ProcessAccessType::None: + ASSERT_NOT_REACHED(); + } + + callbackAggregator->addPendingCallback(); + processPool->networkProcess()->fetchWebsiteData(m_sessionID, dataTypes, fetchOptions, [callbackAggregator, processPool](WebsiteData websiteData) { + callbackAggregator->removePendingCallback(WTFMove(websiteData)); + }); + } + } + + auto webProcessAccessType = computeWebProcessAccessTypeForDataFetch(dataTypes, !isPersistent()); + if (webProcessAccessType != ProcessAccessType::None) { + for (auto& process : processes()) { + switch (webProcessAccessType) { + case ProcessAccessType::OnlyIfLaunched: + if (!process->canSendMessage()) + continue; + break; + + case ProcessAccessType::Launch: + // FIXME: Handle this. + ASSERT_NOT_REACHED(); + break; + + case ProcessAccessType::None: + ASSERT_NOT_REACHED(); + } + + callbackAggregator->addPendingCallback(); + process->fetchWebsiteData(m_sessionID, dataTypes, [callbackAggregator](WebsiteData websiteData) { + callbackAggregator->removePendingCallback(WTFMove(websiteData)); + }); + } + } + + if (dataTypes.contains(WebsiteDataType::SessionStorage) && m_storageManager) { + callbackAggregator->addPendingCallback(); + + m_storageManager->getSessionStorageOrigins([callbackAggregator](HashSet<WebCore::SecurityOriginData>&& origins) { + WebsiteData websiteData; + + while (!origins.isEmpty()) + websiteData.entries.append(WebsiteData::Entry { origins.takeAny(), WebsiteDataType::SessionStorage, 0 }); + + callbackAggregator->removePendingCallback(WTFMove(websiteData)); + }); + } + + if (dataTypes.contains(WebsiteDataType::LocalStorage) && m_storageManager) { + callbackAggregator->addPendingCallback(); + + m_storageManager->getLocalStorageOrigins([callbackAggregator](HashSet<WebCore::SecurityOriginData>&& origins) { + WebsiteData websiteData; + + while (!origins.isEmpty()) + websiteData.entries.append(WebsiteData::Entry { origins.takeAny(), WebsiteDataType::LocalStorage, 0 }); + + callbackAggregator->removePendingCallback(WTFMove(websiteData)); + }); + } + + if (dataTypes.contains(WebsiteDataType::OfflineWebApplicationCache) && isPersistent()) { + callbackAggregator->addPendingCallback(); + + m_queue->dispatch([fetchOptions, applicationCacheDirectory = m_configuration.applicationCacheDirectory.isolatedCopy(), applicationCacheFlatFileSubdirectoryName = m_configuration.applicationCacheFlatFileSubdirectoryName.isolatedCopy(), callbackAggregator] { + auto storage = WebCore::ApplicationCacheStorage::create(applicationCacheDirectory, applicationCacheFlatFileSubdirectoryName); + + WebsiteData websiteData; + + HashSet<RefPtr<WebCore::SecurityOrigin>> origins; + // FIXME: getOriginsWithCache should return a collection of SecurityOriginDatas. + storage->getOriginsWithCache(origins); + + for (auto& origin : origins) { + uint64_t size = fetchOptions.contains(WebsiteDataFetchOption::ComputeSizes) ? storage->diskUsageForOrigin(*origin) : 0; + WebsiteData::Entry entry { WebCore::SecurityOriginData::fromSecurityOrigin(*origin), WebsiteDataType::OfflineWebApplicationCache, size }; + + websiteData.entries.append(WTFMove(entry)); + } + + RunLoop::main().dispatch([callbackAggregator, origins = WTFMove(origins), websiteData = WTFMove(websiteData)]() mutable { + callbackAggregator->removePendingCallback(WTFMove(websiteData)); + }); + }); + } + + if (dataTypes.contains(WebsiteDataType::WebSQLDatabases) && isPersistent()) { + callbackAggregator->addPendingCallback(); + + m_queue->dispatch([webSQLDatabaseDirectory = m_configuration.webSQLDatabaseDirectory.isolatedCopy(), callbackAggregator] { + auto origins = WebCore::DatabaseTracker::trackerWithDatabasePath(webSQLDatabaseDirectory)->origins(); + RunLoop::main().dispatch([callbackAggregator, origins = WTFMove(origins)]() mutable { + WebsiteData websiteData; + for (auto& origin : origins) + websiteData.entries.append(WebsiteData::Entry { WTFMove(origin), WebsiteDataType::WebSQLDatabases, 0 }); + callbackAggregator->removePendingCallback(WTFMove(websiteData)); + }); + }); + } + +#if ENABLE(DATABASE_PROCESS) + if (dataTypes.contains(WebsiteDataType::IndexedDBDatabases) && isPersistent()) { + for (auto& processPool : processPools()) { + processPool->ensureDatabaseProcess(); + + callbackAggregator->addPendingCallback(); + processPool->databaseProcess()->fetchWebsiteData(m_sessionID, dataTypes, [callbackAggregator, processPool](WebsiteData websiteData) { + callbackAggregator->removePendingCallback(WTFMove(websiteData)); + }); + } + } +#endif + + if (dataTypes.contains(WebsiteDataType::MediaKeys) && isPersistent()) { + callbackAggregator->addPendingCallback(); + + m_queue->dispatch([mediaKeysStorageDirectory = m_configuration.mediaKeysStorageDirectory.isolatedCopy(), callbackAggregator] { + auto origins = mediaKeyOrigins(mediaKeysStorageDirectory); + + RunLoop::main().dispatch([callbackAggregator, origins = WTFMove(origins)]() mutable { + WebsiteData websiteData; + for (auto& origin : origins) + websiteData.entries.append(WebsiteData::Entry { origin, WebsiteDataType::MediaKeys, 0 }); + + callbackAggregator->removePendingCallback(WTFMove(websiteData)); + }); + }); + } + +#if ENABLE(NETSCAPE_PLUGIN_API) + if (dataTypes.contains(WebsiteDataType::PlugInData) && isPersistent()) { + class State { + public: + static void fetchData(Ref<CallbackAggregator>&& callbackAggregator, Vector<PluginModuleInfo>&& plugins) + { + new State(WTFMove(callbackAggregator), WTFMove(plugins)); + } + + private: + State(Ref<CallbackAggregator>&& callbackAggregator, Vector<PluginModuleInfo>&& plugins) + : m_callbackAggregator(WTFMove(callbackAggregator)) + , m_plugins(WTFMove(plugins)) + { + m_callbackAggregator->addPendingCallback(); + + fetchWebsiteDataForNextPlugin(); + } + + ~State() + { + ASSERT(m_plugins.isEmpty()); + } + + void fetchWebsiteDataForNextPlugin() + { + if (m_plugins.isEmpty()) { + WebsiteData websiteData; + websiteData.hostNamesWithPluginData = WTFMove(m_hostNames); + + m_callbackAggregator->removePendingCallback(WTFMove(websiteData)); + + delete this; + return; + } + + auto plugin = m_plugins.takeLast(); + PluginProcessManager::singleton().fetchWebsiteData(plugin, [this](Vector<String> hostNames) { + for (auto& hostName : hostNames) + m_hostNames.add(WTFMove(hostName)); + fetchWebsiteDataForNextPlugin(); + }); + } + + Ref<CallbackAggregator> m_callbackAggregator; + Vector<PluginModuleInfo> m_plugins; + HashSet<String> m_hostNames; + }; + + State::fetchData(*callbackAggregator, plugins()); + } +#endif + + callbackAggregator->callIfNeeded(); +} + +void WebsiteDataStore::fetchDataForTopPrivatelyOwnedDomains(OptionSet<WebsiteDataType> dataTypes, OptionSet<WebsiteDataFetchOption> fetchOptions, const Vector<String>& topPrivatelyOwnedDomains, std::function<void(Vector<WebsiteDataRecord>)> completionHandler) +{ + fetchData(dataTypes, fetchOptions, [topPrivatelyOwnedDomains, completionHandler, this](auto existingDataRecords) { + Vector<WebsiteDataRecord> matchingDataRecords; + Vector<String> domainsWithDataRecords; + for (auto& dataRecord : existingDataRecords) { + bool dataRecordAdded; + for (auto& dataRecordOriginData : dataRecord.origins) { + dataRecordAdded = false; + String dataRecordHost = dataRecordOriginData.securityOrigin().get().host(); + for (auto& topPrivatelyOwnedDomain : topPrivatelyOwnedDomains) { + if (dataRecordHost.endsWithIgnoringASCIICase(topPrivatelyOwnedDomain)) { + auto suffixStart = dataRecordHost.length() - topPrivatelyOwnedDomain.length(); + if (!suffixStart || dataRecordHost[suffixStart - 1] == '.') { + matchingDataRecords.append(dataRecord); + domainsWithDataRecords.append(topPrivatelyOwnedDomain); + dataRecordAdded = true; + break; + } + } + } + if (dataRecordAdded) + break; + } + } + completionHandler(matchingDataRecords); + }); +} + +static ProcessAccessType computeNetworkProcessAccessTypeForDataRemoval(OptionSet<WebsiteDataType> dataTypes, bool isNonPersistentStore) +{ + ProcessAccessType processAccessType = ProcessAccessType::None; + + if (dataTypes.contains(WebsiteDataType::Cookies)) { + if (isNonPersistentStore) + processAccessType = std::max(processAccessType, ProcessAccessType::OnlyIfLaunched); + else + processAccessType = std::max(processAccessType, ProcessAccessType::Launch); + } + + if (dataTypes.contains(WebsiteDataType::DiskCache) && !isNonPersistentStore) + processAccessType = std::max(processAccessType, ProcessAccessType::Launch); + + if (dataTypes.contains(WebsiteDataType::HSTSCache)) + processAccessType = std::max(processAccessType, ProcessAccessType::Launch); + + return processAccessType; +} + +static ProcessAccessType computeWebProcessAccessTypeForDataRemoval(OptionSet<WebsiteDataType> dataTypes, bool isNonPersistentStore) +{ + UNUSED_PARAM(isNonPersistentStore); + + ProcessAccessType processAccessType = ProcessAccessType::None; + + if (dataTypes.contains(WebsiteDataType::MemoryCache)) + processAccessType = std::max(processAccessType, ProcessAccessType::OnlyIfLaunched); + + return processAccessType; +} + +void WebsiteDataStore::removeData(OptionSet<WebsiteDataType> dataTypes, std::chrono::system_clock::time_point modifiedSince, std::function<void ()> completionHandler) +{ + struct CallbackAggregator : ThreadSafeRefCounted<CallbackAggregator> { + explicit CallbackAggregator (std::function<void ()> completionHandler) + : completionHandler(WTFMove(completionHandler)) + { + } + + void addPendingCallback() + { + pendingCallbacks++; + } + + void removePendingCallback() + { + ASSERT(pendingCallbacks); + --pendingCallbacks; + + callIfNeeded(); + } + + void callIfNeeded() + { + if (!pendingCallbacks) + RunLoop::main().dispatch(WTFMove(completionHandler)); + } + + unsigned pendingCallbacks = 0; + std::function<void()> completionHandler; + }; + + RefPtr<CallbackAggregator> callbackAggregator = adoptRef(new CallbackAggregator(WTFMove(completionHandler))); + +#if ENABLE(VIDEO) + if (dataTypes.contains(WebsiteDataType::DiskCache)) { + callbackAggregator->addPendingCallback(); + m_queue->dispatch([modifiedSince, mediaCacheDirectory = m_configuration.mediaCacheDirectory.isolatedCopy(), callbackAggregator] { + WebCore::HTMLMediaElement::clearMediaCache(mediaCacheDirectory, modifiedSince); + + WTF::RunLoop::main().dispatch([callbackAggregator] { + callbackAggregator->removePendingCallback(); + }); + }); + } +#endif + + auto networkProcessAccessType = computeNetworkProcessAccessTypeForDataRemoval(dataTypes, !isPersistent()); + if (networkProcessAccessType != ProcessAccessType::None) { + for (auto& processPool : processPools()) { + switch (networkProcessAccessType) { + case ProcessAccessType::OnlyIfLaunched: + if (!processPool->networkProcess()) + continue; + break; + + case ProcessAccessType::Launch: + processPool->ensureNetworkProcess(); + break; + + case ProcessAccessType::None: + ASSERT_NOT_REACHED(); + } + + callbackAggregator->addPendingCallback(); + processPool->networkProcess()->deleteWebsiteData(m_sessionID, dataTypes, modifiedSince, [callbackAggregator, processPool] { + callbackAggregator->removePendingCallback(); + }); + } + } + + auto webProcessAccessType = computeWebProcessAccessTypeForDataRemoval(dataTypes, !isPersistent()); + if (webProcessAccessType != ProcessAccessType::None) { + for (auto& process : processes()) { + switch (webProcessAccessType) { + case ProcessAccessType::OnlyIfLaunched: + if (!process->canSendMessage()) + continue; + break; + + case ProcessAccessType::Launch: + // FIXME: Handle this. + ASSERT_NOT_REACHED(); + break; + + case ProcessAccessType::None: + ASSERT_NOT_REACHED(); + } + + callbackAggregator->addPendingCallback(); + process->deleteWebsiteData(m_sessionID, dataTypes, modifiedSince, [callbackAggregator] { + callbackAggregator->removePendingCallback(); + }); + } + } + + if (dataTypes.contains(WebsiteDataType::SessionStorage) && m_storageManager) { + callbackAggregator->addPendingCallback(); + + m_storageManager->deleteSessionStorageOrigins([callbackAggregator] { + callbackAggregator->removePendingCallback(); + }); + } + + if (dataTypes.contains(WebsiteDataType::LocalStorage) && m_storageManager) { + callbackAggregator->addPendingCallback(); + + m_storageManager->deleteLocalStorageOriginsModifiedSince(modifiedSince, [callbackAggregator] { + callbackAggregator->removePendingCallback(); + }); + } + + if (dataTypes.contains(WebsiteDataType::OfflineWebApplicationCache) && isPersistent()) { + callbackAggregator->addPendingCallback(); + + m_queue->dispatch([applicationCacheDirectory = m_configuration.applicationCacheDirectory.isolatedCopy(), applicationCacheFlatFileSubdirectoryName = m_configuration.applicationCacheFlatFileSubdirectoryName.isolatedCopy(), callbackAggregator] { + auto storage = WebCore::ApplicationCacheStorage::create(applicationCacheDirectory, applicationCacheFlatFileSubdirectoryName); + + storage->deleteAllCaches(); + + WTF::RunLoop::main().dispatch([callbackAggregator] { + callbackAggregator->removePendingCallback(); + }); + }); + } + + if (dataTypes.contains(WebsiteDataType::WebSQLDatabases) && isPersistent()) { + callbackAggregator->addPendingCallback(); + + m_queue->dispatch([webSQLDatabaseDirectory = m_configuration.webSQLDatabaseDirectory.isolatedCopy(), callbackAggregator, modifiedSince] { + WebCore::DatabaseTracker::trackerWithDatabasePath(webSQLDatabaseDirectory)->deleteDatabasesModifiedSince(modifiedSince); + + RunLoop::main().dispatch([callbackAggregator] { + callbackAggregator->removePendingCallback(); + }); + }); + } + +#if ENABLE(DATABASE_PROCESS) + if (dataTypes.contains(WebsiteDataType::IndexedDBDatabases) && isPersistent()) { + for (auto& processPool : processPools()) { + processPool->ensureDatabaseProcess(); + + callbackAggregator->addPendingCallback(); + processPool->databaseProcess()->deleteWebsiteData(m_sessionID, dataTypes, modifiedSince, [callbackAggregator, processPool] { + callbackAggregator->removePendingCallback(); + }); + } + } +#endif + + if (dataTypes.contains(WebsiteDataType::MediaKeys) && isPersistent()) { + callbackAggregator->addPendingCallback(); + + m_queue->dispatch([mediaKeysStorageDirectory = m_configuration.mediaKeysStorageDirectory.isolatedCopy(), callbackAggregator, modifiedSince] { + removeMediaKeys(mediaKeysStorageDirectory, modifiedSince); + + RunLoop::main().dispatch([callbackAggregator] { + callbackAggregator->removePendingCallback(); + }); + }); + } + + if (dataTypes.contains(WebsiteDataType::SearchFieldRecentSearches) && isPersistent()) { + callbackAggregator->addPendingCallback(); + + m_queue->dispatch([modifiedSince, callbackAggregator] { + platformRemoveRecentSearches(modifiedSince); + + RunLoop::main().dispatch([callbackAggregator] { + callbackAggregator->removePendingCallback(); + }); + }); + } + +#if ENABLE(NETSCAPE_PLUGIN_API) + if (dataTypes.contains(WebsiteDataType::PlugInData) && isPersistent()) { + class State { + public: + static void deleteData(Ref<CallbackAggregator>&& callbackAggregator, Vector<PluginModuleInfo>&& plugins, std::chrono::system_clock::time_point modifiedSince) + { + new State(WTFMove(callbackAggregator), WTFMove(plugins), modifiedSince); + } + + private: + State(Ref<CallbackAggregator>&& callbackAggregator, Vector<PluginModuleInfo>&& plugins, std::chrono::system_clock::time_point modifiedSince) + : m_callbackAggregator(WTFMove(callbackAggregator)) + , m_plugins(WTFMove(plugins)) + , m_modifiedSince(modifiedSince) + { + m_callbackAggregator->addPendingCallback(); + + deleteWebsiteDataForNextPlugin(); + } + + ~State() + { + ASSERT(m_plugins.isEmpty()); + } + + void deleteWebsiteDataForNextPlugin() + { + if (m_plugins.isEmpty()) { + m_callbackAggregator->removePendingCallback(); + + delete this; + return; + } + + auto plugin = m_plugins.takeLast(); + PluginProcessManager::singleton().deleteWebsiteData(plugin, m_modifiedSince, [this] { + deleteWebsiteDataForNextPlugin(); + }); + } + + Ref<CallbackAggregator> m_callbackAggregator; + Vector<PluginModuleInfo> m_plugins; + std::chrono::system_clock::time_point m_modifiedSince; + }; + + State::deleteData(*callbackAggregator, plugins(), modifiedSince); + } +#endif + + // There's a chance that we don't have any pending callbacks. If so, we want to dispatch the completion handler right away. + callbackAggregator->callIfNeeded(); +} + +void WebsiteDataStore::removeData(OptionSet<WebsiteDataType> dataTypes, const Vector<WebsiteDataRecord>& dataRecords, std::function<void ()> completionHandler) +{ + Vector<WebCore::SecurityOriginData> origins; + + for (const auto& dataRecord : dataRecords) { + for (auto& origin : dataRecord.origins) + origins.append(origin); + } + + struct CallbackAggregator : ThreadSafeRefCounted<CallbackAggregator> { + explicit CallbackAggregator (std::function<void ()> completionHandler) + : completionHandler(WTFMove(completionHandler)) + { + } + + void addPendingCallback() + { + pendingCallbacks++; + } + + void removePendingCallback() + { + ASSERT(pendingCallbacks); + --pendingCallbacks; + + callIfNeeded(); + } + + void callIfNeeded() + { + if (!pendingCallbacks) + RunLoop::main().dispatch(WTFMove(completionHandler)); + } + + unsigned pendingCallbacks = 0; + std::function<void()> completionHandler; + }; + + RefPtr<CallbackAggregator> callbackAggregator = adoptRef(new CallbackAggregator(WTFMove(completionHandler))); + + if (dataTypes.contains(WebsiteDataType::DiskCache)) { + HashSet<WebCore::SecurityOriginData> origins; + for (const auto& dataRecord : dataRecords) { + for (const auto& origin : dataRecord.origins) + origins.add(origin); + } + +#if ENABLE(VIDEO) + callbackAggregator->addPendingCallback(); + m_queue->dispatch([origins = WTFMove(origins), mediaCacheDirectory = m_configuration.mediaCacheDirectory.isolatedCopy(), callbackAggregator] { + + // FIXME: Move SecurityOrigin::toRawString to SecurityOriginData and + // make HTMLMediaElement::clearMediaCacheForOrigins take SecurityOriginData. + HashSet<RefPtr<WebCore::SecurityOrigin>> securityOrigins; + for (auto& origin : origins) + securityOrigins.add(origin.securityOrigin()); + WebCore::HTMLMediaElement::clearMediaCacheForOrigins(mediaCacheDirectory, securityOrigins); + + WTF::RunLoop::main().dispatch([callbackAggregator] { + callbackAggregator->removePendingCallback(); + }); + }); +#endif + } + + auto networkProcessAccessType = computeNetworkProcessAccessTypeForDataRemoval(dataTypes, !isPersistent()); + if (networkProcessAccessType != ProcessAccessType::None) { + for (auto& processPool : processPools()) { + switch (networkProcessAccessType) { + case ProcessAccessType::OnlyIfLaunched: + if (!processPool->networkProcess()) + continue; + break; + + case ProcessAccessType::Launch: + processPool->ensureNetworkProcess(); + break; + + case ProcessAccessType::None: + ASSERT_NOT_REACHED(); + } + + Vector<String> cookieHostNames; + for (const auto& dataRecord : dataRecords) { + for (auto& hostName : dataRecord.cookieHostNames) + cookieHostNames.append(hostName); + } + + callbackAggregator->addPendingCallback(); + processPool->networkProcess()->deleteWebsiteDataForOrigins(m_sessionID, dataTypes, origins, cookieHostNames, [callbackAggregator, processPool] { + callbackAggregator->removePendingCallback(); + }); + } + } + + auto webProcessAccessType = computeWebProcessAccessTypeForDataRemoval(dataTypes, !isPersistent()); + if (webProcessAccessType != ProcessAccessType::None) { + for (auto& process : processes()) { + switch (webProcessAccessType) { + case ProcessAccessType::OnlyIfLaunched: + if (!process->canSendMessage()) + continue; + break; + + case ProcessAccessType::Launch: + // FIXME: Handle this. + ASSERT_NOT_REACHED(); + break; + + case ProcessAccessType::None: + ASSERT_NOT_REACHED(); + } + + callbackAggregator->addPendingCallback(); + + process->deleteWebsiteDataForOrigins(m_sessionID, dataTypes, origins, [callbackAggregator] { + callbackAggregator->removePendingCallback(); + }); + } + } + + if (dataTypes.contains(WebsiteDataType::SessionStorage) && m_storageManager) { + callbackAggregator->addPendingCallback(); + + m_storageManager->deleteSessionStorageEntriesForOrigins(origins, [callbackAggregator] { + callbackAggregator->removePendingCallback(); + }); + } + + if (dataTypes.contains(WebsiteDataType::LocalStorage) && m_storageManager) { + callbackAggregator->addPendingCallback(); + + m_storageManager->deleteLocalStorageEntriesForOrigins(origins, [callbackAggregator] { + callbackAggregator->removePendingCallback(); + }); + } + + if (dataTypes.contains(WebsiteDataType::OfflineWebApplicationCache) && isPersistent()) { + HashSet<WebCore::SecurityOriginData> origins; + for (const auto& dataRecord : dataRecords) { + for (const auto& origin : dataRecord.origins) + origins.add(origin); + } + + callbackAggregator->addPendingCallback(); + m_queue->dispatch([origins = WTFMove(origins), applicationCacheDirectory = m_configuration.applicationCacheDirectory.isolatedCopy(), applicationCacheFlatFileSubdirectoryName = m_configuration.applicationCacheFlatFileSubdirectoryName.isolatedCopy(), callbackAggregator] { + auto storage = WebCore::ApplicationCacheStorage::create(applicationCacheDirectory, applicationCacheFlatFileSubdirectoryName); + + for (const auto& origin : origins) + storage->deleteCacheForOrigin(origin.securityOrigin()); + + WTF::RunLoop::main().dispatch([callbackAggregator] { + callbackAggregator->removePendingCallback(); + }); + }); + } + + if (dataTypes.contains(WebsiteDataType::WebSQLDatabases) && isPersistent()) { + HashSet<WebCore::SecurityOriginData> origins; + for (const auto& dataRecord : dataRecords) { + for (const auto& origin : dataRecord.origins) + origins.add(origin); + } + + callbackAggregator->addPendingCallback(); + m_queue->dispatch([origins = WTFMove(origins), callbackAggregator, webSQLDatabaseDirectory = m_configuration.webSQLDatabaseDirectory.isolatedCopy()] { + auto databaseTracker = WebCore::DatabaseTracker::trackerWithDatabasePath(webSQLDatabaseDirectory); + for (auto& origin : origins) + databaseTracker->deleteOrigin(origin); + RunLoop::main().dispatch([callbackAggregator] { + callbackAggregator->removePendingCallback(); + }); + }); + } + +#if ENABLE(DATABASE_PROCESS) + if (dataTypes.contains(WebsiteDataType::IndexedDBDatabases) && isPersistent()) { + for (auto& processPool : processPools()) { + processPool->ensureDatabaseProcess(); + + callbackAggregator->addPendingCallback(); + processPool->databaseProcess()->deleteWebsiteDataForOrigins(m_sessionID, dataTypes, origins, [callbackAggregator, processPool] { + callbackAggregator->removePendingCallback(); + }); + } + } +#endif + + if (dataTypes.contains(WebsiteDataType::MediaKeys) && isPersistent()) { + HashSet<WebCore::SecurityOriginData> origins; + for (const auto& dataRecord : dataRecords) { + for (const auto& origin : dataRecord.origins) + origins.add(origin); + } + + callbackAggregator->addPendingCallback(); + m_queue->dispatch([mediaKeysStorageDirectory = m_configuration.mediaKeysStorageDirectory.isolatedCopy(), callbackAggregator, origins = WTFMove(origins)] { + + removeMediaKeys(mediaKeysStorageDirectory, origins); + + RunLoop::main().dispatch([callbackAggregator] { + callbackAggregator->removePendingCallback(); + }); + }); + } + +#if ENABLE(NETSCAPE_PLUGIN_API) + if (dataTypes.contains(WebsiteDataType::PlugInData) && isPersistent()) { + Vector<String> hostNames; + for (const auto& dataRecord : dataRecords) { + for (const auto& hostName : dataRecord.pluginDataHostNames) + hostNames.append(hostName); + } + + + class State { + public: + static void deleteData(Ref<CallbackAggregator>&& callbackAggregator, Vector<PluginModuleInfo>&& plugins, Vector<String>&& hostNames) + { + new State(WTFMove(callbackAggregator), WTFMove(plugins), WTFMove(hostNames)); + } + + private: + State(Ref<CallbackAggregator>&& callbackAggregator, Vector<PluginModuleInfo>&& plugins, Vector<String>&& hostNames) + : m_callbackAggregator(WTFMove(callbackAggregator)) + , m_plugins(WTFMove(plugins)) + , m_hostNames(WTFMove(hostNames)) + { + m_callbackAggregator->addPendingCallback(); + + deleteWebsiteDataForNextPlugin(); + } + + ~State() + { + ASSERT(m_plugins.isEmpty()); + } + + void deleteWebsiteDataForNextPlugin() + { + if (m_plugins.isEmpty()) { + m_callbackAggregator->removePendingCallback(); + + delete this; + return; + } + + auto plugin = m_plugins.takeLast(); + PluginProcessManager::singleton().deleteWebsiteDataForHostNames(plugin, m_hostNames, [this] { + deleteWebsiteDataForNextPlugin(); + }); + } + + Ref<CallbackAggregator> m_callbackAggregator; + Vector<PluginModuleInfo> m_plugins; + Vector<String> m_hostNames; + }; + + State::deleteData(*callbackAggregator, plugins(), WTFMove(hostNames)); + } +#endif + + // There's a chance that we don't have any pending callbacks. If so, we want to dispatch the completion handler right away. + callbackAggregator->callIfNeeded(); +} + +void WebsiteDataStore::removeDataForTopPrivatelyOwnedDomains(OptionSet<WebsiteDataType> dataTypes, OptionSet<WebsiteDataFetchOption> fetchOptions, const Vector<String>& topPrivatelyOwnedDomains, std::function<void()> completionHandler) +{ + fetchDataForTopPrivatelyOwnedDomains(dataTypes, fetchOptions, topPrivatelyOwnedDomains, [dataTypes, completionHandler, this](auto websiteDataRecords) { + this->removeData(dataTypes, websiteDataRecords, [completionHandler]() { + completionHandler(); + }); + }); +} + +void WebsiteDataStore::webPageWasAdded(WebPageProxy& webPageProxy) +{ + if (m_storageManager) + m_storageManager->createSessionStorageNamespace(webPageProxy.pageID(), std::numeric_limits<unsigned>::max()); +} + +void WebsiteDataStore::webPageWasRemoved(WebPageProxy& webPageProxy) +{ + if (m_storageManager) + m_storageManager->destroySessionStorageNamespace(webPageProxy.pageID()); +} + +void WebsiteDataStore::webProcessWillOpenConnection(WebProcessProxy& webProcessProxy, IPC::Connection& connection) +{ + if (m_storageManager) + m_storageManager->processWillOpenConnection(webProcessProxy, connection); + + if (m_resourceLoadStatistics) + m_resourceLoadStatistics->processWillOpenConnection(webProcessProxy, connection); +} + +void WebsiteDataStore::webPageWillOpenConnection(WebPageProxy& webPageProxy, IPC::Connection& connection) +{ + if (m_storageManager) + m_storageManager->setAllowedSessionStorageNamespaceConnection(webPageProxy.pageID(), &connection); +} + +void WebsiteDataStore::webPageDidCloseConnection(WebPageProxy& webPageProxy, IPC::Connection&) +{ + if (m_storageManager) + m_storageManager->setAllowedSessionStorageNamespaceConnection(webPageProxy.pageID(), nullptr); +} + +void WebsiteDataStore::webProcessDidCloseConnection(WebProcessProxy& webProcessProxy, IPC::Connection& connection) +{ + if (m_resourceLoadStatistics) + m_resourceLoadStatistics->processDidCloseConnection(webProcessProxy, connection); + + if (m_storageManager) + m_storageManager->processDidCloseConnection(webProcessProxy, connection); +} + +HashSet<RefPtr<WebProcessPool>> WebsiteDataStore::processPools() const +{ + HashSet<RefPtr<WebProcessPool>> processPools; + for (auto& process : processes()) + processPools.add(&process->processPool()); + + if (processPools.isEmpty()) { + // Check if we're one of the legacy data stores. + for (auto& processPool : WebProcessPool::allProcessPools()) { + if (auto dataStore = processPool->websiteDataStore()) { + if (&dataStore->websiteDataStore() == this) { + processPools.add(processPool); + break; + } + } + } + } + + if (processPools.isEmpty()) { + auto processPool = WebProcessPool::create(API::ProcessPoolConfiguration::createWithWebsiteDataStoreConfiguration(m_configuration)); + processPools.add(processPool.ptr()); + } + + return processPools; +} + +#if ENABLE(NETSCAPE_PLUGIN_API) +Vector<PluginModuleInfo> WebsiteDataStore::plugins() const +{ + Vector<PluginModuleInfo> plugins; + + for (auto& processPool : processPools()) { + for (auto& plugin : processPool->pluginInfoStore().plugins()) + plugins.append(plugin); + } + + return plugins; +} +#endif + +static String computeMediaKeyFile(const String& mediaKeyDirectory) +{ + return WebCore::pathByAppendingComponent(mediaKeyDirectory, "SecureStop.plist"); +} + +Vector<WebCore::SecurityOriginData> WebsiteDataStore::mediaKeyOrigins(const String& mediaKeysStorageDirectory) +{ + ASSERT(!mediaKeysStorageDirectory.isEmpty()); + + Vector<WebCore::SecurityOriginData> origins; + + for (const auto& originPath : WebCore::listDirectory(mediaKeysStorageDirectory, "*")) { + auto mediaKeyFile = computeMediaKeyFile(originPath); + if (!WebCore::fileExists(mediaKeyFile)) + continue; + + auto mediaKeyIdentifier = WebCore::pathGetFileName(originPath); + + if (auto securityOrigin = WebCore::SecurityOriginData::fromDatabaseIdentifier(mediaKeyIdentifier)) + origins.append(*securityOrigin); + } + + return origins; +} + +void WebsiteDataStore::removeMediaKeys(const String& mediaKeysStorageDirectory, std::chrono::system_clock::time_point modifiedSince) +{ + ASSERT(!mediaKeysStorageDirectory.isEmpty()); + + for (const auto& mediaKeyDirectory : WebCore::listDirectory(mediaKeysStorageDirectory, "*")) { + auto mediaKeyFile = computeMediaKeyFile(mediaKeyDirectory); + + time_t modificationTime; + if (!WebCore::getFileModificationTime(mediaKeyFile, modificationTime)) + continue; + + if (std::chrono::system_clock::from_time_t(modificationTime) < modifiedSince) + continue; + + WebCore::deleteFile(mediaKeyFile); + WebCore::deleteEmptyDirectory(mediaKeyDirectory); + } +} + +void WebsiteDataStore::removeMediaKeys(const String& mediaKeysStorageDirectory, const HashSet<WebCore::SecurityOriginData>& origins) +{ + ASSERT(!mediaKeysStorageDirectory.isEmpty()); + + for (const auto& origin : origins) { + auto mediaKeyDirectory = WebCore::pathByAppendingComponent(mediaKeysStorageDirectory, origin.databaseIdentifier()); + auto mediaKeyFile = computeMediaKeyFile(mediaKeyDirectory); + + WebCore::deleteFile(mediaKeyFile); + WebCore::deleteEmptyDirectory(mediaKeyDirectory); + } +} + +bool WebsiteDataStore::resourceLoadStatisticsEnabled() const +{ + return m_resourceLoadStatistics ? m_resourceLoadStatistics->resourceLoadStatisticsEnabled() : false; +} + +void WebsiteDataStore::setResourceLoadStatisticsEnabled(bool enabled) +{ + if (!m_resourceLoadStatistics) + return; + + if (enabled == resourceLoadStatisticsEnabled()) + return; + + m_resourceLoadStatistics->setResourceLoadStatisticsEnabled(enabled); + + for (auto& processPool : WebProcessPool::allProcessPools()) { + processPool->setResourceLoadStatisticsEnabled(enabled); + processPool->sendToAllProcesses(Messages::WebProcess::SetResourceLoadStatisticsEnabled(enabled)); + } +} + +void WebsiteDataStore::registerSharedResourceLoadObserver() +{ + if (!m_resourceLoadStatistics) + return; + + m_resourceLoadStatistics->registerSharedResourceLoadObserver(); +} + +} diff --git a/Source/WebKit2/UIProcess/WebsiteData/WebsiteDataStore.h b/Source/WebKit2/UIProcess/WebsiteData/WebsiteDataStore.h new file mode 100644 index 000000000..cba299d0b --- /dev/null +++ b/Source/WebKit2/UIProcess/WebsiteData/WebsiteDataStore.h @@ -0,0 +1,131 @@ +/* + * Copyright (C) 2014-2017 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. + */ + +#pragma once + +#include "WebProcessLifetimeObserver.h" +#include <WebCore/SecurityOriginData.h> +#include <WebCore/SecurityOriginHash.h> +#include <WebCore/SessionID.h> +#include <functional> +#include <wtf/HashSet.h> +#include <wtf/OptionSet.h> +#include <wtf/RefCounted.h> +#include <wtf/RefPtr.h> +#include <wtf/WorkQueue.h> +#include <wtf/text/WTFString.h> + +namespace WebCore { +class SecurityOrigin; +} + +namespace WebKit { + +class StorageManager; +class WebPageProxy; +class WebProcessPool; +class WebResourceLoadStatisticsStore; +enum class WebsiteDataFetchOption; +enum class WebsiteDataType; +struct WebsiteDataRecord; + +#if ENABLE(NETSCAPE_PLUGIN_API) +struct PluginModuleInfo; +#endif + +class WebsiteDataStore : public RefCounted<WebsiteDataStore>, public WebProcessLifetimeObserver { +public: + struct Configuration { + String networkCacheDirectory; + String applicationCacheDirectory; + String applicationCacheFlatFileSubdirectoryName; + + String mediaCacheDirectory; + String webSQLDatabaseDirectory; + String localStorageDirectory; + String mediaKeysStorageDirectory; + String resourceLoadStatisticsDirectory; + }; + static Ref<WebsiteDataStore> createNonPersistent(); + static Ref<WebsiteDataStore> create(Configuration); + virtual ~WebsiteDataStore(); + + uint64_t identifier() const { return m_identifier; } + + bool isPersistent() const { return !m_sessionID.isEphemeral(); } + WebCore::SessionID sessionID() const { return m_sessionID; } + + bool resourceLoadStatisticsEnabled() const; + void setResourceLoadStatisticsEnabled(bool); + void registerSharedResourceLoadObserver(); + + static void cloneSessionData(WebPageProxy& sourcePage, WebPageProxy& newPage); + + void fetchData(OptionSet<WebsiteDataType>, OptionSet<WebsiteDataFetchOption>, std::function<void (Vector<WebsiteDataRecord>)> completionHandler); + void fetchDataForTopPrivatelyOwnedDomains(OptionSet<WebsiteDataType>, OptionSet<WebsiteDataFetchOption>, const Vector<String>& topPrivatelyOwnedDomains, std::function<void(Vector<WebsiteDataRecord>)> completionHandler); + void removeData(OptionSet<WebsiteDataType>, std::chrono::system_clock::time_point modifiedSince, std::function<void ()> completionHandler); + void removeData(OptionSet<WebsiteDataType>, const Vector<WebsiteDataRecord>&, std::function<void ()> completionHandler); + void removeDataForTopPrivatelyOwnedDomains(OptionSet<WebsiteDataType>, OptionSet<WebsiteDataFetchOption>, const Vector<String>& topPrivatelyOwnedDomains, std::function<void()> completionHandler); + + StorageManager* storageManager() { return m_storageManager.get(); } + +private: + explicit WebsiteDataStore(WebCore::SessionID); + explicit WebsiteDataStore(Configuration); + + // WebProcessLifetimeObserver. + void webPageWasAdded(WebPageProxy&) override; + void webPageWasRemoved(WebPageProxy&) override; + void webProcessWillOpenConnection(WebProcessProxy&, IPC::Connection&) override; + void webPageWillOpenConnection(WebPageProxy&, IPC::Connection&) override; + void webPageDidCloseConnection(WebPageProxy&, IPC::Connection&) override; + void webProcessDidCloseConnection(WebProcessProxy&, IPC::Connection&) override; + + void platformInitialize(); + void platformDestroy(); + static void platformRemoveRecentSearches(std::chrono::system_clock::time_point); + + HashSet<RefPtr<WebProcessPool>> processPools() const; + +#if ENABLE(NETSCAPE_PLUGIN_API) + Vector<PluginModuleInfo> plugins() const; +#endif + + static Vector<WebCore::SecurityOriginData> mediaKeyOrigins(const String& mediaKeysStorageDirectory); + static void removeMediaKeys(const String& mediaKeysStorageDirectory, std::chrono::system_clock::time_point modifiedSince); + static void removeMediaKeys(const String& mediaKeysStorageDirectory, const HashSet<WebCore::SecurityOriginData>&); + + const uint64_t m_identifier; + const WebCore::SessionID m_sessionID; + + const Configuration m_configuration; + + const RefPtr<StorageManager> m_storageManager; + const RefPtr<WebResourceLoadStatisticsStore> m_resourceLoadStatistics; + + Ref<WorkQueue> m_queue; +}; + +} diff --git a/Source/WebKit2/UIProcess/WebsiteData/unix/WebsiteDataStoreUnix.cpp b/Source/WebKit2/UIProcess/WebsiteData/unix/WebsiteDataStoreUnix.cpp new file mode 100644 index 000000000..76956fd75 --- /dev/null +++ b/Source/WebKit2/UIProcess/WebsiteData/unix/WebsiteDataStoreUnix.cpp @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2015 University of Szeged. 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 THE COPYRIGHT HOLDERS ``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 THE COPYRIGHT HOLDERS OR + * 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 "WebsiteDataStore.h" + +namespace WebKit { + +void WebsiteDataStore::platformInitialize() +{ +} + +void WebsiteDataStore::platformDestroy() +{ +} + +} // namespace WebKit |