blob: edbbb82037aa3f9a193963d71f16645f2669682b [file] [log] [blame]
droger3e8d98b2015-03-18 15:29:531// 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.
4
5#include "ios/net/cookies/cookie_store_ios.h"
6
7#import <Foundation/Foundation.h>
avi571943672015-12-22 02:12:498#include <stddef.h>
droger3e8d98b2015-03-18 15:29:539
10#include "base/bind.h"
11#include "base/files/file_path.h"
12#include "base/files/file_util.h"
13#include "base/ios/ios_util.h"
14#include "base/location.h"
15#include "base/logging.h"
16#include "base/mac/foundation_util.h"
17#include "base/mac/scoped_nsobject.h"
avi571943672015-12-22 02:12:4918#include "base/macros.h"
avide3063472016-09-19 13:52:2719#include "base/memory/ptr_util.h"
droger3e8d98b2015-03-18 15:29:5320#include "base/memory/weak_ptr.h"
droger3e8d98b2015-03-18 15:29:5321#include "base/metrics/histogram.h"
22#include "base/observer_list.h"
23#include "base/sequenced_task_runner.h"
droger3e8d98b2015-03-18 15:29:5324#include "base/strings/sys_string_conversions.h"
25#include "base/task_runner_util.h"
26#include "base/threading/thread_restrictions.h"
gab3d426fa62016-05-11 18:13:3327#include "base/threading/thread_task_runner_handle.h"
droger3e8d98b2015-03-18 15:29:5328#include "ios/net/cookies/cookie_creation_time_manager.h"
29#include "ios/net/cookies/cookie_store_ios_client.h"
30#include "ios/net/cookies/system_cookie_util.h"
31#import "net/base/mac/url_conversions.h"
32#include "net/cookies/cookie_util.h"
ellyjones98a0b9202015-09-04 17:22:2433#include "net/cookies/parsed_cookie.h"
droger3e8d98b2015-03-18 15:29:5334#include "url/gurl.h"
35
stkhapugind335d772016-11-16 16:48:4536#if !defined(__has_feature) || !__has_feature(objc_arc)
37#error "This file requires ARC support."
38#endif
39
droger3e8d98b2015-03-18 15:29:5340namespace net {
41
42namespace {
43
44#if !defined(NDEBUG)
45// The current cookie store. This weak pointer must not be used to do actual
46// work. Its only purpose is to check that there is only one synchronized
47// cookie store.
48CookieStoreIOS* g_current_synchronized_store = nullptr;
49#endif
50
51#pragma mark NotificationTrampoline
52
53// NotificationTrampoline dispatches cookie notifications to all the existing
54// CookieStoreIOS.
55class NotificationTrampoline {
56 public:
57 static NotificationTrampoline* GetInstance();
58
59 void AddObserver(CookieNotificationObserver* obs);
60 void RemoveObserver(CookieNotificationObserver* obs);
61
62 // Notify the observers.
63 void NotifyCookiesChanged();
64 void NotifyCookiePolicyChanged();
65
66 private:
67 NotificationTrampoline();
68 ~NotificationTrampoline();
69
brettw236d3172015-06-03 16:31:4370 base::ObserverList<CookieNotificationObserver> observer_list_;
droger3e8d98b2015-03-18 15:29:5371
72 DISALLOW_COPY_AND_ASSIGN(NotificationTrampoline);
73
74 static NotificationTrampoline* g_notification_trampoline;
75};
76
77#pragma mark NotificationTrampoline implementation
78
79NotificationTrampoline* NotificationTrampoline::GetInstance() {
80 if (!g_notification_trampoline)
81 g_notification_trampoline = new NotificationTrampoline;
82 return g_notification_trampoline;
83}
84
85void NotificationTrampoline::AddObserver(CookieNotificationObserver* obs) {
86 observer_list_.AddObserver(obs);
87}
88
89void NotificationTrampoline::RemoveObserver(CookieNotificationObserver* obs) {
90 observer_list_.RemoveObserver(obs);
91}
92
93void NotificationTrampoline::NotifyCookiesChanged() {
ericwilligersff4feda2016-10-18 00:33:4494 for (auto& observer : observer_list_)
95 observer.OnSystemCookiesChanged();
droger3e8d98b2015-03-18 15:29:5396}
97
98void NotificationTrampoline::NotifyCookiePolicyChanged() {
ericwilligersff4feda2016-10-18 00:33:4499 for (auto& observer : observer_list_)
100 observer.OnSystemCookiePolicyChanged();
droger3e8d98b2015-03-18 15:29:53101}
102
103NotificationTrampoline::NotificationTrampoline() {
104}
105
106NotificationTrampoline::~NotificationTrampoline() {
107}
108
109// Global instance of NotificationTrampoline.
110NotificationTrampoline* NotificationTrampoline::g_notification_trampoline =
111 nullptr;
112
113#pragma mark Utility functions
114
115// Returns the path to Cookie.binarycookies file on the file system where
116// WKWebView flushes its cookies.
117base::FilePath GetBinaryCookiesFilePath() {
118 base::FilePath path = base::mac::GetUserLibraryPath();
119 // The relative path of the file (from the user library folder) where
120 // WKWebView stores its cookies.
121 const std::string kCookiesFilePath = "Cookies/Cookies.binarycookies";
122 return path.Append(kCookiesFilePath);
123}
124
125// Clears all cookies from the .binarycookies file.
126// Must be called from a thread where IO operations are allowed.
127// Preconditions: There must be no active WKWebViews present in the app.
pkld9e3f932016-10-17 20:43:30128// Note that the .binarycookies file is present only on iOS8+.
droger3e8d98b2015-03-18 15:29:53129void ClearAllCookiesFromBinaryCookiesFile() {
droger3e8d98b2015-03-18 15:29:53130 base::FilePath path = GetBinaryCookiesFilePath();
131 if (base::PathExists(path)) {
132 bool success = base::DeleteFile(path, false);
133 if (!success) {
134 DLOG(WARNING) << "Failed to remove binarycookies file.";
135 }
136 }
droger3e8d98b2015-03-18 15:29:53137}
138
139// Builds a NSHTTPCookie from a header cookie line ("Set-Cookie: xxx") and a
140// URL.
141NSHTTPCookie* GetNSHTTPCookieFromCookieLine(const std::string& cookie_line,
142 const GURL& url,
143 base::Time server_time) {
144 NSURL* nsurl = net::NSURLWithGURL(url);
145 NSString* ns_cookie_line = base::SysUTF8ToNSString(cookie_line);
146 if (!ns_cookie_line) {
147 DLOG(ERROR) << "Cookie line is not UTF8: " << cookie_line;
148 return nil;
149 }
150 NSArray* cookies = [NSHTTPCookie cookiesWithResponseHeaderFields:@{
151 @"Set-Cookie" : ns_cookie_line
152 } forURL:nsurl];
153 if ([cookies count] != 1)
154 return nil;
155
156 NSHTTPCookie* cookie = [cookies objectAtIndex:0];
157 if (![cookie expiresDate] || server_time.is_null())
158 return cookie;
159
160 // Perform clock skew correction.
161 base::TimeDelta clock_skew = base::Time::Now() - server_time;
162 NSDate* corrected_expire_date =
163 [[cookie expiresDate] dateByAddingTimeInterval:clock_skew.InSecondsF()];
164 NSMutableDictionary* properties =
165 [NSMutableDictionary dictionaryWithDictionary:[cookie properties]];
166 [properties setObject:corrected_expire_date forKey:NSHTTPCookieExpires];
167 NSHTTPCookie* corrected_cookie =
168 [NSHTTPCookie cookieWithProperties:properties];
169 DCHECK(corrected_cookie);
170 return corrected_cookie;
171}
172
173// Compares cookies based on the path lengths and the creation times, as per
174// RFC6265.
175NSInteger CompareCookies(id a, id b, void* context) {
176 NSHTTPCookie* cookie_a = (NSHTTPCookie*)a;
177 NSHTTPCookie* cookie_b = (NSHTTPCookie*)b;
178 // Compare path lengths first.
179 NSUInteger path_length_a = [[cookie_a path] length];
180 NSUInteger path_length_b = [[cookie_b path] length];
181 if (path_length_a < path_length_b)
182 return NSOrderedDescending;
183 if (path_length_b < path_length_a)
184 return NSOrderedAscending;
185
186 // Compare creation times.
187 CookieCreationTimeManager* manager = (CookieCreationTimeManager*)context;
188 DCHECK(manager);
189 base::Time created_a = manager->GetCreationTime(cookie_a);
190 base::Time created_b = manager->GetCreationTime(cookie_b);
191#if !defined(CRNET)
192 // CookieCreationTimeManager is returning creation times that are null.
193 // Since in CrNet, the cookie store is recreated on startup, let's suppress
194 // this warning for now.
195 // TODO(huey): Instead of suppressing the warning, assign a creation time
196 // to cookies if one doesn't already exist.
197 DLOG_IF(ERROR, created_a.is_null() || created_b.is_null())
198 << "Cookie without creation date";
199#endif
200 if (created_a < created_b)
201 return NSOrderedAscending;
202 return (created_a > created_b) ? NSOrderedDescending : NSOrderedSame;
203}
204
205// Gets the cookies for |url| from the system cookie store.
shreyasv1ae3c3a32015-11-03 20:13:12206NSArray* GetCookiesForURL(NSHTTPCookieStorage* system_store,
207 const GURL& url, CookieCreationTimeManager* manager) {
208 NSArray* cookies = [system_store cookiesForURL:net::NSURLWithGURL(url)];
droger3e8d98b2015-03-18 15:29:53209
210 // Sort cookies by decreasing path length, then creation time, as per RFC6265.
211 return [cookies sortedArrayUsingFunction:CompareCookies context:manager];
212}
213
mmenke9fa44f2d2016-01-22 23:36:39214// Gets all cookies from the system cookie store.
215NSArray* GetAllCookies(NSHTTPCookieStorage* system_store,
216 CookieCreationTimeManager* manager) {
217 NSArray* cookies = [system_store cookies];
218
219 // Sort cookies by decreasing path length, then creation time, as per RFC6265.
220 return [cookies sortedArrayUsingFunction:CompareCookies context:manager];
221}
222
droger3e8d98b2015-03-18 15:29:53223// Builds a cookie line (such as "key1=value1; key2=value2") from an array of
224// cookies.
mkwstc611e6d2016-02-23 15:45:55225std::string BuildCookieLineWithOptions(NSArray* cookies,
226 const net::CookieOptions& options) {
droger3e8d98b2015-03-18 15:29:53227 // The exclude_httponly() option would only be used by a javascript engine.
228 DCHECK(!options.exclude_httponly());
229
230 // This utility function returns all the cookies, including the httponly ones.
231 // This is fine because we don't support the exclude_httponly option.
232 NSDictionary* header = [NSHTTPCookie requestHeaderFieldsWithCookies:cookies];
233 return base::SysNSStringToUTF8([header valueForKey:@"Cookie"]);
234}
235
236// Tests whether the |cookie| is a session cookie.
237bool IsCookieSessionCookie(NSHTTPCookie* cookie, base::Time time) {
238 return [cookie isSessionOnly];
239}
240
241// Tests whether the |creation_time| of |cookie| is in the time range defined
242// by |time_begin| and |time_end|. A null |time_end| means end-of-time.
243bool IsCookieCreatedBetween(base::Time time_begin,
244 base::Time time_end,
245 NSHTTPCookie* cookie,
246 base::Time creation_time) {
247 return time_begin <= creation_time &&
248 (time_end.is_null() || creation_time <= time_end);
249}
250
251// Tests whether the |creation_time| of |cookie| is in the time range defined
252// by |time_begin| and |time_end| and the cookie host match |host|. A null
253// |time_end| means end-of-time.
dmurphfaea244c2016-04-09 00:42:30254bool IsCookieCreatedBetweenWithPredicate(
255 base::Time time_begin,
256 base::Time time_end,
257 const net::CookieStore::CookiePredicate& predicate,
258 NSHTTPCookie* cookie,
259 base::Time creation_time) {
260 if (predicate.is_null())
261 return false;
262 CanonicalCookie canonical_cookie =
263 CanonicalCookieFromSystemCookie(cookie, creation_time);
264 return IsCookieCreatedBetween(time_begin, time_end, cookie, creation_time) &&
265 predicate.Run(canonical_cookie);
droger3e8d98b2015-03-18 15:29:53266}
267
268// Adds cookies in |cookies| with name |name| to |filtered|.
269void OnlyCookiesWithName(const net::CookieList& cookies,
270 const std::string& name,
271 net::CookieList* filtered) {
272 for (const auto& cookie : cookies) {
273 if (cookie.Name() == name)
274 filtered->push_back(cookie);
275 }
276}
277
ellyjones98a0b9202015-09-04 17:22:24278// Returns whether the specified cookie line has an explicit Domain attribute or
279// not.
280bool HasExplicitDomain(const std::string& cookie_line) {
281 ParsedCookie cookie(cookie_line);
282 return cookie.HasDomain();
283}
284
droger3e8d98b2015-03-18 15:29:53285} // namespace
286
287#pragma mark -
288#pragma mark CookieStoreIOS
289
290CookieStoreIOS::CookieStoreIOS(
291 net::CookieMonster::PersistentCookieStore* persistent_store)
shreyasv1ae3c3a32015-11-03 20:13:12292 : CookieStoreIOS(persistent_store,
293 [NSHTTPCookieStorage sharedHTTPCookieStorage]) {
294}
295
296CookieStoreIOS::CookieStoreIOS(
297 net::CookieMonster::PersistentCookieStore* persistent_store,
298 NSHTTPCookieStorage* system_store)
mmenke606c59c2016-03-07 18:20:55299 : cookie_monster_(new net::CookieMonster(persistent_store, nullptr)),
stkhapugind335d772016-11-16 16:48:45300 system_store_(system_store),
shreyasv1ae3c3a32015-11-03 20:13:12301 creation_time_manager_(new CookieCreationTimeManager),
droger3e8d98b2015-03-18 15:29:53302 metrics_enabled_(false),
303 flush_delay_(base::TimeDelta::FromSeconds(10)),
304 synchronization_state_(NOT_SYNCHRONIZED),
mmenke606c59c2016-03-07 18:20:55305 cookie_cache_(new CookieCache()),
306 weak_factory_(this) {
shreyasv1ae3c3a32015-11-03 20:13:12307 DCHECK(system_store);
308
droger3e8d98b2015-03-18 15:29:53309 NotificationTrampoline::GetInstance()->AddObserver(this);
mmenke606c59c2016-03-07 18:20:55310
droger3e8d98b2015-03-18 15:29:53311 cookie_monster_->SetPersistSessionCookies(true);
312 cookie_monster_->SetForceKeepSessionState();
313}
314
mmenke606c59c2016-03-07 18:20:55315CookieStoreIOS::~CookieStoreIOS() {
316 NotificationTrampoline::GetInstance()->RemoveObserver(this);
mmenke606c59c2016-03-07 18:20:55317}
318
droger3e8d98b2015-03-18 15:29:53319// static
320void CookieStoreIOS::SetCookiePolicy(CookiePolicy setting) {
321 NSHTTPCookieAcceptPolicy policy = (setting == ALLOW)
322 ? NSHTTPCookieAcceptPolicyAlways
323 : NSHTTPCookieAcceptPolicyNever;
324 NSHTTPCookieStorage* store = [NSHTTPCookieStorage sharedHTTPCookieStorage];
325 NSHTTPCookieAcceptPolicy current_policy = [store cookieAcceptPolicy];
326 if (current_policy == policy)
327 return;
328 [store setCookieAcceptPolicy:policy];
329 NotificationTrampoline::GetInstance()->NotifyCookiePolicyChanged();
330}
331
shreyasv1ae3c3a32015-11-03 20:13:12332// static
dcheng942f39d72016-04-07 21:11:23333std::unique_ptr<CookieStoreIOS> CookieStoreIOS::CreateCookieStore(
shreyasv1ae3c3a32015-11-03 20:13:12334 NSHTTPCookieStorage* cookie_storage) {
335 DCHECK(cookie_storage);
droger3e8d98b2015-03-18 15:29:53336 // TODO(huey): Update this when CrNet supports multiple cookie jars.
shreyasv1ae3c3a32015-11-03 20:13:12337 [cookie_storage setCookieAcceptPolicy:NSHTTPCookieAcceptPolicyAlways];
droger3e8d98b2015-03-18 15:29:53338
339 // Create a cookie store with no persistent store backing. Then, populate
340 // it from the system's cookie jar.
dcheng942f39d72016-04-07 21:11:23341 std::unique_ptr<CookieStoreIOS> cookie_store(
mmenke606c59c2016-03-07 18:20:55342 new CookieStoreIOS(nullptr, cookie_storage));
droger3e8d98b2015-03-18 15:29:53343 cookie_store->synchronization_state_ = SYNCHRONIZED;
mmenke96f3bab2016-01-22 17:34:02344 cookie_store->FlushStore(base::Closure());
droger3e8d98b2015-03-18 15:29:53345 return cookie_store;
346}
347
348// static
349void CookieStoreIOS::SwitchSynchronizedStore(CookieStoreIOS* old_store,
350 CookieStoreIOS* new_store) {
351 DCHECK(new_store);
352 DCHECK_NE(new_store, old_store);
353 if (old_store)
354 old_store->SetSynchronizedWithSystemStore(false);
355 new_store->SetSynchronizedWithSystemStore(true);
356}
357
358// static
359void CookieStoreIOS::NotifySystemCookiesChanged() {
360 NotificationTrampoline::GetInstance()->NotifyCookiesChanged();
361}
362
droger3e8d98b2015-03-18 15:29:53363void CookieStoreIOS::UnSynchronize() {
364 SetSynchronizedWithSystemStore(false);
365}
366
367void CookieStoreIOS::SetMetricsEnabled() {
368 static CookieStoreIOS* g_cookie_store_with_metrics = nullptr;
369 DCHECK(!g_cookie_store_with_metrics || g_cookie_store_with_metrics == this)
370 << "Only one cookie store may use metrics.";
371 g_cookie_store_with_metrics = this;
372 metrics_enabled_ = true;
373}
374
375#pragma mark -
376#pragma mark CookieStore methods
377
378void CookieStoreIOS::SetCookieWithOptionsAsync(
379 const GURL& url,
380 const std::string& cookie_line,
381 const net::CookieOptions& options,
382 const SetCookiesCallback& callback) {
383 DCHECK(thread_checker_.CalledOnValidThread());
384
385 switch (synchronization_state_) {
386 case NOT_SYNCHRONIZED:
387 cookie_monster_->SetCookieWithOptionsAsync(url, cookie_line, options,
388 WrapSetCallback(callback));
389 break;
390 case SYNCHRONIZING:
391 tasks_pending_synchronization_.push_back(
mmenke606c59c2016-03-07 18:20:55392 base::Bind(&CookieStoreIOS::SetCookieWithOptionsAsync,
393 weak_factory_.GetWeakPtr(), url, cookie_line, options,
394 WrapSetCallback(callback)));
droger3e8d98b2015-03-18 15:29:53395 break;
396 case SYNCHRONIZED:
397 // The exclude_httponly() option would only be used by a javascript
398 // engine.
399 DCHECK(!options.exclude_httponly());
400 // If cookies are not allowed, they are stashed in the CookieMonster, and
401 // should be written there instead.
402 DCHECK(SystemCookiesAllowed());
403
404 base::Time server_time =
405 options.has_server_time() ? options.server_time() : base::Time();
406 NSHTTPCookie* cookie =
407 GetNSHTTPCookieFromCookieLine(cookie_line, url, server_time);
408 DLOG_IF(WARNING, !cookie)
409 << "Could not create cookie for line: " << cookie_line;
410
411 // On iOS, [cookie domain] is not empty when the cookie domain is not
ellyjones98a0b9202015-09-04 17:22:24412 // specified: it is inferred from the URL instead. The only case when it
413 // is empty is when the domain attribute is incorrectly formatted.
droger3e8d98b2015-03-18 15:29:53414 std::string domain_string(base::SysNSStringToUTF8([cookie domain]));
droger3e8d98b2015-03-18 15:29:53415 std::string dummy;
ellyjones98a0b9202015-09-04 17:22:24416 bool has_explicit_domain = HasExplicitDomain(cookie_line);
417 bool has_valid_domain =
418 net::cookie_util::GetCookieDomainWithString(
419 url, domain_string, &dummy);
420 // A cookie can be set if all of:
421 // a) The cookie line is well-formed
422 // b) The Domain attribute, if present, was not malformed
423 // c) At least one of:
424 // 1) The cookie had no explicit Domain, so the Domain was inferred
425 // from the URL, or
426 // 2) The cookie had an explicit Domain for which the URL is allowed
427 // to set cookies.
droger3e8d98b2015-03-18 15:29:53428 bool success = (cookie != nil) && !domain_string.empty() &&
ellyjones98a0b9202015-09-04 17:22:24429 (!has_explicit_domain || has_valid_domain);
droger3e8d98b2015-03-18 15:29:53430
431 if (success) {
shreyasv1ae3c3a32015-11-03 20:13:12432 [system_store_ setCookie:cookie];
droger3e8d98b2015-03-18 15:29:53433 creation_time_manager_->SetCreationTime(
434 cookie,
435 creation_time_manager_->MakeUniqueCreationTime(base::Time::Now()));
436 }
437
438 if (!callback.is_null())
439 callback.Run(success);
440 break;
441 }
442}
443
mmenkeea4cd402016-02-02 04:03:10444void CookieStoreIOS::SetCookieWithDetailsAsync(
445 const GURL& url,
446 const std::string& name,
447 const std::string& value,
448 const std::string& domain,
449 const std::string& path,
mmenkefdd4fc72016-02-05 20:53:24450 base::Time creation_time,
451 base::Time expiration_time,
452 base::Time last_access_time,
mmenkeea4cd402016-02-02 04:03:10453 bool secure,
454 bool http_only,
mkwste1a29582016-03-15 10:07:52455 CookieSameSite same_site,
mmenkeea4cd402016-02-02 04:03:10456 bool enforce_strict_secure,
457 CookiePriority priority,
458 const SetCookiesCallback& callback) {
459 DCHECK(thread_checker_.CalledOnValidThread());
460
461 switch (synchronization_state_) {
462 case NOT_SYNCHRONIZED:
463 cookie_monster_->SetCookieWithDetailsAsync(
464 url, name, value, domain, path, creation_time, expiration_time,
mmenkefdd4fc72016-02-05 20:53:24465 last_access_time, secure, http_only, same_site, enforce_strict_secure,
466 priority, WrapSetCallback(callback));
mmenkeea4cd402016-02-02 04:03:10467 break;
468 case SYNCHRONIZING:
mmenke606c59c2016-03-07 18:20:55469 tasks_pending_synchronization_.push_back(
470 base::Bind(&CookieStoreIOS::SetCookieWithDetailsAsync,
471 weak_factory_.GetWeakPtr(), url, name, value, domain, path,
472 creation_time, expiration_time, last_access_time, secure,
473 http_only, same_site, enforce_strict_secure, priority,
474 WrapSetCallback(callback)));
mmenkeea4cd402016-02-02 04:03:10475 break;
476 case SYNCHRONIZED:
477 // If cookies are not allowed, they are stashed in the CookieMonster, and
478 // should be written there instead.
479 DCHECK(SystemCookiesAllowed());
480
481 bool success = false;
482
mmenkefdd4fc72016-02-05 20:53:24483 if (creation_time.is_null())
484 creation_time = base::Time::Now();
485
mmenkeea4cd402016-02-02 04:03:10486 // First create a CanonicalCookie, to normalize the arguments,
487 // particularly domain and path, and perform validation.
dcheng942f39d72016-04-07 21:11:23488 std::unique_ptr<net::CanonicalCookie> canonical_cookie =
mmenkeea4cd402016-02-02 04:03:10489 net::CanonicalCookie::Create(
490 url, name, value, domain, path, creation_time, expiration_time,
dcheng942f39d72016-04-07 21:11:23491 secure, http_only, same_site, enforce_strict_secure, priority);
mmenkeea4cd402016-02-02 04:03:10492
493 if (canonical_cookie) {
494 NSHTTPCookie* cookie =
495 SystemCookieFromCanonicalCookie(*canonical_cookie);
496
497 if (cookie != nil) {
498 [system_store_ setCookie:cookie];
mmenkeea4cd402016-02-02 04:03:10499 creation_time_manager_->SetCreationTime(
500 cookie, creation_time_manager_->MakeUniqueCreationTime(
mmenkefdd4fc72016-02-05 20:53:24501 canonical_cookie->CreationDate()));
mmenkeea4cd402016-02-02 04:03:10502 success = true;
503 }
504 }
505
506 if (!callback.is_null())
507 callback.Run(success);
508 break;
509 }
510}
511
droger3e8d98b2015-03-18 15:29:53512void CookieStoreIOS::GetCookiesWithOptionsAsync(
513 const GURL& url,
514 const net::CookieOptions& options,
515 const GetCookiesCallback& callback) {
516 DCHECK(thread_checker_.CalledOnValidThread());
517
518 switch (synchronization_state_) {
519 case NOT_SYNCHRONIZED:
520 cookie_monster_->GetCookiesWithOptionsAsync(url, options, callback);
521 break;
522 case SYNCHRONIZING:
523 tasks_pending_synchronization_.push_back(
mmenke606c59c2016-03-07 18:20:55524 base::Bind(&CookieStoreIOS::GetCookiesWithOptionsAsync,
525 weak_factory_.GetWeakPtr(), url, options, callback));
droger3e8d98b2015-03-18 15:29:53526 break;
527 case SYNCHRONIZED:
528 // If cookies are not allowed, they are stashed in the CookieMonster, and
529 // should be read from there instead.
530 DCHECK(SystemCookiesAllowed());
531 // The exclude_httponly() option would only be used by a javascript
532 // engine.
533 DCHECK(!options.exclude_httponly());
534
mkwstc611e6d2016-02-23 15:45:55535 // TODO(mkwst): If/when iOS supports Same-Site cookies, we'll need to pass
536 // options in here as well. https://crbug.com/459154
shreyasv1ae3c3a32015-11-03 20:13:12537 NSArray* cookies = GetCookiesForURL(system_store_,
538 url, creation_time_manager_.get());
droger3e8d98b2015-03-18 15:29:53539 if (!callback.is_null())
mkwstc611e6d2016-02-23 15:45:55540 callback.Run(BuildCookieLineWithOptions(cookies, options));
droger3e8d98b2015-03-18 15:29:53541 break;
542 }
543}
544
mkwstc611e6d2016-02-23 15:45:55545void CookieStoreIOS::GetCookieListWithOptionsAsync(
droger3e8d98b2015-03-18 15:29:53546 const GURL& url,
mkwstc611e6d2016-02-23 15:45:55547 const net::CookieOptions& options,
droger3e8d98b2015-03-18 15:29:53548 const GetCookieListCallback& callback) {
549 DCHECK(thread_checker_.CalledOnValidThread());
550
551 switch (synchronization_state_) {
552 case NOT_SYNCHRONIZED:
mkwstc611e6d2016-02-23 15:45:55553 cookie_monster_->GetCookieListWithOptionsAsync(url, options, callback);
droger3e8d98b2015-03-18 15:29:53554 break;
555 case SYNCHRONIZING:
mkwstc611e6d2016-02-23 15:45:55556 tasks_pending_synchronization_.push_back(
mmenke606c59c2016-03-07 18:20:55557 base::Bind(&CookieStoreIOS::GetCookieListWithOptionsAsync,
558 weak_factory_.GetWeakPtr(), url, options, callback));
droger3e8d98b2015-03-18 15:29:53559 break;
560 case SYNCHRONIZED:
561 if (!SystemCookiesAllowed()) {
562 // If cookies are not allowed, the cookies are stashed in the
563 // CookieMonster, so get them from there.
mkwstc611e6d2016-02-23 15:45:55564 cookie_monster_->GetCookieListWithOptionsAsync(url, options, callback);
droger3e8d98b2015-03-18 15:29:53565 return;
566 }
567
mkwstc611e6d2016-02-23 15:45:55568 // TODO(mkwst): If/when iOS supports Same-Site cookies, we'll need to pass
569 // options in here as well. https://crbug.com/459154
shreyasv1ae3c3a32015-11-03 20:13:12570 NSArray* cookies = GetCookiesForURL(system_store_,
571 url, creation_time_manager_.get());
mmenke9fa44f2d2016-01-22 23:36:39572 net::CookieList cookie_list = CanonicalCookieListFromSystemCookies(
573 cookies);
droger3e8d98b2015-03-18 15:29:53574 if (!callback.is_null())
575 callback.Run(cookie_list);
576 break;
577 }
578}
579
mmenke9fa44f2d2016-01-22 23:36:39580void CookieStoreIOS::GetAllCookiesAsync(const GetCookieListCallback& callback) {
581 DCHECK(thread_checker_.CalledOnValidThread());
582
583 switch (synchronization_state_) {
584 case NOT_SYNCHRONIZED:
585 cookie_monster_->GetAllCookiesAsync(callback);
586 break;
587 case SYNCHRONIZING:
mmenke606c59c2016-03-07 18:20:55588 tasks_pending_synchronization_.push_back(
589 base::Bind(&CookieStoreIOS::GetAllCookiesAsync,
590 weak_factory_.GetWeakPtr(), callback));
mmenke9fa44f2d2016-01-22 23:36:39591 break;
592 case SYNCHRONIZED:
593 if (!SystemCookiesAllowed()) {
594 // If cookies are not allowed, the cookies are stashed in the
595 // CookieMonster, so get them from there.
596 cookie_monster_->GetAllCookiesAsync(callback);
597 return;
598 }
599
600 NSArray* cookies = GetAllCookies(system_store_,
601 creation_time_manager_.get());
602 net::CookieList cookie_list = CanonicalCookieListFromSystemCookies(
603 cookies);
604 if (!callback.is_null()) {
605 callback.Run(cookie_list);
606 }
607 break;
608 }
609}
610
droger3e8d98b2015-03-18 15:29:53611void CookieStoreIOS::DeleteCookieAsync(const GURL& url,
612 const std::string& cookie_name,
613 const base::Closure& callback) {
614 DCHECK(thread_checker_.CalledOnValidThread());
615
616 switch (synchronization_state_) {
617 case NOT_SYNCHRONIZED:
618 cookie_monster_->DeleteCookieAsync(url, cookie_name,
619 WrapClosure(callback));
620 break;
621 case SYNCHRONIZING:
mmenke606c59c2016-03-07 18:20:55622 tasks_pending_synchronization_.push_back(base::Bind(
623 &CookieStoreIOS::DeleteCookieAsync, weak_factory_.GetWeakPtr(), url,
624 cookie_name, WrapClosure(callback)));
droger3e8d98b2015-03-18 15:29:53625 break;
626 case SYNCHRONIZED:
shreyasv1ae3c3a32015-11-03 20:13:12627 NSArray* cookies = GetCookiesForURL(system_store_,
628 url, creation_time_manager_.get());
droger3e8d98b2015-03-18 15:29:53629 for (NSHTTPCookie* cookie in cookies) {
630 if ([[cookie name]
631 isEqualToString:base::SysUTF8ToNSString(cookie_name)]) {
shreyasv1ae3c3a32015-11-03 20:13:12632 [system_store_ deleteCookie:cookie];
droger3e8d98b2015-03-18 15:29:53633 creation_time_manager_->DeleteCreationTime(cookie);
634 }
635 }
636 if (!callback.is_null())
637 callback.Run();
638 break;
639 }
640}
641
mmenke24379d52016-02-05 23:50:17642void CookieStoreIOS::DeleteCanonicalCookieAsync(
643 const CanonicalCookie& cookie,
644 const DeleteCallback& callback) {
645 DCHECK(thread_checker_.CalledOnValidThread());
646
647 switch (synchronization_state_) {
648 case NOT_SYNCHRONIZED:
649 cookie_monster_->DeleteCanonicalCookieAsync(cookie,
650 WrapDeleteCallback(callback));
651 break;
652 case SYNCHRONIZING:
mmenke606c59c2016-03-07 18:20:55653 tasks_pending_synchronization_.push_back(base::Bind(
654 &CookieStoreIOS::DeleteCanonicalCookieAsync,
655 weak_factory_.GetWeakPtr(), cookie, WrapDeleteCallback(callback)));
mmenke24379d52016-02-05 23:50:17656 break;
657 case SYNCHRONIZED:
658 // This relies on the fact cookies are given unique creation dates.
659 CookieFilterFunction filter = base::Bind(
660 IsCookieCreatedBetween, cookie.CreationDate(), cookie.CreationDate());
661 DeleteCookiesWithFilter(filter, callback);
662 }
663}
664
droger3e8d98b2015-03-18 15:29:53665void CookieStoreIOS::DeleteAllCreatedBetweenAsync(
666 const base::Time& delete_begin,
667 const base::Time& delete_end,
668 const DeleteCallback& callback) {
669 DCHECK(thread_checker_.CalledOnValidThread());
670
671 if (metrics_enabled_)
672 ResetCookieCountMetrics();
673
674 switch (synchronization_state_) {
675 case NOT_SYNCHRONIZED:
676 cookie_monster_->DeleteAllCreatedBetweenAsync(
677 delete_begin, delete_end, WrapDeleteCallback(callback));
678 break;
679 case SYNCHRONIZING:
680 tasks_pending_synchronization_.push_back(
mmenke606c59c2016-03-07 18:20:55681 base::Bind(&CookieStoreIOS::DeleteAllCreatedBetweenAsync,
682 weak_factory_.GetWeakPtr(), delete_begin, delete_end,
683 WrapDeleteCallback(callback)));
droger3e8d98b2015-03-18 15:29:53684 break;
685 case SYNCHRONIZED:
686 CookieFilterFunction filter =
687 base::Bind(&IsCookieCreatedBetween, delete_begin, delete_end);
688 DeleteCookiesWithFilter(filter, callback);
689 break;
690 }
691}
692
dmurphfaea244c2016-04-09 00:42:30693void CookieStoreIOS::DeleteAllCreatedBetweenWithPredicateAsync(
694 const base::Time& delete_begin,
695 const base::Time& delete_end,
696 const CookiePredicate& predicate,
droger3e8d98b2015-03-18 15:29:53697 const DeleteCallback& callback) {
698 DCHECK(thread_checker_.CalledOnValidThread());
699
700 if (metrics_enabled_)
701 ResetCookieCountMetrics();
702
703 switch (synchronization_state_) {
704 case NOT_SYNCHRONIZED:
dmurphfaea244c2016-04-09 00:42:30705 cookie_monster_->DeleteAllCreatedBetweenWithPredicateAsync(
706 delete_begin, delete_end, predicate, WrapDeleteCallback(callback));
droger3e8d98b2015-03-18 15:29:53707 break;
708 case SYNCHRONIZING:
mmenke606c59c2016-03-07 18:20:55709 tasks_pending_synchronization_.push_back(
dmurphfaea244c2016-04-09 00:42:30710 base::Bind(&CookieStoreIOS::DeleteAllCreatedBetweenWithPredicateAsync,
711 weak_factory_.GetWeakPtr(), delete_begin, delete_end,
712 predicate, WrapDeleteCallback(callback)));
droger3e8d98b2015-03-18 15:29:53713 break;
714 case SYNCHRONIZED:
dmurphfaea244c2016-04-09 00:42:30715 CookieFilterFunction filter =
716 base::Bind(IsCookieCreatedBetweenWithPredicate, delete_begin,
717 delete_end, predicate);
droger3e8d98b2015-03-18 15:29:53718 DeleteCookiesWithFilter(filter, callback);
719 break;
720 }
721}
722
723void CookieStoreIOS::DeleteSessionCookiesAsync(const DeleteCallback& callback) {
724 DCHECK(thread_checker_.CalledOnValidThread());
725
726 if (metrics_enabled_)
727 ResetCookieCountMetrics();
728
729 switch (synchronization_state_) {
730 case NOT_SYNCHRONIZED:
731 cookie_monster_->DeleteSessionCookiesAsync(WrapDeleteCallback(callback));
732 break;
733 case SYNCHRONIZING:
734 tasks_pending_synchronization_.push_back(
mmenke606c59c2016-03-07 18:20:55735 base::Bind(&CookieStoreIOS::DeleteSessionCookiesAsync,
736 weak_factory_.GetWeakPtr(), WrapDeleteCallback(callback)));
droger3e8d98b2015-03-18 15:29:53737 break;
738 case SYNCHRONIZED:
739 CookieFilterFunction filter = base::Bind(&IsCookieSessionCookie);
740 DeleteCookiesWithFilter(filter, callback);
741 break;
742 }
743}
744
mmenke96f3bab2016-01-22 17:34:02745void CookieStoreIOS::FlushStore(const base::Closure& closure) {
746 DCHECK(thread_checker_.CalledOnValidThread());
747
748 if (SystemCookiesAllowed()) {
749 // If cookies are disabled, the system store is empty, and the cookies are
750 // stashed on disk. Do not delete the cookies on the disk in this case.
751 WriteToCookieMonster([system_store_ cookies]);
752 }
753 cookie_monster_->FlushStore(closure);
754 flush_closure_.Cancel();
755}
756
droger3e8d98b2015-03-18 15:29:53757#pragma mark -
droger3e8d98b2015-03-18 15:29:53758#pragma mark Private methods
759
760void CookieStoreIOS::ClearSystemStore() {
761 DCHECK(thread_checker_.CalledOnValidThread());
droger3e8d98b2015-03-18 15:29:53762 base::scoped_nsobject<NSArray> copy(
shreyasv1ae3c3a32015-11-03 20:13:12763 [[NSArray alloc] initWithArray:[system_store_ cookies]]);
droger3e8d98b2015-03-18 15:29:53764 for (NSHTTPCookie* cookie in copy.get())
shreyasv1ae3c3a32015-11-03 20:13:12765 [system_store_ deleteCookie:cookie];
766 DCHECK_EQ(0u, [[system_store_ cookies] count]);
droger3e8d98b2015-03-18 15:29:53767 creation_time_manager_->Clear();
768}
769
770void CookieStoreIOS::OnSystemCookiePolicyChanged() {
771 DCHECK(thread_checker_.CalledOnValidThread());
772
shreyasv1ae3c3a32015-11-03 20:13:12773 // If the CookieStoreIOS is not synchronized or is not backed by
774 // |NSHTTPCookieStorage sharedHTTPCookieStorage| this callback is irrelevant.
775 if (synchronization_state_ == NOT_SYNCHRONIZED ||
776 system_store_ != [NSHTTPCookieStorage sharedHTTPCookieStorage]) {
droger3e8d98b2015-03-18 15:29:53777 return;
shreyasv1ae3c3a32015-11-03 20:13:12778 }
droger3e8d98b2015-03-18 15:29:53779
droger3e8d98b2015-03-18 15:29:53780 NSHTTPCookieAcceptPolicy policy =
shreyasv1ae3c3a32015-11-03 20:13:12781 [system_store_ cookieAcceptPolicy];
droger3e8d98b2015-03-18 15:29:53782 if (policy == NSHTTPCookieAcceptPolicyAlways) {
783 // If cookies are disabled, the system cookie store should be empty.
shreyasv1ae3c3a32015-11-03 20:13:12784 DCHECK(![[system_store_ cookies] count]);
droger5aa3f262015-06-22 15:47:30785 DCHECK(synchronization_state_ != SYNCHRONIZING);
droger3e8d98b2015-03-18 15:29:53786 synchronization_state_ = SYNCHRONIZING;
mmenke606c59c2016-03-07 18:20:55787 cookie_monster_->GetAllCookiesAsync(base::Bind(
788 &CookieStoreIOS::AddCookiesToSystemStore, weak_factory_.GetWeakPtr()));
droger3e8d98b2015-03-18 15:29:53789 } else {
790 DCHECK_EQ(NSHTTPCookieAcceptPolicyNever, policy);
mmenke96f3bab2016-01-22 17:34:02791 // FlushStore() does not write the cookies to disk when they are disabled.
droger3e8d98b2015-03-18 15:29:53792 // Explicitly copy them.
shreyasv1ae3c3a32015-11-03 20:13:12793 WriteToCookieMonster([system_store_ cookies]);
mmenke96f3bab2016-01-22 17:34:02794 FlushStore(base::Closure());
droger3e8d98b2015-03-18 15:29:53795 ClearSystemStore();
droger5aa3f262015-06-22 15:47:30796 if (synchronization_state_ == SYNCHRONIZING) {
797 // If synchronization was in progress, abort it and leave the cookie store
798 // empty.
799 // Temporarily toggle the synchronization state so that pending tasks are
800 // redirected to cookie_monster_ and can complete normally.
801 synchronization_state_ = NOT_SYNCHRONIZED;
802 RunAllPendingTasks();
803 synchronization_state_ = SYNCHRONIZED;
804 }
droger3e8d98b2015-03-18 15:29:53805 }
806}
807
808void CookieStoreIOS::SetSynchronizedWithSystemStore(bool synchronized) {
809 DCHECK(thread_checker_.CalledOnValidThread());
810
811 if (synchronized == (synchronization_state_ != NOT_SYNCHRONIZED))
812 return; // The cookie store is already in the desired state.
813
814#if !defined(NDEBUG)
815 if (!synchronized) {
816 DCHECK_EQ(this, g_current_synchronized_store)
817 << "This cookie store was not synchronized";
818 g_current_synchronized_store = nullptr;
819 } else {
820 DCHECK_EQ((CookieStoreIOS*)nullptr, g_current_synchronized_store)
821 << "Un-synchronize the current cookie store first.";
822 g_current_synchronized_store = this;
823 }
824#endif
825
826 NSHTTPCookieAcceptPolicy policy =
shreyasv1ae3c3a32015-11-03 20:13:12827 [system_store_ cookieAcceptPolicy];
droger3e8d98b2015-03-18 15:29:53828 DCHECK(policy == NSHTTPCookieAcceptPolicyAlways ||
829 policy == NSHTTPCookieAcceptPolicyNever);
830
831 // If cookies are disabled, the system cookie store should be empty.
832 DCHECK(policy == NSHTTPCookieAcceptPolicyAlways ||
shreyasv1ae3c3a32015-11-03 20:13:12833 ![[system_store_ cookies] count]);
droger3e8d98b2015-03-18 15:29:53834
835 // If cookies are disabled, nothing is done now, the work will be done when
836 // cookies are re-enabled.
837 if (policy == NSHTTPCookieAcceptPolicyAlways) {
838 if (synchronized) {
839 synchronization_state_ = SYNCHRONIZING;
840 ClearSystemStore();
841 cookie_monster_->GetAllCookiesAsync(
mmenke606c59c2016-03-07 18:20:55842 base::Bind(&CookieStoreIOS::AddCookiesToSystemStore,
843 weak_factory_.GetWeakPtr()));
droger3e8d98b2015-03-18 15:29:53844 return;
845 } else {
846 // Copy the cookies from the global store to |cookie_monster_|.
mmenke96f3bab2016-01-22 17:34:02847 FlushStore(base::Closure());
droger3e8d98b2015-03-18 15:29:53848 }
849 }
850 synchronization_state_ = synchronized ? SYNCHRONIZED : NOT_SYNCHRONIZED;
droger5aa3f262015-06-22 15:47:30851
852 if (synchronization_state_ == NOT_SYNCHRONIZED) {
853 // If there are pending tasks, then it means that the synchronization is
854 // being canceled. All pending tasks can be sent to cookie_monster_.
855 RunAllPendingTasks();
856 }
droger3e8d98b2015-03-18 15:29:53857}
858
859bool CookieStoreIOS::SystemCookiesAllowed() {
860 DCHECK(thread_checker_.CalledOnValidThread());
shreyasv1ae3c3a32015-11-03 20:13:12861 return [system_store_ cookieAcceptPolicy] ==
droger3e8d98b2015-03-18 15:29:53862 NSHTTPCookieAcceptPolicyAlways;
863}
864
865void CookieStoreIOS::AddCookiesToSystemStore(const net::CookieList& cookies) {
866 DCHECK(thread_checker_.CalledOnValidThread());
867 if (!SystemCookiesAllowed() || synchronization_state_ != SYNCHRONIZING) {
droger5aa3f262015-06-22 15:47:30868 // If synchronization was aborted, the pending tasks have been processed at
869 // that time. Now is too late.
droger3e8d98b2015-03-18 15:29:53870 DCHECK(tasks_pending_synchronization_.empty());
871 return;
872 }
873
874 // Report metrics.
875 if (metrics_enabled_) {
876 size_t cookie_count = cookies.size();
877 UMA_HISTOGRAM_COUNTS_10000("CookieIOS.CookieReadCount", cookie_count);
878 CheckForCookieLoss(cookie_count, COOKIES_READ);
879 }
880
881 net::CookieList::const_iterator it;
882 for (it = cookies.begin(); it != cookies.end(); ++it) {
883 const net::CanonicalCookie& net_cookie = *it;
884 NSHTTPCookie* system_cookie = SystemCookieFromCanonicalCookie(net_cookie);
885 // Canonical cookie may not be convertable into system cookie if it contains
886 // invalid characters.
887 if (!system_cookie)
888 continue;
shreyasv1ae3c3a32015-11-03 20:13:12889 [system_store_ setCookie:system_cookie];
droger3e8d98b2015-03-18 15:29:53890 creation_time_manager_->SetCreationTime(system_cookie,
891 net_cookie.CreationDate());
892 }
893
894 synchronization_state_ = SYNCHRONIZED;
droger5aa3f262015-06-22 15:47:30895 RunAllPendingTasks();
droger3e8d98b2015-03-18 15:29:53896}
897
898void CookieStoreIOS::WriteToCookieMonster(NSArray* system_cookies) {
899 DCHECK(thread_checker_.CalledOnValidThread());
900 if (synchronization_state_ != SYNCHRONIZED)
901 return;
902
903 // Copy the cookies from the global cookie store to |cookie_monster_|.
904 // Unlike the system store, CookieMonster requires unique creation times.
905 net::CookieList cookie_list;
906 NSUInteger cookie_count = [system_cookies count];
907 cookie_list.reserve(cookie_count);
908 for (NSHTTPCookie* cookie in system_cookies) {
909 cookie_list.push_back(CanonicalCookieFromSystemCookie(
910 cookie, creation_time_manager_->GetCreationTime(cookie)));
911 }
912 cookie_monster_->SetAllCookiesAsync(cookie_list, SetCookiesCallback());
913
914 // Update metrics.
915 if (metrics_enabled_)
916 UMA_HISTOGRAM_COUNTS_10000("CookieIOS.CookieWrittenCount", cookie_count);
917}
918
droger5aa3f262015-06-22 15:47:30919void CookieStoreIOS::RunAllPendingTasks() {
920 // Executing the tasks while synchronizing would not run the tasks, but merely
921 // re-enqueue them. This function also does not support mutation of the queue
922 // during the iteration.
923 DCHECK(synchronization_state_ != SYNCHRONIZING);
924 for (const auto& task : tasks_pending_synchronization_) {
925 task.Run();
926 }
927 tasks_pending_synchronization_.clear();
928}
929
droger3e8d98b2015-03-18 15:29:53930void CookieStoreIOS::DeleteCookiesWithFilter(const CookieFilterFunction& filter,
931 const DeleteCallback& callback) {
932 DCHECK(thread_checker_.CalledOnValidThread());
933 DCHECK_EQ(SYNCHRONIZED, synchronization_state_);
shreyasv1ae3c3a32015-11-03 20:13:12934 NSArray* cookies = [system_store_ cookies];
droger3e8d98b2015-03-18 15:29:53935
936 // Collect the cookies to delete.
937 base::scoped_nsobject<NSMutableArray> to_delete(
938 [[NSMutableArray alloc] init]);
939 for (NSHTTPCookie* cookie in cookies) {
940 base::Time creation_time = creation_time_manager_->GetCreationTime(cookie);
941 if (filter.Run(cookie, creation_time))
942 [to_delete addObject:cookie];
943 }
944
945 // Delete them.
946 for (NSHTTPCookie* cookie in to_delete.get()) {
shreyasv1ae3c3a32015-11-03 20:13:12947 [system_store_ deleteCookie:cookie];
droger3e8d98b2015-03-18 15:29:53948 creation_time_manager_->DeleteCreationTime(cookie);
949 }
950
951 if (!callback.is_null())
952 callback.Run([to_delete count]);
953}
954
955void CookieStoreIOS::OnSystemCookiesChanged() {
956 DCHECK(thread_checker_.CalledOnValidThread());
957
shreyasv1ae3c3a32015-11-03 20:13:12958 // If the CookieStoreIOS is not synchronized or is not backed by
959 // |NSHTTPCookieStorage sharedHTTPCookieStorage| this callback is irrelevant.
960 if (synchronization_state_ != SYNCHRONIZED ||
961 system_store_ != [NSHTTPCookieStorage sharedHTTPCookieStorage]) {
droger3e8d98b2015-03-18 15:29:53962 return;
shreyasv1ae3c3a32015-11-03 20:13:12963 }
droger3e8d98b2015-03-18 15:29:53964
965 for (const auto& hook_map_entry : hook_map_) {
966 std::pair<GURL, std::string> key = hook_map_entry.first;
967 std::vector<net::CanonicalCookie> removed_cookies;
968 std::vector<net::CanonicalCookie> added_cookies;
969 if (UpdateCacheForCookieFromSystem(key.first, key.second, &removed_cookies,
970 &added_cookies)) {
nharper352933e2016-09-30 18:24:57971 RunCallbacksForCookies(key.first, key.second, removed_cookies,
972 net::CookieStore::ChangeCause::UNKNOWN_DELETION);
973 RunCallbacksForCookies(key.first, key.second, added_cookies,
974 net::CookieStore::ChangeCause::INSERTED);
droger3e8d98b2015-03-18 15:29:53975 }
976 }
977
978 // Do not schedule a flush if one is already scheduled.
979 if (!flush_closure_.IsCancelled())
980 return;
981
mmenke96f3bab2016-01-22 17:34:02982 flush_closure_.Reset(base::Bind(&CookieStoreIOS::FlushStore,
mmenke606c59c2016-03-07 18:20:55983 weak_factory_.GetWeakPtr(), base::Closure()));
skyostilc2740982015-06-05 19:15:31984 base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
droger3e8d98b2015-03-18 15:29:53985 FROM_HERE, flush_closure_.callback(), flush_delay_);
986}
987
dcheng942f39d72016-04-07 21:11:23988std::unique_ptr<net::CookieStore::CookieChangedSubscription>
droger3e8d98b2015-03-18 15:29:53989CookieStoreIOS::AddCallbackForCookie(const GURL& gurl,
990 const std::string& name,
991 const CookieChangedCallback& callback) {
992 DCHECK(thread_checker_.CalledOnValidThread());
993
994 // Prefill cookie cache with all pertinent cookies for |url| if needed.
995 std::pair<GURL, std::string> key(gurl, name);
996 if (hook_map_.count(key) == 0) {
997 UpdateCacheForCookieFromSystem(gurl, name, nullptr, nullptr);
998 if (hook_map_.count(key) == 0)
avide3063472016-09-19 13:52:27999 hook_map_[key] = base::MakeUnique<CookieChangedCallbackList>();
droger3e8d98b2015-03-18 15:29:531000 }
1001
1002 DCHECK(hook_map_.find(key) != hook_map_.end());
1003 return hook_map_[key]->Add(callback);
1004}
1005
nharper5babb5e62016-03-09 18:58:071006bool CookieStoreIOS::IsEphemeral() {
1007 return cookie_monster_->IsEphemeral();
1008}
1009
droger3e8d98b2015-03-18 15:29:531010bool CookieStoreIOS::UpdateCacheForCookieFromSystem(
1011 const GURL& gurl,
1012 const std::string& name,
1013 std::vector<net::CanonicalCookie>* out_removed_cookies,
1014 std::vector<net::CanonicalCookie>* out_added_cookies) {
1015 DCHECK(thread_checker_.CalledOnValidThread());
1016 std::vector<net::CanonicalCookie> system_cookies;
1017 GetSystemCookies(gurl, name, &system_cookies);
1018 return cookie_cache_->Update(gurl, name, system_cookies, out_removed_cookies,
1019 out_added_cookies);
1020}
1021
1022void CookieStoreIOS::RunCallbacksForCookies(
1023 const GURL& url,
1024 const std::string& name,
1025 const std::vector<net::CanonicalCookie>& cookies,
nharper352933e2016-09-30 18:24:571026 net::CookieStore::ChangeCause cause) {
droger3e8d98b2015-03-18 15:29:531027 DCHECK(thread_checker_.CalledOnValidThread());
1028 if (cookies.empty())
1029 return;
1030
1031 std::pair<GURL, std::string> key(url, name);
avide3063472016-09-19 13:52:271032 CookieChangedCallbackList* callbacks = hook_map_[key].get();
droger3e8d98b2015-03-18 15:29:531033 for (const auto& cookie : cookies) {
1034 DCHECK_EQ(name, cookie.Name());
nharper352933e2016-09-30 18:24:571035 callbacks->Notify(cookie, cause);
droger3e8d98b2015-03-18 15:29:531036 }
1037}
1038
1039bool CookieStoreIOS::GetSystemCookies(
1040 const GURL& gurl,
1041 const std::string& name,
1042 std::vector<net::CanonicalCookie>* cookies) {
1043 DCHECK(thread_checker_.CalledOnValidThread());
1044 NSURL* url = net::NSURLWithGURL(gurl);
shreyasv1ae3c3a32015-11-03 20:13:121045 NSArray* nscookies = [system_store_ cookiesForURL:url];
droger3e8d98b2015-03-18 15:29:531046 bool found_cookies = false;
1047 for (NSHTTPCookie* nscookie in nscookies) {
1048 if (nscookie.name.UTF8String == name) {
1049 net::CanonicalCookie canonical_cookie = CanonicalCookieFromSystemCookie(
1050 nscookie, creation_time_manager_->GetCreationTime(nscookie));
1051 cookies->push_back(canonical_cookie);
1052 found_cookies = true;
1053 }
1054 }
1055 return found_cookies;
1056}
1057
1058void CookieStoreIOS::GotCookieListFor(const std::pair<GURL, std::string> key,
1059 const net::CookieList& cookies) {
1060 DCHECK(thread_checker_.CalledOnValidThread());
1061
1062 net::CookieList filtered;
1063 OnlyCookiesWithName(cookies, key.second, &filtered);
1064 std::vector<net::CanonicalCookie> removed_cookies;
1065 std::vector<net::CanonicalCookie> added_cookies;
1066 if (cookie_cache_->Update(key.first, key.second, filtered, &removed_cookies,
1067 &added_cookies)) {
nharper352933e2016-09-30 18:24:571068 RunCallbacksForCookies(key.first, key.second, removed_cookies,
1069 net::CookieStore::ChangeCause::UNKNOWN_DELETION);
1070 RunCallbacksForCookies(key.first, key.second, added_cookies,
1071 net::CookieStore::ChangeCause::INSERTED);
droger3e8d98b2015-03-18 15:29:531072 }
1073}
1074
1075void CookieStoreIOS::DidClearNSHTTPCookieStorageCookies(
1076 const DeleteCallback& delete_callback,
1077 int num_deleted) {
1078 DCHECK(thread_checker_.CalledOnValidThread());
1079
1080 CookieStoreIOSClient* client = net::GetCookieStoreIOSClient();
1081 DCHECK(client);
1082 auto sequenced_task_runner = client->GetTaskRunner();
1083 DCHECK(sequenced_task_runner);
mmenke606c59c2016-03-07 18:20:551084 auto callback =
1085 base::Bind(&CookieStoreIOS::DidClearBinaryCookiesFileCookies,
1086 weak_factory_.GetWeakPtr(), delete_callback, num_deleted);
droger3e8d98b2015-03-18 15:29:531087 sequenced_task_runner.get()->PostTaskAndReply(
1088 FROM_HERE, base::Bind(&ClearAllCookiesFromBinaryCookiesFile), callback);
1089}
1090
1091void CookieStoreIOS::DidClearBinaryCookiesFileCookies(
1092 const DeleteCallback& callback,
1093 int num_deleted_from_nshttp_cookie_storage) {
1094 DCHECK(thread_checker_.CalledOnValidThread());
1095
1096 CookieStoreIOSClient* client = net::GetCookieStoreIOSClient();
1097 DCHECK(client);
1098 client->DidChangeCookieStorage();
droger4c3ea112016-02-22 19:19:331099 if (!callback.is_null())
1100 callback.Run(num_deleted_from_nshttp_cookie_storage);
droger3e8d98b2015-03-18 15:29:531101}
1102
1103void CookieStoreIOS::UpdateCachesFromCookieMonster() {
1104 DCHECK(thread_checker_.CalledOnValidThread());
1105 for (const auto& hook_map_entry : hook_map_) {
1106 std::pair<GURL, std::string> key = hook_map_entry.first;
mmenke606c59c2016-03-07 18:20:551107 GetCookieListCallback callback = base::Bind(
1108 &CookieStoreIOS::GotCookieListFor, weak_factory_.GetWeakPtr(), key);
droger3e8d98b2015-03-18 15:29:531109 cookie_monster_->GetAllCookiesForURLAsync(key.first, callback);
1110 }
1111}
1112
1113void CookieStoreIOS::UpdateCachesAfterSet(const SetCookiesCallback& callback,
1114 bool success) {
1115 DCHECK(thread_checker_.CalledOnValidThread());
1116 if (success)
1117 UpdateCachesFromCookieMonster();
droger4c3ea112016-02-22 19:19:331118 if (!callback.is_null())
1119 callback.Run(success);
droger3e8d98b2015-03-18 15:29:531120}
1121
1122void CookieStoreIOS::UpdateCachesAfterDelete(const DeleteCallback& callback,
1123 int num_deleted) {
1124 DCHECK(thread_checker_.CalledOnValidThread());
1125 UpdateCachesFromCookieMonster();
droger4c3ea112016-02-22 19:19:331126 if (!callback.is_null())
1127 callback.Run(num_deleted);
droger3e8d98b2015-03-18 15:29:531128}
1129
1130void CookieStoreIOS::UpdateCachesAfterClosure(const base::Closure& callback) {
1131 DCHECK(thread_checker_.CalledOnValidThread());
1132 UpdateCachesFromCookieMonster();
droger4c3ea112016-02-22 19:19:331133 if (!callback.is_null())
1134 callback.Run();
droger3e8d98b2015-03-18 15:29:531135}
1136
mmenke9fa44f2d2016-01-22 23:36:391137net::CookieList
1138CookieStoreIOS::CanonicalCookieListFromSystemCookies(NSArray* cookies) {
1139 net::CookieList cookie_list;
1140 cookie_list.reserve([cookies count]);
1141 for (NSHTTPCookie* cookie in cookies) {
1142 base::Time created = creation_time_manager_->GetCreationTime(cookie);
1143 cookie_list.push_back(CanonicalCookieFromSystemCookie(cookie, created));
1144 }
1145 return cookie_list;
1146}
1147
droger3e8d98b2015-03-18 15:29:531148CookieStoreIOS::SetCookiesCallback CookieStoreIOS::WrapSetCallback(
1149 const SetCookiesCallback& callback) {
1150 DCHECK(thread_checker_.CalledOnValidThread());
mmenke606c59c2016-03-07 18:20:551151 return base::Bind(&CookieStoreIOS::UpdateCachesAfterSet,
1152 weak_factory_.GetWeakPtr(), callback);
droger3e8d98b2015-03-18 15:29:531153}
1154
1155CookieStoreIOS::DeleteCallback CookieStoreIOS::WrapDeleteCallback(
1156 const DeleteCallback& callback) {
1157 DCHECK(thread_checker_.CalledOnValidThread());
mmenke606c59c2016-03-07 18:20:551158 return base::Bind(&CookieStoreIOS::UpdateCachesAfterDelete,
1159 weak_factory_.GetWeakPtr(), callback);
droger3e8d98b2015-03-18 15:29:531160}
1161
1162base::Closure CookieStoreIOS::WrapClosure(const base::Closure& callback) {
1163 DCHECK(thread_checker_.CalledOnValidThread());
mmenke606c59c2016-03-07 18:20:551164 return base::Bind(&CookieStoreIOS::UpdateCachesAfterClosure,
1165 weak_factory_.GetWeakPtr(), callback);
droger3e8d98b2015-03-18 15:29:531166}
1167
1168} // namespace net