diff options
27 files changed, 2737 insertions, 2624 deletions
diff --git a/net/base/cookie_monster.h b/net/base/cookie_monster.h index 0e8febb..f7ebbcc 100644 --- a/net/base/cookie_monster.h +++ b/net/base/cookie_monster.h @@ -2,970 +2,13 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// Brought to you by the letter D and the number 2. +// Provides a temporary redirection while clients are updated to use the new +// path. #ifndef NET_BASE_COOKIE_MONSTER_H_ #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_forward.h" -#include "base/gtest_prod_util.h" -#include "base/memory/ref_counted.h" -#include "base/memory/scoped_ptr.h" -#include "base/synchronization/lock.h" -#include "base/time.h" -#include "net/base/cookie_store.h" -#include "net/base/net_export.h" - -class GURL; - -namespace base { -class Histogram; -class TimeTicks; -} // namespace base - -namespace net { - -class CookieList; - -// The cookie monster is the system for storing and retrieving cookies. It has -// an in-memory list of all cookies, and synchronizes non-session cookies to an -// optional permanent storage that implements the PersistentCookieStore -// interface. -// -// 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. -// -// 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. -// - Verify that our domain enforcement and non-dotted handling is correct -class NET_EXPORT CookieMonster : public CookieStore { - public: - class CanonicalCookie; - class Delegate; - class ParsedCookie; - class PersistentCookieStore; - - // Terminology: - // * The 'top level domain' (TLD) of an internet domain name is - // the terminal "." free substring (e.g. "com" for google.com - // or world.std.com). - // * The 'effective top level domain' (eTLD) is the longest - // "." initiated terminal substring of an internet domain name - // that is controlled by a general domain registrar. - // (e.g. "co.uk" for news.bbc.co.uk). - // * The 'effective top level domain plus one' (eTLD+1) is the - // shortest "." delimited terminal substring of an internet - // domain name that is not controlled by a general domain - // registrar (e.g. "bbc.co.uk" for news.bbc.co.uk, or - // "google.com" for news.google.com). The general assumption - // is that all hosts and domains under an eTLD+1 share some - // administrative control. - - // CookieMap is the central data structure of the CookieMonster. It - // is a map whose values are pointers to CanonicalCookie data - // structures (the data structures are owned by the CookieMonster - // and must be destroyed when removed from the map). The key is based on the - // effective domain of the cookies. If the domain of the cookie has an - // eTLD+1, that is the key for the map. If the domain of the cookie does not - // have an eTLD+1, the key of the map is the host the cookie applies to (it is - // not legal to have domain cookies without an eTLD+1). This rule - // excludes cookies for, e.g, ".com", ".co.uk", or ".internalnetwork". - // This behavior is the same as the behavior in Firefox v 3.6.10. - - // NOTE(deanm): - // I benchmarked hash_multimap vs multimap. We're going to be query-heavy - // so it would seem like hashing would help. However they were very - // close, with multimap being a tiny bit faster. I think this is because - // our map is at max around 1000 entries, and the additional complexity - // for the hashing might not overcome the O(log(1000)) for querying - // a multimap. Also, multimap is standard, another reason to use it. - // TODO(rdsmith): This benchmark should be re-done now that we're allowing - // subtantially more entries in the map. - typedef std::multimap<std::string, CanonicalCookie*> CookieMap; - typedef std::pair<CookieMap::iterator, CookieMap::iterator> CookieMapItPair; - - // The store passed in should not have had Init() called on it yet. This - // class will take care of initializing it. The backing store is NOT owned by - // this class, but it must remain valid for the duration of the cookie - // monster's existence. If |store| is NULL, then no backing store will be - // updated. If |delegate| is non-NULL, it will be notified on - // creation/deletion of cookies. - CookieMonster(PersistentCookieStore* store, Delegate* delegate); - - // Only used during unit testing. - CookieMonster(PersistentCookieStore* store, - Delegate* delegate, - int last_access_threshold_milliseconds); - - // Parses the string with the cookie time (very forgivingly). - static base::Time ParseCookieTime(const std::string& time_string); - - // Helper function that adds all cookies from |list| into this instance. - bool InitializeFrom(const CookieList& list); - - typedef base::Callback<void(const CookieList& cookies)> GetCookieListCallback; - typedef base::Callback<void(bool success)> DeleteCookieCallback; - - // Sets a cookie given explicit user-provided cookie attributes. The cookie - // name, value, domain, etc. are each provided as separate strings. This - // function expects each attribute to be well-formed. It will check for - // disallowed characters (e.g. the ';' character is disallowed within the - // cookie value attribute) and will return false without setting the cookie - // if such characters are found. - void SetCookieWithDetailsAsync(const GURL& url, - const std::string& name, - const std::string& value, - const std::string& domain, - const std::string& path, - const base::Time& expiration_time, - bool secure, bool http_only, - const SetCookiesCallback& callback); - - - // Returns all the cookies, for use in management UI, etc. This does not mark - // the cookies as having been accessed. - // The returned cookies are ordered by longest path, then by earliest - // creation date. - void GetAllCookiesAsync(const GetCookieListCallback& callback); - - // Returns all the cookies, for use in management UI, etc. Filters results - // using given url scheme, host / domain and path and options. This does not - // mark the cookies as having been accessed. - // The returned cookies are ordered by longest path, then earliest - // creation date. - void GetAllCookiesForURLWithOptionsAsync( - const GURL& url, - const CookieOptions& options, - const GetCookieListCallback& callback); - - // Invokes GetAllCookiesForURLWithOptions with options set to include HTTP - // only cookies. - void GetAllCookiesForURLAsync(const GURL& url, - const GetCookieListCallback& callback); - - // Deletes all of the cookies. - void DeleteAllAsync(const DeleteCallback& callback); - - // Deletes all cookies that match the host of the given URL - // regardless of path. This includes all http_only and secure cookies, - // but does not include any domain cookies that may apply to this host. - // Returns the number of cookies deleted. - void DeleteAllForHostAsync(const GURL& url, - const DeleteCallback& callback); - - // Deletes one specific cookie. - void DeleteCanonicalCookieAsync(const CanonicalCookie& cookie, - const DeleteCookieCallback& callback); - - // Override the default list of schemes that are allowed to be set in - // this cookie store. Calling his overrides the value of - // "enable_file_scheme_". - // If this this method is called, it must be called before first use of - // the instance (i.e. as part of the instance initialization process). - void SetCookieableSchemes(const char* schemes[], size_t num_schemes); - - // Instructs the cookie monster to not delete expired cookies. This is used - // in cases where the cookie monster is used as a data structure to keep - // arbitrary cookies. - void SetKeepExpiredCookies(); - - // Delegates the call to set the |clear_local_store_on_exit_| flag of the - // PersistentStore if it exists. - void SetClearPersistentStoreOnExit(bool clear_local_store); - - // There are some unknowns about how to correctly handle file:// cookies, - // and our implementation for this is not robust enough. This allows you - // to enable support, but it should only be used for testing. Bug 1157243. - // Must be called before creating a CookieMonster instance. - static void EnableFileScheme(); - - // Flush the backing store (if any) to disk and post the given callback when - // done. - // WARNING: THE CALLBACK WILL RUN ON A RANDOM THREAD. IT MUST BE THREAD SAFE. - // It may be posted to the current thread, or it may run on the thread that - // actually does the flushing. Your Task should generally post a notification - // to the thread you actually want to be notified on. - void FlushStore(const base::Closure& callback); - - // CookieStore implementation. - - // Sets the cookies specified by |cookie_list| returned from |url| - // with options |options| in effect. - virtual void SetCookieWithOptionsAsync( - const GURL& url, - const std::string& cookie_line, - const CookieOptions& options, - const SetCookiesCallback& callback) OVERRIDE; - - // Gets all cookies that apply to |url| given |options|. - // The returned cookies are ordered by longest path, then earliest - // creation date. - virtual void GetCookiesWithOptionsAsync( - const GURL& url, - const CookieOptions& options, - const GetCookiesCallback& callback) OVERRIDE; - - virtual void GetCookiesWithInfoAsync( - const GURL& url, - const CookieOptions& options, - const GetCookieInfoCallback& callback) OVERRIDE; - - // Deletes all cookies with that might apply to |url| that has |cookie_name|. - virtual void DeleteCookieAsync( - const GURL& url, const std::string& cookie_name, - const base::Closure& callback) OVERRIDE; - - // Deletes all of the cookies that have a creation_date greater than or equal - // to |delete_begin| and less than |delete_end| - // Returns the number of cookies that have been deleted. - virtual void DeleteAllCreatedBetweenAsync( - const base::Time& delete_begin, - const base::Time& delete_end, - const DeleteCallback& callback) OVERRIDE; - - virtual CookieMonster* GetCookieMonster() OVERRIDE; - - // Enables writing session cookies into the cookie database. If this this - // method is called, it must be called before first use of the instance - // (i.e. as part of the instance initialization process). - void SetPersistSessionCookies(bool persist_session_cookies); - - // Protects session cookies from deletion on shutdown. - void SaveSessionCookies(); - - // Debugging method to perform various validation checks on the map. - // Currently just checking that there are no null CanonicalCookie pointers - // in the map. - // Argument |arg| is to allow retaining of arbitrary data if the CHECKs - // in the function trip. TODO(rdsmith):Remove hack. - void ValidateMap(int arg); - - // The default list of schemes the cookie monster can handle. - static const char* kDefaultCookieableSchemes[]; - static const int kDefaultCookieableSchemesCount; - - private: - // For queueing the cookie monster calls. - class CookieMonsterTask; - class DeleteAllCreatedBetweenTask; - class DeleteAllForHostTask; - class DeleteAllTask; - class DeleteCookieTask; - class DeleteCanonicalCookieTask; - class GetAllCookiesForURLWithOptionsTask; - class GetAllCookiesTask; - class GetCookiesWithOptionsTask; - class GetCookiesWithInfoTask; - class SetCookieWithDetailsTask; - class SetCookieWithOptionsTask; - - // Testing support. - // For SetCookieWithCreationTime. - FRIEND_TEST_ALL_PREFIXES(CookieMonsterTest, - TestCookieDeleteAllCreatedBetweenTimestamps); - - // For gargage collection constants. - FRIEND_TEST_ALL_PREFIXES(CookieMonsterTest, TestHostGarbageCollection); - FRIEND_TEST_ALL_PREFIXES(CookieMonsterTest, TestTotalGarbageCollection); - FRIEND_TEST_ALL_PREFIXES(CookieMonsterTest, GarbageCollectionTriggers); - FRIEND_TEST_ALL_PREFIXES(CookieMonsterTest, TestGCTimes); - - // For validation of key values. - FRIEND_TEST_ALL_PREFIXES(CookieMonsterTest, TestDomainTree); - FRIEND_TEST_ALL_PREFIXES(CookieMonsterTest, TestImport); - FRIEND_TEST_ALL_PREFIXES(CookieMonsterTest, GetKey); - FRIEND_TEST_ALL_PREFIXES(CookieMonsterTest, TestGetKey); - - // For FindCookiesForKey. - FRIEND_TEST_ALL_PREFIXES(CookieMonsterTest, ShortLivedSessionCookies); - - // Internal reasons for deletion, used to populate informative histograms - // and to provide a public cause for onCookieChange notifications. - // - // If you add or remove causes from this list, please be sure to also update - // the Delegate::ChangeCause mapping inside ChangeCauseMapping. Moreover, - // these are used as array indexes, so avoid reordering to keep the - // histogram buckets consistent. New items (if necessary) should be added - // at the end of the list, just before DELETE_COOKIE_LAST_ENTRY. - enum DeletionCause { - DELETE_COOKIE_EXPLICIT = 0, - DELETE_COOKIE_OVERWRITE, - DELETE_COOKIE_EXPIRED, - DELETE_COOKIE_EVICTED, - DELETE_COOKIE_DUPLICATE_IN_BACKING_STORE, - DELETE_COOKIE_DONT_RECORD, // e.g. For final cleanup after flush to store. - DELETE_COOKIE_EVICTED_DOMAIN, - DELETE_COOKIE_EVICTED_GLOBAL, - - // Cookies evicted during domain level garbage collection that - // were accessed longer ago than kSafeFromGlobalPurgeDays - DELETE_COOKIE_EVICTED_DOMAIN_PRE_SAFE, - - // Cookies evicted during domain level garbage collection that - // were accessed more recently than kSafeFromGlobalPurgeDays - // (and thus would have been preserved by global garbage collection). - DELETE_COOKIE_EVICTED_DOMAIN_POST_SAFE, - - // A common idiom is to remove a cookie by overwriting it with an - // already-expired expiration date. This captures that case. - DELETE_COOKIE_EXPIRED_OVERWRITE, - - DELETE_COOKIE_LAST_ENTRY - }; - - // Cookie garbage collection thresholds. Based off of the Mozilla defaults. - // When the number of cookies gets to k{Domain,}MaxCookies - // purge down to k{Domain,}MaxCookies - k{Domain,}PurgeCookies. - // It might seem scary to have a high purge value, but really it's not. - // You just make sure that you increase the max to cover the increase - // in purge, and we would have been purging the same amount of cookies. - // We're just going through the garbage collection process less often. - // Note that the DOMAIN values are per eTLD+1; see comment for the - // CookieMap typedef. So, e.g., the maximum number of cookies allowed for - // google.com and all of its subdomains will be 150-180. - // - // Any cookies accessed more recently than kSafeFromGlobalPurgeDays will not - // be evicted by global garbage collection, even if we have more than - // kMaxCookies. This does not affect domain garbage collection. - // - // Present in .h file to make accessible to tests through FRIEND_TEST. - // Actual definitions are in cookie_monster.cc. - static const size_t kDomainMaxCookies; - static const size_t kDomainPurgeCookies; - static const size_t kMaxCookies; - static const size_t kPurgeCookies; - - // The number of days since last access that cookies will not be subject - // to global garbage collection. - static const int kSafeFromGlobalPurgeDays; - - // Record statistics every kRecordStatisticsIntervalSeconds of uptime. - static const int kRecordStatisticsIntervalSeconds = 10 * 60; - - virtual ~CookieMonster(); - - // The following are synchronous calls to which the asynchronous methods - // delegate either immediately (if the store is loaded) or through a deferred - // task (if the store is not yet loaded). - bool SetCookieWithDetails(const GURL& url, - const std::string& name, - const std::string& value, - const std::string& domain, - const std::string& path, - const base::Time& expiration_time, - bool secure, bool http_only); - - CookieList GetAllCookies(); - - CookieList GetAllCookiesForURLWithOptions(const GURL& url, - const CookieOptions& options); - - CookieList GetAllCookiesForURL(const GURL& url); - - int DeleteAll(bool sync_to_store); - - int DeleteAllCreatedBetween(const base::Time& delete_begin, - const base::Time& delete_end); - - int DeleteAllForHost(const GURL& url); - - bool DeleteCanonicalCookie(const CanonicalCookie& cookie); - - bool SetCookieWithOptions(const GURL& url, - const std::string& cookie_line, - const CookieOptions& options); - - std::string GetCookiesWithOptions(const GURL& url, - const CookieOptions& options); - - void GetCookiesWithInfo(const GURL& url, - const CookieOptions& options, - std::string* cookie_line, - std::vector<CookieInfo>* cookie_infos); - - void DeleteCookie(const GURL& url, const std::string& cookie_name); - - 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. - // Note: this method should always be called with lock_ held. - void InitIfNecessary() { - if (!initialized_) { - if (store_) { - InitStore(); - } else { - loaded_ = true; - } - initialized_ = true; - } - } - - // Initializes the backing store and reads existing cookies from it. - // Should only be called by InitIfNecessary(). - void InitStore(); - - // 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_blocked_on_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); - - // Invokes deferred calls. - void InvokeQueue(); - - // Checks that |cookies_| matches our invariants, and tries to repair any - // inconsistencies. (In other words, it does not have duplicate cookies). - void EnsureCookiesMapIsValid(); - - // Checks for any duplicate cookies for CookieMap key |key| which lie between - // |begin| and |end|. If any are found, all but the most recent are deleted. - // Returns the number of duplicate cookies that were deleted. - int TrimDuplicateCookiesForKey(const std::string& key, - CookieMap::iterator begin, - CookieMap::iterator end); - - void SetDefaultCookieableSchemes(); - - void FindCookiesForHostAndDomain(const GURL& url, - const CookieOptions& options, - bool update_access_time, - std::vector<CanonicalCookie*>* cookies); - - void FindCookiesForKey(const std::string& key, - const GURL& url, - const CookieOptions& options, - const base::Time& current, - bool update_access_time, - std::vector<CanonicalCookie*>* cookies); - - // Delete any cookies that are equivalent to |ecc| (same path, domain, etc). - // If |skip_httponly| is true, httponly cookies will not be deleted. The - // return value with be true if |skip_httponly| skipped an httponly cookie. - // |key| is the key to find the cookie in cookies_; see the comment before - // the CookieMap typedef for details. - // NOTE: There should never be more than a single matching equivalent cookie. - bool DeleteAnyEquivalentCookie(const std::string& key, - const CanonicalCookie& ecc, - bool skip_httponly, - bool already_expired); - - // Takes ownership of *cc. - void InternalInsertCookie(const std::string& key, - 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, - const base::Time& creation_time, - const CookieOptions& options); - - void InternalUpdateCookieAccessTime(CanonicalCookie* cc, - const base::Time& current_time); - - // |deletion_cause| argument is used for collecting statistics and choosing - // the correct Delegate::ChangeCause for OnCookieChanged notifications. - void InternalDeleteCookie(CookieMap::iterator it, bool sync_to_store, - DeletionCause deletion_cause); - - // If the number of cookies for CookieMap key |key|, or globally, are - // over the preset maximums above, garbage collect, first for the host and - // then globally. See comments above garbage collection threshold - // constants for details. - // - // Returns the number of cookies deleted (useful for debugging). - int GarbageCollect(const base::Time& current, const std::string& key); - - // Helper for GarbageCollect(); can be called directly as well. Deletes - // all expired cookies in |itpair|. If |cookie_its| is non-NULL, it is - // populated with all the non-expired cookies from |itpair|. - // - // Returns the number of cookies deleted. - int GarbageCollectExpired(const base::Time& current, - const CookieMapItPair& itpair, - std::vector<CookieMap::iterator>* cookie_its); - - // Helper for GarbageCollect(). Deletes all cookies in the list - // that were accessed before |keep_accessed_after|, using DeletionCause - // |cause|. If |keep_accessed_after| is null, deletes all cookies in the - // list. Returns the number of cookies deleted. - int GarbageCollectDeleteList(const base::Time& current, - const base::Time& keep_accessed_after, - DeletionCause cause, - std::vector<CookieMap::iterator>& cookie_its); - - // Find the key (for lookup in cookies_) based on the given domain. - // See comment on keys before the CookieMap typedef. - std::string GetKey(const std::string& domain) const; - - bool HasCookieableScheme(const GURL& url); - - // Statistics support - - // This function should be called repeatedly, and will record - // statistics if a sufficient time period has passed. - void RecordPeriodicStats(const base::Time& current_time); - - // Initialize the above variables; should only be called from - // the constructor. - void InitializeHistograms(); - - // The resolution of our time isn't enough, so we do something - // ugly and increment when we've seen the same time twice. - base::Time CurrentTime(); - - // 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_; - base::Histogram* histogram_between_access_interval_minutes_; - base::Histogram* histogram_evicted_last_access_minutes_; - base::Histogram* histogram_count_; - base::Histogram* histogram_domain_count_; - base::Histogram* histogram_etldp1_count_; - base::Histogram* histogram_domain_per_etldp1_count_; - base::Histogram* histogram_number_duplicate_db_cookies_; - base::Histogram* histogram_cookie_deletion_cause_; - base::Histogram* histogram_time_get_; - base::Histogram* histogram_time_mac_; - base::Histogram* histogram_time_blocked_on_load_; - - CookieMap cookies_; - - // Indicates whether the cookie store has been initialized. This happens - // lazily in InitStoreIfNecessary(). - bool initialized_; - - // Indicates whether loading from the backend store is completed and - // calls may be immediately processed. - bool loaded_; - - // 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_; - - scoped_refptr<PersistentCookieStore> store_; - - base::Time last_time_seen_; - - // Minimum delay after updating a cookie's LastAccessDate before we will - // update it again. - const base::TimeDelta last_access_threshold_; - - // Approximate date of access time of least recently accessed cookie - // in |cookies_|. Note that this is not guaranteed to be accurate, only a) - // to be before or equal to the actual time, and b) to be accurate - // immediately after a garbage collection that scans through all the cookies. - // 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 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_; - - // Lock for thread-safety - base::Lock lock_; - - base::Time last_statistic_record_time_; - - bool keep_expired_cookies_; - bool persist_session_cookies_; - - static bool enable_file_scheme_; - - DISALLOW_COPY_AND_ASSIGN(CookieMonster); -}; - -class NET_EXPORT CookieMonster::CanonicalCookie { - public: - - // These constructors do no validation or canonicalization of their inputs; - // the resulting CanonicalCookies should not be relied on to be canonical - // unless the caller has done appropriate validation and canonicalization - // themselves. - CanonicalCookie(); - CanonicalCookie(const GURL& url, - const std::string& name, - const std::string& value, - const std::string& domain, - const std::string& path, - const std::string& mac_key, - const std::string& mac_algorithm, - const base::Time& creation, - const base::Time& expiration, - const base::Time& last_access, - bool secure, - bool httponly, - bool has_expires, - bool is_persistent); - - // This constructor does canonicalization but not validation. - // The result of this constructor should not be relied on in contexts - // in which pre-validation of the ParsedCookie has not been done. - CanonicalCookie(const GURL& url, const ParsedCookie& pc); - - ~CanonicalCookie(); - - // Supports the default copy constructor. - - // Creates a canonical cookie from parsed cookie. - // Canonicalizes and validates inputs. May return NULL if an attribute - // value is invalid. - static CanonicalCookie* Create(const GURL& url, - const ParsedCookie& pc); - - // Creates a canonical cookie from unparsed attribute values. - // Canonicalizes and validates inputs. May return NULL if an attribute - // value is invalid. - static CanonicalCookie* Create(const GURL& url, - const std::string& name, - const std::string& value, - const std::string& domain, - const std::string& path, - const std::string& mac_key, - const std::string& mac_algorithm, - const base::Time& creation, - const base::Time& expiration, - bool secure, - bool http_only, - bool is_persistent); - - const std::string& Source() const { return source_; } - const std::string& Name() const { return name_; } - const std::string& Value() const { return value_; } - const std::string& Domain() const { return domain_; } - const std::string& Path() const { return path_; } - const std::string& MACKey() const { return mac_key_; } - const std::string& MACAlgorithm() const { return mac_algorithm_; } - const base::Time& CreationDate() const { return creation_date_; } - const base::Time& LastAccessDate() const { return last_access_date_; } - bool DoesExpire() const { return has_expires_; } - bool IsPersistent() const { return is_persistent_; } - const base::Time& ExpiryDate() const { return expiry_date_; } - bool IsSecure() const { return secure_; } - bool IsHttpOnly() const { return httponly_; } - bool IsDomainCookie() const { - return !domain_.empty() && domain_[0] == '.'; } - bool IsHostCookie() const { return !IsDomainCookie(); } - - bool IsExpired(const base::Time& current) { - return has_expires_ && current >= expiry_date_; - } - - // Are the cookies considered equivalent in the eyes of RFC 2965. - // The RFC says that name must match (case-sensitive), domain must - // match (case insensitive), and path must match (case sensitive). - // For the case insensitive domain compare, we rely on the domain - // having been canonicalized (in - // GetCookieDomainWithString->CanonicalizeHost). - bool IsEquivalent(const CanonicalCookie& ecc) const { - // It seems like it would make sense to take secure and httponly into - // account, but the RFC doesn't specify this. - // NOTE: Keep this logic in-sync with TrimDuplicateCookiesForHost(). - return (name_ == ecc.Name() && domain_ == ecc.Domain() - && path_ == ecc.Path()); - } - - void SetLastAccessDate(const base::Time& date) { - last_access_date_ = date; - } - - bool IsOnPath(const std::string& url_path) const; - bool IsDomainMatch(const std::string& scheme, const std::string& host) const; - - std::string DebugString() const; - - // Returns the cookie source when cookies are set for |url|. This function - // is public for unit test purposes only. - static std::string GetCookieSourceFromURL(const GURL& url); - - private: - // Gives the session cookie an expiration time if needed - void SetSessionCookieExpiryTime(); - - // The source member of a canonical cookie is the origin of the URL that tried - // to set this cookie, minus the port number if any. This field is not - // persistent though; its only used in the in-tab cookies dialog to show the - // user the source URL. This is used for both allowed and blocked cookies. - // When a CanonicalCookie is constructed from the backing store (common case) - // this field will be null. CanonicalCookie consumers should not rely on - // this field unless they guarantee that the creator of those - // CanonicalCookies properly initialized the field. - // TODO(abarth): We might need to make this field persistent for MAC cookies. - std::string source_; - std::string name_; - std::string value_; - std::string domain_; - std::string path_; - std::string mac_key_; // TODO(abarth): Persist to disk. - std::string mac_algorithm_; // TODO(abarth): Persist to disk. - base::Time creation_date_; - base::Time expiry_date_; - base::Time last_access_date_; - bool secure_; - bool httponly_; - bool has_expires_; - bool is_persistent_; -}; - -class CookieMonster::Delegate - : public base::RefCountedThreadSafe<CookieMonster::Delegate> { - public: - // The publicly relevant reasons a cookie might be changed. - enum ChangeCause { - // The cookie was changed directly by a consumer's action. - CHANGE_COOKIE_EXPLICIT, - // The cookie was automatically removed due to an insert operation that - // overwrote it. - CHANGE_COOKIE_OVERWRITE, - // The cookie was automatically removed as it expired. - CHANGE_COOKIE_EXPIRED, - // The cookie was automatically evicted during garbage collection. - CHANGE_COOKIE_EVICTED, - // The cookie was overwritten with an already-expired expiration date. - CHANGE_COOKIE_EXPIRED_OVERWRITE - }; - - // Will be called when a cookie is added or removed. The function is passed - // the respective |cookie| which was added to or removed from the cookies. - // If |removed| is true, the cookie was deleted, and |cause| will be set - // to the reason for it's removal. If |removed| is false, the cookie was - // added, and |cause| will be set to CHANGE_COOKIE_EXPLICIT. - // - // As a special case, note that updating a cookie's properties is implemented - // as a two step process: the cookie to be updated is first removed entirely, - // generating a notification with cause CHANGE_COOKIE_OVERWRITE. Afterwards, - // a new cookie is written with the updated values, generating a notification - // with cause CHANGE_COOKIE_EXPLICIT. - virtual void OnCookieChanged(const CookieMonster::CanonicalCookie& cookie, - bool removed, - ChangeCause cause) = 0; - protected: - friend class base::RefCountedThreadSafe<CookieMonster::Delegate>; - virtual ~Delegate() {} -}; - -class NET_EXPORT CookieMonster::ParsedCookie { - public: - typedef std::pair<std::string, std::string> TokenValuePair; - typedef std::vector<TokenValuePair> PairList; - - // The maximum length of a cookie string we will try to parse - static const size_t kMaxCookieSize = 4096; - // The maximum number of Token/Value pairs. Shouldn't have more than 8. - static const int kMaxPairs = 16; - - // Construct from a cookie string like "BLAH=1; path=/; domain=.google.com" - ParsedCookie(const std::string& cookie_line); - ~ParsedCookie(); - - // You should not call any other methods on the class if !IsValid - bool IsValid() const { return is_valid_; } - - const std::string& Name() const { return pairs_[0].first; } - const std::string& Token() const { return Name(); } - const std::string& Value() const { return pairs_[0].second; } - - bool HasPath() const { return path_index_ != 0; } - const std::string& Path() const { return pairs_[path_index_].second; } - bool HasDomain() const { return domain_index_ != 0; } - const std::string& Domain() const { return pairs_[domain_index_].second; } - bool HasMACKey() const { return mac_key_index_ != 0; } - const std::string& MACKey() const { return pairs_[mac_key_index_].second; } - bool HasMACAlgorithm() const { return mac_algorithm_index_ != 0; } - const std::string& MACAlgorithm() const { - return pairs_[mac_algorithm_index_].second; - } - bool HasExpires() const { return expires_index_ != 0; } - const std::string& Expires() const { return pairs_[expires_index_].second; } - bool HasMaxAge() const { return maxage_index_ != 0; } - const std::string& MaxAge() const { return pairs_[maxage_index_].second; } - bool IsSecure() const { return secure_index_ != 0; } - bool IsHttpOnly() const { return httponly_index_ != 0; } - - // Returns the number of attributes, for example, returning 2 for: - // "BLAH=hah; path=/; domain=.google.com" - size_t NumberOfAttributes() const { return pairs_.size() - 1; } - - // For debugging only! - std::string DebugString() const; - - // Returns an iterator pointing to the first terminator character found in - // the given string. - static std::string::const_iterator FindFirstTerminator(const std::string& s); - - // Given iterators pointing to the beginning and end of a string segment, - // returns as output arguments token_start and token_end to the start and end - // positions of a cookie attribute token name parsed from the segment, and - // updates the segment iterator to point to the next segment to be parsed. - // If no token is found, the function returns false. - static bool ParseToken(std::string::const_iterator* it, - const std::string::const_iterator& end, - std::string::const_iterator* token_start, - std::string::const_iterator* token_end); - - // Given iterators pointing to the beginning and end of a string segment, - // returns as output arguments value_start and value_end to the start and end - // positions of a cookie attribute value parsed from the segment, and updates - // the segment iterator to point to the next segment to be parsed. - static void ParseValue(std::string::const_iterator* it, - const std::string::const_iterator& end, - std::string::const_iterator* value_start, - std::string::const_iterator* value_end); - - // Same as the above functions, except the input is assumed to contain the - // desired token/value and nothing else. - static std::string ParseTokenString(const std::string& token); - static std::string ParseValueString(const std::string& value); - - private: - static const char kTerminator[]; - static const int kTerminatorLen; - static const char kWhitespace[]; - static const char kValueSeparator[]; - static const char kTokenSeparator[]; - - void ParseTokenValuePairs(const std::string& cookie_line); - void SetupAttributes(); - - PairList pairs_; - bool is_valid_; - // These will default to 0, but that should never be valid since the - // 0th index is the user supplied token/value, not an attribute. - // We're really never going to have more than like 8 attributes, so we - // could fit these into 3 bits each if we're worried about size... - size_t path_index_; - size_t domain_index_; - size_t mac_key_index_; - size_t mac_algorithm_index_; - size_t expires_index_; - size_t maxage_index_; - size_t secure_index_; - size_t httponly_index_; - - DISALLOW_COPY_AND_ASSIGN(ParsedCookie); -}; - -typedef base::RefCountedThreadSafe<CookieMonster::PersistentCookieStore> - RefcountedPersistentCookieStore; - -class CookieMonster::PersistentCookieStore - : public RefcountedPersistentCookieStore { - public: - virtual ~PersistentCookieStore() {} - - typedef base::Callback<void(const std::vector< - CookieMonster::CanonicalCookie*>&)> LoadedCallback; - - // Initializes the store and retrieves the existing cookies. This will be - // 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; - virtual void DeleteCookie(const CanonicalCookie& cc) = 0; - - // Sets the value of the user preference whether the persistent storage - // must be deleted upon destruction. - virtual void SetClearLocalStateOnExit(bool clear_local_state) = 0; - - // Flushes the store and posts |callback| when complete. - virtual void Flush(const base::Closure& callback) = 0; - - protected: - PersistentCookieStore() {} - - private: - DISALLOW_COPY_AND_ASSIGN(PersistentCookieStore); -}; - -class CookieList : public std::vector<CookieMonster::CanonicalCookie> { -}; - -} // namespace net +#include "net/cookies/cookie_monster.h" #endif // NET_BASE_COOKIE_MONSTER_H_ diff --git a/net/base/cookie_monster_store_test.h b/net/base/cookie_monster_store_test.h index e341f7d..d65b3fb 100644 --- a/net/base/cookie_monster_store_test.h +++ b/net/base/cookie_monster_store_test.h @@ -1,206 +1,14 @@ -// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// 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. -// 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. +// Provides a temporary redirection while clients are updated to use the new +// path. #ifndef NET_BASE_COOKIE_MONSTER_STORE_TEST_H_ #define NET_BASE_COOKIE_MONSTER_STORE_TEST_H_ #pragma once -#include <map> -#include <string> -#include <utility> -#include <vector> -#include "net/base/cookie_monster.h" - -namespace base { -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 { - ADD, - UPDATE_ACCESS_TIME, - REMOVE, - }; - - CookieStoreCommand(Type type, - const CookieMonster::CanonicalCookie& cookie) - : type(type), - cookie(cookie) {} - - Type type; - 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 CookieMonster::PersistentCookieStore { - public: - typedef std::vector<CookieStoreCommand> CommandList; - - MockPersistentCookieStore(); - virtual ~MockPersistentCookieStore(); - - void SetLoadExpectation( - bool return_value, - const std::vector<CookieMonster::CanonicalCookie*>& result); - - const CommandList& commands() const { - return commands_; - } - - 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; - - virtual void UpdateCookieAccessTime( - const CookieMonster::CanonicalCookie& cookie) OVERRIDE; - - virtual void DeleteCookie( - const CookieMonster::CanonicalCookie& cookie) OVERRIDE; - - virtual void Flush(const base::Closure& callback) OVERRIDE; - - // No files are created so nothing to clear either - virtual void SetClearLocalStateOnExit(bool clear_local_state) OVERRIDE; - - private: - CommandList commands_; - - // 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); -}; - -// Mock for CookieMonster::Delegate -class MockCookieMonsterDelegate : public CookieMonster::Delegate { - public: - typedef std::pair<CookieMonster::CanonicalCookie, bool> - CookieNotification; - - MockCookieMonsterDelegate(); - - const std::vector<CookieNotification>& changes() const { return changes_; } - - void reset() { changes_.clear(); } - - virtual void OnCookieChanged( - const CookieMonster::CanonicalCookie& cookie, - bool removed, - CookieMonster::Delegate::ChangeCause cause) OVERRIDE; - - private: - virtual ~MockCookieMonsterDelegate(); - - std::vector<CookieNotification> changes_; - - DISALLOW_COPY_AND_ASSIGN(MockCookieMonsterDelegate); -}; - -// Helper to build a single CanonicalCookie. -CookieMonster::CanonicalCookie BuildCanonicalCookie( - const std::string& key, - const std::string& cookie_line, - const base::Time& creation_time); - -// Helper to build a list of CanonicalCookie*s. -void AddCookieToList( - const std::string& key, - const std::string& cookie_line, - const base::Time& creation_time, - std::vector<CookieMonster::CanonicalCookie*>* out_list); - -// Just act like a backing database. Keep cookie information from -// Add/Update/Delete and regurgitate it when Load is called. -class MockSimplePersistentCookieStore - : public CookieMonster::PersistentCookieStore { - public: - MockSimplePersistentCookieStore(); - virtual ~MockSimplePersistentCookieStore(); - - 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; - - virtual void UpdateCookieAccessTime( - const CookieMonster::CanonicalCookie& cookie) OVERRIDE; - - virtual void DeleteCookie( - const CookieMonster::CanonicalCookie& cookie) OVERRIDE; - - virtual void Flush(const base::Closure& callback) OVERRIDE; - - 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 -// MockSimplePersistentCookieStore for garbage collection testing. -// -// Fill the store through import with |num_cookies| cookies, |num_old_cookies| -// with access time Now()-days_old, the rest with access time Now(). -// Do two SetCookies(). Return whether each of the two SetCookies() took -// longer than |gc_perf_micros| to complete, and how many cookie were -// left in the store afterwards. -CookieMonster* CreateMonsterFromStoreForGC( - int num_cookies, - int num_old_cookies, - int days_old); - -} // namespace net +#include "net/cookies/cookie_monster_store_test.h" #endif // NET_BASE_COOKIE_MONSTER_STORE_TEST_H_ diff --git a/net/base/cookie_options.h b/net/base/cookie_options.h index 203adaf..74c9664 100644 --- a/net/base/cookie_options.h +++ b/net/base/cookie_options.h @@ -1,40 +1,14 @@ -// Copyright (c) 2006-2009 The Chromium Authors. All rights reserved. +// 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. -// Brought to you by number 42. +// Provides a temporary redirection while clients are updated to use the new +// path. #ifndef NET_BASE_COOKIE_OPTIONS_H_ #define NET_BASE_COOKIE_OPTIONS_H_ #pragma once -namespace net { - -class CookieOptions { - public: - // Default is to exclude httponly, which means: - // - reading operations will not return httponly cookies. - // - writing operations will not write httponly cookies. - CookieOptions() - : exclude_httponly_(true), - force_session_(false) { - } - - void set_exclude_httponly() { exclude_httponly_ = true; } - void set_include_httponly() { exclude_httponly_ = false; } - bool exclude_httponly() const { return exclude_httponly_; } - - // Forces a cookie to be saved as a session cookie. If the expiration time of - // the cookie is in the past, i.e. the cookie would end up being deleted, this - // option is ignored. See CookieMonsterTest.ForceSessionOnly. - void set_force_session() { force_session_ = true; } - bool force_session() const { return force_session_; } - - private: - bool exclude_httponly_; - bool force_session_; -}; -} // namespace net +#include "net/cookies/cookie_options.h" #endif // NET_BASE_COOKIE_OPTIONS_H_ - diff --git a/net/base/cookie_store.h b/net/base/cookie_store.h index 0336a9b..810836d 100644 --- a/net/base/cookie_store.h +++ b/net/base/cookie_store.h @@ -1,113 +1,14 @@ -// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// 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. -// Brought to you by number 42. +// Provides a temporary redirection while clients are updated to use the new +// path. #ifndef NET_BASE_COOKIE_STORE_H_ #define NET_BASE_COOKIE_STORE_H_ #pragma once -#include <string> -#include <vector> - -#include "base/basictypes.h" -#include "base/callback.h" -#include "base/memory/ref_counted.h" -#include "base/time.h" -#include "net/base/cookie_options.h" -#include "net/base/net_export.h" - -class GURL; - -namespace net { - -class CookieMonster; - -// An interface for storing and retrieving cookies. Implementations need to -// be thread safe as its methods can be accessed from IO as well as UI threads. -class NET_EXPORT CookieStore : public base::RefCountedThreadSafe<CookieStore> { - public: - // This struct contains additional consumer-specific information that might - // be stored with cookies; currently just MAC information, see: - // http://tools.ietf.org/html/draft-ietf-oauth-v2-http-mac - struct NET_EXPORT CookieInfo { - CookieInfo(); - ~CookieInfo(); - - // The name of the cookie. - std::string name; - // TODO(abarth): Add value if any clients need it. - - // The time at which the cookie was created. - base::Time creation_date; - - // The value of the MAC-Key and MAC-Algorithm attributes, if present. - std::string mac_key; - std::string mac_algorithm; - }; - - // Callback definitions. - typedef base::Callback <void( - const std::string& cookie_line, - const std::vector<CookieInfo>& cookie_infos)> GetCookieInfoCallback; - typedef base::Callback<void(const std::string& cookie)> - GetCookiesCallback; - typedef base::Callback<void(bool success)> SetCookiesCallback; - typedef base::Callback<void(int num_deleted)> DeleteCallback; - - - // Sets a single cookie. Expects a cookie line, like "a=1; domain=b.com". - // - // Fails either if the cookie is invalid or if this is a non-HTTPONLY cookie - // and it would overwrite an existing HTTPONLY cookie. - // Returns true if the cookie is successfully set. - virtual void SetCookieWithOptionsAsync( - const GURL& url, - const std::string& cookie_line, - const CookieOptions& options, - const SetCookiesCallback& callback) = 0; - - // TODO(???): what if the total size of all the cookies >4k, can we have a - // header that big or do we need multiple Cookie: headers? - // Note: Some sites, such as Facebook, occasionally use Cookie headers >4k. - // - // Simple interface, gets a cookie string "a=b; c=d" for the given URL. - // Use options to access httponly cookies. - virtual void GetCookiesWithOptionsAsync( - const GURL& url, const CookieOptions& options, - const GetCookiesCallback& callback) = 0; - - // This function is similar to GetCookiesWithOptions same functionality as - // GetCookiesWithOptions except that it additionally provides detailed - // information about the cookie contained in the cookie line. See |struct - // CookieInfo| above for details. - virtual void GetCookiesWithInfoAsync( - const GURL& url, - const CookieOptions& options, - const GetCookieInfoCallback& callback) = 0; - - // Deletes the passed in cookie for the specified URL. - virtual void DeleteCookieAsync(const GURL& url, - const std::string& cookie_name, - const base::Closure& callback) = 0; - - // Deletes all of the cookies that have a creation_date greater than or equal - // to |delete_begin| and less than |delete_end| - // Returns the number of cookies that have been deleted. - virtual void DeleteAllCreatedBetweenAsync(const base::Time& delete_begin, - const base::Time& delete_end, - const DeleteCallback& callback) = 0; - - // Returns the underlying CookieMonster. - virtual CookieMonster* GetCookieMonster() = 0; - - protected: - friend class base::RefCountedThreadSafe<CookieStore>; - CookieStore(); - virtual ~CookieStore(); -}; - -} // namespace net +#include "net/cookies/cookie_store.h" #endif // NET_BASE_COOKIE_STORE_H_ diff --git a/net/base/cookie_store_test_callbacks.h b/net/base/cookie_store_test_callbacks.h index 0957839..328a0b5 100644 --- a/net/base/cookie_store_test_callbacks.h +++ b/net/base/cookie_store_test_callbacks.h @@ -2,136 +2,13 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +// Provides a temporary redirection while clients are updated to use the new +// path. + #ifndef NET_BASE_COOKIE_STORE_TEST_CALLBACKS_H_ #define NET_BASE_COOKIE_STORE_TEST_CALLBACKS_H_ #pragma once -#include <string> -#include <vector> - -#include "net/base/cookie_store.h" - -class MessageLoop; - -namespace base { -class Thread; -} - -namespace net { - -// Defines common behaviour for the callbacks from GetCookies, SetCookies, etc. -// Asserts that the current thread is the expected invocation thread, sends a -// quit to the thread in which it was constructed. -class CookieCallback { - public: - // Indicates whether the callback has been called. - bool did_run() { return did_run_; } - - protected: - // Constructs a callback that expects to be called in the given thread and - // will, upon execution, send a QUIT to the constructing thread. - explicit CookieCallback(base::Thread* run_in_thread); - - // Constructs a callback that expects to be called in current thread and will - // send a QUIT to the constructing thread. - CookieCallback(); - - // Tests whether the current thread was the caller's thread. - // Sends a QUIT to the constructing thread. - void CallbackEpilogue(); - - private: - bool did_run_; - base::Thread* run_in_thread_; - MessageLoop* run_in_loop_; - MessageLoop* parent_loop_; - MessageLoop* loop_to_quit_; -}; - -// Callback implementations for the asynchronous CookieStore methods. - -class SetCookieCallback : public CookieCallback { - public: - SetCookieCallback(); - explicit SetCookieCallback(base::Thread* run_in_thread); - - void Run(bool result) { - result_ = result; - CallbackEpilogue(); - } - - bool result() { return result_; } - - private: - bool result_; -}; - -class GetCookieStringCallback : public CookieCallback { - public: - GetCookieStringCallback(); - explicit GetCookieStringCallback(base::Thread* run_in_thread); - - void Run(const std::string& cookie) { - cookie_ = cookie; - CallbackEpilogue(); - } - - const std::string& cookie() { return cookie_; } - - private: - std::string cookie_; -}; - -class GetCookiesWithInfoCallback : public CookieCallback { - public: - GetCookiesWithInfoCallback(); - explicit GetCookiesWithInfoCallback(base::Thread* run_in_thread); - ~GetCookiesWithInfoCallback(); - - void Run( - const std::string& cookie_line, - const std::vector<CookieStore::CookieInfo>& cookie_info) { - cookie_line_ = cookie_line; - cookie_info_ = cookie_info; - CallbackEpilogue(); - } - - const std::string& cookie_line() { return cookie_line_; } - const std::vector<CookieStore::CookieInfo>& cookie_info() { - return cookie_info_; - } - - private: - std::string cookie_line_; - std::vector<CookieStore::CookieInfo> cookie_info_; -}; - -class DeleteCallback : public CookieCallback { - public: - DeleteCallback(); - explicit DeleteCallback(base::Thread* run_in_thread); - - void Run(int num_deleted) { - num_deleted_ = num_deleted; - CallbackEpilogue(); - } - - int num_deleted() { return num_deleted_; } - - private: - int num_deleted_; -}; - -class DeleteCookieCallback : public CookieCallback { - public: - DeleteCookieCallback(); - explicit DeleteCookieCallback(base::Thread* run_in_thread); - - void Run() { - CallbackEpilogue(); - } -}; - -} // namespace net +#include "net/cookies/cookie_store_test_callbacks.h" #endif // NET_BASE_COOKIE_STORE_TEST_CALLBACKS_H_ diff --git a/net/base/cookie_store_test_helpers.h b/net/base/cookie_store_test_helpers.h index 3474655..04d17b0 100644 --- a/net/base/cookie_store_test_helpers.h +++ b/net/base/cookie_store_test_helpers.h @@ -1,105 +1,14 @@ -// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// 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. +// Provides a temporary redirection while clients are updated to use the new +// path. + #ifndef NET_BASE_COOKIE_STORE_TEST_HELPERS_H_ #define NET_BASE_COOKIE_STORE_TEST_HELPERS_H_ #pragma once -#include "net/base/cookie_monster.h" - -#include <string> -#include <vector> - -#include "base/callback_forward.h" -#include "testing/gtest/include/gtest/gtest.h" - -namespace net { - -class DelayedCookieMonster : public CookieStore { - public: - DelayedCookieMonster(); - - // Call the asynchronous CookieMonster function, expect it to immediately - // invoke the internal callback. - // Post a delayed task to invoke the original callback with the results. - - virtual void SetCookieWithOptionsAsync( - const GURL& url, - const std::string& cookie_line, - const CookieOptions& options, - const CookieMonster::SetCookiesCallback& callback) OVERRIDE; - - virtual void GetCookiesWithOptionsAsync( - const GURL& url, const CookieOptions& options, - const CookieMonster::GetCookiesCallback& callback) OVERRIDE; - - virtual void GetCookiesWithInfoAsync( - const GURL& url, - const CookieOptions& options, - const CookieMonster::GetCookieInfoCallback& callback) OVERRIDE; - - virtual bool SetCookieWithOptions(const GURL& url, - const std::string& cookie_line, - const CookieOptions& options); - - virtual std::string GetCookiesWithOptions(const GURL& url, - const CookieOptions& options); - - virtual void GetCookiesWithInfo(const GURL& url, - const CookieOptions& options, - std::string* cookie_line, - std::vector<CookieInfo>* cookie_infos); - - virtual void DeleteCookie(const GURL& url, - const std::string& cookie_name); - - virtual void DeleteCookieAsync(const GURL& url, - const std::string& cookie_name, - const base::Closure& callback) OVERRIDE; - - virtual void DeleteAllCreatedBetweenAsync( - const base::Time& delete_begin, - const base::Time& delete_end, - const DeleteCallback& callback) OVERRIDE; - - virtual CookieMonster* GetCookieMonster() OVERRIDE; - - private: - - // Be called immediately from CookieMonster. - - void GetCookiesInternalCallback( - const std::string& cookie_line, - const std::vector<CookieStore::CookieInfo>& cookie_info); - - void SetCookiesInternalCallback(bool result); - - void GetCookiesWithOptionsInternalCallback(const std::string& cookie); - - // Invoke the original callbacks. - - void InvokeGetCookiesCallback( - const CookieMonster::GetCookieInfoCallback& callback); - - void InvokeSetCookiesCallback( - const CookieMonster::SetCookiesCallback& callback); - - void InvokeGetCookieStringCallback( - const CookieMonster::GetCookiesCallback& callback); - - friend class base::RefCountedThreadSafe<DelayedCookieMonster>; - virtual ~DelayedCookieMonster(); - - scoped_refptr<CookieMonster> cookie_monster_; - - bool did_run_; - bool result_; - std::string cookie_; - std::string cookie_line_; - std::vector<CookieStore::CookieInfo> cookie_info_; -}; - -} // namespace net +#include "net/cookies/cookie_store_test_helpers.h" #endif // NET_BASE_COOKIE_STORE_TEST_HELPERS_H_ diff --git a/net/base/cookie_store_unittest.h b/net/base/cookie_store_unittest.h index 862edbb..42fff04 100644 --- a/net/base/cookie_store_unittest.h +++ b/net/base/cookie_store_unittest.h @@ -2,1054 +2,13 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +// Provides a temporary redirection while clients are updated to use the new +// path. + #ifndef NET_BASE_COOKIE_STORE_UNITTEST_H_ #define NET_BASE_COOKIE_STORE_UNITTEST_H_ #pragma once -#include "base/bind.h" -#include "base/message_loop.h" -#include "base/string_tokenizer.h" -#include "base/threading/thread.h" -#include "googleurl/src/gurl.h" -#include "net/base/cookie_monster.h" -#include "net/base/cookie_store.h" -#include "net/base/cookie_store_test_callbacks.h" -#include "testing/gtest/include/gtest/gtest.h" - -// This file declares unittest templates that can be used to test common -// behavior of any CookieStore implementation. -// See cookie_monster_unittest.cc for an example of an implementation. - -namespace net { - -using base::Thread; - -const int kTimeout = 1000; - -const char kUrlFtp[] = "ftp://ftp.google.izzle/"; -const char kUrlGoogle[] = "http://www.google.izzle"; -const char kUrlGoogleFoo[] = "http://www.google.izzle/foo"; -const char kUrlGoogleBar[] = "http://www.google.izzle/bar"; -const char kUrlGoogleSecure[] = "https://www.google.izzle"; -const char kValidCookieLine[] = "A=B; path=/"; -const char kValidDomainCookieLine[] = "A=B; path=/; domain=google.izzle"; - -// The CookieStoreTestTraits must have the following members: -// struct CookieStoreTestTraits { -// // Factory function. -// static scoped_refptr<CookieStore> Create(); -// -// // The cookie store is a CookieMonster. Only used to test -// // GetCookieMonster(). -// static const bool is_cookie_monster; -// -// // The cookie store supports cookies with the exclude_httponly() option. -// static const bool supports_http_only; -// -// // The cookie store supports the GetCookiesWithInfoAsync() method. -// static const bool supports_cookies_with_info; -// -// // The cookie store is able to make the difference between the ".com" -// // and the "com" domains. -// static const bool supports_non_dotted_domains; -// -// // The cookie store handles the domains with trailing dots (such as "com.") -// // correctly. -// static const bool supports_trailing_dots; -// -// // The cookie store rejects cookies for invalid schemes such as ftp. -// static const bool filters_schemes; -// -// // The cookie store has a bug happening when a path is a substring of -// // another. -// static const bool has_path_prefix_bug; -// -// // Time to wait between two cookie insertions to ensure that cookies have -// // different creation times. -// static const int creation_time_granularity_in_ms; -// }; - -template <class CookieStoreTestTraits> -class CookieStoreTest : public testing::Test { - protected: - CookieStoreTest() - : url_google_(kUrlGoogle), - url_google_secure_(kUrlGoogleSecure), - url_google_foo_(kUrlGoogleFoo), - url_google_bar_(kUrlGoogleBar) { - // This test may be used outside of the net test suite, and thus may not - // have a message loop. - if (!MessageLoop::current()) - message_loop_.reset(new MessageLoop); - weak_factory_.reset( - new base::WeakPtrFactory<MessageLoop>(MessageLoop::current())); - } - - // Helper methods for the asynchronous Cookie Store API that call the - // asynchronous method and then pump the loop until the callback is invoked, - // finally returning the value. - - std::string GetCookies(CookieStore* cs, const GURL& url) { - DCHECK(cs); - CookieOptions options; - if (!CookieStoreTestTraits::supports_http_only) - options.set_include_httponly(); - GetCookieStringCallback callback; - cs->GetCookiesWithOptionsAsync( - url, options, - base::Bind(&GetCookieStringCallback::Run, base::Unretained(&callback))); - RunFor(kTimeout); - EXPECT_TRUE(callback.did_run()); - return callback.cookie(); - } - - std::string GetCookiesWithOptions(CookieStore* cs, - const GURL& url, - const CookieOptions& options) { - DCHECK(cs); - GetCookieStringCallback callback; - cs->GetCookiesWithOptionsAsync( - url, options, base::Bind(&GetCookieStringCallback::Run, - base::Unretained(&callback))); - RunFor(kTimeout); - EXPECT_TRUE(callback.did_run()); - return callback.cookie(); - } - - void GetCookiesWithInfo(CookieStore* cs, - const GURL& url, - const CookieOptions& options, - std::string* cookie_line, - std::vector<CookieStore::CookieInfo>* cookie_info) { - DCHECK(cs); - DCHECK(cookie_line); - DCHECK(cookie_info); - GetCookiesWithInfoCallback callback; - cs->GetCookiesWithInfoAsync( - url, options, base::Bind(&GetCookiesWithInfoCallback::Run, - base::Unretained(&callback))); - RunFor(kTimeout); - EXPECT_TRUE(callback.did_run()); - *cookie_line = callback.cookie_line(); - *cookie_info = callback.cookie_info(); - } - - bool SetCookieWithOptions(CookieStore* cs, - const GURL& url, - const std::string& cookie_line, - const CookieOptions& options) { - DCHECK(cs); - SetCookieCallback callback; - cs->SetCookieWithOptionsAsync( - url, cookie_line, options, - base::Bind(&SetCookieCallback::Run, base::Unretained(&callback))); - RunFor(kTimeout); - EXPECT_TRUE(callback.did_run()); - return callback.result(); - } - - bool SetCookie(CookieStore* cs, - const GURL& url, - const std::string& cookie_line) { - CookieOptions options; - if (!CookieStoreTestTraits::supports_http_only) - options.set_include_httponly(); - return SetCookieWithOptions(cs, url, cookie_line, options); - } - - void DeleteCookie(CookieStore* cs, - const GURL& url, - const std::string& cookie_name) { - DCHECK(cs); - DeleteCookieCallback callback; - cs->DeleteCookieAsync( - url, cookie_name, - base::Bind(&DeleteCookieCallback::Run, base::Unretained(&callback))); - RunFor(kTimeout); - EXPECT_TRUE(callback.did_run()); - } - - int DeleteCreatedBetween(CookieStore* cs, - const base::Time& delete_begin, - const base::Time& delete_end) { - DCHECK(cs); - DeleteCallback callback; - cs->DeleteAllCreatedBetweenAsync( - delete_begin, delete_end, - base::Bind(&DeleteCallback::Run, base::Unretained(&callback))); - RunFor(kTimeout); - EXPECT_TRUE(callback.did_run()); - return callback.num_deleted(); - } - - void RunFor(int ms) { - // Runs the test thread message loop for up to |ms| milliseconds. - MessageLoop::current()->PostDelayedTask( - FROM_HERE, base::Bind(&MessageLoop::Quit, weak_factory_->GetWeakPtr()), - base::TimeDelta::FromMilliseconds(ms)); - MessageLoop::current()->Run(); - weak_factory_->InvalidateWeakPtrs(); - } - - scoped_refptr<CookieStore> GetCookieStore() { - return CookieStoreTestTraits::Create(); - } - - // Compares two cookie lines. - void MatchCookieLines(const std::string& line1, const std::string& line2) { - EXPECT_EQ(TokenizeCookieLine(line1), TokenizeCookieLine(line2)); - } - - // Check the cookie line by polling until equality or a timeout is reached. - void MatchCookieLineWithTimeout(CookieStore* cs, - const GURL& url, - const std::string& line) { - std::string cookies = GetCookies(cs, url); - bool matched = (TokenizeCookieLine(line) == TokenizeCookieLine(cookies)); - base::Time polling_end_date = base::Time::Now() + - base::TimeDelta::FromMilliseconds( - CookieStoreTestTraits::creation_time_granularity_in_ms); - - while (!matched && base::Time::Now() <= polling_end_date) { - base::PlatformThread::Sleep(base::TimeDelta::FromMilliseconds(10)); - cookies = GetCookies(cs, url); - matched = (TokenizeCookieLine(line) == TokenizeCookieLine(cookies)); - } - - EXPECT_TRUE(matched) << "\"" << cookies - << "\" does not match \"" << line << "\""; - } - - GURL url_google_; - GURL url_google_secure_; - GURL url_google_foo_; - GURL url_google_bar_; - - scoped_ptr<base::WeakPtrFactory<MessageLoop> > weak_factory_; - scoped_ptr<MessageLoop> message_loop_; - - private: - // Returns a set of strings of type "name=value". Fails in case of duplicate. - std::set<std::string> TokenizeCookieLine(const std::string& line) { - std::set<std::string> tokens; - StringTokenizer tokenizer(line, " ;"); - while (tokenizer.GetNext()) - EXPECT_TRUE(tokens.insert(tokenizer.token()).second); - return tokens; - } -}; - -TYPED_TEST_CASE_P(CookieStoreTest); - -TYPED_TEST_P(CookieStoreTest, TypeTest) { - scoped_refptr<CookieStore> cs(this->GetCookieStore()); - EXPECT_EQ(cs->GetCookieMonster(), - (TypeParam::is_cookie_monster) ? - static_cast<CookieMonster*>(cs.get()) : NULL); -} - -TYPED_TEST_P(CookieStoreTest, DomainTest) { - scoped_refptr<CookieStore> cs(this->GetCookieStore()); - EXPECT_TRUE(this->SetCookie(cs, this->url_google_, "A=B")); - this->MatchCookieLines("A=B", this->GetCookies(cs, this->url_google_)); - EXPECT_TRUE(this->SetCookie(cs, this->url_google_, - "C=D; domain=.google.izzle")); - this->MatchCookieLines("A=B; C=D", this->GetCookies(cs, this->url_google_)); - - // Verify that A=B was set as a host cookie rather than a domain - // cookie -- should not be accessible from a sub sub-domain. - this->MatchCookieLines("C=D", - this->GetCookies(cs, GURL("http://foo.www.google.izzle"))); - - // Test and make sure we find domain cookies on the same domain. - EXPECT_TRUE(this->SetCookie(cs, this->url_google_, - "E=F; domain=.www.google.izzle")); - this->MatchCookieLines("A=B; C=D; E=F", - this->GetCookies(cs, this->url_google_)); - - // Test setting a domain= that doesn't start w/ a dot, should - // treat it as a domain cookie, as if there was a pre-pended dot. - EXPECT_TRUE(this->SetCookie(cs, this->url_google_, - "G=H; domain=www.google.izzle")); - this->MatchCookieLines("A=B; C=D; E=F; G=H", - this->GetCookies(cs, this->url_google_)); - - // Test domain enforcement, should fail on a sub-domain or something too deep. - EXPECT_FALSE(this->SetCookie(cs, this->url_google_, "I=J; domain=.izzle")); - this->MatchCookieLines("", this->GetCookies(cs, GURL("http://a.izzle"))); - EXPECT_FALSE(this->SetCookie(cs, this->url_google_, - "K=L; domain=.bla.www.google.izzle")); - this->MatchCookieLines("C=D; E=F; G=H", - this->GetCookies(cs, GURL("http://bla.www.google.izzle"))); - this->MatchCookieLines("A=B; C=D; E=F; G=H", - this->GetCookies(cs, this->url_google_)); -} - -// FireFox recognizes domains containing trailing periods as valid. -// IE and Safari do not. Assert the expected policy here. -TYPED_TEST_P(CookieStoreTest, DomainWithTrailingDotTest) { - scoped_refptr<CookieStore> cs(this->GetCookieStore()); - EXPECT_FALSE(this->SetCookie(cs, this->url_google_, - "a=1; domain=.www.google.com.")); - EXPECT_FALSE(this->SetCookie(cs, this->url_google_, - "b=2; domain=.www.google.com..")); - this->MatchCookieLines("", this->GetCookies(cs, this->url_google_)); -} - -// Test that cookies can bet set on higher level domains. -// http://b/issue?id=896491 -TYPED_TEST_P(CookieStoreTest, ValidSubdomainTest) { - scoped_refptr<CookieStore> cs(this->GetCookieStore()); - GURL url_abcd("http://a.b.c.d.com"); - GURL url_bcd("http://b.c.d.com"); - GURL url_cd("http://c.d.com"); - GURL url_d("http://d.com"); - - EXPECT_TRUE(this->SetCookie(cs, url_abcd, "a=1; domain=.a.b.c.d.com")); - EXPECT_TRUE(this->SetCookie(cs, url_abcd, "b=2; domain=.b.c.d.com")); - EXPECT_TRUE(this->SetCookie(cs, url_abcd, "c=3; domain=.c.d.com")); - EXPECT_TRUE(this->SetCookie(cs, url_abcd, "d=4; domain=.d.com")); - - this->MatchCookieLines("a=1; b=2; c=3; d=4", this->GetCookies(cs, url_abcd)); - this->MatchCookieLines("b=2; c=3; d=4", this->GetCookies(cs, url_bcd)); - this->MatchCookieLines("c=3; d=4", this->GetCookies(cs, url_cd)); - this->MatchCookieLines("d=4", this->GetCookies(cs, url_d)); - - // Check that the same cookie can exist on different sub-domains. - EXPECT_TRUE(this->SetCookie(cs, url_bcd, "X=bcd; domain=.b.c.d.com")); - EXPECT_TRUE(this->SetCookie(cs, url_bcd, "X=cd; domain=.c.d.com")); - this->MatchCookieLines("b=2; c=3; d=4; X=bcd; X=cd", - this->GetCookies(cs, url_bcd)); - this->MatchCookieLines("c=3; d=4; X=cd", this->GetCookies(cs, url_cd)); -} - -// Test that setting a cookie which specifies an invalid domain has -// no side-effect. An invalid domain in this context is one which does -// not match the originating domain. -// http://b/issue?id=896472 -TYPED_TEST_P(CookieStoreTest, InvalidDomainTest) { - { - scoped_refptr<CookieStore> cs(this->GetCookieStore()); - GURL url_foobar("http://foo.bar.com"); - - // More specific sub-domain than allowed. - EXPECT_FALSE(this->SetCookie(cs, url_foobar, - "a=1; domain=.yo.foo.bar.com")); - - EXPECT_FALSE(this->SetCookie(cs, url_foobar, "b=2; domain=.foo.com")); - EXPECT_FALSE(this->SetCookie(cs, url_foobar, "c=3; domain=.bar.foo.com")); - - // Different TLD, but the rest is a substring. - EXPECT_FALSE(this->SetCookie(cs, url_foobar, - "d=4; domain=.foo.bar.com.net")); - - // A substring that isn't really a parent domain. - EXPECT_FALSE(this->SetCookie(cs, url_foobar, "e=5; domain=ar.com")); - - // Completely invalid domains: - EXPECT_FALSE(this->SetCookie(cs, url_foobar, "f=6; domain=.")); - EXPECT_FALSE(this->SetCookie(cs, url_foobar, "g=7; domain=/")); - EXPECT_FALSE(this->SetCookie(cs, url_foobar, - "h=8; domain=http://foo.bar.com")); - EXPECT_FALSE(this->SetCookie(cs, url_foobar, "i=9; domain=..foo.bar.com")); - EXPECT_FALSE(this->SetCookie(cs, url_foobar, "j=10; domain=..bar.com")); - - // Make sure there isn't something quirky in the domain canonicalization - // that supports full URL semantics. - EXPECT_FALSE(this->SetCookie(cs, url_foobar, - "k=11; domain=.foo.bar.com?blah")); - EXPECT_FALSE(this->SetCookie(cs, url_foobar, - "l=12; domain=.foo.bar.com/blah")); - EXPECT_FALSE(this->SetCookie(cs, url_foobar, - "m=13; domain=.foo.bar.com:80")); - EXPECT_FALSE(this->SetCookie(cs, url_foobar, - "n=14; domain=.foo.bar.com:")); - EXPECT_FALSE(this->SetCookie(cs, url_foobar, - "o=15; domain=.foo.bar.com#sup")); - - this->MatchCookieLines("", this->GetCookies(cs, url_foobar)); - } - - { - // Make sure the cookie code hasn't gotten its subdomain string handling - // reversed, missed a suffix check, etc. It's important here that the two - // hosts below have the same domain + registry. - scoped_refptr<CookieStore> cs(this->GetCookieStore()); - GURL url_foocom("http://foo.com.com"); - EXPECT_FALSE(this->SetCookie(cs, url_foocom, - "a=1; domain=.foo.com.com.com")); - this->MatchCookieLines("", this->GetCookies(cs, url_foocom)); - } -} - -// Test the behavior of omitting dot prefix from domain, should -// function the same as FireFox. -// http://b/issue?id=889898 -TYPED_TEST_P(CookieStoreTest, DomainWithoutLeadingDotTest) { - { // The omission of dot results in setting a domain cookie. - scoped_refptr<CookieStore> cs(this->GetCookieStore()); - GURL url_hosted("http://manage.hosted.filefront.com"); - GURL url_filefront("http://www.filefront.com"); - EXPECT_TRUE(this->SetCookie(cs, url_hosted, - "sawAd=1; domain=filefront.com")); - this->MatchCookieLines("sawAd=1", this->GetCookies(cs, url_hosted)); - this->MatchCookieLines("sawAd=1", this->GetCookies(cs, url_filefront)); - } - - { // Even when the domains match exactly, don't consider it host cookie. - scoped_refptr<CookieStore> cs(this->GetCookieStore()); - GURL url("http://www.google.com"); - EXPECT_TRUE(this->SetCookie(cs, url, "a=1; domain=www.google.com")); - this->MatchCookieLines("a=1", this->GetCookies(cs, url)); - this->MatchCookieLines("a=1", - this->GetCookies(cs, GURL("http://sub.www.google.com"))); - this->MatchCookieLines("", - this->GetCookies(cs, GURL("http://something-else.com"))); - } -} - -// Test that the domain specified in cookie string is treated case-insensitive -// http://b/issue?id=896475. -TYPED_TEST_P(CookieStoreTest, CaseInsensitiveDomainTest) { - scoped_refptr<CookieStore> cs(this->GetCookieStore()); - GURL url("http://www.google.com"); - EXPECT_TRUE(this->SetCookie(cs, url, "a=1; domain=.GOOGLE.COM")); - EXPECT_TRUE(this->SetCookie(cs, url, "b=2; domain=.wWw.gOOgLE.coM")); - this->MatchCookieLines("a=1; b=2", this->GetCookies(cs, url)); -} - -TYPED_TEST_P(CookieStoreTest, TestIpAddress) { - GURL url_ip("http://1.2.3.4/weee"); - { - scoped_refptr<CookieStore> cs(this->GetCookieStore()); - EXPECT_TRUE(this->SetCookie(cs, url_ip, kValidCookieLine)); - this->MatchCookieLines("A=B", this->GetCookies(cs, url_ip)); - } - - { // IP addresses should not be able to set domain cookies. - scoped_refptr<CookieStore> cs(this->GetCookieStore()); - EXPECT_FALSE(this->SetCookie(cs, url_ip, "b=2; domain=.1.2.3.4")); - EXPECT_FALSE(this->SetCookie(cs, url_ip, "c=3; domain=.3.4")); - this->MatchCookieLines("", this->GetCookies(cs, url_ip)); - // It should be allowed to set a cookie if domain= matches the IP address - // exactly. This matches IE/Firefox, even though it seems a bit wrong. - EXPECT_FALSE(this->SetCookie(cs, url_ip, "b=2; domain=1.2.3.3")); - this->MatchCookieLines("", this->GetCookies(cs, url_ip)); - EXPECT_TRUE(this->SetCookie(cs, url_ip, "b=2; domain=1.2.3.4")); - this->MatchCookieLines("b=2", this->GetCookies(cs, url_ip)); - } -} - -// Test host cookies, and setting of cookies on TLD. -TYPED_TEST_P(CookieStoreTest, TestNonDottedAndTLD) { - { - scoped_refptr<CookieStore> cs(this->GetCookieStore()); - GURL url("http://com/"); - // Allow setting on "com", (but only as a host cookie). - EXPECT_TRUE(this->SetCookie(cs, url, "a=1")); - EXPECT_FALSE(this->SetCookie(cs, url, "b=2; domain=.com")); - EXPECT_FALSE(this->SetCookie(cs, url, "c=3; domain=com")); - this->MatchCookieLines("a=1", this->GetCookies(cs, url)); - // Make sure it doesn't show up for a normal .com, it should be a host - // not a domain cookie. - this->MatchCookieLines("", - this->GetCookies(cs, GURL("http://hopefully-no-cookies.com/"))); - if (TypeParam::supports_non_dotted_domains) { - this->MatchCookieLines("", this->GetCookies(cs, GURL("http://.com/"))); - } - } - - { - // http://com. should be treated the same as http://com. - scoped_refptr<CookieStore> cs(this->GetCookieStore()); - GURL url("http://com./index.html"); - if (TypeParam::supports_trailing_dots) { - EXPECT_TRUE(this->SetCookie(cs, url, "a=1")); - this->MatchCookieLines("a=1", this->GetCookies(cs, url)); - this->MatchCookieLines("", - this->GetCookies(cs, GURL("http://hopefully-no-cookies.com./"))); - } else { - EXPECT_FALSE(this->SetCookie(cs, url, "a=1")); - } - } - - { // Should not be able to set host cookie from a subdomain. - scoped_refptr<CookieStore> cs(this->GetCookieStore()); - GURL url("http://a.b"); - EXPECT_FALSE(this->SetCookie(cs, url, "a=1; domain=.b")); - EXPECT_FALSE(this->SetCookie(cs, url, "b=2; domain=b")); - this->MatchCookieLines("", this->GetCookies(cs, url)); - } - - { // Same test as above, but explicitly on a known TLD (com). - scoped_refptr<CookieStore> cs(this->GetCookieStore()); - GURL url("http://google.com"); - EXPECT_FALSE(this->SetCookie(cs, url, "a=1; domain=.com")); - EXPECT_FALSE(this->SetCookie(cs, url, "b=2; domain=com")); - this->MatchCookieLines("", this->GetCookies(cs, url)); - } - - { // Make sure can't set cookie on TLD which is dotted. - scoped_refptr<CookieStore> cs(this->GetCookieStore()); - GURL url("http://google.co.uk"); - EXPECT_FALSE(this->SetCookie(cs, url, "a=1; domain=.co.uk")); - EXPECT_FALSE(this->SetCookie(cs, url, "b=2; domain=.uk")); - this->MatchCookieLines("", this->GetCookies(cs, url)); - this->MatchCookieLines("", - this->GetCookies(cs, GURL("http://something-else.co.uk"))); - this->MatchCookieLines("", - this->GetCookies(cs, GURL("http://something-else.uk"))); - } - - { // Intranet URLs should only be able to set host cookies. - scoped_refptr<CookieStore> cs(this->GetCookieStore()); - GURL url("http://b"); - EXPECT_TRUE(this->SetCookie(cs, url, "a=1")); - EXPECT_FALSE(this->SetCookie(cs, url, "b=2; domain=.b")); - EXPECT_FALSE(this->SetCookie(cs, url, "c=3; domain=b")); - this->MatchCookieLines("a=1", this->GetCookies(cs, url)); - } -} - -// Test reading/writing cookies when the domain ends with a period, -// as in "www.google.com." -TYPED_TEST_P(CookieStoreTest, TestHostEndsWithDot) { - scoped_refptr<CookieStore> cs(this->GetCookieStore()); - GURL url("http://www.google.com"); - GURL url_with_dot("http://www.google.com."); - EXPECT_TRUE(this->SetCookie(cs, url, "a=1")); - this->MatchCookieLines("a=1", this->GetCookies(cs, url)); - - if (TypeParam::supports_trailing_dots) { - // Do not share cookie space with the dot version of domain. - // Note: this is not what FireFox does, but it _is_ what IE+Safari do. - EXPECT_FALSE(this->SetCookie(cs, url, "b=2; domain=.www.google.com.")); - this->MatchCookieLines("a=1", this->GetCookies(cs, url)); - - EXPECT_TRUE(this->SetCookie(cs, url_with_dot, "b=2; domain=.google.com.")); - this->MatchCookieLines("b=2", this->GetCookies(cs, url_with_dot)); - } else { - EXPECT_TRUE(this->SetCookie(cs, url, "b=2; domain=.www.google.com.")); - EXPECT_FALSE(this->SetCookie(cs, url_with_dot, "b=2; domain=.google.com.")); - } - - // Make sure there weren't any side effects. - this->MatchCookieLines("", - this->GetCookies(cs, GURL("http://hopefully-no-cookies.com/"))); - this->MatchCookieLines("", this->GetCookies(cs, GURL("http://.com/"))); -} - -TYPED_TEST_P(CookieStoreTest, InvalidScheme) { - if (!TypeParam::filters_schemes) - return; - - scoped_refptr<CookieStore> cs(this->GetCookieStore()); - EXPECT_FALSE(this->SetCookie(cs, GURL(kUrlFtp), kValidCookieLine)); -} - -TYPED_TEST_P(CookieStoreTest, InvalidScheme_Read) { - if (!TypeParam::filters_schemes) - return; - - scoped_refptr<CookieStore> cs(this->GetCookieStore()); - EXPECT_TRUE(this->SetCookie(cs, GURL(kUrlGoogle), kValidDomainCookieLine)); - this->MatchCookieLines("", this->GetCookies(cs, GURL(kUrlFtp))); -} - -TYPED_TEST_P(CookieStoreTest, PathTest) { - scoped_refptr<CookieStore> cs(this->GetCookieStore()); - std::string url("http://www.google.izzle"); - EXPECT_TRUE(this->SetCookie(cs, GURL(url), "A=B; path=/wee")); - this->MatchCookieLines("A=B", this->GetCookies(cs, GURL(url + "/wee"))); - this->MatchCookieLines("A=B", this->GetCookies(cs, GURL(url + "/wee/"))); - this->MatchCookieLines("A=B", this->GetCookies(cs, GURL(url + "/wee/war"))); - this->MatchCookieLines("A=B", - this->GetCookies(cs, GURL(url + "/wee/war/more/more"))); - if (!TypeParam::has_path_prefix_bug) - this->MatchCookieLines("", this->GetCookies(cs, GURL(url + "/weehee"))); - this->MatchCookieLines("", this->GetCookies(cs, GURL(url + "/"))); - - // If we add a 0 length path, it should default to / - EXPECT_TRUE(this->SetCookie(cs, GURL(url), "A=C; path=")); - this->MatchCookieLines("A=B; A=C", this->GetCookies(cs, GURL(url + "/wee"))); - this->MatchCookieLines("A=C", this->GetCookies(cs, GURL(url + "/"))); -} - -TYPED_TEST_P(CookieStoreTest, HttpOnlyTest) { - if (!TypeParam::supports_http_only) - return; - - scoped_refptr<CookieStore> cs(this->GetCookieStore()); - CookieOptions options; - options.set_include_httponly(); - - // Create a httponly cookie. - EXPECT_TRUE(this->SetCookieWithOptions(cs, this->url_google_, "A=B; httponly", - options)); - - // Check httponly read protection. - this->MatchCookieLines("", this->GetCookies(cs, this->url_google_)); - this->MatchCookieLines("A=B", - this->GetCookiesWithOptions(cs, this->url_google_, options)); - - // Check httponly overwrite protection. - EXPECT_FALSE(this->SetCookie(cs, this->url_google_, "A=C")); - this->MatchCookieLines("", this->GetCookies(cs, this->url_google_)); - this->MatchCookieLines("A=B", - this->GetCookiesWithOptions(cs, this->url_google_, options)); - EXPECT_TRUE(this->SetCookieWithOptions(cs, this->url_google_, "A=C", - options)); - this->MatchCookieLines("A=C", this->GetCookies(cs, this->url_google_)); - - // Check httponly create protection. - EXPECT_FALSE(this->SetCookie(cs, this->url_google_, "B=A; httponly")); - this->MatchCookieLines("A=C", - this->GetCookiesWithOptions(cs, this->url_google_, options)); - EXPECT_TRUE(this->SetCookieWithOptions(cs, this->url_google_, "B=A; httponly", - options)); - this->MatchCookieLines("A=C; B=A", - this->GetCookiesWithOptions(cs, this->url_google_, options)); - this->MatchCookieLines("A=C", this->GetCookies(cs, this->url_google_)); -} - -TYPED_TEST_P(CookieStoreTest, TestGetCookiesWithInfo) { - if (!TypeParam::supports_cookies_with_info) - return; - - scoped_refptr<CookieStore> cs(this->GetCookieStore()); - CookieOptions options; - - EXPECT_TRUE(this->SetCookieWithOptions(cs, this->url_google_, "A=B", - options)); - EXPECT_TRUE(this->SetCookieWithOptions(cs, this->url_google_, - "C=D; Mac-Key=390jfn0awf3; Mac-Algorithm=hmac-sha-1", options)); - - this->MatchCookieLines("A=B; C=D", - this->GetCookiesWithOptions(cs, this->url_google_, options)); - - std::string cookie_line; - std::vector<CookieStore::CookieInfo> cookie_infos; - - this->GetCookiesWithInfo(cs, this->url_google_, options, &cookie_line, - &cookie_infos); - - EXPECT_EQ("A=B; C=D", cookie_line); - - EXPECT_EQ(2U, cookie_infos.size()); - - EXPECT_EQ("A", cookie_infos[0].name); - EXPECT_EQ("", cookie_infos[0].mac_key); - EXPECT_EQ("", cookie_infos[0].mac_algorithm); - - EXPECT_EQ("C", cookie_infos[1].name); - EXPECT_EQ("390jfn0awf3", cookie_infos[1].mac_key); - EXPECT_EQ("hmac-sha-1", cookie_infos[1].mac_algorithm); -} - -TYPED_TEST_P(CookieStoreTest, TestCookieDeletion) { - scoped_refptr<CookieStore> cs(this->GetCookieStore()); - - // Create a session cookie. - EXPECT_TRUE(this->SetCookie(cs, this->url_google_, kValidCookieLine)); - this->MatchCookieLines("A=B", this->GetCookies(cs, this->url_google_)); - // Delete it via Max-Age. - EXPECT_TRUE(this->SetCookie(cs, this->url_google_, - std::string(kValidCookieLine) + "; max-age=0")); - this->MatchCookieLineWithTimeout(cs, this->url_google_, ""); - - // Create a session cookie. - EXPECT_TRUE(this->SetCookie(cs, this->url_google_, kValidCookieLine)); - this->MatchCookieLines("A=B", this->GetCookies(cs, this->url_google_)); - // Delete it via Expires. - EXPECT_TRUE(this->SetCookie(cs, this->url_google_, - std::string(kValidCookieLine) + - "; expires=Mon, 18-Apr-1977 22:50:13 GMT")); - this->MatchCookieLines("", this->GetCookies(cs, this->url_google_)); - - // Create a persistent cookie. - EXPECT_TRUE(this->SetCookie(cs, this->url_google_, - std::string(kValidCookieLine) + - "; expires=Mon, 18-Apr-22 22:50:13 GMT")); - - this->MatchCookieLines("A=B", this->GetCookies(cs, this->url_google_)); - // Delete it via Max-Age. - EXPECT_TRUE(this->SetCookie(cs, this->url_google_, - std::string(kValidCookieLine) + "; max-age=0")); - this->MatchCookieLineWithTimeout(cs, this->url_google_, ""); - - // Create a persistent cookie. - EXPECT_TRUE(this->SetCookie(cs, this->url_google_, - std::string(kValidCookieLine) + - "; expires=Mon, 18-Apr-22 22:50:13 GMT")); - this->MatchCookieLines("A=B", this->GetCookies(cs, this->url_google_)); - // Delete it via Expires. - EXPECT_TRUE(this->SetCookie(cs, this->url_google_, - std::string(kValidCookieLine) + - "; expires=Mon, 18-Apr-1977 22:50:13 GMT")); - this->MatchCookieLines("", this->GetCookies(cs, this->url_google_)); - - // Create a persistent cookie. - EXPECT_TRUE(this->SetCookie(cs, this->url_google_, - std::string(kValidCookieLine) + - "; expires=Mon, 18-Apr-22 22:50:13 GMT")); - this->MatchCookieLines("A=B", this->GetCookies(cs, this->url_google_)); - // Delete it via Expires, with a unix epoch of 0. - EXPECT_TRUE(this->SetCookie(cs, this->url_google_, - std::string(kValidCookieLine) + - "; expires=Thu, 1-Jan-1970 00:00:00 GMT")); - this->MatchCookieLines("", this->GetCookies(cs, this->url_google_)); -} - -TYPED_TEST_P(CookieStoreTest, TestDeleteAllCreatedBetween) { - scoped_refptr<CookieStore> cs(this->GetCookieStore()); - const base::Time last_month = base::Time::Now() - - base::TimeDelta::FromDays(30); - const base::Time last_minute = base::Time::Now() - - base::TimeDelta::FromMinutes(1); - const base::Time next_minute = base::Time::Now() + - base::TimeDelta::FromMinutes(1); - const base::Time next_month = base::Time::Now() + - base::TimeDelta::FromDays(30); - - // Add a cookie. - EXPECT_TRUE(this->SetCookie(cs, this->url_google_, "A=B")); - // Check that the cookie is in the store. - this->MatchCookieLines("A=B", this->GetCookies(cs, this->url_google_)); - - // Remove cookies in empty intervals. - EXPECT_EQ(0, this->DeleteCreatedBetween(cs, last_month, last_minute)); - EXPECT_EQ(0, this->DeleteCreatedBetween(cs, next_minute, next_month)); - // Check that the cookie is still there. - this->MatchCookieLines("A=B", this->GetCookies(cs, this->url_google_)); - - // Remove the cookie with an interval defined by two dates. - EXPECT_EQ(1, this->DeleteCreatedBetween(cs, last_minute, next_minute)); - // Check that the cookie disappeared. - this->MatchCookieLines("", this->GetCookies(cs, this->url_google_)); - - // Add another cookie. - EXPECT_TRUE(this->SetCookie(cs, this->url_google_, "C=D")); - // Check that the cookie is in the store. - this->MatchCookieLines("C=D", this->GetCookies(cs, this->url_google_)); - - // Remove the cookie with a null ending time. - EXPECT_EQ(1, this->DeleteCreatedBetween(cs, last_minute, base::Time())); - // Check that the cookie disappeared. - this->MatchCookieLines("", this->GetCookies(cs, this->url_google_)); -} - -TYPED_TEST_P(CookieStoreTest, TestSecure) { - scoped_refptr<CookieStore> cs(this->GetCookieStore()); - - EXPECT_TRUE(this->SetCookie(cs, this->url_google_, "A=B")); - this->MatchCookieLines("A=B", this->GetCookies(cs, this->url_google_)); - this->MatchCookieLines("A=B", this->GetCookies(cs, this->url_google_secure_)); - - EXPECT_TRUE(this->SetCookie(cs, this->url_google_secure_, "A=B; secure")); - // The secure should overwrite the non-secure. - this->MatchCookieLines("", this->GetCookies(cs, this->url_google_)); - this->MatchCookieLines("A=B", this->GetCookies(cs, this->url_google_secure_)); - - EXPECT_TRUE(this->SetCookie(cs, this->url_google_secure_, "D=E; secure")); - this->MatchCookieLines("", this->GetCookies(cs, this->url_google_)); - this->MatchCookieLines("A=B; D=E", - this->GetCookies(cs, this->url_google_secure_)); - - EXPECT_TRUE(this->SetCookie(cs, this->url_google_secure_, "A=B")); - // The non-secure should overwrite the secure. - this->MatchCookieLines("A=B", this->GetCookies(cs, this->url_google_)); - this->MatchCookieLines("D=E; A=B", - this->GetCookies(cs, this->url_google_secure_)); -} - -static const int kLastAccessThresholdMilliseconds = 200; - -// Formerly NetUtilTest.CookieTest back when we used wininet's cookie handling. -TYPED_TEST_P(CookieStoreTest, NetUtilCookieTest) { - const GURL test_url("http://mojo.jojo.google.izzle/"); - - scoped_refptr<CookieStore> cs(this->GetCookieStore()); - - EXPECT_TRUE(this->SetCookie(cs, test_url, "foo=bar")); - std::string value = this->GetCookies(cs, test_url); - this->MatchCookieLines("foo=bar", value); - - // test that we can retrieve all cookies: - EXPECT_TRUE(this->SetCookie(cs, test_url, "x=1")); - EXPECT_TRUE(this->SetCookie(cs, test_url, "y=2")); - - std::string result = this->GetCookies(cs, test_url); - EXPECT_FALSE(result.empty()); - EXPECT_NE(result.find("x=1"), std::string::npos) << result; - EXPECT_NE(result.find("y=2"), std::string::npos) << result; -} - -TYPED_TEST_P(CookieStoreTest, OverwritePersistentCookie) { - GURL url_google("http://www.google.com/"); - GURL url_chromium("http://chromium.org"); - scoped_refptr<CookieStore> cs(this->GetCookieStore()); - - // Insert a cookie "a" for path "/path1" - EXPECT_TRUE( - this->SetCookie(cs, url_google, "a=val1; path=/path1; " - "expires=Mon, 18-Apr-22 22:50:13 GMT")); - - // Insert a cookie "b" for path "/path1" - EXPECT_TRUE( - this->SetCookie(cs, url_google, "b=val1; path=/path1; " - "expires=Mon, 18-Apr-22 22:50:14 GMT")); - - // Insert a cookie "b" for path "/path1", that is httponly. This should - // overwrite the non-http-only version. - CookieOptions allow_httponly; - allow_httponly.set_include_httponly(); - EXPECT_TRUE( - this->SetCookieWithOptions(cs, url_google, - "b=val2; path=/path1; httponly; " - "expires=Mon, 18-Apr-22 22:50:14 GMT", - allow_httponly)); - - // Insert a cookie "a" for path "/path1". This should overwrite. - EXPECT_TRUE(this->SetCookie(cs, url_google, - "a=val33; path=/path1; " - "expires=Mon, 18-Apr-22 22:50:14 GMT")); - - // Insert a cookie "a" for path "/path2". This should NOT overwrite - // cookie "a", since the path is different. - EXPECT_TRUE(this->SetCookie(cs, url_google, - "a=val9; path=/path2; " - "expires=Mon, 18-Apr-22 22:50:14 GMT")); - - // Insert a cookie "a" for path "/path1", but this time for "chromium.org". - // Although the name and path match, the hostnames do not, so shouldn't - // overwrite. - EXPECT_TRUE(this->SetCookie(cs, url_chromium, - "a=val99; path=/path1; " - "expires=Mon, 18-Apr-22 22:50:14 GMT")); - - if (TypeParam::supports_http_only) { - this->MatchCookieLines("a=val33", - this->GetCookies(cs, GURL("http://www.google.com/path1"))); - } else { - this->MatchCookieLines("a=val33; b=val2", - this->GetCookies(cs, GURL("http://www.google.com/path1"))); - } - this->MatchCookieLines("a=val9", - this->GetCookies(cs, GURL("http://www.google.com/path2"))); - this->MatchCookieLines("a=val99", - this->GetCookies(cs, GURL("http://chromium.org/path1"))); -} - -TYPED_TEST_P(CookieStoreTest, CookieOrdering) { - // Put a random set of cookies into a store and make sure they're returned in - // the right order. - // Cookies should be sorted by path length and creation time, as per RFC6265. - scoped_refptr<CookieStore> cs(this->GetCookieStore()); - EXPECT_TRUE(this->SetCookie(cs, GURL("http://d.c.b.a.google.com/aa/x.html"), - "c=1")); - EXPECT_TRUE(this->SetCookie(cs, GURL("http://b.a.google.com/aa/bb/cc/x.html"), - "d=1; domain=b.a.google.com")); - base::PlatformThread::Sleep(base::TimeDelta::FromMilliseconds( - TypeParam::creation_time_granularity_in_ms)); - EXPECT_TRUE(this->SetCookie(cs, GURL("http://b.a.google.com/aa/bb/cc/x.html"), - "a=4; domain=b.a.google.com")); - base::PlatformThread::Sleep(base::TimeDelta::FromMilliseconds( - TypeParam::creation_time_granularity_in_ms)); - EXPECT_TRUE(this->SetCookie(cs, - GURL("http://c.b.a.google.com/aa/bb/cc/x.html"), - "e=1; domain=c.b.a.google.com")); - EXPECT_TRUE(this->SetCookie(cs, - GURL("http://d.c.b.a.google.com/aa/bb/x.html"), - "b=1")); - EXPECT_TRUE(this->SetCookie(cs, GURL("http://news.bbc.co.uk/midpath/x.html"), - "g=10")); - EXPECT_EQ("d=1; a=4; e=1; b=1; c=1", - this->GetCookies(cs, GURL("http://d.c.b.a.google.com/aa/bb/cc/dd"))); -} - -REGISTER_TYPED_TEST_CASE_P(CookieStoreTest, - TypeTest, DomainTest, DomainWithTrailingDotTest, ValidSubdomainTest, - InvalidDomainTest, DomainWithoutLeadingDotTest, CaseInsensitiveDomainTest, - TestIpAddress, TestNonDottedAndTLD, TestHostEndsWithDot, InvalidScheme, - InvalidScheme_Read, PathTest, HttpOnlyTest, TestGetCookiesWithInfo, - TestCookieDeletion, TestDeleteAllCreatedBetween, TestSecure, - NetUtilCookieTest, OverwritePersistentCookie, CookieOrdering); - -template<class CookieStoreTestTraits> -class MultiThreadedCookieStoreTest : - public CookieStoreTest<CookieStoreTestTraits> { - public: - MultiThreadedCookieStoreTest() : other_thread_("CMTthread") {} - - // Helper methods for calling the asynchronous CookieStore methods - // from a different thread. - - void GetCookiesTask(CookieStore* cs, - const GURL& url, - GetCookieStringCallback* callback) { - CookieOptions options; - if (!CookieStoreTestTraits::supports_http_only) - options.set_include_httponly(); - cs->GetCookiesWithOptionsAsync( - url, options, - base::Bind(&GetCookieStringCallback::Run, base::Unretained(callback))); - } - - void GetCookiesWithOptionsTask(CookieStore* cs, - const GURL& url, - const CookieOptions& options, - GetCookieStringCallback* callback) { - cs->GetCookiesWithOptionsAsync( - url, options, - base::Bind(&GetCookieStringCallback::Run, base::Unretained(callback))); - } - - void GetCookiesWithInfoTask(CookieStore* cs, - const GURL& url, - const CookieOptions& options, - GetCookiesWithInfoCallback* callback) { - cs->GetCookiesWithInfoAsync( - url, options, - base::Bind(&GetCookiesWithInfoCallback::Run, - base::Unretained(callback))); - } - - void SetCookieWithOptionsTask(CookieStore* cs, - const GURL& url, - const std::string& cookie_line, - const CookieOptions& options, - SetCookieCallback* callback) { - cs->SetCookieWithOptionsAsync( - url, cookie_line, options, - base::Bind(&SetCookieCallback::Run, base::Unretained(callback))); - } - - void DeleteCookieTask(CookieStore* cs, - const GURL& url, - const std::string& cookie_name, - DeleteCookieCallback* callback) { - cs->DeleteCookieAsync( - url, cookie_name, - base::Bind(&DeleteCookieCallback::Run, base::Unretained(callback))); - } - - protected: - void RunOnOtherThread(const base::Closure& task) { - other_thread_.Start(); - other_thread_.message_loop()->PostTask(FROM_HERE, task); - CookieStoreTest<CookieStoreTestTraits>::RunFor(kTimeout); - other_thread_.Stop(); - } - - Thread other_thread_; -}; - -TYPED_TEST_CASE_P(MultiThreadedCookieStoreTest); - -// TODO(ycxiao): Eventually, we will need to create a separate thread, create -// the cookie store on that thread (or at least its store, i.e., the DB -// thread). -TYPED_TEST_P(MultiThreadedCookieStoreTest, ThreadCheckGetCookies) { - scoped_refptr<CookieStore> cs(this->GetCookieStore()); - EXPECT_TRUE(this->SetCookie(cs, this->url_google_, "A=B")); - this->MatchCookieLines("A=B", this->GetCookies(cs, this->url_google_)); - GetCookieStringCallback callback(&this->other_thread_); - base::Closure task = base::Bind( - &net::MultiThreadedCookieStoreTest<TypeParam>::GetCookiesTask, - base::Unretained(this), - cs, this->url_google_, &callback); - this->RunOnOtherThread(task); - EXPECT_TRUE(callback.did_run()); - EXPECT_EQ("A=B", callback.cookie()); -} - -TYPED_TEST_P(MultiThreadedCookieStoreTest, ThreadCheckGetCookiesWithOptions) { - scoped_refptr<CookieStore> cs(this->GetCookieStore()); - CookieOptions options; - if (!TypeParam::supports_http_only) - options.set_include_httponly(); - EXPECT_TRUE(this->SetCookie(cs, this->url_google_, "A=B")); - this->MatchCookieLines("A=B", - this->GetCookiesWithOptions(cs, this->url_google_, options)); - GetCookieStringCallback callback(&this->other_thread_); - base::Closure task = base::Bind( - &net::MultiThreadedCookieStoreTest<TypeParam>::GetCookiesWithOptionsTask, - base::Unretained(this), - cs, this->url_google_, options, &callback); - this->RunOnOtherThread(task); - EXPECT_TRUE(callback.did_run()); - EXPECT_EQ("A=B", callback.cookie()); -} - -TYPED_TEST_P(MultiThreadedCookieStoreTest, ThreadCheckGetCookiesWithInfo) { - if (!TypeParam::supports_cookies_with_info) - return; - scoped_refptr<CookieStore> cs(this->GetCookieStore()); - CookieOptions options; - std::string cookie_line; - std::vector<CookieStore::CookieInfo> cookie_infos; - EXPECT_TRUE(this->SetCookie(cs, this->url_google_, "A=B")); - this->GetCookiesWithInfo(cs, this->url_google_, options, &cookie_line, - &cookie_infos); - this->MatchCookieLines("A=B", cookie_line); - EXPECT_EQ(1U, cookie_infos.size()); - EXPECT_EQ("A", cookie_infos[0].name); - EXPECT_EQ("", cookie_infos[0].mac_key); - EXPECT_EQ("", cookie_infos[0].mac_algorithm); - GetCookiesWithInfoCallback callback(&this->other_thread_); - base::Closure task = base::Bind( - &net::MultiThreadedCookieStoreTest<TypeParam>::GetCookiesWithInfoTask, - base::Unretained(this), cs, this->url_google_, options, &callback); - this->RunOnOtherThread(task); - EXPECT_TRUE(callback.did_run()); - this->MatchCookieLines("A=B", callback.cookie_line()); - EXPECT_EQ(1U, callback.cookie_info().size()); - EXPECT_EQ("A", callback.cookie_info()[0].name); - EXPECT_EQ("", callback.cookie_info()[0].mac_key); - EXPECT_EQ("", callback.cookie_info()[0].mac_algorithm); -} - -TYPED_TEST_P(MultiThreadedCookieStoreTest, ThreadCheckSetCookieWithOptions) { - scoped_refptr<CookieStore> cs(this->GetCookieStore()); - CookieOptions options; - if (!TypeParam::supports_http_only) - options.set_include_httponly(); - EXPECT_TRUE(this->SetCookieWithOptions(cs, this->url_google_, "A=B", - options)); - SetCookieCallback callback(&this->other_thread_); - base::Closure task = base::Bind( - &net::MultiThreadedCookieStoreTest<TypeParam>::SetCookieWithOptionsTask, - base::Unretained(this), - cs, this->url_google_, "A=B", options, &callback); - this->RunOnOtherThread(task); - EXPECT_TRUE(callback.did_run()); - EXPECT_TRUE(callback.result()); -} - -TYPED_TEST_P(MultiThreadedCookieStoreTest, ThreadCheckDeleteCookie) { - scoped_refptr<CookieStore> cs(this->GetCookieStore()); - CookieOptions options; - if (!TypeParam::supports_http_only) - options.set_include_httponly(); - EXPECT_TRUE(this->SetCookieWithOptions(cs, this->url_google_, "A=B", - options)); - this->DeleteCookie(cs, this->url_google_, "A"); - EXPECT_TRUE(this->SetCookieWithOptions(cs, this->url_google_, "A=B", - options)); - DeleteCookieCallback callback(&this->other_thread_); - base::Closure task = base::Bind( - &net::MultiThreadedCookieStoreTest<TypeParam>::DeleteCookieTask, - base::Unretained(this), - cs, this->url_google_, "A", &callback); - this->RunOnOtherThread(task); - EXPECT_TRUE(callback.did_run()); -} - -REGISTER_TYPED_TEST_CASE_P(MultiThreadedCookieStoreTest, - ThreadCheckGetCookies, ThreadCheckGetCookiesWithOptions, - ThreadCheckGetCookiesWithInfo, ThreadCheckSetCookieWithOptions, - ThreadCheckDeleteCookie); - -} // namespace net +#include "net/cookies/cookie_store_unittest.h" #endif // NET_BASE_COOKIE_STORE_UNITTEST_H_ diff --git a/net/base/cookie_util.h b/net/base/cookie_util.h index bda52f0..61f72d8 100644 --- a/net/base/cookie_util.h +++ b/net/base/cookie_util.h @@ -1,40 +1,14 @@ -// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// 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. +// Provides a temporary redirection while clients are updated to use the new +// path. + #ifndef NET_BASE_COOKIE_UTIL_H_ #define NET_BASE_COOKIE_UTIL_H_ #pragma once -#include <string> - -#include "net/base/net_export.h" - -class GURL; - -namespace net { -namespace cookie_util { - -// Returns the effective TLD+1 for a given host. This only makes sense for http -// and https schemes. For other schemes, the host will be returned unchanged -// (minus any leading period). -NET_EXPORT std::string GetEffectiveDomain(const std::string& scheme, - const std::string& host); - -// Determine the actual cookie domain based on the domain string passed -// (if any) and the URL from which the cookie came. -// On success returns true, and sets cookie_domain to either a -// -host cookie domain (ex: "google.com") -// -domain cookie domain (ex: ".google.com") -NET_EXPORT bool GetCookieDomainWithString(const GURL& url, - const std::string& domain_string, - std::string* result); - -// Returns true if a domain string represents a host-only cookie, -// i.e. it doesn't begin with a leading '.' character. -NET_EXPORT bool DomainIsHostOnly(const std::string& domain_string); - -} // namspace cookie_util -} // namespace net +#include "net/cookies/cookie_util.h" #endif // NET_BASE_COOKIE_UTIL_H_ diff --git a/net/cookies/OWNERS b/net/cookies/OWNERS new file mode 100644 index 0000000..82e44d8 --- /dev/null +++ b/net/cookies/OWNERS @@ -0,0 +1 @@ +erikwright@chromium.org diff --git a/net/base/cookie_monster.cc b/net/cookies/cookie_monster.cc index 8904c0b..8a609d8 100644 --- a/net/base/cookie_monster.cc +++ b/net/cookies/cookie_monster.cc @@ -42,7 +42,7 @@ * * ***** END LICENSE BLOCK ***** */ -#include "net/base/cookie_monster.h" +#include "net/cookies/cookie_monster.h" #include <algorithm> #include <set> @@ -61,7 +61,7 @@ #include "base/stringprintf.h" #include "googleurl/src/gurl.h" #include "googleurl/src/url_canon.h" -#include "net/base/cookie_util.h" +#include "net/cookies/cookie_util.h" #include "net/base/registry_controlled_domain.h" using base::Time; diff --git a/net/cookies/cookie_monster.h b/net/cookies/cookie_monster.h new file mode 100644 index 0000000..b23dcfb --- /dev/null +++ b/net/cookies/cookie_monster.h @@ -0,0 +1,971 @@ +// 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. + +// Brought to you by the letter D and the number 2. + +#ifndef NET_COOKIES_COOKIE_MONSTER_H_ +#define NET_COOKIES_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_forward.h" +#include "base/gtest_prod_util.h" +#include "base/memory/ref_counted.h" +#include "base/memory/scoped_ptr.h" +#include "base/synchronization/lock.h" +#include "base/time.h" +#include "net/cookies/cookie_store.h" +#include "net/base/net_export.h" + +class GURL; + +namespace base { +class Histogram; +class TimeTicks; +} // namespace base + +namespace net { + +class CookieList; + +// The cookie monster is the system for storing and retrieving cookies. It has +// an in-memory list of all cookies, and synchronizes non-session cookies to an +// optional permanent storage that implements the PersistentCookieStore +// interface. +// +// 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. +// +// 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. +// - Verify that our domain enforcement and non-dotted handling is correct +class NET_EXPORT CookieMonster : public CookieStore { + public: + class CanonicalCookie; + class Delegate; + class ParsedCookie; + class PersistentCookieStore; + + // Terminology: + // * The 'top level domain' (TLD) of an internet domain name is + // the terminal "." free substring (e.g. "com" for google.com + // or world.std.com). + // * The 'effective top level domain' (eTLD) is the longest + // "." initiated terminal substring of an internet domain name + // that is controlled by a general domain registrar. + // (e.g. "co.uk" for news.bbc.co.uk). + // * The 'effective top level domain plus one' (eTLD+1) is the + // shortest "." delimited terminal substring of an internet + // domain name that is not controlled by a general domain + // registrar (e.g. "bbc.co.uk" for news.bbc.co.uk, or + // "google.com" for news.google.com). The general assumption + // is that all hosts and domains under an eTLD+1 share some + // administrative control. + + // CookieMap is the central data structure of the CookieMonster. It + // is a map whose values are pointers to CanonicalCookie data + // structures (the data structures are owned by the CookieMonster + // and must be destroyed when removed from the map). The key is based on the + // effective domain of the cookies. If the domain of the cookie has an + // eTLD+1, that is the key for the map. If the domain of the cookie does not + // have an eTLD+1, the key of the map is the host the cookie applies to (it is + // not legal to have domain cookies without an eTLD+1). This rule + // excludes cookies for, e.g, ".com", ".co.uk", or ".internalnetwork". + // This behavior is the same as the behavior in Firefox v 3.6.10. + + // NOTE(deanm): + // I benchmarked hash_multimap vs multimap. We're going to be query-heavy + // so it would seem like hashing would help. However they were very + // close, with multimap being a tiny bit faster. I think this is because + // our map is at max around 1000 entries, and the additional complexity + // for the hashing might not overcome the O(log(1000)) for querying + // a multimap. Also, multimap is standard, another reason to use it. + // TODO(rdsmith): This benchmark should be re-done now that we're allowing + // subtantially more entries in the map. + typedef std::multimap<std::string, CanonicalCookie*> CookieMap; + typedef std::pair<CookieMap::iterator, CookieMap::iterator> CookieMapItPair; + + // The store passed in should not have had Init() called on it yet. This + // class will take care of initializing it. The backing store is NOT owned by + // this class, but it must remain valid for the duration of the cookie + // monster's existence. If |store| is NULL, then no backing store will be + // updated. If |delegate| is non-NULL, it will be notified on + // creation/deletion of cookies. + CookieMonster(PersistentCookieStore* store, Delegate* delegate); + + // Only used during unit testing. + CookieMonster(PersistentCookieStore* store, + Delegate* delegate, + int last_access_threshold_milliseconds); + + // Parses the string with the cookie time (very forgivingly). + static base::Time ParseCookieTime(const std::string& time_string); + + // Helper function that adds all cookies from |list| into this instance. + bool InitializeFrom(const CookieList& list); + + typedef base::Callback<void(const CookieList& cookies)> GetCookieListCallback; + typedef base::Callback<void(bool success)> DeleteCookieCallback; + + // Sets a cookie given explicit user-provided cookie attributes. The cookie + // name, value, domain, etc. are each provided as separate strings. This + // function expects each attribute to be well-formed. It will check for + // disallowed characters (e.g. the ';' character is disallowed within the + // cookie value attribute) and will return false without setting the cookie + // if such characters are found. + void SetCookieWithDetailsAsync(const GURL& url, + const std::string& name, + const std::string& value, + const std::string& domain, + const std::string& path, + const base::Time& expiration_time, + bool secure, bool http_only, + const SetCookiesCallback& callback); + + + // Returns all the cookies, for use in management UI, etc. This does not mark + // the cookies as having been accessed. + // The returned cookies are ordered by longest path, then by earliest + // creation date. + void GetAllCookiesAsync(const GetCookieListCallback& callback); + + // Returns all the cookies, for use in management UI, etc. Filters results + // using given url scheme, host / domain and path and options. This does not + // mark the cookies as having been accessed. + // The returned cookies are ordered by longest path, then earliest + // creation date. + void GetAllCookiesForURLWithOptionsAsync( + const GURL& url, + const CookieOptions& options, + const GetCookieListCallback& callback); + + // Invokes GetAllCookiesForURLWithOptions with options set to include HTTP + // only cookies. + void GetAllCookiesForURLAsync(const GURL& url, + const GetCookieListCallback& callback); + + // Deletes all of the cookies. + void DeleteAllAsync(const DeleteCallback& callback); + + // Deletes all cookies that match the host of the given URL + // regardless of path. This includes all http_only and secure cookies, + // but does not include any domain cookies that may apply to this host. + // Returns the number of cookies deleted. + void DeleteAllForHostAsync(const GURL& url, + const DeleteCallback& callback); + + // Deletes one specific cookie. + void DeleteCanonicalCookieAsync(const CanonicalCookie& cookie, + const DeleteCookieCallback& callback); + + // Override the default list of schemes that are allowed to be set in + // this cookie store. Calling his overrides the value of + // "enable_file_scheme_". + // If this this method is called, it must be called before first use of + // the instance (i.e. as part of the instance initialization process). + void SetCookieableSchemes(const char* schemes[], size_t num_schemes); + + // Instructs the cookie monster to not delete expired cookies. This is used + // in cases where the cookie monster is used as a data structure to keep + // arbitrary cookies. + void SetKeepExpiredCookies(); + + // Delegates the call to set the |clear_local_store_on_exit_| flag of the + // PersistentStore if it exists. + void SetClearPersistentStoreOnExit(bool clear_local_store); + + // There are some unknowns about how to correctly handle file:// cookies, + // and our implementation for this is not robust enough. This allows you + // to enable support, but it should only be used for testing. Bug 1157243. + // Must be called before creating a CookieMonster instance. + static void EnableFileScheme(); + + // Flush the backing store (if any) to disk and post the given callback when + // done. + // WARNING: THE CALLBACK WILL RUN ON A RANDOM THREAD. IT MUST BE THREAD SAFE. + // It may be posted to the current thread, or it may run on the thread that + // actually does the flushing. Your Task should generally post a notification + // to the thread you actually want to be notified on. + void FlushStore(const base::Closure& callback); + + // CookieStore implementation. + + // Sets the cookies specified by |cookie_list| returned from |url| + // with options |options| in effect. + virtual void SetCookieWithOptionsAsync( + const GURL& url, + const std::string& cookie_line, + const CookieOptions& options, + const SetCookiesCallback& callback) OVERRIDE; + + // Gets all cookies that apply to |url| given |options|. + // The returned cookies are ordered by longest path, then earliest + // creation date. + virtual void GetCookiesWithOptionsAsync( + const GURL& url, + const CookieOptions& options, + const GetCookiesCallback& callback) OVERRIDE; + + virtual void GetCookiesWithInfoAsync( + const GURL& url, + const CookieOptions& options, + const GetCookieInfoCallback& callback) OVERRIDE; + + // Deletes all cookies with that might apply to |url| that has |cookie_name|. + virtual void DeleteCookieAsync( + const GURL& url, const std::string& cookie_name, + const base::Closure& callback) OVERRIDE; + + // Deletes all of the cookies that have a creation_date greater than or equal + // to |delete_begin| and less than |delete_end| + // Returns the number of cookies that have been deleted. + virtual void DeleteAllCreatedBetweenAsync( + const base::Time& delete_begin, + const base::Time& delete_end, + const DeleteCallback& callback) OVERRIDE; + + virtual CookieMonster* GetCookieMonster() OVERRIDE; + + // Enables writing session cookies into the cookie database. If this this + // method is called, it must be called before first use of the instance + // (i.e. as part of the instance initialization process). + void SetPersistSessionCookies(bool persist_session_cookies); + + // Protects session cookies from deletion on shutdown. + void SaveSessionCookies(); + + // Debugging method to perform various validation checks on the map. + // Currently just checking that there are no null CanonicalCookie pointers + // in the map. + // Argument |arg| is to allow retaining of arbitrary data if the CHECKs + // in the function trip. TODO(rdsmith):Remove hack. + void ValidateMap(int arg); + + // The default list of schemes the cookie monster can handle. + static const char* kDefaultCookieableSchemes[]; + static const int kDefaultCookieableSchemesCount; + + private: + // For queueing the cookie monster calls. + class CookieMonsterTask; + class DeleteAllCreatedBetweenTask; + class DeleteAllForHostTask; + class DeleteAllTask; + class DeleteCookieTask; + class DeleteCanonicalCookieTask; + class GetAllCookiesForURLWithOptionsTask; + class GetAllCookiesTask; + class GetCookiesWithOptionsTask; + class GetCookiesWithInfoTask; + class SetCookieWithDetailsTask; + class SetCookieWithOptionsTask; + + // Testing support. + // For SetCookieWithCreationTime. + FRIEND_TEST_ALL_PREFIXES(CookieMonsterTest, + TestCookieDeleteAllCreatedBetweenTimestamps); + + // For gargage collection constants. + FRIEND_TEST_ALL_PREFIXES(CookieMonsterTest, TestHostGarbageCollection); + FRIEND_TEST_ALL_PREFIXES(CookieMonsterTest, TestTotalGarbageCollection); + FRIEND_TEST_ALL_PREFIXES(CookieMonsterTest, GarbageCollectionTriggers); + FRIEND_TEST_ALL_PREFIXES(CookieMonsterTest, TestGCTimes); + + // For validation of key values. + FRIEND_TEST_ALL_PREFIXES(CookieMonsterTest, TestDomainTree); + FRIEND_TEST_ALL_PREFIXES(CookieMonsterTest, TestImport); + FRIEND_TEST_ALL_PREFIXES(CookieMonsterTest, GetKey); + FRIEND_TEST_ALL_PREFIXES(CookieMonsterTest, TestGetKey); + + // For FindCookiesForKey. + FRIEND_TEST_ALL_PREFIXES(CookieMonsterTest, ShortLivedSessionCookies); + + // Internal reasons for deletion, used to populate informative histograms + // and to provide a public cause for onCookieChange notifications. + // + // If you add or remove causes from this list, please be sure to also update + // the Delegate::ChangeCause mapping inside ChangeCauseMapping. Moreover, + // these are used as array indexes, so avoid reordering to keep the + // histogram buckets consistent. New items (if necessary) should be added + // at the end of the list, just before DELETE_COOKIE_LAST_ENTRY. + enum DeletionCause { + DELETE_COOKIE_EXPLICIT = 0, + DELETE_COOKIE_OVERWRITE, + DELETE_COOKIE_EXPIRED, + DELETE_COOKIE_EVICTED, + DELETE_COOKIE_DUPLICATE_IN_BACKING_STORE, + DELETE_COOKIE_DONT_RECORD, // e.g. For final cleanup after flush to store. + DELETE_COOKIE_EVICTED_DOMAIN, + DELETE_COOKIE_EVICTED_GLOBAL, + + // Cookies evicted during domain level garbage collection that + // were accessed longer ago than kSafeFromGlobalPurgeDays + DELETE_COOKIE_EVICTED_DOMAIN_PRE_SAFE, + + // Cookies evicted during domain level garbage collection that + // were accessed more recently than kSafeFromGlobalPurgeDays + // (and thus would have been preserved by global garbage collection). + DELETE_COOKIE_EVICTED_DOMAIN_POST_SAFE, + + // A common idiom is to remove a cookie by overwriting it with an + // already-expired expiration date. This captures that case. + DELETE_COOKIE_EXPIRED_OVERWRITE, + + DELETE_COOKIE_LAST_ENTRY + }; + + // Cookie garbage collection thresholds. Based off of the Mozilla defaults. + // When the number of cookies gets to k{Domain,}MaxCookies + // purge down to k{Domain,}MaxCookies - k{Domain,}PurgeCookies. + // It might seem scary to have a high purge value, but really it's not. + // You just make sure that you increase the max to cover the increase + // in purge, and we would have been purging the same amount of cookies. + // We're just going through the garbage collection process less often. + // Note that the DOMAIN values are per eTLD+1; see comment for the + // CookieMap typedef. So, e.g., the maximum number of cookies allowed for + // google.com and all of its subdomains will be 150-180. + // + // Any cookies accessed more recently than kSafeFromGlobalPurgeDays will not + // be evicted by global garbage collection, even if we have more than + // kMaxCookies. This does not affect domain garbage collection. + // + // Present in .h file to make accessible to tests through FRIEND_TEST. + // Actual definitions are in cookie_monster.cc. + static const size_t kDomainMaxCookies; + static const size_t kDomainPurgeCookies; + static const size_t kMaxCookies; + static const size_t kPurgeCookies; + + // The number of days since last access that cookies will not be subject + // to global garbage collection. + static const int kSafeFromGlobalPurgeDays; + + // Record statistics every kRecordStatisticsIntervalSeconds of uptime. + static const int kRecordStatisticsIntervalSeconds = 10 * 60; + + virtual ~CookieMonster(); + + // The following are synchronous calls to which the asynchronous methods + // delegate either immediately (if the store is loaded) or through a deferred + // task (if the store is not yet loaded). + bool SetCookieWithDetails(const GURL& url, + const std::string& name, + const std::string& value, + const std::string& domain, + const std::string& path, + const base::Time& expiration_time, + bool secure, bool http_only); + + CookieList GetAllCookies(); + + CookieList GetAllCookiesForURLWithOptions(const GURL& url, + const CookieOptions& options); + + CookieList GetAllCookiesForURL(const GURL& url); + + int DeleteAll(bool sync_to_store); + + int DeleteAllCreatedBetween(const base::Time& delete_begin, + const base::Time& delete_end); + + int DeleteAllForHost(const GURL& url); + + bool DeleteCanonicalCookie(const CanonicalCookie& cookie); + + bool SetCookieWithOptions(const GURL& url, + const std::string& cookie_line, + const CookieOptions& options); + + std::string GetCookiesWithOptions(const GURL& url, + const CookieOptions& options); + + void GetCookiesWithInfo(const GURL& url, + const CookieOptions& options, + std::string* cookie_line, + std::vector<CookieInfo>* cookie_infos); + + void DeleteCookie(const GURL& url, const std::string& cookie_name); + + 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. + // Note: this method should always be called with lock_ held. + void InitIfNecessary() { + if (!initialized_) { + if (store_) { + InitStore(); + } else { + loaded_ = true; + } + initialized_ = true; + } + } + + // Initializes the backing store and reads existing cookies from it. + // Should only be called by InitIfNecessary(). + void InitStore(); + + // 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_blocked_on_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); + + // Invokes deferred calls. + void InvokeQueue(); + + // Checks that |cookies_| matches our invariants, and tries to repair any + // inconsistencies. (In other words, it does not have duplicate cookies). + void EnsureCookiesMapIsValid(); + + // Checks for any duplicate cookies for CookieMap key |key| which lie between + // |begin| and |end|. If any are found, all but the most recent are deleted. + // Returns the number of duplicate cookies that were deleted. + int TrimDuplicateCookiesForKey(const std::string& key, + CookieMap::iterator begin, + CookieMap::iterator end); + + void SetDefaultCookieableSchemes(); + + void FindCookiesForHostAndDomain(const GURL& url, + const CookieOptions& options, + bool update_access_time, + std::vector<CanonicalCookie*>* cookies); + + void FindCookiesForKey(const std::string& key, + const GURL& url, + const CookieOptions& options, + const base::Time& current, + bool update_access_time, + std::vector<CanonicalCookie*>* cookies); + + // Delete any cookies that are equivalent to |ecc| (same path, domain, etc). + // If |skip_httponly| is true, httponly cookies will not be deleted. The + // return value with be true if |skip_httponly| skipped an httponly cookie. + // |key| is the key to find the cookie in cookies_; see the comment before + // the CookieMap typedef for details. + // NOTE: There should never be more than a single matching equivalent cookie. + bool DeleteAnyEquivalentCookie(const std::string& key, + const CanonicalCookie& ecc, + bool skip_httponly, + bool already_expired); + + // Takes ownership of *cc. + void InternalInsertCookie(const std::string& key, + 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, + const base::Time& creation_time, + const CookieOptions& options); + + void InternalUpdateCookieAccessTime(CanonicalCookie* cc, + const base::Time& current_time); + + // |deletion_cause| argument is used for collecting statistics and choosing + // the correct Delegate::ChangeCause for OnCookieChanged notifications. + void InternalDeleteCookie(CookieMap::iterator it, bool sync_to_store, + DeletionCause deletion_cause); + + // If the number of cookies for CookieMap key |key|, or globally, are + // over the preset maximums above, garbage collect, first for the host and + // then globally. See comments above garbage collection threshold + // constants for details. + // + // Returns the number of cookies deleted (useful for debugging). + int GarbageCollect(const base::Time& current, const std::string& key); + + // Helper for GarbageCollect(); can be called directly as well. Deletes + // all expired cookies in |itpair|. If |cookie_its| is non-NULL, it is + // populated with all the non-expired cookies from |itpair|. + // + // Returns the number of cookies deleted. + int GarbageCollectExpired(const base::Time& current, + const CookieMapItPair& itpair, + std::vector<CookieMap::iterator>* cookie_its); + + // Helper for GarbageCollect(). Deletes all cookies in the list + // that were accessed before |keep_accessed_after|, using DeletionCause + // |cause|. If |keep_accessed_after| is null, deletes all cookies in the + // list. Returns the number of cookies deleted. + int GarbageCollectDeleteList(const base::Time& current, + const base::Time& keep_accessed_after, + DeletionCause cause, + std::vector<CookieMap::iterator>& cookie_its); + + // Find the key (for lookup in cookies_) based on the given domain. + // See comment on keys before the CookieMap typedef. + std::string GetKey(const std::string& domain) const; + + bool HasCookieableScheme(const GURL& url); + + // Statistics support + + // This function should be called repeatedly, and will record + // statistics if a sufficient time period has passed. + void RecordPeriodicStats(const base::Time& current_time); + + // Initialize the above variables; should only be called from + // the constructor. + void InitializeHistograms(); + + // The resolution of our time isn't enough, so we do something + // ugly and increment when we've seen the same time twice. + base::Time CurrentTime(); + + // 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_; + base::Histogram* histogram_between_access_interval_minutes_; + base::Histogram* histogram_evicted_last_access_minutes_; + base::Histogram* histogram_count_; + base::Histogram* histogram_domain_count_; + base::Histogram* histogram_etldp1_count_; + base::Histogram* histogram_domain_per_etldp1_count_; + base::Histogram* histogram_number_duplicate_db_cookies_; + base::Histogram* histogram_cookie_deletion_cause_; + base::Histogram* histogram_time_get_; + base::Histogram* histogram_time_mac_; + base::Histogram* histogram_time_blocked_on_load_; + + CookieMap cookies_; + + // Indicates whether the cookie store has been initialized. This happens + // lazily in InitStoreIfNecessary(). + bool initialized_; + + // Indicates whether loading from the backend store is completed and + // calls may be immediately processed. + bool loaded_; + + // 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_; + + scoped_refptr<PersistentCookieStore> store_; + + base::Time last_time_seen_; + + // Minimum delay after updating a cookie's LastAccessDate before we will + // update it again. + const base::TimeDelta last_access_threshold_; + + // Approximate date of access time of least recently accessed cookie + // in |cookies_|. Note that this is not guaranteed to be accurate, only a) + // to be before or equal to the actual time, and b) to be accurate + // immediately after a garbage collection that scans through all the cookies. + // 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 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_; + + // Lock for thread-safety + base::Lock lock_; + + base::Time last_statistic_record_time_; + + bool keep_expired_cookies_; + bool persist_session_cookies_; + + static bool enable_file_scheme_; + + DISALLOW_COPY_AND_ASSIGN(CookieMonster); +}; + +class NET_EXPORT CookieMonster::CanonicalCookie { + public: + + // These constructors do no validation or canonicalization of their inputs; + // the resulting CanonicalCookies should not be relied on to be canonical + // unless the caller has done appropriate validation and canonicalization + // themselves. + CanonicalCookie(); + CanonicalCookie(const GURL& url, + const std::string& name, + const std::string& value, + const std::string& domain, + const std::string& path, + const std::string& mac_key, + const std::string& mac_algorithm, + const base::Time& creation, + const base::Time& expiration, + const base::Time& last_access, + bool secure, + bool httponly, + bool has_expires, + bool is_persistent); + + // This constructor does canonicalization but not validation. + // The result of this constructor should not be relied on in contexts + // in which pre-validation of the ParsedCookie has not been done. + CanonicalCookie(const GURL& url, const ParsedCookie& pc); + + ~CanonicalCookie(); + + // Supports the default copy constructor. + + // Creates a canonical cookie from parsed cookie. + // Canonicalizes and validates inputs. May return NULL if an attribute + // value is invalid. + static CanonicalCookie* Create(const GURL& url, + const ParsedCookie& pc); + + // Creates a canonical cookie from unparsed attribute values. + // Canonicalizes and validates inputs. May return NULL if an attribute + // value is invalid. + static CanonicalCookie* Create(const GURL& url, + const std::string& name, + const std::string& value, + const std::string& domain, + const std::string& path, + const std::string& mac_key, + const std::string& mac_algorithm, + const base::Time& creation, + const base::Time& expiration, + bool secure, + bool http_only, + bool is_persistent); + + const std::string& Source() const { return source_; } + const std::string& Name() const { return name_; } + const std::string& Value() const { return value_; } + const std::string& Domain() const { return domain_; } + const std::string& Path() const { return path_; } + const std::string& MACKey() const { return mac_key_; } + const std::string& MACAlgorithm() const { return mac_algorithm_; } + const base::Time& CreationDate() const { return creation_date_; } + const base::Time& LastAccessDate() const { return last_access_date_; } + bool DoesExpire() const { return has_expires_; } + bool IsPersistent() const { return is_persistent_; } + const base::Time& ExpiryDate() const { return expiry_date_; } + bool IsSecure() const { return secure_; } + bool IsHttpOnly() const { return httponly_; } + bool IsDomainCookie() const { + return !domain_.empty() && domain_[0] == '.'; } + bool IsHostCookie() const { return !IsDomainCookie(); } + + bool IsExpired(const base::Time& current) { + return has_expires_ && current >= expiry_date_; + } + + // Are the cookies considered equivalent in the eyes of RFC 2965. + // The RFC says that name must match (case-sensitive), domain must + // match (case insensitive), and path must match (case sensitive). + // For the case insensitive domain compare, we rely on the domain + // having been canonicalized (in + // GetCookieDomainWithString->CanonicalizeHost). + bool IsEquivalent(const CanonicalCookie& ecc) const { + // It seems like it would make sense to take secure and httponly into + // account, but the RFC doesn't specify this. + // NOTE: Keep this logic in-sync with TrimDuplicateCookiesForHost(). + return (name_ == ecc.Name() && domain_ == ecc.Domain() + && path_ == ecc.Path()); + } + + void SetLastAccessDate(const base::Time& date) { + last_access_date_ = date; + } + + bool IsOnPath(const std::string& url_path) const; + bool IsDomainMatch(const std::string& scheme, const std::string& host) const; + + std::string DebugString() const; + + // Returns the cookie source when cookies are set for |url|. This function + // is public for unit test purposes only. + static std::string GetCookieSourceFromURL(const GURL& url); + + private: + // Gives the session cookie an expiration time if needed + void SetSessionCookieExpiryTime(); + + // The source member of a canonical cookie is the origin of the URL that tried + // to set this cookie, minus the port number if any. This field is not + // persistent though; its only used in the in-tab cookies dialog to show the + // user the source URL. This is used for both allowed and blocked cookies. + // When a CanonicalCookie is constructed from the backing store (common case) + // this field will be null. CanonicalCookie consumers should not rely on + // this field unless they guarantee that the creator of those + // CanonicalCookies properly initialized the field. + // TODO(abarth): We might need to make this field persistent for MAC cookies. + std::string source_; + std::string name_; + std::string value_; + std::string domain_; + std::string path_; + std::string mac_key_; // TODO(abarth): Persist to disk. + std::string mac_algorithm_; // TODO(abarth): Persist to disk. + base::Time creation_date_; + base::Time expiry_date_; + base::Time last_access_date_; + bool secure_; + bool httponly_; + bool has_expires_; + bool is_persistent_; +}; + +class CookieMonster::Delegate + : public base::RefCountedThreadSafe<CookieMonster::Delegate> { + public: + // The publicly relevant reasons a cookie might be changed. + enum ChangeCause { + // The cookie was changed directly by a consumer's action. + CHANGE_COOKIE_EXPLICIT, + // The cookie was automatically removed due to an insert operation that + // overwrote it. + CHANGE_COOKIE_OVERWRITE, + // The cookie was automatically removed as it expired. + CHANGE_COOKIE_EXPIRED, + // The cookie was automatically evicted during garbage collection. + CHANGE_COOKIE_EVICTED, + // The cookie was overwritten with an already-expired expiration date. + CHANGE_COOKIE_EXPIRED_OVERWRITE + }; + + // Will be called when a cookie is added or removed. The function is passed + // the respective |cookie| which was added to or removed from the cookies. + // If |removed| is true, the cookie was deleted, and |cause| will be set + // to the reason for it's removal. If |removed| is false, the cookie was + // added, and |cause| will be set to CHANGE_COOKIE_EXPLICIT. + // + // As a special case, note that updating a cookie's properties is implemented + // as a two step process: the cookie to be updated is first removed entirely, + // generating a notification with cause CHANGE_COOKIE_OVERWRITE. Afterwards, + // a new cookie is written with the updated values, generating a notification + // with cause CHANGE_COOKIE_EXPLICIT. + virtual void OnCookieChanged(const CookieMonster::CanonicalCookie& cookie, + bool removed, + ChangeCause cause) = 0; + protected: + friend class base::RefCountedThreadSafe<CookieMonster::Delegate>; + virtual ~Delegate() {} +}; + +class NET_EXPORT CookieMonster::ParsedCookie { + public: + typedef std::pair<std::string, std::string> TokenValuePair; + typedef std::vector<TokenValuePair> PairList; + + // The maximum length of a cookie string we will try to parse + static const size_t kMaxCookieSize = 4096; + // The maximum number of Token/Value pairs. Shouldn't have more than 8. + static const int kMaxPairs = 16; + + // Construct from a cookie string like "BLAH=1; path=/; domain=.google.com" + ParsedCookie(const std::string& cookie_line); + ~ParsedCookie(); + + // You should not call any other methods on the class if !IsValid + bool IsValid() const { return is_valid_; } + + const std::string& Name() const { return pairs_[0].first; } + const std::string& Token() const { return Name(); } + const std::string& Value() const { return pairs_[0].second; } + + bool HasPath() const { return path_index_ != 0; } + const std::string& Path() const { return pairs_[path_index_].second; } + bool HasDomain() const { return domain_index_ != 0; } + const std::string& Domain() const { return pairs_[domain_index_].second; } + bool HasMACKey() const { return mac_key_index_ != 0; } + const std::string& MACKey() const { return pairs_[mac_key_index_].second; } + bool HasMACAlgorithm() const { return mac_algorithm_index_ != 0; } + const std::string& MACAlgorithm() const { + return pairs_[mac_algorithm_index_].second; + } + bool HasExpires() const { return expires_index_ != 0; } + const std::string& Expires() const { return pairs_[expires_index_].second; } + bool HasMaxAge() const { return maxage_index_ != 0; } + const std::string& MaxAge() const { return pairs_[maxage_index_].second; } + bool IsSecure() const { return secure_index_ != 0; } + bool IsHttpOnly() const { return httponly_index_ != 0; } + + // Returns the number of attributes, for example, returning 2 for: + // "BLAH=hah; path=/; domain=.google.com" + size_t NumberOfAttributes() const { return pairs_.size() - 1; } + + // For debugging only! + std::string DebugString() const; + + // Returns an iterator pointing to the first terminator character found in + // the given string. + static std::string::const_iterator FindFirstTerminator(const std::string& s); + + // Given iterators pointing to the beginning and end of a string segment, + // returns as output arguments token_start and token_end to the start and end + // positions of a cookie attribute token name parsed from the segment, and + // updates the segment iterator to point to the next segment to be parsed. + // If no token is found, the function returns false. + static bool ParseToken(std::string::const_iterator* it, + const std::string::const_iterator& end, + std::string::const_iterator* token_start, + std::string::const_iterator* token_end); + + // Given iterators pointing to the beginning and end of a string segment, + // returns as output arguments value_start and value_end to the start and end + // positions of a cookie attribute value parsed from the segment, and updates + // the segment iterator to point to the next segment to be parsed. + static void ParseValue(std::string::const_iterator* it, + const std::string::const_iterator& end, + std::string::const_iterator* value_start, + std::string::const_iterator* value_end); + + // Same as the above functions, except the input is assumed to contain the + // desired token/value and nothing else. + static std::string ParseTokenString(const std::string& token); + static std::string ParseValueString(const std::string& value); + + private: + static const char kTerminator[]; + static const int kTerminatorLen; + static const char kWhitespace[]; + static const char kValueSeparator[]; + static const char kTokenSeparator[]; + + void ParseTokenValuePairs(const std::string& cookie_line); + void SetupAttributes(); + + PairList pairs_; + bool is_valid_; + // These will default to 0, but that should never be valid since the + // 0th index is the user supplied token/value, not an attribute. + // We're really never going to have more than like 8 attributes, so we + // could fit these into 3 bits each if we're worried about size... + size_t path_index_; + size_t domain_index_; + size_t mac_key_index_; + size_t mac_algorithm_index_; + size_t expires_index_; + size_t maxage_index_; + size_t secure_index_; + size_t httponly_index_; + + DISALLOW_COPY_AND_ASSIGN(ParsedCookie); +}; + +typedef base::RefCountedThreadSafe<CookieMonster::PersistentCookieStore> + RefcountedPersistentCookieStore; + +class CookieMonster::PersistentCookieStore + : public RefcountedPersistentCookieStore { + public: + virtual ~PersistentCookieStore() {} + + typedef base::Callback<void(const std::vector< + CookieMonster::CanonicalCookie*>&)> LoadedCallback; + + // Initializes the store and retrieves the existing cookies. This will be + // 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; + virtual void DeleteCookie(const CanonicalCookie& cc) = 0; + + // Sets the value of the user preference whether the persistent storage + // must be deleted upon destruction. + virtual void SetClearLocalStateOnExit(bool clear_local_state) = 0; + + // Flushes the store and posts |callback| when complete. + virtual void Flush(const base::Closure& callback) = 0; + + protected: + PersistentCookieStore() {} + + private: + DISALLOW_COPY_AND_ASSIGN(PersistentCookieStore); +}; + +class CookieList : public std::vector<CookieMonster::CanonicalCookie> { +}; + +} // namespace net + +#endif // NET_COOKIES_COOKIE_MONSTER_H_ diff --git a/net/base/cookie_monster_perftest.cc b/net/cookies/cookie_monster_perftest.cc index a425138..eeab92f 100644 --- a/net/base/cookie_monster_perftest.cc +++ b/net/cookies/cookie_monster_perftest.cc @@ -1,4 +1,4 @@ -// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// 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. @@ -10,8 +10,8 @@ #include "base/string_util.h" #include "base/stringprintf.h" #include "googleurl/src/gurl.h" -#include "net/base/cookie_monster.h" -#include "net/base/cookie_monster_store_test.h" +#include "net/cookies/cookie_monster.h" +#include "net/cookies/cookie_monster_store_test.h" #include "testing/gtest/include/gtest/gtest.h" namespace { diff --git a/net/base/cookie_monster_store_test.cc b/net/cookies/cookie_monster_store_test.cc index e916e28..822aacc 100644 --- a/net/base/cookie_monster_store_test.cc +++ b/net/cookies/cookie_monster_store_test.cc @@ -1,8 +1,8 @@ -// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// 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 "net/base/cookie_monster_store_test.h" +#include "net/cookies/cookie_monster_store_test.h" #include "base/bind.h" #include "base/message_loop.h" diff --git a/net/cookies/cookie_monster_store_test.h b/net/cookies/cookie_monster_store_test.h new file mode 100644 index 0000000..a05ddd3 --- /dev/null +++ b/net/cookies/cookie_monster_store_test.h @@ -0,0 +1,206 @@ +// 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. + +// 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. + +#ifndef NET_COOKIES_COOKIE_MONSTER_STORE_TEST_H_ +#define NET_COOKIES_COOKIE_MONSTER_STORE_TEST_H_ +#pragma once + +#include <map> +#include <string> +#include <utility> +#include <vector> +#include "net/cookies/cookie_monster.h" + +namespace base { +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 { + ADD, + UPDATE_ACCESS_TIME, + REMOVE, + }; + + CookieStoreCommand(Type type, + const CookieMonster::CanonicalCookie& cookie) + : type(type), + cookie(cookie) {} + + Type type; + 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 CookieMonster::PersistentCookieStore { + public: + typedef std::vector<CookieStoreCommand> CommandList; + + MockPersistentCookieStore(); + virtual ~MockPersistentCookieStore(); + + void SetLoadExpectation( + bool return_value, + const std::vector<CookieMonster::CanonicalCookie*>& result); + + const CommandList& commands() const { + return commands_; + } + + 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; + + virtual void UpdateCookieAccessTime( + const CookieMonster::CanonicalCookie& cookie) OVERRIDE; + + virtual void DeleteCookie( + const CookieMonster::CanonicalCookie& cookie) OVERRIDE; + + virtual void Flush(const base::Closure& callback) OVERRIDE; + + // No files are created so nothing to clear either + virtual void SetClearLocalStateOnExit(bool clear_local_state) OVERRIDE; + + private: + CommandList commands_; + + // 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); +}; + +// Mock for CookieMonster::Delegate +class MockCookieMonsterDelegate : public CookieMonster::Delegate { + public: + typedef std::pair<CookieMonster::CanonicalCookie, bool> + CookieNotification; + + MockCookieMonsterDelegate(); + + const std::vector<CookieNotification>& changes() const { return changes_; } + + void reset() { changes_.clear(); } + + virtual void OnCookieChanged( + const CookieMonster::CanonicalCookie& cookie, + bool removed, + CookieMonster::Delegate::ChangeCause cause) OVERRIDE; + + private: + virtual ~MockCookieMonsterDelegate(); + + std::vector<CookieNotification> changes_; + + DISALLOW_COPY_AND_ASSIGN(MockCookieMonsterDelegate); +}; + +// Helper to build a single CanonicalCookie. +CookieMonster::CanonicalCookie BuildCanonicalCookie( + const std::string& key, + const std::string& cookie_line, + const base::Time& creation_time); + +// Helper to build a list of CanonicalCookie*s. +void AddCookieToList( + const std::string& key, + const std::string& cookie_line, + const base::Time& creation_time, + std::vector<CookieMonster::CanonicalCookie*>* out_list); + +// Just act like a backing database. Keep cookie information from +// Add/Update/Delete and regurgitate it when Load is called. +class MockSimplePersistentCookieStore + : public CookieMonster::PersistentCookieStore { + public: + MockSimplePersistentCookieStore(); + virtual ~MockSimplePersistentCookieStore(); + + 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; + + virtual void UpdateCookieAccessTime( + const CookieMonster::CanonicalCookie& cookie) OVERRIDE; + + virtual void DeleteCookie( + const CookieMonster::CanonicalCookie& cookie) OVERRIDE; + + virtual void Flush(const base::Closure& callback) OVERRIDE; + + 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 +// MockSimplePersistentCookieStore for garbage collection testing. +// +// Fill the store through import with |num_cookies| cookies, |num_old_cookies| +// with access time Now()-days_old, the rest with access time Now(). +// Do two SetCookies(). Return whether each of the two SetCookies() took +// longer than |gc_perf_micros| to complete, and how many cookie were +// left in the store afterwards. +CookieMonster* CreateMonsterFromStoreForGC( + int num_cookies, + int num_old_cookies, + int days_old); + +} // namespace net + +#endif // NET_COOKIES_COOKIE_MONSTER_STORE_TEST_H_ diff --git a/net/base/cookie_monster_unittest.cc b/net/cookies/cookie_monster_unittest.cc index 541a2b1..24390bc 100644 --- a/net/base/cookie_monster_unittest.cc +++ b/net/cookies/cookie_monster_unittest.cc @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "net/base/cookie_store_unittest.h" +#include "net/cookies/cookie_store_unittest.h" #include <time.h> #include <string> @@ -18,9 +18,9 @@ #include "base/threading/thread.h" #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 "net/base/cookie_util.h" +#include "net/cookies/cookie_monster.h" +#include "net/cookies/cookie_monster_store_test.h" // For CookieStore mock +#include "net/cookies/cookie_util.h" #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" diff --git a/net/cookies/cookie_options.h b/net/cookies/cookie_options.h new file mode 100644 index 0000000..a59054a --- /dev/null +++ b/net/cookies/cookie_options.h @@ -0,0 +1,40 @@ +// 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. + +// Brought to you by number 42. + +#ifndef NET_COOKIES_COOKIE_OPTIONS_H_ +#define NET_COOKIES_COOKIE_OPTIONS_H_ +#pragma once + +namespace net { + +class CookieOptions { + public: + // Default is to exclude httponly, which means: + // - reading operations will not return httponly cookies. + // - writing operations will not write httponly cookies. + CookieOptions() + : exclude_httponly_(true), + force_session_(false) { + } + + void set_exclude_httponly() { exclude_httponly_ = true; } + void set_include_httponly() { exclude_httponly_ = false; } + bool exclude_httponly() const { return exclude_httponly_; } + + // Forces a cookie to be saved as a session cookie. If the expiration time of + // the cookie is in the past, i.e. the cookie would end up being deleted, this + // option is ignored. See CookieMonsterTest.ForceSessionOnly. + void set_force_session() { force_session_ = true; } + bool force_session() const { return force_session_; } + + private: + bool exclude_httponly_; + bool force_session_; +}; +} // namespace net + +#endif // NET_COOKIES_COOKIE_OPTIONS_H_ + diff --git a/net/base/cookie_store.cc b/net/cookies/cookie_store.cc index a806d6c..25060904 100644 --- a/net/base/cookie_store.cc +++ b/net/cookies/cookie_store.cc @@ -1,10 +1,10 @@ -// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// 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 "net/base/cookie_store.h" +#include "net/cookies/cookie_store.h" -#include "net/base/cookie_options.h" +#include "net/cookies/cookie_options.h" namespace net { diff --git a/net/cookies/cookie_store.h b/net/cookies/cookie_store.h new file mode 100644 index 0000000..b9772d5 --- /dev/null +++ b/net/cookies/cookie_store.h @@ -0,0 +1,113 @@ +// 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. + +// Brought to you by number 42. + +#ifndef NET_COOKIES_COOKIE_STORE_H_ +#define NET_COOKIES_COOKIE_STORE_H_ +#pragma once + +#include <string> +#include <vector> + +#include "base/basictypes.h" +#include "base/callback.h" +#include "base/memory/ref_counted.h" +#include "base/time.h" +#include "net/cookies/cookie_options.h" +#include "net/base/net_export.h" + +class GURL; + +namespace net { + +class CookieMonster; + +// An interface for storing and retrieving cookies. Implementations need to +// be thread safe as its methods can be accessed from IO as well as UI threads. +class NET_EXPORT CookieStore : public base::RefCountedThreadSafe<CookieStore> { + public: + // This struct contains additional consumer-specific information that might + // be stored with cookies; currently just MAC information, see: + // http://tools.ietf.org/html/draft-ietf-oauth-v2-http-mac + struct NET_EXPORT CookieInfo { + CookieInfo(); + ~CookieInfo(); + + // The name of the cookie. + std::string name; + // TODO(abarth): Add value if any clients need it. + + // The time at which the cookie was created. + base::Time creation_date; + + // The value of the MAC-Key and MAC-Algorithm attributes, if present. + std::string mac_key; + std::string mac_algorithm; + }; + + // Callback definitions. + typedef base::Callback <void( + const std::string& cookie_line, + const std::vector<CookieInfo>& cookie_infos)> GetCookieInfoCallback; + typedef base::Callback<void(const std::string& cookie)> + GetCookiesCallback; + typedef base::Callback<void(bool success)> SetCookiesCallback; + typedef base::Callback<void(int num_deleted)> DeleteCallback; + + + // Sets a single cookie. Expects a cookie line, like "a=1; domain=b.com". + // + // Fails either if the cookie is invalid or if this is a non-HTTPONLY cookie + // and it would overwrite an existing HTTPONLY cookie. + // Returns true if the cookie is successfully set. + virtual void SetCookieWithOptionsAsync( + const GURL& url, + const std::string& cookie_line, + const CookieOptions& options, + const SetCookiesCallback& callback) = 0; + + // TODO(???): what if the total size of all the cookies >4k, can we have a + // header that big or do we need multiple Cookie: headers? + // Note: Some sites, such as Facebook, occasionally use Cookie headers >4k. + // + // Simple interface, gets a cookie string "a=b; c=d" for the given URL. + // Use options to access httponly cookies. + virtual void GetCookiesWithOptionsAsync( + const GURL& url, const CookieOptions& options, + const GetCookiesCallback& callback) = 0; + + // This function is similar to GetCookiesWithOptions same functionality as + // GetCookiesWithOptions except that it additionally provides detailed + // information about the cookie contained in the cookie line. See |struct + // CookieInfo| above for details. + virtual void GetCookiesWithInfoAsync( + const GURL& url, + const CookieOptions& options, + const GetCookieInfoCallback& callback) = 0; + + // Deletes the passed in cookie for the specified URL. + virtual void DeleteCookieAsync(const GURL& url, + const std::string& cookie_name, + const base::Closure& callback) = 0; + + // Deletes all of the cookies that have a creation_date greater than or equal + // to |delete_begin| and less than |delete_end| + // Returns the number of cookies that have been deleted. + virtual void DeleteAllCreatedBetweenAsync(const base::Time& delete_begin, + const base::Time& delete_end, + const DeleteCallback& callback) = 0; + + // Returns the underlying CookieMonster. + virtual CookieMonster* GetCookieMonster() = 0; + + protected: + friend class base::RefCountedThreadSafe<CookieStore>; + CookieStore(); + virtual ~CookieStore(); +}; + +} // namespace net + +#endif // NET_COOKIES_COOKIE_STORE_H_ diff --git a/net/base/cookie_store_test_callbacks.cc b/net/cookies/cookie_store_test_callbacks.cc index e16df06..0d4cb5c 100644 --- a/net/base/cookie_store_test_callbacks.cc +++ b/net/cookies/cookie_store_test_callbacks.cc @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "net/base/cookie_store_test_callbacks.h" +#include "net/cookies/cookie_store_test_callbacks.h" #include "base/message_loop.h" #include "base/threading/thread.h" diff --git a/net/cookies/cookie_store_test_callbacks.h b/net/cookies/cookie_store_test_callbacks.h new file mode 100644 index 0000000..d201d32 --- /dev/null +++ b/net/cookies/cookie_store_test_callbacks.h @@ -0,0 +1,137 @@ +// 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. + +#ifndef NET_COOKIES_COOKIE_STORE_TEST_CALLBACKS_H_ +#define NET_COOKIES_COOKIE_STORE_TEST_CALLBACKS_H_ +#pragma once + +#include <string> +#include <vector> + +#include "net/cookies/cookie_store.h" + +class MessageLoop; + +namespace base { +class Thread; +} + +namespace net { + +// Defines common behaviour for the callbacks from GetCookies, SetCookies, etc. +// Asserts that the current thread is the expected invocation thread, sends a +// quit to the thread in which it was constructed. +class CookieCallback { + public: + // Indicates whether the callback has been called. + bool did_run() { return did_run_; } + + protected: + // Constructs a callback that expects to be called in the given thread and + // will, upon execution, send a QUIT to the constructing thread. + explicit CookieCallback(base::Thread* run_in_thread); + + // Constructs a callback that expects to be called in current thread and will + // send a QUIT to the constructing thread. + CookieCallback(); + + // Tests whether the current thread was the caller's thread. + // Sends a QUIT to the constructing thread. + void CallbackEpilogue(); + + private: + bool did_run_; + base::Thread* run_in_thread_; + MessageLoop* run_in_loop_; + MessageLoop* parent_loop_; + MessageLoop* loop_to_quit_; +}; + +// Callback implementations for the asynchronous CookieStore methods. + +class SetCookieCallback : public CookieCallback { + public: + SetCookieCallback(); + explicit SetCookieCallback(base::Thread* run_in_thread); + + void Run(bool result) { + result_ = result; + CallbackEpilogue(); + } + + bool result() { return result_; } + + private: + bool result_; +}; + +class GetCookieStringCallback : public CookieCallback { + public: + GetCookieStringCallback(); + explicit GetCookieStringCallback(base::Thread* run_in_thread); + + void Run(const std::string& cookie) { + cookie_ = cookie; + CallbackEpilogue(); + } + + const std::string& cookie() { return cookie_; } + + private: + std::string cookie_; +}; + +class GetCookiesWithInfoCallback : public CookieCallback { + public: + GetCookiesWithInfoCallback(); + explicit GetCookiesWithInfoCallback(base::Thread* run_in_thread); + ~GetCookiesWithInfoCallback(); + + void Run( + const std::string& cookie_line, + const std::vector<CookieStore::CookieInfo>& cookie_info) { + cookie_line_ = cookie_line; + cookie_info_ = cookie_info; + CallbackEpilogue(); + } + + const std::string& cookie_line() { return cookie_line_; } + const std::vector<CookieStore::CookieInfo>& cookie_info() { + return cookie_info_; + } + + private: + std::string cookie_line_; + std::vector<CookieStore::CookieInfo> cookie_info_; +}; + +class DeleteCallback : public CookieCallback { + public: + DeleteCallback(); + explicit DeleteCallback(base::Thread* run_in_thread); + + void Run(int num_deleted) { + num_deleted_ = num_deleted; + CallbackEpilogue(); + } + + int num_deleted() { return num_deleted_; } + + private: + int num_deleted_; +}; + +class DeleteCookieCallback : public CookieCallback { + public: + DeleteCookieCallback(); + explicit DeleteCookieCallback(base::Thread* run_in_thread); + + void Run() { + CallbackEpilogue(); + } +}; + +} // namespace net + +#endif // NET_COOKIES_COOKIE_STORE_TEST_CALLBACKS_H_ diff --git a/net/base/cookie_store_test_helpers.cc b/net/cookies/cookie_store_test_helpers.cc index 832e3da..e764c9c 100644 --- a/net/base/cookie_store_test_helpers.cc +++ b/net/cookies/cookie_store_test_helpers.cc @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "net/base/cookie_store_test_helpers.h" +#include "net/cookies/cookie_store_test_helpers.h" #include "base/bind.h" #include "base/message_loop.h" diff --git a/net/cookies/cookie_store_test_helpers.h b/net/cookies/cookie_store_test_helpers.h new file mode 100644 index 0000000..4e7ce27 --- /dev/null +++ b/net/cookies/cookie_store_test_helpers.h @@ -0,0 +1,105 @@ +// 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. + +#ifndef NET_COOKIES_COOKIE_STORE_TEST_HELPERS_H_ +#define NET_COOKIES_COOKIE_STORE_TEST_HELPERS_H_ +#pragma once + +#include "net/cookies/cookie_monster.h" + +#include <string> +#include <vector> + +#include "base/callback_forward.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace net { + +class DelayedCookieMonster : public CookieStore { + public: + DelayedCookieMonster(); + + // Call the asynchronous CookieMonster function, expect it to immediately + // invoke the internal callback. + // Post a delayed task to invoke the original callback with the results. + + virtual void SetCookieWithOptionsAsync( + const GURL& url, + const std::string& cookie_line, + const CookieOptions& options, + const CookieMonster::SetCookiesCallback& callback) OVERRIDE; + + virtual void GetCookiesWithOptionsAsync( + const GURL& url, const CookieOptions& options, + const CookieMonster::GetCookiesCallback& callback) OVERRIDE; + + virtual void GetCookiesWithInfoAsync( + const GURL& url, + const CookieOptions& options, + const CookieMonster::GetCookieInfoCallback& callback) OVERRIDE; + + virtual bool SetCookieWithOptions(const GURL& url, + const std::string& cookie_line, + const CookieOptions& options); + + virtual std::string GetCookiesWithOptions(const GURL& url, + const CookieOptions& options); + + virtual void GetCookiesWithInfo(const GURL& url, + const CookieOptions& options, + std::string* cookie_line, + std::vector<CookieInfo>* cookie_infos); + + virtual void DeleteCookie(const GURL& url, + const std::string& cookie_name); + + virtual void DeleteCookieAsync(const GURL& url, + const std::string& cookie_name, + const base::Closure& callback) OVERRIDE; + + virtual void DeleteAllCreatedBetweenAsync( + const base::Time& delete_begin, + const base::Time& delete_end, + const DeleteCallback& callback) OVERRIDE; + + virtual CookieMonster* GetCookieMonster() OVERRIDE; + + private: + + // Be called immediately from CookieMonster. + + void GetCookiesInternalCallback( + const std::string& cookie_line, + const std::vector<CookieStore::CookieInfo>& cookie_info); + + void SetCookiesInternalCallback(bool result); + + void GetCookiesWithOptionsInternalCallback(const std::string& cookie); + + // Invoke the original callbacks. + + void InvokeGetCookiesCallback( + const CookieMonster::GetCookieInfoCallback& callback); + + void InvokeSetCookiesCallback( + const CookieMonster::SetCookiesCallback& callback); + + void InvokeGetCookieStringCallback( + const CookieMonster::GetCookiesCallback& callback); + + friend class base::RefCountedThreadSafe<DelayedCookieMonster>; + virtual ~DelayedCookieMonster(); + + scoped_refptr<CookieMonster> cookie_monster_; + + bool did_run_; + bool result_; + std::string cookie_; + std::string cookie_line_; + std::vector<CookieStore::CookieInfo> cookie_info_; +}; + +} // namespace net + +#endif // NET_COOKIES_COOKIE_STORE_TEST_HELPERS_H_ diff --git a/net/cookies/cookie_store_unittest.h b/net/cookies/cookie_store_unittest.h new file mode 100644 index 0000000..e1d23f8 --- /dev/null +++ b/net/cookies/cookie_store_unittest.h @@ -0,0 +1,1055 @@ +// 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. + +#ifndef NET_COOKIES_COOKIE_STORE_UNITTEST_H_ +#define NET_COOKIES_COOKIE_STORE_UNITTEST_H_ +#pragma once + +#include "base/bind.h" +#include "base/message_loop.h" +#include "base/string_tokenizer.h" +#include "base/threading/thread.h" +#include "googleurl/src/gurl.h" +#include "net/cookies/cookie_monster.h" +#include "net/cookies/cookie_store.h" +#include "net/cookies/cookie_store_test_callbacks.h" +#include "testing/gtest/include/gtest/gtest.h" + +// This file declares unittest templates that can be used to test common +// behavior of any CookieStore implementation. +// See cookie_monster_unittest.cc for an example of an implementation. + +namespace net { + +using base::Thread; + +const int kTimeout = 1000; + +const char kUrlFtp[] = "ftp://ftp.google.izzle/"; +const char kUrlGoogle[] = "http://www.google.izzle"; +const char kUrlGoogleFoo[] = "http://www.google.izzle/foo"; +const char kUrlGoogleBar[] = "http://www.google.izzle/bar"; +const char kUrlGoogleSecure[] = "https://www.google.izzle"; +const char kValidCookieLine[] = "A=B; path=/"; +const char kValidDomainCookieLine[] = "A=B; path=/; domain=google.izzle"; + +// The CookieStoreTestTraits must have the following members: +// struct CookieStoreTestTraits { +// // Factory function. +// static scoped_refptr<CookieStore> Create(); +// +// // The cookie store is a CookieMonster. Only used to test +// // GetCookieMonster(). +// static const bool is_cookie_monster; +// +// // The cookie store supports cookies with the exclude_httponly() option. +// static const bool supports_http_only; +// +// // The cookie store supports the GetCookiesWithInfoAsync() method. +// static const bool supports_cookies_with_info; +// +// // The cookie store is able to make the difference between the ".com" +// // and the "com" domains. +// static const bool supports_non_dotted_domains; +// +// // The cookie store handles the domains with trailing dots (such as "com.") +// // correctly. +// static const bool supports_trailing_dots; +// +// // The cookie store rejects cookies for invalid schemes such as ftp. +// static const bool filters_schemes; +// +// // The cookie store has a bug happening when a path is a substring of +// // another. +// static const bool has_path_prefix_bug; +// +// // Time to wait between two cookie insertions to ensure that cookies have +// // different creation times. +// static const int creation_time_granularity_in_ms; +// }; + +template <class CookieStoreTestTraits> +class CookieStoreTest : public testing::Test { + protected: + CookieStoreTest() + : url_google_(kUrlGoogle), + url_google_secure_(kUrlGoogleSecure), + url_google_foo_(kUrlGoogleFoo), + url_google_bar_(kUrlGoogleBar) { + // This test may be used outside of the net test suite, and thus may not + // have a message loop. + if (!MessageLoop::current()) + message_loop_.reset(new MessageLoop); + weak_factory_.reset( + new base::WeakPtrFactory<MessageLoop>(MessageLoop::current())); + } + + // Helper methods for the asynchronous Cookie Store API that call the + // asynchronous method and then pump the loop until the callback is invoked, + // finally returning the value. + + std::string GetCookies(CookieStore* cs, const GURL& url) { + DCHECK(cs); + CookieOptions options; + if (!CookieStoreTestTraits::supports_http_only) + options.set_include_httponly(); + GetCookieStringCallback callback; + cs->GetCookiesWithOptionsAsync( + url, options, + base::Bind(&GetCookieStringCallback::Run, base::Unretained(&callback))); + RunFor(kTimeout); + EXPECT_TRUE(callback.did_run()); + return callback.cookie(); + } + + std::string GetCookiesWithOptions(CookieStore* cs, + const GURL& url, + const CookieOptions& options) { + DCHECK(cs); + GetCookieStringCallback callback; + cs->GetCookiesWithOptionsAsync( + url, options, base::Bind(&GetCookieStringCallback::Run, + base::Unretained(&callback))); + RunFor(kTimeout); + EXPECT_TRUE(callback.did_run()); + return callback.cookie(); + } + + void GetCookiesWithInfo(CookieStore* cs, + const GURL& url, + const CookieOptions& options, + std::string* cookie_line, + std::vector<CookieStore::CookieInfo>* cookie_info) { + DCHECK(cs); + DCHECK(cookie_line); + DCHECK(cookie_info); + GetCookiesWithInfoCallback callback; + cs->GetCookiesWithInfoAsync( + url, options, base::Bind(&GetCookiesWithInfoCallback::Run, + base::Unretained(&callback))); + RunFor(kTimeout); + EXPECT_TRUE(callback.did_run()); + *cookie_line = callback.cookie_line(); + *cookie_info = callback.cookie_info(); + } + + bool SetCookieWithOptions(CookieStore* cs, + const GURL& url, + const std::string& cookie_line, + const CookieOptions& options) { + DCHECK(cs); + SetCookieCallback callback; + cs->SetCookieWithOptionsAsync( + url, cookie_line, options, + base::Bind(&SetCookieCallback::Run, base::Unretained(&callback))); + RunFor(kTimeout); + EXPECT_TRUE(callback.did_run()); + return callback.result(); + } + + bool SetCookie(CookieStore* cs, + const GURL& url, + const std::string& cookie_line) { + CookieOptions options; + if (!CookieStoreTestTraits::supports_http_only) + options.set_include_httponly(); + return SetCookieWithOptions(cs, url, cookie_line, options); + } + + void DeleteCookie(CookieStore* cs, + const GURL& url, + const std::string& cookie_name) { + DCHECK(cs); + DeleteCookieCallback callback; + cs->DeleteCookieAsync( + url, cookie_name, + base::Bind(&DeleteCookieCallback::Run, base::Unretained(&callback))); + RunFor(kTimeout); + EXPECT_TRUE(callback.did_run()); + } + + int DeleteCreatedBetween(CookieStore* cs, + const base::Time& delete_begin, + const base::Time& delete_end) { + DCHECK(cs); + DeleteCallback callback; + cs->DeleteAllCreatedBetweenAsync( + delete_begin, delete_end, + base::Bind(&DeleteCallback::Run, base::Unretained(&callback))); + RunFor(kTimeout); + EXPECT_TRUE(callback.did_run()); + return callback.num_deleted(); + } + + void RunFor(int ms) { + // Runs the test thread message loop for up to |ms| milliseconds. + MessageLoop::current()->PostDelayedTask( + FROM_HERE, base::Bind(&MessageLoop::Quit, weak_factory_->GetWeakPtr()), + base::TimeDelta::FromMilliseconds(ms)); + MessageLoop::current()->Run(); + weak_factory_->InvalidateWeakPtrs(); + } + + scoped_refptr<CookieStore> GetCookieStore() { + return CookieStoreTestTraits::Create(); + } + + // Compares two cookie lines. + void MatchCookieLines(const std::string& line1, const std::string& line2) { + EXPECT_EQ(TokenizeCookieLine(line1), TokenizeCookieLine(line2)); + } + + // Check the cookie line by polling until equality or a timeout is reached. + void MatchCookieLineWithTimeout(CookieStore* cs, + const GURL& url, + const std::string& line) { + std::string cookies = GetCookies(cs, url); + bool matched = (TokenizeCookieLine(line) == TokenizeCookieLine(cookies)); + base::Time polling_end_date = base::Time::Now() + + base::TimeDelta::FromMilliseconds( + CookieStoreTestTraits::creation_time_granularity_in_ms); + + while (!matched && base::Time::Now() <= polling_end_date) { + base::PlatformThread::Sleep(base::TimeDelta::FromMilliseconds(10)); + cookies = GetCookies(cs, url); + matched = (TokenizeCookieLine(line) == TokenizeCookieLine(cookies)); + } + + EXPECT_TRUE(matched) << "\"" << cookies + << "\" does not match \"" << line << "\""; + } + + GURL url_google_; + GURL url_google_secure_; + GURL url_google_foo_; + GURL url_google_bar_; + + scoped_ptr<base::WeakPtrFactory<MessageLoop> > weak_factory_; + scoped_ptr<MessageLoop> message_loop_; + + private: + // Returns a set of strings of type "name=value". Fails in case of duplicate. + std::set<std::string> TokenizeCookieLine(const std::string& line) { + std::set<std::string> tokens; + StringTokenizer tokenizer(line, " ;"); + while (tokenizer.GetNext()) + EXPECT_TRUE(tokens.insert(tokenizer.token()).second); + return tokens; + } +}; + +TYPED_TEST_CASE_P(CookieStoreTest); + +TYPED_TEST_P(CookieStoreTest, TypeTest) { + scoped_refptr<CookieStore> cs(this->GetCookieStore()); + EXPECT_EQ(cs->GetCookieMonster(), + (TypeParam::is_cookie_monster) ? + static_cast<CookieMonster*>(cs.get()) : NULL); +} + +TYPED_TEST_P(CookieStoreTest, DomainTest) { + scoped_refptr<CookieStore> cs(this->GetCookieStore()); + EXPECT_TRUE(this->SetCookie(cs, this->url_google_, "A=B")); + this->MatchCookieLines("A=B", this->GetCookies(cs, this->url_google_)); + EXPECT_TRUE(this->SetCookie(cs, this->url_google_, + "C=D; domain=.google.izzle")); + this->MatchCookieLines("A=B; C=D", this->GetCookies(cs, this->url_google_)); + + // Verify that A=B was set as a host cookie rather than a domain + // cookie -- should not be accessible from a sub sub-domain. + this->MatchCookieLines("C=D", + this->GetCookies(cs, GURL("http://foo.www.google.izzle"))); + + // Test and make sure we find domain cookies on the same domain. + EXPECT_TRUE(this->SetCookie(cs, this->url_google_, + "E=F; domain=.www.google.izzle")); + this->MatchCookieLines("A=B; C=D; E=F", + this->GetCookies(cs, this->url_google_)); + + // Test setting a domain= that doesn't start w/ a dot, should + // treat it as a domain cookie, as if there was a pre-pended dot. + EXPECT_TRUE(this->SetCookie(cs, this->url_google_, + "G=H; domain=www.google.izzle")); + this->MatchCookieLines("A=B; C=D; E=F; G=H", + this->GetCookies(cs, this->url_google_)); + + // Test domain enforcement, should fail on a sub-domain or something too deep. + EXPECT_FALSE(this->SetCookie(cs, this->url_google_, "I=J; domain=.izzle")); + this->MatchCookieLines("", this->GetCookies(cs, GURL("http://a.izzle"))); + EXPECT_FALSE(this->SetCookie(cs, this->url_google_, + "K=L; domain=.bla.www.google.izzle")); + this->MatchCookieLines("C=D; E=F; G=H", + this->GetCookies(cs, GURL("http://bla.www.google.izzle"))); + this->MatchCookieLines("A=B; C=D; E=F; G=H", + this->GetCookies(cs, this->url_google_)); +} + +// FireFox recognizes domains containing trailing periods as valid. +// IE and Safari do not. Assert the expected policy here. +TYPED_TEST_P(CookieStoreTest, DomainWithTrailingDotTest) { + scoped_refptr<CookieStore> cs(this->GetCookieStore()); + EXPECT_FALSE(this->SetCookie(cs, this->url_google_, + "a=1; domain=.www.google.com.")); + EXPECT_FALSE(this->SetCookie(cs, this->url_google_, + "b=2; domain=.www.google.com..")); + this->MatchCookieLines("", this->GetCookies(cs, this->url_google_)); +} + +// Test that cookies can bet set on higher level domains. +// http://b/issue?id=896491 +TYPED_TEST_P(CookieStoreTest, ValidSubdomainTest) { + scoped_refptr<CookieStore> cs(this->GetCookieStore()); + GURL url_abcd("http://a.b.c.d.com"); + GURL url_bcd("http://b.c.d.com"); + GURL url_cd("http://c.d.com"); + GURL url_d("http://d.com"); + + EXPECT_TRUE(this->SetCookie(cs, url_abcd, "a=1; domain=.a.b.c.d.com")); + EXPECT_TRUE(this->SetCookie(cs, url_abcd, "b=2; domain=.b.c.d.com")); + EXPECT_TRUE(this->SetCookie(cs, url_abcd, "c=3; domain=.c.d.com")); + EXPECT_TRUE(this->SetCookie(cs, url_abcd, "d=4; domain=.d.com")); + + this->MatchCookieLines("a=1; b=2; c=3; d=4", this->GetCookies(cs, url_abcd)); + this->MatchCookieLines("b=2; c=3; d=4", this->GetCookies(cs, url_bcd)); + this->MatchCookieLines("c=3; d=4", this->GetCookies(cs, url_cd)); + this->MatchCookieLines("d=4", this->GetCookies(cs, url_d)); + + // Check that the same cookie can exist on different sub-domains. + EXPECT_TRUE(this->SetCookie(cs, url_bcd, "X=bcd; domain=.b.c.d.com")); + EXPECT_TRUE(this->SetCookie(cs, url_bcd, "X=cd; domain=.c.d.com")); + this->MatchCookieLines("b=2; c=3; d=4; X=bcd; X=cd", + this->GetCookies(cs, url_bcd)); + this->MatchCookieLines("c=3; d=4; X=cd", this->GetCookies(cs, url_cd)); +} + +// Test that setting a cookie which specifies an invalid domain has +// no side-effect. An invalid domain in this context is one which does +// not match the originating domain. +// http://b/issue?id=896472 +TYPED_TEST_P(CookieStoreTest, InvalidDomainTest) { + { + scoped_refptr<CookieStore> cs(this->GetCookieStore()); + GURL url_foobar("http://foo.bar.com"); + + // More specific sub-domain than allowed. + EXPECT_FALSE(this->SetCookie(cs, url_foobar, + "a=1; domain=.yo.foo.bar.com")); + + EXPECT_FALSE(this->SetCookie(cs, url_foobar, "b=2; domain=.foo.com")); + EXPECT_FALSE(this->SetCookie(cs, url_foobar, "c=3; domain=.bar.foo.com")); + + // Different TLD, but the rest is a substring. + EXPECT_FALSE(this->SetCookie(cs, url_foobar, + "d=4; domain=.foo.bar.com.net")); + + // A substring that isn't really a parent domain. + EXPECT_FALSE(this->SetCookie(cs, url_foobar, "e=5; domain=ar.com")); + + // Completely invalid domains: + EXPECT_FALSE(this->SetCookie(cs, url_foobar, "f=6; domain=.")); + EXPECT_FALSE(this->SetCookie(cs, url_foobar, "g=7; domain=/")); + EXPECT_FALSE(this->SetCookie(cs, url_foobar, + "h=8; domain=http://foo.bar.com")); + EXPECT_FALSE(this->SetCookie(cs, url_foobar, "i=9; domain=..foo.bar.com")); + EXPECT_FALSE(this->SetCookie(cs, url_foobar, "j=10; domain=..bar.com")); + + // Make sure there isn't something quirky in the domain canonicalization + // that supports full URL semantics. + EXPECT_FALSE(this->SetCookie(cs, url_foobar, + "k=11; domain=.foo.bar.com?blah")); + EXPECT_FALSE(this->SetCookie(cs, url_foobar, + "l=12; domain=.foo.bar.com/blah")); + EXPECT_FALSE(this->SetCookie(cs, url_foobar, + "m=13; domain=.foo.bar.com:80")); + EXPECT_FALSE(this->SetCookie(cs, url_foobar, + "n=14; domain=.foo.bar.com:")); + EXPECT_FALSE(this->SetCookie(cs, url_foobar, + "o=15; domain=.foo.bar.com#sup")); + + this->MatchCookieLines("", this->GetCookies(cs, url_foobar)); + } + + { + // Make sure the cookie code hasn't gotten its subdomain string handling + // reversed, missed a suffix check, etc. It's important here that the two + // hosts below have the same domain + registry. + scoped_refptr<CookieStore> cs(this->GetCookieStore()); + GURL url_foocom("http://foo.com.com"); + EXPECT_FALSE(this->SetCookie(cs, url_foocom, + "a=1; domain=.foo.com.com.com")); + this->MatchCookieLines("", this->GetCookies(cs, url_foocom)); + } +} + +// Test the behavior of omitting dot prefix from domain, should +// function the same as FireFox. +// http://b/issue?id=889898 +TYPED_TEST_P(CookieStoreTest, DomainWithoutLeadingDotTest) { + { // The omission of dot results in setting a domain cookie. + scoped_refptr<CookieStore> cs(this->GetCookieStore()); + GURL url_hosted("http://manage.hosted.filefront.com"); + GURL url_filefront("http://www.filefront.com"); + EXPECT_TRUE(this->SetCookie(cs, url_hosted, + "sawAd=1; domain=filefront.com")); + this->MatchCookieLines("sawAd=1", this->GetCookies(cs, url_hosted)); + this->MatchCookieLines("sawAd=1", this->GetCookies(cs, url_filefront)); + } + + { // Even when the domains match exactly, don't consider it host cookie. + scoped_refptr<CookieStore> cs(this->GetCookieStore()); + GURL url("http://www.google.com"); + EXPECT_TRUE(this->SetCookie(cs, url, "a=1; domain=www.google.com")); + this->MatchCookieLines("a=1", this->GetCookies(cs, url)); + this->MatchCookieLines("a=1", + this->GetCookies(cs, GURL("http://sub.www.google.com"))); + this->MatchCookieLines("", + this->GetCookies(cs, GURL("http://something-else.com"))); + } +} + +// Test that the domain specified in cookie string is treated case-insensitive +// http://b/issue?id=896475. +TYPED_TEST_P(CookieStoreTest, CaseInsensitiveDomainTest) { + scoped_refptr<CookieStore> cs(this->GetCookieStore()); + GURL url("http://www.google.com"); + EXPECT_TRUE(this->SetCookie(cs, url, "a=1; domain=.GOOGLE.COM")); + EXPECT_TRUE(this->SetCookie(cs, url, "b=2; domain=.wWw.gOOgLE.coM")); + this->MatchCookieLines("a=1; b=2", this->GetCookies(cs, url)); +} + +TYPED_TEST_P(CookieStoreTest, TestIpAddress) { + GURL url_ip("http://1.2.3.4/weee"); + { + scoped_refptr<CookieStore> cs(this->GetCookieStore()); + EXPECT_TRUE(this->SetCookie(cs, url_ip, kValidCookieLine)); + this->MatchCookieLines("A=B", this->GetCookies(cs, url_ip)); + } + + { // IP addresses should not be able to set domain cookies. + scoped_refptr<CookieStore> cs(this->GetCookieStore()); + EXPECT_FALSE(this->SetCookie(cs, url_ip, "b=2; domain=.1.2.3.4")); + EXPECT_FALSE(this->SetCookie(cs, url_ip, "c=3; domain=.3.4")); + this->MatchCookieLines("", this->GetCookies(cs, url_ip)); + // It should be allowed to set a cookie if domain= matches the IP address + // exactly. This matches IE/Firefox, even though it seems a bit wrong. + EXPECT_FALSE(this->SetCookie(cs, url_ip, "b=2; domain=1.2.3.3")); + this->MatchCookieLines("", this->GetCookies(cs, url_ip)); + EXPECT_TRUE(this->SetCookie(cs, url_ip, "b=2; domain=1.2.3.4")); + this->MatchCookieLines("b=2", this->GetCookies(cs, url_ip)); + } +} + +// Test host cookies, and setting of cookies on TLD. +TYPED_TEST_P(CookieStoreTest, TestNonDottedAndTLD) { + { + scoped_refptr<CookieStore> cs(this->GetCookieStore()); + GURL url("http://com/"); + // Allow setting on "com", (but only as a host cookie). + EXPECT_TRUE(this->SetCookie(cs, url, "a=1")); + EXPECT_FALSE(this->SetCookie(cs, url, "b=2; domain=.com")); + EXPECT_FALSE(this->SetCookie(cs, url, "c=3; domain=com")); + this->MatchCookieLines("a=1", this->GetCookies(cs, url)); + // Make sure it doesn't show up for a normal .com, it should be a host + // not a domain cookie. + this->MatchCookieLines("", + this->GetCookies(cs, GURL("http://hopefully-no-cookies.com/"))); + if (TypeParam::supports_non_dotted_domains) { + this->MatchCookieLines("", this->GetCookies(cs, GURL("http://.com/"))); + } + } + + { + // http://com. should be treated the same as http://com. + scoped_refptr<CookieStore> cs(this->GetCookieStore()); + GURL url("http://com./index.html"); + if (TypeParam::supports_trailing_dots) { + EXPECT_TRUE(this->SetCookie(cs, url, "a=1")); + this->MatchCookieLines("a=1", this->GetCookies(cs, url)); + this->MatchCookieLines("", + this->GetCookies(cs, GURL("http://hopefully-no-cookies.com./"))); + } else { + EXPECT_FALSE(this->SetCookie(cs, url, "a=1")); + } + } + + { // Should not be able to set host cookie from a subdomain. + scoped_refptr<CookieStore> cs(this->GetCookieStore()); + GURL url("http://a.b"); + EXPECT_FALSE(this->SetCookie(cs, url, "a=1; domain=.b")); + EXPECT_FALSE(this->SetCookie(cs, url, "b=2; domain=b")); + this->MatchCookieLines("", this->GetCookies(cs, url)); + } + + { // Same test as above, but explicitly on a known TLD (com). + scoped_refptr<CookieStore> cs(this->GetCookieStore()); + GURL url("http://google.com"); + EXPECT_FALSE(this->SetCookie(cs, url, "a=1; domain=.com")); + EXPECT_FALSE(this->SetCookie(cs, url, "b=2; domain=com")); + this->MatchCookieLines("", this->GetCookies(cs, url)); + } + + { // Make sure can't set cookie on TLD which is dotted. + scoped_refptr<CookieStore> cs(this->GetCookieStore()); + GURL url("http://google.co.uk"); + EXPECT_FALSE(this->SetCookie(cs, url, "a=1; domain=.co.uk")); + EXPECT_FALSE(this->SetCookie(cs, url, "b=2; domain=.uk")); + this->MatchCookieLines("", this->GetCookies(cs, url)); + this->MatchCookieLines("", + this->GetCookies(cs, GURL("http://something-else.co.uk"))); + this->MatchCookieLines("", + this->GetCookies(cs, GURL("http://something-else.uk"))); + } + + { // Intranet URLs should only be able to set host cookies. + scoped_refptr<CookieStore> cs(this->GetCookieStore()); + GURL url("http://b"); + EXPECT_TRUE(this->SetCookie(cs, url, "a=1")); + EXPECT_FALSE(this->SetCookie(cs, url, "b=2; domain=.b")); + EXPECT_FALSE(this->SetCookie(cs, url, "c=3; domain=b")); + this->MatchCookieLines("a=1", this->GetCookies(cs, url)); + } +} + +// Test reading/writing cookies when the domain ends with a period, +// as in "www.google.com." +TYPED_TEST_P(CookieStoreTest, TestHostEndsWithDot) { + scoped_refptr<CookieStore> cs(this->GetCookieStore()); + GURL url("http://www.google.com"); + GURL url_with_dot("http://www.google.com."); + EXPECT_TRUE(this->SetCookie(cs, url, "a=1")); + this->MatchCookieLines("a=1", this->GetCookies(cs, url)); + + if (TypeParam::supports_trailing_dots) { + // Do not share cookie space with the dot version of domain. + // Note: this is not what FireFox does, but it _is_ what IE+Safari do. + EXPECT_FALSE(this->SetCookie(cs, url, "b=2; domain=.www.google.com.")); + this->MatchCookieLines("a=1", this->GetCookies(cs, url)); + + EXPECT_TRUE(this->SetCookie(cs, url_with_dot, "b=2; domain=.google.com.")); + this->MatchCookieLines("b=2", this->GetCookies(cs, url_with_dot)); + } else { + EXPECT_TRUE(this->SetCookie(cs, url, "b=2; domain=.www.google.com.")); + EXPECT_FALSE(this->SetCookie(cs, url_with_dot, "b=2; domain=.google.com.")); + } + + // Make sure there weren't any side effects. + this->MatchCookieLines("", + this->GetCookies(cs, GURL("http://hopefully-no-cookies.com/"))); + this->MatchCookieLines("", this->GetCookies(cs, GURL("http://.com/"))); +} + +TYPED_TEST_P(CookieStoreTest, InvalidScheme) { + if (!TypeParam::filters_schemes) + return; + + scoped_refptr<CookieStore> cs(this->GetCookieStore()); + EXPECT_FALSE(this->SetCookie(cs, GURL(kUrlFtp), kValidCookieLine)); +} + +TYPED_TEST_P(CookieStoreTest, InvalidScheme_Read) { + if (!TypeParam::filters_schemes) + return; + + scoped_refptr<CookieStore> cs(this->GetCookieStore()); + EXPECT_TRUE(this->SetCookie(cs, GURL(kUrlGoogle), kValidDomainCookieLine)); + this->MatchCookieLines("", this->GetCookies(cs, GURL(kUrlFtp))); +} + +TYPED_TEST_P(CookieStoreTest, PathTest) { + scoped_refptr<CookieStore> cs(this->GetCookieStore()); + std::string url("http://www.google.izzle"); + EXPECT_TRUE(this->SetCookie(cs, GURL(url), "A=B; path=/wee")); + this->MatchCookieLines("A=B", this->GetCookies(cs, GURL(url + "/wee"))); + this->MatchCookieLines("A=B", this->GetCookies(cs, GURL(url + "/wee/"))); + this->MatchCookieLines("A=B", this->GetCookies(cs, GURL(url + "/wee/war"))); + this->MatchCookieLines("A=B", + this->GetCookies(cs, GURL(url + "/wee/war/more/more"))); + if (!TypeParam::has_path_prefix_bug) + this->MatchCookieLines("", this->GetCookies(cs, GURL(url + "/weehee"))); + this->MatchCookieLines("", this->GetCookies(cs, GURL(url + "/"))); + + // If we add a 0 length path, it should default to / + EXPECT_TRUE(this->SetCookie(cs, GURL(url), "A=C; path=")); + this->MatchCookieLines("A=B; A=C", this->GetCookies(cs, GURL(url + "/wee"))); + this->MatchCookieLines("A=C", this->GetCookies(cs, GURL(url + "/"))); +} + +TYPED_TEST_P(CookieStoreTest, HttpOnlyTest) { + if (!TypeParam::supports_http_only) + return; + + scoped_refptr<CookieStore> cs(this->GetCookieStore()); + CookieOptions options; + options.set_include_httponly(); + + // Create a httponly cookie. + EXPECT_TRUE(this->SetCookieWithOptions(cs, this->url_google_, "A=B; httponly", + options)); + + // Check httponly read protection. + this->MatchCookieLines("", this->GetCookies(cs, this->url_google_)); + this->MatchCookieLines("A=B", + this->GetCookiesWithOptions(cs, this->url_google_, options)); + + // Check httponly overwrite protection. + EXPECT_FALSE(this->SetCookie(cs, this->url_google_, "A=C")); + this->MatchCookieLines("", this->GetCookies(cs, this->url_google_)); + this->MatchCookieLines("A=B", + this->GetCookiesWithOptions(cs, this->url_google_, options)); + EXPECT_TRUE(this->SetCookieWithOptions(cs, this->url_google_, "A=C", + options)); + this->MatchCookieLines("A=C", this->GetCookies(cs, this->url_google_)); + + // Check httponly create protection. + EXPECT_FALSE(this->SetCookie(cs, this->url_google_, "B=A; httponly")); + this->MatchCookieLines("A=C", + this->GetCookiesWithOptions(cs, this->url_google_, options)); + EXPECT_TRUE(this->SetCookieWithOptions(cs, this->url_google_, "B=A; httponly", + options)); + this->MatchCookieLines("A=C; B=A", + this->GetCookiesWithOptions(cs, this->url_google_, options)); + this->MatchCookieLines("A=C", this->GetCookies(cs, this->url_google_)); +} + +TYPED_TEST_P(CookieStoreTest, TestGetCookiesWithInfo) { + if (!TypeParam::supports_cookies_with_info) + return; + + scoped_refptr<CookieStore> cs(this->GetCookieStore()); + CookieOptions options; + + EXPECT_TRUE(this->SetCookieWithOptions(cs, this->url_google_, "A=B", + options)); + EXPECT_TRUE(this->SetCookieWithOptions(cs, this->url_google_, + "C=D; Mac-Key=390jfn0awf3; Mac-Algorithm=hmac-sha-1", options)); + + this->MatchCookieLines("A=B; C=D", + this->GetCookiesWithOptions(cs, this->url_google_, options)); + + std::string cookie_line; + std::vector<CookieStore::CookieInfo> cookie_infos; + + this->GetCookiesWithInfo(cs, this->url_google_, options, &cookie_line, + &cookie_infos); + + EXPECT_EQ("A=B; C=D", cookie_line); + + EXPECT_EQ(2U, cookie_infos.size()); + + EXPECT_EQ("A", cookie_infos[0].name); + EXPECT_EQ("", cookie_infos[0].mac_key); + EXPECT_EQ("", cookie_infos[0].mac_algorithm); + + EXPECT_EQ("C", cookie_infos[1].name); + EXPECT_EQ("390jfn0awf3", cookie_infos[1].mac_key); + EXPECT_EQ("hmac-sha-1", cookie_infos[1].mac_algorithm); +} + +TYPED_TEST_P(CookieStoreTest, TestCookieDeletion) { + scoped_refptr<CookieStore> cs(this->GetCookieStore()); + + // Create a session cookie. + EXPECT_TRUE(this->SetCookie(cs, this->url_google_, kValidCookieLine)); + this->MatchCookieLines("A=B", this->GetCookies(cs, this->url_google_)); + // Delete it via Max-Age. + EXPECT_TRUE(this->SetCookie(cs, this->url_google_, + std::string(kValidCookieLine) + "; max-age=0")); + this->MatchCookieLineWithTimeout(cs, this->url_google_, ""); + + // Create a session cookie. + EXPECT_TRUE(this->SetCookie(cs, this->url_google_, kValidCookieLine)); + this->MatchCookieLines("A=B", this->GetCookies(cs, this->url_google_)); + // Delete it via Expires. + EXPECT_TRUE(this->SetCookie(cs, this->url_google_, + std::string(kValidCookieLine) + + "; expires=Mon, 18-Apr-1977 22:50:13 GMT")); + this->MatchCookieLines("", this->GetCookies(cs, this->url_google_)); + + // Create a persistent cookie. + EXPECT_TRUE(this->SetCookie(cs, this->url_google_, + std::string(kValidCookieLine) + + "; expires=Mon, 18-Apr-22 22:50:13 GMT")); + + this->MatchCookieLines("A=B", this->GetCookies(cs, this->url_google_)); + // Delete it via Max-Age. + EXPECT_TRUE(this->SetCookie(cs, this->url_google_, + std::string(kValidCookieLine) + "; max-age=0")); + this->MatchCookieLineWithTimeout(cs, this->url_google_, ""); + + // Create a persistent cookie. + EXPECT_TRUE(this->SetCookie(cs, this->url_google_, + std::string(kValidCookieLine) + + "; expires=Mon, 18-Apr-22 22:50:13 GMT")); + this->MatchCookieLines("A=B", this->GetCookies(cs, this->url_google_)); + // Delete it via Expires. + EXPECT_TRUE(this->SetCookie(cs, this->url_google_, + std::string(kValidCookieLine) + + "; expires=Mon, 18-Apr-1977 22:50:13 GMT")); + this->MatchCookieLines("", this->GetCookies(cs, this->url_google_)); + + // Create a persistent cookie. + EXPECT_TRUE(this->SetCookie(cs, this->url_google_, + std::string(kValidCookieLine) + + "; expires=Mon, 18-Apr-22 22:50:13 GMT")); + this->MatchCookieLines("A=B", this->GetCookies(cs, this->url_google_)); + // Delete it via Expires, with a unix epoch of 0. + EXPECT_TRUE(this->SetCookie(cs, this->url_google_, + std::string(kValidCookieLine) + + "; expires=Thu, 1-Jan-1970 00:00:00 GMT")); + this->MatchCookieLines("", this->GetCookies(cs, this->url_google_)); +} + +TYPED_TEST_P(CookieStoreTest, TestDeleteAllCreatedBetween) { + scoped_refptr<CookieStore> cs(this->GetCookieStore()); + const base::Time last_month = base::Time::Now() - + base::TimeDelta::FromDays(30); + const base::Time last_minute = base::Time::Now() - + base::TimeDelta::FromMinutes(1); + const base::Time next_minute = base::Time::Now() + + base::TimeDelta::FromMinutes(1); + const base::Time next_month = base::Time::Now() + + base::TimeDelta::FromDays(30); + + // Add a cookie. + EXPECT_TRUE(this->SetCookie(cs, this->url_google_, "A=B")); + // Check that the cookie is in the store. + this->MatchCookieLines("A=B", this->GetCookies(cs, this->url_google_)); + + // Remove cookies in empty intervals. + EXPECT_EQ(0, this->DeleteCreatedBetween(cs, last_month, last_minute)); + EXPECT_EQ(0, this->DeleteCreatedBetween(cs, next_minute, next_month)); + // Check that the cookie is still there. + this->MatchCookieLines("A=B", this->GetCookies(cs, this->url_google_)); + + // Remove the cookie with an interval defined by two dates. + EXPECT_EQ(1, this->DeleteCreatedBetween(cs, last_minute, next_minute)); + // Check that the cookie disappeared. + this->MatchCookieLines("", this->GetCookies(cs, this->url_google_)); + + // Add another cookie. + EXPECT_TRUE(this->SetCookie(cs, this->url_google_, "C=D")); + // Check that the cookie is in the store. + this->MatchCookieLines("C=D", this->GetCookies(cs, this->url_google_)); + + // Remove the cookie with a null ending time. + EXPECT_EQ(1, this->DeleteCreatedBetween(cs, last_minute, base::Time())); + // Check that the cookie disappeared. + this->MatchCookieLines("", this->GetCookies(cs, this->url_google_)); +} + +TYPED_TEST_P(CookieStoreTest, TestSecure) { + scoped_refptr<CookieStore> cs(this->GetCookieStore()); + + EXPECT_TRUE(this->SetCookie(cs, this->url_google_, "A=B")); + this->MatchCookieLines("A=B", this->GetCookies(cs, this->url_google_)); + this->MatchCookieLines("A=B", this->GetCookies(cs, this->url_google_secure_)); + + EXPECT_TRUE(this->SetCookie(cs, this->url_google_secure_, "A=B; secure")); + // The secure should overwrite the non-secure. + this->MatchCookieLines("", this->GetCookies(cs, this->url_google_)); + this->MatchCookieLines("A=B", this->GetCookies(cs, this->url_google_secure_)); + + EXPECT_TRUE(this->SetCookie(cs, this->url_google_secure_, "D=E; secure")); + this->MatchCookieLines("", this->GetCookies(cs, this->url_google_)); + this->MatchCookieLines("A=B; D=E", + this->GetCookies(cs, this->url_google_secure_)); + + EXPECT_TRUE(this->SetCookie(cs, this->url_google_secure_, "A=B")); + // The non-secure should overwrite the secure. + this->MatchCookieLines("A=B", this->GetCookies(cs, this->url_google_)); + this->MatchCookieLines("D=E; A=B", + this->GetCookies(cs, this->url_google_secure_)); +} + +static const int kLastAccessThresholdMilliseconds = 200; + +// Formerly NetUtilTest.CookieTest back when we used wininet's cookie handling. +TYPED_TEST_P(CookieStoreTest, NetUtilCookieTest) { + const GURL test_url("http://mojo.jojo.google.izzle/"); + + scoped_refptr<CookieStore> cs(this->GetCookieStore()); + + EXPECT_TRUE(this->SetCookie(cs, test_url, "foo=bar")); + std::string value = this->GetCookies(cs, test_url); + this->MatchCookieLines("foo=bar", value); + + // test that we can retrieve all cookies: + EXPECT_TRUE(this->SetCookie(cs, test_url, "x=1")); + EXPECT_TRUE(this->SetCookie(cs, test_url, "y=2")); + + std::string result = this->GetCookies(cs, test_url); + EXPECT_FALSE(result.empty()); + EXPECT_NE(result.find("x=1"), std::string::npos) << result; + EXPECT_NE(result.find("y=2"), std::string::npos) << result; +} + +TYPED_TEST_P(CookieStoreTest, OverwritePersistentCookie) { + GURL url_google("http://www.google.com/"); + GURL url_chromium("http://chromium.org"); + scoped_refptr<CookieStore> cs(this->GetCookieStore()); + + // Insert a cookie "a" for path "/path1" + EXPECT_TRUE( + this->SetCookie(cs, url_google, "a=val1; path=/path1; " + "expires=Mon, 18-Apr-22 22:50:13 GMT")); + + // Insert a cookie "b" for path "/path1" + EXPECT_TRUE( + this->SetCookie(cs, url_google, "b=val1; path=/path1; " + "expires=Mon, 18-Apr-22 22:50:14 GMT")); + + // Insert a cookie "b" for path "/path1", that is httponly. This should + // overwrite the non-http-only version. + CookieOptions allow_httponly; + allow_httponly.set_include_httponly(); + EXPECT_TRUE( + this->SetCookieWithOptions(cs, url_google, + "b=val2; path=/path1; httponly; " + "expires=Mon, 18-Apr-22 22:50:14 GMT", + allow_httponly)); + + // Insert a cookie "a" for path "/path1". This should overwrite. + EXPECT_TRUE(this->SetCookie(cs, url_google, + "a=val33; path=/path1; " + "expires=Mon, 18-Apr-22 22:50:14 GMT")); + + // Insert a cookie "a" for path "/path2". This should NOT overwrite + // cookie "a", since the path is different. + EXPECT_TRUE(this->SetCookie(cs, url_google, + "a=val9; path=/path2; " + "expires=Mon, 18-Apr-22 22:50:14 GMT")); + + // Insert a cookie "a" for path "/path1", but this time for "chromium.org". + // Although the name and path match, the hostnames do not, so shouldn't + // overwrite. + EXPECT_TRUE(this->SetCookie(cs, url_chromium, + "a=val99; path=/path1; " + "expires=Mon, 18-Apr-22 22:50:14 GMT")); + + if (TypeParam::supports_http_only) { + this->MatchCookieLines("a=val33", + this->GetCookies(cs, GURL("http://www.google.com/path1"))); + } else { + this->MatchCookieLines("a=val33; b=val2", + this->GetCookies(cs, GURL("http://www.google.com/path1"))); + } + this->MatchCookieLines("a=val9", + this->GetCookies(cs, GURL("http://www.google.com/path2"))); + this->MatchCookieLines("a=val99", + this->GetCookies(cs, GURL("http://chromium.org/path1"))); +} + +TYPED_TEST_P(CookieStoreTest, CookieOrdering) { + // Put a random set of cookies into a store and make sure they're returned in + // the right order. + // Cookies should be sorted by path length and creation time, as per RFC6265. + scoped_refptr<CookieStore> cs(this->GetCookieStore()); + EXPECT_TRUE(this->SetCookie(cs, GURL("http://d.c.b.a.google.com/aa/x.html"), + "c=1")); + EXPECT_TRUE(this->SetCookie(cs, GURL("http://b.a.google.com/aa/bb/cc/x.html"), + "d=1; domain=b.a.google.com")); + base::PlatformThread::Sleep(base::TimeDelta::FromMilliseconds( + TypeParam::creation_time_granularity_in_ms)); + EXPECT_TRUE(this->SetCookie(cs, GURL("http://b.a.google.com/aa/bb/cc/x.html"), + "a=4; domain=b.a.google.com")); + base::PlatformThread::Sleep(base::TimeDelta::FromMilliseconds( + TypeParam::creation_time_granularity_in_ms)); + EXPECT_TRUE(this->SetCookie(cs, + GURL("http://c.b.a.google.com/aa/bb/cc/x.html"), + "e=1; domain=c.b.a.google.com")); + EXPECT_TRUE(this->SetCookie(cs, + GURL("http://d.c.b.a.google.com/aa/bb/x.html"), + "b=1")); + EXPECT_TRUE(this->SetCookie(cs, GURL("http://news.bbc.co.uk/midpath/x.html"), + "g=10")); + EXPECT_EQ("d=1; a=4; e=1; b=1; c=1", + this->GetCookies(cs, GURL("http://d.c.b.a.google.com/aa/bb/cc/dd"))); +} + +REGISTER_TYPED_TEST_CASE_P(CookieStoreTest, + TypeTest, DomainTest, DomainWithTrailingDotTest, ValidSubdomainTest, + InvalidDomainTest, DomainWithoutLeadingDotTest, CaseInsensitiveDomainTest, + TestIpAddress, TestNonDottedAndTLD, TestHostEndsWithDot, InvalidScheme, + InvalidScheme_Read, PathTest, HttpOnlyTest, TestGetCookiesWithInfo, + TestCookieDeletion, TestDeleteAllCreatedBetween, TestSecure, + NetUtilCookieTest, OverwritePersistentCookie, CookieOrdering); + +template<class CookieStoreTestTraits> +class MultiThreadedCookieStoreTest : + public CookieStoreTest<CookieStoreTestTraits> { + public: + MultiThreadedCookieStoreTest() : other_thread_("CMTthread") {} + + // Helper methods for calling the asynchronous CookieStore methods + // from a different thread. + + void GetCookiesTask(CookieStore* cs, + const GURL& url, + GetCookieStringCallback* callback) { + CookieOptions options; + if (!CookieStoreTestTraits::supports_http_only) + options.set_include_httponly(); + cs->GetCookiesWithOptionsAsync( + url, options, + base::Bind(&GetCookieStringCallback::Run, base::Unretained(callback))); + } + + void GetCookiesWithOptionsTask(CookieStore* cs, + const GURL& url, + const CookieOptions& options, + GetCookieStringCallback* callback) { + cs->GetCookiesWithOptionsAsync( + url, options, + base::Bind(&GetCookieStringCallback::Run, base::Unretained(callback))); + } + + void GetCookiesWithInfoTask(CookieStore* cs, + const GURL& url, + const CookieOptions& options, + GetCookiesWithInfoCallback* callback) { + cs->GetCookiesWithInfoAsync( + url, options, + base::Bind(&GetCookiesWithInfoCallback::Run, + base::Unretained(callback))); + } + + void SetCookieWithOptionsTask(CookieStore* cs, + const GURL& url, + const std::string& cookie_line, + const CookieOptions& options, + SetCookieCallback* callback) { + cs->SetCookieWithOptionsAsync( + url, cookie_line, options, + base::Bind(&SetCookieCallback::Run, base::Unretained(callback))); + } + + void DeleteCookieTask(CookieStore* cs, + const GURL& url, + const std::string& cookie_name, + DeleteCookieCallback* callback) { + cs->DeleteCookieAsync( + url, cookie_name, + base::Bind(&DeleteCookieCallback::Run, base::Unretained(callback))); + } + + protected: + void RunOnOtherThread(const base::Closure& task) { + other_thread_.Start(); + other_thread_.message_loop()->PostTask(FROM_HERE, task); + CookieStoreTest<CookieStoreTestTraits>::RunFor(kTimeout); + other_thread_.Stop(); + } + + Thread other_thread_; +}; + +TYPED_TEST_CASE_P(MultiThreadedCookieStoreTest); + +// TODO(ycxiao): Eventually, we will need to create a separate thread, create +// the cookie store on that thread (or at least its store, i.e., the DB +// thread). +TYPED_TEST_P(MultiThreadedCookieStoreTest, ThreadCheckGetCookies) { + scoped_refptr<CookieStore> cs(this->GetCookieStore()); + EXPECT_TRUE(this->SetCookie(cs, this->url_google_, "A=B")); + this->MatchCookieLines("A=B", this->GetCookies(cs, this->url_google_)); + GetCookieStringCallback callback(&this->other_thread_); + base::Closure task = base::Bind( + &net::MultiThreadedCookieStoreTest<TypeParam>::GetCookiesTask, + base::Unretained(this), + cs, this->url_google_, &callback); + this->RunOnOtherThread(task); + EXPECT_TRUE(callback.did_run()); + EXPECT_EQ("A=B", callback.cookie()); +} + +TYPED_TEST_P(MultiThreadedCookieStoreTest, ThreadCheckGetCookiesWithOptions) { + scoped_refptr<CookieStore> cs(this->GetCookieStore()); + CookieOptions options; + if (!TypeParam::supports_http_only) + options.set_include_httponly(); + EXPECT_TRUE(this->SetCookie(cs, this->url_google_, "A=B")); + this->MatchCookieLines("A=B", + this->GetCookiesWithOptions(cs, this->url_google_, options)); + GetCookieStringCallback callback(&this->other_thread_); + base::Closure task = base::Bind( + &net::MultiThreadedCookieStoreTest<TypeParam>::GetCookiesWithOptionsTask, + base::Unretained(this), + cs, this->url_google_, options, &callback); + this->RunOnOtherThread(task); + EXPECT_TRUE(callback.did_run()); + EXPECT_EQ("A=B", callback.cookie()); +} + +TYPED_TEST_P(MultiThreadedCookieStoreTest, ThreadCheckGetCookiesWithInfo) { + if (!TypeParam::supports_cookies_with_info) + return; + scoped_refptr<CookieStore> cs(this->GetCookieStore()); + CookieOptions options; + std::string cookie_line; + std::vector<CookieStore::CookieInfo> cookie_infos; + EXPECT_TRUE(this->SetCookie(cs, this->url_google_, "A=B")); + this->GetCookiesWithInfo(cs, this->url_google_, options, &cookie_line, + &cookie_infos); + this->MatchCookieLines("A=B", cookie_line); + EXPECT_EQ(1U, cookie_infos.size()); + EXPECT_EQ("A", cookie_infos[0].name); + EXPECT_EQ("", cookie_infos[0].mac_key); + EXPECT_EQ("", cookie_infos[0].mac_algorithm); + GetCookiesWithInfoCallback callback(&this->other_thread_); + base::Closure task = base::Bind( + &net::MultiThreadedCookieStoreTest<TypeParam>::GetCookiesWithInfoTask, + base::Unretained(this), cs, this->url_google_, options, &callback); + this->RunOnOtherThread(task); + EXPECT_TRUE(callback.did_run()); + this->MatchCookieLines("A=B", callback.cookie_line()); + EXPECT_EQ(1U, callback.cookie_info().size()); + EXPECT_EQ("A", callback.cookie_info()[0].name); + EXPECT_EQ("", callback.cookie_info()[0].mac_key); + EXPECT_EQ("", callback.cookie_info()[0].mac_algorithm); +} + +TYPED_TEST_P(MultiThreadedCookieStoreTest, ThreadCheckSetCookieWithOptions) { + scoped_refptr<CookieStore> cs(this->GetCookieStore()); + CookieOptions options; + if (!TypeParam::supports_http_only) + options.set_include_httponly(); + EXPECT_TRUE(this->SetCookieWithOptions(cs, this->url_google_, "A=B", + options)); + SetCookieCallback callback(&this->other_thread_); + base::Closure task = base::Bind( + &net::MultiThreadedCookieStoreTest<TypeParam>::SetCookieWithOptionsTask, + base::Unretained(this), + cs, this->url_google_, "A=B", options, &callback); + this->RunOnOtherThread(task); + EXPECT_TRUE(callback.did_run()); + EXPECT_TRUE(callback.result()); +} + +TYPED_TEST_P(MultiThreadedCookieStoreTest, ThreadCheckDeleteCookie) { + scoped_refptr<CookieStore> cs(this->GetCookieStore()); + CookieOptions options; + if (!TypeParam::supports_http_only) + options.set_include_httponly(); + EXPECT_TRUE(this->SetCookieWithOptions(cs, this->url_google_, "A=B", + options)); + this->DeleteCookie(cs, this->url_google_, "A"); + EXPECT_TRUE(this->SetCookieWithOptions(cs, this->url_google_, "A=B", + options)); + DeleteCookieCallback callback(&this->other_thread_); + base::Closure task = base::Bind( + &net::MultiThreadedCookieStoreTest<TypeParam>::DeleteCookieTask, + base::Unretained(this), + cs, this->url_google_, "A", &callback); + this->RunOnOtherThread(task); + EXPECT_TRUE(callback.did_run()); +} + +REGISTER_TYPED_TEST_CASE_P(MultiThreadedCookieStoreTest, + ThreadCheckGetCookies, ThreadCheckGetCookiesWithOptions, + ThreadCheckGetCookiesWithInfo, ThreadCheckSetCookieWithOptions, + ThreadCheckDeleteCookie); + +} // namespace net + +#endif // NET_COOKIES_COOKIE_STORE_UNITTEST_H_ diff --git a/net/base/cookie_util.cc b/net/cookies/cookie_util.cc index 8d8a0c3..6e1833f 100644 --- a/net/base/cookie_util.cc +++ b/net/cookies/cookie_util.cc @@ -1,8 +1,8 @@ -// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// 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 "net/base/cookie_util.h" +#include "net/cookies/cookie_util.h" #include "base/logging.h" #include "googleurl/src/gurl.h" diff --git a/net/cookies/cookie_util.h b/net/cookies/cookie_util.h new file mode 100644 index 0000000..4c6e72f --- /dev/null +++ b/net/cookies/cookie_util.h @@ -0,0 +1,40 @@ +// 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. + +#ifndef NET_COOKIES_COOKIE_UTIL_H_ +#define NET_COOKIES_COOKIE_UTIL_H_ +#pragma once + +#include <string> + +#include "net/base/net_export.h" + +class GURL; + +namespace net { +namespace cookie_util { + +// Returns the effective TLD+1 for a given host. This only makes sense for http +// and https schemes. For other schemes, the host will be returned unchanged +// (minus any leading period). +NET_EXPORT std::string GetEffectiveDomain(const std::string& scheme, + const std::string& host); + +// Determine the actual cookie domain based on the domain string passed +// (if any) and the URL from which the cookie came. +// On success returns true, and sets cookie_domain to either a +// -host cookie domain (ex: "google.com") +// -domain cookie domain (ex: ".google.com") +NET_EXPORT bool GetCookieDomainWithString(const GURL& url, + const std::string& domain_string, + std::string* result); + +// Returns true if a domain string represents a host-only cookie, +// i.e. it doesn't begin with a leading '.' character. +NET_EXPORT bool DomainIsHostOnly(const std::string& domain_string); + +} // namspace cookie_util +} // namespace net + +#endif // NET_COOKIES_COOKIE_UTIL_H_ diff --git a/net/base/cookie_util_unittest.cc b/net/cookies/cookie_util_unittest.cc index 4c3db80..5d2a420 100644 --- a/net/base/cookie_util_unittest.cc +++ b/net/cookies/cookie_util_unittest.cc @@ -3,7 +3,7 @@ // found in the LICENSE file. #include "base/basictypes.h" -#include "net/base/cookie_util.h" +#include "net/cookies/cookie_util.h" #include "testing/gtest/include/gtest/gtest.h" TEST(CookieUtilTest, TestDomainIsHostOnly) { diff --git a/net/net.gyp b/net/net.gyp index 1087bfe..13b2b8c 100644 --- a/net/net.gyp +++ b/net/net.gyp @@ -75,13 +75,6 @@ 'base/completion_callback.h', 'base/connection_type_histograms.cc', 'base/connection_type_histograms.h', - 'base/cookie_monster.cc', - 'base/cookie_monster.h', - 'base/cookie_options.h', - 'base/cookie_store.cc', - 'base/cookie_store.h', - 'base/cookie_util.cc', - 'base/cookie_util.h', 'base/crl_set.cc', 'base/crl_set.h', 'base/crypto_module.h', @@ -276,6 +269,13 @@ 'base/x509_util_openssl.h', 'base/zap.cc', 'base/zap.h', + 'cookies/cookie_monster.cc', + 'cookies/cookie_monster.h', + 'cookies/cookie_options.h', + 'cookies/cookie_store.cc', + 'cookies/cookie_store.h', + 'cookies/cookie_util.cc', + 'cookies/cookie_util.h', 'disk_cache/addr.cc', 'disk_cache/addr.h', 'disk_cache/backend_impl.cc', @@ -1023,9 +1023,6 @@ 'base/big_endian_unittest.cc', 'base/cert_database_nss_unittest.cc', 'base/cert_verifier_unittest.cc', - 'base/cookie_monster_unittest.cc', - 'base/cookie_store_unittest.h', - 'base/cookie_util_unittest.cc', 'base/crl_set_unittest.cc', 'base/data_url_unittest.cc', 'base/default_origin_bound_cert_store_unittest.cc', @@ -1079,6 +1076,9 @@ 'base/x509_cert_types_unittest.cc', 'base/x509_util_nss_unittest.cc', 'base/x509_util_openssl_unittest.cc', + 'cookies/cookie_monster_unittest.cc', + 'cookies/cookie_store_unittest.h', + 'cookies/cookie_util_unittest.cc', 'disk_cache/addr_unittest.cc', 'disk_cache/backend_unittest.cc', 'disk_cache/bitmap_unittest.cc', @@ -1428,7 +1428,7 @@ '../testing/gtest.gyp:gtest', ], 'sources': [ - 'base/cookie_monster_perftest.cc', + 'cookies/cookie_monster_perftest.cc', 'disk_cache/disk_cache_perftest.cc', 'proxy/proxy_resolver_perftest.cc', ], @@ -1510,12 +1510,6 @@ 'sources': [ 'base/cert_test_util.cc', 'base/cert_test_util.h', - 'base/cookie_monster_store_test.cc', - 'base/cookie_monster_store_test.h', - 'base/cookie_store_test_callbacks.cc', - 'base/cookie_store_test_callbacks.h', - 'base/cookie_store_test_helpers.cc', - 'base/cookie_store_test_helpers.h', 'base/mock_file_stream.cc', 'base/mock_file_stream.h', 'base/mock_host_resolver.cc', @@ -1524,6 +1518,12 @@ 'base/net_test_suite.h', 'base/test_completion_callback.cc', 'base/test_completion_callback.h', + 'cookies/cookie_monster_store_test.cc', + 'cookies/cookie_monster_store_test.h', + 'cookies/cookie_store_test_callbacks.cc', + 'cookies/cookie_store_test_callbacks.h', + 'cookies/cookie_store_test_helpers.cc', + 'cookies/cookie_store_test_helpers.h', 'disk_cache/disk_cache_test_base.cc', 'disk_cache/disk_cache_test_base.h', 'disk_cache/disk_cache_test_util.cc', |