root/chrome/browser/net/evicted_domain_cookie_counter.cc

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. Report
  2. CurrentTime
  3. purge_count_
  4. purge_count_
  5. GetStorageSize
  6. OnCookieChanged
  7. GetKey
  8. CompareEvictedCookie
  9. GarbageCollect
  10. StoreEvictedCookie
  11. ProcessNewCookie

// 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/strings/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) {
    UMA_HISTOGRAM_CUSTOM_TIMES("Cookie.ReinstatedCookiesGoogle",
                               reinstatement_delay,
                               TimeDelta::FromSeconds(1),
                               TimeDelta::FromDays(7),
                               50);
  } else {
    UMA_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_.get())
    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
bool EvictedDomainCookieCounter::CompareEvictedCookie(
    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, 0u);

  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

/* [<][>][^][v][top][bottom][index][help] */