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

Unified Diff: content/browser/net/sqlite_persistent_cookie_store.cc

Issue 1134113007: Revert of Moves SQLitePersistentCookieStore to net/extras/sqlite. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@cookies
Patch Set: Created 5 years, 7 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 side-by-side diff with in-line comments
Download patch
Index: content/browser/net/sqlite_persistent_cookie_store.cc
diff --git a/content/browser/net/sqlite_persistent_cookie_store.cc b/content/browser/net/sqlite_persistent_cookie_store.cc
new file mode 100644
index 0000000000000000000000000000000000000000..19731654fc8e41624676f62c824387ec82e2b453
--- /dev/null
+++ b/content/browser/net/sqlite_persistent_cookie_store.cc
@@ -0,0 +1,1465 @@
+// Copyright (c) 2012 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 "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/files/file_path.h"
+#include "base/files/file_util.h"
+#include "base/location.h"
+#include "base/logging.h"
+#include "base/memory/ref_counted.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/metrics/field_trial.h"
+#include "base/metrics/histogram.h"
+#include "base/profiler/scoped_tracker.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_store_factory.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 "net/extras/sqlite/cookie_crypto_delegate.h"
+#include "sql/error_delegate_util.h"
+#include "sql/meta_table.h"
+#include "sql/statement.h"
+#include "sql/transaction.h"
+#include "storage/browser/quota/special_storage_policy.h"
+#include "third_party/sqlite/sqlite3.h"
+#include "url/gurl.h"
+
+using base::Time;
+
+namespace {
+
+// The persistent cookie store is loaded into memory on eTLD at a time. This
+// variable controls the delay between loading eTLDs, so as to not overload the
+// CPU or I/O with these low priority requests immediately after start up.
+const int kLoadDelayMilliseconds = 0;
+
+} // namespace
+
+namespace content {
+
+// This class is designed to be shared between any client thread and the
+// background task runner. It batches operations and commits them on a timer.
+//
+// SQLitePersistentCookieStore::Load is called to load all cookies. It
+// delegates to Backend::Load, which posts a Backend::LoadAndNotifyOnDBThread
+// task to the background runner. This task calls Backend::ChainLoadCookies(),
+// which repeatedly posts itself to the BG runner to load each eTLD+1's cookies
+// in separate tasks. When this is complete, Backend::CompleteLoadOnIOThread is
+// posted to the client runner, which notifies the caller of
+// SQLitePersistentCookieStore::Load that the load is complete.
+//
+// If a priority load request is invoked via SQLitePersistentCookieStore::
+// LoadCookiesForKey, it is delegated to Backend::LoadCookiesForKey, which posts
+// Backend::LoadKeyAndNotifyOnDBThread to the BG runner. That routine loads just
+// that single domain key (eTLD+1)'s cookies, and posts a Backend::
+// CompleteLoadForKeyOnIOThread to the client runner to notify the caller of
+// SQLitePersistentCookieStore::LoadCookiesForKey that that load is complete.
+//
+// Subsequent to loading, mutations may be queued by any thread using
+// AddCookie, UpdateCookieAccessTime, and DeleteCookie. These are flushed to
+// disk on the BG runner every 30 seconds, 512 operations, or call to Flush(),
+// whichever occurs first.
+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,
+ storage::SpecialStoragePolicy* special_storage_policy,
+ net::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) {}
+
+ // Creates or loads the SQLite database.
+ void Load(const LoadedCallback& loaded_callback);
+
+ // Loads cookies for the domain key (eTLD+1).
+ void LoadCookiesForKey(const std::string& domain,
+ const LoadedCallback& loaded_callback);
+
+ // Steps through all results of |smt|, makes a cookie from each, and adds the
+ // cookie to |cookies|. This method also updates |cookies_per_origin_| and
+ // |num_cookies_read_|.
+ void MakeCookiesFromSQLStatement(std::vector<net::CanonicalCookie*>* cookies,
+ sql::Statement* statement);
+
+ // Batch a cookie addition.
+ void AddCookie(const net::CanonicalCookie& cc);
+
+ // Batch a cookie access time update.
+ void UpdateCookieAccessTime(const net::CanonicalCookie& cc);
+
+ // Batch a cookie deletion.
+ void DeleteCookie(const net::CanonicalCookie& cc);
+
+ // Commit pending operations as soon as possible.
+ void Flush(const base::Closure& callback);
+
+ // Commit any pending operations and close the database. This must be called
+ // before the object is destructed.
+ void Close();
+
+ void SetForceKeepSessionState();
+
+ private:
+ friend class base::RefCountedThreadSafe<SQLitePersistentCookieStore::Backend>;
+
+ // You should call Close() before destructing this object.
+ ~Backend() {
+ DCHECK(!db_.get()) << "Close should have already been called.";
+ DCHECK(num_pending_ == 0 && pending_.empty());
+
+ for (net::CanonicalCookie* cookie : cookies_) {
+ delete cookie;
+ }
+ }
+
+ // Database upgrade statements.
+ 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:
+ // Creates or loads the SQLite database on background runner.
+ void LoadAndNotifyInBackground(const LoadedCallback& loaded_callback,
+ const base::Time& posted_at);
+
+ // Loads cookies for the domain key (eTLD+1) on background runner.
+ void LoadKeyAndNotifyInBackground(const std::string& domains,
+ const LoadedCallback& loaded_callback,
+ const base::Time& posted_at);
+
+ // Notifies the CookieMonster when loading completes for a specific domain key
+ // or for all domain keys. Triggers the callback and passes it all cookies
+ // that have been loaded from DB since last IO notification.
+ void Notify(const LoadedCallback& loaded_callback, bool load_success);
+
+ // Sends notification when the entire store is loaded, and reports metrics
+ // for the total time to load and aggregated results from any priority loads
+ // that occurred.
+ void CompleteLoadInForeground(const LoadedCallback& loaded_callback,
+ bool load_success);
+
+ // Sends notification when a single priority load completes. Updates priority
+ // load metric data. The data is sent only after the final load completes.
+ void CompleteLoadForKeyInForeground(const LoadedCallback& loaded_callback,
+ bool load_success,
+ const base::Time& requested_at);
+
+ // Sends all metrics, including posting a ReportMetricsInBackground task.
+ // Called after all priority and regular loading is complete.
+ void ReportMetrics();
+
+ // Sends background-runner owned metrics (i.e., the combined duration of all
+ // BG-runner tasks).
+ void ReportMetricsInBackground();
+
+ // Initialize the data base.
+ bool InitializeDatabase();
+
+ // Loads cookies for the next domain key from the DB, then either reschedules
+ // itself or schedules the provided callback to run on the client runner (if
+ // all domains are loaded).
+ void ChainLoadCookies(const LoadedCallback& loaded_callback);
+
+ // Load all cookies for a set of domains/hosts
+ bool LoadCookiesForDomains(const std::set<std::string>& key);
+
+ // Batch a cookie operation (add or delete)
+ void BatchOperation(PendingOperation::OperationType op,
+ const net::CanonicalCookie& cc);
+ // Commit our pending operations to the database.
+ void Commit();
+ // Close() executed on the background runner.
+ 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);
+
+ // Shared code between the different load strategies to be used after all
+ // cookies have been loaded.
+ void FinishedLoadingCookies(const LoadedCallback& loaded_callback,
+ bool success);
+
+ base::FilePath path_;
+ scoped_ptr<sql::Connection> db_;
+ sql::MetaTable meta_table_;
+
+ typedef std::list<PendingOperation*> PendingOperationsList;
+ PendingOperationsList pending_;
+ PendingOperationsList::size_type num_pending_;
+ // True if the persistent store should skip delete on exit rules.
+ bool force_keep_session_state_;
+ // Guard |cookies_|, |pending_|, |num_pending_|, |force_keep_session_state_|
+ base::Lock lock_;
+
+ // Temporary buffer for cookies loaded from DB. Accumulates cookies to reduce
+ // the number of messages sent to the client runner. Sent back in response to
+ // individual load requests for domain keys or when all loading completes.
+ // Ownership of the cookies in this vector is transferred to the client in
+ // response to individual load requests or when all loading completes.
+ std::vector<net::CanonicalCookie*> cookies_;
+
+ // Map of domain keys(eTLD+1) to domains/hosts that are to be loaded from DB.
+ std::map<std::string, std::set<std::string> > keys_to_load_;
+
+ // Map of (domain keys(eTLD+1), is secure cookie) to number of cookies in the
+ // database.
+ typedef std::pair<std::string, bool> CookieOrigin;
+ typedef std::map<CookieOrigin, int> CookiesPerOriginMap;
+ CookiesPerOriginMap cookies_per_origin_;
+
+ // Indicates if DB has been initialized.
+ bool initialized_;
+
+ // Indicates if the kill-database callback has been scheduled.
+ bool corruption_detected_;
+
+ // If false, we should filter out session cookies when reading the DB.
+ bool restore_old_session_cookies_;
+
+ // Policy defining what data is deleted on shutdown.
+ scoped_refptr<storage::SpecialStoragePolicy> special_storage_policy_;
+
+ // The cumulative time spent loading the cookies on the background runner.
+ // Incremented and reported from the background runner.
+ base::TimeDelta cookie_load_duration_;
+
+ // The total number of cookies read. Incremented and reported on the
+ // background runner.
+ int num_cookies_read_;
+
+ scoped_refptr<base::SequencedTaskRunner> client_task_runner_;
+ scoped_refptr<base::SequencedTaskRunner> background_task_runner_;
+
+ // Guards the following metrics-related properties (only accessed when
+ // starting/completing priority loads or completing the total load).
+ base::Lock metrics_lock_;
+ int num_priority_waiting_;
+ // The total number of priority requests.
+ int total_priority_requests_;
+ // The time when |num_priority_waiting_| incremented to 1.
+ base::Time current_priority_wait_start_;
+ // The cumulative duration of time when |num_priority_waiting_| was greater
+ // than 1.
+ base::TimeDelta priority_wait_duration_;
+ // Class with functions that do cryptographic operations (for protecting
+ // cookies stored persistently).
+ //
+ // Not owned.
+ net::CookieCryptoDelegate* crypto_;
+
+ DISALLOW_COPY_AND_ASSIGN(Backend);
+};
+
+namespace {
+
+// Version number of the database.
+//
+// Version 9 adds a partial index to track non-persistent cookies.
+// Non-persistent cookies sometimes need to be deleted on startup. There are
+// frequently few or no non-persistent cookies, so the partial index allows the
+// deletion to be sped up or skipped, without having to page in the DB.
+//
+// Version 8 adds "first-party only" cookies.
+//
+// Version 7 adds encrypted values. Old values will continue to be used but
+// all new values written will be encrypted on selected operating systems. New
+// records read by old clients will simply get an empty cookie value while old
+// records read by new clients will continue to operate with the unencrypted
+// version. New and old clients alike will always write/update records with
+// what they support.
+//
+// Version 6 adds cookie priorities. This allows developers to influence the
+// order in which cookies are evicted in order to meet domain cookie limits.
+//
+// Version 5 adds the columns has_expires and is_persistent, so that the
+// database can store session cookies as well as persistent cookies. Databases
+// of version 5 are incompatible with older versions of code. If a database of
+// version 5 is read by older code, session cookies will be treated as normal
+// cookies. Currently, these fields are written, but not read anymore.
+//
+// In version 4, we migrated the time epoch. If you open the DB with an older
+// version on Mac or Linux, the times will look wonky, but the file will likely
+// be usable. On Windows version 3 and 4 are the same.
+//
+// Version 3 updated the database to include the last access time, so we can
+// expire them in decreasing order of use when we've reached the maximum
+// number of cookies.
+const int kCurrentVersionNumber = 9;
+const int kCompatibleVersionNumber = 5;
+
+// Possible values for the 'priority' column.
+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;
+}
+
+// Increments a specified TimeDelta by the duration between this object's
+// constructor and destructor. Not thread safe. Multiple instances may be
+// created with the same delta instance as long as their lifetimes are nested.
+// The shortest lived instances have no impact.
+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);
+};
+
+// Initializes the cookies table, returning true on success.
+bool InitTable(sql::Connection* db) {
+ if (db->DoesTableExist("cookies"))
+ return true;
+
+ 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 '',"
+ "firstpartyonly INTEGER NOT NULL DEFAULT 0)",
+ CookiePriorityToDBCookiePriority(net::COOKIE_PRIORITY_DEFAULT)));
+ if (!db->Execute(stmt.c_str()))
+ return false;
+
+ if (!db->Execute("CREATE INDEX domain ON cookies(host_key)"))
+ return false;
+
+#if defined(OS_IOS)
+ // iOS 8.1 and older doesn't support partial indices. iOS 8.2 supports
+ // partial indices.
+ if (!db->Execute("CREATE INDEX is_transient ON cookies(persistent)")) {
+#else
+ if (!db->Execute(
+ "CREATE INDEX is_transient ON cookies(persistent) "
+ "where persistent != 1")) {
+#endif
+ return false;
+ }
+
+ return true;
+}
+
+} // namespace
+
+void SQLitePersistentCookieStore::Backend::Load(
+ const LoadedCallback& loaded_callback) {
+ 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, posted_at));
+}
+
+void SQLitePersistentCookieStore::Backend::CompleteLoadForKeyInForeground(
+ const LoadedCallback& loaded_callback,
+ bool load_success,
+ const::Time& requested_at) {
+ DCHECK(client_task_runner_->RunsTasksOnCurrentThread());
+
+ UMA_HISTOGRAM_CUSTOM_TIMES(
+ "Cookie.TimeKeyLoadTotalWait",
+ base::Time::Now() - requested_at,
+ base::TimeDelta::FromMilliseconds(1), base::TimeDelta::FromMinutes(1),
+ 50);
+
+ 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 false if we were previously initialized but the DB has since been
+ // closed, or if corruption caused a database reset during initialization.
+ 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");
+
+ // Unretained to avoid a ref loop with |db_|.
+ 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();
+
+ // Retrieve all the domains
+ 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();
+
+ // Build a map of domain keys (always eTLD+1) to domains.
+ 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;
+
+ if (!restore_old_session_cookies_)
+ DeleteSessionCookiesOnStartup();
+ 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_) {
+ // Close() has been called on this store.
+ load_success = false;
+ } else if (keys_to_load_.size() > 0) {
+ // Load cookies for the first domain key.
+ 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 is successful and there are more domain keys to be loaded,
+ // then post a background task to continue chain-load;
+ // Otherwise notify on client runner.
+ if (load_success && keys_to_load_.size() > 0) {
+ bool success = background_task_runner_->PostDelayedTask(
+ FROM_HERE,
+ base::Bind(&Backend::ChainLoadCookies, this, loaded_callback),
+ base::TimeDelta::FromMilliseconds(kLoadDelayMilliseconds));
+ if (!success) {
+ LOG(WARNING) << "Failed to post task from " << FROM_HERE.ToString()
+ << " to background_task_runner_.";
+ }
+ } else {
+ FinishedLoadingCookies(loaded_callback, load_success);
+ }
+}
+
+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, firstpartyonly, 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, firstpartyonly, last_access_utc, "
+ "has_expires, persistent, priority FROM cookies WHERE host_key = ? "
+ "AND persistent = 1"));
+ }
+ if (!smt.is_valid()) {
+ smt.Clear(); // Disconnect smt_ref from db_.
+ 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);
+ MakeCookiesFromSQLStatement(&cookies, &smt);
+ smt.Reset(true);
+ }
+ {
+ base::AutoLock locked(lock_);
+ cookies_.insert(cookies_.end(), cookies.begin(), cookies.end());
+ }
+ return true;
+}
+
+void SQLitePersistentCookieStore::Backend::MakeCookiesFromSQLStatement(
+ std::vector<net::CanonicalCookie*>* cookies,
+ sql::Statement* statement) {
+ sql::Statement& smt = *statement;
+ 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(
+ // The "source" URL is not used with persisted cookies.
+ GURL(), // Source
+ smt.ColumnString(2), // name
+ value, // value
+ smt.ColumnString(1), // domain
+ smt.ColumnString(5), // path
+ Time::FromInternalValue(smt.ColumnInt64(0)), // creation_utc
+ Time::FromInternalValue(smt.ColumnInt64(6)), // expires_utc
+ Time::FromInternalValue(smt.ColumnInt64(10)), // last_access_utc
+ smt.ColumnInt(7) != 0, // secure
+ smt.ColumnInt(8) != 0, // httponly
+ smt.ColumnInt(9) != 0, // firstpartyonly
+ DBCookiePriorityToCookiePriority(
+ static_cast<DBCookiePriority>(smt.ColumnInt(13))))); // priority
+ 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_;
+ }
+}
+
+bool SQLitePersistentCookieStore::Backend::EnsureDatabaseVersion() {
+ // Version check.
+ 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) {
+ // The time epoch changed for Mac & Linux in this version to match Windows.
+ // This patch came after the main epoch change happened, so some
+ // developers have "good" times for cookies added by the more recent
+ // versions. So we have to be careful to only update times that are under
+ // the old system (which will appear to be from before 1970 in the new
+ // system). The magic number used below is 1970 in our time units.
+ 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;
+ // Alter the table to add the priority column with a default value.
+ 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;
+ // Alter the table to add empty "encrypted value" column.
+ 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 == 7) {
+ const base::TimeTicks start_time = base::TimeTicks::Now();
+ sql::Transaction transaction(db_.get());
+ if (!transaction.Begin())
+ return false;
+ // Alter the table to add a 'firstpartyonly' column.
+ if (!db_->Execute(
+ "ALTER TABLE cookies "
+ "ADD COLUMN firstpartyonly INTEGER DEFAULT 0")) {
+ LOG(WARNING) << "Unable to update cookie database to version 8.";
+ return false;
+ }
+ ++cur_version;
+ meta_table_.SetVersionNumber(cur_version);
+ meta_table_.SetCompatibleVersionNumber(
+ std::min(cur_version, kCompatibleVersionNumber));
+ transaction.Commit();
+ UMA_HISTOGRAM_TIMES("Cookie.TimeDatabaseMigrationToV8",
+ base::TimeTicks::Now() - start_time);
+ }
+
+ if (cur_version == 8) {
+ const base::TimeTicks start_time = base::TimeTicks::Now();
+ sql::Transaction transaction(db_.get());
+ if (!transaction.Begin())
+ return false;
+
+ if (!db_->Execute("DROP INDEX IF EXISTS cookie_times")) {
+ LOG(WARNING)
+ << "Unable to drop table cookie_times in update to version 9.";
+ return false;
+ }
+
+ if (!db_->Execute(
+ "CREATE INDEX IF NOT EXISTS domain ON cookies(host_key)")) {
+ LOG(WARNING) << "Unable to create index domain in update to version 9.";
+ return false;
+ }
+
+#if defined(OS_IOS)
+ // iOS 8.1 and older doesn't support partial indices. iOS 8.2 supports
+ // partial indices.
+ if (!db_->Execute(
+ "CREATE INDEX IF NOT EXISTS is_transient ON cookies(persistent)")) {
+#else
+ if (!db_->Execute(
+ "CREATE INDEX IF NOT EXISTS is_transient ON cookies(persistent) "
+ "where persistent != 1")) {
+#endif
+ LOG(WARNING)
+ << "Unable to create index is_transient in update to version 9.";
+ return false;
+ }
+ ++cur_version;
+ meta_table_.SetVersionNumber(cur_version);
+ meta_table_.SetCompatibleVersionNumber(
+ std::min(cur_version, kCompatibleVersionNumber));
+ transaction.Commit();
+ UMA_HISTOGRAM_TIMES("Cookie.TimeDatabaseMigrationToV9",
+ base::TimeTicks::Now() - start_time);
+ }
+
+ // Put future migration cases here.
+
+ if (cur_version < kCurrentVersionNumber) {
+ UMA_HISTOGRAM_COUNTS_100("Cookie.CorruptMetaTable", 1);
+
+ meta_table_.Reset();
+ db_.reset(new sql::Connection);
+ if (!sql::Connection::Delete(path_) ||
+ !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) {
+ // Commit every 30 seconds.
+ static const int kCommitIntervalMs = 30 * 1000;
+ // Commit right away if we have more than 512 outstanding operations.
+ static const size_t kCommitAfterBatchSize = 512;
+ DCHECK(!background_task_runner_->RunsTasksOnCurrentThread());
+
+ // We do a full copy of the cookie here, and hopefully just here.
+ 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) {
+ // We've gotten our first entry for this batch, fire off the timer.
+ 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) {
+ // We've reached a big enough batch, fire off a commit now.
+ 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;
+ }
+
+ // Maybe an old timer fired or we are already Close()'ed.
+ 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, firstpartyonly, "
+ "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) {
+ // Free the cookies as we commit them to the database.
+ 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, ""); // value
+ crypto_->EncryptString(po->cc().Value(), &encrypted_value);
+ // BindBlob() immediately makes an internal copy of the data.
+ 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); // encrypted_value
+ }
+ 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.BindInt(9, po->cc().IsFirstPartyOnly());
+ add_smt.BindInt64(10, po->cc().LastAccessDate().ToInternalValue());
+ add_smt.BindInt(11, po->cc().IsPersistent());
+ add_smt.BindInt(12, po->cc().IsPersistent());
+ add_smt.BindInt(13,
+ 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()) {
+ // We want the completion task to run immediately after Commit() returns.
+ // Posting it from here means there is less chance of another task getting
+ // onto the message queue first, than if we posted it from Commit() itself.
+ PostBackgroundTask(FROM_HERE, callback);
+ }
+}
+
+// Fire off a close message to the background runner. We could still have a
+// pending commit timer or Load operations holding references on us, but if/when
+// this fires we will already have been cleaned up and it will be ignored.
+void SQLitePersistentCookieStore::Backend::Close() {
+ if (background_task_runner_->RunsTasksOnCurrentThread()) {
+ InternalBackgroundClose();
+ } else {
+ // Must close the backend on the background runner.
+ PostBackgroundTask(FROM_HERE,
+ base::Bind(&Backend::InternalBackgroundClose, this));
+ }
+}
+
+void SQLitePersistentCookieStore::Backend::InternalBackgroundClose() {
+ DCHECK(background_task_runner_->RunsTasksOnCurrentThread());
+ // Commit any pending operations
+ 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;
+
+ // TODO(shess): Running KillDatabase() multiple times should be
+ // safe.
+ if (corruption_detected_)
+ return;
+
+ corruption_detected_ = true;
+
+ // Don't just do the close/delete here, as we are being called by |db| and
+ // that seems dangerous.
+ // TODO(shess): Consider just calling RazeAndClose() immediately.
+ // db_ may not be safe to reset at this point, but RazeAndClose()
+ // would cause the stack to unwind safely with errors.
+ PostBackgroundTask(FROM_HERE, base::Bind(&Backend::KillDatabase, this));
+}
+
+void SQLitePersistentCookieStore::Backend::KillDatabase() {
+ DCHECK(background_task_runner_->RunsTasksOnCurrentThread());
+
+ if (db_) {
+ // This Backend will now be in-memory only. In a future run we will recreate
+ // the database. Hopefully things go better then!
+ 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());
+ base::Time start_time = base::Time::Now();
+ if (!db_->Execute("DELETE FROM cookies WHERE persistent != 1"))
+ LOG(WARNING) << "Unable to delete session cookies.";
+
+ UMA_HISTOGRAM_TIMES("Cookie.Startup.TimeSpentDeletingCookies",
+ base::Time::Now() - start_time);
+ UMA_HISTOGRAM_COUNTS("Cookie.Startup.NumberOfCookiesDeleted",
+ db_->GetLastChangeCount());
+}
+
+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_.";
+ }
+}
+
+void SQLitePersistentCookieStore::Backend::FinishedLoadingCookies(
+ const LoadedCallback& loaded_callback,
+ bool success) {
+ PostClientTask(FROM_HERE, base::Bind(&Backend::CompleteLoadInForeground, this,
+ loaded_callback, success));
+}
+
+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,
+ storage::SpecialStoragePolicy* special_storage_policy,
+ net::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();
+ // We release our reference to the Backend, though it will probably still have
+ // a reference if the background runner has not run Close() yet.
+}
+
+CookieStoreConfig::CookieStoreConfig()
+ : session_cookie_mode(EPHEMERAL_SESSION_COOKIES),
+ crypto_delegate(NULL) {
+ // Default to an in-memory cookie store.
+}
+
+CookieStoreConfig::CookieStoreConfig(
+ const base::FilePath& path,
+ SessionCookieMode session_cookie_mode,
+ storage::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) {
+ // TODO(bcwhite): Remove ScopedTracker below once crbug.com/483686 is fixed.
+ tracked_objects::ScopedTracker tracking_profile(
+ FROM_HERE_WITH_EXPLICIT_FUNCTION("483686 content::CreateCookieStore"));
+
+ net::CookieMonster* cookie_monster = NULL;
+
+ if (config.path.empty()) {
+ // Empty path means in-memory store.
+ cookie_monster = new net::CookieMonster(NULL, config.cookie_delegate.get());
+ } 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.get()) {
+ client_task_runner =
+ BrowserThread::GetMessageLoopProxyForThread(BrowserThread::IO);
+ }
+
+ if (!background_task_runner.get()) {
+ 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.get(),
+ config.crypto_delegate);
+
+ cookie_monster =
+ new net::CookieMonster(persistent_store, config.cookie_delegate.get());
+ if ((config.session_cookie_mode ==
+ CookieStoreConfig::PERSISTANT_SESSION_COOKIES) ||
+ (config.session_cookie_mode ==
+ CookieStoreConfig::RESTORED_SESSION_COOKIES)) {
+ cookie_monster->SetPersistSessionCookies(true);
+ }
+ }
+
+ return cookie_monster;
+}
+
+} // namespace content
« no previous file with comments | « content/browser/net/sqlite_persistent_cookie_store.h ('k') | content/browser/net/sqlite_persistent_cookie_store_perftest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698