Title: [256820] trunk
Revision
256820
Author
[email protected]
Date
2020-02-17 22:32:42 -0800 (Mon, 17 Feb 2020)

Log Message

[WK2][Cocoa] Implement in-WebProcess cookie cache to avoid sync IPC for document.cookie in most cases
https://bugs.webkit.org/show_bug.cgi?id=207593
<rdar://problem/56027027>

Reviewed by Antti Koivisto.

Source/WebCore:

Implement in-WebProcess DOM cookie cache for serving `document.cookie` requests from _javascript_.

The first time document.cookie is called for a given host, the WebProcess will pull in all the
non-HTTPOnly cookies for that host from the NetworkProcess (still via sync IPC) and store them
in an in-memory cookie store. Later document.cookie calls for this host from this WebProcess
will then leverage the in-memory cookie store and avoid doing a sync IPC to the NetworkProcess
entirely.

To maintain the in-process cookie store up-to-date, the WebProcess subscribe for cookie-change
notifications from the NetworkProcess, only for the hosts it is interested in.

If the page's _javascript_ sets a cookie by setting document.cookie, we will not invalidate the
cache for performance reasons. Instead, we set the cookie in our in-memory cookie before
sending the new cookie to the NetworkProcess.

For compatibility reasons, any sync IPC to a given host will currently invalidate the cookie
cache for this host. This is because this synchronous load may cause cookies to get set
synchronously and the page could access document.cookie right after the sync XHR. This behavior
is covered by the following existing test:
- http/tests/cookies/sync-xhr-set-cookie-invalidates-cache.html

Another limitation of the current implementation of the cookie cache is that it is currently
only leveraged for first party content. This is suboptimal and could be improved in a later
iteration. However, the default behavior in Safari is that third-party iframes do not have
cookie access unless they request it using the storage access API. We also currently have
a limit of 5 hosts with cached cookies per WebProcess.

Tests: http/tests/cookies/document-cookie-after-showModalDialog.html
       http/tests/cookies/document-cookie-during-iframe-parsing.html

* dom/Document.cpp:
(WebCore::Document::didLoadResourceSynchronously):
* dom/Document.h:
* dom/ScriptExecutionContext.cpp:
(WebCore::ScriptExecutionContext::didLoadResourceSynchronously):
* dom/ScriptExecutionContext.h:
* loader/CookieJar.h:
* loader/ThreadableLoader.cpp:
(WebCore::ThreadableLoader::loadResourceSynchronously):
* page/MemoryRelease.cpp:
(WebCore::releaseCriticalMemory):
* page/Settings.yaml:
* platform/network/NetworkStorageSession.h:
(WebCore::CookieChangeObserver::~CookieChangeObserver):
* platform/network/cf/NetworkStorageSessionCFNet.cpp:
(WebCore::NetworkStorageSession::NetworkStorageSession):
(WebCore::NetworkStorageSession::cookieStorage const):
* platform/network/cocoa/NetworkStorageSessionCocoa.mm:
(WebCore::NetworkStorageSession::~NetworkStorageSession):
(WebCore::NetworkStorageSession::setCookie):
(WebCore::NetworkStorageSession::setCookies):
(WebCore::NetworkStorageSession::deleteCookie):
(WebCore::nsCookiesToCookieVector):
(WebCore::NetworkStorageSession::nsCookieStorage const):
(WebCore::createPrivateStorageSession):
(WebCore::NetworkStorageSession::httpCookies const):
(WebCore::NetworkStorageSession::deleteHTTPCookie const):
(WebCore::NetworkStorageSession::setHTTPCookiesForURL const):
(WebCore::NetworkStorageSession::httpCookiesForURL const):
(WebCore::filterCookies):
(WebCore::NetworkStorageSession::cookiesForURL const):
(WebCore::NetworkStorageSession::cookiesForSession const):
(WebCore::NetworkStorageSession::cookiesForDOM const):
(WebCore::NetworkStorageSession::cookieRequestHeaderFieldValue const):
(WebCore::NetworkStorageSession::setCookiesFromDOM const):
(WebCore::NetworkStorageSession::getRawCookies const):
(WebCore::NetworkStorageSession::deleteCookiesForHostnames):
(WebCore::NetworkStorageSession::registerCookieChangeListenersIfNecessary):
(WebCore::NetworkStorageSession::unregisterCookieChangeListenersIfNecessary):
(WebCore::NetworkStorageSession::startListeningForCookieChangeNotifications):
(WebCore::NetworkStorageSession::stopListeningForCookieChangeNotifications):
(WebCore::NetworkStorageSession::domCookiesForHost):
(WebCore::NetworkStorageSession::supportsCookieChangeListenerAPI const):

Source/WebCore/PAL:

Add new CFNetwork SPI to CFNetworkSPI.h for open source builds and for using respondsToSelector.

* pal/spi/cf/CFNetworkSPI.h:

Source/WebKit:

See WebCore ChangeLog.

* NetworkProcess/NetworkConnectionToWebProcess.cpp:
(WebKit::NetworkConnectionToWebProcess::~NetworkConnectionToWebProcess):
(WebKit::NetworkConnectionToWebProcess::domCookiesForHost):
(WebKit::NetworkConnectionToWebProcess::unsubscribeFromCookieChangeNotifications):
(WebKit::NetworkConnectionToWebProcess::cookiesAdded):
(WebKit::NetworkConnectionToWebProcess::cookiesDeleted):
* NetworkProcess/NetworkConnectionToWebProcess.h:
* NetworkProcess/NetworkConnectionToWebProcess.messages.in:
* Scripts/webkit/messages.py:
* Shared/WebPreferences.yaml:
* Sources.txt:
* SourcesCocoa.txt:
* WebKit.xcodeproj/project.pbxproj:
* WebProcess/Network/NetworkProcessConnection.cpp:
(WebKit::NetworkProcessConnection::cookiesAdded):
(WebKit::NetworkProcessConnection::cookiesDeleted):
* WebProcess/Network/NetworkProcessConnection.h:
* WebProcess/Network/NetworkProcessConnection.messages.in:
* WebProcess/WebPage/Cocoa/WebCookieCacheCocoa.mm: Copied from Source/WebKit/WebProcess/WebPage/WebCookieJar.h.
(WebKit::WebCookieCache::inMemoryStorageSession):
* WebProcess/WebPage/WebCookieCache.cpp: Added.
(WebKit::WebCookieCache::isFunctional):
(WebKit::WebCookieCache::cookiesForDOM):
(WebKit::WebCookieCache::setCookiesFromDOM):
(WebKit::WebCookieCache::cookiesAdded):
(WebKit::WebCookieCache::cookiesDeleted):
(WebKit::WebCookieCache::clear):
(WebKit::WebCookieCache::clearForHost):
(WebKit::WebCookieCache::pruneCacheIfNecessary):
* WebProcess/WebPage/WebCookieCache.h: Copied from Source/WebKit/WebProcess/WebPage/WebCookieJar.h.
* WebProcess/WebPage/WebCookieJar.cpp:
(WebKit::WebCookieJar::isEligibleForCache const):
(WebKit::WebCookieJar::cookies const):
(WebKit::WebCookieJar::setCookies):
(WebKit::WebCookieJar::cookiesAdded):
(WebKit::WebCookieJar::cookiesDeleted):
(WebKit::WebCookieJar::clearCache):
(WebKit::WebCookieJar::clearCacheForHost):
* WebProcess/WebPage/WebCookieJar.h:
* WebProcess/WebPage/WebPage.cpp:
(WebKit::m_overriddenMediaType):
* WebProcess/WebProcess.cpp:
(WebKit::WebProcess::WebProcess):
* WebProcess/WebProcess.h:
(WebKit::WebProcess::cookieJar):

Source/WTF:

Add build time flags for new feature.

* wtf/PlatformEnable.h:
* wtf/PlatformHave.h:

LayoutTests:

Add layout test coverage.

* http/tests/cookies/document-cookie-after-showModalDialog-expected.txt: Added.
* http/tests/cookies/document-cookie-after-showModalDialog.html: Added.
* http/tests/cookies/document-cookie-during-iframe-parsing-expected.txt: Added.
* http/tests/cookies/document-cookie-during-iframe-parsing.html: Added.
* http/tests/cookies/resources/close-modal-dialog.html: Added.
* http/tests/cookies/resources/document-cookie-during-iframe-parsing-iframe.html: Added.
* http/tests/cookies/resources/set-cookie-and-serve.php: Added.

Modified Paths

Added Paths

Diff

Modified: trunk/LayoutTests/ChangeLog (256819 => 256820)


--- trunk/LayoutTests/ChangeLog	2020-02-18 06:10:16 UTC (rev 256819)
+++ trunk/LayoutTests/ChangeLog	2020-02-18 06:32:42 UTC (rev 256820)
@@ -1,3 +1,21 @@
+2020-02-17  Chris Dumez  <[email protected]>
+
+        [WK2][Cocoa] Implement in-WebProcess cookie cache to avoid sync IPC for document.cookie in most cases
+        https://bugs.webkit.org/show_bug.cgi?id=207593
+        <rdar://problem/56027027>
+
+        Reviewed by Antti Koivisto.
+
+        Add layout test coverage.
+
+        * http/tests/cookies/document-cookie-after-showModalDialog-expected.txt: Added.
+        * http/tests/cookies/document-cookie-after-showModalDialog.html: Added.
+        * http/tests/cookies/document-cookie-during-iframe-parsing-expected.txt: Added.
+        * http/tests/cookies/document-cookie-during-iframe-parsing.html: Added.
+        * http/tests/cookies/resources/close-modal-dialog.html: Added.
+        * http/tests/cookies/resources/document-cookie-during-iframe-parsing-iframe.html: Added.
+        * http/tests/cookies/resources/set-cookie-and-serve.php: Added.
+
 2020-02-17  Ryan Haddad  <[email protected]>
 
         Changed results due to ANGLE use

Added: trunk/LayoutTests/http/tests/cookies/document-cookie-after-showModalDialog-expected.txt (0 => 256820)


--- trunk/LayoutTests/http/tests/cookies/document-cookie-after-showModalDialog-expected.txt	                        (rev 0)
+++ trunk/LayoutTests/http/tests/cookies/document-cookie-after-showModalDialog-expected.txt	2020-02-18 06:32:42 UTC (rev 256820)
@@ -0,0 +1,11 @@
+Tests that document.cookie returns the right value after a showModalDialog() call
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+PASS normalizeCookie(document.cookie) is "testKey=testValue"
+PASS normalizeCookie(document.cookie) is "foo=bar; testKey=testValue"
+PASS successfullyParsed is true
+
+TEST COMPLETE
+

Added: trunk/LayoutTests/http/tests/cookies/document-cookie-after-showModalDialog.html (0 => 256820)


--- trunk/LayoutTests/http/tests/cookies/document-cookie-after-showModalDialog.html	                        (rev 0)
+++ trunk/LayoutTests/http/tests/cookies/document-cookie-after-showModalDialog.html	2020-02-18 06:32:42 UTC (rev 256820)
@@ -0,0 +1,37 @@
+<!DOCTYPE html>
+<html>
+<head>
+<script src=''></script>
+<script src=""
+</head>
+<body>
+<input id="testButton" type="button" value="Click me"></input>
+<script>
+description('Tests that document.cookie returns the right value after a showModalDialog() call');
+jsTestIsAsync = true;
+
+if (window.testRunner)
+    testRunner.setCanOpenWindows();
+
+testButton._onclick_ = () => {
+    document.cookie = "testKey=testValue";
+    shouldBeEqualToString('normalizeCookie(document.cookie)', 'testKey=testValue');
+
+    showModalDialog("resources/set-cookie-and-serve.php?cookie-name=foo&cookie-value=bar&destination=close-modal-dialog.html");
+
+    // This is so the cookie gets removed at the end of the test.
+    registerCookieForCleanup('foo=bar; path=/');
+    shouldBeEqualToString('normalizeCookie(document.cookie)', 'foo=bar; testKey=testValue');
+
+    finishJSTest();
+}
+
+_onload_ = () => {
+    internals.withUserGesture(() => {
+        testButton.click();
+    });
+};
+</script>
+<script src=''></script>
+</body>
+</html>

Added: trunk/LayoutTests/http/tests/cookies/document-cookie-during-iframe-parsing-expected.txt (0 => 256820)


--- trunk/LayoutTests/http/tests/cookies/document-cookie-during-iframe-parsing-expected.txt	                        (rev 0)
+++ trunk/LayoutTests/http/tests/cookies/document-cookie-during-iframe-parsing-expected.txt	2020-02-18 06:32:42 UTC (rev 256820)
@@ -0,0 +1,12 @@
+Tests that document.cookie returns the right value after a showModalDialog() call
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+PASS normalizeCookie(document.cookie) is "testKey=testValue"
+PASS normalizeCookie(cookiesInFrame) is "foo=bar; testKey=testValue"
+PASS normalizeCookie(document.cookie) is "foo=bar; testKey=testValue"
+PASS successfullyParsed is true
+
+TEST COMPLETE
+

Added: trunk/LayoutTests/http/tests/cookies/document-cookie-during-iframe-parsing.html (0 => 256820)


--- trunk/LayoutTests/http/tests/cookies/document-cookie-during-iframe-parsing.html	                        (rev 0)
+++ trunk/LayoutTests/http/tests/cookies/document-cookie-during-iframe-parsing.html	2020-02-18 06:32:42 UTC (rev 256820)
@@ -0,0 +1,34 @@
+<!DOCTYPE html>
+<html>
+<head>
+<script src=''></script>
+<script src=""
+</head>
+<body>
+<script>
+description('Tests that document.cookie returns the right value after a showModalDialog() call');
+jsTestIsAsync = true;
+
+function validateCookiesDuringChildFrameParsing(_cookiesInFrame)
+{
+    cookiesInFrame = _cookiesInFrame;
+    shouldBeEqualToString('normalizeCookie(cookiesInFrame)', 'foo=bar; testKey=testValue');
+    shouldBeEqualToString('normalizeCookie(document.cookie)', 'foo=bar; testKey=testValue');
+    finishJSTest();    
+}
+
+_onload_ = () => {
+    document.cookie = "testKey=testValue";
+    shouldBeEqualToString('normalizeCookie(document.cookie)', 'testKey=testValue');
+
+    // This is so the cookie gets removed at the end of the test.
+    registerCookieForCleanup('foo=bar; path=/');
+
+    let iframe = document.createElement("iframe");
+    iframe.src = ""
+    document.body.appendChild(iframe);
+};
+</script>
+<script src=''></script>
+</body>
+</html>

Added: trunk/LayoutTests/http/tests/cookies/resources/close-modal-dialog.html (0 => 256820)


--- trunk/LayoutTests/http/tests/cookies/resources/close-modal-dialog.html	                        (rev 0)
+++ trunk/LayoutTests/http/tests/cookies/resources/close-modal-dialog.html	2020-02-18 06:32:42 UTC (rev 256820)
@@ -0,0 +1,7 @@
+<script>
+_onload_ = () => {
+    close();
+    if (window.testRunner)
+       testRunner.abortModal();
+};
+</script>

Added: trunk/LayoutTests/http/tests/cookies/resources/document-cookie-during-iframe-parsing-iframe.html (0 => 256820)


--- trunk/LayoutTests/http/tests/cookies/resources/document-cookie-during-iframe-parsing-iframe.html	                        (rev 0)
+++ trunk/LayoutTests/http/tests/cookies/resources/document-cookie-during-iframe-parsing-iframe.html	2020-02-18 06:32:42 UTC (rev 256820)
@@ -0,0 +1,3 @@
+<script>
+top.validateCookiesDuringChildFrameParsing(document.cookie);
+</script>

Added: trunk/LayoutTests/http/tests/cookies/resources/set-cookie-and-serve.php (0 => 256820)


--- trunk/LayoutTests/http/tests/cookies/resources/set-cookie-and-serve.php	                        (rev 0)
+++ trunk/LayoutTests/http/tests/cookies/resources/set-cookie-and-serve.php	2020-02-18 06:32:42 UTC (rev 256820)
@@ -0,0 +1,13 @@
+<?php
+$cookieName = $_GET['cookie-name'];
+$cookieValue = $_GET['cookie-value'];
+$destination = $_GET['destination'];
+header("Content-Type: text/html");
+header("Cache-Control: no-store");
+header("Set-Cookie: ${cookieName}=${cookieValue}; path=/");
+$fp = fopen($destination, 'rb');
+header("Content-Length: " . filesize($destination));
+
+fpassthru($fp);
+exit;
+?>
Property changes on: trunk/LayoutTests/http/tests/cookies/resources/set-cookie-and-serve.php
___________________________________________________________________

Added: svn:executable

+* \ No newline at end of property

Modified: trunk/Source/WTF/ChangeLog (256819 => 256820)


--- trunk/Source/WTF/ChangeLog	2020-02-18 06:10:16 UTC (rev 256819)
+++ trunk/Source/WTF/ChangeLog	2020-02-18 06:32:42 UTC (rev 256820)
@@ -1,3 +1,16 @@
+2020-02-17  Chris Dumez  <[email protected]>
+
+        [WK2][Cocoa] Implement in-WebProcess cookie cache to avoid sync IPC for document.cookie in most cases
+        https://bugs.webkit.org/show_bug.cgi?id=207593
+        <rdar://problem/56027027>
+
+        Reviewed by Antti Koivisto.
+
+        Add build time flags for new feature.
+
+        * wtf/PlatformEnable.h:
+        * wtf/PlatformHave.h:
+
 2020-02-17  Tim Horton  <[email protected]>
 
         Add and adopt HAVE(LOOKUP_GESTURE_RECOGNIZER)

Modified: trunk/Source/WTF/wtf/HashSet.h (256819 => 256820)


--- trunk/Source/WTF/wtf/HashSet.h	2020-02-18 06:10:16 UTC (rev 256819)
+++ trunk/Source/WTF/wtf/HashSet.h	2020-02-18 06:32:42 UTC (rev 256820)
@@ -112,6 +112,8 @@
     // them are new to the set. Returns false if the set is unchanged.
     template<typename IteratorType>
     bool add(IteratorType begin, IteratorType end);
+    template<typename IteratorType>
+    bool remove(IteratorType begin, IteratorType end);
 
     bool remove(const ValueType&);
     bool remove(iterator);
@@ -271,6 +273,16 @@
 }
 
 template<typename T, typename U, typename V>
+template<typename IteratorType>
+inline bool HashSet<T, U, V>::remove(IteratorType begin, IteratorType end)
+{
+    bool changed = false;
+    for (IteratorType iter = begin; iter != end; ++iter)
+        changed |= remove(*iter);
+    return changed;
+}
+
+template<typename T, typename U, typename V>
 inline bool HashSet<T, U, V>::remove(iterator it)
 {
     if (it.m_impl == m_impl.end())

Modified: trunk/Source/WTF/wtf/PlatformHave.h (256819 => 256820)


--- trunk/Source/WTF/wtf/PlatformHave.h	2020-02-18 06:10:16 UTC (rev 256819)
+++ trunk/Source/WTF/wtf/PlatformHave.h	2020-02-18 06:32:42 UTC (rev 256820)
@@ -561,6 +561,10 @@
 #define HAVE_DEVICE_IDENTITY 1
 #endif
 
+#if (PLATFORM(MAC) && __MAC_OS_X_VERSION_MIN_REQUIRED >= 101600) || (PLATFORM(IOS_FAMILY) && __IPHONE_OS_VERSION_MIN_REQUIRED >= 140000)
+#define HAVE_COOKIE_CHANGE_LISTENER_API 1
+#endif
+
 #if (PLATFORM(MAC) && __MAC_OS_X_VERSION_MIN_REQUIRED >= 101500) || PLATFORM(IOS_FAMILY)
 #define HAVE_DATA_PROTECTION_KEYCHAIN 1
 #endif

Modified: trunk/Source/WebCore/ChangeLog (256819 => 256820)


--- trunk/Source/WebCore/ChangeLog	2020-02-18 06:10:16 UTC (rev 256819)
+++ trunk/Source/WebCore/ChangeLog	2020-02-18 06:32:42 UTC (rev 256820)
@@ -1,5 +1,87 @@
 2020-02-17  Chris Dumez  <[email protected]>
 
+        [WK2][Cocoa] Implement in-WebProcess cookie cache to avoid sync IPC for document.cookie in most cases
+        https://bugs.webkit.org/show_bug.cgi?id=207593
+        <rdar://problem/56027027>
+
+        Reviewed by Antti Koivisto.
+
+        Implement in-WebProcess DOM cookie cache for serving `document.cookie` requests from _javascript_.
+
+        The first time document.cookie is called for a given host, the WebProcess will pull in all the
+        non-HTTPOnly cookies for that host from the NetworkProcess (still via sync IPC) and store them
+        in an in-memory cookie store. Later document.cookie calls for this host from this WebProcess
+        will then leverage the in-memory cookie store and avoid doing a sync IPC to the NetworkProcess
+        entirely.
+
+        To maintain the in-process cookie store up-to-date, the WebProcess subscribe for cookie-change
+        notifications from the NetworkProcess, only for the hosts it is interested in.
+
+        If the page's _javascript_ sets a cookie by setting document.cookie, we will not invalidate the
+        cache for performance reasons. Instead, we set the cookie in our in-memory cookie before
+        sending the new cookie to the NetworkProcess.
+
+        For compatibility reasons, any sync IPC to a given host will currently invalidate the cookie
+        cache for this host. This is because this synchronous load may cause cookies to get set
+        synchronously and the page could access document.cookie right after the sync XHR. This behavior
+        is covered by the following existing test:
+        - http/tests/cookies/sync-xhr-set-cookie-invalidates-cache.html
+
+        Another limitation of the current implementation of the cookie cache is that it is currently
+        only leveraged for first party content. This is suboptimal and could be improved in a later
+        iteration. However, the default behavior in Safari is that third-party iframes do not have
+        cookie access unless they request it using the storage access API. We also currently have
+        a limit of 5 hosts with cached cookies per WebProcess.
+
+        Tests: http/tests/cookies/document-cookie-after-showModalDialog.html
+               http/tests/cookies/document-cookie-during-iframe-parsing.html
+
+        * dom/Document.cpp:
+        (WebCore::Document::didLoadResourceSynchronously):
+        * dom/Document.h:
+        * dom/ScriptExecutionContext.cpp:
+        (WebCore::ScriptExecutionContext::didLoadResourceSynchronously):
+        * dom/ScriptExecutionContext.h:
+        * loader/CookieJar.h:
+        * loader/ThreadableLoader.cpp:
+        (WebCore::ThreadableLoader::loadResourceSynchronously):
+        * page/MemoryRelease.cpp:
+        (WebCore::releaseCriticalMemory):
+        * page/Settings.yaml:
+        * platform/network/NetworkStorageSession.h:
+        (WebCore::CookieChangeObserver::~CookieChangeObserver):
+        * platform/network/cf/NetworkStorageSessionCFNet.cpp:
+        (WebCore::NetworkStorageSession::NetworkStorageSession):
+        (WebCore::NetworkStorageSession::cookieStorage const):
+        * platform/network/cocoa/NetworkStorageSessionCocoa.mm:
+        (WebCore::NetworkStorageSession::~NetworkStorageSession):
+        (WebCore::NetworkStorageSession::setCookie):
+        (WebCore::NetworkStorageSession::setCookies):
+        (WebCore::NetworkStorageSession::deleteCookie):
+        (WebCore::nsCookiesToCookieVector):
+        (WebCore::NetworkStorageSession::nsCookieStorage const):
+        (WebCore::createPrivateStorageSession):
+        (WebCore::NetworkStorageSession::httpCookies const):
+        (WebCore::NetworkStorageSession::deleteHTTPCookie const):
+        (WebCore::NetworkStorageSession::setHTTPCookiesForURL const):
+        (WebCore::NetworkStorageSession::httpCookiesForURL const):
+        (WebCore::filterCookies):
+        (WebCore::NetworkStorageSession::cookiesForURL const):
+        (WebCore::NetworkStorageSession::cookiesForSession const):
+        (WebCore::NetworkStorageSession::cookiesForDOM const):
+        (WebCore::NetworkStorageSession::cookieRequestHeaderFieldValue const):
+        (WebCore::NetworkStorageSession::setCookiesFromDOM const):
+        (WebCore::NetworkStorageSession::getRawCookies const):
+        (WebCore::NetworkStorageSession::deleteCookiesForHostnames):
+        (WebCore::NetworkStorageSession::registerCookieChangeListenersIfNecessary):
+        (WebCore::NetworkStorageSession::unregisterCookieChangeListenersIfNecessary):
+        (WebCore::NetworkStorageSession::startListeningForCookieChangeNotifications):
+        (WebCore::NetworkStorageSession::stopListeningForCookieChangeNotifications):
+        (WebCore::NetworkStorageSession::domCookiesForHost):
+        (WebCore::NetworkStorageSession::supportsCookieChangeListenerAPI const):
+
+2020-02-17  Chris Dumez  <[email protected]>
+
         Defer execution of async scripts until until the document is loaded
         https://bugs.webkit.org/show_bug.cgi?id=207698
         <rdar://problem/57625747>

Modified: trunk/Source/WebCore/PAL/ChangeLog (256819 => 256820)


--- trunk/Source/WebCore/PAL/ChangeLog	2020-02-18 06:10:16 UTC (rev 256819)
+++ trunk/Source/WebCore/PAL/ChangeLog	2020-02-18 06:32:42 UTC (rev 256820)
@@ -1,3 +1,15 @@
+2020-02-17  Chris Dumez  <[email protected]>
+
+        [WK2][Cocoa] Implement in-WebProcess cookie cache to avoid sync IPC for document.cookie in most cases
+        https://bugs.webkit.org/show_bug.cgi?id=207593
+        <rdar://problem/56027027>
+
+        Reviewed by Antti Koivisto.
+
+        Add new CFNetwork SPI to CFNetworkSPI.h for open source builds and for using respondsToSelector.
+
+        * pal/spi/cf/CFNetworkSPI.h:
+
 2020-02-17  Peng Liu  <[email protected]>
 
         Fix check-webkit-style errors related to AVFoundationSPI.h

Modified: trunk/Source/WebCore/PAL/pal/spi/cf/CFNetworkSPI.h (256819 => 256820)


--- trunk/Source/WebCore/PAL/pal/spi/cf/CFNetworkSPI.h	2020-02-18 06:10:16 UTC (rev 256819)
+++ trunk/Source/WebCore/PAL/pal/spi/cf/CFNetworkSPI.h	2020-02-18 06:32:42 UTC (rev 256820)
@@ -391,6 +391,10 @@
 
 @interface NSHTTPCookieStorage ()
 + (void)_setSharedHTTPCookieStorage:(NSHTTPCookieStorage *)storage;
+- (void)_setSubscribedDomainsForCookieChanges:(NSSet<NSString*>* __nullable)domainList;
+- (void)_setCookiesAddedHandler:(void(^__nullable)(NSArray<NSHTTPCookie*>* addedCookies, NSURL* __nullable urlForAddedCookies))cookiesAddedHandler onQueue:(dispatch_queue_t __nullable)queue;
+- (void)_setCookiesDeletedHandler:(void(^__nullable)(NSArray<NSHTTPCookie*>* __nullable deletedCookies, bool deletedAllCookies))cookiesDeletedHandler onQueue:(dispatch_queue_t __nullable)queue;
+- (NSArray* __nullable)_getCookiesForDomain:(NSString*)domain;
 @end
 
 @interface NSURLResponse ()

Modified: trunk/Source/WebCore/dom/Document.cpp (256819 => 256820)


--- trunk/Source/WebCore/dom/Document.cpp	2020-02-18 06:10:16 UTC (rev 256819)
+++ trunk/Source/WebCore/dom/Document.cpp	2020-02-18 06:32:42 UTC (rev 256820)
@@ -7203,11 +7203,14 @@
     m_cachedDOMCookies = String();
 }
 
-void Document::didLoadResourceSynchronously()
+void Document::didLoadResourceSynchronously(const URL& url)
 {
     // Synchronous resources loading can set cookies so we invalidate the cookies cache
     // in this case, to be safe.
     invalidateDOMCookieCache();
+
+    if (auto* page = this->page())
+        page->cookieJar().clearCacheForHost(url.host().toString());
 }
 
 void Document::ensurePlugInsInjectedScript(DOMWrapperWorld& world)

Modified: trunk/Source/WebCore/dom/Document.h (256819 => 256820)


--- trunk/Source/WebCore/dom/Document.h	2020-02-18 06:10:16 UTC (rev 256819)
+++ trunk/Source/WebCore/dom/Document.h	2020-02-18 06:32:42 UTC (rev 256820)
@@ -1641,7 +1641,7 @@
     void setCachedDOMCookies(const String&);
     bool isDOMCookieCacheValid() const { return m_cookieCacheExpiryTimer.isActive(); }
     void invalidateDOMCookieCache();
-    void didLoadResourceSynchronously() final;
+    void didLoadResourceSynchronously(const URL&) final;
 
     bool canNavigateInternal(Frame& targetFrame);
     bool isNavigationBlockedByThirdPartyIFrameRedirectBlocking(Frame& targetFrame, const URL& destinationURL);

Modified: trunk/Source/WebCore/dom/ScriptExecutionContext.cpp (256819 => 256820)


--- trunk/Source/WebCore/dom/ScriptExecutionContext.cpp	2020-02-18 06:10:16 UTC (rev 256819)
+++ trunk/Source/WebCore/dom/ScriptExecutionContext.cpp	2020-02-18 06:32:42 UTC (rev 256820)
@@ -218,7 +218,7 @@
     m_messagePorts.remove(&messagePort);
 }
 
-void ScriptExecutionContext::didLoadResourceSynchronously()
+void ScriptExecutionContext::didLoadResourceSynchronously(const URL&)
 {
 }
 

Modified: trunk/Source/WebCore/dom/ScriptExecutionContext.h (256819 => 256820)


--- trunk/Source/WebCore/dom/ScriptExecutionContext.h	2020-02-18 06:10:16 UTC (rev 256819)
+++ trunk/Source/WebCore/dom/ScriptExecutionContext.h	2020-02-18 06:32:42 UTC (rev 256820)
@@ -152,7 +152,7 @@
     void createdMessagePort(MessagePort&);
     void destroyedMessagePort(MessagePort&);
 
-    virtual void didLoadResourceSynchronously();
+    virtual void didLoadResourceSynchronously(const URL&);
 
     void ref() { refScriptExecutionContext(); }
     void deref() { derefScriptExecutionContext(); }

Modified: trunk/Source/WebCore/loader/CookieJar.h (256819 => 256820)


--- trunk/Source/WebCore/loader/CookieJar.h	2020-02-18 06:10:16 UTC (rev 256819)
+++ trunk/Source/WebCore/loader/CookieJar.h	2020-02-18 06:32:42 UTC (rev 256820)
@@ -60,6 +60,10 @@
     virtual bool getRawCookies(const Document&, const URL&, Vector<Cookie>&) const;
     virtual void deleteCookie(const Document&, const URL&, const String& cookieName);
 
+    // Cookie Cache.
+    virtual void clearCache() { }
+    virtual void clearCacheForHost(const String&) { }
+
     virtual ~CookieJar();
 protected:
     static SameSiteInfo sameSiteInfo(const Document&);

Modified: trunk/Source/WebCore/loader/ThreadableLoader.cpp (256819 => 256820)


--- trunk/Source/WebCore/loader/ThreadableLoader.cpp	2020-02-18 06:10:16 UTC (rev 256819)
+++ trunk/Source/WebCore/loader/ThreadableLoader.cpp	2020-02-18 06:32:42 UTC (rev 256820)
@@ -110,11 +110,12 @@
 
 void ThreadableLoader::loadResourceSynchronously(ScriptExecutionContext& context, ResourceRequest&& request, ThreadableLoaderClient& client, const ThreadableLoaderOptions& options)
 {
+    auto resourceURL = request.url();
     if (is<WorkerGlobalScope>(context))
         WorkerThreadableLoader::loadResourceSynchronously(downcast<WorkerGlobalScope>(context), WTFMove(request), client, options);
     else
         DocumentThreadableLoader::loadResourceSynchronously(downcast<Document>(context), WTFMove(request), client, options);
-    context.didLoadResourceSynchronously();
+    context.didLoadResourceSynchronously(resourceURL);
 }
 
 void ThreadableLoader::logError(ScriptExecutionContext& context, const ResourceError& error, const String& initiator)

Modified: trunk/Source/WebCore/page/MemoryRelease.cpp (256819 => 256820)


--- trunk/Source/WebCore/page/MemoryRelease.cpp	2020-02-18 06:10:16 UTC (rev 256819)
+++ trunk/Source/WebCore/page/MemoryRelease.cpp	2020-02-18 06:32:42 UTC (rev 256820)
@@ -33,6 +33,7 @@
 #include "Chrome.h"
 #include "ChromeClient.h"
 #include "CommonVM.h"
+#include "CookieJar.h"
 #include "Document.h"
 #include "FontCache.h"
 #include "Frame.h"
@@ -99,6 +100,10 @@
 
     CSSValuePool::singleton().drain();
 
+    Page::forEachPage([](auto& page) {
+        page.cookieJar().clearCache();
+    });
+
     for (auto& document : copyToVectorOf<RefPtr<Document>>(Document::allDocuments())) {
         document->styleScope().releaseMemory();
         document->fontSelector().emptyCaches();

Modified: trunk/Source/WebCore/page/Settings.yaml (256819 => 256820)


--- trunk/Source/WebCore/page/Settings.yaml	2020-02-18 06:10:16 UTC (rev 256819)
+++ trunk/Source/WebCore/page/Settings.yaml	2020-02-18 06:32:42 UTC (rev 256820)
@@ -361,6 +361,9 @@
 HTTPSUpgradeEnabled:
   initial: false
 
+inProcessCookieCacheEnabled:
+  initial: false
+
 thirdPartyIframeRedirectBlockingEnabled:
   initial: true
 

Modified: trunk/Source/WebCore/platform/network/NetworkStorageSession.cpp (256819 => 256820)


--- trunk/Source/WebCore/platform/network/NetworkStorageSession.cpp	2020-02-18 06:10:16 UTC (rev 256819)
+++ trunk/Source/WebCore/platform/network/NetworkStorageSession.cpp	2020-02-18 06:32:42 UTC (rev 256820)
@@ -26,6 +26,7 @@
 #include "config.h"
 #include "NetworkStorageSession.h"
 
+#include "Cookie.h"
 #include "HTTPCookieAcceptPolicy.h"
 #include "RuntimeApplicationChecks.h"
 #include <wtf/NeverDestroyed.h>
@@ -56,6 +57,14 @@
         removeProcessPrivilege(ProcessPrivilege::CanAccessRawCookies);
 }
 
+#if !PLATFORM(COCOA)
+Vector<Cookie> NetworkStorageSession::domCookiesForHost(const String&)
+{
+    ASSERT_NOT_IMPLEMENTED_YET();
+    return { };
+}
+#endif // !PLATFORM(COCOA)
+
 #if ENABLE(RESOURCE_LOAD_STATISTICS)
 
 bool NetworkStorageSession::shouldBlockThirdPartyCookies(const RegistrableDomain& registrableDomain) const

Modified: trunk/Source/WebCore/platform/network/NetworkStorageSession.h (256819 => 256820)


--- trunk/Source/WebCore/platform/network/NetworkStorageSession.h	2020-02-18 06:10:16 UTC (rev 256819)
+++ trunk/Source/WebCore/platform/network/NetworkStorageSession.h	2020-02-18 06:32:42 UTC (rev 256820)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2012-2018 Apple Inc. All rights reserved.
+ * Copyright (C) 2012-2020 Apple Inc. All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
@@ -60,6 +60,9 @@
 
 #if PLATFORM(COCOA)
 #include "CookieStorageObserver.h"
+OBJC_CLASS NSArray;
+OBJC_CLASS NSHTTPCookie;
+OBJC_CLASS NSMutableSet;
 #endif
 
 namespace WebCore {
@@ -79,6 +82,15 @@
 enum class FirstPartyWebsiteDataRemovalMode : uint8_t { AllButCookies, None, AllButCookiesLiveOnTestingTimeout, AllButCookiesReproTestingTimeout };
 enum class ShouldAskITP : bool { No, Yes };
 
+#if HAVE(COOKIE_CHANGE_LISTENER_API)
+class CookieChangeObserver {
+public:
+    virtual ~CookieChangeObserver() { }
+    virtual void cookiesAdded(const String& host, const Vector<WebCore::Cookie>&) = 0;
+    virtual void cookiesDeleted() = 0;
+};
+#endif
+
 class NetworkStorageSession {
     WTF_MAKE_NONCOPYABLE(NetworkStorageSession); WTF_MAKE_FAST_ALLOCATED;
 public:
@@ -92,9 +104,14 @@
     WEBCORE_EXPORT NSHTTPCookieStorage *nsCookieStorage() const;
 #endif
 
+#if PLATFORM(COCOA)
+    WEBCORE_EXPORT ~NetworkStorageSession();
+#endif
+
 #if PLATFORM(COCOA) || USE(CFURLCONNECTION)
     WEBCORE_EXPORT static RetainPtr<CFURLStorageSessionRef> createCFStorageSessionForIdentifier(CFStringRef identifier);
-    WEBCORE_EXPORT NetworkStorageSession(PAL::SessionID, RetainPtr<CFURLStorageSessionRef>&&, RetainPtr<CFHTTPCookieStorageRef>&&);
+    enum class IsInMemoryCookieStore : bool { No, Yes };
+    WEBCORE_EXPORT NetworkStorageSession(PAL::SessionID, RetainPtr<CFURLStorageSessionRef>&&, RetainPtr<CFHTTPCookieStorageRef>&&, IsInMemoryCookieStore = IsInMemoryCookieStore::No);
     WEBCORE_EXPORT explicit NetworkStorageSession(PAL::SessionID);
 
     // May be null, in which case a Foundation default should be used.
@@ -146,6 +163,14 @@
     WEBCORE_EXPORT std::pair<String, bool> cookieRequestHeaderFieldValue(const URL& firstParty, const SameSiteInfo&, const URL&, Optional<FrameIdentifier>, Optional<PageIdentifier>, IncludeSecureCookies, ShouldAskITP) const;
     WEBCORE_EXPORT std::pair<String, bool> cookieRequestHeaderFieldValue(const CookieRequestHeaderFieldProxy&) const;
 
+    WEBCORE_EXPORT Vector<Cookie> domCookiesForHost(const String& host);
+
+#if HAVE(COOKIE_CHANGE_LISTENER_API)
+    WEBCORE_EXPORT void startListeningForCookieChangeNotifications(CookieChangeObserver&, const String& host);
+    WEBCORE_EXPORT void stopListeningForCookieChangeNotifications(CookieChangeObserver&, const HashSet<String>& hosts);
+    WEBCORE_EXPORT bool supportsCookieChangeListenerAPI() const;
+#endif
+
 #if ENABLE(RESOURCE_LOAD_STATISTICS)
     void setResourceLoadStatisticsEnabled(bool enabled) { m_isResourceLoadStatisticsEnabled = enabled; }
     WEBCORE_EXPORT bool shouldBlockCookies(const ResourceRequest&, Optional<FrameIdentifier>, Optional<PageIdentifier>) const;
@@ -173,11 +198,27 @@
 #endif
 
 private:
+#if PLATFORM(COCOA)
+    enum IncludeHTTPOnlyOrNot { DoNotIncludeHTTPOnly, IncludeHTTPOnly };
+    std::pair<String, bool> cookiesForSession(const URL& firstParty, const SameSiteInfo&, const URL&, Optional<FrameIdentifier>, Optional<PageIdentifier>, IncludeHTTPOnlyOrNot, IncludeSecureCookies, ShouldAskITP = ShouldAskITP::Yes) const;
+    NSArray *httpCookies(CFHTTPCookieStorageRef) const;
+    NSArray *httpCookiesForURL(CFHTTPCookieStorageRef, NSURL *firstParty, const Optional<SameSiteInfo>&, NSURL *) const;
+    NSArray *cookiesForURL(const URL& firstParty, const SameSiteInfo&, const URL&, Optional<FrameIdentifier>, Optional<PageIdentifier>, ShouldAskITP) const;
+    void setHTTPCookiesForURL(CFHTTPCookieStorageRef, NSArray *cookies, NSURL *, NSURL *mainDocumentURL, const SameSiteInfo&) const;
+    void deleteHTTPCookie(CFHTTPCookieStorageRef, NSHTTPCookie *) const;
+#endif
+
+#if HAVE(COOKIE_CHANGE_LISTENER_API)
+    void registerCookieChangeListenersIfNecessary();
+    void unregisterCookieChangeListenersIfNecessary();
+#endif
+
     PAL::SessionID m_sessionID;
 
 #if PLATFORM(COCOA) || USE(CFURLCONNECTION)
     RetainPtr<CFURLStorageSessionRef> m_platformSession;
     RetainPtr<CFHTTPCookieStorageRef> m_platformCookieStorage;
+    bool m_isInMemoryCookieStore { false };
 #elif USE(SOUP)
     static void cookiesDidChange(NetworkStorageSession*);
 
@@ -190,6 +231,12 @@
     RefPtr<NetworkingContext> m_context;
 #endif
 
+#if HAVE(COOKIE_CHANGE_LISTENER_API)
+    bool m_didRegisterCookieListeners { false };
+    RetainPtr<NSMutableSet> m_subscribedDomainsForCookieChanges;
+    HashMap<String, HashSet<CookieChangeObserver*>> m_cookieChangeObservers;
+#endif
+
     CredentialStorage m_credentialStorage;
 
 #if ENABLE(RESOURCE_LOAD_STATISTICS)

Modified: trunk/Source/WebCore/platform/network/cf/NetworkStorageSessionCFNet.cpp (256819 => 256820)


--- trunk/Source/WebCore/platform/network/cf/NetworkStorageSessionCFNet.cpp	2020-02-18 06:10:16 UTC (rev 256819)
+++ trunk/Source/WebCore/platform/network/cf/NetworkStorageSessionCFNet.cpp	2020-02-18 06:32:42 UTC (rev 256820)
@@ -73,11 +73,12 @@
     return storageSession;
 }
 
-NetworkStorageSession::NetworkStorageSession(PAL::SessionID sessionID, RetainPtr<CFURLStorageSessionRef>&& platformSession, RetainPtr<CFHTTPCookieStorageRef>&& platformCookieStorage)
+NetworkStorageSession::NetworkStorageSession(PAL::SessionID sessionID, RetainPtr<CFURLStorageSessionRef>&& platformSession, RetainPtr<CFHTTPCookieStorageRef>&& platformCookieStorage, IsInMemoryCookieStore isInMemoryCookieStore)
     : m_sessionID(sessionID)
     , m_platformSession(WTFMove(platformSession))
+    , m_isInMemoryCookieStore(isInMemoryCookieStore == IsInMemoryCookieStore::Yes)
 {
-    ASSERT(processMayUseCookieAPI() || !platformCookieStorage);
+    ASSERT(processMayUseCookieAPI() || !platformCookieStorage || m_isInMemoryCookieStore);
     m_platformCookieStorage = platformCookieStorage ? WTFMove(platformCookieStorage) : cookieStorage();
 }
 
@@ -88,10 +89,10 @@
 
 RetainPtr<CFHTTPCookieStorageRef> NetworkStorageSession::cookieStorage() const
 {
-    if (!processMayUseCookieAPI())
+    if (!processMayUseCookieAPI() && !m_isInMemoryCookieStore)
         return nullptr;
 
-    ASSERT(hasProcessPrivilege(ProcessPrivilege::CanAccessRawCookies));
+    ASSERT(hasProcessPrivilege(ProcessPrivilege::CanAccessRawCookies) || m_isInMemoryCookieStore);
 
     if (m_platformCookieStorage)
         return m_platformCookieStorage;

Modified: trunk/Source/WebCore/platform/network/cocoa/NetworkStorageSessionCocoa.mm (256819 => 256820)


--- trunk/Source/WebCore/platform/network/cocoa/NetworkStorageSessionCocoa.mm	2020-02-18 06:10:16 UTC (rev 256819)
+++ trunk/Source/WebCore/platform/network/cocoa/NetworkStorageSessionCocoa.mm	2020-02-18 06:32:42 UTC (rev 256820)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2015-2018 Apple Inc.  All rights reserved.
+ * Copyright (C) 2015-2020 Apple Inc.  All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
@@ -44,9 +44,16 @@
 
 namespace WebCore {
 
+NetworkStorageSession::~NetworkStorageSession()
+{
+#if HAVE(COOKIE_CHANGE_LISTENER_API)
+    unregisterCookieChangeListenersIfNecessary();
+#endif
+}
+
 void NetworkStorageSession::setCookie(const Cookie& cookie)
 {
-    ASSERT(hasProcessPrivilege(ProcessPrivilege::CanAccessRawCookies));
+    ASSERT(hasProcessPrivilege(ProcessPrivilege::CanAccessRawCookies) || m_isInMemoryCookieStore);
 
     BEGIN_BLOCK_OBJC_EXCEPTIONS;
     [nsCookieStorage() setCookie:(NSHTTPCookie *)cookie];
@@ -55,7 +62,7 @@
 
 void NetworkStorageSession::setCookies(const Vector<Cookie>& cookies, const URL& url, const URL& mainDocumentURL)
 {
-    ASSERT(hasProcessPrivilege(ProcessPrivilege::CanAccessRawCookies));
+    ASSERT(hasProcessPrivilege(ProcessPrivilege::CanAccessRawCookies) || m_isInMemoryCookieStore);
 
     RetainPtr<NSMutableArray> nsCookies = adoptNS([[NSMutableArray alloc] initWithCapacity:cookies.size()]);
     for (const auto& cookie : cookies)
@@ -68,19 +75,18 @@
 
 void NetworkStorageSession::deleteCookie(const Cookie& cookie)
 {
-    ASSERT(hasProcessPrivilege(ProcessPrivilege::CanAccessRawCookies));
+    ASSERT(hasProcessPrivilege(ProcessPrivilege::CanAccessRawCookies) || m_isInMemoryCookieStore);
     [nsCookieStorage() deleteCookie:(NSHTTPCookie *)cookie];
 }
 
-static Vector<Cookie> nsCookiesToCookieVector(NSArray<NSHTTPCookie *> *nsCookies)
+static Vector<Cookie> nsCookiesToCookieVector(NSArray<NSHTTPCookie *> *nsCookies, const Function<bool(NSHTTPCookie *)>& filter = { })
 {
-    ASSERT(hasProcessPrivilege(ProcessPrivilege::CanAccessRawCookies));
-
     Vector<Cookie> cookies;
     cookies.reserveInitialCapacity(nsCookies.count);
-    for (NSHTTPCookie *nsCookie in nsCookies)
-        cookies.uncheckedAppend(nsCookie);
-
+    for (NSHTTPCookie *nsCookie in nsCookies) {
+        if (!filter || filter(nsCookie))
+            cookies.uncheckedAppend(nsCookie);
+    }
     return cookies;
 }
 
@@ -118,7 +124,7 @@
 
 NSHTTPCookieStorage *NetworkStorageSession::nsCookieStorage() const
 {
-    ASSERT(hasProcessPrivilege(ProcessPrivilege::CanAccessRawCookies));
+    ASSERT(hasProcessPrivilege(ProcessPrivilege::CanAccessRawCookies) || m_isInMemoryCookieStore);
     auto cfCookieStorage = cookieStorage();
     if (!cfCookieStorage || [NSHTTPCookieStorage sharedHTTPCookieStorage]._cookieStorage == cfCookieStorage)
         return [NSHTTPCookieStorage sharedHTTPCookieStorage];
@@ -158,11 +164,6 @@
     CFURLCacheSetDiskCapacity(cache.get(), 0); // Setting disk cache size should not be necessary once <rdar://problem/12656814> is fixed.
     CFURLCacheSetMemoryCapacity(cache.get(), [[NSURLCache sharedURLCache] memoryCapacity]);
 
-    if (!NetworkStorageSession::processMayUseCookieAPI())
-        return storageSession.leakRef();
-
-    ASSERT(hasProcessPrivilege(ProcessPrivilege::CanAccessRawCookies));
-
     auto cookieStorage = adoptCF(_CFURLStorageSessionCopyCookieStorage(kCFAllocatorDefault, storageSession.get()));
     if (!cookieStorage)
         return nullptr;
@@ -173,20 +174,23 @@
     return storageSession.leakRef();
 }
 
-static NSArray *httpCookies(CFHTTPCookieStorageRef cookieStorage)
+NSArray *NetworkStorageSession::httpCookies(CFHTTPCookieStorageRef cookieStorage) const
 {
-    ASSERT(hasProcessPrivilege(ProcessPrivilege::CanAccessRawCookies));
-    if (!cookieStorage)
+    ASSERT(hasProcessPrivilege(ProcessPrivilege::CanAccessRawCookies) || m_isInMemoryCookieStore);
+    if (!cookieStorage) {
+        RELEASE_ASSERT(!m_isInMemoryCookieStore);
         return [[NSHTTPCookieStorage sharedHTTPCookieStorage] cookies];
+    }
     
     auto cookies = adoptCF(CFHTTPCookieStorageCopyCookies(cookieStorage));
     return [NSHTTPCookie _cf2nsCookies:cookies.get()];
 }
 
-static void deleteHTTPCookie(CFHTTPCookieStorageRef cookieStorage, NSHTTPCookie *cookie)
+void NetworkStorageSession::deleteHTTPCookie(CFHTTPCookieStorageRef cookieStorage, NSHTTPCookie *cookie) const
 {
-    ASSERT(hasProcessPrivilege(ProcessPrivilege::CanAccessRawCookies));
+    ASSERT(hasProcessPrivilege(ProcessPrivilege::CanAccessRawCookies) || m_isInMemoryCookieStore);
     if (!cookieStorage) {
+        RELEASE_ASSERT(!m_isInMemoryCookieStore);
         [[NSHTTPCookieStorage sharedHTTPCookieStorage] deleteCookie:cookie];
         return;
     }
@@ -226,9 +230,9 @@
     return cookiesPtr->autorelease();
 }
 
-static void setHTTPCookiesForURL(CFHTTPCookieStorageRef cookieStorage, NSArray *cookies, NSURL *url, NSURL *mainDocumentURL, const SameSiteInfo& sameSiteInfo)
+void NetworkStorageSession::setHTTPCookiesForURL(CFHTTPCookieStorageRef cookieStorage, NSArray *cookies, NSURL *url, NSURL *mainDocumentURL, const SameSiteInfo& sameSiteInfo) const
 {
-    ASSERT(hasProcessPrivilege(ProcessPrivilege::CanAccessRawCookies));
+    ASSERT(hasProcessPrivilege(ProcessPrivilege::CanAccessRawCookies) || m_isInMemoryCookieStore);
     if (!cookieStorage) {
 #if !(PLATFORM(MAC) && __MAC_OS_X_VERSION_MIN_REQUIRED < 101400) && !PLATFORM(WATCHOS) && !PLATFORM(APPLETV)
         if ([NSHTTPCookieStorage instancesRespondToSelector:@selector(_setCookies:forURL:mainDocumentURL:policyProperties:)])
@@ -255,21 +259,22 @@
 #endif
 }
 
-static NSArray *httpCookiesForURL(CFHTTPCookieStorageRef cookieStorage, NSURL *firstParty, const Optional<SameSiteInfo>& sameSiteInfo, NSURL *url)
+NSArray *NetworkStorageSession::httpCookiesForURL(CFHTTPCookieStorageRef cookieStorage, NSURL *firstParty, const Optional<SameSiteInfo>& sameSiteInfo, NSURL *url) const
 {
-    ASSERT(hasProcessPrivilege(ProcessPrivilege::CanAccessRawCookies));
-    if (!cookieStorage)
+    ASSERT(hasProcessPrivilege(ProcessPrivilege::CanAccessRawCookies) || m_isInMemoryCookieStore);
+    if (!cookieStorage) {
+        RELEASE_ASSERT(!m_isInMemoryCookieStore);
         cookieStorage = _CFHTTPCookieStorageGetDefault(kCFAllocatorDefault);
+    }
 
     // FIXME: Stop creating a new NSHTTPCookieStorage object each time we want to query the cookie jar.
     // NetworkStorageSession could instead keep a NSHTTPCookieStorage object for us.
     RetainPtr<NSHTTPCookieStorage> nsCookieStorage = adoptNS([[NSHTTPCookieStorage alloc] _initWithCFHTTPCookieStorage:cookieStorage]);
-    return cookiesForURL(nsCookieStorage.get(), url, firstParty, sameSiteInfo);
+    return WebCore::cookiesForURL(nsCookieStorage.get(), url, firstParty, sameSiteInfo);
 }
 
 static RetainPtr<NSArray> filterCookies(NSArray *unfilteredCookies, Optional<Seconds> cappedLifetime)
 {
-    ASSERT(hasProcessPrivilege(ProcessPrivilege::CanAccessRawCookies));
     NSUInteger count = [unfilteredCookies count];
     RetainPtr<NSMutableArray> filteredCookies = adoptNS([[NSMutableArray alloc] initWithCapacity:count]);
 
@@ -302,10 +307,10 @@
     return filteredCookies;
 }
 
-static NSArray *cookiesForURL(const NetworkStorageSession& session, const URL& firstParty, const SameSiteInfo& sameSiteInfo, const URL& url, Optional<FrameIdentifier> frameID, Optional<PageIdentifier> pageID, ShouldAskITP shouldAskITP)
+NSArray *NetworkStorageSession::cookiesForURL(const URL& firstParty, const SameSiteInfo& sameSiteInfo, const URL& url, Optional<FrameIdentifier> frameID, Optional<PageIdentifier> pageID, ShouldAskITP shouldAskITP) const
 {
 #if ENABLE(RESOURCE_LOAD_STATISTICS)
-    if (shouldAskITP == ShouldAskITP::Yes && session.shouldBlockCookies(firstParty, url, frameID, pageID))
+    if (shouldAskITP == ShouldAskITP::Yes && shouldBlockCookies(firstParty, url, frameID, pageID))
         return nil;
 #else
     UNUSED_PARAM(frameID);
@@ -312,17 +317,16 @@
     UNUSED_PARAM(pageID);
     UNUSED_PARAM(shouldAskITP);
 #endif
-    return httpCookiesForURL(session.cookieStorage().get(), firstParty, sameSiteInfo, url);
+    return httpCookiesForURL(cookieStorage().get(), firstParty, sameSiteInfo, url);
 }
 
-enum IncludeHTTPOnlyOrNot { DoNotIncludeHTTPOnly, IncludeHTTPOnly };
-static std::pair<String, bool> cookiesForSession(const NetworkStorageSession& session, const URL& firstParty, const SameSiteInfo& sameSiteInfo, const URL& url, Optional<FrameIdentifier> frameID, Optional<PageIdentifier> pageID, IncludeHTTPOnlyOrNot includeHTTPOnly, IncludeSecureCookies includeSecureCookies, ShouldAskITP shouldAskITP = ShouldAskITP::Yes)
+std::pair<String, bool> NetworkStorageSession::cookiesForSession(const URL& firstParty, const SameSiteInfo& sameSiteInfo, const URL& url, Optional<FrameIdentifier> frameID, Optional<PageIdentifier> pageID, IncludeHTTPOnlyOrNot includeHTTPOnly, IncludeSecureCookies includeSecureCookies, ShouldAskITP shouldAskITP) const
 {
-    ASSERT(hasProcessPrivilege(ProcessPrivilege::CanAccessRawCookies));
+    ASSERT(hasProcessPrivilege(ProcessPrivilege::CanAccessRawCookies) || m_isInMemoryCookieStore);
 
     BEGIN_BLOCK_OBJC_EXCEPTIONS;
 
-    NSArray *cookies = cookiesForURL(session, firstParty, sameSiteInfo, url, frameID, pageID, shouldAskITP);
+    NSArray *cookies = cookiesForURL(firstParty, sameSiteInfo, url, frameID, pageID, shouldAskITP);
     if (![cookies count])
         return { String(), false }; // Return a null string, not an empty one that StringBuilder would create below.
 
@@ -374,22 +378,22 @@
 
 std::pair<String, bool> NetworkStorageSession::cookiesForDOM(const URL& firstParty, const SameSiteInfo& sameSiteInfo, const URL& url, Optional<FrameIdentifier> frameID, Optional<PageIdentifier> pageID, IncludeSecureCookies includeSecureCookies, ShouldAskITP shouldAskITP) const
 {
-    return cookiesForSession(*this, firstParty, sameSiteInfo, url, frameID, pageID, DoNotIncludeHTTPOnly, includeSecureCookies, shouldAskITP);
+    return cookiesForSession(firstParty, sameSiteInfo, url, frameID, pageID, DoNotIncludeHTTPOnly, includeSecureCookies, shouldAskITP);
 }
 
 std::pair<String, bool> NetworkStorageSession::cookieRequestHeaderFieldValue(const URL& firstParty, const SameSiteInfo& sameSiteInfo, const URL& url, Optional<FrameIdentifier> frameID, Optional<PageIdentifier> pageID, IncludeSecureCookies includeSecureCookies, ShouldAskITP shouldAskITP) const
 {
-    return cookiesForSession(*this, firstParty, sameSiteInfo, url, frameID, pageID, IncludeHTTPOnly, includeSecureCookies, shouldAskITP);
+    return cookiesForSession(firstParty, sameSiteInfo, url, frameID, pageID, IncludeHTTPOnly, includeSecureCookies, shouldAskITP);
 }
 
 std::pair<String, bool> NetworkStorageSession::cookieRequestHeaderFieldValue(const CookieRequestHeaderFieldProxy& headerFieldProxy) const
 {
-    return cookiesForSession(*this, headerFieldProxy.firstParty, headerFieldProxy.sameSiteInfo, headerFieldProxy.url, headerFieldProxy.frameID, headerFieldProxy.pageID, IncludeHTTPOnly, headerFieldProxy.includeSecureCookies);
+    return cookiesForSession(headerFieldProxy.firstParty, headerFieldProxy.sameSiteInfo, headerFieldProxy.url, headerFieldProxy.frameID, headerFieldProxy.pageID, IncludeHTTPOnly, headerFieldProxy.includeSecureCookies);
 }
 
 void NetworkStorageSession::setCookiesFromDOM(const URL& firstParty, const SameSiteInfo& sameSiteInfo, const URL& url, Optional<FrameIdentifier> frameID, Optional<PageIdentifier> pageID, ShouldAskITP shouldAskITP, const String& cookieStr) const
 {
-    ASSERT(hasProcessPrivilege(ProcessPrivilege::CanAccessRawCookies));
+    ASSERT(hasProcessPrivilege(ProcessPrivilege::CanAccessRawCookies) || m_isInMemoryCookieStore);
 
     BEGIN_BLOCK_OBJC_EXCEPTIONS;
 
@@ -457,7 +461,7 @@
     rawCookies.clear();
     BEGIN_BLOCK_OBJC_EXCEPTIONS;
 
-    NSArray *cookies = cookiesForURL(*this, firstParty, sameSiteInfo, url, frameID, pageID, shouldAskITP);
+    NSArray *cookies = cookiesForURL(firstParty, sameSiteInfo, url, frameID, pageID, shouldAskITP);
     NSUInteger count = [cookies count];
     rawCookies.reserveCapacity(count);
     for (NSUInteger i = 0; i < count; ++i) {
@@ -518,7 +522,7 @@
 
 void NetworkStorageSession::deleteCookiesForHostnames(const Vector<String>& hostnames, IncludeHttpOnlyCookies includeHttpOnlyCookies)
 {
-    ASSERT(hasProcessPrivilege(ProcessPrivilege::CanAccessRawCookies));
+    ASSERT(hasProcessPrivilege(ProcessPrivilege::CanAccessRawCookies) || m_isInMemoryCookieStore);
 
     BEGIN_BLOCK_OBJC_EXCEPTIONS;
 
@@ -566,4 +570,98 @@
     [storage _saveCookies];
 }
 
+Vector<Cookie> NetworkStorageSession::domCookiesForHost(const String& host)
+{
+    NSArray *nsCookies = [nsCookieStorage() _getCookiesForDomain:(NSString *)host];
+    return nsCookiesToCookieVector(nsCookies, [](NSHTTPCookie *cookie) { return !cookie.HTTPOnly; });
+}
+
+#if HAVE(COOKIE_CHANGE_LISTENER_API)
+
+void NetworkStorageSession::registerCookieChangeListenersIfNecessary()
+{
+    if (m_didRegisterCookieListeners)
+        return;
+
+    m_didRegisterCookieListeners = true;
+    [nsCookieStorage() _setCookiesAddedHandler:^(NSArray<NSHTTPCookie *> * nsCookies, NSURL *urlForAddedCookies) {
+        Vector<Cookie> cookies = nsCookiesToCookieVector(nsCookies);
+        auto host = URL(urlForAddedCookies).host().toString();
+        RELEASE_ASSERT(!host.isNull());
+        auto it = m_cookieChangeObservers.find(host);
+        if (it == m_cookieChangeObservers.end())
+            return;
+        for (auto* observer : it->value)
+            observer->cookiesAdded(host, cookies);
+    } onQueue:dispatch_get_main_queue()];
+    [nsCookieStorage() _setCookiesDeletedHandler:^(NSArray<NSHTTPCookie *> *, bool /*deletedAllCookies*/) {
+        for (auto& observers : m_cookieChangeObservers.values()) {
+            for (auto* observer : observers)
+                observer->cookiesDeleted();
+        }
+    } onQueue:dispatch_get_main_queue()];
+}
+
+void NetworkStorageSession::unregisterCookieChangeListenersIfNecessary()
+{
+    if (!m_didRegisterCookieListeners)
+        return;
+
+    [nsCookieStorage() _setCookiesAddedHandler:nil onQueue:nil];
+    [nsCookieStorage() _setCookiesDeletedHandler:nil onQueue:nil];
+    [nsCookieStorage() _setSubscribedDomainsForCookieChanges:nil];
+    m_didRegisterCookieListeners = false;
+}
+
+void NetworkStorageSession::startListeningForCookieChangeNotifications(CookieChangeObserver& observer, const String& host)
+{
+    registerCookieChangeListenersIfNecessary();
+
+    auto& observers = m_cookieChangeObservers.ensure(host, [] {
+        return HashSet<CookieChangeObserver*> { };
+    }).iterator->value;
+    ASSERT(!observers.contains(&observer));
+    observers.add(&observer);
+
+    if (!m_subscribedDomainsForCookieChanges)
+        m_subscribedDomainsForCookieChanges = adoptNS([[NSMutableSet alloc] init]);
+    else if ([m_subscribedDomainsForCookieChanges containsObject:(NSString *)host])
+        return;
+
+    [m_subscribedDomainsForCookieChanges addObject:(NSString *)host];
+    [nsCookieStorage() _setSubscribedDomainsForCookieChanges:m_subscribedDomainsForCookieChanges.get()];
+}
+
+void NetworkStorageSession::stopListeningForCookieChangeNotifications(CookieChangeObserver& observer, const HashSet<String>& hosts)
+{
+    bool subscribedURLsChanged = false;
+    for (auto& host : hosts) {
+        auto it = m_cookieChangeObservers.find(host);
+        ASSERT(it != m_cookieChangeObservers.end());
+        if (it == m_cookieChangeObservers.end())
+            continue;
+
+        auto& observers = it->value;
+        ASSERT(observers.contains(&observer));
+        observers.remove(&observer);
+        if (observers.isEmpty()) {
+            m_cookieChangeObservers.remove(it);
+            ASSERT([m_subscribedDomainsForCookieChanges containsObject:(NSString *)host]);
+            [m_subscribedDomainsForCookieChanges removeObject:(NSString *)host];
+            subscribedURLsChanged = true;
+        }
+    }
+    if (subscribedURLsChanged)
+        [nsCookieStorage() _setSubscribedDomainsForCookieChanges:m_subscribedDomainsForCookieChanges.get()];
+}
+
+// FIXME: This can eventually go away, this is merely to ensure a smooth transition to the new API.
+bool NetworkStorageSession::supportsCookieChangeListenerAPI() const
+{
+    static const bool supportsAPI = [nsCookieStorage() respondsToSelector:@selector(_setSubscribedDomainsForCookieChanges:)];
+    return supportsAPI;
+}
+
+#endif // HAVE(COOKIE_CHANGE_LISTENER_API)
+
 } // namespace WebCore

Modified: trunk/Source/WebKit/ChangeLog (256819 => 256820)


--- trunk/Source/WebKit/ChangeLog	2020-02-18 06:10:16 UTC (rev 256819)
+++ trunk/Source/WebKit/ChangeLog	2020-02-18 06:32:42 UTC (rev 256820)
@@ -1,5 +1,61 @@
 2020-02-17  Chris Dumez  <[email protected]>
 
+        [WK2][Cocoa] Implement in-WebProcess cookie cache to avoid sync IPC for document.cookie in most cases
+        https://bugs.webkit.org/show_bug.cgi?id=207593
+        <rdar://problem/56027027>
+
+        Reviewed by Antti Koivisto.
+
+        See WebCore ChangeLog.
+
+        * NetworkProcess/NetworkConnectionToWebProcess.cpp:
+        (WebKit::NetworkConnectionToWebProcess::~NetworkConnectionToWebProcess):
+        (WebKit::NetworkConnectionToWebProcess::domCookiesForHost):
+        (WebKit::NetworkConnectionToWebProcess::unsubscribeFromCookieChangeNotifications):
+        (WebKit::NetworkConnectionToWebProcess::cookiesAdded):
+        (WebKit::NetworkConnectionToWebProcess::cookiesDeleted):
+        * NetworkProcess/NetworkConnectionToWebProcess.h:
+        * NetworkProcess/NetworkConnectionToWebProcess.messages.in:
+        * Scripts/webkit/messages.py:
+        * Shared/WebPreferences.yaml:
+        * Sources.txt:
+        * SourcesCocoa.txt:
+        * WebKit.xcodeproj/project.pbxproj:
+        * WebProcess/Network/NetworkProcessConnection.cpp:
+        (WebKit::NetworkProcessConnection::cookiesAdded):
+        (WebKit::NetworkProcessConnection::cookiesDeleted):
+        * WebProcess/Network/NetworkProcessConnection.h:
+        * WebProcess/Network/NetworkProcessConnection.messages.in:
+        * WebProcess/WebPage/Cocoa/WebCookieCacheCocoa.mm: Copied from Source/WebKit/WebProcess/WebPage/WebCookieJar.h.
+        (WebKit::WebCookieCache::inMemoryStorageSession):
+        * WebProcess/WebPage/WebCookieCache.cpp: Added.
+        (WebKit::WebCookieCache::isFunctional):
+        (WebKit::WebCookieCache::cookiesForDOM):
+        (WebKit::WebCookieCache::setCookiesFromDOM):
+        (WebKit::WebCookieCache::cookiesAdded):
+        (WebKit::WebCookieCache::cookiesDeleted):
+        (WebKit::WebCookieCache::clear):
+        (WebKit::WebCookieCache::clearForHost):
+        (WebKit::WebCookieCache::pruneCacheIfNecessary):
+        * WebProcess/WebPage/WebCookieCache.h: Copied from Source/WebKit/WebProcess/WebPage/WebCookieJar.h.
+        * WebProcess/WebPage/WebCookieJar.cpp:
+        (WebKit::WebCookieJar::isEligibleForCache const):
+        (WebKit::WebCookieJar::cookies const):
+        (WebKit::WebCookieJar::setCookies):
+        (WebKit::WebCookieJar::cookiesAdded):
+        (WebKit::WebCookieJar::cookiesDeleted):
+        (WebKit::WebCookieJar::clearCache):
+        (WebKit::WebCookieJar::clearCacheForHost):
+        * WebProcess/WebPage/WebCookieJar.h:
+        * WebProcess/WebPage/WebPage.cpp:
+        (WebKit::m_overriddenMediaType):
+        * WebProcess/WebProcess.cpp:
+        (WebKit::WebProcess::WebProcess):
+        * WebProcess/WebProcess.h:
+        (WebKit::WebProcess::cookieJar):
+
+2020-02-17  Chris Dumez  <[email protected]>
+
         Defer execution of async scripts until until the document is loaded
         https://bugs.webkit.org/show_bug.cgi?id=207698
         <rdar://problem/57625747>

Modified: trunk/Source/WebKit/NetworkProcess/NetworkConnectionToWebProcess.cpp (256819 => 256820)


--- trunk/Source/WebKit/NetworkProcess/NetworkConnectionToWebProcess.cpp	2020-02-18 06:10:16 UTC (rev 256819)
+++ trunk/Source/WebKit/NetworkProcess/NetworkConnectionToWebProcess.cpp	2020-02-18 06:32:42 UTC (rev 256820)
@@ -124,6 +124,11 @@
     for (auto& port : m_processEntangledPorts)
         networkProcess().messagePortChannelRegistry().didCloseMessagePort(port);
 
+#if HAVE(COOKIE_CHANGE_LISTENER_API)
+    if (auto* networkStorageSession = storageSession())
+        networkStorageSession->stopListeningForCookieChangeNotifications(*this, m_hostsWithCookieListeners);
+#endif
+
 #if USE(LIBWEBRTC)
     if (m_rtcProvider)
         m_rtcProvider->close();
@@ -633,6 +638,48 @@
     networkStorageSession->deleteCookie(url, cookieName);
 }
 
+void NetworkConnectionToWebProcess::domCookiesForHost(const String& host, bool subscribeToCookieChangeNotifications, CompletionHandler<void(const Vector<WebCore::Cookie>&)>&& completionHandler)
+{
+    auto* networkStorageSession = storageSession();
+    if (!networkStorageSession)
+        return completionHandler({ });
+
+#if HAVE(COOKIE_CHANGE_LISTENER_API)
+    if (subscribeToCookieChangeNotifications) {
+        ASSERT(!m_hostsWithCookieListeners.contains(host));
+        m_hostsWithCookieListeners.add(host);
+        networkStorageSession->startListeningForCookieChangeNotifications(*this, host);
+    }
+#else
+    UNUSED_PARAM(subscribeToCookieChangeNotifications);
+#endif
+
+    completionHandler(networkStorageSession->domCookiesForHost(host));
+}
+
+#if HAVE(COOKIE_CHANGE_LISTENER_API)
+
+void NetworkConnectionToWebProcess::unsubscribeFromCookieChangeNotifications(const HashSet<String>& hosts)
+{
+    bool removed = m_hostsWithCookieListeners.remove(hosts.begin(), hosts.end());
+    ASSERT_UNUSED(removed, removed);
+
+    if (auto* networkStorageSession = storageSession())
+        networkStorageSession->stopListeningForCookieChangeNotifications(*this, hosts);
+}
+
+void NetworkConnectionToWebProcess::cookiesAdded(const String& host, const Vector<WebCore::Cookie>& cookies)
+{
+    connection().send(Messages::NetworkProcessConnection::CookiesAdded(host, cookies), 0);
+}
+
+void NetworkConnectionToWebProcess::cookiesDeleted()
+{
+    connection().send(Messages::NetworkProcessConnection::CookiesDeleted(), 0);
+}
+
+#endif
+
 void NetworkConnectionToWebProcess::registerFileBlobURL(const URL& url, const String& path, SandboxExtension::Handle&& extensionHandle, const String& contentType)
 {
     auto* session = networkSession();

Modified: trunk/Source/WebKit/NetworkProcess/NetworkConnectionToWebProcess.h (256819 => 256820)


--- trunk/Source/WebKit/NetworkProcess/NetworkConnectionToWebProcess.h	2020-02-18 06:10:16 UTC (rev 256819)
+++ trunk/Source/WebKit/NetworkProcess/NetworkConnectionToWebProcess.h	2020-02-18 06:32:42 UTC (rev 256820)
@@ -42,6 +42,7 @@
 #include <WebCore/MessagePortChannelProvider.h>
 #include <WebCore/MessagePortIdentifier.h>
 #include <WebCore/NetworkLoadInformation.h>
+#include <WebCore/NetworkStorageSession.h>
 #include <WebCore/PageIdentifier.h>
 #include <WebCore/ProcessIdentifier.h>
 #include <WebCore/RegistrableDomain.h>
@@ -88,6 +89,9 @@
 #if ENABLE(APPLE_PAY_REMOTE_UI)
     , public WebPaymentCoordinatorProxy::Client
 #endif
+#if HAVE(COOKIE_CHANGE_LISTENER_API)
+    , public WebCore::CookieChangeObserver
+#endif
     , IPC::Connection::Client {
 public:
     using RegistrableDomain = WebCore::RegistrableDomain;
@@ -267,6 +271,16 @@
 
     uint64_t nextMessageBatchIdentifier(Function<void()>&&);
 
+    void domCookiesForHost(const String& host, bool subscribeToCookieChangeNotifications, CompletionHandler<void(const Vector<WebCore::Cookie>&)>&&);
+
+#if HAVE(COOKIE_CHANGE_LISTENER_API)
+    void unsubscribeFromCookieChangeNotifications(const HashSet<String>& hosts);
+
+    // WebCore::CookieChangeObserver.
+    void cookiesAdded(const String& host, const Vector<WebCore::Cookie>&) final;
+    void cookiesDeleted() final;
+#endif
+
     struct ResourceNetworkActivityTracker {
         ResourceNetworkActivityTracker() = default;
         ResourceNetworkActivityTracker(const ResourceNetworkActivityTracker&) = default;
@@ -330,6 +344,9 @@
 #if ENABLE(WEB_RTC)
     NetworkMDNSRegister m_mdnsRegister;
 #endif
+#if HAVE(COOKIE_CHANGE_LISTENER_API)
+    HashSet<String> m_hostsWithCookieListeners;
+#endif
 
     bool m_captureExtraNetworkLoadMetricsEnabled { false };
 

Modified: trunk/Source/WebKit/NetworkProcess/NetworkConnectionToWebProcess.messages.in (256819 => 256820)


--- trunk/Source/WebKit/NetworkProcess/NetworkConnectionToWebProcess.messages.in	2020-02-18 06:10:16 UTC (rev 256819)
+++ trunk/Source/WebKit/NetworkProcess/NetworkConnectionToWebProcess.messages.in	2020-02-18 06:32:42 UTC (rev 256820)
@@ -39,6 +39,10 @@
     CookieRequestHeaderFieldValue(URL firstParty, struct WebCore::SameSiteInfo sameSiteInfo, URL url, Optional<WebCore::FrameIdentifier> frameID, Optional<WebCore::PageIdentifier> pageID, enum:bool WebCore::IncludeSecureCookies includeSecureCookies, enum:bool WebCore::ShouldAskITP shouldAskITP) -> (String cookieString, bool didAccessSecureCookies) Synchronous
     GetRawCookies(URL firstParty, struct WebCore::SameSiteInfo sameSiteInfo, URL url, Optional<WebCore::FrameIdentifier> frameID, Optional<WebCore::PageIdentifier> pageID, enum:bool WebCore::ShouldAskITP shouldAskITP) -> (Vector<WebCore::Cookie> cookies) Synchronous
     DeleteCookie(URL url, String cookieName)
+    DomCookiesForHost(String host, bool subscribeToCookieChangeNotifications) -> (Vector<WebCore::Cookie> cookies) Synchronous
+#if HAVE(COOKIE_CHANGE_LISTENER_API)
+    UnsubscribeFromCookieChangeNotifications(HashSet<String> hosts)
+#endif
 
     RegisterFileBlobURL(URL url, String path, WebKit::SandboxExtension::Handle extensionHandle, String contentType)
     RegisterBlobURL(URL url, Vector<WebCore::BlobPart> blobParts, String contentType)

Modified: trunk/Source/WebKit/Scripts/webkit/messages.py (256819 => 256820)


--- trunk/Source/WebKit/Scripts/webkit/messages.py	2020-02-18 06:10:16 UTC (rev 256819)
+++ trunk/Source/WebKit/Scripts/webkit/messages.py	2020-02-18 06:32:42 UTC (rev 256820)
@@ -625,6 +625,7 @@
         'WebKit::WebWheelEvent': ['"WebEvent.h"'],
         'WebCore::MediaEngineSupportParameters': ['<WebCore/MediaPlayer.h>'],
         'WebCore::ISOWebVTTCue': ['<WebCore/ISOVTTCue.h>'],
+        'struct WebCore::Cookie': ['<WebCore/Cookie.h>'],
         'struct WebCore::ElementContext': ['<WebCore/ElementContext.h>'],
         'struct WebKit::WebUserScriptData': ['"WebUserContentControllerDataTypes.h"'],
         'struct WebKit::WebUserStyleSheetData': ['"WebUserContentControllerDataTypes.h"'],

Modified: trunk/Source/WebKit/Shared/WebPreferences.yaml (256819 => 256820)


--- trunk/Source/WebKit/Shared/WebPreferences.yaml	2020-02-18 06:10:16 UTC (rev 256819)
+++ trunk/Source/WebKit/Shared/WebPreferences.yaml	2020-02-18 06:32:42 UTC (rev 256820)
@@ -55,6 +55,13 @@
    humanReadableDescription: "Automatic HTTPS upgrade for known supported sites"
    category: experimental
 
+InProcessCookieCacheEnabled:
+   type: bool
+   defaultValue: true
+   humanReadableName: "In-Process Cookie Cache"
+   humanReadableDescription: "In-Process DOM Cookie Cache"
+   category: experimental
+
 ThirdPartyIframeRedirectBlockingEnabled:
    type: bool
    defaultValue: true

Modified: trunk/Source/WebKit/Sources.txt (256819 => 256820)


--- trunk/Source/WebKit/Sources.txt	2020-02-18 06:10:16 UTC (rev 256819)
+++ trunk/Source/WebKit/Sources.txt	2020-02-18 06:32:42 UTC (rev 256820)
@@ -624,6 +624,7 @@
 WebProcess/WebPage/VisitedLinkTableController.cpp
 WebProcess/WebPage/WebBackForwardListProxy.cpp
 WebProcess/WebPage/WebContextMenu.cpp
+WebProcess/WebPage/WebCookieCache.cpp
 WebProcess/WebPage/WebCookieJar.cpp
 WebProcess/WebPage/WebDocumentLoader.cpp
 WebProcess/WebPage/WebFrame.cpp

Modified: trunk/Source/WebKit/SourcesCocoa.txt (256819 => 256820)


--- trunk/Source/WebKit/SourcesCocoa.txt	2020-02-18 06:10:16 UTC (rev 256819)
+++ trunk/Source/WebKit/SourcesCocoa.txt	2020-02-18 06:32:42 UTC (rev 256820)
@@ -613,6 +613,7 @@
 WebProcess/WebPage/WKAccessibilityWebPageObjectIOS.mm
 
 WebProcess/WebPage/Cocoa/TextCheckingControllerProxy.mm
+WebProcess/WebPage/Cocoa/WebCookieCacheCocoa.mm
 WebProcess/WebPage/Cocoa/WebPageCocoa.mm
 WebProcess/WebPage/Cocoa/WebRemoteObjectRegistry.cpp
 

Modified: trunk/Source/WebKit/WebKit.xcodeproj/project.pbxproj (256819 => 256820)


--- trunk/Source/WebKit/WebKit.xcodeproj/project.pbxproj	2020-02-18 06:10:16 UTC (rev 256819)
+++ trunk/Source/WebKit/WebKit.xcodeproj/project.pbxproj	2020-02-18 06:32:42 UTC (rev 256820)
@@ -943,6 +943,7 @@
 		466BC03C1FA266DA002FA9C1 /* WebSWContextManagerConnection.h in Headers */ = {isa = PBXBuildFile; fileRef = 466BC0391FA266C9002FA9C1 /* WebSWContextManagerConnection.h */; };
 		4671FF1F23217EFF001B64C7 /* WebResourceLoadObserver.h in Headers */ = {isa = PBXBuildFile; fileRef = 4671FF1D23217EFF001B64C7 /* WebResourceLoadObserver.h */; };
 		467E43E82243FF7D00B13924 /* WebProcessDataStoreParameters.h in Headers */ = {isa = PBXBuildFile; fileRef = 467E43E72243FF6D00B13924 /* WebProcessDataStoreParameters.h */; };
+		46809A7C23D9225E00C297D0 /* WebCookieCache.h in Headers */ = {isa = PBXBuildFile; fileRef = 46809A7A23D9225300C297D0 /* WebCookieCache.h */; };
 		46A2B6091E5676A600C3DEDA /* BackgroundProcessResponsivenessTimer.h in Headers */ = {isa = PBXBuildFile; fileRef = 46A2B6071E5675A200C3DEDA /* BackgroundProcessResponsivenessTimer.h */; };
 		46B0524722668D8500265B97 /* WebDeviceOrientationAndMotionAccessController.h in Headers */ = {isa = PBXBuildFile; fileRef = 46B0524422668D2300265B97 /* WebDeviceOrientationAndMotionAccessController.h */; };
 		46BEB6D722FB9BD700269867 /* StorageArea.h in Headers */ = {isa = PBXBuildFile; fileRef = 46BEB6D522FB9BD600269867 /* StorageArea.h */; };
@@ -3486,6 +3487,8 @@
 		4671FF1D23217EFF001B64C7 /* WebResourceLoadObserver.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = WebResourceLoadObserver.h; sourceTree = "<group>"; };
 		4671FF1E23217EFF001B64C7 /* WebResourceLoadObserver.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = WebResourceLoadObserver.cpp; sourceTree = "<group>"; };
 		467E43E72243FF6D00B13924 /* WebProcessDataStoreParameters.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WebProcessDataStoreParameters.h; sourceTree = "<group>"; };
+		46809A7A23D9225300C297D0 /* WebCookieCache.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WebCookieCache.h; sourceTree = "<group>"; };
+		46809A7B23D9225300C297D0 /* WebCookieCache.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = WebCookieCache.cpp; sourceTree = "<group>"; };
 		4683569A21E81CC7006E27A3 /* ProvisionalPageProxy.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ProvisionalPageProxy.h; sourceTree = "<group>"; };
 		4683569B21E81CC7006E27A3 /* ProvisionalPageProxy.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ProvisionalPageProxy.cpp; sourceTree = "<group>"; };
 		46A2B6061E5675A200C3DEDA /* BackgroundProcessResponsivenessTimer.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = BackgroundProcessResponsivenessTimer.cpp; sourceTree = "<group>"; };
@@ -8428,6 +8431,8 @@
 				BC72B9F911E6476B001EB4EA /* WebBackForwardListProxy.h */,
 				51871B59127CB89D00F76232 /* WebContextMenu.cpp */,
 				51871B5A127CB89D00F76232 /* WebContextMenu.h */,
+				46809A7B23D9225300C297D0 /* WebCookieCache.cpp */,
+				46809A7A23D9225300C297D0 /* WebCookieCache.h */,
 				5C7FB46E21E97C0B009E3241 /* WebCookieJar.cpp */,
 				5C7FB46F21E97C0C009E3241 /* WebCookieJar.h */,
 				1A5B1C5218987EDF004FCF9B /* WebDocumentLoader.cpp */,
@@ -10668,6 +10673,7 @@
 				51ACBB82127A8BAD00D203B9 /* WebContextMenuProxy.h in Headers */,
 				51ACBBA0127A8F2C00D203B9 /* WebContextMenuProxyMac.h in Headers */,
 				BCF4DE25168FA44800C94AFC /* WebContextSupplement.h in Headers */,
+				46809A7C23D9225E00C297D0 /* WebCookieCache.h in Headers */,
 				5C7FB47021E97DC5009E3241 /* WebCookieJar.h in Headers */,
 				330934501315B94D0097A7BC /* WebCookieManager.h in Headers */,
 				330934481315B9220097A7BC /* WebCookieManagerMessages.h in Headers */,

Modified: trunk/Source/WebKit/WebProcess/Network/NetworkProcessConnection.cpp (256819 => 256820)


--- trunk/Source/WebKit/WebProcess/Network/NetworkProcessConnection.cpp	2020-02-18 06:10:16 UTC (rev 256819)
+++ trunk/Source/WebKit/WebProcess/Network/NetworkProcessConnection.cpp	2020-02-18 06:32:42 UTC (rev 256820)
@@ -32,6 +32,7 @@
 #include "StorageAreaMap.h"
 #include "StorageAreaMapMessages.h"
 #include "WebCacheStorageProvider.h"
+#include "WebCookieJar.h"
 #include "WebCoreArgumentCoders.h"
 #include "WebIDBConnectionToServer.h"
 #include "WebIDBConnectionToServerMessages.h"
@@ -234,6 +235,18 @@
     m_cookieAcceptPolicy = newPolicy;
 }
 
+#if HAVE(COOKIE_CHANGE_LISTENER_API)
+void NetworkProcessConnection::cookiesAdded(const String& host, const Vector<WebCore::Cookie>& cookies)
+{
+    WebProcess::singleton().cookieJar().cookiesAdded(host, cookies);
+}
+
+void NetworkProcessConnection::cookiesDeleted()
+{
+    WebProcess::singleton().cookieJar().cookiesDeleted();
+}
+#endif
+
 #if ENABLE(SHAREABLE_RESOURCE)
 void NetworkProcessConnection::didCacheResource(const ResourceRequest& request, const ShareableResource::Handle& handle)
 {

Modified: trunk/Source/WebKit/WebProcess/Network/NetworkProcessConnection.h (256819 => 256820)


--- trunk/Source/WebKit/WebProcess/Network/NetworkProcessConnection.h	2020-02-18 06:10:16 UTC (rev 256819)
+++ trunk/Source/WebKit/WebProcess/Network/NetworkProcessConnection.h	2020-02-18 06:32:42 UTC (rev 256820)
@@ -41,6 +41,7 @@
 class ResourceError;
 class ResourceRequest;
 class ResourceResponse;
+struct Cookie;
 struct MessagePortIdentifier;
 struct MessageWithMessagePorts;
 enum class HTTPCookieAcceptPolicy : uint8_t;
@@ -83,6 +84,11 @@
 
     bool cookiesEnabled() const;
 
+#if HAVE(COOKIE_CHANGE_LISTENER_API)
+    void cookiesAdded(const String& host, const Vector<WebCore::Cookie>&);
+    void cookiesDeleted();
+#endif
+
 private:
     NetworkProcessConnection(IPC::Connection::Identifier, WebCore::HTTPCookieAcceptPolicy);
 

Modified: trunk/Source/WebKit/WebProcess/Network/NetworkProcessConnection.messages.in (256819 => 256820)


--- trunk/Source/WebKit/WebProcess/Network/NetworkProcessConnection.messages.in	2020-02-18 06:10:16 UTC (rev 256819)
+++ trunk/Source/WebKit/WebProcess/Network/NetworkProcessConnection.messages.in	2020-02-18 06:32:42 UTC (rev 256820)
@@ -31,6 +31,11 @@
     SetOnLineState(bool isOnLine);
     CookieAcceptPolicyChanged(enum:uint8_t WebCore::HTTPCookieAcceptPolicy policy);
 
+#if HAVE(COOKIE_CHANGE_LISTENER_API)
+    CookiesAdded(String host, Vector<struct WebCore::Cookie> cookies);
+    CookiesDeleted();
+#endif
+
     CheckProcessLocalPortForActivity(struct WebCore::MessagePortIdentifier port) -> (WebCore::MessagePortChannelProvider::HasActivity hasActivity) Async
     MessagesAvailableForPort(struct WebCore::MessagePortIdentifier port)
 }

Copied: trunk/Source/WebKit/WebProcess/WebPage/Cocoa/WebCookieCacheCocoa.mm (from rev 256808, trunk/Source/WebKit/WebProcess/WebPage/WebCookieJar.h) (0 => 256820)


--- trunk/Source/WebKit/WebProcess/WebPage/Cocoa/WebCookieCacheCocoa.mm	                        (rev 0)
+++ trunk/Source/WebKit/WebProcess/WebPage/Cocoa/WebCookieCacheCocoa.mm	2020-02-18 06:32:42 UTC (rev 256820)
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2020 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#import "config.h"
+#import "WebCookieCache.h"
+
+#import "WebProcess.h"
+#import <WebCore/NetworkStorageSession.h>
+
+namespace WebKit {
+
+using namespace WebCore;
+
+NetworkStorageSession& WebCookieCache::inMemoryStorageSession()
+{
+    if (!m_inMemoryStorageSession) {
+        String sessionName = makeString("WebKitInProcessStorage-", getCurrentProcessID());
+        auto storageSession = adoptCF(WebCore::createPrivateStorageSession(sessionName.createCFString().get()));
+        auto cookieStorage = adoptCF(_CFURLStorageSessionCopyCookieStorage(kCFAllocatorDefault, storageSession.get()));
+        m_inMemoryStorageSession = makeUnique<NetworkStorageSession>(WebProcess::singleton().sessionID(), WTFMove(storageSession), WTFMove(cookieStorage), NetworkStorageSession::IsInMemoryCookieStore::Yes);
+    }
+    return *m_inMemoryStorageSession;
+}
+
+} // namespace WebKit

Added: trunk/Source/WebKit/WebProcess/WebPage/WebCookieCache.cpp (0 => 256820)


--- trunk/Source/WebKit/WebProcess/WebPage/WebCookieCache.cpp	                        (rev 0)
+++ trunk/Source/WebKit/WebProcess/WebPage/WebCookieCache.cpp	2020-02-18 06:32:42 UTC (rev 256820)
@@ -0,0 +1,124 @@
+/*
+ * Copyright (C) 2020 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+#include "WebCookieCache.h"
+
+#include "NetworkConnectionToWebProcessMessages.h"
+#include "NetworkProcessConnection.h"
+#include "WebProcess.h"
+
+namespace WebKit {
+
+using namespace WebCore;
+
+bool WebCookieCache::isSupported()
+{
+#if HAVE(COOKIE_CHANGE_LISTENER_API)
+    // FIXME: This can eventually be removed, this is merely to ensure a smooth transition to the new API.
+    return inMemoryStorageSession().supportsCookieChangeListenerAPI();
+#else
+    return false;
+#endif
+}
+
+String WebCookieCache::cookiesForDOM(const URL& firstParty, const SameSiteInfo& sameSiteInfo, const URL& url, FrameIdentifier frameID, PageIdentifier pageID, IncludeSecureCookies includeSecureCookies)
+{
+    String host = url.host().toString();
+    if (!m_hostsWithInMemoryStorage.contains(host)) {
+        Vector<Cookie> cookies;
+        bool subscribeToCookieChangeNotifications = true;
+        if (!WebProcess::singleton().ensureNetworkProcessConnection().connection().sendSync(Messages::NetworkConnectionToWebProcess::DomCookiesForHost(url.host().toString(), subscribeToCookieChangeNotifications), Messages::NetworkConnectionToWebProcess::DomCookiesForHost::Reply(cookies), 0))
+            return { };
+        pruneCacheIfNecessary();
+        m_hostsWithInMemoryStorage.add(host);
+        for (auto& cookie : cookies)
+            inMemoryStorageSession().setCookie(cookie);
+    }
+    return inMemoryStorageSession().cookiesForDOM(firstParty, sameSiteInfo, url, frameID, pageID, includeSecureCookies, ShouldAskITP::No).first;
+}
+
+void WebCookieCache::setCookiesFromDOM(const URL& firstParty, const SameSiteInfo& sameSiteInfo, const URL& url, FrameIdentifier frameID, PageIdentifier pageID, const String& cookieString)
+{
+    String host = url.host().toString();
+    if (m_hostsWithInMemoryStorage.contains(host))
+        inMemoryStorageSession().setCookiesFromDOM(firstParty, sameSiteInfo, url, frameID, pageID, ShouldAskITP::No, cookieString);
+}
+
+void WebCookieCache::cookiesAdded(const String& host, const Vector<Cookie>& cookies)
+{
+    if (!m_hostsWithInMemoryStorage.contains(host))
+        return;
+
+    for (auto& cookie : cookies)
+        inMemoryStorageSession().setCookie(cookie);
+}
+
+void WebCookieCache::cookiesDeleted()
+{
+    clear();
+}
+
+void WebCookieCache::clear()
+{
+#if HAVE(COOKIE_CHANGE_LISTENER_API)
+    if (!m_hostsWithInMemoryStorage.isEmpty())
+        WebProcess::singleton().ensureNetworkProcessConnection().connection().send(Messages::NetworkConnectionToWebProcess::UnsubscribeFromCookieChangeNotifications(m_hostsWithInMemoryStorage), 0);
+#endif
+    m_hostsWithInMemoryStorage.clear();
+    m_inMemoryStorageSession = nullptr;
+}
+
+void WebCookieCache::clearForHost(const String& host)
+{
+    auto it = m_hostsWithInMemoryStorage.find(host);
+    if (it == m_hostsWithInMemoryStorage.end())
+        return;
+
+    m_hostsWithInMemoryStorage.remove(it);
+    inMemoryStorageSession().deleteCookiesForHostnames(Vector<String> { host });
+#if HAVE(COOKIE_CHANGE_LISTENER_API)
+    WebProcess::singleton().ensureNetworkProcessConnection().connection().send(Messages::NetworkConnectionToWebProcess::UnsubscribeFromCookieChangeNotifications(HashSet<String> { host }), 0);
+#endif
+}
+
+void WebCookieCache::pruneCacheIfNecessary()
+{
+    // We may want to raise this limit if we start using the cache for third-party iframes.
+    static const unsigned maxCachedHosts = 5;
+
+    while (m_hostsWithInMemoryStorage.size() >= maxCachedHosts)
+        clearForHost(*m_hostsWithInMemoryStorage.random());
+}
+
+#if !PLATFORM(COCOA)
+NetworkStorageSession& WebCookieCache::inMemoryStorageSession()
+{
+    ASSERT_NOT_IMPLEMENTED_YET();
+    return *m_inMemoryStorageSession;
+}
+#endif
+
+} // namespace WebKit

Copied: trunk/Source/WebKit/WebProcess/WebPage/WebCookieCache.h (from rev 256808, trunk/Source/WebKit/WebProcess/WebPage/WebCookieJar.h) (0 => 256820)


--- trunk/Source/WebKit/WebProcess/WebPage/WebCookieCache.h	                        (rev 0)
+++ trunk/Source/WebKit/WebProcess/WebPage/WebCookieCache.h	2020-02-18 06:32:42 UTC (rev 256820)
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2020 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#include <WebCore/CookieJar.h>
+#include <WebCore/SameSiteInfo.h>
+#include <wtf/HashSet.h>
+
+namespace WebCore {
+class NetworkStorageSession;
+}
+
+namespace WebKit {
+
+class WebCookieCache {
+public:
+    WebCookieCache() = default;
+
+    bool isSupported();
+
+    String cookiesForDOM(const URL& firstParty, const WebCore::SameSiteInfo&, const URL&, WebCore::FrameIdentifier, WebCore::PageIdentifier, WebCore::IncludeSecureCookies);
+    void setCookiesFromDOM(const URL& firstParty, const WebCore::SameSiteInfo&, const URL&, WebCore::FrameIdentifier, WebCore::PageIdentifier, const String& cookieString);
+
+    void cookiesAdded(const String& host, const Vector<WebCore::Cookie>&);
+    void cookiesDeleted();
+
+    void clear();
+    void clearForHost(const String&);
+
+private:
+    WebCore::NetworkStorageSession& inMemoryStorageSession();
+    void pruneCacheIfNecessary();
+
+    HashSet<String> m_hostsWithInMemoryStorage;
+    std::unique_ptr<WebCore::NetworkStorageSession> m_inMemoryStorageSession;
+};
+
+} // namespace WebKit

Modified: trunk/Source/WebKit/WebProcess/WebPage/WebCookieJar.cpp (256819 => 256820)


--- trunk/Source/WebKit/WebProcess/WebPage/WebCookieJar.cpp	2020-02-18 06:10:16 UTC (rev 256819)
+++ trunk/Source/WebKit/WebProcess/WebPage/WebCookieJar.cpp	2020-02-18 06:32:42 UTC (rev 256820)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2019 Apple Inc. All rights reserved.
+ * Copyright (C) 2019-2020 Apple Inc. All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
@@ -37,6 +37,7 @@
 #include <WebCore/Frame.h>
 #include <WebCore/FrameLoader.h>
 #include <WebCore/FrameLoaderClient.h>
+#include <WebCore/Settings.h>
 #include <WebCore/StorageSessionProvider.h>
 
 namespace WebKit {
@@ -88,6 +89,23 @@
 }
 #endif
 
+bool WebCookieJar::isEligibleForCache(WebFrame& frame, const URL& firstPartyForCookies, const URL& resourceURL) const
+{
+    auto* page = frame.page() ? frame.page()->corePage() : nullptr;
+    if (!page || !page->settings().inProcessCookieCacheEnabled())
+        return false;
+
+    if (!m_cache.isSupported())
+        return false;
+
+    // For now, we only cache cookies for first-party content. Third-party cookie caching is a bit more complicated due to partitioning and storage access.
+    RegistrableDomain resourceDomain { resourceURL };
+    if (resourceDomain.isEmpty())
+        return false;
+
+    return frame.isMainFrame() || RegistrableDomain { firstPartyForCookies } == resourceDomain;
+}
+
 String WebCookieJar::cookies(WebCore::Document& document, const URL& url) const
 {
     auto* webFrame = document.frame() ? WebFrame::fromCoreFrame(*document.frame()) : nullptr;
@@ -100,12 +118,17 @@
         return { };
 #endif
 
+    auto sameSiteInfo = CookieJar::sameSiteInfo(document);
+    auto includeSecureCookies = CookieJar::shouldIncludeSecureCookies(document, url);
     auto frameID = webFrame->frameID();
     auto pageID = webFrame->page()->identifier();
 
+    if (isEligibleForCache(*webFrame, document.firstPartyForCookies(), url))
+        return m_cache.cookiesForDOM(document.firstPartyForCookies(), sameSiteInfo, url, frameID, pageID, includeSecureCookies);
+
     String cookieString;
     bool secureCookiesAccessed = false;
-    if (!WebProcess::singleton().ensureNetworkProcessConnection().connection().sendSync(Messages::NetworkConnectionToWebProcess::CookiesForDOM(document.firstPartyForCookies(), sameSiteInfo(document), url, frameID, pageID, shouldIncludeSecureCookies(document, url), shouldAskITPInNetworkProcess), Messages::NetworkConnectionToWebProcess::CookiesForDOM::Reply(cookieString, secureCookiesAccessed), 0))
+    if (!WebProcess::singleton().ensureNetworkProcessConnection().connection().sendSync(Messages::NetworkConnectionToWebProcess::CookiesForDOM(document.firstPartyForCookies(), sameSiteInfo, url, frameID, pageID, includeSecureCookies, shouldAskITPInNetworkProcess), Messages::NetworkConnectionToWebProcess::CookiesForDOM::Reply(cookieString, secureCookiesAccessed), 0))
         return { };
 
     return cookieString;
@@ -123,12 +146,36 @@
         return;
 #endif
 
+    auto sameSiteInfo = CookieJar::sameSiteInfo(document);
     auto frameID = webFrame->frameID();
     auto pageID = webFrame->page()->identifier();
 
-    WebProcess::singleton().ensureNetworkProcessConnection().connection().send(Messages::NetworkConnectionToWebProcess::SetCookiesFromDOM(document.firstPartyForCookies(), sameSiteInfo(document), url, frameID, pageID, shouldAskITPInNetworkProcess, cookieString), 0);
+    if (isEligibleForCache(*webFrame, document.firstPartyForCookies(), url))
+        m_cache.setCookiesFromDOM(document.firstPartyForCookies(), sameSiteInfo, url, frameID, pageID, cookieString);
+
+    WebProcess::singleton().ensureNetworkProcessConnection().connection().send(Messages::NetworkConnectionToWebProcess::SetCookiesFromDOM(document.firstPartyForCookies(), sameSiteInfo, url, frameID, pageID, shouldAskITPInNetworkProcess, cookieString), 0);
 }
 
+void WebCookieJar::cookiesAdded(const String& host, const Vector<WebCore::Cookie>& cookies)
+{
+    m_cache.cookiesAdded(host, cookies);
+}
+
+void WebCookieJar::cookiesDeleted()
+{
+    m_cache.cookiesDeleted();
+}
+
+void WebCookieJar::clearCache()
+{
+    m_cache.clear();
+}
+
+void WebCookieJar::clearCacheForHost(const String& host)
+{
+    m_cache.clearForHost(host);
+}
+
 bool WebCookieJar::cookiesEnabled(const Document& document) const
 {
     auto* webFrame = document.frame() ? WebFrame::fromCoreFrame(*document.frame()) : nullptr;

Modified: trunk/Source/WebKit/WebProcess/WebPage/WebCookieJar.h (256819 => 256820)


--- trunk/Source/WebKit/WebProcess/WebPage/WebCookieJar.h	2020-02-18 06:10:16 UTC (rev 256819)
+++ trunk/Source/WebKit/WebProcess/WebPage/WebCookieJar.h	2020-02-18 06:32:42 UTC (rev 256820)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2019 Apple Inc. All rights reserved.
+ * Copyright (C) 2019-2020 Apple Inc. All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
@@ -25,10 +25,17 @@
 
 #pragma once
 
+#include "WebCookieCache.h"
 #include <WebCore/CookieJar.h>
 
+namespace WebCore {
+struct Cookie;
+}
+
 namespace WebKit {
 
+class WebFrame;
+
 class WebCookieJar final : public WebCore::CookieJar {
 public:
     static Ref<WebCookieJar> create() { return adoptRef(*new WebCookieJar); }
@@ -39,8 +46,18 @@
     std::pair<String, WebCore::SecureCookiesAccessed> cookieRequestHeaderFieldValue(const URL& firstParty, const WebCore::SameSiteInfo&, const URL&, Optional<WebCore::FrameIdentifier>, Optional<WebCore::PageIdentifier>, WebCore::IncludeSecureCookies) const final;
     bool getRawCookies(const WebCore::Document&, const URL&, Vector<WebCore::Cookie>&) const final;
     void deleteCookie(const WebCore::Document&, const URL&, const String& cookieName) final;
+
+    void cookiesAdded(const String& host, const Vector<WebCore::Cookie>&);
+    void cookiesDeleted();
+
 private:
     WebCookieJar();
+
+    void clearCache() final;
+    void clearCacheForHost(const String&) final;
+    bool isEligibleForCache(WebFrame&, const URL& firstPartyForCookies, const URL& resourceURL) const;
+
+    mutable WebCookieCache m_cache;
 };
 
 } // namespace WebKit

Modified: trunk/Source/WebKit/WebProcess/WebPage/WebPage.cpp (256819 => 256820)


--- trunk/Source/WebKit/WebProcess/WebPage/WebPage.cpp	2020-02-18 06:10:16 UTC (rev 256819)
+++ trunk/Source/WebKit/WebProcess/WebPage/WebPage.cpp	2020-02-18 06:32:42 UTC (rev 256820)
@@ -466,7 +466,7 @@
         makeUniqueRef<WebKit::LibWebRTCProvider>(),
         WebProcess::singleton().cacheStorageProvider(),
         WebBackForwardListProxy::create(*this),
-        WebCookieJar::create(),
+        WebProcess::singleton().cookieJar(),
         makeUniqueRef<WebProgressTrackerClient>(*this),
         makeUniqueRef<MediaRecorderProvider>()
     );

Modified: trunk/Source/WebKit/WebProcess/WebProcess.cpp (256819 => 256820)


--- trunk/Source/WebKit/WebProcess/WebProcess.cpp	2020-02-18 06:10:16 UTC (rev 256819)
+++ trunk/Source/WebKit/WebProcess/WebProcess.cpp	2020-02-18 06:32:42 UTC (rev 256820)
@@ -48,6 +48,7 @@
 #include "WebAutomationSessionProxy.h"
 #include "WebCacheStorageProvider.h"
 #include "WebConnectionToUIProcess.h"
+#include "WebCookieJar.h"
 #include "WebCoreArgumentCoders.h"
 #include "WebFrame.h"
 #include "WebFrameNetworkingContext.h"
@@ -205,6 +206,7 @@
     , m_webInspectorInterruptDispatcher(WebInspectorInterruptDispatcher::create())
     , m_webLoaderStrategy(*new WebLoaderStrategy)
     , m_cacheStorageProvider(WebCacheStorageProvider::create())
+    , m_cookieJar(WebCookieJar::create())
     , m_dnsPrefetchHystereris([this](PAL::HysteresisState state) { if (state == PAL::HysteresisState::Stopped) m_dnsPrefetchedHosts.clear(); })
 #if ENABLE(NETSCAPE_PLUGIN_API)
     , m_pluginProcessConnectionManager(PluginProcessConnectionManager::create())

Modified: trunk/Source/WebKit/WebProcess/WebProcess.h (256819 => 256820)


--- trunk/Source/WebKit/WebProcess/WebProcess.h	2020-02-18 06:10:16 UTC (rev 256819)
+++ trunk/Source/WebKit/WebProcess/WebProcess.h	2020-02-18 06:32:42 UTC (rev 256820)
@@ -120,6 +120,7 @@
 class WaylandCompositorDisplay;
 class WebAutomationSessionProxy;
 class WebCacheStorageProvider;
+class WebCookieJar;
 class WebCompiledContentRuleListData;
 class WebConnectionToUIProcess;
 class WebFrame;
@@ -293,6 +294,7 @@
     WebAutomationSessionProxy* automationSessionProxy() { return m_automationSessionProxy.get(); }
 
     WebCacheStorageProvider& cacheStorageProvider() { return m_cacheStorageProvider.get(); }
+    WebCookieJar& cookieJar() { return m_cookieJar.get(); }
     WebSocketChannelManager& webSocketChannelManager() { return m_webSocketChannelManager; }
 
 #if PLATFORM(IOS_FAMILY)
@@ -558,6 +560,7 @@
 #endif
 
     Ref<WebCacheStorageProvider> m_cacheStorageProvider;
+    Ref<WebCookieJar> m_cookieJar;
     WebSocketChannelManager m_webSocketChannelManager;
 
     std::unique_ptr<LibWebRTCNetwork> m_libWebRTCNetwork;

Modified: trunk/Tools/TestWebKitAPI/Tests/WebKitCocoa/CookieAcceptPolicy.mm (256819 => 256820)


--- trunk/Tools/TestWebKitAPI/Tests/WebKitCocoa/CookieAcceptPolicy.mm	2020-02-18 06:10:16 UTC (rev 256819)
+++ trunk/Tools/TestWebKitAPI/Tests/WebKitCocoa/CookieAcceptPolicy.mm	2020-02-18 06:32:42 UTC (rev 256820)
@@ -33,6 +33,10 @@
 #import <pal/spi/cf/CFNetworkSPI.h>
 #import <wtf/RetainPtr.h>
 
+// FIXME: This test is causing flakiness in API tests. It sets the cookie accept policy to 'Never'
+// and following tests often are unable to set cookies.
+#if !PLATFORM(IOS_FAMILY)
+
 static bool receivedScriptMessage = false;
 static RetainPtr<WKScriptMessage> lastScriptMessage;
 
@@ -72,3 +76,5 @@
     [[NSHTTPCookieStorage sharedHTTPCookieStorage] setCookieAcceptPolicy:originalCookieAcceptPolicy];
     [[NSHTTPCookieStorage sharedHTTPCookieStorage] _saveCookies];
 }
+
+#endif // !PLATFORM(IOS_FAMILY)

Modified: trunk/Tools/TestWebKitAPI/Tests/WebKitCocoa/CookiePrivateBrowsing.mm (256819 => 256820)


--- trunk/Tools/TestWebKitAPI/Tests/WebKitCocoa/CookiePrivateBrowsing.mm	2020-02-18 06:10:16 UTC (rev 256819)
+++ trunk/Tools/TestWebKitAPI/Tests/WebKitCocoa/CookiePrivateBrowsing.mm	2020-02-18 06:32:42 UTC (rev 256820)
@@ -26,11 +26,14 @@
 #include "config.h"
 
 #import "PlatformUtilities.h"
+#import "Test.h"
+#import "TestWKWebView.h"
 #import <WebKit/WKProcessPool.h>
 #import <WebKit/WKProcessPoolPrivate.h>
 #import <WebKit/WKWebView.h>
 #import <WebKit/WKWebViewConfiguration.h>
 #import <wtf/RetainPtr.h>
+#import <wtf/text/WTFString.h>
 
 static bool receivedAlert;
 
@@ -68,3 +71,60 @@
     [view2 loadHTMLString:alertOldCookie baseURL:[NSURL URLWithString:@"http://example.com/"]];
     TestWebKitAPI::Util::run(&receivedAlert);
 }
+
+TEST(WebKit, CookieCacheSyncAcrossProcess)
+{
+    auto configuration = adoptNS([[WKWebViewConfiguration alloc] init]);
+    [configuration setWebsiteDataStore:[WKWebsiteDataStore nonPersistentDataStore]];
+    auto view1 = adoptNS([[TestWKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:configuration.get()]);
+    auto view2 = adoptNS([[TestWKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:configuration.get()]);
+    [view1 synchronouslyLoadHTMLString:@"foo" baseURL:[NSURL URLWithString:@"http://example.com/"]];
+    [view2 synchronouslyLoadHTMLString:@"bar" baseURL:[NSURL URLWithString:@"http://example.com/"]];
+
+    // Cache DOM cookies in first WebView.
+    __block bool doneEvaluatingJavaScript = false;
+    [view1 evaluateJavaScript:@"document.cookie;" completionHandler:^(id _Nullable cookie, NSError * _Nullable error) {
+        EXPECT_NULL(error);
+        EXPECT_TRUE([cookie isKindOfClass:[NSString class]]);
+        EXPECT_WK_STREQ("", (NSString *)cookie);
+        doneEvaluatingJavaScript = true;
+    }];
+    TestWebKitAPI::Util::run(&doneEvaluatingJavaScript);
+
+    // Cache DOM cookies in second WebView.
+    doneEvaluatingJavaScript = false;
+    [view2 evaluateJavaScript:@"document.cookie;" completionHandler:^(id _Nullable cookie, NSError * _Nullable error) {
+        EXPECT_NULL(error);
+        EXPECT_TRUE([cookie isKindOfClass:[NSString class]]);
+        EXPECT_WK_STREQ("", (NSString *)cookie);
+        doneEvaluatingJavaScript = true;
+    }];
+    TestWebKitAPI::Util::run(&doneEvaluatingJavaScript);
+
+    // Setting cookie in first Webview / process.
+    doneEvaluatingJavaScript = false;
+    [view1 evaluateJavaScript:@"document.cookie = 'foo=bar'; document.cookie;" completionHandler:^(id _Nullable cookie, NSError * _Nullable error) {
+        EXPECT_NULL(error);
+        EXPECT_TRUE([cookie isKindOfClass:[NSString class]]);
+        EXPECT_WK_STREQ("foo=bar", (NSString *)cookie);
+        doneEvaluatingJavaScript = true;
+    }];
+    TestWebKitAPI::Util::run(&doneEvaluatingJavaScript);
+
+    // Making sure new cookie gets sync'd to second WebView process.
+    int timeout = 0;
+    __block String cookieString;
+    do {
+        TestWebKitAPI::Util::sleep(0.1);
+        doneEvaluatingJavaScript = false;
+        [view2 evaluateJavaScript:@"document.cookie;" completionHandler:^(id _Nullable cookie, NSError * _Nullable error) {
+            EXPECT_NULL(error);
+            EXPECT_TRUE([cookie isKindOfClass:[NSString class]]);
+            cookieString = (NSString *)cookie;
+            doneEvaluatingJavaScript = true;
+        }];
+        TestWebKitAPI::Util::run(&doneEvaluatingJavaScript);
+        ++timeout;
+    } while (cookieString != "" && timeout < 50);
+    EXPECT_WK_STREQ("foo=bar", cookieString);
+}
_______________________________________________
webkit-changes mailing list
[email protected]
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to