Split memory store logic out of WebResourceLoadStatisticsStore to clarify threading...
authorcdumez@apple.com <cdumez@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Thu, 28 Jun 2018 17:07:36 +0000 (17:07 +0000)
committercdumez@apple.com <cdumez@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Thu, 28 Jun 2018 17:07:36 +0000 (17:07 +0000)
https://bugs.webkit.org/show_bug.cgi?id=187055
<rdar://problem/41584026>

Reviewed by Brent Fulgham.

Split memory store logic out of WebResourceLoadStatisticsStore and into a ResourceLoadStatisticsMemoryStore class
to clarify the threading model. Previously, some of the methods of the WebResourceLoadStatisticsStore had to be
called on the main thread and some of them on the background queue, which was confusing and error prone. Now,
all WebResourceLoadStatisticsStore methods (except for IPC ones which will be addressed in a follow-up) are called
on the main thread. The ResourceLoadStatisticsMemoryStore objects is constructed / used and destroyed on the
background queue, similarly to the ResourceLoadStatisticsPersistentStore. The WebResourceLoadStatisticsStore
objects merely proxies calls from WebKit to those persistent / memory stores and takes care of hopping back and
forth between the background thread and the work queue.

While spliting code code, I found several instances where we were calling completion handlers on the wrong thread.
I fixed those in this patch now that the model is clearer.

We can likely clean up (organize the code a bit better) in a follow-up). This patch takes care of splitting the
code as it was. Code that was called on the background queue was moved to ResourceLoadStatisticsMemoryStore class
and code that was called on the main thread stays in WebResourceLoadStatisticsStore.

* CMakeLists.txt:
* UIProcess/Cocoa/ResourceLoadStatisticsMemoryStoreCocoa.mm: Renamed from Source/WebKit/UIProcess/Cocoa/WebResourceLoadStatisticsStoreCocoa.mm.
(WebKit::ResourceLoadStatisticsMemoryStore::registerUserDefaultsIfNeeded):
* UIProcess/ResourceLoadStatisticsMemoryStore.cpp: Added.
(WebKit::appendWithDelimiter):
(WebKit::OperatingDate::fromWallTime):
(WebKit::OperatingDate::today):
(WebKit::OperatingDate::secondsSinceEpoch const):
(WebKit::OperatingDate::operator== const):
(WebKit::OperatingDate::operator< const):
(WebKit::OperatingDate::operator<= const):
(WebKit::OperatingDate::OperatingDate):
(WebKit::mergeOperatingDates):
(WebKit::pruneResources):
(WebKit::computeImportance):
(WebKit::ResourceLoadStatisticsMemoryStore::ResourceLoadStatisticsMemoryStore):
(WebKit::ResourceLoadStatisticsMemoryStore::~ResourceLoadStatisticsMemoryStore):
(WebKit::ResourceLoadStatisticsMemoryStore::setPersistentStorage):
(WebKit::ResourceLoadStatisticsMemoryStore::calculateAndSubmitTelemetry):
(WebKit::ResourceLoadStatisticsMemoryStore::setNotifyPagesWhenDataRecordsWereScanned):
(WebKit::ResourceLoadStatisticsMemoryStore::setShouldClassifyResourcesBeforeDataRecordsRemoval):
(WebKit::ResourceLoadStatisticsMemoryStore::setShouldSubmitTelemetry):
(WebKit::ResourceLoadStatisticsMemoryStore::removeDataRecords):
(WebKit::ResourceLoadStatisticsMemoryStore::recursivelyGetAllDomainsThatHaveRedirectedToThisDomain):
(WebKit::ResourceLoadStatisticsMemoryStore::markAsPrevalentIfHasRedirectedToPrevalent):
(WebKit::ResourceLoadStatisticsMemoryStore::processStatisticsAndDataRecords):
(WebKit::ResourceLoadStatisticsMemoryStore::hasStorageAccess):
(WebKit::ResourceLoadStatisticsMemoryStore::requestStorageAccess):
(WebKit::ResourceLoadStatisticsMemoryStore::requestStorageAccessUnderOpener):
(WebKit::ResourceLoadStatisticsMemoryStore::grantStorageAccess):
(WebKit::ResourceLoadStatisticsMemoryStore::grantStorageAccessInternal):
(WebKit::ResourceLoadStatisticsMemoryStore::grandfatherExistingWebsiteData):
(WebKit::ResourceLoadStatisticsMemoryStore::setResourceLoadStatisticsDebugMode):
(WebKit::ResourceLoadStatisticsMemoryStore::scheduleStatisticsProcessingRequestIfNecessary):
(WebKit::ResourceLoadStatisticsMemoryStore::cancelPendingStatisticsProcessingRequest):
(WebKit::ResourceLoadStatisticsMemoryStore::logFrameNavigation):
(WebKit::ResourceLoadStatisticsMemoryStore::logUserInteraction):
(WebKit::ResourceLoadStatisticsMemoryStore::logNonRecentUserInteraction):
(WebKit::ResourceLoadStatisticsMemoryStore::clearUserInteraction):
(WebKit::ResourceLoadStatisticsMemoryStore::hasHadUserInteraction):
(WebKit::ResourceLoadStatisticsMemoryStore::setPrevalentResource):
(WebKit::ResourceLoadStatisticsMemoryStore::isPrevalentResource const):
(WebKit::ResourceLoadStatisticsMemoryStore::isVeryPrevalentResource const):
(WebKit::ResourceLoadStatisticsMemoryStore::isRegisteredAsSubFrameUnder):
(WebKit::ResourceLoadStatisticsMemoryStore::isRegisteredAsRedirectingTo):
(WebKit::ResourceLoadStatisticsMemoryStore::clearPrevalentResource):
(WebKit::ResourceLoadStatisticsMemoryStore::setGrandfathered):
(WebKit::ResourceLoadStatisticsMemoryStore::isGrandfathered const):
(WebKit::ResourceLoadStatisticsMemoryStore::setSubframeUnderTopFrameOrigin):
(WebKit::ResourceLoadStatisticsMemoryStore::setSubresourceUnderTopFrameOrigin):
(WebKit::ResourceLoadStatisticsMemoryStore::setSubresourceUniqueRedirectTo):
(WebKit::ResourceLoadStatisticsMemoryStore::setSubresourceUniqueRedirectFrom):
(WebKit::ResourceLoadStatisticsMemoryStore::setTopFrameUniqueRedirectTo):
(WebKit::ResourceLoadStatisticsMemoryStore::setTopFrameUniqueRedirectFrom):
(WebKit::ResourceLoadStatisticsMemoryStore::setTimeToLiveUserInteraction):
(WebKit::ResourceLoadStatisticsMemoryStore::setTimeToLiveCookiePartitionFree):
(WebKit::ResourceLoadStatisticsMemoryStore::setMinimumTimeBetweenDataRecordsRemoval):
(WebKit::ResourceLoadStatisticsMemoryStore::setGrandfatheringTime):
(WebKit::ResourceLoadStatisticsMemoryStore::shouldRemoveDataRecords const):
(WebKit::ResourceLoadStatisticsMemoryStore::setDataRecordsBeingRemoved):
(WebKit::ResourceLoadStatisticsMemoryStore::ensureResourceStatisticsForPrimaryDomain):
(WebKit::ResourceLoadStatisticsMemoryStore::createEncoderFromData const):
(WebKit::ResourceLoadStatisticsMemoryStore::mergeWithDataFromDecoder):
(WebKit::ResourceLoadStatisticsMemoryStore::clear):
(WebKit::ResourceLoadStatisticsMemoryStore::wasAccessedAsFirstPartyDueToUserInteraction):
(WebKit::ResourceLoadStatisticsMemoryStore::mergeStatistics):
(WebKit::ResourceLoadStatisticsMemoryStore::shouldPartitionCookies):
(WebKit::ResourceLoadStatisticsMemoryStore::shouldBlockCookies):
(WebKit::ResourceLoadStatisticsMemoryStore::hasUserGrantedStorageAccessThroughPrompt):
(WebKit::ResourceLoadStatisticsMemoryStore::updateCookiePartitioning):
(WebKit::ResourceLoadStatisticsMemoryStore::updateCookiePartitioningForDomains):
(WebKit::ResourceLoadStatisticsMemoryStore::clearPartitioningStateForDomains):
(WebKit::ResourceLoadStatisticsMemoryStore::resetCookiePartitioningState):
(WebKit::ResourceLoadStatisticsMemoryStore::processStatistics const):
(WebKit::ResourceLoadStatisticsMemoryStore::hasHadUnexpiredRecentUserInteraction const):
(WebKit::ResourceLoadStatisticsMemoryStore::topPrivatelyControlledDomainsToRemoveWebsiteDataFor):
(WebKit::ResourceLoadStatisticsMemoryStore::includeTodayAsOperatingDateIfNecessary):
(WebKit::ResourceLoadStatisticsMemoryStore::hasStatisticsExpired const):
(WebKit::ResourceLoadStatisticsMemoryStore::setMaxStatisticsEntries):
(WebKit::ResourceLoadStatisticsMemoryStore::setPruneEntriesDownTo):
(WebKit::ResourceLoadStatisticsMemoryStore::pruneStatisticsIfNeeded):
(WebKit::ResourceLoadStatisticsMemoryStore::resetParametersToDefaultValues):
(WebKit::ResourceLoadStatisticsMemoryStore::logTestingEvent):
(WebKit::ResourceLoadStatisticsMemoryStore::setLastSeen):
(WebKit::ResourceLoadStatisticsMemoryStore::setVeryPrevalentResource):
(WebKit::ResourceLoadStatisticsMemoryStore::removeAllStorageAccess):
* UIProcess/ResourceLoadStatisticsMemoryStore.h: Added.
(WebKit::ResourceLoadStatisticsMemoryStore::isEmpty const):
(WebKit::ResourceLoadStatisticsMemoryStore::setStorageAccessPromptsEnabled):
(WebKit::ResourceLoadStatisticsMemoryStore::setDebugLogggingEnabled):
* UIProcess/ResourceLoadStatisticsPersistentStorage.cpp:
(WebKit::ResourceLoadStatisticsPersistentStorage::ResourceLoadStatisticsPersistentStorage):
(WebKit::ResourceLoadStatisticsPersistentStorage::startMonitoringDisk):
(WebKit::ResourceLoadStatisticsPersistentStorage::monitorDirectoryForNewStatistics):
(WebKit::ResourceLoadStatisticsPersistentStorage::scheduleOrWriteMemoryStore):
* UIProcess/ResourceLoadStatisticsPersistentStorage.h:
* UIProcess/WebResourceLoadStatisticsStore.cpp:
(WebKit::WebResourceLoadStatisticsStore::setNotifyPagesWhenDataRecordsWereScanned):
(WebKit::WebResourceLoadStatisticsStore::setShouldClassifyResourcesBeforeDataRecordsRemoval):
(WebKit::WebResourceLoadStatisticsStore::setShouldSubmitTelemetry):
(WebKit::WebResourceLoadStatisticsStore::WebResourceLoadStatisticsStore):
(WebKit::WebResourceLoadStatisticsStore::flushAndDestroyPersistentStore):
(WebKit::WebResourceLoadStatisticsStore::setResourceLoadStatisticsDebugMode):
(WebKit::WebResourceLoadStatisticsStore::scheduleStatisticsAndDataRecordsProcessing):
(WebKit::WebResourceLoadStatisticsStore::resourceLoadStatisticsUpdated):
(WebKit::WebResourceLoadStatisticsStore::hasStorageAccess):
(WebKit::WebResourceLoadStatisticsStore::requestStorageAccess):
(WebKit::WebResourceLoadStatisticsStore::requestStorageAccessUnderOpener):
(WebKit::WebResourceLoadStatisticsStore::grantStorageAccess):
(WebKit::WebResourceLoadStatisticsStore::callGrantStorageAccessHandler):
(WebKit::WebResourceLoadStatisticsStore::removeAllStorageAccess):
(WebKit::WebResourceLoadStatisticsStore::performDailyTasks):
(WebKit::WebResourceLoadStatisticsStore::submitTelemetry):
(WebKit::WebResourceLoadStatisticsStore::logFrameNavigation):
(WebKit::WebResourceLoadStatisticsStore::logUserInteraction):
(WebKit::WebResourceLoadStatisticsStore::logNonRecentUserInteraction):
(WebKit::WebResourceLoadStatisticsStore::clearUserInteraction):
(WebKit::WebResourceLoadStatisticsStore::hasHadUserInteraction):
(WebKit::WebResourceLoadStatisticsStore::setLastSeen):
(WebKit::WebResourceLoadStatisticsStore::setPrevalentResource):
(WebKit::WebResourceLoadStatisticsStore::setVeryPrevalentResource):
(WebKit::WebResourceLoadStatisticsStore::isPrevalentResource):
(WebKit::WebResourceLoadStatisticsStore::isVeryPrevalentResource):
(WebKit::WebResourceLoadStatisticsStore::isRegisteredAsSubFrameUnder):
(WebKit::WebResourceLoadStatisticsStore::isRegisteredAsRedirectingTo):
(WebKit::WebResourceLoadStatisticsStore::clearPrevalentResource):
(WebKit::WebResourceLoadStatisticsStore::setGrandfathered):
(WebKit::WebResourceLoadStatisticsStore::isGrandfathered):
(WebKit::WebResourceLoadStatisticsStore::setSubframeUnderTopFrameOrigin):
(WebKit::WebResourceLoadStatisticsStore::setSubresourceUnderTopFrameOrigin):
(WebKit::WebResourceLoadStatisticsStore::setSubresourceUniqueRedirectTo):
(WebKit::WebResourceLoadStatisticsStore::setSubresourceUniqueRedirectFrom):
(WebKit::WebResourceLoadStatisticsStore::setTopFrameUniqueRedirectTo):
(WebKit::WebResourceLoadStatisticsStore::setTopFrameUniqueRedirectFrom):
(WebKit::WebResourceLoadStatisticsStore::scheduleCookiePartitioningUpdate):
(WebKit::WebResourceLoadStatisticsStore::scheduleCookiePartitioningUpdateForDomains):
(WebKit::WebResourceLoadStatisticsStore::scheduleClearPartitioningStateForDomains):
(WebKit::WebResourceLoadStatisticsStore::scheduleCookiePartitioningStateReset):
(WebKit::WebResourceLoadStatisticsStore::scheduleClearInMemory):
(WebKit::WebResourceLoadStatisticsStore::scheduleClearInMemoryAndPersistent):
(WebKit::WebResourceLoadStatisticsStore::setTimeToLiveUserInteraction):
(WebKit::WebResourceLoadStatisticsStore::setTimeToLiveCookiePartitionFree):
(WebKit::WebResourceLoadStatisticsStore::setMinimumTimeBetweenDataRecordsRemoval):
(WebKit::WebResourceLoadStatisticsStore::setGrandfatheringTime):
(WebKit::WebResourceLoadStatisticsStore::callUpdatePrevalentDomainsToPartitionOrBlockCookiesHandler):
(WebKit::WebResourceLoadStatisticsStore::callRemoveDomainsHandler):
(WebKit::WebResourceLoadStatisticsStore::setMaxStatisticsEntries):
(WebKit::WebResourceLoadStatisticsStore::setPruneEntriesDownTo):
(WebKit::WebResourceLoadStatisticsStore::resetParametersToDefaultValues):
(WebKit::WebResourceLoadStatisticsStore::logTestingEvent):
* UIProcess/WebResourceLoadStatisticsStore.h:
* UIProcess/WebResourceLoadStatisticsTelemetry.cpp:
(WebKit::sortedPrevalentResourceTelemetry):
(WebKit::WebResourceLoadStatisticsTelemetry::calculateAndSubmit):
* UIProcess/WebResourceLoadStatisticsTelemetry.h:
* WebKit.xcodeproj/project.pbxproj:

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@233310 268f45cc-cd09-0410-ab3c-d52691b4dbfc

12 files changed:
Source/WebKit/CMakeLists.txt
Source/WebKit/ChangeLog
Source/WebKit/UIProcess/Cocoa/ResourceLoadStatisticsMemoryStoreCocoa.mm [moved from Source/WebKit/UIProcess/Cocoa/WebResourceLoadStatisticsStoreCocoa.mm with 93% similarity]
Source/WebKit/UIProcess/ResourceLoadStatisticsMemoryStore.cpp [new file with mode: 0644]
Source/WebKit/UIProcess/ResourceLoadStatisticsMemoryStore.h [new file with mode: 0644]
Source/WebKit/UIProcess/ResourceLoadStatisticsPersistentStorage.cpp
Source/WebKit/UIProcess/ResourceLoadStatisticsPersistentStorage.h
Source/WebKit/UIProcess/WebResourceLoadStatisticsStore.cpp
Source/WebKit/UIProcess/WebResourceLoadStatisticsStore.h
Source/WebKit/UIProcess/WebResourceLoadStatisticsTelemetry.cpp
Source/WebKit/UIProcess/WebResourceLoadStatisticsTelemetry.h
Source/WebKit/WebKit.xcodeproj/project.pbxproj

index 422e524..6cab2d7 100644 (file)
@@ -308,6 +308,7 @@ set(WebKit_SOURCES
     UIProcess/ProcessAssertion.cpp
     UIProcess/ProcessThrottler.cpp
     UIProcess/RemoteWebInspectorProxy.cpp
+    UIProcess/ResourceLoadStatisticsMemoryStore.cpp
     UIProcess/ResourceLoadStatisticsPersistentStorage.cpp
     UIProcess/ResponsivenessTimer.cpp
     UIProcess/ServiceWorkerProcessProxy.cpp
index c863b2e..23ab0f5 100644 (file)
@@ -1,3 +1,184 @@
+2018-06-28  Chris Dumez  <cdumez@apple.com>
+
+        Split memory store logic out of WebResourceLoadStatisticsStore to clarify threading model
+        https://bugs.webkit.org/show_bug.cgi?id=187055
+        <rdar://problem/41584026>
+
+        Reviewed by Brent Fulgham.
+
+        Split memory store logic out of WebResourceLoadStatisticsStore and into a ResourceLoadStatisticsMemoryStore class
+        to clarify the threading model. Previously, some of the methods of the WebResourceLoadStatisticsStore had to be
+        called on the main thread and some of them on the background queue, which was confusing and error prone. Now,
+        all WebResourceLoadStatisticsStore methods (except for IPC ones which will be addressed in a follow-up) are called
+        on the main thread. The ResourceLoadStatisticsMemoryStore objects is constructed / used and destroyed on the
+        background queue, similarly to the ResourceLoadStatisticsPersistentStore. The WebResourceLoadStatisticsStore
+        objects merely proxies calls from WebKit to those persistent / memory stores and takes care of hopping back and
+        forth between the background thread and the work queue.
+
+        While spliting code code, I found several instances where we were calling completion handlers on the wrong thread.
+        I fixed those in this patch now that the model is clearer.
+
+        We can likely clean up (organize the code a bit better) in a follow-up). This patch takes care of splitting the
+        code as it was. Code that was called on the background queue was moved to ResourceLoadStatisticsMemoryStore class
+        and code that was called on the main thread stays in WebResourceLoadStatisticsStore.
+
+        * CMakeLists.txt:
+        * UIProcess/Cocoa/ResourceLoadStatisticsMemoryStoreCocoa.mm: Renamed from Source/WebKit/UIProcess/Cocoa/WebResourceLoadStatisticsStoreCocoa.mm.
+        (WebKit::ResourceLoadStatisticsMemoryStore::registerUserDefaultsIfNeeded):
+        * UIProcess/ResourceLoadStatisticsMemoryStore.cpp: Added.
+        (WebKit::appendWithDelimiter):
+        (WebKit::OperatingDate::fromWallTime):
+        (WebKit::OperatingDate::today):
+        (WebKit::OperatingDate::secondsSinceEpoch const):
+        (WebKit::OperatingDate::operator== const):
+        (WebKit::OperatingDate::operator< const):
+        (WebKit::OperatingDate::operator<= const):
+        (WebKit::OperatingDate::OperatingDate):
+        (WebKit::mergeOperatingDates):
+        (WebKit::pruneResources):
+        (WebKit::computeImportance):
+        (WebKit::ResourceLoadStatisticsMemoryStore::ResourceLoadStatisticsMemoryStore):
+        (WebKit::ResourceLoadStatisticsMemoryStore::~ResourceLoadStatisticsMemoryStore):
+        (WebKit::ResourceLoadStatisticsMemoryStore::setPersistentStorage):
+        (WebKit::ResourceLoadStatisticsMemoryStore::calculateAndSubmitTelemetry):
+        (WebKit::ResourceLoadStatisticsMemoryStore::setNotifyPagesWhenDataRecordsWereScanned):
+        (WebKit::ResourceLoadStatisticsMemoryStore::setShouldClassifyResourcesBeforeDataRecordsRemoval):
+        (WebKit::ResourceLoadStatisticsMemoryStore::setShouldSubmitTelemetry):
+        (WebKit::ResourceLoadStatisticsMemoryStore::removeDataRecords):
+        (WebKit::ResourceLoadStatisticsMemoryStore::recursivelyGetAllDomainsThatHaveRedirectedToThisDomain):
+        (WebKit::ResourceLoadStatisticsMemoryStore::markAsPrevalentIfHasRedirectedToPrevalent):
+        (WebKit::ResourceLoadStatisticsMemoryStore::processStatisticsAndDataRecords):
+        (WebKit::ResourceLoadStatisticsMemoryStore::hasStorageAccess):
+        (WebKit::ResourceLoadStatisticsMemoryStore::requestStorageAccess):
+        (WebKit::ResourceLoadStatisticsMemoryStore::requestStorageAccessUnderOpener):
+        (WebKit::ResourceLoadStatisticsMemoryStore::grantStorageAccess):
+        (WebKit::ResourceLoadStatisticsMemoryStore::grantStorageAccessInternal):
+        (WebKit::ResourceLoadStatisticsMemoryStore::grandfatherExistingWebsiteData):
+        (WebKit::ResourceLoadStatisticsMemoryStore::setResourceLoadStatisticsDebugMode):
+        (WebKit::ResourceLoadStatisticsMemoryStore::scheduleStatisticsProcessingRequestIfNecessary):
+        (WebKit::ResourceLoadStatisticsMemoryStore::cancelPendingStatisticsProcessingRequest):
+        (WebKit::ResourceLoadStatisticsMemoryStore::logFrameNavigation):
+        (WebKit::ResourceLoadStatisticsMemoryStore::logUserInteraction):
+        (WebKit::ResourceLoadStatisticsMemoryStore::logNonRecentUserInteraction):
+        (WebKit::ResourceLoadStatisticsMemoryStore::clearUserInteraction):
+        (WebKit::ResourceLoadStatisticsMemoryStore::hasHadUserInteraction):
+        (WebKit::ResourceLoadStatisticsMemoryStore::setPrevalentResource):
+        (WebKit::ResourceLoadStatisticsMemoryStore::isPrevalentResource const):
+        (WebKit::ResourceLoadStatisticsMemoryStore::isVeryPrevalentResource const):
+        (WebKit::ResourceLoadStatisticsMemoryStore::isRegisteredAsSubFrameUnder):
+        (WebKit::ResourceLoadStatisticsMemoryStore::isRegisteredAsRedirectingTo):
+        (WebKit::ResourceLoadStatisticsMemoryStore::clearPrevalentResource):
+        (WebKit::ResourceLoadStatisticsMemoryStore::setGrandfathered):
+        (WebKit::ResourceLoadStatisticsMemoryStore::isGrandfathered const):
+        (WebKit::ResourceLoadStatisticsMemoryStore::setSubframeUnderTopFrameOrigin):
+        (WebKit::ResourceLoadStatisticsMemoryStore::setSubresourceUnderTopFrameOrigin):
+        (WebKit::ResourceLoadStatisticsMemoryStore::setSubresourceUniqueRedirectTo):
+        (WebKit::ResourceLoadStatisticsMemoryStore::setSubresourceUniqueRedirectFrom):
+        (WebKit::ResourceLoadStatisticsMemoryStore::setTopFrameUniqueRedirectTo):
+        (WebKit::ResourceLoadStatisticsMemoryStore::setTopFrameUniqueRedirectFrom):
+        (WebKit::ResourceLoadStatisticsMemoryStore::setTimeToLiveUserInteraction):
+        (WebKit::ResourceLoadStatisticsMemoryStore::setTimeToLiveCookiePartitionFree):
+        (WebKit::ResourceLoadStatisticsMemoryStore::setMinimumTimeBetweenDataRecordsRemoval):
+        (WebKit::ResourceLoadStatisticsMemoryStore::setGrandfatheringTime):
+        (WebKit::ResourceLoadStatisticsMemoryStore::shouldRemoveDataRecords const):
+        (WebKit::ResourceLoadStatisticsMemoryStore::setDataRecordsBeingRemoved):
+        (WebKit::ResourceLoadStatisticsMemoryStore::ensureResourceStatisticsForPrimaryDomain):
+        (WebKit::ResourceLoadStatisticsMemoryStore::createEncoderFromData const):
+        (WebKit::ResourceLoadStatisticsMemoryStore::mergeWithDataFromDecoder):
+        (WebKit::ResourceLoadStatisticsMemoryStore::clear):
+        (WebKit::ResourceLoadStatisticsMemoryStore::wasAccessedAsFirstPartyDueToUserInteraction):
+        (WebKit::ResourceLoadStatisticsMemoryStore::mergeStatistics):
+        (WebKit::ResourceLoadStatisticsMemoryStore::shouldPartitionCookies):
+        (WebKit::ResourceLoadStatisticsMemoryStore::shouldBlockCookies):
+        (WebKit::ResourceLoadStatisticsMemoryStore::hasUserGrantedStorageAccessThroughPrompt):
+        (WebKit::ResourceLoadStatisticsMemoryStore::updateCookiePartitioning):
+        (WebKit::ResourceLoadStatisticsMemoryStore::updateCookiePartitioningForDomains):
+        (WebKit::ResourceLoadStatisticsMemoryStore::clearPartitioningStateForDomains):
+        (WebKit::ResourceLoadStatisticsMemoryStore::resetCookiePartitioningState):
+        (WebKit::ResourceLoadStatisticsMemoryStore::processStatistics const):
+        (WebKit::ResourceLoadStatisticsMemoryStore::hasHadUnexpiredRecentUserInteraction const):
+        (WebKit::ResourceLoadStatisticsMemoryStore::topPrivatelyControlledDomainsToRemoveWebsiteDataFor):
+        (WebKit::ResourceLoadStatisticsMemoryStore::includeTodayAsOperatingDateIfNecessary):
+        (WebKit::ResourceLoadStatisticsMemoryStore::hasStatisticsExpired const):
+        (WebKit::ResourceLoadStatisticsMemoryStore::setMaxStatisticsEntries):
+        (WebKit::ResourceLoadStatisticsMemoryStore::setPruneEntriesDownTo):
+        (WebKit::ResourceLoadStatisticsMemoryStore::pruneStatisticsIfNeeded):
+        (WebKit::ResourceLoadStatisticsMemoryStore::resetParametersToDefaultValues):
+        (WebKit::ResourceLoadStatisticsMemoryStore::logTestingEvent):
+        (WebKit::ResourceLoadStatisticsMemoryStore::setLastSeen):
+        (WebKit::ResourceLoadStatisticsMemoryStore::setVeryPrevalentResource):
+        (WebKit::ResourceLoadStatisticsMemoryStore::removeAllStorageAccess):
+        * UIProcess/ResourceLoadStatisticsMemoryStore.h: Added.
+        (WebKit::ResourceLoadStatisticsMemoryStore::isEmpty const):
+        (WebKit::ResourceLoadStatisticsMemoryStore::setStorageAccessPromptsEnabled):
+        (WebKit::ResourceLoadStatisticsMemoryStore::setDebugLogggingEnabled):
+        * UIProcess/ResourceLoadStatisticsPersistentStorage.cpp:
+        (WebKit::ResourceLoadStatisticsPersistentStorage::ResourceLoadStatisticsPersistentStorage):
+        (WebKit::ResourceLoadStatisticsPersistentStorage::startMonitoringDisk):
+        (WebKit::ResourceLoadStatisticsPersistentStorage::monitorDirectoryForNewStatistics):
+        (WebKit::ResourceLoadStatisticsPersistentStorage::scheduleOrWriteMemoryStore):
+        * UIProcess/ResourceLoadStatisticsPersistentStorage.h:
+        * UIProcess/WebResourceLoadStatisticsStore.cpp:
+        (WebKit::WebResourceLoadStatisticsStore::setNotifyPagesWhenDataRecordsWereScanned):
+        (WebKit::WebResourceLoadStatisticsStore::setShouldClassifyResourcesBeforeDataRecordsRemoval):
+        (WebKit::WebResourceLoadStatisticsStore::setShouldSubmitTelemetry):
+        (WebKit::WebResourceLoadStatisticsStore::WebResourceLoadStatisticsStore):
+        (WebKit::WebResourceLoadStatisticsStore::flushAndDestroyPersistentStore):
+        (WebKit::WebResourceLoadStatisticsStore::setResourceLoadStatisticsDebugMode):
+        (WebKit::WebResourceLoadStatisticsStore::scheduleStatisticsAndDataRecordsProcessing):
+        (WebKit::WebResourceLoadStatisticsStore::resourceLoadStatisticsUpdated):
+        (WebKit::WebResourceLoadStatisticsStore::hasStorageAccess):
+        (WebKit::WebResourceLoadStatisticsStore::requestStorageAccess):
+        (WebKit::WebResourceLoadStatisticsStore::requestStorageAccessUnderOpener):
+        (WebKit::WebResourceLoadStatisticsStore::grantStorageAccess):
+        (WebKit::WebResourceLoadStatisticsStore::callGrantStorageAccessHandler):
+        (WebKit::WebResourceLoadStatisticsStore::removeAllStorageAccess):
+        (WebKit::WebResourceLoadStatisticsStore::performDailyTasks):
+        (WebKit::WebResourceLoadStatisticsStore::submitTelemetry):
+        (WebKit::WebResourceLoadStatisticsStore::logFrameNavigation):
+        (WebKit::WebResourceLoadStatisticsStore::logUserInteraction):
+        (WebKit::WebResourceLoadStatisticsStore::logNonRecentUserInteraction):
+        (WebKit::WebResourceLoadStatisticsStore::clearUserInteraction):
+        (WebKit::WebResourceLoadStatisticsStore::hasHadUserInteraction):
+        (WebKit::WebResourceLoadStatisticsStore::setLastSeen):
+        (WebKit::WebResourceLoadStatisticsStore::setPrevalentResource):
+        (WebKit::WebResourceLoadStatisticsStore::setVeryPrevalentResource):
+        (WebKit::WebResourceLoadStatisticsStore::isPrevalentResource):
+        (WebKit::WebResourceLoadStatisticsStore::isVeryPrevalentResource):
+        (WebKit::WebResourceLoadStatisticsStore::isRegisteredAsSubFrameUnder):
+        (WebKit::WebResourceLoadStatisticsStore::isRegisteredAsRedirectingTo):
+        (WebKit::WebResourceLoadStatisticsStore::clearPrevalentResource):
+        (WebKit::WebResourceLoadStatisticsStore::setGrandfathered):
+        (WebKit::WebResourceLoadStatisticsStore::isGrandfathered):
+        (WebKit::WebResourceLoadStatisticsStore::setSubframeUnderTopFrameOrigin):
+        (WebKit::WebResourceLoadStatisticsStore::setSubresourceUnderTopFrameOrigin):
+        (WebKit::WebResourceLoadStatisticsStore::setSubresourceUniqueRedirectTo):
+        (WebKit::WebResourceLoadStatisticsStore::setSubresourceUniqueRedirectFrom):
+        (WebKit::WebResourceLoadStatisticsStore::setTopFrameUniqueRedirectTo):
+        (WebKit::WebResourceLoadStatisticsStore::setTopFrameUniqueRedirectFrom):
+        (WebKit::WebResourceLoadStatisticsStore::scheduleCookiePartitioningUpdate):
+        (WebKit::WebResourceLoadStatisticsStore::scheduleCookiePartitioningUpdateForDomains):
+        (WebKit::WebResourceLoadStatisticsStore::scheduleClearPartitioningStateForDomains):
+        (WebKit::WebResourceLoadStatisticsStore::scheduleCookiePartitioningStateReset):
+        (WebKit::WebResourceLoadStatisticsStore::scheduleClearInMemory):
+        (WebKit::WebResourceLoadStatisticsStore::scheduleClearInMemoryAndPersistent):
+        (WebKit::WebResourceLoadStatisticsStore::setTimeToLiveUserInteraction):
+        (WebKit::WebResourceLoadStatisticsStore::setTimeToLiveCookiePartitionFree):
+        (WebKit::WebResourceLoadStatisticsStore::setMinimumTimeBetweenDataRecordsRemoval):
+        (WebKit::WebResourceLoadStatisticsStore::setGrandfatheringTime):
+        (WebKit::WebResourceLoadStatisticsStore::callUpdatePrevalentDomainsToPartitionOrBlockCookiesHandler):
+        (WebKit::WebResourceLoadStatisticsStore::callRemoveDomainsHandler):
+        (WebKit::WebResourceLoadStatisticsStore::setMaxStatisticsEntries):
+        (WebKit::WebResourceLoadStatisticsStore::setPruneEntriesDownTo):
+        (WebKit::WebResourceLoadStatisticsStore::resetParametersToDefaultValues):
+        (WebKit::WebResourceLoadStatisticsStore::logTestingEvent):
+        * UIProcess/WebResourceLoadStatisticsStore.h:
+        * UIProcess/WebResourceLoadStatisticsTelemetry.cpp:
+        (WebKit::sortedPrevalentResourceTelemetry):
+        (WebKit::WebResourceLoadStatisticsTelemetry::calculateAndSubmit):
+        * UIProcess/WebResourceLoadStatisticsTelemetry.h:
+        * WebKit.xcodeproj/project.pbxproj:
+
 2018-06-28  Michael Catanzaro  <mcatanzaro@igalia.com>
 
         [GTK] ASSERTION FAILED: !HashTranslator::equal(KeyTraits::emptyValue(), key) when dragging file into webview
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2017 Apple Inc. All rights reserved.
+ * Copyright (C) 2017-2018 Apple Inc. All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
  * THE POSSIBILITY OF SUCH DAMAGE.
  */
 
-#include "config.h"
-#import "WebResourceLoadStatisticsStore.h"
+#import "config.h"
+#import "ResourceLoadStatisticsMemoryStore.h"
 
 namespace WebKit {
 
-void WebResourceLoadStatisticsStore::registerUserDefaultsIfNeeded()
+void ResourceLoadStatisticsMemoryStore::registerUserDefaultsIfNeeded()
 {
     static dispatch_once_t initOnce;
 
-    dispatch_once(&initOnce, ^ {
+    dispatch_once(&initOnce, ^{
         Seconds timeToLiveUserInteraction([[NSUserDefaults standardUserDefaults] doubleForKey:@"ResourceLoadStatisticsTimeToLiveUserInteraction"]);
         if (timeToLiveUserInteraction > 0_s && timeToLiveUserInteraction <= 24_h * 30)
             setTimeToLiveUserInteraction(timeToLiveUserInteraction);
diff --git a/Source/WebKit/UIProcess/ResourceLoadStatisticsMemoryStore.cpp b/Source/WebKit/UIProcess/ResourceLoadStatisticsMemoryStore.cpp
new file mode 100644 (file)
index 0000000..0fa5487
--- /dev/null
@@ -0,0 +1,1215 @@
+/*
+ * Copyright (C) 2017-2018 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 "ResourceLoadStatisticsMemoryStore.h"
+
+#include "Logging.h"
+#include "PluginProcessManager.h"
+#include "PluginProcessProxy.h"
+#include "ResourceLoadStatisticsPersistentStorage.h"
+#include "WebProcessProxy.h"
+#include "WebResourceLoadStatisticsTelemetry.h"
+#include "WebsiteDataStore.h"
+#include <WebCore/KeyedCoding.h>
+#include <WebCore/ResourceLoadStatistics.h>
+#include <wtf/DateMath.h>
+#include <wtf/MathExtras.h>
+
+#if !RELEASE_LOG_DISABLED
+#include <wtf/text/StringBuilder.h>
+#endif
+
+namespace WebKit {
+
+using namespace WebCore;
+
+constexpr unsigned statisticsModelVersion { 12 };
+constexpr unsigned maxNumberOfRecursiveCallsInRedirectTraceBack { 50 };
+constexpr Seconds minimumStatisticsProcessingInterval { 5_s };
+constexpr unsigned operatingDatesWindow { 30 };
+constexpr unsigned maxImportance { 3 };
+
+#if !RELEASE_LOG_DISABLED
+static String domainsToString(const Vector<String>& domains)
+{
+    StringBuilder builder;
+    for (auto& domain : domains) {
+        if (!builder.isEmpty())
+            builder.appendLiteral(", ");
+        builder.append(domain);
+    }
+    return builder.toString();
+}
+#endif
+
+class OperatingDate {
+public:
+    OperatingDate() = default;
+
+    static OperatingDate fromWallTime(WallTime time)
+    {
+        double ms = time.secondsSinceEpoch().milliseconds();
+        int year = msToYear(ms);
+        int yearDay = dayInYear(ms, year);
+        int month = monthFromDayInYear(yearDay, isLeapYear(year));
+        int monthDay = dayInMonthFromDayInYear(yearDay, isLeapYear(year));
+
+        return OperatingDate { year, month, monthDay };
+    }
+
+    static OperatingDate today()
+    {
+        return OperatingDate::fromWallTime(WallTime::now());
+    }
+
+    Seconds secondsSinceEpoch() const
+    {
+        return Seconds { dateToDaysFrom1970(m_year, m_month, m_monthDay) * secondsPerDay };
+    }
+
+    bool operator==(const OperatingDate& other) const
+    {
+        return m_monthDay == other.m_monthDay && m_month == other.m_month && m_year == other.m_year;
+    }
+
+    bool operator<(const OperatingDate& other) const
+    {
+        return secondsSinceEpoch() < other.secondsSinceEpoch();
+    }
+
+    bool operator<=(const OperatingDate& other) const
+    {
+        return secondsSinceEpoch() <= other.secondsSinceEpoch();
+    }
+
+private:
+    OperatingDate(int year, int month, int monthDay)
+        : m_year(year)
+        , m_month(month)
+        , m_monthDay(monthDay)
+    { }
+
+    int m_year { 0 };
+    int m_month { 0 }; // [0, 11].
+    int m_monthDay { 0 }; // [1, 31].
+};
+
+static Vector<OperatingDate> mergeOperatingDates(const Vector<OperatingDate>& existingDates, Vector<OperatingDate>&& newDates)
+{
+    if (existingDates.isEmpty())
+        return WTFMove(newDates);
+
+    Vector<OperatingDate> mergedDates(existingDates.size() + newDates.size());
+
+    // Merge the two sorted vectors of dates.
+    std::merge(existingDates.begin(), existingDates.end(), newDates.begin(), newDates.end(), mergedDates.begin());
+    // Remove duplicate dates.
+    removeRepeatedElements(mergedDates);
+
+    // Drop old dates until the Vector size reaches operatingDatesWindow.
+    while (mergedDates.size() > operatingDatesWindow)
+        mergedDates.remove(0);
+
+    return mergedDates;
+}
+
+struct StatisticsLastSeen {
+    String topPrivatelyOwnedDomain;
+    WallTime lastSeen;
+};
+
+static void pruneResources(HashMap<String, WebCore::ResourceLoadStatistics>& statisticsMap, Vector<StatisticsLastSeen>& statisticsToPrune, size_t& numberOfEntriesToPrune)
+{
+    if (statisticsToPrune.size() > numberOfEntriesToPrune) {
+        std::sort(statisticsToPrune.begin(), statisticsToPrune.end(), [](const StatisticsLastSeen& a, const StatisticsLastSeen& b) {
+            return a.lastSeen < b.lastSeen;
+        });
+    }
+
+    for (size_t i = 0, end = std::min(numberOfEntriesToPrune, statisticsToPrune.size()); i != end; ++i, --numberOfEntriesToPrune)
+        statisticsMap.remove(statisticsToPrune[i].topPrivatelyOwnedDomain);
+}
+
+static unsigned computeImportance(const ResourceLoadStatistics& resourceStatistic)
+{
+    unsigned importance = maxImportance;
+    if (!resourceStatistic.isPrevalentResource)
+        importance -= 1;
+    if (!resourceStatistic.hadUserInteraction)
+        importance -= 2;
+    return importance;
+}
+
+ResourceLoadStatisticsMemoryStore::ResourceLoadStatisticsMemoryStore(WebResourceLoadStatisticsStore& store, WorkQueue& workQueue)
+    : m_store(store)
+    , m_workQueue(workQueue)
+{
+    ASSERT(!RunLoop::isMain());
+
+#if PLATFORM(COCOA)
+    registerUserDefaultsIfNeeded();
+#endif
+    includeTodayAsOperatingDateIfNecessary();
+}
+
+ResourceLoadStatisticsMemoryStore::~ResourceLoadStatisticsMemoryStore()
+{
+    ASSERT(!RunLoop::isMain());
+}
+
+void ResourceLoadStatisticsMemoryStore::setPersistentStorage(ResourceLoadStatisticsPersistentStorage& persistentStorage)
+{
+    m_persistentStorage = makeWeakPtr(persistentStorage);
+}
+
+void ResourceLoadStatisticsMemoryStore::calculateAndSubmitTelemetry() const
+{
+    ASSERT(!RunLoop::isMain());
+
+    if (m_parameters.shouldSubmitTelemetry)
+        WebResourceLoadStatisticsTelemetry::calculateAndSubmit(*this);
+}
+
+void ResourceLoadStatisticsMemoryStore::setNotifyPagesWhenDataRecordsWereScanned(bool value)
+{
+    ASSERT(!RunLoop::isMain());
+    m_parameters.shouldNotifyPagesWhenDataRecordsWereScanned = value;
+}
+
+void ResourceLoadStatisticsMemoryStore::setShouldClassifyResourcesBeforeDataRecordsRemoval(bool value)
+{
+    ASSERT(!RunLoop::isMain());
+    m_parameters.shouldClassifyResourcesBeforeDataRecordsRemoval = value;
+}
+
+void ResourceLoadStatisticsMemoryStore::setShouldSubmitTelemetry(bool value)
+{
+    ASSERT(!RunLoop::isMain());
+    m_parameters.shouldSubmitTelemetry = value;
+}
+
+void ResourceLoadStatisticsMemoryStore::removeDataRecords(CompletionHandler<void()>&& callback)
+{
+    ASSERT(!RunLoop::isMain());
+
+    if (!shouldRemoveDataRecords()) {
+        callback();
+        return;
+    }
+
+#if ENABLE(NETSCAPE_PLUGIN_API)
+    m_activePluginTokens.clear();
+    for (auto plugin : PluginProcessManager::singleton().pluginProcesses())
+        m_activePluginTokens.add(plugin->pluginProcessToken());
+#endif
+
+    auto prevalentResourceDomains = topPrivatelyControlledDomainsToRemoveWebsiteDataFor();
+    if (prevalentResourceDomains.isEmpty()) {
+        callback();
+        return;
+    }
+
+#if !RELEASE_LOG_DISABLED
+    if (m_debugLoggingEnabled)
+        RELEASE_LOG_INFO(ResourceLoadStatisticsDebug, "About to remove data records for %{public}s.", domainsToString(prevalentResourceDomains).utf8().data());
+#endif
+
+    setDataRecordsBeingRemoved(true);
+
+    RunLoop::main().dispatch([prevalentResourceDomains = crossThreadCopy(prevalentResourceDomains), callback = WTFMove(callback), this, weakThis = makeWeakPtr(*this)] () mutable {
+        WebProcessProxy::deleteWebsiteDataForTopPrivatelyControlledDomainsInAllPersistentDataStores(WebResourceLoadStatisticsStore::monitoredDataTypes(), WTFMove(prevalentResourceDomains), m_parameters.shouldNotifyPagesWhenDataRecordsWereScanned, [callback = WTFMove(callback), this, weakThis = WTFMove(weakThis)](const HashSet<String>& domainsWithDeletedWebsiteData) mutable {
+            m_workQueue->dispatch([topDomains = crossThreadCopy(domainsWithDeletedWebsiteData), callback = WTFMove(callback), this, weakThis = WTFMove(weakThis)] () mutable {
+                if (!weakThis) {
+                    callback();
+                    return;
+                }
+                for (auto& prevalentResourceDomain : topDomains) {
+                    auto& statistic = ensureResourceStatisticsForPrimaryDomain(prevalentResourceDomain);
+                    ++statistic.dataRecordsRemoved;
+                }
+                setDataRecordsBeingRemoved(false);
+                callback();
+#if !RELEASE_LOG_DISABLED
+                RELEASE_LOG_INFO_IF(m_debugLoggingEnabled, ResourceLoadStatisticsDebug, "Done removing data records.");
+#endif
+            });
+        });
+    });
+}
+
+unsigned ResourceLoadStatisticsMemoryStore::recursivelyGetAllDomainsThatHaveRedirectedToThisDomain(const WebCore::ResourceLoadStatistics& resourceStatistic, HashSet<String>& domainsThatHaveRedirectedTo, unsigned numberOfRecursiveCalls) const
+{
+    ASSERT(!RunLoop::isMain());
+
+    if (numberOfRecursiveCalls >= maxNumberOfRecursiveCallsInRedirectTraceBack) {
+        ASSERT_NOT_REACHED();
+        WTFLogAlways("Hit %u recursive calls in redirect backtrace. Returning early.", maxNumberOfRecursiveCallsInRedirectTraceBack);
+        return numberOfRecursiveCalls;
+    }
+
+    numberOfRecursiveCalls++;
+
+    for (auto& subresourceUniqueRedirectFromDomain : resourceStatistic.subresourceUniqueRedirectsFrom.values()) {
+        auto mapEntry = m_resourceStatisticsMap.find(subresourceUniqueRedirectFromDomain);
+        if (mapEntry == m_resourceStatisticsMap.end() || mapEntry->value.isPrevalentResource)
+            continue;
+        if (domainsThatHaveRedirectedTo.add(mapEntry->value.highLevelDomain).isNewEntry)
+            numberOfRecursiveCalls = recursivelyGetAllDomainsThatHaveRedirectedToThisDomain(mapEntry->value, domainsThatHaveRedirectedTo, numberOfRecursiveCalls);
+    }
+    for (auto& topFrameUniqueRedirectFromDomain : resourceStatistic.topFrameUniqueRedirectsFrom.values()) {
+        auto mapEntry = m_resourceStatisticsMap.find(topFrameUniqueRedirectFromDomain);
+        if (mapEntry == m_resourceStatisticsMap.end() || mapEntry->value.isPrevalentResource)
+            continue;
+        if (domainsThatHaveRedirectedTo.add(mapEntry->value.highLevelDomain).isNewEntry)
+            numberOfRecursiveCalls = recursivelyGetAllDomainsThatHaveRedirectedToThisDomain(mapEntry->value, domainsThatHaveRedirectedTo, numberOfRecursiveCalls);
+    }
+
+    return numberOfRecursiveCalls;
+}
+
+void ResourceLoadStatisticsMemoryStore::markAsPrevalentIfHasRedirectedToPrevalent(WebCore::ResourceLoadStatistics& resourceStatistic)
+{
+    ASSERT(!RunLoop::isMain());
+
+    if (resourceStatistic.isPrevalentResource)
+        return;
+
+    for (auto& subresourceDomainRedirectedTo : resourceStatistic.subresourceUniqueRedirectsTo.values()) {
+        auto mapEntry = m_resourceStatisticsMap.find(subresourceDomainRedirectedTo);
+        if (mapEntry != m_resourceStatisticsMap.end() && mapEntry->value.isPrevalentResource) {
+            setPrevalentResource(resourceStatistic, ResourceLoadPrevalence::High);
+            return;
+        }
+    }
+
+    for (auto& topFrameDomainRedirectedTo : resourceStatistic.topFrameUniqueRedirectsTo.values()) {
+        auto mapEntry = m_resourceStatisticsMap.find(topFrameDomainRedirectedTo);
+        if (mapEntry != m_resourceStatisticsMap.end() && mapEntry->value.isPrevalentResource) {
+            setPrevalentResource(resourceStatistic, ResourceLoadPrevalence::High);
+            return;
+        }
+    }
+}
+
+void ResourceLoadStatisticsMemoryStore::processStatisticsAndDataRecords()
+{
+    ASSERT(!RunLoop::isMain());
+
+    if (m_parameters.shouldClassifyResourcesBeforeDataRecordsRemoval) {
+        for (auto& resourceStatistic : m_resourceStatisticsMap.values()) {
+            if (!resourceStatistic.isVeryPrevalentResource) {
+                markAsPrevalentIfHasRedirectedToPrevalent(resourceStatistic);
+                auto currentPrevalence = resourceStatistic.isPrevalentResource ? ResourceLoadPrevalence::High : ResourceLoadPrevalence::Low;
+                auto newPrevalence = m_resourceLoadStatisticsClassifier.calculateResourcePrevalence(resourceStatistic, currentPrevalence);
+                if (newPrevalence != currentPrevalence)
+                    setPrevalentResource(resourceStatistic, newPrevalence);
+            }
+        }
+    }
+
+    if (m_parameters.shouldNotifyPagesWhenDataRecordsWereScanned) {
+        removeDataRecords([this, weakThis = makeWeakPtr(*this)] {
+            ASSERT(!RunLoop::isMain());
+            if (!weakThis)
+                return;
+
+            pruneStatisticsIfNeeded();
+            if (m_persistentStorage)
+                m_persistentStorage->scheduleOrWriteMemoryStore(ResourceLoadStatisticsPersistentStorage::ForceImmediateWrite::No);
+
+            RunLoop::main().dispatch([] {
+                WebProcessProxy::notifyPageStatisticsAndDataRecordsProcessed();
+            });
+        });
+    } else {
+        removeDataRecords([this, weakThis = makeWeakPtr(*this)] {
+            ASSERT(!RunLoop::isMain());
+            if (!weakThis)
+                return;
+
+            pruneStatisticsIfNeeded();
+            if (m_persistentStorage)
+                m_persistentStorage->scheduleOrWriteMemoryStore(ResourceLoadStatisticsPersistentStorage::ForceImmediateWrite::No);
+        });
+    }
+}
+
+void ResourceLoadStatisticsMemoryStore::hasStorageAccess(const String& subFramePrimaryDomain, const String& topFramePrimaryDomain, uint64_t frameID, uint64_t pageID, CompletionHandler<void(bool)>&& completionHandler)
+{
+    ASSERT(!RunLoop::isMain());
+
+    auto& subFrameStatistic = ensureResourceStatisticsForPrimaryDomain(subFramePrimaryDomain);
+    if (shouldBlockCookies(subFrameStatistic)) {
+        completionHandler(false);
+        return;
+    }
+
+    if (!shouldPartitionCookies(subFrameStatistic)) {
+        completionHandler(true);
+        return;
+    }
+
+    RunLoop::main().dispatch([store = makeRef(m_store), subFramePrimaryDomain = subFramePrimaryDomain.isolatedCopy(), topFramePrimaryDomain = topFramePrimaryDomain.isolatedCopy(), frameID, pageID, completionHandler = WTFMove(completionHandler)]() mutable {
+        store->callHasStorageAccessForFrameHandler(subFramePrimaryDomain, topFramePrimaryDomain, frameID, pageID, [store = store.copyRef(), completionHandler = WTFMove(completionHandler)](bool result) mutable {
+            store->statisticsQueue().dispatch([completionHandler = WTFMove(completionHandler), result] {
+                completionHandler(result);
+            });
+        });
+    });
+}
+
+void ResourceLoadStatisticsMemoryStore::requestStorageAccess(String&& subFramePrimaryDomain, String&& topFramePrimaryDomain, uint64_t frameID, uint64_t pageID, bool promptEnabled, CompletionHandler<void(StorageAccessStatus)>&& completionHandler)
+{
+    ASSERT(!RunLoop::isMain());
+
+    auto& subFrameStatistic = ensureResourceStatisticsForPrimaryDomain(subFramePrimaryDomain);
+    if (shouldBlockCookies(subFrameStatistic)) {
+        completionHandler(StorageAccessStatus::CannotRequestAccess);
+        return;
+    }
+
+    if (!shouldPartitionCookies(subFrameStatistic)) {
+        completionHandler(StorageAccessStatus::HasAccess);
+        return;
+    }
+
+    auto userWasPromptedEarlier = promptEnabled && hasUserGrantedStorageAccessThroughPrompt(subFrameStatistic, topFramePrimaryDomain);
+    if (promptEnabled && !userWasPromptedEarlier) {
+        completionHandler(StorageAccessStatus::RequiresUserPrompt);
+        return;
+    }
+
+    subFrameStatistic.timesAccessedAsFirstPartyDueToStorageAccessAPI++;
+
+    grantStorageAccessInternal(WTFMove(subFramePrimaryDomain), WTFMove(topFramePrimaryDomain), frameID, pageID, userWasPromptedEarlier, [completionHandler = WTFMove(completionHandler)] (bool wasGrantedAccess) mutable {
+        completionHandler(wasGrantedAccess ? StorageAccessStatus::HasAccess : StorageAccessStatus::CannotRequestAccess);
+    });
+}
+
+void ResourceLoadStatisticsMemoryStore::requestStorageAccessUnderOpener(String&& primaryDomainInNeedOfStorageAccess, uint64_t openerPageID, String&& openerPrimaryDomain, bool isTriggeredByUserGesture)
+{
+    ASSERT(primaryDomainInNeedOfStorageAccess != openerPrimaryDomain);
+    ASSERT(!RunLoop::isMain());
+
+    if (primaryDomainInNeedOfStorageAccess == openerPrimaryDomain)
+        return;
+
+    auto& domainInNeedOfStorageAccessStatistic = ensureResourceStatisticsForPrimaryDomain(primaryDomainInNeedOfStorageAccess);
+    auto cookiesBlocked = shouldBlockCookies(domainInNeedOfStorageAccessStatistic);
+
+    // There are no cookies to get access to if the domain has its cookies blocked and did not get user interaction now.
+    if (cookiesBlocked && !isTriggeredByUserGesture)
+        return;
+
+    // The domain already has access if its cookies are neither blocked nor partitioned.
+    if (!cookiesBlocked && !shouldPartitionCookies(domainInNeedOfStorageAccessStatistic))
+        return;
+
+#if !RELEASE_LOG_DISABLED
+    RELEASE_LOG_INFO_IF(m_debugLoggingEnabled, ResourceLoadStatisticsDebug, "Grant storage access for %{public}s under opener %{public}s, %{public}s user interaction.", primaryDomainInNeedOfStorageAccess.utf8().data(), openerPrimaryDomain.utf8().data(), (isTriggeredByUserGesture ? "with" : "without"));
+#endif
+    grantStorageAccessInternal(WTFMove(primaryDomainInNeedOfStorageAccess), WTFMove(openerPrimaryDomain), std::nullopt, openerPageID, false, [](bool) { });
+}
+
+void ResourceLoadStatisticsMemoryStore::grantStorageAccess(String&& subFrameHost, String&& topFrameHost, uint64_t frameID, uint64_t pageID, bool userWasPromptedNow, CompletionHandler<void(bool)>&& completionHandler)
+{
+    ASSERT(!RunLoop::isMain());
+
+    auto subFramePrimaryDomain = ResourceLoadStatistics::primaryDomain(subFrameHost);
+    auto topFramePrimaryDomain = ResourceLoadStatistics::primaryDomain(topFrameHost);
+    if (userWasPromptedNow) {
+        auto& subFrameStatistic = ensureResourceStatisticsForPrimaryDomain(subFramePrimaryDomain);
+        ASSERT(subFrameStatistic.hadUserInteraction);
+        subFrameStatistic.storageAccessUnderTopFrameOrigins.add(topFramePrimaryDomain);
+    }
+    grantStorageAccessInternal(WTFMove(subFrameHost), WTFMove(topFrameHost), frameID, pageID, userWasPromptedNow, WTFMove(completionHandler));
+}
+
+void ResourceLoadStatisticsMemoryStore::grantStorageAccessInternal(String&& subFramePrimaryDomain, String&& topFramePrimaryDomain, std::optional<uint64_t> frameID, uint64_t pageID, bool userWasPromptedNowOrEarlier, CompletionHandler<void(bool)>&& callback)
+{
+    ASSERT(!RunLoop::isMain());
+
+    if (subFramePrimaryDomain == topFramePrimaryDomain) {
+        callback(true);
+        return;
+    }
+
+    // FIXME: Remove m_storageAccessPromptsEnabled check if prompting is no longer experimental.
+    if (userWasPromptedNowOrEarlier && m_storageAccessPromptsEnabled) {
+        auto& subFrameStatistic = ensureResourceStatisticsForPrimaryDomain(subFramePrimaryDomain);
+        ASSERT(subFrameStatistic.hadUserInteraction);
+        ASSERT(subFrameStatistic.storageAccessUnderTopFrameOrigins.contains(topFramePrimaryDomain));
+        subFrameStatistic.mostRecentUserInteractionTime = WallTime::now();
+    }
+
+    RunLoop::main().dispatch([subFramePrimaryDomain = subFramePrimaryDomain.isolatedCopy(), topFramePrimaryDomain = topFramePrimaryDomain.isolatedCopy(), frameID, pageID, store = makeRef(m_store), callback = WTFMove(callback)]() mutable {
+        store->callGrantStorageAccessHandler(subFramePrimaryDomain, topFramePrimaryDomain, frameID, pageID, [callback = WTFMove(callback), store = store.copyRef()](bool value) mutable {
+            store->statisticsQueue().dispatch([callback = WTFMove(callback), value] {
+                callback(value);
+            });
+        });
+    });
+}
+
+void ResourceLoadStatisticsMemoryStore::grandfatherExistingWebsiteData(CompletionHandler<void()>&& callback)
+{
+    ASSERT(!RunLoop::isMain());
+
+    RunLoop::main().dispatch([this, weakThis = makeWeakPtr(*this), callback = WTFMove(callback)] () mutable {
+        // FIXME: This method being a static call on WebProcessProxy is wrong.
+        // It should be on the data store that this object belongs to.
+        WebProcessProxy::topPrivatelyControlledDomainsWithWebsiteData(WebResourceLoadStatisticsStore::monitoredDataTypes(), m_parameters.shouldNotifyPagesWhenDataRecordsWereScanned, [this, weakThis = WTFMove(weakThis), callback = WTFMove(callback)] (HashSet<String>&& topPrivatelyControlledDomainsWithWebsiteData) mutable {
+            m_workQueue->dispatch([this, weakThis = WTFMove(weakThis), topDomains = crossThreadCopy(topPrivatelyControlledDomainsWithWebsiteData), callback = WTFMove(callback)] () mutable {
+                if (!weakThis) {
+                    callback();
+                    return;
+                }
+
+                for (auto& topPrivatelyControlledDomain : topDomains) {
+                    auto& statistic = ensureResourceStatisticsForPrimaryDomain(topPrivatelyControlledDomain);
+                    statistic.grandfathered = true;
+                }
+                m_endOfGrandfatheringTimestamp = WallTime::now() + m_parameters.grandfatheringTime;
+                if (m_persistentStorage)
+                    m_persistentStorage->scheduleOrWriteMemoryStore(ResourceLoadStatisticsPersistentStorage::ForceImmediateWrite::Yes);
+                callback();
+                logTestingEvent("Grandfathered"_s);
+            });
+        });
+    });
+}
+
+void ResourceLoadStatisticsMemoryStore::setResourceLoadStatisticsDebugMode(bool enable)
+{
+    ASSERT(!RunLoop::isMain());
+
+    m_debugModeEnabled = enable;
+#if !RELEASE_LOG_DISABLED
+    RELEASE_LOG_INFO_IF(m_debugLoggingEnabled, ResourceLoadStatisticsDebug, "ITP Debug Mode %{public}s.", (m_debugModeEnabled ? "enabled" : "disabled"));
+#endif
+}
+
+void ResourceLoadStatisticsMemoryStore::scheduleStatisticsProcessingRequestIfNecessary()
+{
+    ASSERT(!RunLoop::isMain());
+
+    m_pendingStatisticsProcessingRequestIdentifier = ++m_lastStatisticsProcessingRequestIdentifier;
+    m_workQueue->dispatchAfter(minimumStatisticsProcessingInterval, [this, weakThis = makeWeakPtr(*this), statisticsProcessingRequestIdentifier = *m_pendingStatisticsProcessingRequestIdentifier] {
+        if (!weakThis)
+            return;
+
+        if (!m_pendingStatisticsProcessingRequestIdentifier || *m_pendingStatisticsProcessingRequestIdentifier != statisticsProcessingRequestIdentifier) {
+            // This request has been canceled.
+            return;
+        }
+
+        updateCookiePartitioning([]() { });
+        processStatisticsAndDataRecords();
+    });
+}
+
+void ResourceLoadStatisticsMemoryStore::cancelPendingStatisticsProcessingRequest()
+{
+    ASSERT(!RunLoop::isMain());
+
+    m_pendingStatisticsProcessingRequestIdentifier = std::nullopt;
+}
+
+void ResourceLoadStatisticsMemoryStore::logFrameNavigation(const String& targetPrimaryDomain, const String& mainFramePrimaryDomain, const String& sourcePrimaryDomain, const String& targetHost, const String& mainFrameHost, bool areTargetAndMainFrameDomainsAssociated, bool areTargetAndSourceDomainsAssociated, bool isRedirect, bool isMainFrame)
+{
+    ASSERT(!RunLoop::isMain());
+
+    bool statisticsWereUpdated = false;
+    if (targetHost != mainFrameHost && !(areTargetAndMainFrameDomainsAssociated || areTargetAndSourceDomainsAssociated)) {
+        auto& targetStatistics = ensureResourceStatisticsForPrimaryDomain(targetPrimaryDomain);
+        targetStatistics.lastSeen = ResourceLoadStatistics::reduceTimeResolution(WallTime::now());
+        if (targetStatistics.subframeUnderTopFrameOrigins.add(mainFramePrimaryDomain).isNewEntry)
+            statisticsWereUpdated = true;
+    }
+
+    if (isRedirect && !areTargetAndSourceDomainsAssociated) {
+        if (isMainFrame) {
+            auto& redirectingOriginStatistics = ensureResourceStatisticsForPrimaryDomain(sourcePrimaryDomain);
+            if (redirectingOriginStatistics.topFrameUniqueRedirectsTo.add(targetPrimaryDomain).isNewEntry)
+                statisticsWereUpdated = true;
+            auto& targetStatistics = ensureResourceStatisticsForPrimaryDomain(targetPrimaryDomain);
+            if (targetStatistics.topFrameUniqueRedirectsFrom.add(sourcePrimaryDomain).isNewEntry)
+                statisticsWereUpdated = true;
+        } else {
+            auto& redirectingOriginStatistics = ensureResourceStatisticsForPrimaryDomain(sourcePrimaryDomain);
+            if (redirectingOriginStatistics.subresourceUniqueRedirectsTo.add(targetPrimaryDomain).isNewEntry)
+                statisticsWereUpdated = true;
+            auto& targetStatistics = ensureResourceStatisticsForPrimaryDomain(targetPrimaryDomain);
+            if (targetStatistics.subresourceUniqueRedirectsFrom.add(sourcePrimaryDomain).isNewEntry)
+                statisticsWereUpdated = true;
+        }
+    }
+
+    if (statisticsWereUpdated)
+        scheduleStatisticsProcessingRequestIfNecessary();
+}
+
+void ResourceLoadStatisticsMemoryStore::logUserInteraction(const String& primaryDomain)
+{
+    ASSERT(!RunLoop::isMain());
+
+    auto& statistics = ensureResourceStatisticsForPrimaryDomain(primaryDomain);
+    statistics.hadUserInteraction = true;
+    statistics.mostRecentUserInteractionTime = WallTime::now();
+
+    if (statistics.isMarkedForCookieBlocking)
+        updateCookiePartitioningForDomains({ primaryDomain }, { }, { }, ShouldClearFirst::No, []() { });
+}
+
+void ResourceLoadStatisticsMemoryStore::logNonRecentUserInteraction(const String& primaryDomain)
+{
+    ASSERT(!RunLoop::isMain());
+
+    auto& statistics = ensureResourceStatisticsForPrimaryDomain(primaryDomain);
+    statistics.hadUserInteraction = true;
+    statistics.mostRecentUserInteractionTime = WallTime::now() - (m_parameters.timeToLiveCookiePartitionFree + Seconds::fromHours(1));
+
+    updateCookiePartitioningForDomains({ primaryDomain }, { }, { }, ShouldClearFirst::No, []() { });
+}
+
+void ResourceLoadStatisticsMemoryStore::clearUserInteraction(const String& primaryDomain)
+{
+    ASSERT(!RunLoop::isMain());
+
+    auto& statistics = ensureResourceStatisticsForPrimaryDomain(primaryDomain);
+    statistics.hadUserInteraction = false;
+    statistics.mostRecentUserInteractionTime = { };
+}
+
+bool ResourceLoadStatisticsMemoryStore::hasHadUserInteraction(const String& primaryDomain)
+{
+    ASSERT(!RunLoop::isMain());
+
+    auto mapEntry = m_resourceStatisticsMap.find(primaryDomain);
+    return mapEntry == m_resourceStatisticsMap.end() ? false: hasHadUnexpiredRecentUserInteraction(mapEntry->value);
+}
+
+void ResourceLoadStatisticsMemoryStore::setPrevalentResource(WebCore::ResourceLoadStatistics& resourceStatistic, ResourceLoadPrevalence newPrevalence)
+{
+    ASSERT(!RunLoop::isMain());
+
+    resourceStatistic.isPrevalentResource = true;
+    resourceStatistic.isVeryPrevalentResource = newPrevalence == ResourceLoadPrevalence::VeryHigh;
+    HashSet<String> domainsThatHaveRedirectedTo;
+    recursivelyGetAllDomainsThatHaveRedirectedToThisDomain(resourceStatistic, domainsThatHaveRedirectedTo, 0);
+    for (auto& domain : domainsThatHaveRedirectedTo) {
+        auto mapEntry = m_resourceStatisticsMap.find(domain);
+        if (mapEntry == m_resourceStatisticsMap.end())
+            continue;
+        ASSERT(!mapEntry->value.isPrevalentResource);
+        mapEntry->value.isPrevalentResource = true;
+    }
+}
+
+bool ResourceLoadStatisticsMemoryStore::isPrevalentResource(const String& primaryDomain) const
+{
+    ASSERT(!RunLoop::isMain());
+
+    auto mapEntry = m_resourceStatisticsMap.find(primaryDomain);
+    return mapEntry == m_resourceStatisticsMap.end() ? false : mapEntry->value.isPrevalentResource;
+}
+
+bool ResourceLoadStatisticsMemoryStore::isVeryPrevalentResource(const String& primaryDomain) const
+{
+    ASSERT(!RunLoop::isMain());
+
+    auto mapEntry = m_resourceStatisticsMap.find(primaryDomain);
+    return mapEntry == m_resourceStatisticsMap.end() ? false : mapEntry->value.isPrevalentResource && mapEntry->value.isVeryPrevalentResource;
+}
+
+bool ResourceLoadStatisticsMemoryStore::isRegisteredAsSubFrameUnder(const String& subFramePrimaryDomain, const String& topFramePrimaryDomain) const
+{
+    ASSERT(!RunLoop::isMain());
+
+    auto mapEntry = m_resourceStatisticsMap.find(subFramePrimaryDomain);
+    return mapEntry == m_resourceStatisticsMap.end() ? false : mapEntry->value.subframeUnderTopFrameOrigins.contains(topFramePrimaryDomain);
+}
+
+bool ResourceLoadStatisticsMemoryStore::isRegisteredAsRedirectingTo(const String& hostRedirectedFromPrimaryDomain, const String& hostRedirectedToPrimaryDomain) const
+{
+    ASSERT(!RunLoop::isMain());
+
+    auto mapEntry = m_resourceStatisticsMap.find(hostRedirectedFromPrimaryDomain);
+    return mapEntry == m_resourceStatisticsMap.end() ? false : mapEntry->value.subresourceUniqueRedirectsTo.contains(hostRedirectedToPrimaryDomain);
+}
+
+void ResourceLoadStatisticsMemoryStore::clearPrevalentResource(const String& primaryDomain)
+{
+    ASSERT(!RunLoop::isMain());
+
+    auto& statistics = ensureResourceStatisticsForPrimaryDomain(primaryDomain);
+    statistics.isPrevalentResource = false;
+    statistics.isVeryPrevalentResource = false;
+}
+
+void ResourceLoadStatisticsMemoryStore::setGrandfathered(const String& primaryDomain, bool value)
+{
+    ASSERT(!RunLoop::isMain());
+
+    auto& statistics = ensureResourceStatisticsForPrimaryDomain(primaryDomain);
+    statistics.grandfathered = value;
+}
+
+bool ResourceLoadStatisticsMemoryStore::isGrandfathered(const String& primaryDomain) const
+{
+    ASSERT(!RunLoop::isMain());
+
+    auto mapEntry = m_resourceStatisticsMap.find(primaryDomain);
+    return mapEntry == m_resourceStatisticsMap.end() ? false : mapEntry->value.grandfathered;
+}
+
+void ResourceLoadStatisticsMemoryStore::setSubframeUnderTopFrameOrigin(const String& primarySubFrameDomain, const String& primaryTopFrameDomain)
+{
+    ASSERT(!RunLoop::isMain());
+
+    auto& statistics = ensureResourceStatisticsForPrimaryDomain(primarySubFrameDomain);
+    statistics.subframeUnderTopFrameOrigins.add(primaryTopFrameDomain);
+    // For consistency, make sure we also have a statistics entry for the top frame domain.
+    ensureResourceStatisticsForPrimaryDomain(primaryTopFrameDomain);
+}
+
+void ResourceLoadStatisticsMemoryStore::setSubresourceUnderTopFrameOrigin(const String& primarySubresourceDomain, const String& primaryTopFrameDomain)
+{
+    ASSERT(!RunLoop::isMain());
+
+    auto& statistics = ensureResourceStatisticsForPrimaryDomain(primarySubresourceDomain);
+    statistics.subresourceUnderTopFrameOrigins.add(primaryTopFrameDomain);
+    // For consistency, make sure we also have a statistics entry for the top frame domain.
+    ensureResourceStatisticsForPrimaryDomain(primaryTopFrameDomain);
+}
+
+void ResourceLoadStatisticsMemoryStore::setSubresourceUniqueRedirectTo(const String& primarySubresourceDomain, const String& primaryRedirectDomain)
+{
+    ASSERT(!RunLoop::isMain());
+
+    auto& statistics = ensureResourceStatisticsForPrimaryDomain(primarySubresourceDomain);
+    statistics.subresourceUniqueRedirectsTo.add(primaryRedirectDomain);
+    // For consistency, make sure we also have a statistics entry for the redirect domain.
+    ensureResourceStatisticsForPrimaryDomain(primaryRedirectDomain);
+}
+
+void ResourceLoadStatisticsMemoryStore::setSubresourceUniqueRedirectFrom(const String& primarySubresourceDomain, const String& primaryRedirectDomain)
+{
+    ASSERT(!RunLoop::isMain());
+
+    auto& statistics = ensureResourceStatisticsForPrimaryDomain(primarySubresourceDomain);
+    statistics.subresourceUniqueRedirectsFrom.add(primaryRedirectDomain);
+    // For consistency, make sure we also have a statistics entry for the redirect domain.
+    ensureResourceStatisticsForPrimaryDomain(primaryRedirectDomain);
+}
+
+void ResourceLoadStatisticsMemoryStore::setTopFrameUniqueRedirectTo(const String& topFramePrimaryDomain, const String& primaryRedirectDomain)
+{
+    ASSERT(!RunLoop::isMain());
+
+    auto& statistics = ensureResourceStatisticsForPrimaryDomain(topFramePrimaryDomain);
+    statistics.topFrameUniqueRedirectsTo.add(primaryRedirectDomain);
+    // For consistency, make sure we also have a statistics entry for the redirect domain.
+    ensureResourceStatisticsForPrimaryDomain(primaryRedirectDomain);
+}
+
+void ResourceLoadStatisticsMemoryStore::setTopFrameUniqueRedirectFrom(const String& topFramePrimaryDomain, const String& primaryRedirectDomain)
+{
+    ASSERT(!RunLoop::isMain());
+
+    auto& statistics = ensureResourceStatisticsForPrimaryDomain(topFramePrimaryDomain);
+    statistics.topFrameUniqueRedirectsFrom.add(primaryRedirectDomain);
+    // For consistency, make sure we also have a statistics entry for the redirect domain.
+    ensureResourceStatisticsForPrimaryDomain(primaryRedirectDomain);
+}
+
+void ResourceLoadStatisticsMemoryStore::setTimeToLiveUserInteraction(Seconds seconds)
+{
+    ASSERT(!RunLoop::isMain());
+    ASSERT(seconds >= 0_s);
+
+    m_parameters.timeToLiveUserInteraction = seconds;
+}
+
+void ResourceLoadStatisticsMemoryStore::setTimeToLiveCookiePartitionFree(Seconds seconds)
+{
+    ASSERT(!RunLoop::isMain());
+    ASSERT(seconds >= 0_s);
+
+    m_parameters.timeToLiveCookiePartitionFree = seconds;
+}
+
+void ResourceLoadStatisticsMemoryStore::setMinimumTimeBetweenDataRecordsRemoval(Seconds seconds)
+{
+    ASSERT(!RunLoop::isMain());
+    ASSERT(seconds >= 0_s);
+
+    m_parameters.minimumTimeBetweenDataRecordsRemoval = seconds;
+}
+
+void ResourceLoadStatisticsMemoryStore::setGrandfatheringTime(Seconds seconds)
+{
+    ASSERT(!RunLoop::isMain());
+    ASSERT(seconds >= 0_s);
+
+    m_parameters.grandfatheringTime = seconds;
+}
+
+bool ResourceLoadStatisticsMemoryStore::shouldRemoveDataRecords() const
+{
+    ASSERT(!RunLoop::isMain());
+
+    if (m_dataRecordsBeingRemoved)
+        return false;
+
+#if ENABLE(NETSCAPE_PLUGIN_API)
+    for (auto plugin : PluginProcessManager::singleton().pluginProcesses()) {
+        if (!m_activePluginTokens.contains(plugin->pluginProcessToken()))
+            return true;
+    }
+#endif
+
+    return !m_lastTimeDataRecordsWereRemoved || MonotonicTime::now() >= (m_lastTimeDataRecordsWereRemoved + m_parameters.minimumTimeBetweenDataRecordsRemoval);
+}
+
+void ResourceLoadStatisticsMemoryStore::setDataRecordsBeingRemoved(bool value)
+{
+    ASSERT(!RunLoop::isMain());
+
+    m_dataRecordsBeingRemoved = value;
+    if (m_dataRecordsBeingRemoved)
+        m_lastTimeDataRecordsWereRemoved = MonotonicTime::now();
+}
+
+ResourceLoadStatistics& ResourceLoadStatisticsMemoryStore::ensureResourceStatisticsForPrimaryDomain(const String& primaryDomain)
+{
+    ASSERT(!RunLoop::isMain());
+
+    return m_resourceStatisticsMap.ensure(primaryDomain, [&primaryDomain] {
+        return ResourceLoadStatistics(primaryDomain);
+    }).iterator->value;
+}
+
+std::unique_ptr<KeyedEncoder> ResourceLoadStatisticsMemoryStore::createEncoderFromData() const
+{
+    ASSERT(!RunLoop::isMain());
+
+    auto encoder = KeyedEncoder::encoder();
+    encoder->encodeUInt32("version", statisticsModelVersion);
+    encoder->encodeDouble("endOfGrandfatheringTimestamp", m_endOfGrandfatheringTimestamp.secondsSinceEpoch().value());
+
+    encoder->encodeObjects("browsingStatistics", m_resourceStatisticsMap.begin(), m_resourceStatisticsMap.end(), [](KeyedEncoder& encoderInner, const auto& origin) {
+        origin.value.encode(encoderInner);
+    });
+
+    encoder->encodeObjects("operatingDates", m_operatingDates.begin(), m_operatingDates.end(), [](KeyedEncoder& encoderInner, OperatingDate date) {
+        encoderInner.encodeDouble("date", date.secondsSinceEpoch().value());
+    });
+
+    return encoder;
+}
+
+void ResourceLoadStatisticsMemoryStore::mergeWithDataFromDecoder(KeyedDecoder& decoder)
+{
+    ASSERT(!RunLoop::isMain());
+
+    unsigned versionOnDisk;
+    if (!decoder.decodeUInt32("version", versionOnDisk))
+        return;
+
+    if (versionOnDisk > statisticsModelVersion) {
+        WTFLogAlways("Found resource load statistics on disk with model version %u whereas the highest supported version is %u. Resetting.", versionOnDisk, statisticsModelVersion);
+        return;
+    }
+
+    double endOfGrandfatheringTimestamp;
+    if (decoder.decodeDouble("endOfGrandfatheringTimestamp", endOfGrandfatheringTimestamp))
+        m_endOfGrandfatheringTimestamp = WallTime::fromRawSeconds(endOfGrandfatheringTimestamp);
+    else
+        m_endOfGrandfatheringTimestamp = { };
+
+    Vector<ResourceLoadStatistics> loadedStatistics;
+    bool succeeded = decoder.decodeObjects("browsingStatistics", loadedStatistics, [versionOnDisk](KeyedDecoder& decoderInner, ResourceLoadStatistics& statistics) {
+        return statistics.decode(decoderInner, versionOnDisk);
+    });
+
+    if (!succeeded)
+        return;
+
+    mergeStatistics(WTFMove(loadedStatistics));
+    updateCookiePartitioning([]() { });
+
+    Vector<OperatingDate> operatingDates;
+    succeeded = decoder.decodeObjects("operatingDates", operatingDates, [](KeyedDecoder& decoder, OperatingDate& date) {
+        double value;
+        if (!decoder.decodeDouble("date", value))
+            return false;
+
+        date = OperatingDate::fromWallTime(WallTime::fromRawSeconds(value));
+        return true;
+    });
+
+    if (!succeeded)
+        return;
+
+    m_operatingDates = mergeOperatingDates(m_operatingDates, WTFMove(operatingDates));
+}
+
+void ResourceLoadStatisticsMemoryStore::clear()
+{
+    ASSERT(!RunLoop::isMain());
+
+    m_resourceStatisticsMap.clear();
+    m_operatingDates.clear();
+
+    removeAllStorageAccess();
+    updateCookiePartitioningForDomains({ }, { }, { }, ShouldClearFirst::Yes, []() { });
+}
+
+bool ResourceLoadStatisticsMemoryStore::wasAccessedAsFirstPartyDueToUserInteraction(const ResourceLoadStatistics& current, const ResourceLoadStatistics& updated) const
+{
+    if (!current.hadUserInteraction && !updated.hadUserInteraction)
+        return false;
+
+    auto mostRecentUserInteractionTime = std::max(current.mostRecentUserInteractionTime, updated.mostRecentUserInteractionTime);
+
+    return updated.lastSeen <= mostRecentUserInteractionTime + m_parameters.timeToLiveCookiePartitionFree;
+}
+
+void ResourceLoadStatisticsMemoryStore::mergeStatistics(Vector<ResourceLoadStatistics>&& statistics)
+{
+    ASSERT(!RunLoop::isMain());
+
+    for (auto& statistic : statistics) {
+        auto result = m_resourceStatisticsMap.ensure(statistic.highLevelDomain, [&statistic] {
+            return WTFMove(statistic);
+        });
+        if (!result.isNewEntry) {
+            if (wasAccessedAsFirstPartyDueToUserInteraction(result.iterator->value, statistic))
+                result.iterator->value.timesAccessedAsFirstPartyDueToUserInteraction++;
+            result.iterator->value.merge(statistic);
+        }
+    }
+}
+
+bool ResourceLoadStatisticsMemoryStore::shouldPartitionCookies(const ResourceLoadStatistics& statistic)
+{
+    return statistic.isPrevalentResource && statistic.hadUserInteraction;
+}
+
+bool ResourceLoadStatisticsMemoryStore::shouldBlockCookies(const ResourceLoadStatistics& statistic)
+{
+    return statistic.isPrevalentResource && !statistic.hadUserInteraction;
+}
+
+bool ResourceLoadStatisticsMemoryStore::hasUserGrantedStorageAccessThroughPrompt(const ResourceLoadStatistics& statistic, const String& firstPartyPrimaryDomain)
+{
+    return statistic.storageAccessUnderTopFrameOrigins.contains(firstPartyPrimaryDomain);
+}
+
+void ResourceLoadStatisticsMemoryStore::updateCookiePartitioning(CompletionHandler<void()>&& completionHandler)
+{
+    ASSERT(!RunLoop::isMain());
+
+    Vector<String> domainsToPartition;
+    Vector<String> domainsToBlock;
+    Vector<String> domainsToNeitherPartitionNorBlock;
+    for (auto& resourceStatistic : m_resourceStatisticsMap.values()) {
+
+        bool shouldPartition = shouldPartitionCookies(resourceStatistic);
+        bool shouldBlock = shouldBlockCookies(resourceStatistic);
+
+        if (shouldPartition && !resourceStatistic.isMarkedForCookiePartitioning) {
+            domainsToPartition.append(resourceStatistic.highLevelDomain);
+            resourceStatistic.isMarkedForCookiePartitioning = true;
+        } else if (shouldBlock && !resourceStatistic.isMarkedForCookieBlocking) {
+            domainsToBlock.append(resourceStatistic.highLevelDomain);
+            resourceStatistic.isMarkedForCookieBlocking = true;
+        } else if (!shouldPartition && !shouldBlock && resourceStatistic.isPrevalentResource) {
+            domainsToNeitherPartitionNorBlock.append(resourceStatistic.highLevelDomain);
+            resourceStatistic.isMarkedForCookiePartitioning = false;
+            resourceStatistic.isMarkedForCookieBlocking = false;
+        }
+    }
+
+    if (domainsToPartition.isEmpty() && domainsToBlock.isEmpty() && domainsToNeitherPartitionNorBlock.isEmpty()) {
+        completionHandler();
+        return;
+    }
+
+#if !RELEASE_LOG_DISABLED
+    if (m_debugLoggingEnabled) {
+        if (!domainsToPartition.isEmpty())
+            RELEASE_LOG_INFO(ResourceLoadStatisticsDebug, "About to partition cookies in third-party contexts for %{public}s.", domainsToString(domainsToPartition).utf8().data());
+        if (!domainsToBlock.isEmpty())
+            RELEASE_LOG_INFO(ResourceLoadStatisticsDebug, "About to block cookies in third-party contexts for %{public}s.", domainsToString(domainsToBlock).utf8().data());
+        if (!domainsToNeitherPartitionNorBlock.isEmpty())
+            RELEASE_LOG_INFO(ResourceLoadStatisticsDebug, "About to neither partition nor block cookies in third-party contexts for %{public}s.", domainsToString(domainsToNeitherPartitionNorBlock).utf8().data());
+    }
+#endif
+
+    RunLoop::main().dispatch([this, store = makeRef(m_store), domainsToPartition = crossThreadCopy(domainsToPartition), domainsToBlock = crossThreadCopy(domainsToBlock), domainsToNeitherPartitionNorBlock = crossThreadCopy(domainsToNeitherPartitionNorBlock), completionHandler = WTFMove(completionHandler)] () mutable {
+        store->callUpdatePrevalentDomainsToPartitionOrBlockCookiesHandler(domainsToPartition, domainsToBlock, domainsToNeitherPartitionNorBlock, ShouldClearFirst::No, [this, store = store.copyRef(), completionHandler = WTFMove(completionHandler)]() mutable {
+            store->statisticsQueue().dispatch([this, completionHandler = WTFMove(completionHandler)] {
+                completionHandler();
+
+#if !RELEASE_LOG_DISABLED
+                RELEASE_LOG_INFO_IF(m_debugLoggingEnabled, ResourceLoadStatisticsDebug, "Done updating cookie partitioning and blocking.");
+#endif
+            });
+        });
+    });
+}
+
+void ResourceLoadStatisticsMemoryStore::updateCookiePartitioningForDomains(const Vector<String>& domainsToPartition, const Vector<String>& domainsToBlock, const Vector<String>& domainsToNeitherPartitionNorBlock, ShouldClearFirst shouldClearFirst, CompletionHandler<void()>&& completionHandler)
+{
+    ASSERT(!RunLoop::isMain());
+
+    if (domainsToPartition.isEmpty() && domainsToBlock.isEmpty() && domainsToNeitherPartitionNorBlock.isEmpty() && shouldClearFirst == ShouldClearFirst::No) {
+        completionHandler();
+        return;
+    }
+
+    if (shouldClearFirst == ShouldClearFirst::Yes)
+        resetCookiePartitioningState();
+    else {
+        for (auto& domain : domainsToNeitherPartitionNorBlock) {
+            auto& statistic = ensureResourceStatisticsForPrimaryDomain(domain);
+            statistic.isMarkedForCookiePartitioning = false;
+            statistic.isMarkedForCookieBlocking = false;
+        }
+    }
+
+    for (auto& domain : domainsToPartition)
+        ensureResourceStatisticsForPrimaryDomain(domain).isMarkedForCookiePartitioning = true;
+
+    for (auto& domain : domainsToBlock)
+        ensureResourceStatisticsForPrimaryDomain(domain).isMarkedForCookieBlocking = true;
+
+    RunLoop::main().dispatch([store = makeRef(m_store), shouldClearFirst, domainsToPartition = crossThreadCopy(domainsToPartition), domainsToBlock = crossThreadCopy(domainsToBlock), domainsToNeitherPartitionNorBlock = crossThreadCopy(domainsToNeitherPartitionNorBlock), completionHandler = WTFMove(completionHandler)] () mutable {
+        store->callUpdatePrevalentDomainsToPartitionOrBlockCookiesHandler(domainsToPartition, domainsToBlock, domainsToNeitherPartitionNorBlock, shouldClearFirst, [store = store.copyRef(), completionHandler = WTFMove(completionHandler)]() mutable {
+            store->statisticsQueue().dispatch([completionHandler = WTFMove(completionHandler)]() mutable {
+                completionHandler();
+            });
+        });
+    });
+}
+
+void ResourceLoadStatisticsMemoryStore::clearPartitioningStateForDomains(const Vector<String>& domains, CompletionHandler<void()>&& completionHandler)
+{
+    ASSERT(!RunLoop::isMain());
+
+    if (domains.isEmpty()) {
+        completionHandler();
+        return;
+    }
+
+    RunLoop::main().dispatch([store = makeRef(m_store), domains = crossThreadCopy(domains)] {
+        store->callRemoveDomainsHandler(domains);
+    });
+
+    for (auto& domain : domains) {
+        auto& statistic = ensureResourceStatisticsForPrimaryDomain(domain);
+        statistic.isMarkedForCookiePartitioning = false;
+        statistic.isMarkedForCookieBlocking = false;
+    }
+
+    completionHandler();
+}
+
+void ResourceLoadStatisticsMemoryStore::resetCookiePartitioningState()
+{
+    ASSERT(!RunLoop::isMain());
+
+    for (auto& resourceStatistic : m_resourceStatisticsMap.values()) {
+        resourceStatistic.isMarkedForCookiePartitioning = false;
+        resourceStatistic.isMarkedForCookieBlocking = false;
+    }
+}
+
+void ResourceLoadStatisticsMemoryStore::processStatistics(const Function<void(const ResourceLoadStatistics&)>& processFunction) const
+{
+    ASSERT(!RunLoop::isMain());
+
+    for (auto& resourceStatistic : m_resourceStatisticsMap.values())
+        processFunction(resourceStatistic);
+}
+
+bool ResourceLoadStatisticsMemoryStore::hasHadUnexpiredRecentUserInteraction(ResourceLoadStatistics& resourceStatistic) const
+{
+    ASSERT(!RunLoop::isMain());
+
+    if (resourceStatistic.hadUserInteraction && hasStatisticsExpired(resourceStatistic)) {
+        // Drop privacy sensitive data because we no longer need it.
+        // Set timestamp to 0 so that statistics merge will know
+        // it has been reset as opposed to its default -1.
+        resourceStatistic.mostRecentUserInteractionTime = { };
+        resourceStatistic.storageAccessUnderTopFrameOrigins.clear();
+        resourceStatistic.hadUserInteraction = false;
+    }
+
+    return resourceStatistic.hadUserInteraction;
+}
+
+Vector<String> ResourceLoadStatisticsMemoryStore::topPrivatelyControlledDomainsToRemoveWebsiteDataFor()
+{
+    ASSERT(!RunLoop::isMain());
+
+    bool shouldCheckForGrandfathering = m_endOfGrandfatheringTimestamp > WallTime::now();
+    bool shouldClearGrandfathering = !shouldCheckForGrandfathering && m_endOfGrandfatheringTimestamp;
+
+    if (shouldClearGrandfathering)
+        m_endOfGrandfatheringTimestamp = { };
+
+    Vector<String> prevalentResources;
+    for (auto& statistic : m_resourceStatisticsMap.values()) {
+        if (statistic.isPrevalentResource && !hasHadUnexpiredRecentUserInteraction(statistic) && (!shouldCheckForGrandfathering || !statistic.grandfathered))
+            prevalentResources.append(statistic.highLevelDomain);
+
+        if (shouldClearGrandfathering && statistic.grandfathered)
+            statistic.grandfathered = false;
+    }
+
+    return prevalentResources;
+}
+
+void ResourceLoadStatisticsMemoryStore::includeTodayAsOperatingDateIfNecessary()
+{
+    ASSERT(!RunLoop::isMain());
+
+    auto today = OperatingDate::today();
+    if (!m_operatingDates.isEmpty() && today <= m_operatingDates.last())
+        return;
+
+    while (m_operatingDates.size() >= operatingDatesWindow)
+        m_operatingDates.remove(0);
+
+    m_operatingDates.append(today);
+}
+
+bool ResourceLoadStatisticsMemoryStore::hasStatisticsExpired(const ResourceLoadStatistics& resourceStatistic) const
+{
+    ASSERT(!RunLoop::isMain());
+
+    if (m_operatingDates.size() >= operatingDatesWindow) {
+        if (OperatingDate::fromWallTime(resourceStatistic.mostRecentUserInteractionTime) < m_operatingDates.first())
+            return true;
+    }
+
+    // If we don't meet the real criteria for an expired statistic, check the user setting for a tighter restriction (mainly for testing).
+    if (m_parameters.timeToLiveUserInteraction) {
+        if (WallTime::now() > resourceStatistic.mostRecentUserInteractionTime + m_parameters.timeToLiveUserInteraction.value())
+            return true;
+    }
+
+    return false;
+}
+
+void ResourceLoadStatisticsMemoryStore::setMaxStatisticsEntries(size_t maximumEntryCount)
+{
+    ASSERT(!RunLoop::isMain());
+
+    m_parameters.maxStatisticsEntries = maximumEntryCount;
+}
+
+void ResourceLoadStatisticsMemoryStore::setPruneEntriesDownTo(size_t pruneTargetCount)
+{
+    ASSERT(!RunLoop::isMain());
+
+    m_parameters.pruneEntriesDownTo = pruneTargetCount;
+}
+
+void ResourceLoadStatisticsMemoryStore::pruneStatisticsIfNeeded()
+{
+    ASSERT(!RunLoop::isMain());
+
+    if (m_resourceStatisticsMap.size() <= m_parameters.maxStatisticsEntries)
+        return;
+
+    ASSERT(m_parameters.pruneEntriesDownTo <= m_parameters.maxStatisticsEntries);
+
+    size_t numberOfEntriesLeftToPrune = m_resourceStatisticsMap.size() - m_parameters.pruneEntriesDownTo;
+    ASSERT(numberOfEntriesLeftToPrune);
+
+    Vector<StatisticsLastSeen> resourcesToPrunePerImportance[maxImportance + 1];
+    for (auto& resourceStatistic : m_resourceStatisticsMap.values())
+        resourcesToPrunePerImportance[computeImportance(resourceStatistic)].append({ resourceStatistic.highLevelDomain, resourceStatistic.lastSeen });
+
+    for (unsigned importance = 0; numberOfEntriesLeftToPrune && importance <= maxImportance; ++importance)
+        pruneResources(m_resourceStatisticsMap, resourcesToPrunePerImportance[importance], numberOfEntriesLeftToPrune);
+
+    ASSERT(!numberOfEntriesLeftToPrune);
+}
+
+void ResourceLoadStatisticsMemoryStore::resetParametersToDefaultValues()
+{
+    ASSERT(!RunLoop::isMain());
+
+    m_parameters = { };
+}
+
+void ResourceLoadStatisticsMemoryStore::logTestingEvent(const String& event)
+{
+    ASSERT(!RunLoop::isMain());
+
+    RunLoop::main().dispatch([store = makeRef(m_store), event = event.isolatedCopy()] {
+        store->logTestingEvent(event);
+    });
+}
+
+void ResourceLoadStatisticsMemoryStore::setLastSeen(const String& primaryDomain, Seconds seconds)
+{
+    ASSERT(!RunLoop::isMain());
+
+    auto& statistics = ensureResourceStatisticsForPrimaryDomain(primaryDomain);
+    statistics.lastSeen = WallTime::fromRawSeconds(seconds.seconds());
+}
+
+void ResourceLoadStatisticsMemoryStore::setPrevalentResource(const String& primaryDomain)
+{
+    ASSERT(!RunLoop::isMain());
+
+    auto& resourceStatistic = ensureResourceStatisticsForPrimaryDomain(primaryDomain);
+    setPrevalentResource(resourceStatistic, ResourceLoadPrevalence::High);
+}
+
+void ResourceLoadStatisticsMemoryStore::setVeryPrevalentResource(const String& primaryDomain)
+{
+    ASSERT(!RunLoop::isMain());
+
+    auto& resourceStatistic = ensureResourceStatisticsForPrimaryDomain(primaryDomain);
+    setPrevalentResource(resourceStatistic, ResourceLoadPrevalence::VeryHigh);
+}
+
+void ResourceLoadStatisticsMemoryStore::removeAllStorageAccess()
+{
+    ASSERT(!RunLoop::isMain());
+    RunLoop::main().dispatch([store = makeRef(m_store)] () {
+        store->removeAllStorageAccess();
+    });
+}
+
+} // namespace WebKit
diff --git a/Source/WebKit/UIProcess/ResourceLoadStatisticsMemoryStore.h b/Source/WebKit/UIProcess/ResourceLoadStatisticsMemoryStore.h
new file mode 100644 (file)
index 0000000..4a08b7b
--- /dev/null
@@ -0,0 +1,192 @@
+/*
+ * Copyright (C) 2017-2018 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#include "ResourceLoadStatisticsClassifier.h"
+#include "WebResourceLoadStatisticsStore.h"
+#include <wtf/CompletionHandler.h>
+#include <wtf/Vector.h>
+#include <wtf/WeakPtr.h>
+#include <wtf/WorkQueue.h>
+
+#if HAVE(CORE_PREDICTION)
+#include "ResourceLoadStatisticsClassifierCocoa.h"
+#endif
+
+namespace WebCore {
+class KeyedDecoder;
+class KeyedEncoder;
+struct ResourceLoadStatistics;
+}
+
+namespace WebKit {
+
+class OperatingDate;
+class ResourceLoadStatisticsPersistentStorage;
+
+// This is always constructed / used / destroyed on the WebResourceLoadStatisticsStore's statistics queue.
+class ResourceLoadStatisticsMemoryStore : public CanMakeWeakPtr<ResourceLoadStatisticsMemoryStore> {
+public:
+    ResourceLoadStatisticsMemoryStore(WebResourceLoadStatisticsStore&, WorkQueue&);
+    ~ResourceLoadStatisticsMemoryStore();
+
+    void setPersistentStorage(ResourceLoadStatisticsPersistentStorage&);
+
+    void clear();
+    bool isEmpty() const { return m_resourceStatisticsMap.isEmpty(); }
+
+    std::unique_ptr<WebCore::KeyedEncoder> createEncoderFromData() const;
+    void mergeWithDataFromDecoder(WebCore::KeyedDecoder&);
+
+    void mergeStatistics(Vector<WebCore::ResourceLoadStatistics>&&);
+    void processStatistics(const Function<void(const WebCore::ResourceLoadStatistics&)>&) const;
+
+    void resetCookiePartitioningState();
+    void updateCookiePartitioning(CompletionHandler<void()>&&);
+    void updateCookiePartitioningForDomains(const Vector<String>& domainsToPartition, const Vector<String>& domainsToBlock, const Vector<String>& domainsToNeitherPartitionNorBlock, ShouldClearFirst, CompletionHandler<void()>&&);
+    void clearPartitioningStateForDomains(const Vector<String>& domains, CompletionHandler<void()>&&);
+
+    void includeTodayAsOperatingDateIfNecessary();
+    void processStatisticsAndDataRecords();
+
+    void requestStorageAccessUnderOpener(String&& primaryDomainInNeedOfStorageAccess, uint64_t openerPageID, String&& openerPrimaryDomain, bool isTriggeredByUserGesture);
+    void removeAllStorageAccess();
+
+    void grandfatherExistingWebsiteData(CompletionHandler<void()>&&);
+    void cancelPendingStatisticsProcessingRequest();
+
+    bool isRegisteredAsSubFrameUnder(const String& subFramePrimaryDomain, const String& topFramePrimaryDomain) const;
+    bool isRegisteredAsRedirectingTo(const String& hostRedirectedFromPrimaryDomain, const String& hostRedirectedToPrimaryDomain) const;
+
+    void clearPrevalentResource(const String& primaryDomain);
+    bool isPrevalentResource(const String& primaryDomain) const;
+    bool isVeryPrevalentResource(const String& primaryDomain) const;
+    void setPrevalentResource(const String& primaryDomain);
+    void setVeryPrevalentResource(const String& primaryDomain);
+
+    void setGrandfathered(const String& primaryDomain, bool value);
+    bool isGrandfathered(const String& primaryDomain) const;
+
+    void setSubframeUnderTopFrameOrigin(const String& primarySubFrameDomain, const String& primaryTopFrameDomain);
+    void setSubresourceUnderTopFrameOrigin(const String& primarySubresourceDomain, const String& primaryTopFrameDomain);
+    void setSubresourceUniqueRedirectTo(const String& primarySubresourceDomain, const String& primaryRedirectDomain);
+    void setSubresourceUniqueRedirectFrom(const String& primarySubresourceDomain, const String& primaryRedirectDomain);
+    void setTopFrameUniqueRedirectTo(const String& topFramePrimaryDomain, const String& primaryRedirectDomain);
+    void setTopFrameUniqueRedirectFrom(const String& topFramePrimaryDomain, const String& primaryRedirectDomain);
+
+    void logTestingEvent(const String&);
+
+    void setMaxStatisticsEntries(size_t maximumEntryCount);
+    void setPruneEntriesDownTo(size_t pruneTargetCount);
+    void resetParametersToDefaultValues();
+
+    void calculateAndSubmitTelemetry() const;
+
+    void setNotifyPagesWhenDataRecordsWereScanned(bool);
+    void setShouldClassifyResourcesBeforeDataRecordsRemoval(bool);
+    void setShouldSubmitTelemetry(bool);
+    void setTimeToLiveUserInteraction(Seconds);
+    void setTimeToLiveCookiePartitionFree(Seconds);
+    void setMinimumTimeBetweenDataRecordsRemoval(Seconds);
+    void setGrandfatheringTime(Seconds);
+    void setResourceLoadStatisticsDebugMode(bool);
+
+    void hasStorageAccess(const String& subFramePrimaryDomain, const String& topFramePrimaryDomain, uint64_t frameID, uint64_t pageID, CompletionHandler<void(bool)>&&);
+    void requestStorageAccess(String&& subFramePrimaryDomain, String&& topFramePrimaryDomain, uint64_t frameID, uint64_t pageID, bool promptEnabled, CompletionHandler<void(StorageAccessStatus)>&&);
+    void grantStorageAccess(String&& subFrameHost, String&& topFrameHost, uint64_t frameID, uint64_t pageID, bool userWasPromptedNow, CompletionHandler<void(bool)>&&);
+
+    void logNonRecentUserInteraction(const String& primaryDomain);
+    void logFrameNavigation(const String& targetPrimaryDomain, const String& mainFramePrimaryDomain, const String& sourcePrimaryDomain, const String& targetHost, const String& mainFrameHost, bool areTargetAndMainFrameDomainsAssociated, bool areTargetAndSourceDomainsAssociated, bool isRedirect, bool isMainFrame);
+    void logUserInteraction(const String& primaryDomain);
+
+    void clearUserInteraction(const String& primaryDomain);
+    bool hasHadUserInteraction(const String& primaryDomain);
+
+    void setLastSeen(const String& primaryDomain, Seconds);
+
+private:
+    static bool shouldPartitionCookies(const WebCore::ResourceLoadStatistics&);
+    static bool shouldBlockCookies(const WebCore::ResourceLoadStatistics&);
+    static bool hasUserGrantedStorageAccessThroughPrompt(const WebCore::ResourceLoadStatistics&, const String& firstPartyPrimaryDomain);
+    bool hasHadUnexpiredRecentUserInteraction(WebCore::ResourceLoadStatistics&) const;
+    bool hasStatisticsExpired(const WebCore::ResourceLoadStatistics&) const;
+    bool wasAccessedAsFirstPartyDueToUserInteraction(const WebCore::ResourceLoadStatistics& current, const WebCore::ResourceLoadStatistics& updated) const;
+    void setPrevalentResource(WebCore::ResourceLoadStatistics&, ResourceLoadPrevalence);
+    unsigned recursivelyGetAllDomainsThatHaveRedirectedToThisDomain(const WebCore::ResourceLoadStatistics&, HashSet<String>& domainsThatHaveRedirectedTo, unsigned numberOfRecursiveCalls) const;
+    void setStorageAccessPromptsEnabled(bool enabled) { m_storageAccessPromptsEnabled  = enabled; }
+    bool shouldRemoveDataRecords() const;
+    void setDebugLogggingEnabled(bool enabled) { m_debugLoggingEnabled  = enabled; }
+    void setDataRecordsBeingRemoved(bool);
+    void scheduleStatisticsProcessingRequestIfNecessary();
+    void grantStorageAccessInternal(String&& subFrameHost, String&& topFrameHost, std::optional<uint64_t> frameID, uint64_t pageID, bool userWasPromptedNowOrEarlier, CompletionHandler<void(bool)>&&);
+    void markAsPrevalentIfHasRedirectedToPrevalent(WebCore::ResourceLoadStatistics&);
+    void removeDataRecords(CompletionHandler<void()>&&);
+    void pruneStatisticsIfNeeded();
+    WebCore::ResourceLoadStatistics& ensureResourceStatisticsForPrimaryDomain(const String&);
+    Vector<String> topPrivatelyControlledDomainsToRemoveWebsiteDataFor();
+
+#if PLATFORM(COCOA)
+    void registerUserDefaultsIfNeeded();
+#endif
+
+    struct Parameters {
+        size_t pruneEntriesDownTo { 800 };
+        size_t maxStatisticsEntries { 1000 };
+        std::optional<Seconds> timeToLiveUserInteraction;
+        Seconds timeToLiveCookiePartitionFree { 24_h };
+        Seconds minimumTimeBetweenDataRecordsRemoval { 1_h };
+        Seconds grandfatheringTime { 24_h * 7 };
+        bool shouldNotifyPagesWhenDataRecordsWereScanned { false };
+        bool shouldClassifyResourcesBeforeDataRecordsRemoval { true };
+        bool shouldSubmitTelemetry { true };
+    };
+
+    WebResourceLoadStatisticsStore& m_store;
+    Ref<WorkQueue> m_workQueue;
+    WeakPtr<ResourceLoadStatisticsPersistentStorage> m_persistentStorage;
+    HashMap<String, WebCore::ResourceLoadStatistics> m_resourceStatisticsMap;
+#if HAVE(CORE_PREDICTION)
+    ResourceLoadStatisticsClassifierCocoa m_resourceLoadStatisticsClassifier;
+#else
+    ResourceLoadStatisticsClassifier m_resourceLoadStatisticsClassifier;
+#endif
+#if ENABLE(NETSCAPE_PLUGIN_API)
+    HashSet<uint64_t> m_activePluginTokens;
+#endif
+    Parameters m_parameters;
+    Vector<OperatingDate> m_operatingDates;
+    WallTime m_endOfGrandfatheringTimestamp;
+    bool m_debugLoggingEnabled { false };
+    bool m_debugModeEnabled { false };
+    bool m_storageAccessPromptsEnabled { false };
+    bool m_dataRecordsBeingRemoved { false };
+    MonotonicTime m_lastTimeDataRecordsWereRemoved;
+
+    uint64_t m_lastStatisticsProcessingRequestIdentifier { 0 };
+    std::optional<uint64_t> m_pendingStatisticsProcessingRequestIdentifier;
+};
+
+} // namespace WebKit
index 2344680..0968722 100644 (file)
@@ -27,6 +27,7 @@
 #include "ResourceLoadStatisticsPersistentStorage.h"
 
 #include "Logging.h"
+#include "ResourceLoadStatisticsMemoryStore.h"
 #include "WebResourceLoadStatisticsStore.h"
 #include <WebCore/FileMonitor.h>
 #include <WebCore/FileSystem.h>
@@ -81,13 +82,16 @@ static std::unique_ptr<KeyedDecoder> createDecoderForFile(const String& path)
     return KeyedDecoder::decoder(reinterpret_cast<const uint8_t*>(buffer.data()), buffer.size());
 }
 
-ResourceLoadStatisticsPersistentStorage::ResourceLoadStatisticsPersistentStorage(WebResourceLoadStatisticsStore& store, const String& storageDirectoryPath, IsReadOnly isReadOnly)
-    : m_memoryStore(store)
+ResourceLoadStatisticsPersistentStorage::ResourceLoadStatisticsPersistentStorage(ResourceLoadStatisticsMemoryStore& memoryStore, WorkQueue& workQueue, const String& storageDirectoryPath, IsReadOnly isReadOnly)
+    : m_memoryStore(memoryStore)
+    , m_workQueue(workQueue)
     , m_storageDirectoryPath(storageDirectoryPath)
     , m_isReadOnly(isReadOnly)
 {
     ASSERT(!RunLoop::isMain());
 
+    m_memoryStore.setPersistentStorage(*this);
+
     populateMemoryStoreFromDisk();
     startMonitoringDisk();
 }
@@ -124,7 +128,7 @@ void ResourceLoadStatisticsPersistentStorage::startMonitoringDisk()
     if (resourceLogPath.isEmpty())
         return;
 
-    m_fileMonitor = std::make_unique<FileMonitor>(resourceLogPath, m_memoryStore.statisticsQueue(), [this, weakThis = makeWeakPtr(*this)] (FileMonitor::FileChangeType type) {
+    m_fileMonitor = std::make_unique<FileMonitor>(resourceLogPath, m_workQueue.copyRef(), [this, weakThis = makeWeakPtr(*this)] (FileMonitor::FileChangeType type) {
         ASSERT(!RunLoop::isMain());
         if (!weakThis)
             return;
@@ -134,7 +138,7 @@ void ResourceLoadStatisticsPersistentStorage::startMonitoringDisk()
             refreshMemoryStoreFromDisk();
             break;
         case FileMonitor::FileChangeType::Removal:
-            m_memoryStore.clearInMemory();
+            m_memoryStore.clear();
             m_fileMonitor = nullptr;
             monitorDirectoryForNewStatistics();
             break;
@@ -156,7 +160,7 @@ void ResourceLoadStatisticsPersistentStorage::monitorDirectoryForNewStatistics()
         }
     }
 
-    m_fileMonitor = std::make_unique<FileMonitor>(storagePath, m_memoryStore.statisticsQueue(), [this] (FileMonitor::FileChangeType type) {
+    m_fileMonitor = std::make_unique<FileMonitor>(storagePath, m_workQueue.copyRef(), [this] (FileMonitor::FileChangeType type) {
         ASSERT(!RunLoop::isMain());
         if (type == FileMonitor::FileChangeType::Removal) {
             // Directory was removed!
@@ -286,7 +290,7 @@ void ResourceLoadStatisticsPersistentStorage::scheduleOrWriteMemoryStore(ForceIm
         if (!m_hasPendingWrite) {
             m_hasPendingWrite = true;
             Seconds delay = minimumWriteInterval - timeSinceLastWrite + 1_s;
-            m_memoryStore.statisticsQueue().dispatchAfter(delay, [weakThis = makeWeakPtr(*this)] {
+            m_workQueue->dispatchAfter(delay, [weakThis = makeWeakPtr(*this)] {
                 if (weakThis)
                     weakThis->writeMemoryStoreToDisk();
             });
index b58de87..05ea993 100644 (file)
@@ -30,6 +30,7 @@
 #include <wtf/RunLoop.h>
 #include <wtf/WallTime.h>
 #include <wtf/WeakPtr.h>
+#include <wtf/WorkQueue.h>
 #include <wtf/text/WTFString.h>
 
 namespace WebCore {
@@ -38,13 +39,13 @@ class FileMonitor;
 
 namespace WebKit {
 
-class WebResourceLoadStatisticsStore;
+class ResourceLoadStatisticsMemoryStore;
 
 // Can only be constructed / destroyed / used from the WebResourceLoadStatisticsStore's statistic queue.
 class ResourceLoadStatisticsPersistentStorage : public CanMakeWeakPtr<ResourceLoadStatisticsPersistentStorage> {
 public:
     enum class IsReadOnly { No, Yes };
-    ResourceLoadStatisticsPersistentStorage(WebResourceLoadStatisticsStore&, const String& storageDirectoryPath, IsReadOnly);
+    ResourceLoadStatisticsPersistentStorage(ResourceLoadStatisticsMemoryStore&, WorkQueue&, const String& storageDirectoryPath, IsReadOnly);
     ~ResourceLoadStatisticsPersistentStorage();
 
     void clear();
@@ -65,7 +66,8 @@ private:
     void excludeFromBackup() const;
     void refreshMemoryStoreFromDisk();
 
-    WebResourceLoadStatisticsStore& m_memoryStore;
+    ResourceLoadStatisticsMemoryStore& m_memoryStore;
+    Ref<WorkQueue> m_workQueue;
     const String m_storageDirectoryPath;
     std::unique_ptr<WebCore::FileMonitor> m_fileMonitor;
     WallTime m_lastStatisticsFileSyncTime;
index b08c281..3057e00 100644 (file)
@@ -27,8 +27,8 @@
 #include "WebResourceLoadStatisticsStore.h"
 
 #include "Logging.h"
-#include "PluginProcessManager.h"
-#include "PluginProcessProxy.h"
+#include "ResourceLoadStatisticsMemoryStore.h"
+#include "ResourceLoadStatisticsPersistentStorage.h"
 #include "WebFrameProxy.h"
 #include "WebPageProxy.h"
 #include "WebProcessMessages.h"
 #include "WebResourceLoadStatisticsTelemetry.h"
 #include "WebsiteDataFetchOption.h"
 #include "WebsiteDataStore.h"
-#include <WebCore/KeyedCoding.h>
 #include <WebCore/ResourceLoadStatistics.h>
 #include <wtf/CrossThreadCopier.h>
-#include <wtf/DateMath.h>
-#include <wtf/MathExtras.h>
 #include <wtf/NeverDestroyed.h>
-#if !RELEASE_LOG_DISABLED
-#include <wtf/text/StringBuilder.h>
-#endif
 #include <wtf/threads/BinarySemaphore.h>
 
 namespace WebKit {
 using namespace WebCore;
 
-constexpr unsigned operatingDatesWindow { 30 };
-constexpr unsigned statisticsModelVersion { 12 };
-constexpr unsigned maxImportance { 3 };
-constexpr unsigned maxNumberOfRecursiveCallsInRedirectTraceBack { 50 };
-constexpr Seconds minimumStatisticsProcessingInterval { 5_s };
-
 template<typename T> static inline String isolatedPrimaryDomain(const T& value)
 {
     return ResourceLoadStatistics::primaryDomain(value).isolatedCopy();
@@ -93,75 +81,34 @@ const OptionSet<WebsiteDataType>& WebResourceLoadStatisticsStore::monitoredDataT
     return dataTypes;
 }
 
-class OperatingDate {
-public:
-    OperatingDate() = default;
-
-    static OperatingDate fromWallTime(WallTime time)
-    {
-        double ms = time.secondsSinceEpoch().milliseconds();
-        int year = msToYear(ms);
-        int yearDay = dayInYear(ms, year);
-        int month = monthFromDayInYear(yearDay, isLeapYear(year));
-        int monthDay = dayInMonthFromDayInYear(yearDay, isLeapYear(year));
-
-        return OperatingDate { year, month, monthDay };
-    }
-
-    static OperatingDate today()
-    {
-        return OperatingDate::fromWallTime(WallTime::now());
-    }
-
-    Seconds secondsSinceEpoch() const
-    {
-        return Seconds { dateToDaysFrom1970(m_year, m_month, m_monthDay) * secondsPerDay };
-    }
-
-    bool operator==(const OperatingDate& other) const
-    {
-        return m_monthDay == other.m_monthDay && m_month == other.m_month && m_year == other.m_year;
-    }
-
-    bool operator<(const OperatingDate& other) const
-    {
-        return secondsSinceEpoch() < other.secondsSinceEpoch();
-    }
-
-    bool operator<=(const OperatingDate& other) const
-    {
-        return secondsSinceEpoch() <= other.secondsSinceEpoch();
-    }
-
-private:
-    OperatingDate(int year, int month, int monthDay)
-        : m_year(year)
-        , m_month(month)
-        , m_monthDay(monthDay)
-    { }
+void WebResourceLoadStatisticsStore::setNotifyPagesWhenDataRecordsWereScanned(bool value)
+{
+    ASSERT(RunLoop::isMain());
 
-    int m_year { 0 };
-    int m_month { 0 }; // [0, 11].
-    int m_monthDay { 0 }; // [1, 31].
-};
+    m_statisticsQueue->dispatch([this, protectedThis = makeRef(*this), value] {
+        if (m_memoryStore)
+            m_memoryStore->setNotifyPagesWhenDataRecordsWereScanned(value);
+    });
+}
 
-static Vector<OperatingDate> mergeOperatingDates(const Vector<OperatingDate>& existingDates, Vector<OperatingDate>&& newDates)
+void WebResourceLoadStatisticsStore::setShouldClassifyResourcesBeforeDataRecordsRemoval(bool value)
 {
-    if (existingDates.isEmpty())
-        return WTFMove(newDates);
-
-    Vector<OperatingDate> mergedDates(existingDates.size() + newDates.size());
+    ASSERT(RunLoop::isMain());
 
-    // Merge the two sorted vectors of dates.
-    std::merge(existingDates.begin(), existingDates.end(), newDates.begin(), newDates.end(), mergedDates.begin());
-    // Remove duplicate dates.
-    removeRepeatedElements(mergedDates);
+    m_statisticsQueue->dispatch([this, protectedThis = makeRef(*this), value] {
+        if (m_memoryStore)
+            m_memoryStore->setShouldClassifyResourcesBeforeDataRecordsRemoval(value);
+    });
+}
 
-    // Drop old dates until the Vector size reaches operatingDatesWindow.
-    while (mergedDates.size() > operatingDatesWindow)
-        mergedDates.remove(0);
+void WebResourceLoadStatisticsStore::setShouldSubmitTelemetry(bool value)
+{
+    ASSERT(RunLoop::isMain());
 
-    return mergedDates;
+    m_statisticsQueue->dispatch([this, protectedThis = makeRef(*this), value] {
+        if (m_memoryStore)
+            m_memoryStore->setShouldSubmitTelemetry(value);
+    });
 }
 
 WebResourceLoadStatisticsStore::WebResourceLoadStatisticsStore(const String& resourceLoadStatisticsDirectory, Function<void(const String&)>&& testingCallback, bool isEphemeral, UpdatePrevalentDomainsToPartitionOrBlockCookiesHandler&& updatePrevalentDomainsToPartitionOrBlockCookiesHandler, HasStorageAccessForFrameHandler&& hasStorageAccessForFrameHandler, GrantStorageAccessHandler&& grantStorageAccessHandler, RemoveAllStorageAccessHandler&& removeAllStorageAccessHandler, RemovePrevalentDomainsHandler&& removeDomainsHandler)
@@ -176,18 +123,14 @@ WebResourceLoadStatisticsStore::WebResourceLoadStatisticsStore(const String& res
 {
     ASSERT(RunLoop::isMain());
 
-#if PLATFORM(COCOA)
-    registerUserDefaultsIfNeeded();
-#endif
-
     m_statisticsQueue->dispatch([this, protectedThis = makeRef(*this), isEphemeral, resourceLoadStatisticsDirectory = resourceLoadStatisticsDirectory.isolatedCopy()] {
-        m_persistentStorage = std::make_unique<ResourceLoadStatisticsPersistentStorage>(*this, resourceLoadStatisticsDirectory, isEphemeral ? ResourceLoadStatisticsPersistentStorage::IsReadOnly::Yes : ResourceLoadStatisticsPersistentStorage::IsReadOnly::No);
-        includeTodayAsOperatingDateIfNecessary();
+        m_memoryStore = std::make_unique<ResourceLoadStatisticsMemoryStore>(*this, m_statisticsQueue);
+        m_persistentStorage = std::make_unique<ResourceLoadStatisticsPersistentStorage>(*m_memoryStore, m_statisticsQueue, resourceLoadStatisticsDirectory, isEphemeral ? ResourceLoadStatisticsPersistentStorage::IsReadOnly::Yes : ResourceLoadStatisticsPersistentStorage::IsReadOnly::No);
     });
 
     m_statisticsQueue->dispatchAfter(5_s, [this, protectedThis = makeRef(*this)] {
-        if (m_parameters.shouldSubmitTelemetry)
-            WebResourceLoadStatisticsTelemetry::calculateAndSubmit(*this);
+        if (m_memoryStore)
+            m_memoryStore->calculateAndSubmitTelemetry();
     });
 
     m_dailyTasksTimer.startRepeating(24_h);
@@ -200,7 +143,9 @@ WebResourceLoadStatisticsStore::~WebResourceLoadStatisticsStore()
 
 void WebResourceLoadStatisticsStore::flushAndDestroyPersistentStore()
 {
-    if (!m_persistentStorage)
+    ASSERT(RunLoop::isMain());
+
+    if (!m_persistentStorage && !m_memoryStore)
         return;
 
     // Make sure we destroy the persistent store on the background queue and wait for it to die
@@ -208,184 +153,48 @@ void WebResourceLoadStatisticsStore::flushAndDestroyPersistentStore()
     BinarySemaphore semaphore;
     m_statisticsQueue->dispatch([&semaphore, this] {
         m_persistentStorage = nullptr;
+        m_memoryStore = nullptr;
         semaphore.signal();
     });
     semaphore.wait(WallTime::infinity());
 }
 
-#if !RELEASE_LOG_DISABLED
-static void appendWithDelimiter(StringBuilder& builder, const String& domain, bool isFirstItem)
+void WebResourceLoadStatisticsStore::setResourceLoadStatisticsDebugMode(bool value)
 {
-    if (!isFirstItem)
-        builder.appendLiteral(", ");
-    builder.append(domain);
-}
-#endif
-
-void WebResourceLoadStatisticsStore::removeDataRecords(CompletionHandler<void()>&& callback)
-{
-    ASSERT(!RunLoop::isMain());
-    
-    if (!shouldRemoveDataRecords()) {
-        callback();
-        return;
-    }
-
-#if ENABLE(NETSCAPE_PLUGIN_API)
-    m_activePluginTokens.clear();
-    for (auto plugin : PluginProcessManager::singleton().pluginProcesses())
-        m_activePluginTokens.add(plugin->pluginProcessToken());
-#endif
-
-    auto prevalentResourceDomains = topPrivatelyControlledDomainsToRemoveWebsiteDataFor();
-    if (prevalentResourceDomains.isEmpty()) {
-        callback();
-        return;
-    }
-
-#if !RELEASE_LOG_DISABLED
-    if (m_debugLoggingEnabled) {
-        StringBuilder domainsToRemoveDataRecordsForBuilder;
-        bool isFirstItem = true;
-        for (auto& domain : prevalentResourceDomains) {
-            appendWithDelimiter(domainsToRemoveDataRecordsForBuilder, domain, isFirstItem);
-            isFirstItem = false;
-        }
-        RELEASE_LOG_INFO(ResourceLoadStatisticsDebug, "About to remove data records for %{public}s.", domainsToRemoveDataRecordsForBuilder.toString().utf8().data());
-    }
-#endif
+    ASSERT(RunLoop::isMain());
 
-    setDataRecordsBeingRemoved(true);
-
-    RunLoop::main().dispatch([prevalentResourceDomains = crossThreadCopy(prevalentResourceDomains), callback = WTFMove(callback), this, protectedThis = makeRef(*this)] () mutable {
-        WebProcessProxy::deleteWebsiteDataForTopPrivatelyControlledDomainsInAllPersistentDataStores(WebResourceLoadStatisticsStore::monitoredDataTypes(), WTFMove(prevalentResourceDomains), m_parameters.shouldNotifyPagesWhenDataRecordsWereScanned, [callback = WTFMove(callback), this, protectedThis = WTFMove(protectedThis)](const HashSet<String>& domainsWithDeletedWebsiteData) mutable {
-            m_statisticsQueue->dispatch([topDomains = crossThreadCopy(domainsWithDeletedWebsiteData), callback = WTFMove(callback), this, protectedThis = WTFMove(protectedThis)] () mutable {
-                for (auto& prevalentResourceDomain : topDomains) {
-                    auto& statistic = ensureResourceStatisticsForPrimaryDomain(prevalentResourceDomain);
-                    ++statistic.dataRecordsRemoved;
-                }
-                setDataRecordsBeingRemoved(false);
-                callback();
-#if !RELEASE_LOG_DISABLED
-                RELEASE_LOG_INFO_IF(m_debugLoggingEnabled, ResourceLoadStatisticsDebug, "Done removing data records.");
-#endif
-            });
-        });
+    m_statisticsQueue->dispatch([this, protectedThis = makeRef(*this), value] {
+        if (m_memoryStore)
+            m_memoryStore->setResourceLoadStatisticsDebugMode(value);
     });
 }
 
 void WebResourceLoadStatisticsStore::scheduleStatisticsAndDataRecordsProcessing()
 {
     ASSERT(RunLoop::isMain());
+
     m_statisticsQueue->dispatch([this, protectedThis = makeRef(*this)] {
-        processStatisticsAndDataRecords();
+        if (m_memoryStore)
+            m_memoryStore->processStatisticsAndDataRecords();
     });
 }
 
-
-unsigned WebResourceLoadStatisticsStore::recursivelyGetAllDomainsThatHaveRedirectedToThisDomain(const WebCore::ResourceLoadStatistics& resourceStatistic, HashSet<String>& domainsThatHaveRedirectedTo, unsigned numberOfRecursiveCalls)
-{
-    if (numberOfRecursiveCalls >= maxNumberOfRecursiveCallsInRedirectTraceBack) {
-        ASSERT_NOT_REACHED();
-        WTFLogAlways("Hit %u recursive calls in redirect backtrace. Returning early.", maxNumberOfRecursiveCallsInRedirectTraceBack);
-        return numberOfRecursiveCalls;
-    }
-
-    numberOfRecursiveCalls++;
-    
-    for (auto& subresourceUniqueRedirectFromDomain : resourceStatistic.subresourceUniqueRedirectsFrom.values()) {
-        auto mapEntry = m_resourceStatisticsMap.find(subresourceUniqueRedirectFromDomain);
-        if (mapEntry == m_resourceStatisticsMap.end() || mapEntry->value.isPrevalentResource)
-            continue;
-        if (domainsThatHaveRedirectedTo.add(mapEntry->value.highLevelDomain).isNewEntry)
-            numberOfRecursiveCalls = recursivelyGetAllDomainsThatHaveRedirectedToThisDomain(mapEntry->value, domainsThatHaveRedirectedTo, numberOfRecursiveCalls);
-    }
-    for (auto& topFrameUniqueRedirectFromDomain : resourceStatistic.topFrameUniqueRedirectsFrom.values()) {
-        auto mapEntry = m_resourceStatisticsMap.find(topFrameUniqueRedirectFromDomain);
-        if (mapEntry == m_resourceStatisticsMap.end() || mapEntry->value.isPrevalentResource)
-            continue;
-        if (domainsThatHaveRedirectedTo.add(mapEntry->value.highLevelDomain).isNewEntry)
-            numberOfRecursiveCalls = recursivelyGetAllDomainsThatHaveRedirectedToThisDomain(mapEntry->value, domainsThatHaveRedirectedTo, numberOfRecursiveCalls);
-    }
-    
-    return numberOfRecursiveCalls;
-}
-
-void WebResourceLoadStatisticsStore::markAsPrevalentIfHasRedirectedToPrevalent(WebCore::ResourceLoadStatistics& resourceStatistic)
+// On background queue due to IPC.
+void WebResourceLoadStatisticsStore::resourceLoadStatisticsUpdated(Vector<WebCore::ResourceLoadStatistics>&& origins)
 {
     ASSERT(!RunLoop::isMain());
 
-    if (resourceStatistic.isPrevalentResource)
+    if (!m_memoryStore)
         return;
-    
-    for (auto& subresourceDomainRedirectedTo : resourceStatistic.subresourceUniqueRedirectsTo.values()) {
-        auto mapEntry = m_resourceStatisticsMap.find(subresourceDomainRedirectedTo);
-        if (mapEntry != m_resourceStatisticsMap.end() && mapEntry->value.isPrevalentResource) {
-            setPrevalentResource(resourceStatistic, ResourceLoadPrevalence::High);
-            return;
-        }
-    }
-
-    for (auto& topFrameDomainRedirectedTo : resourceStatistic.topFrameUniqueRedirectsTo.values()) {
-        auto mapEntry = m_resourceStatisticsMap.find(topFrameDomainRedirectedTo);
-        if (mapEntry != m_resourceStatisticsMap.end() && mapEntry->value.isPrevalentResource) {
-            setPrevalentResource(resourceStatistic, ResourceLoadPrevalence::High);
-            return;
-        }
-    }
-}
-
-void WebResourceLoadStatisticsStore::processStatisticsAndDataRecords()
-{
-    ASSERT(!RunLoop::isMain());
 
-    if (m_parameters.shouldClassifyResourcesBeforeDataRecordsRemoval) {
-        for (auto& resourceStatistic : m_resourceStatisticsMap.values()) {
-            if (!resourceStatistic.isVeryPrevalentResource) {
-                markAsPrevalentIfHasRedirectedToPrevalent(resourceStatistic);
-                auto currentPrevalence = resourceStatistic.isPrevalentResource ? ResourceLoadPrevalence::High : ResourceLoadPrevalence::Low;
-                auto newPrevalence = m_resourceLoadStatisticsClassifier.calculateResourcePrevalence(resourceStatistic, currentPrevalence);
-                if (newPrevalence != currentPrevalence)
-                    setPrevalentResource(resourceStatistic, newPrevalence);
-            }
-        }
-    }
-    
-    if (m_parameters.shouldNotifyPagesWhenDataRecordsWereScanned) {
-        removeDataRecords([this, protectedThis = makeRef(*this)] {
-            ASSERT(!RunLoop::isMain());
-
-            pruneStatisticsIfNeeded();
-            if (m_persistentStorage)
-                m_persistentStorage->scheduleOrWriteMemoryStore(ResourceLoadStatisticsPersistentStorage::ForceImmediateWrite::No);
-
-            RunLoop::main().dispatch([] {
-                WebProcessProxy::notifyPageStatisticsAndDataRecordsProcessed();
-            });
-        });
-    } else {
-        removeDataRecords([this, protectedThis = makeRef(*this)] {
-            ASSERT(!RunLoop::isMain());
-
-            pruneStatisticsIfNeeded();
-            if (m_persistentStorage)
-                m_persistentStorage->scheduleOrWriteMemoryStore(ResourceLoadStatisticsPersistentStorage::ForceImmediateWrite::No);
-        });
-    }
-}
-
-void WebResourceLoadStatisticsStore::resourceLoadStatisticsUpdated(Vector<WebCore::ResourceLoadStatistics>&& origins)
-{
-    ASSERT(!RunLoop::isMain());
-
-    mergeStatistics(WTFMove(origins));
+    m_memoryStore->mergeStatistics(WTFMove(origins));
 
     // We can cancel any pending request to process statistics since we're doing it synchronously below.
-    cancelPendingStatisticsProcessingRequest();
+    m_memoryStore->cancelPendingStatisticsProcessingRequest();
 
     // Fire before processing statistics to propagate user interaction as fast as possible to the network process.
-    updateCookiePartitioning([]() { });
-    processStatisticsAndDataRecords();
+    m_memoryStore->updateCookiePartitioning([]() { });
+    m_memoryStore->processStatisticsAndDataRecords();
 }
 
 void WebResourceLoadStatisticsStore::hasStorageAccess(String&& subFrameHost, String&& topFrameHost, uint64_t frameID, uint64_t pageID, CompletionHandler<void (bool)>&& completionHandler)
@@ -394,30 +203,27 @@ void WebResourceLoadStatisticsStore::hasStorageAccess(String&& subFrameHost, Str
     ASSERT(RunLoop::isMain());
 
     m_statisticsQueue->dispatch([this, protectedThis = makeRef(*this), subFramePrimaryDomain = isolatedPrimaryDomain(subFrameHost), topFramePrimaryDomain = isolatedPrimaryDomain(topFrameHost), frameID, pageID, completionHandler = WTFMove(completionHandler)] () mutable {
-        
-        auto& subFrameStatistic = ensureResourceStatisticsForPrimaryDomain(subFramePrimaryDomain);
-        if (shouldBlockCookies(subFrameStatistic)) {
+        if (!m_memoryStore) {
             RunLoop::main().dispatch([completionHandler = WTFMove(completionHandler)] {
                 completionHandler(false);
             });
             return;
         }
-
-        if (!shouldPartitionCookies(subFrameStatistic)) {
-            RunLoop::main().dispatch([completionHandler = WTFMove(completionHandler)] {
-                completionHandler(true);
-            });
-            return;
-        }
-
-        m_hasStorageAccessForFrameHandler(subFramePrimaryDomain, topFramePrimaryDomain, frameID, pageID, [completionHandler = WTFMove(completionHandler)] (bool value) mutable {
-            RunLoop::main().dispatch([completionHandler = WTFMove(completionHandler), value] () mutable {
-                completionHandler(value);
+        m_memoryStore->hasStorageAccess(subFramePrimaryDomain, topFramePrimaryDomain, frameID, pageID, [completionHandler = WTFMove(completionHandler)](bool hasStorageAccess) mutable {
+            RunLoop::main().dispatch([completionHandler = WTFMove(completionHandler), hasStorageAccess] {
+                completionHandler(hasStorageAccess);
             });
         });
     });
 }
 
+void WebResourceLoadStatisticsStore::callHasStorageAccessForFrameHandler(const String& resourceDomain, const String& firstPartyDomain, uint64_t frameID, uint64_t pageID, Function<void(bool hasAccess)>&& callback)
+{
+    ASSERT(RunLoop::isMain());
+
+    m_hasStorageAccessForFrameHandler(resourceDomain, firstPartyDomain, frameID, pageID, WTFMove(callback));
+}
+
 void WebResourceLoadStatisticsStore::requestStorageAccess(String&& subFrameHost, String&& topFrameHost, uint64_t frameID, uint64_t pageID, bool promptEnabled, CompletionHandler<void(StorageAccessStatus)>&& completionHandler)
 {
     ASSERT(subFrameHost != topFrameHost);
@@ -431,133 +237,60 @@ void WebResourceLoadStatisticsStore::requestStorageAccess(String&& subFrameHost,
     }
 
     m_statisticsQueue->dispatch([this, protectedThis = makeRef(*this), subFramePrimaryDomain = crossThreadCopy(subFramePrimaryDomain), topFramePrimaryDomain = crossThreadCopy(topFramePrimaryDomain), frameID, pageID, promptEnabled, completionHandler = WTFMove(completionHandler)] () mutable {
-
-        auto& subFrameStatistic = ensureResourceStatisticsForPrimaryDomain(subFramePrimaryDomain);
-        if (shouldBlockCookies(subFrameStatistic)) {
+        if (!m_memoryStore) {
             RunLoop::main().dispatch([completionHandler = WTFMove(completionHandler)] {
                 completionHandler(StorageAccessStatus::CannotRequestAccess);
             });
             return;
         }
-        
-        if (!shouldPartitionCookies(subFrameStatistic)) {
-            RunLoop::main().dispatch([completionHandler = WTFMove(completionHandler)] {
-                completionHandler(StorageAccessStatus::HasAccess);
-            });
-            return;
-        }
 
-        auto userWasPromptedEarlier = promptEnabled && hasUserGrantedStorageAccessThroughPrompt(subFrameStatistic, topFramePrimaryDomain);
-        if (promptEnabled && !userWasPromptedEarlier) {
-            RunLoop::main().dispatch([completionHandler = WTFMove(completionHandler)] {
-                completionHandler(StorageAccessStatus::RequiresUserPrompt);
-            });
-            return;
-        }
-
-        subFrameStatistic.timesAccessedAsFirstPartyDueToStorageAccessAPI++;
-
-        grantStorageAccessInternal(WTFMove(subFramePrimaryDomain), WTFMove(topFramePrimaryDomain), frameID, pageID, userWasPromptedEarlier, [completionHandler = WTFMove(completionHandler)] (bool wasGrantedAccess) mutable {
-            RunLoop::main().dispatch([completionHandler = WTFMove(completionHandler), wasGrantedAccess] () mutable {
-                completionHandler(wasGrantedAccess ? StorageAccessStatus::HasAccess : StorageAccessStatus::CannotRequestAccess);
+        m_memoryStore->requestStorageAccess(WTFMove(subFramePrimaryDomain), WTFMove(topFramePrimaryDomain), frameID, pageID, promptEnabled, [completionHandler = WTFMove(completionHandler)](StorageAccessStatus status) mutable {
+            RunLoop::main().dispatch([completionHandler = WTFMove(completionHandler), status] {
+                completionHandler(status);
             });
         });
     });
 }
 
+// On background queue due to IPC.
 void WebResourceLoadStatisticsStore::requestStorageAccessUnderOpener(String&& primaryDomainInNeedOfStorageAccess, uint64_t openerPageID, String&& openerPrimaryDomain, bool isTriggeredByUserGesture)
 {
-    ASSERT(primaryDomainInNeedOfStorageAccess != openerPrimaryDomain);
     ASSERT(!RunLoop::isMain());
-
-    if (primaryDomainInNeedOfStorageAccess == openerPrimaryDomain)
-        return;
-
-    auto& domainInNeedOfStorageAccessStatistic = ensureResourceStatisticsForPrimaryDomain(primaryDomainInNeedOfStorageAccess);
-    auto cookiesBlocked = shouldBlockCookies(domainInNeedOfStorageAccessStatistic);
-
-    // There are no cookies to get access to if the domain has its cookies blocked and did not get user interaction now.
-    if (cookiesBlocked && !isTriggeredByUserGesture)
-        return;
-
-    // The domain already has access if its cookies are neither blocked nor partitioned.
-    if (!cookiesBlocked && !shouldPartitionCookies(domainInNeedOfStorageAccessStatistic))
-        return;
-
-#if !RELEASE_LOG_DISABLED
-    RELEASE_LOG_INFO_IF(m_debugLoggingEnabled, ResourceLoadStatisticsDebug, "Grant storage access for %{public}s under opener %{public}s, %{public}s user interaction.", primaryDomainInNeedOfStorageAccess.utf8().data(), openerPrimaryDomain.utf8().data(), (isTriggeredByUserGesture ? "with" : "without"));
-#endif
-    grantStorageAccessInternal(WTFMove(primaryDomainInNeedOfStorageAccess), WTFMove(openerPrimaryDomain), std::nullopt, openerPageID, false, [](bool) { });
+    if (m_memoryStore)
+        m_memoryStore->requestStorageAccessUnderOpener(WTFMove(primaryDomainInNeedOfStorageAccess), openerPageID, WTFMove(openerPrimaryDomain), isTriggeredByUserGesture);
 }
 
 void WebResourceLoadStatisticsStore::grantStorageAccess(String&& subFrameHost, String&& topFrameHost, uint64_t frameID, uint64_t pageID, bool userWasPromptedNow, CompletionHandler<void(bool)>&& completionHandler)
 {
     ASSERT(RunLoop::isMain());
     m_statisticsQueue->dispatch([this, protectedThis = makeRef(*this), subFrameHost = crossThreadCopy(subFrameHost), topFrameHost = crossThreadCopy(topFrameHost), frameID, pageID, userWasPromptedNow, completionHandler = WTFMove(completionHandler)] () mutable {
-        auto subFramePrimaryDomain = isolatedPrimaryDomain(subFrameHost);
-        auto topFramePrimaryDomain = isolatedPrimaryDomain(topFrameHost);
-        if (userWasPromptedNow) {
-            auto& subFrameStatistic = ensureResourceStatisticsForPrimaryDomain(subFramePrimaryDomain);
-            ASSERT(subFrameStatistic.hadUserInteraction);
-            subFrameStatistic.storageAccessUnderTopFrameOrigins.add(topFramePrimaryDomain);
+        if (!m_memoryStore) {
+            RunLoop::main().dispatch([completionHandler = WTFMove(completionHandler)] {
+                completionHandler(false);
+            });
+            return;
         }
-        grantStorageAccessInternal(WTFMove(subFrameHost), WTFMove(topFrameHost), frameID, pageID, userWasPromptedNow, [completionHandler = WTFMove(completionHandler)] (bool wasGrantedAccess) mutable {
-            RunLoop::main().dispatch([completionHandler = WTFMove(completionHandler), wasGrantedAccess] () mutable {
+
+        m_memoryStore->grantStorageAccess(WTFMove(subFrameHost), WTFMove(topFrameHost), frameID, pageID, userWasPromptedNow, [completionHandler = WTFMove(completionHandler)](bool wasGrantedAccess) mutable {
+            RunLoop::main().dispatch([completionHandler = WTFMove(completionHandler), wasGrantedAccess] {
                 completionHandler(wasGrantedAccess);
             });
         });
     });
 }
 
-void WebResourceLoadStatisticsStore::grantStorageAccessInternal(String&& subFramePrimaryDomain, String&& topFramePrimaryDomain, std::optional<uint64_t> frameID, uint64_t pageID, bool userWasPromptedNowOrEarlier, CompletionHandler<void(bool)>&& callback)
+void WebResourceLoadStatisticsStore::callGrantStorageAccessHandler(const String& subFramePrimaryDomain, const String& topFramePrimaryDomain, std::optional<uint64_t> frameID, uint64_t pageID, CompletionHandler<void(bool)>&& callback)
 {
-    ASSERT(!RunLoop::isMain());
-
-    if (subFramePrimaryDomain == topFramePrimaryDomain) {
-        callback(true);
-        return;
-    }
-
-    // FIXME: Remove m_storageAccessPromptsEnabled check if prompting is no longer experimental.
-    if (userWasPromptedNowOrEarlier && m_storageAccessPromptsEnabled) {
-        auto& subFrameStatistic = ensureResourceStatisticsForPrimaryDomain(subFramePrimaryDomain);
-        ASSERT(subFrameStatistic.hadUserInteraction);
-        ASSERT(subFrameStatistic.storageAccessUnderTopFrameOrigins.contains(topFramePrimaryDomain));
-        subFrameStatistic.mostRecentUserInteractionTime = WallTime::now();
-    }
+    ASSERT(RunLoop::isMain());
 
     m_grantStorageAccessHandler(subFramePrimaryDomain, topFramePrimaryDomain, frameID, pageID, WTFMove(callback));
 }
 
 void WebResourceLoadStatisticsStore::removeAllStorageAccess()
 {
-    ASSERT(!RunLoop::isMain());
-    RunLoop::main().dispatch([this, protectedThis = makeRef(*this)] () {
-        m_removeAllStorageAccessHandler();
-    });
-}
-
-void WebResourceLoadStatisticsStore::grandfatherExistingWebsiteData(CompletionHandler<void()>&& callback)
-{
-    ASSERT(!RunLoop::isMain());
+    ASSERT(RunLoop::isMain());
 
-    RunLoop::main().dispatch([this, protectedThis = makeRef(*this), callback = WTFMove(callback)] () mutable {
-        // FIXME: This method being a static call on WebProcessProxy is wrong.
-        // It should be on the data store that this object belongs to.
-        WebProcessProxy::topPrivatelyControlledDomainsWithWebsiteData(WebResourceLoadStatisticsStore::monitoredDataTypes(), m_parameters.shouldNotifyPagesWhenDataRecordsWereScanned, [this, protectedThis = WTFMove(protectedThis), callback = WTFMove(callback)] (HashSet<String>&& topPrivatelyControlledDomainsWithWebsiteData) mutable {
-            m_statisticsQueue->dispatch([this, protectedThis = WTFMove(protectedThis), topDomains = crossThreadCopy(topPrivatelyControlledDomainsWithWebsiteData), callback = WTFMove(callback)] () mutable {
-                for (auto& topPrivatelyControlledDomain : topDomains) {
-                    auto& statistic = ensureResourceStatisticsForPrimaryDomain(topPrivatelyControlledDomain);
-                    statistic.grandfathered = true;
-                }
-                m_endOfGrandfatheringTimestamp = WallTime::now() + m_parameters.grandfatheringTime;
-                if (m_persistentStorage)
-                    m_persistentStorage->scheduleOrWriteMemoryStore(ResourceLoadStatisticsPersistentStorage::ForceImmediateWrite::Yes);
-                callback();
-                logTestingEvent("Grandfathered"_s);
-            });
-        });
-    });
+    m_removeAllStorageAccessHandler();
 }
     
 void WebResourceLoadStatisticsStore::processWillOpenConnection(WebProcessProxy&, IPC::Connection& connection)
@@ -580,51 +313,24 @@ void WebResourceLoadStatisticsStore::performDailyTasks()
     ASSERT(RunLoop::isMain());
 
     m_statisticsQueue->dispatch([this, protectedThis = makeRef(*this)] {
-        includeTodayAsOperatingDateIfNecessary();
+        if (!m_memoryStore)
+            return;
+
+        m_memoryStore->includeTodayAsOperatingDateIfNecessary();
+        m_memoryStore->calculateAndSubmitTelemetry();
     });
-    if (m_parameters.shouldSubmitTelemetry)
-        submitTelemetry();
 }
 
 void WebResourceLoadStatisticsStore::submitTelemetry()
 {
     ASSERT(RunLoop::isMain());
-    m_statisticsQueue->dispatch([this, protectedThis = makeRef(*this)] {
-        WebResourceLoadStatisticsTelemetry::calculateAndSubmit(*this);
-    });
-}
-
-void WebResourceLoadStatisticsStore::setResourceLoadStatisticsDebugMode(bool enable)
-{
-    m_debugModeEnabled = enable;
-#if !RELEASE_LOG_DISABLED
-    RELEASE_LOG_INFO_IF(m_debugLoggingEnabled, ResourceLoadStatisticsDebug, "ITP Debug Mode %{public}s.", (m_debugModeEnabled ? "enabled" : "disabled"));
-#endif
-}
-
-void WebResourceLoadStatisticsStore::scheduleStatisticsProcessingRequestIfNecessary()
-{
-    ASSERT(!RunLoop::isMain());
-
-    m_pendingStatisticsProcessingRequestIdentifier = ++m_lastStatisticsProcessingRequestIdentifier;
-    m_statisticsQueue->dispatchAfter(minimumStatisticsProcessingInterval, [this, protectedThis = makeRef(*this), statisticsProcessingRequestIdentifier = *m_pendingStatisticsProcessingRequestIdentifier] {
-        if (!m_pendingStatisticsProcessingRequestIdentifier || *m_pendingStatisticsProcessingRequestIdentifier != statisticsProcessingRequestIdentifier) {
-            // This request has been canceled.
-            return;
-        }
 
-        updateCookiePartitioning([]() { });
-        processStatisticsAndDataRecords();
+    m_statisticsQueue->dispatch([this, protectedThis = makeRef(*this)] {
+        if (m_memoryStore)
+            WebResourceLoadStatisticsTelemetry::calculateAndSubmit(*m_memoryStore);
     });
 }
 
-void WebResourceLoadStatisticsStore::cancelPendingStatisticsProcessingRequest()
-{
-    ASSERT(!RunLoop::isMain());
-
-    m_pendingStatisticsProcessingRequestIdentifier = std::nullopt;
-}
-
 void WebResourceLoadStatisticsStore::logFrameNavigation(const WebFrameProxy& frame, const URL& pageURL, const WebCore::ResourceRequest& request, const WebCore::URL& redirectURL)
 {
     ASSERT(RunLoop::isMain());
@@ -657,34 +363,8 @@ void WebResourceLoadStatisticsStore::logFrameNavigation(const WebFrameProxy& fra
     bool areTargetAndSourceDomainsAssociated = areDomainsAssociated(page, targetPrimaryDomain, sourcePrimaryDomain);
 
     m_statisticsQueue->dispatch([this, protectedThis = makeRef(*this), targetPrimaryDomain = targetPrimaryDomain.isolatedCopy(), mainFramePrimaryDomain = mainFramePrimaryDomain.isolatedCopy(), sourcePrimaryDomain = sourcePrimaryDomain.isolatedCopy(), targetHost = targetHost.toString().isolatedCopy(), mainFrameHost = mainFrameHost.toString().isolatedCopy(), areTargetAndMainFrameDomainsAssociated, areTargetAndSourceDomainsAssociated, isRedirect, isMainFrame = frame.isMainFrame()] {
-        bool statisticsWereUpdated = false;
-        if (targetHost != mainFrameHost && !(areTargetAndMainFrameDomainsAssociated || areTargetAndSourceDomainsAssociated)) {
-            auto& targetStatistics = ensureResourceStatisticsForPrimaryDomain(targetPrimaryDomain);
-            targetStatistics.lastSeen = ResourceLoadStatistics::reduceTimeResolution(WallTime::now());
-            if (targetStatistics.subframeUnderTopFrameOrigins.add(mainFramePrimaryDomain).isNewEntry)
-                statisticsWereUpdated = true;
-        }
-
-        if (isRedirect && !areTargetAndSourceDomainsAssociated) {
-            if (isMainFrame) {
-                auto& redirectingOriginStatistics = ensureResourceStatisticsForPrimaryDomain(sourcePrimaryDomain);
-                if (redirectingOriginStatistics.topFrameUniqueRedirectsTo.add(targetPrimaryDomain).isNewEntry)
-                    statisticsWereUpdated = true;
-                auto& targetStatistics = ensureResourceStatisticsForPrimaryDomain(targetPrimaryDomain);
-                if (targetStatistics.topFrameUniqueRedirectsFrom.add(sourcePrimaryDomain).isNewEntry)
-                    statisticsWereUpdated = true;
-            } else {
-                auto& redirectingOriginStatistics = ensureResourceStatisticsForPrimaryDomain(sourcePrimaryDomain);
-                if (redirectingOriginStatistics.subresourceUniqueRedirectsTo.add(targetPrimaryDomain).isNewEntry)
-                    statisticsWereUpdated = true;
-                auto& targetStatistics = ensureResourceStatisticsForPrimaryDomain(targetPrimaryDomain);
-                if (targetStatistics.subresourceUniqueRedirectsFrom.add(sourcePrimaryDomain).isNewEntry)
-                    statisticsWereUpdated = true;
-            }
-        }
-
-        if (statisticsWereUpdated)
-            scheduleStatisticsProcessingRequestIfNecessary();
+        if (m_memoryStore)
+            m_memoryStore->logFrameNavigation(targetPrimaryDomain, mainFramePrimaryDomain, sourcePrimaryDomain, targetHost, mainFrameHost, areTargetAndMainFrameDomainsAssociated, areTargetAndSourceDomainsAssociated, isRedirect, isMainFrame);
     });
 }
 
@@ -696,12 +376,8 @@ void WebResourceLoadStatisticsStore::logUserInteraction(const URL& url)
         return;
 
     m_statisticsQueue->dispatch([this, protectedThis = makeRef(*this), primaryDomain = isolatedPrimaryDomain(url)] {
-        auto& statistics = ensureResourceStatisticsForPrimaryDomain(primaryDomain);
-        statistics.hadUserInteraction = true;
-        statistics.mostRecentUserInteractionTime = WallTime::now();
-
-        if (statistics.isMarkedForCookieBlocking)
-            updateCookiePartitioningForDomains({ primaryDomain }, { }, { }, ShouldClearFirst::No, []() { });
+        if (m_memoryStore)
+            m_memoryStore->logUserInteraction(primaryDomain);
     });
 }
 
@@ -713,11 +389,8 @@ void WebResourceLoadStatisticsStore::logNonRecentUserInteraction(const URL& url)
         return;
     
     m_statisticsQueue->dispatch([this, protectedThis = makeRef(*this), primaryDomain = isolatedPrimaryDomain(url)] {
-        auto& statistics = ensureResourceStatisticsForPrimaryDomain(primaryDomain);
-        statistics.hadUserInteraction = true;
-        statistics.mostRecentUserInteractionTime = WallTime::now() - (m_parameters.timeToLiveCookiePartitionFree + Seconds::fromHours(1));
-
-        updateCookiePartitioningForDomains({ primaryDomain }, { }, { }, ShouldClearFirst::No, []() { });
+        if (m_memoryStore)
+            m_memoryStore->logNonRecentUserInteraction(primaryDomain);
     });
 }
 
@@ -729,9 +402,8 @@ void WebResourceLoadStatisticsStore::clearUserInteraction(const URL& url)
         return;
 
     m_statisticsQueue->dispatch([this, protectedThis = makeRef(*this), primaryDomain = isolatedPrimaryDomain(url)] {
-        auto& statistics = ensureResourceStatisticsForPrimaryDomain(primaryDomain);
-        statistics.hadUserInteraction = false;
-        statistics.mostRecentUserInteractionTime = { };
+        if (m_memoryStore)
+            m_memoryStore->clearUserInteraction(primaryDomain);
     });
 }
 
@@ -745,8 +417,7 @@ void WebResourceLoadStatisticsStore::hasHadUserInteraction(const URL& url, Compl
     }
 
     m_statisticsQueue->dispatch([this, protectedThis = makeRef(*this), primaryDomain = isolatedPrimaryDomain(url), completionHandler = WTFMove(completionHandler)] () mutable {
-        auto mapEntry = m_resourceStatisticsMap.find(primaryDomain);
-        bool hadUserInteraction = mapEntry == m_resourceStatisticsMap.end() ? false: hasHadUnexpiredRecentUserInteraction(mapEntry->value);
+        bool hadUserInteraction = m_memoryStore ? m_memoryStore->hasHadUserInteraction(primaryDomain) : false;
         RunLoop::main().dispatch([hadUserInteraction, completionHandler = WTFMove(completionHandler)] {
             completionHandler(hadUserInteraction);
         });
@@ -761,8 +432,8 @@ void WebResourceLoadStatisticsStore::setLastSeen(const URL& url, Seconds seconds
         return;
     
     m_statisticsQueue->dispatch([this, protectedThis = makeRef(*this), primaryDomain = isolatedPrimaryDomain(url), seconds] {
-        auto& statistics = ensureResourceStatisticsForPrimaryDomain(primaryDomain);
-        statistics.lastSeen = WallTime::fromRawSeconds(seconds.seconds());
+        if (m_memoryStore)
+            m_memoryStore->setLastSeen(primaryDomain, seconds);
     });
 }
     
@@ -774,40 +445,24 @@ void WebResourceLoadStatisticsStore::setPrevalentResource(const URL& url)
         return;
 
     m_statisticsQueue->dispatch([this, protectedThis = makeRef(*this), primaryDomain = isolatedPrimaryDomain(url)] {
-        auto& resourceStatistic = ensureResourceStatisticsForPrimaryDomain(primaryDomain);
-        setPrevalentResource(resourceStatistic, ResourceLoadPrevalence::High);
+        if (m_memoryStore)
+            m_memoryStore->setPrevalentResource(primaryDomain);
     });
 }
 
 void WebResourceLoadStatisticsStore::setVeryPrevalentResource(const URL& url)
 {
-    ASSERT(isMainThread());
+    ASSERT(RunLoop::isMain());
 
     if (url.isBlankURL() || url.isEmpty())
         return;
     
     m_statisticsQueue->dispatch([this, protectedThis = makeRef(*this), primaryDomain = isolatedPrimaryDomain(url)] {
-        auto& resourceStatistic = ensureResourceStatisticsForPrimaryDomain(primaryDomain);
-        setPrevalentResource(resourceStatistic, ResourceLoadPrevalence::VeryHigh);
+        if (m_memoryStore)
+            m_memoryStore->setVeryPrevalentResource(primaryDomain);
     });
 }
 
-void WebResourceLoadStatisticsStore::setPrevalentResource(WebCore::ResourceLoadStatistics& resourceStatistic, ResourceLoadPrevalence newPrevalence)
-{
-    ASSERT(!RunLoop::isMain());
-    resourceStatistic.isPrevalentResource = true;
-    resourceStatistic.isVeryPrevalentResource = newPrevalence == ResourceLoadPrevalence::VeryHigh;
-    HashSet<String> domainsThatHaveRedirectedTo;
-    recursivelyGetAllDomainsThatHaveRedirectedToThisDomain(resourceStatistic, domainsThatHaveRedirectedTo, 0);
-    for (auto& domain : domainsThatHaveRedirectedTo) {
-        auto mapEntry = m_resourceStatisticsMap.find(domain);
-        if (mapEntry == m_resourceStatisticsMap.end())
-            continue;
-        ASSERT(!mapEntry->value.isPrevalentResource);
-        mapEntry->value.isPrevalentResource = true;
-    }
-}
-
 void WebResourceLoadStatisticsStore::isPrevalentResource(const URL& url, CompletionHandler<void (bool)>&& completionHandler)
 {
     ASSERT(RunLoop::isMain());
@@ -818,8 +473,7 @@ void WebResourceLoadStatisticsStore::isPrevalentResource(const URL& url, Complet
     }
 
     m_statisticsQueue->dispatch([this, protectedThis = makeRef(*this), primaryDomain = isolatedPrimaryDomain(url), completionHandler = WTFMove(completionHandler)] () mutable {
-        auto mapEntry = m_resourceStatisticsMap.find(primaryDomain);
-        bool isPrevalentResource = mapEntry == m_resourceStatisticsMap.end() ? false : mapEntry->value.isPrevalentResource;
+        bool isPrevalentResource = m_memoryStore ? m_memoryStore->isPrevalentResource(primaryDomain) : false;
         RunLoop::main().dispatch([isPrevalentResource, completionHandler = WTFMove(completionHandler)] {
             completionHandler(isPrevalentResource);
         });
@@ -828,7 +482,7 @@ void WebResourceLoadStatisticsStore::isPrevalentResource(const URL& url, Complet
 
 void WebResourceLoadStatisticsStore::isVeryPrevalentResource(const URL& url, CompletionHandler<void(bool)>&& completionHandler)
 {
-    ASSERT(isMainThread());
+    ASSERT(RunLoop::isMain());
 
     if (url.isBlankURL() || url.isEmpty()) {
         completionHandler(false);
@@ -836,8 +490,7 @@ void WebResourceLoadStatisticsStore::isVeryPrevalentResource(const URL& url, Com
     }
     
     m_statisticsQueue->dispatch([this, protectedThis = makeRef(*this), primaryDomain = isolatedPrimaryDomain(url), completionHandler = WTFMove(completionHandler)] () mutable {
-        auto mapEntry = m_resourceStatisticsMap.find(primaryDomain);
-        bool isVeryPrevalentResource = mapEntry == m_resourceStatisticsMap.end() ? false : mapEntry->value.isPrevalentResource && mapEntry->value.isVeryPrevalentResource;
+        bool isVeryPrevalentResource = m_memoryStore ? m_memoryStore->isVeryPrevalentResource(primaryDomain) : false;
         RunLoop::main().dispatch([isVeryPrevalentResource, completionHandler = WTFMove(completionHandler)] {
             completionHandler(isVeryPrevalentResource);
         });
@@ -849,8 +502,7 @@ void WebResourceLoadStatisticsStore::isRegisteredAsSubFrameUnder(const URL& subF
     ASSERT(RunLoop::isMain());
 
     m_statisticsQueue->dispatch([this, protectedThis = makeRef(*this), subFramePrimaryDomain = isolatedPrimaryDomain(subFrame), topFramePrimaryDomain = isolatedPrimaryDomain(topFrame), completionHandler = WTFMove(completionHandler)] () mutable {
-        auto mapEntry = m_resourceStatisticsMap.find(subFramePrimaryDomain);
-        bool isRegisteredAsSubFrameUnder = mapEntry == m_resourceStatisticsMap.end() ? false : mapEntry->value.subframeUnderTopFrameOrigins.contains(topFramePrimaryDomain);
+        bool isRegisteredAsSubFrameUnder = m_memoryStore ? m_memoryStore->isRegisteredAsSubFrameUnder(subFramePrimaryDomain, topFramePrimaryDomain) : false;
         RunLoop::main().dispatch([isRegisteredAsSubFrameUnder, completionHandler = WTFMove(completionHandler)] {
             completionHandler(isRegisteredAsSubFrameUnder);
         });
@@ -862,8 +514,7 @@ void WebResourceLoadStatisticsStore::isRegisteredAsRedirectingTo(const URL& host
     ASSERT(RunLoop::isMain());
 
     m_statisticsQueue->dispatch([this, protectedThis = makeRef(*this), hostRedirectedFromPrimaryDomain = isolatedPrimaryDomain(hostRedirectedFrom), hostRedirectedToPrimaryDomain = isolatedPrimaryDomain(hostRedirectedTo), completionHandler = WTFMove(completionHandler)] () mutable {
-        auto mapEntry = m_resourceStatisticsMap.find(hostRedirectedFromPrimaryDomain);
-        bool isRegisteredAsRedirectingTo = mapEntry == m_resourceStatisticsMap.end() ? false : mapEntry->value.subresourceUniqueRedirectsTo.contains(hostRedirectedToPrimaryDomain);
+        bool isRegisteredAsRedirectingTo = m_memoryStore ? m_memoryStore->isRegisteredAsRedirectingTo(hostRedirectedFromPrimaryDomain, hostRedirectedToPrimaryDomain) : false;
         RunLoop::main().dispatch([isRegisteredAsRedirectingTo, completionHandler = WTFMove(completionHandler)] {
             completionHandler(isRegisteredAsRedirectingTo);
         });
@@ -878,9 +529,8 @@ void WebResourceLoadStatisticsStore::clearPrevalentResource(const URL& url)
         return;
 
     m_statisticsQueue->dispatch([this, protectedThis = makeRef(*this), primaryDomain = isolatedPrimaryDomain(url)] {
-        auto& statistics = ensureResourceStatisticsForPrimaryDomain(primaryDomain);
-        statistics.isPrevalentResource = false;
-        statistics.isVeryPrevalentResource = false;
+        if (m_memoryStore)
+            m_memoryStore->clearPrevalentResource(primaryDomain);
     });
 }
 
@@ -892,8 +542,8 @@ void WebResourceLoadStatisticsStore::setGrandfathered(const URL& url, bool value
         return;
 
     m_statisticsQueue->dispatch([this, protectedThis = makeRef(*this), primaryDomain = isolatedPrimaryDomain(url), value] {
-        auto& statistics = ensureResourceStatisticsForPrimaryDomain(primaryDomain);
-        statistics.grandfathered = value;
+        if (m_memoryStore)
+            m_memoryStore->setGrandfathered(primaryDomain, value);
     });
 }
 
@@ -907,8 +557,7 @@ void WebResourceLoadStatisticsStore::isGrandfathered(const URL& url, CompletionH
     }
 
     m_statisticsQueue->dispatch([this, protectedThis = makeRef(*this), completionHandler = WTFMove(completionHandler), primaryDomain = isolatedPrimaryDomain(url)] () mutable {
-        auto mapEntry = m_resourceStatisticsMap.find(primaryDomain);
-        bool isGrandFathered = mapEntry == m_resourceStatisticsMap.end() ? false : mapEntry->value.grandfathered;
+        bool isGrandFathered = m_memoryStore ? m_memoryStore->isGrandfathered(primaryDomain) : false;
         RunLoop::main().dispatch([isGrandFathered, completionHandler = WTFMove(completionHandler)] {
             completionHandler(isGrandFathered);
         });
@@ -923,10 +572,8 @@ void WebResourceLoadStatisticsStore::setSubframeUnderTopFrameOrigin(const URL& s
         return;
 
     m_statisticsQueue->dispatch([this, protectedThis = makeRef(*this), primaryTopFrameDomain = isolatedPrimaryDomain(topFrame), primarySubFrameDomain = isolatedPrimaryDomain(subframe)] {
-        auto& statistics = ensureResourceStatisticsForPrimaryDomain(primarySubFrameDomain);
-        statistics.subframeUnderTopFrameOrigins.add(primaryTopFrameDomain);
-        // For consistency, make sure we also have a statistics entry for the top frame domain.
-        ensureResourceStatisticsForPrimaryDomain(primaryTopFrameDomain);
+        if (m_memoryStore)
+            m_memoryStore->setSubframeUnderTopFrameOrigin(primarySubFrameDomain, primaryTopFrameDomain);
     });
 }
 
@@ -938,10 +585,8 @@ void WebResourceLoadStatisticsStore::setSubresourceUnderTopFrameOrigin(const URL
         return;
 
     m_statisticsQueue->dispatch([this, protectedThis = makeRef(*this), primaryTopFrameDomain = isolatedPrimaryDomain(topFrame), primarySubresourceDomain = isolatedPrimaryDomain(subresource)] {
-        auto& statistics = ensureResourceStatisticsForPrimaryDomain(primarySubresourceDomain);
-        statistics.subresourceUnderTopFrameOrigins.add(primaryTopFrameDomain);
-        // For consistency, make sure we also have a statistics entry for the top frame domain.
-        ensureResourceStatisticsForPrimaryDomain(primaryTopFrameDomain);
+        if (m_memoryStore)
+            m_memoryStore->setSubresourceUnderTopFrameOrigin(primarySubresourceDomain, primaryTopFrameDomain);
     });
 }
 
@@ -953,10 +598,8 @@ void WebResourceLoadStatisticsStore::setSubresourceUniqueRedirectTo(const URL& s
         return;
 
     m_statisticsQueue->dispatch([this, protectedThis = makeRef(*this), primaryRedirectDomain = isolatedPrimaryDomain(hostNameRedirectedTo), primarySubresourceDomain = isolatedPrimaryDomain(subresource)] {
-        auto& statistics = ensureResourceStatisticsForPrimaryDomain(primarySubresourceDomain);
-        statistics.subresourceUniqueRedirectsTo.add(primaryRedirectDomain);
-        // For consistency, make sure we also have a statistics entry for the redirect domain.
-        ensureResourceStatisticsForPrimaryDomain(primaryRedirectDomain);
+        if (m_memoryStore)
+            m_memoryStore->setSubresourceUniqueRedirectTo(primarySubresourceDomain, primaryRedirectDomain);
     });
 }
 
@@ -968,10 +611,8 @@ void WebResourceLoadStatisticsStore::setSubresourceUniqueRedirectFrom(const URL&
         return;
     
     m_statisticsQueue->dispatch([this, protectedThis = makeRef(*this), primaryRedirectDomain = isolatedPrimaryDomain(hostNameRedirectedFrom), primarySubresourceDomain = isolatedPrimaryDomain(subresource)] {
-        auto& statistics = ensureResourceStatisticsForPrimaryDomain(primarySubresourceDomain);
-        statistics.subresourceUniqueRedirectsFrom.add(primaryRedirectDomain);
-        // For consistency, make sure we also have a statistics entry for the redirect domain.
-        ensureResourceStatisticsForPrimaryDomain(primaryRedirectDomain);
+        if (m_memoryStore)
+            m_memoryStore->setSubresourceUniqueRedirectFrom(primarySubresourceDomain, primaryRedirectDomain);
     });
 }
 
@@ -983,10 +624,8 @@ void WebResourceLoadStatisticsStore::setTopFrameUniqueRedirectTo(const URL& topF
         return;
     
     m_statisticsQueue->dispatch([this, protectedThis = makeRef(*this), primaryRedirectDomain = isolatedPrimaryDomain(hostNameRedirectedTo), topFramePrimaryDomain = isolatedPrimaryDomain(topFrameHostName)] {
-        auto& statistics = ensureResourceStatisticsForPrimaryDomain(topFramePrimaryDomain);
-        statistics.topFrameUniqueRedirectsTo.add(primaryRedirectDomain);
-        // For consistency, make sure we also have a statistics entry for the redirect domain.
-        ensureResourceStatisticsForPrimaryDomain(primaryRedirectDomain);
+        if (m_memoryStore)
+            m_memoryStore->setTopFrameUniqueRedirectTo(topFramePrimaryDomain, primaryRedirectDomain);
     });
 }
 
@@ -998,10 +637,8 @@ void WebResourceLoadStatisticsStore::setTopFrameUniqueRedirectFrom(const URL& to
         return;
     
     m_statisticsQueue->dispatch([this, protectedThis = makeRef(*this), primaryRedirectDomain = isolatedPrimaryDomain(hostNameRedirectedFrom), topFramePrimaryDomain = isolatedPrimaryDomain(topFrameHostName)] {
-        auto& statistics = ensureResourceStatisticsForPrimaryDomain(topFramePrimaryDomain);
-        statistics.topFrameUniqueRedirectsFrom.add(primaryRedirectDomain);
-        // For consistency, make sure we also have a statistics entry for the redirect domain.
-        ensureResourceStatisticsForPrimaryDomain(primaryRedirectDomain);
+        if (m_memoryStore)
+            m_memoryStore->setTopFrameUniqueRedirectFrom(topFramePrimaryDomain, primaryRedirectDomain);
     });
 }
 
@@ -1011,7 +648,13 @@ void WebResourceLoadStatisticsStore::scheduleCookiePartitioningUpdate(Completion
     ASSERT(RunLoop::isMain());
 
     m_statisticsQueue->dispatch([this, protectedThis = makeRef(*this), completionHandler = WTFMove(completionHandler)] () mutable {
-        updateCookiePartitioning([completionHandler = WTFMove(completionHandler)]() mutable {
+        if (!m_memoryStore) {
+            RunLoop::main().dispatch([completionHandler = WTFMove(completionHandler)]() {
+                completionHandler();
+            });
+            return;
+        }
+        m_memoryStore->updateCookiePartitioning([completionHandler = WTFMove(completionHandler)]() mutable {
             RunLoop::main().dispatch([completionHandler = WTFMove(completionHandler)]() {
                 completionHandler();
             });
@@ -1024,7 +667,14 @@ void WebResourceLoadStatisticsStore::scheduleCookiePartitioningUpdateForDomains(
     // Helper function used by testing system. Should only be called from the main thread.
     ASSERT(RunLoop::isMain());
     m_statisticsQueue->dispatch([this, protectedThis = makeRef(*this), domainsToPartition = crossThreadCopy(domainsToPartition), domainsToBlock = crossThreadCopy(domainsToBlock), domainsToNeitherPartitionNorBlock = crossThreadCopy(domainsToNeitherPartitionNorBlock), shouldClearFirst, completionHandler = WTFMove(completionHandler)] () mutable {
-        updateCookiePartitioningForDomains(domainsToPartition, domainsToBlock, domainsToNeitherPartitionNorBlock, shouldClearFirst, [completionHandler = WTFMove(completionHandler)]() mutable {
+        if (!m_memoryStore) {
+            RunLoop::main().dispatch([completionHandler = WTFMove(completionHandler)]() {
+                completionHandler();
+            });
+            return;
+        }
+
+        m_memoryStore->updateCookiePartitioningForDomains(domainsToPartition, domainsToBlock, domainsToNeitherPartitionNorBlock, shouldClearFirst, [completionHandler = WTFMove(completionHandler)]() mutable {
             RunLoop::main().dispatch([completionHandler = WTFMove(completionHandler)]() {
                 completionHandler();
             });
@@ -1037,7 +687,14 @@ void WebResourceLoadStatisticsStore::scheduleClearPartitioningStateForDomains(co
     // Helper function used by testing system. Should only be called from the main thread.
     ASSERT(RunLoop::isMain());
     m_statisticsQueue->dispatch([this, protectedThis = makeRef(*this), domains = crossThreadCopy(domains), completionHandler = WTFMove(completionHandler)] () mutable {
-        clearPartitioningStateForDomains(domains, [completionHandler = WTFMove(completionHandler)]() mutable {
+        if (!m_memoryStore) {
+            RunLoop::main().dispatch([completionHandler = WTFMove(completionHandler)]() {
+                completionHandler();
+            });
+            return;
+        }
+
+        m_memoryStore->clearPartitioningStateForDomains(domains, [completionHandler = WTFMove(completionHandler)]() mutable {
             RunLoop::main().dispatch([completionHandler = WTFMove(completionHandler)]() {
                 completionHandler();
             });
@@ -1050,7 +707,8 @@ void WebResourceLoadStatisticsStore::scheduleCookiePartitioningStateReset()
 {
     ASSERT(RunLoop::isMain());
     m_statisticsQueue->dispatch([this, protectedThis = makeRef(*this)] {
-        resetCookiePartitioningState();
+        if (m_memoryStore)
+            m_memoryStore->resetCookiePartitioningState();
     });
 }
 #endif
@@ -1059,7 +717,8 @@ void WebResourceLoadStatisticsStore::scheduleClearInMemory()
 {
     ASSERT(RunLoop::isMain());
     m_statisticsQueue->dispatch([this, protectedThis = makeRef(*this)] {
-        clearInMemory();
+        if (m_memoryStore)
+            m_memoryStore->clear();
     });
 }
 
@@ -1067,20 +726,28 @@ void WebResourceLoadStatisticsStore::scheduleClearInMemoryAndPersistent(ShouldGr
 {
     ASSERT(RunLoop::isMain());
     m_statisticsQueue->dispatch([this, protectedThis = makeRef(*this), shouldGrandfather, completionHandler = WTFMove(completionHandler)] () mutable {
-        clearInMemory();
+        if (m_memoryStore)
+            m_memoryStore->clear();
         if (m_persistentStorage)
             m_persistentStorage->clear();
         
-        if (shouldGrandfather == ShouldGrandfather::Yes)
-            grandfatherExistingWebsiteData(WTFMove(completionHandler));
-        else {
-            completionHandler();
-        }
+        CompletionHandler<void()> callCompletionHandlerOnMainThread = [completionHandler = WTFMove(completionHandler)]() mutable {
+            RunLoop::main().dispatch([completionHandler = WTFMove(completionHandler)] {
+                completionHandler();
+            });
+        };
+
+        if (shouldGrandfather == ShouldGrandfather::Yes && m_memoryStore)
+            m_memoryStore->grandfatherExistingWebsiteData(WTFMove(callCompletionHandlerOnMainThread));
+        else
+            callCompletionHandlerOnMainThread();
     });
 }
 
 void WebResourceLoadStatisticsStore::scheduleClearInMemoryAndPersistent(WallTime modifiedSince, ShouldGrandfather shouldGrandfather, CompletionHandler<void()>&& callback)
 {
+    ASSERT(RunLoop::isMain());
+
     // For now, be conservative and clear everything regardless of modifiedSince.
     UNUSED_PARAM(modifiedSince);
     scheduleClearInMemoryAndPersistent(shouldGrandfather, WTFMove(callback));
@@ -1088,462 +755,88 @@ void WebResourceLoadStatisticsStore::scheduleClearInMemoryAndPersistent(WallTime
 
 void WebResourceLoadStatisticsStore::setTimeToLiveUserInteraction(Seconds seconds)
 {
-    ASSERT(seconds >= 0_s);
-    m_parameters.timeToLiveUserInteraction = seconds;
-}
-
-void WebResourceLoadStatisticsStore::setTimeToLiveCookiePartitionFree(Seconds seconds)
-{
-    ASSERT(seconds >= 0_s);
-    m_parameters.timeToLiveCookiePartitionFree = seconds;
-}
-
-void WebResourceLoadStatisticsStore::setMinimumTimeBetweenDataRecordsRemoval(Seconds seconds)
-{
-    ASSERT(seconds >= 0_s);
-    m_parameters.minimumTimeBetweenDataRecordsRemoval = seconds;
-}
-
-void WebResourceLoadStatisticsStore::setGrandfatheringTime(Seconds seconds)
-{
-    ASSERT(seconds >= 0_s);
-    m_parameters.grandfatheringTime = seconds;
-}
-
-bool WebResourceLoadStatisticsStore::shouldRemoveDataRecords() const
-{
-    ASSERT(!RunLoop::isMain());
-    if (m_dataRecordsBeingRemoved)
-        return false;
-
-#if ENABLE(NETSCAPE_PLUGIN_API)
-    for (auto plugin : PluginProcessManager::singleton().pluginProcesses()) {
-        if (!m_activePluginTokens.contains(plugin->pluginProcessToken()))
-            return true;
-    }
-#endif
-
-    return !m_lastTimeDataRecordsWereRemoved || MonotonicTime::now() >= (m_lastTimeDataRecordsWereRemoved + m_parameters.minimumTimeBetweenDataRecordsRemoval);
-}
-
-void WebResourceLoadStatisticsStore::setDataRecordsBeingRemoved(bool value)
-{
-    ASSERT(!RunLoop::isMain());
-    m_dataRecordsBeingRemoved = value;
-    if (m_dataRecordsBeingRemoved)
-        m_lastTimeDataRecordsWereRemoved = MonotonicTime::now();
-}
-
-ResourceLoadStatistics& WebResourceLoadStatisticsStore::ensureResourceStatisticsForPrimaryDomain(const String& primaryDomain)
-{
-    ASSERT(!RunLoop::isMain());
-    return m_resourceStatisticsMap.ensure(primaryDomain, [&primaryDomain] {
-        return ResourceLoadStatistics(primaryDomain);
-    }).iterator->value;
-}
-
-std::unique_ptr<KeyedEncoder> WebResourceLoadStatisticsStore::createEncoderFromData() const
-{
-    ASSERT(!RunLoop::isMain());
-    auto encoder = KeyedEncoder::encoder();
-    encoder->encodeUInt32("version", statisticsModelVersion);
-    encoder->encodeDouble("endOfGrandfatheringTimestamp", m_endOfGrandfatheringTimestamp.secondsSinceEpoch().value());
-
-    encoder->encodeObjects("browsingStatistics", m_resourceStatisticsMap.begin(), m_resourceStatisticsMap.end(), [](KeyedEncoder& encoderInner, const auto& origin) {
-        origin.value.encode(encoderInner);
-    });
-
-    encoder->encodeObjects("operatingDates", m_operatingDates.begin(), m_operatingDates.end(), [](KeyedEncoder& encoderInner, OperatingDate date) {
-        encoderInner.encodeDouble("date", date.secondsSinceEpoch().value());
-    });
-
-    return encoder;
-}
-
-void WebResourceLoadStatisticsStore::mergeWithDataFromDecoder(KeyedDecoder& decoder)
-{
-    ASSERT(!RunLoop::isMain());
-
-    unsigned versionOnDisk;
-    if (!decoder.decodeUInt32("version", versionOnDisk))
-        return;
-
-    if (versionOnDisk > statisticsModelVersion) {
-        WTFLogAlways("Found resource load statistics on disk with model version %u whereas the highest supported version is %u. Resetting.", versionOnDisk, statisticsModelVersion);
-        return;
-    }
-
-    double endOfGrandfatheringTimestamp;
-    if (decoder.decodeDouble("endOfGrandfatheringTimestamp", endOfGrandfatheringTimestamp))
-        m_endOfGrandfatheringTimestamp = WallTime::fromRawSeconds(endOfGrandfatheringTimestamp);
-    else
-        m_endOfGrandfatheringTimestamp = { };
-
-    Vector<ResourceLoadStatistics> loadedStatistics;
-    bool succeeded = decoder.decodeObjects("browsingStatistics", loadedStatistics, [versionOnDisk](KeyedDecoder& decoderInner, ResourceLoadStatistics& statistics) {
-        return statistics.decode(decoderInner, versionOnDisk);
-    });
-
-    if (!succeeded)
-        return;
-
-    mergeStatistics(WTFMove(loadedStatistics));
-    updateCookiePartitioning([]() { });
-
-    Vector<OperatingDate> operatingDates;
-    succeeded = decoder.decodeObjects("operatingDates", operatingDates, [](KeyedDecoder& decoder, OperatingDate& date) {
-        double value;
-        if (!decoder.decodeDouble("date", value))
-            return false;
-
-        date = OperatingDate::fromWallTime(WallTime::fromRawSeconds(value));
-        return true;
+    ASSERT(RunLoop::isMain());
+    m_statisticsQueue->dispatch([this, protectedThis = makeRef(*this), seconds] {
+        if (m_memoryStore)
+            m_memoryStore->setTimeToLiveUserInteraction(seconds);
     });
-
-    if (!succeeded)
-        return;
-
-    m_operatingDates = mergeOperatingDates(m_operatingDates, WTFMove(operatingDates));
-}
-
-void WebResourceLoadStatisticsStore::clearInMemory()
-{
-    ASSERT(!RunLoop::isMain());
-    m_resourceStatisticsMap.clear();
-    m_operatingDates.clear();
-
-    removeAllStorageAccess();
-    updateCookiePartitioningForDomains({ }, { }, { }, ShouldClearFirst::Yes, []() { });
-}
-
-bool WebResourceLoadStatisticsStore::wasAccessedAsFirstPartyDueToUserInteraction(const ResourceLoadStatistics& current, const ResourceLoadStatistics& updated)
-{
-    if (!current.hadUserInteraction && !updated.hadUserInteraction)
-        return false;
-
-    auto mostRecentUserInteractionTime = std::max(current.mostRecentUserInteractionTime, updated.mostRecentUserInteractionTime);
-
-    return updated.lastSeen <= mostRecentUserInteractionTime + m_parameters.timeToLiveCookiePartitionFree;
 }
 
-void WebResourceLoadStatisticsStore::mergeStatistics(Vector<ResourceLoadStatistics>&& statistics)
-{
-    ASSERT(!RunLoop::isMain());
-    for (auto& statistic : statistics) {
-        auto result = m_resourceStatisticsMap.ensure(statistic.highLevelDomain, [&statistic] {
-            return WTFMove(statistic);
-        });
-        if (!result.isNewEntry) {
-            if (wasAccessedAsFirstPartyDueToUserInteraction(result.iterator->value, statistic))
-                result.iterator->value.timesAccessedAsFirstPartyDueToUserInteraction++;
-            result.iterator->value.merge(statistic);
-        }
-    }
-}
-
-bool WebResourceLoadStatisticsStore::shouldPartitionCookies(const ResourceLoadStatistics& statistic) const
-{
-    return statistic.isPrevalentResource && statistic.hadUserInteraction;
-}
-
-bool WebResourceLoadStatisticsStore::shouldBlockCookies(const ResourceLoadStatistics& statistic) const
-{
-    return statistic.isPrevalentResource && !statistic.hadUserInteraction;
-}
-
-bool WebResourceLoadStatisticsStore::hasUserGrantedStorageAccessThroughPrompt(const ResourceLoadStatistics& statistic, const String& firstPartyPrimaryDomain) const
-{
-    return statistic.storageAccessUnderTopFrameOrigins.contains(firstPartyPrimaryDomain);
-}
-
-void WebResourceLoadStatisticsStore::updateCookiePartitioning(CompletionHandler<void()>&& completionHandler)
+void WebResourceLoadStatisticsStore::setTimeToLiveCookiePartitionFree(Seconds seconds)
 {
-    ASSERT(!RunLoop::isMain());
-
-    Vector<String> domainsToPartition;
-    Vector<String> domainsToBlock;
-    Vector<String> domainsToNeitherPartitionNorBlock;
-    for (auto& resourceStatistic : m_resourceStatisticsMap.values()) {
-
-        bool shouldPartition = shouldPartitionCookies(resourceStatistic);
-        bool shouldBlock = shouldBlockCookies(resourceStatistic);
-
-        if (shouldPartition && !resourceStatistic.isMarkedForCookiePartitioning) {
-            domainsToPartition.append(resourceStatistic.highLevelDomain);
-            resourceStatistic.isMarkedForCookiePartitioning = true;
-        } else if (shouldBlock && !resourceStatistic.isMarkedForCookieBlocking) {
-            domainsToBlock.append(resourceStatistic.highLevelDomain);
-            resourceStatistic.isMarkedForCookieBlocking = true;
-        } else if (!shouldPartition && !shouldBlock && resourceStatistic.isPrevalentResource) {
-            domainsToNeitherPartitionNorBlock.append(resourceStatistic.highLevelDomain);
-            resourceStatistic.isMarkedForCookiePartitioning = false;
-            resourceStatistic.isMarkedForCookieBlocking = false;
-        }
-    }
-
-    if (domainsToPartition.isEmpty() && domainsToBlock.isEmpty() && domainsToNeitherPartitionNorBlock.isEmpty()) {
-        completionHandler();
-        return;
-    }
-
-#if !RELEASE_LOG_DISABLED
-    if (m_debugLoggingEnabled) {
-        if (!domainsToPartition.isEmpty()) {
-            StringBuilder domainsToPartitionBuilder;
-            bool isFirstDomain = true;
-            for (auto& domain : domainsToPartition) {
-                appendWithDelimiter(domainsToPartitionBuilder, domain, isFirstDomain);
-                isFirstDomain = false;
-            }
-            RELEASE_LOG_INFO(ResourceLoadStatisticsDebug, "About to partition cookies in third-party contexts for %{public}s.", domainsToPartitionBuilder.toString().utf8().data());
-        }
-
-        if (!domainsToBlock.isEmpty()) {
-            StringBuilder domainsToBlockBuilder;
-            bool isFirstDomain = true;
-            for (auto& domain : domainsToBlock) {
-                appendWithDelimiter(domainsToBlockBuilder, domain, isFirstDomain);
-                isFirstDomain = false;
-            }
-            RELEASE_LOG_INFO(ResourceLoadStatisticsDebug, "About to block cookies in third-party contexts for %{public}s.", domainsToBlockBuilder.toString().utf8().data());
-        }
-
-        if (!domainsToNeitherPartitionNorBlock.isEmpty()) {
-            StringBuilder domainsToNeitherPartitionNorBlockBuilder;
-            bool isFirstDomain = true;
-            for (auto& domain : domainsToNeitherPartitionNorBlock) {
-                appendWithDelimiter(domainsToNeitherPartitionNorBlockBuilder, domain, isFirstDomain);
-                isFirstDomain = false;
-            }
-            RELEASE_LOG_INFO(ResourceLoadStatisticsDebug, "About to neither partition nor block cookies in third-party contexts for %{public}s.", domainsToNeitherPartitionNorBlockBuilder.toString().utf8().data());
-        }
-    }
-#endif
-
-    RunLoop::main().dispatch([this, protectedThis = makeRef(*this), domainsToPartition = crossThreadCopy(domainsToPartition), domainsToBlock = crossThreadCopy(domainsToBlock), domainsToNeitherPartitionNorBlock = crossThreadCopy(domainsToNeitherPartitionNorBlock), completionHandler = WTFMove(completionHandler)] () mutable {
-        m_updatePrevalentDomainsToPartitionOrBlockCookiesHandler(domainsToPartition, domainsToBlock, domainsToNeitherPartitionNorBlock, ShouldClearFirst::No, [this, protectedThis = WTFMove(protectedThis), completionHandler = WTFMove(completionHandler)]() mutable {
-            m_statisticsQueue->dispatch([completionHandler = WTFMove(completionHandler)]() mutable {
-                completionHandler();
-            });
-        });
-#if !RELEASE_LOG_DISABLED
-        RELEASE_LOG_INFO_IF(m_debugLoggingEnabled, ResourceLoadStatisticsDebug, "Done updating cookie partitioning and blocking.");
-#endif
+    ASSERT(RunLoop::isMain());
+    m_statisticsQueue->dispatch([this, protectedThis = makeRef(*this), seconds] {
+        if (m_memoryStore)
+            m_memoryStore->setTimeToLiveCookiePartitionFree(seconds);
     });
 }
 
-void WebResourceLoadStatisticsStore::updateCookiePartitioningForDomains(const Vector<String>& domainsToPartition, const Vector<String>& domainsToBlock, const Vector<String>& domainsToNeitherPartitionNorBlock, ShouldClearFirst shouldClearFirst, CompletionHandler<void()>&& completionHandler)
+void WebResourceLoadStatisticsStore::setMinimumTimeBetweenDataRecordsRemoval(Seconds seconds)
 {
-    ASSERT(!RunLoop::isMain());
-    if (domainsToPartition.isEmpty() && domainsToBlock.isEmpty() && domainsToNeitherPartitionNorBlock.isEmpty() && shouldClearFirst == ShouldClearFirst::No) {
-        completionHandler();
-        return;
-    }
-    
-    if (shouldClearFirst == ShouldClearFirst::Yes)
-        resetCookiePartitioningState();
-    else {
-        for (auto& domain : domainsToNeitherPartitionNorBlock) {
-            auto& statistic = ensureResourceStatisticsForPrimaryDomain(domain);
-            statistic.isMarkedForCookiePartitioning = false;
-            statistic.isMarkedForCookieBlocking = false;
-        }
-    }
-
-    for (auto& domain : domainsToPartition)
-        ensureResourceStatisticsForPrimaryDomain(domain).isMarkedForCookiePartitioning = true;
-
-    for (auto& domain : domainsToBlock)
-        ensureResourceStatisticsForPrimaryDomain(domain).isMarkedForCookieBlocking = true;
-
-    RunLoop::main().dispatch([this, shouldClearFirst, protectedThis = makeRef(*this), domainsToPartition = crossThreadCopy(domainsToPartition), domainsToBlock = crossThreadCopy(domainsToBlock), domainsToNeitherPartitionNorBlock = crossThreadCopy(domainsToNeitherPartitionNorBlock), completionHandler = WTFMove(completionHandler)] () mutable {
-        m_updatePrevalentDomainsToPartitionOrBlockCookiesHandler(domainsToPartition, domainsToBlock, domainsToNeitherPartitionNorBlock, shouldClearFirst, [this, protectedThis = WTFMove(protectedThis), completionHandler = WTFMove(completionHandler)]() mutable {
-            m_statisticsQueue->dispatch([completionHandler = WTFMove(completionHandler)]() mutable {
-                completionHandler();
-            });
-        });
+    ASSERT(RunLoop::isMain());
+    m_statisticsQueue->dispatch([this, protectedThis = makeRef(*this), seconds] {
+        if (m_memoryStore)
+            m_memoryStore->setMinimumTimeBetweenDataRecordsRemoval(seconds);
     });
 }
 
-void WebResourceLoadStatisticsStore::clearPartitioningStateForDomains(const Vector<String>& domains, CompletionHandler<void()>&& completionHandler)
+void WebResourceLoadStatisticsStore::setGrandfatheringTime(Seconds seconds)
 {
-    ASSERT(!RunLoop::isMain());
-    if (domains.isEmpty()) {
-        completionHandler();
-        return;
-    }
-
-    RunLoop::main().dispatch([this, protectedThis = makeRef(*this), domains = crossThreadCopy(domains)] () {
-        m_removeDomainsHandler(domains);
+    ASSERT(RunLoop::isMain());
+    m_statisticsQueue->dispatch([this, protectedThis = makeRef(*this), seconds] {
+        if (m_memoryStore)
+            m_memoryStore->setGrandfatheringTime(seconds);
     });
-
-    for (auto& domain : domains) {
-        auto& statistic = ensureResourceStatisticsForPrimaryDomain(domain);
-        statistic.isMarkedForCookiePartitioning = false;
-        statistic.isMarkedForCookieBlocking = false;
-    }
-
-    completionHandler();
-}
-
-void WebResourceLoadStatisticsStore::resetCookiePartitioningState()
-{
-    ASSERT(!RunLoop::isMain());
-    for (auto& resourceStatistic : m_resourceStatisticsMap.values()) {
-        resourceStatistic.isMarkedForCookiePartitioning = false;
-        resourceStatistic.isMarkedForCookieBlocking = false;
-    }
-}
-
-void WebResourceLoadStatisticsStore::processStatistics(const WTF::Function<void (const ResourceLoadStatistics&)>& processFunction) const
-{
-    ASSERT(!RunLoop::isMain());
-    for (auto& resourceStatistic : m_resourceStatisticsMap.values())
-        processFunction(resourceStatistic);
-}
-
-bool WebResourceLoadStatisticsStore::hasHadUnexpiredRecentUserInteraction(ResourceLoadStatistics& resourceStatistic) const
-{
-    if (resourceStatistic.hadUserInteraction && hasStatisticsExpired(resourceStatistic)) {
-        // Drop privacy sensitive data because we no longer need it.
-        // Set timestamp to 0 so that statistics merge will know
-        // it has been reset as opposed to its default -1.
-        resourceStatistic.mostRecentUserInteractionTime = { };
-        resourceStatistic.storageAccessUnderTopFrameOrigins.clear();
-        resourceStatistic.hadUserInteraction = false;
-    }
-
-    return resourceStatistic.hadUserInteraction;
 }
 
-Vector<String> WebResourceLoadStatisticsStore::topPrivatelyControlledDomainsToRemoveWebsiteDataFor()
+void WebResourceLoadStatisticsStore::callUpdatePrevalentDomainsToPartitionOrBlockCookiesHandler(const Vector<String>& domainsToPartition, const Vector<String>& domainsToBlock, const Vector<String>& domainsToNeitherPartitionNorBlock, ShouldClearFirst shouldClearFirst, CompletionHandler<void()>&& completionHandler)
 {
-    ASSERT(!RunLoop::isMain());
-
-    bool shouldCheckForGrandfathering = m_endOfGrandfatheringTimestamp > WallTime::now();
-    bool shouldClearGrandfathering = !shouldCheckForGrandfathering && m_endOfGrandfatheringTimestamp;
-
-    if (shouldClearGrandfathering)
-        m_endOfGrandfatheringTimestamp = { };
-
-    Vector<String> prevalentResources;
-    for (auto& statistic : m_resourceStatisticsMap.values()) {
-        if (statistic.isPrevalentResource && !hasHadUnexpiredRecentUserInteraction(statistic) && (!shouldCheckForGrandfathering || !statistic.grandfathered))
-            prevalentResources.append(statistic.highLevelDomain);
-
-        if (shouldClearGrandfathering && statistic.grandfathered)
-            statistic.grandfathered = false;
-    }
-
-    return prevalentResources;
-}
-
-void WebResourceLoadStatisticsStore::includeTodayAsOperatingDateIfNecessary()
-{
-    ASSERT(!RunLoop::isMain());
-
-    auto today = OperatingDate::today();
-    if (!m_operatingDates.isEmpty() && today <= m_operatingDates.last())
-        return;
-
-    while (m_operatingDates.size() >= operatingDatesWindow)
-        m_operatingDates.remove(0);
-
-    m_operatingDates.append(today);
+    ASSERT(RunLoop::isMain());
+    m_updatePrevalentDomainsToPartitionOrBlockCookiesHandler(domainsToPartition, domainsToBlock, domainsToNeitherPartitionNorBlock, shouldClearFirst, WTFMove(completionHandler));
 }
 
-bool WebResourceLoadStatisticsStore::hasStatisticsExpired(const ResourceLoadStatistics& resourceStatistic) const
+void WebResourceLoadStatisticsStore::callRemoveDomainsHandler(const Vector<String>& domains)
 {
-    if (m_operatingDates.size() >= operatingDatesWindow) {
-        if (OperatingDate::fromWallTime(resourceStatistic.mostRecentUserInteractionTime) < m_operatingDates.first())
-            return true;
-    }
-
-    // If we don't meet the real criteria for an expired statistic, check the user setting for a tighter restriction (mainly for testing).
-    if (m_parameters.timeToLiveUserInteraction) {
-        if (WallTime::now() > resourceStatistic.mostRecentUserInteractionTime + m_parameters.timeToLiveUserInteraction.value())
-            return true;
-    }
+    ASSERT(RunLoop::isMain());
 
-    return false;
+    m_removeDomainsHandler(domains);
 }
     
 void WebResourceLoadStatisticsStore::setMaxStatisticsEntries(size_t maximumEntryCount)
 {
-    m_parameters.maxStatisticsEntries = maximumEntryCount;
+    ASSERT(RunLoop::isMain());
+    m_statisticsQueue->dispatch([this, protectedThis = makeRef(*this), maximumEntryCount] {
+        if (m_memoryStore)
+            m_memoryStore->setMaxStatisticsEntries(maximumEntryCount);
+    });
 }
     
 void WebResourceLoadStatisticsStore::setPruneEntriesDownTo(size_t pruneTargetCount)
 {
-    m_parameters.pruneEntriesDownTo = pruneTargetCount;
-}
-    
-struct StatisticsLastSeen {
-    String topPrivatelyOwnedDomain;
-    WallTime lastSeen;
-};
-    
-static void pruneResources(HashMap<String, WebCore::ResourceLoadStatistics>& statisticsMap, Vector<StatisticsLastSeen>& statisticsToPrune, size_t& numberOfEntriesToPrune)
-{
-    if (statisticsToPrune.size() > numberOfEntriesToPrune) {
-        std::sort(statisticsToPrune.begin(), statisticsToPrune.end(), [](const StatisticsLastSeen& a, const StatisticsLastSeen& b) {
-            return a.lastSeen < b.lastSeen;
-        });
-    }
-
-    for (size_t i = 0, end = std::min(numberOfEntriesToPrune, statisticsToPrune.size()); i != end; ++i, --numberOfEntriesToPrune)
-        statisticsMap.remove(statisticsToPrune[i].topPrivatelyOwnedDomain);
-}
-    
-static unsigned computeImportance(const ResourceLoadStatistics& resourceStatistic)
-{
-    unsigned importance = maxImportance;
-    if (!resourceStatistic.isPrevalentResource)
-        importance -= 1;
-    if (!resourceStatistic.hadUserInteraction)
-        importance -= 2;
-    return importance;
-}
-    
-void WebResourceLoadStatisticsStore::pruneStatisticsIfNeeded()
-{
-    ASSERT(!RunLoop::isMain());
-    if (m_resourceStatisticsMap.size() <= m_parameters.maxStatisticsEntries)
-        return;
-
-    ASSERT(m_parameters.pruneEntriesDownTo <= m_parameters.maxStatisticsEntries);
-
-    size_t numberOfEntriesLeftToPrune = m_resourceStatisticsMap.size() - m_parameters.pruneEntriesDownTo;
-    ASSERT(numberOfEntriesLeftToPrune);
-    
-    Vector<StatisticsLastSeen> resourcesToPrunePerImportance[maxImportance + 1];
-    for (auto& resourceStatistic : m_resourceStatisticsMap.values())
-        resourcesToPrunePerImportance[computeImportance(resourceStatistic)].append({ resourceStatistic.highLevelDomain, resourceStatistic.lastSeen });
-    
-    for (unsigned importance = 0; numberOfEntriesLeftToPrune && importance <= maxImportance; ++importance)
-        pruneResources(m_resourceStatisticsMap, resourcesToPrunePerImportance[importance], numberOfEntriesLeftToPrune);
+    ASSERT(RunLoop::isMain());
 
-    ASSERT(!numberOfEntriesLeftToPrune);
+    m_statisticsQueue->dispatch([this, protectedThis = makeRef(*this), pruneTargetCount] {
+        if (m_memoryStore)
+            m_memoryStore->setPruneEntriesDownTo(pruneTargetCount);
+    });
 }
 
 void WebResourceLoadStatisticsStore::resetParametersToDefaultValues()
 {
-    m_parameters = { };
+    ASSERT(RunLoop::isMain());
+
+    m_statisticsQueue->dispatch([this, protectedThis(makeRef(*this))] {
+        if (m_memoryStore)
+            m_memoryStore->resetParametersToDefaultValues();
+    });
 }
 
 void WebResourceLoadStatisticsStore::logTestingEvent(const String& event)
 {
-    if (!m_statisticsTestingCallback)
-        return;
+    ASSERT(RunLoop::isMain());
 
-    if (RunLoop::isMain())
+    if (m_statisticsTestingCallback)
         m_statisticsTestingCallback(event);
-    else {
-        RunLoop::main().dispatch([this, protectedThis = makeRef(*this), event = event.isolatedCopy()] {
-            if (m_statisticsTestingCallback)
-                m_statisticsTestingCallback(event);
-        });
-    }
 }
 
 } // namespace WebKit
index 359c59d..4504e89 100644 (file)
 #pragma once
 
 #include "Connection.h"
-#include "ResourceLoadStatisticsClassifier.h"
-#include "ResourceLoadStatisticsPersistentStorage.h"
 #include "WebsiteDataType.h"
 #include <wtf/CompletionHandler.h>
-#include <wtf/HashSet.h>
-#include <wtf/MonotonicTime.h>
 #include <wtf/RunLoop.h>
 #include <wtf/Vector.h>
 #include <wtf/WallTime.h>
 #include <wtf/text/WTFString.h>
 
-#if HAVE(CORE_PREDICTION)
-#include "ResourceLoadStatisticsClassifierCocoa.h"
-#endif
-
 namespace WTF {
 class WorkQueue;
 }
 
 namespace WebCore {
-class KeyedDecoder;
-class KeyedEncoder;
 class ResourceRequest;
 class URL;
 struct ResourceLoadStatistics;
@@ -55,7 +45,8 @@ struct ResourceLoadStatistics;
 
 namespace WebKit {
 
-class OperatingDate;
+class ResourceLoadStatisticsMemoryStore;
+class ResourceLoadStatisticsPersistentStorage;
 class WebFrameProxy;
 class WebProcessProxy;
 
@@ -68,12 +59,12 @@ enum class StorageAccessStatus {
 
 class WebResourceLoadStatisticsStore final : public IPC::Connection::WorkQueueMessageReceiver {
 public:
-    using UpdatePrevalentDomainsToPartitionOrBlockCookiesHandler = WTF::Function<void(const Vector<String>& domainsToPartition, const Vector<String>& domainsToBlock, const Vector<String>& domainsToNeitherPartitionNorBlock, ShouldClearFirst, CompletionHandler<void()>&&)>;
-    using HasStorageAccessForFrameHandler = WTF::Function<void(const String& resourceDomain, const String& firstPartyDomain, uint64_t frameID, uint64_t pageID, WTF::Function<void(bool hasAccess)>&& callback)>;
-    using GrantStorageAccessHandler = WTF::Function<void(const String& resourceDomain, const String& firstPartyDomain, std::optional<uint64_t> frameID, uint64_t pageID, WTF::Function<void(bool wasGranted)>&& callback)>;
-    using RemoveAllStorageAccessHandler = WTF::Function<void()>;
-    using RemovePrevalentDomainsHandler = WTF::Function<void (const Vector<String>&)>;
-    static Ref<WebResourceLoadStatisticsStore> create(const String& resourceLoadStatisticsDirectory, Function<void (const String&)>&& testingCallback, bool isEphemeral, UpdatePrevalentDomainsToPartitionOrBlockCookiesHandler&& updatePrevalentDomainsToPartitionOrBlockCookiesHandler = [](const WTF::Vector<String>&, const WTF::Vector<String>&, const WTF::Vector<String>&, ShouldClearFirst, CompletionHandler<void()>&& callback) { callback(); }, HasStorageAccessForFrameHandler&& hasStorageAccessForFrameHandler = [](const String&, const String&, uint64_t, uint64_t, WTF::Function<void(bool)>&&) { }, GrantStorageAccessHandler&& grantStorageAccessHandler = [](const String&, const String&, std::optional<uint64_t>, uint64_t, WTF::Function<void(bool)>&&) { }, RemoveAllStorageAccessHandler&& removeAllStorageAccessHandler = []() { }, RemovePrevalentDomainsHandler&& removeDomainsHandler = [] (const WTF::Vector<String>&) { })
+    using UpdatePrevalentDomainsToPartitionOrBlockCookiesHandler = Function<void(const Vector<String>& domainsToPartition, const Vector<String>& domainsToBlock, const Vector<String>& domainsToNeitherPartitionNorBlock, ShouldClearFirst, CompletionHandler<void()>&&)>;
+    using HasStorageAccessForFrameHandler = Function<void(const String& resourceDomain, const String& firstPartyDomain, uint64_t frameID, uint64_t pageID, Function<void(bool hasAccess)>&& callback)>;
+    using GrantStorageAccessHandler = Function<void(const String& resourceDomain, const String& firstPartyDomain, std::optional<uint64_t> frameID, uint64_t pageID, Function<void(bool wasGranted)>&& callback)>;
+    using RemoveAllStorageAccessHandler = Function<void()>;
+    using RemovePrevalentDomainsHandler = Function<void(const Vector<String>&)>;
+    static Ref<WebResourceLoadStatisticsStore> create(const String& resourceLoadStatisticsDirectory, Function<void (const String&)>&& testingCallback, bool isEphemeral, UpdatePrevalentDomainsToPartitionOrBlockCookiesHandler&& updatePrevalentDomainsToPartitionOrBlockCookiesHandler = [](const Vector<String>&, const Vector<String>&, const Vector<String>&, ShouldClearFirst, CompletionHandler<void()>&& callback) { callback(); }, HasStorageAccessForFrameHandler&& hasStorageAccessForFrameHandler = [](const String&, const String&, uint64_t, uint64_t, Function<void(bool)>&&) { }, GrantStorageAccessHandler&& grantStorageAccessHandler = [](const String&, const String&, std::optional<uint64_t>, uint64_t, Function<void(bool)>&&) { }, RemoveAllStorageAccessHandler&& removeAllStorageAccessHandler = []() { }, RemovePrevalentDomainsHandler&& removeDomainsHandler = [] (const Vector<String>&) { })
     {
         return adoptRef(*new WebResourceLoadStatisticsStore(resourceLoadStatisticsDirectory, WTFMove(testingCallback), isEphemeral, WTFMove(updatePrevalentDomainsToPartitionOrBlockCookiesHandler), WTFMove(hasStorageAccessForFrameHandler), WTFMove(grantStorageAccessHandler), WTFMove(removeAllStorageAccessHandler), WTFMove(removeDomainsHandler)));
     }
@@ -82,16 +73,15 @@ public:
 
     static const OptionSet<WebsiteDataType>& monitoredDataTypes();
 
-    bool isEmpty() const { return m_resourceStatisticsMap.isEmpty(); }
     WorkQueue& statisticsQueue() { return m_statisticsQueue.get(); }
 
-    void setNotifyPagesWhenDataRecordsWereScanned(bool value) { m_parameters.shouldNotifyPagesWhenDataRecordsWereScanned = value; }
-    void setShouldClassifyResourcesBeforeDataRecordsRemoval(bool value) { m_parameters.shouldClassifyResourcesBeforeDataRecordsRemoval = value; }
-    void setShouldSubmitTelemetry(bool value) { m_parameters.shouldSubmitTelemetry = value; }
+    void setNotifyPagesWhenDataRecordsWereScanned(bool);
+    void setShouldClassifyResourcesBeforeDataRecordsRemoval(bool);
+    void setShouldSubmitTelemetry(bool);
 
     void resourceLoadStatisticsUpdated(Vector<WebCore::ResourceLoadStatistics>&& origins);
 
-    void hasStorageAccess(String&& subFrameHost, String&& topFrameHost, uint64_t frameID, uint64_t pageID, WTF::CompletionHandler<void (bool)>&& callback);
+    void hasStorageAccess(String&& subFrameHost, String&& topFrameHost, uint64_t frameID, uint64_t pageID, CompletionHandler<void(bool)>&& callback);
     void requestStorageAccess(String&& subFrameHost, String&& topFrameHost, uint64_t frameID, uint64_t pageID, bool promptEnabled, CompletionHandler<void(StorageAccessStatus)>&&);
     void requestStorageAccessUnderOpener(String&& primaryDomainInNeedOfStorageAccess, uint64_t openerPageID, String&& openerPrimaryDomain, bool isTriggeredByUserGesture);
     void grantStorageAccess(String&& subFrameHost, String&& topFrameHost, uint64_t frameID, uint64_t pageID, bool userWasPromptedNow, CompletionHandler<void(bool)>&&);
@@ -144,91 +134,34 @@ public:
     void setGrandfatheringTime(Seconds);
     void setMaxStatisticsEntries(size_t);
     void setPruneEntriesDownTo(size_t);
-    
-    void processStatistics(const WTF::Function<void (const WebCore::ResourceLoadStatistics&)>&) const;
-    void pruneStatisticsIfNeeded();
 
     void resetParametersToDefaultValues();
 
-    std::unique_ptr<WebCore::KeyedEncoder> createEncoderFromData() const;
-    void mergeWithDataFromDecoder(WebCore::KeyedDecoder&);
-    void clearInMemory();
-    void grandfatherExistingWebsiteData(CompletionHandler<void()>&&);
-
     void setResourceLoadStatisticsDebugMode(bool);
 
     void setStatisticsTestingCallback(Function<void (const String&)>&& callback) { m_statisticsTestingCallback = WTFMove(callback); }
     void logTestingEvent(const String&);
+    void callGrantStorageAccessHandler(const String& subFramePrimaryDomain, const String& topFramePrimaryDomain, std::optional<uint64_t> frameID, uint64_t pageID, CompletionHandler<void(bool)>&&);
+    void removeAllStorageAccess();
+    void callUpdatePrevalentDomainsToPartitionOrBlockCookiesHandler(const Vector<String>& domainsToPartition, const Vector<String>& domainsToBlock, const Vector<String>& domainsToNeitherPartitionNorBlock, ShouldClearFirst, CompletionHandler<void()>&&);
+    void callRemoveDomainsHandler(const Vector<String>& domains);
+    void callHasStorageAccessForFrameHandler(const String& resourceDomain, const String& firstPartyDomain, uint64_t frameID, uint64_t pageID, Function<void(bool)>&&);
 
 private:
     WebResourceLoadStatisticsStore(const String&, Function<void(const String&)>&& testingCallback, bool isEphemeral, UpdatePrevalentDomainsToPartitionOrBlockCookiesHandler&&, HasStorageAccessForFrameHandler&&, GrantStorageAccessHandler&&, RemoveAllStorageAccessHandler&&, RemovePrevalentDomainsHandler&&);
 
-    void removeDataRecords(CompletionHandler<void()>&&);
-
     // IPC::MessageReceiver
     void didReceiveMessage(IPC::Connection&, IPC::Decoder&) override;
 
     void performDailyTasks();
-    bool shouldRemoveDataRecords() const;
-    void setDataRecordsBeingRemoved(bool);
-
-    bool shouldPartitionCookies(const WebCore::ResourceLoadStatistics&) const;
-    bool shouldBlockCookies(const WebCore::ResourceLoadStatistics&) const;
-    bool hasUserGrantedStorageAccessThroughPrompt(const WebCore::ResourceLoadStatistics&, const String& firstPartyPrimaryDomain) const;
-    bool hasStatisticsExpired(const WebCore::ResourceLoadStatistics&) const;
-    bool hasHadUnexpiredRecentUserInteraction(WebCore::ResourceLoadStatistics&) const;
-    void includeTodayAsOperatingDateIfNecessary();
-    Vector<String> topPrivatelyControlledDomainsToRemoveWebsiteDataFor();
-    void updateCookiePartitioning(CompletionHandler<void()>&&);
-    void updateCookiePartitioningForDomains(const Vector<String>& domainsToPartition, const Vector<String>& domainsToBlock, const Vector<String>& domainsToNeitherPartitionNorBlock, ShouldClearFirst, CompletionHandler<void()>&&);
-    void clearPartitioningStateForDomains(const Vector<String>& domains, CompletionHandler<void()>&&);
-    void mergeStatistics(Vector<WebCore::ResourceLoadStatistics>&&);
-    WebCore::ResourceLoadStatistics& ensureResourceStatisticsForPrimaryDomain(const String&);
-    unsigned recursivelyGetAllDomainsThatHaveRedirectedToThisDomain(const WebCore::ResourceLoadStatistics&, HashSet<String>& domainsThatHaveRedirectedTo, unsigned numberOfRecursiveCalls);
-    void markAsPrevalentIfHasRedirectedToPrevalent(WebCore::ResourceLoadStatistics&);
-    void setPrevalentResource(WebCore::ResourceLoadStatistics&, ResourceLoadPrevalence);
-    void processStatisticsAndDataRecords();
-
-    void scheduleStatisticsProcessingRequestIfNecessary();
-    void cancelPendingStatisticsProcessingRequest();
-
-    void resetCookiePartitioningState();
-    StorageAccessStatus storageAccessStatus(const String& subFramePrimaryDomain, const String& topFramePrimaryDomain);
-    void grantStorageAccessInternal(String&& subFrameHost, String&& topFrameHost, std::optional<uint64_t> frameID, uint64_t pageID, bool userWasPromptedNowOrEarlier, CompletionHandler<void(bool)>&&);
-    void removeAllStorageAccess();
 
-    void setDebugLogggingEnabled(bool enabled) { m_debugLoggingEnabled  = enabled; }
-    void setStorageAccessPromptsEnabled(bool enabled) { m_storageAccessPromptsEnabled  = enabled; }
+    StorageAccessStatus storageAccessStatus(const String& subFramePrimaryDomain, const String& topFramePrimaryDomain);
 
     void flushAndDestroyPersistentStore();
 
-#if PLATFORM(COCOA)
-    void registerUserDefaultsIfNeeded();
-#endif
-
-    bool wasAccessedAsFirstPartyDueToUserInteraction(const WebCore::ResourceLoadStatistics& current, const WebCore::ResourceLoadStatistics& updated);
-
-    struct Parameters {
-        size_t pruneEntriesDownTo { 800 };
-        size_t maxStatisticsEntries { 1000 };
-        std::optional<Seconds> timeToLiveUserInteraction;
-        Seconds timeToLiveCookiePartitionFree { 24_h };
-        Seconds minimumTimeBetweenDataRecordsRemoval { 1_h };
-        Seconds grandfatheringTime { 24_h * 7 };
-        bool shouldNotifyPagesWhenDataRecordsWereScanned { false };
-        bool shouldClassifyResourcesBeforeDataRecordsRemoval { true };
-        bool shouldSubmitTelemetry { true };
-    };
-
-    HashMap<String, WebCore::ResourceLoadStatistics> m_resourceStatisticsMap;
-#if HAVE(CORE_PREDICTION)
-    ResourceLoadStatisticsClassifierCocoa m_resourceLoadStatisticsClassifier;
-#else
-    ResourceLoadStatisticsClassifier m_resourceLoadStatisticsClassifier;
-#endif
-    Ref<WTF::WorkQueue> m_statisticsQueue;
+    Ref<WorkQueue> m_statisticsQueue;
+    std::unique_ptr<ResourceLoadStatisticsMemoryStore> m_memoryStore;
     std::unique_ptr<ResourceLoadStatisticsPersistentStorage> m_persistentStorage;
-    Vector<OperatingDate> m_operatingDates;
 
     UpdatePrevalentDomainsToPartitionOrBlockCookiesHandler m_updatePrevalentDomainsToPartitionOrBlockCookiesHandler;
     HasStorageAccessForFrameHandler m_hasStorageAccessForFrameHandler;
@@ -236,26 +169,11 @@ private:
     RemoveAllStorageAccessHandler m_removeAllStorageAccessHandler;
     RemovePrevalentDomainsHandler m_removeDomainsHandler;
 
-    WallTime m_endOfGrandfatheringTimestamp;
     RunLoop::Timer<WebResourceLoadStatisticsStore> m_dailyTasksTimer;
-    MonotonicTime m_lastTimeDataRecordsWereRemoved;
 
-    Parameters m_parameters;
-
-#if ENABLE(NETSCAPE_PLUGIN_API)
-    HashSet<uint64_t> m_activePluginTokens;
-#endif
-    bool m_dataRecordsBeingRemoved { false };
-
-    bool m_debugModeEnabled { false };
-    bool m_debugLoggingEnabled { false };
-    bool m_storageAccessPromptsEnabled { false };
     bool m_hasScheduledProcessStats { false };
 
     Function<void (const String&)> m_statisticsTestingCallback;
-
-    uint64_t m_lastStatisticsProcessingRequestIdentifier { 0 };
-    std::optional<uint64_t> m_pendingStatisticsProcessingRequestIdentifier;
 };
 
 } // namespace WebKit
index 0b9f786..f9692e1 100644 (file)
@@ -26,9 +26,9 @@
 #include "config.h"
 #include "WebResourceLoadStatisticsTelemetry.h"
 
+#include "ResourceLoadStatisticsMemoryStore.h"
 #include "WebProcessPool.h"
 #include "WebProcessProxy.h"
-#include "WebResourceLoadStatisticsStore.h"
 #include <WebCore/DiagnosticLoggingKeys.h>
 #include <WebCore/ResourceLoadStatistics.h>
 #include <wtf/MainThread.h>
@@ -54,7 +54,7 @@ struct PrevalentResourceTelemetry {
     unsigned timesAccessedAsFirstPartyDueToStorageAccessAPI;
 };
 
-static Vector<PrevalentResourceTelemetry> sortedPrevalentResourceTelemetry(const WebResourceLoadStatisticsStore& store)
+static Vector<PrevalentResourceTelemetry> sortedPrevalentResourceTelemetry(const ResourceLoadStatisticsMemoryStore& store)
 {
     ASSERT(!RunLoop::isMain());
     Vector<PrevalentResourceTelemetry> sorted;
@@ -240,7 +240,7 @@ void static notifyPages(const Vector<PrevalentResourceTelemetry>& sortedPrevalen
     notifyPages(sortedPrevalentResources.size(), totalNumberOfPrevalentResourcesWithUserInteraction, median(sortedPrevalentResourcesWithoutUserInteraction, 0, 2, subframeUnderTopFrameOriginsGetter));
 }
     
-void WebResourceLoadStatisticsTelemetry::calculateAndSubmit(const WebResourceLoadStatisticsStore& resourceLoadStatisticsStore)
+void WebResourceLoadStatisticsTelemetry::calculateAndSubmit(const ResourceLoadStatisticsMemoryStore& resourceLoadStatisticsStore)
 {
     ASSERT(!RunLoop::isMain());
     
index d89fafa..f96e61a 100644 (file)
 
 namespace WebKit {
 
-class WebResourceLoadStatisticsStore;
+class ResourceLoadStatisticsMemoryStore;
 
 namespace WebResourceLoadStatisticsTelemetry {
     
-void calculateAndSubmit(const WebResourceLoadStatisticsStore&);
+void calculateAndSubmit(const ResourceLoadStatisticsMemoryStore&);
 void setNotifyPagesWhenTelemetryWasCaptured(bool);
     
 }
index 456608a..1d86470 100644 (file)
                7A8A9D581EF119B0009801AE /* APIInjectedBundleClient.h in Headers */ = {isa = PBXBuildFile; fileRef = 7A8A9D571EF119AA009801AE /* APIInjectedBundleClient.h */; };
                7A8A9D5A1EF13029009801AE /* APIInjectedBundleBundleClient.h in Headers */ = {isa = PBXBuildFile; fileRef = 7A8A9D591EF13020009801AE /* APIInjectedBundleBundleClient.h */; };
                7A8A9D5C1EF14598009801AE /* APIInjectedBundlePageResourceLoadClient.h in Headers */ = {isa = PBXBuildFile; fileRef = 7A8A9D5B1EF1458E009801AE /* APIInjectedBundlePageResourceLoadClient.h */; };
-               7AAD175F1EA6AF99003B0894 /* WebResourceLoadStatisticsStoreCocoa.mm in Sources */ = {isa = PBXBuildFile; fileRef = 7AAD175E1EA6AF37003B0894 /* WebResourceLoadStatisticsStoreCocoa.mm */; };
                7AB6EA451EEAAE3800037B2B /* APIIconDatabaseClient.h in Headers */ = {isa = PBXBuildFile; fileRef = 7AB6EA441EEAAE2300037B2B /* APIIconDatabaseClient.h */; };
                7AB6EA471EEAB6B800037B2B /* APIGeolocationProvider.h in Headers */ = {isa = PBXBuildFile; fileRef = 7AB6EA461EEAB6B000037B2B /* APIGeolocationProvider.h */; };
                7AF236201E79A3E400438A05 /* WebErrors.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 7AF2361E1E79A3B400438A05 /* WebErrors.cpp */; };
                8372DB281A67562800C697C5 /* WebPageDiagnosticLoggingClient.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8372DB261A67562800C697C5 /* WebPageDiagnosticLoggingClient.cpp */; };
                8372DB291A67562800C697C5 /* WebPageDiagnosticLoggingClient.h in Headers */ = {isa = PBXBuildFile; fileRef = 8372DB271A67562800C697C5 /* WebPageDiagnosticLoggingClient.h */; };
                8372DB2F1A677D4A00C697C5 /* WKDiagnosticLoggingResultType.h in Headers */ = {isa = PBXBuildFile; fileRef = 8372DB2E1A677D4A00C697C5 /* WKDiagnosticLoggingResultType.h */; settings = {ATTRIBUTES = (Private, ); }; };
+               837A660120E2AD8500A9DBD8 /* ResourceLoadStatisticsMemoryStoreCocoa.mm in Sources */ = {isa = PBXBuildFile; fileRef = 837A660020E2AD8400A9DBD8 /* ResourceLoadStatisticsMemoryStoreCocoa.mm */; };
                83891B631A68B3420030F386 /* APIDiagnosticLoggingClient.h in Headers */ = {isa = PBXBuildFile; fileRef = 83891B621A68B3420030F386 /* APIDiagnosticLoggingClient.h */; };
                83891B691A68BEBC0030F386 /* _WKDiagnosticLoggingDelegate.h in Headers */ = {isa = PBXBuildFile; fileRef = 83891B681A68BEBC0030F386 /* _WKDiagnosticLoggingDelegate.h */; settings = {ATTRIBUTES = (Private, ); }; };
                83891B6C1A68C30B0030F386 /* DiagnosticLoggingClient.h in Headers */ = {isa = PBXBuildFile; fileRef = 83891B6A1A68C30B0030F386 /* DiagnosticLoggingClient.h */; };
                839A2F321E2067450039057E /* HighPerformanceGraphicsUsageSampler.h in Headers */ = {isa = PBXBuildFile; fileRef = 839A2F301E2067390039057E /* HighPerformanceGraphicsUsageSampler.h */; };
                83A0ED341F747CCD003299EB /* PreconnectTask.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 83A0ED331F747CC7003299EB /* PreconnectTask.cpp */; };
                83A0ED351F747CCF003299EB /* PreconnectTask.h in Headers */ = {isa = PBXBuildFile; fileRef = 83A0ED321F747CC6003299EB /* PreconnectTask.h */; };
+               83B3CB0F20E2AAFE00441E9B /* ResourceLoadStatisticsMemoryStore.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 83681DCB20E2904B00C249CD /* ResourceLoadStatisticsMemoryStore.cpp */; };
+               83B3CB1020E2AB0100441E9B /* ResourceLoadStatisticsMemoryStore.h in Headers */ = {isa = PBXBuildFile; fileRef = 83681DCA20E2904A00C249CD /* ResourceLoadStatisticsMemoryStore.h */; };
                83BDCCB91AC5FDB6003F6441 /* NetworkCacheStatistics.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 83BDCCB81AC5FDB6003F6441 /* NetworkCacheStatistics.cpp */; };
                83D454D71BE9D3C4006C93BD /* NetworkLoadClient.h in Headers */ = {isa = PBXBuildFile; fileRef = 83D454D61BE9D3C4006C93BD /* NetworkLoadClient.h */; };
                83EE575B1DB7D61100C74C50 /* WebValidationMessageClient.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 83EE57591DB7D60600C74C50 /* WebValidationMessageClient.cpp */; };
                        dstSubfolderSpec = 7;
                        files = (
                                4112EDDA20E407A500BEA92A /* cfca.com.npP11CertEnroll.MAC.CGB.sb in Copy Plug-in Sandbox Profiles */,
+                               E19BDA86193665E300B97F57 /* com.apple.appstore.CodeRedeemerNetscapePlugin.sb in Copy Plug-in Sandbox Profiles */,
                                4112EDDB20E407A500BEA92A /* com.apple.BocomSubmitCtrl.sb in Copy Plug-in Sandbox Profiles */,
+                               7CB16FEF1724BA23007A0A95 /* com.apple.ist.ds.appleconnect.webplugin.sb in Copy Plug-in Sandbox Profiles */,
                                4112EDDC20E407A500BEA92A /* com.apple.NPSafeInput.sb in Copy Plug-in Sandbox Profiles */,
                                4112EDDD20E407A500BEA92A /* com.apple.NPSafeSubmit.sb in Copy Plug-in Sandbox Profiles */,
-                               4112EDDE20E407A500BEA92A /* com.cfca.npSecEditCtl.MAC.BOC.plugin.sb in Copy Plug-in Sandbox Profiles */,
-                               4112EDDF20E407A500BEA92A /* com.cmbchina.CMBSecurity.sb in Copy Plug-in Sandbox Profiles */,
-                               4112EDE020E407A500BEA92A /* com.ftsafe.NPAPI-Core-Safe-SoftKeybaord.plugin.rfc1034identifier.sb in Copy Plug-in Sandbox Profiles */,
-                               E19BDA86193665E300B97F57 /* com.apple.appstore.CodeRedeemerNetscapePlugin.sb in Copy Plug-in Sandbox Profiles */,
-                               7CB16FEF1724BA23007A0A95 /* com.apple.ist.ds.appleconnect.webplugin.sb in Copy Plug-in Sandbox Profiles */,
                                7CB16FF01724BA24007A0A95 /* com.apple.QuickTime Plugin.plugin.sb in Copy Plug-in Sandbox Profiles */,
                                7A772C8D1DDD4A25000F34F1 /* com.apple.WebKit.plugin-common.sb in Copy Plug-in Sandbox Profiles */,
+                               4112EDDE20E407A500BEA92A /* com.cfca.npSecEditCtl.MAC.BOC.plugin.sb in Copy Plug-in Sandbox Profiles */,
                                414DD37A20BF49A5006959FB /* com.cisco.webex.plugin.gpc64.sb in Copy Plug-in Sandbox Profiles */,
+                               4112EDDF20E407A500BEA92A /* com.cmbchina.CMBSecurity.sb in Copy Plug-in Sandbox Profiles */,
+                               4112EDE020E407A500BEA92A /* com.ftsafe.NPAPI-Core-Safe-SoftKeybaord.plugin.rfc1034identifier.sb in Copy Plug-in Sandbox Profiles */,
                                413CCD5020DEBC740065A21A /* com.google.googletalkbrowserplugin.sb in Copy Plug-in Sandbox Profiles */,
                                4157E4B020E2ECDF00A6C0D7 /* com.google.o1dbrowserplugin.sb in Copy Plug-in Sandbox Profiles */,
                                A102A7081EC0EEE900D81D82 /* com.macromedia.Flash Player ESR.plugin.sb in Copy Plug-in Sandbox Profiles */,
                7A9CD8C01C77984900D9F6C7 /* WebResourceLoadStatisticsStore.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = WebResourceLoadStatisticsStore.cpp; sourceTree = "<group>"; };
                7A9CD8C11C77984900D9F6C7 /* WebResourceLoadStatisticsStore.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WebResourceLoadStatisticsStore.h; sourceTree = "<group>"; };
                7A9CD8C21C779AD600D9F6C7 /* WebResourceLoadStatisticsStore.messages.in */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = WebResourceLoadStatisticsStore.messages.in; sourceTree = "<group>"; };
-               7AAD175E1EA6AF37003B0894 /* WebResourceLoadStatisticsStoreCocoa.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = WebResourceLoadStatisticsStoreCocoa.mm; sourceTree = "<group>"; };
                7AB6EA441EEAAE2300037B2B /* APIIconDatabaseClient.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = APIIconDatabaseClient.h; sourceTree = "<group>"; };
                7AB6EA461EEAB6B000037B2B /* APIGeolocationProvider.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = APIGeolocationProvider.h; sourceTree = "<group>"; };
                7ACFAAD820B88D4F00C53203 /* process-webcontent-entitlements.sh */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.script.sh; path = "process-webcontent-entitlements.sh"; sourceTree = "<group>"; };
                834B25101A842C8700CFB150 /* NetworkCacheStatistics.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = NetworkCacheStatistics.h; sourceTree = "<group>"; };
                8360349D1ACB34D600626549 /* WebSQLiteDatabaseTracker.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = WebSQLiteDatabaseTracker.cpp; sourceTree = "<group>"; };
                8360349E1ACB34D600626549 /* WebSQLiteDatabaseTracker.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WebSQLiteDatabaseTracker.h; sourceTree = "<group>"; };
+               83681DCA20E2904A00C249CD /* ResourceLoadStatisticsMemoryStore.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ResourceLoadStatisticsMemoryStore.h; sourceTree = "<group>"; };
+               83681DCB20E2904B00C249CD /* ResourceLoadStatisticsMemoryStore.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ResourceLoadStatisticsMemoryStore.cpp; sourceTree = "<group>"; };
                8372DB241A674C8F00C697C5 /* WKPageDiagnosticLoggingClient.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WKPageDiagnosticLoggingClient.h; sourceTree = "<group>"; };
                8372DB261A67562800C697C5 /* WebPageDiagnosticLoggingClient.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = WebPageDiagnosticLoggingClient.cpp; sourceTree = "<group>"; };
                8372DB271A67562800C697C5 /* WebPageDiagnosticLoggingClient.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WebPageDiagnosticLoggingClient.h; sourceTree = "<group>"; };
                8372DB2E1A677D4A00C697C5 /* WKDiagnosticLoggingResultType.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WKDiagnosticLoggingResultType.h; sourceTree = "<group>"; };
+               837A660020E2AD8400A9DBD8 /* ResourceLoadStatisticsMemoryStoreCocoa.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = ResourceLoadStatisticsMemoryStoreCocoa.mm; sourceTree = "<group>"; };
                83891B621A68B3420030F386 /* APIDiagnosticLoggingClient.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = APIDiagnosticLoggingClient.h; sourceTree = "<group>"; };
                83891B681A68BEBC0030F386 /* _WKDiagnosticLoggingDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = _WKDiagnosticLoggingDelegate.h; sourceTree = "<group>"; };
                83891B6A1A68C30B0030F386 /* DiagnosticLoggingClient.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DiagnosticLoggingClient.h; sourceTree = "<group>"; };
                                CDA29A1E1CBEB5FB00901CCF /* PlaybackSessionManagerProxy.h */,
                                CDA29A221CBEB61A00901CCF /* PlaybackSessionManagerProxy.messages.in */,
                                CDA29A1F1CBEB5FB00901CCF /* PlaybackSessionManagerProxy.mm */,
+                               837A660020E2AD8400A9DBD8 /* ResourceLoadStatisticsMemoryStoreCocoa.mm */,
                                1A002D47196B345D00B9AD44 /* SessionStateCoding.h */,
                                1A002D46196B345D00B9AD44 /* SessionStateCoding.mm */,
                                3157135C2040A9B20084F9CF /* SystemPreviewControllerCocoa.mm */,
                                7C4694CB1A4B510A00AD5845 /* WebPasteboardProxyCocoa.mm */,
                                7CE4D2151A49148400C7F152 /* WebProcessPoolCocoa.mm */,
                                1A04F6171A4A3A7A00A21B6E /* WebProcessProxyCocoa.mm */,
-                               7AAD175E1EA6AF37003B0894 /* WebResourceLoadStatisticsStoreCocoa.mm */,
                                51D124311E6DE521002B2820 /* WebURLSchemeHandlerCocoa.h */,
                                51D124321E6DE521002B2820 /* WebURLSchemeHandlerCocoa.mm */,
                                2DFC7DB91BCCC19500C1548C /* WebViewImpl.h */,
                                A55BA81B1BA25B1E007CD33D /* RemoteWebInspectorProxy.cpp */,
                                A55BA8191BA25B1E007CD33D /* RemoteWebInspectorProxy.h */,
                                A55BA81A1BA25B1E007CD33D /* RemoteWebInspectorProxy.messages.in */,
+                               83681DCB20E2904B00C249CD /* ResourceLoadStatisticsMemoryStore.cpp */,
+                               83681DCA20E2904A00C249CD /* ResourceLoadStatisticsMemoryStore.h */,
                                51E6C1621F2935CD00FD3437 /* ResourceLoadStatisticsPersistentStorage.cpp */,
                                51E6C1611F2935CD00FD3437 /* ResourceLoadStatisticsPersistentStorage.h */,
                                BC111B08112F5E3C00337BAB /* ResponsivenessTimer.cpp */,
                                A55BA8171BA23E12007CD33D /* RemoteWebInspectorUI.h in Headers */,
                                6BE969CD1E54E054008B7483 /* ResourceLoadStatisticsClassifier.h in Headers */,
                                6BE969CB1E54D4CF008B7483 /* ResourceLoadStatisticsClassifierCocoa.h in Headers */,
+                               83B3CB1020E2AB0100441E9B /* ResourceLoadStatisticsMemoryStore.h in Headers */,
                                51E6C1641F2935DD00FD3437 /* ResourceLoadStatisticsPersistentStorage.h in Headers */,
                                1A30066E1110F4F70031937C /* ResponsivenessTimer.h in Headers */,
                                410482CE1DDD324F00F006D0 /* RTCNetwork.h in Headers */,
                                1BBBE4A019B66C53006B7D81 /* RemoteWebInspectorUIMessageReceiver.cpp in Sources */,
                                6BE969C71E54D4B6008B7483 /* ResourceLoadStatisticsClassifier.cpp in Sources */,
                                6BE969CA1E54D4CF008B7483 /* ResourceLoadStatisticsClassifierCocoa.cpp in Sources */,
+                               83B3CB0F20E2AAFE00441E9B /* ResourceLoadStatisticsMemoryStore.cpp in Sources */,
+                               837A660120E2AD8500A9DBD8 /* ResourceLoadStatisticsMemoryStoreCocoa.mm in Sources */,
                                51E6C1631F2935D800FD3437 /* ResourceLoadStatisticsPersistentStorage.cpp in Sources */,
                                51E6C1601F29356000FD3437 /* ResourceLoadStatisticsPersistentStorageIOS.mm in Sources */,
                                BC111B09112F5E3C00337BAB /* ResponsivenessTimer.cpp in Sources */,
                                510AFFB916542048001BA05E /* WebResourceLoader.cpp in Sources */,
                                51F060E11654318500F3281B /* WebResourceLoaderMessageReceiver.cpp in Sources */,
                                7A791EFC1C7D08C500C4C52B /* WebResourceLoadStatisticsStore.cpp in Sources */,
-                               7AAD175F1EA6AF99003B0894 /* WebResourceLoadStatisticsStoreCocoa.mm in Sources */,
                                7A791EFA1C7CFCF100C4C52B /* WebResourceLoadStatisticsStoreMessageReceiver.cpp in Sources */,
                                6B821DDD1EEF05DD00D7AF4A /* WebResourceLoadStatisticsTelemetry.cpp in Sources */,
                                413075AF1DE85F580039EC69 /* WebRTCMonitor.cpp in Sources */,