cookie_monster.cc revision 4e180b6a0b4720a9b8e9e959a882386f690f08ff
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.
4
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
9 *
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/
14 *
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.
19 *
20 * The Original Code is mozilla.org code.
21 *
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.
26 *
27 * Contributor(s):
28 *   Daniel Witte (dwitte@stanford.edu)
29 *   Michiel van Leeuwen (mvl@exedo.nl)
30 *
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.
42 *
43 * ***** END LICENSE BLOCK ***** */
44
45#include "net/cookies/cookie_monster.h"
46
47#include <algorithm>
48#include <functional>
49#include <set>
50
51#include "base/basictypes.h"
52#include "base/bind.h"
53#include "base/callback.h"
54#include "base/logging.h"
55#include "base/memory/scoped_ptr.h"
56#include "base/message_loop/message_loop.h"
57#include "base/message_loop/message_loop_proxy.h"
58#include "base/metrics/histogram.h"
59#include "base/strings/string_util.h"
60#include "base/strings/stringprintf.h"
61#include "net/base/registry_controlled_domains/registry_controlled_domain.h"
62#include "net/cookies/canonical_cookie.h"
63#include "net/cookies/cookie_util.h"
64#include "net/cookies/parsed_cookie.h"
65#include "url/gurl.h"
66
67using base::Time;
68using base::TimeDelta;
69using base::TimeTicks;
70
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::tasks_pending_ and running it when notification
75// of cookie load completion is received via CookieMonster::OnLoaded. This
76// callback is passed to the persistent store from CookieMonster::InitStore(),
77// which is called on the first operation invoked on the CookieMonster.
78//
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
84// CookieMonster::tasks_pending_for_key_ and executed upon receiving
85// notification of key load completion via CookieMonster::OnKeyLoaded(). If
86// multiple requests for the same eTLD+1 are received before key load
87// completion, only the first request calls
88// PersistentCookieStore::LoadCookiesForKey, all subsequent requests are queued
89// in CookieMonster::tasks_pending_for_key_ and executed upon receiving
90// notification of key load completion triggered by the first request for the
91// same eTLD+1.
92
93static const int kMinutesInTenYears = 10 * 365 * 24 * 60;
94
95namespace net {
96
97// See comments at declaration of these variables in cookie_monster.h
98// for details.
99const size_t CookieMonster::kDomainMaxCookies           = 180;
100const size_t CookieMonster::kDomainPurgeCookies         = 30;
101const size_t CookieMonster::kMaxCookies                 = 3300;
102const size_t CookieMonster::kPurgeCookies               = 300;
103
104const size_t CookieMonster::kDomainCookiesQuotaLow    = 30;
105const size_t CookieMonster::kDomainCookiesQuotaMedium = 50;
106const size_t CookieMonster::kDomainCookiesQuotaHigh   =
107    kDomainMaxCookies - kDomainPurgeCookies
108    - kDomainCookiesQuotaLow - kDomainCookiesQuotaMedium;
109
110const int CookieMonster::kSafeFromGlobalPurgeDays       = 30;
111
112namespace {
113
114bool ContainsControlCharacter(const std::string& s) {
115  for (std::string::const_iterator i = s.begin(); i != s.end(); ++i) {
116    if ((*i >= 0) && (*i <= 31))
117      return true;
118  }
119
120  return false;
121}
122
123typedef std::vector<CanonicalCookie*> CanonicalCookieVector;
124
125// Default minimum delay after updating a cookie's LastAccessDate before we
126// will update it again.
127const int kDefaultAccessUpdateThresholdSeconds = 60;
128
129// Comparator to sort cookies from highest creation date to lowest
130// creation date.
131struct OrderByCreationTimeDesc {
132  bool operator()(const CookieMonster::CookieMap::iterator& a,
133                  const CookieMonster::CookieMap::iterator& b) const {
134    return a->second->CreationDate() > b->second->CreationDate();
135  }
136};
137
138// Constants for use in VLOG
139const int kVlogPerCookieMonster = 1;
140const int kVlogPeriodic = 3;
141const int kVlogGarbageCollection = 5;
142const int kVlogSetCookies = 7;
143const int kVlogGetCookies = 9;
144
145// Mozilla sorts on the path length (longest first), and then it
146// sorts by creation time (oldest first).
147// The RFC says the sort order for the domain attribute is undefined.
148bool CookieSorter(CanonicalCookie* cc1, CanonicalCookie* cc2) {
149  if (cc1->Path().length() == cc2->Path().length())
150    return cc1->CreationDate() < cc2->CreationDate();
151  return cc1->Path().length() > cc2->Path().length();
152}
153
154bool LRACookieSorter(const CookieMonster::CookieMap::iterator& it1,
155                     const CookieMonster::CookieMap::iterator& it2) {
156  // Cookies accessed less recently should be deleted first.
157  if (it1->second->LastAccessDate() != it2->second->LastAccessDate())
158    return it1->second->LastAccessDate() < it2->second->LastAccessDate();
159
160  // In rare cases we might have two cookies with identical last access times.
161  // To preserve the stability of the sort, in these cases prefer to delete
162  // older cookies over newer ones.  CreationDate() is guaranteed to be unique.
163  return it1->second->CreationDate() < it2->second->CreationDate();
164}
165
166// Our strategy to find duplicates is:
167// (1) Build a map from (cookiename, cookiepath) to
168//     {list of cookies with this signature, sorted by creation time}.
169// (2) For each list with more than 1 entry, keep the cookie having the
170//     most recent creation time, and delete the others.
171//
172// Two cookies are considered equivalent if they have the same domain,
173// name, and path.
174struct CookieSignature {
175 public:
176  CookieSignature(const std::string& name,
177                  const std::string& domain,
178                  const std::string& path)
179      : name(name), domain(domain), path(path) {
180  }
181
182  // To be a key for a map this class needs to be assignable, copyable,
183  // and have an operator<.  The default assignment operator
184  // and copy constructor are exactly what we want.
185
186  bool operator<(const CookieSignature& cs) const {
187    // Name compare dominates, then domain, then path.
188    int diff = name.compare(cs.name);
189    if (diff != 0)
190      return diff < 0;
191
192    diff = domain.compare(cs.domain);
193    if (diff != 0)
194      return diff < 0;
195
196    return path.compare(cs.path) < 0;
197  }
198
199  std::string name;
200  std::string domain;
201  std::string path;
202};
203
204// Determine the cookie domain to use for setting the specified cookie.
205bool GetCookieDomain(const GURL& url,
206                     const ParsedCookie& pc,
207                     std::string* result) {
208  std::string domain_string;
209  if (pc.HasDomain())
210    domain_string = pc.Domain();
211  return cookie_util::GetCookieDomainWithString(url, domain_string, result);
212}
213
214// For a CookieItVector iterator range [|it_begin|, |it_end|),
215// sorts the first |num_sort| + 1 elements by LastAccessDate().
216// The + 1 element exists so for any interval of length <= |num_sort| starting
217// from |cookies_its_begin|, a LastAccessDate() bound can be found.
218void SortLeastRecentlyAccessed(
219    CookieMonster::CookieItVector::iterator it_begin,
220    CookieMonster::CookieItVector::iterator it_end,
221    size_t num_sort) {
222  DCHECK_LT(static_cast<int>(num_sort), it_end - it_begin);
223  std::partial_sort(it_begin, it_begin + num_sort + 1, it_end, LRACookieSorter);
224}
225
226// Predicate to support PartitionCookieByPriority().
227struct CookiePriorityEqualsTo
228    : std::unary_function<const CookieMonster::CookieMap::iterator, bool> {
229  CookiePriorityEqualsTo(CookiePriority priority)
230    : priority_(priority) {}
231
232  bool operator()(const CookieMonster::CookieMap::iterator it) const {
233    return it->second->Priority() == priority_;
234  }
235
236  const CookiePriority priority_;
237};
238
239// For a CookieItVector iterator range [|it_begin|, |it_end|),
240// moves all cookies with a given |priority| to the beginning of the list.
241// Returns: An iterator in [it_begin, it_end) to the first element with
242// priority != |priority|, or |it_end| if all have priority == |priority|.
243CookieMonster::CookieItVector::iterator PartitionCookieByPriority(
244    CookieMonster::CookieItVector::iterator it_begin,
245    CookieMonster::CookieItVector::iterator it_end,
246    CookiePriority priority) {
247  return std::partition(it_begin, it_end, CookiePriorityEqualsTo(priority));
248}
249
250bool LowerBoundAccessDateComparator(
251  const CookieMonster::CookieMap::iterator it, const Time& access_date) {
252  return it->second->LastAccessDate() < access_date;
253}
254
255// For a CookieItVector iterator range [|it_begin|, |it_end|)
256// from a CookieItVector sorted by LastAccessDate(), returns the
257// first iterator with access date >= |access_date|, or cookie_its_end if this
258// holds for all.
259CookieMonster::CookieItVector::iterator LowerBoundAccessDate(
260    const CookieMonster::CookieItVector::iterator its_begin,
261    const CookieMonster::CookieItVector::iterator its_end,
262    const Time& access_date) {
263  return std::lower_bound(its_begin, its_end, access_date,
264                          LowerBoundAccessDateComparator);
265}
266
267// Mapping between DeletionCause and Delegate::ChangeCause; the mapping also
268// provides a boolean that specifies whether or not an OnCookieChanged
269// notification ought to be generated.
270typedef struct ChangeCausePair_struct {
271  CookieMonster::Delegate::ChangeCause cause;
272  bool notify;
273} ChangeCausePair;
274ChangeCausePair ChangeCauseMapping[] = {
275  // DELETE_COOKIE_EXPLICIT
276  { CookieMonster::Delegate::CHANGE_COOKIE_EXPLICIT, true },
277  // DELETE_COOKIE_OVERWRITE
278  { CookieMonster::Delegate::CHANGE_COOKIE_OVERWRITE, true },
279  // DELETE_COOKIE_EXPIRED
280  { CookieMonster::Delegate::CHANGE_COOKIE_EXPIRED, true },
281  // DELETE_COOKIE_EVICTED
282  { CookieMonster::Delegate::CHANGE_COOKIE_EVICTED, true },
283  // DELETE_COOKIE_DUPLICATE_IN_BACKING_STORE
284  { CookieMonster::Delegate::CHANGE_COOKIE_EXPLICIT, false },
285  // DELETE_COOKIE_DONT_RECORD
286  { CookieMonster::Delegate::CHANGE_COOKIE_EXPLICIT, false },
287  // DELETE_COOKIE_EVICTED_DOMAIN
288  { CookieMonster::Delegate::CHANGE_COOKIE_EVICTED, true },
289  // DELETE_COOKIE_EVICTED_GLOBAL
290  { CookieMonster::Delegate::CHANGE_COOKIE_EVICTED, true },
291  // DELETE_COOKIE_EVICTED_DOMAIN_PRE_SAFE
292  { CookieMonster::Delegate::CHANGE_COOKIE_EVICTED, true },
293  // DELETE_COOKIE_EVICTED_DOMAIN_POST_SAFE
294  { CookieMonster::Delegate::CHANGE_COOKIE_EVICTED, true },
295  // DELETE_COOKIE_EXPIRED_OVERWRITE
296  { CookieMonster::Delegate::CHANGE_COOKIE_EXPIRED_OVERWRITE, true },
297  // DELETE_COOKIE_CONTROL_CHAR
298  { CookieMonster::Delegate::CHANGE_COOKIE_EVICTED, true},
299  // DELETE_COOKIE_LAST_ENTRY
300  { CookieMonster::Delegate::CHANGE_COOKIE_EXPLICIT, false }
301};
302
303std::string BuildCookieLine(const CanonicalCookieVector& cookies) {
304  std::string cookie_line;
305  for (CanonicalCookieVector::const_iterator it = cookies.begin();
306       it != cookies.end(); ++it) {
307    if (it != cookies.begin())
308      cookie_line += "; ";
309    // In Mozilla if you set a cookie like AAAA, it will have an empty token
310    // and a value of AAAA.  When it sends the cookie back, it will send AAAA,
311    // so we need to avoid sending =AAAA for a blank token value.
312    if (!(*it)->Name().empty())
313      cookie_line += (*it)->Name() + "=";
314    cookie_line += (*it)->Value();
315  }
316  return cookie_line;
317}
318
319}  // namespace
320
321// static
322bool CookieMonster::default_enable_file_scheme_ = false;
323
324CookieMonster::CookieMonster(PersistentCookieStore* store, Delegate* delegate)
325    : initialized_(false),
326      loaded_(false),
327      store_(store),
328      last_access_threshold_(
329          TimeDelta::FromSeconds(kDefaultAccessUpdateThresholdSeconds)),
330      delegate_(delegate),
331      last_statistic_record_time_(Time::Now()),
332      keep_expired_cookies_(false),
333      persist_session_cookies_(false),
334      priority_aware_garbage_collection_(false) {
335  InitializeHistograms();
336  SetDefaultCookieableSchemes();
337}
338
339CookieMonster::CookieMonster(PersistentCookieStore* store,
340                             Delegate* delegate,
341                             int last_access_threshold_milliseconds)
342    : initialized_(false),
343      loaded_(false),
344      store_(store),
345      last_access_threshold_(base::TimeDelta::FromMilliseconds(
346          last_access_threshold_milliseconds)),
347      delegate_(delegate),
348      last_statistic_record_time_(base::Time::Now()),
349      keep_expired_cookies_(false),
350      persist_session_cookies_(false),
351      priority_aware_garbage_collection_(false) {
352  InitializeHistograms();
353  SetDefaultCookieableSchemes();
354}
355
356
357// Task classes for queueing the coming request.
358
359class CookieMonster::CookieMonsterTask
360    : public base::RefCountedThreadSafe<CookieMonsterTask> {
361 public:
362  // Runs the task and invokes the client callback on the thread that
363  // originally constructed the task.
364  virtual void Run() = 0;
365
366 protected:
367  explicit CookieMonsterTask(CookieMonster* cookie_monster);
368  virtual ~CookieMonsterTask();
369
370  // Invokes the callback immediately, if the current thread is the one
371  // that originated the task, or queues the callback for execution on the
372  // appropriate thread. Maintains a reference to this CookieMonsterTask
373  // instance until the callback completes.
374  void InvokeCallback(base::Closure callback);
375
376  CookieMonster* cookie_monster() {
377    return cookie_monster_;
378  }
379
380 private:
381  friend class base::RefCountedThreadSafe<CookieMonsterTask>;
382
383  CookieMonster* cookie_monster_;
384  scoped_refptr<base::MessageLoopProxy> thread_;
385
386  DISALLOW_COPY_AND_ASSIGN(CookieMonsterTask);
387};
388
389CookieMonster::CookieMonsterTask::CookieMonsterTask(
390    CookieMonster* cookie_monster)
391    : cookie_monster_(cookie_monster),
392      thread_(base::MessageLoopProxy::current()) {
393}
394
395CookieMonster::CookieMonsterTask::~CookieMonsterTask() {}
396
397// Unfortunately, one cannot re-bind a Callback with parameters into a closure.
398// Therefore, the closure passed to InvokeCallback is a clumsy binding of
399// Callback::Run on a wrapped Callback instance. Since Callback is not
400// reference counted, we bind to an instance that is a member of the
401// CookieMonsterTask subclass. Then, we cannot simply post the callback to a
402// message loop because the underlying instance may be destroyed (along with the
403// CookieMonsterTask instance) in the interim. Therefore, we post a callback
404// bound to the CookieMonsterTask, which *is* reference counted (thus preventing
405// destruction of the original callback), and which invokes the closure (which
406// invokes the original callback with the returned data).
407void CookieMonster::CookieMonsterTask::InvokeCallback(base::Closure callback) {
408  if (thread_->BelongsToCurrentThread()) {
409    callback.Run();
410  } else {
411    thread_->PostTask(FROM_HERE, base::Bind(
412        &CookieMonsterTask::InvokeCallback, this, callback));
413  }
414}
415
416// Task class for SetCookieWithDetails call.
417class CookieMonster::SetCookieWithDetailsTask : public CookieMonsterTask {
418 public:
419  SetCookieWithDetailsTask(CookieMonster* cookie_monster,
420                           const GURL& url,
421                           const std::string& name,
422                           const std::string& value,
423                           const std::string& domain,
424                           const std::string& path,
425                           const base::Time& expiration_time,
426                           bool secure,
427                           bool http_only,
428                           CookiePriority priority,
429                           const SetCookiesCallback& callback)
430      : CookieMonsterTask(cookie_monster),
431        url_(url),
432        name_(name),
433        value_(value),
434        domain_(domain),
435        path_(path),
436        expiration_time_(expiration_time),
437        secure_(secure),
438        http_only_(http_only),
439        priority_(priority),
440        callback_(callback) {
441  }
442
443  // CookieMonsterTask:
444  virtual void Run() OVERRIDE;
445
446 protected:
447  virtual ~SetCookieWithDetailsTask() {}
448
449 private:
450  GURL url_;
451  std::string name_;
452  std::string value_;
453  std::string domain_;
454  std::string path_;
455  base::Time expiration_time_;
456  bool secure_;
457  bool http_only_;
458  CookiePriority priority_;
459  SetCookiesCallback callback_;
460
461  DISALLOW_COPY_AND_ASSIGN(SetCookieWithDetailsTask);
462};
463
464void CookieMonster::SetCookieWithDetailsTask::Run() {
465  bool success = this->cookie_monster()->
466      SetCookieWithDetails(url_, name_, value_, domain_, path_,
467                           expiration_time_, secure_, http_only_, priority_);
468  if (!callback_.is_null()) {
469    this->InvokeCallback(base::Bind(&SetCookiesCallback::Run,
470                                    base::Unretained(&callback_), success));
471  }
472}
473
474// Task class for GetAllCookies call.
475class CookieMonster::GetAllCookiesTask : public CookieMonsterTask {
476 public:
477  GetAllCookiesTask(CookieMonster* cookie_monster,
478                    const GetCookieListCallback& callback)
479      : CookieMonsterTask(cookie_monster),
480        callback_(callback) {
481  }
482
483  // CookieMonsterTask
484  virtual void Run() OVERRIDE;
485
486 protected:
487  virtual ~GetAllCookiesTask() {}
488
489 private:
490  GetCookieListCallback callback_;
491
492  DISALLOW_COPY_AND_ASSIGN(GetAllCookiesTask);
493};
494
495void CookieMonster::GetAllCookiesTask::Run() {
496  if (!callback_.is_null()) {
497    CookieList cookies = this->cookie_monster()->GetAllCookies();
498    this->InvokeCallback(base::Bind(&GetCookieListCallback::Run,
499                                    base::Unretained(&callback_), cookies));
500    }
501}
502
503// Task class for GetAllCookiesForURLWithOptions call.
504class CookieMonster::GetAllCookiesForURLWithOptionsTask
505    : public CookieMonsterTask {
506 public:
507  GetAllCookiesForURLWithOptionsTask(
508      CookieMonster* cookie_monster,
509      const GURL& url,
510      const CookieOptions& options,
511      const GetCookieListCallback& callback)
512      : CookieMonsterTask(cookie_monster),
513        url_(url),
514        options_(options),
515        callback_(callback) {
516  }
517
518  // CookieMonsterTask:
519  virtual void Run() OVERRIDE;
520
521 protected:
522  virtual ~GetAllCookiesForURLWithOptionsTask() {}
523
524 private:
525  GURL url_;
526  CookieOptions options_;
527  GetCookieListCallback callback_;
528
529  DISALLOW_COPY_AND_ASSIGN(GetAllCookiesForURLWithOptionsTask);
530};
531
532void CookieMonster::GetAllCookiesForURLWithOptionsTask::Run() {
533  if (!callback_.is_null()) {
534    CookieList cookies = this->cookie_monster()->
535        GetAllCookiesForURLWithOptions(url_, options_);
536    this->InvokeCallback(base::Bind(&GetCookieListCallback::Run,
537                                    base::Unretained(&callback_), cookies));
538  }
539}
540
541template <typename Result> struct CallbackType {
542  typedef base::Callback<void(Result)> Type;
543};
544
545template <> struct CallbackType<void> {
546  typedef base::Closure Type;
547};
548
549// Base task class for Delete*Task.
550template <typename Result>
551class CookieMonster::DeleteTask : public CookieMonsterTask {
552 public:
553  DeleteTask(CookieMonster* cookie_monster,
554             const typename CallbackType<Result>::Type& callback)
555      : CookieMonsterTask(cookie_monster),
556        callback_(callback) {
557  }
558
559  // CookieMonsterTask:
560  virtual void Run() OVERRIDE;
561
562 private:
563  // Runs the delete task and returns a result.
564  virtual Result RunDeleteTask() = 0;
565  base::Closure RunDeleteTaskAndBindCallback();
566  void FlushDone(const base::Closure& callback);
567
568  typename CallbackType<Result>::Type callback_;
569
570  DISALLOW_COPY_AND_ASSIGN(DeleteTask);
571};
572
573template <typename Result>
574base::Closure CookieMonster::DeleteTask<Result>::
575RunDeleteTaskAndBindCallback() {
576  Result result = RunDeleteTask();
577  if (callback_.is_null())
578    return base::Closure();
579  return base::Bind(callback_, result);
580}
581
582template <>
583base::Closure CookieMonster::DeleteTask<void>::RunDeleteTaskAndBindCallback() {
584  RunDeleteTask();
585  return callback_;
586}
587
588template <typename Result>
589void CookieMonster::DeleteTask<Result>::Run() {
590  this->cookie_monster()->FlushStore(
591      base::Bind(&DeleteTask<Result>::FlushDone, this,
592                 RunDeleteTaskAndBindCallback()));
593}
594
595template <typename Result>
596void CookieMonster::DeleteTask<Result>::FlushDone(
597    const base::Closure& callback) {
598  if (!callback.is_null()) {
599    this->InvokeCallback(callback);
600  }
601}
602
603// Task class for DeleteAll call.
604class CookieMonster::DeleteAllTask : public DeleteTask<int> {
605 public:
606  DeleteAllTask(CookieMonster* cookie_monster,
607                const DeleteCallback& callback)
608      : DeleteTask(cookie_monster, callback) {
609  }
610
611  // DeleteTask:
612  virtual int RunDeleteTask() OVERRIDE;
613
614 protected:
615  virtual ~DeleteAllTask() {}
616
617 private:
618  DISALLOW_COPY_AND_ASSIGN(DeleteAllTask);
619};
620
621int CookieMonster::DeleteAllTask::RunDeleteTask() {
622  return this->cookie_monster()->DeleteAll(true);
623}
624
625// Task class for DeleteAllCreatedBetween call.
626class CookieMonster::DeleteAllCreatedBetweenTask : public DeleteTask<int> {
627 public:
628  DeleteAllCreatedBetweenTask(CookieMonster* cookie_monster,
629                              const Time& delete_begin,
630                              const Time& delete_end,
631                              const DeleteCallback& callback)
632      : DeleteTask(cookie_monster, callback),
633        delete_begin_(delete_begin),
634        delete_end_(delete_end) {
635  }
636
637  // DeleteTask:
638  virtual int RunDeleteTask() OVERRIDE;
639
640 protected:
641  virtual ~DeleteAllCreatedBetweenTask() {}
642
643 private:
644  Time delete_begin_;
645  Time delete_end_;
646
647  DISALLOW_COPY_AND_ASSIGN(DeleteAllCreatedBetweenTask);
648};
649
650int CookieMonster::DeleteAllCreatedBetweenTask::RunDeleteTask() {
651  return this->cookie_monster()->
652      DeleteAllCreatedBetween(delete_begin_, delete_end_);
653}
654
655// Task class for DeleteAllForHost call.
656class CookieMonster::DeleteAllForHostTask : public DeleteTask<int> {
657 public:
658  DeleteAllForHostTask(CookieMonster* cookie_monster,
659                       const GURL& url,
660                       const DeleteCallback& callback)
661      : DeleteTask(cookie_monster, callback),
662        url_(url) {
663  }
664
665  // DeleteTask:
666  virtual int RunDeleteTask() OVERRIDE;
667
668 protected:
669  virtual ~DeleteAllForHostTask() {}
670
671 private:
672  GURL url_;
673
674  DISALLOW_COPY_AND_ASSIGN(DeleteAllForHostTask);
675};
676
677int CookieMonster::DeleteAllForHostTask::RunDeleteTask() {
678  return this->cookie_monster()->DeleteAllForHost(url_);
679}
680
681// Task class for DeleteAllCreatedBetweenForHost call.
682class CookieMonster::DeleteAllCreatedBetweenForHostTask
683    : public DeleteTask<int> {
684 public:
685  DeleteAllCreatedBetweenForHostTask(
686      CookieMonster* cookie_monster,
687      Time delete_begin,
688      Time delete_end,
689      const GURL& url,
690      const DeleteCallback& callback)
691      : DeleteTask(cookie_monster, callback),
692        delete_begin_(delete_begin),
693        delete_end_(delete_end),
694        url_(url) {
695  }
696
697  // DeleteTask:
698  virtual int RunDeleteTask() OVERRIDE;
699
700 protected:
701  virtual ~DeleteAllCreatedBetweenForHostTask() {}
702
703 private:
704  Time delete_begin_;
705  Time delete_end_;
706  GURL url_;
707
708  DISALLOW_COPY_AND_ASSIGN(DeleteAllCreatedBetweenForHostTask);
709};
710
711int CookieMonster::DeleteAllCreatedBetweenForHostTask::RunDeleteTask() {
712  return this->cookie_monster()->DeleteAllCreatedBetweenForHost(
713      delete_begin_, delete_end_, url_);
714}
715
716// Task class for DeleteCanonicalCookie call.
717class CookieMonster::DeleteCanonicalCookieTask : public DeleteTask<bool> {
718 public:
719  DeleteCanonicalCookieTask(CookieMonster* cookie_monster,
720                            const CanonicalCookie& cookie,
721                            const DeleteCookieCallback& callback)
722      : DeleteTask(cookie_monster, callback),
723        cookie_(cookie) {
724  }
725
726  // DeleteTask:
727  virtual bool RunDeleteTask() OVERRIDE;
728
729 protected:
730  virtual ~DeleteCanonicalCookieTask() {}
731
732 private:
733  CanonicalCookie cookie_;
734
735  DISALLOW_COPY_AND_ASSIGN(DeleteCanonicalCookieTask);
736};
737
738bool CookieMonster::DeleteCanonicalCookieTask::RunDeleteTask() {
739  return this->cookie_monster()->DeleteCanonicalCookie(cookie_);
740}
741
742// Task class for SetCookieWithOptions call.
743class CookieMonster::SetCookieWithOptionsTask : public CookieMonsterTask {
744 public:
745  SetCookieWithOptionsTask(CookieMonster* cookie_monster,
746                           const GURL& url,
747                           const std::string& cookie_line,
748                           const CookieOptions& options,
749                           const SetCookiesCallback& callback)
750      : CookieMonsterTask(cookie_monster),
751        url_(url),
752        cookie_line_(cookie_line),
753        options_(options),
754        callback_(callback) {
755  }
756
757  // CookieMonsterTask:
758  virtual void Run() OVERRIDE;
759
760 protected:
761  virtual ~SetCookieWithOptionsTask() {}
762
763 private:
764  GURL url_;
765  std::string cookie_line_;
766  CookieOptions options_;
767  SetCookiesCallback callback_;
768
769  DISALLOW_COPY_AND_ASSIGN(SetCookieWithOptionsTask);
770};
771
772void CookieMonster::SetCookieWithOptionsTask::Run() {
773  bool result = this->cookie_monster()->
774      SetCookieWithOptions(url_, cookie_line_, options_);
775  if (!callback_.is_null()) {
776    this->InvokeCallback(base::Bind(&SetCookiesCallback::Run,
777                                    base::Unretained(&callback_), result));
778  }
779}
780
781// Task class for GetCookiesWithOptions call.
782class CookieMonster::GetCookiesWithOptionsTask : public CookieMonsterTask {
783 public:
784  GetCookiesWithOptionsTask(CookieMonster* cookie_monster,
785                            const GURL& url,
786                            const CookieOptions& options,
787                            const GetCookiesCallback& callback)
788      : CookieMonsterTask(cookie_monster),
789        url_(url),
790        options_(options),
791        callback_(callback) {
792  }
793
794  // CookieMonsterTask:
795  virtual void Run() OVERRIDE;
796
797 protected:
798  virtual ~GetCookiesWithOptionsTask() {}
799
800 private:
801  GURL url_;
802  CookieOptions options_;
803  GetCookiesCallback callback_;
804
805  DISALLOW_COPY_AND_ASSIGN(GetCookiesWithOptionsTask);
806};
807
808void CookieMonster::GetCookiesWithOptionsTask::Run() {
809  std::string cookie = this->cookie_monster()->
810      GetCookiesWithOptions(url_, options_);
811  if (!callback_.is_null()) {
812    this->InvokeCallback(base::Bind(&GetCookiesCallback::Run,
813                                    base::Unretained(&callback_), cookie));
814  }
815}
816
817// Task class for DeleteCookie call.
818class CookieMonster::DeleteCookieTask : public DeleteTask<void> {
819 public:
820  DeleteCookieTask(CookieMonster* cookie_monster,
821                   const GURL& url,
822                   const std::string& cookie_name,
823                   const base::Closure& callback)
824      : DeleteTask(cookie_monster, callback),
825        url_(url),
826        cookie_name_(cookie_name) {
827  }
828
829  // DeleteTask:
830  virtual void RunDeleteTask() OVERRIDE;
831
832 protected:
833  virtual ~DeleteCookieTask() {}
834
835 private:
836  GURL url_;
837  std::string cookie_name_;
838
839  DISALLOW_COPY_AND_ASSIGN(DeleteCookieTask);
840};
841
842void CookieMonster::DeleteCookieTask::RunDeleteTask() {
843  this->cookie_monster()->DeleteCookie(url_, cookie_name_);
844}
845
846// Task class for DeleteSessionCookies call.
847class CookieMonster::DeleteSessionCookiesTask : public DeleteTask<int> {
848 public:
849  DeleteSessionCookiesTask(CookieMonster* cookie_monster,
850                           const DeleteCallback& callback)
851      : DeleteTask(cookie_monster, callback) {
852  }
853
854  // DeleteTask:
855  virtual int RunDeleteTask() OVERRIDE;
856
857 protected:
858  virtual ~DeleteSessionCookiesTask() {}
859
860 private:
861
862  DISALLOW_COPY_AND_ASSIGN(DeleteSessionCookiesTask);
863};
864
865int CookieMonster::DeleteSessionCookiesTask::RunDeleteTask() {
866  return this->cookie_monster()->DeleteSessionCookies();
867}
868
869// Task class for HasCookiesForETLDP1Task call.
870class CookieMonster::HasCookiesForETLDP1Task : public CookieMonsterTask {
871 public:
872  HasCookiesForETLDP1Task(
873      CookieMonster* cookie_monster,
874      const std::string& etldp1,
875      const HasCookiesForETLDP1Callback& callback)
876      : CookieMonsterTask(cookie_monster),
877        etldp1_(etldp1),
878        callback_(callback) {
879  }
880
881  // CookieMonsterTask:
882  virtual void Run() OVERRIDE;
883
884 protected:
885  virtual ~HasCookiesForETLDP1Task() {}
886
887 private:
888  std::string etldp1_;
889  HasCookiesForETLDP1Callback callback_;
890
891  DISALLOW_COPY_AND_ASSIGN(HasCookiesForETLDP1Task);
892};
893
894void CookieMonster::HasCookiesForETLDP1Task::Run() {
895  bool result = this->cookie_monster()->HasCookiesForETLDP1(etldp1_);
896  if (!callback_.is_null()) {
897    this->InvokeCallback(
898        base::Bind(&HasCookiesForETLDP1Callback::Run,
899                   base::Unretained(&callback_), result));
900  }
901}
902
903// Asynchronous CookieMonster API
904
905void CookieMonster::SetCookieWithDetailsAsync(
906    const GURL& url,
907    const std::string& name,
908    const std::string& value,
909    const std::string& domain,
910    const std::string& path,
911    const Time& expiration_time,
912    bool secure,
913    bool http_only,
914    CookiePriority priority,
915    const SetCookiesCallback& callback) {
916  scoped_refptr<SetCookieWithDetailsTask> task =
917      new SetCookieWithDetailsTask(this, url, name, value, domain, path,
918                                   expiration_time, secure, http_only, priority,
919                                   callback);
920
921  DoCookieTaskForURL(task, url);
922}
923
924void CookieMonster::GetAllCookiesAsync(const GetCookieListCallback& callback) {
925  scoped_refptr<GetAllCookiesTask> task =
926      new GetAllCookiesTask(this, callback);
927
928  DoCookieTask(task);
929}
930
931
932void CookieMonster::GetAllCookiesForURLWithOptionsAsync(
933    const GURL& url,
934    const CookieOptions& options,
935    const GetCookieListCallback& callback) {
936  scoped_refptr<GetAllCookiesForURLWithOptionsTask> task =
937      new GetAllCookiesForURLWithOptionsTask(this, url, options, callback);
938
939  DoCookieTaskForURL(task, url);
940}
941
942void CookieMonster::GetAllCookiesForURLAsync(
943    const GURL& url, const GetCookieListCallback& callback) {
944  CookieOptions options;
945  options.set_include_httponly();
946  scoped_refptr<GetAllCookiesForURLWithOptionsTask> task =
947      new GetAllCookiesForURLWithOptionsTask(this, url, options, callback);
948
949  DoCookieTaskForURL(task, url);
950}
951
952void CookieMonster::HasCookiesForETLDP1Async(
953    const std::string& etldp1,
954    const HasCookiesForETLDP1Callback& callback) {
955  scoped_refptr<HasCookiesForETLDP1Task> task =
956      new HasCookiesForETLDP1Task(this, etldp1, callback);
957
958  DoCookieTaskForURL(task, GURL("http://" + etldp1));
959}
960
961void CookieMonster::DeleteAllAsync(const DeleteCallback& callback) {
962  scoped_refptr<DeleteAllTask> task =
963      new DeleteAllTask(this, callback);
964
965  DoCookieTask(task);
966}
967
968void CookieMonster::DeleteAllCreatedBetweenAsync(
969    const Time& delete_begin, const Time& delete_end,
970    const DeleteCallback& callback) {
971  scoped_refptr<DeleteAllCreatedBetweenTask> task =
972      new DeleteAllCreatedBetweenTask(this, delete_begin, delete_end,
973                                      callback);
974
975  DoCookieTask(task);
976}
977
978void CookieMonster::DeleteAllCreatedBetweenForHostAsync(
979    const Time delete_begin,
980    const Time delete_end,
981    const GURL& url,
982    const DeleteCallback& callback) {
983  scoped_refptr<DeleteAllCreatedBetweenForHostTask> task =
984      new DeleteAllCreatedBetweenForHostTask(
985          this, delete_begin, delete_end, url, callback);
986
987  DoCookieTaskForURL(task, url);
988}
989
990void CookieMonster::DeleteAllForHostAsync(
991    const GURL& url, const DeleteCallback& callback) {
992  scoped_refptr<DeleteAllForHostTask> task =
993      new DeleteAllForHostTask(this, url, callback);
994
995  DoCookieTaskForURL(task, url);
996}
997
998void CookieMonster::DeleteCanonicalCookieAsync(
999    const CanonicalCookie& cookie,
1000    const DeleteCookieCallback& callback) {
1001  scoped_refptr<DeleteCanonicalCookieTask> task =
1002      new DeleteCanonicalCookieTask(this, cookie, callback);
1003
1004  DoCookieTask(task);
1005}
1006
1007void CookieMonster::SetCookieWithOptionsAsync(
1008    const GURL& url,
1009    const std::string& cookie_line,
1010    const CookieOptions& options,
1011    const SetCookiesCallback& callback) {
1012  scoped_refptr<SetCookieWithOptionsTask> task =
1013      new SetCookieWithOptionsTask(this, url, cookie_line, options, callback);
1014
1015  DoCookieTaskForURL(task, url);
1016}
1017
1018void CookieMonster::GetCookiesWithOptionsAsync(
1019    const GURL& url,
1020    const CookieOptions& options,
1021    const GetCookiesCallback& callback) {
1022  scoped_refptr<GetCookiesWithOptionsTask> task =
1023      new GetCookiesWithOptionsTask(this, url, options, callback);
1024
1025  DoCookieTaskForURL(task, url);
1026}
1027
1028void CookieMonster::DeleteCookieAsync(const GURL& url,
1029                                      const std::string& cookie_name,
1030                                      const base::Closure& callback) {
1031  scoped_refptr<DeleteCookieTask> task =
1032      new DeleteCookieTask(this, url, cookie_name, callback);
1033
1034  DoCookieTaskForURL(task, url);
1035}
1036
1037void CookieMonster::DeleteSessionCookiesAsync(
1038    const CookieStore::DeleteCallback& callback) {
1039  scoped_refptr<DeleteSessionCookiesTask> task =
1040      new DeleteSessionCookiesTask(this, callback);
1041
1042  DoCookieTask(task);
1043}
1044
1045void CookieMonster::DoCookieTask(
1046    const scoped_refptr<CookieMonsterTask>& task_item) {
1047  {
1048    base::AutoLock autolock(lock_);
1049    InitIfNecessary();
1050    if (!loaded_) {
1051      tasks_pending_.push(task_item);
1052      return;
1053    }
1054  }
1055
1056  task_item->Run();
1057}
1058
1059void CookieMonster::DoCookieTaskForURL(
1060    const scoped_refptr<CookieMonsterTask>& task_item,
1061    const GURL& url) {
1062  {
1063    base::AutoLock autolock(lock_);
1064    InitIfNecessary();
1065    // If cookies for the requested domain key (eTLD+1) have been loaded from DB
1066    // then run the task, otherwise load from DB.
1067    if (!loaded_) {
1068      // Checks if the domain key has been loaded.
1069      std::string key(cookie_util::GetEffectiveDomain(url.scheme(),
1070                                                       url.host()));
1071      if (keys_loaded_.find(key) == keys_loaded_.end()) {
1072        std::map<std::string, std::deque<scoped_refptr<CookieMonsterTask> > >
1073          ::iterator it = tasks_pending_for_key_.find(key);
1074        if (it == tasks_pending_for_key_.end()) {
1075          store_->LoadCookiesForKey(key,
1076            base::Bind(&CookieMonster::OnKeyLoaded, this, key));
1077          it = tasks_pending_for_key_.insert(std::make_pair(key,
1078            std::deque<scoped_refptr<CookieMonsterTask> >())).first;
1079        }
1080        it->second.push_back(task_item);
1081        return;
1082      }
1083    }
1084  }
1085  task_item->Run();
1086}
1087
1088bool CookieMonster::SetCookieWithDetails(const GURL& url,
1089                                         const std::string& name,
1090                                         const std::string& value,
1091                                         const std::string& domain,
1092                                         const std::string& path,
1093                                         const base::Time& expiration_time,
1094                                         bool secure,
1095                                         bool http_only,
1096                                         CookiePriority priority) {
1097  base::AutoLock autolock(lock_);
1098
1099  if (!HasCookieableScheme(url))
1100    return false;
1101
1102  Time creation_time = CurrentTime();
1103  last_time_seen_ = creation_time;
1104
1105  scoped_ptr<CanonicalCookie> cc;
1106  cc.reset(CanonicalCookie::Create(url, name, value, domain, path,
1107                                   creation_time, expiration_time,
1108                                   secure, http_only, priority));
1109
1110  if (!cc.get())
1111    return false;
1112
1113  CookieOptions options;
1114  options.set_include_httponly();
1115  return SetCanonicalCookie(&cc, creation_time, options);
1116}
1117
1118bool CookieMonster::InitializeFrom(const CookieList& list) {
1119  base::AutoLock autolock(lock_);
1120  InitIfNecessary();
1121  for (net::CookieList::const_iterator iter = list.begin();
1122           iter != list.end(); ++iter) {
1123    scoped_ptr<CanonicalCookie> cookie(new CanonicalCookie(*iter));
1124    net::CookieOptions options;
1125    options.set_include_httponly();
1126    if (!SetCanonicalCookie(&cookie, cookie->CreationDate(), options))
1127      return false;
1128  }
1129  return true;
1130}
1131
1132CookieList CookieMonster::GetAllCookies() {
1133  base::AutoLock autolock(lock_);
1134
1135  // This function is being called to scrape the cookie list for management UI
1136  // or similar.  We shouldn't show expired cookies in this list since it will
1137  // just be confusing to users, and this function is called rarely enough (and
1138  // is already slow enough) that it's OK to take the time to garbage collect
1139  // the expired cookies now.
1140  //
1141  // Note that this does not prune cookies to be below our limits (if we've
1142  // exceeded them) the way that calling GarbageCollect() would.
1143  GarbageCollectExpired(Time::Now(),
1144                        CookieMapItPair(cookies_.begin(), cookies_.end()),
1145                        NULL);
1146
1147  // Copy the CanonicalCookie pointers from the map so that we can use the same
1148  // sorter as elsewhere, then copy the result out.
1149  std::vector<CanonicalCookie*> cookie_ptrs;
1150  cookie_ptrs.reserve(cookies_.size());
1151  for (CookieMap::iterator it = cookies_.begin(); it != cookies_.end(); ++it)
1152    cookie_ptrs.push_back(it->second);
1153  std::sort(cookie_ptrs.begin(), cookie_ptrs.end(), CookieSorter);
1154
1155  CookieList cookie_list;
1156  cookie_list.reserve(cookie_ptrs.size());
1157  for (std::vector<CanonicalCookie*>::const_iterator it = cookie_ptrs.begin();
1158       it != cookie_ptrs.end(); ++it)
1159    cookie_list.push_back(**it);
1160
1161  return cookie_list;
1162}
1163
1164CookieList CookieMonster::GetAllCookiesForURLWithOptions(
1165    const GURL& url,
1166    const CookieOptions& options) {
1167  base::AutoLock autolock(lock_);
1168
1169  std::vector<CanonicalCookie*> cookie_ptrs;
1170  FindCookiesForHostAndDomain(url, options, false, &cookie_ptrs);
1171  std::sort(cookie_ptrs.begin(), cookie_ptrs.end(), CookieSorter);
1172
1173  CookieList cookies;
1174  for (std::vector<CanonicalCookie*>::const_iterator it = cookie_ptrs.begin();
1175       it != cookie_ptrs.end(); it++)
1176    cookies.push_back(**it);
1177
1178  return cookies;
1179}
1180
1181CookieList CookieMonster::GetAllCookiesForURL(const GURL& url) {
1182  CookieOptions options;
1183  options.set_include_httponly();
1184
1185  return GetAllCookiesForURLWithOptions(url, options);
1186}
1187
1188int CookieMonster::DeleteAll(bool sync_to_store) {
1189  base::AutoLock autolock(lock_);
1190
1191  int num_deleted = 0;
1192  for (CookieMap::iterator it = cookies_.begin(); it != cookies_.end();) {
1193    CookieMap::iterator curit = it;
1194    ++it;
1195    InternalDeleteCookie(curit, sync_to_store,
1196                         sync_to_store ? DELETE_COOKIE_EXPLICIT :
1197                             DELETE_COOKIE_DONT_RECORD /* Destruction. */);
1198    ++num_deleted;
1199  }
1200
1201  return num_deleted;
1202}
1203
1204int CookieMonster::DeleteAllCreatedBetween(const Time& delete_begin,
1205                                           const Time& delete_end) {
1206  base::AutoLock autolock(lock_);
1207
1208  int num_deleted = 0;
1209  for (CookieMap::iterator it = cookies_.begin(); it != cookies_.end();) {
1210    CookieMap::iterator curit = it;
1211    CanonicalCookie* cc = curit->second;
1212    ++it;
1213
1214    if (cc->CreationDate() >= delete_begin &&
1215        (delete_end.is_null() || cc->CreationDate() < delete_end)) {
1216      InternalDeleteCookie(curit,
1217                           true,  /*sync_to_store*/
1218                           DELETE_COOKIE_EXPLICIT);
1219      ++num_deleted;
1220    }
1221  }
1222
1223  return num_deleted;
1224}
1225
1226int CookieMonster::DeleteAllCreatedBetweenForHost(const Time delete_begin,
1227                                                  const Time delete_end,
1228                                                  const GURL& url) {
1229  base::AutoLock autolock(lock_);
1230
1231  if (!HasCookieableScheme(url))
1232    return 0;
1233
1234  const std::string host(url.host());
1235
1236  // We store host cookies in the store by their canonical host name;
1237  // domain cookies are stored with a leading ".".  So this is a pretty
1238  // simple lookup and per-cookie delete.
1239  int num_deleted = 0;
1240  for (CookieMapItPair its = cookies_.equal_range(GetKey(host));
1241       its.first != its.second;) {
1242    CookieMap::iterator curit = its.first;
1243    ++its.first;
1244
1245    const CanonicalCookie* const cc = curit->second;
1246
1247    // Delete only on a match as a host cookie.
1248    if (cc->IsHostCookie() && cc->IsDomainMatch(host) &&
1249        cc->CreationDate() >= delete_begin &&
1250        // The assumption that null |delete_end| is equivalent to
1251        // Time::Max() is confusing.
1252        (delete_end.is_null() || cc->CreationDate() < delete_end)) {
1253      num_deleted++;
1254
1255      InternalDeleteCookie(curit, true, DELETE_COOKIE_EXPLICIT);
1256    }
1257  }
1258  return num_deleted;
1259}
1260
1261int CookieMonster::DeleteAllForHost(const GURL& url) {
1262  return DeleteAllCreatedBetweenForHost(Time(), Time::Max(), url);
1263}
1264
1265
1266bool CookieMonster::DeleteCanonicalCookie(const CanonicalCookie& cookie) {
1267  base::AutoLock autolock(lock_);
1268
1269  for (CookieMapItPair its = cookies_.equal_range(GetKey(cookie.Domain()));
1270       its.first != its.second; ++its.first) {
1271    // The creation date acts as our unique index...
1272    if (its.first->second->CreationDate() == cookie.CreationDate()) {
1273      InternalDeleteCookie(its.first, true, DELETE_COOKIE_EXPLICIT);
1274      return true;
1275    }
1276  }
1277  return false;
1278}
1279
1280void CookieMonster::SetCookieableSchemes(const char* schemes[],
1281                                         size_t num_schemes) {
1282  base::AutoLock autolock(lock_);
1283
1284  // Cookieable Schemes must be set before first use of function.
1285  DCHECK(!initialized_);
1286
1287  cookieable_schemes_.clear();
1288  cookieable_schemes_.insert(cookieable_schemes_.end(),
1289                             schemes, schemes + num_schemes);
1290}
1291
1292void CookieMonster::SetEnableFileScheme(bool accept) {
1293  // This assumes "file" is always at the end of the array. See the comment
1294  // above kDefaultCookieableSchemes.
1295  int num_schemes = accept ? kDefaultCookieableSchemesCount :
1296      kDefaultCookieableSchemesCount - 1;
1297  SetCookieableSchemes(kDefaultCookieableSchemes, num_schemes);
1298}
1299
1300void CookieMonster::SetKeepExpiredCookies() {
1301  keep_expired_cookies_ = true;
1302}
1303
1304// static
1305void CookieMonster::EnableFileScheme() {
1306  default_enable_file_scheme_ = true;
1307}
1308
1309void CookieMonster::FlushStore(const base::Closure& callback) {
1310  base::AutoLock autolock(lock_);
1311  if (initialized_ && store_.get())
1312    store_->Flush(callback);
1313  else if (!callback.is_null())
1314    base::MessageLoop::current()->PostTask(FROM_HERE, callback);
1315}
1316
1317bool CookieMonster::SetCookieWithOptions(const GURL& url,
1318                                         const std::string& cookie_line,
1319                                         const CookieOptions& options) {
1320  base::AutoLock autolock(lock_);
1321
1322  if (!HasCookieableScheme(url)) {
1323    return false;
1324  }
1325
1326  return SetCookieWithCreationTimeAndOptions(url, cookie_line, Time(), options);
1327}
1328
1329std::string CookieMonster::GetCookiesWithOptions(const GURL& url,
1330                                                 const CookieOptions& options) {
1331  base::AutoLock autolock(lock_);
1332
1333  if (!HasCookieableScheme(url))
1334    return std::string();
1335
1336  TimeTicks start_time(TimeTicks::Now());
1337
1338  std::vector<CanonicalCookie*> cookies;
1339  FindCookiesForHostAndDomain(url, options, true, &cookies);
1340  std::sort(cookies.begin(), cookies.end(), CookieSorter);
1341
1342  std::string cookie_line = BuildCookieLine(cookies);
1343
1344  histogram_time_get_->AddTime(TimeTicks::Now() - start_time);
1345
1346  VLOG(kVlogGetCookies) << "GetCookies() result: " << cookie_line;
1347
1348  return cookie_line;
1349}
1350
1351void CookieMonster::DeleteCookie(const GURL& url,
1352                                 const std::string& cookie_name) {
1353  base::AutoLock autolock(lock_);
1354
1355  if (!HasCookieableScheme(url))
1356    return;
1357
1358  CookieOptions options;
1359  options.set_include_httponly();
1360  // Get the cookies for this host and its domain(s).
1361  std::vector<CanonicalCookie*> cookies;
1362  FindCookiesForHostAndDomain(url, options, true, &cookies);
1363  std::set<CanonicalCookie*> matching_cookies;
1364
1365  for (std::vector<CanonicalCookie*>::const_iterator it = cookies.begin();
1366       it != cookies.end(); ++it) {
1367    if ((*it)->Name() != cookie_name)
1368      continue;
1369    if (url.path().find((*it)->Path()))
1370      continue;
1371    matching_cookies.insert(*it);
1372  }
1373
1374  for (CookieMap::iterator it = cookies_.begin(); it != cookies_.end();) {
1375    CookieMap::iterator curit = it;
1376    ++it;
1377    if (matching_cookies.find(curit->second) != matching_cookies.end()) {
1378      InternalDeleteCookie(curit, true, DELETE_COOKIE_EXPLICIT);
1379    }
1380  }
1381}
1382
1383int CookieMonster::DeleteSessionCookies() {
1384  base::AutoLock autolock(lock_);
1385
1386  int num_deleted = 0;
1387  for (CookieMap::iterator it = cookies_.begin(); it != cookies_.end();) {
1388    CookieMap::iterator curit = it;
1389    CanonicalCookie* cc = curit->second;
1390    ++it;
1391
1392    if (!cc->IsPersistent()) {
1393      InternalDeleteCookie(curit,
1394                           true,  /*sync_to_store*/
1395                           DELETE_COOKIE_EXPIRED);
1396      ++num_deleted;
1397    }
1398  }
1399
1400  return num_deleted;
1401}
1402
1403bool CookieMonster::HasCookiesForETLDP1(const std::string& etldp1) {
1404  base::AutoLock autolock(lock_);
1405
1406  const std::string key(GetKey(etldp1));
1407
1408  CookieMapItPair its = cookies_.equal_range(key);
1409  return its.first != its.second;
1410}
1411
1412CookieMonster* CookieMonster::GetCookieMonster() {
1413  return this;
1414}
1415
1416// This function must be called before the CookieMonster is used.
1417void CookieMonster::SetPersistSessionCookies(bool persist_session_cookies) {
1418  DCHECK(!initialized_);
1419  persist_session_cookies_ = persist_session_cookies;
1420}
1421
1422// This function must be called before the CookieMonster is used.
1423void CookieMonster::SetPriorityAwareGarbageCollection(
1424    bool priority_aware_garbage_collection) {
1425  DCHECK(!initialized_);
1426  priority_aware_garbage_collection_ = priority_aware_garbage_collection;
1427}
1428
1429void CookieMonster::SetForceKeepSessionState() {
1430  if (store_.get()) {
1431    store_->SetForceKeepSessionState();
1432  }
1433}
1434
1435CookieMonster::~CookieMonster() {
1436  DeleteAll(false);
1437}
1438
1439bool CookieMonster::SetCookieWithCreationTime(const GURL& url,
1440                                              const std::string& cookie_line,
1441                                              const base::Time& creation_time) {
1442  DCHECK(!store_.get()) << "This method is only to be used by unit-tests.";
1443  base::AutoLock autolock(lock_);
1444
1445  if (!HasCookieableScheme(url)) {
1446    return false;
1447  }
1448
1449  InitIfNecessary();
1450  return SetCookieWithCreationTimeAndOptions(url, cookie_line, creation_time,
1451                                             CookieOptions());
1452}
1453
1454void CookieMonster::InitStore() {
1455  DCHECK(store_.get()) << "Store must exist to initialize";
1456
1457  // We bind in the current time so that we can report the wall-clock time for
1458  // loading cookies.
1459  store_->Load(base::Bind(&CookieMonster::OnLoaded, this, TimeTicks::Now()));
1460}
1461
1462void CookieMonster::OnLoaded(TimeTicks beginning_time,
1463                             const std::vector<CanonicalCookie*>& cookies) {
1464  StoreLoadedCookies(cookies);
1465  histogram_time_blocked_on_load_->AddTime(TimeTicks::Now() - beginning_time);
1466
1467  // Invoke the task queue of cookie request.
1468  InvokeQueue();
1469}
1470
1471void CookieMonster::OnKeyLoaded(const std::string& key,
1472                                const std::vector<CanonicalCookie*>& cookies) {
1473  // This function does its own separate locking.
1474  StoreLoadedCookies(cookies);
1475
1476  std::deque<scoped_refptr<CookieMonsterTask> > tasks_pending_for_key;
1477  {
1478    base::AutoLock autolock(lock_);
1479    keys_loaded_.insert(key);
1480    std::map<std::string, std::deque<scoped_refptr<CookieMonsterTask> > >
1481      ::iterator it = tasks_pending_for_key_.find(key);
1482    if (it == tasks_pending_for_key_.end())
1483      return;
1484    it->second.swap(tasks_pending_for_key);
1485    tasks_pending_for_key_.erase(it);
1486  }
1487
1488  while (!tasks_pending_for_key.empty()) {
1489    scoped_refptr<CookieMonsterTask> task = tasks_pending_for_key.front();
1490    task->Run();
1491    tasks_pending_for_key.pop_front();
1492  }
1493}
1494
1495void CookieMonster::StoreLoadedCookies(
1496    const std::vector<CanonicalCookie*>& cookies) {
1497  // Initialize the store and sync in any saved persistent cookies.  We don't
1498  // care if it's expired, insert it so it can be garbage collected, removed,
1499  // and sync'd.
1500  base::AutoLock autolock(lock_);
1501
1502  CookieItVector cookies_with_control_chars;
1503
1504  for (std::vector<CanonicalCookie*>::const_iterator it = cookies.begin();
1505       it != cookies.end(); ++it) {
1506    int64 cookie_creation_time = (*it)->CreationDate().ToInternalValue();
1507
1508    if (creation_times_.insert(cookie_creation_time).second) {
1509      CookieMap::iterator inserted =
1510          InternalInsertCookie(GetKey((*it)->Domain()), *it, false);
1511      const Time cookie_access_time((*it)->LastAccessDate());
1512      if (earliest_access_time_.is_null() ||
1513          cookie_access_time < earliest_access_time_)
1514        earliest_access_time_ = cookie_access_time;
1515
1516      if (ContainsControlCharacter((*it)->Name()) ||
1517          ContainsControlCharacter((*it)->Value())) {
1518          cookies_with_control_chars.push_back(inserted);
1519      }
1520    } else {
1521      LOG(ERROR) << base::StringPrintf("Found cookies with duplicate creation "
1522                                       "times in backing store: "
1523                                       "{name='%s', domain='%s', path='%s'}",
1524                                       (*it)->Name().c_str(),
1525                                       (*it)->Domain().c_str(),
1526                                       (*it)->Path().c_str());
1527      // We've been given ownership of the cookie and are throwing it
1528      // away; reclaim the space.
1529      delete (*it);
1530    }
1531  }
1532
1533  // Any cookies that contain control characters that we have loaded from the
1534  // persistent store should be deleted. See http://crbug.com/238041.
1535  for (CookieItVector::iterator it = cookies_with_control_chars.begin();
1536       it != cookies_with_control_chars.end();) {
1537    CookieItVector::iterator curit = it;
1538    ++it;
1539
1540    InternalDeleteCookie(*curit, true, DELETE_COOKIE_CONTROL_CHAR);
1541  }
1542
1543  // After importing cookies from the PersistentCookieStore, verify that
1544  // none of our other constraints are violated.
1545  // In particular, the backing store might have given us duplicate cookies.
1546
1547  // This method could be called multiple times due to priority loading, thus
1548  // cookies loaded in previous runs will be validated again, but this is OK
1549  // since they are expected to be much fewer than total DB.
1550  EnsureCookiesMapIsValid();
1551}
1552
1553void CookieMonster::InvokeQueue() {
1554  while (true) {
1555    scoped_refptr<CookieMonsterTask> request_task;
1556    {
1557      base::AutoLock autolock(lock_);
1558      if (tasks_pending_.empty()) {
1559        loaded_ = true;
1560        creation_times_.clear();
1561        keys_loaded_.clear();
1562        break;
1563      }
1564      request_task = tasks_pending_.front();
1565      tasks_pending_.pop();
1566    }
1567    request_task->Run();
1568  }
1569}
1570
1571void CookieMonster::EnsureCookiesMapIsValid() {
1572  lock_.AssertAcquired();
1573
1574  int num_duplicates_trimmed = 0;
1575
1576  // Iterate through all the of the cookies, grouped by host.
1577  CookieMap::iterator prev_range_end = cookies_.begin();
1578  while (prev_range_end != cookies_.end()) {
1579    CookieMap::iterator cur_range_begin = prev_range_end;
1580    const std::string key = cur_range_begin->first;  // Keep a copy.
1581    CookieMap::iterator cur_range_end = cookies_.upper_bound(key);
1582    prev_range_end = cur_range_end;
1583
1584    // Ensure no equivalent cookies for this host.
1585    num_duplicates_trimmed +=
1586        TrimDuplicateCookiesForKey(key, cur_range_begin, cur_range_end);
1587  }
1588
1589  // Record how many duplicates were found in the database.
1590  // See InitializeHistograms() for details.
1591  histogram_cookie_deletion_cause_->Add(num_duplicates_trimmed);
1592}
1593
1594int CookieMonster::TrimDuplicateCookiesForKey(
1595    const std::string& key,
1596    CookieMap::iterator begin,
1597    CookieMap::iterator end) {
1598  lock_.AssertAcquired();
1599
1600  // Set of cookies ordered by creation time.
1601  typedef std::set<CookieMap::iterator, OrderByCreationTimeDesc> CookieSet;
1602
1603  // Helper map we populate to find the duplicates.
1604  typedef std::map<CookieSignature, CookieSet> EquivalenceMap;
1605  EquivalenceMap equivalent_cookies;
1606
1607  // The number of duplicate cookies that have been found.
1608  int num_duplicates = 0;
1609
1610  // Iterate through all of the cookies in our range, and insert them into
1611  // the equivalence map.
1612  for (CookieMap::iterator it = begin; it != end; ++it) {
1613    DCHECK_EQ(key, it->first);
1614    CanonicalCookie* cookie = it->second;
1615
1616    CookieSignature signature(cookie->Name(), cookie->Domain(),
1617                              cookie->Path());
1618    CookieSet& set = equivalent_cookies[signature];
1619
1620    // We found a duplicate!
1621    if (!set.empty())
1622      num_duplicates++;
1623
1624    // We save the iterator into |cookies_| rather than the actual cookie
1625    // pointer, since we may need to delete it later.
1626    bool insert_success = set.insert(it).second;
1627    DCHECK(insert_success) <<
1628        "Duplicate creation times found in duplicate cookie name scan.";
1629  }
1630
1631  // If there were no duplicates, we are done!
1632  if (num_duplicates == 0)
1633    return 0;
1634
1635  // Make sure we find everything below that we did above.
1636  int num_duplicates_found = 0;
1637
1638  // Otherwise, delete all the duplicate cookies, both from our in-memory store
1639  // and from the backing store.
1640  for (EquivalenceMap::iterator it = equivalent_cookies.begin();
1641       it != equivalent_cookies.end();
1642       ++it) {
1643    const CookieSignature& signature = it->first;
1644    CookieSet& dupes = it->second;
1645
1646    if (dupes.size() <= 1)
1647      continue;  // This cookiename/path has no duplicates.
1648    num_duplicates_found += dupes.size() - 1;
1649
1650    // Since |dups| is sorted by creation time (descending), the first cookie
1651    // is the most recent one, so we will keep it. The rest are duplicates.
1652    dupes.erase(dupes.begin());
1653
1654    LOG(ERROR) << base::StringPrintf(
1655        "Found %d duplicate cookies for host='%s', "
1656        "with {name='%s', domain='%s', path='%s'}",
1657        static_cast<int>(dupes.size()),
1658        key.c_str(),
1659        signature.name.c_str(),
1660        signature.domain.c_str(),
1661        signature.path.c_str());
1662
1663    // Remove all the cookies identified by |dupes|. It is valid to delete our
1664    // list of iterators one at a time, since |cookies_| is a multimap (they
1665    // don't invalidate existing iterators following deletion).
1666    for (CookieSet::iterator dupes_it = dupes.begin();
1667         dupes_it != dupes.end();
1668         ++dupes_it) {
1669      InternalDeleteCookie(*dupes_it, true,
1670                           DELETE_COOKIE_DUPLICATE_IN_BACKING_STORE);
1671    }
1672  }
1673  DCHECK_EQ(num_duplicates, num_duplicates_found);
1674
1675  return num_duplicates;
1676}
1677
1678// Note: file must be the last scheme.
1679const char* CookieMonster::kDefaultCookieableSchemes[] =
1680    { "http", "https", "file" };
1681const int CookieMonster::kDefaultCookieableSchemesCount =
1682    arraysize(kDefaultCookieableSchemes);
1683
1684void CookieMonster::SetDefaultCookieableSchemes() {
1685  int num_schemes = default_enable_file_scheme_ ?
1686      kDefaultCookieableSchemesCount : kDefaultCookieableSchemesCount - 1;
1687  SetCookieableSchemes(kDefaultCookieableSchemes, num_schemes);
1688}
1689
1690void CookieMonster::FindCookiesForHostAndDomain(
1691    const GURL& url,
1692    const CookieOptions& options,
1693    bool update_access_time,
1694    std::vector<CanonicalCookie*>* cookies) {
1695  lock_.AssertAcquired();
1696
1697  const Time current_time(CurrentTime());
1698
1699  // Probe to save statistics relatively frequently.  We do it here rather
1700  // than in the set path as many websites won't set cookies, and we
1701  // want to collect statistics whenever the browser's being used.
1702  RecordPeriodicStats(current_time);
1703
1704  // Can just dispatch to FindCookiesForKey
1705  const std::string key(GetKey(url.host()));
1706  FindCookiesForKey(key, url, options, current_time,
1707                    update_access_time, cookies);
1708}
1709
1710void CookieMonster::FindCookiesForKey(const std::string& key,
1711                                      const GURL& url,
1712                                      const CookieOptions& options,
1713                                      const Time& current,
1714                                      bool update_access_time,
1715                                      std::vector<CanonicalCookie*>* cookies) {
1716  lock_.AssertAcquired();
1717
1718  for (CookieMapItPair its = cookies_.equal_range(key);
1719       its.first != its.second; ) {
1720    CookieMap::iterator curit = its.first;
1721    CanonicalCookie* cc = curit->second;
1722    ++its.first;
1723
1724    // If the cookie is expired, delete it.
1725    if (cc->IsExpired(current) && !keep_expired_cookies_) {
1726      InternalDeleteCookie(curit, true, DELETE_COOKIE_EXPIRED);
1727      continue;
1728    }
1729
1730    // Filter out cookies that should not be included for a request to the
1731    // given |url|. HTTP only cookies are filtered depending on the passed
1732    // cookie |options|.
1733    if (!cc->IncludeForRequestURL(url, options))
1734      continue;
1735
1736    // Add this cookie to the set of matching cookies. Update the access
1737    // time if we've been requested to do so.
1738    if (update_access_time) {
1739      InternalUpdateCookieAccessTime(cc, current);
1740    }
1741    cookies->push_back(cc);
1742  }
1743}
1744
1745bool CookieMonster::DeleteAnyEquivalentCookie(const std::string& key,
1746                                              const CanonicalCookie& ecc,
1747                                              bool skip_httponly,
1748                                              bool already_expired) {
1749  lock_.AssertAcquired();
1750
1751  bool found_equivalent_cookie = false;
1752  bool skipped_httponly = false;
1753  for (CookieMapItPair its = cookies_.equal_range(key);
1754       its.first != its.second; ) {
1755    CookieMap::iterator curit = its.first;
1756    CanonicalCookie* cc = curit->second;
1757    ++its.first;
1758
1759    if (ecc.IsEquivalent(*cc)) {
1760      // We should never have more than one equivalent cookie, since they should
1761      // overwrite each other.
1762      CHECK(!found_equivalent_cookie) <<
1763          "Duplicate equivalent cookies found, cookie store is corrupted.";
1764      if (skip_httponly && cc->IsHttpOnly()) {
1765        skipped_httponly = true;
1766      } else {
1767        InternalDeleteCookie(curit, true, already_expired ?
1768            DELETE_COOKIE_EXPIRED_OVERWRITE : DELETE_COOKIE_OVERWRITE);
1769      }
1770      found_equivalent_cookie = true;
1771    }
1772  }
1773  return skipped_httponly;
1774}
1775
1776CookieMonster::CookieMap::iterator CookieMonster::InternalInsertCookie(
1777    const std::string& key,
1778    CanonicalCookie* cc,
1779    bool sync_to_store) {
1780  lock_.AssertAcquired();
1781
1782  if ((cc->IsPersistent() || persist_session_cookies_) && store_.get() &&
1783      sync_to_store)
1784    store_->AddCookie(*cc);
1785  CookieMap::iterator inserted =
1786      cookies_.insert(CookieMap::value_type(key, cc));
1787  if (delegate_.get()) {
1788    delegate_->OnCookieChanged(
1789        *cc, false, Delegate::CHANGE_COOKIE_EXPLICIT);
1790  }
1791
1792  return inserted;
1793}
1794
1795bool CookieMonster::SetCookieWithCreationTimeAndOptions(
1796    const GURL& url,
1797    const std::string& cookie_line,
1798    const Time& creation_time_or_null,
1799    const CookieOptions& options) {
1800  lock_.AssertAcquired();
1801
1802  VLOG(kVlogSetCookies) << "SetCookie() line: " << cookie_line;
1803
1804  Time creation_time = creation_time_or_null;
1805  if (creation_time.is_null()) {
1806    creation_time = CurrentTime();
1807    last_time_seen_ = creation_time;
1808  }
1809
1810  scoped_ptr<CanonicalCookie> cc(
1811      CanonicalCookie::Create(url, cookie_line, creation_time, options));
1812
1813  if (!cc.get()) {
1814    VLOG(kVlogSetCookies) << "WARNING: Failed to allocate CanonicalCookie";
1815    return false;
1816  }
1817  return SetCanonicalCookie(&cc, creation_time, options);
1818}
1819
1820bool CookieMonster::SetCanonicalCookie(scoped_ptr<CanonicalCookie>* cc,
1821                                       const Time& creation_time,
1822                                       const CookieOptions& options) {
1823  const std::string key(GetKey((*cc)->Domain()));
1824  bool already_expired = (*cc)->IsExpired(creation_time);
1825  if (DeleteAnyEquivalentCookie(key, **cc, options.exclude_httponly(),
1826                                already_expired)) {
1827    VLOG(kVlogSetCookies) << "SetCookie() not clobbering httponly cookie";
1828    return false;
1829  }
1830
1831  VLOG(kVlogSetCookies) << "SetCookie() key: " << key << " cc: "
1832                        << (*cc)->DebugString();
1833
1834  // Realize that we might be setting an expired cookie, and the only point
1835  // was to delete the cookie which we've already done.
1836  if (!already_expired || keep_expired_cookies_) {
1837    // See InitializeHistograms() for details.
1838    if ((*cc)->IsPersistent()) {
1839      histogram_expiration_duration_minutes_->Add(
1840          ((*cc)->ExpiryDate() - creation_time).InMinutes());
1841    }
1842
1843    InternalInsertCookie(key, cc->release(), true);
1844  } else {
1845    VLOG(kVlogSetCookies) << "SetCookie() not storing already expired cookie.";
1846  }
1847
1848  // We assume that hopefully setting a cookie will be less common than
1849  // querying a cookie.  Since setting a cookie can put us over our limits,
1850  // make sure that we garbage collect...  We can also make the assumption that
1851  // if a cookie was set, in the common case it will be used soon after,
1852  // and we will purge the expired cookies in GetCookies().
1853  GarbageCollect(creation_time, key);
1854
1855  return true;
1856}
1857
1858void CookieMonster::InternalUpdateCookieAccessTime(CanonicalCookie* cc,
1859                                                   const Time& current) {
1860  lock_.AssertAcquired();
1861
1862  // Based off the Mozilla code.  When a cookie has been accessed recently,
1863  // don't bother updating its access time again.  This reduces the number of
1864  // updates we do during pageload, which in turn reduces the chance our storage
1865  // backend will hit its batch thresholds and be forced to update.
1866  if ((current - cc->LastAccessDate()) < last_access_threshold_)
1867    return;
1868
1869  // See InitializeHistograms() for details.
1870  histogram_between_access_interval_minutes_->Add(
1871      (current - cc->LastAccessDate()).InMinutes());
1872
1873  cc->SetLastAccessDate(current);
1874  if ((cc->IsPersistent() || persist_session_cookies_) && store_.get())
1875    store_->UpdateCookieAccessTime(*cc);
1876}
1877
1878// InternalDeleteCookies must not invalidate iterators other than the one being
1879// deleted.
1880void CookieMonster::InternalDeleteCookie(CookieMap::iterator it,
1881                                         bool sync_to_store,
1882                                         DeletionCause deletion_cause) {
1883  lock_.AssertAcquired();
1884
1885  // Ideally, this would be asserted up where we define ChangeCauseMapping,
1886  // but DeletionCause's visibility (or lack thereof) forces us to make
1887  // this check here.
1888  COMPILE_ASSERT(arraysize(ChangeCauseMapping) == DELETE_COOKIE_LAST_ENTRY + 1,
1889                 ChangeCauseMapping_size_not_eq_DeletionCause_enum_size);
1890
1891  // See InitializeHistograms() for details.
1892  if (deletion_cause != DELETE_COOKIE_DONT_RECORD)
1893    histogram_cookie_deletion_cause_->Add(deletion_cause);
1894
1895  CanonicalCookie* cc = it->second;
1896  VLOG(kVlogSetCookies) << "InternalDeleteCookie() cc: " << cc->DebugString();
1897
1898  if ((cc->IsPersistent() || persist_session_cookies_) && store_.get() &&
1899      sync_to_store)
1900    store_->DeleteCookie(*cc);
1901  if (delegate_.get()) {
1902    ChangeCausePair mapping = ChangeCauseMapping[deletion_cause];
1903
1904    if (mapping.notify)
1905      delegate_->OnCookieChanged(*cc, true, mapping.cause);
1906  }
1907  cookies_.erase(it);
1908  delete cc;
1909}
1910
1911// Domain expiry behavior is unchanged by key/expiry scheme (the
1912// meaning of the key is different, but that's not visible to this routine).
1913int CookieMonster::GarbageCollect(const Time& current,
1914                                  const std::string& key) {
1915  lock_.AssertAcquired();
1916
1917  int num_deleted = 0;
1918  Time safe_date(
1919      Time::Now() - TimeDelta::FromDays(kSafeFromGlobalPurgeDays));
1920
1921  // Collect garbage for this key, minding cookie priorities.
1922  if (cookies_.count(key) > kDomainMaxCookies) {
1923    VLOG(kVlogGarbageCollection) << "GarbageCollect() key: " << key;
1924
1925    CookieItVector cookie_its;
1926    num_deleted += GarbageCollectExpired(
1927        current, cookies_.equal_range(key), &cookie_its);
1928    if (cookie_its.size() > kDomainMaxCookies) {
1929      VLOG(kVlogGarbageCollection) << "Deep Garbage Collect domain.";
1930      size_t purge_goal =
1931          cookie_its.size() - (kDomainMaxCookies - kDomainPurgeCookies);
1932      DCHECK(purge_goal > kDomainPurgeCookies);
1933
1934      // Boundary iterators into |cookie_its| for different priorities.
1935      CookieItVector::iterator it_bdd[4];
1936      // Intialize |it_bdd| while sorting |cookie_its| by priorities.
1937      // Schematic: [MLLHMHHLMM] => [LLL|MMMM|HHH], with 4 boundaries.
1938      it_bdd[0] = cookie_its.begin();
1939      it_bdd[3] = cookie_its.end();
1940      it_bdd[1] = PartitionCookieByPriority(it_bdd[0], it_bdd[3],
1941                                            COOKIE_PRIORITY_LOW);
1942      it_bdd[2] = PartitionCookieByPriority(it_bdd[1], it_bdd[3],
1943                                            COOKIE_PRIORITY_MEDIUM);
1944      size_t quota[3] = {
1945        kDomainCookiesQuotaLow,
1946        kDomainCookiesQuotaMedium,
1947        kDomainCookiesQuotaHigh
1948      };
1949
1950      // Purge domain cookies in 3 rounds.
1951      // Round 1: consider low-priority cookies only: evict least-recently
1952      //   accessed, while protecting quota[0] of these from deletion.
1953      // Round 2: consider {low, medium}-priority cookies, evict least-recently
1954      //   accessed, while protecting quota[0] + quota[1].
1955      // Round 3: consider all cookies, evict least-recently accessed.
1956      size_t accumulated_quota = 0;
1957      CookieItVector::iterator it_purge_begin = it_bdd[0];
1958      for (int i = 0; i < 3 && purge_goal > 0; ++i) {
1959        accumulated_quota += quota[i];
1960
1961        // If we are not using priority, only do Round 3. This reproduces the
1962        // old way of indiscriminately purging least-recently accessed cookies.
1963        if (!priority_aware_garbage_collection_ && i < 2)
1964          continue;
1965
1966        size_t num_considered = it_bdd[i + 1] - it_purge_begin;
1967        if (num_considered <= accumulated_quota)
1968          continue;
1969
1970        // Number of cookies that will be purged in this round.
1971        size_t round_goal =
1972            std::min(purge_goal, num_considered - accumulated_quota);
1973        purge_goal -= round_goal;
1974
1975        SortLeastRecentlyAccessed(it_purge_begin, it_bdd[i + 1], round_goal);
1976        // Cookies accessed on or after |safe_date| would have been safe from
1977        // global purge, and we want to keep track of this.
1978        CookieItVector::iterator it_purge_end = it_purge_begin + round_goal;
1979        CookieItVector::iterator it_purge_middle =
1980            LowerBoundAccessDate(it_purge_begin, it_purge_end, safe_date);
1981        // Delete cookies accessed before |safe_date|.
1982        num_deleted += GarbageCollectDeleteRange(
1983            current,
1984            DELETE_COOKIE_EVICTED_DOMAIN_PRE_SAFE,
1985            it_purge_begin,
1986            it_purge_middle);
1987        // Delete cookies accessed on or after |safe_date|.
1988        num_deleted += GarbageCollectDeleteRange(
1989            current,
1990            DELETE_COOKIE_EVICTED_DOMAIN_POST_SAFE,
1991            it_purge_middle,
1992            it_purge_end);
1993        it_purge_begin = it_purge_end;
1994      }
1995      DCHECK_EQ(0U, purge_goal);
1996    }
1997  }
1998
1999  // Collect garbage for everything. With firefox style we want to preserve
2000  // cookies accessed in kSafeFromGlobalPurgeDays, otherwise evict.
2001  if (cookies_.size() > kMaxCookies &&
2002      earliest_access_time_ < safe_date) {
2003    VLOG(kVlogGarbageCollection) << "GarbageCollect() everything";
2004    CookieItVector cookie_its;
2005    num_deleted += GarbageCollectExpired(
2006        current, CookieMapItPair(cookies_.begin(), cookies_.end()),
2007        &cookie_its);
2008    if (cookie_its.size() > kMaxCookies) {
2009      VLOG(kVlogGarbageCollection) << "Deep Garbage Collect everything.";
2010      size_t purge_goal = cookie_its.size() - (kMaxCookies - kPurgeCookies);
2011      DCHECK(purge_goal > kPurgeCookies);
2012      // Sorts up to *and including* |cookie_its[purge_goal]|, so
2013      // |earliest_access_time| will be properly assigned even if
2014      // |global_purge_it| == |cookie_its.begin() + purge_goal|.
2015      SortLeastRecentlyAccessed(cookie_its.begin(), cookie_its.end(),
2016                                purge_goal);
2017      // Find boundary to cookies older than safe_date.
2018      CookieItVector::iterator global_purge_it =
2019          LowerBoundAccessDate(cookie_its.begin(),
2020                               cookie_its.begin() + purge_goal,
2021                               safe_date);
2022      // Only delete the old cookies.
2023      num_deleted += GarbageCollectDeleteRange(
2024          current,
2025          DELETE_COOKIE_EVICTED_GLOBAL,
2026          cookie_its.begin(),
2027          global_purge_it);
2028      // Set access day to the oldest cookie that wasn't deleted.
2029      earliest_access_time_ = (*global_purge_it)->second->LastAccessDate();
2030    }
2031  }
2032
2033  return num_deleted;
2034}
2035
2036int CookieMonster::GarbageCollectExpired(
2037    const Time& current,
2038    const CookieMapItPair& itpair,
2039    CookieItVector* cookie_its) {
2040  if (keep_expired_cookies_)
2041    return 0;
2042
2043  lock_.AssertAcquired();
2044
2045  int num_deleted = 0;
2046  for (CookieMap::iterator it = itpair.first, end = itpair.second; it != end;) {
2047    CookieMap::iterator curit = it;
2048    ++it;
2049
2050    if (curit->second->IsExpired(current)) {
2051      InternalDeleteCookie(curit, true, DELETE_COOKIE_EXPIRED);
2052      ++num_deleted;
2053    } else if (cookie_its) {
2054      cookie_its->push_back(curit);
2055    }
2056  }
2057
2058  return num_deleted;
2059}
2060
2061int CookieMonster::GarbageCollectDeleteRange(
2062    const Time& current,
2063    DeletionCause cause,
2064    CookieItVector::iterator it_begin,
2065    CookieItVector::iterator it_end) {
2066  for (CookieItVector::iterator it = it_begin; it != it_end; it++) {
2067    histogram_evicted_last_access_minutes_->Add(
2068        (current - (*it)->second->LastAccessDate()).InMinutes());
2069    InternalDeleteCookie((*it), true, cause);
2070  }
2071  return it_end - it_begin;
2072}
2073
2074// A wrapper around registry_controlled_domains::GetDomainAndRegistry
2075// to make clear we're creating a key for our local map.  Here and
2076// in FindCookiesForHostAndDomain() are the only two places where
2077// we need to conditionalize based on key type.
2078//
2079// Note that this key algorithm explicitly ignores the scheme.  This is
2080// because when we're entering cookies into the map from the backing store,
2081// we in general won't have the scheme at that point.
2082// In practical terms, this means that file cookies will be stored
2083// in the map either by an empty string or by UNC name (and will be
2084// limited by kMaxCookiesPerHost), and extension cookies will be stored
2085// based on the single extension id, as the extension id won't have the
2086// form of a DNS host and hence GetKey() will return it unchanged.
2087//
2088// Arguably the right thing to do here is to make the key
2089// algorithm dependent on the scheme, and make sure that the scheme is
2090// available everywhere the key must be obtained (specfically at backing
2091// store load time).  This would require either changing the backing store
2092// database schema to include the scheme (far more trouble than it's worth), or
2093// separating out file cookies into their own CookieMonster instance and
2094// thus restricting each scheme to a single cookie monster (which might
2095// be worth it, but is still too much trouble to solve what is currently a
2096// non-problem).
2097std::string CookieMonster::GetKey(const std::string& domain) const {
2098  std::string effective_domain(
2099      registry_controlled_domains::GetDomainAndRegistry(
2100          domain, registry_controlled_domains::EXCLUDE_PRIVATE_REGISTRIES));
2101  if (effective_domain.empty())
2102    effective_domain = domain;
2103
2104  if (!effective_domain.empty() && effective_domain[0] == '.')
2105    return effective_domain.substr(1);
2106  return effective_domain;
2107}
2108
2109bool CookieMonster::IsCookieableScheme(const std::string& scheme) {
2110  base::AutoLock autolock(lock_);
2111
2112  return std::find(cookieable_schemes_.begin(), cookieable_schemes_.end(),
2113                   scheme) != cookieable_schemes_.end();
2114}
2115
2116bool CookieMonster::HasCookieableScheme(const GURL& url) {
2117  lock_.AssertAcquired();
2118
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;
2125    }
2126  }
2127
2128  // The scheme didn't match any in our whitelist.
2129  VLOG(kVlogPerCookieMonster) << "WARNING: Unsupported cookie scheme: "
2130                              << url.scheme();
2131  return false;
2132}
2133
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.
2142void CookieMonster::RecordPeriodicStats(const base::Time& current_time) {
2143  const base::TimeDelta kRecordStatisticsIntervalTime(
2144      base::TimeDelta::FromSeconds(kRecordStatisticsIntervalSeconds));
2145
2146  // If we've taken statistics recently, return.
2147  if (current_time - last_statistic_record_time_ <=
2148      kRecordStatisticsIntervalTime) {
2149    return;
2150  }
2151
2152  // See InitializeHistograms() for details.
2153  histogram_count_->Add(cookies_.size());
2154
2155  // More detailed statistics on cookie counts at different granularities.
2156  TimeTicks beginning_of_time(TimeTicks::Now());
2157
2158  for (CookieMap::const_iterator it_key = cookies_.begin();
2159       it_key != cookies_.end(); ) {
2160    const std::string& key(it_key->first);
2161
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]++;
2170
2171      its_cookies.first++;
2172    }
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);
2178
2179    it_key = its_cookies.second;
2180  }
2181
2182  VLOG(kVlogPeriodic)
2183      << "Time for recording cookie stats (us): "
2184      << (TimeTicks::Now() - beginning_of_time).InMicroseconds();
2185
2186  last_statistic_record_time_ = current_time;
2187}
2188
2189// Initialize all histogram counter variables used in this class.
2190//
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.
2200//
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.
2206//
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.
2212void 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);
2238
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);
2243
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);
2249
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_blocked_on_load_ = base::Histogram::FactoryTimeGet(
2255      "Cookie.TimeBlockedOnLoad",
2256      base::TimeDelta::FromMilliseconds(1), base::TimeDelta::FromMinutes(1),
2257      50, base::Histogram::kUmaTargetedHistogramFlag);
2258}
2259
2260
2261// The system resolution is not high enough, so we can have multiple
2262// set cookies that result in the same system time.  When this happens, we
2263// increment by one Time unit.  Let's hope computers don't get too fast.
2264Time CookieMonster::CurrentTime() {
2265  return std::max(Time::Now(),
2266      Time::FromInternalValue(last_time_seen_.ToInternalValue() + 1));
2267}
2268
2269}  // namespace net
2270