blob: d13730de170c2029b3aa76dce6b14c18c1d5cf87 [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
Mohammad Refaat6a4276492017-09-08 00:18:525#import "ios/net/cookies/cookie_store_ios.h"
droger3e8d98b2015-03-18 15:29:536
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"
droger3e8d98b2015-03-18 15:29:5313#include "base/location.h"
14#include "base/logging.h"
15#include "base/mac/foundation_util.h"
avi571943672015-12-22 02:12:4916#include "base/macros.h"
droger3e8d98b2015-03-18 15:29:5317#include "base/memory/weak_ptr.h"
asvitkinef1899e32017-01-27 16:30:2918#include "base/metrics/histogram_macros.h"
droger3e8d98b2015-03-18 15:29:5319#include "base/observer_list.h"
20#include "base/sequenced_task_runner.h"
droger3e8d98b2015-03-18 15:29:5321#include "base/strings/sys_string_conversions.h"
22#include "base/task_runner_util.h"
23#include "base/threading/thread_restrictions.h"
gab3d426fa62016-05-11 18:13:3324#include "base/threading/thread_task_runner_handle.h"
rdsmitha6ce4442017-06-21 17:11:0525#include "base/time/time.h"
droger3e8d98b2015-03-18 15:29:5326#include "ios/net/cookies/cookie_store_ios_client.h"
Mohammad Refaat6a4276492017-09-08 00:18:5227#import "ios/net/cookies/ns_http_system_cookie_store.h"
28#import "ios/net/cookies/system_cookie_util.h"
Scott Violetd9ec3a6d2018-03-27 03:08:5629#include "ios/net/ios_net_buildflags.h"
droger3e8d98b2015-03-18 15:29:5330#import "net/base/mac/url_conversions.h"
31#include "net/cookies/cookie_util.h"
ellyjones98a0b9202015-09-04 17:22:2432#include "net/cookies/parsed_cookie.h"
Helen Li4e4cb0d2018-08-21 14:50:4733#include "net/log/net_log.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
Chris Mumfordd8ed9f82018-05-01 15:43:1342using CookieDeletionInfo = CookieDeletionInfo;
Chris Mumford800caa62018-04-20 19:34:4443
droger3e8d98b2015-03-18 15:29:5344namespace {
45
Victor Costan14f47c12018-03-01 08:02:2446class CookieStoreIOSCookieChangeSubscription : public CookieChangeSubscription {
Randy Smith4780ccc2017-09-29 17:39:4147 public:
Victor Costan14f47c12018-03-01 08:02:2448 using CookieChangeCallbackList =
49 base::CallbackList<void(const CanonicalCookie& cookie,
50 CookieChangeCause cause)>;
51 CookieStoreIOSCookieChangeSubscription(
52 std::unique_ptr<CookieChangeCallbackList::Subscription> subscription)
Randy Smith4780ccc2017-09-29 17:39:4153 : subscription_(std::move(subscription)) {}
Victor Costan14f47c12018-03-01 08:02:2454 ~CookieStoreIOSCookieChangeSubscription() override {}
Randy Smith4780ccc2017-09-29 17:39:4155
56 private:
Victor Costan14f47c12018-03-01 08:02:2457 std::unique_ptr<CookieChangeCallbackList::Subscription> subscription_;
Randy Smith4780ccc2017-09-29 17:39:4158
Victor Costan14f47c12018-03-01 08:02:2459 DISALLOW_COPY_AND_ASSIGN(CookieStoreIOSCookieChangeSubscription);
Randy Smith4780ccc2017-09-29 17:39:4160};
61
droger3e8d98b2015-03-18 15:29:5362#pragma mark NotificationTrampoline
63
64// NotificationTrampoline dispatches cookie notifications to all the existing
65// CookieStoreIOS.
66class NotificationTrampoline {
67 public:
68 static NotificationTrampoline* GetInstance();
69
70 void AddObserver(CookieNotificationObserver* obs);
71 void RemoveObserver(CookieNotificationObserver* obs);
72
73 // Notify the observers.
74 void NotifyCookiesChanged();
droger3e8d98b2015-03-18 15:29:5375
76 private:
77 NotificationTrampoline();
78 ~NotificationTrampoline();
79
Trent Apteda250ec3ab2018-08-19 08:52:1980 base::ObserverList<CookieNotificationObserver>::Unchecked observer_list_;
droger3e8d98b2015-03-18 15:29:5381
82 DISALLOW_COPY_AND_ASSIGN(NotificationTrampoline);
83
84 static NotificationTrampoline* g_notification_trampoline;
85};
86
87#pragma mark NotificationTrampoline implementation
88
89NotificationTrampoline* NotificationTrampoline::GetInstance() {
90 if (!g_notification_trampoline)
91 g_notification_trampoline = new NotificationTrampoline;
92 return g_notification_trampoline;
93}
94
95void NotificationTrampoline::AddObserver(CookieNotificationObserver* obs) {
96 observer_list_.AddObserver(obs);
97}
98
99void NotificationTrampoline::RemoveObserver(CookieNotificationObserver* obs) {
100 observer_list_.RemoveObserver(obs);
101}
102
103void NotificationTrampoline::NotifyCookiesChanged() {
ericwilligersff4feda2016-10-18 00:33:44104 for (auto& observer : observer_list_)
105 observer.OnSystemCookiesChanged();
droger3e8d98b2015-03-18 15:29:53106}
107
droger3e8d98b2015-03-18 15:29:53108NotificationTrampoline::NotificationTrampoline() {
109}
110
111NotificationTrampoline::~NotificationTrampoline() {
112}
113
114// Global instance of NotificationTrampoline.
115NotificationTrampoline* NotificationTrampoline::g_notification_trampoline =
116 nullptr;
117
118#pragma mark Utility functions
119
droger3e8d98b2015-03-18 15:29:53120// Builds a NSHTTPCookie from a header cookie line ("Set-Cookie: xxx") and a
121// URL.
122NSHTTPCookie* GetNSHTTPCookieFromCookieLine(const std::string& cookie_line,
123 const GURL& url,
124 base::Time server_time) {
125 NSURL* nsurl = net::NSURLWithGURL(url);
126 NSString* ns_cookie_line = base::SysUTF8ToNSString(cookie_line);
127 if (!ns_cookie_line) {
128 DLOG(ERROR) << "Cookie line is not UTF8: " << cookie_line;
129 return nil;
130 }
131 NSArray* cookies = [NSHTTPCookie cookiesWithResponseHeaderFields:@{
132 @"Set-Cookie" : ns_cookie_line
133 } forURL:nsurl];
134 if ([cookies count] != 1)
135 return nil;
136
137 NSHTTPCookie* cookie = [cookies objectAtIndex:0];
138 if (![cookie expiresDate] || server_time.is_null())
139 return cookie;
140
141 // Perform clock skew correction.
142 base::TimeDelta clock_skew = base::Time::Now() - server_time;
143 NSDate* corrected_expire_date =
144 [[cookie expiresDate] dateByAddingTimeInterval:clock_skew.InSecondsF()];
145 NSMutableDictionary* properties =
146 [NSMutableDictionary dictionaryWithDictionary:[cookie properties]];
147 [properties setObject:corrected_expire_date forKey:NSHTTPCookieExpires];
148 NSHTTPCookie* corrected_cookie =
149 [NSHTTPCookie cookieWithProperties:properties];
150 DCHECK(corrected_cookie);
151 return corrected_cookie;
152}
153
Mohammad Refaat41032802017-10-06 04:15:57154// Returns an empty closure if |callback| is null callback or binds the
155// callback to |success|.
156base::OnceClosure BindSetCookiesCallback(
157 CookieStoreIOS::SetCookiesCallback* callback,
158 bool success) {
159 base::OnceClosure set_callback;
160 if (!callback->is_null()) {
161 set_callback = base::BindOnce(std::move(*callback), success);
162 }
163 return set_callback;
164}
165
droger3e8d98b2015-03-18 15:29:53166// Adds cookies in |cookies| with name |name| to |filtered|.
167void OnlyCookiesWithName(const net::CookieList& cookies,
168 const std::string& name,
169 net::CookieList* filtered) {
170 for (const auto& cookie : cookies) {
171 if (cookie.Name() == name)
172 filtered->push_back(cookie);
173 }
174}
175
ellyjones98a0b9202015-09-04 17:22:24176// Returns whether the specified cookie line has an explicit Domain attribute or
177// not.
178bool HasExplicitDomain(const std::string& cookie_line) {
179 ParsedCookie cookie(cookie_line);
180 return cookie.HasDomain();
181}
182
droger3e8d98b2015-03-18 15:29:53183} // namespace
184
185#pragma mark -
Victor Costan14f47c12018-03-01 08:02:24186
187#pragma mark CookieStoreIOS::CookieChangeDispatcherIOS
188
189CookieStoreIOS::CookieChangeDispatcherIOS::CookieChangeDispatcherIOS(
190 CookieStoreIOS* cookie_store)
191 : cookie_store_(cookie_store) {
192 DCHECK(cookie_store);
193}
194
195CookieStoreIOS::CookieChangeDispatcherIOS::~CookieChangeDispatcherIOS() =
196 default;
197
198std::unique_ptr<CookieChangeSubscription>
199CookieStoreIOS::CookieChangeDispatcherIOS::AddCallbackForCookie(
200 const GURL& gurl,
201 const std::string& name,
202 CookieChangeCallback callback) {
203 return cookie_store_->AddCallbackForCookie(gurl, name, std::move(callback));
204}
205
206std::unique_ptr<CookieChangeSubscription>
Victor Costan2a1891672018-03-02 06:01:53207CookieStoreIOS::CookieChangeDispatcherIOS::AddCallbackForUrl(
208 const GURL& gurl,
209 CookieChangeCallback callback) {
210 // Implement when needed by iOS consumers.
211 NOTIMPLEMENTED();
212 return nullptr;
213}
214
215std::unique_ptr<CookieChangeSubscription>
Victor Costan14f47c12018-03-01 08:02:24216CookieStoreIOS::CookieChangeDispatcherIOS::AddCallbackForAllChanges(
217 CookieChangeCallback callback) {
218 // Implement when needed by iOS consumers.
219 NOTIMPLEMENTED();
220 return nullptr;
221}
222
droger3e8d98b2015-03-18 15:29:53223#pragma mark CookieStoreIOS
224
Mohammad Refaat6a4276492017-09-08 00:18:52225CookieStoreIOS::CookieStoreIOS(
Helen Li4e4cb0d2018-08-21 14:50:47226 std::unique_ptr<SystemCookieStore> system_cookie_store,
227 NetLog* net_log)
Mohammad Refaat6a4276492017-09-08 00:18:52228 : CookieStoreIOS(/*persistent_store=*/nullptr,
Helen Li4e4cb0d2018-08-21 14:50:47229 std::move(system_cookie_store),
230 net_log) {}
Mohammad Refaat6a4276492017-09-08 00:18:52231
Thomas Anderson1a03bbe2018-03-02 19:05:47232CookieStoreIOS::CookieStoreIOS(NSHTTPCookieStorage* ns_cookie_store)
Helen Li4e4cb0d2018-08-21 14:50:47233 : CookieStoreIOS(std::make_unique<NSHTTPSystemCookieStore>(ns_cookie_store),
234 nullptr /* net_log */) {}
235
236CookieStoreIOS::CookieStoreIOS(NSHTTPCookieStorage* ns_cookie_store,
237 NetLog* net_log)
238 : CookieStoreIOS(std::make_unique<NSHTTPSystemCookieStore>(ns_cookie_store),
239 net_log) {}
shreyasv1ae3c3a32015-11-03 20:13:12240
mmenke606c59c2016-03-07 18:20:55241CookieStoreIOS::~CookieStoreIOS() {
242 NotificationTrampoline::GetInstance()->RemoveObserver(this);
mmenke606c59c2016-03-07 18:20:55243}
244
droger3e8d98b2015-03-18 15:29:53245// static
droger3e8d98b2015-03-18 15:29:53246void CookieStoreIOS::NotifySystemCookiesChanged() {
247 NotificationTrampoline::GetInstance()->NotifyCookiesChanged();
248}
249
droger3e8d98b2015-03-18 15:29:53250void CookieStoreIOS::SetMetricsEnabled() {
251 static CookieStoreIOS* g_cookie_store_with_metrics = nullptr;
252 DCHECK(!g_cookie_store_with_metrics || g_cookie_store_with_metrics == this)
253 << "Only one cookie store may use metrics.";
254 g_cookie_store_with_metrics = this;
255 metrics_enabled_ = true;
256}
257
258#pragma mark -
259#pragma mark CookieStore methods
260
261void CookieStoreIOS::SetCookieWithOptionsAsync(
262 const GURL& url,
263 const std::string& cookie_line,
264 const net::CookieOptions& options,
rdsmith7ac81712017-06-22 17:09:54265 SetCookiesCallback callback) {
Victor Costanb805fba2018-02-21 13:49:42266 DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
droger3e8d98b2015-03-18 15:29:53267
Victor Costanfb17d1a2018-02-23 03:42:26268 // If cookies are not allowed, a CookieStoreIOS subclass should be used
269 // instead.
270 DCHECK(SystemCookiesAllowed());
271
maksim.sisovfdd9f092017-01-30 10:41:42272 // The exclude_httponly() option would only be used by a javascript
273 // engine.
274 DCHECK(!options.exclude_httponly());
droger3e8d98b2015-03-18 15:29:53275
maksim.sisovfdd9f092017-01-30 10:41:42276 base::Time server_time =
277 options.has_server_time() ? options.server_time() : base::Time();
278 NSHTTPCookie* cookie =
279 GetNSHTTPCookieFromCookieLine(cookie_line, url, server_time);
280 DLOG_IF(WARNING, !cookie) << "Could not create cookie for line: "
281 << cookie_line;
droger3e8d98b2015-03-18 15:29:53282
maksim.sisovfdd9f092017-01-30 10:41:42283 // On iOS, [cookie domain] is not empty when the cookie domain is not
284 // specified: it is inferred from the URL instead. The only case when it
285 // is empty is when the domain attribute is incorrectly formatted.
286 std::string domain_string(base::SysNSStringToUTF8([cookie domain]));
287 std::string dummy;
288 bool has_explicit_domain = HasExplicitDomain(cookie_line);
289 bool has_valid_domain =
290 net::cookie_util::GetCookieDomainWithString(url, domain_string, &dummy);
291 // A cookie can be set if all of:
292 // a) The cookie line is well-formed
293 // b) The Domain attribute, if present, was not malformed
294 // c) At least one of:
295 // 1) The cookie had no explicit Domain, so the Domain was inferred
296 // from the URL, or
297 // 2) The cookie had an explicit Domain for which the URL is allowed
298 // to set cookies.
299 bool success = (cookie != nil) && !domain_string.empty() &&
300 (!has_explicit_domain || has_valid_domain);
droger3e8d98b2015-03-18 15:29:53301
maksim.sisovfdd9f092017-01-30 10:41:42302 if (success) {
Mohammad Refaat41032802017-10-06 04:15:57303 system_store_->SetCookieAsync(cookie,
304 BindSetCookiesCallback(&callback, true));
305 return;
droger3e8d98b2015-03-18 15:29:53306 }
maksim.sisovfdd9f092017-01-30 10:41:42307
308 if (!callback.is_null())
Mohammad Refaat41032802017-10-06 04:15:57309 std::move(callback).Run(false);
droger3e8d98b2015-03-18 15:29:53310}
311
rdsmitha6ce4442017-06-21 17:11:05312void CookieStoreIOS::SetCanonicalCookieAsync(
313 std::unique_ptr<net::CanonicalCookie> cookie,
314 bool secure_source,
315 bool modify_http_only,
rdsmith7ac81712017-06-22 17:09:54316 SetCookiesCallback callback) {
Victor Costanfb17d1a2018-02-23 03:42:26317 DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
318
319 // If cookies are not allowed, a CookieStoreIOS subclass should be used
320 // instead.
321 DCHECK(SystemCookiesAllowed());
322
rdsmitha6ce4442017-06-21 17:11:05323 DCHECK(cookie->IsCanonical());
324 // The exclude_httponly() option would only be used by a javascript
325 // engine.
326 DCHECK(modify_http_only);
327
328 if (cookie->IsSecure() && !secure_source) {
329 if (!callback.is_null())
rdsmith7ac81712017-06-22 17:09:54330 std::move(callback).Run(false);
rdsmitha6ce4442017-06-21 17:11:05331 return;
332 }
333
334 NSHTTPCookie* ns_cookie = SystemCookieFromCanonicalCookie(*cookie.get());
335
336 if (ns_cookie != nil) {
Mohammad Refaat41032802017-10-06 04:15:57337 system_store_->SetCookieAsync(ns_cookie, &cookie->CreationDate(),
338 BindSetCookiesCallback(&callback, true));
rdsmitha6ce4442017-06-21 17:11:05339 return;
340 }
341
342 if (!callback.is_null())
rdsmith7ac81712017-06-22 17:09:54343 std::move(callback).Run(false);
rdsmitha6ce4442017-06-21 17:11:05344}
345
mkwstc611e6d2016-02-23 15:45:55346void CookieStoreIOS::GetCookieListWithOptionsAsync(
droger3e8d98b2015-03-18 15:29:53347 const GURL& url,
mkwstc611e6d2016-02-23 15:45:55348 const net::CookieOptions& options,
rdsmith7ac81712017-06-22 17:09:54349 GetCookieListCallback callback) {
Victor Costanb805fba2018-02-21 13:49:42350 DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
Victor Costanfb17d1a2018-02-23 03:42:26351
352 // If cookies are not allowed, a CookieStoreIOS subclass should be used
353 // instead.
354 DCHECK(SystemCookiesAllowed());
maksim.sisovfdd9f092017-01-30 10:41:42355
356 // TODO(mkwst): If/when iOS supports Same-Site cookies, we'll need to pass
357 // options in here as well. https://crbug.com/459154
Mohammad Refaat41032802017-10-06 04:15:57358 system_store_->GetCookiesForURLAsync(
359 url,
360 base::BindOnce(&CookieStoreIOS::RunGetCookieListCallbackOnSystemCookies,
361 weak_factory_.GetWeakPtr(), base::Passed(&callback)));
droger3e8d98b2015-03-18 15:29:53362}
363
rdsmith7ac81712017-06-22 17:09:54364void CookieStoreIOS::GetAllCookiesAsync(GetCookieListCallback callback) {
Victor Costanb805fba2018-02-21 13:49:42365 DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
mmenke9fa44f2d2016-01-22 23:36:39366
Victor Costanfb17d1a2018-02-23 03:42:26367 // If cookies are not allowed, a CookieStoreIOS subclass should be used
368 // instead.
369 DCHECK(SystemCookiesAllowed());
370
Mohammad Refaat41032802017-10-06 04:15:57371 // TODO(crbug.com/459154): If/when iOS supports Same-Site cookies, we'll need
372 // to pass options in here as well.
373 system_store_->GetAllCookiesAsync(
374 base::BindOnce(&CookieStoreIOS::RunGetCookieListCallbackOnSystemCookies,
375 weak_factory_.GetWeakPtr(), base::Passed(&callback)));
mmenke9fa44f2d2016-01-22 23:36:39376}
377
droger3e8d98b2015-03-18 15:29:53378void CookieStoreIOS::DeleteCookieAsync(const GURL& url,
379 const std::string& cookie_name,
rdsmith7ac81712017-06-22 17:09:54380 base::OnceClosure callback) {
Victor Costanb805fba2018-02-21 13:49:42381 DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
Victor Costanfb17d1a2018-02-23 03:42:26382
383 // If cookies are not allowed, a CookieStoreIOS subclass should be used
384 // instead.
385 DCHECK(SystemCookiesAllowed());
386
Mohammad Refaat41032802017-10-06 04:15:57387 __block base::OnceClosure shared_callback = std::move(callback);
388 base::WeakPtr<SystemCookieStore> weak_system_store =
389 system_store_->GetWeakPtr();
390 system_store_->GetCookiesForURLAsync(
Sylvain Defresnea44edf42018-06-05 15:08:34391 url, base::BindOnce(^(NSArray<NSHTTPCookie*>* cookies) {
Mohammad Refaat41032802017-10-06 04:15:57392 for (NSHTTPCookie* cookie in cookies) {
393 if ([cookie.name
394 isEqualToString:base::SysUTF8ToNSString(cookie_name)] &&
395 weak_system_store) {
396 weak_system_store->DeleteCookieAsync(
397 cookie, SystemCookieStore::SystemCookieCallback());
398 }
399 }
400 if (!shared_callback.is_null())
401 std::move(shared_callback).Run();
402 }));
droger3e8d98b2015-03-18 15:29:53403}
404
rdsmith7ac81712017-06-22 17:09:54405void CookieStoreIOS::DeleteCanonicalCookieAsync(const CanonicalCookie& cookie,
406 DeleteCallback callback) {
Victor Costanb805fba2018-02-21 13:49:42407 DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
mmenke24379d52016-02-05 23:50:17408
Victor Costanfb17d1a2018-02-23 03:42:26409 // If cookies are not allowed, a CookieStoreIOS subclass should be used
410 // instead.
411 DCHECK(SystemCookiesAllowed());
412
maksim.sisovfdd9f092017-01-30 10:41:42413 // This relies on the fact cookies are given unique creation dates.
Chris Mumford800caa62018-04-20 19:34:44414 CookieDeletionInfo delete_info(cookie.CreationDate(), cookie.CreationDate());
415 DeleteCookiesMatchingInfoAsync(std::move(delete_info), std::move(callback));
mmenke24379d52016-02-05 23:50:17416}
417
Chris Mumford800caa62018-04-20 19:34:44418void CookieStoreIOS::DeleteAllCreatedInTimeRangeAsync(
Chris Mumfordd8ed9f82018-05-01 15:43:13419 const CookieDeletionInfo::TimeRange& creation_range,
rdsmith7ac81712017-06-22 17:09:54420 DeleteCallback callback) {
Victor Costanb805fba2018-02-21 13:49:42421 DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
droger3e8d98b2015-03-18 15:29:53422
Victor Costanfb17d1a2018-02-23 03:42:26423 // If cookies are not allowed, a CookieStoreIOS subclass should be used
424 // instead.
425 DCHECK(SystemCookiesAllowed());
426
427 if (metrics_enabled())
droger3e8d98b2015-03-18 15:29:53428 ResetCookieCountMetrics();
429
Chris Mumford800caa62018-04-20 19:34:44430 CookieDeletionInfo delete_info(creation_range.start(), creation_range.end());
431 DeleteCookiesMatchingInfoAsync(std::move(delete_info), std::move(callback));
droger3e8d98b2015-03-18 15:29:53432}
433
Chris Mumfordd8ed9f82018-05-01 15:43:13434void CookieStoreIOS::DeleteAllMatchingInfoAsync(CookieDeletionInfo delete_info,
435 DeleteCallback callback) {
Victor Costanb805fba2018-02-21 13:49:42436 DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
droger3e8d98b2015-03-18 15:29:53437
Victor Costanfb17d1a2018-02-23 03:42:26438 // If cookies are not allowed, a CookieStoreIOS subclass should be used
439 // instead.
440 DCHECK(SystemCookiesAllowed());
441
442 if (metrics_enabled())
droger3e8d98b2015-03-18 15:29:53443 ResetCookieCountMetrics();
444
Chris Mumford800caa62018-04-20 19:34:44445 DeleteCookiesMatchingInfoAsync(std::move(delete_info), std::move(callback));
droger3e8d98b2015-03-18 15:29:53446}
447
rdsmith7ac81712017-06-22 17:09:54448void CookieStoreIOS::DeleteSessionCookiesAsync(DeleteCallback callback) {
Victor Costanb805fba2018-02-21 13:49:42449 DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
droger3e8d98b2015-03-18 15:29:53450
Victor Costanfb17d1a2018-02-23 03:42:26451 // If cookies are not allowed, a CookieStoreIOS subclass should be used
452 // instead.
453 DCHECK(SystemCookiesAllowed());
454
455 if (metrics_enabled())
droger3e8d98b2015-03-18 15:29:53456 ResetCookieCountMetrics();
457
Chris Mumford800caa62018-04-20 19:34:44458 CookieDeletionInfo delete_info;
459 delete_info.session_control =
460 CookieDeletionInfo::SessionControl::SESSION_COOKIES;
461 DeleteCookiesMatchingInfoAsync(std::move(delete_info), std::move(callback));
droger3e8d98b2015-03-18 15:29:53462}
463
rdsmith7ac81712017-06-22 17:09:54464void CookieStoreIOS::FlushStore(base::OnceClosure closure) {
Victor Costanb805fba2018-02-21 13:49:42465 DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
mmenke96f3bab2016-01-22 17:34:02466
467 if (SystemCookiesAllowed()) {
468 // If cookies are disabled, the system store is empty, and the cookies are
469 // stashed on disk. Do not delete the cookies on the disk in this case.
Mohammad Refaat41032802017-10-06 04:15:57470 system_store_->GetAllCookiesAsync(
471 base ::BindOnce(&CookieStoreIOS::FlushStoreFromCookies,
472 weak_factory_.GetWeakPtr(), std::move(closure)));
473 return;
mmenke96f3bab2016-01-22 17:34:02474 }
Mohammad Refaat41032802017-10-06 04:15:57475
Victor Costanfb17d1a2018-02-23 03:42:26476 // This code path is used by a CookieStoreIOS subclass, which shares this
477 // implementation.
rdsmith7ac81712017-06-22 17:09:54478 cookie_monster_->FlushStore(std::move(closure));
mmenke96f3bab2016-01-22 17:34:02479 flush_closure_.Cancel();
480}
481
droger3e8d98b2015-03-18 15:29:53482#pragma mark -
maksim.sisovfdd9f092017-01-30 10:41:42483#pragma mark Protected methods
droger3e8d98b2015-03-18 15:29:53484
eugenebut2cbccb72016-12-30 16:27:52485CookieStoreIOS::CookieStoreIOS(
486 net::CookieMonster::PersistentCookieStore* persistent_store,
Helen Li4e4cb0d2018-08-21 14:50:47487 std::unique_ptr<SystemCookieStore> system_store,
488 NetLog* net_log)
489 : cookie_monster_(new net::CookieMonster(persistent_store,
Helen Lifb313a92018-08-14 15:46:44490 nullptr /* channel_id_service */,
Helen Li4e4cb0d2018-08-21 14:50:47491 net_log)),
Mohammad Refaat6a4276492017-09-08 00:18:52492 system_store_(std::move(system_store)),
eugenebut2cbccb72016-12-30 16:27:52493 metrics_enabled_(false),
eugenebut2cbccb72016-12-30 16:27:52494 cookie_cache_(new CookieCache()),
Victor Costan14f47c12018-03-01 08:02:24495 change_dispatcher_(this),
eugenebut2cbccb72016-12-30 16:27:52496 weak_factory_(this) {
Mohammad Refaat6a4276492017-09-08 00:18:52497 DCHECK(system_store_);
eugenebut2cbccb72016-12-30 16:27:52498
499 NotificationTrampoline::GetInstance()->AddObserver(this);
500
501 cookie_monster_->SetPersistSessionCookies(true);
502 cookie_monster_->SetForceKeepSessionState();
503}
504
Victor Costanfb17d1a2018-02-23 03:42:26505void CookieStoreIOS::FlushStoreFromCookies(base::OnceClosure closure,
506 NSArray<NSHTTPCookie*>* cookies) {
507 WriteToCookieMonster(cookies);
508 cookie_monster_->FlushStore(std::move(closure));
509 flush_closure_.Cancel();
510}
511
maksim.sisovfdd9f092017-01-30 10:41:42512CookieStoreIOS::SetCookiesCallback CookieStoreIOS::WrapSetCallback(
rdsmith7ac81712017-06-22 17:09:54513 SetCookiesCallback callback) {
Victor Costanb805fba2018-02-21 13:49:42514 DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
rdsmith7ac81712017-06-22 17:09:54515 return base::BindOnce(&CookieStoreIOS::UpdateCachesAfterSet,
516 weak_factory_.GetWeakPtr(), std::move(callback));
maksim.sisovfdd9f092017-01-30 10:41:42517}
518
519CookieStoreIOS::DeleteCallback CookieStoreIOS::WrapDeleteCallback(
rdsmith7ac81712017-06-22 17:09:54520 DeleteCallback callback) {
Victor Costanb805fba2018-02-21 13:49:42521 DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
rdsmith7ac81712017-06-22 17:09:54522 return base::BindOnce(&CookieStoreIOS::UpdateCachesAfterDelete,
523 weak_factory_.GetWeakPtr(), std::move(callback));
maksim.sisovfdd9f092017-01-30 10:41:42524}
525
rdsmith7ac81712017-06-22 17:09:54526base::OnceClosure CookieStoreIOS::WrapClosure(base::OnceClosure callback) {
Victor Costanb805fba2018-02-21 13:49:42527 DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
rdsmith7ac81712017-06-22 17:09:54528 return base::BindOnce(&CookieStoreIOS::UpdateCachesAfterClosure,
529 weak_factory_.GetWeakPtr(), std::move(callback));
maksim.sisovfdd9f092017-01-30 10:41:42530}
531
532#pragma mark -
533#pragma mark Private methods
534
droger3e8d98b2015-03-18 15:29:53535bool CookieStoreIOS::SystemCookiesAllowed() {
Victor Costanb805fba2018-02-21 13:49:42536 DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
Mohammad Refaat6a4276492017-09-08 00:18:52537 return system_store_->GetCookieAcceptPolicy() !=
538 NSHTTPCookieAcceptPolicyNever;
droger3e8d98b2015-03-18 15:29:53539}
540
droger3e8d98b2015-03-18 15:29:53541void CookieStoreIOS::WriteToCookieMonster(NSArray* system_cookies) {
Victor Costanb805fba2018-02-21 13:49:42542 DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
droger3e8d98b2015-03-18 15:29:53543 // Copy the cookies from the global cookie store to |cookie_monster_|.
544 // Unlike the system store, CookieMonster requires unique creation times.
545 net::CookieList cookie_list;
546 NSUInteger cookie_count = [system_cookies count];
547 cookie_list.reserve(cookie_count);
548 for (NSHTTPCookie* cookie in system_cookies) {
549 cookie_list.push_back(CanonicalCookieFromSystemCookie(
Mohammad Refaat41032802017-10-06 04:15:57550 cookie, system_store_->GetCookieCreationTime(cookie)));
droger3e8d98b2015-03-18 15:29:53551 }
552 cookie_monster_->SetAllCookiesAsync(cookie_list, SetCookiesCallback());
553
554 // Update metrics.
555 if (metrics_enabled_)
556 UMA_HISTOGRAM_COUNTS_10000("CookieIOS.CookieWrittenCount", cookie_count);
557}
558
Chris Mumford800caa62018-04-20 19:34:44559void CookieStoreIOS::DeleteCookiesMatchingInfoAsync(
560 CookieDeletionInfo delete_info,
561 DeleteCallback callback) {
Victor Costanb805fba2018-02-21 13:49:42562 DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
Mohammad Refaat41032802017-10-06 04:15:57563 __block DeleteCallback shared_callback = std::move(callback);
Chris Mumford800caa62018-04-20 19:34:44564 __block CookieDeletionInfo shared_delete_info = std::move(delete_info);
Mohammad Refaat41032802017-10-06 04:15:57565 base::WeakPtr<SystemCookieStore> weak_system_store =
566 system_store_->GetWeakPtr();
567 system_store_->GetAllCookiesAsync(
Sylvain Defresnea44edf42018-06-05 15:08:34568 base::BindOnce(^(NSArray<NSHTTPCookie*>* cookies) {
Mohammad Refaat2e6de022018-05-29 20:36:44569 if (!weak_system_store) {
570 if (!shared_callback.is_null())
571 std::move(shared_callback).Run(0);
572 return;
573 }
Mohammad Refaat41032802017-10-06 04:15:57574 int to_delete_count = 0;
575 for (NSHTTPCookie* cookie in cookies) {
Chris Mumford800caa62018-04-20 19:34:44576 base::Time creation_time =
577 weak_system_store->GetCookieCreationTime(cookie);
578 CanonicalCookie cc =
579 CanonicalCookieFromSystemCookie(cookie, creation_time);
Mohammad Refaat2e6de022018-05-29 20:36:44580 if (shared_delete_info.Matches(cc)) {
Mohammad Refaat41032802017-10-06 04:15:57581 weak_system_store->DeleteCookieAsync(
582 cookie, SystemCookieStore::SystemCookieCallback());
583 to_delete_count++;
584 }
585 }
droger3e8d98b2015-03-18 15:29:53586
Mohammad Refaat41032802017-10-06 04:15:57587 if (!shared_callback.is_null())
588 std::move(shared_callback).Run(to_delete_count);
589 }));
droger3e8d98b2015-03-18 15:29:53590}
591
592void CookieStoreIOS::OnSystemCookiesChanged() {
Victor Costanb805fba2018-02-21 13:49:42593 DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
droger3e8d98b2015-03-18 15:29:53594
droger3e8d98b2015-03-18 15:29:53595 for (const auto& hook_map_entry : hook_map_) {
596 std::pair<GURL, std::string> key = hook_map_entry.first;
Mohammad Refaat41032802017-10-06 04:15:57597 UpdateCacheForCookieFromSystem(key.first, key.second,
598 /*run_callbacks=*/true);
droger3e8d98b2015-03-18 15:29:53599 }
600
601 // Do not schedule a flush if one is already scheduled.
602 if (!flush_closure_.IsCancelled())
603 return;
604
mmenke96f3bab2016-01-22 17:34:02605 flush_closure_.Reset(base::Bind(&CookieStoreIOS::FlushStore,
mmenke606c59c2016-03-07 18:20:55606 weak_factory_.GetWeakPtr(), base::Closure()));
skyostilc2740982015-06-05 19:15:31607 base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
eugenebut8a37ce252017-01-10 14:13:13608 FROM_HERE, flush_closure_.callback(), base::TimeDelta::FromSeconds(10));
droger3e8d98b2015-03-18 15:29:53609}
610
Victor Costan14f47c12018-03-01 08:02:24611CookieChangeDispatcher& CookieStoreIOS::GetChangeDispatcher() {
612 return change_dispatcher_;
613}
614
615bool CookieStoreIOS::IsEphemeral() {
616 return cookie_monster_->IsEphemeral();
617}
618
619std::unique_ptr<CookieChangeSubscription> CookieStoreIOS::AddCallbackForCookie(
620 const GURL& gurl,
621 const std::string& name,
622 CookieChangeCallback callback) {
Victor Costanb805fba2018-02-21 13:49:42623 DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
droger3e8d98b2015-03-18 15:29:53624
625 // Prefill cookie cache with all pertinent cookies for |url| if needed.
626 std::pair<GURL, std::string> key(gurl, name);
627 if (hook_map_.count(key) == 0) {
Mohammad Refaat41032802017-10-06 04:15:57628 UpdateCacheForCookieFromSystem(gurl, name, /*run_callbacks=*/false);
Victor Costan14f47c12018-03-01 08:02:24629 hook_map_[key] = std::make_unique<CookieChangeCallbackList>();
droger3e8d98b2015-03-18 15:29:53630 }
631
632 DCHECK(hook_map_.find(key) != hook_map_.end());
Victor Costan14f47c12018-03-01 08:02:24633 return std::make_unique<CookieStoreIOSCookieChangeSubscription>(
634 hook_map_[key]->Add(std::move(callback)));
nharper5babb5e62016-03-09 18:58:07635}
636
Mohammad Refaat41032802017-10-06 04:15:57637void CookieStoreIOS::UpdateCacheForCookieFromSystem(
droger3e8d98b2015-03-18 15:29:53638 const GURL& gurl,
Mohammad Refaat41032802017-10-06 04:15:57639 const std::string& cookie_name,
640 bool run_callbacks) {
Victor Costanb805fba2018-02-21 13:49:42641 DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
Mohammad Refaat41032802017-10-06 04:15:57642 system_store_->GetCookiesForURLAsync(
643 gurl, base::BindOnce(&CookieStoreIOS::UpdateCacheForCookies,
644 weak_factory_.GetWeakPtr(), gurl, cookie_name,
645 run_callbacks));
646}
647
648void CookieStoreIOS::UpdateCacheForCookies(const GURL& gurl,
649 const std::string& cookie_name,
650 bool run_callbacks,
651 NSArray<NSHTTPCookie*>* nscookies) {
652 std::vector<net::CanonicalCookie> cookies;
653 std::vector<net::CanonicalCookie> out_removed_cookies;
654 std::vector<net::CanonicalCookie> out_added_cookies;
655 for (NSHTTPCookie* nscookie in nscookies) {
656 if (base::SysNSStringToUTF8(nscookie.name) == cookie_name) {
657 net::CanonicalCookie canonical_cookie = CanonicalCookieFromSystemCookie(
658 nscookie, system_store_->GetCookieCreationTime(nscookie));
659 cookies.push_back(canonical_cookie);
660 }
661 }
662
663 bool changes = cookie_cache_->Update(
664 gurl, cookie_name, cookies, &out_removed_cookies, &out_added_cookies);
665 if (run_callbacks && changes) {
666 RunCallbacksForCookies(gurl, cookie_name, out_removed_cookies,
Victor Costan14f47c12018-03-01 08:02:24667 net::CookieChangeCause::UNKNOWN_DELETION);
Mohammad Refaat41032802017-10-06 04:15:57668 RunCallbacksForCookies(gurl, cookie_name, out_added_cookies,
Victor Costan14f47c12018-03-01 08:02:24669 net::CookieChangeCause::INSERTED);
Mohammad Refaat41032802017-10-06 04:15:57670 }
droger3e8d98b2015-03-18 15:29:53671}
672
673void CookieStoreIOS::RunCallbacksForCookies(
674 const GURL& url,
675 const std::string& name,
676 const std::vector<net::CanonicalCookie>& cookies,
Victor Costan14f47c12018-03-01 08:02:24677 net::CookieChangeCause cause) {
Victor Costanb805fba2018-02-21 13:49:42678 DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
droger3e8d98b2015-03-18 15:29:53679 if (cookies.empty())
680 return;
681
682 std::pair<GURL, std::string> key(url, name);
Victor Costan14f47c12018-03-01 08:02:24683 CookieChangeCallbackList* callbacks = hook_map_[key].get();
droger3e8d98b2015-03-18 15:29:53684 for (const auto& cookie : cookies) {
685 DCHECK_EQ(name, cookie.Name());
nharper352933e2016-09-30 18:24:57686 callbacks->Notify(cookie, cause);
droger3e8d98b2015-03-18 15:29:53687 }
688}
689
droger3e8d98b2015-03-18 15:29:53690void CookieStoreIOS::GotCookieListFor(const std::pair<GURL, std::string> key,
691 const net::CookieList& cookies) {
Victor Costanb805fba2018-02-21 13:49:42692 DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
droger3e8d98b2015-03-18 15:29:53693
694 net::CookieList filtered;
695 OnlyCookiesWithName(cookies, key.second, &filtered);
696 std::vector<net::CanonicalCookie> removed_cookies;
697 std::vector<net::CanonicalCookie> added_cookies;
698 if (cookie_cache_->Update(key.first, key.second, filtered, &removed_cookies,
699 &added_cookies)) {
nharper352933e2016-09-30 18:24:57700 RunCallbacksForCookies(key.first, key.second, removed_cookies,
Victor Costan14f47c12018-03-01 08:02:24701 net::CookieChangeCause::UNKNOWN_DELETION);
nharper352933e2016-09-30 18:24:57702 RunCallbacksForCookies(key.first, key.second, added_cookies,
Victor Costan14f47c12018-03-01 08:02:24703 net::CookieChangeCause::INSERTED);
droger3e8d98b2015-03-18 15:29:53704 }
705}
706
droger3e8d98b2015-03-18 15:29:53707void CookieStoreIOS::UpdateCachesFromCookieMonster() {
Victor Costanb805fba2018-02-21 13:49:42708 DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
droger3e8d98b2015-03-18 15:29:53709 for (const auto& hook_map_entry : hook_map_) {
710 std::pair<GURL, std::string> key = hook_map_entry.first;
rdsmith7ac81712017-06-22 17:09:54711 GetCookieListCallback callback = base::BindOnce(
mmenke606c59c2016-03-07 18:20:55712 &CookieStoreIOS::GotCookieListFor, weak_factory_.GetWeakPtr(), key);
rdsmith7ac81712017-06-22 17:09:54713 cookie_monster_->GetAllCookiesForURLAsync(key.first, std::move(callback));
droger3e8d98b2015-03-18 15:29:53714 }
715}
716
rdsmith7ac81712017-06-22 17:09:54717void CookieStoreIOS::UpdateCachesAfterSet(SetCookiesCallback callback,
droger3e8d98b2015-03-18 15:29:53718 bool success) {
Victor Costanb805fba2018-02-21 13:49:42719 DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
droger3e8d98b2015-03-18 15:29:53720 if (success)
721 UpdateCachesFromCookieMonster();
droger4c3ea112016-02-22 19:19:33722 if (!callback.is_null())
rdsmith7ac81712017-06-22 17:09:54723 std::move(callback).Run(success);
droger3e8d98b2015-03-18 15:29:53724}
725
rdsmith7ac81712017-06-22 17:09:54726void CookieStoreIOS::UpdateCachesAfterDelete(DeleteCallback callback,
rdsmitha5beda162017-07-08 13:55:42727 uint32_t num_deleted) {
Victor Costanb805fba2018-02-21 13:49:42728 DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
droger3e8d98b2015-03-18 15:29:53729 UpdateCachesFromCookieMonster();
droger4c3ea112016-02-22 19:19:33730 if (!callback.is_null())
rdsmith7ac81712017-06-22 17:09:54731 std::move(callback).Run(num_deleted);
droger3e8d98b2015-03-18 15:29:53732}
733
rdsmith7ac81712017-06-22 17:09:54734void CookieStoreIOS::UpdateCachesAfterClosure(base::OnceClosure callback) {
Victor Costanb805fba2018-02-21 13:49:42735 DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
droger3e8d98b2015-03-18 15:29:53736 UpdateCachesFromCookieMonster();
droger4c3ea112016-02-22 19:19:33737 if (!callback.is_null())
rdsmith7ac81712017-06-22 17:09:54738 std::move(callback).Run();
droger3e8d98b2015-03-18 15:29:53739}
740
mmenke9fa44f2d2016-01-22 23:36:39741net::CookieList
742CookieStoreIOS::CanonicalCookieListFromSystemCookies(NSArray* cookies) {
743 net::CookieList cookie_list;
744 cookie_list.reserve([cookies count]);
745 for (NSHTTPCookie* cookie in cookies) {
Mohammad Refaat41032802017-10-06 04:15:57746 base::Time created = system_store_->GetCookieCreationTime(cookie);
mmenke9fa44f2d2016-01-22 23:36:39747 cookie_list.push_back(CanonicalCookieFromSystemCookie(cookie, created));
748 }
749 return cookie_list;
750}
751
Mohammad Refaat41032802017-10-06 04:15:57752void CookieStoreIOS::RunGetCookieListCallbackOnSystemCookies(
753 CookieStoreIOS::GetCookieListCallback callback,
754 NSArray<NSHTTPCookie*>* cookies) {
755 if (!callback.is_null()) {
756 std::move(callback).Run(CanonicalCookieListFromSystemCookies(cookies));
757 }
758}
759
droger3e8d98b2015-03-18 15:29:53760} // namespace net