Source/WebCore/ChangeLog

 12020-04-30 John Wilander <wilander@apple.com>
 2
 3 Exempt app-bound domains from ITP's website data deletion and third-party cookie blocking between themselves
 4 https://bugs.webkit.org/show_bug.cgi?id=210674
 5 <rdar://problem/61950767>
 6
 7 Reviewed by NOBODY (OOPS!).
 8
 9 This change adds functionality to NetworkStorageSession to allow it to exempt
 10 app-bound domains from third-party cookie blocking.
 11
 12 Calls to NetworkStorageSession::setAppBoundDomains() and
 13 NetworkStorageSession::addAppBoundDomainForTesting() automatically sets the
 14 cookie blocking policy to
 15 ThirdPartyCookieBlockingMode::AllExceptBetweenAppBoundDomains.
 16
 17 Calls to NetworkStorageSession::resetAppBoundDomains() automatically sets the
 18 cookie blocking policy to ThirdPartyCookieBlockingMode::All which is the
 19 default.
 20
 21 Tests: http/tests/resourceLoadStatistics/exemptDomains/app-bound-domains-exempt-from-cookie-blocking-between-each-other.html
 22 http/tests/resourceLoadStatistics/exemptDomains/app-bound-domains-exempt-from-website-data-deletion-database.html
 23 http/tests/resourceLoadStatistics/exemptDomains/app-bound-domains-exempt-from-website-data-deletion.html
 24
 25 * platform/network/NetworkStorageSession.cpp:
 26 (WebCore::NetworkStorageSession::shouldBlockCookies const):
 27 (WebCore::NetworkStorageSession::shouldExemptDomainPairFromThirdPartyCookieBlocking const):
 28 (WebCore::NetworkStorageSession::setAppBoundDomains):
 29 (WebCore::NetworkStorageSession::addAppBoundDomainForTesting):
 30 * platform/network/NetworkStorageSession.h:
 31 (WebCore::NetworkStorageSession::resetAppBoundDomains):
 32
1332020-04-30 Devin Rousso <drousso@apple.com>
234
335 WebKit.WebContent process crashes when web developer tools are opened in Safari

Source/WebKit/ChangeLog

 12020-04-30 John Wilander <wilander@apple.com>
 2
 3 Exempt app-bound domains from ITP's website data deletion and third-party cookie blocking between themselves
 4 https://bugs.webkit.org/show_bug.cgi?id=210674
 5 <rdar://problem/61950767>
 6
 7 Reviewed by NOBODY (OOPS!).
 8
 9 This change forwards information about app-bound domains to ITP and web
 10 processes so that they can be exempt from website data deletion and
 11 third-party cookie blocking between themselves.
 12
 13 Since app-bound domains are loaded lazily from disk and on a background
 14 thread, this patch forwards them in ResourceLoadStatisticsParameters if
 15 they've already been loaded. Then every time app-bound domains are
 16 updated, they are forwarded to ITP. This ensures that ITP will have them
 17 as soon as possible.
 18
 19 Setting app-bound domains for the purposes of ITP automatically switches
 20 ITP's cookie blocking policy to the new
 21 WebCore::ThirdPartyCookieBlockingMode::AllExceptBetweenAppBoundDomains.
 22
 23 A new function is added: WebsiteDataStore::getAppBoundDomainsIfInitialized()
 24 This function allows fetching of app-bound domains without triggering
 25 the lazy loading. This is just to allow speculative configuration of ITP
 26 right when it's created — if any app-bound domains are already configured,
 27 forward them to ITP via ResourceLoadStatisticsParameters.
 28
 29 The C API changes are for test purposes.
 30
 31 * NetworkProcess/Classifier/ResourceLoadStatisticsDatabaseStore.cpp:
 32 * NetworkProcess/Classifier/ResourceLoadStatisticsMemoryStore.cpp:
 33 (WebKit::ResourceLoadStatisticsMemoryStore::registrableDomainsToDeleteOrRestrictWebsiteDataFor):
 34 * NetworkProcess/Classifier/ResourceLoadStatisticsStore.cpp:
 35 (WebKit::ResourceLoadStatisticsStore::setAppBoundDomains):
 36 (WebKit::ResourceLoadStatisticsStore::addAppBoundDomainForTesting):
 37 (WebKit::ResourceLoadStatisticsStore::resetParametersToDefaultValues):
 38 (WebKit::ResourceLoadStatisticsStore::shouldExemptFromWebsiteDataDeletion const):
 39 * NetworkProcess/Classifier/ResourceLoadStatisticsStore.h:
 40 (WebKit::ResourceLoadStatisticsStore::appBoundDomains const):
 41 (WebKit::ResourceLoadStatisticsStore::standaloneApplicationDomain const): Deleted.
 42 * NetworkProcess/Classifier/WebResourceLoadStatisticsStore.cpp:
 43 (WebKit::WebResourceLoadStatisticsStore::setAppBoundDomains):
 44 (WebKit::WebResourceLoadStatisticsStore::addAppBoundDomainForTesting):
 45 (WebKit::WebResourceLoadStatisticsStore::resetParametersToDefaultValues):
 46 * NetworkProcess/Classifier/WebResourceLoadStatisticsStore.h:
 47 * NetworkProcess/NetworkProcess.cpp:
 48 (WebKit::NetworkProcess::setAppBoundDomainsForResourceLoadStatistics):
 49 (WebKit::NetworkProcess::addAppBoundDomainForTesting):
 50 * NetworkProcess/NetworkProcess.h:
 51 * NetworkProcess/NetworkProcess.messages.in:
 52 * NetworkProcess/NetworkSession.cpp:
 53 (WebKit::NetworkSession::NetworkSession):
 54 * Shared/ResourceLoadStatisticsParameters.h:
 55 (WebKit::ResourceLoadStatisticsParameters::encode const):
 56 (WebKit::ResourceLoadStatisticsParameters::decode):
 57 * UIProcess/API/C/WKWebsiteDataStoreRef.cpp:
 58 (WKWebsiteDataStoreStatisticsAddAppBoundDomainForTesting):
 59 * UIProcess/API/C/WKWebsiteDataStoreRef.h:
 60 * UIProcess/Network/NetworkProcessProxy.cpp:
 61 (WebKit::NetworkProcessProxy::setAppBoundDomainsForResourceLoadStatistics):
 62 (WebKit::NetworkProcessProxy::addAppBoundDomainForTesting):
 63 * UIProcess/Network/NetworkProcessProxy.h:
 64 * UIProcess/WebProcessPool.cpp:
 65 (WebKit::WebProcessPool::ensureNetworkProcess):
 66 * UIProcess/WebsiteData/Cocoa/WebsiteDataStoreCocoa.mm:
 67 (WebKit::WebsiteDataStore::initializeAppBoundDomains):
 68 (WebKit::WebsiteDataStore::ensureAppBoundDomains const):
 69 (WebKit::WebsiteDataStore::getAppBoundDomainsIfInitialized const):
 70 * UIProcess/WebsiteData/WebsiteDataStore.cpp:
 71 (WebKit::WebsiteDataStore::addResourceLoadStatisticsAppBoundDomainForTesting):
 72 (WebKit::WebsiteDataStore::parameters):
 73 (WebKit::WebsiteDataStore::setAppBoundDomainsForResourceLoadStatistics):
 74 * UIProcess/WebsiteData/WebsiteDataStore.h:
 75
1762020-04-30 Alex Christensen <achristensen@webkit.org>
277
378 Add SPI to change a WKWebView's CORS disabling pattern after initialization

Source/WebCore/platform/network/NetworkStorageSession.cpp

@@bool NetworkStorageSession::shouldBlockCookies(const URL& firstPartyForCookies,
134134 switch (m_thirdPartyCookieBlockingMode) {
135135 case ThirdPartyCookieBlockingMode::All:
136136 return true;
 137 case ThirdPartyCookieBlockingMode::AllExceptBetweenAppBoundDomains:
 138 return !shouldExemptDomainPairFromThirdPartyCookieBlocking(firstPartyDomain, resourceDomain);
137139 case ThirdPartyCookieBlockingMode::AllOnSitesWithoutUserInteraction:
138140 if (!hasHadUserInteractionAsFirstParty(firstPartyDomain))
139141 return true;

@@bool NetworkStorageSession::shouldBlockCookies(const URL& firstPartyForCookies,
145147 return false;
146148}
147149
 150bool NetworkStorageSession::shouldExemptDomainPairFromThirdPartyCookieBlocking(const RegistrableDomain& topFrameDomain, const RegistrableDomain& resourceDomain) const
 151{
 152 ASSERT(topFrameDomain != resourceDomain);
 153 if (topFrameDomain.isEmpty() || resourceDomain.isEmpty())
 154 return false;
 155
 156 return topFrameDomain == resourceDomain || (m_appBoundDomains.contains(topFrameDomain) && m_appBoundDomains.contains(resourceDomain));
 157}
 158
148159Optional<Seconds> NetworkStorageSession::maxAgeCacheCap(const ResourceRequest& request)
149160{
150161 if (m_cacheMaxAgeCapForPrevalentResources && shouldBlockCookies(request, WTF::nullopt, WTF::nullopt))

@@void NetworkStorageSession::setThirdPartyCookieBlockingMode(ThirdPartyCookieBloc
292303 m_thirdPartyCookieBlockingMode = blockingMode;
293304}
294305
 306void NetworkStorageSession::setAppBoundDomains(HashSet<RegistrableDomain>&& domains)
 307{
 308 m_appBoundDomains = WTFMove(domains);
 309 setThirdPartyCookieBlockingMode(ThirdPartyCookieBlockingMode::AllExceptBetweenAppBoundDomains);
 310}
 311
 312void NetworkStorageSession::resetAppBoundDomains()
 313{
 314 m_appBoundDomains.clear();
 315 setThirdPartyCookieBlockingMode(ThirdPartyCookieBlockingMode::All);
 316}
 317
 318void NetworkStorageSession::addAppBoundDomainForTesting(const RegistrableDomain& domain)
 319{
 320 // Only support two app-bound domains for testing.
 321 if (m_appBoundDomains.size() >= 2)
 322 return;
 323 m_appBoundDomains.add(domain);
 324 setThirdPartyCookieBlockingMode(ThirdPartyCookieBlockingMode::AllExceptBetweenAppBoundDomains);
 325}
 326
295327Optional<Seconds> NetworkStorageSession::clientSideCookieCap(const RegistrableDomain& firstParty, Optional<PageIdentifier> pageID) const
296328{
297329 if (!m_ageCapForClientSideCookies || !pageID || m_navigatedToWithLinkDecorationByPrevalentResource.isEmpty())

Source/WebCore/platform/network/NetworkStorageSession.h

@@struct SameSiteInfo;
7777enum class HTTPCookieAcceptPolicy : uint8_t;
7878enum class IncludeSecureCookies : bool;
7979enum class IncludeHttpOnlyCookies : bool;
80 enum class ThirdPartyCookieBlockingMode : uint8_t { All, AllOnSitesWithoutUserInteraction, OnlyAccordingToPerDomainPolicy };
 80enum class ThirdPartyCookieBlockingMode : uint8_t { All, AllExceptBetweenAppBoundDomains, AllOnSitesWithoutUserInteraction, OnlyAccordingToPerDomainPolicy };
8181enum class SameSiteStrictEnforcementEnabled : bool { Yes, No };
8282enum class FirstPartyWebsiteDataRemovalMode : uint8_t { AllButCookies, None, AllButCookiesLiveOnTestingTimeout, AllButCookiesReproTestingTimeout };
8383enum class ShouldAskITP : bool { No, Yes };

@@public:
9595class NetworkStorageSession {
9696 WTF_MAKE_NONCOPYABLE(NetworkStorageSession); WTF_MAKE_FAST_ALLOCATED;
9797public:
 98 using TopFrameDomain = WebCore::RegistrableDomain;
 99 using SubResourceDomain = WebCore::RegistrableDomain;
 100
98101 WEBCORE_EXPORT static void permitProcessToUseCookieAPI(bool);
99102 WEBCORE_EXPORT static bool processMayUseCookieAPI();
100103

@@public:
196199 WEBCORE_EXPORT void didCommitCrossSiteLoadWithDataTransferFromPrevalentResource(const RegistrableDomain& toDomain, PageIdentifier);
197200 WEBCORE_EXPORT void resetCrossSiteLoadsWithLinkDecorationForTesting();
198201 WEBCORE_EXPORT void setThirdPartyCookieBlockingMode(ThirdPartyCookieBlockingMode);
 202 WEBCORE_EXPORT void setAppBoundDomains(HashSet<RegistrableDomain>&&);
 203 WEBCORE_EXPORT void resetAppBoundDomains();
 204 WEBCORE_EXPORT void addAppBoundDomainForTesting(const RegistrableDomain&);
199205#endif
200206
201207private:

@@private:
241247
242248#if ENABLE(RESOURCE_LOAD_STATISTICS)
243249 bool m_isResourceLoadStatisticsEnabled = false;
244  Optional<Seconds> clientSideCookieCap(const RegistrableDomain& firstParty, Optional<PageIdentifier>) const;
 250 Optional<Seconds> clientSideCookieCap(const TopFrameDomain&, Optional<PageIdentifier>) const;
 251 bool shouldExemptDomainPairFromThirdPartyCookieBlocking(const TopFrameDomain&, const SubResourceDomain&) const;
245252 HashSet<RegistrableDomain> m_registrableDomainsToBlockAndDeleteCookiesFor;
246253 HashSet<RegistrableDomain> m_registrableDomainsToBlockButKeepCookiesFor;
247254 HashSet<RegistrableDomain> m_registrableDomainsWithUserInteractionAsFirstParty;

@@private:
253260 HashMap<WebCore::PageIdentifier, RegistrableDomain> m_navigatedToWithLinkDecorationByPrevalentResource;
254261 bool m_navigationWithLinkDecorationTestMode = false;
255262 ThirdPartyCookieBlockingMode m_thirdPartyCookieBlockingMode { ThirdPartyCookieBlockingMode::All };
 263 HashSet<RegistrableDomain> m_appBoundDomains;
256264#endif
257265
258266#if PLATFORM(COCOA)

@@template<> struct EnumTraits<WebCore::ThirdPartyCookieBlockingMode> {
277285 using values = EnumValues<
278286 WebCore::ThirdPartyCookieBlockingMode,
279287 WebCore::ThirdPartyCookieBlockingMode::All,
 288 WebCore::ThirdPartyCookieBlockingMode::AllExceptBetweenAppBoundDomains,
280289 WebCore::ThirdPartyCookieBlockingMode::AllOnSitesWithoutUserInteraction,
281290 WebCore::ThirdPartyCookieBlockingMode::OnlyAccordingToPerDomainPolicy
282291 >;

Source/WebKit/NetworkProcess/Classifier/ResourceLoadStatisticsDatabaseStore.cpp

@@RegistrableDomainsToDeleteOrRestrictWebsiteDataFor ResourceLoadStatisticsDatabas
26142614 Vector<DomainData> domains = this->domains();
26152615 Vector<unsigned> domainIDsToClearGrandfathering;
26162616 for (auto& statistic : domains) {
2617  if (statistic.registrableDomain == standaloneApplicationDomain())
 2617 if (shouldExemptFromWebsiteDataDeletion(statistic.registrableDomain))
26182618 continue;
26192619 oldestUserInteraction = std::min(oldestUserInteraction, statistic.mostRecentUserInteractionTime);
26202620 if (shouldRemoveAllWebsiteDataFor(statistic, shouldCheckForGrandfathering)) {

Source/WebKit/NetworkProcess/Classifier/ResourceLoadStatisticsMemoryStore.cpp

@@RegistrableDomainsToDeleteOrRestrictWebsiteDataFor ResourceLoadStatisticsMemoryS
980980 auto oldestUserInteraction = now;
981981 RegistrableDomainsToDeleteOrRestrictWebsiteDataFor toDeleteOrRestrictFor;
982982 for (auto& statistic : m_resourceStatisticsMap.values()) {
983  if (statistic.registrableDomain == standaloneApplicationDomain())
 983 if (shouldExemptFromWebsiteDataDeletion(statistic.registrableDomain))
984984 continue;
985985 oldestUserInteraction = std::min(oldestUserInteraction, statistic.mostRecentUserInteractionTime);
986986 if (shouldRemoveAllWebsiteDataFor(statistic, shouldCheckForGrandfathering)) {

Source/WebKit/NetworkProcess/Classifier/ResourceLoadStatisticsStore.cpp

@@void ResourceLoadStatisticsStore::setPrevalentResourceForDebugMode(const Registr
314314 m_debugManualPrevalentResource = domain;
315315}
316316
 317void ResourceLoadStatisticsStore::setAppBoundDomains(HashSet<RegistrableDomain>&& domains)
 318{
 319 m_appBoundDomains = WTFMove(domains);
 320 setThirdPartyCookieBlockingMode(ThirdPartyCookieBlockingMode::AllExceptBetweenAppBoundDomains);
 321}
 322
 323void ResourceLoadStatisticsStore::addAppBoundDomainForTesting(RegistrableDomain&& domain)
 324{
 325 // Only support two app-bound domains for testing.
 326 if (m_appBoundDomains.size() >= 2)
 327 return;
 328 m_appBoundDomains.add(WTFMove(domain));
 329 setThirdPartyCookieBlockingMode(ThirdPartyCookieBlockingMode::AllExceptBetweenAppBoundDomains);
 330}
 331
317332void ResourceLoadStatisticsStore::scheduleStatisticsProcessingRequestIfNecessary()
318333{
319334 ASSERT(!RunLoop::isMain());

@@void ResourceLoadStatisticsStore::resetParametersToDefaultValues()
565580 ASSERT(!RunLoop::isMain());
566581
567582 m_parameters = { };
 583 m_standaloneApplicationDomain = { };
 584 m_appBoundDomains.clear();
568585}
569586
570587void ResourceLoadStatisticsStore::logTestingEvent(const String& event)

@@void ResourceLoadStatisticsStore::debugLogDomainsInBatches(const char* action, c
649666 RELEASE_LOG_INFO(ITPDebug, "%" PUBLIC_LOG_STRING " to (%{public}d of %u): %" PUBLIC_LOG_STRING ".", action, batchNumber, numberOfBatches, domainsToString(batch).utf8().data());
650667}
651668
 669bool ResourceLoadStatisticsStore::shouldExemptFromWebsiteDataDeletion(const RegistrableDomain& domain) const
 670{
 671 return !domain.isEmpty() && (domain == m_standaloneApplicationDomain || m_appBoundDomains.contains(domain));
 672}
 673
652674} // namespace WebKit
653675
654676#endif

Source/WebKit/NetworkProcess/Classifier/ResourceLoadStatisticsStore.h

@@public:
169169 bool isSameSiteStrictEnforcementEnabled() const { return m_sameSiteStrictEnforcementEnabled == WebCore::SameSiteStrictEnforcementEnabled::Yes; };
170170 void setFirstPartyWebsiteDataRemovalMode(WebCore::FirstPartyWebsiteDataRemovalMode mode) { m_firstPartyWebsiteDataRemovalMode = mode; }
171171 void setStandaloneApplicationDomain(RegistrableDomain&& domain) { m_standaloneApplicationDomain = WTFMove(domain); }
 172 void setAppBoundDomains(HashSet<RegistrableDomain>&&);
 173 void addAppBoundDomainForTesting(RegistrableDomain&&);
 174 HashSet<RegistrableDomain> appBoundDomains() const { return m_appBoundDomains; }
172175
173176 virtual bool areAllThirdPartyCookiesBlockedUnder(const TopFrameDomain&) = 0;
174177 virtual void hasStorageAccess(const SubFrameDomain&, const TopFrameDomain&, Optional<WebCore::FrameIdentifier>, WebCore::PageIdentifier, CompletionHandler<void(bool)>&&) = 0;

@@protected:
249252 bool debugLoggingEnabled() const { return m_debugLoggingEnabled; };
250253 bool debugModeEnabled() const { return m_debugModeEnabled; }
251254 WebCore::FirstPartyWebsiteDataRemovalMode firstPartyWebsiteDataRemovalMode() const { return m_firstPartyWebsiteDataRemovalMode; }
252  RegistrableDomain standaloneApplicationDomain() const { return m_standaloneApplicationDomain; }
 255 bool shouldExemptFromWebsiteDataDeletion(const RegistrableDomain&) const;
253256
254257 static constexpr unsigned maxNumberOfRecursiveCallsInRedirectTraceBack { 50 };
255258

@@private:
291294 ShouldIncludeLocalhost m_shouldIncludeLocalhost { ShouldIncludeLocalhost::Yes };
292295 WebCore::FirstPartyWebsiteDataRemovalMode m_firstPartyWebsiteDataRemovalMode { WebCore::FirstPartyWebsiteDataRemovalMode::AllButCookies };
293296 RegistrableDomain m_standaloneApplicationDomain;
 297 HashSet<RegistrableDomain> m_appBoundDomains;
294298};
295299
296300} // namespace WebKit

Source/WebKit/NetworkProcess/Classifier/WebResourceLoadStatisticsStore.cpp

@@void WebResourceLoadStatisticsStore::setStandaloneApplicationDomain(const Regist
678678 });
679679}
680680
 681void WebResourceLoadStatisticsStore::setAppBoundDomains(HashSet<RegistrableDomain>&& domains)
 682{
 683 ASSERT(RunLoop::isMain());
 684
 685 if (isEphemeral() || domains.isEmpty())
 686 return;
 687
 688 postTask([this, domains = crossThreadCopy(domains)]() mutable {
 689 if (!m_statisticsStore)
 690 return;
 691 m_statisticsStore->setAppBoundDomains(WTFMove(domains));
 692 });
 693
 694 if (m_networkSession) {
 695 if (auto* storageSession = m_networkSession->networkStorageSession())
 696 storageSession->setAppBoundDomains(WTFMove(domains));
 697 }
 698}
 699
 700void WebResourceLoadStatisticsStore::addAppBoundDomainForTesting(const WebCore::RegistrableDomain& domain, CompletionHandler<void(uint64_t)>&& completionHandler)
 701{
 702 ASSERT(RunLoop::isMain());
 703
 704 if (isEphemeral() || domain.isEmpty()) {
 705 ASSERT_NOT_REACHED();
 706 completionHandler(0);
 707 return;
 708 }
 709
 710 if (m_networkSession) {
 711 if (auto* storageSession = m_networkSession->networkStorageSession())
 712 storageSession->addAppBoundDomainForTesting(domain);
 713 }
 714
 715 postTask([this, domain = crossThreadCopy(domain), completionHandler = WTFMove(completionHandler)]() mutable {
 716 if (!m_statisticsStore)
 717 return;
 718 m_statisticsStore->addAppBoundDomainForTesting(WTFMove(domain));
 719 auto totalAppBoundDomains = m_statisticsStore->appBoundDomains().size();
 720 postTaskReply([totalAppBoundDomains, completionHandler = WTFMove(completionHandler)]() mutable {
 721 completionHandler(totalAppBoundDomains);
 722 });
 723 });
 724}
 725
681726void WebResourceLoadStatisticsStore::didCreateNetworkProcess()
682727{
683728 ASSERT(RunLoop::isMain());

@@void WebResourceLoadStatisticsStore::resetParametersToDefaultValues(CompletionHa
12801325 return;
12811326 }
12821327
 1328 if (m_networkSession) {
 1329 if (auto* storageSession = m_networkSession->networkStorageSession())
 1330 storageSession->resetAppBoundDomains();
 1331 }
 1332
12831333 postTask([this, completionHandler = WTFMove(completionHandler)]() mutable {
12841334 if (m_statisticsStore)
12851335 m_statisticsStore->resetParametersToDefaultValues();

Source/WebKit/NetworkProcess/Classifier/WebResourceLoadStatisticsStore.h

@@struct ThirdPartyData {
279279 void setSameSiteStrictEnforcementEnabled(WebCore::SameSiteStrictEnforcementEnabled);
280280 void setFirstPartyWebsiteDataRemovalMode(WebCore::FirstPartyWebsiteDataRemovalMode, CompletionHandler<void()>&&);
281281 void setStandaloneApplicationDomain(const RegistrableDomain&, CompletionHandler<void()>&&);
 282 void setAppBoundDomains(HashSet<RegistrableDomain>&&);
 283 void addAppBoundDomainForTesting(const WebCore::RegistrableDomain&, CompletionHandler<void(uint64_t)>&&);
282284 void didCreateNetworkProcess();
283285
284286 void notifyResourceLoadStatisticsProcessed();

Source/WebKit/NetworkProcess/NetworkProcess.cpp

@@void NetworkProcess::hasIsolatedSession(PAL::SessionID sessionID, const WebCore:
13071307 completionHandler(result);
13081308}
13091309
 1310void NetworkProcess::setAppBoundDomainsForResourceLoadStatistics(PAL::SessionID sessionID, HashSet<WebCore::RegistrableDomain>&& appBoundDomains)
 1311{
 1312 if (auto* networkSession = this->networkSession(sessionID)) {
 1313 if (auto* resourceLoadStatistics = networkSession->resourceLoadStatistics())
 1314 resourceLoadStatistics->setAppBoundDomains(WTFMove(appBoundDomains));
 1315 }
 1316}
 1317
13101318void NetworkProcess::setShouldDowngradeReferrerForTesting(bool enabled, CompletionHandler<void()>&& completionHandler)
13111319{
13121320 forEachNetworkSession([enabled](auto& networkSession) {

@@void NetworkProcess::setToSameSiteStrictCookiesForTesting(PAL::SessionID session
13551363 completionHandler();
13561364 }
13571365}
 1366
 1367void NetworkProcess::addAppBoundDomainForTesting(PAL::SessionID sessionID, const WebCore::RegistrableDomain& domain, CompletionHandler<void(uint64_t)>&& completionHandler)
 1368{
 1369 if (auto* networkSession = this->networkSession(sessionID)) {
 1370 if (auto* resourceLoadStatistics = networkSession->resourceLoadStatistics()) {
 1371 resourceLoadStatistics->addAppBoundDomainForTesting(domain, WTFMove(completionHandler));
 1372 return;
 1373 }
 1374 }
 1375 ASSERT_NOT_REACHED();
 1376 completionHandler(0);
 1377}
13581378#endif // ENABLE(RESOURCE_LOAD_STATISTICS)
13591379
13601380void NetworkProcess::preconnectTo(PAL::SessionID sessionID, const URL& url, const String& userAgent, WebCore::StoredCredentialsPolicy storedCredentialsPolicy, Optional<NavigatingToAppBoundDomain> isNavigatingToAppBoundDomain)

Source/WebKit/NetworkProcess/NetworkProcess.h

@@public:
261261 void setCrossSiteLoadWithLinkDecorationForTesting(PAL::SessionID, const RegistrableDomain& fromDomain, const RegistrableDomain& toDomain, CompletionHandler<void()>&&);
262262 void resetCrossSiteLoadsWithLinkDecorationForTesting(PAL::SessionID, CompletionHandler<void()>&&);
263263 void hasIsolatedSession(PAL::SessionID, const WebCore::RegistrableDomain&, CompletionHandler<void(bool)>&&) const;
 264 void setAppBoundDomainsForResourceLoadStatistics(PAL::SessionID, HashSet<WebCore::RegistrableDomain>&&);
264265 bool isITPDatabaseEnabled() const { return m_isITPDatabaseEnabled; }
265266 void setShouldDowngradeReferrerForTesting(bool, CompletionHandler<void()>&&);
266267 void setShouldBlockThirdPartyCookiesForTesting(PAL::SessionID, WebCore::ThirdPartyCookieBlockingMode, CompletionHandler<void()>&&);
267268 void setShouldEnbleSameSiteStrictEnforcementForTesting(PAL::SessionID, WebCore::SameSiteStrictEnforcementEnabled, CompletionHandler<void()>&&);
268269 void setFirstPartyWebsiteDataRemovalModeForTesting(PAL::SessionID, WebCore::FirstPartyWebsiteDataRemovalMode, CompletionHandler<void()>&&);
269270 void setToSameSiteStrictCookiesForTesting(PAL::SessionID, const WebCore::RegistrableDomain&, CompletionHandler<void()>&&);
 271 void addAppBoundDomainForTesting(PAL::SessionID, const WebCore::RegistrableDomain&, CompletionHandler<void(uint64_t)>&&);
270272#endif
271273
272274 using CacheStorageRootPathCallback = CompletionHandler<void(String&&)>;

Source/WebKit/NetworkProcess/NetworkProcess.messages.in

@@messages -> NetworkProcess LegacyReceiver {
140140 ResetCrossSiteLoadsWithLinkDecorationForTesting(PAL::SessionID sessionID) -> () Async
141141 DeleteCookiesForTesting(PAL::SessionID sessionID, WebCore::RegistrableDomain domain, bool includeHttpOnlyCookies) -> () Async
142142 HasIsolatedSession(PAL::SessionID sessionID, WebCore::RegistrableDomain domain) -> (bool hasIsolatedSession) Async
 143 SetAppBoundDomainsForResourceLoadStatistics(PAL::SessionID sessionID, HashSet<WebCore::RegistrableDomain> appBoundDomains)
143144 SetShouldDowngradeReferrerForTesting(bool enabled) -> () Async
144145 SetShouldBlockThirdPartyCookiesForTesting(PAL::SessionID sessionID, enum:uint8_t WebCore::ThirdPartyCookieBlockingMode blockingMode) -> () Async
145146 SetShouldEnbleSameSiteStrictEnforcementForTesting(PAL::SessionID sessionID, enum:bool WebCore::SameSiteStrictEnforcementEnabled enabled) -> () Async
146147 SetFirstPartyWebsiteDataRemovalModeForTesting(PAL::SessionID sessionID, enum:uint8_t WebCore::FirstPartyWebsiteDataRemovalMode mode) -> () Async
147148 SetToSameSiteStrictCookiesForTesting(PAL::SessionID sessionID, WebCore::RegistrableDomain domain) -> () Async
 149 AddAppBoundDomainForTesting(PAL::SessionID sessionID, WebCore::RegistrableDomain appBoundDomain) -> (uint64_t totalAppBoundDomains) Async
148150#endif
149151
150152 SetSessionIsControlledByAutomation(PAL::SessionID sessionID, bool controlled);

Source/WebKit/NetworkProcess/NetworkSession.cpp

@@NetworkSession::NetworkSession(NetworkProcess& networkProcess, const NetworkSess
129129
130130#if ENABLE(RESOURCE_LOAD_STATISTICS)
131131 setResourceLoadStatisticsEnabled(parameters.resourceLoadStatisticsParameters.enabled);
 132 if (m_resourceLoadStatistics && !parameters.resourceLoadStatisticsParameters.appBoundDomains.isEmpty()) {
 133 HashSet<RegistrableDomain> set;
 134 set.reserveInitialCapacity(parameters.resourceLoadStatisticsParameters.appBoundDomains.size());
 135 set.add(parameters.resourceLoadStatisticsParameters.appBoundDomains.begin(), parameters.resourceLoadStatisticsParameters.appBoundDomains.end());
 136 m_resourceLoadStatistics->setAppBoundDomains(WTFMove(set));
 137 }
132138#endif
133139}
134140

Source/WebKit/Shared/ResourceLoadStatisticsParameters.h

@@struct ResourceLoadStatisticsParameters {
4848#endif
4949 WebCore::FirstPartyWebsiteDataRemovalMode firstPartyWebsiteDataRemovalMode { WebCore::FirstPartyWebsiteDataRemovalMode::AllButCookies };
5050 WebCore::RegistrableDomain standaloneApplicationDomain { };
 51 Vector<WebCore::RegistrableDomain> appBoundDomains { };
5152 WebCore::RegistrableDomain manualPrevalentResource { };
5253
5354 void encode(IPC::Encoder& encoder) const

@@struct ResourceLoadStatisticsParameters {
6566#endif
6667 encoder << firstPartyWebsiteDataRemovalMode;
6768 encoder << standaloneApplicationDomain;
 69 encoder << appBoundDomains;
6870 encoder << manualPrevalentResource;
6971 }
7072

@@struct ResourceLoadStatisticsParameters {
127129 if (!standaloneApplicationDomain)
128130 return WTF::nullopt;
129131
 132 Optional<Vector<WebCore::RegistrableDomain>> appBoundDomains;
 133 decoder >> appBoundDomains;
 134 if (!appBoundDomains)
 135 return WTF::nullopt;
 136
130137 Optional<WebCore::RegistrableDomain> manualPrevalentResource;
131138 decoder >> manualPrevalentResource;
132139 if (!manualPrevalentResource)

@@struct ResourceLoadStatisticsParameters {
146153#endif
147154 WTFMove(*firstPartyWebsiteDataRemovalMode),
148155 WTFMove(*standaloneApplicationDomain),
 156 WTFMove(*appBoundDomains),
149157 WTFMove(*manualPrevalentResource),
150158 }};
151159 }

Source/WebKit/UIProcess/API/C/WKWebsiteDataStoreRef.cpp

@@void WKWebsiteDataStoreSetResourceLoadStatisticsToSameSiteStrictCookiesForTestin
617617#endif
618618}
619619
 620void WKWebsiteDataStoreStatisticsAddAppBoundDomainForTesting(WKWebsiteDataStoreRef dataStoreRef, WKStringRef hostName, void* context, WKWebsiteDataStoreStatisticsAddAppBoundDomainForTestingFunction completionHandler)
 621{
 622#if ENABLE(RESOURCE_LOAD_STATISTICS)
 623 WebKit::toImpl(dataStoreRef)->addResourceLoadStatisticsAppBoundDomainForTesting(URL(URL(), WebKit::toImpl(hostName)->string()), [context, completionHandler] (uint64_t totalAppBoundDomains) {
 624 completionHandler(totalAppBoundDomains, context);
 625 });
 626#else
 627 UNUSED_PARAM(dataStoreRef);
 628 UNUSED_PARAM(hostName);
 629 completionHandler(0, context);
 630#endif
 631}
 632
620633void WKWebsiteDataStoreStatisticsResetToConsistentState(WKWebsiteDataStoreRef dataStoreRef, void* context, WKWebsiteDataStoreStatisticsResetToConsistentStateFunction completionHandler)
621634{
622635#if ENABLE(RESOURCE_LOAD_STATISTICS)

Source/WebKit/UIProcess/API/C/WKWebsiteDataStoreRef.h

@@typedef void (*WKWebsiteDataStoreSetResourceLoadStatisticsFirstPartyWebsiteDataR
129129WK_EXPORT void WKWebsiteDataStoreSetResourceLoadStatisticsFirstPartyWebsiteDataRemovalModeForTesting(WKWebsiteDataStoreRef dataStoreRef, bool enabled, void* context, WKWebsiteDataStoreSetResourceLoadStatisticsFirstPartyWebsiteDataRemovalModeForTestingFunction completionHandler);
130130typedef void (*WKWebsiteDataStoreSetResourceLoadStatisticsToSameSiteStrictCookiesForTestingFunction)(void* functionContext);
131131WK_EXPORT void WKWebsiteDataStoreSetResourceLoadStatisticsToSameSiteStrictCookiesForTesting(WKWebsiteDataStoreRef dataStoreRef, WKStringRef hostName, void* context, WKWebsiteDataStoreSetResourceLoadStatisticsToSameSiteStrictCookiesForTestingFunction completionHandler);
 132typedef void (*WKWebsiteDataStoreStatisticsAddAppBoundDomainForTestingFunction)(uint64_t totalAppBoundDomains, void* functionContext);
 133WK_EXPORT void WKWebsiteDataStoreStatisticsAddAppBoundDomainForTesting(WKWebsiteDataStoreRef dataStoreRef, WKStringRef hostName, void* context, WKWebsiteDataStoreStatisticsAddAppBoundDomainForTestingFunction completionHandler);
132134typedef void (*WKWebsiteDataStoreStatisticsResetToConsistentStateFunction)(void* functionContext);
133135WK_EXPORT void WKWebsiteDataStoreStatisticsResetToConsistentState(WKWebsiteDataStoreRef dataStoreRef, void* context, WKWebsiteDataStoreStatisticsResetToConsistentStateFunction completionHandler);
134136

Source/WebKit/UIProcess/Network/NetworkProcessProxy.cpp

@@void NetworkProcessProxy::hasIsolatedSession(PAL::SessionID sessionID, const Reg
11211121 sendWithAsyncReply(Messages::NetworkProcess::HasIsolatedSession(sessionID, domain), WTFMove(completionHandler));
11221122}
11231123
 1124void NetworkProcessProxy::setAppBoundDomainsForResourceLoadStatistics(PAL::SessionID sessionID, const HashSet<RegistrableDomain>& appBoundDomains)
 1125{
 1126 if (!canSendMessage())
 1127 return;
 1128
 1129 send(Messages::NetworkProcess::SetAppBoundDomainsForResourceLoadStatistics(sessionID, appBoundDomains), 0);
 1130}
 1131
11241132void NetworkProcessProxy::setShouldDowngradeReferrerForTesting(bool enabled, CompletionHandler<void()>&& completionHandler)
11251133{
11261134 if (!canSendMessage()) {

@@void NetworkProcessProxy::setToSameSiteStrictCookiesForTesting(PAL::SessionID se
11601168
11611169 sendWithAsyncReply(Messages::NetworkProcess::SetToSameSiteStrictCookiesForTesting(sessionID, domain), WTFMove(completionHandler));
11621170}
 1171
 1172void NetworkProcessProxy::addAppBoundDomainForTesting(PAL::SessionID sessionID, RegistrableDomain&& domain, CompletionHandler<void(uint64_t)>&& completionHandler)
 1173{
 1174 if (!canSendMessage()) {
 1175 completionHandler(0);
 1176 return;
 1177 }
 1178
 1179 sendWithAsyncReply(Messages::NetworkProcess::AddAppBoundDomainForTesting(sessionID, WTFMove(domain)), WTFMove(completionHandler));
 1180}
11631181#endif // ENABLE(RESOURCE_LOAD_STATISTICS)
11641182
11651183void NetworkProcessProxy::sendProcessWillSuspendImminentlyForTesting()

Source/WebKit/UIProcess/Network/NetworkProcessProxy.h

@@public:
174174 void deleteCookiesForTesting(PAL::SessionID, const RegistrableDomain&, bool includeHttpOnlyCookies, CompletionHandler<void()>&&);
175175 void deleteWebsiteDataInUIProcessForRegistrableDomains(PAL::SessionID, OptionSet<WebsiteDataType>, OptionSet<WebsiteDataFetchOption>, Vector<RegistrableDomain>, CompletionHandler<void(HashSet<WebCore::RegistrableDomain>&&)>&&);
176176 void hasIsolatedSession(PAL::SessionID, const RegistrableDomain&, CompletionHandler<void(bool)>&&);
 177 void setAppBoundDomainsForResourceLoadStatistics(PAL::SessionID, const HashSet<RegistrableDomain>&);
 178 void addAppBoundDomainForTesting(PAL::SessionID, RegistrableDomain&&, CompletionHandler<void(uint64_t)>&&);
177179 void setShouldDowngradeReferrerForTesting(bool, CompletionHandler<void()>&&);
178180 void setShouldBlockThirdPartyCookiesForTesting(PAL::SessionID, WebCore::ThirdPartyCookieBlockingMode, CompletionHandler<void()>&&);
179181 void setShouldEnbleSameSiteStrictEnforcementForTesting(PAL::SessionID, WebCore::SameSiteStrictEnforcementEnabled, CompletionHandler<void()>&&);

Source/WebKit/UIProcess/WebProcessPool.cpp

@@NetworkProcessProxy& WebProcessPool::ensureNetworkProcess(WebsiteDataStore* with
605605 WebCore::ThirdPartyCookieBlockingMode thirdPartyCookieBlockingMode = WebCore::ThirdPartyCookieBlockingMode::All;
606606 WebCore::SameSiteStrictEnforcementEnabled sameSiteStrictEnforcementEnabled = WebCore::SameSiteStrictEnforcementEnabled::No;
607607#endif
608  WebCore::RegistrableDomain standaloneApplicationDomain { };
609608 WebCore::FirstPartyWebsiteDataRemovalMode firstPartyWebsiteDataRemovalMode = WebCore::FirstPartyWebsiteDataRemovalMode::AllButCookies;
 609 WebCore::RegistrableDomain standaloneApplicationDomain { };
 610 Vector<WebCore::RegistrableDomain> appBoundDomains;
610611 WebCore::RegistrableDomain manualPrevalentResource { };
611612 WEB_PROCESS_POOL_ADDITIONS_2
612613 if (withWebsiteDataStore) {

@@NetworkProcessProxy& WebProcessPool::ensureNetworkProcess(WebsiteDataStore* with
624625#endif
625626 firstPartyWebsiteDataRemovalMode = networkSessionParameters.resourceLoadStatisticsParameters.firstPartyWebsiteDataRemovalMode;
626627 standaloneApplicationDomain = networkSessionParameters.resourceLoadStatisticsParameters.standaloneApplicationDomain;
 628 appBoundDomains = networkSessionParameters.resourceLoadStatisticsParameters.appBoundDomains;
627629 manualPrevalentResource = networkSessionParameters.resourceLoadStatisticsParameters.manualPrevalentResource;
628630 }
629631

@@NetworkProcessProxy& WebProcessPool::ensureNetworkProcess(WebsiteDataStore* with
650652#endif
651653 firstPartyWebsiteDataRemovalMode = networkSessionParameters.resourceLoadStatisticsParameters.firstPartyWebsiteDataRemovalMode;
652654 standaloneApplicationDomain = networkSessionParameters.resourceLoadStatisticsParameters.standaloneApplicationDomain;
 655 appBoundDomains = networkSessionParameters.resourceLoadStatisticsParameters.appBoundDomains;
653656 manualPrevalentResource = networkSessionParameters.resourceLoadStatisticsParameters.manualPrevalentResource;
654657 }
655658

@@NetworkProcessProxy& WebProcessPool::ensureNetworkProcess(WebsiteDataStore* with
689692 sameSiteStrictEnforcementEnabled,
690693#endif
691694 firstPartyWebsiteDataRemovalMode,
 695 standaloneApplicationDomain,
 696 appBoundDomains,
692697 manualPrevalentResource,
693698 };
694699

Source/WebKit/UIProcess/WebsiteData/Cocoa/WebsiteDataStoreCocoa.mm

@@void WebsiteDataStore::initializeAppBoundDomains(ForceReinitialization forceRein
405405
406406 static const auto maxAppBoundDomainCount = 10;
407407
408  appBoundDomainQueue().dispatch([isInAppBrowserPrivacyEnabled = parameters().networkSessionParameters.isInAppBrowserPrivacyEnabled, forceReinitialization] () mutable {
 408 appBoundDomainQueue().dispatch([this, isInAppBrowserPrivacyEnabled = parameters().networkSessionParameters.isInAppBrowserPrivacyEnabled, forceReinitialization] () mutable {
409409 if (hasInitializedAppBoundDomains && forceReinitialization != ForceReinitialization::Yes)
410410 return;
411411
412412 NSArray<NSString *> *domains = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"WKAppBoundDomains"];
413413 keyExists = domains ? true : false;
414414
415  RunLoop::main().dispatch([isInAppBrowserPrivacyEnabled, forceReinitialization, domains = retainPtr(domains)] {
 415 RunLoop::main().dispatch([this, isInAppBrowserPrivacyEnabled, forceReinitialization, domains = retainPtr(domains)] {
416416 if (forceReinitialization == ForceReinitialization::Yes)
417417 appBoundDomains().clear();
418418

@@void WebsiteDataStore::initializeAppBoundDomains(ForceReinitialization forceRein
433433 WEBSITE_DATA_STORE_ADDITIONS
434434 }
435435 hasInitializedAppBoundDomains = true;
 436 setAppBoundDomainsForResourceLoadStatistics();
436437 });
437438 });
438439}

@@void WebsiteDataStore::ensureAppBoundDomains(CompletionHandler<void(const HashSe
444445 return;
445446 }
446447
 448 // Hopping to the background thread then back to the main thread
 449 // ensures that initializeAppBoundDomains() has finished.
447450 appBoundDomainQueue().dispatch([completionHandler = WTFMove(completionHandler)] () mutable {
448451 RunLoop::main().dispatch([completionHandler = WTFMove(completionHandler)] () mutable {
449452 ASSERT(hasInitializedAppBoundDomains);

@@void WebsiteDataStore::getAppBoundDomains(CompletionHandler<void(const HashSet<W
484487 });
485488}
486489
 490Optional<HashSet<WebCore::RegistrableDomain>> WebsiteDataStore::getAppBoundDomainsIfInitialized() const
 491{
 492 ASSERT(RunLoop::isMain());
 493 if (!hasInitializedAppBoundDomains)
 494 return WTF::nullopt;
 495 return appBoundDomains();
 496}
 497
487498void WebsiteDataStore::reinitializeAppBoundDomains()
488499{
489500 hasInitializedAppBoundDomains = false;

Source/WebKit/UIProcess/WebsiteData/WebsiteDataStore.cpp

@@void WebsiteDataStore::setResourceLoadStatisticsToSameSiteStrictCookiesForTestin
19061906 networkProcess->setToSameSiteStrictCookiesForTesting(m_sessionID, WebCore::RegistrableDomain { url }, [callbackAggregator = callbackAggregator.copyRef()] { });
19071907 }
19081908}
 1909
 1910void WebsiteDataStore::addResourceLoadStatisticsAppBoundDomainForTesting(const URL& url, CompletionHandler<void(uint64_t)>&& completionHandler)
 1911{
 1912 auto callbackAggregator = CallbackAggregator::create([this, domain = WebCore::RegistrableDomain { url }, completionHandler = WTFMove(completionHandler)]() mutable {
 1913 for (auto& processPool : processPools()) {
 1914 if (auto* networkProcess = processPool->networkProcess()) {
 1915 networkProcess->addAppBoundDomainForTesting(m_sessionID, WTFMove(domain), WTFMove(completionHandler));
 1916 return;
 1917 }
 1918 }
 1919 completionHandler(0);
 1920 });
 1921
 1922 if (m_thirdPartyCookieBlockingMode != WebCore::ThirdPartyCookieBlockingMode::AllExceptBetweenAppBoundDomains) {
 1923 m_thirdPartyCookieBlockingMode = WebCore::ThirdPartyCookieBlockingMode::AllExceptBetweenAppBoundDomains;
 1924 for (auto& webProcess : processes())
 1925 webProcess.setShouldBlockThirdPartyCookiesForTesting(WebCore::ThirdPartyCookieBlockingMode::AllExceptBetweenAppBoundDomains, [callbackAggregator = callbackAggregator.copyRef()] { });
 1926 }
 1927}
19091928#endif // ENABLE(RESOURCE_LOAD_STATISTICS)
19101929
19111930void WebsiteDataStore::setCacheMaxAgeCapForPrevalentResources(Seconds seconds, CompletionHandler<void()>&& completionHandler)

@@WebsiteDataStoreParameters WebsiteDataStore::parameters()
22192238 bool shouldIncludeLocalhostInResourceLoadStatistics = false;
22202239 bool enableResourceLoadStatisticsDebugMode = false;
22212240 auto firstPartyWebsiteDataRemovalMode = WebCore::FirstPartyWebsiteDataRemovalMode::AllButCookies;
 2241 WebCore::RegistrableDomain standaloneApplicationDomain { };
 2242 Vector<WebCore::RegistrableDomain> appBoundDomains;
 2243#if PLATFORM(COCOA)
 2244 auto optionalAppBoundDomains = getAppBoundDomainsIfInitialized();
 2245 if (optionalAppBoundDomains && m_appBoundITPRelaxationEnabled)
 2246 appBoundDomains = copyToVector(*optionalAppBoundDomains);
 2247#endif
22222248 WebCore::RegistrableDomain resourceLoadStatisticsManualPrevalentResource { };
22232249
22242250 ResourceLoadStatisticsParameters resourceLoadStatisticsParameters = {

@@WebsiteDataStoreParameters WebsiteDataStore::parameters()
22392265 WebCore::SameSiteStrictEnforcementEnabled::No,
22402266#endif
22412267 firstPartyWebsiteDataRemovalMode,
 2268 WTFMove(standaloneApplicationDomain),
 2269 WTFMove(appBoundDomains),
22422270 WTFMove(resourceLoadStatisticsManualPrevalentResource),
22432271 };
22442272

@@void WebsiteDataStore::clearAppBoundSession(CompletionHandler<void()>&& completi
24072435 }
24082436}
24092437
 2438#if ENABLE(RESOURCE_LOAD_STATISTICS)
 2439void WebsiteDataStore::setAppBoundDomainsForResourceLoadStatistics()
 2440{
 2441#if PLATFORM(COCOA)
 2442 if (!m_appBoundITPRelaxationEnabled)
 2443 return;
 2444
 2445 auto appBoundDomains = getAppBoundDomainsIfInitialized();
 2446 ASSERT(appBoundDomains);
 2447 if (!appBoundDomains)
 2448 return;
 2449
 2450 if (m_thirdPartyCookieBlockingMode != WebCore::ThirdPartyCookieBlockingMode::AllExceptBetweenAppBoundDomains) {
 2451 m_thirdPartyCookieBlockingMode = WebCore::ThirdPartyCookieBlockingMode::AllExceptBetweenAppBoundDomains;
 2452 for (auto& webProcess : processes())
 2453 webProcess.setShouldBlockThirdPartyCookiesForTesting(WebCore::ThirdPartyCookieBlockingMode::AllExceptBetweenAppBoundDomains, [] { });
 2454 }
 2455
 2456 for (auto& processPool : processPools()) {
 2457 if (auto* networkProcess = processPool->networkProcess())
 2458 networkProcess->setAppBoundDomainsForResourceLoadStatistics(m_sessionID, *appBoundDomains);
 2459 }
 2460#endif
 2461}
 2462#endif
24102463}

Source/WebKit/UIProcess/WebsiteData/WebsiteDataStore.h

@@public:
201201 void setResourceLoadStatisticsFirstPartyWebsiteDataRemovalModeForTesting(bool enabled, CompletionHandler<void()>&&);
202202 void setResourceLoadStatisticsToSameSiteStrictCookiesForTesting(const URL&, CompletionHandler<void()>&&);
203203 WebCore::ThirdPartyCookieBlockingMode thirdPartyCookieBlockingMode() const;
 204 void addResourceLoadStatisticsAppBoundDomainForTesting(const URL&, CompletionHandler<void(uint64_t)>&&);
204205 bool isItpStateExplicitlySet() const { return m_isItpStateExplicitlySet; }
205206 void useExplicitITPState() { m_isItpStateExplicitlySet = true; }
206207#endif

@@private:
327328
328329 void maybeRegisterWithSessionIDMap();
329330
 331 Optional<HashSet<WebCore::RegistrableDomain>> getAppBoundDomainsIfInitialized() const;
 332#if ENABLE(RESOURCE_LOAD_STATISTICS)
 333 void setAppBoundDomainsForResourceLoadStatistics();
 334#endif
 335
330336 const PAL::SessionID m_sessionID;
331337
332338 Ref<WebsiteDataStoreConfiguration> m_resolvedConfiguration;

@@private:
339345 bool m_resourceLoadStatisticsDebugMode { false };
340346 bool m_resourceLoadStatisticsEnabled { false };
341347 WTF::Function<void(const String&)> m_statisticsTestingCallback;
 348 mutable Optional<WebCore::ThirdPartyCookieBlockingMode> m_thirdPartyCookieBlockingMode; // Lazily computed.
 349 // Remove if we decide to enable this feature.
 350 bool m_appBoundITPRelaxationEnabled { false };
342351#endif
343352
344353 Ref<WorkQueue> m_queue;

@@private:
377386#if HAVE(APP_SSO)
378387 UniqueRef<SOAuthorizationCoordinator> m_soAuthorizationCoordinator;
379388#endif
380 #if ENABLE(RESOURCE_LOAD_STATISTICS)
381  mutable Optional<WebCore::ThirdPartyCookieBlockingMode> m_thirdPartyCookieBlockingMode; // Lazily computed.
382 #endif
383389};
384390
385391}

Tools/ChangeLog

 12020-04-30 John Wilander <wilander@apple.com>
 2
 3 Exempt app-bound domains from ITP's website data deletion and third-party cookie blocking between themselves
 4 https://bugs.webkit.org/show_bug.cgi?id=210674
 5 <rdar://problem/61950767>
 6
 7 Reviewed by NOBODY (OOPS!).
 8
 9 This change adds a new TestRunner function
 10 addStatisticsAppBoundDomain() which returns the total number
 11 of app-bound domains configured so that it can be checked.
 12
 13 * WebKitTestRunner/InjectedBundle/Bindings/TestRunner.idl:
 14 * WebKitTestRunner/InjectedBundle/TestRunner.cpp:
 15 (WTR::TestRunner::addStatisticsAppBoundDomain):
 16 * WebKitTestRunner/InjectedBundle/TestRunner.h:
 17 * WebKitTestRunner/TestController.cpp:
 18 (WTR::resourceStatisticsUnsignedResultCallback):
 19 (WTR::TestController::addStatisticsAppBoundDomain):
 20 * WebKitTestRunner/TestController.h:
 21 * WebKitTestRunner/TestInvocation.cpp:
 22 (WTR::TestInvocation::didReceiveSynchronousMessageFromInjectedBundle):
 23
1242020-04-30 Alex Christensen <achristensen@webkit.org>
225
326 Add SPI to change a WKWebView's CORS disabling pattern after initialization

Tools/WebKitTestRunner/InjectedBundle/Bindings/TestRunner.idl

@@interface TestRunner {
348348 void setStatisticsShouldBlockThirdPartyCookies(boolean value, object callback, optional boolean onlyOnSitesWithoutUserInteraction);
349349 void setStatisticsFirstPartyWebsiteDataRemovalMode(boolean value, object callback);
350350 void statisticsSetToSameSiteStrictCookies(DOMString hostName, object callback);
 351 unsigned long addStatisticsAppBoundDomain(DOMString hostName);
351352 void loadedThirdPartyDomains(object callback);
352353
353354 // Injected bundle form client.

Tools/WebKitTestRunner/InjectedBundle/TestRunner.cpp

@@void TestRunner::statisticsCallDidSetToSameSiteStrictCookiesCallback()
23302330 callTestRunnerCallback(StatisticsDidSetToSameSiteStrictCookiesCallbackID);
23312331}
23322332
 2333unsigned TestRunner::addStatisticsAppBoundDomain(JSStringRef hostName)
 2334{
 2335 auto messageName = adoptWK(WKStringCreateWithUTF8CString("AddStatisticsAppBoundDomain"));
 2336 auto messageBody = adoptWK(WKStringCreateWithJSString(hostName));
 2337 WKTypeRef returnData = nullptr;
 2338 WKBundlePagePostSynchronousMessageForTesting(InjectedBundle::singleton().page()->page(), messageName.get(), messageBody.get(), &returnData);
 2339 ASSERT(WKGetTypeID(returnData) == WKUInt64GetTypeID());
 2340 return WKUInt64GetValue(adoptWK(static_cast<WKUInt64Ref>(returnData)).get());
 2341}
 2342
23332343void TestRunner::statisticsResetToConsistentState(JSValueRef completionHandler)
23342344{
23352345 cacheTestRunnerCallback(StatisticsDidResetToConsistentStateCallbackID, completionHandler);

Tools/WebKitTestRunner/InjectedBundle/TestRunner.h

@@public:
452452 void statisticsCallDidSetFirstPartyWebsiteDataRemovalModeCallback();
453453 void statisticsSetToSameSiteStrictCookies(JSStringRef hostName, JSValueRef callback);
454454 void statisticsCallDidSetToSameSiteStrictCookiesCallback();
 455 unsigned addStatisticsAppBoundDomain(JSStringRef hostName);
455456 void statisticsResetToConsistentState(JSValueRef completionHandler);
456457 void statisticsCallDidResetToConsistentStateCallback();
457458 void loadedThirdPartyDomains(JSValueRef callback);

Tools/WebKitTestRunner/TestController.cpp

@@struct ResourceStatisticsCallbackContext {
33913391 bool done { false };
33923392 bool result { false };
33933393 WKRetainPtr<WKStringRef> resourceLoadStatisticsRepresentation;
 3394 uint64_t unsignedResult;
33943395};
33953396
33963397static void resourceStatisticsStringResultCallback(WKStringRef resourceLoadStatisticsRepresentation, void* userData)

@@static void resourceStatisticsBooleanResultCallback(bool result, void* userData)
34163417 context->testController.notifyDone();
34173418}
34183419
 3420static void resourceStatisticsUnsignedResultCallback(uint64_t result, void* userData)
 3421{
 3422 auto* context = static_cast<ResourceStatisticsCallbackContext*>(userData);
 3423 context->unsignedResult = result;
 3424 context->done = true;
 3425 context->testController.notifyDone();
 3426}
 3427
34193428void TestController::clearStatisticsDataForDomain(WKStringRef domain)
34203429{
34213430 ResourceStatisticsCallbackContext context(*this);

@@void TestController::setStatisticsToSameSiteStrictCookies(WKStringRef hostName)
37773786 m_currentInvocation->didSetToSameSiteStrictCookies();
37783787}
37793788
 3789unsigned TestController::addStatisticsAppBoundDomain(WKStringRef hostName)
 3790{
 3791 ResourceStatisticsCallbackContext context(*this);
 3792 WKWebsiteDataStoreStatisticsAddAppBoundDomainForTesting(websiteDataStore(), hostName, &context, resourceStatisticsUnsignedResultCallback);
 3793 runUntil(context.done, noTimeout);
 3794 return context.unsignedResult;
 3795}
 3796
37803797void TestController::statisticsResetToConsistentState()
37813798{
37823799 ResourceStatisticsCallbackContext context(*this);

Tools/WebKitTestRunner/TestController.h

@@public:
266266 void setStatisticsShouldBlockThirdPartyCookies(bool value, bool onlyOnSitesWithoutUserInteraction);
267267 void setStatisticsFirstPartyWebsiteDataRemovalMode(bool value);
268268 void setStatisticsToSameSiteStrictCookies(WKStringRef hostName);
 269 unsigned addStatisticsAppBoundDomain(WKStringRef hostName);
269270 void statisticsResetToConsistentState();
270271
271272 void getAllStorageAccessEntries();

Tools/WebKitTestRunner/TestInvocation.cpp

@@WKRetainPtr<WKTypeRef> TestInvocation::didReceiveSynchronousMessageFromInjectedB
16401640 return result;
16411641 }
16421642
 1643 if (WKStringIsEqualToUTF8CString(messageName, "AddStatisticsAppBoundDomain")) {
 1644 ASSERT(WKGetTypeID(messageBody) == WKStringGetTypeID());
 1645 WKStringRef hostName = static_cast<WKStringRef>(messageBody);
 1646 auto result = adoptWK(WKUInt64Create(TestController::singleton().addStatisticsAppBoundDomain(hostName)));
 1647 return result;
 1648 }
 1649
16431650 if (WKStringIsEqualToUTF8CString(messageName, "ClearDOMCache")) {
16441651 ASSERT(WKGetTypeID(messageBody) == WKStringGetTypeID());
16451652 WKStringRef origin = static_cast<WKStringRef>(messageBody);

LayoutTests/ChangeLog

 12020-04-30 John Wilander <wilander@apple.com>
 2
 3 Exempt app-bound domains from ITP's website data deletion and third-party cookie blocking between themselves
 4 https://bugs.webkit.org/show_bug.cgi?id=210674
 5 <rdar://problem/61950767>
 6
 7 Reviewed by NOBODY (OOPS!).
 8
 9 * http/tests/resourceLoadStatistics/exemptDomains/app-bound-domains-exempt-from-cookie-blocking-between-each-other-expected.txt: Added.
 10 * http/tests/resourceLoadStatistics/exemptDomains/app-bound-domains-exempt-from-cookie-blocking-between-each-other.html: Added.
 11 * http/tests/resourceLoadStatistics/exemptDomains/app-bound-domains-exempt-from-website-data-deletion-database-expected.txt: Added.
 12 * http/tests/resourceLoadStatistics/exemptDomains/app-bound-domains-exempt-from-website-data-deletion-database.html: Added.
 13 * http/tests/resourceLoadStatistics/exemptDomains/app-bound-domains-exempt-from-website-data-deletion-expected.txt: Added.
 14 * http/tests/resourceLoadStatistics/exemptDomains/app-bound-domains-exempt-from-website-data-deletion.html: Added.
 15
1162020-04-30 Devin Rousso <drousso@apple.com>
217
318 WebKit.WebContent process crashes when web developer tools are opened in Safari

LayoutTests/http/tests/resourceLoadStatistics/exemptDomains/app-bound-domains-exempt-from-cookie-blocking-between-each-other-expected.txt

 1Tests that app-bound domains are exempt from third-party cookie blocking between themselves.
 2
 3On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
 4
 5
 6PASS successfullyParsed is true
 7
 8TEST COMPLETE
 9
 10
 11--------
 12Frame: '<!--frame1-->'
 13--------
 14Should not receive cookie.
 15Did not receive cookie named 'firstPartyCookie'.
 16Did not receive cookie named 'partitionedCookie'.
 17Client-side document.cookie:
 18
 19--------
 20Frame: '<!--frame2-->'
 21--------
 22Should receive cookie.
 23Received cookie named 'firstPartyCookie'.
 24Did not receive cookie named 'partitionedCookie'.
 25Client-side document.cookie: firstPartyCookie=value

LayoutTests/http/tests/resourceLoadStatistics/exemptDomains/app-bound-domains-exempt-from-cookie-blocking-between-each-other.html

 1<!DOCTYPE html>
 2<html lang="en">
 3<head>
 4 <meta charset="UTF-8">
 5 <script src="/js-test-resources/js-test.js"></script>
 6 <script src="../resources/util.js"></script>
 7</head>
 8<body>
 9<script>
 10 description("Tests that app-bound domains are exempt from third-party cookie blocking between themselves.");
 11 jsTestIsAsync = true;
 12
 13 const firstPartyOrigin = "http://127.0.0.1:8000";
 14 const thirdPartyOrigin = "http://localhost:8000";
 15 const thirdPartyResourceUrl = thirdPartyOrigin + "/resourceLoadStatistics/resources";
 16 const firstPartyCookieName = "firstPartyCookie";
 17 const subPathToSetFirstPartyCookie = "/set-cookie.php?name=" + firstPartyCookieName + "&value=value";
 18 const partitionedCookieName = "partitionedCookie";
 19 const subPathToSetPartitionedCookie = "/set-cookie.php?name=" + partitionedCookieName + "&value=value";
 20 const returnUrl = firstPartyOrigin + "/resourceLoadStatistics/exemptDomains/app-bound-domains-exempt-from-cookie-blocking-between-each-other.html";
 21 const subPathToGetCookies = "/get-cookies.php?name1=" + firstPartyCookieName + "&name2=" + partitionedCookieName;
 22 const redirectChainUrl = thirdPartyResourceUrl + "/redirect.php?redirectTo=" + thirdPartyResourceUrl + subPathToGetCookies;
 23
 24 function openIframe(url, onLoadHandler) {
 25 const element = document.createElement("iframe");
 26 element.src = url;
 27 if (onLoadHandler) {
 28 element.onload = onLoadHandler;
 29 }
 30 document.body.appendChild(element);
 31 }
 32
 33 function runTest() {
 34 switch (document.location.hash) {
 35 case "#step1":
 36 // Set first-party cookie for localhost.
 37 document.location.href = thirdPartyResourceUrl + subPathToSetFirstPartyCookie + "#" + returnUrl + "#step2";
 38 break;
 39 case "#step2":
 40 // Check that the cookie doesn't get sent for localhost under 127.0.0.1 since they are both configured as app-bound domains.
 41 document.location.hash = "step3";
 42 openIframe(thirdPartyResourceUrl + subPathToGetCookies + "&message=Should not receive cookie.", function() {
 43 if (2 !== testRunner.addStatisticsAppBoundDomain(firstPartyOrigin))
 44 testFailed("Didn't get a total of two app-bound domains after adding the second.");
 45 runTest();
 46 });
 47 break;
 48 case "#step3":
 49 // Check that the cookie gets sent for localhost under 127.0.0.1 since they are now both configured as app-bound domains.
 50 document.location.hash = "step4";
 51 openIframe(thirdPartyResourceUrl + subPathToGetCookies + "&message=Should receive cookie.", runTest);
 52 break;
 53 case "#step4":
 54 testRunner.setStatisticsShouldBlockThirdPartyCookies(false, function() {
 55 setEnableFeature(false, finishJSTest);
 56 });
 57 break;
 58 }
 59 }
 60
 61 if (document.location.hash === "") {
 62 setEnableFeature(true, function () {
 63 if (1 !== testRunner.addStatisticsAppBoundDomain(thirdPartyOrigin))
 64 testFailed("Didn't get a total of one app-bound domain after adding the first.");
 65 testRunner.dumpChildFramesAsText();
 66 document.location.hash = "step1";
 67 runTest();
 68 });
 69 } else {
 70 runTest();
 71 }
 72</script>
 73</body>
 74</html>

LayoutTests/http/tests/resourceLoadStatistics/exemptDomains/app-bound-domains-exempt-from-website-data-deletion-database-expected.txt

 1Check that website data does not get removed after a period of no user interaction if the website is app-bound.
 2
 3Before deletion: Client-side cookie exists.
 4Before deletion: HttpOnly cookie exists.
 5Before deletion: Regular server-side cookie exists.
 6Before deletion: LocalStorage entry does exist.
 7Before deletion: IDB entry does exist.
 8
 9After deletion: HttpOnly cookie exists.
 10After deletion: Client-side cookie exists.
 11After deletion: Regular server-side cookie exists.
 12After deletion: LocalStorage entry does exist.
 13After deletion: IDB entry does exist.
 14
 15

LayoutTests/http/tests/resourceLoadStatistics/exemptDomains/app-bound-domains-exempt-from-website-data-deletion-database.html

 1<!DOCTYPE html>
 2<html>
 3<head>
 4 <script src="/cookies/resources/cookie-utilities.js"></script>
 5 <script src="../resources/util.js"></script>
 6</head>
 7<body onload="setTimeout('runTest()', 0)">
 8<div id="description">Check that website data does not get removed after a period of no user interaction if the website is app-bound.</div>
 9<br>
 10<div id="output"></div>
 11<br>
 12<script>
 13 testRunner.waitUntilDone();
 14 testRunner.dumpAsText();
 15 testRunner.setUseITPDatabase(true);
 16
 17 const httpOnlyCookieName = "http-only-cookie";
 18 const serverSideCookieName = "server-side-cookie";
 19 const clientSideCookieName = "client-side-cookie";
 20
 21 function sortStringArray(a, b) {
 22 a = a.toLowerCase();
 23 b = b.toLowerCase();
 24
 25 return a > b ? 1 : b > a ? -1 : 0;
 26 }
 27
 28 function addLinebreakToOutput() {
 29 let element = document.createElement("br");
 30 output.appendChild(element);
 31 }
 32
 33 function addOutput(message) {
 34 let element = document.createElement("div");
 35 element.innerText = message;
 36 output.appendChild(element);
 37 }
 38
 39 function checkCookies(isAfterDeletion) {
 40 let unsortedTestPassedMessages = [];
 41 let cookies = internals.getCookies();
 42 if (!cookies.length)
 43 addOutput((isAfterDeletion ? "After" : "Before") + " script-accessible deletion: No cookies found.");
 44 for (let cookie of cookies) {
 45 switch (cookie.name) {
 46 case httpOnlyCookieName:
 47 unsortedTestPassedMessages.push((isAfterDeletion ? "After" : "Before") + " deletion: " + (isAfterDeletion ? " " : "") + "HttpOnly cookie exists.");
 48 break;
 49 case serverSideCookieName:
 50 unsortedTestPassedMessages.push((isAfterDeletion ? "After" : "Before") + " deletion: Regular server-side cookie exists.");
 51 break;
 52 case clientSideCookieName:
 53 unsortedTestPassedMessages.push((isAfterDeletion ? "After" : "Before") + " deletion: Client-side cookie exists.");
 54 break;
 55 }
 56 }
 57 let sortedTestPassedMessages = unsortedTestPassedMessages.sort(sortStringArray);
 58 for (let testPassedMessage of sortedTestPassedMessages) {
 59 addOutput(testPassedMessage);
 60 }
 61 }
 62
 63 const dbName = "TestDatabase";
 64
 65 function createIDBDataStore(callback) {
 66 let request = indexedDB.open(dbName);
 67 request.onerror = function() {
 68 addOutput("Couldn't create indexedDB.");
 69 finishTest();
 70 };
 71 request.onupgradeneeded = function(event) {
 72 let db = event.target.result;
 73 let objStore = db.createObjectStore("test", {autoIncrement: true});
 74 objStore.add("value");
 75 callback();
 76 }
 77 }
 78
 79 const maxIntervals = 20;
 80
 81 let intervalCounterIDB;
 82 let checkIDBCallback;
 83 let checkIDBIntervalID;
 84 let semaphoreIDBCheck = false;
 85 function checkIDBDataStoreExists(isAfterDeletion, callback) {
 86 let request;
 87 intervalCounterIDB = 0;
 88 checkIDBCallback = callback;
 89 if (!isAfterDeletion) {
 90 // Check until there is a IDB.
 91 checkIDBIntervalID = setInterval(function() {
 92 if (semaphoreIDBCheck)
 93 return;
 94 semaphoreIDBCheck = true;
 95
 96 if (++intervalCounterIDB >= maxIntervals) {
 97 clearInterval(checkIDBIntervalID);
 98 addOutput("Before deletion: IDB entry does not exist.");
 99 semaphoreIDBCheck = false;
 100 checkIDBCallback();
 101 } else {
 102 request = indexedDB.open(dbName);
 103 request.onerror = function () {
 104 clearInterval(checkIDBIntervalID);
 105 addOutput("Couldn't open indexedDB.");
 106 semaphoreIDBCheck = false;
 107 finishTest();
 108 };
 109 request.onupgradeneeded = function () {
 110 // Let the next interval check again.
 111 semaphoreIDBCheck = false;
 112 };
 113 request.onsuccess = function () {
 114 clearInterval(checkIDBIntervalID);
 115 addOutput("Before deletion: IDB entry does exist.");
 116 semaphoreIDBCheck = false;
 117 checkIDBCallback();
 118 };
 119 }
 120 }, 200);
 121 } else {
 122 // Check until there is a IDB.
 123 checkIDBIntervalID = setInterval(function () {
 124 if (semaphoreIDBCheck)
 125 return;
 126 semaphoreIDBCheck = true;
 127
 128 if (++intervalCounterIDB >= maxIntervals) {
 129 clearInterval(checkIDBIntervalID);
 130 addOutput("After deletion: IDB entry checks exhausted.");
 131 semaphoreIDBCheck = false;
 132 checkIDBCallback();
 133 } else {
 134 request = indexedDB.open(dbName);
 135 request.onerror = function () {
 136 clearInterval(checkIDBIntervalID);
 137 addOutput("Couldn't open indexedDB.");
 138 semaphoreIDBCheck = false;
 139 finishTest();
 140 };
 141 request.onupgradeneeded = function () {
 142 // Let the next interval check again.
 143 semaphoreIDBCheck = false;
 144 };
 145 request.onsuccess = function () {
 146 clearInterval(checkIDBIntervalID);
 147 addOutput("After deletion: IDB entry does exist.");
 148 semaphoreIDBCheck = false;
 149 checkIDBCallback();
 150 };
 151 }
 152 }, 200);
 153 }
 154 }
 155
 156 let intervalCounterLocalStorage;
 157 let checkLocalStorageCallback;
 158 let checkLocalStorageIntervalID;
 159 const localStorageName = "test";
 160 const localStorageValue = "value";
 161 function checkLocalStorageExists(isAfterDeletion, callback) {
 162 intervalCounterLocalStorage = 0;
 163 checkLocalStorageCallback = callback;
 164 if (!isAfterDeletion) {
 165 // Check until there is LocalStorage.
 166 checkLocalStorageIntervalID = setInterval(function() {
 167 if (++intervalCounterLocalStorage >= maxIntervals) {
 168 clearInterval(checkLocalStorageIntervalID);
 169 let value = localStorage.getItem(localStorageName);
 170 addOutput("Before deletion: LocalStorage entry " + (value === localStorageValue ? "does" : "does not") + " exist.");
 171 checkLocalStorageCallback();
 172 } else if (testRunner.isStatisticsHasLocalStorage(originUnderTest)) {
 173 clearInterval(checkLocalStorageIntervalID);
 174 let value = localStorage.getItem(localStorageName);
 175 addOutput("Before deletion: LocalStorage entry " + (value === localStorageValue ? "does" : "does not") + " exist.");
 176 checkLocalStorageCallback();
 177 }
 178 }, 100);
 179 } else {
 180 // Check until there is no LocalStorage.
 181 checkLocalStorageIntervalID = setInterval(function() {
 182 if (++intervalCounterLocalStorage >= maxIntervals) {
 183 clearInterval(checkLocalStorageIntervalID);
 184 let value = localStorage.getItem(localStorageName);
 185 addOutput("After deletion: LocalStorage entry " + (value === localStorageValue ? "does" : "does not") + " exist.");
 186 checkLocalStorageCallback();
 187 } else if (!testRunner.isStatisticsHasLocalStorage(originUnderTest)) {
 188 clearInterval(checkLocalStorageIntervalID);
 189 let value = localStorage.getItem(localStorageName);
 190 addOutput("After deletion: LocalStorage entry " + (value === localStorageValue ? "does" : "does not") + " exist.");
 191 checkLocalStorageCallback();
 192 }
 193 }, 100);
 194 }
 195 }
 196
 197 async function writeWebsiteDataAndContinue() {
 198 // Write cookies.
 199 await fetch("/cookies/resources/set-http-only-cookie.php?cookieName=" + httpOnlyCookieName, { credentials: "same-origin" });
 200 await fetch("/cookies/resources/setCookies.cgi", { headers: { "Set-Cookie": serverSideCookieName + "=1; path=/;" }, credentials: "same-origin" });
 201 document.cookie = clientSideCookieName + "=1";
 202
 203 checkCookies(false);
 204
 205 // Write LocalStorage
 206 localStorage.setItem(localStorageName, localStorageValue);
 207 checkLocalStorageExists(false, function() {
 208
 209 // Write IndexedDB.
 210 createIDBDataStore(function () {
 211 checkIDBDataStoreExists(false, function() {
 212 addLinebreakToOutput();
 213 processWebsiteDataAndContinue();
 214 });
 215 });
 216 });
 217 }
 218
 219 function processWebsiteDataAndContinue() {
 220 testRunner.installStatisticsDidScanDataRecordsCallback(checkWebsiteDataAndContinue);
 221 testRunner.statisticsProcessStatisticsAndDataRecords();
 222 }
 223
 224 function checkWebsiteDataAndContinue() {
 225 checkCookies(true);
 226 checkLocalStorageExists(true, function () {
 227 checkIDBDataStoreExists(true, finishTest);
 228 });
 229 }
 230
 231 function finishTest() {
 232 resetCookies();
 233 testRunner.setStatisticsFirstPartyWebsiteDataRemovalMode(false, function() {
 234 setEnableFeature(false, function() {
 235 testRunner.notifyDone();
 236 });
 237 });
 238 }
 239
 240 const originUnderTest = "http://127.0.0.1:8000";
 241 function runTest() {
 242 setEnableFeature(true, function () {
 243 if (1 !== testRunner.addStatisticsAppBoundDomain(originUnderTest))
 244 testFailed("Didn't get a total of one app-bound domain after adding the first.");
 245
 246 testRunner.setStatisticsFirstPartyWebsiteDataRemovalMode(true, function() {
 247 testRunner.setStatisticsPrevalentResource(originUnderTest, true, function() {
 248 if (!testRunner.isStatisticsPrevalentResource(originUnderTest))
 249 addOutput("FAIL: " + originUnderTest + " didn't get classified as prevalent.");
 250 writeWebsiteDataAndContinue();
 251 });
 252 });
 253 });
 254 }
 255</script>
 256</body>
 257</html>

LayoutTests/http/tests/resourceLoadStatistics/exemptDomains/app-bound-domains-exempt-from-website-data-deletion-expected.txt

 1Check that website data does not get removed after a period of no user interaction if the website is app-bound.
 2
 3Before deletion: Client-side cookie exists.
 4Before deletion: HttpOnly cookie exists.
 5Before deletion: Regular server-side cookie exists.
 6Before deletion: LocalStorage entry does exist.
 7Before deletion: IDB entry does exist.
 8
 9After deletion: HttpOnly cookie exists.
 10After deletion: Client-side cookie exists.
 11After deletion: Regular server-side cookie exists.
 12After deletion: LocalStorage entry does exist.
 13After deletion: IDB entry does exist.
 14
 15

LayoutTests/http/tests/resourceLoadStatistics/exemptDomains/app-bound-domains-exempt-from-website-data-deletion.html

 1<!DOCTYPE html>
 2<html>
 3<head>
 4 <script src="/cookies/resources/cookie-utilities.js"></script>
 5 <script src="../resources/util.js"></script>
 6</head>
 7<body onload="setTimeout('runTest()', 0)">
 8<div id="description">Check that website data does not get removed after a period of no user interaction if the website is app-bound.</div>
 9<br>
 10<div id="output"></div>
 11<br>
 12<script>
 13 testRunner.waitUntilDone();
 14 testRunner.dumpAsText();
 15
 16 const httpOnlyCookieName = "http-only-cookie";
 17 const serverSideCookieName = "server-side-cookie";
 18 const clientSideCookieName = "client-side-cookie";
 19
 20 function sortStringArray(a, b) {
 21 a = a.toLowerCase();
 22 b = b.toLowerCase();
 23
 24 return a > b ? 1 : b > a ? -1 : 0;
 25 }
 26
 27 function addLinebreakToOutput() {
 28 let element = document.createElement("br");
 29 output.appendChild(element);
 30 }
 31
 32 function addOutput(message) {
 33 let element = document.createElement("div");
 34 element.innerText = message;
 35 output.appendChild(element);
 36 }
 37
 38 function checkCookies(isAfterDeletion) {
 39 let unsortedTestPassedMessages = [];
 40 let cookies = internals.getCookies();
 41 if (!cookies.length)
 42 addOutput((isAfterDeletion ? "After" : "Before") + " script-accessible deletion: No cookies found.");
 43 for (let cookie of cookies) {
 44 switch (cookie.name) {
 45 case httpOnlyCookieName:
 46 unsortedTestPassedMessages.push((isAfterDeletion ? "After" : "Before") + " deletion: " + (isAfterDeletion ? " " : "") + "HttpOnly cookie exists.");
 47 break;
 48 case serverSideCookieName:
 49 unsortedTestPassedMessages.push((isAfterDeletion ? "After" : "Before") + " deletion: Regular server-side cookie exists.");
 50 break;
 51 case clientSideCookieName:
 52 unsortedTestPassedMessages.push((isAfterDeletion ? "After" : "Before") + " deletion: Client-side cookie exists.");
 53 break;
 54 }
 55 }
 56 let sortedTestPassedMessages = unsortedTestPassedMessages.sort(sortStringArray);
 57 for (let testPassedMessage of sortedTestPassedMessages) {
 58 addOutput(testPassedMessage);
 59 }
 60 }
 61
 62 const dbName = "TestDatabase";
 63
 64 function createIDBDataStore(callback) {
 65 let request = indexedDB.open(dbName);
 66 request.onerror = function() {
 67 addOutput("Couldn't create indexedDB.");
 68 finishTest();
 69 };
 70 request.onupgradeneeded = function(event) {
 71 let db = event.target.result;
 72 let objStore = db.createObjectStore("test", {autoIncrement: true});
 73 objStore.add("value");
 74 callback();
 75 }
 76 }
 77
 78 const maxIntervals = 20;
 79
 80 let intervalCounterIDB;
 81 let checkIDBCallback;
 82 let checkIDBIntervalID;
 83 let semaphoreIDBCheck = false;
 84 function checkIDBDataStoreExists(isAfterDeletion, callback) {
 85 let request;
 86 intervalCounterIDB = 0;
 87 checkIDBCallback = callback;
 88 if (!isAfterDeletion) {
 89 // Check until there is a IDB.
 90 checkIDBIntervalID = setInterval(function() {
 91 if (semaphoreIDBCheck)
 92 return;
 93 semaphoreIDBCheck = true;
 94
 95 if (++intervalCounterIDB >= maxIntervals) {
 96 clearInterval(checkIDBIntervalID);
 97 addOutput("Before deletion: IDB entry does not exist.");
 98 semaphoreIDBCheck = false;
 99 checkIDBCallback();
 100 } else {
 101 request = indexedDB.open(dbName);
 102 request.onerror = function () {
 103 clearInterval(checkIDBIntervalID);
 104 addOutput("Couldn't open indexedDB.");
 105 semaphoreIDBCheck = false;
 106 finishTest();
 107 };
 108 request.onupgradeneeded = function () {
 109 // Let the next interval check again.
 110 semaphoreIDBCheck = false;
 111 };
 112 request.onsuccess = function () {
 113 clearInterval(checkIDBIntervalID);
 114 addOutput("Before deletion: IDB entry does exist.");
 115 semaphoreIDBCheck = false;
 116 checkIDBCallback();
 117 };
 118 }
 119 }, 200);
 120 } else {
 121 // Check until there is a IDB.
 122 checkIDBIntervalID = setInterval(function () {
 123 if (semaphoreIDBCheck)
 124 return;
 125 semaphoreIDBCheck = true;
 126
 127 if (++intervalCounterIDB >= maxIntervals) {
 128 clearInterval(checkIDBIntervalID);
 129 addOutput("After deletion: IDB entry checks exhausted.");
 130 semaphoreIDBCheck = false;
 131 checkIDBCallback();
 132 } else {
 133 request = indexedDB.open(dbName);
 134 request.onerror = function () {
 135 clearInterval(checkIDBIntervalID);
 136 addOutput("Couldn't open indexedDB.");
 137 semaphoreIDBCheck = false;
 138 finishTest();
 139 };
 140 request.onupgradeneeded = function () {
 141 // Let the next interval check again.
 142 semaphoreIDBCheck = false;
 143 };
 144 request.onsuccess = function () {
 145 clearInterval(checkIDBIntervalID);
 146 addOutput("After deletion: IDB entry does exist.");
 147 semaphoreIDBCheck = false;
 148 checkIDBCallback();
 149 };
 150 }
 151 }, 200);
 152 }
 153 }
 154
 155 let intervalCounterLocalStorage;
 156 let checkLocalStorageCallback;
 157 let checkLocalStorageIntervalID;
 158 const localStorageName = "test";
 159 const localStorageValue = "value";
 160 function checkLocalStorageExists(isAfterDeletion, callback) {
 161 intervalCounterLocalStorage = 0;
 162 checkLocalStorageCallback = callback;
 163 if (!isAfterDeletion) {
 164 // Check until there is LocalStorage.
 165 checkLocalStorageIntervalID = setInterval(function() {
 166 if (++intervalCounterLocalStorage >= maxIntervals) {
 167 clearInterval(checkLocalStorageIntervalID);
 168 let value = localStorage.getItem(localStorageName);
 169 addOutput("Before deletion: LocalStorage entry " + (value === localStorageValue ? "does" : "does not") + " exist.");
 170 checkLocalStorageCallback();
 171 } else if (testRunner.isStatisticsHasLocalStorage(originUnderTest)) {
 172 clearInterval(checkLocalStorageIntervalID);
 173 let value = localStorage.getItem(localStorageName);
 174 addOutput("Before deletion: LocalStorage entry " + (value === localStorageValue ? "does" : "does not") + " exist.");
 175 checkLocalStorageCallback();
 176 }
 177 }, 100);
 178 } else {
 179 // Check until there is no LocalStorage.
 180 checkLocalStorageIntervalID = setInterval(function() {
 181 if (++intervalCounterLocalStorage >= maxIntervals) {
 182 clearInterval(checkLocalStorageIntervalID);
 183 let value = localStorage.getItem(localStorageName);
 184 addOutput("After deletion: LocalStorage entry " + (value === localStorageValue ? "does" : "does not") + " exist.");
 185 checkLocalStorageCallback();
 186 } else if (!testRunner.isStatisticsHasLocalStorage(originUnderTest)) {
 187 clearInterval(checkLocalStorageIntervalID);
 188 let value = localStorage.getItem(localStorageName);
 189 addOutput("After deletion: LocalStorage entry " + (value === localStorageValue ? "does" : "does not") + " exist.");
 190 checkLocalStorageCallback();
 191 }
 192 }, 100);
 193 }
 194 }
 195
 196 async function writeWebsiteDataAndContinue() {
 197 // Write cookies.
 198 await fetch("/cookies/resources/set-http-only-cookie.php?cookieName=" + httpOnlyCookieName, { credentials: "same-origin" });
 199 await fetch("/cookies/resources/setCookies.cgi", { headers: { "Set-Cookie": serverSideCookieName + "=1; path=/;" }, credentials: "same-origin" });
 200 document.cookie = clientSideCookieName + "=1";
 201
 202 checkCookies(false);
 203
 204 // Write LocalStorage
 205 localStorage.setItem(localStorageName, localStorageValue);
 206 checkLocalStorageExists(false, function() {
 207
 208 // Write IndexedDB.
 209 createIDBDataStore(function () {
 210 checkIDBDataStoreExists(false, function() {
 211 addLinebreakToOutput();
 212 processWebsiteDataAndContinue();
 213 });
 214 });
 215 });
 216 }
 217
 218 function processWebsiteDataAndContinue() {
 219 testRunner.installStatisticsDidScanDataRecordsCallback(checkWebsiteDataAndContinue);
 220 testRunner.statisticsProcessStatisticsAndDataRecords();
 221 }
 222
 223 function checkWebsiteDataAndContinue() {
 224 checkCookies(true);
 225 checkLocalStorageExists(true, function () {
 226 checkIDBDataStoreExists(true, finishTest);
 227 });
 228 }
 229
 230 function finishTest() {
 231 resetCookies();
 232 testRunner.setStatisticsFirstPartyWebsiteDataRemovalMode(false, function() {
 233 setEnableFeature(false, function() {
 234 testRunner.notifyDone();
 235 });
 236 });
 237 }
 238
 239 const originUnderTest = "http://127.0.0.1:8000";
 240 function runTest() {
 241 setEnableFeature(true, function () {
 242 if (1 !== testRunner.addStatisticsAppBoundDomain(originUnderTest))
 243 testFailed("Didn't get a total of one app-bound domain after adding the first.");
 244
 245 testRunner.setStatisticsFirstPartyWebsiteDataRemovalMode(true, function() {
 246 testRunner.setStatisticsPrevalentResource(originUnderTest, true, function() {
 247 if (!testRunner.isStatisticsPrevalentResource(originUnderTest))
 248 addOutput("FAIL: " + originUnderTest + " didn't get classified as prevalent.");
 249 writeWebsiteDataAndContinue();
 250 });
 251 });
 252 });
 253 }
 254</script>
 255</body>
 256</html>