summaryrefslogtreecommitdiffstats
path: root/chrome/browser/net/sqlite_persistent_cookie_store.cc
diff options
context:
space:
mode:
authorguohui@google.com <guohui@google.com@0039d316-1c4b-4281-b951-d872f2087c98>2011-10-17 17:35:04 +0000
committerguohui@google.com <guohui@google.com@0039d316-1c4b-4281-b951-d872f2087c98>2011-10-17 17:35:04 +0000
commit8562034e402a7a8e778896f817d1fdcfb3d934c9 (patch)
tree7e209a3b76da5d81981363b39e2867edbf84b54c /chrome/browser/net/sqlite_persistent_cookie_store.cc
parentf5796891ec99f4977eb1761166a33a6b22f459fd (diff)
downloadchromium_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.cc276
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(