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.
5 #include "chrome/browser/net/evicted_domain_cookie_counter.h"
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"
16 namespace chrome_browser_net
{
19 using base::TimeDelta
;
23 const size_t kMaxEvictedDomainCookies
= 500;
24 const size_t kPurgeEvictedDomainCookies
= 100;
26 class DelegateImpl
: public EvictedDomainCookieCounter::Delegate
{
30 // EvictedDomainCookieCounter::Delegate implementation.
32 const EvictedDomainCookieCounter::EvictedCookie
& evicted_cookie
,
33 const Time
& reinstatement_time
) OVERRIDE
;
34 virtual Time
CurrentTime() const OVERRIDE
;
37 DelegateImpl::DelegateImpl() {}
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 UMA_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",
49 TimeDelta::FromSeconds(1),
50 TimeDelta::FromDays(7),
53 UMA_HISTOGRAM_CUSTOM_TIMES("Cookie.ReinstatedCookiesOther",
55 TimeDelta::FromSeconds(1),
56 TimeDelta::FromDays(7),
61 Time
DelegateImpl::CurrentTime() const {
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
) {
75 EvictedDomainCookieCounter::EvictedDomainCookieCounter(
76 scoped_refptr
<net::CookieMonster::Delegate
> next_cookie_monster_delegate
,
77 scoped_ptr
<Delegate
> cookie_counter_delegate
,
80 : next_cookie_monster_delegate_(next_cookie_monster_delegate
),
81 cookie_counter_delegate_(cookie_counter_delegate
.Pass()),
83 purge_count_(purge_count
) {
84 DCHECK(cookie_counter_delegate_
);
85 DCHECK_LT(purge_count
, max_size_
);
88 EvictedDomainCookieCounter::~EvictedDomainCookieCounter() {
89 STLDeleteContainerPairSecondPointers(evicted_cookies_
.begin(),
90 evicted_cookies_
.end());
93 size_t EvictedDomainCookieCounter::GetStorageSize() const {
94 return evicted_cookies_
.size();
97 void EvictedDomainCookieCounter::OnCookieChanged(
98 const net::CanonicalCookie
& cookie
,
101 EvictedDomainCookieCounter::EvictedCookieKey
key(GetKey(cookie
));
102 Time
current_time(cookie_counter_delegate_
->CurrentTime());
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
);
110 if (next_cookie_monster_delegate_
.get())
111 next_cookie_monster_delegate_
->OnCookieChanged(cookie
, removed
, cause
);
114 void EvictedDomainCookieCounter::OnLoaded() {
115 if (next_cookie_monster_delegate_
.get())
116 next_cookie_monster_delegate_
->OnLoaded();
120 EvictedDomainCookieCounter::EvictedCookieKey
121 EvictedDomainCookieCounter::GetKey(const net::CanonicalCookie
& cookie
) {
122 return cookie
.Domain() + ";" + cookie
.Path() + ";" + cookie
.Name();
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
;
133 void EvictedDomainCookieCounter::GarbageCollect(const Time
& current_time
) {
134 if (evicted_cookies_
.size() <= max_size_
)
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);
144 std::vector
<EvictedCookieMap::iterator
> remove_list
;
145 remove_list
.reserve(evicted_cookies_
.size());
147 EvictedCookieMap::iterator it
= evicted_cookies_
.begin();
148 while (it
!= evicted_cookies_
.end()) {
149 if (it
->second
->is_expired(current_time
)) {
151 evicted_cookies_
.erase(it
++); // Post-increment idiom for in-loop removal.
155 if (remove_quota
) // Don't bother storing if quota met.
156 remove_list
.push_back(it
);
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
]);
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
);
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
) {
187 delete prev_entry
.first
->second
;
188 prev_entry
.first
->second
= evicted_cookie
;
191 GarbageCollect(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
);
203 evicted_cookies_
.erase(it
);
207 } // namespace chrome_browser_net