• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2013 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "chrome/browser/net/evicted_domain_cookie_counter.h"
6 
7 #include <algorithm>
8 #include <vector>
9 
10 #include "base/metrics/histogram.h"
11 #include "base/stl_util.h"
12 #include "base/strings/string_util.h"
13 #include "components/google/core/browser/google_util.h"
14 #include "net/cookies/canonical_cookie.h"
15 
16 namespace chrome_browser_net {
17 
18 using base::Time;
19 using base::TimeDelta;
20 
21 namespace {
22 
23 const size_t kMaxEvictedDomainCookies = 500;
24 const size_t kPurgeEvictedDomainCookies = 100;
25 
26 class DelegateImpl : public EvictedDomainCookieCounter::Delegate {
27  public:
28   DelegateImpl();
29 
30   // EvictedDomainCookieCounter::Delegate implementation.
31   virtual void Report(
32       const EvictedDomainCookieCounter::EvictedCookie& evicted_cookie,
33       const Time& reinstatement_time) OVERRIDE;
34   virtual Time CurrentTime() const OVERRIDE;
35 };
36 
DelegateImpl()37 DelegateImpl::DelegateImpl() {}
38 
Report(const EvictedDomainCookieCounter::EvictedCookie & evicted_cookie,const Time & reinstatement_time)39 void DelegateImpl::Report(
40     const EvictedDomainCookieCounter::EvictedCookie& evicted_cookie,
41     const Time& reinstatement_time) {
42   TimeDelta reinstatement_delay(
43       reinstatement_time - evicted_cookie.eviction_time);
44   // Need to duplicate HISTOGRAM_CUSTOM_TIMES(), since it is a macro that
45   // defines a static variable.
46   if (evicted_cookie.is_google) {
47     UMA_HISTOGRAM_CUSTOM_TIMES("Cookie.ReinstatedCookiesGoogle",
48                                reinstatement_delay,
49                                TimeDelta::FromSeconds(1),
50                                TimeDelta::FromDays(7),
51                                50);
52   } else {
53     UMA_HISTOGRAM_CUSTOM_TIMES("Cookie.ReinstatedCookiesOther",
54                                reinstatement_delay,
55                                TimeDelta::FromSeconds(1),
56                                TimeDelta::FromDays(7),
57                                50);
58   }
59 }
60 
CurrentTime() const61 Time DelegateImpl::CurrentTime() const {
62   return Time::Now();
63 }
64 
65 }  // namespace
66 
EvictedDomainCookieCounter(scoped_refptr<net::CookieMonster::Delegate> next_cookie_monster_delegate)67 EvictedDomainCookieCounter::EvictedDomainCookieCounter(
68     scoped_refptr<net::CookieMonster::Delegate> next_cookie_monster_delegate)
69     : next_cookie_monster_delegate_(next_cookie_monster_delegate),
70       cookie_counter_delegate_(new DelegateImpl),
71       max_size_(kMaxEvictedDomainCookies),
72       purge_count_(kPurgeEvictedDomainCookies) {
73 }
74 
EvictedDomainCookieCounter(scoped_refptr<net::CookieMonster::Delegate> next_cookie_monster_delegate,scoped_ptr<Delegate> cookie_counter_delegate,size_t max_size,size_t purge_count)75 EvictedDomainCookieCounter::EvictedDomainCookieCounter(
76     scoped_refptr<net::CookieMonster::Delegate> next_cookie_monster_delegate,
77     scoped_ptr<Delegate> cookie_counter_delegate,
78     size_t max_size,
79     size_t purge_count)
80     : next_cookie_monster_delegate_(next_cookie_monster_delegate),
81       cookie_counter_delegate_(cookie_counter_delegate.Pass()),
82       max_size_(max_size),
83       purge_count_(purge_count) {
84   DCHECK(cookie_counter_delegate_);
85   DCHECK_LT(purge_count, max_size_);
86 }
87 
~EvictedDomainCookieCounter()88 EvictedDomainCookieCounter::~EvictedDomainCookieCounter() {
89   STLDeleteContainerPairSecondPointers(evicted_cookies_.begin(),
90                                        evicted_cookies_.end());
91 }
92 
GetStorageSize() const93 size_t EvictedDomainCookieCounter::GetStorageSize() const {
94   return evicted_cookies_.size();
95 }
96 
OnCookieChanged(const net::CanonicalCookie & cookie,bool removed,ChangeCause cause)97 void EvictedDomainCookieCounter::OnCookieChanged(
98     const net::CanonicalCookie& cookie,
99     bool removed,
100     ChangeCause cause) {
101   EvictedDomainCookieCounter::EvictedCookieKey key(GetKey(cookie));
102   Time current_time(cookie_counter_delegate_->CurrentTime());
103   if (removed) {
104     if (cause == net::CookieMonster::Delegate::CHANGE_COOKIE_EVICTED)
105       StoreEvictedCookie(key, cookie, current_time);
106   } else {  // Includes adds or updates.
107     ProcessNewCookie(key, cookie, current_time);
108   }
109 
110   if (next_cookie_monster_delegate_.get())
111     next_cookie_monster_delegate_->OnCookieChanged(cookie, removed, cause);
112 }
113 
OnLoaded()114 void EvictedDomainCookieCounter::OnLoaded() {
115   if (next_cookie_monster_delegate_.get())
116     next_cookie_monster_delegate_->OnLoaded();
117 }
118 
119 // static
120 EvictedDomainCookieCounter::EvictedCookieKey
GetKey(const net::CanonicalCookie & cookie)121     EvictedDomainCookieCounter::GetKey(const net::CanonicalCookie& cookie) {
122   return cookie.Domain() + ";" + cookie.Path() + ";" + cookie.Name();
123 }
124 
125 // static
CompareEvictedCookie(const EvictedCookieMap::iterator evicted_cookie1,const EvictedCookieMap::iterator evicted_cookie2)126 bool EvictedDomainCookieCounter::CompareEvictedCookie(
127     const EvictedCookieMap::iterator evicted_cookie1,
128     const EvictedCookieMap::iterator evicted_cookie2) {
129   return evicted_cookie1->second->eviction_time
130       < evicted_cookie2->second->eviction_time;
131 }
132 
GarbageCollect(const Time & current_time)133 void EvictedDomainCookieCounter::GarbageCollect(const Time& current_time) {
134   if (evicted_cookies_.size() <= max_size_)
135     return;
136 
137   // From |evicted_cookies_|, removed all expired cookies, and remove cookies
138   // with the oldest |eviction_time| so that |size_goal| is attained.
139   size_t size_goal = max_size_ - purge_count_;
140   // Bound on number of non-expired cookies to remove.
141   size_t remove_quota = evicted_cookies_.size() - size_goal;
142   DCHECK_GT(remove_quota, 0u);
143 
144   std::vector<EvictedCookieMap::iterator> remove_list;
145   remove_list.reserve(evicted_cookies_.size());
146 
147   EvictedCookieMap::iterator it = evicted_cookies_.begin();
148   while (it != evicted_cookies_.end()) {
149     if (it->second->is_expired(current_time)) {
150       delete it->second;
151       evicted_cookies_.erase(it++); // Post-increment idiom for in-loop removal.
152       if (remove_quota)
153         --remove_quota;
154     } else {
155       if (remove_quota)  // Don't bother storing if quota met.
156         remove_list.push_back(it);
157       ++it;
158     }
159   }
160 
161   // Free the oldest |remove_quota| non-expired cookies.
162   std::partial_sort(remove_list.begin(), remove_list.begin() + remove_quota,
163                     remove_list.end(), CompareEvictedCookie);
164   for (size_t i = 0; i < remove_quota; ++i) {
165     delete remove_list[i]->second;
166     evicted_cookies_.erase(remove_list[i]);
167   }
168 
169   // Apply stricter check if non-expired cookies were deleted.
170   DCHECK(remove_quota ? evicted_cookies_.size() == size_goal :
171          evicted_cookies_.size() <= size_goal);
172 }
173 
StoreEvictedCookie(const EvictedCookieKey & key,const net::CanonicalCookie & cookie,const Time & current_time)174 void EvictedDomainCookieCounter::StoreEvictedCookie(
175     const EvictedCookieKey& key,
176     const net::CanonicalCookie& cookie,
177     const Time& current_time) {
178   bool is_google = google_util::IsGoogleHostname(
179       cookie.Domain(), google_util::ALLOW_SUBDOMAIN);
180   EvictedCookie* evicted_cookie =
181       new EvictedCookie(current_time, cookie.ExpiryDate(), is_google);
182   std::pair<EvictedCookieMap::iterator, bool> prev_entry =
183       evicted_cookies_.insert(
184           EvictedCookieMap::value_type(key, evicted_cookie));
185   if (!prev_entry.second) {
186     NOTREACHED();
187     delete prev_entry.first->second;
188     prev_entry.first->second = evicted_cookie;
189   }
190 
191   GarbageCollect(current_time);
192 }
193 
ProcessNewCookie(const EvictedCookieKey & key,const net::CanonicalCookie & cc,const Time & current_time)194 void EvictedDomainCookieCounter::ProcessNewCookie(
195     const EvictedCookieKey& key,
196     const net::CanonicalCookie& cc,
197     const Time& current_time) {
198   EvictedCookieMap::iterator it = evicted_cookies_.find(key);
199   if (it != evicted_cookies_.end()) {
200     if (!it->second->is_expired(current_time))  // Reinstatement.
201       cookie_counter_delegate_->Report(*it->second, current_time);
202     delete it->second;
203     evicted_cookies_.erase(it);
204   }
205 }
206 
207 }  // namespace chrome_browser_net
208