diff options
author | guohui@google.com <guohui@google.com@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-10-17 17:35:04 +0000 |
---|---|---|
committer | guohui@google.com <guohui@google.com@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-10-17 17:35:04 +0000 |
commit | 8562034e402a7a8e778896f817d1fdcfb3d934c9 (patch) | |
tree | 7e209a3b76da5d81981363b39e2867edbf84b54c /chrome/browser/net/sqlite_persistent_cookie_store.cc | |
parent | f5796891ec99f4977eb1761166a33a6b22f459fd (diff) | |
download | chromium_src-8562034e402a7a8e778896f817d1fdcfb3d934c9.zip chromium_src-8562034e402a7a8e778896f817d1fdcfb3d934c9.tar.gz chromium_src-8562034e402a7a8e778896f817d1fdcfb3d934c9.tar.bz2 |
The change list splits loading of cookies from DB by the domain key(eTLD+1).
BUG=52909
TEST=NONE
TBR=rdsmith@chromium.org
Review URL: http://codereview.chromium.org/8318006
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@105836 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome/browser/net/sqlite_persistent_cookie_store.cc')
-rw-r--r-- | chrome/browser/net/sqlite_persistent_cookie_store.cc | 276 |
1 files changed, 220 insertions, 56 deletions
diff --git a/chrome/browser/net/sqlite_persistent_cookie_store.cc b/chrome/browser/net/sqlite_persistent_cookie_store.cc index b2adff1..632802b 100644 --- a/chrome/browser/net/sqlite_persistent_cookie_store.cc +++ b/chrome/browser/net/sqlite_persistent_cookie_store.cc @@ -5,6 +5,9 @@ #include "chrome/browser/net/sqlite_persistent_cookie_store.h" #include <list> +#include <map> +#include <set> +#include <utility> #include "base/basictypes.h" #include "base/bind.h" @@ -15,11 +18,13 @@ #include "base/memory/scoped_ptr.h" #include "base/metrics/histogram.h" #include "base/string_util.h" +#include "base/synchronization/lock.h" #include "base/threading/thread.h" #include "base/threading/thread_restrictions.h" #include "chrome/browser/diagnostics/sqlite_diagnostics.h" #include "content/browser/browser_thread.h" #include "googleurl/src/gurl.h" +#include "net/base/registry_controlled_domain.h" #include "sql/meta_table.h" #include "sql/statement.h" #include "sql/transaction.h" @@ -27,10 +32,24 @@ using base::Time; // This class is designed to be shared between any calling threads and the -// database thread. It batches operations and commits them on a timer. -// This class expects to be Load()'ed once on any thread. Loading occurs -// asynchronously on the DB thread and the caller will be notified on the IO -// thread. Subsequent to loading, mutations may be queued by any thread using +// database thread. 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 DB thread. This task calls Backend::ChainLoadCookies(), which +// repeatedly posts itself to the DB thread to load each eTLD+1's cookies in +// separate tasks. When this is complete, Backend::NotifyOnIOThread is posted +// to the IO thread, 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 DB thread. That routine loads just +// that single domain key (eTLD+1)'s cookies, and posts a Backend:: +// NotifyOnIOThread to the IO thread 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 DB thread every 30 seconds, 512 operations, or call to Flush(), // whichever occurs first. @@ -41,11 +60,16 @@ class SQLitePersistentCookieStore::Backend : path_(path), db_(NULL), num_pending_(0), - clear_local_state_on_exit_(false) { + clear_local_state_on_exit_(false), + initialized_(false) { } - // Creates or load the SQLite database. - bool Load(const LoadedCallback& loaded_callback); + // 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); // Batch a cookie addition. void AddCookie(const net::CookieMonster::CanonicalCookie& cc); @@ -98,17 +122,30 @@ class SQLitePersistentCookieStore::Backend }; private: - // Creates or load the SQLite database on DB thread. + // Creates or loads the SQLite database on DB thread. void LoadAndNotifyOnDBThread(const LoadedCallback& loaded_callback); - // Notify the CookieMonster when loading complete. + + // Loads cookies for the domain key (eTLD+1) on DB thread. + void LoadKeyAndNotifyOnDBThread(const std::string& domains, + const LoadedCallback& loaded_callback); + + // 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 NotifyOnIOThread( const LoadedCallback& loaded_callback, - bool load_success, - const std::vector<net::CookieMonster::CanonicalCookie*>& cookies); + bool load_success); + // Initialize the data base. bool InitializeDatabase(); - // Load cookies to the data base, and read cookies. - bool LoadInternal(std::vector<net::CookieMonster::CanonicalCookie*>* cookies); + + // Loads cookies for the next domain key from the DB, then either reschedules + // itself or schedules the provided callback to run on the IO thread (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, @@ -127,9 +164,21 @@ class SQLitePersistentCookieStore::Backend PendingOperationsList::size_type num_pending_; // True if the persistent store should be deleted upon destruction. bool clear_local_state_on_exit_; - // Guard |pending_|, |num_pending_| and |clear_local_state_on_exit_|. + // Guard |cookies_|, |pending_|, |num_pending_| and + // |clear_local_state_on_exit_|. base::Lock lock_; + // Temporary buffer for cookies loaded from DB. Accumulates cookies to reduce + // the number of messages sent to the IO thread. Sent back in response to + // individual load requests for domain keys or when all loading completes. + std::vector<net::CookieMonster::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_; + + // Indicates if DB has been initialized. + bool initialized_; + DISALLOW_COPY_AND_ASSIGN(Backend); }; @@ -167,43 +216,91 @@ bool InitTable(sql::Connection* db) { // so we want those people to get it. Ignore errors, since it may exist. db->Execute("CREATE INDEX IF NOT EXISTS cookie_times ON cookies" " (creation_utc)"); + + db->Execute("CREATE INDEX IF NOT EXISTS domain ON cookies(host_key)"); + return true; } } // namespace -bool SQLitePersistentCookieStore::Backend::Load( +void SQLitePersistentCookieStore::Backend::Load( const LoadedCallback& loaded_callback) { // This function should be called only once per instance. DCHECK(!db_.get()); BrowserThread::PostTask( BrowserThread::DB, FROM_HERE, - base::Bind(&Backend::LoadAndNotifyOnDBThread, base::Unretained(this), - loaded_callback)); - return true; + base::Bind(&Backend::LoadAndNotifyOnDBThread, this, loaded_callback)); +} + +void SQLitePersistentCookieStore::Backend::LoadCookiesForKey( + const std::string& key, + const LoadedCallback& loaded_callback) { + BrowserThread::PostTask( + BrowserThread::DB, FROM_HERE, + base::Bind(&Backend::LoadKeyAndNotifyOnDBThread, this, + key, + loaded_callback)); } void SQLitePersistentCookieStore::Backend::LoadAndNotifyOnDBThread( const LoadedCallback& loaded_callback) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB)); - std::vector<net::CookieMonster::CanonicalCookie*> cookies; - bool load_success = LoadInternal(&cookies); + if (!InitializeDatabase()) { + BrowserThread::PostTask( + BrowserThread::IO, FROM_HERE, + base::Bind(&SQLitePersistentCookieStore::Backend::NotifyOnIOThread, + this, loaded_callback, false)); + } else { + ChainLoadCookies(loaded_callback); + } +} + +void SQLitePersistentCookieStore::Backend::LoadKeyAndNotifyOnDBThread( + const std::string& key, + const LoadedCallback& loaded_callback) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB)); + + 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; + } + } - BrowserThread::PostTask(BrowserThread::IO, FROM_HERE, base::Bind( - &SQLitePersistentCookieStore::Backend::NotifyOnIOThread, - base::Unretained(this), loaded_callback, load_success, cookies)); + BrowserThread::PostTask( + BrowserThread::IO, FROM_HERE, + base::Bind(&SQLitePersistentCookieStore::Backend::NotifyOnIOThread, + this, loaded_callback, success)); } void SQLitePersistentCookieStore::Backend::NotifyOnIOThread( const LoadedCallback& loaded_callback, - bool load_success, - const std::vector<net::CookieMonster::CanonicalCookie*>& cookies) { + bool load_success) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); + + std::vector<net::CookieMonster::CanonicalCookie*> cookies; + { + base::AutoLock locked(lock_); + cookies.swap(cookies_); + } + loaded_callback.Run(cookies); } bool SQLitePersistentCookieStore::Backend::InitializeDatabase() { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB)); + + if (initialized_) { + return true; + } + const FilePath dir = path_.DirName(); if (!file_util::PathExists(dir) && !file_util::CreateDirectory(dir)) { return false; @@ -225,47 +322,108 @@ bool SQLitePersistentCookieStore::Backend::InitializeDatabase() { } db_->Preload(); + + // Retrieve all the domains + sql::Statement smt(db_->GetUniqueStatement( + "SELECT DISTINCT host_key FROM cookies")); + + if (!smt) { + NOTREACHED() << "select statement prep failed"; + db_.reset(); + return false; + } + + // Build a map of domain keys (always eTLD+1) to domains. + while (smt.Step()) { + std::string domain = smt.ColumnString(0); + std::string key = + net::RegistryControlledDomainService::GetDomainAndRegistry(domain); + + std::map<std::string, std::set<std::string> >::iterator it = + keys_to_load_.find(key); + if (it == keys_to_load_.end()) + it = keys_to_load_.insert(std::make_pair + (key, std::set<std::string>())).first; + it->second.insert(domain); + } + + initialized_ = true; return true; } -bool SQLitePersistentCookieStore::Backend::LoadInternal( - std::vector<net::CookieMonster::CanonicalCookie*>* cookies) { - if (!InitializeDatabase()) { - return false; +void SQLitePersistentCookieStore::Backend::ChainLoadCookies( + const LoadedCallback& loaded_callback) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB)); + + bool load_success = true; + + 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); } - // Slurp all the cookies into the out-vector. - sql::Statement smt(db_->GetUniqueStatement( - "SELECT creation_utc, host_key, name, value, path, expires_utc, secure, " - "httponly, last_access_utc FROM cookies")); + // If load is successful and there are more domain keys to be loaded, + // then post a DB task to continue chain-load; + // Otherwise notify on IO thread. + if (load_success && keys_to_load_.size() > 0) { + BrowserThread::PostTask( + BrowserThread::DB, FROM_HERE, + base::Bind(&Backend::ChainLoadCookies, this, loaded_callback)); + } else { + BrowserThread::PostTask( + BrowserThread::IO, FROM_HERE, + base::Bind(&SQLitePersistentCookieStore::Backend::NotifyOnIOThread, + this, loaded_callback, load_success)); + } +} + +bool SQLitePersistentCookieStore::Backend::LoadCookiesForDomains( + const std::set<std::string>& domains) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB)); + + sql::Statement smt(db_->GetCachedStatement(SQL_FROM_HERE, + "SELECT creation_utc, host_key, name, value, path, expires_utc, secure, " + "httponly, last_access_utc FROM cookies WHERE host_key = ?")); if (!smt) { NOTREACHED() << "select statement prep failed"; db_.reset(); return false; } - while (smt.Step()) { - scoped_ptr<net::CookieMonster::CanonicalCookie> cc( - new net::CookieMonster::CanonicalCookie( - // The "source" URL is not used with persisted cookies. - GURL(), // Source - smt.ColumnString(2), // name - smt.ColumnString(3), // value - smt.ColumnString(1), // domain - smt.ColumnString(4), // path - std::string(), // TODO(abarth): Persist mac_key - std::string(), // TODO(abarth): Persist mac_algorithm - Time::FromInternalValue(smt.ColumnInt64(0)), // creation_utc - Time::FromInternalValue(smt.ColumnInt64(5)), // expires_utc - Time::FromInternalValue(smt.ColumnInt64(8)), // last_access_utc - smt.ColumnInt(6) != 0, // secure - smt.ColumnInt(7) != 0, // httponly - true)); // has_expires - DLOG_IF(WARNING, - cc->CreationDate() > Time::Now()) << L"CreationDate too recent"; - cookies->push_back(cc.release()); + std::vector<net::CookieMonster::CanonicalCookie*> cookies; + std::set<std::string>::const_iterator it = domains.begin(); + for (; it != domains.end(); ++it) { + smt.BindString(0, *it); + while (smt.Step()) { + scoped_ptr<net::CookieMonster::CanonicalCookie> cc( + new net::CookieMonster::CanonicalCookie( + // The "source" URL is not used with persisted cookies. + GURL(), // Source + smt.ColumnString(2), // name + smt.ColumnString(3), // value + smt.ColumnString(1), // domain + smt.ColumnString(4), // path + std::string(), // TODO(abarth): Persist mac_key + std::string(), // TODO(abarth): Persist mac_algorithm + Time::FromInternalValue(smt.ColumnInt64(0)), // creation_utc + Time::FromInternalValue(smt.ColumnInt64(5)), // expires_utc + Time::FromInternalValue(smt.ColumnInt64(8)), // last_access_utc + smt.ColumnInt(6) != 0, // secure + smt.ColumnInt(7) != 0, // httponly + true)); // has_expires + DLOG_IF(WARNING, + cc->CreationDate() > Time::Now()) << L"CreationDate too recent"; + cookies.push_back(cc.release()); + } + smt.Reset(); + } + { + base::AutoLock locked(lock_); + cookies_.insert(cookies_.end(), cookies.begin(), cookies.end()); } - return true; } @@ -534,8 +692,14 @@ SQLitePersistentCookieStore::~SQLitePersistentCookieStore() { } } -bool SQLitePersistentCookieStore::Load(const LoadedCallback& loaded_callback) { - return backend_->Load(loaded_callback); +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( |