Title: [261242] trunk
Revision
261242
Author
wilan...@apple.com
Date
2020-05-06 11:44:26 -0700 (Wed, 06 May 2020)

Log Message

Exempt app-bound domains from ITP's website data deletion and third-party cookie blocking between themselves
https://bugs.webkit.org/show_bug.cgi?id=210674
<rdar://problem/61950767>

Reviewed by Chris Dumez.

Source/WebCore:

This change adds functionality to NetworkStorageSession to allow it to exempt
app-bound domains from third-party cookie blocking.

Tests: http/tests/resourceLoadStatistics/exemptDomains/app-bound-domains-exempt-from-cookie-blocking-between-each-other.html
       http/tests/resourceLoadStatistics/exemptDomains/app-bound-domains-exempt-from-website-data-deletion-database.html
       http/tests/resourceLoadStatistics/exemptDomains/app-bound-domains-exempt-from-website-data-deletion.html

* platform/network/NetworkStorageSession.cpp:
(WebCore::NetworkStorageSession::shouldBlockCookies const):
(WebCore::NetworkStorageSession::shouldExemptDomainPairFromThirdPartyCookieBlocking const):
(WebCore::NetworkStorageSession::setAppBoundDomains):
(WebCore::NetworkStorageSession::resetAppBoundDomains):
* platform/network/NetworkStorageSession.h:

Source/WebKit:

This change forwards information about app-bound domains to ITP and web
processes so that they can be exempt from website data deletion and
third-party cookie blocking between themselves.

App-bound domains are configured statically and apply to all website
data stores. Therefore the setting needs to be forwarded to all
website data stores and ITP functionality in all network and web
content processes. This is done through the new static function
WebsiteDataStore::setAppBoundDomainsForITPIfInitialized().

Since app-bound domains are loaded lazily from disk and on a background
thread, this patch forwards them in ResourceLoadStatisticsParameters if
they've already been loaded. Then every time app-bound domains are
updated, they are forwarded to ITP. This ensures that ITP will have them
as soon as possible.

Setting app-bound domains for the purposes of ITP automatically switches
ITP's cookie blocking policy to the new
WebCore::ThirdPartyCookieBlockingMode::AllExceptBetweenAppBoundDomains.
This is done in WebResourceLoadStatisticsStore::setAppBoundDomains().

The C API changes are for test purposes.

* NetworkProcess/Classifier/ResourceLoadStatisticsDatabaseStore.cpp:
* NetworkProcess/Classifier/ResourceLoadStatisticsMemoryStore.cpp:
(WebKit::ResourceLoadStatisticsMemoryStore::registrableDomainsToDeleteOrRestrictWebsiteDataFor):
* NetworkProcess/Classifier/ResourceLoadStatisticsStore.cpp:
(WebKit::ResourceLoadStatisticsStore::setAppBoundDomains):
(WebKit::ResourceLoadStatisticsStore::resetParametersToDefaultValues):
(WebKit::ResourceLoadStatisticsStore::shouldExemptFromWebsiteDataDeletion const):
* NetworkProcess/Classifier/ResourceLoadStatisticsStore.h:
(WebKit::ResourceLoadStatisticsStore::standaloneApplicationDomain const): Deleted.
* NetworkProcess/Classifier/WebResourceLoadStatisticsStore.cpp:
(WebKit::WebResourceLoadStatisticsStore::setAppBoundDomains):
(WebKit::WebResourceLoadStatisticsStore::resetParametersToDefaultValues):
* NetworkProcess/Classifier/WebResourceLoadStatisticsStore.h:
* NetworkProcess/NetworkProcess.cpp:
(WebKit::NetworkProcess::setAppBoundDomainsForResourceLoadStatistics):
(WebKit::NetworkProcess::setThirdPartyCookieBlockingMode):
(WebKit::NetworkProcess::setShouldBlockThirdPartyCookiesForTesting): Deleted.
    Renamed setThirdPartyCookieBlockingMode.
* NetworkProcess/NetworkProcess.h:
* NetworkProcess/NetworkProcess.messages.in:
* NetworkProcess/cocoa/NetworkSessionCocoa.mm:
(WebKit::NetworkSessionCocoa::NetworkSessionCocoa):
* Shared/ResourceLoadStatisticsParameters.h:
(WebKit::ResourceLoadStatisticsParameters::encode const):
(WebKit::ResourceLoadStatisticsParameters::decode):
* UIProcess/API/C/WKWebsiteDataStoreRef.cpp:
(WKWebsiteDataStoreSetAppBoundDomainsForTesting):
* UIProcess/API/C/WKWebsiteDataStoreRef.h:
* UIProcess/Network/NetworkProcessProxy.cpp:
(WebKit::NetworkProcessProxy::setAppBoundDomainsForResourceLoadStatistics):
(WebKit::NetworkProcessProxy::setThirdPartyCookieBlockingMode):
(WebKit::NetworkProcessProxy::setShouldBlockThirdPartyCookiesForTesting): Deleted.
    Renamed setThirdPartyCookieBlockingMode.
* UIProcess/Network/NetworkProcessProxy.h:
* UIProcess/WebProcessPool.cpp:
(WebKit::WebProcessPool::ensureNetworkProcess):
* UIProcess/WebProcessProxy.cpp:
(WebKit::WebProcessProxy::setThirdPartyCookieBlockingMode):
(WebKit::WebProcessProxy::setShouldBlockThirdPartyCookiesForTesting): Deleted.
    Renamed setThirdPartyCookieBlockingMode.
* UIProcess/WebProcessProxy.h:
* UIProcess/WebsiteData/Cocoa/WebsiteDataStoreCocoa.mm:
(WebKit::WebsiteDataStore::initializeAppBoundDomains):
(WebKit::WebsiteDataStore::ensureAppBoundDomains const):
(WebKit::WebsiteDataStore::appBoundDomainsIfInitialized):
    This function allows fetching of app-bound domains without triggering
    the lazy loading. This is just to allow speculative configuration of ITP
    right when it's created — if any app-bound domains are already configured,
    forward them to ITP via ResourceLoadStatisticsParameters.
(WebKit::WebsiteDataStore::setAppBoundDomainsForTesting):
    This function is Cocoa-specific and only accepts localhost and 127.0.0.1
    to be configured as app-bound domains.
* UIProcess/WebsiteData/WebsiteDataStore.cpp:
(WebKit::WebsiteDataStore::setResourceLoadStatisticsShouldBlockThirdPartyCookiesForTesting):
(WebKit::WebsiteDataStore::setThirdPartyCookieBlockingMode):
(WebKit::WebsiteDataStore::parameters):
(WebKit::WebsiteDataStore::forwardAppBoundDomainsToITPIfInitialized):
(WebKit::WebsiteDataStore::setAppBoundDomainsForITP):
* UIProcess/WebsiteData/WebsiteDataStore.h:
* WebProcess/WebProcess.cpp:
(WebKit::WebProcess::setThirdPartyCookieBlockingMode):
(WebKit::WebProcess::setShouldBlockThirdPartyCookiesForTesting): Deleted.
* WebProcess/WebProcess.h:
* WebProcess/WebProcess.messages.in:

Tools:

This change adds a new TestRunner function
setAppBoundDomain() which takes an array of origin
strings and sets them to app-bound domains.

* WebKitTestRunner/InjectedBundle/Bindings/TestRunner.idl:
* WebKitTestRunner/InjectedBundle/InjectedBundle.cpp:
(WTR::InjectedBundle::didReceiveMessageToPage):
* WebKitTestRunner/InjectedBundle/TestRunner.cpp:
(WTR::TestRunner::setAppBoundDomains):
(WTR::TestRunner::didSetAppBoundDomainsCallback):
* WebKitTestRunner/InjectedBundle/TestRunner.h:
* WebKitTestRunner/TestController.cpp:
(WTR::AppBoundDomainsCallbackContext::AppBoundDomainsCallbackContext):
(WTR::didSetAppBoundDomainsCallback):
(WTR::TestController::setAppBoundDomains):
* WebKitTestRunner/TestController.h:
* WebKitTestRunner/TestInvocation.cpp:
(WTR::TestInvocation::didReceiveMessageFromInjectedBundle):
(WTR::TestInvocation::didSetAppBoundDomains):
* WebKitTestRunner/TestInvocation.h:

LayoutTests:

* http/tests/resourceLoadStatistics/exemptDomains/app-bound-domains-exempt-from-cookie-blocking-between-each-other-expected.txt: Added.
* http/tests/resourceLoadStatistics/exemptDomains/app-bound-domains-exempt-from-cookie-blocking-between-each-other.html: Added.
* http/tests/resourceLoadStatistics/exemptDomains/app-bound-domains-exempt-from-website-data-deletion-database-expected.txt: Added.
* http/tests/resourceLoadStatistics/exemptDomains/app-bound-domains-exempt-from-website-data-deletion-database.html: Added.
* http/tests/resourceLoadStatistics/exemptDomains/app-bound-domains-exempt-from-website-data-deletion-expected.txt: Added.
* http/tests/resourceLoadStatistics/exemptDomains/app-bound-domains-exempt-from-website-data-deletion.html: Added.

Modified Paths

Added Paths

Diff

Modified: trunk/LayoutTests/ChangeLog (261241 => 261242)


--- trunk/LayoutTests/ChangeLog	2020-05-06 18:43:19 UTC (rev 261241)
+++ trunk/LayoutTests/ChangeLog	2020-05-06 18:44:26 UTC (rev 261242)
@@ -1,3 +1,18 @@
+2020-05-06  John Wilander  <wilan...@apple.com>
+
+        Exempt app-bound domains from ITP's website data deletion and third-party cookie blocking between themselves
+        https://bugs.webkit.org/show_bug.cgi?id=210674
+        <rdar://problem/61950767>
+
+        Reviewed by Chris Dumez.
+
+        * http/tests/resourceLoadStatistics/exemptDomains/app-bound-domains-exempt-from-cookie-blocking-between-each-other-expected.txt: Added.
+        * http/tests/resourceLoadStatistics/exemptDomains/app-bound-domains-exempt-from-cookie-blocking-between-each-other.html: Added.
+        * http/tests/resourceLoadStatistics/exemptDomains/app-bound-domains-exempt-from-website-data-deletion-database-expected.txt: Added.
+        * http/tests/resourceLoadStatistics/exemptDomains/app-bound-domains-exempt-from-website-data-deletion-database.html: Added.
+        * http/tests/resourceLoadStatistics/exemptDomains/app-bound-domains-exempt-from-website-data-deletion-expected.txt: Added.
+        * http/tests/resourceLoadStatistics/exemptDomains/app-bound-domains-exempt-from-website-data-deletion.html: Added.
+
 2020-05-06  Daniel Bates  <daba...@apple.com>
 
         [iOS] ASSERTION FAILED: !(_keyboardFlags & WebEventKeyboardInputModifierFlagsChanged) in -[WebEvent charactersIgnoringModifiers] when pressing modifier on PDF

Added: trunk/LayoutTests/http/tests/resourceLoadStatistics/exemptDomains/app-bound-domains-exempt-from-cookie-blocking-between-each-other-expected.txt (0 => 261242)


--- trunk/LayoutTests/http/tests/resourceLoadStatistics/exemptDomains/app-bound-domains-exempt-from-cookie-blocking-between-each-other-expected.txt	                        (rev 0)
+++ trunk/LayoutTests/http/tests/resourceLoadStatistics/exemptDomains/app-bound-domains-exempt-from-cookie-blocking-between-each-other-expected.txt	2020-05-06 18:44:26 UTC (rev 261242)
@@ -0,0 +1,33 @@
+Tests that app-bound domains are exempt from third-party cookie blocking between themselves.
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
+
+--------
+Frame: '<!--frame1-->'
+--------
+Should not receive cookie.
+Did not receive cookie named 'firstPartyCookie'.
+Did not receive cookie named 'partitionedCookie'.
+Client-side document.cookie:
+
+--------
+Frame: '<!--frame2-->'
+--------
+Should not receive cookie.
+Did not receive cookie named 'firstPartyCookie'.
+Did not receive cookie named 'partitionedCookie'.
+Client-side document.cookie:
+
+--------
+Frame: '<!--frame3-->'
+--------
+Should receive cookie.
+Received cookie named 'firstPartyCookie'.
+Did not receive cookie named 'partitionedCookie'.
+Client-side document.cookie: firstPartyCookie=value

Added: trunk/LayoutTests/http/tests/resourceLoadStatistics/exemptDomains/app-bound-domains-exempt-from-cookie-blocking-between-each-other.html (0 => 261242)


--- trunk/LayoutTests/http/tests/resourceLoadStatistics/exemptDomains/app-bound-domains-exempt-from-cookie-blocking-between-each-other.html	                        (rev 0)
+++ trunk/LayoutTests/http/tests/resourceLoadStatistics/exemptDomains/app-bound-domains-exempt-from-cookie-blocking-between-each-other.html	2020-05-06 18:44:26 UTC (rev 261242)
@@ -0,0 +1,83 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+    <meta charset="UTF-8">
+    <script src=""
+    <script src=""
+</head>
+<body>
+<script>
+    description("Tests that app-bound domains are exempt from third-party cookie blocking between themselves.");
+    jsTestIsAsync = true;
+
+    const firstPartyOrigin = "http://127.0.0.1:8000";
+    const thirdPartyOrigin = "http://localhost:8000";
+    const thirdPartyResourceUrl = thirdPartyOrigin + "/resourceLoadStatistics/resources";
+    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 = firstPartyOrigin + "/resourceLoadStatistics/exemptDomains/app-bound-domains-exempt-from-cookie-blocking-between-each-other.html";
+    const subPathToGetCookies = "/get-cookies.php?name1=" + firstPartyCookieName + "&name2=" + partitionedCookieName;
+    const redirectChainUrl = thirdPartyResourceUrl + "/redirect.php?redirectTo=" + thirdPartyResourceUrl + subPathToGetCookies;
+
+    function openIframe(url, onLoadHandler) {
+        const element = document.createElement("iframe");
+        element.src = ""
+        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 = "" + subPathToSetFirstPartyCookie + "#" + returnUrl + "#step2";
+                break;
+            case "#step2":
+                // Check that the cookie doesn't get sent for localhost under 127.0.0.1 since only localhost is configured as an app-bound domain.
+                document.location.hash = "step3";
+                testRunner.setAppBoundDomains([ thirdPartyOrigin ], function() {
+                    openIframe(thirdPartyResourceUrl + subPathToGetCookies + "&message=Should not receive cookie.", function() {
+                        runTest();
+                    });
+                });
+                break;
+            case "#step3":
+                // Check that the cookie doesn't get sent for localhost under 127.0.0.1 since only 127.0.0.1 is configured as an app-bound domain.
+                document.location.hash = "step4";
+                testRunner.setAppBoundDomains([ firstPartyOrigin ], function() {
+                    openIframe(thirdPartyResourceUrl + subPathToGetCookies + "&message=Should not receive cookie.", function() {
+                        runTest();
+                    });
+                });
+                break;
+            case "#step4":
+                // Check that the cookie gets sent for localhost under 127.0.0.1 since they are now both configured as app-bound domains.
+                document.location.hash = "step5";
+                testRunner.setAppBoundDomains([ firstPartyOrigin, thirdPartyOrigin ], function() {
+                    openIframe(thirdPartyResourceUrl + subPathToGetCookies + "&message=Should receive cookie.", runTest);
+                });
+                break;
+            case "#step5":
+                testRunner.setStatisticsShouldBlockThirdPartyCookies(false, function() {
+                    setEnableFeature(false, finishJSTest);
+                });
+                break;
+        }
+    }
+
+    if (document.location.hash === "") {
+        setEnableFeature(true, function () {
+            testRunner.dumpChildFramesAsText();
+            document.location.hash = "step1";
+            runTest();
+        });
+    } else {
+        runTest();
+    }
+</script>
+</body>
+</html>

Added: trunk/LayoutTests/http/tests/resourceLoadStatistics/exemptDomains/app-bound-domains-exempt-from-website-data-deletion-database-expected.txt (0 => 261242)


--- trunk/LayoutTests/http/tests/resourceLoadStatistics/exemptDomains/app-bound-domains-exempt-from-website-data-deletion-database-expected.txt	                        (rev 0)
+++ trunk/LayoutTests/http/tests/resourceLoadStatistics/exemptDomains/app-bound-domains-exempt-from-website-data-deletion-database-expected.txt	2020-05-06 18:44:26 UTC (rev 261242)
@@ -0,0 +1,15 @@
+Check that website data does not get removed after a period of no user interaction if the website is app-bound.
+
+Before deletion: Client-side cookie exists.
+Before deletion: HttpOnly cookie exists.
+Before deletion: Regular server-side cookie exists.
+Before deletion: LocalStorage entry does exist.
+Before deletion: IDB entry does exist.
+
+After deletion: HttpOnly cookie exists.
+After deletion: Client-side cookie exists.
+After deletion: Regular server-side cookie exists.
+After deletion: LocalStorage entry does exist.
+After deletion: IDB entry does exist.
+
+

Added: trunk/LayoutTests/http/tests/resourceLoadStatistics/exemptDomains/app-bound-domains-exempt-from-website-data-deletion-database.html (0 => 261242)


--- trunk/LayoutTests/http/tests/resourceLoadStatistics/exemptDomains/app-bound-domains-exempt-from-website-data-deletion-database.html	                        (rev 0)
+++ trunk/LayoutTests/http/tests/resourceLoadStatistics/exemptDomains/app-bound-domains-exempt-from-website-data-deletion-database.html	2020-05-06 18:44:26 UTC (rev 261242)
@@ -0,0 +1,256 @@
+<!DOCTYPE html>
+<html>
+<head>
+    <script src=""
+    <script src=""
+</head>
+<body _onload_="setTimeout('runTest()', 0)">
+<div id="description">Check that website data does not get removed after a period of no user interaction if the website is app-bound.</div>
+<br>
+<div id="output"></div>
+<br>
+<script>
+    testRunner.waitUntilDone();
+    testRunner.dumpAsText();
+    testRunner.setUseITPDatabase(true);
+
+    const httpOnlyCookieName = "http-only-cookie";
+    const serverSideCookieName = "server-side-cookie";
+    const clientSideCookieName = "client-side-cookie";
+
+    function sortStringArray(a, b) {
+        a = a.toLowerCase();
+        b = b.toLowerCase();
+
+        return a > b ? 1 : b > a ? -1 : 0;
+    }
+
+    function addLinebreakToOutput() {
+        let element = document.createElement("br");
+        output.appendChild(element);
+    }
+
+    function addOutput(message) {
+        let element = document.createElement("div");
+        element.innerText = message;
+        output.appendChild(element);
+    }
+
+    function checkCookies(isAfterDeletion) {
+        let unsortedTestPassedMessages = [];
+        let cookies = internals.getCookies();
+        if (!cookies.length)
+            addOutput((isAfterDeletion ? "After" : "Before") + " script-accessible deletion: No cookies found.");
+        for (let cookie of cookies) {
+            switch (cookie.name) {
+                case httpOnlyCookieName:
+                    unsortedTestPassedMessages.push((isAfterDeletion ? "After" : "Before") + " deletion: " + (isAfterDeletion ? " " : "") + "HttpOnly cookie exists.");
+                    break;
+                case serverSideCookieName:
+                    unsortedTestPassedMessages.push((isAfterDeletion ? "After" : "Before") + " deletion: Regular server-side cookie exists.");
+                    break;
+                case clientSideCookieName:
+                    unsortedTestPassedMessages.push((isAfterDeletion ? "After" : "Before") + " deletion: Client-side cookie exists.");
+                    break;
+            }
+        }
+        let sortedTestPassedMessages = unsortedTestPassedMessages.sort(sortStringArray);
+        for (let testPassedMessage of sortedTestPassedMessages) {
+            addOutput(testPassedMessage);
+        }
+    }
+
+    const dbName = "TestDatabase";
+
+    function createIDBDataStore(callback) {
+        let request = indexedDB.open(dbName);
+        request._onerror_ = function() {
+            addOutput("Couldn't create indexedDB.");
+            finishTest();
+        };
+        request._onupgradeneeded_ = function(event) {
+            let db = event.target.result;
+            let objStore = db.createObjectStore("test", {autoIncrement: true});
+            objStore.add("value");
+            callback();
+        }
+    }
+
+    const maxIntervals = 20;
+
+    let intervalCounterIDB;
+    let checkIDBCallback;
+    let checkIDBIntervalID;
+    let semaphoreIDBCheck = false;
+    function checkIDBDataStoreExists(isAfterDeletion, callback) {
+        let request;
+        intervalCounterIDB = 0;
+        checkIDBCallback = callback;
+        if (!isAfterDeletion) {
+            // Check until there is a IDB.
+            checkIDBIntervalID = setInterval(function() {
+                if (semaphoreIDBCheck)
+                    return;
+                semaphoreIDBCheck = true;
+
+                if (++intervalCounterIDB >= maxIntervals) {
+                    clearInterval(checkIDBIntervalID);
+                    addOutput("Before deletion: IDB entry does not exist.");
+                    semaphoreIDBCheck = false;
+                    checkIDBCallback();
+                } else {
+                    request = indexedDB.open(dbName);
+                    request._onerror_ = function () {
+                        clearInterval(checkIDBIntervalID);
+                        addOutput("Couldn't open indexedDB.");
+                        semaphoreIDBCheck = false;
+                        finishTest();
+                    };
+                    request._onupgradeneeded_ = function () {
+                        // Let the next interval check again.
+                        semaphoreIDBCheck = false;
+                    };
+                    request._onsuccess_ = function () {
+                        clearInterval(checkIDBIntervalID);
+                        addOutput("Before deletion: IDB entry does exist.");
+                        semaphoreIDBCheck = false;
+                        checkIDBCallback();
+                    };
+                }
+            }, 200);
+        } else {
+            // Check until there is a IDB.
+            checkIDBIntervalID = setInterval(function () {
+                if (semaphoreIDBCheck)
+                    return;
+                semaphoreIDBCheck = true;
+
+                if (++intervalCounterIDB >= maxIntervals) {
+                    clearInterval(checkIDBIntervalID);
+                    addOutput("After deletion: IDB entry checks exhausted.");
+                    semaphoreIDBCheck = false;
+                    checkIDBCallback();
+                } else {
+                    request = indexedDB.open(dbName);
+                    request._onerror_ = function () {
+                        clearInterval(checkIDBIntervalID);
+                        addOutput("Couldn't open indexedDB.");
+                        semaphoreIDBCheck = false;
+                        finishTest();
+                    };
+                    request._onupgradeneeded_ = function () {
+                        // Let the next interval check again.
+                        semaphoreIDBCheck = false;
+                    };
+                    request._onsuccess_ = function () {
+                        clearInterval(checkIDBIntervalID);
+                        addOutput("After deletion: IDB entry does exist.");
+                        semaphoreIDBCheck = false;
+                        checkIDBCallback();
+                    };
+                }
+            }, 200);
+        }
+    }
+
+    let intervalCounterLocalStorage;
+    let checkLocalStorageCallback;
+    let checkLocalStorageIntervalID;
+    const localStorageName = "test";
+    const localStorageValue = "value";
+    function checkLocalStorageExists(isAfterDeletion, callback) {
+        intervalCounterLocalStorage = 0;
+        checkLocalStorageCallback = callback;
+        if (!isAfterDeletion) {
+            // Check until there is LocalStorage.
+            checkLocalStorageIntervalID = setInterval(function() {
+                if (++intervalCounterLocalStorage >= maxIntervals) {
+                    clearInterval(checkLocalStorageIntervalID);
+                    let value = localStorage.getItem(localStorageName);
+                    addOutput("Before deletion: LocalStorage entry " + (value === localStorageValue ? "does" : "does not") + " exist.");
+                    checkLocalStorageCallback();
+                } else if (testRunner.isStatisticsHasLocalStorage(originUnderTest)) {
+                    clearInterval(checkLocalStorageIntervalID);
+                    let value = localStorage.getItem(localStorageName);
+                    addOutput("Before deletion: LocalStorage entry " + (value === localStorageValue ? "does" : "does not") + " exist.");
+                    checkLocalStorageCallback();
+                }
+            }, 100);
+        } else {
+            // Check until there is no LocalStorage.
+            checkLocalStorageIntervalID = setInterval(function() {
+                if (++intervalCounterLocalStorage >= maxIntervals) {
+                    clearInterval(checkLocalStorageIntervalID);
+                    let value = localStorage.getItem(localStorageName);
+                    addOutput("After deletion: LocalStorage entry " + (value === localStorageValue ? "does" : "does not") + " exist.");
+                    checkLocalStorageCallback();
+                } else if (!testRunner.isStatisticsHasLocalStorage(originUnderTest)) {
+                    clearInterval(checkLocalStorageIntervalID);
+                    let value = localStorage.getItem(localStorageName);
+                    addOutput("After deletion: LocalStorage entry " + (value === localStorageValue ? "does" : "does not") + " exist.");
+                    checkLocalStorageCallback();
+                }
+            }, 100);
+        }
+    }
+
+    async function writeWebsiteDataAndContinue() {
+        // Write cookies.
+        await fetch("/cookies/resources/set-http-only-cookie.php?cookieName=" + httpOnlyCookieName, { credentials: "same-origin" });
+        await fetch("/cookies/resources/setCookies.cgi", { headers: { "Set-Cookie": serverSideCookieName + "=1; path=/;" }, credentials: "same-origin" });
+        document.cookie = clientSideCookieName + "=1";
+
+        checkCookies(false);
+
+        // Write LocalStorage
+        localStorage.setItem(localStorageName, localStorageValue);
+        checkLocalStorageExists(false, function() {
+
+            // Write IndexedDB.
+            createIDBDataStore(function () {
+                checkIDBDataStoreExists(false, function() {
+                    addLinebreakToOutput();
+                    processWebsiteDataAndContinue();
+                });
+            });
+        });
+    }
+
+    function processWebsiteDataAndContinue() {
+        testRunner.installStatisticsDidScanDataRecordsCallback(checkWebsiteDataAndContinue);
+        testRunner.statisticsProcessStatisticsAndDataRecords();
+    }
+
+    function checkWebsiteDataAndContinue() {
+        checkCookies(true);
+        checkLocalStorageExists(true, function () {
+            checkIDBDataStoreExists(true, finishTest);
+        });
+    }
+
+    function finishTest() {
+        resetCookies();
+        testRunner.setStatisticsFirstPartyWebsiteDataRemovalMode(false, function() {
+            setEnableFeature(false, function() {
+                testRunner.notifyDone();
+            });
+        });
+    }
+
+    const originUnderTest  = "http://127.0.0.1:8000";
+    function runTest() {
+        setEnableFeature(true, function () {
+            testRunner.setAppBoundDomains([ originUnderTest ], function() {
+                testRunner.setStatisticsFirstPartyWebsiteDataRemovalMode(true, function() {
+                    testRunner.setStatisticsPrevalentResource(originUnderTest, true, function() {
+                        if (!testRunner.isStatisticsPrevalentResource(originUnderTest))
+                            addOutput("FAIL: " + originUnderTest + " didn't get classified as prevalent.");
+                        writeWebsiteDataAndContinue();
+                    });
+                });
+            });
+        });
+    }
+</script>
+</body>
+</html>

Added: trunk/LayoutTests/http/tests/resourceLoadStatistics/exemptDomains/app-bound-domains-exempt-from-website-data-deletion-expected.txt (0 => 261242)


--- trunk/LayoutTests/http/tests/resourceLoadStatistics/exemptDomains/app-bound-domains-exempt-from-website-data-deletion-expected.txt	                        (rev 0)
+++ trunk/LayoutTests/http/tests/resourceLoadStatistics/exemptDomains/app-bound-domains-exempt-from-website-data-deletion-expected.txt	2020-05-06 18:44:26 UTC (rev 261242)
@@ -0,0 +1,15 @@
+Check that website data does not get removed after a period of no user interaction if the website is app-bound.
+
+Before deletion: Client-side cookie exists.
+Before deletion: HttpOnly cookie exists.
+Before deletion: Regular server-side cookie exists.
+Before deletion: LocalStorage entry does exist.
+Before deletion: IDB entry does exist.
+
+After deletion: HttpOnly cookie exists.
+After deletion: Client-side cookie exists.
+After deletion: Regular server-side cookie exists.
+After deletion: LocalStorage entry does exist.
+After deletion: IDB entry does exist.
+
+

Added: trunk/LayoutTests/http/tests/resourceLoadStatistics/exemptDomains/app-bound-domains-exempt-from-website-data-deletion.html (0 => 261242)


--- trunk/LayoutTests/http/tests/resourceLoadStatistics/exemptDomains/app-bound-domains-exempt-from-website-data-deletion.html	                        (rev 0)
+++ trunk/LayoutTests/http/tests/resourceLoadStatistics/exemptDomains/app-bound-domains-exempt-from-website-data-deletion.html	2020-05-06 18:44:26 UTC (rev 261242)
@@ -0,0 +1,255 @@
+<!DOCTYPE html>
+<html>
+<head>
+    <script src=""
+    <script src=""
+</head>
+<body _onload_="setTimeout('runTest()', 0)">
+<div id="description">Check that website data does not get removed after a period of no user interaction if the website is app-bound.</div>
+<br>
+<div id="output"></div>
+<br>
+<script>
+    testRunner.waitUntilDone();
+    testRunner.dumpAsText();
+
+    const httpOnlyCookieName = "http-only-cookie";
+    const serverSideCookieName = "server-side-cookie";
+    const clientSideCookieName = "client-side-cookie";
+
+    function sortStringArray(a, b) {
+        a = a.toLowerCase();
+        b = b.toLowerCase();
+
+        return a > b ? 1 : b > a ? -1 : 0;
+    }
+
+    function addLinebreakToOutput() {
+        let element = document.createElement("br");
+        output.appendChild(element);
+    }
+
+    function addOutput(message) {
+        let element = document.createElement("div");
+        element.innerText = message;
+        output.appendChild(element);
+    }
+
+    function checkCookies(isAfterDeletion) {
+        let unsortedTestPassedMessages = [];
+        let cookies = internals.getCookies();
+        if (!cookies.length)
+            addOutput((isAfterDeletion ? "After" : "Before") + " script-accessible deletion: No cookies found.");
+        for (let cookie of cookies) {
+            switch (cookie.name) {
+                case httpOnlyCookieName:
+                    unsortedTestPassedMessages.push((isAfterDeletion ? "After" : "Before") + " deletion: " + (isAfterDeletion ? " " : "") + "HttpOnly cookie exists.");
+                    break;
+                case serverSideCookieName:
+                    unsortedTestPassedMessages.push((isAfterDeletion ? "After" : "Before") + " deletion: Regular server-side cookie exists.");
+                    break;
+                case clientSideCookieName:
+                    unsortedTestPassedMessages.push((isAfterDeletion ? "After" : "Before") + " deletion: Client-side cookie exists.");
+                    break;
+            }
+        }
+        let sortedTestPassedMessages = unsortedTestPassedMessages.sort(sortStringArray);
+        for (let testPassedMessage of sortedTestPassedMessages) {
+            addOutput(testPassedMessage);
+        }
+    }
+
+    const dbName = "TestDatabase";
+
+    function createIDBDataStore(callback) {
+        let request = indexedDB.open(dbName);
+        request._onerror_ = function() {
+            addOutput("Couldn't create indexedDB.");
+            finishTest();
+        };
+        request._onupgradeneeded_ = function(event) {
+            let db = event.target.result;
+            let objStore = db.createObjectStore("test", {autoIncrement: true});
+            objStore.add("value");
+            callback();
+        }
+    }
+
+    const maxIntervals = 20;
+
+    let intervalCounterIDB;
+    let checkIDBCallback;
+    let checkIDBIntervalID;
+    let semaphoreIDBCheck = false;
+    function checkIDBDataStoreExists(isAfterDeletion, callback) {
+        let request;
+        intervalCounterIDB = 0;
+        checkIDBCallback = callback;
+        if (!isAfterDeletion) {
+            // Check until there is a IDB.
+            checkIDBIntervalID = setInterval(function() {
+                if (semaphoreIDBCheck)
+                    return;
+                semaphoreIDBCheck = true;
+
+                if (++intervalCounterIDB >= maxIntervals) {
+                    clearInterval(checkIDBIntervalID);
+                    addOutput("Before deletion: IDB entry does not exist.");
+                    semaphoreIDBCheck = false;
+                    checkIDBCallback();
+                } else {
+                    request = indexedDB.open(dbName);
+                    request._onerror_ = function () {
+                        clearInterval(checkIDBIntervalID);
+                        addOutput("Couldn't open indexedDB.");
+                        semaphoreIDBCheck = false;
+                        finishTest();
+                    };
+                    request._onupgradeneeded_ = function () {
+                        // Let the next interval check again.
+                        semaphoreIDBCheck = false;
+                    };
+                    request._onsuccess_ = function () {
+                        clearInterval(checkIDBIntervalID);
+                        addOutput("Before deletion: IDB entry does exist.");
+                        semaphoreIDBCheck = false;
+                        checkIDBCallback();
+                    };
+                }
+            }, 200);
+        } else {
+            // Check until there is a IDB.
+            checkIDBIntervalID = setInterval(function () {
+                if (semaphoreIDBCheck)
+                    return;
+                semaphoreIDBCheck = true;
+
+                if (++intervalCounterIDB >= maxIntervals) {
+                    clearInterval(checkIDBIntervalID);
+                    addOutput("After deletion: IDB entry checks exhausted.");
+                    semaphoreIDBCheck = false;
+                    checkIDBCallback();
+                } else {
+                    request = indexedDB.open(dbName);
+                    request._onerror_ = function () {
+                        clearInterval(checkIDBIntervalID);
+                        addOutput("Couldn't open indexedDB.");
+                        semaphoreIDBCheck = false;
+                        finishTest();
+                    };
+                    request._onupgradeneeded_ = function () {
+                        // Let the next interval check again.
+                        semaphoreIDBCheck = false;
+                    };
+                    request._onsuccess_ = function () {
+                        clearInterval(checkIDBIntervalID);
+                        addOutput("After deletion: IDB entry does exist.");
+                        semaphoreIDBCheck = false;
+                        checkIDBCallback();
+                    };
+                }
+            }, 200);
+        }
+    }
+
+    let intervalCounterLocalStorage;
+    let checkLocalStorageCallback;
+    let checkLocalStorageIntervalID;
+    const localStorageName = "test";
+    const localStorageValue = "value";
+    function checkLocalStorageExists(isAfterDeletion, callback) {
+        intervalCounterLocalStorage = 0;
+        checkLocalStorageCallback = callback;
+        if (!isAfterDeletion) {
+            // Check until there is LocalStorage.
+            checkLocalStorageIntervalID = setInterval(function() {
+                if (++intervalCounterLocalStorage >= maxIntervals) {
+                    clearInterval(checkLocalStorageIntervalID);
+                    let value = localStorage.getItem(localStorageName);
+                    addOutput("Before deletion: LocalStorage entry " + (value === localStorageValue ? "does" : "does not") + " exist.");
+                    checkLocalStorageCallback();
+                } else if (testRunner.isStatisticsHasLocalStorage(originUnderTest)) {
+                    clearInterval(checkLocalStorageIntervalID);
+                    let value = localStorage.getItem(localStorageName);
+                    addOutput("Before deletion: LocalStorage entry " + (value === localStorageValue ? "does" : "does not") + " exist.");
+                    checkLocalStorageCallback();
+                }
+            }, 100);
+        } else {
+            // Check until there is no LocalStorage.
+            checkLocalStorageIntervalID = setInterval(function() {
+                if (++intervalCounterLocalStorage >= maxIntervals) {
+                    clearInterval(checkLocalStorageIntervalID);
+                    let value = localStorage.getItem(localStorageName);
+                    addOutput("After deletion: LocalStorage entry " + (value === localStorageValue ? "does" : "does not") + " exist.");
+                    checkLocalStorageCallback();
+                } else if (!testRunner.isStatisticsHasLocalStorage(originUnderTest)) {
+                    clearInterval(checkLocalStorageIntervalID);
+                    let value = localStorage.getItem(localStorageName);
+                    addOutput("After deletion: LocalStorage entry " + (value === localStorageValue ? "does" : "does not") + " exist.");
+                    checkLocalStorageCallback();
+                }
+            }, 100);
+        }
+    }
+
+    async function writeWebsiteDataAndContinue() {
+        // Write cookies.
+        await fetch("/cookies/resources/set-http-only-cookie.php?cookieName=" + httpOnlyCookieName, { credentials: "same-origin" });
+        await fetch("/cookies/resources/setCookies.cgi", { headers: { "Set-Cookie": serverSideCookieName + "=1; path=/;" }, credentials: "same-origin" });
+        document.cookie = clientSideCookieName + "=1";
+
+        checkCookies(false);
+
+        // Write LocalStorage
+        localStorage.setItem(localStorageName, localStorageValue);
+        checkLocalStorageExists(false, function() {
+
+            // Write IndexedDB.
+            createIDBDataStore(function () {
+                checkIDBDataStoreExists(false, function() {
+                    addLinebreakToOutput();
+                    processWebsiteDataAndContinue();
+                });
+            });
+        });
+    }
+
+    function processWebsiteDataAndContinue() {
+        testRunner.installStatisticsDidScanDataRecordsCallback(checkWebsiteDataAndContinue);
+        testRunner.statisticsProcessStatisticsAndDataRecords();
+    }
+
+    function checkWebsiteDataAndContinue() {
+        checkCookies(true);
+        checkLocalStorageExists(true, function () {
+            checkIDBDataStoreExists(true, finishTest);
+        });
+    }
+
+    function finishTest() {
+        resetCookies();
+        testRunner.setStatisticsFirstPartyWebsiteDataRemovalMode(false, function() {
+            setEnableFeature(false, function() {
+                testRunner.notifyDone();
+            });
+        });
+    }
+
+    const originUnderTest  = "http://127.0.0.1:8000";
+    function runTest() {
+        setEnableFeature(true, function () {
+            testRunner.setAppBoundDomains([ originUnderTest ], function() {
+                testRunner.setStatisticsFirstPartyWebsiteDataRemovalMode(true, function() {
+                    testRunner.setStatisticsPrevalentResource(originUnderTest, true, function() {
+                        if (!testRunner.isStatisticsPrevalentResource(originUnderTest))
+                            addOutput("FAIL: " + originUnderTest + " didn't get classified as prevalent.");
+                        writeWebsiteDataAndContinue();
+                    });
+                });
+            });
+        });
+    }
+</script>
+</body>
+</html>

Modified: trunk/Source/WebCore/ChangeLog (261241 => 261242)


--- trunk/Source/WebCore/ChangeLog	2020-05-06 18:43:19 UTC (rev 261241)
+++ trunk/Source/WebCore/ChangeLog	2020-05-06 18:44:26 UTC (rev 261242)
@@ -1,3 +1,25 @@
+2020-05-06  John Wilander  <wilan...@apple.com>
+
+        Exempt app-bound domains from ITP's website data deletion and third-party cookie blocking between themselves
+        https://bugs.webkit.org/show_bug.cgi?id=210674
+        <rdar://problem/61950767>
+
+        Reviewed by Chris Dumez.
+
+        This change adds functionality to NetworkStorageSession to allow it to exempt
+        app-bound domains from third-party cookie blocking.
+
+        Tests: http/tests/resourceLoadStatistics/exemptDomains/app-bound-domains-exempt-from-cookie-blocking-between-each-other.html
+               http/tests/resourceLoadStatistics/exemptDomains/app-bound-domains-exempt-from-website-data-deletion-database.html
+               http/tests/resourceLoadStatistics/exemptDomains/app-bound-domains-exempt-from-website-data-deletion.html
+
+        * platform/network/NetworkStorageSession.cpp:
+        (WebCore::NetworkStorageSession::shouldBlockCookies const):
+        (WebCore::NetworkStorageSession::shouldExemptDomainPairFromThirdPartyCookieBlocking const):
+        (WebCore::NetworkStorageSession::setAppBoundDomains):
+        (WebCore::NetworkStorageSession::resetAppBoundDomains):
+        * platform/network/NetworkStorageSession.h:
+
 2020-05-06  Antti Koivisto  <an...@apple.com>
 
         Add basic support for generating accurate wheel event listener region

Modified: trunk/Source/WebCore/platform/network/NetworkStorageSession.cpp (261241 => 261242)


--- trunk/Source/WebCore/platform/network/NetworkStorageSession.cpp	2020-05-06 18:43:19 UTC (rev 261241)
+++ trunk/Source/WebCore/platform/network/NetworkStorageSession.cpp	2020-05-06 18:44:26 UTC (rev 261242)
@@ -134,6 +134,8 @@
     switch (m_thirdPartyCookieBlockingMode) {
     case ThirdPartyCookieBlockingMode::All:
         return true;
+    case ThirdPartyCookieBlockingMode::AllExceptBetweenAppBoundDomains:
+        return !shouldExemptDomainPairFromThirdPartyCookieBlocking(firstPartyDomain, resourceDomain);
     case ThirdPartyCookieBlockingMode::AllOnSitesWithoutUserInteraction:
         if (!hasHadUserInteractionAsFirstParty(firstPartyDomain))
             return true;
@@ -145,6 +147,15 @@
     return false;
 }
 
+bool NetworkStorageSession::shouldExemptDomainPairFromThirdPartyCookieBlocking(const RegistrableDomain& topFrameDomain, const RegistrableDomain& resourceDomain) const
+{
+    ASSERT(topFrameDomain != resourceDomain);
+    if (topFrameDomain.isEmpty() || resourceDomain.isEmpty())
+        return false;
+
+    return topFrameDomain == resourceDomain || (m_appBoundDomains.contains(topFrameDomain) && m_appBoundDomains.contains(resourceDomain));
+}
+
 Optional<Seconds> NetworkStorageSession::maxAgeCacheCap(const ResourceRequest& request)
 {
     if (m_cacheMaxAgeCapForPrevalentResources && shouldBlockCookies(request, WTF::nullopt, WTF::nullopt))
@@ -292,6 +303,16 @@
     m_thirdPartyCookieBlockingMode = blockingMode;
 }
 
+void NetworkStorageSession::setAppBoundDomains(HashSet<RegistrableDomain>&& domains)
+{
+    m_appBoundDomains = WTFMove(domains);
+}
+
+void NetworkStorageSession::resetAppBoundDomains()
+{
+    m_appBoundDomains.clear();
+}
+
 Optional<Seconds> NetworkStorageSession::clientSideCookieCap(const RegistrableDomain& firstParty, Optional<PageIdentifier> pageID) const
 {
     if (!m_ageCapForClientSideCookies || !pageID || m_navigatedToWithLinkDecorationByPrevalentResource.isEmpty())

Modified: trunk/Source/WebCore/platform/network/NetworkStorageSession.h (261241 => 261242)


--- trunk/Source/WebCore/platform/network/NetworkStorageSession.h	2020-05-06 18:43:19 UTC (rev 261241)
+++ trunk/Source/WebCore/platform/network/NetworkStorageSession.h	2020-05-06 18:44:26 UTC (rev 261242)
@@ -77,7 +77,7 @@
 enum class HTTPCookieAcceptPolicy : uint8_t;
 enum class IncludeSecureCookies : bool;
 enum class IncludeHttpOnlyCookies : bool;
-enum class ThirdPartyCookieBlockingMode : uint8_t { All, AllOnSitesWithoutUserInteraction, OnlyAccordingToPerDomainPolicy };
+enum class ThirdPartyCookieBlockingMode : uint8_t { All, AllExceptBetweenAppBoundDomains, AllOnSitesWithoutUserInteraction, OnlyAccordingToPerDomainPolicy };
 enum class SameSiteStrictEnforcementEnabled : bool { Yes, No };
 enum class FirstPartyWebsiteDataRemovalMode : uint8_t { AllButCookies, None, AllButCookiesLiveOnTestingTimeout, AllButCookiesReproTestingTimeout };
 enum class ShouldAskITP : bool { No, Yes };
@@ -95,6 +95,9 @@
 class NetworkStorageSession {
     WTF_MAKE_NONCOPYABLE(NetworkStorageSession); WTF_MAKE_FAST_ALLOCATED;
 public:
+    using TopFrameDomain = WebCore::RegistrableDomain;
+    using SubResourceDomain = WebCore::RegistrableDomain;
+
     WEBCORE_EXPORT static void permitProcessToUseCookieAPI(bool);
     WEBCORE_EXPORT static bool processMayUseCookieAPI();
 
@@ -196,6 +199,8 @@
     WEBCORE_EXPORT void didCommitCrossSiteLoadWithDataTransferFromPrevalentResource(const RegistrableDomain& toDomain, PageIdentifier);
     WEBCORE_EXPORT void resetCrossSiteLoadsWithLinkDecorationForTesting();
     WEBCORE_EXPORT void setThirdPartyCookieBlockingMode(ThirdPartyCookieBlockingMode);
+    WEBCORE_EXPORT void setAppBoundDomains(HashSet<RegistrableDomain>&&);
+    WEBCORE_EXPORT void resetAppBoundDomains();
 #endif
 
 private:
@@ -241,7 +246,8 @@
 
 #if ENABLE(RESOURCE_LOAD_STATISTICS)
     bool m_isResourceLoadStatisticsEnabled = false;
-    Optional<Seconds> clientSideCookieCap(const RegistrableDomain& firstParty, Optional<PageIdentifier>) const;
+    Optional<Seconds> clientSideCookieCap(const TopFrameDomain&, Optional<PageIdentifier>) const;
+    bool shouldExemptDomainPairFromThirdPartyCookieBlocking(const TopFrameDomain&, const SubResourceDomain&) const;
     HashSet<RegistrableDomain> m_registrableDomainsToBlockAndDeleteCookiesFor;
     HashSet<RegistrableDomain> m_registrableDomainsToBlockButKeepCookiesFor;
     HashSet<RegistrableDomain> m_registrableDomainsWithUserInteractionAsFirstParty;
@@ -253,6 +259,7 @@
     HashMap<WebCore::PageIdentifier, RegistrableDomain> m_navigatedToWithLinkDecorationByPrevalentResource;
     bool m_navigationWithLinkDecorationTestMode = false;
     ThirdPartyCookieBlockingMode m_thirdPartyCookieBlockingMode { ThirdPartyCookieBlockingMode::All };
+    HashSet<RegistrableDomain> m_appBoundDomains;
 #endif
 
 #if PLATFORM(COCOA)
@@ -277,6 +284,7 @@
     using values = EnumValues<
         WebCore::ThirdPartyCookieBlockingMode,
         WebCore::ThirdPartyCookieBlockingMode::All,
+        WebCore::ThirdPartyCookieBlockingMode::AllExceptBetweenAppBoundDomains,
         WebCore::ThirdPartyCookieBlockingMode::AllOnSitesWithoutUserInteraction,
         WebCore::ThirdPartyCookieBlockingMode::OnlyAccordingToPerDomainPolicy
     >;

Modified: trunk/Source/WebKit/ChangeLog (261241 => 261242)


--- trunk/Source/WebKit/ChangeLog	2020-05-06 18:43:19 UTC (rev 261241)
+++ trunk/Source/WebKit/ChangeLog	2020-05-06 18:44:26 UTC (rev 261242)
@@ -1,3 +1,99 @@
+2020-05-06  John Wilander  <wilan...@apple.com>
+
+        Exempt app-bound domains from ITP's website data deletion and third-party cookie blocking between themselves
+        https://bugs.webkit.org/show_bug.cgi?id=210674
+        <rdar://problem/61950767>
+
+        Reviewed by Chris Dumez.
+
+        This change forwards information about app-bound domains to ITP and web
+        processes so that they can be exempt from website data deletion and
+        third-party cookie blocking between themselves.
+
+        App-bound domains are configured statically and apply to all website
+        data stores. Therefore the setting needs to be forwarded to all
+        website data stores and ITP functionality in all network and web
+        content processes. This is done through the new static function
+        WebsiteDataStore::setAppBoundDomainsForITPIfInitialized().
+
+        Since app-bound domains are loaded lazily from disk and on a background
+        thread, this patch forwards them in ResourceLoadStatisticsParameters if
+        they've already been loaded. Then every time app-bound domains are
+        updated, they are forwarded to ITP. This ensures that ITP will have them
+        as soon as possible.
+
+        Setting app-bound domains for the purposes of ITP automatically switches
+        ITP's cookie blocking policy to the new
+        WebCore::ThirdPartyCookieBlockingMode::AllExceptBetweenAppBoundDomains.
+        This is done in WebResourceLoadStatisticsStore::setAppBoundDomains().
+
+        The C API changes are for test purposes.
+
+        * NetworkProcess/Classifier/ResourceLoadStatisticsDatabaseStore.cpp:
+        * NetworkProcess/Classifier/ResourceLoadStatisticsMemoryStore.cpp:
+        (WebKit::ResourceLoadStatisticsMemoryStore::registrableDomainsToDeleteOrRestrictWebsiteDataFor):
+        * NetworkProcess/Classifier/ResourceLoadStatisticsStore.cpp:
+        (WebKit::ResourceLoadStatisticsStore::setAppBoundDomains):
+        (WebKit::ResourceLoadStatisticsStore::resetParametersToDefaultValues):
+        (WebKit::ResourceLoadStatisticsStore::shouldExemptFromWebsiteDataDeletion const):
+        * NetworkProcess/Classifier/ResourceLoadStatisticsStore.h:
+        (WebKit::ResourceLoadStatisticsStore::standaloneApplicationDomain const): Deleted.
+        * NetworkProcess/Classifier/WebResourceLoadStatisticsStore.cpp:
+        (WebKit::WebResourceLoadStatisticsStore::setAppBoundDomains):
+        (WebKit::WebResourceLoadStatisticsStore::resetParametersToDefaultValues):
+        * NetworkProcess/Classifier/WebResourceLoadStatisticsStore.h:
+        * NetworkProcess/NetworkProcess.cpp:
+        (WebKit::NetworkProcess::setAppBoundDomainsForResourceLoadStatistics):
+        (WebKit::NetworkProcess::setThirdPartyCookieBlockingMode):
+        (WebKit::NetworkProcess::setShouldBlockThirdPartyCookiesForTesting): Deleted.
+            Renamed setThirdPartyCookieBlockingMode.
+        * NetworkProcess/NetworkProcess.h:
+        * NetworkProcess/NetworkProcess.messages.in:
+        * NetworkProcess/cocoa/NetworkSessionCocoa.mm:
+        (WebKit::NetworkSessionCocoa::NetworkSessionCocoa):
+        * Shared/ResourceLoadStatisticsParameters.h:
+        (WebKit::ResourceLoadStatisticsParameters::encode const):
+        (WebKit::ResourceLoadStatisticsParameters::decode):
+        * UIProcess/API/C/WKWebsiteDataStoreRef.cpp:
+        (WKWebsiteDataStoreSetAppBoundDomainsForTesting):
+        * UIProcess/API/C/WKWebsiteDataStoreRef.h:
+        * UIProcess/Network/NetworkProcessProxy.cpp:
+        (WebKit::NetworkProcessProxy::setAppBoundDomainsForResourceLoadStatistics):
+        (WebKit::NetworkProcessProxy::setThirdPartyCookieBlockingMode):
+        (WebKit::NetworkProcessProxy::setShouldBlockThirdPartyCookiesForTesting): Deleted.
+            Renamed setThirdPartyCookieBlockingMode.
+        * UIProcess/Network/NetworkProcessProxy.h:
+        * UIProcess/WebProcessPool.cpp:
+        (WebKit::WebProcessPool::ensureNetworkProcess):
+        * UIProcess/WebProcessProxy.cpp:
+        (WebKit::WebProcessProxy::setThirdPartyCookieBlockingMode):
+        (WebKit::WebProcessProxy::setShouldBlockThirdPartyCookiesForTesting): Deleted.
+            Renamed setThirdPartyCookieBlockingMode.
+        * UIProcess/WebProcessProxy.h:
+        * UIProcess/WebsiteData/Cocoa/WebsiteDataStoreCocoa.mm:
+        (WebKit::WebsiteDataStore::initializeAppBoundDomains):
+        (WebKit::WebsiteDataStore::ensureAppBoundDomains const):
+        (WebKit::WebsiteDataStore::appBoundDomainsIfInitialized):
+            This function allows fetching of app-bound domains without triggering
+            the lazy loading. This is just to allow speculative configuration of ITP
+            right when it's created — if any app-bound domains are already configured,
+            forward them to ITP via ResourceLoadStatisticsParameters.
+        (WebKit::WebsiteDataStore::setAppBoundDomainsForTesting):
+            This function is Cocoa-specific and only accepts localhost and 127.0.0.1
+            to be configured as app-bound domains.
+        * UIProcess/WebsiteData/WebsiteDataStore.cpp:
+        (WebKit::WebsiteDataStore::setResourceLoadStatisticsShouldBlockThirdPartyCookiesForTesting):
+        (WebKit::WebsiteDataStore::setThirdPartyCookieBlockingMode):
+        (WebKit::WebsiteDataStore::parameters):
+        (WebKit::WebsiteDataStore::forwardAppBoundDomainsToITPIfInitialized):
+        (WebKit::WebsiteDataStore::setAppBoundDomainsForITP):
+        * UIProcess/WebsiteData/WebsiteDataStore.h:
+        * WebProcess/WebProcess.cpp:
+        (WebKit::WebProcess::setThirdPartyCookieBlockingMode):
+        (WebKit::WebProcess::setShouldBlockThirdPartyCookiesForTesting): Deleted.
+        * WebProcess/WebProcess.h:
+        * WebProcess/WebProcess.messages.in:
+
 2020-05-06  Daniel Bates  <daba...@apple.com>
 
         [iOS] ASSERTION FAILED: !(_keyboardFlags & WebEventKeyboardInputModifierFlagsChanged) in -[WebEvent charactersIgnoringModifiers] when pressing modifier on PDF

Modified: trunk/Source/WebKit/NetworkProcess/Classifier/ResourceLoadStatisticsDatabaseStore.cpp (261241 => 261242)


--- trunk/Source/WebKit/NetworkProcess/Classifier/ResourceLoadStatisticsDatabaseStore.cpp	2020-05-06 18:43:19 UTC (rev 261241)
+++ trunk/Source/WebKit/NetworkProcess/Classifier/ResourceLoadStatisticsDatabaseStore.cpp	2020-05-06 18:44:26 UTC (rev 261242)
@@ -2621,7 +2621,7 @@
     Vector<DomainData> domains = this->domains();
     Vector<unsigned> domainIDsToClearGrandfathering;
     for (auto& statistic : domains) {
-        if (statistic.registrableDomain == standaloneApplicationDomain())
+        if (shouldExemptFromWebsiteDataDeletion(statistic.registrableDomain))
             continue;
         oldestUserInteraction = std::min(oldestUserInteraction, statistic.mostRecentUserInteractionTime);
         if (shouldRemoveAllWebsiteDataFor(statistic, shouldCheckForGrandfathering)) {

Modified: trunk/Source/WebKit/NetworkProcess/Classifier/ResourceLoadStatisticsMemoryStore.cpp (261241 => 261242)


--- trunk/Source/WebKit/NetworkProcess/Classifier/ResourceLoadStatisticsMemoryStore.cpp	2020-05-06 18:43:19 UTC (rev 261241)
+++ trunk/Source/WebKit/NetworkProcess/Classifier/ResourceLoadStatisticsMemoryStore.cpp	2020-05-06 18:44:26 UTC (rev 261242)
@@ -980,7 +980,7 @@
     auto oldestUserInteraction = now;
     RegistrableDomainsToDeleteOrRestrictWebsiteDataFor toDeleteOrRestrictFor;
     for (auto& statistic : m_resourceStatisticsMap.values()) {
-        if (statistic.registrableDomain == standaloneApplicationDomain())
+        if (shouldExemptFromWebsiteDataDeletion(statistic.registrableDomain))
             continue;
         oldestUserInteraction = std::min(oldestUserInteraction, statistic.mostRecentUserInteractionTime);
         if (shouldRemoveAllWebsiteDataFor(statistic, shouldCheckForGrandfathering)) {

Modified: trunk/Source/WebKit/NetworkProcess/Classifier/ResourceLoadStatisticsStore.cpp (261241 => 261242)


--- trunk/Source/WebKit/NetworkProcess/Classifier/ResourceLoadStatisticsStore.cpp	2020-05-06 18:43:19 UTC (rev 261241)
+++ trunk/Source/WebKit/NetworkProcess/Classifier/ResourceLoadStatisticsStore.cpp	2020-05-06 18:44:26 UTC (rev 261242)
@@ -317,6 +317,11 @@
     m_debugManualPrevalentResource = domain;
 }
 
+void ResourceLoadStatisticsStore::setAppBoundDomains(HashSet<RegistrableDomain>&& domains)
+{
+    m_appBoundDomains = WTFMove(domains);
+}
+
 void ResourceLoadStatisticsStore::scheduleStatisticsProcessingRequestIfNecessary()
 {
     ASSERT(!RunLoop::isMain());
@@ -568,6 +573,7 @@
     ASSERT(!RunLoop::isMain());
 
     m_parameters = { };
+    m_appBoundDomains.clear();
 }
 
 void ResourceLoadStatisticsStore::logTestingEvent(const String& event)
@@ -652,6 +658,11 @@
         RELEASE_LOG_INFO(ITPDebug, "%" PUBLIC_LOG_STRING " to (%{public}d of %u): %" PUBLIC_LOG_STRING ".", action, batchNumber, numberOfBatches, domainsToString(batch).utf8().data());
 }
 
+bool ResourceLoadStatisticsStore::shouldExemptFromWebsiteDataDeletion(const RegistrableDomain& domain) const
+{
+    return !domain.isEmpty() && (domain == m_standaloneApplicationDomain || m_appBoundDomains.contains(domain));
+}
+
 } // namespace WebKit
 
 #endif

Modified: trunk/Source/WebKit/NetworkProcess/Classifier/ResourceLoadStatisticsStore.h (261241 => 261242)


--- trunk/Source/WebKit/NetworkProcess/Classifier/ResourceLoadStatisticsStore.h	2020-05-06 18:43:19 UTC (rev 261241)
+++ trunk/Source/WebKit/NetworkProcess/Classifier/ResourceLoadStatisticsStore.h	2020-05-06 18:44:26 UTC (rev 261242)
@@ -169,6 +169,7 @@
     bool isSameSiteStrictEnforcementEnabled() const { return m_sameSiteStrictEnforcementEnabled == WebCore::SameSiteStrictEnforcementEnabled::Yes; };
     void setFirstPartyWebsiteDataRemovalMode(WebCore::FirstPartyWebsiteDataRemovalMode mode) { m_firstPartyWebsiteDataRemovalMode = mode; }
     void setStandaloneApplicationDomain(RegistrableDomain&& domain) { m_standaloneApplicationDomain = WTFMove(domain); }
+    void setAppBoundDomains(HashSet<RegistrableDomain>&&);
 
     virtual bool areAllThirdPartyCookiesBlockedUnder(const TopFrameDomain&) = 0;
     virtual void hasStorageAccess(const SubFrameDomain&, const TopFrameDomain&, Optional<WebCore::FrameIdentifier>, WebCore::PageIdentifier, CompletionHandler<void(bool)>&&) = 0;
@@ -249,7 +250,7 @@
     bool debugLoggingEnabled() const { return m_debugLoggingEnabled; };
     bool debugModeEnabled() const { return m_debugModeEnabled; }
     WebCore::FirstPartyWebsiteDataRemovalMode firstPartyWebsiteDataRemovalMode() const { return m_firstPartyWebsiteDataRemovalMode; }
-    RegistrableDomain standaloneApplicationDomain() const { return m_standaloneApplicationDomain; }
+    bool shouldExemptFromWebsiteDataDeletion(const RegistrableDomain&) const;
 
     static constexpr unsigned maxNumberOfRecursiveCallsInRedirectTraceBack { 50 };
     
@@ -291,6 +292,7 @@
     ShouldIncludeLocalhost m_shouldIncludeLocalhost { ShouldIncludeLocalhost::Yes };
     WebCore::FirstPartyWebsiteDataRemovalMode m_firstPartyWebsiteDataRemovalMode { WebCore::FirstPartyWebsiteDataRemovalMode::AllButCookies };
     RegistrableDomain m_standaloneApplicationDomain;
+    HashSet<RegistrableDomain> m_appBoundDomains;
 };
 
 } // namespace WebKit

Modified: trunk/Source/WebKit/NetworkProcess/Classifier/WebResourceLoadStatisticsStore.cpp (261241 => 261242)


--- trunk/Source/WebKit/NetworkProcess/Classifier/WebResourceLoadStatisticsStore.cpp	2020-05-06 18:43:19 UTC (rev 261241)
+++ trunk/Source/WebKit/NetworkProcess/Classifier/WebResourceLoadStatisticsStore.cpp	2020-05-06 18:44:26 UTC (rev 261242)
@@ -678,6 +678,33 @@
     });
 }
 
+void WebResourceLoadStatisticsStore::setAppBoundDomains(HashSet<RegistrableDomain>&& domains, CompletionHandler<void()>&& completionHandler)
+{
+    ASSERT(RunLoop::isMain());
+
+    if (isEphemeral() || domains.isEmpty()) {
+        completionHandler();
+        return;
+    }
+
+    auto domainsCopy = crossThreadCopy(domains);
+
+    if (m_networkSession) {
+        if (auto* storageSession = m_networkSession->networkStorageSession()) {
+            storageSession->setAppBoundDomains(WTFMove(domains));
+            storageSession->setThirdPartyCookieBlockingMode(ThirdPartyCookieBlockingMode::AllExceptBetweenAppBoundDomains);
+        }
+    }
+
+    postTask([this, domains = WTFMove(domainsCopy), completionHandler = WTFMove(completionHandler)]() mutable {
+        if (m_statisticsStore) {
+            m_statisticsStore->setAppBoundDomains(WTFMove(domains));
+            m_statisticsStore->setThirdPartyCookieBlockingMode(ThirdPartyCookieBlockingMode::AllExceptBetweenAppBoundDomains);
+        }
+        postTaskReply(WTFMove(completionHandler));
+    });
+}
+
 void WebResourceLoadStatisticsStore::didCreateNetworkProcess()
 {
     ASSERT(RunLoop::isMain());
@@ -1280,6 +1307,11 @@
         return;
     }
 
+    if (m_networkSession) {
+        if (auto* storageSession = m_networkSession->networkStorageSession())
+            storageSession->resetAppBoundDomains();
+    }
+
     postTask([this, completionHandler = WTFMove(completionHandler)]() mutable {
         if (m_statisticsStore)
             m_statisticsStore->resetParametersToDefaultValues();

Modified: trunk/Source/WebKit/NetworkProcess/Classifier/WebResourceLoadStatisticsStore.h (261241 => 261242)


--- trunk/Source/WebKit/NetworkProcess/Classifier/WebResourceLoadStatisticsStore.h	2020-05-06 18:43:19 UTC (rev 261241)
+++ trunk/Source/WebKit/NetworkProcess/Classifier/WebResourceLoadStatisticsStore.h	2020-05-06 18:44:26 UTC (rev 261242)
@@ -279,6 +279,7 @@
     void setSameSiteStrictEnforcementEnabled(WebCore::SameSiteStrictEnforcementEnabled);
     void setFirstPartyWebsiteDataRemovalMode(WebCore::FirstPartyWebsiteDataRemovalMode, CompletionHandler<void()>&&);
     void setStandaloneApplicationDomain(const RegistrableDomain&, CompletionHandler<void()>&&);
+    void setAppBoundDomains(HashSet<RegistrableDomain>&&, CompletionHandler<void()>&&);
     void didCreateNetworkProcess();
 
     void notifyResourceLoadStatisticsProcessed();

Modified: trunk/Source/WebKit/NetworkProcess/NetworkProcess.cpp (261241 => 261242)


--- trunk/Source/WebKit/NetworkProcess/NetworkProcess.cpp	2020-05-06 18:43:19 UTC (rev 261241)
+++ trunk/Source/WebKit/NetworkProcess/NetworkProcess.cpp	2020-05-06 18:44:26 UTC (rev 261242)
@@ -1307,6 +1307,18 @@
     completionHandler(result);
 }
 
+void NetworkProcess::setAppBoundDomainsForResourceLoadStatistics(PAL::SessionID sessionID, HashSet<WebCore::RegistrableDomain>&& appBoundDomains, CompletionHandler<void()>&& completionHandler)
+{
+    if (auto* networkSession = this->networkSession(sessionID)) {
+        if (auto* resourceLoadStatistics = networkSession->resourceLoadStatistics()) {
+            resourceLoadStatistics->setAppBoundDomains(WTFMove(appBoundDomains), WTFMove(completionHandler));
+            return;
+        }
+    }
+    ASSERT_NOT_REACHED();
+    completionHandler();
+}
+
 void NetworkProcess::setShouldDowngradeReferrerForTesting(bool enabled, CompletionHandler<void()>&& completionHandler)
 {
     forEachNetworkSession([enabled](auto& networkSession) {
@@ -1315,7 +1327,7 @@
     completionHandler();
 }
 
-void NetworkProcess::setShouldBlockThirdPartyCookiesForTesting(PAL::SessionID sessionID, WebCore::ThirdPartyCookieBlockingMode blockingMode, CompletionHandler<void()>&& completionHandler)
+void NetworkProcess::setThirdPartyCookieBlockingMode(PAL::SessionID sessionID, WebCore::ThirdPartyCookieBlockingMode blockingMode, CompletionHandler<void()>&& completionHandler)
 {
     if (auto* networkSession = this->networkSession(sessionID))
         networkSession->setThirdPartyCookieBlockingMode(blockingMode);

Modified: trunk/Source/WebKit/NetworkProcess/NetworkProcess.h (261241 => 261242)


--- trunk/Source/WebKit/NetworkProcess/NetworkProcess.h	2020-05-06 18:43:19 UTC (rev 261241)
+++ trunk/Source/WebKit/NetworkProcess/NetworkProcess.h	2020-05-06 18:44:26 UTC (rev 261242)
@@ -261,9 +261,10 @@
     void setCrossSiteLoadWithLinkDecorationForTesting(PAL::SessionID, const RegistrableDomain& fromDomain, const RegistrableDomain& toDomain, CompletionHandler<void()>&&);
     void resetCrossSiteLoadsWithLinkDecorationForTesting(PAL::SessionID, CompletionHandler<void()>&&);
     void hasIsolatedSession(PAL::SessionID, const WebCore::RegistrableDomain&, CompletionHandler<void(bool)>&&) const;
+    void setAppBoundDomainsForResourceLoadStatistics(PAL::SessionID, HashSet<WebCore::RegistrableDomain>&&, CompletionHandler<void()>&&);
     bool isITPDatabaseEnabled() const { return m_isITPDatabaseEnabled; }
     void setShouldDowngradeReferrerForTesting(bool, CompletionHandler<void()>&&);
-    void setShouldBlockThirdPartyCookiesForTesting(PAL::SessionID, WebCore::ThirdPartyCookieBlockingMode, CompletionHandler<void()>&&);
+    void setThirdPartyCookieBlockingMode(PAL::SessionID, WebCore::ThirdPartyCookieBlockingMode, CompletionHandler<void()>&&);
     void setShouldEnbleSameSiteStrictEnforcementForTesting(PAL::SessionID, WebCore::SameSiteStrictEnforcementEnabled, CompletionHandler<void()>&&);
     void setFirstPartyWebsiteDataRemovalModeForTesting(PAL::SessionID, WebCore::FirstPartyWebsiteDataRemovalMode, CompletionHandler<void()>&&);
     void setToSameSiteStrictCookiesForTesting(PAL::SessionID, const WebCore::RegistrableDomain&, CompletionHandler<void()>&&);

Modified: trunk/Source/WebKit/NetworkProcess/NetworkProcess.messages.in (261241 => 261242)


--- trunk/Source/WebKit/NetworkProcess/NetworkProcess.messages.in	2020-05-06 18:43:19 UTC (rev 261241)
+++ trunk/Source/WebKit/NetworkProcess/NetworkProcess.messages.in	2020-05-06 18:44:26 UTC (rev 261242)
@@ -141,8 +141,9 @@
     ResetCrossSiteLoadsWithLinkDecorationForTesting(PAL::SessionID sessionID) -> () Async
     DeleteCookiesForTesting(PAL::SessionID sessionID, WebCore::RegistrableDomain domain, bool includeHttpOnlyCookies) -> () Async
     HasIsolatedSession(PAL::SessionID sessionID, WebCore::RegistrableDomain domain) -> (bool hasIsolatedSession) Async
+    SetAppBoundDomainsForResourceLoadStatistics(PAL::SessionID sessionID, HashSet<WebCore::RegistrableDomain> appBoundDomains) -> () Async
     SetShouldDowngradeReferrerForTesting(bool enabled) -> () Async
-    SetShouldBlockThirdPartyCookiesForTesting(PAL::SessionID sessionID, enum:uint8_t WebCore::ThirdPartyCookieBlockingMode blockingMode) -> () Async
+    SetThirdPartyCookieBlockingMode(PAL::SessionID sessionID, enum:uint8_t WebCore::ThirdPartyCookieBlockingMode blockingMode) -> () Async
     SetShouldEnbleSameSiteStrictEnforcementForTesting(PAL::SessionID sessionID, enum:bool WebCore::SameSiteStrictEnforcementEnabled enabled) -> () Async
     SetFirstPartyWebsiteDataRemovalModeForTesting(PAL::SessionID sessionID, enum:uint8_t WebCore::FirstPartyWebsiteDataRemovalMode mode) -> () Async
     SetToSameSiteStrictCookiesForTesting(PAL::SessionID sessionID, WebCore::RegistrableDomain domain) -> () Async

Modified: trunk/Source/WebKit/NetworkProcess/cocoa/NetworkSessionCocoa.mm (261241 => 261242)


--- trunk/Source/WebKit/NetworkProcess/cocoa/NetworkSessionCocoa.mm	2020-05-06 18:43:19 UTC (rev 261241)
+++ trunk/Source/WebKit/NetworkProcess/cocoa/NetworkSessionCocoa.mm	2020-05-06 18:44:26 UTC (rev 261242)
@@ -1227,6 +1227,8 @@
     m_deviceManagementRestrictionsEnabled = parameters.deviceManagementRestrictionsEnabled;
     m_allLoadsBlockedByDeviceManagementRestrictionsForTesting = parameters.allLoadsBlockedByDeviceManagementRestrictionsForTesting;
 
+    if (m_resourceLoadStatistics && !parameters.resourceLoadStatisticsParameters.appBoundDomains.isEmpty())
+        m_resourceLoadStatistics->setAppBoundDomains(WTFMove(parameters.resourceLoadStatisticsParameters.appBoundDomains), [] { });
 #if HAVE(SESSION_CLEANUP)
     activateSessionCleanup(*this, parameters);
 #endif

Modified: trunk/Source/WebKit/Shared/ResourceLoadStatisticsParameters.h (261241 => 261242)


--- trunk/Source/WebKit/Shared/ResourceLoadStatisticsParameters.h	2020-05-06 18:43:19 UTC (rev 261241)
+++ trunk/Source/WebKit/Shared/ResourceLoadStatisticsParameters.h	2020-05-06 18:44:26 UTC (rev 261242)
@@ -47,8 +47,9 @@
     WebCore::SameSiteStrictEnforcementEnabled sameSiteStrictEnforcementEnabled { WebCore::SameSiteStrictEnforcementEnabled::No };
 #endif
     WebCore::FirstPartyWebsiteDataRemovalMode firstPartyWebsiteDataRemovalMode { WebCore::FirstPartyWebsiteDataRemovalMode::AllButCookies };
-    WebCore::RegistrableDomain standaloneApplicationDomain { };
-    WebCore::RegistrableDomain manualPrevalentResource { };
+    WebCore::RegistrableDomain standaloneApplicationDomain;
+    HashSet<WebCore::RegistrableDomain> appBoundDomains;
+    WebCore::RegistrableDomain manualPrevalentResource;
     
     void encode(IPC::Encoder& encoder) const
     {
@@ -65,6 +66,7 @@
 #endif
         encoder << firstPartyWebsiteDataRemovalMode;
         encoder << standaloneApplicationDomain;
+        encoder << appBoundDomains;
         encoder << manualPrevalentResource;
     }
 
@@ -127,6 +129,11 @@
         if (!standaloneApplicationDomain)
             return WTF::nullopt;
 
+        Optional<HashSet<WebCore::RegistrableDomain>> appBoundDomains;
+        decoder >> appBoundDomains;
+        if (!appBoundDomains)
+            return WTF::nullopt;
+
         Optional<WebCore::RegistrableDomain> manualPrevalentResource;
         decoder >> manualPrevalentResource;
         if (!manualPrevalentResource)
@@ -146,6 +153,7 @@
 #endif
             WTFMove(*firstPartyWebsiteDataRemovalMode),
             WTFMove(*standaloneApplicationDomain),
+            WTFMove(*appBoundDomains),
             WTFMove(*manualPrevalentResource),
         }};
     }

Modified: trunk/Source/WebKit/UIProcess/API/C/WKWebsiteDataStoreRef.cpp (261241 => 261242)


--- trunk/Source/WebKit/UIProcess/API/C/WKWebsiteDataStoreRef.cpp	2020-05-06 18:43:19 UTC (rev 261241)
+++ trunk/Source/WebKit/UIProcess/API/C/WKWebsiteDataStoreRef.cpp	2020-05-06 18:44:26 UTC (rev 261242)
@@ -42,6 +42,7 @@
 #include "WebsiteDataRecord.h"
 #include "WebsiteDataStore.h"
 #include "WebsiteDataType.h"
+#include <WebCore/RegistrableDomain.h>
 #include <wtf/CallbackAggregator.h>
 #include <wtf/URL.h>
 
@@ -617,6 +618,31 @@
 #endif
 }
 
+void WKWebsiteDataStoreSetAppBoundDomainsForTesting(WKArrayRef originURLsRef, void* context, WKWebsiteDataStoreSetAppBoundDomainsForTestingFunction completionHandler)
+{
+#if PLATFORM(COCOA)
+    RefPtr<API::Array> originURLsArray = toImpl(originURLsRef);
+    size_t newSize = originURLsArray ? originURLsArray->size() : 0;
+    HashSet<WebCore::RegistrableDomain> domains;
+    domains.reserveInitialCapacity(newSize);
+    for (size_t i = 0; i < newSize; ++i) {
+        auto* originURL = originURLsArray->at<API::URL>(i);
+        if (!originURL)
+            continue;
+        
+        domains.add(WebCore::RegistrableDomain { URL(URL(), originURL->string()) });
+    }
+
+    WebKit::WebsiteDataStore::setAppBoundDomainsForTesting(WTFMove(domains), [context, completionHandler] {
+        completionHandler(context);
+    });
+#else
+    UNUSED_PARAM(originURLsRef);
+    UNUSED_PARAM(context);
+    UNUSED_PARAM(completionHandler);
+#endif
+}
+
 void WKWebsiteDataStoreStatisticsResetToConsistentState(WKWebsiteDataStoreRef dataStoreRef, void* context, WKWebsiteDataStoreStatisticsResetToConsistentStateFunction completionHandler)
 {
 #if ENABLE(RESOURCE_LOAD_STATISTICS)

Modified: trunk/Source/WebKit/UIProcess/API/C/WKWebsiteDataStoreRef.h (261241 => 261242)


--- trunk/Source/WebKit/UIProcess/API/C/WKWebsiteDataStoreRef.h	2020-05-06 18:43:19 UTC (rev 261241)
+++ trunk/Source/WebKit/UIProcess/API/C/WKWebsiteDataStoreRef.h	2020-05-06 18:44:26 UTC (rev 261242)
@@ -129,6 +129,8 @@
 WK_EXPORT void WKWebsiteDataStoreSetResourceLoadStatisticsFirstPartyWebsiteDataRemovalModeForTesting(WKWebsiteDataStoreRef dataStoreRef, bool enabled, void* context, WKWebsiteDataStoreSetResourceLoadStatisticsFirstPartyWebsiteDataRemovalModeForTestingFunction completionHandler);
 typedef void (*WKWebsiteDataStoreSetResourceLoadStatisticsToSameSiteStrictCookiesForTestingFunction)(void* functionContext);
 WK_EXPORT void WKWebsiteDataStoreSetResourceLoadStatisticsToSameSiteStrictCookiesForTesting(WKWebsiteDataStoreRef dataStoreRef, WKStringRef hostName, void* context, WKWebsiteDataStoreSetResourceLoadStatisticsToSameSiteStrictCookiesForTestingFunction completionHandler);
+typedef void (*WKWebsiteDataStoreSetAppBoundDomainsForTestingFunction)(void* functionContext);
+WK_EXPORT void WKWebsiteDataStoreSetAppBoundDomainsForTesting(WKArrayRef originURLsRef, void* context, WKWebsiteDataStoreSetAppBoundDomainsForTestingFunction completionHandler);
 typedef void (*WKWebsiteDataStoreStatisticsResetToConsistentStateFunction)(void* functionContext);
 WK_EXPORT void WKWebsiteDataStoreStatisticsResetToConsistentState(WKWebsiteDataStoreRef dataStoreRef, void* context, WKWebsiteDataStoreStatisticsResetToConsistentStateFunction completionHandler);
 

Modified: trunk/Source/WebKit/UIProcess/Network/NetworkProcessProxy.cpp (261241 => 261242)


--- trunk/Source/WebKit/UIProcess/Network/NetworkProcessProxy.cpp	2020-05-06 18:43:19 UTC (rev 261241)
+++ trunk/Source/WebKit/UIProcess/Network/NetworkProcessProxy.cpp	2020-05-06 18:44:26 UTC (rev 261242)
@@ -1136,6 +1136,13 @@
     sendWithAsyncReply(Messages::NetworkProcess::HasIsolatedSession(sessionID, domain), WTFMove(completionHandler));
 }
 
+void NetworkProcessProxy::setAppBoundDomainsForResourceLoadStatistics(PAL::SessionID sessionID, const HashSet<RegistrableDomain>& appBoundDomains, CompletionHandler<void()>&& completionHandler)
+{
+    sendWithAsyncReply(Messages::NetworkProcess::SetAppBoundDomainsForResourceLoadStatistics(sessionID, appBoundDomains), [activity = throttler().backgroundActivity("NetworkProcessProxy::setAppBoundDomainsForResourceLoadStatistics"_s), completionHandler = WTFMove(completionHandler)]() mutable {
+        completionHandler();
+    });
+}
+
 void NetworkProcessProxy::setShouldDowngradeReferrerForTesting(bool enabled, CompletionHandler<void()>&& completionHandler)
 {
     if (!canSendMessage()) {
@@ -1148,9 +1155,9 @@
     });
 }
 
-void NetworkProcessProxy::setShouldBlockThirdPartyCookiesForTesting(PAL::SessionID sessionID, ThirdPartyCookieBlockingMode blockingMode, CompletionHandler<void()>&& completionHandler)
+void NetworkProcessProxy::setThirdPartyCookieBlockingMode(PAL::SessionID sessionID, ThirdPartyCookieBlockingMode blockingMode, CompletionHandler<void()>&& completionHandler)
 {
-    sendWithAsyncReply(Messages::NetworkProcess::SetShouldBlockThirdPartyCookiesForTesting(sessionID, blockingMode), [activity = throttler().backgroundActivity("NetworkProcessProxy::setShouldBlockThirdPartyCookiesForTesting"_s), completionHandler = WTFMove(completionHandler)]() mutable {
+    sendWithAsyncReply(Messages::NetworkProcess::SetThirdPartyCookieBlockingMode(sessionID, blockingMode), [activity = throttler().backgroundActivity("NetworkProcessProxy::setThirdPartyCookieBlockingMode"_s), completionHandler = WTFMove(completionHandler)]() mutable {
         completionHandler();
     });
 }

Modified: trunk/Source/WebKit/UIProcess/Network/NetworkProcessProxy.h (261241 => 261242)


--- trunk/Source/WebKit/UIProcess/Network/NetworkProcessProxy.h	2020-05-06 18:43:19 UTC (rev 261241)
+++ trunk/Source/WebKit/UIProcess/Network/NetworkProcessProxy.h	2020-05-06 18:44:26 UTC (rev 261242)
@@ -175,8 +175,9 @@
     void deleteCookiesForTesting(PAL::SessionID, const RegistrableDomain&, bool includeHttpOnlyCookies, CompletionHandler<void()>&&);
     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 setAppBoundDomainsForResourceLoadStatistics(PAL::SessionID, const HashSet<RegistrableDomain>&, CompletionHandler<void()>&&);
     void setShouldDowngradeReferrerForTesting(bool, CompletionHandler<void()>&&);
-    void setShouldBlockThirdPartyCookiesForTesting(PAL::SessionID, WebCore::ThirdPartyCookieBlockingMode, CompletionHandler<void()>&&);
+    void setThirdPartyCookieBlockingMode(PAL::SessionID, WebCore::ThirdPartyCookieBlockingMode, CompletionHandler<void()>&&);
     void setShouldEnbleSameSiteStrictEnforcementForTesting(PAL::SessionID, WebCore::SameSiteStrictEnforcementEnabled, CompletionHandler<void()>&&);
     void setFirstPartyWebsiteDataRemovalModeForTesting(PAL::SessionID, WebCore::FirstPartyWebsiteDataRemovalMode, CompletionHandler<void()>&&);
     void setToSameSiteStrictCookiesForTesting(PAL::SessionID, const RegistrableDomain&, CompletionHandler<void()>&&);

Modified: trunk/Source/WebKit/UIProcess/WebProcessPool.cpp (261241 => 261242)


--- trunk/Source/WebKit/UIProcess/WebProcessPool.cpp	2020-05-06 18:43:19 UTC (rev 261241)
+++ trunk/Source/WebKit/UIProcess/WebProcessPool.cpp	2020-05-06 18:44:26 UTC (rev 261242)
@@ -605,9 +605,10 @@
     WebCore::ThirdPartyCookieBlockingMode thirdPartyCookieBlockingMode = WebCore::ThirdPartyCookieBlockingMode::All;
     WebCore::SameSiteStrictEnforcementEnabled sameSiteStrictEnforcementEnabled = WebCore::SameSiteStrictEnforcementEnabled::No;
 #endif
-    WebCore::RegistrableDomain standaloneApplicationDomain { };
     WebCore::FirstPartyWebsiteDataRemovalMode firstPartyWebsiteDataRemovalMode = WebCore::FirstPartyWebsiteDataRemovalMode::AllButCookies;
-    WebCore::RegistrableDomain manualPrevalentResource { };
+    WebCore::RegistrableDomain standaloneApplicationDomain;
+    HashSet<WebCore::RegistrableDomain> appBoundDomains;
+    WebCore::RegistrableDomain manualPrevalentResource;
     WEB_PROCESS_POOL_ADDITIONS_2
     if (withWebsiteDataStore) {
         enableResourceLoadStatistics = withWebsiteDataStore->resourceLoadStatisticsEnabled();
@@ -624,6 +625,7 @@
 #endif
             firstPartyWebsiteDataRemovalMode = networkSessionParameters.resourceLoadStatisticsParameters.firstPartyWebsiteDataRemovalMode;
             standaloneApplicationDomain = networkSessionParameters.resourceLoadStatisticsParameters.standaloneApplicationDomain;
+            appBoundDomains = networkSessionParameters.resourceLoadStatisticsParameters.appBoundDomains;
             manualPrevalentResource = networkSessionParameters.resourceLoadStatisticsParameters.manualPrevalentResource;
         }
 
@@ -650,6 +652,7 @@
 #endif
             firstPartyWebsiteDataRemovalMode = networkSessionParameters.resourceLoadStatisticsParameters.firstPartyWebsiteDataRemovalMode;
             standaloneApplicationDomain = networkSessionParameters.resourceLoadStatisticsParameters.standaloneApplicationDomain;
+            appBoundDomains = networkSessionParameters.resourceLoadStatisticsParameters.appBoundDomains;
             manualPrevalentResource = networkSessionParameters.resourceLoadStatisticsParameters.manualPrevalentResource;
         }
 
@@ -689,6 +692,8 @@
         sameSiteStrictEnforcementEnabled,
 #endif
         firstPartyWebsiteDataRemovalMode,
+        standaloneApplicationDomain,
+        appBoundDomains,
         manualPrevalentResource,
     };
 

Modified: trunk/Source/WebKit/UIProcess/WebProcessProxy.cpp (261241 => 261242)


--- trunk/Source/WebKit/UIProcess/WebProcessProxy.cpp	2020-05-06 18:43:19 UTC (rev 261241)
+++ trunk/Source/WebKit/UIProcess/WebProcessProxy.cpp	2020-05-06 18:44:26 UTC (rev 261242)
@@ -458,9 +458,9 @@
         page.value->postMessageToInjectedBundle("ResourceLoadStatisticsTelemetryFinished", messageBody);
 }
 
-void WebProcessProxy::setShouldBlockThirdPartyCookiesForTesting(ThirdPartyCookieBlockingMode thirdPartyCookieBlockingMode, CompletionHandler<void()>&& completionHandler)
+void WebProcessProxy::setThirdPartyCookieBlockingMode(ThirdPartyCookieBlockingMode thirdPartyCookieBlockingMode, CompletionHandler<void()>&& completionHandler)
 {
-    sendWithAsyncReply(Messages::WebProcess::SetShouldBlockThirdPartyCookiesForTesting(thirdPartyCookieBlockingMode), [activity = throttler().backgroundActivity("WebProcessProxy::setShouldBlockThirdPartyCookiesForTesting"_s), completionHandler = WTFMove(completionHandler)]() mutable {
+    sendWithAsyncReply(Messages::WebProcess::SetThirdPartyCookieBlockingMode(thirdPartyCookieBlockingMode), [activity = throttler().backgroundActivity("WebProcessProxy::setThirdPartyCookieBlockingMode"_s), completionHandler = WTFMove(completionHandler)]() mutable {
         completionHandler();
     });
 }

Modified: trunk/Source/WebKit/UIProcess/WebProcessProxy.h (261241 => 261242)


--- trunk/Source/WebKit/UIProcess/WebProcessProxy.h	2020-05-06 18:43:19 UTC (rev 261241)
+++ trunk/Source/WebKit/UIProcess/WebProcessProxy.h	2020-05-06 18:44:26 UTC (rev 261242)
@@ -219,7 +219,7 @@
     static void notifyWebsiteDataDeletionForRegistrableDomainsFinished();
     static void notifyWebsiteDataScanForRegistrableDomainsFinished();
 
-    void setShouldBlockThirdPartyCookiesForTesting(WebCore::ThirdPartyCookieBlockingMode, CompletionHandler<void()>&&);
+    void setThirdPartyCookieBlockingMode(WebCore::ThirdPartyCookieBlockingMode, CompletionHandler<void()>&&);
 #endif
 
     void enableSuddenTermination();

Modified: trunk/Source/WebKit/UIProcess/WebsiteData/Cocoa/WebsiteDataStoreCocoa.mm (261241 => 261242)


--- trunk/Source/WebKit/UIProcess/WebsiteData/Cocoa/WebsiteDataStoreCocoa.mm	2020-05-06 18:43:19 UTC (rev 261241)
+++ trunk/Source/WebKit/UIProcess/WebsiteData/Cocoa/WebsiteDataStoreCocoa.mm	2020-05-06 18:44:26 UTC (rev 261242)
@@ -413,6 +413,9 @@
         keyExists = domains ? true : false;
         
         RunLoop::main().dispatch([isInAppBrowserPrivacyEnabled, forceReinitialization, domains = retainPtr(domains)] {
+            if (hasInitializedAppBoundDomains && forceReinitialization != ForceReinitialization::Yes)
+                return;
+
             if (forceReinitialization == ForceReinitialization::Yes)
                 appBoundDomains().clear();
 
@@ -433,6 +436,8 @@
                 WEBSITE_DATA_STORE_ADDITIONS
             }
             hasInitializedAppBoundDomains = true;
+            if (isAppBoundITPRelaxationEnabled)
+                forwardAppBoundDomainsToITPIfInitialized([] { });
         });
     });
 }
@@ -444,6 +449,8 @@
         return;
     }
 
+    // Hopping to the background thread then back to the main thread
+    // ensures that initializeAppBoundDomains() has finished.
     appBoundDomainQueue().dispatch([completionHandler = WTFMove(completionHandler)] () mutable {
         RunLoop::main().dispatch([completionHandler = WTFMove(completionHandler)] () mutable {
             ASSERT(hasInitializedAppBoundDomains);
@@ -486,6 +493,24 @@
     });
 }
 
+Optional<HashSet<WebCore::RegistrableDomain>> WebsiteDataStore::appBoundDomainsIfInitialized()
+{
+    ASSERT(RunLoop::isMain());
+    if (!hasInitializedAppBoundDomains)
+        return WTF::nullopt;
+    return appBoundDomains();
+}
+
+void WebsiteDataStore::setAppBoundDomainsForTesting(HashSet<WebCore::RegistrableDomain>&& domains, CompletionHandler<void()>&& completionHandler)
+{
+    for (auto& domain : domains)
+        RELEASE_ASSERT(domain == "localhost"_s || domain == "127.0.0.1"_s);
+
+    appBoundDomains() = WTFMove(domains);
+    hasInitializedAppBoundDomains = true;
+    forwardAppBoundDomainsToITPIfInitialized(WTFMove(completionHandler));
+}
+
 void WebsiteDataStore::reinitializeAppBoundDomains()
 {
     hasInitializedAppBoundDomains = false;

Modified: trunk/Source/WebKit/UIProcess/WebsiteData/WebsiteDataStore.cpp (261241 => 261242)


--- trunk/Source/WebKit/UIProcess/WebsiteData/WebsiteDataStore.cpp	2020-05-06 18:43:19 UTC (rev 261241)
+++ trunk/Source/WebKit/UIProcess/WebsiteData/WebsiteDataStore.cpp	2020-05-06 18:44:26 UTC (rev 261242)
@@ -1862,16 +1862,22 @@
     WebCore::ThirdPartyCookieBlockingMode blockingMode = WebCore::ThirdPartyCookieBlockingMode::OnlyAccordingToPerDomainPolicy;
     if (enabled)
         blockingMode = onlyOnSitesWithoutUserInteraction ? WebCore::ThirdPartyCookieBlockingMode::AllOnSitesWithoutUserInteraction : WebCore::ThirdPartyCookieBlockingMode::All;
+    setThirdPartyCookieBlockingMode(blockingMode, WTFMove(completionHandler));
+}
 
+void WebsiteDataStore::setThirdPartyCookieBlockingMode(WebCore::ThirdPartyCookieBlockingMode blockingMode, CompletionHandler<void()>&& completionHandler)
+{
+    auto callbackAggregator = CallbackAggregator::create(WTFMove(completionHandler));
+
     if (thirdPartyCookieBlockingMode() != blockingMode) {
         m_thirdPartyCookieBlockingMode = blockingMode;
         for (auto& webProcess : processes())
-            webProcess.setShouldBlockThirdPartyCookiesForTesting(blockingMode, [callbackAggregator = callbackAggregator.copyRef()] { });
+            webProcess.setThirdPartyCookieBlockingMode(blockingMode, [callbackAggregator = callbackAggregator.copyRef()] { });
     }
 
     for (auto& processPool : processPools()) {
         if (auto* networkProcess = processPool->networkProcess())
-            networkProcess->setShouldBlockThirdPartyCookiesForTesting(m_sessionID, blockingMode, [callbackAggregator = callbackAggregator.copyRef()] { });
+            networkProcess->setThirdPartyCookieBlockingMode(m_sessionID, blockingMode, [callbackAggregator = callbackAggregator.copyRef()] { });
     }
 }
 
@@ -2225,8 +2231,13 @@
     bool shouldIncludeLocalhostInResourceLoadStatistics = false;
     bool enableResourceLoadStatisticsDebugMode = false;
     auto firstPartyWebsiteDataRemovalMode = WebCore::FirstPartyWebsiteDataRemovalMode::AllButCookies;
-    WebCore::RegistrableDomain resourceLoadStatisticsManualPrevalentResource { };
-
+    WebCore::RegistrableDomain standaloneApplicationDomain;
+    HashSet<WebCore::RegistrableDomain> appBoundDomains;
+#if PLATFORM(COCOA)
+    if (isAppBoundITPRelaxationEnabled)
+        appBoundDomains = appBoundDomainsIfInitialized().valueOr(HashSet<WebCore::RegistrableDomain> { });
+#endif
+    WebCore::RegistrableDomain resourceLoadStatisticsManualPrevalentResource;
     ResourceLoadStatisticsParameters resourceLoadStatisticsParameters = {
         WTFMove(resourceLoadStatisticsDirectory),
         WTFMove(resourceLoadStatisticsDirectoryHandle),
@@ -2245,6 +2256,8 @@
         WebCore::SameSiteStrictEnforcementEnabled::No,
 #endif
         firstPartyWebsiteDataRemovalMode,
+        WTFMove(standaloneApplicationDomain),
+        WTFMove(appBoundDomains),
         WTFMove(resourceLoadStatisticsManualPrevalentResource),
     };
 
@@ -2424,4 +2437,39 @@
     }
 }
 
+#if PLATFORM(COCOA)
+void WebsiteDataStore::forwardAppBoundDomainsToITPIfInitialized(CompletionHandler<void()>&& completionHandler)
+{
+    auto callbackAggregator = CallbackAggregator::create(WTFMove(completionHandler));
+    auto appBoundDomains = appBoundDomainsIfInitialized();
+    if (!appBoundDomains)
+        return;
+
+    auto propagateAppBoundDomains = [callbackAggregator = callbackAggregator.copyRef()] (WebsiteDataStore* store, const HashSet<WebCore::RegistrableDomain>& domains) {
+        if (!store)
+            return;
+
+        if (store->thirdPartyCookieBlockingMode() != WebCore::ThirdPartyCookieBlockingMode::AllExceptBetweenAppBoundDomains)
+            store->setThirdPartyCookieBlockingMode(WebCore::ThirdPartyCookieBlockingMode::AllExceptBetweenAppBoundDomains, [callbackAggregator = callbackAggregator.copyRef()] { });
+
+        store->setAppBoundDomainsForITP(domains, [callbackAggregator = callbackAggregator.copyRef()] { });
+    };
+
+    propagateAppBoundDomains(globalDefaultDataStore().get(), *appBoundDomains);
+
+    for (auto* store : nonDefaultDataStores().values())
+        propagateAppBoundDomains(store, *appBoundDomains);
 }
+
+void WebsiteDataStore::setAppBoundDomainsForITP(const HashSet<WebCore::RegistrableDomain>& domains, CompletionHandler<void()>&& completionHandler)
+{
+    auto callbackAggregator = CallbackAggregator::create(WTFMove(completionHandler));
+
+    for (auto& processPool : processPools()) {
+        if (auto* networkProcess = processPool->networkProcess())
+            networkProcess->setAppBoundDomainsForResourceLoadStatistics(m_sessionID, domains, [callbackAggregator = callbackAggregator.copyRef()] { });
+    }
+}
+#endif
+
+}

Modified: trunk/Source/WebKit/UIProcess/WebsiteData/WebsiteDataStore.h (261241 => 261242)


--- trunk/Source/WebKit/UIProcess/WebsiteData/WebsiteDataStore.h	2020-05-06 18:43:19 UTC (rev 261241)
+++ trunk/Source/WebKit/UIProcess/WebsiteData/WebsiteDataStore.h	2020-05-06 18:44:26 UTC (rev 261242)
@@ -199,6 +199,7 @@
     void hasIsolatedSessionForTesting(const URL&, CompletionHandler<void(bool)>&&) const;
     void setResourceLoadStatisticsShouldDowngradeReferrerForTesting(bool, CompletionHandler<void()>&&);
     void setResourceLoadStatisticsShouldBlockThirdPartyCookiesForTesting(bool enabled, bool onlyOnSitesWithoutUserInteraction, CompletionHandler<void()>&&);
+    void setThirdPartyCookieBlockingMode(WebCore::ThirdPartyCookieBlockingMode, CompletionHandler<void()>&&);
     void setResourceLoadStatisticsShouldEnbleSameSiteStrictEnforcementForTesting(bool enabled, CompletionHandler<void()>&&);
     void setResourceLoadStatisticsFirstPartyWebsiteDataRemovalModeForTesting(bool enabled, CompletionHandler<void()>&&);
     void setResourceLoadStatisticsToSameSiteStrictCookiesForTesting(const URL&, CompletionHandler<void()>&&);
@@ -296,6 +297,7 @@
     void getAppBoundDomains(CompletionHandler<void(const HashSet<WebCore::RegistrableDomain>&)>&&) const;
     void ensureAppBoundDomains(CompletionHandler<void(const HashSet<WebCore::RegistrableDomain>&)>&&) const;
     void reinitializeAppBoundDomains();
+    static void setAppBoundDomainsForTesting(HashSet<WebCore::RegistrableDomain>&&, CompletionHandler<void()>&&);
 
 private:
     enum class ForceReinitialization : bool { No, Yes };
@@ -331,6 +333,13 @@
 
     void maybeRegisterWithSessionIDMap();
 
+#if PLATFORM(COCOA)
+    static Optional<HashSet<WebCore::RegistrableDomain>> appBoundDomainsIfInitialized();
+    constexpr static const std::atomic<bool> isAppBoundITPRelaxationEnabled = false;
+    static void forwardAppBoundDomainsToITPIfInitialized(CompletionHandler<void()>&&);
+    void setAppBoundDomainsForITP(const HashSet<WebCore::RegistrableDomain>&, CompletionHandler<void()>&&);
+#endif
+
     const PAL::SessionID m_sessionID;
 
     Ref<WebsiteDataStoreConfiguration> m_resolvedConfiguration;

Modified: trunk/Source/WebKit/WebProcess/WebProcess.cpp (261241 => 261242)


--- trunk/Source/WebKit/WebProcess/WebProcess.cpp	2020-05-06 18:43:19 UTC (rev 261241)
+++ trunk/Source/WebKit/WebProcess/WebProcess.cpp	2020-05-06 18:44:26 UTC (rev 261242)
@@ -1895,7 +1895,7 @@
 }
 
 #if ENABLE(RESOURCE_LOAD_STATISTICS)
-void WebProcess::setShouldBlockThirdPartyCookiesForTesting(ThirdPartyCookieBlockingMode thirdPartyCookieBlockingMode, CompletionHandler<void()>&& completionHandler)
+void WebProcess::setThirdPartyCookieBlockingMode(ThirdPartyCookieBlockingMode thirdPartyCookieBlockingMode, CompletionHandler<void()>&& completionHandler)
 {
     m_thirdPartyCookieBlockingMode = thirdPartyCookieBlockingMode;
     completionHandler();

Modified: trunk/Source/WebKit/WebProcess/WebProcess.h (261241 => 261242)


--- trunk/Source/WebKit/WebProcess/WebProcess.h	2020-05-06 18:43:19 UTC (rev 261241)
+++ trunk/Source/WebKit/WebProcess/WebProcess.h	2020-05-06 18:44:26 UTC (rev 261242)
@@ -458,7 +458,7 @@
 #endif
 
 #if ENABLE(RESOURCE_LOAD_STATISTICS)
-    void setShouldBlockThirdPartyCookiesForTesting(WebCore::ThirdPartyCookieBlockingMode, CompletionHandler<void()>&&);
+    void setThirdPartyCookieBlockingMode(WebCore::ThirdPartyCookieBlockingMode, CompletionHandler<void()>&&);
 #endif
 
     void platformInitializeProcess(const AuxiliaryProcessInitializationParameters&);

Modified: trunk/Source/WebKit/WebProcess/WebProcess.messages.in (261241 => 261242)


--- trunk/Source/WebKit/WebProcess/WebProcess.messages.in	2020-05-06 18:43:19 UTC (rev 261241)
+++ trunk/Source/WebKit/WebProcess/WebProcess.messages.in	2020-05-06 18:44:26 UTC (rev 261242)
@@ -157,7 +157,7 @@
 
 #if ENABLE(RESOURCE_LOAD_STATISTICS)
     SeedResourceLoadStatisticsForTesting(WebCore::RegistrableDomain firstPartyDomain, WebCore::RegistrableDomain thirdPartyDomain, bool shouldScheduleNotification) -> () Async
-    SetShouldBlockThirdPartyCookiesForTesting(enum:uint8_t WebCore::ThirdPartyCookieBlockingMode blockingMode) -> () Async
+    SetThirdPartyCookieBlockingMode(enum:uint8_t WebCore::ThirdPartyCookieBlockingMode blockingMode) -> () Async
 #endif
 
 #if PLATFORM(IOS)

Modified: trunk/Tools/ChangeLog (261241 => 261242)


--- trunk/Tools/ChangeLog	2020-05-06 18:43:19 UTC (rev 261241)
+++ trunk/Tools/ChangeLog	2020-05-06 18:44:26 UTC (rev 261242)
@@ -1,3 +1,32 @@
+2020-05-06  John Wilander  <wilan...@apple.com>
+
+        Exempt app-bound domains from ITP's website data deletion and third-party cookie blocking between themselves
+        https://bugs.webkit.org/show_bug.cgi?id=210674
+        <rdar://problem/61950767>
+
+        Reviewed by Chris Dumez.
+
+        This change adds a new TestRunner function
+        setAppBoundDomain() which takes an array of origin
+        strings and sets them to app-bound domains.
+
+        * WebKitTestRunner/InjectedBundle/Bindings/TestRunner.idl:
+        * WebKitTestRunner/InjectedBundle/InjectedBundle.cpp:
+        (WTR::InjectedBundle::didReceiveMessageToPage):
+        * WebKitTestRunner/InjectedBundle/TestRunner.cpp:
+        (WTR::TestRunner::setAppBoundDomains):
+        (WTR::TestRunner::didSetAppBoundDomainsCallback):
+        * WebKitTestRunner/InjectedBundle/TestRunner.h:
+        * WebKitTestRunner/TestController.cpp:
+        (WTR::AppBoundDomainsCallbackContext::AppBoundDomainsCallbackContext):
+        (WTR::didSetAppBoundDomainsCallback):
+        (WTR::TestController::setAppBoundDomains):
+        * WebKitTestRunner/TestController.h:
+        * WebKitTestRunner/TestInvocation.cpp:
+        (WTR::TestInvocation::didReceiveMessageFromInjectedBundle):
+        (WTR::TestInvocation::didSetAppBoundDomains):
+        * WebKitTestRunner/TestInvocation.h:
+
 2020-05-06  Aakash Jain  <aakash_j...@apple.com>
 
         Delete code for feeder queue

Modified: trunk/Tools/WebKitTestRunner/InjectedBundle/Bindings/TestRunner.idl (261241 => 261242)


--- trunk/Tools/WebKitTestRunner/InjectedBundle/Bindings/TestRunner.idl	2020-05-06 18:43:19 UTC (rev 261241)
+++ trunk/Tools/WebKitTestRunner/InjectedBundle/Bindings/TestRunner.idl	2020-05-06 18:44:26 UTC (rev 261242)
@@ -393,7 +393,9 @@
     void resetMockMediaDevices();
     void setMockCameraOrientation(unsigned long orientation);
     boolean isMockRealtimeMediaSourceCenterEnabled();
+
     boolean hasAppBoundSession();
+    void setAppBoundDomains(object originsArray, object callback);
 
     void injectUserScript(DOMString string);
     readonly attribute unsigned long userScriptInjectedCount;

Modified: trunk/Tools/WebKitTestRunner/InjectedBundle/InjectedBundle.cpp (261241 => 261242)


--- trunk/Tools/WebKitTestRunner/InjectedBundle/InjectedBundle.cpp	2020-05-06 18:43:19 UTC (rev 261241)
+++ trunk/Tools/WebKitTestRunner/InjectedBundle/InjectedBundle.cpp	2020-05-06 18:44:26 UTC (rev 261242)
@@ -518,7 +518,12 @@
         m_testRunner->performCustomMenuAction();
         return;
     }
-    
+
+    if (WKStringIsEqualToUTF8CString(messageName, "CallDidSetAppBoundDomains")) {
+        m_testRunner->didSetAppBoundDomainsCallback();
+        return;
+    }
+
     WKRetainPtr<WKStringRef> errorMessageName = adoptWK(WKStringCreateWithUTF8CString("Error"));
     WKRetainPtr<WKStringRef> errorMessageBody = adoptWK(WKStringCreateWithUTF8CString("Unknown"));
     WKBundlePagePostMessage(page, errorMessageName.get(), errorMessageBody.get());

Modified: trunk/Tools/WebKitTestRunner/InjectedBundle/TestRunner.cpp (261241 => 261242)


--- trunk/Tools/WebKitTestRunner/InjectedBundle/TestRunner.cpp	2020-05-06 18:43:19 UTC (rev 261241)
+++ trunk/Tools/WebKitTestRunner/InjectedBundle/TestRunner.cpp	2020-05-06 18:44:26 UTC (rev 261242)
@@ -763,6 +763,7 @@
     TextFieldDidBeginEditingCallbackID,
     TextFieldDidEndEditingCallbackID,
     CustomMenuActionCallbackID,
+    DidSetAppBoundDomainsCallbackID,
     FirstUIScriptCallbackID = 100
 };
 
@@ -2975,4 +2976,43 @@
     return WKBooleanGetValue(adoptWK(static_cast<WKBooleanRef>(returnData)).get());
 }
 
+void TestRunner::setAppBoundDomains(JSValueRef originArray, JSValueRef completionHandler)
+{
+    cacheTestRunnerCallback(DidSetAppBoundDomainsCallbackID, completionHandler);
+
+    JSContextRef context = WKBundleFrameGetJavaScriptContext(WKBundlePageGetMainFrame(InjectedBundle::singleton().page()->page()));
+
+    if (!JSValueIsArray(context, originArray))
+        return;
+
+    JSObjectRef origins = JSValueToObject(context, originArray, nullptr);
+    static auto lengthProperty = adopt(JSStringCreateWithUTF8CString("length"));
+    JSValueRef originsLengthValue = JSObjectGetProperty(context, origins, lengthProperty.get(), nullptr);
+    if (!JSValueIsNumber(context, originsLengthValue))
+        return;
+
+    auto originURLs = adoptWK(WKMutableArrayCreate());
+    auto originsLength = static_cast<size_t>(JSValueToNumber(context, originsLengthValue, nullptr));
+    for (size_t i = 0; i < originsLength; ++i) {
+        JSValueRef originValue = JSObjectGetPropertyAtIndex(context, origins, i, nullptr);
+        if (!JSValueIsString(context, originValue))
+            continue;
+
+        auto origin = adopt(JSValueToStringCopy(context, originValue, nullptr));
+        size_t originBufferSize = JSStringGetMaximumUTF8CStringSize(origin.get()) + 1;
+        auto originBuffer = makeUniqueArray<char>(originBufferSize);
+        JSStringGetUTF8CString(origin.get(), originBuffer.get(), originBufferSize);
+
+        WKArrayAppendItem(originURLs.get(), adoptWK(WKURLCreateWithUTF8CString(originBuffer.get())).get());
+    }
+
+    auto messageName = adoptWK(WKStringCreateWithUTF8CString("SetAppBoundDomains"));
+    WKBundlePostMessage(InjectedBundle::singleton().bundle(), messageName.get(), originURLs.get());
+}
+
+void TestRunner::didSetAppBoundDomainsCallback()
+{
+    callTestRunnerCallback(DidSetAppBoundDomainsCallbackID);
+}
+
 } // namespace WTR

Modified: trunk/Tools/WebKitTestRunner/InjectedBundle/TestRunner.h (261241 => 261242)


--- trunk/Tools/WebKitTestRunner/InjectedBundle/TestRunner.h	2020-05-06 18:43:19 UTC (rev 261241)
+++ trunk/Tools/WebKitTestRunner/InjectedBundle/TestRunner.h	2020-05-06 18:44:26 UTC (rev 261242)
@@ -500,7 +500,10 @@
     void resetMockMediaDevices();
     void setMockCameraOrientation(unsigned);
     bool isMockRealtimeMediaSourceCenterEnabled();
+
     bool hasAppBoundSession();
+    void setAppBoundDomains(JSValueRef originArray, JSValueRef callback);
+    void didSetAppBoundDomainsCallback();
 
     size_t userScriptInjectedCount() const;
     void injectUserScript(JSStringRef);

Modified: trunk/Tools/WebKitTestRunner/TestController.cpp (261241 => 261242)


--- trunk/Tools/WebKitTestRunner/TestController.cpp	2020-05-06 18:43:19 UTC (rev 261241)
+++ trunk/Tools/WebKitTestRunner/TestController.cpp	2020-05-06 18:44:26 UTC (rev 261242)
@@ -3777,6 +3777,30 @@
     m_currentInvocation->didSetToSameSiteStrictCookies();
 }
 
+struct AppBoundDomainsCallbackContext {
+    explicit AppBoundDomainsCallbackContext(TestController& controller)
+        : testController(controller)
+    {
+    }
+
+    bool done { false };
+    TestController& testController;
+};
+
+static void didSetAppBoundDomainsCallback(void* callbackContext)
+{
+    auto* context = static_cast<AppBoundDomainsCallbackContext*>(callbackContext);
+    context->done = true;
+}
+
+void TestController::setAppBoundDomains(WKArrayRef originURLs)
+{
+    AppBoundDomainsCallbackContext context(*this);
+    WKWebsiteDataStoreSetAppBoundDomainsForTesting(originURLs, &context, didSetAppBoundDomainsCallback);
+    runUntil(context.done, noTimeout);
+    m_currentInvocation->didSetAppBoundDomains();
+}
+
 void TestController::statisticsResetToConsistentState()
 {
     ResourceStatisticsCallbackContext context(*this);

Modified: trunk/Tools/WebKitTestRunner/TestController.h (261241 => 261242)


--- trunk/Tools/WebKitTestRunner/TestController.h	2020-05-06 18:43:19 UTC (rev 261241)
+++ trunk/Tools/WebKitTestRunner/TestController.h	2020-05-06 18:44:26 UTC (rev 261242)
@@ -266,6 +266,7 @@
     void setStatisticsShouldBlockThirdPartyCookies(bool value, bool onlyOnSitesWithoutUserInteraction);
     void setStatisticsFirstPartyWebsiteDataRemovalMode(bool value);
     void setStatisticsToSameSiteStrictCookies(WKStringRef hostName);
+    void setAppBoundDomains(WKArrayRef originURLs);
     void statisticsResetToConsistentState();
 
     void getAllStorageAccessEntries();
@@ -352,6 +353,8 @@
     void setAdClickAttributionConversionURLForTesting(WKURLRef);
     void markAdClickAttributionsAsExpiredForTesting();
 
+    void didSetAppBoundDomains() const;
+
 private:
     WKRetainPtr<WKPageConfigurationRef> generatePageConfiguration(const TestOptions&);
     WKRetainPtr<WKContextConfigurationRef> generateContextConfiguration(const TestOptions::ContextOptions&) const;

Modified: trunk/Tools/WebKitTestRunner/TestInvocation.cpp (261241 => 261242)


--- trunk/Tools/WebKitTestRunner/TestInvocation.cpp	2020-05-06 18:43:19 UTC (rev 261241)
+++ trunk/Tools/WebKitTestRunner/TestInvocation.cpp	2020-05-06 18:44:26 UTC (rev 261242)
@@ -994,6 +994,13 @@
         return;
     }
 
+    if (WKStringIsEqualToUTF8CString(messageName, "SetAppBoundDomains")) {
+        ASSERT(WKGetTypeID(messageBody) == WKArrayGetTypeID());
+        WKArrayRef originURLs = static_cast<WKArrayRef>(messageBody);
+        TestController::singleton().setAppBoundDomains(originURLs);
+        return;
+    }
+
     ASSERT_NOT_REACHED();
 }
 
@@ -2011,6 +2018,12 @@
     WKPagePostMessageToInjectedBundle(TestController::singleton().mainWebView()->page(), messageName.get(), 0);
 }
 
+void TestInvocation::didSetAppBoundDomains()
+{
+    WKRetainPtr<WKStringRef> messageName = adoptWK(WKStringCreateWithUTF8CString("CallDidSetAppBoundDomains"));
+    WKPagePostMessageToInjectedBundle(TestController::singleton().mainWebView()->page(), messageName.get(), nullptr);
+}
+
 void TestInvocation::dumpResourceLoadStatistics()
 {
     m_shouldDumpResourceLoadStatistics = true;

Modified: trunk/Tools/WebKitTestRunner/TestInvocation.h (261241 => 261242)


--- trunk/Tools/WebKitTestRunner/TestInvocation.h	2020-05-06 18:43:19 UTC (rev 261241)
+++ trunk/Tools/WebKitTestRunner/TestInvocation.h	2020-05-06 18:44:26 UTC (rev 261242)
@@ -90,7 +90,9 @@
     void didReceiveLoadedThirdPartyDomains(Vector<String>&& domains);
 
     void didRemoveAllSessionCredentials();
-    
+
+    void didSetAppBoundDomains();
+
     void dumpResourceLoadStatistics();
 
     bool canOpenWindows() const { return m_canOpenWindows; }
_______________________________________________
webkit-changes mailing list
webkit-changes@lists.webkit.org
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to