1/*
2 * Copyright (C) 2014-2019 Apple Inc. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 * 1. Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 *
13 * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
14 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
15 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
17 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
18 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
19 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
20 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
21 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
22 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
23 * THE POSSIBILITY OF SUCH DAMAGE.
24 */
25
26#include "config.h"
27#include "WebsiteDataStore.h"
28
29#include "APIProcessPoolConfiguration.h"
30#include "APIWebsiteDataRecord.h"
31#include "APIWebsiteDataStore.h"
32#include "AuthenticatorManager.h"
33#include "DeviceIdHashSaltStorage.h"
34#include "MockAuthenticatorManager.h"
35#include "NetworkProcessMessages.h"
36#include "ShouldGrandfatherStatistics.h"
37#include "StorageAccessStatus.h"
38#include "StorageManager.h"
39#include "WebProcessCache.h"
40#include "WebProcessMessages.h"
41#include "WebProcessPool.h"
42#include "WebResourceLoadStatisticsStore.h"
43#include "WebsiteData.h"
44#include "WebsiteDataStoreClient.h"
45#include "WebsiteDataStoreParameters.h"
46#include <WebCore/ApplicationCacheStorage.h>
47#include <WebCore/CredentialStorage.h>
48#include <WebCore/DatabaseTracker.h>
49#include <WebCore/HTMLMediaElement.h>
50#include <WebCore/OriginLock.h>
51#include <WebCore/RegistrableDomain.h>
52#include <WebCore/SecurityOrigin.h>
53#include <WebCore/SecurityOriginData.h>
54#include <WebCore/StorageQuotaManager.h>
55#include <wtf/CallbackAggregator.h>
56#include <wtf/CompletionHandler.h>
57#include <wtf/CrossThreadCopier.h>
58#include <wtf/FileSystem.h>
59#include <wtf/ProcessPrivilege.h>
60#include <wtf/RunLoop.h>
61
62#if ENABLE(NETSCAPE_PLUGIN_API)
63#include "PluginProcessManager.h"
64#endif
65
66#if HAVE(SEC_KEY_PROXY)
67#include "SecKeyProxyStore.h"
68#endif
69
70namespace WebKit {
71
72static bool allowsWebsiteDataRecordsForAllOrigins;
73void WebsiteDataStore::allowWebsiteDataRecordsForAllOrigins()
74{
75 allowsWebsiteDataRecordsForAllOrigins = true;
76}
77
78static HashMap<PAL::SessionID, WebsiteDataStore*>& nonDefaultDataStores()
79{
80 RELEASE_ASSERT(isUIThread());
81 static NeverDestroyed<HashMap<PAL::SessionID, WebsiteDataStore*>> map;
82 return map;
83}
84
85Ref<WebsiteDataStore> WebsiteDataStore::createNonPersistent()
86{
87 return adoptRef(*new WebsiteDataStore(PAL::SessionID::generateEphemeralSessionID()));
88}
89
90Ref<WebsiteDataStore> WebsiteDataStore::create(Ref<WebsiteDataStoreConfiguration>&& configuration, PAL::SessionID sessionID)
91{
92 return adoptRef(*new WebsiteDataStore(WTFMove(configuration), sessionID));
93}
94
95WebsiteDataStore::WebsiteDataStore(Ref<WebsiteDataStoreConfiguration>&& configuration, PAL::SessionID sessionID)
96 : m_sessionID(sessionID)
97 , m_resolvedConfiguration(WTFMove(configuration))
98 , m_configuration(m_resolvedConfiguration->copy())
99 , m_storageManager(StorageManager::create(m_configuration->localStorageDirectory()))
100 , m_deviceIdHashSaltStorage(DeviceIdHashSaltStorage::create(isPersistent() ? m_configuration->deviceIdHashSaltsStorageDirectory() : String()))
101 , m_queue(WorkQueue::create("com.apple.WebKit.WebsiteDataStore"))
102 , m_sourceApplicationBundleIdentifier(m_configuration->sourceApplicationBundleIdentifier())
103 , m_sourceApplicationSecondaryIdentifier(m_configuration->sourceApplicationSecondaryIdentifier())
104#if ENABLE(WEB_AUTHN)
105 , m_authenticatorManager(makeUniqueRef<AuthenticatorManager>())
106#endif
107 , m_client(makeUniqueRef<WebsiteDataStoreClient>())
108{
109#if HAVE(LOAD_OPTIMIZER)
110WEBSITEDATASTORE_LOADOPTIMIZER_ADDITIONS_2
111#endif
112 WTF::setProcessPrivileges(allPrivileges());
113 maybeRegisterWithSessionIDMap();
114 platformInitialize();
115
116 ASSERT(RunLoop::isMain());
117}
118
119WebsiteDataStore::WebsiteDataStore(PAL::SessionID sessionID)
120 : m_sessionID(sessionID)
121 , m_resolvedConfiguration(WebsiteDataStoreConfiguration::create())
122 , m_configuration(m_resolvedConfiguration->copy())
123 , m_storageManager(StorageManager::create({ }))
124 , m_deviceIdHashSaltStorage(DeviceIdHashSaltStorage::create(isPersistent() ? m_configuration->deviceIdHashSaltsStorageDirectory() : String()))
125 , m_queue(WorkQueue::create("com.apple.WebKit.WebsiteDataStore"))
126#if ENABLE(WEB_AUTHN)
127 , m_authenticatorManager(makeUniqueRef<AuthenticatorManager>())
128#endif
129 , m_client(makeUniqueRef<WebsiteDataStoreClient>())
130{
131#if HAVE(LOAD_OPTIMIZER)
132WEBSITEDATASTORE_LOADOPTIMIZER_ADDITIONS_2
133#endif
134 maybeRegisterWithSessionIDMap();
135 platformInitialize();
136
137 ASSERT(RunLoop::isMain());
138}
139
140WebsiteDataStore::~WebsiteDataStore()
141{
142 ASSERT(RunLoop::isMain());
143
144 platformDestroy();
145
146 if (m_sessionID.isValid() && m_sessionID != PAL::SessionID::defaultSessionID()) {
147 ASSERT(nonDefaultDataStores().get(m_sessionID) == this);
148 nonDefaultDataStores().remove(m_sessionID);
149 for (auto& processPool : WebProcessPool::allProcessPools()) {
150 if (auto* networkProcess = processPool->networkProcess())
151 networkProcess->removeSession(m_sessionID);
152 }
153 }
154}
155
156void WebsiteDataStore::maybeRegisterWithSessionIDMap()
157{
158 if (m_sessionID.isValid() && m_sessionID != PAL::SessionID::defaultSessionID()) {
159 auto result = nonDefaultDataStores().add(m_sessionID, this);
160 ASSERT_UNUSED(result, result.isNewEntry);
161 }
162}
163
164WebsiteDataStore* WebsiteDataStore::existingNonDefaultDataStoreForSessionID(PAL::SessionID sessionID)
165{
166 return sessionID.isValid() && sessionID != PAL::SessionID::defaultSessionID() ? nonDefaultDataStores().get(sessionID) : nullptr;
167}
168
169WebProcessPool* WebsiteDataStore::processPoolForCookieStorageOperations()
170{
171 auto pools = processPools(1, false);
172 return pools.isEmpty() ? nullptr : pools.begin()->get();
173}
174
175void WebsiteDataStore::resolveDirectoriesIfNecessary()
176{
177 if (m_hasResolvedDirectories)
178 return;
179 m_hasResolvedDirectories = true;
180
181 // Resolve directory paths.
182 if (!m_configuration->applicationCacheDirectory().isEmpty())
183 m_resolvedConfiguration->setApplicationCacheDirectory(resolveAndCreateReadWriteDirectoryForSandboxExtension(m_configuration->applicationCacheDirectory()));
184 if (!m_configuration->mediaCacheDirectory().isEmpty())
185 m_resolvedConfiguration->setMediaCacheDirectory(resolveAndCreateReadWriteDirectoryForSandboxExtension(m_configuration->mediaCacheDirectory()));
186 if (!m_configuration->mediaKeysStorageDirectory().isEmpty())
187 m_resolvedConfiguration->setMediaKeysStorageDirectory(resolveAndCreateReadWriteDirectoryForSandboxExtension(m_configuration->mediaKeysStorageDirectory()));
188 if (!m_configuration->webSQLDatabaseDirectory().isEmpty())
189 m_resolvedConfiguration->setWebSQLDatabaseDirectory(resolveAndCreateReadWriteDirectoryForSandboxExtension(m_configuration->webSQLDatabaseDirectory()));
190 if (!m_configuration->indexedDBDatabaseDirectory().isEmpty())
191 m_resolvedConfiguration->setIndexedDBDatabaseDirectory(resolveAndCreateReadWriteDirectoryForSandboxExtension(m_configuration->indexedDBDatabaseDirectory()));
192 if (!m_configuration->deviceIdHashSaltsStorageDirectory().isEmpty())
193 m_resolvedConfiguration->setDeviceIdHashSaltsStorageDirectory(resolveAndCreateReadWriteDirectoryForSandboxExtension(m_configuration->deviceIdHashSaltsStorageDirectory()));
194 if (!m_configuration->resourceLoadStatisticsDirectory().isEmpty())
195 m_resolvedConfiguration->setResourceLoadStatisticsDirectory(resolveAndCreateReadWriteDirectoryForSandboxExtension(m_configuration->resourceLoadStatisticsDirectory()));
196 if (!m_configuration->serviceWorkerRegistrationDirectory().isEmpty() && m_resolvedConfiguration->serviceWorkerRegistrationDirectory().isEmpty())
197 m_resolvedConfiguration->setServiceWorkerRegistrationDirectory(resolveAndCreateReadWriteDirectoryForSandboxExtension(m_configuration->serviceWorkerRegistrationDirectory()));
198 if (!m_configuration->javaScriptConfigurationDirectory().isEmpty())
199 m_resolvedConfiguration->setJavaScriptConfigurationDirectory(resolvePathForSandboxExtension(m_configuration->javaScriptConfigurationDirectory()));
200 if (!m_configuration->cacheStorageDirectory().isEmpty() && m_resolvedConfiguration->cacheStorageDirectory().isEmpty())
201 m_resolvedConfiguration->setCacheStorageDirectory(resolvePathForSandboxExtension(m_configuration->cacheStorageDirectory()));
202
203 // Resolve directories for file paths.
204 if (!m_configuration->cookieStorageFile().isEmpty()) {
205 m_resolvedConfiguration->setCookieStorageFile(resolveAndCreateReadWriteDirectoryForSandboxExtension(FileSystem::directoryName(m_configuration->cookieStorageFile())));
206 m_resolvedConfiguration->setCookieStorageFile(FileSystem::pathByAppendingComponent(m_resolvedConfiguration->cookieStorageFile(), FileSystem::pathGetFileName(m_configuration->cookieStorageFile())));
207 }
208}
209
210void WebsiteDataStore::cloneSessionData(WebPageProxy& sourcePage, WebPageProxy& newPage)
211{
212 auto& sourceDataStore = sourcePage.websiteDataStore();
213 auto& newDataStore = newPage.websiteDataStore();
214
215 // FIXME: Handle this.
216 if (&sourceDataStore != &newDataStore)
217 return;
218
219 if (!sourceDataStore.m_storageManager)
220 return;
221
222 sourceDataStore.m_storageManager->cloneSessionStorageNamespace(sourcePage.pageID(), newPage.pageID());
223}
224
225enum class ProcessAccessType {
226 None,
227 OnlyIfLaunched,
228 Launch,
229};
230
231static ProcessAccessType computeNetworkProcessAccessTypeForDataFetch(OptionSet<WebsiteDataType> dataTypes, bool isNonPersistentStore)
232{
233 ProcessAccessType processAccessType = ProcessAccessType::None;
234
235 if (dataTypes.contains(WebsiteDataType::Cookies)) {
236 if (isNonPersistentStore)
237 processAccessType = std::max(processAccessType, ProcessAccessType::OnlyIfLaunched);
238 else
239 processAccessType = std::max(processAccessType, ProcessAccessType::Launch);
240 }
241
242 if (dataTypes.contains(WebsiteDataType::Credentials) && isNonPersistentStore)
243 processAccessType = std::max(processAccessType, ProcessAccessType::OnlyIfLaunched);
244
245 if (dataTypes.contains(WebsiteDataType::DiskCache) && !isNonPersistentStore)
246 processAccessType = std::max(processAccessType, ProcessAccessType::Launch);
247
248 if (dataTypes.contains(WebsiteDataType::DOMCache))
249 processAccessType = std::max(processAccessType, ProcessAccessType::Launch);
250
251 if (dataTypes.contains(WebsiteDataType::IndexedDBDatabases) && !isNonPersistentStore)
252 processAccessType = std::max(processAccessType, ProcessAccessType::Launch);
253
254#if ENABLE(SERVICE_WORKER)
255 if (dataTypes.contains(WebsiteDataType::ServiceWorkerRegistrations) && !isNonPersistentStore)
256 processAccessType = std::max(processAccessType, ProcessAccessType::Launch);
257#endif
258
259 return processAccessType;
260}
261
262static ProcessAccessType computeWebProcessAccessTypeForDataFetch(OptionSet<WebsiteDataType> dataTypes, bool isNonPersistentStore)
263{
264 UNUSED_PARAM(isNonPersistentStore);
265
266 ProcessAccessType processAccessType = ProcessAccessType::None;
267
268 if (dataTypes.contains(WebsiteDataType::MemoryCache))
269 return ProcessAccessType::OnlyIfLaunched;
270
271 return processAccessType;
272}
273
274void WebsiteDataStore::fetchData(OptionSet<WebsiteDataType> dataTypes, OptionSet<WebsiteDataFetchOption> fetchOptions, Function<void(Vector<WebsiteDataRecord>)>&& completionHandler)
275{
276 fetchDataAndApply(dataTypes, fetchOptions, nullptr, WTFMove(completionHandler));
277}
278
279void WebsiteDataStore::fetchDataAndApply(OptionSet<WebsiteDataType> dataTypes, OptionSet<WebsiteDataFetchOption> fetchOptions, RefPtr<WorkQueue>&& queue, Function<void(Vector<WebsiteDataRecord>)>&& apply)
280{
281 struct CallbackAggregator final : ThreadSafeRefCounted<CallbackAggregator> {
282 CallbackAggregator(OptionSet<WebsiteDataFetchOption> fetchOptions, RefPtr<WorkQueue>&& queue, Function<void(Vector<WebsiteDataRecord>)>&& apply, WebsiteDataStore& dataStore)
283 : fetchOptions(fetchOptions)
284 , queue(WTFMove(queue))
285 , apply(WTFMove(apply))
286 , protectedDataStore(dataStore)
287 {
288 ASSERT(RunLoop::isMain());
289 }
290
291 ~CallbackAggregator()
292 {
293 ASSERT(!pendingCallbacks);
294
295 // Make sure the data store gets destroyed on the main thread even though the CallbackAggregator can get destroyed on a background queue.
296 RunLoop::main().dispatch([protectedDataStore = WTFMove(protectedDataStore)] { });
297 }
298
299 void addPendingCallback()
300 {
301 pendingCallbacks++;
302 }
303
304 void removePendingCallback(WebsiteData websiteData)
305 {
306 ASSERT(pendingCallbacks);
307 --pendingCallbacks;
308
309 for (auto& entry : websiteData.entries) {
310 auto displayName = WebsiteDataRecord::displayNameForOrigin(entry.origin);
311 if (!displayName) {
312 if (!allowsWebsiteDataRecordsForAllOrigins)
313 continue;
314
315 displayName = makeString(entry.origin.protocol, " ", entry.origin.host);
316 }
317
318 auto& record = m_websiteDataRecords.add(displayName, WebsiteDataRecord { }).iterator->value;
319 if (!record.displayName)
320 record.displayName = WTFMove(displayName);
321
322 record.add(entry.type, entry.origin);
323
324 if (fetchOptions.contains(WebsiteDataFetchOption::ComputeSizes)) {
325 if (!record.size)
326 record.size = WebsiteDataRecord::Size { 0, { } };
327
328 record.size->totalSize += entry.size;
329 record.size->typeSizes.add(static_cast<unsigned>(entry.type), 0).iterator->value += entry.size;
330 }
331 }
332
333 for (auto& hostName : websiteData.hostNamesWithCookies) {
334 auto displayName = WebsiteDataRecord::displayNameForCookieHostName(hostName);
335 if (!displayName)
336 continue;
337
338 auto& record = m_websiteDataRecords.add(displayName, WebsiteDataRecord { }).iterator->value;
339 if (!record.displayName)
340 record.displayName = WTFMove(displayName);
341
342 record.addCookieHostName(hostName);
343 }
344
345#if ENABLE(NETSCAPE_PLUGIN_API)
346 for (auto& hostName : websiteData.hostNamesWithPluginData) {
347 auto displayName = WebsiteDataRecord::displayNameForHostName(hostName);
348 if (!displayName)
349 continue;
350
351 auto& record = m_websiteDataRecords.add(displayName, WebsiteDataRecord { }).iterator->value;
352 if (!record.displayName)
353 record.displayName = WTFMove(displayName);
354
355 record.addPluginDataHostName(hostName);
356 }
357#endif
358
359 for (auto& origin : websiteData.originsWithCredentials) {
360 auto displayName = WebsiteDataRecord::displayNameForOrigin(WebCore::SecurityOriginData::fromURL(URL(URL(), origin)));
361 ASSERT(!displayName.isEmpty());
362
363 auto& record = m_websiteDataRecords.add(displayName, WebsiteDataRecord { }).iterator->value;
364 if (!record.displayName)
365 record.displayName = WTFMove(displayName);
366
367 record.addOriginWithCredential(origin);
368 }
369
370 for (auto& hostName : websiteData.hostNamesWithHSTSCache) {
371 auto displayName = WebsiteDataRecord::displayNameForHostName(hostName);
372 if (!displayName)
373 continue;
374
375 auto& record = m_websiteDataRecords.add(displayName, WebsiteDataRecord { }).iterator->value;
376 if (!record.displayName)
377 record.displayName = WTFMove(displayName);
378
379 record.addHSTSCacheHostname(hostName);
380 }
381
382 callIfNeeded();
383 }
384
385 void callIfNeeded()
386 {
387 if (pendingCallbacks)
388 return;
389
390 Vector<WebsiteDataRecord> records;
391 records.reserveInitialCapacity(m_websiteDataRecords.size());
392 for (auto& record : m_websiteDataRecords.values())
393 records.uncheckedAppend(WTFMove(record));
394
395 auto processRecords = [apply = WTFMove(apply), records = WTFMove(records)] () mutable {
396 apply(WTFMove(records));
397 };
398
399 if (queue)
400 queue->dispatch(WTFMove(processRecords));
401 else
402 RunLoop::main().dispatch(WTFMove(processRecords));
403 }
404
405 const OptionSet<WebsiteDataFetchOption> fetchOptions;
406
407 unsigned pendingCallbacks = 0;
408 RefPtr<WorkQueue> queue;
409 Function<void(Vector<WebsiteDataRecord>)> apply;
410
411 HashMap<String, WebsiteDataRecord> m_websiteDataRecords;
412 Ref<WebsiteDataStore> protectedDataStore;
413 };
414
415 RefPtr<CallbackAggregator> callbackAggregator = adoptRef(new CallbackAggregator(fetchOptions, WTFMove(queue), WTFMove(apply), *this));
416
417#if ENABLE(VIDEO)
418 if (dataTypes.contains(WebsiteDataType::DiskCache)) {
419 callbackAggregator->addPendingCallback();
420 m_queue->dispatch([mediaCacheDirectory = m_configuration->mediaCacheDirectory().isolatedCopy(), callbackAggregator] {
421 // FIXME: Make HTMLMediaElement::originsInMediaCache return a collection of SecurityOriginDatas.
422 HashSet<RefPtr<WebCore::SecurityOrigin>> origins = WebCore::HTMLMediaElement::originsInMediaCache(mediaCacheDirectory);
423 WebsiteData websiteData;
424
425 for (auto& origin : origins) {
426 WebsiteData::Entry entry { origin->data(), WebsiteDataType::DiskCache, 0 };
427 websiteData.entries.append(WTFMove(entry));
428 }
429
430 RunLoop::main().dispatch([callbackAggregator, origins = WTFMove(origins), websiteData = WTFMove(websiteData)]() mutable {
431 callbackAggregator->removePendingCallback(WTFMove(websiteData));
432 });
433 });
434 }
435#endif
436
437 auto networkProcessAccessType = computeNetworkProcessAccessTypeForDataFetch(dataTypes, !isPersistent());
438 if (networkProcessAccessType != ProcessAccessType::None) {
439 for (auto& processPool : processPools()) {
440 switch (networkProcessAccessType) {
441 case ProcessAccessType::OnlyIfLaunched:
442 if (!processPool->networkProcess())
443 continue;
444 break;
445
446 case ProcessAccessType::Launch:
447 processPool->ensureNetworkProcess(this);
448 break;
449
450 case ProcessAccessType::None:
451 ASSERT_NOT_REACHED();
452 }
453
454 callbackAggregator->addPendingCallback();
455 processPool->networkProcess()->fetchWebsiteData(m_sessionID, dataTypes, fetchOptions, [callbackAggregator, processPool](WebsiteData websiteData) {
456 callbackAggregator->removePendingCallback(WTFMove(websiteData));
457 });
458 }
459 }
460
461 auto webProcessAccessType = computeWebProcessAccessTypeForDataFetch(dataTypes, !isPersistent());
462 if (webProcessAccessType != ProcessAccessType::None) {
463 for (auto& process : processes()) {
464 switch (webProcessAccessType) {
465 case ProcessAccessType::OnlyIfLaunched:
466 if (!process->canSendMessage())
467 continue;
468 break;
469
470 case ProcessAccessType::Launch:
471 // FIXME: Handle this.
472 ASSERT_NOT_REACHED();
473 break;
474
475 case ProcessAccessType::None:
476 ASSERT_NOT_REACHED();
477 }
478
479 callbackAggregator->addPendingCallback();
480 process->fetchWebsiteData(m_sessionID, dataTypes, [callbackAggregator](WebsiteData websiteData) {
481 callbackAggregator->removePendingCallback(WTFMove(websiteData));
482 });
483 }
484 }
485
486 if (dataTypes.contains(WebsiteDataType::SessionStorage) && m_storageManager) {
487 callbackAggregator->addPendingCallback();
488
489 m_storageManager->getSessionStorageOrigins([callbackAggregator](HashSet<WebCore::SecurityOriginData>&& origins) {
490 WebsiteData websiteData;
491
492 while (!origins.isEmpty())
493 websiteData.entries.append(WebsiteData::Entry { origins.takeAny(), WebsiteDataType::SessionStorage, 0 });
494
495 callbackAggregator->removePendingCallback(WTFMove(websiteData));
496 });
497 }
498
499 if (dataTypes.contains(WebsiteDataType::LocalStorage) && m_storageManager) {
500 callbackAggregator->addPendingCallback();
501
502 m_storageManager->getLocalStorageOrigins([callbackAggregator](HashSet<WebCore::SecurityOriginData>&& origins) {
503 WebsiteData websiteData;
504
505 while (!origins.isEmpty())
506 websiteData.entries.append(WebsiteData::Entry { origins.takeAny(), WebsiteDataType::LocalStorage, 0 });
507
508 callbackAggregator->removePendingCallback(WTFMove(websiteData));
509 });
510 }
511
512 if (dataTypes.contains(WebsiteDataType::DeviceIdHashSalt)) {
513 callbackAggregator->addPendingCallback();
514
515 m_deviceIdHashSaltStorage->getDeviceIdHashSaltOrigins([callbackAggregator](auto&& origins) {
516 WebsiteData websiteData;
517
518 while (!origins.isEmpty())
519 websiteData.entries.append(WebsiteData::Entry { origins.takeAny(), WebsiteDataType::DeviceIdHashSalt, 0 });
520
521 callbackAggregator->removePendingCallback(WTFMove(websiteData));
522 });
523 }
524
525 if (dataTypes.contains(WebsiteDataType::OfflineWebApplicationCache) && isPersistent()) {
526 callbackAggregator->addPendingCallback();
527
528 m_queue->dispatch([fetchOptions, applicationCacheDirectory = m_configuration->applicationCacheDirectory().isolatedCopy(), applicationCacheFlatFileSubdirectoryName = m_configuration->applicationCacheFlatFileSubdirectoryName().isolatedCopy(), callbackAggregator] {
529 auto storage = WebCore::ApplicationCacheStorage::create(applicationCacheDirectory, applicationCacheFlatFileSubdirectoryName);
530
531 WebsiteData websiteData;
532
533 // FIXME: getOriginsWithCache should return a collection of SecurityOriginDatas.
534 auto origins = storage->originsWithCache();
535
536 for (auto& origin : origins) {
537 uint64_t size = fetchOptions.contains(WebsiteDataFetchOption::ComputeSizes) ? storage->diskUsageForOrigin(origin) : 0;
538 WebsiteData::Entry entry { origin->data(), WebsiteDataType::OfflineWebApplicationCache, size };
539
540 websiteData.entries.append(WTFMove(entry));
541 }
542
543 RunLoop::main().dispatch([callbackAggregator, origins = WTFMove(origins), websiteData = WTFMove(websiteData)]() mutable {
544 callbackAggregator->removePendingCallback(WTFMove(websiteData));
545 });
546 });
547 }
548
549 if (dataTypes.contains(WebsiteDataType::WebSQLDatabases) && isPersistent()) {
550 callbackAggregator->addPendingCallback();
551
552 m_queue->dispatch([webSQLDatabaseDirectory = m_configuration->webSQLDatabaseDirectory().isolatedCopy(), callbackAggregator] {
553 auto origins = WebCore::DatabaseTracker::trackerWithDatabasePath(webSQLDatabaseDirectory)->origins();
554 RunLoop::main().dispatch([callbackAggregator, origins = WTFMove(origins)]() mutable {
555 WebsiteData websiteData;
556 for (auto& origin : origins)
557 websiteData.entries.append(WebsiteData::Entry { WTFMove(origin), WebsiteDataType::WebSQLDatabases, 0 });
558 callbackAggregator->removePendingCallback(WTFMove(websiteData));
559 });
560 });
561 }
562
563 if (dataTypes.contains(WebsiteDataType::MediaKeys) && isPersistent()) {
564 callbackAggregator->addPendingCallback();
565
566 m_queue->dispatch([mediaKeysStorageDirectory = m_configuration->mediaKeysStorageDirectory().isolatedCopy(), callbackAggregator] {
567 auto origins = mediaKeyOrigins(mediaKeysStorageDirectory);
568
569 RunLoop::main().dispatch([callbackAggregator, origins = WTFMove(origins)]() mutable {
570 WebsiteData websiteData;
571 for (auto& origin : origins)
572 websiteData.entries.append(WebsiteData::Entry { origin, WebsiteDataType::MediaKeys, 0 });
573
574 callbackAggregator->removePendingCallback(WTFMove(websiteData));
575 });
576 });
577 }
578
579#if PLATFORM(COCOA)
580 if (dataTypes.contains(WebsiteDataType::Credentials) && isPersistent()) {
581 for (auto& processPool : processPools()) {
582 if (!processPool->networkProcess())
583 continue;
584
585 callbackAggregator->addPendingCallback();
586 WTF::CompletionHandler<void(Vector<WebCore::SecurityOriginData>&&)> completionHandler = [callbackAggregator](Vector<WebCore::SecurityOriginData>&& origins) mutable {
587 WebsiteData websiteData;
588 for (auto& origin : origins)
589 websiteData.entries.append(WebsiteData::Entry { origin, WebsiteDataType::Credentials, 0 });
590 callbackAggregator->removePendingCallback(WTFMove(websiteData));
591 };
592 processPool->networkProcess()->sendWithAsyncReply(Messages::NetworkProcess::OriginsWithPersistentCredentials(), WTFMove(completionHandler));
593 }
594 }
595#endif
596
597#if ENABLE(NETSCAPE_PLUGIN_API)
598 if (dataTypes.contains(WebsiteDataType::PlugInData) && isPersistent()) {
599 class State {
600 public:
601 static void fetchData(Ref<CallbackAggregator>&& callbackAggregator, Vector<PluginModuleInfo>&& plugins)
602 {
603 new State(WTFMove(callbackAggregator), WTFMove(plugins));
604 }
605
606 private:
607 State(Ref<CallbackAggregator>&& callbackAggregator, Vector<PluginModuleInfo>&& plugins)
608 : m_callbackAggregator(WTFMove(callbackAggregator))
609 , m_plugins(WTFMove(plugins))
610 {
611 m_callbackAggregator->addPendingCallback();
612
613 fetchWebsiteDataForNextPlugin();
614 }
615
616 ~State()
617 {
618 ASSERT(m_plugins.isEmpty());
619 }
620
621 void fetchWebsiteDataForNextPlugin()
622 {
623 if (m_plugins.isEmpty()) {
624 WebsiteData websiteData;
625 websiteData.hostNamesWithPluginData = WTFMove(m_hostNames);
626
627 m_callbackAggregator->removePendingCallback(WTFMove(websiteData));
628
629 delete this;
630 return;
631 }
632
633 auto plugin = m_plugins.takeLast();
634 PluginProcessManager::singleton().fetchWebsiteData(plugin, m_callbackAggregator->fetchOptions, [this](Vector<String> hostNames) {
635 for (auto& hostName : hostNames)
636 m_hostNames.add(WTFMove(hostName));
637 fetchWebsiteDataForNextPlugin();
638 });
639 }
640
641 Ref<CallbackAggregator> m_callbackAggregator;
642 Vector<PluginModuleInfo> m_plugins;
643 HashSet<String> m_hostNames;
644 };
645
646 State::fetchData(*callbackAggregator, plugins());
647 }
648#endif
649
650 callbackAggregator->callIfNeeded();
651}
652
653#if ENABLE(RESOURCE_LOAD_STATISTICS)
654void WebsiteDataStore::fetchDataForRegistrableDomains(OptionSet<WebsiteDataType> dataTypes, OptionSet<WebsiteDataFetchOption> fetchOptions, const Vector<WebCore::RegistrableDomain>& domains, CompletionHandler<void(Vector<WebsiteDataRecord>&&, HashSet<WebCore::RegistrableDomain>&&)>&& completionHandler)
655{
656 fetchDataAndApply(dataTypes, fetchOptions, m_queue.copyRef(), [domains = crossThreadCopy(domains), completionHandler = WTFMove(completionHandler)] (auto&& existingDataRecords) mutable {
657 ASSERT(!RunLoop::isMain());
658
659 Vector<WebsiteDataRecord> matchingDataRecords;
660 HashSet<WebCore::RegistrableDomain> domainsWithMatchingDataRecords;
661 for (auto&& dataRecord : existingDataRecords) {
662 for (auto& domain : domains) {
663 if (dataRecord.matches(domain)) {
664 matchingDataRecords.append(WTFMove(dataRecord));
665 domainsWithMatchingDataRecords.add(domain.isolatedCopy());
666 break;
667 }
668 }
669 }
670 RunLoop::main().dispatch([completionHandler = WTFMove(completionHandler), matchingDataRecords = WTFMove(matchingDataRecords), domainsWithMatchingDataRecords = WTFMove(domainsWithMatchingDataRecords)] () mutable {
671 completionHandler(WTFMove(matchingDataRecords), WTFMove(domainsWithMatchingDataRecords));
672 });
673 });
674}
675#endif
676
677static ProcessAccessType computeNetworkProcessAccessTypeForDataRemoval(OptionSet<WebsiteDataType> dataTypes, bool isNonPersistentStore)
678{
679 ProcessAccessType processAccessType = ProcessAccessType::None;
680
681 for (auto dataType : dataTypes) {
682 if (dataType == WebsiteDataType::Cookies) {
683 if (isNonPersistentStore)
684 processAccessType = std::max(processAccessType, ProcessAccessType::OnlyIfLaunched);
685 else
686 processAccessType = std::max(processAccessType, ProcessAccessType::Launch);
687 } else if (WebsiteData::ownerProcess(dataType) == WebsiteDataProcessType::Network)
688 return ProcessAccessType::Launch;
689 }
690
691 return processAccessType;
692}
693
694static ProcessAccessType computeWebProcessAccessTypeForDataRemoval(OptionSet<WebsiteDataType> dataTypes, bool isNonPersistentStore)
695{
696 UNUSED_PARAM(isNonPersistentStore);
697
698 ProcessAccessType processAccessType = ProcessAccessType::None;
699
700 if (dataTypes.contains(WebsiteDataType::MemoryCache))
701 processAccessType = std::max(processAccessType, ProcessAccessType::OnlyIfLaunched);
702
703 if (dataTypes.contains(WebsiteDataType::Credentials))
704 processAccessType = std::max(processAccessType, ProcessAccessType::OnlyIfLaunched);
705
706 return processAccessType;
707}
708
709void WebsiteDataStore::removeData(OptionSet<WebsiteDataType> dataTypes, WallTime modifiedSince, Function<void()>&& completionHandler)
710{
711 struct CallbackAggregator : ThreadSafeRefCounted<CallbackAggregator> {
712 CallbackAggregator(WebsiteDataStore& dataStore, Function<void()>&& completionHandler)
713 : completionHandler(WTFMove(completionHandler))
714 , protectedDataStore(dataStore)
715 {
716 ASSERT(RunLoop::isMain());
717 }
718
719 ~CallbackAggregator()
720 {
721 // Make sure the data store gets destroyed on the main thread even though the CallbackAggregator can get destroyed on a background queue.
722 RunLoop::main().dispatch([protectedDataStore = WTFMove(protectedDataStore)] { });
723 }
724
725 void addPendingCallback()
726 {
727 pendingCallbacks++;
728 }
729
730 void removePendingCallback()
731 {
732 ASSERT(pendingCallbacks);
733 --pendingCallbacks;
734
735 callIfNeeded();
736 }
737
738 void callIfNeeded()
739 {
740 if (!pendingCallbacks)
741 RunLoop::main().dispatch(WTFMove(completionHandler));
742 }
743
744 unsigned pendingCallbacks = 0;
745 Function<void()> completionHandler;
746 Ref<WebsiteDataStore> protectedDataStore;
747 };
748
749 RefPtr<CallbackAggregator> callbackAggregator = adoptRef(new CallbackAggregator(*this, WTFMove(completionHandler)));
750
751#if ENABLE(VIDEO)
752 if (dataTypes.contains(WebsiteDataType::DiskCache)) {
753 callbackAggregator->addPendingCallback();
754 m_queue->dispatch([modifiedSince, mediaCacheDirectory = m_configuration->mediaCacheDirectory().isolatedCopy(), callbackAggregator] {
755 WebCore::HTMLMediaElement::clearMediaCache(mediaCacheDirectory, modifiedSince);
756
757 WTF::RunLoop::main().dispatch([callbackAggregator] {
758 callbackAggregator->removePendingCallback();
759 });
760 });
761 }
762#endif
763
764#if ENABLE(RESOURCE_LOAD_STATISTICS)
765 bool didNotifyNetworkProcessToDeleteWebsiteData = false;
766#endif
767 auto networkProcessAccessType = computeNetworkProcessAccessTypeForDataRemoval(dataTypes, !isPersistent());
768 if (networkProcessAccessType != ProcessAccessType::None) {
769 for (auto& processPool : processPools()) {
770 switch (networkProcessAccessType) {
771 case ProcessAccessType::OnlyIfLaunched:
772 if (!processPool->networkProcess())
773 continue;
774 break;
775
776 case ProcessAccessType::Launch:
777 processPool->ensureNetworkProcess(this);
778 break;
779
780 case ProcessAccessType::None:
781 ASSERT_NOT_REACHED();
782 }
783
784 callbackAggregator->addPendingCallback();
785 processPool->networkProcess()->deleteWebsiteData(m_sessionID, dataTypes, modifiedSince, [callbackAggregator, processPool] {
786 callbackAggregator->removePendingCallback();
787 });
788#if ENABLE(RESOURCE_LOAD_STATISTICS)
789 didNotifyNetworkProcessToDeleteWebsiteData = true;
790#endif
791 }
792 }
793
794 auto webProcessAccessType = computeWebProcessAccessTypeForDataRemoval(dataTypes, !isPersistent());
795 if (webProcessAccessType != ProcessAccessType::None) {
796 for (auto& processPool : processPools()) {
797 processPool->clearSuspendedPages(AllowProcessCaching::No);
798 processPool->webProcessCache().clear();
799 }
800
801 for (auto& process : processes()) {
802 switch (webProcessAccessType) {
803 case ProcessAccessType::OnlyIfLaunched:
804 if (!process->canSendMessage())
805 continue;
806 break;
807
808 case ProcessAccessType::Launch:
809 // FIXME: Handle this.
810 ASSERT_NOT_REACHED();
811 break;
812
813 case ProcessAccessType::None:
814 ASSERT_NOT_REACHED();
815 }
816
817 callbackAggregator->addPendingCallback();
818 process->deleteWebsiteData(m_sessionID, dataTypes, modifiedSince, [callbackAggregator] {
819 callbackAggregator->removePendingCallback();
820 });
821 }
822 }
823
824 if (dataTypes.contains(WebsiteDataType::SessionStorage) && m_storageManager) {
825 callbackAggregator->addPendingCallback();
826
827 m_storageManager->deleteSessionStorageOrigins([callbackAggregator] {
828 callbackAggregator->removePendingCallback();
829 });
830 }
831
832 if (dataTypes.contains(WebsiteDataType::LocalStorage) && m_storageManager) {
833 callbackAggregator->addPendingCallback();
834
835 m_storageManager->deleteLocalStorageOriginsModifiedSince(modifiedSince, [callbackAggregator] {
836 callbackAggregator->removePendingCallback();
837 });
838 }
839
840 if (dataTypes.contains(WebsiteDataType::DeviceIdHashSalt) || (dataTypes.contains(WebsiteDataType::Cookies))) {
841 callbackAggregator->addPendingCallback();
842
843 m_deviceIdHashSaltStorage->deleteDeviceIdHashSaltOriginsModifiedSince(modifiedSince, [callbackAggregator] {
844 callbackAggregator->removePendingCallback();
845 });
846 }
847
848 if (dataTypes.contains(WebsiteDataType::OfflineWebApplicationCache) && isPersistent()) {
849 callbackAggregator->addPendingCallback();
850
851 m_queue->dispatch([applicationCacheDirectory = m_configuration->applicationCacheDirectory().isolatedCopy(), applicationCacheFlatFileSubdirectoryName = m_configuration->applicationCacheFlatFileSubdirectoryName().isolatedCopy(), callbackAggregator] {
852 auto storage = WebCore::ApplicationCacheStorage::create(applicationCacheDirectory, applicationCacheFlatFileSubdirectoryName);
853
854 storage->deleteAllCaches();
855
856 WTF::RunLoop::main().dispatch([callbackAggregator] {
857 callbackAggregator->removePendingCallback();
858 });
859 });
860 }
861
862 if (dataTypes.contains(WebsiteDataType::WebSQLDatabases) && isPersistent()) {
863 callbackAggregator->addPendingCallback();
864
865 m_queue->dispatch([webSQLDatabaseDirectory = m_configuration->webSQLDatabaseDirectory().isolatedCopy(), callbackAggregator, modifiedSince] {
866 WebCore::DatabaseTracker::trackerWithDatabasePath(webSQLDatabaseDirectory)->deleteDatabasesModifiedSince(modifiedSince);
867
868 RunLoop::main().dispatch([callbackAggregator] {
869 callbackAggregator->removePendingCallback();
870 });
871 });
872 }
873
874 if (dataTypes.contains(WebsiteDataType::MediaKeys) && isPersistent()) {
875 callbackAggregator->addPendingCallback();
876
877 m_queue->dispatch([mediaKeysStorageDirectory = m_configuration->mediaKeysStorageDirectory().isolatedCopy(), callbackAggregator, modifiedSince] {
878 removeMediaKeys(mediaKeysStorageDirectory, modifiedSince);
879
880 RunLoop::main().dispatch([callbackAggregator] {
881 callbackAggregator->removePendingCallback();
882 });
883 });
884 }
885
886 if (dataTypes.contains(WebsiteDataType::SearchFieldRecentSearches) && isPersistent()) {
887 callbackAggregator->addPendingCallback();
888
889 m_queue->dispatch([modifiedSince, callbackAggregator] {
890 platformRemoveRecentSearches(modifiedSince);
891
892 RunLoop::main().dispatch([callbackAggregator] {
893 callbackAggregator->removePendingCallback();
894 });
895 });
896 }
897
898#if ENABLE(NETSCAPE_PLUGIN_API)
899 if (dataTypes.contains(WebsiteDataType::PlugInData) && isPersistent()) {
900 class State {
901 public:
902 static void deleteData(Ref<CallbackAggregator>&& callbackAggregator, Vector<PluginModuleInfo>&& plugins, WallTime modifiedSince)
903 {
904 new State(WTFMove(callbackAggregator), WTFMove(plugins), modifiedSince);
905 }
906
907 private:
908 State(Ref<CallbackAggregator>&& callbackAggregator, Vector<PluginModuleInfo>&& plugins, WallTime modifiedSince)
909 : m_callbackAggregator(WTFMove(callbackAggregator))
910 , m_plugins(WTFMove(plugins))
911 , m_modifiedSince(modifiedSince)
912 {
913 m_callbackAggregator->addPendingCallback();
914
915 deleteWebsiteDataForNextPlugin();
916 }
917
918 ~State()
919 {
920 ASSERT(m_plugins.isEmpty());
921 }
922
923 void deleteWebsiteDataForNextPlugin()
924 {
925 if (m_plugins.isEmpty()) {
926 m_callbackAggregator->removePendingCallback();
927
928 delete this;
929 return;
930 }
931
932 auto plugin = m_plugins.takeLast();
933 PluginProcessManager::singleton().deleteWebsiteData(plugin, m_modifiedSince, [this] {
934 deleteWebsiteDataForNextPlugin();
935 });
936 }
937
938 Ref<CallbackAggregator> m_callbackAggregator;
939 Vector<PluginModuleInfo> m_plugins;
940 WallTime m_modifiedSince;
941 };
942
943 State::deleteData(*callbackAggregator, plugins(), modifiedSince);
944 }
945#endif
946
947#if ENABLE(RESOURCE_LOAD_STATISTICS)
948 if (dataTypes.contains(WebsiteDataType::ResourceLoadStatistics)) {
949 if (!didNotifyNetworkProcessToDeleteWebsiteData) {
950 for (auto& processPool : processPools()) {
951 if (auto* process = processPool->networkProcess()) {
952 callbackAggregator->addPendingCallback();
953 process->deleteWebsiteData(m_sessionID, dataTypes, modifiedSince, [callbackAggregator] {
954 callbackAggregator->removePendingCallback();
955 });
956 }
957 }
958 }
959
960 callbackAggregator->addPendingCallback();
961 clearResourceLoadStatisticsInWebProcesses([callbackAggregator] {
962 callbackAggregator->removePendingCallback();
963 });
964 }
965#endif
966
967 // There's a chance that we don't have any pending callbacks. If so, we want to dispatch the completion handler right away.
968 callbackAggregator->callIfNeeded();
969}
970
971void WebsiteDataStore::removeData(OptionSet<WebsiteDataType> dataTypes, const Vector<WebsiteDataRecord>& dataRecords, Function<void()>&& completionHandler)
972{
973 Vector<WebCore::SecurityOriginData> origins;
974
975 for (const auto& dataRecord : dataRecords) {
976 for (auto& origin : dataRecord.origins)
977 origins.append(origin);
978 }
979
980 struct CallbackAggregator : ThreadSafeRefCounted<CallbackAggregator> {
981 CallbackAggregator(WebsiteDataStore& dataStore, Function<void()>&& completionHandler)
982 : completionHandler(WTFMove(completionHandler))
983 , protectedDataStore(dataStore)
984 {
985 ASSERT(RunLoop::isMain());
986 }
987
988 ~CallbackAggregator()
989 {
990 // Make sure the data store gets destroyed on the main thread even though the CallbackAggregator can get destroyed on a background queue.
991 RunLoop::main().dispatch([protectedDataStore = WTFMove(protectedDataStore)] { });
992 }
993
994 void addPendingCallback()
995 {
996 pendingCallbacks++;
997 }
998
999 void removePendingCallback()
1000 {
1001 ASSERT(pendingCallbacks);
1002 --pendingCallbacks;
1003
1004 callIfNeeded();
1005 }
1006
1007 void callIfNeeded()
1008 {
1009 if (!pendingCallbacks)
1010 RunLoop::main().dispatch(WTFMove(completionHandler));
1011 }
1012
1013 unsigned pendingCallbacks = 0;
1014 Function<void()> completionHandler;
1015 Ref<WebsiteDataStore> protectedDataStore;
1016 };
1017
1018 RefPtr<CallbackAggregator> callbackAggregator = adoptRef(new CallbackAggregator(*this, WTFMove(completionHandler)));
1019
1020 if (dataTypes.contains(WebsiteDataType::DiskCache)) {
1021 HashSet<WebCore::SecurityOriginData> origins;
1022 for (const auto& dataRecord : dataRecords) {
1023 for (const auto& origin : dataRecord.origins)
1024 origins.add(origin);
1025 }
1026
1027#if ENABLE(VIDEO)
1028 callbackAggregator->addPendingCallback();
1029 m_queue->dispatch([origins = WTFMove(origins), mediaCacheDirectory = m_configuration->mediaCacheDirectory().isolatedCopy(), callbackAggregator] {
1030
1031 // FIXME: Move SecurityOrigin::toRawString to SecurityOriginData and
1032 // make HTMLMediaElement::clearMediaCacheForOrigins take SecurityOriginData.
1033 HashSet<RefPtr<WebCore::SecurityOrigin>> securityOrigins;
1034 for (auto& origin : origins)
1035 securityOrigins.add(origin.securityOrigin());
1036 WebCore::HTMLMediaElement::clearMediaCacheForOrigins(mediaCacheDirectory, securityOrigins);
1037
1038 WTF::RunLoop::main().dispatch([callbackAggregator] {
1039 callbackAggregator->removePendingCallback();
1040 });
1041 });
1042#endif
1043 }
1044
1045 auto networkProcessAccessType = computeNetworkProcessAccessTypeForDataRemoval(dataTypes, !isPersistent());
1046 if (networkProcessAccessType != ProcessAccessType::None) {
1047 for (auto& processPool : processPools()) {
1048 switch (networkProcessAccessType) {
1049 case ProcessAccessType::OnlyIfLaunched:
1050 if (!processPool->networkProcess())
1051 continue;
1052 break;
1053
1054 case ProcessAccessType::Launch:
1055 processPool->ensureNetworkProcess(this);
1056 break;
1057
1058 case ProcessAccessType::None:
1059 ASSERT_NOT_REACHED();
1060 }
1061
1062 Vector<String> cookieHostNames;
1063 Vector<String> HSTSCacheHostNames;
1064 for (const auto& dataRecord : dataRecords) {
1065 for (auto& hostName : dataRecord.cookieHostNames)
1066 cookieHostNames.append(hostName);
1067 for (auto& hostName : dataRecord.HSTSCacheHostNames)
1068 HSTSCacheHostNames.append(hostName);
1069 }
1070
1071 callbackAggregator->addPendingCallback();
1072 processPool->networkProcess()->deleteWebsiteDataForOrigins(m_sessionID, dataTypes, origins, cookieHostNames, HSTSCacheHostNames, [callbackAggregator, processPool] {
1073 callbackAggregator->removePendingCallback();
1074 });
1075 }
1076 }
1077
1078 auto webProcessAccessType = computeWebProcessAccessTypeForDataRemoval(dataTypes, !isPersistent());
1079 if (webProcessAccessType != ProcessAccessType::None) {
1080 for (auto& process : processes()) {
1081 switch (webProcessAccessType) {
1082 case ProcessAccessType::OnlyIfLaunched:
1083 if (!process->canSendMessage())
1084 continue;
1085 break;
1086
1087 case ProcessAccessType::Launch:
1088 // FIXME: Handle this.
1089 ASSERT_NOT_REACHED();
1090 break;
1091
1092 case ProcessAccessType::None:
1093 ASSERT_NOT_REACHED();
1094 }
1095
1096 callbackAggregator->addPendingCallback();
1097
1098 process->deleteWebsiteDataForOrigins(m_sessionID, dataTypes, origins, [callbackAggregator] {
1099 callbackAggregator->removePendingCallback();
1100 });
1101 }
1102 }
1103
1104 if (dataTypes.contains(WebsiteDataType::SessionStorage) && m_storageManager) {
1105 callbackAggregator->addPendingCallback();
1106
1107 m_storageManager->deleteSessionStorageEntriesForOrigins(origins, [callbackAggregator] {
1108 callbackAggregator->removePendingCallback();
1109 });
1110 }
1111
1112 if (dataTypes.contains(WebsiteDataType::LocalStorage) && m_storageManager) {
1113 callbackAggregator->addPendingCallback();
1114
1115 m_storageManager->deleteLocalStorageEntriesForOrigins(origins, [callbackAggregator] {
1116 callbackAggregator->removePendingCallback();
1117 });
1118 }
1119
1120 if (dataTypes.contains(WebsiteDataType::DeviceIdHashSalt) || (dataTypes.contains(WebsiteDataType::Cookies))) {
1121 callbackAggregator->addPendingCallback();
1122
1123 m_deviceIdHashSaltStorage->deleteDeviceIdHashSaltForOrigins(origins, [callbackAggregator] {
1124 callbackAggregator->removePendingCallback();
1125 });
1126 }
1127
1128 if (dataTypes.contains(WebsiteDataType::OfflineWebApplicationCache) && isPersistent()) {
1129 HashSet<WebCore::SecurityOriginData> origins;
1130 for (const auto& dataRecord : dataRecords) {
1131 for (const auto& origin : dataRecord.origins)
1132 origins.add(origin);
1133 }
1134
1135 callbackAggregator->addPendingCallback();
1136 m_queue->dispatch([origins = WTFMove(origins), applicationCacheDirectory = m_configuration->applicationCacheDirectory().isolatedCopy(), applicationCacheFlatFileSubdirectoryName = m_configuration->applicationCacheFlatFileSubdirectoryName().isolatedCopy(), callbackAggregator] {
1137 auto storage = WebCore::ApplicationCacheStorage::create(applicationCacheDirectory, applicationCacheFlatFileSubdirectoryName);
1138
1139 for (const auto& origin : origins)
1140 storage->deleteCacheForOrigin(origin.securityOrigin());
1141
1142 WTF::RunLoop::main().dispatch([callbackAggregator] {
1143 callbackAggregator->removePendingCallback();
1144 });
1145 });
1146 }
1147
1148 if (dataTypes.contains(WebsiteDataType::WebSQLDatabases) && isPersistent()) {
1149 HashSet<WebCore::SecurityOriginData> origins;
1150 for (const auto& dataRecord : dataRecords) {
1151 for (const auto& origin : dataRecord.origins)
1152 origins.add(origin);
1153 }
1154
1155 callbackAggregator->addPendingCallback();
1156 m_queue->dispatch([origins = WTFMove(origins), callbackAggregator, webSQLDatabaseDirectory = m_configuration->webSQLDatabaseDirectory().isolatedCopy()] {
1157 auto databaseTracker = WebCore::DatabaseTracker::trackerWithDatabasePath(webSQLDatabaseDirectory);
1158 for (auto& origin : origins)
1159 databaseTracker->deleteOrigin(origin);
1160 RunLoop::main().dispatch([callbackAggregator] {
1161 callbackAggregator->removePendingCallback();
1162 });
1163 });
1164 }
1165
1166 if (dataTypes.contains(WebsiteDataType::MediaKeys) && isPersistent()) {
1167 HashSet<WebCore::SecurityOriginData> origins;
1168 for (const auto& dataRecord : dataRecords) {
1169 for (const auto& origin : dataRecord.origins)
1170 origins.add(origin);
1171 }
1172
1173 callbackAggregator->addPendingCallback();
1174 m_queue->dispatch([mediaKeysStorageDirectory = m_configuration->mediaKeysStorageDirectory().isolatedCopy(), callbackAggregator, origins = WTFMove(origins)] {
1175
1176 removeMediaKeys(mediaKeysStorageDirectory, origins);
1177
1178 RunLoop::main().dispatch([callbackAggregator] {
1179 callbackAggregator->removePendingCallback();
1180 });
1181 });
1182 }
1183
1184 if (dataTypes.contains(WebsiteDataType::Credentials) && isPersistent()) {
1185 for (auto& processPool : processPools()) {
1186 if (!processPool->networkProcess())
1187 continue;
1188
1189 callbackAggregator->addPendingCallback();
1190 WTF::CompletionHandler<void()> completionHandler = [callbackAggregator]() mutable {
1191 callbackAggregator->removePendingCallback();
1192 };
1193 processPool->networkProcess()->sendWithAsyncReply(Messages::NetworkProcess::RemoveCredentialsWithOrigins(origins), WTFMove(completionHandler));
1194 }
1195 }
1196
1197#if ENABLE(NETSCAPE_PLUGIN_API)
1198 if (dataTypes.contains(WebsiteDataType::PlugInData) && isPersistent()) {
1199 Vector<String> hostNames;
1200 for (const auto& dataRecord : dataRecords) {
1201 for (const auto& hostName : dataRecord.pluginDataHostNames)
1202 hostNames.append(hostName);
1203 }
1204
1205
1206 class State {
1207 public:
1208 static void deleteData(Ref<CallbackAggregator>&& callbackAggregator, Vector<PluginModuleInfo>&& plugins, Vector<String>&& hostNames)
1209 {
1210 new State(WTFMove(callbackAggregator), WTFMove(plugins), WTFMove(hostNames));
1211 }
1212
1213 private:
1214 State(Ref<CallbackAggregator>&& callbackAggregator, Vector<PluginModuleInfo>&& plugins, Vector<String>&& hostNames)
1215 : m_callbackAggregator(WTFMove(callbackAggregator))
1216 , m_plugins(WTFMove(plugins))
1217 , m_hostNames(WTFMove(hostNames))
1218 {
1219 m_callbackAggregator->addPendingCallback();
1220
1221 deleteWebsiteDataForNextPlugin();
1222 }
1223
1224 ~State()
1225 {
1226 ASSERT(m_plugins.isEmpty());
1227 }
1228
1229 void deleteWebsiteDataForNextPlugin()
1230 {
1231 if (m_plugins.isEmpty()) {
1232 m_callbackAggregator->removePendingCallback();
1233
1234 delete this;
1235 return;
1236 }
1237
1238 auto plugin = m_plugins.takeLast();
1239 PluginProcessManager::singleton().deleteWebsiteDataForHostNames(plugin, m_hostNames, [this] {
1240 deleteWebsiteDataForNextPlugin();
1241 });
1242 }
1243
1244 Ref<CallbackAggregator> m_callbackAggregator;
1245 Vector<PluginModuleInfo> m_plugins;
1246 Vector<String> m_hostNames;
1247 };
1248
1249 if (!hostNames.isEmpty())
1250 State::deleteData(*callbackAggregator, plugins(), WTFMove(hostNames));
1251 }
1252#endif
1253
1254 // There's a chance that we don't have any pending callbacks. If so, we want to dispatch the completion handler right away.
1255 callbackAggregator->callIfNeeded();
1256}
1257
1258#if ENABLE(RESOURCE_LOAD_STATISTICS)
1259void WebsiteDataStore::setMaxStatisticsEntries(size_t maximumEntryCount, CompletionHandler<void()>&& completionHandler)
1260{
1261 ASSERT(RunLoop::isMain());
1262
1263 auto callbackAggregator = CallbackAggregator::create(WTFMove(completionHandler));
1264
1265 for (auto& processPool : processPools())
1266 processPool->ensureNetworkProcess().setMaxStatisticsEntries(m_sessionID, maximumEntryCount, [processPool, callbackAggregator = callbackAggregator.copyRef()] { });
1267}
1268
1269void WebsiteDataStore::setPruneEntriesDownTo(size_t pruneTargetCount, CompletionHandler<void()>&& completionHandler)
1270{
1271 ASSERT(RunLoop::isMain());
1272
1273 auto callbackAggregator = CallbackAggregator::create(WTFMove(completionHandler));
1274
1275 for (auto& processPool : processPools()) {
1276 processPool->ensureNetworkProcess().setPruneEntriesDownTo(m_sessionID, pruneTargetCount, [processPool, callbackAggregator = callbackAggregator.copyRef()] { });
1277 }
1278}
1279
1280void WebsiteDataStore::setGrandfatheringTime(Seconds seconds, CompletionHandler<void()>&& completionHandler)
1281{
1282 ASSERT(RunLoop::isMain());
1283
1284 auto callbackAggregator = CallbackAggregator::create(WTFMove(completionHandler));
1285
1286 for (auto& processPool : processPools())
1287 processPool->ensureNetworkProcess().setGrandfatheringTime(m_sessionID, seconds, [processPool, callbackAggregator = callbackAggregator.copyRef()] { });
1288}
1289
1290void WebsiteDataStore::setMinimumTimeBetweenDataRecordsRemoval(Seconds seconds, CompletionHandler<void()>&& completionHandler)
1291{
1292 ASSERT(RunLoop::isMain());
1293
1294 auto callbackAggregator = CallbackAggregator::create(WTFMove(completionHandler));
1295
1296 for (auto& processPool : processPools())
1297 processPool->ensureNetworkProcess().setMinimumTimeBetweenDataRecordsRemoval(m_sessionID, seconds, [processPool, callbackAggregator = callbackAggregator.copyRef()] { });
1298}
1299
1300void WebsiteDataStore::dumpResourceLoadStatistics(CompletionHandler<void(const String&)>&& completionHandler)
1301{
1302 ASSERT(RunLoop::isMain());
1303
1304 for (auto& processPool : processPools()) {
1305 if (auto* process = processPool->networkProcess()) {
1306 process->dumpResourceLoadStatistics(m_sessionID, WTFMove(completionHandler));
1307 RELEASE_ASSERT(processPools().size() == 1);
1308 break;
1309 }
1310 }
1311}
1312
1313void WebsiteDataStore::isPrevalentResource(const URL& url, CompletionHandler<void(bool isPrevalent)>&& completionHandler)
1314{
1315 ASSERT(RunLoop::isMain());
1316
1317 if (url.protocolIsAbout() || url.isEmpty()) {
1318 completionHandler(false);
1319 return;
1320 }
1321
1322 for (auto& processPool : processPools()) {
1323 if (auto* process = processPool->networkProcess()) {
1324 process->isPrevalentResource(m_sessionID, WebCore::RegistrableDomain { url }, WTFMove(completionHandler));
1325 RELEASE_ASSERT(processPools().size() == 1);
1326 break;
1327 }
1328 }
1329}
1330
1331void WebsiteDataStore::setPrevalentResource(const URL& url, CompletionHandler<void()>&& completionHandler)
1332{
1333 ASSERT(RunLoop::isMain());
1334
1335 if (url.protocolIsAbout() || url.isEmpty()) {
1336 completionHandler();
1337 return;
1338 }
1339
1340 auto callbackAggregator = CallbackAggregator::create(WTFMove(completionHandler));
1341
1342 for (auto& processPool : processPools()) {
1343 processPool->ensureNetworkProcess().setPrevalentResource(m_sessionID, WebCore::RegistrableDomain { url }, [processPool, callbackAggregator = callbackAggregator.copyRef()] { });
1344 }
1345}
1346
1347void WebsiteDataStore::setPrevalentResourceForDebugMode(const URL& url, CompletionHandler<void()>&& completionHandler)
1348{
1349 ASSERT(RunLoop::isMain());
1350
1351 if (url.protocolIsAbout() || url.isEmpty()) {
1352 completionHandler();
1353 return;
1354 }
1355
1356 auto callbackAggregator = CallbackAggregator::create(WTFMove(completionHandler));
1357
1358 for (auto& processPool : processPools()) {
1359 if (auto* process = processPool->networkProcess())
1360 process->setPrevalentResourceForDebugMode(m_sessionID, WebCore::RegistrableDomain { url }, [processPool, callbackAggregator = callbackAggregator.copyRef()] { });
1361 }
1362}
1363
1364void WebsiteDataStore::isVeryPrevalentResource(const URL& url, CompletionHandler<void(bool isVeryPrevalent)>&& completionHandler)
1365{
1366 ASSERT(RunLoop::isMain());
1367
1368 if (url.protocolIsAbout() || url.isEmpty()) {
1369 completionHandler(false);
1370 return;
1371 }
1372
1373 for (auto& processPool : processPools()) {
1374 if (auto* process = processPool->networkProcess()) {
1375 process->isVeryPrevalentResource(m_sessionID, WebCore::RegistrableDomain { url }, WTFMove(completionHandler));
1376 ASSERT(processPools().size() == 1);
1377 break;
1378 }
1379 }
1380}
1381
1382void WebsiteDataStore::setVeryPrevalentResource(const URL& url, CompletionHandler<void()>&& completionHandler)
1383{
1384 ASSERT(RunLoop::isMain());
1385
1386 if (url.protocolIsAbout() || url.isEmpty()) {
1387 completionHandler();
1388 return;
1389 }
1390
1391 auto callbackAggregator = CallbackAggregator::create(WTFMove(completionHandler));
1392
1393 for (auto& processPool : processPools()) {
1394 if (auto* process = processPool->networkProcess())
1395 process->setVeryPrevalentResource(m_sessionID, WebCore::RegistrableDomain { url }, [processPool, callbackAggregator = callbackAggregator.copyRef()] { });
1396 }
1397}
1398
1399void WebsiteDataStore::setShouldClassifyResourcesBeforeDataRecordsRemoval(bool value, CompletionHandler<void()>&& completionHandler)
1400{
1401 ASSERT(RunLoop::isMain());
1402
1403 auto callbackAggregator = CallbackAggregator::create(WTFMove(completionHandler));
1404
1405 for (auto& processPool : processPools())
1406 processPool->ensureNetworkProcess().setShouldClassifyResourcesBeforeDataRecordsRemoval(m_sessionID, value, [processPool, callbackAggregator = callbackAggregator.copyRef()] { });
1407}
1408
1409void WebsiteDataStore::setSubframeUnderTopFrameDomain(const URL& subFrameURL, const URL& topFrameURL, CompletionHandler<void()>&& completionHandler)
1410{
1411 ASSERT(RunLoop::isMain());
1412
1413 if (subFrameURL.protocolIsAbout() || subFrameURL.isEmpty() || topFrameURL.protocolIsAbout() || topFrameURL.isEmpty()) {
1414 completionHandler();
1415 return;
1416 }
1417
1418 auto callbackAggregator = CallbackAggregator::create(WTFMove(completionHandler));
1419
1420 for (auto& processPool : processPools()) {
1421 if (auto* process = processPool->networkProcess())
1422 process->setSubframeUnderTopFrameDomain(m_sessionID, WebCore::RegistrableDomain { subFrameURL }, WebCore::RegistrableDomain { topFrameURL }, [processPool, callbackAggregator = callbackAggregator.copyRef()] { });
1423 }
1424}
1425
1426void WebsiteDataStore::isRegisteredAsSubFrameUnder(const URL& subFrameURL, const URL& topFrameURL, CompletionHandler<void(bool)>&& completionHandler)
1427{
1428 ASSERT(RunLoop::isMain());
1429
1430 for (auto& processPool : processPools()) {
1431 if (auto* process = processPool->networkProcess()) {
1432 process->isRegisteredAsSubFrameUnder(m_sessionID, WebCore::RegistrableDomain { subFrameURL }, WebCore::RegistrableDomain { topFrameURL }, WTFMove(completionHandler));
1433 ASSERT(processPools().size() == 1);
1434 break;
1435 }
1436 }
1437}
1438
1439void WebsiteDataStore::setSubresourceUnderTopFrameDomain(const URL& subresourceURL, const URL& topFrameURL, CompletionHandler<void()>&& completionHandler)
1440{
1441 ASSERT(RunLoop::isMain());
1442
1443 if (subresourceURL.protocolIsAbout() || subresourceURL.isEmpty() || topFrameURL.protocolIsAbout() || topFrameURL.isEmpty()) {
1444 completionHandler();
1445 return;
1446 }
1447
1448 auto callbackAggregator = CallbackAggregator::create(WTFMove(completionHandler));
1449
1450 for (auto& processPool : processPools()) {
1451 if (auto* process = processPool->networkProcess())
1452 process->setSubresourceUnderTopFrameDomain(m_sessionID, WebCore::RegistrableDomain { subresourceURL }, WebCore::RegistrableDomain { topFrameURL }, [processPool, callbackAggregator = callbackAggregator.copyRef()] { });
1453 }
1454}
1455
1456void WebsiteDataStore::isRegisteredAsSubresourceUnder(const URL& subresourceURL, const URL& topFrameURL, CompletionHandler<void(bool)>&& completionHandler)
1457{
1458 ASSERT(RunLoop::isMain());
1459
1460 for (auto& processPool : processPools()) {
1461 if (auto* process = processPool->networkProcess()) {
1462 process->isRegisteredAsSubresourceUnder(m_sessionID, WebCore::RegistrableDomain { subresourceURL }, WebCore::RegistrableDomain { topFrameURL }, WTFMove(completionHandler));
1463 ASSERT(processPools().size() == 1);
1464 break;
1465 }
1466 }
1467}
1468
1469void WebsiteDataStore::setSubresourceUniqueRedirectTo(const URL& subresourceURL, const URL& urlRedirectedTo, CompletionHandler<void()>&& completionHandler)
1470{
1471 ASSERT(RunLoop::isMain());
1472
1473 if (subresourceURL.protocolIsAbout() || subresourceURL.isEmpty() || urlRedirectedTo.protocolIsAbout() || urlRedirectedTo.isEmpty()) {
1474 completionHandler();
1475 return;
1476 }
1477
1478 auto callbackAggregator = CallbackAggregator::create(WTFMove(completionHandler));
1479
1480 for (auto& processPool : processPools()) {
1481 if (auto* process = processPool->networkProcess())
1482 process->setSubresourceUniqueRedirectTo(m_sessionID, WebCore::RegistrableDomain { subresourceURL }, WebCore::RegistrableDomain { urlRedirectedTo }, [processPool, callbackAggregator = callbackAggregator.copyRef()] { });
1483 }
1484}
1485
1486void WebsiteDataStore::setSubresourceUniqueRedirectFrom(const URL& subresourceURL, const URL& urlRedirectedFrom, CompletionHandler<void()>&& completionHandler)
1487{
1488 ASSERT(RunLoop::isMain());
1489
1490 if (subresourceURL.protocolIsAbout() || subresourceURL.isEmpty() || urlRedirectedFrom.protocolIsAbout() || urlRedirectedFrom.isEmpty()) {
1491 completionHandler();
1492 return;
1493 }
1494
1495 auto callbackAggregator = CallbackAggregator::create(WTFMove(completionHandler));
1496
1497 for (auto& processPool : processPools()) {
1498 if (auto* process = processPool->networkProcess())
1499 process->setSubresourceUniqueRedirectFrom(m_sessionID, WebCore::RegistrableDomain { subresourceURL }, WebCore::RegistrableDomain { urlRedirectedFrom }, [processPool, callbackAggregator = callbackAggregator.copyRef()] { });
1500 }
1501}
1502
1503void WebsiteDataStore::setTopFrameUniqueRedirectTo(const URL& topFrameURL, const URL& urlRedirectedTo, CompletionHandler<void()>&& completionHandler)
1504{
1505 ASSERT(RunLoop::isMain());
1506
1507 if (topFrameURL.protocolIsAbout() || topFrameURL.isEmpty() || urlRedirectedTo.protocolIsAbout() || urlRedirectedTo.isEmpty()) {
1508 completionHandler();
1509 return;
1510 }
1511
1512 auto callbackAggregator = CallbackAggregator::create(WTFMove(completionHandler));
1513
1514 for (auto& processPool : processPools()) {
1515 if (auto* process = processPool->networkProcess())
1516 process->setTopFrameUniqueRedirectTo(m_sessionID, WebCore::RegistrableDomain { topFrameURL }, WebCore::RegistrableDomain { urlRedirectedTo }, [processPool, callbackAggregator = callbackAggregator.copyRef()] { });
1517 }
1518}
1519
1520void WebsiteDataStore::setTopFrameUniqueRedirectFrom(const URL& topFrameURL, const URL& urlRedirectedFrom, CompletionHandler<void()>&& completionHandler)
1521{
1522 ASSERT(RunLoop::isMain());
1523
1524 if (topFrameURL.protocolIsAbout() || topFrameURL.isEmpty() || urlRedirectedFrom.protocolIsAbout() || urlRedirectedFrom.isEmpty()) {
1525 completionHandler();
1526 return;
1527 }
1528
1529 auto callbackAggregator = CallbackAggregator::create(WTFMove(completionHandler));
1530
1531 for (auto& processPool : processPools()) {
1532 if (auto* process = processPool->networkProcess())
1533 process->setTopFrameUniqueRedirectFrom(m_sessionID, WebCore::RegistrableDomain { topFrameURL }, WebCore::RegistrableDomain { urlRedirectedFrom }, [processPool, callbackAggregator = callbackAggregator.copyRef()] { });
1534 }
1535}
1536
1537void WebsiteDataStore::isRegisteredAsRedirectingTo(const URL& urlRedirectedFrom, const URL& urlRedirectedTo, CompletionHandler<void(bool)>&& completionHandler)
1538{
1539 ASSERT(RunLoop::isMain());
1540
1541 for (auto& processPool : processPools()) {
1542 if (auto* process = processPool->networkProcess()) {
1543 process->isRegisteredAsRedirectingTo(m_sessionID, WebCore::RegistrableDomain { urlRedirectedFrom }, WebCore::RegistrableDomain { urlRedirectedTo }, WTFMove(completionHandler));
1544 ASSERT(processPools().size() == 1);
1545 break;
1546 }
1547 }
1548}
1549
1550void WebsiteDataStore::clearPrevalentResource(const URL& url, CompletionHandler<void()>&& completionHandler)
1551{
1552 ASSERT(RunLoop::isMain());
1553
1554 if (url.protocolIsAbout() || url.isEmpty()) {
1555 completionHandler();
1556 return;
1557 }
1558
1559 auto callbackAggregator = CallbackAggregator::create(WTFMove(completionHandler));
1560
1561 for (auto& processPool : processPools()) {
1562 if (auto* process = processPool->networkProcess())
1563 process->clearPrevalentResource(m_sessionID, WebCore::RegistrableDomain { url }, [processPool, callbackAggregator = callbackAggregator.copyRef()] { });
1564 }
1565}
1566
1567void WebsiteDataStore::resetParametersToDefaultValues(CompletionHandler<void()>&& completionHandler)
1568{
1569 ASSERT(RunLoop::isMain());
1570
1571 auto callbackAggregator = CallbackAggregator::create(WTFMove(completionHandler));
1572
1573 for (auto& processPool : processPools()) {
1574 if (auto* process = processPool->networkProcess())
1575 process->resetParametersToDefaultValues(m_sessionID, [processPool, callbackAggregator = callbackAggregator.copyRef()] { });
1576 }
1577}
1578
1579void WebsiteDataStore::submitTelemetry()
1580{
1581 ASSERT(RunLoop::isMain());
1582
1583 for (auto& processPool : processPools()) {
1584 if (auto* process = processPool->networkProcess())
1585 process->submitTelemetry(m_sessionID, [] { });
1586 }
1587}
1588
1589void WebsiteDataStore::scheduleClearInMemoryAndPersistent(WallTime modifiedSince, ShouldGrandfatherStatistics shouldGrandfather, CompletionHandler<void()>&& completionHandler)
1590{
1591 ASSERT(RunLoop::isMain());
1592
1593 auto callbackAggregator = CallbackAggregator::create(WTFMove(completionHandler));
1594
1595 for (auto& processPool : processPools()) {
1596 if (auto* process = processPool->networkProcess())
1597 process->scheduleClearInMemoryAndPersistent(m_sessionID, modifiedSince, shouldGrandfather, [processPool, callbackAggregator = callbackAggregator.copyRef()] { });
1598 }
1599}
1600
1601void WebsiteDataStore::scheduleClearInMemoryAndPersistent(ShouldGrandfatherStatistics shouldGrandfather, CompletionHandler<void()>&& completionHandler)
1602{
1603 ASSERT(RunLoop::isMain());
1604
1605 auto callbackAggregator = CallbackAggregator::create(WTFMove(completionHandler));
1606
1607 for (auto& processPool : processPools()) {
1608 if (auto* process = processPool->networkProcess())
1609 process->scheduleClearInMemoryAndPersistent(m_sessionID, { }, shouldGrandfather, [processPool, callbackAggregator = callbackAggregator.copyRef()] { });
1610 }
1611}
1612
1613void WebsiteDataStore::scheduleCookieBlockingUpdate(CompletionHandler<void()>&& completionHandler)
1614{
1615 auto callbackAggregator = CallbackAggregator::create(WTFMove(completionHandler));
1616
1617 for (auto& processPool : processPools()) {
1618 if (auto* process = processPool->networkProcess())
1619 process->scheduleCookieBlockingUpdate(m_sessionID, [processPool, callbackAggregator = callbackAggregator.copyRef()] { });
1620 }
1621}
1622
1623void WebsiteDataStore::scheduleStatisticsAndDataRecordsProcessing(CompletionHandler<void()>&& completionHandler)
1624{
1625 ASSERT(RunLoop::isMain());
1626
1627 auto callbackAggregator = CallbackAggregator::create(WTFMove(completionHandler));
1628
1629 for (auto& processPool : processPools()) {
1630 if (auto* process = processPool->networkProcess())
1631 process->scheduleStatisticsAndDataRecordsProcessing(m_sessionID, [processPool, callbackAggregator = callbackAggregator.copyRef()] { });
1632 }
1633}
1634
1635void WebsiteDataStore::setLastSeen(const URL& url, Seconds seconds, CompletionHandler<void()>&& completionHandler)
1636{
1637 if (url.protocolIsAbout() || url.isEmpty()) {
1638 completionHandler();
1639 return;
1640 }
1641
1642 auto callbackAggregator = CallbackAggregator::create(WTFMove(completionHandler));
1643
1644 for (auto& processPool : processPools()) {
1645 if (auto* process = processPool->networkProcess())
1646 process->setLastSeen(m_sessionID, WebCore::RegistrableDomain { url }, seconds, [processPool, callbackAggregator = callbackAggregator.copyRef()] { });
1647 }
1648}
1649
1650void WebsiteDataStore::setNotifyPagesWhenDataRecordsWereScanned(bool value, CompletionHandler<void()>&& completionHandler)
1651{
1652 auto callbackAggregator = CallbackAggregator::create(WTFMove(completionHandler));
1653
1654 for (auto& processPool : processPools())
1655 processPool->ensureNetworkProcess().setNotifyPagesWhenDataRecordsWereScanned(m_sessionID, value, [processPool, callbackAggregator = callbackAggregator.copyRef()] { });
1656}
1657
1658void WebsiteDataStore::setIsRunningResourceLoadStatisticsTest(bool value, CompletionHandler<void()>&& completionHandler)
1659{
1660 auto callbackAggregator = CallbackAggregator::create(WTFMove(completionHandler));
1661
1662 for (auto& processPool : processPools())
1663 processPool->ensureNetworkProcess().setIsRunningResourceLoadStatisticsTest(m_sessionID, value, [processPool, callbackAggregator = callbackAggregator.copyRef()] { });
1664}
1665
1666void WebsiteDataStore::setNotifyPagesWhenTelemetryWasCaptured(bool value, CompletionHandler<void()>&& completionHandler)
1667{
1668 auto callbackAggregator = CallbackAggregator::create(WTFMove(completionHandler));
1669
1670 for (auto& processPool : processPools())
1671 processPool->ensureNetworkProcess().setNotifyPagesWhenTelemetryWasCaptured(m_sessionID, value, [processPool, callbackAggregator = callbackAggregator.copyRef()] { });
1672}
1673
1674void WebsiteDataStore::getAllStorageAccessEntries(uint64_t pageID, CompletionHandler<void(Vector<String>&& domains)>&& completionHandler)
1675{
1676 auto* webPage = WebProcessProxy::webPage(pageID);
1677 if (!webPage) {
1678 completionHandler({ });
1679 return;
1680 }
1681
1682 auto& networkProcess = webPage->process().processPool().ensureNetworkProcess();
1683 networkProcess.getAllStorageAccessEntries(m_sessionID, WTFMove(completionHandler));
1684}
1685
1686
1687void WebsiteDataStore::setTimeToLiveUserInteraction(Seconds seconds, CompletionHandler<void()>&& completionHandler)
1688{
1689 auto callbackAggregator = CallbackAggregator::create(WTFMove(completionHandler));
1690
1691 for (auto& processPool : processPools())
1692 processPool->ensureNetworkProcess().setTimeToLiveUserInteraction(m_sessionID, seconds, [callbackAggregator = callbackAggregator.copyRef()] { });
1693}
1694
1695void WebsiteDataStore::logUserInteraction(const URL& url, CompletionHandler<void()>&& completionHandler)
1696{
1697 ASSERT(RunLoop::isMain());
1698
1699 if (url.protocolIsAbout() || url.isEmpty()) {
1700 completionHandler();
1701 return;
1702 }
1703
1704 auto callbackAggregator = CallbackAggregator::create(WTFMove(completionHandler));
1705
1706 for (auto& processPool : processPools()) {
1707 if (auto* process = processPool->networkProcess())
1708 process->logUserInteraction(m_sessionID, WebCore::RegistrableDomain { url }, [callbackAggregator = callbackAggregator.copyRef()] { });
1709 }
1710}
1711
1712void WebsiteDataStore::hasHadUserInteraction(const URL& url, CompletionHandler<void(bool)>&& completionHandler)
1713{
1714 ASSERT(RunLoop::isMain());
1715
1716 if (url.protocolIsAbout() || url.isEmpty()) {
1717 completionHandler(false);
1718 return;
1719 }
1720
1721 for (auto& processPool : processPools()) {
1722 if (auto* process = processPool->networkProcess()) {
1723 process->hasHadUserInteraction(m_sessionID, WebCore::RegistrableDomain { url }, WTFMove(completionHandler));
1724 ASSERT(processPools().size() == 1);
1725 break;
1726 }
1727 }
1728}
1729
1730void WebsiteDataStore::clearUserInteraction(const URL& url, CompletionHandler<void()>&& completionHandler)
1731{
1732 ASSERT(RunLoop::isMain());
1733
1734 if (url.protocolIsAbout() || url.isEmpty()) {
1735 completionHandler();
1736 return;
1737 }
1738
1739 auto callbackAggregator = CallbackAggregator::create(WTFMove(completionHandler));
1740
1741 for (auto& processPool : processPools()) {
1742 if (auto* process = processPool->networkProcess())
1743 process->clearUserInteraction(m_sessionID, WebCore::RegistrableDomain { url }, [callbackAggregator = callbackAggregator.copyRef()] { });
1744 }
1745}
1746
1747void WebsiteDataStore::setGrandfathered(const URL& url, bool isGrandfathered, CompletionHandler<void()>&& completionHandler)
1748{
1749 ASSERT(RunLoop::isMain());
1750
1751 if (url.protocolIsAbout() || url.isEmpty()) {
1752 completionHandler();
1753 return;
1754 }
1755
1756 auto callbackAggregator = CallbackAggregator::create(WTFMove(completionHandler));
1757
1758 for (auto& processPool : processPools()) {
1759 if (auto* process = processPool->networkProcess())
1760 process->setGrandfathered(m_sessionID, WebCore::RegistrableDomain { url }, isGrandfathered, [callbackAggregator = callbackAggregator.copyRef()] { });
1761 }
1762}
1763
1764void WebsiteDataStore::setCrossSiteLoadWithLinkDecorationForTesting(const URL& fromURL, const URL& toURL, CompletionHandler<void()>&& completionHandler)
1765{
1766 ASSERT(RunLoop::isMain());
1767
1768 auto callbackAggregator = CallbackAggregator::create(WTFMove(completionHandler));
1769
1770 for (auto& processPool : processPools()) {
1771 if (auto* process = processPool->networkProcess())
1772 process->setCrossSiteLoadWithLinkDecorationForTesting(m_sessionID, WebCore::RegistrableDomain { fromURL }, WebCore::RegistrableDomain { toURL }, [processPool, callbackAggregator = callbackAggregator.copyRef()] { });
1773 }
1774}
1775
1776void WebsiteDataStore::resetCrossSiteLoadsWithLinkDecorationForTesting(CompletionHandler<void()>&& completionHandler)
1777{
1778 auto callbackAggregator = CallbackAggregator::create(WTFMove(completionHandler));
1779
1780 for (auto& processPool : processPools()) {
1781 if (auto* networkProcess = processPool->networkProcess())
1782 networkProcess->resetCrossSiteLoadsWithLinkDecorationForTesting(m_sessionID, [callbackAggregator = callbackAggregator.copyRef()] { });
1783 }
1784}
1785
1786void WebsiteDataStore::deleteCookiesForTesting(const URL& url, bool includeHttpOnlyCookies, CompletionHandler<void()>&& completionHandler)
1787{
1788 auto callbackAggregator = CallbackAggregator::create(WTFMove(completionHandler));
1789
1790 for (auto& processPool : processPools()) {
1791 if (auto* networkProcess = processPool->networkProcess())
1792 networkProcess->deleteCookiesForTesting(m_sessionID, WebCore::RegistrableDomain { url }, includeHttpOnlyCookies, [callbackAggregator = callbackAggregator.copyRef()] { });
1793 }
1794}
1795
1796void WebsiteDataStore::hasLocalStorageForTesting(const URL& url, CompletionHandler<void(bool)>&& completionHandler) const
1797{
1798 if (!m_storageManager) {
1799 completionHandler(false);
1800 return;
1801 }
1802
1803 m_storageManager->getLocalStorageOrigins([url, completionHandler = WTFMove(completionHandler)](HashSet<WebCore::SecurityOriginData>&& origins) mutable {
1804 for (auto& origin : origins) {
1805 if (origin.host == url.host()) {
1806 completionHandler(true);
1807 return;
1808 }
1809 }
1810
1811 completionHandler(false);
1812 });
1813}
1814#endif // ENABLE(RESOURCE_LOAD_STATISTICS)
1815
1816void WebsiteDataStore::setCacheMaxAgeCapForPrevalentResources(Seconds seconds, CompletionHandler<void()>&& completionHandler)
1817{
1818#if ENABLE(RESOURCE_LOAD_STATISTICS)
1819 auto callbackAggregator = CallbackAggregator::create(WTFMove(completionHandler));
1820
1821 for (auto& processPool : processPools())
1822 processPool->ensureNetworkProcess().setCacheMaxAgeCapForPrevalentResources(m_sessionID, seconds, [callbackAggregator = callbackAggregator.copyRef()] { });
1823#else
1824 UNUSED_PARAM(seconds);
1825 completionHandler();
1826#endif
1827}
1828
1829void WebsiteDataStore::resetCacheMaxAgeCapForPrevalentResources(CompletionHandler<void()>&& completionHandler)
1830{
1831#if ENABLE(RESOURCE_LOAD_STATISTICS)
1832 auto callbackAggregator = CallbackAggregator::create(WTFMove(completionHandler));
1833
1834 for (auto& processPool : processPools()) {
1835 if (auto* networkProcess = processPool->networkProcess())
1836 networkProcess->resetCacheMaxAgeCapForPrevalentResources(m_sessionID, [callbackAggregator = callbackAggregator.copyRef()] { });
1837 }
1838#else
1839 completionHandler();
1840#endif
1841}
1842
1843void WebsiteDataStore::webPageWasAdded(WebPageProxy& webPageProxy)
1844{
1845 if (m_storageManager)
1846 m_storageManager->createSessionStorageNamespace(webPageProxy.pageID(), std::numeric_limits<unsigned>::max());
1847}
1848
1849void WebsiteDataStore::webPageWasInvalidated(WebPageProxy& webPageProxy)
1850{
1851 if (m_storageManager)
1852 m_storageManager->destroySessionStorageNamespace(webPageProxy.pageID());
1853}
1854
1855void WebsiteDataStore::webProcessWillOpenConnection(WebProcessProxy& webProcessProxy, IPC::Connection& connection)
1856{
1857 if (m_storageManager)
1858 m_storageManager->processWillOpenConnection(webProcessProxy, connection);
1859}
1860
1861void WebsiteDataStore::webPageWillOpenConnection(WebPageProxy& webPageProxy, IPC::Connection& connection)
1862{
1863 if (m_storageManager)
1864 m_storageManager->addAllowedSessionStorageNamespaceConnection(webPageProxy.pageID(), connection);
1865}
1866
1867void WebsiteDataStore::webPageDidCloseConnection(WebPageProxy& webPageProxy, IPC::Connection& connection)
1868{
1869 if (m_storageManager)
1870 m_storageManager->removeAllowedSessionStorageNamespaceConnection(webPageProxy.pageID(), connection);
1871}
1872
1873void WebsiteDataStore::webProcessDidCloseConnection(WebProcessProxy& webProcessProxy, IPC::Connection& connection)
1874{
1875 if (m_storageManager)
1876 m_storageManager->processDidCloseConnection(webProcessProxy, connection);
1877}
1878
1879bool WebsiteDataStore::isAssociatedProcessPool(WebProcessPool& processPool) const
1880{
1881 if (auto* processPoolDataStore = processPool.websiteDataStore())
1882 return &processPoolDataStore->websiteDataStore() == this;
1883 return false;
1884}
1885
1886HashSet<RefPtr<WebProcessPool>> WebsiteDataStore::processPools(size_t count, bool ensureAPoolExists) const
1887{
1888 HashSet<RefPtr<WebProcessPool>> processPools;
1889 for (auto& process : processes())
1890 processPools.add(&process->processPool());
1891
1892 if (processPools.isEmpty()) {
1893 // Check if we're one of the legacy data stores.
1894 for (auto& processPool : WebProcessPool::allProcessPools()) {
1895 if (!isAssociatedProcessPool(*processPool))
1896 continue;
1897
1898 processPools.add(processPool);
1899
1900 if (processPools.size() == count)
1901 break;
1902 }
1903 }
1904
1905 if (processPools.isEmpty() && count && ensureAPoolExists) {
1906 auto processPool = WebProcessPool::create(API::ProcessPoolConfiguration::createWithWebsiteDataStoreConfiguration(m_configuration));
1907 processPools.add(processPool.ptr());
1908 }
1909
1910 return processPools;
1911}
1912
1913#if ENABLE(NETSCAPE_PLUGIN_API)
1914Vector<PluginModuleInfo> WebsiteDataStore::plugins() const
1915{
1916 Vector<PluginModuleInfo> plugins;
1917
1918 for (auto& processPool : processPools()) {
1919 for (auto& plugin : processPool->pluginInfoStore().plugins())
1920 plugins.append(plugin);
1921 }
1922
1923 return plugins;
1924}
1925#endif
1926
1927static String computeMediaKeyFile(const String& mediaKeyDirectory)
1928{
1929 return FileSystem::pathByAppendingComponent(mediaKeyDirectory, "SecureStop.plist");
1930}
1931
1932Vector<WebCore::SecurityOriginData> WebsiteDataStore::mediaKeyOrigins(const String& mediaKeysStorageDirectory)
1933{
1934 ASSERT(!mediaKeysStorageDirectory.isEmpty());
1935
1936 Vector<WebCore::SecurityOriginData> origins;
1937
1938 for (const auto& originPath : FileSystem::listDirectory(mediaKeysStorageDirectory, "*")) {
1939 auto mediaKeyFile = computeMediaKeyFile(originPath);
1940 if (!FileSystem::fileExists(mediaKeyFile))
1941 continue;
1942
1943 auto mediaKeyIdentifier = FileSystem::pathGetFileName(originPath);
1944
1945 if (auto securityOrigin = WebCore::SecurityOriginData::fromDatabaseIdentifier(mediaKeyIdentifier))
1946 origins.append(*securityOrigin);
1947 }
1948
1949 return origins;
1950}
1951
1952void WebsiteDataStore::removeMediaKeys(const String& mediaKeysStorageDirectory, WallTime modifiedSince)
1953{
1954 ASSERT(!mediaKeysStorageDirectory.isEmpty());
1955
1956 for (const auto& mediaKeyDirectory : FileSystem::listDirectory(mediaKeysStorageDirectory, "*")) {
1957 auto mediaKeyFile = computeMediaKeyFile(mediaKeyDirectory);
1958
1959 auto modificationTime = FileSystem::getFileModificationTime(mediaKeyFile);
1960 if (!modificationTime)
1961 continue;
1962
1963 if (modificationTime.value() < modifiedSince)
1964 continue;
1965
1966 FileSystem::deleteFile(mediaKeyFile);
1967 FileSystem::deleteEmptyDirectory(mediaKeyDirectory);
1968 }
1969}
1970
1971void WebsiteDataStore::removeMediaKeys(const String& mediaKeysStorageDirectory, const HashSet<WebCore::SecurityOriginData>& origins)
1972{
1973 ASSERT(!mediaKeysStorageDirectory.isEmpty());
1974
1975 for (const auto& origin : origins) {
1976 auto mediaKeyDirectory = FileSystem::pathByAppendingComponent(mediaKeysStorageDirectory, origin.databaseIdentifier());
1977 auto mediaKeyFile = computeMediaKeyFile(mediaKeyDirectory);
1978
1979 FileSystem::deleteFile(mediaKeyFile);
1980 FileSystem::deleteEmptyDirectory(mediaKeyDirectory);
1981 }
1982}
1983
1984bool WebsiteDataStore::resourceLoadStatisticsEnabled() const
1985{
1986#if ENABLE(RESOURCE_LOAD_STATISTICS)
1987 return m_resourceLoadStatisticsEnabled;
1988#else
1989 return false;
1990#endif
1991}
1992
1993bool WebsiteDataStore::resourceLoadStatisticsDebugMode() const
1994{
1995#if ENABLE(RESOURCE_LOAD_STATISTICS)
1996 return m_resourceLoadStatisticsDebugMode;
1997#else
1998 return false;
1999#endif
2000}
2001
2002void WebsiteDataStore::setResourceLoadStatisticsEnabled(bool enabled)
2003{
2004#if ENABLE(RESOURCE_LOAD_STATISTICS)
2005 if (m_sessionID.isEphemeral() || enabled == resourceLoadStatisticsEnabled())
2006 return;
2007
2008 if (enabled) {
2009 enableResourceLoadStatisticsAndSetTestingCallback(nullptr);
2010 return;
2011 }
2012
2013 for (auto& processPool : processPools(std::numeric_limits<size_t>::max(), false)) {
2014 processPool->setResourceLoadStatisticsEnabled(false);
2015 processPool->sendToNetworkingProcess(Messages::NetworkProcess::SetResourceLoadStatisticsEnabled(false));
2016 }
2017#else
2018 UNUSED_PARAM(enabled);
2019#endif
2020}
2021
2022void WebsiteDataStore::setResourceLoadStatisticsDebugMode(bool enabled)
2023{
2024 setResourceLoadStatisticsDebugMode(enabled, []() { });
2025}
2026
2027void WebsiteDataStore::setResourceLoadStatisticsDebugMode(bool enabled, CompletionHandler<void()>&& completionHandler)
2028{
2029#if ENABLE(RESOURCE_LOAD_STATISTICS)
2030 m_resourceLoadStatisticsDebugMode = enabled;
2031
2032 auto callbackAggregator = CallbackAggregator::create(WTFMove(completionHandler));
2033
2034 for (auto& processPool : processPools())
2035 processPool->ensureNetworkProcess().setResourceLoadStatisticsDebugMode(m_sessionID, enabled, [callbackAggregator = callbackAggregator.copyRef()] { });
2036#else
2037 UNUSED_PARAM(enabled);
2038 UNUSED_PARAM(completionHandler);
2039#endif
2040}
2041
2042#if ENABLE(RESOURCE_LOAD_STATISTICS)
2043void WebsiteDataStore::enableResourceLoadStatisticsAndSetTestingCallback(Function<void (const String&)>&& callback)
2044{
2045 ASSERT(!m_sessionID.isEphemeral());
2046
2047 m_resourceLoadStatisticsEnabled = true;
2048 setStatisticsTestingCallback(WTFMove(callback));
2049
2050 resolveDirectoriesIfNecessary();
2051
2052 for (auto& processPool : processPools(std::numeric_limits<size_t>::max(), false)) {
2053 processPool->setResourceLoadStatisticsEnabled(true);
2054 processPool->sendToNetworkingProcess(Messages::NetworkProcess::SetResourceLoadStatisticsEnabled(true));
2055 }
2056}
2057
2058void WebsiteDataStore::logTestingEvent(const String& event)
2059{
2060 ASSERT(RunLoop::isMain());
2061
2062 if (m_statisticsTestingCallback)
2063 m_statisticsTestingCallback(event);
2064}
2065
2066void WebsiteDataStore::clearResourceLoadStatisticsInWebProcesses(CompletionHandler<void()>&& callback)
2067{
2068 if (resourceLoadStatisticsEnabled()) {
2069 for (auto& processPool : processPools())
2070 processPool->clearResourceLoadStatistics();
2071 }
2072 callback();
2073}
2074#endif
2075
2076Vector<WebCore::Cookie> WebsiteDataStore::pendingCookies() const
2077{
2078 return copyToVector(m_pendingCookies);
2079}
2080
2081void WebsiteDataStore::addPendingCookie(const WebCore::Cookie& cookie)
2082{
2083 m_pendingCookies.add(cookie);
2084}
2085
2086void WebsiteDataStore::removePendingCookie(const WebCore::Cookie& cookie)
2087{
2088 m_pendingCookies.remove(cookie);
2089}
2090
2091void WebsiteDataStore::clearPendingCookies()
2092{
2093 m_pendingCookies.clear();
2094}
2095
2096uint64_t WebsiteDataStore::perThirdPartyOriginStorageQuota() const
2097{
2098 // FIXME: Consider whether allowing to set a perThirdPartyOriginStorageQuota from a WebsiteDataStore.
2099 return WebCore::StorageQuotaManager::defaultThirdPartyQuotaFromPerOriginQuota(perOriginStorageQuota());
2100}
2101
2102#if !PLATFORM(COCOA)
2103WebsiteDataStoreParameters WebsiteDataStore::parameters()
2104{
2105 WebsiteDataStoreParameters parameters;
2106 parameters.networkSessionParameters.sessionID = m_sessionID;
2107
2108 resolveDirectoriesIfNecessary();
2109
2110#if ENABLE(INDEXED_DATABASE)
2111 parameters.indexedDatabaseDirectory = resolvedIndexedDatabaseDirectory();
2112 if (!parameters.indexedDatabaseDirectory.isEmpty())
2113 SandboxExtension::createHandleForReadWriteDirectory(parameters.indexedDatabaseDirectory, parameters.indexedDatabaseDirectoryExtensionHandle);
2114#endif
2115
2116#if ENABLE(SERVICE_WORKER)
2117 parameters.serviceWorkerRegistrationDirectory = resolvedServiceWorkerRegistrationDirectory();
2118 if (!parameters.serviceWorkerRegistrationDirectory.isEmpty())
2119 SandboxExtension::createHandleForReadWriteDirectory(parameters.serviceWorkerRegistrationDirectory, parameters.serviceWorkerRegistrationDirectoryExtensionHandle);
2120#endif
2121
2122 parameters.perOriginStorageQuota = perOriginStorageQuota();
2123 parameters.perThirdPartyOriginStorageQuota = perThirdPartyOriginStorageQuota();
2124
2125 platformSetNetworkParameters(parameters);
2126
2127 return parameters;
2128}
2129#endif
2130
2131#if HAVE(SEC_KEY_PROXY)
2132void WebsiteDataStore::addSecKeyProxyStore(Ref<SecKeyProxyStore>&& store)
2133{
2134 m_secKeyProxyStores.append(WTFMove(store));
2135}
2136#endif
2137
2138#if ENABLE(WEB_AUTHN)
2139void WebsiteDataStore::setMockWebAuthenticationConfiguration(MockWebAuthenticationConfiguration&& configuration)
2140{
2141 if (!m_authenticatorManager->isMock()) {
2142 m_authenticatorManager = makeUniqueRef<MockAuthenticatorManager>(WTFMove(configuration));
2143 return;
2144 }
2145 static_cast<MockAuthenticatorManager*>(&m_authenticatorManager)->setTestConfiguration(WTFMove(configuration));
2146}
2147#endif
2148
2149API::HTTPCookieStore& WebsiteDataStore::cookieStore()
2150{
2151 if (!m_cookieStore)
2152 m_cookieStore = API::HTTPCookieStore::create(*this);
2153
2154 return *m_cookieStore;
2155}
2156
2157void WebsiteDataStore::didCreateNetworkProcess()
2158{
2159}
2160
2161bool WebsiteDataStore::setSourceApplicationSecondaryIdentifier(String&& identifier)
2162{
2163 if (m_networkingHasBegun)
2164 return false;
2165 m_sourceApplicationSecondaryIdentifier = WTFMove(identifier);
2166 return true;
2167}
2168
2169bool WebsiteDataStore::setAllowsTLSFallback(bool allows)
2170{
2171 if (m_networkingHasBegun)
2172 return false;
2173 m_allowsTLSFallback = allows;
2174 return true;
2175}
2176
2177bool WebsiteDataStore::setSourceApplicationBundleIdentifier(String&& identifier)
2178{
2179 if (m_networkingHasBegun)
2180 return false;
2181 m_sourceApplicationBundleIdentifier = WTFMove(identifier);
2182 return true;
2183}
2184
2185}
2186