Attempt to fix the Mac and iOS build following r230944
[WebKit-https.git] / Source / WebCore / platform / network / mac / CookieJarMac.mm
1 /*
2  * Copyright (C) 2003-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 "PlatformCookieJar.h"
28
29 #import "CookieRequestHeaderFieldProxy.h"
30 #import "CookiesStrategy.h"
31 #import "NetworkStorageSession.h"
32 #import <pal/spi/cf/CFNetworkSPI.h>
33 #import <wtf/BlockObjCExceptions.h>
34
35 #import "Cookie.h"
36 #import "CookieStorage.h"
37 #import "SameSiteInfo.h"
38 #import "URL.h"
39 #import <wtf/Optional.h>
40 #import <wtf/ProcessPrivilege.h>
41 #import <wtf/text/StringBuilder.h>
42
43 @interface NSURL ()
44 - (CFURLRef)_cfurl;
45 @end
46
47 #if (PLATFORM(MAC) && __MAC_OS_X_VERSION_MIN_REQUIRED >= 101400) || (PLATFORM(IOS) && __IPHONE_OS_VERSION_MIN_REQUIRED >= 120000)
48 @interface NSHTTPCookieStorage (Staging)
49 - (void)_getCookiesForURL:(NSURL *)url mainDocumentURL:(NSURL *)mainDocumentURL partition:(NSString *)partition policyProperties:(NSDictionary*)props completionHandler:(void (^)(NSArray *))completionHandler;
50 @end
51 #endif
52
53 namespace WebCore {
54
55 static NSArray *httpCookies(CFHTTPCookieStorageRef cookieStorage)
56 {
57     ASSERT(hasProcessPrivilege(ProcessPrivilege::CanAccessRawCookies));
58     if (!cookieStorage)
59         return [[NSHTTPCookieStorage sharedHTTPCookieStorage] cookies];
60     
61     auto cookies = adoptCF(CFHTTPCookieStorageCopyCookies(cookieStorage));
62     return [NSHTTPCookie _cf2nsCookies:cookies.get()];
63 }
64
65 static void deleteHTTPCookie(CFHTTPCookieStorageRef cookieStorage, NSHTTPCookie *cookie)
66 {
67     ASSERT(hasProcessPrivilege(ProcessPrivilege::CanAccessRawCookies));
68     if (!cookieStorage) {
69         [[NSHTTPCookieStorage sharedHTTPCookieStorage] deleteCookie:cookie];
70         return;
71     }
72     
73     CFHTTPCookieStorageDeleteCookie(cookieStorage, [cookie _GetInternalCFHTTPCookie]);
74 }
75
76 static RetainPtr<NSDictionary> policyProperties(const SameSiteInfo& sameSiteInfo, NSURL *url)
77 {
78 #if (PLATFORM(MAC) && __MAC_OS_X_VERSION_MIN_REQUIRED >= 101400) || (PLATFORM(IOS) && __IPHONE_OS_VERSION_MIN_REQUIRED >= 120000)
79     static NSURL *emptyURL = [[NSURL alloc] initWithString:@""];
80     NSDictionary *policyProperties = @{
81         @"_kCFHTTPCookiePolicyPropertySiteForCookies": sameSiteInfo.isSameSite ? url : emptyURL,
82         @"_kCFHTTPCookiePolicyPropertyIsTopLevelNavigation": [NSNumber numberWithBool:sameSiteInfo.isTopSite],
83     };
84     return policyProperties;
85 #else
86     UNUSED_PARAM(sameSiteInfo);
87     UNUSED_PARAM(url);
88     return nullptr;
89 #endif
90 }
91
92 static NSArray *cookiesForURL(NSHTTPCookieStorage *storage, NSURL *url, NSURL *mainDocumentURL, const std::optional<SameSiteInfo>& sameSiteInfo, NSString *partition = nullptr)
93 {
94     // The _getCookiesForURL: method calls the completionHandler synchronously. We use std::optional<> to ensure this invariant.
95     std::optional<RetainPtr<NSArray *>> cookiesPtr;
96     auto completionHandler = [&cookiesPtr] (NSArray *cookies) { cookiesPtr = retainPtr(cookies); };
97 #if (PLATFORM(MAC) && __MAC_OS_X_VERSION_MIN_REQUIRED >= 101400) || (PLATFORM(IOS) && __IPHONE_OS_VERSION_MIN_REQUIRED >= 120000)
98     if ([storage respondsToSelector:@selector(_getCookiesForURL:mainDocumentURL:partition:policyProperties:completionHandler:)])
99         [storage _getCookiesForURL:url mainDocumentURL:mainDocumentURL partition:partition policyProperties:sameSiteInfo ? policyProperties(sameSiteInfo.value(), url).get() : nullptr completionHandler:completionHandler];
100     else
101         [storage _getCookiesForURL:url mainDocumentURL:mainDocumentURL partition:partition completionHandler:completionHandler];
102 #else
103     [storage _getCookiesForURL:url mainDocumentURL:mainDocumentURL partition:partition completionHandler:completionHandler];
104     UNUSED_PARAM(sameSiteInfo);
105 #endif
106     ASSERT(!!cookiesPtr);
107     return cookiesPtr->autorelease();
108 }
109
110 static void setHTTPCookiesForURL(CFHTTPCookieStorageRef cookieStorage, NSArray *cookies, NSURL *url, NSURL *mainDocumentURL, const SameSiteInfo& sameSiteInfo)
111 {
112     ASSERT(hasProcessPrivilege(ProcessPrivilege::CanAccessRawCookies));
113     if (!cookieStorage) {
114 #if (PLATFORM(MAC) && __MAC_OS_X_VERSION_MIN_REQUIRED >= 101400) || (PLATFORM(IOS) && __IPHONE_OS_VERSION_MIN_REQUIRED >= 120000)
115         if ([NSHTTPCookieStorage instancesRespondToSelector:@selector(_setCookies:forURL:mainDocumentURL:policyProperties:)])
116             [[NSHTTPCookieStorage sharedHTTPCookieStorage] _setCookies:cookies forURL:url mainDocumentURL:mainDocumentURL policyProperties:policyProperties(sameSiteInfo, url).get()];
117         else
118 #endif
119             [[NSHTTPCookieStorage sharedHTTPCookieStorage] setCookies:cookies forURL:url mainDocumentURL:mainDocumentURL];
120         return;
121     }
122 #if (PLATFORM(MAC) && __MAC_OS_X_VERSION_MIN_REQUIRED >= 101400) || (PLATFORM(IOS) && __IPHONE_OS_VERSION_MIN_REQUIRED >= 120000)
123     if ([NSHTTPCookieStorage instancesRespondToSelector:@selector(_setCookies:forURL:mainDocumentURL:policyProperties:)]) {
124         // FIXME: Stop creating a new NSHTTPCookieStorage object each time we want to query the cookie jar.
125         // NetworkStorageSession could instead keep a NSHTTPCookieStorage object for us.
126         RetainPtr<NSHTTPCookieStorage> nsCookieStorage = adoptNS([[NSHTTPCookieStorage alloc] _initWithCFHTTPCookieStorage:cookieStorage]);
127         [nsCookieStorage _setCookies:cookies forURL:url mainDocumentURL:mainDocumentURL policyProperties:policyProperties(sameSiteInfo, url).get()];
128     } else {
129 #endif
130         auto cfCookies = adoptCF([NSHTTPCookie _ns2cfCookies:cookies]);
131         CFHTTPCookieStorageSetCookies(cookieStorage, cfCookies.get(), [url _cfurl], [mainDocumentURL _cfurl]);
132 #if (PLATFORM(MAC) && __MAC_OS_X_VERSION_MIN_REQUIRED >= 101400) || (PLATFORM(IOS) && __IPHONE_OS_VERSION_MIN_REQUIRED >= 120000)
133     }
134 #else
135     UNUSED_PARAM(sameSiteInfo);
136 #endif
137 }
138
139 static NSArray *httpCookiesForURL(CFHTTPCookieStorageRef cookieStorage, NSURL *firstParty, const std::optional<SameSiteInfo>& sameSiteInfo, NSURL *url)
140 {
141     ASSERT(hasProcessPrivilege(ProcessPrivilege::CanAccessRawCookies));
142     if (!cookieStorage)
143         cookieStorage = _CFHTTPCookieStorageGetDefault(kCFAllocatorDefault);
144
145     // FIXME: Stop creating a new NSHTTPCookieStorage object each time we want to query the cookie jar.
146     // NetworkStorageSession could instead keep a NSHTTPCookieStorage object for us.
147     RetainPtr<NSHTTPCookieStorage> nsCookieStorage = adoptNS([[NSHTTPCookieStorage alloc] _initWithCFHTTPCookieStorage:cookieStorage]);
148     return cookiesForURL(nsCookieStorage.get(), url, firstParty, sameSiteInfo);
149 }
150
151 static RetainPtr<NSArray> filterCookies(NSArray *unfilteredCookies)
152 {
153     ASSERT(hasProcessPrivilege(ProcessPrivilege::CanAccessRawCookies));
154     NSUInteger count = [unfilteredCookies count];
155     RetainPtr<NSMutableArray> filteredCookies = adoptNS([[NSMutableArray alloc] initWithCapacity:count]);
156
157     for (NSUInteger i = 0; i < count; ++i) {
158         NSHTTPCookie *cookie = (NSHTTPCookie *)[unfilteredCookies objectAtIndex:i];
159
160         // <rdar://problem/5632883> On 10.5, NSHTTPCookieStorage would store an empty cookie,
161         // which would be sent as "Cookie: =". We have a workaround in setCookies() to prevent
162         // that, but we also need to avoid sending cookies that were previously stored, and
163         // there's no harm to doing this check because such a cookie is never valid.
164         if (![[cookie name] length])
165             continue;
166
167         if ([cookie isHTTPOnly])
168             continue;
169
170         [filteredCookies.get() addObject:cookie];
171     }
172
173     return filteredCookies;
174 }
175
176 #if HAVE(CFNETWORK_STORAGE_PARTITIONING)
177
178 static NSArray *applyPartitionToCookies(NSString *partition, NSArray *cookies)
179 {
180     ASSERT(hasProcessPrivilege(ProcessPrivilege::CanAccessRawCookies));
181
182     // FIXME 24747739: CFNetwork should expose this key as SPI
183     static NSString * const partitionKey = @"StoragePartition";
184
185     NSMutableArray *partitionedCookies = [NSMutableArray arrayWithCapacity:cookies.count];
186     for (NSHTTPCookie *cookie in cookies) {
187         RetainPtr<NSMutableDictionary> properties = adoptNS([cookie.properties mutableCopy]);
188         [properties setObject:partition forKey:partitionKey];
189         [partitionedCookies addObject:[NSHTTPCookie cookieWithProperties:properties.get()]];
190     }
191
192     return partitionedCookies;
193 }
194
195 static bool cookiesAreBlockedForURL(const NetworkStorageSession& session, const URL& firstParty, const URL& url)
196 {
197     return session.shouldBlockCookies(firstParty, url);
198 }
199
200 static NSArray *cookiesInPartitionForURL(const NetworkStorageSession& session, const URL& firstParty, const SameSiteInfo& sameSiteInfo, const URL& url, std::optional<uint64_t> frameID, std::optional<uint64_t> pageID)
201 {
202     ASSERT(hasProcessPrivilege(ProcessPrivilege::CanAccessRawCookies));
203     String partition = session.cookieStoragePartition(firstParty, url, frameID, pageID);
204     if (partition.isEmpty())
205         return nil;
206
207     // FIXME: Stop creating a new NSHTTPCookieStorage object each time we want to query the cookie jar.
208     // NetworkStorageSession could instead keep a NSHTTPCookieStorage object for us.
209     RetainPtr<NSHTTPCookieStorage> cookieStorage;
210     if (auto storage = session.cookieStorage())
211         cookieStorage = adoptNS([[NSHTTPCookieStorage alloc] _initWithCFHTTPCookieStorage:storage.get()]);
212     else
213         cookieStorage = [NSHTTPCookieStorage sharedHTTPCookieStorage];
214     return cookiesForURL(cookieStorage.get(), url, firstParty, sameSiteInfo, partition);
215 }
216
217 #endif // HAVE(CFNETWORK_STORAGE_PARTITIONING)
218     
219 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)
220 {
221 #if HAVE(CFNETWORK_STORAGE_PARTITIONING)
222     if (cookiesAreBlockedForURL(session, firstParty, url))
223         return nil;
224     
225     if (NSArray *cookies = cookiesInPartitionForURL(session, firstParty, sameSiteInfo, url, frameID, pageID))
226         return cookies;
227 #else
228     UNUSED_PARAM(frameID);
229     UNUSED_PARAM(pageID);
230 #endif
231     return httpCookiesForURL(session.cookieStorage().get(), firstParty, sameSiteInfo, url);
232 }
233
234 enum IncludeHTTPOnlyOrNot { DoNotIncludeHTTPOnly, IncludeHTTPOnly };
235 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)
236 {
237     ASSERT(hasProcessPrivilege(ProcessPrivilege::CanAccessRawCookies));
238
239     BEGIN_BLOCK_OBJC_EXCEPTIONS;
240
241     NSArray *cookies = cookiesForURL(session, firstParty, sameSiteInfo, url, frameID, pageID);
242     if (![cookies count])
243         return { String(), false }; // Return a null string, not an empty one that StringBuilder would create below.
244
245     StringBuilder cookiesBuilder;
246     bool didAccessSecureCookies = false;
247     for (NSHTTPCookie *cookie in cookies) {
248         if (![[cookie name] length])
249             continue;
250
251         if (!includeHTTPOnly && [cookie isHTTPOnly])
252             continue;
253
254         if ([cookie isSecure]) {
255             didAccessSecureCookies = true;
256             if (includeSecureCookies == IncludeSecureCookies::No)
257                 continue;
258         }
259
260         if (!cookiesBuilder.isEmpty())
261             cookiesBuilder.appendLiteral("; ");
262
263         cookiesBuilder.append([cookie name]);
264         cookiesBuilder.append('=');
265         cookiesBuilder.append([cookie value]);
266     }
267     return { cookiesBuilder.toString(), didAccessSecureCookies };
268
269     END_BLOCK_OBJC_EXCEPTIONS;
270     return { String(), false };
271 }
272
273 static void deleteAllHTTPCookies(CFHTTPCookieStorageRef cookieStorage)
274 {
275     ASSERT(hasProcessPrivilege(ProcessPrivilege::CanAccessRawCookies));
276
277     if (!cookieStorage) {
278         NSHTTPCookieStorage *cookieStorage = [NSHTTPCookieStorage sharedHTTPCookieStorage];
279         NSArray *cookies = [cookieStorage cookies];
280         if (!cookies)
281             return;
282
283         for (NSHTTPCookie *cookie in cookies)
284             [cookieStorage deleteCookie:cookie];
285         return;
286     }
287
288     CFHTTPCookieStorageDeleteAllCookies(cookieStorage);
289 }
290
291 std::pair<String, bool> cookiesForDOM(const NetworkStorageSession& session, const URL& firstParty, const SameSiteInfo& sameSiteInfo, const URL& url, std::optional<uint64_t> frameID, std::optional<uint64_t> pageID, IncludeSecureCookies includeSecureCookies)
292 {
293     return cookiesForSession(session, firstParty, sameSiteInfo, url, frameID, pageID, DoNotIncludeHTTPOnly, includeSecureCookies);
294 }
295
296 std::pair<String, bool> cookieRequestHeaderFieldValue(const NetworkStorageSession& session, const URL& firstParty, const SameSiteInfo& sameSiteInfo, const URL& url, std::optional<uint64_t> frameID, std::optional<uint64_t> pageID, IncludeSecureCookies includeSecureCookies)
297 {
298     return cookiesForSession(session, firstParty, sameSiteInfo, url, frameID, pageID, IncludeHTTPOnly, includeSecureCookies);
299 }
300
301 std::pair<String, bool> cookieRequestHeaderFieldValue(const NetworkStorageSession& session, const CookieRequestHeaderFieldProxy& headerFieldProxy)
302 {
303     return cookiesForSession(session, headerFieldProxy.firstParty, headerFieldProxy.sameSiteInfo, headerFieldProxy.url, headerFieldProxy.frameID, headerFieldProxy.pageID, IncludeHTTPOnly, headerFieldProxy.includeSecureCookies);
304 }
305
306 void setCookiesFromDOM(const NetworkStorageSession& session, const URL& firstParty, const SameSiteInfo& sameSiteInfo, const URL& url, std::optional<uint64_t> frameID, std::optional<uint64_t> pageID, const String& cookieStr)
307 {
308     ASSERT(hasProcessPrivilege(ProcessPrivilege::CanAccessRawCookies));
309
310     BEGIN_BLOCK_OBJC_EXCEPTIONS;
311
312     // <rdar://problem/5632883> On 10.5, NSHTTPCookieStorage would store an empty cookie,
313     // which would be sent as "Cookie: =".
314     if (cookieStr.isEmpty())
315         return;
316
317     // <http://bugs.webkit.org/show_bug.cgi?id=6531>, <rdar://4409034>
318     // cookiesWithResponseHeaderFields doesn't parse cookies without a value
319     String cookieString = cookieStr.contains('=') ? cookieStr : cookieStr + "=";
320
321     NSURL *cookieURL = url;
322     NSDictionary *headerFields = [NSDictionary dictionaryWithObject:cookieString forKey:@"Set-Cookie"];
323
324 #if PLATFORM(MAC)
325     NSArray *unfilteredCookies = [NSHTTPCookie _parsedCookiesWithResponseHeaderFields:headerFields forURL:cookieURL];
326 #else
327     NSArray *unfilteredCookies = [NSHTTPCookie cookiesWithResponseHeaderFields:headerFields forURL:cookieURL];
328 #endif
329
330     RetainPtr<NSArray> filteredCookies = filterCookies(unfilteredCookies);
331     ASSERT([filteredCookies.get() count] <= 1);
332
333 #if HAVE(CFNETWORK_STORAGE_PARTITIONING)
334     if (session.shouldBlockCookies(firstParty, url))
335         return;
336
337     String partition = session.cookieStoragePartition(firstParty, url, frameID, pageID);
338     if (!partition.isEmpty())
339         filteredCookies = applyPartitionToCookies(partition, filteredCookies.get());
340 #else
341     UNUSED_PARAM(frameID);
342     UNUSED_PARAM(pageID);
343 #endif
344
345     setHTTPCookiesForURL(session.cookieStorage().get(), filteredCookies.get(), cookieURL, firstParty, sameSiteInfo);
346
347     END_BLOCK_OBJC_EXCEPTIONS;
348 }
349
350 static NSHTTPCookieAcceptPolicy httpCookieAcceptPolicy(CFHTTPCookieStorageRef cookieStorage)
351 {
352     ASSERT(hasProcessPrivilege(ProcessPrivilege::CanAccessRawCookies));
353
354     if (!cookieStorage)
355         return [[NSHTTPCookieStorage sharedHTTPCookieStorage] cookieAcceptPolicy];
356
357     return static_cast<NSHTTPCookieAcceptPolicy>(CFHTTPCookieStorageGetCookieAcceptPolicy(cookieStorage));
358 }
359
360 bool cookiesEnabled(const NetworkStorageSession& session)
361 {
362     BEGIN_BLOCK_OBJC_EXCEPTIONS;
363
364     NSHTTPCookieAcceptPolicy cookieAcceptPolicy = httpCookieAcceptPolicy(session.cookieStorage().get());
365     return cookieAcceptPolicy == NSHTTPCookieAcceptPolicyAlways || cookieAcceptPolicy == NSHTTPCookieAcceptPolicyOnlyFromMainDocumentDomain || cookieAcceptPolicy == NSHTTPCookieAcceptPolicyExclusivelyFromMainDocumentDomain;
366
367     END_BLOCK_OBJC_EXCEPTIONS;
368     return false;
369 }
370
371 bool getRawCookies(const NetworkStorageSession& session, const URL& firstParty, const SameSiteInfo& sameSiteInfo, const URL& url, std::optional<uint64_t> frameID, std::optional<uint64_t> pageID, Vector<Cookie>& rawCookies)
372 {
373     rawCookies.clear();
374     BEGIN_BLOCK_OBJC_EXCEPTIONS;
375
376     NSArray *cookies = cookiesForURL(session, firstParty, sameSiteInfo, url, frameID, pageID);
377     NSUInteger count = [cookies count];
378     rawCookies.reserveCapacity(count);
379     for (NSUInteger i = 0; i < count; ++i) {
380         NSHTTPCookie *cookie = (NSHTTPCookie *)[cookies objectAtIndex:i];
381         rawCookies.uncheckedAppend({ cookie });
382     }
383
384     END_BLOCK_OBJC_EXCEPTIONS;
385     return true;
386 }
387
388 void deleteCookie(const NetworkStorageSession& session, const URL& url, const String& cookieName)
389 {
390     ASSERT(hasProcessPrivilege(ProcessPrivilege::CanAccessRawCookies));
391
392     BEGIN_BLOCK_OBJC_EXCEPTIONS;
393
394     RetainPtr<CFHTTPCookieStorageRef> cookieStorage = session.cookieStorage();
395     NSArray *cookies = httpCookiesForURL(cookieStorage.get(), nil, std::nullopt, url);
396
397     NSString *cookieNameString = cookieName;
398
399     NSUInteger count = [cookies count];
400     for (NSUInteger i = 0; i < count; ++i) {
401         NSHTTPCookie *cookie = (NSHTTPCookie *)[cookies objectAtIndex:i];
402         if ([[cookie name] isEqualToString:cookieNameString])
403             deleteHTTPCookie(cookieStorage.get(), cookie);
404     }
405
406     END_BLOCK_OBJC_EXCEPTIONS;
407 }
408
409 void getHostnamesWithCookies(const NetworkStorageSession& session, HashSet<String>& hostnames)
410 {
411     BEGIN_BLOCK_OBJC_EXCEPTIONS;
412
413     NSArray *cookies = httpCookies(session.cookieStorage().get());
414     
415     for (NSHTTPCookie* cookie in cookies)
416         hostnames.add([cookie domain]);
417     
418     END_BLOCK_OBJC_EXCEPTIONS;
419 }
420
421 void deleteAllCookies(const NetworkStorageSession& session)
422 {
423     deleteAllHTTPCookies(session.cookieStorage().get());
424 }
425
426 void deleteCookiesForHostnames(const NetworkStorageSession& session, const Vector<String>& hostnames)
427 {
428     ASSERT(hasProcessPrivilege(ProcessPrivilege::CanAccessRawCookies));
429
430     BEGIN_BLOCK_OBJC_EXCEPTIONS;
431
432     RetainPtr<CFHTTPCookieStorageRef> cookieStorage = session.cookieStorage();
433     NSArray *cookies = httpCookies(cookieStorage.get());
434     if (!cookies)
435         return;
436
437     HashMap<String, Vector<RetainPtr<NSHTTPCookie>>> cookiesByDomain;
438     for (NSHTTPCookie* cookie in cookies) {
439         auto& cookies = cookiesByDomain.add(cookie.domain, Vector<RetainPtr<NSHTTPCookie>>()).iterator->value;
440         cookies.append(cookie);
441     }
442
443     for (const auto& hostname : hostnames) {
444         auto it = cookiesByDomain.find(hostname);
445         if (it == cookiesByDomain.end())
446             continue;
447
448         for (auto& cookie : it->value)
449             deleteHTTPCookie(cookieStorage.get(), cookie.get());
450     }
451
452     [session.nsCookieStorage() _saveCookies];
453
454     END_BLOCK_OBJC_EXCEPTIONS;
455 }
456
457 void deleteAllCookiesModifiedSince(const NetworkStorageSession& session, WallTime timePoint)
458 {
459     ASSERT(hasProcessPrivilege(ProcessPrivilege::CanAccessRawCookies));
460
461     if (![NSHTTPCookieStorage instancesRespondToSelector:@selector(removeCookiesSinceDate:)])
462         return;
463
464     NSTimeInterval timeInterval = timePoint.secondsSinceEpoch().seconds();
465     NSDate *date = [NSDate dateWithTimeIntervalSince1970:timeInterval];
466
467     auto *storage = session.nsCookieStorage();
468
469     [storage removeCookiesSinceDate:date];
470     [storage _saveCookies];
471 }
472
473 }