Resource Load Statistics: Grandfather domains for existing data records
authorwilander@apple.com <wilander@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Wed, 17 May 2017 23:55:24 +0000 (23:55 +0000)
committerwilander@apple.com <wilander@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Wed, 17 May 2017 23:55:24 +0000 (23:55 +0000)
https://bugs.webkit.org/show_bug.cgi?id=172155
<rdar://problem/24913532>

Reviewed by Alex Christensen.

Source/WebCore:

Test: http/tests/loading/resourceLoadStatistics/grandfathering.html

* loader/ResourceLoadObserver.cpp:
(WebCore::ResourceLoadObserver::setGrandfathered):
(WebCore::ResourceLoadObserver::isGrandfathered):
(WebCore::ResourceLoadObserver::setMinimumTimeBetweeenDataRecordsRemoval):
(WebCore::ResourceLoadObserver::setGrandfatheringTime):
    Functions for testing and configuration.
    ResourceLoadObserver::setMinimumTimeBetweeenDataRecordsRemoval() changed as a result of moving
    WebKit::WebResourceLoadStatisticsStore::setMinimumTimeBetweeenDataRecordsRemoval() here.
* loader/ResourceLoadObserver.h:
* loader/ResourceLoadStatisticsStore.cpp:
(WebCore::ResourceLoadStatisticsStore::createEncoderFromData):
(WebCore::ResourceLoadStatisticsStore::readDataFromDecoder):
    Now contains endOfGrandfatheringTimestamp.
(WebCore::ResourceLoadStatisticsStore::clearInMemoryAndPersistent):
    Now makes a call to m_grandfatherExistingWebsiteDataHandler().
(WebCore::ResourceLoadStatisticsStore::setGrandfatherExistingWebsiteDataCallback):
(WebCore::ResourceLoadStatisticsStore::setMinimumTimeBetweeenDataRecordsRemoval):
    Changed as a result of moving
    WebKit::WebResourceLoadStatisticsStore::setMinimumTimeBetweeenDataRecordsRemoval() here.
(WebCore::ResourceLoadStatisticsStore::setGrandfatheringTime):
(WebCore::ResourceLoadStatisticsStore::topPrivatelyControlledDomainsToRemoveWebsiteDataFor):
    Renamed since it now also takes grandfathering into account.
(WebCore::ResourceLoadStatisticsStore::updateStatisticsForRemovedDataRecords):
    Fixed typo in local variable name.
(WebCore::ResourceLoadStatisticsStore::handleFreshStartWithEmptyOrNoStore):
(WebCore::ResourceLoadStatisticsStore::shouldRemoveDataRecords):
    Convenience function added.
(WebCore::ResourceLoadStatisticsStore::dataRecordsBeingRemoved):
    Convenience function added.
(WebCore::ResourceLoadStatisticsStore::dataRecordsWereRemoved):
    Convenience function added.
(WebCore::ResourceLoadStatisticsStore::prevalentResourceDomainsWithoutUserInteraction): Deleted.
    Replaced by ResourceLoadStatisticsStore::topPrivatelyControlledDomainsToRemoveWebsiteDataFor().
* loader/ResourceLoadStatisticsStore.h:

Source/WebKit2:

When WebResourceLoadStatisticsStore starts fresh it needs
to scan existing website data records and 'grandfather' them to
allow ample time to capture user interaction.

* Shared/WebPreferencesDefinitions.h:
* UIProcess/API/C/WKResourceLoadStatisticsManager.cpp:
(WKResourceLoadStatisticsManagerSetGrandfathered):
(WKResourceLoadStatisticsManagerIsGrandfathered):
(WKResourceLoadStatisticsManagerSetMinimumTimeBetweeenDataRecordsRemoval):
(WKResourceLoadStatisticsManagerSetGrandfatheringTime):
    Functions for testing and configuration.
* UIProcess/API/C/WKResourceLoadStatisticsManager.h:
* UIProcess/Cocoa/WebResourceLoadStatisticsManagerCocoa.mm:
(WebKit::WebResourceLoadStatisticsManager::registerUserDefaultsIfNeeded):
    Added grandfathering configuration.
* UIProcess/WebProcessProxy.cpp:
(WebKit::WebProcessProxy::topPrivatelyControlledDomainsWithWebiteData):
    New function to get all top privately controlled domains that
    have website data.
* UIProcess/WebProcessProxy.h:
* UIProcess/WebResourceLoadStatisticsManager.cpp:
(WebKit::WebResourceLoadStatisticsManager::setGrandfathered):
(WebKit::WebResourceLoadStatisticsManager::isGrandfathered):
(WebKit::WebResourceLoadStatisticsManager::setMinimumTimeBetweeenDataRecordsRemoval):
(WebKit::WebResourceLoadStatisticsManager::setGrandfatheringTime):
(WebKit::WebResourceLoadStatisticsManager::resetToConsistentState):
    Functions for testing and configuration.
    WebResourceLoadStatisticsManager::setMinimumTimeBetweeenDataRecordsRemoval() changed
    as a result of WebResourceLoadStatisticsStore::setMinimumTimeBetweeenDataRecordsRemoval()
    moving to WebCore::ResourceLoadObserver::setMinimumTimeBetweeenDataRecordsRemoval().
* UIProcess/WebResourceLoadStatisticsManager.h:
* UIProcess/WebResourceLoadStatisticsStore.cpp:
(WebKit::initializeDataTypesToRemove):
(WebKit::WebResourceLoadStatisticsStore::removeDataRecords):
    Moved handling of pending removal into WebCore::ResourceLoadStatisticsStore since
    that's where grandfathering happens.
(WebKit::WebResourceLoadStatisticsStore::registerSharedResourceLoadObserver):
    Moved registration of write persistent store callback and reading of Cocoa defaults to
    WebResourceLoadStatisticsStore::registerSharedResourceLoadObserver without parameters
    so they are called for platforms without CFNETWORK_STORAGE_PARTITIONING.
    Now includes registering WebResourceLoadStatisticsStore::grandfatherExistingWebsiteData()
    as handler for grandfathering since it involves reading of the website data store.
(WebKit::WebResourceLoadStatisticsStore::grandfatherExistingWebsiteData):
(WebKit::WebResourceLoadStatisticsStore::readDataFromDiskIfNeeded):
(WebKit::WebResourceLoadStatisticsStore::setMinimumTimeBetweeenDataRecordsRemoval): Deleted.
    Now happens in WebCore::ResourceLoadObserver::setMinimumTimeBetweeenDataRecordsRemoval().
* UIProcess/WebResourceLoadStatisticsStore.h:
* UIProcess/WebsiteData/WebsiteDataRecord.cpp:
(WebKit::WebsiteDataRecord::topPrivatelyControlledDomain):
    New function to ask a WebsiteDataRecord for its top privately controlled domain.
* UIProcess/WebsiteData/WebsiteDataRecord.h:
* UIProcess/WebsiteData/WebsiteDataStore.cpp:
(WebKit::WebsiteDataStore::topPrivatelyControlledDomainsWithWebsiteData):
    New function to get all top privately controlled domains that
    have website data.
* UIProcess/WebsiteData/WebsiteDataStore.h:

Tools:

Adds test infrastructure needed for the added functionality.

* WebKitTestRunner/InjectedBundle/Bindings/TestRunner.idl:
* WebKitTestRunner/InjectedBundle/InjectedBundle.cpp:
(WTR::InjectedBundle::didReceiveMessageToPage):
* WebKitTestRunner/InjectedBundle/TestRunner.cpp:
(WTR::TestRunner::setStatisticsGrandfathered):
(WTR::TestRunner::isStatisticsGrandfathered):
(WTR::TestRunner::installStatisticsDidScanDataRecordsCallback):
(WTR::TestRunner::statisticsDidScanDataRecordsCallback):
(WTR::TestRunner::setStatisticsGrandfatheringTime):
* WebKitTestRunner/InjectedBundle/TestRunner.h:
* WebKitTestRunner/TestController.cpp:
(WTR::TestController::setStatisticsGrandfathered):
(WTR::TestController::isStatisticsGrandfathered):
(WTR::TestController::setStatisticsGrandfatheringTime):
* WebKitTestRunner/TestController.h:
* WebKitTestRunner/TestInvocation.cpp:
(WTR::TestInvocation::didReceiveSynchronousMessageFromInjectedBundle):

LayoutTests:

* http/tests/loading/resourceLoadStatistics/grandfathering-expected.txt: Added.
* http/tests/loading/resourceLoadStatistics/grandfathering.html: Added.
* platform/wk2/TestExpectations:
    Marked it Pass for WebKit2.

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

32 files changed:
LayoutTests/ChangeLog
LayoutTests/http/tests/loading/resourceLoadStatistics/grandfathering-expected.txt [new file with mode: 0644]
LayoutTests/http/tests/loading/resourceLoadStatistics/grandfathering.html [new file with mode: 0644]
LayoutTests/platform/wk2/TestExpectations
Source/WebCore/ChangeLog
Source/WebCore/loader/ResourceLoadObserver.cpp
Source/WebCore/loader/ResourceLoadObserver.h
Source/WebCore/loader/ResourceLoadStatisticsStore.cpp
Source/WebCore/loader/ResourceLoadStatisticsStore.h
Source/WebKit2/ChangeLog
Source/WebKit2/Shared/WebPreferencesDefinitions.h
Source/WebKit2/UIProcess/API/C/WKResourceLoadStatisticsManager.cpp
Source/WebKit2/UIProcess/API/C/WKResourceLoadStatisticsManager.h
Source/WebKit2/UIProcess/Cocoa/WebResourceLoadStatisticsManagerCocoa.mm
Source/WebKit2/UIProcess/WebProcessProxy.cpp
Source/WebKit2/UIProcess/WebProcessProxy.h
Source/WebKit2/UIProcess/WebResourceLoadStatisticsManager.cpp
Source/WebKit2/UIProcess/WebResourceLoadStatisticsManager.h
Source/WebKit2/UIProcess/WebResourceLoadStatisticsStore.cpp
Source/WebKit2/UIProcess/WebResourceLoadStatisticsStore.h
Source/WebKit2/UIProcess/WebsiteData/WebsiteDataRecord.cpp
Source/WebKit2/UIProcess/WebsiteData/WebsiteDataRecord.h
Source/WebKit2/UIProcess/WebsiteData/WebsiteDataStore.cpp
Source/WebKit2/UIProcess/WebsiteData/WebsiteDataStore.h
Tools/ChangeLog
Tools/WebKitTestRunner/InjectedBundle/Bindings/TestRunner.idl
Tools/WebKitTestRunner/InjectedBundle/InjectedBundle.cpp
Tools/WebKitTestRunner/InjectedBundle/TestRunner.cpp
Tools/WebKitTestRunner/InjectedBundle/TestRunner.h
Tools/WebKitTestRunner/TestController.cpp
Tools/WebKitTestRunner/TestController.h
Tools/WebKitTestRunner/TestInvocation.cpp

index f217601..068db43 100644 (file)
@@ -1,3 +1,16 @@
+2017-05-17  John Wilander  <wilander@apple.com>
+
+        Resource Load Statistics: Grandfather domains for existing data records
+        https://bugs.webkit.org/show_bug.cgi?id=172155
+        <rdar://problem/24913532>
+
+        Reviewed by Alex Christensen.
+
+        * http/tests/loading/resourceLoadStatistics/grandfathering-expected.txt: Added.
+        * http/tests/loading/resourceLoadStatistics/grandfathering.html: Added.
+        * platform/wk2/TestExpectations:
+            Marked it Pass for WebKit2.
+
 2017-05-17  Zalan Bujtas  <zalan@apple.com>
 
         Debug ASSERT: WebCore::RenderImageResource::shutdown
diff --git a/LayoutTests/http/tests/loading/resourceLoadStatistics/grandfathering-expected.txt b/LayoutTests/http/tests/loading/resourceLoadStatistics/grandfathering-expected.txt
new file mode 100644 (file)
index 0000000..2c16c6e
--- /dev/null
@@ -0,0 +1,43 @@
+main frame - didStartProvisionalLoadForFrame
+main frame - didCommitLoadForFrame
+main frame - didReceiveTitle: Test for Grandfathering
+main frame - didChangeLocationWithinPageForFrame
+main frame - willPerformClientRedirectToURL: http://localhost:8000/loading/resourceLoadStatistics/resources/set-cookie.php?name=firstPartyCookie&value=value#http://localhost:8000/loading/resourceLoadStatistics/grandfathering.html#step2 
+main frame - didFinishDocumentLoadForFrame
+main frame - didFinishLoadForFrame
+main frame - didStartProvisionalLoadForFrame
+main frame - didCancelClientRedirectForFrame
+main frame - didCommitLoadForFrame
+main frame - didFinishDocumentLoadForFrame
+main frame - didHandleOnloadEventsForFrame
+main frame - didFinishLoadForFrame
+main frame - willPerformClientRedirectToURL: http://localhost:8000/loading/resourceLoadStatistics/grandfathering.html#step2 
+main frame - didStartProvisionalLoadForFrame
+main frame - didCancelClientRedirectForFrame
+main frame - didCommitLoadForFrame
+main frame - didReceiveTitle: Test for Grandfathering
+main frame - didChangeLocationWithinPageForFrame
+main frame - didChangeLocationWithinPageForFrame
+main frame - didFinishDocumentLoadForFrame
+main frame - didHandleOnloadEventsForFrame
+main frame - didFinishLoadForFrame
+main frame - didChangeLocationWithinPageForFrame
+main frame - willPerformClientRedirectToURL: http://127.0.0.1:8000/loading/resourceLoadStatistics/resources/set-cookie.php?name=thirdPartyCookie&value=value#http://localhost:8000/loading/resourceLoadStatistics/grandfathering.html#step6 
+main frame - didStartProvisionalLoadForFrame
+main frame - didCancelClientRedirectForFrame
+main frame - didCommitLoadForFrame
+main frame - didFinishDocumentLoadForFrame
+main frame - didHandleOnloadEventsForFrame
+main frame - didFinishLoadForFrame
+main frame - willPerformClientRedirectToURL: http://localhost:8000/loading/resourceLoadStatistics/grandfathering.html#step6 
+main frame - didStartProvisionalLoadForFrame
+main frame - didCancelClientRedirectForFrame
+main frame - didCommitLoadForFrame
+main frame - didReceiveTitle: Test for Grandfathering
+main frame - didChangeLocationWithinPageForFrame
+main frame - didChangeLocationWithinPageForFrame
+main frame - didFinishDocumentLoadForFrame
+main frame - didHandleOnloadEventsForFrame
+main frame - didFinishLoadForFrame
+PASS Grandfathered cookie was not purged.
+
diff --git a/LayoutTests/http/tests/loading/resourceLoadStatistics/grandfathering.html b/LayoutTests/http/tests/loading/resourceLoadStatistics/grandfathering.html
new file mode 100644 (file)
index 0000000..9b69bc2
--- /dev/null
@@ -0,0 +1,119 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+    <meta charset="UTF-8">
+    <title>Test for Grandfathering</title>
+    <script src="../../resources/js-test-pre.js"></script>
+</head>
+<body>
+<script>
+    const firstPartyOrigin = "http://localhost:8000";
+    const firstPartyBaseUrl = firstPartyOrigin + "/loading/resourceLoadStatistics/resources";
+    const firstPartyCookieName = "firstPartyCookie";
+    const thirdPartyCookieName = "thirdPartyCookie";
+    const cookieValue = "value";
+    const subPathToSetFirstPartyCookie = "/set-cookie.php?name=" + firstPartyCookieName + "&value=" + cookieValue;
+    const returnUrl = firstPartyOrigin + "/loading/resourceLoadStatistics/grandfathering.html";
+    const thirdPartyOrigin = "http://127.0.0.1:8000";
+    const thirdPartyBaseUrl = thirdPartyOrigin + "/loading/resourceLoadStatistics/resources";
+    const subPathToSetThirdPartyCookie = "/set-cookie.php?name=" + thirdPartyCookieName + "&value=" + cookieValue;
+
+    function setEnableFeature(enable) {
+        if (!enable) {
+            testRunner.statisticsResetToConsistentState();
+        }
+        testRunner.setStatisticsNotifyPagesWhenDataRecordsWereScanned(enable);
+        internals.setResourceLoadStatisticsEnabled(enable);
+        testRunner.setCookieStoragePartitioningEnabled(enable);
+    }
+
+    function finishTest() {
+        setEnableFeature(false);
+        testRunner.notifyDone();
+    }
+
+    function fireDataModificationHandlerAndContinue() {
+        testRunner.installStatisticsDidModifyDataRecordsCallback(function() {
+            runTest();
+        });
+        testRunner.statisticsFireDataModificationHandler();
+    }
+
+    function clearInMemoryAndPersistentStoreAndContinue() {
+        testRunner.installStatisticsDidScanDataRecordsCallback(function() {
+            runTest();
+        });
+        testRunner.statisticsClearInMemoryAndPersistentStore();
+    }
+
+    function runTest() {
+        switch (document.location.hash) {
+            case "#step1":
+                // Set a first-party cookie for localhost.
+                document.location.href = firstPartyBaseUrl + subPathToSetFirstPartyCookie + "#" + returnUrl + "#step2";
+                break;
+            case "#step2":
+                document.location.hash = "step3";
+                // Check that localhost's cookie is there.
+                if (document.cookie !== firstPartyCookieName + "=" + cookieValue)
+                    testFailed("First-party cookie not set.");
+                runTest();
+                break;
+            case "#step3":
+                document.location.hash = "step4";
+                // Clear store to trigger grandfathering.
+                clearInMemoryAndPersistentStoreAndContinue();
+                break;
+            case "#step4":
+                document.location.hash = "step5";
+                // Set localhost as prevalent resource.
+                testRunner.setStatisticsPrevalentResource("http://localhost", true);
+                if (!testRunner.isStatisticsPrevalentResource("http://localhost"))
+                    testFailed("Localhost did not get set as prevalent resource.");
+                runTest();
+                break;
+            case "#step5":
+                // Set a first-party cookie for 127.0.0.1.
+                document.location.href = thirdPartyBaseUrl + subPathToSetThirdPartyCookie + "#" + returnUrl + "#step6";
+                break;
+            case "#step6":
+                document.location.hash = "step7";
+                // Set 127.0.0.1 as prevalent resource
+                testRunner.setStatisticsPrevalentResource("http://127.0.0.1", true);
+                if (!testRunner.isStatisticsPrevalentResource("http://127.0.0.1"))
+                    testFailed("127.0.0.1 did not get set as prevalent resource.");
+                runTest();
+                break;
+            case "#step7":
+                document.location.hash = "step8";
+                // Trigger a purge.
+                fireDataModificationHandlerAndContinue();
+                break;
+            case "#step8":
+                // Check that localhost's cookie is still there.
+                if (document.cookie !== firstPartyCookieName + "=" + cookieValue)
+                    testFailed("First-party cookie purged.");
+                else
+                    testPassed("Grandfathered cookie was not purged.");
+                finishTest();
+                break;
+            default:
+                testFailed("Should not be reached.");
+        }
+    }
+
+    if (document.location.hash === "" && window.testRunner && window.internals) {
+        setEnableFeature(true);
+
+        testRunner.setStatisticsShouldClassifyResourcesBeforeDataRecordsRemoval(false);
+        testRunner.setStatisticsMinimumTimeBetweeenDataRecordsRemoval(0);
+
+        testRunner.waitUntilDone();
+        testRunner.dumpChildFramesAsText();
+        document.location.hash = "step1";
+    }
+
+    runTest();
+</script>
+</body>
+</html>
\ No newline at end of file
index 7306fee..55e5b00 100644 (file)
@@ -703,6 +703,7 @@ http/tests/loading/resourceLoadStatistics/classify-as-prevalent-based-on-sub-fra
 http/tests/loading/resourceLoadStatistics/classify-as-prevalent-based-on-subresource-under-top-frame-origins.html [ Pass ]
 http/tests/loading/resourceLoadStatistics/classify-as-prevalent-based-on-subresource-unique-redirects-to.html [ Pass ]
 http/tests/loading/resourceLoadStatistics/clear-in-memory-and-persistent-store.html [ Pass ]
+http/tests/loading/resourceLoadStatistics/grandfathering.html [ Pass ]
 
 ### END OF (5) Progressions, expected successes that are expected failures in WebKit1.
 ########################################
index 5e7ae33..2a876e0 100644 (file)
@@ -1,3 +1,48 @@
+2017-05-17  John Wilander  <wilander@apple.com>
+
+        Resource Load Statistics: Grandfather domains for existing data records
+        https://bugs.webkit.org/show_bug.cgi?id=172155
+        <rdar://problem/24913532>
+
+        Reviewed by Alex Christensen.
+
+        Test: http/tests/loading/resourceLoadStatistics/grandfathering.html
+
+        * loader/ResourceLoadObserver.cpp:
+        (WebCore::ResourceLoadObserver::setGrandfathered):
+        (WebCore::ResourceLoadObserver::isGrandfathered):
+        (WebCore::ResourceLoadObserver::setMinimumTimeBetweeenDataRecordsRemoval):
+        (WebCore::ResourceLoadObserver::setGrandfatheringTime):
+            Functions for testing and configuration.
+            ResourceLoadObserver::setMinimumTimeBetweeenDataRecordsRemoval() changed as a result of moving
+            WebKit::WebResourceLoadStatisticsStore::setMinimumTimeBetweeenDataRecordsRemoval() here.
+        * loader/ResourceLoadObserver.h:
+        * loader/ResourceLoadStatisticsStore.cpp:
+        (WebCore::ResourceLoadStatisticsStore::createEncoderFromData):
+        (WebCore::ResourceLoadStatisticsStore::readDataFromDecoder):
+            Now contains endOfGrandfatheringTimestamp.
+        (WebCore::ResourceLoadStatisticsStore::clearInMemoryAndPersistent):
+            Now makes a call to m_grandfatherExistingWebsiteDataHandler().
+        (WebCore::ResourceLoadStatisticsStore::setGrandfatherExistingWebsiteDataCallback):
+        (WebCore::ResourceLoadStatisticsStore::setMinimumTimeBetweeenDataRecordsRemoval):
+            Changed as a result of moving
+            WebKit::WebResourceLoadStatisticsStore::setMinimumTimeBetweeenDataRecordsRemoval() here.
+        (WebCore::ResourceLoadStatisticsStore::setGrandfatheringTime):
+        (WebCore::ResourceLoadStatisticsStore::topPrivatelyControlledDomainsToRemoveWebsiteDataFor):
+            Renamed since it now also takes grandfathering into account.
+        (WebCore::ResourceLoadStatisticsStore::updateStatisticsForRemovedDataRecords):
+            Fixed typo in local variable name.
+        (WebCore::ResourceLoadStatisticsStore::handleFreshStartWithEmptyOrNoStore):
+        (WebCore::ResourceLoadStatisticsStore::shouldRemoveDataRecords):
+            Convenience function added.
+        (WebCore::ResourceLoadStatisticsStore::dataRecordsBeingRemoved):
+            Convenience function added.
+        (WebCore::ResourceLoadStatisticsStore::dataRecordsWereRemoved):
+            Convenience function added.
+        (WebCore::ResourceLoadStatisticsStore::prevalentResourceDomainsWithoutUserInteraction): Deleted.
+            Replaced by ResourceLoadStatisticsStore::topPrivatelyControlledDomainsToRemoveWebsiteDataFor().
+        * loader/ResourceLoadStatisticsStore.h:
+
 2017-05-17  Zalan Bujtas  <zalan@apple.com>
 
         Debug ASSERT: WebCore::RenderImageResource::shutdown
index f0d9937..7dd4923 100644 (file)
@@ -394,6 +394,26 @@ void ResourceLoadObserver::clearPrevalentResource(const URL& url)
     
     statistics.isPrevalentResource = false;
 }
+    
+void ResourceLoadObserver::setGrandfathered(const URL& url, bool value)
+{
+    if (url.isBlankURL() || url.isEmpty())
+        return;
+    
+    auto& statistics = m_store->ensureResourceStatisticsForPrimaryDomain(primaryDomain(url));
+    
+    statistics.grandfathered = value;
+}
+    
+bool ResourceLoadObserver::isGrandfathered(const URL& url)
+{
+    if (url.isBlankURL() || url.isEmpty())
+        return false;
+    
+    auto& statistics = m_store->ensureResourceStatisticsForPrimaryDomain(primaryDomain(url));
+    
+    return statistics.grandfathered;
+}
 
 void ResourceLoadObserver::setSubframeUnderTopFrameOrigin(const URL& subframe, const URL& topFrame)
 {
@@ -432,12 +452,22 @@ void ResourceLoadObserver::setTimeToLiveCookiePartitionFree(double seconds)
     m_store->setTimeToLiveCookiePartitionFree(seconds);
 }
 
+void ResourceLoadObserver::setMinimumTimeBetweeenDataRecordsRemoval(double seconds)
+{
+    m_store->setMinimumTimeBetweeenDataRecordsRemoval(seconds);
+}
+    
 void ResourceLoadObserver::setReducedTimestampResolution(double seconds)
 {
     if (seconds > 0)
         timestampResolution = seconds;
 }
 
+void ResourceLoadObserver::setGrandfatheringTime(double seconds)
+{
+    m_store->setMinimumTimeBetweeenDataRecordsRemoval(seconds);
+}
+    
 void ResourceLoadObserver::fireDataModificationHandler()
 {
     m_store->fireDataModificationHandler();
index e85402b..799353a 100644 (file)
@@ -57,15 +57,19 @@ public:
     WEBCORE_EXPORT void setPrevalentResource(const URL&);
     WEBCORE_EXPORT bool isPrevalentResource(const URL&);
     WEBCORE_EXPORT void clearPrevalentResource(const URL&);
-
+    WEBCORE_EXPORT void setGrandfathered(const URL&, bool value);
+    WEBCORE_EXPORT bool isGrandfathered(const URL&);
+    
     WEBCORE_EXPORT void setSubframeUnderTopFrameOrigin(const URL& subframe, const URL& topFrame);
     WEBCORE_EXPORT void setSubresourceUnderTopFrameOrigin(const URL& subresource, const URL& topFrame);
     WEBCORE_EXPORT void setSubresourceUniqueRedirectTo(const URL& subresource, const URL& hostNameRedirectedTo);
 
     WEBCORE_EXPORT void setTimeToLiveUserInteraction(double seconds);
     WEBCORE_EXPORT void setTimeToLiveCookiePartitionFree(double seconds);
+    WEBCORE_EXPORT void setMinimumTimeBetweeenDataRecordsRemoval(double seconds);
     WEBCORE_EXPORT void setReducedTimestampResolution(double seconds);
-
+    WEBCORE_EXPORT void setGrandfatheringTime(double seconds);
+    
     WEBCORE_EXPORT void fireDataModificationHandler();
     WEBCORE_EXPORT void fireShouldPartitionCookiesHandler();
     WEBCORE_EXPORT void fireShouldPartitionCookiesHandler(const Vector<String>& domainsToRemove, const Vector<String>& domainsToAdd, bool clearFirst);
index 088103b..5ab3343 100644 (file)
 
 namespace WebCore {
 
-static const auto statisticsModelVersion = 3;
-// 30 days in seconds
-static auto timeToLiveUserInteraction = 2592000;
-// 1 day in seconds
-static auto timeToLiveCookiePartitionFree = 86400;
+static const auto statisticsModelVersion = 4;
+static const auto secondsPerDay = 24 * 3600;
+static auto timeToLiveUserInteraction = 30 * secondsPerDay;
+static auto timeToLiveCookiePartitionFree = 1 * secondsPerDay;
+static auto grandfatheringTime = 3 * secondsPerDay;
+static auto minimumTimeBetweeenDataRecordsRemoval = 60;
 
 Ref<ResourceLoadStatisticsStore> ResourceLoadStatisticsStore::create()
 {
@@ -79,6 +80,7 @@ std::unique_ptr<KeyedEncoder> ResourceLoadStatisticsStore::createEncoderFromData
     auto encoder = KeyedEncoder::encoder();
 
     encoder->encodeUInt32("version", statisticsModelVersion);
+    encoder->encodeDouble("endOfGrandfatheringTimestamp", m_endOfGrandfatheringTimestamp);
     encoder->encodeObjects("browsingStatistics", m_resourceStatisticsMap.begin(), m_resourceStatisticsMap.end(), [](KeyedEncoder& encoderInner, const StatisticsValue& origin) {
         origin.value.encode(encoderInner);
     });
@@ -94,6 +96,16 @@ void ResourceLoadStatisticsStore::readDataFromDecoder(KeyedDecoder& decoder)
     unsigned version;
     if (!decoder.decodeUInt32("version", version))
         version = 1;
+
+    static const auto minimumVersionWithGrandfathering = 3;
+    if (version > minimumVersionWithGrandfathering) {
+        double endOfGrandfatheringTimestamp;
+        if (decoder.decodeDouble("endOfGrandfatheringTimestamp", endOfGrandfatheringTimestamp))
+            m_endOfGrandfatheringTimestamp = endOfGrandfatheringTimestamp;
+        else
+            m_endOfGrandfatheringTimestamp = 0;
+    }
+
     Vector<ResourceLoadStatistics> loadedStatistics;
     bool succeeded = decoder.decodeObjects("browsingStatistics", loadedStatistics, [version](KeyedDecoder& decoderInner, ResourceLoadStatistics& statistics) {
         return statistics.decode(decoderInner, version);
@@ -126,6 +138,8 @@ void ResourceLoadStatisticsStore::clearInMemoryAndPersistent()
     clearInMemory();
     if (m_writePersistentStoreHandler)
         m_writePersistentStoreHandler();
+    if (m_grandfatherExistingWebsiteDataHandler)
+        m_grandfatherExistingWebsiteDataHandler();
 }
 
 String ResourceLoadStatisticsStore::statisticsForOrigin(const String& origin)
@@ -175,6 +189,11 @@ void ResourceLoadStatisticsStore::setWritePersistentStoreCallback(std::function<
     m_writePersistentStoreHandler = WTFMove(handler);
 }
 
+void ResourceLoadStatisticsStore::setGrandfatherExistingWebsiteDataCallback(std::function<void()>&& handler)
+{
+    m_grandfatherExistingWebsiteDataHandler = WTFMove(handler);
+}
+
 void ResourceLoadStatisticsStore::fireDataModificationHandler()
 {
     if (m_dataAddedHandler)
@@ -202,10 +221,10 @@ void ResourceLoadStatisticsStore::fireShouldPartitionCookiesHandler()
             domainsToAdd.append(resourceStatistic.highLevelDomain);
         }
     }
-    
+
     if (domainsToRemove.isEmpty() && domainsToAdd.isEmpty())
         return;
-    
+
     if (m_shouldPartitionCookiesForDomainsHandler)
         m_shouldPartitionCookiesForDomainsHandler(domainsToRemove, domainsToAdd, false);
 }
@@ -214,7 +233,7 @@ void ResourceLoadStatisticsStore::fireShouldPartitionCookiesHandler(const Vector
 {
     if (domainsToRemove.isEmpty() && domainsToAdd.isEmpty())
         return;
-
+    
     if (m_shouldPartitionCookiesForDomainsHandler)
         m_shouldPartitionCookiesForDomainsHandler(domainsToRemove, domainsToAdd, clearFirst);
 
@@ -242,6 +261,18 @@ void ResourceLoadStatisticsStore::setTimeToLiveCookiePartitionFree(double second
         timeToLiveCookiePartitionFree = seconds;
 }
 
+void ResourceLoadStatisticsStore::setMinimumTimeBetweeenDataRecordsRemoval(double seconds)
+{
+    if (seconds >= 0)
+        minimumTimeBetweeenDataRecordsRemoval = seconds;
+}
+
+void ResourceLoadStatisticsStore::setGrandfatheringTime(double seconds)
+{
+    if (seconds >= 0)
+        grandfatheringTime = seconds;
+}
+
 void ResourceLoadStatisticsStore::processStatistics(std::function<void(ResourceLoadStatistics&)>&& processFunction)
 {
     for (auto& resourceStatistic : m_resourceStatisticsMap.values())
@@ -266,21 +297,65 @@ bool ResourceLoadStatisticsStore::hasHadRecentUserInteraction(ResourceLoadStatis
     return true;
 }
 
-Vector<String> ResourceLoadStatisticsStore::prevalentResourceDomainsWithoutUserInteraction()
+Vector<String> ResourceLoadStatisticsStore::topPrivatelyControlledDomainsToRemoveWebsiteDataFor()
 {
+    bool shouldCheckForGrandfathering = m_endOfGrandfatheringTimestamp > currentTime();
+    bool shouldClearGrandfathering = !shouldCheckForGrandfathering && m_endOfGrandfatheringTimestamp;
+
+    if (shouldClearGrandfathering)
+        m_endOfGrandfatheringTimestamp = 0;
+
     Vector<String> prevalentResources;
-    for (auto& resourceStatistic : m_resourceStatisticsMap.values()) {
-        if (resourceStatistic.isPrevalentResource && !hasHadRecentUserInteraction(resourceStatistic))
-            prevalentResources.append(resourceStatistic.highLevelDomain);
+    for (auto& statistic : m_resourceStatisticsMap.values()) {
+        if (statistic.isPrevalentResource
+            && !hasHadRecentUserInteraction(statistic)
+            && (!shouldCheckForGrandfathering || !statistic.grandfathered))
+            prevalentResources.append(statistic.highLevelDomain);
+
+        if (shouldClearGrandfathering && statistic.grandfathered)
+            statistic.grandfathered = false;
     }
+
     return prevalentResources;
 }
 
 void ResourceLoadStatisticsStore::updateStatisticsForRemovedDataRecords(const Vector<String>& prevalentResourceDomains)
 {
     for (auto& prevalentResourceDomain : prevalentResourceDomains) {
-        ResourceLoadStatistics& statisic = ensureResourceStatisticsForPrimaryDomain(prevalentResourceDomain);
-        ++statisic.dataRecordsRemoved;
+        ResourceLoadStatistics& statistic = ensureResourceStatisticsForPrimaryDomain(prevalentResourceDomain);
+        ++statistic.dataRecordsRemoved;
+    }
+}
+
+void ResourceLoadStatisticsStore::handleFreshStartWithEmptyOrNoStore(HashSet<String>&& topPrivatelyControlledDomainsToGrandfather)
+{
+    for (auto& topPrivatelyControlledDomain : topPrivatelyControlledDomainsToGrandfather) {
+        ResourceLoadStatistics& statistic = ensureResourceStatisticsForPrimaryDomain(topPrivatelyControlledDomain);
+        statistic.grandfathered = true;
     }
+    m_endOfGrandfatheringTimestamp = std::floor(currentTime()) + grandfatheringTime;
 }
+
+bool ResourceLoadStatisticsStore::shouldRemoveDataRecords()
+{
+    if (m_dataRecordsRemovalPending)
+        return false;
+
+    if (m_lastTimeDataRecordsWereRemoved && currentTime() < m_lastTimeDataRecordsWereRemoved + minimumTimeBetweeenDataRecordsRemoval)
+        return false;
+
+    return true;
+}
+
+void ResourceLoadStatisticsStore::dataRecordsBeingRemoved()
+{
+    m_lastTimeDataRecordsWereRemoved = currentTime();
+    m_dataRecordsRemovalPending = true;
+}
+
+void ResourceLoadStatisticsStore::dataRecordsWereRemoved()
+{
+    m_dataRecordsRemovalPending = false;
+}
+
 }
index ded1619..e1af557 100644 (file)
@@ -26,6 +26,7 @@
 #pragma once
 
 #include "ResourceLoadStatistics.h"
+#include <wtf/HashSet.h>
 
 namespace WebCore {
 
@@ -47,7 +48,7 @@ public:
     bool isEmpty() const { return m_resourceStatisticsMap.isEmpty(); }
     size_t size() const { return m_resourceStatisticsMap.size(); }
     WEBCORE_EXPORT void clearInMemory();
-    WEBCORE_EXPORT void clearInMemoryAndPersistent();
+    void clearInMemoryAndPersistent();
 
     ResourceLoadStatistics& ensureResourceStatisticsForPrimaryDomain(const String&);
     void setResourceStatisticsForPrimaryDomain(const String&, ResourceLoadStatistics&&);
@@ -60,18 +61,26 @@ public:
     WEBCORE_EXPORT void setNotificationCallback(std::function<void()>);
     WEBCORE_EXPORT void setShouldPartitionCookiesCallback(std::function<void(const Vector<String>& domainsToRemove, const Vector<String>& domainsToAdd, bool clearFirst)>&&);
     WEBCORE_EXPORT void setWritePersistentStoreCallback(std::function<void()>&&);
+    WEBCORE_EXPORT void setGrandfatherExistingWebsiteDataCallback(std::function<void()>&&);
 
     void fireDataModificationHandler();
     void setTimeToLiveUserInteraction(double seconds);
     void setTimeToLiveCookiePartitionFree(double seconds);
+    void setMinimumTimeBetweeenDataRecordsRemoval(double seconds);
+    void setGrandfatheringTime(double seconds);    
     WEBCORE_EXPORT void fireShouldPartitionCookiesHandler();
     void fireShouldPartitionCookiesHandler(const Vector<String>& domainsToRemove, const Vector<String>& domainsToAdd, bool clearFirst);
 
     WEBCORE_EXPORT void processStatistics(std::function<void(ResourceLoadStatistics&)>&&);
 
     WEBCORE_EXPORT bool hasHadRecentUserInteraction(ResourceLoadStatistics&);
-    WEBCORE_EXPORT Vector<String> prevalentResourceDomainsWithoutUserInteraction();
+    WEBCORE_EXPORT Vector<String> topPrivatelyControlledDomainsToRemoveWebsiteDataFor();
     WEBCORE_EXPORT void updateStatisticsForRemovedDataRecords(const Vector<String>& prevalentResourceDomains);
+
+    WEBCORE_EXPORT void handleFreshStartWithEmptyOrNoStore(HashSet<String>&& topPrivatelyControlledDomainsToGrandfather);
+    WEBCORE_EXPORT bool shouldRemoveDataRecords();
+    WEBCORE_EXPORT void dataRecordsBeingRemoved();
+    WEBCORE_EXPORT void dataRecordsWereRemoved();
 private:
     ResourceLoadStatisticsStore() = default;
 
@@ -79,6 +88,11 @@ private:
     std::function<void()> m_dataAddedHandler;
     std::function<void(const Vector<String>&, const Vector<String>&, bool clearFirst)> m_shouldPartitionCookiesForDomainsHandler;
     std::function<void()> m_writePersistentStoreHandler;
+    std::function<void()> m_grandfatherExistingWebsiteDataHandler;
+
+    double m_endOfGrandfatheringTimestamp { 0 };
+    double m_lastTimeDataRecordsWereRemoved { 0 };
+    bool m_dataRecordsRemovalPending { false };
 };
     
 } // namespace WebCore
index 9713996..a4bd3ba 100644 (file)
@@ -1,3 +1,68 @@
+2017-05-17  John Wilander  <wilander@apple.com>
+
+        Resource Load Statistics: Grandfather domains for existing data records
+        https://bugs.webkit.org/show_bug.cgi?id=172155
+        <rdar://problem/24913532>
+
+        Reviewed by Alex Christensen.
+
+        When WebResourceLoadStatisticsStore starts fresh it needs
+        to scan existing website data records and 'grandfather' them to
+        allow ample time to capture user interaction.
+
+        * Shared/WebPreferencesDefinitions.h:
+        * UIProcess/API/C/WKResourceLoadStatisticsManager.cpp:
+        (WKResourceLoadStatisticsManagerSetGrandfathered):
+        (WKResourceLoadStatisticsManagerIsGrandfathered):
+        (WKResourceLoadStatisticsManagerSetMinimumTimeBetweeenDataRecordsRemoval):
+        (WKResourceLoadStatisticsManagerSetGrandfatheringTime):
+            Functions for testing and configuration.
+        * UIProcess/API/C/WKResourceLoadStatisticsManager.h:
+        * UIProcess/Cocoa/WebResourceLoadStatisticsManagerCocoa.mm:
+        (WebKit::WebResourceLoadStatisticsManager::registerUserDefaultsIfNeeded):
+            Added grandfathering configuration.
+        * UIProcess/WebProcessProxy.cpp:
+        (WebKit::WebProcessProxy::topPrivatelyControlledDomainsWithWebiteData):
+            New function to get all top privately controlled domains that
+            have website data.
+        * UIProcess/WebProcessProxy.h:
+        * UIProcess/WebResourceLoadStatisticsManager.cpp:
+        (WebKit::WebResourceLoadStatisticsManager::setGrandfathered):
+        (WebKit::WebResourceLoadStatisticsManager::isGrandfathered):
+        (WebKit::WebResourceLoadStatisticsManager::setMinimumTimeBetweeenDataRecordsRemoval):
+        (WebKit::WebResourceLoadStatisticsManager::setGrandfatheringTime):
+        (WebKit::WebResourceLoadStatisticsManager::resetToConsistentState):
+            Functions for testing and configuration.
+            WebResourceLoadStatisticsManager::setMinimumTimeBetweeenDataRecordsRemoval() changed
+            as a result of WebResourceLoadStatisticsStore::setMinimumTimeBetweeenDataRecordsRemoval()
+            moving to WebCore::ResourceLoadObserver::setMinimumTimeBetweeenDataRecordsRemoval().
+        * UIProcess/WebResourceLoadStatisticsManager.h:
+        * UIProcess/WebResourceLoadStatisticsStore.cpp:
+        (WebKit::initializeDataTypesToRemove):
+        (WebKit::WebResourceLoadStatisticsStore::removeDataRecords):
+            Moved handling of pending removal into WebCore::ResourceLoadStatisticsStore since
+            that's where grandfathering happens.
+        (WebKit::WebResourceLoadStatisticsStore::registerSharedResourceLoadObserver):
+            Moved registration of write persistent store callback and reading of Cocoa defaults to
+            WebResourceLoadStatisticsStore::registerSharedResourceLoadObserver without parameters
+            so they are called for platforms without CFNETWORK_STORAGE_PARTITIONING.
+            Now includes registering WebResourceLoadStatisticsStore::grandfatherExistingWebsiteData()
+            as handler for grandfathering since it involves reading of the website data store.
+        (WebKit::WebResourceLoadStatisticsStore::grandfatherExistingWebsiteData):
+        (WebKit::WebResourceLoadStatisticsStore::readDataFromDiskIfNeeded):
+        (WebKit::WebResourceLoadStatisticsStore::setMinimumTimeBetweeenDataRecordsRemoval): Deleted.
+            Now happens in WebCore::ResourceLoadObserver::setMinimumTimeBetweeenDataRecordsRemoval().
+        * UIProcess/WebResourceLoadStatisticsStore.h:
+        * UIProcess/WebsiteData/WebsiteDataRecord.cpp:
+        (WebKit::WebsiteDataRecord::topPrivatelyControlledDomain):
+            New function to ask a WebsiteDataRecord for its top privately controlled domain.
+        * UIProcess/WebsiteData/WebsiteDataRecord.h:
+        * UIProcess/WebsiteData/WebsiteDataStore.cpp:
+        (WebKit::WebsiteDataStore::topPrivatelyControlledDomainsWithWebsiteData):
+            New function to get all top privately controlled domains that
+            have website data.
+        * UIProcess/WebsiteData/WebsiteDataStore.h:
+
 2017-05-17  Brent Fulgham  <bfulgham@apple.com>
 
         [WK2][iOS] Allow access to additional IOHID user class
index 6d876e7..c84ec98 100644 (file)
     macro(ResourceLoadStatisticsTimeToLiveUserInteraction, resourceLoadStatisticsTimeToLiveUserInteraction, Double, double, 2592000, "", "") \
     macro(ResourceLoadStatisticsTimeToLiveCookiePartitionFree, resourceLoadStatisticsTimeToLiveCookiePartitionFree, Double, double, 86400, "", "") \
     macro(ResourceLoadStatisticsReducedTimestampResolution, resourceLoadStatisticsReducedTimestampResolution, Double, double, 3600, "", "") \
-    \
+    macro(ResourceLoadStatisticsGrandfatheringTime, resourceLoadStatisticsGrandfatheringTime, Double, double, 259200, "", "") \
+\
 
 #define FOR_EACH_WEBKIT_UINT32_PREFERENCE(macro) \
     macro(FontSmoothingLevel, fontSmoothingLevel, UInt32, uint32_t, FontSmoothingLevelMedium, "", "") \
index 2e1a8fe..1c4a174 100644 (file)
@@ -56,6 +56,16 @@ bool WKResourceLoadStatisticsManagerIsHasHadUserInteraction(WKStringRef hostName
     return WebResourceLoadStatisticsManager::hasHadUserInteraction(toWTFString(hostName));
 }
 
+void WKResourceLoadStatisticsManagerSetGrandfathered(WKStringRef hostName, bool value)
+{
+    WebResourceLoadStatisticsManager::setGrandfathered(toWTFString(hostName), value);
+}
+
+bool WKResourceLoadStatisticsManagerIsGrandfathered(WKStringRef hostName)
+{
+    return WebResourceLoadStatisticsManager::isGrandfathered(toWTFString(hostName));
+}
+
 void WKResourceLoadStatisticsManagerSetSubframeUnderTopFrameOrigin(WKStringRef hostName, WKStringRef topFrameHostName)
 {
     WebResourceLoadStatisticsManager::setSubframeUnderTopFrameOrigin(toWTFString(hostName), toWTFString(topFrameHostName));
@@ -81,6 +91,16 @@ void WKResourceLoadStatisticsManagerSetTimeToLiveCookiePartitionFree(double seco
     WebResourceLoadStatisticsManager::setTimeToLiveCookiePartitionFree(seconds);
 }
 
+void WKResourceLoadStatisticsManagerSetMinimumTimeBetweeenDataRecordsRemoval(double seconds)
+{
+    WebResourceLoadStatisticsManager::setMinimumTimeBetweeenDataRecordsRemoval(seconds);
+}
+
+void WKResourceLoadStatisticsManagerSetGrandfatheringTime(double seconds)
+{
+    WebResourceLoadStatisticsManager::setGrandfatheringTime(seconds);
+}
+
 void WKResourceLoadStatisticsManagerFireDataModificationHandler()
 {
     WebResourceLoadStatisticsManager::fireDataModificationHandler();
@@ -106,11 +126,6 @@ void WKResourceLoadStatisticsManagerSetShouldClassifyResourcesBeforeDataRecordsR
     WebResourceLoadStatisticsManager::setShouldClassifyResourcesBeforeDataRecordsRemoval(value);
 }
 
-void WKResourceLoadStatisticsManagerSetMinimumTimeBetweeenDataRecordsRemoval(double seconds)
-{
-    WebResourceLoadStatisticsManager::setMinimumTimeBetweeenDataRecordsRemoval(seconds);
-}
-
 void WKResourceLoadStatisticsManagerClearInMemoryAndPersistentStore()
 {
     WebResourceLoadStatisticsManager::clearInMemoryAndPersistentStore();
index 82f2384..18a8ea9 100644 (file)
@@ -37,17 +37,20 @@ extern "C" {
     WK_EXPORT bool WKResourceLoadStatisticsManagerIsPrevalentResource(WKStringRef hostName);
     WK_EXPORT void WKResourceLoadStatisticsManagerSetHasHadUserInteraction(WKStringRef hostName, bool value);
     WK_EXPORT bool WKResourceLoadStatisticsManagerIsHasHadUserInteraction(WKStringRef hostName);
+    WK_EXPORT void WKResourceLoadStatisticsManagerSetGrandfathered(WKStringRef hostName, bool value);
+    WK_EXPORT bool WKResourceLoadStatisticsManagerIsGrandfathered(WKStringRef hostName);
     WK_EXPORT void WKResourceLoadStatisticsManagerSetSubframeUnderTopFrameOrigin(WKStringRef hostName, WKStringRef topFrameHostName);
     WK_EXPORT void WKResourceLoadStatisticsManagerSetSubresourceUnderTopFrameOrigin(WKStringRef hostName, WKStringRef topFrameHostName);
     WK_EXPORT void WKResourceLoadStatisticsManagerSetSubresourceUniqueRedirectTo(WKStringRef hostName, WKStringRef hostNameRedirectedTo);
     WK_EXPORT void WKResourceLoadStatisticsManagerSetTimeToLiveUserInteraction(double seconds);
     WK_EXPORT void WKResourceLoadStatisticsManagerSetTimeToLiveCookiePartitionFree(double seconds);
+    WK_EXPORT void WKResourceLoadStatisticsManagerSetMinimumTimeBetweeenDataRecordsRemoval(double seconds);
+    WK_EXPORT void WKResourceLoadStatisticsManagerSetGrandfatheringTime(double seconds);
     WK_EXPORT void WKResourceLoadStatisticsManagerFireDataModificationHandler();
     WK_EXPORT void WKResourceLoadStatisticsManagerFireShouldPartitionCookiesHandler();
     WK_EXPORT void WKResourceLoadStatisticsManagerFireShouldPartitionCookiesHandlerForOneDomain(WKStringRef hostName, bool value);
     WK_EXPORT void WKResourceLoadStatisticsManagerSetNotifyPagesWhenDataRecordsWereScanned(bool value);
     WK_EXPORT void WKResourceLoadStatisticsManagerSetShouldClassifyResourcesBeforeDataRecordsRemoval(bool value);
-    WK_EXPORT void WKResourceLoadStatisticsManagerSetMinimumTimeBetweeenDataRecordsRemoval(double seconds);
     WK_EXPORT void WKResourceLoadStatisticsManagerClearInMemoryAndPersistentStore();
     WK_EXPORT void WKResourceLoadStatisticsManagerClearInMemoryAndPersistentStoreModifiedSinceHours(unsigned);
     WK_EXPORT void WKResourceLoadStatisticsManagerResetToConsistentState();
index 402cf0d..d570e4f 100644 (file)
@@ -52,6 +52,10 @@ void WebResourceLoadStatisticsManager::registerUserDefaultsIfNeeded()
         double reducedTimestampResolution = [[NSUserDefaults standardUserDefaults] doubleForKey: WebPreferencesKey::resourceLoadStatisticsReducedTimestampResolutionKey()];
         if (reducedTimestampResolution > 0 && reducedTimestampResolution <= hourInSeconds)
             ResourceLoadObserver::sharedObserver().setReducedTimestampResolution(reducedTimestampResolution);
+
+        double grandfatheringTime = [[NSUserDefaults standardUserDefaults] doubleForKey: WebPreferencesKey::resourceLoadStatisticsGrandfatheringTimeKey()];
+        if (grandfatheringTime > 0 && grandfatheringTime <= 7 * dayInSeconds)
+            ResourceLoadObserver::sharedObserver().setGrandfatheringTime(grandfatheringTime);
     });
 }
 
index 830135c..4acb2e3 100644 (file)
@@ -265,6 +265,64 @@ void WebProcessProxy::deleteWebsiteDataForTopPrivatelyControlledDomainsInAllPers
     }
 }
 
+void WebProcessProxy::topPrivatelyControlledDomainsWithWebiteData(OptionSet<WebsiteDataType> dataTypes, bool shouldNotifyPage, std::function<void(HashSet<String>&&)> completionHandler)
+{
+    struct CallbackAggregator : ThreadSafeRefCounted<CallbackAggregator> {
+        explicit CallbackAggregator(std::function<void(HashSet<String>&&)> completionHandler)
+            : completionHandler(WTFMove(completionHandler))
+        {
+        }
+        
+        void addDomainsWithDeletedWebsiteData(HashSet<String>&& domains)
+        {
+            domainsWithDeletedWebsiteData.add(domains.begin(), domains.end());
+        }
+        
+        void addPendingCallback()
+        {
+            ++pendingCallbacks;
+        }
+        
+        void removePendingCallback()
+        {
+            ASSERT(pendingCallbacks);
+            --pendingCallbacks;
+            
+            callIfNeeded();
+        }
+        
+        void callIfNeeded()
+        {
+            if (!pendingCallbacks)
+                completionHandler(WTFMove(domainsWithDeletedWebsiteData));
+        }
+        
+        unsigned pendingCallbacks = 0;
+        std::function<void(HashSet<String>&&)> completionHandler;
+        HashSet<String> domainsWithDeletedWebsiteData;
+    };
+    
+    RefPtr<CallbackAggregator> callbackAggregator = adoptRef(new CallbackAggregator(WTFMove(completionHandler)));
+    
+    HashSet<WebCore::SessionID> visitedSessionIDs;
+    for (auto& page : globalPageMap()) {
+        auto& dataStore = page.value->websiteDataStore();
+        if (!dataStore.isPersistent() || visitedSessionIDs.contains(dataStore.sessionID()))
+            continue;
+        visitedSessionIDs.add(dataStore.sessionID());
+        callbackAggregator->addPendingCallback();
+        dataStore.topPrivatelyControlledDomainsWithWebsiteData(dataTypes, { }, [callbackAggregator, shouldNotifyPage, page](HashSet<String>&& domainsWithDataRecords) {
+            if (shouldNotifyPage)
+                page.value->postMessageToInjectedBundle("WebsiteDataScanForTopPrivatelyControlledDomainsFinished", nullptr);
+            
+            WTF::RunLoop::main().dispatch([callbackAggregator, domainsWithDataRecords = WTFMove(domainsWithDataRecords)]() mutable {
+                callbackAggregator->addDomainsWithDeletedWebsiteData(WTFMove(domainsWithDataRecords));
+                callbackAggregator->removePendingCallback();
+            });
+        });
+    }
+}
+
 Ref<WebPageProxy> WebProcessProxy::createWebPage(PageClient& pageClient, Ref<API::PageConfiguration>&& pageConfiguration)
 {
     uint64_t pageID = generatePageID();
index 41582a7..d074d37 100644 (file)
@@ -142,6 +142,7 @@ public:
     void deleteWebsiteData(WebCore::SessionID, OptionSet<WebsiteDataType>, std::chrono::system_clock::time_point modifiedSince, Function<void()> completionHandler);
     void deleteWebsiteDataForOrigins(WebCore::SessionID, OptionSet<WebsiteDataType>, const Vector<WebCore::SecurityOriginData>&, Function<void()> completionHandler);
     static void deleteWebsiteDataForTopPrivatelyControlledDomainsInAllPersistentDataStores(OptionSet<WebsiteDataType>, Vector<String>&& topPrivatelyControlledDomains, bool shouldNotifyPages, std::function<void(Vector<String>)> completionHandler);
+    static void topPrivatelyControlledDomainsWithWebiteData(OptionSet<WebsiteDataType> dataTypes, bool shouldNotifyPage, std::function<void(HashSet<String>&&)> completionHandler);
 
     void enableSuddenTermination();
     void disableSuddenTermination();
index 750bb70..e4bb180 100644 (file)
@@ -61,6 +61,16 @@ bool WebResourceLoadStatisticsManager::hasHadUserInteraction(const String& hostN
     return WebCore::ResourceLoadObserver::sharedObserver().hasHadUserInteraction(URL(URL(), hostName));
 }
 
+void WebResourceLoadStatisticsManager::setGrandfathered(const String& hostName, bool value)
+{
+    WebCore::ResourceLoadObserver::sharedObserver().setGrandfathered(URL(URL(), hostName), value);
+}
+
+bool WebResourceLoadStatisticsManager::isGrandfathered(const String& hostName)
+{
+    return WebCore::ResourceLoadObserver::sharedObserver().isGrandfathered(URL(URL(), hostName));
+}
+
 void WebResourceLoadStatisticsManager::setSubframeUnderTopFrameOrigin(const String& hostName, const String& topFrameHostName)
 {
     WebCore::ResourceLoadObserver::sharedObserver().setSubframeUnderTopFrameOrigin(URL(URL(), hostName), URL(URL(), topFrameHostName));
@@ -86,6 +96,16 @@ void WebResourceLoadStatisticsManager::setTimeToLiveCookiePartitionFree(double s
     WebCore::ResourceLoadObserver::sharedObserver().setTimeToLiveCookiePartitionFree(seconds);
 }
 
+void WebResourceLoadStatisticsManager::setMinimumTimeBetweeenDataRecordsRemoval(double seconds)
+{
+    WebCore::ResourceLoadObserver::sharedObserver().setMinimumTimeBetweeenDataRecordsRemoval(seconds);
+}
+
+void WebResourceLoadStatisticsManager::setGrandfatheringTime(double seconds)
+{
+    WebCore::ResourceLoadObserver::sharedObserver().setGrandfatheringTime(seconds);
+}
+
 void WebResourceLoadStatisticsManager::fireDataModificationHandler()
 {
     WebCore::ResourceLoadObserver::sharedObserver().fireDataModificationHandler();
@@ -114,11 +134,6 @@ void WebResourceLoadStatisticsManager::setShouldClassifyResourcesBeforeDataRecor
     WebResourceLoadStatisticsStore::setShouldClassifyResourcesBeforeDataRecordsRemoval(value);
 }
 
-void WebResourceLoadStatisticsManager::setMinimumTimeBetweeenDataRecordsRemoval(double seconds)
-{
-    WebResourceLoadStatisticsStore::setMinimumTimeBetweeenDataRecordsRemoval(seconds);
-}
-
 void WebResourceLoadStatisticsManager::clearInMemoryAndPersistentStore()
 {
     WebCore::ResourceLoadObserver::sharedObserver().clearInMemoryAndPersistentStore();
@@ -133,9 +148,9 @@ void WebResourceLoadStatisticsManager::resetToConsistentState()
 {
     WebCore::ResourceLoadObserver::sharedObserver().setTimeToLiveUserInteraction(2592000);
     WebCore::ResourceLoadObserver::sharedObserver().setTimeToLiveCookiePartitionFree(86400);
+    WebCore::ResourceLoadObserver::sharedObserver().setMinimumTimeBetweeenDataRecordsRemoval(60);
     WebResourceLoadStatisticsStore::setNotifyPagesWhenDataRecordsWereScanned(false);
     WebResourceLoadStatisticsStore::setShouldClassifyResourcesBeforeDataRecordsRemoval(true);
-    WebResourceLoadStatisticsStore::setMinimumTimeBetweeenDataRecordsRemoval(60);
     WebCore::ResourceLoadObserver::sharedObserver().clearInMemoryStore();
 }
     
index ddbb277..0a8c288 100644 (file)
@@ -42,18 +42,21 @@ public:
     static bool isPrevalentResource(const String& hostName);
     static void setHasHadUserInteraction(const String& hostName, bool value);
     static bool hasHadUserInteraction(const String& hostName);
+    static void setGrandfathered(const String& hostName, bool value);
+    static bool isGrandfathered(const String& hostName);
     static void setSubframeUnderTopFrameOrigin(const String& hostName, const String& topFrameHostName);
     static void setSubresourceUnderTopFrameOrigin(const String& hostName, const String& topFrameHostName);
     static void setSubresourceUniqueRedirectTo(const String& hostName, const String& hostNameRedirectedTo);
     static void setTimeToLiveUserInteraction(double seconds);
     static void setTimeToLiveCookiePartitionFree(double seconds);
+    static void setMinimumTimeBetweeenDataRecordsRemoval(double seconds);
+    static void setGrandfatheringTime(double seconds);
     static void setReducedTimestampResolution(double seconds);
     static void fireDataModificationHandler();
     static void fireShouldPartitionCookiesHandler();
     static void fireShouldPartitionCookiesHandlerForOneDomain(const String& hostName, bool value);
     static void setNotifyPagesWhenDataRecordsWereScanned(bool);
     static void setShouldClassifyResourcesBeforeDataRecordsRemoval(bool value);
-    static void setMinimumTimeBetweeenDataRecordsRemoval(double seconds);
     static void clearInMemoryAndPersistentStore();
     static void clearInMemoryAndPersistentStoreModifiedSinceHours(unsigned);
     static void resetToConsistentState();
index 7134cff..8448c96 100644 (file)
@@ -36,7 +36,6 @@
 #include <WebCore/KeyedCoding.h>
 #include <WebCore/ResourceLoadObserver.h>
 #include <WebCore/ResourceLoadStatistics.h>
-#include <wtf/CurrentTime.h>
 #include <wtf/MainThread.h>
 #include <wtf/MathExtras.h>
 #include <wtf/RunLoop.h>
@@ -46,7 +45,6 @@ using namespace WebCore;
 
 namespace WebKit {
 
-static auto minimumTimeBetweeenDataRecordsRemoval = 60;
 static OptionSet<WebKit::WebsiteDataType> dataTypesToRemove;
 static auto notifyPages = false;
 static auto shouldClassifyResourcesBeforeDataRecordsRemoval = true;
@@ -77,12 +75,6 @@ void WebResourceLoadStatisticsStore::setShouldClassifyResourcesBeforeDataRecords
     shouldClassifyResourcesBeforeDataRecordsRemoval = value;
 }
 
-void WebResourceLoadStatisticsStore::setMinimumTimeBetweeenDataRecordsRemoval(double seconds)
-{
-    if (seconds >= 0)
-        minimumTimeBetweeenDataRecordsRemoval = seconds;
-}
-
 void WebResourceLoadStatisticsStore::classifyResource(ResourceLoadStatistics& resourceStatistic)
 {
     if (!resourceStatistic.isPrevalentResource
@@ -90,46 +82,44 @@ void WebResourceLoadStatisticsStore::classifyResource(ResourceLoadStatistics& re
         resourceStatistic.isPrevalentResource = true;
 }
 
+static inline void initializeDataTypesToRemove()
+{
+    dataTypesToRemove |= WebsiteDataType::Cookies;
+    dataTypesToRemove |= WebsiteDataType::OfflineWebApplicationCache;
+    dataTypesToRemove |= WebsiteDataType::SessionStorage;
+    dataTypesToRemove |= WebsiteDataType::LocalStorage;
+    dataTypesToRemove |= WebsiteDataType::WebSQLDatabases;
+    dataTypesToRemove |= WebsiteDataType::IndexedDBDatabases;
+    dataTypesToRemove |= WebsiteDataType::MediaKeys;
+    dataTypesToRemove |= WebsiteDataType::HSTSCache;
+    dataTypesToRemove |= WebsiteDataType::SearchFieldRecentSearches;
+#if ENABLE(NETSCAPE_PLUGIN_API)
+    dataTypesToRemove |= WebsiteDataType::PlugInData;
+#endif
+#if ENABLE(MEDIA_STREAM)
+    dataTypesToRemove |= WebsiteDataType::MediaDeviceIdentifier;
+#endif
+}
+    
 void WebResourceLoadStatisticsStore::removeDataRecords()
 {
-    if (m_dataRecordsRemovalPending)
+    if (!coreStore().shouldRemoveDataRecords())
         return;
 
-    double now = currentTime();
-    if (m_lastTimeDataRecordsWereRemoved
-        && now < m_lastTimeDataRecordsWereRemoved + minimumTimeBetweeenDataRecordsRemoval)
-        return;
-
-    Vector<String> prevalentResourceDomains = coreStore().prevalentResourceDomainsWithoutUserInteraction();
+    Vector<String> prevalentResourceDomains = coreStore().topPrivatelyControlledDomainsToRemoveWebsiteDataFor();
     if (!prevalentResourceDomains.size())
         return;
     
-    m_dataRecordsRemovalPending = true;
-    m_lastTimeDataRecordsWereRemoved = now;
-
-    if (dataTypesToRemove.isEmpty()) {
-        dataTypesToRemove |= WebsiteDataType::Cookies;
-        dataTypesToRemove |= WebsiteDataType::OfflineWebApplicationCache;
-        dataTypesToRemove |= WebsiteDataType::SessionStorage;
-        dataTypesToRemove |= WebsiteDataType::LocalStorage;
-        dataTypesToRemove |= WebsiteDataType::WebSQLDatabases;
-        dataTypesToRemove |= WebsiteDataType::IndexedDBDatabases;
-        dataTypesToRemove |= WebsiteDataType::MediaKeys;
-        dataTypesToRemove |= WebsiteDataType::HSTSCache;
-        dataTypesToRemove |= WebsiteDataType::SearchFieldRecentSearches;
-#if ENABLE(NETSCAPE_PLUGIN_API)
-        dataTypesToRemove |= WebsiteDataType::PlugInData;
-#endif
-#if ENABLE(MEDIA_STREAM)
-        dataTypesToRemove |= WebsiteDataType::MediaDeviceIdentifier;
-#endif
-    }
+    coreStore().dataRecordsBeingRemoved();
+
+    if (dataTypesToRemove.isEmpty())
+        initializeDataTypesToRemove();
 
     // Switch to the main thread to get the default website data store
     RunLoop::main().dispatch([prevalentResourceDomains = WTFMove(prevalentResourceDomains), this] () mutable {
         WebProcessProxy::deleteWebsiteDataForTopPrivatelyControlledDomainsInAllPersistentDataStores(dataTypesToRemove, WTFMove(prevalentResourceDomains), notifyPages, [this](Vector<String> domainsWithDeletedWebsiteData) mutable {
             this->coreStore().updateStatisticsForRemovedDataRecords(domainsWithDeletedWebsiteData);
-            m_dataRecordsRemovalPending = false;
+            this->coreStore().dataRecordsWereRemoved();
         });
     });
 }
@@ -178,19 +168,35 @@ void WebResourceLoadStatisticsStore::registerSharedResourceLoadObserver()
             return;
         processStatisticsAndDataRecords();
     });
+    m_resourceLoadStatisticsStore->setWritePersistentStoreCallback([this]() {
+        writeStoreToDisk();
+    });
+    m_resourceLoadStatisticsStore->setGrandfatherExistingWebsiteDataCallback([this]() {
+        grandfatherExistingWebsiteData();
+    });
+#if PLATFORM(COCOA)
+    WebResourceLoadStatisticsManager::registerUserDefaultsIfNeeded();
+#endif
 }
     
 void WebResourceLoadStatisticsStore::registerSharedResourceLoadObserver(std::function<void(const Vector<String>& domainsToRemove, const Vector<String>& domainsToAdd, bool clearFirst)>&& shouldPartitionCookiesForDomainsHandler)
 {
     registerSharedResourceLoadObserver();
-#if PLATFORM(COCOA)
-    WebResourceLoadStatisticsManager::registerUserDefaultsIfNeeded();
-#endif
     m_resourceLoadStatisticsStore->setShouldPartitionCookiesCallback([shouldPartitionCookiesForDomainsHandler = WTFMove(shouldPartitionCookiesForDomainsHandler)] (const Vector<String>& domainsToRemove, const Vector<String>& domainsToAdd, bool clearFirst) {
         shouldPartitionCookiesForDomainsHandler(domainsToRemove, domainsToAdd, clearFirst);
     });
-    m_resourceLoadStatisticsStore->setWritePersistentStoreCallback([this]() {
-        writeStoreToDisk();
+}
+
+void WebResourceLoadStatisticsStore::grandfatherExistingWebsiteData()
+{
+    if (dataTypesToRemove.isEmpty())
+        initializeDataTypesToRemove();
+    
+    // Switch to the main thread to get the default website data store
+    RunLoop::main().dispatch([this] () mutable {
+        WebProcessProxy::topPrivatelyControlledDomainsWithWebiteData(dataTypesToRemove, notifyPages, [this](HashSet<String>&& topPrivatelyControlledDomainsWithWebsiteData) mutable {
+            this->coreStore().handleFreshStartWithEmptyOrNoStore(WTFMove(topPrivatelyControlledDomainsWithWebsiteData));
+        });
     });
 }
 
@@ -203,10 +209,15 @@ void WebResourceLoadStatisticsStore::readDataFromDiskIfNeeded()
         coreStore().clearInMemory();
 
         auto decoder = createDecoderFromDisk("full_browsing_session");
-        if (!decoder)
+        if (!decoder) {
+            grandfatherExistingWebsiteData();
             return;
+        }
 
         coreStore().readDataFromDecoder(*decoder);
+
+        if (coreStore().isEmpty())
+            grandfatherExistingWebsiteData();
     });
 }
 
index f0820d0..74f11ba 100644 (file)
@@ -89,6 +89,8 @@ private:
     // IPC::MessageReceiver
     void didReceiveMessage(IPC::Connection&, IPC::Decoder&) override;
 
+    void grandfatherExistingWebsiteData();
+
     void writeStoreToDisk();
     void writeEncoderToDisk(WebCore::KeyedEncoder&, const String& label) const;
     std::unique_ptr<WebCore::KeyedDecoder> createDecoderFromDisk(const String& label) const;
@@ -103,9 +105,6 @@ private:
     Ref<WTF::WorkQueue> m_statisticsQueue;
     String m_statisticsStoragePath;
     bool m_resourceLoadStatisticsEnabled { false };
-
-    double m_lastTimeDataRecordsWereRemoved { 0 };
-    bool m_dataRecordsRemovalPending { false };
 };
 
 } // namespace WebKit
index 385ea0d..f99e055 100644 (file)
@@ -137,4 +137,12 @@ bool WebsiteDataRecord::matchesTopPrivatelyControlledDomain(const String& topPri
     return false;
 }
 
+String WebsiteDataRecord::topPrivatelyControlledDomain()
+{
+    if (types.contains(WebsiteDataType::Cookies))
+        return WebCore::topPrivatelyControlledDomain(cookieHostNames.takeAny());
+
+    return WebCore::topPrivatelyControlledDomain(origins.takeAny().securityOrigin().get().host());
+}
+
 }
index f7b26b7..db2695a 100644 (file)
@@ -70,6 +70,7 @@ struct WebsiteDataRecord {
 #endif
     
     bool matchesTopPrivatelyControlledDomain(const String&) const;
+    String topPrivatelyControlledDomain();
 };
 
 }
index 13a7ad9..1d60f2a 100644 (file)
@@ -519,6 +519,16 @@ void WebsiteDataStore::fetchDataForTopPrivatelyControlledDomains(OptionSet<Websi
     });
 }
     
+void WebsiteDataStore::topPrivatelyControlledDomainsWithWebsiteData(OptionSet<WebsiteDataType> dataTypes, OptionSet<WebsiteDataFetchOption> fetchOptions, std::function<void(HashSet<String>&&)> completionHandler)
+{
+    fetchData(dataTypes, fetchOptions, [completionHandler, this](auto&& existingDataRecords) {
+        HashSet<String> domainsWithDataRecords;
+        for (auto&& dataRecord : existingDataRecords)
+            domainsWithDataRecords.add(dataRecord.topPrivatelyControlledDomain());
+        completionHandler(WTFMove(domainsWithDataRecords));
+    });
+}
+
 static ProcessAccessType computeNetworkProcessAccessTypeForDataRemoval(OptionSet<WebsiteDataType> dataTypes, bool isNonPersistentStore)
 {
     ProcessAccessType processAccessType = ProcessAccessType::None;
index 7be6435..ab56992 100644 (file)
@@ -95,6 +95,7 @@ public:
 
     void fetchData(OptionSet<WebsiteDataType>, OptionSet<WebsiteDataFetchOption>, std::function<void (Vector<WebsiteDataRecord>)> completionHandler);
     void fetchDataForTopPrivatelyControlledDomains(OptionSet<WebsiteDataType>, OptionSet<WebsiteDataFetchOption>, Vector<String>&& topPrivatelyControlledDomains, std::function<void(Vector<WebsiteDataRecord>&&, Vector<String>&&)> completionHandler);
+    void topPrivatelyControlledDomainsWithWebsiteData(OptionSet<WebsiteDataType> dataTypes, OptionSet<WebsiteDataFetchOption> fetchOptions, std::function<void(HashSet<String>&&)> completionHandler);
     void removeData(OptionSet<WebsiteDataType>, std::chrono::system_clock::time_point modifiedSince, std::function<void ()> completionHandler);
     void removeData(OptionSet<WebsiteDataType>, const Vector<WebsiteDataRecord>&, std::function<void ()> completionHandler);
     void removeDataForTopPrivatelyControlledDomains(OptionSet<WebsiteDataType>, OptionSet<WebsiteDataFetchOption>, Vector<String>&& topPrivatelyControlledDomains, std::function<void(Vector<String>)> completionHandler);
index a11f2dc..5411875 100644 (file)
@@ -1,3 +1,31 @@
+2017-05-17  John Wilander  <wilander@apple.com>
+
+        Resource Load Statistics: Grandfather domains for existing data records
+        https://bugs.webkit.org/show_bug.cgi?id=172155
+        <rdar://problem/24913532>
+
+        Reviewed by Alex Christensen.
+
+        Adds test infrastructure needed for the added functionality.
+
+        * WebKitTestRunner/InjectedBundle/Bindings/TestRunner.idl:
+        * WebKitTestRunner/InjectedBundle/InjectedBundle.cpp:
+        (WTR::InjectedBundle::didReceiveMessageToPage):
+        * WebKitTestRunner/InjectedBundle/TestRunner.cpp:
+        (WTR::TestRunner::setStatisticsGrandfathered):
+        (WTR::TestRunner::isStatisticsGrandfathered):
+        (WTR::TestRunner::installStatisticsDidScanDataRecordsCallback):
+        (WTR::TestRunner::statisticsDidScanDataRecordsCallback):
+        (WTR::TestRunner::setStatisticsGrandfatheringTime):
+        * WebKitTestRunner/InjectedBundle/TestRunner.h:
+        * WebKitTestRunner/TestController.cpp:
+        (WTR::TestController::setStatisticsGrandfathered):
+        (WTR::TestController::isStatisticsGrandfathered):
+        (WTR::TestController::setStatisticsGrandfatheringTime):
+        * WebKitTestRunner/TestController.h:
+        * WebKitTestRunner/TestInvocation.cpp:
+        (WTR::TestInvocation::didReceiveSynchronousMessageFromInjectedBundle):
+
 2017-05-17  Alex Christensen  <achristensen@webkit.org>
 
         Fix ios-simulator API test after r216977
index d9ad33d..abbb868 100644 (file)
@@ -251,10 +251,13 @@ interface TestRunner {
 
     // Resource Load Statistics
     void installStatisticsDidModifyDataRecordsCallback(object callback);
+    void installStatisticsDidScanDataRecordsCallback(object callback);
     void setStatisticsPrevalentResource(DOMString hostName, boolean value);
     boolean isStatisticsPrevalentResource(DOMString hostName);
     void setStatisticsHasHadUserInteraction(DOMString hostName, boolean value);
     boolean isStatisticsHasHadUserInteraction(DOMString hostName);
+    void setStatisticsGrandfathered(DOMString hostName, boolean value);
+    boolean isStatisticsGrandfathered(DOMString hostName);
     void setStatisticsSubframeUnderTopFrameOrigin(DOMString hostName, DOMString topFrameHostName);
     void setStatisticsSubresourceUnderTopFrameOrigin(DOMString hostName, DOMString topFrameHostName);
     void setStatisticsSubresourceUniqueRedirectTo(DOMString hostName, DOMString hostNameRedirectedTo);
@@ -266,6 +269,7 @@ interface TestRunner {
     void setStatisticsNotifyPagesWhenDataRecordsWereScanned(boolean value);
     void setStatisticsShouldClassifyResourcesBeforeDataRecordsRemoval(boolean value);
     void setStatisticsMinimumTimeBetweeenDataRecordsRemoval(double seconds);
+    void setStatisticsGrandfatheringTime(double seconds);
     void statisticsClearInMemoryAndPersistentStore();
     void statisticsClearInMemoryAndPersistentStoreModifiedSinceHours(unsigned long hours);
     void statisticsResetToConsistentState();
index f9f5ebc..716c97c 100644 (file)
@@ -271,6 +271,11 @@ void InjectedBundle::didReceiveMessageToPage(WKBundlePageRef page, WKStringRef m
         return;
     }
 
+    if (WKStringIsEqualToUTF8CString(messageName, "WebsiteDataScanForTopPrivatelyControlledDomainsFinished")) {
+        m_testRunner->statisticsDidScanDataRecordsCallback();
+        return;
+    }
+
     WKRetainPtr<WKStringRef> errorMessageName(AdoptWK, WKStringCreateWithUTF8CString("Error"));
     WKRetainPtr<WKStringRef> errorMessageBody(AdoptWK, WKStringCreateWithUTF8CString("Unknown"));
     WKBundlePagePostMessage(page, errorMessageName.get(), errorMessageBody.get());
index 1e883c5..1c390a7 100644 (file)
@@ -641,6 +641,7 @@ enum {
     DidEndSwipeCallbackID,
     DidRemoveSwipeSnapshotCallbackID,
     StatisticsDidModifyDataRecordsCallbackID,
+    StatisticsDidScanDataRecordsCallbackID,
     FirstUIScriptCallbackID = 100
 };
 
@@ -1245,6 +1246,42 @@ bool TestRunner::isStatisticsHasHadUserInteraction(JSStringRef hostName)
     return WKBooleanGetValue(static_cast<WKBooleanRef>(returnData));
 }
 
+void TestRunner::setStatisticsGrandfathered(JSStringRef hostName, bool value)
+{
+    Vector<WKRetainPtr<WKStringRef>> keys;
+    Vector<WKRetainPtr<WKTypeRef>> values;
+    
+    keys.append({ AdoptWK, WKStringCreateWithUTF8CString("HostName") });
+    values.append({ AdoptWK, WKStringCreateWithJSString(hostName) });
+    
+    keys.append({ AdoptWK, WKStringCreateWithUTF8CString("Value") });
+    values.append({ AdoptWK, WKBooleanCreate(value) });
+    
+    Vector<WKStringRef> rawKeys;
+    Vector<WKTypeRef> rawValues;
+    rawKeys.resize(keys.size());
+    rawValues.resize(values.size());
+    
+    for (size_t i = 0; i < keys.size(); ++i) {
+        rawKeys[i] = keys[i].get();
+        rawValues[i] = values[i].get();
+    }
+    
+    WKRetainPtr<WKStringRef> messageName(AdoptWK, WKStringCreateWithUTF8CString("SetStatisticsGrandfathered"));
+    WKRetainPtr<WKDictionaryRef> messageBody(AdoptWK, WKDictionaryCreate(rawKeys.data(), rawValues.data(), rawKeys.size()));
+    
+    WKBundlePostSynchronousMessage(InjectedBundle::singleton().bundle(), messageName.get(), messageBody.get(), nullptr);
+}
+    
+bool TestRunner::isStatisticsGrandfathered(JSStringRef hostName)
+{
+    WKRetainPtr<WKStringRef> messageName(AdoptWK, WKStringCreateWithUTF8CString("IsStatisticsGrandfathered"));
+    WKRetainPtr<WKStringRef> messageBody(AdoptWK, WKStringCreateWithJSString(hostName));
+    WKTypeRef returnData = 0;
+    WKBundlePagePostSynchronousMessageForTesting(InjectedBundle::singleton().page()->page(), messageName.get(), messageBody.get(), &returnData);
+    return WKBooleanGetValue(static_cast<WKBooleanRef>(returnData));
+}
+
 void TestRunner::setStatisticsSubframeUnderTopFrameOrigin(JSStringRef hostName, JSStringRef topFrameHostName)
 {
     Vector<WKRetainPtr<WKStringRef>> keys;
@@ -1341,6 +1378,16 @@ void TestRunner::statisticsDidModifyDataRecordsCallback()
     callTestRunnerCallback(StatisticsDidModifyDataRecordsCallbackID);
 }
 
+void TestRunner::installStatisticsDidScanDataRecordsCallback(JSValueRef callback)
+{
+    cacheTestRunnerCallback(StatisticsDidScanDataRecordsCallbackID, callback);
+}
+
+void TestRunner::statisticsDidScanDataRecordsCallback()
+{
+    callTestRunnerCallback(StatisticsDidScanDataRecordsCallbackID);
+}
+
 void TestRunner::statisticsFireDataModificationHandler()
 {
     WKRetainPtr<WKStringRef> messageName(AdoptWK, WKStringCreateWithUTF8CString("StatisticsFireDataModificationHandler"));
@@ -1399,6 +1446,13 @@ void TestRunner::setStatisticsMinimumTimeBetweeenDataRecordsRemoval(double secon
     WKBundlePostSynchronousMessage(InjectedBundle::singleton().bundle(), messageName.get(), messageBody.get(), nullptr);
 }
 
+void TestRunner::setStatisticsGrandfatheringTime(double seconds)
+{
+    WKRetainPtr<WKStringRef> messageName(AdoptWK, WKStringCreateWithUTF8CString("SetStatisticsGrandfatheringTime"));
+    WKRetainPtr<WKDoubleRef> messageBody(AdoptWK, WKDoubleCreate(seconds));
+    WKBundlePostSynchronousMessage(InjectedBundle::singleton().bundle(), messageName.get(), messageBody.get(), nullptr);
+}
+
 void TestRunner::statisticsClearInMemoryAndPersistentStore()
 {
     WKRetainPtr<WKStringRef> messageName(AdoptWK, WKStringCreateWithUTF8CString("StatisticsClearInMemoryAndPersistentStore"));
index 979081a..024d799 100644 (file)
@@ -348,7 +348,9 @@ public:
     
     // Resource Load Statistics
     void installStatisticsDidModifyDataRecordsCallback(JSValueRef callback);
+    void installStatisticsDidScanDataRecordsCallback(JSValueRef callback);
     void statisticsDidModifyDataRecordsCallback();
+    void statisticsDidScanDataRecordsCallback();
     void statisticsFireDataModificationHandler();
     void statisticsFireShouldPartitionCookiesHandler();
     void statisticsFireShouldPartitionCookiesHandlerForOneDomain(JSStringRef hostName, bool value);
@@ -356,6 +358,8 @@ public:
     bool isStatisticsPrevalentResource(JSStringRef hostName);
     void setStatisticsHasHadUserInteraction(JSStringRef hostName, bool value);
     bool isStatisticsHasHadUserInteraction(JSStringRef hostName);
+    void setStatisticsGrandfathered(JSStringRef hostName, bool value);
+    bool isStatisticsGrandfathered(JSStringRef hostName);
     void setStatisticsSubframeUnderTopFrameOrigin(JSStringRef hostName, JSStringRef topFrameHostName);
     void setStatisticsSubresourceUnderTopFrameOrigin(JSStringRef hostName, JSStringRef topFrameHostName);
     void setStatisticsSubresourceUniqueRedirectTo(JSStringRef hostName, JSStringRef hostNameRedirectedTo);
@@ -364,6 +368,7 @@ public:
     void setStatisticsNotifyPagesWhenDataRecordsWereScanned(bool);
     void setStatisticsShouldClassifyResourcesBeforeDataRecordsRemoval(bool);
     void setStatisticsMinimumTimeBetweeenDataRecordsRemoval(double);
+    void setStatisticsGrandfatheringTime(double seconds);
     void statisticsClearInMemoryAndPersistentStore();
     void statisticsClearInMemoryAndPersistentStoreModifiedSinceHours(unsigned hours);
     void statisticsResetToConsistentState();
index afdd9a8..ced1d15 100644 (file)
@@ -2208,6 +2208,16 @@ bool TestController::isStatisticsHasHadUserInteraction(WKStringRef hostName)
     return WKResourceLoadStatisticsManagerIsHasHadUserInteraction(hostName);
 }
 
+void TestController::setStatisticsGrandfathered(WKStringRef hostName, bool value)
+{
+    WKResourceLoadStatisticsManagerSetGrandfathered(hostName, value);
+}
+
+bool TestController::isStatisticsGrandfathered(WKStringRef hostName)
+{
+    return WKResourceLoadStatisticsManagerIsGrandfathered(hostName);
+}
+
 void TestController::setStatisticsSubframeUnderTopFrameOrigin(WKStringRef hostName, WKStringRef topFrameHostName)
 {
     WKResourceLoadStatisticsManagerSetSubframeUnderTopFrameOrigin(hostName, topFrameHostName);
@@ -2263,6 +2273,11 @@ void TestController::setStatisticsMinimumTimeBetweeenDataRecordsRemoval(double s
     WKResourceLoadStatisticsManagerSetMinimumTimeBetweeenDataRecordsRemoval(seconds);
 }
 
+void TestController::setStatisticsGrandfatheringTime(double seconds)
+{
+    WKResourceLoadStatisticsManagerSetGrandfatheringTime(seconds);
+}
+
 void TestController::statisticsClearInMemoryAndPersistentStore()
 {
     WKResourceLoadStatisticsManagerClearInMemoryAndPersistentStore();
index b740487..652a558 100644 (file)
@@ -153,6 +153,8 @@ public:
     bool isStatisticsPrevalentResource(WKStringRef hostName);
     void setStatisticsHasHadUserInteraction(WKStringRef hostName, bool value);
     bool isStatisticsHasHadUserInteraction(WKStringRef hostName);
+    void setStatisticsGrandfathered(WKStringRef hostName, bool value);
+    bool isStatisticsGrandfathered(WKStringRef hostName);
     void setStatisticsSubframeUnderTopFrameOrigin(WKStringRef hostName, WKStringRef topFrameHostName);
     void setStatisticsSubresourceUnderTopFrameOrigin(WKStringRef hostName, WKStringRef topFrameHostName);
     void setStatisticsSubresourceUniqueRedirectTo(WKStringRef hostName, WKStringRef hostNameRedirectedTo);
@@ -164,6 +166,7 @@ public:
     void setStatisticsNotifyPagesWhenDataRecordsWereScanned(bool);
     void setStatisticsShouldClassifyResourcesBeforeDataRecordsRemoval(bool);
     void setStatisticsMinimumTimeBetweeenDataRecordsRemoval(double);
+    void setStatisticsGrandfatheringTime(double seconds);
     void statisticsClearInMemoryAndPersistentStore();
     void statisticsClearInMemoryAndPersistentStoreModifiedSinceHours(unsigned);
     void statisticsResetToConsistentState();
index a774692..bedaa89 100644 (file)
@@ -953,6 +953,29 @@ WKRetainPtr<WKTypeRef> TestInvocation::didReceiveSynchronousMessageFromInjectedB
         return result;
     }
     
+    if (WKStringIsEqualToUTF8CString(messageName, "SetStatisticsGrandfathered")) {
+        ASSERT(WKGetTypeID(messageBody) == WKDictionaryGetTypeID());
+        
+        WKDictionaryRef messageBodyDictionary = static_cast<WKDictionaryRef>(messageBody);
+        WKRetainPtr<WKStringRef> hostNameKey(AdoptWK, WKStringCreateWithUTF8CString("HostName"));
+        WKRetainPtr<WKStringRef> valueKey(AdoptWK, WKStringCreateWithUTF8CString("Value"));
+        
+        WKStringRef hostName = static_cast<WKStringRef>(WKDictionaryGetItemForKey(messageBodyDictionary, hostNameKey.get()));
+        WKBooleanRef value = static_cast<WKBooleanRef>(WKDictionaryGetItemForKey(messageBodyDictionary, valueKey.get()));
+        
+        TestController::singleton().setStatisticsGrandfathered(hostName, WKBooleanGetValue(value));
+        return nullptr;
+    }
+    
+    if (WKStringIsEqualToUTF8CString(messageName, "IsStatisticsGrandfathered")) {
+        ASSERT(WKGetTypeID(messageBody) == WKStringGetTypeID());
+        
+        WKStringRef hostName = static_cast<WKStringRef>(messageBody);
+        bool isGrandfathered = TestController::singleton().isStatisticsGrandfathered(hostName);
+        WKRetainPtr<WKTypeRef> result(AdoptWK, WKBooleanCreate(isGrandfathered));
+        return result;
+    }
+    
     if (WKStringIsEqualToUTF8CString(messageName, "SetStatisticsSubframeUnderTopFrameOrigin")) {
         ASSERT(WKGetTypeID(messageBody) == WKDictionaryGetTypeID());
         
@@ -1054,6 +1077,13 @@ WKRetainPtr<WKTypeRef> TestInvocation::didReceiveSynchronousMessageFromInjectedB
         return nullptr;
     }
     
+    if (WKStringIsEqualToUTF8CString(messageName, "SetStatisticsGrandfatheringTime")) {
+        ASSERT(WKGetTypeID(messageBody) == WKDoubleGetTypeID());
+        WKDoubleRef seconds = static_cast<WKDoubleRef>(messageBody);
+        TestController::singleton().setStatisticsGrandfatheringTime(WKDoubleGetValue(seconds));
+        return nullptr;
+    }
+    
     if (WKStringIsEqualToUTF8CString(messageName, "StatisticsClearInMemoryAndPersistentStore")) {
         TestController::singleton().statisticsClearInMemoryAndPersistentStore();
         return nullptr;