Implement Same-Site cookies
authordbates@webkit.org <dbates@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Mon, 23 Apr 2018 20:58:35 +0000 (20:58 +0000)
committerdbates@webkit.org <dbates@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Mon, 23 Apr 2018 20:58:35 +0000 (20:58 +0000)
https://bugs.webkit.org/show_bug.cgi?id=159464
<rdar://problem/27196358>

Reviewed by Brent Fulgham.

Source/WebCore:

Implements support for Same-Site cookies as per <https://tools.ietf.org/html/draft-ietf-httpbis-cookie-same-site-00>.
The implementation is materially consistent with the spec. though implements the computation
for a document's "site for cookies" indirectly as part of loading its frame. This is done to
avoid traversing the frame tree on each subresource request initiated by the document or one
of its workers. We take advantage of the fact that Web Workers and Service Workers use their
host document's loader to load resources on their behalf to use the correct "site for cookies"
for requests (e.g. fetch()) initiating by them without the need to duplicate and store the
host document's "site for cookies" in the worker's script execution context.

The implementation differs from the spec. in the handling of about: URLs and the empty URL
and makes the implementation in WebKit match the behavior of Chrome and Firefox as well as
consistent with origin inheritance as described in <https://html.spec.whatwg.org/multipage/browsers.html#origin>
(16 April 2018). Specifically, requests to about:blank, about:srcdoc and the empty URL ("")
are treated as same-site because these URLs inherit their origin from their owner.

Tests: http/tests/cookies/same-site/fetch-after-navigating-iframe-in-cross-origin-page.html
       http/tests/cookies/same-site/fetch-after-top-level-navigation-from-cross-origin-page.html
       http/tests/cookies/same-site/fetch-after-top-level-navigation-initiated-from-iframe-in-cross-origin-page.html
       http/tests/cookies/same-site/fetch-cookies-set-in-about-blank-iframe.html
       http/tests/cookies/same-site/fetch-in-about-blank-page.html
       http/tests/cookies/same-site/fetch-in-cross-origin-iframe.html
       http/tests/cookies/same-site/fetch-in-cross-origin-page.html
       http/tests/cookies/same-site/fetch-in-cross-origin-service-worker.html
       http/tests/cookies/same-site/fetch-in-cross-origin-worker.html
       http/tests/cookies/same-site/fetch-in-same-origin-page.html
       http/tests/cookies/same-site/fetch-in-same-origin-service-worker.html
       http/tests/cookies/same-site/fetch-in-same-origin-srcdoc-iframe.html
       http/tests/cookies/same-site/fetch-in-same-origin-worker.html
       http/tests/cookies/same-site/popup-cross-site-post.html
       http/tests/cookies/same-site/popup-cross-site.html
       http/tests/cookies/same-site/popup-same-site-post.html
       http/tests/cookies/same-site/popup-same-site-via-cross-site-redirect.html
       http/tests/cookies/same-site/popup-same-site-via-same-site-redirect.html
       http/tests/cookies/same-site/popup-same-site.html

* Sources.txt: Add source file SameSiteInfo.cpp.
* WebCore.xcodeproj/project.pbxproj: Add source files SameSiteInfo.{cpp, h}.
* dom/Document.cpp:
(WebCore::Document::initSecurityContext): Modified to call SecurityPolicy::shouldInheritSecurityOriginFromOwner().
(WebCore::Document::shouldInheritContentSecurityPolicyFromOwner const): Ditto.
(WebCore::shouldInheritSecurityOriginFromOwner): Deleted; moved to SecurityPolicy.
* dom/Document.h:
(WebCore::Document::firstPartyForSameSiteCookies const): Added.
(WebCore::Document::setFirstPartyForSameSiteCookies): Added.
* loader/CookieJar.cpp:
(WebCore::sameSiteInfo): Returns the same-site info for the request used to load the specified document.
(WebCore::cookies): Pass the same-site info down to the platform.
(WebCore::cookieRequestHeaderFieldProxy): Ditto.
(WebCore::setCookies): Ditto.
(WebCore::cookieRequestHeaderFieldValue): Ditto.
(WebCore::getRawCookies): Ditto.
* loader/DocumentLoader.cpp:
(WebCore::DocumentLoader::willSendRequest): Add same-site info to the request.
(WebCore::DocumentLoader::startLoadingMainResource): Update a FIXME comment to explain that
we can simplify ResourceRequestBase if we can remove the call to addExtraFieldsToMainResourceRequest()
here. Specifically, we would not need to differentiate between a request with an unspecified
same-site state (default state of a new request) from a request whose same-site state has
been explicitly set if we can assume that the same-site state of a request is set exactly
once. In absence of this guarantee we need an "unspecified" state to avoid overriding existing
same-site information computed with a null initiating document (the case of a new address bar
initiated load) from a load initiated by the document associated with this loader.
* loader/FrameLoader.cpp:
(WebCore::FrameLoader::setFirstPartyForCookies): Modified to also update the first party for
same-site cookies ("site for cookies").
(WebCore::FrameLoader::load): Add same-site info to the request.
(WebCore::FrameLoader::reload): Ditto.
(WebCore::FrameLoader::setOriginalURLForDownloadRequest): Ditto.
(WebCore::FrameLoader::addExtraFieldsToRequest): If the request does not already have
same-site info then compute it and add it to the request. Mark main frame main resource
requests as a "top-site".
(WebCore::FrameLoader::addSameSiteInfoToRequestIfNeeded): Implements the "'Same-site' and 'cross-site'
Requests" algorithm from <https://tools.ietf.org/html/draft-ietf-httpbis-cookie-same-site-00#section-2.1>.
(WebCore::createWindow): Add same-site info to the request.
* loader/FrameLoader.h:
* loader/ResourceLoader.cpp:
(WebCore::ResourceLoader::init): Ditto.
* page/DOMWindow.cpp:
(WebCore::DOMWindow::createWindow): Ditto.
* page/SecurityPolicy.cpp:
(WebCore::SecurityPolicy::shouldInheritSecurityOriginFromOwner): Moved from Document.cpp.
* page/SecurityPolicy.h:
* platform/CookiesStrategy.h:
* platform/network/CacheValidation.cpp:
(WebCore::headerValueForVary): Pass the same-site info down to the platform.
* platform/network/CookieRequestHeaderFieldProxy.h:
(WebCore::CookieRequestHeaderFieldProxy::encode const): Encode same-site bits.
(WebCore::CookieRequestHeaderFieldProxy::decode): Decode same-site bits.
* platform/network/PlatformCookieJar.h:
* platform/network/ResourceRequestBase.cpp:
(WebCore::ResourceRequestBase::setAsIsolatedCopy): Added.
(WebCore::ResourceRequestBase::isSameSite const): Added.
(WebCore::ResourceRequestBase::setIsSameSite): Added.
(WebCore::ResourceRequestBase::isTopSite const): Added.
(WebCore::ResourceRequestBase::setIsTopSite): Added.
(WebCore::equalIgnoringHeaderFields):
* platform/network/ResourceRequestBase.h:
(WebCore::ResourceRequestBase::isSameSiteUnspecified const): Added. See comment for DocumentLoader::startLoadingMainResource()
for more details.
(WebCore::registrableDomainsAreEqual): Added.
(WebCore::ResourceRequestBase::encodeBase const): Encode same-site bits.
(WebCore::ResourceRequestBase::decodeBase): Decode same-site bits.
* platform/network/SameSiteInfo.cpp: Added.
(WebCore::SameSiteInfo::create):
* platform/network/SameSiteInfo.h: Added.
(WebCore::SameSiteInfo::encode const):
(WebCore::SameSiteInfo::decode):
* platform/network/cf/CookieJarCFNet.cpp:
(WebCore::setCookiesFromDOM): Pass Same-Site info down.
(WebCore::cookiesForDOM): Ditto.
(WebCore::cookieRequestHeaderFieldValue): Ditto.
(WebCore::getRawCookies): Ditto.
* platform/network/cf/ResourceRequestCFNet.cpp:
(WebCore::siteForCookies): Added.
(WebCore::ResourceRequest::doUpdatePlatformRequest): Update platform request with same-site info.
(WebCore::ResourceRequest::doUpdateResourceRequest): Ditto.
* platform/network/cocoa/ResourceRequestCocoa.mm:
(WebCore::ResourceRequest::doUpdateResourceRequest): Ditto.
(WebCore::siteForCookies): Added.
(WebCore::ResourceRequest::doUpdatePlatformRequest): Update platform request with same-site info.
* platform/network/curl/CookieJarCurl.cpp:
(WebCore::cookiesForDOM): Pass Same-Site info down.
(WebCore::setCookiesFromDOM): Ditto.
(WebCore::cookieRequestHeaderFieldValue): Ditto.
(WebCore::getRawCookies): Ditto.
* platform/network/curl/CookieJarCurl.h:
* platform/network/curl/CookieJarCurlDatabase.cpp:
(WebCore::CookieJarCurlDatabase::setCookiesFromDOM const): Ditto.
(WebCore::CookieJarCurlDatabase::cookiesForDOM const): Ditto.
(WebCore::CookieJarCurlDatabase::cookieRequestHeaderFieldValue const): Ditto.
(WebCore::CookieJarCurlDatabase::getRawCookies const): Ditto.
* platform/network/curl/CookieJarCurlDatabase.h:
* platform/network/curl/ResourceHandleCurl.cpp:
(WebCore::ResourceHandle::createCurlRequest): Ditto.
* platform/network/mac/CookieJarMac.mm:
(WebCore::cookiesForURL): Added; shared function to return the cookies based on the specified criterion.
(WebCore::setHTTPCookiesForURL): Moved from the bottom of the file to top to be closer to the other
CFNetwork helper functions. Modified to support fetching same-site cookies.
(WebCore::httpCookiesForURL): Moved to be under setHTTPCookiesForURL(). Modified to call cookiesForURL().
Note the SPI used in cookiesForURL() apply the same criterion for whether to fetch secure cookies as we
were computing here. That is, the CFNetwork SPI only returns secure cookies if the specified URL's scheme
case-insensitively matches "https".
(WebCore::cookiesInPartitionForURL): Wrote in terms of cookiesForURL().
(WebCore::cookiesForSession): Pass the Same-Site info.
(WebCore::cookiesForDOM): Ditto.
(WebCore::cookieRequestHeaderFieldValue): Ditto.
(WebCore::setCookiesFromDOM): Ditto.
(WebCore::getRawCookies): Ditto.
(WebCore::deleteCookie): Pass std::nullopt for the Same-Site info so that we do not consider the SameSite
attribute when fetching cookies to delete.
* platform/network/soup/CookieJarSoup.cpp:
(WebCore::setCookiesFromDOM): Pass the Same-Site info.
(WebCore::cookiesForDOM): Ditto.
(WebCore::cookieRequestHeaderFieldValue): Ditto.
(WebCore::getRawCookies): Ditto.
* workers/service/context/ServiceWorkerThreadProxy.cpp:
(WebCore::createPageForServiceWorker): Set the first party for same site cookies ("site for cookies") to
the script URL.
* xml/XSLTProcessor.cpp:
(WebCore::XSLTProcessor::createDocumentFromSource): Copy the first party for same-site cookies to the
new document.

Source/WebCore/PAL:

Forward declare some SPI.

* pal/spi/cf/CFNetworkSPI.h:

Source/WebKit:

Pass the Same-Site info through the WebKit abstractions.

* NetworkProcess/NetworkConnectionToWebProcess.cpp:
(WebKit::NetworkConnectionToWebProcess::cookiesForDOM):
(WebKit::NetworkConnectionToWebProcess::setCookiesFromDOM):
(WebKit::NetworkConnectionToWebProcess::cookieRequestHeaderFieldValue):
(WebKit::NetworkConnectionToWebProcess::getRawCookies):
* NetworkProcess/NetworkConnectionToWebProcess.h:
* NetworkProcess/NetworkConnectionToWebProcess.messages.in:
* NetworkProcess/NetworkResourceLoader.cpp:
(WebKit::NetworkResourceLoader::logCookieInformation const):
(WebKit::logBlockedCookieInformation):
(logCookieInformationInternal):
(NetworkResourceLoader::logCookieInformation):
* NetworkProcess/NetworkResourceLoader.h:
* NetworkProcess/cache/NetworkCacheSpeculativeLoadManager.cpp:
(WebKit::NetworkCache::constructRevalidationRequest):
* NetworkProcess/cache/NetworkCacheSubresourcesEntry.cpp:
(WebKit::NetworkCache::SubresourceInfo::encode const):
(WebKit::NetworkCache::SubresourceInfo::decode):
(WebKit::NetworkCache::SubresourceInfo::SubresourceInfo):
* NetworkProcess/cache/NetworkCacheSubresourcesEntry.h:
(WebKit::NetworkCache::SubresourceInfo::isSameSite const):
(WebKit::NetworkCache::SubresourceInfo::isTopSite const): Returns false; subresources do not represent
a top-level navigation.
* NetworkProcess/cocoa/NetworkDataTaskCocoa.mm:
(WebKit::NetworkDataTaskCocoa::isThirdPartyRequest):
(WebKit::updateTaskWithFirstPartyForSameSiteCookies):
(WebKit::NetworkDataTaskCocoa::NetworkDataTaskCocoa):
(WebKit::NetworkDataTaskCocoa::willPerformHTTPRedirection):
* UIProcess/WebProcessPool.cpp:
(WebKit::WebProcessPool::download):
* WebProcess/WebCoreSupport/WebPlatformStrategies.cpp:
(WebKit::WebPlatformStrategies::cookiesForDOM):
(WebKit::WebPlatformStrategies::setCookiesFromDOM):
(WebKit::WebPlatformStrategies::cookieRequestHeaderFieldValue):
(WebKit::WebPlatformStrategies::getRawCookies):
* WebProcess/WebCoreSupport/WebPlatformStrategies.h:

Source/WebKitLegacy/mac:

Pass the Same-Site info through the strategy.

* WebCoreSupport/WebPlatformStrategies.h:
* WebCoreSupport/WebPlatformStrategies.mm:
(WebPlatformStrategies::cookiesForDOM):
(WebPlatformStrategies::setCookiesFromDOM):
(WebPlatformStrategies::cookieRequestHeaderFieldValue):
(WebPlatformStrategies::getRawCookies):

Source/WebKitLegacy/win:

Pass the Same-Site info through the strategy.

* WebCoreSupport/WebPlatformStrategies.cpp:
(WebPlatformStrategies::cookiesForDOM):
(WebPlatformStrategies::setCookiesFromDOM):
(WebPlatformStrategies::cookieRequestHeaderFieldValue):
(WebPlatformStrategies::getRawCookies):
* WebCoreSupport/WebPlatformStrategies.h:

LayoutTests:

Add tests. These tests are skipped on all ports for now, including Mac and iOS. We will
look to subsequently enable the tests for Mac and iOS once we have CFNetwork support for
Same-Site cookies.

The following tests and utilities were taken in whole or in part from Blink:
    http/tests/cookies/resources/echo-json.php
    http/tests/cookies/resources/post-cookies-onmessage.php
    http/tests/cookies/resources/post-cookies-to-opener.php
    http/tests/cookies/resources/testharness-helpers.js
    http/tests/cookies/same-site/popup-cross-site-post.html
    http/tests/cookies/same-site/popup-cross-site.html
    http/tests/cookies/same-site/popup-same-site-post.html
    http/tests/cookies/same-site/popup-same-site.html

The following files were derived from tests taken from Blink:
    http/tests/cookies/same-site/popup-same-site-via-cross-site-redirect.html
    http/tests/cookies/same-site/popup-same-site-via-same-site-redirect.html

* TestExpectations: Skip on all ports for now.
* http/tests/cookies/resources/cookie-utilities.js: Added.
(createCookie):
(setBaseDocumentWhenFetchingDOMCookies):
(setDOMCookie):
* http/tests/cookies/resources/cookie-utility.php:
* http/tests/cookies/resources/echo-json.php: Added.
* http/tests/cookies/resources/post-cookies-onmessage.php: Added.
* http/tests/cookies/resources/post-cookies-to-opener.php: Added.
* http/tests/cookies/resources/testharness-helpers.js: Added.
(clearKnownCookies):
* http/tests/cookies/same-site/fetch-after-navigating-iframe-in-cross-origin-page-expected.txt: Added.
* http/tests/cookies/same-site/fetch-after-navigating-iframe-in-cross-origin-page.html: Added.
* http/tests/cookies/same-site/fetch-after-top-level-navigation-from-cross-origin-page-expected.txt: Added.
* http/tests/cookies/same-site/fetch-after-top-level-navigation-from-cross-origin-page.html: Added.
* http/tests/cookies/same-site/fetch-after-top-level-navigation-initiated-from-iframe-in-cross-origin-page-expected.txt: Added.
* http/tests/cookies/same-site/fetch-after-top-level-navigation-initiated-from-iframe-in-cross-origin-page.html: Added.
* http/tests/cookies/same-site/fetch-cookies-set-in-about-blank-iframe-expected.txt: Added.
* http/tests/cookies/same-site/fetch-cookies-set-in-about-blank-iframe.html: Added.
* http/tests/cookies/same-site/fetch-in-about-blank-page-expected.txt: Added.
* http/tests/cookies/same-site/fetch-in-about-blank-page.html: Added.
* http/tests/cookies/same-site/fetch-in-cross-origin-iframe-expected.txt: Added.
* http/tests/cookies/same-site/fetch-in-cross-origin-iframe.html: Added.
* http/tests/cookies/same-site/fetch-in-cross-origin-page-expected.txt: Added.
* http/tests/cookies/same-site/fetch-in-cross-origin-page.html: Added.
* http/tests/cookies/same-site/fetch-in-cross-origin-service-worker-expected.txt: Added.
* http/tests/cookies/same-site/fetch-in-cross-origin-service-worker.html: Added.
* http/tests/cookies/same-site/fetch-in-cross-origin-worker-expected.txt: Added.
* http/tests/cookies/same-site/fetch-in-cross-origin-worker.html: Added.
* http/tests/cookies/same-site/fetch-in-same-origin-page-expected.txt: Added.
* http/tests/cookies/same-site/fetch-in-same-origin-page.html: Added.
* http/tests/cookies/same-site/fetch-in-same-origin-service-worker-expected.txt: Added.
* http/tests/cookies/same-site/fetch-in-same-origin-service-worker.html: Added.
* http/tests/cookies/same-site/fetch-in-same-origin-srcdoc-iframe-expected.txt: Added.
* http/tests/cookies/same-site/fetch-in-same-origin-srcdoc-iframe.html: Added.
* http/tests/cookies/same-site/fetch-in-same-origin-worker-expected.txt: Added.
* http/tests/cookies/same-site/fetch-in-same-origin-worker.html: Added.
* http/tests/cookies/same-site/popup-cross-site-expected.txt: Added.
* http/tests/cookies/same-site/popup-cross-site-post-expected.txt: Added.
* http/tests/cookies/same-site/popup-cross-site-post.html: Added.
* http/tests/cookies/same-site/popup-cross-site.html: Added.
* http/tests/cookies/same-site/popup-same-site-expected.txt: Added.
* http/tests/cookies/same-site/popup-same-site-post-expected.txt: Added.
* http/tests/cookies/same-site/popup-same-site-post.html: Added.
* http/tests/cookies/same-site/popup-same-site-via-cross-site-redirect-expected.txt: Added.
* http/tests/cookies/same-site/popup-same-site-via-cross-site-redirect.html: Added.
* http/tests/cookies/same-site/popup-same-site-via-same-site-redirect-expected.txt: Added.
* http/tests/cookies/same-site/popup-same-site-via-same-site-redirect.html: Added.
* http/tests/cookies/same-site/popup-same-site.html: Added.
* http/tests/cookies/same-site/resources/click-hyperlink.php: Added.
* http/tests/cookies/same-site/resources/echo-iframe-src.php: Added.
* http/tests/cookies/same-site/resources/fetch-after-navigating-iframe-in-cross-origin-page.php: Added.
* http/tests/cookies/same-site/resources/fetch-after-top-level-navigation-from-cross-origin-page.php: Added.
* http/tests/cookies/same-site/resources/fetch-after-top-level-navigation-initiated-from-iframe-in-cross-origin-page.php: Added.
* http/tests/cookies/same-site/resources/fetch-in-cross-origin-iframe.html: Added.
* http/tests/cookies/same-site/resources/fetch-in-cross-origin-service-worker.html: Added.
* http/tests/cookies/same-site/resources/fetch-in-cross-origin-worker.js: Added.
(async.checkResult):
* http/tests/cookies/same-site/resources/fetch-in-same-origin-service-worker.php: Added.
* http/tests/cookies/same-site/resources/fetch-in-same-origin-worker.js: Added.
(async.checkResult):
* http/tests/cookies/same-site/resources/passthrough-service-worker.js: Added.
* platform/mac-wk1/TestExpectations: Skip the Service Worker tests as they are not supported in LegacyWebKit.

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

112 files changed:
LayoutTests/ChangeLog
LayoutTests/TestExpectations
LayoutTests/http/tests/cookies/resources/cookie-utilities.js [new file with mode: 0644]
LayoutTests/http/tests/cookies/resources/cookie-utility.php
LayoutTests/http/tests/cookies/resources/echo-json.php [new file with mode: 0644]
LayoutTests/http/tests/cookies/resources/post-cookies-onmessage.php [new file with mode: 0644]
LayoutTests/http/tests/cookies/resources/post-cookies-to-opener.php [new file with mode: 0644]
LayoutTests/http/tests/cookies/resources/testharness-helpers.js [new file with mode: 0644]
LayoutTests/http/tests/cookies/same-site/fetch-after-navigating-iframe-in-cross-origin-page-expected.txt [new file with mode: 0644]
LayoutTests/http/tests/cookies/same-site/fetch-after-navigating-iframe-in-cross-origin-page.html [new file with mode: 0644]
LayoutTests/http/tests/cookies/same-site/fetch-after-top-level-navigation-from-cross-origin-page-expected.txt [new file with mode: 0644]
LayoutTests/http/tests/cookies/same-site/fetch-after-top-level-navigation-from-cross-origin-page.html [new file with mode: 0644]
LayoutTests/http/tests/cookies/same-site/fetch-after-top-level-navigation-initiated-from-iframe-in-cross-origin-page-expected.txt [new file with mode: 0644]
LayoutTests/http/tests/cookies/same-site/fetch-after-top-level-navigation-initiated-from-iframe-in-cross-origin-page.html [new file with mode: 0644]
LayoutTests/http/tests/cookies/same-site/fetch-cookies-set-in-about-blank-iframe-expected.txt [new file with mode: 0644]
LayoutTests/http/tests/cookies/same-site/fetch-cookies-set-in-about-blank-iframe.html [new file with mode: 0644]
LayoutTests/http/tests/cookies/same-site/fetch-in-about-blank-page-expected.txt [new file with mode: 0644]
LayoutTests/http/tests/cookies/same-site/fetch-in-about-blank-page.html [new file with mode: 0644]
LayoutTests/http/tests/cookies/same-site/fetch-in-cross-origin-iframe-expected.txt [new file with mode: 0644]
LayoutTests/http/tests/cookies/same-site/fetch-in-cross-origin-iframe.html [new file with mode: 0644]
LayoutTests/http/tests/cookies/same-site/fetch-in-cross-origin-page-expected.txt [new file with mode: 0644]
LayoutTests/http/tests/cookies/same-site/fetch-in-cross-origin-page.html [new file with mode: 0644]
LayoutTests/http/tests/cookies/same-site/fetch-in-cross-origin-service-worker-expected.txt [new file with mode: 0644]
LayoutTests/http/tests/cookies/same-site/fetch-in-cross-origin-service-worker.html [new file with mode: 0644]
LayoutTests/http/tests/cookies/same-site/fetch-in-cross-origin-worker-expected.txt [new file with mode: 0644]
LayoutTests/http/tests/cookies/same-site/fetch-in-cross-origin-worker.html [new file with mode: 0644]
LayoutTests/http/tests/cookies/same-site/fetch-in-same-origin-page-expected.txt [new file with mode: 0644]
LayoutTests/http/tests/cookies/same-site/fetch-in-same-origin-page.html [new file with mode: 0644]
LayoutTests/http/tests/cookies/same-site/fetch-in-same-origin-service-worker-expected.txt [new file with mode: 0644]
LayoutTests/http/tests/cookies/same-site/fetch-in-same-origin-service-worker.html [new file with mode: 0644]
LayoutTests/http/tests/cookies/same-site/fetch-in-same-origin-srcdoc-iframe-expected.txt [new file with mode: 0644]
LayoutTests/http/tests/cookies/same-site/fetch-in-same-origin-srcdoc-iframe.html [new file with mode: 0644]
LayoutTests/http/tests/cookies/same-site/fetch-in-same-origin-worker-expected.txt [new file with mode: 0644]
LayoutTests/http/tests/cookies/same-site/fetch-in-same-origin-worker.html [new file with mode: 0644]
LayoutTests/http/tests/cookies/same-site/popup-cross-site-expected.txt [new file with mode: 0644]
LayoutTests/http/tests/cookies/same-site/popup-cross-site-post-expected.txt [new file with mode: 0644]
LayoutTests/http/tests/cookies/same-site/popup-cross-site-post.html [new file with mode: 0644]
LayoutTests/http/tests/cookies/same-site/popup-cross-site.html [new file with mode: 0644]
LayoutTests/http/tests/cookies/same-site/popup-same-site-expected.txt [new file with mode: 0644]
LayoutTests/http/tests/cookies/same-site/popup-same-site-post-expected.txt [new file with mode: 0644]
LayoutTests/http/tests/cookies/same-site/popup-same-site-post.html [new file with mode: 0644]
LayoutTests/http/tests/cookies/same-site/popup-same-site-via-cross-site-redirect-expected.txt [new file with mode: 0644]
LayoutTests/http/tests/cookies/same-site/popup-same-site-via-cross-site-redirect.html [new file with mode: 0644]
LayoutTests/http/tests/cookies/same-site/popup-same-site-via-same-site-redirect-expected.txt [new file with mode: 0644]
LayoutTests/http/tests/cookies/same-site/popup-same-site-via-same-site-redirect.html [new file with mode: 0644]
LayoutTests/http/tests/cookies/same-site/popup-same-site.html [new file with mode: 0644]
LayoutTests/http/tests/cookies/same-site/resources/click-hyperlink.php [new file with mode: 0644]
LayoutTests/http/tests/cookies/same-site/resources/echo-iframe-src.php [new file with mode: 0644]
LayoutTests/http/tests/cookies/same-site/resources/fetch-after-navigating-iframe-in-cross-origin-page.php [new file with mode: 0644]
LayoutTests/http/tests/cookies/same-site/resources/fetch-after-top-level-navigation-from-cross-origin-page.php [new file with mode: 0644]
LayoutTests/http/tests/cookies/same-site/resources/fetch-after-top-level-navigation-initiated-from-iframe-in-cross-origin-page.php [new file with mode: 0644]
LayoutTests/http/tests/cookies/same-site/resources/fetch-in-cross-origin-iframe.html [new file with mode: 0644]
LayoutTests/http/tests/cookies/same-site/resources/fetch-in-cross-origin-service-worker.html [new file with mode: 0644]
LayoutTests/http/tests/cookies/same-site/resources/fetch-in-cross-origin-worker.js [new file with mode: 0644]
LayoutTests/http/tests/cookies/same-site/resources/fetch-in-same-origin-service-worker.php [new file with mode: 0644]
LayoutTests/http/tests/cookies/same-site/resources/fetch-in-same-origin-worker.js [new file with mode: 0644]
LayoutTests/http/tests/cookies/same-site/resources/passthrough-service-worker.js [new file with mode: 0644]
LayoutTests/platform/mac-wk1/TestExpectations
Source/WebCore/ChangeLog
Source/WebCore/PAL/ChangeLog
Source/WebCore/PAL/pal/spi/cf/CFNetworkSPI.h
Source/WebCore/Sources.txt
Source/WebCore/WebCore.xcodeproj/project.pbxproj
Source/WebCore/dom/Document.cpp
Source/WebCore/dom/Document.h
Source/WebCore/loader/CookieJar.cpp
Source/WebCore/loader/DocumentLoader.cpp
Source/WebCore/loader/FrameLoader.cpp
Source/WebCore/loader/FrameLoader.h
Source/WebCore/loader/ResourceLoader.cpp
Source/WebCore/page/DOMWindow.cpp
Source/WebCore/page/SecurityPolicy.cpp
Source/WebCore/page/SecurityPolicy.h
Source/WebCore/platform/CookiesStrategy.h
Source/WebCore/platform/network/CacheValidation.cpp
Source/WebCore/platform/network/CookieRequestHeaderFieldProxy.h
Source/WebCore/platform/network/PlatformCookieJar.h
Source/WebCore/platform/network/ResourceRequestBase.cpp
Source/WebCore/platform/network/ResourceRequestBase.h
Source/WebCore/platform/network/SameSiteInfo.cpp [new file with mode: 0644]
Source/WebCore/platform/network/SameSiteInfo.h [new file with mode: 0644]
Source/WebCore/platform/network/cf/CookieJarCFNet.cpp
Source/WebCore/platform/network/cf/ResourceRequestCFNet.cpp
Source/WebCore/platform/network/cocoa/ResourceRequestCocoa.mm
Source/WebCore/platform/network/curl/CookieJarCurl.cpp
Source/WebCore/platform/network/curl/CookieJarCurl.h
Source/WebCore/platform/network/curl/CookieJarCurlDatabase.cpp
Source/WebCore/platform/network/curl/CookieJarCurlDatabase.h
Source/WebCore/platform/network/curl/ResourceHandleCurl.cpp
Source/WebCore/platform/network/mac/CookieJarMac.mm
Source/WebCore/platform/network/soup/CookieJarSoup.cpp
Source/WebCore/workers/service/context/ServiceWorkerThreadProxy.cpp
Source/WebCore/xml/XSLTProcessor.cpp
Source/WebKit/ChangeLog
Source/WebKit/NetworkProcess/NetworkConnectionToWebProcess.cpp
Source/WebKit/NetworkProcess/NetworkConnectionToWebProcess.h
Source/WebKit/NetworkProcess/NetworkConnectionToWebProcess.messages.in
Source/WebKit/NetworkProcess/NetworkResourceLoader.cpp
Source/WebKit/NetworkProcess/NetworkResourceLoader.h
Source/WebKit/NetworkProcess/cache/NetworkCacheSpeculativeLoadManager.cpp
Source/WebKit/NetworkProcess/cache/NetworkCacheSubresourcesEntry.cpp
Source/WebKit/NetworkProcess/cache/NetworkCacheSubresourcesEntry.h
Source/WebKit/NetworkProcess/cocoa/NetworkDataTaskCocoa.mm
Source/WebKit/UIProcess/WebProcessPool.cpp
Source/WebKit/WebProcess/WebCoreSupport/WebPlatformStrategies.cpp
Source/WebKit/WebProcess/WebCoreSupport/WebPlatformStrategies.h
Source/WebKitLegacy/mac/ChangeLog
Source/WebKitLegacy/mac/WebCoreSupport/WebPlatformStrategies.h
Source/WebKitLegacy/mac/WebCoreSupport/WebPlatformStrategies.mm
Source/WebKitLegacy/win/ChangeLog
Source/WebKitLegacy/win/WebCoreSupport/WebPlatformStrategies.cpp
Source/WebKitLegacy/win/WebCoreSupport/WebPlatformStrategies.h

index a7cc961..646db36 100644 (file)
@@ -1,3 +1,93 @@
+2018-04-23  Daniel Bates  <dabates@apple.com>
+
+        Implement Same-Site cookies
+        https://bugs.webkit.org/show_bug.cgi?id=159464
+        <rdar://problem/27196358>
+
+        Reviewed by Brent Fulgham.
+
+        Add tests. These tests are skipped on all ports for now, including Mac and iOS. We will
+        look to subsequently enable the tests for Mac and iOS once we have CFNetwork support for
+        Same-Site cookies.
+
+        The following tests and utilities were taken in whole or in part from Blink:
+            http/tests/cookies/resources/echo-json.php
+            http/tests/cookies/resources/post-cookies-onmessage.php
+            http/tests/cookies/resources/post-cookies-to-opener.php
+            http/tests/cookies/resources/testharness-helpers.js
+            http/tests/cookies/same-site/popup-cross-site-post.html
+            http/tests/cookies/same-site/popup-cross-site.html
+            http/tests/cookies/same-site/popup-same-site-post.html
+            http/tests/cookies/same-site/popup-same-site.html
+
+        The following files were derived from tests taken from Blink:
+            http/tests/cookies/same-site/popup-same-site-via-cross-site-redirect.html
+            http/tests/cookies/same-site/popup-same-site-via-same-site-redirect.html
+
+        * TestExpectations: Skip on all ports for now.
+        * http/tests/cookies/resources/cookie-utilities.js: Added.
+        (createCookie):
+        (setBaseDocumentWhenFetchingDOMCookies):
+        (setDOMCookie):
+        * http/tests/cookies/resources/cookie-utility.php:
+        * http/tests/cookies/resources/echo-json.php: Added.
+        * http/tests/cookies/resources/post-cookies-onmessage.php: Added.
+        * http/tests/cookies/resources/post-cookies-to-opener.php: Added.
+        * http/tests/cookies/resources/testharness-helpers.js: Added.
+        (clearKnownCookies):
+        * http/tests/cookies/same-site/fetch-after-navigating-iframe-in-cross-origin-page-expected.txt: Added.
+        * http/tests/cookies/same-site/fetch-after-navigating-iframe-in-cross-origin-page.html: Added.
+        * http/tests/cookies/same-site/fetch-after-top-level-navigation-from-cross-origin-page-expected.txt: Added.
+        * http/tests/cookies/same-site/fetch-after-top-level-navigation-from-cross-origin-page.html: Added.
+        * http/tests/cookies/same-site/fetch-after-top-level-navigation-initiated-from-iframe-in-cross-origin-page-expected.txt: Added.
+        * http/tests/cookies/same-site/fetch-after-top-level-navigation-initiated-from-iframe-in-cross-origin-page.html: Added.
+        * http/tests/cookies/same-site/fetch-cookies-set-in-about-blank-iframe-expected.txt: Added.
+        * http/tests/cookies/same-site/fetch-cookies-set-in-about-blank-iframe.html: Added.
+        * http/tests/cookies/same-site/fetch-in-about-blank-page-expected.txt: Added.
+        * http/tests/cookies/same-site/fetch-in-about-blank-page.html: Added.
+        * http/tests/cookies/same-site/fetch-in-cross-origin-iframe-expected.txt: Added.
+        * http/tests/cookies/same-site/fetch-in-cross-origin-iframe.html: Added.
+        * http/tests/cookies/same-site/fetch-in-cross-origin-page-expected.txt: Added.
+        * http/tests/cookies/same-site/fetch-in-cross-origin-page.html: Added.
+        * http/tests/cookies/same-site/fetch-in-cross-origin-service-worker-expected.txt: Added.
+        * http/tests/cookies/same-site/fetch-in-cross-origin-service-worker.html: Added.
+        * http/tests/cookies/same-site/fetch-in-cross-origin-worker-expected.txt: Added.
+        * http/tests/cookies/same-site/fetch-in-cross-origin-worker.html: Added.
+        * http/tests/cookies/same-site/fetch-in-same-origin-page-expected.txt: Added.
+        * http/tests/cookies/same-site/fetch-in-same-origin-page.html: Added.
+        * http/tests/cookies/same-site/fetch-in-same-origin-service-worker-expected.txt: Added.
+        * http/tests/cookies/same-site/fetch-in-same-origin-service-worker.html: Added.
+        * http/tests/cookies/same-site/fetch-in-same-origin-srcdoc-iframe-expected.txt: Added.
+        * http/tests/cookies/same-site/fetch-in-same-origin-srcdoc-iframe.html: Added.
+        * http/tests/cookies/same-site/fetch-in-same-origin-worker-expected.txt: Added.
+        * http/tests/cookies/same-site/fetch-in-same-origin-worker.html: Added.
+        * http/tests/cookies/same-site/popup-cross-site-expected.txt: Added.
+        * http/tests/cookies/same-site/popup-cross-site-post-expected.txt: Added.
+        * http/tests/cookies/same-site/popup-cross-site-post.html: Added.
+        * http/tests/cookies/same-site/popup-cross-site.html: Added.
+        * http/tests/cookies/same-site/popup-same-site-expected.txt: Added.
+        * http/tests/cookies/same-site/popup-same-site-post-expected.txt: Added.
+        * http/tests/cookies/same-site/popup-same-site-post.html: Added.
+        * http/tests/cookies/same-site/popup-same-site-via-cross-site-redirect-expected.txt: Added.
+        * http/tests/cookies/same-site/popup-same-site-via-cross-site-redirect.html: Added.
+        * http/tests/cookies/same-site/popup-same-site-via-same-site-redirect-expected.txt: Added.
+        * http/tests/cookies/same-site/popup-same-site-via-same-site-redirect.html: Added.
+        * http/tests/cookies/same-site/popup-same-site.html: Added.
+        * http/tests/cookies/same-site/resources/click-hyperlink.php: Added.
+        * http/tests/cookies/same-site/resources/echo-iframe-src.php: Added.
+        * http/tests/cookies/same-site/resources/fetch-after-navigating-iframe-in-cross-origin-page.php: Added.
+        * http/tests/cookies/same-site/resources/fetch-after-top-level-navigation-from-cross-origin-page.php: Added.
+        * http/tests/cookies/same-site/resources/fetch-after-top-level-navigation-initiated-from-iframe-in-cross-origin-page.php: Added.
+        * http/tests/cookies/same-site/resources/fetch-in-cross-origin-iframe.html: Added.
+        * http/tests/cookies/same-site/resources/fetch-in-cross-origin-service-worker.html: Added.
+        * http/tests/cookies/same-site/resources/fetch-in-cross-origin-worker.js: Added.
+        (async.checkResult):
+        * http/tests/cookies/same-site/resources/fetch-in-same-origin-service-worker.php: Added.
+        * http/tests/cookies/same-site/resources/fetch-in-same-origin-worker.js: Added.
+        (async.checkResult):
+        * http/tests/cookies/same-site/resources/passthrough-service-worker.js: Added.
+        * platform/mac-wk1/TestExpectations: Skip the Service Worker tests as they are not supported in LegacyWebKit.
+
 2018-04-23  Chris Dumez  <cdumez@apple.com>
 
         HTML String load cannot be prevented by responding 'Cancel' asynchronously in decidePolicyForNavigationAction
index fc923dd..d17e964 100644 (file)
@@ -44,6 +44,7 @@ fast/css/variables/env/ios [ Skip ]
 http/tests/events/touch/ios [ Skip ]
 http/tests/preload/viewport [ Skip ]
 http/tests/gzip-content-encoding [ Skip ]
+http/tests/cookies/same-site [ Skip ]
 
 # window.showModalDialog is only tested in DumpRenderTree on Mac.
 editing/execCommand/show-modal-dialog-during-execCommand.html [ Skip ]
diff --git a/LayoutTests/http/tests/cookies/resources/cookie-utilities.js b/LayoutTests/http/tests/cookies/resources/cookie-utilities.js
new file mode 100644 (file)
index 0000000..51b5ab6
--- /dev/null
@@ -0,0 +1,230 @@
+if (self.testRunner)
+    testRunner.waitUntilDone();
+
+var g_childWindow;
+var g_cachedCookies;
+var g_baseURLWhenFetchingCookies = "";
+var g_baseDocumentWhenFetchingDOMCookies;
+
+function createCookie(name, value, additionalProperties)
+{
+    let cookie = `${name}=${value}`;
+    for (let propertyName in additionalProperties) {
+        cookie += `; ${propertyName}`;
+        let propertyValue = additionalProperties[propertyName];
+        if (propertyValue != undefined)
+            cookie += "=" + propertyValue;
+    }
+    return cookie;
+}
+
+function setBaseDocumentWhenFetchingDOMCookies(aDocument)
+{
+    g_baseDocumentWhenFetchingDOMCookies = aDocument;
+}
+
+function setDOMCookie(name, value, additionalProperties={})
+{
+    g_baseDocumentWhenFetchingDOMCookies.cookie = createCookie(name, value, additionalProperties);
+}
+
+function getDOMCookies()
+{
+    if (!g_baseDocumentWhenFetchingDOMCookies)
+        g_baseDocumentWhenFetchingDOMCookies = document;
+    let cookies = g_baseDocumentWhenFetchingDOMCookies.cookie.split("; ");
+    let result = {};
+    for (let keyAndValuePair of cookies) {
+        let [key, value] = keyAndValuePair.split("=");
+        result[key] = value;
+    }
+   return result;
+}
+
+async function setCookie(name, value, additionalProperties={})
+{
+    invalidateCachedCookies();
+
+    let cookie = createCookie(name, value, additionalProperties);
+    let promise = new Promise((resolved, rejected) => {
+        let xhr = new XMLHttpRequest;
+        xhr.open("GET", "/cookies/resources/setCookies.cgi");
+        xhr.setRequestHeader("SET-COOKIE", cookie);
+        xhr.onload = () => resolved(xhr.responseText);
+        xhr.onerror = rejected;
+        xhr.send(null);
+    });
+    return promise;
+}
+
+function disableSetAlwaysAcceptCookies() {
+    if (window.testRunner)
+        testRunner.setAlwaysAcceptCookies(false);
+}
+
+async function resetCookies(urls)
+{
+    let testingURLs = [
+        "http://127.0.0.1:8000",
+        "http://localhost:8000",
+    ];
+
+    urls = urls || testingURLs;
+    console.assert(urls.length);
+
+    function setUp() {
+        return new Promise((resolve) => {
+            if (window.testRunner) {
+                testRunner.setCanOpenWindows(true);
+                testRunner.setAlwaysAcceptCookies(true);
+                testRunner.setPopupBlockingEnabled(false);
+            }
+            resolve();
+        });
+    }
+
+    function cleanUp() {
+        return new Promise((resolve) => {
+            disableSetAlwaysAcceptCookies();
+            g_childWindow.close();
+            g_childWindow = null;
+            resolve();
+        });
+    }
+
+    let promise = setUp();
+    for (let url of urls) {
+        promise = promise.then(() => {
+            return new Promise((resolve, reject) => {
+                // FIXME: For some reason we get a SecurityError when passing childWindow to resolve() in Safari Version 11.0.3 (13604.5.6)
+                // and not in Chrome Canary 67.0.3390.0 (why?). As a workaround, store the child window reference in a global variable.
+                window.addEventListener("message", (messageEvent) => resolve(messageEvent), {capture: true, once: true});
+                g_childWindow = window.open(url + "/cookies/resources/cookie-utility.php?queryfunction=deleteCookiesAndPostMessage", "reset");
+                if (!g_childWindow)
+                    reject(null);
+            });
+        });
+    }
+    return promise.then(cleanUp);
+}
+
+async function resetCookiesForCurrentOrigin()
+{
+    invalidateCachedCookies();
+
+    let promise = new Promise((resolved, rejected) => {
+        let xhr = new XMLHttpRequest;
+        xhr.open("GET", "/cookies/resources/cookie-utility.php?queryfunction=deleteCookies");
+        xhr.onload = (progressEvent) => {
+            disableSetAlwaysAcceptCookies();
+            resolved(progressEvent);
+        };
+        xhr.onerror = (progressEvent) => {
+            disableSetAlwaysAcceptCookies();
+            rejected(progressEvent);
+        };
+
+        if (window.testRunner)
+            testRunner.setAlwaysAcceptCookies(true);
+
+        xhr.send(null);
+    });
+    return promise;
+}
+
+function setBaseURLWhenFetchingCookies(baseURLWhenFetchingCookies)
+{
+    g_baseURLWhenFetchingCookies = baseURLWhenFetchingCookies;
+}
+
+function invalidateCachedCookies()
+{
+    g_cachedCookies = null;
+}
+
+function _setCachedCookiesJSON(cookies)
+{
+    g_cachedCookies = JSON.parse(cookies);
+}
+
+async function getCookies()
+{
+    if (g_cachedCookies)
+        return g_cachedCookies;
+
+    let promise = new Promise((resolved, rejected) => {
+        let xhr = new XMLHttpRequest;
+        xhr.open("GET", `${g_baseURLWhenFetchingCookies}/cookies/resources/echo-json.php`);
+        xhr.onload = () => resolved(xhr.responseText ? JSON.parse(xhr.responseText) : {});
+        xhr.onerror = () => rejected({});
+        xhr.send(null);
+    });
+    g_cachedCookies = await promise;
+    return g_cachedCookies;
+}
+
+async function shouldNotHaveCookie(name)
+{
+    let cookies = await getCookies();
+    let value = cookies[name];
+    if (value == undefined)
+        testPassed(`Do not have cookie "${name}".`);
+    else
+        testFailed(`Should not have cookie "${name}". But do with value ${value}.`);
+}
+
+async function shouldHaveCookie(name)
+{
+    let cookies = await getCookies();
+    let value = cookies[name];
+    if (value == undefined)
+        testFailed(`Should have cookie "${name}". But do not.`);
+    else
+        testPassed(`Has cookie "${name}".`);
+}
+
+async function shouldHaveCookieWithValue(name, expectedValue)
+{
+    console.assert(expectedValue !== undefined);
+    let cookies = await getCookies();
+    let value = cookies[name];
+    if (value == undefined)
+        testFailed(`Should have cookie "${name}". But do not.`);
+    else if (value === expectedValue)
+        testPassed(`Has cookie "${name}" with value ${value}.`);
+    else
+        testFailed(`Cookie "${name}" should have value ${expectedValue}. Was ${value}.`);
+}
+
+function shouldNotHaveDOMCookie(name)
+{
+    let cookies = getDOMCookies();
+    let value = cookies[name];
+    if (value == undefined)
+        testPassed(`Do not have DOM cookie "${name}".`);
+    else
+        testFailed(`Should not have DOM cookie "${name}". But do with value ${value}.`);
+}
+
+function shouldHaveDOMCookie(name)
+{
+    let cookies = getDOMCookies();
+    let value = cookies[name];
+    if (value == undefined)
+        testFailed(`Should have DOM cookie "${name}". But do not.`);
+    else
+        testPassed(`Has DOM cookie "${name}".`);
+}
+
+function shouldHaveDOMCookieWithValue(name, expectedValue)
+{
+    console.assert(expectedValue !== undefined);
+    let cookies = getDOMCookies();
+    let value = cookies[name];
+    if (value == undefined)
+        testFailed(`Should have DOM cookie "${name}". But do not.`);
+    else if (value === expectedValue)
+        testPassed(`Has DOM cookie "${name}" with value ${value}.`);
+    else
+        testFailed(`DOM cookie "${name}" should have value ${expectedValue}. Was ${value}.`);
+}
index a33e7ac..9f6e005 100644 (file)
@@ -6,6 +6,13 @@ function deleteCookie($value, $name)
     setcookie($name, "deleted", time() - 86400, '/');
 }
 
+if ($queryfunction == "deleteCookiesAndPostMessage") {
+    array_walk($_COOKIE, deleteCookie);
+    echo "<script>window.opener.postMessage('done', '*');</script>\n";
+    return;
+}
+
+
 if ($queryfunction == "deleteCookies") {
     array_walk($_COOKIE, deleteCookie);
     echo "Deleted all cookies";
diff --git a/LayoutTests/http/tests/cookies/resources/echo-json.php b/LayoutTests/http/tests/cookies/resources/echo-json.php
new file mode 100644 (file)
index 0000000..5ee6af6
--- /dev/null
@@ -0,0 +1,8 @@
+<?php
+header("Content-Type: application/json");
+header("Access-Control-Allow-Credentials: true");
+header("Access-Control-Allow-External: true");
+header("Access-Control-Allow-Origin: ${_SERVER['HTTP_ORIGIN']}");
+
+echo json_encode($_COOKIE);
+?>
diff --git a/LayoutTests/http/tests/cookies/resources/post-cookies-onmessage.php b/LayoutTests/http/tests/cookies/resources/post-cookies-onmessage.php
new file mode 100644 (file)
index 0000000..028d51a
--- /dev/null
@@ -0,0 +1,14 @@
+<!doctype html>
+<script>
+
+var from_http = <?php
+echo json_encode($_COOKIE);
+?>;
+
+window.addEventListener("message", e => {
+    e.source.postMessage({
+        'http': from_http,
+        'document': document.cookie
+    }, "*");
+});
+</script>
diff --git a/LayoutTests/http/tests/cookies/resources/post-cookies-to-opener.php b/LayoutTests/http/tests/cookies/resources/post-cookies-to-opener.php
new file mode 100644 (file)
index 0000000..39c8524
--- /dev/null
@@ -0,0 +1,11 @@
+<!doctype html>
+<script>
+var from_http = <?php
+echo json_encode($_COOKIE);
+?>;
+
+window.opener.postMessage({
+    'http': from_http,
+    'document': document.cookie
+}, "*");
+</script>
diff --git a/LayoutTests/http/tests/cookies/resources/testharness-helpers.js b/LayoutTests/http/tests/cookies/resources/testharness-helpers.js
new file mode 100644 (file)
index 0000000..703b2f3
--- /dev/null
@@ -0,0 +1,22 @@
+var ORIGINAL_HOST  = "example.test";
+var TEST_ROOT = "not-example.test";
+var TEST_HOST = "cookies." + TEST_ROOT;
+var TEST_SUB  = "subdomain." + TEST_HOST;
+
+var STRICT_DOM = "strict_from_dom";
+var IMPLICIT_STRICT_DOM = "implicit_strict_from_dom";
+var STRICT_BECAUSE_INVALID_SAMESITE_VALUE = "strict_because_invalid_SameSite_value";
+var LAX_DOM = "lax_from_dom";
+var NORMAL_DOM = "normal_from_dom";
+
+// Clear the three well-known cookies.
+function clearKnownCookies() {
+    var cookies = [ STRICT_DOM, LAX_DOM, NORMAL_DOM, IMPLICIT_STRICT_DOM, STRICT_BECAUSE_INVALID_SAMESITE_VALUE ];
+    cookies.forEach(c => { document.cookie = c + "=0; expires=Thu, 01 Jan 1970 00:00:01 GMT; path=/"; });
+}
+
+function normalizeCookie(cookie)
+{
+    return cookie.split(/;\s*/).sort().join("; ");
+}
+
diff --git a/LayoutTests/http/tests/cookies/same-site/fetch-after-navigating-iframe-in-cross-origin-page-expected.txt b/LayoutTests/http/tests/cookies/same-site/fetch-after-navigating-iframe-in-cross-origin-page-expected.txt
new file mode 100644 (file)
index 0000000..97f2a76
--- /dev/null
@@ -0,0 +1,25 @@
+
+
+--------
+Frame: '<!--framePath //<!--frame0-->-->'
+--------
+Tests that Same-Site cookies for 127.0.0.1 are not sent with a frame navigation for a frame embedded in a page with a different origin.
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+Cookies sent with HTTP request:
+PASS Do not have cookie "strict".
+PASS Do not have cookie "implicit-strict".
+PASS Do not have cookie "strict-because-invalid-SameSite-value".
+PASS Do not have cookie "lax".
+
+Cookies visible in DOM:
+PASS Do not have DOM cookie "strict".
+PASS Do not have DOM cookie "implicit-strict".
+PASS Do not have DOM cookie "strict-because-invalid-SameSite-value".
+PASS Do not have DOM cookie "lax".
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
diff --git a/LayoutTests/http/tests/cookies/same-site/fetch-after-navigating-iframe-in-cross-origin-page.html b/LayoutTests/http/tests/cookies/same-site/fetch-after-navigating-iframe-in-cross-origin-page.html
new file mode 100644 (file)
index 0000000..cb2e520
--- /dev/null
@@ -0,0 +1,19 @@
+<!DOCTYPE html>
+<html>
+<head>
+<script src="../resources/cookie-utilities.js"></script>
+<body>
+<script>
+async function runTest()
+{
+    await resetCookies();
+    await setCookie("strict", "6", {"SameSite": "Strict", "Max-Age": 100, "path": "/"});
+    await setCookie("implicit-strict", "6", {"SameSite": null, "Max-Age": 100, "path": "/"});
+    await setCookie("strict-because-invalid-SameSite-value", "6", {"SameSite": "invalid", "Max-Age": 100, "path": "/"});
+    await setCookie("lax", "6", {"SameSite": "Lax", "Max-Age": 100, "path": "/"});
+    window.location.href = "http://localhost:8000/cookies/same-site/resources/echo-iframe-src.php?src=http%3A//127.0.0.1%3A8000/cookies/same-site/resources/click-hyperlink.php%3Fhref%3Dfetch-after-navigating-iframe-in-cross-origin-page.php";
+}
+runTest();
+</script>
+</body>
+</html>
diff --git a/LayoutTests/http/tests/cookies/same-site/fetch-after-top-level-navigation-from-cross-origin-page-expected.txt b/LayoutTests/http/tests/cookies/same-site/fetch-after-top-level-navigation-from-cross-origin-page-expected.txt
new file mode 100644 (file)
index 0000000..1e8a2e3
--- /dev/null
@@ -0,0 +1,20 @@
+Tests that a SameSite Lax cookie for 127.0.0.1 is sent with a top-level navigation initiated from a page with a different origin.
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+Cookies sent with HTTP request:
+PASS Do not have cookie "strict".
+PASS Do not have cookie "implicit-strict".
+PASS Do not have cookie "strict-because-invalid-SameSite-value".
+PASS Has cookie "lax" with value 5.
+
+Cookies visible in DOM:
+PASS Do not have DOM cookie "strict".
+PASS Do not have DOM cookie "implicit-strict".
+PASS Do not have DOM cookie "strict-because-invalid-SameSite-value".
+PASS Has DOM cookie "lax" with value 5.
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
diff --git a/LayoutTests/http/tests/cookies/same-site/fetch-after-top-level-navigation-from-cross-origin-page.html b/LayoutTests/http/tests/cookies/same-site/fetch-after-top-level-navigation-from-cross-origin-page.html
new file mode 100644 (file)
index 0000000..833a56b
--- /dev/null
@@ -0,0 +1,19 @@
+<!DOCTYPE html>
+<html>
+<head>
+<script src="../resources/cookie-utilities.js"></script>
+<body>
+<script>
+async function runTest()
+{
+    await resetCookies();
+    await setCookie("strict", "5", {"SameSite": "Strict", "Max-Age": 100, "path": "/"});
+    await setCookie("implicit-strict", "5", {"SameSite": null, "Max-Age": 100, "path": "/"});
+    await setCookie("strict-because-invalid-SameSite-value", "5", {"SameSite": "invalid", "Max-Age": 100, "path": "/"});
+    await setCookie("lax", "5", {"SameSite": "Lax", "Max-Age": 100, "path": "/"});
+    window.location.href = "http://localhost:8000/cookies/same-site/resources/click-hyperlink.php?href=http%3A//127.0.0.1%3A8000/cookies/same-site/resources/fetch-after-top-level-navigation-from-cross-origin-page.php";
+}
+runTest();
+</script>
+</body>
+</html>
diff --git a/LayoutTests/http/tests/cookies/same-site/fetch-after-top-level-navigation-initiated-from-iframe-in-cross-origin-page-expected.txt b/LayoutTests/http/tests/cookies/same-site/fetch-after-top-level-navigation-initiated-from-iframe-in-cross-origin-page-expected.txt
new file mode 100644 (file)
index 0000000..fa2cf4f
--- /dev/null
@@ -0,0 +1,20 @@
+Tests that a SameSite Lax cookie for 127.0.0.1 is sent with a top-level navigation initiated from a frame embedded in a page with a different origin.
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+Cookies sent with HTTP request:
+PASS Do not have cookie "strict".
+PASS Do not have cookie "implicit-strict".
+PASS Do not have cookie "strict-because-invalid-SameSite-value".
+PASS Has cookie "lax" with value 4.
+
+Cookies visible in DOM:
+PASS Do not have DOM cookie "strict".
+PASS Do not have DOM cookie "implicit-strict".
+PASS Do not have DOM cookie "strict-because-invalid-SameSite-value".
+PASS Has DOM cookie "lax" with value 4.
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
diff --git a/LayoutTests/http/tests/cookies/same-site/fetch-after-top-level-navigation-initiated-from-iframe-in-cross-origin-page.html b/LayoutTests/http/tests/cookies/same-site/fetch-after-top-level-navigation-initiated-from-iframe-in-cross-origin-page.html
new file mode 100644 (file)
index 0000000..66143b8
--- /dev/null
@@ -0,0 +1,19 @@
+<!DOCTYPE html>
+<html>
+<head>
+<script src="../resources/cookie-utilities.js"></script>
+<body>
+<script>
+async function runTest()
+{
+    await resetCookies();
+    await setCookie("strict", "4", {"SameSite": "Strict", "Max-Age": 100, "path": "/"});
+    await setCookie("implicit-strict", "4", {"SameSite": null, "Max-Age": 100, "path": "/"});
+    await setCookie("strict-because-invalid-SameSite-value", "4", {"SameSite": "invalid", "Max-Age": 100, "path": "/"});
+    await setCookie("lax", "4", {"SameSite": "Lax", "Max-Age": 100, "path": "/"});
+    window.location.href = "http://localhost:8000/cookies/same-site/resources/echo-iframe-src.php?src=http%3A//127.0.0.1%3A8000/cookies/same-site/resources/click-hyperlink.php%3Fhref%3Dfetch-after-top-level-navigation-initiated-from-iframe-in-cross-origin-page.php%26target%3D_top";
+}
+runTest();
+</script>
+</body>
+</html>
diff --git a/LayoutTests/http/tests/cookies/same-site/fetch-cookies-set-in-about-blank-iframe-expected.txt b/LayoutTests/http/tests/cookies/same-site/fetch-cookies-set-in-about-blank-iframe-expected.txt
new file mode 100644 (file)
index 0000000..f3e108c
--- /dev/null
@@ -0,0 +1,20 @@
+Tests that Same-Site cookies for 127.0.0.1 can be set in an about:blank iframe and retrieved by the top-most frame.
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+Cookies sent with HTTP request:
+PASS Has cookie "strict" with value 14.
+PASS Has cookie "implicit-strict" with value 14.
+PASS Has cookie "strict-because-invalid-SameSite-value" with value 14.
+PASS Has cookie "lax" with value 14.
+
+Cookies visible in DOM:
+PASS Has DOM cookie "strict" with value 14.
+PASS Has DOM cookie "implicit-strict" with value 14.
+PASS Has DOM cookie "strict-because-invalid-SameSite-value" with value 14.
+PASS Has DOM cookie "lax" with value 14.
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
diff --git a/LayoutTests/http/tests/cookies/same-site/fetch-cookies-set-in-about-blank-iframe.html b/LayoutTests/http/tests/cookies/same-site/fetch-cookies-set-in-about-blank-iframe.html
new file mode 100644 (file)
index 0000000..4ecdc6f
--- /dev/null
@@ -0,0 +1,57 @@
+<!DOCTYPE html>
+<html>
+<head>
+<script src="/js-test-resources/js-test.js"></script>
+<script src="../resources/cookie-utilities.js"></script>
+<body>
+<script>
+window.jsTestIsAsync = true;
+
+description("Tests that Same-Site cookies for 127.0.0.1 can be set in an about:blank iframe and retrieved by the top-most frame.");
+
+async function runTest()
+{
+    let appendIframeAndWaitUntilLoadedPromise = new Promise((resolved) => {
+        let iframe = document.createElement("iframe");
+        iframe.onload = (e) => resolved(iframe);
+        iframe.src = "about:blank";
+        document.body.appendChild(iframe);
+    });
+    let iframe = await appendIframeAndWaitUntilLoadedPromise;
+
+    let didLoadScriptInIFrame = new Promise((resolve) => {
+        let script = iframe.contentDocument.createElement("script");
+        script.onload = resolve;
+        script.src = "../resources/cookie-utilities.js";
+        iframe.contentDocument.head.appendChild(script);
+    });
+    await didLoadScriptInIFrame;
+
+    // Set cookies in about:blank iframe.
+    let iframeContentWindow = iframe.contentWindow;
+    await iframeContentWindow.resetCookies();
+    await iframeContentWindow.setCookie("strict", "14", {"SameSite": "Strict", "Max-Age": 100, "path": "/"});
+    await iframeContentWindow.setCookie("implicit-strict", "14", {"SameSite": null, "Max-Age": 100, "path": "/"});
+    await iframeContentWindow.setCookie("strict-because-invalid-SameSite-value", "14", {"SameSite": "invalid", "Max-Age": 100, "path": "/"});
+    await iframeContentWindow.setCookie("lax", "14", {"SameSite": "Lax", "Max-Age": 100, "path": "/"});
+
+    // Fetch cookies in top-most frame.
+    debug("Cookies sent with HTTP request:");
+    await shouldHaveCookieWithValue("strict", "14");
+    await shouldHaveCookieWithValue("implicit-strict", "14");
+    await shouldHaveCookieWithValue("strict-because-invalid-SameSite-value", "14");
+    await shouldHaveCookieWithValue("lax", "14");
+
+    debug("<br>Cookies visible in DOM:");
+    shouldHaveDOMCookieWithValue("strict", "14");
+    shouldHaveDOMCookieWithValue("implicit-strict", "14");
+    shouldHaveDOMCookieWithValue("strict-because-invalid-SameSite-value", "14");
+    shouldHaveDOMCookieWithValue("lax", "14");
+
+    await iframeContentWindow.resetCookies();
+    finishJSTest();
+}
+runTest();
+</script>
+</body>
+</html>
diff --git a/LayoutTests/http/tests/cookies/same-site/fetch-in-about-blank-page-expected.txt b/LayoutTests/http/tests/cookies/same-site/fetch-in-about-blank-page-expected.txt
new file mode 100644 (file)
index 0000000..337e760
--- /dev/null
@@ -0,0 +1,14 @@
+Tests that Same-Site DOM cookies for 127.0.0.1 are visible from about:blank iframe.
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+Cookies visible in DOM:
+PASS Has DOM cookie "strict" with value 13.
+PASS Has DOM cookie "implicit-strict" with value 13.
+PASS Has DOM cookie "strict-because-invalid-SameSite-value" with value 13.
+PASS Has DOM cookie "lax" with value 13.
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
diff --git a/LayoutTests/http/tests/cookies/same-site/fetch-in-about-blank-page.html b/LayoutTests/http/tests/cookies/same-site/fetch-in-about-blank-page.html
new file mode 100644 (file)
index 0000000..e20f6cc
--- /dev/null
@@ -0,0 +1,42 @@
+<!DOCTYPE html>
+<html>
+<head>
+<script src="/js-test-resources/js-test.js"></script>
+<script src="../resources/cookie-utilities.js"></script>
+<body>
+<script>
+window.jsTestIsAsync = true;
+
+description("Tests that Same-Site DOM cookies for 127.0.0.1 are visible from about:blank iframe.");
+
+async function runTest()
+{
+    await resetCookies();
+    await setCookie("strict", "13", {"SameSite": "Strict", "Max-Age": 100, "path": "/"});
+    await setCookie("implicit-strict", "13", {"SameSite": null, "Max-Age": 100, "path": "/"});
+    await setCookie("strict-because-invalid-SameSite-value", "13", {"SameSite": "invalid", "Max-Age": 100, "path": "/"});
+    await setCookie("lax", "13", {"SameSite": "Lax", "Max-Age": 100, "path": "/"});
+
+    let appendIframeAndWaitUntilLoadedPromise = new Promise((resolved) => {
+        let iframe = document.createElement("iframe");
+        iframe.onload = (e) => resolved(iframe);
+        iframe.src = "about:blank";
+        document.body.appendChild(iframe);
+    });
+    let iframe = await appendIframeAndWaitUntilLoadedPromise;
+
+    setBaseDocumentWhenFetchingDOMCookies(iframe.contentDocument);
+
+    debug("Cookies visible in DOM:");
+    shouldHaveDOMCookieWithValue("strict", "13");
+    shouldHaveDOMCookieWithValue("implicit-strict", "13");
+    shouldHaveDOMCookieWithValue("strict-because-invalid-SameSite-value", "13");
+    shouldHaveDOMCookieWithValue("lax", "13");
+
+    await resetCookies();
+    finishJSTest();
+}
+runTest();
+</script>
+</body>
+</html>
diff --git a/LayoutTests/http/tests/cookies/same-site/fetch-in-cross-origin-iframe-expected.txt b/LayoutTests/http/tests/cookies/same-site/fetch-in-cross-origin-iframe-expected.txt
new file mode 100644 (file)
index 0000000..926c30d
--- /dev/null
@@ -0,0 +1,25 @@
+
+
+--------
+Frame: '<!--framePath //<!--frame0-->-->'
+--------
+Tests that Same-Site cookies for 127.0.0.1 are not sent with a request initiated from a iframe with a different origin.
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+Cookies sent with HTTP request:
+PASS Do not have cookie "strict".
+PASS Do not have cookie "implicit-strict".
+PASS Do not have cookie "strict-because-invalid-SameSite-value".
+PASS Do not have cookie "lax".
+
+Cookies visible in DOM:
+PASS Do not have DOM cookie "strict".
+PASS Do not have DOM cookie "implicit-strict".
+PASS Do not have DOM cookie "strict-because-invalid-SameSite-value".
+PASS Do not have DOM cookie "lax".
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
diff --git a/LayoutTests/http/tests/cookies/same-site/fetch-in-cross-origin-iframe.html b/LayoutTests/http/tests/cookies/same-site/fetch-in-cross-origin-iframe.html
new file mode 100644 (file)
index 0000000..8dc9aa6
--- /dev/null
@@ -0,0 +1,19 @@
+<!DOCTYPE html>
+<html>
+<head>
+<script src="../resources/cookie-utilities.js"></script>
+<body>
+<script>
+async function runTest()
+{
+    await resetCookies();
+    await setCookie("strict", "3", {"SameSite": "Strict", "Max-Age": 100, "path": "/"});
+    await setCookie("implicit-strict", "3", {"SameSite": null, "Max-Age": 100, "path": "/"});
+    await setCookie("strict-because-invalid-SameSite-value", "3", {"SameSite": "invalid", "Max-Age": 100, "path": "/"});
+    await setCookie("lax", "3", {"SameSite": "Lax", "Max-Age": 100, "path": "/"});
+    window.location.href = "http://127.0.0.1:8000/cookies/same-site/resources/echo-iframe-src.php?src=http://localhost:8000/cookies/same-site/resources/fetch-in-cross-origin-iframe.html";
+}
+runTest();
+</script>
+</body>
+</html>
diff --git a/LayoutTests/http/tests/cookies/same-site/fetch-in-cross-origin-page-expected.txt b/LayoutTests/http/tests/cookies/same-site/fetch-in-cross-origin-page-expected.txt
new file mode 100644 (file)
index 0000000..f075506
--- /dev/null
@@ -0,0 +1,20 @@
+Tests that Same-Site cookies for 127.0.0.1 are not sent with a request initiated from a page with a different origin.
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+Cookies sent with HTTP request:
+PASS Do not have cookie "strict".
+PASS Do not have cookie "implicit-strict".
+PASS Do not have cookie "strict-because-invalid-SameSite-value".
+PASS Do not have cookie "lax".
+
+Cookies visible in DOM:
+PASS Do not have DOM cookie "strict".
+PASS Do not have DOM cookie "implicit-strict".
+PASS Do not have DOM cookie "strict-because-invalid-SameSite-value".
+PASS Do not have DOM cookie "lax".
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
diff --git a/LayoutTests/http/tests/cookies/same-site/fetch-in-cross-origin-page.html b/LayoutTests/http/tests/cookies/same-site/fetch-in-cross-origin-page.html
new file mode 100644 (file)
index 0000000..0d611e5
--- /dev/null
@@ -0,0 +1,45 @@
+<!DOCTYPE html>
+<html>
+<head>
+<script src="/js-test-resources/js-test.js"></script>
+<script src="../resources/cookie-utilities.js"></script>
+<body>
+<script>
+window.jsTestIsAsync = true;
+
+description("Tests that Same-Site cookies for 127.0.0.1 are not sent with a request initiated from a page with a different origin.");
+
+async function runTest()
+{
+    if (document.location.hostname === "127.0.0.1") {
+        await resetCookies();
+        await setCookie("strict", "1", {"SameSite": "Strict", "Max-Age": 100, "path": "/"});
+        await setCookie("implicit-strict", "1", {"SameSite": null, "Max-Age": 100, "path": "/"});
+        await setCookie("strict-because-invalid-SameSite-value", "1", {"SameSite": "invalid", "Max-Age": 100, "path": "/"});
+        await setCookie("lax", "1", {"SameSite": "Lax", "Max-Age": 100, "path": "/"});
+        window.location.hostname = "localhost";
+    } else {
+        // localhost
+        setBaseURLWhenFetchingCookies("http://127.0.0.1:8000");
+
+        debug("Cookies sent with HTTP request:");
+        await shouldNotHaveCookie("strict");
+        await shouldNotHaveCookie("implicit-strict");
+        await shouldNotHaveCookie("strict-because-invalid-SameSite-value");
+        await shouldNotHaveCookie("lax");
+
+        debug("<br>Cookies visible in DOM:");
+        shouldNotHaveDOMCookie("strict");
+        shouldNotHaveDOMCookie("implicit-strict");
+        shouldNotHaveDOMCookie("strict-because-invalid-SameSite-value");
+        shouldNotHaveDOMCookie("lax");
+
+        await resetCookies();
+        finishJSTest();
+    }
+}
+
+runTest();
+</script>
+</body>
+</html>
diff --git a/LayoutTests/http/tests/cookies/same-site/fetch-in-cross-origin-service-worker-expected.txt b/LayoutTests/http/tests/cookies/same-site/fetch-in-cross-origin-service-worker-expected.txt
new file mode 100644 (file)
index 0000000..2ee7c21
--- /dev/null
@@ -0,0 +1,25 @@
+
+
+--------
+Frame: '<!--framePath //<!--frame0-->-->'
+--------
+Tests that Same-Site cookies for 127.0.0.1 are not sent with a request initiated from an iframe- and processed by a service worker- with a different origin.
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+Cookies sent with HTTP request:
+PASS Do not have cookie "strict".
+PASS Do not have cookie "implicit-strict".
+PASS Do not have cookie "strict-because-invalid-SameSite-value".
+PASS Do not have cookie "lax".
+
+Cookies visible in DOM:
+PASS Do not have DOM cookie "strict".
+PASS Do not have DOM cookie "implicit-strict".
+PASS Do not have DOM cookie "strict-because-invalid-SameSite-value".
+PASS Do not have DOM cookie "lax".
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
diff --git a/LayoutTests/http/tests/cookies/same-site/fetch-in-cross-origin-service-worker.html b/LayoutTests/http/tests/cookies/same-site/fetch-in-cross-origin-service-worker.html
new file mode 100644 (file)
index 0000000..cfc5796
--- /dev/null
@@ -0,0 +1,33 @@
+<!DOCTYPE html>
+<html>
+<head>
+<script src="/workers/service/resources/sw-test-pre.js"></script>
+<script src="../resources/cookie-utilities.js"></script>
+<body>
+<script>
+if (window.testRunner)
+    testRunner.dumpChildFramesAsText();
+
+async function runTest()
+{
+    if (!navigator.serviceWorker) {
+        document.writeln("This test requires Service Workers.");
+        return;
+    }
+
+    if (window.location.hostname === "127.0.0.1") {
+        await resetCookies();
+        await setCookie("strict", "10", {"SameSite": "Strict", "Max-Age": 100, "path": "/"});
+        await setCookie("implicit-strict", "10", {"SameSite": null, "Max-Age": 100, "path": "/"});
+        await setCookie("strict-because-invalid-SameSite-value", "10", {"SameSite": "invalid", "Max-Age": 100, "path": "/"});
+        await setCookie("lax", "10", {"SameSite": "Lax", "Max-Age": 100, "path": "/"});
+        window.location.hostname = "localhost";
+    } else {
+        // localhost
+        await interceptedFrame("resources/passthrough-service-worker.js", "resources/fetch-in-cross-origin-service-worker.html");
+    }
+}
+runTest();
+</script>
+</body>
+</html>
diff --git a/LayoutTests/http/tests/cookies/same-site/fetch-in-cross-origin-worker-expected.txt b/LayoutTests/http/tests/cookies/same-site/fetch-in-cross-origin-worker-expected.txt
new file mode 100644 (file)
index 0000000..2cde00b
--- /dev/null
@@ -0,0 +1,14 @@
+Tests that Same-Site cookies for 127.0.0.1 are not sent with a Web Worker initiated cross-origin XHR.
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+Starting worker: resources/fetch-in-cross-origin-worker.js
+PASS [Worker] Do not have cookie "strict".
+PASS [Worker] Do not have cookie "implicit-strict".
+PASS [Worker] Do not have cookie "strict-because-invalid-SameSite-value".
+PASS [Worker] Do not have cookie "lax".
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
diff --git a/LayoutTests/http/tests/cookies/same-site/fetch-in-cross-origin-worker.html b/LayoutTests/http/tests/cookies/same-site/fetch-in-cross-origin-worker.html
new file mode 100644 (file)
index 0000000..a7c32b0
--- /dev/null
@@ -0,0 +1,33 @@
+<!DOCTYPE html>
+<html>
+<head>
+<script src="/js-test-resources/js-test.js"></script>
+<script src="../resources/cookie-utilities.js"></script>
+<body>
+<script>
+window.jsTestIsAsync = true;
+
+description("Tests that Same-Site cookies for 127.0.0.1 are not sent with a Web Worker initiated cross-origin XHR.");
+
+// Swizzle finishJSTest() so that we reset cookies before calling finishJSTest().
+var savedFinishJSTest = finishJSTest;
+window.finishJSTest = () => resetCookies().then(savedFinishJSTest);
+
+async function runTest()
+{
+    if (window.location.hostname === "127.0.0.1") {
+        await resetCookies();
+        await setCookie("strict", "13", {"SameSite": "Strict", "Max-Age": 100, "path": "/"});
+        await setCookie("implicit-strict", "13", {"SameSite": null, "Max-Age": 100, "path": "/"});
+        await setCookie("strict-because-invalid-SameSite-value", "13", {"SameSite": "invalid", "Max-Age": 100, "path": "/"});
+        await setCookie("lax", "13", {"SameSite": "Lax", "Max-Age": 100, "path": "/"});
+        window.location.hostname = "localhost";
+    } else {
+        // localhost
+        startWorker("resources/fetch-in-cross-origin-worker.js");
+    }
+}
+runTest();
+</script>
+</body>
+</html>
diff --git a/LayoutTests/http/tests/cookies/same-site/fetch-in-same-origin-page-expected.txt b/LayoutTests/http/tests/cookies/same-site/fetch-in-same-origin-page-expected.txt
new file mode 100644 (file)
index 0000000..763e8a1
--- /dev/null
@@ -0,0 +1,20 @@
+Tests that Same-Site cookies for 127.0.0.1 are sent with a request initiated same origin.
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+Cookies sent with HTTP request:
+PASS Has cookie "strict" with value 2.
+PASS Has cookie "implicit-strict" with value 2.
+PASS Has cookie "strict-because-invalid-SameSite-value" with value 2.
+PASS Has cookie "lax" with value 2.
+
+Cookies visible in DOM:
+PASS Has DOM cookie "strict" with value 2.
+PASS Has DOM cookie "implicit-strict" with value 2.
+PASS Has DOM cookie "strict-because-invalid-SameSite-value" with value 2.
+PASS Has DOM cookie "lax" with value 2.
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
diff --git a/LayoutTests/http/tests/cookies/same-site/fetch-in-same-origin-page.html b/LayoutTests/http/tests/cookies/same-site/fetch-in-same-origin-page.html
new file mode 100644 (file)
index 0000000..ac8e6fa
--- /dev/null
@@ -0,0 +1,38 @@
+<!DOCTYPE html>
+<html>
+<head>
+<script src="/js-test-resources/js-test.js"></script>
+<script src="../resources/cookie-utilities.js"></script>
+<body>
+<script>
+window.jsTestIsAsync = true;
+
+description("Tests that Same-Site cookies for 127.0.0.1 are sent with a request initiated same origin.");
+
+async function runTest()
+{
+    await resetCookies();
+    await setCookie("strict", "2", {"SameSite": "Strict", "Max-Age": 100, "path": "/"});
+    await setCookie("implicit-strict", "2", {"SameSite": null, "Max-Age": 100, "path": "/"});
+    await setCookie("strict-because-invalid-SameSite-value", "2", {"SameSite": "invalid", "Max-Age": 100, "path": "/"});
+    await setCookie("lax", "2", {"SameSite": "Lax", "Max-Age": 100, "path": "/"});
+
+    debug("Cookies sent with HTTP request:");
+    await shouldHaveCookieWithValue("strict", "2");
+    await shouldHaveCookieWithValue("implicit-strict", "2");
+    await shouldHaveCookieWithValue("strict-because-invalid-SameSite-value", "2");
+    await shouldHaveCookieWithValue("lax", "2");
+
+    debug("<br>Cookies visible in DOM:");
+    shouldHaveDOMCookieWithValue("strict", "2");
+    shouldHaveDOMCookieWithValue("implicit-strict", "2");
+    shouldHaveDOMCookieWithValue("strict-because-invalid-SameSite-value", "2");
+    shouldHaveDOMCookieWithValue("lax", "2");
+
+    await resetCookies();
+    finishJSTest();
+}
+runTest();
+</script>
+</body>
+</html>
diff --git a/LayoutTests/http/tests/cookies/same-site/fetch-in-same-origin-service-worker-expected.txt b/LayoutTests/http/tests/cookies/same-site/fetch-in-same-origin-service-worker-expected.txt
new file mode 100644 (file)
index 0000000..4fb70b7
--- /dev/null
@@ -0,0 +1,25 @@
+
+
+--------
+Frame: '<!--framePath //<!--frame0-->-->'
+--------
+Tests that Same-Site cookies for 127.0.0.1 are sent with a request initiated from an iframe- and processed by a service worker- with the same origin.
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+Cookies sent with HTTP request:
+PASS Has cookie "strict" with value 11.
+PASS Has cookie "implicit-strict" with value 11.
+PASS Has cookie "strict-because-invalid-SameSite-value" with value 11.
+PASS Has cookie "lax" with value 11.
+
+Cookies visible in DOM:
+PASS Has DOM cookie "strict" with value 11.
+PASS Has DOM cookie "implicit-strict" with value 11.
+PASS Has DOM cookie "strict-because-invalid-SameSite-value" with value 11.
+PASS Has DOM cookie "lax" with value 11.
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
diff --git a/LayoutTests/http/tests/cookies/same-site/fetch-in-same-origin-service-worker.html b/LayoutTests/http/tests/cookies/same-site/fetch-in-same-origin-service-worker.html
new file mode 100644 (file)
index 0000000..e57cf7d
--- /dev/null
@@ -0,0 +1,28 @@
+<!DOCTYPE html>
+<html>
+<head>
+<script src="/workers/service/resources/sw-test-pre.js"></script>
+<script src="../resources/cookie-utilities.js"></script>
+<body>
+<script>
+if (window.testRunner)
+    testRunner.dumpChildFramesAsText();
+
+async function runTest()
+{
+    if (!navigator.serviceWorker) {
+        document.writeln("This test requires Service Workers.");
+        return;
+    }
+
+    await resetCookies();
+    await setCookie("strict", "11", {"SameSite": "Strict", "Max-Age": 100, "path": "/"});
+    await setCookie("implicit-strict", "11", {"SameSite": null, "Max-Age": 100, "path": "/"});
+    await setCookie("strict-because-invalid-SameSite-value", "11", {"SameSite": "invalid", "Max-Age": 100, "path": "/"});
+    await setCookie("lax", "11", {"SameSite": "Lax", "Max-Age": 100, "path": "/"});
+    await interceptedFrame("resources/passthrough-service-worker.js", "resources/fetch-in-same-origin-service-worker.php");
+}
+runTest();
+</script>
+</body>
+</html>
diff --git a/LayoutTests/http/tests/cookies/same-site/fetch-in-same-origin-srcdoc-iframe-expected.txt b/LayoutTests/http/tests/cookies/same-site/fetch-in-same-origin-srcdoc-iframe-expected.txt
new file mode 100644 (file)
index 0000000..a1ba27b
--- /dev/null
@@ -0,0 +1,36 @@
+Tests that Same-Site cookies for 127.0.0.1 can be retrieved from a srcdoc iframe.
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+Retrieve cookies from srcdoc iframe:
+
+Cookies sent with HTTP request:
+PASS Has cookie "strict" with value 8.
+PASS Has cookie "implicit-strict" with value 8.
+PASS Has cookie "strict-because-invalid-SameSite-value" with value 8.
+PASS Has cookie "lax" with value 8.
+
+Cookies visible in DOM:
+PASS Has DOM cookie "strict" with value 8.
+PASS Has DOM cookie "implicit-strict" with value 8.
+PASS Has DOM cookie "strict-because-invalid-SameSite-value" with value 8.
+PASS Has DOM cookie "lax" with value 8.
+
+Retrieve cookies from srcdoc iframe inside srcdoc iframe:
+
+Cookies sent with HTTP request:
+PASS Has cookie "strict" with value 8.
+PASS Has cookie "implicit-strict" with value 8.
+PASS Has cookie "strict-because-invalid-SameSite-value" with value 8.
+PASS Has cookie "lax" with value 8.
+
+Cookies visible in DOM:
+PASS Has DOM cookie "strict" with value 8.
+PASS Has DOM cookie "implicit-strict" with value 8.
+PASS Has DOM cookie "strict-because-invalid-SameSite-value" with value 8.
+PASS Has DOM cookie "lax" with value 8.
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
diff --git a/LayoutTests/http/tests/cookies/same-site/fetch-in-same-origin-srcdoc-iframe.html b/LayoutTests/http/tests/cookies/same-site/fetch-in-same-origin-srcdoc-iframe.html
new file mode 100644 (file)
index 0000000..6f9caf9
--- /dev/null
@@ -0,0 +1,82 @@
+<!DOCTYPE html>
+<html>
+<head>
+<script src="/js-test-resources/js-test.js"></script>
+<script src="../resources/cookie-utilities.js"></script>
+<body>
+<script id="srcdoc-script" type="text/plain">
+async function runTest()
+{
+    document.body.appendChild(document.createTextNode(window.name)); // For debugging
+
+    window.top.invalidateCachedCookies();
+
+    window.top.debug("<br>Cookies sent with HTTP request:");
+    await window.top.shouldHaveCookieWithValue("strict", "8");
+    await window.top.shouldHaveCookieWithValue("implicit-strict", "8");
+    await window.top.shouldHaveCookieWithValue("strict-because-invalid-SameSite-value", "8");
+    await window.top.shouldHaveCookieWithValue("lax", "8");
+
+    window.top.debug("<br>Cookies visible in DOM:");
+    window.top.shouldHaveDOMCookieWithValue("strict", "8");
+    window.top.shouldHaveDOMCookieWithValue("implicit-strict", "8");
+    window.top.shouldHaveDOMCookieWithValue("strict-because-invalid-SameSite-value", "8");
+    window.top.shouldHaveDOMCookieWithValue("lax", "8");
+}
+</script>
+<script>
+window.jsTestIsAsync = true;
+
+description("Tests that Same-Site cookies for 127.0.0.1 can be retrieved from a srcdoc iframe.");
+
+const SrcdocScript = `<script>${document.getElementById("srcdoc-script").text}</` + "script>";
+
+function appendSrcdocIframeToBodyOfDocument(document, name, content, callback)
+{
+    let iframe = document.createElement("iframe");
+    iframe.name = name;
+    iframe.onload = () => callback(iframe);
+    iframe.srcdoc = content;
+    document.body.appendChild(iframe);
+}
+
+async function testRetrieveCookiesFromSrcdocIframe()
+{
+    let appendIFrameAndWaitUntilLoaded = new Promise((resolved) => appendSrcdocIframeToBodyOfDocument(document, "A", SrcdocScript, resolved));
+    let iframe = await appendIFrameAndWaitUntilLoaded;
+    await iframe.contentWindow.runTest();
+}
+
+async function testRetrieveCookiesFromNestedSrcdocIframe()
+{
+    let appendIFrameAndWaitUntilLoaded = new Promise((resolved) => appendSrcdocIframeToBodyOfDocument(document, "B", "<body></body>", resolved));
+    let iframe = await appendIFrameAndWaitUntilLoaded;
+    let appendInnerIFrameAndWaitUntilLoaded = new Promise((resolved) => appendSrcdocIframeToBodyOfDocument(iframe.contentDocument, "B_1", SrcdocScript, resolved));
+    let innerIframe = await appendInnerIFrameAndWaitUntilLoaded;
+    await innerIframe.contentWindow.runTest();
+}
+
+async function runTest()
+{
+    await resetCookies();
+    await setCookie("strict", "8", {"SameSite": "Strict", "Max-Age": 100, "path": "/"});
+    await setCookie("implicit-strict", "8", {"SameSite": null, "Max-Age": 100, "path": "/"});
+    await setCookie("strict-because-invalid-SameSite-value", "8", {"SameSite": "invalid", "Max-Age": 100, "path": "/"});
+    await setCookie("lax", "8", {"SameSite": "Lax", "Max-Age": 100, "path": "/"});
+
+    debug("Retrieve cookies from srcdoc iframe:");
+    await testRetrieveCookiesFromSrcdocIframe();
+
+    debug("<br>Retrieve cookies from srcdoc iframe inside srcdoc iframe:")
+    await testRetrieveCookiesFromNestedSrcdocIframe();
+
+    for(let iframe of document.querySelectorAll("iframe"))
+        document.body.removeChild(iframe);
+
+    await resetCookies();
+    finishJSTest();
+}
+runTest();
+</script>
+</body>
+</html>
diff --git a/LayoutTests/http/tests/cookies/same-site/fetch-in-same-origin-worker-expected.txt b/LayoutTests/http/tests/cookies/same-site/fetch-in-same-origin-worker-expected.txt
new file mode 100644 (file)
index 0000000..a24fb87
--- /dev/null
@@ -0,0 +1,14 @@
+Tests that Same-Site cookies for 127.0.0.1 are sent with a Web Worker initiated XHR.
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+Starting worker: resources/fetch-in-same-origin-worker.js
+PASS [Worker] Has cookie "strict" with value 12.
+PASS [Worker] Has cookie "implicit-strict" with value 12.
+PASS [Worker] Has cookie "strict-because-invalid-SameSite-value" with value 12.
+PASS [Worker] Has cookie "lax" with value 12.
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
diff --git a/LayoutTests/http/tests/cookies/same-site/fetch-in-same-origin-worker.html b/LayoutTests/http/tests/cookies/same-site/fetch-in-same-origin-worker.html
new file mode 100644 (file)
index 0000000..a70d551
--- /dev/null
@@ -0,0 +1,28 @@
+<!DOCTYPE html>
+<html>
+<head>
+<script src="/js-test-resources/js-test.js"></script>
+<script src="../resources/cookie-utilities.js"></script>
+<body>
+<script>
+window.jsTestIsAsync = true;
+
+description("Tests that Same-Site cookies for 127.0.0.1 are sent with a Web Worker initiated XHR.");
+
+// Swizzle finishJSTest() so that we reset cookies before calling finishJSTest().
+var savedFinishJSTest = finishJSTest;
+window.finishJSTest = () => resetCookies().then(savedFinishJSTest);
+
+async function runTest()
+{
+    await resetCookies();
+    await setCookie("strict", "12", {"SameSite": "Strict", "Max-Age": 100, "path": "/"});
+    await setCookie("implicit-strict", "12", {"SameSite": null, "Max-Age": 100, "path": "/"});
+    await setCookie("strict-because-invalid-SameSite-value", "12", {"SameSite": "invalid", "Max-Age": 100, "path": "/"});
+    await setCookie("lax", "12", {"SameSite": "Lax", "Max-Age": 100, "path": "/"});
+    startWorker("resources/fetch-in-same-origin-worker.js");
+}
+runTest();
+</script>
+</body>
+</html>
diff --git a/LayoutTests/http/tests/cookies/same-site/popup-cross-site-expected.txt b/LayoutTests/http/tests/cookies/same-site/popup-cross-site-expected.txt
new file mode 100644 (file)
index 0000000..4c9fda7
--- /dev/null
@@ -0,0 +1,3 @@
+
+PASS '127.0.0.1' is not same-site with 'localhost', so strict samesite cookies are not sent. 
+
diff --git a/LayoutTests/http/tests/cookies/same-site/popup-cross-site-post-expected.txt b/LayoutTests/http/tests/cookies/same-site/popup-cross-site-post-expected.txt
new file mode 100644 (file)
index 0000000..05c1dc1
--- /dev/null
@@ -0,0 +1,3 @@
+
+PASS '127.0.0.1' is not same-site with 'localhost', so samesite cookies are not sent via POST. 
+
diff --git a/LayoutTests/http/tests/cookies/same-site/popup-cross-site-post.html b/LayoutTests/http/tests/cookies/same-site/popup-cross-site-post.html
new file mode 100644 (file)
index 0000000..57bddf0
--- /dev/null
@@ -0,0 +1,36 @@
+<!DOCTYPE html>
+<script src="/js-test-resources/testharness.js"></script>
+<script src="/js-test-resources/testharnessreport.js"></script>
+<script src="../resources/testharness-helpers.js"></script>
+<body>
+<script>
+if (window.location.hostname == "127.0.0.1") {
+    clearKnownCookies();
+    document.cookie = STRICT_DOM + "=1; SameSite=Strict; Max-Age=100; path=/";
+    document.cookie = IMPLICIT_STRICT_DOM + "=1; SameSite; Max-Age=100; path=/";
+    document.cookie = STRICT_BECAUSE_INVALID_SAMESITE_VALUE + "=1; SameSite=invalid; Max-Age=100; path=/";
+    document.cookie = LAX_DOM + "=1; SameSite=Lax; Max-Age=100; path=/";
+    document.cookie = NORMAL_DOM + "=1; Max-Age=100; path=/";
+    window.location.hostname = "localhost";
+} else {
+    async_test(t => {
+        window.addEventListener("message", t.step_func_done(e => {
+            assert_equals(e.data.http[STRICT_DOM], undefined, "strict");
+            assert_equals(e.data.http[IMPLICIT_STRICT_DOM], undefined, "implicit-strict");
+            assert_equals(e.data.http[STRICT_BECAUSE_INVALID_SAMESITE_VALUE], undefined, "strict-because-invalid-SameSite-value");
+            assert_equals(e.data.http[LAX_DOM], undefined, "lax");
+            assert_equals(e.data.http[NORMAL_DOM], "1", "normal");
+            assert_equals(normalizeCookie(e.data.document), normalizeCookie(LAX_DOM + "=1; " + NORMAL_DOM + "=1"));
+            e.source.close();
+        }));
+
+        var f = document.createElement('form');
+        f.action = "http://127.0.0.1:8000/cookies/resources/post-cookies-to-opener.php";
+        f.method = "POST";
+        f.target = "_blank"
+        window.onload = t.step_func(f.submit.bind(f));
+        document.body.appendChild(f);
+    }, "'127.0.0.1' is not same-site with 'localhost', so samesite cookies are not sent via POST.");
+}
+</script>
+</body>
diff --git a/LayoutTests/http/tests/cookies/same-site/popup-cross-site.html b/LayoutTests/http/tests/cookies/same-site/popup-cross-site.html
new file mode 100644 (file)
index 0000000..e08ff02
--- /dev/null
@@ -0,0 +1,26 @@
+<!DOCTYPE html>
+<script src="/js-test-resources/testharness.js"></script>
+<script src="/js-test-resources/testharnessreport.js"></script>
+<script src="../resources/testharness-helpers.js"></script>
+<script>
+if (window.location.hostname == "127.0.0.1") {
+    document.cookie = STRICT_DOM + "=1; SameSite=Strict; Max-Age=100; path=/";
+    document.cookie = IMPLICIT_STRICT_DOM + "=1; SameSite; Max-Age=100; path=/";
+    document.cookie = STRICT_BECAUSE_INVALID_SAMESITE_VALUE + "=1; SameSite=invalid; Max-Age=100; path=/";
+    document.cookie = LAX_DOM + "=1; SameSite=Lax; Max-Age=100; path=/";
+    document.cookie = NORMAL_DOM + "=1; Max-Age=100; path=/";
+    window.location.hostname = "localhost";
+} else {
+    async_test(t => {
+        var i = window.open("http://127.0.0.1:8000/cookies/resources/post-cookies-to-opener.php");
+        window.addEventListener("message", t.step_func_done(e => {
+            assert_equals(e.data.http[STRICT_DOM], undefined, "strict");
+            assert_equals(e.data.http[IMPLICIT_STRICT_DOM], undefined, "implicit-strict");
+            assert_equals(e.data.http[STRICT_BECAUSE_INVALID_SAMESITE_VALUE], undefined, "strict-because-invalid-SameSite-value");
+            assert_equals(e.data.http[LAX_DOM], "1", "lax");
+            assert_equals(e.data.http[NORMAL_DOM], "1", "normal");
+            assert_equals(normalizeCookie(e.data.document), normalizeCookie(LAX_DOM + "=1; " + NORMAL_DOM + "=1"));
+        }));
+    }, "'127.0.0.1' is not same-site with 'localhost', so strict samesite cookies are not sent.");
+}
+</script>
diff --git a/LayoutTests/http/tests/cookies/same-site/popup-same-site-expected.txt b/LayoutTests/http/tests/cookies/same-site/popup-same-site-expected.txt
new file mode 100644 (file)
index 0000000..39aa2ba
--- /dev/null
@@ -0,0 +1,3 @@
+
+PASS '127.0.0.1' is same-site with itself, so samesite cookies are sent. 
+
diff --git a/LayoutTests/http/tests/cookies/same-site/popup-same-site-post-expected.txt b/LayoutTests/http/tests/cookies/same-site/popup-same-site-post-expected.txt
new file mode 100644 (file)
index 0000000..213153f
--- /dev/null
@@ -0,0 +1,3 @@
+
+PASS '127.0.0.1' is same-site with itself, so samesite cookies are sent via POST. 
+
diff --git a/LayoutTests/http/tests/cookies/same-site/popup-same-site-post.html b/LayoutTests/http/tests/cookies/same-site/popup-same-site-post.html
new file mode 100644 (file)
index 0000000..0107f29
--- /dev/null
@@ -0,0 +1,33 @@
+<!DOCTYPE html>
+<script src="/js-test-resources/testharness.js"></script>
+<script src="/js-test-resources/testharnessreport.js"></script>
+<script src="../resources/testharness-helpers.js"></script>
+<body>
+<script>
+async_test(t => {
+    clearKnownCookies();
+    document.cookie = STRICT_DOM + "=1; SameSite=Strict; Max-Age=100; path=/";
+    document.cookie = IMPLICIT_STRICT_DOM + "=1; SameSite; Max-Age=100; path=/";
+    document.cookie = STRICT_BECAUSE_INVALID_SAMESITE_VALUE + "=1; SameSite=invalid; Max-Age=100; path=/";
+    document.cookie = LAX_DOM + "=1; SameSite=Lax; Max-Age=100; path=/";
+    document.cookie = NORMAL_DOM + "=1; Max-Age=100; path=/";
+
+    window.addEventListener("message", t.step_func_done(e => {
+        assert_equals(e.data.http[STRICT_DOM], "1", "strict");
+        assert_equals(e.data.http[IMPLICIT_STRICT_DOM], "1", "implicit-strict");
+        assert_equals(e.data.http[STRICT_BECAUSE_INVALID_SAMESITE_VALUE], "1", "strict-because-invalid-SameSite-value");
+        assert_equals(e.data.http[LAX_DOM], "1", "lax");
+        assert_equals(e.data.http[NORMAL_DOM], "1", "normal");
+        assert_equals(normalizeCookie(e.data.document), normalizeCookie(STRICT_BECAUSE_INVALID_SAMESITE_VALUE + "=1; " + IMPLICIT_STRICT_DOM + "=1; " + STRICT_DOM + "=1; " + LAX_DOM + "=1; " + NORMAL_DOM + "=1"));
+        e.source.close();
+    }));
+
+    var f = document.createElement('form');
+    f.action = "http://127.0.0.1:8000/cookies/resources/post-cookies-to-opener.php";
+    f.method = "POST";
+    f.target = "_blank";
+    window.onload = t.step_func(f.submit.bind(f));
+    document.body.appendChild(f);
+}, "'127.0.0.1' is same-site with itself, so samesite cookies are sent via POST.");
+</script>
+</body>
diff --git a/LayoutTests/http/tests/cookies/same-site/popup-same-site-via-cross-site-redirect-expected.txt b/LayoutTests/http/tests/cookies/same-site/popup-same-site-via-cross-site-redirect-expected.txt
new file mode 100644 (file)
index 0000000..39aa2ba
--- /dev/null
@@ -0,0 +1,3 @@
+
+PASS '127.0.0.1' is same-site with itself, so samesite cookies are sent. 
+
diff --git a/LayoutTests/http/tests/cookies/same-site/popup-same-site-via-cross-site-redirect.html b/LayoutTests/http/tests/cookies/same-site/popup-same-site-via-cross-site-redirect.html
new file mode 100644 (file)
index 0000000..a1d46b0
--- /dev/null
@@ -0,0 +1,25 @@
+<!DOCTYPE html>
+<script src="/js-test-resources/testharness.js"></script>
+<script src="/js-test-resources/testharnessreport.js"></script>
+<script src="../resources/testharness-helpers.js"></script>
+<script>
+async_test(t => {
+    clearKnownCookies();
+    document.cookie = STRICT_DOM + "=1; SameSite=Strict; Max-Age=100; path=/";
+    document.cookie = IMPLICIT_STRICT_DOM + "=1; SameSite; Max-Age=100; path=/";
+    document.cookie = STRICT_BECAUSE_INVALID_SAMESITE_VALUE + "=1; SameSite=invalid; Max-Age=100; path=/";
+    document.cookie = LAX_DOM + "=1; SameSite=Lax; Max-Age=100; path=/";
+    document.cookie = NORMAL_DOM + "=1; Max-Age=100; path=/";
+
+    window.addEventListener("message", t.step_func_done(e => {
+        assert_equals(e.data.http[STRICT_DOM], undefined, "strict");
+        assert_equals(e.data.http[IMPLICIT_STRICT_DOM], undefined, "implicit-strict");
+        assert_equals(e.data.http[STRICT_BECAUSE_INVALID_SAMESITE_VALUE], undefined, "strict-because-invalid-SameSite-value");
+        assert_equals(e.data.http[LAX_DOM], "1", "lax");
+        assert_equals(e.data.http[NORMAL_DOM], "1", "normal");
+        assert_equals(normalizeCookie(e.data.document), normalizeCookie(LAX_DOM + "=1; " + NORMAL_DOM + "=1"));
+    }));
+
+    var i = window.open("http://localhost:8000/resources/redirect.php?url=http://127.0.0.1:8000/cookies/resources/post-cookies-to-opener.php");
+}, "'127.0.0.1' is same-site with itself, so samesite cookies are sent.");
+</script>
diff --git a/LayoutTests/http/tests/cookies/same-site/popup-same-site-via-same-site-redirect-expected.txt b/LayoutTests/http/tests/cookies/same-site/popup-same-site-via-same-site-redirect-expected.txt
new file mode 100644 (file)
index 0000000..39aa2ba
--- /dev/null
@@ -0,0 +1,3 @@
+
+PASS '127.0.0.1' is same-site with itself, so samesite cookies are sent. 
+
diff --git a/LayoutTests/http/tests/cookies/same-site/popup-same-site-via-same-site-redirect.html b/LayoutTests/http/tests/cookies/same-site/popup-same-site-via-same-site-redirect.html
new file mode 100644 (file)
index 0000000..21528f4
--- /dev/null
@@ -0,0 +1,25 @@
+<!DOCTYPE html>
+<script src="/js-test-resources/testharness.js"></script>
+<script src="/js-test-resources/testharnessreport.js"></script>
+<script src="../resources/testharness-helpers.js"></script>
+<script>
+async_test(t => {
+    clearKnownCookies();
+    document.cookie = STRICT_DOM + "=1; SameSite=Strict; Max-Age=100; path=/";
+    document.cookie = IMPLICIT_STRICT_DOM + "=1; SameSite; Max-Age=100; path=/";
+    document.cookie = STRICT_BECAUSE_INVALID_SAMESITE_VALUE + "=1; SameSite=invalid; Max-Age=100; path=/";
+    document.cookie = LAX_DOM + "=1; SameSite=Lax; Max-Age=100; path=/";
+    document.cookie = NORMAL_DOM + "=1; Max-Age=100; path=/";
+
+    window.addEventListener("message", t.step_func_done(e => {
+        assert_equals(e.data.http[STRICT_DOM], "1", "strict");
+        assert_equals(e.data.http[IMPLICIT_STRICT_DOM], "1", "implicit-strict");
+        assert_equals(e.data.http[STRICT_BECAUSE_INVALID_SAMESITE_VALUE], "1", "strict-because-invalid-SameSite-value");
+        assert_equals(e.data.http[LAX_DOM], "1", "lax");
+        assert_equals(e.data.http[NORMAL_DOM], "1", "normal");
+        assert_equals(normalizeCookie(e.data.document), normalizeCookie(STRICT_BECAUSE_INVALID_SAMESITE_VALUE + "=1; " + IMPLICIT_STRICT_DOM + "=1; " + STRICT_DOM + "=1; " + LAX_DOM + "=1; " + NORMAL_DOM + "=1"));
+    }));
+
+    var i = window.open("http://127.0.0.1:8000/resources/redirect.php?url=http://127.0.0.1:8000/cookies/resources/post-cookies-to-opener.php");
+}, "'127.0.0.1' is same-site with itself, so samesite cookies are sent.");
+</script>
diff --git a/LayoutTests/http/tests/cookies/same-site/popup-same-site.html b/LayoutTests/http/tests/cookies/same-site/popup-same-site.html
new file mode 100644 (file)
index 0000000..f90a268
--- /dev/null
@@ -0,0 +1,25 @@
+<!DOCTYPE html>
+<script src="/js-test-resources/testharness.js"></script>
+<script src="/js-test-resources/testharnessreport.js"></script>
+<script src="../resources/testharness-helpers.js"></script>
+<script>
+async_test(t => {
+    clearKnownCookies();
+    document.cookie = STRICT_DOM + "=1; SameSite=Strict; Max-Age=100; path=/";
+    document.cookie = IMPLICIT_STRICT_DOM + "=1; SameSite; Max-Age=100; path=/";
+    document.cookie = STRICT_BECAUSE_INVALID_SAMESITE_VALUE + "=1; SameSite=invalid; Max-Age=100; path=/";
+    document.cookie = LAX_DOM + "=1; SameSite=Lax; Max-Age=100; path=/";
+    document.cookie = NORMAL_DOM + "=1; Max-Age=100; path=/";
+
+    window.addEventListener("message", t.step_func_done(e => {
+        assert_equals(e.data.http[STRICT_DOM], "1", "strict");
+        assert_equals(e.data.http[IMPLICIT_STRICT_DOM], "1", "implicit-strict");
+        assert_equals(e.data.http[STRICT_BECAUSE_INVALID_SAMESITE_VALUE], "1", "strict-because-invalid-SameSite-value");
+        assert_equals(e.data.http[LAX_DOM], "1", "lax");
+        assert_equals(e.data.http[NORMAL_DOM], "1", "normal");
+        assert_equals(normalizeCookie(e.data.document), normalizeCookie(STRICT_BECAUSE_INVALID_SAMESITE_VALUE + "=1; " + IMPLICIT_STRICT_DOM + "=1; " + STRICT_DOM + "=1; " + LAX_DOM + "=1; " + NORMAL_DOM + "=1"));
+    }));
+
+    var i = window.open("http://127.0.0.1:8000/cookies/resources/post-cookies-to-opener.php");
+}, "'127.0.0.1' is same-site with itself, so samesite cookies are sent.");
+</script>
diff --git a/LayoutTests/http/tests/cookies/same-site/resources/click-hyperlink.php b/LayoutTests/http/tests/cookies/same-site/resources/click-hyperlink.php
new file mode 100644 (file)
index 0000000..58ab852
--- /dev/null
@@ -0,0 +1,18 @@
+<!DOCTYPE>
+<html>
+<head>
+<script>
+if (window.testRunner)
+    testRunner.waitUntilDone();
+</script>
+</head>
+<body>
+<?php
+    $targetAttribute = "";
+    if (!empty($_GET["target"]))
+        $targetAttribute = 'target="' . $_GET["target"] . '"';
+?>
+<a href="<?php echo $_GET['href']; ?>" <?php echo $targetAttribute; ?>>Click</a>
+<script>document.querySelector("a").click()</script>
+</body>
+</html>
diff --git a/LayoutTests/http/tests/cookies/same-site/resources/echo-iframe-src.php b/LayoutTests/http/tests/cookies/same-site/resources/echo-iframe-src.php
new file mode 100644 (file)
index 0000000..b37bd78
--- /dev/null
@@ -0,0 +1,15 @@
+<!DOCTYPE html>
+<html>
+<head>
+<script>
+if (window.testRunner) {
+    testRunner.dumpAsText();
+    testRunner.dumpChildFramesAsText();
+    testRunner.waitUntilDone();
+}
+</script>
+</head>
+<body>
+<iframe src="<?php echo $_GET['src']; ?>"></iframe>
+</body>
+</html>
diff --git a/LayoutTests/http/tests/cookies/same-site/resources/fetch-after-navigating-iframe-in-cross-origin-page.php b/LayoutTests/http/tests/cookies/same-site/resources/fetch-after-navigating-iframe-in-cross-origin-page.php
new file mode 100644 (file)
index 0000000..62cbc38
--- /dev/null
@@ -0,0 +1,35 @@
+<!DOCTYPE html>
+<html>
+<head>
+<script src="/js-test-resources/js-test.js"></script>
+<script src="../../resources/cookie-utilities.js"></script>
+<script>_setCachedCookiesJSON('<?php echo json_encode($_COOKIE); ?>')</script>
+</head>
+<body>
+<script>
+window.jsTestIsAsync = true;
+
+description("Tests that Same-Site cookies for 127.0.0.1 are not sent with a frame navigation for a frame embedded in a page with a different origin.");
+
+async function checkResult()
+{
+    debug("Cookies sent with HTTP request:");
+    await shouldNotHaveCookie("strict");
+    await shouldNotHaveCookie("implicit-strict");
+    await shouldNotHaveCookie("strict-because-invalid-SameSite-value");
+    await shouldNotHaveCookie("lax");
+
+    debug("<br>Cookies visible in DOM:");
+    shouldNotHaveDOMCookie("strict");
+    shouldNotHaveDOMCookie("implicit-strict");
+    shouldNotHaveDOMCookie("strict-because-invalid-SameSite-value");
+    shouldNotHaveDOMCookie("lax");
+
+    await resetCookies();
+    finishJSTest();
+}
+
+checkResult();
+</script>
+</body>
+</html>
diff --git a/LayoutTests/http/tests/cookies/same-site/resources/fetch-after-top-level-navigation-from-cross-origin-page.php b/LayoutTests/http/tests/cookies/same-site/resources/fetch-after-top-level-navigation-from-cross-origin-page.php
new file mode 100644 (file)
index 0000000..df19fb1
--- /dev/null
@@ -0,0 +1,35 @@
+<!DOCTYPE html>
+<html>
+<head>
+<script src="/js-test-resources/js-test.js"></script>
+<script src="../../resources/cookie-utilities.js"></script>
+<script>_setCachedCookiesJSON('<?php echo json_encode($_COOKIE); ?>')</script>
+</head>
+<body>
+<script>
+window.jsTestIsAsync = true;
+
+description("Tests that a SameSite Lax cookie for 127.0.0.1 is sent with a top-level navigation initiated from a page with a different origin.");
+
+async function checkResult()
+{
+    debug("Cookies sent with HTTP request:");
+    await shouldNotHaveCookie("strict");
+    await shouldNotHaveCookie("implicit-strict");
+    await shouldNotHaveCookie("strict-because-invalid-SameSite-value");
+    await shouldHaveCookieWithValue("lax", "5");
+
+    debug("<br>Cookies visible in DOM:");
+    shouldNotHaveDOMCookie("strict");
+    shouldNotHaveDOMCookie("implicit-strict");
+    shouldNotHaveDOMCookie("strict-because-invalid-SameSite-value");
+    shouldHaveDOMCookieWithValue("lax", "5");
+
+    await resetCookies();
+    finishJSTest();
+}
+
+checkResult();
+</script>
+</body>
+</html>
diff --git a/LayoutTests/http/tests/cookies/same-site/resources/fetch-after-top-level-navigation-initiated-from-iframe-in-cross-origin-page.php b/LayoutTests/http/tests/cookies/same-site/resources/fetch-after-top-level-navigation-initiated-from-iframe-in-cross-origin-page.php
new file mode 100644 (file)
index 0000000..677b78c
--- /dev/null
@@ -0,0 +1,35 @@
+<!DOCTYPE html>
+<html>
+<head>
+<script src="/js-test-resources/js-test.js"></script>
+<script src="../../resources/cookie-utilities.js"></script>
+<script>_setCachedCookiesJSON('<?php echo json_encode($_COOKIE); ?>')</script>
+</head>
+<body>
+<script>
+window.jsTestIsAsync = true;
+
+description("Tests that a SameSite Lax cookie for 127.0.0.1 is sent with a top-level navigation initiated from a frame embedded in a page with a different origin.");
+
+async function checkResult()
+{
+    debug("Cookies sent with HTTP request:");
+    await shouldNotHaveCookie("strict");
+    await shouldNotHaveCookie("implicit-strict");
+    await shouldNotHaveCookie("strict-because-invalid-SameSite-value");
+    await shouldHaveCookieWithValue("lax", "4");
+
+    debug("<br>Cookies visible in DOM:");
+    shouldNotHaveDOMCookie("strict");
+    shouldNotHaveDOMCookie("implicit-strict");
+    shouldNotHaveDOMCookie("strict-because-invalid-SameSite-value");
+    shouldHaveDOMCookieWithValue("lax", "4");
+
+    await resetCookies();
+    finishJSTest();
+}
+
+checkResult();
+</script>
+</body>
+</html>
diff --git a/LayoutTests/http/tests/cookies/same-site/resources/fetch-in-cross-origin-iframe.html b/LayoutTests/http/tests/cookies/same-site/resources/fetch-in-cross-origin-iframe.html
new file mode 100644 (file)
index 0000000..d58a733
--- /dev/null
@@ -0,0 +1,36 @@
+<!DOCTYPE html>
+<html>
+<head>
+<script src="/js-test-resources/js-test.js"></script>
+<script src="../../resources/cookie-utilities.js"></script>
+</head>
+<body>
+<script>
+window.jsTestIsAsync = true;
+
+description("Tests that Same-Site cookies for 127.0.0.1 are not sent with a request initiated from a iframe with a different origin.");
+
+async function checkResult()
+{
+    setBaseURLWhenFetchingCookies("http://127.0.0.1:8000");
+
+    debug("Cookies sent with HTTP request:");
+    await shouldNotHaveCookie("strict");
+    await shouldNotHaveCookie("implicit-strict");
+    await shouldNotHaveCookie("strict-because-invalid-SameSite-value");
+    await shouldNotHaveCookie("lax");
+
+    debug("<br>Cookies visible in DOM:");
+    shouldNotHaveDOMCookie("strict");
+    shouldNotHaveDOMCookie("implicit-strict");
+    shouldNotHaveDOMCookie("strict-because-invalid-SameSite-value");
+    shouldNotHaveDOMCookie("lax");
+
+    await resetCookies();
+    finishJSTest();
+}
+
+checkResult();
+</script>
+</body>
+</html>
diff --git a/LayoutTests/http/tests/cookies/same-site/resources/fetch-in-cross-origin-service-worker.html b/LayoutTests/http/tests/cookies/same-site/resources/fetch-in-cross-origin-service-worker.html
new file mode 100644 (file)
index 0000000..d5941c7
--- /dev/null
@@ -0,0 +1,36 @@
+<!DOCTYPE html>
+<html>
+<head>
+<script src="/js-test-resources/js-test.js"></script>
+<script src="../../resources/cookie-utilities.js"></script>
+</head>
+<body>
+<script>
+window.jsTestIsAsync = true;
+
+description("Tests that Same-Site cookies for 127.0.0.1 are not sent with a request initiated from an iframe- and processed by a service worker- with a different origin.");
+
+async function checkResult()
+{
+    setBaseURLWhenFetchingCookies("http://127.0.0.1:8000");
+
+    debug("Cookies sent with HTTP request:");
+    await shouldNotHaveCookie("strict");
+    await shouldNotHaveCookie("implicit-strict");
+    await shouldNotHaveCookie("strict-because-invalid-SameSite-value");
+    await shouldNotHaveCookie("lax");
+
+    debug("<br>Cookies visible in DOM:");
+    shouldNotHaveDOMCookie("strict");
+    shouldNotHaveDOMCookie("implicit-strict");
+    shouldNotHaveDOMCookie("strict-because-invalid-SameSite-value");
+    shouldNotHaveDOMCookie("lax");
+
+    await resetCookies();
+    finishJSTest();
+}
+
+checkResult();
+</script>
+</body>
+</html>
diff --git a/LayoutTests/http/tests/cookies/same-site/resources/fetch-in-cross-origin-worker.js b/LayoutTests/http/tests/cookies/same-site/resources/fetch-in-cross-origin-worker.js
new file mode 100644 (file)
index 0000000..3f13d73
--- /dev/null
@@ -0,0 +1,14 @@
+importScripts("/js-test-resources/js-test.js");
+importScripts("../../resources/cookie-utilities.js");
+
+async function checkResult()
+{
+    setBaseURLWhenFetchingCookies("http://127.0.0.1:8000");
+    await shouldNotHaveCookie("strict");
+    await shouldNotHaveCookie("implicit-strict");
+    await shouldNotHaveCookie("strict-because-invalid-SameSite-value");
+    await shouldNotHaveCookie("lax");
+    finishJSTest();
+}
+
+checkResult();
diff --git a/LayoutTests/http/tests/cookies/same-site/resources/fetch-in-same-origin-service-worker.php b/LayoutTests/http/tests/cookies/same-site/resources/fetch-in-same-origin-service-worker.php
new file mode 100644 (file)
index 0000000..5ee9a65
--- /dev/null
@@ -0,0 +1,35 @@
+<!DOCTYPE html>
+<html>
+<head>
+<script src="/js-test-resources/js-test.js"></script>
+<script src="../../resources/cookie-utilities.js"></script>
+<script>_setCachedCookiesJSON('<?php echo json_encode($_COOKIE); ?>')</script>
+</head>
+<body>
+<script>
+window.jsTestIsAsync = true;
+
+description("Tests that Same-Site cookies for 127.0.0.1 are sent with a request initiated from an iframe- and processed by a service worker- with the same origin.");
+
+async function checkResult()
+{
+    debug("Cookies sent with HTTP request:");
+    await shouldHaveCookieWithValue("strict", "11");
+    await shouldHaveCookieWithValue("implicit-strict", "11");
+    await shouldHaveCookieWithValue("strict-because-invalid-SameSite-value", "11");
+    await shouldHaveCookieWithValue("lax", "11");
+
+    debug("<br>Cookies visible in DOM:");
+    shouldHaveDOMCookieWithValue("strict", "11");
+    shouldHaveDOMCookieWithValue("implicit-strict", "11");
+    shouldHaveDOMCookieWithValue("strict-because-invalid-SameSite-value", "11");
+    shouldHaveDOMCookieWithValue("lax", "11");
+
+    await resetCookies();
+    finishJSTest();
+}
+
+checkResult();
+</script>
+</body>
+</html>
diff --git a/LayoutTests/http/tests/cookies/same-site/resources/fetch-in-same-origin-worker.js b/LayoutTests/http/tests/cookies/same-site/resources/fetch-in-same-origin-worker.js
new file mode 100644 (file)
index 0000000..79a860a
--- /dev/null
@@ -0,0 +1,13 @@
+importScripts("/js-test-resources/js-test.js");
+importScripts("../../resources/cookie-utilities.js");
+
+async function checkResult()
+{
+    await shouldHaveCookieWithValue("strict", "12");
+    await shouldHaveCookieWithValue("implicit-strict", "12");
+    await shouldHaveCookieWithValue("strict-because-invalid-SameSite-value", "12");
+    await shouldHaveCookieWithValue("lax", "12");
+    finishJSTest();
+}
+
+checkResult();
diff --git a/LayoutTests/http/tests/cookies/same-site/resources/passthrough-service-worker.js b/LayoutTests/http/tests/cookies/same-site/resources/passthrough-service-worker.js
new file mode 100644 (file)
index 0000000..0084c18
--- /dev/null
@@ -0,0 +1,3 @@
+self.addEventListener("fetch", (event) => {
+    event.respondWith(fetch(event.request.url, { credentials: "include" }));
+});
index a6fa18a..c0e6da9 100644 (file)
@@ -146,6 +146,8 @@ http/wpt/cache-storage [ Skip ]
 http/tests/cache-storage [ Skip ]
 imported/w3c/web-platform-tests/streams/readable-byte-streams/detached-buffers.serviceworker.https.html [ Skip ]
 http/tests/appcache/main-resource-redirect-with-sw.html [ Skip ]
+http/tests/cookies/same-site/fetch-in-cross-origin-service-worker.html [ Skip ]
+http/tests/cookies/same-site/fetch-in-same-origin-service-worker.html [ Skip ]
 
 # Skip WebRTC for now in WK1
 imported/w3c/web-platform-tests/webrtc [ Skip ]
index c29e10c..4002bec 100644 (file)
@@ -1,3 +1,172 @@
+2018-04-23  Daniel Bates  <dabates@apple.com>
+
+        Implement Same-Site cookies
+        https://bugs.webkit.org/show_bug.cgi?id=159464
+        <rdar://problem/27196358>
+
+        Reviewed by Brent Fulgham.
+
+        Implements support for Same-Site cookies as per <https://tools.ietf.org/html/draft-ietf-httpbis-cookie-same-site-00>.
+        The implementation is materially consistent with the spec. though implements the computation
+        for a document's "site for cookies" indirectly as part of loading its frame. This is done to
+        avoid traversing the frame tree on each subresource request initiated by the document or one
+        of its workers. We take advantage of the fact that Web Workers and Service Workers use their
+        host document's loader to load resources on their behalf to use the correct "site for cookies"
+        for requests (e.g. fetch()) initiating by them without the need to duplicate and store the
+        host document's "site for cookies" in the worker's script execution context.
+
+        The implementation differs from the spec. in the handling of about: URLs and the empty URL
+        and makes the implementation in WebKit match the behavior of Chrome and Firefox as well as
+        consistent with origin inheritance as described in <https://html.spec.whatwg.org/multipage/browsers.html#origin>
+        (16 April 2018). Specifically, requests to about:blank, about:srcdoc and the empty URL ("")
+        are treated as same-site because these URLs inherit their origin from their owner.
+
+        Tests: http/tests/cookies/same-site/fetch-after-navigating-iframe-in-cross-origin-page.html
+               http/tests/cookies/same-site/fetch-after-top-level-navigation-from-cross-origin-page.html
+               http/tests/cookies/same-site/fetch-after-top-level-navigation-initiated-from-iframe-in-cross-origin-page.html
+               http/tests/cookies/same-site/fetch-cookies-set-in-about-blank-iframe.html
+               http/tests/cookies/same-site/fetch-in-about-blank-page.html
+               http/tests/cookies/same-site/fetch-in-cross-origin-iframe.html
+               http/tests/cookies/same-site/fetch-in-cross-origin-page.html
+               http/tests/cookies/same-site/fetch-in-cross-origin-service-worker.html
+               http/tests/cookies/same-site/fetch-in-cross-origin-worker.html
+               http/tests/cookies/same-site/fetch-in-same-origin-page.html
+               http/tests/cookies/same-site/fetch-in-same-origin-service-worker.html
+               http/tests/cookies/same-site/fetch-in-same-origin-srcdoc-iframe.html
+               http/tests/cookies/same-site/fetch-in-same-origin-worker.html
+               http/tests/cookies/same-site/popup-cross-site-post.html
+               http/tests/cookies/same-site/popup-cross-site.html
+               http/tests/cookies/same-site/popup-same-site-post.html
+               http/tests/cookies/same-site/popup-same-site-via-cross-site-redirect.html
+               http/tests/cookies/same-site/popup-same-site-via-same-site-redirect.html
+               http/tests/cookies/same-site/popup-same-site.html
+
+        * Sources.txt: Add source file SameSiteInfo.cpp.
+        * WebCore.xcodeproj/project.pbxproj: Add source files SameSiteInfo.{cpp, h}.
+        * dom/Document.cpp:
+        (WebCore::Document::initSecurityContext): Modified to call SecurityPolicy::shouldInheritSecurityOriginFromOwner().
+        (WebCore::Document::shouldInheritContentSecurityPolicyFromOwner const): Ditto.
+        (WebCore::shouldInheritSecurityOriginFromOwner): Deleted; moved to SecurityPolicy.
+        * dom/Document.h:
+        (WebCore::Document::firstPartyForSameSiteCookies const): Added.
+        (WebCore::Document::setFirstPartyForSameSiteCookies): Added.
+        * loader/CookieJar.cpp:
+        (WebCore::sameSiteInfo): Returns the same-site info for the request used to load the specified document.
+        (WebCore::cookies): Pass the same-site info down to the platform.
+        (WebCore::cookieRequestHeaderFieldProxy): Ditto.
+        (WebCore::setCookies): Ditto.
+        (WebCore::cookieRequestHeaderFieldValue): Ditto.
+        (WebCore::getRawCookies): Ditto.
+        * loader/DocumentLoader.cpp:
+        (WebCore::DocumentLoader::willSendRequest): Add same-site info to the request.
+        (WebCore::DocumentLoader::startLoadingMainResource): Update a FIXME comment to explain that
+        we can simplify ResourceRequestBase if we can remove the call to addExtraFieldsToMainResourceRequest()
+        here. Specifically, we would not need to differentiate between a request with an unspecified
+        same-site state (default state of a new request) from a request whose same-site state has
+        been explicitly set if we can assume that the same-site state of a request is set exactly
+        once. In absence of this guarantee we need an "unspecified" state to avoid overriding existing
+        same-site information computed with a null initiating document (the case of a new address bar
+        initiated load) from a load initiated by the document associated with this loader.
+        * loader/FrameLoader.cpp:
+        (WebCore::FrameLoader::setFirstPartyForCookies): Modified to also update the first party for
+        same-site cookies ("site for cookies").
+        (WebCore::FrameLoader::load): Add same-site info to the request.
+        (WebCore::FrameLoader::reload): Ditto.
+        (WebCore::FrameLoader::setOriginalURLForDownloadRequest): Ditto.
+        (WebCore::FrameLoader::addExtraFieldsToRequest): If the request does not already have
+        same-site info then compute it and add it to the request. Mark main frame main resource
+        requests as a "top-site".
+        (WebCore::FrameLoader::addSameSiteInfoToRequestIfNeeded): Implements the "'Same-site' and 'cross-site'
+        Requests" algorithm from <https://tools.ietf.org/html/draft-ietf-httpbis-cookie-same-site-00#section-2.1>.
+        (WebCore::createWindow): Add same-site info to the request.
+        * loader/FrameLoader.h:
+        * loader/ResourceLoader.cpp:
+        (WebCore::ResourceLoader::init): Ditto.
+        * page/DOMWindow.cpp:
+        (WebCore::DOMWindow::createWindow): Ditto.
+        * page/SecurityPolicy.cpp:
+        (WebCore::SecurityPolicy::shouldInheritSecurityOriginFromOwner): Moved from Document.cpp.
+        * page/SecurityPolicy.h:
+        * platform/CookiesStrategy.h:
+        * platform/network/CacheValidation.cpp:
+        (WebCore::headerValueForVary): Pass the same-site info down to the platform.
+        * platform/network/CookieRequestHeaderFieldProxy.h:
+        (WebCore::CookieRequestHeaderFieldProxy::encode const): Encode same-site bits.
+        (WebCore::CookieRequestHeaderFieldProxy::decode): Decode same-site bits.
+        * platform/network/PlatformCookieJar.h:
+        * platform/network/ResourceRequestBase.cpp:
+        (WebCore::ResourceRequestBase::setAsIsolatedCopy): Added.
+        (WebCore::ResourceRequestBase::isSameSite const): Added.
+        (WebCore::ResourceRequestBase::setIsSameSite): Added.
+        (WebCore::ResourceRequestBase::isTopSite const): Added.
+        (WebCore::ResourceRequestBase::setIsTopSite): Added.
+        (WebCore::equalIgnoringHeaderFields):
+        * platform/network/ResourceRequestBase.h:
+        (WebCore::ResourceRequestBase::isSameSiteUnspecified const): Added. See comment for DocumentLoader::startLoadingMainResource()
+        for more details.
+        (WebCore::registrableDomainsAreEqual): Added.
+        (WebCore::ResourceRequestBase::encodeBase const): Encode same-site bits.
+        (WebCore::ResourceRequestBase::decodeBase): Decode same-site bits.
+        * platform/network/SameSiteInfo.cpp: Added.
+        (WebCore::SameSiteInfo::create):
+        * platform/network/SameSiteInfo.h: Added.
+        (WebCore::SameSiteInfo::encode const):
+        (WebCore::SameSiteInfo::decode):
+        * platform/network/cf/CookieJarCFNet.cpp:
+        (WebCore::setCookiesFromDOM): Pass Same-Site info down.
+        (WebCore::cookiesForDOM): Ditto.
+        (WebCore::cookieRequestHeaderFieldValue): Ditto.
+        (WebCore::getRawCookies): Ditto.
+        * platform/network/cf/ResourceRequestCFNet.cpp:
+        (WebCore::siteForCookies): Added.
+        (WebCore::ResourceRequest::doUpdatePlatformRequest): Update platform request with same-site info.
+        (WebCore::ResourceRequest::doUpdateResourceRequest): Ditto.
+        * platform/network/cocoa/ResourceRequestCocoa.mm:
+        (WebCore::ResourceRequest::doUpdateResourceRequest): Ditto.
+        (WebCore::siteForCookies): Added.
+        (WebCore::ResourceRequest::doUpdatePlatformRequest): Update platform request with same-site info.
+        * platform/network/curl/CookieJarCurl.cpp:
+        (WebCore::cookiesForDOM): Pass Same-Site info down.
+        (WebCore::setCookiesFromDOM): Ditto.
+        (WebCore::cookieRequestHeaderFieldValue): Ditto.
+        (WebCore::getRawCookies): Ditto.
+        * platform/network/curl/CookieJarCurl.h:
+        * platform/network/curl/CookieJarCurlDatabase.cpp:
+        (WebCore::CookieJarCurlDatabase::setCookiesFromDOM const): Ditto.
+        (WebCore::CookieJarCurlDatabase::cookiesForDOM const): Ditto.
+        (WebCore::CookieJarCurlDatabase::cookieRequestHeaderFieldValue const): Ditto.
+        (WebCore::CookieJarCurlDatabase::getRawCookies const): Ditto.
+        * platform/network/curl/CookieJarCurlDatabase.h:
+        * platform/network/curl/ResourceHandleCurl.cpp:
+        (WebCore::ResourceHandle::createCurlRequest): Ditto.
+        * platform/network/mac/CookieJarMac.mm:
+        (WebCore::cookiesForURL): Added; shared function to return the cookies based on the specified criterion. 
+        (WebCore::setHTTPCookiesForURL): Moved from the bottom of the file to top to be closer to the other
+        CFNetwork helper functions. Modified to support fetching same-site cookies.
+        (WebCore::httpCookiesForURL): Moved to be under setHTTPCookiesForURL(). Modified to call cookiesForURL().
+        Note the SPI used in cookiesForURL() apply the same criterion for whether to fetch secure cookies as we
+        were computing here. That is, the CFNetwork SPI only returns secure cookies if the specified URL's scheme
+        case-insensitively matches "https".
+        (WebCore::cookiesInPartitionForURL): Wrote in terms of cookiesForURL().
+        (WebCore::cookiesForSession): Pass the Same-Site info.
+        (WebCore::cookiesForDOM): Ditto.
+        (WebCore::cookieRequestHeaderFieldValue): Ditto.
+        (WebCore::setCookiesFromDOM): Ditto.
+        (WebCore::getRawCookies): Ditto.
+        (WebCore::deleteCookie): Pass std::nullopt for the Same-Site info so that we do not consider the SameSite
+        attribute when fetching cookies to delete.
+        * platform/network/soup/CookieJarSoup.cpp:
+        (WebCore::setCookiesFromDOM): Pass the Same-Site info.
+        (WebCore::cookiesForDOM): Ditto.
+        (WebCore::cookieRequestHeaderFieldValue): Ditto.
+        (WebCore::getRawCookies): Ditto.
+        * workers/service/context/ServiceWorkerThreadProxy.cpp:
+        (WebCore::createPageForServiceWorker): Set the first party for same site cookies ("site for cookies") to
+        the script URL.
+        * xml/XSLTProcessor.cpp:
+        (WebCore::XSLTProcessor::createDocumentFromSource): Copy the first party for same-site cookies to the
+        new document.
+
 2018-04-23  Chris Dumez  <cdumez@apple.com>
 
         HTML String load cannot be prevented by responding 'Cancel' asynchronously in decidePolicyForNavigationAction
index 07dac18..303ac8e 100644 (file)
@@ -1,3 +1,15 @@
+2018-04-23  Daniel Bates  <dabates@apple.com>
+
+        Implement Same-Site cookies
+        https://bugs.webkit.org/show_bug.cgi?id=159464
+        <rdar://problem/27196358>
+
+        Reviewed by Brent Fulgham.
+
+        Forward declare some SPI.
+
+        * pal/spi/cf/CFNetworkSPI.h:
+
 2018-04-20  Tim Horton  <timothy_horton@apple.com>
 
         Adjust geolocation feature flag
index 9ef1caa..0b684c8 100644 (file)
@@ -34,7 +34,6 @@
 #define USE_CFNETWORK_IGNORE_HSTS 1
 #endif
 
-
 #if PLATFORM(WIN) || USE(APPLE_INTERNAL_SDK)
 
 #include <CFNetwork/CFHTTPCookiesPriv.h>
@@ -124,16 +123,6 @@ typedef void (^CFCachedURLResponseCallBackBlock)(CFCachedURLResponseRef);
 - (NSDate *)_lastModifiedDate;
 @end
 
-@interface NSURLSessionTask (TimingData)
-- (NSDictionary *)_timingData;
-@end
-
-#if (PLATFORM(MAC) && __MAC_OS_X_VERSION_MIN_REQUIRED >= 101300) || (PLATFORM(IOS) && __IPHONE_OS_VERSION_MIN_REQUIRED >= 110000)
-@interface NSURLSessionTask (ResourceHints)
-@property (nonatomic, assign) BOOL _preconnect;
-@end
-#endif
-
 @interface NSHTTPCookie ()
 - (CFHTTPCookieRef)_CFHTTPCookie;
 + (CFArrayRef __nullable)_ns2cfCookies:(NSArray * __nullable)nsCookies CF_RETURNS_RETAINED;
@@ -170,13 +159,21 @@ typedef void (^CFCachedURLResponseCallBackBlock)(CFCachedURLResponseRef);
 @end
 
 @interface NSHTTPCookieStorage ()
-- (void)_getCookiesForURL:(NSURL *)url mainDocumentURL:(NSURL *)mainDocumentURL partition:(NSString *)partition completionHandler:(void (^)(NSArray *))completionHandler;
 - (id)_initWithIdentifier:(NSString *)identifier private:(bool)isPrivate;
+- (void)_getCookiesForURL:(NSURL *)url mainDocumentURL:(NSURL *)mainDocumentURL partition:(NSString *)partition completionHandler:(void (^)(NSArray *))completionHandler;
+- (void)_getCookiesForURL:(NSURL *)url mainDocumentURL:(NSURL *)mainDocumentURL partition:(NSString *)partition policyProperties:(NSDictionary*)props completionHandler:(void (^)(NSArray *))completionHandler;
+- (void)_setCookies:(NSArray *)cookies forURL:(NSURL *)URL mainDocumentURL:(NSURL *)mainDocumentURL policyProperties:(NSDictionary*) props;
 @end
 
 @interface NSURLSessionTask ()
+- (NSDictionary *)_timingData;
 @property (readwrite, copy) NSString *_pathToDownloadTaskFile;
 @property (copy) NSString *_storagePartitionIdentifier;
+@property (nullable, readwrite, retain) NSURL *_siteForCookies;
+@property (readwrite) BOOL _isTopLevelNavigation;
+#if (PLATFORM(MAC) && __MAC_OS_X_VERSION_MIN_REQUIRED >= 101300) || (PLATFORM(IOS) && __IPHONE_OS_VERSION_MIN_REQUIRED >= 110000)
+@property (nonatomic, assign) BOOL _preconnect;
+#endif
 @end
 
 #endif // defined(__OBJC__)
index 5c74a61..8c8f77a 100644 (file)
@@ -1744,6 +1744,7 @@ platform/network/ResourceHandle.cpp
 platform/network/ResourceHandleClient.cpp
 platform/network/ResourceRequestBase.cpp
 platform/network/ResourceResponseBase.cpp
+platform/network/SameSiteInfo.cpp
 platform/network/SocketStreamHandle.cpp
 platform/network/SocketStreamHandleImpl.cpp
 platform/network/SynchronousLoaderClient.cpp
index ec50fd1..eabec00 100644 (file)
                CE799FA41C6A503A0097B518 /* ContentSecurityPolicyDirective.h in Headers */ = {isa = PBXBuildFile; fileRef = CE799FA21C6A503A0097B518 /* ContentSecurityPolicyDirective.h */; };
                CE799FA81C6A50570097B518 /* ContentSecurityPolicyMediaListDirective.h in Headers */ = {isa = PBXBuildFile; fileRef = CE799FA61C6A50570097B518 /* ContentSecurityPolicyMediaListDirective.h */; };
                CE799FAC1C6A50660097B518 /* ContentSecurityPolicySourceListDirective.h in Headers */ = {isa = PBXBuildFile; fileRef = CE799FAA1C6A50660097B518 /* ContentSecurityPolicySourceListDirective.h */; };
+               CE7A6C28208537E200FA2B46 /* SameSiteInfo.h in Headers */ = {isa = PBXBuildFile; fileRef = CE7A6C26208537E200FA2B46 /* SameSiteInfo.h */; settings = {ATTRIBUTES = (Private, ); }; };
                CE7B2DB31586ABAD0098B3FA /* AlternativeTextUIController.h in Headers */ = {isa = PBXBuildFile; fileRef = CE7B2DAF1586ABAD0098B3FA /* AlternativeTextUIController.h */; settings = {ATTRIBUTES = (Private, ); }; };
                CE7B2DB51586ABAD0098B3FA /* TextAlternativeWithRange.h in Headers */ = {isa = PBXBuildFile; fileRef = CE7B2DB11586ABAD0098B3FA /* TextAlternativeWithRange.h */; settings = {ATTRIBUTES = (Private, ); }; };
                CE7E17831C83A49100AD06AF /* ContentSecurityPolicyHash.h in Headers */ = {isa = PBXBuildFile; fileRef = CE7E17821C83A49100AD06AF /* ContentSecurityPolicyHash.h */; settings = {ATTRIBUTES = (Private, ); }; };
                CE799FA61C6A50570097B518 /* ContentSecurityPolicyMediaListDirective.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ContentSecurityPolicyMediaListDirective.h; path = csp/ContentSecurityPolicyMediaListDirective.h; sourceTree = "<group>"; };
                CE799FA91C6A50660097B518 /* ContentSecurityPolicySourceListDirective.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = ContentSecurityPolicySourceListDirective.cpp; path = csp/ContentSecurityPolicySourceListDirective.cpp; sourceTree = "<group>"; };
                CE799FAA1C6A50660097B518 /* ContentSecurityPolicySourceListDirective.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ContentSecurityPolicySourceListDirective.h; path = csp/ContentSecurityPolicySourceListDirective.h; sourceTree = "<group>"; };
+               CE7A6C26208537E200FA2B46 /* SameSiteInfo.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SameSiteInfo.h; sourceTree = "<group>"; };
+               CE7A6C27208537E200FA2B46 /* SameSiteInfo.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = SameSiteInfo.cpp; sourceTree = "<group>"; };
                CE7B2DAF1586ABAD0098B3FA /* AlternativeTextUIController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AlternativeTextUIController.h; sourceTree = "<group>"; };
                CE7B2DB01586ABAD0098B3FA /* AlternativeTextUIController.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = AlternativeTextUIController.mm; sourceTree = "<group>"; };
                CE7B2DB11586ABAD0098B3FA /* TextAlternativeWithRange.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TextAlternativeWithRange.h; sourceTree = "<group>"; };
                                514C76680CE923A1007EF3CD /* ResourceRequestBase.h */,
                                514C76690CE923A1007EF3CD /* ResourceResponseBase.cpp */,
                                514C766A0CE923A1007EF3CD /* ResourceResponseBase.h */,
+                               CE7A6C27208537E200FA2B46 /* SameSiteInfo.cpp */,
+                               CE7A6C26208537E200FA2B46 /* SameSiteInfo.h */,
                                510D4A2E103165EE0049EA54 /* SocketStreamError.h */,
                                510D4A30103165EE0049EA54 /* SocketStreamHandle.cpp */,
                                510D4A31103165EE0049EA54 /* SocketStreamHandle.h */,
                                1A569D250D7E2B82007C3983 /* runtime_root.h in Headers */,
                                293EAE1F1356B2FE0067ACF9 /* RuntimeApplicationChecks.h in Headers */,
                                7C52229E1E1DAE47002CB8F7 /* RuntimeEnabledFeatures.h in Headers */,
+                               CE7A6C28208537E200FA2B46 /* SameSiteInfo.h in Headers */,
                                CDD7089718359F6F002B3DC6 /* SampleMap.h in Headers */,
                                49E911CB0EF86D47009D0CAF /* ScaleTransformOperation.h in Headers */,
                                5DFE8F570D16477C0076E937 /* ScheduledAction.h in Headers */,
index 86f83e7..1d58572 100644 (file)
@@ -5494,20 +5494,6 @@ ExceptionOr<Ref<XPathResult>> Document::evaluate(const String& expression, Node*
     return m_xpathEvaluator->evaluate(expression, contextNode, WTFMove(resolver), type, result);
 }
 
-static bool shouldInheritSecurityOriginFromOwner(const URL& url)
-{
-    // Paraphrased from <https://html.spec.whatwg.org/multipage/browsers.html#origin> (8 July 2016)
-    //
-    // If a Document has the address "about:blank"
-    //      The origin of the document is the origin it was assigned when its browsing context was created.
-    // If a Document has the address "about:srcdoc"
-    //      The origin of the document is the origin of its parent document.
-    //
-    // Note: We generalize this to invalid URLs because we treat such URLs as about:blank.
-    //
-    return url.isEmpty() || equalIgnoringASCIICase(url.string(), blankURL()) || equalLettersIgnoringASCIICase(url.string(), "about:srcdoc");
-}
-
 void Document::initSecurityContext()
 {
     if (haveInitializedSecurityOrigin()) {
@@ -5584,7 +5570,7 @@ void Document::initSecurityContext()
     if (parentDocument)
         setStrictMixedContentMode(parentDocument->isStrictMixedContentMode());
 
-    if (!shouldInheritSecurityOriginFromOwner(m_url))
+    if (!SecurityPolicy::shouldInheritSecurityOriginFromOwner(m_url))
         return;
 
     // If we do not obtain a meaningful origin from the URL, then we try to
@@ -5628,7 +5614,7 @@ void Document::initSecurityContext()
 bool Document::shouldInheritContentSecurityPolicyFromOwner() const
 {
     ASSERT(m_frame);
-    if (shouldInheritSecurityOriginFromOwner(m_url))
+    if (SecurityPolicy::shouldInheritSecurityOriginFromOwner(m_url))
         return true;
     if (!isPluginDocument())
         return false;
index a4fb02f..4b62486 100644 (file)
@@ -918,6 +918,14 @@ public:
     //
     const URL& firstPartyForCookies() const { return m_firstPartyForCookies; }
     void setFirstPartyForCookies(const URL& url) { m_firstPartyForCookies = url; }
+
+    // The full URL corresponding to the "site for cookies" in the Same-Site Cookies spec.,
+    // <https://tools.ietf.org/html/draft-ietf-httpbis-cookie-same-site-00>. It is either
+    // the URL of the top-level document or the null URL depending on whether the registrable
+    // domain of this document's URL matches the registrable domain of its parent's/opener's
+    // URL. For the top-level document, it is set to the document's URL.
+    const URL& firstPartyForSameSiteCookies() const { return m_firstPartyForSameSiteCookies; }
+    void setFirstPartyForSameSiteCookies(const URL& url) { m_firstPartyForSameSiteCookies = url; }
     
     // The following implements the rule from HTML 4 for what valid names are.
     // To get this right for all the XML cases, we probably have to improve this or move it
@@ -1551,6 +1559,7 @@ private:
     URL m_baseElementURL; // The URL set by the <base> element.
     URL m_cookieURL; // The URL to use for cookie access.
     URL m_firstPartyForCookies; // The policy URL for third-party cookie blocking.
+    URL m_firstPartyForSameSiteCookies; // The policy URL for Same-Site cookies.
 
     // Document.documentURI:
     // Although URL-like, Document.documentURI can actually be set to any
index a24fd69..49b798d 100644 (file)
@@ -29,6 +29,7 @@
 #include "CookieRequestHeaderFieldProxy.h"
 #include "CookiesStrategy.h"
 #include "Document.h"
+#include "DocumentLoader.h"
 #include "Frame.h"
 #include "FrameLoader.h"
 #include "FrameLoaderClient.h"
@@ -36,6 +37,7 @@
 #include "NetworkingContext.h"
 #include "PlatformCookieJar.h"
 #include "PlatformStrategies.h"
+#include "SameSiteInfo.h"
 #include <wtf/SystemTracing.h>
 
 namespace WebCore {
@@ -61,6 +63,13 @@ static IncludeSecureCookies shouldIncludeSecureCookies(const Document& document,
     return (url.protocolIs("https") && !document.foundMixedContent().contains(SecurityContext::MixedContentType::Active)) ? IncludeSecureCookies::Yes : IncludeSecureCookies::No;
 }
 
+static inline SameSiteInfo sameSiteInfo(const Document& document)
+{
+    if (auto* loader = document.loader())
+        return SameSiteInfo::create(loader->request());
+    return { };
+}
+
 String cookies(Document& document, const URL& url)
 {
     TraceScope scope(FetchCookiesStart, FetchCookiesEnd);
@@ -70,9 +79,9 @@ String cookies(Document& document, const URL& url)
     std::pair<String, bool> result;
     auto frame = document.frame();
     if (frame)
-        result = platformStrategies()->cookiesStrategy()->cookiesForDOM(storageSession(document), document.firstPartyForCookies(), url, frame->loader().client().frameID(), frame->loader().client().pageID(), includeSecureCookies);
+        result = platformStrategies()->cookiesStrategy()->cookiesForDOM(storageSession(document), document.firstPartyForCookies(), sameSiteInfo(document), url, frame->loader().client().frameID(), frame->loader().client().pageID(), includeSecureCookies);
     else
-        result = platformStrategies()->cookiesStrategy()->cookiesForDOM(storageSession(document), document.firstPartyForCookies(), url, std::nullopt, std::nullopt, includeSecureCookies);
+        result = platformStrategies()->cookiesStrategy()->cookiesForDOM(storageSession(document), document.firstPartyForCookies(), sameSiteInfo(document), url, std::nullopt, std::nullopt, includeSecureCookies);
 
     if (result.second)
         document.setSecureCookiesAccessed();
@@ -87,6 +96,7 @@ CookieRequestHeaderFieldProxy cookieRequestHeaderFieldProxy(const Document& docu
     CookieRequestHeaderFieldProxy proxy;
     proxy.sessionID = storageSession(document).sessionID();
     proxy.firstParty = document.firstPartyForCookies();
+    proxy.sameSiteInfo = sameSiteInfo(document);
     proxy.url = url;
     proxy.includeSecureCookies = shouldIncludeSecureCookies(document, url);
     if (auto* frame = document.frame()) {
@@ -100,9 +110,9 @@ void setCookies(Document& document, const URL& url, const String& cookieString)
 {
     auto frame = document.frame();
     if (frame)
-        platformStrategies()->cookiesStrategy()->setCookiesFromDOM(storageSession(document), document.firstPartyForCookies(), url, frame->loader().client().frameID(), frame->loader().client().pageID(), cookieString);
+        platformStrategies()->cookiesStrategy()->setCookiesFromDOM(storageSession(document), document.firstPartyForCookies(), sameSiteInfo(document), url, frame->loader().client().frameID(), frame->loader().client().pageID(), cookieString);
     else
-        platformStrategies()->cookiesStrategy()->setCookiesFromDOM(storageSession(document), document.firstPartyForCookies(), url, std::nullopt, std::nullopt, cookieString);
+        platformStrategies()->cookiesStrategy()->setCookiesFromDOM(storageSession(document), document.firstPartyForCookies(), sameSiteInfo(document), url, std::nullopt, std::nullopt, cookieString);
 }
 
 bool cookiesEnabled(const Document& document)
@@ -117,9 +127,9 @@ String cookieRequestHeaderFieldValue(Document& document, const URL& url)
     std::pair<String, bool> result;
     auto frame = document.frame();
     if (frame)
-        result = platformStrategies()->cookiesStrategy()->cookieRequestHeaderFieldValue(storageSession(document), document.firstPartyForCookies(), url, frame->loader().client().frameID(), frame->loader().client().pageID(), includeSecureCookies);
+        result = platformStrategies()->cookiesStrategy()->cookieRequestHeaderFieldValue(storageSession(document), document.firstPartyForCookies(), sameSiteInfo(document), url, frame->loader().client().frameID(), frame->loader().client().pageID(), includeSecureCookies);
     else
-        result = platformStrategies()->cookiesStrategy()->cookieRequestHeaderFieldValue(storageSession(document), document.firstPartyForCookies(), url, std::nullopt, std::nullopt, includeSecureCookies);
+        result = platformStrategies()->cookiesStrategy()->cookieRequestHeaderFieldValue(storageSession(document), document.firstPartyForCookies(), sameSiteInfo(document), url, std::nullopt, std::nullopt, includeSecureCookies);
 
     if (result.second)
         document.setSecureCookiesAccessed();
@@ -131,9 +141,9 @@ bool getRawCookies(const Document& document, const URL& url, Vector<Cookie>& coo
 {
     auto frame = document.frame();
     if (frame)
-        return platformStrategies()->cookiesStrategy()->getRawCookies(storageSession(document), document.firstPartyForCookies(), url, frame->loader().client().frameID(), frame->loader().client().pageID(), cookies);
+        return platformStrategies()->cookiesStrategy()->getRawCookies(storageSession(document), document.firstPartyForCookies(), sameSiteInfo(document), url, frame->loader().client().frameID(), frame->loader().client().pageID(), cookies);
 
-    return platformStrategies()->cookiesStrategy()->getRawCookies(storageSession(document), document.firstPartyForCookies(), url, std::nullopt, std::nullopt, cookies);
+    return platformStrategies()->cookiesStrategy()->getRawCookies(storageSession(document), document.firstPartyForCookies(), sameSiteInfo(document), url, std::nullopt, std::nullopt, cookies);
 }
 
 void deleteCookie(const Document& document, const URL& url, const String& cookieName)
index fbc7434..340d092 100644 (file)
@@ -604,6 +604,8 @@ void DocumentLoader::willSendRequest(ResourceRequest&& newRequest, const Resourc
     if (m_frame->isMainFrame())
         newRequest.setFirstPartyForCookies(newRequest.url());
 
+    FrameLoader::addSameSiteInfoToRequestIfNeeded(newRequest, m_frame->document());
+
     if (!didReceiveRedirectResponse)
         frameLoader()->client().dispatchWillChangeDocument();
 
@@ -1685,6 +1687,8 @@ void DocumentLoader::startLoadingMainResource(ShouldContinue shouldContinue)
     // If not, it would be great to remove this line of code.
     // Note that currently, some requests may have incorrect extra fields even if this function has been called,
     // because we pass a wrong loadType (see FIXME in addExtraFieldsToMainResourceRequest()).
+    // If we remove this line of code then ResourceRequestBase does not need to track whether isSameSite
+    // is unspecified.
     frameLoader()->addExtraFieldsToMainResourceRequest(m_request);
 
     ASSERT(timing().startTime());
index 8a07152..97cf0b6 100644 (file)
@@ -1055,6 +1055,12 @@ void FrameLoader::setFirstPartyForCookies(const URL& url)
 {
     for (Frame* frame = &m_frame; frame; frame = frame->tree().traverseNext(&m_frame))
         frame->document()->setFirstPartyForCookies(url);
+
+    String registrableDomain = ResourceRequest::partitionName(url.host());
+    for (Frame* frame = &m_frame; frame; frame = frame->tree().traverseNext(&m_frame)) {
+        if (SecurityPolicy::shouldInheritSecurityOriginFromOwner(frame->document()->url()) || registrableDomainsAreEqual(frame->document()->url(), registrableDomain))
+            frame->document()->setFirstPartyForSameSiteCookies(url);
+    }
 }
 
 // This does the same kind of work that didOpenURL does, except it relies on the fact
@@ -1398,6 +1404,7 @@ void FrameLoader::load(FrameLoadRequest&& request)
         request.setSubstituteData(defaultSubstituteDataForURL(request.resourceRequest().url()));
 
     Ref<DocumentLoader> loader = m_client.createDocumentLoader(request.resourceRequest(), request.substituteData());
+    addSameSiteInfoToRequestIfNeeded(loader->request());
     applyShouldOpenExternalURLsPolicyToNewDocumentLoader(m_frame, loader, request);
 
     SetForScope<bool> currentLoadShouldCheckNavigationPolicyGuard(m_currentLoadShouldCheckNavigationPolicy, request.shouldCheckNavigationPolicy());
@@ -1674,6 +1681,8 @@ void FrameLoader::reload(OptionSet<ReloadOption> options)
     // FIXME: We don't have a mechanism to revalidate the main resource without reloading at the moment.
     request.setCachePolicy(ReloadIgnoringCacheData);
 
+    addSameSiteInfoToRequestIfNeeded(request);
+
     // If we're about to re-post, set up action so the application can warn the user.
     if (request.httpMethod() == "POST")
         loader->setTriggeringAction({ *m_frame.document(), request, InitiatedByMainFrame::Unknown, NavigationType::FormResubmitted });
@@ -2440,12 +2449,15 @@ void FrameLoader::setOriginalURLForDownloadRequest(ResourceRequest& request)
     // FIXME: Rename firstPartyForCookies back to mainDocumentURL. It was a mistake to think that it was only used for cookies.
     // The originalURL is defined as the URL of the page where the download was initiated.
     URL originalURL;
-    if (m_frame.document()) {
-        originalURL = m_frame.document()->firstPartyForCookies();
+    auto* initiator = m_frame.document();
+    if (initiator) {
+        originalURL = initiator->firstPartyForCookies();
         // If there is no main document URL, it means that this document is newly opened and just for download purpose.
         // In this case, we need to set the originalURL to this document's opener's main document URL.
-        if (originalURL.isEmpty() && opener() && opener()->document())
+        if (originalURL.isEmpty() && opener() && opener()->document()) {
             originalURL = opener()->document()->firstPartyForCookies();
+            initiator = opener()->document();
+        }
     }
     // If the originalURL is the same as the requested URL, we are processing a download
     // initiated directly without a page and do not need to specify the originalURL.
@@ -2453,6 +2465,7 @@ void FrameLoader::setOriginalURLForDownloadRequest(ResourceRequest& request)
         request.setFirstPartyForCookies(URL());
     else
         request.setFirstPartyForCookies(originalURL);
+    addSameSiteInfoToRequestIfNeeded(request, initiator);
 }
 
 void FrameLoader::didReachLayoutMilestone(LayoutMilestones milestones)
@@ -2683,6 +2696,20 @@ void FrameLoader::addExtraFieldsToRequest(ResourceRequest& request, FrameLoadTyp
             request.setFirstPartyForCookies(document->firstPartyForCookies());
     }
 
+    if (request.isSameSiteUnspecified()) {
+        auto* initiator = m_frame.document();
+        if (isMainResource) {
+            auto* ownerFrame = m_frame.tree().parent();
+            if (!ownerFrame)
+                ownerFrame = m_opener;
+            if (ownerFrame)
+                initiator = ownerFrame->document();
+            ASSERT(ownerFrame || m_frame.isMainFrame());
+        }
+        addSameSiteInfoToRequestIfNeeded(request, initiator);
+        request.setIsTopSite(isMainResource && m_frame.isMainFrame());
+    }
+
     Page* page = frame().page();
     bool hasSpecificCachePolicy = request.cachePolicy() != UseProtocolCachePolicy;
 
@@ -2750,6 +2777,24 @@ void FrameLoader::addHTTPOriginIfNeeded(ResourceRequest& request, const String&
     request.setHTTPOrigin(origin);
 }
 
+// Implements the "'Same-site' and 'cross-site' Requests" algorithm from <https://tools.ietf.org/html/draft-ietf-httpbis-cookie-same-site-00#section-2.1>.
+// The algorithm is ammended to treat URLs that inherit their security origin from their owner (e.g. about:blank)
+// as same-site. This matches the behavior of Chrome and Firefox.
+void FrameLoader::addSameSiteInfoToRequestIfNeeded(ResourceRequest& request, const Document* initiator)
+{
+    if (!request.isSameSiteUnspecified())
+        return;
+    if (!initiator) {
+        request.setIsSameSite(true);
+        return;
+    }
+    if (SecurityPolicy::shouldInheritSecurityOriginFromOwner(request.url())) {
+        request.setIsSameSite(true);
+        return;
+    }
+    request.setIsSameSite(registrableDomainsAreEqual(initiator->firstPartyForSameSiteCookies(), request.url()));
+}
+
 void FrameLoader::addHTTPUpgradeInsecureRequestsIfNeeded(ResourceRequest& request)
 {
     if (request.url().protocolIs("https")) {
@@ -3805,6 +3850,7 @@ RefPtr<Frame> createWindow(Frame& openerFrame, Frame& lookupFrame, FrameLoadRequ
         request.resourceRequest().setHTTPReferrer(referrer);
     FrameLoader::addHTTPOriginIfNeeded(request.resourceRequest(), openerFrame.loader().outgoingOrigin());
     FrameLoader::addHTTPUpgradeInsecureRequestsIfNeeded(request.resourceRequest());
+    FrameLoader::addSameSiteInfoToRequestIfNeeded(request.resourceRequest(), openerFrame.document());
 
     Page* oldPage = openerFrame.page();
     if (!oldPage)
index 53faa49..c7cd4ca 100644 (file)
@@ -214,6 +214,7 @@ public:
     
     static void addHTTPOriginIfNeeded(ResourceRequest&, const String& origin);
     static void addHTTPUpgradeInsecureRequestsIfNeeded(ResourceRequest&);
+    static void addSameSiteInfoToRequestIfNeeded(ResourceRequest&, const Document* initiator = nullptr);
 
     FrameLoaderClient& client() const { return m_client; }
 
index 807809b..230d6f9 100644 (file)
@@ -136,16 +136,17 @@ void ResourceLoader::init(ResourceRequest&& clientRequest, CompletionHandler<voi
         releaseResources();
         return completionHandler(false);
     }
-    
-    // https://bugs.webkit.org/show_bug.cgi?id=26391
+
     // The various plug-in implementations call directly to ResourceLoader::load() instead of piping requests
     // through FrameLoader. As a result, they miss the FrameLoader::addExtraFieldsToRequest() step which sets
-    // up the 1st party for cookies URL. Until plug-in implementations can be reigned in to pipe through that
-    // method, we need to make sure there is always a 1st party for cookies set.
+    // up the 1st party for cookies URL and Same-Site info. Until plug-in implementations can be reigned in
+    // to pipe through that method, we need to make sure there is always both a 1st party for cookies set and
+    // Same-Site info. See <https://bugs.webkit.org/show_bug.cgi?id=26391>.
     if (clientRequest.firstPartyForCookies().isNull()) {
         if (Document* document = m_frame->document())
             clientRequest.setFirstPartyForCookies(document->firstPartyForCookies());
     }
+    FrameLoader::addSameSiteInfoToRequestIfNeeded(clientRequest, m_frame->document());
 
     willSendRequestInternal(WTFMove(clientRequest), ResourceResponse(), [this, protectedThis = makeRef(*this), completionHandler = WTFMove(completionHandler)](ResourceRequest&& request) mutable {
 
index b023a4e..c709a2d 100644 (file)
@@ -2290,6 +2290,7 @@ RefPtr<Frame> DOMWindow::createWindow(const String& urlString, const AtomicStrin
 
     if (created) {
         ResourceRequest resourceRequest { completedURL, referrer, UseProtocolCachePolicy };
+        FrameLoader::addSameSiteInfoToRequestIfNeeded(resourceRequest, openerFrame.document());
         FrameLoadRequest frameLoadRequest { *activeWindow.document(), activeWindow.document()->securityOrigin(), resourceRequest, ASCIILiteral("_self"), LockHistory::No, LockBackForwardList::No, MaybeSendReferrer, AllowNavigationToInvalidURL::Yes, NewFrameOpenerPolicy::Allow, activeDocument->shouldOpenExternalURLsPolicyToPropagate(), initiatedByMainFrame };
         if (openerFrame.document() && !protocolHostAndPortAreEqual(openerFrame.document()->url(), frameLoadRequest.resourceRequest().url()))
             frameLoadRequest.setIsCrossOriginWindowOpenNavigation(true);
index 091fdb4..fe61283 100644 (file)
@@ -136,6 +136,20 @@ String SecurityPolicy::generateReferrerHeader(ReferrerPolicy referrerPolicy, con
     return shouldHideReferrer(url, referrer) ? String() : referrer;
 }
 
+bool SecurityPolicy::shouldInheritSecurityOriginFromOwner(const URL& url)
+{
+    // Paraphrased from <https://html.spec.whatwg.org/multipage/browsers.html#origin> (8 July 2016)
+    //
+    // If a Document has the address "about:blank"
+    //      The origin of the document is the origin it was assigned when its browsing context was created.
+    // If a Document has the address "about:srcdoc"
+    //      The origin of the document is the origin of its parent document.
+    //
+    // Note: We generalize this to invalid URLs because we treat such URLs as about:blank.
+    //
+    return url.isEmpty() || equalIgnoringASCIICase(url.string(), blankURL()) || equalLettersIgnoringASCIICase(url.string(), "about:srcdoc");
+}
+
 void SecurityPolicy::setLocalLoadPolicy(LocalLoadPolicy policy)
 {
     localLoadPolicy = policy;
index cd1916c..cd37ca3 100644 (file)
@@ -51,6 +51,8 @@ public:
     // referrer header should be omitted.
     WEBCORE_EXPORT static String generateReferrerHeader(ReferrerPolicy, const URL&, const String& referrer);
 
+    static bool shouldInheritSecurityOriginFromOwner(const URL&);
+
     enum LocalLoadPolicy {
         AllowLocalLoadsForAll, // No restriction on local loads.
         AllowLocalLoadsForLocalAndSubstituteData,
index 3f3fa30..b439786 100644 (file)
@@ -36,17 +36,18 @@ class NetworkStorageSession;
 class URL;
 
 struct Cookie;
+struct SameSiteInfo;
 
 enum class IncludeSecureCookies { No, Yes };
 
 class CookiesStrategy {
 public:
-    virtual std::pair<String, bool> cookiesForDOM(const NetworkStorageSession&, const URL& firstParty, const URL&, std::optional<uint64_t> frameID, std::optional<uint64_t> pageID, IncludeSecureCookies) = 0;
-    virtual void setCookiesFromDOM(const NetworkStorageSession&, const URL& firstParty, const URL&, std::optional<uint64_t> frameID, std::optional<uint64_t> pageID, const String& cookieString) = 0;
+    virtual std::pair<String, bool> cookiesForDOM(const NetworkStorageSession&, const URL& firstParty, const SameSiteInfo&, const URL&, std::optional<uint64_t> frameID, std::optional<uint64_t> pageID, IncludeSecureCookies) = 0;
+    virtual void setCookiesFromDOM(const NetworkStorageSession&, const URL& firstParty, const SameSiteInfo&, const URL&, std::optional<uint64_t> frameID, std::optional<uint64_t> pageID, const String& cookieString) = 0;
     virtual bool cookiesEnabled(const NetworkStorageSession&) = 0;
-    virtual std::pair<String, bool> cookieRequestHeaderFieldValue(const NetworkStorageSession&, const URL& firstParty, const URL&, std::optional<uint64_t> frameID, std::optional<uint64_t> pageID, IncludeSecureCookies) = 0;
-    virtual std::pair<String, bool> cookieRequestHeaderFieldValue(PAL::SessionID, const URL& firstParty, const URL&, std::optional<uint64_t> frameID, std::optional<uint64_t> pageID, IncludeSecureCookies) = 0;
-    virtual bool getRawCookies(const NetworkStorageSession&, const URL& firstParty, const URL&, std::optional<uint64_t> frameID, std::optional<uint64_t> pageID, Vector<Cookie>&) = 0;
+    virtual std::pair<String, bool> cookieRequestHeaderFieldValue(const NetworkStorageSession&, const URL& firstParty, const SameSiteInfo&, const URL&, std::optional<uint64_t> frameID, std::optional<uint64_t> pageID, IncludeSecureCookies) = 0;
+    virtual std::pair<String, bool> cookieRequestHeaderFieldValue(PAL::SessionID, const URL& firstParty, const SameSiteInfo&, const URL&, std::optional<uint64_t> frameID, std::optional<uint64_t> pageID, IncludeSecureCookies) = 0;
+    virtual bool getRawCookies(const NetworkStorageSession&, const URL& firstParty, const SameSiteInfo&, const URL&, std::optional<uint64_t> frameID, std::optional<uint64_t> pageID, Vector<Cookie>&) = 0;
     virtual void deleteCookie(const NetworkStorageSession&, const URL&, const String& cookieName) = 0;
 
 protected:
index 1213c31..d91476c 100644 (file)
@@ -33,6 +33,7 @@
 #include "PlatformStrategies.h"
 #include "ResourceRequest.h"
 #include "ResourceResponse.h"
+#include "SameSiteInfo.h"
 #include <wtf/text/StringView.h>
 
 namespace WebCore {
@@ -337,9 +338,9 @@ static String headerValueForVary(const ResourceRequest& request, const String& h
         auto* cookieStrategy = platformStrategies() ? platformStrategies()->cookiesStrategy() : nullptr;
         if (!cookieStrategy) {
             ASSERT(sessionID == PAL::SessionID::defaultSessionID());
-            return cookieRequestHeaderFieldValue(NetworkStorageSession::defaultStorageSession(), request.firstPartyForCookies(), request.url(), std::nullopt, std::nullopt, includeSecureCookies).first;
+            return cookieRequestHeaderFieldValue(NetworkStorageSession::defaultStorageSession(), request.firstPartyForCookies(), SameSiteInfo::create(request), request.url(), std::nullopt, std::nullopt, includeSecureCookies).first;
         }
-        return cookieStrategy->cookieRequestHeaderFieldValue(sessionID, request.firstPartyForCookies(), request.url(), std::nullopt, std::nullopt, includeSecureCookies).first;
+        return cookieStrategy->cookieRequestHeaderFieldValue(sessionID, request.firstPartyForCookies(), SameSiteInfo::create(request), request.url(), std::nullopt, std::nullopt, includeSecureCookies).first;
     }
     return request.httpHeaderField(headerName);
 }
index a4593de..249a003 100644 (file)
@@ -26,6 +26,7 @@
 #pragma once
 
 #include "CookiesStrategy.h"
+#include "SameSiteInfo.h"
 #include "URL.h"
 #include <pal/SessionID.h>
 
@@ -34,6 +35,7 @@ namespace WebCore {
 struct CookieRequestHeaderFieldProxy {
     PAL::SessionID sessionID;
     URL firstParty;
+    SameSiteInfo sameSiteInfo;
     URL url;
     std::optional<uint64_t> frameID;
     std::optional<uint64_t> pageID;
@@ -48,6 +50,7 @@ void CookieRequestHeaderFieldProxy::encode(Encoder& encoder) const
 {
     encoder << sessionID;
     encoder << firstParty;
+    encoder << sameSiteInfo;
     encoder << url;
     encoder << frameID;
     encoder << pageID;
@@ -62,6 +65,8 @@ std::optional<CookieRequestHeaderFieldProxy> CookieRequestHeaderFieldProxy::deco
         return std::nullopt;
     if (!decoder.decode(result.firstParty))
         return std::nullopt;
+    if (!decoder.decode(result.sameSiteInfo))
+        return std::nullopt;
     if (!decoder.decode(result.url))
         return std::nullopt;
     if (!decoder.decode(result.frameID))
index 9441c2b..78943d9 100644 (file)
@@ -37,17 +37,18 @@ class NetworkStorageSession;
 
 struct Cookie;
 struct CookieRequestHeaderFieldProxy;
+struct SameSiteInfo;
 
 enum class IncludeSecureCookies;
 
 // FIXME: These should probably be NetworkStorageSession member functions.
 
-WEBCORE_EXPORT std::pair<String, bool> cookiesForDOM(const NetworkStorageSession&, const URL& firstParty, const URL&, std::optional<uint64_t> frameID, std::optional<uint64_t> pageID, IncludeSecureCookies);
-WEBCORE_EXPORT void setCookiesFromDOM(const NetworkStorageSession&, const URL& firstParty, const URL&, std::optional<uint64_t> frameID, std::optional<uint64_t> pageID, const String&);
+WEBCORE_EXPORT std::pair<String, bool> cookiesForDOM(const NetworkStorageSession&, const URL& firstParty, const SameSiteInfo&, const URL&, std::optional<uint64_t> frameID, std::optional<uint64_t> pageID, IncludeSecureCookies);
+WEBCORE_EXPORT void setCookiesFromDOM(const NetworkStorageSession&, const URL& firstParty, const SameSiteInfo&, const URL&, std::optional<uint64_t> frameID, std::optional<uint64_t> pageID, const String&);
 WEBCORE_EXPORT bool cookiesEnabled(const NetworkStorageSession&);
-WEBCORE_EXPORT std::pair<String, bool> cookieRequestHeaderFieldValue(const NetworkStorageSession&, const URL& firstParty, const URL&, std::optional<uint64_t> frameID, std::optional<uint64_t> pageID, IncludeSecureCookies);
+WEBCORE_EXPORT std::pair<String, bool> cookieRequestHeaderFieldValue(const NetworkStorageSession&, const URL& firstParty, const SameSiteInfo&, const URL&, std::optional<uint64_t> frameID, std::optional<uint64_t> pageID, IncludeSecureCookies);
 WEBCORE_EXPORT std::pair<String, bool> cookieRequestHeaderFieldValue(const NetworkStorageSession&, const CookieRequestHeaderFieldProxy&);
-WEBCORE_EXPORT bool getRawCookies(const NetworkStorageSession&, const URL& firstParty, const URL&, std::optional<uint64_t> frameID, std::optional<uint64_t> pageID, Vector<Cookie>&);
+WEBCORE_EXPORT bool getRawCookies(const NetworkStorageSession&, const URL& firstParty, const SameSiteInfo&, const URL&, std::optional<uint64_t> frameID, std::optional<uint64_t> pageID, Vector<Cookie>&);
 WEBCORE_EXPORT void deleteCookie(const NetworkStorageSession&, const URL&, const String&);
 WEBCORE_EXPORT void getHostnamesWithCookies(const NetworkStorageSession&, HashSet<String>& hostnames);
 WEBCORE_EXPORT void deleteCookiesForHostnames(const NetworkStorageSession&, const Vector<String>& cookieHostNames);
index 2f60c65..2b650cb 100644 (file)
@@ -66,6 +66,11 @@ void ResourceRequestBase::setAsIsolatedCopy(const ResourceRequest& other)
     setInitiatorIdentifier(other.initiatorIdentifier().isolatedCopy());
     setCachePartition(other.cachePartition().isolatedCopy());
 
+    if (!other.isSameSiteUnspecified()) {
+        setIsSameSite(other.isSameSite());
+        setIsTopSite(other.isTopSite());
+    }
+
     updateResourceRequest();
     m_httpHeaderFields = other.httpHeaderFields().isolatedCopy();
 
@@ -223,6 +228,45 @@ void ResourceRequestBase::setFirstPartyForCookies(const URL& firstPartyForCookie
     m_platformRequestUpdated = false;
 }
 
+bool ResourceRequestBase::isSameSite() const
+{
+    updateResourceRequest();
+
+    return m_sameSiteDisposition == SameSiteDisposition::SameSite;
+}
+
+void ResourceRequestBase::setIsSameSite(bool isSameSite)
+{
+    updateResourceRequest();
+
+    SameSiteDisposition newDisposition = isSameSite ? SameSiteDisposition::SameSite : SameSiteDisposition::CrossSite;
+    if (m_sameSiteDisposition == newDisposition)
+        return;
+
+    m_sameSiteDisposition = newDisposition;
+
+    m_platformRequestUpdated = false;
+}
+
+bool ResourceRequestBase::isTopSite() const
+{
+    updateResourceRequest();
+
+    return m_isTopSite;
+}
+
+void ResourceRequestBase::setIsTopSite(bool isTopSite)
+{
+    updateResourceRequest();
+
+    if (m_isTopSite == isTopSite)
+        return;
+
+    m_isTopSite = isTopSite;
+
+    m_platformRequestUpdated = false;
+}
+
 const String& ResourceRequestBase::httpMethod() const
 {
     updateResourceRequest(); 
@@ -545,6 +589,12 @@ bool equalIgnoringHeaderFields(const ResourceRequestBase& a, const ResourceReque
     
     if (a.firstPartyForCookies() != b.firstPartyForCookies())
         return false;
+
+    if (a.isSameSite() != b.isSameSite())
+        return false;
+
+    if (a.isTopSite() != b.isTopSite())
+        return false;
     
     if (a.httpMethod() != b.httpMethod())
         return false;
index 5e8aabc..8f3218b 100644 (file)
@@ -77,7 +77,18 @@ public:
     
     WEBCORE_EXPORT const URL& firstPartyForCookies() const;
     WEBCORE_EXPORT void setFirstPartyForCookies(const URL&);
-    
+
+    // Same-Site cookies; see <https://tools.ietf.org/html/draft-ietf-httpbis-cookie-same-site-00#section-2.1>
+    // and <https://tools.ietf.org/html/draft-ietf-httpbis-cookie-same-site-00#section-5.2>.
+    // FIXME: For some reason the main resource request may be updated more than once. We start off as Unspecified
+    // to detect if we need to compute the same-site and top-site state or not. See FIXME in DocumentLoader::startLoadingMainResource().
+    enum class SameSiteDisposition { Unspecified, SameSite, CrossSite };
+    bool isSameSiteUnspecified() const { return m_sameSiteDisposition == SameSiteDisposition::Unspecified; }
+    WEBCORE_EXPORT bool isSameSite() const; // Whether this request's registrable domain matches the request's initiator's "site for cookies".
+    WEBCORE_EXPORT void setIsSameSite(bool);
+    WEBCORE_EXPORT bool isTopSite() const; // Whether this request is for a top-level navigation.
+    WEBCORE_EXPORT void setIsTopSite(bool);
+
     WEBCORE_EXPORT const String& httpMethod() const;
     WEBCORE_EXPORT void setHTTPMethod(const String& httpMethod);
     
@@ -213,6 +224,8 @@ protected:
     mutable bool m_resourceRequestBodyUpdated { false };
     mutable bool m_platformRequestBodyUpdated { false };
     bool m_hiddenFromInspector { false };
+    SameSiteDisposition m_sameSiteDisposition { SameSiteDisposition::Unspecified };
+    bool m_isTopSite { false };
     ResourceLoadPriority m_priority { ResourceLoadPriority::Low };
     Requester m_requester { Requester::Unspecified };
     String m_initiatorIdentifier;
@@ -226,6 +239,16 @@ private:
 
 bool equalIgnoringHeaderFields(const ResourceRequestBase&, const ResourceRequestBase&);
 
+// FIXME: Find a better place for these functions.
+inline bool registrableDomainsAreEqual(const URL& a, const URL& b)
+{
+    return ResourceRequestBase::partitionName(a.host()) == ResourceRequestBase::partitionName(b.host());
+}
+inline bool registrableDomainsAreEqual(const URL& a, const String& registrableDomain)
+{
+    return ResourceRequestBase::partitionName(a.host()) == registrableDomain;
+}
+
 inline bool operator==(const ResourceRequest& a, const ResourceRequest& b) { return ResourceRequestBase::equal(a, b); }
 inline bool operator!=(ResourceRequest& a, const ResourceRequest& b) { return !(a == b); }
 
@@ -245,6 +268,8 @@ ALWAYS_INLINE void ResourceRequestBase::encodeBase(Encoder& encoder) const
     encoder << m_responseContentDispositionEncodingFallbackArray;
     encoder.encodeEnum(m_cachePolicy);
     encoder << m_allowCookies;
+    encoder.encodeEnum(m_sameSiteDisposition);
+    encoder << m_isTopSite;
     encoder.encodeEnum(m_priority);
     encoder.encodeEnum(m_requester);
 }
@@ -282,6 +307,16 @@ ALWAYS_INLINE bool ResourceRequestBase::decodeBase(Decoder& decoder)
         return false;
     m_allowCookies = allowCookies;
 
+    SameSiteDisposition sameSiteDisposition;
+    if (!decoder.decodeEnum(sameSiteDisposition))
+        return false;
+    m_sameSiteDisposition = sameSiteDisposition;
+
+    bool isTopSite;
+    if (!decoder.decode(isTopSite))
+        return false;
+    m_isTopSite = isTopSite;
+
     ResourceLoadPriority priority;
     if (!decoder.decodeEnum(priority))
         return false;
diff --git a/Source/WebCore/platform/network/SameSiteInfo.cpp b/Source/WebCore/platform/network/SameSiteInfo.cpp
new file mode 100644 (file)
index 0000000..9edb56a
--- /dev/null
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2018 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 "SameSiteInfo.h"
+
+#include "ResourceRequest.h"
+
+namespace WebCore {
+
+SameSiteInfo SameSiteInfo::create(const ResourceRequest& request)
+{
+    return { request.isSameSite(), request.isTopSite() };
+}
+
+} // namespace WebCore
diff --git a/Source/WebCore/platform/network/SameSiteInfo.h b/Source/WebCore/platform/network/SameSiteInfo.h
new file mode 100644 (file)
index 0000000..fea759c
--- /dev/null
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2018 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
+
+namespace WebCore {
+
+class ResourceRequest;
+
+struct SameSiteInfo {
+    WEBCORE_EXPORT static SameSiteInfo create(const ResourceRequest&);
+
+    bool isSameSite { false };
+    bool isTopSite { false };
+
+    template <class Encoder> void encode(Encoder&) const;
+    template <class Decoder> static bool decode(Decoder&, SameSiteInfo&);
+};
+
+template <class Encoder>
+void SameSiteInfo::encode(Encoder& encoder) const
+{
+    encoder << isSameSite;
+    encoder << isTopSite;
+}
+
+template <class Decoder>
+bool SameSiteInfo::decode(Decoder& decoder, SameSiteInfo& info)
+{
+    if (!decoder.decode(info.isSameSite))
+        return false;
+    if (!decoder.decode(info.isTopSite))
+        return false;
+    return true;
+}
+
+} // namespace WebCore
index b91c78b..b6bd805 100644 (file)
@@ -168,7 +168,7 @@ static CFArrayRef createCookies(CFDictionaryRef headerFields, CFURLRef url)
     return parsedCookies;
 }
 
-void setCookiesFromDOM(const NetworkStorageSession& session, const URL& firstParty, const URL& url, std::optional<uint64_t> frameID, std::optional<uint64_t> pageID, const String& value)
+void setCookiesFromDOM(const NetworkStorageSession& session, const URL& firstParty, const SameSiteInfo&, const URL& url, std::optional<uint64_t> frameID, std::optional<uint64_t> pageID, const String& value)
 {
     UNUSED_PARAM(frameID);
     UNUSED_PARAM(pageID);
@@ -204,7 +204,7 @@ static bool containsSecureCookies(CFArrayRef cookies)
     return false;
 }
 
-std::pair<String, bool> cookiesForDOM(const NetworkStorageSession& session, const URL& firstParty, const URL& url, std::optional<uint64_t> frameID, std::optional<uint64_t> pageID, IncludeSecureCookies includeSecureCookies)
+std::pair<String, bool> cookiesForDOM(const NetworkStorageSession& session, const URL& firstParty, const SameSiteInfo&, const URL& url, std::optional<uint64_t> frameID, std::optional<uint64_t> pageID, IncludeSecureCookies includeSecureCookies)
 {
     UNUSED_PARAM(frameID);
     UNUSED_PARAM(pageID);
@@ -219,7 +219,7 @@ std::pair<String, bool> cookiesForDOM(const NetworkStorageSession& session, cons
     return { cookieString, didAccessSecureCookies };
 }
 
-std::pair<String, bool> cookieRequestHeaderFieldValue(const NetworkStorageSession& session, const URL& firstParty, const URL& url, std::optional<uint64_t> frameID, std::optional<uint64_t> pageID, IncludeSecureCookies includeSecureCookies)
+std::pair<String, bool> cookieRequestHeaderFieldValue(const NetworkStorageSession& session, const URL& firstParty, const SameSiteInfo&, const URL& url, std::optional<uint64_t> frameID, std::optional<uint64_t> pageID, IncludeSecureCookies includeSecureCookies)
 {
     UNUSED_PARAM(frameID);
     UNUSED_PARAM(pageID);
@@ -243,7 +243,7 @@ bool cookiesEnabled(const NetworkStorageSession& session)
     return policy == CFHTTPCookieStorageAcceptPolicyOnlyFromMainDocumentDomain || policy == CFHTTPCookieStorageAcceptPolicyExclusivelyFromMainDocumentDomain || policy == CFHTTPCookieStorageAcceptPolicyAlways;
 }
 
-bool getRawCookies(const NetworkStorageSession& session, const URL& firstParty, const URL& url, std::optional<uint64_t> frameID, std::optional<uint64_t> pageID, Vector<Cookie>& rawCookies)
+bool getRawCookies(const NetworkStorageSession& session, const URL& firstParty, const SameSiteInfo&, const URL& url, std::optional<uint64_t> frameID, std::optional<uint64_t> pageID, Vector<Cookie>& rawCookies)
 {
     UNUSED_PARAM(frameID);
     UNUSED_PARAM(pageID);
index 15618fb..3422035 100644 (file)
@@ -29,6 +29,7 @@
 #include "HTTPHeaderNames.h"
 #include "ResourceRequest.h"
 #include <pal/spi/cf/CFNetworkSPI.h>
+#include <wtf/cf/TypeCastsCF.h>
 
 #if ENABLE(PUBLIC_SUFFIX_LIST)
 #include "PublicSuffix.h"
@@ -49,6 +50,8 @@
 #include <WebKitSystemInterface/WebKitSystemInterface.h>
 #endif
 
+WTF_DECLARE_CF_TYPE_TRAIT(CFURL);
+
 namespace WebCore {
 
 // FIXME: Make this a NetworkingContext property.
@@ -141,6 +144,19 @@ static inline CFURLRequestCachePolicy toPlatformRequestCachePolicy(ResourceReque
     return kCFURLRequestCachePolicyReloadIgnoringCache;
 }
 
+static CFURLRef siteForCookies(ResourceRequest::SameSiteDisposition disposition, CFURLRef url)
+{
+    switch (disposition) {
+    case ResourceRequest::SameSiteDisposition::Unspecified:
+        return { };
+    case ResourceRequest::SameSiteDisposition::SameSite:
+        return url;
+    case ResourceRequest::SameSiteDisposition::CrossSite:
+        static CFURLRef emptyURL = CFURLCreateWithString(nullptr, CFSTR(""), nullptr);
+        return emptyURL;
+    }
+}
+
 void ResourceRequest::doUpdatePlatformRequest()
 {
     CFMutableURLRequestRef cfRequest;
@@ -169,6 +185,11 @@ void ResourceRequest::doUpdatePlatformRequest()
 
     CFURLRequestSetShouldHandleHTTPCookies(cfRequest, allowCookies());
 
+    _CFURLRequestSetProtocolProperty(cfRequest, CFSTR("_kCFHTTPCookiePolicyPropertySiteForCookies"), siteForCookies(m_sameSiteDisposition, url));
+    int isTopSite = m_isTopSite;
+    RetainPtr<CFNumberRef> isTopSiteCF = adoptCF(CFNumberCreate(nullptr, kCFNumberIntType, &isTopSite));
+    _CFURLRequestSetProtocolProperty(cfRequest, _kCFHTTPCookiePolicyPropertyisTopSite, isTopSiteCF.get());
+
     unsigned fallbackCount = m_responseContentDispositionEncodingFallbackArray.size();
     RetainPtr<CFMutableArrayRef> encodingFallbacks = adoptCF(CFArrayCreateMutable(kCFAllocatorDefault, fallbackCount, 0));
     for (unsigned i = 0; i != fallbackCount; ++i) {
@@ -246,6 +267,17 @@ void ResourceRequest::doUpdateResourceRequest()
     if (resourcePrioritiesEnabled())
         m_priority = toResourceLoadPriority(CFURLRequestGetRequestPriority(m_cfRequest.get()));
 
+    RetainPtr<CFURLRef> siteForCookies = adoptCF(checked_cf_cast<CFURLRef>(_CFURLRequestCopyProtocolPropertyForKey(m_cfRequest.get(), CFSTR("_kCFHTTPCookiePolicyPropertySiteForCookies"))));
+    m_sameSiteDisposition = !siteForCookies ? SameSiteDisposition::Unspecified : (registrableDomainsAreEqual(siteForCookies.get(), m_url) ? SameSiteDisposition::SameSite : SameSiteDisposition::CrossSite);
+    RetainPtr<CFNumberRef> isTopSiteCF = adoptCF(checked_cf_cast<CFNumber>(_CFURLRequestCopyProtocolPropertyForKey(m_cfRequest.get(), _kCFHTTPCookiePolicyPropertyisTopSite)));
+    if (!isTopSiteCF)
+        m_isTopSite = false;
+    else {
+        int isTopSite = 0;
+        CFNumberGetValue(isTopSiteCF.get(), kCFNumberIntType, &isTopSite);
+        m_isTopSite = isTopSite;
+    }
+
     m_httpHeaderFields.clear();
     if (CFDictionaryRef headers = CFURLRequestCopyAllHTTPHeaderFields(m_cfRequest.get())) {
         CFIndex headerCount = CFDictionaryGetCount(headers);
index 0e095a3..3d52cfa 100644 (file)
@@ -87,6 +87,10 @@ void ResourceRequest::doUpdateResourceRequest()
     m_timeoutInterval = [m_nsRequest.get() timeoutInterval];
     m_firstPartyForCookies = [m_nsRequest.get() mainDocumentURL];
 
+    URL siteForCookies { [m_nsRequest.get() _propertyForKey:@"_kCFHTTPCookiePolicyPropertySiteForCookies"] };
+    m_sameSiteDisposition = siteForCookies.isNull() ? SameSiteDisposition::Unspecified : (registrableDomainsAreEqual(siteForCookies, m_url) ? SameSiteDisposition::SameSite : SameSiteDisposition::CrossSite);
+    m_isTopSite = static_cast<NSNumber*>([m_nsRequest.get() _propertyForKey:@"_kCFHTTPCookiePolicyPropertyIsTopLevelNavigation"]).boolValue;
+
     if (NSString* method = [m_nsRequest.get() HTTPMethod])
         m_httpMethod = method;
     m_allowCookies = [m_nsRequest.get() HTTPShouldHandleCookies];
@@ -129,6 +133,18 @@ void ResourceRequest::doUpdateResourceHTTPBody()
     }
 }
 
+static NSURL *siteForCookies(ResourceRequest::SameSiteDisposition disposition, NSURL *url)
+{
+    switch (disposition) {
+    case ResourceRequest::SameSiteDisposition::Unspecified:
+        return { };
+    case ResourceRequest::SameSiteDisposition::SameSite:
+        return url;
+    case ResourceRequest::SameSiteDisposition::CrossSite:
+        return [NSURL URLWithString:@""];
+    }
+}
+
 void ResourceRequest::doUpdatePlatformRequest()
 {
     if (isNull()) {
@@ -162,6 +178,9 @@ void ResourceRequest::doUpdatePlatformRequest()
         [nsRequest setHTTPMethod:httpMethod()];
     [nsRequest setHTTPShouldHandleCookies:allowCookies()];
 
+    [nsRequest _setProperty:siteForCookies(m_sameSiteDisposition, nsRequest.URL) forKey:@"_kCFHTTPCookiePolicyPropertySiteForCookies"];
+    [nsRequest _setProperty:[NSNumber numberWithBool:m_isTopSite] forKey:@"_kCFHTTPCookiePolicyPropertyIsTopLevelNavigation"];
+
     // Cannot just use setAllHTTPHeaderFields here, because it does not remove headers.
     for (NSString *oldHeaderName in [nsRequest allHTTPHeaderFields])
         [nsRequest setValue:nil forHTTPHeaderField:oldHeaderName];
index f83ce1b..066b6ed 100644 (file)
 
 namespace WebCore {
 
-std::pair<String, bool> cookiesForDOM(const NetworkStorageSession& session, const URL& firstParty, const URL& url, std::optional<uint64_t> frameID, std::optional<uint64_t> pageID, IncludeSecureCookies includeSecureCookies)
+std::pair<String, bool> cookiesForDOM(const NetworkStorageSession& session, const URL& firstParty, const SameSiteInfo& sameSiteInfo, const URL& url, std::optional<uint64_t> frameID, std::optional<uint64_t> pageID, IncludeSecureCookies includeSecureCookies)
 {
-    return session.cookieStorage().cookiesForDOM(session, firstParty, url, frameID, pageID, includeSecureCookies);
+    return session.cookieStorage().cookiesForDOM(session, firstParty, sameSiteInfo, url, frameID, pageID, includeSecureCookies);
 }
 
-void setCookiesFromDOM(const NetworkStorageSession& session, const URL& firstParty, const URL& url, std::optional<uint64_t> frameID, std::optional<uint64_t> pageID, const String& value)
+void setCookiesFromDOM(const NetworkStorageSession& session, const URL& firstParty, const SameSiteInfo& sameSiteInfo, const URL& url, std::optional<uint64_t> frameID, std::optional<uint64_t> pageID, const String& value)
 {
-    session.cookieStorage().setCookiesFromDOM(session, firstParty, url, frameID, pageID, value);
+    session.cookieStorage().setCookiesFromDOM(session, firstParty, sameSiteInfo, url, frameID, pageID, value);
 }
 
-std::pair<String, bool> cookieRequestHeaderFieldValue(const NetworkStorageSession& session, const URL& firstParty, const URL& url, std::optional<uint64_t> frameID, std::optional<uint64_t> pageID, IncludeSecureCookies includeSecureCookies)
+std::pair<String, bool> cookieRequestHeaderFieldValue(const NetworkStorageSession& session, const URL& firstParty, const SameSiteInfo& sameSiteInfo, const URL& url, std::optional<uint64_t> frameID, std::optional<uint64_t> pageID, IncludeSecureCookies includeSecureCookies)
 {
-    return session.cookieStorage().cookieRequestHeaderFieldValue(session, firstParty, url, frameID, pageID, includeSecureCookies);
+    return session.cookieStorage().cookieRequestHeaderFieldValue(session, firstParty, sameSiteInfo, url, frameID, pageID, includeSecureCookies);
 }
 
 std::pair<String, bool> cookieRequestHeaderFieldValue(const NetworkStorageSession& session, const CookieRequestHeaderFieldProxy& headerFieldProxy)
@@ -55,9 +55,9 @@ bool cookiesEnabled(const NetworkStorageSession& session)
     return session.cookieStorage().cookiesEnabled(session);
 }
 
-bool getRawCookies(const NetworkStorageSession& session, const URL& firstParty, const URL& url, std::optional<uint64_t> frameID, std::optional<uint64_t> pageID, Vector<Cookie>& rawCookies)
+bool getRawCookies(const NetworkStorageSession& session, const URL& firstParty, const SameSiteInfo& sameSiteInfo, const URL& url, std::optional<uint64_t> frameID, std::optional<uint64_t> pageID, Vector<Cookie>& rawCookies)
 {
-    return session.cookieStorage().getRawCookies(session, firstParty, url, frameID, pageID, rawCookies);
+    return session.cookieStorage().getRawCookies(session, firstParty, sameSiteInfo, url, frameID, pageID, rawCookies);
 }
 
 void deleteCookie(const NetworkStorageSession& session, const URL& url, const String& cookie)
index 7b5b079..2aa8dfd 100644 (file)
 
 namespace WebCore {
 
-struct Cookie;
-struct CookieRequestHeaderFieldProxy;
-class NetworkStorageSession;
-class URL;
-
 class CookieJarCurl {
 public:
-    virtual std::pair<String, bool> cookiesForDOM(const NetworkStorageSession&, const URL& firstParty, const URL&, std::optional<uint64_t> frameID, std::optional<uint64_t> pageID, IncludeSecureCookies) const = 0;
-    virtual void setCookiesFromDOM(const NetworkStorageSession&, const URL& firstParty, const URL&, std::optional<uint64_t> frameID, std::optional<uint64_t> pageID, const String&) const = 0;
+    virtual std::pair<String, bool> cookiesForDOM(const NetworkStorageSession&, const URL& firstParty, const SameSiteInfo&, const URL&, std::optional<uint64_t> frameID, std::optional<uint64_t> pageID, IncludeSecureCookies) const = 0;
+    virtual void setCookiesFromDOM(const NetworkStorageSession&, const URL& firstParty, const SameSiteInfo&, const URL&, std::optional<uint64_t> frameID, std::optional<uint64_t> pageID, const String&) const = 0;
     virtual void setCookiesFromHTTPResponse(const NetworkStorageSession&, const URL&, const String&) const = 0;
     virtual bool cookiesEnabled(const NetworkStorageSession&) const = 0;
-    virtual std::pair<String, bool> cookieRequestHeaderFieldValue(const NetworkStorageSession&, const URL& firstParty, const URL&, std::optional<uint64_t> frameID, std::optional<uint64_t> pageID, IncludeSecureCookies) const = 0;
+    virtual std::pair<String, bool> cookieRequestHeaderFieldValue(const NetworkStorageSession&, const URL& firstParty, const SameSiteInfo&, const URL&, std::optional<uint64_t> frameID, std::optional<uint64_t> pageID, IncludeSecureCookies) const = 0;
     virtual std::pair<String, bool> cookieRequestHeaderFieldValue(const NetworkStorageSession&, const CookieRequestHeaderFieldProxy&) const = 0;
-    virtual bool getRawCookies(const NetworkStorageSession&, const URL& firstParty, const URL&, std::optional<uint64_t> frameID, std::optional<uint64_t> pageID, Vector<Cookie>&) const = 0;
+    virtual bool getRawCookies(const NetworkStorageSession&, const URL& firstParty, const SameSiteInfo&, const URL&, std::optional<uint64_t> frameID, std::optional<uint64_t> pageID, Vector<Cookie>&) const = 0;
     virtual void deleteCookie(const NetworkStorageSession&, const URL&, const String&) const = 0;
     virtual void getHostnamesWithCookies(const NetworkStorageSession&, HashSet<String>& hostnames) const = 0;
     virtual void deleteCookiesForHostnames(const NetworkStorageSession&, const Vector<String>& cookieHostNames) const = 0;
index 6a84768..d2951b3 100644 (file)
@@ -60,7 +60,7 @@ static String cookiesForSession(const NetworkStorageSession& session, const URL&
     return cookies.toString();
 }
 
-void CookieJarCurlDatabase::setCookiesFromDOM(const NetworkStorageSession& session, const URL& firstParty, const URL& url, std::optional<uint64_t> frameID, std::optional<uint64_t> pageID, const String& value) const
+void CookieJarCurlDatabase::setCookiesFromDOM(const NetworkStorageSession& session, const URL& firstParty, const SameSiteInfo&, const URL& url, std::optional<uint64_t> frameID, std::optional<uint64_t> pageID, const String& value) const
 {
     UNUSED_PARAM(frameID);
     UNUSED_PARAM(pageID);
@@ -76,7 +76,7 @@ void CookieJarCurlDatabase::setCookiesFromHTTPResponse(const NetworkStorageSessi
     cookieJarDB.setCookie(url.string(), value, false);
 }
 
-std::pair<String, bool> CookieJarCurlDatabase::cookiesForDOM(const NetworkStorageSession& session, const URL& firstParty, const URL& url, std::optional<uint64_t> frameID, std::optional<uint64_t> pageID, IncludeSecureCookies) const
+std::pair<String, bool> CookieJarCurlDatabase::cookiesForDOM(const NetworkStorageSession& session, const URL& firstParty, const SameSiteInfo&, const URL& url, std::optional<uint64_t> frameID, std::optional<uint64_t> pageID, IncludeSecureCookies) const
 {
     UNUSED_PARAM(frameID);
     UNUSED_PARAM(pageID);
@@ -85,7 +85,7 @@ std::pair<String, bool> CookieJarCurlDatabase::cookiesForDOM(const NetworkStorag
     return { cookiesForSession(session, firstParty, url, false), false };
 }
 
-std::pair<String, bool> CookieJarCurlDatabase::cookieRequestHeaderFieldValue(const NetworkStorageSession& session, const URL& firstParty, const URL& url, std::optional<uint64_t> frameID, std::optional<uint64_t> pageID, IncludeSecureCookies) const
+std::pair<String, bool> CookieJarCurlDatabase::cookieRequestHeaderFieldValue(const NetworkStorageSession& session, const URL& firstParty, const SameSiteInfo&, const URL& url, std::optional<uint64_t> frameID, std::optional<uint64_t> pageID, IncludeSecureCookies) const
 {
     UNUSED_PARAM(frameID);
     UNUSED_PARAM(pageID);
@@ -104,7 +104,7 @@ bool CookieJarCurlDatabase::cookiesEnabled(const NetworkStorageSession& session)
     return session.cookieDatabase().isEnabled();
 }
 
-bool CookieJarCurlDatabase::getRawCookies(const NetworkStorageSession& session, const URL& firstParty, const URL&, std::optional<uint64_t> frameID, std::optional<uint64_t> pageID, Vector<Cookie>& rawCookies) const
+bool CookieJarCurlDatabase::getRawCookies(const NetworkStorageSession& session, const URL& firstParty, const SameSiteInfo&, const URL&, std::optional<uint64_t> frameID, std::optional<uint64_t> pageID, Vector<Cookie>& rawCookies) const
 {
     UNUSED_PARAM(frameID);
     UNUSED_PARAM(pageID);
index a97f9b7..fda1557 100644 (file)
 namespace WebCore {
 
 class CookieJarCurlDatabase : public CookieJarCurl {
-    std::pair<String, bool> cookiesForDOM(const NetworkStorageSession&, const URL& firstParty, const URL&, std::optional<uint64_t> frameID, std::optional<uint64_t> pageID, IncludeSecureCookies) const override;
-    void setCookiesFromDOM(const NetworkStorageSession&, const URL& firstParty, const URL&, std::optional<uint64_t> frameID, std::optional<uint64_t> pageID, const String&) const override;
+    std::pair<String, bool> cookiesForDOM(const NetworkStorageSession&, const URL& firstParty, const SameSiteInfo&, const URL&, std::optional<uint64_t> frameID, std::optional<uint64_t> pageID, IncludeSecureCookies) const override;
+    void setCookiesFromDOM(const NetworkStorageSession&, const URL& firstParty, const SameSiteInfo&, const URL&, std::optional<uint64_t> frameID, std::optional<uint64_t> pageID, const String&) const override;
     void setCookiesFromHTTPResponse(const NetworkStorageSession&, const URL&, const String&) const override;
     bool cookiesEnabled(const NetworkStorageSession&) const override;
-    std::pair<String, bool> cookieRequestHeaderFieldValue(const NetworkStorageSession&, const URL& firstParty, const URL&, std::optional<uint64_t> frameID, std::optional<uint64_t> pageID, IncludeSecureCookies) const override;
+    std::pair<String, bool> cookieRequestHeaderFieldValue(const NetworkStorageSession&, const URL& firstParty, const SameSiteInfo&, const URL&, std::optional<uint64_t> frameID, std::optional<uint64_t> pageID, IncludeSecureCookies) const override;
     std::pair<String, bool> cookieRequestHeaderFieldValue(const NetworkStorageSession&, const CookieRequestHeaderFieldProxy&) const override;
-    bool getRawCookies(const NetworkStorageSession&, const URL& firstParty, const URL&, std::optional<uint64_t> frameID, std::optional<uint64_t> pageID, Vector<Cookie>&) const override;
+    bool getRawCookies(const NetworkStorageSession&, const URL& firstParty, const SameSiteInfo&, const URL&, std::optional<uint64_t> frameID, std::optional<uint64_t> pageID, Vector<Cookie>&) const override;
     void deleteCookie(const NetworkStorageSession&, const URL&, const String&) const override;
     void getHostnamesWithCookies(const NetworkStorageSession&, HashSet<String>& hostnames) const override;
     void deleteCookiesForHostnames(const NetworkStorageSession&, const Vector<String>& cookieHostNames) const override;
index 68622ea..cc4d3cc 100644 (file)
@@ -43,6 +43,7 @@
 #include "Logging.h"
 #include "NetworkStorageSession.h"
 #include "ResourceHandleInternal.h"
+#include "SameSiteInfo.h"
 #include "SharedBuffer.h"
 #include "SynchronousLoaderClient.h"
 #include "TextEncoding.h"
@@ -145,7 +146,7 @@ Ref<CurlRequest> ResourceHandle::createCurlRequest(ResourceRequest& request, Req
         auto& storageSession = NetworkStorageSession::defaultStorageSession();
         auto& cookieJar = storageSession.cookieStorage();
         auto includeSecureCookies = request.url().protocolIs("https") ? IncludeSecureCookies::Yes : IncludeSecureCookies::No;
-        String cookieHeaderField = cookieJar.cookieRequestHeaderFieldValue(storageSession, request.firstPartyForCookies(), request.url(), std::nullopt, std::nullopt, includeSecureCookies).first;
+        String cookieHeaderField = cookieJar.cookieRequestHeaderFieldValue(storageSession, request.firstPartyForCookies(), SameSiteInfo::create(request), request.url(), std::nullopt, std::nullopt, includeSecureCookies).first;
         if (!cookieHeaderField.isEmpty())
             request.addHTTPHeaderField(HTTPHeaderName::Cookie, cookieHeaderField);
     }
index bdd0612..403a63f 100644 (file)
@@ -34,6 +34,7 @@
 
 #import "Cookie.h"
 #import "CookieStorage.h"
+#import "SameSiteInfo.h"
 #import "URL.h"
 #import <wtf/Optional.h>
 #import <wtf/ProcessPrivilege.h>
@@ -66,21 +67,70 @@ static void deleteHTTPCookie(CFHTTPCookieStorageRef cookieStorage, NSHTTPCookie
     CFHTTPCookieStorageDeleteCookie(cookieStorage, [cookie _GetInternalCFHTTPCookie]);
 }
 
-static NSArray *httpCookiesForURL(CFHTTPCookieStorageRef cookieStorage, NSURL *firstParty, NSURL *url)
+static NSArray *cookiesForURL(NSHTTPCookieStorage *storage, NSURL *url, NSURL *mainDocumentURL, const std::optional<SameSiteInfo>& sameSiteInfo, NSString *partition = nullptr)
+{
+    // The _getCookiesForURL: method calls the completionHandler synchronously. We use std::optional<> to ensure this invariant.
+    std::optional<RetainPtr<NSArray *>> cookiesPtr;
+    auto completionHandler = [&cookiesPtr] (NSArray *cookies) { cookiesPtr = retainPtr(cookies); };
+    NSDictionary *policyProperties;
+    if (!sameSiteInfo)
+        policyProperties = nullptr;
+    else {
+        static NSURL *emptyURL = [[NSURL alloc] initWithString:@""];
+        policyProperties = @{
+            @"_kCFHTTPCookiePolicyPropertySiteForCookies": sameSiteInfo->isSameSite ? url : emptyURL,
+            @"_kCFHTTPCookiePolicyPropertyIsTopLevelNavigation": [NSNumber numberWithBool:sameSiteInfo->isTopSite],
+        };
+    }
+    if ([storage respondsToSelector:@selector(_getCookiesForURL:mainDocumentURL:partition:policyProperties:completionHandler:)])
+        [storage _getCookiesForURL:url mainDocumentURL:mainDocumentURL partition:partition policyProperties:policyProperties completionHandler:completionHandler];
+    else
+        [storage _getCookiesForURL:url mainDocumentURL:mainDocumentURL partition:partition completionHandler:completionHandler];
+    UNUSED_PARAM(sameSiteInfo);
+    ASSERT(!!cookiesPtr);
+    return cookiesPtr->autorelease();
+}
+
+static void setHTTPCookiesForURL(CFHTTPCookieStorageRef cookieStorage, NSArray *cookies, NSURL *url, NSURL *mainDocumentURL, const SameSiteInfo& sameSiteInfo)
+{
+    ASSERT(hasProcessPrivilege(ProcessPrivilege::CanAccessRawCookies));
+    // FIXME: We should not allocate the empty URL. Instead use CFSTR() and convert to NSString. Even better define this constant
+    // somewhere it can be shared.
+    static NSURL *emptyURL = [[NSURL alloc] initWithString:@""];
+    NSDictionary *policyProperties = @{
+        (NSString *)_kCFHTTPCookiePolicyPropertySiteForCookies: sameSiteInfo.isSameSite ? url : emptyURL,
+        (NSString *)_kCFHTTPCookiePolicyPropertyIsTopLevelNavigation: [NSNumber numberWithBool:sameSiteInfo.isTopSite],
+    };
+    if (!cookieStorage) {
+        if ([NSHTTPCookieStorage instancesRespondToSelector:@selector(_setCookies:forURL:mainDocumentURL:policyProperties:)])
+            [[NSHTTPCookieStorage sharedHTTPCookieStorage] _setCookies:cookies forURL:url mainDocumentURL:mainDocumentURL policyProperties:policyProperties];
+        else
+            [[NSHTTPCookieStorage sharedHTTPCookieStorage] setCookies:cookies forURL:url mainDocumentURL:mainDocumentURL];
+        return;
+    }
+    if ([NSHTTPCookieStorage instancesRespondToSelector:@selector(_setCookies:forURL:mainDocumentURL:policyProperties:)]) {
+        // 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]);
+        [nsCookieStorage _setCookies:cookies forURL:url mainDocumentURL:mainDocumentURL policyProperties:policyProperties];
+    } else {
+        auto cfCookies = adoptCF([NSHTTPCookie _ns2cfCookies:cookies]);
+        CFHTTPCookieStorageSetCookies(cookieStorage, cfCookies.get(), [url _cfurl], [mainDocumentURL _cfurl]);
+    }
+}
+
+static NSArray *httpCookiesForURL(CFHTTPCookieStorageRef cookieStorage, NSURL *firstParty, const std::optional<SameSiteInfo>& sameSiteInfo, NSURL *url)
 {
     ASSERT(hasProcessPrivilege(ProcessPrivilege::CanAccessRawCookies));
     if (!cookieStorage)
         cookieStorage = _CFHTTPCookieStorageGetDefault(kCFAllocatorDefault);
 
-    bool secure = ![[url scheme] caseInsensitiveCompare:@"https"];
-    
-    auto cookies = adoptCF(_CFHTTPCookieStorageCopyCookiesForURLWithMainDocumentURL(cookieStorage, static_cast<CFURLRef>(url), static_cast<CFURLRef>(firstParty), secure));
-    NSArray *nsCookies = [NSHTTPCookie _cf2nsCookies:cookies.get()];
-
-    return nsCookies;
+    // 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);
 }
 
-    
 static RetainPtr<NSArray> filterCookies(NSArray *unfilteredCookies)
 {
     ASSERT(hasProcessPrivilege(ProcessPrivilege::CanAccessRawCookies));
@@ -130,7 +180,7 @@ static bool cookiesAreBlockedForURL(const NetworkStorageSession& session, const
     return session.shouldBlockCookies(firstParty, url);
 }
 
-static NSArray *cookiesInPartitionForURL(const NetworkStorageSession& session, const URL& firstParty, const URL& url, std::optional<uint64_t> frameID, std::optional<uint64_t> pageID)
+static NSArray *cookiesInPartitionForURL(const NetworkStorageSession& session, const URL& firstParty, const SameSiteInfo& sameSiteInfo, const URL& url, std::optional<uint64_t> frameID, std::optional<uint64_t> pageID)
 {
     ASSERT(hasProcessPrivilege(ProcessPrivilege::CanAccessRawCookies));
     String partition = session.cookieStoragePartition(firstParty, url, frameID, pageID);
@@ -144,42 +194,34 @@ static NSArray *cookiesInPartitionForURL(const NetworkStorageSession& session, c
         cookieStorage = adoptNS([[NSHTTPCookieStorage alloc] _initWithCFHTTPCookieStorage:storage.get()]);
     else
         cookieStorage = [NSHTTPCookieStorage sharedHTTPCookieStorage];
-
-    // The _getCookiesForURL: method calls the completionHandler synchronously.
-    std::optional<RetainPtr<NSArray *>> cookiesPtr;
-    [cookieStorage _getCookiesForURL:url mainDocumentURL:firstParty partition:partition completionHandler:[&cookiesPtr](NSArray *cookies) {
-        cookiesPtr = retainPtr(cookies);
-    }];
-    ASSERT(!!cookiesPtr);
-
-    return cookiesPtr->autorelease();
+    return cookiesForURL(cookieStorage.get(), url, firstParty, sameSiteInfo, partition);
 }
 
 #endif // HAVE(CFNETWORK_STORAGE_PARTITIONING)
     
-static NSArray *cookiesForURL(const NetworkStorageSession& session, const URL& firstParty, const URL& url, std::optional<uint64_t> frameID, std::optional<uint64_t> pageID)
+static NSArray *cookiesForURL(const NetworkStorageSession& session, const URL& firstParty, const SameSiteInfo& sameSiteInfo, const URL& url, std::optional<uint64_t> frameID, std::optional<uint64_t> pageID)
 {
 #if HAVE(CFNETWORK_STORAGE_PARTITIONING)
     if (cookiesAreBlockedForURL(session, firstParty, url))
         return nil;
     
-    if (NSArray *cookies = cookiesInPartitionForURL(session, firstParty, url, frameID, pageID))
+    if (NSArray *cookies = cookiesInPartitionForURL(session, firstParty, sameSiteInfo, url, frameID, pageID))
         return cookies;
 #else
     UNUSED_PARAM(frameID);
     UNUSED_PARAM(pageID);
 #endif
-    return httpCookiesForURL(session.cookieStorage().get(), firstParty, url);
+    return httpCookiesForURL(session.cookieStorage().get(), firstParty, sameSiteInfo, url);
 }
 
 enum IncludeHTTPOnlyOrNot { DoNotIncludeHTTPOnly, IncludeHTTPOnly };
-static std::pair<String, bool> cookiesForSession(const NetworkStorageSession& session, const URL& firstParty, const URL& url, std::optional<uint64_t> frameID, std::optional<uint64_t> pageID, IncludeHTTPOnlyOrNot includeHTTPOnly, IncludeSecureCookies includeSecureCookies)
+static std::pair<String, bool> cookiesForSession(const NetworkStorageSession& session, const URL& firstParty, const SameSiteInfo& sameSiteInfo, const URL& url, std::optional<uint64_t> frameID, std::optional<uint64_t> pageID, IncludeHTTPOnlyOrNot includeHTTPOnly, IncludeSecureCookies includeSecureCookies)
 {
     ASSERT(hasProcessPrivilege(ProcessPrivilege::CanAccessRawCookies));
 
     BEGIN_BLOCK_OBJC_EXCEPTIONS;
 
-    NSArray *cookies = cookiesForURL(session, firstParty, url, frameID, pageID);
+    NSArray *cookies = cookiesForURL(session, firstParty, sameSiteInfo, url, frameID, pageID);
     if (![cookies count])
         return { String(), false }; // Return a null string, not an empty one that StringBuilder would create below.
 
@@ -211,19 +253,6 @@ static std::pair<String, bool> cookiesForSession(const NetworkStorageSession& se
     return { String(), false };
 }
 
-static void setHTTPCookiesForURL(CFHTTPCookieStorageRef cookieStorage, NSArray *cookies, NSURL *url, NSURL *mainDocumentURL)
-{
-    ASSERT(hasProcessPrivilege(ProcessPrivilege::CanAccessRawCookies));
-
-    if (!cookieStorage) {
-        [[NSHTTPCookieStorage sharedHTTPCookieStorage] setCookies:cookies forURL:url mainDocumentURL:mainDocumentURL];
-        return;
-    }
-
-    auto cfCookies = adoptCF([NSHTTPCookie _ns2cfCookies:cookies]);
-    CFHTTPCookieStorageSetCookies(cookieStorage, cfCookies.get(), [url _cfurl], [mainDocumentURL _cfurl]);
-}
-
 static void deleteAllHTTPCookies(CFHTTPCookieStorageRef cookieStorage)
 {
     ASSERT(hasProcessPrivilege(ProcessPrivilege::CanAccessRawCookies));
@@ -242,22 +271,22 @@ static void deleteAllHTTPCookies(CFHTTPCookieStorageRef cookieStorage)
     CFHTTPCookieStorageDeleteAllCookies(cookieStorage);
 }
 
-std::pair<String, bool> cookiesForDOM(const NetworkStorageSession& session, const URL& firstParty, const URL& url, std::optional<uint64_t> frameID, std::optional<uint64_t> pageID, IncludeSecureCookies includeSecureCookies)
+std::pair<String, bool> cookiesForDOM(const NetworkStorageSession& session, const URL& firstParty, const SameSiteInfo& sameSiteInfo, const URL& url, std::optional<uint64_t> frameID, std::optional<uint64_t> pageID, IncludeSecureCookies includeSecureCookies)
 {
-    return cookiesForSession(session, firstParty, url, frameID, pageID, DoNotIncludeHTTPOnly, includeSecureCookies);
+    return cookiesForSession(session, firstParty, sameSiteInfo, url, frameID, pageID, DoNotIncludeHTTPOnly, includeSecureCookies);
 }
 
-std::pair<String, bool> cookieRequestHeaderFieldValue(const NetworkStorageSession& session, const URL& firstParty, const URL& url, std::optional<uint64_t> frameID, std::optional<uint64_t> pageID, IncludeSecureCookies includeSecureCookies)
+std::pair<String, bool> cookieRequestHeaderFieldValue(const NetworkStorageSession& session, const URL& firstParty, const SameSiteInfo& sameSiteInfo, const URL& url, std::optional<uint64_t> frameID, std::optional<uint64_t> pageID, IncludeSecureCookies includeSecureCookies)
 {
-    return cookiesForSession(session, firstParty, url, frameID, pageID, IncludeHTTPOnly, includeSecureCookies);
+    return cookiesForSession(session, firstParty, sameSiteInfo, url, frameID, pageID, IncludeHTTPOnly, includeSecureCookies);
 }
 
 std::pair<String, bool> cookieRequestHeaderFieldValue(const NetworkStorageSession& session, const CookieRequestHeaderFieldProxy& headerFieldProxy)
 {
-    return cookiesForSession(session, headerFieldProxy.firstParty, headerFieldProxy.url, headerFieldProxy.frameID, headerFieldProxy.pageID, IncludeHTTPOnly, headerFieldProxy.includeSecureCookies);
+    return cookiesForSession(session, headerFieldProxy.firstParty, headerFieldProxy.sameSiteInfo, headerFieldProxy.url, headerFieldProxy.frameID, headerFieldProxy.pageID, IncludeHTTPOnly, headerFieldProxy.includeSecureCookies);
 }
 
-void setCookiesFromDOM(const NetworkStorageSession& session, const URL& firstParty, const URL& url, std::optional<uint64_t> frameID, std::optional<uint64_t> pageID, const String& cookieStr)
+void setCookiesFromDOM(const NetworkStorageSession& session, const URL& firstParty, const SameSiteInfo& sameSiteInfo, const URL& url, std::optional<uint64_t> frameID, std::optional<uint64_t> pageID, const String& cookieStr)
 {
     ASSERT(hasProcessPrivilege(ProcessPrivilege::CanAccessRawCookies));
 
@@ -296,7 +325,7 @@ void setCookiesFromDOM(const NetworkStorageSession& session, const URL& firstPar
     UNUSED_PARAM(pageID);
 #endif
 
-    setHTTPCookiesForURL(session.cookieStorage().get(), filteredCookies.get(), cookieURL, firstParty);
+    setHTTPCookiesForURL(session.cookieStorage().get(), filteredCookies.get(), cookieURL, firstParty, sameSiteInfo);
 
     END_BLOCK_OBJC_EXCEPTIONS;
 }
@@ -322,12 +351,12 @@ bool cookiesEnabled(const NetworkStorageSession& session)
     return false;
 }
 
-bool getRawCookies(const NetworkStorageSession& session, const URL& firstParty, const URL& url, std::optional<uint64_t> frameID, std::optional<uint64_t> pageID, Vector<Cookie>& rawCookies)
+bool getRawCookies(const NetworkStorageSession& session, const URL& firstParty, const SameSiteInfo& sameSiteInfo, const URL& url, std::optional<uint64_t> frameID, std::optional<uint64_t> pageID, Vector<Cookie>& rawCookies)
 {
     rawCookies.clear();
     BEGIN_BLOCK_OBJC_EXCEPTIONS;
 
-    NSArray *cookies = cookiesForURL(session, firstParty, url, frameID, pageID);
+    NSArray *cookies = cookiesForURL(session, firstParty, sameSiteInfo, url, frameID, pageID);
     NSUInteger count = [cookies count];
     rawCookies.reserveCapacity(count);
     for (NSUInteger i = 0; i < count; ++i) {
@@ -346,7 +375,7 @@ void deleteCookie(const NetworkStorageSession& session, const URL& url, const St
     BEGIN_BLOCK_OBJC_EXCEPTIONS;
 
     RetainPtr<CFHTTPCookieStorageRef> cookieStorage = session.cookieStorage();
-    NSArray *cookies = httpCookiesForURL(cookieStorage.get(), nil, url);
+    NSArray *cookies = httpCookiesForURL(cookieStorage.get(), nil, std::nullopt, url);
 
     NSString *cookieNameString = cookieName;
 
index fa0d481..d6f928a 100644 (file)
@@ -51,7 +51,7 @@ static inline bool httpOnlyCookieExists(const GSList* cookies, const gchar* name
     return false;
 }
 
-void setCookiesFromDOM(const NetworkStorageSession& session, const URL& firstParty, const URL& url, std::optional<uint64_t> frameID, std::optional<uint64_t> pageID, const String& value)
+void setCookiesFromDOM(const NetworkStorageSession& session, const URL& firstParty, const SameSiteInfo&, const URL& url, std::optional<uint64_t> frameID, std::optional<uint64_t> pageID, const String& value)
 {
     UNUSED_PARAM(frameID);
     UNUSED_PARAM(pageID);
@@ -119,15 +119,17 @@ static std::pair<String, bool> cookiesForSession(const NetworkStorageSession& se
     return { String::fromUTF8(cookieHeader.get()), didAccessSecureCookies };
 }
 
-std::pair<String, bool> cookiesForDOM(const NetworkStorageSession& session, const URL&, const URL& url, std::optional<uint64_t> frameID, std::optional<uint64_t> pageID, IncludeSecureCookies includeSecureCookies)
+std::pair<String, bool> cookiesForDOM(const NetworkStorageSession& session, const URL& firstParty, const SameSiteInfo&, const URL& url, std::optional<uint64_t> frameID, std::optional<uint64_t> pageID, IncludeSecureCookies includeSecureCookies)
 {
+    UNUSED_PARAM(firstParty);
     UNUSED_PARAM(frameID);
     UNUSED_PARAM(pageID);
     return cookiesForSession(session, url, false, includeSecureCookies);
 }
 
-std::pair<String, bool> cookieRequestHeaderFieldValue(const NetworkStorageSession& session, const URL& /*firstParty*/, const URL& url, std::optional<uint64_t> frameID, std::optional<uint64_t> pageID, IncludeSecureCookies includeSecureCookies)
+std::pair<String, bool> cookieRequestHeaderFieldValue(const NetworkStorageSession& session, const URL& firstParty, const SameSiteInfo&, const URL& url, std::optional<uint64_t> frameID, std::optional<uint64_t> pageID, IncludeSecureCookies includeSecureCookies)
 {
+    UNUSED_PARAM(firstParty);
     UNUSED_PARAM(frameID);
     UNUSED_PARAM(pageID);
     // Secure cookies will still only be included if url's protocol is https.
@@ -145,8 +147,9 @@ bool cookiesEnabled(const NetworkStorageSession& session)
     return policy == SOUP_COOKIE_JAR_ACCEPT_ALWAYS || policy == SOUP_COOKIE_JAR_ACCEPT_NO_THIRD_PARTY;
 }
 
-bool getRawCookies(const NetworkStorageSession& session, const URL& /*firstParty*/, const URL& url, std::optional<uint64_t> frameID, std::optional<uint64_t> pageID, Vector<Cookie>& rawCookies)
+bool getRawCookies(const NetworkStorageSession& session, const URL& firstParty, const SameSiteInfo&, const URL& url, std::optional<uint64_t> frameID, std::optional<uint64_t> pageID, Vector<Cookie>& rawCookies)
 {
+    UNUSED_PARAM(firstParty);
     UNUSED_PARAM(frameID);
     UNUSED_PARAM(pageID);
     rawCookies.clear();
index 6686570..89467f3 100644 (file)
@@ -66,7 +66,8 @@ static inline UniqueRef<Page> createPageForServiceWorker(PageConfiguration&& con
     auto origin = data.registration.key.topOrigin().securityOrigin();
     origin->setStorageBlockingPolicy(storageBlockingPolicy);
 
-    document->setFirstPartyForCookies(topOriginURL(origin));
+    document->setFirstPartyForSameSiteCookies(topOriginURL(origin));
+    document->setFirstPartyForCookies(data.scriptURL);
     document->setDomainForCachePartition(origin->domainForCachePartition());
     mainFrame.setDocument(WTFMove(document));
     return page;
index a90b965..c0e6890 100644 (file)
@@ -91,6 +91,7 @@ Ref<Document> XSLTProcessor::createDocumentFromSource(const String& sourceString
             result->setSecurityOriginPolicy(oldDocument->securityOriginPolicy());
             result->setCookieURL(oldDocument->cookieURL());
             result->setFirstPartyForCookies(oldDocument->firstPartyForCookies());
+            result->setFirstPartyForSameSiteCookies(oldDocument->firstPartyForSameSiteCookies());
             result->setStrictMixedContentMode(oldDocument->isStrictMixedContentMode());
             result->contentSecurityPolicy()->copyStateFrom(oldDocument->contentSecurityPolicy());
             result->contentSecurityPolicy()->copyUpgradeInsecureRequestStateFrom(*oldDocument->contentSecurityPolicy());
index 220ef64..735ae46 100644 (file)
@@ -1,3 +1,50 @@
+2018-04-23  Daniel Bates  <dabates@apple.com>
+
+        Implement Same-Site cookies
+        https://bugs.webkit.org/show_bug.cgi?id=159464
+        <rdar://problem/27196358>
+
+        Reviewed by Brent Fulgham.
+
+        Pass the Same-Site info through the WebKit abstractions.
+
+        * NetworkProcess/NetworkConnectionToWebProcess.cpp:
+        (WebKit::NetworkConnectionToWebProcess::cookiesForDOM):
+        (WebKit::NetworkConnectionToWebProcess::setCookiesFromDOM):
+        (WebKit::NetworkConnectionToWebProcess::cookieRequestHeaderFieldValue):
+        (WebKit::NetworkConnectionToWebProcess::getRawCookies):
+        * NetworkProcess/NetworkConnectionToWebProcess.h:
+        * NetworkProcess/NetworkConnectionToWebProcess.messages.in:
+        * NetworkProcess/NetworkResourceLoader.cpp:
+        (WebKit::NetworkResourceLoader::logCookieInformation const):
+        (WebKit::logBlockedCookieInformation):
+        (logCookieInformationInternal):
+        (NetworkResourceLoader::logCookieInformation):
+        * NetworkProcess/NetworkResourceLoader.h:
+        * NetworkProcess/cache/NetworkCacheSpeculativeLoadManager.cpp:
+        (WebKit::NetworkCache::constructRevalidationRequest):
+        * NetworkProcess/cache/NetworkCacheSubresourcesEntry.cpp:
+        (WebKit::NetworkCache::SubresourceInfo::encode const):
+        (WebKit::NetworkCache::SubresourceInfo::decode):
+        (WebKit::NetworkCache::SubresourceInfo::SubresourceInfo):
+        * NetworkProcess/cache/NetworkCacheSubresourcesEntry.h:
+        (WebKit::NetworkCache::SubresourceInfo::isSameSite const):
+        (WebKit::NetworkCache::SubresourceInfo::isTopSite const): Returns false; subresources do not represent
+        a top-level navigation.
+        * NetworkProcess/cocoa/NetworkDataTaskCocoa.mm:
+        (WebKit::NetworkDataTaskCocoa::isThirdPartyRequest):
+        (WebKit::updateTaskWithFirstPartyForSameSiteCookies):
+        (WebKit::NetworkDataTaskCocoa::NetworkDataTaskCocoa):
+        (WebKit::NetworkDataTaskCocoa::willPerformHTTPRedirection):
+        * UIProcess/WebProcessPool.cpp:
+        (WebKit::WebProcessPool::download):
+        * WebProcess/WebCoreSupport/WebPlatformStrategies.cpp:
+        (WebKit::WebPlatformStrategies::cookiesForDOM):
+        (WebKit::WebPlatformStrategies::setCookiesFromDOM):
+        (WebKit::WebPlatformStrategies::cookieRequestHeaderFieldValue):
+        (WebKit::WebPlatformStrategies::getRawCookies):
+        * WebProcess/WebCoreSupport/WebPlatformStrategies.h:
+
 2018-04-23  Chris Dumez  <cdumez@apple.com>
 
         WebProcessProxy frequently re-takes a process assertion for the network process even though is already has one
index a32f728..179c24f 100644 (file)
@@ -55,6 +55,7 @@
 #include <WebCore/PlatformCookieJar.h>
 #include <WebCore/ResourceLoaderOptions.h>
 #include <WebCore/ResourceRequest.h>
+#include <WebCore/SameSiteInfo.h>
 #include <WebCore/SecurityPolicy.h>
 #include <pal/SessionID.h>
 
@@ -357,23 +358,23 @@ void NetworkConnectionToWebProcess::convertMainResourceLoadToDownload(PAL::Sessi
     loader->convertToDownload(downloadID, request, response);
 }
 
-void NetworkConnectionToWebProcess::cookiesForDOM(PAL::SessionID sessionID, const URL& firstParty, const URL& url, std::optional<uint64_t> frameID, std::optional<uint64_t> pageID, IncludeSecureCookies includeSecureCookies, String& cookieString, bool& secureCookiesAccessed)
+void NetworkConnectionToWebProcess::cookiesForDOM(PAL::SessionID sessionID, const URL& firstParty, const SameSiteInfo& sameSiteInfo, const URL& url, std::optional<uint64_t> frameID, std::optional<uint64_t> pageID, IncludeSecureCookies includeSecureCookies, String& cookieString, bool& secureCookiesAccessed)
 {
     auto& networkStorageSession = storageSession(sessionID);
-    std::tie(cookieString, secureCookiesAccessed) = WebCore::cookiesForDOM(networkStorageSession, firstParty, url, frameID, pageID, includeSecureCookies);
+    std::tie(cookieString, secureCookiesAccessed) = WebCore::cookiesForDOM(networkStorageSession, firstParty, sameSiteInfo, url, frameID, pageID, includeSecureCookies);
 #if HAVE(CFNETWORK_STORAGE_PARTITIONING) && !RELEASE_LOG_DISABLED
     if (NetworkProcess::singleton().shouldLogCookieInformation())
-        NetworkResourceLoader::logCookieInformation("NetworkConnectionToWebProcess::cookiesForDOM", reinterpret_cast<const void*>(this), networkStorageSession, firstParty, url, emptyString(), frameID, pageID, std::nullopt);
+        NetworkResourceLoader::logCookieInformation("NetworkConnectionToWebProcess::cookiesForDOM", reinterpret_cast<const void*>(this), networkStorageSession, firstParty, sameSiteInfo, url, emptyString(), frameID, pageID, std::nullopt);
 #endif
 }
 
-void NetworkConnectionToWebProcess::setCookiesFromDOM(PAL::SessionID sessionID, const URL& firstParty, const URL& url, std::optional<uint64_t> frameID, std::optional<uint64_t> pageID, const String& cookieString)
+void NetworkConnectionToWebProcess::setCookiesFromDOM(PAL::SessionID sessionID, const URL& firstParty, const SameSiteInfo& sameSiteInfo, const URL& url, std::optional<uint64_t> frameID, std::optional<uint64_t> pageID, const String& cookieString)
 {
     auto& networkStorageSession = storageSession(sessionID);
-    WebCore::setCookiesFromDOM(networkStorageSession, firstParty, url, frameID, pageID, cookieString);
+    WebCore::setCookiesFromDOM(networkStorageSession, firstParty, sameSiteInfo, url, frameID, pageID, cookieString);
 #if HAVE(CFNETWORK_STORAGE_PARTITIONING) && !RELEASE_LOG_DISABLED
     if (NetworkProcess::singleton().shouldLogCookieInformation())
-        NetworkResourceLoader::logCookieInformation("NetworkConnectionToWebProcess::setCookiesFromDOM", reinterpret_cast<const void*>(this), networkStorageSession, firstParty, url, emptyString(), frameID, pageID, std::nullopt);
+        NetworkResourceLoader::logCookieInformation("NetworkConnectionToWebProcess::setCookiesFromDOM", reinterpret_cast<const void*>(this), networkStorageSession, firstParty, sameSiteInfo, url, emptyString(), frameID, pageID, std::nullopt);
 #endif
 }
 
@@ -382,14 +383,14 @@ void NetworkConnectionToWebProcess::cookiesEnabled(PAL::SessionID sessionID, boo
     result = WebCore::cookiesEnabled(storageSession(sessionID));
 }
 
-void NetworkConnectionToWebProcess::cookieRequestHeaderFieldValue(PAL::SessionID sessionID, const URL& firstParty, const URL& url, std::optional<uint64_t> frameID, std::optional<uint64_t> pageID, IncludeSecureCookies includeSecureCookies, String& cookieString, bool& secureCookiesAccessed)
+void NetworkConnectionToWebProcess::cookieRequestHeaderFieldValue(PAL::SessionID sessionID, const URL& firstParty, const SameSiteInfo& sameSiteInfo, const URL& url, std::optional<uint64_t> frameID, std::optional<uint64_t> pageID, IncludeSecureCookies includeSecureCookies, String& cookieString, bool& secureCookiesAccessed)
 {
-    std::tie(cookieString, secureCookiesAccessed) = WebCore::cookieRequestHeaderFieldValue(storageSession(sessionID), firstParty, url, frameID, pageID, includeSecureCookies);
+    std::tie(cookieString, secureCookiesAccessed) = WebCore::cookieRequestHeaderFieldValue(storageSession(sessionID), firstParty, sameSiteInfo, url, frameID, pageID, includeSecureCookies);
 }
 
-void NetworkConnectionToWebProcess::getRawCookies(PAL::SessionID sessionID, const URL& firstParty, const URL& url, std::optional<uint64_t> frameID, std::optional<uint64_t> pageID, Vector<Cookie>& result)
+void NetworkConnectionToWebProcess::getRawCookies(PAL::SessionID sessionID, const URL& firstParty, const SameSiteInfo& sameSiteInfo, const URL& url, std::optional<uint64_t> frameID, std::optional<uint64_t> pageID, Vector<Cookie>& result)
 {
-    WebCore::getRawCookies(storageSession(sessionID), firstParty, url, frameID, pageID, result);
+    WebCore::getRawCookies(storageSession(sessionID), firstParty, sameSiteInfo, url, frameID, pageID, result);
 }
 
 void NetworkConnectionToWebProcess::deleteCookie(PAL::SessionID sessionID, const URL& url, const String& cookieName)
index f526b27..4cea2c6 100644 (file)
@@ -40,6 +40,7 @@ class BlobDataFileReference;
 class HTTPHeaderMap;
 class ResourceError;
 class ResourceRequest;
+struct SameSiteInfo;
 
 enum class IncludeSecureCookies;
 }
@@ -137,11 +138,11 @@ private:
     void startDownload(PAL::SessionID, DownloadID, const WebCore::ResourceRequest&, const String& suggestedName = { });
     void convertMainResourceLoadToDownload(PAL::SessionID, uint64_t mainResourceLoadIdentifier, DownloadID, const WebCore::ResourceRequest&, const WebCore::ResourceResponse&);
 
-    void cookiesForDOM(PAL::SessionID, const WebCore::URL& firstParty, const WebCore::URL&, std::optional<uint64_t> frameID, std::optional<uint64_t> pageID, WebCore::IncludeSecureCookies, String& cookieString, bool& secureCookiesAccessed);
-    void setCookiesFromDOM(PAL::SessionID, const WebCore::URL& firstParty, const WebCore::URL&, std::optional<uint64_t> frameID, std::optional<uint64_t> pageID, const String&);
+    void cookiesForDOM(PAL::SessionID, const WebCore::URL& firstParty, const WebCore::SameSiteInfo&, const WebCore::URL&, std::optional<uint64_t> frameID, std::optional<uint64_t> pageID, WebCore::IncludeSecureCookies, String& cookieString, bool& secureCookiesAccessed);
+    void setCookiesFromDOM(PAL::SessionID, const WebCore::URL& firstParty, const WebCore::SameSiteInfo&, const WebCore::URL&, std::optional<uint64_t> frameID, std::optional<uint64_t> pageID, const String&);
     void cookiesEnabled(PAL::SessionID, bool& result);
-    void cookieRequestHeaderFieldValue(PAL::SessionID, const WebCore::URL& firstParty, const WebCore::URL&, std::optional<uint64_t> frameID, std::optional<uint64_t> pageID, WebCore::IncludeSecureCookies, String& cookieString, bool& secureCookiesAccessed);
-    void getRawCookies(PAL::SessionID, const WebCore::URL& firstParty, const WebCore::URL&, std::optional<uint64_t> frameID, std::optional<uint64_t> pageID, Vector<WebCore::Cookie>&);
+    void cookieRequestHeaderFieldValue(PAL::SessionID, const WebCore::URL& firstParty, const WebCore::SameSiteInfo&, const WebCore::URL&, std::optional<uint64_t> frameID, std::optional<uint64_t> pageID, WebCore::IncludeSecureCookies, String& cookieString, bool& secureCookiesAccessed);
+    void getRawCookies(PAL::SessionID, const WebCore::URL& firstParty, const WebCore::SameSiteInfo&, const WebCore::URL&, std::optional<uint64_t> frameID, std::optional<uint64_t> pageID, Vector<WebCore::Cookie>&);
     void deleteCookie(PAL::SessionID, const WebCore::URL&, const String& cookieName);
 
     void registerFileBlobURL(const WebCore::URL&, const String& path, SandboxExtension::Handle&&, const String& contentType);
index d652490..9a5a4f2 100644 (file)
@@ -33,11 +33,11 @@ messages -> NetworkConnectionToWebProcess LegacyReceiver {
     StartDownload(PAL::SessionID sessionID, WebKit::DownloadID downloadID, WebCore::ResourceRequest request, String suggestedName)
     ConvertMainResourceLoadToDownload(PAL::SessionID sessionID, uint64_t mainResourceLoadIdentifier, WebKit::DownloadID downloadID, WebCore::ResourceRequest request, WebCore::ResourceResponse response)
 
-    CookiesForDOM(PAL::SessionID sessionID, WebCore::URL firstParty, WebCore::URL url, std::optional<uint64_t> frameID, std::optional<uint64_t> pageID, enum WebCore::IncludeSecureCookies includeSecureCookies) -> (String cookieString, bool didAccessSecureCookies)
-    SetCookiesFromDOM(PAL::SessionID sessionID, WebCore::URL firstParty, WebCore::URL url, std::optional<uint64_t> frameID, std::optional<uint64_t> pageID, String cookieString)
+    CookiesForDOM(PAL::SessionID sessionID, WebCore::URL firstParty, struct WebCore::SameSiteInfo sameSiteInfo, WebCore::URL url, std::optional<uint64_t> frameID, std::optional<uint64_t> pageID, enum WebCore::IncludeSecureCookies includeSecureCookies) -> (String cookieString, bool didAccessSecureCookies)
+    SetCookiesFromDOM(PAL::SessionID sessionID, WebCore::URL firstParty, struct WebCore::SameSiteInfo sameSiteInfo, WebCore::URL url, std::optional<uint64_t> frameID, std::optional<uint64_t> pageID, String cookieString)
     CookiesEnabled(PAL::SessionID sessionID) -> (bool enabled)
-    CookieRequestHeaderFieldValue(PAL::SessionID sessionID, WebCore::URL firstParty, WebCore::URL url, std::optional<uint64_t> frameID, std::optional<uint64_t> pageID, enum WebCore::IncludeSecureCookies includeSecureCookies) -> (String cookieString, bool didAccessSecureCookies)
-    GetRawCookies(PAL::SessionID sessionID, WebCore::URL firstParty, WebCore::URL url, std::optional<uint64_t> frameID, std::optional<uint64_t> pageID) -> (Vector<WebCore::Cookie> cookies)
+    CookieRequestHeaderFieldValue(PAL::SessionID sessionID, WebCore::URL firstParty, struct WebCore::SameSiteInfo sameSiteInfo, WebCore::URL url, std::optional<uint64_t> frameID, std::optional<uint64_t> pageID, enum WebCore::IncludeSecureCookies includeSecureCookies) -> (String cookieString, bool didAccessSecureCookies)
+    GetRawCookies(PAL::SessionID sessionID, WebCore::URL firstParty, struct WebCore::SameSiteInfo sameSiteInfo, WebCore::URL url, std::optional<uint64_t> frameID, std::optional<uint64_t> pageID) -> (Vector<WebCore::Cookie> cookies)
     DeleteCookie(PAL::SessionID sessionID, WebCore::URL url, String cookieName)
 
     RegisterFileBlobURL(WebCore::URL url, String path, WebKit::SandboxExtension::Handle extensionHandle, String contentType)
index b370036..6fd670f 100644 (file)
@@ -46,6 +46,7 @@
 #include <WebCore/HTTPHeaderNames.h>
 #include <WebCore/NetworkLoadMetrics.h>
 #include <WebCore/ProtectionSpace.h>
+#include <WebCore/SameSiteInfo.h>
 #include <WebCore/SharedBuffer.h>
 #include <WebCore/SynchronousLoaderClient.h>
 #include <wtf/RunLoop.h>
@@ -870,10 +871,10 @@ void NetworkResourceLoader::logCookieInformation() const
     auto networkStorageSession = WebCore::NetworkStorageSession::storageSession(sessionID());
     ASSERT(networkStorageSession);
 
-    logCookieInformation("NetworkResourceLoader", reinterpret_cast<const void*>(this), *networkStorageSession, originalRequest().firstPartyForCookies(), originalRequest().url(), originalRequest().httpReferrer(), frameID(), pageID(), identifier());
+    logCookieInformation("NetworkResourceLoader", reinterpret_cast<const void*>(this), *networkStorageSession, originalRequest().firstPartyForCookies(), SameSiteInfo::create(originalRequest()), originalRequest().url(), originalRequest().httpReferrer(), frameID(), pageID(), identifier());
 }
 
-static void logBlockedCookieInformation(const String& label, const void* loggedObject, const WebCore::NetworkStorageSession& networkStorageSession, const WebCore::URL& firstParty, const WebCore::URL& url, const String& referrer, std::optional<uint64_t> frameID, std::optional<uint64_t> pageID, std::optional<uint64_t> identifier)
+static void logBlockedCookieInformation(const String& label, const void* loggedObject, const WebCore::NetworkStorageSession& networkStorageSession, const WebCore::URL& firstParty, const SameSiteInfo& sameSiteInfo, const WebCore::URL& url, const String& referrer, std::optional<uint64_t> frameID, std::optional<uint64_t> pageID, std::optional<uint64_t> identifier)
 {
     ASSERT(NetworkResourceLoader::shouldLogCookieInformation());
 
@@ -892,18 +893,20 @@ static void logBlockedCookieInformation(const String& label, const void* loggedO
     LOCAL_LOG(R"(  "partition": "%{public}s",)", "BLOCKED");
     LOCAL_LOG(R"(  "hasStorageAccess": %{public}s,)", "false");
     LOCAL_LOG(R"(  "referer": "%{public}s",)", escapedReferrer.utf8().data());
+    LOCAL_LOG(R"(  "isSameSite": "%{public}s",)", sameSiteInfo.isSameSite ? "true" : "false");
+    LOCAL_LOG(R"(  "isTopSite": "%{public}s",)", sameSiteInfo.isTopSite ? "true" : "false");
     LOCAL_LOG(R"(  "cookies": [])");
     LOCAL_LOG(R"(  "})");
 #undef LOCAL_LOG
 #undef LOCAL_LOG_IF_ALLOWED
 }
 
-static void logCookieInformationInternal(const String& label, const void* loggedObject, const WebCore::NetworkStorageSession& networkStorageSession, const WebCore::URL& partition, const WebCore::URL& url, const String& referrer, std::optional<uint64_t> frameID, std::optional<uint64_t> pageID, std::optional<uint64_t> identifier)
+static void logCookieInformationInternal(const String& label, const void* loggedObject, const WebCore::NetworkStorageSession& networkStorageSession, const WebCore::URL& partition, const WebCore::SameSiteInfo& sameSiteInfo, const WebCore::URL& url, const String& referrer, std::optional<uint64_t> frameID, std::optional<uint64_t> pageID, std::optional<uint64_t> identifier)
 {
     ASSERT(NetworkResourceLoader::shouldLogCookieInformation());
 
     Vector<WebCore::Cookie> cookies;
-    if (!WebCore::getRawCookies(networkStorageSession, partition, url, frameID, pageID, cookies))
+    if (!WebCore::getRawCookies(networkStorageSession, partition, sameSiteInfo, url, frameID, pageID, cookies))
         return;
 
     auto escapedURL = escapeForJSON(url.string());
@@ -922,6 +925,8 @@ static void logCookieInformationInternal(const String& label, const void* logged
     LOCAL_LOG(R"(  "partition": "%{public}s",)", escapedPartition.utf8().data());
     LOCAL_LOG(R"(  "hasStorageAccess": %{public}s,)", hasStorageAccess ? "true" : "false");
     LOCAL_LOG(R"(  "referer": "%{public}s",)", escapedReferrer.utf8().data());
+    LOCAL_LOG(R"(  "isSameSite": "%{public}s",)", sameSiteInfo.isSameSite ? "true" : "false");
+    LOCAL_LOG(R"(  "isTopSite": "%{public}s",)", sameSiteInfo.isTopSite ? "true" : "false");
     LOCAL_LOG(R"(  "cookies": [)");
 
     auto size = cookies.size();
@@ -937,6 +942,7 @@ static void logCookieInformationInternal(const String& label, const void* logged
         auto escapedPath = escapeForJSON(cookie.path);
         auto escapedComment = escapeForJSON(cookie.comment);
         auto escapedCommentURL = escapeForJSON(cookie.commentURL.string());
+        // FIXME: Log Same-Site policy for each cookie. See <https://bugs.webkit.org/show_bug.cgi?id=184894>.
 
         LOCAL_LOG(R"(  { "name": "%{public}s",)", escapedName.utf8().data());
         LOCAL_LOG(R"(    "value": "%{public}s",)", escapedValue.utf8().data());
@@ -956,15 +962,15 @@ static void logCookieInformationInternal(const String& label, const void* logged
 #undef LOCAL_LOG_IF_ALLOWED
 }
 
-void NetworkResourceLoader::logCookieInformation(const String& label, const void* loggedObject, const NetworkStorageSession& networkStorageSession, const URL& firstParty, const URL& url, const String& referrer, std::optional<uint64_t> frameID, std::optional<uint64_t> pageID, std::optional<uint64_t> identifier)
+void NetworkResourceLoader::logCookieInformation(const String& label, const void* loggedObject, const NetworkStorageSession& networkStorageSession, const URL& firstParty, const SameSiteInfo& sameSiteInfo, const URL& url, const String& referrer, std::optional<uint64_t> frameID, std::optional<uint64_t> pageID, std::optional<uint64_t> identifier)
 {
     ASSERT(shouldLogCookieInformation());
 
     if (networkStorageSession.shouldBlockCookies(firstParty, url))
-        logBlockedCookieInformation(label, loggedObject, networkStorageSession, firstParty, url, referrer, frameID, pageID, identifier);
+        logBlockedCookieInformation(label, loggedObject, networkStorageSession, firstParty, sameSiteInfo, url, referrer, frameID, pageID, identifier);
     else {
         auto partition = URL(ParsedURLString, networkStorageSession.cookieStoragePartition(firstParty, url, frameID, pageID));
-        logCookieInformationInternal(label, loggedObject, networkStorageSession, partition, url, referrer, frameID, pageID, identifier);
+        logCookieInformationInternal(label, loggedObject, networkStorageSession, partition, sameSiteInfo, url, referrer, frameID, pageID, identifier);
     }
 }
 #endif
index d69982d..8769708 100644 (file)
@@ -109,7 +109,7 @@ public:
 
 #if HAVE(CFNETWORK_STORAGE_PARTITIONING) && !RELEASE_LOG_DISABLED
     static bool shouldLogCookieInformation();
-    static void logCookieInformation(const String& label, const void* loggedObject, const WebCore::NetworkStorageSession&, const WebCore::URL& firstParty, const WebCore::URL&, const String& referrer, std::optional<uint64_t> frameID, std::optional<uint64_t> pageID, std::optional<uint64_t> identifier);
+    static void logCookieInformation(const String& label, const void* loggedObject, const WebCore::NetworkStorageSession&, const WebCore::URL& firstParty, const WebCore::SameSiteInfo&, const WebCore::URL&, const String& referrer, std::optional<uint64_t> frameID, std::optional<uint64_t> pageID, std::optional<uint64_t> identifier);
 #endif
 
 private:
index c53f7d2..7777f20 100644 (file)
@@ -90,6 +90,8 @@ static inline ResourceRequest constructRevalidationRequest(const Key& key, const
     ResourceRequest revalidationRequest(key.identifier());
     revalidationRequest.setHTTPHeaderFields(subResourceInfo.requestHeaders());
     revalidationRequest.setFirstPartyForCookies(subResourceInfo.firstPartyForCookies());
+    revalidationRequest.setIsSameSite(subResourceInfo.isSameSite());
+    revalidationRequest.setIsTopSite(subResourceInfo.isTopSite());
     if (!key.partition().isEmpty())
         revalidationRequest.setCachePartition(key.partition());
     ASSERT_WITH_MESSAGE(key.range().isEmpty(), "range is not supported");
index fc53ef0..7b88922 100644 (file)
@@ -45,6 +45,7 @@ void SubresourceInfo::encode(WTF::Persistence::Encoder& encoder) const
     if (m_isTransient)
         return;
 
+    encoder << m_isSameSite;
     encoder << m_firstPartyForCookies;
     encoder << m_requestHeaders;
     encoder.encodeEnum(m_priority);
@@ -64,7 +65,10 @@ bool SubresourceInfo::decode(WTF::Persistence::Decoder& decoder, SubresourceInfo
     
     if (info.m_isTransient)
         return true;
-    
+
+    if (!decoder.decode(info.m_isSameSite))
+        return false;
+
     if (!decoder.decode(info.m_firstPartyForCookies))
         return false;
 
@@ -115,6 +119,7 @@ SubresourceInfo::SubresourceInfo(const Key& key, const WebCore::ResourceRequest&
     , m_lastSeen(WallTime::now())
     , m_firstSeen(previousInfo ? previousInfo->firstSeen() : m_lastSeen)
     , m_isTransient(!previousInfo)
+    , m_isSameSite(request.isSameSite())
     , m_firstPartyForCookies(request.firstPartyForCookies())
     , m_requestHeaders(request.httpHeaderFields())
     , m_priority(request.priority())
index 0b9156e..ef252a1 100644 (file)
@@ -53,7 +53,10 @@ public:
     const WebCore::URL& firstPartyForCookies() const { ASSERT(!m_isTransient); return m_firstPartyForCookies; }
     const WebCore::HTTPHeaderMap& requestHeaders() const { ASSERT(!m_isTransient); return m_requestHeaders; }
     WebCore::ResourceLoadPriority priority() const { ASSERT(!m_isTransient); return m_priority; }
-    
+
+    bool isSameSite() const { ASSERT(!m_isTransient); return m_isSameSite; }
+    bool isTopSite() const { return false; }
+
     void setNonTransient() { m_isTransient = false; }
 
 private:
@@ -61,6 +64,7 @@ private:
     WallTime m_lastSeen;
     WallTime m_firstSeen;
     bool m_isTransient { false };
+    bool m_isSameSite { false };
     WebCore::URL m_firstPartyForCookies;
     WebCore::HTTPHeaderMap m_requestHeaders;
     WebCore::ResourceLoadPriority m_priority;
index 5f652b8..5ad36a5 100644 (file)
@@ -150,7 +150,18 @@ void NetworkDataTaskCocoa::applyCookiePartitioningPolicy(const String& requiredS
 
 bool NetworkDataTaskCocoa::isThirdPartyRequest(const WebCore::ResourceRequest& request)
 {
-    return request.partitionName(request.url().host()) != request.partitionName(request.firstPartyForCookies().host());
+    return !WebCore::registrableDomainsAreEqual(request.url(), request.firstPartyForCookies());
+}
+
+static void updateTaskWithFirstPartyForSameSiteCookies(NSURLSessionDataTask* task, const WebCore::ResourceRequest& request)
+{
+    if (request.isSameSiteUnspecified())
+        return;
+    static NSURL *emptyURL = [[NSURL alloc] initWithString:@""];
+    if ([task respondsToSelector:@selector(set_siteForCookies:)])
+        task._siteForCookies = request.isSameSite() ? task.currentRequest.URL : emptyURL;
+    if ([task respondsToSelector:@selector(set_isTopLevelNavigation:)])
+        task._isTopLevelNavigation = request.isTopSite();
 }
 
 NetworkDataTaskCocoa::NetworkDataTaskCocoa(NetworkSession& session, NetworkDataTaskClient& client, const WebCore::ResourceRequest& requestWithCredentials, uint64_t frameID, uint64_t pageID, WebCore::StoredCredentialsPolicy storedCredentialsPolicy, WebCore::ContentSniffingPolicy shouldContentSniff, WebCore::ContentEncodingSniffingPolicy shouldContentEncodingSniff, bool shouldClearReferrerOnHTTPSToHTTPRedirect, PreconnectOnly shouldPreconnectOnly)
@@ -243,6 +254,8 @@ NetworkDataTaskCocoa::NetworkDataTaskCocoa(NetworkSession& session, NetworkDataT
 
     if (WebCore::ResourceRequest::resourcePrioritiesEnabled())
         m_task.get().priority = toNSURLSessionTaskPriority(request.priority());
+
+    updateTaskWithFirstPartyForSameSiteCookies(m_task.get(), request);
 }
 
 NetworkDataTaskCocoa::~NetworkDataTaskCocoa()
@@ -365,6 +378,8 @@ void NetworkDataTaskCocoa::willPerformHTTPRedirection(WebCore::ResourceResponse&
     }
 #endif
 
+    updateTaskWithFirstPartyForSameSiteCookies(m_task.get(), request);
+
     if (m_client)
         m_client->willPerformHTTPRedirection(WTFMove(redirectResponse), WTFMove(request), WTFMove(completionHandler));
     else {
index 21dcc3f..cef3fb6 100644 (file)
@@ -1187,10 +1187,15 @@ DownloadProxy* WebProcessPool::download(WebPageProxy* initiatingPage, const Reso
         ResourceRequest updatedRequest(request);
         // Request's firstPartyForCookies will be used as Original URL of the download request.
         // We set the value to top level document's URL.
-        if (initiatingPage)
-            updatedRequest.setFirstPartyForCookies(URL(URL(), initiatingPage->pageLoadState().url()));
-        else
+        if (initiatingPage) {
+            URL initiatingPageURL = URL { URL { }, initiatingPage->pageLoadState().url() };
+            updatedRequest.setFirstPartyForCookies(initiatingPageURL);
+            updatedRequest.setIsSameSite(registrableDomainsAreEqual(initiatingPageURL, request.url()));
+        } else {
             updatedRequest.setFirstPartyForCookies(URL());
+            updatedRequest.setIsSameSite(false);
+        }
+        updatedRequest.setIsTopSite(false);
         networkProcess()->send(Messages::NetworkProcess::DownloadRequest(sessionID, downloadProxy->downloadID(), updatedRequest, suggestedFilename), 0);
         return downloadProxy;
     }
index d287320..97a3270 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2010-2017 Apple Inc. All rights reserved.
+ * Copyright (C) 2010-2018 Apple Inc. All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
@@ -58,6 +58,7 @@
 #include <WebCore/PlatformPasteboard.h>
 #include <WebCore/ProgressTracker.h>
 #include <WebCore/ResourceError.h>
+#include <WebCore/SameSiteInfo.h>
 #include <WebCore/StorageNamespace.h>
 #include <WebCore/SubframeLoader.h>
 #include <WebCore/URL.h>
@@ -108,19 +109,19 @@ BlobRegistry* WebPlatformStrategies::createBlobRegistry()
 
 // CookiesStrategy
 
-std::pair<String, bool> WebPlatformStrategies::cookiesForDOM(const NetworkStorageSession& session, const URL& firstParty, const URL& url, std::optional<uint64_t> frameID, std::optional<uint64_t> pageID, IncludeSecureCookies includeSecureCookies)
+std::pair<String, bool> WebPlatformStrategies::cookiesForDOM(const NetworkStorageSession& session, const URL& firstParty, const SameSiteInfo& sameSiteInfo, const URL& url, std::optional<uint64_t> frameID, std::optional<uint64_t> pageID, IncludeSecureCookies includeSecureCookies)
 {
     String cookieString;
     bool secureCookiesAccessed = false;
-    if (!WebProcess::singleton().ensureNetworkProcessConnection().connection().sendSync(Messages::NetworkConnectionToWebProcess::CookiesForDOM(session.sessionID(), firstParty, url, frameID, pageID, includeSecureCookies), Messages::NetworkConnectionToWebProcess::CookiesForDOM::Reply(cookieString, secureCookiesAccessed), 0))
+    if (!WebProcess::singleton().ensureNetworkProcessConnection().connection().sendSync(Messages::NetworkConnectionToWebProcess::CookiesForDOM(session.sessionID(), firstParty, sameSiteInfo, url, frameID, pageID, includeSecureCookies), Messages::NetworkConnectionToWebProcess::CookiesForDOM::Reply(cookieString, secureCookiesAccessed), 0))
         return { String(), false };
 
     return { cookieString, secureCookiesAccessed };
 }
 
-void WebPlatformStrategies::setCookiesFromDOM(const NetworkStorageSession& session, const URL& firstParty, const URL& url, std::optional<uint64_t> frameID, std::optional<uint64_t> pageID, const String& cookieString)
+void WebPlatformStrategies::setCookiesFromDOM(const NetworkStorageSession& session, const URL& firstParty, const WebCore::SameSiteInfo& sameSiteInfo, const URL& url, std::optional<uint64_t> frameID, std::optional<uint64_t> pageID, const String& cookieString)
 {
-    WebProcess::singleton().ensureNetworkProcessConnection().connection().send(Messages::NetworkConnectionToWebProcess::SetCookiesFromDOM(session.sessionID(), firstParty, url, frameID, pageID, cookieString), 0);
+    WebProcess::singleton().ensureNetworkProcessConnection().connection().send(Messages::NetworkConnectionToWebProcess::SetCookiesFromDOM(session.sessionID(), firstParty, sameSiteInfo, url, frameID, pageID, cookieString), 0);
 }
 
 bool WebPlatformStrategies::cookiesEnabled(const NetworkStorageSession& session)
@@ -131,23 +132,23 @@ bool WebPlatformStrategies::cookiesEnabled(const NetworkStorageSession& session)
     return result;
 }
 
-std::pair<String, bool> WebPlatformStrategies::cookieRequestHeaderFieldValue(const NetworkStorageSession& session, const URL& firstParty, const URL& url, std::optional<uint64_t> frameID, std::optional<uint64_t> pageID, IncludeSecureCookies includeSecureCookies)
+std::pair<String, bool> WebPlatformStrategies::cookieRequestHeaderFieldValue(const NetworkStorageSession& session, const URL& firstParty, const SameSiteInfo& sameSiteInfo, const URL& url, std::optional<uint64_t> frameID, std::optional<uint64_t> pageID, IncludeSecureCookies includeSecureCookies)
 {
-    return cookieRequestHeaderFieldValue(session.sessionID(), firstParty, url, frameID, pageID, includeSecureCookies);
+    return cookieRequestHeaderFieldValue(session.sessionID(), firstParty, sameSiteInfo, url, frameID, pageID, includeSecureCookies);
 }
 
-std::pair<String, bool> WebPlatformStrategies::cookieRequestHeaderFieldValue(PAL::SessionID sessionID, const URL& firstParty, const URL& url, std::optional<uint64_t> frameID, std::optional<uint64_t> pageID, IncludeSecureCookies includeSecureCookies)
+std::pair<String, bool> WebPlatformStrategies::cookieRequestHeaderFieldValue(PAL::SessionID sessionID, const URL& firstParty, const SameSiteInfo& sameSiteInfo, const URL& url, std::optional<uint64_t> frameID, std::optional<uint64_t> pageID, IncludeSecureCookies includeSecureCookies)
 {
     String cookieString;
     bool secureCookiesAccessed = false;
-    if (!WebProcess::singleton().ensureNetworkProcessConnection().connection().sendSync(Messages::NetworkConnectionToWebProcess::CookieRequestHeaderFieldValue(sessionID, firstParty, url, frameID, pageID, includeSecureCookies), Messages::NetworkConnectionToWebProcess::CookieRequestHeaderFieldValue::Reply(cookieString, secureCookiesAccessed), 0, Seconds::infinity(), IPC::SendSyncOption::DoNotProcessIncomingMessagesWhenWaitingForSyncReply))
+    if (!WebProcess::singleton().ensureNetworkProcessConnection().connection().sendSync(Messages::NetworkConnectionToWebProcess::CookieRequestHeaderFieldValue(sessionID, firstParty, sameSiteInfo, url, frameID, pageID, includeSecureCookies), Messages::NetworkConnectionToWebProcess::CookieRequestHeaderFieldValue::Reply(cookieString, secureCookiesAccessed), 0, Seconds::infinity(), IPC::SendSyncOption::DoNotProcessIncomingMessagesWhenWaitingForSyncReply))
         return { String(), false };
     return { cookieString, secureCookiesAccessed };
 }
 
-bool WebPlatformStrategies::getRawCookies(const NetworkStorageSession& session, const URL& firstParty, const URL& url, std::optional<uint64_t> frameID, std::optional<uint64_t> pageID, Vector<Cookie>& rawCookies)
+bool WebPlatformStrategies::getRawCookies(const NetworkStorageSession& session, const URL& firstParty, const SameSiteInfo& sameSiteInfo, const URL& url, std::optional<uint64_t> frameID, std::optional<uint64_t> pageID, Vector<Cookie>& rawCookies)
 {
-    if (!WebProcess::singleton().ensureNetworkProcessConnection().connection().sendSync(Messages::NetworkConnectionToWebProcess::GetRawCookies(session.sessionID(), firstParty, url, frameID, pageID), Messages::NetworkConnectionToWebProcess::GetRawCookies::Reply(rawCookies), 0))
+    if (!WebProcess::singleton().ensureNetworkProcessConnection().connection().sendSync(Messages::NetworkConnectionToWebProcess::GetRawCookies(session.sessionID(), firstParty, sameSiteInfo, url, frameID, pageID), Messages::NetworkConnectionToWebProcess::GetRawCookies::Reply(rawCookies), 0))
         return false;
     return true;
 }
index 1964a8f..659e763 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2010-2017 Apple Inc. All rights reserved.
+ * Copyright (C) 2010-2018 Apple Inc. All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
@@ -47,12 +47,12 @@ private:
     WebCore::BlobRegistry* createBlobRegistry() override;
 
     // WebCore::CookiesStrategy
-    std::pair<String, bool> cookiesForDOM(const WebCore::NetworkStorageSession&, const WebCore::URL& firstParty, const WebCore::URL&, std::optional<uint64_t> frameID, std::optional<uint64_t> pageID, WebCore::IncludeSecureCookies) override;
-    void setCookiesFromDOM(const WebCore::NetworkStorageSession&, const WebCore::URL& firstParty, const WebCore::URL&, std::optional<uint64_t> frameID, std::optional<uint64_t> pageID, const String&) override;
+    std::pair<String, bool> cookiesForDOM(const WebCore::NetworkStorageSession&, const WebCore::URL& firstParty, const WebCore::SameSiteInfo&, const WebCore::URL&, std::optional<uint64_t> frameID, std::optional<uint64_t> pageID, WebCore::IncludeSecureCookies) override;
+    void setCookiesFromDOM(const WebCore::NetworkStorageSession&, const WebCore::URL& firstParty, const WebCore::SameSiteInfo&, const WebCore::URL&, std::optional<uint64_t> frameID, std::optional<uint64_t> pageID, const String&) override;
     bool cookiesEnabled(const WebCore::NetworkStorageSession&) override;
-    std::pair<String, bool> cookieRequestHeaderFieldValue(const WebCore::NetworkStorageSession&, const WebCore::URL& firstParty, const WebCore::URL&, std::optional<uint64_t> frameID, std::optional<uint64_t> pageID, WebCore::IncludeSecureCookies) override;
-    std::pair<String, bool> cookieRequestHeaderFieldValue(PAL::SessionID, const WebCore::URL& firstParty, const WebCore::URL&, std::optional<uint64_t> frameID, std::optional<uint64_t> pageID, WebCore::IncludeSecureCookies) override;
-    bool getRawCookies(const WebCore::NetworkStorageSession&, const WebCore::URL& firstParty, const WebCore::URL&, std::optional<uint64_t> frameID, std::optional<uint64_t> pageID, Vector<WebCore::Cookie>&) override;
+    std::pair<String, bool> cookieRequestHeaderFieldValue(const WebCore::NetworkStorageSession&, const WebCore::URL& firstParty, const WebCore::SameSiteInfo&, const WebCore::URL&, std::optional<uint64_t> frameID, std::optional<uint64_t> pageID, WebCore::IncludeSecureCookies) override;
+    std::pair<String, bool> cookieRequestHeaderFieldValue(PAL::SessionID, const WebCore::URL& firstParty, const WebCore::SameSiteInfo&, const WebCore::URL&, std::optional<uint64_t> frameID, std::optional<uint64_t> pageID, WebCore::IncludeSecureCookies) override;
+    bool getRawCookies(const WebCore::NetworkStorageSession&, const WebCore::URL& firstParty, const WebCore::SameSiteInfo&, const WebCore::URL&, std::optional<uint64_t> frameID, std::optional<uint64_t> pageID, Vector<WebCore::Cookie>&) override;
     void deleteCookie(const WebCore::NetworkStorageSession&, const WebCore::URL&, const String&) override;
 
     // WebCore::PasteboardStrategy
index 20282c7..405fc29 100644 (file)
@@ -1,3 +1,20 @@
+2018-04-23  Daniel Bates  <dabates@apple.com>
+
+        Implement Same-Site cookies
+        https://bugs.webkit.org/show_bug.cgi?id=159464
+        <rdar://problem/27196358>
+
+        Reviewed by Brent Fulgham.
+
+        Pass the Same-Site info through the strategy.
+
+        * WebCoreSupport/WebPlatformStrategies.h:
+        * WebCoreSupport/WebPlatformStrategies.mm:
+        (WebPlatformStrategies::cookiesForDOM):
+        (WebPlatformStrategies::setCookiesFromDOM):
+        (WebPlatformStrategies::cookieRequestHeaderFieldValue):
+        (WebPlatformStrategies::getRawCookies):
+
 2018-04-20  Timothy Hatcher  <timothy@apple.com>
 
         NULL dereference crash sometimes under [super initWithCoder:] in WebView
index 842ccdc..720fe65 100644 (file)
@@ -48,12 +48,12 @@ private:
     WebCore::BlobRegistry* createBlobRegistry() override;
 
     // WebCore::CookiesStrategy
-    std::pair<String, bool> cookiesForDOM(const WebCore::NetworkStorageSession&, const WebCore::URL& firstParty, const WebCore::URL&, std::optional<uint64_t> frameID, std::optional<uint64_t> pageID, WebCore::IncludeSecureCookies) override;
-    void setCookiesFromDOM(const WebCore::NetworkStorageSession&, const WebCore::URL& firstParty, const WebCore::URL&, std::optional<uint64_t> frameID, std::optional<uint64_t> pageID, const String&) override;
+    std::pair<String, bool> cookiesForDOM(const WebCore::NetworkStorageSession&, const WebCore::URL& firstParty, const WebCore::SameSiteInfo&, const WebCore::URL&, std::optional<uint64_t> frameID, std::optional<uint64_t> pageID, WebCore::IncludeSecureCookies) override;
+    void setCookiesFromDOM(const WebCore::NetworkStorageSession&, const WebCore::URL& firstParty, const WebCore::SameSiteInfo&, const WebCore::URL&, std::optional<uint64_t> frameID, std::optional<uint64_t> pageID, const String&) override;
     bool cookiesEnabled(const WebCore::NetworkStorageSession&) override;
-    std::pair<String, bool> cookieRequestHeaderFieldValue(const WebCore::NetworkStorageSession&, const WebCore::URL& firstParty, const WebCore::URL&, std::optional<uint64_t> frameID, std::optional<uint64_t> pageID, WebCore::IncludeSecureCookies) override;
-    std::pair<String, bool> cookieRequestHeaderFieldValue(PAL::SessionID, const WebCore::URL& firstParty, const WebCore::URL&, std::optional<uint64_t> frameID, std::optional<uint64_t> pageID, WebCore::IncludeSecureCookies) override;
-    bool getRawCookies(const WebCore::NetworkStorageSession&, const WebCore::URL& firstParty, const WebCore::URL&, std::optional<uint64_t> frameID, std::optional<uint64_t> pageID, Vector<WebCore::Cookie>&) override;
+    std::pair<String, bool> cookieRequestHeaderFieldValue(const WebCore::NetworkStorageSession&, const WebCore::URL& firstParty, const WebCore::SameSiteInfo&, const WebCore::URL&, std::optional<uint64_t> frameID, std::optional<uint64_t> pageID, WebCore::IncludeSecureCookies) override;
+    std::pair<String, bool> cookieRequestHeaderFieldValue(PAL::SessionID, const WebCore::URL& firstParty, const WebCore::SameSiteInfo&, const WebCore::URL&, std::optional<uint64_t> frameID, std::optional<uint64_t> pageID, WebCore::IncludeSecureCookies) override;
+    bool getRawCookies(const WebCore::NetworkStorageSession&, const WebCore::URL& firstParty, const WebCore::SameSiteInfo&, const WebCore::URL&, std::optional<uint64_t> frameID, std::optional<uint64_t> pageID, Vector<WebCore::Cookie>&) override;
     void deleteCookie(const WebCore::NetworkStorageSession&, const WebCore::URL&, const String&) override;
 
     // WebCore::PasteboardStrategy
index 227e488..ebfcb6f 100644 (file)
@@ -73,14 +73,14 @@ BlobRegistry* WebPlatformStrategies::createBlobRegistry()
     return new WebCore::BlobRegistryImpl;
 }
 
-std::pair<String, bool> WebPlatformStrategies::cookiesForDOM(const NetworkStorageSession& session, const URL& firstParty, const URL& url, std::optional<uint64_t> frameID, std::optional<uint64_t> pageID, IncludeSecureCookies includeSecureCookies)
+std::pair<String, bool> WebPlatformStrategies::cookiesForDOM(const NetworkStorageSession& session, const URL& firstParty, const SameSiteInfo& sameSiteInfo, const URL& url, std::optional<uint64_t> frameID, std::optional<uint64_t> pageID, IncludeSecureCookies includeSecureCookies)
 {
-    return WebCore::cookiesForDOM(session, firstParty, url, frameID, pageID, includeSecureCookies);
+    return WebCore::cookiesForDOM(session, firstParty, sameSiteInfo, url, frameID, pageID, includeSecureCookies);
 }
 
-void WebPlatformStrategies::setCookiesFromDOM(const NetworkStorageSession& session, const URL& firstParty, const URL& url, std::optional<uint64_t> frameID, std::optional<uint64_t> pageID, const String& cookieString)
+void WebPlatformStrategies::setCookiesFromDOM(const NetworkStorageSession& session, const URL& firstParty, const SameSiteInfo& sameSiteInfo, const URL& url, std::optional<uint64_t> frameID, std::optional<uint64_t> pageID, const String& cookieString)
 {
-    WebCore::setCookiesFromDOM(session, firstParty, url, frameID, pageID, cookieString);
+    WebCore::setCookiesFromDOM(session, firstParty, sameSiteInfo, url, frameID, pageID, cookieString);
 }
 
 bool WebPlatformStrategies::cookiesEnabled(const NetworkStorageSession& session)
@@ -88,20 +88,20 @@ bool WebPlatformStrategies::cookiesEnabled(const NetworkStorageSession& session)
     return WebCore::cookiesEnabled(session);
 }
 
-std::pair<String, bool> WebPlatformStrategies::cookieRequestHeaderFieldValue(const NetworkStorageSession& session, const URL& firstParty, const URL& url, std::optional<uint64_t> frameID, std::optional<uint64_t> pageID, IncludeSecureCookies includeSecureCookies)
+std::pair<String, bool> WebPlatformStrategies::cookieRequestHeaderFieldValue(const NetworkStorageSession& session, const URL& firstParty, const SameSiteInfo& sameSiteInfo, const URL& url, std::optional<uint64_t> frameID, std::optional<uint64_t> pageID, IncludeSecureCookies includeSecureCookies)
 {
-    return WebCore::cookieRequestHeaderFieldValue(session, firstParty, url, frameID, pageID, includeSecureCookies);
+    return WebCore::cookieRequestHeaderFieldValue(session, firstParty, sameSiteInfo, url, frameID, pageID, includeSecureCookies);
 }
 
-std::pair<String, bool> WebPlatformStrategies::cookieRequestHeaderFieldValue(PAL::SessionID sessionID, const URL& firstParty, const URL& url, std::optional<uint64_t> frameID, std::optional<uint64_t> pageID, IncludeSecureCookies includeSecureCookies)
+std::pair<String, bool> WebPlatformStrategies::cookieRequestHeaderFieldValue(PAL::SessionID sessionID, const URL& firstParty, const SameSiteInfo& sameSiteInfo, const URL& url, std::optional<uint64_t> frameID, std::optional<uint64_t> pageID, IncludeSecureCookies includeSecureCookies)
 {
     auto& session = sessionID.isEphemeral() ? WebFrameNetworkingContext::ensurePrivateBrowsingSession() : NetworkStorageSession::defaultStorageSession();
-    return WebCore::cookieRequestHeaderFieldValue(session, firstParty, url, frameID, pageID, includeSecureCookies);
+    return WebCore::cookieRequestHeaderFieldValue(session, firstParty, sameSiteInfo, url, frameID, pageID, includeSecureCookies);
 }
 
-bool WebPlatformStrategies::getRawCookies(const NetworkStorageSession& session, const URL& firstParty, const URL& url, std::optional<uint64_t> frameID, std::optional<uint64_t> pageID, Vector<Cookie>& rawCookies)
+bool WebPlatformStrategies::getRawCookies(const NetworkStorageSession& session, const URL& firstParty, const SameSiteInfo& sameSiteInfo, const URL& url, std::optional<uint64_t> frameID, std::optional<uint64_t> pageID, Vector<Cookie>& rawCookies)
 {
-    return WebCore::getRawCookies(session, firstParty, url, frameID, pageID, rawCookies);
+    return WebCore::getRawCookies(session, firstParty, sameSiteInfo, url, frameID, pageID, rawCookies);
 }
 
 void WebPlatformStrategies::deleteCookie(const NetworkStorageSession& session, const URL& url, const String& cookieName)
index 628c369..70132a1 100644 (file)
@@ -1,3 +1,20 @@
+2018-04-23  Daniel Bates  <dabates@apple.com>
+
+        Implement Same-Site cookies
+        https://bugs.webkit.org/show_bug.cgi?id=159464
+        <rdar://problem/27196358>
+
+        Reviewed by Brent Fulgham.
+
+        Pass the Same-Site info through the strategy.
+
+        * WebCoreSupport/WebPlatformStrategies.cpp:
+        (WebPlatformStrategies::cookiesForDOM):
+        (WebPlatformStrategies::setCookiesFromDOM):
+        (WebPlatformStrategies::cookieRequestHeaderFieldValue):
+        (WebPlatformStrategies::getRawCookies):
+        * WebCoreSupport/WebPlatformStrategies.h:
+
 2018-04-20  Youenn Fablet  <youenn@apple.com>
 
         Make PluginData cache its web visible plugins
index 4f7034f..de26268 100644 (file)
@@ -70,14 +70,14 @@ BlobRegistry* WebPlatformStrategies::createBlobRegistry()
     return new BlobRegistryImpl;
 }
 
-std::pair<String, bool> WebPlatformStrategies::cookiesForDOM(const WebCore::NetworkStorageSession& session, const WebCore::URL& firstParty, const WebCore::URL& url, std::optional<uint64_t> frameID, std::optional<uint64_t> pageID, WebCore::IncludeSecureCookies includeSecureCookies)
+std::pair<String, bool> WebPlatformStrategies::cookiesForDOM(const NetworkStorageSession& session, const URL& firstParty, const SameSiteInfo& sameSiteInfo, const URL& url, std::optional<uint64_t> frameID, std::optional<uint64_t> pageID, IncludeSecureCookies includeSecureCookies)
 {
-    return WebCore::cookiesForDOM(session, firstParty, url, frameID, pageID, includeSecureCookies);
+    return WebCore::cookiesForDOM(session, firstParty, sameSiteInfo, url, frameID, pageID, includeSecureCookies);
 }
 
-void WebPlatformStrategies::setCookiesFromDOM(const NetworkStorageSession& session, const URL& firstParty, const URL& url, std::optional<uint64_t> frameID, std::optional<uint64_t> pageID, const String& cookieString)
+void WebPlatformStrategies::setCookiesFromDOM(const NetworkStorageSession& session, const URL& firstParty, const SameSiteInfo& sameSiteInfo, const URL& url, std::optional<uint64_t> frameID, std::optional<uint64_t> pageID, const String& cookieString)
 {
-    WebCore::setCookiesFromDOM(session, firstParty, url, frameID, pageID, cookieString);
+    WebCore::setCookiesFromDOM(session, firstParty, sameSiteInfo, url, frameID, pageID, cookieString);
 }
 
 bool WebPlatformStrategies::cookiesEnabled(const NetworkStorageSession& session)
@@ -85,20 +85,20 @@ bool WebPlatformStrategies::cookiesEnabled(const NetworkStorageSession& session)
     return WebCore::cookiesEnabled(session);
 }
 
-std::pair<String, bool> WebPlatformStrategies::cookieRequestHeaderFieldValue(const NetworkStorageSession& session, const URL& firstParty, const URL& url, std::optional<uint64_t> frameID, std::optional<uint64_t> pageID, WebCore::IncludeSecureCookies includeSecureCookies)
+std::pair<String, bool> WebPlatformStrategies::cookieRequestHeaderFieldValue(const NetworkStorageSession& session, const URL& firstParty, const SameSiteInfo& sameSiteInfo, const URL& url, std::optional<uint64_t> frameID, std::optional<uint64_t> pageID, IncludeSecureCookies includeSecureCookies)
 {
-    return WebCore::cookieRequestHeaderFieldValue(session, firstParty, url, frameID, pageID, includeSecureCookies);
+    return WebCore::cookieRequestHeaderFieldValue(session, firstParty, sameSiteInfo, url, frameID, pageID, includeSecureCookies);
 }
 
-std::pair<String, bool> WebPlatformStrategies::cookieRequestHeaderFieldValue(PAL::SessionID sessionID, const URL& firstParty, const URL& url, std::optional<uint64_t> frameID, std::optional<uint64_t> pageID, WebCore::IncludeSecureCookies includeSecureCookies)
+std::pair<String, bool> WebPlatformStrategies::cookieRequestHeaderFieldValue(PAL::SessionID sessionID, const URL& firstParty, const SameSiteInfo& sameSiteInfo, const URL& url, std::optional<uint64_t> frameID, std::optional<uint64_t> pageID, IncludeSecureCookies includeSecureCookies)
 {
     auto& session = sessionID.isEphemeral() ? WebFrameNetworkingContext::ensurePrivateBrowsingSession() : NetworkStorageSession::defaultStorageSession();
-    return WebCore::cookieRequestHeaderFieldValue(session, firstParty, url, frameID, pageID, includeSecureCookies);
+    return WebCore::cookieRequestHeaderFieldValue(session, firstParty, sameSiteInfo, url, frameID, pageID, includeSecureCookies);
 }
 
-bool WebPlatformStrategies::getRawCookies(const NetworkStorageSession& session, const URL& firstParty, const URL& url, std::optional<uint64_t> frameID, std::optional<uint64_t> pageID, Vector<Cookie>& rawCookies)
+bool WebPlatformStrategies::getRawCookies(const NetworkStorageSession& session, const URL& firstParty, const SameSiteInfo& sameSiteInfo, const URL& url, std::optional<uint64_t> frameID, std::optional<uint64_t> pageID, Vector<Cookie>& rawCookies)
 {
-    return WebCore::getRawCookies(session, firstParty, url, frameID, pageID, rawCookies);
+    return WebCore::getRawCookies(session, firstParty, sameSiteInfo, url, frameID, pageID, rawCookies);
 }
 
 void WebPlatformStrategies::deleteCookie(const NetworkStorageSession& session, const URL& url, const String& cookieName)
index af0e917..0c4fb04 100644 (file)
@@ -45,11 +45,11 @@ private:
     virtual WebCore::BlobRegistry* createBlobRegistry();
 
     // WebCore::CookiesStrategy
-    std::pair<String, bool> cookiesForDOM(const WebCore::NetworkStorageSession&, const WebCore::URL& firstParty, const WebCore::URL&, std::optional<uint64_t> frameID, std::optional<uint64_t> pageID, WebCore::IncludeSecureCookies) override;
-    virtual void setCookiesFromDOM(const WebCore::NetworkStorageSession&, const WebCore::URL& firstParty, const WebCore::URL&, std::optional<uint64_t> frameID, std::optional<uint64_t> pageID, const String&);
+    std::pair<String, bool> cookiesForDOM(const WebCore::NetworkStorageSession&, const WebCore::URL& firstParty, const WebCore::SameSiteInfo&, const WebCore::URL&, std::optional<uint64_t> frameID, std::optional<uint64_t> pageID, WebCore::IncludeSecureCookies) override;
+    virtual void setCookiesFromDOM(const WebCore::NetworkStorageSession&, const WebCore::URL& firstParty, const WebCore::SameSiteInfo&, const WebCore::URL&, std::optional<uint64_t> frameID, std::optional<uint64_t> pageID, const String&);
     virtual bool cookiesEnabled(const WebCore::NetworkStorageSession&);
-    std::pair<String, bool> cookieRequestHeaderFieldValue(const WebCore::NetworkStorageSession&, const WebCore::URL& firstParty, const WebCore::URL&, std::optional<uint64_t> frameID, std::optional<uint64_t> pageID, WebCore::IncludeSecureCookies) override;
-    std::pair<String, bool> cookieRequestHeaderFieldValue(PAL::SessionID, const WebCore::URL& firstParty, const WebCore::URL&, std::optional<uint64_t> frameID, std::optional<uint64_t> pageID, WebCore::IncludeSecureCookies) override;
-    virtual bool getRawCookies(const WebCore::NetworkStorageSession&, const WebCore::URL& firstParty, const WebCore::URL&, std::optional<uint64_t> frameID, std::optional<uint64_t> pageID, Vector<WebCore::Cookie>&);
+    std::pair<String, bool> cookieRequestHeaderFieldValue(const WebCore::NetworkStorageSession&, const WebCore::URL& firstParty, const WebCore::SameSiteInfo&, const WebCore::URL&, std::optional<uint64_t> frameID, std::optional<uint64_t> pageID, WebCore::IncludeSecureCookies) override;
+    std::pair<String, bool> cookieRequestHeaderFieldValue(PAL::SessionID, const WebCore::URL& firstParty, const WebCore::SameSiteInfo&, const WebCore::URL&, std::optional<uint64_t> frameID, std::optional<uint64_t> pageID, WebCore::IncludeSecureCookies) override;
+    virtual bool getRawCookies(const WebCore::NetworkStorageSession&, const WebCore::URL& firstParty, const WebCore::SameSiteInfo&, const WebCore::URL&, std::optional<uint64_t> frameID, std::optional<uint64_t> pageID, Vector<WebCore::Cookie>&);
     virtual void deleteCookie(const WebCore::NetworkStorageSession&, const WebCore::URL&, const String&);
 };