Unreviewed, rolling out r234489.
[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 #pragma clang diagnostic push
68 #pragma clang diagnostic ignored "-Wdeprecated-declarations"
69 #endif
70 DECLARE_CF_TYPE_TRAIT(CFHTTPCookie);
71 #if COMPILER(CLANG)
72 #pragma clang diagnostic pop
73 #endif
74
75 #undef DECLARE_CF_TYPE_TRAIT
76 } // namespace WTF
77
78 #endif
79
80 namespace WebCore {
81
82 static bool storageAccessAPIEnabled;
83
84 static RetainPtr<CFURLStorageSessionRef> createCFStorageSessionForIdentifier(CFStringRef identifier)
85 {
86     auto storageSession = adoptCF(_CFURLStorageSessionCreate(kCFAllocatorDefault, identifier, nullptr));
87
88     if (!storageSession)
89         return nullptr;
90
91     auto cache = adoptCF(_CFURLStorageSessionCopyCache(kCFAllocatorDefault, storageSession.get()));
92     if (!cache)
93         return nullptr;
94
95     CFURLCacheSetDiskCapacity(cache.get(), 0);
96
97     auto sharedCache = adoptCF(CFURLCacheCopySharedURLCache());
98     CFURLCacheSetMemoryCapacity(cache.get(), CFURLCacheMemoryCapacity(sharedCache.get()));
99
100     if (!NetworkStorageSession::processMayUseCookieAPI())
101         return storageSession;
102
103     ASSERT(hasProcessPrivilege(ProcessPrivilege::CanAccessRawCookies));
104
105     auto cookieStorage = adoptCF(_CFURLStorageSessionCopyCookieStorage(kCFAllocatorDefault, storageSession.get()));
106     if (!cookieStorage)
107         return nullptr;
108
109     auto sharedCookieStorage = _CFHTTPCookieStorageGetDefault(kCFAllocatorDefault);
110     auto sharedPolicy = CFHTTPCookieStorageGetCookieAcceptPolicy(sharedCookieStorage);
111     CFHTTPCookieStorageSetCookieAcceptPolicy(cookieStorage.get(), sharedPolicy);
112
113     return storageSession;
114 }
115
116 NetworkStorageSession::NetworkStorageSession(PAL::SessionID sessionID, RetainPtr<CFURLStorageSessionRef>&& platformSession, RetainPtr<CFHTTPCookieStorageRef>&& platformCookieStorage)
117     : m_sessionID(sessionID)
118     , m_platformSession(WTFMove(platformSession))
119 {
120     ASSERT(processMayUseCookieAPI() || !platformCookieStorage);
121     m_platformCookieStorage = platformCookieStorage ? WTFMove(platformCookieStorage) : cookieStorage();
122 }
123
124 NetworkStorageSession::NetworkStorageSession(PAL::SessionID sessionID)
125     : m_sessionID(sessionID)
126 {
127 }
128
129
130 static std::unique_ptr<NetworkStorageSession>& defaultNetworkStorageSession()
131 {
132     ASSERT(isMainThread());
133     static NeverDestroyed<std::unique_ptr<NetworkStorageSession>> session;
134     return session;
135 }
136
137 void NetworkStorageSession::switchToNewTestingSession()
138 {
139     // 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>).
140     String sessionName = String::format("WebKit Test-%u", static_cast<uint32_t>(getCurrentProcessID()));
141
142     RetainPtr<CFURLStorageSessionRef> session;
143 #if PLATFORM(COCOA)
144     session = adoptCF(createPrivateStorageSession(sessionName.createCFString().get()));
145 #else
146     session = adoptCF(wkCreatePrivateStorageSession(sessionName.createCFString().get(), defaultStorageSession().platformSession()));
147 #endif
148
149     RetainPtr<CFHTTPCookieStorageRef> cookieStorage;
150     if (NetworkStorageSession::processMayUseCookieAPI()) {
151         ASSERT(hasProcessPrivilege(ProcessPrivilege::CanAccessRawCookies));
152         if (session)
153             cookieStorage = adoptCF(_CFURLStorageSessionCopyCookieStorage(kCFAllocatorDefault, session.get()));
154     }
155
156     defaultNetworkStorageSession() = std::make_unique<NetworkStorageSession>(PAL::SessionID::defaultSessionID(), WTFMove(session), WTFMove(cookieStorage));
157 }
158
159 NetworkStorageSession& NetworkStorageSession::defaultStorageSession()
160 {
161     if (!defaultNetworkStorageSession())
162         defaultNetworkStorageSession() = std::make_unique<NetworkStorageSession>(PAL::SessionID::defaultSessionID());
163     return *defaultNetworkStorageSession();
164 }
165
166 void NetworkStorageSession::ensureSession(PAL::SessionID sessionID, const String& identifierBase, RetainPtr<CFHTTPCookieStorageRef>&& cookieStorage)
167 {
168     auto addResult = globalSessionMap().add(sessionID, nullptr);
169     if (!addResult.isNewEntry)
170         return;
171
172     RetainPtr<CFStringRef> cfIdentifier = String(identifierBase + ".PrivateBrowsing").createCFString();
173
174     RetainPtr<CFURLStorageSessionRef> storageSession;
175     if (sessionID.isEphemeral()) {
176 #if PLATFORM(COCOA)
177         storageSession = adoptCF(createPrivateStorageSession(cfIdentifier.get()));
178 #else
179         storageSession = adoptCF(wkCreatePrivateStorageSession(cfIdentifier.get(), defaultNetworkStorageSession()->platformSession()));
180 #endif
181     } else
182         storageSession = createCFStorageSessionForIdentifier(cfIdentifier.get());
183
184     if (NetworkStorageSession::processMayUseCookieAPI()) {
185         ASSERT(hasProcessPrivilege(ProcessPrivilege::CanAccessRawCookies));
186         if (!cookieStorage && storageSession)
187             cookieStorage = adoptCF(_CFURLStorageSessionCopyCookieStorage(kCFAllocatorDefault, storageSession.get()));
188     }
189
190     addResult.iterator->value = std::make_unique<NetworkStorageSession>(sessionID, WTFMove(storageSession), WTFMove(cookieStorage));
191 }
192
193 void NetworkStorageSession::ensureSession(PAL::SessionID sessionID, const String& identifierBase)
194 {
195     ensureSession(sessionID, identifierBase, nullptr);
196 }
197
198 RetainPtr<CFHTTPCookieStorageRef> NetworkStorageSession::cookieStorage() const
199 {
200     if (!processMayUseCookieAPI())
201         return nullptr;
202
203     ASSERT(hasProcessPrivilege(ProcessPrivilege::CanAccessRawCookies));
204
205     if (m_platformCookieStorage)
206         return m_platformCookieStorage;
207
208     if (m_platformSession)
209         return adoptCF(_CFURLStorageSessionCopyCookieStorage(kCFAllocatorDefault, m_platformSession.get()));
210
211 #if USE(CFURLCONNECTION)
212     return _CFHTTPCookieStorageGetDefault(kCFAllocatorDefault);
213 #else
214     // When using NSURLConnection, we also use its shared cookie storage.
215     return nullptr;
216 #endif
217 }
218
219 void NetworkStorageSession::setStorageAccessAPIEnabled(bool enabled)
220 {
221     storageAccessAPIEnabled = enabled;
222 }
223
224 #if HAVE(CFNETWORK_STORAGE_PARTITIONING)
225
226 static inline String getPartitioningDomain(const URL& url)
227 {
228 #if ENABLE(PUBLIC_SUFFIX_LIST)
229     auto domain = topPrivatelyControlledDomain(url.host().toString());
230     if (domain.isEmpty())
231         domain = url.host().toString();
232 #else
233     auto domain = url.host().toString();
234 #endif
235     return domain;
236 }
237
238 bool NetworkStorageSession::shouldBlockThirdPartyCookies(const String& topPrivatelyControlledDomain) const
239 {
240     if (topPrivatelyControlledDomain.isEmpty())
241         return false;
242
243     return m_topPrivatelyControlledDomainsToBlock.contains(topPrivatelyControlledDomain);
244 }
245
246 bool NetworkStorageSession::shouldBlockCookies(const ResourceRequest& request, std::optional<uint64_t> frameID, std::optional<uint64_t> pageID) const
247 {
248     return shouldBlockCookies(request.firstPartyForCookies(), request.url(), frameID, pageID);
249 }
250     
251 bool NetworkStorageSession::shouldBlockCookies(const URL& firstPartyForCookies, const URL& resource, std::optional<uint64_t> frameID, std::optional<uint64_t> pageID) const
252 {
253     auto firstPartyDomain = getPartitioningDomain(firstPartyForCookies);
254     if (firstPartyDomain.isEmpty())
255         return false;
256
257     auto resourceDomain = getPartitioningDomain(resource);
258     if (resourceDomain.isEmpty())
259         return false;
260
261     if (firstPartyDomain == resourceDomain)
262         return false;
263
264     if (pageID && hasStorageAccess(resourceDomain, firstPartyDomain, frameID, pageID.value()))
265         return false;
266
267     return shouldBlockThirdPartyCookies(resourceDomain);
268 }
269
270 void NetworkStorageSession::setPrevalentDomainsToBlockCookiesFor(const Vector<String>& domains, bool clearFirst)
271 {
272     if (clearFirst) {
273         m_topPrivatelyControlledDomainsToBlock.clear();
274         m_framesGrantedStorageAccess.clear();
275     }
276
277     m_topPrivatelyControlledDomainsToBlock.add(domains.begin(), domains.end());
278 }
279
280 void NetworkStorageSession::removePrevalentDomains(const Vector<String>& domains)
281 {
282     for (auto& domain : domains)
283         m_topPrivatelyControlledDomainsToBlock.remove(domain);
284 }
285
286 bool NetworkStorageSession::hasStorageAccess(const String& resourceDomain, const String& firstPartyDomain, std::optional<uint64_t> frameID, uint64_t pageID) const
287 {
288     if (frameID) {
289         auto framesGrantedIterator = m_framesGrantedStorageAccess.find(pageID);
290         if (framesGrantedIterator != m_framesGrantedStorageAccess.end()) {
291             auto it = framesGrantedIterator->value.find(frameID.value());
292             if (it != framesGrantedIterator->value.end() && it->value == resourceDomain)
293                 return true;
294         }
295     }
296
297     if (!firstPartyDomain.isEmpty()) {
298         auto pagesGrantedIterator = m_pagesGrantedStorageAccess.find(pageID);
299         if (pagesGrantedIterator != m_pagesGrantedStorageAccess.end()) {
300             auto it = pagesGrantedIterator->value.find(firstPartyDomain);
301             if (it != pagesGrantedIterator->value.end() && it->value == resourceDomain)
302                 return true;
303         }
304     }
305
306     return false;
307 }
308
309 Vector<String> NetworkStorageSession::getAllStorageAccessEntries() const
310 {
311     Vector<String> entries;
312     for (auto& innerMap : m_framesGrantedStorageAccess.values()) {
313         for (auto& value : innerMap.values())
314             entries.append(value);
315     }
316     return entries;
317 }
318     
319 void NetworkStorageSession::grantStorageAccess(const String& resourceDomain, const String& firstPartyDomain, std::optional<uint64_t> frameID, uint64_t pageID)
320 {
321     if (!frameID) {
322         if (firstPartyDomain.isEmpty())
323             return;
324         auto pagesGrantedIterator = m_pagesGrantedStorageAccess.find(pageID);
325         if (pagesGrantedIterator == m_pagesGrantedStorageAccess.end()) {
326             HashMap<String, String> entry;
327             entry.add(firstPartyDomain, resourceDomain);
328             m_pagesGrantedStorageAccess.add(pageID, entry);
329         } else {
330             auto firstPartyDomainIterator = pagesGrantedIterator->value.find(firstPartyDomain);
331             if (firstPartyDomainIterator == pagesGrantedIterator->value.end())
332                 pagesGrantedIterator->value.add(firstPartyDomain, resourceDomain);
333             else
334                 firstPartyDomainIterator->value = resourceDomain;
335         }
336         return;
337     }
338
339     auto pagesGrantedIterator = m_framesGrantedStorageAccess.find(pageID);
340     if (pagesGrantedIterator == m_framesGrantedStorageAccess.end()) {
341         HashMap<uint64_t, String, DefaultHash<uint64_t>::Hash, WTF::UnsignedWithZeroKeyHashTraits<uint64_t>> entry;
342         entry.add(frameID.value(), resourceDomain);
343         m_framesGrantedStorageAccess.add(pageID, entry);
344     } else {
345         auto framesGrantedIterator = pagesGrantedIterator->value.find(frameID.value());
346         if (framesGrantedIterator == pagesGrantedIterator->value.end())
347             pagesGrantedIterator->value.add(frameID.value(), resourceDomain);
348         else
349             framesGrantedIterator->value = resourceDomain;
350     }
351 }
352
353 void NetworkStorageSession::removeStorageAccessForFrame(uint64_t frameID, uint64_t pageID)
354 {
355     auto iteration = m_framesGrantedStorageAccess.find(pageID);
356     if (iteration == m_framesGrantedStorageAccess.end())
357         return;
358
359     iteration->value.remove(frameID);
360 }
361
362 void NetworkStorageSession::removeStorageAccessForAllFramesOnPage(uint64_t pageID)
363 {
364     m_pagesGrantedStorageAccess.remove(pageID);
365     m_framesGrantedStorageAccess.remove(pageID);
366 }
367
368 void NetworkStorageSession::removeAllStorageAccess()
369 {
370     m_pagesGrantedStorageAccess.clear();
371     m_framesGrantedStorageAccess.clear();
372 }
373
374 #endif // HAVE(CFNETWORK_STORAGE_PARTITIONING)
375
376 #if !PLATFORM(COCOA)
377 void NetworkStorageSession::setCookies(const Vector<Cookie>&, const URL&, const URL&)
378 {
379     // FIXME: Implement this. <https://webkit.org/b/156298>
380 }
381 #endif
382
383 } // namespace WebCore
384
385 #if USE(CFURLCONNECTION)
386
387 namespace WebCore {
388
389 static const CFStringRef s_setCookieKeyCF = CFSTR("Set-Cookie");
390 static const CFStringRef s_cookieCF = CFSTR("Cookie");
391 static const CFStringRef s_createdCF = CFSTR("Created");
392
393 static inline RetainPtr<CFStringRef> cookieDomain(CFHTTPCookieRef cookie)
394 {
395     return adoptCF(CFHTTPCookieCopyDomain(cookie));
396 }
397
398 static double canonicalCookieTime(double time)
399 {
400     if (!time)
401         return time;
402
403     return (time + kCFAbsoluteTimeIntervalSince1970) * 1000;
404 }
405
406 static double cookieCreatedTime(CFHTTPCookieRef cookie)
407 {
408     RetainPtr<CFDictionaryRef> props = adoptCF(CFHTTPCookieCopyProperties(cookie));
409     auto value = CFDictionaryGetValue(props.get(), s_createdCF);
410
411     auto asNumber = dynamic_cf_cast<CFNumberRef>(value);
412     if (asNumber) {
413         double asDouble;
414         if (CFNumberGetValue(asNumber, kCFNumberFloat64Type, &asDouble))
415             return canonicalCookieTime(asDouble);
416         return 0.0;
417     }
418
419     auto asString = dynamic_cf_cast<CFStringRef>(value);
420     if (asString)
421         return canonicalCookieTime(CFStringGetDoubleValue(asString));
422
423     return 0.0;
424 }
425
426 static inline CFAbsoluteTime cookieExpirationTime(CFHTTPCookieRef cookie)
427 {
428     return canonicalCookieTime(CFHTTPCookieGetExpirationTime(cookie));
429 }
430
431 static inline RetainPtr<CFStringRef> cookieName(CFHTTPCookieRef cookie)
432 {
433     return adoptCF(CFHTTPCookieCopyName(cookie));
434 }
435
436 static inline RetainPtr<CFStringRef> cookiePath(CFHTTPCookieRef cookie)
437 {
438     return adoptCF(CFHTTPCookieCopyPath(cookie));
439 }
440
441 static inline RetainPtr<CFStringRef> cookieValue(CFHTTPCookieRef cookie)
442 {
443     return adoptCF(CFHTTPCookieCopyValue(cookie));
444 }
445
446 static RetainPtr<CFArrayRef> filterCookies(CFArrayRef unfilteredCookies)
447 {
448     ASSERT(unfilteredCookies);
449     CFIndex count = CFArrayGetCount(unfilteredCookies);
450     RetainPtr<CFMutableArrayRef> filteredCookies = adoptCF(CFArrayCreateMutable(0, count, &kCFTypeArrayCallBacks));
451     for (CFIndex i = 0; i < count; ++i) {
452         CFHTTPCookieRef cookie = (CFHTTPCookieRef)CFArrayGetValueAtIndex(unfilteredCookies, i);
453
454         // <rdar://problem/5632883> CFHTTPCookieStorage would store an empty cookie,
455         // which would be sent as "Cookie: =". We have a workaround in setCookies() to prevent
456         // that, but we also need to avoid sending cookies that were previously stored, and
457         // there's no harm to doing this check because such a cookie is never valid.
458         if (!CFStringGetLength(cookieName(cookie).get()))
459             continue;
460
461         if (CFHTTPCookieIsHTTPOnly(cookie))
462             continue;
463
464         CFArrayAppendValue(filteredCookies.get(), cookie);
465     }
466     return filteredCookies;
467 }
468
469 static RetainPtr<CFArrayRef> copyCookiesForURLWithFirstPartyURL(const NetworkStorageSession& session, const URL& firstParty, const URL& url, IncludeSecureCookies includeSecureCookies)
470 {
471     bool secure = includeSecureCookies == IncludeSecureCookies::Yes;
472
473     ASSERT(!secure || (secure && url.protocolIs("https")));
474
475     UNUSED_PARAM(firstParty);
476     return adoptCF(CFHTTPCookieStorageCopyCookiesForURL(session.cookieStorage().get(), url.createCFURL().get(), secure));
477 }
478
479 static CFArrayRef createCookies(CFDictionaryRef headerFields, CFURLRef url)
480 {
481     CFArrayRef parsedCookies = CFHTTPCookieCreateWithResponseHeaderFields(kCFAllocatorDefault, headerFields, url);
482     if (!parsedCookies)
483         parsedCookies = CFArrayCreate(kCFAllocatorDefault, 0, 0, &kCFTypeArrayCallBacks);
484
485     return parsedCookies;
486 }
487
488 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
489 {
490     UNUSED_PARAM(frameID);
491     UNUSED_PARAM(pageID);
492     // <rdar://problem/5632883> CFHTTPCookieStorage stores an empty cookie, which would be sent as "Cookie: =".
493     if (value.isEmpty())
494         return;
495
496     RetainPtr<CFURLRef> urlCF = url.createCFURL();
497     RetainPtr<CFURLRef> firstPartyForCookiesCF = firstParty.createCFURL();
498
499     // <http://bugs.webkit.org/show_bug.cgi?id=6531>, <rdar://4409034>
500     // cookiesWithResponseHeaderFields doesn't parse cookies without a value
501     String cookieString = value.contains('=') ? value : value + "=";
502
503     RetainPtr<CFStringRef> cookieStringCF = cookieString.createCFString();
504     auto cookieStringCFPtr = cookieStringCF.get();
505     RetainPtr<CFDictionaryRef> headerFieldsCF = adoptCF(CFDictionaryCreate(kCFAllocatorDefault,
506         (const void**)&s_setCookieKeyCF, (const void**)&cookieStringCFPtr, 1,
507         &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks));
508
509     RetainPtr<CFArrayRef> unfilteredCookies = adoptCF(createCookies(headerFieldsCF.get(), urlCF.get()));
510     CFHTTPCookieStorageSetCookies(cookieStorage().get(), filterCookies(unfilteredCookies.get()).get(), urlCF.get(), firstPartyForCookiesCF.get());
511 }
512
513 static bool containsSecureCookies(CFArrayRef cookies)
514 {
515     CFIndex cookieCount = CFArrayGetCount(cookies);
516     while (cookieCount--) {
517         if (CFHTTPCookieIsSecure(checked_cf_cast<CFHTTPCookieRef>(CFArrayGetValueAtIndex(cookies, cookieCount))))
518             return true;
519     }
520
521     return false;
522 }
523
524 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
525 {
526     UNUSED_PARAM(frameID);
527     UNUSED_PARAM(pageID);
528     RetainPtr<CFArrayRef> cookiesCF = copyCookiesForURLWithFirstPartyURL(*this, firstParty, url, includeSecureCookies);
529
530     auto filteredCookies = filterCookies(cookiesCF.get());
531
532     bool didAccessSecureCookies = containsSecureCookies(filteredCookies.get());
533
534     RetainPtr<CFDictionaryRef> headerCF = adoptCF(CFHTTPCookieCopyRequestHeaderFields(kCFAllocatorDefault, filteredCookies.get()));
535     String cookieString = checked_cf_cast<CFStringRef>(CFDictionaryGetValue(headerCF.get(), s_cookieCF));
536     return { cookieString, didAccessSecureCookies };
537 }
538
539 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
540 {
541     UNUSED_PARAM(frameID);
542     UNUSED_PARAM(pageID);
543     RetainPtr<CFArrayRef> cookiesCF = copyCookiesForURLWithFirstPartyURL(*this, firstParty, url, includeSecureCookies);
544
545     bool didAccessSecureCookies = containsSecureCookies(cookiesCF.get());
546
547     RetainPtr<CFDictionaryRef> headerCF = adoptCF(CFHTTPCookieCopyRequestHeaderFields(kCFAllocatorDefault, cookiesCF.get()));
548     String cookieString = checked_cf_cast<CFStringRef>(CFDictionaryGetValue(headerCF.get(), s_cookieCF));
549     return { cookieString, didAccessSecureCookies };
550 }
551
552 std::pair<String, bool> NetworkStorageSession::cookieRequestHeaderFieldValue(const CookieRequestHeaderFieldProxy& headerFieldProxy) const
553 {
554     return cookieRequestHeaderFieldValue(headerFieldProxy.firstParty, headerFieldProxy.sameSiteInfo, headerFieldProxy.url, headerFieldProxy.frameID, headerFieldProxy.pageID, headerFieldProxy.includeSecureCookies);
555 }
556
557 bool NetworkStorageSession::cookiesEnabled() const
558 {
559     CFHTTPCookieStorageAcceptPolicy policy = CFHTTPCookieStorageGetCookieAcceptPolicy(cookieStorage().get());
560     return policy == CFHTTPCookieStorageAcceptPolicyOnlyFromMainDocumentDomain || policy == CFHTTPCookieStorageAcceptPolicyExclusivelyFromMainDocumentDomain || policy == CFHTTPCookieStorageAcceptPolicyAlways;
561 }
562
563 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
564 {
565     UNUSED_PARAM(frameID);
566     UNUSED_PARAM(pageID);
567     rawCookies.clear();
568
569     auto includeSecureCookies = url.protocolIs("https") ? IncludeSecureCookies::Yes : IncludeSecureCookies::No;
570
571     RetainPtr<CFArrayRef> cookiesCF = copyCookiesForURLWithFirstPartyURL(*this, firstParty, url, includeSecureCookies);
572
573     CFIndex count = CFArrayGetCount(cookiesCF.get());
574     rawCookies.reserveCapacity(count);
575
576     for (CFIndex i = 0; i < count; i++) {
577         CFHTTPCookieRef cfCookie = checked_cf_cast<CFHTTPCookieRef>(CFArrayGetValueAtIndex(cookiesCF.get(), i));
578         Cookie cookie;
579         cookie.name = cookieName(cfCookie).get();
580         cookie.value = cookieValue(cfCookie).get();
581         cookie.domain = cookieDomain(cfCookie).get();
582         cookie.path = cookiePath(cfCookie).get();
583         cookie.created = cookieCreatedTime(cfCookie);
584         cookie.expires = cookieExpirationTime(cfCookie);
585         cookie.httpOnly = CFHTTPCookieIsHTTPOnly(cfCookie);
586         cookie.secure = CFHTTPCookieIsSecure(cfCookie);
587         cookie.session = false; // FIXME: Need API for if a cookie is a session cookie.
588         rawCookies.uncheckedAppend(WTFMove(cookie));
589     }
590
591     return true;
592 }
593
594 void NetworkStorageSession::deleteCookie(const URL& url, const String& name) const
595 {
596     RetainPtr<CFHTTPCookieStorageRef> cookieStorage = this->cookieStorage();
597
598     RetainPtr<CFURLRef> urlCF = url.createCFURL();
599
600     bool sendSecureCookies = url.protocolIs("https");
601     RetainPtr<CFArrayRef> cookiesCF = adoptCF(CFHTTPCookieStorageCopyCookiesForURL(cookieStorage.get(), urlCF.get(), sendSecureCookies));
602
603     CFIndex count = CFArrayGetCount(cookiesCF.get());
604     for (CFIndex i = 0; i < count; i++) {
605         CFHTTPCookieRef cookie = checked_cf_cast<CFHTTPCookieRef>(CFArrayGetValueAtIndex(cookiesCF.get(), i));
606         if (String(cookieName(cookie).get()) == name) {
607             CFHTTPCookieStorageDeleteCookie(cookieStorage.get(), cookie);
608             break;
609         }
610     }
611 }
612
613 void NetworkStorageSession::getHostnamesWithCookies(HashSet<String>& hostnames)
614 {
615     RetainPtr<CFArrayRef> cookiesCF = adoptCF(CFHTTPCookieStorageCopyCookies(cookieStorage().get()));
616     if (!cookiesCF)
617         return;
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         RetainPtr<CFStringRef> domain = cookieDomain(cookie);
623         hostnames.add(domain.get());
624     }
625 }
626
627 void NetworkStorageSession::deleteAllCookies()
628 {
629     CFHTTPCookieStorageDeleteAllCookies(cookieStorage().get());
630 }
631
632 void NetworkStorageSession::deleteCookiesForHostnames(const Vector<String>& hostnames)
633 {
634 }
635
636 void NetworkStorageSession::deleteAllCookiesModifiedSince(WallTime)
637 {
638 }
639
640 } // namespace WebCore
641
642 #endif // USE(CFURLCONNECTION)