diff options
Diffstat (limited to 'net')
-rw-r--r-- | net/base/cookie_monster.cc | 116 | ||||
-rw-r--r-- | net/base/cookie_monster.h | 71 | ||||
-rw-r--r-- | net/base/cookie_monster_perftest.cc | 1 | ||||
-rw-r--r-- | net/base/cookie_monster_store_test.cc | 59 | ||||
-rw-r--r-- | net/base/cookie_monster_store_test.h | 50 | ||||
-rw-r--r-- | net/base/cookie_monster_unittest.cc | 88 |
6 files changed, 312 insertions, 73 deletions
diff --git a/net/base/cookie_monster.cc b/net/base/cookie_monster.cc index 6afe739..3ee2d40 100644 --- a/net/base/cookie_monster.cc +++ b/net/base/cookie_monster.cc @@ -49,6 +49,7 @@ #include "base/basictypes.h" #include "base/bind.h" +#include "base/callback.h" #include "base/format_macros.h" #include "base/logging.h" #include "base/memory/scoped_ptr.h" @@ -67,6 +68,26 @@ using base::Time; using base::TimeDelta; using base::TimeTicks; +// In steady state, most cookie requests can be satisfied by the in memory +// cookie monster store. However, if a request comes in during the initial +// cookie load, it must be delayed until that load completes. That is done by +// queueing it on CookieMonster::queue_ and running it when notification of +// cookie load completion is received via CookieMonster::OnLoaded. This callback +// is passed to the persistent store from CookieMonster::InitStore(), which is +// called on the first operation invoked on the CookieMonster. +// +// On the browser critical paths (e.g. for loading initial web pages in a +// session restore) it may take too long to wait for the full load. If a cookie +// request is for a specific URL, DoCookieTaskForURL is called, which triggers a +// priority load if the key is not loaded yet by calling PersistentCookieStore +// :: LoadCookiesForKey. The request is queued in CookieMonster::tasks_queued +// and executed upon receiving notification of key load completion via +// CookieMonster::OnKeyLoaded(). If multiple requests for the same eTLD+1 are +// received before key load completion, only the first request calls +// PersistentCookieStore::LoadCookiesForKey, all subsequent requests are queued +// in CookieMonster::tasks_queued and executed upon receiving notification of +// key load completion triggered by the first request for the same eTLD+1. + static const int kMinutesInTenYears = 10 * 365 * 24 * 60; namespace net { @@ -993,7 +1014,7 @@ void CookieMonster::SetCookieWithDetailsAsync( expiration_time, secure, http_only, callback); - DoCookieTask(task); + DoCookieTaskForURL(task, url); } void CookieMonster::GetAllCookiesAsync(const GetCookieListCallback& callback) { @@ -1011,7 +1032,7 @@ void CookieMonster::GetAllCookiesForURLWithOptionsAsync( scoped_refptr<GetAllCookiesForURLWithOptionsTask> task = new GetAllCookiesForURLWithOptionsTask(this, url, options, callback); - DoCookieTask(task); + DoCookieTaskForURL(task, url); } void CookieMonster::GetAllCookiesForURLAsync( @@ -1021,7 +1042,7 @@ void CookieMonster::GetAllCookiesForURLAsync( scoped_refptr<GetAllCookiesForURLWithOptionsTask> task = new GetAllCookiesForURLWithOptionsTask(this, url, options, callback); - DoCookieTask(task); + DoCookieTaskForURL(task, url); } void CookieMonster::DeleteAllAsync(const DeleteCallback& callback) { @@ -1046,7 +1067,7 @@ void CookieMonster::DeleteAllForHostAsync( scoped_refptr<DeleteAllForHostTask> task = new DeleteAllForHostTask(this, url, callback); - DoCookieTask(task); + DoCookieTaskForURL(task, url); } void CookieMonster::DeleteCanonicalCookieAsync( @@ -1066,7 +1087,7 @@ void CookieMonster::SetCookieWithOptionsAsync( scoped_refptr<SetCookieWithOptionsTask> task = new SetCookieWithOptionsTask(this, url, cookie_line, options, callback); - DoCookieTask(task); + DoCookieTaskForURL(task, url); } void CookieMonster::GetCookiesWithOptionsAsync( @@ -1076,7 +1097,7 @@ void CookieMonster::GetCookiesWithOptionsAsync( scoped_refptr<GetCookiesWithOptionsTask> task = new GetCookiesWithOptionsTask(this, url, options, callback); - DoCookieTask(task); + DoCookieTaskForURL(task, url); } void CookieMonster::GetCookiesWithInfoAsync( @@ -1086,7 +1107,7 @@ void CookieMonster::GetCookiesWithInfoAsync( scoped_refptr<GetCookiesWithInfoTask> task = new GetCookiesWithInfoTask(this, url, options, callback); - DoCookieTask(task); + DoCookieTaskForURL(task, url); } void CookieMonster::DeleteCookieAsync(const GURL& url, @@ -1095,15 +1116,14 @@ void CookieMonster::DeleteCookieAsync(const GURL& url, scoped_refptr<DeleteCookieTask> task = new DeleteCookieTask(this, url, cookie_name, callback); - DoCookieTask(task); + DoCookieTaskForURL(task, url); } void CookieMonster::DoCookieTask( const scoped_refptr<CookieMonsterTask>& task_item) { - InitIfNecessary(); - { base::AutoLock autolock(lock_); + InitIfNecessary(); if (!loaded_) { queue_.push(task_item); return; @@ -1113,6 +1133,34 @@ void CookieMonster::DoCookieTask( task_item->Run(); } +void CookieMonster::DoCookieTaskForURL( + const scoped_refptr<CookieMonsterTask>& task_item, + const GURL& url) { + { + base::AutoLock autolock(lock_); + InitIfNecessary(); + // If cookies for the requested domain key (eTLD+1) have been loaded from DB + // then run the task, otherwise load from DB. + if (!loaded_) { + // Checks if the domain key has been loaded. + std::string key(GetEffectiveDomain(url.scheme(), url.host())); + if (keys_loaded_.find(key) == keys_loaded_.end()) { + std::map<std::string, std::deque<scoped_refptr<CookieMonsterTask> > > + ::iterator it = tasks_queued_.find(key); + if (it == tasks_queued_.end()) { + store_->LoadCookiesForKey(key, + base::Bind(&CookieMonster::OnKeyLoaded, this, key)); + it = tasks_queued_.insert(std::make_pair(key, + std::deque<scoped_refptr<CookieMonsterTask> >())).first; + } + it->second.push_back(task_item); + return; + } + } + } + task_item->Run(); +} + bool CookieMonster::SetCookieWithDetails( const GURL& url, const std::string& name, const std::string& value, const std::string& domain, const std::string& path, @@ -1470,6 +1518,30 @@ void CookieMonster::OnLoaded(TimeTicks beginning_time, InvokeQueue(); } +void CookieMonster::OnKeyLoaded(const std::string& key, + const std::vector<CanonicalCookie*>& cookies) { + // This function does its own separate locking. + StoreLoadedCookies(cookies); + + std::deque<scoped_refptr<CookieMonsterTask> > tasks_queued; + { + base::AutoLock autolock(lock_); + keys_loaded_.insert(key); + std::map<std::string, std::deque<scoped_refptr<CookieMonsterTask> > > + ::iterator it = tasks_queued_.find(key); + if (it == tasks_queued_.end()) + return; + it->second.swap(tasks_queued); + tasks_queued_.erase(it); + } + + while (!tasks_queued.empty()) { + scoped_refptr<CookieMonsterTask> task = tasks_queued.front(); + task->Run(); + tasks_queued.pop_front(); + } +} + void CookieMonster::StoreLoadedCookies( const std::vector<CanonicalCookie*>& cookies) { // Initialize the store and sync in any saved persistent cookies. We don't @@ -1477,24 +1549,16 @@ void CookieMonster::StoreLoadedCookies( // and sync'd. base::AutoLock autolock(lock_); - // Avoid ever letting cookies with duplicate creation times into the store; - // that way we don't have to worry about what sections of code are safe - // to call while it's in that state. - std::set<int64> creation_times; - - // Presumably later than any access time in the store. - Time earliest_access_time; - for (std::vector<CanonicalCookie*>::const_iterator it = cookies.begin(); it != cookies.end(); ++it) { int64 cookie_creation_time = (*it)->CreationDate().ToInternalValue(); - if (creation_times.insert(cookie_creation_time).second) { + if (creation_times_.insert(cookie_creation_time).second) { InternalInsertCookie(GetKey((*it)->Domain()), *it, false); const Time cookie_access_time((*it)->LastAccessDate()); - if (earliest_access_time.is_null() || - cookie_access_time < earliest_access_time) - earliest_access_time = cookie_access_time; + if (earliest_access_time_.is_null() || + cookie_access_time < earliest_access_time_) + earliest_access_time_ = cookie_access_time; } else { LOG(ERROR) << base::StringPrintf("Found cookies with duplicate creation " "times in backing store: " @@ -1507,12 +1571,14 @@ void CookieMonster::StoreLoadedCookies( delete (*it); } } - earliest_access_time_= earliest_access_time; // After importing cookies from the PersistentCookieStore, verify that // none of our other constraints are violated. - // // In particular, the backing store might have given us duplicate cookies. + + // This method could be called multiple times due to priority loading, thus + // cookies loaded in previous runs will be validated again, but this is OK + // since they are expected to be much fewer than total DB. EnsureCookiesMapIsValid(); } @@ -1523,6 +1589,8 @@ void CookieMonster::InvokeQueue() { base::AutoLock autolock(lock_); if (queue_.empty()) { loaded_ = true; + creation_times_.clear(); + keys_loaded_.clear(); break; } request_task = queue_.front(); diff --git a/net/base/cookie_monster.h b/net/base/cookie_monster.h index 44fbc6d..9dea152 100644 --- a/net/base/cookie_monster.h +++ b/net/base/cookie_monster.h @@ -8,13 +8,16 @@ #define NET_BASE_COOKIE_MONSTER_H_ #pragma once +#include <deque> #include <map> #include <queue> +#include <set> #include <string> #include <utility> #include <vector> #include "base/basictypes.h" +#include "base/callback.h" #include "base/gtest_prod_util.h" #include "base/memory/ref_counted.h" #include "base/memory/scoped_ptr.h" @@ -29,7 +32,7 @@ class GURL; namespace base { class Histogram; class TimeTicks; -} +} // namespace base namespace net { @@ -43,11 +46,18 @@ class CookieList; // This class IS thread-safe. Normally, it is only used on the I/O thread, but // is also accessed directly through Automation for UI testing. // -// Several methods exist in asynchronous forms. Calls may be deferred if all -// affected cookies are not yet loaded from the backing store. Otherwise, the -// callback may be invoked immediately (prior to return of the asynchronous +// All cookie tasks are handled asynchronously. Tasks may be deferred if +// all affected cookies are not yet loaded from the backing store. Otherwise, +// the callback may be invoked immediately (prior to return of the asynchronous // function). // +// A cookie task is either pending loading of the entire cookie store, or +// loading of cookies for a specfic domain key(eTLD+1). In the former case, the +// cookie task will be queued in queue_ while PersistentCookieStore chain loads +// the cookie store on DB thread. In the latter case, the cookie task will be +// queued in tasks_queued_ while PermanentCookieStore loads cookies for the +// specified domain key(eTLD+1) on DB thread. +// // Callbacks are guaranteed to be invoked on the calling thread. // // TODO(deanm) Implement CookieMonster, the cookie database. @@ -450,9 +460,19 @@ class NET_EXPORT CookieMonster : public CookieStore { // Stores cookies loaded from the backing store and invokes any deferred // calls. |beginning_time| should be the moment PersistentCookieStore::Load // was invoked and is used for reporting histogram_time_load_. + // See PersistentCookieStore::Load for details on the contents of cookies. void OnLoaded(base::TimeTicks beginning_time, const std::vector<CanonicalCookie*>& cookies); + // Stores cookies loaded from the backing store and invokes the deferred + // task(s) pending loading of cookies associated with the domain key + // (eTLD+1). Called when all cookies for the domain key(eTLD+1) have been + // loaded from DB. See PersistentCookieStore::Load for details on the contents + // of cookies. + void OnKeyLoaded( + const std::string& key, + const std::vector<CanonicalCookie*>& cookies); + // Stores the loaded cookies. void StoreLoadedCookies(const std::vector<CanonicalCookie*>& cookies); @@ -568,10 +588,15 @@ class NET_EXPORT CookieMonster : public CookieStore { // ugly and increment when we've seen the same time twice. base::Time CurrentTime(); - // Run the cookie request task if cookie loaded, otherwise added the task - // to task queue. + // Runs the task if, or defers the task until, the full cookie database is + // loaded. void DoCookieTask(const scoped_refptr<CookieMonsterTask>& task_item); + // Runs the task if, or defers the task until, the cookies for the given URL + // are loaded. + void DoCookieTaskForURL(const scoped_refptr<CookieMonsterTask>& task_item, + const GURL& url); + // Histogram variables; see CookieMonster::InitializeHistograms() in // cookie_monster.cc for details. base::Histogram* histogram_expiration_duration_minutes_; @@ -597,8 +622,17 @@ class NET_EXPORT CookieMonster : public CookieStore { // calls may be immediately processed. bool loaded_; - // Queues calls to CookieMonster until loading from the backend store is - // completed. + // List of domain keys that have been loaded from the DB. + std::set<std::string> keys_loaded_; + + // Map of domain keys to their associated task queues. These tasks are blocked + // until all cookies for the associated domain key eTLD+1 are loaded from the + // backend store. + std::map<std::string, std::deque<scoped_refptr<CookieMonsterTask> > > + tasks_queued_; + + // Queues tasks that are blocked until all cookies are loaded from the backend + // store. std::queue<scoped_refptr<CookieMonsterTask> > queue_; // Indicates whether this cookie monster uses the new effective domain @@ -620,10 +654,16 @@ class NET_EXPORT CookieMonster : public CookieStore { // This value is used to determine whether global garbage collection might // find cookies to purge. // Note: The default Time() constructor will create a value that compares - // earlier than any other time value, which is is wanted. Thus this + // earlier than any other time value, which is wanted. Thus this // value is not initialized. base::Time earliest_access_time_; + // During loading, holds the set of all loaded cookie creation times. Used to + // avoid ever letting cookies with duplicate creation times into the store; + // that way we don't have to worry about what sections of code are safe + // to call while it's in that state. + std::set<int64> creation_times_; + std::vector<std::string> cookieable_schemes_; scoped_refptr<Delegate> delegate_; @@ -919,8 +959,17 @@ class CookieMonster::PersistentCookieStore CookieMonster::CanonicalCookie*>&)> LoadedCallback; // Initializes the store and retrieves the existing cookies. This will be - // called only once at startup. - virtual bool Load(const LoadedCallback& loaded_callback) = 0; + // called only once at startup. The callback will return all the cookies + // that are not yet returned to CookieMonster by previous priority loads. + virtual void Load(const LoadedCallback& loaded_callback) = 0; + + // Does a priority load of all cookies for the domain key (eTLD+1). The + // callback will return all the cookies that are not yet returned by previous + // loads, which includes cookies for the requested domain key if they are not + // already returned, plus all cookies that are chain-loaded and not yet + // returned to CookieMonster. + virtual void LoadCookiesForKey(const std::string& key, + const LoadedCallback& loaded_callback) = 0; virtual void AddCookie(const CanonicalCookie& cc) = 0; virtual void UpdateCookieAccessTime(const CanonicalCookie& cc) = 0; diff --git a/net/base/cookie_monster_perftest.cc b/net/base/cookie_monster_perftest.cc index 98a2b5b..a425138 100644 --- a/net/base/cookie_monster_perftest.cc +++ b/net/base/cookie_monster_perftest.cc @@ -63,6 +63,7 @@ class BaseCallback { // Therefore, callbacks will actually always complete synchronously. If the // tests get more advanced we need to add other means of signaling // completion. + MessageLoop::current()->RunAllPending(); EXPECT_TRUE(has_run_); has_run_ = false; } diff --git a/net/base/cookie_monster_store_test.cc b/net/base/cookie_monster_store_test.cc index f8257c3..dc1b495 100644 --- a/net/base/cookie_monster_store_test.cc +++ b/net/base/cookie_monster_store_test.cc @@ -4,6 +4,7 @@ #include "net/base/cookie_monster_store_test.h" +#include "base/bind.h" #include "base/message_loop.h" #include "base/stringprintf.h" #include "base/time.h" @@ -11,9 +12,17 @@ #include "testing/gtest/include/gtest/gtest.h" namespace net { +LoadedCallbackTask::LoadedCallbackTask(LoadedCallback loaded_callback, + std::vector<CookieMonster::CanonicalCookie*> cookies) + : loaded_callback_(loaded_callback), + cookies_(cookies) { +} + +LoadedCallbackTask::~LoadedCallbackTask() {} MockPersistentCookieStore::MockPersistentCookieStore() - : load_return_value_(true) { + : load_return_value_(true), + loaded_(false) { } MockPersistentCookieStore::~MockPersistentCookieStore() {} @@ -25,14 +34,27 @@ void MockPersistentCookieStore::SetLoadExpectation( load_result_ = result; } -bool MockPersistentCookieStore::Load(const LoadedCallback& loaded_callback) { - bool ok = load_return_value_; +void MockPersistentCookieStore::Load(const LoadedCallback& loaded_callback) { std::vector<CookieMonster::CanonicalCookie*> out_cookies; - if (ok) { + if (load_return_value_) { out_cookies = load_result_; + loaded_ = true; + } + MessageLoop::current()->PostTask(FROM_HERE, + base::Bind(&LoadedCallbackTask::Run, + new LoadedCallbackTask(loaded_callback, out_cookies))); +} + +void MockPersistentCookieStore::LoadCookiesForKey(const std::string& key, + const LoadedCallback& loaded_callback) { + if (!loaded_) { + Load(loaded_callback); + } else { + MessageLoop::current()->PostTask(FROM_HERE, + base::Bind(&LoadedCallbackTask::Run, + new LoadedCallbackTask(loaded_callback, + std::vector<CookieMonster::CanonicalCookie*>()))); } - loaded_callback.Run(out_cookies); - return ok; } void MockPersistentCookieStore::AddCookie( @@ -113,19 +135,36 @@ CookieMonster::CanonicalCookie BuildCanonicalCookie( !cookie_expires.is_null()); } -MockSimplePersistentCookieStore::MockSimplePersistentCookieStore() {} +MockSimplePersistentCookieStore::MockSimplePersistentCookieStore() + : loaded_(false) {} MockSimplePersistentCookieStore::~MockSimplePersistentCookieStore() {} -bool MockSimplePersistentCookieStore::Load( +void MockSimplePersistentCookieStore::Load( const LoadedCallback& loaded_callback) { std::vector<CookieMonster::CanonicalCookie*> out_cookies; + for (CanonicalCookieMap::const_iterator it = cookies_.begin(); it != cookies_.end(); it++) out_cookies.push_back( new CookieMonster::CanonicalCookie(it->second)); - loaded_callback.Run(out_cookies); - return true; + + MessageLoop::current()->PostTask(FROM_HERE, + base::Bind(&LoadedCallbackTask::Run, + new LoadedCallbackTask(loaded_callback, out_cookies))); + loaded_ = true; +} + +void MockSimplePersistentCookieStore::LoadCookiesForKey(const std::string& key, + const LoadedCallback& loaded_callback) { + if (!loaded_) { + Load(loaded_callback); + } else { + MessageLoop::current()->PostTask(FROM_HERE, + base::Bind(&LoadedCallbackTask::Run, + new LoadedCallbackTask(loaded_callback, + std::vector<CookieMonster::CanonicalCookie*>()))); + } } void MockSimplePersistentCookieStore::AddCookie( diff --git a/net/base/cookie_monster_store_test.h b/net/base/cookie_monster_store_test.h index 4bae6f9..658e9e8 100644 --- a/net/base/cookie_monster_store_test.h +++ b/net/base/cookie_monster_store_test.h @@ -23,6 +23,29 @@ class Time; namespace net { +// Wrapper class for posting a loaded callback. Since the Callback class is not +// reference counted, we cannot post a callback to the message loop directly, +// instead we post a LoadedCallbackTask. +class LoadedCallbackTask + : public base::RefCountedThreadSafe<LoadedCallbackTask> { + public: + typedef CookieMonster::PersistentCookieStore::LoadedCallback LoadedCallback; + + LoadedCallbackTask(LoadedCallback loaded_callback, + std::vector<CookieMonster::CanonicalCookie*> cookies); + ~LoadedCallbackTask(); + + void Run() { + loaded_callback_.Run(cookies_); + } + + private: + LoadedCallback loaded_callback_; + std::vector<CookieMonster::CanonicalCookie*> cookies_; + + DISALLOW_COPY_AND_ASSIGN(LoadedCallbackTask); +}; // Wrapper class LoadedCallbackTask + // Describes a call to one of the 3 functions of PersistentCookieStore. struct CookieStoreCommand { enum Type { @@ -59,7 +82,10 @@ class MockPersistentCookieStore return commands_; } - virtual bool Load(const LoadedCallback& loaded_callback) OVERRIDE; + virtual void Load(const LoadedCallback& loaded_callback) OVERRIDE; + + virtual void LoadCookiesForKey(const std::string& key, + const LoadedCallback& loaded_callback) OVERRIDE; virtual void AddCookie(const CookieMonster::CanonicalCookie& cookie) OVERRIDE; @@ -80,6 +106,9 @@ class MockPersistentCookieStore // Deferred result to use when Load() is called. bool load_return_value_; std::vector<CookieMonster::CanonicalCookie*> load_result_; + // Indicates if the store has been fully loaded to avoid returning duplicate + // cookies. + bool loaded_; DISALLOW_COPY_AND_ASSIGN(MockPersistentCookieStore); }; @@ -130,26 +159,33 @@ class MockSimplePersistentCookieStore MockSimplePersistentCookieStore(); virtual ~MockSimplePersistentCookieStore(); - virtual bool Load(const LoadedCallback& loaded_callback); + virtual void Load(const LoadedCallback& loaded_callback) OVERRIDE; + + virtual void LoadCookiesForKey(const std::string& key, + const LoadedCallback& loaded_callback) OVERRIDE; virtual void AddCookie( - const CookieMonster::CanonicalCookie& cookie); + const CookieMonster::CanonicalCookie& cookie) OVERRIDE; virtual void UpdateCookieAccessTime( - const CookieMonster::CanonicalCookie& cookie); + const CookieMonster::CanonicalCookie& cookie) OVERRIDE; virtual void DeleteCookie( - const CookieMonster::CanonicalCookie& cookie); + const CookieMonster::CanonicalCookie& cookie) OVERRIDE; - virtual void Flush(Task* completion_task); + virtual void Flush(Task* completion_task) OVERRIDE; - virtual void SetClearLocalStateOnExit(bool clear_local_state); + virtual void SetClearLocalStateOnExit(bool clear_local_state) OVERRIDE; private: typedef std::map<int64, CookieMonster::CanonicalCookie> CanonicalCookieMap; CanonicalCookieMap cookies_; + + // Indicates if the store has been fully loaded to avoid return duplicate + // cookies in subsequent load requests + bool loaded_; }; // Helper function for creating a CookieMonster backed by a diff --git a/net/base/cookie_monster_unittest.cc b/net/base/cookie_monster_unittest.cc index fac72f8..7b53397 100644 --- a/net/base/cookie_monster_unittest.cc +++ b/net/base/cookie_monster_unittest.cc @@ -37,7 +37,9 @@ namespace { class NewMockPersistentCookieStore : public CookieMonster::PersistentCookieStore { public: - MOCK_METHOD1(Load, bool(const LoadedCallback& loaded_callback)); + MOCK_METHOD1(Load, void(const LoadedCallback& loaded_callback)); + MOCK_METHOD2(LoadCookiesForKey, void(const std::string& key, + const LoadedCallback& loaded_callback)); MOCK_METHOD1(AddCookie, void(const CookieMonster::CanonicalCookie& cc)); MOCK_METHOD1(UpdateCookieAccessTime, void(const CookieMonster::CanonicalCookie& cc)); @@ -1008,12 +1010,16 @@ ACTION_P3(GetAllCookiesForUrlAction, cookie_monster, url, callback) { cookie_monster->GetAllCookiesForURLAsync(url, callback->AsCallback()); } +ACTION_P(PushCallbackAction, callback_vector) { + callback_vector->push(arg1); +} } // namespace // This test suite verifies the task deferral behaviour of the CookieMonster. // Specifically, for each asynchronous method, verify that: // 1. invoking it on an uninitialized cookie store causes the store to begin -// loading its backing data. +// chain-loading its backing data or loading data for a specific domain key +// (eTLD+1). // 2. The initial invocation does not complete until the loading completes. // 3. Invocations after the loading has completed complete immediately. class DeferredCookieTaskTest : public CookieMonsterTest { @@ -1039,9 +1045,16 @@ class DeferredCookieTaskTest : public CookieMonsterTest { testing::Mock::VerifyAndClear(persistent_store_.get()); } - // Invokes the PersistentCookieStore::Load completion callback and waits + // Invokes the PersistentCookieStore::LoadCookiesForKey completion callbacks + // and PersistentCookieStore::Load completion callback and waits // until the message loop is quit. void CompleteLoadingAndWait() { + while (!loaded_for_key_callbacks_.empty()) { + loaded_for_key_callbacks_.front().Run(loaded_cookies_); + loaded_cookies_.clear(); + loaded_for_key_callbacks_.pop(); + } + loaded_callback_.Run(loaded_cookies_); RunFor(kTimeout); } @@ -1055,13 +1068,34 @@ class DeferredCookieTaskTest : public CookieMonsterTest { Begin(); } + void BeginWithForDomainKey(std::string key, + testing::Action<void(void)> action) { + EXPECT_CALL(*this, Begin()).WillOnce(action); + ExpectLoadCall(); + ExpectLoadForKeyCall(key, false); + Begin(); + } + // Declares an expectation that PersistentCookieStore::Load will be called, // saving the provided callback and sending a quit to the message loop. void ExpectLoadCall() { EXPECT_CALL(*persistent_store_, Load(testing::_)).WillOnce(testing::DoAll( testing::SaveArg<0>(&loaded_callback_), - QuitCurrentMessageLoop(), - testing::Return(true))); + QuitCurrentMessageLoop())); + } + + // Declares an expectation that PersistentCookieStore::LoadCookiesForKey + // will be called, saving the provided callback and sending a quit to the + // message loop. + void ExpectLoadForKeyCall(std::string key, bool quit_queue) { + if (quit_queue) + EXPECT_CALL(*persistent_store_, LoadCookiesForKey(key, testing::_)). + WillOnce(testing::DoAll( + PushCallbackAction(&loaded_for_key_callbacks_), + QuitCurrentMessageLoop())); + else + EXPECT_CALL(*persistent_store_, LoadCookiesForKey(key, testing::_)). + WillOnce(PushCallbackAction(&loaded_for_key_callbacks_)); } // Invokes the initial action. @@ -1073,11 +1107,17 @@ class DeferredCookieTaskTest : public CookieMonsterTest { private: // Declares that mock expectations in this test suite are strictly ordered. testing::InSequence in_sequence_; - // Holds cookies to be returned from PersistentCookieStore::Load. + // Holds cookies to be returned from PersistentCookieStore::Load or + // PersistentCookieStore::LoadCookiesForKey. std::vector<CookieMonster::CanonicalCookie*> loaded_cookies_; // Stores the callback passed from the CookieMonster to the - // PersistentCookieStore + // PersistentCookieStore::Load CookieMonster::PersistentCookieStore::LoadedCallback loaded_callback_; + // Stores the callback passed from the CookieMonster to the + // PersistentCookieStore::LoadCookiesForKey + std::queue<CookieMonster::PersistentCookieStore::LoadedCallback> + loaded_for_key_callbacks_; + // Stores the CookieMonster under test. scoped_refptr<CookieMonster> cookie_monster_; // Stores the mock PersistentCookieStore. @@ -1091,7 +1131,7 @@ TEST_F(DeferredCookieTaskTest, DeferredGetCookies) { MockGetCookiesCallback get_cookies_callback; - BeginWith(GetCookiesAction( + BeginWithForDomainKey("google.izzle", GetCookiesAction( &cookie_monster(), url_google_, &get_cookies_callback)); WaitForLoadCall(); @@ -1111,7 +1151,7 @@ TEST_F(DeferredCookieTaskTest, DeferredGetCookiesWithInfo) { MockGetCookieInfoCallback get_cookie_info_callback; - BeginWith(GetCookiesWithInfoAction( + BeginWithForDomainKey("google.izzle", GetCookiesWithInfoAction( &cookie_monster(), url_google_, &get_cookie_info_callback)); WaitForLoadCall(); @@ -1128,7 +1168,7 @@ TEST_F(DeferredCookieTaskTest, DeferredGetCookiesWithInfo) { TEST_F(DeferredCookieTaskTest, DeferredSetCookie) { MockSetCookiesCallback set_cookies_callback; - BeginWith(SetCookieAction( + BeginWithForDomainKey("google.izzle", SetCookieAction( &cookie_monster(), url_google_, "A=B", &set_cookies_callback)); WaitForLoadCall(); @@ -1145,7 +1185,7 @@ TEST_F(DeferredCookieTaskTest, DeferredSetCookie) { TEST_F(DeferredCookieTaskTest, DeferredDeleteCookie) { MockClosure delete_cookie_callback; - BeginWith(DeleteCookieAction( + BeginWithForDomainKey("google.izzle", DeleteCookieAction( &cookie_monster(), url_google_, "A", &delete_cookie_callback)); WaitForLoadCall(); @@ -1162,7 +1202,7 @@ TEST_F(DeferredCookieTaskTest, DeferredDeleteCookie) { TEST_F(DeferredCookieTaskTest, DeferredSetCookieWithDetails) { MockSetCookiesCallback set_cookies_callback; - BeginWith(SetCookieWithDetailsAction( + BeginWithForDomainKey("google.izzle", SetCookieWithDetailsAction( &cookie_monster(), url_google_foo_, "A", "B", std::string(), "/foo", base::Time(), false, false, &set_cookies_callback)); @@ -1205,7 +1245,7 @@ TEST_F(DeferredCookieTaskTest, DeferredGetAllForUrlCookies) { MockGetCookieListCallback get_cookie_list_callback; - BeginWith(GetAllCookiesForUrlAction( + BeginWithForDomainKey("google.izzle", GetAllCookiesForUrlAction( &cookie_monster(), url_google_, &get_cookie_list_callback)); WaitForLoadCall(); @@ -1227,7 +1267,7 @@ TEST_F(DeferredCookieTaskTest, DeferredGetAllForUrlWithOptionsCookies) { MockGetCookieListCallback get_cookie_list_callback; - BeginWith(GetAllCookiesForUrlWithOptionsAction( + BeginWithForDomainKey("google.izzle", GetAllCookiesForUrlWithOptionsAction( &cookie_monster(), url_google_, &get_cookie_list_callback)); WaitForLoadCall(); @@ -1278,7 +1318,7 @@ TEST_F(DeferredCookieTaskTest, DeferredDeleteAllCreatedBetweenCookies) { TEST_F(DeferredCookieTaskTest, DeferredDeleteAllForHostCookies) { MockDeleteCallback delete_callback; - BeginWith(DeleteAllForHostAction( + BeginWithForDomainKey("google.izzle", DeleteAllForHostAction( &cookie_monster(), url_google_, &delete_callback)); WaitForLoadCall(); @@ -1334,17 +1374,17 @@ TEST_F(DeferredCookieTaskTest, DeferredTaskOrder) { DeleteCookieAction( &cookie_monster(), url_google_, "A", &delete_cookie_callback))); ExpectLoadCall(); + ExpectLoadForKeyCall("google.izzle", false); Begin(); WaitForLoadCall(); EXPECT_CALL(get_cookies_callback, Invoke("X=1")).WillOnce( GetCookiesWithInfoAction( &cookie_monster(), url_google_, &get_cookie_info_callback)); - - EXPECT_CALL(set_cookies_callback, Invoke(true)); - EXPECT_CALL(delete_cookie_callback, Invoke()); EXPECT_CALL(get_cookie_info_callback, Invoke("X=1", testing::_)).WillOnce( QuitCurrentMessageLoop()); + EXPECT_CALL(set_cookies_callback, Invoke(true)); + EXPECT_CALL(delete_cookie_callback, Invoke()); CompleteLoadingAndWait(); } @@ -2970,10 +3010,16 @@ class FlushablePersistentStore : public CookieMonster::PersistentCookieStore { public: FlushablePersistentStore() : flush_count_(0) {} - bool Load(const LoadedCallback& loaded_callback) { + void Load(const LoadedCallback& loaded_callback) { std::vector<CookieMonster::CanonicalCookie*> out_cookies; - loaded_callback.Run(out_cookies); - return false; + MessageLoop::current()->PostTask(FROM_HERE, + base::Bind(&net::LoadedCallbackTask::Run, + new net::LoadedCallbackTask(loaded_callback, out_cookies))); + } + + void LoadCookiesForKey(const std::string& key, + const LoadedCallback& loaded_callback) { + Load(loaded_callback); } void AddCookie(const CookieMonster::CanonicalCookie&) {} |