This source file includes following definitions.
- crypto_
- cc_
- op
- cc
- CookiePriorityToDBCookiePriority
- DBCookiePriorityToCookiePriority
- start_
- InitTable
- Load
- LoadCookiesForKey
- LoadAndNotifyInBackground
- LoadKeyAndNotifyInBackground
- CompleteLoadForKeyInForeground
- ReportMetricsInBackground
- ReportMetrics
- CompleteLoadInForeground
- Notify
- InitializeDatabase
- ChainLoadCookies
- LoadCookiesForDomains
- EnsureDatabaseVersion
- AddCookie
- UpdateCookieAccessTime
- DeleteCookie
- BatchOperation
- Commit
- Flush
- Close
- InternalBackgroundClose
- DeleteSessionCookiesOnShutdown
- DatabaseErrorCallback
- KillDatabase
- SetForceKeepSessionState
- DeleteSessionCookiesOnStartup
- PostBackgroundTask
- PostClientTask
- Load
- LoadCookiesForKey
- AddCookie
- UpdateCookieAccessTime
- DeleteCookie
- SetForceKeepSessionState
- Flush
- crypto_delegate
- crypto_delegate
- CreateCookieStore
#include "content/browser/net/sqlite_persistent_cookie_store.h"
#include <list>
#include <map>
#include <set>
#include <utility>
#include "base/basictypes.h"
#include "base/bind.h"
#include "base/callback.h"
#include "base/command_line.h"
#include "base/file_util.h"
#include "base/files/file_path.h"
#include "base/location.h"
#include "base/logging.h"
#include "base/memory/ref_counted.h"
#include "base/memory/scoped_ptr.h"
#include "base/metrics/histogram.h"
#include "base/sequenced_task_runner.h"
#include "base/strings/string_util.h"
#include "base/strings/stringprintf.h"
#include "base/synchronization/lock.h"
#include "base/threading/sequenced_worker_pool.h"
#include "base/time/time.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/cookie_crypto_delegate.h"
#include "content/public/browser/cookie_store_factory.h"
#include "content/public/common/content_switches.h"
#include "net/base/registry_controlled_domains/registry_controlled_domain.h"
#include "net/cookies/canonical_cookie.h"
#include "net/cookies/cookie_constants.h"
#include "net/cookies/cookie_util.h"
#include "sql/error_delegate_util.h"
#include "sql/meta_table.h"
#include "sql/statement.h"
#include "sql/transaction.h"
#include "third_party/sqlite/sqlite3.h"
#include "url/gurl.h"
#include "webkit/browser/quota/special_storage_policy.h"
using base::Time;
namespace content {
class SQLitePersistentCookieStore::Backend
: public base::RefCountedThreadSafe<SQLitePersistentCookieStore::Backend> {
public:
Backend(
const base::FilePath& path,
const scoped_refptr<base::SequencedTaskRunner>& client_task_runner,
const scoped_refptr<base::SequencedTaskRunner>& background_task_runner,
bool restore_old_session_cookies,
quota::SpecialStoragePolicy* special_storage_policy,
CookieCryptoDelegate* crypto_delegate)
: path_(path),
num_pending_(0),
force_keep_session_state_(false),
initialized_(false),
corruption_detected_(false),
restore_old_session_cookies_(restore_old_session_cookies),
special_storage_policy_(special_storage_policy),
num_cookies_read_(0),
client_task_runner_(client_task_runner),
background_task_runner_(background_task_runner),
num_priority_waiting_(0),
total_priority_requests_(0),
crypto_(crypto_delegate) {}
void Load(const LoadedCallback& loaded_callback);
void LoadCookiesForKey(const std::string& domain,
const LoadedCallback& loaded_callback);
void AddCookie(const net::CanonicalCookie& cc);
void UpdateCookieAccessTime(const net::CanonicalCookie& cc);
void DeleteCookie(const net::CanonicalCookie& cc);
void Flush(const base::Closure& callback);
void Close();
void SetForceKeepSessionState();
private:
friend class base::RefCountedThreadSafe<SQLitePersistentCookieStore::Backend>;
~Backend() {
DCHECK(!db_.get()) << "Close should have already been called.";
DCHECK(num_pending_ == 0 && pending_.empty());
}
bool EnsureDatabaseVersion();
class PendingOperation {
public:
typedef enum {
COOKIE_ADD,
COOKIE_UPDATEACCESS,
COOKIE_DELETE,
} OperationType;
PendingOperation(OperationType op, const net::CanonicalCookie& cc)
: op_(op), cc_(cc) { }
OperationType op() const { return op_; }
const net::CanonicalCookie& cc() const { return cc_; }
private:
OperationType op_;
net::CanonicalCookie cc_;
};
private:
void LoadAndNotifyInBackground(const LoadedCallback& loaded_callback,
const base::Time& posted_at);
void LoadKeyAndNotifyInBackground(const std::string& domains,
const LoadedCallback& loaded_callback,
const base::Time& posted_at);
void Notify(const LoadedCallback& loaded_callback, bool load_success);
void CompleteLoadInForeground(const LoadedCallback& loaded_callback,
bool load_success);
void CompleteLoadForKeyInForeground(const LoadedCallback& loaded_callback,
bool load_success);
void ReportMetrics();
void ReportMetricsInBackground();
bool InitializeDatabase();
void ChainLoadCookies(const LoadedCallback& loaded_callback);
bool LoadCookiesForDomains(const std::set<std::string>& key);
void BatchOperation(PendingOperation::OperationType op,
const net::CanonicalCookie& cc);
void Commit();
void InternalBackgroundClose();
void DeleteSessionCookiesOnStartup();
void DeleteSessionCookiesOnShutdown();
void DatabaseErrorCallback(int error, sql::Statement* stmt);
void KillDatabase();
void PostBackgroundTask(const tracked_objects::Location& origin,
const base::Closure& task);
void PostClientTask(const tracked_objects::Location& origin,
const base::Closure& task);
base::FilePath path_;
scoped_ptr<sql::Connection> db_;
sql::MetaTable meta_table_;
typedef std::list<PendingOperation*> PendingOperationsList;
PendingOperationsList pending_;
PendingOperationsList::size_type num_pending_;
bool force_keep_session_state_;
base::Lock lock_;
std::vector<net::CanonicalCookie*> cookies_;
std::map<std::string, std::set<std::string> > keys_to_load_;
typedef std::pair<std::string, bool> CookieOrigin;
typedef std::map<CookieOrigin, int> CookiesPerOriginMap;
CookiesPerOriginMap cookies_per_origin_;
bool initialized_;
bool corruption_detected_;
bool restore_old_session_cookies_;
scoped_refptr<quota::SpecialStoragePolicy> special_storage_policy_;
base::TimeDelta cookie_load_duration_;
int num_cookies_read_;
scoped_refptr<base::SequencedTaskRunner> client_task_runner_;
scoped_refptr<base::SequencedTaskRunner> background_task_runner_;
base::Lock metrics_lock_;
int num_priority_waiting_;
int total_priority_requests_;
base::Time current_priority_wait_start_;
base::TimeDelta priority_wait_duration_;
CookieCryptoDelegate* crypto_;
DISALLOW_COPY_AND_ASSIGN(Backend);
};
namespace {
const int kCurrentVersionNumber = 7;
const int kCompatibleVersionNumber = 5;
enum DBCookiePriority {
kCookiePriorityLow = 0,
kCookiePriorityMedium = 1,
kCookiePriorityHigh = 2,
};
DBCookiePriority CookiePriorityToDBCookiePriority(net::CookiePriority value) {
switch (value) {
case net::COOKIE_PRIORITY_LOW:
return kCookiePriorityLow;
case net::COOKIE_PRIORITY_MEDIUM:
return kCookiePriorityMedium;
case net::COOKIE_PRIORITY_HIGH:
return kCookiePriorityHigh;
}
NOTREACHED();
return kCookiePriorityMedium;
}
net::CookiePriority DBCookiePriorityToCookiePriority(DBCookiePriority value) {
switch (value) {
case kCookiePriorityLow:
return net::COOKIE_PRIORITY_LOW;
case kCookiePriorityMedium:
return net::COOKIE_PRIORITY_MEDIUM;
case kCookiePriorityHigh:
return net::COOKIE_PRIORITY_HIGH;
}
NOTREACHED();
return net::COOKIE_PRIORITY_DEFAULT;
}
class IncrementTimeDelta {
public:
explicit IncrementTimeDelta(base::TimeDelta* delta) :
delta_(delta),
original_value_(*delta),
start_(base::Time::Now()) {}
~IncrementTimeDelta() {
*delta_ = original_value_ + base::Time::Now() - start_;
}
private:
base::TimeDelta* delta_;
base::TimeDelta original_value_;
base::Time start_;
DISALLOW_COPY_AND_ASSIGN(IncrementTimeDelta);
};
bool InitTable(sql::Connection* db) {
if (!db->DoesTableExist("cookies")) {
std::string stmt(base::StringPrintf(
"CREATE TABLE cookies ("
"creation_utc INTEGER NOT NULL UNIQUE PRIMARY KEY,"
"host_key TEXT NOT NULL,"
"name TEXT NOT NULL,"
"value TEXT NOT NULL,"
"path TEXT NOT NULL,"
"expires_utc INTEGER NOT NULL,"
"secure INTEGER NOT NULL,"
"httponly INTEGER NOT NULL,"
"last_access_utc INTEGER NOT NULL, "
"has_expires INTEGER NOT NULL DEFAULT 1, "
"persistent INTEGER NOT NULL DEFAULT 1,"
"priority INTEGER NOT NULL DEFAULT %d,"
"encrypted_value BLOB DEFAULT '')",
CookiePriorityToDBCookiePriority(net::COOKIE_PRIORITY_DEFAULT)));
if (!db->Execute(stmt.c_str()))
return false;
}
if (!db->Execute("DROP INDEX IF EXISTS cookie_times"))
return false;
if (!db->Execute("CREATE INDEX IF NOT EXISTS domain ON cookies(host_key)"))
return false;
return true;
}
}
void SQLitePersistentCookieStore::Backend::Load(
const LoadedCallback& loaded_callback) {
DCHECK(!db_.get());
PostBackgroundTask(FROM_HERE, base::Bind(
&Backend::LoadAndNotifyInBackground, this,
loaded_callback, base::Time::Now()));
}
void SQLitePersistentCookieStore::Backend::LoadCookiesForKey(
const std::string& key,
const LoadedCallback& loaded_callback) {
{
base::AutoLock locked(metrics_lock_);
if (num_priority_waiting_ == 0)
current_priority_wait_start_ = base::Time::Now();
num_priority_waiting_++;
total_priority_requests_++;
}
PostBackgroundTask(FROM_HERE, base::Bind(
&Backend::LoadKeyAndNotifyInBackground,
this, key, loaded_callback, base::Time::Now()));
}
void SQLitePersistentCookieStore::Backend::LoadAndNotifyInBackground(
const LoadedCallback& loaded_callback, const base::Time& posted_at) {
DCHECK(background_task_runner_->RunsTasksOnCurrentThread());
IncrementTimeDelta increment(&cookie_load_duration_);
UMA_HISTOGRAM_CUSTOM_TIMES(
"Cookie.TimeLoadDBQueueWait",
base::Time::Now() - posted_at,
base::TimeDelta::FromMilliseconds(1), base::TimeDelta::FromMinutes(1),
50);
if (!InitializeDatabase()) {
PostClientTask(FROM_HERE, base::Bind(
&Backend::CompleteLoadInForeground, this, loaded_callback, false));
} else {
ChainLoadCookies(loaded_callback);
}
}
void SQLitePersistentCookieStore::Backend::LoadKeyAndNotifyInBackground(
const std::string& key,
const LoadedCallback& loaded_callback,
const base::Time& posted_at) {
DCHECK(background_task_runner_->RunsTasksOnCurrentThread());
IncrementTimeDelta increment(&cookie_load_duration_);
UMA_HISTOGRAM_CUSTOM_TIMES(
"Cookie.TimeKeyLoadDBQueueWait",
base::Time::Now() - posted_at,
base::TimeDelta::FromMilliseconds(1), base::TimeDelta::FromMinutes(1),
50);
bool success = false;
if (InitializeDatabase()) {
std::map<std::string, std::set<std::string> >::iterator
it = keys_to_load_.find(key);
if (it != keys_to_load_.end()) {
success = LoadCookiesForDomains(it->second);
keys_to_load_.erase(it);
} else {
success = true;
}
}
PostClientTask(FROM_HERE, base::Bind(
&SQLitePersistentCookieStore::Backend::CompleteLoadForKeyInForeground,
this, loaded_callback, success));
}
void SQLitePersistentCookieStore::Backend::CompleteLoadForKeyInForeground(
const LoadedCallback& loaded_callback,
bool load_success) {
DCHECK(client_task_runner_->RunsTasksOnCurrentThread());
Notify(loaded_callback, load_success);
{
base::AutoLock locked(metrics_lock_);
num_priority_waiting_--;
if (num_priority_waiting_ == 0) {
priority_wait_duration_ +=
base::Time::Now() - current_priority_wait_start_;
}
}
}
void SQLitePersistentCookieStore::Backend::ReportMetricsInBackground() {
UMA_HISTOGRAM_CUSTOM_TIMES(
"Cookie.TimeLoad",
cookie_load_duration_,
base::TimeDelta::FromMilliseconds(1), base::TimeDelta::FromMinutes(1),
50);
}
void SQLitePersistentCookieStore::Backend::ReportMetrics() {
PostBackgroundTask(FROM_HERE, base::Bind(
&SQLitePersistentCookieStore::Backend::ReportMetricsInBackground, this));
{
base::AutoLock locked(metrics_lock_);
UMA_HISTOGRAM_CUSTOM_TIMES(
"Cookie.PriorityBlockingTime",
priority_wait_duration_,
base::TimeDelta::FromMilliseconds(1), base::TimeDelta::FromMinutes(1),
50);
UMA_HISTOGRAM_COUNTS_100(
"Cookie.PriorityLoadCount",
total_priority_requests_);
UMA_HISTOGRAM_COUNTS_10000(
"Cookie.NumberOfLoadedCookies",
num_cookies_read_);
}
}
void SQLitePersistentCookieStore::Backend::CompleteLoadInForeground(
const LoadedCallback& loaded_callback, bool load_success) {
Notify(loaded_callback, load_success);
if (load_success)
ReportMetrics();
}
void SQLitePersistentCookieStore::Backend::Notify(
const LoadedCallback& loaded_callback,
bool load_success) {
DCHECK(client_task_runner_->RunsTasksOnCurrentThread());
std::vector<net::CanonicalCookie*> cookies;
{
base::AutoLock locked(lock_);
cookies.swap(cookies_);
}
loaded_callback.Run(cookies);
}
bool SQLitePersistentCookieStore::Backend::InitializeDatabase() {
DCHECK(background_task_runner_->RunsTasksOnCurrentThread());
if (initialized_ || corruption_detected_) {
return db_ != NULL;
}
base::Time start = base::Time::Now();
const base::FilePath dir = path_.DirName();
if (!base::PathExists(dir) && !base::CreateDirectory(dir)) {
return false;
}
int64 db_size = 0;
if (base::GetFileSize(path_, &db_size))
UMA_HISTOGRAM_COUNTS("Cookie.DBSizeInKB", db_size / 1024 );
db_.reset(new sql::Connection);
db_->set_histogram_tag("Cookie");
db_->set_error_callback(
base::Bind(&SQLitePersistentCookieStore::Backend::DatabaseErrorCallback,
base::Unretained(this)));
if (!db_->Open(path_)) {
NOTREACHED() << "Unable to open cookie DB.";
if (corruption_detected_)
db_->Raze();
meta_table_.Reset();
db_.reset();
return false;
}
if (!EnsureDatabaseVersion() || !InitTable(db_.get())) {
NOTREACHED() << "Unable to open cookie DB.";
if (corruption_detected_)
db_->Raze();
meta_table_.Reset();
db_.reset();
return false;
}
UMA_HISTOGRAM_CUSTOM_TIMES(
"Cookie.TimeInitializeDB",
base::Time::Now() - start,
base::TimeDelta::FromMilliseconds(1), base::TimeDelta::FromMinutes(1),
50);
start = base::Time::Now();
sql::Statement smt(db_->GetUniqueStatement(
"SELECT DISTINCT host_key FROM cookies"));
if (!smt.is_valid()) {
if (corruption_detected_)
db_->Raze();
meta_table_.Reset();
db_.reset();
return false;
}
std::vector<std::string> host_keys;
while (smt.Step())
host_keys.push_back(smt.ColumnString(0));
UMA_HISTOGRAM_CUSTOM_TIMES(
"Cookie.TimeLoadDomains",
base::Time::Now() - start,
base::TimeDelta::FromMilliseconds(1), base::TimeDelta::FromMinutes(1),
50);
base::Time start_parse = base::Time::Now();
for (size_t idx = 0; idx < host_keys.size(); ++idx) {
const std::string& domain = host_keys[idx];
std::string key =
net::registry_controlled_domains::GetDomainAndRegistry(
domain,
net::registry_controlled_domains::INCLUDE_PRIVATE_REGISTRIES);
keys_to_load_[key].insert(domain);
}
UMA_HISTOGRAM_CUSTOM_TIMES(
"Cookie.TimeParseDomains",
base::Time::Now() - start_parse,
base::TimeDelta::FromMilliseconds(1), base::TimeDelta::FromMinutes(1),
50);
UMA_HISTOGRAM_CUSTOM_TIMES(
"Cookie.TimeInitializeDomainMap",
base::Time::Now() - start,
base::TimeDelta::FromMilliseconds(1), base::TimeDelta::FromMinutes(1),
50);
initialized_ = true;
return true;
}
void SQLitePersistentCookieStore::Backend::ChainLoadCookies(
const LoadedCallback& loaded_callback) {
DCHECK(background_task_runner_->RunsTasksOnCurrentThread());
IncrementTimeDelta increment(&cookie_load_duration_);
bool load_success = true;
if (!db_) {
load_success = false;
} else if (keys_to_load_.size() > 0) {
std::map<std::string, std::set<std::string> >::iterator
it = keys_to_load_.begin();
load_success = LoadCookiesForDomains(it->second);
keys_to_load_.erase(it);
}
if (load_success && keys_to_load_.size() > 0) {
PostBackgroundTask(FROM_HERE, base::Bind(
&Backend::ChainLoadCookies, this, loaded_callback));
} else {
PostClientTask(FROM_HERE, base::Bind(
&Backend::CompleteLoadInForeground, this,
loaded_callback, load_success));
if (load_success && !restore_old_session_cookies_)
DeleteSessionCookiesOnStartup();
}
}
bool SQLitePersistentCookieStore::Backend::LoadCookiesForDomains(
const std::set<std::string>& domains) {
DCHECK(background_task_runner_->RunsTasksOnCurrentThread());
sql::Statement smt;
if (restore_old_session_cookies_) {
smt.Assign(db_->GetCachedStatement(
SQL_FROM_HERE,
"SELECT creation_utc, host_key, name, value, encrypted_value, path, "
"expires_utc, secure, httponly, last_access_utc, has_expires, "
"persistent, priority FROM cookies WHERE host_key = ?"));
} else {
smt.Assign(db_->GetCachedStatement(
SQL_FROM_HERE,
"SELECT creation_utc, host_key, name, value, encrypted_value, path, "
"expires_utc, secure, httponly, last_access_utc, has_expires, "
"persistent, priority FROM cookies WHERE host_key = ? "
"AND persistent = 1"));
}
if (!smt.is_valid()) {
smt.Clear();
meta_table_.Reset();
db_.reset();
return false;
}
std::vector<net::CanonicalCookie*> cookies;
std::set<std::string>::const_iterator it = domains.begin();
for (; it != domains.end(); ++it) {
smt.BindString(0, *it);
while (smt.Step()) {
std::string value;
std::string encrypted_value = smt.ColumnString(4);
if (!encrypted_value.empty() && crypto_) {
crypto_->DecryptString(encrypted_value, &value);
} else {
DCHECK(encrypted_value.empty());
value = smt.ColumnString(3);
}
scoped_ptr<net::CanonicalCookie> cc(new net::CanonicalCookie(
GURL(),
smt.ColumnString(2),
value,
smt.ColumnString(1),
smt.ColumnString(5),
Time::FromInternalValue(smt.ColumnInt64(0)),
Time::FromInternalValue(smt.ColumnInt64(6)),
Time::FromInternalValue(smt.ColumnInt64(9)),
smt.ColumnInt(7) != 0,
smt.ColumnInt(8) != 0,
DBCookiePriorityToCookiePriority(
static_cast<DBCookiePriority>(smt.ColumnInt(12)))));
DLOG_IF(WARNING,
cc->CreationDate() > Time::Now()) << L"CreationDate too recent";
cookies_per_origin_[CookieOrigin(cc->Domain(), cc->IsSecure())]++;
cookies.push_back(cc.release());
++num_cookies_read_;
}
smt.Reset(true);
}
{
base::AutoLock locked(lock_);
cookies_.insert(cookies_.end(), cookies.begin(), cookies.end());
}
return true;
}
bool SQLitePersistentCookieStore::Backend::EnsureDatabaseVersion() {
if (!meta_table_.Init(
db_.get(), kCurrentVersionNumber, kCompatibleVersionNumber)) {
return false;
}
if (meta_table_.GetCompatibleVersionNumber() > kCurrentVersionNumber) {
LOG(WARNING) << "Cookie database is too new.";
return false;
}
int cur_version = meta_table_.GetVersionNumber();
if (cur_version == 2) {
sql::Transaction transaction(db_.get());
if (!transaction.Begin())
return false;
if (!db_->Execute("ALTER TABLE cookies ADD COLUMN last_access_utc "
"INTEGER DEFAULT 0") ||
!db_->Execute("UPDATE cookies SET last_access_utc = creation_utc")) {
LOG(WARNING) << "Unable to update cookie database to version 3.";
return false;
}
++cur_version;
meta_table_.SetVersionNumber(cur_version);
meta_table_.SetCompatibleVersionNumber(
std::min(cur_version, kCompatibleVersionNumber));
transaction.Commit();
}
if (cur_version == 3) {
sql::Transaction transaction(db_.get());
transaction.Begin();
#if !defined(OS_WIN)
ignore_result(db_->Execute(
"UPDATE cookies "
"SET creation_utc = creation_utc + 11644473600000000 "
"WHERE rowid IN "
"(SELECT rowid FROM cookies WHERE "
"creation_utc > 0 AND creation_utc < 11644473600000000)"));
ignore_result(db_->Execute(
"UPDATE cookies "
"SET expires_utc = expires_utc + 11644473600000000 "
"WHERE rowid IN "
"(SELECT rowid FROM cookies WHERE "
"expires_utc > 0 AND expires_utc < 11644473600000000)"));
ignore_result(db_->Execute(
"UPDATE cookies "
"SET last_access_utc = last_access_utc + 11644473600000000 "
"WHERE rowid IN "
"(SELECT rowid FROM cookies WHERE "
"last_access_utc > 0 AND last_access_utc < 11644473600000000)"));
#endif
++cur_version;
meta_table_.SetVersionNumber(cur_version);
transaction.Commit();
}
if (cur_version == 4) {
const base::TimeTicks start_time = base::TimeTicks::Now();
sql::Transaction transaction(db_.get());
if (!transaction.Begin())
return false;
if (!db_->Execute("ALTER TABLE cookies "
"ADD COLUMN has_expires INTEGER DEFAULT 1") ||
!db_->Execute("ALTER TABLE cookies "
"ADD COLUMN persistent INTEGER DEFAULT 1")) {
LOG(WARNING) << "Unable to update cookie database to version 5.";
return false;
}
++cur_version;
meta_table_.SetVersionNumber(cur_version);
meta_table_.SetCompatibleVersionNumber(
std::min(cur_version, kCompatibleVersionNumber));
transaction.Commit();
UMA_HISTOGRAM_TIMES("Cookie.TimeDatabaseMigrationToV5",
base::TimeTicks::Now() - start_time);
}
if (cur_version == 5) {
const base::TimeTicks start_time = base::TimeTicks::Now();
sql::Transaction transaction(db_.get());
if (!transaction.Begin())
return false;
std::string stmt(base::StringPrintf(
"ALTER TABLE cookies ADD COLUMN priority INTEGER DEFAULT %d",
CookiePriorityToDBCookiePriority(net::COOKIE_PRIORITY_DEFAULT)));
if (!db_->Execute(stmt.c_str())) {
LOG(WARNING) << "Unable to update cookie database to version 6.";
return false;
}
++cur_version;
meta_table_.SetVersionNumber(cur_version);
meta_table_.SetCompatibleVersionNumber(
std::min(cur_version, kCompatibleVersionNumber));
transaction.Commit();
UMA_HISTOGRAM_TIMES("Cookie.TimeDatabaseMigrationToV6",
base::TimeTicks::Now() - start_time);
}
if (cur_version == 6) {
const base::TimeTicks start_time = base::TimeTicks::Now();
sql::Transaction transaction(db_.get());
if (!transaction.Begin())
return false;
if (!db_->Execute("ALTER TABLE cookies "
"ADD COLUMN encrypted_value BLOB DEFAULT ''")) {
LOG(WARNING) << "Unable to update cookie database to version 7.";
return false;
}
++cur_version;
meta_table_.SetVersionNumber(cur_version);
meta_table_.SetCompatibleVersionNumber(
std::min(cur_version, kCompatibleVersionNumber));
transaction.Commit();
UMA_HISTOGRAM_TIMES("Cookie.TimeDatabaseMigrationToV7",
base::TimeTicks::Now() - start_time);
}
if (cur_version < kCurrentVersionNumber) {
UMA_HISTOGRAM_COUNTS_100("Cookie.CorruptMetaTable", 1);
meta_table_.Reset();
db_.reset(new sql::Connection);
if (!base::DeleteFile(path_, false) ||
!db_->Open(path_) ||
!meta_table_.Init(
db_.get(), kCurrentVersionNumber, kCompatibleVersionNumber)) {
UMA_HISTOGRAM_COUNTS_100("Cookie.CorruptMetaTableRecoveryFailed", 1);
NOTREACHED() << "Unable to reset the cookie DB.";
meta_table_.Reset();
db_.reset();
return false;
}
}
return true;
}
void SQLitePersistentCookieStore::Backend::AddCookie(
const net::CanonicalCookie& cc) {
BatchOperation(PendingOperation::COOKIE_ADD, cc);
}
void SQLitePersistentCookieStore::Backend::UpdateCookieAccessTime(
const net::CanonicalCookie& cc) {
BatchOperation(PendingOperation::COOKIE_UPDATEACCESS, cc);
}
void SQLitePersistentCookieStore::Backend::DeleteCookie(
const net::CanonicalCookie& cc) {
BatchOperation(PendingOperation::COOKIE_DELETE, cc);
}
void SQLitePersistentCookieStore::Backend::BatchOperation(
PendingOperation::OperationType op,
const net::CanonicalCookie& cc) {
static const int kCommitIntervalMs = 30 * 1000;
static const size_t kCommitAfterBatchSize = 512;
DCHECK(!background_task_runner_->RunsTasksOnCurrentThread());
scoped_ptr<PendingOperation> po(new PendingOperation(op, cc));
PendingOperationsList::size_type num_pending;
{
base::AutoLock locked(lock_);
pending_.push_back(po.release());
num_pending = ++num_pending_;
}
if (num_pending == 1) {
if (!background_task_runner_->PostDelayedTask(
FROM_HERE, base::Bind(&Backend::Commit, this),
base::TimeDelta::FromMilliseconds(kCommitIntervalMs))) {
NOTREACHED() << "background_task_runner_ is not running.";
}
} else if (num_pending == kCommitAfterBatchSize) {
PostBackgroundTask(FROM_HERE, base::Bind(&Backend::Commit, this));
}
}
void SQLitePersistentCookieStore::Backend::Commit() {
DCHECK(background_task_runner_->RunsTasksOnCurrentThread());
PendingOperationsList ops;
{
base::AutoLock locked(lock_);
pending_.swap(ops);
num_pending_ = 0;
}
if (!db_.get() || ops.empty())
return;
sql::Statement add_smt(db_->GetCachedStatement(SQL_FROM_HERE,
"INSERT INTO cookies (creation_utc, host_key, name, value, "
"encrypted_value, path, expires_utc, secure, httponly, last_access_utc, "
"has_expires, persistent, priority) "
"VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?)"));
if (!add_smt.is_valid())
return;
sql::Statement update_access_smt(db_->GetCachedStatement(SQL_FROM_HERE,
"UPDATE cookies SET last_access_utc=? WHERE creation_utc=?"));
if (!update_access_smt.is_valid())
return;
sql::Statement del_smt(db_->GetCachedStatement(SQL_FROM_HERE,
"DELETE FROM cookies WHERE creation_utc=?"));
if (!del_smt.is_valid())
return;
sql::Transaction transaction(db_.get());
if (!transaction.Begin())
return;
for (PendingOperationsList::iterator it = ops.begin();
it != ops.end(); ++it) {
scoped_ptr<PendingOperation> po(*it);
switch (po->op()) {
case PendingOperation::COOKIE_ADD:
cookies_per_origin_[
CookieOrigin(po->cc().Domain(), po->cc().IsSecure())]++;
add_smt.Reset(true);
add_smt.BindInt64(0, po->cc().CreationDate().ToInternalValue());
add_smt.BindString(1, po->cc().Domain());
add_smt.BindString(2, po->cc().Name());
if (crypto_) {
std::string encrypted_value;
add_smt.BindCString(3, "");
crypto_->EncryptString(po->cc().Value(), &encrypted_value);
add_smt.BindBlob(4, encrypted_value.data(),
static_cast<int>(encrypted_value.length()));
} else {
add_smt.BindString(3, po->cc().Value());
add_smt.BindBlob(4, "", 0);
}
add_smt.BindString(5, po->cc().Path());
add_smt.BindInt64(6, po->cc().ExpiryDate().ToInternalValue());
add_smt.BindInt(7, po->cc().IsSecure());
add_smt.BindInt(8, po->cc().IsHttpOnly());
add_smt.BindInt64(9, po->cc().LastAccessDate().ToInternalValue());
add_smt.BindInt(10, po->cc().IsPersistent());
add_smt.BindInt(11, po->cc().IsPersistent());
add_smt.BindInt(
12, CookiePriorityToDBCookiePriority(po->cc().Priority()));
if (!add_smt.Run())
NOTREACHED() << "Could not add a cookie to the DB.";
break;
case PendingOperation::COOKIE_UPDATEACCESS:
update_access_smt.Reset(true);
update_access_smt.BindInt64(0,
po->cc().LastAccessDate().ToInternalValue());
update_access_smt.BindInt64(1,
po->cc().CreationDate().ToInternalValue());
if (!update_access_smt.Run())
NOTREACHED() << "Could not update cookie last access time in the DB.";
break;
case PendingOperation::COOKIE_DELETE:
cookies_per_origin_[
CookieOrigin(po->cc().Domain(), po->cc().IsSecure())]--;
del_smt.Reset(true);
del_smt.BindInt64(0, po->cc().CreationDate().ToInternalValue());
if (!del_smt.Run())
NOTREACHED() << "Could not delete a cookie from the DB.";
break;
default:
NOTREACHED();
break;
}
}
bool succeeded = transaction.Commit();
UMA_HISTOGRAM_ENUMERATION("Cookie.BackingStoreUpdateResults",
succeeded ? 0 : 1, 2);
}
void SQLitePersistentCookieStore::Backend::Flush(
const base::Closure& callback) {
DCHECK(!background_task_runner_->RunsTasksOnCurrentThread());
PostBackgroundTask(FROM_HERE, base::Bind(&Backend::Commit, this));
if (!callback.is_null()) {
PostBackgroundTask(FROM_HERE, callback);
}
}
void SQLitePersistentCookieStore::Backend::Close() {
if (background_task_runner_->RunsTasksOnCurrentThread()) {
InternalBackgroundClose();
} else {
PostBackgroundTask(FROM_HERE,
base::Bind(&Backend::InternalBackgroundClose, this));
}
}
void SQLitePersistentCookieStore::Backend::InternalBackgroundClose() {
DCHECK(background_task_runner_->RunsTasksOnCurrentThread());
Commit();
if (!force_keep_session_state_ && special_storage_policy_.get() &&
special_storage_policy_->HasSessionOnlyOrigins()) {
DeleteSessionCookiesOnShutdown();
}
meta_table_.Reset();
db_.reset();
}
void SQLitePersistentCookieStore::Backend::DeleteSessionCookiesOnShutdown() {
DCHECK(background_task_runner_->RunsTasksOnCurrentThread());
if (!db_)
return;
if (!special_storage_policy_.get())
return;
sql::Statement del_smt(db_->GetCachedStatement(
SQL_FROM_HERE, "DELETE FROM cookies WHERE host_key=? AND secure=?"));
if (!del_smt.is_valid()) {
LOG(WARNING) << "Unable to delete cookies on shutdown.";
return;
}
sql::Transaction transaction(db_.get());
if (!transaction.Begin()) {
LOG(WARNING) << "Unable to delete cookies on shutdown.";
return;
}
for (CookiesPerOriginMap::iterator it = cookies_per_origin_.begin();
it != cookies_per_origin_.end(); ++it) {
if (it->second <= 0) {
DCHECK_EQ(0, it->second);
continue;
}
const GURL url(net::cookie_util::CookieOriginToURL(it->first.first,
it->first.second));
if (!url.is_valid() || !special_storage_policy_->IsStorageSessionOnly(url))
continue;
del_smt.Reset(true);
del_smt.BindString(0, it->first.first);
del_smt.BindInt(1, it->first.second);
if (!del_smt.Run())
NOTREACHED() << "Could not delete a cookie from the DB.";
}
if (!transaction.Commit())
LOG(WARNING) << "Unable to delete cookies on shutdown.";
}
void SQLitePersistentCookieStore::Backend::DatabaseErrorCallback(
int error,
sql::Statement* stmt) {
DCHECK(background_task_runner_->RunsTasksOnCurrentThread());
if (!sql::IsErrorCatastrophic(error))
return;
if (corruption_detected_)
return;
corruption_detected_ = true;
PostBackgroundTask(FROM_HERE, base::Bind(&Backend::KillDatabase, this));
}
void SQLitePersistentCookieStore::Backend::KillDatabase() {
DCHECK(background_task_runner_->RunsTasksOnCurrentThread());
if (db_) {
bool success = db_->RazeAndClose();
UMA_HISTOGRAM_BOOLEAN("Cookie.KillDatabaseResult", success);
meta_table_.Reset();
db_.reset();
}
}
void SQLitePersistentCookieStore::Backend::SetForceKeepSessionState() {
base::AutoLock locked(lock_);
force_keep_session_state_ = true;
}
void SQLitePersistentCookieStore::Backend::DeleteSessionCookiesOnStartup() {
DCHECK(background_task_runner_->RunsTasksOnCurrentThread());
if (!db_->Execute("DELETE FROM cookies WHERE persistent == 0"))
LOG(WARNING) << "Unable to delete session cookies.";
}
void SQLitePersistentCookieStore::Backend::PostBackgroundTask(
const tracked_objects::Location& origin, const base::Closure& task) {
if (!background_task_runner_->PostTask(origin, task)) {
LOG(WARNING) << "Failed to post task from " << origin.ToString()
<< " to background_task_runner_.";
}
}
void SQLitePersistentCookieStore::Backend::PostClientTask(
const tracked_objects::Location& origin, const base::Closure& task) {
if (!client_task_runner_->PostTask(origin, task)) {
LOG(WARNING) << "Failed to post task from " << origin.ToString()
<< " to client_task_runner_.";
}
}
SQLitePersistentCookieStore::SQLitePersistentCookieStore(
const base::FilePath& path,
const scoped_refptr<base::SequencedTaskRunner>& client_task_runner,
const scoped_refptr<base::SequencedTaskRunner>& background_task_runner,
bool restore_old_session_cookies,
quota::SpecialStoragePolicy* special_storage_policy,
CookieCryptoDelegate* crypto_delegate)
: backend_(new Backend(path,
client_task_runner,
background_task_runner,
restore_old_session_cookies,
special_storage_policy,
crypto_delegate)) {
}
void SQLitePersistentCookieStore::Load(const LoadedCallback& loaded_callback) {
backend_->Load(loaded_callback);
}
void SQLitePersistentCookieStore::LoadCookiesForKey(
const std::string& key,
const LoadedCallback& loaded_callback) {
backend_->LoadCookiesForKey(key, loaded_callback);
}
void SQLitePersistentCookieStore::AddCookie(const net::CanonicalCookie& cc) {
backend_->AddCookie(cc);
}
void SQLitePersistentCookieStore::UpdateCookieAccessTime(
const net::CanonicalCookie& cc) {
backend_->UpdateCookieAccessTime(cc);
}
void SQLitePersistentCookieStore::DeleteCookie(const net::CanonicalCookie& cc) {
backend_->DeleteCookie(cc);
}
void SQLitePersistentCookieStore::SetForceKeepSessionState() {
backend_->SetForceKeepSessionState();
}
void SQLitePersistentCookieStore::Flush(const base::Closure& callback) {
backend_->Flush(callback);
}
SQLitePersistentCookieStore::~SQLitePersistentCookieStore() {
backend_->Close();
}
CookieStoreConfig::CookieStoreConfig()
: session_cookie_mode(EPHEMERAL_SESSION_COOKIES),
crypto_delegate(NULL) {
}
CookieStoreConfig::CookieStoreConfig(
const base::FilePath& path,
SessionCookieMode session_cookie_mode,
quota::SpecialStoragePolicy* storage_policy,
net::CookieMonsterDelegate* cookie_delegate)
: path(path),
session_cookie_mode(session_cookie_mode),
storage_policy(storage_policy),
cookie_delegate(cookie_delegate),
crypto_delegate(NULL) {
CHECK(!path.empty() || session_cookie_mode == EPHEMERAL_SESSION_COOKIES);
}
CookieStoreConfig::~CookieStoreConfig() {
}
net::CookieStore* CreateCookieStore(const CookieStoreConfig& config) {
net::CookieMonster* cookie_monster = NULL;
if (config.path.empty()) {
cookie_monster = new net::CookieMonster(NULL, config.cookie_delegate);
} else {
scoped_refptr<base::SequencedTaskRunner> client_task_runner =
config.client_task_runner;
scoped_refptr<base::SequencedTaskRunner> background_task_runner =
config.background_task_runner;
if (!client_task_runner) {
client_task_runner =
BrowserThread::GetMessageLoopProxyForThread(BrowserThread::IO);
}
if (!background_task_runner) {
background_task_runner =
BrowserThread::GetBlockingPool()->GetSequencedTaskRunner(
BrowserThread::GetBlockingPool()->GetSequenceToken());
}
SQLitePersistentCookieStore* persistent_store =
new SQLitePersistentCookieStore(
config.path,
client_task_runner,
background_task_runner,
(config.session_cookie_mode ==
CookieStoreConfig::RESTORED_SESSION_COOKIES),
config.storage_policy,
config.crypto_delegate);
cookie_monster =
new net::CookieMonster(persistent_store, config.cookie_delegate);
if ((config.session_cookie_mode ==
CookieStoreConfig::PERSISTANT_SESSION_COOKIES) ||
(config.session_cookie_mode ==
CookieStoreConfig::RESTORED_SESSION_COOKIES)) {
cookie_monster->SetPersistSessionCookies(true);
}
}
if (CommandLine::InitializedForCurrentProcess() &&
CommandLine::ForCurrentProcess()->HasSwitch(
switches::kEnableFileCookies)) {
cookie_monster->SetEnableFileScheme(true);
}
return cookie_monster;
}
}