Home | History | Annotate | Download | only in net
      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 "chrome/browser/net/evicted_domain_cookie_counter.h"
      6 
      7 #include <algorithm>
      8 #include <vector>
      9 
     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"
     15 
     16 namespace chrome_browser_net {
     17 
     18 using base::Time;
     19 using base::TimeDelta;
     20 
     21 namespace {
     22 
     23 const size_t kMaxEvictedDomainCookies = 500;
     24 const size_t kPurgeEvictedDomainCookies = 100;
     25 
     26 class DelegateImpl : public EvictedDomainCookieCounter::Delegate {
     27  public:
     28   DelegateImpl();
     29 
     30   // EvictedDomainCookieCounter::Delegate implementation.
     31   virtual void Report(
     32       const EvictedDomainCookieCounter::EvictedCookie& evicted_cookie,
     33       const Time& reinstatement_time) OVERRIDE;
     34   virtual Time CurrentTime() const OVERRIDE;
     35 };
     36 
     37 DelegateImpl::DelegateImpl() {}
     38 
     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 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",
     48                                reinstatement_delay,
     49                                TimeDelta::FromSeconds(1),
     50                                TimeDelta::FromDays(7),
     51                                50);
     52   } else {
     53     UMA_HISTOGRAM_CUSTOM_TIMES("Cookie.ReinstatedCookiesOther",
     54                                reinstatement_delay,
     55                                TimeDelta::FromSeconds(1),
     56                                TimeDelta::FromDays(7),
     57                                50);
     58   }
     59 }
     60 
     61 Time DelegateImpl::CurrentTime() const {
     62   return Time::Now();
     63 }
     64 
     65 }  // namespace
     66 
     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) {
     73 }
     74 
     75 EvictedDomainCookieCounter::EvictedDomainCookieCounter(
     76     scoped_refptr<net::CookieMonster::Delegate> next_cookie_monster_delegate,
     77     scoped_ptr<Delegate> cookie_counter_delegate,
     78     size_t max_size,
     79     size_t purge_count)
     80     : next_cookie_monster_delegate_(next_cookie_monster_delegate),
     81       cookie_counter_delegate_(cookie_counter_delegate.Pass()),
     82       max_size_(max_size),
     83       purge_count_(purge_count) {
     84   DCHECK(cookie_counter_delegate_);
     85   DCHECK_LT(purge_count, max_size_);
     86 }
     87 
     88 EvictedDomainCookieCounter::~EvictedDomainCookieCounter() {
     89   STLDeleteContainerPairSecondPointers(evicted_cookies_.begin(),
     90                                        evicted_cookies_.end());
     91 }
     92 
     93 size_t EvictedDomainCookieCounter::GetStorageSize() const {
     94   return evicted_cookies_.size();
     95 }
     96 
     97 void EvictedDomainCookieCounter::OnCookieChanged(
     98     const net::CanonicalCookie& cookie,
     99     bool removed,
    100     ChangeCause cause) {
    101   EvictedDomainCookieCounter::EvictedCookieKey key(GetKey(cookie));
    102   Time current_time(cookie_counter_delegate_->CurrentTime());
    103   if (removed) {
    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);
    108   }
    109 
    110   if (next_cookie_monster_delegate_.get())
    111     next_cookie_monster_delegate_->OnCookieChanged(cookie, removed, cause);
    112 }
    113 
    114 void EvictedDomainCookieCounter::OnLoaded() {
    115   if (next_cookie_monster_delegate_.get())
    116     next_cookie_monster_delegate_->OnLoaded();
    117 }
    118 
    119 // static
    120 EvictedDomainCookieCounter::EvictedCookieKey
    121     EvictedDomainCookieCounter::GetKey(const net::CanonicalCookie& cookie) {
    122   return cookie.Domain() + ";" + cookie.Path() + ";" + cookie.Name();
    123 }
    124 
    125 // static
    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;
    131 }
    132 
    133 void EvictedDomainCookieCounter::GarbageCollect(const Time& current_time) {
    134   if (evicted_cookies_.size() <= max_size_)
    135     return;
    136 
    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);
    143 
    144   std::vector<EvictedCookieMap::iterator> remove_list;
    145   remove_list.reserve(evicted_cookies_.size());
    146 
    147   EvictedCookieMap::iterator it = evicted_cookies_.begin();
    148   while (it != evicted_cookies_.end()) {
    149     if (it->second->is_expired(current_time)) {
    150       delete it->second;
    151       evicted_cookies_.erase(it++); // Post-increment idiom for in-loop removal.
    152       if (remove_quota)
    153         --remove_quota;
    154     } else {
    155       if (remove_quota)  // Don't bother storing if quota met.
    156         remove_list.push_back(it);
    157       ++it;
    158     }
    159   }
    160 
    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]);
    167   }
    168 
    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);
    172 }
    173 
    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) {
    186     NOTREACHED();
    187     delete prev_entry.first->second;
    188     prev_entry.first->second = evicted_cookie;
    189   }
    190 
    191   GarbageCollect(current_time);
    192 }
    193 
    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);
    202     delete it->second;
    203     evicted_cookies_.erase(it);
    204   }
    205 }
    206 
    207 }  // namespace chrome_browser_net
    208