blob: e08988ab685895adcbbe581fe49ef2c751253d2c [file] [log] [blame]
/*
* Copyright (C) 2014-2022 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 "APIHTTPCookieStore.h"
#include "APIProcessPoolConfiguration.h"
#include "APIWebsiteDataRecord.h"
#include "AuthenticatorManager.h"
#include "DeviceIdHashSaltStorage.h"
#include "GPUProcessProxy.h"
#include "Logging.h"
#include "MockAuthenticatorManager.h"
#include "NetworkProcessConnectionInfo.h"
#include "NetworkProcessMessages.h"
#include "ShouldGrandfatherStatistics.h"
#include "StorageAccessStatus.h"
#include "WebBackForwardCache.h"
#include "WebKit2Initialize.h"
#include "WebNotificationManagerProxy.h"
#include "WebPageProxy.h"
#include "WebProcessCache.h"
#include "WebProcessMessages.h"
#include "WebProcessPool.h"
#include "WebResourceLoadStatisticsStore.h"
#include "WebsiteData.h"
#include "WebsiteDataStoreClient.h"
#include "WebsiteDataStoreParameters.h"
#include <WebCore/ApplicationCacheStorage.h>
#include <WebCore/CredentialStorage.h>
#include <WebCore/DatabaseTracker.h>
#include <WebCore/HTMLMediaElement.h>
#include <WebCore/NetworkStorageSession.h>
#include <WebCore/OriginLock.h>
#include <WebCore/RegistrableDomain.h>
#include <WebCore/SecurityOrigin.h>
#include <WebCore/SecurityOriginData.h>
#include <WebCore/StorageQuotaManager.h>
#include <WebCore/WebLockRegistry.h>
#include <wtf/CallbackAggregator.h>
#include <wtf/CompletionHandler.h>
#include <wtf/CrossThreadCopier.h>
#include <wtf/FileSystem.h>
#include <wtf/ProcessPrivilege.h>
#include <wtf/RunLoop.h>
#if OS(DARWIN)
#include <wtf/spi/darwin/OSVariantSPI.h>
#endif
#if HAVE(SEC_KEY_PROXY)
#include "SecKeyProxyStore.h"
#endif
#if HAVE(APP_SSO)
#include "SOAuthorizationCoordinator.h"
#endif
#if PLATFORM(COCOA)
#include "DefaultWebBrowserChecks.h"
#endif
#if ENABLE(WEB_AUTHN)
#include "VirtualAuthenticatorManager.h"
#endif // ENABLE(WEB_AUTHN)
namespace WebKit {
static bool allowsWebsiteDataRecordsForAllOrigins;
void WebsiteDataStore::allowWebsiteDataRecordsForAllOrigins()
{
allowsWebsiteDataRecordsForAllOrigins = true;
}
static HashMap<String, PAL::SessionID>& activeGeneralStorageDirectories()
{
static MainThreadNeverDestroyed<HashMap<String, PAL::SessionID>> directoryToSessionMap;
return directoryToSessionMap;
}
static HashMap<PAL::SessionID, WebsiteDataStore*>& allDataStores()
{
RELEASE_ASSERT(isUIThread());
static NeverDestroyed<HashMap<PAL::SessionID, WebsiteDataStore*>> map;
return map;
}
void WebsiteDataStore::forEachWebsiteDataStore(Function<void(WebsiteDataStore&)>&& function)
{
for (auto* dataStore : allDataStores().values())
function(*dataStore);
}
Ref<WebsiteDataStore> WebsiteDataStore::createNonPersistent()
{
return adoptRef(*new WebsiteDataStore(WebsiteDataStoreConfiguration::create(IsPersistent::No), PAL::SessionID::generateEphemeralSessionID()));
}
Ref<WebsiteDataStore> WebsiteDataStore::create(Ref<WebsiteDataStoreConfiguration>&& configuration, PAL::SessionID sessionID)
{
return adoptRef(*new WebsiteDataStore(WTFMove(configuration), sessionID));
}
WebsiteDataStore::WebsiteDataStore(Ref<WebsiteDataStoreConfiguration>&& configuration, PAL::SessionID sessionID)
: m_sessionID(sessionID)
, m_resolvedConfiguration(WTFMove(configuration))
, m_configuration(m_resolvedConfiguration->copy())
, m_deviceIdHashSaltStorage(DeviceIdHashSaltStorage::create(isPersistent() ? m_configuration->deviceIdHashSaltsStorageDirectory() : String()))
, m_queue(WorkQueue::create("com.apple.WebKit.WebsiteDataStore"))
#if ENABLE(WEB_AUTHN)
, m_authenticatorManager(makeUniqueRef<AuthenticatorManager>())
#endif
, m_client(makeUniqueRef<WebsiteDataStoreClient>())
, m_webLockRegistry(WebCore::LocalWebLockRegistry::create())
{
WTF::setProcessPrivileges(allPrivileges());
registerWithSessionIDMap();
platformInitialize();
ASSERT(RunLoop::isMain());
if (auto directory = m_configuration->generalStorageDirectory(); isPersistent() && !directory.isEmpty()) {
if (!activeGeneralStorageDirectories().add(directory, m_sessionID).isNewEntry)
RELEASE_LOG_FAULT(Storage, "GeneralStorageDirectory for session %" PRIu64 " is already in use by session %" PRIu64, m_sessionID.toUInt64(), activeGeneralStorageDirectories().get(directory).toUInt64());
}
}
WebsiteDataStore::~WebsiteDataStore()
{
ASSERT(RunLoop::isMain());
RELEASE_ASSERT(m_sessionID.isValid());
platformDestroy();
ASSERT(allDataStores().get(m_sessionID) == this);
if (auto generalStorageDirectory = m_configuration->generalStorageDirectory(); isPersistent() && !generalStorageDirectory.isEmpty())
activeGeneralStorageDirectories().remove(generalStorageDirectory);
allDataStores().remove(m_sessionID);
if (m_networkProcess)
m_networkProcess->removeSession(*this);
#if ENABLE(GPU_PROCESS)
if (auto* gpuProcessProxy = GPUProcessProxy::singletonIfCreated())
gpuProcessProxy->removeSession(m_sessionID);
#endif
}
static RefPtr<WebsiteDataStore>& globalDefaultDataStore()
{
static NeverDestroyed<RefPtr<WebsiteDataStore>> globalDefaultDataStore;
return globalDefaultDataStore.get();
}
Ref<WebsiteDataStore> WebsiteDataStore::defaultDataStore()
{
InitializeWebKit2();
auto& store = globalDefaultDataStore();
if (!store)
store = adoptRef(new WebsiteDataStore(WebsiteDataStoreConfiguration::create(IsPersistent::Yes), PAL::SessionID::defaultSessionID()));
return *store;
}
void WebsiteDataStore::deleteDefaultDataStoreForTesting()
{
globalDefaultDataStore() = nullptr;
}
bool WebsiteDataStore::defaultDataStoreExists()
{
return !!globalDefaultDataStore();
}
void WebsiteDataStore::registerWithSessionIDMap()
{
auto result = allDataStores().add(m_sessionID, this);
ASSERT_UNUSED(result, result.isNewEntry);
}
WebsiteDataStore* WebsiteDataStore::existingDataStoreForSessionID(PAL::SessionID sessionID)
{
return allDataStores().get(sessionID);
}
#if HAVE(APP_SSO)
SOAuthorizationCoordinator& WebsiteDataStore::soAuthorizationCoordinator(const WebPageProxy& pageProxy)
{
RELEASE_ASSERT(pageProxy.preferences().isExtensibleSSOEnabled());
if (!m_soAuthorizationCoordinator)
m_soAuthorizationCoordinator = WTF::makeUnique<SOAuthorizationCoordinator>();
return *m_soAuthorizationCoordinator;
}
#endif
static Ref<NetworkProcessProxy> networkProcessForSession(PAL::SessionID sessionID)
{
#if PLATFORM(GTK) || PLATFORM(WPE)
if (sessionID.isEphemeral()) {
// Reuse a previous persistent session network process for ephemeral sessions.
for (auto* dataStore : allDataStores().values()) {
if (dataStore->isPersistent())
return dataStore->networkProcess();
}
}
return NetworkProcessProxy::create();
#else
UNUSED_PARAM(sessionID);
return NetworkProcessProxy::ensureDefaultNetworkProcess();
#endif
}
NetworkProcessProxy& WebsiteDataStore::networkProcess()
{
if (!m_networkProcess) {
m_networkProcess = networkProcessForSession(m_sessionID);
m_networkProcess->addSession(*this, NetworkProcessProxy::SendParametersToNetworkProcess::Yes);
}
return *m_networkProcess;
}
NetworkProcessProxy& WebsiteDataStore::networkProcess() const
{
return const_cast<WebsiteDataStore&>(*this).networkProcess();
}
void WebsiteDataStore::removeNetworkProcessReference()
{
m_networkProcess = nullptr;
}
void WebsiteDataStore::registerProcess(WebProcessProxy& process)
{
ASSERT(process.pageCount() || process.provisionalPageCount());
m_processes.add(process);
}
void WebsiteDataStore::unregisterProcess(WebProcessProxy& process)
{
ASSERT(!process.pageCount());
ASSERT(!process.provisionalPageCount());
m_processes.remove(process);
}
void WebsiteDataStore::resolveDirectoriesIfNecessary()
{
if (m_hasResolvedDirectories)
return;
m_hasResolvedDirectories = true;
// Resolve directory paths.
if (!m_configuration->applicationCacheDirectory().isEmpty())
m_resolvedConfiguration->setApplicationCacheDirectory(resolveAndCreateReadWriteDirectoryForSandboxExtension(m_configuration->applicationCacheDirectory()));
if (!m_configuration->mediaCacheDirectory().isEmpty())
m_resolvedConfiguration->setMediaCacheDirectory(resolveAndCreateReadWriteDirectoryForSandboxExtension(m_configuration->mediaCacheDirectory()));
if (!m_configuration->mediaKeysStorageDirectory().isEmpty())
m_resolvedConfiguration->setMediaKeysStorageDirectory(resolveAndCreateReadWriteDirectoryForSandboxExtension(m_configuration->mediaKeysStorageDirectory()));
if (!m_configuration->indexedDBDatabaseDirectory().isEmpty())
m_resolvedConfiguration->setIndexedDBDatabaseDirectory(resolveAndCreateReadWriteDirectoryForSandboxExtension(m_configuration->indexedDBDatabaseDirectory()));
if (!m_configuration->alternativeServicesDirectory().isEmpty() && WebsiteDataStore::http3Enabled())
m_resolvedConfiguration->setAlternativeServicesDirectory(resolveAndCreateReadWriteDirectoryForSandboxExtension(m_configuration->alternativeServicesDirectory()));
if (!m_configuration->deviceIdHashSaltsStorageDirectory().isEmpty())
m_resolvedConfiguration->setDeviceIdHashSaltsStorageDirectory(resolveAndCreateReadWriteDirectoryForSandboxExtension(m_configuration->deviceIdHashSaltsStorageDirectory()));
if (!m_configuration->networkCacheDirectory().isEmpty())
m_resolvedConfiguration->setNetworkCacheDirectory(resolveAndCreateReadWriteDirectoryForSandboxExtension(m_configuration->networkCacheDirectory()));
if (!m_configuration->resourceLoadStatisticsDirectory().isEmpty())
m_resolvedConfiguration->setResourceLoadStatisticsDirectory(resolveAndCreateReadWriteDirectoryForSandboxExtension(m_configuration->resourceLoadStatisticsDirectory()));
if (!m_configuration->privateClickMeasurementStorageDirectory().isEmpty())
m_resolvedConfiguration->setPrivateClickMeasurementStorageDirectory(resolveAndCreateReadWriteDirectoryForSandboxExtension(m_configuration->privateClickMeasurementStorageDirectory()));
if (!m_configuration->serviceWorkerRegistrationDirectory().isEmpty() && m_resolvedConfiguration->serviceWorkerRegistrationDirectory().isEmpty())
m_resolvedConfiguration->setServiceWorkerRegistrationDirectory(resolveAndCreateReadWriteDirectoryForSandboxExtension(m_configuration->serviceWorkerRegistrationDirectory()));
if (!m_configuration->javaScriptConfigurationDirectory().isEmpty())
m_resolvedConfiguration->setJavaScriptConfigurationDirectory(resolvePathForSandboxExtension(m_configuration->javaScriptConfigurationDirectory()));
if (!m_configuration->cacheStorageDirectory().isEmpty() && m_resolvedConfiguration->cacheStorageDirectory().isEmpty())
m_resolvedConfiguration->setCacheStorageDirectory(resolvePathForSandboxExtension(m_configuration->cacheStorageDirectory()));
if (!m_configuration->hstsStorageDirectory().isEmpty() && m_resolvedConfiguration->hstsStorageDirectory().isEmpty())
m_resolvedConfiguration->setHSTSStorageDirectory(resolvePathForSandboxExtension(m_configuration->hstsStorageDirectory()));
if (!m_configuration->generalStorageDirectory().isEmpty())
m_resolvedConfiguration->setGeneralStorageDirectory(resolveAndCreateReadWriteDirectoryForSandboxExtension(m_configuration->generalStorageDirectory()));
#if ENABLE(ARKIT_INLINE_PREVIEW)
if (!m_configuration->modelElementCacheDirectory().isEmpty())
m_resolvedConfiguration->setModelElementCacheDirectory(resolveAndCreateReadWriteDirectoryForSandboxExtension(m_configuration->modelElementCacheDirectory()));
#endif
// Resolve directories for file paths.
if (!m_configuration->cookieStorageFile().isEmpty()) {
m_resolvedConfiguration->setCookieStorageFile(resolveAndCreateReadWriteDirectoryForSandboxExtension(FileSystem::parentPath(m_configuration->cookieStorageFile())));
m_resolvedConfiguration->setCookieStorageFile(FileSystem::pathByAppendingComponent(m_resolvedConfiguration->cookieStorageFile(), FileSystem::pathFileName(m_configuration->cookieStorageFile())));
}
}
enum class ProcessAccessType : uint8_t { None, OnlyIfLaunched, Launch };
static ProcessAccessType computeNetworkProcessAccessTypeForDataFetch(OptionSet<WebsiteDataType> dataTypes, bool isNonPersistentStore)
{
for (auto dataType : dataTypes) {
if (WebsiteData::ownerProcess(dataType) == WebsiteDataProcessType::Network)
return isNonPersistentStore ? ProcessAccessType::OnlyIfLaunched : ProcessAccessType::Launch;
}
return ProcessAccessType::None;
}
static ProcessAccessType computeWebProcessAccessTypeForDataFetch(OptionSet<WebsiteDataType> dataTypes, bool /* isNonPersistentStore */)
{
if (dataTypes.contains(WebsiteDataType::MemoryCache))
return ProcessAccessType::OnlyIfLaunched;
return ProcessAccessType::None;
}
void WebsiteDataStore::fetchData(OptionSet<WebsiteDataType> dataTypes, OptionSet<WebsiteDataFetchOption> fetchOptions, Function<void(Vector<WebsiteDataRecord>)>&& completionHandler)
{
fetchDataAndApply(dataTypes, fetchOptions, WorkQueue::main(), WTFMove(completionHandler));
}
void WebsiteDataStore::fetchDataAndApply(OptionSet<WebsiteDataType> dataTypes, OptionSet<WebsiteDataFetchOption> fetchOptions, Ref<WorkQueue>&& queue, Function<void(Vector<WebsiteDataRecord>)>&& apply)
{
RELEASE_LOG(Storage, "WebsiteDataStore::fetchDataAndApply started to fetch data for session %" PRIu64, m_sessionID.toUInt64());
class CallbackAggregator final : public ThreadSafeRefCounted<CallbackAggregator, WTF::DestructionThread::MainRunLoop> {
public:
static Ref<CallbackAggregator> create(OptionSet<WebsiteDataFetchOption> fetchOptions, Ref<WorkQueue>&& queue, Function<void(Vector<WebsiteDataRecord>)>&& apply, WebsiteDataStore& dataStore)
{
return adoptRef(*new CallbackAggregator(fetchOptions, WTFMove(queue), WTFMove(apply), dataStore));
}
~CallbackAggregator()
{
ASSERT(RunLoop::isMain());
auto records = WTF::map(WTFMove(m_websiteDataRecords), [this](auto&& entry) {
return m_queue.ptr() != &WorkQueue::main() ? crossThreadCopy(WTFMove(entry.value)) : WTFMove(entry.value);
});
m_queue->dispatch([apply = WTFMove(m_apply), records = WTFMove(records), sessionID = m_protectedDataStore->sessionID()] () mutable {
apply(WTFMove(records));
RELEASE_LOG(Storage, "WebsiteDataStore::fetchDataAndApply finished fetching data for session %" PRIu64, sessionID.toUInt64());
});
}
const OptionSet<WebsiteDataFetchOption>& fetchOptions() const { return m_fetchOptions; }
void addWebsiteData(WebsiteData&& websiteData)
{
if (!RunLoop::isMain()) {
RunLoop::main().dispatch([protectedThis = Ref { *this }, websiteData = crossThreadCopy(websiteData)]() mutable {
protectedThis->addWebsiteData(WTFMove(websiteData));
});
return;
}
ASSERT(RunLoop::isMain());
for (auto& entry : websiteData.entries) {
auto displayName = WebsiteDataRecord::displayNameForOrigin(entry.origin);
if (!displayName) {
if (!allowsWebsiteDataRecordsForAllOrigins)
continue;
String hostString = entry.origin.host.isEmpty() ? emptyString() : makeString(" ", entry.origin.host);
displayName = makeString(entry.origin.protocol, hostString);
}
auto& record = m_websiteDataRecords.add(displayName, WebsiteDataRecord { }).iterator->value;
if (!record.displayName)
record.displayName = WTFMove(displayName);
record.add(entry.type, entry.origin);
if (m_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);
}
for (auto& hostName : websiteData.hostNamesWithHSTSCache) {
auto displayName = WebsiteDataRecord::displayNameForHostName(hostName);
if (!displayName)
continue;
auto& record = m_websiteDataRecords.add(displayName, WebsiteDataRecord { }).iterator->value;
if (!record.displayName)
record.displayName = WTFMove(displayName);
record.addHSTSCacheHostname(hostName);
}
#if ENABLE(INTELLIGENT_TRACKING_PREVENTION)
for (const auto& domain : websiteData.registrableDomainsWithResourceLoadStatistics) {
auto displayName = WebsiteDataRecord::displayNameForHostName(domain.string());
if (!displayName)
continue;
auto& record = m_websiteDataRecords.add(displayName, WebsiteDataRecord { }).iterator->value;
if (!record.displayName)
record.displayName = WTFMove(displayName);
record.addResourceLoadStatisticsRegistrableDomain(domain);
}
#endif
}
private:
CallbackAggregator(OptionSet<WebsiteDataFetchOption> fetchOptions, Ref<WorkQueue>&& queue, Function<void(Vector<WebsiteDataRecord>)>&& apply, WebsiteDataStore& dataStore)
: m_fetchOptions(fetchOptions)
, m_queue(WTFMove(queue))
, m_apply(WTFMove(apply))
, m_protectedDataStore(dataStore)
{
ASSERT(RunLoop::isMain());
}
const OptionSet<WebsiteDataFetchOption> m_fetchOptions;
Ref<WorkQueue> m_queue;
Function<void(Vector<WebsiteDataRecord>)> m_apply;
HashMap<String, WebsiteDataRecord> m_websiteDataRecords;
Ref<WebsiteDataStore> m_protectedDataStore;
};
auto callbackAggregator = CallbackAggregator::create(fetchOptions, WTFMove(queue), WTFMove(apply), *this);
#if ENABLE(VIDEO)
if (dataTypes.contains(WebsiteDataType::DiskCache)) {
m_queue->dispatch([mediaCacheDirectory = m_configuration->mediaCacheDirectory().isolatedCopy(), callbackAggregator] {
WebsiteData websiteData;
auto origins = WebCore::HTMLMediaElement::originsInMediaCache(mediaCacheDirectory);
websiteData.entries = WTF::map(origins, [](auto& origin) {
return WebsiteData::Entry { origin, WebsiteDataType::DiskCache, 0 };
});
callbackAggregator->addWebsiteData(WTFMove(websiteData));
});
}
#endif
auto networkProcessAccessType = computeNetworkProcessAccessTypeForDataFetch(dataTypes, !isPersistent());
switch (networkProcessAccessType) {
case ProcessAccessType::Launch:
networkProcess();
ASSERT(m_networkProcess);
FALLTHROUGH;
case ProcessAccessType::OnlyIfLaunched:
if (m_networkProcess) {
m_networkProcess->fetchWebsiteData(m_sessionID, dataTypes, fetchOptions, [callbackAggregator](WebsiteData websiteData) {
callbackAggregator->addWebsiteData(WTFMove(websiteData));
});
}
break;
case ProcessAccessType::None:
break;
}
auto webProcessAccessType = computeWebProcessAccessTypeForDataFetch(dataTypes, !isPersistent());
if (webProcessAccessType != ProcessAccessType::None) {
for (auto& process : processes()) {
if (webProcessAccessType == ProcessAccessType::OnlyIfLaunched && process.state() != WebProcessProxy::State::Running)
continue;
process.fetchWebsiteData(m_sessionID, dataTypes, [callbackAggregator](WebsiteData websiteData) {
callbackAggregator->addWebsiteData(WTFMove(websiteData));
});
}
}
if (dataTypes.contains(WebsiteDataType::DeviceIdHashSalt)) {
m_deviceIdHashSaltStorage->getDeviceIdHashSaltOrigins([callbackAggregator](auto&& origins) {
WebsiteData websiteData;
websiteData.entries = WTF::map(origins, [](auto& origin) {
return WebsiteData::Entry { origin, WebsiteDataType::DeviceIdHashSalt, 0 };
});
callbackAggregator->addWebsiteData(WTFMove(websiteData));
});
}
if (dataTypes.contains(WebsiteDataType::OfflineWebApplicationCache) && isPersistent()) {
m_queue->dispatch([fetchOptions, applicationCacheDirectory = m_configuration->applicationCacheDirectory().isolatedCopy(), applicationCacheFlatFileSubdirectoryName = m_configuration->applicationCacheFlatFileSubdirectoryName().isolatedCopy(), callbackAggregator] {
auto storage = WebCore::ApplicationCacheStorage::create(applicationCacheDirectory, applicationCacheFlatFileSubdirectoryName);
WebsiteData websiteData;
auto origins = storage->originsWithCache();
websiteData.entries = WTF::map(origins, [&](auto& origin) {
uint64_t size = fetchOptions.contains(WebsiteDataFetchOption::ComputeSizes) ? storage->diskUsageForOrigin(origin) : 0;
return WebsiteData::Entry { origin, WebsiteDataType::OfflineWebApplicationCache, size };
});
callbackAggregator->addWebsiteData(WTFMove(websiteData));
});
}
if (dataTypes.contains(WebsiteDataType::MediaKeys) && isPersistent()) {
m_queue->dispatch([mediaKeysStorageDirectory = m_configuration->mediaKeysStorageDirectory().isolatedCopy(), callbackAggregator] {
WebsiteData websiteData;
websiteData.entries = mediaKeyOrigins(mediaKeysStorageDirectory).map([](auto& origin) {
return WebsiteData::Entry { origin, WebsiteDataType::MediaKeys, 0 };
});
callbackAggregator->addWebsiteData(WTFMove(websiteData));
});
}
}
#if ENABLE(INTELLIGENT_TRACKING_PREVENTION)
void WebsiteDataStore::fetchDataForRegistrableDomains(OptionSet<WebsiteDataType> dataTypes, OptionSet<WebsiteDataFetchOption> fetchOptions, Vector<WebCore::RegistrableDomain>&& domains, CompletionHandler<void(Vector<WebsiteDataRecord>&&, HashSet<WebCore::RegistrableDomain>&&)>&& completionHandler)
{
fetchDataAndApply(dataTypes, fetchOptions, m_queue.copyRef(), [domains = crossThreadCopy(domains), completionHandler = WTFMove(completionHandler)] (auto&& existingDataRecords) mutable {
ASSERT(!RunLoop::isMain());
Vector<WebsiteDataRecord> matchingDataRecords;
HashSet<WebCore::RegistrableDomain> domainsWithMatchingDataRecords;
for (auto&& dataRecord : existingDataRecords) {
for (auto& domain : domains) {
if (dataRecord.matches(domain)) {
matchingDataRecords.append(WTFMove(dataRecord));
domainsWithMatchingDataRecords.add(domain.isolatedCopy());
break;
}
}
}
RunLoop::main().dispatch([completionHandler = WTFMove(completionHandler), matchingDataRecords = WTFMove(matchingDataRecords), domainsWithMatchingDataRecords = WTFMove(domainsWithMatchingDataRecords)] () mutable {
completionHandler(WTFMove(matchingDataRecords), WTFMove(domainsWithMatchingDataRecords));
});
});
}
#endif
static ProcessAccessType computeNetworkProcessAccessTypeForDataRemoval(OptionSet<WebsiteDataType> dataTypes, bool isNonPersistentStore)
{
ProcessAccessType processAccessType = ProcessAccessType::None;
for (auto dataType : dataTypes) {
if (dataTypes.contains(WebsiteDataType::MemoryCache))
processAccessType = ProcessAccessType::OnlyIfLaunched;
if (WebsiteData::ownerProcess(dataType) != WebsiteDataProcessType::Network)
continue;
if (dataType != WebsiteDataType::Cookies || !isNonPersistentStore)
return ProcessAccessType::Launch;
processAccessType = ProcessAccessType::OnlyIfLaunched;
}
return processAccessType;
}
static ProcessAccessType computeWebProcessAccessTypeForDataRemoval(OptionSet<WebsiteDataType> dataTypes, bool /* isNonPersistentStore */)
{
if (dataTypes.contains(WebsiteDataType::MemoryCache))
return ProcessAccessType::OnlyIfLaunched;
return ProcessAccessType::None;
}
class RemovalCallbackAggregator : public ThreadSafeRefCounted<RemovalCallbackAggregator, WTF::DestructionThread::MainRunLoop> {
public:
static Ref<RemovalCallbackAggregator> create(WebsiteDataStore& dataStore, CompletionHandler<void()>&& completionHandler)
{
return adoptRef(*new RemovalCallbackAggregator(dataStore, WTFMove(completionHandler)));
}
~RemovalCallbackAggregator()
{
ASSERT(RunLoop::isMain());
RunLoop::main().dispatch(WTFMove(m_completionHandler));
}
private:
RemovalCallbackAggregator(WebsiteDataStore& dataStore, CompletionHandler<void()>&& completionHandler)
: m_protectedDataStore(dataStore)
, m_completionHandler(WTFMove(completionHandler))
{
ASSERT(RunLoop::isMain());
}
Ref<WebsiteDataStore> m_protectedDataStore;
CompletionHandler<void()> m_completionHandler;
};
void WebsiteDataStore::removeData(OptionSet<WebsiteDataType> dataTypes, WallTime modifiedSince, Function<void()>&& completionHandler)
{
RELEASE_LOG(Storage, "WebsiteDataStore::removeData started to delete data modified since %f for session %" PRIu64, modifiedSince.secondsSinceEpoch().value(), m_sessionID.toUInt64());
auto callbackAggregator = RemovalCallbackAggregator::create(*this, [sessionID = m_sessionID, completionHandler = WTFMove(completionHandler)] {
RELEASE_LOG(Storage, "WebsiteDataStore::removeData finished deleting modified data for session %" PRIu64, sessionID.toUInt64());
completionHandler();
});
#if ENABLE(VIDEO)
if (dataTypes.contains(WebsiteDataType::DiskCache)) {
m_queue->dispatch([modifiedSince, mediaCacheDirectory = m_configuration->mediaCacheDirectory().isolatedCopy(), callbackAggregator] {
WebCore::HTMLMediaElement::clearMediaCache(mediaCacheDirectory, modifiedSince);
});
}
#endif
#if ENABLE(INTELLIGENT_TRACKING_PREVENTION)
bool didNotifyNetworkProcessToDeleteWebsiteData = false;
#endif
auto networkProcessAccessType = computeNetworkProcessAccessTypeForDataRemoval(dataTypes, !isPersistent());
switch (networkProcessAccessType) {
case ProcessAccessType::Launch:
networkProcess();
ASSERT(m_networkProcess);
FALLTHROUGH;
case ProcessAccessType::OnlyIfLaunched:
if (m_networkProcess) {
m_networkProcess->deleteWebsiteData(m_sessionID, dataTypes, modifiedSince, [callbackAggregator] { });
#if ENABLE(INTELLIGENT_TRACKING_PREVENTION)
didNotifyNetworkProcessToDeleteWebsiteData = true;
#endif
}
break;
case ProcessAccessType::None:
break;
}
auto webProcessAccessType = computeWebProcessAccessTypeForDataRemoval(dataTypes, !isPersistent());
if (webProcessAccessType != ProcessAccessType::None) {
for (auto& processPool : processPools()) {
// Clear back/forward cache first as processes removed from the back/forward cache will likely
// be added to the WebProcess cache.
processPool->backForwardCache().removeEntriesForSession(sessionID());
processPool->webProcessCache().clearAllProcessesForSession(sessionID());
}
for (auto& process : processes()) {
if (webProcessAccessType == ProcessAccessType::OnlyIfLaunched && process.state() != WebProcessProxy::State::Running)
continue;
process.deleteWebsiteData(m_sessionID, dataTypes, modifiedSince, [callbackAggregator] { });
}
}
if (dataTypes.contains(WebsiteDataType::DeviceIdHashSalt) || (dataTypes.contains(WebsiteDataType::Cookies)))
m_deviceIdHashSaltStorage->deleteDeviceIdHashSaltOriginsModifiedSince(modifiedSince, [callbackAggregator] { });
if (dataTypes.contains(WebsiteDataType::OfflineWebApplicationCache) && isPersistent()) {
m_queue->dispatch([applicationCacheDirectory = m_configuration->applicationCacheDirectory().isolatedCopy(), applicationCacheFlatFileSubdirectoryName = m_configuration->applicationCacheFlatFileSubdirectoryName().isolatedCopy(), callbackAggregator] {
auto storage = WebCore::ApplicationCacheStorage::create(applicationCacheDirectory, applicationCacheFlatFileSubdirectoryName);
storage->deleteAllCaches();
});
}
if (dataTypes.contains(WebsiteDataType::MediaKeys) && isPersistent()) {
m_queue->dispatch([mediaKeysStorageDirectory = m_configuration->mediaKeysStorageDirectory().isolatedCopy(), callbackAggregator, modifiedSince] {
removeMediaKeys(mediaKeysStorageDirectory, modifiedSince);
});
}
if (dataTypes.contains(WebsiteDataType::SearchFieldRecentSearches) && isPersistent()) {
m_queue->dispatch([modifiedSince, callbackAggregator] {
platformRemoveRecentSearches(modifiedSince);
});
}
#if ENABLE(INTELLIGENT_TRACKING_PREVENTION)
if (dataTypes.contains(WebsiteDataType::ResourceLoadStatistics)) {
if (!didNotifyNetworkProcessToDeleteWebsiteData)
networkProcess().deleteWebsiteData(m_sessionID, dataTypes, modifiedSince, [callbackAggregator] { });
clearResourceLoadStatisticsInWebProcesses([callbackAggregator] { });
}
#endif
}
void WebsiteDataStore::removeData(OptionSet<WebsiteDataType> dataTypes, const Vector<WebsiteDataRecord>& dataRecords, Function<void()>&& completionHandler)
{
RELEASE_LOG(Storage, "WebsiteDataStore::removeData started to delete data for session %" PRIu64, m_sessionID.toUInt64());
auto callbackAggregator = RemovalCallbackAggregator::create(*this, [sessionID = m_sessionID, completionHandler = WTFMove(completionHandler)] {
RELEASE_LOG(Storage, "WebsiteDataStore::removeData finished deleting data for session %" PRIu64, sessionID.toUInt64());
completionHandler();
});
Vector<WebCore::SecurityOriginData> origins;
for (const auto& dataRecord : dataRecords) {
for (auto& origin : dataRecord.origins)
origins.append(origin);
}
if (dataTypes.contains(WebsiteDataType::DiskCache)) {
HashSet<WebCore::SecurityOriginData> origins;
for (const auto& dataRecord : dataRecords) {
for (const auto& origin : dataRecord.origins)
origins.add(crossThreadCopy(origin));
}
#if ENABLE(VIDEO)
m_queue->dispatch([origins = WTFMove(origins), mediaCacheDirectory = m_configuration->mediaCacheDirectory().isolatedCopy(), callbackAggregator] {
WebCore::HTMLMediaElement::clearMediaCacheForOrigins(mediaCacheDirectory, origins);
});
#endif
}
auto networkProcessAccessType = computeNetworkProcessAccessTypeForDataRemoval(dataTypes, !isPersistent());
if (networkProcessAccessType != ProcessAccessType::None) {
auto pools = networkProcessAccessType == ProcessAccessType::Launch ? ensureProcessPools() : processPools();
for (auto& processPool : pools) {
switch (networkProcessAccessType) {
case ProcessAccessType::OnlyIfLaunched:
break;
case ProcessAccessType::Launch:
networkProcess();
break;
case ProcessAccessType::None:
ASSERT_NOT_REACHED();
}
Vector<String> cookieHostNames;
Vector<String> HSTSCacheHostNames;
Vector<WebCore::RegistrableDomain> registrableDomains;
for (const auto& dataRecord : dataRecords) {
for (auto& hostName : dataRecord.cookieHostNames)
cookieHostNames.append(hostName);
for (auto& hostName : dataRecord.HSTSCacheHostNames)
HSTSCacheHostNames.append(hostName);
#if ENABLE(INTELLIGENT_TRACKING_PREVENTION)
registrableDomains.reserveCapacity(registrableDomains.size() + dataRecord.resourceLoadStatisticsRegistrableDomains.size());
for (auto& registrableDomain : dataRecord.resourceLoadStatisticsRegistrableDomains)
registrableDomains.uncheckedAppend(registrableDomain);
#endif
}
networkProcess().deleteWebsiteDataForOrigins(m_sessionID, dataTypes, origins, cookieHostNames, HSTSCacheHostNames, registrableDomains, [callbackAggregator, processPool] { });
}
}
auto webProcessAccessType = computeWebProcessAccessTypeForDataRemoval(dataTypes, !isPersistent());
if (webProcessAccessType != ProcessAccessType::None) {
for (auto& process : processes()) {
if (webProcessAccessType == ProcessAccessType::OnlyIfLaunched && process.state() != WebProcessProxy::State::Running)
continue;
process.deleteWebsiteDataForOrigins(m_sessionID, dataTypes, origins, [callbackAggregator] { });
}
}
if (dataTypes.contains(WebsiteDataType::DeviceIdHashSalt) || (dataTypes.contains(WebsiteDataType::Cookies))) {
m_deviceIdHashSaltStorage->deleteDeviceIdHashSaltForOrigins(origins, [callbackAggregator] { });
}
if (dataTypes.contains(WebsiteDataType::OfflineWebApplicationCache) && isPersistent()) {
HashSet<WebCore::SecurityOriginData> origins;
for (const auto& dataRecord : dataRecords) {
for (const auto& origin : dataRecord.origins)
origins.add(crossThreadCopy(origin));
}
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);
});
}
if (dataTypes.contains(WebsiteDataType::MediaKeys) && isPersistent()) {
HashSet<WebCore::SecurityOriginData> origins;
for (const auto& dataRecord : dataRecords) {
for (const auto& origin : dataRecord.origins)
origins.add(crossThreadCopy(origin));
}
m_queue->dispatch([mediaKeysStorageDirectory = m_configuration->mediaKeysStorageDirectory().isolatedCopy(), callbackAggregator, origins = WTFMove(origins)] {
removeMediaKeys(mediaKeysStorageDirectory, origins);
});
}
}
void WebsiteDataStore::setServiceWorkerTimeoutForTesting(Seconds seconds)
{
networkProcess().sendSync(Messages::NetworkProcess::SetServiceWorkerFetchTimeoutForTesting(seconds), Messages::NetworkProcess::SetServiceWorkerFetchTimeoutForTesting::Reply(), 0);
}
void WebsiteDataStore::resetServiceWorkerTimeoutForTesting()
{
networkProcess().sendSync(Messages::NetworkProcess::ResetServiceWorkerFetchTimeoutForTesting(), Messages::NetworkProcess::ResetServiceWorkerFetchTimeoutForTesting::Reply(), 0);
}
bool WebsiteDataStore::hasServiceWorkerBackgroundActivityForTesting() const
{
#if ENABLE(SERVICE_WORKER)
return WTF::anyOf(WebProcessPool::allProcessPools(), [](auto& pool) { return pool->hasServiceWorkerBackgroundActivityForTesting(); });
#else
return false;
#endif
}
#if ENABLE(INTELLIGENT_TRACKING_PREVENTION)
void WebsiteDataStore::setMaxStatisticsEntries(size_t maximumEntryCount, CompletionHandler<void()>&& completionHandler)
{
ASSERT(RunLoop::isMain());
auto callbackAggregator = CallbackAggregator::create(WTFMove(completionHandler));
networkProcess().setMaxStatisticsEntries(m_sessionID, maximumEntryCount, [callbackAggregator] { });
}
void WebsiteDataStore::setPruneEntriesDownTo(size_t pruneTargetCount, CompletionHandler<void()>&& completionHandler)
{
ASSERT(RunLoop::isMain());
auto callbackAggregator = CallbackAggregator::create(WTFMove(completionHandler));
networkProcess().setPruneEntriesDownTo(m_sessionID, pruneTargetCount, [callbackAggregator] { });
}
void WebsiteDataStore::setGrandfatheringTime(Seconds seconds, CompletionHandler<void()>&& completionHandler)
{
ASSERT(RunLoop::isMain());
auto callbackAggregator = CallbackAggregator::create(WTFMove(completionHandler));
networkProcess().setGrandfatheringTime(m_sessionID, seconds, [callbackAggregator] { });
}
void WebsiteDataStore::setMinimumTimeBetweenDataRecordsRemoval(Seconds seconds, CompletionHandler<void()>&& completionHandler)
{
ASSERT(RunLoop::isMain());
auto callbackAggregator = CallbackAggregator::create(WTFMove(completionHandler));
networkProcess().setMinimumTimeBetweenDataRecordsRemoval(m_sessionID, seconds, [callbackAggregator] { });
}
void WebsiteDataStore::dumpResourceLoadStatistics(CompletionHandler<void(const String&)>&& completionHandler)
{
ASSERT(RunLoop::isMain());
networkProcess().dumpResourceLoadStatistics(m_sessionID, WTFMove(completionHandler));
}
void WebsiteDataStore::isPrevalentResource(const URL& url, CompletionHandler<void(bool isPrevalent)>&& completionHandler)
{
ASSERT(RunLoop::isMain());
if (url.protocolIsAbout() || url.isEmpty()) {
completionHandler(false);
return;
}
networkProcess().isPrevalentResource(m_sessionID, WebCore::RegistrableDomain { url }, WTFMove(completionHandler));
}
void WebsiteDataStore::isGrandfathered(const URL& url, CompletionHandler<void(bool isPrevalent)>&& completionHandler)
{
ASSERT(RunLoop::isMain());
if (url.protocolIsAbout() || url.isEmpty()) {
completionHandler(false);
return;
}
networkProcess().isGrandfathered(m_sessionID, WebCore::RegistrableDomain { url }, WTFMove(completionHandler));
}
void WebsiteDataStore::setPrevalentResource(const URL& url, CompletionHandler<void()>&& completionHandler)
{
ASSERT(RunLoop::isMain());
if (url.protocolIsAbout() || url.isEmpty()) {
completionHandler();
return;
}
auto callbackAggregator = CallbackAggregator::create(WTFMove(completionHandler));
networkProcess().setPrevalentResource(m_sessionID, WebCore::RegistrableDomain { url }, [callbackAggregator] { });
}
void WebsiteDataStore::setPrevalentResourceForDebugMode(const URL& url, CompletionHandler<void()>&& completionHandler)
{
ASSERT(RunLoop::isMain());
if (url.protocolIsAbout() || url.isEmpty()) {
completionHandler();
return;
}
auto callbackAggregator = CallbackAggregator::create(WTFMove(completionHandler));
networkProcess().setPrevalentResourceForDebugMode(m_sessionID, WebCore::RegistrableDomain { url }, [callbackAggregator] { });
}
void WebsiteDataStore::isVeryPrevalentResource(const URL& url, CompletionHandler<void(bool isVeryPrevalent)>&& completionHandler)
{
ASSERT(RunLoop::isMain());
if (url.protocolIsAbout() || url.isEmpty()) {
completionHandler(false);
return;
}
networkProcess().isVeryPrevalentResource(m_sessionID, WebCore::RegistrableDomain { url }, WTFMove(completionHandler));
}
void WebsiteDataStore::setVeryPrevalentResource(const URL& url, CompletionHandler<void()>&& completionHandler)
{
ASSERT(RunLoop::isMain());
if (url.protocolIsAbout() || url.isEmpty()) {
completionHandler();
return;
}
auto callbackAggregator = CallbackAggregator::create(WTFMove(completionHandler));
networkProcess().setVeryPrevalentResource(m_sessionID, WebCore::RegistrableDomain { url }, [callbackAggregator] { });
}
void WebsiteDataStore::setShouldClassifyResourcesBeforeDataRecordsRemoval(bool value, CompletionHandler<void()>&& completionHandler)
{
ASSERT(RunLoop::isMain());
auto callbackAggregator = CallbackAggregator::create(WTFMove(completionHandler));
networkProcess().setShouldClassifyResourcesBeforeDataRecordsRemoval(m_sessionID, value, [callbackAggregator] { });
}
void WebsiteDataStore::setSubframeUnderTopFrameDomain(const URL& subFrameURL, const URL& topFrameURL, CompletionHandler<void()>&& completionHandler)
{
ASSERT(RunLoop::isMain());
if (subFrameURL.protocolIsAbout() || subFrameURL.isEmpty() || topFrameURL.protocolIsAbout() || topFrameURL.isEmpty()) {
completionHandler();
return;
}
auto callbackAggregator = CallbackAggregator::create(WTFMove(completionHandler));
networkProcess().setSubframeUnderTopFrameDomain(m_sessionID, WebCore::RegistrableDomain { subFrameURL }, WebCore::RegistrableDomain { topFrameURL }, [callbackAggregator] { });
}
void WebsiteDataStore::isRegisteredAsSubFrameUnder(const URL& subFrameURL, const URL& topFrameURL, CompletionHandler<void(bool)>&& completionHandler)
{
ASSERT(RunLoop::isMain());
networkProcess().isRegisteredAsSubFrameUnder(m_sessionID, WebCore::RegistrableDomain { subFrameURL }, WebCore::RegistrableDomain { topFrameURL }, WTFMove(completionHandler));
}
void WebsiteDataStore::setSubresourceUnderTopFrameDomain(const URL& subresourceURL, const URL& topFrameURL, CompletionHandler<void()>&& completionHandler)
{
ASSERT(RunLoop::isMain());
if (subresourceURL.protocolIsAbout() || subresourceURL.isEmpty() || topFrameURL.protocolIsAbout() || topFrameURL.isEmpty()) {
completionHandler();
return;
}
auto callbackAggregator = CallbackAggregator::create(WTFMove(completionHandler));
networkProcess().setSubresourceUnderTopFrameDomain(m_sessionID, WebCore::RegistrableDomain { subresourceURL }, WebCore::RegistrableDomain { topFrameURL }, [callbackAggregator] { });
}
void WebsiteDataStore::isRegisteredAsSubresourceUnder(const URL& subresourceURL, const URL& topFrameURL, CompletionHandler<void(bool)>&& completionHandler)
{
ASSERT(RunLoop::isMain());
networkProcess().isRegisteredAsSubresourceUnder(m_sessionID, WebCore::RegistrableDomain { subresourceURL }, WebCore::RegistrableDomain { topFrameURL }, WTFMove(completionHandler));
}
void WebsiteDataStore::setSubresourceUniqueRedirectTo(const URL& subresourceURL, const URL& urlRedirectedTo, CompletionHandler<void()>&& completionHandler)
{
ASSERT(RunLoop::isMain());
if (subresourceURL.protocolIsAbout() || subresourceURL.isEmpty() || urlRedirectedTo.protocolIsAbout() || urlRedirectedTo.isEmpty()) {
completionHandler();
return;
}
auto callbackAggregator = CallbackAggregator::create(WTFMove(completionHandler));
networkProcess().setSubresourceUniqueRedirectTo(m_sessionID, WebCore::RegistrableDomain { subresourceURL }, WebCore::RegistrableDomain { urlRedirectedTo }, [callbackAggregator] { });
}
void WebsiteDataStore::setSubresourceUniqueRedirectFrom(const URL& subresourceURL, const URL& urlRedirectedFrom, CompletionHandler<void()>&& completionHandler)
{
ASSERT(RunLoop::isMain());
if (subresourceURL.protocolIsAbout() || subresourceURL.isEmpty() || urlRedirectedFrom.protocolIsAbout() || urlRedirectedFrom.isEmpty()) {
completionHandler();
return;
}
networkProcess().setSubresourceUniqueRedirectFrom(m_sessionID, WebCore::RegistrableDomain { subresourceURL }, WebCore::RegistrableDomain { urlRedirectedFrom }, WTFMove(completionHandler));
}
void WebsiteDataStore::setTopFrameUniqueRedirectTo(const URL& topFrameURL, const URL& urlRedirectedTo, CompletionHandler<void()>&& completionHandler)
{
ASSERT(RunLoop::isMain());
if (topFrameURL.protocolIsAbout() || topFrameURL.isEmpty() || urlRedirectedTo.protocolIsAbout() || urlRedirectedTo.isEmpty()) {
completionHandler();
return;
}
networkProcess().setTopFrameUniqueRedirectTo(m_sessionID, WebCore::RegistrableDomain { topFrameURL }, WebCore::RegistrableDomain { urlRedirectedTo }, WTFMove(completionHandler));
}
void WebsiteDataStore::setTopFrameUniqueRedirectFrom(const URL& topFrameURL, const URL& urlRedirectedFrom, CompletionHandler<void()>&& completionHandler)
{
ASSERT(RunLoop::isMain());
if (topFrameURL.protocolIsAbout() || topFrameURL.isEmpty() || urlRedirectedFrom.protocolIsAbout() || urlRedirectedFrom.isEmpty()) {
completionHandler();
return;
}
networkProcess().setTopFrameUniqueRedirectFrom(m_sessionID, WebCore::RegistrableDomain { topFrameURL }, WebCore::RegistrableDomain { urlRedirectedFrom }, WTFMove(completionHandler));
}
void WebsiteDataStore::isRegisteredAsRedirectingTo(const URL& urlRedirectedFrom, const URL& urlRedirectedTo, CompletionHandler<void(bool)>&& completionHandler)
{
ASSERT(RunLoop::isMain());
networkProcess().isRegisteredAsRedirectingTo(m_sessionID, WebCore::RegistrableDomain { urlRedirectedFrom }, WebCore::RegistrableDomain { urlRedirectedTo }, WTFMove(completionHandler));
}
void WebsiteDataStore::clearPrevalentResource(const URL& url, CompletionHandler<void()>&& completionHandler)
{
ASSERT(RunLoop::isMain());
if (url.protocolIsAbout() || url.isEmpty()) {
completionHandler();
return;
}
networkProcess().clearPrevalentResource(m_sessionID, WebCore::RegistrableDomain { url }, WTFMove(completionHandler));
}
void WebsiteDataStore::resetParametersToDefaultValues(CompletionHandler<void()>&& completionHandler)
{
ASSERT(RunLoop::isMain());
networkProcess().resetParametersToDefaultValues(m_sessionID, WTFMove(completionHandler));
}
void WebsiteDataStore::scheduleClearInMemoryAndPersistent(WallTime modifiedSince, ShouldGrandfatherStatistics shouldGrandfather, CompletionHandler<void()>&& completionHandler)
{
ASSERT(RunLoop::isMain());
networkProcess().scheduleClearInMemoryAndPersistent(m_sessionID, modifiedSince, shouldGrandfather, WTFMove(completionHandler));
}
void WebsiteDataStore::getResourceLoadStatisticsDataSummary(CompletionHandler<void(Vector<WebResourceLoadStatisticsStore::ThirdPartyData>&&)>&& completionHandler)
{
ASSERT(RunLoop::isMain());
struct LocalCallbackAggregator : RefCounted<LocalCallbackAggregator> {
LocalCallbackAggregator(CompletionHandler<void(Vector<WebResourceLoadStatisticsStore::ThirdPartyData>&&)>&& completionHandler)
: m_completionHandler(WTFMove(completionHandler))
{
ASSERT(RunLoop::isMain());
};
~LocalCallbackAggregator()
{
ASSERT(RunLoop::isMain());
m_completionHandler(WTFMove(m_results));
}
void addResult(Vector<WebResourceLoadStatisticsStore::ThirdPartyData>&& results)
{
m_results.appendVector(WTFMove(results));
}
CompletionHandler<void(Vector<WebResourceLoadStatisticsStore::ThirdPartyData>&&)> m_completionHandler;
Vector<WebResourceLoadStatisticsStore::ThirdPartyData> m_results;
};
auto localCallbackAggregator = adoptRef(new LocalCallbackAggregator(WTFMove(completionHandler)));
auto wtfCallbackAggregator = WTF::CallbackAggregator::create([this, protectedThis = Ref { *this }, localCallbackAggregator] {
networkProcess().getResourceLoadStatisticsDataSummary(m_sessionID, [localCallbackAggregator](Vector<WebResourceLoadStatisticsStore::ThirdPartyData>&& data) {
localCallbackAggregator->addResult(WTFMove(data));
});
});
for (auto& processPool : WebProcessPool::allProcessPools())
processPool->sendResourceLoadStatisticsDataImmediately([wtfCallbackAggregator] { });
}
void WebsiteDataStore::scheduleClearInMemoryAndPersistent(ShouldGrandfatherStatistics shouldGrandfather, CompletionHandler<void()>&& completionHandler)
{
ASSERT(RunLoop::isMain());
networkProcess().scheduleClearInMemoryAndPersistent(m_sessionID, { }, shouldGrandfather, WTFMove(completionHandler));
}
void WebsiteDataStore::scheduleCookieBlockingUpdate(CompletionHandler<void()>&& completionHandler)
{
auto callbackAggregator = CallbackAggregator::create(WTFMove(completionHandler));
networkProcess().scheduleCookieBlockingUpdate(m_sessionID, [callbackAggregator] { });
}
void WebsiteDataStore::scheduleStatisticsAndDataRecordsProcessing(CompletionHandler<void()>&& completionHandler)
{
ASSERT(RunLoop::isMain());
auto callbackAggregator = CallbackAggregator::create(WTFMove(completionHandler));
networkProcess().scheduleStatisticsAndDataRecordsProcessing(m_sessionID, [callbackAggregator] { });
}
void WebsiteDataStore::statisticsDatabaseHasAllTables(CompletionHandler<void(bool)>&& completionHandler)
{
ASSERT(RunLoop::isMain());
networkProcess().statisticsDatabaseHasAllTables(m_sessionID, WTFMove(completionHandler));
}
void WebsiteDataStore::setLastSeen(const URL& url, Seconds seconds, CompletionHandler<void()>&& completionHandler)
{
if (url.protocolIsAbout() || url.isEmpty()) {
completionHandler();
return;
}
auto callbackAggregator = CallbackAggregator::create(WTFMove(completionHandler));
networkProcess().setLastSeen(m_sessionID, WebCore::RegistrableDomain { url }, seconds, [callbackAggregator] { });
}
void WebsiteDataStore::domainIDExistsInDatabase(int domainID, CompletionHandler<void(bool)>&& completionHandler)
{
ASSERT(RunLoop::isMain());
networkProcess().domainIDExistsInDatabase(m_sessionID, domainID, WTFMove(completionHandler));
}
void WebsiteDataStore::mergeStatisticForTesting(const URL& url, const URL& topFrameUrl1, const URL& topFrameUrl2, Seconds lastSeen, bool hadUserInteraction, Seconds mostRecentUserInteraction, bool isGrandfathered, bool isPrevalent, bool isVeryPrevalent, unsigned dataRecordsRemoved, CompletionHandler<void()>&& completionHandler)
{
if (url.protocolIsAbout() || url.isEmpty()) {
completionHandler();
return;
}
auto callbackAggregator = CallbackAggregator::create(WTFMove(completionHandler));
networkProcess().mergeStatisticForTesting(m_sessionID, WebCore::RegistrableDomain { url }, WebCore::RegistrableDomain { topFrameUrl1 }, WebCore::RegistrableDomain { topFrameUrl2 }, lastSeen, hadUserInteraction, mostRecentUserInteraction, isGrandfathered, isPrevalent, isVeryPrevalent, dataRecordsRemoved, [callbackAggregator] { });
}
void WebsiteDataStore::insertExpiredStatisticForTesting(const URL& url, unsigned numberOfOperatingDaysPassed, bool hadUserInteraction, bool isScheduledForAllButCookieDataRemoval, bool isPrevalent, CompletionHandler<void()>&& completionHandler)
{
if (url.protocolIsAbout() || url.isEmpty()) {
completionHandler();
return;
}
auto callbackAggregator = CallbackAggregator::create(WTFMove(completionHandler));
networkProcess().insertExpiredStatisticForTesting(m_sessionID, WebCore::RegistrableDomain { url }, numberOfOperatingDaysPassed, hadUserInteraction, isScheduledForAllButCookieDataRemoval, isPrevalent, [callbackAggregator] { });
}
void WebsiteDataStore::setNotifyPagesWhenDataRecordsWereScanned(bool value, CompletionHandler<void()>&& completionHandler)
{
auto callbackAggregator = CallbackAggregator::create(WTFMove(completionHandler));
networkProcess().setNotifyPagesWhenDataRecordsWereScanned(m_sessionID, value, [callbackAggregator] { });
}
void WebsiteDataStore::setIsRunningResourceLoadStatisticsTest(bool value, CompletionHandler<void()>&& completionHandler)
{
useExplicitITPState();
auto callbackAggregator = CallbackAggregator::create(WTFMove(completionHandler));
networkProcess().setIsRunningResourceLoadStatisticsTest(m_sessionID, value, [callbackAggregator] { });
}
void WebsiteDataStore::getAllStorageAccessEntries(WebPageProxyIdentifier pageID, CompletionHandler<void(Vector<String>&& domains)>&& completionHandler)
{
auto* webPage = WebProcessProxy::webPage(pageID);
if (!webPage) {
completionHandler({ });
return;
}
networkProcess().getAllStorageAccessEntries(m_sessionID, WTFMove(completionHandler));
}
void WebsiteDataStore::setTimeToLiveUserInteraction(Seconds seconds, CompletionHandler<void()>&& completionHandler)
{
auto callbackAggregator = CallbackAggregator::create(WTFMove(completionHandler));
networkProcess().setTimeToLiveUserInteraction(m_sessionID, seconds, [callbackAggregator] { });
}
void WebsiteDataStore::logUserInteraction(const URL& url, CompletionHandler<void()>&& completionHandler)
{
ASSERT(RunLoop::isMain());
if (url.protocolIsAbout() || url.isEmpty()) {
completionHandler();
return;
}
auto callbackAggregator = CallbackAggregator::create(WTFMove(completionHandler));
networkProcess().logUserInteraction(m_sessionID, WebCore::RegistrableDomain { url }, [callbackAggregator] { });
}
void WebsiteDataStore::hasHadUserInteraction(const URL& url, CompletionHandler<void(bool)>&& completionHandler)
{
ASSERT(RunLoop::isMain());
if (url.protocolIsAbout() || url.isEmpty()) {
completionHandler(false);
return;
}
networkProcess().hasHadUserInteraction(m_sessionID, WebCore::RegistrableDomain { url }, WTFMove(completionHandler));
}
void WebsiteDataStore::isRelationshipOnlyInDatabaseOnce(const URL& subUrl, const URL& topUrl, CompletionHandler<void(bool)>&& completionHandler)
{
ASSERT(RunLoop::isMain());
if (subUrl.protocolIsAbout() || subUrl.isEmpty() || topUrl.protocolIsAbout() || topUrl.isEmpty()) {
completionHandler(false);
return;
}
networkProcess().isRelationshipOnlyInDatabaseOnce(m_sessionID, WebCore::RegistrableDomain { subUrl }, WebCore::RegistrableDomain { topUrl }, WTFMove(completionHandler));
}
void WebsiteDataStore::clearUserInteraction(const URL& url, CompletionHandler<void()>&& completionHandler)
{
ASSERT(RunLoop::isMain());
if (url.protocolIsAbout() || url.isEmpty()) {
completionHandler();
return;
}
auto callbackAggregator = CallbackAggregator::create(WTFMove(completionHandler));
networkProcess().clearUserInteraction(m_sessionID, WebCore::RegistrableDomain { url }, [callbackAggregator] { });
}
void WebsiteDataStore::setGrandfathered(const URL& url, bool isGrandfathered, CompletionHandler<void()>&& completionHandler)
{
ASSERT(RunLoop::isMain());
if (url.protocolIsAbout() || url.isEmpty()) {
completionHandler();
return;
}
auto callbackAggregator = CallbackAggregator::create(WTFMove(completionHandler));
networkProcess().setGrandfathered(m_sessionID, WebCore::RegistrableDomain { url }, isGrandfathered, [callbackAggregator] { });
}
void WebsiteDataStore::setCrossSiteLoadWithLinkDecorationForTesting(const URL& fromURL, const URL& toURL, CompletionHandler<void()>&& completionHandler)
{
ASSERT(RunLoop::isMain());
auto callbackAggregator = CallbackAggregator::create(WTFMove(completionHandler));
networkProcess().setCrossSiteLoadWithLinkDecorationForTesting(m_sessionID, WebCore::RegistrableDomain { fromURL }, WebCore::RegistrableDomain { toURL }, [callbackAggregator] { });
}
void WebsiteDataStore::resetCrossSiteLoadsWithLinkDecorationForTesting(CompletionHandler<void()>&& completionHandler)
{
auto callbackAggregator = CallbackAggregator::create(WTFMove(completionHandler));
networkProcess().resetCrossSiteLoadsWithLinkDecorationForTesting(m_sessionID, [callbackAggregator] { });
}
void WebsiteDataStore::deleteCookiesForTesting(const URL& url, bool includeHttpOnlyCookies, CompletionHandler<void()>&& completionHandler)
{
auto callbackAggregator = CallbackAggregator::create(WTFMove(completionHandler));
networkProcess().deleteCookiesForTesting(m_sessionID, WebCore::RegistrableDomain { url }, includeHttpOnlyCookies, [callbackAggregator] { });
}
void WebsiteDataStore::hasLocalStorageForTesting(const URL& url, CompletionHandler<void(bool)>&& completionHandler) const
{
networkProcess().hasLocalStorage(m_sessionID, WebCore::RegistrableDomain { url }, WTFMove(completionHandler));
}
void WebsiteDataStore::hasIsolatedSessionForTesting(const URL& url, CompletionHandler<void(bool)>&& completionHandler) const
{
networkProcess().hasIsolatedSession(m_sessionID, WebCore::RegistrableDomain { url }, WTFMove(completionHandler));
}
void WebsiteDataStore::setResourceLoadStatisticsShouldDowngradeReferrerForTesting(bool enabled, CompletionHandler<void()>&& completionHandler)
{
auto callbackAggregator = CallbackAggregator::create(WTFMove(completionHandler));
networkProcess().setShouldDowngradeReferrerForTesting(enabled, [callbackAggregator] { });
}
#if !PLATFORM(COCOA)
WebCore::ThirdPartyCookieBlockingMode WebsiteDataStore::thirdPartyCookieBlockingMode() const
{
if (!m_thirdPartyCookieBlockingMode)
m_thirdPartyCookieBlockingMode = WebCore::ThirdPartyCookieBlockingMode::All;
return *m_thirdPartyCookieBlockingMode;
}
#endif
void WebsiteDataStore::setResourceLoadStatisticsShouldBlockThirdPartyCookiesForTesting(bool enabled, bool onlyOnSitesWithoutUserInteraction, CompletionHandler<void()>&& completionHandler)
{
auto callbackAggregator = CallbackAggregator::create(WTFMove(completionHandler));
WebCore::ThirdPartyCookieBlockingMode blockingMode = WebCore::ThirdPartyCookieBlockingMode::OnlyAccordingToPerDomainPolicy;
if (enabled)
blockingMode = onlyOnSitesWithoutUserInteraction ? WebCore::ThirdPartyCookieBlockingMode::AllOnSitesWithoutUserInteraction : WebCore::ThirdPartyCookieBlockingMode::All;
setThirdPartyCookieBlockingMode(blockingMode, WTFMove(completionHandler));
}
void WebsiteDataStore::setThirdPartyCookieBlockingMode(WebCore::ThirdPartyCookieBlockingMode blockingMode, CompletionHandler<void()>&& completionHandler)
{
auto callbackAggregator = CallbackAggregator::create(WTFMove(completionHandler));
if (thirdPartyCookieBlockingMode() != blockingMode) {
m_thirdPartyCookieBlockingMode = blockingMode;
for (auto& webProcess : processes())
webProcess.setThirdPartyCookieBlockingMode(blockingMode, [callbackAggregator] { });
}
networkProcess().setThirdPartyCookieBlockingMode(m_sessionID, blockingMode, [callbackAggregator] { });
}
void WebsiteDataStore::setResourceLoadStatisticsShouldEnbleSameSiteStrictEnforcementForTesting(bool enabled, CompletionHandler<void()>&& completionHandler)
{
auto flag = enabled ? WebCore::SameSiteStrictEnforcementEnabled::Yes : WebCore::SameSiteStrictEnforcementEnabled::No;
auto callbackAggregator = CallbackAggregator::create(WTFMove(completionHandler));
networkProcess().setShouldEnbleSameSiteStrictEnforcementForTesting(m_sessionID, flag, [callbackAggregator] { });
}
void WebsiteDataStore::setResourceLoadStatisticsFirstPartyWebsiteDataRemovalModeForTesting(bool enabled, CompletionHandler<void()>&& completionHandler)
{
auto callbackAggregator = CallbackAggregator::create(WTFMove(completionHandler));
auto mode = enabled ? WebCore::FirstPartyWebsiteDataRemovalMode::AllButCookies : WebCore::FirstPartyWebsiteDataRemovalMode::None;
networkProcess().setFirstPartyWebsiteDataRemovalModeForTesting(m_sessionID, mode, [callbackAggregator] { });
}
void WebsiteDataStore::setResourceLoadStatisticsToSameSiteStrictCookiesForTesting(const URL& url, CompletionHandler<void()>&& completionHandler)
{
auto callbackAggregator = CallbackAggregator::create(WTFMove(completionHandler));
networkProcess().setToSameSiteStrictCookiesForTesting(m_sessionID, WebCore::RegistrableDomain { url }, [callbackAggregator] { });
}
void WebsiteDataStore::setResourceLoadStatisticsFirstPartyHostCNAMEDomainForTesting(const URL& firstPartyURL, const URL& cnameURL, CompletionHandler<void()>&& completionHandler)
{
if (cnameURL.host() != "testwebkit.org"_s && cnameURL.host() != "3rdpartytestwebkit.org"_s) {
completionHandler();
return;
}
auto callbackAggregator = CallbackAggregator::create(WTFMove(completionHandler));
networkProcess().setFirstPartyHostCNAMEDomainForTesting(m_sessionID, firstPartyURL.host().toString(), WebCore::RegistrableDomain { cnameURL }, [callbackAggregator] { });
}
void WebsiteDataStore::setResourceLoadStatisticsThirdPartyCNAMEDomainForTesting(const URL& cnameURL, CompletionHandler<void()>&& completionHandler)
{
if (cnameURL.host() != "testwebkit.org"_s && cnameURL.host() != "3rdpartytestwebkit.org"_s) {
completionHandler();
return;
}
auto callbackAggregator = CallbackAggregator::create(WTFMove(completionHandler));
networkProcess().setThirdPartyCNAMEDomainForTesting(m_sessionID, WebCore::RegistrableDomain { cnameURL }, [callbackAggregator] { });
}
#endif // ENABLE(INTELLIGENT_TRACKING_PREVENTION)
void WebsiteDataStore::setCachedProcessSuspensionDelayForTesting(Seconds delay)
{
WebProcessCache::setCachedProcessSuspensionDelayForTesting(delay);
}
void WebsiteDataStore::syncLocalStorage(CompletionHandler<void()>&& completionHandler)
{
networkProcess().sendWithAsyncReply(Messages::NetworkProcess::SyncLocalStorage(), WTFMove(completionHandler));
}
void WebsiteDataStore::setCacheMaxAgeCapForPrevalentResources(Seconds seconds, CompletionHandler<void()>&& completionHandler)
{
#if ENABLE(INTELLIGENT_TRACKING_PREVENTION)
auto callbackAggregator = CallbackAggregator::create(WTFMove(completionHandler));
networkProcess().setCacheMaxAgeCapForPrevalentResources(m_sessionID, seconds, [callbackAggregator] { });
#else
UNUSED_PARAM(seconds);
completionHandler();
#endif
}
void WebsiteDataStore::resetCacheMaxAgeCapForPrevalentResources(CompletionHandler<void()>&& completionHandler)
{
#if ENABLE(INTELLIGENT_TRACKING_PREVENTION)
networkProcess().resetCacheMaxAgeCapForPrevalentResources(m_sessionID, WTFMove(completionHandler));
#else
completionHandler();
#endif
}
HashSet<RefPtr<WebProcessPool>> WebsiteDataStore::processPools(size_t limit) const
{
HashSet<RefPtr<WebProcessPool>> processPools;
for (auto& process : processes()) {
if (auto* processPool = process.processPoolIfExists()) {
processPools.add(processPool);
if (processPools.size() == limit)
break;
}
}
if (processPools.isEmpty()) {
// Check if we're one of the legacy data stores.
for (auto& processPool : WebProcessPool::allProcessPools()) {
processPools.add(processPool.ptr());
if (processPools.size() == limit)
break;
}
}
return processPools;
}
HashSet<RefPtr<WebProcessPool>> WebsiteDataStore::ensureProcessPools() const
{
auto processPools = this->processPools();
if (processPools.isEmpty())
processPools.add(WebProcessPool::create(API::ProcessPoolConfiguration::create()));
return processPools;
}
static String computeMediaKeyFile(const String& mediaKeyDirectory)
{
return FileSystem::pathByAppendingComponent(mediaKeyDirectory, "SecureStop.plist"_s);
}
void WebsiteDataStore::allowSpecificHTTPSCertificateForHost(const WebCore::CertificateInfo& certificate, const String& host)
{
networkProcess().send(Messages::NetworkProcess::AllowSpecificHTTPSCertificateForHost(certificate, host), 0);
}
void WebsiteDataStore::allowTLSCertificateChainForLocalPCMTesting(const WebCore::CertificateInfo& certificate)
{
networkProcess().send(Messages::NetworkProcess::AllowTLSCertificateChainForLocalPCMTesting(sessionID(), certificate), 0);
}
Vector<WebCore::SecurityOriginData> WebsiteDataStore::mediaKeyOrigins(const String& mediaKeysStorageDirectory)
{
ASSERT(!mediaKeysStorageDirectory.isEmpty());
Vector<WebCore::SecurityOriginData> origins;
for (const auto& mediaKeyIdentifier : FileSystem::listDirectory(mediaKeysStorageDirectory)) {
auto originPath = FileSystem::pathByAppendingComponent(mediaKeysStorageDirectory, mediaKeyIdentifier);
auto mediaKeyFile = computeMediaKeyFile(originPath);
if (!FileSystem::fileExists(mediaKeyFile))
continue;
if (auto securityOrigin = WebCore::SecurityOriginData::fromDatabaseIdentifier(mediaKeyIdentifier))
origins.append(*securityOrigin);
}
return origins;
}
void WebsiteDataStore::removeMediaKeys(const String& mediaKeysStorageDirectory, WallTime modifiedSince)
{
ASSERT(!mediaKeysStorageDirectory.isEmpty());
for (const auto& directoryName : FileSystem::listDirectory(mediaKeysStorageDirectory)) {
auto mediaKeyDirectory = FileSystem::pathByAppendingComponent(mediaKeysStorageDirectory, directoryName);
auto mediaKeyFile = computeMediaKeyFile(mediaKeyDirectory);
auto modificationTime = FileSystem::fileModificationTime(mediaKeyFile);
if (!modificationTime)
continue;
if (modificationTime.value() < modifiedSince)
continue;
FileSystem::deleteFile(mediaKeyFile);
FileSystem::deleteEmptyDirectory(mediaKeyDirectory);
}
}
void WebsiteDataStore::removeMediaKeys(const String& mediaKeysStorageDirectory, const HashSet<WebCore::SecurityOriginData>& origins)
{
ASSERT(!mediaKeysStorageDirectory.isEmpty());
for (const auto& origin : origins) {
auto mediaKeyDirectory = FileSystem::pathByAppendingComponent(mediaKeysStorageDirectory, origin.databaseIdentifier());
auto mediaKeyFile = computeMediaKeyFile(mediaKeyDirectory);
FileSystem::deleteFile(mediaKeyFile);
FileSystem::deleteEmptyDirectory(mediaKeyDirectory);
}
}
void WebsiteDataStore::getNetworkProcessConnection(WebProcessProxy& webProcessProxy, CompletionHandler<void(const NetworkProcessConnectionInfo&)>&& reply, ShouldRetryOnFailure shouldRetryOnFailure)
{
auto& networkProcessProxy = networkProcess();
networkProcessProxy.getNetworkProcessConnection(webProcessProxy, [weakThis = WeakPtr { *this }, networkProcessProxy = WeakPtr { networkProcessProxy }, webProcessProxy = WeakPtr { webProcessProxy }, reply = WTFMove(reply), shouldRetryOnFailure] (auto& connectionInfo) mutable {
if (UNLIKELY(!IPC::Connection::identifierIsValid(connectionInfo.identifier()))) {
if (shouldRetryOnFailure == ShouldRetryOnFailure::No || !webProcessProxy) {
RELEASE_LOG_ERROR(Process, "getNetworkProcessConnection: Failed to get connection to network process, will reply invalid identifier ...");
reply({ });
return;
}
// Retry on the next RunLoop iteration because we may be inside the WebsiteDataStore destructor.
RunLoop::main().dispatch([weakThis = WTFMove(weakThis), networkProcessProxy = WTFMove(networkProcessProxy), webProcessProxy = WTFMove(webProcessProxy), reply = WTFMove(reply)] () mutable {
if (RefPtr<WebsiteDataStore> strongThis = weakThis.get(); strongThis && webProcessProxy) {
// Terminate if it is the same network process.
if (networkProcessProxy && strongThis->m_networkProcess == networkProcessProxy.get())
strongThis->terminateNetworkProcess();
RELEASE_LOG_ERROR(Process, "getNetworkProcessConnection: Failed to get connection to network process, will retry ...");
strongThis->getNetworkProcessConnection(*webProcessProxy, WTFMove(reply), ShouldRetryOnFailure::No);
return;
}
RELEASE_LOG_ERROR(Process, "getNetworkProcessConnection: Failed to get connection to network process, will reply invalid identifier ...");
reply({ });
});
return;
}
reply(connectionInfo);
});
}
void WebsiteDataStore::networkProcessDidTerminate(NetworkProcessProxy& networkProcess)
{
ASSERT(!m_networkProcess || m_networkProcess == &networkProcess);
m_networkProcess = nullptr;
}
void WebsiteDataStore::terminateNetworkProcess()
{
if (auto networkProcess = std::exchange(m_networkProcess, nullptr))
networkProcess->requestTermination();
}
void WebsiteDataStore::sendNetworkProcessPrepareToSuspendForTesting(CompletionHandler<void()>&& completionHandler)
{
networkProcess().sendPrepareToSuspend(IsSuspensionImminent::No, 0.0, WTFMove(completionHandler));
}
void WebsiteDataStore::sendNetworkProcessWillSuspendImminentlyForTesting()
{
networkProcess().sendProcessWillSuspendImminentlyForTesting();
}
void WebsiteDataStore::sendNetworkProcessDidResume()
{
networkProcess().sendProcessDidResume(ProcessThrottlerClient::ResumeReason::ForegroundActivity);
}
bool WebsiteDataStore::resourceLoadStatisticsEnabled() const
{
#if ENABLE(INTELLIGENT_TRACKING_PREVENTION)
return m_resourceLoadStatisticsEnabled;
#else
return false;
#endif
}
bool WebsiteDataStore::resourceLoadStatisticsDebugMode() const
{
#if ENABLE(INTELLIGENT_TRACKING_PREVENTION)
return m_resourceLoadStatisticsDebugMode;
#else
return false;
#endif
}
void WebsiteDataStore::setResourceLoadStatisticsEnabled(bool enabled)
{
#if ENABLE(INTELLIGENT_TRACKING_PREVENTION)
if (enabled == resourceLoadStatisticsEnabled())
return;
if (enabled) {
m_resourceLoadStatisticsEnabled = true;
resolveDirectoriesIfNecessary();
if (m_networkProcess)
m_networkProcess->send(Messages::NetworkProcess::SetResourceLoadStatisticsEnabled(m_sessionID, true), 0);
for (auto& processPool : processPools())
processPool->sendToAllProcessesForSession(Messages::WebProcess::SetResourceLoadStatisticsEnabled(true), m_sessionID);
return;
}
if (m_networkProcess)
m_networkProcess->send(Messages::NetworkProcess::SetResourceLoadStatisticsEnabled(m_sessionID, false), 0);
for (auto& processPool : processPools())
processPool->sendToAllProcessesForSession(Messages::WebProcess::SetResourceLoadStatisticsEnabled(false), m_sessionID);
m_resourceLoadStatisticsEnabled = false;
#else
UNUSED_PARAM(enabled);
#endif
}
#if ENABLE(INTELLIGENT_TRACKING_PREVENTION)
void WebsiteDataStore::setStatisticsTestingCallback(Function<void(const String&)>&& callback)
{
if (callback) {
networkProcess().send(Messages::NetworkProcess::SetResourceLoadStatisticsLogTestingEvent(true), 0);
}
m_statisticsTestingCallback = WTFMove(callback);
}
#endif
void WebsiteDataStore::setResourceLoadStatisticsDebugMode(bool enabled)
{
setResourceLoadStatisticsDebugMode(enabled, []() { });
}
void WebsiteDataStore::setResourceLoadStatisticsDebugMode(bool enabled, CompletionHandler<void()>&& completionHandler)
{
#if ENABLE(INTELLIGENT_TRACKING_PREVENTION)
m_resourceLoadStatisticsDebugMode = enabled;
auto callbackAggregator = CallbackAggregator::create(WTFMove(completionHandler));
networkProcess().setResourceLoadStatisticsDebugMode(m_sessionID, enabled, [callbackAggregator] { });
#else
UNUSED_PARAM(enabled);
UNUSED_PARAM(completionHandler);
#endif
}
void WebsiteDataStore::isResourceLoadStatisticsEphemeral(CompletionHandler<void(bool)>&& completionHandler) const
{
#if ENABLE(INTELLIGENT_TRACKING_PREVENTION)
if (!resourceLoadStatisticsEnabled() || !m_sessionID.isEphemeral()) {
completionHandler(false);
return;
}
networkProcess().isResourceLoadStatisticsEphemeral(m_sessionID, WTFMove(completionHandler));
#else
completionHandler(false);
#endif
}
void WebsiteDataStore::setPrivateClickMeasurementDebugMode(bool enabled)
{
networkProcess().setPrivateClickMeasurementDebugMode(sessionID(), enabled);
}
void WebsiteDataStore::closeDatabases(CompletionHandler<void()>&& completionHandler)
{
auto callbackAggregator = CallbackAggregator::create(WTFMove(completionHandler));
networkProcess().sendWithAsyncReply(Messages::NetworkProcess::ClosePCMDatabase(m_sessionID), [callbackAggregator] { });
#if ENABLE(INTELLIGENT_TRACKING_PREVENTION)
networkProcess().sendWithAsyncReply(Messages::NetworkProcess::CloseITPDatabase(m_sessionID), [callbackAggregator] { });
#endif
}
#if ENABLE(INTELLIGENT_TRACKING_PREVENTION)
void WebsiteDataStore::logTestingEvent(const String& event)
{
ASSERT(RunLoop::isMain());
if (m_statisticsTestingCallback)
m_statisticsTestingCallback(event);
}
void WebsiteDataStore::clearResourceLoadStatisticsInWebProcesses(CompletionHandler<void()>&& callback)
{
if (resourceLoadStatisticsEnabled()) {
for (auto& processPool : processPools())
processPool->sendToAllProcessesForSession(Messages::WebProcess::ClearResourceLoadStatistics(), m_sessionID);
}
callback();
}
#endif
void WebsiteDataStore::setAllowsAnySSLCertificateForWebSocket(bool allows)
{
networkProcess().sendSync(Messages::NetworkProcess::SetAllowsAnySSLCertificateForWebSocket(allows), Messages::NetworkProcess::SetAllowsAnySSLCertificateForWebSocket::Reply(), 0);
}
void WebsiteDataStore::clearCachedCredentials()
{
networkProcess().send(Messages::NetworkProcess::ClearCachedCredentials(sessionID()), 0);
}
void WebsiteDataStore::dispatchOnQueue(Function<void()>&& function)
{
m_queue->dispatch(WTFMove(function));
}
uint64_t WebsiteDataStore::perThirdPartyOriginStorageQuota() const
{
// FIXME: Consider whether allowing to set a perThirdPartyOriginStorageQuota from a WebsiteDataStore.
return perOriginStorageQuota() / 10;
}
void WebsiteDataStore::setCacheModelSynchronouslyForTesting(CacheModel cacheModel)
{
for (auto& processPool : WebProcessPool::allProcessPools())
processPool->setCacheModelSynchronouslyForTesting(cacheModel);
}
Vector<WebsiteDataStoreParameters> WebsiteDataStore::parametersFromEachWebsiteDataStore()
{
return WTF::map(allDataStores(), [](auto& entry) {
return entry.value->parameters();
});
}
WebsiteDataStoreParameters WebsiteDataStore::parameters()
{
WebsiteDataStoreParameters parameters;
resolveDirectoriesIfNecessary();
auto resourceLoadStatisticsDirectory = m_resolvedConfiguration->resourceLoadStatisticsDirectory();
SandboxExtension::Handle resourceLoadStatisticsDirectoryHandle;
if (!resourceLoadStatisticsDirectory.isEmpty()) {
if (auto handle = SandboxExtension::createHandleForReadWriteDirectory(resourceLoadStatisticsDirectory))
resourceLoadStatisticsDirectoryHandle = WTFMove(*handle);
}
auto privateClickMeasurementStorageDirectory = m_resolvedConfiguration->privateClickMeasurementStorageDirectory();
SandboxExtension::Handle privateClickMeasurementStorageDirectoryHandle;
if (!privateClickMeasurementStorageDirectory.isEmpty()) {
if (auto handle = SandboxExtension::createHandleForReadWriteDirectory(privateClickMeasurementStorageDirectory))
privateClickMeasurementStorageDirectoryHandle = WTFMove(*handle);
}
auto networkCacheDirectory = resolvedNetworkCacheDirectory();
SandboxExtension::Handle networkCacheDirectoryExtensionHandle;
if (!networkCacheDirectory.isEmpty()) {
// FIXME: SandboxExtension::createHandleForReadWriteDirectory resolves the directory, but that has already been done. Remove this duplicate work.
if (auto handle = SandboxExtension::createHandleForReadWriteDirectory(networkCacheDirectory))
networkCacheDirectoryExtensionHandle = WTFMove(*handle);
}
auto hstsStorageDirectory = resolvedHSTSStorageDirectory();
SandboxExtension::Handle hstsStorageDirectoryExtensionHandle;
if (!hstsStorageDirectory.isEmpty()) {
// FIXME: SandboxExtension::createHandleForReadWriteDirectory resolves the directory, but that has already been done. Remove this duplicate work.
if (auto handle = SandboxExtension::createHandleForReadWriteDirectory(hstsStorageDirectory))
hstsStorageDirectoryExtensionHandle = WTFMove(*handle);
}
bool shouldIncludeLocalhostInResourceLoadStatistics = false;
bool enableResourceLoadStatisticsDebugMode = false;
auto firstPartyWebsiteDataRemovalMode = WebCore::FirstPartyWebsiteDataRemovalMode::AllButCookies;
WebCore::RegistrableDomain standaloneApplicationDomain;
HashSet<WebCore::RegistrableDomain> appBoundDomains;
#if ENABLE(APP_BOUND_DOMAINS)
if (isAppBoundITPRelaxationEnabled)
appBoundDomains = valueOrDefault(appBoundDomainsIfInitialized());
#endif
WebCore::RegistrableDomain resourceLoadStatisticsManualPrevalentResource;
ResourceLoadStatisticsParameters resourceLoadStatisticsParameters = {
WTFMove(resourceLoadStatisticsDirectory),
WTFMove(resourceLoadStatisticsDirectoryHandle),
WTFMove(privateClickMeasurementStorageDirectory),
WTFMove(privateClickMeasurementStorageDirectoryHandle),
resourceLoadStatisticsEnabled(),
#if ENABLE(INTELLIGENT_TRACKING_PREVENTION)
isItpStateExplicitlySet(),
hasStatisticsTestingCallback(),
#else
false,
false,
#endif
shouldIncludeLocalhostInResourceLoadStatistics,
enableResourceLoadStatisticsDebugMode,
#if ENABLE(INTELLIGENT_TRACKING_PREVENTION)
thirdPartyCookieBlockingMode(),
WebCore::SameSiteStrictEnforcementEnabled::No,
#endif
firstPartyWebsiteDataRemovalMode,
WTFMove(standaloneApplicationDomain),
WTFMove(appBoundDomains),
WTFMove(resourceLoadStatisticsManualPrevalentResource),
};
NetworkSessionCreationParameters networkSessionParameters;
networkSessionParameters.sessionID = m_sessionID;
networkSessionParameters.boundInterfaceIdentifier = configuration().boundInterfaceIdentifier();
networkSessionParameters.allowsCellularAccess = configuration().allowsCellularAccess() ? AllowsCellularAccess::Yes : AllowsCellularAccess::No;
networkSessionParameters.deviceManagementRestrictionsEnabled = m_configuration->deviceManagementRestrictionsEnabled();
networkSessionParameters.allLoadsBlockedByDeviceManagementRestrictionsForTesting = m_configuration->allLoadsBlockedByDeviceManagementRestrictionsForTesting();
networkSessionParameters.webPushDaemonConnectionConfiguration = m_configuration->webPushDaemonConnectionConfiguration();
networkSessionParameters.networkCacheDirectory = WTFMove(networkCacheDirectory);
networkSessionParameters.networkCacheDirectoryExtensionHandle = WTFMove(networkCacheDirectoryExtensionHandle);
networkSessionParameters.hstsStorageDirectory = WTFMove(hstsStorageDirectory);
networkSessionParameters.hstsStorageDirectoryExtensionHandle = WTFMove(hstsStorageDirectoryExtensionHandle);
networkSessionParameters.dataConnectionServiceType = m_configuration->dataConnectionServiceType();
networkSessionParameters.fastServerTrustEvaluationEnabled = m_configuration->fastServerTrustEvaluationEnabled();
networkSessionParameters.networkCacheSpeculativeValidationEnabled = m_configuration->networkCacheSpeculativeValidationEnabled();
networkSessionParameters.shouldUseTestingNetworkSession = m_configuration->testingSessionEnabled();
networkSessionParameters.staleWhileRevalidateEnabled = m_configuration->staleWhileRevalidateEnabled();
networkSessionParameters.testSpeedMultiplier = m_configuration->testSpeedMultiplier();
networkSessionParameters.suppressesConnectionTerminationOnSystemChange = m_configuration->suppressesConnectionTerminationOnSystemChange();
networkSessionParameters.allowsServerPreconnect = m_configuration->allowsServerPreconnect();
networkSessionParameters.resourceLoadStatisticsParameters = WTFMove(resourceLoadStatisticsParameters);
networkSessionParameters.requiresSecureHTTPSProxyConnection = m_configuration->requiresSecureHTTPSProxyConnection();
networkSessionParameters.shouldRunServiceWorkersOnMainThreadForTesting = m_configuration->shouldRunServiceWorkersOnMainThreadForTesting();
networkSessionParameters.overrideServiceWorkerRegistrationCountTestingValue = m_configuration->overrideServiceWorkerRegistrationCountTestingValue();
networkSessionParameters.preventsSystemHTTPProxyAuthentication = m_configuration->preventsSystemHTTPProxyAuthentication();
networkSessionParameters.allowsHSTSWithUntrustedRootCertificate = m_configuration->allowsHSTSWithUntrustedRootCertificate();
networkSessionParameters.pcmMachServiceName = m_configuration->pcmMachServiceName();
networkSessionParameters.webPushMachServiceName = m_configuration->webPushMachServiceName();
#if !HAVE(NSURLSESSION_WEBSOCKET)
networkSessionParameters.shouldAcceptInsecureCertificatesForWebSockets = m_configuration->shouldAcceptInsecureCertificatesForWebSockets();
#endif
networkSessionParameters.shouldUseCustomStoragePaths = m_configuration->shouldUseCustomStoragePaths();
networkSessionParameters.perOriginStorageQuota = perOriginStorageQuota();
networkSessionParameters.perThirdPartyOriginStorageQuota = perThirdPartyOriginStorageQuota();
if (auto directory = resolvedLocalStorageDirectory(); !directory.isEmpty()) {
networkSessionParameters.localStorageDirectory = directory;
// FIXME: SandboxExtension::createHandleForReadWriteDirectory resolves the directory, but that has already been done. Remove this duplicate work.
if (auto handle = SandboxExtension::createHandleForReadWriteDirectory(directory))
networkSessionParameters.localStorageDirectoryExtensionHandle = WTFMove(*handle);
}
if (auto directory = resolvedIndexedDBDatabaseDirectory(); !directory.isEmpty()) {
networkSessionParameters.indexedDBDirectory = directory;
// FIXME: SandboxExtension::createHandleForReadWriteDirectory resolves the directory, but that has already been done. Remove this duplicate work.
if (auto handle = SandboxExtension::createHandleForReadWriteDirectory(directory))
networkSessionParameters.indexedDBDirectoryExtensionHandle = WTFMove(*handle);
}
#if ENABLE(SERVICE_WORKER)
if (auto directory = resolvedServiceWorkerRegistrationDirectory(); !directory.isEmpty()) {
networkSessionParameters.serviceWorkerRegistrationDirectory = directory;
// FIXME: SandboxExtension::createHandleForReadWriteDirectory resolves the directory, but that has already been done. Remove this duplicate work.
if (auto handle = SandboxExtension::createHandleForReadWriteDirectory(directory))
networkSessionParameters.serviceWorkerRegistrationDirectoryExtensionHandle = WTFMove(*handle);
}
networkSessionParameters.serviceWorkerProcessTerminationDelayEnabled = m_configuration->serviceWorkerProcessTerminationDelayEnabled();
#endif
if (auto directory = resolvedGeneralStorageDirectory(); !directory.isEmpty()) {
networkSessionParameters.generalStorageDirectory = directory;
if (auto handle = SandboxExtension::createHandleForReadWriteDirectory(directory))
networkSessionParameters.generalStorageDirectoryHandle = WTFMove(*handle);
}
if (auto directory = cacheStorageDirectory(); !directory.isEmpty()) {
networkSessionParameters.cacheStorageDirectory = directory;
if (auto handle = SandboxExtension::createHandleForReadWriteDirectory(directory))
networkSessionParameters.cacheStorageDirectoryExtensionHandle = WTFMove(*handle);
}
parameters.networkSessionParameters = WTFMove(networkSessionParameters);
#if ENABLE(INTELLIGENT_TRACKING_PREVENTION)
parameters.networkSessionParameters.resourceLoadStatisticsParameters.enabled = m_resourceLoadStatisticsEnabled;
#endif
platformSetNetworkParameters(parameters);
#if PLATFORM(COCOA)
parameters.networkSessionParameters.appHasRequestedCrossWebsiteTrackingPermission = hasRequestedCrossWebsiteTrackingPermission();
parameters.networkSessionParameters.useNetworkLoader = useNetworkLoader();
#endif
#if PLATFORM(IOS_FAMILY)
if (isPersistent()) {
if (auto directory = cookieStorageDirectory(); !directory.isEmpty())
parameters.cookieStorageDirectoryExtensionHandle = SandboxExtension::createHandleForReadWriteDirectory(directory);
if (auto directory = networkingCachesDirectory(); !directory.isEmpty())
parameters.containerCachesDirectoryExtensionHandle = SandboxExtension::createHandleForReadWriteDirectory(directory);
if (auto directory = parentBundleDirectory(); !directory.isEmpty())
parameters.parentBundleDirectoryExtensionHandle = SandboxExtension::createHandle(directory, SandboxExtension::Type::ReadOnly);
if (auto handleAndFilePath = SandboxExtension::createHandleForTemporaryFile(emptyString(), SandboxExtension::Type::ReadWrite))
parameters.tempDirectoryExtensionHandle = WTFMove(handleAndFilePath->first);
}
#endif
return parameters;
}
#if HAVE(SEC_KEY_PROXY)
void WebsiteDataStore::addSecKeyProxyStore(Ref<SecKeyProxyStore>&& store)
{
m_secKeyProxyStores.append(WTFMove(store));
}
#endif
#if ENABLE(WEB_AUTHN)
void WebsiteDataStore::setMockWebAuthenticationConfiguration(WebCore::MockWebAuthenticationConfiguration&& configuration)
{
if (!m_authenticatorManager->isMock()) {
m_authenticatorManager = makeUniqueRef<MockAuthenticatorManager>(WTFMove(configuration));
return;
}
static_cast<MockAuthenticatorManager*>(&m_authenticatorManager)->setTestConfiguration(WTFMove(configuration));
}
VirtualAuthenticatorManager& WebsiteDataStore::virtualAuthenticatorManager()
{
if (!m_authenticatorManager->isVirtual())
m_authenticatorManager = makeUniqueRef<VirtualAuthenticatorManager>();
return static_cast<VirtualAuthenticatorManager&>(m_authenticatorManager.get());
}
#endif
API::HTTPCookieStore& WebsiteDataStore::cookieStore()
{
if (!m_cookieStore)
m_cookieStore = API::HTTPCookieStore::create(*this);
return *m_cookieStore;
}
void WebsiteDataStore::resetQuota(CompletionHandler<void()>&& completionHandler)
{
auto callbackAggregator = CallbackAggregator::create(WTFMove(completionHandler));
networkProcess().resetQuota(m_sessionID, [callbackAggregator] { });
}
void WebsiteDataStore::clearStorage(CompletionHandler<void()>&& completionHandler)
{
networkProcess().clearStorage(m_sessionID, WTFMove(completionHandler));
}
#if !PLATFORM(COCOA)
String WebsiteDataStore::defaultMediaCacheDirectory()
{
// FIXME: Implement. https://bugs.webkit.org/show_bug.cgi?id=156369 and https://bugs.webkit.org/show_bug.cgi?id=156370
return String();
}
String WebsiteDataStore::defaultAlternativeServicesDirectory()
{
// FIXME: Implement.
return String();
}
String WebsiteDataStore::defaultJavaScriptConfigurationDirectory()
{
// FIXME: Implement.
return String();
}
bool WebsiteDataStore::http3Enabled()
{
return false;
}
bool WebsiteDataStore::networkProcessHasEntitlementForTesting(const String&)
{
return false;
}
bool WebsiteDataStore::defaultShouldUseCustomStoragePaths()
{
return true;
}
#endif // !PLATFORM(COCOA)
#if !USE(GLIB) && !PLATFORM(COCOA)
String WebsiteDataStore::defaultDeviceIdHashSaltsStorageDirectory()
{
// Not implemented.
return String();
}
#endif
void WebsiteDataStore::renameOriginInWebsiteData(URL&& oldName, URL&& newName, OptionSet<WebsiteDataType> dataTypes, CompletionHandler<void()>&& completionHandler)
{
networkProcess().renameOriginInWebsiteData(m_sessionID, oldName, newName, dataTypes, WTFMove(completionHandler));
}
void WebsiteDataStore::originDirectoryForTesting(URL&& origin, URL&& topOrigin, WebsiteDataType type, CompletionHandler<void(const String&)>&& completionHandler)
{
networkProcess().websiteDataOriginDirectoryForTesting(m_sessionID, WTFMove(origin), WTFMove(topOrigin), type, WTFMove(completionHandler));
}
#if ENABLE(APP_BOUND_DOMAINS)
void WebsiteDataStore::hasAppBoundSession(CompletionHandler<void(bool)>&& completionHandler) const
{
networkProcess().hasAppBoundSession(m_sessionID, WTFMove(completionHandler));
}
void WebsiteDataStore::clearAppBoundSession(CompletionHandler<void()>&& completionHandler)
{
networkProcess().clearAppBoundSession(m_sessionID, WTFMove(completionHandler));
}
void WebsiteDataStore::forwardAppBoundDomainsToITPIfInitialized(CompletionHandler<void()>&& completionHandler)
{
auto callbackAggregator = CallbackAggregator::create(WTFMove(completionHandler));
auto appBoundDomains = appBoundDomainsIfInitialized();
if (!appBoundDomains)
return;
auto propagateAppBoundDomains = [callbackAggregator] (WebsiteDataStore* store, const HashSet<WebCore::RegistrableDomain>& domains) {
if (!store)
return;
if (store->thirdPartyCookieBlockingMode() != WebCore::ThirdPartyCookieBlockingMode::AllExceptBetweenAppBoundDomains)
store->setThirdPartyCookieBlockingMode(WebCore::ThirdPartyCookieBlockingMode::AllExceptBetweenAppBoundDomains, [callbackAggregator] { });
store->setAppBoundDomainsForITP(domains, [callbackAggregator] { });
};
propagateAppBoundDomains(globalDefaultDataStore().get(), *appBoundDomains);
for (auto* store : allDataStores().values())
propagateAppBoundDomains(store, *appBoundDomains);
}
void WebsiteDataStore::setAppBoundDomainsForITP(const HashSet<WebCore::RegistrableDomain>& domains, CompletionHandler<void()>&& completionHandler)
{
auto callbackAggregator = CallbackAggregator::create(WTFMove(completionHandler));
networkProcess().setAppBoundDomainsForResourceLoadStatistics(m_sessionID, domains, [callbackAggregator] { });
}
#endif
void WebsiteDataStore::updateBundleIdentifierInNetworkProcess(const String& bundleIdentifier, CompletionHandler<void()>&& completionHandler)
{
auto callbackAggregator = CallbackAggregator::create(WTFMove(completionHandler));
networkProcess().updateBundleIdentifier(bundleIdentifier, [callbackAggregator] { });
}
void WebsiteDataStore::clearBundleIdentifierInNetworkProcess(CompletionHandler<void()>&& completionHandler)
{
auto callbackAggregator = CallbackAggregator::create(WTFMove(completionHandler));
networkProcess().clearBundleIdentifier([callbackAggregator] { });
}
void WebsiteDataStore::countNonDefaultSessionSets(CompletionHandler<void(size_t)>&& completionHandler)
{
networkProcess().sendWithAsyncReply(Messages::NetworkProcess::CountNonDefaultSessionSets(m_sessionID), WTFMove(completionHandler));
}
static bool nextNetworkProcessLaunchShouldFailForTesting { false };
void WebsiteDataStore::makeNextNetworkProcessLaunchFailForTesting()
{
nextNetworkProcessLaunchShouldFailForTesting = true;
}
bool WebsiteDataStore::shouldMakeNextNetworkProcessLaunchFailForTesting()
{
return std::exchange(nextNetworkProcessLaunchShouldFailForTesting, false);
}
void WebsiteDataStore::showServiceWorkerNotification(IPC::Connection& connection, const WebCore::NotificationData& notificationData)
{
WebNotificationManagerProxy::sharedServiceWorkerManager().show(nullptr, connection, notificationData);
}
void WebsiteDataStore::cancelServiceWorkerNotification(const UUID& notificationID)
{
WebNotificationManagerProxy::sharedServiceWorkerManager().cancel(nullptr, notificationID);
}
void WebsiteDataStore::clearServiceWorkerNotification(const UUID& notificationID)
{
Vector<UUID> notifications = { notificationID };
WebNotificationManagerProxy::sharedServiceWorkerManager().clearNotifications(nullptr, notifications);
}
void WebsiteDataStore::didDestroyServiceWorkerNotification(const UUID& notificationID)
{
WebNotificationManagerProxy::sharedServiceWorkerManager().didDestroyNotification(nullptr, notificationID);
}
void WebsiteDataStore::openWindowFromServiceWorker(const String& urlString, const WebCore::SecurityOriginData& serviceWorkerOrigin, CompletionHandler<void(std::optional<WebCore::PageIdentifier>)>&& callback)
{
auto innerCallback = [callback = WTFMove(callback)] (WebPageProxy* newPage) mutable {
if (!newPage) {
callback(std::nullopt);
return;
}
if (!newPage->pageLoadState().isLoading()) {
RELEASE_LOG(Loading, "The WKWebView provided in response to a ServiceWorker openWindow request was not in the loading state");
callback(std::nullopt);
return;
}
#if ENABLE(SERVICE_WORKER)
newPage->setServiceWorkerOpenWindowCompletionCallback(WTFMove(callback));
#endif
};
m_client->openWindowFromServiceWorker(urlString, serviceWorkerOrigin, WTFMove(innerCallback));
}
}