Speech refactoring: Reimplemented SpeechRecognitionManagerImpl as a FSM. (CL1.7)
[chromium-blink-merge.git] / net / cookies / cookie_monster.cc
blob84fc30dd67e4c8ac0df8d4e76930ce4ea39c3318
1 // Copyright (c) 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 // Portions of this code based on Mozilla:
6 // (netwerk/cookie/src/nsCookieService.cpp)
7 /* ***** BEGIN LICENSE BLOCK *****
8 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
10 * The contents of this file are subject to the Mozilla Public License Version
11 * 1.1 (the "License"); you may not use this file except in compliance with
12 * the License. You may obtain a copy of the License at
13 * http://www.mozilla.org/MPL/
15 * Software distributed under the License is distributed on an "AS IS" basis,
16 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
17 * for the specific language governing rights and limitations under the
18 * License.
20 * The Original Code is mozilla.org code.
22 * The Initial Developer of the Original Code is
23 * Netscape Communications Corporation.
24 * Portions created by the Initial Developer are Copyright (C) 2003
25 * the Initial Developer. All Rights Reserved.
27 * Contributor(s):
28 * Daniel Witte (dwitte@stanford.edu)
29 * Michiel van Leeuwen (mvl@exedo.nl)
31 * Alternatively, the contents of this file may be used under the terms of
32 * either the GNU General Public License Version 2 or later (the "GPL"), or
33 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
34 * in which case the provisions of the GPL or the LGPL are applicable instead
35 * of those above. If you wish to allow use of your version of this file only
36 * under the terms of either the GPL or the LGPL, and not to allow others to
37 * use your version of this file under the terms of the MPL, indicate your
38 * decision by deleting the provisions above and replace them with the notice
39 * and other provisions required by the GPL or the LGPL. If you do not delete
40 * the provisions above, a recipient may use your version of this file under
41 * the terms of any one of the MPL, the GPL or the LGPL.
43 * ***** END LICENSE BLOCK ***** */
45 #include "net/cookies/cookie_monster.h"
47 #include <algorithm>
48 #include <set>
50 #include "base/basictypes.h"
51 #include "base/bind.h"
52 #include "base/callback.h"
53 #include "base/format_macros.h"
54 #include "base/logging.h"
55 #include "base/memory/scoped_ptr.h"
56 #include "base/message_loop.h"
57 #include "base/message_loop_proxy.h"
58 #include "base/metrics/histogram.h"
59 #include "base/string_tokenizer.h"
60 #include "base/string_util.h"
61 #include "base/stringprintf.h"
62 #include "googleurl/src/gurl.h"
63 #include "googleurl/src/url_canon.h"
64 #include "net/cookies/cookie_util.h"
65 #include "net/base/registry_controlled_domain.h"
67 using base::Time;
68 using base::TimeDelta;
69 using base::TimeTicks;
71 // In steady state, most cookie requests can be satisfied by the in memory
72 // cookie monster store. However, if a request comes in during the initial
73 // cookie load, it must be delayed until that load completes. That is done by
74 // queueing it on CookieMonster::queue_ and running it when notification of
75 // cookie load completion is received via CookieMonster::OnLoaded. This callback
76 // is passed to the persistent store from CookieMonster::InitStore(), which is
77 // called on the first operation invoked on the CookieMonster.
79 // On the browser critical paths (e.g. for loading initial web pages in a
80 // session restore) it may take too long to wait for the full load. If a cookie
81 // request is for a specific URL, DoCookieTaskForURL is called, which triggers a
82 // priority load if the key is not loaded yet by calling PersistentCookieStore
83 // :: LoadCookiesForKey. The request is queued in CookieMonster::tasks_queued
84 // and executed upon receiving notification of key load completion via
85 // CookieMonster::OnKeyLoaded(). If multiple requests for the same eTLD+1 are
86 // received before key load completion, only the first request calls
87 // PersistentCookieStore::LoadCookiesForKey, all subsequent requests are queued
88 // in CookieMonster::tasks_queued and executed upon receiving notification of
89 // key load completion triggered by the first request for the same eTLD+1.
91 static const int kMinutesInTenYears = 10 * 365 * 24 * 60;
93 namespace net {
95 // See comments at declaration of these variables in cookie_monster.h
96 // for details.
97 const size_t CookieMonster::kDomainMaxCookies = 180;
98 const size_t CookieMonster::kDomainPurgeCookies = 30;
99 const size_t CookieMonster::kMaxCookies = 3300;
100 const size_t CookieMonster::kPurgeCookies = 300;
101 const int CookieMonster::kSafeFromGlobalPurgeDays = 30;
103 namespace {
105 typedef std::vector<CookieMonster::CanonicalCookie*> CanonicalCookieVector;
107 // Default minimum delay after updating a cookie's LastAccessDate before we
108 // will update it again.
109 const int kDefaultAccessUpdateThresholdSeconds = 60;
111 // Comparator to sort cookies from highest creation date to lowest
112 // creation date.
113 struct OrderByCreationTimeDesc {
114 bool operator()(const CookieMonster::CookieMap::iterator& a,
115 const CookieMonster::CookieMap::iterator& b) const {
116 return a->second->CreationDate() > b->second->CreationDate();
120 // Constants for use in VLOG
121 const int kVlogPerCookieMonster = 1;
122 const int kVlogPeriodic = 3;
123 const int kVlogGarbageCollection = 5;
124 const int kVlogSetCookies = 7;
125 const int kVlogGetCookies = 9;
127 #if defined(ENABLE_PERSISTENT_SESSION_COOKIES)
128 const int kPersistentSessionCookieExpiryInDays = 14;
129 #endif
131 // Mozilla sorts on the path length (longest first), and then it
132 // sorts by creation time (oldest first).
133 // The RFC says the sort order for the domain attribute is undefined.
134 bool CookieSorter(CookieMonster::CanonicalCookie* cc1,
135 CookieMonster::CanonicalCookie* cc2) {
136 if (cc1->Path().length() == cc2->Path().length())
137 return cc1->CreationDate() < cc2->CreationDate();
138 return cc1->Path().length() > cc2->Path().length();
141 bool LRUCookieSorter(const CookieMonster::CookieMap::iterator& it1,
142 const CookieMonster::CookieMap::iterator& it2) {
143 // Cookies accessed less recently should be deleted first.
144 if (it1->second->LastAccessDate() != it2->second->LastAccessDate())
145 return it1->second->LastAccessDate() < it2->second->LastAccessDate();
147 // In rare cases we might have two cookies with identical last access times.
148 // To preserve the stability of the sort, in these cases prefer to delete
149 // older cookies over newer ones. CreationDate() is guaranteed to be unique.
150 return it1->second->CreationDate() < it2->second->CreationDate();
153 // Our strategy to find duplicates is:
154 // (1) Build a map from (cookiename, cookiepath) to
155 // {list of cookies with this signature, sorted by creation time}.
156 // (2) For each list with more than 1 entry, keep the cookie having the
157 // most recent creation time, and delete the others.
159 // Two cookies are considered equivalent if they have the same domain,
160 // name, and path.
161 struct CookieSignature {
162 public:
163 CookieSignature(const std::string& name, const std::string& domain,
164 const std::string& path)
165 : name(name),
166 domain(domain),
167 path(path) {}
169 // To be a key for a map this class needs to be assignable, copyable,
170 // and have an operator<. The default assignment operator
171 // and copy constructor are exactly what we want.
173 bool operator<(const CookieSignature& cs) const {
174 // Name compare dominates, then domain, then path.
175 int diff = name.compare(cs.name);
176 if (diff != 0)
177 return diff < 0;
179 diff = domain.compare(cs.domain);
180 if (diff != 0)
181 return diff < 0;
183 return path.compare(cs.path) < 0;
186 std::string name;
187 std::string domain;
188 std::string path;
191 // Determine the cookie domain to use for setting the specified cookie.
192 bool GetCookieDomain(const GURL& url,
193 const CookieMonster::ParsedCookie& pc,
194 std::string* result) {
195 std::string domain_string;
196 if (pc.HasDomain())
197 domain_string = pc.Domain();
198 return cookie_util::GetCookieDomainWithString(url, domain_string, result);
201 std::string CanonPathWithString(const GURL& url,
202 const std::string& path_string) {
203 // The RFC says the path should be a prefix of the current URL path.
204 // However, Mozilla allows you to set any path for compatibility with
205 // broken websites. We unfortunately will mimic this behavior. We try
206 // to be generous and accept cookies with an invalid path attribute, and
207 // default the path to something reasonable.
209 // The path was supplied in the cookie, we'll take it.
210 if (!path_string.empty() && path_string[0] == '/')
211 return path_string;
213 // The path was not supplied in the cookie or invalid, we will default
214 // to the current URL path.
215 // """Defaults to the path of the request URL that generated the
216 // Set-Cookie response, up to, but not including, the
217 // right-most /."""
218 // How would this work for a cookie on /? We will include it then.
219 const std::string& url_path = url.path();
221 size_t idx = url_path.find_last_of('/');
223 // The cookie path was invalid or a single '/'.
224 if (idx == 0 || idx == std::string::npos)
225 return std::string("/");
227 // Return up to the rightmost '/'.
228 return url_path.substr(0, idx);
231 std::string CanonPath(const GURL& url,
232 const CookieMonster::ParsedCookie& pc) {
233 std::string path_string;
234 if (pc.HasPath())
235 path_string = pc.Path();
236 return CanonPathWithString(url, path_string);
239 Time CanonExpiration(const CookieMonster::ParsedCookie& pc,
240 const Time& current) {
241 // First, try the Max-Age attribute.
242 uint64 max_age = 0;
243 if (pc.HasMaxAge() &&
244 #ifdef COMPILER_MSVC
245 sscanf_s(
246 #else
247 sscanf(
248 #endif
249 pc.MaxAge().c_str(), " %" PRIu64, &max_age) == 1) {
250 return current + TimeDelta::FromSeconds(max_age);
253 // Try the Expires attribute.
254 if (pc.HasExpires())
255 return CookieMonster::ParseCookieTime(pc.Expires());
257 // Invalid or no expiration, persistent cookie.
258 return Time();
261 // Helper for GarbageCollection. If |cookie_its->size() > num_max|, remove the
262 // |num_max - num_purge| most recently accessed cookies from cookie_its.
263 // (In other words, leave the entries that are candidates for
264 // eviction in cookie_its.) The cookies returned will be in order sorted by
265 // access time, least recently accessed first. The access time of the least
266 // recently accessed entry not returned will be placed in
267 // |*lra_removed| if that pointer is set. FindLeastRecentlyAccessed
268 // returns false if no manipulation is done (because the list size is less
269 // than num_max), true otherwise.
270 bool FindLeastRecentlyAccessed(
271 size_t num_max,
272 size_t num_purge,
273 Time* lra_removed,
274 std::vector<CookieMonster::CookieMap::iterator>* cookie_its) {
275 DCHECK_LE(num_purge, num_max);
276 if (cookie_its->size() > num_max) {
277 VLOG(kVlogGarbageCollection)
278 << "FindLeastRecentlyAccessed() Deep Garbage Collect.";
279 num_purge += cookie_its->size() - num_max;
280 DCHECK_GT(cookie_its->size(), num_purge);
282 // Add 1 so that we can get the last time left in the store.
283 std::partial_sort(cookie_its->begin(), cookie_its->begin() + num_purge + 1,
284 cookie_its->end(), LRUCookieSorter);
285 *lra_removed =
286 (*(cookie_its->begin() + num_purge))->second->LastAccessDate();
287 cookie_its->erase(cookie_its->begin() + num_purge, cookie_its->end());
288 return true;
290 return false;
293 // Mapping between DeletionCause and Delegate::ChangeCause; the mapping also
294 // provides a boolean that specifies whether or not an OnCookieChanged
295 // notification ought to be generated.
296 typedef struct ChangeCausePair_struct {
297 CookieMonster::Delegate::ChangeCause cause;
298 bool notify;
299 } ChangeCausePair;
300 ChangeCausePair ChangeCauseMapping[] = {
301 // DELETE_COOKIE_EXPLICIT
302 { CookieMonster::Delegate::CHANGE_COOKIE_EXPLICIT, true },
303 // DELETE_COOKIE_OVERWRITE
304 { CookieMonster::Delegate::CHANGE_COOKIE_OVERWRITE, true },
305 // DELETE_COOKIE_EXPIRED
306 { CookieMonster::Delegate::CHANGE_COOKIE_EXPIRED, true },
307 // DELETE_COOKIE_EVICTED
308 { CookieMonster::Delegate::CHANGE_COOKIE_EVICTED, true },
309 // DELETE_COOKIE_DUPLICATE_IN_BACKING_STORE
310 { CookieMonster::Delegate::CHANGE_COOKIE_EXPLICIT, false },
311 // DELETE_COOKIE_DONT_RECORD
312 { CookieMonster::Delegate::CHANGE_COOKIE_EXPLICIT, false },
313 // DELETE_COOKIE_EVICTED_DOMAIN
314 { CookieMonster::Delegate::CHANGE_COOKIE_EVICTED, true },
315 // DELETE_COOKIE_EVICTED_GLOBAL
316 { CookieMonster::Delegate::CHANGE_COOKIE_EVICTED, true },
317 // DELETE_COOKIE_EVICTED_DOMAIN_PRE_SAFE
318 { CookieMonster::Delegate::CHANGE_COOKIE_EVICTED, true },
319 // DELETE_COOKIE_EVICTED_DOMAIN_POST_SAFE
320 { CookieMonster::Delegate::CHANGE_COOKIE_EVICTED, true },
321 // DELETE_COOKIE_EXPIRED_OVERWRITE
322 { CookieMonster::Delegate::CHANGE_COOKIE_EXPIRED_OVERWRITE, true },
323 // DELETE_COOKIE_LAST_ENTRY
324 { CookieMonster::Delegate::CHANGE_COOKIE_EXPLICIT, false }
327 std::string BuildCookieLine(const CanonicalCookieVector& cookies) {
328 std::string cookie_line;
329 for (CanonicalCookieVector::const_iterator it = cookies.begin();
330 it != cookies.end(); ++it) {
331 if (it != cookies.begin())
332 cookie_line += "; ";
333 // In Mozilla if you set a cookie like AAAA, it will have an empty token
334 // and a value of AAAA. When it sends the cookie back, it will send AAAA,
335 // so we need to avoid sending =AAAA for a blank token value.
336 if (!(*it)->Name().empty())
337 cookie_line += (*it)->Name() + "=";
338 cookie_line += (*it)->Value();
340 return cookie_line;
343 void BuildCookieInfoList(const CanonicalCookieVector& cookies,
344 std::vector<CookieStore::CookieInfo>* cookie_infos) {
345 for (CanonicalCookieVector::const_iterator it = cookies.begin();
346 it != cookies.end(); ++it) {
347 const CookieMonster::CanonicalCookie* cookie = *it;
348 CookieStore::CookieInfo cookie_info;
350 cookie_info.name = cookie->Name();
351 cookie_info.creation_date = cookie->CreationDate();
352 cookie_info.mac_key = cookie->MACKey();
353 cookie_info.mac_algorithm = cookie->MACAlgorithm();
355 cookie_infos->push_back(cookie_info);
359 } // namespace
361 // static
362 bool CookieMonster::enable_file_scheme_ = false;
364 CookieMonster::CookieMonster(PersistentCookieStore* store, Delegate* delegate)
365 : initialized_(false),
366 loaded_(false),
367 store_(store),
368 last_access_threshold_(
369 TimeDelta::FromSeconds(kDefaultAccessUpdateThresholdSeconds)),
370 delegate_(delegate),
371 last_statistic_record_time_(Time::Now()),
372 keep_expired_cookies_(false),
373 persist_session_cookies_(false) {
374 InitializeHistograms();
375 SetDefaultCookieableSchemes();
378 CookieMonster::CookieMonster(PersistentCookieStore* store,
379 Delegate* delegate,
380 int last_access_threshold_milliseconds)
381 : initialized_(false),
382 loaded_(false),
383 store_(store),
384 last_access_threshold_(base::TimeDelta::FromMilliseconds(
385 last_access_threshold_milliseconds)),
386 delegate_(delegate),
387 last_statistic_record_time_(base::Time::Now()),
388 keep_expired_cookies_(false),
389 persist_session_cookies_(false) {
390 InitializeHistograms();
391 SetDefaultCookieableSchemes();
394 // Parse a cookie expiration time. We try to be lenient, but we need to
395 // assume some order to distinguish the fields. The basic rules:
396 // - The month name must be present and prefix the first 3 letters of the
397 // full month name (jan for January, jun for June).
398 // - If the year is <= 2 digits, it must occur after the day of month.
399 // - The time must be of the format hh:mm:ss.
400 // An average cookie expiration will look something like this:
401 // Sat, 15-Apr-17 21:01:22 GMT
402 Time CookieMonster::ParseCookieTime(const std::string& time_string) {
403 static const char* kMonths[] = { "jan", "feb", "mar", "apr", "may", "jun",
404 "jul", "aug", "sep", "oct", "nov", "dec" };
405 static const int kMonthsLen = arraysize(kMonths);
406 // We want to be pretty liberal, and support most non-ascii and non-digit
407 // characters as a delimiter. We can't treat : as a delimiter, because it
408 // is the delimiter for hh:mm:ss, and we want to keep this field together.
409 // We make sure to include - and +, since they could prefix numbers.
410 // If the cookie attribute came in in quotes (ex expires="XXX"), the quotes
411 // will be preserved, and we will get them here. So we make sure to include
412 // quote characters, and also \ for anything that was internally escaped.
413 static const char* kDelimiters = "\t !\"#$%&'()*+,-./;<=>?@[\\]^_`{|}~";
415 Time::Exploded exploded = {0};
417 StringTokenizer tokenizer(time_string, kDelimiters);
419 bool found_day_of_month = false;
420 bool found_month = false;
421 bool found_time = false;
422 bool found_year = false;
424 while (tokenizer.GetNext()) {
425 const std::string token = tokenizer.token();
426 DCHECK(!token.empty());
427 bool numerical = IsAsciiDigit(token[0]);
429 // String field
430 if (!numerical) {
431 if (!found_month) {
432 for (int i = 0; i < kMonthsLen; ++i) {
433 // Match prefix, so we could match January, etc
434 if (base::strncasecmp(token.c_str(), kMonths[i], 3) == 0) {
435 exploded.month = i + 1;
436 found_month = true;
437 break;
440 } else {
441 // If we've gotten here, it means we've already found and parsed our
442 // month, and we have another string, which we would expect to be the
443 // the time zone name. According to the RFC and my experiments with
444 // how sites format their expirations, we don't have much of a reason
445 // to support timezones. We don't want to ever barf on user input,
446 // but this DCHECK should pass for well-formed data.
447 // DCHECK(token == "GMT");
449 // Numeric field w/ a colon
450 } else if (token.find(':') != std::string::npos) {
451 if (!found_time &&
452 #ifdef COMPILER_MSVC
453 sscanf_s(
454 #else
455 sscanf(
456 #endif
457 token.c_str(), "%2u:%2u:%2u", &exploded.hour,
458 &exploded.minute, &exploded.second) == 3) {
459 found_time = true;
460 } else {
461 // We should only ever encounter one time-like thing. If we're here,
462 // it means we've found a second, which shouldn't happen. We keep
463 // the first. This check should be ok for well-formed input:
464 // NOTREACHED();
466 // Numeric field
467 } else {
468 // Overflow with atoi() is unspecified, so we enforce a max length.
469 if (!found_day_of_month && token.length() <= 2) {
470 exploded.day_of_month = atoi(token.c_str());
471 found_day_of_month = true;
472 } else if (!found_year && token.length() <= 5) {
473 exploded.year = atoi(token.c_str());
474 found_year = true;
475 } else {
476 // If we're here, it means we've either found an extra numeric field,
477 // or a numeric field which was too long. For well-formed input, the
478 // following check would be reasonable:
479 // NOTREACHED();
484 if (!found_day_of_month || !found_month || !found_time || !found_year) {
485 // We didn't find all of the fields we need. For well-formed input, the
486 // following check would be reasonable:
487 // NOTREACHED() << "Cookie parse expiration failed: " << time_string;
488 return Time();
491 // Normalize the year to expand abbreviated years to the full year.
492 if (exploded.year >= 69 && exploded.year <= 99)
493 exploded.year += 1900;
494 if (exploded.year >= 0 && exploded.year <= 68)
495 exploded.year += 2000;
497 // If our values are within their correct ranges, we got our time.
498 if (exploded.day_of_month >= 1 && exploded.day_of_month <= 31 &&
499 exploded.month >= 1 && exploded.month <= 12 &&
500 exploded.year >= 1601 && exploded.year <= 30827 &&
501 exploded.hour <= 23 && exploded.minute <= 59 && exploded.second <= 59) {
502 return Time::FromUTCExploded(exploded);
505 // One of our values was out of expected range. For well-formed input,
506 // the following check would be reasonable:
507 // NOTREACHED() << "Cookie exploded expiration failed: " << time_string;
509 return Time();
512 // Task classes for queueing the coming request.
514 class CookieMonster::CookieMonsterTask
515 : public base::RefCountedThreadSafe<CookieMonsterTask> {
516 public:
517 // Runs the task and invokes the client callback on the thread that
518 // originally constructed the task.
519 virtual void Run() = 0;
521 protected:
522 explicit CookieMonsterTask(CookieMonster* cookie_monster);
523 virtual ~CookieMonsterTask();
525 // Invokes the callback immediately, if the current thread is the one
526 // that originated the task, or queues the callback for execution on the
527 // appropriate thread. Maintains a reference to this CookieMonsterTask
528 // instance until the callback completes.
529 void InvokeCallback(base::Closure callback);
531 CookieMonster* cookie_monster() {
532 return cookie_monster_;
535 friend class base::RefCountedThreadSafe<CookieMonsterTask>;
537 private:
538 CookieMonster* cookie_monster_;
539 scoped_refptr<base::MessageLoopProxy> thread_;
541 DISALLOW_COPY_AND_ASSIGN(CookieMonsterTask);
544 CookieMonster::CookieMonsterTask::CookieMonsterTask(
545 CookieMonster* cookie_monster)
546 : cookie_monster_(cookie_monster),
547 thread_(base::MessageLoopProxy::current()) { }
549 CookieMonster::CookieMonsterTask::~CookieMonsterTask() { }
551 // Unfortunately, one cannot re-bind a Callback with parameters into a closure.
552 // Therefore, the closure passed to InvokeCallback is a clumsy binding of
553 // Callback::Run on a wrapped Callback instance. Since Callback is not
554 // reference counted, we bind to an instance that is a member of the
555 // CookieMonsterTask subclass. Then, we cannot simply post the callback to a
556 // message loop because the underlying instance may be destroyed (along with the
557 // CookieMonsterTask instance) in the interim. Therefore, we post a callback
558 // bound to the CookieMonsterTask, which *is* reference counted (thus preventing
559 // destruction of the original callback), and which invokes the closure (which
560 // invokes the original callback with the returned data).
561 void CookieMonster::CookieMonsterTask::InvokeCallback(base::Closure callback) {
562 if (thread_->BelongsToCurrentThread()) {
563 callback.Run();
564 } else {
565 thread_->PostTask(FROM_HERE, base::Bind(
566 &CookieMonster::CookieMonsterTask::InvokeCallback, this, callback));
570 // Task class for SetCookieWithDetails call.
571 class CookieMonster::SetCookieWithDetailsTask
572 : public CookieMonster::CookieMonsterTask {
573 public:
574 SetCookieWithDetailsTask(
575 CookieMonster* cookie_monster,
576 const GURL& url, const std::string& name, const std::string& value,
577 const std::string& domain, const std::string& path,
578 const base::Time& expiration_time, bool secure, bool http_only,
579 const CookieMonster::SetCookiesCallback& callback)
580 : CookieMonsterTask(cookie_monster),
581 url_(url),
582 name_(name),
583 value_(value),
584 domain_(domain),
585 path_(path),
586 expiration_time_(expiration_time),
587 secure_(secure),
588 http_only_(http_only),
589 callback_(callback) { }
591 virtual void Run() OVERRIDE;
593 private:
594 GURL url_;
595 std::string name_;
596 std::string value_;
597 std::string domain_;
598 std::string path_;
599 base::Time expiration_time_;
600 bool secure_;
601 bool http_only_;
602 CookieMonster::SetCookiesCallback callback_;
604 DISALLOW_COPY_AND_ASSIGN(SetCookieWithDetailsTask);
607 void CookieMonster::SetCookieWithDetailsTask::Run() {
608 bool success = this->cookie_monster()->
609 SetCookieWithDetails(url_, name_, value_, domain_, path_,
610 expiration_time_, secure_, http_only_);
611 if (!callback_.is_null()) {
612 this->InvokeCallback(base::Bind(&CookieMonster::SetCookiesCallback::Run,
613 base::Unretained(&callback_), success));
617 // Task class for GetAllCookies call.
618 class CookieMonster::GetAllCookiesTask
619 : public CookieMonster::CookieMonsterTask {
620 public:
621 GetAllCookiesTask(CookieMonster* cookie_monster,
622 const CookieMonster::GetCookieListCallback& callback)
623 : CookieMonsterTask(cookie_monster),
624 callback_(callback) { }
626 virtual void Run() OVERRIDE;
628 private:
629 CookieMonster::GetCookieListCallback callback_;
631 DISALLOW_COPY_AND_ASSIGN(GetAllCookiesTask);
634 void CookieMonster::GetAllCookiesTask::Run() {
635 if (!callback_.is_null()) {
636 CookieList cookies = this->cookie_monster()->GetAllCookies();
637 this->InvokeCallback(base::Bind(&CookieMonster::GetCookieListCallback::Run,
638 base::Unretained(&callback_), cookies));
642 // Task class for GetAllCookiesForURLWithOptions call.
643 class CookieMonster::GetAllCookiesForURLWithOptionsTask
644 : public CookieMonster::CookieMonsterTask {
645 public:
646 GetAllCookiesForURLWithOptionsTask(
647 CookieMonster* cookie_monster,
648 const GURL& url,
649 const CookieOptions& options,
650 const CookieMonster::GetCookieListCallback& callback)
651 : CookieMonsterTask(cookie_monster),
652 url_(url),
653 options_(options),
654 callback_(callback) { }
656 virtual void Run() OVERRIDE;
658 private:
659 GURL url_;
660 CookieOptions options_;
661 CookieMonster::GetCookieListCallback callback_;
663 DISALLOW_COPY_AND_ASSIGN(GetAllCookiesForURLWithOptionsTask);
666 void CookieMonster::GetAllCookiesForURLWithOptionsTask::Run() {
667 if (!callback_.is_null()) {
668 CookieList cookies = this->cookie_monster()->
669 GetAllCookiesForURLWithOptions(url_, options_);
670 this->InvokeCallback(base::Bind(&CookieMonster::GetCookieListCallback::Run,
671 base::Unretained(&callback_), cookies));
675 // Task class for DeleteAll call.
676 class CookieMonster::DeleteAllTask : public CookieMonster::CookieMonsterTask {
677 public:
678 DeleteAllTask(CookieMonster* cookie_monster,
679 const CookieMonster::DeleteCallback& callback)
680 : CookieMonsterTask(cookie_monster),
681 callback_(callback) { }
683 virtual void Run() OVERRIDE;
685 private:
686 CookieMonster::DeleteCallback callback_;
688 DISALLOW_COPY_AND_ASSIGN(DeleteAllTask);
691 void CookieMonster::DeleteAllTask::Run() {
692 int num_deleted = this->cookie_monster()->DeleteAll(true);
693 if (!callback_.is_null()) {
694 this->InvokeCallback(base::Bind(&CookieMonster::DeleteCallback::Run,
695 base::Unretained(&callback_), num_deleted));
699 // Task class for DeleteAllCreatedBetween call.
700 class CookieMonster::DeleteAllCreatedBetweenTask
701 : public CookieMonster::CookieMonsterTask {
702 public:
703 DeleteAllCreatedBetweenTask(
704 CookieMonster* cookie_monster,
705 const Time& delete_begin,
706 const Time& delete_end,
707 const CookieMonster::DeleteCallback& callback)
708 : CookieMonsterTask(cookie_monster),
709 delete_begin_(delete_begin),
710 delete_end_(delete_end),
711 callback_(callback) { }
713 virtual void Run() OVERRIDE;
715 private:
716 Time delete_begin_;
717 Time delete_end_;
718 CookieMonster::DeleteCallback callback_;
720 DISALLOW_COPY_AND_ASSIGN(DeleteAllCreatedBetweenTask);
723 void CookieMonster::DeleteAllCreatedBetweenTask::Run() {
724 int num_deleted = this->cookie_monster()->
725 DeleteAllCreatedBetween(delete_begin_, delete_end_);
726 if (!callback_.is_null()) {
727 this->InvokeCallback(base::Bind(&CookieMonster::DeleteCallback::Run,
728 base::Unretained(&callback_), num_deleted));
732 // Task class for DeleteAllForHost call.
733 class CookieMonster::DeleteAllForHostTask
734 : public CookieMonster::CookieMonsterTask {
735 public:
736 DeleteAllForHostTask(CookieMonster* cookie_monster,
737 const GURL& url,
738 const CookieMonster::DeleteCallback& callback)
739 : CookieMonsterTask(cookie_monster),
740 url_(url),
741 callback_(callback) { }
743 virtual void Run() OVERRIDE;
745 private:
746 GURL url_;
747 CookieMonster::DeleteCallback callback_;
749 DISALLOW_COPY_AND_ASSIGN(DeleteAllForHostTask);
752 void CookieMonster::DeleteAllForHostTask::Run() {
753 int num_deleted = this->cookie_monster()->DeleteAllForHost(url_);
754 if (!callback_.is_null()) {
755 this->InvokeCallback(base::Bind(&CookieMonster::DeleteCallback::Run,
756 base::Unretained(&callback_), num_deleted));
760 // Task class for DeleteCanonicalCookie call.
761 class CookieMonster::DeleteCanonicalCookieTask
762 : public CookieMonster::CookieMonsterTask {
763 public:
764 DeleteCanonicalCookieTask(
765 CookieMonster* cookie_monster,
766 const CookieMonster::CanonicalCookie& cookie,
767 const CookieMonster::DeleteCookieCallback& callback)
768 : CookieMonsterTask(cookie_monster),
769 cookie_(cookie),
770 callback_(callback) { }
772 virtual void Run() OVERRIDE;
774 private:
775 CookieMonster::CanonicalCookie cookie_;
776 CookieMonster::DeleteCookieCallback callback_;
778 DISALLOW_COPY_AND_ASSIGN(DeleteCanonicalCookieTask);
781 void CookieMonster::DeleteCanonicalCookieTask::Run() {
782 bool result = this->cookie_monster()->DeleteCanonicalCookie(cookie_);
783 if (!callback_.is_null()) {
784 this->InvokeCallback(base::Bind(&CookieMonster::DeleteCookieCallback::Run,
785 base::Unretained(&callback_), result));
789 // Task class for SetCookieWithOptions call.
790 class CookieMonster::SetCookieWithOptionsTask
791 : public CookieMonster::CookieMonsterTask {
792 public:
793 SetCookieWithOptionsTask(CookieMonster* cookie_monster,
794 const GURL& url,
795 const std::string& cookie_line,
796 const CookieOptions& options,
797 const CookieMonster::SetCookiesCallback& callback)
798 : CookieMonsterTask(cookie_monster),
799 url_(url),
800 cookie_line_(cookie_line),
801 options_(options),
802 callback_(callback) { }
804 virtual void Run() OVERRIDE;
806 private:
807 GURL url_;
808 std::string cookie_line_;
809 CookieOptions options_;
810 CookieMonster::SetCookiesCallback callback_;
812 DISALLOW_COPY_AND_ASSIGN(SetCookieWithOptionsTask);
815 void CookieMonster::SetCookieWithOptionsTask::Run() {
816 bool result = this->cookie_monster()->
817 SetCookieWithOptions(url_, cookie_line_, options_);
818 if (!callback_.is_null()) {
819 this->InvokeCallback(base::Bind(&CookieMonster::SetCookiesCallback::Run,
820 base::Unretained(&callback_), result));
824 // Task class for GetCookiesWithOptions call.
825 class CookieMonster::GetCookiesWithOptionsTask
826 : public CookieMonster::CookieMonsterTask {
827 public:
828 GetCookiesWithOptionsTask(CookieMonster* cookie_monster,
829 const GURL& url,
830 const CookieOptions& options,
831 const CookieMonster::GetCookiesCallback& callback)
832 : CookieMonsterTask(cookie_monster),
833 url_(url),
834 options_(options),
835 callback_(callback) { }
837 virtual void Run() OVERRIDE;
839 private:
840 GURL url_;
841 CookieOptions options_;
842 CookieMonster::GetCookiesCallback callback_;
844 DISALLOW_COPY_AND_ASSIGN(GetCookiesWithOptionsTask);
847 void CookieMonster::GetCookiesWithOptionsTask::Run() {
848 std::string cookie = this->cookie_monster()->
849 GetCookiesWithOptions(url_, options_);
850 if (!callback_.is_null()) {
851 this->InvokeCallback(base::Bind(&CookieMonster::GetCookiesCallback::Run,
852 base::Unretained(&callback_), cookie));
856 // Task class for GetCookiesWithInfo call.
857 class CookieMonster::GetCookiesWithInfoTask
858 : public CookieMonster::CookieMonsterTask {
859 public:
860 GetCookiesWithInfoTask(CookieMonster* cookie_monster,
861 const GURL& url,
862 const CookieOptions& options,
863 const CookieMonster::GetCookieInfoCallback& callback)
864 : CookieMonsterTask(cookie_monster),
865 url_(url),
866 options_(options),
867 callback_(callback) { }
869 virtual void Run() OVERRIDE;
871 private:
872 GURL url_;
873 CookieOptions options_;
874 CookieMonster::GetCookieInfoCallback callback_;
876 DISALLOW_COPY_AND_ASSIGN(GetCookiesWithInfoTask);
879 void CookieMonster::GetCookiesWithInfoTask::Run() {
880 if (!callback_.is_null()) {
881 std::string cookie_line;
882 std::vector<CookieMonster::CookieInfo> cookie_infos;
883 this->cookie_monster()->
884 GetCookiesWithInfo(url_, options_, &cookie_line, &cookie_infos);
885 this->InvokeCallback(base::Bind(&CookieMonster::GetCookieInfoCallback::Run,
886 base::Unretained(&callback_),
887 cookie_line, cookie_infos));
891 // Task class for DeleteCookie call.
892 class CookieMonster::DeleteCookieTask
893 : public CookieMonster::CookieMonsterTask {
894 public:
895 DeleteCookieTask(CookieMonster* cookie_monster,
896 const GURL& url,
897 const std::string& cookie_name,
898 const base::Closure& callback)
899 : CookieMonsterTask(cookie_monster),
900 url_(url),
901 cookie_name_(cookie_name),
902 callback_(callback) { }
904 virtual void Run() OVERRIDE;
906 private:
907 GURL url_;
908 std::string cookie_name_;
909 base::Closure callback_;
911 DISALLOW_COPY_AND_ASSIGN(DeleteCookieTask);
914 void CookieMonster::DeleteCookieTask::Run() {
915 this->cookie_monster()->DeleteCookie(url_, cookie_name_);
916 if (!callback_.is_null()) {
917 this->InvokeCallback(callback_);
921 // Task class for DeleteSessionCookies call.
922 class CookieMonster::DeleteSessionCookiesTask
923 : public CookieMonster::CookieMonsterTask {
924 public:
925 DeleteSessionCookiesTask(
926 CookieMonster* cookie_monster,
927 const CookieMonster::DeleteCallback& callback)
928 : CookieMonsterTask(cookie_monster),
929 callback_(callback) { }
931 virtual void Run() OVERRIDE;
933 private:
934 CookieMonster::DeleteCallback callback_;
936 DISALLOW_COPY_AND_ASSIGN(DeleteSessionCookiesTask);
939 void CookieMonster::DeleteSessionCookiesTask::Run() {
940 int num_deleted = this->cookie_monster()->DeleteSessionCookies();
941 if (!callback_.is_null()) {
942 this->InvokeCallback(base::Bind(&CookieMonster::DeleteCallback::Run,
943 base::Unretained(&callback_), num_deleted));
947 // Asynchronous CookieMonster API
949 void CookieMonster::SetCookieWithDetailsAsync(
950 const GURL& url, const std::string& name, const std::string& value,
951 const std::string& domain, const std::string& path,
952 const base::Time& expiration_time, bool secure, bool http_only,
953 const SetCookiesCallback& callback) {
954 scoped_refptr<SetCookieWithDetailsTask> task =
955 new SetCookieWithDetailsTask(this, url, name, value, domain, path,
956 expiration_time, secure, http_only,
957 callback);
959 DoCookieTaskForURL(task, url);
962 void CookieMonster::GetAllCookiesAsync(const GetCookieListCallback& callback) {
963 scoped_refptr<GetAllCookiesTask> task =
964 new GetAllCookiesTask(this, callback);
966 DoCookieTask(task);
970 void CookieMonster::GetAllCookiesForURLWithOptionsAsync(
971 const GURL& url,
972 const CookieOptions& options,
973 const GetCookieListCallback& callback) {
974 scoped_refptr<GetAllCookiesForURLWithOptionsTask> task =
975 new GetAllCookiesForURLWithOptionsTask(this, url, options, callback);
977 DoCookieTaskForURL(task, url);
980 void CookieMonster::GetAllCookiesForURLAsync(
981 const GURL& url, const GetCookieListCallback& callback) {
982 CookieOptions options;
983 options.set_include_httponly();
984 scoped_refptr<GetAllCookiesForURLWithOptionsTask> task =
985 new GetAllCookiesForURLWithOptionsTask(this, url, options, callback);
987 DoCookieTaskForURL(task, url);
990 void CookieMonster::DeleteAllAsync(const DeleteCallback& callback) {
991 scoped_refptr<DeleteAllTask> task =
992 new DeleteAllTask(this, callback);
994 DoCookieTask(task);
997 void CookieMonster::DeleteAllCreatedBetweenAsync(
998 const Time& delete_begin, const Time& delete_end,
999 const DeleteCallback& callback) {
1000 scoped_refptr<DeleteAllCreatedBetweenTask> task =
1001 new DeleteAllCreatedBetweenTask(this, delete_begin, delete_end,
1002 callback);
1004 DoCookieTask(task);
1007 void CookieMonster::DeleteAllForHostAsync(
1008 const GURL& url, const DeleteCallback& callback) {
1009 scoped_refptr<DeleteAllForHostTask> task =
1010 new DeleteAllForHostTask(this, url, callback);
1012 DoCookieTaskForURL(task, url);
1015 void CookieMonster::DeleteCanonicalCookieAsync(
1016 const CanonicalCookie& cookie,
1017 const DeleteCookieCallback& callback) {
1018 scoped_refptr<DeleteCanonicalCookieTask> task =
1019 new DeleteCanonicalCookieTask(this, cookie, callback);
1021 DoCookieTask(task);
1024 void CookieMonster::SetCookieWithOptionsAsync(
1025 const GURL& url,
1026 const std::string& cookie_line,
1027 const CookieOptions& options,
1028 const SetCookiesCallback& callback) {
1029 scoped_refptr<SetCookieWithOptionsTask> task =
1030 new SetCookieWithOptionsTask(this, url, cookie_line, options, callback);
1032 DoCookieTaskForURL(task, url);
1035 void CookieMonster::GetCookiesWithOptionsAsync(
1036 const GURL& url,
1037 const CookieOptions& options,
1038 const GetCookiesCallback& callback) {
1039 scoped_refptr<GetCookiesWithOptionsTask> task =
1040 new GetCookiesWithOptionsTask(this, url, options, callback);
1042 DoCookieTaskForURL(task, url);
1045 void CookieMonster::GetCookiesWithInfoAsync(
1046 const GURL& url,
1047 const CookieOptions& options,
1048 const GetCookieInfoCallback& callback) {
1049 scoped_refptr<GetCookiesWithInfoTask> task =
1050 new GetCookiesWithInfoTask(this, url, options, callback);
1052 DoCookieTaskForURL(task, url);
1055 void CookieMonster::DeleteCookieAsync(const GURL& url,
1056 const std::string& cookie_name,
1057 const base::Closure& callback) {
1058 scoped_refptr<DeleteCookieTask> task =
1059 new DeleteCookieTask(this, url, cookie_name, callback);
1061 DoCookieTaskForURL(task, url);
1064 void CookieMonster::DeleteSessionCookiesAsync(
1065 const CookieStore::DeleteCallback& callback) {
1066 scoped_refptr<DeleteSessionCookiesTask> task =
1067 new DeleteSessionCookiesTask(this, callback);
1069 DoCookieTask(task);
1072 void CookieMonster::DoCookieTask(
1073 const scoped_refptr<CookieMonsterTask>& task_item) {
1075 base::AutoLock autolock(lock_);
1076 InitIfNecessary();
1077 if (!loaded_) {
1078 queue_.push(task_item);
1079 return;
1083 task_item->Run();
1086 void CookieMonster::DoCookieTaskForURL(
1087 const scoped_refptr<CookieMonsterTask>& task_item,
1088 const GURL& url) {
1090 base::AutoLock autolock(lock_);
1091 InitIfNecessary();
1092 // If cookies for the requested domain key (eTLD+1) have been loaded from DB
1093 // then run the task, otherwise load from DB.
1094 if (!loaded_) {
1095 // Checks if the domain key has been loaded.
1096 std::string key(cookie_util::GetEffectiveDomain(url.scheme(),
1097 url.host()));
1098 if (keys_loaded_.find(key) == keys_loaded_.end()) {
1099 std::map<std::string, std::deque<scoped_refptr<CookieMonsterTask> > >
1100 ::iterator it = tasks_queued_.find(key);
1101 if (it == tasks_queued_.end()) {
1102 store_->LoadCookiesForKey(key,
1103 base::Bind(&CookieMonster::OnKeyLoaded, this, key));
1104 it = tasks_queued_.insert(std::make_pair(key,
1105 std::deque<scoped_refptr<CookieMonsterTask> >())).first;
1107 it->second.push_back(task_item);
1108 return;
1112 task_item->Run();
1115 bool CookieMonster::SetCookieWithDetails(
1116 const GURL& url, const std::string& name, const std::string& value,
1117 const std::string& domain, const std::string& path,
1118 const base::Time& expiration_time, bool secure, bool http_only) {
1119 base::AutoLock autolock(lock_);
1121 if (!HasCookieableScheme(url))
1122 return false;
1124 Time creation_time = CurrentTime();
1125 last_time_seen_ = creation_time;
1127 // TODO(abarth): Take these values as parameters.
1128 std::string mac_key;
1129 std::string mac_algorithm;
1131 scoped_ptr<CanonicalCookie> cc;
1132 cc.reset(CanonicalCookie::Create(
1133 url, name, value, domain, path,
1134 mac_key, mac_algorithm,
1135 creation_time, expiration_time,
1136 secure, http_only, !expiration_time.is_null()));
1138 if (!cc.get())
1139 return false;
1141 CookieOptions options;
1142 options.set_include_httponly();
1143 return SetCanonicalCookie(&cc, creation_time, options);
1146 bool CookieMonster::InitializeFrom(const CookieList& list) {
1147 base::AutoLock autolock(lock_);
1148 InitIfNecessary();
1149 for (net::CookieList::const_iterator iter = list.begin();
1150 iter != list.end(); ++iter) {
1151 scoped_ptr<net::CookieMonster::CanonicalCookie> cookie;
1152 cookie.reset(new net::CookieMonster::CanonicalCookie(*iter));
1153 net::CookieOptions options;
1154 options.set_include_httponly();
1155 if (!SetCanonicalCookie(&cookie, cookie->CreationDate(),
1156 options)) {
1157 return false;
1160 return true;
1163 CookieList CookieMonster::GetAllCookies() {
1164 base::AutoLock autolock(lock_);
1166 // This function is being called to scrape the cookie list for management UI
1167 // or similar. We shouldn't show expired cookies in this list since it will
1168 // just be confusing to users, and this function is called rarely enough (and
1169 // is already slow enough) that it's OK to take the time to garbage collect
1170 // the expired cookies now.
1172 // Note that this does not prune cookies to be below our limits (if we've
1173 // exceeded them) the way that calling GarbageCollect() would.
1174 GarbageCollectExpired(Time::Now(),
1175 CookieMapItPair(cookies_.begin(), cookies_.end()),
1176 NULL);
1178 // Copy the CanonicalCookie pointers from the map so that we can use the same
1179 // sorter as elsewhere, then copy the result out.
1180 std::vector<CanonicalCookie*> cookie_ptrs;
1181 cookie_ptrs.reserve(cookies_.size());
1182 for (CookieMap::iterator it = cookies_.begin(); it != cookies_.end(); ++it)
1183 cookie_ptrs.push_back(it->second);
1184 std::sort(cookie_ptrs.begin(), cookie_ptrs.end(), CookieSorter);
1186 CookieList cookie_list;
1187 cookie_list.reserve(cookie_ptrs.size());
1188 for (std::vector<CanonicalCookie*>::const_iterator it = cookie_ptrs.begin();
1189 it != cookie_ptrs.end(); ++it)
1190 cookie_list.push_back(**it);
1192 return cookie_list;
1195 CookieList CookieMonster::GetAllCookiesForURLWithOptions(
1196 const GURL& url,
1197 const CookieOptions& options) {
1198 base::AutoLock autolock(lock_);
1200 std::vector<CanonicalCookie*> cookie_ptrs;
1201 FindCookiesForHostAndDomain(url, options, false, &cookie_ptrs);
1202 std::sort(cookie_ptrs.begin(), cookie_ptrs.end(), CookieSorter);
1204 CookieList cookies;
1205 for (std::vector<CanonicalCookie*>::const_iterator it = cookie_ptrs.begin();
1206 it != cookie_ptrs.end(); it++)
1207 cookies.push_back(**it);
1209 return cookies;
1212 CookieList CookieMonster::GetAllCookiesForURL(const GURL& url) {
1213 CookieOptions options;
1214 options.set_include_httponly();
1216 return GetAllCookiesForURLWithOptions(url, options);
1219 int CookieMonster::DeleteAll(bool sync_to_store) {
1220 base::AutoLock autolock(lock_);
1222 int num_deleted = 0;
1223 for (CookieMap::iterator it = cookies_.begin(); it != cookies_.end();) {
1224 CookieMap::iterator curit = it;
1225 ++it;
1226 InternalDeleteCookie(curit, sync_to_store,
1227 sync_to_store ? DELETE_COOKIE_EXPLICIT :
1228 DELETE_COOKIE_DONT_RECORD /* Destruction. */);
1229 ++num_deleted;
1232 return num_deleted;
1235 int CookieMonster::DeleteAllCreatedBetween(const Time& delete_begin,
1236 const Time& delete_end) {
1237 base::AutoLock autolock(lock_);
1239 int num_deleted = 0;
1240 for (CookieMap::iterator it = cookies_.begin(); it != cookies_.end();) {
1241 CookieMap::iterator curit = it;
1242 CanonicalCookie* cc = curit->second;
1243 ++it;
1245 if (cc->CreationDate() >= delete_begin &&
1246 (delete_end.is_null() || cc->CreationDate() < delete_end)) {
1247 InternalDeleteCookie(curit,
1248 true, /*sync_to_store*/
1249 DELETE_COOKIE_EXPLICIT);
1250 ++num_deleted;
1254 return num_deleted;
1257 int CookieMonster::DeleteAllForHost(const GURL& url) {
1258 base::AutoLock autolock(lock_);
1260 if (!HasCookieableScheme(url))
1261 return 0;
1263 const std::string scheme(url.scheme());
1264 const std::string host(url.host());
1266 // We store host cookies in the store by their canonical host name;
1267 // domain cookies are stored with a leading ".". So this is a pretty
1268 // simple lookup and per-cookie delete.
1269 int num_deleted = 0;
1270 for (CookieMapItPair its = cookies_.equal_range(GetKey(host));
1271 its.first != its.second;) {
1272 CookieMap::iterator curit = its.first;
1273 ++its.first;
1275 const CanonicalCookie* const cc = curit->second;
1277 // Delete only on a match as a host cookie.
1278 if (cc->IsHostCookie() && cc->IsDomainMatch(scheme, host)) {
1279 num_deleted++;
1281 InternalDeleteCookie(curit, true, DELETE_COOKIE_EXPLICIT);
1284 return num_deleted;
1287 bool CookieMonster::DeleteCanonicalCookie(const CanonicalCookie& cookie) {
1288 base::AutoLock autolock(lock_);
1290 for (CookieMapItPair its = cookies_.equal_range(GetKey(cookie.Domain()));
1291 its.first != its.second; ++its.first) {
1292 // The creation date acts as our unique index...
1293 if (its.first->second->CreationDate() == cookie.CreationDate()) {
1294 InternalDeleteCookie(its.first, true, DELETE_COOKIE_EXPLICIT);
1295 return true;
1298 return false;
1301 void CookieMonster::SetCookieableSchemes(
1302 const char* schemes[], size_t num_schemes) {
1303 base::AutoLock autolock(lock_);
1305 // Cookieable Schemes must be set before first use of function.
1306 DCHECK(!initialized_);
1308 cookieable_schemes_.clear();
1309 cookieable_schemes_.insert(cookieable_schemes_.end(),
1310 schemes, schemes + num_schemes);
1313 void CookieMonster::SetKeepExpiredCookies() {
1314 keep_expired_cookies_ = true;
1317 void CookieMonster::SetClearPersistentStoreOnExit(bool clear_local_store) {
1318 if (store_)
1319 store_->SetClearLocalStateOnExit(clear_local_store);
1322 // static
1323 void CookieMonster::EnableFileScheme() {
1324 enable_file_scheme_ = true;
1327 void CookieMonster::FlushStore(const base::Closure& callback) {
1328 base::AutoLock autolock(lock_);
1329 if (initialized_ && store_)
1330 store_->Flush(callback);
1331 else if (!callback.is_null())
1332 MessageLoop::current()->PostTask(FROM_HERE, callback);
1335 bool CookieMonster::SetCookieWithOptions(const GURL& url,
1336 const std::string& cookie_line,
1337 const CookieOptions& options) {
1338 base::AutoLock autolock(lock_);
1340 if (!HasCookieableScheme(url)) {
1341 return false;
1344 return SetCookieWithCreationTimeAndOptions(url, cookie_line, Time(), options);
1347 std::string CookieMonster::GetCookiesWithOptions(const GURL& url,
1348 const CookieOptions& options) {
1349 base::AutoLock autolock(lock_);
1351 if (!HasCookieableScheme(url))
1352 return std::string();
1354 TimeTicks start_time(TimeTicks::Now());
1356 std::vector<CanonicalCookie*> cookies;
1357 FindCookiesForHostAndDomain(url, options, true, &cookies);
1358 std::sort(cookies.begin(), cookies.end(), CookieSorter);
1360 std::string cookie_line = BuildCookieLine(cookies);
1362 histogram_time_get_->AddTime(TimeTicks::Now() - start_time);
1364 VLOG(kVlogGetCookies) << "GetCookies() result: " << cookie_line;
1366 return cookie_line;
1369 void CookieMonster::GetCookiesWithInfo(const GURL& url,
1370 const CookieOptions& options,
1371 std::string* cookie_line,
1372 std::vector<CookieInfo>* cookie_infos) {
1373 DCHECK(cookie_line->empty());
1374 DCHECK(cookie_infos->empty());
1376 base::AutoLock autolock(lock_);
1378 if (!HasCookieableScheme(url))
1379 return;
1381 TimeTicks start_time(TimeTicks::Now());
1383 std::vector<CanonicalCookie*> cookies;
1384 FindCookiesForHostAndDomain(url, options, true, &cookies);
1385 std::sort(cookies.begin(), cookies.end(), CookieSorter);
1386 *cookie_line = BuildCookieLine(cookies);
1388 histogram_time_get_->AddTime(TimeTicks::Now() - start_time);
1390 TimeTicks mac_start_time = TimeTicks::Now();
1391 BuildCookieInfoList(cookies, cookie_infos);
1392 histogram_time_mac_->AddTime(TimeTicks::Now() - mac_start_time);
1395 void CookieMonster::DeleteCookie(const GURL& url,
1396 const std::string& cookie_name) {
1397 base::AutoLock autolock(lock_);
1399 if (!HasCookieableScheme(url))
1400 return;
1402 CookieOptions options;
1403 options.set_include_httponly();
1404 // Get the cookies for this host and its domain(s).
1405 std::vector<CanonicalCookie*> cookies;
1406 FindCookiesForHostAndDomain(url, options, true, &cookies);
1407 std::set<CanonicalCookie*> matching_cookies;
1409 for (std::vector<CanonicalCookie*>::const_iterator it = cookies.begin();
1410 it != cookies.end(); ++it) {
1411 if ((*it)->Name() != cookie_name)
1412 continue;
1413 if (url.path().find((*it)->Path()))
1414 continue;
1415 matching_cookies.insert(*it);
1418 for (CookieMap::iterator it = cookies_.begin(); it != cookies_.end();) {
1419 CookieMap::iterator curit = it;
1420 ++it;
1421 if (matching_cookies.find(curit->second) != matching_cookies.end()) {
1422 InternalDeleteCookie(curit, true, DELETE_COOKIE_EXPLICIT);
1427 int CookieMonster::DeleteSessionCookies() {
1428 base::AutoLock autolock(lock_);
1430 int num_deleted = 0;
1431 for (CookieMap::iterator it = cookies_.begin(); it != cookies_.end();) {
1432 CookieMap::iterator curit = it;
1433 CanonicalCookie* cc = curit->second;
1434 ++it;
1436 if (!cc->IsPersistent()) {
1437 InternalDeleteCookie(curit,
1438 true, /*sync_to_store*/
1439 DELETE_COOKIE_EXPIRED);
1440 ++num_deleted;
1444 return num_deleted;
1447 CookieMonster* CookieMonster::GetCookieMonster() {
1448 return this;
1451 void CookieMonster::SetPersistSessionCookies(bool persist_session_cookies) {
1452 // This function must be called before the CookieMonster is used.
1453 DCHECK(!initialized_);
1454 persist_session_cookies_ = persist_session_cookies;
1457 void CookieMonster::SaveSessionCookies() {
1458 if (store_) {
1459 store_->SetClearLocalStateOnExit(false);
1463 CookieMonster::~CookieMonster() {
1464 DeleteAll(false);
1467 bool CookieMonster::SetCookieWithCreationTime(const GURL& url,
1468 const std::string& cookie_line,
1469 const base::Time& creation_time) {
1470 DCHECK(!store_) << "This method is only to be used by unit-tests.";
1471 base::AutoLock autolock(lock_);
1473 if (!HasCookieableScheme(url)) {
1474 return false;
1477 InitIfNecessary();
1478 return SetCookieWithCreationTimeAndOptions(url, cookie_line, creation_time,
1479 CookieOptions());
1482 void CookieMonster::InitStore() {
1483 DCHECK(store_) << "Store must exist to initialize";
1485 // We bind in the current time so that we can report the wall-clock time for
1486 // loading cookies.
1487 store_->Load(base::Bind(&CookieMonster::OnLoaded, this, TimeTicks::Now()));
1490 void CookieMonster::OnLoaded(TimeTicks beginning_time,
1491 const std::vector<CanonicalCookie*>& cookies) {
1492 StoreLoadedCookies(cookies);
1493 histogram_time_blocked_on_load_->AddTime(TimeTicks::Now() - beginning_time);
1495 // Invoke the task queue of cookie request.
1496 InvokeQueue();
1499 void CookieMonster::OnKeyLoaded(const std::string& key,
1500 const std::vector<CanonicalCookie*>& cookies) {
1501 // This function does its own separate locking.
1502 StoreLoadedCookies(cookies);
1504 std::deque<scoped_refptr<CookieMonsterTask> > tasks_queued;
1506 base::AutoLock autolock(lock_);
1507 keys_loaded_.insert(key);
1508 std::map<std::string, std::deque<scoped_refptr<CookieMonsterTask> > >
1509 ::iterator it = tasks_queued_.find(key);
1510 if (it == tasks_queued_.end())
1511 return;
1512 it->second.swap(tasks_queued);
1513 tasks_queued_.erase(it);
1516 while (!tasks_queued.empty()) {
1517 scoped_refptr<CookieMonsterTask> task = tasks_queued.front();
1518 task->Run();
1519 tasks_queued.pop_front();
1523 void CookieMonster::StoreLoadedCookies(
1524 const std::vector<CanonicalCookie*>& cookies) {
1525 // Initialize the store and sync in any saved persistent cookies. We don't
1526 // care if it's expired, insert it so it can be garbage collected, removed,
1527 // and sync'd.
1528 base::AutoLock autolock(lock_);
1530 for (std::vector<CanonicalCookie*>::const_iterator it = cookies.begin();
1531 it != cookies.end(); ++it) {
1532 int64 cookie_creation_time = (*it)->CreationDate().ToInternalValue();
1534 if (creation_times_.insert(cookie_creation_time).second) {
1535 InternalInsertCookie(GetKey((*it)->Domain()), *it, false);
1536 const Time cookie_access_time((*it)->LastAccessDate());
1537 if (earliest_access_time_.is_null() ||
1538 cookie_access_time < earliest_access_time_)
1539 earliest_access_time_ = cookie_access_time;
1540 } else {
1541 LOG(ERROR) << base::StringPrintf("Found cookies with duplicate creation "
1542 "times in backing store: "
1543 "{name='%s', domain='%s', path='%s'}",
1544 (*it)->Name().c_str(),
1545 (*it)->Domain().c_str(),
1546 (*it)->Path().c_str());
1547 // We've been given ownership of the cookie and are throwing it
1548 // away; reclaim the space.
1549 delete (*it);
1553 // After importing cookies from the PersistentCookieStore, verify that
1554 // none of our other constraints are violated.
1555 // In particular, the backing store might have given us duplicate cookies.
1557 // This method could be called multiple times due to priority loading, thus
1558 // cookies loaded in previous runs will be validated again, but this is OK
1559 // since they are expected to be much fewer than total DB.
1560 EnsureCookiesMapIsValid();
1563 void CookieMonster::InvokeQueue() {
1564 while (true) {
1565 scoped_refptr<CookieMonsterTask> request_task;
1567 base::AutoLock autolock(lock_);
1568 if (queue_.empty()) {
1569 loaded_ = true;
1570 creation_times_.clear();
1571 keys_loaded_.clear();
1572 break;
1574 request_task = queue_.front();
1575 queue_.pop();
1577 request_task->Run();
1581 void CookieMonster::EnsureCookiesMapIsValid() {
1582 lock_.AssertAcquired();
1584 int num_duplicates_trimmed = 0;
1586 // Iterate through all the of the cookies, grouped by host.
1587 CookieMap::iterator prev_range_end = cookies_.begin();
1588 while (prev_range_end != cookies_.end()) {
1589 CookieMap::iterator cur_range_begin = prev_range_end;
1590 const std::string key = cur_range_begin->first; // Keep a copy.
1591 CookieMap::iterator cur_range_end = cookies_.upper_bound(key);
1592 prev_range_end = cur_range_end;
1594 // Ensure no equivalent cookies for this host.
1595 num_duplicates_trimmed +=
1596 TrimDuplicateCookiesForKey(key, cur_range_begin, cur_range_end);
1599 // Record how many duplicates were found in the database.
1600 // See InitializeHistograms() for details.
1601 histogram_cookie_deletion_cause_->Add(num_duplicates_trimmed);
1604 int CookieMonster::TrimDuplicateCookiesForKey(
1605 const std::string& key,
1606 CookieMap::iterator begin,
1607 CookieMap::iterator end) {
1608 lock_.AssertAcquired();
1610 // Set of cookies ordered by creation time.
1611 typedef std::set<CookieMap::iterator, OrderByCreationTimeDesc> CookieSet;
1613 // Helper map we populate to find the duplicates.
1614 typedef std::map<CookieSignature, CookieSet> EquivalenceMap;
1615 EquivalenceMap equivalent_cookies;
1617 // The number of duplicate cookies that have been found.
1618 int num_duplicates = 0;
1620 // Iterate through all of the cookies in our range, and insert them into
1621 // the equivalence map.
1622 for (CookieMap::iterator it = begin; it != end; ++it) {
1623 DCHECK_EQ(key, it->first);
1624 CanonicalCookie* cookie = it->second;
1626 CookieSignature signature(cookie->Name(), cookie->Domain(),
1627 cookie->Path());
1628 CookieSet& set = equivalent_cookies[signature];
1630 // We found a duplicate!
1631 if (!set.empty())
1632 num_duplicates++;
1634 // We save the iterator into |cookies_| rather than the actual cookie
1635 // pointer, since we may need to delete it later.
1636 bool insert_success = set.insert(it).second;
1637 DCHECK(insert_success) <<
1638 "Duplicate creation times found in duplicate cookie name scan.";
1641 // If there were no duplicates, we are done!
1642 if (num_duplicates == 0)
1643 return 0;
1645 // Make sure we find everything below that we did above.
1646 int num_duplicates_found = 0;
1648 // Otherwise, delete all the duplicate cookies, both from our in-memory store
1649 // and from the backing store.
1650 for (EquivalenceMap::iterator it = equivalent_cookies.begin();
1651 it != equivalent_cookies.end();
1652 ++it) {
1653 const CookieSignature& signature = it->first;
1654 CookieSet& dupes = it->second;
1656 if (dupes.size() <= 1)
1657 continue; // This cookiename/path has no duplicates.
1658 num_duplicates_found += dupes.size() - 1;
1660 // Since |dups| is sorted by creation time (descending), the first cookie
1661 // is the most recent one, so we will keep it. The rest are duplicates.
1662 dupes.erase(dupes.begin());
1664 LOG(ERROR) << base::StringPrintf(
1665 "Found %d duplicate cookies for host='%s', "
1666 "with {name='%s', domain='%s', path='%s'}",
1667 static_cast<int>(dupes.size()),
1668 key.c_str(),
1669 signature.name.c_str(),
1670 signature.domain.c_str(),
1671 signature.path.c_str());
1673 // Remove all the cookies identified by |dupes|. It is valid to delete our
1674 // list of iterators one at a time, since |cookies_| is a multimap (they
1675 // don't invalidate existing iterators following deletion).
1676 for (CookieSet::iterator dupes_it = dupes.begin();
1677 dupes_it != dupes.end();
1678 ++dupes_it) {
1679 InternalDeleteCookie(*dupes_it, true,
1680 DELETE_COOKIE_DUPLICATE_IN_BACKING_STORE);
1683 DCHECK_EQ(num_duplicates, num_duplicates_found);
1685 return num_duplicates;
1688 // Note: file must be the last scheme.
1689 const char* CookieMonster::kDefaultCookieableSchemes[] =
1690 { "http", "https", "file" };
1691 const int CookieMonster::kDefaultCookieableSchemesCount =
1692 arraysize(CookieMonster::kDefaultCookieableSchemes);
1694 void CookieMonster::SetDefaultCookieableSchemes() {
1695 int num_schemes = enable_file_scheme_ ?
1696 kDefaultCookieableSchemesCount : kDefaultCookieableSchemesCount - 1;
1697 SetCookieableSchemes(kDefaultCookieableSchemes, num_schemes);
1701 void CookieMonster::FindCookiesForHostAndDomain(
1702 const GURL& url,
1703 const CookieOptions& options,
1704 bool update_access_time,
1705 std::vector<CanonicalCookie*>* cookies) {
1706 lock_.AssertAcquired();
1708 const Time current_time(CurrentTime());
1710 // Probe to save statistics relatively frequently. We do it here rather
1711 // than in the set path as many websites won't set cookies, and we
1712 // want to collect statistics whenever the browser's being used.
1713 RecordPeriodicStats(current_time);
1715 // Can just dispatch to FindCookiesForKey
1716 const std::string key(GetKey(url.host()));
1717 FindCookiesForKey(key, url, options, current_time,
1718 update_access_time, cookies);
1721 void CookieMonster::FindCookiesForKey(
1722 const std::string& key,
1723 const GURL& url,
1724 const CookieOptions& options,
1725 const Time& current,
1726 bool update_access_time,
1727 std::vector<CanonicalCookie*>* cookies) {
1728 lock_.AssertAcquired();
1730 const std::string scheme(url.scheme());
1731 const std::string host(url.host());
1732 bool secure = url.SchemeIsSecure();
1734 for (CookieMapItPair its = cookies_.equal_range(key);
1735 its.first != its.second; ) {
1736 CookieMap::iterator curit = its.first;
1737 CanonicalCookie* cc = curit->second;
1738 ++its.first;
1740 // If the cookie is expired, delete it.
1741 if (cc->IsExpired(current) && !keep_expired_cookies_) {
1742 InternalDeleteCookie(curit, true, DELETE_COOKIE_EXPIRED);
1743 continue;
1746 // Filter out HttpOnly cookies, per options.
1747 if (options.exclude_httponly() && cc->IsHttpOnly())
1748 continue;
1750 // Filter out secure cookies unless we're https.
1751 if (!secure && cc->IsSecure())
1752 continue;
1754 // Filter out cookies that don't apply to this domain.
1755 if (!cc->IsDomainMatch(scheme, host))
1756 continue;
1758 if (!cc->IsOnPath(url.path()))
1759 continue;
1761 // Add this cookie to the set of matching cookies. Update the access
1762 // time if we've been requested to do so.
1763 if (update_access_time) {
1764 InternalUpdateCookieAccessTime(cc, current);
1766 cookies->push_back(cc);
1770 bool CookieMonster::DeleteAnyEquivalentCookie(const std::string& key,
1771 const CanonicalCookie& ecc,
1772 bool skip_httponly,
1773 bool already_expired) {
1774 lock_.AssertAcquired();
1776 bool found_equivalent_cookie = false;
1777 bool skipped_httponly = false;
1778 for (CookieMapItPair its = cookies_.equal_range(key);
1779 its.first != its.second; ) {
1780 CookieMap::iterator curit = its.first;
1781 CanonicalCookie* cc = curit->second;
1782 ++its.first;
1784 if (ecc.IsEquivalent(*cc)) {
1785 // We should never have more than one equivalent cookie, since they should
1786 // overwrite each other.
1787 CHECK(!found_equivalent_cookie) <<
1788 "Duplicate equivalent cookies found, cookie store is corrupted.";
1789 if (skip_httponly && cc->IsHttpOnly()) {
1790 skipped_httponly = true;
1791 } else {
1792 InternalDeleteCookie(curit, true, already_expired ?
1793 DELETE_COOKIE_EXPIRED_OVERWRITE : DELETE_COOKIE_OVERWRITE);
1795 found_equivalent_cookie = true;
1798 return skipped_httponly;
1801 void CookieMonster::InternalInsertCookie(const std::string& key,
1802 CanonicalCookie* cc,
1803 bool sync_to_store) {
1804 lock_.AssertAcquired();
1806 if ((cc->IsPersistent() || persist_session_cookies_) &&
1807 store_ && sync_to_store)
1808 store_->AddCookie(*cc);
1809 cookies_.insert(CookieMap::value_type(key, cc));
1810 if (delegate_.get()) {
1811 delegate_->OnCookieChanged(
1812 *cc, false, CookieMonster::Delegate::CHANGE_COOKIE_EXPLICIT);
1816 bool CookieMonster::SetCookieWithCreationTimeAndOptions(
1817 const GURL& url,
1818 const std::string& cookie_line,
1819 const Time& creation_time_or_null,
1820 const CookieOptions& options) {
1821 lock_.AssertAcquired();
1823 VLOG(kVlogSetCookies) << "SetCookie() line: " << cookie_line;
1825 Time creation_time = creation_time_or_null;
1826 if (creation_time.is_null()) {
1827 creation_time = CurrentTime();
1828 last_time_seen_ = creation_time;
1831 // Parse the cookie.
1832 ParsedCookie pc(cookie_line);
1834 if (!pc.IsValid()) {
1835 VLOG(kVlogSetCookies) << "WARNING: Couldn't parse cookie";
1836 return false;
1839 if (options.exclude_httponly() && pc.IsHttpOnly()) {
1840 VLOG(kVlogSetCookies) << "SetCookie() not setting httponly cookie";
1841 return false;
1844 std::string cookie_domain;
1845 if (!GetCookieDomain(url, pc, &cookie_domain)) {
1846 return false;
1849 std::string cookie_path = CanonPath(url, pc);
1850 std::string mac_key = pc.HasMACKey() ? pc.MACKey() : std::string();
1851 std::string mac_algorithm = pc.HasMACAlgorithm() ?
1852 pc.MACAlgorithm() : std::string();
1854 scoped_ptr<CanonicalCookie> cc;
1855 Time cookie_expires = CanonExpiration(pc, creation_time);
1857 bool session_only = options.force_session() || cookie_expires.is_null();
1858 cc.reset(new CanonicalCookie(url, pc.Name(), pc.Value(), cookie_domain,
1859 cookie_path, mac_key, mac_algorithm,
1860 creation_time, cookie_expires,
1861 creation_time, pc.IsSecure(), pc.IsHttpOnly(),
1862 !cookie_expires.is_null(),
1863 !session_only));
1865 if (!cc.get()) {
1866 VLOG(kVlogSetCookies) << "WARNING: Failed to allocate CanonicalCookie";
1867 return false;
1869 return SetCanonicalCookie(&cc, creation_time, options);
1872 bool CookieMonster::SetCanonicalCookie(scoped_ptr<CanonicalCookie>* cc,
1873 const Time& creation_time,
1874 const CookieOptions& options) {
1875 const std::string key(GetKey((*cc)->Domain()));
1876 bool already_expired = (*cc)->IsExpired(creation_time);
1877 if (DeleteAnyEquivalentCookie(key, **cc, options.exclude_httponly(),
1878 already_expired)) {
1879 VLOG(kVlogSetCookies) << "SetCookie() not clobbering httponly cookie";
1880 return false;
1883 VLOG(kVlogSetCookies) << "SetCookie() key: " << key << " cc: "
1884 << (*cc)->DebugString();
1886 // Realize that we might be setting an expired cookie, and the only point
1887 // was to delete the cookie which we've already done.
1888 if (!already_expired || keep_expired_cookies_) {
1889 // See InitializeHistograms() for details.
1890 if ((*cc)->DoesExpire()) {
1891 histogram_expiration_duration_minutes_->Add(
1892 ((*cc)->ExpiryDate() - creation_time).InMinutes());
1895 InternalInsertCookie(key, cc->release(), true);
1898 // We assume that hopefully setting a cookie will be less common than
1899 // querying a cookie. Since setting a cookie can put us over our limits,
1900 // make sure that we garbage collect... We can also make the assumption that
1901 // if a cookie was set, in the common case it will be used soon after,
1902 // and we will purge the expired cookies in GetCookies().
1903 GarbageCollect(creation_time, key);
1905 return true;
1908 void CookieMonster::InternalUpdateCookieAccessTime(CanonicalCookie* cc,
1909 const Time& current) {
1910 lock_.AssertAcquired();
1912 // Based off the Mozilla code. When a cookie has been accessed recently,
1913 // don't bother updating its access time again. This reduces the number of
1914 // updates we do during pageload, which in turn reduces the chance our storage
1915 // backend will hit its batch thresholds and be forced to update.
1916 if ((current - cc->LastAccessDate()) < last_access_threshold_)
1917 return;
1919 // See InitializeHistograms() for details.
1920 histogram_between_access_interval_minutes_->Add(
1921 (current - cc->LastAccessDate()).InMinutes());
1923 cc->SetLastAccessDate(current);
1924 if ((cc->IsPersistent() || persist_session_cookies_) && store_)
1925 store_->UpdateCookieAccessTime(*cc);
1928 void CookieMonster::InternalDeleteCookie(CookieMap::iterator it,
1929 bool sync_to_store,
1930 DeletionCause deletion_cause) {
1931 lock_.AssertAcquired();
1933 // Ideally, this would be asserted up where we define ChangeCauseMapping,
1934 // but DeletionCause's visibility (or lack thereof) forces us to make
1935 // this check here.
1936 COMPILE_ASSERT(arraysize(ChangeCauseMapping) == DELETE_COOKIE_LAST_ENTRY + 1,
1937 ChangeCauseMapping_size_not_eq_DeletionCause_enum_size);
1939 // See InitializeHistograms() for details.
1940 if (deletion_cause != DELETE_COOKIE_DONT_RECORD)
1941 histogram_cookie_deletion_cause_->Add(deletion_cause);
1943 CanonicalCookie* cc = it->second;
1944 VLOG(kVlogSetCookies) << "InternalDeleteCookie() cc: " << cc->DebugString();
1946 if ((cc->IsPersistent() || persist_session_cookies_)
1947 && store_ && sync_to_store)
1948 store_->DeleteCookie(*cc);
1949 if (delegate_.get()) {
1950 ChangeCausePair mapping = ChangeCauseMapping[deletion_cause];
1952 if (mapping.notify)
1953 delegate_->OnCookieChanged(*cc, true, mapping.cause);
1955 cookies_.erase(it);
1956 delete cc;
1959 // Domain expiry behavior is unchanged by key/expiry scheme (the
1960 // meaning of the key is different, but that's not visible to this
1961 // routine).
1962 int CookieMonster::GarbageCollect(const Time& current,
1963 const std::string& key) {
1964 lock_.AssertAcquired();
1966 int num_deleted = 0;
1968 // Collect garbage for this key.
1969 if (cookies_.count(key) > kDomainMaxCookies) {
1970 VLOG(kVlogGarbageCollection) << "GarbageCollect() key: " << key;
1972 std::vector<CookieMap::iterator> cookie_its;
1973 num_deleted += GarbageCollectExpired(
1974 current, cookies_.equal_range(key), &cookie_its);
1975 base::Time oldest_removed;
1976 if (FindLeastRecentlyAccessed(kDomainMaxCookies, kDomainPurgeCookies,
1977 &oldest_removed, &cookie_its)) {
1978 // Delete in two passes so we can figure out what we're nuking
1979 // that would be kept at the global level.
1980 int num_subject_to_global_purge =
1981 GarbageCollectDeleteList(
1982 current,
1983 Time::Now() - TimeDelta::FromDays(kSafeFromGlobalPurgeDays),
1984 DELETE_COOKIE_EVICTED_DOMAIN_PRE_SAFE,
1985 cookie_its);
1986 num_deleted += num_subject_to_global_purge;
1987 // Correct because FindLeastRecentlyAccessed returns a sorted list.
1988 cookie_its.erase(cookie_its.begin(),
1989 cookie_its.begin() + num_subject_to_global_purge);
1990 num_deleted +=
1991 GarbageCollectDeleteList(
1992 current,
1993 Time(),
1994 DELETE_COOKIE_EVICTED_DOMAIN_POST_SAFE,
1995 cookie_its);
1999 // Collect garbage for everything. With firefox style we want to
2000 // preserve cookies touched in kSafeFromGlobalPurgeDays, otherwise
2001 // not.
2002 if (cookies_.size() > kMaxCookies &&
2003 (earliest_access_time_ <
2004 Time::Now() - TimeDelta::FromDays(kSafeFromGlobalPurgeDays))) {
2005 VLOG(kVlogGarbageCollection) << "GarbageCollect() everything";
2006 std::vector<CookieMap::iterator> cookie_its;
2007 base::Time oldest_left;
2008 num_deleted += GarbageCollectExpired(
2009 current, CookieMapItPair(cookies_.begin(), cookies_.end()),
2010 &cookie_its);
2011 if (FindLeastRecentlyAccessed(kMaxCookies, kPurgeCookies,
2012 &oldest_left, &cookie_its)) {
2013 Time oldest_safe_cookie(
2014 (Time::Now() - TimeDelta::FromDays(kSafeFromGlobalPurgeDays)));
2015 int num_evicted = GarbageCollectDeleteList(
2016 current,
2017 oldest_safe_cookie,
2018 DELETE_COOKIE_EVICTED_GLOBAL,
2019 cookie_its);
2021 // If no cookies were preserved by the time limit, the global last
2022 // access is set to the value returned from FindLeastRecentlyAccessed.
2023 // If the time limit preserved some cookies, we use the last access of
2024 // the oldest preserved cookie.
2025 if (num_evicted == static_cast<int>(cookie_its.size())) {
2026 earliest_access_time_ = oldest_left;
2027 } else {
2028 earliest_access_time_ =
2029 (*(cookie_its.begin() + num_evicted))->second->LastAccessDate();
2031 num_deleted += num_evicted;
2035 return num_deleted;
2038 int CookieMonster::GarbageCollectExpired(
2039 const Time& current,
2040 const CookieMapItPair& itpair,
2041 std::vector<CookieMap::iterator>* cookie_its) {
2042 if (keep_expired_cookies_)
2043 return 0;
2045 lock_.AssertAcquired();
2047 int num_deleted = 0;
2048 for (CookieMap::iterator it = itpair.first, end = itpair.second; it != end;) {
2049 CookieMap::iterator curit = it;
2050 ++it;
2052 if (curit->second->IsExpired(current)) {
2053 InternalDeleteCookie(curit, true, DELETE_COOKIE_EXPIRED);
2054 ++num_deleted;
2055 } else if (cookie_its) {
2056 cookie_its->push_back(curit);
2060 return num_deleted;
2063 int CookieMonster::GarbageCollectDeleteList(
2064 const Time& current,
2065 const Time& keep_accessed_after,
2066 DeletionCause cause,
2067 std::vector<CookieMap::iterator>& cookie_its) {
2068 int num_deleted = 0;
2069 for (std::vector<CookieMap::iterator>::iterator it = cookie_its.begin();
2070 it != cookie_its.end(); it++) {
2071 if (keep_accessed_after.is_null() ||
2072 (*it)->second->LastAccessDate() < keep_accessed_after) {
2073 histogram_evicted_last_access_minutes_->Add(
2074 (current - (*it)->second->LastAccessDate()).InMinutes());
2075 InternalDeleteCookie((*it), true, cause);
2076 num_deleted++;
2079 return num_deleted;
2082 // A wrapper around RegistryControlledDomainService::GetDomainAndRegistry
2083 // to make clear we're creating a key for our local map. Here and
2084 // in FindCookiesForHostAndDomain() are the only two places where
2085 // we need to conditionalize based on key type.
2087 // Note that this key algorithm explicitly ignores the scheme. This is
2088 // because when we're entering cookies into the map from the backing store,
2089 // we in general won't have the scheme at that point.
2090 // In practical terms, this means that file cookies will be stored
2091 // in the map either by an empty string or by UNC name (and will be
2092 // limited by kMaxCookiesPerHost), and extension cookies will be stored
2093 // based on the single extension id, as the extension id won't have the
2094 // form of a DNS host and hence GetKey() will return it unchanged.
2096 // Arguably the right thing to do here is to make the key
2097 // algorithm dependent on the scheme, and make sure that the scheme is
2098 // available everywhere the key must be obtained (specfically at backing
2099 // store load time). This would require either changing the backing store
2100 // database schema to include the scheme (far more trouble than it's worth), or
2101 // separating out file cookies into their own CookieMonster instance and
2102 // thus restricting each scheme to a single cookie monster (which might
2103 // be worth it, but is still too much trouble to solve what is currently a
2104 // non-problem).
2105 std::string CookieMonster::GetKey(const std::string& domain) const {
2106 std::string effective_domain(
2107 RegistryControlledDomainService::GetDomainAndRegistry(domain));
2108 if (effective_domain.empty())
2109 effective_domain = domain;
2111 if (!effective_domain.empty() && effective_domain[0] == '.')
2112 return effective_domain.substr(1);
2113 return effective_domain;
2116 bool CookieMonster::HasCookieableScheme(const GURL& url) {
2117 lock_.AssertAcquired();
2119 // Make sure the request is on a cookie-able url scheme.
2120 for (size_t i = 0; i < cookieable_schemes_.size(); ++i) {
2121 // We matched a scheme.
2122 if (url.SchemeIs(cookieable_schemes_[i].c_str())) {
2123 // We've matched a supported scheme.
2124 return true;
2128 // The scheme didn't match any in our whitelist.
2129 VLOG(kVlogPerCookieMonster) << "WARNING: Unsupported cookie scheme: "
2130 << url.scheme();
2131 return false;
2134 // Test to see if stats should be recorded, and record them if so.
2135 // The goal here is to get sampling for the average browser-hour of
2136 // activity. We won't take samples when the web isn't being surfed,
2137 // and when the web is being surfed, we'll take samples about every
2138 // kRecordStatisticsIntervalSeconds.
2139 // last_statistic_record_time_ is initialized to Now() rather than null
2140 // in the constructor so that we won't take statistics right after
2141 // startup, to avoid bias from browsers that are started but not used.
2142 void CookieMonster::RecordPeriodicStats(const base::Time& current_time) {
2143 const base::TimeDelta kRecordStatisticsIntervalTime(
2144 base::TimeDelta::FromSeconds(kRecordStatisticsIntervalSeconds));
2146 // If we've taken statistics recently, return.
2147 if (current_time - last_statistic_record_time_ <=
2148 kRecordStatisticsIntervalTime) {
2149 return;
2152 // See InitializeHistograms() for details.
2153 histogram_count_->Add(cookies_.size());
2155 // More detailed statistics on cookie counts at different granularities.
2156 TimeTicks beginning_of_time(TimeTicks::Now());
2158 for (CookieMap::const_iterator it_key = cookies_.begin();
2159 it_key != cookies_.end(); ) {
2160 const std::string& key(it_key->first);
2162 int key_count = 0;
2163 typedef std::map<std::string, unsigned int> DomainMap;
2164 DomainMap domain_map;
2165 CookieMapItPair its_cookies = cookies_.equal_range(key);
2166 while (its_cookies.first != its_cookies.second) {
2167 key_count++;
2168 const std::string& cookie_domain(its_cookies.first->second->Domain());
2169 domain_map[cookie_domain]++;
2171 its_cookies.first++;
2173 histogram_etldp1_count_->Add(key_count);
2174 histogram_domain_per_etldp1_count_->Add(domain_map.size());
2175 for (DomainMap::const_iterator domain_map_it = domain_map.begin();
2176 domain_map_it != domain_map.end(); domain_map_it++)
2177 histogram_domain_count_->Add(domain_map_it->second);
2179 it_key = its_cookies.second;
2182 VLOG(kVlogPeriodic)
2183 << "Time for recording cookie stats (us): "
2184 << (TimeTicks::Now() - beginning_of_time).InMicroseconds();
2186 last_statistic_record_time_ = current_time;
2189 // Initialize all histogram counter variables used in this class.
2191 // Normal histogram usage involves using the macros defined in
2192 // histogram.h, which automatically takes care of declaring these
2193 // variables (as statics), initializing them, and accumulating into
2194 // them, all from a single entry point. Unfortunately, that solution
2195 // doesn't work for the CookieMonster, as it's vulnerable to races between
2196 // separate threads executing the same functions and hence initializing the
2197 // same static variables. There isn't a race danger in the histogram
2198 // accumulation calls; they are written to be resilient to simultaneous
2199 // calls from multiple threads.
2201 // The solution taken here is to have per-CookieMonster instance
2202 // variables that are constructed during CookieMonster construction.
2203 // Note that these variables refer to the same underlying histogram,
2204 // so we still race (but safely) with other CookieMonster instances
2205 // for accumulation.
2207 // To do this we've expanded out the individual histogram macros calls,
2208 // with declarations of the variables in the class decl, initialization here
2209 // (done from the class constructor) and direct calls to the accumulation
2210 // methods where needed. The specific histogram macro calls on which the
2211 // initialization is based are included in comments below.
2212 void CookieMonster::InitializeHistograms() {
2213 // From UMA_HISTOGRAM_CUSTOM_COUNTS
2214 histogram_expiration_duration_minutes_ = base::Histogram::FactoryGet(
2215 "Cookie.ExpirationDurationMinutes",
2216 1, kMinutesInTenYears, 50,
2217 base::Histogram::kUmaTargetedHistogramFlag);
2218 histogram_between_access_interval_minutes_ = base::Histogram::FactoryGet(
2219 "Cookie.BetweenAccessIntervalMinutes",
2220 1, kMinutesInTenYears, 50,
2221 base::Histogram::kUmaTargetedHistogramFlag);
2222 histogram_evicted_last_access_minutes_ = base::Histogram::FactoryGet(
2223 "Cookie.EvictedLastAccessMinutes",
2224 1, kMinutesInTenYears, 50,
2225 base::Histogram::kUmaTargetedHistogramFlag);
2226 histogram_count_ = base::Histogram::FactoryGet(
2227 "Cookie.Count", 1, 4000, 50,
2228 base::Histogram::kUmaTargetedHistogramFlag);
2229 histogram_domain_count_ = base::Histogram::FactoryGet(
2230 "Cookie.DomainCount", 1, 4000, 50,
2231 base::Histogram::kUmaTargetedHistogramFlag);
2232 histogram_etldp1_count_ = base::Histogram::FactoryGet(
2233 "Cookie.Etldp1Count", 1, 4000, 50,
2234 base::Histogram::kUmaTargetedHistogramFlag);
2235 histogram_domain_per_etldp1_count_ = base::Histogram::FactoryGet(
2236 "Cookie.DomainPerEtldp1Count", 1, 4000, 50,
2237 base::Histogram::kUmaTargetedHistogramFlag);
2239 // From UMA_HISTOGRAM_COUNTS_10000 & UMA_HISTOGRAM_CUSTOM_COUNTS
2240 histogram_number_duplicate_db_cookies_ = base::Histogram::FactoryGet(
2241 "Net.NumDuplicateCookiesInDb", 1, 10000, 50,
2242 base::Histogram::kUmaTargetedHistogramFlag);
2244 // From UMA_HISTOGRAM_ENUMERATION
2245 histogram_cookie_deletion_cause_ = base::LinearHistogram::FactoryGet(
2246 "Cookie.DeletionCause", 1,
2247 DELETE_COOKIE_LAST_ENTRY - 1, DELETE_COOKIE_LAST_ENTRY,
2248 base::Histogram::kUmaTargetedHistogramFlag);
2250 // From UMA_HISTOGRAM_{CUSTOM_,}TIMES
2251 histogram_time_get_ = base::Histogram::FactoryTimeGet("Cookie.TimeGet",
2252 base::TimeDelta::FromMilliseconds(1), base::TimeDelta::FromMinutes(1),
2253 50, base::Histogram::kUmaTargetedHistogramFlag);
2254 histogram_time_mac_ = base::Histogram::FactoryTimeGet("Cookie.TimeGetMac",
2255 base::TimeDelta::FromMilliseconds(1), base::TimeDelta::FromMinutes(1),
2256 50, base::Histogram::kUmaTargetedHistogramFlag);
2257 histogram_time_blocked_on_load_ = base::Histogram::FactoryTimeGet(
2258 "Cookie.TimeBlockedOnLoad",
2259 base::TimeDelta::FromMilliseconds(1), base::TimeDelta::FromMinutes(1),
2260 50, base::Histogram::kUmaTargetedHistogramFlag);
2264 // The system resolution is not high enough, so we can have multiple
2265 // set cookies that result in the same system time. When this happens, we
2266 // increment by one Time unit. Let's hope computers don't get too fast.
2267 Time CookieMonster::CurrentTime() {
2268 return std::max(Time::Now(),
2269 Time::FromInternalValue(last_time_seen_.ToInternalValue() + 1));
2272 CookieMonster::ParsedCookie::ParsedCookie(const std::string& cookie_line)
2273 : is_valid_(false),
2274 path_index_(0),
2275 domain_index_(0),
2276 mac_key_index_(0),
2277 mac_algorithm_index_(0),
2278 expires_index_(0),
2279 maxage_index_(0),
2280 secure_index_(0),
2281 httponly_index_(0) {
2283 if (cookie_line.size() > kMaxCookieSize) {
2284 VLOG(1) << "Not parsing cookie, too large: " << cookie_line.size();
2285 return;
2288 ParseTokenValuePairs(cookie_line);
2289 if (!pairs_.empty()) {
2290 is_valid_ = true;
2291 SetupAttributes();
2295 CookieMonster::ParsedCookie::~ParsedCookie() {
2298 // Returns true if |c| occurs in |chars|
2299 // TODO(erikwright): maybe make this take an iterator, could check for end also?
2300 static inline bool CharIsA(const char c, const char* chars) {
2301 return strchr(chars, c) != NULL;
2303 // Seek the iterator to the first occurrence of a character in |chars|.
2304 // Returns true if it hit the end, false otherwise.
2305 static inline bool SeekTo(std::string::const_iterator* it,
2306 const std::string::const_iterator& end,
2307 const char* chars) {
2308 for (; *it != end && !CharIsA(**it, chars); ++(*it)) {}
2309 return *it == end;
2311 // Seek the iterator to the first occurrence of a character not in |chars|.
2312 // Returns true if it hit the end, false otherwise.
2313 static inline bool SeekPast(std::string::const_iterator* it,
2314 const std::string::const_iterator& end,
2315 const char* chars) {
2316 for (; *it != end && CharIsA(**it, chars); ++(*it)) {}
2317 return *it == end;
2319 static inline bool SeekBackPast(std::string::const_iterator* it,
2320 const std::string::const_iterator& end,
2321 const char* chars) {
2322 for (; *it != end && CharIsA(**it, chars); --(*it)) {}
2323 return *it == end;
2326 const char CookieMonster::ParsedCookie::kTerminator[] = "\n\r\0";
2327 const int CookieMonster::ParsedCookie::kTerminatorLen =
2328 sizeof(kTerminator) - 1;
2329 const char CookieMonster::ParsedCookie::kWhitespace[] = " \t";
2330 const char CookieMonster::ParsedCookie::kValueSeparator[] = ";";
2331 const char CookieMonster::ParsedCookie::kTokenSeparator[] = ";=";
2333 // Create a cookie-line for the cookie. For debugging only!
2334 // If we want to use this for something more than debugging, we
2335 // should rewrite it better...
2336 std::string CookieMonster::ParsedCookie::DebugString() const {
2337 std::string out;
2338 for (PairList::const_iterator it = pairs_.begin();
2339 it != pairs_.end(); ++it) {
2340 out.append(it->first);
2341 out.append("=");
2342 out.append(it->second);
2343 out.append("; ");
2345 return out;
2348 std::string::const_iterator CookieMonster::ParsedCookie::FindFirstTerminator(
2349 const std::string& s) {
2350 std::string::const_iterator end = s.end();
2351 size_t term_pos =
2352 s.find_first_of(std::string(kTerminator, kTerminatorLen));
2353 if (term_pos != std::string::npos) {
2354 // We found a character we should treat as an end of string.
2355 end = s.begin() + term_pos;
2357 return end;
2360 bool CookieMonster::ParsedCookie::ParseToken(
2361 std::string::const_iterator* it,
2362 const std::string::const_iterator& end,
2363 std::string::const_iterator* token_start,
2364 std::string::const_iterator* token_end) {
2365 DCHECK(it && token_start && token_end);
2366 std::string::const_iterator token_real_end;
2368 // Seek past any whitespace before the "token" (the name).
2369 // token_start should point at the first character in the token
2370 if (SeekPast(it, end, kWhitespace))
2371 return false; // No token, whitespace or empty.
2372 *token_start = *it;
2374 // Seek over the token, to the token separator.
2375 // token_real_end should point at the token separator, i.e. '='.
2376 // If it == end after the seek, we probably have a token-value.
2377 SeekTo(it, end, kTokenSeparator);
2378 token_real_end = *it;
2380 // Ignore any whitespace between the token and the token separator.
2381 // token_end should point after the last interesting token character,
2382 // pointing at either whitespace, or at '=' (and equal to token_real_end).
2383 if (*it != *token_start) { // We could have an empty token name.
2384 --(*it); // Go back before the token separator.
2385 // Skip over any whitespace to the first non-whitespace character.
2386 SeekBackPast(it, *token_start, kWhitespace);
2387 // Point after it.
2388 ++(*it);
2390 *token_end = *it;
2392 // Seek us back to the end of the token.
2393 *it = token_real_end;
2394 return true;
2397 void CookieMonster::ParsedCookie::ParseValue(
2398 std::string::const_iterator* it,
2399 const std::string::const_iterator& end,
2400 std::string::const_iterator* value_start,
2401 std::string::const_iterator* value_end) {
2402 DCHECK(it && value_start && value_end);
2404 // Seek past any whitespace that might in-between the token and value.
2405 SeekPast(it, end, kWhitespace);
2406 // value_start should point at the first character of the value.
2407 *value_start = *it;
2409 // Just look for ';' to terminate ('=' allowed).
2410 // We can hit the end, maybe they didn't terminate.
2411 SeekTo(it, end, kValueSeparator);
2413 // Will be pointed at the ; seperator or the end.
2414 *value_end = *it;
2416 // Ignore any unwanted whitespace after the value.
2417 if (*value_end != *value_start) { // Could have an empty value
2418 --(*value_end);
2419 SeekBackPast(value_end, *value_start, kWhitespace);
2420 ++(*value_end);
2424 std::string CookieMonster::ParsedCookie::ParseTokenString(
2425 const std::string& token) {
2426 std::string::const_iterator it = token.begin();
2427 std::string::const_iterator end = FindFirstTerminator(token);
2429 std::string::const_iterator token_start, token_end;
2430 if (ParseToken(&it, end, &token_start, &token_end))
2431 return std::string(token_start, token_end);
2432 return std::string();
2435 std::string CookieMonster::ParsedCookie::ParseValueString(
2436 const std::string& value) {
2437 std::string::const_iterator it = value.begin();
2438 std::string::const_iterator end = FindFirstTerminator(value);
2440 std::string::const_iterator value_start, value_end;
2441 ParseValue(&it, end, &value_start, &value_end);
2442 return std::string(value_start, value_end);
2445 // Parse all token/value pairs and populate pairs_.
2446 void CookieMonster::ParsedCookie::ParseTokenValuePairs(
2447 const std::string& cookie_line) {
2448 pairs_.clear();
2450 // Ok, here we go. We should be expecting to be starting somewhere
2451 // before the cookie line, not including any header name...
2452 std::string::const_iterator start = cookie_line.begin();
2453 std::string::const_iterator it = start;
2455 // TODO(erikwright): Make sure we're stripping \r\n in the network code.
2456 // Then we can log any unexpected terminators.
2457 std::string::const_iterator end = FindFirstTerminator(cookie_line);
2459 for (int pair_num = 0; pair_num < kMaxPairs && it != end; ++pair_num) {
2460 TokenValuePair pair;
2462 std::string::const_iterator token_start, token_end;
2463 if (!ParseToken(&it, end, &token_start, &token_end))
2464 break;
2466 if (it == end || *it != '=') {
2467 // We have a token-value, we didn't have any token name.
2468 if (pair_num == 0) {
2469 // For the first time around, we want to treat single values
2470 // as a value with an empty name. (Mozilla bug 169091).
2471 // IE seems to also have this behavior, ex "AAA", and "AAA=10" will
2472 // set 2 different cookies, and setting "BBB" will then replace "AAA".
2473 pair.first = "";
2474 // Rewind to the beginning of what we thought was the token name,
2475 // and let it get parsed as a value.
2476 it = token_start;
2477 } else {
2478 // Any not-first attribute we want to treat a value as a
2479 // name with an empty value... This is so something like
2480 // "secure;" will get parsed as a Token name, and not a value.
2481 pair.first = std::string(token_start, token_end);
2483 } else {
2484 // We have a TOKEN=VALUE.
2485 pair.first = std::string(token_start, token_end);
2486 ++it; // Skip past the '='.
2489 // OK, now try to parse a value.
2490 std::string::const_iterator value_start, value_end;
2491 ParseValue(&it, end, &value_start, &value_end);
2492 // OK, we're finished with a Token/Value.
2493 pair.second = std::string(value_start, value_end);
2495 // From RFC2109: "Attributes (names) (attr) are case-insensitive."
2496 if (pair_num != 0)
2497 StringToLowerASCII(&pair.first);
2498 pairs_.push_back(pair);
2500 // We've processed a token/value pair, we're either at the end of
2501 // the string or a ValueSeparator like ';', which we want to skip.
2502 if (it != end)
2503 ++it;
2507 void CookieMonster::ParsedCookie::SetupAttributes() {
2508 static const char kPathTokenName[] = "path";
2509 static const char kDomainTokenName[] = "domain";
2510 static const char kMACKeyTokenName[] = "mac-key";
2511 static const char kMACAlgorithmTokenName[] = "mac-algorithm";
2512 static const char kExpiresTokenName[] = "expires";
2513 static const char kMaxAgeTokenName[] = "max-age";
2514 static const char kSecureTokenName[] = "secure";
2515 static const char kHttpOnlyTokenName[] = "httponly";
2517 // We skip over the first token/value, the user supplied one.
2518 for (size_t i = 1; i < pairs_.size(); ++i) {
2519 if (pairs_[i].first == kPathTokenName) {
2520 path_index_ = i;
2521 } else if (pairs_[i].first == kDomainTokenName) {
2522 domain_index_ = i;
2523 } else if (pairs_[i].first == kMACKeyTokenName) {
2524 mac_key_index_ = i;
2525 } else if (pairs_[i].first == kMACAlgorithmTokenName) {
2526 mac_algorithm_index_ = i;
2527 } else if (pairs_[i].first == kExpiresTokenName) {
2528 expires_index_ = i;
2529 } else if (pairs_[i].first == kMaxAgeTokenName) {
2530 maxage_index_ = i;
2531 } else if (pairs_[i].first == kSecureTokenName) {
2532 secure_index_ = i;
2533 } else if (pairs_[i].first == kHttpOnlyTokenName) {
2534 httponly_index_ = i;
2535 } else {
2536 /* some attribute we don't know or don't care about. */
2541 CookieMonster::CanonicalCookie::CanonicalCookie()
2542 : secure_(false),
2543 httponly_(false),
2544 has_expires_(false),
2545 is_persistent_(false) {
2546 SetSessionCookieExpiryTime();
2549 CookieMonster::CanonicalCookie::CanonicalCookie(
2550 const GURL& url, const std::string& name, const std::string& value,
2551 const std::string& domain, const std::string& path,
2552 const std::string& mac_key, const std::string& mac_algorithm,
2553 const base::Time& creation, const base::Time& expiration,
2554 const base::Time& last_access, bool secure, bool httponly, bool has_expires,
2555 bool is_persistent)
2556 : source_(GetCookieSourceFromURL(url)),
2557 name_(name),
2558 value_(value),
2559 domain_(domain),
2560 path_(path),
2561 mac_key_(mac_key),
2562 mac_algorithm_(mac_algorithm),
2563 creation_date_(creation),
2564 expiry_date_(expiration),
2565 last_access_date_(last_access),
2566 secure_(secure),
2567 httponly_(httponly),
2568 has_expires_(has_expires),
2569 is_persistent_(is_persistent) {
2570 if (!has_expires_) {
2571 DCHECK(!is_persistent_);
2572 SetSessionCookieExpiryTime();
2576 CookieMonster::CanonicalCookie::CanonicalCookie(const GURL& url,
2577 const ParsedCookie& pc)
2578 : source_(GetCookieSourceFromURL(url)),
2579 name_(pc.Name()),
2580 value_(pc.Value()),
2581 path_(CanonPath(url, pc)),
2582 mac_key_(pc.MACKey()),
2583 mac_algorithm_(pc.MACAlgorithm()),
2584 creation_date_(Time::Now()),
2585 last_access_date_(Time()),
2586 secure_(pc.IsSecure()),
2587 httponly_(pc.IsHttpOnly()),
2588 has_expires_(pc.HasExpires()),
2589 is_persistent_(pc.HasExpires()) {
2590 if (has_expires_)
2591 expiry_date_ = CanonExpiration(pc, creation_date_);
2592 else
2593 SetSessionCookieExpiryTime();
2595 // Do the best we can with the domain.
2596 std::string cookie_domain;
2597 std::string domain_string;
2598 if (pc.HasDomain()) {
2599 domain_string = pc.Domain();
2601 bool result
2602 = cookie_util::GetCookieDomainWithString(url, domain_string,
2603 &cookie_domain);
2604 // Caller is responsible for passing in good arguments.
2605 DCHECK(result);
2606 domain_ = cookie_domain;
2609 CookieMonster::CanonicalCookie::~CanonicalCookie() {
2612 std::string CookieMonster::CanonicalCookie::GetCookieSourceFromURL(
2613 const GURL& url) {
2614 if (url.SchemeIsFile())
2615 return url.spec();
2617 url_canon::Replacements<char> replacements;
2618 replacements.ClearPort();
2619 if (url.SchemeIsSecure())
2620 replacements.SetScheme("http", url_parse::Component(0, 4));
2622 return url.GetOrigin().ReplaceComponents(replacements).spec();
2625 void CookieMonster::CanonicalCookie::SetSessionCookieExpiryTime() {
2626 #if defined(ENABLE_PERSISTENT_SESSION_COOKIES)
2627 // Mobile apps can sometimes be shut down without any warning, so the session
2628 // cookie has to be persistent and given a default expiration time.
2629 expiry_date_ = base::Time::Now() +
2630 base::TimeDelta::FromDays(kPersistentSessionCookieExpiryInDays);
2631 has_expires_ = true;
2632 #endif
2635 CookieMonster::CanonicalCookie* CookieMonster::CanonicalCookie::Create(
2636 const GURL& url,
2637 const ParsedCookie& pc) {
2638 if (!pc.IsValid()) {
2639 return NULL;
2642 std::string domain_string;
2643 if (!GetCookieDomain(url, pc, &domain_string)) {
2644 return NULL;
2646 std::string path_string = CanonPath(url, pc);
2647 std::string mac_key = pc.HasMACKey() ? pc.MACKey() : std::string();
2648 std::string mac_algorithm = pc.HasMACAlgorithm() ?
2649 pc.MACAlgorithm() : std::string();
2650 Time creation_time = Time::Now();
2651 Time expiration_time;
2652 if (pc.HasExpires())
2653 expiration_time = net::CookieMonster::ParseCookieTime(pc.Expires());
2655 return (Create(url, pc.Name(), pc.Value(), domain_string, path_string,
2656 mac_key, mac_algorithm, creation_time, expiration_time,
2657 pc.IsSecure(), pc.IsHttpOnly(), !expiration_time.is_null()));
2660 CookieMonster::CanonicalCookie* CookieMonster::CanonicalCookie::Create(
2661 const GURL& url,
2662 const std::string& name,
2663 const std::string& value,
2664 const std::string& domain,
2665 const std::string& path,
2666 const std::string& mac_key,
2667 const std::string& mac_algorithm,
2668 const base::Time& creation,
2669 const base::Time& expiration,
2670 bool secure,
2671 bool http_only,
2672 bool is_persistent) {
2673 // Expect valid attribute tokens and values, as defined by the ParsedCookie
2674 // logic, otherwise don't create the cookie.
2675 std::string parsed_name = ParsedCookie::ParseTokenString(name);
2676 if (parsed_name != name)
2677 return NULL;
2678 std::string parsed_value = ParsedCookie::ParseValueString(value);
2679 if (parsed_value != value)
2680 return NULL;
2682 std::string parsed_domain = ParsedCookie::ParseValueString(domain);
2683 if (parsed_domain != domain)
2684 return NULL;
2685 std::string cookie_domain;
2686 if (!cookie_util::GetCookieDomainWithString(url, parsed_domain,
2687 &cookie_domain)) {
2688 return NULL;
2691 std::string parsed_path = ParsedCookie::ParseValueString(path);
2692 if (parsed_path != path)
2693 return NULL;
2695 std::string cookie_path = CanonPathWithString(url, parsed_path);
2696 // Expect that the path was either not specified (empty), or is valid.
2697 if (!parsed_path.empty() && cookie_path != parsed_path)
2698 return NULL;
2699 // Canonicalize path again to make sure it escapes characters as needed.
2700 url_parse::Component path_component(0, cookie_path.length());
2701 url_canon::RawCanonOutputT<char> canon_path;
2702 url_parse::Component canon_path_component;
2703 url_canon::CanonicalizePath(cookie_path.data(), path_component,
2704 &canon_path, &canon_path_component);
2705 cookie_path = std::string(canon_path.data() + canon_path_component.begin,
2706 canon_path_component.len);
2708 return new CanonicalCookie(url, parsed_name, parsed_value, cookie_domain,
2709 cookie_path, mac_key, mac_algorithm, creation,
2710 expiration, creation, secure, http_only,
2711 !expiration.is_null(), is_persistent);
2714 bool CookieMonster::CanonicalCookie::IsOnPath(
2715 const std::string& url_path) const {
2717 // A zero length would be unsafe for our trailing '/' checks, and
2718 // would also make no sense for our prefix match. The code that
2719 // creates a CanonicalCookie should make sure the path is never zero length,
2720 // but we double check anyway.
2721 if (path_.empty())
2722 return false;
2724 // The Mozilla code broke this into three cases, based on if the cookie path
2725 // was longer, the same length, or shorter than the length of the url path.
2726 // I think the approach below is simpler.
2728 // Make sure the cookie path is a prefix of the url path. If the
2729 // url path is shorter than the cookie path, then the cookie path
2730 // can't be a prefix.
2731 if (url_path.find(path_) != 0)
2732 return false;
2734 // Now we know that url_path is >= cookie_path, and that cookie_path
2735 // is a prefix of url_path. If they are the are the same length then
2736 // they are identical, otherwise we need an additional check:
2738 // In order to avoid in correctly matching a cookie path of /blah
2739 // with a request path of '/blahblah/', we need to make sure that either
2740 // the cookie path ends in a trailing '/', or that we prefix up to a '/'
2741 // in the url path. Since we know that the url path length is greater
2742 // than the cookie path length, it's safe to index one byte past.
2743 if (path_.length() != url_path.length() &&
2744 path_[path_.length() - 1] != '/' &&
2745 url_path[path_.length()] != '/')
2746 return false;
2748 return true;
2751 bool CookieMonster::CanonicalCookie::IsDomainMatch(
2752 const std::string& scheme,
2753 const std::string& host) const {
2754 // Can domain match in two ways; as a domain cookie (where the cookie
2755 // domain begins with ".") or as a host cookie (where it doesn't).
2757 // Some consumers of the CookieMonster expect to set cookies on
2758 // URLs like http://.strange.url. To retrieve cookies in this instance,
2759 // we allow matching as a host cookie even when the domain_ starts with
2760 // a period.
2761 if (host == domain_)
2762 return true;
2764 // Domain cookie must have an initial ".". To match, it must be
2765 // equal to url's host with initial period removed, or a suffix of
2766 // it.
2768 // Arguably this should only apply to "http" or "https" cookies, but
2769 // extension cookie tests currently use the funtionality, and if we
2770 // ever decide to implement that it should be done by preventing
2771 // such cookies from being set.
2772 if (domain_.empty() || domain_[0] != '.')
2773 return false;
2775 // The host with a "." prefixed.
2776 if (domain_.compare(1, std::string::npos, host) == 0)
2777 return true;
2779 // A pure suffix of the host (ok since we know the domain already
2780 // starts with a ".")
2781 return (host.length() > domain_.length() &&
2782 host.compare(host.length() - domain_.length(),
2783 domain_.length(), domain_) == 0);
2786 std::string CookieMonster::CanonicalCookie::DebugString() const {
2787 return base::StringPrintf(
2788 "name: %s value: %s domain: %s path: %s creation: %"
2789 PRId64,
2790 name_.c_str(), value_.c_str(),
2791 domain_.c_str(), path_.c_str(),
2792 static_cast<int64>(creation_date_.ToTimeT()));
2795 } // namespace