Chromium Code Reviews| Index: chrome/browser/net/evicted_domain_cookie_counter.cc |
| diff --git a/chrome/browser/net/evicted_domain_cookie_counter.cc b/chrome/browser/net/evicted_domain_cookie_counter.cc |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..080688b3f0b9287719be200518b90175fd2b036f |
| --- /dev/null |
| +++ b/chrome/browser/net/evicted_domain_cookie_counter.cc |
| @@ -0,0 +1,203 @@ |
| +// Copyright 2013 The Chromium Authors. All rights reserved. |
| +// Use of this source code is governed by a BSD-style license that can be |
| +// found in the LICENSE file. |
| + |
| +#include "chrome/browser/net/evicted_domain_cookie_counter.h" |
| + |
| +#include <algorithm> |
| +#include <vector> |
| + |
| +#include "base/metrics/histogram.h" |
| +#include "base/stl_util.h" |
| +#include "base/string_util.h" |
| +#include "chrome/browser/google/google_util.h" |
| +#include "net/cookies/canonical_cookie.h" |
| + |
| +namespace chrome_browser_net { |
| + |
| +using base::Time; |
| +using base::TimeDelta; |
| + |
| +namespace { |
| + |
| +const size_t kMaxEvictedDomainCookies = 500; |
| +const size_t kPurgeEvictedDomainCookies = 100; |
| + |
| +class DelegateImpl : public EvictedDomainCookieCounter::Delegate { |
| + public: |
| + DelegateImpl(); |
| + |
| + // EvictedDomainCookieCounter::Delegate implementation. |
| + virtual void Report( |
| + const EvictedDomainCookieCounter::EvictedCookie& evicted_cookie, |
| + const Time& reinstatement_time) OVERRIDE; |
| + virtual Time CurrentTime() const OVERRIDE; |
| +}; |
| + |
| +DelegateImpl::DelegateImpl() {} |
| + |
| +void DelegateImpl::Report( |
| + const EvictedDomainCookieCounter::EvictedCookie& evicted_cookie, |
| + const Time& reinstatement_time) { |
| + TimeDelta reinstatement_delay( |
| + reinstatement_time - evicted_cookie.eviction_time); |
| + // Need to duplicate HISTOGRAM_CUSTOM_TIMES(), since it is a macro that |
| + // defines a static variable. |
| + if (evicted_cookie.is_google) { |
| + HISTOGRAM_CUSTOM_TIMES("Cookie.ReinstatedCookiesGoogle", |
| + reinstatement_delay, |
| + TimeDelta::FromSeconds(1), |
| + TimeDelta::FromDays(7), |
| + 50); |
| + } else { |
| + HISTOGRAM_CUSTOM_TIMES("Cookie.ReinstatedCookiesOther", |
| + reinstatement_delay, |
| + TimeDelta::FromSeconds(1), |
| + TimeDelta::FromDays(7), |
| + 50); |
| + } |
| +} |
| + |
| +Time DelegateImpl::CurrentTime() const { |
| + return Time::Now(); |
| +} |
| + |
| +} // namespace |
| + |
| +EvictedDomainCookieCounter::EvictedDomainCookieCounter( |
| + scoped_refptr<net::CookieMonster::Delegate> next_cookie_monster_delegate) |
| + : next_cookie_monster_delegate_(next_cookie_monster_delegate), |
| + cookie_counter_delegate_(new DelegateImpl), |
| + max_size_(kMaxEvictedDomainCookies), |
| + purge_count_(kPurgeEvictedDomainCookies) { |
| +} |
| + |
| +EvictedDomainCookieCounter::EvictedDomainCookieCounter( |
| + scoped_refptr<net::CookieMonster::Delegate> next_cookie_monster_delegate, |
| + scoped_ptr<Delegate> cookie_counter_delegate, |
| + size_t max_size, |
| + size_t purge_count) |
| + : next_cookie_monster_delegate_(next_cookie_monster_delegate), |
| + cookie_counter_delegate_(cookie_counter_delegate.Pass()), |
| + max_size_(max_size), |
| + purge_count_(purge_count) { |
| + DCHECK(cookie_counter_delegate_); |
| + DCHECK_LT(purge_count, max_size_); |
| +} |
| + |
| +EvictedDomainCookieCounter::~EvictedDomainCookieCounter() { |
| + STLDeleteContainerPairSecondPointers(evicted_cookies_.begin(), |
| + evicted_cookies_.end()); |
| +} |
| + |
| +size_t EvictedDomainCookieCounter::GetStorageSize() const { |
| + return evicted_cookies_.size(); |
| +} |
| + |
| +void EvictedDomainCookieCounter::OnCookieChanged( |
| + const net::CanonicalCookie& cookie, |
| + bool removed, |
| + ChangeCause cause) { |
| + EvictedDomainCookieCounter::EvictedCookieKey key(GetKey(cookie)); |
| + Time current_time(cookie_counter_delegate_->CurrentTime()); |
| + if (removed) { |
| + if (cause == net::CookieMonster::Delegate::CHANGE_COOKIE_EVICTED) |
| + StoreEvictedCookie(key, cookie, current_time); |
| + } else { // Includes adds or updates. |
| + ProcessNewCookie(key, cookie, current_time); |
| + } |
| + |
| + if (next_cookie_monster_delegate_) |
| + next_cookie_monster_delegate_->OnCookieChanged(cookie, removed, cause); |
| +} |
| + |
| +// static |
| +EvictedDomainCookieCounter::EvictedCookieKey |
| + EvictedDomainCookieCounter::GetKey(const net::CanonicalCookie& cookie) { |
| + return cookie.Domain() + ";" + cookie.Path() + ";" + cookie.Name(); |
| +} |
| + |
| +// static |
| +// Comparator for sorting, to make recently evicted cookies appear earlier. |
|
mmenke
2013/04/03 14:27:36
One more nit - function-level comments should go w
huangs
2013/04/03 15:42:15
Done.
|
| +bool EvictedDomainCookieCounter::CompareEvictedCookie( |
|
huangs
2013/04/02 19:21:52
Making this a static member (instead of local func
|
| + const EvictedCookieMap::iterator evicted_cookie1, |
| + const EvictedCookieMap::iterator evicted_cookie2) { |
| + return evicted_cookie1->second->eviction_time |
| + < evicted_cookie2->second->eviction_time; |
| +} |
| + |
| +void EvictedDomainCookieCounter::GarbageCollect(const Time& current_time) { |
| + if (evicted_cookies_.size() <= max_size_) |
| + return; |
| + |
| + // From |evicted_cookies_|, removed all expired cookies, and remove cookies |
| + // with the oldest |eviction_time| so that |size_goal| is attained. |
| + size_t size_goal = max_size_ - purge_count_; |
| + // Bound on number of non-expired cookies to remove. |
| + size_t remove_quota = evicted_cookies_.size() - size_goal; |
| + DCHECK_GT(remove_quota, static_cast<size_t>(0)); |
| + |
| + std::vector<EvictedCookieMap::iterator> remove_list; |
| + remove_list.reserve(evicted_cookies_.size()); |
| + |
| + EvictedCookieMap::iterator it = evicted_cookies_.begin(); |
| + while (it != evicted_cookies_.end()) { |
| + if (it->second->is_expired(current_time)) { |
| + delete it->second; |
| + evicted_cookies_.erase(it++); // Post-increment idiom for in-loop removal. |
| + if (remove_quota) |
| + --remove_quota; |
| + } else { |
| + if (remove_quota) // Don't bother storing if quota met. |
| + remove_list.push_back(it); |
| + ++it; |
| + } |
| + } |
| + |
| + // Free the oldest |remove_quota| non-expired cookies. |
| + std::partial_sort(remove_list.begin(), remove_list.begin() + remove_quota, |
| + remove_list.end(), CompareEvictedCookie); |
| + for (size_t i = 0; i < remove_quota; ++i) { |
| + delete remove_list[i]->second; |
| + evicted_cookies_.erase(remove_list[i]); |
| + } |
| + |
| + // Apply stricter check if non-expired cookies were deleted. |
| + DCHECK(remove_quota ? evicted_cookies_.size() == size_goal : |
| + evicted_cookies_.size() <= size_goal); |
| +} |
| + |
| +void EvictedDomainCookieCounter::StoreEvictedCookie( |
| + const EvictedCookieKey& key, |
| + const net::CanonicalCookie& cookie, |
| + const Time& current_time) { |
| + bool is_google = google_util::IsGoogleHostname( |
| + cookie.Domain(), google_util::ALLOW_SUBDOMAIN); |
| + EvictedCookie* evicted_cookie = |
| + new EvictedCookie(current_time, cookie.ExpiryDate(), is_google); |
| + std::pair<EvictedCookieMap::iterator, bool> prev_entry = |
| + evicted_cookies_.insert( |
| + EvictedCookieMap::value_type(key, evicted_cookie)); |
| + if (!prev_entry.second) { |
| + NOTREACHED(); |
| + delete prev_entry.first->second; |
| + prev_entry.first->second = evicted_cookie; |
| + } |
| + |
| + GarbageCollect(current_time); |
| +} |
| + |
| +void EvictedDomainCookieCounter::ProcessNewCookie( |
| + const EvictedCookieKey& key, |
| + const net::CanonicalCookie& cc, |
| + const Time& current_time) { |
| + EvictedCookieMap::iterator it = evicted_cookies_.find(key); |
| + if (it != evicted_cookies_.end()) { |
| + if (!it->second->is_expired(current_time)) // Reinstatement. |
| + cookie_counter_delegate_->Report(*it->second, current_time); |
| + delete it->second; |
| + evicted_cookies_.erase(it); |
| + } |
| +} |
| + |
| +} // namespace chrome_browser_net |