Give names to all utility processes.
[chromium-blink-merge.git] / ios / net / cookies / cookie_store_ios.mm
blob4ccf8b700d4e937efb37b12e2c70c5e819ab6720
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>
9 #include "base/bind.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"
31 #include "url/gurl.h"
33 namespace net {
35 namespace {
37 #if !defined(NDEBUG)
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
40 // cookie store.
41 CookieStoreIOS* g_current_synchronized_store = nullptr;
42 #endif
44 #pragma mark NotificationTrampoline
46 // NotificationTrampoline dispatches cookie notifications to all the existing
47 // CookieStoreIOS.
48 class NotificationTrampoline {
49  public:
50   static NotificationTrampoline* GetInstance();
52   void AddObserver(CookieNotificationObserver* obs);
53   void RemoveObserver(CookieNotificationObserver* obs);
55   // Notify the observers.
56   void NotifyCookiesChanged();
57   void NotifyCookiePolicyChanged();
59  private:
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 =
104     nullptr;
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()) {
124     return;
125   }
126   base::FilePath path = GetBinaryCookiesFilePath();
127   if (base::PathExists(path)) {
128     bool success = base::DeleteFile(path, false);
129     if (!success) {
130       DLOG(WARNING) << "Failed to remove binarycookies file.";
131     }
132   }
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
139 // URL.
140 NSHTTPCookie* GetNSHTTPCookieFromCookieLine(const std::string& cookie_line,
141                                             const GURL& url,
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;
147     return nil;
148   }
149   NSArray* cookies = [NSHTTPCookie cookiesWithResponseHeaderFields:@{
150     @"Set-Cookie" : ns_cookie_line
151   } forURL:nsurl];
152   if ([cookies count] != 1)
153     return nil;
155   NSHTTPCookie* cookie = [cookies objectAtIndex:0];
156   if (![cookie expiresDate] || server_time.is_null())
157     return cookie;
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
173 // RFC6265.
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;
187   DCHECK(manager);
188   base::Time created_a = manager->GetCreationTime(cookie_a);
189   base::Time created_b = manager->GetCreationTime(cookie_b);
190 #if !defined(CRNET)
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";
198 #endif
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
214 // cookies.
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,
234                             base::Time time_end,
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,
245                                    base::Time time_end,
246                                    NSString* host,
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);
262   }
265 }  // namespace
267 #pragma mark -
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();
283 // static
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)
291     return;
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());
306   return cookie_store;
309 // static
310 void CookieStoreIOS::SwitchSynchronizedStore(CookieStoreIOS* old_store,
311                                              CookieStoreIOS* new_store) {
312   DCHECK(new_store);
313   DCHECK_NE(new_store, old_store);
314   if (old_store)
315     old_store->SetSynchronizedWithSystemStore(false);
316   new_store->SetSynchronizedWithSystemStore(true);
319 // static
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]);
332   }
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;
349 #pragma mark -
350 #pragma mark CookieStore methods
352 void CookieStoreIOS::SetCookieWithOptionsAsync(
353     const GURL& url,
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));
363       break;
364     case SYNCHRONIZING:
365       tasks_pending_synchronization_.push_back(
366           base::Bind(&CookieStoreIOS::SetCookieWithOptionsAsync, this, url,
367                      cookie_line, options, WrapSetCallback(callback)));
368       break;
369     case SYNCHRONIZED:
370       // The exclude_httponly() option would only be used by a javascript
371       // engine.
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
389       //   correctly
390       std::string domain_string(base::SysNSStringToUTF8([cookie domain]));
391       const std::string url_host(url.host());
392       std::string dummy;
393       bool success = (cookie != nil) && !domain_string.empty() &&
394                      (url_host == domain_string ||
395                       net::cookie_util::GetCookieDomainWithString(
396                           url, domain_string, &dummy));
398       if (success) {
399         [[NSHTTPCookieStorage sharedHTTPCookieStorage] setCookie:cookie];
400         creation_time_manager_->SetCreationTime(
401             cookie,
402             creation_time_manager_->MakeUniqueCreationTime(base::Time::Now()));
403       }
405       if (!callback.is_null())
406         callback.Run(success);
407       break;
408   }
411 void CookieStoreIOS::GetCookiesWithOptionsAsync(
412     const GURL& url,
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);
420       break;
421     case SYNCHRONIZING:
422       tasks_pending_synchronization_.push_back(
423           base::Bind(&CookieStoreIOS::GetCookiesWithOptionsAsync, this, url,
424                      options, callback));
425       break;
426     case SYNCHRONIZED:
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
431       // engine.
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));
437       break;
438   }
441 void CookieStoreIOS::GetAllCookiesForURLAsync(
442     const GURL& url,
443     const GetCookieListCallback& callback) {
444   DCHECK(thread_checker_.CalledOnValidThread());
446   switch (synchronization_state_) {
447     case NOT_SYNCHRONIZED:
448       cookie_monster_->GetAllCookiesForURLAsync(url, callback);
449       break;
450     case SYNCHRONIZING:
451       tasks_pending_synchronization_.push_back(base::Bind(
452           &CookieStoreIOS::GetAllCookiesForURLAsync, this, url, callback));
453       break;
454     case SYNCHRONIZED:
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);
459         return;
460       }
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));
468       }
469       if (!callback.is_null())
470         callback.Run(cookie_list);
471       break;
472   }
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));
484       break;
485     case SYNCHRONIZING:
486       tasks_pending_synchronization_.push_back(
487           base::Bind(&CookieStoreIOS::DeleteCookieAsync, this, url, cookie_name,
488                      WrapClosure(callback)));
489       break;
490     case SYNCHRONIZED:
491       NSArray* cookies = GetCookiesForURL(url, creation_time_manager_.get());
492       for (NSHTTPCookie* cookie in cookies) {
493         if ([[cookie name]
494                 isEqualToString:base::SysUTF8ToNSString(cookie_name)]) {
495           [[NSHTTPCookieStorage sharedHTTPCookieStorage] deleteCookie:cookie];
496           creation_time_manager_->DeleteCreationTime(cookie);
497         }
498       }
499       if (!callback.is_null())
500         callback.Run();
501       break;
502   }
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());
510   return nullptr;
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));
526       break;
527     case SYNCHRONIZING:
528       tasks_pending_synchronization_.push_back(
529           base::Bind(&CookieStoreIOS::DeleteAllCreatedBetweenAsync, this,
530                      delete_begin, delete_end, WrapDeleteCallback(callback)));
531       break;
532     case SYNCHRONIZED:
533       CookieFilterFunction filter =
534           base::Bind(&IsCookieCreatedBetween, delete_begin, delete_end);
535       DeleteCookiesWithFilter(filter, callback);
536       break;
537   }
540 void CookieStoreIOS::DeleteAllCreatedBetweenForHostAsync(
541     const base::Time delete_begin,
542     const base::Time delete_end,
543     const GURL& url,
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));
554       break;
555     case SYNCHRONIZING:
556       tasks_pending_synchronization_.push_back(base::Bind(
557           &CookieStoreIOS::DeleteAllCreatedBetweenForHostAsync, this,
558           delete_begin, delete_end, url, WrapDeleteCallback(callback)));
559       break;
560     case SYNCHRONIZED:
561       NSString* host = base::SysUTF8ToNSString(url.host());
562       CookieFilterFunction filter = base::Bind(IsCookieCreatedBetweenForHost,
563                                                delete_begin, delete_end, host);
564       DeleteCookiesWithFilter(filter, callback);
565       break;
566   }
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));
578       break;
579     case SYNCHRONIZING:
580       tasks_pending_synchronization_.push_back(
581           base::Bind(&CookieStoreIOS::DeleteSessionCookiesAsync, this,
582                      WrapDeleteCallback(callback)));
583       break;
584     case SYNCHRONIZED:
585       CookieFilterFunction filter = base::Bind(&IsCookieSessionCookie);
586       DeleteCookiesWithFilter(filter, callback);
587       break;
588   }
591 #pragma mark -
592 #pragma mark Protected methods
594 CookieStoreIOS::~CookieStoreIOS() {
595   NotificationTrampoline::GetInstance()->RemoveObserver(this);
596   STLDeleteContainerPairSecondPointers(hook_map_.begin(), hook_map_.end());
599 #pragma mark -
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)
618     return;
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));
631   } else {
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());
638     ClearSystemStore();
639   }
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.
648 #if !defined(NDEBUG)
649   if (!synchronized) {
650     DCHECK_EQ(this, g_current_synchronized_store)
651         << "This cookie store was not synchronized";
652     g_current_synchronized_store = nullptr;
653   } else {
654     DCHECK_EQ((CookieStoreIOS*)nullptr, g_current_synchronized_store)
655         << "Un-synchronize the current cookie store first.";
656     g_current_synchronized_store = this;
657   }
658 #endif
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) {
672     if (synchronized) {
673       synchronization_state_ = SYNCHRONIZING;
674       ClearSystemStore();
675       cookie_monster_->GetAllCookiesAsync(
676           base::Bind(&CookieStoreIOS::AddCookiesToSystemStore, this));
677       return;
678     } else {
679       // Copy the cookies from the global store to |cookie_monster_|.
680       Flush(base::Closure());
681     }
682   }
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
696     // not supported.
697     // Note: it should be ok to drop the tasks here (at least if cookies are not
698     // allowed).
699     DCHECK(tasks_pending_synchronization_.empty());
700     return;
701   }
703   // Report metrics.
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);
708   }
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.
716     if (!system_cookie)
717       continue;
718     [[NSHTTPCookieStorage sharedHTTPCookieStorage] setCookie:system_cookie];
719     creation_time_manager_->SetCreationTime(system_cookie,
720                                             net_cookie.CreationDate());
721   }
723   synchronization_state_ = SYNCHRONIZED;
724   // Run all the pending tasks.
725   for (const auto& task : tasks_pending_synchronization_) {
726     task.Run();
727   }
728   tasks_pending_synchronization_.clear();
731 void CookieStoreIOS::WriteToCookieMonster(NSArray* system_cookies) {
732   DCHECK(thread_checker_.CalledOnValidThread());
733   if (synchronization_state_ != SYNCHRONIZED)
734     return;
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)));
744   }
745   cookie_monster_->SetAllCookiesAsync(cookie_list, SetCookiesCallback());
747   // Update metrics.
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];
765   }
767   // Delete them.
768   for (NSHTTPCookie* cookie in to_delete.get()) {
769     [[NSHTTPCookieStorage sharedHTTPCookieStorage] deleteCookie:cookie];
770     creation_time_manager_->DeleteCreationTime(cookie);
771   }
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)
782     return;
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,
789                                        &added_cookies)) {
790       RunCallbacksForCookies(key.first, key.second, removed_cookies, true);
791       RunCallbacksForCookies(key.first, key.second, added_cookies, false);
792     }
793   }
795   // Do not schedule a flush if one is already scheduled.
796   if (!flush_closure_.IsCancelled())
797     return;
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;
817   }
819   DCHECK(hook_map_.find(key) != hook_map_.end());
820   return hook_map_[key]->Add(callback);
823 bool CookieStoreIOS::UpdateCacheForCookieFromSystem(
824     const GURL& gurl,
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,
832                                out_added_cookies);
835 void CookieStoreIOS::RunCallbacksForCookies(
836     const GURL& url,
837     const std::string& name,
838     const std::vector<net::CanonicalCookie>& cookies,
839     bool removed) {
840   DCHECK(thread_checker_.CalledOnValidThread());
841   if (cookies.empty())
842     return;
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);
849   }
852 bool CookieStoreIOS::GetSystemCookies(
853     const GURL& gurl,
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;
867     }
868   }
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,
881                             &added_cookies)) {
882     RunCallbacksForCookies(key.first, key.second, removed_cookies, true);
883     RunCallbacksForCookies(key.first, key.second, added_cookies, false);
884   }
887 void CookieStoreIOS::DidClearNSHTTPCookieStorageCookies(
888     const DeleteCallback& delete_callback,
889     int num_deleted) {
890   DCHECK(thread_checker_.CalledOnValidThread());
892   CookieStoreIOSClient* client = net::GetCookieStoreIOSClient();
893   DCHECK(client);
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();
908   DCHECK(client);
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);
920   }
923 void CookieStoreIOS::UpdateCachesAfterSet(const SetCookiesCallback& callback,
924                                           bool success) {
925   DCHECK(thread_checker_.CalledOnValidThread());
926   if (success)
927     UpdateCachesFromCookieMonster();
928   callback.Run(success);
931 void CookieStoreIOS::UpdateCachesAfterDelete(const DeleteCallback& callback,
932                                              int num_deleted) {
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();
941   callback.Run();
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);
961 }  // namespace net