9e9a91b7250f362b85945f3d70276e6abb1add42
[WebKit.git] / Source / WebCore / platform / network / cocoa / NetworkStorageSessionCocoa.mm
1 /*
2  * Copyright (C) 2015-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. ``AS IS'' AND ANY
14  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
17  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24  */
25
26 #import "config.h"
27 #import "NetworkStorageSession.h"
28
29 #import "Cookie.h"
30 #import "CookieRequestHeaderFieldProxy.h"
31 #import "CookieStorageObserver.h"
32 #import "HTTPCookieAcceptPolicyCocoa.h"
33 #import "SameSiteInfo.h"
34 #import <pal/spi/cf/CFNetworkSPI.h>
35 #import <wtf/BlockObjCExceptions.h>
36 #import <wtf/Optional.h>
37 #import <wtf/ProcessPrivilege.h>
38 #import <wtf/URL.h>
39 #import <wtf/text/StringBuilder.h>
40
41 @interface NSURL ()
42 - (CFURLRef)_cfurl;
43 @end
44
45 namespace WebCore {
46
47 void NetworkStorageSession::setCookie(const Cookie& cookie)
48 {
49     ASSERT(hasProcessPrivilege(ProcessPrivilege::CanAccessRawCookies));
50
51     BEGIN_BLOCK_OBJC_EXCEPTIONS;
52     [nsCookieStorage() setCookie:(NSHTTPCookie *)cookie];
53     END_BLOCK_OBJC_EXCEPTIONS;
54 }
55
56 void NetworkStorageSession::setCookies(const Vector<Cookie>& cookies, const URL& url, const URL& mainDocumentURL)
57 {
58     ASSERT(hasProcessPrivilege(ProcessPrivilege::CanAccessRawCookies));
59
60     RetainPtr<NSMutableArray> nsCookies = adoptNS([[NSMutableArray alloc] initWithCapacity:cookies.size()]);
61     for (const auto& cookie : cookies)
62         [nsCookies addObject:(NSHTTPCookie *)cookie];
63
64     BEGIN_BLOCK_OBJC_EXCEPTIONS;
65     [nsCookieStorage() setCookies:nsCookies.get() forURL:(NSURL *)url mainDocumentURL:(NSURL *)mainDocumentURL];
66     END_BLOCK_OBJC_EXCEPTIONS;
67 }
68
69 void NetworkStorageSession::deleteCookie(const Cookie& cookie)
70 {
71     ASSERT(hasProcessPrivilege(ProcessPrivilege::CanAccessRawCookies));
72     [nsCookieStorage() deleteCookie:(NSHTTPCookie *)cookie];
73 }
74
75 static Vector<Cookie> nsCookiesToCookieVector(NSArray<NSHTTPCookie *> *nsCookies)
76 {
77     ASSERT(hasProcessPrivilege(ProcessPrivilege::CanAccessRawCookies));
78
79     Vector<Cookie> cookies;
80     cookies.reserveInitialCapacity(nsCookies.count);
81     for (NSHTTPCookie *nsCookie in nsCookies)
82         cookies.uncheckedAppend(nsCookie);
83
84     return cookies;
85 }
86
87 Vector<Cookie> NetworkStorageSession::getAllCookies()
88 {
89     ASSERT(hasProcessPrivilege(ProcessPrivilege::CanAccessRawCookies));
90     return nsCookiesToCookieVector(nsCookieStorage().cookies);
91 }
92
93 Vector<Cookie> NetworkStorageSession::getCookies(const URL& url)
94 {
95     ASSERT(hasProcessPrivilege(ProcessPrivilege::CanAccessRawCookies));
96     return nsCookiesToCookieVector([nsCookieStorage() cookiesForURL:(NSURL *)url]);
97 }
98
99 void NetworkStorageSession::hasCookies(const RegistrableDomain& domain, CompletionHandler<void(bool)>&& completionHandler) const
100 {
101     ASSERT(hasProcessPrivilege(ProcessPrivilege::CanAccessRawCookies));
102     
103     for (NSHTTPCookie *nsCookie in nsCookieStorage().cookies) {
104         if (RegistrableDomain::uncheckedCreateFromHost(nsCookie.domain) == domain) {
105             completionHandler(true);
106             return;
107         }
108     }
109
110     completionHandler(false);
111 }
112
113 void NetworkStorageSession::flushCookieStore()
114 {
115     ASSERT(hasProcessPrivilege(ProcessPrivilege::CanAccessRawCookies));
116     [nsCookieStorage() _saveCookies];
117 }
118
119 NSHTTPCookieStorage *NetworkStorageSession::nsCookieStorage() const
120 {
121     ASSERT(hasProcessPrivilege(ProcessPrivilege::CanAccessRawCookies));
122     auto cfCookieStorage = cookieStorage();
123     if (!cfCookieStorage || [NSHTTPCookieStorage sharedHTTPCookieStorage]._cookieStorage == cfCookieStorage)
124         return [NSHTTPCookieStorage sharedHTTPCookieStorage];
125
126     return [[[NSHTTPCookieStorage alloc] _initWithCFHTTPCookieStorage:cfCookieStorage.get()] autorelease];
127 }
128
129 CookieStorageObserver& NetworkStorageSession::cookieStorageObserver() const
130 {
131     if (!m_cookieStorageObserver)
132         m_cookieStorageObserver = makeUnique<CookieStorageObserver>(nsCookieStorage());
133
134     return *m_cookieStorageObserver;
135 }
136
137 CFURLStorageSessionRef createPrivateStorageSession(CFStringRef identifier)
138 {
139     const void* sessionPropertyKeys[] = { _kCFURLStorageSessionIsPrivate };
140     const void* sessionPropertyValues[] = { kCFBooleanTrue };
141     auto sessionProperties = adoptCF(CFDictionaryCreate(kCFAllocatorDefault, sessionPropertyKeys, sessionPropertyValues, sizeof(sessionPropertyKeys) / sizeof(*sessionPropertyKeys), &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks));
142     auto storageSession = adoptCF(_CFURLStorageSessionCreate(kCFAllocatorDefault, identifier, sessionProperties.get()));
143
144     if (!storageSession)
145         return nullptr;
146
147     // The private storage session should have the same properties as the default storage session,
148     // with the exception that it should be in-memory only storage.
149
150     // FIXME 9199649: If any of the storages do not exist, do no use the storage session.
151     // This could occur if there is an issue figuring out where to place a storage on disk (e.g. the
152     // sandbox does not allow CFNetwork access).
153
154     auto cache = adoptCF(_CFURLStorageSessionCopyCache(kCFAllocatorDefault, storageSession.get()));
155     if (!cache)
156         return nullptr;
157
158     CFURLCacheSetDiskCapacity(cache.get(), 0); // Setting disk cache size should not be necessary once <rdar://problem/12656814> is fixed.
159     CFURLCacheSetMemoryCapacity(cache.get(), [[NSURLCache sharedURLCache] memoryCapacity]);
160
161     if (!NetworkStorageSession::processMayUseCookieAPI())
162         return storageSession.leakRef();
163
164     ASSERT(hasProcessPrivilege(ProcessPrivilege::CanAccessRawCookies));
165
166     auto cookieStorage = adoptCF(_CFURLStorageSessionCopyCookieStorage(kCFAllocatorDefault, storageSession.get()));
167     if (!cookieStorage)
168         return nullptr;
169
170     // FIXME: Use _CFHTTPCookieStorageGetDefault when USE(CFNETWORK) is defined in WebKit for consistency.
171     CFHTTPCookieStorageSetCookieAcceptPolicy(cookieStorage.get(), [[NSHTTPCookieStorage sharedHTTPCookieStorage] cookieAcceptPolicy]);
172
173     return storageSession.leakRef();
174 }
175
176 static NSArray *httpCookies(CFHTTPCookieStorageRef cookieStorage)
177 {
178     ASSERT(hasProcessPrivilege(ProcessPrivilege::CanAccessRawCookies));
179     if (!cookieStorage)
180         return [[NSHTTPCookieStorage sharedHTTPCookieStorage] cookies];
181     
182     auto cookies = adoptCF(CFHTTPCookieStorageCopyCookies(cookieStorage));
183     return [NSHTTPCookie _cf2nsCookies:cookies.get()];
184 }
185
186 static void deleteHTTPCookie(CFHTTPCookieStorageRef cookieStorage, NSHTTPCookie *cookie)
187 {
188     ASSERT(hasProcessPrivilege(ProcessPrivilege::CanAccessRawCookies));
189     if (!cookieStorage) {
190         [[NSHTTPCookieStorage sharedHTTPCookieStorage] deleteCookie:cookie];
191         return;
192     }
193     
194     CFHTTPCookieStorageDeleteCookie(cookieStorage, [cookie _GetInternalCFHTTPCookie]);
195 }
196
197 #if !(PLATFORM(MAC) && __MAC_OS_X_VERSION_MIN_REQUIRED < 101400) && !PLATFORM(WATCHOS) && !PLATFORM(APPLETV)
198 static RetainPtr<NSDictionary> policyProperties(const SameSiteInfo& sameSiteInfo, NSURL *url)
199 {
200     static NSURL *emptyURL = [[NSURL alloc] initWithString:@""];
201     NSDictionary *policyProperties = @{
202         @"_kCFHTTPCookiePolicyPropertySiteForCookies": sameSiteInfo.isSameSite ? url : emptyURL,
203         @"_kCFHTTPCookiePolicyPropertyIsTopLevelNavigation": [NSNumber numberWithBool:sameSiteInfo.isTopSite],
204     };
205     return policyProperties;
206 }
207 #endif
208
209 static NSArray *cookiesForURL(NSHTTPCookieStorage *storage, NSURL *url, NSURL *mainDocumentURL, const Optional<SameSiteInfo>& sameSiteInfo, NSString *partition = nullptr)
210 {
211     // The _getCookiesForURL: method calls the completionHandler synchronously. We use Optional<> to ensure this invariant.
212     Optional<RetainPtr<NSArray>> cookiesPtr;
213     auto completionHandler = [&cookiesPtr] (NSArray *cookies) {
214         cookiesPtr = retainPtr(cookies);
215     };
216 #if !(PLATFORM(MAC) && __MAC_OS_X_VERSION_MIN_REQUIRED < 101400) && !PLATFORM(WATCHOS) && !PLATFORM(APPLETV)
217     if ([storage respondsToSelector:@selector(_getCookiesForURL:mainDocumentURL:partition:policyProperties:completionHandler:)])
218         [storage _getCookiesForURL:url mainDocumentURL:mainDocumentURL partition:partition policyProperties:sameSiteInfo ? policyProperties(sameSiteInfo.value(), url).get() : nullptr completionHandler:completionHandler];
219     else
220         [storage _getCookiesForURL:url mainDocumentURL:mainDocumentURL partition:partition completionHandler:completionHandler];
221 #else
222     [storage _getCookiesForURL:url mainDocumentURL:mainDocumentURL partition:partition completionHandler:completionHandler];
223     UNUSED_PARAM(sameSiteInfo);
224 #endif
225     ASSERT(!!cookiesPtr);
226     return cookiesPtr->autorelease();
227 }
228
229 static void setHTTPCookiesForURL(CFHTTPCookieStorageRef cookieStorage, NSArray *cookies, NSURL *url, NSURL *mainDocumentURL, const SameSiteInfo& sameSiteInfo)
230 {
231     ASSERT(hasProcessPrivilege(ProcessPrivilege::CanAccessRawCookies));
232     if (!cookieStorage) {
233 #if !(PLATFORM(MAC) && __MAC_OS_X_VERSION_MIN_REQUIRED < 101400) && !PLATFORM(WATCHOS) && !PLATFORM(APPLETV)
234         if ([NSHTTPCookieStorage instancesRespondToSelector:@selector(_setCookies:forURL:mainDocumentURL:policyProperties:)])
235             [[NSHTTPCookieStorage sharedHTTPCookieStorage] _setCookies:cookies forURL:url mainDocumentURL:mainDocumentURL policyProperties:policyProperties(sameSiteInfo, url).get()];
236         else
237 #endif
238             [[NSHTTPCookieStorage sharedHTTPCookieStorage] setCookies:cookies forURL:url mainDocumentURL:mainDocumentURL];
239         return;
240     }
241 #if !(PLATFORM(MAC) && __MAC_OS_X_VERSION_MIN_REQUIRED < 101400) && !PLATFORM(WATCHOS) && !PLATFORM(APPLETV)
242     if ([NSHTTPCookieStorage instancesRespondToSelector:@selector(_setCookies:forURL:mainDocumentURL:policyProperties:)]) {
243         // FIXME: Stop creating a new NSHTTPCookieStorage object each time we want to query the cookie jar.
244         // NetworkStorageSession could instead keep a NSHTTPCookieStorage object for us.
245         RetainPtr<NSHTTPCookieStorage> nsCookieStorage = adoptNS([[NSHTTPCookieStorage alloc] _initWithCFHTTPCookieStorage:cookieStorage]);
246         [nsCookieStorage _setCookies:cookies forURL:url mainDocumentURL:mainDocumentURL policyProperties:policyProperties(sameSiteInfo, url).get()];
247     } else {
248 #endif
249         auto cfCookies = adoptCF([NSHTTPCookie _ns2cfCookies:cookies]);
250         CFHTTPCookieStorageSetCookies(cookieStorage, cfCookies.get(), [url _cfurl], [mainDocumentURL _cfurl]);
251 #if !(PLATFORM(MAC) && __MAC_OS_X_VERSION_MIN_REQUIRED < 101400) && !PLATFORM(WATCHOS) && !PLATFORM(APPLETV)
252     }
253 #else
254     UNUSED_PARAM(sameSiteInfo);
255 #endif
256 }
257
258 static NSArray *httpCookiesForURL(CFHTTPCookieStorageRef cookieStorage, NSURL *firstParty, const Optional<SameSiteInfo>& sameSiteInfo, NSURL *url)
259 {
260     ASSERT(hasProcessPrivilege(ProcessPrivilege::CanAccessRawCookies));
261     if (!cookieStorage)
262         cookieStorage = _CFHTTPCookieStorageGetDefault(kCFAllocatorDefault);
263
264     // FIXME: Stop creating a new NSHTTPCookieStorage object each time we want to query the cookie jar.
265     // NetworkStorageSession could instead keep a NSHTTPCookieStorage object for us.
266     RetainPtr<NSHTTPCookieStorage> nsCookieStorage = adoptNS([[NSHTTPCookieStorage alloc] _initWithCFHTTPCookieStorage:cookieStorage]);
267     return cookiesForURL(nsCookieStorage.get(), url, firstParty, sameSiteInfo);
268 }
269
270 static RetainPtr<NSArray> filterCookies(NSArray *unfilteredCookies, Optional<Seconds> cappedLifetime)
271 {
272     ASSERT(hasProcessPrivilege(ProcessPrivilege::CanAccessRawCookies));
273     NSUInteger count = [unfilteredCookies count];
274     RetainPtr<NSMutableArray> filteredCookies = adoptNS([[NSMutableArray alloc] initWithCapacity:count]);
275
276     for (NSUInteger i = 0; i < count; ++i) {
277         NSHTTPCookie *cookie = (NSHTTPCookie *)[unfilteredCookies objectAtIndex:i];
278
279         // <rdar://problem/5632883> On 10.5, NSHTTPCookieStorage would store an empty cookie,
280         // which would be sent as "Cookie: =". We have a workaround in setCookies() to prevent
281         // that, but we also need to avoid sending cookies that were previously stored, and
282         // there's no harm to doing this check because such a cookie is never valid.
283         if (![[cookie name] length])
284             continue;
285
286         if ([cookie isHTTPOnly])
287             continue;
288
289         // Cap lifetime of persistent, client-side cookies to a week.
290         if (cappedLifetime && ![cookie isSessionOnly]) {
291             if (!cookie.expiresDate || cookie.expiresDate.timeIntervalSinceNow > cappedLifetime->seconds()) {
292                 RetainPtr<NSMutableDictionary<NSHTTPCookiePropertyKey, id>> properties = adoptNS([[cookie properties] mutableCopy]);
293                 RetainPtr<NSDate> dateInAWeek = adoptNS([[NSDate alloc] initWithTimeIntervalSinceNow:cappedLifetime->seconds()]);
294                 [properties setObject:dateInAWeek.get() forKey:NSHTTPCookieExpires];
295                 cookie = [NSHTTPCookie cookieWithProperties:properties.get()];
296             }
297         }
298
299         [filteredCookies.get() addObject:cookie];
300     }
301
302     return filteredCookies;
303 }
304
305 static NSArray *cookiesForURL(const NetworkStorageSession& session, const URL& firstParty, const SameSiteInfo& sameSiteInfo, const URL& url, Optional<FrameIdentifier> frameID, Optional<PageIdentifier> pageID, ShouldAskITP shouldAskITP)
306 {
307 #if ENABLE(RESOURCE_LOAD_STATISTICS)
308     if (shouldAskITP == ShouldAskITP::Yes && session.shouldBlockCookies(firstParty, url, frameID, pageID))
309         return nil;
310 #else
311     UNUSED_PARAM(frameID);
312     UNUSED_PARAM(pageID);
313     UNUSED_PARAM(shouldAskITP);
314 #endif
315     return httpCookiesForURL(session.cookieStorage().get(), firstParty, sameSiteInfo, url);
316 }
317
318 enum IncludeHTTPOnlyOrNot { DoNotIncludeHTTPOnly, IncludeHTTPOnly };
319 static std::pair<String, bool> cookiesForSession(const NetworkStorageSession& session, const URL& firstParty, const SameSiteInfo& sameSiteInfo, const URL& url, Optional<FrameIdentifier> frameID, Optional<PageIdentifier> pageID, IncludeHTTPOnlyOrNot includeHTTPOnly, IncludeSecureCookies includeSecureCookies, ShouldAskITP shouldAskITP = ShouldAskITP::Yes)
320 {
321     ASSERT(hasProcessPrivilege(ProcessPrivilege::CanAccessRawCookies));
322
323     BEGIN_BLOCK_OBJC_EXCEPTIONS;
324
325     NSArray *cookies = cookiesForURL(session, firstParty, sameSiteInfo, url, frameID, pageID, shouldAskITP);
326     if (![cookies count])
327         return { String(), false }; // Return a null string, not an empty one that StringBuilder would create below.
328
329     StringBuilder cookiesBuilder;
330     bool didAccessSecureCookies = false;
331     for (NSHTTPCookie *cookie in cookies) {
332         if (![[cookie name] length])
333             continue;
334
335         if (!includeHTTPOnly && [cookie isHTTPOnly])
336             continue;
337
338         if ([cookie isSecure]) {
339             didAccessSecureCookies = true;
340             if (includeSecureCookies == IncludeSecureCookies::No)
341                 continue;
342         }
343
344         if (!cookiesBuilder.isEmpty())
345             cookiesBuilder.appendLiteral("; ");
346
347         cookiesBuilder.append([cookie name]);
348         cookiesBuilder.append('=');
349         cookiesBuilder.append([cookie value]);
350     }
351     return { cookiesBuilder.toString(), didAccessSecureCookies };
352
353     END_BLOCK_OBJC_EXCEPTIONS;
354     return { String(), false };
355 }
356
357 static void deleteAllHTTPCookies(CFHTTPCookieStorageRef cookieStorage)
358 {
359     ASSERT(hasProcessPrivilege(ProcessPrivilege::CanAccessRawCookies));
360
361     if (!cookieStorage) {
362         NSHTTPCookieStorage *cookieStorage = [NSHTTPCookieStorage sharedHTTPCookieStorage];
363         NSArray *cookies = [cookieStorage cookies];
364         if (!cookies)
365             return;
366
367         for (NSHTTPCookie *cookie in cookies)
368             [cookieStorage deleteCookie:cookie];
369         return;
370     }
371
372     CFHTTPCookieStorageDeleteAllCookies(cookieStorage);
373 }
374
375 std::pair<String, bool> NetworkStorageSession::cookiesForDOM(const URL& firstParty, const SameSiteInfo& sameSiteInfo, const URL& url, Optional<FrameIdentifier> frameID, Optional<PageIdentifier> pageID, IncludeSecureCookies includeSecureCookies, ShouldAskITP shouldAskITP) const
376 {
377     return cookiesForSession(*this, firstParty, sameSiteInfo, url, frameID, pageID, DoNotIncludeHTTPOnly, includeSecureCookies, shouldAskITP);
378 }
379
380 std::pair<String, bool> NetworkStorageSession::cookieRequestHeaderFieldValue(const URL& firstParty, const SameSiteInfo& sameSiteInfo, const URL& url, Optional<FrameIdentifier> frameID, Optional<PageIdentifier> pageID, IncludeSecureCookies includeSecureCookies, ShouldAskITP shouldAskITP) const
381 {
382     return cookiesForSession(*this, firstParty, sameSiteInfo, url, frameID, pageID, IncludeHTTPOnly, includeSecureCookies, shouldAskITP);
383 }
384
385 std::pair<String, bool> NetworkStorageSession::cookieRequestHeaderFieldValue(const CookieRequestHeaderFieldProxy& headerFieldProxy) const
386 {
387     return cookiesForSession(*this, headerFieldProxy.firstParty, headerFieldProxy.sameSiteInfo, headerFieldProxy.url, headerFieldProxy.frameID, headerFieldProxy.pageID, IncludeHTTPOnly, headerFieldProxy.includeSecureCookies);
388 }
389
390 void NetworkStorageSession::setCookiesFromDOM(const URL& firstParty, const SameSiteInfo& sameSiteInfo, const URL& url, Optional<FrameIdentifier> frameID, Optional<PageIdentifier> pageID, ShouldAskITP shouldAskITP, const String& cookieStr) const
391 {
392     ASSERT(hasProcessPrivilege(ProcessPrivilege::CanAccessRawCookies));
393
394     BEGIN_BLOCK_OBJC_EXCEPTIONS;
395
396 #if ENABLE(RESOURCE_LOAD_STATISTICS)
397     if (shouldAskITP == ShouldAskITP::Yes && shouldBlockCookies(firstParty, url, frameID, pageID))
398         return;
399 #else
400     UNUSED_PARAM(frameID);
401     UNUSED_PARAM(pageID);
402     UNUSED_PARAM(shouldAskITP);
403 #endif
404
405     // <rdar://problem/5632883> On 10.5, NSHTTPCookieStorage would store an empty cookie,
406     // which would be sent as "Cookie: =".
407     if (cookieStr.isEmpty())
408         return;
409
410     // <http://bugs.webkit.org/show_bug.cgi?id=6531>, <rdar://4409034>
411     // cookiesWithResponseHeaderFields doesn't parse cookies without a value
412     String cookieString = cookieStr.contains('=') ? cookieStr : cookieStr + "=";
413
414     NSURL *cookieURL = url;
415     NSDictionary *headerFields = [NSDictionary dictionaryWithObject:cookieString forKey:@"Set-Cookie"];
416
417 #if PLATFORM(MAC)
418     NSArray *unfilteredCookies = [NSHTTPCookie _parsedCookiesWithResponseHeaderFields:headerFields forURL:cookieURL];
419 #else
420     NSArray *unfilteredCookies = [NSHTTPCookie cookiesWithResponseHeaderFields:headerFields forURL:cookieURL];
421 #endif
422
423 #if ENABLE(RESOURCE_LOAD_STATISTICS)
424     RetainPtr<NSArray> filteredCookies = filterCookies(unfilteredCookies, clientSideCookieCap(RegistrableDomain { firstParty }, pageID));
425 #else
426     RetainPtr<NSArray> filteredCookies = filterCookies(unfilteredCookies, WTF::nullopt);
427 #endif
428     ASSERT([filteredCookies.get() count] <= 1);
429
430     setHTTPCookiesForURL(cookieStorage().get(), filteredCookies.get(), cookieURL, firstParty, sameSiteInfo);
431
432     END_BLOCK_OBJC_EXCEPTIONS;
433 }
434
435 static NSHTTPCookieAcceptPolicy httpCookieAcceptPolicy(CFHTTPCookieStorageRef cookieStorage)
436 {
437     ASSERT(hasProcessPrivilege(ProcessPrivilege::CanAccessRawCookies));
438
439     if (!cookieStorage)
440         return [[NSHTTPCookieStorage sharedHTTPCookieStorage] cookieAcceptPolicy];
441
442     return static_cast<NSHTTPCookieAcceptPolicy>(CFHTTPCookieStorageGetCookieAcceptPolicy(cookieStorage));
443 }
444
445 HTTPCookieAcceptPolicy NetworkStorageSession::cookieAcceptPolicy() const
446 {
447     BEGIN_BLOCK_OBJC_EXCEPTIONS;
448     auto policy = httpCookieAcceptPolicy(cookieStorage().get());
449     return toHTTPCookieAcceptPolicy(policy);
450     END_BLOCK_OBJC_EXCEPTIONS;
451
452     return HTTPCookieAcceptPolicy::Never;
453 }
454
455 bool NetworkStorageSession::getRawCookies(const URL& firstParty, const SameSiteInfo& sameSiteInfo, const URL& url, Optional<FrameIdentifier> frameID, Optional<PageIdentifier> pageID, ShouldAskITP shouldAskITP, Vector<Cookie>& rawCookies) const
456 {
457     rawCookies.clear();
458     BEGIN_BLOCK_OBJC_EXCEPTIONS;
459
460     NSArray *cookies = cookiesForURL(*this, firstParty, sameSiteInfo, url, frameID, pageID, shouldAskITP);
461     NSUInteger count = [cookies count];
462     rawCookies.reserveCapacity(count);
463     for (NSUInteger i = 0; i < count; ++i) {
464         NSHTTPCookie *cookie = (NSHTTPCookie *)[cookies objectAtIndex:i];
465         rawCookies.uncheckedAppend({ cookie });
466     }
467
468     END_BLOCK_OBJC_EXCEPTIONS;
469     return true;
470 }
471
472 void NetworkStorageSession::deleteCookie(const URL& url, const String& cookieName) const
473 {
474     ASSERT(hasProcessPrivilege(ProcessPrivilege::CanAccessRawCookies));
475
476     BEGIN_BLOCK_OBJC_EXCEPTIONS;
477
478     RetainPtr<CFHTTPCookieStorageRef> cookieStorage = this->cookieStorage();
479     NSArray *cookies = httpCookiesForURL(cookieStorage.get(), nil, WTF::nullopt, url);
480
481     NSString *cookieNameString = cookieName;
482
483     NSUInteger count = [cookies count];
484     for (NSUInteger i = 0; i < count; ++i) {
485         NSHTTPCookie *cookie = (NSHTTPCookie *)[cookies objectAtIndex:i];
486         if ([[cookie name] isEqualToString:cookieNameString])
487             deleteHTTPCookie(cookieStorage.get(), cookie);
488     }
489
490     END_BLOCK_OBJC_EXCEPTIONS;
491 }
492
493 void NetworkStorageSession::getHostnamesWithCookies(HashSet<String>& hostnames)
494 {
495     BEGIN_BLOCK_OBJC_EXCEPTIONS;
496
497     NSArray *cookies = httpCookies(cookieStorage().get());
498     
499     for (NSHTTPCookie* cookie in cookies) {
500         if (NSString *domain = [cookie domain])
501             hostnames.add(domain);
502         else
503             ASSERT_NOT_REACHED();
504     }
505     
506     END_BLOCK_OBJC_EXCEPTIONS;
507 }
508
509 void NetworkStorageSession::deleteAllCookies()
510 {
511     deleteAllHTTPCookies(cookieStorage().get());
512 }
513
514 void NetworkStorageSession::deleteCookiesForHostnames(const Vector<String>& hostnames)
515 {
516     deleteCookiesForHostnames(hostnames, IncludeHttpOnlyCookies::Yes);
517 }
518
519 void NetworkStorageSession::deleteCookiesForHostnames(const Vector<String>& hostnames, IncludeHttpOnlyCookies includeHttpOnlyCookies)
520 {
521     ASSERT(hasProcessPrivilege(ProcessPrivilege::CanAccessRawCookies));
522
523     BEGIN_BLOCK_OBJC_EXCEPTIONS;
524
525     RetainPtr<CFHTTPCookieStorageRef> cookieStorage = this->cookieStorage();
526     NSArray *cookies = httpCookies(cookieStorage.get());
527     if (!cookies)
528         return;
529
530     HashMap<String, Vector<RetainPtr<NSHTTPCookie>>> cookiesByDomain;
531     for (NSHTTPCookie *cookie in cookies) {
532         if (!cookie.domain || (includeHttpOnlyCookies == IncludeHttpOnlyCookies::No && cookie.isHTTPOnly))
533             continue;
534         cookiesByDomain.ensure(cookie.domain, [] {
535             return Vector<RetainPtr<NSHTTPCookie>>();
536         }).iterator->value.append(cookie);
537     }
538
539     for (const auto& hostname : hostnames) {
540         auto it = cookiesByDomain.find(hostname);
541         if (it == cookiesByDomain.end())
542             continue;
543
544         for (auto& cookie : it->value)
545             deleteHTTPCookie(cookieStorage.get(), cookie.get());
546     }
547
548     [nsCookieStorage() _saveCookies];
549
550     END_BLOCK_OBJC_EXCEPTIONS;
551 }
552
553 void NetworkStorageSession::deleteAllCookiesModifiedSince(WallTime timePoint)
554 {
555     ASSERT(hasProcessPrivilege(ProcessPrivilege::CanAccessRawCookies));
556
557     if (![NSHTTPCookieStorage instancesRespondToSelector:@selector(removeCookiesSinceDate:)])
558         return;
559
560     NSTimeInterval timeInterval = timePoint.secondsSinceEpoch().seconds();
561     NSDate *date = [NSDate dateWithTimeIntervalSince1970:timeInterval];
562
563     auto *storage = nsCookieStorage();
564
565     [storage removeCookiesSinceDate:date];
566     [storage _saveCookies];
567 }
568
569 } // namespace WebCore