WebKit Bugzilla
Attachment 343687 Details for
Bug 187055
: Split memory store logic out of WebResourceLoadStatisticsStore to clarify threading model
Home
|
New
|
Browse
|
Search
|
[?]
|
Reports
|
Requests
|
Help
|
New Account
|
Log In
Remember
[x]
|
Forgot Password
Login:
[x]
[patch]
Patch
bug-187055-20180626215528.patch (text/plain), 192.88 KB, created by
Chris Dumez
on 2018-06-26 21:55:02 PDT
(
hide
)
Description:
Patch
Filename:
MIME Type:
Creator:
Chris Dumez
Created:
2018-06-26 21:55:02 PDT
Size:
192.88 KB
patch
obsolete
>Subversion Revision: 233216 >diff --git a/Source/WebKit/ChangeLog b/Source/WebKit/ChangeLog >index 82b9a9a393a21854ba8b0c95be4baaf5adf2ba7d..cf20db490f51e0d2612585be9b94c92cda8c0a97 100644 >--- a/Source/WebKit/ChangeLog >+++ b/Source/WebKit/ChangeLog >@@ -1,3 +1,183 @@ >+2018-06-26 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 >+ >+ Reviewed by NOBODY (OOPS!). >+ >+ 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 hoping 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-26 Tim Horton <timothy_horton@apple.com> > > Promote some experimental features to traditional features >diff --git a/Source/WebKit/CMakeLists.txt b/Source/WebKit/CMakeLists.txt >index 422e5242d69e23bf11df69d978329c571b08a186..6cab2d7d70e4eaf824a402af821b85abd8ff3145 100644 >--- a/Source/WebKit/CMakeLists.txt >+++ b/Source/WebKit/CMakeLists.txt >@@ -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 >diff --git a/Source/WebKit/UIProcess/Cocoa/ResourceLoadStatisticsMemoryStoreCocoa.mm b/Source/WebKit/UIProcess/Cocoa/ResourceLoadStatisticsMemoryStoreCocoa.mm >new file mode 100644 >index 0000000000000000000000000000000000000000..e6b145948bd79c287bf2ed941fd5d11397ce53e1 >--- /dev/null >+++ b/Source/WebKit/UIProcess/Cocoa/ResourceLoadStatisticsMemoryStoreCocoa.mm >@@ -0,0 +1,58 @@ >+/* >+ * 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. >+ */ >+ >+#import "config.h" >+#import "ResourceLoadStatisticsMemoryStore.h" >+ >+namespace WebKit { >+ >+void ResourceLoadStatisticsMemoryStore::registerUserDefaultsIfNeeded() >+{ >+ static dispatch_once_t initOnce; >+ >+ dispatch_once(&initOnce, ^{ >+ Seconds timeToLiveUserInteraction([[NSUserDefaults standardUserDefaults] doubleForKey:@"ResourceLoadStatisticsTimeToLiveUserInteraction"]); >+ if (timeToLiveUserInteraction > 0_s && timeToLiveUserInteraction <= 24_h * 30) >+ setTimeToLiveUserInteraction(timeToLiveUserInteraction); >+ >+ Seconds timeToLiveCookiePartitionFree([[NSUserDefaults standardUserDefaults] doubleForKey:@"ResourceLoadStatisticsTimeToLiveCookiePartitionFree"]); >+ if (timeToLiveCookiePartitionFree > 0_s && timeToLiveCookiePartitionFree <= 24_h) >+ setTimeToLiveCookiePartitionFree(timeToLiveCookiePartitionFree); >+ >+ Seconds minimumTimeBetweenDataRecordsRemoval([[NSUserDefaults standardUserDefaults] doubleForKey:@"ResourceLoadStatisticsMinimumTimeBetweenDataRecordsRemoval"]); >+ if (minimumTimeBetweenDataRecordsRemoval > 0_s && minimumTimeBetweenDataRecordsRemoval < 1_h) >+ setMinimumTimeBetweenDataRecordsRemoval(minimumTimeBetweenDataRecordsRemoval); >+ >+ Seconds grandfatheringTime([[NSUserDefaults standardUserDefaults] doubleForKey:@"ResourceLoadStatisticsGrandfatheringTime"]); >+ if (grandfatheringTime > 0_s && grandfatheringTime <= 24_h * 7) >+ setGrandfatheringTime(grandfatheringTime); >+ >+ setDebugLogggingEnabled([[NSUserDefaults standardUserDefaults] boolForKey:@"ResourceLoadStatisticsDebugLoggingEnabled"]); >+ setResourceLoadStatisticsDebugMode([[NSUserDefaults standardUserDefaults] boolForKey:@"ExperimentalResourceLoadStatisticsDebugMode"]); >+ setStorageAccessPromptsEnabled([[NSUserDefaults standardUserDefaults] boolForKey:@"ExperimentalStorageAccessPromptsEnabled"]); >+ }); >+} >+ >+} >diff --git a/Source/WebKit/UIProcess/Cocoa/WebResourceLoadStatisticsStoreCocoa.mm b/Source/WebKit/UIProcess/Cocoa/WebResourceLoadStatisticsStoreCocoa.mm >deleted file mode 100644 >index fe312c775487955680353cc3ba6cf807b50ea618..0000000000000000000000000000000000000000 >--- a/Source/WebKit/UIProcess/Cocoa/WebResourceLoadStatisticsStoreCocoa.mm >+++ /dev/null >@@ -1,58 +0,0 @@ >-/* >- * Copyright (C) 2017 Apple Inc. All rights reserved. >- * >- * Redistribution and use in source and binary forms, with or without >- * modification, are permitted provided that the following conditions >- * are met: >- * 1. Redistributions of source code must retain the above copyright >- * notice, this list of conditions and the following disclaimer. >- * 2. Redistributions in binary form must reproduce the above copyright >- * notice, this list of conditions and the following disclaimer in the >- * documentation and/or other materials provided with the distribution. >- * >- * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' >- * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, >- * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR >- * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS >- * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR >- * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF >- * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS >- * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN >- * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) >- * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF >- * THE POSSIBILITY OF SUCH DAMAGE. >- */ >- >-#include "config.h" >-#import "WebResourceLoadStatisticsStore.h" >- >-namespace WebKit { >- >-void WebResourceLoadStatisticsStore::registerUserDefaultsIfNeeded() >-{ >- static dispatch_once_t initOnce; >- >- dispatch_once(&initOnce, ^ { >- Seconds timeToLiveUserInteraction([[NSUserDefaults standardUserDefaults] doubleForKey:@"ResourceLoadStatisticsTimeToLiveUserInteraction"]); >- if (timeToLiveUserInteraction > 0_s && timeToLiveUserInteraction <= 24_h * 30) >- setTimeToLiveUserInteraction(timeToLiveUserInteraction); >- >- Seconds timeToLiveCookiePartitionFree([[NSUserDefaults standardUserDefaults] doubleForKey:@"ResourceLoadStatisticsTimeToLiveCookiePartitionFree"]); >- if (timeToLiveCookiePartitionFree > 0_s && timeToLiveCookiePartitionFree <= 24_h) >- setTimeToLiveCookiePartitionFree(timeToLiveCookiePartitionFree); >- >- Seconds minimumTimeBetweenDataRecordsRemoval([[NSUserDefaults standardUserDefaults] doubleForKey:@"ResourceLoadStatisticsMinimumTimeBetweenDataRecordsRemoval"]); >- if (minimumTimeBetweenDataRecordsRemoval > 0_s && minimumTimeBetweenDataRecordsRemoval < 1_h) >- setMinimumTimeBetweenDataRecordsRemoval(minimumTimeBetweenDataRecordsRemoval); >- >- Seconds grandfatheringTime([[NSUserDefaults standardUserDefaults] doubleForKey:@"ResourceLoadStatisticsGrandfatheringTime"]); >- if (grandfatheringTime > 0_s && grandfatheringTime <= 24_h * 7) >- setGrandfatheringTime(grandfatheringTime); >- >- setDebugLogggingEnabled([[NSUserDefaults standardUserDefaults] boolForKey:@"ResourceLoadStatisticsDebugLoggingEnabled"]); >- setResourceLoadStatisticsDebugMode([[NSUserDefaults standardUserDefaults] boolForKey:@"ExperimentalResourceLoadStatisticsDebugMode"]); >- setStorageAccessPromptsEnabled([[NSUserDefaults standardUserDefaults] boolForKey:@"ExperimentalStorageAccessPromptsEnabled"]); >- }); >-} >- >-} >diff --git a/Source/WebKit/UIProcess/ResourceLoadStatisticsMemoryStore.cpp b/Source/WebKit/UIProcess/ResourceLoadStatisticsMemoryStore.cpp >new file mode 100644 >index 0000000000000000000000000000000000000000..0fa548789921efbb14cbc447cf4e142234a77005 >--- /dev/null >+++ b/Source/WebKit/UIProcess/ResourceLoadStatisticsMemoryStore.cpp >@@ -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 >index 0000000000000000000000000000000000000000..4a08b7b56b31534c545c837ea70e7d78b36975c4 >--- /dev/null >+++ b/Source/WebKit/UIProcess/ResourceLoadStatisticsMemoryStore.h >@@ -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 >diff --git a/Source/WebKit/UIProcess/ResourceLoadStatisticsPersistentStorage.cpp b/Source/WebKit/UIProcess/ResourceLoadStatisticsPersistentStorage.cpp >index 234468020a2aaf7e459bcd1849245ad86a102330..0dd44b0aab862088f8ff8552cf5de20cfada34c0 100644 >--- a/Source/WebKit/UIProcess/ResourceLoadStatisticsPersistentStorage.cpp >+++ b/Source/WebKit/UIProcess/ResourceLoadStatisticsPersistentStorage.cpp >@@ -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,8 +82,9 @@ 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) > { >@@ -124,7 +126,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 +136,7 @@ void ResourceLoadStatisticsPersistentStorage::startMonitoringDisk() > refreshMemoryStoreFromDisk(); > break; > case FileMonitor::FileChangeType::Removal: >- m_memoryStore.clearInMemory(); >+ m_memoryStore.clear(); > m_fileMonitor = nullptr; > monitorDirectoryForNewStatistics(); > break; >@@ -156,7 +158,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 +288,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(); > }); >diff --git a/Source/WebKit/UIProcess/ResourceLoadStatisticsPersistentStorage.h b/Source/WebKit/UIProcess/ResourceLoadStatisticsPersistentStorage.h >index b58de8771b7d5f496b42504420c627665c7602e1..05ea99347b7e3a0b6767bea3fc7b1b7fd040d533 100644 >--- a/Source/WebKit/UIProcess/ResourceLoadStatisticsPersistentStorage.h >+++ b/Source/WebKit/UIProcess/ResourceLoadStatisticsPersistentStorage.h >@@ -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; >diff --git a/Source/WebKit/UIProcess/WebResourceLoadStatisticsStore.cpp b/Source/WebKit/UIProcess/WebResourceLoadStatisticsStore.cpp >index b08c2814a2b75176ee8249d8e4a26ab6ec3029ad..09d8b6f87532138778e45ee6e398e20159fa1723 100644 >--- a/Source/WebKit/UIProcess/WebResourceLoadStatisticsStore.cpp >+++ b/Source/WebKit/UIProcess/WebResourceLoadStatisticsStore.cpp >@@ -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" >@@ -37,26 +37,14 @@ > #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,15 @@ 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_memoryStore->setPersistentStorage(*m_persistentStorage); > }); > > 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 +144,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 +154,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 +204,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 +238,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 +314,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 +364,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 +377,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 +390,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 +403,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 +418,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 +433,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 +446,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 +474,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 +483,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 +491,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 +503,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 +515,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 +530,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 +543,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 +558,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 +573,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 +586,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 +599,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 +612,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 +625,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 +638,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 +649,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 +668,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 +688,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 +708,8 @@ void WebResourceLoadStatisticsStore::scheduleCookiePartitioningStateReset() > { > ASSERT(RunLoop::isMain()); > m_statisticsQueue->dispatch([this, protectedThis = makeRef(*this)] { >- resetCookiePartitioningState(); >+ if (m_memoryStore) >+ m_memoryStore->resetCookiePartitioningState(); > }); > } > #endif >@@ -1059,7 +718,8 @@ void WebResourceLoadStatisticsStore::scheduleClearInMemory() > { > ASSERT(RunLoop::isMain()); > m_statisticsQueue->dispatch([this, protectedThis = makeRef(*this)] { >- clearInMemory(); >+ if (m_memoryStore) >+ m_memoryStore->clear(); > }); > } > >@@ -1067,20 +727,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 +756,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 >diff --git a/Source/WebKit/UIProcess/WebResourceLoadStatisticsStore.h b/Source/WebKit/UIProcess/WebResourceLoadStatisticsStore.h >index 96a529f484bc56d4d2c67fd0e872e128bacd3ba2..c52a2055fbfca8e36a02244e9f971216e10aa523 100644 >--- a/Source/WebKit/UIProcess/WebResourceLoadStatisticsStore.h >+++ b/Source/WebKit/UIProcess/WebResourceLoadStatisticsStore.h >@@ -26,28 +26,18 @@ > #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 = []() { }) { }, 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 = []() { }) { }, 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 >diff --git a/Source/WebKit/UIProcess/WebResourceLoadStatisticsTelemetry.cpp b/Source/WebKit/UIProcess/WebResourceLoadStatisticsTelemetry.cpp >index 0b9f7863bb601178c563ff9dc36c4871937e58e2..f9692e12912a6c1c098d3b9f60804ae2ff37df9a 100644 >--- a/Source/WebKit/UIProcess/WebResourceLoadStatisticsTelemetry.cpp >+++ b/Source/WebKit/UIProcess/WebResourceLoadStatisticsTelemetry.cpp >@@ -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()); > >diff --git a/Source/WebKit/UIProcess/WebResourceLoadStatisticsTelemetry.h b/Source/WebKit/UIProcess/WebResourceLoadStatisticsTelemetry.h >index d89fafa2fcb453d643340ef14a7444bf1792feed..f96e61a1af0ab7353b031db753f9ddb6f52c629b 100644 >--- a/Source/WebKit/UIProcess/WebResourceLoadStatisticsTelemetry.h >+++ b/Source/WebKit/UIProcess/WebResourceLoadStatisticsTelemetry.h >@@ -29,11 +29,11 @@ > > namespace WebKit { > >-class WebResourceLoadStatisticsStore; >+class ResourceLoadStatisticsMemoryStore; > > namespace WebResourceLoadStatisticsTelemetry { > >-void calculateAndSubmit(const WebResourceLoadStatisticsStore&); >+void calculateAndSubmit(const ResourceLoadStatisticsMemoryStore&); > void setNotifyPagesWhenTelemetryWasCaptured(bool); > > } >diff --git a/Source/WebKit/WebKit.xcodeproj/project.pbxproj b/Source/WebKit/WebKit.xcodeproj/project.pbxproj >index f10b1b8b972489310a86d95aad2a1713ecc5f035..9ab391f1b4461258a2e3c3a321e03661c8e02fe1 100644 >--- a/Source/WebKit/WebKit.xcodeproj/project.pbxproj >+++ b/Source/WebKit/WebKit.xcodeproj/project.pbxproj >@@ -1324,7 +1324,6 @@ > 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 */; }; >@@ -1460,6 +1459,7 @@ > 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 */; }; >@@ -1471,6 +1471,8 @@ > 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 */; }; >@@ -3805,7 +3807,6 @@ > 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>"; }; >@@ -3951,10 +3952,13 @@ > 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>"; }; >@@ -5543,6 +5547,7 @@ > CDA29A1E1CBEB5FB00901CCF /* PlaybackSessionManagerProxy.h */, > CDA29A221CBEB61A00901CCF /* PlaybackSessionManagerProxy.messages.in */, > CDA29A1F1CBEB5FB00901CCF /* PlaybackSessionManagerProxy.mm */, >+ 837A660020E2AD8400A9DBD8 /* ResourceLoadStatisticsMemoryStoreCocoa.mm */, > 1A002D47196B345D00B9AD44 /* SessionStateCoding.h */, > 1A002D46196B345D00B9AD44 /* SessionStateCoding.mm */, > 3157135C2040A9B20084F9CF /* SystemPreviewControllerCocoa.mm */, >@@ -5560,7 +5565,6 @@ > 7C4694CB1A4B510A00AD5845 /* WebPasteboardProxyCocoa.mm */, > 7CE4D2151A49148400C7F152 /* WebProcessPoolCocoa.mm */, > 1A04F6171A4A3A7A00A21B6E /* WebProcessProxyCocoa.mm */, >- 7AAD175E1EA6AF37003B0894 /* WebResourceLoadStatisticsStoreCocoa.mm */, > 51D124311E6DE521002B2820 /* WebURLSchemeHandlerCocoa.h */, > 51D124321E6DE521002B2820 /* WebURLSchemeHandlerCocoa.mm */, > 2DFC7DB91BCCC19500C1548C /* WebViewImpl.h */, >@@ -7313,6 +7317,8 @@ > 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 */, >@@ -9200,6 +9206,7 @@ > 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 */, >@@ -10975,6 +10982,8 @@ > 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 */, >@@ -11288,7 +11297,6 @@ > 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 */,
You cannot view the attachment while viewing its details because your browser does not support IFRAMEs.
View the attachment on a separate page
.
View Attachment As Diff
View Attachment As Raw
Actions:
View
|
Formatted Diff
|
Diff
Attachments on
bug 187055
:
343632
|
343646
|
343672
|
343687
|
343717
|
343811