1 // Copyright 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "ios/net/cookies/cookie_store_ios.h"
7 #import <Foundation/Foundation.h>
10 #include "base/files/file_path.h"
11 #include "base/files/file_util.h"
12 #include "base/ios/ios_util.h"
13 #include "base/location.h"
14 #include "base/logging.h"
15 #include "base/mac/foundation_util.h"
16 #include "base/mac/scoped_nsobject.h"
17 #include "base/memory/weak_ptr.h"
18 #include "base/message_loop/message_loop_proxy.h"
19 #include "base/metrics/histogram.h"
20 #include "base/observer_list.h"
21 #include "base/sequenced_task_runner.h"
22 #include "base/stl_util.h"
23 #include "base/strings/sys_string_conversions.h"
24 #include "base/task_runner_util.h"
25 #include "base/threading/thread_restrictions.h"
26 #include "ios/net/cookies/cookie_creation_time_manager.h"
27 #include "ios/net/cookies/cookie_store_ios_client.h"
28 #include "ios/net/cookies/system_cookie_util.h"
29 #import "net/base/mac/url_conversions.h"
30 #include "net/cookies/cookie_util.h"
38 // The current cookie store. This weak pointer must not be used to do actual
39 // work. Its only purpose is to check that there is only one synchronized
41 CookieStoreIOS* g_current_synchronized_store = nullptr;
44 #pragma mark NotificationTrampoline
46 // NotificationTrampoline dispatches cookie notifications to all the existing
48 class NotificationTrampoline {
50 static NotificationTrampoline* GetInstance();
52 void AddObserver(CookieNotificationObserver* obs);
53 void RemoveObserver(CookieNotificationObserver* obs);
55 // Notify the observers.
56 void NotifyCookiesChanged();
57 void NotifyCookiePolicyChanged();
60 NotificationTrampoline();
61 ~NotificationTrampoline();
63 ObserverList<CookieNotificationObserver> observer_list_;
65 DISALLOW_COPY_AND_ASSIGN(NotificationTrampoline);
67 static NotificationTrampoline* g_notification_trampoline;
70 #pragma mark NotificationTrampoline implementation
72 NotificationTrampoline* NotificationTrampoline::GetInstance() {
73 if (!g_notification_trampoline)
74 g_notification_trampoline = new NotificationTrampoline;
75 return g_notification_trampoline;
78 void NotificationTrampoline::AddObserver(CookieNotificationObserver* obs) {
79 observer_list_.AddObserver(obs);
82 void NotificationTrampoline::RemoveObserver(CookieNotificationObserver* obs) {
83 observer_list_.RemoveObserver(obs);
86 void NotificationTrampoline::NotifyCookiesChanged() {
87 FOR_EACH_OBSERVER(CookieNotificationObserver, observer_list_,
88 OnSystemCookiesChanged());
91 void NotificationTrampoline::NotifyCookiePolicyChanged() {
92 FOR_EACH_OBSERVER(CookieNotificationObserver, observer_list_,
93 OnSystemCookiePolicyChanged());
96 NotificationTrampoline::NotificationTrampoline() {
99 NotificationTrampoline::~NotificationTrampoline() {
102 // Global instance of NotificationTrampoline.
103 NotificationTrampoline* NotificationTrampoline::g_notification_trampoline =
106 #pragma mark Utility functions
108 // Returns the path to Cookie.binarycookies file on the file system where
109 // WKWebView flushes its cookies.
110 base::FilePath GetBinaryCookiesFilePath() {
111 base::FilePath path = base::mac::GetUserLibraryPath();
112 // The relative path of the file (from the user library folder) where
113 // WKWebView stores its cookies.
114 const std::string kCookiesFilePath = "Cookies/Cookies.binarycookies";
115 return path.Append(kCookiesFilePath);
118 // Clears all cookies from the .binarycookies file.
119 // Must be called from a thread where IO operations are allowed.
120 // Preconditions: There must be no active WKWebViews present in the app.
121 void ClearAllCookiesFromBinaryCookiesFile() {
122 // The .binarycookies file is present only on iOS8+.
123 if (!base::ios::IsRunningOnIOS8OrLater()) {
126 base::FilePath path = GetBinaryCookiesFilePath();
127 if (base::PathExists(path)) {
128 bool success = base::DeleteFile(path, false);
130 DLOG(WARNING) << "Failed to remove binarycookies file.";
133 // TODO(shreyasv): Should .binarycookies be parsed to find out how many
134 // more cookies are deleted? Investigate further if the accuracy of this
135 // actually matters to the callback.
138 // Builds a NSHTTPCookie from a header cookie line ("Set-Cookie: xxx") and a
140 NSHTTPCookie* GetNSHTTPCookieFromCookieLine(const std::string& cookie_line,
142 base::Time server_time) {
143 NSURL* nsurl = net::NSURLWithGURL(url);
144 NSString* ns_cookie_line = base::SysUTF8ToNSString(cookie_line);
145 if (!ns_cookie_line) {
146 DLOG(ERROR) << "Cookie line is not UTF8: " << cookie_line;
149 NSArray* cookies = [NSHTTPCookie cookiesWithResponseHeaderFields:@{
150 @"Set-Cookie" : ns_cookie_line
152 if ([cookies count] != 1)
155 NSHTTPCookie* cookie = [cookies objectAtIndex:0];
156 if (![cookie expiresDate] || server_time.is_null())
159 // Perform clock skew correction.
160 base::TimeDelta clock_skew = base::Time::Now() - server_time;
161 NSDate* corrected_expire_date =
162 [[cookie expiresDate] dateByAddingTimeInterval:clock_skew.InSecondsF()];
163 NSMutableDictionary* properties =
164 [NSMutableDictionary dictionaryWithDictionary:[cookie properties]];
165 [properties setObject:corrected_expire_date forKey:NSHTTPCookieExpires];
166 NSHTTPCookie* corrected_cookie =
167 [NSHTTPCookie cookieWithProperties:properties];
168 DCHECK(corrected_cookie);
169 return corrected_cookie;
172 // Compares cookies based on the path lengths and the creation times, as per
174 NSInteger CompareCookies(id a, id b, void* context) {
175 NSHTTPCookie* cookie_a = (NSHTTPCookie*)a;
176 NSHTTPCookie* cookie_b = (NSHTTPCookie*)b;
177 // Compare path lengths first.
178 NSUInteger path_length_a = [[cookie_a path] length];
179 NSUInteger path_length_b = [[cookie_b path] length];
180 if (path_length_a < path_length_b)
181 return NSOrderedDescending;
182 if (path_length_b < path_length_a)
183 return NSOrderedAscending;
185 // Compare creation times.
186 CookieCreationTimeManager* manager = (CookieCreationTimeManager*)context;
188 base::Time created_a = manager->GetCreationTime(cookie_a);
189 base::Time created_b = manager->GetCreationTime(cookie_b);
191 // CookieCreationTimeManager is returning creation times that are null.
192 // Since in CrNet, the cookie store is recreated on startup, let's suppress
193 // this warning for now.
194 // TODO(huey): Instead of suppressing the warning, assign a creation time
195 // to cookies if one doesn't already exist.
196 DLOG_IF(ERROR, created_a.is_null() || created_b.is_null())
197 << "Cookie without creation date";
199 if (created_a < created_b)
200 return NSOrderedAscending;
201 return (created_a > created_b) ? NSOrderedDescending : NSOrderedSame;
204 // Gets the cookies for |url| from the system cookie store.
205 NSArray* GetCookiesForURL(const GURL& url, CookieCreationTimeManager* manager) {
206 NSArray* cookies = [[NSHTTPCookieStorage sharedHTTPCookieStorage]
207 cookiesForURL:net::NSURLWithGURL(url)];
209 // Sort cookies by decreasing path length, then creation time, as per RFC6265.
210 return [cookies sortedArrayUsingFunction:CompareCookies context:manager];
213 // Builds a cookie line (such as "key1=value1; key2=value2") from an array of
215 std::string BuildCookieLine(NSArray* cookies,
216 const net::CookieOptions& options) {
217 // The exclude_httponly() option would only be used by a javascript engine.
218 DCHECK(!options.exclude_httponly());
220 // This utility function returns all the cookies, including the httponly ones.
221 // This is fine because we don't support the exclude_httponly option.
222 NSDictionary* header = [NSHTTPCookie requestHeaderFieldsWithCookies:cookies];
223 return base::SysNSStringToUTF8([header valueForKey:@"Cookie"]);
226 // Tests whether the |cookie| is a session cookie.
227 bool IsCookieSessionCookie(NSHTTPCookie* cookie, base::Time time) {
228 return [cookie isSessionOnly];
231 // Tests whether the |creation_time| of |cookie| is in the time range defined
232 // by |time_begin| and |time_end|. A null |time_end| means end-of-time.
233 bool IsCookieCreatedBetween(base::Time time_begin,
235 NSHTTPCookie* cookie,
236 base::Time creation_time) {
237 return time_begin <= creation_time &&
238 (time_end.is_null() || creation_time <= time_end);
241 // Tests whether the |creation_time| of |cookie| is in the time range defined
242 // by |time_begin| and |time_end| and the cookie host match |host|. A null
243 // |time_end| means end-of-time.
244 bool IsCookieCreatedBetweenForHost(base::Time time_begin,
247 NSHTTPCookie* cookie,
248 base::Time creation_time) {
249 NSString* domain = [cookie domain];
250 return [domain characterAtIndex:0] != '.' &&
251 [domain caseInsensitiveCompare:host] == NSOrderedSame &&
252 IsCookieCreatedBetween(time_begin, time_end, cookie, creation_time);
255 // Adds cookies in |cookies| with name |name| to |filtered|.
256 void OnlyCookiesWithName(const net::CookieList& cookies,
257 const std::string& name,
258 net::CookieList* filtered) {
259 for (const auto& cookie : cookies) {
260 if (cookie.Name() == name)
261 filtered->push_back(cookie);
268 #pragma mark CookieStoreIOS
270 CookieStoreIOS::CookieStoreIOS(
271 net::CookieMonster::PersistentCookieStore* persistent_store)
272 : creation_time_manager_(new CookieCreationTimeManager),
273 metrics_enabled_(false),
274 flush_delay_(base::TimeDelta::FromSeconds(10)),
275 synchronization_state_(NOT_SYNCHRONIZED),
276 cookie_cache_(new CookieCache()) {
277 NotificationTrampoline::GetInstance()->AddObserver(this);
278 cookie_monster_ = new net::CookieMonster(persistent_store, nullptr);
279 cookie_monster_->SetPersistSessionCookies(true);
280 cookie_monster_->SetForceKeepSessionState();
284 void CookieStoreIOS::SetCookiePolicy(CookiePolicy setting) {
285 NSHTTPCookieAcceptPolicy policy = (setting == ALLOW)
286 ? NSHTTPCookieAcceptPolicyAlways
287 : NSHTTPCookieAcceptPolicyNever;
288 NSHTTPCookieStorage* store = [NSHTTPCookieStorage sharedHTTPCookieStorage];
289 NSHTTPCookieAcceptPolicy current_policy = [store cookieAcceptPolicy];
290 if (current_policy == policy)
292 [store setCookieAcceptPolicy:policy];
293 NotificationTrampoline::GetInstance()->NotifyCookiePolicyChanged();
296 CookieStoreIOS* CookieStoreIOS::CreateCookieStoreFromNSHTTPCookieStorage() {
297 // TODO(huey): Update this when CrNet supports multiple cookie jars.
298 [[NSHTTPCookieStorage sharedHTTPCookieStorage]
299 setCookieAcceptPolicy:NSHTTPCookieAcceptPolicyAlways];
301 // Create a cookie store with no persistent store backing. Then, populate
302 // it from the system's cookie jar.
303 CookieStoreIOS* cookie_store = new CookieStoreIOS(nullptr);
304 cookie_store->synchronization_state_ = SYNCHRONIZED;
305 cookie_store->Flush(base::Closure());
310 void CookieStoreIOS::SwitchSynchronizedStore(CookieStoreIOS* old_store,
311 CookieStoreIOS* new_store) {
313 DCHECK_NE(new_store, old_store);
315 old_store->SetSynchronizedWithSystemStore(false);
316 new_store->SetSynchronizedWithSystemStore(true);
320 void CookieStoreIOS::NotifySystemCookiesChanged() {
321 NotificationTrampoline::GetInstance()->NotifyCookiesChanged();
324 void CookieStoreIOS::Flush(const base::Closure& closure) {
325 DCHECK(thread_checker_.CalledOnValidThread());
327 if (SystemCookiesAllowed()) {
328 // If cookies are disabled, the system store is empty, and the cookies are
329 // stashed on disk. Do not delete the cookies on the disk in this case.
330 WriteToCookieMonster(
331 [[NSHTTPCookieStorage sharedHTTPCookieStorage] cookies]);
333 cookie_monster_->FlushStore(closure);
334 flush_closure_.Cancel();
337 void CookieStoreIOS::UnSynchronize() {
338 SetSynchronizedWithSystemStore(false);
341 void CookieStoreIOS::SetMetricsEnabled() {
342 static CookieStoreIOS* g_cookie_store_with_metrics = nullptr;
343 DCHECK(!g_cookie_store_with_metrics || g_cookie_store_with_metrics == this)
344 << "Only one cookie store may use metrics.";
345 g_cookie_store_with_metrics = this;
346 metrics_enabled_ = true;
350 #pragma mark CookieStore methods
352 void CookieStoreIOS::SetCookieWithOptionsAsync(
354 const std::string& cookie_line,
355 const net::CookieOptions& options,
356 const SetCookiesCallback& callback) {
357 DCHECK(thread_checker_.CalledOnValidThread());
359 switch (synchronization_state_) {
360 case NOT_SYNCHRONIZED:
361 cookie_monster_->SetCookieWithOptionsAsync(url, cookie_line, options,
362 WrapSetCallback(callback));
365 tasks_pending_synchronization_.push_back(
366 base::Bind(&CookieStoreIOS::SetCookieWithOptionsAsync, this, url,
367 cookie_line, options, WrapSetCallback(callback)));
370 // The exclude_httponly() option would only be used by a javascript
372 DCHECK(!options.exclude_httponly());
373 // If cookies are not allowed, they are stashed in the CookieMonster, and
374 // should be written there instead.
375 DCHECK(SystemCookiesAllowed());
377 base::Time server_time =
378 options.has_server_time() ? options.server_time() : base::Time();
379 NSHTTPCookie* cookie =
380 GetNSHTTPCookieFromCookieLine(cookie_line, url, server_time);
381 DLOG_IF(WARNING, !cookie)
382 << "Could not create cookie for line: " << cookie_line;
384 // On iOS, [cookie domain] is not empty when the cookie domain is not
385 // specified: it is inferred from the URL instead.
386 // That is why two specific cases are tested here:
387 // - url_host == domain_string, happens when the cookie has no domain
388 // - domain_string.empty(), happens when the domain is not formatted
390 std::string domain_string(base::SysNSStringToUTF8([cookie domain]));
391 const std::string url_host(url.host());
393 bool success = (cookie != nil) && !domain_string.empty() &&
394 (url_host == domain_string ||
395 net::cookie_util::GetCookieDomainWithString(
396 url, domain_string, &dummy));
399 [[NSHTTPCookieStorage sharedHTTPCookieStorage] setCookie:cookie];
400 creation_time_manager_->SetCreationTime(
402 creation_time_manager_->MakeUniqueCreationTime(base::Time::Now()));
405 if (!callback.is_null())
406 callback.Run(success);
411 void CookieStoreIOS::GetCookiesWithOptionsAsync(
413 const net::CookieOptions& options,
414 const GetCookiesCallback& callback) {
415 DCHECK(thread_checker_.CalledOnValidThread());
417 switch (synchronization_state_) {
418 case NOT_SYNCHRONIZED:
419 cookie_monster_->GetCookiesWithOptionsAsync(url, options, callback);
422 tasks_pending_synchronization_.push_back(
423 base::Bind(&CookieStoreIOS::GetCookiesWithOptionsAsync, this, url,
427 // If cookies are not allowed, they are stashed in the CookieMonster, and
428 // should be read from there instead.
429 DCHECK(SystemCookiesAllowed());
430 // The exclude_httponly() option would only be used by a javascript
432 DCHECK(!options.exclude_httponly());
434 NSArray* cookies = GetCookiesForURL(url, creation_time_manager_.get());
435 if (!callback.is_null())
436 callback.Run(BuildCookieLine(cookies, options));
441 void CookieStoreIOS::GetAllCookiesForURLAsync(
443 const GetCookieListCallback& callback) {
444 DCHECK(thread_checker_.CalledOnValidThread());
446 switch (synchronization_state_) {
447 case NOT_SYNCHRONIZED:
448 cookie_monster_->GetAllCookiesForURLAsync(url, callback);
451 tasks_pending_synchronization_.push_back(base::Bind(
452 &CookieStoreIOS::GetAllCookiesForURLAsync, this, url, callback));
455 if (!SystemCookiesAllowed()) {
456 // If cookies are not allowed, the cookies are stashed in the
457 // CookieMonster, so get them from there.
458 cookie_monster_->GetAllCookiesForURLAsync(url, callback);
462 NSArray* cookies = GetCookiesForURL(url, creation_time_manager_.get());
463 net::CookieList cookie_list;
464 cookie_list.reserve([cookies count]);
465 for (NSHTTPCookie* cookie in cookies) {
466 base::Time created = creation_time_manager_->GetCreationTime(cookie);
467 cookie_list.push_back(CanonicalCookieFromSystemCookie(cookie, created));
469 if (!callback.is_null())
470 callback.Run(cookie_list);
475 void CookieStoreIOS::DeleteCookieAsync(const GURL& url,
476 const std::string& cookie_name,
477 const base::Closure& callback) {
478 DCHECK(thread_checker_.CalledOnValidThread());
480 switch (synchronization_state_) {
481 case NOT_SYNCHRONIZED:
482 cookie_monster_->DeleteCookieAsync(url, cookie_name,
483 WrapClosure(callback));
486 tasks_pending_synchronization_.push_back(
487 base::Bind(&CookieStoreIOS::DeleteCookieAsync, this, url, cookie_name,
488 WrapClosure(callback)));
491 NSArray* cookies = GetCookiesForURL(url, creation_time_manager_.get());
492 for (NSHTTPCookie* cookie in cookies) {
494 isEqualToString:base::SysUTF8ToNSString(cookie_name)]) {
495 [[NSHTTPCookieStorage sharedHTTPCookieStorage] deleteCookie:cookie];
496 creation_time_manager_->DeleteCreationTime(cookie);
499 if (!callback.is_null())
505 // CookieStoreIOS is an implementation of CookieStore which is not a
506 // CookieMonster. As CookieStore is the main cookie API, a caller of
507 // GetCookieMonster must handle the case where this returns null.
508 net::CookieMonster* CookieStoreIOS::GetCookieMonster() {
509 DCHECK(thread_checker_.CalledOnValidThread());
513 void CookieStoreIOS::DeleteAllCreatedBetweenAsync(
514 const base::Time& delete_begin,
515 const base::Time& delete_end,
516 const DeleteCallback& callback) {
517 DCHECK(thread_checker_.CalledOnValidThread());
519 if (metrics_enabled_)
520 ResetCookieCountMetrics();
522 switch (synchronization_state_) {
523 case NOT_SYNCHRONIZED:
524 cookie_monster_->DeleteAllCreatedBetweenAsync(
525 delete_begin, delete_end, WrapDeleteCallback(callback));
528 tasks_pending_synchronization_.push_back(
529 base::Bind(&CookieStoreIOS::DeleteAllCreatedBetweenAsync, this,
530 delete_begin, delete_end, WrapDeleteCallback(callback)));
533 CookieFilterFunction filter =
534 base::Bind(&IsCookieCreatedBetween, delete_begin, delete_end);
535 DeleteCookiesWithFilter(filter, callback);
540 void CookieStoreIOS::DeleteAllCreatedBetweenForHostAsync(
541 const base::Time delete_begin,
542 const base::Time delete_end,
544 const DeleteCallback& callback) {
545 DCHECK(thread_checker_.CalledOnValidThread());
547 if (metrics_enabled_)
548 ResetCookieCountMetrics();
550 switch (synchronization_state_) {
551 case NOT_SYNCHRONIZED:
552 cookie_monster_->DeleteAllCreatedBetweenForHostAsync(
553 delete_begin, delete_end, url, WrapDeleteCallback(callback));
556 tasks_pending_synchronization_.push_back(base::Bind(
557 &CookieStoreIOS::DeleteAllCreatedBetweenForHostAsync, this,
558 delete_begin, delete_end, url, WrapDeleteCallback(callback)));
561 NSString* host = base::SysUTF8ToNSString(url.host());
562 CookieFilterFunction filter = base::Bind(IsCookieCreatedBetweenForHost,
563 delete_begin, delete_end, host);
564 DeleteCookiesWithFilter(filter, callback);
569 void CookieStoreIOS::DeleteSessionCookiesAsync(const DeleteCallback& callback) {
570 DCHECK(thread_checker_.CalledOnValidThread());
572 if (metrics_enabled_)
573 ResetCookieCountMetrics();
575 switch (synchronization_state_) {
576 case NOT_SYNCHRONIZED:
577 cookie_monster_->DeleteSessionCookiesAsync(WrapDeleteCallback(callback));
580 tasks_pending_synchronization_.push_back(
581 base::Bind(&CookieStoreIOS::DeleteSessionCookiesAsync, this,
582 WrapDeleteCallback(callback)));
585 CookieFilterFunction filter = base::Bind(&IsCookieSessionCookie);
586 DeleteCookiesWithFilter(filter, callback);
592 #pragma mark Protected methods
594 CookieStoreIOS::~CookieStoreIOS() {
595 NotificationTrampoline::GetInstance()->RemoveObserver(this);
596 STLDeleteContainerPairSecondPointers(hook_map_.begin(), hook_map_.end());
600 #pragma mark Private methods
602 void CookieStoreIOS::ClearSystemStore() {
603 DCHECK(thread_checker_.CalledOnValidThread());
604 NSHTTPCookieStorage* cookie_storage =
605 [NSHTTPCookieStorage sharedHTTPCookieStorage];
606 base::scoped_nsobject<NSArray> copy(
607 [[NSArray alloc] initWithArray:[cookie_storage cookies]]);
608 for (NSHTTPCookie* cookie in copy.get())
609 [cookie_storage deleteCookie:cookie];
610 DCHECK_EQ(0u, [[cookie_storage cookies] count]);
611 creation_time_manager_->Clear();
614 void CookieStoreIOS::OnSystemCookiePolicyChanged() {
615 DCHECK(thread_checker_.CalledOnValidThread());
617 if (synchronization_state_ == NOT_SYNCHRONIZED)
620 // If the state is SYNCHRONIZING, this function will be a no-op because
621 // AddCookiesToSystemStore() will return early.
623 NSHTTPCookieAcceptPolicy policy =
624 [[NSHTTPCookieStorage sharedHTTPCookieStorage] cookieAcceptPolicy];
625 if (policy == NSHTTPCookieAcceptPolicyAlways) {
626 // If cookies are disabled, the system cookie store should be empty.
627 DCHECK(![[[NSHTTPCookieStorage sharedHTTPCookieStorage] cookies] count]);
628 synchronization_state_ = SYNCHRONIZING;
629 cookie_monster_->GetAllCookiesAsync(
630 base::Bind(&CookieStoreIOS::AddCookiesToSystemStore, this));
632 DCHECK_EQ(NSHTTPCookieAcceptPolicyNever, policy);
633 // Flush() does not write the cookies to disk when they are disabled.
634 // Explicitly copy them.
635 WriteToCookieMonster(
636 [[NSHTTPCookieStorage sharedHTTPCookieStorage] cookies]);
637 Flush(base::Closure());
642 void CookieStoreIOS::SetSynchronizedWithSystemStore(bool synchronized) {
643 DCHECK(thread_checker_.CalledOnValidThread());
645 if (synchronized == (synchronization_state_ != NOT_SYNCHRONIZED))
646 return; // The cookie store is already in the desired state.
650 DCHECK_EQ(this, g_current_synchronized_store)
651 << "This cookie store was not synchronized";
652 g_current_synchronized_store = nullptr;
654 DCHECK_EQ((CookieStoreIOS*)nullptr, g_current_synchronized_store)
655 << "Un-synchronize the current cookie store first.";
656 g_current_synchronized_store = this;
660 NSHTTPCookieAcceptPolicy policy =
661 [[NSHTTPCookieStorage sharedHTTPCookieStorage] cookieAcceptPolicy];
662 DCHECK(policy == NSHTTPCookieAcceptPolicyAlways ||
663 policy == NSHTTPCookieAcceptPolicyNever);
665 // If cookies are disabled, the system cookie store should be empty.
666 DCHECK(policy == NSHTTPCookieAcceptPolicyAlways ||
667 ![[[NSHTTPCookieStorage sharedHTTPCookieStorage] cookies] count]);
669 // If cookies are disabled, nothing is done now, the work will be done when
670 // cookies are re-enabled.
671 if (policy == NSHTTPCookieAcceptPolicyAlways) {
673 synchronization_state_ = SYNCHRONIZING;
675 cookie_monster_->GetAllCookiesAsync(
676 base::Bind(&CookieStoreIOS::AddCookiesToSystemStore, this));
679 // Copy the cookies from the global store to |cookie_monster_|.
680 Flush(base::Closure());
683 synchronization_state_ = synchronized ? SYNCHRONIZED : NOT_SYNCHRONIZED;
686 bool CookieStoreIOS::SystemCookiesAllowed() {
687 DCHECK(thread_checker_.CalledOnValidThread());
688 return [[NSHTTPCookieStorage sharedHTTPCookieStorage] cookieAcceptPolicy] ==
689 NSHTTPCookieAcceptPolicyAlways;
692 void CookieStoreIOS::AddCookiesToSystemStore(const net::CookieList& cookies) {
693 DCHECK(thread_checker_.CalledOnValidThread());
694 if (!SystemCookiesAllowed() || synchronization_state_ != SYNCHRONIZING) {
695 // The case where synchronization aborts while there are pending tasks is
697 // Note: it should be ok to drop the tasks here (at least if cookies are not
699 DCHECK(tasks_pending_synchronization_.empty());
704 if (metrics_enabled_) {
705 size_t cookie_count = cookies.size();
706 UMA_HISTOGRAM_COUNTS_10000("CookieIOS.CookieReadCount", cookie_count);
707 CheckForCookieLoss(cookie_count, COOKIES_READ);
710 net::CookieList::const_iterator it;
711 for (it = cookies.begin(); it != cookies.end(); ++it) {
712 const net::CanonicalCookie& net_cookie = *it;
713 NSHTTPCookie* system_cookie = SystemCookieFromCanonicalCookie(net_cookie);
714 // Canonical cookie may not be convertable into system cookie if it contains
715 // invalid characters.
718 [[NSHTTPCookieStorage sharedHTTPCookieStorage] setCookie:system_cookie];
719 creation_time_manager_->SetCreationTime(system_cookie,
720 net_cookie.CreationDate());
723 synchronization_state_ = SYNCHRONIZED;
724 // Run all the pending tasks.
725 for (const auto& task : tasks_pending_synchronization_) {
728 tasks_pending_synchronization_.clear();
731 void CookieStoreIOS::WriteToCookieMonster(NSArray* system_cookies) {
732 DCHECK(thread_checker_.CalledOnValidThread());
733 if (synchronization_state_ != SYNCHRONIZED)
736 // Copy the cookies from the global cookie store to |cookie_monster_|.
737 // Unlike the system store, CookieMonster requires unique creation times.
738 net::CookieList cookie_list;
739 NSUInteger cookie_count = [system_cookies count];
740 cookie_list.reserve(cookie_count);
741 for (NSHTTPCookie* cookie in system_cookies) {
742 cookie_list.push_back(CanonicalCookieFromSystemCookie(
743 cookie, creation_time_manager_->GetCreationTime(cookie)));
745 cookie_monster_->SetAllCookiesAsync(cookie_list, SetCookiesCallback());
748 if (metrics_enabled_)
749 UMA_HISTOGRAM_COUNTS_10000("CookieIOS.CookieWrittenCount", cookie_count);
752 void CookieStoreIOS::DeleteCookiesWithFilter(const CookieFilterFunction& filter,
753 const DeleteCallback& callback) {
754 DCHECK(thread_checker_.CalledOnValidThread());
755 DCHECK_EQ(SYNCHRONIZED, synchronization_state_);
756 NSArray* cookies = [[NSHTTPCookieStorage sharedHTTPCookieStorage] cookies];
758 // Collect the cookies to delete.
759 base::scoped_nsobject<NSMutableArray> to_delete(
760 [[NSMutableArray alloc] init]);
761 for (NSHTTPCookie* cookie in cookies) {
762 base::Time creation_time = creation_time_manager_->GetCreationTime(cookie);
763 if (filter.Run(cookie, creation_time))
764 [to_delete addObject:cookie];
768 for (NSHTTPCookie* cookie in to_delete.get()) {
769 [[NSHTTPCookieStorage sharedHTTPCookieStorage] deleteCookie:cookie];
770 creation_time_manager_->DeleteCreationTime(cookie);
773 if (!callback.is_null())
774 callback.Run([to_delete count]);
777 void CookieStoreIOS::OnSystemCookiesChanged() {
778 DCHECK(thread_checker_.CalledOnValidThread());
780 // If the CookieStoreIOS is not synchronized, system cookies are irrelevant.
781 if (synchronization_state_ != SYNCHRONIZED)
784 for (const auto& hook_map_entry : hook_map_) {
785 std::pair<GURL, std::string> key = hook_map_entry.first;
786 std::vector<net::CanonicalCookie> removed_cookies;
787 std::vector<net::CanonicalCookie> added_cookies;
788 if (UpdateCacheForCookieFromSystem(key.first, key.second, &removed_cookies,
790 RunCallbacksForCookies(key.first, key.second, removed_cookies, true);
791 RunCallbacksForCookies(key.first, key.second, added_cookies, false);
795 // Do not schedule a flush if one is already scheduled.
796 if (!flush_closure_.IsCancelled())
799 flush_closure_.Reset(base::Bind(&CookieStoreIOS::Flush,
800 base::Unretained(this), base::Closure()));
801 base::MessageLoopProxy::current()->PostDelayedTask(
802 FROM_HERE, flush_closure_.callback(), flush_delay_);
805 scoped_ptr<net::CookieStore::CookieChangedSubscription>
806 CookieStoreIOS::AddCallbackForCookie(const GURL& gurl,
807 const std::string& name,
808 const CookieChangedCallback& callback) {
809 DCHECK(thread_checker_.CalledOnValidThread());
811 // Prefill cookie cache with all pertinent cookies for |url| if needed.
812 std::pair<GURL, std::string> key(gurl, name);
813 if (hook_map_.count(key) == 0) {
814 UpdateCacheForCookieFromSystem(gurl, name, nullptr, nullptr);
815 if (hook_map_.count(key) == 0)
816 hook_map_[key] = new CookieChangedCallbackList;
819 DCHECK(hook_map_.find(key) != hook_map_.end());
820 return hook_map_[key]->Add(callback);
823 bool CookieStoreIOS::UpdateCacheForCookieFromSystem(
825 const std::string& name,
826 std::vector<net::CanonicalCookie>* out_removed_cookies,
827 std::vector<net::CanonicalCookie>* out_added_cookies) {
828 DCHECK(thread_checker_.CalledOnValidThread());
829 std::vector<net::CanonicalCookie> system_cookies;
830 GetSystemCookies(gurl, name, &system_cookies);
831 return cookie_cache_->Update(gurl, name, system_cookies, out_removed_cookies,
835 void CookieStoreIOS::RunCallbacksForCookies(
837 const std::string& name,
838 const std::vector<net::CanonicalCookie>& cookies,
840 DCHECK(thread_checker_.CalledOnValidThread());
844 std::pair<GURL, std::string> key(url, name);
845 CookieChangedCallbackList* callbacks = hook_map_[key];
846 for (const auto& cookie : cookies) {
847 DCHECK_EQ(name, cookie.Name());
848 callbacks->Notify(cookie, removed);
852 bool CookieStoreIOS::GetSystemCookies(
854 const std::string& name,
855 std::vector<net::CanonicalCookie>* cookies) {
856 DCHECK(thread_checker_.CalledOnValidThread());
857 NSURL* url = net::NSURLWithGURL(gurl);
858 NSHTTPCookieStorage* storage = [NSHTTPCookieStorage sharedHTTPCookieStorage];
859 NSArray* nscookies = [storage cookiesForURL:url];
860 bool found_cookies = false;
861 for (NSHTTPCookie* nscookie in nscookies) {
862 if (nscookie.name.UTF8String == name) {
863 net::CanonicalCookie canonical_cookie = CanonicalCookieFromSystemCookie(
864 nscookie, creation_time_manager_->GetCreationTime(nscookie));
865 cookies->push_back(canonical_cookie);
866 found_cookies = true;
869 return found_cookies;
872 void CookieStoreIOS::GotCookieListFor(const std::pair<GURL, std::string> key,
873 const net::CookieList& cookies) {
874 DCHECK(thread_checker_.CalledOnValidThread());
876 net::CookieList filtered;
877 OnlyCookiesWithName(cookies, key.second, &filtered);
878 std::vector<net::CanonicalCookie> removed_cookies;
879 std::vector<net::CanonicalCookie> added_cookies;
880 if (cookie_cache_->Update(key.first, key.second, filtered, &removed_cookies,
882 RunCallbacksForCookies(key.first, key.second, removed_cookies, true);
883 RunCallbacksForCookies(key.first, key.second, added_cookies, false);
887 void CookieStoreIOS::DidClearNSHTTPCookieStorageCookies(
888 const DeleteCallback& delete_callback,
890 DCHECK(thread_checker_.CalledOnValidThread());
892 CookieStoreIOSClient* client = net::GetCookieStoreIOSClient();
894 auto sequenced_task_runner = client->GetTaskRunner();
895 DCHECK(sequenced_task_runner);
896 auto callback = base::Bind(&CookieStoreIOS::DidClearBinaryCookiesFileCookies,
897 this, delete_callback, num_deleted);
898 sequenced_task_runner.get()->PostTaskAndReply(
899 FROM_HERE, base::Bind(&ClearAllCookiesFromBinaryCookiesFile), callback);
902 void CookieStoreIOS::DidClearBinaryCookiesFileCookies(
903 const DeleteCallback& callback,
904 int num_deleted_from_nshttp_cookie_storage) {
905 DCHECK(thread_checker_.CalledOnValidThread());
907 CookieStoreIOSClient* client = net::GetCookieStoreIOSClient();
909 client->DidChangeCookieStorage();
910 callback.Run(num_deleted_from_nshttp_cookie_storage);
913 void CookieStoreIOS::UpdateCachesFromCookieMonster() {
914 DCHECK(thread_checker_.CalledOnValidThread());
915 for (const auto& hook_map_entry : hook_map_) {
916 std::pair<GURL, std::string> key = hook_map_entry.first;
917 GetCookieListCallback callback =
918 base::Bind(&CookieStoreIOS::GotCookieListFor, this, key);
919 cookie_monster_->GetAllCookiesForURLAsync(key.first, callback);
923 void CookieStoreIOS::UpdateCachesAfterSet(const SetCookiesCallback& callback,
925 DCHECK(thread_checker_.CalledOnValidThread());
927 UpdateCachesFromCookieMonster();
928 callback.Run(success);
931 void CookieStoreIOS::UpdateCachesAfterDelete(const DeleteCallback& callback,
933 DCHECK(thread_checker_.CalledOnValidThread());
934 UpdateCachesFromCookieMonster();
935 callback.Run(num_deleted);
938 void CookieStoreIOS::UpdateCachesAfterClosure(const base::Closure& callback) {
939 DCHECK(thread_checker_.CalledOnValidThread());
940 UpdateCachesFromCookieMonster();
944 CookieStoreIOS::SetCookiesCallback CookieStoreIOS::WrapSetCallback(
945 const SetCookiesCallback& callback) {
946 DCHECK(thread_checker_.CalledOnValidThread());
947 return base::Bind(&CookieStoreIOS::UpdateCachesAfterSet, this, callback);
950 CookieStoreIOS::DeleteCallback CookieStoreIOS::WrapDeleteCallback(
951 const DeleteCallback& callback) {
952 DCHECK(thread_checker_.CalledOnValidThread());
953 return base::Bind(&CookieStoreIOS::UpdateCachesAfterDelete, this, callback);
956 base::Closure CookieStoreIOS::WrapClosure(const base::Closure& callback) {
957 DCHECK(thread_checker_.CalledOnValidThread());
958 return base::Bind(&CookieStoreIOS::UpdateCachesAfterClosure, this, callback);