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
);
114 EvictedDomainCookieCounter::EvictedCookieKey
115 EvictedDomainCookieCounter::GetKey(const net::CanonicalCookie
& cookie
) {
116 return cookie
.Domain() + ";" + cookie
.Path() + ";" + cookie
.Name();
120 bool EvictedDomainCookieCounter::CompareEvictedCookie(
121 const EvictedCookieMap::iterator evicted_cookie1
,
122 const EvictedCookieMap::iterator evicted_cookie2
) {
123 return evicted_cookie1
->second
->eviction_time
124 < evicted_cookie2
->second
->eviction_time
;
127 void EvictedDomainCookieCounter::GarbageCollect(const Time
& current_time
) {
128 if (evicted_cookies_
.size() <= max_size_
)
131 // From |evicted_cookies_|, removed all expired cookies, and remove cookies
132 // with the oldest |eviction_time| so that |size_goal| is attained.
133 size_t size_goal
= max_size_
- purge_count_
;
134 // Bound on number of non-expired cookies to remove.
135 size_t remove_quota
= evicted_cookies_
.size() - size_goal
;
136 DCHECK_GT(remove_quota
, 0u);
138 std::vector
<EvictedCookieMap::iterator
> remove_list
;
139 remove_list
.reserve(evicted_cookies_
.size());
141 EvictedCookieMap::iterator it
= evicted_cookies_
.begin();
142 while (it
!= evicted_cookies_
.end()) {
143 if (it
->second
->is_expired(current_time
)) {
145 evicted_cookies_
.erase(it
++); // Post-increment idiom for in-loop removal.
149 if (remove_quota
) // Don't bother storing if quota met.
150 remove_list
.push_back(it
);
155 // Free the oldest |remove_quota| non-expired cookies.
156 std::partial_sort(remove_list
.begin(), remove_list
.begin() + remove_quota
,
157 remove_list
.end(), CompareEvictedCookie
);
158 for (size_t i
= 0; i
< remove_quota
; ++i
) {
159 delete remove_list
[i
]->second
;
160 evicted_cookies_
.erase(remove_list
[i
]);
163 // Apply stricter check if non-expired cookies were deleted.
164 DCHECK(remove_quota
? evicted_cookies_
.size() == size_goal
:
165 evicted_cookies_
.size() <= size_goal
);
168 void EvictedDomainCookieCounter::StoreEvictedCookie(
169 const EvictedCookieKey
& key
,
170 const net::CanonicalCookie
& cookie
,
171 const Time
& current_time
) {
172 bool is_google
= google_util::IsGoogleHostname(
173 cookie
.Domain(), google_util::ALLOW_SUBDOMAIN
);
174 EvictedCookie
* evicted_cookie
=
175 new EvictedCookie(current_time
, cookie
.ExpiryDate(), is_google
);
176 std::pair
<EvictedCookieMap::iterator
, bool> prev_entry
=
177 evicted_cookies_
.insert(
178 EvictedCookieMap::value_type(key
, evicted_cookie
));
179 if (!prev_entry
.second
) {
181 delete prev_entry
.first
->second
;
182 prev_entry
.first
->second
= evicted_cookie
;
185 GarbageCollect(current_time
);
188 void EvictedDomainCookieCounter::ProcessNewCookie(
189 const EvictedCookieKey
& key
,
190 const net::CanonicalCookie
& cc
,
191 const Time
& current_time
) {
192 EvictedCookieMap::iterator it
= evicted_cookies_
.find(key
);
193 if (it
!= evicted_cookies_
.end()) {
194 if (!it
->second
->is_expired(current_time
)) // Reinstatement.
195 cookie_counter_delegate_
->Report(*it
->second
, current_time
);
197 evicted_cookies_
.erase(it
);
201 } // namespace chrome_browser_net