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.
31 void Report(const EvictedDomainCookieCounter::EvictedCookie
& evicted_cookie
,
32 const Time
& reinstatement_time
) override
;
33 Time
CurrentTime() const override
;
36 DelegateImpl::DelegateImpl() {}
38 void DelegateImpl::Report(
39 const EvictedDomainCookieCounter::EvictedCookie
& evicted_cookie
,
40 const Time
& reinstatement_time
) {
41 TimeDelta
reinstatement_delay(
42 reinstatement_time
- evicted_cookie
.eviction_time
);
43 // Need to duplicate UMA_HISTOGRAM_CUSTOM_TIMES(), since it is a macro that
44 // defines a static variable.
45 if (evicted_cookie
.is_google
) {
46 UMA_HISTOGRAM_CUSTOM_TIMES("Cookie.ReinstatedCookiesGoogle",
48 TimeDelta::FromSeconds(1),
49 TimeDelta::FromDays(7),
52 UMA_HISTOGRAM_CUSTOM_TIMES("Cookie.ReinstatedCookiesOther",
54 TimeDelta::FromSeconds(1),
55 TimeDelta::FromDays(7),
60 Time
DelegateImpl::CurrentTime() const {
66 EvictedDomainCookieCounter::EvictedDomainCookieCounter(
67 scoped_refptr
<net::CookieMonster::Delegate
> next_cookie_monster_delegate
)
68 : next_cookie_monster_delegate_(next_cookie_monster_delegate
),
69 cookie_counter_delegate_(new DelegateImpl
),
70 max_size_(kMaxEvictedDomainCookies
),
71 purge_count_(kPurgeEvictedDomainCookies
) {
74 EvictedDomainCookieCounter::EvictedDomainCookieCounter(
75 scoped_refptr
<net::CookieMonster::Delegate
> next_cookie_monster_delegate
,
76 scoped_ptr
<Delegate
> cookie_counter_delegate
,
79 : next_cookie_monster_delegate_(next_cookie_monster_delegate
),
80 cookie_counter_delegate_(cookie_counter_delegate
.Pass()),
82 purge_count_(purge_count
) {
83 DCHECK(cookie_counter_delegate_
);
84 DCHECK_LT(purge_count
, max_size_
);
87 EvictedDomainCookieCounter::~EvictedDomainCookieCounter() {
88 STLDeleteContainerPairSecondPointers(evicted_cookies_
.begin(),
89 evicted_cookies_
.end());
92 size_t EvictedDomainCookieCounter::GetStorageSize() const {
93 return evicted_cookies_
.size();
96 void EvictedDomainCookieCounter::OnCookieChanged(
97 const net::CanonicalCookie
& cookie
,
100 EvictedDomainCookieCounter::EvictedCookieKey
key(GetKey(cookie
));
101 Time
current_time(cookie_counter_delegate_
->CurrentTime());
103 if (cause
== net::CookieMonster::Delegate::CHANGE_COOKIE_EVICTED
)
104 StoreEvictedCookie(key
, cookie
, current_time
);
105 } else { // Includes adds or updates.
106 ProcessNewCookie(key
, cookie
, current_time
);
109 if (next_cookie_monster_delegate_
.get())
110 next_cookie_monster_delegate_
->OnCookieChanged(cookie
, removed
, cause
);
113 void EvictedDomainCookieCounter::OnLoaded() {
114 if (next_cookie_monster_delegate_
.get())
115 next_cookie_monster_delegate_
->OnLoaded();
119 EvictedDomainCookieCounter::EvictedCookieKey
120 EvictedDomainCookieCounter::GetKey(const net::CanonicalCookie
& cookie
) {
121 return cookie
.Domain() + ";" + cookie
.Path() + ";" + cookie
.Name();
125 bool EvictedDomainCookieCounter::CompareEvictedCookie(
126 const EvictedCookieMap::iterator evicted_cookie1
,
127 const EvictedCookieMap::iterator evicted_cookie2
) {
128 return evicted_cookie1
->second
->eviction_time
129 < evicted_cookie2
->second
->eviction_time
;
132 void EvictedDomainCookieCounter::GarbageCollect(const Time
& current_time
) {
133 if (evicted_cookies_
.size() <= max_size_
)
136 // From |evicted_cookies_|, removed all expired cookies, and remove cookies
137 // with the oldest |eviction_time| so that |size_goal| is attained.
138 size_t size_goal
= max_size_
- purge_count_
;
139 // Bound on number of non-expired cookies to remove.
140 size_t remove_quota
= evicted_cookies_
.size() - size_goal
;
141 DCHECK_GT(remove_quota
, 0u);
143 std::vector
<EvictedCookieMap::iterator
> remove_list
;
144 remove_list
.reserve(evicted_cookies_
.size());
146 EvictedCookieMap::iterator it
= evicted_cookies_
.begin();
147 while (it
!= evicted_cookies_
.end()) {
148 if (it
->second
->is_expired(current_time
)) {
150 evicted_cookies_
.erase(it
++); // Post-increment idiom for in-loop removal.
154 if (remove_quota
) // Don't bother storing if quota met.
155 remove_list
.push_back(it
);
160 // Free the oldest |remove_quota| non-expired cookies.
161 std::partial_sort(remove_list
.begin(), remove_list
.begin() + remove_quota
,
162 remove_list
.end(), CompareEvictedCookie
);
163 for (size_t i
= 0; i
< remove_quota
; ++i
) {
164 delete remove_list
[i
]->second
;
165 evicted_cookies_
.erase(remove_list
[i
]);
168 // Apply stricter check if non-expired cookies were deleted.
169 DCHECK(remove_quota
? evicted_cookies_
.size() == size_goal
:
170 evicted_cookies_
.size() <= size_goal
);
173 void EvictedDomainCookieCounter::StoreEvictedCookie(
174 const EvictedCookieKey
& key
,
175 const net::CanonicalCookie
& cookie
,
176 const Time
& current_time
) {
177 bool is_google
= google_util::IsGoogleHostname(
178 cookie
.Domain(), google_util::ALLOW_SUBDOMAIN
);
179 EvictedCookie
* evicted_cookie
=
180 new EvictedCookie(current_time
, cookie
.ExpiryDate(), is_google
);
181 std::pair
<EvictedCookieMap::iterator
, bool> prev_entry
=
182 evicted_cookies_
.insert(
183 EvictedCookieMap::value_type(key
, evicted_cookie
));
184 if (!prev_entry
.second
) {
186 delete prev_entry
.first
->second
;
187 prev_entry
.first
->second
= evicted_cookie
;
190 GarbageCollect(current_time
);
193 void EvictedDomainCookieCounter::ProcessNewCookie(
194 const EvictedCookieKey
& key
,
195 const net::CanonicalCookie
& cc
,
196 const Time
& current_time
) {
197 EvictedCookieMap::iterator it
= evicted_cookies_
.find(key
);
198 if (it
!= evicted_cookies_
.end()) {
199 if (!it
->second
->is_expired(current_time
)) // Reinstatement.
200 cookie_counter_delegate_
->Report(*it
->second
, current_time
);
202 evicted_cookies_
.erase(it
);
206 } // namespace chrome_browser_net