Move URL from WebCore to WTF
[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 <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 #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, std::optional<Seconds> cappedLifetime)
264 {
265     ASSERT(hasProcessPrivilege(ProcessPrivilege::CanAccessRawCookies));
266     NSUInteger count = [unfilteredCookies count];
267     RetainPtr<NSMutableArray> filteredCookies = adoptNS([[NSMutableArray alloc] initWithCapacity:count]);
268
269     for (NSUInteger i = 0; i < count; ++i) {
270         NSHTTPCookie *cookie = (NSHTTPCookie *)[unfilteredCookies objectAtIndex:i];
271
272         // <rdar://problem/5632883> On 10.5, NSHTTPCookieStorage would store an empty cookie,
273         // which would be sent as "Cookie: =". We have a workaround in setCookies() to prevent
274         // that, but we also need to avoid sending cookies that were previously stored, and
275         // there's no harm to doing this check because such a cookie is never valid.
276         if (![[cookie name] length])
277             continue;
278
279         if ([cookie isHTTPOnly])
280             continue;
281
282         // Cap lifetime of persistent, client-side cookies to a week.
283         if (cappedLifetime && ![cookie isSessionOnly]) {
284             if (!cookie.expiresDate || cookie.expiresDate.timeIntervalSinceNow > cappedLifetime->seconds()) {
285                 RetainPtr<NSMutableDictionary<NSHTTPCookiePropertyKey, id>> properties = adoptNS([[cookie properties] mutableCopy]);
286                 RetainPtr<NSDate> dateInAWeek = adoptNS([[NSDate alloc] initWithTimeIntervalSinceNow:cappedLifetime->seconds()]);
287                 [properties setObject:dateInAWeek.get() forKey:NSHTTPCookieExpires];
288                 cookie = [NSHTTPCookie cookieWithProperties:properties.get()];
289             }
290         }
291
292         [filteredCookies.get() addObject:cookie];
293     }
294
295     return filteredCookies;
296 }
297
298 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)
299 {
300 #if ENABLE(RESOURCE_LOAD_STATISTICS)
301     if (session.shouldBlockCookies(firstParty, url, frameID, pageID))
302         return nil;
303 #else
304     UNUSED_PARAM(frameID);
305     UNUSED_PARAM(pageID);
306 #endif
307     return httpCookiesForURL(session.cookieStorage().get(), firstParty, sameSiteInfo, url);
308 }
309
310 enum IncludeHTTPOnlyOrNot { DoNotIncludeHTTPOnly, IncludeHTTPOnly };
311 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)
312 {
313     ASSERT(hasProcessPrivilege(ProcessPrivilege::CanAccessRawCookies));
314
315     BEGIN_BLOCK_OBJC_EXCEPTIONS;
316
317     NSArray *cookies = cookiesForURL(session, firstParty, sameSiteInfo, url, frameID, pageID);
318     if (![cookies count])
319         return { String(), false }; // Return a null string, not an empty one that StringBuilder would create below.
320
321     StringBuilder cookiesBuilder;
322     bool didAccessSecureCookies = false;
323     for (NSHTTPCookie *cookie in cookies) {
324         if (![[cookie name] length])
325             continue;
326
327         if (!includeHTTPOnly && [cookie isHTTPOnly])
328             continue;
329
330         if ([cookie isSecure]) {
331             didAccessSecureCookies = true;
332             if (includeSecureCookies == IncludeSecureCookies::No)
333                 continue;
334         }
335
336         if (!cookiesBuilder.isEmpty())
337             cookiesBuilder.appendLiteral("; ");
338
339         cookiesBuilder.append([cookie name]);
340         cookiesBuilder.append('=');
341         cookiesBuilder.append([cookie value]);
342     }
343     return { cookiesBuilder.toString(), didAccessSecureCookies };
344
345     END_BLOCK_OBJC_EXCEPTIONS;
346     return { String(), false };
347 }
348
349 static void deleteAllHTTPCookies(CFHTTPCookieStorageRef cookieStorage)
350 {
351     ASSERT(hasProcessPrivilege(ProcessPrivilege::CanAccessRawCookies));
352
353     if (!cookieStorage) {
354         NSHTTPCookieStorage *cookieStorage = [NSHTTPCookieStorage sharedHTTPCookieStorage];
355         NSArray *cookies = [cookieStorage cookies];
356         if (!cookies)
357             return;
358
359         for (NSHTTPCookie *cookie in cookies)
360             [cookieStorage deleteCookie:cookie];
361         return;
362     }
363
364     CFHTTPCookieStorageDeleteAllCookies(cookieStorage);
365 }
366
367 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
368 {
369     return cookiesForSession(*this, firstParty, sameSiteInfo, url, frameID, pageID, DoNotIncludeHTTPOnly, includeSecureCookies);
370 }
371
372 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
373 {
374     return cookiesForSession(*this, firstParty, sameSiteInfo, url, frameID, pageID, IncludeHTTPOnly, includeSecureCookies);
375 }
376
377 std::pair<String, bool> NetworkStorageSession::cookieRequestHeaderFieldValue(const CookieRequestHeaderFieldProxy& headerFieldProxy) const
378 {
379     return cookiesForSession(*this, headerFieldProxy.firstParty, headerFieldProxy.sameSiteInfo, headerFieldProxy.url, headerFieldProxy.frameID, headerFieldProxy.pageID, IncludeHTTPOnly, headerFieldProxy.includeSecureCookies);
380 }
381
382 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
383 {
384     ASSERT(hasProcessPrivilege(ProcessPrivilege::CanAccessRawCookies));
385
386     BEGIN_BLOCK_OBJC_EXCEPTIONS;
387
388     // <rdar://problem/5632883> On 10.5, NSHTTPCookieStorage would store an empty cookie,
389     // which would be sent as "Cookie: =".
390     if (cookieStr.isEmpty())
391         return;
392
393     // <http://bugs.webkit.org/show_bug.cgi?id=6531>, <rdar://4409034>
394     // cookiesWithResponseHeaderFields doesn't parse cookies without a value
395     String cookieString = cookieStr.contains('=') ? cookieStr : cookieStr + "=";
396
397     NSURL *cookieURL = url;
398     NSDictionary *headerFields = [NSDictionary dictionaryWithObject:cookieString forKey:@"Set-Cookie"];
399
400 #if PLATFORM(MAC)
401     NSArray *unfilteredCookies = [NSHTTPCookie _parsedCookiesWithResponseHeaderFields:headerFields forURL:cookieURL];
402 #else
403     NSArray *unfilteredCookies = [NSHTTPCookie cookiesWithResponseHeaderFields:headerFields forURL:cookieURL];
404 #endif
405
406 #if ENABLE(RESOURCE_LOAD_STATISTICS)
407     RetainPtr<NSArray> filteredCookies = filterCookies(unfilteredCookies, m_ageCapForClientSideCookies);
408 #else
409     RetainPtr<NSArray> filteredCookies = filterCookies(unfilteredCookies, false);
410 #endif
411     ASSERT([filteredCookies.get() count] <= 1);
412
413 #if ENABLE(RESOURCE_LOAD_STATISTICS)
414     if (shouldBlockCookies(firstParty, url, frameID, pageID))
415         return;
416 #else
417     UNUSED_PARAM(frameID);
418     UNUSED_PARAM(pageID);
419 #endif
420
421     setHTTPCookiesForURL(cookieStorage().get(), filteredCookies.get(), cookieURL, firstParty, sameSiteInfo);
422
423     END_BLOCK_OBJC_EXCEPTIONS;
424 }
425
426 static NSHTTPCookieAcceptPolicy httpCookieAcceptPolicy(CFHTTPCookieStorageRef cookieStorage)
427 {
428     ASSERT(hasProcessPrivilege(ProcessPrivilege::CanAccessRawCookies));
429
430     if (!cookieStorage)
431         return [[NSHTTPCookieStorage sharedHTTPCookieStorage] cookieAcceptPolicy];
432
433     return static_cast<NSHTTPCookieAcceptPolicy>(CFHTTPCookieStorageGetCookieAcceptPolicy(cookieStorage));
434 }
435
436 bool NetworkStorageSession::cookiesEnabled() const
437 {
438     BEGIN_BLOCK_OBJC_EXCEPTIONS;
439
440     NSHTTPCookieAcceptPolicy cookieAcceptPolicy = httpCookieAcceptPolicy(cookieStorage().get());
441     return cookieAcceptPolicy == NSHTTPCookieAcceptPolicyAlways || cookieAcceptPolicy == NSHTTPCookieAcceptPolicyOnlyFromMainDocumentDomain || cookieAcceptPolicy == NSHTTPCookieAcceptPolicyExclusivelyFromMainDocumentDomain;
442
443     END_BLOCK_OBJC_EXCEPTIONS;
444     return false;
445 }
446
447 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
448 {
449     rawCookies.clear();
450     BEGIN_BLOCK_OBJC_EXCEPTIONS;
451
452     NSArray *cookies = cookiesForURL(*this, firstParty, sameSiteInfo, url, frameID, pageID);
453     NSUInteger count = [cookies count];
454     rawCookies.reserveCapacity(count);
455     for (NSUInteger i = 0; i < count; ++i) {
456         NSHTTPCookie *cookie = (NSHTTPCookie *)[cookies objectAtIndex:i];
457         rawCookies.uncheckedAppend({ cookie });
458     }
459
460     END_BLOCK_OBJC_EXCEPTIONS;
461     return true;
462 }
463
464 void NetworkStorageSession::deleteCookie(const URL& url, const String& cookieName) const
465 {
466     ASSERT(hasProcessPrivilege(ProcessPrivilege::CanAccessRawCookies));
467
468     BEGIN_BLOCK_OBJC_EXCEPTIONS;
469
470     RetainPtr<CFHTTPCookieStorageRef> cookieStorage = this->cookieStorage();
471     NSArray *cookies = httpCookiesForURL(cookieStorage.get(), nil, std::nullopt, url);
472
473     NSString *cookieNameString = cookieName;
474
475     NSUInteger count = [cookies count];
476     for (NSUInteger i = 0; i < count; ++i) {
477         NSHTTPCookie *cookie = (NSHTTPCookie *)[cookies objectAtIndex:i];
478         if ([[cookie name] isEqualToString:cookieNameString])
479             deleteHTTPCookie(cookieStorage.get(), cookie);
480     }
481
482     END_BLOCK_OBJC_EXCEPTIONS;
483 }
484
485 void NetworkStorageSession::getHostnamesWithCookies(HashSet<String>& hostnames)
486 {
487     BEGIN_BLOCK_OBJC_EXCEPTIONS;
488
489     NSArray *cookies = httpCookies(cookieStorage().get());
490     
491     for (NSHTTPCookie* cookie in cookies)
492         hostnames.add([cookie domain]);
493     
494     END_BLOCK_OBJC_EXCEPTIONS;
495 }
496
497 void NetworkStorageSession::deleteAllCookies()
498 {
499     deleteAllHTTPCookies(cookieStorage().get());
500 }
501
502 void NetworkStorageSession::deleteCookiesForHostnames(const Vector<String>& hostnames)
503 {
504     ASSERT(hasProcessPrivilege(ProcessPrivilege::CanAccessRawCookies));
505
506     BEGIN_BLOCK_OBJC_EXCEPTIONS;
507
508     RetainPtr<CFHTTPCookieStorageRef> cookieStorage = this->cookieStorage();
509     NSArray *cookies = httpCookies(cookieStorage.get());
510     if (!cookies)
511         return;
512
513     HashMap<String, Vector<RetainPtr<NSHTTPCookie>>> cookiesByDomain;
514     for (NSHTTPCookie *cookie in cookies) {
515         if (!cookie.domain)
516             continue;
517         cookiesByDomain.ensure(cookie.domain, [] {
518             return Vector<RetainPtr<NSHTTPCookie>>();
519         }).iterator->value.append(cookie);
520     }
521
522     for (const auto& hostname : hostnames) {
523         auto it = cookiesByDomain.find(hostname);
524         if (it == cookiesByDomain.end())
525             continue;
526
527         for (auto& cookie : it->value)
528             deleteHTTPCookie(cookieStorage.get(), cookie.get());
529     }
530
531     [nsCookieStorage() _saveCookies];
532
533     END_BLOCK_OBJC_EXCEPTIONS;
534 }
535
536 void NetworkStorageSession::deleteAllCookiesModifiedSince(WallTime timePoint)
537 {
538     ASSERT(hasProcessPrivilege(ProcessPrivilege::CanAccessRawCookies));
539
540     if (![NSHTTPCookieStorage instancesRespondToSelector:@selector(removeCookiesSinceDate:)])
541         return;
542
543     NSTimeInterval timeInterval = timePoint.secondsSinceEpoch().seconds();
544     NSDate *date = [NSDate dateWithTimeIntervalSince1970:timeInterval];
545
546     auto *storage = nsCookieStorage();
547
548     [storage removeCookiesSinceDate:date];
549     [storage _saveCookies];
550 }
551
552 } // namespace WebCore