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