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