ffd6a614cce78170a9c15c7d4a8a3336da4ba402
[WebKit-https.git] / Source / WebCore / platform / network / cf / NetworkStorageSessionCFNet.cpp
1 /*
2  * Copyright (C) 2012-2018 Apple Inc. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
14  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
15  * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
17  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
18  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
19  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
20  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
21  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
22  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
23  * THE POSSIBILITY OF SUCH DAMAGE.
24  */
25
26 #include "config.h"
27 #include "NetworkStorageSession.h"
28
29 #include <wtf/MainThread.h>
30 #include <wtf/NeverDestroyed.h>
31 #include <wtf/ProcessID.h>
32 #include <wtf/ProcessPrivilege.h>
33
34 #if PLATFORM(COCOA)
35 #include "PublicSuffix.h"
36 #include "ResourceRequest.h"
37 #else
38 #include <WebKitSystemInterface/WebKitSystemInterface.h>
39 #endif
40 #if USE(CFURLCONNECTION)
41 #include "Cookie.h"
42 #include "CookieRequestHeaderFieldProxy.h"
43 #include "CookiesStrategy.h"
44 #include "NotImplemented.h"
45 #include "URL.h"
46 #include <CFNetwork/CFHTTPCookiesPriv.h>
47 #include <CoreFoundation/CoreFoundation.h>
48 #include <pal/spi/cf/CFNetworkSPI.h>
49 #include <windows.h>
50 #include <wtf/SoftLinking.h>
51 #include <wtf/cf/TypeCastsCF.h>
52 #include <wtf/text/WTFString.h>
53
54 enum {
55     CFHTTPCookieStorageAcceptPolicyExclusivelyFromMainDocumentDomain = 3
56 };
57
58 namespace WTF {
59
60 #define DECLARE_CF_TYPE_TRAIT(ClassName) \
61 template <> \
62 struct CFTypeTrait<ClassName##Ref> { \
63 static inline CFTypeID typeID() { return ClassName##GetTypeID(); } \
64 };
65
66 #if COMPILER(CLANG)
67 ALLOW_DEPRECATED_DECLARATIONS_BEGIN
68 #endif
69 DECLARE_CF_TYPE_TRAIT(CFHTTPCookie);
70 #if COMPILER(CLANG)
71 ALLOW_DEPRECATED_DECLARATIONS_END
72 #endif
73
74 #undef DECLARE_CF_TYPE_TRAIT
75 } // namespace WTF
76
77 #endif
78
79 namespace WebCore {
80
81 static bool storageAccessAPIEnabled;
82
83 static RetainPtr<CFURLStorageSessionRef> createCFStorageSessionForIdentifier(CFStringRef identifier)
84 {
85     auto storageSession = adoptCF(_CFURLStorageSessionCreate(kCFAllocatorDefault, identifier, nullptr));
86
87     if (!storageSession)
88         return nullptr;
89
90     auto cache = adoptCF(_CFURLStorageSessionCopyCache(kCFAllocatorDefault, storageSession.get()));
91     if (!cache)
92         return nullptr;
93
94     CFURLCacheSetDiskCapacity(cache.get(), 0);
95
96     auto sharedCache = adoptCF(CFURLCacheCopySharedURLCache());
97     CFURLCacheSetMemoryCapacity(cache.get(), CFURLCacheMemoryCapacity(sharedCache.get()));
98
99     if (!NetworkStorageSession::processMayUseCookieAPI())
100         return storageSession;
101
102     ASSERT(hasProcessPrivilege(ProcessPrivilege::CanAccessRawCookies));
103
104     auto cookieStorage = adoptCF(_CFURLStorageSessionCopyCookieStorage(kCFAllocatorDefault, storageSession.get()));
105     if (!cookieStorage)
106         return nullptr;
107
108     auto sharedCookieStorage = _CFHTTPCookieStorageGetDefault(kCFAllocatorDefault);
109     auto sharedPolicy = CFHTTPCookieStorageGetCookieAcceptPolicy(sharedCookieStorage);
110     CFHTTPCookieStorageSetCookieAcceptPolicy(cookieStorage.get(), sharedPolicy);
111
112     return storageSession;
113 }
114
115 NetworkStorageSession::NetworkStorageSession(PAL::SessionID sessionID, RetainPtr<CFURLStorageSessionRef>&& platformSession, RetainPtr<CFHTTPCookieStorageRef>&& platformCookieStorage)
116     : m_sessionID(sessionID)
117     , m_platformSession(WTFMove(platformSession))
118 {
119     ASSERT(processMayUseCookieAPI() || !platformCookieStorage);
120     m_platformCookieStorage = platformCookieStorage ? WTFMove(platformCookieStorage) : cookieStorage();
121 }
122
123 NetworkStorageSession::NetworkStorageSession(PAL::SessionID sessionID)
124     : m_sessionID(sessionID)
125 {
126 }
127
128
129 static std::unique_ptr<NetworkStorageSession>& defaultNetworkStorageSession()
130 {
131     ASSERT(isMainThread());
132     static NeverDestroyed<std::unique_ptr<NetworkStorageSession>> session;
133     return session;
134 }
135
136 void NetworkStorageSession::switchToNewTestingSession()
137 {
138     // Session name should be short enough for shared memory region name to be under the limit, otehrwise sandbox rules won't work (see <rdar://problem/13642852>).
139     String sessionName = String::format("WebKit Test-%u", static_cast<uint32_t>(getCurrentProcessID()));
140
141     RetainPtr<CFURLStorageSessionRef> session;
142 #if PLATFORM(COCOA)
143     session = adoptCF(createPrivateStorageSession(sessionName.createCFString().get()));
144 #else
145     session = adoptCF(wkCreatePrivateStorageSession(sessionName.createCFString().get(), defaultStorageSession().platformSession()));
146 #endif
147
148     RetainPtr<CFHTTPCookieStorageRef> cookieStorage;
149     if (NetworkStorageSession::processMayUseCookieAPI()) {
150         ASSERT(hasProcessPrivilege(ProcessPrivilege::CanAccessRawCookies));
151         if (session)
152             cookieStorage = adoptCF(_CFURLStorageSessionCopyCookieStorage(kCFAllocatorDefault, session.get()));
153     }
154
155     defaultNetworkStorageSession() = std::make_unique<NetworkStorageSession>(PAL::SessionID::defaultSessionID(), WTFMove(session), WTFMove(cookieStorage));
156 }
157
158 NetworkStorageSession& NetworkStorageSession::defaultStorageSession()
159 {
160     if (!defaultNetworkStorageSession())
161         defaultNetworkStorageSession() = std::make_unique<NetworkStorageSession>(PAL::SessionID::defaultSessionID());
162     return *defaultNetworkStorageSession();
163 }
164
165 void NetworkStorageSession::ensureSession(PAL::SessionID sessionID, const String& identifierBase, RetainPtr<CFHTTPCookieStorageRef>&& cookieStorage)
166 {
167     auto addResult = globalSessionMap().add(sessionID, nullptr);
168     if (!addResult.isNewEntry)
169         return;
170
171     RetainPtr<CFStringRef> cfIdentifier = String(identifierBase + ".PrivateBrowsing").createCFString();
172
173     RetainPtr<CFURLStorageSessionRef> storageSession;
174     if (sessionID.isEphemeral()) {
175 #if PLATFORM(COCOA)
176         storageSession = adoptCF(createPrivateStorageSession(cfIdentifier.get()));
177 #else
178         storageSession = adoptCF(wkCreatePrivateStorageSession(cfIdentifier.get(), defaultNetworkStorageSession()->platformSession()));
179 #endif
180     } else
181         storageSession = createCFStorageSessionForIdentifier(cfIdentifier.get());
182
183     if (NetworkStorageSession::processMayUseCookieAPI()) {
184         ASSERT(hasProcessPrivilege(ProcessPrivilege::CanAccessRawCookies));
185         if (!cookieStorage && storageSession)
186             cookieStorage = adoptCF(_CFURLStorageSessionCopyCookieStorage(kCFAllocatorDefault, storageSession.get()));
187     }
188
189     addResult.iterator->value = std::make_unique<NetworkStorageSession>(sessionID, WTFMove(storageSession), WTFMove(cookieStorage));
190 }
191
192 void NetworkStorageSession::ensureSession(PAL::SessionID sessionID, const String& identifierBase)
193 {
194     ensureSession(sessionID, identifierBase, nullptr);
195 }
196
197 RetainPtr<CFHTTPCookieStorageRef> NetworkStorageSession::cookieStorage() const
198 {
199     if (!processMayUseCookieAPI())
200         return nullptr;
201
202     ASSERT(hasProcessPrivilege(ProcessPrivilege::CanAccessRawCookies));
203
204     if (m_platformCookieStorage)
205         return m_platformCookieStorage;
206
207     if (m_platformSession)
208         return adoptCF(_CFURLStorageSessionCopyCookieStorage(kCFAllocatorDefault, m_platformSession.get()));
209
210 #if USE(CFURLCONNECTION)
211     return _CFHTTPCookieStorageGetDefault(kCFAllocatorDefault);
212 #else
213     // When using NSURLConnection, we also use its shared cookie storage.
214     return nullptr;
215 #endif
216 }
217
218 void NetworkStorageSession::setStorageAccessAPIEnabled(bool enabled)
219 {
220     storageAccessAPIEnabled = enabled;
221 }
222
223 #if ENABLE(RESOURCE_LOAD_STATISTICS)
224
225 static inline String getPartitioningDomain(const URL& url)
226 {
227 #if ENABLE(PUBLIC_SUFFIX_LIST)
228     auto domain = topPrivatelyControlledDomain(url.host().toString());
229     if (domain.isEmpty())
230         domain = url.host().toString();
231 #else
232     auto domain = url.host().toString();
233 #endif
234     return domain;
235 }
236
237 bool NetworkStorageSession::shouldBlockThirdPartyCookies(const String& topPrivatelyControlledDomain) const
238 {
239     if (topPrivatelyControlledDomain.isEmpty())
240         return false;
241
242     return m_topPrivatelyControlledDomainsToBlock.contains(topPrivatelyControlledDomain);
243 }
244
245 bool NetworkStorageSession::shouldBlockCookies(const ResourceRequest& request, std::optional<uint64_t> frameID, std::optional<uint64_t> pageID) const
246 {
247     return shouldBlockCookies(request.firstPartyForCookies(), request.url(), frameID, pageID);
248 }
249     
250 bool NetworkStorageSession::shouldBlockCookies(const URL& firstPartyForCookies, const URL& resource, std::optional<uint64_t> frameID, std::optional<uint64_t> pageID) const
251 {
252     auto firstPartyDomain = getPartitioningDomain(firstPartyForCookies);
253     if (firstPartyDomain.isEmpty())
254         return false;
255
256     auto resourceDomain = getPartitioningDomain(resource);
257     if (resourceDomain.isEmpty())
258         return false;
259
260     if (firstPartyDomain == resourceDomain)
261         return false;
262
263     if (pageID && hasStorageAccess(resourceDomain, firstPartyDomain, frameID, pageID.value()))
264         return false;
265
266     return shouldBlockThirdPartyCookies(resourceDomain);
267 }
268
269 std::optional<Seconds> NetworkStorageSession::maxAgeCacheCap(const ResourceRequest& request)
270 {
271     if (m_cacheMaxAgeCapForPrevalentResources && shouldBlockCookies(request, std::nullopt, std::nullopt))
272         return m_cacheMaxAgeCapForPrevalentResources;
273     return std::nullopt;
274 }
275
276 void NetworkStorageSession::setShouldCapLifetimeForClientSideCookies(bool value)
277 {
278     m_shouldCapLifetimeForClientSideCookies = value;
279 }
280
281 void NetworkStorageSession::setPrevalentDomainsToBlockCookiesFor(const Vector<String>& domains)
282 {
283     m_topPrivatelyControlledDomainsToBlock.clear();
284     m_topPrivatelyControlledDomainsToBlock.add(domains.begin(), domains.end());
285 }
286
287 void NetworkStorageSession::removePrevalentDomains(const Vector<String>& domains)
288 {
289     for (auto& domain : domains)
290         m_topPrivatelyControlledDomainsToBlock.remove(domain);
291 }
292
293 bool NetworkStorageSession::hasStorageAccess(const String& resourceDomain, const String& firstPartyDomain, std::optional<uint64_t> frameID, uint64_t pageID) const
294 {
295     if (frameID) {
296         auto framesGrantedIterator = m_framesGrantedStorageAccess.find(pageID);
297         if (framesGrantedIterator != m_framesGrantedStorageAccess.end()) {
298             auto it = framesGrantedIterator->value.find(frameID.value());
299             if (it != framesGrantedIterator->value.end() && it->value == resourceDomain)
300                 return true;
301         }
302     }
303
304     if (!firstPartyDomain.isEmpty()) {
305         auto pagesGrantedIterator = m_pagesGrantedStorageAccess.find(pageID);
306         if (pagesGrantedIterator != m_pagesGrantedStorageAccess.end()) {
307             auto it = pagesGrantedIterator->value.find(firstPartyDomain);
308             if (it != pagesGrantedIterator->value.end() && it->value == resourceDomain)
309                 return true;
310         }
311     }
312
313     return false;
314 }
315
316 Vector<String> NetworkStorageSession::getAllStorageAccessEntries() const
317 {
318     Vector<String> entries;
319     for (auto& innerMap : m_framesGrantedStorageAccess.values()) {
320         for (auto& value : innerMap.values())
321             entries.append(value);
322     }
323     return entries;
324 }
325     
326 void NetworkStorageSession::grantStorageAccess(const String& resourceDomain, const String& firstPartyDomain, std::optional<uint64_t> frameID, uint64_t pageID)
327 {
328     if (!frameID) {
329         if (firstPartyDomain.isEmpty())
330             return;
331         auto pagesGrantedIterator = m_pagesGrantedStorageAccess.find(pageID);
332         if (pagesGrantedIterator == m_pagesGrantedStorageAccess.end()) {
333             HashMap<String, String> entry;
334             entry.add(firstPartyDomain, resourceDomain);
335             m_pagesGrantedStorageAccess.add(pageID, entry);
336         } else {
337             auto firstPartyDomainIterator = pagesGrantedIterator->value.find(firstPartyDomain);
338             if (firstPartyDomainIterator == pagesGrantedIterator->value.end())
339                 pagesGrantedIterator->value.add(firstPartyDomain, resourceDomain);
340             else
341                 firstPartyDomainIterator->value = resourceDomain;
342         }
343         return;
344     }
345
346     auto pagesGrantedIterator = m_framesGrantedStorageAccess.find(pageID);
347     if (pagesGrantedIterator == m_framesGrantedStorageAccess.end()) {
348         HashMap<uint64_t, String, DefaultHash<uint64_t>::Hash, WTF::UnsignedWithZeroKeyHashTraits<uint64_t>> entry;
349         entry.add(frameID.value(), resourceDomain);
350         m_framesGrantedStorageAccess.add(pageID, entry);
351     } else {
352         auto framesGrantedIterator = pagesGrantedIterator->value.find(frameID.value());
353         if (framesGrantedIterator == pagesGrantedIterator->value.end())
354             pagesGrantedIterator->value.add(frameID.value(), resourceDomain);
355         else
356             framesGrantedIterator->value = resourceDomain;
357     }
358 }
359
360 void NetworkStorageSession::removeStorageAccessForFrame(uint64_t frameID, uint64_t pageID)
361 {
362     auto iteration = m_framesGrantedStorageAccess.find(pageID);
363     if (iteration == m_framesGrantedStorageAccess.end())
364         return;
365
366     iteration->value.remove(frameID);
367 }
368
369 void NetworkStorageSession::removeStorageAccessForAllFramesOnPage(uint64_t pageID)
370 {
371     m_pagesGrantedStorageAccess.remove(pageID);
372     m_framesGrantedStorageAccess.remove(pageID);
373 }
374
375 void NetworkStorageSession::removeAllStorageAccess()
376 {
377     m_pagesGrantedStorageAccess.clear();
378     m_framesGrantedStorageAccess.clear();
379 }
380
381 void NetworkStorageSession::setCacheMaxAgeCapForPrevalentResources(Seconds seconds)
382 {
383     m_cacheMaxAgeCapForPrevalentResources = seconds;
384 }
385     
386 void NetworkStorageSession::resetCacheMaxAgeCapForPrevalentResources()
387 {
388     m_cacheMaxAgeCapForPrevalentResources = std::nullopt;
389 }
390 #endif //  ENABLE(RESOURCE_LOAD_STATISTICS)
391
392 #if !PLATFORM(COCOA)
393 void NetworkStorageSession::setCookies(const Vector<Cookie>&, const URL&, const URL&)
394 {
395     // FIXME: Implement this. <https://webkit.org/b/156298>
396 }
397 #endif
398
399 } // namespace WebCore
400
401 #if USE(CFURLCONNECTION)
402
403 namespace WebCore {
404
405 static const CFStringRef s_setCookieKeyCF = CFSTR("Set-Cookie");
406 static const CFStringRef s_cookieCF = CFSTR("Cookie");
407 static const CFStringRef s_createdCF = CFSTR("Created");
408
409 static inline RetainPtr<CFStringRef> cookieDomain(CFHTTPCookieRef cookie)
410 {
411     return adoptCF(CFHTTPCookieCopyDomain(cookie));
412 }
413
414 static double canonicalCookieTime(double time)
415 {
416     if (!time)
417         return time;
418
419     return (time + kCFAbsoluteTimeIntervalSince1970) * 1000;
420 }
421
422 static double cookieCreatedTime(CFHTTPCookieRef cookie)
423 {
424     RetainPtr<CFDictionaryRef> props = adoptCF(CFHTTPCookieCopyProperties(cookie));
425     auto value = CFDictionaryGetValue(props.get(), s_createdCF);
426
427     auto asNumber = dynamic_cf_cast<CFNumberRef>(value);
428     if (asNumber) {
429         double asDouble;
430         if (CFNumberGetValue(asNumber, kCFNumberFloat64Type, &asDouble))
431             return canonicalCookieTime(asDouble);
432         return 0.0;
433     }
434
435     auto asString = dynamic_cf_cast<CFStringRef>(value);
436     if (asString)
437         return canonicalCookieTime(CFStringGetDoubleValue(asString));
438
439     return 0.0;
440 }
441
442 static inline CFAbsoluteTime cookieExpirationTime(CFHTTPCookieRef cookie)
443 {
444     return canonicalCookieTime(CFHTTPCookieGetExpirationTime(cookie));
445 }
446
447 static inline RetainPtr<CFStringRef> cookieName(CFHTTPCookieRef cookie)
448 {
449     return adoptCF(CFHTTPCookieCopyName(cookie));
450 }
451
452 static inline RetainPtr<CFStringRef> cookiePath(CFHTTPCookieRef cookie)
453 {
454     return adoptCF(CFHTTPCookieCopyPath(cookie));
455 }
456
457 static inline RetainPtr<CFStringRef> cookieValue(CFHTTPCookieRef cookie)
458 {
459     return adoptCF(CFHTTPCookieCopyValue(cookie));
460 }
461
462 static RetainPtr<CFArrayRef> filterCookies(CFArrayRef unfilteredCookies)
463 {
464     ASSERT(unfilteredCookies);
465     CFIndex count = CFArrayGetCount(unfilteredCookies);
466     RetainPtr<CFMutableArrayRef> filteredCookies = adoptCF(CFArrayCreateMutable(0, count, &kCFTypeArrayCallBacks));
467     for (CFIndex i = 0; i < count; ++i) {
468         CFHTTPCookieRef cookie = (CFHTTPCookieRef)CFArrayGetValueAtIndex(unfilteredCookies, i);
469
470         // <rdar://problem/5632883> CFHTTPCookieStorage would store an empty cookie,
471         // which would be sent as "Cookie: =". We have a workaround in setCookies() to prevent
472         // that, but we also need to avoid sending cookies that were previously stored, and
473         // there's no harm to doing this check because such a cookie is never valid.
474         if (!CFStringGetLength(cookieName(cookie).get()))
475             continue;
476
477         if (CFHTTPCookieIsHTTPOnly(cookie))
478             continue;
479
480         CFArrayAppendValue(filteredCookies.get(), cookie);
481     }
482     return filteredCookies;
483 }
484
485 static RetainPtr<CFArrayRef> copyCookiesForURLWithFirstPartyURL(const NetworkStorageSession& session, const URL& firstParty, const URL& url, IncludeSecureCookies includeSecureCookies)
486 {
487     bool secure = includeSecureCookies == IncludeSecureCookies::Yes;
488
489     ASSERT(!secure || (secure && url.protocolIs("https")));
490
491     UNUSED_PARAM(firstParty);
492     return adoptCF(CFHTTPCookieStorageCopyCookiesForURL(session.cookieStorage().get(), url.createCFURL().get(), secure));
493 }
494
495 static CFArrayRef createCookies(CFDictionaryRef headerFields, CFURLRef url)
496 {
497     CFArrayRef parsedCookies = CFHTTPCookieCreateWithResponseHeaderFields(kCFAllocatorDefault, headerFields, url);
498     if (!parsedCookies)
499         parsedCookies = CFArrayCreate(kCFAllocatorDefault, 0, 0, &kCFTypeArrayCallBacks);
500
501     return parsedCookies;
502 }
503
504 void NetworkStorageSession::setCookiesFromDOM(const URL& firstParty, const SameSiteInfo&, const URL& url, std::optional<uint64_t> frameID, std::optional<uint64_t> pageID, const String& value) const
505 {
506     UNUSED_PARAM(frameID);
507     UNUSED_PARAM(pageID);
508     // <rdar://problem/5632883> CFHTTPCookieStorage stores an empty cookie, which would be sent as "Cookie: =".
509     if (value.isEmpty())
510         return;
511
512     RetainPtr<CFURLRef> urlCF = url.createCFURL();
513     RetainPtr<CFURLRef> firstPartyForCookiesCF = firstParty.createCFURL();
514
515     // <http://bugs.webkit.org/show_bug.cgi?id=6531>, <rdar://4409034>
516     // cookiesWithResponseHeaderFields doesn't parse cookies without a value
517     String cookieString = value.contains('=') ? value : value + "=";
518
519     RetainPtr<CFStringRef> cookieStringCF = cookieString.createCFString();
520     auto cookieStringCFPtr = cookieStringCF.get();
521     RetainPtr<CFDictionaryRef> headerFieldsCF = adoptCF(CFDictionaryCreate(kCFAllocatorDefault,
522         (const void**)&s_setCookieKeyCF, (const void**)&cookieStringCFPtr, 1,
523         &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks));
524
525     RetainPtr<CFArrayRef> unfilteredCookies = adoptCF(createCookies(headerFieldsCF.get(), urlCF.get()));
526     CFHTTPCookieStorageSetCookies(cookieStorage().get(), filterCookies(unfilteredCookies.get()).get(), urlCF.get(), firstPartyForCookiesCF.get());
527 }
528
529 static bool containsSecureCookies(CFArrayRef cookies)
530 {
531     CFIndex cookieCount = CFArrayGetCount(cookies);
532     while (cookieCount--) {
533         if (CFHTTPCookieIsSecure(checked_cf_cast<CFHTTPCookieRef>(CFArrayGetValueAtIndex(cookies, cookieCount))))
534             return true;
535     }
536
537     return false;
538 }
539
540 std::pair<String, bool> NetworkStorageSession::cookiesForDOM(const URL& firstParty, const SameSiteInfo&, const URL& url, std::optional<uint64_t> frameID, std::optional<uint64_t> pageID, IncludeSecureCookies includeSecureCookies) const
541 {
542     UNUSED_PARAM(frameID);
543     UNUSED_PARAM(pageID);
544     RetainPtr<CFArrayRef> cookiesCF = copyCookiesForURLWithFirstPartyURL(*this, firstParty, url, includeSecureCookies);
545
546     auto filteredCookies = filterCookies(cookiesCF.get());
547
548     bool didAccessSecureCookies = containsSecureCookies(filteredCookies.get());
549
550     RetainPtr<CFDictionaryRef> headerCF = adoptCF(CFHTTPCookieCopyRequestHeaderFields(kCFAllocatorDefault, filteredCookies.get()));
551     String cookieString = checked_cf_cast<CFStringRef>(CFDictionaryGetValue(headerCF.get(), s_cookieCF));
552     return { cookieString, didAccessSecureCookies };
553 }
554
555 std::pair<String, bool> NetworkStorageSession::cookieRequestHeaderFieldValue(const URL& firstParty, const SameSiteInfo&, const URL& url, std::optional<uint64_t> frameID, std::optional<uint64_t> pageID, IncludeSecureCookies includeSecureCookies) const
556 {
557     UNUSED_PARAM(frameID);
558     UNUSED_PARAM(pageID);
559     RetainPtr<CFArrayRef> cookiesCF = copyCookiesForURLWithFirstPartyURL(*this, firstParty, url, includeSecureCookies);
560
561     bool didAccessSecureCookies = containsSecureCookies(cookiesCF.get());
562
563     RetainPtr<CFDictionaryRef> headerCF = adoptCF(CFHTTPCookieCopyRequestHeaderFields(kCFAllocatorDefault, cookiesCF.get()));
564     String cookieString = checked_cf_cast<CFStringRef>(CFDictionaryGetValue(headerCF.get(), s_cookieCF));
565     return { cookieString, didAccessSecureCookies };
566 }
567
568 std::pair<String, bool> NetworkStorageSession::cookieRequestHeaderFieldValue(const CookieRequestHeaderFieldProxy& headerFieldProxy) const
569 {
570     return cookieRequestHeaderFieldValue(headerFieldProxy.firstParty, headerFieldProxy.sameSiteInfo, headerFieldProxy.url, headerFieldProxy.frameID, headerFieldProxy.pageID, headerFieldProxy.includeSecureCookies);
571 }
572
573 bool NetworkStorageSession::cookiesEnabled() const
574 {
575     CFHTTPCookieStorageAcceptPolicy policy = CFHTTPCookieStorageGetCookieAcceptPolicy(cookieStorage().get());
576     return policy == CFHTTPCookieStorageAcceptPolicyOnlyFromMainDocumentDomain || policy == CFHTTPCookieStorageAcceptPolicyExclusivelyFromMainDocumentDomain || policy == CFHTTPCookieStorageAcceptPolicyAlways;
577 }
578
579 bool NetworkStorageSession::getRawCookies(const URL& firstParty, const SameSiteInfo&, const URL& url, std::optional<uint64_t> frameID, std::optional<uint64_t> pageID, Vector<Cookie>& rawCookies) const
580 {
581     UNUSED_PARAM(frameID);
582     UNUSED_PARAM(pageID);
583     rawCookies.clear();
584
585     auto includeSecureCookies = url.protocolIs("https") ? IncludeSecureCookies::Yes : IncludeSecureCookies::No;
586
587     RetainPtr<CFArrayRef> cookiesCF = copyCookiesForURLWithFirstPartyURL(*this, firstParty, url, includeSecureCookies);
588
589     CFIndex count = CFArrayGetCount(cookiesCF.get());
590     rawCookies.reserveCapacity(count);
591
592     for (CFIndex i = 0; i < count; i++) {
593         CFHTTPCookieRef cfCookie = checked_cf_cast<CFHTTPCookieRef>(CFArrayGetValueAtIndex(cookiesCF.get(), i));
594         Cookie cookie;
595         cookie.name = cookieName(cfCookie).get();
596         cookie.value = cookieValue(cfCookie).get();
597         cookie.domain = cookieDomain(cfCookie).get();
598         cookie.path = cookiePath(cfCookie).get();
599         cookie.created = cookieCreatedTime(cfCookie);
600         cookie.expires = cookieExpirationTime(cfCookie);
601         cookie.httpOnly = CFHTTPCookieIsHTTPOnly(cfCookie);
602         cookie.secure = CFHTTPCookieIsSecure(cfCookie);
603         cookie.session = false; // FIXME: Need API for if a cookie is a session cookie.
604         rawCookies.uncheckedAppend(WTFMove(cookie));
605     }
606
607     return true;
608 }
609
610 void NetworkStorageSession::deleteCookie(const URL& url, const String& name) const
611 {
612     RetainPtr<CFHTTPCookieStorageRef> cookieStorage = this->cookieStorage();
613
614     RetainPtr<CFURLRef> urlCF = url.createCFURL();
615
616     bool sendSecureCookies = url.protocolIs("https");
617     RetainPtr<CFArrayRef> cookiesCF = adoptCF(CFHTTPCookieStorageCopyCookiesForURL(cookieStorage.get(), urlCF.get(), sendSecureCookies));
618
619     CFIndex count = CFArrayGetCount(cookiesCF.get());
620     for (CFIndex i = 0; i < count; i++) {
621         CFHTTPCookieRef cookie = checked_cf_cast<CFHTTPCookieRef>(CFArrayGetValueAtIndex(cookiesCF.get(), i));
622         if (String(cookieName(cookie).get()) == name) {
623             CFHTTPCookieStorageDeleteCookie(cookieStorage.get(), cookie);
624             break;
625         }
626     }
627 }
628
629 void NetworkStorageSession::getHostnamesWithCookies(HashSet<String>& hostnames)
630 {
631     RetainPtr<CFArrayRef> cookiesCF = adoptCF(CFHTTPCookieStorageCopyCookies(cookieStorage().get()));
632     if (!cookiesCF)
633         return;
634
635     CFIndex count = CFArrayGetCount(cookiesCF.get());
636     for (CFIndex i = 0; i < count; ++i) {
637         CFHTTPCookieRef cookie = checked_cf_cast<CFHTTPCookieRef>(CFArrayGetValueAtIndex(cookiesCF.get(), i));
638         RetainPtr<CFStringRef> domain = cookieDomain(cookie);
639         hostnames.add(domain.get());
640     }
641 }
642
643 void NetworkStorageSession::deleteAllCookies()
644 {
645     CFHTTPCookieStorageDeleteAllCookies(cookieStorage().get());
646 }
647
648 void NetworkStorageSession::deleteCookiesForHostnames(const Vector<String>& hostnames)
649 {
650 }
651
652 void NetworkStorageSession::deleteAllCookiesModifiedSince(WallTime)
653 {
654 }
655
656 } // namespace WebCore
657
658 #endif // USE(CFURLCONNECTION)