summaryrefslogtreecommitdiff
path: root/chromium/net/cookies
diff options
context:
space:
mode:
authorAllan Sandfeld Jensen <allan.jensen@qt.io>2022-02-02 12:21:57 +0100
committerAllan Sandfeld Jensen <allan.jensen@qt.io>2022-02-12 08:13:00 +0000
commit606d85f2a5386472314d39923da28c70c60dc8e7 (patch)
treea8f4d7bf997f349f45605e6058259fba0630e4d7 /chromium/net/cookies
parent5786336dda477d04fb98483dca1a5426eebde2d7 (diff)
downloadqtwebengine-chromium-606d85f2a5386472314d39923da28c70c60dc8e7.tar.gz
BASELINE: Update Chromium to 96.0.4664.181
Change-Id: I762cd1da89d73aa6313b4a753fe126c34833f046 Reviewed-by: Allan Sandfeld Jensen <allan.jensen@qt.io>
Diffstat (limited to 'chromium/net/cookies')
-rw-r--r--chromium/net/cookies/COMMON_METADATA3
-rw-r--r--chromium/net/cookies/DIR_METADATA4
-rw-r--r--chromium/net/cookies/canonical_cookie.cc141
-rw-r--r--chromium/net/cookies/canonical_cookie.h48
-rw-r--r--chromium/net/cookies/canonical_cookie_fuzzer.cc12
-rw-r--r--chromium/net/cookies/canonical_cookie_unittest.cc558
-rw-r--r--chromium/net/cookies/cookie_access_delegate.h7
-rw-r--r--chromium/net/cookies/cookie_access_result.h8
-rw-r--r--chromium/net/cookies/cookie_change_dispatcher.h35
-rw-r--r--chromium/net/cookies/cookie_constants.cc5
-rw-r--r--chromium/net/cookies/cookie_constants.h17
-rw-r--r--chromium/net/cookies/cookie_inclusion_status.cc83
-rw-r--r--chromium/net/cookies/cookie_inclusion_status.h47
-rw-r--r--chromium/net/cookies/cookie_inclusion_status_unittest.cc76
-rw-r--r--chromium/net/cookies/cookie_monster.cc583
-rw-r--r--chromium/net/cookies/cookie_monster.h135
-rw-r--r--chromium/net/cookies/cookie_monster_change_dispatcher.cc17
-rw-r--r--chromium/net/cookies/cookie_monster_change_dispatcher.h17
-rw-r--r--chromium/net/cookies/cookie_monster_perftest.cc2
-rw-r--r--chromium/net/cookies/cookie_monster_store_test.cc14
-rw-r--r--chromium/net/cookies/cookie_monster_store_test.h6
-rw-r--r--chromium/net/cookies/cookie_monster_unittest.cc625
-rw-r--r--chromium/net/cookies/cookie_partition_key.cc8
-rw-r--r--chromium/net/cookies/cookie_partition_key.h27
-rw-r--r--chromium/net/cookies/cookie_partition_key_fuzzer.cc29
-rw-r--r--chromium/net/cookies/cookie_partition_key_unittest.cc38
-rw-r--r--chromium/net/cookies/cookie_partition_keychain.cc37
-rw-r--r--chromium/net/cookies/cookie_partition_keychain.h85
-rw-r--r--chromium/net/cookies/cookie_partition_keychain_unittest.cc75
-rw-r--r--chromium/net/cookies/cookie_store.cc4
-rw-r--r--chromium/net/cookies/cookie_store.h15
-rw-r--r--chromium/net/cookies/cookie_store_change_unittest.h241
-rw-r--r--chromium/net/cookies/cookie_store_test_helpers.cc9
-rw-r--r--chromium/net/cookies/cookie_store_test_helpers.h24
-rw-r--r--chromium/net/cookies/cookie_store_unittest.h112
-rw-r--r--chromium/net/cookies/cookie_util.cc2
-rw-r--r--chromium/net/cookies/cookie_util_unittest.cc3
-rw-r--r--chromium/net/cookies/parse_cookie_line_fuzzer.cc55
-rw-r--r--chromium/net/cookies/parsed_cookie.cc369
-rw-r--r--chromium/net/cookies/parsed_cookie.h71
-rw-r--r--chromium/net/cookies/parsed_cookie_unittest.cc584
-rw-r--r--chromium/net/cookies/site_for_cookies.cc20
-rw-r--r--chromium/net/cookies/site_for_cookies.h5
-rw-r--r--chromium/net/cookies/site_for_cookies_unittest.cc26
-rw-r--r--chromium/net/cookies/static_cookie_policy.h5
-rw-r--r--chromium/net/cookies/test_cookie_access_delegate.h6
46 files changed, 3454 insertions, 839 deletions
diff --git a/chromium/net/cookies/COMMON_METADATA b/chromium/net/cookies/COMMON_METADATA
new file mode 100644
index 00000000000..191a115309d
--- /dev/null
+++ b/chromium/net/cookies/COMMON_METADATA
@@ -0,0 +1,3 @@
+monorail {
+ component: "Internals>Network>Cookies"
+} \ No newline at end of file
diff --git a/chromium/net/cookies/DIR_METADATA b/chromium/net/cookies/DIR_METADATA
index 69f95ee3873..7499c3237e1 100644
--- a/chromium/net/cookies/DIR_METADATA
+++ b/chromium/net/cookies/DIR_METADATA
@@ -6,6 +6,4 @@
# For the schema of this file, see Metadata message:
# https://source.chromium.org/chromium/infra/infra/+/main:go/src/infra/tools/dirmd/proto/dir_metadata.proto
-monorail {
- component: "Internals>Network>Cookies"
-} \ No newline at end of file
+mixins: "//net/cookies/COMMON_METADATA"
diff --git a/chromium/net/cookies/canonical_cookie.cc b/chromium/net/cookies/canonical_cookie.cc
index e9d0122b6c2..e3720d750ae 100644
--- a/chromium/net/cookies/canonical_cookie.cc
+++ b/chromium/net/cookies/canonical_cookie.cc
@@ -71,7 +71,6 @@
#include "url/url_util.h"
using base::Time;
-using base::TimeDelta;
namespace net {
@@ -427,7 +426,7 @@ Time CanonicalCookie::CanonExpiration(const ParsedCookie& pc,
return Time::Min();
// "... Otherwise, let the expiry-time be the current date and time plus
// delta-seconds seconds."
- return current + TimeDelta::FromSeconds(max_age);
+ return current + base::Seconds(max_age);
} else {
// If the conversion wasn't perfect, but the best-effort conversion
// resulted in an overflow/underflow, use the min/max representable time.
@@ -474,9 +473,7 @@ std::unique_ptr<CanonicalCookie> CanonicalCookie::Create(
if (!parsed_cookie.IsValid()) {
DVLOG(net::cookie_util::kVlogSetCookies)
<< "WARNING: Couldn't parse cookie";
- // TODO(crbug.com/1228815): Apply more specific exclusion reasons.
- DCHECK(status->HasExclusionReason(
- CookieInclusionStatus::EXCLUDE_FAILURE_TO_STORE));
+ DCHECK(!status->IsInclude());
// Don't continue, because an invalid ParsedCookie doesn't have any
// attributes.
// TODO(chlily): Log metrics.
@@ -568,6 +565,10 @@ std::unique_ptr<CanonicalCookie> CanonicalCookie::Create(
UMA_HISTOGRAM_BOOLEAN("Cookie.ControlCharacterTruncation",
parsed_cookie.HasTruncatedNameOrValue());
+ UMA_HISTOGRAM_ENUMERATION(
+ "Cookie.TruncatingCharacterInCookieString",
+ parsed_cookie.GetTruncatingCharacterInCookieStringType());
+
return cc;
}
@@ -597,26 +598,56 @@ std::unique_ptr<CanonicalCookie> CanonicalCookie::CreateSanitizedCookie(
*status = CookieInclusionStatus();
// Validate consistency of passed arguments.
- if (ParsedCookie::ParseTokenString(name) != name ||
- !ParsedCookie::IsValidCookieAttributeValue(name)) {
+ if (ParsedCookie::ParseTokenString(name) != name) {
status->AddExclusionReason(
net::CookieInclusionStatus::EXCLUDE_FAILURE_TO_STORE);
- } else if (ParsedCookie::ParseValueString(value) != value ||
- !ParsedCookie::IsValidCookieAttributeValue(value)) {
+ } else if (ParsedCookie::ParseValueString(value) != value) {
status->AddExclusionReason(
net::CookieInclusionStatus::EXCLUDE_FAILURE_TO_STORE);
} else if (ParsedCookie::ParseValueString(path) != path) {
+ // NOTE: If `path` contains "terminating characters" ('\r', '\n', and
+ // '\0'), ';', or leading / trailing whitespace, path will be rejected,
+ // but any other control characters will just get URL-encoded below.
status->AddExclusionReason(
net::CookieInclusionStatus::EXCLUDE_FAILURE_TO_STORE);
- } else if (name.empty() && value.empty()) {
+ }
+
+ if (base::FeatureList::IsEnabled(features::kExtraCookieValidityChecks)) {
+ // Validate name and value against character set and size limit constraints.
+ // If IsValidCookieNameValuePair identifies that `name` and/or `value` are
+ // invalid, it will add an ExclusionReason to `status`.
+ ParsedCookie::IsValidCookieNameValuePair(name, value, status);
+
+ } else if (!ParsedCookie::IsValidCookieAttributeValueLegacy(name) ||
+ !ParsedCookie::IsValidCookieAttributeValueLegacy(value) ||
+ (name.empty() && value.empty())) {
status->AddExclusionReason(
net::CookieInclusionStatus::EXCLUDE_FAILURE_TO_STORE);
}
- if (ParsedCookie::ParseValueString(domain) != domain) {
+ // Validate domain against character set and size limit constraints.
+ bool domain_is_valid = true;
+
+ if ((ParsedCookie::ParseValueString(domain) != domain)) {
status->AddExclusionReason(
net::CookieInclusionStatus::EXCLUDE_INVALID_DOMAIN);
+ domain_is_valid = false;
+ }
+
+ if (base::FeatureList::IsEnabled(features::kExtraCookieValidityChecks)) {
+ if (!ParsedCookie::CookieAttributeValueHasValidCharSet(domain)) {
+ status->AddExclusionReason(
+ net::CookieInclusionStatus::EXCLUDE_INVALID_DOMAIN);
+ domain_is_valid = false;
+ }
+ if (!ParsedCookie::CookieAttributeValueHasValidSize(domain)) {
+ status->AddExclusionReason(
+ net::CookieInclusionStatus::EXCLUDE_ATTRIBUTE_VALUE_EXCEEDS_MAX_SIZE);
+ domain_is_valid = false;
+ }
}
+ const std::string& domain_attribute =
+ domain_is_valid ? domain : std::string();
std::string cookie_domain;
// This validation step must happen before GetCookieDomainWithString, so it
@@ -624,7 +655,7 @@ std::unique_ptr<CanonicalCookie> CanonicalCookie::CreateSanitizedCookie(
if (!cookie_util::DomainIsHostOnly(url.host())) {
status->AddExclusionReason(
net::CookieInclusionStatus::EXCLUDE_INVALID_DOMAIN);
- } else if (!cookie_util::GetCookieDomainWithString(url, domain,
+ } else if (!cookie_util::GetCookieDomainWithString(url, domain_attribute,
&cookie_domain)) {
status->AddExclusionReason(
net::CookieInclusionStatus::EXCLUDE_INVALID_DOMAIN);
@@ -646,13 +677,35 @@ std::unique_ptr<CanonicalCookie> CanonicalCookie::CreateSanitizedCookie(
int source_port = url.EffectiveIntPort();
std::string cookie_path = CanonicalCookie::CanonPathWithString(url, path);
- if (!path.empty() && cookie_path != path) {
- status->AddExclusionReason(
- net::CookieInclusionStatus::EXCLUDE_FAILURE_TO_STORE);
+ // Canonicalize path again to make sure it escapes characters as needed.
+ url::Component path_component(0, cookie_path.length());
+ url::RawCanonOutputT<char> canon_path;
+ url::Component canon_path_component;
+ url::CanonicalizePath(cookie_path.data(), path_component, &canon_path,
+ &canon_path_component);
+ std::string encoded_cookie_path = std::string(
+ canon_path.data() + canon_path_component.begin, canon_path_component.len);
+
+ if (!path.empty()) {
+ if (cookie_path != path) {
+ // The path attribute was specified and found to be invalid, so record an
+ // error.
+ status->AddExclusionReason(
+ net::CookieInclusionStatus::EXCLUDE_FAILURE_TO_STORE);
+ } else if (base::FeatureList::IsEnabled(
+ features::kExtraCookieValidityChecks) &&
+ !ParsedCookie::CookieAttributeValueHasValidSize(
+ encoded_cookie_path)) {
+ // The path attribute was specified and encodes into a value that's longer
+ // than the length limit, so record an error.
+ status->AddExclusionReason(
+ net::CookieInclusionStatus::EXCLUDE_ATTRIBUTE_VALUE_EXCEEDS_MAX_SIZE);
+ }
}
CookiePrefix prefix = GetCookiePrefix(name);
- if (!IsCookiePrefixValid(prefix, url, secure, domain, cookie_path)) {
+ if (!IsCookiePrefixValid(prefix, url, secure, domain_attribute,
+ cookie_path)) {
status->AddExclusionReason(
net::CookieInclusionStatus::EXCLUDE_INVALID_PREFIX);
}
@@ -675,19 +728,10 @@ std::unique_ptr<CanonicalCookie> CanonicalCookie::CreateSanitizedCookie(
if (!status->IsInclude())
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;
- url::Component canon_path_component;
- url::CanonicalizePath(cookie_path.data(), path_component, &canon_path,
- &canon_path_component);
- cookie_path = std::string(canon_path.data() + canon_path_component.begin,
- canon_path_component.len);
-
std::unique_ptr<CanonicalCookie> cc = base::WrapUnique(new CanonicalCookie(
- name, value, cookie_domain, cookie_path, creation_time, expiration_time,
- last_access_time, secure, http_only, same_site, priority, same_party,
- partition_key, source_scheme, source_port));
+ name, value, cookie_domain, encoded_cookie_path, creation_time,
+ expiration_time, last_access_time, secure, http_only, same_site, priority,
+ same_party, partition_key, source_scheme, source_port));
DCHECK(cc->IsCanonical());
return cc;
@@ -714,8 +758,17 @@ std::unique_ptr<CanonicalCookie> CanonicalCookie::FromStorage(
std::move(name), std::move(value), std::move(domain), std::move(path),
creation, expiration, last_access, secure, httponly, same_site, priority,
same_party, partition_key, source_scheme, source_port));
- if (!cc->IsCanonical())
+
+ if (cc->IsCanonical()) {
+ // This will help capture the number of times a cookie is canonical but does
+ // not have a valid name+value size length
+ bool valid_cookie_name_value_pair =
+ ParsedCookie::IsValidCookieNameValuePair(cc->Name(), cc->Value());
+ UMA_HISTOGRAM_BOOLEAN("Cookie.FromStorageWithValidLength",
+ valid_cookie_name_value_pair);
+ } else {
return nullptr;
+ }
return cc;
}
@@ -758,6 +811,9 @@ void CanonicalCookie::SetSourcePort(int port) {
bool CanonicalCookie::IsEquivalentForSecureCookieMatching(
const CanonicalCookie& secure_cookie) const {
+ // Partition keys must both be equivalent.
+ bool same_partition_key = PartitionKey() == secure_cookie.PartitionKey();
+
// Names must be the same
bool same_name = name_ == secure_cookie.Name();
@@ -772,7 +828,7 @@ bool CanonicalCookie::IsEquivalentForSecureCookieMatching(
bool path_match = secure_cookie.IsOnPath(Path());
bool equivalent_for_secure_cookie_matching =
- same_name && domain_match && path_match;
+ same_partition_key && same_name && domain_match && path_match;
// IsEquivalent() is a stricter check than this.
DCHECK(!IsEquivalent(secure_cookie) || equivalent_for_secure_cookie_matching);
@@ -1273,14 +1329,31 @@ bool CanonicalCookie::PartialCompare(const CanonicalCookie& other) const {
bool CanonicalCookie::IsCanonical() const {
// Not checking domain or path against ParsedCookie as it may have
- // come purely from the URL.
+ // come purely from the URL. Also, don't call IsValidCookieNameValuePair()
+ // here because we don't want to enforce the size checks on names or values
+ // that may have been reconstituted from the cookie store.
+ // TODO(crbug.com/1244172) Eventually we should check the size of name+value,
+ // assuming we collect metrics and determine that a low percentage of cookies
+ // would fail this check. Note that we still don't want to enforce length
+ // checks on domain or path for the reason stated above.
+
if (ParsedCookie::ParseTokenString(name_) != name_ ||
- !ParsedCookie::ValueMatchesParsedValue(value_) ||
- !ParsedCookie::IsValidCookieAttributeValue(name_) ||
- !ParsedCookie::IsValidCookieAttributeValue(value_)) {
+ !ParsedCookie::ValueMatchesParsedValue(value_)) {
return false;
}
+ if (base::FeatureList::IsEnabled(features::kExtraCookieValidityChecks)) {
+ if (!ParsedCookie::IsValidCookieName(name_) ||
+ !ParsedCookie::IsValidCookieValue(value_)) {
+ return false;
+ }
+ } else {
+ if (!ParsedCookie::IsValidCookieAttributeValueLegacy(name_) ||
+ !ParsedCookie::IsValidCookieAttributeValueLegacy(value_)) {
+ return false;
+ }
+ }
+
if (!last_access_date_.is_null() && creation_date_.is_null())
return false;
diff --git a/chromium/net/cookies/canonical_cookie.h b/chromium/net/cookies/canonical_cookie.h
index a8dd4c7a704..a67345da239 100644
--- a/chromium/net/cookies/canonical_cookie.h
+++ b/chromium/net/cookies/canonical_cookie.h
@@ -167,6 +167,12 @@ class NET_EXPORT CanonicalCookie {
CookieSourceScheme scheme_secure = CookieSourceScheme::kUnset,
int source_port = url::PORT_UNSPECIFIED);
+ bool operator<(const CanonicalCookie& other) const {
+ // Use the cookie properties that uniquely identify a cookie to determine
+ // ordering.
+ return UniqueKey() < other.UniqueKey();
+ }
+
const std::string& Name() const { return name_; }
const std::string& Value() const { return value_; }
// We represent the cookie's host-only-flag as the absence of a leading dot in
@@ -189,24 +195,6 @@ class NET_EXPORT CanonicalCookie {
return partition_key_;
}
- // Methods for serializing and deserializing a partition key to/from a string.
- // This will be used for Android, storing persistent partitioned cookies, and
- // loading partitioned cookies into Java code.
- //
- // This function returns if the partition key is not opaque. We do not want
- // to serialize cookies with opaque origins in their partition key to disk,
- // because if the browser session ends we will not be able to attach the
- // saved cookie to any future requests. This is because opaque origins' nonces
- // are only stored in volatile memory.
- bool SerializePartitionKey(std::string& out) const WARN_UNUSED_RESULT;
- // Deserializes the result of the method above.
- // If the result is abls::nullopt, the resulting cookie is not partitioned.
- //
- // Returns if the resulting partition key is valid.
- static bool DeserializePartitionKey(const std::string& in,
- absl::optional<SchemefulSite>& out)
- WARN_UNUSED_RESULT;
-
// Returns an enum indicating the scheme of the origin that
// set this cookie. This is not part of the cookie spec but is being used to
// collect metrics for a potential change to the cookie spec
@@ -245,10 +233,10 @@ class NET_EXPORT CanonicalCookie {
// Returns a key such that two cookies with the same UniqueKey() are
// guaranteed to be equivalent in the sense of IsEquivalent().
+ // The `partition_key_` field will always be nullopt when partitioned cookies
+ // are not enabled.
UniqueCookieKey UniqueKey() const {
- if (base::FeatureList::IsEnabled(features::kPartitionedCookies))
- return std::make_tuple(partition_key_, name_, domain_, path_);
- return std::make_tuple(absl::nullopt, name_, domain_, path_);
+ return std::make_tuple(partition_key_, name_, domain_, path_);
}
// Checks a looser set of equivalency rules than 'IsEquivalent()' in order
@@ -260,6 +248,8 @@ class NET_EXPORT CanonicalCookie {
// Returns 'true' if this cookie's name matches |secure_cookie|, and this
// cookie is a domain-match for |secure_cookie| (or vice versa), and
// |secure_cookie|'s path is "on" this cookie's path (as per 'IsOnPath()').
+ // If partitioned cookies are enabled, it also checks that the cookie has
+ // the same partition key as |secure_cookie|.
//
// Note that while the domain-match cuts both ways (e.g. 'example.com'
// matches 'www.example.com' in either direction), the path-match is
@@ -287,6 +277,19 @@ class NET_EXPORT CanonicalCookie {
bool IsEquivalentForSecureCookieMatching(
const CanonicalCookie& secure_cookie) const;
+ // Returns true if the |other| cookie's data members (instance variables)
+ // match, for comparing cookies in colletions.
+ bool HasEquivalentDataMembers(const CanonicalCookie& other) const {
+ return creation_date_ == other.creation_date_ &&
+ last_access_date_ == other.last_access_date_ &&
+ expiry_date_ == other.expiry_date_ && secure_ == other.secure_ &&
+ httponly_ == other.httponly_ && same_site_ == other.same_site_ &&
+ priority_ == other.priority_ && same_party_ == other.same_party_ &&
+ partition_key_ == other.partition_key_ && name_ == other.name_ &&
+ value_ == other.value_ && domain_ == other.domain_ &&
+ path_ == other.path_;
+ }
+
void SetSourceScheme(CookieSourceScheme source_scheme) {
source_scheme_ = source_scheme;
}
@@ -345,6 +348,9 @@ class NET_EXPORT CanonicalCookie {
std::string DebugString() const;
+ // Returns the canonical path based on the specified url and path attribute
+ // value. Note that this method does not enforce character set or size
+ // checks on `path_string`.
static std::string CanonPathWithString(const GURL& url,
const std::string& path_string);
diff --git a/chromium/net/cookies/canonical_cookie_fuzzer.cc b/chromium/net/cookies/canonical_cookie_fuzzer.cc
index aa3bb25fa0f..558cd88e2aa 100644
--- a/chromium/net/cookies/canonical_cookie_fuzzer.cc
+++ b/chromium/net/cookies/canonical_cookie_fuzzer.cc
@@ -25,10 +25,14 @@ const base::Time getRandomTime(FuzzedDataProvider* data_provider) {
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
FuzzedDataProvider data_provider(data, size);
- const std::string name = data_provider.ConsumeRandomLengthString(800);
- const std::string value = data_provider.ConsumeRandomLengthString(800);
- const std::string domain = data_provider.ConsumeRandomLengthString(800);
- const std::string path = data_provider.ConsumeRandomLengthString(800);
+ const std::string name = data_provider.ConsumeRandomLengthString(
+ net::ParsedCookie::kMaxCookieNamePlusValueSize + 10);
+ const std::string value = data_provider.ConsumeRandomLengthString(
+ net::ParsedCookie::kMaxCookieNamePlusValueSize + 10);
+ const std::string domain = data_provider.ConsumeRandomLengthString(
+ net::ParsedCookie::kMaxCookieAttributeValueSize + 10);
+ const std::string path = data_provider.ConsumeRandomLengthString(
+ net::ParsedCookie::kMaxCookieAttributeValueSize + 10);
const GURL url(data_provider.ConsumeRandomLengthString(800));
if (!url.is_valid())
diff --git a/chromium/net/cookies/canonical_cookie_unittest.cc b/chromium/net/cookies/canonical_cookie_unittest.cc
index 55d49f245f1..d2e84a10bad 100644
--- a/chromium/net/cookies/canonical_cookie_unittest.cc
+++ b/chromium/net/cookies/canonical_cookie_unittest.cc
@@ -12,6 +12,7 @@
#include "net/cookies/cookie_constants.h"
#include "net/cookies/cookie_inclusion_status.h"
#include "net/cookies/cookie_options.h"
+#include "net/cookies/parsed_cookie.h"
#include "testing/gmock/include/gmock/gmock-matchers.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
@@ -130,7 +131,7 @@ TEST(CanonicalCookieTest, Constructor) {
EXPECT_EQ(cookie6->SourcePort(), url::PORT_INVALID);
}
-TEST(CanonicalCookie, CreationCornerCases) {
+TEST(CanonicalCookieTest, CreationCornerCases) {
base::Time creation_time = base::Time::Now();
std::unique_ptr<CanonicalCookie> cookie;
absl::optional<base::Time> server_time = absl::nullopt;
@@ -162,6 +163,23 @@ TEST(CanonicalCookie, CreationCornerCases) {
EXPECT_FALSE(cookie.get());
EXPECT_TRUE(status.HasExclusionReason(
CookieInclusionStatus::ExclusionReason::EXCLUDE_FAILURE_TO_STORE));
+
+ // The ParsedCookie constructor unit tests cover many edge cases related to
+ // invalid sizes when parsing a cookie line, and since CanonicalCookie::Create
+ // creates a ParsedCookie immediately, there's no point in replicating all
+ // of those tests here. We should test that the corresponding ExclusionReason
+ // gets passed back correctly, though.
+ std::string too_long_value(ParsedCookie::kMaxCookieNamePlusValueSize + 1,
+ 'a');
+
+ cookie = CanonicalCookie::Create(GURL("http://www.example.com/test/foo.html"),
+ too_long_value, creation_time, server_time,
+ absl::nullopt /* cookie_partition_key */,
+ &status);
+ EXPECT_FALSE(cookie.get());
+ EXPECT_TRUE(
+ status.HasExclusionReason(CookieInclusionStatus::ExclusionReason::
+ EXCLUDE_NAME_VALUE_PAIR_EXCEEDS_MAX_SIZE));
}
TEST(CanonicalCookieTest, Create) {
@@ -503,8 +521,7 @@ TEST(CanonicalCookieTest, CreateWithMaxAge) {
EXPECT_TRUE(cookie.get());
EXPECT_TRUE(cookie->IsPersistent());
EXPECT_FALSE(cookie->IsExpired(creation_time));
- EXPECT_EQ(base::TimeDelta::FromSeconds(60) + creation_time,
- cookie->ExpiryDate());
+ EXPECT_EQ(base::Seconds(60) + creation_time, cookie->ExpiryDate());
// Max-age with expires (max-age should take precedence).
cookie = CanonicalCookie::Create(
@@ -513,8 +530,7 @@ TEST(CanonicalCookieTest, CreateWithMaxAge) {
EXPECT_TRUE(cookie.get());
EXPECT_TRUE(cookie->IsPersistent());
EXPECT_FALSE(cookie->IsExpired(creation_time));
- EXPECT_EQ(base::TimeDelta::FromSeconds(60) + creation_time,
- cookie->ExpiryDate());
+ EXPECT_EQ(base::Seconds(60) + creation_time, cookie->ExpiryDate());
// Max-age=0 should create an expired cookie with expiry equal to the earliest
// representable time.
@@ -543,8 +559,7 @@ TEST(CanonicalCookieTest, CreateWithMaxAge) {
EXPECT_TRUE(cookie.get());
EXPECT_TRUE(cookie->IsPersistent());
EXPECT_FALSE(cookie->IsExpired(creation_time));
- EXPECT_EQ(base::TimeDelta::FromSeconds(60) + creation_time,
- cookie->ExpiryDate());
+ EXPECT_EQ(base::Seconds(60) + creation_time, cookie->ExpiryDate());
// Max-age with non-integer should be ignored.
cookie = CanonicalCookie::Create(url, "A=1; max-age=abcd", creation_time,
@@ -597,7 +612,7 @@ TEST(CanonicalCookieTest, EmptyExpiry) {
EXPECT_EQ(base::Time(), cookie->ExpiryDate());
// With a stale server time
- server_time = creation_time - base::TimeDelta::FromHours(1);
+ server_time = creation_time - base::Hours(1);
cookie = CanonicalCookie::Create(url, cookie_line, creation_time, server_time,
absl::nullopt /* cookie_partition_key */);
EXPECT_TRUE(cookie.get());
@@ -606,7 +621,7 @@ TEST(CanonicalCookieTest, EmptyExpiry) {
EXPECT_EQ(base::Time(), cookie->ExpiryDate());
// With a future server time
- server_time = creation_time + base::TimeDelta::FromHours(1);
+ server_time = creation_time + base::Hours(1);
cookie = CanonicalCookie::Create(url, cookie_line, creation_time, server_time,
absl::nullopt /* cookie_partition_key */);
EXPECT_TRUE(cookie.get());
@@ -622,7 +637,7 @@ TEST(CanonicalCookieTest, IsEquivalent) {
std::string cookie_domain = ".www.example.com";
std::string cookie_path = "/path";
base::Time creation_time = base::Time::Now();
- base::Time expiration_time = creation_time + base::TimeDelta::FromDays(2);
+ base::Time expiration_time = creation_time + base::Days(2);
bool secure = false;
bool httponly = false;
CookieSameSite same_site = CookieSameSite::NO_RESTRICTION;
@@ -654,8 +669,7 @@ TEST(CanonicalCookieTest, IsEquivalent) {
EXPECT_TRUE(cookie->IsEquivalentForSecureCookieMatching(*other_cookie));
EXPECT_TRUE(other_cookie->IsEquivalentForSecureCookieMatching(*cookie));
- base::Time other_creation_time =
- creation_time + base::TimeDelta::FromMinutes(2);
+ base::Time other_creation_time = creation_time + base::Minutes(2);
other_cookie = CanonicalCookie::CreateUnsafeCookieForTesting(
cookie_name, "2", cookie_domain, cookie_path, other_creation_time,
expiration_time, base::Time(), secure, httponly, same_site,
@@ -749,77 +763,38 @@ TEST(CanonicalCookieTest, IsEquivalent) {
EXPECT_FALSE(cookie->IsEquivalent(*other_cookie));
EXPECT_TRUE(cookie->IsEquivalentForSecureCookieMatching(*other_cookie));
EXPECT_FALSE(other_cookie->IsEquivalentForSecureCookieMatching(*cookie));
-}
-class PartitionedCanonicalCookieTest : public testing::TestWithParam<bool> {
- protected:
- void SetUp() override {
- if (PartitionedCookiesEnabled())
- scoped_feature_list_.InitAndEnableFeature(features::kPartitionedCookies);
- testing::TestWithParam<bool>::SetUp();
- }
-
- bool PartitionedCookiesEnabled() { return GetParam(); }
-
- base::test::ScopedFeatureList scoped_feature_list_;
-};
-
-INSTANTIATE_TEST_SUITE_P(/* no label */,
- PartitionedCanonicalCookieTest,
- testing::Bool());
-
-TEST_P(PartitionedCanonicalCookieTest, IsEquivalent) {
- const std::string name = "__Host-foo";
- const std::string value = "bar";
- const std::string domain = "";
- const std::string path = "/";
- const base::Time creation_time = base::Time();
- const base::Time expiration_time = base::Time();
- const base::Time last_accessed_time = base::Time();
- const bool secure = true;
- const bool http_only = false;
- const CookieSameSite same_site = CookieSameSite::NO_RESTRICTION;
- const CookiePriority cookie_priority = COOKIE_PRIORITY_DEFAULT;
- const bool same_party = false;
-
- const absl::optional<CookiePartitionKey> cookie_partition_key =
+ // Partitioned cookies are not equivalent to unpartitioned cookies.
+ other_cookie = CanonicalCookie::CreateUnsafeCookieForTesting(
+ cookie_name, cookie_value, cookie_domain, cookie_path, creation_time,
+ expiration_time, base::Time(), secure, httponly, same_site,
+ COOKIE_PRIORITY_MEDIUM, same_party,
absl::make_optional(
- CookiePartitionKey::FromURLForTesting(GURL("https://foo.com")));
- const absl::optional<CookiePartitionKey> other_cookie_partition_key =
+ CookiePartitionKey::FromURLForTesting(GURL("https://foo.com"))));
+ EXPECT_FALSE(cookie->IsEquivalent(*other_cookie));
+ EXPECT_FALSE(cookie->IsEquivalentForSecureCookieMatching(*other_cookie));
+
+ // Partitioned cookies are equal if they have the same partition key.
+ auto paritioned_cookie = CanonicalCookie::CreateUnsafeCookieForTesting(
+ cookie_name, cookie_value, cookie_domain, cookie_path, creation_time,
+ expiration_time, base::Time(), secure, httponly, same_site,
+ COOKIE_PRIORITY_MEDIUM, same_party,
absl::make_optional(
- CookiePartitionKey::FromURLForTesting(GURL("https://bar.com")));
-
- auto unpartitioned_cookie = CanonicalCookie::CreateUnsafeCookieForTesting(
- name, value, domain, path, creation_time, expiration_time,
- last_accessed_time, secure, http_only, same_site, cookie_priority,
- same_party, absl::nullopt);
- auto partitioned_cookie = CanonicalCookie::CreateUnsafeCookieForTesting(
- name, value, domain, path, creation_time, expiration_time,
- last_accessed_time, secure, http_only, same_site, cookie_priority,
- same_party, cookie_partition_key);
-
- // Check that a cookie without a partition key is not equivalent to a cookie
- // with one when partitioned cookie are enabled. If they are disabled the
- // cookies should be equivalent.
- EXPECT_NE(PartitionedCookiesEnabled(),
- unpartitioned_cookie->IsEquivalent(*partitioned_cookie));
-
- // Check that cookies with the same partition key are equivalent. This should
- // be true regardless of the partitioned cookies setting.
- auto other_cookie = CanonicalCookie::CreateUnsafeCookieForTesting(
- name, value, domain, path, creation_time, expiration_time,
- last_accessed_time, secure, http_only, same_site, cookie_priority,
- same_party, cookie_partition_key);
- EXPECT_TRUE(partitioned_cookie->IsEquivalent(*other_cookie));
+ CookiePartitionKey::FromURLForTesting(GURL("https://foo.com"))));
+ EXPECT_TRUE(paritioned_cookie->IsEquivalent(*other_cookie));
+ EXPECT_TRUE(
+ paritioned_cookie->IsEquivalentForSecureCookieMatching(*other_cookie));
- // Check that cookies with different partition keys are not equivalent only
- // when partitioned cookies are enabled.
+ // Partitioned cookies with different partition keys are not equal
other_cookie = CanonicalCookie::CreateUnsafeCookieForTesting(
- name, value, domain, path, creation_time, expiration_time,
- last_accessed_time, secure, http_only, same_site, cookie_priority,
- same_party, other_cookie_partition_key);
- EXPECT_NE(PartitionedCookiesEnabled(),
- partitioned_cookie->IsEquivalent(*other_cookie));
+ cookie_name, cookie_value, cookie_domain, cookie_path, creation_time,
+ expiration_time, base::Time(), secure, httponly, same_site,
+ COOKIE_PRIORITY_MEDIUM, same_party,
+ absl::make_optional(
+ CookiePartitionKey::FromURLForTesting(GURL("https://bar.com"))));
+ EXPECT_FALSE(paritioned_cookie->IsEquivalent(*other_cookie));
+ EXPECT_FALSE(
+ paritioned_cookie->IsEquivalentForSecureCookieMatching(*other_cookie));
}
TEST(CanonicalCookieTest, IsEquivalentForSecureCookieMatching) {
@@ -828,6 +803,7 @@ TEST(CanonicalCookieTest, IsEquivalentForSecureCookieMatching) {
const char* name;
const char* domain;
const char* path;
+ absl::optional<CookiePartitionKey> cookie_partition_key = absl::nullopt;
} cookie, secure_cookie;
bool equivalent;
bool is_symmetric; // Whether the reverse comparison has the same result.
@@ -852,18 +828,46 @@ TEST(CanonicalCookieTest, IsEquivalentForSecureCookieMatching) {
// Different paths don't match
{{"A", "a.foo.com", "/sub"}, {"A", "a.foo.com", "/other"}, false, true},
{{"A", "a.foo.com", "/a/b"}, {"A", "a.foo.com", "/a/c"}, false, true},
+ // Partitioned cookies are not equivalent to unpartitioned cookies.
+ {{"A", ".a.foo.com", "/"},
+ {"A", ".a.foo.com", "/",
+ absl::make_optional(
+ CookiePartitionKey::FromURLForTesting(GURL("https://bar.com")))},
+ false,
+ true},
+ // Partitioned cookies are equivalent if they have the same partition key.
+ {{"A", "a.foo.com", "/",
+ absl::make_optional(
+ CookiePartitionKey::FromURLForTesting(GURL("https://bar.com")))},
+ {"A", "a.foo.com", "/",
+ absl::make_optional(
+ CookiePartitionKey::FromURLForTesting(GURL("https://bar.com")))},
+ true,
+ true},
+ // Partitioned cookies are *not* equivalent if they have the different
+ // partition keys.
+ {{"A", "a.foo.com", "/",
+ absl::make_optional(
+ CookiePartitionKey::FromURLForTesting(GURL("https://bar.com")))},
+ {"A", "a.foo.com", "/",
+ absl::make_optional(
+ CookiePartitionKey::FromURLForTesting(GURL("https://baz.com")))},
+ false,
+ true},
};
for (auto test : kTests) {
auto cookie = CanonicalCookie::CreateUnsafeCookieForTesting(
test.cookie.name, "value1", test.cookie.domain, test.cookie.path,
- base::Time(), base::Time(), base::Time(), false /* secure */, false,
- CookieSameSite::LAX_MODE, COOKIE_PRIORITY_MEDIUM, false);
+ base::Time(), base::Time(), base::Time(), false /* secure */,
+ false /* httponly */, CookieSameSite::LAX_MODE, COOKIE_PRIORITY_MEDIUM,
+ false /* sameparty */, test.cookie.cookie_partition_key);
auto secure_cookie = CanonicalCookie::CreateUnsafeCookieForTesting(
test.secure_cookie.name, "value2", test.secure_cookie.domain,
test.secure_cookie.path, base::Time(), base::Time(), base::Time(),
- true /* secure */, false, CookieSameSite::LAX_MODE,
- COOKIE_PRIORITY_MEDIUM, false);
+ true /* secure */, false /* httponly */, CookieSameSite::LAX_MODE,
+ COOKIE_PRIORITY_MEDIUM, false /* sameparty */,
+ test.secure_cookie.cookie_partition_key);
EXPECT_EQ(test.equivalent,
cookie->IsEquivalentForSecureCookieMatching(*secure_cookie));
@@ -2377,6 +2381,20 @@ TEST(CanonicalCookieTest, IsCanonical) {
COOKIE_PRIORITY_LOW, false)
->IsCanonical());
+ // "localhost" as domain.
+ EXPECT_TRUE(CanonicalCookie::CreateUnsafeCookieForTesting(
+ "A", "B", "localhost", "/path", base::Time(), base::Time(),
+ base::Time(), false, false, CookieSameSite::NO_RESTRICTION,
+ COOKIE_PRIORITY_LOW, false)
+ ->IsCanonical());
+
+ // Localhost IPv4 address as domain.
+ EXPECT_TRUE(CanonicalCookie::CreateUnsafeCookieForTesting(
+ "A", "B", "127.0.0.1", "/path", base::Time(), base::Time(),
+ base::Time(), false, false, CookieSameSite::NO_RESTRICTION,
+ COOKIE_PRIORITY_LOW, false)
+ ->IsCanonical());
+
// Simple IPv4 address as domain.
EXPECT_TRUE(CanonicalCookie::CreateUnsafeCookieForTesting(
"A", "B", "1.2.3.4", "/path", base::Time(), base::Time(),
@@ -2384,7 +2402,28 @@ TEST(CanonicalCookieTest, IsCanonical) {
COOKIE_PRIORITY_LOW, false)
->IsCanonical());
- // NOn-canonical IPv4 address as domain.
+ // period-prefixed IPv4 address as domain.
+ EXPECT_FALSE(CanonicalCookie::CreateUnsafeCookieForTesting(
+ "A", "B", ".1.3.2.4", "/path", base::Time(), base::Time(),
+ base::Time(), false, false, CookieSameSite::NO_RESTRICTION,
+ COOKIE_PRIORITY_LOW, false)
+ ->IsCanonical());
+
+ // period-prefixed truncated IPv4 address as domain.
+ EXPECT_FALSE(CanonicalCookie::CreateUnsafeCookieForTesting(
+ "A", "B", ".3.2.4", "/path", base::Time(), base::Time(),
+ base::Time(), true, false, CookieSameSite::NO_RESTRICTION,
+ COOKIE_PRIORITY_LOW, false)
+ ->IsCanonical());
+
+ // truncated IPv4 address as domain.
+ EXPECT_FALSE(CanonicalCookie::CreateUnsafeCookieForTesting(
+ "A", "B", "3.2.4", "/path", base::Time(), base::Time(),
+ base::Time(), true, false, CookieSameSite::NO_RESTRICTION,
+ COOKIE_PRIORITY_LOW, false)
+ ->IsCanonical());
+
+ // Non-canonical IPv4 address as domain.
EXPECT_FALSE(CanonicalCookie::CreateUnsafeCookieForTesting(
"A", "B", "01.2.03.4", "/path", base::Time(), base::Time(),
base::Time(), false, false, CookieSameSite::NO_RESTRICTION,
@@ -2436,6 +2475,20 @@ TEST(CanonicalCookieTest, IsCanonical) {
CookieSameSite::NO_RESTRICTION, COOKIE_PRIORITY_LOW, false)
->IsCanonical());
+ // Lowercased hex IPv6 address as domain for domain cookie.
+ EXPECT_FALSE(CanonicalCookie::CreateUnsafeCookieForTesting(
+ "A", "B", ".[2001:db8:ac10:fe01::]", "/path", base::Time(),
+ base::Time(), base::Time(), false, false,
+ CookieSameSite::NO_RESTRICTION, COOKIE_PRIORITY_LOW, false)
+ ->IsCanonical());
+
+ // Incomplete lowercased hex IPv6 address as domain.
+ EXPECT_FALSE(CanonicalCookie::CreateUnsafeCookieForTesting(
+ "A", "B", "[2001:db8:ac10:fe01:]", "/path", base::Time(),
+ base::Time(), base::Time(), false, false,
+ CookieSameSite::NO_RESTRICTION, COOKIE_PRIORITY_LOW, false)
+ ->IsCanonical());
+
// Properly formatted host cookie.
EXPECT_TRUE(CanonicalCookie::CreateUnsafeCookieForTesting(
"__Host-A", "B", "x.y", "/", base::Time(), base::Time(),
@@ -2630,23 +2683,28 @@ TEST(CanonicalCookieTest, BuildCookieLine) {
url, "D=E", now, server_time, absl::nullopt /* cookie_partition_key */));
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), server_time,
- absl::nullopt /* cookie_partition_key */));
+ cookies.push_back(
+ CanonicalCookie::Create(url, "F=G", now - base::Seconds(1), server_time,
+ absl::nullopt /* cookie_partition_key */));
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), server_time,
- absl::nullopt /* cookie_partition_key */));
+ cookies.push_back(
+ CanonicalCookie::Create(url, "D=E", now - base::Seconds(2), server_time,
+ absl::nullopt /* cookie_partition_key */));
MatchCookieLineToVector("A=B; C; D=E; F=G; D=E", cookies);
+ // BuildCookieLine should match the spec in the case of an empty name with a
+ // value containing an equal sign (even if it currently produces "invalid"
+ // cookie lines).
+ cookies.push_back(CanonicalCookie::Create(
+ url, "=H=I", now, server_time, absl::nullopt /* cookie_partition_key */));
+ MatchCookieLineToVector("A=B; C; D=E; F=G; D=E; H=I", cookies);
}
// Confirm that input arguments are reflected in the output cookie.
TEST(CanonicalCookieTest, CreateSanitizedCookie_Inputs) {
- 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);
+ base::Time two_hours_ago = base::Time::Now() - base::Hours(2);
+ base::Time one_hour_ago = base::Time::Now() - base::Hours(1);
+ base::Time one_hour_from_now = base::Time::Now() + base::Hours(1);
CookieInclusionStatus status;
std::unique_ptr<CanonicalCookie> cc;
@@ -2785,10 +2843,9 @@ TEST(CanonicalCookieTest, CreateSanitizedCookie_Inputs) {
// Make sure sanitization and blocking of cookies works correctly.
TEST(CanonicalCookieTest, CreateSanitizedCookie_Logic) {
- 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);
+ base::Time two_hours_ago = base::Time::Now() - base::Hours(2);
+ base::Time one_hour_ago = base::Time::Now() - base::Hours(1);
+ base::Time one_hour_from_now = base::Time::Now() + base::Hours(1);
CookieInclusionStatus status;
// Simple path and domain variations.
@@ -3011,7 +3068,7 @@ TEST(CanonicalCookieTest, CreateSanitizedCookie_Logic) {
// Path with unusual characters escaped.
cc = CanonicalCookie::CreateSanitizedCookie(
- GURL("http://www.foo.com"), "A", "B", std::string(), "/foo",
+ GURL("http://www.foo.com"), "A", "B", std::string(), "/foo\x7F",
base::Time(), base::Time(), base::Time(), false /*secure*/,
false /*httponly*/, CookieSameSite::NO_RESTRICTION,
COOKIE_PRIORITY_DEFAULT, false /*same_party*/,
@@ -3020,6 +3077,41 @@ TEST(CanonicalCookieTest, CreateSanitizedCookie_Logic) {
EXPECT_EQ("/foo%7F", cc->Path());
EXPECT_TRUE(status.IsInclude());
+ // Ensure that all characters get escaped the same on all platforms. This is
+ // also useful for visualizing which characters will actually be escaped.
+ std::stringstream ss;
+ ss << "/";
+ for (uint8_t character = 0; character < 0xFF; character++) {
+ // Skip any "terminating characters" that CreateSanitizedCookie does not
+ // allow to be in `path`.
+ if (character == '\0' || character == '\n' || character == '\r' ||
+ character == ';') {
+ continue;
+ }
+ ss << character;
+ }
+ ss << "\xFF";
+ std::string initial(ss.str());
+ std::string expected =
+ "/%01%02%03%04%05%06%07%08%09%0B%0C%0E%0F%10%11%12%13%14%15%16%17%18%19%"
+ "1A%1B%1C%1D%1E%1F%20!%22%23$%&'()*+,-./"
+ "0123456789:%3C=%3E%3F@ABCDEFGHIJKLMNOPQRSTUVWXYZ[/"
+ "]%5E_%60abcdefghijklmnopqrstuvwxyz%7B%7C%7D~%7F%80%81%82%83%84%85%86%87%"
+ "88%89%8A%8B%8C%8D%8E%8F%90%91%92%93%94%95%96%97%98%99%9A%9B%9C%9D%9E%9F%"
+ "A0%A1%A2%A3%A4%A5%A6%A7%A8%A9%AA%AB%AC%AD%AE%AF%B0%B1%B2%B3%B4%B5%B6%B7%"
+ "B8%B9%BA%BB%BC%BD%BE%BF%C0%C1%C2%C3%C4%C5%C6%C7%C8%C9%CA%CB%CC%CD%CE%CF%"
+ "D0%D1%D2%D3%D4%D5%D6%D7%D8%D9%DA%DB%DC%DD%DE%DF%E0%E1%E2%E3%E4%E5%E6%E7%"
+ "E8%E9%EA%EB%EC%ED%EE%EF%F0%F1%F2%F3%F4%F5%F6%F7%F8%F9%FA%FB%FC%FD%FE%FF";
+ cc = CanonicalCookie::CreateSanitizedCookie(
+ GURL("http://www.foo.com"), "A", "B", std::string(), initial,
+ base::Time(), base::Time(), base::Time(), false /*secure*/,
+ false /*httponly*/, CookieSameSite::NO_RESTRICTION,
+ COOKIE_PRIORITY_DEFAULT, false /*same_party*/,
+ absl::nullopt /*partition_key*/, &status);
+ ASSERT_TRUE(cc);
+ EXPECT_EQ(expected, cc->Path());
+ EXPECT_TRUE(status.IsInclude());
+
// Empty name and value.
EXPECT_FALSE(CanonicalCookie::CreateSanitizedCookie(
GURL("http://www.foo.com"), "", "", std::string(), "/", base::Time(),
@@ -3029,6 +3121,38 @@ TEST(CanonicalCookieTest, CreateSanitizedCookie_Logic) {
EXPECT_TRUE(status.HasExactlyExclusionReasonsForTesting(
{CookieInclusionStatus::EXCLUDE_FAILURE_TO_STORE}));
+ // Check that value can contain an equal sign, even when no name is present.
+ // Note that in newer drafts of RFC6265bis, it is specified that a cookie with
+ // an empty name and a value containing an equal sign should result in a
+ // corresponding cookie line that omits the preceding equal sign. This means
+ // that the cookie line won't be deserialized into the original cookie in this
+ // case. For now, we'll test for compliance with the spec here, but we aim to
+ // collect metrics and hopefully fix this in the spec (and then in
+ // CanonicalCookie) at some point.
+ // For reference, see: https://github.com/httpwg/http-extensions/pull/1592
+ cc = CanonicalCookie::CreateSanitizedCookie(
+ GURL("http://www.foo.com"), "", "ambiguous=value", std::string(),
+ std::string(), base::Time(), base::Time(), base::Time(), false /*secure*/,
+ false /*httponly*/, CookieSameSite::NO_RESTRICTION,
+ COOKIE_PRIORITY_DEFAULT, false /*same_party*/,
+ absl::nullopt /*partition_key*/, &status);
+ EXPECT_TRUE(cc);
+ std::vector<std::unique_ptr<CanonicalCookie>> cookies;
+ cookies.push_back(std::move(cc));
+ MatchCookieLineToVector("ambiguous=value", cookies);
+
+ // Check that name can't contain an equal sign ("ambiguous=name=value" should
+ // correctly be parsed as name: "ambiguous" and value "name=value", so
+ // allowing this case would result in cookies that can't serialize correctly).
+ EXPECT_FALSE(CanonicalCookie::CreateSanitizedCookie(
+ GURL("http://www.foo.com"), "ambiguous=name", "value", std::string(),
+ std::string(), base::Time(), base::Time(), base::Time(), false /*secure*/,
+ false /*httponly*/, CookieSameSite::NO_RESTRICTION,
+ COOKIE_PRIORITY_DEFAULT, false /*same_party*/,
+ absl::nullopt /*partition_key*/, &status));
+ EXPECT_TRUE(status.HasExactlyExclusionReasonsForTesting(
+ {CookieInclusionStatus::EXCLUDE_FAILURE_TO_STORE}));
+
// A __Secure- cookie must be Secure.
EXPECT_TRUE(CanonicalCookie::CreateSanitizedCookie(
GURL("https://www.foo.com"), "__Secure-A", "B", ".www.foo.com", "/",
@@ -3265,13 +3389,199 @@ TEST(CanonicalCookieTest, CreateSanitizedCookie_Logic) {
{CookieInclusionStatus::EXCLUDE_FAILURE_TO_STORE,
CookieInclusionStatus::EXCLUDE_INVALID_DOMAIN,
CookieInclusionStatus::EXCLUDE_INVALID_SAMEPARTY}));
+
+ // Check that RFC6265bis name + value string length limits are enforced.
+ std::string max_name(ParsedCookie::kMaxCookieNamePlusValueSize, 'a');
+ std::string max_value(ParsedCookie::kMaxCookieNamePlusValueSize, 'b');
+ std::string almost_max_name = max_name.substr(1, std::string::npos);
+ std::string almost_max_value = max_value.substr(1, std::string::npos);
+
+ EXPECT_TRUE(CanonicalCookie::CreateSanitizedCookie(
+ GURL("http://www.foo.com/foo"), max_name, "", std::string(), "/foo",
+ one_hour_ago, one_hour_from_now, base::Time(), false /*secure*/,
+ false /*httponly*/, CookieSameSite::NO_RESTRICTION,
+ COOKIE_PRIORITY_DEFAULT, false /*same_party*/,
+ absl::nullopt /*partition_key*/, &status));
+ EXPECT_TRUE(status.IsInclude());
+ EXPECT_TRUE(CanonicalCookie::CreateSanitizedCookie(
+ GURL("http://www.foo.com/foo"), "", max_value, std::string(), "/foo",
+ one_hour_ago, one_hour_from_now, base::Time(), false /*secure*/,
+ false /*httponly*/, CookieSameSite::NO_RESTRICTION,
+ COOKIE_PRIORITY_DEFAULT, false /*same_party*/,
+ absl::nullopt /*partition_key*/, &status));
+ EXPECT_TRUE(status.IsInclude());
+ EXPECT_TRUE(CanonicalCookie::CreateSanitizedCookie(
+ GURL("http://www.foo.com/foo"), almost_max_name, "b", std::string(),
+ "/foo", one_hour_ago, one_hour_from_now, base::Time(), false /*secure*/,
+ false /*httponly*/, CookieSameSite::NO_RESTRICTION,
+ COOKIE_PRIORITY_DEFAULT, false /*same_party*/,
+ absl::nullopt /*partition_key*/, &status));
+ EXPECT_TRUE(status.IsInclude());
+ EXPECT_TRUE(CanonicalCookie::CreateSanitizedCookie(
+ GURL("http://www.foo.com/foo"), "a", almost_max_value, std::string(),
+ "/foo", one_hour_ago, one_hour_from_now, base::Time(), false /*secure*/,
+ false /*httponly*/, CookieSameSite::NO_RESTRICTION,
+ COOKIE_PRIORITY_DEFAULT, false /*same_party*/,
+ absl::nullopt /*partition_key*/, &status));
+ EXPECT_TRUE(status.IsInclude());
+
+ for (const bool toggle : {false, true}) {
+ base::test::ScopedFeatureList scope_feature_list;
+ scope_feature_list.InitWithFeatureState(
+ features::kExtraCookieValidityChecks, toggle);
+
+ cc = CanonicalCookie::CreateSanitizedCookie(
+ GURL("http://www.foo.com/foo"), max_name, "X", std::string(), "/foo",
+ one_hour_ago, one_hour_from_now, base::Time(), false /*secure*/,
+ false /*httponly*/, CookieSameSite::NO_RESTRICTION,
+ COOKIE_PRIORITY_DEFAULT, false /*same_party*/,
+ absl::nullopt /*partition_key*/, &status);
+ if (base::FeatureList::IsEnabled(features::kExtraCookieValidityChecks)) {
+ EXPECT_FALSE(cc);
+ EXPECT_TRUE(status.HasExactlyExclusionReasonsForTesting(
+ {CookieInclusionStatus::EXCLUDE_NAME_VALUE_PAIR_EXCEEDS_MAX_SIZE}));
+ } else {
+ EXPECT_TRUE(cc);
+ EXPECT_TRUE(status.IsInclude());
+ }
+ cc = CanonicalCookie::CreateSanitizedCookie(
+ GURL("http://www.foo.com/foo"), "X", max_value, std::string(), "/foo",
+ one_hour_ago, one_hour_from_now, base::Time(), false /*secure*/,
+ false /*httponly*/, CookieSameSite::NO_RESTRICTION,
+ COOKIE_PRIORITY_DEFAULT, false /*same_party*/,
+ absl::nullopt /*partition_key*/, &status);
+ if (base::FeatureList::IsEnabled(features::kExtraCookieValidityChecks)) {
+ EXPECT_FALSE(cc);
+ EXPECT_TRUE(status.HasExactlyExclusionReasonsForTesting(
+ {CookieInclusionStatus::EXCLUDE_NAME_VALUE_PAIR_EXCEEDS_MAX_SIZE}));
+ } else {
+ EXPECT_TRUE(cc);
+ EXPECT_TRUE(status.IsInclude());
+ }
+ }
+
+ // Check that the RFC6265bis attribute value size limits apply to the Path
+ // attribute value.
+ std::string almost_max_path(ParsedCookie::kMaxCookieAttributeValueSize - 1,
+ 'c');
+ std::string max_path = "/" + almost_max_path;
+ std::string too_long_path = "/X" + almost_max_path;
+
+ cc = CanonicalCookie::CreateSanitizedCookie(
+ GURL("http://www.foo.com" + max_path), "name", "value", std::string(),
+ max_path, one_hour_ago, one_hour_from_now, base::Time(), false /*secure*/,
+ false /*httponly*/, CookieSameSite::NO_RESTRICTION,
+ COOKIE_PRIORITY_DEFAULT, false /*same_party*/,
+ absl::nullopt /*partition_key*/, &status);
+ EXPECT_TRUE(cc);
+ EXPECT_EQ(max_path, cc->Path());
+ EXPECT_TRUE(status.IsInclude());
+
+ for (const bool toggle : {false, true}) {
+ base::test::ScopedFeatureList scope_feature_list;
+ scope_feature_list.InitWithFeatureState(
+ features::kExtraCookieValidityChecks, toggle);
+ cc = CanonicalCookie::CreateSanitizedCookie(
+ GURL("http://www.foo.com/path-attr-from-url/"), "name", "value",
+ std::string(), too_long_path, one_hour_ago, one_hour_from_now,
+ base::Time(), false /*secure*/, false /*httponly*/,
+ CookieSameSite::NO_RESTRICTION, COOKIE_PRIORITY_DEFAULT,
+ false /*same_party*/, absl::nullopt /*partition_key*/, &status);
+ if (base::FeatureList::IsEnabled(features::kExtraCookieValidityChecks)) {
+ EXPECT_FALSE(cc);
+ EXPECT_TRUE(status.HasExactlyExclusionReasonsForTesting(
+ {CookieInclusionStatus::EXCLUDE_ATTRIBUTE_VALUE_EXCEEDS_MAX_SIZE}));
+ } else {
+ EXPECT_TRUE(cc);
+ EXPECT_EQ(too_long_path, cc->Path());
+ EXPECT_TRUE(status.IsInclude());
+ }
+ }
+
+ // Check that length limits on the Path attribute value are not enforced in
+ // the case where no Path attribute is specified and the path value is
+ // implicitly set from the URL.
+ cc = CanonicalCookie::CreateSanitizedCookie(
+ GURL("http://www.foo.com" + too_long_path + "/"), "name", "value",
+ std::string(), std::string(), one_hour_ago, one_hour_from_now,
+ base::Time(), false /*secure*/, false /*httponly*/,
+ CookieSameSite::NO_RESTRICTION, COOKIE_PRIORITY_DEFAULT,
+ false /*same_party*/, absl::nullopt /*partition_key*/, &status);
+ EXPECT_TRUE(cc);
+ EXPECT_EQ(too_long_path, cc->Path());
+ EXPECT_TRUE(status.IsInclude());
+
+ // The Path attribute value gets URL-encoded, so ensure that the size limit is
+ // enforced after this (to avoid setting cookies where the Path attribute
+ // value would otherwise exceed the lengths specified in the RFC).
+ for (const bool toggle : {false, true}) {
+ base::test::ScopedFeatureList scope_feature_list;
+ scope_feature_list.InitWithFeatureState(
+ features::kExtraCookieValidityChecks, toggle);
+ std::string expanding_path(ParsedCookie::kMaxCookieAttributeValueSize / 2,
+ '#');
+ expanding_path = "/" + expanding_path;
+
+ cc = CanonicalCookie::CreateSanitizedCookie(
+ GURL("http://www.foo.com/path-attr-from-url/"), "name", "value",
+ std::string(), expanding_path, one_hour_ago, one_hour_from_now,
+ base::Time(), false /*secure*/, false /*httponly*/,
+ CookieSameSite::NO_RESTRICTION, COOKIE_PRIORITY_DEFAULT,
+ false /*same_party*/, absl::nullopt /*partition_key*/, &status);
+ if (base::FeatureList::IsEnabled(features::kExtraCookieValidityChecks)) {
+ EXPECT_FALSE(cc);
+ EXPECT_TRUE(status.HasExactlyExclusionReasonsForTesting(
+ {CookieInclusionStatus::EXCLUDE_ATTRIBUTE_VALUE_EXCEEDS_MAX_SIZE}));
+ } else {
+ EXPECT_TRUE(cc);
+ // "#" expands into "%23"; -2 because '/' doesn't expand
+ EXPECT_EQ((expanding_path.size() * 3) - 2, cc->Path().size());
+ EXPECT_TRUE(status.IsInclude());
+ }
+ }
+
+ // Check that the RFC6265bis attribute value size limits apply to the Domain
+ // attribute value.
+ std::string max_domain(ParsedCookie::kMaxCookieAttributeValueSize, 'd');
+ max_domain.replace(ParsedCookie::kMaxCookieAttributeValueSize - 4, 4, ".com");
+ std::string too_long_domain = "x" + max_domain;
+
+ cc = CanonicalCookie::CreateSanitizedCookie(
+ GURL("http://" + max_domain + "/"), "name", "value", max_domain, "/",
+ one_hour_ago, one_hour_from_now, base::Time(), false /*secure*/,
+ false /*httponly*/, CookieSameSite::NO_RESTRICTION,
+ COOKIE_PRIORITY_DEFAULT, false /*same_party*/,
+ absl::nullopt /*partition_key*/, &status);
+ EXPECT_TRUE(cc);
+ EXPECT_EQ(max_domain, cc->DomainWithoutDot());
+ EXPECT_TRUE(status.IsInclude());
+ cc = CanonicalCookie::CreateSanitizedCookie(
+ GURL("http://www.domain-from-url.com/"), "name", "value", too_long_domain,
+ "/", one_hour_ago, one_hour_from_now, base::Time(), false /*secure*/,
+ false /*httponly*/, CookieSameSite::NO_RESTRICTION,
+ COOKIE_PRIORITY_DEFAULT, false /*same_party*/,
+ absl::nullopt /*partition_key*/, &status);
+ EXPECT_FALSE(cc);
+ EXPECT_TRUE(status.HasExactlyExclusionReasonsForTesting(
+ {CookieInclusionStatus::EXCLUDE_ATTRIBUTE_VALUE_EXCEEDS_MAX_SIZE}));
+ // Check that length limits on the Domain attribute value are not enforced in
+ // the case where no Domain attribute is specified and the domain value is
+ // implicitly set from the URL.
+ cc = CanonicalCookie::CreateSanitizedCookie(
+ GURL("http://" + too_long_domain + "/"), "name", "value", std::string(),
+ "/", one_hour_ago, one_hour_from_now, base::Time(), false /*secure*/,
+ false /*httponly*/, CookieSameSite::NO_RESTRICTION,
+ COOKIE_PRIORITY_DEFAULT, false /*same_party*/,
+ absl::nullopt /*partition_key*/, &status);
+ EXPECT_TRUE(cc);
+ EXPECT_EQ(too_long_domain, cc->DomainWithoutDot());
+ EXPECT_TRUE(status.IsInclude());
}
TEST(CanonicalCookieTest, FromStorage) {
- 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);
+ base::Time two_hours_ago = base::Time::Now() - base::Hours(2);
+ base::Time one_hour_ago = base::Time::Now() - base::Hours(1);
+ base::Time one_hour_from_now = base::Time::Now() + base::Hours(1);
std::unique_ptr<CanonicalCookie> cc = CanonicalCookie::FromStorage(
"A", "B", "www.foo.com", "/bar", two_hours_ago, one_hour_from_now,
@@ -4433,4 +4743,48 @@ TEST(CanonicalCookieTest, IsSetPermittedInContext_RedirectDowngradeWarning) {
}
}
+TEST(CanonicalCookieTest, TestIsCanonicalWithInvalidSizeHistograms) {
+ base::HistogramTester histograms;
+ const char kFromStorageWithValidLengthHistogram[] =
+ "Cookie.FromStorageWithValidLength";
+ const base::HistogramBase::Sample kInValid = 0;
+ const base::HistogramBase::Sample kValid = 1;
+
+ base::Time two_hours_ago = base::Time::Now() - base::Hours(2);
+ base::Time one_hour_ago = base::Time::Now() - base::Hours(1);
+ base::Time one_hour_from_now = base::Time::Now() + base::Hours(1);
+
+ // Test a cookie that is canonical and valid size
+ EXPECT_TRUE(CanonicalCookie::FromStorage(
+ "A", "B", "www.foo.com", "/bar", two_hours_ago, one_hour_from_now,
+ one_hour_ago, false /*secure*/, false /*httponly*/,
+ CookieSameSite::NO_RESTRICTION, COOKIE_PRIORITY_DEFAULT,
+ false /*same_party*/, absl::nullopt /*partition_key*/,
+ CookieSourceScheme::kSecure, 87));
+
+ histograms.ExpectBucketCount(kFromStorageWithValidLengthHistogram, kInValid,
+ 0);
+ histograms.ExpectBucketCount(kFromStorageWithValidLengthHistogram, kValid, 1);
+
+ // Test loading a couple of cookies which are canonical but with an invalid
+ // size
+ const std::string kCookieBig(4096, 'a');
+ EXPECT_TRUE(CanonicalCookie::FromStorage(
+ kCookieBig, "B", "www.foo.com", "/bar", two_hours_ago, one_hour_from_now,
+ one_hour_ago, false /*secure*/, false /*httponly*/,
+ CookieSameSite::NO_RESTRICTION, COOKIE_PRIORITY_DEFAULT,
+ false /*same_party*/, absl::nullopt /*partition_key*/,
+ CookieSourceScheme::kSecure, 87));
+ EXPECT_TRUE(CanonicalCookie::FromStorage(
+ "A", kCookieBig, "www.foo.com", "/bar", two_hours_ago, one_hour_from_now,
+ one_hour_ago, false /*secure*/, false /*httponly*/,
+ CookieSameSite::NO_RESTRICTION, COOKIE_PRIORITY_DEFAULT,
+ false /*same_party*/, absl::nullopt /*partition_key*/,
+ CookieSourceScheme::kSecure, 87));
+
+ histograms.ExpectBucketCount(kFromStorageWithValidLengthHistogram, kInValid,
+ 2);
+ histograms.ExpectBucketCount(kFromStorageWithValidLengthHistogram, kValid, 1);
+}
+
} // namespace net
diff --git a/chromium/net/cookies/cookie_access_delegate.h b/chromium/net/cookies/cookie_access_delegate.h
index 2c5715ef14e..0dfba9e99f2 100644
--- a/chromium/net/cookies/cookie_access_delegate.h
+++ b/chromium/net/cookies/cookie_access_delegate.h
@@ -21,6 +21,10 @@ class SiteForCookies;
class NET_EXPORT CookieAccessDelegate {
public:
CookieAccessDelegate();
+
+ CookieAccessDelegate(const CookieAccessDelegate&) = delete;
+ CookieAccessDelegate& operator=(const CookieAccessDelegate&) = delete;
+
virtual ~CookieAccessDelegate();
// Returns true if the passed in |url| should be permitted to access secure
@@ -60,9 +64,6 @@ class NET_EXPORT CookieAccessDelegate {
// Returns the First-Party Sets.
virtual base::flat_map<net::SchemefulSite, std::set<net::SchemefulSite>>
RetrieveFirstPartySets() const = 0;
-
- private:
- DISALLOW_COPY_AND_ASSIGN(CookieAccessDelegate);
};
} // namespace net
diff --git a/chromium/net/cookies/cookie_access_result.h b/chromium/net/cookies/cookie_access_result.h
index 0c99a19381d..8e38b4dbc15 100644
--- a/chromium/net/cookies/cookie_access_result.h
+++ b/chromium/net/cookies/cookie_access_result.h
@@ -33,6 +33,14 @@ struct NET_EXPORT CookieAccessResult {
~CookieAccessResult();
+ bool operator==(const CookieAccessResult& other) const {
+ return status == other.status &&
+ effective_same_site == other.effective_same_site &&
+ access_semantics == other.access_semantics &&
+ is_allowed_to_access_secure_cookies ==
+ other.is_allowed_to_access_secure_cookies;
+ }
+
CookieInclusionStatus status;
CookieEffectiveSameSite effective_same_site =
CookieEffectiveSameSite::UNDEFINED;
diff --git a/chromium/net/cookies/cookie_change_dispatcher.h b/chromium/net/cookies/cookie_change_dispatcher.h
index ffa9a003da7..0f6fc31183c 100644
--- a/chromium/net/cookies/cookie_change_dispatcher.h
+++ b/chromium/net/cookies/cookie_change_dispatcher.h
@@ -98,10 +98,11 @@ using CookieChangeCallback =
class CookieChangeSubscription {
public:
CookieChangeSubscription() = default;
- virtual ~CookieChangeSubscription() = default;
- private:
- DISALLOW_COPY_AND_ASSIGN(CookieChangeSubscription);
+ CookieChangeSubscription(const CookieChangeSubscription&) = delete;
+ CookieChangeSubscription& operator=(const CookieChangeSubscription&) = delete;
+
+ virtual ~CookieChangeSubscription() = default;
};
// Exposes changes to a CookieStore's contents.
@@ -122,29 +123,45 @@ class CookieChangeSubscription {
class CookieChangeDispatcher {
public:
CookieChangeDispatcher() = default;
+
+ CookieChangeDispatcher(const CookieChangeDispatcher&) = delete;
+ CookieChangeDispatcher& operator=(const CookieChangeDispatcher&) = delete;
+
virtual ~CookieChangeDispatcher() = default;
- // Observe changes to all cookies named |name| that would be sent in a
- // request to |url|.
+ // Observe changes to all cookies named `name` that would be sent in a
+ // request to `url`.
+ //
+ // If `cookie_partition_key` is nullopt, then we ignore all change events for
+ // partitioned cookies. Otherwise it only subscribes to change events for
+ // partitioned cookies with the same provided key.
+ // Unpartitioned cookies are not affected by the `cookie_partition_key`
+ // parameter.
virtual std::unique_ptr<CookieChangeSubscription> AddCallbackForCookie(
const GURL& url,
const std::string& name,
+ const absl::optional<CookiePartitionKey>& cookie_partition_key,
CookieChangeCallback callback) WARN_UNUSED_RESULT = 0;
- // Observe changes to the cookies that would be sent for a request to |url|.
+ // Observe changes to the cookies that would be sent for a request to `url`.
+ //
+ // If `cookie_partition_key` is nullopt, then we ignore all change events for
+ // partitioned cookies. Otherwise it only subscribes to change events for
+ // partitioned cookies with the same provided key.
+ // Unpartitioned cookies are not affected by the `cookie_partition_key`
+ // parameter.
virtual std::unique_ptr<CookieChangeSubscription> AddCallbackForUrl(
const GURL& url,
+ const absl::optional<CookiePartitionKey>& cookie_partition_key,
CookieChangeCallback callback) WARN_UNUSED_RESULT = 0;
// Observe all the CookieStore's changes.
//
// The callback will not observe a few bookkeeping changes.
// See kChangeCauseMapping in cookie_monster.cc for details.
+ // TODO(crbug.com/1225444): Add support for Partitioned cookies.
virtual std::unique_ptr<CookieChangeSubscription> AddCallbackForAllChanges(
CookieChangeCallback callback) WARN_UNUSED_RESULT = 0;
-
- private:
- DISALLOW_COPY_AND_ASSIGN(CookieChangeDispatcher);
};
} // namespace net
diff --git a/chromium/net/cookies/cookie_constants.cc b/chromium/net/cookies/cookie_constants.cc
index acb6142e961..a443d13d57f 100644
--- a/chromium/net/cookies/cookie_constants.cc
+++ b/chromium/net/cookies/cookie_constants.cc
@@ -12,9 +12,8 @@
namespace net {
-const base::TimeDelta kLaxAllowUnsafeMaxAge = base::TimeDelta::FromMinutes(2);
-const base::TimeDelta kShortLaxAllowUnsafeMaxAge =
- base::TimeDelta::FromSeconds(10);
+const base::TimeDelta kLaxAllowUnsafeMaxAge = base::Minutes(2);
+const base::TimeDelta kShortLaxAllowUnsafeMaxAge = base::Seconds(10);
namespace {
diff --git a/chromium/net/cookies/cookie_constants.h b/chromium/net/cookies/cookie_constants.h
index 8e014241066..5285462cd51 100644
--- a/chromium/net/cookies/cookie_constants.h
+++ b/chromium/net/cookies/cookie_constants.h
@@ -353,6 +353,23 @@ CookieSourceSchemeName GetSchemeNameEnum(const GURL& url);
// Empty string was chosen because it is the smallest, non-null value.
NET_EXPORT extern const char kEmptyCookiePartitionKey[];
+// Used for a histogram that measures which character caused the cookie
+// string to be truncated.
+//
+// Do not reorder or renumber. Used for metrics.
+enum class TruncatingCharacterInCookieStringType {
+ // No truncating character in the cookie line.
+ kTruncatingCharNone = 0,
+ // Cookie line truncated because of \x0.
+ kTruncatingCharNull = 1,
+ // Cookie line truncated because of \xD.
+ kTruncatingCharNewline = 2,
+ // Cookie line truncated because of \xA.
+ kTruncatingCharLineFeed = 3,
+
+ kMaxValue = kTruncatingCharLineFeed, // Keep as the last value.
+};
+
} // namespace net
#endif // NET_COOKIES_COOKIE_CONSTANTS_H_
diff --git a/chromium/net/cookies/cookie_inclusion_status.cc b/chromium/net/cookies/cookie_inclusion_status.cc
index 0b70482c6cc..3dc25274b9a 100644
--- a/chromium/net/cookies/cookie_inclusion_status.cc
+++ b/chromium/net/cookies/cookie_inclusion_status.cc
@@ -9,30 +9,27 @@
namespace net {
-namespace {
+CookieInclusionStatus::CookieInclusionStatus() = default;
-uint32_t GetExclusionBitmask(CookieInclusionStatus::ExclusionReason reason) {
- return 1u << static_cast<uint32_t>(reason);
+CookieInclusionStatus::CookieInclusionStatus(ExclusionReason reason) {
+ exclusion_reasons_[reason] = true;
}
-uint32_t GetWarningBitmask(CookieInclusionStatus::WarningReason reason) {
- return 1u << static_cast<uint32_t>(reason);
+CookieInclusionStatus::CookieInclusionStatus(ExclusionReason reason,
+ WarningReason warning) {
+ exclusion_reasons_[reason] = true;
+ warning_reasons_[warning] = true;
}
-} // namespace
-
-CookieInclusionStatus::CookieInclusionStatus() = default;
+CookieInclusionStatus::CookieInclusionStatus(WarningReason warning) {
+ warning_reasons_[warning] = true;
+}
-CookieInclusionStatus::CookieInclusionStatus(ExclusionReason reason)
- : exclusion_reasons_(GetExclusionBitmask(reason)) {}
+CookieInclusionStatus::CookieInclusionStatus(
+ const CookieInclusionStatus& other) = default;
-CookieInclusionStatus::CookieInclusionStatus(ExclusionReason reason,
- WarningReason warning)
- : exclusion_reasons_(GetExclusionBitmask(reason)),
- warning_reasons_(GetWarningBitmask(warning)) {}
-
-CookieInclusionStatus::CookieInclusionStatus(WarningReason warning)
- : warning_reasons_(GetWarningBitmask(warning)) {}
+CookieInclusionStatus& CookieInclusionStatus::operator=(
+ const CookieInclusionStatus& other) = default;
bool CookieInclusionStatus::operator==(
const CookieInclusionStatus& other) const {
@@ -46,27 +43,27 @@ bool CookieInclusionStatus::operator!=(
}
bool CookieInclusionStatus::IsInclude() const {
- return exclusion_reasons_ == 0u;
+ return exclusion_reasons_.none();
}
bool CookieInclusionStatus::HasExclusionReason(ExclusionReason reason) const {
- return exclusion_reasons_ & GetExclusionBitmask(reason);
+ return exclusion_reasons_[reason];
}
bool CookieInclusionStatus::HasOnlyExclusionReason(
ExclusionReason reason) const {
- return exclusion_reasons_ == GetExclusionBitmask(reason);
+ return exclusion_reasons_[reason] && exclusion_reasons_.count() == 1;
}
void CookieInclusionStatus::AddExclusionReason(ExclusionReason reason) {
- exclusion_reasons_ |= GetExclusionBitmask(reason);
+ exclusion_reasons_[reason] = true;
// If the cookie would be excluded for reasons other than the new SameSite
// rules, don't bother warning about it.
MaybeClearSameSiteWarning();
}
void CookieInclusionStatus::RemoveExclusionReason(ExclusionReason reason) {
- exclusion_reasons_ &= ~(GetExclusionBitmask(reason));
+ exclusion_reasons_[reason] = false;
}
void CookieInclusionStatus::RemoveExclusionReasons(
@@ -74,13 +71,14 @@ void CookieInclusionStatus::RemoveExclusionReasons(
exclusion_reasons_ = ExclusionReasonsWithout(reasons);
}
-uint32_t CookieInclusionStatus::ExclusionReasonsWithout(
+CookieInclusionStatus::ExclusionReasonBitset
+CookieInclusionStatus::ExclusionReasonsWithout(
const std::vector<ExclusionReason>& reasons) const {
- uint32_t mask = 0u;
+ CookieInclusionStatus::ExclusionReasonBitset result(exclusion_reasons_);
for (const ExclusionReason reason : reasons) {
- mask |= GetExclusionBitmask(reason);
+ result[reason] = false;
}
- return exclusion_reasons_ & ~mask;
+ return result;
}
void CookieInclusionStatus::MaybeClearSameSiteWarning() {
@@ -113,11 +111,11 @@ bool CookieInclusionStatus::ShouldRecordDowngradeMetrics() const {
}
bool CookieInclusionStatus::ShouldWarn() const {
- return warning_reasons_ != 0u;
+ return warning_reasons_.any();
}
bool CookieInclusionStatus::HasWarningReason(WarningReason reason) const {
- return warning_reasons_ & GetWarningBitmask(reason);
+ return warning_reasons_[reason];
}
bool CookieInclusionStatus::HasDowngradeWarning(
@@ -147,11 +145,11 @@ bool CookieInclusionStatus::HasDowngradeWarning(
}
void CookieInclusionStatus::AddWarningReason(WarningReason reason) {
- warning_reasons_ |= GetWarningBitmask(reason);
+ warning_reasons_[reason] = true;
}
void CookieInclusionStatus::RemoveWarningReason(WarningReason reason) {
- warning_reasons_ &= ~(GetWarningBitmask(reason));
+ warning_reasons_[reason] = false;
}
CookieInclusionStatus::ContextDowngradeMetricValues
@@ -222,6 +220,10 @@ std::string CookieInclusionStatus::GetDebugString() const {
{EXCLUDE_INVALID_PREFIX, "EXCLUDE_INVALID_PREFIX"},
{EXCLUDE_INVALID_SAMEPARTY, "EXCLUDE_INVALID_SAMEPARTY"},
{EXCLUDE_INVALID_PARTITIONED, "EXCLUDE_INVALID_PARTITIONED"},
+ {EXCLUDE_NAME_VALUE_PAIR_EXCEEDS_MAX_SIZE,
+ "EXCLUDE_NAME_VALUE_PAIR_EXCEEDS_MAX_SIZE"},
+ {EXCLUDE_ATTRIBUTE_VALUE_EXCEEDS_MAX_SIZE,
+ "EXCLUDE_ATTRIBUTE_VALUE_EXCEEDS_MAX_SIZE"},
}) {
if (HasExclusionReason(reason.first))
base::StrAppend(&out, {reason.second, ", "});
@@ -267,6 +269,8 @@ std::string CookieInclusionStatus::GetDebugString() const {
"WARN_SAMESITE_NONE_INCLUDED_BY_SAMESITE_STRICT"},
{WARN_CROSS_SITE_REDIRECT_DOWNGRADE_CHANGES_INCLUSION,
"WARN_CROSS_SITE_REDIRECT_DOWNGRADE_CHANGES_INCLUSION"},
+ {WARN_ATTRIBUTE_VALUE_EXCEEDS_MAX_SIZE,
+ "WARN_ATTRIBUTE_VALUE_EXCEEDS_MAX_SIZE"},
}) {
if (HasWarningReason(reason.first))
base::StrAppend(&out, {reason.second, ", "});
@@ -278,14 +282,6 @@ std::string CookieInclusionStatus::GetDebugString() const {
return out;
}
-bool CookieInclusionStatus::IsValid() const {
- // Bit positions where there should not be any true bits.
- uint32_t exclusion_mask = ~0u << static_cast<int>(NUM_EXCLUSION_REASONS);
- uint32_t warning_mask = ~0u << static_cast<int>(NUM_WARNING_REASONS);
- return (exclusion_mask & exclusion_reasons_) == 0u &&
- (warning_mask & warning_reasons_) == 0u;
-}
-
bool CookieInclusionStatus::HasExactlyExclusionReasonsForTesting(
std::vector<CookieInclusionStatus::ExclusionReason> reasons) const {
CookieInclusionStatus expected = MakeFromReasonsForTesting(reasons);
@@ -299,6 +295,17 @@ bool CookieInclusionStatus::HasExactlyWarningReasonsForTesting(
}
// static
+bool CookieInclusionStatus::ValidateExclusionAndWarningFromWire(
+ uint32_t exclusion_reasons,
+ uint32_t warning_reasons) {
+ uint32_t exclusion_mask =
+ static_cast<uint32_t>(~0ul << ExclusionReason::NUM_EXCLUSION_REASONS);
+ uint32_t warning_mask =
+ static_cast<uint32_t>(~0ul << WarningReason::NUM_WARNING_REASONS);
+ return (exclusion_reasons & exclusion_mask) == 0 &&
+ (warning_reasons & warning_mask) == 0;
+}
+
CookieInclusionStatus CookieInclusionStatus::MakeFromReasonsForTesting(
std::vector<ExclusionReason> reasons,
std::vector<WarningReason> warnings) {
diff --git a/chromium/net/cookies/cookie_inclusion_status.h b/chromium/net/cookies/cookie_inclusion_status.h
index 1ee8e9efc03..f6388598fbb 100644
--- a/chromium/net/cookies/cookie_inclusion_status.h
+++ b/chromium/net/cookies/cookie_inclusion_status.h
@@ -5,6 +5,7 @@
#ifndef NET_COOKIES_COOKIE_INCLUSION_STATUS_H_
#define NET_COOKIES_COOKIE_INCLUSION_STATUS_H_
+#include <bitset>
#include <ostream>
#include <string>
#include <vector>
@@ -84,6 +85,14 @@ class NET_EXPORT CookieInclusionStatus {
// valid if the cookie has a __Host- prefix and does not have the SameParty
// attribute.
EXCLUDE_INVALID_PARTITIONED = 18,
+ // Cookie exceeded the name/value pair size limit.
+ EXCLUDE_NAME_VALUE_PAIR_EXCEEDS_MAX_SIZE = 19,
+ // Cookie exceeded the attribute size limit. Note that this exclusion value
+ // won't be used by code that parses cookie lines since RFC6265bis
+ // indicates that large attributes should be ignored instead of causing the
+ // whole cookie to be rejected. There will be a corresponding WarningReason
+ // to notify users that an attribute value was ignored in that case.
+ EXCLUDE_ATTRIBUTE_VALUE_EXCEEDS_MAX_SIZE = 20,
// This should be kept last.
NUM_EXCLUSION_REASONS
@@ -208,6 +217,12 @@ class NET_EXPORT CookieInclusionStatus {
// included/excluded in both cases.
WARN_CROSS_SITE_REDIRECT_DOWNGRADE_CHANGES_INCLUSION = 17,
+ // The cookie exceeded the attribute size limit. RFC6265bis indicates that
+ // large attributes should be ignored instead of causing the whole cookie
+ // to be rejected. This is applied by the code that parses cookie lines and
+ // notifies the user that an attribute value was ignored.
+ WARN_ATTRIBUTE_VALUE_EXCEEDS_MAX_SIZE = 18,
+
// This should be kept last.
NUM_WARNING_REASONS
};
@@ -239,6 +254,11 @@ class NET_EXPORT CookieInclusionStatus {
// Keep last.
kMaxValue = LAX_CROSS_LAX_SECURE
};
+
+ using ExclusionReasonBitset =
+ std::bitset<ExclusionReason::NUM_EXCLUSION_REASONS>;
+ using WarningReasonBitset = std::bitset<WarningReason::NUM_WARNING_REASONS>;
+
// Makes a status that says include and should not warn.
CookieInclusionStatus();
@@ -249,6 +269,10 @@ class NET_EXPORT CookieInclusionStatus {
// Makes a status that contains the given warning.
explicit CookieInclusionStatus(WarningReason warning);
+ // Copyable.
+ CookieInclusionStatus(const CookieInclusionStatus& other);
+ CookieInclusionStatus& operator=(const CookieInclusionStatus& other);
+
bool operator==(const CookieInclusionStatus& other) const;
bool operator!=(const CookieInclusionStatus& other) const;
@@ -303,13 +327,13 @@ class NET_EXPORT CookieInclusionStatus {
void RemoveWarningReason(WarningReason reason);
// Used for serialization/deserialization.
- uint32_t exclusion_reasons() const { return exclusion_reasons_; }
- void set_exclusion_reasons(uint32_t exclusion_reasons) {
+ ExclusionReasonBitset exclusion_reasons() const { return exclusion_reasons_; }
+ void set_exclusion_reasons(ExclusionReasonBitset exclusion_reasons) {
exclusion_reasons_ = exclusion_reasons;
}
- uint32_t warning_reasons() const { return warning_reasons_; }
- void set_warning_reasons(uint32_t warning_reasons) {
+ WarningReasonBitset warning_reasons() const { return warning_reasons_; }
+ void set_warning_reasons(WarningReasonBitset warning_reasons) {
warning_reasons_ = warning_reasons;
}
@@ -319,11 +343,6 @@ class NET_EXPORT CookieInclusionStatus {
// Get exclusion reason(s) and warning in string format.
std::string GetDebugString() const;
- // Checks that the underlying bit vector representation doesn't contain any
- // extraneous bits that are not mapped to any enum values. Does not check
- // for reasons which semantically cannot coexist.
- bool IsValid() const;
-
// Checks whether the exclusion reasons are exactly the set of exclusion
// reasons in the vector. (Ignores warnings.)
bool HasExactlyExclusionReasonsForTesting(
@@ -334,6 +353,10 @@ class NET_EXPORT CookieInclusionStatus {
bool HasExactlyWarningReasonsForTesting(
std::vector<WarningReason> reasons) const;
+ // Validates mojo data, since mojo does not support bitsets.
+ static bool ValidateExclusionAndWarningFromWire(uint32_t exclusion_reasons,
+ uint32_t warning_reasons);
+
// Makes a status that contains the given exclusion reasons and warning.
static CookieInclusionStatus MakeFromReasonsForTesting(
std::vector<ExclusionReason> reasons,
@@ -341,14 +364,14 @@ class NET_EXPORT CookieInclusionStatus {
private:
// Returns the `exclusion_reasons_` with the given `reasons` unset.
- uint32_t ExclusionReasonsWithout(
+ ExclusionReasonBitset ExclusionReasonsWithout(
const std::vector<ExclusionReason>& reasons) const;
// A bit vector of the applicable exclusion reasons.
- uint32_t exclusion_reasons_ = 0u;
+ ExclusionReasonBitset exclusion_reasons_;
// A bit vector of the applicable warning reasons.
- uint32_t warning_reasons_ = 0u;
+ WarningReasonBitset warning_reasons_;
};
NET_EXPORT inline std::ostream& operator<<(std::ostream& os,
diff --git a/chromium/net/cookies/cookie_inclusion_status_unittest.cc b/chromium/net/cookies/cookie_inclusion_status_unittest.cc
index 3efec01415c..2ea50787594 100644
--- a/chromium/net/cookies/cookie_inclusion_status_unittest.cc
+++ b/chromium/net/cookies/cookie_inclusion_status_unittest.cc
@@ -15,7 +15,6 @@ TEST(CookieInclusionStatusTest, IncludeStatus) {
static_cast<int>(CookieInclusionStatus::NUM_WARNING_REASONS);
// Zero-argument constructor
CookieInclusionStatus status;
- EXPECT_TRUE(status.IsValid());
EXPECT_TRUE(status.IsInclude());
for (int i = 0; i < num_exclusion_reasons; ++i) {
EXPECT_FALSE(status.HasExclusionReason(
@@ -36,7 +35,6 @@ TEST(CookieInclusionStatusTest, ExcludeStatus) {
for (int i = 0; i < num_exclusion_reasons; ++i) {
auto reason1 = static_cast<CookieInclusionStatus::ExclusionReason>(i);
CookieInclusionStatus status_one_reason(reason1);
- EXPECT_TRUE(status_one_reason.IsValid());
EXPECT_FALSE(status_one_reason.IsInclude());
EXPECT_TRUE(status_one_reason.HasExclusionReason(reason1));
EXPECT_TRUE(status_one_reason.HasOnlyExclusionReason(reason1));
@@ -51,7 +49,6 @@ TEST(CookieInclusionStatusTest, ExcludeStatus) {
CookieInclusionStatus status_two_reasons = status_one_reason;
status_two_reasons.AddExclusionReason(reason2);
- EXPECT_TRUE(status_two_reasons.IsValid());
EXPECT_FALSE(status_two_reasons.IsInclude());
EXPECT_TRUE(status_two_reasons.HasExclusionReason(reason1));
EXPECT_TRUE(status_two_reasons.HasExclusionReason(reason2));
@@ -61,40 +58,11 @@ TEST(CookieInclusionStatusTest, ExcludeStatus) {
}
}
-TEST(CookieInclusionStatusTest, NotValid) {
- CookieInclusionStatus status;
- int num_exclusion_reasons =
- static_cast<int>(CookieInclusionStatus::NUM_EXCLUSION_REASONS);
- int num_warning_reasons =
- static_cast<int>(CookieInclusionStatus::NUM_WARNING_REASONS);
- status.set_exclusion_reasons(1 << num_exclusion_reasons);
- EXPECT_FALSE(status.IsInclude());
- EXPECT_FALSE(status.IsValid());
-
- status.set_exclusion_reasons(~0u);
- EXPECT_FALSE(status.IsInclude());
- EXPECT_FALSE(status.IsValid());
-
- status.set_warning_reasons(1 << num_warning_reasons);
- EXPECT_FALSE(status.IsInclude());
- EXPECT_FALSE(status.IsValid());
-
- status.set_warning_reasons(~0u);
- EXPECT_FALSE(status.IsInclude());
- EXPECT_FALSE(status.IsValid());
-
- status.set_exclusion_reasons(1 << num_exclusion_reasons);
- status.set_warning_reasons(1 << num_warning_reasons);
- EXPECT_FALSE(status.IsInclude());
- EXPECT_FALSE(status.IsValid());
-}
-
TEST(CookieInclusionStatusTest, AddExclusionReason) {
CookieInclusionStatus status;
status.AddWarningReason(
CookieInclusionStatus::WARN_SAMESITE_UNSPECIFIED_LAX_ALLOW_UNSAFE);
status.AddExclusionReason(CookieInclusionStatus::EXCLUDE_UNKNOWN_ERROR);
- EXPECT_TRUE(status.IsValid());
EXPECT_TRUE(status.HasExactlyExclusionReasonsForTesting(
{CookieInclusionStatus::EXCLUDE_UNKNOWN_ERROR}));
// Adding an exclusion reason other than
@@ -107,7 +75,6 @@ TEST(CookieInclusionStatusTest, AddExclusionReason) {
CookieInclusionStatus::WARN_SAMESITE_UNSPECIFIED_CROSS_SITE_CONTEXT);
status.AddExclusionReason(
CookieInclusionStatus::EXCLUDE_SAMESITE_UNSPECIFIED_TREATED_AS_LAX);
- EXPECT_TRUE(status.IsValid());
EXPECT_TRUE(status.HasExactlyExclusionReasonsForTesting(
{CookieInclusionStatus::EXCLUDE_SAMESITE_UNSPECIFIED_TREATED_AS_LAX}));
EXPECT_TRUE(status.HasExactlyWarningReasonsForTesting(
@@ -123,7 +90,6 @@ TEST(CookieInclusionStatusTest, CheckEachWarningReason) {
for (int i = 0; i < num_warning_reasons; ++i) {
auto reason = static_cast<CookieInclusionStatus::WarningReason>(i);
status.AddWarningReason(reason);
- EXPECT_TRUE(status.IsValid());
EXPECT_TRUE(status.IsInclude());
EXPECT_TRUE(status.ShouldWarn());
EXPECT_TRUE(status.HasWarningReason(reason));
@@ -140,12 +106,10 @@ TEST(CookieInclusionStatusTest, CheckEachWarningReason) {
TEST(CookieInclusionStatusTest, RemoveExclusionReason) {
CookieInclusionStatus status(CookieInclusionStatus::EXCLUDE_UNKNOWN_ERROR);
- EXPECT_TRUE(status.IsValid());
ASSERT_TRUE(
status.HasExclusionReason(CookieInclusionStatus::EXCLUDE_UNKNOWN_ERROR));
status.RemoveExclusionReason(CookieInclusionStatus::EXCLUDE_UNKNOWN_ERROR);
- EXPECT_TRUE(status.IsValid());
EXPECT_FALSE(
status.HasExclusionReason(CookieInclusionStatus::EXCLUDE_UNKNOWN_ERROR));
@@ -153,7 +117,6 @@ TEST(CookieInclusionStatusTest, RemoveExclusionReason) {
ASSERT_FALSE(
status.HasExclusionReason(CookieInclusionStatus::NUM_EXCLUSION_REASONS));
status.RemoveExclusionReason(CookieInclusionStatus::NUM_EXCLUSION_REASONS);
- EXPECT_TRUE(status.IsValid());
EXPECT_FALSE(
status.HasExclusionReason(CookieInclusionStatus::NUM_EXCLUSION_REASONS));
}
@@ -162,14 +125,12 @@ TEST(CookieInclusionStatusTest, RemoveWarningReason) {
CookieInclusionStatus status(
CookieInclusionStatus::EXCLUDE_UNKNOWN_ERROR,
CookieInclusionStatus::WARN_SAMESITE_NONE_INSECURE);
- EXPECT_TRUE(status.IsValid());
EXPECT_TRUE(status.ShouldWarn());
ASSERT_TRUE(status.HasWarningReason(
CookieInclusionStatus::WARN_SAMESITE_NONE_INSECURE));
status.RemoveWarningReason(
CookieInclusionStatus::WARN_SAMESITE_NONE_INSECURE);
- EXPECT_TRUE(status.IsValid());
EXPECT_FALSE(status.ShouldWarn());
EXPECT_FALSE(status.HasWarningReason(
CookieInclusionStatus::WARN_SAMESITE_NONE_INSECURE));
@@ -179,7 +140,6 @@ TEST(CookieInclusionStatusTest, RemoveWarningReason) {
CookieInclusionStatus::WARN_SAMESITE_UNSPECIFIED_CROSS_SITE_CONTEXT));
status.RemoveWarningReason(
CookieInclusionStatus::WARN_SAMESITE_UNSPECIFIED_CROSS_SITE_CONTEXT);
- EXPECT_TRUE(status.IsValid());
EXPECT_FALSE(status.ShouldWarn());
EXPECT_FALSE(status.HasWarningReason(
CookieInclusionStatus::WARN_SAMESITE_UNSPECIFIED_CROSS_SITE_CONTEXT));
@@ -258,7 +218,6 @@ TEST(CookieInclusionStatusTest, RemoveExclusionReasons) {
CookieInclusionStatus::EXCLUDE_SAMESITE_STRICT,
CookieInclusionStatus::EXCLUDE_USER_PREFERENCES,
});
- EXPECT_TRUE(status.IsValid());
ASSERT_TRUE(status.HasExactlyExclusionReasonsForTesting({
CookieInclusionStatus::EXCLUDE_UNKNOWN_ERROR,
CookieInclusionStatus::EXCLUDE_SAMESITE_STRICT,
@@ -269,7 +228,6 @@ TEST(CookieInclusionStatusTest, RemoveExclusionReasons) {
{CookieInclusionStatus::EXCLUDE_UNKNOWN_ERROR,
CookieInclusionStatus::EXCLUDE_UNKNOWN_ERROR,
CookieInclusionStatus::EXCLUDE_SAMESITE_STRICT});
- EXPECT_TRUE(status.IsValid());
EXPECT_TRUE(status.HasExactlyExclusionReasonsForTesting({
CookieInclusionStatus::EXCLUDE_USER_PREFERENCES,
}));
@@ -278,10 +236,42 @@ TEST(CookieInclusionStatusTest, RemoveExclusionReasons) {
ASSERT_FALSE(
status.HasExclusionReason(CookieInclusionStatus::NUM_EXCLUSION_REASONS));
status.RemoveExclusionReasons({CookieInclusionStatus::NUM_EXCLUSION_REASONS});
- EXPECT_TRUE(status.IsValid());
EXPECT_TRUE(status.HasExactlyExclusionReasonsForTesting({
CookieInclusionStatus::EXCLUDE_USER_PREFERENCES,
}));
}
+TEST(CookieInclusionStatusTest, ValidateExclusionAndWarningFromWire) {
+ uint32_t exclusion_reasons = 0ul;
+ uint32_t warning_reasons = 0ul;
+
+ EXPECT_TRUE(CookieInclusionStatus::ValidateExclusionAndWarningFromWire(
+ exclusion_reasons, warning_reasons));
+
+ exclusion_reasons = static_cast<uint32_t>(~0ul);
+ warning_reasons = static_cast<uint32_t>(~0ul);
+ EXPECT_FALSE(CookieInclusionStatus::ValidateExclusionAndWarningFromWire(
+ exclusion_reasons, warning_reasons));
+ EXPECT_FALSE(CookieInclusionStatus::ValidateExclusionAndWarningFromWire(
+ exclusion_reasons, 0u));
+ EXPECT_FALSE(CookieInclusionStatus::ValidateExclusionAndWarningFromWire(
+ 0u, warning_reasons));
+
+ exclusion_reasons = (1u << CookieInclusionStatus::EXCLUDE_DOMAIN_MISMATCH);
+ warning_reasons = (1u << CookieInclusionStatus::WARN_TREATED_AS_SAMEPARTY);
+ EXPECT_TRUE(CookieInclusionStatus::ValidateExclusionAndWarningFromWire(
+ exclusion_reasons, warning_reasons));
+
+ exclusion_reasons = (1u << CookieInclusionStatus::NUM_EXCLUSION_REASONS);
+ warning_reasons = (1u << CookieInclusionStatus::NUM_WARNING_REASONS);
+ EXPECT_FALSE(CookieInclusionStatus::ValidateExclusionAndWarningFromWire(
+ exclusion_reasons, warning_reasons));
+
+ exclusion_reasons =
+ (1u << (CookieInclusionStatus::NUM_EXCLUSION_REASONS - 1));
+ warning_reasons = (1u << (CookieInclusionStatus::NUM_WARNING_REASONS - 1));
+ EXPECT_TRUE(CookieInclusionStatus::ValidateExclusionAndWarningFromWire(
+ exclusion_reasons, warning_reasons));
+}
+
} // namespace net
diff --git a/chromium/net/cookies/cookie_monster.cc b/chromium/net/cookies/cookie_monster.cc
index f13f2ce6133..5c3c232d596 100644
--- a/chromium/net/cookies/cookie_monster.cc
+++ b/chromium/net/cookies/cookie_monster.cc
@@ -64,7 +64,6 @@
#include "base/strings/string_util.h"
#include "base/strings/stringprintf.h"
#include "base/threading/thread_task_runner_handle.h"
-#include "base/trace_event/process_memory_dump.h"
#include "net/base/features.h"
#include "net/base/isolation_info.h"
#include "net/base/registry_controlled_domains/registry_controlled_domain.h"
@@ -83,7 +82,6 @@
#include "url/url_canon.h"
using base::Time;
-using base::TimeDelta;
using base::TimeTicks;
using TimeRange = net::CookieDeletionInfo::TimeRange;
@@ -154,6 +152,8 @@ const size_t CookieMonster::kPurgeCookies = 300;
const size_t CookieMonster::kMaxDomainPurgedKeys = 100;
+const size_t CookieMonster::kPerPartitionDomainMaxCookies = 10;
+
const size_t CookieMonster::kDomainCookiesQuotaLow = 30;
const size_t CookieMonster::kDomainCookiesQuotaMedium = 50;
const size_t CookieMonster::kDomainCookiesQuotaHigh =
@@ -269,6 +269,8 @@ const ChangeCausePair kChangeCauseMapping[] = {
{CookieChangeCause::EVICTED, true},
// DELETE_COOKIE_NON_SECURE
{CookieChangeCause::EVICTED, true},
+ // DELETE_COOKIE_EVICTED_PER_PARTITION_DOMAIN
+ {CookieChangeCause::EVICTED, true},
// DELETE_COOKIE_LAST_ENTRY
{CookieChangeCause::EXPLICIT, false}};
@@ -320,15 +322,15 @@ void HistogramExpirationDuration(const CanonicalCookie& cookie,
CookieMonster::CookieMonster(scoped_refptr<PersistentCookieStore> store,
NetLog* net_log)
- : CookieMonster(
- std::move(store),
- base::TimeDelta::FromSeconds(kDefaultAccessUpdateThresholdSeconds),
- net_log) {}
+ : CookieMonster(std::move(store),
+ base::Seconds(kDefaultAccessUpdateThresholdSeconds),
+ net_log) {}
CookieMonster::CookieMonster(scoped_refptr<PersistentCookieStore> store,
base::TimeDelta last_access_threshold,
NetLog* net_log)
: num_keys_(0u),
+ num_partitioned_cookies_(0u),
change_dispatcher_(this),
initialized_(false),
started_fetching_all_cookies_(false),
@@ -398,6 +400,7 @@ void CookieMonster::SetCanonicalCookieAsync(
void CookieMonster::GetCookieListWithOptionsAsync(
const GURL& url,
const CookieOptions& options,
+ const CookiePartitionKeychain& cookie_partition_keychain,
GetCookieListCallback callback) {
DoCookieCallbackForURL(
base::BindOnce(
@@ -405,7 +408,7 @@ void CookieMonster::GetCookieListWithOptionsAsync(
// the callback on |*this|, so the callback will not outlive
// the object.
&CookieMonster::GetCookieListWithOptions, base::Unretained(this), url,
- options, std::move(callback)),
+ options, cookie_partition_keychain, std::move(callback)),
url);
}
@@ -525,32 +528,6 @@ CookieChangeDispatcher& CookieMonster::GetChangeDispatcher() {
return change_dispatcher_;
}
-void CookieMonster::DumpMemoryStats(
- base::trace_event::ProcessMemoryDump* pmd,
- const std::string& parent_absolute_name) const {
- const char kRelPath[] = "/cookie_monster";
-
- pmd->CreateAllocatorDump(parent_absolute_name + kRelPath + "/cookies")
- ->AddScalar(base::trace_event::MemoryAllocatorDump::kNameObjectCount,
- base::trace_event::MemoryAllocatorDump::kUnitsObjects,
- cookies_.size());
-
- pmd->CreateAllocatorDump(parent_absolute_name + kRelPath +
- "/tasks_pending_global")
- ->AddScalar(base::trace_event::MemoryAllocatorDump::kNameObjectCount,
- base::trace_event::MemoryAllocatorDump::kUnitsObjects,
- tasks_pending_.size());
-
- size_t total_pending_for_key = 0;
- for (const auto& kv : tasks_pending_for_key_)
- total_pending_for_key += kv.second.size();
- pmd->CreateAllocatorDump(parent_absolute_name + kRelPath +
- "/tasks_pending_for_key")
- ->AddScalar(base::trace_event::MemoryAllocatorDump::kNameObjectCount,
- base::trace_event::MemoryAllocatorDump::kUnitsObjects,
- total_pending_for_key);
-}
-
CookieMonster::~CookieMonster() {
DCHECK(thread_checker_.CalledOnValidThread());
net_log_.EndEvent(NetLogEventType::COOKIE_STORE_ALIVE);
@@ -580,6 +557,7 @@ void CookieMonster::GetAllCookies(GetAllCookiesCallback callback) {
// exceeded them) the way that calling GarbageCollect() would.
GarbageCollectExpired(
Time::Now(), CookieMapItPair(cookies_.begin(), cookies_.end()), nullptr);
+ GarbageCollectAllExpiredPartitionedCookies(Time::Now());
// Copy the CanonicalCookie pointers from the map so that we can use the same
// sorter as elsewhere, then copy the result out.
@@ -587,6 +565,12 @@ void CookieMonster::GetAllCookies(GetAllCookiesCallback callback) {
cookie_ptrs.reserve(cookies_.size());
for (const auto& cookie : cookies_)
cookie_ptrs.push_back(cookie.second.get());
+
+ for (const auto& cookie_partition : partitioned_cookies_) {
+ for (const auto& cookie : *cookie_partition.second.get())
+ cookie_ptrs.push_back(cookie.second.get());
+ }
+
std::sort(cookie_ptrs.begin(), cookie_ptrs.end(), CookieSorter);
CookieList cookie_list;
@@ -608,9 +592,11 @@ void CookieMonster::AttachAccessSemanticsListForCookieList(
access_semantics_list);
}
-void CookieMonster::GetCookieListWithOptions(const GURL& url,
- const CookieOptions& options,
- GetCookieListCallback callback) {
+void CookieMonster::GetCookieListWithOptions(
+ const GURL& url,
+ const CookieOptions& options,
+ const CookiePartitionKeychain& cookie_partition_keychain,
+ GetCookieListCallback callback) {
DCHECK(thread_checker_.CalledOnValidThread());
CookieAccessResultList included_cookies;
@@ -618,6 +604,24 @@ void CookieMonster::GetCookieListWithOptions(const GURL& url,
if (HasCookieableScheme(url)) {
std::vector<CanonicalCookie*> cookie_ptrs =
FindCookiesForRegistryControlledHost(url);
+ if (!cookie_partition_keychain.IsEmpty()) {
+ if (cookie_partition_keychain.ContainsAllKeys()) {
+ for (const auto& it : partitioned_cookies_) {
+ std::vector<CanonicalCookie*> partitioned_cookie_ptrs =
+ FindPartitionedCookiesForRegistryControlledHost(it.first, url);
+ cookie_ptrs.insert(cookie_ptrs.end(), partitioned_cookie_ptrs.begin(),
+ partitioned_cookie_ptrs.end());
+ }
+ } else {
+ for (const CookiePartitionKey& key :
+ cookie_partition_keychain.PartitionKeys()) {
+ std::vector<CanonicalCookie*> partitioned_cookie_ptrs =
+ FindPartitionedCookiesForRegistryControlledHost(key, url);
+ cookie_ptrs.insert(cookie_ptrs.end(), partitioned_cookie_ptrs.begin(),
+ partitioned_cookie_ptrs.end());
+ }
+ }
+ }
std::sort(cookie_ptrs.begin(), cookie_ptrs.end(), CookieSorter);
included_cookies.reserve(cookie_ptrs.size());
@@ -646,6 +650,30 @@ void CookieMonster::DeleteAllCreatedInTimeRange(const TimeRange& creation_range,
}
}
+ for (PartitionedCookieMap::iterator partition_it =
+ partitioned_cookies_.begin();
+ partition_it != partitioned_cookies_.end();) {
+ auto cur_partition_it = partition_it;
+ CookieMap::iterator cookie_it = cur_partition_it->second->begin();
+ CookieMap::iterator cookie_end = cur_partition_it->second->end();
+ // InternalDeletePartitionedCookie may delete this cookie partition if it
+ // only has one cookie, so we need to increment the iterator beforehand.
+ ++partition_it;
+
+ while (cookie_it != cookie_end) {
+ auto cur_cookie_it = cookie_it;
+ CanonicalCookie* cc = cur_cookie_it->second.get();
+ ++cookie_it;
+
+ if (creation_range.Contains(cc->CreationDate())) {
+ InternalDeletePartitionedCookie(cur_partition_it, cur_cookie_it,
+ true /*sync_to_store*/,
+ DELETE_COOKIE_EXPLICIT);
+ ++num_deleted;
+ }
+ }
+ }
+
FlushStore(
base::BindOnce(&MaybeRunDeleteCallback, weak_ptr_factory_.GetWeakPtr(),
callback ? base::BindOnce(std::move(callback), num_deleted)
@@ -675,20 +703,37 @@ bool CookieMonster::MatchCookieDeletionInfo(
void CookieMonster::DeleteCanonicalCookie(const CanonicalCookie& cookie,
DeleteCallback callback) {
DCHECK(thread_checker_.CalledOnValidThread());
-
uint32_t result = 0u;
- for (CookieMapItPair its = cookies_.equal_range(GetKey(cookie.Domain()));
- its.first != its.second; ++its.first) {
- const std::unique_ptr<CanonicalCookie>& candidate = its.first->second;
- // Historically, this has refused modification if the cookie has changed
- // value in between the CanonicalCookie object was returned by a getter
- // and when this ran. The later parts of the conditional (everything but
- // the equivalence check) attempt to preserve this behavior.
- if (candidate->IsEquivalent(cookie) &&
- candidate->Value() == cookie.Value()) {
- InternalDeleteCookie(its.first, true, DELETE_COOKIE_EXPLICIT);
- result = 1u;
- break;
+ CookieMap* cookie_map = nullptr;
+ PartitionedCookieMap::iterator cookie_partition_it;
+
+ if (cookie.IsPartitioned()) {
+ cookie_partition_it =
+ partitioned_cookies_.find(cookie.PartitionKey().value());
+ if (cookie_partition_it != partitioned_cookies_.end())
+ cookie_map = cookie_partition_it->second.get();
+ } else {
+ cookie_map = &cookies_;
+ }
+ if (cookie_map) {
+ for (CookieMapItPair its = cookie_map->equal_range(GetKey(cookie.Domain()));
+ its.first != its.second; ++its.first) {
+ const std::unique_ptr<CanonicalCookie>& candidate = its.first->second;
+ // Historically, this has refused modification if the cookie has changed
+ // value in between the CanonicalCookie object was returned by a getter
+ // and when this ran. The later parts of the conditional (everything but
+ // the equivalence check) attempt to preserve this behavior.
+ if (candidate->IsEquivalent(cookie) &&
+ candidate->Value() == cookie.Value()) {
+ if (cookie.IsPartitioned()) {
+ InternalDeletePartitionedCookie(cookie_partition_it, its.first, true,
+ DELETE_COOKIE_EXPLICIT);
+ } else {
+ InternalDeleteCookie(its.first, true, DELETE_COOKIE_EXPLICIT);
+ }
+ result = 1u;
+ break;
+ }
}
}
FlushStore(
@@ -713,6 +758,27 @@ void CookieMonster::DeleteMatchingCookies(DeletePredicate predicate,
++num_deleted;
}
}
+ for (auto partition_it = partitioned_cookies_.begin();
+ partition_it != partitioned_cookies_.end();) {
+ // InternalDeletePartitionedCookie may invalidate |partition_it| if that
+ // cookie partition only has one cookie.
+ auto cur_partition_it = partition_it;
+ CookieMap::iterator cookie_it = cur_partition_it->second->begin();
+ CookieMap::iterator cookie_end = cur_partition_it->second->end();
+ ++partition_it;
+
+ while (cookie_it != cookie_end) {
+ auto cur_cookie_it = cookie_it;
+ CanonicalCookie* cc = cur_cookie_it->second.get();
+ ++cookie_it;
+
+ if (predicate.Run(*cc)) {
+ InternalDeletePartitionedCookie(cur_partition_it, cur_cookie_it, true,
+ cause);
+ ++num_deleted;
+ }
+ }
+ }
FlushStore(
base::BindOnce(&MaybeRunDeleteCallback, weak_ptr_factory_.GetWeakPtr(),
@@ -751,9 +817,9 @@ void CookieMonster::OnLoaded(
std::vector<std::unique_ptr<CanonicalCookie>> cookies) {
DCHECK(thread_checker_.CalledOnValidThread());
StoreLoadedCookies(std::move(cookies));
- base::UmaHistogramCustomTimes(
- "Cookie.TimeBlockedOnLoad", base::TimeTicks::Now() - beginning_time,
- TimeDelta::FromMilliseconds(1), TimeDelta::FromMinutes(1), 50);
+ base::UmaHistogramCustomTimes("Cookie.TimeBlockedOnLoad",
+ base::TimeTicks::Now() - beginning_time,
+ base::Milliseconds(1), base::Minutes(1), 50);
// Invoke the task queue of cookie request.
InvokeQueue();
@@ -794,24 +860,40 @@ void CookieMonster::StoreLoadedCookies(
// Even if a key is expired, insert it so it can be garbage collected,
// removed, and sync'd.
CookieItVector cookies_with_control_chars;
+ std::vector<PartitionedCookieMapIterators>
+ partitioned_cookies_with_control_chars;
for (auto& cookie : cookies) {
CanonicalCookie* cookie_ptr = cookie.get();
CookieAccessResult access_result;
access_result.access_semantics = CookieAccessSemantics::UNKNOWN;
- auto inserted = InternalInsertCookie(
- GetKey(cookie_ptr->Domain()), std::move(cookie),
- false /* sync_to_store */, access_result, false /* dispatch_change */);
+
+ if (cookie_ptr->IsPartitioned()) {
+ auto inserted = InternalInsertPartitionedCookie(
+ GetKey(cookie_ptr->Domain()), std::move(cookie),
+ false /* sync_to_store */, access_result,
+ false /* dispatch_change */);
+ if (ContainsControlCharacter(cookie_ptr->Name()) ||
+ ContainsControlCharacter(cookie_ptr->Value())) {
+ partitioned_cookies_with_control_chars.push_back(inserted);
+ }
+ } else {
+ auto inserted =
+ InternalInsertCookie(GetKey(cookie_ptr->Domain()), std::move(cookie),
+ false /* sync_to_store */, access_result,
+ false /* dispatch_change */);
+
+ if (ContainsControlCharacter(cookie_ptr->Name()) ||
+ ContainsControlCharacter(cookie_ptr->Value())) {
+ cookies_with_control_chars.push_back(inserted);
+ }
+ }
+
const Time cookie_access_time(cookie_ptr->LastAccessDate());
if (earliest_access_time_.is_null() ||
cookie_access_time < earliest_access_time_) {
earliest_access_time_ = cookie_access_time;
}
-
- if (ContainsControlCharacter(cookie_ptr->Name()) ||
- ContainsControlCharacter(cookie_ptr->Value())) {
- cookies_with_control_chars.push_back(inserted);
- }
}
// Any cookies that contain control characters that we have loaded from the
@@ -820,9 +902,17 @@ void CookieMonster::StoreLoadedCookies(
it != cookies_with_control_chars.end();) {
auto curit = it;
++it;
-
InternalDeleteCookie(*curit, true, DELETE_COOKIE_CONTROL_CHAR);
}
+ for (auto it = partitioned_cookies_with_control_chars.begin();
+ it != partitioned_cookies_with_control_chars.end();) {
+ // InternalDeletePartitionedCookie may invalidate the current iterator, so
+ // we increment the iterator in the loop before calling the function.
+ auto curit = it;
+ ++it;
+ InternalDeletePartitionedCookie(curit->first, curit->second, true,
+ DELETE_COOKIE_CONTROL_CHAR);
+ }
// After importing cookies from the PersistentCookieStore, verify that
// none of our other constraints are violated.
@@ -875,7 +965,28 @@ void CookieMonster::EnsureCookiesMapIsValid() {
prev_range_end = cur_range_end;
// Ensure no equivalent cookies for this host.
- TrimDuplicateCookiesForKey(key, cur_range_begin, cur_range_end);
+ TrimDuplicateCookiesForKey(key, cur_range_begin, cur_range_end,
+ absl::nullopt);
+ }
+
+ for (auto cookie_partition_it = partitioned_cookies_.begin();
+ cookie_partition_it != partitioned_cookies_.end();) {
+ auto cur_cookie_partition_it = cookie_partition_it;
+ ++cookie_partition_it;
+
+ // Iterate through the cookies in this partition, grouped by host.
+ CookieMap* cookie_partition = cur_cookie_partition_it->second.get();
+ auto prev_range_end = cookie_partition->begin();
+ while (prev_range_end != cookie_partition->end()) {
+ auto cur_range_begin = prev_range_end;
+ const std::string key = cur_range_begin->first; // Keep a copy.
+ auto cur_range_end = cookie_partition->upper_bound(key);
+ prev_range_end = cur_range_end;
+
+ // Ensure no equivalent cookies for this host and cookie partition key.
+ TrimDuplicateCookiesForKey(key, cur_range_begin, cur_range_end,
+ absl::make_optional(cur_cookie_partition_it));
+ }
}
}
@@ -885,9 +996,11 @@ void CookieMonster::EnsureCookiesMapIsValid() {
// (2) For each list with more than 1 entry, keep the cookie having the
// most recent creation time, and delete the others.
//
-void CookieMonster::TrimDuplicateCookiesForKey(const std::string& key,
- CookieMap::iterator begin,
- CookieMap::iterator end) {
+void CookieMonster::TrimDuplicateCookiesForKey(
+ const std::string& key,
+ CookieMap::iterator begin,
+ CookieMap::iterator end,
+ absl::optional<PartitionedCookieMap::iterator> cookie_partition_it) {
DCHECK(thread_checker_.CalledOnValidThread());
// Set of cookies ordered by creation time.
@@ -954,24 +1067,34 @@ void CookieMonster::TrimDuplicateCookiesForKey(const std::string& key,
// list of iterators one at a time, since |cookies_| is a multimap (they
// don't invalidate existing iterators following deletion).
for (const CookieMap::iterator& dupe : dupes) {
- InternalDeleteCookie(dupe, true,
- DELETE_COOKIE_DUPLICATE_IN_BACKING_STORE);
+ if (cookie_partition_it) {
+ InternalDeletePartitionedCookie(
+ cookie_partition_it.value(), dupe, true,
+ DELETE_COOKIE_DUPLICATE_IN_BACKING_STORE);
+ } else {
+ InternalDeleteCookie(dupe, true,
+ DELETE_COOKIE_DUPLICATE_IN_BACKING_STORE);
+ }
}
}
DCHECK_EQ(num_duplicates, num_duplicates_found);
}
std::vector<CanonicalCookie*>
-CookieMonster::FindCookiesForRegistryControlledHost(const GURL& url) {
+CookieMonster::FindCookiesForRegistryControlledHost(const GURL& url,
+ CookieMap* cookie_map) {
DCHECK(thread_checker_.CalledOnValidThread());
+ if (!cookie_map)
+ cookie_map = &cookies_;
+
Time current_time = Time::Now();
// Retrieve all cookies for a given key
const std::string key(GetKey(url.host_piece()));
std::vector<CanonicalCookie*> cookies;
- for (CookieMapItPair its = cookies_.equal_range(key);
+ for (CookieMapItPair its = cookie_map->equal_range(key);
its.first != its.second;) {
auto curit = its.first;
CanonicalCookie* cc = curit->second.get();
@@ -987,6 +1110,20 @@ CookieMonster::FindCookiesForRegistryControlledHost(const GURL& url) {
return cookies;
}
+std::vector<CanonicalCookie*>
+CookieMonster::FindPartitionedCookiesForRegistryControlledHost(
+ const CookiePartitionKey& cookie_partition_key,
+ const GURL& url) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+
+ PartitionedCookieMap::iterator it =
+ partitioned_cookies_.find(cookie_partition_key);
+ if (it == partitioned_cookies_.end())
+ return std::vector<CanonicalCookie*>();
+
+ return FindCookiesForRegistryControlledHost(url, it->second.get());
+}
+
void CookieMonster::FilterCookiesWithOptions(
const GURL url,
const CookieOptions options,
@@ -1077,19 +1214,25 @@ void CookieMonster::MaybeDeleteEquivalentCookieAndUpdateStatus(
bool skip_httponly,
bool already_expired,
base::Time* creation_date_to_inherit,
- CookieInclusionStatus* status) {
+ CookieInclusionStatus* status,
+ absl::optional<PartitionedCookieMap::iterator> cookie_partition_it) {
DCHECK(thread_checker_.CalledOnValidThread());
DCHECK(!status->HasExclusionReason(
CookieInclusionStatus::EXCLUDE_OVERWRITE_SECURE));
DCHECK(!status->HasExclusionReason(
CookieInclusionStatus::EXCLUDE_OVERWRITE_HTTP_ONLY));
+ CookieMap* cookie_map = &cookies_;
+ if (cookie_partition_it) {
+ cookie_map = cookie_partition_it.value()->second.get();
+ }
+
bool found_equivalent_cookie = false;
- CookieMap::iterator deletion_candidate_it = cookies_.end();
+ CookieMap::iterator deletion_candidate_it = cookie_map->end();
CanonicalCookie* skipped_secure_cookie = nullptr;
// Check every cookie matching this domain key for equivalence.
- CookieMapItPair range_its = cookies_.equal_range(key);
+ CookieMapItPair range_its = cookie_map->equal_range(key);
for (auto cur_it = range_its.first; cur_it != range_its.second; ++cur_it) {
CanonicalCookie* cur_existing_cookie = cur_it->second.get();
@@ -1125,7 +1268,7 @@ void CookieMonster::MaybeDeleteEquivalentCookieAndUpdateStatus(
// overwrite each other.
CHECK(!found_equivalent_cookie)
<< "Duplicate equivalent cookies found, cookie store is corrupted.";
- DCHECK(deletion_candidate_it == cookies_.end());
+ DCHECK(deletion_candidate_it == cookie_map->end());
found_equivalent_cookie = true;
// The |cookie_being_set| is rejected for trying to overwrite an httponly
@@ -1145,14 +1288,22 @@ void CookieMonster::MaybeDeleteEquivalentCookieAndUpdateStatus(
}
}
- if (deletion_candidate_it != cookies_.end()) {
+ if (deletion_candidate_it != cookie_map->end()) {
CanonicalCookie* deletion_candidate = deletion_candidate_it->second.get();
if (deletion_candidate->Value() == cookie_being_set.Value())
*creation_date_to_inherit = deletion_candidate->CreationDate();
if (status->IsInclude()) {
- InternalDeleteCookie(deletion_candidate_it, true /* sync_to_store */,
- already_expired ? DELETE_COOKIE_EXPIRED_OVERWRITE
- : DELETE_COOKIE_OVERWRITE);
+ if (cookie_being_set.IsPartitioned()) {
+ InternalDeletePartitionedCookie(
+ cookie_partition_it.value(), deletion_candidate_it,
+ true /* sync_to_store */,
+ already_expired ? DELETE_COOKIE_EXPIRED_OVERWRITE
+ : DELETE_COOKIE_OVERWRITE);
+ } else {
+ InternalDeleteCookie(deletion_candidate_it, true /* sync_to_store */,
+ already_expired ? DELETE_COOKIE_EXPIRED_OVERWRITE
+ : DELETE_COOKIE_OVERWRITE);
+ }
} else if (status->HasExclusionReason(
CookieInclusionStatus::EXCLUDE_OVERWRITE_SECURE)) {
// Log that we preserved a cookie that would have been deleted due to
@@ -1185,20 +1336,11 @@ CookieMonster::CookieMap::iterator CookieMonster::InternalInsertCookie(
return NetLogCookieMonsterCookieAdded(
cc.get(), sync_to_store, capture_mode);
});
- if ((cc_ptr->IsPersistent() || persist_session_cookies_) && store_.get() &&
- sync_to_store) {
+ if (ShouldUpdatePersistentStore(cc_ptr) && sync_to_store)
store_->AddCookie(*cc_ptr);
- }
auto inserted = cookies_.insert(CookieMap::value_type(key, std::move(cc)));
- int32_t type_sample =
- !cc_ptr->IsEffectivelySameSiteNone(access_result.access_semantics)
- ? 1 << COOKIE_TYPE_SAME_SITE
- : 0;
- type_sample |= cc_ptr->IsHttpOnly() ? 1 << COOKIE_TYPE_HTTPONLY : 0;
- type_sample |= cc_ptr->IsSecure() ? 1 << COOKIE_TYPE_SECURE : 0;
- UMA_HISTOGRAM_EXACT_LINEAR("Cookie.Type", type_sample,
- (1 << COOKIE_TYPE_LAST_ENTRY));
+ LogCookieTypeToUMA(cc_ptr, access_result);
DCHECK(access_result.status.IsInclude());
if (dispatch_change) {
@@ -1224,6 +1366,69 @@ CookieMonster::CookieMap::iterator CookieMonster::InternalInsertCookie(
return inserted;
}
+bool CookieMonster::ShouldUpdatePersistentStore(CanonicalCookie* cc) {
+ return (cc->IsPersistent() || persist_session_cookies_) && store_.get();
+}
+
+void CookieMonster::LogCookieTypeToUMA(
+ CanonicalCookie* cc,
+ const CookieAccessResult& access_result) {
+ int32_t type_sample =
+ !cc->IsEffectivelySameSiteNone(access_result.access_semantics)
+ ? 1 << COOKIE_TYPE_SAME_SITE
+ : 0;
+ type_sample |= cc->IsHttpOnly() ? 1 << COOKIE_TYPE_HTTPONLY : 0;
+ type_sample |= cc->IsSecure() ? 1 << COOKIE_TYPE_SECURE : 0;
+ UMA_HISTOGRAM_EXACT_LINEAR("Cookie.Type", type_sample,
+ (1 << COOKIE_TYPE_LAST_ENTRY));
+}
+
+CookieMonster::PartitionedCookieMapIterators
+CookieMonster::InternalInsertPartitionedCookie(
+ std::string key,
+ std::unique_ptr<CanonicalCookie> cc,
+ bool sync_to_store,
+ const CookieAccessResult& access_result,
+ bool dispatch_change) {
+ DCHECK(cc->IsPartitioned());
+ DCHECK(thread_checker_.CalledOnValidThread());
+ CanonicalCookie* cc_ptr = cc.get();
+
+ net_log_.AddEvent(NetLogEventType::COOKIE_STORE_COOKIE_ADDED,
+ [&](NetLogCaptureMode capture_mode) {
+ return NetLogCookieMonsterCookieAdded(
+ cc.get(), sync_to_store, capture_mode);
+ });
+ if (ShouldUpdatePersistentStore(cc_ptr) && sync_to_store)
+ store_->AddCookie(*cc_ptr);
+
+ CookiePartitionKey partition_key(cc->PartitionKey().value());
+ PartitionedCookieMap::iterator partition_it =
+ partitioned_cookies_.find(partition_key);
+ if (partition_it == partitioned_cookies_.end()) {
+ partition_it =
+ partitioned_cookies_
+ .insert(PartitionedCookieMap::value_type(
+ std::move(partition_key), std::make_unique<CookieMap>()))
+ .first;
+ }
+
+ CookieMap::iterator cookie_it = partition_it->second->insert(
+ CookieMap::value_type(std::move(key), std::move(cc)));
+ ++num_partitioned_cookies_;
+
+ LogCookieTypeToUMA(cc_ptr, access_result);
+
+ DCHECK(access_result.status.IsInclude());
+ if (dispatch_change) {
+ change_dispatcher_.DispatchChange(
+ CookieChangeInfo(*cc_ptr, access_result, CookieChangeCause::INSERTED),
+ true);
+ }
+
+ return std::make_pair(partition_it, cookie_it);
+}
+
void CookieMonster::SetCanonicalCookie(std::unique_ptr<CanonicalCookie> cc,
const GURL& source_url,
const CookieOptions& options,
@@ -1252,13 +1457,29 @@ void CookieMonster::SetCanonicalCookie(std::unique_ptr<CanonicalCookie> cc,
base::Time creation_date_to_inherit;
+ absl::optional<PartitionedCookieMap::iterator> cookie_partition_it;
+ bool should_try_to_delete_duplicates = true;
+
+ if (cc->IsPartitioned()) {
+ auto it = partitioned_cookies_.find(cc->PartitionKey().value());
+ if (it == partitioned_cookies_.end()) {
+ // This is the first cookie in its partition, so it won't have any
+ // duplicates.
+ should_try_to_delete_duplicates = false;
+ } else {
+ cookie_partition_it = absl::make_optional(it);
+ }
+ }
+
// Iterates through existing cookies for the same eTLD+1, and potentially
// deletes an existing cookie, so any ExclusionReasons in |status| that would
// prevent such deletion should be finalized beforehand.
- MaybeDeleteEquivalentCookieAndUpdateStatus(
- key, *cc, access_result.is_allowed_to_access_secure_cookies,
- options.exclude_httponly(), already_expired, &creation_date_to_inherit,
- &access_result.status);
+ if (should_try_to_delete_duplicates) {
+ MaybeDeleteEquivalentCookieAndUpdateStatus(
+ key, *cc, access_result.is_allowed_to_access_secure_cookies,
+ options.exclude_httponly(), already_expired, &creation_date_to_inherit,
+ &access_result.status, cookie_partition_it);
+ }
if (access_result.status.HasExclusionReason(
CookieInclusionStatus::EXCLUDE_OVERWRITE_SECURE) ||
@@ -1290,6 +1511,11 @@ void CookieMonster::SetCanonicalCookie(std::unique_ptr<CanonicalCookie> cc,
1 + IsolationInfo::kPartyContextMaxSize);
}
+ bool is_partitioned_cookie = cc->IsPartitioned();
+ CookiePartitionKey cookie_partition_key;
+ if (is_partitioned_cookie)
+ cookie_partition_key = cc->PartitionKey().value();
+
// 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) {
@@ -1319,7 +1545,12 @@ void CookieMonster::SetCanonicalCookie(std::unique_ptr<CanonicalCookie> cc,
cc->SetCreationDate(creation_date_to_inherit);
}
- InternalInsertCookie(key, std::move(cc), true, access_result);
+ if (is_partitioned_cookie) {
+ InternalInsertPartitionedCookie(key, std::move(cc), true,
+ access_result);
+ } else {
+ InternalInsertCookie(key, std::move(cc), true, access_result);
+ }
} else {
DVLOG(net::cookie_util::kVlogSetCookies)
<< "SetCookie() not storing already expired cookie.";
@@ -1330,7 +1561,12 @@ void CookieMonster::SetCanonicalCookie(std::unique_ptr<CanonicalCookie> cc,
// make sure that we garbage collect... We can also make the assumption
// that if a cookie was set, in the common case it will be used soon after,
// and we will purge the expired cookies in GetCookies().
- GarbageCollect(creation_date, key);
+ if (is_partitioned_cookie) {
+ GarbageCollectPartitionedCookies(creation_date, cookie_partition_key,
+ key);
+ } else {
+ GarbageCollect(creation_date, key);
+ }
if (IsLocalhost(source_url)) {
UMA_HISTOGRAM_ENUMERATION(
@@ -1376,9 +1612,17 @@ void CookieMonster::SetAllCookies(CookieList list,
CookieAccessResult access_result;
access_result.access_semantics = GetAccessSemanticsForCookie(cookie);
- InternalInsertCookie(key, std::make_unique<CanonicalCookie>(cookie), true,
- access_result);
- GarbageCollect(creation_time, key);
+
+ if (cookie.IsPartitioned()) {
+ InternalInsertPartitionedCookie(
+ key, std::make_unique<CanonicalCookie>(cookie), true, access_result);
+ GarbageCollectPartitionedCookies(creation_time,
+ cookie.PartitionKey().value(), key);
+ } else {
+ InternalInsertCookie(key, std::make_unique<CanonicalCookie>(cookie), true,
+ access_result);
+ GarbageCollect(creation_time, key);
+ }
}
// TODO(rdsmith): If this function always returns the same value, it
@@ -1400,7 +1644,7 @@ void CookieMonster::InternalUpdateCookieAccessTime(CanonicalCookie* cc,
return;
cc->SetLastAccessDate(current);
- if ((cc->IsPersistent() || persist_session_cookies_) && store_.get())
+ if (ShouldUpdatePersistentStore(cc))
store_->UpdateCookieAccessTime(*cc);
}
@@ -1431,10 +1675,9 @@ void CookieMonster::InternalDeleteCookie(CookieMap::iterator it,
});
}
- if ((cc->IsPersistent() || persist_session_cookies_) && store_.get() &&
- sync_to_store) {
+ if (ShouldUpdatePersistentStore(cc) && sync_to_store)
store_->DeleteCookie(*cc);
- }
+
change_dispatcher_.DispatchChange(
CookieChangeInfo(
*cc,
@@ -1457,6 +1700,54 @@ void CookieMonster::InternalDeleteCookie(CookieMap::iterator it,
cookies_.erase(it);
}
+void CookieMonster::InternalDeletePartitionedCookie(
+ PartitionedCookieMap::iterator partition_it,
+ CookieMap::iterator cookie_it,
+ bool sync_to_store,
+ DeletionCause deletion_cause) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+
+ // Ideally, this would be asserted up where we define kChangeCauseMapping,
+ // but DeletionCause's visibility (or lack thereof) forces us to make
+ // this check here.
+ static_assert(base::size(kChangeCauseMapping) == DELETE_COOKIE_LAST_ENTRY + 1,
+ "kChangeCauseMapping size should match DeletionCause size");
+
+ CanonicalCookie* cc = cookie_it->second.get();
+ DCHECK(cc->IsPartitioned());
+ DVLOG(net::cookie_util::kVlogSetCookies)
+ << "InternalDeletePartitionedCookie()"
+ << ", cause:" << deletion_cause << ", cc: " << cc->DebugString();
+
+ ChangeCausePair mapping = kChangeCauseMapping[deletion_cause];
+ if (deletion_cause != DELETE_COOKIE_DONT_RECORD) {
+ net_log_.AddEvent(NetLogEventType::COOKIE_STORE_COOKIE_DELETED,
+ [&](NetLogCaptureMode capture_mode) {
+ return NetLogCookieMonsterCookieDeleted(
+ cc, mapping.cause, sync_to_store, capture_mode);
+ });
+ }
+
+ if (ShouldUpdatePersistentStore(cc) && sync_to_store)
+ store_->DeleteCookie(*cc);
+
+ change_dispatcher_.DispatchChange(
+ CookieChangeInfo(
+ *cc,
+ CookieAccessResult(CookieEffectiveSameSite::UNDEFINED,
+ CookieInclusionStatus(),
+ GetAccessSemanticsForCookie(*cc),
+ true /* is_allowed_to_access_secure_cookies */),
+ mapping.cause),
+ mapping.notify);
+
+ partition_it->second->erase(cookie_it);
+ --num_partitioned_cookies_;
+
+ if (partition_it->second->empty())
+ partitioned_cookies_.erase(partition_it);
+}
+
// 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,
@@ -1464,7 +1755,7 @@ size_t CookieMonster::GarbageCollect(const Time& current,
DCHECK(thread_checker_.CalledOnValidThread());
size_t num_deleted = 0;
- Time safe_date(Time::Now() - TimeDelta::FromDays(kSafeFromGlobalPurgeDays));
+ Time safe_date(Time::Now() - base::Days(kSafeFromGlobalPurgeDays));
// Collect garbage for this key, minding cookie priorities.
if (cookies_.count(key) > kDomainMaxCookies) {
@@ -1625,6 +1916,50 @@ size_t CookieMonster::GarbageCollect(const Time& current,
return num_deleted;
}
+size_t CookieMonster::GarbageCollectPartitionedCookies(
+ const base::Time& current,
+ const CookiePartitionKey& cookie_partition_key,
+ const std::string& key) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+
+ size_t num_deleted = 0;
+ PartitionedCookieMap::iterator cookie_partition_it =
+ partitioned_cookies_.find(cookie_partition_key);
+ DCHECK(cookie_partition_it != partitioned_cookies_.end());
+
+ if (cookie_partition_it->second->count(key) > kPerPartitionDomainMaxCookies) {
+ // TODO(crbug.com/1225444): Log garbage collection for partitioned cookies.
+
+ CookieItVector non_expired_cookie_its;
+ num_deleted += GarbageCollectExpiredPartitionedCookies(
+ current, cookie_partition_it,
+ cookie_partition_it->second->equal_range(key), &non_expired_cookie_its);
+
+ if (non_expired_cookie_its.size() > kPerPartitionDomainMaxCookies) {
+ // TODO(crbug.com/1225444): Log deep garbage collection for partitioned
+ // cookies.
+
+ // For now, just delete the least recently accessed partition cookies
+ // until we are under the per-partition domain limit. All partitioned
+ // cookies are Secure since they require the __Host- prefix.
+ std::sort(non_expired_cookie_its.begin(), non_expired_cookie_its.end(),
+ LRACookieSorter);
+ for (size_t i = 0;
+ i < (non_expired_cookie_its.size() - kPerPartitionDomainMaxCookies);
+ ++i) {
+ InternalDeletePartitionedCookie(
+ cookie_partition_it, non_expired_cookie_its[i], true,
+ DELETE_COOKIE_EVICTED_PER_PARTITION_DOMAIN);
+ ++num_deleted;
+ }
+ }
+ }
+
+ // TODO(crbug.com/1225444): Enforce global limit on partitioned cookies.
+
+ return num_deleted;
+}
+
size_t CookieMonster::PurgeLeastRecentMatches(CookieItVector* cookies,
CookiePriority priority,
size_t to_protect,
@@ -1698,6 +2033,47 @@ size_t CookieMonster::GarbageCollectExpired(const Time& current,
return num_deleted;
}
+size_t CookieMonster::GarbageCollectExpiredPartitionedCookies(
+ const Time& current,
+ const PartitionedCookieMap::iterator& cookie_partition_it,
+ const CookieMapItPair& itpair,
+ CookieItVector* cookie_its) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+
+ int num_deleted = 0;
+ for (CookieMap::iterator it = itpair.first, end = itpair.second; it != end;) {
+ auto curit = it;
+ ++it;
+
+ if (curit->second->IsExpired(current)) {
+ InternalDeletePartitionedCookie(cookie_partition_it, curit, true,
+ DELETE_COOKIE_EXPIRED);
+ ++num_deleted;
+ } else if (cookie_its) {
+ cookie_its->push_back(curit);
+ }
+ }
+
+ return num_deleted;
+}
+
+void CookieMonster::GarbageCollectAllExpiredPartitionedCookies(
+ const Time& current) {
+ for (auto it = partitioned_cookies_.begin();
+ it != partitioned_cookies_.end();) {
+ // GarbageCollectExpiredPartitionedCookies calls
+ // InternalDeletePartitionedCookie which may invalidate
+ // |cur_cookie_partition_it|.
+ auto cur_cookie_partition_it = it;
+ ++it;
+ GarbageCollectExpiredPartitionedCookies(
+ current, cur_cookie_partition_it,
+ CookieMapItPair(cur_cookie_partition_it->second->begin(),
+ cur_cookie_partition_it->second->end()),
+ nullptr /*cookie_its*/);
+ }
+}
+
size_t CookieMonster::GarbageCollectDeleteRange(
const Time& current,
DeletionCause cause,
@@ -1809,7 +2185,7 @@ void CookieMonster::RecordPeriodicStats(const base::Time& current_time) {
DCHECK(thread_checker_.CalledOnValidThread());
const base::TimeDelta kRecordStatisticsIntervalTime(
- base::TimeDelta::FromSeconds(kRecordStatisticsIntervalSeconds));
+ base::Seconds(kRecordStatisticsIntervalSeconds));
// If we've taken statistics recently, return.
if (current_time - last_statistic_record_time_ <=
@@ -1821,6 +2197,7 @@ void CookieMonster::RecordPeriodicStats(const base::Time& current_time) {
last_statistic_record_time_ = current_time;
}
+// TODO(crbug.com/1225444): Record periodic stats for Partitioned cookies.
bool CookieMonster::DoRecordPeriodicStats() {
// These values are all bogus if we have only partially loaded the cookies.
if (started_fetching_all_cookies_ && !finished_fetching_all_cookies_)
diff --git a/chromium/net/cookies/cookie_monster.h b/chromium/net/cookies/cookie_monster.h
index 8439ba30a16..5828ff23cdb 100644
--- a/chromium/net/cookies/cookie_monster.h
+++ b/chromium/net/cookies/cookie_monster.h
@@ -85,6 +85,8 @@ class NET_EXPORT CookieMonster : public CookieStore {
// not legal to have domain cookies without an eTLD+1). This rule
// excludes cookies for, e.g, ".com", ".co.uk", or ".internalnetwork".
// This behavior is the same as the behavior in Firefox v 3.6.10.
+ // CookieMap does not store cookies that were set with the Partitioned
+ // attribute, those are stored in PartitionedCookieMap.
// NOTE(deanm):
// I benchmarked hash_multimap vs multimap. We're going to be query-heavy
@@ -100,6 +102,18 @@ class NET_EXPORT CookieMonster : public CookieStore {
using CookieMapItPair = std::pair<CookieMap::iterator, CookieMap::iterator>;
using CookieItVector = std::vector<CookieMap::iterator>;
+ // PartitionedCookieMap only stores cookies that were set with the Partitioned
+ // attribute. The map is double-keyed on cookie's partition key and
+ // the cookie's effective domain of the cookie (the key of CookieMap).
+ // We store partitioned cookies in a separate map so that the queries for a
+ // request's unpartitioned and partitioned cookies will both be more
+ // efficient (since querying two smaller maps is more efficient that querying
+ // one larger map twice).
+ using PartitionedCookieMap =
+ std::map<CookiePartitionKey, std::unique_ptr<CookieMap>>;
+ using PartitionedCookieMapIterators =
+ std::pair<PartitionedCookieMap::iterator, CookieMap::iterator>;
+
// Cookie garbage collection thresholds. Based off of the Mozilla defaults.
// When the number of cookies gets to k{Domain,}MaxCookies
// purge down to k{Domain,}MaxCookies - k{Domain,}PurgeCookies.
@@ -122,6 +136,10 @@ class NET_EXPORT CookieMonster : public CookieStore {
// Max number of keys to store for domains that have been purged.
static const size_t kMaxDomainPurgedKeys;
+ // Partitioned cookie garbage collection thresholds.
+ static const size_t kPerPartitionDomainMaxCookies;
+ // TODO(crbug.com/1225444): Add global limit to number of partitioned cookies.
+
// Quota for cookies with {low, medium, high} priorities within a domain.
static const size_t kDomainCookiesQuotaLow;
static const size_t kDomainCookiesQuotaMedium;
@@ -145,6 +163,9 @@ class NET_EXPORT CookieMonster : public CookieStore {
base::TimeDelta last_access_threshold,
NetLog* net_log);
+ CookieMonster(const CookieMonster&) = delete;
+ CookieMonster& operator=(const CookieMonster&) = delete;
+
~CookieMonster() override;
// Writes all the cookies in |list| into the store, replacing all cookies
@@ -162,6 +183,7 @@ class NET_EXPORT CookieMonster : public CookieStore {
SetCookiesCallback callback) override;
void GetCookieListWithOptionsAsync(const GURL& url,
const CookieOptions& options,
+ const CookiePartitionKeychain& s,
GetCookieListCallback callback) override;
void GetAllCookiesAsync(GetAllCookiesCallback callback) override;
void GetAllCookiesWithAccessSemanticsAsync(
@@ -191,9 +213,6 @@ class NET_EXPORT CookieMonster : public CookieStore {
static const char* const kDefaultCookieableSchemes[];
static const int kDefaultCookieableSchemesCount;
- void DumpMemoryStats(base::trace_event::ProcessMemoryDump* pmd,
- const std::string& parent_absolute_name) const override;
-
// Find a key based on the given domain, which will be used to find all
// cookies potentially relevant to it. This is used for lookup in cookies_ as
// well as for PersistentCookieStore::LoadCookiesForKey. See comment on keys
@@ -257,7 +276,7 @@ class NET_EXPORT CookieMonster : public CookieStore {
// Cookies evicted during domain-level garbage collection.
DELETE_COOKIE_EVICTED_DOMAIN = 6,
- // Cookies evicted during global garbage collection (which takes place after
+ // 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,
@@ -278,7 +297,11 @@ class NET_EXPORT CookieMonster : public CookieStore {
// right after expired cookies.
DELETE_COOKIE_NON_SECURE = 12,
- DELETE_COOKIE_LAST_ENTRY = 13
+ // Partitioned cookies evicted during per-partition domain-level garbage
+ // collection.
+ DELETE_COOKIE_EVICTED_PER_PARTITION_DOMAIN = 13,
+
+ DELETE_COOKIE_LAST_ENTRY = 14,
};
// This enum is used to generate a histogramed bitmask measureing the types
@@ -356,9 +379,11 @@ class NET_EXPORT CookieMonster : public CookieStore {
GetAllCookiesWithAccessSemanticsCallback callback,
const CookieList& cookie_list);
- void GetCookieListWithOptions(const GURL& url,
- const CookieOptions& options,
- GetCookieListCallback callback);
+ void GetCookieListWithOptions(
+ const GURL& url,
+ const CookieOptions& options,
+ const CookiePartitionKeychain& cookie_partition_keychain,
+ GetCookieListCallback callback);
void DeleteAllCreatedInTimeRange(
const CookieDeletionInfo::TimeRange& creation_range,
@@ -417,13 +442,24 @@ class NET_EXPORT CookieMonster : public CookieStore {
// Checks for any duplicate cookies for CookieMap key |key| which lie between
// |begin| and |end|. If any are found, all but the most recent are deleted.
- void TrimDuplicateCookiesForKey(const std::string& key,
- CookieMap::iterator begin,
- CookieMap::iterator end);
+ //
+ // If |cookie_partition_it| is not nullopt, then this function trims cookies
+ // from the CookieMap in |partitioned_cookies_| at |cookie_partition_it|
+ // instead of trimming cookies from |cookies_|.
+ void TrimDuplicateCookiesForKey(
+ const std::string& key,
+ CookieMap::iterator begin,
+ CookieMap::iterator end,
+ absl::optional<PartitionedCookieMap::iterator> cookie_partition_it);
void SetDefaultCookieableSchemes();
std::vector<CanonicalCookie*> FindCookiesForRegistryControlledHost(
+ const GURL& url,
+ CookieMap* cookie_map = nullptr);
+
+ std::vector<CanonicalCookie*> FindPartitionedCookiesForRegistryControlledHost(
+ const CookiePartitionKey& cookie_partition_key,
const GURL& url);
void FilterCookiesWithOptions(const GURL url,
@@ -455,6 +491,11 @@ class NET_EXPORT CookieMonster : public CookieStore {
// the function. The function will update |*status| with exclusion reasons if
// a secure cookie was skipped or an httponly cookie was skipped.
//
+ // If |cookie_partition_it| is nullopt, it will search |cookies_| for
+ // duplicates of |cookie_being_set|. Otherwise, |cookie_partition_it|'s value
+ // is the iterator of the CookieMap in |partitioned_cookies_| we should search
+ // for duplicates.
+ //
// NOTE: There should never be more than a single matching equivalent cookie.
void MaybeDeleteEquivalentCookieAndUpdateStatus(
const std::string& key,
@@ -463,7 +504,8 @@ class NET_EXPORT CookieMonster : public CookieStore {
bool skip_httponly,
bool already_expired,
base::Time* creation_date_to_inherit,
- CookieInclusionStatus* status);
+ CookieInclusionStatus* status,
+ absl::optional<PartitionedCookieMap::iterator> cookie_partition_it);
// Inserts `cc` into cookies_. Returns an iterator that points to the inserted
// cookie in `cookies_`. Guarantee: all iterators to `cookies_` remain valid.
@@ -476,6 +518,22 @@ class NET_EXPORT CookieMonster : public CookieStore {
const CookieAccessResult& access_result,
bool dispatch_change = true);
+ // Returns true if the cookie should be (or is already) synced to the store.
+ // Used for cookies during insertion and deletion into the in-memory store.
+ bool ShouldUpdatePersistentStore(CanonicalCookie* cc);
+
+ void LogCookieTypeToUMA(CanonicalCookie* cc,
+ const CookieAccessResult& access_result);
+
+ // Inserts `cc` into partitioned_cookies_. Should only be used when
+ // cc->IsPartitioned() is true.
+ PartitionedCookieMapIterators InternalInsertPartitionedCookie(
+ std::string key,
+ std::unique_ptr<CanonicalCookie> cc,
+ bool sync_to_store,
+ const CookieAccessResult& access_result,
+ bool dispatch_change = true);
+
// Sets all cookies from |list| after deleting any equivalent cookie.
// For data gathering purposes, this routine is treated as if it is
// restoring saved cookies; some statistics are not gathered in this case.
@@ -491,14 +549,37 @@ class NET_EXPORT CookieMonster : public CookieStore {
bool sync_to_store,
DeletionCause deletion_cause);
+ // Deletes a Partitioned cookie. Returns true if the deletion operation
+ // resulted in the CookieMap the cookie was stored in was deleted.
+ //
+ // If the CookieMap which contains the deleted cookie only has one entry, then
+ // this function will also delete the CookieMap from PartitionedCookieMap.
+ // This may invalidate the |cookie_partition_it| argument.
+ void InternalDeletePartitionedCookie(
+ PartitionedCookieMap::iterator partition_it,
+ CookieMap::iterator cookie_it,
+ bool sync_to_store,
+ DeletionCause deletion_cause);
+
// If the number of cookies for CookieMap key |key|, or globally, are
// over the preset maximums above, garbage collect, first for the host and
// then globally. See comments above garbage collection threshold
- // constants for details.
+ // constants for details. Also removes expired cookies.
//
// Returns the number of cookies deleted (useful for debugging).
size_t GarbageCollect(const base::Time& current, const std::string& key);
+ // Run garbage collection for PartitionedCookieMap keys |cookie_partition_key|
+ // and |key|.
+ //
+ // Partitioned cookies are subject to different limits than unpartitioned
+ // cookies in order to prevent leaking entropy about user behavior across
+ // cookie partitions.
+ size_t GarbageCollectPartitionedCookies(
+ const base::Time& current,
+ const CookiePartitionKey& cookie_partition_key,
+ const std::string& key);
+
// 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.
@@ -523,6 +604,22 @@ class NET_EXPORT CookieMonster : public CookieStore {
const CookieMapItPair& itpair,
CookieItVector* cookie_its);
+ // Deletes all expired cookies in the double-keyed PartitionedCookie map in
+ // the CookieMap at |cookie_partition_it|. It deletes all cookies in that
+ // CookieMap in |itpair|. If |cookie_its| is non-NULL, all non-expired cookies
+ // from |itpair| are appended to |cookie_its|.
+ //
+ // Returns the number of cookies deleted.
+ size_t GarbageCollectExpiredPartitionedCookies(
+ const base::Time& current,
+ const PartitionedCookieMap::iterator& cookie_partition_it,
+ const CookieMapItPair& itpair,
+ CookieItVector* cookie_its);
+
+ // Helper function to garbage collect all expired cookies in
+ // PartitionedCookieMap.
+ void GarbageCollectAllExpiredPartitionedCookies(const base::Time& current);
+
// Helper for GarbageCollect(). Deletes all cookies in the range specified by
// [|it_begin|, |it_end|). Returns the number of cookies deleted.
size_t GarbageCollectDeleteRange(const base::Time& current,
@@ -595,6 +692,12 @@ class NET_EXPORT CookieMonster : public CookieStore {
CookieMap cookies_;
+ PartitionedCookieMap partitioned_cookies_;
+
+ // Number of distinct partitioned cookies globally. This is used to enforce a
+ // global maximum on the number of partitioned cookies.
+ size_t num_partitioned_cookies_;
+
CookieMonsterChangeDispatcher change_dispatcher_;
// Indicates whether the cookie store has been initialized.
@@ -653,8 +756,6 @@ class NET_EXPORT CookieMonster : public CookieStore {
base::ThreadChecker thread_checker_;
base::WeakPtrFactory<CookieMonster> weak_ptr_factory_{this};
-
- DISALLOW_COPY_AND_ASSIGN(CookieMonster);
};
typedef base::RefCountedThreadSafe<CookieMonster::PersistentCookieStore>
@@ -667,6 +768,9 @@ class NET_EXPORT CookieMonster::PersistentCookieStore
std::vector<std::unique_ptr<CanonicalCookie>>)>
LoadedCallback;
+ PersistentCookieStore(const PersistentCookieStore&) = delete;
+ PersistentCookieStore& operator=(const PersistentCookieStore&) = delete;
+
// 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.
@@ -709,7 +813,6 @@ class NET_EXPORT CookieMonster::PersistentCookieStore
private:
friend class base::RefCountedThreadSafe<PersistentCookieStore>;
- DISALLOW_COPY_AND_ASSIGN(PersistentCookieStore);
};
} // namespace net
diff --git a/chromium/net/cookies/cookie_monster_change_dispatcher.cc b/chromium/net/cookies/cookie_monster_change_dispatcher.cc
index 43dffc1657d..6cac1881162 100644
--- a/chromium/net/cookies/cookie_monster_change_dispatcher.cc
+++ b/chromium/net/cookies/cookie_monster_change_dispatcher.cc
@@ -33,11 +33,13 @@ CookieMonsterChangeDispatcher::Subscription::Subscription(
std::string domain_key,
std::string name_key,
GURL url,
+ absl::optional<CookiePartitionKey> cookie_partition_key,
net::CookieChangeCallback callback)
: change_dispatcher_(std::move(change_dispatcher)),
domain_key_(std::move(domain_key)),
name_key_(std::move(name_key)),
url_(std::move(url)),
+ cookie_partition_key_(std::move(cookie_partition_key)),
callback_(std::move(callback)),
task_runner_(base::ThreadTaskRunnerHandle::Get()) {
DCHECK(url_.is_valid() || url_.is_empty());
@@ -80,6 +82,11 @@ void CookieMonsterChangeDispatcher::Subscription::DispatchChange(
}
}
+ if (change.cookie.IsPartitioned() &&
+ change.cookie.PartitionKey() != cookie_partition_key_) {
+ return;
+ }
+
// TODO(mmenke, pwnall): Run callbacks synchronously?
task_runner_->PostTask(
FROM_HERE, base::BindOnce(&Subscription::DoDispatchChange,
@@ -130,12 +137,13 @@ std::unique_ptr<CookieChangeSubscription>
CookieMonsterChangeDispatcher::AddCallbackForCookie(
const GURL& url,
const std::string& name,
+ const absl::optional<CookiePartitionKey>& cookie_partition_key,
CookieChangeCallback callback) {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
std::unique_ptr<Subscription> subscription = std::make_unique<Subscription>(
weak_ptr_factory_.GetWeakPtr(), DomainKey(url), NameKey(name), url,
- std::move(callback));
+ cookie_partition_key, std::move(callback));
LinkSubscription(subscription.get());
return subscription;
@@ -144,12 +152,14 @@ CookieMonsterChangeDispatcher::AddCallbackForCookie(
std::unique_ptr<CookieChangeSubscription>
CookieMonsterChangeDispatcher::AddCallbackForUrl(
const GURL& url,
+ const absl::optional<CookiePartitionKey>& cookie_partition_key,
CookieChangeCallback callback) {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
std::unique_ptr<Subscription> subscription = std::make_unique<Subscription>(
weak_ptr_factory_.GetWeakPtr(), DomainKey(url),
- std::string(kGlobalNameKey), url, std::move(callback));
+ std::string(kGlobalNameKey), url, cookie_partition_key,
+ std::move(callback));
LinkSubscription(subscription.get());
return subscription;
@@ -162,7 +172,8 @@ CookieMonsterChangeDispatcher::AddCallbackForAllChanges(
std::unique_ptr<Subscription> subscription = std::make_unique<Subscription>(
weak_ptr_factory_.GetWeakPtr(), std::string(kGlobalDomainKey),
- std::string(kGlobalNameKey), GURL(""), std::move(callback));
+ std::string(kGlobalNameKey), GURL(""), CookiePartitionKey::Todo(),
+ std::move(callback));
LinkSubscription(subscription.get());
return subscription;
diff --git a/chromium/net/cookies/cookie_monster_change_dispatcher.h b/chromium/net/cookies/cookie_monster_change_dispatcher.h
index c769efe9b99..bf25388810f 100644
--- a/chromium/net/cookies/cookie_monster_change_dispatcher.h
+++ b/chromium/net/cookies/cookie_monster_change_dispatcher.h
@@ -33,6 +33,11 @@ class CookieMonsterChangeDispatcher : public CookieChangeDispatcher {
// Expects |cookie_monster| to outlive this.
explicit CookieMonsterChangeDispatcher(const CookieMonster* cookie_monster);
+
+ CookieMonsterChangeDispatcher(const CookieMonsterChangeDispatcher&) = delete;
+ CookieMonsterChangeDispatcher& operator=(
+ const CookieMonsterChangeDispatcher&) = delete;
+
~CookieMonsterChangeDispatcher() override;
// The key in CookieNameMap for a cookie name.
@@ -48,9 +53,11 @@ class CookieMonsterChangeDispatcher : public CookieChangeDispatcher {
std::unique_ptr<CookieChangeSubscription> AddCallbackForCookie(
const GURL& url,
const std::string& name,
+ const absl::optional<CookiePartitionKey>& cookie_partition_key,
CookieChangeCallback callback) override WARN_UNUSED_RESULT;
std::unique_ptr<CookieChangeSubscription> AddCallbackForUrl(
const GURL& url,
+ const absl::optional<CookiePartitionKey>& cookie_partition_key,
CookieChangeCallback callback) override WARN_UNUSED_RESULT;
std::unique_ptr<CookieChangeSubscription> AddCallbackForAllChanges(
CookieChangeCallback callback) override WARN_UNUSED_RESULT;
@@ -69,8 +76,12 @@ class CookieMonsterChangeDispatcher : public CookieChangeDispatcher {
std::string domain_key,
std::string name_key,
GURL url,
+ absl::optional<CookiePartitionKey> cookie_partition_key,
net::CookieChangeCallback callback);
+ Subscription(const Subscription&) = delete;
+ Subscription& operator=(const Subscription&) = delete;
+
~Subscription() override;
// The lookup key used in the domain subscription map.
@@ -91,6 +102,8 @@ class CookieMonsterChangeDispatcher : public CookieChangeDispatcher {
const std::string domain_key_; // kGlobalDomainKey means no filtering.
const std::string name_key_; // kGlobalNameKey means no filtering.
const GURL url_; // empty() means no URL-based filtering.
+ // nullopt means all Partitioned cookies will be ignored.
+ const absl::optional<CookiePartitionKey> cookie_partition_key_;
const net::CookieChangeCallback callback_;
void DoDispatchChange(const CookieChangeInfo& change) const;
@@ -103,8 +116,6 @@ class CookieMonsterChangeDispatcher : public CookieChangeDispatcher {
// Used to cancel delayed calls to DoDispatchChange() when the subscription
// gets destroyed.
base::WeakPtrFactory<Subscription> weak_ptr_factory_{this};
-
- DISALLOW_COPY_AND_ASSIGN(Subscription);
};
// The last level of the subscription data structures.
@@ -147,8 +158,6 @@ class CookieMonsterChangeDispatcher : public CookieChangeDispatcher {
// Vends weak pointers to subscriptions.
base::WeakPtrFactory<CookieMonsterChangeDispatcher> weak_ptr_factory_{this};
-
- DISALLOW_COPY_AND_ASSIGN(CookieMonsterChangeDispatcher);
};
} // namespace net
diff --git a/chromium/net/cookies/cookie_monster_perftest.cc b/chromium/net/cookies/cookie_monster_perftest.cc
index 61bb68fcadc..3d9746c7093 100644
--- a/chromium/net/cookies/cookie_monster_perftest.cc
+++ b/chromium/net/cookies/cookie_monster_perftest.cc
@@ -117,7 +117,7 @@ class GetCookieListCallback : public CookieTestCallback {
public:
const CookieList& GetCookieList(CookieMonster* cm, const GURL& gurl) {
cm->GetCookieListWithOptionsAsync(
- gurl, options_,
+ gurl, options_, CookiePartitionKeychain(),
base::BindOnce(&GetCookieListCallback::Run, base::Unretained(this)));
WaitForCallback();
return cookie_list_;
diff --git a/chromium/net/cookies/cookie_monster_store_test.cc b/chromium/net/cookies/cookie_monster_store_test.cc
index 63e85b3e690..786039566d1 100644
--- a/chromium/net/cookies/cookie_monster_store_test.cc
+++ b/chromium/net/cookies/cookie_monster_store_test.cc
@@ -205,7 +205,7 @@ std::unique_ptr<CookieMonster> CreateMonsterFromStoreForGC(
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));
+ base::Time past_creation(base::Time::Now() - base::Days(1000));
scoped_refptr<MockSimplePersistentCookieStore> store(
new MockSimplePersistentCookieStore);
int total_cookies = num_secure_cookies + num_non_secure_cookies;
@@ -222,13 +222,11 @@ std::unique_ptr<CookieMonster> CreateMonsterFromStoreForGC(
num_old_cookies = num_old_non_secure_cookies;
secure = false;
}
- base::Time creation_time =
- past_creation + base::TimeDelta::FromMicroseconds(i);
- base::Time expiration_time = current + base::TimeDelta::FromDays(30);
- base::Time last_access_time =
- ((i - base) < num_old_cookies)
- ? current - base::TimeDelta::FromDays(days_old)
- : current;
+ base::Time creation_time = past_creation + base::Microseconds(i);
+ base::Time expiration_time = current + base::Days(30);
+ base::Time last_access_time = ((i - base) < num_old_cookies)
+ ? current - base::Days(days_old)
+ : current;
// The URL must be HTTPS since |secure| can be true or false, and because
// strict secure cookies are enforced, the cookie will fail to be created if
diff --git a/chromium/net/cookies/cookie_monster_store_test.h b/chromium/net/cookies/cookie_monster_store_test.h
index c925612720f..7244241f182 100644
--- a/chromium/net/cookies/cookie_monster_store_test.h
+++ b/chromium/net/cookies/cookie_monster_store_test.h
@@ -77,6 +77,10 @@ class MockPersistentCookieStore : public CookieMonster::PersistentCookieStore {
MockPersistentCookieStore();
+ MockPersistentCookieStore(const MockPersistentCookieStore&) = delete;
+ MockPersistentCookieStore& operator=(const MockPersistentCookieStore&) =
+ delete;
+
// 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) {
@@ -125,8 +129,6 @@ class MockPersistentCookieStore : public CookieMonster::PersistentCookieStore {
// Indicates if the store has been fully loaded to avoid returning duplicate
// cookies.
bool loaded_;
-
- DISALLOW_COPY_AND_ASSIGN(MockPersistentCookieStore);
};
// Helper to build a single CanonicalCookie.
diff --git a/chromium/net/cookies/cookie_monster_unittest.cc b/chromium/net/cookies/cookie_monster_unittest.cc
index 23c3f2e783a..f610c750570 100644
--- a/chromium/net/cookies/cookie_monster_unittest.cc
+++ b/chromium/net/cookies/cookie_monster_unittest.cc
@@ -62,7 +62,6 @@
namespace net {
using base::Time;
-using base::TimeDelta;
using CookieDeletionInfo = net::CookieDeletionInfo;
namespace {
@@ -109,6 +108,7 @@ struct CookieMonsterTestTraits {
static const bool has_exact_change_ordering = true;
static const int creation_time_granularity_in_ms = 0;
static const bool supports_cookie_access_semantics = true;
+ static const bool supports_partitioned_cookies = true;
};
INSTANTIATE_TYPED_TEST_SUITE_P(CookieMonster,
@@ -133,12 +133,16 @@ class CookieMonsterTestBase : public CookieStoreTest<T> {
using CookieStoreTest<T>::http_www_foo_;
using CookieStoreTest<T>::https_www_foo_;
- CookieList GetAllCookiesForURLWithOptions(CookieMonster* cm,
- const GURL& url,
- const CookieOptions& options) {
+ CookieList GetAllCookiesForURLWithOptions(
+ CookieMonster* cm,
+ const GURL& url,
+ const CookieOptions& options,
+ const CookiePartitionKeychain& cookie_partition_keychain =
+ CookiePartitionKeychain()) {
DCHECK(cm);
GetCookieListCallback callback;
- cm->GetCookieListWithOptionsAsync(url, options, callback.MakeCallback());
+ cm->GetCookieListWithOptionsAsync(url, options, cookie_partition_keychain,
+ callback.MakeCallback());
callback.WaitUntilDone();
return callback.cookies();
}
@@ -146,10 +150,13 @@ class CookieMonsterTestBase : public CookieStoreTest<T> {
CookieAccessResultList GetExcludedCookiesForURLWithOptions(
CookieMonster* cm,
const GURL& url,
- const CookieOptions& options) {
+ const CookieOptions& options,
+ const CookiePartitionKeychain& cookie_partition_keychain =
+ CookiePartitionKeychain()) {
DCHECK(cm);
GetCookieListCallback callback;
- cm->GetCookieListWithOptionsAsync(url, options, callback.MakeCallback());
+ cm->GetCookieListWithOptionsAsync(url, options, cookie_partition_keychain,
+ callback.MakeCallback());
callback.WaitUntilDone();
return callback.excluded_cookies();
}
@@ -162,17 +169,19 @@ class CookieMonsterTestBase : public CookieStoreTest<T> {
return callback.result().status.IsInclude();
}
- bool SetCookieWithCreationTime(CookieMonster* cm,
- const GURL& url,
- const std::string& cookie_line,
- base::Time creation_time) {
+ bool SetCookieWithCreationTime(
+ CookieMonster* cm,
+ const GURL& url,
+ const std::string& cookie_line,
+ base::Time creation_time,
+ absl::optional<CookiePartitionKey> cookie_partition_key = absl::nullopt) {
DCHECK(cm);
DCHECK(!creation_time.is_null());
ResultSavingCookieCallback<CookieAccessResult> callback;
cm->SetCanonicalCookieAsync(
CanonicalCookie::Create(url, cookie_line, creation_time,
absl::nullopt /* server_time */,
- absl::nullopt /* cookie_partition_key */),
+ cookie_partition_key),
url, CookieOptions::MakeAllInclusive(), callback.MakeCallback());
callback.WaitUntilDone();
return callback.result().status.IsInclude();
@@ -215,6 +224,8 @@ class CookieMonsterTestBase : public CookieStoreTest<T> {
std::string url_top_level_domain_plus_1(GURL(kTopLevelDomainPlus1).host());
std::string url_top_level_domain_plus_2(GURL(kTopLevelDomainPlus2).host());
std::string url_top_level_domain_plus_3(GURL(kTopLevelDomainPlus3).host());
+ std::string url_top_level_domain_secure(
+ GURL(kTopLevelDomainPlus2Secure).host());
std::string url_other(GURL(kOtherDomain).host());
this->DeleteAll(cm);
@@ -309,6 +320,18 @@ class CookieMonsterTestBase : public CookieStoreTest<T> {
base::Time(), base::Time(), false, false, CookieSameSite::LAX_MODE,
COOKIE_PRIORITY_DEFAULT, false));
+ // Partitioned cookies
+ cookies.push_back(CanonicalCookie::CreateUnsafeCookieForTesting(
+ "__Host-pc_1", "A", url_top_level_domain_secure, "/", now, base::Time(),
+ base::Time(), true, false, CookieSameSite::NO_RESTRICTION,
+ CookiePriority::COOKIE_PRIORITY_DEFAULT, false,
+ CookiePartitionKey::FromURLForTesting(GURL(kTopLevelDomainPlus1))));
+ cookies.push_back(CanonicalCookie::CreateUnsafeCookieForTesting(
+ "__Host-pc_2", "B", url_top_level_domain_secure, "/", now, base::Time(),
+ base::Time(), true, false, CookieSameSite::NO_RESTRICTION,
+ CookiePriority::COOKIE_PRIORITY_DEFAULT, false,
+ CookiePartitionKey::FromURLForTesting(GURL(kTopLevelDomainPlus1))));
+
for (auto& cookie : cookies) {
GURL source_url = cookie_util::SimulatedCookieSource(
*cookie, cookie->IsSecure() ? "https" : "http");
@@ -317,7 +340,7 @@ class CookieMonsterTestBase : public CookieStoreTest<T> {
}
EXPECT_EQ(cookies.size(), this->GetAllCookies(cm).size());
- return now + base::TimeDelta::FromMilliseconds(100);
+ return now + base::Milliseconds(100);
}
Time GetFirstCookieAccessDate(CookieMonster* cm) {
@@ -878,6 +901,29 @@ class CookieMonsterTestBase : public CookieStoreTest<T> {
70U);
}
+ // Test enforcement of the per-partition domain limit on partitioned cookies.
+ void TestPartitionedCookiesGarbageCollectionHelper() {
+ DCHECK_EQ(10u, CookieMonster::kPerPartitionDomainMaxCookies);
+ int max_cookies = CookieMonster::kPerPartitionDomainMaxCookies;
+ auto cm = std::make_unique<CookieMonster>(nullptr, &net_log_);
+
+ auto cookie_partition_key =
+ CookiePartitionKey::FromURLForTesting(GURL("https://toplevelsite.com"));
+ for (int i = 0; i < max_cookies + 5; ++i) {
+ std::string cookie = base::StringPrintf("__Host-a%02d=b", i);
+ EXPECT_TRUE(SetCookie(cm.get(), https_www_foo_.url(),
+ cookie + "; secure; path=/; partitioned",
+ cookie_partition_key));
+ std::string cookies =
+ this->GetCookies(cm.get(), https_www_foo_.url(),
+ CookiePartitionKeychain(cookie_partition_key));
+ EXPECT_NE(cookies.find(cookie), std::string::npos);
+ EXPECT_LE(CountInString(cookies, '='), max_cookies);
+ }
+ // TODO(crbug.com/1225444): Test recording stats for deleting partitioned
+ // cookies.
+ }
+
// Function for creating a CM with a number of cookies in it,
// no store (and hence no ability to affect access time).
CookieMonster* CreateMonsterForGC(int num_cookies) {
@@ -1006,12 +1052,12 @@ class DeferredCookieTaskTest : public CookieMonsterTest {
TEST_F(DeferredCookieTaskTest, DeferredGetCookieList) {
DeclareLoadedCookie(http_www_foo_.url(),
"X=1; path=/; expires=Mon, 18-Apr-22 22:50:14 GMT",
- Time::Now() + TimeDelta::FromDays(3));
+ Time::Now() + base::Days(3));
GetCookieListCallback call1;
cookie_monster_->GetCookieListWithOptionsAsync(
http_www_foo_.url(), CookieOptions::MakeAllInclusive(),
- call1.MakeCallback());
+ CookiePartitionKeychain(), call1.MakeCallback());
base::RunLoop().RunUntilIdle();
EXPECT_FALSE(call1.was_run());
@@ -1024,7 +1070,7 @@ TEST_F(DeferredCookieTaskTest, DeferredGetCookieList) {
GetCookieListCallback call2;
cookie_monster_->GetCookieListWithOptionsAsync(
http_www_foo_.url(), CookieOptions::MakeAllInclusive(),
- call2.MakeCallback());
+ CookiePartitionKeychain(), call2.MakeCallback());
// Already ready, no need for second load.
EXPECT_THAT(call2.cookies(), MatchesCookieLine("X=1"));
EXPECT_EQ("", TakeCommandSummary());
@@ -1096,7 +1142,7 @@ TEST_F(DeferredCookieTaskTest, DeferredSetAllCookies) {
TEST_F(DeferredCookieTaskTest, DeferredGetAllCookies) {
DeclareLoadedCookie(http_www_foo_.url(),
"X=1; path=/; expires=Mon, 18-Apr-22 22:50:14 GMT",
- Time::Now() + TimeDelta::FromDays(3));
+ Time::Now() + base::Days(3));
GetAllCookiesCallback call1;
cookie_monster_->GetAllCookiesAsync(call1.MakeCallback());
@@ -1118,12 +1164,12 @@ TEST_F(DeferredCookieTaskTest, DeferredGetAllCookies) {
TEST_F(DeferredCookieTaskTest, DeferredGetAllForUrlCookies) {
DeclareLoadedCookie(http_www_foo_.url(),
"X=1; path=/; expires=Mon, 18-Apr-22 22:50:14 GMT",
- Time::Now() + TimeDelta::FromDays(3));
+ Time::Now() + base::Days(3));
GetCookieListCallback call1;
cookie_monster_->GetCookieListWithOptionsAsync(
http_www_foo_.url(), CookieOptions::MakeAllInclusive(),
- call1.MakeCallback());
+ CookiePartitionKeychain(), call1.MakeCallback());
base::RunLoop().RunUntilIdle();
EXPECT_FALSE(call1.was_run());
@@ -1135,7 +1181,7 @@ TEST_F(DeferredCookieTaskTest, DeferredGetAllForUrlCookies) {
GetCookieListCallback call2;
cookie_monster_->GetCookieListWithOptionsAsync(
http_www_foo_.url(), CookieOptions::MakeAllInclusive(),
- call2.MakeCallback());
+ CookiePartitionKeychain(), call2.MakeCallback());
EXPECT_TRUE(call2.was_run());
EXPECT_THAT(call2.cookies(), MatchesCookieLine("X=1"));
EXPECT_EQ("", TakeCommandSummary());
@@ -1144,12 +1190,12 @@ TEST_F(DeferredCookieTaskTest, DeferredGetAllForUrlCookies) {
TEST_F(DeferredCookieTaskTest, DeferredGetAllForUrlWithOptionsCookies) {
DeclareLoadedCookie(http_www_foo_.url(),
"X=1; path=/; expires=Mon, 18-Apr-22 22:50:14 GMT",
- Time::Now() + TimeDelta::FromDays(3));
+ Time::Now() + base::Days(3));
GetCookieListCallback call1;
cookie_monster_->GetCookieListWithOptionsAsync(
http_www_foo_.url(), CookieOptions::MakeAllInclusive(),
- call1.MakeCallback());
+ CookiePartitionKeychain(), call1.MakeCallback());
base::RunLoop().RunUntilIdle();
EXPECT_FALSE(call1.was_run());
@@ -1161,7 +1207,7 @@ TEST_F(DeferredCookieTaskTest, DeferredGetAllForUrlWithOptionsCookies) {
GetCookieListCallback call2;
cookie_monster_->GetCookieListWithOptionsAsync(
http_www_foo_.url(), CookieOptions::MakeAllInclusive(),
- call2.MakeCallback());
+ CookiePartitionKeychain(), call2.MakeCallback());
EXPECT_TRUE(call2.was_run());
EXPECT_THAT(call2.cookies(), MatchesCookieLine("X=1"));
EXPECT_EQ("", TakeCommandSummary());
@@ -1170,7 +1216,7 @@ TEST_F(DeferredCookieTaskTest, DeferredGetAllForUrlWithOptionsCookies) {
TEST_F(DeferredCookieTaskTest, DeferredDeleteAllCookies) {
DeclareLoadedCookie(http_www_foo_.url(),
"X=1; path=/; expires=Mon, 18-Apr-22 22:50:14 GMT",
- Time::Now() + TimeDelta::FromDays(3));
+ Time::Now() + base::Days(3));
ResultSavingCookieCallback<uint32_t> call1;
cookie_monster_->DeleteAllAsync(call1.MakeCallback());
@@ -1307,7 +1353,7 @@ TEST_F(DeferredCookieTaskTest, DeferredTaskOrder) {
cookie_monster_->SetPersistSessionCookies(true);
DeclareLoadedCookie(http_www_foo_.url(),
"X=1; path=/; expires=Mon, 18-Apr-22 22:50:14 GMT",
- Time::Now() + TimeDelta::FromDays(3));
+ Time::Now() + base::Days(3));
bool get_cookie_list_callback_was_run = false;
GetCookieListCallback get_cookie_list_callback_deferred;
@@ -1315,6 +1361,7 @@ TEST_F(DeferredCookieTaskTest, DeferredTaskOrder) {
base::RunLoop run_loop;
cookie_monster_->GetCookieListWithOptionsAsync(
http_www_foo_.url(), CookieOptions::MakeAllInclusive(),
+ CookiePartitionKeychain(),
base::BindLambdaForTesting(
[&](const CookieAccessResultList& cookies,
const CookieAccessResultList& excluded_list) {
@@ -1331,6 +1378,7 @@ TEST_F(DeferredCookieTaskTest, DeferredTaskOrder) {
// before it.
cookie_monster_->GetCookieListWithOptionsAsync(
http_www_foo_.url(), CookieOptions::MakeAllInclusive(),
+ CookiePartitionKeychain(),
get_cookie_list_callback_deferred.MakeCallback());
run_loop.Quit();
@@ -1389,6 +1437,19 @@ TEST_F(CookieMonsterTest, TestCookieDeleteAll) {
EXPECT_EQ(CookieStoreCommand::REMOVE, store->commands()[1].type);
EXPECT_EQ("", GetCookiesWithOptions(cm.get(), http_www_foo_.url(), options));
+
+ // Create a Partitioned cookie.
+ auto cookie_partition_key =
+ CookiePartitionKey::FromURLForTesting(GURL("https://toplevelsite.com"));
+ EXPECT_TRUE(SetCookie(
+ cm.get(), https_www_foo_.url(),
+ "__Host-" + std::string(kValidCookieLine) + "; partitioned; secure",
+ cookie_partition_key));
+ EXPECT_EQ(1u, DeleteAll(cm.get()));
+ EXPECT_EQ(
+ "", GetCookiesWithOptions(cm.get(), http_www_foo_.url(), options,
+ CookiePartitionKeychain(cookie_partition_key)));
+ EXPECT_EQ(2u, store->commands().size());
}
TEST_F(CookieMonsterTest, TestCookieDeleteAllCreatedInTimeRangeTimestamps) {
@@ -1397,39 +1458,79 @@ TEST_F(CookieMonsterTest, TestCookieDeleteAllCreatedInTimeRangeTimestamps) {
Time now = Time::Now();
// Nothing has been added so nothing should be deleted.
- EXPECT_EQ(0u,
- DeleteAllCreatedInTimeRange(
- cm.get(), TimeRange(now - TimeDelta::FromDays(99), Time())));
+ EXPECT_EQ(0u, DeleteAllCreatedInTimeRange(
+ cm.get(), TimeRange(now - base::Days(99), Time())));
// Create 5 cookies with different creation dates.
EXPECT_TRUE(
SetCookieWithCreationTime(cm.get(), http_www_foo_.url(), "T-0=Now", now));
EXPECT_TRUE(SetCookieWithCreationTime(cm.get(), http_www_foo_.url(),
- "T-1=Yesterday",
- now - TimeDelta::FromDays(1)));
+ "T-1=Yesterday", now - base::Days(1)));
EXPECT_TRUE(SetCookieWithCreationTime(cm.get(), http_www_foo_.url(),
- "T-2=DayBefore",
- now - TimeDelta::FromDays(2)));
+ "T-2=DayBefore", now - base::Days(2)));
EXPECT_TRUE(SetCookieWithCreationTime(cm.get(), http_www_foo_.url(),
- "T-3=ThreeDays",
- now - TimeDelta::FromDays(3)));
+ "T-3=ThreeDays", now - base::Days(3)));
EXPECT_TRUE(SetCookieWithCreationTime(cm.get(), http_www_foo_.url(),
- "T-7=LastWeek",
- now - TimeDelta::FromDays(7)));
+ "T-7=LastWeek", now - base::Days(7)));
// Try to delete threedays and the daybefore.
- EXPECT_EQ(2u, DeleteAllCreatedInTimeRange(
- cm.get(), TimeRange(now - TimeDelta::FromDays(3),
- now - TimeDelta::FromDays(1))));
+ EXPECT_EQ(2u,
+ DeleteAllCreatedInTimeRange(
+ cm.get(), TimeRange(now - base::Days(3), now - base::Days(1))));
// Try to delete yesterday, also make sure that delete_end is not
// inclusive.
EXPECT_EQ(1u, DeleteAllCreatedInTimeRange(
- cm.get(), TimeRange(now - TimeDelta::FromDays(2), now)));
+ cm.get(), TimeRange(now - base::Days(2), now)));
// Make sure the delete_begin is inclusive.
EXPECT_EQ(1u, DeleteAllCreatedInTimeRange(
- cm.get(), TimeRange(now - TimeDelta::FromDays(7), now)));
+ cm.get(), TimeRange(now - base::Days(7), now)));
+
+ // Delete the last (now) item.
+ EXPECT_EQ(1u, DeleteAllCreatedInTimeRange(cm.get(), TimeRange()));
+
+ // Really make sure everything is gone.
+ EXPECT_EQ(0u, DeleteAll(cm.get()));
+
+ // Test the same deletion process with partitioned cookies. Partitioned
+ // cookies should behave the same way as unpartitioned cookies here, they are
+ // just stored in a different data structure internally.
+
+ EXPECT_TRUE(
+ SetCookieWithCreationTime(cm.get(), http_www_foo_.url(), "T-0=Now", now,
+ CookiePartitionKey::FromURLForTesting(
+ GURL("https://toplevelsite0.com"))));
+ EXPECT_TRUE(SetCookieWithCreationTime(
+ cm.get(), https_www_foo_.url(), "T-1=Yesterday", now - base::Days(1),
+ CookiePartitionKey::FromURLForTesting(
+ GURL("https://toplevelsite1.com"))));
+ EXPECT_TRUE(SetCookieWithCreationTime(
+ cm.get(), http_www_foo_.url(), "T-2=DayBefore", now - base::Days(2),
+ CookiePartitionKey::FromURLForTesting(
+ GURL("https://toplevelsite1.com"))));
+ EXPECT_TRUE(SetCookieWithCreationTime(
+ cm.get(), http_www_foo_.url(), "T-3=ThreeDays", now - base::Days(3),
+ CookiePartitionKey::FromURLForTesting(
+ GURL("https://toplevelsite2.com"))));
+ EXPECT_TRUE(SetCookieWithCreationTime(
+ cm.get(), http_www_foo_.url(), "T-7=LastWeek", now - base::Days(7),
+ CookiePartitionKey::FromURLForTesting(
+ GURL("https://toplevelsite3.com"))));
+
+ // Try to delete threedays and the daybefore.
+ EXPECT_EQ(2u,
+ DeleteAllCreatedInTimeRange(
+ cm.get(), TimeRange(now - base::Days(3), now - base::Days(1))));
+
+ // Try to delete yesterday, also make sure that delete_end is not
+ // inclusive.
+ EXPECT_EQ(1u, DeleteAllCreatedInTimeRange(
+ cm.get(), TimeRange(now - base::Days(2), now)));
+
+ // Make sure the delete_begin is inclusive.
+ EXPECT_EQ(1u, DeleteAllCreatedInTimeRange(
+ cm.get(), TimeRange(now - base::Days(7), now)));
// Delete the last (now) item.
EXPECT_EQ(1u, DeleteAllCreatedInTimeRange(cm.get(), TimeRange()));
@@ -1447,41 +1548,78 @@ TEST_F(CookieMonsterTest,
CanonicalCookie test_cookie;
// Nothing has been added so nothing should be deleted.
- EXPECT_EQ(0u, DeleteAllMatchingInfo(
- cm.get(),
- CookieDeletionInfo(now - TimeDelta::FromDays(99), Time())));
+ EXPECT_EQ(0u,
+ DeleteAllMatchingInfo(
+ cm.get(), CookieDeletionInfo(now - base::Days(99), Time())));
// Create 5 cookies with different creation dates.
EXPECT_TRUE(
SetCookieWithCreationTime(cm.get(), http_www_foo_.url(), "T-0=Now", now));
EXPECT_TRUE(SetCookieWithCreationTime(cm.get(), http_www_foo_.url(),
- "T-1=Yesterday",
- now - TimeDelta::FromDays(1)));
+ "T-1=Yesterday", now - base::Days(1)));
EXPECT_TRUE(SetCookieWithCreationTime(cm.get(), http_www_foo_.url(),
- "T-2=DayBefore",
- now - TimeDelta::FromDays(2)));
+ "T-2=DayBefore", now - base::Days(2)));
EXPECT_TRUE(SetCookieWithCreationTime(cm.get(), http_www_foo_.url(),
- "T-3=ThreeDays",
- now - TimeDelta::FromDays(3)));
+ "T-3=ThreeDays", now - base::Days(3)));
EXPECT_TRUE(SetCookieWithCreationTime(cm.get(), http_www_foo_.url(),
- "T-7=LastWeek",
- now - TimeDelta::FromDays(7)));
+ "T-7=LastWeek", now - base::Days(7)));
// Delete threedays and the daybefore.
- EXPECT_EQ(2u,
- DeleteAllMatchingInfo(
- cm.get(), CookieDeletionInfo(now - TimeDelta::FromDays(3),
- now - TimeDelta::FromDays(1))));
+ EXPECT_EQ(2u, DeleteAllMatchingInfo(cm.get(),
+ CookieDeletionInfo(now - base::Days(3),
+ now - base::Days(1))));
// Delete yesterday, also make sure that delete_end is not inclusive.
- EXPECT_EQ(
- 1u, DeleteAllMatchingInfo(
- cm.get(), CookieDeletionInfo(now - TimeDelta::FromDays(2), now)));
+ EXPECT_EQ(1u, DeleteAllMatchingInfo(
+ cm.get(), CookieDeletionInfo(now - base::Days(2), now)));
// Make sure the delete_begin is inclusive.
- EXPECT_EQ(
- 1u, DeleteAllMatchingInfo(
- cm.get(), CookieDeletionInfo(now - TimeDelta::FromDays(7), now)));
+ EXPECT_EQ(1u, DeleteAllMatchingInfo(
+ cm.get(), CookieDeletionInfo(now - base::Days(7), now)));
+
+ // Delete the last (now) item.
+ EXPECT_EQ(1u, DeleteAllMatchingInfo(cm.get(), CookieDeletionInfo()));
+
+ // Really make sure everything is gone.
+ EXPECT_EQ(0u, DeleteAll(cm.get()));
+
+ // Test the same deletion process with partitioned cookies. Partitioned
+ // cookies should behave the same way as unpartitioned cookies here, they are
+ // just stored in a different data structure internally.
+
+ EXPECT_TRUE(
+ SetCookieWithCreationTime(cm.get(), http_www_foo_.url(), "T-0=Now", now,
+ CookiePartitionKey::FromURLForTesting(
+ GURL("https://toplevelsite0.com"))));
+ EXPECT_TRUE(SetCookieWithCreationTime(
+ cm.get(), https_www_foo_.url(), "T-1=Yesterday", now - base::Days(1),
+ CookiePartitionKey::FromURLForTesting(
+ GURL("https://toplevelsite1.com"))));
+ EXPECT_TRUE(SetCookieWithCreationTime(
+ cm.get(), http_www_foo_.url(), "T-2=DayBefore", now - base::Days(2),
+ CookiePartitionKey::FromURLForTesting(
+ GURL("https://toplevelsite1.com"))));
+ EXPECT_TRUE(SetCookieWithCreationTime(
+ cm.get(), http_www_foo_.url(), "T-3=ThreeDays", now - base::Days(3),
+ CookiePartitionKey::FromURLForTesting(
+ GURL("https://toplevelsite2.com"))));
+ EXPECT_TRUE(SetCookieWithCreationTime(
+ cm.get(), http_www_foo_.url(), "T-7=LastWeek", now - base::Days(7),
+ CookiePartitionKey::FromURLForTesting(
+ GURL("https://toplevelsite3.com"))));
+
+ // Delete threedays and the daybefore.
+ EXPECT_EQ(2u, DeleteAllMatchingInfo(cm.get(),
+ CookieDeletionInfo(now - base::Days(3),
+ now - base::Days(1))));
+
+ // Delete yesterday, also make sure that delete_end is not inclusive.
+ EXPECT_EQ(1u, DeleteAllMatchingInfo(
+ cm.get(), CookieDeletionInfo(now - base::Days(2), now)));
+
+ // Make sure the delete_begin is inclusive.
+ EXPECT_EQ(1u, DeleteAllMatchingInfo(
+ cm.get(), CookieDeletionInfo(now - base::Days(7), now)));
// Delete the last (now) item.
EXPECT_EQ(1u, DeleteAllMatchingInfo(cm.get(), CookieDeletionInfo()));
@@ -1513,6 +1651,12 @@ TEST_F(CookieMonsterTest, TestCookieDeleteMatchingCookies) {
EXPECT_TRUE(SetCookieWithCreationTime(cm.get(), GURL("https://c.com"),
"c1=1;Secure", now));
+ // Set a partitioned cookie.
+ EXPECT_TRUE(SetCookieWithCreationTime(
+ cm.get(), GURL("https://d.com"),
+ "__Host-pc=123; path=/; secure; partitioned", now,
+ CookiePartitionKey::FromURLForTesting(GURL("https://e.com"))));
+
// Delete http cookies.
EXPECT_EQ(2u, DeleteMatchingCookies(
cm.get(),
@@ -1522,7 +1666,8 @@ TEST_F(CookieMonsterTest, TestCookieDeleteMatchingCookies) {
EXPECT_THAT(GetAllCookies(cm.get()),
ElementsAre(MatchesCookieNameDomain("a1", "a.com"),
MatchesCookieNameDomain("b1", "b.com"),
- MatchesCookieNameDomain("c1", "c.com")));
+ MatchesCookieNameDomain("c1", "c.com"),
+ MatchesCookieNameDomain("__Host-pc", "d.com")));
// Delete remaining cookie for a.com.
EXPECT_EQ(1u, DeleteMatchingCookies(
@@ -1532,7 +1677,15 @@ TEST_F(CookieMonsterTest, TestCookieDeleteMatchingCookies) {
})));
EXPECT_THAT(GetAllCookies(cm.get()),
ElementsAre(MatchesCookieNameDomain("b1", "b.com"),
- MatchesCookieNameDomain("c1", "c.com")));
+ MatchesCookieNameDomain("c1", "c.com"),
+ MatchesCookieNameDomain("__Host-pc", "d.com")));
+
+ // Delete the partitioned cookie.
+ EXPECT_EQ(1u, DeleteMatchingCookies(
+ cm.get(),
+ base::BindRepeating([](const net::CanonicalCookie& cookie) {
+ return cookie.IsPartitioned();
+ })));
// Delete the last two item.
EXPECT_EQ(2u, DeleteMatchingCookies(
@@ -1545,10 +1698,9 @@ TEST_F(CookieMonsterTest, TestCookieDeleteMatchingCookies) {
EXPECT_TRUE(GetAllCookies(cm.get()).empty());
}
-static const base::TimeDelta kLastAccessThreshold =
- base::TimeDelta::FromMilliseconds(200);
+static const base::TimeDelta kLastAccessThreshold = base::Milliseconds(200);
static const base::TimeDelta kAccessDelay =
- kLastAccessThreshold + base::TimeDelta::FromMilliseconds(20);
+ kLastAccessThreshold + base::Milliseconds(20);
TEST_F(CookieMonsterTest, TestLastAccess) {
std::unique_ptr<CookieMonster> cm(
@@ -1605,6 +1757,10 @@ TEST_F(CookieMonsterTest, TestPriorityAwareGarbageCollectionMixed) {
TestPriorityAwareGarbageCollectHelperMixed();
}
+TEST_F(CookieMonsterTest, TestPartitionedCookiesGarbageCollection) {
+ TestPartitionedCookiesGarbageCollectionHelper();
+}
+
TEST_F(CookieMonsterTest, SetCookieableSchemes) {
auto cm = std::make_unique<CookieMonster>(nullptr, &net_log_);
@@ -1692,6 +1848,23 @@ TEST_F(CookieMonsterTest, GetAllCookiesForURL) {
cm.get(), https_www_foo_.url(),
https_www_foo_.Format("I=J; domain=.%D; secure; sameparty"), options));
+ // Create partitioned cookies for the same site with some partition key.
+ auto cookie_partition_key1 =
+ CookiePartitionKey::FromURLForTesting(GURL("https://toplevelsite1.com"));
+ auto cookie_partition_key2 =
+ CookiePartitionKey::FromURLForTesting(GURL("https://toplevelsite2.com"));
+ auto cookie_partition_key3 =
+ CookiePartitionKey::FromURLForTesting(GURL("https://toplevelsite3.com"));
+ EXPECT_TRUE(CreateAndSetCookie(
+ cm.get(), https_www_bar_.url(), "__Host-K=L; secure; path=/; partitioned",
+ options, absl::nullopt, absl::nullopt, cookie_partition_key1));
+ EXPECT_TRUE(CreateAndSetCookie(
+ cm.get(), https_www_bar_.url(), "__Host-M=N; secure; path=/; partitioned",
+ options, absl::nullopt, absl::nullopt, cookie_partition_key2));
+ EXPECT_TRUE(CreateAndSetCookie(
+ cm.get(), https_www_bar_.url(), "__Host-O=P; secure; path=/; partitioned",
+ options, absl::nullopt, absl::nullopt, cookie_partition_key3));
+
base::HistogramTester histogram_tester;
const Time last_access_date(GetFirstCookieAccessDate(cm.get()));
@@ -1721,6 +1894,42 @@ TEST_F(CookieMonsterTest, GetAllCookiesForURL) {
MatchesCookieNameDomain("E", http_www_foo_.Format(".%D")),
MatchesCookieNameDomain("I", http_www_foo_.Format(".%D"))));
+ // Test reading partitioned cookies for a single partition.
+ EXPECT_THAT(
+ GetAllCookiesForURL(cm.get(), https_www_bar_.url(),
+ CookiePartitionKeychain(cookie_partition_key1)),
+ ElementsAre(MatchesCookieNameDomain("G", https_www_bar_.Format(".%D")),
+ MatchesCookieNameDomain("__Host-K", https_www_bar_.host())));
+ EXPECT_THAT(
+ GetAllCookiesForURL(cm.get(), https_www_bar_.url(),
+ CookiePartitionKeychain(cookie_partition_key2)),
+ ElementsAre(MatchesCookieNameDomain("G", https_www_bar_.Format(".%D")),
+ MatchesCookieNameDomain("__Host-M", https_www_bar_.host())));
+
+ // Test reading partitioned cookies from multiple partitions.
+ EXPECT_THAT(
+ GetAllCookiesForURL(cm.get(), https_www_bar_.url(),
+ CookiePartitionKeychain(
+ {cookie_partition_key1, cookie_partition_key2})),
+ ElementsAre(MatchesCookieNameDomain("G", https_www_bar_.Format(".%D")),
+ MatchesCookieNameDomain("__Host-K", https_www_bar_.host()),
+ MatchesCookieNameDomain("__Host-M", https_www_bar_.host())));
+
+ // Test reading partitioned cookies from every partition.
+ EXPECT_THAT(
+ GetAllCookiesForURL(cm.get(), https_www_bar_.url(),
+ CookiePartitionKeychain::ContainsAll()),
+ ElementsAre(MatchesCookieNameDomain("G", https_www_bar_.Format(".%D")),
+ MatchesCookieNameDomain("__Host-K", https_www_bar_.host()),
+ MatchesCookieNameDomain("__Host-M", https_www_bar_.host()),
+ MatchesCookieNameDomain("__Host-O", https_www_bar_.host())));
+
+ // Test excluding partitioned cookies.
+ EXPECT_THAT(
+ GetAllCookiesForURL(cm.get(), https_www_bar_.url(),
+ CookiePartitionKeychain()),
+ ElementsAre(MatchesCookieNameDomain("G", https_www_bar_.Format(".%D"))));
+
EXPECT_THAT(
histogram_tester.GetAllSamples("Cookie.SamePartyReadIncluded.IsHTTP"),
testing::ElementsAre(base::Bucket(1 /* min */, 1 /* samples */)));
@@ -1890,7 +2099,7 @@ TEST_F(CookieMonsterTest, CookieSorting) {
"A=A1; path=/", "A=A2; path=/foo", "A=A3; path=/foo/bar"}) {
EXPECT_TRUE(SetCookieWithSystemTime(cm.get(), http_www_foo_.url(),
cookie_line, system_time));
- system_time += base::TimeDelta::FromMilliseconds(100);
+ system_time += base::Milliseconds(100);
}
// Re-set cookie which should not change sort order, as the creation date
@@ -1911,8 +2120,7 @@ TEST_F(CookieMonsterTest, CookieSorting) {
TEST_F(CookieMonsterTest, InheritCreationDate) {
auto cm = std::make_unique<CookieMonster>(nullptr, &net_log_);
- base::Time the_not_so_distant_past(base::Time::Now() -
- base::TimeDelta::FromSeconds(1000));
+ base::Time the_not_so_distant_past(base::Time::Now() - base::Seconds(1000));
EXPECT_TRUE(SetCookieWithCreationTime(cm.get(), http_www_foo_.url(),
"Name=Value; path=/",
the_not_so_distant_past));
@@ -1955,6 +2163,31 @@ TEST_F(CookieMonsterTest, DeleteExpiredCookiesOnGet) {
cookies = GetAllCookiesForURL(cm.get(), http_www_foo_.url());
EXPECT_EQ(1u, cookies.size());
+
+ // Test partitioned cookies. They should exhibit the same behavior but are
+ // stored in a different data structure internally.
+ auto cookie_partition_key =
+ CookiePartitionKey::FromURLForTesting(GURL("https://toplevelsite.com"));
+
+ EXPECT_TRUE(SetCookie(cm.get(), https_www_bar_.url(),
+ "__Host-A=B; secure; path=/; partitioned",
+ cookie_partition_key));
+ EXPECT_TRUE(SetCookie(cm.get(), https_www_bar_.url(),
+ "__Host-C=D; secure; path=/; partitioned",
+ cookie_partition_key));
+
+ cookies = GetAllCookiesForURL(cm.get(), https_www_bar_.url(),
+ CookiePartitionKeychain(cookie_partition_key));
+ EXPECT_EQ(2u, cookies.size());
+
+ EXPECT_TRUE(SetCookie(cm.get(), https_www_bar_.url(),
+ "__Host-C=D; secure; path=/; partitioned; expires=Thu, "
+ "01-Jan-1970 00:00:00 GMT",
+ cookie_partition_key));
+
+ cookies = GetAllCookiesForURL(cm.get(), https_www_bar_.url(),
+ CookiePartitionKeychain(cookie_partition_key));
+ EXPECT_EQ(1u, cookies.size());
}
// Tests importing from a persistent cookie store that contains duplicate
@@ -1978,16 +2211,16 @@ TEST_F(CookieMonsterTest, DontImportDuplicateCookies) {
AddCookieToList(GURL("http://www.foo.com"),
"X=1; path=/; expires=Mon, 18-Apr-22 22:50:14 GMT",
- Time::Now() + TimeDelta::FromDays(3), &initial_cookies);
+ Time::Now() + base::Days(3), &initial_cookies);
AddCookieToList(GURL("http://www.foo.com"),
"X=2; path=/; expires=Mon, 18-Apr-22 22:50:14 GMT",
- Time::Now() + TimeDelta::FromDays(1), &initial_cookies);
+ Time::Now() + base::Days(1), &initial_cookies);
// ===> This one is the WINNER (biggest creation time). <====
AddCookieToList(GURL("http://www.foo.com"),
"X=3; path=/; expires=Mon, 18-Apr-22 22:50:14 GMT",
- Time::Now() + TimeDelta::FromDays(4), &initial_cookies);
+ Time::Now() + base::Days(4), &initial_cookies);
AddCookieToList(GURL("http://www.foo.com"),
"X=4; path=/; expires=Mon, 18-Apr-22 22:50:14 GMT",
@@ -1999,16 +2232,16 @@ TEST_F(CookieMonsterTest, DontImportDuplicateCookies) {
// ===> This one is the WINNER (biggest creation time). <====
AddCookieToList(GURL("http://www.foo.com"),
"X=a1; path=/2; expires=Mon, 18-Apr-22 22:50:14 GMT",
- Time::Now() + TimeDelta::FromDays(9), &initial_cookies);
+ Time::Now() + base::Days(9), &initial_cookies);
AddCookieToList(GURL("http://www.foo.com"),
"X=a2; path=/2; expires=Mon, 18-Apr-22 22:50:14 GMT",
- Time::Now() + TimeDelta::FromDays(2), &initial_cookies);
+ Time::Now() + base::Days(2), &initial_cookies);
// Insert 1 cookie with name "Y" on path "/".
AddCookieToList(GURL("http://www.foo.com"),
"Y=a; path=/; expires=Mon, 18-Apr-22 22:50:14 GMT",
- Time::Now() + TimeDelta::FromDays(10), &initial_cookies);
+ Time::Now() + base::Days(10), &initial_cookies);
// Inject our initial cookies into the mock PersistentCookieStore.
store->SetLoadExpectation(true, std::move(initial_cookies));
@@ -2032,6 +2265,47 @@ TEST_F(CookieMonsterTest, DontImportDuplicateCookies) {
EXPECT_EQ(CookieStoreCommand::REMOVE, store->commands()[3].type);
}
+TEST_F(CookieMonsterTest, DontImportDuplicateCookies_PartitionedCookies) {
+ std::vector<std::unique_ptr<CanonicalCookie>> initial_cookies;
+
+ auto cookie_partition_key =
+ CookiePartitionKey::FromURLForTesting(GURL("https://www.foo.com"));
+ GURL cookie_url("https://www.bar.com");
+
+ // Insert 3 partitioned cookies with same name, partition key, and path.
+
+ // ===> This one is the WINNER (biggest creation time). <====
+ auto cc = CanonicalCookie::Create(
+ cookie_url, "__Host-Z=a; Secure; Path=/; Partitioned; Max-Age=3456000",
+ Time::Now() + base::Days(2), absl::nullopt, cookie_partition_key);
+ initial_cookies.push_back(std::move(cc));
+
+ cc = CanonicalCookie::Create(
+ cookie_url, "__Host-Z=b; Secure; Path=/; Partitioned; Max-Age=3456000",
+ Time::Now(), absl::nullopt, cookie_partition_key);
+ initial_cookies.push_back(std::move(cc));
+
+ cc = CanonicalCookie::Create(
+ cookie_url, "__Host-Z=c; Secure; Path=/; Partitioned; Max-Age=3456000",
+ Time::Now() + base::Days(1), absl::nullopt, cookie_partition_key);
+ initial_cookies.push_back(std::move(cc));
+
+ scoped_refptr<MockPersistentCookieStore> store(new MockPersistentCookieStore);
+ std::unique_ptr<CookieMonster> cm(new CookieMonster(store.get(), &net_log_));
+
+ store->SetLoadExpectation(true, std::move(initial_cookies));
+
+ EXPECT_EQ("__Host-Z=a",
+ GetCookies(cm.get(), GURL("https://www.bar.com/"),
+ CookiePartitionKeychain(cookie_partition_key)));
+
+ // Verify that the PersistentCookieStore was told to kill the 2
+ // duplicates.
+ ASSERT_EQ(2u, store->commands().size());
+ EXPECT_EQ(CookieStoreCommand::REMOVE, store->commands()[0].type);
+ EXPECT_EQ(CookieStoreCommand::REMOVE, store->commands()[1].type);
+}
+
// Tests importing from a persistent cookie store that contains cookies
// with duplicate creation times. This is OK now, but it still interacts
// with the de-duplication algorithm.
@@ -2041,7 +2315,7 @@ TEST_F(CookieMonsterTest, ImportDuplicateCreationTimes) {
scoped_refptr<MockPersistentCookieStore> store(new MockPersistentCookieStore);
Time now(Time::Now());
- Time earlier(now - TimeDelta::FromDays(1));
+ Time earlier(now - base::Days(1));
// Insert 8 cookies, four with the current time as creation times, and
// four with the earlier time as creation times. We should only get
@@ -2081,6 +2355,63 @@ TEST_F(CookieMonsterTest, ImportDuplicateCreationTimes) {
EXPECT_NE(name1, name2);
}
+TEST_F(CookieMonsterTest, ImportDuplicateCreationTimes_PartitionedCookies) {
+ scoped_refptr<MockPersistentCookieStore> store(new MockPersistentCookieStore);
+
+ Time now(Time::Now());
+ Time earlier(now - base::Days(1));
+
+ GURL cookie_url("https://www.foo.com");
+ auto cookie_partition_key =
+ CookiePartitionKey::FromURLForTesting(GURL("https://www.bar.com"));
+
+ // Insert 6 cookies, four with the current time as creation times, and
+ // four with the earlier time as creation times. We should only get
+ // two cookies remaining, but which two (other than that there should
+ // be one from each set) will be random.
+
+ std::vector<std::unique_ptr<CanonicalCookie>> initial_cookies;
+ auto cc = CanonicalCookie::Create(
+ cookie_url, "__Host-X=1; Secure; Path=/; Partitioned; Max-Age=3456000",
+ now, absl::nullopt, cookie_partition_key);
+ initial_cookies.push_back(std::move(cc));
+ cc = CanonicalCookie::Create(
+ cookie_url, "__Host-X=2; Secure; Path=/; Partitioned; Max-Age=3456000",
+ now, absl::nullopt, cookie_partition_key);
+ initial_cookies.push_back(std::move(cc));
+ cc = CanonicalCookie::Create(
+ cookie_url, "__Host-X=3; Secure; Path=/; Partitioned; Max-Age=3456000",
+ now, absl::nullopt, cookie_partition_key);
+ initial_cookies.push_back(std::move(cc));
+
+ cc = CanonicalCookie::Create(
+ cookie_url, "__Host-Y=1; Secure; Path=/; Partitioned; Max-Age=3456000",
+ earlier, absl::nullopt, cookie_partition_key);
+ initial_cookies.push_back(std::move(cc));
+ cc = CanonicalCookie::Create(
+ cookie_url, "__Host-Y=2; Secure; Path=/; Partitioned; Max-Age=3456000",
+ earlier, absl::nullopt, cookie_partition_key);
+ initial_cookies.push_back(std::move(cc));
+ cc = CanonicalCookie::Create(
+ cookie_url, "__Host-Y=3; Secure; Path=/; Partitioned; Max-Age=3456000",
+ earlier, absl::nullopt, cookie_partition_key);
+ initial_cookies.push_back(std::move(cc));
+
+ // Inject our initial cookies into the mock PersistentCookieStore.
+ store->SetLoadExpectation(true, std::move(initial_cookies));
+
+ std::unique_ptr<CookieMonster> cm(new CookieMonster(store.get(), &net_log_));
+
+ CookieList list(GetAllCookies(cm.get()));
+ EXPECT_EQ(2U, list.size());
+ // Confirm that we have one of each.
+ std::string name1(list[0].Name());
+ std::string name2(list[1].Name());
+ EXPECT_TRUE(name1 == "__Host-X" || name2 == "__Host-X");
+ EXPECT_TRUE(name1 == "__Host-Y" || name2 == "__Host-Y");
+ EXPECT_NE(name1, name2);
+}
+
TEST_F(CookieMonsterTest, PredicateSeesAllCookies) {
auto cm = std::make_unique<CookieMonster>(nullptr, &net_log_);
@@ -2090,7 +2421,7 @@ TEST_F(CookieMonsterTest, PredicateSeesAllCookies) {
CookieDeletionInfo delete_info(base::Time(), now);
delete_info.value_for_testing = "A";
- EXPECT_EQ(8u, DeleteAllMatchingInfo(cm.get(), std::move(delete_info)));
+ EXPECT_EQ(9u, DeleteAllMatchingInfo(cm.get(), std::move(delete_info)));
EXPECT_EQ("dom_2=B; dom_3=C; host_3=C",
GetCookies(cm.get(), GURL(kTopLevelDomainPlus3)));
@@ -2100,6 +2431,11 @@ TEST_F(CookieMonsterTest, PredicateSeesAllCookies) {
EXPECT_EQ("dom_path_2=B; host_path_2=B; dom_2=B; host_2=B; sec_host=B",
GetCookies(cm.get(), GURL(kTopLevelDomainPlus2Secure +
std::string("/dir1/dir2/xxx"))));
+ EXPECT_EQ(
+ "dom_2=B; host_2=B; sec_host=B; __Host-pc_2=B",
+ GetCookies(cm.get(), GURL(kTopLevelDomainPlus2Secure),
+ CookiePartitionKeychain(CookiePartitionKey::FromURLForTesting(
+ GURL(kTopLevelDomainPlus1)))));
}
// Mainly a test of GetEffectiveDomain, or more specifically, of the
@@ -2127,23 +2463,25 @@ TEST_F(CookieMonsterTest, GetKey) {
}
// Test that cookies transfer from/to the backing store correctly.
+// TODO(crbug.com/1225444): Include partitioned cookies in this test when we
+// start saving them in the persistent store.
TEST_F(CookieMonsterTest, BackingStoreCommunication) {
// Store details for cookies transforming through the backing store interface.
base::Time current(base::Time::Now());
scoped_refptr<MockSimplePersistentCookieStore> store(
new MockSimplePersistentCookieStore);
- base::Time expires(base::Time::Now() + base::TimeDelta::FromSeconds(100));
+ base::Time expires(base::Time::Now() + base::Seconds(100));
const CookiesInputInfo input_info[] = {
{GURL("https://a.b.foo.com"), "a", "1", "a.b.foo.com", "/path/to/cookie",
expires, true /* secure */, false, CookieSameSite::NO_RESTRICTION,
COOKIE_PRIORITY_DEFAULT, false},
{GURL("https://www.foo.com"), "b", "2", ".foo.com", "/path/from/cookie",
- expires + TimeDelta::FromSeconds(10), true, true,
- CookieSameSite::NO_RESTRICTION, COOKIE_PRIORITY_DEFAULT, true},
+ expires + base::Seconds(10), true, true, CookieSameSite::NO_RESTRICTION,
+ COOKIE_PRIORITY_DEFAULT, true},
{GURL("https://foo.com"), "c", "3", "foo.com", "/another/path/to/cookie",
- base::Time::Now() + base::TimeDelta::FromSeconds(100), false, false,
+ base::Time::Now() + base::Seconds(100), false, false,
CookieSameSite::STRICT_MODE, COOKIE_PRIORITY_DEFAULT, false}};
const int INPUT_DELETE = 1;
@@ -2430,6 +2768,7 @@ TEST_F(CookieMonsterTest, WhileLoadingDeleteAllGetForURL) {
GetCookieListCallback get_cookie_list_callback;
cm->GetCookieListWithOptionsAsync(kUrl, CookieOptions::MakeAllInclusive(),
+ CookiePartitionKeychain(),
get_cookie_list_callback.MakeCallback());
// Only the main load should have been queued.
@@ -2618,13 +2957,18 @@ TEST_F(CookieMonsterTest, SetAllCookies) {
base::Time(), base::Time(), false, false, CookieSameSite::NO_RESTRICTION,
COOKIE_PRIORITY_DEFAULT, false));
list.push_back(*CanonicalCookie::CreateUnsafeCookieForTesting(
- "W", "X", "." + http_www_foo_.url().host(), "/bar", base::Time::Now(),
+ "C", "D", "." + http_www_foo_.url().host(), "/bar", base::Time::Now(),
base::Time(), base::Time(), false, false, CookieSameSite::NO_RESTRICTION,
COOKIE_PRIORITY_DEFAULT, false));
list.push_back(*CanonicalCookie::CreateUnsafeCookieForTesting(
- "Y", "Z", "." + http_www_foo_.url().host(), "/", base::Time::Now(),
+ "W", "X", "." + http_www_foo_.url().host(), "/", base::Time::Now(),
base::Time(), base::Time(), false, false, CookieSameSite::NO_RESTRICTION,
COOKIE_PRIORITY_DEFAULT, false));
+ list.push_back(*CanonicalCookie::CreateUnsafeCookieForTesting(
+ "__Host-Y", "Z", https_www_foo_.url().host(), "/", base::Time::Now(),
+ base::Time(), base::Time(), true, false, CookieSameSite::NO_RESTRICTION,
+ CookiePriority::COOKIE_PRIORITY_DEFAULT, false,
+ CookiePartitionKey::FromURLForTesting(GURL("https://toplevelsite.com"))));
// SetAllCookies must not flush.
ASSERT_EQ(0, store->flush_count());
@@ -2632,13 +2976,13 @@ TEST_F(CookieMonsterTest, SetAllCookies) {
EXPECT_EQ(0, store->flush_count());
CookieList cookies = GetAllCookies(cm.get());
- size_t expected_size = 3; // "A", "W" and "Y". "U" is gone.
+ size_t expected_size = 4; // "A", "W" and "Y". "U" is gone.
EXPECT_EQ(expected_size, cookies.size());
auto it = cookies.begin();
ASSERT_TRUE(it != cookies.end());
- EXPECT_EQ("W", it->Name());
- EXPECT_EQ("X", it->Value());
+ EXPECT_EQ("C", it->Name());
+ EXPECT_EQ("D", it->Value());
EXPECT_EQ("/bar", it->Path()); // The path has been updated.
ASSERT_TRUE(++it != cookies.end());
@@ -2646,7 +2990,11 @@ TEST_F(CookieMonsterTest, SetAllCookies) {
EXPECT_EQ("B", it->Value());
ASSERT_TRUE(++it != cookies.end());
- EXPECT_EQ("Y", it->Name());
+ EXPECT_EQ("W", it->Name());
+ EXPECT_EQ("X", it->Value());
+
+ ASSERT_TRUE(++it != cookies.end());
+ EXPECT_EQ("__Host-Y", it->Name());
EXPECT_EQ("Z", it->Value());
cm = nullptr;
@@ -2706,8 +3054,8 @@ TEST_F(CookieMonsterTest, HistogramCheck) {
expired_histogram->SnapshotSamples());
auto cookie = CanonicalCookie::CreateUnsafeCookieForTesting(
"a", "b", "a.url", "/", base::Time(),
- base::Time::Now() + base::TimeDelta::FromMinutes(59), base::Time(), true,
- false, CookieSameSite::NO_RESTRICTION, COOKIE_PRIORITY_DEFAULT, false);
+ base::Time::Now() + base::Minutes(59), base::Time(), true, false,
+ CookieSameSite::NO_RESTRICTION, COOKIE_PRIORITY_DEFAULT, false);
GURL source_url = cookie_util::SimulatedCookieSource(*cookie, "https");
ASSERT_TRUE(SetCanonicalCookie(cm.get(), std::move(cookie), source_url,
true /*modify_httponly*/));
@@ -2816,11 +3164,11 @@ TEST_F(CookieMonsterTest, PersisentCookieStorageTest) {
// See http://crbug.com/238041 for background.
TEST_F(CookieMonsterTest, ControlCharacterPurge) {
const Time now1(Time::Now());
- const Time now2(Time::Now() + TimeDelta::FromSeconds(1));
- const Time now3(Time::Now() + TimeDelta::FromSeconds(2));
- const Time now4(Time::Now() + TimeDelta::FromSeconds(3));
- const Time later(now1 + TimeDelta::FromDays(1));
- const GURL url("http://host/path");
+ const Time now2(Time::Now() + base::Seconds(1));
+ const Time now3(Time::Now() + base::Seconds(2));
+ const Time now4(Time::Now() + base::Seconds(3));
+ const Time later(now1 + base::Days(1));
+ const GURL url("https://host/path");
const std::string domain("host");
const std::string path("/path");
@@ -2852,6 +3200,19 @@ TEST_F(CookieMonsterTest, ControlCharacterPurge) {
COOKIE_PRIORITY_DEFAULT, false /* sameparty */);
initial_cookies.push_back(std::move(cc2));
+ // Partitioned cookies with control characters should not be loaded.
+ auto cookie_partition_key =
+ CookiePartitionKey::FromURLForTesting(GURL("https://toplevelsite.com"));
+ std::unique_ptr<CanonicalCookie> cc3 =
+ CanonicalCookie::CreateUnsafeCookieForTesting(
+ "__Host-baz",
+ "\x7F"
+ "boo",
+ domain, "/", now3, later, base::Time(), true /* secure */,
+ false /* httponly */, CookieSameSite::NO_RESTRICTION,
+ COOKIE_PRIORITY_DEFAULT, false /* sameparty */, cookie_partition_key);
+ initial_cookies.push_back(std::move(cc3));
+
AddCookieToList(url, "hello=world; path=" + path, now4, &initial_cookies);
// Inject our initial cookies into the mock PersistentCookieStore.
@@ -2859,7 +3220,9 @@ TEST_F(CookieMonsterTest, ControlCharacterPurge) {
std::unique_ptr<CookieMonster> cm(new CookieMonster(store.get(), &net_log_));
- EXPECT_EQ("foo=bar; hello=world", GetCookies(cm.get(), url));
+ EXPECT_EQ(
+ "foo=bar; hello=world",
+ GetCookies(cm.get(), url, CookiePartitionKeychain(cookie_partition_key)));
}
// Test that cookie source schemes are histogrammed correctly.
@@ -3067,9 +3430,9 @@ TEST_F(CookieMonsterTest, SecureCookieLocalhost) {
// status.
{
GetCookieListCallback callback;
- cm->GetCookieListWithOptionsAsync(insecure_localhost,
- CookieOptions::MakeAllInclusive(),
- callback.MakeCallback());
+ cm->GetCookieListWithOptionsAsync(
+ insecure_localhost, CookieOptions::MakeAllInclusive(),
+ CookiePartitionKeychain(), callback.MakeCallback());
callback.WaitUntilDone();
EXPECT_EQ(2u, callback.cookies_with_access_results().size());
for (const auto& cookie_item : callback.cookies_with_access_results()) {
@@ -3085,9 +3448,9 @@ TEST_F(CookieMonsterTest, SecureCookieLocalhost) {
// status.
{
GetCookieListCallback callback;
- cm->GetCookieListWithOptionsAsync(secure_localhost,
- CookieOptions::MakeAllInclusive(),
- callback.MakeCallback());
+ cm->GetCookieListWithOptionsAsync(
+ secure_localhost, CookieOptions::MakeAllInclusive(),
+ CookiePartitionKeychain(), callback.MakeCallback());
callback.WaitUntilDone();
EXPECT_EQ(2u, callback.cookies_with_access_results().size());
for (const auto& cookie_item : callback.cookies_with_access_results()) {
@@ -3202,6 +3565,48 @@ TEST_F(CookieMonsterTest, MaybeDeleteEquivalentCookieAndUpdateStatus) {
entries, 0, NetLogEventType::COOKIE_STORE_COOKIE_REJECTED_SECURE));
}
+TEST_F(CookieMonsterTest,
+ MaybeDeleteEquivalentCookieAndUpdateStatus_PartitionedCookies) {
+ scoped_refptr<MockPersistentCookieStore> store(new MockPersistentCookieStore);
+ std::unique_ptr<CookieMonster> cm(new CookieMonster(store.get(), &net_log_));
+
+ // Test adding two cookies with the same name, domain, and path but different
+ // partition keys.
+ auto cookie_partition_key1 =
+ CookiePartitionKey::FromURLForTesting(GURL("https://toplevelsite1.com"));
+
+ auto preexisting_cookie = CanonicalCookie::Create(
+ https_www_foo_.url(), "__Host-A=B; Secure; Path=/; Partitioned; HttpOnly",
+ base::Time::Now(), absl::nullopt /* server_time */,
+ cookie_partition_key1 /* cookie_partition_key */);
+ CookieAccessResult access_result = SetCanonicalCookieReturnAccessResult(
+ cm.get(), std::move(preexisting_cookie), https_www_foo_.url(),
+ true /* can_modify_httponly */);
+ ASSERT_TRUE(access_result.status.IsInclude());
+
+ // Should be able to set a cookie with a different partition key.
+ EXPECT_TRUE(SetCookie(cm.get(), https_www_foo_.url(),
+ "__Host-A=C; Secure; Path=/; Partitioned",
+ CookiePartitionKey::FromURLForTesting(
+ GURL("https://toplevelsite2.com"))));
+
+ // Should not overwrite HttpOnly cookie.
+ auto bad_cookie = CanonicalCookie::Create(
+ https_www_foo_.url(), "__Host-A=D; Secure; Path=/; Partitioned",
+ base::Time::Now(), absl::nullopt /* server_time */,
+ cookie_partition_key1);
+ access_result = SetCanonicalCookieReturnAccessResult(
+ cm.get(), std::move(bad_cookie), https_www_foo_.url(),
+ false /* can_modify_httponly */);
+ EXPECT_TRUE(access_result.status.HasExactlyExclusionReasonsForTesting(
+ {CookieInclusionStatus::EXCLUDE_OVERWRITE_HTTP_ONLY}));
+ EXPECT_THAT(
+ GetCookiesWithOptions(cm.get(), https_www_foo_.url(),
+ CookieOptions::MakeAllInclusive(),
+ CookiePartitionKeychain(cookie_partition_key1)),
+ ::testing::HasSubstr("A=B"));
+}
+
// Test skipping a cookie in MaybeDeleteEquivalentCookieAndUpdateStatus for
// multiple reasons (Secure and HttpOnly).
TEST_F(CookieMonsterTest, SkipDontOverwriteForMultipleReasons) {
@@ -3502,7 +3907,7 @@ TEST_F(CookieMonsterTest, LeaveSecureCookiesAlone_DomainMatch) {
const char* kDomain = "b.a.foo.com";
const char* kSubdomain = "c.b.a.foo.com";
// This domain does not match any, aside from the registrable domain.
- const char* kOtherDomain = "z.foo.com";
+ const char* kAnotherDomain = "z.foo.com";
for (const char* preexisting_cookie_host :
{kRegistrableDomain, kSuperdomain, kDomain, kSubdomain}) {
@@ -3591,7 +3996,7 @@ TEST_F(CookieMonsterTest, LeaveSecureCookiesAlone_DomainMatch) {
// Test non-domain-matching case. These sets should all be allowed because the
// cookie is not equivalent.
GURL nonmatching_https_url(base::StrCat(
- {url::kHttpsScheme, url::kStandardSchemeSeparator, kOtherDomain}));
+ {url::kHttpsScheme, url::kStandardSchemeSeparator, kAnotherDomain}));
for (const char* host : {kSuperdomain, kDomain, kSubdomain}) {
GURL https_url(
@@ -3605,7 +4010,7 @@ TEST_F(CookieMonsterTest, LeaveSecureCookiesAlone_DomainMatch) {
.IsInclude());
EXPECT_TRUE(CreateAndSetCookieReturnStatus(
cm.get(), nonmatching_https_url,
- base::StrCat({"B=0; Secure; Domain=", kOtherDomain}))
+ base::StrCat({"B=0; Secure; Domain=", kAnotherDomain}))
.IsInclude());
// New cookie from insecure URL is set.
@@ -3932,9 +4337,9 @@ TEST_F(CookieMonsterTest, SetCanonicalCookieDoesNotBlockForLoadAll) {
// Get cookies for a different URL.
GetCookieListCallback callback_get;
- cm.GetCookieListWithOptionsAsync(GURL("http://b.com/"),
- CookieOptions::MakeAllInclusive(),
- callback_get.MakeCallback());
+ cm.GetCookieListWithOptionsAsync(
+ GURL("http://b.com/"), CookieOptions::MakeAllInclusive(),
+ CookiePartitionKeychain(), callback_get.MakeCallback());
// Now go through the store commands, and execute individual loads.
const auto& commands = persistent_store->commands();
@@ -3999,7 +4404,7 @@ TEST_F(CookieMonsterTest, DeleteDuplicateCTime) {
TEST_F(CookieMonsterTest, DeleteCookieWithInheritedTimestamps) {
Time t1 = Time::Now();
- Time t2 = t1 + base::TimeDelta::FromSeconds(1);
+ Time t2 = t1 + base::Seconds(1);
GURL url("http://www.example.com");
std::string cookie_line = "foo=bar";
CookieOptions options = CookieOptions::MakeAllInclusive();
@@ -4257,7 +4662,7 @@ TEST_F(CookieMonsterNotificationTest, NoNotificationOnLoad) {
url, "Y=1; path=/", base::Time::Now(), absl::nullopt /* server_time */,
absl::nullopt /* cookie_partition_key */));
initial_cookies.push_back(CanonicalCookie::Create(
- url, "Y=2; path=/", base::Time::Now() + base::TimeDelta::FromDays(1),
+ url, "Y=2; path=/", base::Time::Now() + base::Days(1),
absl::nullopt /* server_time */,
absl::nullopt /* cookie_partition_key */));
diff --git a/chromium/net/cookies/cookie_partition_key.cc b/chromium/net/cookies/cookie_partition_key.cc
index 055866802c3..e0542e20816 100644
--- a/chromium/net/cookies/cookie_partition_key.cc
+++ b/chromium/net/cookies/cookie_partition_key.cc
@@ -35,6 +35,10 @@ bool CookiePartitionKey::operator==(const CookiePartitionKey& other) const {
return site_ == other.site_;
}
+bool CookiePartitionKey::operator!=(const CookiePartitionKey& other) const {
+ return site_ != other.site_;
+}
+
bool CookiePartitionKey::operator<(const CookiePartitionKey& other) const {
return site_ < other.site_;
}
@@ -46,6 +50,8 @@ bool CookiePartitionKey::Serialize(const absl::optional<CookiePartitionKey>& in,
out = kEmptyCookiePartitionKey;
return true;
}
+ if (!base::FeatureList::IsEnabled(features::kPartitionedCookies))
+ return false;
if (in->site_.GetURL().SchemeIsFile()) {
out = in->site_.SerializeFileSiteWithHost();
return true;
@@ -63,6 +69,8 @@ bool CookiePartitionKey::Deserialize(const std::string& in,
out = absl::nullopt;
return true;
}
+ if (!base::FeatureList::IsEnabled(features::kPartitionedCookies))
+ return false;
auto schemeful_site = SchemefulSite::Deserialize(in);
// SchemfulSite is opaque if the input is invalid.
if (schemeful_site.opaque())
diff --git a/chromium/net/cookies/cookie_partition_key.h b/chromium/net/cookies/cookie_partition_key.h
index 5f8207f727e..5ce04f20f14 100644
--- a/chromium/net/cookies/cookie_partition_key.h
+++ b/chromium/net/cookies/cookie_partition_key.h
@@ -13,23 +13,11 @@
#include "third_party/abseil-cpp/absl/types/optional.h"
#include "url/gurl.h"
-namespace network {
-namespace mojom {
-class CookiePartitionKeyDataView;
-} // namespace mojom
-} // namespace network
-
-namespace mojo {
-template <typename DataViewType, typename T>
-struct StructTraits;
-} // namespace mojo
-
namespace net {
class NET_EXPORT CookiePartitionKey {
public:
CookiePartitionKey();
- explicit CookiePartitionKey(const SchemefulSite& site);
CookiePartitionKey(const CookiePartitionKey& other);
CookiePartitionKey(CookiePartitionKey&& other);
CookiePartitionKey& operator=(const CookiePartitionKey& other);
@@ -37,6 +25,7 @@ class NET_EXPORT CookiePartitionKey {
~CookiePartitionKey();
bool operator==(const CookiePartitionKey& other) const;
+ bool operator!=(const CookiePartitionKey& other) const;
bool operator<(const CookiePartitionKey& other) const;
// Methods for serializing and deserializing a partition key to/from a string.
@@ -70,17 +59,23 @@ class NET_EXPORT CookiePartitionKey {
static absl::optional<CookiePartitionKey> FromNetworkIsolationKey(
const NetworkIsolationKey& network_isolation_key);
+ // Create a new CookiePartitionKey from the site of an existing
+ // CookiePartitionKey. This should only be used for sites of partition keys
+ // which were already created using Deserialize or FromNetworkIsolationKey.
+ static CookiePartitionKey FromWire(const SchemefulSite& site) {
+ return CookiePartitionKey(site);
+ }
+
// Temporary method, used to mark the places where we need to supply the
// cookie partition key to CanonicalCookie::Create.
static absl::optional<CookiePartitionKey> Todo() { return absl::nullopt; }
+ const SchemefulSite& site() const { return site_; }
+
private:
+ explicit CookiePartitionKey(const SchemefulSite& site);
explicit CookiePartitionKey(const GURL& url);
- // IPC needs access to internal site.
- friend struct mojo::StructTraits<network::mojom::CookiePartitionKeyDataView,
- CookiePartitionKey>;
-
SchemefulSite site_;
};
diff --git a/chromium/net/cookies/cookie_partition_key_fuzzer.cc b/chromium/net/cookies/cookie_partition_key_fuzzer.cc
index ddc089acd53..0770838160d 100644
--- a/chromium/net/cookies/cookie_partition_key_fuzzer.cc
+++ b/chromium/net/cookies/cookie_partition_key_fuzzer.cc
@@ -7,11 +7,17 @@
#include <fuzzer/FuzzedDataProvider.h>
+#include "base/test/scoped_feature_list.h"
+#include "net/base/features.h"
#include "net/cookies/cookie_partition_key.h"
+#include "url/origin.h"
namespace net {
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+ base::test::ScopedFeatureList scoped_feature_list;
+ scoped_feature_list.InitAndEnableFeature(features::kPartitionedCookies);
+
FuzzedDataProvider data_provider(data, size);
std::string url_str = data_provider.ConsumeRandomLengthString(800);
@@ -19,22 +25,19 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
if (!url.is_valid())
return 0;
- SchemefulSite site(url::Origin::Create(url));
absl::optional<CookiePartitionKey> partition_key =
- absl::make_optional(CookiePartitionKey(site));
-
- bool result = CookiePartitionKey::Deserialize(url_str, partition_key);
- if (site.opaque())
- CHECK(!result);
- else
- CHECK(result);
+ absl::make_optional(CookiePartitionKey::FromURLForTesting(url));
+ bool is_opaque = url::Origin::Create(url).opaque();
std::string tmp;
- result = CookiePartitionKey::Serialize(partition_key, tmp);
- if (site.opaque())
- CHECK(!result);
- else
- CHECK(result);
+ CHECK_NE(is_opaque, CookiePartitionKey::Serialize(partition_key, tmp));
+
+ CHECK_NE(is_opaque, CookiePartitionKey::Deserialize(url_str, partition_key));
+
+ if (!is_opaque) {
+ CHECK(absl::make_optional(CookiePartitionKey::FromURLForTesting(url)) ==
+ partition_key);
+ }
return 0;
}
diff --git a/chromium/net/cookies/cookie_partition_key_unittest.cc b/chromium/net/cookies/cookie_partition_key_unittest.cc
index 7f83ba7ccb9..44ea201e88f 100644
--- a/chromium/net/cookies/cookie_partition_key_unittest.cc
+++ b/chromium/net/cookies/cookie_partition_key_unittest.cc
@@ -63,8 +63,15 @@ TEST_P(CookiePartitionKeyTest, Serialization) {
for (const auto& tc : cases) {
std::string got;
- EXPECT_EQ(tc.expected_ret, CookiePartitionKey::Serialize(tc.input, got));
- EXPECT_EQ(tc.expected_output, got);
+ if (PartitionedCookiesEnabled()) {
+ EXPECT_EQ(tc.expected_ret, CookiePartitionKey::Serialize(tc.input, got));
+ EXPECT_EQ(tc.expected_output, got);
+ } else {
+ // Serialize should only return true for unpartitioned cookies if the
+ // feature is disabled.
+ EXPECT_NE(tc.input.has_value(),
+ CookiePartitionKey::Serialize(tc.input, got));
+ }
}
}
@@ -83,12 +90,20 @@ TEST_P(CookiePartitionKeyTest, Deserialization) {
for (const auto& tc : cases) {
absl::optional<CookiePartitionKey> got;
- EXPECT_EQ(tc.expected_ret, CookiePartitionKey::Deserialize(tc.input, got));
- if (tc.expected_output.has_value()) {
- EXPECT_TRUE(got.has_value());
- EXPECT_EQ(tc.expected_output.value(), got.value());
+ if (PartitionedCookiesEnabled()) {
+ EXPECT_EQ(tc.expected_ret,
+ CookiePartitionKey::Deserialize(tc.input, got));
+ if (tc.expected_output.has_value()) {
+ EXPECT_TRUE(got.has_value());
+ EXPECT_EQ(tc.expected_output.value(), got.value());
+ } else {
+ EXPECT_FALSE(got.has_value());
+ }
} else {
- EXPECT_FALSE(got.has_value());
+ // Deserialize should only return true for unpartitioned cookies if the
+ // feature is disabled.
+ EXPECT_EQ(tc.input == kEmptyCookiePartitionKey,
+ CookiePartitionKey::Deserialize(tc.input, got));
}
}
}
@@ -106,10 +121,17 @@ TEST_P(CookiePartitionKeyTest, FromNetworkIsolationKey) {
bool partitioned_cookies_enabled = PartitionedCookiesEnabled();
EXPECT_EQ(partitioned_cookies_enabled, got.has_value());
if (partitioned_cookies_enabled) {
- EXPECT_EQ(CookiePartitionKey(top_level_site), got.value());
+ EXPECT_EQ(CookiePartitionKey::FromURLForTesting(top_level_site.GetURL()),
+ got.value());
}
}
+TEST_P(CookiePartitionKeyTest, FromWire) {
+ auto want = CookiePartitionKey::FromURLForTesting(GURL("https://foo.com"));
+ auto got = CookiePartitionKey::FromWire(want.site());
+ EXPECT_EQ(want, got);
+}
+
} // namespace net
#endif // NET_COOKIES_COOKIE_PARTITION_KEY_UNITTEST_H_
diff --git a/chromium/net/cookies/cookie_partition_keychain.cc b/chromium/net/cookies/cookie_partition_keychain.cc
new file mode 100644
index 00000000000..8eb83dc7048
--- /dev/null
+++ b/chromium/net/cookies/cookie_partition_keychain.cc
@@ -0,0 +1,37 @@
+// Copyright 2021 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/cookies/cookie_partition_keychain.h"
+
+namespace net {
+
+CookiePartitionKeychain::CookiePartitionKeychain() = default;
+
+CookiePartitionKeychain::CookiePartitionKeychain(
+ const CookiePartitionKeychain& other) = default;
+
+CookiePartitionKeychain::CookiePartitionKeychain(
+ CookiePartitionKeychain&& other) = default;
+
+CookiePartitionKeychain::CookiePartitionKeychain(
+ const CookiePartitionKey& key) {
+ keys_.push_back(key);
+}
+
+CookiePartitionKeychain::CookiePartitionKeychain(
+ const std::vector<CookiePartitionKey>& keys)
+ : keys_(keys) {}
+
+CookiePartitionKeychain::CookiePartitionKeychain(bool contains_all_keys_)
+ : contains_all_keys_(contains_all_keys_) {}
+
+CookiePartitionKeychain& CookiePartitionKeychain::operator=(
+ const CookiePartitionKeychain& other) = default;
+
+CookiePartitionKeychain& CookiePartitionKeychain::operator=(
+ CookiePartitionKeychain&& other) = default;
+
+CookiePartitionKeychain::~CookiePartitionKeychain() = default;
+
+} // namespace net
diff --git a/chromium/net/cookies/cookie_partition_keychain.h b/chromium/net/cookies/cookie_partition_keychain.h
new file mode 100644
index 00000000000..beef4aa322e
--- /dev/null
+++ b/chromium/net/cookies/cookie_partition_keychain.h
@@ -0,0 +1,85 @@
+// Copyright 2021 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef NET_COOKIES_COOKIE_PARTITION_KEYCHAIN_H_
+#define NET_COOKIES_COOKIE_PARTITION_KEYCHAIN_H_
+
+#include <set>
+#include <vector>
+
+#include "net/base/net_export.h"
+#include "net/cookies/cookie_partition_key.h"
+
+namespace net {
+
+// A data structure used to represent a collection of cookie partition keys.
+//
+// It can represent all possible cookie partition keys when
+// `contains_all_keys_` is true.
+//
+// It can also represent a finite number of cookie partition keys, including
+// zero.
+// TODO(crbug.com/1225444): Consider changing the name of this class since the
+// term "keychain" has a certain meaning for iOS and macOS.
+class NET_EXPORT CookiePartitionKeychain {
+ public:
+ // Creates an empty keychain.
+ explicit CookiePartitionKeychain();
+ CookiePartitionKeychain(const CookiePartitionKeychain& other);
+ CookiePartitionKeychain(CookiePartitionKeychain&& other);
+ // Creates a keychain with a single element.
+ explicit CookiePartitionKeychain(const CookiePartitionKey& key);
+ // Creates a set that contains each partition key in the vector.
+ explicit CookiePartitionKeychain(const std::vector<CookiePartitionKey>& keys);
+
+ CookiePartitionKeychain& operator=(const CookiePartitionKeychain& other);
+ CookiePartitionKeychain& operator=(CookiePartitionKeychain&& other);
+ ~CookiePartitionKeychain();
+
+ static CookiePartitionKeychain ContainsAll() {
+ return CookiePartitionKeychain(true);
+ }
+
+ static CookiePartitionKeychain FromOptional(
+ const absl::optional<CookiePartitionKey>& opt_key) {
+ return opt_key ? CookiePartitionKeychain(opt_key.value())
+ : CookiePartitionKeychain();
+ }
+
+ // Temporary method used to record where we need to decide how to build the
+ // CookiePartitionKeychain.
+ //
+ // Returns an empty keychain, so no partitioned cookies will be returned at
+ // callsites this is used.
+ //
+ // TODO(crbug.com/1225444): Remove this method and update callsites to use
+ // appropriate constructor.
+ static CookiePartitionKeychain Todo() { return CookiePartitionKeychain(); }
+
+ // CookieMonster can check if the keychain is empty to avoid searching the
+ // PartitionedCookieMap at all.
+ bool IsEmpty() const { return !contains_all_keys_ && keys_.empty(); }
+
+ // Returns if the keychain contains every partition key.
+ bool ContainsAllKeys() const { return contains_all_keys_; }
+
+ // Iterate over all keys in the keychain, do not call this method if
+ // `contains_all_keys` is true.
+ const std::vector<CookiePartitionKey>& PartitionKeys() const {
+ DCHECK(!contains_all_keys_);
+ return keys_;
+ }
+
+ private:
+ explicit CookiePartitionKeychain(bool contains_all_keys_);
+
+ bool contains_all_keys_ = false;
+ // If `contains_all_keys_` is true, `keys_` must be empty.
+ // If `keys_` is not empty, then `contains_all_keys_` must be false.
+ std::vector<CookiePartitionKey> keys_;
+};
+
+} // namespace net
+
+#endif // NET_COOKIES_COOKIE_PARTITION_KEYCHAIN_H_
diff --git a/chromium/net/cookies/cookie_partition_keychain_unittest.cc b/chromium/net/cookies/cookie_partition_keychain_unittest.cc
new file mode 100644
index 00000000000..770d8cdee8a
--- /dev/null
+++ b/chromium/net/cookies/cookie_partition_keychain_unittest.cc
@@ -0,0 +1,75 @@
+// Copyright 2021 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef NET_COOKIES_COOKIE_PARTITION_KEYCHAIN_UNITTEST_H_
+#define NET_COOKIES_COOKIE_PARTITION_KEYCHAIN_UNITTEST_H_
+
+#include "net/cookies/cookie_partition_keychain.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace net {
+
+TEST(CookiePartitionKeychainTest, EmptySet) {
+ CookiePartitionKeychain keychain;
+
+ EXPECT_TRUE(keychain.IsEmpty());
+ EXPECT_FALSE(keychain.ContainsAllKeys());
+ EXPECT_EQ(0u, keychain.PartitionKeys().size());
+}
+
+TEST(CookiePartitionKeychainTest, SingletonSet) {
+ CookiePartitionKeychain keychain(
+ CookiePartitionKey::FromURLForTesting(GURL("https://www.foo.com")));
+
+ EXPECT_FALSE(keychain.IsEmpty());
+ EXPECT_FALSE(keychain.ContainsAllKeys());
+ EXPECT_THAT(
+ keychain.PartitionKeys(),
+ testing::UnorderedElementsAre(
+ CookiePartitionKey::FromURLForTesting(GURL("https://www.foo.com"))));
+}
+
+TEST(CookiePartitionKeychainTest, MultipleElements) {
+ CookiePartitionKeychain keychain({
+ CookiePartitionKey::FromURLForTesting(GURL("https://www.foo.com")),
+ CookiePartitionKey::FromURLForTesting(GURL("https://www.bar.com")),
+ });
+
+ EXPECT_FALSE(keychain.IsEmpty());
+ EXPECT_FALSE(keychain.ContainsAllKeys());
+ EXPECT_THAT(
+ keychain.PartitionKeys(),
+ testing::UnorderedElementsAre(
+ CookiePartitionKey::FromURLForTesting(
+ GURL("https://subdomain.foo.com")),
+ CookiePartitionKey::FromURLForTesting(GURL("https://www.bar.com"))));
+}
+
+TEST(CookiePartitionKeychainTest, ContainsAll) {
+ CookiePartitionKeychain keychain = CookiePartitionKeychain::ContainsAll();
+ EXPECT_FALSE(keychain.IsEmpty());
+ EXPECT_TRUE(keychain.ContainsAllKeys());
+}
+
+TEST(CookiePartitionKeychainTest, FromOptional) {
+ CookiePartitionKeychain keychain =
+ CookiePartitionKeychain::FromOptional(absl::nullopt);
+ EXPECT_TRUE(keychain.IsEmpty());
+ EXPECT_FALSE(keychain.ContainsAllKeys());
+
+ keychain = CookiePartitionKeychain::FromOptional(
+ absl::make_optional<CookiePartitionKey>(
+ CookiePartitionKey::FromURLForTesting(GURL("https://www.foo.com"))));
+ EXPECT_FALSE(keychain.IsEmpty());
+ EXPECT_FALSE(keychain.ContainsAllKeys());
+ EXPECT_THAT(
+ keychain.PartitionKeys(),
+ testing::UnorderedElementsAre(
+ CookiePartitionKey::FromURLForTesting(GURL("https://www.foo.com"))));
+}
+
+} // namespace net
+
+#endif // NET_COOKIES_COOKIE_PARTITION_KEYCHAIN_UNITTEST_H_
diff --git a/chromium/net/cookies/cookie_store.cc b/chromium/net/cookies/cookie_store.cc
index 6f7b1ca42e9..adc34fb0d41 100644
--- a/chromium/net/cookies/cookie_store.cc
+++ b/chromium/net/cookies/cookie_store.cc
@@ -45,8 +45,4 @@ void CookieStore::SetCookieAccessDelegate(
cookie_access_delegate_ = std::move(delegate);
}
-void CookieStore::DumpMemoryStats(
- base::trace_event::ProcessMemoryDump* pmd,
- const std::string& parent_absolute_name) const {}
-
} // namespace net
diff --git a/chromium/net/cookies/cookie_store.h b/chromium/net/cookies/cookie_store.h
index 0642e632ded..3a6c7c10d26 100644
--- a/chromium/net/cookies/cookie_store.h
+++ b/chromium/net/cookies/cookie_store.h
@@ -20,15 +20,10 @@
#include "net/cookies/cookie_access_result.h"
#include "net/cookies/cookie_deletion_info.h"
#include "net/cookies/cookie_options.h"
+#include "net/cookies/cookie_partition_keychain.h"
class GURL;
-namespace base {
-namespace trace_event {
-class ProcessMemoryDump;
-}
-} // namespace base
-
namespace net {
class CookieChangeDispatcher;
@@ -79,9 +74,13 @@ class NET_EXPORT CookieStore {
// creation date.
// To get all the cookies for a URL, use this method with an all-inclusive
// |options|.
+ // If |cookie_partition_keychain| is not empty, then this function will return
+ // the partitioned cookies for that URL whose partition keys are in the
+ // keychain *in addition to* the unpartitioned cookies for that URL.
virtual void GetCookieListWithOptionsAsync(
const GURL& url,
const CookieOptions& options,
+ const CookiePartitionKeychain& cookie_partition_keychain,
GetCookieListCallback callback) = 0;
// Returns all the cookies, for use in management UI, etc. This does not mark
@@ -155,10 +154,6 @@ class NET_EXPORT CookieStore {
// Transfer ownership of a CookieAccessDelegate.
void SetCookieAccessDelegate(std::unique_ptr<CookieAccessDelegate> delegate);
- // Reports the estimate of dynamically allocated memory in bytes.
- virtual void DumpMemoryStats(base::trace_event::ProcessMemoryDump* pmd,
- const std::string& parent_absolute_name) const;
-
// This may be null if no delegate has been set yet, or the delegate has been
// reset to null.
const CookieAccessDelegate* cookie_access_delegate() const {
diff --git a/chromium/net/cookies/cookie_store_change_unittest.h b/chromium/net/cookies/cookie_store_change_unittest.h
index 5c742e776fc..47e5fb28521 100644
--- a/chromium/net/cookies/cookie_store_change_unittest.h
+++ b/chromium/net/cookies/cookie_store_change_unittest.h
@@ -715,7 +715,7 @@ TYPED_TEST_P(CookieStoreChangeUrlTest, NoCookie) {
std::vector<CookieChangeInfo> cookie_changes;
std::unique_ptr<CookieChangeSubscription> subscription =
cs->GetChangeDispatcher().AddCallbackForUrl(
- this->http_www_foo_.url(),
+ this->http_www_foo_.url(), absl::nullopt /* cookie_partition_key */,
base::BindRepeating(
&CookieStoreChangeTestBase<TypeParam>::OnCookieChange,
base::Unretained(&cookie_changes)));
@@ -733,7 +733,7 @@ TYPED_TEST_P(CookieStoreChangeUrlTest, InitialCookie) {
this->DeliverChangeNotifications();
std::unique_ptr<CookieChangeSubscription> subscription =
cs->GetChangeDispatcher().AddCallbackForUrl(
- this->http_www_foo_.url(),
+ this->http_www_foo_.url(), absl::nullopt /* cookie_partition_key */,
base::BindRepeating(
&CookieStoreChangeTestBase<TypeParam>::OnCookieChange,
base::Unretained(&cookie_changes)));
@@ -749,7 +749,7 @@ TYPED_TEST_P(CookieStoreChangeUrlTest, InsertOne) {
std::vector<CookieChangeInfo> cookie_changes;
std::unique_ptr<CookieChangeSubscription> subscription =
cs->GetChangeDispatcher().AddCallbackForUrl(
- this->http_www_foo_.url(),
+ this->http_www_foo_.url(), absl::nullopt /* cookie_partition_key */,
base::BindRepeating(
&CookieStoreChangeTestBase<TypeParam>::OnCookieChange,
base::Unretained(&cookie_changes)));
@@ -776,7 +776,7 @@ TYPED_TEST_P(CookieStoreChangeUrlTest, InsertMany) {
std::vector<CookieChangeInfo> cookie_changes;
std::unique_ptr<CookieChangeSubscription> subscription =
cs->GetChangeDispatcher().AddCallbackForUrl(
- this->http_www_foo_.url(),
+ this->http_www_foo_.url(), absl::nullopt /* cookie_partition_key */,
base::BindRepeating(
&CookieStoreChangeTestBase<TypeParam>::OnCookieChange,
base::Unretained(&cookie_changes)));
@@ -820,7 +820,7 @@ TYPED_TEST_P(CookieStoreChangeUrlTest, InsertFiltering) {
std::vector<CookieChangeInfo> cookie_changes;
std::unique_ptr<CookieChangeSubscription> subscription =
cs->GetChangeDispatcher().AddCallbackForUrl(
- this->www_foo_foo_.url(),
+ this->www_foo_foo_.url(), absl::nullopt /* cookie_partition_key */,
base::BindRepeating(
&CookieStoreChangeTestBase<TypeParam>::OnCookieChange,
base::Unretained(&cookie_changes)));
@@ -874,7 +874,7 @@ TYPED_TEST_P(CookieStoreChangeUrlTest, DeleteOne) {
std::vector<CookieChangeInfo> cookie_changes;
std::unique_ptr<CookieChangeSubscription> subscription =
cs->GetChangeDispatcher().AddCallbackForUrl(
- this->http_www_foo_.url(),
+ this->http_www_foo_.url(), absl::nullopt /* cookie_partition_key */,
base::BindRepeating(
&CookieStoreChangeTestBase<TypeParam>::OnCookieChange,
base::Unretained(&cookie_changes)));
@@ -904,7 +904,7 @@ TYPED_TEST_P(CookieStoreChangeUrlTest, DeleteTwo) {
std::vector<CookieChangeInfo> cookie_changes;
std::unique_ptr<CookieChangeSubscription> subscription =
cs->GetChangeDispatcher().AddCallbackForUrl(
- this->http_www_foo_.url(),
+ this->http_www_foo_.url(), absl::nullopt /* cookie_partition_key */,
base::BindRepeating(
&CookieStoreChangeTestBase<TypeParam>::OnCookieChange,
base::Unretained(&cookie_changes)));
@@ -953,7 +953,7 @@ TYPED_TEST_P(CookieStoreChangeUrlTest, DeleteFiltering) {
std::vector<CookieChangeInfo> cookie_changes;
std::unique_ptr<CookieChangeSubscription> subscription =
cs->GetChangeDispatcher().AddCallbackForUrl(
- this->www_foo_foo_.url(),
+ this->www_foo_foo_.url(), absl::nullopt /* cookie_partition_key */,
base::BindRepeating(
&CookieStoreChangeTestBase<TypeParam>::OnCookieChange,
base::Unretained(&cookie_changes)));
@@ -1019,7 +1019,7 @@ TYPED_TEST_P(CookieStoreChangeUrlTest, Overwrite) {
std::vector<CookieChangeInfo> cookie_changes;
std::unique_ptr<CookieChangeSubscription> subscription =
cs->GetChangeDispatcher().AddCallbackForUrl(
- this->http_www_foo_.url(),
+ this->http_www_foo_.url(), absl::nullopt /* cookie_partition_key */,
base::BindRepeating(
&CookieStoreChangeTestBase<TypeParam>::OnCookieChange,
base::Unretained(&cookie_changes)));
@@ -1063,7 +1063,7 @@ TYPED_TEST_P(CookieStoreChangeUrlTest, OverwriteFiltering) {
std::vector<CookieChangeInfo> cookie_changes;
std::unique_ptr<CookieChangeSubscription> subscription =
cs->GetChangeDispatcher().AddCallbackForUrl(
- this->www_foo_foo_.url(),
+ this->www_foo_foo_.url(), absl::nullopt /* cookie_partition_key */,
base::BindRepeating(
&CookieStoreChangeTestBase<TypeParam>::OnCookieChange,
base::Unretained(&cookie_changes)));
@@ -1159,7 +1159,7 @@ TYPED_TEST_P(CookieStoreChangeUrlTest, OverwriteWithHttpOnly) {
std::vector<CookieChangeInfo> cookie_changes;
std::unique_ptr<CookieChangeSubscription> subscription =
cs->GetChangeDispatcher().AddCallbackForUrl(
- this->www_foo_foo_.url(),
+ this->www_foo_foo_.url(), absl::nullopt /* cookie_partition_key */,
base::BindRepeating(
&CookieStoreChangeTestBase<TypeParam>::OnCookieChange,
base::Unretained(&cookie_changes)));
@@ -1220,7 +1220,7 @@ TYPED_TEST_P(CookieStoreChangeUrlTest, Deregister) {
std::vector<CookieChangeInfo> cookie_changes;
std::unique_ptr<CookieChangeSubscription> subscription =
cs->GetChangeDispatcher().AddCallbackForUrl(
- this->http_www_foo_.url(),
+ this->http_www_foo_.url(), absl::nullopt /* cookie_partition_key */,
base::BindRepeating(
&CookieStoreChangeTestBase<TypeParam>::OnCookieChange,
base::Unretained(&cookie_changes)));
@@ -1256,13 +1256,13 @@ TYPED_TEST_P(CookieStoreChangeUrlTest, DeregisterMultiple) {
std::vector<CookieChangeInfo> cookie_changes_1, cookie_changes_2;
std::unique_ptr<CookieChangeSubscription> subscription1 =
cs->GetChangeDispatcher().AddCallbackForUrl(
- this->http_www_foo_.url(),
+ this->http_www_foo_.url(), absl::nullopt /* cookie_partition_key */,
base::BindRepeating(
&CookieStoreChangeTestBase<TypeParam>::OnCookieChange,
base::Unretained(&cookie_changes_1)));
std::unique_ptr<CookieChangeSubscription> subscription2 =
cs->GetChangeDispatcher().AddCallbackForUrl(
- this->http_www_foo_.url(),
+ this->http_www_foo_.url(), absl::nullopt /* cookie_partition_key */,
base::BindRepeating(
&CookieStoreChangeTestBase<TypeParam>::OnCookieChange,
base::Unretained(&cookie_changes_2)));
@@ -1313,7 +1313,7 @@ TYPED_TEST_P(CookieStoreChangeUrlTest, DispatchRace) {
std::vector<CookieChangeInfo> cookie_changes;
std::unique_ptr<CookieChangeSubscription> subscription =
cs->GetChangeDispatcher().AddCallbackForUrl(
- this->http_www_foo_.url(),
+ this->http_www_foo_.url(), absl::nullopt /* cookie_partition_key */,
base::BindRepeating(
&CookieStoreChangeTestBase<TypeParam>::OnCookieChange,
base::Unretained(&cookie_changes)));
@@ -1340,7 +1340,7 @@ TYPED_TEST_P(CookieStoreChangeUrlTest, DeregisterRace) {
std::vector<CookieChangeInfo> cookie_changes;
std::unique_ptr<CookieChangeSubscription> subscription =
cs->GetChangeDispatcher().AddCallbackForUrl(
- this->http_www_foo_.url(),
+ this->http_www_foo_.url(), absl::nullopt /* cookie_partition_key */,
base::BindRepeating(
&CookieStoreChangeTestBase<TypeParam>::OnCookieChange,
base::Unretained(&cookie_changes)));
@@ -1385,13 +1385,13 @@ TYPED_TEST_P(CookieStoreChangeUrlTest, DeregisterRaceMultiple) {
std::vector<CookieChangeInfo> cookie_changes_1, cookie_changes_2;
std::unique_ptr<CookieChangeSubscription> subscription1 =
cs->GetChangeDispatcher().AddCallbackForUrl(
- this->http_www_foo_.url(),
+ this->http_www_foo_.url(), absl::nullopt /* cookie_partition_key */,
base::BindRepeating(
&CookieStoreChangeTestBase<TypeParam>::OnCookieChange,
base::Unretained(&cookie_changes_1)));
std::unique_ptr<CookieChangeSubscription> subscription2 =
cs->GetChangeDispatcher().AddCallbackForUrl(
- this->http_www_foo_.url(),
+ this->http_www_foo_.url(), absl::nullopt /* cookie_partition_key */,
base::BindRepeating(
&CookieStoreChangeTestBase<TypeParam>::OnCookieChange,
base::Unretained(&cookie_changes_2)));
@@ -1446,13 +1446,13 @@ TYPED_TEST_P(CookieStoreChangeUrlTest, DifferentSubscriptionsDisjoint) {
std::vector<CookieChangeInfo> cookie_changes_1, cookie_changes_2;
std::unique_ptr<CookieChangeSubscription> subscription1 =
cs->GetChangeDispatcher().AddCallbackForUrl(
- this->http_www_foo_.url(),
+ this->http_www_foo_.url(), absl::nullopt /* cookie_partition_key */,
base::BindRepeating(
&CookieStoreChangeTestBase<TypeParam>::OnCookieChange,
base::Unretained(&cookie_changes_1)));
std::unique_ptr<CookieChangeSubscription> subscription2 =
cs->GetChangeDispatcher().AddCallbackForUrl(
- this->http_bar_com_.url(),
+ this->http_bar_com_.url(), absl::nullopt /* cookie_partition_key */,
base::BindRepeating(
&CookieStoreChangeTestBase<TypeParam>::OnCookieChange,
base::Unretained(&cookie_changes_2)));
@@ -1490,13 +1490,13 @@ TYPED_TEST_P(CookieStoreChangeUrlTest, DifferentSubscriptionsDomains) {
std::vector<CookieChangeInfo> cookie_changes_1, cookie_changes_2;
std::unique_ptr<CookieChangeSubscription> subscription1 =
cs->GetChangeDispatcher().AddCallbackForUrl(
- this->http_www_foo_.url(),
+ this->http_www_foo_.url(), absl::nullopt /* cookie_partition_key */,
base::BindRepeating(
&CookieStoreChangeTestBase<TypeParam>::OnCookieChange,
base::Unretained(&cookie_changes_1)));
std::unique_ptr<CookieChangeSubscription> subscription2 =
cs->GetChangeDispatcher().AddCallbackForUrl(
- this->http_bar_com_.url(),
+ this->http_bar_com_.url(), absl::nullopt /* cookie_partition_key */,
base::BindRepeating(
&CookieStoreChangeTestBase<TypeParam>::OnCookieChange,
base::Unretained(&cookie_changes_2)));
@@ -1534,13 +1534,13 @@ TYPED_TEST_P(CookieStoreChangeUrlTest, DifferentSubscriptionsPaths) {
std::vector<CookieChangeInfo> cookie_changes_1, cookie_changes_2;
std::unique_ptr<CookieChangeSubscription> subscription1 =
cs->GetChangeDispatcher().AddCallbackForUrl(
- this->http_www_foo_.url(),
+ this->http_www_foo_.url(), absl::nullopt /* cookie_partition_key */,
base::BindRepeating(
&CookieStoreChangeTestBase<TypeParam>::OnCookieChange,
base::Unretained(&cookie_changes_1)));
std::unique_ptr<CookieChangeSubscription> subscription2 =
cs->GetChangeDispatcher().AddCallbackForUrl(
- this->www_foo_foo_.url(),
+ this->www_foo_foo_.url(), absl::nullopt /* cookie_partition_key */,
base::BindRepeating(
&CookieStoreChangeTestBase<TypeParam>::OnCookieChange,
base::Unretained(&cookie_changes_2)));
@@ -1590,19 +1590,19 @@ TYPED_TEST_P(CookieStoreChangeUrlTest, DifferentSubscriptionsFiltering) {
std::vector<CookieChangeInfo> cookie_changes_3;
std::unique_ptr<CookieChangeSubscription> subscription1 =
cs->GetChangeDispatcher().AddCallbackForUrl(
- this->http_www_foo_.url(),
+ this->http_www_foo_.url(), absl::nullopt /* cookie_partition_key */,
base::BindRepeating(
&CookieStoreChangeTestBase<TypeParam>::OnCookieChange,
base::Unretained(&cookie_changes_1)));
std::unique_ptr<CookieChangeSubscription> subscription2 =
cs->GetChangeDispatcher().AddCallbackForUrl(
- this->http_bar_com_.url(),
+ this->http_bar_com_.url(), absl::nullopt /* cookie_partition_key */,
base::BindRepeating(
&CookieStoreChangeTestBase<TypeParam>::OnCookieChange,
base::Unretained(&cookie_changes_2)));
std::unique_ptr<CookieChangeSubscription> subscription3 =
cs->GetChangeDispatcher().AddCallbackForUrl(
- this->www_foo_foo_.url(),
+ this->www_foo_foo_.url(), absl::nullopt /* cookie_partition_key */,
base::BindRepeating(
&CookieStoreChangeTestBase<TypeParam>::OnCookieChange,
base::Unretained(&cookie_changes_3)));
@@ -1667,13 +1667,13 @@ TYPED_TEST_P(CookieStoreChangeUrlTest, MultipleSubscriptions) {
std::vector<CookieChangeInfo> cookie_changes_1, cookie_changes_2;
std::unique_ptr<CookieChangeSubscription> subscription1 =
cs->GetChangeDispatcher().AddCallbackForUrl(
- this->http_www_foo_.url(),
+ this->http_www_foo_.url(), absl::nullopt /* cookie_partition_key */,
base::BindRepeating(
&CookieStoreChangeTestBase<TypeParam>::OnCookieChange,
base::Unretained(&cookie_changes_1)));
std::unique_ptr<CookieChangeSubscription> subscription2 =
cs->GetChangeDispatcher().AddCallbackForUrl(
- this->http_www_foo_.url(),
+ this->http_www_foo_.url(), absl::nullopt /* cookie_partition_key */,
base::BindRepeating(
&CookieStoreChangeTestBase<TypeParam>::OnCookieChange,
base::Unretained(&cookie_changes_2)));
@@ -1706,7 +1706,7 @@ TYPED_TEST_P(CookieStoreChangeUrlTest, ChangeIncludesCookieAccessSemantics) {
std::vector<CookieChangeInfo> cookie_changes;
std::unique_ptr<CookieChangeSubscription> subscription =
cs->GetChangeDispatcher().AddCallbackForUrl(
- GURL("http://domain1.test"),
+ GURL("http://domain1.test"), absl::nullopt /* cookie_partition_key */,
base::BindRepeating(
&CookieStoreChangeTestBase<TypeParam>::OnCookieChange,
base::Unretained(&cookie_changes)));
@@ -1723,6 +1723,77 @@ TYPED_TEST_P(CookieStoreChangeUrlTest, ChangeIncludesCookieAccessSemantics) {
cookie_changes[0].access_result.access_semantics));
}
+TYPED_TEST_P(CookieStoreChangeUrlTest, PartitionedCookies) {
+ if (!TypeParam::supports_url_cookie_tracking ||
+ !TypeParam::supports_partitioned_cookies)
+ return;
+
+ CookieStore* cs = this->GetCookieStore();
+ std::vector<CookieChangeInfo> cookie_changes;
+ std::unique_ptr<CookieChangeSubscription> subscription =
+ cs->GetChangeDispatcher().AddCallbackForUrl(
+ GURL("https://www.example.com/"),
+ absl::make_optional(CookiePartitionKey::FromURLForTesting(
+ GURL("https://www.foo.com"))),
+ base::BindRepeating(
+ &CookieStoreChangeTestBase<TypeParam>::OnCookieChange,
+ base::Unretained(&cookie_changes)));
+
+ // Unpartitioned cookie
+ this->CreateAndSetCookie(cs, GURL("https://www.example.com/"),
+ "__Host-a=1; Secure; Path=/",
+ CookieOptions::MakeAllInclusive());
+ // Partitioned cookie with the same partition key
+ this->CreateAndSetCookie(
+ cs, GURL("https://www.example.com/"),
+ "__Host-b=2; Secure; Path=/; Partitioned",
+ CookieOptions::MakeAllInclusive(), absl::nullopt /* server_time */,
+ absl::nullopt /* system_time */,
+ absl::make_optional(
+ CookiePartitionKey::FromURLForTesting(GURL("https://sub.foo.com"))));
+ // Partitioned cookie with a different partition key
+ this->CreateAndSetCookie(
+ cs, GURL("https://www.example.com"),
+ "__Host-c=3; Secure; Path=/; Partitioned",
+ CookieOptions::MakeAllInclusive(), absl::nullopt /* server_time */,
+ absl::nullopt /* system_time */,
+ absl::make_optional(
+ CookiePartitionKey::FromURLForTesting(GURL("https://www.bar.com"))));
+ this->DeliverChangeNotifications();
+
+ ASSERT_EQ(2u, cookie_changes.size());
+ EXPECT_FALSE(cookie_changes[0].cookie.IsPartitioned());
+ EXPECT_EQ("__Host-a", cookie_changes[0].cookie.Name());
+ EXPECT_TRUE(cookie_changes[1].cookie.IsPartitioned());
+ EXPECT_EQ(CookiePartitionKey::FromURLForTesting(GURL("https://www.foo.com")),
+ cookie_changes[1].cookie.PartitionKey().value());
+ EXPECT_EQ("__Host-b", cookie_changes[1].cookie.Name());
+
+ // Test that when the partition key parameter is nullopt that all Partitioned
+ // cookies do not emit events.
+
+ std::vector<CookieChangeInfo> other_cookie_changes;
+ std::unique_ptr<CookieChangeSubscription> other_subscription =
+ cs->GetChangeDispatcher().AddCallbackForUrl(
+ GURL("https://www.example.com/"),
+ absl::nullopt /* cookie_partition_key */,
+ base::BindRepeating(
+ &CookieStoreChangeTestBase<TypeParam>::OnCookieChange,
+ base::Unretained(&other_cookie_changes)));
+ // Update Max-Age: None -> 7200
+ this->CreateAndSetCookie(
+ cs, GURL("https://www.example.com"),
+ "__Host-b=2; Secure; Path=/; Partitioned; Max-Age=7200",
+ CookieOptions::MakeAllInclusive(), absl::nullopt /* server_time */,
+ absl::nullopt /* system_time */,
+ absl::make_optional(
+ CookiePartitionKey::FromURLForTesting(GURL("https://www.foo.com"))));
+ this->DeliverChangeNotifications();
+ ASSERT_EQ(0u, other_cookie_changes.size());
+ // Check that the other listener was invoked.
+ ASSERT_LT(2u, cookie_changes.size());
+}
+
TYPED_TEST_P(CookieStoreChangeNamedTest, NoCookie) {
if (!TypeParam::supports_named_cookie_tracking)
return;
@@ -1732,6 +1803,7 @@ TYPED_TEST_P(CookieStoreChangeNamedTest, NoCookie) {
std::unique_ptr<CookieChangeSubscription> subscription =
cs->GetChangeDispatcher().AddCallbackForCookie(
this->http_www_foo_.url(), "abc",
+ absl::nullopt /* cookie_partition_key */,
base::BindRepeating(
&CookieStoreChangeTestBase<TypeParam>::OnCookieChange,
base::Unretained(&cookie_changes)));
@@ -1750,6 +1822,7 @@ TYPED_TEST_P(CookieStoreChangeNamedTest, InitialCookie) {
std::unique_ptr<CookieChangeSubscription> subscription =
cs->GetChangeDispatcher().AddCallbackForCookie(
this->http_www_foo_.url(), "abc",
+ absl::nullopt /* cookie_partition_key */,
base::BindRepeating(
&CookieStoreChangeTestBase<TypeParam>::OnCookieChange,
base::Unretained(&cookie_changes)));
@@ -1766,6 +1839,7 @@ TYPED_TEST_P(CookieStoreChangeNamedTest, InsertOne) {
std::unique_ptr<CookieChangeSubscription> subscription =
cs->GetChangeDispatcher().AddCallbackForCookie(
this->http_www_foo_.url(), "abc",
+ absl::nullopt /* cookie_partition_key */,
base::BindRepeating(
&CookieStoreChangeTestBase<TypeParam>::OnCookieChange,
base::Unretained(&cookie_changes)));
@@ -1793,6 +1867,7 @@ TYPED_TEST_P(CookieStoreChangeNamedTest, InsertTwo) {
std::unique_ptr<CookieChangeSubscription> subscription =
cs->GetChangeDispatcher().AddCallbackForCookie(
this->www_foo_foo_.url(), "abc",
+ absl::nullopt /* cookie_partition_key */,
base::BindRepeating(
&CookieStoreChangeTestBase<TypeParam>::OnCookieChange,
base::Unretained(&cookie_changes)));
@@ -1834,6 +1909,7 @@ TYPED_TEST_P(CookieStoreChangeNamedTest, InsertFiltering) {
std::unique_ptr<CookieChangeSubscription> subscription =
cs->GetChangeDispatcher().AddCallbackForCookie(
this->www_foo_foo_.url(), "abc",
+ absl::nullopt /* cookie_partition_key */,
base::BindRepeating(
&CookieStoreChangeTestBase<TypeParam>::OnCookieChange,
base::Unretained(&cookie_changes)));
@@ -1893,6 +1969,7 @@ TYPED_TEST_P(CookieStoreChangeNamedTest, DeleteOne) {
std::unique_ptr<CookieChangeSubscription> subscription =
cs->GetChangeDispatcher().AddCallbackForCookie(
this->http_www_foo_.url(), "abc",
+ absl::nullopt /* cookie_partition_key */,
base::BindRepeating(
&CookieStoreChangeTestBase<TypeParam>::OnCookieChange,
base::Unretained(&cookie_changes)));
@@ -1923,6 +2000,7 @@ TYPED_TEST_P(CookieStoreChangeNamedTest, DeleteTwo) {
std::unique_ptr<CookieChangeSubscription> subscription =
cs->GetChangeDispatcher().AddCallbackForCookie(
this->www_foo_foo_.url(), "abc",
+ absl::nullopt /* cookie_partition_key */,
base::BindRepeating(
&CookieStoreChangeTestBase<TypeParam>::OnCookieChange,
base::Unretained(&cookie_changes)));
@@ -1967,6 +2045,7 @@ TYPED_TEST_P(CookieStoreChangeNamedTest, DeleteFiltering) {
std::unique_ptr<CookieChangeSubscription> subscription =
cs->GetChangeDispatcher().AddCallbackForCookie(
this->www_foo_foo_.url(), "abc",
+ absl::nullopt /* cookie_partition_key */,
base::BindRepeating(
&CookieStoreChangeTestBase<TypeParam>::OnCookieChange,
base::Unretained(&cookie_changes)));
@@ -2037,6 +2116,7 @@ TYPED_TEST_P(CookieStoreChangeNamedTest, Overwrite) {
std::unique_ptr<CookieChangeSubscription> subscription =
cs->GetChangeDispatcher().AddCallbackForCookie(
this->http_www_foo_.url(), "abc",
+ absl::nullopt /* cookie_partition_key */,
base::BindRepeating(
&CookieStoreChangeTestBase<TypeParam>::OnCookieChange,
base::Unretained(&cookie_changes)));
@@ -2081,6 +2161,7 @@ TYPED_TEST_P(CookieStoreChangeNamedTest, OverwriteFiltering) {
std::unique_ptr<CookieChangeSubscription> subscription =
cs->GetChangeDispatcher().AddCallbackForCookie(
this->www_foo_foo_.url(), "abc",
+ absl::nullopt /* cookie_partition_key */,
base::BindRepeating(
&CookieStoreChangeTestBase<TypeParam>::OnCookieChange,
base::Unretained(&cookie_changes)));
@@ -2184,6 +2265,7 @@ TYPED_TEST_P(CookieStoreChangeNamedTest, OverwriteWithHttpOnly) {
std::unique_ptr<CookieChangeSubscription> subscription =
cs->GetChangeDispatcher().AddCallbackForCookie(
this->www_foo_foo_.url(), "abc",
+ absl::nullopt /* cookie_partition_key */,
base::BindRepeating(
&CookieStoreChangeTestBase<TypeParam>::OnCookieChange,
base::Unretained(&cookie_changes)));
@@ -2246,6 +2328,7 @@ TYPED_TEST_P(CookieStoreChangeNamedTest, Deregister) {
std::unique_ptr<CookieChangeSubscription> subscription =
cs->GetChangeDispatcher().AddCallbackForCookie(
this->www_foo_foo_.url(), "abc",
+ absl::nullopt /* cookie_partition_key */,
base::BindRepeating(
&CookieStoreChangeTestBase<TypeParam>::OnCookieChange,
base::Unretained(&cookie_changes)));
@@ -2285,12 +2368,14 @@ TYPED_TEST_P(CookieStoreChangeNamedTest, DeregisterMultiple) {
std::unique_ptr<CookieChangeSubscription> subscription1 =
cs->GetChangeDispatcher().AddCallbackForCookie(
this->www_foo_foo_.url(), "abc",
+ absl::nullopt /* cookie_partition_key */,
base::BindRepeating(
&CookieStoreChangeTestBase<TypeParam>::OnCookieChange,
base::Unretained(&cookie_changes_1)));
std::unique_ptr<CookieChangeSubscription> subscription2 =
cs->GetChangeDispatcher().AddCallbackForCookie(
this->www_foo_foo_.url(), "abc",
+ absl::nullopt /* cookie_partition_key */,
base::BindRepeating(
&CookieStoreChangeTestBase<TypeParam>::OnCookieChange,
base::Unretained(&cookie_changes_2)));
@@ -2348,6 +2433,7 @@ TYPED_TEST_P(CookieStoreChangeNamedTest, DispatchRace) {
std::unique_ptr<CookieChangeSubscription> subscription =
cs->GetChangeDispatcher().AddCallbackForCookie(
this->www_foo_foo_.url(), "abc",
+ absl::nullopt /* cookie_partition_key */,
base::BindRepeating(
&CookieStoreChangeTestBase<TypeParam>::OnCookieChange,
base::Unretained(&cookie_changes)));
@@ -2377,6 +2463,7 @@ TYPED_TEST_P(CookieStoreChangeNamedTest, DeregisterRace) {
std::unique_ptr<CookieChangeSubscription> subscription =
cs->GetChangeDispatcher().AddCallbackForCookie(
this->www_foo_foo_.url(), "abc",
+ absl::nullopt /* cookie_partition_key */,
base::BindRepeating(
&CookieStoreChangeTestBase<TypeParam>::OnCookieChange,
base::Unretained(&cookie_changes)));
@@ -2424,12 +2511,14 @@ TYPED_TEST_P(CookieStoreChangeNamedTest, DeregisterRaceMultiple) {
std::unique_ptr<CookieChangeSubscription> subscription1 =
cs->GetChangeDispatcher().AddCallbackForCookie(
this->www_foo_foo_.url(), "abc",
+ absl::nullopt /* cookie_partition_key */,
base::BindRepeating(
&CookieStoreChangeTestBase<TypeParam>::OnCookieChange,
base::Unretained(&cookie_changes_1)));
std::unique_ptr<CookieChangeSubscription> subscription2 =
cs->GetChangeDispatcher().AddCallbackForCookie(
this->www_foo_foo_.url(), "abc",
+ absl::nullopt /* cookie_partition_key */,
base::BindRepeating(
&CookieStoreChangeTestBase<TypeParam>::OnCookieChange,
base::Unretained(&cookie_changes_2)));
@@ -2490,12 +2579,14 @@ TYPED_TEST_P(CookieStoreChangeNamedTest, DifferentSubscriptionsDisjoint) {
std::unique_ptr<CookieChangeSubscription> subscription1 =
cs->GetChangeDispatcher().AddCallbackForCookie(
this->http_www_foo_.url(), "abc",
+ absl::nullopt /* cookie_partition_key */,
base::BindRepeating(
&CookieStoreChangeTestBase<TypeParam>::OnCookieChange,
base::Unretained(&cookie_changes_1)));
std::unique_ptr<CookieChangeSubscription> subscription2 =
cs->GetChangeDispatcher().AddCallbackForCookie(
this->http_bar_com_.url(), "ghi",
+ absl::nullopt /* cookie_partition_key */,
base::BindRepeating(
&CookieStoreChangeTestBase<TypeParam>::OnCookieChange,
base::Unretained(&cookie_changes_2)));
@@ -2534,12 +2625,14 @@ TYPED_TEST_P(CookieStoreChangeNamedTest, DifferentSubscriptionsDomains) {
std::unique_ptr<CookieChangeSubscription> subscription1 =
cs->GetChangeDispatcher().AddCallbackForCookie(
this->http_www_foo_.url(), "abc",
+ absl::nullopt /* cookie_partition_key */,
base::BindRepeating(
&CookieStoreChangeTestBase<TypeParam>::OnCookieChange,
base::Unretained(&cookie_changes_1)));
std::unique_ptr<CookieChangeSubscription> subscription2 =
cs->GetChangeDispatcher().AddCallbackForCookie(
this->http_bar_com_.url(), "abc",
+ absl::nullopt /* cookie_partition_key */,
base::BindRepeating(
&CookieStoreChangeTestBase<TypeParam>::OnCookieChange,
base::Unretained(&cookie_changes_2)));
@@ -2578,12 +2671,14 @@ TYPED_TEST_P(CookieStoreChangeNamedTest, DifferentSubscriptionsNames) {
std::unique_ptr<CookieChangeSubscription> subscription1 =
cs->GetChangeDispatcher().AddCallbackForCookie(
this->http_www_foo_.url(), "abc",
+ absl::nullopt /* cookie_partition_key */,
base::BindRepeating(
&CookieStoreChangeTestBase<TypeParam>::OnCookieChange,
base::Unretained(&cookie_changes_1)));
std::unique_ptr<CookieChangeSubscription> subscription2 =
cs->GetChangeDispatcher().AddCallbackForCookie(
this->http_www_foo_.url(), "ghi",
+ absl::nullopt /* cookie_partition_key */,
base::BindRepeating(
&CookieStoreChangeTestBase<TypeParam>::OnCookieChange,
base::Unretained(&cookie_changes_2)));
@@ -2622,12 +2717,14 @@ TYPED_TEST_P(CookieStoreChangeNamedTest, DifferentSubscriptionsPaths) {
std::unique_ptr<CookieChangeSubscription> subscription1 =
cs->GetChangeDispatcher().AddCallbackForCookie(
this->http_www_foo_.url(), "abc",
+ absl::nullopt /* cookie_partition_key */,
base::BindRepeating(
&CookieStoreChangeTestBase<TypeParam>::OnCookieChange,
base::Unretained(&cookie_changes_1)));
std::unique_ptr<CookieChangeSubscription> subscription2 =
cs->GetChangeDispatcher().AddCallbackForCookie(
this->www_foo_foo_.url(), "abc",
+ absl::nullopt /* cookie_partition_key */,
base::BindRepeating(
&CookieStoreChangeTestBase<TypeParam>::OnCookieChange,
base::Unretained(&cookie_changes_2)));
@@ -2679,24 +2776,28 @@ TYPED_TEST_P(CookieStoreChangeNamedTest, DifferentSubscriptionsFiltering) {
std::unique_ptr<CookieChangeSubscription> subscription1 =
cs->GetChangeDispatcher().AddCallbackForCookie(
this->http_www_foo_.url(), "abc",
+ absl::nullopt /* cookie_partition_key */,
base::BindRepeating(
&CookieStoreChangeTestBase<TypeParam>::OnCookieChange,
base::Unretained(&cookie_changes_1)));
std::unique_ptr<CookieChangeSubscription> subscription2 =
cs->GetChangeDispatcher().AddCallbackForCookie(
this->http_www_foo_.url(), "hij",
+ absl::nullopt /* cookie_partition_key */,
base::BindRepeating(
&CookieStoreChangeTestBase<TypeParam>::OnCookieChange,
base::Unretained(&cookie_changes_2)));
std::unique_ptr<CookieChangeSubscription> subscription3 =
cs->GetChangeDispatcher().AddCallbackForCookie(
this->http_bar_com_.url(), "abc",
+ absl::nullopt /* cookie_partition_key */,
base::BindRepeating(
&CookieStoreChangeTestBase<TypeParam>::OnCookieChange,
base::Unretained(&cookie_changes_3)));
std::unique_ptr<CookieChangeSubscription> subscription4 =
cs->GetChangeDispatcher().AddCallbackForCookie(
this->www_foo_foo_.url(), "abc",
+ absl::nullopt /* cookie_partition_key */,
base::BindRepeating(
&CookieStoreChangeTestBase<TypeParam>::OnCookieChange,
base::Unretained(&cookie_changes_4)));
@@ -2783,12 +2884,14 @@ TYPED_TEST_P(CookieStoreChangeNamedTest, MultipleSubscriptions) {
std::unique_ptr<CookieChangeSubscription> subscription1 =
cs->GetChangeDispatcher().AddCallbackForCookie(
this->http_www_foo_.url(), "abc",
+ absl::nullopt /* cookie_partition_key */,
base::BindRepeating(
&CookieStoreChangeTestBase<TypeParam>::OnCookieChange,
base::Unretained(&cookie_changes_1)));
std::unique_ptr<CookieChangeSubscription> subscription2 =
cs->GetChangeDispatcher().AddCallbackForCookie(
this->http_www_foo_.url(), "abc",
+ absl::nullopt /* cookie_partition_key */,
base::BindRepeating(
&CookieStoreChangeTestBase<TypeParam>::OnCookieChange,
base::Unretained(&cookie_changes_2)));
@@ -2817,6 +2920,7 @@ TYPED_TEST_P(CookieStoreChangeNamedTest, SubscriptionOutlivesStore) {
std::unique_ptr<CookieChangeSubscription> subscription =
this->GetCookieStore()->GetChangeDispatcher().AddCallbackForCookie(
this->http_www_foo_.url(), "abc",
+ absl::nullopt /* cookie_partition_key */,
base::BindRepeating(
&CookieStoreChangeTestBase<TypeParam>::OnCookieChange,
base::Unretained(&cookie_changes)));
@@ -2842,6 +2946,7 @@ TYPED_TEST_P(CookieStoreChangeNamedTest, ChangeIncludesCookieAccessSemantics) {
std::unique_ptr<CookieChangeSubscription> subscription =
cs->GetChangeDispatcher().AddCallbackForCookie(
GURL("http://domain1.test"), "cookie",
+ absl::nullopt /* cookie_partition_key */,
base::BindRepeating(
&CookieStoreChangeTestBase<TypeParam>::OnCookieChange,
base::Unretained(&cookie_changes)));
@@ -2858,6 +2963,76 @@ TYPED_TEST_P(CookieStoreChangeNamedTest, ChangeIncludesCookieAccessSemantics) {
cookie_changes[0].access_result.access_semantics));
}
+TYPED_TEST_P(CookieStoreChangeNamedTest, PartitionedCookies) {
+ if (!TypeParam::supports_named_cookie_tracking ||
+ !TypeParam::supports_partitioned_cookies)
+ return;
+
+ CookieStore* cs = this->GetCookieStore();
+ std::vector<CookieChangeInfo> cookie_changes;
+ std::unique_ptr<CookieChangeSubscription> subscription =
+ cs->GetChangeDispatcher().AddCallbackForCookie(
+ GURL("https://www.example.com"), "__Host-a",
+ absl::make_optional(CookiePartitionKey::FromURLForTesting(
+ GURL("https://www.foo.com"))),
+ base::BindRepeating(
+ &CookieStoreChangeTestBase<TypeParam>::OnCookieChange,
+ base::Unretained(&cookie_changes)));
+
+ // Unpartitioned cookie
+ this->CreateAndSetCookie(cs, GURL("https://www.example.com"),
+ "__Host-a=1; Secure; Path=/",
+ CookieOptions::MakeAllInclusive());
+ // Partitioned cookie with the same partition key
+ this->CreateAndSetCookie(
+ cs, GURL("https://www.example.com"),
+ "__Host-a=2; Secure; Path=/; Partitioned",
+ CookieOptions::MakeAllInclusive(), absl::nullopt /* server_time */,
+ absl::nullopt /* system_time */,
+ absl::make_optional(
+ CookiePartitionKey::FromURLForTesting(GURL("https://sub.foo.com"))));
+ // Partitioned cookie with a different partition key
+ this->CreateAndSetCookie(
+ cs, GURL("https://www.example.com"),
+ "__Host-a=3; Secure; Path=/; Partitioned",
+ CookieOptions::MakeAllInclusive(), absl::nullopt /* server_time */,
+ absl::nullopt /* system_time */,
+ absl::make_optional(
+ CookiePartitionKey::FromURLForTesting(GURL("https://www.bar.com"))));
+ this->DeliverChangeNotifications();
+
+ ASSERT_EQ(2u, cookie_changes.size());
+ EXPECT_FALSE(cookie_changes[0].cookie.IsPartitioned());
+ EXPECT_EQ("1", cookie_changes[0].cookie.Value());
+ EXPECT_TRUE(cookie_changes[1].cookie.IsPartitioned());
+ EXPECT_EQ(CookiePartitionKey::FromURLForTesting(GURL("https://www.foo.com")),
+ cookie_changes[1].cookie.PartitionKey().value());
+ EXPECT_EQ("2", cookie_changes[1].cookie.Value());
+
+ // Test that when the partition key parameter is nullopt that all Partitioned
+ // cookies do not emit events.
+ std::vector<CookieChangeInfo> other_cookie_changes;
+ std::unique_ptr<CookieChangeSubscription> other_subscription =
+ cs->GetChangeDispatcher().AddCallbackForCookie(
+ GURL("https://www.example.com"), "__Host-a",
+ absl::nullopt /* cookie_partition_key */,
+ base::BindRepeating(
+ &CookieStoreChangeTestBase<TypeParam>::OnCookieChange,
+ base::Unretained(&other_cookie_changes)));
+ // Update Max-Age: None -> 7200
+ this->CreateAndSetCookie(
+ cs, GURL("https://www.example.com"),
+ "__Host-a=2; Secure; Path=/; Partitioned; Max-Age=7200",
+ CookieOptions::MakeAllInclusive(), absl::nullopt /* server_time */,
+ absl::nullopt /* system_time */,
+ absl::make_optional(
+ CookiePartitionKey::FromURLForTesting(GURL("https://www.foo.com"))));
+ this->DeliverChangeNotifications();
+ ASSERT_EQ(0u, other_cookie_changes.size());
+ // Check that the other listener was invoked.
+ ASSERT_LT(2u, cookie_changes.size());
+}
+
REGISTER_TYPED_TEST_SUITE_P(CookieStoreChangeGlobalTest,
NoCookie,
InitialCookie,
@@ -2897,7 +3072,8 @@ REGISTER_TYPED_TEST_SUITE_P(CookieStoreChangeUrlTest,
DifferentSubscriptionsPaths,
DifferentSubscriptionsFiltering,
MultipleSubscriptions,
- ChangeIncludesCookieAccessSemantics);
+ ChangeIncludesCookieAccessSemantics,
+ PartitionedCookies);
REGISTER_TYPED_TEST_SUITE_P(CookieStoreChangeNamedTest,
NoCookie,
@@ -2923,7 +3099,8 @@ REGISTER_TYPED_TEST_SUITE_P(CookieStoreChangeNamedTest,
DifferentSubscriptionsFiltering,
MultipleSubscriptions,
SubscriptionOutlivesStore,
- ChangeIncludesCookieAccessSemantics);
+ ChangeIncludesCookieAccessSemantics,
+ PartitionedCookies);
} // namespace net
diff --git a/chromium/net/cookies/cookie_store_test_helpers.cc b/chromium/net/cookies/cookie_store_test_helpers.cc
index 0736a515ca5..31d038e9cd9 100644
--- a/chromium/net/cookies/cookie_store_test_helpers.cc
+++ b/chromium/net/cookies/cookie_store_test_helpers.cc
@@ -46,6 +46,7 @@ std::unique_ptr<CookieChangeSubscription>
DelayedCookieMonsterChangeDispatcher::AddCallbackForCookie(
const GURL& url,
const std::string& name,
+ const absl::optional<CookiePartitionKey>& cookie_partition_key,
CookieChangeCallback callback) {
ADD_FAILURE();
return nullptr;
@@ -53,6 +54,7 @@ DelayedCookieMonsterChangeDispatcher::AddCallbackForCookie(
std::unique_ptr<CookieChangeSubscription>
DelayedCookieMonsterChangeDispatcher::AddCallbackForUrl(
const GURL& url,
+ const absl::optional<CookiePartitionKey>& cookie_partition_key,
CookieChangeCallback callback) {
ADD_FAILURE();
return nullptr;
@@ -102,16 +104,17 @@ void DelayedCookieMonster::SetCanonicalCookieAsync(
FROM_HERE,
base::BindOnce(&DelayedCookieMonster::InvokeSetCookiesCallback,
base::Unretained(this), std::move(callback)),
- base::TimeDelta::FromMilliseconds(kDelayedTime));
+ base::Milliseconds(kDelayedTime));
}
void DelayedCookieMonster::GetCookieListWithOptionsAsync(
const GURL& url,
const CookieOptions& options,
+ const CookiePartitionKeychain& cookie_partition_keychain,
CookieMonster::GetCookieListCallback callback) {
did_run_ = false;
cookie_monster_->GetCookieListWithOptionsAsync(
- url, options,
+ url, options, cookie_partition_keychain,
base::BindOnce(
&DelayedCookieMonster::GetCookieListWithOptionsInternalCallback,
base::Unretained(this)));
@@ -120,7 +123,7 @@ void DelayedCookieMonster::GetCookieListWithOptionsAsync(
FROM_HERE,
base::BindOnce(&DelayedCookieMonster::InvokeGetCookieListCallback,
base::Unretained(this), std::move(callback)),
- base::TimeDelta::FromMilliseconds(kDelayedTime));
+ base::Milliseconds(kDelayedTime));
}
void DelayedCookieMonster::GetAllCookiesAsync(GetAllCookiesCallback callback) {
diff --git a/chromium/net/cookies/cookie_store_test_helpers.h b/chromium/net/cookies/cookie_store_test_helpers.h
index 63182daf15e..d95a0578a86 100644
--- a/chromium/net/cookies/cookie_store_test_helpers.h
+++ b/chromium/net/cookies/cookie_store_test_helpers.h
@@ -24,27 +24,35 @@ namespace net {
class DelayedCookieMonsterChangeDispatcher : public CookieChangeDispatcher {
public:
DelayedCookieMonsterChangeDispatcher();
+
+ DelayedCookieMonsterChangeDispatcher(
+ const DelayedCookieMonsterChangeDispatcher&) = delete;
+ DelayedCookieMonsterChangeDispatcher& operator=(
+ const DelayedCookieMonsterChangeDispatcher&) = delete;
+
~DelayedCookieMonsterChangeDispatcher() override;
// net::CookieChangeDispatcher
std::unique_ptr<CookieChangeSubscription> AddCallbackForCookie(
const GURL& url,
const std::string& name,
+ const absl::optional<CookiePartitionKey>& cookie_partition_key,
CookieChangeCallback callback) override WARN_UNUSED_RESULT;
std::unique_ptr<CookieChangeSubscription> AddCallbackForUrl(
const GURL& url,
+ const absl::optional<CookiePartitionKey>& cookie_partition_key,
CookieChangeCallback callback) override WARN_UNUSED_RESULT;
std::unique_ptr<CookieChangeSubscription> AddCallbackForAllChanges(
CookieChangeCallback callback) override WARN_UNUSED_RESULT;
-
- private:
- DISALLOW_COPY_AND_ASSIGN(DelayedCookieMonsterChangeDispatcher);
};
class DelayedCookieMonster : public CookieStore {
public:
DelayedCookieMonster();
+ DelayedCookieMonster(const DelayedCookieMonster&) = delete;
+ DelayedCookieMonster& operator=(const DelayedCookieMonster&) = delete;
+
~DelayedCookieMonster() override;
// Call the asynchronous CookieMonster function, expect it to immediately
@@ -56,9 +64,11 @@ class DelayedCookieMonster : public CookieStore {
const CookieOptions& options,
SetCookiesCallback callback) override;
- void GetCookieListWithOptionsAsync(const GURL& url,
- const CookieOptions& options,
- GetCookieListCallback callback) override;
+ void GetCookieListWithOptionsAsync(
+ const GURL& url,
+ const CookieOptions& options,
+ const CookiePartitionKeychain& cookie_partition_keychain,
+ GetCookieListCallback callback) override;
void GetAllCookiesAsync(GetAllCookiesCallback callback) override;
@@ -111,8 +121,6 @@ class DelayedCookieMonster : public CookieStore {
std::string cookie_line_;
CookieAccessResultList cookie_access_result_list_;
CookieList cookie_list_;
-
- DISALLOW_COPY_AND_ASSIGN(DelayedCookieMonster);
};
class CookieURLHelper {
diff --git a/chromium/net/cookies/cookie_store_unittest.h b/chromium/net/cookies/cookie_store_unittest.h
index 1631f1667dd..e0af8710105 100644
--- a/chromium/net/cookies/cookie_store_unittest.h
+++ b/chromium/net/cookies/cookie_store_unittest.h
@@ -126,7 +126,8 @@ class CookieStoreTest : public testing::Test {
www_foo_foo_("http://www.foo.com/foo"),
www_foo_bar_("http://www.foo.com/bar"),
http_baz_com_("http://baz.com"),
- http_bar_com_("http://bar.com") {
+ http_bar_com_("http://bar.com"),
+ https_www_bar_("https://www.bar.com") {
// This test may be used outside of the net test suite, and thus may not
// have a task environment.
if (!base::CurrentThread::Get()) {
@@ -140,36 +141,52 @@ class CookieStoreTest : public testing::Test {
// finally returning the value.
// TODO(chlily): Consolidate some of these.
- std::string GetCookies(CookieStore* cs, const GURL& url) {
+ std::string GetCookies(
+ CookieStore* cs,
+ const GURL& url,
+ const CookiePartitionKeychain& cookie_partition_keychain =
+ CookiePartitionKeychain()) {
DCHECK(cs);
CookieOptions options;
if (!CookieStoreTestTraits::supports_http_only)
options.set_include_httponly();
options.set_same_site_cookie_context(
net::CookieOptions::SameSiteCookieContext::MakeInclusive());
- return GetCookiesWithOptions(cs, url, options);
+ return GetCookiesWithOptions(cs, url, options, cookie_partition_keychain);
}
- std::string GetCookiesWithOptions(CookieStore* cs,
- const GURL& url,
- const CookieOptions& options) {
+ std::string GetCookiesWithOptions(
+ CookieStore* cs,
+ const GURL& url,
+ const CookieOptions& options,
+ const CookiePartitionKeychain& cookie_partition_keychain =
+ CookiePartitionKeychain()) {
return CanonicalCookie::BuildCookieLine(
- GetCookieListWithOptions(cs, url, options));
+ GetCookieListWithOptions(cs, url, options, cookie_partition_keychain));
}
- CookieList GetCookieListWithOptions(CookieStore* cs,
- const GURL& url,
- const CookieOptions& options) {
+ CookieList GetCookieListWithOptions(
+ CookieStore* cs,
+ const GURL& url,
+ const CookieOptions& options,
+ const CookiePartitionKeychain& cookie_partition_keychain =
+ CookiePartitionKeychain()) {
DCHECK(cs);
GetCookieListCallback callback;
- cs->GetCookieListWithOptionsAsync(url, options, callback.MakeCallback());
+ cs->GetCookieListWithOptionsAsync(url, options, cookie_partition_keychain,
+ callback.MakeCallback());
callback.WaitUntilDone();
return callback.cookies();
}
// This does not update the access time on the cookies.
- CookieList GetAllCookiesForURL(CookieStore* cs, const GURL& url) {
- return GetCookieListWithOptions(cs, url, CookieOptions::MakeAllInclusive());
+ CookieList GetAllCookiesForURL(
+ CookieStore* cs,
+ const GURL& url,
+ const CookiePartitionKeychain& cookie_partition_keychain =
+ CookiePartitionKeychain()) {
+ return GetCookieListWithOptions(cs, url, CookieOptions::MakeAllInclusive(),
+ cookie_partition_keychain);
}
// This does not update the access time on the cookies.
@@ -179,7 +196,8 @@ class CookieStoreTest : public testing::Test {
GetCookieListCallback callback;
CookieOptions options = CookieOptions::MakeAllInclusive();
options.set_return_excluded_cookies();
- cs->GetCookieListWithOptionsAsync(url, options, callback.MakeCallback());
+ cs->GetCookieListWithOptionsAsync(
+ url, options, CookiePartitionKeychain::Todo(), callback.MakeCallback());
callback.WaitUntilDone();
return callback.excluded_cookies();
}
@@ -198,10 +216,11 @@ class CookieStoreTest : public testing::Test {
const std::string& cookie_line,
const CookieOptions& options,
absl::optional<base::Time> server_time = absl::nullopt,
- absl::optional<base::Time> system_time = absl::nullopt) {
+ absl::optional<base::Time> system_time = absl::nullopt,
+ absl::optional<CookiePartitionKey> cookie_partition_key = absl::nullopt) {
auto cookie = CanonicalCookie::Create(
url, cookie_line, system_time.value_or(base::Time::Now()), server_time,
- absl::nullopt /* cookie_partition_key */);
+ cookie_partition_key);
if (!cookie)
return false;
@@ -256,15 +275,18 @@ class CookieStoreTest : public testing::Test {
absl::make_optional(server_time));
}
- bool SetCookie(CookieStore* cs,
- const GURL& url,
- const std::string& cookie_line) {
+ bool SetCookie(
+ CookieStore* cs,
+ const GURL& url,
+ const std::string& cookie_line,
+ absl::optional<CookiePartitionKey> cookie_partition_key = absl::nullopt) {
CookieOptions options;
if (!CookieStoreTestTraits::supports_http_only)
options.set_include_httponly();
options.set_same_site_cookie_context(
net::CookieOptions::SameSiteCookieContext::MakeInclusive());
- return CreateAndSetCookie(cs, url, cookie_line, options);
+ return CreateAndSetCookie(cs, url, cookie_line, options, absl::nullopt,
+ absl::nullopt, cookie_partition_key);
}
CookieInclusionStatus CreateAndSetCookieReturnStatus(
@@ -388,12 +410,13 @@ class CookieStoreTest : public testing::Test {
const std::string& line) {
std::string cookies = GetCookies(cs, url);
bool matched = (TokenizeCookieLine(line) == TokenizeCookieLine(cookies));
- base::Time polling_end_date = base::Time::Now() +
- base::TimeDelta::FromMilliseconds(
+ base::Time polling_end_date =
+ base::Time::Now() +
+ base::Milliseconds(
CookieStoreTestTraits::creation_time_granularity_in_ms);
while (!matched && base::Time::Now() <= polling_end_date) {
- base::PlatformThread::Sleep(base::TimeDelta::FromMilliseconds(10));
+ base::PlatformThread::Sleep(base::Milliseconds(10));
cookies = GetCookies(cs, url);
matched = (TokenizeCookieLine(line) == TokenizeCookieLine(cookies));
}
@@ -413,6 +436,7 @@ class CookieStoreTest : public testing::Test {
const CookieURLHelper www_foo_bar_;
const CookieURLHelper http_baz_com_;
const CookieURLHelper http_bar_com_;
+ const CookieURLHelper https_www_bar_;
std::unique_ptr<base::test::SingleThreadTaskEnvironment> task_environment_;
@@ -433,10 +457,9 @@ TYPED_TEST_SUITE_P(CookieStoreTest);
TYPED_TEST_P(CookieStoreTest, FilterTest) {
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);
+ base::Time two_hours_ago = base::Time::Now() - base::Hours(2);
+ base::Time one_hour_ago = base::Time::Now() - base::Hours(1);
+ base::Time one_hour_from_now = base::Time::Now() + base::Hours(1);
std::unique_ptr<CanonicalCookie> cc(CanonicalCookie::CreateSanitizedCookie(
this->www_foo_foo_.url(), "A", "B", std::string(), "/foo", one_hour_ago,
@@ -560,10 +583,9 @@ TYPED_TEST_P(CookieStoreTest, FilterTest) {
TYPED_TEST_P(CookieStoreTest, SetCanonicalCookieTest) {
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);
+ base::Time two_hours_ago = base::Time::Now() - base::Hours(2);
+ base::Time one_hour_ago = base::Time::Now() - base::Hours(1);
+ base::Time one_hour_from_now = base::Time::Now() + base::Hours(1);
std::string foo_foo_host(this->www_foo_foo_.url().host());
std::string foo_bar_domain(this->www_foo_bar_.domain());
@@ -1276,12 +1298,12 @@ TYPED_TEST_P(CookieStoreTest, EmptyExpires) {
this->GetCookiesWithOptions(cs, url, options));
absl::optional<base::Time> server_time =
- absl::make_optional(base::Time::Now() - base::TimeDelta::FromHours(1));
+ absl::make_optional(base::Time::Now() - base::Hours(1));
this->CreateAndSetCookie(cs, url, set_cookie_line, options, server_time);
this->MatchCookieLines(cookie_line,
this->GetCookiesWithOptions(cs, url, options));
- server_time = base::Time::Now() + base::TimeDelta::FromHours(1);
+ server_time = base::Time::Now() + base::Hours(1);
this->CreateAndSetCookie(cs, url, set_cookie_line, options, server_time);
this->MatchCookieLines(cookie_line,
this->GetCookiesWithOptions(cs, url, options));
@@ -1432,14 +1454,10 @@ TYPED_TEST_P(CookieStoreTest, TestDeleteAll) {
TYPED_TEST_P(CookieStoreTest, TestDeleteAllCreatedInTimeRange) {
CookieStore* cs = this->GetCookieStore();
- const base::Time last_month = base::Time::Now() -
- base::TimeDelta::FromDays(30);
- const base::Time last_minute = base::Time::Now() -
- base::TimeDelta::FromMinutes(1);
- const base::Time next_minute = base::Time::Now() +
- base::TimeDelta::FromMinutes(1);
- const base::Time next_month = base::Time::Now() +
- base::TimeDelta::FromDays(30);
+ const base::Time last_month = base::Time::Now() - base::Days(30);
+ const base::Time last_minute = base::Time::Now() - base::Minutes(1);
+ const base::Time next_minute = base::Time::Now() + base::Minutes(1);
+ const base::Time next_month = base::Time::Now() + base::Days(30);
// Add a cookie.
EXPECT_TRUE(this->SetCookie(cs, this->http_www_foo_.url(), "A=B"));
@@ -1480,8 +1498,8 @@ TYPED_TEST_P(CookieStoreTest, TestDeleteAllCreatedInTimeRange) {
TYPED_TEST_P(CookieStoreTest, TestDeleteAllWithInfo) {
CookieStore* cs = this->GetCookieStore();
base::Time now = base::Time::Now();
- base::Time last_month = base::Time::Now() - base::TimeDelta::FromDays(30);
- base::Time last_minute = base::Time::Now() - base::TimeDelta::FromMinutes(1);
+ base::Time last_month = base::Time::Now() - base::Days(30);
+ base::Time last_minute = base::Time::Now() - base::Minutes(1);
// These 3 cookies match the time range and host.
EXPECT_TRUE(this->SetCookie(cs, this->http_www_foo_.url(), "A=B"));
@@ -1665,12 +1683,12 @@ TYPED_TEST_P(CookieStoreTest, CookieOrdering) {
this->SetCookie(cs, GURL("http://d.c.b.a.foo.com/aa/x.html"), "c=1"));
EXPECT_TRUE(this->SetCookie(cs, GURL("http://b.a.foo.com/aa/bb/cc/x.html"),
"d=1; domain=b.a.foo.com"));
- base::PlatformThread::Sleep(base::TimeDelta::FromMilliseconds(
- TypeParam::creation_time_granularity_in_ms));
+ base::PlatformThread::Sleep(
+ base::Milliseconds(TypeParam::creation_time_granularity_in_ms));
EXPECT_TRUE(this->SetCookie(cs, GURL("http://b.a.foo.com/aa/bb/cc/x.html"),
"a=4; domain=b.a.foo.com"));
- base::PlatformThread::Sleep(base::TimeDelta::FromMilliseconds(
- TypeParam::creation_time_granularity_in_ms));
+ base::PlatformThread::Sleep(
+ base::Milliseconds(TypeParam::creation_time_granularity_in_ms));
EXPECT_TRUE(this->SetCookie(cs, GURL("http://c.b.a.foo.com/aa/bb/cc/x.html"),
"e=1; domain=c.b.a.foo.com"));
EXPECT_TRUE(
diff --git a/chromium/net/cookies/cookie_util.cc b/chromium/net/cookies/cookie_util.cc
index 8033226d032..89618a98f34 100644
--- a/chromium/net/cookies/cookie_util.cc
+++ b/chromium/net/cookies/cookie_util.cc
@@ -252,7 +252,7 @@ bool CookieWithAccessResultSorter(const CookieWithAccessResult& a,
} // namespace
void FireStorageAccessHistogram(StorageAccessResult result) {
- UMA_HISTOGRAM_ENUMERATION("API.StorageAccess.AllowedRequests", result);
+ UMA_HISTOGRAM_ENUMERATION("API.StorageAccess.AllowedRequests2", result);
}
bool DomainIsHostOnly(const std::string& domain_string) {
diff --git a/chromium/net/cookies/cookie_util_unittest.cc b/chromium/net/cookies/cookie_util_unittest.cc
index 91d5558b289..2cedc332599 100644
--- a/chromium/net/cookies/cookie_util_unittest.cc
+++ b/chromium/net/cookies/cookie_util_unittest.cc
@@ -168,8 +168,7 @@ TEST(CookieUtilTest, ParseCookieExpirationTimeBeyond2038) {
// It should either have an exact value, or be base::Time::Max(). For
// simplicity just check that it is greater than an arbitray date.
- base::Time almost_jan_2038 =
- base::Time::UnixEpoch() + base::TimeDelta::FromDays(365 * 68);
+ base::Time almost_jan_2038 = base::Time::UnixEpoch() + base::Days(365 * 68);
EXPECT_LT(almost_jan_2038, parsed_time);
}
}
diff --git a/chromium/net/cookies/parse_cookie_line_fuzzer.cc b/chromium/net/cookies/parse_cookie_line_fuzzer.cc
index 830a8bfc078..3bb8dd889b4 100644
--- a/chromium/net/cookies/parse_cookie_line_fuzzer.cc
+++ b/chromium/net/cookies/parse_cookie_line_fuzzer.cc
@@ -10,17 +10,27 @@
#include "base/check_op.h"
#include "net/cookies/parsed_cookie.h"
-const std::string GetArbitraryString(FuzzedDataProvider* data_provider) {
- // Adding a fudge factor to kMaxCookieSize so that both branches of the bounds
- // detection code will be tested.
+const std::string GetArbitraryNameValueString(
+ FuzzedDataProvider* data_provider) {
+ // There's no longer an upper bound on the size of a cookie line, but
+ // in practice using double kMaxCookieNamePlusValueSize should allow
+ // the majority of interesting cases to be covered.
return data_provider->ConsumeRandomLengthString(
- net::ParsedCookie::kMaxCookieSize + 10);
+ net::ParsedCookie::kMaxCookieNamePlusValueSize * 2);
+}
+
+const std::string GetArbitraryAttributeValueString(
+ FuzzedDataProvider* data_provider) {
+ // Adding a fudge factor to kMaxCookieAttributeValueSize so that both branches
+ // of the bounds detection code will be tested.
+ return data_provider->ConsumeRandomLengthString(
+ net::ParsedCookie::kMaxCookieAttributeValueSize + 10);
}
// Entry point for LibFuzzer.
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
FuzzedDataProvider data_provider(data, size);
- const std::string cookie_line = GetArbitraryString(&data_provider);
+ const std::string cookie_line = GetArbitraryNameValueString(&data_provider);
net::ParsedCookie parsed_cookie(cookie_line);
// Call zero or one of ParsedCookie's mutator methods. Should not call
@@ -28,10 +38,10 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
const uint8_t action = data_provider.ConsumeIntegralInRange(0, 12);
switch (action) {
case 1:
- parsed_cookie.SetName(GetArbitraryString(&data_provider));
+ parsed_cookie.SetName(GetArbitraryNameValueString(&data_provider));
break;
case 2:
- parsed_cookie.SetValue(GetArbitraryString(&data_provider));
+ parsed_cookie.SetValue(GetArbitraryNameValueString(&data_provider));
break;
}
@@ -39,16 +49,20 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
switch (action) {
case 3:
if (parsed_cookie.IsValid())
- parsed_cookie.SetPath(GetArbitraryString(&data_provider));
+ parsed_cookie.SetPath(
+ GetArbitraryAttributeValueString(&data_provider));
break;
case 4:
- parsed_cookie.SetDomain(GetArbitraryString(&data_provider));
+ parsed_cookie.SetDomain(
+ GetArbitraryAttributeValueString(&data_provider));
break;
case 5:
- parsed_cookie.SetExpires(GetArbitraryString(&data_provider));
+ parsed_cookie.SetExpires(
+ GetArbitraryAttributeValueString(&data_provider));
break;
case 6:
- parsed_cookie.SetMaxAge(GetArbitraryString(&data_provider));
+ parsed_cookie.SetMaxAge(
+ GetArbitraryAttributeValueString(&data_provider));
break;
case 7:
parsed_cookie.SetIsSecure(data_provider.ConsumeBool());
@@ -57,10 +71,12 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
parsed_cookie.SetIsHttpOnly(data_provider.ConsumeBool());
break;
case 9:
- parsed_cookie.SetSameSite(GetArbitraryString(&data_provider));
+ parsed_cookie.SetSameSite(
+ GetArbitraryAttributeValueString(&data_provider));
break;
case 10:
- parsed_cookie.SetPriority(GetArbitraryString(&data_provider));
+ parsed_cookie.SetPriority(
+ GetArbitraryAttributeValueString(&data_provider));
break;
case 11:
parsed_cookie.SetIsSameParty(data_provider.ConsumeBool());
@@ -76,17 +92,8 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
const std::string serialized = parsed_cookie.ToCookieLine();
net::ParsedCookie reparsed_cookie(serialized);
const std::string reserialized = reparsed_cookie.ToCookieLine();
-
- // RFC6265 requires semicolons to be followed by spaces. Because our parser
- // permits this rule to be broken, but follows the rule in ToCookieLine(),
- // it's possible to serialize a string that's longer than the original
- // input. If the serialized string exceeds kMaxCookieSize, the parser will
- // reject it. For this fuzzer, we are considering this situation a false
- // positive.
- if (serialized.size() <= net::ParsedCookie::kMaxCookieSize) {
- CHECK(reparsed_cookie.IsValid());
- CHECK_EQ(serialized, reserialized);
- }
+ CHECK(reparsed_cookie.IsValid());
+ CHECK_EQ(serialized, reserialized);
}
return 0;
diff --git a/chromium/net/cookies/parsed_cookie.cc b/chromium/net/cookies/parsed_cookie.cc
index a8a449c1a99..56c6bff0c87 100644
--- a/chromium/net/cookies/parsed_cookie.cc
+++ b/chromium/net/cookies/parsed_cookie.cc
@@ -47,6 +47,7 @@
#include "base/logging.h"
#include "base/metrics/histogram_macros.h"
#include "base/strings/string_util.h"
+#include "net/base/features.h"
#include "net/cookies/cookie_constants.h"
#include "net/cookies/cookie_inclusion_status.h"
#include "net/http/http_util.h"
@@ -118,7 +119,12 @@ inline bool SeekBackPast(std::string::const_iterator* it,
// ; US-ASCII characters excluding CTLs,
// ; whitespace DQUOTE, comma, semicolon,
// ; and backslash
-bool IsValidCookieValue(const std::string& value) {
+// Note: This function is being replaced by
+// ParsedCookie::IsValidCookieValue() but remains here while we test that
+// removing this functionality doesn't break things.
+// TODO(crbug.com/1243852) Remove this when kExtraCookieValidityChecks
+// gets removed (assuming the associated changes cause no issues).
+bool IsValidCookieValueLegacy(const std::string& value) {
// Number of characters to skip in validation at beginning and end of string.
size_t skip = 0;
if (value.size() >= 2 && *value.begin() == '"' && *(value.end() - 1) == '"')
@@ -160,12 +166,13 @@ ParsedCookie::ParsedCookie(const std::string& cookie_line,
if (status_out == nullptr) {
status_out = &blank_status;
}
+ *status_out = CookieInclusionStatus();
- if (cookie_line.size() > kMaxCookieSize) {
+ if ((!base::FeatureList::IsEnabled(features::kExtraCookieValidityChecks)) &&
+ cookie_line.size() > kMaxCookieSize) {
DVLOG(1) << "Not parsing cookie, too large: " << cookie_line.size();
- // TODO(crbug.com/1228815): Apply more specific exclusion reasons.
status_out->AddExclusionReason(
- CookieInclusionStatus::EXCLUDE_FAILURE_TO_STORE);
+ CookieInclusionStatus::EXCLUDE_NAME_VALUE_PAIR_EXCEEDS_MAX_SIZE);
return;
}
@@ -210,30 +217,91 @@ CookiePriority ParsedCookie::Priority() const {
}
bool ParsedCookie::SetName(const std::string& name) {
- if (!name.empty() && !HttpUtil::IsToken(name))
- return false;
+ if (base::FeatureList::IsEnabled(features::kExtraCookieValidityChecks)) {
+ const std::string& value = pairs_.empty() ? "" : pairs_[0].second;
+
+ // Ensure there are no invalid characters in `name`. This should be done
+ // before calling ParseTokenString because we want terminating characters
+ // ('\r', '\n', and '\0') and '=' in `name` to cause a rejection instead of
+ // truncation.
+ // TODO(crbug.com/1233602) Once we change logic more broadly to reject
+ // cookies containing these characters, we should be able to simplify this
+ // logic since IsValidCookieNameValuePair() also calls IsValidCookieName().
+ // Also, this check will currently fail if `name` has a tab character in the
+ // leading or trailing whitespace, which is inconsistent with what happens
+ // when parsing a cookie line in the constructor (but the old logic for
+ // SetName() behaved this way as well).
+ if (!IsValidCookieName(name)) {
+ return false;
+ }
- // Fail if we'd be creating a cookie with an empty name and value.
- if (name.empty() && (pairs_.empty() || pairs_[0].second.empty()))
- return false;
+ // Use the same whitespace trimming code as the constructor.
+ const std::string& parsed_name = ParseTokenString(name);
+
+ if (!IsValidCookieNameValuePair(parsed_name, value)) {
+ return false;
+ }
+
+ if (pairs_.empty())
+ pairs_.push_back(std::make_pair("", ""));
+ pairs_[0].first = parsed_name;
+
+ } else {
+ if (!name.empty() && !HttpUtil::IsToken(name))
+ return false;
- if (pairs_.empty())
- pairs_.push_back(std::make_pair("", ""));
- pairs_[0].first = name;
+ // Fail if we'd be creating a cookie with an empty name and value.
+ if (name.empty() && (pairs_.empty() || pairs_[0].second.empty()))
+ return false;
+
+ if (pairs_.empty())
+ pairs_.push_back(std::make_pair("", ""));
+ pairs_[0].first = name;
+ }
return true;
}
bool ParsedCookie::SetValue(const std::string& value) {
- if (!IsValidCookieValue(value))
- return false;
+ if (base::FeatureList::IsEnabled(features::kExtraCookieValidityChecks)) {
+ const std::string& name = pairs_.empty() ? "" : pairs_[0].first;
+
+ // Ensure there are no invalid characters in `value`. This should be done
+ // before calling ParseValueString because we want terminating characters
+ // ('\r', '\n', and '\0') in `value` to cause a rejection instead of
+ // truncation.
+ // TODO(crbug.com/1233602) Once we change logic more broadly to reject
+ // cookies containing these characters, we should be able to simplify this
+ // logic since IsValidCookieNameValuePair() also calls IsValidCookieValue().
+ // Also, this check will currently fail if `value` has a tab character in
+ // the leading or trailing whitespace, which is inconsistent with what
+ // happens when parsing a cookie line in the constructor (but the old logic
+ // for SetValue() behaved this way as well).
+ if (!IsValidCookieValue(value)) {
+ return false;
+ }
- // Fail if we'd be creating a cookie with an empty name and value.
- if (value.empty() && (pairs_.empty() || pairs_[0].first.empty()))
- return false;
+ // Use the same whitespace trimming code as the constructor.
+ const std::string& parsed_value = ParseValueString(value);
+
+ if (!IsValidCookieNameValuePair(name, parsed_value)) {
+ return false;
+ }
+ if (pairs_.empty())
+ pairs_.push_back(std::make_pair("", ""));
+ pairs_[0].second = parsed_value;
+
+ } else {
+ if (!IsValidCookieValueLegacy(value))
+ return false;
- if (pairs_.empty())
- pairs_.push_back(std::make_pair("", ""));
- pairs_[0].second = value;
+ // Fail if we'd be creating a cookie with an empty name and value.
+ if (value.empty() && (pairs_.empty() || pairs_[0].first.empty()))
+ return false;
+
+ if (pairs_.empty())
+ pairs_.push_back(std::make_pair("", ""));
+ pairs_[0].second = value;
+ }
return true;
}
@@ -400,16 +468,120 @@ bool ParsedCookie::ValueMatchesParsedValue(const std::string& value) {
}
// static
-bool ParsedCookie::IsValidCookieAttributeValue(const std::string& value) {
- // The greatest common denominator of cookie attribute values is
- // <any CHAR except CTLs or ";"> according to RFC 6265.
- for (std::string::const_iterator i = value.begin(); i != value.end(); ++i) {
- if (HttpUtil::IsControlChar(*i) || *i == ';')
+bool ParsedCookie::IsValidCookieName(const std::string& name) {
+ // IsValidCookieName() returns whether a string matches the following
+ // grammar:
+ //
+ // cookie-name = *cookie-name-octet
+ // cookie-name-octet = %x20-3A / %x3C / %x3E-7E / %x80-FF
+ // ; octets excluding CTLs, ";", and "="
+ //
+ // This can be used to determine whether cookie names and cookie attribute
+ // names contain any invalid characters.
+ //
+ // Note that RFC6265bis section 4.1.1 suggests a stricter grammar for
+ // parsing cookie names, but we choose to allow a wider range of characters
+ // than what's allowed by that grammar (while still conforming to the
+ // requirements of the parsing algorithm defined in section 5.2).
+ //
+ // For reference, see:
+ // - https://crbug.com/238041
+ for (char i : name) {
+ if (HttpUtil::IsControlChar(i) || i == ';' || i == '=')
+ return false;
+ }
+ return true;
+}
+
+// static
+bool ParsedCookie::IsValidCookieValue(const std::string& value) {
+ // IsValidCookieValue() returns whether a string matches the following
+ // grammar:
+ //
+ // cookie-value = *cookie-value-octet
+ // cookie-value-octet = %x20-3A / %x3C-7E / %x80-FF
+ // ; octets excluding CTLs and ";"
+ //
+ // This can be used to determine whether cookie values contain any invalid
+ // characters.
+ //
+ // Note that RFC6265bis section 4.1.1 suggests a stricter grammar for
+ // parsing cookie values, but we choose to allow a wider range of characters
+ // than what's allowed by that grammar (while still conforming to the
+ // requirements of the parsing algorithm defined in section 5.2).
+ //
+ // For reference, see:
+ // - https://crbug.com/238041
+ for (char i : value) {
+ if (HttpUtil::IsControlChar(i) || i == ';')
return false;
}
return true;
}
+// static
+bool ParsedCookie::IsValidCookieAttributeValueLegacy(const std::string& value) {
+ // This legacy method only checks the character set, so use
+ // CookieAttributeValueHasValidCharSet()
+ return CookieAttributeValueHasValidCharSet(value);
+}
+
+// static
+bool ParsedCookie::CookieAttributeValueHasValidCharSet(
+ const std::string& value) {
+ // A cookie attribute value has the same character set restrictions as cookie
+ // values, so re-use the validation function for that.
+ return IsValidCookieValue(value);
+}
+
+// static
+bool ParsedCookie::CookieAttributeValueHasValidSize(const std::string& value) {
+ return (value.size() <= kMaxCookieAttributeValueSize);
+}
+
+// static
+bool ParsedCookie::IsValidCookieNameValuePair(
+ const std::string& name,
+ const std::string& value,
+ CookieInclusionStatus* status_out) {
+ // Ignore cookies with neither name nor value.
+ if (name.empty() && value.empty()) {
+ if (status_out != nullptr) {
+ // TODO(crbug.com/1228815): Apply more specific exclusion reasons.
+ status_out->AddExclusionReason(
+ CookieInclusionStatus::EXCLUDE_FAILURE_TO_STORE);
+ }
+ // TODO(crbug.com/1228815) Note - if the exclusion reasons change to no
+ // longer be the same, we'll need to not return right away and evaluate all
+ // of the checks.
+ return false;
+ }
+
+ // Enforce a length limit for name + value per RFC6265bis.
+ base::CheckedNumeric<size_t> name_value_pair_size = name.size();
+ name_value_pair_size += value.size();
+ if (!name_value_pair_size.IsValid() ||
+ (name_value_pair_size.ValueOrDie() > kMaxCookieNamePlusValueSize)) {
+ if (status_out != nullptr) {
+ status_out->AddExclusionReason(
+ CookieInclusionStatus::EXCLUDE_NAME_VALUE_PAIR_EXCEEDS_MAX_SIZE);
+ }
+ return false;
+ }
+
+ // Ignore Set-Cookie directives containing control characters. See
+ // http://crbug.com/238041.
+ if (!IsValidCookieName(name) || !IsValidCookieValue(value)) {
+ // TODO(crbug.com/1228815): Apply more specific exclusion reasons.
+ if (status_out != nullptr) {
+ status_out->AddExclusionReason(
+ CookieInclusionStatus::EXCLUDE_FAILURE_TO_STORE);
+ }
+ return false;
+ }
+ return true;
+}
+
// Parse all token/value pairs and populate pairs_.
void ParsedCookie::ParseTokenValuePairs(const std::string& cookie_line,
CookieInclusionStatus& status_out) {
@@ -424,6 +596,26 @@ void ParsedCookie::ParseTokenValuePairs(const std::string& cookie_line,
// Then we can log any unexpected terminators.
std::string::const_iterator end = FindFirstTerminator(cookie_line);
+ // For metrics on truncating character presence in the cookie line.
+ if (end < cookie_line.end()) {
+ switch (*end) {
+ case '\0':
+ truncating_char_in_cookie_string_type_ =
+ TruncatingCharacterInCookieStringType::kTruncatingCharNull;
+ break;
+ case '\r':
+ truncating_char_in_cookie_string_type_ =
+ TruncatingCharacterInCookieStringType::kTruncatingCharNewline;
+ break;
+ case '\n':
+ truncating_char_in_cookie_string_type_ =
+ TruncatingCharacterInCookieStringType::kTruncatingCharLineFeed;
+ break;
+ default:
+ NOTREACHED();
+ }
+ }
+
// Exit early for an empty cookie string.
if (it == end) {
// TODO(crbug.com/1228815): Apply more specific exclusion reasons.
@@ -475,31 +667,76 @@ void ParsedCookie::ParseTokenValuePairs(const std::string& cookie_line,
// OK, we're finished with a Token/Value.
pair.second = std::string(value_start, value_end);
- // Ignore cookies with neither name nor value.
- if (pair_num == 0 && (pair.first.empty() && pair.second.empty())) {
- // TODO(crbug.com/1228815): Apply more specific exclusion reasons.
- status_out.AddExclusionReason(
- CookieInclusionStatus::EXCLUDE_FAILURE_TO_STORE);
- pairs_.clear();
- break;
- }
+ if (base::FeatureList::IsEnabled(features::kExtraCookieValidityChecks)) {
+ bool ignore_pair = false;
+ if (pair_num == 0) {
+ if (!IsValidCookieNameValuePair(pair.first, pair.second, &status_out)) {
+ pairs_.clear();
+ break;
+ }
+ } else {
+ // From RFC2109: "Attributes (names) (attr) are case-insensitive."
+ pair.first = base::ToLowerASCII(pair.first);
+
+ // Attribute names have the same character set limitations as cookie
+ // names, but only a handful of values are allowed. We don't check that
+ // this attribute name is one of the allowed ones here, so just re-use
+ // the cookie name check.
+ if (!IsValidCookieName(pair.first)) {
+ // TODO(crbug.com/1228815): Apply more specific exclusion reasons.
+ status_out.AddExclusionReason(
+ CookieInclusionStatus::EXCLUDE_FAILURE_TO_STORE);
+ pairs_.clear();
+ break;
+ }
+
+ if (!CookieAttributeValueHasValidCharSet(pair.second)) {
+ // If the attribute value contains invalid characters, the whole
+ // cookie should be ignored.
+ status_out.AddExclusionReason(
+ CookieInclusionStatus::EXCLUDE_FAILURE_TO_STORE);
+ pairs_.clear();
+ break;
+ }
+
+ if (!CookieAttributeValueHasValidSize(pair.second)) {
+ // If the attribute value is too large, it should be ignored.
+ ignore_pair = true;
+ status_out.AddWarningReason(
+ CookieInclusionStatus::WARN_ATTRIBUTE_VALUE_EXCEEDS_MAX_SIZE);
+ }
+ }
- // From RFC2109: "Attributes (names) (attr) are case-insensitive."
- if (pair_num != 0)
- pair.first = base::ToLowerASCII(pair.first);
+ if (!ignore_pair) {
+ pairs_.push_back(pair);
+ }
+ } else {
+ // Ignore cookies with neither name nor value.
+ if (pair_num == 0 && (pair.first.empty() && pair.second.empty())) {
+ // TODO(crbug.com/1228815): Apply more specific exclusion reasons.
+ status_out.AddExclusionReason(
+ CookieInclusionStatus::EXCLUDE_FAILURE_TO_STORE);
+ pairs_.clear();
+ break;
+ }
- // Ignore Set-Cookie directives contaning control characters. See
- // http://crbug.com/238041.
- if (!IsValidCookieAttributeValue(pair.first) ||
- !IsValidCookieAttributeValue(pair.second)) {
- // TODO(crbug.com/1228815): Apply more specific exclusion reasons.
- status_out.AddExclusionReason(
- CookieInclusionStatus::EXCLUDE_FAILURE_TO_STORE);
- pairs_.clear();
- break;
- }
+ // From RFC2109: "Attributes (names) (attr) are case-insensitive."
+ if (pair_num != 0)
+ pair.first = base::ToLowerASCII(pair.first);
+
+ // Ignore Set-Cookie directives containing control characters. See
+ // http://crbug.com/238041.
+ if (!IsValidCookieAttributeValueLegacy(pair.first) ||
+ !IsValidCookieAttributeValueLegacy(pair.second)) {
+ // TODO(crbug.com/1228815): Apply more specific exclusion reasons.
+ status_out.AddExclusionReason(
+ CookieInclusionStatus::EXCLUDE_FAILURE_TO_STORE);
+ pairs_.clear();
+ break;
+ }
- pairs_.push_back(pair);
+ pairs_.push_back(pair);
+ }
// We've processed a token/value pair, we're either at the end of
// the string or a ValueSeparator like ';', which we want to skip.
@@ -509,14 +746,14 @@ void ParsedCookie::ParseTokenValuePairs(const std::string& cookie_line,
// For metrics on name/value truncation.
//
- // If we stopped before the (real) end of the string and the leftovers include
- // something other than terminating characters or whitespace then this cookie
- // was truncated due to a terminating CTL.
+ // If we stopped before the (real) end of the string and the leftovers
+ // include something other than terminating characters or whitespace then
+ // this cookie was truncated due to a terminating CTL.
//
- // We only care about the name or value being truncated which means we only
- // care about the first pair. If the pairs_.size() > 1 then that means the
- // truncation happened after name/value parsing which we're not concerned
- // about for metrics.
+ // We only care about the name or value being truncated which means we
+ // only care about the first pair. If the pairs_.size() > 1 then that
+ // means the truncation happened after name/value parsing which we're not
+ // concerned about for metrics.
using std::string_literals::operator""s;
if (pairs_.size() == 1 &&
cookie_line.find_first_not_of("\n\r\0 \t"s, end - start) !=
@@ -567,12 +804,26 @@ bool ParsedCookie::SetString(size_t* index,
// produce a cookie with "path" attribute equal to "baz" (no spaces). We
// should not produce cookie lines that parse to different key/value pairs!
- // Inputs containing invalid characters should be ignored.
- if (!IsValidCookieAttributeValue(untrusted_value))
- return false;
+ // Inputs containing invalid characters or attribute value strings that are
+ // too large should be ignored. Note that we check the attribute value size
+ // after removing leading and trailing whitespace.
+ if (base::FeatureList::IsEnabled(features::kExtraCookieValidityChecks)) {
+ if (!CookieAttributeValueHasValidCharSet(untrusted_value))
+ return false;
+
+ } else {
+ if (!IsValidCookieAttributeValueLegacy(untrusted_value))
+ return false;
+ }
// Use the same whitespace trimming code as the constructor.
const std::string parsed_value = ParseValueString(untrusted_value);
+
+ if (base::FeatureList::IsEnabled(features::kExtraCookieValidityChecks)) {
+ if (!CookieAttributeValueHasValidSize(parsed_value))
+ return false;
+ }
+
if (parsed_value.empty()) {
ClearAttributePair(*index);
return true;
@@ -608,8 +859,8 @@ bool ParsedCookie::SetAttributePair(size_t* index,
void ParsedCookie::ClearAttributePair(size_t index) {
// The first pair (name/value of cookie at pairs_[0]) cannot be cleared.
- // Cookie attributes that don't have a value at the moment, are represented
- // with an index being equal to 0.
+ // Cookie attributes that don't have a value at the moment, are
+ // represented with an index being equal to 0.
if (index == 0)
return;
diff --git a/chromium/net/cookies/parsed_cookie.h b/chromium/net/cookies/parsed_cookie.h
index cd2897bf628..61377bfed70 100644
--- a/chromium/net/cookies/parsed_cookie.h
+++ b/chromium/net/cookies/parsed_cookie.h
@@ -23,11 +23,19 @@ class NET_EXPORT ParsedCookie {
typedef std::pair<std::string, std::string> TokenValuePair;
typedef std::vector<TokenValuePair> PairList;
- // The maximum length of a cookie string we will try to parse
+ // The maximum length of a cookie string we will try to parse.
+ // TODO(crbug.com/1243852) Remove this when kExtraCookieValidityChecks
+ // gets removed (assuming the associated changes cause no issues).
static const size_t kMaxCookieSize = 4096;
+ // The maximum length allowed for a cookie string's name/value pair.
+ static const size_t kMaxCookieNamePlusValueSize = 4096;
+
+ // The maximum length allowed for each attribute value in a cookie string.
+ static const size_t kMaxCookieAttributeValueSize = 1024;
+
// Construct from a cookie string like "BLAH=1; path=/; domain=.google.com"
- // Format is according to RFC 6265. Cookies with both name and value empty
+ // Format is according to RFC6265bis. Cookies with both name and value empty
// will be considered invalid.
// `status_out` is a nullable output param which will be populated with
// informative exclusion reasons if the resulting ParsedCookie is invalid.
@@ -35,6 +43,10 @@ class NET_EXPORT ParsedCookie {
// is valid.
explicit ParsedCookie(const std::string& cookie_line,
CookieInclusionStatus* status_out = nullptr);
+
+ ParsedCookie(const ParsedCookie&) = delete;
+ ParsedCookie& operator=(const ParsedCookie&) = delete;
+
~ParsedCookie();
// You should not call any other methods except for SetName/SetValue on the
@@ -46,13 +58,25 @@ class NET_EXPORT ParsedCookie {
const std::string& Value() const { return pairs_[0].second; }
bool HasPath() const { return path_index_ != 0; }
- const std::string& Path() const { return pairs_[path_index_].second; }
+ const std::string& Path() const {
+ DCHECK(HasPath());
+ return pairs_[path_index_].second;
+ }
bool HasDomain() const { return domain_index_ != 0; }
- const std::string& Domain() const { return pairs_[domain_index_].second; }
+ const std::string& Domain() const {
+ DCHECK(HasDomain());
+ return pairs_[domain_index_].second;
+ }
bool HasExpires() const { return expires_index_ != 0; }
- const std::string& Expires() const { return pairs_[expires_index_].second; }
+ const std::string& Expires() const {
+ DCHECK(HasExpires());
+ return pairs_[expires_index_].second;
+ }
bool HasMaxAge() const { return maxage_index_ != 0; }
- const std::string& MaxAge() const { return pairs_[maxage_index_].second; }
+ const std::string& MaxAge() const {
+ DCHECK(HasMaxAge());
+ return pairs_[maxage_index_].second;
+ }
bool IsSecure() const { return secure_index_ != 0; }
bool IsHttpOnly() const { return httponly_index_ != 0; }
// Also spits out an enum value representing the string given as the SameSite
@@ -63,7 +87,10 @@ class NET_EXPORT ParsedCookie {
bool IsSameParty() const { return same_party_index_ != 0; }
bool IsPartitioned() const { return partitioned_index_ != 0; }
bool HasTruncatedNameOrValue() const { return truncated_name_or_value_; }
-
+ TruncatingCharacterInCookieStringType
+ GetTruncatingCharacterInCookieStringType() const {
+ return truncating_char_in_cookie_string_type_;
+ }
// Returns the number of attributes, for example, returning 2 for:
// "BLAH=hah; path=/; domain=.google.com"
size_t NumberOfAttributes() const { return pairs_.size() - 1; }
@@ -126,8 +153,30 @@ class NET_EXPORT ParsedCookie {
// Returns |true| if the parsed version of |value| matches |value|.
static bool ValueMatchesParsedValue(const std::string& value);
- // Is the string valid as the value of a cookie attribute?
- static bool IsValidCookieAttributeValue(const std::string& value);
+ // Is the string valid as the name of the cookie or as an attribute name?
+ static bool IsValidCookieName(const std::string& name);
+
+ // Is the string valid as the value of the cookie?
+ static bool IsValidCookieValue(const std::string& value);
+
+ // Is the string free of any characters not allowed in attribute values?
+ static bool CookieAttributeValueHasValidCharSet(const std::string& value);
+
+ // Is the string less than the size limits set for attribute values?
+ static bool CookieAttributeValueHasValidSize(const std::string& value);
+
+ // Is the string valid as a cookie attribute value? (only checks the character
+ // set - no length checks performed)
+ static bool IsValidCookieAttributeValueLegacy(const std::string& value);
+
+ // Returns `true` if the name and value combination are valid. Calls
+ // IsValidCookieName() and IsValidCookieValue() on `name` and `value`
+ // respectively, in addition to checking that the sum of the two doesn't
+ // exceed size limits specified in RFC6265bis.
+ static bool IsValidCookieNameValuePair(
+ const std::string& name,
+ const std::string& value,
+ CookieInclusionStatus* status_out = nullptr);
private:
void ParseTokenValuePairs(const std::string& cookie_line,
@@ -177,8 +226,8 @@ class NET_EXPORT ParsedCookie {
// For metrics on cookie name/value truncation. See usage at the bottom of
// `ParseTokenValuePairs()` for more details.
bool truncated_name_or_value_ = false;
-
- DISALLOW_COPY_AND_ASSIGN(ParsedCookie);
+ TruncatingCharacterInCookieStringType truncating_char_in_cookie_string_type_ =
+ TruncatingCharacterInCookieStringType::kTruncatingCharNone;
};
} // namespace net
diff --git a/chromium/net/cookies/parsed_cookie_unittest.cc b/chromium/net/cookies/parsed_cookie_unittest.cc
index c511afaab16..fe8ccbac49c 100644
--- a/chromium/net/cookies/parsed_cookie_unittest.cc
+++ b/chromium/net/cookies/parsed_cookie_unittest.cc
@@ -4,7 +4,10 @@
#include <string>
+#include "base/test/scoped_feature_list.h"
+#include "net/base/features.h"
#include "net/cookies/cookie_constants.h"
+#include "net/cookies/cookie_inclusion_status.h"
#include "net/cookies/parsed_cookie.h"
#include "testing/gtest/include/gtest/gtest.h"
@@ -145,6 +148,12 @@ TEST(ParsedCookieTest, ParseValueStrings) {
}
TEST(ParsedCookieTest, TestQuoted) {
+ // Ensure that kExtraCookieValidityChecks is always enabled so that the
+ // additional test cases get run.
+ base::test::ScopedFeatureList scope_feature_list;
+ scope_feature_list.InitWithFeatureState(features::kExtraCookieValidityChecks,
+ true);
+
// These are some quoting cases which the major browsers all
// handle differently. I've tested Internet Explorer 6, Opera 9.6,
// Firefox 3, and Safari Windows 3.2.1. We originally tried to match
@@ -184,6 +193,11 @@ TEST(ParsedCookieTest, TestQuoted) {
EXPECT_EQ("aBc", pc.Name());
EXPECT_EQ(test.expected, pc.Value());
+ if (base::FeatureList::IsEnabled(features::kExtraCookieValidityChecks)) {
+ EXPECT_TRUE(pc.SetValue(pc.Value()));
+ EXPECT_EQ(test.expected, pc.Value());
+ }
+
// If a path was quoted, the path attribute keeps the quotes. This will
// make the cookie effectively useless, but path parameters aren't supposed
// to be quoted. Bug 1261605.
@@ -248,6 +262,26 @@ TEST(ParsedCookieTest, MissingName) {
EXPECT_EQ("ABC", pc.Value());
EXPECT_EQ(COOKIE_PRIORITY_DEFAULT, pc.Priority());
EXPECT_EQ(0U, pc.NumberOfAttributes());
+
+ // Ensure that a preceding equal sign is emitted in the cookie line.
+
+ // Note that this goes against what's specified in RFC6265bis and differs from
+ // how CanonicalCookie produces cookie lines. As currently written (draft 9),
+ // the spec says that a cookie with an empty name should not prepend an '='
+ // character when writing out the cookie line, but in the case where the value
+ // already contains an equal sign the cookie line will be parsed incorrectly
+ // on the receiving end. ParsedCookie.ToCookieLine is only used by the
+ // extensions API to feed modified cookies into a network request for
+ // reparsing, though, so here it's more important that the values always
+ // deserialize correctly than conform to the spec
+ ParsedCookie pc2("=ABC");
+ EXPECT_EQ("=ABC", pc2.ToCookieLine());
+ EXPECT_TRUE(pc2.SetValue("param=value"));
+ EXPECT_EQ("=param=value", pc2.ToCookieLine());
+ ParsedCookie pc3("=param=value");
+ EXPECT_EQ("", pc3.Name());
+ EXPECT_EQ("param=value", pc3.Value());
+ EXPECT_EQ("=param=value", pc3.ToCookieLine());
}
TEST(ParsedCookieTest, MissingValue) {
@@ -259,6 +293,10 @@ TEST(ParsedCookieTest, MissingValue) {
EXPECT_EQ("/wee", pc.Path());
EXPECT_EQ(COOKIE_PRIORITY_DEFAULT, pc.Priority());
EXPECT_EQ(1U, pc.NumberOfAttributes());
+
+ // Ensure that a trailing equal sign is emitted in the cookie line
+ ParsedCookie pc2("ABC=");
+ EXPECT_EQ("ABC=", pc2.ToCookieLine());
}
TEST(ParsedCookieTest, Whitespace) {
@@ -334,16 +372,229 @@ TEST(ParsedCookieTest, LotsOfPairs) {
}
}
-// TODO(erikwright): some better test cases for invalid cookies.
-TEST(ParsedCookieTest, InvalidTooLong) {
+TEST(ParsedCookieTest, EnforceSizeConstraintsLegacy) {
+ base::test::ScopedFeatureList scope_feature_list;
+ scope_feature_list.InitWithFeatureState(features::kExtraCookieValidityChecks,
+ false);
+ CookieInclusionStatus status;
+
std::string maxstr;
maxstr.resize(ParsedCookie::kMaxCookieSize, 'a');
- ParsedCookie pc1(maxstr);
+ ParsedCookie pc1(maxstr, &status);
EXPECT_TRUE(pc1.IsValid());
+ EXPECT_TRUE(status.IsInclude());
- ParsedCookie pc2(maxstr + "A");
+ ParsedCookie pc2(maxstr + "A", &status);
EXPECT_FALSE(pc2.IsValid());
+ EXPECT_TRUE(status.HasOnlyExclusionReason(
+ CookieInclusionStatus::ExclusionReason::
+ EXCLUDE_NAME_VALUE_PAIR_EXCEEDS_MAX_SIZE));
+}
+
+TEST(ParsedCookieTest, EnforceSizeConstraints) {
+ // Ensure that kExtraCookieValidityChecks is always enabled so that the
+ // additional test cases get run.
+ base::test::ScopedFeatureList scope_feature_list;
+ scope_feature_list.InitWithFeatureState(features::kExtraCookieValidityChecks,
+ true);
+ CookieInclusionStatus status;
+
+ // Create maximum size and one-less-than-maximum size name and value
+ // strings for testing.
+ std::string max_name(ParsedCookie::kMaxCookieNamePlusValueSize, 'a');
+ std::string max_value(ParsedCookie::kMaxCookieNamePlusValueSize, 'b');
+ std::string almost_max_name = max_name.substr(1, std::string::npos);
+ std::string almost_max_value = max_value.substr(1, std::string::npos);
+
+ // Test name + value size limits enforced by the constructor.
+ ParsedCookie pc1(max_name + "=");
+ EXPECT_TRUE(pc1.IsValid());
+ EXPECT_EQ(max_name, pc1.Name());
+
+ ParsedCookie pc2(max_name + "=; path=/foo;");
+ EXPECT_TRUE(pc2.IsValid());
+ EXPECT_EQ(max_name, pc2.Name());
+
+ ParsedCookie pc3(max_name + "X=", &status);
+ EXPECT_FALSE(pc3.IsValid());
+ EXPECT_TRUE(status.HasOnlyExclusionReason(
+ CookieInclusionStatus::ExclusionReason::
+ EXCLUDE_NAME_VALUE_PAIR_EXCEEDS_MAX_SIZE));
+
+ ParsedCookie pc4("=" + max_value);
+ EXPECT_TRUE(pc4.IsValid());
+ EXPECT_EQ(max_value, pc4.Value());
+
+ ParsedCookie pc5("=" + max_value + "; path=/foo;");
+ EXPECT_TRUE(pc5.IsValid());
+ EXPECT_EQ(max_value, pc5.Value());
+
+ ParsedCookie pc6("=" + max_value + "X", &status);
+ EXPECT_FALSE(pc6.IsValid());
+ EXPECT_TRUE(status.HasOnlyExclusionReason(
+ CookieInclusionStatus::ExclusionReason::
+ EXCLUDE_NAME_VALUE_PAIR_EXCEEDS_MAX_SIZE));
+
+ ParsedCookie pc7(almost_max_name + "=x");
+ EXPECT_TRUE(pc7.IsValid());
+ EXPECT_EQ(almost_max_name, pc7.Name());
+ EXPECT_EQ("x", pc7.Value());
+
+ ParsedCookie pc8(almost_max_name + "=x; path=/foo;");
+ EXPECT_TRUE(pc8.IsValid());
+ EXPECT_EQ(almost_max_name, pc8.Name());
+ EXPECT_EQ("x", pc8.Value());
+
+ ParsedCookie pc9(almost_max_name + "=xX", &status);
+ EXPECT_FALSE(pc9.IsValid());
+ EXPECT_TRUE(status.HasOnlyExclusionReason(
+ CookieInclusionStatus::ExclusionReason::
+ EXCLUDE_NAME_VALUE_PAIR_EXCEEDS_MAX_SIZE));
+
+ ParsedCookie pc10("x=" + almost_max_value);
+ EXPECT_TRUE(pc10.IsValid());
+ EXPECT_EQ("x", pc10.Name());
+ EXPECT_EQ(almost_max_value, pc10.Value());
+
+ ParsedCookie pc11("x=" + almost_max_value + "; path=/foo;");
+ EXPECT_TRUE(pc11.IsValid());
+ EXPECT_EQ("x", pc11.Name());
+ EXPECT_EQ(almost_max_value, pc11.Value());
+
+ ParsedCookie pc12("xX=" + almost_max_value, &status);
+ EXPECT_FALSE(pc12.IsValid());
+ EXPECT_TRUE(status.HasOnlyExclusionReason(
+ CookieInclusionStatus::ExclusionReason::
+ EXCLUDE_NAME_VALUE_PAIR_EXCEEDS_MAX_SIZE));
+
+ // Test attribute value size limits enforced by the constructor.
+ std::string almost_max_path(ParsedCookie::kMaxCookieAttributeValueSize - 1,
+ 'c');
+ std::string max_path = "/" + almost_max_path;
+ std::string too_long_path = "/X" + almost_max_path;
+
+ ParsedCookie pc20("name=value; path=" + max_path);
+ EXPECT_TRUE(pc20.IsValid());
+ EXPECT_TRUE(pc20.HasPath());
+ EXPECT_EQ("/" + almost_max_path, pc20.Path());
+
+ ParsedCookie pc21("name=value; path=" + too_long_path, &status);
+ EXPECT_TRUE(pc21.IsValid());
+ EXPECT_FALSE(pc21.HasPath());
+ EXPECT_TRUE(status.HasWarningReason(
+ CookieInclusionStatus::WARN_ATTRIBUTE_VALUE_EXCEEDS_MAX_SIZE));
+
+ // NOTE: max_domain is based on the max attribute value as defined in
+ // RFC6525bis, but this is larger than what is recommended by RFC1123.
+ // In theory some browsers could restrict domains to that smaller size,
+ // but ParsedCookie doesn't.
+ std::string max_domain(ParsedCookie::kMaxCookieAttributeValueSize, 'd');
+ max_domain.replace(ParsedCookie::kMaxCookieAttributeValueSize - 4, 4, ".com");
+ std::string too_long_domain = "x" + max_domain;
+
+ ParsedCookie pc30("name=value; domain=" + max_domain);
+ EXPECT_TRUE(pc30.IsValid());
+ EXPECT_TRUE(pc30.HasDomain());
+ EXPECT_EQ(max_domain, pc30.Domain());
+
+ ParsedCookie pc31("name=value; domain=" + too_long_domain);
+ EXPECT_TRUE(pc31.IsValid());
+ EXPECT_FALSE(pc31.HasDomain());
+ EXPECT_TRUE(status.HasWarningReason(
+ CookieInclusionStatus::WARN_ATTRIBUTE_VALUE_EXCEEDS_MAX_SIZE));
+
+ std::string pc40_suffix = "; domain=example.com";
+
+ ParsedCookie pc40("a=b" + pc40_suffix);
+ EXPECT_TRUE(pc40.IsValid());
+
+ if (base::FeatureList::IsEnabled(features::kExtraCookieValidityChecks)) {
+ // Test name + value size limits enforced by SetName / SetValue
+ EXPECT_FALSE(pc40.SetName(max_name));
+ EXPECT_EQ("a=b" + pc40_suffix, pc40.ToCookieLine());
+ EXPECT_TRUE(pc40.IsValid());
+
+ EXPECT_FALSE(pc40.SetValue(max_value));
+ EXPECT_EQ("a=b" + pc40_suffix, pc40.ToCookieLine());
+ EXPECT_TRUE(pc40.IsValid());
+
+ EXPECT_TRUE(pc40.SetName(almost_max_name));
+ EXPECT_EQ(almost_max_name + "=b" + pc40_suffix, pc40.ToCookieLine());
+ EXPECT_TRUE(pc40.IsValid());
+
+ EXPECT_FALSE(pc40.SetValue("xX"));
+ EXPECT_EQ(almost_max_name + "=b" + pc40_suffix, pc40.ToCookieLine());
+ EXPECT_TRUE(pc40.IsValid());
+
+ EXPECT_TRUE(pc40.SetName("a"));
+ EXPECT_TRUE(pc40.SetValue(almost_max_value));
+ EXPECT_EQ("a=" + almost_max_value + pc40_suffix, pc40.ToCookieLine());
+ EXPECT_TRUE(pc40.IsValid());
+
+ EXPECT_FALSE(pc40.SetName("xX"));
+ EXPECT_EQ("a=" + almost_max_value + pc40_suffix, pc40.ToCookieLine());
+ EXPECT_TRUE(pc40.IsValid());
+ }
+
+ std::string lots_of_spaces(ParsedCookie::kMaxCookieNamePlusValueSize, ' ');
+ std::string test_str = "test";
+ std::string padded_test_str = lots_of_spaces + test_str + lots_of_spaces;
+
+ if (base::FeatureList::IsEnabled(features::kExtraCookieValidityChecks)) {
+ // Ensure that leading/trailing whitespace gets stripped before the length
+ // calculations are enforced.
+ ParsedCookie pc41("name=value");
+ EXPECT_TRUE(pc41.SetName(padded_test_str));
+ EXPECT_TRUE(pc41.SetValue(padded_test_str));
+ EXPECT_EQ(test_str, pc41.Name());
+ EXPECT_EQ(test_str, pc41.Value());
+ }
+
+ std::string name_equals_value = "name=value";
+ ParsedCookie pc50(name_equals_value);
+
+ EXPECT_TRUE(pc50.SetPath(max_path));
+ EXPECT_EQ(pc50.Path(), max_path);
+ EXPECT_EQ(name_equals_value + "; path=" + max_path, pc50.ToCookieLine());
+ EXPECT_TRUE(pc50.IsValid());
+
+ if (base::FeatureList::IsEnabled(features::kExtraCookieValidityChecks)) {
+ // Test attribute value size limits enforced by SetPath
+ EXPECT_FALSE(pc50.SetPath(too_long_path));
+ EXPECT_EQ(pc50.Path(), max_path);
+ EXPECT_EQ(name_equals_value + "; path=" + max_path, pc50.ToCookieLine());
+ EXPECT_TRUE(pc50.IsValid());
+ }
+
+ std::string test_path = "/test";
+ std::string padded_test_path = lots_of_spaces + test_path + lots_of_spaces;
+
+ EXPECT_TRUE(pc50.SetPath(padded_test_path));
+ EXPECT_EQ(test_path, pc50.Path());
+
+ ParsedCookie pc51(name_equals_value);
+
+ EXPECT_TRUE(pc51.SetDomain(max_domain));
+ EXPECT_EQ(pc51.Domain(), max_domain);
+ EXPECT_EQ(name_equals_value + "; domain=" + max_domain, pc51.ToCookieLine());
+ EXPECT_TRUE(pc51.IsValid());
+
+ if (base::FeatureList::IsEnabled(features::kExtraCookieValidityChecks)) {
+ // Test attribute value size limits enforced by SetDomain
+ EXPECT_FALSE(pc51.SetDomain(too_long_domain));
+ EXPECT_EQ(pc51.Domain(), max_domain);
+ EXPECT_EQ(name_equals_value + "; domain=" + max_domain,
+ pc51.ToCookieLine());
+ EXPECT_TRUE(pc51.IsValid());
+ }
+
+ std::string test_domain = "example.com";
+ std::string padded_test_domain =
+ lots_of_spaces + test_domain + lots_of_spaces;
+
+ EXPECT_TRUE(pc51.SetDomain(padded_test_domain));
+ EXPECT_EQ(test_domain, pc51.Domain());
}
TEST(ParsedCookieTest, EmbeddedTerminator) {
@@ -388,53 +639,139 @@ TEST(ParsedCookieTest, SerializeCookieLine) {
}
TEST(ParsedCookieTest, SetNameAndValue) {
- ParsedCookie cookie("a=b");
- EXPECT_TRUE(cookie.IsValid());
- EXPECT_TRUE(cookie.SetDomain("foobar.com"));
- EXPECT_TRUE(cookie.SetName("name"));
- EXPECT_TRUE(cookie.SetValue("value"));
- EXPECT_EQ("name=value; domain=foobar.com", cookie.ToCookieLine());
- EXPECT_TRUE(cookie.IsValid());
-
- // We don't test
- // ParsedCookie invalid("@foo=bar");
- // EXPECT_FALSE(invalid.IsValid());
- // here because we are slightly more tolerant to invalid cookie names and
- // values that are set by webservers. We only enforce a correct name and
- // value if set via SetName() and SetValue().
+ for (const bool toggle : {false, true}) {
+ base::test::ScopedFeatureList scope_feature_list;
+ scope_feature_list.InitWithFeatureState(
+ features::kExtraCookieValidityChecks, toggle);
+
+ ParsedCookie cookie("a=b");
+ EXPECT_TRUE(cookie.IsValid());
+ EXPECT_TRUE(cookie.SetDomain("foobar.com"));
+ EXPECT_TRUE(cookie.SetName("name"));
+ EXPECT_TRUE(cookie.SetValue("value"));
+ EXPECT_EQ("name=value; domain=foobar.com", cookie.ToCookieLine());
+ EXPECT_TRUE(cookie.IsValid());
+
+ ParsedCookie pc("name=value");
+ EXPECT_TRUE(pc.IsValid());
- ParsedCookie pc("name=value");
- EXPECT_TRUE(pc.IsValid());
+ // Set invalid name / value.
+ EXPECT_FALSE(pc.SetName("foo\nbar"));
+ EXPECT_EQ("name=value", pc.ToCookieLine());
+ EXPECT_TRUE(pc.IsValid());
- // Set invalid name / value.
- EXPECT_FALSE(pc.SetName("@foobar"));
- EXPECT_EQ("name=value", pc.ToCookieLine());
- EXPECT_TRUE(pc.IsValid());
+ EXPECT_FALSE(pc.SetName("foo\rbar"));
+ EXPECT_EQ("name=value", pc.ToCookieLine());
+ EXPECT_TRUE(pc.IsValid());
- EXPECT_FALSE(pc.SetValue("foo bar"));
- EXPECT_EQ("name=value", pc.ToCookieLine());
- EXPECT_TRUE(pc.IsValid());
+ EXPECT_FALSE(pc.SetValue(std::string("foo\0bar", 7)));
+ EXPECT_EQ("name=value", pc.ToCookieLine());
+ EXPECT_TRUE(pc.IsValid());
- EXPECT_FALSE(pc.SetValue("\"foobar"));
- EXPECT_EQ("name=value", pc.ToCookieLine());
- EXPECT_TRUE(pc.IsValid());
+ if (base::FeatureList::IsEnabled(features::kExtraCookieValidityChecks)) {
+ // Set previously invalid name / value.
+ EXPECT_TRUE(pc.SetName("@foobar"));
+ EXPECT_EQ("@foobar=value", pc.ToCookieLine());
+ EXPECT_TRUE(pc.IsValid());
+
+ EXPECT_TRUE(pc.SetName("foo bar"));
+ EXPECT_EQ("foo bar=value", pc.ToCookieLine());
+ EXPECT_TRUE(pc.IsValid());
+
+ EXPECT_TRUE(pc.SetName("\"foobar"));
+ EXPECT_EQ("\"foobar=value", pc.ToCookieLine());
+ EXPECT_TRUE(pc.IsValid());
+
+ EXPECT_TRUE(pc.SetValue("foo bar"));
+ EXPECT_EQ("\"foobar=foo bar", pc.ToCookieLine());
+ EXPECT_TRUE(pc.IsValid());
+
+ EXPECT_TRUE(pc.SetValue("\"foobar"));
+ EXPECT_EQ("\"foobar=\"foobar", pc.ToCookieLine());
+ EXPECT_TRUE(pc.IsValid());
+
+ EXPECT_TRUE(pc.SetName(" foo bar "));
+ EXPECT_EQ("foo bar=\"foobar", pc.ToCookieLine());
+ EXPECT_TRUE(pc.IsValid());
+
+ EXPECT_TRUE(pc.SetValue(" foo bar "));
+ EXPECT_EQ("foo bar=foo bar", pc.ToCookieLine());
+ EXPECT_TRUE(pc.IsValid());
+
+ } else {
+ // Set invalid name / value.
+ EXPECT_FALSE(pc.SetName("@foobar"));
+ EXPECT_EQ("name=value", pc.ToCookieLine());
+ EXPECT_TRUE(pc.IsValid());
+
+ EXPECT_FALSE(pc.SetValue("foo bar"));
+ EXPECT_EQ("name=value", pc.ToCookieLine());
+ EXPECT_TRUE(pc.IsValid());
+
+ EXPECT_FALSE(pc.SetValue("\"foobar"));
+ EXPECT_EQ("name=value", pc.ToCookieLine());
+ EXPECT_TRUE(pc.IsValid());
+
+ EXPECT_FALSE(pc.SetName(" foo bar "));
+ EXPECT_EQ("name=value", pc.ToCookieLine());
+ EXPECT_TRUE(pc.IsValid());
+
+ EXPECT_FALSE(pc.SetValue(" foo bar "));
+ EXPECT_EQ("name=value", pc.ToCookieLine());
+ EXPECT_TRUE(pc.IsValid());
+ }
+
+ // Set valid name / value.
+ EXPECT_TRUE(pc.SetValue("value"));
+ EXPECT_TRUE(pc.SetName(std::string()));
+ EXPECT_EQ("=value", pc.ToCookieLine());
+ EXPECT_TRUE(pc.IsValid());
- // Set valid name / value
- EXPECT_TRUE(pc.SetName(std::string()));
- EXPECT_EQ("=value", pc.ToCookieLine());
- EXPECT_TRUE(pc.IsValid());
+ EXPECT_TRUE(pc.SetName("test"));
+ EXPECT_EQ("test=value", pc.ToCookieLine());
+ EXPECT_TRUE(pc.IsValid());
- EXPECT_TRUE(pc.SetName("test"));
- EXPECT_EQ("test=value", pc.ToCookieLine());
- EXPECT_TRUE(pc.IsValid());
+ EXPECT_TRUE(pc.SetValue("\"foobar\""));
+ EXPECT_EQ("test=\"foobar\"", pc.ToCookieLine());
+ EXPECT_TRUE(pc.IsValid());
- EXPECT_TRUE(pc.SetValue("\"foobar\""));
- EXPECT_EQ("test=\"foobar\"", pc.ToCookieLine());
- EXPECT_TRUE(pc.IsValid());
+ EXPECT_TRUE(pc.SetValue(std::string()));
+ EXPECT_EQ("test=", pc.ToCookieLine());
+ EXPECT_TRUE(pc.IsValid());
- EXPECT_TRUE(pc.SetValue(std::string()));
- EXPECT_EQ("test=", pc.ToCookieLine());
- EXPECT_TRUE(pc.IsValid());
+ // Ensure that failure occurs when trying to set a name containing '='.
+ EXPECT_FALSE(pc.SetName("invalid=name"));
+ EXPECT_EQ("test=", pc.ToCookieLine());
+ EXPECT_TRUE(pc.IsValid());
+
+ // Ensure that trying to set a name containing ';' fails.
+ EXPECT_FALSE(pc.SetName("invalid;name"));
+ EXPECT_EQ("test=", pc.ToCookieLine());
+ EXPECT_TRUE(pc.IsValid());
+
+ EXPECT_FALSE(pc.SetValue("invalid;value"));
+ EXPECT_EQ("test=", pc.ToCookieLine());
+ EXPECT_TRUE(pc.IsValid());
+
+ // Ensure tab characters are treated as control characters.
+ // TODO(crbug.com/1233602) Update this such that tab characters are allowed
+ // and are handled correctly.
+ EXPECT_FALSE(pc.SetName("\tinvalid\t"));
+ EXPECT_EQ("test=", pc.ToCookieLine());
+ EXPECT_TRUE(pc.IsValid());
+
+ EXPECT_FALSE(pc.SetValue("\tinvalid\t"));
+ EXPECT_EQ("test=", pc.ToCookieLine());
+ EXPECT_TRUE(pc.IsValid());
+
+ EXPECT_FALSE(pc.SetName("na\tme"));
+ EXPECT_EQ("test=", pc.ToCookieLine());
+ EXPECT_TRUE(pc.IsValid());
+
+ EXPECT_FALSE(pc.SetValue("val\tue"));
+ EXPECT_EQ("test=", pc.ToCookieLine());
+ EXPECT_TRUE(pc.IsValid());
+ }
}
TEST(ParsedCookieTest, SetAttributes) {
@@ -893,41 +1230,84 @@ TEST(ParsedCookieTest, InvalidNonAlphanumericChars) {
}
TEST(ParsedCookieTest, ValidNonAlphanumericChars) {
- // Note that some of these words are pasted backwords thanks to poor vim bidi
- // support. This should not affect the tests, however.
- const char pc1_literal[] = "name=العربية";
- const char pc2_literal[] = "name=普通話";
- const char pc3_literal[] = "name=ภาษาไทย";
- const char pc4_literal[] = "name=עִבְרִית";
- const char pc5_literal[] = "العربية=value";
- const char pc6_literal[] = "普通話=value";
- const char pc7_literal[] = "ภาษาไทย=value";
- const char pc8_literal[] = "עִבְרִית=value";
- ParsedCookie pc1(pc1_literal);
- ParsedCookie pc2(pc2_literal);
- ParsedCookie pc3(pc3_literal);
- ParsedCookie pc4(pc4_literal);
- ParsedCookie pc5(pc5_literal);
- ParsedCookie pc6(pc6_literal);
- ParsedCookie pc7(pc7_literal);
- ParsedCookie pc8(pc8_literal);
-
- EXPECT_TRUE(pc1.IsValid());
- EXPECT_EQ(pc1_literal, pc1.ToCookieLine());
- EXPECT_TRUE(pc2.IsValid());
- EXPECT_EQ(pc2_literal, pc2.ToCookieLine());
- EXPECT_TRUE(pc3.IsValid());
- EXPECT_EQ(pc3_literal, pc3.ToCookieLine());
- EXPECT_TRUE(pc4.IsValid());
- EXPECT_EQ(pc4_literal, pc4.ToCookieLine());
- EXPECT_TRUE(pc5.IsValid());
- EXPECT_EQ(pc5_literal, pc5.ToCookieLine());
- EXPECT_TRUE(pc6.IsValid());
- EXPECT_EQ(pc6_literal, pc6.ToCookieLine());
- EXPECT_TRUE(pc7.IsValid());
- EXPECT_EQ(pc7_literal, pc7.ToCookieLine());
- EXPECT_TRUE(pc8.IsValid());
- EXPECT_EQ(pc8_literal, pc8.ToCookieLine());
+ // Ensure that kExtraCookieValidityChecks is always enabled so that the
+ // additional test cases get run.
+ for (const bool toggle : {false, true}) {
+ base::test::ScopedFeatureList scope_feature_list;
+ scope_feature_list.InitWithFeatureState(
+ features::kExtraCookieValidityChecks, toggle);
+
+ // Note that some of these words are pasted backwords thanks to poor vim
+ // bidi support. This should not affect the tests, however.
+ const char pc1_literal[] = "name=العربية";
+ const char pc2_literal[] = "name=普通話";
+ const char pc3_literal[] = "name=ภาษาไทย";
+ const char pc4_literal[] = "name=עִבְרִית";
+ const char pc5_literal[] = "العربية=value";
+ const char pc6_literal[] = "普通話=value";
+ const char pc7_literal[] = "ภาษาไทย=value";
+ const char pc8_literal[] = "עִבְרִית=value";
+ const char pc9_literal[] = "@foo=bar";
+
+ ParsedCookie pc1(pc1_literal);
+ ParsedCookie pc2(pc2_literal);
+ ParsedCookie pc3(pc3_literal);
+ ParsedCookie pc4(pc4_literal);
+ ParsedCookie pc5(pc5_literal);
+ ParsedCookie pc6(pc6_literal);
+ ParsedCookie pc7(pc7_literal);
+ ParsedCookie pc8(pc8_literal);
+ ParsedCookie pc9(pc9_literal);
+
+ EXPECT_TRUE(pc1.IsValid());
+ EXPECT_EQ(pc1_literal, pc1.ToCookieLine());
+ EXPECT_TRUE(pc2.IsValid());
+ EXPECT_EQ(pc2_literal, pc2.ToCookieLine());
+ EXPECT_TRUE(pc3.IsValid());
+ EXPECT_EQ(pc3_literal, pc3.ToCookieLine());
+ EXPECT_TRUE(pc4.IsValid());
+ EXPECT_EQ(pc4_literal, pc4.ToCookieLine());
+ EXPECT_TRUE(pc5.IsValid());
+ EXPECT_EQ(pc5_literal, pc5.ToCookieLine());
+ EXPECT_TRUE(pc6.IsValid());
+ EXPECT_EQ(pc6_literal, pc6.ToCookieLine());
+ EXPECT_TRUE(pc7.IsValid());
+ EXPECT_EQ(pc7_literal, pc7.ToCookieLine());
+ EXPECT_TRUE(pc8.IsValid());
+ EXPECT_EQ(pc8_literal, pc8.ToCookieLine());
+ EXPECT_TRUE(pc9.IsValid());
+ EXPECT_EQ(pc9_literal, pc9.ToCookieLine());
+
+ if (base::FeatureList::IsEnabled(features::kExtraCookieValidityChecks)) {
+ EXPECT_TRUE(pc1.SetValue(pc1.Value()));
+ EXPECT_EQ(pc1_literal, pc1.ToCookieLine());
+ EXPECT_TRUE(pc1.IsValid());
+ EXPECT_TRUE(pc2.SetValue(pc2.Value()));
+ EXPECT_EQ(pc2_literal, pc2.ToCookieLine());
+ EXPECT_TRUE(pc2.IsValid());
+ EXPECT_TRUE(pc3.SetValue(pc3.Value()));
+ EXPECT_EQ(pc3_literal, pc3.ToCookieLine());
+ EXPECT_TRUE(pc3.IsValid());
+ EXPECT_TRUE(pc4.SetValue(pc4.Value()));
+ EXPECT_EQ(pc4_literal, pc4.ToCookieLine());
+ EXPECT_TRUE(pc4.IsValid());
+ EXPECT_TRUE(pc5.SetName(pc5.Name()));
+ EXPECT_EQ(pc5_literal, pc5.ToCookieLine());
+ EXPECT_TRUE(pc5.IsValid());
+ EXPECT_TRUE(pc6.SetName(pc6.Name()));
+ EXPECT_EQ(pc6_literal, pc6.ToCookieLine());
+ EXPECT_TRUE(pc6.IsValid());
+ EXPECT_TRUE(pc7.SetName(pc7.Name()));
+ EXPECT_EQ(pc7_literal, pc7.ToCookieLine());
+ EXPECT_TRUE(pc7.IsValid());
+ EXPECT_TRUE(pc8.SetName(pc8.Name()));
+ EXPECT_EQ(pc8_literal, pc8.ToCookieLine());
+ EXPECT_TRUE(pc8.IsValid());
+ EXPECT_TRUE(pc9.SetName(pc9.Name()));
+ EXPECT_EQ(pc9_literal, pc9.ToCookieLine());
+ EXPECT_TRUE(pc9.IsValid());
+ }
+ }
}
TEST(ParsedCookieTest, TruncatedNameOrValue) {
@@ -977,4 +1357,52 @@ TEST(ParsedCookieTest, TruncatedNameOrValue) {
}
}
+TEST(ParsedCookieTest, TruncatingCharInCookieLine) {
+ using std::string_literals::operator""s;
+
+ // Test scenarios where a control char may appear at start, middle and end of
+ // a cookie line. Control char array with NULL (\x0), CR (\xD), LF (xA),
+ // HT (\x9) and BS (\x1B).
+ const struct {
+ const char ctlChar;
+ const TruncatingCharacterInCookieStringType
+ expectedTruncatingCharInCookieStringType;
+ } kTests[] = {
+ {'\x0', TruncatingCharacterInCookieStringType::kTruncatingCharNull},
+ {'\xD', TruncatingCharacterInCookieStringType::kTruncatingCharNewline},
+ {'\xA', TruncatingCharacterInCookieStringType::kTruncatingCharLineFeed},
+ {'\x9', TruncatingCharacterInCookieStringType::kTruncatingCharNone},
+ {'\x1B', TruncatingCharacterInCookieStringType::kTruncatingCharNone}};
+
+ for (const auto& test : kTests) {
+ std::string ctl_string(1, test.ctlChar);
+ std::string ctl_at_start_cookie_string = ctl_string + "foo=bar"s;
+ ParsedCookie ctl_at_start_cookie(ctl_at_start_cookie_string);
+ EXPECT_EQ(ctl_at_start_cookie.GetTruncatingCharacterInCookieStringType(),
+ test.expectedTruncatingCharInCookieStringType);
+
+ std::string ctl_at_middle_cookie_string =
+ "foo=bar;"s + ctl_string + "secure"s;
+ ParsedCookie ctl_at_middle_cookie(ctl_at_start_cookie_string);
+ EXPECT_EQ(ctl_at_middle_cookie.GetTruncatingCharacterInCookieStringType(),
+ test.expectedTruncatingCharInCookieStringType);
+
+ std::string ctl_at_end_cookie_string =
+ "foo=bar;"s + "secure;"s + ctl_string;
+ ParsedCookie ctl_at_end_cookie(ctl_at_start_cookie_string);
+ EXPECT_EQ(ctl_at_end_cookie.GetTruncatingCharacterInCookieStringType(),
+ test.expectedTruncatingCharInCookieStringType);
+ }
+ // Test if there are multiple control characters that terminate.
+ std::string ctls_cookie_string = "foo=bar;\xA\xD"s;
+ ParsedCookie ctls_cookie(ctls_cookie_string);
+ EXPECT_EQ(ctls_cookie.GetTruncatingCharacterInCookieStringType(),
+ TruncatingCharacterInCookieStringType::kTruncatingCharLineFeed);
+ // Test with no control characters.
+ std::string cookie_string = "foo=bar;"s;
+ ParsedCookie cookie(cookie_string);
+ EXPECT_EQ(cookie.GetTruncatingCharacterInCookieStringType(),
+ TruncatingCharacterInCookieStringType::kTruncatingCharNone);
+}
+
} // namespace net
diff --git a/chromium/net/cookies/site_for_cookies.cc b/chromium/net/cookies/site_for_cookies.cc
index 977a0b7c7af..5907339ec9b 100644
--- a/chromium/net/cookies/site_for_cookies.cc
+++ b/chromium/net/cookies/site_for_cookies.cc
@@ -4,6 +4,8 @@
#include "net/cookies/site_for_cookies.h"
+#include <tuple>
+
#include "base/strings/strcat.h"
#include "base/strings/string_util.h"
#include "net/base/registry_controlled_domains/registry_controlled_domain.h"
@@ -204,4 +206,22 @@ void SiteForCookies::MarkIfCrossScheme(const SchemefulSite& other) {
schemefully_same_ = false;
}
+bool operator<(const SiteForCookies& lhs, const SiteForCookies& rhs) {
+ // Similar to IsEquivalent(), if they're both null then they're equivalent
+ // and therefore `lhs` is not < `rhs`.
+ if (lhs.IsNull() && rhs.IsNull())
+ return false;
+
+ // If only `lhs` is null then it's always < `rhs`.
+ if (lhs.IsNull())
+ return true;
+
+ // If only `rhs` is null then `lhs` is not < `rhs`.
+ if (rhs.IsNull())
+ return false;
+
+ // Otherwise neither are null and we need to compare the `site_`s.
+ return lhs.site_ < rhs.site_;
+}
+
} // namespace net
diff --git a/chromium/net/cookies/site_for_cookies.h b/chromium/net/cookies/site_for_cookies.h
index d0379b23522..9db7b5cda80 100644
--- a/chromium/net/cookies/site_for_cookies.h
+++ b/chromium/net/cookies/site_for_cookies.h
@@ -169,6 +169,11 @@ class NET_EXPORT SiteForCookies {
// this function to return true.
bool IsNull() const;
+ // Allows SiteForCookies to be used as a key in STL (for example, a std::set
+ // or std::map).
+ NET_EXPORT friend bool operator<(const SiteForCookies& lhs,
+ const SiteForCookies& rhs);
+
private:
FRIEND_TEST_ALL_PREFIXES(SiteForCookiesTest, SameScheme);
FRIEND_TEST_ALL_PREFIXES(SiteForCookiesTest, SameSchemeOpaque);
diff --git a/chromium/net/cookies/site_for_cookies_unittest.cc b/chromium/net/cookies/site_for_cookies_unittest.cc
index 69526f6de35..bf20aefc876 100644
--- a/chromium/net/cookies/site_for_cookies_unittest.cc
+++ b/chromium/net/cookies/site_for_cookies_unittest.cc
@@ -602,4 +602,30 @@ TEST(SiteForCookiesTest, SameSchemeOpaque) {
}
}
+// Quick correctness check that the less-than operator works as expected.
+TEST(SiteForCookiesTest, LessThan) {
+ SiteForCookies first = SiteForCookies::FromUrl(GURL("https://example.com"));
+ SiteForCookies second =
+ SiteForCookies::FromUrl(GURL("https://examplelonger.com"));
+ SiteForCookies third =
+ SiteForCookies::FromUrl(GURL("https://examplelongerstill.com"));
+
+ SiteForCookies null1 = SiteForCookies();
+ SiteForCookies null2 =
+ SiteForCookies::FromUrl(GURL("https://examplelongerstillstill.com"));
+ null2.SetSchemefullySameForTesting(false);
+
+ EXPECT_LT(first, second);
+ EXPECT_LT(second, third);
+ EXPECT_LT(first, third);
+ EXPECT_LT(null1, first);
+ EXPECT_LT(null2, first);
+
+ EXPECT_FALSE(second < first);
+ EXPECT_FALSE(first < null1);
+ EXPECT_FALSE(first < null2);
+ EXPECT_FALSE(null1 < null2);
+ EXPECT_FALSE(null2 < null1);
+}
+
} // namespace net
diff --git a/chromium/net/cookies/static_cookie_policy.h b/chromium/net/cookies/static_cookie_policy.h
index 7f3042cc0da..2fa191b2a04 100644
--- a/chromium/net/cookies/static_cookie_policy.h
+++ b/chromium/net/cookies/static_cookie_policy.h
@@ -33,6 +33,9 @@ class NET_EXPORT StaticCookiePolicy {
explicit StaticCookiePolicy(Type type) : type_(type) {}
+ StaticCookiePolicy(const StaticCookiePolicy&) = delete;
+ StaticCookiePolicy& operator=(const StaticCookiePolicy&) = delete;
+
// Sets the current policy to enforce. This should be called when the user's
// preferences change.
void set_type(Type type) { type_ = type; }
@@ -45,8 +48,6 @@ class NET_EXPORT StaticCookiePolicy {
private:
Type type_;
-
- DISALLOW_COPY_AND_ASSIGN(StaticCookiePolicy);
};
} // namespace net
diff --git a/chromium/net/cookies/test_cookie_access_delegate.h b/chromium/net/cookies/test_cookie_access_delegate.h
index 8a0320ebd53..d04e9e6ac91 100644
--- a/chromium/net/cookies/test_cookie_access_delegate.h
+++ b/chromium/net/cookies/test_cookie_access_delegate.h
@@ -22,6 +22,10 @@ class SchemefulSite;
class TestCookieAccessDelegate : public CookieAccessDelegate {
public:
TestCookieAccessDelegate();
+
+ TestCookieAccessDelegate(const TestCookieAccessDelegate&) = delete;
+ TestCookieAccessDelegate& operator=(const TestCookieAccessDelegate&) = delete;
+
~TestCookieAccessDelegate() override;
// CookieAccessDelegate implementation:
@@ -68,8 +72,6 @@ class TestCookieAccessDelegate : public CookieAccessDelegate {
std::map<std::string, bool> ignore_samesite_restrictions_schemes_;
base::flat_map<net::SchemefulSite, std::set<net::SchemefulSite>>
first_party_sets_;
-
- DISALLOW_COPY_AND_ASSIGN(TestCookieAccessDelegate);
};
} // namespace net