<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head><meta http-equiv="content-type" content="text/html; charset=utf-8" />
<title>[260169] trunk</title>
</head>
<body>

<style type="text/css"><!--
#msg dl.meta { border: 1px #006 solid; background: #369; padding: 6px; color: #fff; }
#msg dl.meta dt { float: left; width: 6em; font-weight: bold; }
#msg dt:after { content:':';}
#msg dl, #msg dt, #msg ul, #msg li, #header, #footer, #logmsg { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt;  }
#msg dl a { font-weight: bold}
#msg dl a:link    { color:#fc3; }
#msg dl a:active  { color:#ff0; }
#msg dl a:visited { color:#cc6; }
h3 { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; font-weight: bold; }
#msg pre { overflow: auto; background: #ffc; border: 1px #fa0 solid; padding: 6px; }
#logmsg { background: #ffc; border: 1px #fa0 solid; padding: 1em 1em 0 1em; }
#logmsg p, #logmsg pre, #logmsg blockquote { margin: 0 0 1em 0; }
#logmsg p, #logmsg li, #logmsg dt, #logmsg dd { line-height: 14pt; }
#logmsg h1, #logmsg h2, #logmsg h3, #logmsg h4, #logmsg h5, #logmsg h6 { margin: .5em 0; }
#logmsg h1:first-child, #logmsg h2:first-child, #logmsg h3:first-child, #logmsg h4:first-child, #logmsg h5:first-child, #logmsg h6:first-child { margin-top: 0; }
#logmsg ul, #logmsg ol { padding: 0; list-style-position: inside; margin: 0 0 0 1em; }
#logmsg ul { text-indent: -1em; padding-left: 1em; }#logmsg ol { text-indent: -1.5em; padding-left: 1.5em; }
#logmsg > ul, #logmsg > ol { margin: 0 0 1em 0; }
#logmsg pre { background: #eee; padding: 1em; }
#logmsg blockquote { border: 1px solid #fa0; border-left-width: 10px; padding: 1em 1em 0 1em; background: white;}
#logmsg dl { margin: 0; }
#logmsg dt { font-weight: bold; }
#logmsg dd { margin: 0; padding: 0 0 0.5em 0; }
#logmsg dd:before { content:'\00bb';}
#logmsg table { border-spacing: 0px; border-collapse: collapse; border-top: 4px solid #fa0; border-bottom: 1px solid #fa0; background: #fff; }
#logmsg table th { text-align: left; font-weight: normal; padding: 0.2em 0.5em; border-top: 1px dotted #fa0; }
#logmsg table td { text-align: right; border-top: 1px dotted #fa0; padding: 0.2em 0.5em; }
#logmsg table thead th { text-align: center; border-bottom: 1px solid #fa0; }
#logmsg table th.Corner { text-align: left; }
#logmsg hr { border: none 0; border-top: 2px dashed #fa0; height: 1px; }
#header, #footer { color: #fff; background: #636; border: 1px #300 solid; padding: 6px; }
#patch { width: 100%; }
#patch h4 {font-family: verdana,arial,helvetica,sans-serif;font-size:10pt;padding:8px;background:#369;color:#fff;margin:0;}
#patch .propset h4, #patch .binary h4 {margin:0;}
#patch pre {padding:0;line-height:1.2em;margin:0;}
#patch .diff {width:100%;background:#eee;padding: 0 0 10px 0;overflow:auto;}
#patch .propset .diff, #patch .binary .diff  {padding:10px 0;}
#patch span {display:block;padding:0 10px;}
#patch .modfile, #patch .addfile, #patch .delfile, #patch .propset, #patch .binary, #patch .copfile {border:1px solid #ccc;margin:10px 0;}
#patch ins {background:#dfd;text-decoration:none;display:block;padding:0 10px;}
#patch del {background:#fdd;text-decoration:none;display:block;padding:0 10px;}
#patch .lines, .info {color:#888;background:#fff;}
--></style>
<div id="msg">
<dl class="meta">
<dt>Revision</dt> <dd><a href="http://trac.webkit.org/projects/webkit/changeset/260169">260169</a></dd>
<dt>Author</dt> <dd>wilander@apple.com</dd>
<dt>Date</dt> <dd>2020-04-15 20:38:09 -0700 (Wed, 15 Apr 2020)</dd>
</dl>

<h3>Log Message</h3>
<pre>Add SPI to configure WebsiteDataStores with a URL for standalone web applications and use it to disable first-party website data removal in ITP
https://bugs.webkit.org/show_bug.cgi?id=209634
<rdar://problem/60943970>

Reviewed by Alex Christensen.

Source/WebKit:

This change adds a new property to _WKWebsiteDataStoreConfiguration.h called
standaloneApplicationURL with which the hosting application can inform the
website data store that it's running as a standalone web application.

This change also forwards an existing standaloneApplicationURL as a
WebCore::RegistrableDomain into ITP so that explicit exemptions can be made
to first parties of standalone web applications. The exemptions made here
all for all of ITP's website data removal. This part of the change is
covered by the new layout tests.

Tests: http/tests/resourceLoadStatistics/standalone-web-application-exempt-from-website-data-deletion-database.html
       http/tests/resourceLoadStatistics/standalone-web-application-exempt-from-website-data-deletion.html

* NetworkProcess/Classifier/ResourceLoadStatisticsDatabaseStore.cpp:
* NetworkProcess/Classifier/ResourceLoadStatisticsMemoryStore.cpp:
(WebKit::ResourceLoadStatisticsMemoryStore::registrableDomainsToDeleteOrRestrictWebsiteDataFor):
* NetworkProcess/Classifier/ResourceLoadStatisticsStore.h:
(WebKit::ResourceLoadStatisticsStore::setStandaloneApplicationDomain):
(WebKit::ResourceLoadStatisticsStore::standaloneApplicationDomain const):
* NetworkProcess/Classifier/WebResourceLoadStatisticsStore.cpp:
(WebKit::WebResourceLoadStatisticsStore::setStandaloneApplicationDomain):
* NetworkProcess/Classifier/WebResourceLoadStatisticsStore.h:
* NetworkProcess/NetworkSession.cpp:
(WebKit::NetworkSession::NetworkSession):
(WebKit::NetworkSession::forwardResourceLoadStatisticsSettings):
* NetworkProcess/NetworkSession.h:
* Shared/ResourceLoadStatisticsParameters.h:
(WebKit::ResourceLoadStatisticsParameters::encode const):
(WebKit::ResourceLoadStatisticsParameters::decode):
* UIProcess/API/Cocoa/_WKWebsiteDataStoreConfiguration.h:
* UIProcess/API/Cocoa/_WKWebsiteDataStoreConfiguration.mm:
(-[_WKWebsiteDataStoreConfiguration standaloneApplicationURL]):
(-[_WKWebsiteDataStoreConfiguration setStandaloneApplication:]):
* UIProcess/WebProcessPool.cpp:
(WebKit::WebProcessPool::ensureNetworkProcess):
* UIProcess/WebsiteData/Cocoa/WebsiteDataStoreCocoa.mm:
(WebKit::WebsiteDataStore::platformSetNetworkParameters):
* UIProcess/WebsiteData/WebsiteDataStoreConfiguration.cpp:
(WebKit::WebsiteDataStoreConfiguration::copy const):
* UIProcess/WebsiteData/WebsiteDataStoreConfiguration.h:
(WebKit::WebsiteDataStoreConfiguration::standaloneApplicationURL const):
(WebKit::WebsiteDataStoreConfiguration::setStandaloneApplicationURL):

Tools:

Added a new test option called standaloneWebApplicationURL so that layout tests can
configure the website data store accordingly. Picking it up and using it requires
creating a new website data store with a configuration that has the standalone web
application URL.

* WebKitTestRunner/TestController.cpp:
(WTR::TestController::configureWebsiteDataStoreTemporaryDirectories):
(WTR::TestController::defaultWebsiteDataStore):
(WTR::parseStringTestHeaderValueAsURL):
(WTR::updateTestOptionsFromTestHeader):
* WebKitTestRunner/TestController.h:
* WebKitTestRunner/TestOptions.h:
(WTR::TestOptions::hasSameInitializationOptions const):
* WebKitTestRunner/cocoa/TestControllerCocoa.mm:
(WTR::TestController::platformCreateWebView):

LayoutTests:

* http/tests/resourceLoadStatistics/standalone-web-application-exempt-from-website-data-deletion-database-expected.txt: Added.
* http/tests/resourceLoadStatistics/standalone-web-application-exempt-from-website-data-deletion-database.html: Added.
* http/tests/resourceLoadStatistics/standalone-web-application-exempt-from-website-data-deletion-expected.txt: Added.
* http/tests/resourceLoadStatistics/standalone-web-application-exempt-from-website-data-deletion.html: Added.</pre>

<h3>Modified Paths</h3>
<ul>
<li><a href="#trunkLayoutTestsChangeLog">trunk/LayoutTests/ChangeLog</a></li>
<li><a href="#trunkSourceWebKitChangeLog">trunk/Source/WebKit/ChangeLog</a></li>
<li><a href="#trunkSourceWebKitNetworkProcessClassifierResourceLoadStatisticsDatabaseStorecpp">trunk/Source/WebKit/NetworkProcess/Classifier/ResourceLoadStatisticsDatabaseStore.cpp</a></li>
<li><a href="#trunkSourceWebKitNetworkProcessClassifierResourceLoadStatisticsMemoryStorecpp">trunk/Source/WebKit/NetworkProcess/Classifier/ResourceLoadStatisticsMemoryStore.cpp</a></li>
<li><a href="#trunkSourceWebKitNetworkProcessClassifierResourceLoadStatisticsStoreh">trunk/Source/WebKit/NetworkProcess/Classifier/ResourceLoadStatisticsStore.h</a></li>
<li><a href="#trunkSourceWebKitNetworkProcessClassifierWebResourceLoadStatisticsStorecpp">trunk/Source/WebKit/NetworkProcess/Classifier/WebResourceLoadStatisticsStore.cpp</a></li>
<li><a href="#trunkSourceWebKitNetworkProcessClassifierWebResourceLoadStatisticsStoreh">trunk/Source/WebKit/NetworkProcess/Classifier/WebResourceLoadStatisticsStore.h</a></li>
<li><a href="#trunkSourceWebKitNetworkProcessNetworkSessioncpp">trunk/Source/WebKit/NetworkProcess/NetworkSession.cpp</a></li>
<li><a href="#trunkSourceWebKitNetworkProcessNetworkSessionh">trunk/Source/WebKit/NetworkProcess/NetworkSession.h</a></li>
<li><a href="#trunkSourceWebKitSharedResourceLoadStatisticsParametersh">trunk/Source/WebKit/Shared/ResourceLoadStatisticsParameters.h</a></li>
<li><a href="#trunkSourceWebKitUIProcessAPICocoa_WKWebsiteDataStoreConfigurationh">trunk/Source/WebKit/UIProcess/API/Cocoa/_WKWebsiteDataStoreConfiguration.h</a></li>
<li><a href="#trunkSourceWebKitUIProcessAPICocoa_WKWebsiteDataStoreConfigurationmm">trunk/Source/WebKit/UIProcess/API/Cocoa/_WKWebsiteDataStoreConfiguration.mm</a></li>
<li><a href="#trunkSourceWebKitUIProcessWebProcessPoolcpp">trunk/Source/WebKit/UIProcess/WebProcessPool.cpp</a></li>
<li><a href="#trunkSourceWebKitUIProcessWebsiteDataCocoaWebsiteDataStoreCocoamm">trunk/Source/WebKit/UIProcess/WebsiteData/Cocoa/WebsiteDataStoreCocoa.mm</a></li>
<li><a href="#trunkSourceWebKitUIProcessWebsiteDataWebsiteDataStoreConfigurationcpp">trunk/Source/WebKit/UIProcess/WebsiteData/WebsiteDataStoreConfiguration.cpp</a></li>
<li><a href="#trunkSourceWebKitUIProcessWebsiteDataWebsiteDataStoreConfigurationh">trunk/Source/WebKit/UIProcess/WebsiteData/WebsiteDataStoreConfiguration.h</a></li>
<li><a href="#trunkToolsChangeLog">trunk/Tools/ChangeLog</a></li>
<li><a href="#trunkToolsWebKitTestRunnerTestControllercpp">trunk/Tools/WebKitTestRunner/TestController.cpp</a></li>
<li><a href="#trunkToolsWebKitTestRunnerTestControllerh">trunk/Tools/WebKitTestRunner/TestController.h</a></li>
<li><a href="#trunkToolsWebKitTestRunnerTestOptionsh">trunk/Tools/WebKitTestRunner/TestOptions.h</a></li>
<li><a href="#trunkToolsWebKitTestRunnercocoaTestControllerCocoamm">trunk/Tools/WebKitTestRunner/cocoa/TestControllerCocoa.mm</a></li>
</ul>

<h3>Added Paths</h3>
<ul>
<li><a href="#trunkLayoutTestshttptestsresourceLoadStatisticsstandalonewebapplicationexemptfromwebsitedatadeletiondatabaseexpectedtxt">trunk/LayoutTests/http/tests/resourceLoadStatistics/standalone-web-application-exempt-from-website-data-deletion-database-expected.txt</a></li>
<li><a href="#trunkLayoutTestshttptestsresourceLoadStatisticsstandalonewebapplicationexemptfromwebsitedatadeletiondatabasehtml">trunk/LayoutTests/http/tests/resourceLoadStatistics/standalone-web-application-exempt-from-website-data-deletion-database.html</a></li>
<li><a href="#trunkLayoutTestshttptestsresourceLoadStatisticsstandalonewebapplicationexemptfromwebsitedatadeletionexpectedtxt">trunk/LayoutTests/http/tests/resourceLoadStatistics/standalone-web-application-exempt-from-website-data-deletion-expected.txt</a></li>
<li><a href="#trunkLayoutTestshttptestsresourceLoadStatisticsstandalonewebapplicationexemptfromwebsitedatadeletionhtml">trunk/LayoutTests/http/tests/resourceLoadStatistics/standalone-web-application-exempt-from-website-data-deletion.html</a></li>
</ul>

</div>
<div id="patch">
<h3>Diff</h3>
<a id="trunkLayoutTestsChangeLog"></a>
<div class="modfile"><h4>Modified: trunk/LayoutTests/ChangeLog (260168 => 260169)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/ChangeLog      2020-04-16 02:19:13 UTC (rev 260168)
+++ trunk/LayoutTests/ChangeLog 2020-04-16 03:38:09 UTC (rev 260169)
</span><span class="lines">@@ -1,3 +1,16 @@
</span><ins>+2020-04-15  John Wilander  <wilander@apple.com>
+
+        Add SPI to configure WebsiteDataStores with a URL for standalone web applications and use it to disable first-party website data removal in ITP
+        https://bugs.webkit.org/show_bug.cgi?id=209634
+        <rdar://problem/60943970>
+
+        Reviewed by Alex Christensen.
+
+        * http/tests/resourceLoadStatistics/standalone-web-application-exempt-from-website-data-deletion-database-expected.txt: Added.
+        * http/tests/resourceLoadStatistics/standalone-web-application-exempt-from-website-data-deletion-database.html: Added.
+        * http/tests/resourceLoadStatistics/standalone-web-application-exempt-from-website-data-deletion-expected.txt: Added.
+        * http/tests/resourceLoadStatistics/standalone-web-application-exempt-from-website-data-deletion.html: Added.
+
</ins><span class="cx"> 2020-04-15  Diego Pino Garcia  <dpino@igalia.com>
</span><span class="cx"> 
</span><span class="cx">         [GTK] Gardening, emit baselines after r260139
</span></span></pre></div>
<a id="trunkLayoutTestshttptestsresourceLoadStatisticsstandalonewebapplicationexemptfromwebsitedatadeletiondatabaseexpectedtxt"></a>
<div class="addfile"><h4>Added: trunk/LayoutTests/http/tests/resourceLoadStatistics/standalone-web-application-exempt-from-website-data-deletion-database-expected.txt (0 => 260169)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/http/tests/resourceLoadStatistics/standalone-web-application-exempt-from-website-data-deletion-database-expected.txt                           (rev 0)
+++ trunk/LayoutTests/http/tests/resourceLoadStatistics/standalone-web-application-exempt-from-website-data-deletion-database-expected.txt      2020-04-16 03:38:09 UTC (rev 260169)
</span><span class="lines">@@ -0,0 +1,15 @@
</span><ins>+Check that non-cookie website data does not get removed after a period of no user interaction if the website is a standalone web application.
+
+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.
+
+
</ins></span></pre></div>
<a id="trunkLayoutTestshttptestsresourceLoadStatisticsstandalonewebapplicationexemptfromwebsitedatadeletiondatabasehtml"></a>
<div class="addfile"><h4>Added: trunk/LayoutTests/http/tests/resourceLoadStatistics/standalone-web-application-exempt-from-website-data-deletion-database.html (0 => 260169)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/http/tests/resourceLoadStatistics/standalone-web-application-exempt-from-website-data-deletion-database.html                           (rev 0)
+++ trunk/LayoutTests/http/tests/resourceLoadStatistics/standalone-web-application-exempt-from-website-data-deletion-database.html      2020-04-16 03:38:09 UTC (rev 260169)
</span><span class="lines">@@ -0,0 +1,255 @@
</span><ins>+<!-- webkit-test-runner [ standaloneWebApplicationURL=http://127.0.0.1 ] -->
+<!DOCTYPE html>
+<html>
+<head>
+    <script src="/cookies/resources/cookie-utilities.js"></script>
+    <script src="resources/util.js"></script>
+</head>
+<body onload="setTimeout('runTest()', 0)">
+<div id="description">Check that non-cookie website data does not get removed after a period of no user interaction if the website is a standalone web application.</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.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>
</ins></span></pre></div>
<a id="trunkLayoutTestshttptestsresourceLoadStatisticsstandalonewebapplicationexemptfromwebsitedatadeletionexpectedtxt"></a>
<div class="addfile"><h4>Added: trunk/LayoutTests/http/tests/resourceLoadStatistics/standalone-web-application-exempt-from-website-data-deletion-expected.txt (0 => 260169)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/http/tests/resourceLoadStatistics/standalone-web-application-exempt-from-website-data-deletion-expected.txt                            (rev 0)
+++ trunk/LayoutTests/http/tests/resourceLoadStatistics/standalone-web-application-exempt-from-website-data-deletion-expected.txt       2020-04-16 03:38:09 UTC (rev 260169)
</span><span class="lines">@@ -0,0 +1,15 @@
</span><ins>+Check that non-cookie website data does not get removed after a period of no user interaction if the website is a standalone web application.
+
+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.
+
+
</ins></span></pre></div>
<a id="trunkLayoutTestshttptestsresourceLoadStatisticsstandalonewebapplicationexemptfromwebsitedatadeletionhtml"></a>
<div class="addfile"><h4>Added: trunk/LayoutTests/http/tests/resourceLoadStatistics/standalone-web-application-exempt-from-website-data-deletion.html (0 => 260169)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/http/tests/resourceLoadStatistics/standalone-web-application-exempt-from-website-data-deletion.html                            (rev 0)
+++ trunk/LayoutTests/http/tests/resourceLoadStatistics/standalone-web-application-exempt-from-website-data-deletion.html       2020-04-16 03:38:09 UTC (rev 260169)
</span><span class="lines">@@ -0,0 +1,254 @@
</span><ins>+<!-- webkit-test-runner [ standaloneWebApplicationURL=http://127.0.0.1 ] -->
+<!DOCTYPE html>
+<html>
+<head>
+    <script src="/cookies/resources/cookie-utilities.js"></script>
+    <script src="resources/util.js"></script>
+</head>
+<body onload="setTimeout('runTest()', 0)">
+<div id="description">Check that non-cookie website data does not get removed after a period of no user interaction if the website is a standalone web application.</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.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>
</ins></span></pre></div>
<a id="trunkSourceWebKitChangeLog"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebKit/ChangeLog (260168 => 260169)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebKit/ChangeLog    2020-04-16 02:19:13 UTC (rev 260168)
+++ trunk/Source/WebKit/ChangeLog       2020-04-16 03:38:09 UTC (rev 260169)
</span><span class="lines">@@ -1,3 +1,54 @@
</span><ins>+2020-04-15  John Wilander  <wilander@apple.com>
+
+        Add SPI to configure WebsiteDataStores with a URL for standalone web applications and use it to disable first-party website data removal in ITP
+        https://bugs.webkit.org/show_bug.cgi?id=209634
+        <rdar://problem/60943970>
+
+        Reviewed by Alex Christensen.
+
+        This change adds a new property to _WKWebsiteDataStoreConfiguration.h called
+        standaloneApplicationURL with which the hosting application can inform the
+        website data store that it's running as a standalone web application.
+
+        This change also forwards an existing standaloneApplicationURL as a
+        WebCore::RegistrableDomain into ITP so that explicit exemptions can be made
+        to first parties of standalone web applications. The exemptions made here
+        all for all of ITP's website data removal. This part of the change is
+        covered by the new layout tests.
+
+        Tests: http/tests/resourceLoadStatistics/standalone-web-application-exempt-from-website-data-deletion-database.html
+               http/tests/resourceLoadStatistics/standalone-web-application-exempt-from-website-data-deletion.html
+
+        * NetworkProcess/Classifier/ResourceLoadStatisticsDatabaseStore.cpp:
+        * NetworkProcess/Classifier/ResourceLoadStatisticsMemoryStore.cpp:
+        (WebKit::ResourceLoadStatisticsMemoryStore::registrableDomainsToDeleteOrRestrictWebsiteDataFor):
+        * NetworkProcess/Classifier/ResourceLoadStatisticsStore.h:
+        (WebKit::ResourceLoadStatisticsStore::setStandaloneApplicationDomain):
+        (WebKit::ResourceLoadStatisticsStore::standaloneApplicationDomain const):
+        * NetworkProcess/Classifier/WebResourceLoadStatisticsStore.cpp:
+        (WebKit::WebResourceLoadStatisticsStore::setStandaloneApplicationDomain):
+        * NetworkProcess/Classifier/WebResourceLoadStatisticsStore.h:
+        * NetworkProcess/NetworkSession.cpp:
+        (WebKit::NetworkSession::NetworkSession):
+        (WebKit::NetworkSession::forwardResourceLoadStatisticsSettings):
+        * NetworkProcess/NetworkSession.h:
+        * Shared/ResourceLoadStatisticsParameters.h:
+        (WebKit::ResourceLoadStatisticsParameters::encode const):
+        (WebKit::ResourceLoadStatisticsParameters::decode):
+        * UIProcess/API/Cocoa/_WKWebsiteDataStoreConfiguration.h:
+        * UIProcess/API/Cocoa/_WKWebsiteDataStoreConfiguration.mm:
+        (-[_WKWebsiteDataStoreConfiguration standaloneApplicationURL]):
+        (-[_WKWebsiteDataStoreConfiguration setStandaloneApplication:]):
+        * UIProcess/WebProcessPool.cpp:
+        (WebKit::WebProcessPool::ensureNetworkProcess):
+        * UIProcess/WebsiteData/Cocoa/WebsiteDataStoreCocoa.mm:
+        (WebKit::WebsiteDataStore::platformSetNetworkParameters):
+        * UIProcess/WebsiteData/WebsiteDataStoreConfiguration.cpp:
+        (WebKit::WebsiteDataStoreConfiguration::copy const):
+        * UIProcess/WebsiteData/WebsiteDataStoreConfiguration.h:
+        (WebKit::WebsiteDataStoreConfiguration::standaloneApplicationURL const):
+        (WebKit::WebsiteDataStoreConfiguration::setStandaloneApplicationURL):
+
</ins><span class="cx"> 2020-04-15  Chris Dumez  <cdumez@apple.com>
</span><span class="cx"> 
</span><span class="cx">         [IPC Hardening] MachMessage::messageSize() should use checked arithmetic
</span></span></pre></div>
<a id="trunkSourceWebKitNetworkProcessClassifierResourceLoadStatisticsDatabaseStorecpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebKit/NetworkProcess/Classifier/ResourceLoadStatisticsDatabaseStore.cpp (260168 => 260169)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebKit/NetworkProcess/Classifier/ResourceLoadStatisticsDatabaseStore.cpp    2020-04-16 02:19:13 UTC (rev 260168)
+++ trunk/Source/WebKit/NetworkProcess/Classifier/ResourceLoadStatisticsDatabaseStore.cpp       2020-04-16 03:38:09 UTC (rev 260169)
</span><span class="lines">@@ -2461,6 +2461,8 @@
</span><span class="cx">     Vector<DomainData> domains = this->domains();
</span><span class="cx">     Vector<unsigned> domainIDsToClearGrandfathering;
</span><span class="cx">     for (auto& statistic : domains) {
</span><ins>+        if (statistic.registrableDomain == standaloneApplicationDomain())
+            continue;
</ins><span class="cx">         oldestUserInteraction = std::min(oldestUserInteraction, statistic.mostRecentUserInteractionTime);
</span><span class="cx">         if (shouldRemoveAllWebsiteDataFor(statistic, shouldCheckForGrandfathering)) {
</span><span class="cx">             toDeleteOrRestrictFor.domainsToDeleteAllCookiesFor.append(statistic.registrableDomain);
</span></span></pre></div>
<a id="trunkSourceWebKitNetworkProcessClassifierResourceLoadStatisticsMemoryStorecpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebKit/NetworkProcess/Classifier/ResourceLoadStatisticsMemoryStore.cpp (260168 => 260169)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebKit/NetworkProcess/Classifier/ResourceLoadStatisticsMemoryStore.cpp      2020-04-16 02:19:13 UTC (rev 260168)
+++ trunk/Source/WebKit/NetworkProcess/Classifier/ResourceLoadStatisticsMemoryStore.cpp 2020-04-16 03:38:09 UTC (rev 260169)
</span><span class="lines">@@ -980,6 +980,8 @@
</span><span class="cx">     auto oldestUserInteraction = now;
</span><span class="cx">     RegistrableDomainsToDeleteOrRestrictWebsiteDataFor toDeleteOrRestrictFor;
</span><span class="cx">     for (auto& statistic : m_resourceStatisticsMap.values()) {
</span><ins>+        if (statistic.registrableDomain == standaloneApplicationDomain())
+            continue;
</ins><span class="cx">         oldestUserInteraction = std::min(oldestUserInteraction, statistic.mostRecentUserInteractionTime);
</span><span class="cx">         if (shouldRemoveAllWebsiteDataFor(statistic, shouldCheckForGrandfathering)) {
</span><span class="cx">             toDeleteOrRestrictFor.domainsToDeleteAllCookiesFor.append(statistic.registrableDomain);
</span></span></pre></div>
<a id="trunkSourceWebKitNetworkProcessClassifierResourceLoadStatisticsStoreh"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebKit/NetworkProcess/Classifier/ResourceLoadStatisticsStore.h (260168 => 260169)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebKit/NetworkProcess/Classifier/ResourceLoadStatisticsStore.h      2020-04-16 02:19:13 UTC (rev 260168)
+++ trunk/Source/WebKit/NetworkProcess/Classifier/ResourceLoadStatisticsStore.h 2020-04-16 03:38:09 UTC (rev 260169)
</span><span class="lines">@@ -168,6 +168,7 @@
</span><span class="cx">     void setSameSiteStrictEnforcementEnabled(WebCore::SameSiteStrictEnforcementEnabled enabled) { m_sameSiteStrictEnforcementEnabled = enabled; };
</span><span class="cx">     bool isSameSiteStrictEnforcementEnabled() const { return m_sameSiteStrictEnforcementEnabled == WebCore::SameSiteStrictEnforcementEnabled::Yes; };
</span><span class="cx">     void setFirstPartyWebsiteDataRemovalMode(WebCore::FirstPartyWebsiteDataRemovalMode mode) { m_firstPartyWebsiteDataRemovalMode = mode; }
</span><ins>+    void setStandaloneApplicationDomain(RegistrableDomain&& domain) { m_standaloneApplicationDomain = WTFMove(domain); }
</ins><span class="cx"> 
</span><span class="cx">     virtual bool areAllThirdPartyCookiesBlockedUnder(const TopFrameDomain&) = 0;
</span><span class="cx">     virtual void hasStorageAccess(const SubFrameDomain&, const TopFrameDomain&, Optional<WebCore::FrameIdentifier>, WebCore::PageIdentifier, CompletionHandler<void(bool)>&&) = 0;
</span><span class="lines">@@ -246,6 +247,7 @@
</span><span class="cx">     bool debugLoggingEnabled() const { return m_debugLoggingEnabled; };
</span><span class="cx">     bool debugModeEnabled() const { return m_debugModeEnabled; }
</span><span class="cx">     WebCore::FirstPartyWebsiteDataRemovalMode firstPartyWebsiteDataRemovalMode() const { return m_firstPartyWebsiteDataRemovalMode; }
</span><ins>+    RegistrableDomain standaloneApplicationDomain() const { return m_standaloneApplicationDomain; }
</ins><span class="cx"> 
</span><span class="cx">     static constexpr unsigned maxNumberOfRecursiveCallsInRedirectTraceBack { 50 };
</span><span class="cx">     
</span><span class="lines">@@ -286,6 +288,7 @@
</span><span class="cx">     bool m_dataRecordsBeingRemoved { false };
</span><span class="cx">     ShouldIncludeLocalhost m_shouldIncludeLocalhost { ShouldIncludeLocalhost::Yes };
</span><span class="cx">     WebCore::FirstPartyWebsiteDataRemovalMode m_firstPartyWebsiteDataRemovalMode { WebCore::FirstPartyWebsiteDataRemovalMode::AllButCookies };
</span><ins>+    RegistrableDomain m_standaloneApplicationDomain;
</ins><span class="cx"> };
</span><span class="cx"> 
</span><span class="cx"> } // namespace WebKit
</span></span></pre></div>
<a id="trunkSourceWebKitNetworkProcessClassifierWebResourceLoadStatisticsStorecpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebKit/NetworkProcess/Classifier/WebResourceLoadStatisticsStore.cpp (260168 => 260169)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebKit/NetworkProcess/Classifier/WebResourceLoadStatisticsStore.cpp 2020-04-16 02:19:13 UTC (rev 260168)
+++ trunk/Source/WebKit/NetworkProcess/Classifier/WebResourceLoadStatisticsStore.cpp    2020-04-16 03:38:09 UTC (rev 260169)
</span><span class="lines">@@ -635,6 +635,26 @@
</span><span class="cx">     });
</span><span class="cx"> }
</span><span class="cx"> 
</span><ins>+void WebResourceLoadStatisticsStore::setStandaloneApplicationDomain(const RegistrableDomain& domain, CompletionHandler<void()>&& completionHandler)
+{
+    ASSERT(RunLoop::isMain());
+
+    if (isEphemeral() || domain.isEmpty()) {
+        completionHandler();
+        return;
+    }
+
+    RELEASE_LOG(ResourceLoadStatistics, "WebResourceLoadStatisticsStore::setStandaloneApplicationDomain() called with non-empty domain.");
+
+    postTask([this, domain = domain.isolatedCopy(), completionHandler = WTFMove(completionHandler)]() mutable {
+        if (m_statisticsStore)
+            m_statisticsStore->setStandaloneApplicationDomain(WTFMove(domain));
+        postTaskReply([completionHandler = WTFMove(completionHandler)]() mutable {
+            completionHandler();
+        });
+    });
+}
+
</ins><span class="cx"> void WebResourceLoadStatisticsStore::didCreateNetworkProcess()
</span><span class="cx"> {
</span><span class="cx">     ASSERT(RunLoop::isMain());
</span></span></pre></div>
<a id="trunkSourceWebKitNetworkProcessClassifierWebResourceLoadStatisticsStoreh"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebKit/NetworkProcess/Classifier/WebResourceLoadStatisticsStore.h (260168 => 260169)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebKit/NetworkProcess/Classifier/WebResourceLoadStatisticsStore.h   2020-04-16 02:19:13 UTC (rev 260168)
+++ trunk/Source/WebKit/NetworkProcess/Classifier/WebResourceLoadStatisticsStore.h      2020-04-16 03:38:09 UTC (rev 260169)
</span><span class="lines">@@ -277,6 +277,7 @@
</span><span class="cx">     void setThirdPartyCookieBlockingMode(WebCore::ThirdPartyCookieBlockingMode);
</span><span class="cx">     void setSameSiteStrictEnforcementEnabled(WebCore::SameSiteStrictEnforcementEnabled);
</span><span class="cx">     void setFirstPartyWebsiteDataRemovalMode(WebCore::FirstPartyWebsiteDataRemovalMode, CompletionHandler<void()>&&);
</span><ins>+    void setStandaloneApplicationDomain(const RegistrableDomain&, CompletionHandler<void()>&&);
</ins><span class="cx">     void didCreateNetworkProcess();
</span><span class="cx"> 
</span><span class="cx">     void notifyResourceLoadStatisticsProcessed();
</span></span></pre></div>
<a id="trunkSourceWebKitNetworkProcessNetworkSessioncpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebKit/NetworkProcess/NetworkSession.cpp (260168 => 260169)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebKit/NetworkProcess/NetworkSession.cpp    2020-04-16 02:19:13 UTC (rev 260168)
+++ trunk/Source/WebKit/NetworkProcess/NetworkSession.cpp       2020-04-16 03:38:09 UTC (rev 260169)
</span><span class="lines">@@ -89,6 +89,7 @@
</span><span class="cx">     , m_thirdPartyCookieBlockingMode(parameters.resourceLoadStatisticsParameters.thirdPartyCookieBlockingMode)
</span><span class="cx">     , m_sameSiteStrictEnforcementEnabled(parameters.resourceLoadStatisticsParameters.sameSiteStrictEnforcementEnabled)
</span><span class="cx">     , m_firstPartyWebsiteDataRemovalMode(parameters.resourceLoadStatisticsParameters.firstPartyWebsiteDataRemovalMode)
</span><ins>+    , m_standaloneApplicationDomain(parameters.resourceLoadStatisticsParameters.standaloneApplicationDomain)
</ins><span class="cx"> #endif
</span><span class="cx">     , m_adClickAttribution(makeUniqueRef<AdClickAttributionManager>(networkProcess, parameters.sessionID))
</span><span class="cx">     , m_testSpeedMultiplier(parameters.testSpeedMultiplier)
</span><span class="lines">@@ -204,6 +205,7 @@
</span><span class="cx">     m_resourceLoadStatistics->setThirdPartyCookieBlockingMode(m_thirdPartyCookieBlockingMode);
</span><span class="cx">     m_resourceLoadStatistics->setSameSiteStrictEnforcementEnabled(m_sameSiteStrictEnforcementEnabled);
</span><span class="cx">     m_resourceLoadStatistics->setFirstPartyWebsiteDataRemovalMode(m_firstPartyWebsiteDataRemovalMode, [] { });
</span><ins>+    m_resourceLoadStatistics->setStandaloneApplicationDomain(m_standaloneApplicationDomain, [] { });
</ins><span class="cx"> }
</span><span class="cx"> 
</span><span class="cx"> bool NetworkSession::isResourceLoadStatisticsEnabled() const
</span></span></pre></div>
<a id="trunkSourceWebKitNetworkProcessNetworkSessionh"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebKit/NetworkProcess/NetworkSession.h (260168 => 260169)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebKit/NetworkProcess/NetworkSession.h      2020-04-16 02:19:13 UTC (rev 260168)
+++ trunk/Source/WebKit/NetworkProcess/NetworkSession.h 2020-04-16 03:38:09 UTC (rev 260169)
</span><span class="lines">@@ -164,6 +164,7 @@
</span><span class="cx">     WebCore::ThirdPartyCookieBlockingMode m_thirdPartyCookieBlockingMode { WebCore::ThirdPartyCookieBlockingMode::All };
</span><span class="cx">     WebCore::SameSiteStrictEnforcementEnabled m_sameSiteStrictEnforcementEnabled { WebCore::SameSiteStrictEnforcementEnabled::No };
</span><span class="cx">     WebCore::FirstPartyWebsiteDataRemovalMode m_firstPartyWebsiteDataRemovalMode { WebCore::FirstPartyWebsiteDataRemovalMode::AllButCookies };
</span><ins>+    WebCore::RegistrableDomain m_standaloneApplicationDomain;
</ins><span class="cx"> #endif
</span><span class="cx">     bool m_isStaleWhileRevalidateEnabled { false };
</span><span class="cx">     UniqueRef<AdClickAttributionManager> m_adClickAttribution;
</span></span></pre></div>
<a id="trunkSourceWebKitSharedResourceLoadStatisticsParametersh"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebKit/Shared/ResourceLoadStatisticsParameters.h (260168 => 260169)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebKit/Shared/ResourceLoadStatisticsParameters.h    2020-04-16 02:19:13 UTC (rev 260168)
+++ trunk/Source/WebKit/Shared/ResourceLoadStatisticsParameters.h       2020-04-16 03:38:09 UTC (rev 260169)
</span><span class="lines">@@ -47,6 +47,7 @@
</span><span class="cx">     WebCore::SameSiteStrictEnforcementEnabled sameSiteStrictEnforcementEnabled { WebCore::SameSiteStrictEnforcementEnabled::No };
</span><span class="cx"> #endif
</span><span class="cx">     WebCore::FirstPartyWebsiteDataRemovalMode firstPartyWebsiteDataRemovalMode { WebCore::FirstPartyWebsiteDataRemovalMode::AllButCookies };
</span><ins>+    WebCore::RegistrableDomain standaloneApplicationDomain { };
</ins><span class="cx">     WebCore::RegistrableDomain manualPrevalentResource { };
</span><span class="cx">     
</span><span class="cx">     void encode(IPC::Encoder& encoder) const
</span><span class="lines">@@ -63,6 +64,7 @@
</span><span class="cx">         encoder << sameSiteStrictEnforcementEnabled;
</span><span class="cx"> #endif
</span><span class="cx">         encoder << firstPartyWebsiteDataRemovalMode;
</span><ins>+        encoder << standaloneApplicationDomain;
</ins><span class="cx">         encoder << manualPrevalentResource;
</span><span class="cx">     }
</span><span class="cx"> 
</span><span class="lines">@@ -120,6 +122,11 @@
</span><span class="cx">         if (!firstPartyWebsiteDataRemovalMode)
</span><span class="cx">             return WTF::nullopt;
</span><span class="cx"> 
</span><ins>+        Optional<WebCore::RegistrableDomain> standaloneApplicationDomain;
+        decoder >> standaloneApplicationDomain;
+        if (!standaloneApplicationDomain)
+            return WTF::nullopt;
+
</ins><span class="cx">         Optional<WebCore::RegistrableDomain> manualPrevalentResource;
</span><span class="cx">         decoder >> manualPrevalentResource;
</span><span class="cx">         if (!manualPrevalentResource)
</span><span class="lines">@@ -138,6 +145,7 @@
</span><span class="cx">             WTFMove(*sameSiteStrictEnforcementEnabled),
</span><span class="cx"> #endif
</span><span class="cx">             WTFMove(*firstPartyWebsiteDataRemovalMode),
</span><ins>+            WTFMove(*standaloneApplicationDomain),
</ins><span class="cx">             WTFMove(*manualPrevalentResource),
</span><span class="cx">         }};
</span><span class="cx">     }
</span></span></pre></div>
<a id="trunkSourceWebKitUIProcessAPICocoa_WKWebsiteDataStoreConfigurationh"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebKit/UIProcess/API/Cocoa/_WKWebsiteDataStoreConfiguration.h (260168 => 260169)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebKit/UIProcess/API/Cocoa/_WKWebsiteDataStoreConfiguration.h       2020-04-16 02:19:13 UTC (rev 260168)
+++ trunk/Source/WebKit/UIProcess/API/Cocoa/_WKWebsiteDataStoreConfiguration.h  2020-04-16 03:38:09 UTC (rev 260169)
</span><span class="lines">@@ -76,6 +76,8 @@
</span><span class="cx"> 
</span><span class="cx"> @property (nonatomic, nullable, copy) NSURL *alternativeServicesStorageDirectory WK_API_AVAILABLE(macos(WK_MAC_TBA), ios(WK_IOS_TBA));
</span><span class="cx"> 
</span><ins>+@property (nonatomic, nullable, copy) NSURL *standaloneApplicationURL WK_API_AVAILABLE(macos(WK_MAC_TBA), ios(WK_IOS_TBA));
+
</ins><span class="cx"> // Testing only.
</span><span class="cx"> @property (nonatomic) BOOL allLoadsBlockedByDeviceManagementRestrictionsForTesting WK_API_AVAILABLE(macos(10.15), ios(13.0));
</span><span class="cx"> 
</span></span></pre></div>
<a id="trunkSourceWebKitUIProcessAPICocoa_WKWebsiteDataStoreConfigurationmm"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebKit/UIProcess/API/Cocoa/_WKWebsiteDataStoreConfiguration.mm (260168 => 260169)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebKit/UIProcess/API/Cocoa/_WKWebsiteDataStoreConfiguration.mm      2020-04-16 02:19:13 UTC (rev 260168)
+++ trunk/Source/WebKit/UIProcess/API/Cocoa/_WKWebsiteDataStoreConfiguration.mm 2020-04-16 03:38:09 UTC (rev 260169)
</span><span class="lines">@@ -443,6 +443,16 @@
</span><span class="cx">     _configuration->setProxyConfiguration((__bridge CFDictionaryRef)[configuration copy]);
</span><span class="cx"> }
</span><span class="cx"> 
</span><ins>+- (NSURL *)standaloneApplicationURL
+{
+    return _configuration->standaloneApplicationURL();
+}
+
+- (void)setStandaloneApplicationURL:(NSURL *)url
+{
+    _configuration->setStandaloneApplicationURL(url);
+}
+
</ins><span class="cx"> - (BOOL)allLoadsBlockedByDeviceManagementRestrictionsForTesting
</span><span class="cx"> {
</span><span class="cx">     return _configuration->allLoadsBlockedByDeviceManagementRestrictionsForTesting();
</span></span></pre></div>
<a id="trunkSourceWebKitUIProcessWebProcessPoolcpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebKit/UIProcess/WebProcessPool.cpp (260168 => 260169)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebKit/UIProcess/WebProcessPool.cpp 2020-04-16 02:19:13 UTC (rev 260168)
+++ trunk/Source/WebKit/UIProcess/WebProcessPool.cpp    2020-04-16 03:38:09 UTC (rev 260169)
</span><span class="lines">@@ -604,6 +604,7 @@
</span><span class="cx">     WebCore::ThirdPartyCookieBlockingMode thirdPartyCookieBlockingMode = WebCore::ThirdPartyCookieBlockingMode::All;
</span><span class="cx">     WebCore::SameSiteStrictEnforcementEnabled sameSiteStrictEnforcementEnabled = WebCore::SameSiteStrictEnforcementEnabled::No;
</span><span class="cx"> #endif
</span><ins>+    WebCore::RegistrableDomain standaloneApplicationDomain { };
</ins><span class="cx">     WebCore::FirstPartyWebsiteDataRemovalMode firstPartyWebsiteDataRemovalMode = WebCore::FirstPartyWebsiteDataRemovalMode::AllButCookies;
</span><span class="cx">     WebCore::RegistrableDomain manualPrevalentResource { };
</span><span class="cx">     WEB_PROCESS_POOL_ADDITIONS_2
</span><span class="lines">@@ -621,6 +622,7 @@
</span><span class="cx">             sameSiteStrictEnforcementEnabled = networkSessionParameters.resourceLoadStatisticsParameters.sameSiteStrictEnforcementEnabled;
</span><span class="cx"> #endif
</span><span class="cx">             firstPartyWebsiteDataRemovalMode = networkSessionParameters.resourceLoadStatisticsParameters.firstPartyWebsiteDataRemovalMode;
</span><ins>+            standaloneApplicationDomain = networkSessionParameters.resourceLoadStatisticsParameters.standaloneApplicationDomain;
</ins><span class="cx">             manualPrevalentResource = networkSessionParameters.resourceLoadStatisticsParameters.manualPrevalentResource;
</span><span class="cx">         }
</span><span class="cx"> 
</span><span class="lines">@@ -646,6 +648,7 @@
</span><span class="cx">             sameSiteStrictEnforcementEnabled = networkSessionParameters.resourceLoadStatisticsParameters.sameSiteStrictEnforcementEnabled;
</span><span class="cx"> #endif
</span><span class="cx">             firstPartyWebsiteDataRemovalMode = networkSessionParameters.resourceLoadStatisticsParameters.firstPartyWebsiteDataRemovalMode;
</span><ins>+            standaloneApplicationDomain = networkSessionParameters.resourceLoadStatisticsParameters.standaloneApplicationDomain;
</ins><span class="cx">             manualPrevalentResource = networkSessionParameters.resourceLoadStatisticsParameters.manualPrevalentResource;
</span><span class="cx">         }
</span><span class="cx"> 
</span></span></pre></div>
<a id="trunkSourceWebKitUIProcessWebsiteDataCocoaWebsiteDataStoreCocoamm"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebKit/UIProcess/WebsiteData/Cocoa/WebsiteDataStoreCocoa.mm (260168 => 260169)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebKit/UIProcess/WebsiteData/Cocoa/WebsiteDataStoreCocoa.mm 2020-04-16 02:19:13 UTC (rev 260168)
+++ trunk/Source/WebKit/UIProcess/WebsiteData/Cocoa/WebsiteDataStoreCocoa.mm    2020-04-16 03:38:09 UTC (rev 260169)
</span><span class="lines">@@ -180,6 +180,7 @@
</span><span class="cx">     parameters.networkSessionParameters.resourceLoadStatisticsParameters.enableDebugMode = enableResourceLoadStatisticsDebugMode;
</span><span class="cx">     parameters.networkSessionParameters.resourceLoadStatisticsParameters.sameSiteStrictEnforcementEnabled = sameSiteStrictEnforcementEnabled;
</span><span class="cx">     parameters.networkSessionParameters.resourceLoadStatisticsParameters.firstPartyWebsiteDataRemovalMode = firstPartyWebsiteDataRemovalMode;
</span><ins>+    parameters.networkSessionParameters.resourceLoadStatisticsParameters.standaloneApplicationDomain = WebCore::RegistrableDomain { m_configuration->standaloneApplicationURL() };
</ins><span class="cx">     parameters.networkSessionParameters.resourceLoadStatisticsParameters.manualPrevalentResource = WTFMove(resourceLoadStatisticsManualPrevalentResource);
</span><span class="cx"> 
</span><span class="cx">     auto cookieFile = resolvedCookieStorageFile();
</span></span></pre></div>
<a id="trunkSourceWebKitUIProcessWebsiteDataWebsiteDataStoreConfigurationcpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebKit/UIProcess/WebsiteData/WebsiteDataStoreConfiguration.cpp (260168 => 260169)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebKit/UIProcess/WebsiteData/WebsiteDataStoreConfiguration.cpp      2020-04-16 02:19:13 UTC (rev 260168)
+++ trunk/Source/WebKit/UIProcess/WebsiteData/WebsiteDataStoreConfiguration.cpp 2020-04-16 03:38:09 UTC (rev 260169)
</span><span class="lines">@@ -93,6 +93,7 @@
</span><span class="cx">     copy->m_allowsServerPreconnect = this->m_allowsServerPreconnect;
</span><span class="cx">     copy->m_requiresSecureHTTPSProxyConnection = this->m_requiresSecureHTTPSProxyConnection;
</span><span class="cx">     copy->m_preventsSystemHTTPProxyAuthentication = this->m_preventsSystemHTTPProxyAuthentication;
</span><ins>+    copy->m_standaloneApplicationURL = this->m_standaloneApplicationURL;
</ins><span class="cx"> #if PLATFORM(COCOA)
</span><span class="cx">     if (m_proxyConfiguration)
</span><span class="cx">         copy->m_proxyConfiguration = adoptCF(CFDictionaryCreateCopy(nullptr, this->m_proxyConfiguration.get()));
</span></span></pre></div>
<a id="trunkSourceWebKitUIProcessWebsiteDataWebsiteDataStoreConfigurationh"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebKit/UIProcess/WebsiteData/WebsiteDataStoreConfiguration.h (260168 => 260169)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebKit/UIProcess/WebsiteData/WebsiteDataStoreConfiguration.h        2020-04-16 02:19:13 UTC (rev 260168)
+++ trunk/Source/WebKit/UIProcess/WebsiteData/WebsiteDataStoreConfiguration.h   2020-04-16 03:38:09 UTC (rev 260169)
</span><span class="lines">@@ -159,6 +159,9 @@
</span><span class="cx">     bool requiresSecureHTTPSProxyConnection() const { return m_requiresSecureHTTPSProxyConnection; };
</span><span class="cx">     void setRequiresSecureHTTPSProxyConnection(bool requires) { m_requiresSecureHTTPSProxyConnection = requires; }
</span><span class="cx"> 
</span><ins>+    const URL& standaloneApplicationURL() const { return m_standaloneApplicationURL; }
+    void setStandaloneApplicationURL(URL&& url) { m_standaloneApplicationURL = WTFMove(url); }
+
</ins><span class="cx"> private:
</span><span class="cx">     IsPersistent m_isPersistent { IsPersistent::No };
</span><span class="cx"> 
</span><span class="lines">@@ -203,6 +206,7 @@
</span><span class="cx">     bool m_preventsSystemHTTPProxyAuthentication { false };
</span><span class="cx">     bool m_requiresSecureHTTPSProxyConnection { false };
</span><span class="cx">     unsigned m_testSpeedMultiplier { 1 };
</span><ins>+    URL m_standaloneApplicationURL;
</ins><span class="cx"> #if PLATFORM(COCOA)
</span><span class="cx">     RetainPtr<CFDictionaryRef> m_proxyConfiguration;
</span><span class="cx"> #endif
</span></span></pre></div>
<a id="trunkToolsChangeLog"></a>
<div class="modfile"><h4>Modified: trunk/Tools/ChangeLog (260168 => 260169)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Tools/ChangeLog    2020-04-16 02:19:13 UTC (rev 260168)
+++ trunk/Tools/ChangeLog       2020-04-16 03:38:09 UTC (rev 260169)
</span><span class="lines">@@ -1,3 +1,27 @@
</span><ins>+2020-04-15  John Wilander  <wilander@apple.com>
+
+        Add SPI to configure WebsiteDataStores with a URL for standalone web applications and use it to disable first-party website data removal in ITP
+        https://bugs.webkit.org/show_bug.cgi?id=209634
+        <rdar://problem/60943970>
+
+        Reviewed by Alex Christensen.
+
+        Added a new test option called standaloneWebApplicationURL so that layout tests can
+        configure the website data store accordingly. Picking it up and using it requires
+        creating a new website data store with a configuration that has the standalone web
+        application URL.
+
+        * WebKitTestRunner/TestController.cpp:
+        (WTR::TestController::configureWebsiteDataStoreTemporaryDirectories):
+        (WTR::TestController::defaultWebsiteDataStore):
+        (WTR::parseStringTestHeaderValueAsURL):
+        (WTR::updateTestOptionsFromTestHeader):
+        * WebKitTestRunner/TestController.h:
+        * WebKitTestRunner/TestOptions.h:
+        (WTR::TestOptions::hasSameInitializationOptions const):
+        * WebKitTestRunner/cocoa/TestControllerCocoa.mm:
+        (WTR::TestController::platformCreateWebView):
+
</ins><span class="cx"> 2020-04-15  Chris Dumez  <cdumez@apple.com>
</span><span class="cx"> 
</span><span class="cx">         [IPC Hardening] MachMessage::messageSize() should use checked arithmetic
</span></span></pre></div>
<a id="trunkToolsWebKitTestRunnerTestControllercpp"></a>
<div class="modfile"><h4>Modified: trunk/Tools/WebKitTestRunner/TestController.cpp (260168 => 260169)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Tools/WebKitTestRunner/TestController.cpp  2020-04-16 02:19:13 UTC (rev 260168)
+++ trunk/Tools/WebKitTestRunner/TestController.cpp     2020-04-16 03:38:09 UTC (rev 260169)
</span><span class="lines">@@ -521,28 +521,33 @@
</span><span class="cx">     return configuration;
</span><span class="cx"> }
</span><span class="cx"> 
</span><ins>+void TestController::configureWebsiteDataStoreTemporaryDirectories(WKWebsiteDataStoreConfigurationRef configuration)
+{
+    if (const char* dumpRenderTreeTemp = libraryPathForTesting()) {
+        String temporaryFolder = String::fromUTF8(dumpRenderTreeTemp);
+
+        WKWebsiteDataStoreConfigurationSetApplicationCacheDirectory(configuration, toWK(temporaryFolder + pathSeparator + "ApplicationCache").get());
+        WKWebsiteDataStoreConfigurationSetNetworkCacheDirectory(configuration, toWK(temporaryFolder + pathSeparator + "Cache").get());
+        WKWebsiteDataStoreConfigurationSetCacheStorageDirectory(configuration, toWK(temporaryFolder + pathSeparator + "CacheStorage").get());
+        WKWebsiteDataStoreConfigurationSetIndexedDBDatabaseDirectory(configuration, toWK(temporaryFolder + pathSeparator + "Databases" + pathSeparator + "IndexedDB").get());
+        WKWebsiteDataStoreConfigurationSetLocalStorageDirectory(configuration, toWK(temporaryFolder + pathSeparator + "LocalStorage").get());
+        WKWebsiteDataStoreConfigurationSetWebSQLDatabaseDirectory(configuration, toWK(temporaryFolder + pathSeparator + "Databases" + pathSeparator + "WebSQL").get());
+        WKWebsiteDataStoreConfigurationSetMediaKeysStorageDirectory(configuration, toWK(temporaryFolder + pathSeparator + "MediaKeys").get());
+        WKWebsiteDataStoreConfigurationSetResourceLoadStatisticsDirectory(configuration, toWK(temporaryFolder + pathSeparator + "ResourceLoadStatistics").get());
+        WKWebsiteDataStoreConfigurationSetServiceWorkerRegistrationDirectory(configuration, toWK(temporaryFolder + pathSeparator + "ServiceWorkers").get());
+        WKWebsiteDataStoreConfigurationSetPerOriginStorageQuota(configuration, 400 * 1024);
+        WKWebsiteDataStoreConfigurationSetNetworkCacheSpeculativeValidationEnabled(configuration, true);
+        WKWebsiteDataStoreConfigurationSetStaleWhileRevalidateEnabled(configuration, true);
+        WKWebsiteDataStoreConfigurationSetTestingSessionEnabled(configuration, true);
+    }
+}
+
</ins><span class="cx"> WKWebsiteDataStoreRef TestController::defaultWebsiteDataStore()
</span><span class="cx"> {
</span><span class="cx">     static WKWebsiteDataStoreRef dataStore = nullptr;
</span><span class="cx">     if (!dataStore) {
</span><span class="cx">         auto configuration = adoptWK(WKWebsiteDataStoreConfigurationCreate());
</span><del>-        if (const char* dumpRenderTreeTemp = libraryPathForTesting()) {
-            String temporaryFolder = String::fromUTF8(dumpRenderTreeTemp);
-
-            WKWebsiteDataStoreConfigurationSetApplicationCacheDirectory(configuration.get(), toWK(temporaryFolder + pathSeparator + "ApplicationCache").get());
-            WKWebsiteDataStoreConfigurationSetNetworkCacheDirectory(configuration.get(), toWK(temporaryFolder + pathSeparator + "Cache").get());
-            WKWebsiteDataStoreConfigurationSetCacheStorageDirectory(configuration.get(), toWK(temporaryFolder + pathSeparator + "CacheStorage").get());
-            WKWebsiteDataStoreConfigurationSetIndexedDBDatabaseDirectory(configuration.get(), toWK(temporaryFolder + pathSeparator + "Databases" + pathSeparator + "IndexedDB").get());
-            WKWebsiteDataStoreConfigurationSetLocalStorageDirectory(configuration.get(), toWK(temporaryFolder + pathSeparator + "LocalStorage").get());
-            WKWebsiteDataStoreConfigurationSetWebSQLDatabaseDirectory(configuration.get(), toWK(temporaryFolder + pathSeparator + "Databases" + pathSeparator + "WebSQL").get());
-            WKWebsiteDataStoreConfigurationSetMediaKeysStorageDirectory(configuration.get(), toWK(temporaryFolder + pathSeparator + "MediaKeys").get());
-            WKWebsiteDataStoreConfigurationSetResourceLoadStatisticsDirectory(configuration.get(), toWK(temporaryFolder + pathSeparator + "ResourceLoadStatistics").get());
-            WKWebsiteDataStoreConfigurationSetServiceWorkerRegistrationDirectory(configuration.get(), toWK(temporaryFolder + pathSeparator + "ServiceWorkers").get());
-            WKWebsiteDataStoreConfigurationSetPerOriginStorageQuota(configuration.get(), 400 * 1024);
-            WKWebsiteDataStoreConfigurationSetNetworkCacheSpeculativeValidationEnabled(configuration.get(), true);
-            WKWebsiteDataStoreConfigurationSetStaleWhileRevalidateEnabled(configuration.get(), true);
-            WKWebsiteDataStoreConfigurationSetTestingSessionEnabled(configuration.get(), true);
-        }
</del><ins>+        configureWebsiteDataStoreTemporaryDirectories(configuration.get());
</ins><span class="cx">         dataStore = WKWebsiteDataStoreCreateWithConfiguration(configuration.get());
</span><span class="cx">     }
</span><span class="cx">     return dataStore;
</span><span class="lines">@@ -1360,6 +1365,11 @@
</span><span class="cx">     return toSTD(adoptWK(WKURLCopyPath(relativeURL.get())));
</span><span class="cx"> }
</span><span class="cx"> 
</span><ins>+static std::string parseStringTestHeaderValueAsURL(const std::string& value)
+{
+    return toSTD(adoptWK(WKURLCopyString(createTestURL(value.c_str()))));
+}
+
</ins><span class="cx"> static void updateTestOptionsFromTestHeader(TestOptions& testOptions, const std::string& pathOrURL, const std::string& absolutePath)
</span><span class="cx"> {
</span><span class="cx">     std::string filename = absolutePath;
</span><span class="lines">@@ -1515,7 +1525,9 @@
</span><span class="cx">             testOptions.allowTopNavigationToDataURLs = parseBooleanTestHeaderValue(value);
</span><span class="cx">         else if (key == "enableInAppBrowserPrivacy")
</span><span class="cx">             testOptions.enableInAppBrowserPrivacy = parseBooleanTestHeaderValue(value);
</span><del>-        
</del><ins>+        else if (key == "standaloneWebApplicationURL")
+            testOptions.standaloneWebApplicationURL = parseStringTestHeaderValueAsURL(value);
+
</ins><span class="cx">         pairStart = pairEnd + 1;
</span><span class="cx">     }
</span><span class="cx"> }
</span></span></pre></div>
<a id="trunkToolsWebKitTestRunnerTestControllerh"></a>
<div class="modfile"><h4>Modified: trunk/Tools/WebKitTestRunner/TestController.h (260168 => 260169)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Tools/WebKitTestRunner/TestController.h    2020-04-16 02:19:13 UTC (rev 260168)
+++ trunk/Tools/WebKitTestRunner/TestController.h       2020-04-16 03:38:09 UTC (rev 260169)
</span><span class="lines">@@ -88,6 +88,7 @@
</span><span class="cx"> class TestController {
</span><span class="cx"> public:
</span><span class="cx">     static TestController& singleton();
</span><ins>+    static void configureWebsiteDataStoreTemporaryDirectories(WKWebsiteDataStoreConfigurationRef);
</ins><span class="cx">     static WKWebsiteDataStoreRef defaultWebsiteDataStore();
</span><span class="cx"> 
</span><span class="cx">     static const unsigned viewWidth;
</span></span></pre></div>
<a id="trunkToolsWebKitTestRunnerTestOptionsh"></a>
<div class="modfile"><h4>Modified: trunk/Tools/WebKitTestRunner/TestOptions.h (260168 => 260169)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Tools/WebKitTestRunner/TestOptions.h       2020-04-16 02:19:13 UTC (rev 260168)
+++ trunk/Tools/WebKitTestRunner/TestOptions.h  2020-04-16 03:38:09 UTC (rev 260169)
</span><span class="lines">@@ -111,6 +111,7 @@
</span><span class="cx">     std::string applicationManifest;
</span><span class="cx">     std::string jscOptions;
</span><span class="cx">     std::string additionalSupportedImageTypes;
</span><ins>+    std::string standaloneWebApplicationURL;
</ins><span class="cx">     HashMap<String, bool> experimentalFeatures;
</span><span class="cx">     HashMap<String, bool> internalDebugFeatures;
</span><span class="cx">     String contentMode;
</span><span class="lines">@@ -170,7 +171,8 @@
</span><span class="cx">             || enableCaptureVideoInGPUProcess != options.enableCaptureVideoInGPUProcess
</span><span class="cx">             || enableCaptureAudioInGPUProcess != options.enableCaptureAudioInGPUProcess
</span><span class="cx">             || allowTopNavigationToDataURLs != options.allowTopNavigationToDataURLs
</span><del>-            || enableInAppBrowserPrivacy != options.enableInAppBrowserPrivacy)
</del><ins>+            || enableInAppBrowserPrivacy != options.enableInAppBrowserPrivacy
+            || standaloneWebApplicationURL != options.standaloneWebApplicationURL)
</ins><span class="cx">             return false;
</span><span class="cx"> 
</span><span class="cx">         if (!contextOptions.hasSameInitializationOptions(options.contextOptions))
</span></span></pre></div>
<a id="trunkToolsWebKitTestRunnercocoaTestControllerCocoamm"></a>
<div class="modfile"><h4>Modified: trunk/Tools/WebKitTestRunner/cocoa/TestControllerCocoa.mm (260168 => 260169)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Tools/WebKitTestRunner/cocoa/TestControllerCocoa.mm        2020-04-16 02:19:13 UTC (rev 260168)
+++ trunk/Tools/WebKitTestRunner/cocoa/TestControllerCocoa.mm   2020-04-16 03:38:09 UTC (rev 260169)
</span><span class="lines">@@ -51,6 +51,7 @@
</span><span class="cx"> #import <WebKit/_WKApplicationManifest.h>
</span><span class="cx"> #import <WebKit/_WKUserContentExtensionStore.h>
</span><span class="cx"> #import <WebKit/_WKUserContentExtensionStorePrivate.h>
</span><ins>+#import <WebKit/_WKWebsiteDataStoreConfiguration.h>
</ins><span class="cx"> #import <wtf/MainThread.h>
</span><span class="cx"> #import <wtf/cocoa/VectorCocoa.h>
</span><span class="cx"> #import <wtf/spi/cocoa/SecuritySPI.h>
</span><span class="lines">@@ -154,10 +155,15 @@
</span><span class="cx">     if (options.enableEditableImages)
</span><span class="cx">         [copiedConfiguration _setEditableImagesEnabled:YES];
</span><span class="cx"> 
</span><del>-    if (options.useEphemeralSession) {
-        auto ephemeralWebsiteDataStore = [WKWebsiteDataStore nonPersistentDataStore];
-        [ephemeralWebsiteDataStore _setResourceLoadStatisticsEnabled:YES];
-        [copiedConfiguration setWebsiteDataStore:ephemeralWebsiteDataStore];
</del><ins>+    if (options.useEphemeralSession || options.standaloneWebApplicationURL.length()) {
+        auto websiteDataStoreConfig = options.useEphemeralSession ? [[[_WKWebsiteDataStoreConfiguration alloc] initNonPersistentConfiguration] autorelease] : [[[_WKWebsiteDataStoreConfiguration alloc] init] autorelease];
+        if (!options.useEphemeralSession)
+            configureWebsiteDataStoreTemporaryDirectories((WKWebsiteDataStoreConfigurationRef)websiteDataStoreConfig);
+        if (options.standaloneWebApplicationURL.length())
+            [websiteDataStoreConfig setStandaloneApplicationURL:[NSURL URLWithString:[NSString stringWithUTF8String:options.standaloneWebApplicationURL.c_str()]]];
+        auto websiteDataStore = [[[WKWebsiteDataStore alloc] _initWithConfiguration:websiteDataStoreConfig] autorelease];
+        [websiteDataStore _setResourceLoadStatisticsEnabled:YES];
+        [copiedConfiguration setWebsiteDataStore:websiteDataStore];
</ins><span class="cx">     }
</span><span class="cx"> 
</span><span class="cx">     [copiedConfiguration _setAllowTopNavigationToDataURLs:options.allowTopNavigationToDataURLs];
</span></span></pre>
</div>
</div>

</body>
</html>