summaryrefslogtreecommitdiffstats
path: root/chrome/browser/browsing_data
diff options
context:
space:
mode:
authorerikchen@chromium.org <erikchen@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2014-08-18 22:38:25 +0000
committererikchen@chromium.org <erikchen@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2014-08-18 22:40:02 +0000
commit85062ca24ea4d3fb2537f60d9af3362fadaf2ae5 (patch)
treef4e1ae661e603c7a847a7b645769aa7b51626cfc /chrome/browser/browsing_data
parent7ae529040707533c856dbb392a0332b2a4457959 (diff)
downloadchromium_src-85062ca24ea4d3fb2537f60d9af3362fadaf2ae5.zip
chromium_src-85062ca24ea4d3fb2537f60d9af3362fadaf2ae5.tar.gz
chromium_src-85062ca24ea4d3fb2537f60d9af3362fadaf2ae5.tar.bz2
Store in memory cookies in a hash set instead of a list.
The old code was performing expensive string searches against cookies stored in a list. See the bug for Instruments/DTrace results of the performance benefits. BUG=401629 Review URL: https://codereview.chromium.org/454623003 Cr-Commit-Position: refs/heads/master@{#290383} git-svn-id: svn://svn.chromium.org/chrome/trunk/src@290383 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome/browser/browsing_data')
-rw-r--r--chrome/browser/browsing_data/browsing_data_cookie_helper.cc84
-rw-r--r--chrome/browser/browsing_data/browsing_data_cookie_helper.h21
-rw-r--r--chrome/browser/browsing_data/browsing_data_cookie_helper_unittest.cc227
-rw-r--r--chrome/browser/browsing_data/canonical_cookie_hash.cc18
-rw-r--r--chrome/browser/browsing_data/canonical_cookie_hash.h86
5 files changed, 282 insertions, 154 deletions
diff --git a/chrome/browser/browsing_data/browsing_data_cookie_helper.cc b/chrome/browser/browsing_data/browsing_data_cookie_helper.cc
index 605c85c..acef30b 100644
--- a/chrome/browser/browsing_data/browsing_data_cookie_helper.cc
+++ b/chrome/browser/browsing_data/browsing_data_cookie_helper.cc
@@ -23,7 +23,7 @@
using content::BrowserThread;
namespace {
-const char kGlobalCookieListURL[] = "chrome://cookielist";
+const char kGlobalCookieSetURL[] = "chrome://cookieset";
}
BrowsingDataCookieHelper::BrowsingDataCookieHelper(
@@ -113,11 +113,11 @@ CannedBrowsingDataCookieHelper* CannedBrowsingDataCookieHelper::Clone() {
CannedBrowsingDataCookieHelper* clone =
new CannedBrowsingDataCookieHelper(request_context_getter());
- for (OriginCookieListMap::iterator it = origin_cookie_list_map_.begin();
- it != origin_cookie_list_map_.end();
+ for (OriginCookieSetMap::iterator it = origin_cookie_set_map_.begin();
+ it != origin_cookie_set_map_.end();
++it) {
- net::CookieList* cookies = clone->GetCookiesFor(it->first);
- cookies->insert(cookies->begin(), it->second->begin(), it->second->end());
+ canonical_cookie::CookieHashSet* cookies = clone->GetCookiesFor(it->first);
+ cookies->insert(it->second->begin(), it->second->end());
}
return clone;
}
@@ -145,15 +145,14 @@ void CannedBrowsingDataCookieHelper::AddChangedCookie(
}
void CannedBrowsingDataCookieHelper::Reset() {
- STLDeleteContainerPairSecondPointers(origin_cookie_list_map_.begin(),
- origin_cookie_list_map_.end());
- origin_cookie_list_map_.clear();
+ STLDeleteContainerPairSecondPointers(origin_cookie_set_map_.begin(),
+ origin_cookie_set_map_.end());
+ origin_cookie_set_map_.clear();
}
bool CannedBrowsingDataCookieHelper::empty() const {
- for (OriginCookieListMap::const_iterator it =
- origin_cookie_list_map_.begin();
- it != origin_cookie_list_map_.end();
+ for (OriginCookieSetMap::const_iterator it = origin_cookie_set_map_.begin();
+ it != origin_cookie_set_map_.end();
++it) {
if (!it->second->empty())
return false;
@@ -164,8 +163,8 @@ bool CannedBrowsingDataCookieHelper::empty() const {
size_t CannedBrowsingDataCookieHelper::GetCookieCount() const {
size_t count = 0;
- for (OriginCookieListMap::const_iterator it = origin_cookie_list_map_.begin();
- it != origin_cookie_list_map_.end();
+ for (OriginCookieSetMap::const_iterator it = origin_cookie_set_map_.begin();
+ it != origin_cookie_set_map_.end();
++it) {
count += it->second->size();
}
@@ -177,8 +176,8 @@ void CannedBrowsingDataCookieHelper::StartFetching(
const net::CookieMonster::GetCookieListCallback& callback) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
net::CookieList cookie_list;
- for (OriginCookieListMap::iterator it = origin_cookie_list_map_.begin();
- it != origin_cookie_list_map_.end();
+ for (OriginCookieSetMap::iterator it = origin_cookie_set_map_.begin();
+ it != origin_cookie_set_map_.end();
++it) {
cookie_list.insert(cookie_list.begin(),
it->second->begin(),
@@ -189,8 +188,8 @@ void CannedBrowsingDataCookieHelper::StartFetching(
void CannedBrowsingDataCookieHelper::DeleteCookie(
const net::CanonicalCookie& cookie) {
- for (OriginCookieListMap::iterator it = origin_cookie_list_map_.begin();
- it != origin_cookie_list_map_.end();
+ for (OriginCookieSetMap::iterator it = origin_cookie_set_map_.begin();
+ it != origin_cookie_set_map_.end();
++it) {
DeleteMatchingCookie(cookie, it->second);
}
@@ -199,28 +198,20 @@ void CannedBrowsingDataCookieHelper::DeleteCookie(
bool CannedBrowsingDataCookieHelper::DeleteMatchingCookie(
const net::CanonicalCookie& add_cookie,
- net::CookieList* cookie_list) {
- typedef net::CookieList::iterator cookie_iterator;
- for (cookie_iterator cookie = cookie_list->begin();
- cookie != cookie_list->end(); ++cookie) {
- if (cookie->Name() == add_cookie.Name() &&
- cookie->Domain() == add_cookie.Domain() &&
- cookie->Path() == add_cookie.Path()) {
- cookie_list->erase(cookie);
- return true;
- }
- }
- return false;
+ canonical_cookie::CookieHashSet* cookie_set) {
+ return cookie_set->erase(add_cookie) > 0;
}
-net::CookieList* CannedBrowsingDataCookieHelper::GetCookiesFor(
+canonical_cookie::CookieHashSet* CannedBrowsingDataCookieHelper::GetCookiesFor(
const GURL& first_party_origin) {
- OriginCookieListMap::iterator it =
- origin_cookie_list_map_.find(first_party_origin);
- if (it == origin_cookie_list_map_.end()) {
- net::CookieList* cookies = new net::CookieList();
- origin_cookie_list_map_.insert(
- std::pair<GURL, net::CookieList*>(first_party_origin, cookies));
+ OriginCookieSetMap::iterator it =
+ origin_cookie_set_map_.find(first_party_origin);
+ if (it == origin_cookie_set_map_.end()) {
+ canonical_cookie::CookieHashSet* cookies =
+ new canonical_cookie::CookieHashSet;
+ origin_cookie_set_map_.insert(
+ std::pair<GURL, canonical_cookie::CookieHashSet*>(first_party_origin,
+ cookies));
return cookies;
}
return it->second;
@@ -229,23 +220,24 @@ net::CookieList* CannedBrowsingDataCookieHelper::GetCookiesFor(
void CannedBrowsingDataCookieHelper::AddCookie(
const GURL& frame_url,
const net::CanonicalCookie& cookie) {
- // Storing cookies in separate cookie lists per frame origin makes the
+ // Storing cookies in separate cookie sets per frame origin makes the
// GetCookieCount method count a cookie multiple times if it is stored in
- // multiple lists.
+ // multiple sets.
// E.g. let "example.com" be redirected to "www.example.com". A cookie set
// with the cookie string "A=B; Domain=.example.com" would be sent to both
- // hosts. This means it would be stored in the separate cookie lists for both
+ // hosts. This means it would be stored in the separate cookie sets for both
// hosts ("example.com", "www.example.com"). The method GetCookieCount would
// count this cookie twice. To prevent this, we us a single global cookie
- // list as a work-around to store all added cookies. Per frame URL cookie
- // lists are currently not used. In the future they will be used for
+ // set as a work-around to store all added cookies. Per frame URL cookie
+ // sets are currently not used. In the future they will be used for
// collecting cookies per origin in redirect chains.
// TODO(markusheintz): A) Change the GetCookiesCount method to prevent
// counting cookies multiple times if they are stored in multiple cookie
- // lists. B) Replace the GetCookieFor method call below with:
+ // sets. B) Replace the GetCookieFor method call below with:
// "GetCookiesFor(frame_url.GetOrigin());"
- net::CookieList* cookie_list =
- GetCookiesFor(GURL(kGlobalCookieListURL));
- DeleteMatchingCookie(cookie, cookie_list);
- cookie_list->push_back(cookie);
+ CR_DEFINE_STATIC_LOCAL(const GURL, origin_cookie_url, (kGlobalCookieSetURL));
+ canonical_cookie::CookieHashSet* cookie_set =
+ GetCookiesFor(origin_cookie_url);
+ DeleteMatchingCookie(cookie, cookie_set);
+ cookie_set->insert(cookie);
}
diff --git a/chrome/browser/browsing_data/browsing_data_cookie_helper.h b/chrome/browser/browsing_data/browsing_data_cookie_helper.h
index cefd2b5..7c100f3 100644
--- a/chrome/browser/browsing_data/browsing_data_cookie_helper.h
+++ b/chrome/browser/browsing_data/browsing_data_cookie_helper.h
@@ -11,6 +11,7 @@
#include "base/basictypes.h"
#include "base/callback.h"
#include "base/memory/ref_counted.h"
+#include "chrome/browser/browsing_data/canonical_cookie_hash.h"
#include "net/cookies/cookie_monster.h"
class GURL;
@@ -90,7 +91,7 @@ class BrowsingDataCookieHelper
// cookies.
class CannedBrowsingDataCookieHelper : public BrowsingDataCookieHelper {
public:
- typedef std::map<GURL, net::CookieList*> OriginCookieListMap;
+ typedef std::map<GURL, canonical_cookie::CookieHashSet*> OriginCookieSetMap;
explicit CannedBrowsingDataCookieHelper(
net::URLRequestContextGetter* request_context);
@@ -138,28 +139,28 @@ class CannedBrowsingDataCookieHelper : public BrowsingDataCookieHelper {
size_t GetCookieCount() const;
// Returns the map that contains the cookie lists for all frame urls.
- const OriginCookieListMap& origin_cookie_list_map() {
- return origin_cookie_list_map_;
+ const OriginCookieSetMap& origin_cookie_set_map() {
+ return origin_cookie_set_map_;
}
private:
- // Check if the cookie list contains a cookie with the same name,
+ // Check if the cookie set contains a cookie with the same name,
// domain, and path as the newly created cookie. Delete the old cookie
// if does.
bool DeleteMatchingCookie(const net::CanonicalCookie& add_cookie,
- net::CookieList* cookie_list);
+ canonical_cookie::CookieHashSet* cookie_set);
virtual ~CannedBrowsingDataCookieHelper();
- // Returns the |CookieList| for the given |origin|.
- net::CookieList* GetCookiesFor(const GURL& origin);
+ // Returns the |CookieSet| for the given |origin|.
+ canonical_cookie::CookieHashSet* GetCookiesFor(const GURL& origin);
- // Adds the |cookie| to the cookie list for the given |frame_url|.
+ // Adds the |cookie| to the cookie set for the given |frame_url|.
void AddCookie(const GURL& frame_url,
const net::CanonicalCookie& cookie);
- // Map that contains the cookie lists for all frame origins.
- OriginCookieListMap origin_cookie_list_map_;
+ // Map that contains the cookie sets for all frame origins.
+ OriginCookieSetMap origin_cookie_set_map_;
DISALLOW_COPY_AND_ASSIGN(CannedBrowsingDataCookieHelper);
};
diff --git a/chrome/browser/browsing_data/browsing_data_cookie_helper_unittest.cc b/chrome/browser/browsing_data/browsing_data_cookie_helper_unittest.cc
index faa861c..64a796d 100644
--- a/chrome/browser/browsing_data/browsing_data_cookie_helper_unittest.cc
+++ b/chrome/browser/browsing_data/browsing_data_cookie_helper_unittest.cc
@@ -15,12 +15,102 @@
namespace {
+// Test expectations for a given cookie.
+class CookieExpectation {
+ public:
+ CookieExpectation() : matched_(false) {}
+
+ bool MatchesCookie(const net::CanonicalCookie& cookie) const {
+ if (!source_.empty() && source_ != cookie.Source())
+ return false;
+ if (!domain_.empty() && domain_ != cookie.Domain())
+ return false;
+ if (!path_.empty() && path_ != cookie.Path())
+ return false;
+ if (!name_.empty() && name_ != cookie.Name())
+ return false;
+ if (!value_.empty() && value_ != cookie.Value())
+ return false;
+ return true;
+ }
+
+ std::string source_;
+ std::string domain_;
+ std::string path_;
+ std::string name_;
+ std::string value_;
+ bool matched_;
+};
+
+// Matches a CookieExpectation against a Cookie.
+class CookieMatcher {
+ public:
+ explicit CookieMatcher(const net::CanonicalCookie& cookie)
+ : cookie_(cookie) {}
+ bool operator()(const CookieExpectation& expectation) {
+ return expectation.MatchesCookie(cookie_);
+ }
+ net::CanonicalCookie cookie_;
+};
+
+// Unary predicate to determine whether an expectation has been matched.
+bool ExpectationIsMatched(const CookieExpectation& expectation) {
+ return expectation.matched_;
+}
+
class BrowsingDataCookieHelperTest : public testing::Test {
public:
BrowsingDataCookieHelperTest()
: testing_profile_(new TestingProfile()) {
}
+ virtual void SetUp() OVERRIDE { cookie_expectations_.clear(); }
+
+ // Adds an expectation for a cookie that satisfies the given parameters.
+ void AddCookieExpectation(const char* source,
+ const char* domain,
+ const char* path,
+ const char* name,
+ const char* value) {
+ CookieExpectation matcher;
+ if (source)
+ matcher.source_ = source;
+ if (domain)
+ matcher.domain_ = domain;
+ if (path)
+ matcher.path_ = path;
+ if (name)
+ matcher.name_ = name;
+ if (value)
+ matcher.value_ = value;
+ cookie_expectations_.push_back(matcher);
+ }
+
+ // Checks the existing expectations, and then clears all existing
+ // expectations.
+ void CheckCookieExpectations() {
+ ASSERT_EQ(cookie_expectations_.size(), cookie_list_.size());
+
+ // For each cookie, look for a matching expectation.
+ for (net::CookieList::iterator it = cookie_list_.begin();
+ it != cookie_list_.end();
+ ++it) {
+ CookieMatcher matcher(*it);
+ std::vector<CookieExpectation>::iterator match = std::find_if(
+ cookie_expectations_.begin(), cookie_expectations_.end(), matcher);
+ if (match != cookie_expectations_.end())
+ match->matched_ = true;
+ }
+
+ // Check that each expectation has been matched.
+ unsigned long match_count = std::count_if(cookie_expectations_.begin(),
+ cookie_expectations_.end(),
+ ExpectationIsMatched);
+ EXPECT_EQ(cookie_expectations_.size(), match_count);
+
+ cookie_expectations_.clear();
+ }
+
void CreateCookiesForTest() {
scoped_refptr<net::CookieMonster> cookie_monster =
testing_profile_->GetCookieMonster();
@@ -44,135 +134,76 @@ class BrowsingDataCookieHelperTest : public testing::Test {
}
void FetchCallback(const net::CookieList& cookies) {
- ASSERT_EQ(2UL, cookies.size());
cookie_list_ = cookies;
- net::CookieList::const_iterator it = cookies.begin();
- // Correct because fetching cookies will get a sorted cookie list.
- ASSERT_TRUE(it != cookies.end());
- EXPECT_EQ("www.google.com", it->Domain());
- EXPECT_EQ("A", it->Name());
-
- ASSERT_TRUE(++it != cookies.end());
- EXPECT_EQ("www.gmail.google.com", it->Domain());
- EXPECT_EQ("B", it->Name());
-
- ASSERT_TRUE(++it == cookies.end());
+ AddCookieExpectation(NULL, "www.google.com", NULL, "A", NULL);
+ AddCookieExpectation(NULL, "www.gmail.google.com", NULL, "B", NULL);
+ CheckCookieExpectations();
}
void DomainCookieCallback(const net::CookieList& cookies) {
- ASSERT_EQ(2UL, cookies.size());
cookie_list_ = cookies;
- net::CookieList::const_iterator it = cookies.begin();
-
- // Correct because fetching cookies will get a sorted cookie list.
- ASSERT_TRUE(it != cookies.end());
- EXPECT_EQ("www.google.com", it->Domain());
- EXPECT_EQ("A", it->Name());
- EXPECT_EQ("1", it->Value());
-
- ASSERT_TRUE(++it != cookies.end());
- EXPECT_EQ(".www.google.com", it->Domain());
- EXPECT_EQ("A", it->Name());
- EXPECT_EQ("2", it->Value());
- ASSERT_TRUE(++it == cookies.end());
+ AddCookieExpectation(NULL, "www.google.com", NULL, "A", "1");
+ AddCookieExpectation(NULL, ".www.google.com", NULL, "A", "2");
+ CheckCookieExpectations();
}
void DeleteCallback(const net::CookieList& cookies) {
- ASSERT_EQ(1UL, cookies.size());
- net::CookieList::const_iterator it = cookies.begin();
-
- ASSERT_TRUE(it != cookies.end());
- EXPECT_EQ("www.gmail.google.com", it->Domain());
- EXPECT_EQ("B", it->Name());
-
- ASSERT_TRUE(++it == cookies.end());
+ cookie_list_ = cookies;
+ AddCookieExpectation(NULL, "www.gmail.google.com", NULL, "B", NULL);
+ CheckCookieExpectations();
}
void CannedUniqueCallback(const net::CookieList& cookies) {
- EXPECT_EQ(1UL, cookies.size());
cookie_list_ = cookies;
- net::CookieList::const_iterator it = cookies.begin();
-
- ASSERT_TRUE(it != cookies.end());
- EXPECT_EQ("http://www.google.com/", it->Source());
- EXPECT_EQ("www.google.com", it->Domain());
- EXPECT_EQ("/", it->Path());
- EXPECT_EQ("A", it->Name());
-
- ASSERT_TRUE(++it == cookies.end());
+ AddCookieExpectation(
+ "http://www.google.com/", "www.google.com", "/", "A", NULL);
+ CheckCookieExpectations();
}
void CannedReplaceCookieCallback(const net::CookieList& cookies) {
- EXPECT_EQ(5UL, cookies.size());
cookie_list_ = cookies;
- net::CookieList::const_iterator it = cookies.begin();
-
- ASSERT_TRUE(it != cookies.end());
- EXPECT_EQ("http://www.google.com/", it->Source());
- EXPECT_EQ("www.google.com", it->Domain());
- EXPECT_EQ("/", it->Path());
- EXPECT_EQ("A", it->Name());
- EXPECT_EQ("2", it->Value());
-
- ASSERT_TRUE(++it != cookies.end());
- EXPECT_EQ("http://www.google.com/", it->Source());
- EXPECT_EQ("www.google.com", it->Domain());
- EXPECT_EQ("/example/0", it->Path());
- EXPECT_EQ("A", it->Name());
- EXPECT_EQ("4", it->Value());
-
- ASSERT_TRUE(++it != cookies.end());
- EXPECT_EQ("http://www.google.com/", it->Source());
- EXPECT_EQ(".google.com", it->Domain());
- EXPECT_EQ("/", it->Path());
- EXPECT_EQ("A", it->Name());
- EXPECT_EQ("6", it->Value());
-
- ASSERT_TRUE(++it != cookies.end());
- EXPECT_EQ("http://www.google.com/", it->Source());
- EXPECT_EQ(".google.com", it->Domain());
- EXPECT_EQ("/example/1", it->Path());
- EXPECT_EQ("A", it->Name());
- EXPECT_EQ("8", it->Value());
-
- ASSERT_TRUE(++it != cookies.end());
- EXPECT_EQ("http://www.google.com/", it->Source());
- EXPECT_EQ(".www.google.com", it->Domain());
- EXPECT_EQ("/", it->Path());
- EXPECT_EQ("A", it->Name());
- EXPECT_EQ("10", it->Value());
-
- ASSERT_TRUE(++it == cookies.end());
+ AddCookieExpectation(
+ "http://www.google.com/", "www.google.com", "/", "A", "2");
+ AddCookieExpectation(
+ "http://www.google.com/", "www.google.com", "/example/0", "A", "4");
+ AddCookieExpectation(
+ "http://www.google.com/", ".google.com", "/", "A", "6");
+ AddCookieExpectation(
+ "http://www.google.com/", ".google.com", "/example/1", "A", "8");
+ AddCookieExpectation(
+ "http://www.google.com/", ".www.google.com", "/", "A", "10");
+ CheckCookieExpectations();
}
void CannedDomainCookieCallback(const net::CookieList& cookies) {
- ASSERT_EQ(2UL, cookies.size());
cookie_list_ = cookies;
- net::CookieList::const_iterator it = cookies.begin();
-
- ASSERT_TRUE(it != cookies.end());
- EXPECT_EQ("http://www.google.com/", it->Source());
- EXPECT_EQ("A", it->Name());
- EXPECT_EQ("www.google.com", it->Domain());
-
- ASSERT_TRUE(++it != cookies.end());
- EXPECT_EQ("http://www.google.com/", it->Source());
- EXPECT_EQ("A", it->Name());
- EXPECT_EQ(".www.google.com", it->Domain());
-
- ASSERT_TRUE(++it == cookies.end());
+ AddCookieExpectation(
+ "http://www.google.com/", "www.google.com", NULL, "A", NULL);
+ AddCookieExpectation(
+ "http://www.google.com/", ".www.google.com", NULL, "A", NULL);
+ CheckCookieExpectations();
}
void CannedDifferentFramesCallback(const net::CookieList& cookie_list) {
ASSERT_EQ(3U, cookie_list.size());
}
+ void DeleteCookie(BrowsingDataCookieHelper* helper, const GURL origin) {
+ for (net::CookieList::iterator it = cookie_list_.begin();
+ it != cookie_list_.end();
+ ++it) {
+ if (it->Source() == net::CanonicalCookie::GetCookieSourceFromURL(origin))
+ helper->DeleteCookie(*it);
+ }
+ }
+
protected:
content::TestBrowserThreadBundle thread_bundle_;
scoped_ptr<TestingProfile> testing_profile_;
+ std::vector<CookieExpectation> cookie_expectations_;
net::CookieList cookie_list_;
};
@@ -237,7 +268,7 @@ TEST_F(BrowsingDataCookieHelperTest, CannedDeleteCookie) {
EXPECT_EQ(2u, helper->GetCookieCount());
- helper->DeleteCookie(cookie_list_[0]);
+ DeleteCookie(helper.get(), origin1);
EXPECT_EQ(1u, helper->GetCookieCount());
helper->StartFetching(
diff --git a/chrome/browser/browsing_data/canonical_cookie_hash.cc b/chrome/browser/browsing_data/canonical_cookie_hash.cc
new file mode 100644
index 0000000..218555e
--- /dev/null
+++ b/chrome/browser/browsing_data/canonical_cookie_hash.cc
@@ -0,0 +1,18 @@
+// Copyright 2014 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 "chrome/browser/browsing_data/canonical_cookie_hash.h"
+
+#include "base/hash.h"
+
+namespace canonical_cookie {
+
+size_t FastHash(const net::CanonicalCookie& cookie) {
+ return base::SuperFastHash(cookie.Name().c_str(), cookie.Name().size()) +
+ 3 * base::SuperFastHash(cookie.Domain().c_str(),
+ cookie.Domain().size()) +
+ 7 * base::SuperFastHash(cookie.Path().c_str(), cookie.Path().size());
+}
+
+}; // namespace canonical_cookie
diff --git a/chrome/browser/browsing_data/canonical_cookie_hash.h b/chrome/browser/browsing_data/canonical_cookie_hash.h
new file mode 100644
index 0000000..a6905df
--- /dev/null
+++ b/chrome/browser/browsing_data/canonical_cookie_hash.h
@@ -0,0 +1,86 @@
+// Copyright 2014 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 utility structures for inserting a CanonicalCookie into a hash set.
+// Two cookies are considered equal if their names, domains, and paths are
+// equivalent.
+
+#ifndef CHROME_BROWSER_BROWSING_DATA_CANONICAL_COOKIE_HASH_H_
+#define CHROME_BROWSER_BROWSING_DATA_CANONICAL_COOKIE_HASH_H_
+
+#if defined(COMPILER_MSVC)
+#include <functional>
+#endif // COMPILER_MSVC
+
+#include "base/containers/hash_tables.h"
+#include "net/cookies/canonical_cookie.h"
+
+namespace canonical_cookie {
+
+// Returns a fast hash of a cookie, based on its name, domain, and path.
+size_t FastHash(const net::CanonicalCookie& cookie);
+
+#if defined(COMPILER_MSVC)
+struct CanonicalCookieTraits {
+ static const size_t bucket_size = 4;
+
+ // Returns a hash of |cookie|.
+ size_t operator()(const net::CanonicalCookie& cookie) const {
+ return FastHash(cookie);
+ }
+
+ // The 'less' operator on cookies. We need to create a total ordering. We
+ // order lexigraphically, first by name, then path, then domain. Name is most
+ // likely to be distinct, so it is compared first, and domain is least likely
+ // to be distinct, so it is compared last.
+ bool operator()(const net::CanonicalCookie& cookie1,
+ const net::CanonicalCookie& cookie2) const {
+ std::less<std::string> less_than;
+ if (less_than(cookie1.Name(), cookie2.Name()))
+ return true;
+ if (less_than(cookie2.Name(), cookie1.Name()))
+ return false;
+ if (less_than(cookie1.Path(), cookie2.Path()))
+ return true;
+ if (less_than(cookie2.Path(), cookie1.Path()))
+ return false;
+ if (less_than(cookie1.Domain(), cookie2.Domain()))
+ return true;
+ if (less_than(cookie2.Domain(), cookie1.Domain()))
+ return false;
+
+ // The cookies are equivalent.
+ return false;
+ }
+};
+
+typedef base::hash_set<net::CanonicalCookie, CanonicalCookieTraits>
+ CookieHashSet;
+
+#else // COMPILER_MSVC
+
+struct CanonicalCookieHasher {
+ std::size_t operator()(const net::CanonicalCookie& cookie) const {
+ return FastHash(cookie);
+ }
+};
+
+struct CanonicalCookieComparer {
+ bool operator()(const net::CanonicalCookie& cookie1,
+ const net::CanonicalCookie& cookie2) const {
+ return cookie1.Name() == cookie2.Name() &&
+ cookie1.Domain() == cookie2.Domain() &&
+ cookie1.Path() == cookie2.Path();
+ }
+};
+
+typedef base::hash_set<net::CanonicalCookie,
+ CanonicalCookieHasher,
+ CanonicalCookieComparer> CookieHashSet;
+
+#endif // COMPILER_MSVC
+
+}; // namespace canonical_cookie
+
+#endif // CHROME_BROWSER_BROWSING_DATA_CANONICAL_COOKIE_HASH_H_