diff options
-rw-r--r-- | net/base/cookie_monster.cc | 62 | ||||
-rw-r--r-- | net/base/cookie_monster.h | 30 | ||||
-rw-r--r-- | net/base/cookie_monster_perftest.cc | 40 | ||||
-rw-r--r-- | net/base/cookie_monster_store_test.h | 151 | ||||
-rwxr-xr-x[-rw-r--r--] | net/base/cookie_monster_unittest.cc | 293 |
5 files changed, 405 insertions, 171 deletions
diff --git a/net/base/cookie_monster.cc b/net/base/cookie_monster.cc index 4c46014..795d3ea8 100644 --- a/net/base/cookie_monster.cc +++ b/net/base/cookie_monster.cc @@ -185,13 +185,29 @@ void CookieMonster::InitStore() { // This prevents multiple vector growth / copies as we append cookies. cookies.reserve(kNumCookiesTotal); store_->Load(&cookies); + + // 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; for (std::vector<KeyedCanonicalCookie>::const_iterator it = cookies.begin(); it != cookies.end(); ++it) { - InternalInsertCookie(it->first, it->second, false); + int64 cookie_creation_time = it->second->CreationDate().ToInternalValue(); + + if (creation_times.insert(cookie_creation_time).second) { + InternalInsertCookie(it->first, it->second, false); + } else { + LOG(ERROR) << StringPrintf("Found cookies with duplicate creation " + "times in backing store: " + "{name='%s', domain='%s', path='%s'}", + it->second->Name().c_str(), + it->first.c_str(), + it->second->Path().c_str()); + } } // After importing cookies from the PersistentCookieStore, verify that - // none of our constraints are violated. + // none of our other constraints are violated. // // In particular, the backing store might have given us duplicate cookies. EnsureCookiesMapIsValid(); @@ -218,9 +234,6 @@ void CookieMonster::EnsureCookiesMapIsValid() { // Record how many duplicates were found in the database. // See InitializeHistograms() for details. histogram_cookie_deletion_cause_->Add(num_duplicates_trimmed); - - // TODO(eroman): Should also verify that there are no cookies with the same - // creation time, since that is assumed to be unique by the rest of the code. } // Our strategy to find duplicates is: @@ -294,13 +307,18 @@ int CookieMonster::TrimDuplicateCookiesForHost( // We save the iterator into |cookies_| rather than the actual cookie // pointer, since we may need to delete it later. - list.insert(it); + bool insert_success = list.insert(it).second; + DCHECK(insert_success) << + "Duplicate creation times found in duplicate cookie name scan."; } // If there were no duplicates, we are done! if (num_duplicates == 0) return 0; + // Make sure we find everything below that we did above. + int num_duplicates_found = 0; + // Otherwise, delete all the duplicate cookies, both from our in-memory store // and from the backing store. for (EquivalenceMap::iterator it = equivalent_cookies.begin(); @@ -311,6 +329,7 @@ int CookieMonster::TrimDuplicateCookiesForHost( if (dupes.size() <= 1) continue; // This cookiename/path has no duplicates. + num_duplicates_found += dupes.size() - 1; // Since |dups| is sorted by creation time (descending), the first cookie // is the most recent one, so we will keep it. The rest are duplicates. @@ -334,6 +353,7 @@ int CookieMonster::TrimDuplicateCookiesForHost( DELETE_COOKIE_DUPLICATE_IN_BACKING_STORE); } } + DCHECK_EQ(num_duplicates, num_duplicates_found); return num_duplicates; } @@ -654,13 +674,7 @@ bool CookieMonster::SetCookieWithCreationTimeAndOptions( const std::string& cookie_line, const Time& creation_time_or_null, const CookieOptions& options) { - AutoLock autolock(lock_); - - if (!HasCookieableScheme(url)) { - return false; - } - - InitIfNecessary(); + lock_.AssertAcquired(); COOKIE_DLOG(INFO) << "SetCookie() line: " << cookie_line; @@ -706,6 +720,20 @@ bool CookieMonster::SetCookieWithCreationTimeAndOptions( return SetCanonicalCookie(&cc, creation_time, options); } +bool CookieMonster::SetCookieWithCreationTime(const GURL& url, + const std::string& cookie_line, + const base::Time& creation_time) { + AutoLock autolock(lock_); + + if (!HasCookieableScheme(url)) { + return false; + } + + InitIfNecessary(); + return SetCookieWithCreationTimeAndOptions(url, cookie_line, creation_time, + CookieOptions()); +} + bool CookieMonster::SetCookieWithDetails( const GURL& url, const std::string& name, const std::string& value, const std::string& domain, const std::string& path, @@ -1030,6 +1058,14 @@ static bool CookieSorter(CookieMonster::CanonicalCookie* cc1, bool CookieMonster::SetCookieWithOptions(const GURL& url, const std::string& cookie_line, const CookieOptions& options) { + AutoLock autolock(lock_); + + if (!HasCookieableScheme(url)) { + return false; + } + + InitIfNecessary(); + return SetCookieWithCreationTimeAndOptions(url, cookie_line, Time(), options); } diff --git a/net/base/cookie_monster.h b/net/base/cookie_monster.h index 544192f..ab479c4 100644 --- a/net/base/cookie_monster.h +++ b/net/base/cookie_monster.h @@ -20,6 +20,7 @@ #include "base/scoped_ptr.h" #include "base/time.h" #include "net/base/cookie_store.h" +#include "testing/gtest/include/gtest/gtest_prod.h" class GURL; @@ -107,18 +108,6 @@ class CookieMonster : public CookieStore { const base::Time& expiration_time, bool secure, bool http_only); - // Exposed for unit testing. - bool SetCookieWithCreationTimeAndOptions(const GURL& url, - const std::string& cookie_line, - const base::Time& creation_time, - const CookieOptions& options); - bool SetCookieWithCreationTime(const GURL& url, - const std::string& cookie_line, - const base::Time& creation_time) { - return SetCookieWithCreationTimeAndOptions(url, cookie_line, creation_time, - CookieOptions()); - } - // Returns all the cookies, for use in management UI, etc. This does not mark // the cookies as having been accessed. CookieList GetAllCookies(); @@ -167,6 +156,14 @@ class CookieMonster : public CookieStore { private: ~CookieMonster(); + // Testing support. + friend class CookieMonsterTest; + FRIEND_TEST(CookieMonsterTest, TestCookieDeleteAllCreatedAfterTimestamp); + FRIEND_TEST(CookieMonsterTest, TestCookieDeleteAllCreatedBetweenTimestamps); + bool SetCookieWithCreationTime(const GURL& url, + const std::string& cookie_line, + const base::Time& creation_time); + // Called by all non-static functions to ensure that the cookies store has // been initialized. This is not done during creating so it doesn't block // the window showing. @@ -220,6 +217,15 @@ class CookieMonster : public CookieStore { CanonicalCookie* cc, bool sync_to_store); + // Helper function that sets cookies with more control. + // Not exposed as we don't want callers to have the ability + // to specify (potentially duplicate) creation times. + bool SetCookieWithCreationTimeAndOptions(const GURL& url, + const std::string& cookie_line, + const base::Time& creation_time, + const CookieOptions& options); + + // Helper function that sets a canonical cookie, deleting equivalents and // performing garbage collection. bool SetCanonicalCookie(scoped_ptr<CanonicalCookie>* cc, diff --git a/net/base/cookie_monster_perftest.cc b/net/base/cookie_monster_perftest.cc index 3d1e7a7..042f971 100644 --- a/net/base/cookie_monster_perftest.cc +++ b/net/base/cookie_monster_perftest.cc @@ -1,12 +1,13 @@ -// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Copyright (c) 2010 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 "net/base/cookie_monster.h" #include "base/perftimer.h" #include "base/string_util.h" -#include "net/base/cookie_monster.h" -#include "testing/gtest/include/gtest/gtest.h" #include "googleurl/src/gurl.h" +#include "net/base/cookie_monster_store_test.h" +#include "testing/gtest/include/gtest/gtest.h" namespace { class ParsedCookieTest : public testing::Test { }; @@ -161,3 +162,36 @@ TEST(CookieMonsterTest, TestDomainTree) { } timer2.Done(); } + +TEST(CookieMonsterTest, TestImport) { + scoped_refptr<MockPersistentCookieStore> store(new MockPersistentCookieStore); + std::vector<net::CookieMonster::KeyedCanonicalCookie> initial_cookies; + + // We want to setup a fairly large backing store, with 300 domains of 50 + // cookies each. Creation times must be unique. + int64 time_tick(base::Time::Now().ToInternalValue()); + + for (int domain_num = 0; domain_num < 300; domain_num++) { + std::string domain_name(StringPrintf(".Domain_%d.com", domain_num)); + std::string gurl("www" + domain_name); + for (int cookie_num = 0; cookie_num < 50; cookie_num++) { + std::string cookie_line(StringPrintf("Cookie_%d=1; Path=/", cookie_num)); + AddKeyedCookieToList(gurl, cookie_line, + base::Time::FromInternalValue(time_tick++), + &initial_cookies); + } + } + + store->SetLoadExpectation(true, initial_cookies); + + scoped_refptr<net::CookieMonster> cm(new net::CookieMonster(store, NULL)); + + // Import will happen on first access. + GURL gurl("www.google.com"); + net::CookieOptions options; + PerfTimeLogger timer("Cookie_monster_import_from_store"); + cm->GetCookiesWithOptions(gurl, options); + timer.Done(); +} + + diff --git a/net/base/cookie_monster_store_test.h b/net/base/cookie_monster_store_test.h new file mode 100644 index 0000000..4b1cc52 --- /dev/null +++ b/net/base/cookie_monster_store_test.h @@ -0,0 +1,151 @@ +// Copyright (c) 2010 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. + +// This file contains test infrastructure for multiple files +// (current cookie_monster_unittest.cc and cookie_monster_perftest.cc) +// that need to test out CookieMonster interactions with the backing store. +// It should only be included by test code. + +#include "base/time.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace { + +// Describes a call to one of the 3 functions of PersistentCookieStore. +struct CookieStoreCommand { + enum Type { + ADD, + UPDATE_ACCESS_TIME, + REMOVE, + }; + + CookieStoreCommand(Type type, + const std::string& key, + const net::CookieMonster::CanonicalCookie& cookie) + : type(type), key(key), cookie(cookie) {} + + Type type; + std::string key; // Only applicable to the ADD command. + net::CookieMonster::CanonicalCookie cookie; +}; + +// Implementation of PersistentCookieStore that captures the +// received commands and saves them to a list. +// The result of calls to Load() can be configured using SetLoadExpectation(). +class MockPersistentCookieStore + : public net::CookieMonster::PersistentCookieStore { + public: + typedef std::vector<CookieStoreCommand> CommandList; + + MockPersistentCookieStore() : load_return_value_(true) { + } + + virtual bool Load( + std::vector<net::CookieMonster::KeyedCanonicalCookie>* out_cookies) { + bool ok = load_return_value_; + if (ok) + *out_cookies = load_result_; + return ok; + } + + virtual void AddCookie(const std::string& key, + const net::CookieMonster::CanonicalCookie& cookie) { + commands_.push_back( + CookieStoreCommand(CookieStoreCommand::ADD, key, cookie)); + } + + virtual void UpdateCookieAccessTime( + const net::CookieMonster::CanonicalCookie& cookie) { + commands_.push_back(CookieStoreCommand( + CookieStoreCommand::UPDATE_ACCESS_TIME, std::string(), cookie)); + } + + virtual void DeleteCookie( + const net::CookieMonster::CanonicalCookie& cookie) { + commands_.push_back( + CookieStoreCommand(CookieStoreCommand::REMOVE, std::string(), cookie)); + } + + void SetLoadExpectation( + bool return_value, + const std::vector<net::CookieMonster::KeyedCanonicalCookie>& result) { + load_return_value_ = return_value; + load_result_ = result; + } + + const CommandList& commands() const { + return commands_; + } + + private: + CommandList commands_; + + // Deferred result to use when Load() is called. + bool load_return_value_; + std::vector<net::CookieMonster::KeyedCanonicalCookie> load_result_; + + DISALLOW_COPY_AND_ASSIGN(MockPersistentCookieStore); +}; + +// Mock for CookieMonster::Delegate +class MockCookieMonsterDelegate : public net::CookieMonster::Delegate { + public: + typedef std::pair<net::CookieMonster::CanonicalCookie, bool> + CookieNotification; + + MockCookieMonsterDelegate() {} + + virtual void OnCookieChanged( + const net::CookieMonster::CanonicalCookie& cookie, + bool removed) { + CookieNotification notification(cookie, removed); + changes_.push_back(notification); + } + + const std::vector<CookieNotification>& changes() const { return changes_; } + + void reset() { changes_.clear(); } + + private: + virtual ~MockCookieMonsterDelegate() {} + + std::vector<CookieNotification> changes_; + + DISALLOW_COPY_AND_ASSIGN(MockCookieMonsterDelegate); +}; + +// Helper to build a list of KeyedCanonicalCookies. +static void AddKeyedCookieToList( + const std::string& key, + const std::string& cookie_line, + const base::Time& creation_time, + std::vector<net::CookieMonster::KeyedCanonicalCookie>* out_list) { + + // Parse the cookie line. + net::CookieMonster::ParsedCookie pc(cookie_line); + EXPECT_TRUE(pc.IsValid()); + + // This helper is simplistic in interpreting a parsed cookie, in order to + // avoid duplicated CookieMonster's CanonPath() and CanonExpiration() + // functions. Would be nice to export them, and re-use here. + EXPECT_FALSE(pc.HasMaxAge()); + EXPECT_TRUE(pc.HasPath()); + base::Time cookie_expires = pc.HasExpires() ? + net::CookieMonster::ParseCookieTime(pc.Expires()) : base::Time(); + std::string cookie_path = pc.Path(); + + scoped_ptr<net::CookieMonster::CanonicalCookie> cookie( + new net::CookieMonster::CanonicalCookie( + pc.Name(), pc.Value(), key, cookie_path, + pc.IsSecure(), pc.IsHttpOnly(), + creation_time, creation_time, + !cookie_expires.is_null(), + cookie_expires)); + + out_list->push_back( + net::CookieMonster::KeyedCanonicalCookie( + key, cookie.release())); +} + +} // namespace diff --git a/net/base/cookie_monster_unittest.cc b/net/base/cookie_monster_unittest.cc index c61a782..39bfbf5 100644..100755 --- a/net/base/cookie_monster_unittest.cc +++ b/net/base/cookie_monster_unittest.cc @@ -1,4 +1,4 @@ -// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Copyright (c) 2010 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. @@ -14,8 +14,11 @@ #include "base/time.h" #include "googleurl/src/gurl.h" #include "net/base/cookie_monster.h" +#include "net/base/cookie_monster_store_test.h" // For CookieStore Mock #include "testing/gtest/include/gtest/gtest.h" +namespace net { + using base::Time; using base::TimeDelta; @@ -24,142 +27,6 @@ namespace { class ParsedCookieTest : public testing::Test { }; class CookieMonsterTest : public testing::Test { }; -// Describes a call to one of the 3 functions of PersistentCookieStore. -struct CookieStoreCommand { - enum Type { - ADD, - UPDATE_ACCESS_TIME, - REMOVE, - }; - - CookieStoreCommand(Type type, - const std::string& key, - const net::CookieMonster::CanonicalCookie& cookie) - : type(type), key(key), cookie(cookie) {} - - Type type; - std::string key; // Only applicable to the ADD command. - net::CookieMonster::CanonicalCookie cookie; -}; - -// Implementation of PersistentCookieStore that captures the -// received commands and saves them to a list. -// The result of calls to Load() can be configured using SetLoadExpectation(). -class MockPersistentCookieStore - : public net::CookieMonster::PersistentCookieStore { - public: - typedef std::vector<CookieStoreCommand> CommandList; - - MockPersistentCookieStore() : load_return_value_(true) { - } - - virtual bool Load( - std::vector<net::CookieMonster::KeyedCanonicalCookie>* out_cookies) { - bool ok = load_return_value_; - if (ok) - *out_cookies = load_result_; - return ok; - } - - virtual void AddCookie(const std::string& key, - const net::CookieMonster::CanonicalCookie& cookie) { - commands_.push_back( - CookieStoreCommand(CookieStoreCommand::ADD, key, cookie)); - } - - virtual void UpdateCookieAccessTime( - const net::CookieMonster::CanonicalCookie& cookie) { - commands_.push_back(CookieStoreCommand( - CookieStoreCommand::UPDATE_ACCESS_TIME, std::string(), cookie)); - } - - virtual void DeleteCookie( - const net::CookieMonster::CanonicalCookie& cookie) { - commands_.push_back( - CookieStoreCommand(CookieStoreCommand::REMOVE, std::string(), cookie)); - } - - void SetLoadExpectation( - bool return_value, - const std::vector<net::CookieMonster::KeyedCanonicalCookie>& result) { - load_return_value_ = return_value; - load_result_ = result; - } - - const CommandList& commands() const { - return commands_; - } - - private: - CommandList commands_; - - // Deferred result to use when Load() is called. - bool load_return_value_; - std::vector<net::CookieMonster::KeyedCanonicalCookie> load_result_; - - DISALLOW_COPY_AND_ASSIGN(MockPersistentCookieStore); -}; - -// Mock for CookieMonster::Delegate -class MockCookieMonsterDelegate : public net::CookieMonster::Delegate { - public: - typedef std::pair<net::CookieMonster::CanonicalCookie, bool> - CookieNotification; - - MockCookieMonsterDelegate() {} - - virtual void OnCookieChanged( - const net::CookieMonster::CanonicalCookie& cookie, - bool removed) { - CookieNotification notification(cookie, removed); - changes_.push_back(notification); - } - - const std::vector<CookieNotification>& changes() const { return changes_; } - - void reset() { changes_.clear(); } - - private: - virtual ~MockCookieMonsterDelegate() {} - - std::vector<CookieNotification> changes_; - - DISALLOW_COPY_AND_ASSIGN(MockCookieMonsterDelegate); -}; - -// Helper to build a list of KeyedCanonicalCookies. -void AddKeyedCookieToList( - const std::string& key, - const std::string& cookie_line, - const Time& creation_time, - std::vector<net::CookieMonster::KeyedCanonicalCookie>* out_list) { - - // Parse the cookie line. - net::CookieMonster::ParsedCookie pc(cookie_line); - EXPECT_TRUE(pc.IsValid()); - - // This helper is simplistic in interpreting a parsed cookie, in order to - // avoid duplicated CookieMonster's CanonPath() and CanonExpiration() - // functions. Would be nice to export them, and re-use here. - EXPECT_FALSE(pc.HasMaxAge()); - EXPECT_TRUE(pc.HasPath()); - Time cookie_expires = pc.HasExpires() ? - net::CookieMonster::ParseCookieTime(pc.Expires()) : Time(); - std::string cookie_path = pc.Path(); - - scoped_ptr<net::CookieMonster::CanonicalCookie> cookie( - new net::CookieMonster::CanonicalCookie( - pc.Name(), pc.Value(), key, cookie_path, - pc.IsSecure(), pc.IsHttpOnly(), - creation_time, creation_time, - !cookie_expires.is_null(), - cookie_expires)); - - out_list->push_back( - net::CookieMonster::KeyedCanonicalCookie( - key, cookie.release())); -} - // Helper for DeleteAllForHost test; repopulates CM with same layout // each time. const char* kTopLevelDomainPlus1 = "http://www.harvard.edu"; @@ -247,7 +114,6 @@ void PopulateCmForDeleteAllForHost(scoped_refptr<net::CookieMonster> cm) { } // namespace - TEST(ParsedCookieTest, TestBasic) { net::CookieMonster::ParsedCookie pc("a=b"); EXPECT_TRUE(pc.IsValid()); @@ -1490,7 +1356,10 @@ TEST(CookieMonsterTest, DontImportDuplicateCookies) { new MockPersistentCookieStore); // We will fill some initial cookies into the PersistentCookieStore, - // to simulate a database with 4 duplicates. + // to simulate a database with 4 duplicates. Note that we need to + // be careful not to have any duplicate creation times at all (as it's a + // violation of a CookieMonster invariant) even if Time::Now() doesn't + // move between calls. std::vector<net::CookieMonster::KeyedCanonicalCookie> initial_cookies; // Insert 4 cookies with name "X" on path "/", with varying creation @@ -1529,13 +1398,13 @@ TEST(CookieMonsterTest, DontImportDuplicateCookies) { AddKeyedCookieToList("www.google.com", "X=a2; path=/2; expires=Mon, 18-Apr-22 22:50:14 GMT", - Time::Now() + TimeDelta::FromDays(1), + Time::Now() + TimeDelta::FromDays(2), &initial_cookies); // Insert 1 cookie with name "Y" on path "/". AddKeyedCookieToList("www.google.com", "Y=a; path=/; expires=Mon, 18-Apr-22 22:50:14 GMT", - Time::Now() + TimeDelta::FromDays(9), + Time::Now() + TimeDelta::FromDays(10), &initial_cookies); // Inject our initial cookies into the mock PersistentCookieStore. @@ -1560,6 +1429,74 @@ TEST(CookieMonsterTest, DontImportDuplicateCookies) { EXPECT_EQ(CookieStoreCommand::REMOVE, store->commands()[3].type); } +// Tests importing from a persistent cookie store that contains cookies +// with duplicate creation times. This situation should be handled by +// dropping the cookies before insertion/visibility to user. +// +// This is a regression test for: http://crbug.com/43188. +TEST(CookieMonsterTest, DontImportDuplicateCreationTimes) { + GURL url_google("http://www.google.com/"); + + scoped_refptr<MockPersistentCookieStore> store( + new MockPersistentCookieStore); + + Time now(Time::Now()); + Time earlier(now - TimeDelta::FromDays(1)); + + // Insert 8 cookies, four with the current time as creation times, and + // four with the earlier time as creation times. We should only get + // two cookies remaining, but which two (other than that there should + // be one from each set) will be random. + std::vector<net::CookieMonster::KeyedCanonicalCookie> initial_cookies; + AddKeyedCookieToList("www.google.com", + "X=1; path=/", + now, + &initial_cookies); + AddKeyedCookieToList("www.google.com", + "X=2; path=/", + now, + &initial_cookies); + AddKeyedCookieToList("www.google.com", + "X=3; path=/", + now, + &initial_cookies); + AddKeyedCookieToList("www.google.com", + "X=4; path=/", + now, + &initial_cookies); + + AddKeyedCookieToList("www.google.com", + "Y=1; path=/", + earlier, + &initial_cookies); + AddKeyedCookieToList("www.google.com", + "Y=2; path=/", + earlier, + &initial_cookies); + AddKeyedCookieToList("www.google.com", + "Y=3; path=/", + earlier, + &initial_cookies); + AddKeyedCookieToList("www.google.com", + "Y=4; path=/", + earlier, + &initial_cookies); + + // Inject our initial cookies into the mock PersistentCookieStore. + store->SetLoadExpectation(true, initial_cookies); + + scoped_refptr<net::CookieMonster> cm(new net::CookieMonster(store, NULL)); + + net::CookieMonster::CookieList list(cm->GetAllCookies()); + EXPECT_EQ(2U, list.size()); + // Confirm that we have one of each. + std::string name1(list[0].Name()); + std::string name2(list[1].Name()); + EXPECT_TRUE(name1 == "X" || name2 == "X"); + EXPECT_TRUE(name1 == "Y" || name2 == "Y"); + EXPECT_NE(name1, name2); +} + TEST(CookieMonsterTest, Delegate) { GURL url_google(kUrlGoogle); @@ -1715,8 +1652,6 @@ TEST(CookieMonsterTest, SetCookieWithDetails) { ASSERT_TRUE(++it == cookies.end()); } - - TEST(CookieMonsterTest, DeleteAllForHost) { scoped_refptr<net::CookieMonster> cm(new net::CookieMonster(NULL, NULL)); @@ -1779,3 +1714,75 @@ TEST(CookieMonsterTest, DeleteAllForHost) { std::string("/dir1/dir2/xxx")))); } + +TEST(CookieMonsterTest, UniqueCreationTime) { + scoped_refptr<net::CookieMonster> cm(new net::CookieMonster(NULL, NULL)); + GURL url_google(kUrlGoogle); + net::CookieOptions options; + + // Add in three cookies through every public interface to the + // CookieMonster and confirm that none of them have duplicate + // creation times. + + // SetCookieWithCreationTime and SetCookieWithCreationTimeAndOptions + // are not included as they aren't going to be public for very much + // longer. + + // SetCookie, SetCookies, SetCookiesWithOptions, + // SetCookieWithOptions, SetCookieWithDetails + + cm->SetCookie(url_google, "SetCookie1=A"); + cm->SetCookie(url_google, "SetCookie2=A"); + cm->SetCookie(url_google, "SetCookie3=A"); + + { + std::vector<std::string> cookie_lines; + cookie_lines.push_back("setCookies1=A"); + cookie_lines.push_back("setCookies2=A"); + cookie_lines.push_back("setCookies4=A"); + cm->SetCookies(url_google, cookie_lines); + } + + { + std::vector<std::string> cookie_lines; + cookie_lines.push_back("setCookiesWithOptions1=A"); + cookie_lines.push_back("setCookiesWithOptions2=A"); + cookie_lines.push_back("setCookiesWithOptions3=A"); + + cm->SetCookiesWithOptions(url_google, cookie_lines, options); + } + + cm->SetCookieWithOptions(url_google, "setCookieWithOptions1=A", options); + cm->SetCookieWithOptions(url_google, "setCookieWithOptions2=A", options); + cm->SetCookieWithOptions(url_google, "setCookieWithOptions3=A", options); + + cm->SetCookieWithDetails(url_google, "setCookieWithDetails1", "A", + ".google.com", "/", Time(), false, false); + cm->SetCookieWithDetails(url_google, "setCookieWithDetails2", "A", + ".google.com", "/", Time(), false, false); + cm->SetCookieWithDetails(url_google, "setCookieWithDetails3", "A", + ".google.com", "/", Time(), false, false); + + // Now we check + net::CookieMonster::CookieList cookie_list(cm->GetAllCookies()); + typedef std::map<int64, net::CookieMonster::CanonicalCookie> TimeCookieMap; + TimeCookieMap check_map; + for (net::CookieMonster::CookieList::const_iterator it = cookie_list.begin(); + it != cookie_list.end(); it++) { + const int64 creation_date = it->CreationDate().ToInternalValue(); + TimeCookieMap::const_iterator + existing_cookie_it(check_map.find(creation_date)); + EXPECT_TRUE(existing_cookie_it == check_map.end()) + << "Cookie " << it->Name() << " has same creation date (" + << it->CreationDate().ToInternalValue() + << ") as previously entered cookie " + << existing_cookie_it->second.Name(); + + if (existing_cookie_it == check_map.end()) { + check_map.insert(TimeCookieMap::value_type( + it->CreationDate().ToInternalValue(), *it)); + } + } +} + +} // namespace |