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/metrics/histogram.h"
19 #include "base/observer_list.h"
20 #include "base/sequenced_task_runner.h"
21 #include "base/stl_util.h"
22 #include "base/strings/sys_string_conversions.h"
23 #include "base/task_runner_util.h"
24 #include "base/thread_task_runner_handle.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 base::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 NSHTTPCookieAcceptPolicy policy =
621 [[NSHTTPCookieStorage sharedHTTPCookieStorage] cookieAcceptPolicy];
622 if (policy == NSHTTPCookieAcceptPolicyAlways) {
623 // If cookies are disabled, the system cookie store should be empty.
624 DCHECK(![[[NSHTTPCookieStorage sharedHTTPCookieStorage] cookies] count]);
625 DCHECK(synchronization_state_ != SYNCHRONIZING);
626 synchronization_state_ = SYNCHRONIZING;
627 cookie_monster_->GetAllCookiesAsync(
628 base::Bind(&CookieStoreIOS::AddCookiesToSystemStore, this));
630 DCHECK_EQ(NSHTTPCookieAcceptPolicyNever, policy);
631 // Flush() does not write the cookies to disk when they are disabled.
632 // Explicitly copy them.
633 WriteToCookieMonster(
634 [[NSHTTPCookieStorage sharedHTTPCookieStorage] cookies]);
635 Flush(base::Closure());
637 if (synchronization_state_ == SYNCHRONIZING) {
638 // If synchronization was in progress, abort it and leave the cookie store
640 // Temporarily toggle the synchronization state so that pending tasks are
641 // redirected to cookie_monster_ and can complete normally.
642 synchronization_state_ = NOT_SYNCHRONIZED;
643 RunAllPendingTasks();
644 synchronization_state_ = SYNCHRONIZED;
649 void CookieStoreIOS::SetSynchronizedWithSystemStore(bool synchronized) {
650 DCHECK(thread_checker_.CalledOnValidThread());
652 if (synchronized == (synchronization_state_ != NOT_SYNCHRONIZED))
653 return; // The cookie store is already in the desired state.
657 DCHECK_EQ(this, g_current_synchronized_store)
658 << "This cookie store was not synchronized";
659 g_current_synchronized_store = nullptr;
661 DCHECK_EQ((CookieStoreIOS*)nullptr, g_current_synchronized_store)
662 << "Un-synchronize the current cookie store first.";
663 g_current_synchronized_store = this;
667 NSHTTPCookieAcceptPolicy policy =
668 [[NSHTTPCookieStorage sharedHTTPCookieStorage] cookieAcceptPolicy];
669 DCHECK(policy == NSHTTPCookieAcceptPolicyAlways ||
670 policy == NSHTTPCookieAcceptPolicyNever);
672 // If cookies are disabled, the system cookie store should be empty.
673 DCHECK(policy == NSHTTPCookieAcceptPolicyAlways ||
674 ![[[NSHTTPCookieStorage sharedHTTPCookieStorage] cookies] count]);
676 // If cookies are disabled, nothing is done now, the work will be done when
677 // cookies are re-enabled.
678 if (policy == NSHTTPCookieAcceptPolicyAlways) {
680 synchronization_state_ = SYNCHRONIZING;
682 cookie_monster_->GetAllCookiesAsync(
683 base::Bind(&CookieStoreIOS::AddCookiesToSystemStore, this));
686 // Copy the cookies from the global store to |cookie_monster_|.
687 Flush(base::Closure());
690 synchronization_state_ = synchronized ? SYNCHRONIZED : NOT_SYNCHRONIZED;
692 if (synchronization_state_ == NOT_SYNCHRONIZED) {
693 // If there are pending tasks, then it means that the synchronization is
694 // being canceled. All pending tasks can be sent to cookie_monster_.
695 RunAllPendingTasks();
699 bool CookieStoreIOS::SystemCookiesAllowed() {
700 DCHECK(thread_checker_.CalledOnValidThread());
701 return [[NSHTTPCookieStorage sharedHTTPCookieStorage] cookieAcceptPolicy] ==
702 NSHTTPCookieAcceptPolicyAlways;
705 void CookieStoreIOS::AddCookiesToSystemStore(const net::CookieList& cookies) {
706 DCHECK(thread_checker_.CalledOnValidThread());
707 if (!SystemCookiesAllowed() || synchronization_state_ != SYNCHRONIZING) {
708 // If synchronization was aborted, the pending tasks have been processed at
709 // that time. Now is too late.
710 DCHECK(tasks_pending_synchronization_.empty());
715 if (metrics_enabled_) {
716 size_t cookie_count = cookies.size();
717 UMA_HISTOGRAM_COUNTS_10000("CookieIOS.CookieReadCount", cookie_count);
718 CheckForCookieLoss(cookie_count, COOKIES_READ);
721 net::CookieList::const_iterator it;
722 for (it = cookies.begin(); it != cookies.end(); ++it) {
723 const net::CanonicalCookie& net_cookie = *it;
724 NSHTTPCookie* system_cookie = SystemCookieFromCanonicalCookie(net_cookie);
725 // Canonical cookie may not be convertable into system cookie if it contains
726 // invalid characters.
729 [[NSHTTPCookieStorage sharedHTTPCookieStorage] setCookie:system_cookie];
730 creation_time_manager_->SetCreationTime(system_cookie,
731 net_cookie.CreationDate());
734 synchronization_state_ = SYNCHRONIZED;
735 RunAllPendingTasks();
738 void CookieStoreIOS::WriteToCookieMonster(NSArray* system_cookies) {
739 DCHECK(thread_checker_.CalledOnValidThread());
740 if (synchronization_state_ != SYNCHRONIZED)
743 // Copy the cookies from the global cookie store to |cookie_monster_|.
744 // Unlike the system store, CookieMonster requires unique creation times.
745 net::CookieList cookie_list;
746 NSUInteger cookie_count = [system_cookies count];
747 cookie_list.reserve(cookie_count);
748 for (NSHTTPCookie* cookie in system_cookies) {
749 cookie_list.push_back(CanonicalCookieFromSystemCookie(
750 cookie, creation_time_manager_->GetCreationTime(cookie)));
752 cookie_monster_->SetAllCookiesAsync(cookie_list, SetCookiesCallback());
755 if (metrics_enabled_)
756 UMA_HISTOGRAM_COUNTS_10000("CookieIOS.CookieWrittenCount", cookie_count);
759 void CookieStoreIOS::RunAllPendingTasks() {
760 // Executing the tasks while synchronizing would not run the tasks, but merely
761 // re-enqueue them. This function also does not support mutation of the queue
762 // during the iteration.
763 DCHECK(synchronization_state_ != SYNCHRONIZING);
764 for (const auto& task : tasks_pending_synchronization_) {
767 tasks_pending_synchronization_.clear();
770 void CookieStoreIOS::DeleteCookiesWithFilter(const CookieFilterFunction& filter,
771 const DeleteCallback& callback) {
772 DCHECK(thread_checker_.CalledOnValidThread());
773 DCHECK_EQ(SYNCHRONIZED, synchronization_state_);
774 NSArray* cookies = [[NSHTTPCookieStorage sharedHTTPCookieStorage] cookies];
776 // Collect the cookies to delete.
777 base::scoped_nsobject<NSMutableArray> to_delete(
778 [[NSMutableArray alloc] init]);
779 for (NSHTTPCookie* cookie in cookies) {
780 base::Time creation_time = creation_time_manager_->GetCreationTime(cookie);
781 if (filter.Run(cookie, creation_time))
782 [to_delete addObject:cookie];
786 for (NSHTTPCookie* cookie in to_delete.get()) {
787 [[NSHTTPCookieStorage sharedHTTPCookieStorage] deleteCookie:cookie];
788 creation_time_manager_->DeleteCreationTime(cookie);
791 if (!callback.is_null())
792 callback.Run([to_delete count]);
795 void CookieStoreIOS::OnSystemCookiesChanged() {
796 DCHECK(thread_checker_.CalledOnValidThread());
798 // If the CookieStoreIOS is not synchronized, system cookies are irrelevant.
799 if (synchronization_state_ != SYNCHRONIZED)
802 for (const auto& hook_map_entry : hook_map_) {
803 std::pair<GURL, std::string> key = hook_map_entry.first;
804 std::vector<net::CanonicalCookie> removed_cookies;
805 std::vector<net::CanonicalCookie> added_cookies;
806 if (UpdateCacheForCookieFromSystem(key.first, key.second, &removed_cookies,
808 RunCallbacksForCookies(key.first, key.second, removed_cookies, true);
809 RunCallbacksForCookies(key.first, key.second, added_cookies, false);
813 // Do not schedule a flush if one is already scheduled.
814 if (!flush_closure_.IsCancelled())
817 flush_closure_.Reset(base::Bind(&CookieStoreIOS::Flush,
818 base::Unretained(this), base::Closure()));
819 base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
820 FROM_HERE, flush_closure_.callback(), flush_delay_);
823 scoped_ptr<net::CookieStore::CookieChangedSubscription>
824 CookieStoreIOS::AddCallbackForCookie(const GURL& gurl,
825 const std::string& name,
826 const CookieChangedCallback& callback) {
827 DCHECK(thread_checker_.CalledOnValidThread());
829 // Prefill cookie cache with all pertinent cookies for |url| if needed.
830 std::pair<GURL, std::string> key(gurl, name);
831 if (hook_map_.count(key) == 0) {
832 UpdateCacheForCookieFromSystem(gurl, name, nullptr, nullptr);
833 if (hook_map_.count(key) == 0)
834 hook_map_[key] = new CookieChangedCallbackList;
837 DCHECK(hook_map_.find(key) != hook_map_.end());
838 return hook_map_[key]->Add(callback);
841 bool CookieStoreIOS::UpdateCacheForCookieFromSystem(
843 const std::string& name,
844 std::vector<net::CanonicalCookie>* out_removed_cookies,
845 std::vector<net::CanonicalCookie>* out_added_cookies) {
846 DCHECK(thread_checker_.CalledOnValidThread());
847 std::vector<net::CanonicalCookie> system_cookies;
848 GetSystemCookies(gurl, name, &system_cookies);
849 return cookie_cache_->Update(gurl, name, system_cookies, out_removed_cookies,
853 void CookieStoreIOS::RunCallbacksForCookies(
855 const std::string& name,
856 const std::vector<net::CanonicalCookie>& cookies,
858 DCHECK(thread_checker_.CalledOnValidThread());
862 std::pair<GURL, std::string> key(url, name);
863 CookieChangedCallbackList* callbacks = hook_map_[key];
864 for (const auto& cookie : cookies) {
865 DCHECK_EQ(name, cookie.Name());
866 callbacks->Notify(cookie, removed);
870 bool CookieStoreIOS::GetSystemCookies(
872 const std::string& name,
873 std::vector<net::CanonicalCookie>* cookies) {
874 DCHECK(thread_checker_.CalledOnValidThread());
875 NSURL* url = net::NSURLWithGURL(gurl);
876 NSHTTPCookieStorage* storage = [NSHTTPCookieStorage sharedHTTPCookieStorage];
877 NSArray* nscookies = [storage cookiesForURL:url];
878 bool found_cookies = false;
879 for (NSHTTPCookie* nscookie in nscookies) {
880 if (nscookie.name.UTF8String == name) {
881 net::CanonicalCookie canonical_cookie = CanonicalCookieFromSystemCookie(
882 nscookie, creation_time_manager_->GetCreationTime(nscookie));
883 cookies->push_back(canonical_cookie);
884 found_cookies = true;
887 return found_cookies;
890 void CookieStoreIOS::GotCookieListFor(const std::pair<GURL, std::string> key,
891 const net::CookieList& cookies) {
892 DCHECK(thread_checker_.CalledOnValidThread());
894 net::CookieList filtered;
895 OnlyCookiesWithName(cookies, key.second, &filtered);
896 std::vector<net::CanonicalCookie> removed_cookies;
897 std::vector<net::CanonicalCookie> added_cookies;
898 if (cookie_cache_->Update(key.first, key.second, filtered, &removed_cookies,
900 RunCallbacksForCookies(key.first, key.second, removed_cookies, true);
901 RunCallbacksForCookies(key.first, key.second, added_cookies, false);
905 void CookieStoreIOS::DidClearNSHTTPCookieStorageCookies(
906 const DeleteCallback& delete_callback,
908 DCHECK(thread_checker_.CalledOnValidThread());
910 CookieStoreIOSClient* client = net::GetCookieStoreIOSClient();
912 auto sequenced_task_runner = client->GetTaskRunner();
913 DCHECK(sequenced_task_runner);
914 auto callback = base::Bind(&CookieStoreIOS::DidClearBinaryCookiesFileCookies,
915 this, delete_callback, num_deleted);
916 sequenced_task_runner.get()->PostTaskAndReply(
917 FROM_HERE, base::Bind(&ClearAllCookiesFromBinaryCookiesFile), callback);
920 void CookieStoreIOS::DidClearBinaryCookiesFileCookies(
921 const DeleteCallback& callback,
922 int num_deleted_from_nshttp_cookie_storage) {
923 DCHECK(thread_checker_.CalledOnValidThread());
925 CookieStoreIOSClient* client = net::GetCookieStoreIOSClient();
927 client->DidChangeCookieStorage();
928 callback.Run(num_deleted_from_nshttp_cookie_storage);
931 void CookieStoreIOS::UpdateCachesFromCookieMonster() {
932 DCHECK(thread_checker_.CalledOnValidThread());
933 for (const auto& hook_map_entry : hook_map_) {
934 std::pair<GURL, std::string> key = hook_map_entry.first;
935 GetCookieListCallback callback =
936 base::Bind(&CookieStoreIOS::GotCookieListFor, this, key);
937 cookie_monster_->GetAllCookiesForURLAsync(key.first, callback);
941 void CookieStoreIOS::UpdateCachesAfterSet(const SetCookiesCallback& callback,
943 DCHECK(thread_checker_.CalledOnValidThread());
945 UpdateCachesFromCookieMonster();
946 callback.Run(success);
949 void CookieStoreIOS::UpdateCachesAfterDelete(const DeleteCallback& callback,
951 DCHECK(thread_checker_.CalledOnValidThread());
952 UpdateCachesFromCookieMonster();
953 callback.Run(num_deleted);
956 void CookieStoreIOS::UpdateCachesAfterClosure(const base::Closure& callback) {
957 DCHECK(thread_checker_.CalledOnValidThread());
958 UpdateCachesFromCookieMonster();
962 CookieStoreIOS::SetCookiesCallback CookieStoreIOS::WrapSetCallback(
963 const SetCookiesCallback& callback) {
964 DCHECK(thread_checker_.CalledOnValidThread());
965 return base::Bind(&CookieStoreIOS::UpdateCachesAfterSet, this, callback);
968 CookieStoreIOS::DeleteCallback CookieStoreIOS::WrapDeleteCallback(
969 const DeleteCallback& callback) {
970 DCHECK(thread_checker_.CalledOnValidThread());
971 return base::Bind(&CookieStoreIOS::UpdateCachesAfterDelete, this, callback);
974 base::Closure CookieStoreIOS::WrapClosure(const base::Closure& callback) {
975 DCHECK(thread_checker_.CalledOnValidThread());
976 return base::Bind(&CookieStoreIOS::UpdateCachesAfterClosure, this, callback);