Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(80)

Side by Side Diff: net/cookies/evicted_domain_cookie_counter.cc

Issue 12805010: Implementing EvictedDomainCookieCounter to keep user metrics on cookie eviction and reinstatement. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Created 7 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
(Empty)
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 "net/cookies/evicted_domain_cookie_counter.h"
6
7 #include <limits>
8 #include <queue>
9 #include <vector>
10
11 #include "base/metrics/histogram.h"
12 #include "base/rand_util.h"
13 #include "base/stl_util.h"
14 #include "base/string_util.h"
15 #include "net/base/registry_controlled_domains/registry_controlled_domain.h"
16
17 namespace net {
18
19 using base::Time;
20 using base::TimeDelta;
21
22 namespace {
23
24 const int kSecondsInWeek = 7 * 24 * 60 * 60;
25
26 const size_t kMaxEvictedDomainCookies = 500;
27 const size_t kPurgeEvictedDomainCookies = 100;
28
29 class DelegateImpl : public EvictedDomainCookieCounter::Delegate {
30 public:
31 DelegateImpl();
32
33 // EvictedDomainCookieCounter::Delegate implementation.
34 virtual void Report(const EvictedDomainCookieCounter::EvictedCookie& ec,
35 const Time& reinstatement_time) OVERRIDE;
36 virtual Time CurrentTime() OVERRIDE;
37
38 private:
39 void InitializeHistograms();
40
41 // Histogram variables; see CookieMonster::InitializeHistograms() in
erikwright (departed) 2013/03/21 19:22:19 Please read the comment in InitializeHistograms th
huangs 2013/03/21 22:08:09 Done.
42 // cookie_monster.cc for details.
43 base::HistogramBase* histogram_reinstated_cookies_google_;
44 base::HistogramBase* histogram_reinstated_cookies_other_;
45 };
46
47 DelegateImpl::DelegateImpl() {
48 InitializeHistograms();
49 }
50
51 void DelegateImpl::Report(const EvictedDomainCookieCounter::EvictedCookie& ec,
52 const Time& reinstatement_time) {
53 TimeDelta reinstatement_delay(reinstatement_time - ec.eviction_time_);
54 if (ec.is_google_)
55 histogram_reinstated_cookies_google_->AddTime(reinstatement_delay);
56 else
57 histogram_reinstated_cookies_other_->AddTime(reinstatement_delay);
58 }
59
60 Time DelegateImpl::CurrentTime() {
61 return Time::Now();
62 }
63
64 // Initialize all histogram counter variables used in this class.
65 // See CookieMonster::InitializeHistograms() for more details.
66 void DelegateImpl::InitializeHistograms() {
67 // From UMA_HISTOGRAM_CUSTOM_COUNTS
68 histogram_reinstated_cookies_google_ = base::Histogram::FactoryGet(
69 "Cookie.ReinstatedCookiesGoogle",
70 1, kSecondsInWeek, 50,
71 base::Histogram::kUmaTargetedHistogramFlag);
72 histogram_reinstated_cookies_other_ = base::Histogram::FactoryGet(
73 "Cookie.ReinstatedCookiesOther",
74 1, kSecondsInWeek, 50,
75 base::Histogram::kUmaTargetedHistogramFlag);
76 }
77
78 } // namespace
79
80 EvictedDomainCookieCounter::EvictedDomainCookieCounter(
81 CookieMonster::Delegate* next_delegate)
82 : next_delegate_(next_delegate),
83 reporter_(new DelegateImpl),
84 max_size_(kMaxEvictedDomainCookies),
85 purge_count_(kPurgeEvictedDomainCookies) {
86 }
87
88 EvictedDomainCookieCounter::EvictedDomainCookieCounter(
89 scoped_refptr<CookieMonster::Delegate> next_delegate,
90 scoped_ptr<Delegate> reporter,
91 size_t max_size,
92 size_t purge_count)
93 : next_delegate_(next_delegate),
94 reporter_(reporter.Pass()),
95 max_size_(max_size),
96 purge_count_(purge_count) {
97 }
98
99 EvictedDomainCookieCounter::~EvictedDomainCookieCounter() {
100 STLDeleteContainerPairSecondPointers(evicted_cookies_.begin(),
101 evicted_cookies_.end());
102 }
103
104 // static
105 // Simplified from google_util.cc: IsGoogleHostname().
106 bool EvictedDomainCookieCounter::IsGoogleCookie(const CanonicalCookie& cc) {
erikwright (departed) 2013/03/21 19:22:19 This implementation is probably fine for the purpo
huangs 2013/03/21 22:08:09 Done.
107 const std::string& host(cc.Domain());
108 size_t tld_length =
109 RegistryControlledDomainService::GetRegistryLength(host, false);
110 if ((tld_length == 0) || (tld_length == std::string::npos))
111 return false;
112
113 std::string host_minus_tld(host, 0, host.length() - tld_length);
114 return LowerCaseEqualsASCII(host_minus_tld, "google.") ||
115 EndsWith(host_minus_tld, ".google.", false);
116 }
117
118 size_t EvictedDomainCookieCounter::GetStorageSize() const {
119 return evicted_cookies_.size();
120 }
121
122 void EvictedDomainCookieCounter::OnCookieChanged(const CanonicalCookie& cc,
123 bool removed,
124 ChangeCause cause) {
125 EvictedDomainCookieCounter::EvictedCookieKey key(GetKey(cc));
126 Time current(reporter_->CurrentTime());
127 if (removed) {
128 if (cause == CookieMonster::Delegate::CHANGE_COOKIE_EVICTED)
129 StoreEvictedCookie(key, cc, current);
130 else // Explicit deletion, so stop tracking.
131 FreeEvictedCookie(key);
132 } else { // Including adds or updates.
133 if (ProcessCookieReinstatement(key, cc, current))
134 FreeEvictedCookie(key);
135 }
136
137 if (next_delegate_)
138 next_delegate_->OnCookieChanged(cc, removed, cause);
139 }
140
141 // static
142 EvictedDomainCookieCounter::EvictedCookieKey
143 EvictedDomainCookieCounter::GetKey(const CanonicalCookie& cc) {
144 return cc.Domain() + ";" + cc.Path() + ";" + cc.Name();
145 }
146
147 void EvictedDomainCookieCounter::FreeEvictedCookie(
148 const EvictedCookieKey& key) {
149 EvictedCookieMap::iterator it = evicted_cookies_.find(key);
150 if (it != evicted_cookies_.end()) {
151 delete it->second;
152 evicted_cookies_.erase(it);
153 }
154 }
155
156 // |current| is passed by value, in case it came from a to-be-deleted element.
erikwright (departed) 2013/03/21 19:22:19 This comment makes no sense? Why can't it be const
huangs 2013/03/21 22:08:09 For the current usage it's fine. But if we pass b
157 void EvictedDomainCookieCounter::FreeExpiredEvictedCookies(Time current) {
158 EvictedCookieMap::iterator it = evicted_cookies_.begin();
159 while (it != evicted_cookies_.end()) {
160 if (it->second->is_expired(current)) {
161 delete it->second;
162 evicted_cookies_.erase(it++); // Post-increment idiom.
163 } else {
164 ++it;
165 }
166 }
167 }
168
169 void EvictedDomainCookieCounter::GarbageCollect(const Time& current) {
erikwright (departed) 2013/03/21 19:22:19 The current implementation seems very costly, sinc
huangs 2013/03/21 22:08:09 Per discussion, only executed when exceeding max_s
170 if (evicted_cookies_.size() < max_size_)
171 return;
172
173 // From |evicted_cookies_|, removed all expired cookies, and remove cookies
174 // with the oldest |eviction_time_| until |size_goal| is attained.
175 size_t size_goal = max_size_ - purge_count_;
176 // Bound on number of non-expired cookies to remove.
177 size_t remove_quota = evicted_cookies_.size() - size_goal;
178 DCHECK(remove_quota > 0);
179
180 // |rand_num| is a tie-breaker to prevent domain-dependent bias when choosing
181 // among evicted cookies that have the same eviction time.
182 struct EvictedCookiePriorityQueueEntry {
183 EvictedCookie* cookie_ptr;
184 int rand_num;
185
186 EvictedCookiePriorityQueueEntry(EvictedCookie* cookie_ptr)
187 : cookie_ptr(cookie_ptr),
188 rand_num(base::RandInt(0, std::numeric_limits<int>::max())) {}
189
190 bool operator < (const EvictedCookiePriorityQueueEntry& other) const {
191 return (cookie_ptr->eviction_time_ != other.cookie_ptr->eviction_time_) ?
192 cookie_ptr->eviction_time_ < other.cookie_ptr->eviction_time_ :
193 rand_num < other.rand_num;
194 }
195 };
196
197 // Use priority queue to store oldest cookies, resize as necessary.
198 std::priority_queue<EvictedCookiePriorityQueueEntry> oldest_set;
199 for (EvictedCookieMap::iterator it = evicted_cookies_.begin();
200 it != evicted_cookies_.end(); ++it) {
201 if (it->second->is_expired(current)) // Will be removed, so decrase quota.
erikwright (departed) 2013/03/21 19:22:19 typo
erikwright (departed) 2013/03/21 19:22:19 By definition the expired cookies will always be a
huangs 2013/03/21 22:08:09 Done.
huangs 2013/03/21 22:08:09 Per discussion, sort is done by eviction time, not
202 remove_quota = remove_quota ? remove_quota - 1 : 0; // Cap to 0 (size_t).
203 else
204 oldest_set.push(EvictedCookiePriorityQueueEntry(it->second));
205
206 if (oldest_set.size() > remove_quota)
207 oldest_set.pop(); // Spare the youngest element from deletion.
208 }
209 DCHECK(oldest_set.size() <= remove_quota);
210
211 // Mark oldest non-expired cookies as expired, for removal.
212 while (!oldest_set.empty()) {
213 oldest_set.top().cookie_ptr->set_expired();
214 oldest_set.pop();
erikwright (departed) 2013/03/21 19:22:19 Couldn't you just pop and then use the key to dele
huangs 2013/03/21 22:08:09 priority_queue<T>::pop() returns void (STL weirdne
215 }
216
217 FreeExpiredEvictedCookies(current);
218 // Apply stricter check if non-expired cookies were deleted.
219 DCHECK(remove_quota ? evicted_cookies_.size() == size_goal :
220 evicted_cookies_.size() <= size_goal);
221 }
222
223 void EvictedDomainCookieCounter::StoreEvictedCookie(const EvictedCookieKey& key,
224 const CanonicalCookie& cc,
225 const Time& current) {
226 if (!cc.IsExpired(current)) {
227 FreeEvictedCookie(key); // Deallocate explicitly.
228
229 EvictedCookie* ec =
230 new EvictedCookie(current, cc.ExpiryDate(), IsGoogleCookie(cc));
231 evicted_cookies_.insert(EvictedCookieMap::value_type(key, ec));
232
233 GarbageCollect(current);
234 }
235 }
236
237 bool EvictedDomainCookieCounter::ProcessCookieReinstatement(
238 const EvictedCookieKey& key,
239 const CanonicalCookie& cc,
240 const Time& current) {
241 bool ret = false;
242 EvictedCookieMap::iterator it = evicted_cookies_.find(key);
243 if (it != evicted_cookies_.end()) {
244 DCHECK(reporter_);
erikwright (departed) 2013/03/21 19:22:19 I don't think this DCHECK is required here. But it
huangs 2013/03/21 22:08:09 Done.
245 if (!it->second->is_expired(current))
246 reporter_->Report(*it->second, current);
247 ret = true;
248 }
249 return ret;
250 }
251
252 } // namespace net
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698