Resource Load Statistics (experimental): Block all third-party cookies on websites without prior user interaction
https://bugs.webkit.org/show_bug.cgi?id=203017
<rdar://problem/56262708>

Reviewed by Alex Christensen.

Source/WebCore:

NetworkStorageSession::shouldBlockCookies() now checks if the first-party website has
received user interaction and if not, blocks all third-party cookies, regardless of the
status of those third-party domains (prevalent or not).

Bundled cleanup task: Remove the two month old feature flag for NSURLSession switching.

Tests: http/tests/resourceLoadStatistics/third-party-cookie-blocking-on-sites-without-user-interaction-database.html
       http/tests/resourceLoadStatistics/third-party-cookie-blocking-on-sites-without-user-interaction.html

* page/RuntimeEnabledFeatures.h:
(WebCore::RuntimeEnabledFeatures::setIsITPSessionSwitchingEnabled): Deleted.
(WebCore::RuntimeEnabledFeatures::isITPSessionSwitchingEnabled const): Deleted.
* page/Settings.yaml:
* platform/network/NetworkStorageSession.cpp:
(WebCore::NetworkStorageSession::hasHadUserInteractionAsFirstParty const):
(WebCore::NetworkStorageSession::shouldBlockCookies const):
(WebCore::NetworkStorageSession::setDomainsWithUserInteractionAsFirstParty):
* platform/network/NetworkStorageSession.h:
(WebCore::NetworkStorageSession::setIsThirdPartyCookieBlockingOnSitesWithoutUserInteractionEnabled):

Source/WebKit:

This enhancement is off by default and controlled by an internal feature flag.

The functional change is in WebCore::NetworkStorageSession::shouldBlockCookies() which is
called from:
- NetworkDataTaskCocoa::NetworkDataTaskCocoa()
- NetworkDataTaskCocoa::willPerformHTTPRedirection

shouldBlockCookies() now checks if the first-party website has received user interaction
and if not, blocks all third-party cookies, regardless of the status of those third-party
domains (prevalent or not).

The changes to ResourceLoadStatisticsDatabaseStore and ResourceLoadStatisticsMemoryStore
are about communicating which domains have received user interaction (prevalent or not)
to the network storage session so that it can enforce the new restriction.

The C API change and piping through the WebsiteDataStore is test infrastructure.

Bundled cleanup task: Remove the two month old feature flag for NSURLSession switching.

* NetworkProcess/Classifier/ResourceLoadStatisticsDatabaseStore.cpp:
(WebKit::CompletionHandler<void):
* NetworkProcess/Classifier/ResourceLoadStatisticsDatabaseStore.h:
* NetworkProcess/Classifier/ResourceLoadStatisticsMemoryStore.cpp:
(WebKit::ResourceLoadStatisticsMemoryStore::clear):
(WebKit::ResourceLoadStatisticsMemoryStore::updateCookieBlocking):
* NetworkProcess/Classifier/WebResourceLoadStatisticsStore.cpp:
(WebKit::WebResourceLoadStatisticsStore::callUpdatePrevalentDomainsToBlockCookiesForHandler):
* NetworkProcess/Classifier/WebResourceLoadStatisticsStore.h:
(WebKit::RegistrableDomainsToBlockCookiesFor::isolatedCopy const):
* NetworkProcess/NetworkProcess.cpp:
(WebKit::NetworkProcess::initializeNetworkProcess):
(WebKit::NetworkProcess::setShouldBlockThirdPartyCookiesForTesting):
* NetworkProcess/NetworkProcess.messages.in:
* NetworkProcess/NetworkSession.cpp:
(WebKit::NetworkSession::NetworkSession):
* NetworkProcess/NetworkSession.h:
(WebKit::NetworkSession::setResourceLoadStatisticsLogTestingEvent):
(WebKit::NetworkSession::shouldIsolateSessionsForPrevalentTopFrames const): Deleted.
* NetworkProcess/NetworkSessionCreationParameters.cpp:
(WebKit::NetworkSessionCreationParameters::encode const):
(WebKit::NetworkSessionCreationParameters::decode):
* NetworkProcess/NetworkSessionCreationParameters.h:
* NetworkProcess/cocoa/NetworkDataTaskCocoa.mm:
(WebKit::NetworkDataTaskCocoa::NetworkDataTaskCocoa):
* NetworkProcess/cocoa/NetworkSessionCocoa.mm:
(WebKit::NetworkSessionCocoa::NetworkSessionCocoa):
* Shared/WebPreferences.yaml:
* UIProcess/API/C/WKWebsiteDataStoreRef.cpp:
(WKWebsiteDataStoreSetResourceLoadStatisticsShouldBlockThirdPartyCookiesForTesting):
(WKWebsiteDataStoreStatisticsResetToConsistentState):
* UIProcess/API/C/WKWebsiteDataStoreRef.h:
* UIProcess/Network/NetworkProcessProxy.cpp:
(WebKit::NetworkProcessProxy::setShouldBlockThirdPartyCookiesForTesting):
* UIProcess/Network/NetworkProcessProxy.h:
* UIProcess/WebsiteData/Cocoa/WebsiteDataStoreCocoa.mm:
(WebKit::WebsiteDataStore::parameters):
* UIProcess/WebsiteData/WebsiteDataStore.cpp:
(WebKit::WebsiteDataStore::setResourceLoadStatisticsShouldBlockThirdPartyCookiesForTesting):
* UIProcess/WebsiteData/WebsiteDataStore.h:

Tools:

These changes are test infrastructure to be able to toggle the function
through testRunner.setStatisticsShouldBlockThirdPartyCookies().

* WebKitTestRunner/InjectedBundle/Bindings/TestRunner.idl:
* WebKitTestRunner/InjectedBundle/InjectedBundle.cpp:
(WTR::InjectedBundle::didReceiveMessageToPage):
* WebKitTestRunner/InjectedBundle/TestRunner.cpp:
(WTR::TestRunner::setStatisticsShouldBlockThirdPartyCookies):
(WTR::TestRunner::statisticsCallDidSetShouldBlockThirdPartyCookiesCallback):
* WebKitTestRunner/InjectedBundle/TestRunner.h:
* WebKitTestRunner/TestController.cpp:
(WTR::TestController::setStatisticsShouldBlockThirdPartyCookies):
* WebKitTestRunner/TestController.h:
* WebKitTestRunner/TestInvocation.cpp:
(WTR::TestInvocation::didReceiveSynchronousMessageFromInjectedBundle):
(WTR::TestInvocation::didSetShouldBlockThirdPartyCookies):
* WebKitTestRunner/TestInvocation.h:

LayoutTests:

* http/tests/resourceLoadStatistics/third-party-cookie-blocking-on-sites-without-user-interaction-database-expected.txt: Added.
* http/tests/resourceLoadStatistics/third-party-cookie-blocking-on-sites-without-user-interaction-database.html: Added.
* http/tests/resourceLoadStatistics/third-party-cookie-blocking-on-sites-without-user-interaction-expected.txt: Added.
* http/tests/resourceLoadStatistics/third-party-cookie-blocking-on-sites-without-user-interaction.html: Added.


git-svn-id: http://svn.webkit.org/repository/webkit/trunk@251213 268f45cc-cd09-0410-ab3c-d52691b4dbfc
diff --git a/LayoutTests/ChangeLog b/LayoutTests/ChangeLog
index cd9e1c8..791daa6 100644
--- a/LayoutTests/ChangeLog
+++ b/LayoutTests/ChangeLog
@@ -1,3 +1,16 @@
+2019-10-16  John Wilander  <wilander@apple.com>
+
+        Resource Load Statistics (experimental): Block all third-party cookies on websites without prior user interaction
+        https://bugs.webkit.org/show_bug.cgi?id=203017
+        <rdar://problem/56262708>
+
+        Reviewed by Alex Christensen.
+
+        * http/tests/resourceLoadStatistics/third-party-cookie-blocking-on-sites-without-user-interaction-database-expected.txt: Added.
+        * http/tests/resourceLoadStatistics/third-party-cookie-blocking-on-sites-without-user-interaction-database.html: Added.
+        * http/tests/resourceLoadStatistics/third-party-cookie-blocking-on-sites-without-user-interaction-expected.txt: Added.
+        * http/tests/resourceLoadStatistics/third-party-cookie-blocking-on-sites-without-user-interaction.html: Added.
+
 2019-10-16  Russell Epstein  <repstein@apple.com>
 
         Correct TestExpectations from r251119.
diff --git a/LayoutTests/http/tests/resourceLoadStatistics/third-party-cookie-blocking-on-sites-without-user-interaction-database-expected.txt b/LayoutTests/http/tests/resourceLoadStatistics/third-party-cookie-blocking-on-sites-without-user-interaction-database-expected.txt
new file mode 100644
index 0000000..a7fed3a
--- /dev/null
+++ b/LayoutTests/http/tests/resourceLoadStatistics/third-party-cookie-blocking-on-sites-without-user-interaction-database-expected.txt
@@ -0,0 +1,25 @@
+Tests that all third-party cookies are blocked under websites that have not received user interaction.
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+PASS successfullyParsed is true
+
+TEST COMPLETE
+  
+
+--------
+Frame: '<!--frame1-->'
+--------
+Before user interaction, should receive no cookies.
+Did not receive cookie named 'firstPartyCookie'.
+Did not receive cookie named 'partitionedCookie'.
+Client-side document.cookie:
+
+--------
+Frame: '<!--frame2-->'
+--------
+After user interaction, should receive first-party cookie.
+Received cookie named 'firstPartyCookie'.
+Did not receive cookie named 'partitionedCookie'.
+Client-side document.cookie: firstPartyCookie=value
diff --git a/LayoutTests/http/tests/resourceLoadStatistics/third-party-cookie-blocking-on-sites-without-user-interaction-database.html b/LayoutTests/http/tests/resourceLoadStatistics/third-party-cookie-blocking-on-sites-without-user-interaction-database.html
new file mode 100644
index 0000000..fc9233c
--- /dev/null
+++ b/LayoutTests/http/tests/resourceLoadStatistics/third-party-cookie-blocking-on-sites-without-user-interaction-database.html
@@ -0,0 +1,78 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+    <meta charset="UTF-8">
+    <script src="/js-test-resources/js-test.js"></script>
+    <script src="resources/util.js"></script>
+</head>
+<body>
+<script>
+    description("Tests that all third-party cookies are blocked under websites that have not received user interaction.");
+    jsTestIsAsync = true;
+
+    const partitionHost = "127.0.0.1:8000";
+    const partitionOrigin = "http://" + partitionHost;
+    const thirdPartyOrigin = "http://localhost:8000";
+    const resourcePath = "/resourceLoadStatistics/resources";
+    const thirdPartyBaseUrl = thirdPartyOrigin + resourcePath;
+    const firstPartyCookieName = "firstPartyCookie";
+    const subPathToSetFirstPartyCookie = "/set-cookie.php?name=" + firstPartyCookieName + "&value=value";
+    const partitionedCookieName = "partitionedCookie";
+    const subPathToSetPartitionedCookie = "/set-cookie.php?name=" + partitionedCookieName + "&value=value";
+    const returnUrl = partitionOrigin + "/resourceLoadStatistics/third-party-cookie-blocking-on-sites-without-user-interaction.html";
+    const subPathToGetCookies = "/get-cookies.php?name1=" + firstPartyCookieName + "&name2=" + partitionedCookieName;
+
+    function openIframe(url, onLoadHandler) {
+        const element = document.createElement("iframe");
+        element.src = url;
+        if (onLoadHandler) {
+            element.onload = onLoadHandler;
+        }
+        document.body.appendChild(element);
+    }
+
+    function runTest() {
+        switch (document.location.hash) {
+            case "#step1":
+                // Set first-party cookie for localhost.
+                document.location.href = thirdPartyBaseUrl + subPathToSetFirstPartyCookie + "#" + returnUrl + "#step2";
+                break;
+            case "#step2":
+                document.location.hash = "step3";
+                // Check that the cookie is blocked for localhost under 127.0.0.1 even though localhost is not prevalent.
+                openIframe(thirdPartyBaseUrl + subPathToGetCookies + "&message=Before user interaction, should receive no cookies.", runTest);
+                break;
+            case "#step3":
+                document.location.hash = "step4";
+                // Produce user interaction for the first-party to allow third-party cookie access.
+                testRunner.setStatisticsHasHadUserInteraction(partitionOrigin, true, function() {
+                    testRunner.statisticsUpdateCookieBlocking(function() {
+                        // Check that the cookie is no longer blocked for localhost under 127.0.0.1.
+                        openIframe(thirdPartyBaseUrl + subPathToGetCookies +  "&message=After user interaction, should receive first-party cookie.", runTest);
+                    });
+                });
+                break;
+            case "#step4":
+                testRunner.setUseITPDatabase(false);
+                testRunner.setStatisticsShouldBlockThirdPartyCookies(false, function() {
+                    setEnableFeature(false, finishJSTest);
+                });
+                break;
+        }
+    }
+
+    if (document.location.hash === "") {
+        testRunner.setUseITPDatabase(true);
+        setEnableFeature(true, function () {
+            if (testRunner.isStatisticsPrevalentResource(thirdPartyOrigin))
+                testFailed("Localhost was classified as prevalent resource before the test starts.");
+            testRunner.dumpChildFramesAsText();
+            document.location.hash = "step1";
+            testRunner.setStatisticsShouldBlockThirdPartyCookies(true, runTest);
+        });
+    } else {
+        runTest();
+    }
+</script>
+</body>
+</html>
diff --git a/LayoutTests/http/tests/resourceLoadStatistics/third-party-cookie-blocking-on-sites-without-user-interaction-expected.txt b/LayoutTests/http/tests/resourceLoadStatistics/third-party-cookie-blocking-on-sites-without-user-interaction-expected.txt
new file mode 100644
index 0000000..a7fed3a
--- /dev/null
+++ b/LayoutTests/http/tests/resourceLoadStatistics/third-party-cookie-blocking-on-sites-without-user-interaction-expected.txt
@@ -0,0 +1,25 @@
+Tests that all third-party cookies are blocked under websites that have not received user interaction.
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+PASS successfullyParsed is true
+
+TEST COMPLETE
+  
+
+--------
+Frame: '<!--frame1-->'
+--------
+Before user interaction, should receive no cookies.
+Did not receive cookie named 'firstPartyCookie'.
+Did not receive cookie named 'partitionedCookie'.
+Client-side document.cookie:
+
+--------
+Frame: '<!--frame2-->'
+--------
+After user interaction, should receive first-party cookie.
+Received cookie named 'firstPartyCookie'.
+Did not receive cookie named 'partitionedCookie'.
+Client-side document.cookie: firstPartyCookie=value
diff --git a/LayoutTests/http/tests/resourceLoadStatistics/third-party-cookie-blocking-on-sites-without-user-interaction.html b/LayoutTests/http/tests/resourceLoadStatistics/third-party-cookie-blocking-on-sites-without-user-interaction.html
new file mode 100644
index 0000000..cdf7323
--- /dev/null
+++ b/LayoutTests/http/tests/resourceLoadStatistics/third-party-cookie-blocking-on-sites-without-user-interaction.html
@@ -0,0 +1,76 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+    <meta charset="UTF-8">
+    <script src="/js-test-resources/js-test.js"></script>
+    <script src="resources/util.js"></script>
+</head>
+<body>
+<script>
+    description("Tests that all third-party cookies are blocked under websites that have not received user interaction.");
+    jsTestIsAsync = true;
+
+    const partitionHost = "127.0.0.1:8000";
+    const partitionOrigin = "http://" + partitionHost;
+    const thirdPartyOrigin = "http://localhost:8000";
+    const resourcePath = "/resourceLoadStatistics/resources";
+    const thirdPartyBaseUrl = thirdPartyOrigin + resourcePath;
+    const firstPartyCookieName = "firstPartyCookie";
+    const subPathToSetFirstPartyCookie = "/set-cookie.php?name=" + firstPartyCookieName + "&value=value";
+    const partitionedCookieName = "partitionedCookie";
+    const subPathToSetPartitionedCookie = "/set-cookie.php?name=" + partitionedCookieName + "&value=value";
+    const returnUrl = partitionOrigin + "/resourceLoadStatistics/third-party-cookie-blocking-on-sites-without-user-interaction.html";
+    const subPathToGetCookies = "/get-cookies.php?name1=" + firstPartyCookieName + "&name2=" + partitionedCookieName;
+
+    function openIframe(url, onLoadHandler) {
+        const element = document.createElement("iframe");
+        element.src = url;
+        if (onLoadHandler) {
+            element.onload = onLoadHandler;
+        }
+        document.body.appendChild(element);
+    }
+
+    function runTest() {
+        switch (document.location.hash) {
+            case "#step1":
+                // Set first-party cookie for localhost.
+                document.location.href = thirdPartyBaseUrl + subPathToSetFirstPartyCookie + "#" + returnUrl + "#step2";
+                break;
+            case "#step2":
+                document.location.hash = "step3";
+                // Check that the cookie is blocked for localhost under 127.0.0.1 even though localhost is not prevalent.
+                openIframe(thirdPartyBaseUrl + subPathToGetCookies + "&message=Before user interaction, should receive no cookies.", runTest);
+                break;
+            case "#step3":
+                document.location.hash = "step4";
+                // Produce user interaction for the first-party to allow third-party cookie access.
+                testRunner.setStatisticsHasHadUserInteraction(partitionOrigin, true, function() {
+                    testRunner.statisticsUpdateCookieBlocking(function() {
+                        // Check that the cookie is no longer blocked for localhost under 127.0.0.1.
+                        openIframe(thirdPartyBaseUrl + subPathToGetCookies +  "&message=After user interaction, should receive first-party cookie.", runTest);
+                    });
+                });
+                break;
+            case "#step4":
+                testRunner.setStatisticsShouldBlockThirdPartyCookies(false, function() {
+                    setEnableFeature(false, finishJSTest);
+                });
+                break;
+        }
+    }
+
+    if (document.location.hash === "") {
+        setEnableFeature(true, function () {
+            if (testRunner.isStatisticsPrevalentResource(thirdPartyOrigin))
+                testFailed("Localhost was classified as prevalent resource before the test starts.");
+            testRunner.dumpChildFramesAsText();
+            document.location.hash = "step1";
+            testRunner.setStatisticsShouldBlockThirdPartyCookies(true, runTest);
+        });
+    } else {
+        runTest();
+    }
+</script>
+</body>
+</html>
diff --git a/Source/WebCore/ChangeLog b/Source/WebCore/ChangeLog
index 2f24c2a..5dfd194 100644
--- a/Source/WebCore/ChangeLog
+++ b/Source/WebCore/ChangeLog
@@ -1,3 +1,31 @@
+2019-10-16  John Wilander  <wilander@apple.com>
+
+        Resource Load Statistics (experimental): Block all third-party cookies on websites without prior user interaction
+        https://bugs.webkit.org/show_bug.cgi?id=203017
+        <rdar://problem/56262708>
+
+        Reviewed by Alex Christensen.
+
+        NetworkStorageSession::shouldBlockCookies() now checks if the first-party website has
+        received user interaction and if not, blocks all third-party cookies, regardless of the
+        status of those third-party domains (prevalent or not).
+
+        Bundled cleanup task: Remove the two month old feature flag for NSURLSession switching.
+
+        Tests: http/tests/resourceLoadStatistics/third-party-cookie-blocking-on-sites-without-user-interaction-database.html
+               http/tests/resourceLoadStatistics/third-party-cookie-blocking-on-sites-without-user-interaction.html
+
+        * page/RuntimeEnabledFeatures.h:
+        (WebCore::RuntimeEnabledFeatures::setIsITPSessionSwitchingEnabled): Deleted.
+        (WebCore::RuntimeEnabledFeatures::isITPSessionSwitchingEnabled const): Deleted.
+        * page/Settings.yaml:
+        * platform/network/NetworkStorageSession.cpp:
+        (WebCore::NetworkStorageSession::hasHadUserInteractionAsFirstParty const):
+        (WebCore::NetworkStorageSession::shouldBlockCookies const):
+        (WebCore::NetworkStorageSession::setDomainsWithUserInteractionAsFirstParty):
+        * platform/network/NetworkStorageSession.h:
+        (WebCore::NetworkStorageSession::setIsThirdPartyCookieBlockingOnSitesWithoutUserInteractionEnabled):
+
 2019-10-16  Zalan Bujtas  <zalan@apple.com>
 
         [LFC][IFC] Add style and text content to Display::Run
diff --git a/Source/WebCore/page/RuntimeEnabledFeatures.h b/Source/WebCore/page/RuntimeEnabledFeatures.h
index 3bbbb14..3d4c56b 100644
--- a/Source/WebCore/page/RuntimeEnabledFeatures.h
+++ b/Source/WebCore/page/RuntimeEnabledFeatures.h
@@ -147,9 +147,6 @@
     void setIsITPDatabaseEnabled(bool isEnabled) { m_isITPDatabaseEnabled = isEnabled; }
     bool isITPDatabaseEnabled() const { return m_isITPDatabaseEnabled; }
 
-    void setIsITPSessionSwitchingEnabled(bool isEnabled) { m_isITPSessionSwitchingEnabled = isEnabled; }
-    bool isITPSessionSwitchingEnabled() const { return m_isITPSessionSwitchingEnabled; }
-
     void setRestrictedHTTPResponseAccess(bool isEnabled) { m_isRestrictedHTTPResponseAccess = isEnabled; }
     bool restrictedHTTPResponseAccess() const { return m_isRestrictedHTTPResponseAccess; }
 
@@ -420,7 +417,6 @@
     bool m_accessibilityObjectModelEnabled { false };
     bool m_ariaReflectionEnabled { true };
     bool m_itpDebugMode { false };
-    bool m_isITPSessionSwitchingEnabled { true };
     bool m_isRestrictedHTTPResponseAccess { true };
     bool m_crossOriginResourcePolicyEnabled { true };
     bool m_isWebGLCompressedTextureASTCSupportEnabled { false };
diff --git a/Source/WebCore/page/Settings.yaml b/Source/WebCore/page/Settings.yaml
index 47f0ed6..88c3789 100644
--- a/Source/WebCore/page/Settings.yaml
+++ b/Source/WebCore/page/Settings.yaml
@@ -869,8 +869,8 @@
   initial: true
   inspectorOverride: true
 
-isITPSessionSwitchingEnabled:
-  initial: true
+isThirdPartyCookieBlockingOnSitesWithoutUserInteractionEnabled:
+  initial: false
 
 isLoggedInAPIEnabled:
   initial: false
diff --git a/Source/WebCore/platform/network/NetworkStorageSession.cpp b/Source/WebCore/platform/network/NetworkStorageSession.cpp
index 31d575f..2c4556a 100644
--- a/Source/WebCore/platform/network/NetworkStorageSession.cpp
+++ b/Source/WebCore/platform/network/NetworkStorageSession.cpp
@@ -78,6 +78,14 @@
     return m_registrableDomainsToBlockButKeepCookiesFor.contains(registrableDomain);
 }
 
+bool NetworkStorageSession::hasHadUserInteractionAsFirstParty(const RegistrableDomain& registrableDomain) const
+{
+    if (registrableDomain.isEmpty())
+        return false;
+
+    return m_registrableDomainsWithUserInteractionAsFirstParty.contains(registrableDomain);
+}
+
 bool NetworkStorageSession::shouldBlockCookies(const ResourceRequest& request, Optional<FrameIdentifier> frameID, Optional<PageIdentifier> pageID) const
 {
     return shouldBlockCookies(request.firstPartyForCookies(), request.url(), frameID, pageID);
@@ -99,6 +107,9 @@
     if (pageID && hasStorageAccess(resourceDomain, firstPartyDomain, frameID, pageID.value()))
         return false;
 
+    if (m_isThirdPartyCookieBlockingOnSitesWithoutUserInteractionEnabled && !hasHadUserInteractionAsFirstParty(firstPartyDomain))
+        return true;
+
     return shouldBlockThirdPartyCookies(resourceDomain);
 }
 
@@ -127,6 +138,12 @@
     m_registrableDomainsToBlockButKeepCookiesFor.add(domains.begin(), domains.end());
 }
 
+void NetworkStorageSession::setDomainsWithUserInteractionAsFirstParty(const Vector<RegistrableDomain>& domains)
+{
+    m_registrableDomainsWithUserInteractionAsFirstParty.clear();
+    m_registrableDomainsWithUserInteractionAsFirstParty.add(domains.begin(), domains.end());
+}
+
 void NetworkStorageSession::removePrevalentDomains(const Vector<RegistrableDomain>& domains)
 {
     for (auto& domain : domains) {
diff --git a/Source/WebCore/platform/network/NetworkStorageSession.h b/Source/WebCore/platform/network/NetworkStorageSession.h
index 5bb8437..2038b88 100644
--- a/Source/WebCore/platform/network/NetworkStorageSession.h
+++ b/Source/WebCore/platform/network/NetworkStorageSession.h
@@ -147,8 +147,10 @@
     WEBCORE_EXPORT bool shouldBlockCookies(const URL& firstPartyForCookies, const URL& resource, Optional<FrameIdentifier>, Optional<PageIdentifier>) const;
     WEBCORE_EXPORT bool shouldBlockThirdPartyCookies(const RegistrableDomain&) const;
     WEBCORE_EXPORT bool shouldBlockThirdPartyCookiesButKeepFirstPartyCookiesFor(const RegistrableDomain&) const;
+    WEBCORE_EXPORT bool hasHadUserInteractionAsFirstParty(const RegistrableDomain&) const;
     WEBCORE_EXPORT void setPrevalentDomainsToBlockAndDeleteCookiesFor(const Vector<RegistrableDomain>&);
     WEBCORE_EXPORT void setPrevalentDomainsToBlockButKeepCookiesFor(const Vector<RegistrableDomain>&);
+    WEBCORE_EXPORT void setDomainsWithUserInteractionAsFirstParty(const Vector<RegistrableDomain>&);
     WEBCORE_EXPORT void setAgeCapForClientSideCookies(Optional<Seconds>);
     WEBCORE_EXPORT void removePrevalentDomains(const Vector<RegistrableDomain>& domains);
     WEBCORE_EXPORT bool hasStorageAccess(const RegistrableDomain& resourceDomain, const RegistrableDomain& firstPartyDomain, Optional<FrameIdentifier>, PageIdentifier) const;
@@ -162,6 +164,7 @@
     WEBCORE_EXPORT Optional<Seconds> maxAgeCacheCap(const ResourceRequest&);
     WEBCORE_EXPORT void didCommitCrossSiteLoadWithDataTransferFromPrevalentResource(const RegistrableDomain& toDomain, PageIdentifier);
     WEBCORE_EXPORT void resetCrossSiteLoadsWithLinkDecorationForTesting();
+    void setIsThirdPartyCookieBlockingOnSitesWithoutUserInteractionEnabled(bool enabled) { m_isThirdPartyCookieBlockingOnSitesWithoutUserInteractionEnabled = enabled; }
 #endif
 
 private:
@@ -188,6 +191,7 @@
     Optional<Seconds> clientSideCookieCap(const RegistrableDomain& firstParty, Optional<PageIdentifier>) const;
     HashSet<RegistrableDomain> m_registrableDomainsToBlockAndDeleteCookiesFor;
     HashSet<RegistrableDomain> m_registrableDomainsToBlockButKeepCookiesFor;
+    HashSet<RegistrableDomain> m_registrableDomainsWithUserInteractionAsFirstParty;
     HashMap<PageIdentifier, HashMap<FrameIdentifier, RegistrableDomain>> m_framesGrantedStorageAccess;
     HashMap<PageIdentifier, HashMap<RegistrableDomain, RegistrableDomain>> m_pagesGrantedStorageAccess;
     Optional<Seconds> m_cacheMaxAgeCapForPrevalentResources { };
@@ -195,6 +199,7 @@
     Optional<Seconds> m_ageCapForClientSideCookiesShort { };
     HashMap<WebCore::PageIdentifier, RegistrableDomain> m_navigatedToWithLinkDecorationByPrevalentResource;
     bool m_navigationWithLinkDecorationTestMode = false;
+    bool m_isThirdPartyCookieBlockingOnSitesWithoutUserInteractionEnabled = false;
 #endif
 
 #if PLATFORM(COCOA)
diff --git a/Source/WebKit/ChangeLog b/Source/WebKit/ChangeLog
index a4a3466..3ed827b 100644
--- a/Source/WebKit/ChangeLog
+++ b/Source/WebKit/ChangeLog
@@ -1,3 +1,71 @@
+2019-10-16  John Wilander  <wilander@apple.com>
+
+        Resource Load Statistics (experimental): Block all third-party cookies on websites without prior user interaction
+        https://bugs.webkit.org/show_bug.cgi?id=203017
+        <rdar://problem/56262708>
+
+        Reviewed by Alex Christensen.
+
+        This enhancement is off by default and controlled by an internal feature flag.
+
+        The functional change is in WebCore::NetworkStorageSession::shouldBlockCookies() which is
+        called from:
+        - NetworkDataTaskCocoa::NetworkDataTaskCocoa()
+        - NetworkDataTaskCocoa::willPerformHTTPRedirection
+
+        shouldBlockCookies() now checks if the first-party website has received user interaction
+        and if not, blocks all third-party cookies, regardless of the status of those third-party
+        domains (prevalent or not).
+
+        The changes to ResourceLoadStatisticsDatabaseStore and ResourceLoadStatisticsMemoryStore
+        are about communicating which domains have received user interaction (prevalent or not)
+        to the network storage session so that it can enforce the new restriction.
+
+        The C API change and piping through the WebsiteDataStore is test infrastructure.
+
+        Bundled cleanup task: Remove the two month old feature flag for NSURLSession switching.
+
+        * NetworkProcess/Classifier/ResourceLoadStatisticsDatabaseStore.cpp:
+        (WebKit::CompletionHandler<void):
+        * NetworkProcess/Classifier/ResourceLoadStatisticsDatabaseStore.h:
+        * NetworkProcess/Classifier/ResourceLoadStatisticsMemoryStore.cpp:
+        (WebKit::ResourceLoadStatisticsMemoryStore::clear):
+        (WebKit::ResourceLoadStatisticsMemoryStore::updateCookieBlocking):
+        * NetworkProcess/Classifier/WebResourceLoadStatisticsStore.cpp:
+        (WebKit::WebResourceLoadStatisticsStore::callUpdatePrevalentDomainsToBlockCookiesForHandler):
+        * NetworkProcess/Classifier/WebResourceLoadStatisticsStore.h:
+        (WebKit::RegistrableDomainsToBlockCookiesFor::isolatedCopy const):
+        * NetworkProcess/NetworkProcess.cpp:
+        (WebKit::NetworkProcess::initializeNetworkProcess):
+        (WebKit::NetworkProcess::setShouldBlockThirdPartyCookiesForTesting):
+        * NetworkProcess/NetworkProcess.messages.in:
+        * NetworkProcess/NetworkSession.cpp:
+        (WebKit::NetworkSession::NetworkSession):
+        * NetworkProcess/NetworkSession.h:
+        (WebKit::NetworkSession::setResourceLoadStatisticsLogTestingEvent):
+        (WebKit::NetworkSession::shouldIsolateSessionsForPrevalentTopFrames const): Deleted.
+        * NetworkProcess/NetworkSessionCreationParameters.cpp:
+        (WebKit::NetworkSessionCreationParameters::encode const):
+        (WebKit::NetworkSessionCreationParameters::decode):
+        * NetworkProcess/NetworkSessionCreationParameters.h:
+        * NetworkProcess/cocoa/NetworkDataTaskCocoa.mm:
+        (WebKit::NetworkDataTaskCocoa::NetworkDataTaskCocoa):
+        * NetworkProcess/cocoa/NetworkSessionCocoa.mm:
+        (WebKit::NetworkSessionCocoa::NetworkSessionCocoa):
+        * Shared/WebPreferences.yaml:
+        * UIProcess/API/C/WKWebsiteDataStoreRef.cpp:
+        (WKWebsiteDataStoreSetResourceLoadStatisticsShouldBlockThirdPartyCookiesForTesting):
+        (WKWebsiteDataStoreStatisticsResetToConsistentState):
+        * UIProcess/API/C/WKWebsiteDataStoreRef.h:
+        * UIProcess/Network/NetworkProcessProxy.cpp:
+        (WebKit::NetworkProcessProxy::setShouldBlockThirdPartyCookiesForTesting):
+        * UIProcess/Network/NetworkProcessProxy.h:
+        * UIProcess/WebsiteData/Cocoa/WebsiteDataStoreCocoa.mm:
+        (WebKit::WebsiteDataStore::parameters):
+        * UIProcess/WebsiteData/WebsiteDataStore.cpp:
+        (WebKit::WebsiteDataStore::setResourceLoadStatisticsShouldBlockThirdPartyCookiesForTesting):
+        * UIProcess/WebsiteData/WebsiteDataStore.h:
+
 2019-10-16  Kate Cheney  <katherine_cheney@apple.com>
 
         WKWebsiteDataStoreStatisticsResetToConsistentState() should call store.setUseITPDatabase(false)
diff --git a/Source/WebKit/NetworkProcess/Classifier/ResourceLoadStatisticsDatabaseStore.cpp b/Source/WebKit/NetworkProcess/Classifier/ResourceLoadStatisticsDatabaseStore.cpp
index 1db541e..eb94abb 100644
--- a/Source/WebKit/NetworkProcess/Classifier/ResourceLoadStatisticsDatabaseStore.cpp
+++ b/Source/WebKit/NetworkProcess/Classifier/ResourceLoadStatisticsDatabaseStore.cpp
@@ -1680,7 +1680,7 @@
     removeAllStorageAccess([callbackAggregator = callbackAggregator.copyRef()] { });
 
     auto registrableDomainsToBlockAndDeleteCookiesFor = ensurePrevalentResourcesForDebugMode();
-    RegistrableDomainsToBlockCookiesFor domainsToBlock { registrableDomainsToBlockAndDeleteCookiesFor, { } };
+    RegistrableDomainsToBlockCookiesFor domainsToBlock { registrableDomainsToBlockAndDeleteCookiesFor, { }, { } };
     updateCookieBlockingForDomains(domainsToBlock, [callbackAggregator = callbackAggregator.copyRef()] { });
 }
 
@@ -1752,19 +1752,35 @@
     return results;
 }
 
+Vector<RegistrableDomain> ResourceLoadStatisticsDatabaseStore::domainsWithUserInteractionAsFirstParty() const
+{
+    ASSERT(!RunLoop::isMain());
+
+    Vector<RegistrableDomain> results;
+    SQLiteStatement statement(m_database, "SELECT registrableDomain FROM ObservedDomains WHERE hadUserInteraction = 1"_s);
+    if (statement.prepare() != SQLITE_OK)
+        return results;
+    
+    while (statement.step() == SQLITE_ROW)
+        results.append(RegistrableDomain::uncheckedCreateFromRegistrableDomainString(statement.getColumnText(0)));
+
+    return results;
+}
+
 void ResourceLoadStatisticsDatabaseStore::updateCookieBlocking(CompletionHandler<void()>&& completionHandler)
 {
     ASSERT(!RunLoop::isMain());
 
     auto domainsToBlockAndDeleteCookiesFor = this->domainsToBlockAndDeleteCookiesFor();
     auto domainsToBlockButKeepCookiesFor = this->domainsToBlockButKeepCookiesFor();
+    auto domainsWithUserInteractionAsFirstParty = this->domainsWithUserInteractionAsFirstParty();
 
-    if (domainsToBlockAndDeleteCookiesFor.isEmpty() && domainsToBlockButKeepCookiesFor.isEmpty()) {
+    if (domainsToBlockAndDeleteCookiesFor.isEmpty() && domainsToBlockButKeepCookiesFor.isEmpty() && domainsWithUserInteractionAsFirstParty.isEmpty()) {
         completionHandler();
         return;
     }
 
-    RegistrableDomainsToBlockCookiesFor domainsToBlock { domainsToBlockAndDeleteCookiesFor, domainsToBlockButKeepCookiesFor };
+    RegistrableDomainsToBlockCookiesFor domainsToBlock { domainsToBlockAndDeleteCookiesFor, domainsToBlockButKeepCookiesFor, domainsWithUserInteractionAsFirstParty };
 
     if (debugLoggingEnabled() && !domainsToBlockAndDeleteCookiesFor.isEmpty() && !domainsToBlockButKeepCookiesFor.isEmpty())
         debugLogDomainsInBatches("block", domainsToBlock);
diff --git a/Source/WebKit/NetworkProcess/Classifier/ResourceLoadStatisticsDatabaseStore.h b/Source/WebKit/NetworkProcess/Classifier/ResourceLoadStatisticsDatabaseStore.h
index beed834..0226325 100644
--- a/Source/WebKit/NetworkProcess/Classifier/ResourceLoadStatisticsDatabaseStore.h
+++ b/Source/WebKit/NetworkProcess/Classifier/ResourceLoadStatisticsDatabaseStore.h
@@ -155,6 +155,7 @@
     void setUserInteraction(const RegistrableDomain&, bool hadUserInteraction, WallTime);
     Vector<RegistrableDomain> domainsToBlockAndDeleteCookiesFor() const;
     Vector<RegistrableDomain> domainsToBlockButKeepCookiesFor() const;
+    Vector<RegistrableDomain> domainsWithUserInteractionAsFirstParty() const;
 
     struct PrevalentDomainData {
         unsigned domainID;
diff --git a/Source/WebKit/NetworkProcess/Classifier/ResourceLoadStatisticsMemoryStore.cpp b/Source/WebKit/NetworkProcess/Classifier/ResourceLoadStatisticsMemoryStore.cpp
index 6589f6b..f92f4fd 100644
--- a/Source/WebKit/NetworkProcess/Classifier/ResourceLoadStatisticsMemoryStore.cpp
+++ b/Source/WebKit/NetworkProcess/Classifier/ResourceLoadStatisticsMemoryStore.cpp
@@ -697,7 +697,7 @@
     removeAllStorageAccess([callbackAggregator = callbackAggregator.copyRef()] { });
 
     auto registrableDomainsToBlockAndDeleteCookiesFor = ensurePrevalentResourcesForDebugMode();
-    RegistrableDomainsToBlockCookiesFor domainsToBlock { registrableDomainsToBlockAndDeleteCookiesFor, { } };
+    RegistrableDomainsToBlockCookiesFor domainsToBlock { registrableDomainsToBlockAndDeleteCookiesFor, { }, { } };
     updateCookieBlockingForDomains(domainsToBlock, [callbackAggregator = callbackAggregator.copyRef()] { });
 }
 
@@ -735,21 +735,22 @@
 
     Vector<RegistrableDomain> domainsToBlockAndDeleteCookiesFor;
     Vector<RegistrableDomain> domainsToBlockButKeepCookiesFor;
+    Vector<RegistrableDomain> domainsWithUserInteractionAsFirstParty;
     for (auto& resourceStatistic : m_resourceStatisticsMap.values()) {
-        if (resourceStatistic.isPrevalentResource) {
-            if (hasHadUnexpiredRecentUserInteraction(resourceStatistic, OperatingDatesWindow::Long))
-                domainsToBlockButKeepCookiesFor.append(resourceStatistic.registrableDomain);
-            else
-                domainsToBlockAndDeleteCookiesFor.append(resourceStatistic.registrableDomain);
-        }
+        if (hasHadUnexpiredRecentUserInteraction(resourceStatistic, OperatingDatesWindow::Long)) {
+            if (resourceStatistic.isPrevalentResource)
+                domainsToBlockButKeepCookiesFor.append(resourceStatistic.registrableDomain.isolatedCopy());
+            domainsWithUserInteractionAsFirstParty.append(resourceStatistic.registrableDomain);
+        } else if (resourceStatistic.isPrevalentResource)
+            domainsToBlockAndDeleteCookiesFor.append(resourceStatistic.registrableDomain);
     }
 
-    if (domainsToBlockAndDeleteCookiesFor.isEmpty() && domainsToBlockButKeepCookiesFor.isEmpty() && !debugModeEnabled()) {
+    if (domainsToBlockAndDeleteCookiesFor.isEmpty() && domainsToBlockButKeepCookiesFor.isEmpty() && domainsWithUserInteractionAsFirstParty.isEmpty() && !debugModeEnabled()) {
         completionHandler();
         return;
     }
 
-    RegistrableDomainsToBlockCookiesFor domainsToBlock { domainsToBlockAndDeleteCookiesFor, domainsToBlockButKeepCookiesFor };
+    RegistrableDomainsToBlockCookiesFor domainsToBlock { domainsToBlockAndDeleteCookiesFor, domainsToBlockButKeepCookiesFor, domainsWithUserInteractionAsFirstParty };
 
     if (debugLoggingEnabled() && !domainsToBlockAndDeleteCookiesFor.isEmpty() && !domainsToBlockButKeepCookiesFor.isEmpty())
         debugLogDomainsInBatches("block", domainsToBlock);
diff --git a/Source/WebKit/NetworkProcess/Classifier/WebResourceLoadStatisticsStore.cpp b/Source/WebKit/NetworkProcess/Classifier/WebResourceLoadStatisticsStore.cpp
index e08ab5d..7b8adec 100644
--- a/Source/WebKit/NetworkProcess/Classifier/WebResourceLoadStatisticsStore.cpp
+++ b/Source/WebKit/NetworkProcess/Classifier/WebResourceLoadStatisticsStore.cpp
@@ -965,6 +965,7 @@
         if (auto* storageSession = m_networkSession->networkStorageSession()) {
             storageSession->setPrevalentDomainsToBlockAndDeleteCookiesFor(domainsToBlock.domainsToBlockAndDeleteCookiesFor);
             storageSession->setPrevalentDomainsToBlockButKeepCookiesFor(domainsToBlock.domainsToBlockButKeepCookiesFor);
+            storageSession->setDomainsWithUserInteractionAsFirstParty(domainsToBlock.domainsWithUserInteractionAsFirstParty);
         }
     }
 
diff --git a/Source/WebKit/NetworkProcess/Classifier/WebResourceLoadStatisticsStore.h b/Source/WebKit/NetworkProcess/Classifier/WebResourceLoadStatisticsStore.h
index 4164ab6..38b7c0e 100644
--- a/Source/WebKit/NetworkProcess/Classifier/WebResourceLoadStatisticsStore.h
+++ b/Source/WebKit/NetworkProcess/Classifier/WebResourceLoadStatisticsStore.h
@@ -65,7 +65,6 @@
 enum class ShouldGrandfatherStatistics : bool;
 enum class ShouldIncludeLocalhost : bool { No, Yes };
 enum class EnableResourceLoadStatisticsDebugMode : bool { No, Yes };
-enum class EnableResourceLoadStatisticsNSURLSessionSwitching : bool { No, Yes };
 enum class WebsiteDataToRemove : uint8_t {
     All,
     AllButHttpOnlyCookies,
@@ -74,7 +73,8 @@
 struct RegistrableDomainsToBlockCookiesFor {
     Vector<WebCore::RegistrableDomain> domainsToBlockAndDeleteCookiesFor;
     Vector<WebCore::RegistrableDomain> domainsToBlockButKeepCookiesFor;
-    RegistrableDomainsToBlockCookiesFor isolatedCopy() const { return { domainsToBlockAndDeleteCookiesFor.isolatedCopy(), domainsToBlockButKeepCookiesFor.isolatedCopy() }; }
+    Vector<WebCore::RegistrableDomain> domainsWithUserInteractionAsFirstParty;
+    RegistrableDomainsToBlockCookiesFor isolatedCopy() const { return { domainsToBlockAndDeleteCookiesFor.isolatedCopy(), domainsToBlockButKeepCookiesFor.isolatedCopy(), domainsWithUserInteractionAsFirstParty.isolatedCopy() }; }
 };
 
 class WebResourceLoadStatisticsStore final : public ThreadSafeRefCounted<WebResourceLoadStatisticsStore, WTF::DestructionThread::Main> {
diff --git a/Source/WebKit/NetworkProcess/NetworkProcess.cpp b/Source/WebKit/NetworkProcess/NetworkProcess.cpp
index 810e748..d5575ce 100644
--- a/Source/WebKit/NetworkProcess/NetworkProcess.cpp
+++ b/Source/WebKit/NetworkProcess/NetworkProcess.cpp
@@ -1258,6 +1258,15 @@
     });
     completionHandler();
 }
+
+void NetworkProcess::setShouldBlockThirdPartyCookiesForTesting(PAL::SessionID sessionID, bool enabled, CompletionHandler<void()>&& completionHandler)
+{
+    if (auto* networkStorageSession = storageSession(sessionID))
+        networkStorageSession->setIsThirdPartyCookieBlockingOnSitesWithoutUserInteractionEnabled(enabled);
+    else
+        ASSERT_NOT_REACHED();
+    completionHandler();
+}
 #endif // ENABLE(RESOURCE_LOAD_STATISTICS)
 
 bool NetworkProcess::sessionIsControlledByAutomation(PAL::SessionID sessionID) const
diff --git a/Source/WebKit/NetworkProcess/NetworkProcess.h b/Source/WebKit/NetworkProcess/NetworkProcess.h
index c89df84..b5db56f 100644
--- a/Source/WebKit/NetworkProcess/NetworkProcess.h
+++ b/Source/WebKit/NetworkProcess/NetworkProcess.h
@@ -266,6 +266,7 @@
     void hasIsolatedSession(PAL::SessionID, const WebCore::RegistrableDomain&, CompletionHandler<void(bool)>&&) const;
     bool isITPDatabaseEnabled() const { return m_isITPDatabaseEnabled; }
     void setShouldDowngradeReferrerForTesting(bool, CompletionHandler<void()>&&);
+    void setShouldBlockThirdPartyCookiesForTesting(PAL::SessionID, bool, CompletionHandler<void()>&&);
 #endif
 
     using CacheStorageRootPathCallback = CompletionHandler<void(String&&)>;
diff --git a/Source/WebKit/NetworkProcess/NetworkProcess.messages.in b/Source/WebKit/NetworkProcess/NetworkProcess.messages.in
index c35d113..41fea0df 100644
--- a/Source/WebKit/NetworkProcess/NetworkProcess.messages.in
+++ b/Source/WebKit/NetworkProcess/NetworkProcess.messages.in
@@ -137,6 +137,7 @@
     DeleteCookiesForTesting(PAL::SessionID sessionID, WebCore::RegistrableDomain domain, bool includeHttpOnlyCookies) -> () Async
     HasIsolatedSession(PAL::SessionID sessionID, WebCore::RegistrableDomain domain) -> (bool hasIsolatedSession) Async
     SetShouldDowngradeReferrerForTesting(bool enabled) -> () Async
+    SetShouldBlockThirdPartyCookiesForTesting(PAL::SessionID sessionID, bool enabled) -> () Async
 #endif
 
     SetSessionIsControlledByAutomation(PAL::SessionID sessionID, bool controlled);
diff --git a/Source/WebKit/NetworkProcess/NetworkSession.h b/Source/WebKit/NetworkProcess/NetworkSession.h
index 9102279..aa44058 100644
--- a/Source/WebKit/NetworkProcess/NetworkSession.h
+++ b/Source/WebKit/NetworkProcess/NetworkSession.h
@@ -93,11 +93,11 @@
     void notifyPageStatisticsTelemetryFinished(unsigned numberOfPrevalentResources, unsigned numberOfPrevalentResourcesWithUserInteraction, unsigned numberOfPrevalentResourcesWithoutUserInteraction, unsigned topPrevalentResourceWithUserInteractionDaysSinceUserInteraction, unsigned medianDaysSinceUserInteractionPrevalentResourceWithUserInteraction, unsigned top3NumberOfPrevalentResourcesWithUI, unsigned top3MedianSubFrameWithoutUI, unsigned top3MedianSubResourceWithoutUI, unsigned top3MedianUniqueRedirectsWithoutUI, unsigned top3MedianDataRecordsRemovedWithoutUI);
     bool enableResourceLoadStatisticsLogTestingEvent() const { return m_enableResourceLoadStatisticsLogTestingEvent; }
     void setResourceLoadStatisticsLogTestingEvent(bool log) { m_enableResourceLoadStatisticsLogTestingEvent = log; }
-    bool shouldIsolateSessionsForPrevalentTopFrames() const { return m_enableResourceLoadStatisticsNSURLSessionSwitching == EnableResourceLoadStatisticsNSURLSessionSwitching::Yes; }
     virtual bool hasIsolatedSession(const WebCore::RegistrableDomain) const { return false; }
     virtual void clearIsolatedSessions() { }
     void setShouldDowngradeReferrerForTesting(bool);
     bool shouldDowngradeReferrer() const;
+    void setShouldBlockThirdPartyCookiesForTesting(bool);
 #endif
     void storeAdClickAttribution(WebCore::AdClickAttribution&&);
     void handleAdClickAttributionConversion(WebCore::AdClickAttribution::Conversion&&, const URL& requestURL, const WebCore::ResourceRequest& redirectRequest);
@@ -140,7 +140,6 @@
     ShouldIncludeLocalhost m_shouldIncludeLocalhostInResourceLoadStatistics { ShouldIncludeLocalhost::Yes };
     EnableResourceLoadStatisticsDebugMode m_enableResourceLoadStatisticsDebugMode { EnableResourceLoadStatisticsDebugMode::No };
     WebCore::RegistrableDomain m_resourceLoadStatisticsManualPrevalentResource;
-    EnableResourceLoadStatisticsNSURLSessionSwitching m_enableResourceLoadStatisticsNSURLSessionSwitching { EnableResourceLoadStatisticsNSURLSessionSwitching::No };
     bool m_enableResourceLoadStatisticsLogTestingEvent;
     bool m_downgradeReferrer { true };
 #endif
diff --git a/Source/WebKit/NetworkProcess/NetworkSessionCreationParameters.cpp b/Source/WebKit/NetworkProcess/NetworkSessionCreationParameters.cpp
index cc14099..d8734b5 100644
--- a/Source/WebKit/NetworkProcess/NetworkSessionCreationParameters.cpp
+++ b/Source/WebKit/NetworkProcess/NetworkSessionCreationParameters.cpp
@@ -68,7 +68,7 @@
     encoder << shouldIncludeLocalhostInResourceLoadStatistics;
     encoder << enableResourceLoadStatisticsDebugMode;
     encoder << resourceLoadStatisticsManualPrevalentResource;
-    encoder << enableResourceLoadStatisticsNSURLSessionSwitching;
+    encoder << enableThirdPartyCookieBlockingOnSitesWithoutUserInteraction;
 
     encoder << networkCacheDirectory << networkCacheDirectoryExtensionHandle;
 
@@ -199,9 +199,9 @@
     if (!resourceLoadStatisticsManualPrevalentResource)
         return WTF::nullopt;
 
-    Optional<bool> enableResourceLoadStatisticsNSURLSessionSwitching;
-    decoder >> enableResourceLoadStatisticsNSURLSessionSwitching;
-    if (!enableResourceLoadStatisticsNSURLSessionSwitching)
+    Optional<bool> enableThirdPartyCookieBlockingOnSitesWithoutUserInteraction;
+    decoder >> enableThirdPartyCookieBlockingOnSitesWithoutUserInteraction;
+    if (!enableThirdPartyCookieBlockingOnSitesWithoutUserInteraction)
         return WTF::nullopt;
 
     Optional<String> networkCacheDirectory;
@@ -282,7 +282,7 @@
         , WTFMove(*enableResourceLoadStatisticsLogTestingEvent)
         , WTFMove(*shouldIncludeLocalhostInResourceLoadStatistics)
         , WTFMove(*enableResourceLoadStatisticsDebugMode)
-        , WTFMove(*enableResourceLoadStatisticsNSURLSessionSwitching)
+        , WTFMove(*enableThirdPartyCookieBlockingOnSitesWithoutUserInteraction)
         , WTFMove(*deviceManagementRestrictionsEnabled)
         , WTFMove(*allLoadsBlockedByDeviceManagementRestrictionsForTesting)
         , WTFMove(*resourceLoadStatisticsManualPrevalentResource)
diff --git a/Source/WebKit/NetworkProcess/NetworkSessionCreationParameters.h b/Source/WebKit/NetworkProcess/NetworkSessionCreationParameters.h
index e801712..4cddb0e 100644
--- a/Source/WebKit/NetworkProcess/NetworkSessionCreationParameters.h
+++ b/Source/WebKit/NetworkProcess/NetworkSessionCreationParameters.h
@@ -85,7 +85,7 @@
     bool enableResourceLoadStatisticsLogTestingEvent { false };
     bool shouldIncludeLocalhostInResourceLoadStatistics { true };
     bool enableResourceLoadStatisticsDebugMode { false };
-    bool enableResourceLoadStatisticsNSURLSessionSwitching { true };
+    bool enableThirdPartyCookieBlockingOnSitesWithoutUserInteraction { false };
     bool deviceManagementRestrictionsEnabled { false };
     bool allLoadsBlockedByDeviceManagementRestrictionsForTesting { false };
     WebCore::RegistrableDomain resourceLoadStatisticsManualPrevalentResource { };
diff --git a/Source/WebKit/NetworkProcess/cocoa/NetworkDataTaskCocoa.mm b/Source/WebKit/NetworkProcess/cocoa/NetworkDataTaskCocoa.mm
index 77a220f..39db8b4 100644
--- a/Source/WebKit/NetworkProcess/cocoa/NetworkDataTaskCocoa.mm
+++ b/Source/WebKit/NetworkProcess/cocoa/NetworkDataTaskCocoa.mm
@@ -213,7 +213,7 @@
     if (auto* networkStorageSession = session.networkStorageSession()) {
         if (!shouldBlockCookies)
             shouldBlockCookies = networkStorageSession->shouldBlockCookies(request, frameID, pageID);
-        needsIsolatedSession = session.shouldIsolateSessionsForPrevalentTopFrames() && networkStorageSession->shouldBlockThirdPartyCookiesButKeepFirstPartyCookiesFor(firstParty);
+        needsIsolatedSession = networkStorageSession->shouldBlockThirdPartyCookiesButKeepFirstPartyCookiesFor(firstParty);
     }
 #endif
     restrictRequestReferrerToOriginIfNeeded(request);
diff --git a/Source/WebKit/NetworkProcess/cocoa/NetworkSessionCocoa.mm b/Source/WebKit/NetworkProcess/cocoa/NetworkSessionCocoa.mm
index 00be2c2..fab6fce 100644
--- a/Source/WebKit/NetworkProcess/cocoa/NetworkSessionCocoa.mm
+++ b/Source/WebKit/NetworkProcess/cocoa/NetworkSessionCocoa.mm
@@ -1109,7 +1109,7 @@
     m_shouldIncludeLocalhostInResourceLoadStatistics = parameters.shouldIncludeLocalhostInResourceLoadStatistics ? ShouldIncludeLocalhost::Yes : ShouldIncludeLocalhost::No;
     m_enableResourceLoadStatisticsDebugMode = parameters.enableResourceLoadStatisticsDebugMode ? EnableResourceLoadStatisticsDebugMode::Yes : EnableResourceLoadStatisticsDebugMode::No;
     m_resourceLoadStatisticsManualPrevalentResource = parameters.resourceLoadStatisticsManualPrevalentResource;
-    m_enableResourceLoadStatisticsNSURLSessionSwitching = parameters.enableResourceLoadStatisticsNSURLSessionSwitching ? EnableResourceLoadStatisticsNSURLSessionSwitching::Yes : EnableResourceLoadStatisticsNSURLSessionSwitching::No;
+    storageSession->setIsThirdPartyCookieBlockingOnSitesWithoutUserInteractionEnabled(parameters.enableThirdPartyCookieBlockingOnSitesWithoutUserInteraction);
     setResourceLoadStatisticsEnabled(parameters.enableResourceLoadStatistics);
 #endif
 
diff --git a/Source/WebKit/Shared/WebPreferences.yaml b/Source/WebKit/Shared/WebPreferences.yaml
index 974618f..6659493 100644
--- a/Source/WebKit/Shared/WebPreferences.yaml
+++ b/Source/WebKit/Shared/WebPreferences.yaml
@@ -1765,12 +1765,11 @@
   webcoreBinding: RuntimeEnabledFeatures
   category: experimental
 
-IsITPSessionSwitchingEnabled:
+IsThirdPartyCookieBlockingOnSitesWithoutUserInteractionEnabled:
   type: bool
-  defaultValue: true
-  humanReadableName: "ITP Session Switching"
-  humanReadableDescription: "Enable session switching for domains classified by ITP"
-  webcoreBinding: RuntimeEnabledFeatures
+  defaultValue: false
+  humanReadableName: "Block 3rd-Party Cookies On Sites Without Interaction (ITP)"
+  humanReadableDescription: "Block all third-party cookies on websites without user interaction"
   category: internal
 
 IsLoggedInAPIEnabled:
diff --git a/Source/WebKit/UIProcess/API/C/WKWebsiteDataStoreRef.cpp b/Source/WebKit/UIProcess/API/C/WKWebsiteDataStoreRef.cpp
index d335060..7089f7e 100644
--- a/Source/WebKit/UIProcess/API/C/WKWebsiteDataStoreRef.cpp
+++ b/Source/WebKit/UIProcess/API/C/WKWebsiteDataStoreRef.cpp
@@ -538,6 +538,17 @@
 #endif
 }
 
+void WKWebsiteDataStoreSetResourceLoadStatisticsShouldBlockThirdPartyCookiesForTesting(WKWebsiteDataStoreRef dataStoreRef, bool enabled, void* context, WKWebsiteDataStoreSetResourceLoadStatisticsShouldBlockThirdPartyCookiesForTestingFunction completionHandler)
+{
+#if ENABLE(RESOURCE_LOAD_STATISTICS)
+    WebKit::toImpl(dataStoreRef)->setResourceLoadStatisticsShouldBlockThirdPartyCookiesForTesting(enabled, [context, completionHandler] {
+        completionHandler(context);
+    });
+#else
+    completionHandler(context);
+#endif
+}
+
 void WKWebsiteDataStoreStatisticsResetToConsistentState(WKWebsiteDataStoreRef dataStoreRef, void* context, WKWebsiteDataStoreStatisticsResetToConsistentStateFunction completionHandler)
 {
 #if ENABLE(RESOURCE_LOAD_STATISTICS)
@@ -550,6 +561,7 @@
     store.resetCacheMaxAgeCapForPrevalentResources([callbackAggregator = callbackAggregator.copyRef()] { });
     store.resetCrossSiteLoadsWithLinkDecorationForTesting([callbackAggregator = callbackAggregator.copyRef()] { });
     store.setResourceLoadStatisticsShouldDowngradeReferrerForTesting(true, [callbackAggregator = callbackAggregator.copyRef()] { });
+    store.setResourceLoadStatisticsShouldBlockThirdPartyCookiesForTesting(false, [callbackAggregator = callbackAggregator.copyRef()] { });
     store.resetParametersToDefaultValues([callbackAggregator = callbackAggregator.copyRef()] { });
     store.scheduleClearInMemoryAndPersistent(WebKit::ShouldGrandfatherStatistics::No, [callbackAggregator = callbackAggregator.copyRef()] { });
     store.setUseITPDatabase(false, [callbackAggregator = callbackAggregator.copyRef()] { });
diff --git a/Source/WebKit/UIProcess/API/C/WKWebsiteDataStoreRef.h b/Source/WebKit/UIProcess/API/C/WKWebsiteDataStoreRef.h
index ad5c2ce..65385f8 100644
--- a/Source/WebKit/UIProcess/API/C/WKWebsiteDataStoreRef.h
+++ b/Source/WebKit/UIProcess/API/C/WKWebsiteDataStoreRef.h
@@ -117,6 +117,8 @@
 WK_EXPORT void WKWebsiteDataStoreStatisticsHasIsolatedSession(WKWebsiteDataStoreRef dataStoreRef, WKStringRef host, void* context, WKWebsiteDataStoreStatisticsHasIsolatedSessionFunction callback);
 typedef void (*WKWebsiteDataStoreSetResourceLoadStatisticsShouldDowngradeReferrerForTestingFunction)(void* functionContext);
 WK_EXPORT void WKWebsiteDataStoreSetResourceLoadStatisticsShouldDowngradeReferrerForTesting(WKWebsiteDataStoreRef dataStoreRef, bool enabled, void* context, WKWebsiteDataStoreSetResourceLoadStatisticsShouldDowngradeReferrerForTestingFunction completionHandler);
+typedef void (*WKWebsiteDataStoreSetResourceLoadStatisticsShouldBlockThirdPartyCookiesForTestingFunction)(void* functionContext);
+WK_EXPORT void WKWebsiteDataStoreSetResourceLoadStatisticsShouldBlockThirdPartyCookiesForTesting(WKWebsiteDataStoreRef dataStoreRef, bool enabled, void* context, WKWebsiteDataStoreSetResourceLoadStatisticsShouldBlockThirdPartyCookiesForTestingFunction completionHandler);
 typedef void (*WKWebsiteDataStoreStatisticsResetToConsistentStateFunction)(void* functionContext);
 WK_EXPORT void WKWebsiteDataStoreStatisticsResetToConsistentState(WKWebsiteDataStoreRef dataStoreRef, void* context, WKWebsiteDataStoreStatisticsResetToConsistentStateFunction completionHandler);
 
diff --git a/Source/WebKit/UIProcess/Network/NetworkProcessProxy.cpp b/Source/WebKit/UIProcess/Network/NetworkProcessProxy.cpp
index 4585cff..6c190529 100644
--- a/Source/WebKit/UIProcess/Network/NetworkProcessProxy.cpp
+++ b/Source/WebKit/UIProcess/Network/NetworkProcessProxy.cpp
@@ -1054,6 +1054,16 @@
     
     sendWithAsyncReply(Messages::NetworkProcess::SetShouldDowngradeReferrerForTesting(enabled), WTFMove(completionHandler));
 }
+
+void NetworkProcessProxy::setShouldBlockThirdPartyCookiesForTesting(PAL::SessionID sessionID, bool enabled, CompletionHandler<void()>&& completionHandler)
+{
+    if (!canSendMessage()) {
+        completionHandler();
+        return;
+    }
+    
+    sendWithAsyncReply(Messages::NetworkProcess::SetShouldBlockThirdPartyCookiesForTesting(sessionID, enabled), WTFMove(completionHandler));
+}
 #endif // ENABLE(RESOURCE_LOAD_STATISTICS)
 
 void NetworkProcessProxy::sendProcessWillSuspendImminently()
diff --git a/Source/WebKit/UIProcess/Network/NetworkProcessProxy.h b/Source/WebKit/UIProcess/Network/NetworkProcessProxy.h
index 6ab2039..da4bed2 100644
--- a/Source/WebKit/UIProcess/Network/NetworkProcessProxy.h
+++ b/Source/WebKit/UIProcess/Network/NetworkProcessProxy.h
@@ -155,6 +155,7 @@
     void deleteWebsiteDataInUIProcessForRegistrableDomains(PAL::SessionID, OptionSet<WebsiteDataType>, OptionSet<WebsiteDataFetchOption>, Vector<RegistrableDomain>, CompletionHandler<void(HashSet<WebCore::RegistrableDomain>&&)>&&);
     void hasIsolatedSession(PAL::SessionID, const RegistrableDomain&, CompletionHandler<void(bool)>&&);
     void setShouldDowngradeReferrerForTesting(bool, CompletionHandler<void()>&&);
+    void setShouldBlockThirdPartyCookiesForTesting(PAL::SessionID, bool, CompletionHandler<void()>&&);
 #endif
 
     void processReadyToSuspend();
diff --git a/Source/WebKit/UIProcess/WebsiteData/Cocoa/WebsiteDataStoreCocoa.mm b/Source/WebKit/UIProcess/WebsiteData/Cocoa/WebsiteDataStoreCocoa.mm
index cd4ab9c..78c66a6 100644
--- a/Source/WebKit/UIProcess/WebsiteData/Cocoa/WebsiteDataStoreCocoa.mm
+++ b/Source/WebKit/UIProcess/WebsiteData/Cocoa/WebsiteDataStoreCocoa.mm
@@ -29,6 +29,7 @@
 #import "CookieStorageUtilsCF.h"
 #import "SandboxUtilities.h"
 #import "StorageManager.h"
+#import "WebPreferencesKeys.h"
 #import "WebResourceLoadStatisticsStore.h"
 #import "WebsiteDataStoreParameters.h"
 #import <WebCore/RegistrableDomain.h>
@@ -68,11 +69,12 @@
     NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
     bool shouldLogCookieInformation = false;
     bool enableResourceLoadStatisticsDebugMode = false;
-    bool enableResourceLoadStatisticsNSURLSessionSwitching = WebCore::RuntimeEnabledFeatures::sharedFeatures().isITPSessionSwitchingEnabled();
-    WebCore::RegistrableDomain resourceLoadStatisticsManualPrevalentResource { };
+    bool enableThirdPartyCookieBlockingOnSitesWithoutUserInteraction = false;
     bool enableLegacyTLS = [defaults boolForKey:@"WebKitEnableLegacyTLS"];
+    WebCore::RegistrableDomain resourceLoadStatisticsManualPrevalentResource { };
 #if ENABLE(RESOURCE_LOAD_STATISTICS)
     enableResourceLoadStatisticsDebugMode = [defaults boolForKey:@"ITPDebugMode"];
+    enableThirdPartyCookieBlockingOnSitesWithoutUserInteraction = [defaults boolForKey:[NSString stringWithFormat:@"InternalDebug%@", WebPreferencesKey::isThirdPartyCookieBlockingOnSitesWithoutUserInteractionEnabledKey().createCFString().get()]];
     auto* manualPrevalentResource = [defaults stringForKey:@"ITPManualPrevalentResource"];
     if (manualPrevalentResource) {
         URL url { URL(), manualPrevalentResource };
@@ -136,7 +138,7 @@
         hasStatisticsTestingCallback(),
         shouldIncludeLocalhostInResourceLoadStatistics,
         enableResourceLoadStatisticsDebugMode,
-        enableResourceLoadStatisticsNSURLSessionSwitching,
+        enableThirdPartyCookieBlockingOnSitesWithoutUserInteraction,
         m_configuration->deviceManagementRestrictionsEnabled(),
         m_configuration->allLoadsBlockedByDeviceManagementRestrictionsForTesting(),
         WTFMove(resourceLoadStatisticsManualPrevalentResource),
diff --git a/Source/WebKit/UIProcess/WebsiteData/WebsiteDataStore.cpp b/Source/WebKit/UIProcess/WebsiteData/WebsiteDataStore.cpp
index 3908d7b..14b60ae 100644
--- a/Source/WebKit/UIProcess/WebsiteData/WebsiteDataStore.cpp
+++ b/Source/WebKit/UIProcess/WebsiteData/WebsiteDataStore.cpp
@@ -1782,6 +1782,20 @@
     }
     ASSERT(!completionHandler);
 }
+
+void WebsiteDataStore::setResourceLoadStatisticsShouldBlockThirdPartyCookiesForTesting(bool enabled, CompletionHandler<void()>&& completionHandler)
+{
+    auto callbackAggregator = CallbackAggregator::create(WTFMove(completionHandler));
+    
+    for (auto& processPool : processPools()) {
+        if (auto* networkProcess = processPool->networkProcess()) {
+            networkProcess->setShouldBlockThirdPartyCookiesForTesting(m_sessionID, enabled, [callbackAggregator = callbackAggregator.copyRef()] { });
+            ASSERT(processPools().size() == 1);
+            break;
+        }
+    }
+    ASSERT(!completionHandler);
+}
 #endif // ENABLE(RESOURCE_LOAD_STATISTICS)
 
 void WebsiteDataStore::setCacheMaxAgeCapForPrevalentResources(Seconds seconds, CompletionHandler<void()>&& completionHandler)
diff --git a/Source/WebKit/UIProcess/WebsiteData/WebsiteDataStore.h b/Source/WebKit/UIProcess/WebsiteData/WebsiteDataStore.h
index 930e305..f9a21db 100644
--- a/Source/WebKit/UIProcess/WebsiteData/WebsiteDataStore.h
+++ b/Source/WebKit/UIProcess/WebsiteData/WebsiteDataStore.h
@@ -192,6 +192,7 @@
     void hasLocalStorageForTesting(const URL&, CompletionHandler<void(bool)>&&) const;
     void hasIsolatedSessionForTesting(const URL&, CompletionHandler<void(bool)>&&) const;
     void setResourceLoadStatisticsShouldDowngradeReferrerForTesting(bool, CompletionHandler<void()>&&);
+    void setResourceLoadStatisticsShouldBlockThirdPartyCookiesForTesting(bool, CompletionHandler<void()>&&);
 #endif
     void setCacheMaxAgeCapForPrevalentResources(Seconds, CompletionHandler<void()>&&);
     void resetCacheMaxAgeCapForPrevalentResources(CompletionHandler<void()>&&);
diff --git a/Tools/ChangeLog b/Tools/ChangeLog
index f402594..a1b8ff4 100644
--- a/Tools/ChangeLog
+++ b/Tools/ChangeLog
@@ -1,3 +1,29 @@
+2019-10-16  John Wilander  <wilander@apple.com>
+
+        Resource Load Statistics (experimental): Block all third-party cookies on websites without prior user interaction
+        https://bugs.webkit.org/show_bug.cgi?id=203017
+        <rdar://problem/56262708>
+
+        Reviewed by Alex Christensen.
+
+        These changes are test infrastructure to be able to toggle the function
+        through testRunner.setStatisticsShouldBlockThirdPartyCookies().
+
+        * WebKitTestRunner/InjectedBundle/Bindings/TestRunner.idl:
+        * WebKitTestRunner/InjectedBundle/InjectedBundle.cpp:
+        (WTR::InjectedBundle::didReceiveMessageToPage):
+        * WebKitTestRunner/InjectedBundle/TestRunner.cpp:
+        (WTR::TestRunner::setStatisticsShouldBlockThirdPartyCookies):
+        (WTR::TestRunner::statisticsCallDidSetShouldBlockThirdPartyCookiesCallback):
+        * WebKitTestRunner/InjectedBundle/TestRunner.h:
+        * WebKitTestRunner/TestController.cpp:
+        (WTR::TestController::setStatisticsShouldBlockThirdPartyCookies):
+        * WebKitTestRunner/TestController.h:
+        * WebKitTestRunner/TestInvocation.cpp:
+        (WTR::TestInvocation::didReceiveSynchronousMessageFromInjectedBundle):
+        (WTR::TestInvocation::didSetShouldBlockThirdPartyCookies):
+        * WebKitTestRunner/TestInvocation.h:
+
 2019-10-16  Kate Cheney  <katherine_cheney@apple.com>
 
         WKWebsiteDataStoreStatisticsResetToConsistentState() should call store.setUseITPDatabase(false)
diff --git a/Tools/WebKitTestRunner/InjectedBundle/Bindings/TestRunner.idl b/Tools/WebKitTestRunner/InjectedBundle/Bindings/TestRunner.idl
index e26b4fa..faef0a0 100644
--- a/Tools/WebKitTestRunner/InjectedBundle/Bindings/TestRunner.idl
+++ b/Tools/WebKitTestRunner/InjectedBundle/Bindings/TestRunner.idl
@@ -341,6 +341,7 @@
     void statisticsResetToConsistentState(object completionHandler);
     boolean hasStatisticsIsolatedSession(DOMString hostName);
     void setStatisticsShouldDowngradeReferrer(boolean value, object callback);
+    void setStatisticsShouldBlockThirdPartyCookies(boolean value, object callback);
 
     // Injected bundle form client.
     void installTextDidChangeInTextFieldCallback(object callback);
diff --git a/Tools/WebKitTestRunner/InjectedBundle/InjectedBundle.cpp b/Tools/WebKitTestRunner/InjectedBundle/InjectedBundle.cpp
index e7ad015..f52ed1a 100644
--- a/Tools/WebKitTestRunner/InjectedBundle/InjectedBundle.cpp
+++ b/Tools/WebKitTestRunner/InjectedBundle/InjectedBundle.cpp
@@ -332,6 +332,11 @@
         return;
     }
 
+    if (WKStringIsEqualToUTF8CString(messageName, "CallDidSetShouldBlockThirdPartyCookies")) {
+        m_testRunner->statisticsCallDidSetShouldBlockThirdPartyCookiesCallback();
+        return;
+    }
+
     if (WKStringIsEqualToUTF8CString(messageName, "CallDidResetStatisticsToConsistentState")) {
         m_testRunner->statisticsCallDidResetToConsistentStateCallback();
         return;
diff --git a/Tools/WebKitTestRunner/InjectedBundle/TestRunner.cpp b/Tools/WebKitTestRunner/InjectedBundle/TestRunner.cpp
index ba5993c7a..b4f0b4b 100644
--- a/Tools/WebKitTestRunner/InjectedBundle/TestRunner.cpp
+++ b/Tools/WebKitTestRunner/InjectedBundle/TestRunner.cpp
@@ -743,6 +743,7 @@
     StatisticsDidResetToConsistentStateCallbackID,
     StatisticsDidSetBlockCookiesForHostCallbackID,
     StatisticsDidSetShouldDowngradeReferrerCallbackID,
+    StatisticsDidSetShouldBlockThirdPartyCookiesCallbackID,
     AllStorageAccessEntriesCallbackID,
     DidRemoveAllSessionCredentialsCallbackID,
     GetApplicationManifestCallbackID,
@@ -2208,6 +2209,23 @@
     callTestRunnerCallback(StatisticsDidSetShouldDowngradeReferrerCallbackID);
 }
 
+void TestRunner::setStatisticsShouldBlockThirdPartyCookies(bool value, JSValueRef completionHandler)
+{
+    if (m_hasSetBlockThirdPartyCookiesCallback)
+        return;
+    
+    cacheTestRunnerCallback(StatisticsDidSetShouldBlockThirdPartyCookiesCallbackID, completionHandler);
+    WKRetainPtr<WKStringRef> messageName = adoptWK(WKStringCreateWithUTF8CString("SetStatisticsShouldBlockThirdPartyCookies"));
+    WKRetainPtr<WKBooleanRef> messageBody = adoptWK(WKBooleanCreate(value));
+    WKBundlePostSynchronousMessage(InjectedBundle::singleton().bundle(), messageName.get(), messageBody.get(), nullptr);
+    m_hasSetBlockThirdPartyCookiesCallback = true;
+}
+
+void TestRunner::statisticsCallDidSetShouldBlockThirdPartyCookiesCallback()
+{
+    callTestRunnerCallback(StatisticsDidSetShouldBlockThirdPartyCookiesCallbackID);
+}
+
 void TestRunner::statisticsCallClearThroughWebsiteDataRemovalCallback()
 {
     callTestRunnerCallback(StatisticsDidClearThroughWebsiteDataRemovalCallbackID);
diff --git a/Tools/WebKitTestRunner/InjectedBundle/TestRunner.h b/Tools/WebKitTestRunner/InjectedBundle/TestRunner.h
index c1f124f..e0394e6 100644
--- a/Tools/WebKitTestRunner/InjectedBundle/TestRunner.h
+++ b/Tools/WebKitTestRunner/InjectedBundle/TestRunner.h
@@ -440,6 +440,8 @@
     bool hasStatisticsIsolatedSession(JSStringRef hostName);
     void setStatisticsShouldDowngradeReferrer(bool, JSValueRef callback);
     void statisticsCallDidSetShouldDowngradeReferrerCallback();
+    void setStatisticsShouldBlockThirdPartyCookies(bool, JSValueRef callback);
+    void statisticsCallDidSetShouldBlockThirdPartyCookiesCallback();
     void statisticsResetToConsistentState(JSValueRef completionHandler);
     void statisticsCallDidResetToConsistentStateCallback();
 
@@ -573,6 +575,7 @@
     bool m_userStyleSheetEnabled { false };
     bool m_dumpAllHTTPRedirectedResponseHeaders { false };
     bool m_hasSetDowngradeReferrerCallback { false };
+    bool m_hasSetBlockThirdPartyCookiesCallback { false };
 };
 
 } // namespace WTR
diff --git a/Tools/WebKitTestRunner/TestController.cpp b/Tools/WebKitTestRunner/TestController.cpp
index 4ba4916..ea9c6a5 100644
--- a/Tools/WebKitTestRunner/TestController.cpp
+++ b/Tools/WebKitTestRunner/TestController.cpp
@@ -3555,6 +3555,14 @@
     m_currentInvocation->didSetShouldDowngradeReferrer();
 }
 
+void TestController::setStatisticsShouldBlockThirdPartyCookies(bool value)
+{
+    ResourceStatisticsCallbackContext context(*this);
+    WKWebsiteDataStoreSetResourceLoadStatisticsShouldBlockThirdPartyCookiesForTesting(TestController::websiteDataStore(), value, &context, resourceStatisticsVoidResultCallback);
+    runUntil(context.done, noTimeout);
+    m_currentInvocation->didSetShouldBlockThirdPartyCookies();
+}
+
 void TestController::statisticsResetToConsistentState()
 {
     ResourceStatisticsCallbackContext context(*this);
diff --git a/Tools/WebKitTestRunner/TestController.h b/Tools/WebKitTestRunner/TestController.h
index 4bf9023..680d2e2 100644
--- a/Tools/WebKitTestRunner/TestController.h
+++ b/Tools/WebKitTestRunner/TestController.h
@@ -252,6 +252,7 @@
     void setStatisticsCacheMaxAgeCap(double seconds);
     bool hasStatisticsIsolatedSession(WKStringRef hostName);
     void setStatisticsShouldDowngradeReferrer(bool value);
+    void setStatisticsShouldBlockThirdPartyCookies(bool value);
     void statisticsResetToConsistentState();
 
     void getAllStorageAccessEntries();
diff --git a/Tools/WebKitTestRunner/TestInvocation.cpp b/Tools/WebKitTestRunner/TestInvocation.cpp
index 8fe9279..3c6269c 100644
--- a/Tools/WebKitTestRunner/TestInvocation.cpp
+++ b/Tools/WebKitTestRunner/TestInvocation.cpp
@@ -1542,6 +1542,13 @@
         return nullptr;
     }
     
+    if (WKStringIsEqualToUTF8CString(messageName, "SetStatisticsShouldBlockThirdPartyCookies")) {
+        ASSERT(WKGetTypeID(messageBody) == WKBooleanGetTypeID());
+        WKBooleanRef value = static_cast<WKBooleanRef>(messageBody);
+        TestController::singleton().setStatisticsShouldBlockThirdPartyCookies(WKBooleanGetValue(value));
+        return nullptr;
+    }
+    
     if (WKStringIsEqualToUTF8CString(messageName, "RemoveAllSessionCredentials")) {
         TestController::singleton().removeAllSessionCredentials();
         return nullptr;
@@ -1825,6 +1832,12 @@
     WKPagePostMessageToInjectedBundle(TestController::singleton().mainWebView()->page(), messageName.get(), 0);
 }
 
+void TestInvocation::didSetShouldBlockThirdPartyCookies()
+{
+    WKRetainPtr<WKStringRef> messageName = adoptWK(WKStringCreateWithUTF8CString("CallDidSetShouldBlockThirdPartyCookies"));
+    WKPagePostMessageToInjectedBundle(TestController::singleton().mainWebView()->page(), messageName.get(), nullptr);
+}
+
 void TestInvocation::didResetStatisticsToConsistentState()
 {
     WKRetainPtr<WKStringRef> messageName = adoptWK(WKStringCreateWithUTF8CString("CallDidResetStatisticsToConsistentState"));
diff --git a/Tools/WebKitTestRunner/TestInvocation.h b/Tools/WebKitTestRunner/TestInvocation.h
index a265eb9..7806fa2 100644
--- a/Tools/WebKitTestRunner/TestInvocation.h
+++ b/Tools/WebKitTestRunner/TestInvocation.h
@@ -74,6 +74,7 @@
 
     void didClearStatisticsThroughWebsiteDataRemoval();
     void didSetShouldDowngradeReferrer();
+    void didSetShouldBlockThirdPartyCookies();
     void didResetStatisticsToConsistentState();
     void didSetBlockCookiesForHost();
     void didSetStatisticsDebugMode();