diff options
author | Allan Sandfeld Jensen <allan.jensen@theqtcompany.com> | 2016-05-09 14:22:11 +0200 |
---|---|---|
committer | Allan Sandfeld Jensen <allan.jensen@qt.io> | 2016-05-09 15:11:45 +0000 |
commit | 2ddb2d3e14eef3de7dbd0cef553d669b9ac2361c (patch) | |
tree | e75f511546c5fd1a173e87c1f9fb11d7ac8d1af3 /chromium/net/cookies | |
parent | a4f3d46271c57e8155ba912df46a05559d14726e (diff) | |
download | qtwebengine-chromium-2ddb2d3e14eef3de7dbd0cef553d669b9ac2361c.tar.gz |
BASELINE: Update Chromium to 51.0.2704.41
Also adds in all smaller components by reversing logic for exclusion.
Change-Id: Ibf90b506e7da088ea2f65dcf23f2b0992c504422
Reviewed-by: Joerg Bornemann <joerg.bornemann@theqtcompany.com>
Diffstat (limited to 'chromium/net/cookies')
26 files changed, 2907 insertions, 2943 deletions
diff --git a/chromium/net/cookies/canonical_cookie.cc b/chromium/net/cookies/canonical_cookie.cc index 15287e0b8b5..bce2b5477fa 100644 --- a/chromium/net/cookies/canonical_cookie.cc +++ b/chromium/net/cookies/canonical_cookie.cc @@ -47,6 +47,7 @@ #include "base/format_macros.h" #include "base/logging.h" #include "base/metrics/histogram_macros.h" +#include "base/strings/string_util.h" #include "base/strings/stringprintf.h" #include "net/cookies/cookie_util.h" #include "net/cookies/parsed_cookie.h" @@ -133,7 +134,7 @@ CanonicalCookie::CanonicalCookie(const GURL& url, const base::Time& last_access, bool secure, bool httponly, - bool firstpartyonly, + CookieSameSite same_site, CookiePriority priority) : source_(url.SchemeIsFile() ? url : url.GetOrigin()), name_(name), @@ -145,36 +146,10 @@ CanonicalCookie::CanonicalCookie(const GURL& url, last_access_date_(last_access), secure_(secure), httponly_(httponly), - first_party_only_(firstpartyonly), + same_site_(same_site), priority_(priority) {} -CanonicalCookie::CanonicalCookie(const GURL& url, const ParsedCookie& pc) - : source_(url.SchemeIsFile() ? url : url.GetOrigin()), - name_(pc.Name()), - value_(pc.Value()), - path_(CanonPath(url, pc)), - creation_date_(Time::Now()), - last_access_date_(Time()), - secure_(pc.IsSecure()), - httponly_(pc.IsHttpOnly()), - first_party_only_(pc.IsFirstPartyOnly()), - priority_(pc.Priority()) { - if (pc.HasExpires()) - expiry_date_ = CanonExpiration(pc, creation_date_, creation_date_); - - // Do the best we can with the domain. - std::string cookie_domain; - std::string domain_string; - if (pc.HasDomain()) { - domain_string = pc.Domain(); - } - bool result - = cookie_util::GetCookieDomainWithString(url, domain_string, - &cookie_domain); - // Caller is responsible for passing in good arguments. - DCHECK(result); - domain_ = cookie_domain; -} +CanonicalCookie::CanonicalCookie(const CanonicalCookie& other) = default; CanonicalCookie::~CanonicalCookie() { } @@ -217,26 +192,27 @@ Time CanonicalCookie::CanonExpiration(const ParsedCookie& pc, } // static -CanonicalCookie* CanonicalCookie::Create(const GURL& url, - const std::string& cookie_line, - const base::Time& creation_time, - const CookieOptions& options) { +scoped_ptr<CanonicalCookie> CanonicalCookie::Create( + const GURL& url, + const std::string& cookie_line, + const base::Time& creation_time, + const CookieOptions& options) { ParsedCookie parsed_cookie(cookie_line); if (!parsed_cookie.IsValid()) { VLOG(kVlogSetCookies) << "WARNING: Couldn't parse cookie"; - return NULL; + return nullptr; } if (options.exclude_httponly() && parsed_cookie.IsHttpOnly()) { VLOG(kVlogSetCookies) << "Create() is not creating a httponly cookie"; - return NULL; + return nullptr; } std::string cookie_domain; if (!GetCookieDomain(url, parsed_cookie, &cookie_domain)) { VLOG(kVlogSetCookies) << "Create() failed to get a cookie domain"; - return NULL; + return nullptr; } // Per 3.2.1 of "Deprecate modification of 'secure' cookies from non-secure @@ -247,7 +223,7 @@ CanonicalCookie* CanonicalCookie::Create(const GURL& url, !url.SchemeIsCryptographic()) { VLOG(kVlogSetCookies) << "Create() is trying to create a secure cookie from an insecure URL"; - return NULL; + return nullptr; } std::string cookie_path = CanonicalCookie::CanonPath(url, parsed_cookie); @@ -269,54 +245,56 @@ CanonicalCookie* CanonicalCookie::Create(const GURL& url, return nullptr; } - return new CanonicalCookie( + return make_scoped_ptr(new CanonicalCookie( url, parsed_cookie.Name(), parsed_cookie.Value(), cookie_domain, cookie_path, creation_time, cookie_expires, creation_time, parsed_cookie.IsSecure(), parsed_cookie.IsHttpOnly(), - parsed_cookie.IsFirstPartyOnly(), parsed_cookie.Priority()); + parsed_cookie.SameSite(), parsed_cookie.Priority())); } -CanonicalCookie* CanonicalCookie::Create(const GURL& url, - const std::string& name, - const std::string& value, - const std::string& domain, - const std::string& path, - const base::Time& creation, - const base::Time& expiration, - bool secure, - bool http_only, - bool first_party_only, - bool enforce_strict_secure, - CookiePriority priority) { +// static +scoped_ptr<CanonicalCookie> CanonicalCookie::Create( + const GURL& url, + const std::string& name, + const std::string& value, + const std::string& domain, + const std::string& path, + const base::Time& creation, + const base::Time& expiration, + bool secure, + bool http_only, + CookieSameSite same_site, + bool enforce_strict_secure, + CookiePriority priority) { // Expect valid attribute tokens and values, as defined by the ParsedCookie // logic, otherwise don't create the cookie. std::string parsed_name = ParsedCookie::ParseTokenString(name); if (parsed_name != name) - return NULL; + return nullptr; std::string parsed_value = ParsedCookie::ParseValueString(value); if (parsed_value != value) - return NULL; + return nullptr; std::string parsed_domain = ParsedCookie::ParseValueString(domain); if (parsed_domain != domain) - return NULL; + return nullptr; std::string cookie_domain; if (!cookie_util::GetCookieDomainWithString(url, parsed_domain, &cookie_domain)) { - return NULL; + return nullptr; } if (enforce_strict_secure && secure && !url.SchemeIsCryptographic()) - return NULL; + return nullptr; std::string parsed_path = ParsedCookie::ParseValueString(path); if (parsed_path != path) - return NULL; + return nullptr; std::string cookie_path = CanonPathWithString(url, parsed_path); // Expect that the path was either not specified (empty), or is valid. if (!parsed_path.empty() && cookie_path != parsed_path) - return NULL; + return nullptr; // Canonicalize path again to make sure it escapes characters as needed. url::Component path_component(0, cookie_path.length()); url::RawCanonOutputT<char> canon_path; @@ -326,9 +304,9 @@ CanonicalCookie* CanonicalCookie::Create(const GURL& url, cookie_path = std::string(canon_path.data() + canon_path_component.begin, canon_path_component.len); - return new CanonicalCookie(url, parsed_name, parsed_value, cookie_domain, - cookie_path, creation, expiration, creation, - secure, http_only, first_party_only, priority); + return make_scoped_ptr(new CanonicalCookie( + url, parsed_name, parsed_value, cookie_domain, cookie_path, creation, + expiration, creation, secure, http_only, same_site, priority)); } bool CanonicalCookie::IsOnPath(const std::string& url_path) const { @@ -344,25 +322,24 @@ bool CanonicalCookie::IsOnPath(const std::string& url_path) const { // was longer, the same length, or shorter than the length of the url path. // I think the approach below is simpler. - // Make sure the cookie path is a prefix of the url path. If the - // url path is shorter than the cookie path, then the cookie path - // can't be a prefix. - if (url_path.find(path_) != 0) + // Make sure the cookie path is a prefix of the url path. If the url path is + // shorter than the cookie path, then the cookie path can't be a prefix. + if (!base::StartsWith(url_path, path_, base::CompareCase::SENSITIVE)) return false; - // Now we know that url_path is >= cookie_path, and that cookie_path - // is a prefix of url_path. If they are the are the same length then - // they are identical, otherwise we need an additional check: + // |url_path| is >= |path_|, and |path_| is a prefix of |url_path|. If they + // are the are the same length then they are identical, otherwise need an + // additional check: // In order to avoid in correctly matching a cookie path of /blah // with a request path of '/blahblah/', we need to make sure that either // the cookie path ends in a trailing '/', or that we prefix up to a '/' // in the url path. Since we know that the url path length is greater // than the cookie path length, it's safe to index one byte past. - if (path_.length() != url_path.length() && - path_[path_.length() - 1] != '/' && - url_path[path_.length()] != '/') + if (path_.length() != url_path.length() && path_.back() != '/' && + url_path[path_.length()] != '/') { return false; + } return true; } @@ -416,9 +393,23 @@ bool CanonicalCookie::IncludeForRequestURL(const GURL& url, // match the cookie-path. if (!IsOnPath(url.path())) return false; - // Don't include first-party-only cookies for non-first-party requests. - if (IsFirstPartyOnly() && !options.include_first_party_only_cookies()) - return false; + // Don't include same-site cookies for cross-site requests. + switch (SameSite()) { + case CookieSameSite::STRICT_MODE: + if (options.same_site_cookie_mode() != + CookieOptions::SameSiteCookieMode::INCLUDE_STRICT_AND_LAX) { + return false; + } + break; + case CookieSameSite::LAX_MODE: + if (options.same_site_cookie_mode() == + CookieOptions::SameSiteCookieMode::DO_NOT_INCLUDE) { + return false; + } + break; + default: + break; + } return true; } diff --git a/chromium/net/cookies/canonical_cookie.h b/chromium/net/cookies/canonical_cookie.h index 9c3a1c9c04b..f95501c7627 100644 --- a/chromium/net/cookies/canonical_cookie.h +++ b/chromium/net/cookies/canonical_cookie.h @@ -9,6 +9,7 @@ #include <vector> #include "base/gtest_prod_util.h" +#include "base/memory/scoped_ptr.h" #include "base/time/time.h" #include "net/base/net_export.h" #include "net/cookies/cookie_constants.h" @@ -37,13 +38,10 @@ class NET_EXPORT CanonicalCookie { const base::Time& last_access, bool secure, bool httponly, - bool firstpartyonly, + CookieSameSite same_site, CookiePriority priority); - // 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(const CanonicalCookie& other); ~CanonicalCookie(); @@ -52,26 +50,26 @@ class NET_EXPORT CanonicalCookie { // Creates a new |CanonicalCookie| from the |cookie_line| and the // |creation_time|. Canonicalizes and validates inputs. May return NULL if // an attribute value is invalid. - static CanonicalCookie* Create(const GURL& url, - const std::string& cookie_line, - const base::Time& creation_time, - const CookieOptions& options); + static scoped_ptr<CanonicalCookie> Create(const GURL& url, + const std::string& cookie_line, + const base::Time& creation_time, + const CookieOptions& options); // 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 base::Time& creation, - const base::Time& expiration, - bool secure, - bool http_only, - bool first_party_only, - bool enforce_strict_secure, - CookiePriority priority); + static scoped_ptr<CanonicalCookie> Create(const GURL& url, + const std::string& name, + const std::string& value, + const std::string& domain, + const std::string& path, + const base::Time& creation, + const base::Time& expiration, + bool secure, + bool http_only, + CookieSameSite same_site, + bool enforce_strict_secure, + CookiePriority priority); const GURL& Source() const { return source_; } const std::string& Name() const { return name_; } @@ -84,7 +82,7 @@ class NET_EXPORT CanonicalCookie { const base::Time& ExpiryDate() const { return expiry_date_; } bool IsSecure() const { return secure_; } bool IsHttpOnly() const { return httponly_; } - bool IsFirstPartyOnly() const { return first_party_only_; } + CookieSameSite SameSite() const { return same_site_; } CookiePriority Priority() const { return priority_; } bool IsDomainCookie() const { return !domain_.empty() && domain_[0] == '.'; } @@ -203,7 +201,7 @@ class NET_EXPORT CanonicalCookie { base::Time last_access_date_; bool secure_; bool httponly_; - bool first_party_only_; + CookieSameSite same_site_; CookiePriority priority_; }; diff --git a/chromium/net/cookies/canonical_cookie_unittest.cc b/chromium/net/cookies/canonical_cookie_unittest.cc index 2a01d9742a2..80b772e9b2f 100644 --- a/chromium/net/cookies/canonical_cookie_unittest.cc +++ b/chromium/net/cookies/canonical_cookie_unittest.cc @@ -19,7 +19,7 @@ TEST(CanonicalCookieTest, Constructor) { CanonicalCookie cookie(url, "A", "2", "www.example.com", "/test", current_time, base::Time(), current_time, false, false, - false, COOKIE_PRIORITY_DEFAULT); + CookieSameSite::DEFAULT_MODE, COOKIE_PRIORITY_DEFAULT); EXPECT_EQ(url.GetOrigin(), cookie.Source()); EXPECT_EQ("A", cookie.Name()); EXPECT_EQ("2", cookie.Value()); @@ -27,11 +27,12 @@ TEST(CanonicalCookieTest, Constructor) { EXPECT_EQ("/test", cookie.Path()); EXPECT_FALSE(cookie.IsSecure()); EXPECT_FALSE(cookie.IsHttpOnly()); - EXPECT_FALSE(cookie.IsFirstPartyOnly()); + EXPECT_EQ(CookieSameSite::NO_RESTRICTION, cookie.SameSite()); CanonicalCookie cookie2(url, "A", "2", std::string(), std::string(), current_time, base::Time(), current_time, false, - false, false, COOKIE_PRIORITY_DEFAULT); + false, CookieSameSite::DEFAULT_MODE, + COOKIE_PRIORITY_DEFAULT); EXPECT_EQ(url.GetOrigin(), cookie.Source()); EXPECT_EQ("A", cookie2.Name()); EXPECT_EQ("2", cookie2.Value()); @@ -39,7 +40,7 @@ TEST(CanonicalCookieTest, Constructor) { EXPECT_EQ("", cookie2.Path()); EXPECT_FALSE(cookie2.IsSecure()); EXPECT_FALSE(cookie2.IsHttpOnly()); - EXPECT_FALSE(cookie2.IsFirstPartyOnly()); + EXPECT_EQ(CookieSameSite::NO_RESTRICTION, cookie2.SameSite()); } TEST(CanonicalCookieTest, Create) { @@ -58,7 +59,7 @@ TEST(CanonicalCookieTest, Create) { EXPECT_FALSE(cookie->IsSecure()); GURL url2("http://www.foo.com"); - cookie.reset(CanonicalCookie::Create(url2, "B=1", creation_time, options)); + cookie = CanonicalCookie::Create(url2, "B=1", creation_time, options); EXPECT_EQ(url2.GetOrigin(), cookie->Source()); EXPECT_EQ("B", cookie->Name()); EXPECT_EQ("1", cookie->Value()); @@ -68,34 +69,37 @@ TEST(CanonicalCookieTest, Create) { // Test creating secure cookies. RFC 6265 allows insecure urls to set secure // cookies. - cookie.reset( - CanonicalCookie::Create(url, "A=2; Secure", creation_time, options)); + cookie = CanonicalCookie::Create(url, "A=2; Secure", creation_time, options); EXPECT_TRUE(cookie.get()); EXPECT_TRUE(cookie->IsSecure()); // Test creating http only cookies. - cookie.reset( - CanonicalCookie::Create(url, "A=2; HttpOnly", creation_time, options)); + cookie = + CanonicalCookie::Create(url, "A=2; HttpOnly", creation_time, options); EXPECT_FALSE(cookie.get()); CookieOptions httponly_options; httponly_options.set_include_httponly(); - cookie.reset(CanonicalCookie::Create(url, "A=2; HttpOnly", creation_time, - httponly_options)); + cookie = CanonicalCookie::Create(url, "A=2; HttpOnly", creation_time, + httponly_options); EXPECT_TRUE(cookie->IsHttpOnly()); - // Test creating http only cookies. - CookieOptions first_party_options; - first_party_options.set_include_first_party_only_cookies(); - cookie.reset(CanonicalCookie::Create(url, "A=2; First-Party-Only", - creation_time, httponly_options)); + // Test creating SameSite cookies. + CookieOptions same_site_options; + same_site_options.set_same_site_cookie_mode( + CookieOptions::SameSiteCookieMode::INCLUDE_STRICT_AND_LAX); + cookie = CanonicalCookie::Create(url, "A=2; SameSite=Strict", creation_time, + same_site_options); EXPECT_TRUE(cookie.get()); - EXPECT_TRUE(cookie->IsFirstPartyOnly()); + EXPECT_EQ(CookieSameSite::STRICT_MODE, cookie->SameSite()); + cookie = CanonicalCookie::Create(url, "A=2; SameSite=Lax", creation_time, + same_site_options); // Test the creating cookies using specific parameter instead of a cookie // string. - cookie.reset(CanonicalCookie::Create( - url, "A", "2", "www.example.com", "/test", creation_time, base::Time(), - false, false, false, false, COOKIE_PRIORITY_DEFAULT)); + cookie = CanonicalCookie::Create(url, "A", "2", "www.example.com", "/test", + creation_time, base::Time(), false, false, + CookieSameSite::DEFAULT_MODE, false, + COOKIE_PRIORITY_DEFAULT); EXPECT_EQ(url.GetOrigin(), cookie->Source()); EXPECT_EQ("A", cookie->Name()); EXPECT_EQ("2", cookie->Value()); @@ -103,11 +107,12 @@ TEST(CanonicalCookieTest, Create) { EXPECT_EQ("/test", cookie->Path()); EXPECT_FALSE(cookie->IsSecure()); EXPECT_FALSE(cookie->IsHttpOnly()); - EXPECT_FALSE(cookie->IsFirstPartyOnly()); + EXPECT_EQ(CookieSameSite::NO_RESTRICTION, cookie->SameSite()); - cookie.reset(CanonicalCookie::Create( - url, "A", "2", ".www.example.com", "/test", creation_time, base::Time(), - false, false, false, false, COOKIE_PRIORITY_DEFAULT)); + cookie = CanonicalCookie::Create(url, "A", "2", ".www.example.com", "/test", + creation_time, base::Time(), false, false, + CookieSameSite::DEFAULT_MODE, false, + COOKIE_PRIORITY_DEFAULT); EXPECT_EQ(url.GetOrigin(), cookie->Source()); EXPECT_EQ("A", cookie->Name()); EXPECT_EQ("2", cookie->Value()); @@ -115,7 +120,24 @@ TEST(CanonicalCookieTest, Create) { EXPECT_EQ("/test", cookie->Path()); EXPECT_FALSE(cookie->IsSecure()); EXPECT_FALSE(cookie->IsHttpOnly()); - EXPECT_FALSE(cookie->IsFirstPartyOnly()); + EXPECT_EQ(CookieSameSite::NO_RESTRICTION, cookie->SameSite()); +} + +TEST(CanonicalCookieTest, CreateInvalidSameSite) { + GURL url("http://www.example.com/test/foo.html"); + base::Time now = base::Time::Now(); + scoped_ptr<CanonicalCookie> cookie; + CookieOptions options; + + // Invalid 'SameSite' attribute values. + options.set_same_site_cookie_mode( + CookieOptions::SameSiteCookieMode::INCLUDE_STRICT_AND_LAX); + + cookie = CanonicalCookie::Create(url, "A=2; SameSite=Invalid", now, options); + EXPECT_EQ(nullptr, cookie.get()); + + cookie = CanonicalCookie::Create(url, "A=2; SameSite", now, options); + EXPECT_EQ(nullptr, cookie.get()); } TEST(CanonicalCookieTest, EmptyExpiry) { @@ -134,8 +156,7 @@ TEST(CanonicalCookieTest, EmptyExpiry) { // With a stale server time options.set_server_time(creation_time - base::TimeDelta::FromHours(1)); - cookie.reset( - CanonicalCookie::Create(url, cookie_line, creation_time, options)); + cookie = CanonicalCookie::Create(url, cookie_line, creation_time, options); EXPECT_TRUE(cookie.get()); EXPECT_FALSE(cookie->IsPersistent()); EXPECT_FALSE(cookie->IsExpired(creation_time)); @@ -143,8 +164,7 @@ TEST(CanonicalCookieTest, EmptyExpiry) { // With a future server time options.set_server_time(creation_time + base::TimeDelta::FromHours(1)); - cookie.reset( - CanonicalCookie::Create(url, cookie_line, creation_time, options)); + cookie = CanonicalCookie::Create(url, cookie_line, creation_time, options); EXPECT_TRUE(cookie.get()); EXPECT_FALSE(cookie->IsPersistent()); EXPECT_FALSE(cookie->IsExpired(creation_time)); @@ -162,19 +182,19 @@ TEST(CanonicalCookieTest, IsEquivalent) { base::Time expiration_time = creation_time + base::TimeDelta::FromDays(2); bool secure(false); bool httponly(false); - bool firstparty(false); + CookieSameSite same_site(CookieSameSite::NO_RESTRICTION); // Test that a cookie is equivalent to itself. scoped_ptr<CanonicalCookie> cookie(new CanonicalCookie( url, cookie_name, cookie_value, cookie_domain, cookie_path, creation_time, - expiration_time, last_access_time, secure, httponly, firstparty, + expiration_time, last_access_time, secure, httponly, same_site, COOKIE_PRIORITY_MEDIUM)); EXPECT_TRUE(cookie->IsEquivalent(*cookie)); // Test that two identical cookies are equivalent. scoped_ptr<CanonicalCookie> other_cookie(new CanonicalCookie( url, cookie_name, cookie_value, cookie_domain, cookie_path, creation_time, - expiration_time, last_access_time, secure, httponly, firstparty, + expiration_time, last_access_time, secure, httponly, same_site, COOKIE_PRIORITY_MEDIUM)); EXPECT_TRUE(cookie->IsEquivalent(*other_cookie)); @@ -183,47 +203,47 @@ TEST(CanonicalCookieTest, IsEquivalent) { other_cookie.reset( new CanonicalCookie(url, cookie_name, "2", cookie_domain, cookie_path, creation_time, expiration_time, last_access_time, - secure, httponly, firstparty, COOKIE_PRIORITY_HIGH)); + secure, httponly, same_site, COOKIE_PRIORITY_HIGH)); EXPECT_TRUE(cookie->IsEquivalent(*other_cookie)); base::Time other_creation_time = creation_time + base::TimeDelta::FromMinutes(2); other_cookie.reset(new CanonicalCookie( url, cookie_name, "2", cookie_domain, cookie_path, other_creation_time, - expiration_time, last_access_time, secure, httponly, firstparty, + expiration_time, last_access_time, secure, httponly, same_site, COOKIE_PRIORITY_MEDIUM)); EXPECT_TRUE(cookie->IsEquivalent(*other_cookie)); other_cookie.reset(new CanonicalCookie( url, cookie_name, cookie_name, cookie_domain, cookie_path, creation_time, - expiration_time, last_access_time, true, httponly, firstparty, + expiration_time, last_access_time, true, httponly, same_site, COOKIE_PRIORITY_LOW)); EXPECT_TRUE(cookie->IsEquivalent(*other_cookie)); other_cookie.reset(new CanonicalCookie( url, cookie_name, cookie_name, cookie_domain, cookie_path, creation_time, - expiration_time, last_access_time, secure, true, firstparty, + expiration_time, last_access_time, secure, true, same_site, COOKIE_PRIORITY_LOW)); EXPECT_TRUE(cookie->IsEquivalent(*other_cookie)); other_cookie.reset(new CanonicalCookie( url, cookie_name, cookie_name, cookie_domain, cookie_path, creation_time, - expiration_time, last_access_time, secure, httponly, true, - COOKIE_PRIORITY_LOW)); + expiration_time, last_access_time, secure, httponly, + CookieSameSite::STRICT_MODE, COOKIE_PRIORITY_LOW)); EXPECT_TRUE(cookie->IsEquivalent(*other_cookie)); // Tests that use different variations of attribute values that // DO affect cookie equivalence. - other_cookie.reset(new CanonicalCookie( - url, "B", cookie_value, cookie_domain, cookie_path, creation_time, - expiration_time, last_access_time, secure, httponly, firstparty, - COOKIE_PRIORITY_MEDIUM)); + other_cookie.reset( + new CanonicalCookie(url, "B", cookie_value, cookie_domain, cookie_path, + creation_time, expiration_time, last_access_time, + secure, httponly, same_site, COOKIE_PRIORITY_MEDIUM)); EXPECT_FALSE(cookie->IsEquivalent(*other_cookie)); other_cookie.reset(new CanonicalCookie( url, cookie_name, cookie_value, "www.example.com", cookie_path, creation_time, expiration_time, last_access_time, secure, httponly, - firstparty, COOKIE_PRIORITY_MEDIUM)); + same_site, COOKIE_PRIORITY_MEDIUM)); EXPECT_TRUE(cookie->IsDomainCookie()); EXPECT_FALSE(other_cookie->IsDomainCookie()); EXPECT_FALSE(cookie->IsEquivalent(*other_cookie)); @@ -231,12 +251,12 @@ TEST(CanonicalCookieTest, IsEquivalent) { other_cookie.reset(new CanonicalCookie( url, cookie_name, cookie_value, ".example.com", cookie_path, creation_time, expiration_time, last_access_time, secure, httponly, - firstparty, COOKIE_PRIORITY_MEDIUM)); + same_site, COOKIE_PRIORITY_MEDIUM)); EXPECT_FALSE(cookie->IsEquivalent(*other_cookie)); other_cookie.reset(new CanonicalCookie( url, cookie_name, cookie_value, cookie_domain, "/test/0", creation_time, - expiration_time, last_access_time, secure, httponly, firstparty, + expiration_time, last_access_time, secure, httponly, same_site, COOKIE_PRIORITY_MEDIUM)); EXPECT_FALSE(cookie->IsEquivalent(*other_cookie)); } @@ -252,19 +272,19 @@ TEST(CanonicalCookieTest, IsEquivalentForSecureCookieMatching) { base::Time expiration_time = creation_time + base::TimeDelta::FromDays(2); bool secure(false); bool httponly(false); - bool firstparty(false); + CookieSameSite same_site(CookieSameSite::NO_RESTRICTION); // Test that a cookie is equivalent to itself. scoped_ptr<CanonicalCookie> cookie(new CanonicalCookie( url, cookie_name, cookie_value, cookie_domain, cookie_path, creation_time, - expiration_time, last_access_time, secure, httponly, firstparty, + expiration_time, last_access_time, secure, httponly, same_site, COOKIE_PRIORITY_MEDIUM)); EXPECT_TRUE(cookie->IsEquivalentForSecureCookieMatching(*cookie)); // Test that two identical cookies are equivalent. scoped_ptr<CanonicalCookie> other_cookie(new CanonicalCookie( url, cookie_name, cookie_value, cookie_domain, cookie_path, creation_time, - expiration_time, last_access_time, secure, httponly, firstparty, + expiration_time, last_access_time, secure, httponly, same_site, COOKIE_PRIORITY_MEDIUM)); EXPECT_TRUE(cookie->IsEquivalentForSecureCookieMatching(*other_cookie)); @@ -277,47 +297,47 @@ TEST(CanonicalCookieTest, IsEquivalentForSecureCookieMatching) { other_cookie.reset( new CanonicalCookie(url, cookie_name, "2", cookie_domain, cookie_path, creation_time, expiration_time, last_access_time, - secure, httponly, firstparty, COOKIE_PRIORITY_HIGH)); + secure, httponly, same_site, COOKIE_PRIORITY_HIGH)); EXPECT_TRUE(cookie->IsEquivalentForSecureCookieMatching(*other_cookie)); base::Time other_creation_time = creation_time + base::TimeDelta::FromMinutes(2); other_cookie.reset(new CanonicalCookie( url, cookie_name, "2", cookie_domain, cookie_path, other_creation_time, - expiration_time, last_access_time, secure, httponly, firstparty, + expiration_time, last_access_time, secure, httponly, same_site, COOKIE_PRIORITY_MEDIUM)); EXPECT_TRUE(cookie->IsEquivalentForSecureCookieMatching(*other_cookie)); other_cookie.reset(new CanonicalCookie( url, cookie_name, cookie_name, cookie_domain, cookie_path, creation_time, - expiration_time, last_access_time, true, httponly, firstparty, + expiration_time, last_access_time, true, httponly, same_site, COOKIE_PRIORITY_LOW)); EXPECT_TRUE(cookie->IsEquivalentForSecureCookieMatching(*other_cookie)); other_cookie.reset(new CanonicalCookie( url, cookie_name, cookie_name, cookie_domain, cookie_path, creation_time, - expiration_time, last_access_time, secure, true, firstparty, + expiration_time, last_access_time, secure, true, same_site, COOKIE_PRIORITY_LOW)); EXPECT_TRUE(cookie->IsEquivalentForSecureCookieMatching(*other_cookie)); other_cookie.reset(new CanonicalCookie( url, cookie_name, cookie_name, cookie_domain, cookie_path, creation_time, - expiration_time, last_access_time, secure, httponly, true, - COOKIE_PRIORITY_LOW)); + expiration_time, last_access_time, secure, httponly, + CookieSameSite::STRICT_MODE, COOKIE_PRIORITY_LOW)); EXPECT_TRUE(cookie->IsEquivalentForSecureCookieMatching(*other_cookie)); // The following 3 tests' expected results differ from their IsEquivalent // counterparts above. other_cookie.reset(new CanonicalCookie( url, cookie_name, cookie_value, cookie_domain, "/test/0", creation_time, - expiration_time, last_access_time, secure, httponly, firstparty, + expiration_time, last_access_time, secure, httponly, same_site, COOKIE_PRIORITY_MEDIUM)); EXPECT_TRUE(cookie->IsEquivalentForSecureCookieMatching(*other_cookie)); other_cookie.reset(new CanonicalCookie( url, cookie_name, cookie_value, "www.example.com", cookie_path, creation_time, expiration_time, last_access_time, secure, httponly, - firstparty, COOKIE_PRIORITY_MEDIUM)); + same_site, COOKIE_PRIORITY_MEDIUM)); EXPECT_TRUE(cookie->IsDomainCookie()); EXPECT_FALSE(other_cookie->IsDomainCookie()); EXPECT_TRUE(cookie->IsEquivalentForSecureCookieMatching(*other_cookie)); @@ -325,17 +345,17 @@ TEST(CanonicalCookieTest, IsEquivalentForSecureCookieMatching) { other_cookie.reset(new CanonicalCookie( url, cookie_name, cookie_value, ".example.com", cookie_path, creation_time, expiration_time, last_access_time, secure, httponly, - firstparty, COOKIE_PRIORITY_MEDIUM)); + same_site, COOKIE_PRIORITY_MEDIUM)); EXPECT_TRUE(cookie->IsEquivalentForSecureCookieMatching(*other_cookie)); // Tests that use different variations of attribute values that // DO affect cookie equivalence. Note that unlike the IsEquivalent tests // above, this does *not* include tests for differing paths or domains that // "domain-match". - other_cookie.reset(new CanonicalCookie( - url, "B", cookie_value, cookie_domain, cookie_path, creation_time, - expiration_time, last_access_time, secure, httponly, firstparty, - COOKIE_PRIORITY_MEDIUM)); + other_cookie.reset( + new CanonicalCookie(url, "B", cookie_value, cookie_domain, cookie_path, + creation_time, expiration_time, last_access_time, + secure, httponly, same_site, COOKIE_PRIORITY_MEDIUM)); EXPECT_FALSE(cookie->IsEquivalentForSecureCookieMatching(*other_cookie)); } @@ -353,8 +373,8 @@ TEST(CanonicalCookieTest, IsDomainMatch) { EXPECT_FALSE(cookie->IsDomainMatch("www0.example.com")); EXPECT_FALSE(cookie->IsDomainMatch("example.com")); - cookie.reset(CanonicalCookie::Create(url, "A=2; Domain=www.example.com", - creation_time, options)); + cookie = CanonicalCookie::Create(url, "A=2; Domain=www.example.com", + creation_time, options); EXPECT_TRUE(cookie->IsDomainCookie()); EXPECT_TRUE(cookie->IsDomainMatch("www.example.com")); EXPECT_TRUE(cookie->IsDomainMatch("www.example.com")); @@ -362,8 +382,8 @@ TEST(CanonicalCookieTest, IsDomainMatch) { EXPECT_FALSE(cookie->IsDomainMatch("www0.example.com")); EXPECT_FALSE(cookie->IsDomainMatch("example.com")); - cookie.reset(CanonicalCookie::Create(url, "A=2; Domain=.www.example.com", - creation_time, options)); + cookie = CanonicalCookie::Create(url, "A=2; Domain=.www.example.com", + creation_time, options); EXPECT_TRUE(cookie->IsDomainMatch("www.example.com")); EXPECT_TRUE(cookie->IsDomainMatch("www.example.com")); EXPECT_TRUE(cookie->IsDomainMatch("foo.www.example.com")); @@ -384,9 +404,8 @@ TEST(CanonicalCookieTest, IsOnPath) { // Test the empty string edge case. EXPECT_FALSE(cookie->IsOnPath(std::string())); - cookie.reset( - CanonicalCookie::Create(GURL("http://www.example.com/test/foo.html"), - "A=2", creation_time, options)); + cookie = CanonicalCookie::Create(GURL("http://www.example.com/test/foo.html"), + "A=2", creation_time, options); EXPECT_FALSE(cookie->IsOnPath("/")); EXPECT_TRUE(cookie->IsOnPath("/test")); EXPECT_TRUE(cookie->IsOnPath("/test/bar.html")); @@ -412,16 +431,16 @@ TEST(CanonicalCookieTest, IncludeForRequestURL) { // Test that cookie with a cookie path that does not match the url path are // not included. - cookie.reset(CanonicalCookie::Create(url, "A=2; Path=/foo/bar", creation_time, - options)); + cookie = CanonicalCookie::Create(url, "A=2; Path=/foo/bar", creation_time, + options); EXPECT_FALSE(cookie->IncludeForRequestURL(url, options)); EXPECT_TRUE(cookie->IncludeForRequestURL( GURL("http://www.example.com/foo/bar/index.html"), options)); // Test that a secure cookie is not included for a non secure URL. GURL secure_url("https://www.example.com"); - cookie.reset(CanonicalCookie::Create(secure_url, "A=2; Secure", creation_time, - options)); + cookie = CanonicalCookie::Create(secure_url, "A=2; Secure", creation_time, + options); EXPECT_TRUE(cookie->IsSecure()); EXPECT_TRUE(cookie->IncludeForRequestURL(secure_url, options)); EXPECT_FALSE(cookie->IncludeForRequestURL(url, options)); @@ -429,54 +448,49 @@ TEST(CanonicalCookieTest, IncludeForRequestURL) { // Test that http only cookies are only included if the include httponly flag // is set on the cookie options. options.set_include_httponly(); - cookie.reset( - CanonicalCookie::Create(url, "A=2; HttpOnly", creation_time, options)); + cookie = + CanonicalCookie::Create(url, "A=2; HttpOnly", creation_time, options); EXPECT_TRUE(cookie->IsHttpOnly()); EXPECT_TRUE(cookie->IncludeForRequestURL(url, options)); options.set_exclude_httponly(); EXPECT_FALSE(cookie->IncludeForRequestURL(url, options)); } -TEST(CanonicalCookieTest, IncludeFirstPartyForFirstPartyURL) { - GURL insecure_url("http://example.test"); - GURL secure_url("https://example.test"); - GURL secure_url_with_path("https://example.test/foo/bar/index.html"); - GURL third_party_url("https://not-example.test"); +TEST(CanonicalCookieTest, IncludeSameSiteForSameSiteURL) { + GURL url("https://example.test"); base::Time creation_time = base::Time::Now(); CookieOptions options; scoped_ptr<CanonicalCookie> cookie; - // First-party-only cookies are not included for non-first-party requests, - // even if other properties match: - cookie.reset(CanonicalCookie::Create(secure_url, "A=2; First-Party-Only", - creation_time, options)); - EXPECT_TRUE(cookie->IsFirstPartyOnly()); - EXPECT_FALSE(cookie->IncludeForRequestURL(secure_url, options)); - cookie.reset(CanonicalCookie::Create( - secure_url, "A=2; Secure; First-Party-Only", creation_time, options)); - EXPECT_TRUE(cookie->IsFirstPartyOnly()); - EXPECT_FALSE(cookie->IncludeForRequestURL(secure_url, options)); - cookie.reset(CanonicalCookie::Create(secure_url_with_path, - "A=2; First-Party-Only; path=/foo/bar", - creation_time, options)); - EXPECT_TRUE(cookie->IsFirstPartyOnly()); - EXPECT_FALSE(cookie->IncludeForRequestURL(secure_url, options)); + // `SameSite=Strict` cookies are included for a URL only if the options' + // SameSiteCookieMode is INCLUDE_STRICT_AND_LAX. + cookie = CanonicalCookie::Create(url, "A=2; SameSite=Strict", creation_time, + options); + EXPECT_EQ(CookieSameSite::STRICT_MODE, cookie->SameSite()); + options.set_same_site_cookie_mode( + CookieOptions::SameSiteCookieMode::DO_NOT_INCLUDE); + EXPECT_FALSE(cookie->IncludeForRequestURL(url, options)); + options.set_same_site_cookie_mode( + CookieOptions::SameSiteCookieMode::INCLUDE_LAX); + EXPECT_FALSE(cookie->IncludeForRequestURL(url, options)); + options.set_same_site_cookie_mode( + CookieOptions::SameSiteCookieMode::INCLUDE_STRICT_AND_LAX); + EXPECT_TRUE(cookie->IncludeForRequestURL(url, options)); - // First-party-only cookies are included for first-party requests: - options.set_include_first_party_only_cookies(); - cookie.reset(CanonicalCookie::Create(secure_url, "A=2; First-Party-Only", - creation_time, options)); - EXPECT_TRUE(cookie->IsFirstPartyOnly()); - EXPECT_TRUE(cookie->IncludeForRequestURL(secure_url, options)); - cookie.reset(CanonicalCookie::Create( - secure_url, "A=2; Secure; First-Party-Only", creation_time, options)); - EXPECT_TRUE(cookie->IsFirstPartyOnly()); - EXPECT_TRUE(cookie->IncludeForRequestURL(secure_url, options)); - cookie.reset(CanonicalCookie::Create(secure_url_with_path, - "A=2; First-Party-Only; path=/foo/bar", - creation_time, options)); - EXPECT_TRUE(cookie->IsFirstPartyOnly()); - EXPECT_TRUE(cookie->IncludeForRequestURL(secure_url_with_path, options)); + // `SameSite=Lax` cookies are included for a URL only if the options' + // SameSiteCookieMode is INCLUDE_STRICT_AND_LAX. + cookie = + CanonicalCookie::Create(url, "A=2; SameSite=Lax", creation_time, options); + EXPECT_EQ(CookieSameSite::LAX_MODE, cookie->SameSite()); + options.set_same_site_cookie_mode( + CookieOptions::SameSiteCookieMode::DO_NOT_INCLUDE); + EXPECT_FALSE(cookie->IncludeForRequestURL(url, options)); + options.set_same_site_cookie_mode( + CookieOptions::SameSiteCookieMode::INCLUDE_LAX); + EXPECT_TRUE(cookie->IncludeForRequestURL(url, options)); + options.set_same_site_cookie_mode( + CookieOptions::SameSiteCookieMode::INCLUDE_STRICT_AND_LAX); + EXPECT_TRUE(cookie->IncludeForRequestURL(url, options)); } TEST(CanonicalCookieTest, PartialCompare) { @@ -549,28 +563,24 @@ TEST(CanonicalCookieTest, SecureCookiePrefix) { CookieOptions options; // A __Secure- cookie must be Secure. - EXPECT_EQ(nullptr, make_scoped_ptr(CanonicalCookie::Create( - https_url, "__Secure-A=B", creation_time, options))); - EXPECT_EQ(nullptr, - make_scoped_ptr(CanonicalCookie::Create( - https_url, "__Secure-A=B; httponly", creation_time, options))); + EXPECT_FALSE(CanonicalCookie::Create(https_url, "__Secure-A=B", creation_time, + options)); + EXPECT_FALSE(CanonicalCookie::Create(https_url, "__Secure-A=B; httponly", + creation_time, options)); // A typoed prefix does not have to be Secure. - EXPECT_NE(nullptr, - make_scoped_ptr(CanonicalCookie::Create( - https_url, "__secure-A=B; Secure", creation_time, options))); - EXPECT_NE(nullptr, make_scoped_ptr(CanonicalCookie::Create( - https_url, "__secure-A=C;", creation_time, options))); - EXPECT_NE(nullptr, - make_scoped_ptr(CanonicalCookie::Create( - https_url, "__SecureA=B; Secure", creation_time, options))); - EXPECT_NE(nullptr, make_scoped_ptr(CanonicalCookie::Create( - https_url, "__SecureA=C;", creation_time, options))); + EXPECT_TRUE(CanonicalCookie::Create(https_url, "__secure-A=B; Secure", + creation_time, options)); + EXPECT_TRUE(CanonicalCookie::Create(https_url, "__secure-A=C;", creation_time, + options)); + EXPECT_TRUE(CanonicalCookie::Create(https_url, "__SecureA=B; Secure", + creation_time, options)); + EXPECT_TRUE(CanonicalCookie::Create(https_url, "__SecureA=C;", creation_time, + options)); // A __Secure- cookie can't be set on a non-secure origin. - EXPECT_EQ(nullptr, - make_scoped_ptr(CanonicalCookie::Create( - http_url, "__Secure-A=B; Secure", creation_time, options))); + EXPECT_FALSE(CanonicalCookie::Create(http_url, "__Secure-A=B; Secure", + creation_time, options)); } TEST(CanonicalCookieTest, HostCookiePrefix) { @@ -581,54 +591,44 @@ TEST(CanonicalCookieTest, HostCookiePrefix) { std::string domain = https_url.host(); // A __Host- cookie must be Secure. - EXPECT_EQ(nullptr, make_scoped_ptr(CanonicalCookie::Create( - https_url, "__Host-A=B;", creation_time, options))); - EXPECT_EQ(nullptr, - make_scoped_ptr(CanonicalCookie::Create( - https_url, "__Host-A=B; Domain=" + domain + "; Path=/;", - creation_time, options))); - EXPECT_NE(nullptr, make_scoped_ptr(CanonicalCookie::Create( - https_url, "__Host-A=B; Path=/; Secure;", - creation_time, options))); + EXPECT_FALSE(CanonicalCookie::Create(https_url, "__Host-A=B;", creation_time, + options)); + EXPECT_FALSE(CanonicalCookie::Create( + https_url, "__Host-A=B; Domain=" + domain + "; Path=/;", creation_time, + options)); + EXPECT_TRUE(CanonicalCookie::Create(https_url, "__Host-A=B; Path=/; Secure;", + creation_time, options)); // A __Host- cookie must be set from a secure scheme. - EXPECT_EQ(nullptr, - make_scoped_ptr(CanonicalCookie::Create( - http_url, "__Host-A=B; Domain=" + domain + "; Path=/; Secure;", - creation_time, options))); - EXPECT_NE(nullptr, make_scoped_ptr(CanonicalCookie::Create( - https_url, "__Host-A=B; Path=/; Secure;", - creation_time, options))); + EXPECT_FALSE(CanonicalCookie::Create( + http_url, "__Host-A=B; Domain=" + domain + "; Path=/; Secure;", + creation_time, options)); + EXPECT_TRUE(CanonicalCookie::Create(https_url, "__Host-A=B; Path=/; Secure;", + creation_time, options)); // A __Host- cookie can't have a Domain. - EXPECT_EQ(nullptr, - make_scoped_ptr(CanonicalCookie::Create( - https_url, "__Host-A=B; Domain=" + domain + "; Path=/; Secure;", - creation_time, options))); - EXPECT_EQ(nullptr, - make_scoped_ptr(CanonicalCookie::Create( - https_url, "__Host-A=B; Domain=" + domain + "; Secure;", - creation_time, options))); + EXPECT_FALSE(CanonicalCookie::Create( + https_url, "__Host-A=B; Domain=" + domain + "; Path=/; Secure;", + creation_time, options)); + EXPECT_FALSE(CanonicalCookie::Create( + https_url, "__Host-A=B; Domain=" + domain + "; Secure;", creation_time, + options)); // A __Host- cookie must have a Path of "/". - EXPECT_EQ(nullptr, make_scoped_ptr(CanonicalCookie::Create( - https_url, "__Host-A=B; Path=/foo; Secure;", - creation_time, options))); - EXPECT_EQ(nullptr, - make_scoped_ptr(CanonicalCookie::Create( - https_url, "__Host-A=B; Secure;", creation_time, options))); - EXPECT_NE(nullptr, make_scoped_ptr(CanonicalCookie::Create( - https_url, "__Host-A=B; Secure; Path=/;", - creation_time, options))); + EXPECT_FALSE(CanonicalCookie::Create( + https_url, "__Host-A=B; Path=/foo; Secure;", creation_time, options)); + EXPECT_FALSE(CanonicalCookie::Create(https_url, "__Host-A=B; Secure;", + creation_time, options)); + EXPECT_TRUE(CanonicalCookie::Create(https_url, "__Host-A=B; Secure; Path=/;", + creation_time, options)); // Rules don't apply for a typoed prefix. - EXPECT_NE(nullptr, - make_scoped_ptr(CanonicalCookie::Create( - http_url, "__host-A=B; Domain=" + domain + "; Path=/; Secure;", - creation_time, options))); - EXPECT_NE(nullptr, make_scoped_ptr(CanonicalCookie::Create( - https_url, "__HostA=B; Domain=" + domain + "; Secure;", - creation_time, options))); + EXPECT_TRUE(CanonicalCookie::Create( + http_url, "__host-A=B; Domain=" + domain + "; Path=/; Secure;", + creation_time, options)); + EXPECT_TRUE(CanonicalCookie::Create( + https_url, "__HostA=B; Domain=" + domain + "; Secure;", creation_time, + options)); } TEST(CanonicalCookieTest, EnforceSecureCookiesRequireSecureScheme) { @@ -653,21 +653,21 @@ TEST(CanonicalCookieTest, EnforceSecureCookiesRequireSecureScheme) { EXPECT_TRUE(https_cookie_secure.get()); scoped_ptr<CanonicalCookie> http_cookie_no_secure_extended( - CanonicalCookie::Create(http_url, "a", "b", "", "", creation_time, - creation_time, false, false, false, true, - COOKIE_PRIORITY_DEFAULT)); + CanonicalCookie::Create( + http_url, "a", "b", "", "", creation_time, creation_time, false, + false, CookieSameSite::STRICT_MODE, true, COOKIE_PRIORITY_DEFAULT)); scoped_ptr<CanonicalCookie> http_cookie_secure_extended( - CanonicalCookie::Create(http_url, "a", "b", "", "", creation_time, - creation_time, true, false, false, true, - COOKIE_PRIORITY_DEFAULT)); + CanonicalCookie::Create( + http_url, "a", "b", "", "", creation_time, creation_time, true, false, + CookieSameSite::STRICT_MODE, true, COOKIE_PRIORITY_DEFAULT)); scoped_ptr<CanonicalCookie> https_cookie_no_secure_extended( - CanonicalCookie::Create(https_url, "a", "b", "", "", creation_time, - creation_time, false, false, false, true, - COOKIE_PRIORITY_DEFAULT)); + CanonicalCookie::Create( + https_url, "a", "b", "", "", creation_time, creation_time, false, + false, CookieSameSite::STRICT_MODE, true, COOKIE_PRIORITY_DEFAULT)); scoped_ptr<CanonicalCookie> https_cookie_secure_extended( - CanonicalCookie::Create(https_url, "a", "b", "", "", creation_time, - creation_time, true, false, false, true, - COOKIE_PRIORITY_DEFAULT)); + CanonicalCookie::Create( + https_url, "a", "b", "", "", creation_time, creation_time, true, + false, CookieSameSite::STRICT_MODE, true, COOKIE_PRIORITY_DEFAULT)); EXPECT_TRUE(http_cookie_no_secure_extended.get()); EXPECT_FALSE(http_cookie_secure_extended.get()); @@ -683,48 +683,42 @@ TEST(CanonicalCookieTest, TestPrefixHistograms) { base::Time creation_time = base::Time::Now(); CookieOptions options; - scoped_ptr<CanonicalCookie> cookie1 = make_scoped_ptr(CanonicalCookie::Create( - https_url, "__Host-A=B;", creation_time, options)); - EXPECT_EQ(nullptr, cookie1); + EXPECT_FALSE(CanonicalCookie::Create(https_url, "__Host-A=B;", creation_time, + options)); histograms.ExpectBucketCount(kCookiePrefixHistogram, CanonicalCookie::COOKIE_PREFIX_HOST, 1); histograms.ExpectBucketCount(kCookiePrefixBlockedHistogram, CanonicalCookie::COOKIE_PREFIX_HOST, 1); - EXPECT_NE(nullptr, make_scoped_ptr(CanonicalCookie::Create( - https_url, "__Host-A=B; Path=/; Secure", creation_time, - options))); + EXPECT_TRUE(CanonicalCookie::Create(https_url, "__Host-A=B; Path=/; Secure", + creation_time, options)); histograms.ExpectBucketCount(kCookiePrefixHistogram, CanonicalCookie::COOKIE_PREFIX_HOST, 2); histograms.ExpectBucketCount(kCookiePrefixBlockedHistogram, CanonicalCookie::COOKIE_PREFIX_HOST, 1); - EXPECT_NE(nullptr, make_scoped_ptr(CanonicalCookie::Create( - https_url, "__HostA=B; Path=/; Secure", creation_time, - options))); + EXPECT_TRUE(CanonicalCookie::Create(https_url, "__HostA=B; Path=/; Secure", + creation_time, options)); histograms.ExpectBucketCount(kCookiePrefixHistogram, CanonicalCookie::COOKIE_PREFIX_HOST, 2); histograms.ExpectBucketCount(kCookiePrefixBlockedHistogram, CanonicalCookie::COOKIE_PREFIX_HOST, 1); - scoped_ptr<CanonicalCookie> cookie2 = make_scoped_ptr(CanonicalCookie::Create( - https_url, "__Secure-A=B;", creation_time, options)); - EXPECT_EQ(nullptr, cookie2); + EXPECT_FALSE(CanonicalCookie::Create(https_url, "__Secure-A=B;", + creation_time, options)); histograms.ExpectBucketCount(kCookiePrefixHistogram, CanonicalCookie::COOKIE_PREFIX_SECURE, 1); histograms.ExpectBucketCount(kCookiePrefixBlockedHistogram, CanonicalCookie::COOKIE_PREFIX_SECURE, 1); - EXPECT_NE(nullptr, make_scoped_ptr(CanonicalCookie::Create( - https_url, "__Secure-A=B; Path=/; Secure", - creation_time, options))); + EXPECT_TRUE(CanonicalCookie::Create(https_url, "__Secure-A=B; Path=/; Secure", + creation_time, options)); histograms.ExpectBucketCount(kCookiePrefixHistogram, CanonicalCookie::COOKIE_PREFIX_SECURE, 2); histograms.ExpectBucketCount(kCookiePrefixBlockedHistogram, CanonicalCookie::COOKIE_PREFIX_SECURE, 1); - EXPECT_NE(nullptr, make_scoped_ptr(CanonicalCookie::Create( - https_url, "__SecureA=B; Path=/; Secure", - creation_time, options))); + EXPECT_TRUE(CanonicalCookie::Create(https_url, "__SecureA=B; Path=/; Secure", + creation_time, options)); histograms.ExpectBucketCount(kCookiePrefixHistogram, CanonicalCookie::COOKIE_PREFIX_SECURE, 2); histograms.ExpectBucketCount(kCookiePrefixBlockedHistogram, diff --git a/chromium/net/cookies/cookie_constants.cc b/chromium/net/cookies/cookie_constants.cc index 05fe22ea437..b618261f8b2 100644 --- a/chromium/net/cookies/cookie_constants.cc +++ b/chromium/net/cookies/cookie_constants.cc @@ -10,12 +10,17 @@ namespace net { namespace { + const char kPriorityLow[] = "low"; const char kPriorityMedium[] = "medium"; const char kPriorityHigh[] = "high"; + +const char kSameSiteLax[] = "lax"; +const char kSameSiteStrict[] = "strict"; + } // namespace -NET_EXPORT const std::string CookiePriorityToString(CookiePriority priority) { +std::string CookiePriorityToString(CookiePriority priority) { switch(priority) { case COOKIE_PRIORITY_HIGH: return kPriorityHigh; @@ -29,7 +34,7 @@ NET_EXPORT const std::string CookiePriorityToString(CookiePriority priority) { return std::string(); } -NET_EXPORT CookiePriority StringToCookiePriority(const std::string& priority) { +CookiePriority StringToCookiePriority(const std::string& priority) { std::string priority_comp = base::ToLowerASCII(priority); if (priority_comp == kPriorityHigh) @@ -42,4 +47,12 @@ NET_EXPORT CookiePriority StringToCookiePriority(const std::string& priority) { return COOKIE_PRIORITY_DEFAULT; } +CookieSameSite StringToCookieSameSite(const std::string& same_site) { + if (base::EqualsCaseInsensitiveASCII(same_site, kSameSiteLax)) + return CookieSameSite::LAX_MODE; + if (base::EqualsCaseInsensitiveASCII(same_site, kSameSiteStrict)) + return CookieSameSite::STRICT_MODE; + return CookieSameSite::DEFAULT_MODE; +} + } // namespace net diff --git a/chromium/net/cookies/cookie_constants.h b/chromium/net/cookies/cookie_constants.h index 7e27e14657d..49dafbdb3a1 100644 --- a/chromium/net/cookies/cookie_constants.h +++ b/chromium/net/cookies/cookie_constants.h @@ -18,13 +18,27 @@ enum CookiePriority { COOKIE_PRIORITY_DEFAULT = COOKIE_PRIORITY_MEDIUM }; +enum class CookieSameSite { + NO_RESTRICTION = 0, + LAX_MODE = 1, + STRICT_MODE = 2, + DEFAULT_MODE = NO_RESTRICTION +}; + // Returns the Set-Cookie header priority token corresponding to |priority|. -NET_EXPORT const std::string CookiePriorityToString(CookiePriority priority); +// +// TODO(mkwst): Remove this once its callsites are refactored. +NET_EXPORT std::string CookiePriorityToString(CookiePriority priority); // Converts the Set-Cookie header priority token |priority| to a CookiePriority. // Defaults to COOKIE_PRIORITY_DEFAULT for empty or unrecognized strings. NET_EXPORT CookiePriority StringToCookiePriority(const std::string& priority); +// Converst the Set-Cookie header SameSite token |same_site| to a +// CookieSameSite. Defaults to CookieSameSite::DEFAULT_MODE for empty or +// unrecognized strings. +NET_EXPORT CookieSameSite StringToCookieSameSite(const std::string& same_site); + } // namespace net #endif // NET_COOKIES_COOKIE_CONSTANTS_H_ diff --git a/chromium/net/cookies/cookie_monster.cc b/chromium/net/cookies/cookie_monster.cc index 15e4f7ee8e1..aaecaf9a265 100644 --- a/chromium/net/cookies/cookie_monster.cc +++ b/chromium/net/cookies/cookie_monster.cc @@ -162,13 +162,10 @@ bool CookieSorter(CanonicalCookie* cc1, CanonicalCookie* cc2) { bool LRACookieSorter(const CookieMonster::CookieMap::iterator& it1, const CookieMonster::CookieMap::iterator& it2) { - // Cookies accessed less recently should be deleted first. if (it1->second->LastAccessDate() != it2->second->LastAccessDate()) return it1->second->LastAccessDate() < it2->second->LastAccessDate(); - // In rare cases we might have two cookies with identical last access times. - // To preserve the stability of the sort, in these cases prefer to delete - // older cookies over newer ones. CreationDate() is guaranteed to be unique. + // Ensure stability for == last access times by falling back to creation. return it1->second->CreationDate() < it2->second->CreationDate(); } @@ -250,30 +247,6 @@ void SplitCookieVectorIntoSecureAndNonSecure( } } -// Predicate to support PartitionCookieByPriority(). -struct CookiePriorityEqualsTo - : std::unary_function<const CookieMonster::CookieMap::iterator, bool> { - explicit CookiePriorityEqualsTo(CookiePriority priority) - : priority_(priority) {} - - bool operator()(const CookieMonster::CookieMap::iterator it) const { - return it->second->Priority() == priority_; - } - - const CookiePriority priority_; -}; - -// For a CookieItVector iterator range [|it_begin|, |it_end|), -// moves all cookies with a given |priority| to the beginning of the list. -// Returns: An iterator in [it_begin, it_end) to the first element with -// priority != |priority|, or |it_end| if all have priority == |priority|. -CookieMonster::CookieItVector::iterator PartitionCookieByPriority( - CookieMonster::CookieItVector::iterator it_begin, - CookieMonster::CookieItVector::iterator it_end, - CookiePriority priority) { - return std::partition(it_begin, it_end, CookiePriorityEqualsTo(priority)); -} - bool LowerBoundAccessDateComparator(const CookieMonster::CookieMap::iterator it, const Time& access_date) { return it->second->LastAccessDate() < access_date; @@ -328,22 +301,6 @@ ChangeCausePair ChangeCauseMapping[] = { // DELETE_COOKIE_LAST_ENTRY {CookieMonsterDelegate::CHANGE_COOKIE_EXPLICIT, false}}; -std::string BuildCookieLine(const CanonicalCookieVector& cookies) { - std::string cookie_line; - for (CanonicalCookieVector::const_iterator it = cookies.begin(); - it != cookies.end(); ++it) { - if (it != cookies.begin()) - cookie_line += "; "; - // In Mozilla if you set a cookie like AAAA, it will have an empty token - // and a value of AAAA. When it sends the cookie back, it will send AAAA, - // so we need to avoid sending =AAAA for a blank token value. - if (!(*it)->Name().empty()) - cookie_line += (*it)->Name() + "="; - cookie_line += (*it)->Value(); - } - return cookie_line; -} - void RunAsync(scoped_refptr<base::TaskRunner> proxy, const CookieStore::CookieChangedCallback& callback, const CanonicalCookie& cookie, @@ -355,19 +312,7 @@ void RunAsync(scoped_refptr<base::TaskRunner> proxy, CookieMonster::CookieMonster(PersistentCookieStore* store, CookieMonsterDelegate* delegate) - : initialized_(false), - started_fetching_all_cookies_(false), - finished_fetching_all_cookies_(false), - fetch_strategy_(kUnknownFetch), - store_(store), - last_access_threshold_( - TimeDelta::FromSeconds(kDefaultAccessUpdateThresholdSeconds)), - delegate_(delegate), - last_statistic_record_time_(Time::Now()), - keep_expired_cookies_(false), - persist_session_cookies_(false) { - InitializeHistograms(); - SetDefaultCookieableSchemes(); + : CookieMonster(store, delegate, kDefaultAccessUpdateThresholdSeconds) { } CookieMonster::CookieMonster(PersistentCookieStore* store, @@ -377,15 +322,18 @@ CookieMonster::CookieMonster(PersistentCookieStore* store, started_fetching_all_cookies_(false), finished_fetching_all_cookies_(false), fetch_strategy_(kUnknownFetch), + seen_global_task_(false), store_(store), last_access_threshold_(base::TimeDelta::FromMilliseconds( last_access_threshold_milliseconds)), delegate_(delegate), last_statistic_record_time_(base::Time::Now()), - keep_expired_cookies_(false), - persist_session_cookies_(false) { + persist_session_cookies_(false), + weak_ptr_factory_(this) { InitializeHistograms(); - SetDefaultCookieableSchemes(); + cookieable_schemes_.insert( + cookieable_schemes_.begin(), kDefaultCookieableSchemes, + kDefaultCookieableSchemes + kDefaultCookieableSchemesCount); } // Task classes for queueing the coming request. @@ -401,51 +349,23 @@ class CookieMonster::CookieMonsterTask explicit CookieMonsterTask(CookieMonster* cookie_monster); virtual ~CookieMonsterTask(); - // Invokes the callback immediately, if the current thread is the one - // that originated the task, or queues the callback for execution on the - // appropriate thread. Maintains a reference to this CookieMonsterTask - // instance until the callback completes. - void InvokeCallback(base::Closure callback); - CookieMonster* cookie_monster() { return cookie_monster_; } private: friend class base::RefCountedThreadSafe<CookieMonsterTask>; CookieMonster* cookie_monster_; - scoped_refptr<base::SingleThreadTaskRunner> thread_; DISALLOW_COPY_AND_ASSIGN(CookieMonsterTask); }; CookieMonster::CookieMonsterTask::CookieMonsterTask( CookieMonster* cookie_monster) - : cookie_monster_(cookie_monster), - thread_(base::ThreadTaskRunnerHandle::Get()) { -} + : cookie_monster_(cookie_monster) {} CookieMonster::CookieMonsterTask::~CookieMonsterTask() { } -// Unfortunately, one cannot re-bind a Callback with parameters into a closure. -// Therefore, the closure passed to InvokeCallback is a clumsy binding of -// Callback::Run on a wrapped Callback instance. Since Callback is not -// reference counted, we bind to an instance that is a member of the -// CookieMonsterTask subclass. Then, we cannot simply post the callback to a -// message loop because the underlying instance may be destroyed (along with the -// CookieMonsterTask instance) in the interim. Therefore, we post a callback -// bound to the CookieMonsterTask, which *is* reference counted (thus preventing -// destruction of the original callback), and which invokes the closure (which -// invokes the original callback with the returned data). -void CookieMonster::CookieMonsterTask::InvokeCallback(base::Closure callback) { - if (thread_->BelongsToCurrentThread()) { - callback.Run(); - } else { - thread_->PostTask(FROM_HERE, base::Bind(&CookieMonsterTask::InvokeCallback, - this, callback)); - } -} - // Task class for SetCookieWithDetails call. class CookieMonster::SetCookieWithDetailsTask : public CookieMonsterTask { public: @@ -455,11 +375,12 @@ class CookieMonster::SetCookieWithDetailsTask : public CookieMonsterTask { const std::string& value, const std::string& domain, const std::string& path, - const base::Time& expiration_time, + base::Time creation_time, + base::Time expiration_time, + base::Time last_access_time, bool secure, bool http_only, - bool first_party_only, - bool enforce_prefixes, + CookieSameSite same_site, bool enforce_strict_secure, CookiePriority priority, const SetCookiesCallback& callback) @@ -469,11 +390,12 @@ class CookieMonster::SetCookieWithDetailsTask : public CookieMonsterTask { value_(value), domain_(domain), path_(path), + creation_time_(creation_time), expiration_time_(expiration_time), + last_access_time_(last_access_time), secure_(secure), http_only_(http_only), - first_party_only_(first_party_only), - enforce_prefixes_(enforce_prefixes), + same_site_(same_site), enforce_strict_secure_(enforce_strict_secure), priority_(priority), callback_(callback) {} @@ -490,11 +412,12 @@ class CookieMonster::SetCookieWithDetailsTask : public CookieMonsterTask { std::string value_; std::string domain_; std::string path_; + base::Time creation_time_; base::Time expiration_time_; + base::Time last_access_time_; bool secure_; bool http_only_; - bool first_party_only_; - bool enforce_prefixes_; + CookieSameSite same_site_; bool enforce_strict_secure_; CookiePriority priority_; SetCookiesCallback callback_; @@ -504,13 +427,11 @@ class CookieMonster::SetCookieWithDetailsTask : public CookieMonsterTask { void CookieMonster::SetCookieWithDetailsTask::Run() { bool success = this->cookie_monster()->SetCookieWithDetails( - url_, name_, value_, domain_, path_, expiration_time_, secure_, - http_only_, first_party_only_, enforce_prefixes_, enforce_strict_secure_, - priority_); - if (!callback_.is_null()) { - this->InvokeCallback(base::Bind(&SetCookiesCallback::Run, - base::Unretained(&callback_), success)); - } + url_, name_, value_, domain_, path_, creation_time_, expiration_time_, + last_access_time_, secure_, http_only_, same_site_, + enforce_strict_secure_, priority_); + if (!callback_.is_null()) + callback_.Run(success); } // Task class for GetAllCookies call. @@ -535,19 +456,17 @@ class CookieMonster::GetAllCookiesTask : public CookieMonsterTask { void CookieMonster::GetAllCookiesTask::Run() { if (!callback_.is_null()) { CookieList cookies = this->cookie_monster()->GetAllCookies(); - this->InvokeCallback(base::Bind(&GetCookieListCallback::Run, - base::Unretained(&callback_), cookies)); + callback_.Run(cookies); } } -// Task class for GetAllCookiesForURLWithOptions call. -class CookieMonster::GetAllCookiesForURLWithOptionsTask - : public CookieMonsterTask { +// Task class for GetCookieListWithOptionsAsync call. +class CookieMonster::GetCookieListWithOptionsTask : public CookieMonsterTask { public: - GetAllCookiesForURLWithOptionsTask(CookieMonster* cookie_monster, - const GURL& url, - const CookieOptions& options, - const GetCookieListCallback& callback) + GetCookieListWithOptionsTask(CookieMonster* cookie_monster, + const GURL& url, + const CookieOptions& options, + const GetCookieListCallback& callback) : CookieMonsterTask(cookie_monster), url_(url), options_(options), @@ -557,22 +476,21 @@ class CookieMonster::GetAllCookiesForURLWithOptionsTask void Run() override; protected: - ~GetAllCookiesForURLWithOptionsTask() override {} + ~GetCookieListWithOptionsTask() override {} private: GURL url_; CookieOptions options_; GetCookieListCallback callback_; - DISALLOW_COPY_AND_ASSIGN(GetAllCookiesForURLWithOptionsTask); + DISALLOW_COPY_AND_ASSIGN(GetCookieListWithOptionsTask); }; -void CookieMonster::GetAllCookiesForURLWithOptionsTask::Run() { +void CookieMonster::GetCookieListWithOptionsTask::Run() { if (!callback_.is_null()) { CookieList cookies = - this->cookie_monster()->GetAllCookiesForURLWithOptions(url_, options_); - this->InvokeCallback(base::Bind(&GetCookieListCallback::Run, - base::Unretained(&callback_), cookies)); + this->cookie_monster()->GetCookieListWithOptions(url_, options_); + callback_.Run(cookies); } } @@ -603,8 +521,11 @@ class CookieMonster::DeleteTask : public CookieMonsterTask { private: // Runs the delete task and returns a result. virtual Result RunDeleteTask() = 0; + // Runs the delete task and then returns a callback to be called after + // flushing the persistent store. + // TODO(mmenke): This seems like a pretty ugly and needlessly confusing API. + // Simplify it? base::Closure RunDeleteTaskAndBindCallback(); - void FlushDone(const base::Closure& callback); typename CallbackType<Result>::Type callback_; @@ -632,36 +553,13 @@ base::Closure CookieMonster::DeleteTask<void>::RunDeleteTaskAndBindCallback() { template <typename Result> void CookieMonster::DeleteTask<Result>::Run() { - this->cookie_monster()->FlushStore(base::Bind( - &DeleteTask<Result>::FlushDone, this, RunDeleteTaskAndBindCallback())); -} - -template <typename Result> -void CookieMonster::DeleteTask<Result>::FlushDone( - const base::Closure& callback) { + base::Closure callback = RunDeleteTaskAndBindCallback(); if (!callback.is_null()) { - this->InvokeCallback(callback); + callback = base::Bind( + &CookieMonster::RunCallback, + this->cookie_monster()->weak_ptr_factory_.GetWeakPtr(), callback); } -} - -// Task class for DeleteAll call. -class CookieMonster::DeleteAllTask : public DeleteTask<int> { - public: - DeleteAllTask(CookieMonster* cookie_monster, const DeleteCallback& callback) - : DeleteTask<int>(cookie_monster, callback) {} - - // DeleteTask: - int RunDeleteTask() override; - - protected: - ~DeleteAllTask() override {} - - private: - DISALLOW_COPY_AND_ASSIGN(DeleteAllTask); -}; - -int CookieMonster::DeleteAllTask::RunDeleteTask() { - return this->cookie_monster()->DeleteAll(true); + this->cookie_monster()->FlushStore(callback); } // Task class for DeleteAllCreatedBetween call. @@ -693,30 +591,6 @@ int CookieMonster::DeleteAllCreatedBetweenTask::RunDeleteTask() { delete_end_); } -// Task class for DeleteAllForHost call. -class CookieMonster::DeleteAllForHostTask : public DeleteTask<int> { - public: - DeleteAllForHostTask(CookieMonster* cookie_monster, - const GURL& url, - const DeleteCallback& callback) - : DeleteTask<int>(cookie_monster, callback), url_(url) {} - - // DeleteTask: - int RunDeleteTask() override; - - protected: - ~DeleteAllForHostTask() override {} - - private: - GURL url_; - - DISALLOW_COPY_AND_ASSIGN(DeleteAllForHostTask); -}; - -int CookieMonster::DeleteAllForHostTask::RunDeleteTask() { - return this->cookie_monster()->DeleteAllForHost(url_); -} - // Task class for DeleteAllCreatedBetweenForHost call. class CookieMonster::DeleteAllCreatedBetweenForHostTask : public DeleteTask<int> { @@ -751,15 +625,15 @@ int CookieMonster::DeleteAllCreatedBetweenForHostTask::RunDeleteTask() { } // Task class for DeleteCanonicalCookie call. -class CookieMonster::DeleteCanonicalCookieTask : public DeleteTask<bool> { +class CookieMonster::DeleteCanonicalCookieTask : public DeleteTask<int> { public: DeleteCanonicalCookieTask(CookieMonster* cookie_monster, const CanonicalCookie& cookie, - const DeleteCookieCallback& callback) - : DeleteTask<bool>(cookie_monster, callback), cookie_(cookie) {} + const DeleteCallback& callback) + : DeleteTask<int>(cookie_monster, callback), cookie_(cookie) {} // DeleteTask: - bool RunDeleteTask() override; + int RunDeleteTask() override; protected: ~DeleteCanonicalCookieTask() override {} @@ -770,7 +644,7 @@ class CookieMonster::DeleteCanonicalCookieTask : public DeleteTask<bool> { DISALLOW_COPY_AND_ASSIGN(DeleteCanonicalCookieTask); }; -bool CookieMonster::DeleteCanonicalCookieTask::RunDeleteTask() { +int CookieMonster::DeleteCanonicalCookieTask::RunDeleteTask() { return this->cookie_monster()->DeleteCanonicalCookie(cookie_); } @@ -806,10 +680,8 @@ class CookieMonster::SetCookieWithOptionsTask : public CookieMonsterTask { void CookieMonster::SetCookieWithOptionsTask::Run() { bool result = this->cookie_monster()->SetCookieWithOptions(url_, cookie_line_, options_); - if (!callback_.is_null()) { - this->InvokeCallback(base::Bind(&SetCookiesCallback::Run, - base::Unretained(&callback_), result)); - } + if (!callback_.is_null()) + callback_.Run(result); } // Task class for SetAllCookies call. @@ -849,10 +721,8 @@ void CookieMonster::SetAllCookiesTask::Run() { if (positive_diff.size() > 0) result = this->cookie_monster()->SetCanonicalCookies(list_); - if (!callback_.is_null()) { - this->InvokeCallback(base::Bind(&SetCookiesCallback::Run, - base::Unretained(&callback_), result)); - } + if (!callback_.is_null()) + callback_.Run(result); } // Task class for GetCookiesWithOptions call. @@ -888,10 +758,8 @@ void CookieMonster::GetCookiesWithOptionsTask::Run() { "456373 CookieMonster::GetCookiesWithOptionsTask::Run")); std::string cookie = this->cookie_monster()->GetCookiesWithOptions(url_, options_); - if (!callback_.is_null()) { - this->InvokeCallback(base::Bind(&GetCookiesCallback::Run, - base::Unretained(&callback_), cookie)); - } + if (!callback_.is_null()) + callback_.Run(cookie); } // Task class for DeleteCookie call. @@ -951,51 +819,95 @@ void CookieMonster::SetCookieWithDetailsAsync( const std::string& value, const std::string& domain, const std::string& path, - const Time& expiration_time, + Time creation_time, + Time expiration_time, + Time last_access_time, bool secure, bool http_only, - bool first_party_only, - bool enforce_prefixes, + CookieSameSite same_site, bool enforce_strict_secure, CookiePriority priority, const SetCookiesCallback& callback) { scoped_refptr<SetCookieWithDetailsTask> task = new SetCookieWithDetailsTask( - this, url, name, value, domain, path, expiration_time, secure, http_only, - first_party_only, enforce_prefixes, enforce_strict_secure, priority, - callback); + this, url, name, value, domain, path, creation_time, expiration_time, + last_access_time, secure, http_only, same_site, enforce_strict_secure, + priority, callback); DoCookieTaskForURL(task, url); } -void CookieMonster::GetAllCookiesAsync(const GetCookieListCallback& callback) { - scoped_refptr<GetAllCookiesTask> task = new GetAllCookiesTask(this, callback); +void CookieMonster::FlushStore(const base::Closure& callback) { + DCHECK(thread_checker_.CalledOnValidThread()); + + if (initialized_ && store_.get()) + store_->Flush(callback); + else if (!callback.is_null()) + base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, callback); +} + +void CookieMonster::SetForceKeepSessionState() { + DCHECK(thread_checker_.CalledOnValidThread()); + + if (store_) + store_->SetForceKeepSessionState(); +} +void CookieMonster::SetAllCookiesAsync(const CookieList& list, + const SetCookiesCallback& callback) { + scoped_refptr<SetAllCookiesTask> task = + new SetAllCookiesTask(this, list, callback); DoCookieTask(task); } -void CookieMonster::GetAllCookiesForURLWithOptionsAsync( +void CookieMonster::SetCookieWithOptionsAsync( const GURL& url, + const std::string& cookie_line, const CookieOptions& options, - const GetCookieListCallback& callback) { - scoped_refptr<GetAllCookiesForURLWithOptionsTask> task = - new GetAllCookiesForURLWithOptionsTask(this, url, options, callback); + const SetCookiesCallback& callback) { + scoped_refptr<SetCookieWithOptionsTask> task = + new SetCookieWithOptionsTask(this, url, cookie_line, options, callback); DoCookieTaskForURL(task, url); } -void CookieMonster::GetAllCookiesForURLAsync( +void CookieMonster::GetCookiesWithOptionsAsync( const GURL& url, + const CookieOptions& options, + const GetCookiesCallback& callback) { + scoped_refptr<GetCookiesWithOptionsTask> task = + new GetCookiesWithOptionsTask(this, url, options, callback); + + DoCookieTaskForURL(task, url); +} + +void CookieMonster::GetCookieListWithOptionsAsync( + const GURL& url, + const CookieOptions& options, const GetCookieListCallback& callback) { - CookieOptions options; - options.set_include_httponly(); - options.set_include_first_party_only_cookies(); - scoped_refptr<GetAllCookiesForURLWithOptionsTask> task = - new GetAllCookiesForURLWithOptionsTask(this, url, options, callback); + scoped_refptr<GetCookieListWithOptionsTask> task = + new GetCookieListWithOptionsTask(this, url, options, callback); DoCookieTaskForURL(task, url); } -void CookieMonster::DeleteAllAsync(const DeleteCallback& callback) { - scoped_refptr<DeleteAllTask> task = new DeleteAllTask(this, callback); +void CookieMonster::GetAllCookiesAsync(const GetCookieListCallback& callback) { + scoped_refptr<GetAllCookiesTask> task = new GetAllCookiesTask(this, callback); + + DoCookieTask(task); +} + +void CookieMonster::DeleteCookieAsync(const GURL& url, + const std::string& cookie_name, + const base::Closure& callback) { + scoped_refptr<DeleteCookieTask> task = + new DeleteCookieTask(this, url, cookie_name, callback); + + DoCookieTaskForURL(task, url); +} + +void CookieMonster::DeleteCanonicalCookieAsync(const CanonicalCookie& cookie, + const DeleteCallback& callback) { + scoped_refptr<DeleteCanonicalCookieTask> task = + new DeleteCanonicalCookieTask(this, cookie, callback); DoCookieTask(task); } @@ -1022,115 +934,74 @@ void CookieMonster::DeleteAllCreatedBetweenForHostAsync( DoCookieTaskForURL(task, url); } -void CookieMonster::DeleteAllForHostAsync(const GURL& url, - const DeleteCallback& callback) { - scoped_refptr<DeleteAllForHostTask> task = - new DeleteAllForHostTask(this, url, callback); - - DoCookieTaskForURL(task, url); -} - -void CookieMonster::DeleteCanonicalCookieAsync( - const CanonicalCookie& cookie, - const DeleteCookieCallback& callback) { - scoped_refptr<DeleteCanonicalCookieTask> task = - new DeleteCanonicalCookieTask(this, cookie, callback); +void CookieMonster::DeleteSessionCookiesAsync( + const CookieStore::DeleteCallback& callback) { + scoped_refptr<DeleteSessionCookiesTask> task = + new DeleteSessionCookiesTask(this, callback); DoCookieTask(task); } -void CookieMonster::SetAllCookiesAsync(const CookieList& list, - const SetCookiesCallback& callback) { - scoped_refptr<SetAllCookiesTask> task = - new SetAllCookiesTask(this, list, callback); - DoCookieTask(task); -} +void CookieMonster::SetCookieableSchemes( + const std::vector<std::string>& schemes) { + DCHECK(thread_checker_.CalledOnValidThread()); -void CookieMonster::SetCookieWithOptionsAsync( - const GURL& url, - const std::string& cookie_line, - const CookieOptions& options, - const SetCookiesCallback& callback) { - scoped_refptr<SetCookieWithOptionsTask> task = - new SetCookieWithOptionsTask(this, url, cookie_line, options, callback); + // Calls to this method will have no effect if made after a WebView or + // CookieManager instance has been created. + if (initialized_) + return; - DoCookieTaskForURL(task, url); + cookieable_schemes_ = schemes; } -void CookieMonster::GetCookiesWithOptionsAsync( - const GURL& url, - const CookieOptions& options, - const GetCookiesCallback& callback) { - scoped_refptr<GetCookiesWithOptionsTask> task = - new GetCookiesWithOptionsTask(this, url, options, callback); - - DoCookieTaskForURL(task, url); +// This function must be called before the CookieMonster is used. +void CookieMonster::SetPersistSessionCookies(bool persist_session_cookies) { + DCHECK(thread_checker_.CalledOnValidThread()); + DCHECK(!initialized_); + persist_session_cookies_ = persist_session_cookies; } -void CookieMonster::DeleteCookieAsync(const GURL& url, - const std::string& cookie_name, - const base::Closure& callback) { - scoped_refptr<DeleteCookieTask> task = - new DeleteCookieTask(this, url, cookie_name, callback); +bool CookieMonster::IsCookieableScheme(const std::string& scheme) { + DCHECK(thread_checker_.CalledOnValidThread()); - DoCookieTaskForURL(task, url); + return std::find(cookieable_schemes_.begin(), cookieable_schemes_.end(), + scheme) != cookieable_schemes_.end(); } -void CookieMonster::DeleteSessionCookiesAsync( - const CookieStore::DeleteCallback& callback) { - scoped_refptr<DeleteSessionCookiesTask> task = - new DeleteSessionCookiesTask(this, callback); +const char* const CookieMonster::kDefaultCookieableSchemes[] = {"http", "https", + "ws", "wss"}; +const int CookieMonster::kDefaultCookieableSchemesCount = + arraysize(kDefaultCookieableSchemes); - DoCookieTask(task); -} +scoped_ptr<CookieStore::CookieChangedSubscription> +CookieMonster::AddCallbackForCookie(const GURL& gurl, + const std::string& name, + const CookieChangedCallback& callback) { + DCHECK(thread_checker_.CalledOnValidThread()); -void CookieMonster::DoCookieTask( - const scoped_refptr<CookieMonsterTask>& task_item) { - { - base::AutoLock autolock(lock_); - MarkCookieStoreAsInitialized(); - FetchAllCookiesIfNecessary(); - if (!finished_fetching_all_cookies_ && store_.get()) { - tasks_pending_.push(task_item); - return; - } - } + std::pair<GURL, std::string> key(gurl, name); + if (hook_map_.count(key) == 0) + hook_map_[key] = make_linked_ptr(new CookieChangedCallbackList()); + return hook_map_[key]->Add( + base::Bind(&RunAsync, base::ThreadTaskRunnerHandle::Get(), callback)); +} - task_item->Run(); +bool CookieMonster::IsEphemeral() { + return store_.get() == nullptr; } -void CookieMonster::DoCookieTaskForURL( - const scoped_refptr<CookieMonsterTask>& task_item, - const GURL& url) { - { - base::AutoLock autolock(lock_); - MarkCookieStoreAsInitialized(); - if (ShouldFetchAllCookiesWhenFetchingAnyCookie()) - FetchAllCookiesIfNecessary(); - // If cookies for the requested domain key (eTLD+1) have been loaded from DB - // then run the task, otherwise load from DB. - if (!finished_fetching_all_cookies_ && store_.get()) { - // Checks if the domain key has been loaded. - std::string key( - cookie_util::GetEffectiveDomain(url.scheme(), url.host())); - if (keys_loaded_.find(key) == keys_loaded_.end()) { - std::map<std::string, - std::deque<scoped_refptr<CookieMonsterTask>>>::iterator it = - tasks_pending_for_key_.find(key); - if (it == tasks_pending_for_key_.end()) { - store_->LoadCookiesForKey( - key, base::Bind(&CookieMonster::OnKeyLoaded, this, key)); - it = tasks_pending_for_key_ - .insert(std::make_pair( - key, std::deque<scoped_refptr<CookieMonsterTask>>())) - .first; - } - it->second.push_back(task_item); - return; - } - } +CookieMonster::~CookieMonster() { + DCHECK(thread_checker_.CalledOnValidThread()); + + // TODO(mmenke): Does it really make sense to run |delegate_| and + // CookieChanged callbacks when the CookieStore is destroyed? + for (CookieMap::iterator cookie_it = cookies_.begin(); + cookie_it != cookies_.end();) { + CookieMap::iterator current_cookie_it = cookie_it; + ++cookie_it; + InternalDeleteCookie(current_cookie_it, false /* sync_to_store */, + DELETE_COOKIE_DONT_RECORD); } - task_item->Run(); } bool CookieMonster::SetCookieWithDetails(const GURL& url, @@ -1138,56 +1009,50 @@ bool CookieMonster::SetCookieWithDetails(const GURL& url, const std::string& value, const std::string& domain, const std::string& path, - const base::Time& expiration_time, + base::Time creation_time, + base::Time expiration_time, + base::Time last_access_time, bool secure, bool http_only, - bool first_party_only, - bool enforce_prefixes, + CookieSameSite same_site, bool enforce_strict_secure, CookiePriority priority) { - base::AutoLock autolock(lock_); + DCHECK(thread_checker_.CalledOnValidThread()); if (!HasCookieableScheme(url)) return false; - Time creation_time = CurrentTime(); - last_time_seen_ = creation_time; + // TODO(mmenke): This class assumes each cookie to have a unique creation + // time. Allowing the caller to set the creation time violates that + // assumption. Worth fixing? Worth noting that time changes between browser + // restarts can cause the same issue. + base::Time actual_creation_time = creation_time; + if (creation_time.is_null()) { + actual_creation_time = CurrentTime(); + last_time_seen_ = actual_creation_time; + } - scoped_ptr<CanonicalCookie> cc; - cc.reset(CanonicalCookie::Create( - url, name, value, domain, path, creation_time, expiration_time, secure, - http_only, first_party_only, enforce_strict_secure, priority)); + scoped_ptr<CanonicalCookie> cc(CanonicalCookie::Create( + url, name, value, domain, path, actual_creation_time, expiration_time, + secure, http_only, same_site, enforce_strict_secure, priority)); if (!cc.get()) return false; + if (!last_access_time.is_null()) + cc->SetLastAccessDate(last_access_time); + CookieOptions options; options.set_include_httponly(); - options.set_include_first_party_only_cookies(); + options.set_same_site_cookie_mode( + CookieOptions::SameSiteCookieMode::INCLUDE_STRICT_AND_LAX); if (enforce_strict_secure) options.set_enforce_strict_secure(); - return SetCanonicalCookie(&cc, creation_time, options); -} - -bool CookieMonster::ImportCookies(const CookieList& list) { - base::AutoLock autolock(lock_); - MarkCookieStoreAsInitialized(); - if (ShouldFetchAllCookiesWhenFetchingAnyCookie()) - FetchAllCookiesIfNecessary(); - for (CookieList::const_iterator iter = list.begin(); iter != list.end(); - ++iter) { - scoped_ptr<CanonicalCookie> cookie(new CanonicalCookie(*iter)); - CookieOptions options; - options.set_include_httponly(); - options.set_include_first_party_only_cookies(); - if (!SetCanonicalCookie(&cookie, cookie->CreationDate(), options)) - return false; - } - return true; + return SetCanonicalCookie(std::move(cc), options); } CookieList CookieMonster::GetAllCookies() { - base::AutoLock autolock(lock_); + DCHECK(thread_checker_.CalledOnValidThread()); // This function is being called to scrape the cookie list for management UI // or similar. We shouldn't show expired cookies in this list since it will @@ -1217,16 +1082,19 @@ CookieList CookieMonster::GetAllCookies() { return cookie_list; } -CookieList CookieMonster::GetAllCookiesForURLWithOptions( +CookieList CookieMonster::GetCookieListWithOptions( const GURL& url, const CookieOptions& options) { - base::AutoLock autolock(lock_); + DCHECK(thread_checker_.CalledOnValidThread()); + + CookieList cookies; + if (!HasCookieableScheme(url)) + return cookies; std::vector<CanonicalCookie*> cookie_ptrs; - FindCookiesForHostAndDomain(url, options, false, &cookie_ptrs); + FindCookiesForHostAndDomain(url, options, &cookie_ptrs); std::sort(cookie_ptrs.begin(), cookie_ptrs.end(), CookieSorter); - CookieList cookies; cookies.reserve(cookie_ptrs.size()); for (std::vector<CanonicalCookie*>::const_iterator it = cookie_ptrs.begin(); it != cookie_ptrs.end(); it++) @@ -1235,26 +1103,9 @@ CookieList CookieMonster::GetAllCookiesForURLWithOptions( return cookies; } -int CookieMonster::DeleteAll(bool sync_to_store) { - base::AutoLock autolock(lock_); - - int num_deleted = 0; - for (CookieMap::iterator it = cookies_.begin(); it != cookies_.end();) { - CookieMap::iterator curit = it; - ++it; - InternalDeleteCookie(curit, sync_to_store, - sync_to_store - ? DELETE_COOKIE_EXPLICIT - : DELETE_COOKIE_DONT_RECORD /* Destruction. */); - ++num_deleted; - } - - return num_deleted; -} - int CookieMonster::DeleteAllCreatedBetween(const Time& delete_begin, const Time& delete_end) { - base::AutoLock autolock(lock_); + DCHECK(thread_checker_.CalledOnValidThread()); int num_deleted = 0; for (CookieMap::iterator it = cookies_.begin(); it != cookies_.end();) { @@ -1276,7 +1127,7 @@ int CookieMonster::DeleteAllCreatedBetween(const Time& delete_begin, int CookieMonster::DeleteAllCreatedBetweenForHost(const Time delete_begin, const Time delete_end, const GURL& url) { - base::AutoLock autolock(lock_); + DCHECK(thread_checker_.CalledOnValidThread()); if (!HasCookieableScheme(url)) return 0; @@ -1308,55 +1159,11 @@ int CookieMonster::DeleteAllCreatedBetweenForHost(const Time delete_begin, return num_deleted; } -int CookieMonster::DeleteAllForHost(const GURL& url) { - return DeleteAllCreatedBetweenForHost(Time(), Time::Max(), url); -} - -bool CookieMonster::DeleteCanonicalCookie(const CanonicalCookie& cookie) { - base::AutoLock autolock(lock_); - - for (CookieMapItPair its = cookies_.equal_range(GetKey(cookie.Domain())); - its.first != its.second; ++its.first) { - // The creation date acts as our unique index... - if (its.first->second->CreationDate() == cookie.CreationDate()) { - InternalDeleteCookie(its.first, true, DELETE_COOKIE_EXPLICIT); - return true; - } - } - return false; -} - -void CookieMonster::SetCookieableSchemes(const char* const schemes[], - size_t num_schemes) { - base::AutoLock autolock(lock_); - - // Calls to this method will have no effect if made after a WebView or - // CookieManager instance has been created. - if (initialized_) { - return; - } - - cookieable_schemes_.clear(); - cookieable_schemes_.insert(cookieable_schemes_.end(), schemes, - schemes + num_schemes); -} - -void CookieMonster::SetKeepExpiredCookies() { - keep_expired_cookies_ = true; -} - -void CookieMonster::FlushStore(const base::Closure& callback) { - base::AutoLock autolock(lock_); - if (initialized_ && store_.get()) - store_->Flush(callback); - else if (!callback.is_null()) - base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, callback); -} bool CookieMonster::SetCookieWithOptions(const GURL& url, const std::string& cookie_line, const CookieOptions& options) { - base::AutoLock autolock(lock_); + DCHECK(thread_checker_.CalledOnValidThread()); if (!HasCookieableScheme(url)) { return false; @@ -1367,13 +1174,13 @@ bool CookieMonster::SetCookieWithOptions(const GURL& url, std::string CookieMonster::GetCookiesWithOptions(const GURL& url, const CookieOptions& options) { - base::AutoLock autolock(lock_); + DCHECK(thread_checker_.CalledOnValidThread()); if (!HasCookieableScheme(url)) return std::string(); std::vector<CanonicalCookie*> cookies; - FindCookiesForHostAndDomain(url, options, true, &cookies); + FindCookiesForHostAndDomain(url, options, &cookies); std::sort(cookies.begin(), cookies.end(), CookieSorter); std::string cookie_line = BuildCookieLine(cookies); @@ -1385,26 +1192,26 @@ std::string CookieMonster::GetCookiesWithOptions(const GURL& url, void CookieMonster::DeleteCookie(const GURL& url, const std::string& cookie_name) { - base::AutoLock autolock(lock_); + DCHECK(thread_checker_.CalledOnValidThread()); if (!HasCookieableScheme(url)) return; CookieOptions options; options.set_include_httponly(); - options.set_include_first_party_only_cookies(); + options.set_same_site_cookie_mode( + CookieOptions::SameSiteCookieMode::INCLUDE_STRICT_AND_LAX); // Get the cookies for this host and its domain(s). std::vector<CanonicalCookie*> cookies; - FindCookiesForHostAndDomain(url, options, true, &cookies); + FindCookiesForHostAndDomain(url, options, &cookies); std::set<CanonicalCookie*> matching_cookies; - for (std::vector<CanonicalCookie*>::const_iterator it = cookies.begin(); - it != cookies.end(); ++it) { - if ((*it)->Name() != cookie_name) + for (const auto& cookie : cookies) { + if (cookie->Name() != cookie_name) continue; - if (url.path().find((*it)->Path())) + if (!cookie->IsOnPath(url.path())) continue; - matching_cookies.insert(*it); + matching_cookies.insert(cookie); } for (CookieMap::iterator it = cookies_.begin(); it != cookies_.end();) { @@ -1416,50 +1223,25 @@ void CookieMonster::DeleteCookie(const GURL& url, } } -int CookieMonster::DeleteSessionCookies() { - base::AutoLock autolock(lock_); +int CookieMonster::DeleteCanonicalCookie(const CanonicalCookie& cookie) { + DCHECK(thread_checker_.CalledOnValidThread()); - int num_deleted = 0; - for (CookieMap::iterator it = cookies_.begin(); it != cookies_.end();) { - CookieMap::iterator curit = it; - CanonicalCookie* cc = curit->second; - ++it; - - if (!cc->IsPersistent()) { - InternalDeleteCookie(curit, true, /*sync_to_store*/ - DELETE_COOKIE_EXPIRED); - ++num_deleted; + for (CookieMapItPair its = cookies_.equal_range(GetKey(cookie.Domain())); + its.first != its.second; ++its.first) { + // The creation date acts as the unique index... + if (its.first->second->CreationDate() == cookie.CreationDate()) { + InternalDeleteCookie(its.first, true, DELETE_COOKIE_EXPLICIT); + return 1; } } - - return num_deleted; -} - -CookieMonster* CookieMonster::GetCookieMonster() { - return this; -} - -// This function must be called before the CookieMonster is used. -void CookieMonster::SetPersistSessionCookies(bool persist_session_cookies) { - DCHECK(!initialized_); - persist_session_cookies_ = persist_session_cookies; -} - -void CookieMonster::SetForceKeepSessionState() { - if (store_.get()) { - store_->SetForceKeepSessionState(); - } -} - -CookieMonster::~CookieMonster() { - DeleteAll(false); + return 0; } bool CookieMonster::SetCookieWithCreationTime(const GURL& url, const std::string& cookie_line, const base::Time& creation_time) { + DCHECK(thread_checker_.CalledOnValidThread()); DCHECK(!store_.get()) << "This method is only to be used by unit-tests."; - base::AutoLock autolock(lock_); if (!HasCookieableScheme(url)) { return false; @@ -1473,18 +1255,53 @@ bool CookieMonster::SetCookieWithCreationTime(const GURL& url, CookieOptions()); } +int CookieMonster::DeleteSessionCookies() { + DCHECK(thread_checker_.CalledOnValidThread()); + + int num_deleted = 0; + for (CookieMap::iterator it = cookies_.begin(); it != cookies_.end();) { + CookieMap::iterator curit = it; + CanonicalCookie* cc = curit->second; + ++it; + + if (!cc->IsPersistent()) { + InternalDeleteCookie(curit, true, /*sync_to_store*/ + DELETE_COOKIE_EXPIRED); + ++num_deleted; + } + } + + return num_deleted; +} + void CookieMonster::MarkCookieStoreAsInitialized() { + DCHECK(thread_checker_.CalledOnValidThread()); initialized_ = true; } void CookieMonster::FetchAllCookiesIfNecessary() { + DCHECK(thread_checker_.CalledOnValidThread()); if (store_.get() && !started_fetching_all_cookies_) { started_fetching_all_cookies_ = true; FetchAllCookies(); } } +void CookieMonster::FetchAllCookies() { + DCHECK(thread_checker_.CalledOnValidThread()); + DCHECK(store_.get()) << "Store must exist to initialize"; + DCHECK(!finished_fetching_all_cookies_) + << "All cookies have already been fetched."; + + // We bind in the current time so that we can report the wall-clock time for + // loading cookies. + store_->Load(base::Bind(&CookieMonster::OnLoaded, + weak_ptr_factory_.GetWeakPtr(), TimeTicks::Now())); +} + bool CookieMonster::ShouldFetchAllCookiesWhenFetchingAnyCookie() { + DCHECK(thread_checker_.CalledOnValidThread()); + if (fetch_strategy_ == kUnknownFetch) { const std::string group_name = base::FieldTrialList::FindFullName(kCookieMonsterFetchStrategyName); @@ -1502,18 +1319,9 @@ bool CookieMonster::ShouldFetchAllCookiesWhenFetchingAnyCookie() { return fetch_strategy_ == kAlwaysFetch; } -void CookieMonster::FetchAllCookies() { - DCHECK(store_.get()) << "Store must exist to initialize"; - DCHECK(!finished_fetching_all_cookies_) - << "All cookies have already been fetched."; - - // We bind in the current time so that we can report the wall-clock time for - // loading cookies. - store_->Load(base::Bind(&CookieMonster::OnLoaded, this, TimeTicks::Now())); -} - void CookieMonster::OnLoaded(TimeTicks beginning_time, const std::vector<CanonicalCookie*>& cookies) { + DCHECK(thread_checker_.CalledOnValidThread()); StoreLoadedCookies(cookies); histogram_time_blocked_on_load_->AddTime(TimeTicks::Now() - beginning_time); @@ -1523,52 +1331,45 @@ void CookieMonster::OnLoaded(TimeTicks beginning_time, void CookieMonster::OnKeyLoaded(const std::string& key, const std::vector<CanonicalCookie*>& cookies) { - // This function does its own separate locking. + DCHECK(thread_checker_.CalledOnValidThread()); + StoreLoadedCookies(cookies); - std::deque<scoped_refptr<CookieMonsterTask>> tasks_pending_for_key; + auto tasks_pending_for_key = tasks_pending_for_key_.find(key); - // We need to do this repeatedly until no more tasks were added to the queue - // during the period where we release the lock. - while (true) { - { - base::AutoLock autolock(lock_); - std::map<std::string, - std::deque<scoped_refptr<CookieMonsterTask>>>::iterator it = - tasks_pending_for_key_.find(key); - if (it == tasks_pending_for_key_.end()) { - keys_loaded_.insert(key); - return; - } - if (it->second.empty()) { - keys_loaded_.insert(key); - tasks_pending_for_key_.erase(it); - return; - } - it->second.swap(tasks_pending_for_key); - } + // TODO(mmenke): Can this be turned into a DCHECK? + if (tasks_pending_for_key == tasks_pending_for_key_.end()) + return; - while (!tasks_pending_for_key.empty()) { - scoped_refptr<CookieMonsterTask> task = tasks_pending_for_key.front(); - task->Run(); - tasks_pending_for_key.pop_front(); - } + // Run all tasks for the key. Note that running a task can result in multiple + // tasks being added to the back of the deque. + while (!tasks_pending_for_key->second.empty()) { + scoped_refptr<CookieMonsterTask> task = + tasks_pending_for_key->second.front(); + tasks_pending_for_key->second.pop_front(); + + task->Run(); } + + tasks_pending_for_key_.erase(tasks_pending_for_key); + + // This has to be done last, in case running a task queues a new task for the + // key, to ensure tasks are run in the correct order. + keys_loaded_.insert(key); } void CookieMonster::StoreLoadedCookies( const std::vector<CanonicalCookie*>& cookies) { + DCHECK(thread_checker_.CalledOnValidThread()); + // TODO(erikwright): Remove ScopedTracker below once crbug.com/457528 is // fixed. tracked_objects::ScopedTracker tracking_profile( FROM_HERE_WITH_EXPLICIT_FUNCTION( "457528 CookieMonster::StoreLoadedCookies")); - // Initialize the store and sync in any saved persistent cookies. We don't - // care if it's expired, insert it so it can be garbage collected, removed, - // and sync'd. - base::AutoLock autolock(lock_); - + // Even if a key is expired, insert it so it can be garbage collected, + // removed, and sync'd. CookieItVector cookies_with_control_chars; for (std::vector<CanonicalCookie*>::const_iterator it = cookies.begin(); @@ -1621,25 +1422,36 @@ void CookieMonster::StoreLoadedCookies( } void CookieMonster::InvokeQueue() { - while (true) { - scoped_refptr<CookieMonsterTask> request_task; - { - base::AutoLock autolock(lock_); - if (tasks_pending_.empty()) { - finished_fetching_all_cookies_ = true; - creation_times_.clear(); - keys_loaded_.clear(); - break; - } - request_task = tasks_pending_.front(); - tasks_pending_.pop(); - } + DCHECK(thread_checker_.CalledOnValidThread()); + + // Move all per-key tasks into the global queue, if there are any. This is + // protection about a race where the store learns about all cookies loading + // before it learned about the cookies for a key loading. + + // Needed to prevent any recursively queued tasks from going back into the + // per-key queues. + seen_global_task_ = true; + for (const auto& tasks_for_key : tasks_pending_for_key_) { + tasks_pending_.insert(tasks_pending_.begin(), tasks_for_key.second.begin(), + tasks_for_key.second.end()); + } + tasks_pending_for_key_.clear(); + + while (!tasks_pending_.empty()) { + scoped_refptr<CookieMonsterTask> request_task = tasks_pending_.front(); + tasks_pending_.pop_front(); request_task->Run(); } + + DCHECK(tasks_pending_for_key_.empty()); + + finished_fetching_all_cookies_ = true; + creation_times_.clear(); + keys_loaded_.clear(); } void CookieMonster::EnsureCookiesMapIsValid() { - lock_.AssertAcquired(); + DCHECK(thread_checker_.CalledOnValidThread()); // Iterate through all the of the cookies, grouped by host. CookieMap::iterator prev_range_end = cookies_.begin(); @@ -1657,7 +1469,7 @@ void CookieMonster::EnsureCookiesMapIsValid() { void CookieMonster::TrimDuplicateCookiesForKey(const std::string& key, CookieMap::iterator begin, CookieMap::iterator end) { - lock_.AssertAcquired(); + DCHECK(thread_checker_.CalledOnValidThread()); // Set of cookies ordered by creation time. typedef std::set<CookieMap::iterator, OrderByCreationTimeDesc> CookieSet; @@ -1729,27 +1541,11 @@ void CookieMonster::TrimDuplicateCookiesForKey(const std::string& key, DCHECK_EQ(num_duplicates, num_duplicates_found); } -// Note: file must be the last scheme. -const char* const CookieMonster::kDefaultCookieableSchemes[] = {"http", - "https", - "ws", - "wss", - "file"}; -const int CookieMonster::kDefaultCookieableSchemesCount = - arraysize(kDefaultCookieableSchemes); - -void CookieMonster::SetDefaultCookieableSchemes() { - // Always disable file scheme unless SetEnableFileScheme(true) is called. - SetCookieableSchemes(kDefaultCookieableSchemes, - kDefaultCookieableSchemesCount - 1); -} - void CookieMonster::FindCookiesForHostAndDomain( const GURL& url, const CookieOptions& options, - bool update_access_time, std::vector<CanonicalCookie*>* cookies) { - lock_.AssertAcquired(); + DCHECK(thread_checker_.CalledOnValidThread()); const Time current_time(CurrentTime()); @@ -1760,17 +1556,15 @@ void CookieMonster::FindCookiesForHostAndDomain( // Can just dispatch to FindCookiesForKey const std::string key(GetKey(url.host())); - FindCookiesForKey(key, url, options, current_time, update_access_time, - cookies); + FindCookiesForKey(key, url, options, current_time, cookies); } void CookieMonster::FindCookiesForKey(const std::string& key, const GURL& url, const CookieOptions& options, const Time& current, - bool update_access_time, std::vector<CanonicalCookie*>* cookies) { - lock_.AssertAcquired(); + DCHECK(thread_checker_.CalledOnValidThread()); for (CookieMapItPair its = cookies_.equal_range(key); its.first != its.second;) { @@ -1779,7 +1573,7 @@ void CookieMonster::FindCookiesForKey(const std::string& key, ++its.first; // If the cookie is expired, delete it. - if (cc->IsExpired(current) && !keep_expired_cookies_) { + if (cc->IsExpired(current)) { InternalDeleteCookie(curit, true, DELETE_COOKIE_EXPIRED); continue; } @@ -1792,7 +1586,7 @@ void CookieMonster::FindCookiesForKey(const std::string& key, // Add this cookie to the set of matching cookies. Update the access // time if we've been requested to do so. - if (update_access_time) { + if (options.update_access_time()) { InternalUpdateCookieAccessTime(cc, current); } cookies->push_back(cc); @@ -1804,7 +1598,7 @@ bool CookieMonster::DeleteAnyEquivalentCookie(const std::string& key, bool skip_httponly, bool already_expired, bool enforce_strict_secure) { - lock_.AssertAcquired(); + DCHECK(thread_checker_.CalledOnValidThread()); bool found_equivalent_cookie = false; bool skipped_httponly = false; @@ -1867,11 +1661,12 @@ CookieMonster::CookieMap::iterator CookieMonster::InternalInsertCookie( const std::string& key, CanonicalCookie* cc, bool sync_to_store) { + DCHECK(thread_checker_.CalledOnValidThread()); + // TODO(mkwst): Remove ScopedTracker below once crbug.com/456373 is fixed. tracked_objects::ScopedTracker tracking_profile( FROM_HERE_WITH_EXPLICIT_FUNCTION( "456373 CookieMonster::InternalInsertCookie")); - lock_.AssertAcquired(); if ((cc->IsPersistent() || persist_session_cookies_) && store_.get() && sync_to_store) @@ -1884,11 +1679,12 @@ CookieMonster::CookieMap::iterator CookieMonster::InternalInsertCookie( } // See InitializeHistograms() for details. - int32_t cookie_type_sample = - cc->IsFirstPartyOnly() ? 1 << COOKIE_TYPE_FIRSTPARTYONLY : 0; - cookie_type_sample |= cc->IsHttpOnly() ? 1 << COOKIE_TYPE_HTTPONLY : 0; - cookie_type_sample |= cc->IsSecure() ? 1 << COOKIE_TYPE_SECURE : 0; - histogram_cookie_type_->Add(cookie_type_sample); + int32_t type_sample = cc->SameSite() != CookieSameSite::NO_RESTRICTION + ? 1 << COOKIE_TYPE_SAME_SITE + : 0; + type_sample |= cc->IsHttpOnly() ? 1 << COOKIE_TYPE_HTTPONLY : 0; + type_sample |= cc->IsSecure() ? 1 << COOKIE_TYPE_SECURE : 0; + histogram_cookie_type_->Add(type_sample); // Histogram the type of scheme used on URLs that set cookies. This // intentionally includes cookies that are set or overwritten by @@ -1910,7 +1706,7 @@ CookieMonster::CookieMap::iterator CookieMonster::InternalInsertCookie( histogram_cookie_source_scheme_->Add(cookie_source_sample); } - RunCallbacks(*cc, false); + RunCookieChangedCallbacks(*cc, false); return inserted; } @@ -1920,7 +1716,7 @@ bool CookieMonster::SetCookieWithCreationTimeAndOptions( const std::string& cookie_line, const Time& creation_time_or_null, const CookieOptions& options) { - lock_.AssertAcquired(); + DCHECK(thread_checker_.CalledOnValidThread()); VLOG(kVlogSetCookies) << "SetCookie() line: " << cookie_line; @@ -1937,16 +1733,18 @@ bool CookieMonster::SetCookieWithCreationTimeAndOptions( VLOG(kVlogSetCookies) << "WARNING: Failed to allocate CanonicalCookie"; return false; } - return SetCanonicalCookie(&cc, creation_time, options); + return SetCanonicalCookie(std::move(cc), options); } -bool CookieMonster::SetCanonicalCookie(scoped_ptr<CanonicalCookie>* cc, - const Time& creation_time, +bool CookieMonster::SetCanonicalCookie(scoped_ptr<CanonicalCookie> cc, const CookieOptions& options) { - const std::string key(GetKey((*cc)->Domain())); - bool already_expired = (*cc)->IsExpired(creation_time); + DCHECK(thread_checker_.CalledOnValidThread()); - if (DeleteAnyEquivalentCookie(key, **cc, options.exclude_httponly(), + Time creation_time = cc->CreationDate(); + const std::string key(GetKey(cc->Domain())); + bool already_expired = cc->IsExpired(creation_time); + + if (DeleteAnyEquivalentCookie(key, *cc, options.exclude_httponly(), already_expired, options.enforce_strict_secure())) { std::string error; @@ -1963,21 +1761,18 @@ bool CookieMonster::SetCanonicalCookie(scoped_ptr<CanonicalCookie>* cc, } VLOG(kVlogSetCookies) << "SetCookie() key: " << key - << " cc: " << (*cc)->DebugString(); + << " cc: " << cc->DebugString(); // Realize that we might be setting an expired cookie, and the only point // was to delete the cookie which we've already done. - if (!already_expired || keep_expired_cookies_) { + if (!already_expired) { // See InitializeHistograms() for details. - if ((*cc)->IsPersistent()) { + if (cc->IsPersistent()) { histogram_expiration_duration_minutes_->Add( - ((*cc)->ExpiryDate() - creation_time).InMinutes()); + (cc->ExpiryDate() - creation_time).InMinutes()); } - { - CanonicalCookie cookie = *(cc->get()); - InternalInsertCookie(key, cc->release(), true); - } + InternalInsertCookie(key, cc.release(), true); } else { VLOG(kVlogSetCookies) << "SetCookie() not storing already expired cookie."; } @@ -1993,15 +1788,16 @@ bool CookieMonster::SetCanonicalCookie(scoped_ptr<CanonicalCookie>* cc, } bool CookieMonster::SetCanonicalCookies(const CookieList& list) { - base::AutoLock autolock(lock_); + DCHECK(thread_checker_.CalledOnValidThread()); CookieOptions options; options.set_include_httponly(); - for (CookieList::const_iterator it = list.begin(); it != list.end(); ++it) { - scoped_ptr<CanonicalCookie> canonical_cookie(new CanonicalCookie(*it)); - if (!SetCanonicalCookie(&canonical_cookie, it->CreationDate(), options)) + for (const auto& cookie : list) { + if (!SetCanonicalCookie(make_scoped_ptr(new CanonicalCookie(cookie)), + options)) { return false; + } } return true; @@ -2009,7 +1805,7 @@ bool CookieMonster::SetCanonicalCookies(const CookieList& list) { void CookieMonster::InternalUpdateCookieAccessTime(CanonicalCookie* cc, const Time& current) { - lock_.AssertAcquired(); + DCHECK(thread_checker_.CalledOnValidThread()); // Based off the Mozilla code. When a cookie has been accessed recently, // don't bother updating its access time again. This reduces the number of @@ -2028,7 +1824,7 @@ void CookieMonster::InternalUpdateCookieAccessTime(CanonicalCookie* cc, void CookieMonster::InternalDeleteCookie(CookieMap::iterator it, bool sync_to_store, DeletionCause deletion_cause) { - lock_.AssertAcquired(); + DCHECK(thread_checker_.CalledOnValidThread()); // Ideally, this would be asserted up where we define ChangeCauseMapping, // but DeletionCause's visibility (or lack thereof) forces us to make @@ -2054,39 +1850,17 @@ void CookieMonster::InternalDeleteCookie(CookieMap::iterator it, if (mapping.notify) delegate_->OnCookieChanged(*cc, true, mapping.cause); } - RunCallbacks(*cc, true); + RunCookieChangedCallbacks(*cc, true); cookies_.erase(it); delete cc; } -size_t CookieMonster::GarbageCollectLeastRecentlyAccessed( - const base::Time& current, - const base::Time& safe_date, - size_t purge_goal, - CookieItVector cookie_its) { - // Sorts up to *and including* |cookie_its[purge_goal]|, so - // |earliest_access_time| will be properly assigned even if - // |global_purge_it| == |cookie_its.begin() + purge_goal|. - SortLeastRecentlyAccessed(cookie_its.begin(), cookie_its.end(), purge_goal); - // Find boundary to cookies older than safe_date. - CookieItVector::iterator global_purge_it = LowerBoundAccessDate( - cookie_its.begin(), cookie_its.begin() + purge_goal, safe_date); - // Only delete the old cookies, and if strict secure is enabled, delete - // non-secure ones first. - size_t num_deleted = - GarbageCollectDeleteRange(current, DELETE_COOKIE_EVICTED_GLOBAL, - cookie_its.begin(), global_purge_it); - // Set access day to the oldest cookie that wasn't deleted. - earliest_access_time_ = (*global_purge_it)->second->LastAccessDate(); - return num_deleted; -} - // Domain expiry behavior is unchanged by key/expiry scheme (the // meaning of the key is different, but that's not visible to this routine). size_t CookieMonster::GarbageCollect(const Time& current, const std::string& key, bool enforce_strict_secure) { - lock_.AssertAcquired(); + DCHECK(thread_checker_.CalledOnValidThread()); size_t num_deleted = 0; Time safe_date(Time::Now() - TimeDelta::FromDays(kSafeFromGlobalPurgeDays)); @@ -2102,6 +1876,7 @@ size_t CookieMonster::GarbageCollect(const Time& current, num_deleted += GarbageCollectExpired(current, cookies_.equal_range(key), cookie_its); + // TODO(mkwst): Soften this. CookieItVector secure_cookie_its; if (enforce_strict_secure && cookie_its->size() > kDomainMaxCookies) { VLOG(kVlogGarbageCollection) << "Garbage collecting non-Secure cookies."; @@ -2116,56 +1891,27 @@ size_t CookieMonster::GarbageCollect(const Time& current, cookie_its->size() - (kDomainMaxCookies - kDomainPurgeCookies); DCHECK(purge_goal > kDomainPurgeCookies); - // Boundary iterators into |cookie_its| for different priorities. - CookieItVector::iterator it_bdd[4]; - // Intialize |it_bdd| while sorting |cookie_its| by priorities. - // Schematic: [MLLHMHHLMM] => [LLL|MMMM|HHH], with 4 boundaries. - it_bdd[0] = cookie_its->begin(); - it_bdd[3] = cookie_its->end(); - it_bdd[1] = - PartitionCookieByPriority(it_bdd[0], it_bdd[3], COOKIE_PRIORITY_LOW); - it_bdd[2] = PartitionCookieByPriority(it_bdd[1], it_bdd[3], - COOKIE_PRIORITY_MEDIUM); - size_t quota[3] = {kDomainCookiesQuotaLow, - kDomainCookiesQuotaMedium, - kDomainCookiesQuotaHigh}; - - // Purge domain cookies in 3 rounds. - // Round 1: consider low-priority cookies only: evict least-recently - // accessed, while protecting quota[0] of these from deletion. - // Round 2: consider {low, medium}-priority cookies, evict least-recently - // accessed, while protecting quota[0] + quota[1]. - // Round 3: consider all cookies, evict least-recently accessed. - size_t accumulated_quota = 0; - CookieItVector::iterator it_purge_begin = it_bdd[0]; - for (int i = 0; i < 3 && purge_goal > 0; ++i) { - accumulated_quota += quota[i]; - - size_t num_considered = it_bdd[i + 1] - it_purge_begin; - if (num_considered <= accumulated_quota) - continue; - - // Number of cookies that will be purged in this round. - size_t round_goal = - std::min(purge_goal, num_considered - accumulated_quota); - purge_goal -= round_goal; - - SortLeastRecentlyAccessed(it_purge_begin, it_bdd[i + 1], round_goal); - // Cookies accessed on or after |safe_date| would have been safe from - // global purge, and we want to keep track of this. - CookieItVector::iterator it_purge_end = it_purge_begin + round_goal; - CookieItVector::iterator it_purge_middle = - LowerBoundAccessDate(it_purge_begin, it_purge_end, safe_date); - // Delete cookies accessed before |safe_date|. - num_deleted += GarbageCollectDeleteRange( - current, DELETE_COOKIE_EVICTED_DOMAIN_PRE_SAFE, it_purge_begin, - it_purge_middle); - // Delete cookies accessed on or after |safe_date|. - num_deleted += GarbageCollectDeleteRange( - current, DELETE_COOKIE_EVICTED_DOMAIN_POST_SAFE, it_purge_middle, - it_purge_end); - it_purge_begin = it_purge_end; + // Sort the cookies by access date, from least-recent to most-recent. + std::sort(cookie_its->begin(), cookie_its->end(), LRACookieSorter); + + // Remove all but the kDomainCookiesQuotaLow most-recently accessed + // cookies with low-priority. Then, if cookies still need to be removed, + // bump the quota and remove low- and medium-priority. Then, if cookies + // _still_ need to be removed, bump the quota and remove cookies with + // any priority. + const size_t kQuotas[3] = {kDomainCookiesQuotaLow, + kDomainCookiesQuotaMedium, + kDomainCookiesQuotaHigh}; + size_t quota = 0; + for (size_t i = 0; i < arraysize(kQuotas) && purge_goal > 0; i++) { + quota += kQuotas[i]; + size_t just_deleted = PurgeLeastRecentMatches( + cookie_its, static_cast<CookiePriority>(i), quota, purge_goal); + DCHECK_LE(just_deleted, purge_goal); + purge_goal -= just_deleted; + num_deleted += just_deleted; } + DCHECK_EQ(0U, purge_goal); } } @@ -2213,13 +1959,49 @@ size_t CookieMonster::GarbageCollect(const Time& current, return num_deleted; } +size_t CookieMonster::PurgeLeastRecentMatches(CookieItVector* cookies, + CookiePriority priority, + size_t to_protect, + size_t purge_goal) { + DCHECK(thread_checker_.CalledOnValidThread()); + + // Find the first protected cookie by walking down from the end of the list + // cookie list (most-recently accessed) until |to_protect| cookies that match + // |priority| are found. + size_t protection_boundary = cookies->size(); + while (to_protect > 0 && protection_boundary > 0) { + protection_boundary--; + if (cookies->at(protection_boundary)->second->Priority() <= priority) + to_protect--; + } + + // Now, walk up from the beginning of the list (least-recently accessed) until + // |purge_goal| cookies are removed, or the iterator hits + // |protection_boundary|. + size_t removed = 0; + size_t current = 0; + while (removed < purge_goal && current < protection_boundary) { + if (cookies->at(current)->second->Priority() <= priority) { + InternalDeleteCookie(cookies->at(current), true, + DELETE_COOKIE_EVICTED_DOMAIN); + cookies->erase(cookies->begin() + current); + removed++; + + // The call to 'erase' above shifts the contents of the vector, but + // doesn't shift |protection_boundary|. Decrement that here to ensure that + // the correct set of cookies is protected. + protection_boundary--; + } else { + current++; + } + } + return removed; +} + size_t CookieMonster::GarbageCollectExpired(const Time& current, const CookieMapItPair& itpair, CookieItVector* cookie_its) { - if (keep_expired_cookies_) - return 0; - - lock_.AssertAcquired(); + DCHECK(thread_checker_.CalledOnValidThread()); int num_deleted = 0; for (CookieMap::iterator it = itpair.first, end = itpair.second; it != end;) { @@ -2240,7 +2022,7 @@ size_t CookieMonster::GarbageCollectExpired(const Time& current, size_t CookieMonster::GarbageCollectNonSecure( const CookieItVector& valid_cookies, CookieItVector* cookie_its) { - lock_.AssertAcquired(); + DCHECK(thread_checker_.CalledOnValidThread()); size_t num_deleted = 0; for (const auto& curr_cookie_it : valid_cookies) { @@ -2260,6 +2042,8 @@ size_t CookieMonster::GarbageCollectDeleteRange( DeletionCause cause, CookieItVector::iterator it_begin, CookieItVector::iterator it_end) { + DCHECK(thread_checker_.CalledOnValidThread()); + for (CookieItVector::iterator it = it_begin; it != it_end; it++) { histogram_evicted_last_access_minutes_->Add( (current - (*it)->second->LastAccessDate()).InMinutes()); @@ -2268,6 +2052,30 @@ size_t CookieMonster::GarbageCollectDeleteRange( return it_end - it_begin; } +size_t CookieMonster::GarbageCollectLeastRecentlyAccessed( + const base::Time& current, + const base::Time& safe_date, + size_t purge_goal, + CookieItVector cookie_its) { + DCHECK(thread_checker_.CalledOnValidThread()); + + // Sorts up to *and including* |cookie_its[purge_goal]|, so + // |earliest_access_time| will be properly assigned even if + // |global_purge_it| == |cookie_its.begin() + purge_goal|. + SortLeastRecentlyAccessed(cookie_its.begin(), cookie_its.end(), purge_goal); + // Find boundary to cookies older than safe_date. + CookieItVector::iterator global_purge_it = LowerBoundAccessDate( + cookie_its.begin(), cookie_its.begin() + purge_goal, safe_date); + // Only delete the old cookies, and if strict secure is enabled, delete + // non-secure ones first. + size_t num_deleted = + GarbageCollectDeleteRange(current, DELETE_COOKIE_EVICTED_GLOBAL, + cookie_its.begin(), global_purge_it); + // Set access day to the oldest cookie that wasn't deleted. + earliest_access_time_ = (*global_purge_it)->second->LastAccessDate(); + return num_deleted; +} + // A wrapper around registry_controlled_domains::GetDomainAndRegistry // to make clear we're creating a key for our local map. Here and // in FindCookiesForHostAndDomain() are the only two places where @@ -2292,6 +2100,8 @@ size_t CookieMonster::GarbageCollectDeleteRange( // be worth it, but is still too much trouble to solve what is currently a // non-problem). std::string CookieMonster::GetKey(const std::string& domain) const { + DCHECK(thread_checker_.CalledOnValidThread()); + std::string effective_domain( registry_controlled_domains::GetDomainAndRegistry( domain, registry_controlled_domains::INCLUDE_PRIVATE_REGISTRIES)); @@ -2303,15 +2113,8 @@ std::string CookieMonster::GetKey(const std::string& domain) const { return effective_domain; } -bool CookieMonster::IsCookieableScheme(const std::string& scheme) { - base::AutoLock autolock(lock_); - - return std::find(cookieable_schemes_.begin(), cookieable_schemes_.end(), - scheme) != cookieable_schemes_.end(); -} - bool CookieMonster::HasCookieableScheme(const GURL& url) { - lock_.AssertAcquired(); + DCHECK(thread_checker_.CalledOnValidThread()); // Make sure the request is on a cookie-able url scheme. for (size_t i = 0; i < cookieable_schemes_.size(); ++i) { @@ -2337,6 +2140,8 @@ bool CookieMonster::HasCookieableScheme(const GURL& url) { // in the constructor so that we won't take statistics right after // startup, to avoid bias from browsers that are started but not used. void CookieMonster::RecordPeriodicStats(const base::Time& current_time) { + DCHECK(thread_checker_.CalledOnValidThread()); + const base::TimeDelta kRecordStatisticsIntervalTime( base::TimeDelta::FromSeconds(kRecordStatisticsIntervalSeconds)); @@ -2377,6 +2182,8 @@ void CookieMonster::RecordPeriodicStats(const base::Time& current_time) { // methods where needed. The specific histogram macro calls on which the // initialization is based are included in comments below. void CookieMonster::InitializeHistograms() { + DCHECK(thread_checker_.CalledOnValidThread()); + // From UMA_HISTOGRAM_CUSTOM_COUNTS histogram_expiration_duration_minutes_ = base::Histogram::FactoryGet( "Cookie.ExpirationDurationMinutes", 1, kMinutesInTenYears, 50, @@ -2418,10 +2225,70 @@ Time CookieMonster::CurrentTime() { last_time_seen_.ToInternalValue() + 1)); } +void CookieMonster::DoCookieTask( + const scoped_refptr<CookieMonsterTask>& task_item) { + DCHECK(thread_checker_.CalledOnValidThread()); + + MarkCookieStoreAsInitialized(); + FetchAllCookiesIfNecessary(); + seen_global_task_ = true; + + if (!finished_fetching_all_cookies_ && store_.get()) { + tasks_pending_.push_back(task_item); + return; + } + + task_item->Run(); +} + +void CookieMonster::DoCookieTaskForURL( + const scoped_refptr<CookieMonsterTask>& task_item, + const GURL& url) { + MarkCookieStoreAsInitialized(); + if (ShouldFetchAllCookiesWhenFetchingAnyCookie()) + FetchAllCookiesIfNecessary(); + + // If cookies for the requested domain key (eTLD+1) have been loaded from DB + // then run the task, otherwise load from DB. + if (!finished_fetching_all_cookies_ && store_.get()) { + // If a global task has been previously seen, queue the task as a global + // task. Note that the CookieMonster may be in the middle of executing + // the global queue, |tasks_pending_| may be empty, which is why another + // bool is needed. + if (seen_global_task_) { + tasks_pending_.push_back(task_item); + return; + } + + // Checks if the domain key has been loaded. + std::string key(cookie_util::GetEffectiveDomain(url.scheme(), url.host())); + if (keys_loaded_.find(key) == keys_loaded_.end()) { + std::map<std::string, + std::deque<scoped_refptr<CookieMonsterTask>>>::iterator it = + tasks_pending_for_key_.find(key); + if (it == tasks_pending_for_key_.end()) { + store_->LoadCookiesForKey( + key, base::Bind(&CookieMonster::OnKeyLoaded, + weak_ptr_factory_.GetWeakPtr(), key)); + it = tasks_pending_for_key_ + .insert(std::make_pair( + key, std::deque<scoped_refptr<CookieMonsterTask>>())) + .first; + } + it->second.push_back(task_item); + return; + } + } + + task_item->Run(); +} + void CookieMonster::ComputeCookieDiff(CookieList* old_cookies, CookieList* new_cookies, CookieList* cookies_to_add, CookieList* cookies_to_delete) { + DCHECK(thread_checker_.CalledOnValidThread()); + DCHECK(old_cookies); DCHECK(new_cookies); DCHECK(cookies_to_add); @@ -2451,45 +2318,23 @@ void CookieMonster::ComputeCookieDiff(CookieList* old_cookies, FullDiffCookieSorter); } -scoped_ptr<CookieStore::CookieChangedSubscription> -CookieMonster::AddCallbackForCookie(const GURL& gurl, - const std::string& name, - const CookieChangedCallback& callback) { - base::AutoLock autolock(lock_); - std::pair<GURL, std::string> key(gurl, name); - if (hook_map_.count(key) == 0) - hook_map_[key] = make_linked_ptr(new CookieChangedCallbackList()); - return hook_map_[key]->Add( - base::Bind(&RunAsync, base::ThreadTaskRunnerHandle::Get(), callback)); +void CookieMonster::RunCallback(const base::Closure& callback) { + DCHECK(thread_checker_.CalledOnValidThread()); + callback.Run(); } -#if defined(OS_ANDROID) -void CookieMonster::SetEnableFileScheme(bool accept) { - // This assumes "file" is always at the end of the array. See the comment - // above kDefaultCookieableSchemes. - // - // TODO(mkwst): We're keeping this method around to support the - // 'CookieManager::setAcceptFileSchemeCookies' method on Android's WebView; - // if/when we can deprecate and remove that method, we can remove this one - // as well. Until then, we'll just ensure that the method has no effect on - // non-android systems. - int num_schemes = accept ? kDefaultCookieableSchemesCount - : kDefaultCookieableSchemesCount - 1; - - SetCookieableSchemes(kDefaultCookieableSchemes, num_schemes); -} -#endif +void CookieMonster::RunCookieChangedCallbacks(const CanonicalCookie& cookie, + bool removed) { + DCHECK(thread_checker_.CalledOnValidThread()); -void CookieMonster::RunCallbacks(const CanonicalCookie& cookie, bool removed) { - lock_.AssertAcquired(); CookieOptions opts; opts.set_include_httponly(); - opts.set_include_first_party_only_cookies(); - // Note that the callbacks in hook_map_ are wrapped with MakeAsync(), so they + opts.set_same_site_cookie_mode( + CookieOptions::SameSiteCookieMode::INCLUDE_STRICT_AND_LAX); + // Note that the callbacks in hook_map_ are wrapped with RunAsync(), so they // are guaranteed to not take long - they just post a RunAsync task back to - // the appropriate thread's message loop and return. It is important that this - // method not run user-supplied callbacks directly, since the CookieMonster - // lock is held and it is easy to accidentally introduce deadlocks. + // the appropriate thread's message loop and return. + // TODO(mmenke): Consider running these synchronously? for (CookieChangedHookMap::iterator it = hook_map_.begin(); it != hook_map_.end(); ++it) { std::pair<GURL, std::string> key = it->first; diff --git a/chromium/net/cookies/cookie_monster.h b/chromium/net/cookies/cookie_monster.h index 94c3813b4a2..12e04942c8c 100644 --- a/chromium/net/cookies/cookie_monster.h +++ b/chromium/net/cookies/cookie_monster.h @@ -24,7 +24,8 @@ #include "base/memory/linked_ptr.h" #include "base/memory/ref_counted.h" #include "base/memory/scoped_ptr.h" -#include "base/synchronization/lock.h" +#include "base/memory/weak_ptr.h" +#include "base/threading/thread_checker.h" #include "base/time/time.h" #include "net/base/net_export.h" #include "net/cookies/canonical_cookie.h" @@ -48,13 +49,8 @@ class ParsedCookie; // 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). +// Tasks may be deferred if all affected cookies are not yet loaded from the +// backing store. Otherwise, callbacks may be invoked immediately. // // 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 @@ -63,8 +59,6 @@ class ParsedCookie; // task will be queued in tasks_pending_for_key_ 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 { @@ -148,148 +142,66 @@ class NET_EXPORT CookieMonster : public CookieStore { CookieMonsterDelegate* delegate, int last_access_threshold_milliseconds); - // Helper function that adds all cookies from |list| into this instance, - // overwriting any equivalent cookies. - bool ImportCookies(const CookieList& list); + ~CookieMonster() override; - typedef base::Callback<void(const CookieList& cookies)> GetCookieListCallback; - typedef base::Callback<void(bool success)> DeleteCookieCallback; + // Replaces all the cookies by |list|. This method does not flush the backend. + void SetAllCookiesAsync(const CookieList& list, + const SetCookiesCallback& callback); - // 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. + // CookieStore implementation. + void SetCookieWithOptionsAsync(const GURL& url, + const std::string& cookie_line, + const CookieOptions& options, + const SetCookiesCallback& callback) override; 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, + base::Time creation_time, + base::Time expiration_time, + base::Time last_access_time, bool secure, bool http_only, - bool first_party, - bool enforce_prefixes, + CookieSameSite same_site, bool enforce_strict_secure, CookiePriority priority, - 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); - - // 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); - - // Resets the list of cookieable schemes to the supplied schemes. Does - // nothing if called after first use of the instance (i.e. after the - // instance initialization process). - void SetCookieableSchemes(const char* const 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(); - - // Protects session cookies from deletion on shutdown. - void SetForceKeepSessionState(); - - // 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); - - // Replaces all the cookies by |list|. This method does not flush the backend. - void SetAllCookiesAsync(const CookieList& list, - const SetCookiesCallback& callback); - - // CookieStore implementation. - - // Sets the cookies specified by |cookie_list| returned from |url| - // with options |options| in effect. - 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. void GetCookiesWithOptionsAsync(const GURL& url, const CookieOptions& options, const GetCookiesCallback& callback) override; - - // Invokes GetAllCookiesForURLWithOptions with options set to include HTTP - // only cookies. - void GetAllCookiesForURLAsync(const GURL& url, - const GetCookieListCallback& callback) override; - - // Deletes all cookies with that might apply to |url| that has |cookie_name|. + void GetCookieListWithOptionsAsync( + const GURL& url, + const CookieOptions& options, + const GetCookieListCallback& callback) override; + void GetAllCookiesAsync(const GetCookieListCallback& callback) override; 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. + void DeleteCanonicalCookieAsync(const CanonicalCookie& cookie, + const DeleteCallback& callback) override; void DeleteAllCreatedBetweenAsync(const base::Time& delete_begin, const base::Time& delete_end, const DeleteCallback& callback) override; - - // Deletes all of the cookies that match the host of the given URL - // regardless of path and that have a creation_date greater than or - // equal to |delete_begin| and less then |delete_end|. 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 DeleteAllCreatedBetweenForHostAsync( const base::Time delete_begin, const base::Time delete_end, const GURL& url, const DeleteCallback& callback) override; - void DeleteSessionCookiesAsync(const DeleteCallback&) override; + void FlushStore(const base::Closure& callback) override; + void SetForceKeepSessionState() override; - CookieMonster* GetCookieMonster() override; + // Resets the list of cookieable schemes to the supplied schemes. Does + // nothing if called after first use of the instance (i.e. after the + // instance initialization process). + void SetCookieableSchemes(const std::vector<std::string>& schemes); // 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); - // 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); - // Determines if the scheme of the URL is a scheme that cookies will be // stored for. bool IsCookieableScheme(const std::string& scheme); @@ -303,21 +215,7 @@ class NET_EXPORT CookieMonster : public CookieStore { const std::string& name, const CookieChangedCallback& callback) override; -#if defined(OS_ANDROID) - // Resets the list of cookieable schemes to kDefaultCookieableSchemes with or - // without 'file' being included. - // - // There are some unknowns about how to correctly handle file:// cookies, - // and our implementation for this is not robust enough (Bug 1157243). - // This allows you to enable support, and is exposed as a public WebView - // API ('CookieManager::setAcceptFileSchemeCookies'). - // - // TODO(mkwst): This method will be removed once we can deprecate and remove - // the Android WebView 'CookieManager::setAcceptFileSchemeCookies' method. - // Until then, this method only has effect on Android, and must not be used - // outside a WebView context. - void SetEnableFileScheme(bool accept); -#endif + bool IsEphemeral() override; private: // For queueing the cookie monster calls. @@ -326,13 +224,12 @@ class NET_EXPORT CookieMonster : public CookieStore { class DeleteTask; class DeleteAllCreatedBetweenTask; class DeleteAllCreatedBetweenForHostTask; - class DeleteAllForHostTask; - class DeleteAllTask; class DeleteCookieTask; class DeleteCanonicalCookieTask; - class GetAllCookiesForURLWithOptionsTask; + class GetCookieListForURLWithOptionsTask; class GetAllCookiesTask; class GetCookiesWithOptionsTask; + class GetCookieListWithOptionsTask; class SetAllCookiesTask; class SetCookieWithDetailsTask; class SetCookieWithOptionsTask; @@ -342,9 +239,6 @@ class NET_EXPORT CookieMonster : public CookieStore { // For SetCookieWithCreationTime. FRIEND_TEST_ALL_PREFIXES(CookieMonsterTest, TestCookieDeleteAllCreatedBetweenTimestamps); - // For SetCookieWithCreationTime. - FRIEND_TEST_ALL_PREFIXES(MultiThreadedCookieMonsterTest, - ThreadCheckDeleteAllCreatedBetweenForHost); // For gargage collection constants. FRIEND_TEST_ALL_PREFIXES(CookieMonsterTest, TestHostGarbageCollection); @@ -386,37 +280,37 @@ class NET_EXPORT CookieMonster : public CookieStore { // 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, + DELETE_COOKIE_OVERWRITE = 1, + DELETE_COOKIE_EXPIRED = 2, + DELETE_COOKIE_EVICTED = 3, + DELETE_COOKIE_DUPLICATE_IN_BACKING_STORE = 4, + DELETE_COOKIE_DONT_RECORD = 5, // For final cleanup after flush to store. + + // Cookies evicted during domain-level garbage collection. + DELETE_COOKIE_EVICTED_DOMAIN = 6, + + // Cookies evicted during global garbage collection (which takes place after + // domain-level garbage collection fails to bring the cookie store under + // the overall quota. + DELETE_COOKIE_EVICTED_GLOBAL = 7, + + // #8 was DELETE_COOKIE_EVICTED_DOMAIN_PRE_SAFE + // #9 was 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_EXPIRED_OVERWRITE = 10, // Cookies are not allowed to contain control characters in the name or // value. However, we used to allow them, so we are now evicting any such // cookies as we load them. See http://crbug.com/238041. - DELETE_COOKIE_CONTROL_CHAR, + DELETE_COOKIE_CONTROL_CHAR = 11, // When strict secure cookies is enabled, non-secure cookies are evicted // right after expired cookies. - DELETE_COOKIE_NON_SECURE, + DELETE_COOKIE_NON_SECURE = 12, - DELETE_COOKIE_LAST_ENTRY + DELETE_COOKIE_LAST_ENTRY = 13 }; // This enum is used to generate a histogramed bitmask measureing the types @@ -424,7 +318,7 @@ class NET_EXPORT CookieMonster : public CookieStore { // New items MUST be added at the end of the list, just before // COOKIE_TYPE_LAST_ENTRY; enum CookieType { - COOKIE_TYPE_FIRSTPARTYONLY = 0, + COOKIE_TYPE_SAME_SITE = 0, COOKIE_TYPE_HTTPONLY, COOKIE_TYPE_SECURE, COOKIE_TYPE_LAST_ENTRY @@ -487,8 +381,6 @@ class NET_EXPORT CookieMonster : public CookieStore { // Record statistics every kRecordStatisticsIntervalSeconds of uptime. static const int kRecordStatisticsIntervalSeconds = 10 * 60; - ~CookieMonster() override; - // 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). @@ -497,31 +389,27 @@ class NET_EXPORT CookieMonster : public CookieStore { const std::string& value, const std::string& domain, const std::string& path, - const base::Time& expiration_time, + base::Time creation_time, + base::Time expiration_time, + base::Time last_access_time, bool secure, bool http_only, - bool first_party, - bool enforce_prefixes, + CookieSameSite same_site, bool enforce_strict_secure, CookiePriority priority); CookieList GetAllCookies(); - CookieList GetAllCookiesForURLWithOptions(const GURL& url, - const CookieOptions& options); - - int DeleteAll(bool sync_to_store); + CookieList GetCookieListWithOptions(const GURL& url, + const CookieOptions& options); int DeleteAllCreatedBetween(const base::Time& delete_begin, const base::Time& delete_end); - int DeleteAllForHost(const GURL& url); int DeleteAllCreatedBetweenForHost(const base::Time delete_begin, const base::Time delete_end, const GURL& url); - bool DeleteCanonicalCookie(const CanonicalCookie& cookie); - bool SetCookieWithOptions(const GURL& url, const std::string& cookie_line, const CookieOptions& options); @@ -531,6 +419,8 @@ class NET_EXPORT CookieMonster : public CookieStore { void DeleteCookie(const GURL& url, const std::string& cookie_name); + int DeleteCanonicalCookie(const CanonicalCookie& cookie); + bool SetCookieWithCreationTime(const GURL& url, const std::string& cookie_line, const base::Time& creation_time); @@ -543,11 +433,9 @@ class NET_EXPORT CookieMonster : public CookieStore { // Fetches all cookies if the backing store exists and they're not already // being fetched. - // Note: this method should always be called with lock_ held. void FetchAllCookiesIfNecessary(); // Fetches all cookies from the backing store. - // Note: this method should always be called with lock_ held. void FetchAllCookies(); // Whether all cookies should be fetched as soon as any is requested. @@ -588,14 +476,12 @@ class NET_EXPORT CookieMonster : public CookieStore { 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). @@ -628,8 +514,7 @@ class NET_EXPORT CookieMonster : public CookieStore { // Helper function that sets a canonical cookie, deleting equivalents and // performing garbage collection. - bool SetCanonicalCookie(scoped_ptr<CanonicalCookie>* cc, - const base::Time& creation_time, + bool SetCanonicalCookie(scoped_ptr<CanonicalCookie> cc, const CookieOptions& options); // Helper function calling SetCanonicalCookie() for all cookies in |list|. @@ -656,6 +541,18 @@ class NET_EXPORT CookieMonster : public CookieStore { const std::string& key, bool enforce_strict_secure); + // Helper for GarbageCollect(). Deletes up to |purge_goal| cookies with a + // priority less than or equal to |priority| from |cookies|, while ensuring + // that at least the |to_protect| most-recent cookies are retained. + // + // |cookies| must be sorted from least-recent to most-recent. + // + // Returns the number of cookies deleted. + size_t PurgeLeastRecentMatches(CookieItVector* cookies, + CookiePriority priority, + size_t to_protect, + size_t purge_goal); + // Helper for GarbageCollect(); can be called directly as well. Deletes all // expired cookies in |itpair|. If |cookie_its| is non-NULL, all the // non-expired cookies from |itpair| are appended to |cookie_its|. @@ -727,9 +624,13 @@ class NET_EXPORT CookieMonster : public CookieStore { CookieList* cookies_to_add, CookieList* cookies_to_delete); + // Runs the given callback. Used to avoid running callbacks after the store + // has been destroyed. + void RunCallback(const base::Closure& callback); + // Run all cookie changed callbacks that are monitoring |cookie|. // |removed| is true if the cookie was deleted. - void RunCallbacks(const CanonicalCookie& cookie, bool removed); + void RunCookieChangedCallbacks(const CanonicalCookie& cookie, bool removed); // Histogram variables; see CookieMonster::InitializeHistograms() in // cookie_monster.cc for details. @@ -765,7 +666,14 @@ class NET_EXPORT CookieMonster : public CookieStore { // Queues tasks that are blocked until all cookies are loaded from the backend // store. - std::queue<scoped_refptr<CookieMonsterTask>> tasks_pending_; + std::deque<scoped_refptr<CookieMonsterTask>> tasks_pending_; + + // Once a global cookie task has been seen, all per-key tasks must be put in + // |tasks_pending_| instead of |tasks_pending_for_key_| to ensure a reasonable + // view of the cookie store. This more to ensure fancy cookie export/import + // code has a consistent view of the CookieStore, rather than out of concern + // for typical use. + bool seen_global_task_; scoped_refptr<PersistentCookieStore> store_; @@ -796,23 +704,18 @@ class NET_EXPORT CookieMonster : public CookieStore { scoped_refptr<CookieMonsterDelegate> delegate_; - // Lock for thread-safety - base::Lock lock_; - base::Time last_statistic_record_time_; - bool keep_expired_cookies_; bool persist_session_cookies_; - // Static setting for whether or not file scheme cookies are allows when - // a new CookieMonster is created, or the accepted schemes on a CookieMonster - // instance are reset back to defaults. - static bool default_enable_file_scheme_; - typedef std::map<std::pair<GURL, std::string>, linked_ptr<CookieChangedCallbackList>> CookieChangedHookMap; CookieChangedHookMap hook_map_; + base::ThreadChecker thread_checker_; + + base::WeakPtrFactory<CookieMonster> weak_ptr_factory_; + DISALLOW_COPY_AND_ASSIGN(CookieMonster); }; @@ -869,6 +772,8 @@ class NET_EXPORT CookieMonster::PersistentCookieStore // 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. + // + // |loaded_callback| may not be NULL. virtual void Load(const LoadedCallback& loaded_callback) = 0; // Does a priority load of all cookies for the domain key (eTLD+1). The @@ -876,6 +781,8 @@ class NET_EXPORT CookieMonster::PersistentCookieStore // 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. + // + // |loaded_callback| may not be NULL. virtual void LoadCookiesForKey(const std::string& key, const LoadedCallback& loaded_callback) = 0; @@ -886,7 +793,8 @@ class NET_EXPORT CookieMonster::PersistentCookieStore // Instructs the store to not discard session only cookies on shutdown. virtual void SetForceKeepSessionState() = 0; - // Flushes the store and posts |callback| when complete. + // Flushes the store and posts |callback| when complete. |callback| may be + // NULL. virtual void Flush(const base::Closure& callback) = 0; protected: diff --git a/chromium/net/cookies/cookie_monster_perftest.cc b/chromium/net/cookies/cookie_monster_perftest.cc index dd35638ead3..1dcb2769733 100644 --- a/chromium/net/cookies/cookie_monster_perftest.cc +++ b/chromium/net/cookies/cookie_monster_perftest.cc @@ -5,6 +5,8 @@ #include <algorithm> #include "base/bind.h" +#include "base/memory/ref_counted.h" +#include "base/memory/scoped_ptr.h" #include "base/message_loop/message_loop.h" #include "base/strings/string_util.h" #include "base/strings/stringprintf.h" @@ -119,7 +121,7 @@ TEST(ParsedCookieTest, TestParseBigCookies) { } TEST_F(CookieMonsterTest, TestAddCookiesOnSingleHost) { - scoped_refptr<CookieMonster> cm(new CookieMonster(NULL, NULL)); + scoped_ptr<CookieMonster> cm(new CookieMonster(nullptr, nullptr)); std::vector<std::string> cookies; for (int i = 0; i < kNumCookies; i++) { cookies.push_back(base::StringPrintf("a%03d=b", i)); @@ -152,7 +154,7 @@ TEST_F(CookieMonsterTest, TestAddCookiesOnSingleHost) { } TEST_F(CookieMonsterTest, TestAddCookieOnManyHosts) { - scoped_refptr<CookieMonster> cm(new CookieMonster(NULL, NULL)); + scoped_ptr<CookieMonster> cm(new CookieMonster(nullptr, nullptr)); std::string cookie(kCookieLine); std::vector<GURL> gurls; // just wanna have ffffuunnn for (int i = 0; i < kNumCookies; ++i) { @@ -185,7 +187,7 @@ TEST_F(CookieMonsterTest, TestAddCookieOnManyHosts) { } TEST_F(CookieMonsterTest, TestDomainTree) { - scoped_refptr<CookieMonster> cm(new CookieMonster(NULL, NULL)); + scoped_ptr<CookieMonster> cm(new CookieMonster(nullptr, nullptr)); GetCookiesCallback getCookiesCallback; SetCookieCallback setCookieCallback; const char domain_cookie_format_tree[] = "a=b; domain=%s"; @@ -238,7 +240,7 @@ TEST_F(CookieMonsterTest, TestDomainTree) { } TEST_F(CookieMonsterTest, TestDomainLine) { - scoped_refptr<CookieMonster> cm(new CookieMonster(NULL, NULL)); + scoped_ptr<CookieMonster> cm(new CookieMonster(nullptr, nullptr)); SetCookieCallback setCookieCallback; GetCookiesCallback getCookiesCallback; std::vector<std::string> domain_list; @@ -287,8 +289,7 @@ TEST_F(CookieMonsterTest, TestImport) { int64_t time_tick(base::Time::Now().ToInternalValue()); for (int domain_num = 0; domain_num < 300; domain_num++) { - std::string domain_name(base::StringPrintf(".Domain_%d.com", domain_num)); - std::string gurl("www" + domain_name); + GURL gurl(base::StringPrintf("http://www.Domain_%d.com", domain_num)); for (int cookie_num = 0; cookie_num < 50; cookie_num++) { std::string cookie_line( base::StringPrintf("Cookie_%d=1; Path=/", cookie_num)); @@ -300,7 +301,7 @@ TEST_F(CookieMonsterTest, TestImport) { store->SetLoadExpectation(true, initial_cookies); - scoped_refptr<CookieMonster> cm(new CookieMonster(store.get(), NULL)); + scoped_ptr<CookieMonster> cm(new CookieMonster(store.get(), nullptr)); // Import will happen on first access. GURL gurl("www.google.com"); @@ -314,7 +315,7 @@ TEST_F(CookieMonsterTest, TestImport) { } TEST_F(CookieMonsterTest, TestGetKey) { - scoped_refptr<CookieMonster> cm(new CookieMonster(NULL, NULL)); + scoped_ptr<CookieMonster> cm(new CookieMonster(nullptr, nullptr)); base::PerfTimeLogger timer("Cookie_monster_get_key"); for (int i = 0; i < kNumCookies; i++) cm->GetKey("www.google.com"); @@ -370,9 +371,9 @@ TEST_F(CookieMonsterTest, TestGCTimes) { }; for (int ci = 0; ci < static_cast<int>(arraysize(test_cases)); ++ci) { const TestCase& test_case(test_cases[ci]); - scoped_refptr<CookieMonster> cm(CreateMonsterFromStoreForGC( + scoped_ptr<CookieMonster> cm = CreateMonsterFromStoreForGC( test_case.num_cookies, test_case.num_old_cookies, 0, 0, - CookieMonster::kSafeFromGlobalPurgeDays * 2)); + CookieMonster::kSafeFromGlobalPurgeDays * 2); GURL gurl("http://google.com"); std::string cookie_line("z=3"); diff --git a/chromium/net/cookies/cookie_monster_store_test.cc b/chromium/net/cookies/cookie_monster_store_test.cc index 8c004ef6140..4573d4ad881 100644 --- a/chromium/net/cookies/cookie_monster_store_test.cc +++ b/chromium/net/cookies/cookie_monster_store_test.cc @@ -17,6 +17,7 @@ #include "url/gurl.h" namespace net { + LoadedCallbackTask::LoadedCallbackTask(LoadedCallback loaded_callback, std::vector<CanonicalCookie*> cookies) : loaded_callback_(loaded_callback), cookies_(cookies) { @@ -25,9 +26,22 @@ LoadedCallbackTask::LoadedCallbackTask(LoadedCallback loaded_callback, LoadedCallbackTask::~LoadedCallbackTask() { } +CookieStoreCommand::CookieStoreCommand( + Type type, + const CookieMonster::PersistentCookieStore::LoadedCallback& loaded_callback, + const std::string& key) + : type(type), loaded_callback(loaded_callback), key(key) {} + +CookieStoreCommand::CookieStoreCommand(Type type, const CanonicalCookie& cookie) + : type(type), cookie(cookie) {} + +CookieStoreCommand::CookieStoreCommand(const CookieStoreCommand& other) = + default; + +CookieStoreCommand::~CookieStoreCommand() {} + MockPersistentCookieStore::MockPersistentCookieStore() - : load_return_value_(true), loaded_(false) { -} + : store_load_commands_(false), load_return_value_(true), loaded_(false) {} void MockPersistentCookieStore::SetLoadExpectation( bool return_value, @@ -37,6 +51,11 @@ void MockPersistentCookieStore::SetLoadExpectation( } void MockPersistentCookieStore::Load(const LoadedCallback& loaded_callback) { + if (store_load_commands_) { + commands_.push_back( + CookieStoreCommand(CookieStoreCommand::LOAD, loaded_callback, "")); + return; + } std::vector<CanonicalCookie*> out_cookies; if (load_return_value_) { out_cookies = load_result_; @@ -51,6 +70,11 @@ void MockPersistentCookieStore::Load(const LoadedCallback& loaded_callback) { void MockPersistentCookieStore::LoadCookiesForKey( const std::string& key, const LoadedCallback& loaded_callback) { + if (store_load_commands_) { + commands_.push_back(CookieStoreCommand( + CookieStoreCommand::LOAD_COOKIES_FOR_KEY, loaded_callback, key)); + return; + } if (!loaded_) { Load(loaded_callback); } else { @@ -68,8 +92,6 @@ void MockPersistentCookieStore::AddCookie(const CanonicalCookie& cookie) { void MockPersistentCookieStore::UpdateCookieAccessTime( const CanonicalCookie& cookie) { - commands_.push_back( - CookieStoreCommand(CookieStoreCommand::UPDATE_ACCESS_TIME, cookie)); } void MockPersistentCookieStore::DeleteCookie(const CanonicalCookie& cookie) { @@ -101,9 +123,10 @@ void MockCookieMonsterDelegate::OnCookieChanged( MockCookieMonsterDelegate::~MockCookieMonsterDelegate() { } -CanonicalCookie BuildCanonicalCookie(const std::string& key, - const std::string& cookie_line, - const base::Time& creation_time) { +scoped_ptr<CanonicalCookie> BuildCanonicalCookie( + const GURL& url, + const std::string& cookie_line, + const base::Time& creation_time) { // Parse the cookie line. ParsedCookie pc(cookie_line); EXPECT_TRUE(pc.IsValid()); @@ -118,18 +141,18 @@ CanonicalCookie BuildCanonicalCookie(const std::string& key, : base::Time(); std::string cookie_path = pc.Path(); - return CanonicalCookie(GURL(), pc.Name(), pc.Value(), key, cookie_path, - creation_time, cookie_expires, creation_time, - pc.IsSecure(), pc.IsHttpOnly(), pc.IsFirstPartyOnly(), - pc.Priority()); + return CanonicalCookie::Create(url, pc.Name(), pc.Value(), url.host(), + cookie_path, creation_time, cookie_expires, + pc.IsSecure(), pc.IsHttpOnly(), pc.SameSite(), + false, pc.Priority()); } -void AddCookieToList(const std::string& key, +void AddCookieToList(const GURL& url, const std::string& cookie_line, const base::Time& creation_time, std::vector<CanonicalCookie*>* out_list) { - scoped_ptr<CanonicalCookie> cookie(new CanonicalCookie( - BuildCanonicalCookie(key, cookie_line, creation_time))); + scoped_ptr<CanonicalCookie> cookie( + BuildCanonicalCookie(url, cookie_line, creation_time)); out_list->push_back(cookie.release()); } @@ -196,11 +219,12 @@ void MockSimplePersistentCookieStore::Flush(const base::Closure& callback) { void MockSimplePersistentCookieStore::SetForceKeepSessionState() { } -CookieMonster* CreateMonsterFromStoreForGC(int num_secure_cookies, - int num_old_secure_cookies, - int num_non_secure_cookies, - int num_old_non_secure_cookies, - int days_old) { +scoped_ptr<CookieMonster> CreateMonsterFromStoreForGC( + int num_secure_cookies, + int num_old_secure_cookies, + int num_non_secure_cookies, + int num_old_non_secure_cookies, + int days_old) { base::Time current(base::Time::Now()); base::Time past_creation(base::Time::Now() - base::TimeDelta::FromDays(1000)); scoped_refptr<MockSimplePersistentCookieStore> store( @@ -229,12 +253,12 @@ CookieMonster* CreateMonsterFromStoreForGC(int num_secure_cookies, CanonicalCookie cc(GURL(), "a", "1", base::StringPrintf("h%05d.izzle", i), "/path", creation_time, expiration_time, - last_access_time, secure, false, false, - COOKIE_PRIORITY_DEFAULT); + last_access_time, secure, false, + CookieSameSite::DEFAULT_MODE, COOKIE_PRIORITY_DEFAULT); store->AddCookie(cc); } - return new CookieMonster(store.get(), NULL); + return make_scoped_ptr(new CookieMonster(store.get(), nullptr)); } MockSimplePersistentCookieStore::~MockSimplePersistentCookieStore() { diff --git a/chromium/net/cookies/cookie_monster_store_test.h b/chromium/net/cookies/cookie_monster_store_test.h index 686bb07afdb..aac83f82992 100644 --- a/chromium/net/cookies/cookie_monster_store_test.h +++ b/chromium/net/cookies/cookie_monster_store_test.h @@ -18,9 +18,12 @@ #include <vector> #include "base/macros.h" +#include "base/memory/scoped_ptr.h" #include "net/cookies/canonical_cookie.h" #include "net/cookies/cookie_monster.h" +class GURL; + namespace base { class Time; } @@ -50,18 +53,41 @@ class LoadedCallbackTask DISALLOW_COPY_AND_ASSIGN(LoadedCallbackTask); }; // Wrapper class LoadedCallbackTask -// Describes a call to one of the 3 functions of PersistentCookieStore. +// Describes a call to one of the 5 functions of PersistentCookieStore. struct CookieStoreCommand { enum Type { + LOAD, + LOAD_COOKIES_FOR_KEY, + // UPDATE_ACCESS_TIME is not included in this list, because get cookie + // commands may or may not end updating the access time, unless they have + // the option set not to do so. ADD, - UPDATE_ACCESS_TIME, REMOVE, }; - CookieStoreCommand(Type type, const CanonicalCookie& cookie) - : type(type), cookie(cookie) {} + // Constructor for LOAD and LOAD_COOKIES_FOR_KEY calls. |key| should be empty + // for LOAD_COOKIES_FOR_KEY. + CookieStoreCommand(Type type, + const CookieMonster::PersistentCookieStore::LoadedCallback& + loaded_callback, + const std::string& key); + + // Constructor for ADD, UPDATE_ACCESS_TIME, and REMOVE calls. + CookieStoreCommand(Type type, const CanonicalCookie& cookie); + + CookieStoreCommand(const CookieStoreCommand& other); + + ~CookieStoreCommand(); Type type; + + // Only non-null for LOAD and LOAD_COOKIES_FOR_KEY. + CookieMonster::PersistentCookieStore::LoadedCallback loaded_callback; + + // Only non-empty for LOAD_COOKIES_FOR_KEY. + std::string key; + + // Only non-null for ADD, UPDATE_ACCESS_TIME, and REMOVE. CanonicalCookie cookie; }; @@ -74,6 +100,12 @@ class MockPersistentCookieStore : public CookieMonster::PersistentCookieStore { MockPersistentCookieStore(); + // When set, Load() and LoadCookiesForKey() calls are store in the command + // list, rather than being automatically executed. Defaults to false. + void set_store_load_commands(bool store_load_commands) { + store_load_commands_ = store_load_commands; + } + void SetLoadExpectation(bool return_value, const std::vector<CanonicalCookie*>& result); @@ -100,6 +132,8 @@ class MockPersistentCookieStore : public CookieMonster::PersistentCookieStore { private: CommandList commands_; + bool store_load_commands_; + // Deferred result to use when Load() is called. bool load_return_value_; std::vector<CanonicalCookie*> load_result_; @@ -134,12 +168,13 @@ class MockCookieMonsterDelegate : public CookieMonsterDelegate { }; // Helper to build a single CanonicalCookie. -CanonicalCookie BuildCanonicalCookie(const std::string& key, - const std::string& cookie_line, - const base::Time& creation_time); +scoped_ptr<CanonicalCookie> BuildCanonicalCookie( + const GURL& url, + const std::string& cookie_line, + const base::Time& creation_time); // Helper to build a list of CanonicalCookie*s. -void AddCookieToList(const std::string& key, +void AddCookieToList(const GURL& url, const std::string& cookie_line, const base::Time& creation_time, std::vector<CanonicalCookie*>* out_list); @@ -188,11 +223,12 @@ class MockSimplePersistentCookieStore // will be marked secure and non-secure, respectively. 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_secure_cookies, - int num_old_secure_cookies, - int num_non_secure_cookies, - int num_old_non_secure_cookies, - int days_old); +scoped_ptr<CookieMonster> CreateMonsterFromStoreForGC( + int num_secure_cookies, + int num_old_secure_cookies, + int num_non_secure_cookies, + int num_old_non_secure_cookies, + int days_old); } // namespace net diff --git a/chromium/net/cookies/cookie_monster_unittest.cc b/chromium/net/cookies/cookie_monster_unittest.cc index efeadd96d58..45c3063b70c 100644 --- a/chromium/net/cookies/cookie_monster_unittest.cc +++ b/chromium/net/cookies/cookie_monster_unittest.cc @@ -70,29 +70,11 @@ const char kTopLevelDomainPlus2Secure[] = "https://www.math.harvard.edu"; const char kTopLevelDomainPlus3[] = "http://www.bourbaki.math.harvard.edu"; const char kOtherDomain[] = "http://www.mit.edu"; -class GetCookieListCallback : public CookieCallback { - public: - GetCookieListCallback() {} - explicit GetCookieListCallback(Thread* run_in_thread) - : CookieCallback(run_in_thread) {} - - void Run(const CookieList& cookies) { - cookies_ = cookies; - CallbackEpilogue(); - } - - const CookieList& cookies() { return cookies_; } - - private: - CookieList cookies_; -}; - struct CookieMonsterTestTraits { - static scoped_refptr<CookieStore> Create() { - return new CookieMonster(NULL, NULL); + static scoped_ptr<CookieStore> Create() { + return make_scoped_ptr(new CookieMonster(nullptr, nullptr)); } - static const bool is_cookie_monster = true; static const bool supports_http_only = true; static const bool supports_non_dotted_domains = true; static const bool preserves_trailing_dots = true; @@ -103,11 +85,10 @@ struct CookieMonsterTestTraits { }; struct CookieMonsterEnforcingStrictSecure { - static scoped_refptr<CookieStore> Create() { - return new CookieMonster(NULL, NULL); + static scoped_ptr<CookieStore> Create() { + return make_scoped_ptr(new CookieMonster(nullptr, nullptr)); } - static const bool is_cookie_monster = true; static const bool supports_http_only = true; static const bool supports_non_dotted_domains = true; static const bool preserves_trailing_dots = true; @@ -121,10 +102,6 @@ INSTANTIATE_TYPED_TEST_CASE_P(CookieMonster, CookieStoreTest, CookieMonsterTestTraits); -INSTANTIATE_TYPED_TEST_CASE_P(CookieMonster, - MultiThreadedCookieStoreTest, - CookieMonsterTestTraits); - INSTANTIATE_TYPED_TEST_CASE_P(CookieMonsterStrictSecure, CookieStoreTest, CookieMonsterEnforcingStrictSecure); @@ -132,88 +109,31 @@ INSTANTIATE_TYPED_TEST_CASE_P(CookieMonsterStrictSecure, template <typename T> class CookieMonsterTestBase : public CookieStoreTest<T> { public: - using CookieStoreTest<T>::RunFor; using CookieStoreTest<T>::SetCookie; protected: using CookieStoreTest<T>::http_www_google_; using CookieStoreTest<T>::https_www_google_; - CookieList GetAllCookies(CookieMonster* cm) { - DCHECK(cm); - GetCookieListCallback callback; - cm->GetAllCookiesAsync( - base::Bind(&GetCookieListCallback::Run, base::Unretained(&callback))); - RunFor(kTimeout); - EXPECT_TRUE(callback.did_run()); - return callback.cookies(); - } - - CookieList GetAllCookiesForURL(CookieMonster* cm, const GURL& url) { - DCHECK(cm); - GetCookieListCallback callback; - cm->GetAllCookiesForURLAsync(url, base::Bind(&GetCookieListCallback::Run, - base::Unretained(&callback))); - RunFor(kTimeout); - EXPECT_TRUE(callback.did_run()); - return callback.cookies(); - } - CookieList GetAllCookiesForURLWithOptions(CookieMonster* cm, const GURL& url, const CookieOptions& options) { DCHECK(cm); GetCookieListCallback callback; - cm->GetAllCookiesForURLWithOptionsAsync( + cm->GetCookieListWithOptionsAsync( url, options, base::Bind(&GetCookieListCallback::Run, base::Unretained(&callback))); - RunFor(kTimeout); - EXPECT_TRUE(callback.did_run()); + callback.WaitUntilDone(); return callback.cookies(); } - bool SetCookieWithDetails(CookieMonster* cm, - 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, - bool first_party_only, - CookiePriority priority) { - DCHECK(cm); - ResultSavingCookieCallback<bool> callback; - cm->SetCookieWithDetailsAsync( - url, name, value, domain, path, expiration_time, secure, http_only, - first_party_only, false /* enforce prefixes */, - false /* enforces strict secure cookies */, priority, - base::Bind(&ResultSavingCookieCallback<bool>::Run, - base::Unretained(&callback))); - RunFor(kTimeout); - EXPECT_TRUE(callback.did_run()); - return callback.result(); - } - bool SetAllCookies(CookieMonster* cm, const CookieList& list) { DCHECK(cm); ResultSavingCookieCallback<bool> callback; cm->SetAllCookiesAsync(list, base::Bind(&ResultSavingCookieCallback<bool>::Run, base::Unretained(&callback))); - RunFor(kTimeout); - EXPECT_TRUE(callback.did_run()); - return callback.result(); - } - - int DeleteAll(CookieMonster* cm) { - DCHECK(cm); - ResultSavingCookieCallback<int> callback; - cm->DeleteAllAsync(base::Bind(&ResultSavingCookieCallback<int>::Run, - base::Unretained(&callback))); - RunFor(kTimeout); - EXPECT_TRUE(callback.did_run()); + callback.WaitUntilDone(); return callback.result(); } @@ -226,8 +146,7 @@ class CookieMonsterTestBase : public CookieStoreTest<T> { delete_begin, delete_end, base::Bind(&ResultSavingCookieCallback<int>::Run, base::Unretained(&callback))); - RunFor(kTimeout); - EXPECT_TRUE(callback.did_run()); + callback.WaitUntilDone(); return callback.result(); } @@ -241,120 +160,102 @@ class CookieMonsterTestBase : public CookieStoreTest<T> { delete_begin, delete_end, url, base::Bind(&ResultSavingCookieCallback<int>::Run, base::Unretained(&callback))); - RunFor(kTimeout); - EXPECT_TRUE(callback.did_run()); - return callback.result(); - } - - int DeleteAllForHost(CookieMonster* cm, const GURL& url) { - DCHECK(cm); - ResultSavingCookieCallback<int> callback; - cm->DeleteAllForHostAsync(url, - base::Bind(&ResultSavingCookieCallback<int>::Run, - base::Unretained(&callback))); - RunFor(kTimeout); - EXPECT_TRUE(callback.did_run()); - return callback.result(); - } - - bool DeleteCanonicalCookie(CookieMonster* cm, const CanonicalCookie& cookie) { - DCHECK(cm); - ResultSavingCookieCallback<bool> callback; - cm->DeleteCanonicalCookieAsync( - cookie, base::Bind(&ResultSavingCookieCallback<bool>::Run, - base::Unretained(&callback))); - RunFor(kTimeout); - EXPECT_TRUE(callback.did_run()); + callback.WaitUntilDone(); return callback.result(); } // Helper for DeleteAllForHost test; repopulates CM with same layout // each time. - void PopulateCmForDeleteAllForHost(scoped_refptr<CookieMonster> cm) { + void PopulateCmForDeleteAllForHost(CookieMonster* cm) { GURL url_top_level_domain_plus_1(kTopLevelDomainPlus1); GURL url_top_level_domain_plus_2(kTopLevelDomainPlus2); GURL url_top_level_domain_plus_2_secure(kTopLevelDomainPlus2Secure); GURL url_top_level_domain_plus_3(kTopLevelDomainPlus3); GURL url_other(kOtherDomain); - DeleteAll(cm.get()); + this->DeleteAll(cm); // Static population for probe: // * Three levels of domain cookie (.b.a, .c.b.a, .d.c.b.a) // * Three levels of host cookie (w.b.a, w.c.b.a, w.d.c.b.a) // * http_only cookie (w.c.b.a) - // * first-party cookie (w.c.b.a) + // * same_site cookie (w.c.b.a) // * Two secure cookies (.c.b.a, w.c.b.a) // * Two domain path cookies (.c.b.a/dir1, .c.b.a/dir1/dir2) // * Two host path cookies (w.c.b.a/dir1, w.c.b.a/dir1/dir2) // Domain cookies EXPECT_TRUE(this->SetCookieWithDetails( - cm.get(), url_top_level_domain_plus_1, "dom_1", "X", ".harvard.edu", - "/", base::Time(), false, false, false, COOKIE_PRIORITY_DEFAULT)); + cm, url_top_level_domain_plus_1, "dom_1", "X", ".harvard.edu", "/", + base::Time(), base::Time(), base::Time(), false, false, + CookieSameSite::DEFAULT_MODE, COOKIE_PRIORITY_DEFAULT)); EXPECT_TRUE(this->SetCookieWithDetails( - cm.get(), url_top_level_domain_plus_2, "dom_2", "X", - ".math.harvard.edu", "/", base::Time(), false, false, false, - COOKIE_PRIORITY_DEFAULT)); + cm, url_top_level_domain_plus_2, "dom_2", "X", ".math.harvard.edu", "/", + base::Time(), base::Time(), base::Time(), false, false, + CookieSameSite::DEFAULT_MODE, COOKIE_PRIORITY_DEFAULT)); EXPECT_TRUE(this->SetCookieWithDetails( - cm.get(), url_top_level_domain_plus_3, "dom_3", "X", - ".bourbaki.math.harvard.edu", "/", base::Time(), false, false, false, + cm, url_top_level_domain_plus_3, "dom_3", "X", + ".bourbaki.math.harvard.edu", "/", base::Time(), base::Time(), + base::Time(), false, false, CookieSameSite::DEFAULT_MODE, COOKIE_PRIORITY_DEFAULT)); // Host cookies EXPECT_TRUE(this->SetCookieWithDetails( - cm.get(), url_top_level_domain_plus_1, "host_1", "X", std::string(), - "/", base::Time(), false, false, false, COOKIE_PRIORITY_DEFAULT)); + cm, url_top_level_domain_plus_1, "host_1", "X", std::string(), "/", + base::Time(), base::Time(), base::Time(), false, false, + CookieSameSite::DEFAULT_MODE, COOKIE_PRIORITY_DEFAULT)); EXPECT_TRUE(this->SetCookieWithDetails( - cm.get(), url_top_level_domain_plus_2, "host_2", "X", std::string(), - "/", base::Time(), false, false, false, COOKIE_PRIORITY_DEFAULT)); + cm, url_top_level_domain_plus_2, "host_2", "X", std::string(), "/", + base::Time(), base::Time(), base::Time(), false, false, + CookieSameSite::DEFAULT_MODE, COOKIE_PRIORITY_DEFAULT)); EXPECT_TRUE(this->SetCookieWithDetails( - cm.get(), url_top_level_domain_plus_3, "host_3", "X", std::string(), - "/", base::Time(), false, false, false, COOKIE_PRIORITY_DEFAULT)); + cm, url_top_level_domain_plus_3, "host_3", "X", std::string(), "/", + base::Time(), base::Time(), base::Time(), false, false, + CookieSameSite::DEFAULT_MODE, COOKIE_PRIORITY_DEFAULT)); // http_only cookie EXPECT_TRUE(this->SetCookieWithDetails( - cm.get(), url_top_level_domain_plus_2, "httpo_check", "x", - std::string(), "/", base::Time(), false, true, false, - COOKIE_PRIORITY_DEFAULT)); + cm, url_top_level_domain_plus_2, "httpo_check", "x", std::string(), "/", + base::Time(), base::Time(), base::Time(), false, true, + CookieSameSite::DEFAULT_MODE, COOKIE_PRIORITY_DEFAULT)); - // first-party cookie + // same-site cookie EXPECT_TRUE(this->SetCookieWithDetails( - cm.get(), url_top_level_domain_plus_2, "firstp_check", "x", - std::string(), "/", base::Time(), false, false, true, - COOKIE_PRIORITY_DEFAULT)); + cm, url_top_level_domain_plus_2, "firstp_check", "x", std::string(), + "/", base::Time(), base::Time(), base::Time(), false, false, + CookieSameSite::STRICT_MODE, COOKIE_PRIORITY_DEFAULT)); // Secure cookies EXPECT_TRUE(this->SetCookieWithDetails( - cm.get(), url_top_level_domain_plus_2_secure, "sec_dom", "X", - ".math.harvard.edu", "/", base::Time(), true, false, false, - COOKIE_PRIORITY_DEFAULT)); + cm, url_top_level_domain_plus_2_secure, "sec_dom", "X", + ".math.harvard.edu", "/", base::Time(), base::Time(), base::Time(), + true, false, CookieSameSite::DEFAULT_MODE, COOKIE_PRIORITY_DEFAULT)); EXPECT_TRUE(this->SetCookieWithDetails( - cm.get(), url_top_level_domain_plus_2_secure, "sec_host", "X", - std::string(), "/", base::Time(), true, false, false, - COOKIE_PRIORITY_DEFAULT)); + cm, url_top_level_domain_plus_2_secure, "sec_host", "X", std::string(), + "/", base::Time(), base::Time(), base::Time(), true, false, + CookieSameSite::DEFAULT_MODE, COOKIE_PRIORITY_DEFAULT)); // Domain path cookies EXPECT_TRUE(this->SetCookieWithDetails( - cm.get(), url_top_level_domain_plus_2, "dom_path_1", "X", - ".math.harvard.edu", "/dir1", base::Time(), false, false, false, - COOKIE_PRIORITY_DEFAULT)); + cm, url_top_level_domain_plus_2, "dom_path_1", "X", ".math.harvard.edu", + "/dir1", base::Time(), base::Time(), base::Time(), false, false, + CookieSameSite::DEFAULT_MODE, COOKIE_PRIORITY_DEFAULT)); EXPECT_TRUE(this->SetCookieWithDetails( - cm.get(), url_top_level_domain_plus_2, "dom_path_2", "X", - ".math.harvard.edu", "/dir1/dir2", base::Time(), false, false, false, - COOKIE_PRIORITY_DEFAULT)); + cm, url_top_level_domain_plus_2, "dom_path_2", "X", ".math.harvard.edu", + "/dir1/dir2", base::Time(), base::Time(), base::Time(), false, false, + CookieSameSite::DEFAULT_MODE, COOKIE_PRIORITY_DEFAULT)); // Host path cookies EXPECT_TRUE(this->SetCookieWithDetails( - cm.get(), url_top_level_domain_plus_2, "host_path_1", "X", - std::string(), "/dir1", base::Time(), false, false, false, - COOKIE_PRIORITY_DEFAULT)); + cm, url_top_level_domain_plus_2, "host_path_1", "X", std::string(), + "/dir1", base::Time(), base::Time(), base::Time(), false, false, + CookieSameSite::DEFAULT_MODE, COOKIE_PRIORITY_DEFAULT)); EXPECT_TRUE(this->SetCookieWithDetails( - cm.get(), url_top_level_domain_plus_2, "host_path_2", "X", - std::string(), "/dir1/dir2", base::Time(), false, false, false, - COOKIE_PRIORITY_DEFAULT)); + cm, url_top_level_domain_plus_2, "host_path_2", "X", std::string(), + "/dir1/dir2", base::Time(), base::Time(), base::Time(), false, false, + CookieSameSite::DEFAULT_MODE, COOKIE_PRIORITY_DEFAULT)); - EXPECT_EQ(14U, this->GetAllCookies(cm.get()).size()); + EXPECT_EQ(14U, this->GetAllCookies(cm).size()); } Time GetFirstCookieAccessDate(CookieMonster* cm) { @@ -383,7 +284,7 @@ class CookieMonsterTestBase : public CookieStoreTest<T> { (domain_max_cookies + domain_purge_cookies) * 2; // Add a bunch of cookies on a single host, should purge them. { - scoped_refptr<CookieMonster> cm(new CookieMonster(NULL, NULL)); + scoped_ptr<CookieMonster> cm(new CookieMonster(nullptr, nullptr)); for (int i = 0; i < more_than_enough_cookies; ++i) { std::string cookie = base::StringPrintf("a%03d=b", i); EXPECT_TRUE(SetCookie(cm.get(), http_www_google_.url(), cookie)); @@ -401,7 +302,7 @@ class CookieMonsterTestBase : public CookieStoreTest<T> { // between them. We shouldn't go above kDomainMaxCookies for both together. GURL url_google_specific(http_www_google_.Format("http://www.gmail.%D")); { - scoped_refptr<CookieMonster> cm(new CookieMonster(NULL, NULL)); + scoped_ptr<CookieMonster> cm(new CookieMonster(nullptr, nullptr)); for (int i = 0; i < more_than_enough_cookies; ++i) { std::string cookie_general = base::StringPrintf("a%03d=b", i); EXPECT_TRUE( @@ -461,7 +362,8 @@ class CookieMonsterTestBase : public CookieStoreTest<T> { size_t expected_low_count, size_t expected_medium_count, size_t expected_high_count) { - DeleteAll(cm); + SCOPED_TRACE(coded_priority_str); + this->DeleteAll(cm); int next_cookie_id = 0; std::vector<CookiePriority> priority_list; std::vector<int> id_list[3]; // Indexed by CookiePriority. @@ -472,7 +374,7 @@ class CookieMonsterTestBase : public CookieStoreTest<T> { base::SPLIT_WANT_ALL)) { DCHECK(!token.empty()); // Take last character as priority. - CookiePriority priority = CharToPriority(token[token.length() - 1]); + CookiePriority priority = CharToPriority(token.back()); std::string priority_str = CookiePriorityToString(priority); // The rest of the string (possibly empty) specifies repetition. int rep = 1; @@ -544,10 +446,10 @@ class CookieMonsterTestBase : public CookieStoreTest<T> { size_t expected_secure_cookies, size_t expected_non_secure_cookies, const AltHosts* alt_host_entries) { - scoped_refptr<CookieMonster> cm; + scoped_ptr<CookieMonster> cm; if (alt_host_entries == nullptr) { - cm = new CookieMonster(nullptr, nullptr); + cm.reset(new CookieMonster(nullptr, nullptr)); } else { // When generating all of these cookies on alternate hosts, they need to // be all older than the max "safe" date for GC, which is currently 30 @@ -595,7 +497,7 @@ class CookieMonsterTestBase : public CookieStoreTest<T> { DCHECK_EQ(50U, CookieMonster::kDomainCookiesQuotaMedium); DCHECK_EQ(70U, CookieMonster::kDomainCookiesQuotaHigh); - scoped_refptr<CookieMonster> cm(new CookieMonster(NULL, NULL)); + scoped_ptr<CookieMonster> cm(new CookieMonster(nullptr, nullptr)); // Each test case adds 181 cookies, so 31 cookies are evicted. // Cookie same priority, repeated for each priority. @@ -712,13 +614,6 @@ class MockDeleteCallback MOCK_METHOD1(Invoke, void(int num_deleted)); }; -class MockDeleteCookieCallback - : public MockCookieCallback<MockDeleteCookieCallback, - CookieMonster::DeleteCookieCallback> { - public: - MOCK_METHOD1(Invoke, void(bool success)); -}; - struct CookiesInputInfo { const GURL url; const std::string name; @@ -728,13 +623,12 @@ struct CookiesInputInfo { const base::Time expiration_time; bool secure; bool http_only; - bool first_party_only; + CookieSameSite same_site; CookiePriority priority; }; -ACTION(QuitCurrentMessageLoop) { - base::ThreadTaskRunnerHandle::Get()->PostTask( - FROM_HERE, base::MessageLoop::QuitWhenIdleClosure()); +ACTION_P(QuitRunLoop, run_loop) { + run_loop->Quit(); } // TODO(erikwright): When the synchronous helpers 'GetCookies' etc. are removed, @@ -763,18 +657,24 @@ ACTION_P4(DeleteAllCreatedBetweenAction, } ACTION_P3(SetCookieWithDetailsAction, cookie_monster, cc, callback) { cookie_monster->SetCookieWithDetailsAsync( - cc.url, cc.name, cc.value, cc.domain, cc.path, cc.expiration_time, - cc.secure, cc.http_only, cc.first_party_only, - false /* enforce prefixes */, false /* enforces strict secure cookies */, - cc.priority, callback->AsCallback()); + cc.url, cc.name, cc.value, cc.domain, cc.path, base::Time(), + cc.expiration_time, base::Time(), cc.secure, cc.http_only, cc.same_site, + false /* enforces strict secure cookies */, cc.priority, + callback->AsCallback()); } ACTION_P2(GetAllCookiesAction, cookie_monster, callback) { cookie_monster->GetAllCookiesAsync(callback->AsCallback()); } -ACTION_P3(DeleteAllForHostAction, cookie_monster, url, callback) { - cookie_monster->DeleteAllForHostAsync(url, callback->AsCallback()); +ACTION_P5(DeleteAllCreatedBetweenForHostAction, + cookie_monster, + delete_begin, + delete_end, + url, + callback) { + cookie_monster->DeleteAllCreatedBetweenForHostAsync( + delete_begin, delete_end, url, callback->AsCallback()); } ACTION_P3(DeleteCanonicalCookieAction, cookie_monster, cookie, callback) { @@ -785,9 +685,9 @@ ACTION_P2(DeleteAllAction, cookie_monster, callback) { cookie_monster->DeleteAllAsync(callback->AsCallback()); } -ACTION_P3(GetAllCookiesForUrlWithOptionsAction, cookie_monster, url, callback) { - cookie_monster->GetAllCookiesForURLWithOptionsAsync(url, CookieOptions(), - callback->AsCallback()); +ACTION_P3(GetCookieListForUrlWithOptionsAction, cookie_monster, url, callback) { + cookie_monster->GetCookieListWithOptionsAsync(url, CookieOptions(), + callback->AsCallback()); } ACTION_P3(GetAllCookiesForUrlAction, cookie_monster, url, callback) { @@ -813,39 +713,36 @@ ACTION_P2(DeleteSessionCookiesAction, cookie_monster, callback) { // 3. Invocations after the loading has completed complete immediately. class DeferredCookieTaskTest : public CookieMonsterTest { protected: - DeferredCookieTaskTest() { + DeferredCookieTaskTest() : expect_load_called_(false) { persistent_store_ = new NewMockPersistentCookieStore(); - cookie_monster_ = new CookieMonster(persistent_store_.get(), NULL); + cookie_monster_.reset(new CookieMonster(persistent_store_.get(), nullptr)); } // Defines a cookie to be returned from PersistentCookieStore::Load - void DeclareLoadedCookie(const std::string& key, + void DeclareLoadedCookie(const GURL& url, const std::string& cookie_line, const base::Time& creation_time) { - AddCookieToList(key, cookie_line, creation_time, &loaded_cookies_); + AddCookieToList(url, cookie_line, creation_time, &loaded_cookies_); } // Runs the message loop, waiting until PersistentCookieStore::Load is called. - // Call CompleteLoadingAndWait to cause the load to complete. + // Call CompleteLoading to cause the load to complete. void WaitForLoadCall() { - RunFor(kTimeout); + load_run_loop_.Run(); // Verify that PeristentStore::Load was called. testing::Mock::VerifyAndClear(persistent_store_.get()); } // Invokes the PersistentCookieStore::LoadCookiesForKey completion callbacks - // and PersistentCookieStore::Load completion callback and waits - // until the message loop is quit. - void CompleteLoadingAndWait() { + // and PersistentCookieStore::Load completion callback. + void CompleteLoading() { while (!loaded_for_key_callbacks_.empty()) { loaded_for_key_callbacks_.front().Run(loaded_cookies_); loaded_cookies_.clear(); loaded_for_key_callbacks_.pop(); } - loaded_callback_.Run(loaded_cookies_); - RunFor(kTimeout); } // Performs the provided action, expecting it to cause a call to @@ -861,30 +758,26 @@ class DeferredCookieTaskTest : public CookieMonsterTest { testing::Action<void(void)> action) { EXPECT_CALL(*this, Begin()).WillOnce(action); ExpectLoadCall(); - ExpectLoadForKeyCall(key, false); + ExpectLoadForKeyCall(key); Begin(); } // Declares an expectation that PersistentCookieStore::Load will be called, - // saving the provided callback and sending a quit to the message loop. + // saving the provided callback and sending a quit to |load_run_loop_|. void ExpectLoadCall() { + // Make sure the |load_run_loop_| is not reused. + CHECK(!expect_load_called_); + expect_load_called_ = true; EXPECT_CALL(*persistent_store_.get(), Load(testing::_)) .WillOnce(testing::DoAll(testing::SaveArg<0>(&loaded_callback_), - QuitCurrentMessageLoop())); + QuitRunLoop(&load_run_loop_))); } // Declares an expectation that PersistentCookieStore::LoadCookiesForKey - // will be called, saving the provided callback and sending a quit to the - // message loop. - void ExpectLoadForKeyCall(const std::string& key, bool quit_queue) { - if (quit_queue) - EXPECT_CALL(*persistent_store_.get(), LoadCookiesForKey(key, testing::_)) - .WillOnce( - testing::DoAll(PushCallbackAction(&loaded_for_key_callbacks_), - QuitCurrentMessageLoop())); - else - EXPECT_CALL(*persistent_store_.get(), LoadCookiesForKey(key, testing::_)) - .WillOnce(PushCallbackAction(&loaded_for_key_callbacks_)); + // will be called, saving the provided callback. + void ExpectLoadForKeyCall(const std::string& key) { + EXPECT_CALL(*persistent_store_.get(), LoadCookiesForKey(key, testing::_)) + .WillOnce(PushCallbackAction(&loaded_for_key_callbacks_)); } // Invokes the initial action. @@ -906,15 +799,18 @@ class DeferredCookieTaskTest : public CookieMonsterTest { // PersistentCookieStore::LoadCookiesForKey std::queue<CookieMonster::PersistentCookieStore::LoadedCallback> loaded_for_key_callbacks_; - + // base::RunLoop used to wait for PersistentCookieStore::Load to be called. + base::RunLoop load_run_loop_; + // Indicates whether ExpectLoadCall() has been called. + bool expect_load_called_; // Stores the CookieMonster under test. - scoped_refptr<CookieMonster> cookie_monster_; + scoped_ptr<CookieMonster> cookie_monster_; // Stores the mock PersistentCookieStore. scoped_refptr<NewMockPersistentCookieStore> persistent_store_; }; TEST_F(DeferredCookieTaskTest, DeferredGetCookies) { - DeclareLoadedCookie(http_www_google_.host(), + DeclareLoadedCookie(http_www_google_.url(), "X=1; path=/; expires=Mon, 18-Apr-22 22:50:14 GMT", Time::Now() + TimeDelta::FromDays(3)); @@ -930,10 +826,11 @@ TEST_F(DeferredCookieTaskTest, DeferredGetCookies) { EXPECT_CALL(get_cookies_callback, Invoke("X=1")) .WillOnce(GetCookiesAction(&cookie_monster(), http_www_google_.url(), &get_cookies_callback)); - EXPECT_CALL(get_cookies_callback, Invoke("X=1")) - .WillOnce(QuitCurrentMessageLoop()); + base::RunLoop loop; + EXPECT_CALL(get_cookies_callback, Invoke("X=1")).WillOnce(QuitRunLoop(&loop)); - CompleteLoadingAndWait(); + CompleteLoading(); + loop.Run(); } TEST_F(DeferredCookieTaskTest, DeferredSetCookie) { @@ -949,23 +846,24 @@ TEST_F(DeferredCookieTaskTest, DeferredSetCookie) { EXPECT_CALL(set_cookies_callback, Invoke(true)) .WillOnce(SetCookieAction(&cookie_monster(), http_www_google_.url(), "X=Y", &set_cookies_callback)); - EXPECT_CALL(set_cookies_callback, Invoke(true)) - .WillOnce(QuitCurrentMessageLoop()); + base::RunLoop loop; + EXPECT_CALL(set_cookies_callback, Invoke(true)).WillOnce(QuitRunLoop(&loop)); - CompleteLoadingAndWait(); + CompleteLoading(); + loop.Run(); } TEST_F(DeferredCookieTaskTest, DeferredSetAllCookies) { MockSetCookiesCallback set_cookies_callback; CookieList list; - list.push_back(CanonicalCookie(http_www_google_.url(), "A", "B", - http_www_google_.domain(), "/", - base::Time::Now(), base::Time(), base::Time(), - false, true, false, COOKIE_PRIORITY_DEFAULT)); - list.push_back(CanonicalCookie(http_www_google_.url(), "C", "D", - http_www_google_.domain(), "/", - base::Time::Now(), base::Time(), base::Time(), - false, true, false, COOKIE_PRIORITY_DEFAULT)); + list.push_back(*CanonicalCookie::Create( + http_www_google_.url(), "A", "B", http_www_google_.domain(), "/", + base::Time::Now(), base::Time(), false, true, + CookieSameSite::DEFAULT_MODE, false, COOKIE_PRIORITY_DEFAULT)); + list.push_back(*CanonicalCookie::Create( + http_www_google_.url(), "C", "D", http_www_google_.domain(), "/", + base::Time::Now(), base::Time(), false, true, + CookieSameSite::DEFAULT_MODE, false, COOKIE_PRIORITY_DEFAULT)); BeginWith( SetAllCookiesAction(&cookie_monster(), list, &set_cookies_callback)); @@ -975,10 +873,11 @@ TEST_F(DeferredCookieTaskTest, DeferredSetAllCookies) { EXPECT_CALL(set_cookies_callback, Invoke(true)) .WillOnce( SetAllCookiesAction(&cookie_monster(), list, &set_cookies_callback)); - EXPECT_CALL(set_cookies_callback, Invoke(true)) - .WillOnce(QuitCurrentMessageLoop()); + base::RunLoop loop; + EXPECT_CALL(set_cookies_callback, Invoke(true)).WillOnce(QuitRunLoop(&loop)); - CompleteLoadingAndWait(); + CompleteLoading(); + loop.Run(); } TEST_F(DeferredCookieTaskTest, DeferredDeleteCookie) { @@ -994,10 +893,11 @@ TEST_F(DeferredCookieTaskTest, DeferredDeleteCookie) { EXPECT_CALL(delete_cookie_callback, Invoke()) .WillOnce(DeleteCookieAction(&cookie_monster(), http_www_google_.url(), "X", &delete_cookie_callback)); - EXPECT_CALL(delete_cookie_callback, Invoke()) - .WillOnce(QuitCurrentMessageLoop()); + base::RunLoop loop; + EXPECT_CALL(delete_cookie_callback, Invoke()).WillOnce(QuitRunLoop(&loop)); - CompleteLoadingAndWait(); + CompleteLoading(); + loop.Run(); } TEST_F(DeferredCookieTaskTest, DeferredSetCookieWithDetails) { @@ -1011,7 +911,7 @@ TEST_F(DeferredCookieTaskTest, DeferredSetCookieWithDetails) { base::Time(), false, false, - false, + CookieSameSite::DEFAULT_MODE, COOKIE_PRIORITY_DEFAULT}; BeginWithForDomainKey( http_www_google_.domain(), @@ -1028,19 +928,20 @@ TEST_F(DeferredCookieTaskTest, DeferredSetCookieWithDetails) { base::Time(), false, false, - false, + CookieSameSite::DEFAULT_MODE, COOKIE_PRIORITY_DEFAULT}; EXPECT_CALL(set_cookies_callback, Invoke(true)) .WillOnce(SetCookieWithDetailsAction(&cookie_monster(), cookie_info_exp, &set_cookies_callback)); - EXPECT_CALL(set_cookies_callback, Invoke(true)) - .WillOnce(QuitCurrentMessageLoop()); + base::RunLoop loop; + EXPECT_CALL(set_cookies_callback, Invoke(true)).WillOnce(QuitRunLoop(&loop)); - CompleteLoadingAndWait(); + CompleteLoading(); + loop.Run(); } TEST_F(DeferredCookieTaskTest, DeferredGetAllCookies) { - DeclareLoadedCookie(http_www_google_.host(), + DeclareLoadedCookie(http_www_google_.url(), "X=1; path=/; expires=Mon, 18-Apr-22 22:50:14 GMT", Time::Now() + TimeDelta::FromDays(3)); @@ -1053,14 +954,16 @@ TEST_F(DeferredCookieTaskTest, DeferredGetAllCookies) { EXPECT_CALL(get_cookie_list_callback, Invoke(testing::_)) .WillOnce( GetAllCookiesAction(&cookie_monster(), &get_cookie_list_callback)); + base::RunLoop loop; EXPECT_CALL(get_cookie_list_callback, Invoke(testing::_)) - .WillOnce(QuitCurrentMessageLoop()); + .WillOnce(QuitRunLoop(&loop)); - CompleteLoadingAndWait(); + CompleteLoading(); + loop.Run(); } TEST_F(DeferredCookieTaskTest, DeferredGetAllForUrlCookies) { - DeclareLoadedCookie(http_www_google_.host(), + DeclareLoadedCookie(http_www_google_.url(), "X=1; path=/; expires=Mon, 18-Apr-22 22:50:14 GMT", Time::Now() + TimeDelta::FromDays(3)); @@ -1077,34 +980,38 @@ TEST_F(DeferredCookieTaskTest, DeferredGetAllForUrlCookies) { .WillOnce(GetAllCookiesForUrlAction(&cookie_monster(), http_www_google_.url(), &get_cookie_list_callback)); + base::RunLoop loop; EXPECT_CALL(get_cookie_list_callback, Invoke(testing::_)) - .WillOnce(QuitCurrentMessageLoop()); + .WillOnce(QuitRunLoop(&loop)); - CompleteLoadingAndWait(); + CompleteLoading(); + loop.Run(); } TEST_F(DeferredCookieTaskTest, DeferredGetAllForUrlWithOptionsCookies) { - DeclareLoadedCookie(http_www_google_.host(), + DeclareLoadedCookie(http_www_google_.url(), "X=1; path=/; expires=Mon, 18-Apr-22 22:50:14 GMT", Time::Now() + TimeDelta::FromDays(3)); MockGetCookieListCallback get_cookie_list_callback; BeginWithForDomainKey(http_www_google_.domain(), - GetAllCookiesForUrlWithOptionsAction( + GetCookieListForUrlWithOptionsAction( &cookie_monster(), http_www_google_.url(), &get_cookie_list_callback)); WaitForLoadCall(); EXPECT_CALL(get_cookie_list_callback, Invoke(testing::_)) - .WillOnce(GetAllCookiesForUrlWithOptionsAction( + .WillOnce(GetCookieListForUrlWithOptionsAction( &cookie_monster(), http_www_google_.url(), &get_cookie_list_callback)); + base::RunLoop loop; EXPECT_CALL(get_cookie_list_callback, Invoke(testing::_)) - .WillOnce(QuitCurrentMessageLoop()); + .WillOnce(QuitRunLoop(&loop)); - CompleteLoadingAndWait(); + CompleteLoading(); + loop.Run(); } TEST_F(DeferredCookieTaskTest, DeferredDeleteAllCookies) { @@ -1116,10 +1023,12 @@ TEST_F(DeferredCookieTaskTest, DeferredDeleteAllCookies) { EXPECT_CALL(delete_callback, Invoke(false)) .WillOnce(DeleteAllAction(&cookie_monster(), &delete_callback)); - EXPECT_CALL(delete_callback, Invoke(false)) - .WillOnce(QuitCurrentMessageLoop()); - CompleteLoadingAndWait(); + base::RunLoop loop; + EXPECT_CALL(delete_callback, Invoke(false)).WillOnce(QuitRunLoop(&loop)); + + CompleteLoading(); + loop.Run(); } TEST_F(DeferredCookieTaskTest, DeferredDeleteAllCreatedBetweenCookies) { @@ -1134,50 +1043,54 @@ TEST_F(DeferredCookieTaskTest, DeferredDeleteAllCreatedBetweenCookies) { .WillOnce(DeleteAllCreatedBetweenAction(&cookie_monster(), base::Time(), base::Time::Now(), &delete_callback)); - EXPECT_CALL(delete_callback, Invoke(false)) - .WillOnce(QuitCurrentMessageLoop()); + base::RunLoop loop; + EXPECT_CALL(delete_callback, Invoke(false)).WillOnce(QuitRunLoop(&loop)); - CompleteLoadingAndWait(); + CompleteLoading(); + loop.Run(); } -TEST_F(DeferredCookieTaskTest, DeferredDeleteAllForHostCookies) { +TEST_F(DeferredCookieTaskTest, DeferredDeleteAllForHostCreatedBetweenCookies) { MockDeleteCallback delete_callback; - BeginWithForDomainKey( - http_www_google_.domain(), - DeleteAllForHostAction(&cookie_monster(), http_www_google_.url(), - &delete_callback)); + BeginWithForDomainKey(http_www_google_.domain(), + DeleteAllCreatedBetweenForHostAction( + &cookie_monster(), base::Time(), base::Time::Now(), + http_www_google_.url(), &delete_callback)); WaitForLoadCall(); EXPECT_CALL(delete_callback, Invoke(false)) - .WillOnce(DeleteAllForHostAction( - &cookie_monster(), http_www_google_.url(), &delete_callback)); - EXPECT_CALL(delete_callback, Invoke(false)) - .WillOnce(QuitCurrentMessageLoop()); + .WillOnce(DeleteAllCreatedBetweenForHostAction( + &cookie_monster(), base::Time(), base::Time::Now(), + http_www_google_.url(), &delete_callback)); + base::RunLoop loop; + EXPECT_CALL(delete_callback, Invoke(false)).WillOnce(QuitRunLoop(&loop)); - CompleteLoadingAndWait(); + CompleteLoading(); + loop.Run(); } TEST_F(DeferredCookieTaskTest, DeferredDeleteCanonicalCookie) { std::vector<CanonicalCookie*> cookies; - CanonicalCookie cookie = BuildCanonicalCookie( - http_www_google_.host(), "X=1; path=/", base::Time::Now()); + scoped_ptr<CanonicalCookie> cookie = BuildCanonicalCookie( + http_www_google_.url(), "X=1; path=/", base::Time::Now()); - MockDeleteCookieCallback delete_cookie_callback; + MockDeleteCallback delete_cookie_callback; - BeginWith(DeleteCanonicalCookieAction(&cookie_monster(), cookie, + BeginWith(DeleteCanonicalCookieAction(&cookie_monster(), *cookie, &delete_cookie_callback)); WaitForLoadCall(); - EXPECT_CALL(delete_cookie_callback, Invoke(false)) - .WillOnce(DeleteCanonicalCookieAction(&cookie_monster(), cookie, + EXPECT_CALL(delete_cookie_callback, Invoke(0)) + .WillOnce(DeleteCanonicalCookieAction(&cookie_monster(), *cookie, &delete_cookie_callback)); - EXPECT_CALL(delete_cookie_callback, Invoke(false)) - .WillOnce(QuitCurrentMessageLoop()); + base::RunLoop loop; + EXPECT_CALL(delete_cookie_callback, Invoke(0)).WillOnce(QuitRunLoop(&loop)); - CompleteLoadingAndWait(); + CompleteLoading(); + loop.Run(); } TEST_F(DeferredCookieTaskTest, DeferredDeleteSessionCookies) { @@ -1190,17 +1103,18 @@ TEST_F(DeferredCookieTaskTest, DeferredDeleteSessionCookies) { EXPECT_CALL(delete_callback, Invoke(false)) .WillOnce( DeleteSessionCookiesAction(&cookie_monster(), &delete_callback)); - EXPECT_CALL(delete_callback, Invoke(false)) - .WillOnce(QuitCurrentMessageLoop()); + base::RunLoop loop; + EXPECT_CALL(delete_callback, Invoke(false)).WillOnce(QuitRunLoop(&loop)); - CompleteLoadingAndWait(); + CompleteLoading(); + loop.Run(); } // Verify that a series of queued tasks are executed in order upon loading of // the backing store and that new tasks received while the queued tasks are // being dispatched go to the end of the queue. TEST_F(DeferredCookieTaskTest, DeferredTaskOrder) { - DeclareLoadedCookie(http_www_google_.host(), + DeclareLoadedCookie(http_www_google_.url(), "X=1; path=/; expires=Mon, 18-Apr-22 22:50:14 GMT", Time::Now() + TimeDelta::FromDays(3)); @@ -1215,7 +1129,7 @@ TEST_F(DeferredCookieTaskTest, DeferredTaskOrder) { SetCookieAction(&cookie_monster(), http_www_google_.url(), "A=B", &set_cookies_callback))); ExpectLoadCall(); - ExpectLoadForKeyCall(http_www_google_.domain(), false); + ExpectLoadForKeyCall(http_www_google_.domain()); Begin(); WaitForLoadCall(); @@ -1223,15 +1137,17 @@ TEST_F(DeferredCookieTaskTest, DeferredTaskOrder) { .WillOnce(GetCookiesAction(&cookie_monster(), http_www_google_.url(), &get_cookies_callback_deferred)); EXPECT_CALL(set_cookies_callback, Invoke(true)); + base::RunLoop loop; EXPECT_CALL(get_cookies_callback_deferred, Invoke("A=B; X=1")) - .WillOnce(QuitCurrentMessageLoop()); + .WillOnce(QuitRunLoop(&loop)); - CompleteLoadingAndWait(); + CompleteLoading(); + loop.Run(); } TEST_F(CookieMonsterTest, TestCookieDeleteAll) { scoped_refptr<MockPersistentCookieStore> store(new MockPersistentCookieStore); - scoped_refptr<CookieMonster> cm(new CookieMonster(store.get(), NULL)); + scoped_ptr<CookieMonster> cm(new CookieMonster(store.get(), nullptr)); CookieOptions options; options.set_include_httponly(); @@ -1264,7 +1180,7 @@ TEST_F(CookieMonsterTest, TestCookieDeleteAll) { } TEST_F(CookieMonsterTest, TestCookieDeleteAllCreatedBetweenTimestamps) { - scoped_refptr<CookieMonster> cm(new CookieMonster(NULL, NULL)); + scoped_ptr<CookieMonster> cm(new CookieMonster(nullptr, nullptr)); Time now = Time::Now(); // Nothing has been added so nothing should be deleted. @@ -1306,8 +1222,8 @@ TEST_F(CookieMonsterTest, TestCookieDeleteAllCreatedBetweenTimestamps) { static const int kAccessDelayMs = kLastAccessThresholdMilliseconds + 20; TEST_F(CookieMonsterTest, TestLastAccess) { - scoped_refptr<CookieMonster> cm( - new CookieMonster(NULL, NULL, kLastAccessThresholdMilliseconds)); + scoped_ptr<CookieMonster> cm( + new CookieMonster(nullptr, nullptr, kLastAccessThresholdMilliseconds)); EXPECT_TRUE(SetCookie(cm.get(), http_www_google_.url(), "A=B")); const Time last_access_date(GetFirstCookieAccessDate(cm.get())); @@ -1315,12 +1231,33 @@ TEST_F(CookieMonsterTest, TestLastAccess) { // Reading the cookie again immediately shouldn't update the access date, // since we're inside the threshold. EXPECT_EQ("A=B", GetCookies(cm.get(), http_www_google_.url())); - EXPECT_TRUE(last_access_date == GetFirstCookieAccessDate(cm.get())); + EXPECT_EQ(last_access_date, GetFirstCookieAccessDate(cm.get())); - // Reading after a short wait should update the access date. + // Reading after a short wait will update the access date, if the cookie + // is requested with options that would update the access date. First, test + // that the flag's behavior is respected. base::PlatformThread::Sleep( base::TimeDelta::FromMilliseconds(kAccessDelayMs)); - EXPECT_EQ("A=B", GetCookies(cm.get(), http_www_google_.url())); + CookieOptions options; + options.set_do_not_update_access_time(); + EXPECT_EQ("A=B", + GetCookiesWithOptions(cm.get(), http_www_google_.url(), options)); + EXPECT_EQ(last_access_date, GetFirstCookieAccessDate(cm.get())); + + // Getting all cookies for a URL doesn't update the accessed time either. + CookieList cookies = GetAllCookiesForURL(cm.get(), http_www_google_.url()); + CookieList::iterator it = cookies.begin(); + ASSERT_TRUE(it != cookies.end()); + EXPECT_EQ(http_www_google_.host(), it->Domain()); + EXPECT_EQ("A", it->Name()); + EXPECT_EQ("B", it->Value()); + EXPECT_EQ(last_access_date, GetFirstCookieAccessDate(cm.get())); + EXPECT_TRUE(++it == cookies.end()); + + // If the flag isn't set, the last accessed time should be updated. + options = CookieOptions(); + EXPECT_EQ("A=B", + GetCookiesWithOptions(cm.get(), http_www_google_.url(), options)); EXPECT_FALSE(last_access_date == GetFirstCookieAccessDate(cm.get())); } @@ -1332,29 +1269,14 @@ TEST_F(CookieMonsterTest, TestPriorityAwareGarbageCollection) { TestPriorityAwareGarbageCollectHelper(); } -TEST_F(CookieMonsterTest, TestDeleteSingleCookie) { - scoped_refptr<CookieMonster> cm(new CookieMonster(NULL, NULL)); - - EXPECT_TRUE(SetCookie(cm.get(), http_www_google_.url(), "A=B")); - EXPECT_TRUE(SetCookie(cm.get(), http_www_google_.url(), "C=D")); - EXPECT_TRUE(SetCookie(cm.get(), http_www_google_.url(), "E=F")); - EXPECT_EQ("A=B; C=D; E=F", GetCookies(cm.get(), http_www_google_.url())); - - EXPECT_TRUE( - FindAndDeleteCookie(cm.get(), http_www_google_.url().host(), "C")); - EXPECT_EQ("A=B; E=F", GetCookies(cm.get(), http_www_google_.url())); - - EXPECT_FALSE(FindAndDeleteCookie(cm.get(), "random.host", "E")); - EXPECT_EQ("A=B; E=F", GetCookies(cm.get(), http_www_google_.url())); -} - TEST_F(CookieMonsterTest, SetCookieableSchemes) { - scoped_refptr<CookieMonster> cm(new CookieMonster(NULL, NULL)); - scoped_refptr<CookieMonster> cm_foo(new CookieMonster(NULL, NULL)); + scoped_ptr<CookieMonster> cm(new CookieMonster(nullptr, nullptr)); + scoped_ptr<CookieMonster> cm_foo(new CookieMonster(nullptr, nullptr)); // Only cm_foo should allow foo:// cookies. - const char* const kSchemes[] = {"foo"}; - cm_foo->SetCookieableSchemes(kSchemes, 1); + std::vector<std::string> schemes; + schemes.push_back("foo"); + cm_foo->SetCookieableSchemes(schemes); GURL foo_url("foo://host/path"); GURL http_url("http://host/path"); @@ -1366,8 +1288,8 @@ TEST_F(CookieMonsterTest, SetCookieableSchemes) { } TEST_F(CookieMonsterTest, GetAllCookiesForURL) { - scoped_refptr<CookieMonster> cm( - new CookieMonster(NULL, NULL, kLastAccessThresholdMilliseconds)); + scoped_ptr<CookieMonster> cm( + new CookieMonster(nullptr, nullptr, kLastAccessThresholdMilliseconds)); // Create an httponly cookie. CookieOptions options; @@ -1431,11 +1353,11 @@ TEST_F(CookieMonsterTest, GetAllCookiesForURL) { ASSERT_TRUE(++it == cookies.end()); // Reading after a short wait should not update the access date. - EXPECT_TRUE(last_access_date == GetFirstCookieAccessDate(cm.get())); + EXPECT_EQ(last_access_date, GetFirstCookieAccessDate(cm.get())); } TEST_F(CookieMonsterTest, GetAllCookiesForURLPathMatching) { - scoped_refptr<CookieMonster> cm(new CookieMonster(NULL, NULL)); + scoped_ptr<CookieMonster> cm(new CookieMonster(nullptr, nullptr)); CookieOptions options; EXPECT_TRUE(SetCookieWithOptions(cm.get(), www_google_foo_.url(), @@ -1473,7 +1395,7 @@ TEST_F(CookieMonsterTest, GetAllCookiesForURLPathMatching) { } TEST_F(CookieMonsterTest, CookieSorting) { - scoped_refptr<CookieMonster> cm(new CookieMonster(NULL, NULL)); + scoped_ptr<CookieMonster> cm(new CookieMonster(nullptr, nullptr)); EXPECT_TRUE(SetCookie(cm.get(), http_www_google_.url(), "B=B1; path=/")); EXPECT_TRUE(SetCookie(cm.get(), http_www_google_.url(), "B=B2; path=/foo")); @@ -1503,7 +1425,7 @@ TEST_F(CookieMonsterTest, CookieSorting) { } TEST_F(CookieMonsterTest, DeleteCookieByName) { - scoped_refptr<CookieMonster> cm(new CookieMonster(NULL, NULL)); + scoped_ptr<CookieMonster> cm(new CookieMonster(nullptr, nullptr)); EXPECT_TRUE(SetCookie(cm.get(), http_www_google_.url(), "A=A1; path=/")); EXPECT_TRUE(SetCookie(cm.get(), http_www_google_.url(), "A=A2; path=/foo")); @@ -1523,40 +1445,6 @@ TEST_F(CookieMonsterTest, DeleteCookieByName) { } } -TEST_F(CookieMonsterTest, ImportCookiesFromCookieMonster) { - scoped_refptr<CookieMonster> cm_1(new CookieMonster(NULL, NULL)); - CookieOptions options; - - EXPECT_TRUE(SetCookieWithOptions(cm_1.get(), www_google_foo_.url(), - "A1=B; path=/foo;", options)); - EXPECT_TRUE(SetCookieWithOptions(cm_1.get(), www_google_bar_.url(), - "A2=D; path=/bar;", options)); - EXPECT_TRUE(SetCookieWithOptions(cm_1.get(), http_www_google_.url(), "A3=F;", - options)); - - CookieList cookies_1 = GetAllCookies(cm_1.get()); - scoped_refptr<CookieMonster> cm_2(new CookieMonster(NULL, NULL)); - ASSERT_TRUE(cm_2->ImportCookies(cookies_1)); - CookieList cookies_2 = GetAllCookies(cm_2.get()); - - size_t expected_size = 3; - EXPECT_EQ(expected_size, cookies_2.size()); - - CookieList::iterator it = cookies_2.begin(); - - ASSERT_TRUE(it != cookies_2.end()); - EXPECT_EQ("A1", it->Name()); - EXPECT_EQ("/foo", it->Path()); - - ASSERT_TRUE(++it != cookies_2.end()); - EXPECT_EQ("A2", it->Name()); - EXPECT_EQ("/bar", it->Path()); - - ASSERT_TRUE(++it != cookies_2.end()); - EXPECT_EQ("A3", it->Name()); - EXPECT_EQ("/", it->Path()); -} - // Tests importing from a persistent cookie store that contains duplicate // equivalent cookies. This situation should be handled by removing the // duplicate cookie (both from the in-memory cache, and from the backing store). @@ -1576,20 +1464,20 @@ TEST_F(CookieMonsterTest, DontImportDuplicateCookies) { // dates. We expect only the most recent one to be preserved following // the import. - AddCookieToList("www.google.com", + AddCookieToList(GURL("http://www.google.com"), "X=1; path=/; expires=Mon, 18-Apr-22 22:50:14 GMT", Time::Now() + TimeDelta::FromDays(3), &initial_cookies); - AddCookieToList("www.google.com", + AddCookieToList(GURL("http://www.google.com"), "X=2; path=/; expires=Mon, 18-Apr-22 22:50:14 GMT", Time::Now() + TimeDelta::FromDays(1), &initial_cookies); // ===> This one is the WINNER (biggest creation time). <==== - AddCookieToList("www.google.com", + AddCookieToList(GURL("http://www.google.com"), "X=3; path=/; expires=Mon, 18-Apr-22 22:50:14 GMT", Time::Now() + TimeDelta::FromDays(4), &initial_cookies); - AddCookieToList("www.google.com", + AddCookieToList(GURL("http://www.google.com"), "X=4; path=/; expires=Mon, 18-Apr-22 22:50:14 GMT", Time::Now(), &initial_cookies); @@ -1597,23 +1485,23 @@ TEST_F(CookieMonsterTest, DontImportDuplicateCookies) { // dates. We expect only the most recent one to be preserved the import. // ===> This one is the WINNER (biggest creation time). <==== - AddCookieToList("www.google.com", + AddCookieToList(GURL("http://www.google.com"), "X=a1; path=/2; expires=Mon, 18-Apr-22 22:50:14 GMT", Time::Now() + TimeDelta::FromDays(9), &initial_cookies); - AddCookieToList("www.google.com", + AddCookieToList(GURL("http://www.google.com"), "X=a2; path=/2; expires=Mon, 18-Apr-22 22:50:14 GMT", Time::Now() + TimeDelta::FromDays(2), &initial_cookies); // Insert 1 cookie with name "Y" on path "/". - AddCookieToList("www.google.com", + AddCookieToList(GURL("http://www.google.com"), "Y=a; path=/; expires=Mon, 18-Apr-22 22:50:14 GMT", Time::Now() + TimeDelta::FromDays(10), &initial_cookies); // Inject our initial cookies into the mock PersistentCookieStore. store->SetLoadExpectation(true, initial_cookies); - scoped_refptr<CookieMonster> cm(new CookieMonster(store.get(), NULL)); + scoped_ptr<CookieMonster> cm(new CookieMonster(store.get(), nullptr)); // Verify that duplicates were not imported for path "/". // (If this had failed, GetCookies() would have also returned X=1, X=2, X=4). @@ -1648,20 +1536,28 @@ TEST_F(CookieMonsterTest, DontImportDuplicateCreationTimes) { // two cookies remaining, but which two (other than that there should // be one from each set) will be random. std::vector<CanonicalCookie*> initial_cookies; - AddCookieToList("www.google.com", "X=1; path=/", now, &initial_cookies); - AddCookieToList("www.google.com", "X=2; path=/", now, &initial_cookies); - AddCookieToList("www.google.com", "X=3; path=/", now, &initial_cookies); - AddCookieToList("www.google.com", "X=4; path=/", now, &initial_cookies); - - AddCookieToList("www.google.com", "Y=1; path=/", earlier, &initial_cookies); - AddCookieToList("www.google.com", "Y=2; path=/", earlier, &initial_cookies); - AddCookieToList("www.google.com", "Y=3; path=/", earlier, &initial_cookies); - AddCookieToList("www.google.com", "Y=4; path=/", earlier, &initial_cookies); + AddCookieToList(GURL("http://www.google.com"), "X=1; path=/", now, + &initial_cookies); + AddCookieToList(GURL("http://www.google.com"), "X=2; path=/", now, + &initial_cookies); + AddCookieToList(GURL("http://www.google.com"), "X=3; path=/", now, + &initial_cookies); + AddCookieToList(GURL("http://www.google.com"), "X=4; path=/", now, + &initial_cookies); + + AddCookieToList(GURL("http://www.google.com"), "Y=1; path=/", earlier, + &initial_cookies); + AddCookieToList(GURL("http://www.google.com"), "Y=2; path=/", earlier, + &initial_cookies); + AddCookieToList(GURL("http://www.google.com"), "Y=3; path=/", earlier, + &initial_cookies); + AddCookieToList(GURL("http://www.google.com"), "Y=4; path=/", earlier, + &initial_cookies); // Inject our initial cookies into the mock PersistentCookieStore. store->SetLoadExpectation(true, initial_cookies); - scoped_refptr<CookieMonster> cm(new CookieMonster(store.get(), NULL)); + scoped_ptr<CookieMonster> cm(new CookieMonster(store.get(), nullptr)); CookieList list(GetAllCookies(cm.get())); EXPECT_EQ(2U, list.size()); @@ -1677,8 +1573,7 @@ TEST_F(CookieMonsterTest, CookieMonsterDelegate) { scoped_refptr<MockPersistentCookieStore> store(new MockPersistentCookieStore); scoped_refptr<MockCookieMonsterDelegate> delegate( new MockCookieMonsterDelegate); - scoped_refptr<CookieMonster> cm( - new CookieMonster(store.get(), delegate.get())); + scoped_ptr<CookieMonster> cm(new CookieMonster(store.get(), delegate.get())); EXPECT_TRUE(SetCookie(cm.get(), http_www_google_.url(), "A=B")); EXPECT_TRUE(SetCookie(cm.get(), http_www_google_.url(), "C=D")); @@ -1756,79 +1651,8 @@ TEST_F(CookieMonsterTest, CookieMonsterDelegate) { delegate->reset(); } -TEST_F(CookieMonsterTest, SetCookieWithDetails) { - scoped_refptr<CookieMonster> cm(new CookieMonster(NULL, NULL)); - - EXPECT_TRUE(SetCookieWithDetails(cm.get(), www_google_foo_.url(), "A", "B", - std::string(), "/foo", base::Time(), false, - false, false, COOKIE_PRIORITY_DEFAULT)); - EXPECT_TRUE(SetCookieWithDetails( - cm.get(), www_google_bar_.url(), "C", "D", www_google_bar_.domain(), - "/bar", base::Time(), false, true, false, COOKIE_PRIORITY_DEFAULT)); - EXPECT_TRUE(SetCookieWithDetails( - cm.get(), http_www_google_.url(), "E", "F", std::string(), std::string(), - base::Time(), true, false, false, COOKIE_PRIORITY_DEFAULT)); - - // Test that malformed attributes fail to set the cookie. - EXPECT_FALSE(SetCookieWithDetails(cm.get(), www_google_foo_.url(), " A", "B", - std::string(), "/foo", base::Time(), false, - false, false, COOKIE_PRIORITY_DEFAULT)); - EXPECT_FALSE(SetCookieWithDetails(cm.get(), www_google_foo_.url(), "A;", "B", - std::string(), "/foo", base::Time(), false, - false, false, COOKIE_PRIORITY_DEFAULT)); - EXPECT_FALSE(SetCookieWithDetails(cm.get(), www_google_foo_.url(), "A=", "B", - std::string(), "/foo", base::Time(), false, - false, false, COOKIE_PRIORITY_DEFAULT)); - EXPECT_FALSE(SetCookieWithDetails( - cm.get(), www_google_foo_.url(), "A", "B", "google.ozzzzzzle", "foo", - base::Time(), false, false, false, COOKIE_PRIORITY_DEFAULT)); - EXPECT_FALSE(SetCookieWithDetails(cm.get(), www_google_foo_.url(), "A=", "B", - std::string(), "foo", base::Time(), false, - false, false, COOKIE_PRIORITY_DEFAULT)); - - CookieList cookies = GetAllCookiesForURL(cm.get(), www_google_foo_.url()); - CookieList::iterator it = cookies.begin(); - - ASSERT_TRUE(it != cookies.end()); - EXPECT_EQ("A", it->Name()); - EXPECT_EQ("B", it->Value()); - EXPECT_EQ(www_google_foo_.host(), it->Domain()); - EXPECT_EQ("/foo", it->Path()); - EXPECT_FALSE(it->IsPersistent()); - EXPECT_FALSE(it->IsSecure()); - EXPECT_FALSE(it->IsHttpOnly()); - - ASSERT_TRUE(++it == cookies.end()); - - cookies = GetAllCookiesForURL(cm.get(), www_google_bar_.url()); - it = cookies.begin(); - - ASSERT_TRUE(it != cookies.end()); - EXPECT_EQ("C", it->Name()); - EXPECT_EQ("D", it->Value()); - EXPECT_EQ(www_google_bar_.Format(".%D"), it->Domain()); - EXPECT_EQ("/bar", it->Path()); - EXPECT_FALSE(it->IsSecure()); - EXPECT_TRUE(it->IsHttpOnly()); - - ASSERT_TRUE(++it == cookies.end()); - - cookies = GetAllCookiesForURL(cm.get(), https_www_google_.url()); - it = cookies.begin(); - - ASSERT_TRUE(it != cookies.end()); - EXPECT_EQ("E", it->Name()); - EXPECT_EQ("F", it->Value()); - EXPECT_EQ("/", it->Path()); - EXPECT_EQ(https_www_google_.host(), it->Domain()); - EXPECT_TRUE(it->IsSecure()); - EXPECT_FALSE(it->IsHttpOnly()); - - ASSERT_TRUE(++it == cookies.end()); -} - TEST_F(CookieMonsterTest, DeleteAllForHost) { - scoped_refptr<CookieMonster> cm(new CookieMonster(NULL, NULL)); + scoped_ptr<CookieMonster> cm(new CookieMonster(nullptr, nullptr)); // Test probes: // * Non-secure URL, mid-level (http://w.c.b.a) @@ -1838,7 +1662,7 @@ TEST_F(CookieMonsterTest, DeleteAllForHost) { // the http_only cookie, the host secure cookie, and the two host // path cookies. http_only, secure, and paths are ignored by // this call, and domain cookies arent touched. - PopulateCmForDeleteAllForHost(cm); + PopulateCmForDeleteAllForHost(cm.get()); EXPECT_EQ("dom_1=X; dom_2=X; dom_3=X; host_3=X", GetCookies(cm.get(), GURL(kTopLevelDomainPlus3))); EXPECT_EQ("dom_1=X; dom_2=X; host_2=X; sec_dom=X; sec_host=X", @@ -1851,7 +1675,9 @@ TEST_F(CookieMonsterTest, DeleteAllForHost) { GetCookies(cm.get(), GURL(kTopLevelDomainPlus2Secure + std::string("/dir1/dir2/xxx")))); - EXPECT_EQ(6, DeleteAllForHost(cm.get(), GURL(kTopLevelDomainPlus2))); + EXPECT_EQ(6, DeleteAllCreatedBetweenForHost(cm.get(), base::Time(), + base::Time::Now(), + GURL(kTopLevelDomainPlus2))); EXPECT_EQ(8U, GetAllCookies(cm.get()).size()); EXPECT_EQ("dom_1=X; dom_2=X; dom_3=X; host_3=X", @@ -1864,8 +1690,10 @@ TEST_F(CookieMonsterTest, DeleteAllForHost) { GetCookies(cm.get(), GURL(kTopLevelDomainPlus2Secure + std::string("/dir1/dir2/xxx")))); - PopulateCmForDeleteAllForHost(cm); - EXPECT_EQ(6, DeleteAllForHost(cm.get(), GURL(kTopLevelDomainPlus2Secure))); + PopulateCmForDeleteAllForHost(cm.get()); + EXPECT_EQ(6, DeleteAllCreatedBetweenForHost( + cm.get(), base::Time(), base::Time::Now(), + GURL(kTopLevelDomainPlus2Secure))); EXPECT_EQ(8U, GetAllCookies(cm.get()).size()); EXPECT_EQ("dom_1=X; dom_2=X; dom_3=X; host_3=X", @@ -1878,9 +1706,11 @@ TEST_F(CookieMonsterTest, DeleteAllForHost) { GetCookies(cm.get(), GURL(kTopLevelDomainPlus2Secure + std::string("/dir1/dir2/xxx")))); - PopulateCmForDeleteAllForHost(cm); - EXPECT_EQ(6, DeleteAllForHost(cm.get(), GURL(kTopLevelDomainPlus2Secure + - std::string("/dir1/xxx")))); + PopulateCmForDeleteAllForHost(cm.get()); + EXPECT_EQ(6, + DeleteAllCreatedBetweenForHost( + cm.get(), base::Time(), base::Time::Now(), + GURL(kTopLevelDomainPlus2Secure + std::string("/dir1/xxx")))); EXPECT_EQ(8U, GetAllCookies(cm.get()).size()); EXPECT_EQ("dom_1=X; dom_2=X; dom_3=X; host_3=X", @@ -1895,7 +1725,7 @@ TEST_F(CookieMonsterTest, DeleteAllForHost) { } TEST_F(CookieMonsterTest, UniqueCreationTime) { - scoped_refptr<CookieMonster> cm(new CookieMonster(NULL, NULL)); + scoped_ptr<CookieMonster> cm(new CookieMonster(nullptr, nullptr)); CookieOptions options; // Add in three cookies through every public interface to the @@ -1921,16 +1751,16 @@ TEST_F(CookieMonsterTest, UniqueCreationTime) { EXPECT_TRUE(SetCookieWithDetails( cm.get(), http_www_google_.url(), "setCookieWithDetails1", "A", - http_www_google_.Format(".%D"), "/", Time(), false, false, false, - COOKIE_PRIORITY_DEFAULT)); + http_www_google_.Format(".%D"), "/", Time(), Time(), Time(), false, false, + CookieSameSite::DEFAULT_MODE, COOKIE_PRIORITY_DEFAULT)); EXPECT_TRUE(SetCookieWithDetails( cm.get(), http_www_google_.url(), "setCookieWithDetails2", "A", - http_www_google_.Format(".%D"), "/", Time(), false, false, false, - COOKIE_PRIORITY_DEFAULT)); + http_www_google_.Format(".%D"), "/", Time(), Time(), Time(), false, false, + CookieSameSite::DEFAULT_MODE, COOKIE_PRIORITY_DEFAULT)); EXPECT_TRUE(SetCookieWithDetails( cm.get(), http_www_google_.url(), "setCookieWithDetails3", "A", - http_www_google_.Format(".%D"), "/", Time(), false, false, false, - COOKIE_PRIORITY_DEFAULT)); + http_www_google_.Format(".%D"), "/", Time(), Time(), Time(), false, false, + CookieSameSite::DEFAULT_MODE, COOKIE_PRIORITY_DEFAULT)); // Now we check CookieList cookie_list(GetAllCookies(cm.get())); @@ -1958,7 +1788,7 @@ TEST_F(CookieMonsterTest, UniqueCreationTime) { // Mainly a test of GetEffectiveDomain, or more specifically, of the // expected behavior of GetEffectiveDomain within the CookieMonster. TEST_F(CookieMonsterTest, GetKey) { - scoped_refptr<CookieMonster> cm(new CookieMonster(NULL, NULL)); + scoped_ptr<CookieMonster> cm(new CookieMonster(nullptr, nullptr)); // This test is really only interesting if GetKey() actually does something. EXPECT_EQ("google.com", cm->GetKey("www.google.com")); @@ -1990,47 +1820,25 @@ TEST_F(CookieMonsterTest, BackingStoreCommunication) { base::Time expires(base::Time::Now() + base::TimeDelta::FromSeconds(100)); const CookiesInputInfo input_info[] = { - {GURL("http://a.b.google.com"), - "a", - "1", - "", - "/path/to/cookie", - expires, - false, - false, - false, - COOKIE_PRIORITY_DEFAULT}, - {GURL("https://www.google.com"), - "b", - "2", - ".google.com", - "/path/from/cookie", - expires + TimeDelta::FromSeconds(10), - true, - true, - false, - COOKIE_PRIORITY_DEFAULT}, - {GURL("https://google.com"), - "c", - "3", - "", - "/another/path/to/cookie", - base::Time::Now() + base::TimeDelta::FromSeconds(100), - true, - false, - true, - COOKIE_PRIORITY_DEFAULT}}; + {GURL("http://a.b.google.com"), "a", "1", "", "/path/to/cookie", expires, + false, false, CookieSameSite::DEFAULT_MODE, COOKIE_PRIORITY_DEFAULT}, + {GURL("https://www.google.com"), "b", "2", ".google.com", + "/path/from/cookie", expires + TimeDelta::FromSeconds(10), true, true, + CookieSameSite::DEFAULT_MODE, COOKIE_PRIORITY_DEFAULT}, + {GURL("https://google.com"), "c", "3", "", "/another/path/to/cookie", + base::Time::Now() + base::TimeDelta::FromSeconds(100), true, false, + CookieSameSite::STRICT_MODE, COOKIE_PRIORITY_DEFAULT}}; const int INPUT_DELETE = 1; // Create new cookies and flush them to the store. { - scoped_refptr<CookieMonster> cmout(new CookieMonster(store.get(), NULL)); + scoped_ptr<CookieMonster> cmout(new CookieMonster(store.get(), nullptr)); for (const CookiesInputInfo* p = input_info; p < &input_info[arraysize(input_info)]; p++) { - EXPECT_TRUE(SetCookieWithDetails(cmout.get(), p->url, p->name, p->value, - p->domain, p->path, p->expiration_time, - p->secure, p->http_only, - p->first_party_only, p->priority)); + EXPECT_TRUE(SetCookieWithDetails( + cmout.get(), p->url, p->name, p->value, p->domain, p->path, + base::Time(), p->expiration_time, base::Time(), p->secure, + p->http_only, p->same_site, p->priority)); } GURL del_url(input_info[INPUT_DELETE] .url.Resolve(input_info[INPUT_DELETE].path) @@ -2040,7 +1848,7 @@ TEST_F(CookieMonsterTest, BackingStoreCommunication) { // Create a new cookie monster and make sure that everything is correct { - scoped_refptr<CookieMonster> cmin(new CookieMonster(store.get(), NULL)); + scoped_ptr<CookieMonster> cmin(new CookieMonster(store.get(), nullptr)); CookieList cookies(GetAllCookies(cmin.get())); ASSERT_EQ(2u, cookies.size()); // Ordering is path length, then creation time. So second cookie @@ -2059,7 +1867,7 @@ TEST_F(CookieMonsterTest, BackingStoreCommunication) { output->CreationDate().ToInternalValue()); EXPECT_EQ(input->secure, output->IsSecure()); EXPECT_EQ(input->http_only, output->IsHttpOnly()); - EXPECT_EQ(input->first_party_only, output->IsFirstPartyOnly()); + EXPECT_EQ(input->same_site, output->SameSite()); EXPECT_TRUE(output->IsPersistent()); EXPECT_EQ(input->expiration_time.ToInternalValue(), output->ExpiryDate().ToInternalValue()); @@ -2070,7 +1878,7 @@ TEST_F(CookieMonsterTest, BackingStoreCommunication) { TEST_F(CookieMonsterTest, CookieListOrdering) { // Put a random set of cookies into a monster and make sure // they're returned in the right order. - scoped_refptr<CookieMonster> cm(new CookieMonster(NULL, NULL)); + scoped_ptr<CookieMonster> cm(new CookieMonster(nullptr, nullptr)); EXPECT_TRUE( SetCookie(cm.get(), GURL("http://d.c.b.a.google.com/aa/x.html"), "c=1")); EXPECT_TRUE(SetCookie(cm.get(), GURL("http://b.a.google.com/aa/bb/cc/x.html"), @@ -2127,7 +1935,7 @@ TEST_F(CookieMonsterTest, MAYBE_GarbageCollectionTriggers) { // First we check to make sure that a whole lot of recent cookies // doesn't get rid of anything after garbage collection is checked for. { - scoped_refptr<CookieMonster> cm( + scoped_ptr<CookieMonster> cm( CreateMonsterForGC(CookieMonster::kMaxCookies * 2)); EXPECT_EQ(CookieMonster::kMaxCookies * 2, GetAllCookies(cm.get()).size()); SetCookie(cm.get(), GURL("http://newdomain.com"), "b=2"); @@ -2169,9 +1977,9 @@ TEST_F(CookieMonsterTest, MAYBE_GarbageCollectionTriggers) { for (int ci = 0; ci < static_cast<int>(arraysize(test_cases)); ++ci) { const TestCase* test_case = &test_cases[ci]; - scoped_refptr<CookieMonster> cm(CreateMonsterFromStoreForGC( + scoped_ptr<CookieMonster> cm = CreateMonsterFromStoreForGC( test_case->num_cookies, test_case->num_old_cookies, 0, 0, - CookieMonster::kSafeFromGlobalPurgeDays * 2)); + CookieMonster::kSafeFromGlobalPurgeDays * 2); EXPECT_EQ(test_case->expected_initial_cookies, GetAllCookies(cm.get()).size()) << "For test case " << ci; @@ -2183,33 +1991,197 @@ TEST_F(CookieMonsterTest, MAYBE_GarbageCollectionTriggers) { } } -// This test checks that keep expired cookies flag is working. -TEST_F(CookieMonsterTest, KeepExpiredCookies) { - scoped_refptr<CookieMonster> cm(new CookieMonster(NULL, NULL)); - cm->SetKeepExpiredCookies(); - CookieOptions options; +// Tests that if the main load event happens before the loaded event for a +// particular key, the tasks for that key run first. +TEST_F(CookieMonsterTest, WhileLoadingLoadCompletesBeforeKeyLoadCompletes) { + const GURL kUrl = GURL(kTopLevelDomainPlus1); - // Set a persistent cookie. - ASSERT_TRUE(SetCookieWithOptions( - cm.get(), http_www_google_.url(), - std::string(kValidCookieLine) + "; expires=Mon, 18-Apr-22 22:50:13 GMT", - options)); + scoped_refptr<MockPersistentCookieStore> store(new MockPersistentCookieStore); + store->set_store_load_commands(true); + scoped_ptr<CookieMonster> cm(new CookieMonster(store.get(), nullptr)); + + // Get all cookies task that queues a task to set a cookie when executed. + ResultSavingCookieCallback<bool> set_cookie_callback; + cm->SetCookieWithOptionsAsync( + kUrl, "a=b", CookieOptions(), + base::Bind(&ResultSavingCookieCallback<bool>::Run, + base::Unretained(&set_cookie_callback))); + + GetCookieListCallback get_cookie_list_callback1; + cm->GetAllCookiesAsync( + base::Bind(&GetCookieListCallback::Run, + base::Unretained(&get_cookie_list_callback1))); + + // Two load events should have been queued. + ASSERT_EQ(2u, store->commands().size()); + ASSERT_EQ(CookieStoreCommand::LOAD, store->commands()[0].type); + ASSERT_EQ(CookieStoreCommand::LOAD_COOKIES_FOR_KEY, + store->commands()[1].type); - // Get the canonical cookie. - CookieList cookie_list = GetAllCookies(cm.get()); - ASSERT_EQ(1U, cookie_list.size()); + // The main load completes first (With no cookies). + store->commands()[0].loaded_callback.Run(std::vector<CanonicalCookie*>()); - // Use a past expiry date to delete the cookie. - ASSERT_TRUE(SetCookieWithOptions( - cm.get(), http_www_google_.url(), - std::string(kValidCookieLine) + "; expires=Mon, 18-Apr-1977 22:50:13 GMT", - options)); + // The tasks should run in order, and the get should see the cookies. + + set_cookie_callback.WaitUntilDone(); + EXPECT_TRUE(set_cookie_callback.result()); + + get_cookie_list_callback1.WaitUntilDone(); + EXPECT_EQ(1u, get_cookie_list_callback1.cookies().size()); + + // The loaded for key event completes late, with not cookies (Since they + // were already loaded). + store->commands()[1].loaded_callback.Run(std::vector<CanonicalCookie*>()); + + // The just set cookie should still be in the store. + GetCookieListCallback get_cookie_list_callback2; + cm->GetAllCookiesAsync( + base::Bind(&GetCookieListCallback::Run, + base::Unretained(&get_cookie_list_callback2))); + get_cookie_list_callback2.WaitUntilDone(); + EXPECT_EQ(1u, get_cookie_list_callback2.cookies().size()); +} + +// Tests that case that DeleteAll is waiting for load to complete, and then a +// get is queued. The get should wait to run until after all the cookies are +// retrieved, and should return nothing, since all cookies were just deleted. +TEST_F(CookieMonsterTest, WhileLoadingDeleteAllGetForURL) { + const GURL kUrl = GURL(kTopLevelDomainPlus1); + + scoped_refptr<MockPersistentCookieStore> store(new MockPersistentCookieStore); + store->set_store_load_commands(true); + scoped_ptr<CookieMonster> cm(new CookieMonster(store.get(), nullptr)); + + ResultSavingCookieCallback<int> delete_callback; + cm->DeleteAllAsync(base::Bind(&ResultSavingCookieCallback<int>::Run, + base::Unretained(&delete_callback))); + + GetCookieListCallback get_cookie_list_callback; + cm->GetCookieListWithOptionsAsync( + kUrl, CookieOptions(), + base::Bind(&GetCookieListCallback::Run, + base::Unretained(&get_cookie_list_callback))); + + // Only the main load should have been queued. + ASSERT_EQ(1u, store->commands().size()); + ASSERT_EQ(CookieStoreCommand::LOAD, store->commands()[0].type); + + std::vector<CanonicalCookie*> cookies; + // When passed to the CookieMonster, it takes ownership of the pointed to + // cookies. + cookies.push_back( + CanonicalCookie::Create(kUrl, "a=b", base::Time(), CookieOptions()) + .release()); + ASSERT_TRUE(cookies[0]); + store->commands()[0].loaded_callback.Run(cookies); + + delete_callback.WaitUntilDone(); + EXPECT_EQ(1, delete_callback.result()); + + get_cookie_list_callback.WaitUntilDone(); + EXPECT_EQ(0u, get_cookie_list_callback.cookies().size()); +} + +// Tests that a set cookie call sandwiched between two get all cookies, all +// before load completes, affects the first but not the second. The set should +// also not trigger a LoadCookiesForKey (As that could complete only after the +// main load for the store). +TEST_F(CookieMonsterTest, WhileLoadingGetAllSetGetAll) { + const GURL kUrl = GURL(kTopLevelDomainPlus1); + + scoped_refptr<MockPersistentCookieStore> store(new MockPersistentCookieStore); + store->set_store_load_commands(true); + scoped_ptr<CookieMonster> cm(new CookieMonster(store.get(), nullptr)); + + GetCookieListCallback get_cookie_list_callback1; + cm->GetAllCookiesAsync( + base::Bind(&GetCookieListCallback::Run, + base::Unretained(&get_cookie_list_callback1))); + + ResultSavingCookieCallback<bool> set_cookie_callback; + cm->SetCookieWithOptionsAsync( + kUrl, "a=b", CookieOptions(), + base::Bind(&ResultSavingCookieCallback<bool>::Run, + base::Unretained(&set_cookie_callback))); + + GetCookieListCallback get_cookie_list_callback2; + cm->GetAllCookiesAsync( + base::Bind(&GetCookieListCallback::Run, + base::Unretained(&get_cookie_list_callback2))); + + // Only the main load should have been queued. + ASSERT_EQ(1u, store->commands().size()); + ASSERT_EQ(CookieStoreCommand::LOAD, store->commands()[0].type); + + // The load completes (With no cookies). + store->commands()[0].loaded_callback.Run(std::vector<CanonicalCookie*>()); + + get_cookie_list_callback1.WaitUntilDone(); + EXPECT_EQ(0u, get_cookie_list_callback1.cookies().size()); + + set_cookie_callback.WaitUntilDone(); + EXPECT_TRUE(set_cookie_callback.result()); + + get_cookie_list_callback2.WaitUntilDone(); + EXPECT_EQ(1u, get_cookie_list_callback2.cookies().size()); +} + +namespace { + +void RunClosureOnCookieListReceived(const base::Closure& closure, + const CookieList& cookie_list) { + closure.Run(); +} + +} // namespace - // Check that the cookie with the past expiry date is still there. - // GetAllCookies() also triggers garbage collection. - cookie_list = GetAllCookies(cm.get()); - ASSERT_EQ(1U, cookie_list.size()); - ASSERT_TRUE(cookie_list[0].IsExpired(Time::Now())); +// Tests that if a single cookie task is queued as a result of a task performed +// on all cookies when loading completes, it will be run after any already +// queued tasks. +TEST_F(CookieMonsterTest, CheckOrderOfCookieTaskQueueWhenLoadingCompletes) { + const GURL kUrl = GURL(kTopLevelDomainPlus1); + + scoped_refptr<MockPersistentCookieStore> store(new MockPersistentCookieStore); + store->set_store_load_commands(true); + scoped_ptr<CookieMonster> cm(new CookieMonster(store.get(), nullptr)); + + // Get all cookies task that queues a task to set a cookie when executed. + ResultSavingCookieCallback<bool> set_cookie_callback; + cm->GetAllCookiesAsync(base::Bind( + &RunClosureOnCookieListReceived, + base::Bind(&CookieStore::SetCookieWithOptionsAsync, + base::Unretained(cm.get()), kUrl, "a=b", CookieOptions(), + base::Bind(&ResultSavingCookieCallback<bool>::Run, + base::Unretained(&set_cookie_callback))))); + + // Get cookie task. Queued before the delete task is executed, so should not + // see the set cookie. + GetCookieListCallback get_cookie_list_callback1; + cm->GetAllCookiesAsync( + base::Bind(&GetCookieListCallback::Run, + base::Unretained(&get_cookie_list_callback1))); + + // Only the main load should have been queued. + ASSERT_EQ(1u, store->commands().size()); + ASSERT_EQ(CookieStoreCommand::LOAD, store->commands()[0].type); + + // The load completes. + store->commands()[0].loaded_callback.Run(std::vector<CanonicalCookie*>()); + + // The get cookies call should see no cookies set. + get_cookie_list_callback1.WaitUntilDone(); + EXPECT_EQ(0u, get_cookie_list_callback1.cookies().size()); + + set_cookie_callback.WaitUntilDone(); + EXPECT_TRUE(set_cookie_callback.result()); + + // A subsequent get cookies call should see the new cookie. + GetCookieListCallback get_cookie_list_callback2; + cm->GetAllCookiesAsync( + base::Bind(&GetCookieListCallback::Run, + base::Unretained(&get_cookie_list_callback2))); + get_cookie_list_callback2.WaitUntilDone(); + EXPECT_EQ(1u, get_cookie_list_callback2.cookies().size()); } namespace { @@ -2273,7 +2245,7 @@ class CallbackCounter : public base::RefCountedThreadSafe<CallbackCounter> { TEST_F(CookieMonsterTest, FlushStore) { scoped_refptr<CallbackCounter> counter(new CallbackCounter()); scoped_refptr<FlushablePersistentStore> store(new FlushablePersistentStore()); - scoped_refptr<CookieMonster> cm(new CookieMonster(store.get(), NULL)); + scoped_ptr<CookieMonster> cm(new CookieMonster(store.get(), nullptr)); ASSERT_EQ(0, store->flush_count()); ASSERT_EQ(0, counter->callback_count()); @@ -2308,7 +2280,7 @@ TEST_F(CookieMonsterTest, FlushStore) { ASSERT_EQ(2, counter->callback_count()); // If there's no backing store, FlushStore() is always a safe no-op. - cm = new CookieMonster(NULL, NULL); + cm.reset(new CookieMonster(nullptr, nullptr)); GetAllCookies(cm.get()); // Force init. cm->FlushStore(base::Closure()); base::MessageLoop::current()->RunUntilIdle(); @@ -2323,7 +2295,7 @@ TEST_F(CookieMonsterTest, FlushStore) { TEST_F(CookieMonsterTest, SetAllCookies) { scoped_refptr<FlushablePersistentStore> store(new FlushablePersistentStore()); - scoped_refptr<CookieMonster> cm(new CookieMonster(store.get(), NULL)); + scoped_ptr<CookieMonster> cm(new CookieMonster(store.get(), nullptr)); cm->SetPersistSessionCookies(true); EXPECT_TRUE(SetCookie(cm.get(), http_www_google_.url(), "U=V; path=/")); @@ -2331,18 +2303,18 @@ TEST_F(CookieMonsterTest, SetAllCookies) { EXPECT_TRUE(SetCookie(cm.get(), http_www_google_.url(), "Y=Z; path=/")); CookieList list; - list.push_back(CanonicalCookie(http_www_google_.url(), "A", "B", - http_www_google_.url().host(), "/", - base::Time::Now(), base::Time(), base::Time(), - false, false, false, COOKIE_PRIORITY_DEFAULT)); - list.push_back(CanonicalCookie(http_www_google_.url(), "W", "X", - http_www_google_.url().host(), "/bar", - base::Time::Now(), base::Time(), base::Time(), - false, false, false, COOKIE_PRIORITY_DEFAULT)); - list.push_back(CanonicalCookie(http_www_google_.url(), "Y", "Z", - http_www_google_.url().host(), "/", - base::Time::Now(), base::Time(), base::Time(), - false, false, false, COOKIE_PRIORITY_DEFAULT)); + list.push_back(*CanonicalCookie::Create( + http_www_google_.url(), "A", "B", http_www_google_.url().host(), "/", + base::Time::Now(), base::Time(), false, false, + CookieSameSite::DEFAULT_MODE, false, COOKIE_PRIORITY_DEFAULT)); + list.push_back(*CanonicalCookie::Create( + http_www_google_.url(), "W", "X", http_www_google_.url().host(), "/bar", + base::Time::Now(), base::Time(), false, false, + CookieSameSite::DEFAULT_MODE, false, COOKIE_PRIORITY_DEFAULT)); + list.push_back(*CanonicalCookie::Create( + http_www_google_.url(), "Y", "Z", http_www_google_.url().host(), "/", + base::Time::Now(), base::Time(), false, false, + CookieSameSite::DEFAULT_MODE, false, COOKIE_PRIORITY_DEFAULT)); // SetAllCookies must not flush. ASSERT_EQ(0, store->flush_count()); @@ -2369,71 +2341,73 @@ TEST_F(CookieMonsterTest, SetAllCookies) { } TEST_F(CookieMonsterTest, ComputeCookieDiff) { - scoped_refptr<CookieMonster> cm(new CookieMonster(NULL, NULL)); + scoped_ptr<CookieMonster> cm(new CookieMonster(nullptr, nullptr)); base::Time now = base::Time::Now(); base::Time creation_time = now - base::TimeDelta::FromSeconds(1); - CanonicalCookie cookie1(http_www_google_.url(), "A", "B", - http_www_google_.url().host(), "/", creation_time, - base::Time(), base::Time(), false, false, false, - COOKIE_PRIORITY_DEFAULT); - CanonicalCookie cookie2(http_www_google_.url(), "C", "D", - http_www_google_.url().host(), "/", creation_time, - base::Time(), base::Time(), false, false, false, - COOKIE_PRIORITY_DEFAULT); - CanonicalCookie cookie3(http_www_google_.url(), "E", "F", - http_www_google_.url().host(), "/", creation_time, - base::Time(), base::Time(), false, false, false, - COOKIE_PRIORITY_DEFAULT); - CanonicalCookie cookie4(http_www_google_.url(), "G", "H", - http_www_google_.url().host(), "/", creation_time, - base::Time(), base::Time(), false, false, false, - COOKIE_PRIORITY_DEFAULT); - CanonicalCookie cookie4_with_new_value( + scoped_ptr<CanonicalCookie> cookie1(CanonicalCookie::Create( + http_www_google_.url(), "A", "B", http_www_google_.url().host(), "/", + creation_time, base::Time(), false, false, CookieSameSite::DEFAULT_MODE, + false, COOKIE_PRIORITY_DEFAULT)); + scoped_ptr<CanonicalCookie> cookie2(CanonicalCookie::Create( + http_www_google_.url(), "C", "D", http_www_google_.url().host(), "/", + creation_time, base::Time(), false, false, CookieSameSite::DEFAULT_MODE, + false, COOKIE_PRIORITY_DEFAULT)); + scoped_ptr<CanonicalCookie> cookie3(CanonicalCookie::Create( + http_www_google_.url(), "E", "F", http_www_google_.url().host(), "/", + creation_time, base::Time(), false, false, CookieSameSite::DEFAULT_MODE, + false, COOKIE_PRIORITY_DEFAULT)); + scoped_ptr<CanonicalCookie> cookie4(CanonicalCookie::Create( + http_www_google_.url(), "G", "H", http_www_google_.url().host(), "/", + creation_time, base::Time(), false, false, CookieSameSite::DEFAULT_MODE, + false, COOKIE_PRIORITY_DEFAULT)); + scoped_ptr<CanonicalCookie> cookie4_with_new_value(CanonicalCookie::Create( http_www_google_.url(), "G", "iamnew", http_www_google_.url().host(), "/", - creation_time, base::Time(), base::Time(), false, false, false, - COOKIE_PRIORITY_DEFAULT); - CanonicalCookie cookie5(http_www_google_.url(), "I", "J", - http_www_google_.url().host(), "/", creation_time, - base::Time(), base::Time(), false, false, false, - COOKIE_PRIORITY_DEFAULT); - CanonicalCookie cookie5_with_new_creation_time( - http_www_google_.url(), "I", "J", http_www_google_.url().host(), "/", now, - base::Time(), base::Time(), false, false, false, COOKIE_PRIORITY_DEFAULT); - CanonicalCookie cookie6(http_www_google_.url(), "K", "L", - http_www_google_.url().host(), "/foo", creation_time, - base::Time(), base::Time(), false, false, false, - COOKIE_PRIORITY_DEFAULT); - CanonicalCookie cookie6_with_new_path( + creation_time, base::Time(), false, false, CookieSameSite::DEFAULT_MODE, + false, COOKIE_PRIORITY_DEFAULT)); + scoped_ptr<CanonicalCookie> cookie5(CanonicalCookie::Create( + http_www_google_.url(), "I", "J", http_www_google_.url().host(), "/", + creation_time, base::Time(), false, false, CookieSameSite::DEFAULT_MODE, + false, COOKIE_PRIORITY_DEFAULT)); + scoped_ptr<CanonicalCookie> cookie5_with_new_creation_time( + CanonicalCookie::Create( + http_www_google_.url(), "I", "J", http_www_google_.url().host(), "/", + now, base::Time(), false, false, CookieSameSite::DEFAULT_MODE, false, + COOKIE_PRIORITY_DEFAULT)); + scoped_ptr<CanonicalCookie> cookie6(CanonicalCookie::Create( + http_www_google_.url(), "K", "L", http_www_google_.url().host(), "/foo", + creation_time, base::Time(), false, false, CookieSameSite::DEFAULT_MODE, + false, COOKIE_PRIORITY_DEFAULT)); + scoped_ptr<CanonicalCookie> cookie6_with_new_path(CanonicalCookie::Create( http_www_google_.url(), "K", "L", http_www_google_.url().host(), "/bar", - creation_time, base::Time(), base::Time(), false, false, false, - COOKIE_PRIORITY_DEFAULT); - CanonicalCookie cookie7(http_www_google_.url(), "M", "N", - http_www_google_.url().host(), "/foo", creation_time, - base::Time(), base::Time(), false, false, false, - COOKIE_PRIORITY_DEFAULT); - CanonicalCookie cookie7_with_new_path( + creation_time, base::Time(), false, false, CookieSameSite::DEFAULT_MODE, + false, COOKIE_PRIORITY_DEFAULT)); + scoped_ptr<CanonicalCookie> cookie7(CanonicalCookie::Create( + http_www_google_.url(), "M", "N", http_www_google_.url().host(), "/foo", + creation_time, base::Time(), false, false, CookieSameSite::DEFAULT_MODE, + false, COOKIE_PRIORITY_DEFAULT)); + scoped_ptr<CanonicalCookie> cookie7_with_new_path(CanonicalCookie::Create( http_www_google_.url(), "M", "N", http_www_google_.url().host(), "/bar", - creation_time, base::Time(), base::Time(), false, false, false, - COOKIE_PRIORITY_DEFAULT); + creation_time, base::Time(), false, false, CookieSameSite::DEFAULT_MODE, + false, COOKIE_PRIORITY_DEFAULT)); CookieList old_cookies; - old_cookies.push_back(cookie1); - old_cookies.push_back(cookie2); - old_cookies.push_back(cookie4); - old_cookies.push_back(cookie5); - old_cookies.push_back(cookie6); - old_cookies.push_back(cookie7); + old_cookies.push_back(*cookie1); + old_cookies.push_back(*cookie2); + old_cookies.push_back(*cookie4); + old_cookies.push_back(*cookie5); + old_cookies.push_back(*cookie6); + old_cookies.push_back(*cookie7); CookieList new_cookies; - new_cookies.push_back(cookie1); - new_cookies.push_back(cookie3); - new_cookies.push_back(cookie4_with_new_value); - new_cookies.push_back(cookie5_with_new_creation_time); - new_cookies.push_back(cookie6_with_new_path); - new_cookies.push_back(cookie7); - new_cookies.push_back(cookie7_with_new_path); + new_cookies.push_back(*cookie1); + new_cookies.push_back(*cookie3); + new_cookies.push_back(*cookie4_with_new_value); + new_cookies.push_back(*cookie5_with_new_creation_time); + new_cookies.push_back(*cookie6_with_new_path); + new_cookies.push_back(*cookie7); + new_cookies.push_back(*cookie7_with_new_path); CookieList cookies_to_add; CookieList cookies_to_delete; @@ -2442,51 +2416,51 @@ TEST_F(CookieMonsterTest, ComputeCookieDiff) { &cookies_to_delete); // |cookie1| has not changed. - EXPECT_FALSE(IsCookieInList(cookie1, cookies_to_add)); - EXPECT_FALSE(IsCookieInList(cookie1, cookies_to_delete)); + EXPECT_FALSE(IsCookieInList(*cookie1, cookies_to_add)); + EXPECT_FALSE(IsCookieInList(*cookie1, cookies_to_delete)); // |cookie2| has been deleted. - EXPECT_FALSE(IsCookieInList(cookie2, cookies_to_add)); - EXPECT_TRUE(IsCookieInList(cookie2, cookies_to_delete)); + EXPECT_FALSE(IsCookieInList(*cookie2, cookies_to_add)); + EXPECT_TRUE(IsCookieInList(*cookie2, cookies_to_delete)); // |cookie3| has been added. - EXPECT_TRUE(IsCookieInList(cookie3, cookies_to_add)); - EXPECT_FALSE(IsCookieInList(cookie3, cookies_to_delete)); + EXPECT_TRUE(IsCookieInList(*cookie3, cookies_to_add)); + EXPECT_FALSE(IsCookieInList(*cookie3, cookies_to_delete)); // |cookie4| has a new value: new cookie overrides the old one (which does not // need to be explicitly removed). - EXPECT_FALSE(IsCookieInList(cookie4, cookies_to_add)); - EXPECT_FALSE(IsCookieInList(cookie4, cookies_to_delete)); - EXPECT_TRUE(IsCookieInList(cookie4_with_new_value, cookies_to_add)); - EXPECT_FALSE(IsCookieInList(cookie4_with_new_value, cookies_to_delete)); + EXPECT_FALSE(IsCookieInList(*cookie4, cookies_to_add)); + EXPECT_FALSE(IsCookieInList(*cookie4, cookies_to_delete)); + EXPECT_TRUE(IsCookieInList(*cookie4_with_new_value, cookies_to_add)); + EXPECT_FALSE(IsCookieInList(*cookie4_with_new_value, cookies_to_delete)); // |cookie5| has a new creation time: new cookie overrides the old one (which // does not need to be explicitly removed). - EXPECT_FALSE(IsCookieInList(cookie5, cookies_to_add)); - EXPECT_FALSE(IsCookieInList(cookie5, cookies_to_delete)); - EXPECT_TRUE(IsCookieInList(cookie5_with_new_creation_time, cookies_to_add)); + EXPECT_FALSE(IsCookieInList(*cookie5, cookies_to_add)); + EXPECT_FALSE(IsCookieInList(*cookie5, cookies_to_delete)); + EXPECT_TRUE(IsCookieInList(*cookie5_with_new_creation_time, cookies_to_add)); EXPECT_FALSE( - IsCookieInList(cookie5_with_new_creation_time, cookies_to_delete)); + IsCookieInList(*cookie5_with_new_creation_time, cookies_to_delete)); // |cookie6| has a new path: the new cookie does not overrides the old one, // which needs to be explicitly removed. - EXPECT_FALSE(IsCookieInList(cookie6, cookies_to_add)); - EXPECT_TRUE(IsCookieInList(cookie6, cookies_to_delete)); - EXPECT_TRUE(IsCookieInList(cookie6_with_new_path, cookies_to_add)); - EXPECT_FALSE(IsCookieInList(cookie6_with_new_path, cookies_to_delete)); + EXPECT_FALSE(IsCookieInList(*cookie6, cookies_to_add)); + EXPECT_TRUE(IsCookieInList(*cookie6, cookies_to_delete)); + EXPECT_TRUE(IsCookieInList(*cookie6_with_new_path, cookies_to_add)); + EXPECT_FALSE(IsCookieInList(*cookie6_with_new_path, cookies_to_delete)); // |cookie7| is kept and |cookie7_with_new_path| is added as a new cookie. - EXPECT_FALSE(IsCookieInList(cookie7, cookies_to_add)); - EXPECT_FALSE(IsCookieInList(cookie7, cookies_to_delete)); - EXPECT_TRUE(IsCookieInList(cookie7_with_new_path, cookies_to_add)); - EXPECT_FALSE(IsCookieInList(cookie7_with_new_path, cookies_to_delete)); + EXPECT_FALSE(IsCookieInList(*cookie7, cookies_to_add)); + EXPECT_FALSE(IsCookieInList(*cookie7, cookies_to_delete)); + EXPECT_TRUE(IsCookieInList(*cookie7_with_new_path, cookies_to_add)); + EXPECT_FALSE(IsCookieInList(*cookie7_with_new_path, cookies_to_delete)); } // Check that DeleteAll does flush (as a sanity check that flush_count() // works). TEST_F(CookieMonsterTest, DeleteAll) { scoped_refptr<FlushablePersistentStore> store(new FlushablePersistentStore()); - scoped_refptr<CookieMonster> cm(new CookieMonster(store.get(), NULL)); + scoped_ptr<CookieMonster> cm(new CookieMonster(store.get(), nullptr)); cm->SetPersistSessionCookies(true); EXPECT_TRUE(SetCookie(cm.get(), http_www_google_.url(), "X=Y; path=/")); @@ -2497,7 +2471,7 @@ TEST_F(CookieMonsterTest, DeleteAll) { } TEST_F(CookieMonsterTest, HistogramCheck) { - scoped_refptr<CookieMonster> cm(new CookieMonster(NULL, NULL)); + scoped_ptr<CookieMonster> cm(new CookieMonster(nullptr, nullptr)); // Should match call in InitializeHistograms, but doesn't really matter // since the histogram should have been initialized by the CM construction // above. @@ -2508,9 +2482,9 @@ TEST_F(CookieMonsterTest, HistogramCheck) { scoped_ptr<base::HistogramSamples> samples1( expired_histogram->SnapshotSamples()); ASSERT_TRUE(SetCookieWithDetails( - cm.get(), GURL("http://fake.a.url"), "a", "b", "a.url", "/", - base::Time::Now() + base::TimeDelta::FromMinutes(59), false, false, false, - COOKIE_PRIORITY_DEFAULT)); + cm.get(), GURL("http://fake.a.url"), "a", "b", "a.url", "/", base::Time(), + base::Time::Now() + base::TimeDelta::FromMinutes(59), base::Time(), false, + false, CookieSameSite::DEFAULT_MODE, COOKIE_PRIORITY_DEFAULT)); scoped_ptr<base::HistogramSamples> samples2( expired_histogram->SnapshotSamples()); @@ -2524,356 +2498,6 @@ TEST_F(CookieMonsterTest, HistogramCheck) { EXPECT_EQ(samples2->TotalCount(), samples3->TotalCount()); } -namespace { - -class MultiThreadedCookieMonsterTest : public CookieMonsterTest { - public: - MultiThreadedCookieMonsterTest() : other_thread_("CMTthread") {} - - // Helper methods for calling the asynchronous CookieMonster methods - // from a different thread. - - void GetAllCookiesTask(CookieMonster* cm, GetCookieListCallback* callback) { - cm->GetAllCookiesAsync( - base::Bind(&GetCookieListCallback::Run, base::Unretained(callback))); - } - - void GetAllCookiesForURLTask(CookieMonster* cm, - const GURL& url, - GetCookieListCallback* callback) { - cm->GetAllCookiesForURLAsync(url, base::Bind(&GetCookieListCallback::Run, - base::Unretained(callback))); - } - - void GetAllCookiesForURLWithOptionsTask(CookieMonster* cm, - const GURL& url, - const CookieOptions& options, - GetCookieListCallback* callback) { - cm->GetAllCookiesForURLWithOptionsAsync( - url, options, - base::Bind(&GetCookieListCallback::Run, base::Unretained(callback))); - } - - void SetCookieWithDetailsTask(CookieMonster* cm, - const GURL& url, - ResultSavingCookieCallback<bool>* callback) { - // Define the parameters here instead of in the calling fucntion. - // The maximum number of parameters for Bind function is 6. - std::string name = "A"; - std::string value = "B"; - std::string domain = std::string(); - std::string path = "/foo"; - base::Time expiration_time = base::Time(); - bool secure = false; - bool http_only = false; - bool first_party_only = false; - CookiePriority priority = COOKIE_PRIORITY_DEFAULT; - cm->SetCookieWithDetailsAsync( - url, name, value, domain, path, expiration_time, secure, http_only, - first_party_only, false /* enforce prefixes */, - false /* enforces strict secure cookies */, priority, - base::Bind(&ResultSavingCookieCallback<bool>::Run, - base::Unretained(callback))); - } - - void DeleteAllCreatedBetweenTask(CookieMonster* cm, - const base::Time& delete_begin, - const base::Time& delete_end, - ResultSavingCookieCallback<int>* callback) { - cm->DeleteAllCreatedBetweenAsync( - delete_begin, delete_end, - base::Bind(&ResultSavingCookieCallback<int>::Run, - base::Unretained(callback))); - } - - void DeleteAllForHostTask(CookieMonster* cm, - const GURL& url, - ResultSavingCookieCallback<int>* callback) { - cm->DeleteAllForHostAsync(url, - base::Bind(&ResultSavingCookieCallback<int>::Run, - base::Unretained(callback))); - } - - void DeleteAllCreatedBetweenForHostTask( - CookieMonster* cm, - const base::Time delete_begin, - const base::Time delete_end, - const GURL& url, - ResultSavingCookieCallback<int>* callback) { - cm->DeleteAllCreatedBetweenForHostAsync( - delete_begin, delete_end, url, - base::Bind(&ResultSavingCookieCallback<int>::Run, - base::Unretained(callback))); - } - - void DeleteCanonicalCookieTask(CookieMonster* cm, - const CanonicalCookie& cookie, - ResultSavingCookieCallback<bool>* callback) { - cm->DeleteCanonicalCookieAsync( - cookie, base::Bind(&ResultSavingCookieCallback<bool>::Run, - base::Unretained(callback))); - } - - protected: - void RunOnOtherThread(const base::Closure& task) { - other_thread_.Start(); - other_thread_.task_runner()->PostTask(FROM_HERE, task); - RunFor(kTimeout); - other_thread_.Stop(); - } - - Thread other_thread_; -}; - -} // namespace - -TEST_F(MultiThreadedCookieMonsterTest, ThreadCheckGetAllCookies) { - scoped_refptr<CookieMonster> cm(new CookieMonster(NULL, NULL)); - EXPECT_TRUE(SetCookie(cm.get(), http_www_google_.url(), "A=B")); - CookieList cookies = GetAllCookies(cm.get()); - CookieList::const_iterator it = cookies.begin(); - ASSERT_TRUE(it != cookies.end()); - EXPECT_EQ(http_www_google_.host(), it->Domain()); - EXPECT_EQ("A", it->Name()); - ASSERT_TRUE(++it == cookies.end()); - GetCookieListCallback callback(&other_thread_); - base::Closure task = - base::Bind(&MultiThreadedCookieMonsterTest::GetAllCookiesTask, - base::Unretained(this), cm, &callback); - RunOnOtherThread(task); - EXPECT_TRUE(callback.did_run()); - it = callback.cookies().begin(); - ASSERT_TRUE(it != callback.cookies().end()); - EXPECT_EQ(http_www_google_.host(), it->Domain()); - EXPECT_EQ("A", it->Name()); - ASSERT_TRUE(++it == callback.cookies().end()); -} - -TEST_F(MultiThreadedCookieMonsterTest, ThreadCheckGetAllCookiesForURL) { - scoped_refptr<CookieMonster> cm(new CookieMonster(NULL, NULL)); - EXPECT_TRUE(SetCookie(cm.get(), http_www_google_.url(), "A=B")); - CookieList cookies = GetAllCookiesForURL(cm.get(), http_www_google_.url()); - CookieList::const_iterator it = cookies.begin(); - ASSERT_TRUE(it != cookies.end()); - EXPECT_EQ(http_www_google_.host(), it->Domain()); - EXPECT_EQ("A", it->Name()); - ASSERT_TRUE(++it == cookies.end()); - GetCookieListCallback callback(&other_thread_); - base::Closure task = - base::Bind(&MultiThreadedCookieMonsterTest::GetAllCookiesForURLTask, - base::Unretained(this), cm, http_www_google_.url(), &callback); - RunOnOtherThread(task); - EXPECT_TRUE(callback.did_run()); - it = callback.cookies().begin(); - ASSERT_TRUE(it != callback.cookies().end()); - EXPECT_EQ(http_www_google_.host(), it->Domain()); - EXPECT_EQ("A", it->Name()); - ASSERT_TRUE(++it == callback.cookies().end()); -} - -TEST_F(MultiThreadedCookieMonsterTest, ThreadCheckGetAllCookiesForURLWithOpt) { - scoped_refptr<CookieMonster> cm(new CookieMonster(NULL, NULL)); - EXPECT_TRUE(SetCookie(cm.get(), http_www_google_.url(), "A=B")); - CookieOptions options; - CookieList cookies = - GetAllCookiesForURLWithOptions(cm.get(), http_www_google_.url(), options); - CookieList::const_iterator it = cookies.begin(); - ASSERT_TRUE(it != cookies.end()); - EXPECT_EQ(http_www_google_.host(), it->Domain()); - EXPECT_EQ("A", it->Name()); - ASSERT_TRUE(++it == cookies.end()); - GetCookieListCallback callback(&other_thread_); - base::Closure task = base::Bind( - &MultiThreadedCookieMonsterTest::GetAllCookiesForURLWithOptionsTask, - base::Unretained(this), cm, http_www_google_.url(), options, &callback); - RunOnOtherThread(task); - EXPECT_TRUE(callback.did_run()); - it = callback.cookies().begin(); - ASSERT_TRUE(it != callback.cookies().end()); - EXPECT_EQ(http_www_google_.host(), it->Domain()); - EXPECT_EQ("A", it->Name()); - ASSERT_TRUE(++it == callback.cookies().end()); -} - -TEST_F(MultiThreadedCookieMonsterTest, ThreadCheckSetCookieWithDetails) { - scoped_refptr<CookieMonster> cm(new CookieMonster(NULL, NULL)); - EXPECT_TRUE(SetCookieWithDetails(cm.get(), www_google_foo_.url(), "A", "B", - std::string(), "/foo", base::Time(), false, - false, false, COOKIE_PRIORITY_DEFAULT)); - ResultSavingCookieCallback<bool> callback(&other_thread_); - base::Closure task = - base::Bind(&MultiThreadedCookieMonsterTest::SetCookieWithDetailsTask, - base::Unretained(this), cm, www_google_foo_.url(), &callback); - RunOnOtherThread(task); - EXPECT_TRUE(callback.did_run()); - EXPECT_TRUE(callback.result()); -} - -TEST_F(MultiThreadedCookieMonsterTest, ThreadCheckDeleteAllCreatedBetween) { - scoped_refptr<CookieMonster> cm(new CookieMonster(NULL, NULL)); - CookieOptions options; - Time now = Time::Now(); - EXPECT_TRUE( - SetCookieWithOptions(cm.get(), http_www_google_.url(), "A=B", options)); - EXPECT_EQ(1, DeleteAllCreatedBetween(cm.get(), now - TimeDelta::FromDays(99), - Time())); - EXPECT_TRUE( - SetCookieWithOptions(cm.get(), http_www_google_.url(), "A=B", options)); - ResultSavingCookieCallback<int> callback(&other_thread_); - base::Closure task = - base::Bind(&MultiThreadedCookieMonsterTest::DeleteAllCreatedBetweenTask, - base::Unretained(this), cm, now - TimeDelta::FromDays(99), - Time(), &callback); - RunOnOtherThread(task); - EXPECT_TRUE(callback.did_run()); - EXPECT_EQ(1, callback.result()); -} - -TEST_F(MultiThreadedCookieMonsterTest, ThreadCheckDeleteAllForHost) { - scoped_refptr<CookieMonster> cm(new CookieMonster(NULL, NULL)); - CookieOptions options; - EXPECT_TRUE( - SetCookieWithOptions(cm.get(), http_www_google_.url(), "A=B", options)); - EXPECT_EQ(1, DeleteAllForHost(cm.get(), http_www_google_.url())); - EXPECT_TRUE( - SetCookieWithOptions(cm.get(), http_www_google_.url(), "A=B", options)); - ResultSavingCookieCallback<int> callback(&other_thread_); - base::Closure task = - base::Bind(&MultiThreadedCookieMonsterTest::DeleteAllForHostTask, - base::Unretained(this), cm, http_www_google_.url(), &callback); - RunOnOtherThread(task); - EXPECT_TRUE(callback.did_run()); - EXPECT_EQ(1, callback.result()); -} - -TEST_F(MultiThreadedCookieMonsterTest, - ThreadCheckDeleteAllCreatedBetweenForHost) { - scoped_refptr<CookieMonster> cm(new CookieMonster(NULL, NULL)); - GURL url_not_google("http://www.notgoogle.com"); - - CookieOptions options; - Time now = Time::Now(); - // ago1 < ago2 < ago3 < now. - Time ago1 = now - TimeDelta::FromDays(101); - Time ago2 = now - TimeDelta::FromDays(100); - Time ago3 = now - TimeDelta::FromDays(99); - - // These 3 cookies match the first deletion. - EXPECT_TRUE( - SetCookieWithOptions(cm.get(), http_www_google_.url(), "A=B", options)); - EXPECT_TRUE( - SetCookieWithOptions(cm.get(), http_www_google_.url(), "C=D", options)); - EXPECT_TRUE( - SetCookieWithOptions(cm.get(), http_www_google_.url(), "Y=Z", options)); - - // This cookie does not match host. - EXPECT_TRUE(SetCookieWithOptions(cm.get(), url_not_google, "E=F", options)); - - // This cookie does not match time range: [ago3, inf], for first deletion, but - // matches for the second deletion. - EXPECT_TRUE( - cm->SetCookieWithCreationTime(http_www_google_.url(), "G=H", ago2)); - - // 1. First set of deletions. - EXPECT_EQ(3, // Deletes A=B, C=D, Y=Z - DeleteAllCreatedBetweenForHost(cm.get(), ago3, Time::Max(), - http_www_google_.url())); - - EXPECT_TRUE( - SetCookieWithOptions(cm.get(), http_www_google_.url(), "A=B", options)); - ResultSavingCookieCallback<int> callback(&other_thread_); - - // 2. Second set of deletions. - base::Closure task = base::Bind( - &MultiThreadedCookieMonsterTest::DeleteAllCreatedBetweenForHostTask, - base::Unretained(this), cm, ago1, Time(), http_www_google_.url(), - &callback); - RunOnOtherThread(task); - EXPECT_TRUE(callback.did_run()); - EXPECT_EQ(2, callback.result()); // Deletes A=B, G=H. -} - -TEST_F(MultiThreadedCookieMonsterTest, ThreadCheckDeleteCanonicalCookie) { - scoped_refptr<CookieMonster> cm(new CookieMonster(NULL, NULL)); - CookieOptions options; - EXPECT_TRUE( - SetCookieWithOptions(cm.get(), http_www_google_.url(), "A=B", options)); - CookieList cookies = GetAllCookies(cm.get()); - CookieList::iterator it = cookies.begin(); - EXPECT_TRUE(DeleteCanonicalCookie(cm.get(), *it)); - - EXPECT_TRUE( - SetCookieWithOptions(cm.get(), http_www_google_.url(), "A=B", options)); - ResultSavingCookieCallback<bool> callback(&other_thread_); - cookies = GetAllCookies(cm.get()); - it = cookies.begin(); - base::Closure task = - base::Bind(&MultiThreadedCookieMonsterTest::DeleteCanonicalCookieTask, - base::Unretained(this), cm, *it, &callback); - RunOnOtherThread(task); - EXPECT_TRUE(callback.did_run()); - EXPECT_TRUE(callback.result()); -} - -// Ensure that cookies for http, https, ws, and wss all share the same storage -// and policies when GetAllCookiesForURLAsync is used. This test is part of -// MultiThreadedCookieMonsterTest in order to test and use -// GetAllCookiesForURLAsync, but it does not use any additional threads. -TEST_F(MultiThreadedCookieMonsterTest, GetAllCookiesForURLEffectiveDomain) { - std::vector<CanonicalCookie*> cookies; - // This cookie will be freed by the CookieMonster. - cookies.push_back(CanonicalCookie::Create( - http_www_google_.url(), kValidCookieLine, Time::Now(), CookieOptions())); - CanonicalCookie cookie = *cookies[0]; - scoped_refptr<NewMockPersistentCookieStore> store( - new NewMockPersistentCookieStore); - scoped_refptr<CookieMonster> cm(new CookieMonster(store.get(), NULL)); - - CookieMonster::PersistentCookieStore::LoadedCallback loaded_callback; - ::testing::StrictMock<::testing::MockFunction<void(int)>> checkpoint; - const std::string key = cookie_util::GetEffectiveDomain( - http_www_google_.url().scheme(), http_www_google_.url().host()); - - ::testing::InSequence s; - EXPECT_CALL(checkpoint, Call(0)); - EXPECT_CALL(*store, Load(::testing::_)); - EXPECT_CALL(*store, LoadCookiesForKey(key, ::testing::_)) - .WillOnce(::testing::SaveArg<1>(&loaded_callback)); - EXPECT_CALL(checkpoint, Call(1)); - // LoadCookiesForKey will never be called after checkpoint.Call(1) although - // we will call GetAllCookiesForURLAsync again, because all URLs below share - // the same key. - EXPECT_CALL(*store, LoadCookiesForKey(::testing::_, ::testing::_)).Times(0); - - GetCookieListCallback callback; - checkpoint.Call(0); - GetAllCookiesForURLTask(cm.get(), http_www_google_.url(), &callback); - checkpoint.Call(1); - ASSERT_FALSE(callback.did_run()); - // Pass the cookies to the CookieMonster. - loaded_callback.Run(cookies); - // Now GetAllCookiesForURLTask is done. - ASSERT_TRUE(callback.did_run()); - // See that the callback was called with the cookies. - ASSERT_EQ(1u, callback.cookies().size()); - EXPECT_TRUE(cookie.IsEquivalent(callback.cookies()[0])); - - // All urls in |urls| should share the same cookie domain. - const GURL kUrls[] = { - http_www_google_.url(), https_www_google_.url(), ws_www_google_.url(), - wss_www_google_.url(), - }; - for (const GURL& url : kUrls) { - // Call the function with |url| and verify it is done synchronously without - // calling LoadCookiesForKey. - GetCookieListCallback callback; - GetAllCookiesForURLTask(cm.get(), url, &callback); - ASSERT_TRUE(callback.did_run()); - ASSERT_EQ(1u, callback.cookies().size()); - EXPECT_TRUE(cookie.IsEquivalent(callback.cookies()[0])); - } -} - TEST_F(CookieMonsterTest, InvalidExpiryTime) { std::string cookie_line = std::string(kValidCookieLine) + "; expires=Blarg arg arg"; @@ -2886,7 +2510,7 @@ TEST_F(CookieMonsterTest, InvalidExpiryTime) { // CookieStore if the "persist session cookies" option is on. TEST_F(CookieMonsterTest, PersistSessionCookies) { scoped_refptr<MockPersistentCookieStore> store(new MockPersistentCookieStore); - scoped_refptr<CookieMonster> cm(new CookieMonster(store.get(), NULL)); + scoped_ptr<CookieMonster> cm(new CookieMonster(store.get(), nullptr)); cm->SetPersistSessionCookies(true); // All cookies set with SetCookie are session cookies. @@ -2922,7 +2546,7 @@ TEST_F(CookieMonsterTest, PersistSessionCookies) { // Test the commands sent to the persistent cookie store. TEST_F(CookieMonsterTest, PersisentCookieStorageTest) { scoped_refptr<MockPersistentCookieStore> store(new MockPersistentCookieStore); - scoped_refptr<CookieMonster> cm(new CookieMonster(store.get(), NULL)); + scoped_ptr<CookieMonster> cm(new CookieMonster(store.get(), nullptr)); // Add a cookie. EXPECT_TRUE(SetCookie(cm.get(), http_www_google_.url(), @@ -2974,24 +2598,24 @@ TEST_F(CookieMonsterTest, ControlCharacterPurge) { std::vector<CanonicalCookie*> initial_cookies; - AddCookieToList(domain, "foo=bar; path=" + path, now1, &initial_cookies); + AddCookieToList(url, "foo=bar; path=" + path, now1, &initial_cookies); // We have to manually build this cookie because it contains a control // character, and our cookie line parser rejects control characters. - CanonicalCookie* cc = - new CanonicalCookie(url, "baz", - "\x05" - "boo", - domain, path, now2, later, now2, false, false, false, - COOKIE_PRIORITY_DEFAULT); - initial_cookies.push_back(cc); + scoped_ptr<CanonicalCookie> cc = CanonicalCookie::Create( + url, "baz", + "\x05" + "boo", + domain, path, now2, later, false, false, CookieSameSite::DEFAULT_MODE, + false, COOKIE_PRIORITY_DEFAULT); + initial_cookies.push_back(cc.release()); - AddCookieToList(domain, "hello=world; path=" + path, now3, &initial_cookies); + AddCookieToList(url, "hello=world; path=" + path, now3, &initial_cookies); // Inject our initial cookies into the mock PersistentCookieStore. store->SetLoadExpectation(true, initial_cookies); - scoped_refptr<CookieMonster> cm(new CookieMonster(store.get(), NULL)); + scoped_ptr<CookieMonster> cm(new CookieMonster(store.get(), nullptr)); EXPECT_EQ("foo=bar; hello=world", GetCookies(cm.get(), url)); } @@ -3002,7 +2626,7 @@ TEST_F(CookieMonsterTest, CookieSourceHistogram) { const std::string cookie_source_histogram = "Cookie.CookieSourceScheme"; scoped_refptr<MockPersistentCookieStore> store(new MockPersistentCookieStore); - scoped_refptr<CookieMonster> cm(new CookieMonster(store.get(), NULL)); + scoped_ptr<CookieMonster> cm(new CookieMonster(store.get(), nullptr)); histograms.ExpectTotalCount(cookie_source_histogram, 0); @@ -3069,7 +2693,7 @@ TEST_F(CookieMonsterTest, CookieDeleteEquivalentHistogramTest) { const std::string cookie_source_histogram = "Cookie.CookieDeleteEquivalent"; scoped_refptr<MockPersistentCookieStore> store(new MockPersistentCookieStore); - scoped_refptr<CookieMonster> cm(new CookieMonster(store.get(), NULL)); + scoped_ptr<CookieMonster> cm(new CookieMonster(store.get(), nullptr)); // Set a secure cookie from a secure origin EXPECT_TRUE(SetCookie(cm.get(), https_www_google_.url(), "A=B; Secure")); @@ -3134,7 +2758,7 @@ TEST_F(CookieMonsterTest, CookieDeleteEquivalentHistogramTest) { } TEST_F(CookieMonsterStrictSecureTest, SetSecureCookies) { - scoped_refptr<CookieMonster> cm(new CookieMonster(NULL, NULL)); + scoped_ptr<CookieMonster> cm(new CookieMonster(nullptr, nullptr)); GURL http_url("http://www.google.com"); GURL http_superdomain_url("http://google.com"); GURL https_url("https://www.google.com"); @@ -3327,7 +2951,7 @@ TEST_F(CookieMonsterStrictSecureTest, EvictSecureCookies) { // Tests that strict secure cookies doesn't trip equivalent cookie checks // accidentally. Regression test for https://crbug.com/569943. TEST_F(CookieMonsterStrictSecureTest, EquivalentCookies) { - scoped_refptr<CookieMonster> cm(new CookieMonster(NULL, NULL)); + scoped_ptr<CookieMonster> cm(new CookieMonster(nullptr, nullptr)); GURL http_url("http://www.google.com"); GURL http_superdomain_url("http://google.com"); GURL https_url("https://www.google.com"); @@ -3352,7 +2976,7 @@ TEST_F(CookieMonsterStrictSecureTest, CookieDeleteEquivalentHistogramTest) { const std::string cookie_source_histogram = "Cookie.CookieDeleteEquivalent"; scoped_refptr<MockPersistentCookieStore> store(new MockPersistentCookieStore); - scoped_refptr<CookieMonster> cm(new CookieMonster(store.get(), NULL)); + scoped_ptr<CookieMonster> cm(new CookieMonster(store.get(), nullptr)); // Set a secure cookie from a secure origin EXPECT_TRUE(SetCookie(cm.get(), https_www_google_.url(), "A=B; Secure")); @@ -3427,7 +3051,7 @@ class CookieMonsterNotificationTest : public CookieMonsterTest { CookieMonsterNotificationTest() : test_url_("http://www.google.com/foo"), store_(new MockPersistentCookieStore), - monster_(new CookieMonster(store_.get(), NULL)) {} + monster_(new CookieMonster(store_.get(), nullptr)) {} ~CookieMonsterNotificationTest() override {} @@ -3438,7 +3062,7 @@ class CookieMonsterNotificationTest : public CookieMonsterTest { private: scoped_refptr<MockPersistentCookieStore> store_; - scoped_refptr<CookieMonster> monster_; + scoped_ptr<CookieMonster> monster_; }; void RecordCookieChanges(std::vector<CanonicalCookie>* out_cookies, diff --git a/chromium/net/cookies/cookie_options.cc b/chromium/net/cookies/cookie_options.cc index d29d51186f1..8698afdbd7b 100644 --- a/chromium/net/cookies/cookie_options.cc +++ b/chromium/net/cookies/cookie_options.cc @@ -10,8 +10,9 @@ namespace net { CookieOptions::CookieOptions() : exclude_httponly_(true), - include_first_party_only_cookies_(false), + same_site_cookie_mode_(SameSiteCookieMode::DO_NOT_INCLUDE), enforce_strict_secure_(false), + update_access_time_(true), server_time_() {} } // namespace net diff --git a/chromium/net/cookies/cookie_options.h b/chromium/net/cookies/cookie_options.h index 4d2a774c1bc..d1c6afc6371 100644 --- a/chromium/net/cookies/cookie_options.h +++ b/chromium/net/cookies/cookie_options.h @@ -9,35 +9,45 @@ #include "base/time/time.h" #include "net/base/net_export.h" +#include "net/cookies/cookie_constants.h" #include "url/gurl.h" namespace net { class NET_EXPORT CookieOptions { public: + enum class SameSiteCookieMode { + INCLUDE_STRICT_AND_LAX, + INCLUDE_LAX, + DO_NOT_INCLUDE + }; + // Creates a CookieOptions object which: // // * Excludes HttpOnly cookies - // * Excludes First-Party-Only cookies + // * Excludes SameSite cookies // * Does not enforce prefix restrictions (e.g. "$Secure-*") + // * Updates last-accessed time. // // These settings can be altered by calling: // // * |set_{include,exclude}_httponly()| - // * |set_include_first_party_only_cookies()| + // * |set_same_site_cookie_mode( + // CookieOptions::SameSiteCookieMode::INCLUDE_STRICT_AND_LAX)| // * |set_enforce_prefixes()| + // * |set_do_not_update_access_time()| CookieOptions(); void set_exclude_httponly() { exclude_httponly_ = true; } void set_include_httponly() { exclude_httponly_ = false; } bool exclude_httponly() const { return exclude_httponly_; } - // Default is to exclude 'first-party-only' cookies. - void set_include_first_party_only_cookies() { - include_first_party_only_cookies_ = true; + // Default is to exclude 'same_site' cookies. + void set_same_site_cookie_mode(SameSiteCookieMode mode) { + same_site_cookie_mode_ = mode; } - bool include_first_party_only_cookies() const { - return include_first_party_only_cookies_; + SameSiteCookieMode same_site_cookie_mode() const { + return same_site_cookie_mode_; } // TODO(jww): Remove once we decide whether to ship modifying 'secure' cookies @@ -54,10 +64,14 @@ class NET_EXPORT CookieOptions { bool has_server_time() const { return !server_time_.is_null(); } base::Time server_time() const { return server_time_; } + void set_do_not_update_access_time() { update_access_time_ = false; } + bool update_access_time() const { return update_access_time_; } + private: bool exclude_httponly_; - bool include_first_party_only_cookies_; + SameSiteCookieMode same_site_cookie_mode_; bool enforce_strict_secure_; + bool update_access_time_; base::Time server_time_; }; diff --git a/chromium/net/cookies/cookie_store.cc b/chromium/net/cookies/cookie_store.cc index e1b2e091001..f1dff9ada43 100644 --- a/chromium/net/cookies/cookie_store.cc +++ b/chromium/net/cookies/cookie_store.cc @@ -8,8 +8,68 @@ namespace net { -CookieStore::CookieStore() {} - CookieStore::~CookieStore() {} +std::string CookieStore::BuildCookieLine( + const std::vector<CanonicalCookie>& cookies) { + std::string cookie_line; + for (const auto& cookie : cookies) { + if (!cookie_line.empty()) + cookie_line += "; "; + // In Mozilla, if you set a cookie like "AAA", it will have an empty token + // and a value of "AAA". When it sends the cookie back, it will send "AAA", + // so we need to avoid sending "=AAA" for a blank token value. + if (!cookie.Name().empty()) + cookie_line += cookie.Name() + "="; + cookie_line += cookie.Value(); + } + return cookie_line; +} + +std::string CookieStore::BuildCookieLine( + const std::vector<CanonicalCookie*>& cookies) { + std::string cookie_line; + for (const auto& cookie : cookies) { + if (!cookie_line.empty()) + cookie_line += "; "; + // In Mozilla, if you set a cookie like "AAA", it will have an empty token + // and a value of "AAA". When it sends the cookie back, it will send "AAA", + // so we need to avoid sending "=AAA" for a blank token value. + if (!cookie->Name().empty()) + cookie_line += cookie->Name() + "="; + cookie_line += cookie->Value(); + } + return cookie_line; +} + +void CookieStore::DeleteAllAsync(const DeleteCallback& callback) { + DeleteAllCreatedBetweenAsync(base::Time(), base::Time::Max(), callback); +} + +void CookieStore::SetForceKeepSessionState() { + // By default, do nothing. +} + +void CookieStore::GetAllCookiesForURLAsync( + const GURL& url, + const GetCookieListCallback& callback) { + CookieOptions options; + options.set_include_httponly(); + options.set_same_site_cookie_mode( + CookieOptions::SameSiteCookieMode::INCLUDE_STRICT_AND_LAX); + options.set_do_not_update_access_time(); + GetCookieListWithOptionsAsync(url, options, callback); +} + +void CookieStore::SetChannelIDServiceID(int id) { + DCHECK_EQ(-1, channel_id_service_id_); + channel_id_service_id_ = id; +} + +int CookieStore::GetChannelIDServiceID() { + return channel_id_service_id_; +} + +CookieStore::CookieStore() : channel_id_service_id_(-1) {} + } // namespace net diff --git a/chromium/net/cookies/cookie_store.h b/chromium/net/cookies/cookie_store.h index e2e24406365..f6426c5d695 100644 --- a/chromium/net/cookies/cookie_store.h +++ b/chromium/net/cookies/cookie_store.h @@ -12,7 +12,7 @@ #include "base/callback.h" #include "base/callback_list.h" -#include "base/memory/ref_counted.h" +#include "base/memory/scoped_ptr.h" #include "base/time/time.h" #include "net/base/net_export.h" #include "net/cookies/canonical_cookie.h" @@ -24,9 +24,14 @@ 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> { +// An interface for storing and retrieving cookies. Implementations are not +// thread safe, as with most other net classes. All methods must be invoked on +// the network thread, and all callbacks will be calle there. +// +// All async functions may either invoke the callback asynchronously, or they +// may be invoked immediately (prior to return of the asynchronous function). +// Destroying the CookieStore will cancel pending async callbacks. +class NET_EXPORT CookieStore { public: // Callback definitions. typedef base::Callback<void(const CookieList& cookies)> GetCookieListCallback; @@ -39,7 +44,21 @@ class NET_EXPORT CookieStore : public base::RefCountedThreadSafe<CookieStore> { CookieChangedCallbackList; typedef CookieChangedCallbackList::Subscription CookieChangedSubscription; - // Sets a single cookie. Expects a cookie line, like "a=1; domain=b.com". + virtual ~CookieStore(); + + // Returns the cookie line (e.g. "cookie1=value1; cookie2=value2") represented + // by |cookies|. The string is built in the same order as the given list. + // + // TODO(mkwst): We really should standardize on either + // 'std::vector<CanonicalCookie>' or 'std::vector<CanonicalCookie*>'. + static std::string BuildCookieLine( + const std::vector<CanonicalCookie>& cookies); + static std::string BuildCookieLine( + const std::vector<CanonicalCookie*>& cookies); + + // Sets the cookies specified by |cookie_list| returned from |url| + // with options |options| in effect. 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. @@ -50,28 +69,85 @@ class NET_EXPORT CookieStore : public base::RefCountedThreadSafe<CookieStore> { const CookieOptions& options, const SetCookiesCallback& callback) = 0; + // 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. + // + // If |creation_time| is null, it will be set to the time the cookie is set. + // If |last_access_time| is null, it be set to |creation_time|. + // + // If unable to set a cookie, will invoke |callback| with false. + virtual void SetCookieWithDetailsAsync( + const GURL& url, + const std::string& name, + const std::string& value, + const std::string& domain, + const std::string& path, + base::Time creation_time, + base::Time expiration_time, + base::Time last_access_time, + bool secure, + bool http_only, + CookieSameSite same_site, + bool enforce_strict_secure, + CookiePriority priority, + 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. + // Gets all cookies that apply to |url| given |options|. Use options to + // access httponly cookies. + // + // The returned cookies are ordered by longest path, then earliest + // creation date. + // + // TODO(mkwst): This method is deprecated; callsites should be updated to + // use 'GetCookieListWithOptionsAsync'. virtual void GetCookiesWithOptionsAsync( const GURL& url, const CookieOptions& options, const GetCookiesCallback& callback) = 0; - // Returns all matching cookies without marking them as accessed, - // including HTTP only cookies. - virtual void GetAllCookiesForURLAsync( + // Obtains a CookieList for the given |url| and |options|. The returned + // cookies are passed into |callback|, ordered by longest path, then earliest + // creation date. + virtual void GetCookieListWithOptionsAsync( const GURL& url, + const CookieOptions& options, const GetCookieListCallback& callback) = 0; - // Deletes the passed in cookie for the specified URL. + // Returns all cookies associated with |url|, including http-only, and + // same-site cookies. The returned cookies are ordered by longest path, then + // by earliest creation date, and are not marked as having been accessed. + // + // TODO(mkwst): This method is deprecated, and should be removed, either by + // updating callsites to use 'GetCookieListWithOptionsAsync' with an explicit + // CookieOptions, or by changing CookieOptions' defaults. + void GetAllCookiesForURLAsync(const GURL& url, + const GetCookieListCallback& 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. + virtual void GetAllCookiesAsync(const GetCookieListCallback& callback) = 0; + + // Deletes all cookies that might apply to |url| that have |cookie_name|. virtual void DeleteCookieAsync(const GURL& url, const std::string& cookie_name, const base::Closure& callback) = 0; + // Deletes one specific cookie. |cookie| must have been returned by a previous + // query on this CookieStore. Invokes |callback| with 1 if a cookie was + // deleted, 0 otherwise. + virtual void DeleteCanonicalCookieAsync(const CanonicalCookie& cookie, + const DeleteCallback& 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. @@ -93,8 +169,17 @@ class NET_EXPORT CookieStore : public base::RefCountedThreadSafe<CookieStore> { virtual void DeleteSessionCookiesAsync(const DeleteCallback&) = 0; - // Returns the underlying CookieMonster. - virtual CookieMonster* GetCookieMonster() = 0; + // Deletes all cookies in the store. + void DeleteAllAsync(const DeleteCallback& callback); + + // Flush the backing store (if any) to disk and post the given callback when + // done. + virtual void FlushStore(const base::Closure& callback) = 0; + + // Protects session cookies from deletion on shutdown, if the underlying + // CookieStore implemention is currently configured to store them to disk. + // Otherwise, does nothing. + virtual void SetForceKeepSessionState(); // Add a callback to be notified when the set of cookies named |name| that // would be sent for a request to |url| changes. The returned handle is @@ -117,10 +202,16 @@ class NET_EXPORT CookieStore : public base::RefCountedThreadSafe<CookieStore> { const std::string& name, const CookieChangedCallback& callback) = 0; + // Returns true if this cookie store is ephemeral, and false if it is backed + // by some sort of persistence layer. + // TODO(nharper): Remove this method once crbug.com/548423 has been closed. + virtual bool IsEphemeral() = 0; + void SetChannelIDServiceID(int id); + int GetChannelIDServiceID(); + protected: - friend class base::RefCountedThreadSafe<CookieStore>; CookieStore(); - virtual ~CookieStore(); + int channel_id_service_id_; }; } // namespace net diff --git a/chromium/net/cookies/cookie_store_test_callbacks.cc b/chromium/net/cookies/cookie_store_test_callbacks.cc index 491fe0e8bf9..9e36e706201 100644 --- a/chromium/net/cookies/cookie_store_test_callbacks.cc +++ b/chromium/net/cookies/cookie_store_test_callbacks.cc @@ -13,18 +13,12 @@ namespace net { CookieCallback::CookieCallback(base::Thread* run_in_thread) - : did_run_(false), - run_in_thread_(run_in_thread), - run_in_loop_(NULL), - parent_loop_(base::MessageLoop::current()), - loop_to_quit_(base::MessageLoop::current()) {} + : run_in_thread_(run_in_thread), run_in_loop_(NULL) {} CookieCallback::CookieCallback() - : did_run_(false), - run_in_thread_(NULL), - run_in_loop_(base::MessageLoop::current()), - parent_loop_(NULL), - loop_to_quit_(base::MessageLoop::current()) {} + : run_in_thread_(NULL), run_in_loop_(base::MessageLoop::current()) {} + +CookieCallback::~CookieCallback() {} void CookieCallback::CallbackEpilogue() { base::MessageLoop* expected_loop = NULL; @@ -36,10 +30,12 @@ void CookieCallback::CallbackEpilogue() { } ASSERT_TRUE(expected_loop != NULL); - did_run_ = true; EXPECT_EQ(expected_loop, base::MessageLoop::current()); - loop_to_quit_->task_runner()->PostTask( - FROM_HERE, base::MessageLoop::QuitWhenIdleClosure()); + loop_to_quit_.Quit(); +} + +void CookieCallback::WaitUntilDone() { + loop_to_quit_.Run(); } StringResultCookieCallback::StringResultCookieCallback() {} @@ -51,4 +47,15 @@ NoResultCookieCallback::NoResultCookieCallback() {} NoResultCookieCallback::NoResultCookieCallback(base::Thread* run_in_thread) : CookieCallback(run_in_thread) {} +GetCookieListCallback::GetCookieListCallback() {} +GetCookieListCallback::GetCookieListCallback(base::Thread* run_in_thread) + : CookieCallback(run_in_thread) {} + +GetCookieListCallback::~GetCookieListCallback() {} + +void GetCookieListCallback::Run(const CookieList& cookies) { + cookies_ = cookies; + CallbackEpilogue(); +} + } // namespace net diff --git a/chromium/net/cookies/cookie_store_test_callbacks.h b/chromium/net/cookies/cookie_store_test_callbacks.h index eccdf1bff26..207eb02f28b 100644 --- a/chromium/net/cookies/cookie_store_test_callbacks.h +++ b/chromium/net/cookies/cookie_store_test_callbacks.h @@ -8,6 +8,8 @@ #include <string> #include <vector> +#include "base/run_loop.h" +#include "net/cookies/canonical_cookie.h" #include "net/cookies/cookie_store.h" namespace base { @@ -22,28 +24,27 @@ namespace net { // 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_; } + // Waits until the callback is invoked. + void WaitUntilDone(); protected: - // Constructs a callback that expects to be called in the given thread and - // will, upon execution, send a QUIT to the constructing thread. + // Constructs a callback that expects to be called in the given 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(); + ~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_; base::MessageLoop* run_in_loop_; - base::MessageLoop* parent_loop_; - base::MessageLoop* loop_to_quit_; + base::RunLoop loop_to_quit_; }; // Callback implementations for the asynchronous CookieStore methods. @@ -94,6 +95,21 @@ class NoResultCookieCallback : public CookieCallback { } }; +class GetCookieListCallback : public CookieCallback { + public: + GetCookieListCallback(); + explicit GetCookieListCallback(base::Thread* run_in_thread); + + ~GetCookieListCallback(); + + void Run(const CookieList& cookies); + + const CookieList& cookies() { return cookies_; } + + private: + CookieList cookies_; +}; + } // namespace net #endif // NET_COOKIES_COOKIE_STORE_TEST_CALLBACKS_H_ diff --git a/chromium/net/cookies/cookie_store_test_helpers.cc b/chromium/net/cookies/cookie_store_test_helpers.cc index e75039c1484..d2dc2106192 100644 --- a/chromium/net/cookies/cookie_store_test_helpers.cc +++ b/chromium/net/cookies/cookie_store_test_helpers.cc @@ -34,10 +34,9 @@ namespace net { const int kDelayedTime = 0; DelayedCookieMonster::DelayedCookieMonster() - : cookie_monster_(new CookieMonster(NULL, NULL)), - did_run_(false), - result_(false) { -} + : cookie_monster_(new CookieMonster(nullptr, nullptr)), + did_run_(false), + result_(false) {} DelayedCookieMonster::~DelayedCookieMonster() { } @@ -53,6 +52,12 @@ void DelayedCookieMonster::GetCookiesWithOptionsInternalCallback( did_run_ = true; } +void DelayedCookieMonster::GetCookieListWithOptionsInternalCallback( + const CookieList& cookie_list) { + cookie_list_ = cookie_list; + did_run_ = true; +} + void DelayedCookieMonster::SetCookieWithOptionsAsync( const GURL& url, const std::string& cookie_line, @@ -70,6 +75,24 @@ void DelayedCookieMonster::SetCookieWithOptionsAsync( base::TimeDelta::FromMilliseconds(kDelayedTime)); } +void DelayedCookieMonster::SetCookieWithDetailsAsync( + const GURL& url, + const std::string& name, + const std::string& value, + const std::string& domain, + const std::string& path, + base::Time creation_time, + base::Time expiration_time, + base::Time last_access_time, + bool secure, + bool http_only, + CookieSameSite same_site, + bool enforce_strict_secure, + CookiePriority priority, + const SetCookiesCallback& callback) { + NOTREACHED(); +} + void DelayedCookieMonster::GetCookiesWithOptionsAsync( const GURL& url, const CookieOptions& options, @@ -87,10 +110,26 @@ void DelayedCookieMonster::GetCookiesWithOptionsAsync( base::TimeDelta::FromMilliseconds(kDelayedTime)); } -void DelayedCookieMonster::GetAllCookiesForURLAsync( +void DelayedCookieMonster::GetCookieListWithOptionsAsync( const GURL& url, + const CookieOptions& options, + const CookieMonster::GetCookieListCallback& callback) { + did_run_ = false; + cookie_monster_->GetCookieListWithOptionsAsync( + url, options, + base::Bind( + &DelayedCookieMonster::GetCookieListWithOptionsInternalCallback, + base::Unretained(this))); + DCHECK_EQ(did_run_, true); + base::ThreadTaskRunnerHandle::Get()->PostDelayedTask( + FROM_HERE, base::Bind(&DelayedCookieMonster::InvokeGetCookieListCallback, + base::Unretained(this), callback), + base::TimeDelta::FromMilliseconds(kDelayedTime)); +} + +void DelayedCookieMonster::GetAllCookiesAsync( const GetCookieListCallback& callback) { - cookie_monster_->GetAllCookiesForURLAsync(url, callback); + cookie_monster_->GetAllCookiesAsync(callback); } void DelayedCookieMonster::InvokeSetCookiesCallback( @@ -105,6 +144,12 @@ void DelayedCookieMonster::InvokeGetCookieStringCallback( callback.Run(cookie_); } +void DelayedCookieMonster::InvokeGetCookieListCallback( + const CookieMonster::GetCookieListCallback& callback) { + if (!callback.is_null()) + callback.Run(cookie_list_); +} + bool DelayedCookieMonster::SetCookieWithOptions( const GURL& url, const std::string& cookie_line, @@ -131,6 +176,12 @@ void DelayedCookieMonster::DeleteCookieAsync(const GURL& url, ADD_FAILURE(); } +void DelayedCookieMonster::DeleteCanonicalCookieAsync( + const CanonicalCookie& cookie, + const DeleteCallback& callback) { + ADD_FAILURE(); +} + void DelayedCookieMonster::DeleteAllCreatedBetweenAsync( const base::Time& delete_begin, const base::Time& delete_end, @@ -150,8 +201,8 @@ void DelayedCookieMonster::DeleteSessionCookiesAsync(const DeleteCallback&) { ADD_FAILURE(); } -CookieMonster* DelayedCookieMonster::GetCookieMonster() { - return cookie_monster_.get(); +void DelayedCookieMonster::FlushStore(const base::Closure& callback) { + ADD_FAILURE(); } scoped_ptr<CookieStore::CookieChangedSubscription> @@ -163,6 +214,10 @@ DelayedCookieMonster::AddCallbackForCookie( return scoped_ptr<CookieStore::CookieChangedSubscription>(); } +bool DelayedCookieMonster::IsEphemeral() { + return true; +} + // // CookieURLHelper // diff --git a/chromium/net/cookies/cookie_store_test_helpers.h b/chromium/net/cookies/cookie_store_test_helpers.h index 1750c7f07bd..0ebae5bb9e2 100644 --- a/chromium/net/cookies/cookie_store_test_helpers.h +++ b/chromium/net/cookies/cookie_store_test_helpers.h @@ -19,6 +19,8 @@ class DelayedCookieMonster : public CookieStore { public: DelayedCookieMonster(); + ~DelayedCookieMonster() override; + // 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. @@ -29,13 +31,32 @@ class DelayedCookieMonster : public CookieStore { const CookieOptions& options, const CookieMonster::SetCookiesCallback& callback) override; + void SetCookieWithDetailsAsync(const GURL& url, + const std::string& name, + const std::string& value, + const std::string& domain, + const std::string& path, + base::Time creation_time, + base::Time expiration_time, + base::Time last_access_time, + bool secure, + bool http_only, + CookieSameSite same_site, + bool enforce_strict_secure, + CookiePriority priority, + const SetCookiesCallback& callback) override; + void GetCookiesWithOptionsAsync( const GURL& url, const CookieOptions& options, const CookieMonster::GetCookiesCallback& callback) override; - void GetAllCookiesForURLAsync(const GURL& url, - const GetCookieListCallback& callback) override; + void GetCookieListWithOptionsAsync( + const GURL& url, + const CookieOptions& options, + const GetCookieListCallback& callback) override; + + void GetAllCookiesAsync(const GetCookieListCallback& callback) override; virtual bool SetCookieWithOptions(const GURL& url, const std::string& cookie_line, @@ -51,6 +72,9 @@ class DelayedCookieMonster : public CookieStore { const std::string& cookie_name, const base::Closure& callback) override; + void DeleteCanonicalCookieAsync(const CanonicalCookie& cookie, + const DeleteCallback& callback) override; + void DeleteAllCreatedBetweenAsync(const base::Time& delete_begin, const base::Time& delete_end, const DeleteCallback& callback) override; @@ -63,18 +87,21 @@ class DelayedCookieMonster : public CookieStore { void DeleteSessionCookiesAsync(const DeleteCallback&) override; - CookieMonster* GetCookieMonster() override; + void FlushStore(const base::Closure& callback) override; scoped_ptr<CookieStore::CookieChangedSubscription> AddCallbackForCookie(const GURL& url, const std::string& name, const CookieChangedCallback& callback) override; + bool IsEphemeral() override; + private: // Be called immediately from CookieMonster. void SetCookiesInternalCallback(bool result); void GetCookiesWithOptionsInternalCallback(const std::string& cookie); + void GetCookieListWithOptionsInternalCallback(const CookieList& cookie); // Invoke the original callbacks. @@ -84,15 +111,18 @@ class DelayedCookieMonster : public CookieStore { void InvokeGetCookieStringCallback( const CookieMonster::GetCookiesCallback& callback); + void InvokeGetCookieListCallback( + const CookieMonster::GetCookieListCallback& callback); + friend class base::RefCountedThreadSafe<DelayedCookieMonster>; - ~DelayedCookieMonster() override; - scoped_refptr<CookieMonster> cookie_monster_; + scoped_ptr<CookieMonster> cookie_monster_; bool did_run_; bool result_; std::string cookie_; std::string cookie_line_; + CookieList cookie_list_; }; class CookieURLHelper { diff --git a/chromium/net/cookies/cookie_store_unittest.cc b/chromium/net/cookies/cookie_store_unittest.cc new file mode 100644 index 00000000000..739336594a7 --- /dev/null +++ b/chromium/net/cookies/cookie_store_unittest.cc @@ -0,0 +1,65 @@ +// Copyright (c) 2016 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 <vector> + +#include "base/time/time.h" +#include "net/cookies/cookie_store.h" +#include "base/memory/scoped_ptr.h" +#include "net/cookies/canonical_cookie.h" +#include "net/cookies/cookie_options.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "url/gurl.h" + +namespace net { + +namespace { + +// Helper for testing BuildCookieLine +void MatchCookieLineToVector( + const std::string& line, + const std::vector<scoped_ptr<CanonicalCookie>>& cookies) { + // Test the std::vector<CanonicalCookie> variant + // ('CookieMonster::CookieList'): + std::vector<CanonicalCookie> list; + for (const auto& cookie : cookies) + list.push_back(*cookie); + EXPECT_EQ(line, CookieStore::BuildCookieLine(list)); + + // Test the std::vector<CanonicalCookie*> variant + // ('CookieMonster::CanonicalCookieVector' (yes, this is absurd)): + std::vector<CanonicalCookie*> ptr_list; + for (const auto& cookie : cookies) + ptr_list.push_back(cookie.get()); + EXPECT_EQ(line, CookieStore::BuildCookieLine(ptr_list)); +} + +} // namespace + +TEST(CookieStoreBaseTest, BuildCookieLine) { + std::vector<scoped_ptr<CanonicalCookie>> cookies; + GURL url("https://example.com/"); + CookieOptions options; + base::Time now = base::Time::Now(); + MatchCookieLineToVector("", cookies); + + cookies.push_back(CanonicalCookie::Create(url, "A=B", now, options)); + MatchCookieLineToVector("A=B", cookies); + // Nameless cookies are sent back without a prefixed '='. + cookies.push_back(CanonicalCookie::Create(url, "C", now, options)); + MatchCookieLineToVector("A=B; C", cookies); + // Cookies separated by ';'. + cookies.push_back(CanonicalCookie::Create(url, "D=E", now, options)); + MatchCookieLineToVector("A=B; C; D=E", cookies); + // BuildCookieLine doesn't reorder the list, it relies on the caller to do so. + cookies.push_back(CanonicalCookie::Create( + url, "F=G", now - base::TimeDelta::FromSeconds(1), options)); + MatchCookieLineToVector("A=B; C; D=E; F=G", cookies); + // BuildCookieLine doesn't deduplicate. + cookies.push_back(CanonicalCookie::Create( + url, "D=E", now - base::TimeDelta::FromSeconds(2), options)); + MatchCookieLineToVector("A=B; C; D=E; F=G; D=E", cookies); +} + +} // namespace net diff --git a/chromium/net/cookies/cookie_store_unittest.h b/chromium/net/cookies/cookie_store_unittest.h index fb78d89bbbc..ce8008f21b5 100644 --- a/chromium/net/cookies/cookie_store_unittest.h +++ b/chromium/net/cookies/cookie_store_unittest.h @@ -41,12 +41,8 @@ const char kValidCookieLine[] = "A=B; path=/"; // 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; +// // Factory function. Will be called at most once per test. +// static scoped_ptr<CookieStore> Create(); // // // The cookie store supports cookies with the exclude_httponly() option. // static const bool supports_http_only; @@ -84,7 +80,9 @@ class CookieStoreTest : public testing::Test { ws_www_google_("ws://www.google.izzle"), wss_www_google_("wss://www.google.izzle"), www_google_foo_("http://www.google.izzle/foo"), - www_google_bar_("http://www.google.izzle/bar") { + www_google_bar_("http://www.google.izzle/bar"), + http_foo_com_("http://foo.com"), + http_bar_com_("http://bar.com") { // This test may be used outside of the net test suite, and thus may not // have a message loop. if (!base::MessageLoop::current()) @@ -102,27 +100,49 @@ class CookieStoreTest : public testing::Test { CookieOptions options; if (!CookieStoreTestTraits::supports_http_only) options.set_include_httponly(); - StringResultCookieCallback callback; - cs->GetCookiesWithOptionsAsync( - url, options, - base::Bind(&StringResultCookieCallback::Run, - base::Unretained(&callback))); - RunFor(kTimeout); - EXPECT_TRUE(callback.did_run()); - return callback.result(); + return GetCookiesWithOptions(cs, url, options); } std::string GetCookiesWithOptions(CookieStore* cs, const GURL& url, const CookieOptions& options) { DCHECK(cs); - StringResultCookieCallback callback; - cs->GetCookiesWithOptionsAsync( - url, options, base::Bind(&StringResultCookieCallback::Run, - base::Unretained(&callback))); - RunFor(kTimeout); - EXPECT_TRUE(callback.did_run()); - return callback.result(); + GetCookieListCallback callback; + cs->GetCookieListWithOptionsAsync( + url, options, + base::Bind(&GetCookieListCallback::Run, base::Unretained(&callback))); + callback.WaitUntilDone(); + return CookieStore::BuildCookieLine(callback.cookies()); + } + + CookieList GetCookieListWithOptions(CookieStore* cs, + const GURL& url, + const CookieOptions& options) { + DCHECK(cs); + GetCookieListCallback callback; + cs->GetCookieListWithOptionsAsync( + url, options, + base::Bind(&GetCookieListCallback::Run, base::Unretained(&callback))); + callback.WaitUntilDone(); + return callback.cookies(); + } + + CookieList GetAllCookiesForURL(CookieStore* cs, const GURL& url) { + DCHECK(cs); + GetCookieListCallback callback; + cs->GetAllCookiesForURLAsync(url, base::Bind(&GetCookieListCallback::Run, + base::Unretained(&callback))); + callback.WaitUntilDone(); + return callback.cookies(); + } + + CookieList GetAllCookies(CookieStore* cs) { + DCHECK(cs); + GetCookieListCallback callback; + cs->GetAllCookiesAsync( + base::Bind(&GetCookieListCallback::Run, base::Unretained(&callback))); + callback.WaitUntilDone(); + return callback.cookies(); } bool SetCookieWithOptions(CookieStore* cs, @@ -136,8 +156,32 @@ class CookieStoreTest : public testing::Test { base::Bind( &ResultSavingCookieCallback<bool>::Run, base::Unretained(&callback))); - RunFor(kTimeout); - EXPECT_TRUE(callback.did_run()); + callback.WaitUntilDone(); + return callback.result(); + } + + bool SetCookieWithDetails(CookieStore* cs, + const GURL& url, + const std::string& name, + const std::string& value, + const std::string& domain, + const std::string& path, + const base::Time creation_time, + const base::Time expiration_time, + const base::Time last_access_time, + bool secure, + bool http_only, + CookieSameSite same_site, + CookiePriority priority) { + DCHECK(cs); + ResultSavingCookieCallback<bool> callback; + cs->SetCookieWithDetailsAsync( + url, name, value, domain, path, creation_time, expiration_time, + last_access_time, secure, http_only, same_site, + false /* enforces strict secure cookies */, priority, + base::Bind(&ResultSavingCookieCallback<bool>::Run, + base::Unretained(&callback))); + callback.WaitUntilDone(); return callback.result(); } @@ -171,8 +215,17 @@ class CookieStoreTest : public testing::Test { cs->DeleteCookieAsync( url, cookie_name, base::Bind(&NoResultCookieCallback::Run, base::Unretained(&callback))); - RunFor(kTimeout); - EXPECT_TRUE(callback.did_run()); + callback.WaitUntilDone(); + } + + int DeleteCanonicalCookie(CookieStore* cs, const CanonicalCookie& cookie) { + DCHECK(cs); + ResultSavingCookieCallback<int> callback; + cs->DeleteCanonicalCookieAsync( + cookie, base::Bind(&ResultSavingCookieCallback<int>::Run, + base::Unretained(&callback))); + callback.WaitUntilDone(); + return callback.result(); } int DeleteCreatedBetween(CookieStore* cs, @@ -185,8 +238,7 @@ class CookieStoreTest : public testing::Test { base::Bind( &ResultSavingCookieCallback<int>::Run, base::Unretained(&callback))); - RunFor(kTimeout); - EXPECT_TRUE(callback.did_run()); + callback.WaitUntilDone(); return callback.result(); } @@ -201,8 +253,7 @@ class CookieStoreTest : public testing::Test { base::Bind( &ResultSavingCookieCallback<int>::Run, base::Unretained(&callback))); - RunFor(kTimeout); - EXPECT_TRUE(callback.did_run()); + callback.WaitUntilDone(); return callback.result(); } @@ -213,23 +264,24 @@ class CookieStoreTest : public testing::Test { base::Bind( &ResultSavingCookieCallback<int>::Run, base::Unretained(&callback))); - RunFor(kTimeout); - EXPECT_TRUE(callback.did_run()); + callback.WaitUntilDone(); return callback.result(); } - void RunFor(int ms) { - // Runs the test thread message loop for up to |ms| milliseconds. - base::ThreadTaskRunnerHandle::Get()->PostDelayedTask( - FROM_HERE, base::Bind(&base::MessageLoop::QuitWhenIdle, - weak_factory_->GetWeakPtr()), - base::TimeDelta::FromMilliseconds(ms)); - base::MessageLoop::current()->Run(); - weak_factory_->InvalidateWeakPtrs(); + int DeleteAll(CookieStore* cs) { + DCHECK(cs); + ResultSavingCookieCallback<int> callback; + cs->DeleteAllAsync(base::Bind(&ResultSavingCookieCallback<int>::Run, + base::Unretained(&callback))); + callback.WaitUntilDone(); + return callback.result(); } - scoped_refptr<CookieStore> GetCookieStore() { - return CookieStoreTestTraits::Create(); + // Returns the CookieStore for the test - each test only uses one CookieStore. + CookieStore* GetCookieStore() { + if (!cookie_store_) + cookie_store_ = CookieStoreTestTraits::Create(); + return cookie_store_.get(); } // Compares two cookie lines. @@ -264,6 +316,8 @@ class CookieStoreTest : public testing::Test { const CookieURLHelper wss_www_google_; const CookieURLHelper www_google_foo_; const CookieURLHelper www_google_bar_; + const CookieURLHelper http_foo_com_; + const CookieURLHelper http_bar_com_; scoped_ptr<base::WeakPtrFactory<base::MessageLoop> > weak_factory_; scoped_ptr<base::MessageLoop> message_loop_; @@ -277,383 +331,467 @@ class CookieStoreTest : public testing::Test { EXPECT_TRUE(tokens.insert(tokenizer.token()).second); return tokens; } + + scoped_ptr<CookieStore> cookie_store_; }; 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, SetCookieWithDetailsAsync) { + CookieStore* cs = this->GetCookieStore(); + + base::Time two_hours_ago = base::Time::Now() - base::TimeDelta::FromHours(2); + base::Time one_hour_ago = base::Time::Now() - base::TimeDelta::FromHours(1); + base::Time one_hour_from_now = + base::Time::Now() + base::TimeDelta::FromHours(1); + + EXPECT_TRUE(this->SetCookieWithDetails( + cs, this->www_google_foo_.url(), "A", "B", std::string(), "/foo", + one_hour_ago, one_hour_from_now, base::Time(), false, false, + CookieSameSite::DEFAULT_MODE, COOKIE_PRIORITY_DEFAULT)); + // Note that for the creation time to be set exactly, without modification, + // it must be different from the one set by the line above. + EXPECT_TRUE(this->SetCookieWithDetails( + cs, this->www_google_bar_.url(), "C", "D", this->www_google_bar_.domain(), + "/bar", two_hours_ago, base::Time(), one_hour_ago, false, true, + CookieSameSite::DEFAULT_MODE, COOKIE_PRIORITY_DEFAULT)); + EXPECT_TRUE(this->SetCookieWithDetails( + cs, this->http_www_google_.url(), "E", "F", std::string(), std::string(), + base::Time(), base::Time(), base::Time(), true, false, + CookieSameSite::DEFAULT_MODE, COOKIE_PRIORITY_DEFAULT)); + + // Test that malformed attributes fail to set the cookie. + EXPECT_FALSE(this->SetCookieWithDetails( + cs, this->www_google_foo_.url(), " A", "B", std::string(), "/foo", + base::Time(), base::Time(), base::Time(), false, false, + CookieSameSite::DEFAULT_MODE, COOKIE_PRIORITY_DEFAULT)); + EXPECT_FALSE(this->SetCookieWithDetails( + cs, this->www_google_foo_.url(), "A;", "B", std::string(), "/foo", + base::Time(), base::Time(), base::Time(), false, false, + CookieSameSite::DEFAULT_MODE, COOKIE_PRIORITY_DEFAULT)); + EXPECT_FALSE(this->SetCookieWithDetails( + cs, this->www_google_foo_.url(), "A=", "B", std::string(), "/foo", + base::Time(), base::Time(), base::Time(), false, false, + CookieSameSite::DEFAULT_MODE, COOKIE_PRIORITY_DEFAULT)); + EXPECT_FALSE(this->SetCookieWithDetails( + cs, this->www_google_foo_.url(), "A", "B", "google.ozzzzzzle", "foo", + base::Time(), base::Time(), base::Time(), false, false, + CookieSameSite::DEFAULT_MODE, COOKIE_PRIORITY_DEFAULT)); + EXPECT_FALSE(this->SetCookieWithDetails( + cs, this->www_google_foo_.url(), "A=", "B", std::string(), "foo", + base::Time(), base::Time(), base::Time(), false, false, + CookieSameSite::DEFAULT_MODE, COOKIE_PRIORITY_DEFAULT)); + + // Get all the cookies for a given URL, regardless of properties. This 'get()' + // operation shouldn't update the access time, as the test checks that the + // access time is set properly upon creation. Updating the access time would + // make that difficult. + CookieOptions options; + options.set_include_httponly(); + options.set_same_site_cookie_mode( + CookieOptions::SameSiteCookieMode::INCLUDE_STRICT_AND_LAX); + options.set_do_not_update_access_time(); + + CookieList cookies = + this->GetCookieListWithOptions(cs, this->www_google_foo_.url(), options); + CookieList::iterator it = cookies.begin(); + + ASSERT_TRUE(it != cookies.end()); + EXPECT_EQ("A", it->Name()); + EXPECT_EQ("B", it->Value()); + EXPECT_EQ(this->www_google_foo_.host(), it->Domain()); + EXPECT_EQ("/foo", it->Path()); + EXPECT_EQ(one_hour_ago, it->CreationDate()); + EXPECT_TRUE(it->IsPersistent()); + // Expect expiration date is in the right range. Some cookie implementations + // may not record it with millisecond accuracy. + EXPECT_LE((one_hour_from_now - it->ExpiryDate()).magnitude().InSeconds(), 5); + // Some CookieStores don't store last access date. + if (!it->LastAccessDate().is_null()) + EXPECT_EQ(one_hour_ago, it->LastAccessDate()); + EXPECT_FALSE(it->IsSecure()); + EXPECT_FALSE(it->IsHttpOnly()); + + ASSERT_TRUE(++it == cookies.end()); + + // Verify that the cookie was set as 'httponly' by passing in a CookieOptions + // that excludes them and getting an empty result. + if (TypeParam::supports_http_only) { + cookies = this->GetCookieListWithOptions(cs, this->www_google_bar_.url(), + CookieOptions()); + it = cookies.begin(); + ASSERT_TRUE(it == cookies.end()); + } + + // Get the cookie using the wide open |options|: + cookies = + this->GetCookieListWithOptions(cs, this->www_google_bar_.url(), options); + it = cookies.begin(); + + ASSERT_TRUE(it != cookies.end()); + EXPECT_EQ("C", it->Name()); + EXPECT_EQ("D", it->Value()); + EXPECT_EQ(this->www_google_bar_.Format(".%D"), it->Domain()); + EXPECT_EQ("/bar", it->Path()); + EXPECT_EQ(two_hours_ago, it->CreationDate()); + EXPECT_FALSE(it->IsPersistent()); + // Some CookieStores don't store last access date. + if (!it->LastAccessDate().is_null()) + EXPECT_EQ(one_hour_ago, it->LastAccessDate()); + EXPECT_FALSE(it->IsSecure()); + EXPECT_TRUE(it->IsHttpOnly()); + + EXPECT_TRUE(++it == cookies.end()); + + cookies = this->GetCookieListWithOptions(cs, this->https_www_google_.url(), + options); + it = cookies.begin(); + + ASSERT_TRUE(it != cookies.end()); + EXPECT_EQ("E", it->Name()); + EXPECT_EQ("F", it->Value()); + EXPECT_EQ("/", it->Path()); + EXPECT_EQ(this->https_www_google_.host(), it->Domain()); + // Cookie should have its creation time set, and be in a reasonable range. + EXPECT_LE((base::Time::Now() - it->CreationDate()).magnitude().InMinutes(), + 2); + EXPECT_FALSE(it->IsPersistent()); + // Some CookieStores don't store last access date. + if (!it->LastAccessDate().is_null()) + EXPECT_EQ(it->CreationDate(), it->LastAccessDate()); + EXPECT_TRUE(it->IsSecure()); + EXPECT_FALSE(it->IsHttpOnly()); + + EXPECT_TRUE(++it == cookies.end()); } TYPED_TEST_P(CookieStoreTest, DomainTest) { - scoped_refptr<CookieStore> cs(this->GetCookieStore()); - EXPECT_TRUE(this->SetCookie(cs.get(), this->http_www_google_.url(), "A=B")); - this->MatchCookieLines( - "A=B", this->GetCookies(cs.get(), this->http_www_google_.url())); + CookieStore* cs = this->GetCookieStore(); + EXPECT_TRUE(this->SetCookie(cs, this->http_www_google_.url(), "A=B")); + this->MatchCookieLines("A=B", + this->GetCookies(cs, this->http_www_google_.url())); EXPECT_TRUE( - this->SetCookie(cs.get(), this->http_www_google_.url(), + this->SetCookie(cs, this->http_www_google_.url(), this->http_www_google_.Format("C=D; domain=.%D"))); - this->MatchCookieLines( - "A=B; C=D", this->GetCookies(cs.get(), this->http_www_google_.url())); + this->MatchCookieLines("A=B; C=D", + this->GetCookies(cs, this->http_www_google_.url())); // 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.get(), GURL(this->http_www_google_.Format("http://foo.www.%D")))); + "C=D", this->GetCookies( + cs, GURL(this->http_www_google_.Format("http://foo.www.%D")))); // Test and make sure we find domain cookies on the same domain. EXPECT_TRUE( - this->SetCookie(cs.get(), this->http_www_google_.url(), + this->SetCookie(cs, this->http_www_google_.url(), this->http_www_google_.Format("E=F; domain=.www.%D"))); - this->MatchCookieLines( - "A=B; C=D; E=F", - this->GetCookies(cs.get(), this->http_www_google_.url())); + this->MatchCookieLines("A=B; C=D; E=F", + this->GetCookies(cs, this->http_www_google_.url())); // 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.get(), this->http_www_google_.url(), + this->SetCookie(cs, this->http_www_google_.url(), this->http_www_google_.Format("G=H; domain=www.%D"))); - this->MatchCookieLines( - "A=B; C=D; E=F; G=H", - this->GetCookies(cs.get(), this->http_www_google_.url())); + this->MatchCookieLines("A=B; C=D; E=F; G=H", + this->GetCookies(cs, this->http_www_google_.url())); // Test domain enforcement, should fail on a sub-domain or something too deep. EXPECT_FALSE( - this->SetCookie(cs.get(), this->http_www_google_.url(), + this->SetCookie(cs, this->http_www_google_.url(), this->http_www_google_.Format("I=J; domain=.%R"))); this->MatchCookieLines( std::string(), - this->GetCookies(cs.get(), - GURL(this->http_www_google_.Format("http://a.%R")))); + this->GetCookies(cs, GURL(this->http_www_google_.Format("http://a.%R")))); EXPECT_FALSE(this->SetCookie( - cs.get(), this->http_www_google_.url(), + cs, this->http_www_google_.url(), this->http_www_google_.Format("K=L; domain=.bla.www.%D"))); this->MatchCookieLines( "C=D; E=F; G=H", this->GetCookies( - cs.get(), GURL(this->http_www_google_.Format("http://bla.www.%D")))); - this->MatchCookieLines( - "A=B; C=D; E=F; G=H", - this->GetCookies(cs.get(), this->http_www_google_.url())); + cs, GURL(this->http_www_google_.Format("http://bla.www.%D")))); + this->MatchCookieLines("A=B; C=D; E=F; G=H", + this->GetCookies(cs, this->http_www_google_.url())); } // 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.get(), this->http_www_google_.url(), + CookieStore* cs = this->GetCookieStore(); + EXPECT_FALSE(this->SetCookie(cs, this->http_www_google_.url(), "a=1; domain=.www.google.com.")); - EXPECT_FALSE(this->SetCookie(cs.get(), this->http_www_google_.url(), + EXPECT_FALSE(this->SetCookie(cs, this->http_www_google_.url(), "b=2; domain=.www.google.com..")); - this->MatchCookieLines( - std::string(), this->GetCookies(cs.get(), this->http_www_google_.url())); + this->MatchCookieLines(std::string(), + this->GetCookies(cs, this->http_www_google_.url())); } // 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()); + 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.get(), url_abcd, "a=1; domain=.a.b.c.d.com")); - EXPECT_TRUE(this->SetCookie(cs.get(), url_abcd, "b=2; domain=.b.c.d.com")); - EXPECT_TRUE(this->SetCookie(cs.get(), url_abcd, "c=3; domain=.c.d.com")); - EXPECT_TRUE(this->SetCookie(cs.get(), url_abcd, "d=4; domain=.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.get(), url_abcd)); - this->MatchCookieLines("b=2; c=3; d=4", this->GetCookies(cs.get(), url_bcd)); - this->MatchCookieLines("c=3; d=4", this->GetCookies(cs.get(), url_cd)); - this->MatchCookieLines("d=4", this->GetCookies(cs.get(), url_d)); + 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.get(), url_bcd, "X=bcd; domain=.b.c.d.com")); - EXPECT_TRUE(this->SetCookie(cs.get(), url_bcd, "X=cd; domain=.c.d.com")); + 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.get(), url_bcd)); - this->MatchCookieLines("c=3; d=4; X=cd", this->GetCookies(cs.get(), url_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.get(), url_foobar, "a=1; domain=.yo.foo.bar.com")); - - EXPECT_FALSE(this->SetCookie(cs.get(), url_foobar, "b=2; domain=.foo.com")); - EXPECT_FALSE( - this->SetCookie(cs.get(), url_foobar, "c=3; domain=.bar.foo.com")); - - // Different TLD, but the rest is a substring. - EXPECT_FALSE( - this->SetCookie(cs.get(), url_foobar, "d=4; domain=.foo.bar.com.net")); - - // A substring that isn't really a parent domain. - EXPECT_FALSE(this->SetCookie(cs.get(), url_foobar, "e=5; domain=ar.com")); - - // Completely invalid domains: - EXPECT_FALSE(this->SetCookie(cs.get(), url_foobar, "f=6; domain=.")); - EXPECT_FALSE(this->SetCookie(cs.get(), url_foobar, "g=7; domain=/")); - EXPECT_FALSE(this->SetCookie( - cs.get(), url_foobar, "h=8; domain=http://foo.bar.com")); - EXPECT_FALSE( - this->SetCookie(cs.get(), url_foobar, "i=9; domain=..foo.bar.com")); - EXPECT_FALSE( - this->SetCookie(cs.get(), 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.get(), url_foobar, "k=11; domain=.foo.bar.com?blah")); - EXPECT_FALSE(this->SetCookie( - cs.get(), url_foobar, "l=12; domain=.foo.bar.com/blah")); - EXPECT_FALSE( - this->SetCookie(cs.get(), url_foobar, "m=13; domain=.foo.bar.com:80")); - EXPECT_FALSE( - this->SetCookie(cs.get(), url_foobar, "n=14; domain=.foo.bar.com:")); - EXPECT_FALSE( - this->SetCookie(cs.get(), url_foobar, "o=15; domain=.foo.bar.com#sup")); + CookieStore* cs = this->GetCookieStore(); + GURL url_foobar("http://foo.bar.com"); - this->MatchCookieLines(std::string(), - this->GetCookies(cs.get(), url_foobar)); - } + // More specific sub-domain than allowed. + EXPECT_FALSE(this->SetCookie(cs, url_foobar, "a=1; domain=.yo.foo.bar.com")); - { - // 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.get(), url_foocom, "a=1; domain=.foo.com.com.com")); - this->MatchCookieLines(std::string(), - this->GetCookies(cs.get(), url_foocom)); - } + 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(std::string(), this->GetCookies(cs, url_foobar)); } -// 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.get(), url_hosted, "sawAd=1; domain=filefront.com")); - this->MatchCookieLines("sawAd=1", this->GetCookies(cs.get(), url_hosted)); - this->MatchCookieLines("sawAd=1", - this->GetCookies(cs.get(), url_filefront)); - } +// 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. +TYPED_TEST_P(CookieStoreTest, InvalidDomainSameDomainAndRegistry) { + 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(std::string(), this->GetCookies(cs, url_foocom)); +} - { // 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.get(), url, "a=1; domain=www.google.com")); - this->MatchCookieLines("a=1", this->GetCookies(cs.get(), url)); - this->MatchCookieLines( - "a=1", this->GetCookies(cs.get(), GURL("http://sub.www.google.com"))); - this->MatchCookieLines( - std::string(), - this->GetCookies(cs.get(), GURL("http://something-else.com"))); - } +// Setting the domain without a dot on a parent domain should add a domain +// cookie. +TYPED_TEST_P(CookieStoreTest, DomainWithoutLeadingDotParentDomain) { + 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 specified domain matches the domain of the URL exactly, treat +// it as setting a domain cookie. +TYPED_TEST_P(CookieStoreTest, DomainWithoutLeadingDotSameDomain) { + 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( + std::string(), 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()); + CookieStore* cs = this->GetCookieStore(); GURL url("http://www.google.com"); - EXPECT_TRUE(this->SetCookie(cs.get(), url, "a=1; domain=.GOOGLE.COM")); - EXPECT_TRUE(this->SetCookie(cs.get(), url, "b=2; domain=.wWw.gOOgLE.coM")); - this->MatchCookieLines("a=1; b=2", this->GetCookies(cs.get(), url)); + 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.get(), url_ip, kValidCookieLine)); - this->MatchCookieLines("A=B", this->GetCookies(cs.get(), url_ip)); - } + 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.get(), url_ip, "b=2; domain=.1.2.3.4")); - EXPECT_FALSE(this->SetCookie(cs.get(), url_ip, "c=3; domain=.3.4")); - this->MatchCookieLines(std::string(), this->GetCookies(cs.get(), 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.get(), url_ip, "b=2; domain=1.2.3.3")); - this->MatchCookieLines(std::string(), this->GetCookies(cs.get(), url_ip)); - EXPECT_TRUE(this->SetCookie(cs.get(), url_ip, "b=2; domain=1.2.3.4")); - this->MatchCookieLines("b=2", this->GetCookies(cs.get(), url_ip)); - } +// IP addresses should not be able to set domain cookies. +TYPED_TEST_P(CookieStoreTest, TestIpAddressNoDomainCookies) { + GURL url_ip("http://1.2.3.4/weee"); + 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(std::string(), 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(std::string(), 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) { - if (TypeParam::supports_non_dotted_domains) { - 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.get(), url, "a=1")); - EXPECT_FALSE(this->SetCookie(cs.get(), url, "b=2; domain=.com")); - - this->MatchCookieLines("a=1", this->GetCookies(cs.get(), url)); - // Make sure it doesn't show up for a normal .com, it should be a host - // not a domain cookie. - this->MatchCookieLines( - std::string(), - this->GetCookies(cs.get(), GURL("http://hopefully-no-cookies.com/"))); - this->MatchCookieLines(std::string(), - this->GetCookies(cs.get(), GURL("http://.com/"))); - } +// Test a TLD setting cookies on itself. +TYPED_TEST_P(CookieStoreTest, TestTLD) { + if (!TypeParam::supports_non_dotted_domains) + return; + CookieStore* cs = this->GetCookieStore(); + GURL url("http://com/"); - if (TypeParam::supports_non_dotted_domains) { - // Exact matches between the domain attribute and the host are treated as - // host cookies, not domain cookies. - scoped_refptr<CookieStore> cs(this->GetCookieStore()); - GURL url("http://com/"); - EXPECT_TRUE(this->SetCookie(cs.get(), url, "a=1; domain=com")); + // Allow setting on "com", (but only as a host cookie). + EXPECT_TRUE(this->SetCookie(cs, url, "a=1")); + // Domain cookies can't be set. + EXPECT_FALSE(this->SetCookie(cs, url, "b=2; domain=.com")); + // Exact matches between the domain attribute and the host are treated as + // host cookies, not domain cookies. + EXPECT_TRUE(this->SetCookie(cs, url, "c=3; domain=com")); - this->MatchCookieLines("a=1", this->GetCookies(cs.get(), url)); - // Make sure it doesn't show up for a normal .com, it should be a host - // not a domain cookie. - this->MatchCookieLines( - std::string(), - this->GetCookies(cs.get(), GURL("http://hopefully-no-cookies.com/"))); - this->MatchCookieLines(std::string(), - this->GetCookies(cs.get(), GURL("http://.com/"))); - } + this->MatchCookieLines("a=1; c=3", this->GetCookies(cs, url)); - { - // http://com. should be treated the same as http://com. - scoped_refptr<CookieStore> cs(this->GetCookieStore()); - GURL url("http://com./index.html"); - EXPECT_TRUE(this->SetCookie(cs.get(), url, "a=1")); - this->MatchCookieLines("a=1", this->GetCookies(cs.get(), url)); - this->MatchCookieLines( - std::string(), - this->GetCookies(cs.get(), - GURL("http://hopefully-no-cookies.com./"))); - } + // Make sure they don't show up for a normal .com, they should be host, + // domain, cookies. + this->MatchCookieLines( + std::string(), + this->GetCookies(cs, GURL("http://hopefully-no-cookies.com/"))); + this->MatchCookieLines(std::string(), + this->GetCookies(cs, GURL("http://.com/"))); +} - { // 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.get(), url, "a=1; domain=.b")); - EXPECT_FALSE(this->SetCookie(cs.get(), url, "b=2; domain=b")); - this->MatchCookieLines(std::string(), this->GetCookies(cs.get(), url)); - } +// http://com. should be treated the same as http://com. +TYPED_TEST_P(CookieStoreTest, TestTLDWithTerminalDot) { + CookieStore* cs = this->GetCookieStore(); + GURL url("http://com./index.html"); + EXPECT_TRUE(this->SetCookie(cs, url, "a=1")); + EXPECT_FALSE(this->SetCookie(cs, url, "b=2; domain=.com.")); + this->MatchCookieLines("a=1", this->GetCookies(cs, url)); + this->MatchCookieLines( + std::string(), + this->GetCookies(cs, GURL("http://hopefully-no-cookies.com./"))); +} - { // 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.get(), url, "a=1; domain=.com")); - EXPECT_FALSE(this->SetCookie(cs.get(), url, "b=2; domain=com")); - this->MatchCookieLines(std::string(), this->GetCookies(cs.get(), url)); - } +TYPED_TEST_P(CookieStoreTest, TestSubdomainSettingCookiesOnUnknownTLD) { + 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(std::string(), 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.get(), url, "a=1; domain=.co.uk")); - EXPECT_FALSE(this->SetCookie(cs.get(), url, "b=2; domain=.uk")); - this->MatchCookieLines(std::string(), this->GetCookies(cs.get(), url)); - this->MatchCookieLines( - std::string(), - this->GetCookies(cs.get(), GURL("http://something-else.co.uk"))); - this->MatchCookieLines( - std::string(), - this->GetCookies(cs.get(), GURL("http://something-else.uk"))); - } +TYPED_TEST_P(CookieStoreTest, TestSubdomainSettingCookiesOnKnownTLD) { + 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(std::string(), this->GetCookies(cs, url)); +} - { // 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.get(), url, "a=1")); - EXPECT_FALSE(this->SetCookie(cs.get(), url, "b=2; domain=.b")); - this->MatchCookieLines("a=1", this->GetCookies(cs.get(), url)); - } +TYPED_TEST_P(CookieStoreTest, TestSubdomainSettingCookiesOnKnownDottedTLD) { + 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(std::string(), this->GetCookies(cs, url)); + this->MatchCookieLines( + std::string(), this->GetCookies(cs, GURL("http://something-else.co.uk"))); + this->MatchCookieLines( + std::string(), this->GetCookies(cs, GURL("http://something-else.uk"))); +} - if (TypeParam::supports_non_dotted_domains) { - // Exact matches between the domain attribute and an intranet host are - // treated as host cookies, not domain cookies. - scoped_refptr<CookieStore> cs(this->GetCookieStore()); - GURL url("http://b/"); - EXPECT_TRUE(this->SetCookie(cs.get(), url, "a=1; domain=b")); +// Intranet URLs should only be able to set host cookies. +TYPED_TEST_P(CookieStoreTest, TestSettingCookiesOnUnknownTLD) { + 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")); + this->MatchCookieLines("a=1", this->GetCookies(cs, url)); +} - this->MatchCookieLines("a=1", this->GetCookies(cs.get(), url)); - // Make sure it doesn't show up for an intranet subdomain, it should be a - // host not a domain cookie. - this->MatchCookieLines( - std::string(), - this->GetCookies(cs.get(), GURL("http://hopefully-no-cookies.b/"))); - this->MatchCookieLines(std::string(), - this->GetCookies(cs.get(), GURL("http://.b/"))); - } +// Exact matches between the domain attribute and an intranet host are +// treated as host cookies, not domain cookies. +TYPED_TEST_P(CookieStoreTest, TestSettingCookiesWithHostDomainOnUnknownTLD) { + if (!TypeParam::supports_non_dotted_domains) + return; + CookieStore* cs = this->GetCookieStore(); + GURL url("http://b"); + EXPECT_TRUE(this->SetCookie(cs, url, "a=1; domain=b")); + + this->MatchCookieLines("a=1", this->GetCookies(cs, url)); + + // Make sure it doesn't show up for an intranet subdomain, it should be + // a host, not domain, cookie. + this->MatchCookieLines( + std::string(), + this->GetCookies(cs, GURL("http://hopefully-no-cookies.b/"))); + this->MatchCookieLines(std::string(), + this->GetCookies(cs, GURL("http://.b/"))); } // 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()); + CookieStore* cs = this->GetCookieStore(); GURL url("http://www.google.com"); GURL url_with_dot("http://www.google.com."); - EXPECT_TRUE(this->SetCookie(cs.get(), url, "a=1")); - this->MatchCookieLines("a=1", this->GetCookies(cs.get(), url)); + EXPECT_TRUE(this->SetCookie(cs, url, "a=1")); + this->MatchCookieLines("a=1", this->GetCookies(cs, url)); // 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. if (TypeParam::preserves_trailing_dots) { - EXPECT_FALSE( - this->SetCookie(cs.get(), url, "b=2; domain=.www.google.com.")); - this->MatchCookieLines("a=1", this->GetCookies(cs.get(), url)); - EXPECT_TRUE( - this->SetCookie(cs.get(), url_with_dot, "b=2; domain=.google.com.")); - this->MatchCookieLines("b=2", this->GetCookies(cs.get(), url_with_dot)); + 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.get(), url, "b=2; domain=.www.google.com.")); - this->MatchCookieLines("a=1 b=2", this->GetCookies(cs.get(), url)); + EXPECT_TRUE(this->SetCookie(cs, url, "b=2; domain=.www.google.com.")); + this->MatchCookieLines("a=1 b=2", this->GetCookies(cs, url)); // Setting this cookie should fail, since the trailing dot on the domain // isn't preserved, and then the domain mismatches the URL. - EXPECT_FALSE( - this->SetCookie(cs.get(), url_with_dot, "b=2; domain=.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( std::string(), - this->GetCookies(cs.get(), GURL("http://hopefully-no-cookies.com/"))); + this->GetCookies(cs, GURL("http://hopefully-no-cookies.com/"))); this->MatchCookieLines(std::string(), - this->GetCookies(cs.get(), GURL("http://.com/"))); + 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.get(), this->ftp_google_.url(), kValidCookieLine)); + CookieStore* cs = this->GetCookieStore(); + EXPECT_FALSE(this->SetCookie(cs, this->ftp_google_.url(), kValidCookieLine)); } TYPED_TEST_P(CookieStoreTest, InvalidScheme_Read) { @@ -663,39 +801,38 @@ TYPED_TEST_P(CookieStoreTest, InvalidScheme_Read) { const std::string kValidDomainCookieLine = this->http_www_google_.Format("A=B; path=/; domain=%D"); - scoped_refptr<CookieStore> cs(this->GetCookieStore()); - EXPECT_TRUE(this->SetCookie(cs.get(), this->http_www_google_.url(), + CookieStore* cs = this->GetCookieStore(); + EXPECT_TRUE(this->SetCookie(cs, this->http_www_google_.url(), kValidDomainCookieLine)); this->MatchCookieLines(std::string(), - this->GetCookies(cs.get(), this->ftp_google_.url())); + this->GetCookies(cs, this->ftp_google_.url())); + EXPECT_EQ(0U, this->GetCookieListWithOptions(cs, this->ftp_google_.url(), + CookieOptions()) + .size()); } TYPED_TEST_P(CookieStoreTest, PathTest) { - scoped_refptr<CookieStore> cs(this->GetCookieStore()); + CookieStore* cs = this->GetCookieStore(); std::string url("http://www.google.izzle"); - EXPECT_TRUE(this->SetCookie(cs.get(), GURL(url), "A=B; path=/wee")); - this->MatchCookieLines("A=B", this->GetCookies(cs.get(), GURL(url + "/wee"))); - this->MatchCookieLines("A=B", - this->GetCookies(cs.get(), GURL(url + "/wee/"))); - this->MatchCookieLines("A=B", - this->GetCookies(cs.get(), GURL(url + "/wee/war"))); + 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.get(), GURL(url + "/wee/war/more/more"))); + "A=B", this->GetCookies(cs, GURL(url + "/wee/war/more/more"))); if (!TypeParam::has_path_prefix_bug) this->MatchCookieLines(std::string(), - this->GetCookies(cs.get(), GURL(url + "/weehee"))); - this->MatchCookieLines(std::string(), - this->GetCookies(cs.get(), GURL(url + "/"))); + this->GetCookies(cs, GURL(url + "/weehee"))); + this->MatchCookieLines(std::string(), this->GetCookies(cs, GURL(url + "/"))); // If we add a 0 length path, it should default to / - EXPECT_TRUE(this->SetCookie(cs.get(), GURL(url), "A=C; path=")); - this->MatchCookieLines("A=B; A=C", - this->GetCookies(cs.get(), GURL(url + "/wee"))); - this->MatchCookieLines("A=C", this->GetCookies(cs.get(), GURL(url + "/"))); + 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, EmptyExpires) { - scoped_refptr<CookieStore> cs(this->GetCookieStore()); + CookieStore* cs = this->GetCookieStore(); CookieOptions options; if (!TypeParam::supports_http_only) options.set_include_httponly(); @@ -704,152 +841,168 @@ TYPED_TEST_P(CookieStoreTest, EmptyExpires) { "ACSTM=20130308043820420042; path=/; domain=ipdl.inpit.go.jp; Expires="; std::string cookie_line = "ACSTM=20130308043820420042"; - this->SetCookieWithOptions(cs.get(), url, set_cookie_line, options); + this->SetCookieWithOptions(cs, url, set_cookie_line, options); this->MatchCookieLines(cookie_line, - this->GetCookiesWithOptions(cs.get(), url, options)); + this->GetCookiesWithOptions(cs, url, options)); options.set_server_time(base::Time::Now() - base::TimeDelta::FromHours(1)); - this->SetCookieWithOptions(cs.get(), url, set_cookie_line, options); + this->SetCookieWithOptions(cs, url, set_cookie_line, options); this->MatchCookieLines(cookie_line, - this->GetCookiesWithOptions(cs.get(), url, options)); + this->GetCookiesWithOptions(cs, url, options)); options.set_server_time(base::Time::Now() + base::TimeDelta::FromHours(1)); - this->SetCookieWithOptions(cs.get(), url, set_cookie_line, options); + this->SetCookieWithOptions(cs, url, set_cookie_line, options); this->MatchCookieLines(cookie_line, - this->GetCookiesWithOptions(cs.get(), url, options)); + this->GetCookiesWithOptions(cs, url, options)); } TYPED_TEST_P(CookieStoreTest, HttpOnlyTest) { if (!TypeParam::supports_http_only) return; - scoped_refptr<CookieStore> cs(this->GetCookieStore()); + CookieStore* cs = this->GetCookieStore(); CookieOptions options; options.set_include_httponly(); // Create a httponly cookie. - EXPECT_TRUE(this->SetCookieWithOptions(cs.get(), this->http_www_google_.url(), + EXPECT_TRUE(this->SetCookieWithOptions(cs, this->http_www_google_.url(), "A=B; httponly", options)); // Check httponly read protection. - this->MatchCookieLines( - std::string(), this->GetCookies(cs.get(), this->http_www_google_.url())); - this->MatchCookieLines( - "A=B", this->GetCookiesWithOptions(cs.get(), this->http_www_google_.url(), - options)); + this->MatchCookieLines(std::string(), + this->GetCookies(cs, this->http_www_google_.url())); + this->MatchCookieLines("A=B", this->GetCookiesWithOptions( + cs, this->http_www_google_.url(), options)); // Check httponly overwrite protection. - EXPECT_FALSE(this->SetCookie(cs.get(), this->http_www_google_.url(), "A=C")); - this->MatchCookieLines( - std::string(), this->GetCookies(cs.get(), this->http_www_google_.url())); - this->MatchCookieLines( - "A=B", this->GetCookiesWithOptions(cs.get(), this->http_www_google_.url(), - options)); - EXPECT_TRUE(this->SetCookieWithOptions(cs.get(), this->http_www_google_.url(), + EXPECT_FALSE(this->SetCookie(cs, this->http_www_google_.url(), "A=C")); + this->MatchCookieLines(std::string(), + this->GetCookies(cs, this->http_www_google_.url())); + this->MatchCookieLines("A=B", this->GetCookiesWithOptions( + cs, this->http_www_google_.url(), options)); + EXPECT_TRUE(this->SetCookieWithOptions(cs, this->http_www_google_.url(), "A=C", options)); - this->MatchCookieLines( - "A=C", this->GetCookies(cs.get(), this->http_www_google_.url())); + this->MatchCookieLines("A=C", + this->GetCookies(cs, this->http_www_google_.url())); // Check httponly create protection. EXPECT_FALSE( - this->SetCookie(cs.get(), this->http_www_google_.url(), "B=A; httponly")); - this->MatchCookieLines( - "A=C", this->GetCookiesWithOptions(cs.get(), this->http_www_google_.url(), - options)); - EXPECT_TRUE(this->SetCookieWithOptions(cs.get(), this->http_www_google_.url(), + this->SetCookie(cs, this->http_www_google_.url(), "B=A; httponly")); + this->MatchCookieLines("A=C", this->GetCookiesWithOptions( + cs, this->http_www_google_.url(), options)); + EXPECT_TRUE(this->SetCookieWithOptions(cs, this->http_www_google_.url(), "B=A; httponly", options)); - this->MatchCookieLines("A=C; B=A", - this->GetCookiesWithOptions( - cs.get(), this->http_www_google_.url(), options)); this->MatchCookieLines( - "A=C", this->GetCookies(cs.get(), this->http_www_google_.url())); + "A=C; B=A", + this->GetCookiesWithOptions(cs, this->http_www_google_.url(), options)); + this->MatchCookieLines("A=C", + this->GetCookies(cs, this->http_www_google_.url())); } TYPED_TEST_P(CookieStoreTest, TestCookieDeletion) { - scoped_refptr<CookieStore> cs(this->GetCookieStore()); + CookieStore* cs = this->GetCookieStore(); // Create a session cookie. - EXPECT_TRUE(this->SetCookie(cs.get(), this->http_www_google_.url(), - kValidCookieLine)); - this->MatchCookieLines( - "A=B", this->GetCookies(cs.get(), this->http_www_google_.url())); + EXPECT_TRUE( + this->SetCookie(cs, this->http_www_google_.url(), kValidCookieLine)); + this->MatchCookieLines("A=B", + this->GetCookies(cs, this->http_www_google_.url())); // Delete it via Max-Age. - EXPECT_TRUE(this->SetCookie(cs.get(), this->http_www_google_.url(), + EXPECT_TRUE(this->SetCookie(cs, this->http_www_google_.url(), std::string(kValidCookieLine) + "; max-age=0")); - this->MatchCookieLineWithTimeout(cs.get(), this->http_www_google_.url(), + this->MatchCookieLineWithTimeout(cs, this->http_www_google_.url(), std::string()); // Create a session cookie. - EXPECT_TRUE(this->SetCookie(cs.get(), this->http_www_google_.url(), - kValidCookieLine)); - this->MatchCookieLines( - "A=B", this->GetCookies(cs.get(), this->http_www_google_.url())); + EXPECT_TRUE( + this->SetCookie(cs, this->http_www_google_.url(), kValidCookieLine)); + this->MatchCookieLines("A=B", + this->GetCookies(cs, this->http_www_google_.url())); // Delete it via Expires. - EXPECT_TRUE(this->SetCookie(cs.get(), this->http_www_google_.url(), + EXPECT_TRUE(this->SetCookie(cs, this->http_www_google_.url(), std::string(kValidCookieLine) + "; expires=Mon, 18-Apr-1977 22:50:13 GMT")); - this->MatchCookieLines( - std::string(), this->GetCookies(cs.get(), this->http_www_google_.url())); + this->MatchCookieLines(std::string(), + this->GetCookies(cs, this->http_www_google_.url())); // Create a persistent cookie. EXPECT_TRUE(this->SetCookie( - cs.get(), this->http_www_google_.url(), + cs, this->http_www_google_.url(), std::string(kValidCookieLine) + "; expires=Mon, 18-Apr-22 22:50:13 GMT")); - this->MatchCookieLines( - "A=B", this->GetCookies(cs.get(), this->http_www_google_.url())); + this->MatchCookieLines("A=B", + this->GetCookies(cs, this->http_www_google_.url())); // Delete it via Max-Age. - EXPECT_TRUE(this->SetCookie(cs.get(), this->http_www_google_.url(), + EXPECT_TRUE(this->SetCookie(cs, this->http_www_google_.url(), std::string(kValidCookieLine) + "; max-age=0")); - this->MatchCookieLineWithTimeout(cs.get(), this->http_www_google_.url(), + this->MatchCookieLineWithTimeout(cs, this->http_www_google_.url(), std::string()); // Create a persistent cookie. EXPECT_TRUE(this->SetCookie( - cs.get(), this->http_www_google_.url(), + cs, this->http_www_google_.url(), std::string(kValidCookieLine) + "; expires=Mon, 18-Apr-22 22:50:13 GMT")); - this->MatchCookieLines( - "A=B", this->GetCookies(cs.get(), this->http_www_google_.url())); + this->MatchCookieLines("A=B", + this->GetCookies(cs, this->http_www_google_.url())); // Delete it via Expires. - EXPECT_TRUE(this->SetCookie(cs.get(), this->http_www_google_.url(), + EXPECT_TRUE(this->SetCookie(cs, this->http_www_google_.url(), std::string(kValidCookieLine) + "; expires=Mon, 18-Apr-1977 22:50:13 GMT")); - this->MatchCookieLines( - std::string(), this->GetCookies(cs.get(), this->http_www_google_.url())); + this->MatchCookieLines(std::string(), + this->GetCookies(cs, this->http_www_google_.url())); // Create a persistent cookie. EXPECT_TRUE(this->SetCookie( - cs.get(), this->http_www_google_.url(), + cs, this->http_www_google_.url(), std::string(kValidCookieLine) + "; expires=Mon, 18-Apr-22 22:50:13 GMT")); - this->MatchCookieLines( - "A=B", this->GetCookies(cs.get(), this->http_www_google_.url())); + this->MatchCookieLines("A=B", + this->GetCookies(cs, this->http_www_google_.url())); // Check that it is not deleted with significant enough clock skew. base::Time server_time; EXPECT_TRUE(base::Time::FromString("Sun, 17-Apr-1977 22:50:13 GMT", &server_time)); EXPECT_TRUE(this->SetCookieWithServerTime( - cs.get(), this->http_www_google_.url(), + cs, this->http_www_google_.url(), std::string(kValidCookieLine) + "; expires=Mon, 18-Apr-1977 22:50:13 GMT", server_time)); - this->MatchCookieLines( - "A=B", this->GetCookies(cs.get(), this->http_www_google_.url())); + this->MatchCookieLines("A=B", + this->GetCookies(cs, this->http_www_google_.url())); // Create a persistent cookie. EXPECT_TRUE(this->SetCookie( - cs.get(), this->http_www_google_.url(), + cs, this->http_www_google_.url(), std::string(kValidCookieLine) + "; expires=Mon, 18-Apr-22 22:50:13 GMT")); - this->MatchCookieLines( - "A=B", this->GetCookies(cs.get(), this->http_www_google_.url())); + this->MatchCookieLines("A=B", + this->GetCookies(cs, this->http_www_google_.url())); // Delete it via Expires, with a unix epoch of 0. - EXPECT_TRUE(this->SetCookie(cs.get(), this->http_www_google_.url(), + EXPECT_TRUE(this->SetCookie(cs, this->http_www_google_.url(), std::string(kValidCookieLine) + "; expires=Thu, 1-Jan-1970 00:00:00 GMT")); - this->MatchCookieLines( - std::string(), this->GetCookies(cs.get(), this->http_www_google_.url())); + this->MatchCookieLines(std::string(), + this->GetCookies(cs, this->http_www_google_.url())); +} + +TYPED_TEST_P(CookieStoreTest, TestDeleteAll) { + CookieStore* cs = this->GetCookieStore(); + + // Set a session cookie. + EXPECT_TRUE( + this->SetCookie(cs, this->http_www_google_.url(), kValidCookieLine)); + EXPECT_EQ("A=B", this->GetCookies(cs, this->http_www_google_.url())); + + // Set a persistent cookie. + EXPECT_TRUE(this->SetCookie(cs, this->http_www_google_.url(), + "C=D; expires=Mon, 18-Apr-22 22:50:13 GMT")); + + EXPECT_EQ(2u, this->GetAllCookies(cs).size()); + + // Delete both, and make sure it works + EXPECT_EQ(2, this->DeleteAll(cs)); + EXPECT_EQ(0u, this->GetAllCookies(cs).size()); } TYPED_TEST_P(CookieStoreTest, TestDeleteAllCreatedBetween) { - scoped_refptr<CookieStore> cs(this->GetCookieStore()); + CookieStore* cs = this->GetCookieStore(); const base::Time last_month = base::Time::Now() - base::TimeDelta::FromDays(30); const base::Time last_minute = base::Time::Now() - @@ -860,90 +1013,86 @@ TYPED_TEST_P(CookieStoreTest, TestDeleteAllCreatedBetween) { base::TimeDelta::FromDays(30); // Add a cookie. - EXPECT_TRUE(this->SetCookie(cs.get(), this->http_www_google_.url(), "A=B")); + EXPECT_TRUE(this->SetCookie(cs, this->http_www_google_.url(), "A=B")); // Check that the cookie is in the store. - this->MatchCookieLines( - "A=B", this->GetCookies(cs.get(), this->http_www_google_.url())); + this->MatchCookieLines("A=B", + this->GetCookies(cs, this->http_www_google_.url())); // Remove cookies in empty intervals. - EXPECT_EQ(0, this->DeleteCreatedBetween(cs.get(), last_month, last_minute)); - EXPECT_EQ(0, this->DeleteCreatedBetween(cs.get(), next_minute, next_month)); + 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.get(), this->http_www_google_.url())); + this->MatchCookieLines("A=B", + this->GetCookies(cs, this->http_www_google_.url())); // Remove the cookie with an interval defined by two dates. - EXPECT_EQ(1, this->DeleteCreatedBetween(cs.get(), last_minute, next_minute)); + EXPECT_EQ(1, this->DeleteCreatedBetween(cs, last_minute, next_minute)); // Check that the cookie disappeared. - this->MatchCookieLines( - std::string(), this->GetCookies(cs.get(), this->http_www_google_.url())); + this->MatchCookieLines(std::string(), + this->GetCookies(cs, this->http_www_google_.url())); // Add another cookie. - EXPECT_TRUE(this->SetCookie(cs.get(), this->http_www_google_.url(), "C=D")); + EXPECT_TRUE(this->SetCookie(cs, this->http_www_google_.url(), "C=D")); // Check that the cookie is in the store. - this->MatchCookieLines( - "C=D", this->GetCookies(cs.get(), this->http_www_google_.url())); + this->MatchCookieLines("C=D", + this->GetCookies(cs, this->http_www_google_.url())); // Remove the cookie with a null ending time. - EXPECT_EQ(1, this->DeleteCreatedBetween(cs.get(), last_minute, base::Time())); + EXPECT_EQ(1, this->DeleteCreatedBetween(cs, last_minute, base::Time())); // Check that the cookie disappeared. - this->MatchCookieLines( - std::string(), this->GetCookies(cs.get(), this->http_www_google_.url())); + this->MatchCookieLines(std::string(), + this->GetCookies(cs, this->http_www_google_.url())); } TYPED_TEST_P(CookieStoreTest, TestDeleteAllCreatedBetweenForHost) { - scoped_refptr<CookieStore> cs(this->GetCookieStore()); + CookieStore* cs = this->GetCookieStore(); GURL url_not_google("http://www.notgoogle.com"); base::Time now = base::Time::Now(); // These 3 cookies match the time range and host. - EXPECT_TRUE(this->SetCookie(cs.get(), this->http_www_google_.url(), "A=B")); - EXPECT_TRUE(this->SetCookie(cs.get(), this->http_www_google_.url(), "C=D")); - EXPECT_TRUE(this->SetCookie(cs.get(), this->http_www_google_.url(), "Y=Z")); + EXPECT_TRUE(this->SetCookie(cs, this->http_www_google_.url(), "A=B")); + EXPECT_TRUE(this->SetCookie(cs, this->http_www_google_.url(), "C=D")); + EXPECT_TRUE(this->SetCookie(cs, this->http_www_google_.url(), "Y=Z")); // This cookie does not match host. - EXPECT_TRUE(this->SetCookie(cs.get(), url_not_google, "E=F")); + EXPECT_TRUE(this->SetCookie(cs, url_not_google, "E=F")); // Delete cookies. - EXPECT_EQ( - 3, // Deletes A=B, C=D, Y=Z - this->DeleteAllCreatedBetweenForHost(cs.get(), now, base::Time::Max(), - this->http_www_google_.url())); + EXPECT_EQ(3, // Deletes A=B, C=D, Y=Z + this->DeleteAllCreatedBetweenForHost(cs, now, base::Time::Max(), + this->http_www_google_.url())); } TYPED_TEST_P(CookieStoreTest, TestSecure) { - scoped_refptr<CookieStore> cs(this->GetCookieStore()); + CookieStore* cs = this->GetCookieStore(); - EXPECT_TRUE(this->SetCookie(cs.get(), this->http_www_google_.url(), "A=B")); - this->MatchCookieLines( - "A=B", this->GetCookies(cs.get(), this->http_www_google_.url())); - this->MatchCookieLines( - "A=B", this->GetCookies(cs.get(), this->https_www_google_.url())); + EXPECT_TRUE(this->SetCookie(cs, this->http_www_google_.url(), "A=B")); + this->MatchCookieLines("A=B", + this->GetCookies(cs, this->http_www_google_.url())); + this->MatchCookieLines("A=B", + this->GetCookies(cs, this->https_www_google_.url())); - EXPECT_TRUE(this->SetCookie(cs.get(), this->https_www_google_.url(), - "A=B; secure")); + EXPECT_TRUE( + this->SetCookie(cs, this->https_www_google_.url(), "A=B; secure")); // The secure should overwrite the non-secure. - this->MatchCookieLines( - std::string(), - this->GetCookies(cs.get(), this->http_www_google_.url())); - this->MatchCookieLines( - "A=B", this->GetCookies(cs.get(), this->https_www_google_.url())); + this->MatchCookieLines(std::string(), + this->GetCookies(cs, this->http_www_google_.url())); + this->MatchCookieLines("A=B", + this->GetCookies(cs, this->https_www_google_.url())); - EXPECT_TRUE(this->SetCookie(cs.get(), this->https_www_google_.url(), - "D=E; secure")); - this->MatchCookieLines( - std::string(), - this->GetCookies(cs.get(), this->http_www_google_.url())); - this->MatchCookieLines( - "A=B; D=E", this->GetCookies(cs.get(), this->https_www_google_.url())); + EXPECT_TRUE( + this->SetCookie(cs, this->https_www_google_.url(), "D=E; secure")); + this->MatchCookieLines(std::string(), + this->GetCookies(cs, this->http_www_google_.url())); + this->MatchCookieLines("A=B; D=E", + this->GetCookies(cs, this->https_www_google_.url())); - EXPECT_TRUE( - this->SetCookie(cs.get(), this->https_www_google_.url(), "A=B")); + EXPECT_TRUE(this->SetCookie(cs, this->https_www_google_.url(), "A=B")); // The non-secure should overwrite the secure. - this->MatchCookieLines( - "A=B", this->GetCookies(cs.get(), this->http_www_google_.url())); - this->MatchCookieLines( - "D=E; A=B", this->GetCookies(cs.get(), this->https_www_google_.url())); + this->MatchCookieLines("A=B", + this->GetCookies(cs, this->http_www_google_.url())); + this->MatchCookieLines("D=E; A=B", + this->GetCookies(cs, this->https_www_google_.url())); } static const int kLastAccessThresholdMilliseconds = 200; @@ -952,17 +1101,17 @@ static const int kLastAccessThresholdMilliseconds = 200; TYPED_TEST_P(CookieStoreTest, NetUtilCookieTest) { const GURL test_url("http://mojo.jojo.google.izzle/"); - scoped_refptr<CookieStore> cs(this->GetCookieStore()); + CookieStore* cs = this->GetCookieStore(); - EXPECT_TRUE(this->SetCookie(cs.get(), test_url, "foo=bar")); - std::string value = this->GetCookies(cs.get(), test_url); + 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.get(), test_url, "x=1")); - EXPECT_TRUE(this->SetCookie(cs.get(), test_url, "y=2")); + EXPECT_TRUE(this->SetCookie(cs, test_url, "x=1")); + EXPECT_TRUE(this->SetCookie(cs, test_url, "y=2")); - std::string result = this->GetCookies(cs.get(), test_url); + 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; @@ -971,17 +1120,15 @@ TYPED_TEST_P(CookieStoreTest, NetUtilCookieTest) { TYPED_TEST_P(CookieStoreTest, OverwritePersistentCookie) { GURL url_google("http://www.google.com/"); GURL url_chromium("http://chromium.org"); - scoped_refptr<CookieStore> cs(this->GetCookieStore()); + CookieStore* cs = this->GetCookieStore(); // Insert a cookie "a" for path "/path1" - EXPECT_TRUE(this->SetCookie(cs.get(), - url_google, + 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.get(), - url_google, + EXPECT_TRUE(this->SetCookie(cs, url_google, "b=val1; path=/path1; " "expires=Mon, 18-Apr-22 22:50:14 GMT")); @@ -989,105 +1136,233 @@ TYPED_TEST_P(CookieStoreTest, OverwritePersistentCookie) { // overwrite the non-http-only version. CookieOptions allow_httponly; allow_httponly.set_include_httponly(); - EXPECT_TRUE(this->SetCookieWithOptions(cs.get(), - url_google, + 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.get(), - url_google, + 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.get(), - url_google, + 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.get(), - url_chromium, + 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.get(), GURL("http://www.google.com/path1"))); + "a=val33", this->GetCookies(cs, GURL("http://www.google.com/path1"))); } else { this->MatchCookieLines( "a=val33; b=val2", - this->GetCookies(cs.get(), GURL("http://www.google.com/path1"))); + this->GetCookies(cs, GURL("http://www.google.com/path1"))); } this->MatchCookieLines( - "a=val9", - this->GetCookies(cs.get(), GURL("http://www.google.com/path2"))); + "a=val9", this->GetCookies(cs, GURL("http://www.google.com/path2"))); this->MatchCookieLines( - "a=val99", this->GetCookies(cs.get(), GURL("http://chromium.org/path1"))); + "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.get(), GURL("http://d.c.b.a.google.com/aa/x.html"), "c=1")); - EXPECT_TRUE(this->SetCookie(cs.get(), - GURL("http://b.a.google.com/aa/bb/cc/x.html"), + 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.get(), - GURL("http://b.a.google.com/aa/bb/cc/x.html"), + 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.get(), + 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.get(), GURL("http://d.c.b.a.google.com/aa/bb/x.html"), "b=1")); - EXPECT_TRUE(this->SetCookie( - cs.get(), 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.get(), - GURL("http://d.c.b.a.google.com/aa/bb/cc/dd"))); + 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"))); + + CookieOptions options; + CookieList cookies = this->GetCookieListWithOptions( + cs, GURL("http://d.c.b.a.google.com/aa/bb/cc/dd"), options); + CookieList::const_iterator it = cookies.begin(); + + ASSERT_TRUE(it != cookies.end()); + EXPECT_EQ("d", it->Name()); + + ASSERT_TRUE(++it != cookies.end()); + EXPECT_EQ("a", it->Name()); + + ASSERT_TRUE(++it != cookies.end()); + EXPECT_EQ("e", it->Name()); + + ASSERT_TRUE(++it != cookies.end()); + EXPECT_EQ("b", it->Name()); + + ASSERT_TRUE(++it != cookies.end()); + EXPECT_EQ("c", it->Name()); + + EXPECT_TRUE(++it == cookies.end()); +} + +// Check that GetAllCookiesAsync returns cookies from multiple domains, in the +// correct order. +TYPED_TEST_P(CookieStoreTest, GetAllCookiesAsync) { + CookieStore* cs = this->GetCookieStore(); + + EXPECT_TRUE( + this->SetCookie(cs, this->http_www_google_.url(), "A=B; path=/a")); + EXPECT_TRUE(this->SetCookie(cs, this->http_foo_com_.url(), "C=D;/")); + EXPECT_TRUE(this->SetCookie(cs, this->http_bar_com_.url(), "E=F; path=/bar")); + + // Check cookies for url. + CookieList cookies = this->GetAllCookies(cs); + CookieList::const_iterator it = cookies.begin(); + + ASSERT_TRUE(it != cookies.end()); + EXPECT_EQ(this->http_bar_com_.host(), it->Domain()); + EXPECT_EQ("/bar", it->Path()); + EXPECT_EQ("E", it->Name()); + EXPECT_EQ("F", it->Value()); + + ASSERT_TRUE(++it != cookies.end()); + EXPECT_EQ(this->http_www_google_.host(), it->Domain()); + EXPECT_EQ("/a", it->Path()); + EXPECT_EQ("A", it->Name()); + EXPECT_EQ("B", it->Value()); + + ASSERT_TRUE(++it != cookies.end()); + EXPECT_EQ(this->http_foo_com_.host(), it->Domain()); + EXPECT_EQ("/", it->Path()); + EXPECT_EQ("C", it->Name()); + EXPECT_EQ("D", it->Value()); + + ASSERT_TRUE(++it == cookies.end()); +} + +TYPED_TEST_P(CookieStoreTest, DeleteCookieAsync) { + CookieStore* cs = this->GetCookieStore(); + + EXPECT_TRUE( + this->SetCookie(cs, this->http_www_google_.url(), "A=A1; path=/")); + EXPECT_TRUE( + this->SetCookie(cs, this->http_www_google_.url(), "A=A2; path=/foo")); + EXPECT_TRUE( + this->SetCookie(cs, this->http_www_google_.url(), "A=A3; path=/bar")); + EXPECT_TRUE( + this->SetCookie(cs, this->http_www_google_.url(), "B=B1; path=/")); + EXPECT_TRUE( + this->SetCookie(cs, this->http_www_google_.url(), "B=B2; path=/foo")); + EXPECT_TRUE( + this->SetCookie(cs, this->http_www_google_.url(), "B=B3; path=/bar")); + + this->DeleteCookie(cs, this->http_www_google_.AppendPath("foo/bar"), "A"); + + CookieList cookies = this->GetAllCookies(cs); + size_t expected_size = 4; + EXPECT_EQ(expected_size, cookies.size()); + for (const auto& cookie : cookies) { + EXPECT_NE("A1", cookie.Value()); + EXPECT_NE("A2", cookie.Value()); + } +} + +TYPED_TEST_P(CookieStoreTest, DeleteCanonicalCookieAsync) { + CookieStore* cs = this->GetCookieStore(); + + // Set two cookies with the same name, and make sure both are set. + EXPECT_TRUE( + this->SetCookie(cs, this->http_www_google_.url(), "A=B;Path=/foo")); + EXPECT_TRUE( + this->SetCookie(cs, this->http_www_google_.url(), "A=C;Path=/bar")); + EXPECT_EQ(2u, this->GetAllCookies(cs).size()); + EXPECT_EQ("A=B", this->GetCookies(cs, this->www_google_foo_.url())); + EXPECT_EQ("A=C", this->GetCookies(cs, this->www_google_bar_.url())); + + // Delete the "/foo" cookie, and make sure only it was deleted. + CookieList cookies = this->GetCookieListWithOptions( + cs, this->www_google_foo_.url(), CookieOptions()); + ASSERT_EQ(1u, cookies.size()); + EXPECT_EQ(1, this->DeleteCanonicalCookie(cs, cookies[0])); + EXPECT_EQ(1u, this->GetAllCookies(cs).size()); + EXPECT_EQ("", this->GetCookies(cs, this->www_google_foo_.url())); + EXPECT_EQ("A=C", this->GetCookies(cs, this->www_google_bar_.url())); + + // Deleting the "/foo" cookie again should fail. + EXPECT_EQ(0, this->DeleteCanonicalCookie(cs, cookies[0])); + + // Try to delete the "/bar" cookie after overwriting it with a new cookie. + cookies = this->GetCookieListWithOptions(cs, this->www_google_bar_.url(), + CookieOptions()); + ASSERT_EQ(1u, cookies.size()); + EXPECT_TRUE( + this->SetCookie(cs, this->http_www_google_.url(), "A=D;Path=/bar")); + EXPECT_EQ(0, this->DeleteCanonicalCookie(cs, cookies[0])); + EXPECT_EQ(1u, this->GetAllCookies(cs).size()); + EXPECT_EQ("A=D", this->GetCookies(cs, this->www_google_bar_.url())); + + // Delete the new "/bar" cookie. + cookies = this->GetCookieListWithOptions(cs, this->www_google_bar_.url(), + CookieOptions()); + ASSERT_EQ(1u, cookies.size()); + EXPECT_EQ(1, this->DeleteCanonicalCookie(cs, cookies[0])); + EXPECT_EQ(0u, this->GetAllCookies(cs).size()); + EXPECT_EQ("", this->GetCookies(cs, this->www_google_bar_.url())); } TYPED_TEST_P(CookieStoreTest, DeleteSessionCookie) { - scoped_refptr<CookieStore> cs(this->GetCookieStore()); + CookieStore* cs = this->GetCookieStore(); // Create a session cookie and a persistent cookie. - EXPECT_TRUE(this->SetCookie(cs.get(), this->http_www_google_.url(), + EXPECT_TRUE(this->SetCookie(cs, this->http_www_google_.url(), std::string(kValidCookieLine))); EXPECT_TRUE(this->SetCookie( - cs.get(), this->http_www_google_.url(), + cs, this->http_www_google_.url(), this->http_www_google_.Format("C=D; path=/; domain=%D;" "expires=Mon, 18-Apr-22 22:50:13 GMT"))); - this->MatchCookieLines( - "A=B; C=D", this->GetCookies(cs.get(), this->http_www_google_.url())); + this->MatchCookieLines("A=B; C=D", + this->GetCookies(cs, this->http_www_google_.url())); // Delete the session cookie. - this->DeleteSessionCookies(cs.get()); + this->DeleteSessionCookies(cs); // Check that the session cookie has been deleted but not the persistent one. - EXPECT_EQ("C=D", this->GetCookies(cs.get(), this->http_www_google_.url())); + EXPECT_EQ("C=D", this->GetCookies(cs, this->http_www_google_.url())); } REGISTER_TYPED_TEST_CASE_P(CookieStoreTest, - TypeTest, + SetCookieWithDetailsAsync, DomainTest, DomainWithTrailingDotTest, ValidSubdomainTest, InvalidDomainTest, - DomainWithoutLeadingDotTest, + InvalidDomainSameDomainAndRegistry, + DomainWithoutLeadingDotParentDomain, + DomainWithoutLeadingDotSameDomain, CaseInsensitiveDomainTest, TestIpAddress, - TestNonDottedAndTLD, + TestIpAddressNoDomainCookies, + TestTLD, + TestTLDWithTerminalDot, + TestSubdomainSettingCookiesOnUnknownTLD, + TestSubdomainSettingCookiesOnKnownTLD, + TestSubdomainSettingCookiesOnKnownDottedTLD, + TestSettingCookiesOnUnknownTLD, + TestSettingCookiesWithHostDomainOnUnknownTLD, TestHostEndsWithDot, InvalidScheme, InvalidScheme_Read, @@ -1095,188 +1370,18 @@ REGISTER_TYPED_TEST_CASE_P(CookieStoreTest, EmptyExpires, HttpOnlyTest, TestCookieDeletion, + TestDeleteAll, TestDeleteAllCreatedBetween, TestDeleteAllCreatedBetweenForHost, TestSecure, NetUtilCookieTest, OverwritePersistentCookie, CookieOrdering, + GetAllCookiesAsync, + DeleteCookieAsync, + DeleteCanonicalCookieAsync, DeleteSessionCookie); -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, - StringResultCookieCallback* callback) { - CookieOptions options; - if (!CookieStoreTestTraits::supports_http_only) - options.set_include_httponly(); - cs->GetCookiesWithOptionsAsync( - url, options, - base::Bind(&StringResultCookieCallback::Run, - base::Unretained(callback))); - } - - void GetCookiesWithOptionsTask(CookieStore* cs, - const GURL& url, - const CookieOptions& options, - StringResultCookieCallback* callback) { - cs->GetCookiesWithOptionsAsync( - url, options, - base::Bind(&StringResultCookieCallback::Run, - base::Unretained(callback))); - } - - void SetCookieWithOptionsTask(CookieStore* cs, - const GURL& url, - const std::string& cookie_line, - const CookieOptions& options, - ResultSavingCookieCallback<bool>* callback) { - cs->SetCookieWithOptionsAsync( - url, cookie_line, options, - base::Bind( - &ResultSavingCookieCallback<bool>::Run, - base::Unretained(callback))); - } - - void DeleteCookieTask(CookieStore* cs, - const GURL& url, - const std::string& cookie_name, - NoResultCookieCallback* callback) { - cs->DeleteCookieAsync( - url, cookie_name, - base::Bind(&NoResultCookieCallback::Run, base::Unretained(callback))); - } - - void DeleteSessionCookiesTask(CookieStore* cs, - ResultSavingCookieCallback<int>* callback) { - cs->DeleteSessionCookiesAsync( - base::Bind( - &ResultSavingCookieCallback<int>::Run, - base::Unretained(callback))); - } - - protected: - void RunOnOtherThread(const base::Closure& task) { - other_thread_.Start(); - other_thread_.task_runner()->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.get(), this->http_www_google_.url(), "A=B")); - this->MatchCookieLines( - "A=B", this->GetCookies(cs.get(), this->http_www_google_.url())); - StringResultCookieCallback callback(&this->other_thread_); - base::Closure task = base::Bind( - &MultiThreadedCookieStoreTest<TypeParam>::GetCookiesTask, - base::Unretained(this), cs, this->http_www_google_.url(), &callback); - this->RunOnOtherThread(task); - EXPECT_TRUE(callback.did_run()); - EXPECT_EQ("A=B", callback.result()); -} - -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.get(), this->http_www_google_.url(), "A=B")); - this->MatchCookieLines( - "A=B", this->GetCookiesWithOptions(cs.get(), this->http_www_google_.url(), - options)); - StringResultCookieCallback callback(&this->other_thread_); - base::Closure task = base::Bind( - &MultiThreadedCookieStoreTest<TypeParam>::GetCookiesWithOptionsTask, - base::Unretained(this), cs, this->http_www_google_.url(), options, - &callback); - this->RunOnOtherThread(task); - EXPECT_TRUE(callback.did_run()); - EXPECT_EQ("A=B", callback.result()); -} - -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.get(), this->http_www_google_.url(), - "A=B", options)); - ResultSavingCookieCallback<bool> callback(&this->other_thread_); - base::Closure task = base::Bind( - &MultiThreadedCookieStoreTest<TypeParam>::SetCookieWithOptionsTask, - base::Unretained(this), cs, this->http_www_google_.url(), "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.get(), this->http_www_google_.url(), - "A=B", options)); - this->DeleteCookie(cs.get(), this->http_www_google_.url(), "A"); - EXPECT_TRUE(this->SetCookieWithOptions(cs.get(), this->http_www_google_.url(), - "A=B", options)); - NoResultCookieCallback callback(&this->other_thread_); - base::Closure task = base::Bind( - &MultiThreadedCookieStoreTest<TypeParam>::DeleteCookieTask, - base::Unretained(this), cs, this->http_www_google_.url(), "A", &callback); - this->RunOnOtherThread(task); - EXPECT_TRUE(callback.did_run()); -} - -TYPED_TEST_P(MultiThreadedCookieStoreTest, ThreadCheckDeleteSessionCookies) { - scoped_refptr<CookieStore> cs(this->GetCookieStore()); - CookieOptions options; - if (!TypeParam::supports_http_only) - options.set_include_httponly(); - EXPECT_TRUE(this->SetCookieWithOptions(cs.get(), this->http_www_google_.url(), - "A=B", options)); - EXPECT_TRUE(this->SetCookieWithOptions( - cs.get(), this->http_www_google_.url(), - "B=C; expires=Mon, 18-Apr-22 22:50:13 GMT", options)); - EXPECT_EQ(1, this->DeleteSessionCookies(cs.get())); - EXPECT_EQ(0, this->DeleteSessionCookies(cs.get())); - EXPECT_TRUE(this->SetCookieWithOptions(cs.get(), this->http_www_google_.url(), - "A=B", options)); - ResultSavingCookieCallback<int> callback(&this->other_thread_); - base::Closure task = base::Bind( - &MultiThreadedCookieStoreTest<TypeParam>::DeleteSessionCookiesTask, - base::Unretained(this), cs, &callback); - this->RunOnOtherThread(task); - EXPECT_TRUE(callback.did_run()); - EXPECT_EQ(1, callback.result()); -} - -REGISTER_TYPED_TEST_CASE_P(MultiThreadedCookieStoreTest, - ThreadCheckGetCookies, - ThreadCheckGetCookiesWithOptions, - ThreadCheckSetCookieWithOptions, - ThreadCheckDeleteCookie, - ThreadCheckDeleteSessionCookies); - } // namespace net #endif // NET_COOKIES_COOKIE_STORE_UNITTEST_H_ diff --git a/chromium/net/cookies/cookie_util.cc b/chromium/net/cookies/cookie_util.cc index 4071e6fd380..210b8d479af 100644 --- a/chromium/net/cookies/cookie_util.cc +++ b/chromium/net/cookies/cookie_util.cc @@ -11,8 +11,8 @@ #include "base/strings/string_tokenizer.h" #include "base/strings/string_util.h" #include "build/build_config.h" -#include "net/base/net_util.h" #include "net/base/registry_controlled_domains/registry_controlled_domain.h" +#include "net/base/url_util.h" #include "url/gurl.h" namespace net { diff --git a/chromium/net/cookies/parse_cookie_line_fuzzer.cc b/chromium/net/cookies/parse_cookie_line_fuzzer.cc new file mode 100644 index 00000000000..997839841ba --- /dev/null +++ b/chromium/net/cookies/parse_cookie_line_fuzzer.cc @@ -0,0 +1,15 @@ +// Copyright 2016 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 <stddef.h> +#include <stdint.h> + +#include "net/cookies/parsed_cookie.h" + +// Entry point for LibFuzzer. +extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { + std::string input(data, data + size); + net::ParsedCookie parsed_cookie(input); + return 0; +} diff --git a/chromium/net/cookies/parsed_cookie.cc b/chromium/net/cookies/parsed_cookie.cc index bd8b0dc2270..2175692f534 100644 --- a/chromium/net/cookies/parsed_cookie.cc +++ b/chromium/net/cookies/parsed_cookie.cc @@ -55,7 +55,7 @@ const char kExpiresTokenName[] = "expires"; const char kMaxAgeTokenName[] = "max-age"; const char kSecureTokenName[] = "secure"; const char kHttpOnlyTokenName[] = "httponly"; -const char kFirstPartyOnlyTokenName[] = "first-party-only"; +const char kSameSiteTokenName[] = "samesite"; const char kPriorityTokenName[] = "priority"; const char kTerminator[] = "\n\r\0"; @@ -163,7 +163,7 @@ ParsedCookie::ParsedCookie(const std::string& cookie_line) maxage_index_(0), secure_index_(0), httponly_index_(0), - firstpartyonly_index_(0), + same_site_index_(0), priority_index_(0) { if (cookie_line.size() > kMaxCookieSize) { VLOG(1) << "Not parsing cookie, too large: " << cookie_line.size(); @@ -179,7 +179,13 @@ ParsedCookie::~ParsedCookie() { } bool ParsedCookie::IsValid() const { - return !pairs_.empty(); + return !pairs_.empty() && IsSameSiteAttributeValid(); +} + +CookieSameSite ParsedCookie::SameSite() const { + return (same_site_index_ == 0) + ? CookieSameSite::DEFAULT_MODE + : StringToCookieSameSite(pairs_[same_site_index_].second); } CookiePriority ParsedCookie::Priority() const { @@ -230,9 +236,8 @@ bool ParsedCookie::SetIsHttpOnly(bool is_http_only) { return SetBool(&httponly_index_, kHttpOnlyTokenName, is_http_only); } -bool ParsedCookie::SetIsFirstPartyOnly(bool is_first_party_only) { - return SetBool(&firstpartyonly_index_, kFirstPartyOnlyTokenName, - is_first_party_only); +bool ParsedCookie::SetSameSite(const std::string& is_same_site) { + return SetString(&same_site_index_, kSameSiteTokenName, is_same_site); } bool ParsedCookie::SetPriority(const std::string& priority) { @@ -245,8 +250,7 @@ std::string ParsedCookie::ToCookieLine() const { if (!out.empty()) out.append("; "); out.append(it->first); - if (it->first != kSecureTokenName && it->first != kHttpOnlyTokenName && - it->first != kFirstPartyOnlyTokenName) { + if (it->first != kSecureTokenName && it->first != kHttpOnlyTokenName) { out.append("="); out.append(it->second); } @@ -437,8 +441,8 @@ void ParsedCookie::SetupAttributes() { secure_index_ = i; } else if (pairs_[i].first == kHttpOnlyTokenName) { httponly_index_ = i; - } else if (pairs_[i].first == kFirstPartyOnlyTokenName) { - firstpartyonly_index_ = i; + } else if (pairs_[i].first == kSameSiteTokenName) { + same_site_index_ = i; } else if (pairs_[i].first == kPriorityTokenName) { priority_index_ = i; } else { @@ -490,14 +494,9 @@ void ParsedCookie::ClearAttributePair(size_t index) { if (index == 0) return; - size_t* indexes[] = {&path_index_, - &domain_index_, - &expires_index_, - &maxage_index_, - &secure_index_, - &httponly_index_, - &firstpartyonly_index_, - &priority_index_}; + size_t* indexes[] = {&path_index_, &domain_index_, &expires_index_, + &maxage_index_, &secure_index_, &httponly_index_, + &same_site_index_, &priority_index_}; for (size_t i = 0; i < arraysize(indexes); ++i) { if (*indexes[i] == index) *indexes[i] = 0; @@ -507,4 +506,8 @@ void ParsedCookie::ClearAttributePair(size_t index) { pairs_.erase(pairs_.begin() + index); } +bool ParsedCookie::IsSameSiteAttributeValid() const { + return same_site_index_ == 0 || SameSite() != CookieSameSite::DEFAULT_MODE; +} + } // namespace diff --git a/chromium/net/cookies/parsed_cookie.h b/chromium/net/cookies/parsed_cookie.h index 8317609be5f..f48015dbd2f 100644 --- a/chromium/net/cookies/parsed_cookie.h +++ b/chromium/net/cookies/parsed_cookie.h @@ -50,7 +50,7 @@ class NET_EXPORT ParsedCookie { const std::string& MaxAge() const { return pairs_[maxage_index_].second; } bool IsSecure() const { return secure_index_ != 0; } bool IsHttpOnly() const { return httponly_index_ != 0; } - bool IsFirstPartyOnly() const { return firstpartyonly_index_ != 0; } + CookieSameSite SameSite() const; CookiePriority Priority() const; // Returns the number of attributes, for example, returning 2 for: @@ -70,7 +70,7 @@ class NET_EXPORT ParsedCookie { bool SetMaxAge(const std::string& maxage); bool SetIsSecure(bool is_secure); bool SetIsHttpOnly(bool is_http_only); - bool SetIsFirstPartyOnly(bool is_first_party_only); + bool SetSameSite(const std::string& same_site); bool SetPriority(const std::string& priority); // Returns the cookie description as it appears in a HTML response header. @@ -129,6 +129,10 @@ class NET_EXPORT ParsedCookie { // |index| refers to a position in |pairs_|. void ClearAttributePair(size_t index); + // Returns false if a 'SameSite' attribute is present, but has an unrecognized + // value. In particular, this includes attributes with empty values. + bool IsSameSiteAttributeValid() const; + PairList pairs_; // These will default to 0, but that should never be valid since the // 0th index is the user supplied token/value, not an attribute. @@ -140,7 +144,7 @@ class NET_EXPORT ParsedCookie { size_t maxage_index_; size_t secure_index_; size_t httponly_index_; - size_t firstpartyonly_index_; + size_t same_site_index_; size_t priority_index_; DISALLOW_COPY_AND_ASSIGN(ParsedCookie); diff --git a/chromium/net/cookies/parsed_cookie_unittest.cc b/chromium/net/cookies/parsed_cookie_unittest.cc index 61a61677799..27efb99adfc 100644 --- a/chromium/net/cookies/parsed_cookie_unittest.cc +++ b/chromium/net/cookies/parsed_cookie_unittest.cc @@ -93,11 +93,11 @@ TEST(ParsedCookieTest, TestNameless) { TEST(ParsedCookieTest, TestAttributeCase) { ParsedCookie pc( - "BLAHHH; Path=/; sECuRe; httpONLY; first-PaRty-only; pRIoRitY=hIgH"); + "BLAHHH; Path=/; sECuRe; httpONLY; sAmESitE=StrIct; pRIoRitY=hIgH"); EXPECT_TRUE(pc.IsValid()); EXPECT_TRUE(pc.IsSecure()); EXPECT_TRUE(pc.IsHttpOnly()); - EXPECT_TRUE(pc.IsFirstPartyOnly()); + EXPECT_EQ(CookieSameSite::STRICT_MODE, pc.SameSite()); EXPECT_TRUE(pc.HasPath()); EXPECT_EQ("/", pc.Path()); EXPECT_EQ("", pc.Name()); @@ -148,7 +148,7 @@ TEST(ParsedCookieTest, MissingValue) { } TEST(ParsedCookieTest, Whitespace) { - ParsedCookie pc(" A = BC ;secure;;; first-party-only "); + ParsedCookie pc(" A = BC ;secure;;; samesite = lax "); EXPECT_TRUE(pc.IsValid()); EXPECT_EQ("A", pc.Name()); EXPECT_EQ("BC", pc.Value()); @@ -156,7 +156,7 @@ TEST(ParsedCookieTest, Whitespace) { EXPECT_FALSE(pc.HasDomain()); EXPECT_TRUE(pc.IsSecure()); EXPECT_FALSE(pc.IsHttpOnly()); - EXPECT_TRUE(pc.IsFirstPartyOnly()); + EXPECT_EQ(CookieSameSite::LAX_MODE, pc.SameSite()); EXPECT_EQ(COOKIE_PRIORITY_DEFAULT, pc.Priority()); // We parse anything between ; as attributes, so we end up with two // attributes with an empty string name and value. @@ -171,7 +171,7 @@ TEST(ParsedCookieTest, MultipleEquals) { EXPECT_FALSE(pc.HasDomain()); EXPECT_TRUE(pc.IsSecure()); EXPECT_TRUE(pc.IsHttpOnly()); - EXPECT_FALSE(pc.IsFirstPartyOnly()); + EXPECT_EQ(CookieSameSite::DEFAULT_MODE, pc.SameSite()); EXPECT_EQ(COOKIE_PRIORITY_DEFAULT, pc.Priority()); EXPECT_EQ(4U, pc.NumberOfAttributes()); } @@ -357,12 +357,12 @@ TEST(ParsedCookieTest, SetAttributes) { EXPECT_TRUE(pc.SetIsSecure(true)); EXPECT_TRUE(pc.SetIsHttpOnly(true)); EXPECT_TRUE(pc.SetIsHttpOnly(true)); - EXPECT_TRUE(pc.SetIsFirstPartyOnly(true)); + EXPECT_TRUE(pc.SetSameSite("LAX")); EXPECT_TRUE(pc.SetPriority("HIGH")); EXPECT_EQ( "name=value; domain=domain.com; path=/; " "expires=Sun, 18-Apr-2027 21:06:29 GMT; max-age=12345; secure; " - "httponly; first-party-only; priority=HIGH", + "httponly; samesite=LAX; priority=HIGH", pc.ToCookieLine()); EXPECT_TRUE(pc.HasDomain()); EXPECT_TRUE(pc.HasPath()); @@ -370,7 +370,7 @@ TEST(ParsedCookieTest, SetAttributes) { EXPECT_TRUE(pc.HasMaxAge()); EXPECT_TRUE(pc.IsSecure()); EXPECT_TRUE(pc.IsHttpOnly()); - EXPECT_TRUE(pc.IsFirstPartyOnly()); + EXPECT_EQ(CookieSameSite::LAX_MODE, pc.SameSite()); EXPECT_EQ(COOKIE_PRIORITY_HIGH, pc.Priority()); // Clear one attribute from the middle. @@ -383,7 +383,7 @@ TEST(ParsedCookieTest, SetAttributes) { EXPECT_EQ( "name=value; domain=domain.com; path=/foo; " "expires=Sun, 18-Apr-2027 21:06:29 GMT; max-age=12345; secure; " - "httponly; first-party-only; priority=HIGH", + "httponly; samesite=LAX; priority=HIGH", pc.ToCookieLine()); // Set priority to medium. @@ -391,7 +391,7 @@ TEST(ParsedCookieTest, SetAttributes) { EXPECT_EQ( "name=value; domain=domain.com; path=/foo; " "expires=Sun, 18-Apr-2027 21:06:29 GMT; max-age=12345; secure; " - "httponly; first-party-only; priority=medium", + "httponly; samesite=LAX; priority=medium", pc.ToCookieLine()); // Clear the rest and change the name and value. @@ -401,7 +401,7 @@ TEST(ParsedCookieTest, SetAttributes) { EXPECT_TRUE(pc.SetMaxAge(std::string())); EXPECT_TRUE(pc.SetIsSecure(false)); EXPECT_TRUE(pc.SetIsHttpOnly(false)); - EXPECT_TRUE(pc.SetIsFirstPartyOnly(false)); + EXPECT_TRUE(pc.SetSameSite(std::string())); EXPECT_TRUE(pc.SetName("name2")); EXPECT_TRUE(pc.SetValue("value2")); EXPECT_TRUE(pc.SetPriority(std::string())); @@ -411,7 +411,7 @@ TEST(ParsedCookieTest, SetAttributes) { EXPECT_FALSE(pc.HasMaxAge()); EXPECT_FALSE(pc.IsSecure()); EXPECT_FALSE(pc.IsHttpOnly()); - EXPECT_FALSE(pc.IsFirstPartyOnly()); + EXPECT_EQ(CookieSameSite::NO_RESTRICTION, pc.SameSite()); EXPECT_EQ("name2=value2", pc.ToCookieLine()); } @@ -449,6 +449,56 @@ TEST(ParsedCookieTest, SetPriority) { EXPECT_EQ(COOKIE_PRIORITY_DEFAULT, pc.Priority()); } +TEST(ParsedCookieTest, SetSameSite) { + ParsedCookie pc("name=value"); + EXPECT_TRUE(pc.IsValid()); + + EXPECT_EQ("name=value", pc.ToCookieLine()); + EXPECT_EQ(CookieSameSite::DEFAULT_MODE, pc.SameSite()); + + // Test each priority, expect case-insensitive compare. + EXPECT_TRUE(pc.SetSameSite("strict")); + EXPECT_EQ("name=value; samesite=strict", pc.ToCookieLine()); + EXPECT_EQ(CookieSameSite::STRICT_MODE, pc.SameSite()); + EXPECT_TRUE(pc.IsValid()); + + EXPECT_TRUE(pc.SetSameSite("lAx")); + EXPECT_EQ("name=value; samesite=lAx", pc.ToCookieLine()); + EXPECT_EQ(CookieSameSite::LAX_MODE, pc.SameSite()); + EXPECT_TRUE(pc.IsValid()); + + EXPECT_TRUE(pc.SetSameSite("LAX")); + EXPECT_EQ("name=value; samesite=LAX", pc.ToCookieLine()); + EXPECT_EQ(CookieSameSite::LAX_MODE, pc.SameSite()); + EXPECT_TRUE(pc.IsValid()); + + EXPECT_TRUE(pc.SetSameSite("")); + EXPECT_EQ("name=value", pc.ToCookieLine()); + EXPECT_EQ(CookieSameSite::DEFAULT_MODE, pc.SameSite()); + EXPECT_TRUE(pc.IsValid()); + + EXPECT_TRUE(pc.SetSameSite("Blah")); + EXPECT_FALSE(pc.IsValid()); +} + +TEST(ParsedCookieTest, InvalidSameSiteValue) { + struct TestCase { + const char* cookie; + bool valid; + CookieSameSite mode; + } cases[]{{"n=v; samesite=strict", true, CookieSameSite::STRICT_MODE}, + {"n=v; samesite=lax", true, CookieSameSite::LAX_MODE}, + {"n=v; samesite=boo", false, CookieSameSite::DEFAULT_MODE}, + {"n=v; samesite", false, CookieSameSite::DEFAULT_MODE}}; + + for (const auto& test : cases) { + SCOPED_TRACE(test.cookie); + ParsedCookie pc(test.cookie); + EXPECT_EQ(test.valid, pc.IsValid()); + EXPECT_EQ(test.mode, pc.SameSite()); + } +} + TEST(ParsedCookieTest, InvalidNonAlphanumericChars) { ParsedCookie pc1("name=\x05"); ParsedCookie pc2( |