electron/patches/chromium/revert_same_party_cookie_at...

4833 lines
223 KiB
Diff

From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: VerteDinde <vertedinde@electronjs.org>
Date: Mon, 13 Nov 2023 21:47:22 -0800
Subject: fix: revert sameparty cookie attribute removal
The SameParty cookie attribute, which is used for same-party, cross-site
contexts with First Party Sets, has been removed upstream by Chrome, but is
depended on by some Electron consumers. This patch is meant to restore the
removed SameParty cookie origin trail functionality and supported methods, while
Chrome completes other API options to use in place of SameParty cookies.
This patch can be removed when Storage Access API cookie support or
equivalent support is completed upstream.
diff --git a/chrome/browser/about_flags.cc b/chrome/browser/about_flags.cc
index de3be2eec25ccf745d1b8d11e7125803ca16167d..e37c9dcf44feaaff89084914165abc19f648e104 100644
--- a/chrome/browser/about_flags.cc
+++ b/chrome/browser/about_flags.cc
@@ -8666,6 +8666,10 @@ const FeatureEntry kFeatureEntries[] = {
flag_descriptions::kChromeRefresh2023TopChromeFontDescription, kOsDesktop,
FEATURE_VALUE_TYPE(features::kChromeRefresh2023TopChromeFont)},
+ {"enable-first-party-sets", flag_descriptions::kEnableFirstPartySetsName,
+ flag_descriptions::kEnableFirstPartySetsDescription, kOsAll,
+ FEATURE_VALUE_TYPE(features::kFirstPartySets)},
+
#if BUILDFLAG(IS_ANDROID)
{"autofill-enable-offers-in-clank-keyboard-accessory",
flag_descriptions::kAutofillEnableOffersInClankKeyboardAccessoryName,
diff --git a/chrome/browser/chrome_content_browser_client.cc b/chrome/browser/chrome_content_browser_client.cc
index 8e21b0a1e691a888f47145d35831a6ae347b0675..70949b74f334a801972680595d72ed20ae609cc0 100644
--- a/chrome/browser/chrome_content_browser_client.cc
+++ b/chrome/browser/chrome_content_browser_client.cc
@@ -7745,9 +7745,9 @@ bool ChromeContentBrowserClient::ShouldDisableOriginAgentClusterDefault(
bool ChromeContentBrowserClient::WillProvidePublicFirstPartySets() {
#if BUILDFLAG(ENABLE_COMPONENT_UPDATER)
- return !is_minimal_mode_ &&
- !base::CommandLine::ForCurrentProcess()->HasSwitch(
- switches::kDisableComponentUpdate);
+ return !base::CommandLine::ForCurrentProcess()->HasSwitch(
+ switches::kDisableComponentUpdate) &&
+ base::FeatureList::IsEnabled(features::kFirstPartySets);
#else
return false;
#endif // BUILDFLAG(ENABLE_COMPONENT_UPDATER)
diff --git a/chrome/browser/chrome_content_browser_client.h b/chrome/browser/chrome_content_browser_client.h
index 8ac74856217e4840b89d2e60e5ffeef160e46d6d..a5d6da17287c22e8a3c095d56be97c87f6ef6f96 100644
--- a/chrome/browser/chrome_content_browser_client.h
+++ b/chrome/browser/chrome_content_browser_client.h
@@ -1168,10 +1168,6 @@ class ChromeContentBrowserClient : public content::ContentBrowserClient {
std::string GetChildProcessSuffix(int child_flags) override;
#endif // BUILDFLAG(IS_MAC)
- // Tracks whether the browser was started in "minimal" mode (as opposed to
- // full browser mode), where most subsystems are not initialized.
- bool is_minimal_mode_ = false;
-
base::WeakPtrFactory<ChromeContentBrowserClient> weak_factory_{this};
};
diff --git a/chrome/browser/component_updater/first_party_sets_component_installer.cc b/chrome/browser/component_updater/first_party_sets_component_installer.cc
index ed00b8e7e9dcd36e0347290172892e3b58b8cb3d..a63fdb27bbf9c6c3d9c5c5d4b4976453a3103564 100644
--- a/chrome/browser/component_updater/first_party_sets_component_installer.cc
+++ b/chrome/browser/component_updater/first_party_sets_component_installer.cc
@@ -42,7 +42,7 @@ constexpr uint8_t kFirstPartySetsPublicKeySHA256[32] = {
0xff, 0x1c, 0x65, 0x66, 0x14, 0xa8, 0x46, 0x37, 0xe6, 0xeb, 0x80,
0x8b, 0x8f, 0xb0, 0xb6, 0x18, 0xa7, 0xcd, 0x3d, 0xbb, 0xfb};
-constexpr char kFirstPartySetsManifestName[] = "Related Website Sets";
+constexpr char kFirstPartySetsManifestName[] = "First-Party Sets";
constexpr base::FilePath::CharType kFirstPartySetsRelativeInstallDir[] =
FILE_PATH_LITERAL("FirstPartySetsPreloaded");
@@ -157,8 +157,8 @@ void FirstPartySetsComponentInstallerPolicy::ComponentReady(
return;
}
- VLOG(1) << "Related Website Sets Component ready, version "
- << version.GetString() << " in " << install_dir.value();
+ VLOG(1) << "First-Party Sets Component ready, version " << version.GetString()
+ << " in " << install_dir.value();
GetConfigPathInstance() =
std::make_pair(GetInstalledPath(install_dir), version);
@@ -202,12 +202,12 @@ void FirstPartySetsComponentInstallerPolicy::ResetForTesting() {
}
void RegisterFirstPartySetsComponent(ComponentUpdateService* cus) {
- VLOG(1) << "Registering Related Website Sets component.";
+ VLOG(1) << "Registering First-Party Sets component.";
auto policy = std::make_unique<FirstPartySetsComponentInstallerPolicy>(
/*on_sets_ready=*/base::BindOnce([](base::Version version,
base::File sets_file) {
- VLOG(1) << "Received Related Website Sets";
+ VLOG(1) << "Received First-Party Sets";
content::FirstPartySetsHandler::GetInstance()->SetPublicFirstPartySets(
version, std::move(sets_file));
}));
diff --git a/chrome/browser/extensions/api/cookies/cookies_api.cc b/chrome/browser/extensions/api/cookies/cookies_api.cc
index 37704856cf800a479cf20774047f6694e2374405..724e22a18bac78baeaca3ceac8435100ebf6463f 100644
--- a/chrome/browser/extensions/api/cookies/cookies_api.cc
+++ b/chrome/browser/extensions/api/cookies/cookies_api.cc
@@ -501,6 +501,7 @@ ExtensionFunction::ResponseAction CookiesSetFunction::Run() {
parsed_args_->details.http_only.value_or(false), //
same_site, //
net::COOKIE_PRIORITY_DEFAULT, //
+ same_party, //
partition_key));
if (!cc) {
// Return error through callbacks so that the proper error message
diff --git a/chrome/browser/first_party_sets/first_party_sets_policy_service.cc b/chrome/browser/first_party_sets/first_party_sets_policy_service.cc
index 0509dabf8f42123851e5eea62d0f8f9575784f7c..bd37482e962d2d06a0303fd04fc9dc8a26081b13 100644
--- a/chrome/browser/first_party_sets/first_party_sets_policy_service.cc
+++ b/chrome/browser/first_party_sets/first_party_sets_policy_service.cc
@@ -132,6 +132,7 @@ void FirstPartySetsPolicyService::Init() {
void FirstPartySetsPolicyService::ComputeFirstPartySetMetadata(
const net::SchemefulSite& site,
const net::SchemefulSite* top_frame_site,
+ const std::set<net::SchemefulSite>& party_context,
base::OnceCallback<void(net::FirstPartySetMetadata)> callback) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (!is_enabled()) {
@@ -143,17 +144,18 @@ void FirstPartySetsPolicyService::ComputeFirstPartySetMetadata(
on_ready_callbacks_.push_back(base::BindOnce(
&FirstPartySetsPolicyService::ComputeFirstPartySetMetadataInternal,
weak_factory_.GetWeakPtr(), site, base::OptionalFromPtr(top_frame_site),
- std::move(callback)));
+ party_context, std::move(callback)));
return;
}
content::FirstPartySetsHandler::GetInstance()->ComputeFirstPartySetMetadata(
- site, top_frame_site, *config_, std::move(callback));
+ site, top_frame_site, party_context, *config_, std::move(callback));
}
void FirstPartySetsPolicyService::ComputeFirstPartySetMetadataInternal(
const net::SchemefulSite& site,
const std::optional<net::SchemefulSite>& top_frame_site,
+ const std::set<net::SchemefulSite>& party_context,
base::OnceCallback<void(net::FirstPartySetMetadata)> callback) const {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
CHECK(config_.has_value());
@@ -164,7 +166,8 @@ void FirstPartySetsPolicyService::ComputeFirstPartySetMetadataInternal(
}
content::FirstPartySetsHandler::GetInstance()->ComputeFirstPartySetMetadata(
- site, base::OptionalToPtr(top_frame_site), *config_, std::move(callback));
+ site, base::OptionalToPtr(top_frame_site), party_context, *config_,
+ std::move(callback));
}
void FirstPartySetsPolicyService::AddRemoteAccessDelegate(
diff --git a/chrome/browser/first_party_sets/first_party_sets_policy_service.h b/chrome/browser/first_party_sets/first_party_sets_policy_service.h
index 3c41142b75ecc46dc3027287f67a667f091d6286..5570e8f627ebc773eba758c89c6e2eb6c5e53370 100644
--- a/chrome/browser/first_party_sets/first_party_sets_policy_service.h
+++ b/chrome/browser/first_party_sets/first_party_sets_policy_service.h
@@ -63,6 +63,7 @@ class FirstPartySetsPolicyService : public KeyedService {
void ComputeFirstPartySetMetadata(
const net::SchemefulSite& site,
const net::SchemefulSite* top_frame_site,
+ const std::set<net::SchemefulSite>& party_context,
base::OnceCallback<void(net::FirstPartySetMetadata)> callback);
// Stores `access_delegate` in a RemoteSet for later IPC calls on it when this
@@ -192,6 +193,7 @@ class FirstPartySetsPolicyService : public KeyedService {
void ComputeFirstPartySetMetadataInternal(
const net::SchemefulSite& site,
const std::optional<net::SchemefulSite>& top_frame_site,
+ const std::set<net::SchemefulSite>& party_context,
base::OnceCallback<void(net::FirstPartySetMetadata)> callback) const;
// Clears the content settings associated with `profile` that were
diff --git a/chrome/browser/flag-metadata.json b/chrome/browser/flag-metadata.json
index d70932e1ad965a3f59014d93e227bc11aadece40..30ba3aaa758327ecad2d205eaca5f1b633c9185b 100644
--- a/chrome/browser/flag-metadata.json
+++ b/chrome/browser/flag-metadata.json
@@ -2813,6 +2813,11 @@
"owners": [ "chromeos-dlp@google.com", "accorsi@chromium.org" ],
"expiry_milestone": 130
},
+ {
+ "name": "enable-first-party-sets",
+ "owners": [ "chrome-first-party-sets@chromium.org" ],
+ "expiry_milestone": 120
+ },
{
"name": "enable-follow-IPH-exp-params",
"owners": [ "sczs@chromium.org", "tinazwang@chromium.org" ],
diff --git a/chrome/browser/flag_descriptions.cc b/chrome/browser/flag_descriptions.cc
index 8385c597f050e421cd6735fc4286389c94f12b5a..09189e029a958477938b56981c9929246f1ec886 100644
--- a/chrome/browser/flag_descriptions.cc
+++ b/chrome/browser/flag_descriptions.cc
@@ -424,6 +424,11 @@ const char kUseDnsHttpsSvcbAlpnDescription[] =
"When enabled, Chrome may try QUIC on the first connection using the ALPN"
" information in the DNS HTTPS record.";
+const char kEnableFirstPartySetsName[] = "Enable First-Party Sets";
+const char kEnableFirstPartySetsDescription[] =
+ "When enabled, Chrome will enable First-Party Sets and the Storage Access "
+ "API.";
+
const char kIsolatedSandboxedIframesName[] = "Isolated sandboxed iframes";
const char kIsolatedSandboxedIframesDescription[] =
"When enabled, applies process isolation to iframes with the 'sandbox' "
diff --git a/chrome/browser/flag_descriptions.h b/chrome/browser/flag_descriptions.h
index a023e473a3c31dca19d214555e67af3dfb1a0b42..e4b5eb3fa6fb872fb0977eb50a48a95e14d6072b 100644
--- a/chrome/browser/flag_descriptions.h
+++ b/chrome/browser/flag_descriptions.h
@@ -526,6 +526,9 @@ extern const char kUseDnsHttpsSvcbAlpnDescription[];
extern const char kEditContextName[];
extern const char kEditContextDescription[];
+extern const char kEnableFirstPartySetsName[];
+extern const char kEnableFirstPartySetsDescription[];
+
extern const char kIsolatedSandboxedIframesName[];
extern const char kIsolatedSandboxedIframesDescription[];
diff --git a/chrome/browser/storage_access_api/storage_access_grant_permission_context.cc b/chrome/browser/storage_access_api/storage_access_grant_permission_context.cc
index e968e807e13c12525f8d19ee6347e39fa9e11c6c..ddfbc69538d435a15b12e0a006a8acaca73af673 100644
--- a/chrome/browser/storage_access_api/storage_access_grant_permission_context.cc
+++ b/chrome/browser/storage_access_api/storage_access_grant_permission_context.cc
@@ -292,7 +292,8 @@ void StorageAccessGrantPermissionContext::DecidePermission(
first_party_sets::FirstPartySetsPolicyServiceFactory::GetForBrowserContext(
browser_context())
->ComputeFirstPartySetMetadata(
- requesting_site, &embedding_site,
+ net::SchemefulSite(requesting_origin), &embedding_site,
+ /*party_context=*/{},
base::BindOnce(&StorageAccessGrantPermissionContext::
CheckForAutoGrantOrAutoDenial,
weak_factory_.GetWeakPtr(), std::move(request_data),
diff --git a/chrome/browser/top_level_storage_access_api/top_level_storage_access_permission_context.cc b/chrome/browser/top_level_storage_access_api/top_level_storage_access_permission_context.cc
index 796307897e89b979689398db639bd95e89e13dc2..c52cff9c19a250e411db3b0deb0f614cb48129ae 100644
--- a/chrome/browser/top_level_storage_access_api/top_level_storage_access_permission_context.cc
+++ b/chrome/browser/top_level_storage_access_api/top_level_storage_access_permission_context.cc
@@ -84,7 +84,8 @@ void TopLevelStorageAccessPermissionContext::DecidePermission(
first_party_sets::FirstPartySetsPolicyServiceFactory::GetForBrowserContext(
browser_context())
->ComputeFirstPartySetMetadata(
- requesting_site, &embedding_site,
+ net::SchemefulSite(requesting_origin), &embedding_site,
+ /*party_context=*/{},
base::BindOnce(&TopLevelStorageAccessPermissionContext::
CheckForAutoGrantOrAutoDenial,
weak_factory_.GetWeakPtr(), std::move(request_data),
diff --git a/components/signin/internal/identity_manager/gaia_cookie_manager_service.cc b/components/signin/internal/identity_manager/gaia_cookie_manager_service.cc
index 2e8e994bd86df365d15255d7214becc5d5db798c..35452ae459d52211cbd15e964566e8ee01e8182d 100644
--- a/components/signin/internal/identity_manager/gaia_cookie_manager_service.cc
+++ b/components/signin/internal/identity_manager/gaia_cookie_manager_service.cc
@@ -569,6 +569,7 @@ void GaiaCookieManagerService::ForceOnCookieChangeProcessing() {
"." + google_url.host(), "/", base::Time(), base::Time(),
base::Time(), true /* secure */, false /* httponly */,
net::CookieSameSite::NO_RESTRICTION, net::COOKIE_PRIORITY_DEFAULT,
+ /* same_party */ false,
absl::nullopt /* cookie_partition_key */);
OnCookieChange(
net::CookieChangeInfo(*cookie, net::CookieAccessResult(),
diff --git a/content/app/content_main_runner_impl.cc b/content/app/content_main_runner_impl.cc
index 48f1cdd0a7763242b81c7a54cad72700c8a42af1..7349441c41ec70834dc8a7c87d06693090921560 100644
--- a/content/app/content_main_runner_impl.cc
+++ b/content/app/content_main_runner_impl.cc
@@ -60,7 +60,6 @@
#include "content/browser/browser_main.h"
#include "content/browser/browser_process_io_thread.h"
#include "content/browser/browser_thread_impl.h"
-#include "content/browser/first_party_sets/first_party_sets_handler_impl.h"
#include "content/browser/gpu/gpu_main_thread_factory.h"
#include "content/browser/renderer_host/render_process_host_impl.h"
#include "content/browser/scheduler/browser_task_executor.h"
@@ -1253,14 +1252,9 @@ int ContentMainRunnerImpl::RunBrowser(MainFunctionParams main_params,
AndroidBatteryMetrics::CreateInstance();
#endif
- GetContentClient()->browser()->SetIsMinimalMode(start_minimal_browser);
- if (start_minimal_browser) {
- ForceInProcessNetworkService();
- // Minimal browser mode doesn't initialize First-Party Sets the "usual"
- // way, so we do it manually.
- content::FirstPartySetsHandlerImpl::GetInstance()->Init(
- base::FilePath(), net::LocalSetDeclaration());
- }
+ // if (start_minimal_browser)
+ // ForceInProcessNetworkService();
+ // }
discardable_shared_memory_manager_ =
std::make_unique<discardable_memory::DiscardableSharedMemoryManager>();
diff --git a/content/browser/BUILD.gn b/content/browser/BUILD.gn
index 143c98e3294fb07b08c1d8e97013034f8453e074..60432c0c6d4618f184f86dd629dd1a50808f97c5 100644
--- a/content/browser/BUILD.gn
+++ b/content/browser/BUILD.gn
@@ -1021,9 +1021,8 @@ source_set("browser") {
"first_party_sets/first_party_set_parser.h",
"first_party_sets/first_party_sets_handler_database_helper.cc",
"first_party_sets/first_party_sets_handler_database_helper.h",
+ "first_party_sets/first_party_sets_handler_impl.cc",
"first_party_sets/first_party_sets_handler_impl.h",
- "first_party_sets/first_party_sets_handler_impl_instance.cc",
- "first_party_sets/first_party_sets_handler_impl_instance.h",
"first_party_sets/first_party_sets_loader.cc",
"first_party_sets/first_party_sets_loader.h",
"first_party_sets/first_party_sets_overrides_policy.cc",
diff --git a/content/browser/cookie_store/cookie_change_subscription.cc b/content/browser/cookie_store/cookie_change_subscription.cc
index dbe3c5b8a6c83c5e8d26b109f24e77b4ab2e604e..fd51ab0f749b63fda2a7848594bb616fe6a48730 100644
--- a/content/browser/cookie_store/cookie_change_subscription.cc
+++ b/content/browser/cookie_store/cookie_change_subscription.cc
@@ -9,8 +9,10 @@
#include "content/browser/cookie_store/cookie_change_subscriptions.pb.h"
#include "content/public/browser/content_browser_client.h"
#include "content/public/common/content_client.h"
+#include "net/base/features.h"
#include "net/cookies/cookie_constants.h"
#include "net/cookies/cookie_util.h"
+#include "net/first_party_sets/same_party_context.h"
#include "services/network/public/cpp/is_potentially_trustworthy.h"
namespace content {
@@ -169,17 +171,26 @@ bool CookieChangeSubscription::ShouldObserveChangeTo(
break;
}
- // We assume that this is a same-site context.
+ // We assume that this is a same-site, same-party context.
net::CookieOptions net_options;
net_options.set_same_site_cookie_context(
net::CookieOptions::SameSiteCookieContext::MakeInclusive());
+ net_options.set_same_party_context(net::SamePartyContext::MakeInclusive());
+ // It doesn't matter which we choose here, since both SameParty and SameSite
+ // semantics should allow this access. But we make a choice to be explicit.
+ net_options.set_is_in_nontrivial_first_party_set(true);
return cookie
- .IncludeForRequestURL(url_, net_options,
- net::CookieAccessParams{
- access_semantics,
- network::IsUrlPotentiallyTrustworthy(url_),
- })
+ .IncludeForRequestURL(
+ url_, net_options,
+ net::CookieAccessParams{
+ access_semantics,
+ network::IsUrlPotentiallyTrustworthy(url_),
+ net::cookie_util::GetSamePartyStatus(
+ cookie, net_options,
+ base::FeatureList::IsEnabled(
+ net::features::kSamePartyAttributeEnabled)),
+ })
.status.IsInclude();
}
diff --git a/content/browser/devtools/protocol/network_handler.cc b/content/browser/devtools/protocol/network_handler.cc
index 2bbc46fd4748246b7fa3e6a8cfe38b43684bf8d7..9b963b4dea16cc7f2e86e4efcbbfbed4c2da050f 100644
--- a/content/browser/devtools/protocol/network_handler.cc
+++ b/content/browser/devtools/protocol/network_handler.cc
@@ -424,7 +424,7 @@ MakeCookieFromProtocolValues(const std::string& name,
net::CanonicalCookie::CreateSanitizedCookie(
url, name, value, normalized_domain, path, base::Time(),
expiration_date, base::Time(), secure, http_only, css, cp,
- deserialized_partition_key);
+ /*same_party*/false, deserialized_partition_key);
if (!cookie)
return Response::InvalidParams("Sanitizing cookie failed");
diff --git a/content/browser/first_party_sets/first_party_sets_handler_impl_instance.cc b/content/browser/first_party_sets/first_party_sets_handler_impl.cc
similarity index 75%
rename from content/browser/first_party_sets/first_party_sets_handler_impl_instance.cc
rename to content/browser/first_party_sets/first_party_sets_handler_impl.cc
index 267e0786e139a7a1fc142b6fd1a92e3a5e8c716a..e13b63383bd35e3508f068da3bc04972955baa9c 100644
--- a/content/browser/first_party_sets/first_party_sets_handler_impl_instance.cc
+++ b/content/browser/first_party_sets/first_party_sets_handler_impl.cc
@@ -1,8 +1,8 @@
-// Copyright 2023 The Chromium Authors
+// Copyright 2022 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#include "content/browser/first_party_sets/first_party_sets_handler_impl_instance.h"
+#include "content/browser/first_party_sets/first_party_sets_handler_impl.h"
#include <memory>
#include <optional>
@@ -20,7 +20,6 @@
#include "base/types/optional_util.h"
#include "base/values.h"
#include "content/browser/first_party_sets/first_party_set_parser.h"
-#include "content/browser/first_party_sets/first_party_sets_handler_impl.h"
#include "content/browser/first_party_sets/first_party_sets_loader.h"
#include "content/browser/first_party_sets/first_party_sets_overrides_policy.h"
#include "content/browser/first_party_sets/first_party_sets_site_data_remover.h"
@@ -81,16 +80,15 @@ void FirstPartySetsHandlerImpl::SetInstanceForTesting(
// static
FirstPartySetsHandler* FirstPartySetsHandler::GetInstance() {
- if (g_test_instance) {
+ if (g_test_instance)
return g_test_instance;
- }
return FirstPartySetsHandlerImpl::GetInstance();
}
// static
FirstPartySetsHandlerImpl* FirstPartySetsHandlerImpl::GetInstance() {
- static base::NoDestructor<FirstPartySetsHandlerImplInstance> instance(
+ static base::NoDestructor<FirstPartySetsHandlerImpl> instance(
GetContentClient()->browser()->IsFirstPartySetsEnabled(),
GetContentClient()->browser()->WillProvidePublicFirstPartySets());
if (g_impl_test_instance) {
@@ -115,19 +113,17 @@ FirstPartySetsHandler::ValidateEnterprisePolicy(
}
// static
-FirstPartySetsHandlerImplInstance
-FirstPartySetsHandlerImplInstance::CreateForTesting(
+FirstPartySetsHandlerImpl FirstPartySetsHandlerImpl::CreateForTesting(
bool enabled,
bool embedder_will_provide_public_sets) {
- return FirstPartySetsHandlerImplInstance(enabled,
- embedder_will_provide_public_sets);
+ return FirstPartySetsHandlerImpl(enabled, embedder_will_provide_public_sets);
}
-void FirstPartySetsHandlerImplInstance::GetContextConfigForPolicy(
+void FirstPartySetsHandlerImpl::GetContextConfigForPolicy(
const base::Value::Dict* policy,
base::OnceCallback<void(net::FirstPartySetsContextConfig)> callback) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
- if (!policy) {
+ if (!policy || !enabled_) {
std::move(callback).Run(net::FirstPartySetsContextConfig());
return;
}
@@ -140,49 +136,57 @@ void FirstPartySetsHandlerImplInstance::GetContextConfigForPolicy(
// of First-Party Sets has been fully initialized.
EnqueuePendingTask(
base::BindOnce(
- &FirstPartySetsHandlerImplInstance::GetContextConfigForPolicyInternal,
+ &FirstPartySetsHandlerImpl::GetContextConfigForPolicyInternal,
// base::Unretained(this) is safe here because this is a static
// singleton.
base::Unretained(this), policy->Clone(), base::ElapsedTimer())
.Then(std::move(callback)));
}
-FirstPartySetsHandlerImplInstance::FirstPartySetsHandlerImplInstance(
+// net::FirstPartySetsContextConfig
+// FirstPartySetsHandlerImpl::ComputeEnterpriseContextConfig(
+// const net::GlobalFirstPartySets& global_sets,
+// const FirstPartySetParser::ParsedPolicySetLists& policy) {
+// return global_sets.ComputeConfig(
+// /*replacement_sets=*/policy.replacements,
+// /*addition_sets=*/
+// policy.additions);
+// }
+
+FirstPartySetsHandlerImpl::FirstPartySetsHandlerImpl(
+ base::PassKey<ScopedMockFirstPartySetsHandler>,
bool enabled,
bool embedder_will_provide_public_sets)
- : enabled_(enabled) {
- if (enabled) {
- on_sets_ready_callbacks_ =
- std::make_unique<base::circular_deque<base::OnceClosure>>();
- sets_loader_ = std::make_unique<FirstPartySetsLoader>(
- base::BindOnce(&FirstPartySetsHandlerImplInstance::SetCompleteSets,
- // base::Unretained(this) is safe here because
- // this is a static singleton.
- base::Unretained(this)));
- if (!embedder_will_provide_public_sets) {
- sets_loader_->SetComponentSets(base::Version(), base::File());
- }
- } else {
- SetCompleteSets(net::GlobalFirstPartySets());
- CHECK(global_sets_.has_value());
- }
-}
+ : FirstPartySetsHandlerImpl(enabled, embedder_will_provide_public_sets) {}
-FirstPartySetsHandlerImplInstance::~FirstPartySetsHandlerImplInstance() =
- default;
-
-std::optional<net::GlobalFirstPartySets>
-FirstPartySetsHandlerImplInstance::GetSets(
- base::OnceCallback<void(net::GlobalFirstPartySets)> callback) {
+FirstPartySetsHandlerImpl::FirstPartySetsHandlerImpl(
+ bool enabled,
+ bool embedder_will_provide_public_sets)
+ : enabled_(enabled),
+ embedder_will_provide_public_sets_(enabled &&
+ embedder_will_provide_public_sets),
+ sets_loader_(std::make_unique<FirstPartySetsLoader>(
+ base::BindOnce(&FirstPartySetsHandlerImpl::SetCompleteSets,
+ // base::Unretained(this) is safe here because
+ // this is a static singleton.
+ base::Unretained(this)))) {}
+
+FirstPartySetsHandlerImpl::~FirstPartySetsHandlerImpl() = default;
+
+absl::optional<net::GlobalFirstPartySets> FirstPartySetsHandlerImpl::GetSets(
+ SetsReadyOnceCallback callback) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
- if (global_sets_.has_value()) {
- return global_sets_->Clone();
+ if (!IsEnabled()) {
+ return net::GlobalFirstPartySets();
}
+ CHECK(IsEnabled());
+ if (global_sets_.has_value())
+ return global_sets_->Clone();
if (!callback.is_null()) {
// base::Unretained(this) is safe here because this is a static singleton.
EnqueuePendingTask(
- base::BindOnce(&FirstPartySetsHandlerImplInstance::GetGlobalSetsSync,
+ base::BindOnce(&FirstPartySetsHandlerImpl::GetGlobalSetsSync,
base::Unretained(this))
.Then(std::move(callback)));
}
@@ -190,33 +194,35 @@ FirstPartySetsHandlerImplInstance::GetSets(
return std::nullopt;
}
-void FirstPartySetsHandlerImplInstance::Init(
- const base::FilePath& user_data_dir,
- const net::LocalSetDeclaration& local_set) {
+void FirstPartySetsHandlerImpl::Init(const base::FilePath& user_data_dir,
+ const net::LocalSetDeclaration& local_set) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
- if (initialized_) {
- return;
- }
+ CHECK(!initialized_);
+ CHECK(sets_loader_);
initialized_ = true;
SetDatabase(user_data_dir);
- if (sets_loader_) {
+ if (IsEnabled()) {
sets_loader_->SetManuallySpecifiedSet(local_set);
+ if (!embedder_will_provide_public_sets_) {
+ sets_loader_->SetComponentSets(base::Version(), base::File());
+ }
+ } else {
+ SetCompleteSets(net::GlobalFirstPartySets());
}
}
-bool FirstPartySetsHandlerImplInstance::IsEnabled() const {
+bool FirstPartySetsHandlerImpl::IsEnabled() const {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
return enabled_;
}
-void FirstPartySetsHandlerImplInstance::SetPublicFirstPartySets(
+void FirstPartySetsHandlerImpl::SetPublicFirstPartySets(
const base::Version& version,
base::File sets_file) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
- if (!sets_loader_) {
- FirstPartySetsLoader::DisposeFile(std::move(sets_file));
+ if (!enabled_ || !embedder_will_provide_public_sets_ || !sets_loader_) {
return;
}
@@ -224,7 +230,7 @@ void FirstPartySetsHandlerImplInstance::SetPublicFirstPartySets(
sets_loader_->SetComponentSets(version, std::move(sets_file));
}
-void FirstPartySetsHandlerImplInstance::GetPersistedSetsForTesting(
+void FirstPartySetsHandlerImpl::GetPersistedSetsForTesting(
const std::string& browser_context_id,
base::OnceCallback<void(
std::optional<std::pair<net::GlobalFirstPartySets,
@@ -242,7 +248,7 @@ void FirstPartySetsHandlerImplInstance::GetPersistedSetsForTesting(
.Then(std::move(callback));
}
-void FirstPartySetsHandlerImplInstance::HasBrowserContextClearedForTesting(
+void FirstPartySetsHandlerImpl::HasBrowserContextClearedForTesting(
const std::string& browser_context_id,
base::OnceCallback<void(std::optional<bool>)> callback) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
@@ -258,17 +264,18 @@ void FirstPartySetsHandlerImplInstance::HasBrowserContextClearedForTesting(
.Then(std::move(callback));
}
-void FirstPartySetsHandlerImplInstance::SetCompleteSets(
+void FirstPartySetsHandlerImpl::SetCompleteSets(
net::GlobalFirstPartySets sets) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
CHECK(!global_sets_.has_value());
+ CHECK(sets_loader_);
global_sets_ = std::move(sets);
sets_loader_.reset();
InvokePendingQueries();
}
-void FirstPartySetsHandlerImplInstance::SetDatabase(
+void FirstPartySetsHandlerImpl::SetDatabase(
const base::FilePath& user_data_dir) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
CHECK(db_helper_.is_null());
@@ -283,26 +290,22 @@ void FirstPartySetsHandlerImplInstance::SetDatabase(
user_data_dir.Append(kFirstPartySetsDatabase));
}
-void FirstPartySetsHandlerImplInstance::EnqueuePendingTask(
- base::OnceClosure run_task) {
+void FirstPartySetsHandlerImpl::EnqueuePendingTask(base::OnceClosure run_task) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
CHECK(!global_sets_.has_value());
- CHECK(on_sets_ready_callbacks_);
if (!first_async_task_timer_.has_value()) {
first_async_task_timer_ = base::ElapsedTimer();
}
- on_sets_ready_callbacks_->push_back(std::move(run_task));
+ on_sets_ready_callbacks_.push_back(std::move(run_task));
}
-void FirstPartySetsHandlerImplInstance::InvokePendingQueries() {
+void FirstPartySetsHandlerImpl::InvokePendingQueries() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
base::circular_deque<base::OnceClosure> queue;
- if (on_sets_ready_callbacks_) {
- queue.swap(*on_sets_ready_callbacks_);
- }
+ queue.swap(on_sets_ready_callbacks_);
base::UmaHistogramCounts10000(
"Cookie.FirstPartySets.Browser.DelayedQueriesCount", queue.size());
@@ -316,11 +319,9 @@ void FirstPartySetsHandlerImplInstance::InvokePendingQueries() {
queue.pop_front();
std::move(callback).Run();
}
- on_sets_ready_callbacks_.reset();
}
-std::optional<net::FirstPartySetEntry>
-FirstPartySetsHandlerImplInstance::FindEntry(
+absl::optional<net::FirstPartySetEntry> FirstPartySetsHandlerImpl::FindEntry(
const net::SchemefulSite& site,
const net::FirstPartySetsContextConfig& config) const {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
@@ -330,14 +331,13 @@ FirstPartySetsHandlerImplInstance::FindEntry(
return global_sets_->FindEntry(site, config);
}
-net::GlobalFirstPartySets FirstPartySetsHandlerImplInstance::GetGlobalSetsSync()
- const {
+net::GlobalFirstPartySets FirstPartySetsHandlerImpl::GetGlobalSetsSync() const {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
CHECK(global_sets_.has_value());
return global_sets_->Clone();
}
-void FirstPartySetsHandlerImplInstance::ClearSiteDataOnChangedSetsForContext(
+void FirstPartySetsHandlerImpl::ClearSiteDataOnChangedSetsForContext(
base::RepeatingCallback<BrowserContext*()> browser_context_getter,
const std::string& browser_context_id,
net::FirstPartySetsContextConfig context_config,
@@ -360,19 +360,17 @@ void FirstPartySetsHandlerImplInstance::ClearSiteDataOnChangedSetsForContext(
// base::Unretained(this) is safe because this is a static singleton.
EnqueuePendingTask(base::BindOnce(
- &FirstPartySetsHandlerImplInstance::
- ClearSiteDataOnChangedSetsForContextInternal,
+ &FirstPartySetsHandlerImpl::ClearSiteDataOnChangedSetsForContextInternal,
base::Unretained(this), browser_context_getter, browser_context_id,
std::move(context_config), std::move(callback)));
}
-void FirstPartySetsHandlerImplInstance::
- ClearSiteDataOnChangedSetsForContextInternal(
- base::RepeatingCallback<BrowserContext*()> browser_context_getter,
- const std::string& browser_context_id,
- net::FirstPartySetsContextConfig context_config,
- base::OnceCallback<void(net::FirstPartySetsContextConfig,
- net::FirstPartySetsCacheFilter)> callback) {
+void FirstPartySetsHandlerImpl::ClearSiteDataOnChangedSetsForContextInternal(
+ base::RepeatingCallback<BrowserContext*()> browser_context_getter,
+ const std::string& browser_context_id,
+ net::FirstPartySetsContextConfig context_config,
+ base::OnceCallback<void(net::FirstPartySetsContextConfig,
+ net::FirstPartySetsCacheFilter)> callback) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
CHECK(global_sets_.has_value());
CHECK(!browser_context_id.empty());
@@ -394,7 +392,7 @@ void FirstPartySetsHandlerImplInstance::
base::OnceCallback<void(std::pair<std::vector<net::SchemefulSite>,
net::FirstPartySetsCacheFilter>)>
on_get_sites_to_clear = base::BindOnce(
- &FirstPartySetsHandlerImplInstance::OnGetSitesToClear,
+ &FirstPartySetsHandlerImpl::OnGetSitesToClear,
// base::Unretained(this) is safe here because this
// is a static singleton.
base::Unretained(this), browser_context_getter, browser_context_id,
@@ -408,7 +406,7 @@ void FirstPartySetsHandlerImplInstance::
.Then(std::move(on_get_sites_to_clear));
}
-void FirstPartySetsHandlerImplInstance::OnGetSitesToClear(
+void FirstPartySetsHandlerImpl::OnGetSitesToClear(
base::RepeatingCallback<BrowserContext*()> browser_context_getter,
const std::string& browser_context_id,
net::FirstPartySetsContextConfig context_config,
@@ -434,16 +432,15 @@ void FirstPartySetsHandlerImplInstance::OnGetSitesToClear(
FirstPartySetsSiteDataRemover::RemoveSiteData(
*browser_context->GetBrowsingDataRemover(),
std::move(sites_to_clear.first),
- base::BindOnce(&FirstPartySetsHandlerImplInstance::
- DidClearSiteDataOnChangedSetsForContext,
- // base::Unretained(this) is safe here because
- // this is a static singleton.
- base::Unretained(this), browser_context_id,
- std::move(context_config),
- std::move(sites_to_clear.second), std::move(callback)));
+ base::BindOnce(
+ &FirstPartySetsHandlerImpl::DidClearSiteDataOnChangedSetsForContext,
+ // base::Unretained(this) is safe here because
+ // this is a static singleton.
+ base::Unretained(this), browser_context_id, std::move(context_config),
+ std::move(sites_to_clear.second), std::move(callback)));
}
-void FirstPartySetsHandlerImplInstance::DidClearSiteDataOnChangedSetsForContext(
+void FirstPartySetsHandlerImpl::DidClearSiteDataOnChangedSetsForContext(
const std::string& browser_context_id,
net::FirstPartySetsContextConfig context_config,
net::FirstPartySetsCacheFilter cache_filter,
@@ -473,28 +470,30 @@ void FirstPartySetsHandlerImplInstance::DidClearSiteDataOnChangedSetsForContext(
std::move(callback).Run(std::move(context_config), std::move(cache_filter));
}
-void FirstPartySetsHandlerImplInstance::ComputeFirstPartySetMetadata(
+void FirstPartySetsHandlerImpl::ComputeFirstPartySetMetadata(
const net::SchemefulSite& site,
const net::SchemefulSite* top_frame_site,
+ const std::set<net::SchemefulSite>& party_context,
const net::FirstPartySetsContextConfig& config,
base::OnceCallback<void(net::FirstPartySetMetadata)> callback) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (!global_sets_.has_value()) {
EnqueuePendingTask(base::BindOnce(
- &FirstPartySetsHandlerImplInstance::
- ComputeFirstPartySetMetadataInternal,
+ &FirstPartySetsHandlerImpl::ComputeFirstPartySetMetadataInternal,
base::Unretained(this), site, base::OptionalFromPtr(top_frame_site),
- config.Clone(), base::ElapsedTimer(), std::move(callback)));
+ party_context, config.Clone(), base::ElapsedTimer(),
+ std::move(callback)));
return;
}
- std::move(callback).Run(
- global_sets_->ComputeMetadata(site, top_frame_site, config));
+ std::move(callback).Run(global_sets_->ComputeMetadata(site, top_frame_site,
+ party_context, config));
}
-void FirstPartySetsHandlerImplInstance::ComputeFirstPartySetMetadataInternal(
+void FirstPartySetsHandlerImpl::ComputeFirstPartySetMetadataInternal(
const net::SchemefulSite& site,
- const std::optional<net::SchemefulSite>& top_frame_site,
+ const absl::optional<net::SchemefulSite>& top_frame_site,
+ const std::set<net::SchemefulSite>& party_context,
const net::FirstPartySetsContextConfig& config,
const base::ElapsedTimer& timer,
base::OnceCallback<void(net::FirstPartySetMetadata)> callback) const {
@@ -506,11 +505,11 @@ void FirstPartySetsHandlerImplInstance::ComputeFirstPartySetMetadataInternal(
timer.Elapsed());
std::move(callback).Run(global_sets_->ComputeMetadata(
- site, base::OptionalToPtr(top_frame_site), config));
+ site, base::OptionalToPtr(top_frame_site), party_context, config));
}
net::FirstPartySetsContextConfig
-FirstPartySetsHandlerImplInstance::GetContextConfigForPolicyInternal(
+FirstPartySetsHandlerImpl::GetContextConfigForPolicyInternal(
const base::Value::Dict& policy,
const std::optional<base::ElapsedTimer>& timer) const {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
@@ -522,22 +521,13 @@ FirstPartySetsHandlerImplInstance::GetContextConfigForPolicyInternal(
timer->Elapsed());
}
- if (!enabled_) {
- return net::FirstPartySetsContextConfig();
- }
-
auto [parsed, warnings] =
FirstPartySetParser::ParseSetsFromEnterprisePolicy(policy);
- if (!parsed.has_value()) {
- return global_sets_->ComputeConfig(net::SetsMutation());
- }
-
- FirstPartySetsOverridesPolicy& policy_result = parsed.value();
- return global_sets_->ComputeConfig(std::move(policy_result.mutation()));
+ return net::FirstPartySetsContextConfig();
}
-bool FirstPartySetsHandlerImplInstance::ForEachEffectiveSetEntry(
+bool FirstPartySetsHandlerImpl::ForEachEffectiveSetEntry(
const net::FirstPartySetsContextConfig& config,
base::FunctionRef<bool(const net::SchemefulSite&,
const net::FirstPartySetEntry&)> f) const {
diff --git a/content/browser/first_party_sets/first_party_sets_handler_impl.h b/content/browser/first_party_sets/first_party_sets_handler_impl.h
index b183d8841eda1fa05ecaf01f63d88fb4e6e7689d..05317c24a2bee20bd112ec8af5227fd38c407001 100644
--- a/content/browser/first_party_sets/first_party_sets_handler_impl.h
+++ b/content/browser/first_party_sets/first_party_sets_handler_impl.h
@@ -5,26 +5,63 @@
#ifndef CONTENT_BROWSER_FIRST_PARTY_SETS_FIRST_PARTY_SETS_HANDLER_IMPL_H_
#define CONTENT_BROWSER_FIRST_PARTY_SETS_FIRST_PARTY_SETS_HANDLER_IMPL_H_
-#include <optional>
+#include <string>
+#include <utility>
+#include "base/containers/circular_deque.h"
+#include "base/files/file.h"
#include "base/files/file_path.h"
#include "base/functional/callback.h"
+#include "base/no_destructor.h"
+#include "base/sequence_checker.h"
+#include "base/thread_annotations.h"
+#include "base/threading/sequence_bound.h"
+#include "base/timer/elapsed_timer.h"
+#include "base/types/pass_key.h"
+#include "base/values.h"
+#include "base/version.h"
+#include "content/browser/first_party_sets/first_party_set_parser.h"
+#include "content/browser/first_party_sets/first_party_sets_handler_database_helper.h"
+#include "content/browser/first_party_sets/first_party_sets_loader.h"
#include "content/common/content_export.h"
#include "content/public/browser/first_party_sets_handler.h"
+#include "net/first_party_sets/first_party_sets_cache_filter.h"
+#include "net/first_party_sets/first_party_sets_context_config.h"
#include "net/first_party_sets/global_first_party_sets.h"
#include "net/first_party_sets/local_set_declaration.h"
+namespace net {
+class FirstPartySetEntry;
+class SchemefulSite;
+} // namespace net
+
namespace content {
-// FirstPartySetsHandlerImpl is an abstract class, encapsulating the
-// content-internal details of the First-Party Sets infrastructure. This class
-// is abstract so that it can be mocked during testing.
+class BrowserContext;
+class ScopedMockFirstPartySetsHandler;
+
+// Class FirstPartySetsHandlerImpl is a singleton, it allows an embedder to
+// provide First-Party Sets inputs from custom sources, then parses/merges the
+// inputs to form the current First-Party Sets data, compares them with the
+// persisted First-Party Sets data used during the last browser session to get
+// a list of sites that changed the First-Party Set they are part of, invokes
+// the provided callback with the current First-Party Sets data, and writes
+// the current First-Party Sets data to disk.
class CONTENT_EXPORT FirstPartySetsHandlerImpl : public FirstPartySetsHandler {
public:
+ using SetsReadyOnceCallback =
+ base::OnceCallback<void(net::GlobalFirstPartySets)>;
+
static FirstPartySetsHandlerImpl* GetInstance();
static void SetInstanceForTesting(FirstPartySetsHandlerImpl* test_instance);
+ ~FirstPartySetsHandlerImpl() override;
+
+ FirstPartySetsHandlerImpl(const FirstPartySetsHandlerImpl&) = delete;
+ FirstPartySetsHandlerImpl& operator=(const FirstPartySetsHandlerImpl&) =
+ delete;
+
// This method reads the persisted First-Party Sets from the file under
// `user_data_dir` and sets the First-Party Set that was provided via the
// flag(s).
@@ -34,8 +71,13 @@ class CONTENT_EXPORT FirstPartySetsHandlerImpl : public FirstPartySetsHandler {
// invocation of Chromium which had First-Party Sets enabled.
//
// Only the first call has any effect.
- void virtual Init(const base::FilePath& user_data_dir,
- const net::LocalSetDeclaration& local_set) = 0;
+ void Init(const base::FilePath& user_data_dir,
+ const net::LocalSetDeclaration& local_set);
+
+ // Factory method that exposes the ctor for testing.
+ static FirstPartySetsHandlerImpl CreateForTesting(
+ bool enabled,
+ bool embedder_will_provide_public_sets);
// Returns the fully-parsed and validated global First-Party Sets data.
// Returns the data synchronously via an std::optional if it's already
@@ -49,8 +91,164 @@ class CONTENT_EXPORT FirstPartySetsHandlerImpl : public FirstPartySetsHandler {
//
// If First-Party Sets is disabled, this returns a populated optional with an
// empty GlobalFirstPartySets instance.
- [[nodiscard]] virtual std::optional<net::GlobalFirstPartySets> GetSets(
- base::OnceCallback<void(net::GlobalFirstPartySets)> callback) = 0;
+ [[nodiscard]] virtual absl::optional<net::GlobalFirstPartySets> GetSets(
+ SetsReadyOnceCallback callback);
+
+ // FirstPartySetsHandler
+ bool IsEnabled() const override;
+ void SetPublicFirstPartySets(const base::Version& version,
+ base::File sets_file) override;
+ absl::optional<net::FirstPartySetEntry> FindEntry(
+ const net::SchemefulSite& site,
+ const net::FirstPartySetsContextConfig& config) const override;
+ void GetContextConfigForPolicy(
+ const base::Value::Dict* policy,
+ base::OnceCallback<void(net::FirstPartySetsContextConfig)> callback)
+ override;
+ void ClearSiteDataOnChangedSetsForContext(
+ base::RepeatingCallback<BrowserContext*()> browser_context_getter,
+ const std::string& browser_context_id,
+ net::FirstPartySetsContextConfig context_config,
+ base::OnceCallback<void(net::FirstPartySetsContextConfig,
+ net::FirstPartySetsCacheFilter)> callback)
+ override;
+ void ComputeFirstPartySetMetadata(
+ const net::SchemefulSite& site,
+ const net::SchemefulSite* top_frame_site,
+ const std::set<net::SchemefulSite>& party_context,
+ const net::FirstPartySetsContextConfig& config,
+ base::OnceCallback<void(net::FirstPartySetMetadata)> callback) override;
+ bool ForEachEffectiveSetEntry(
+ const net::FirstPartySetsContextConfig& config,
+ base::FunctionRef<bool(const net::SchemefulSite&,
+ const net::FirstPartySetEntry&)> f) const override;
+ void GetPersistedSetsForTesting(
+ const std::string& browser_context_id,
+ base::OnceCallback<
+ void(absl::optional<std::pair<net::GlobalFirstPartySets,
+ net::FirstPartySetsContextConfig>>)>
+ callback);
+ void HasBrowserContextClearedForTesting(
+ const std::string& browser_context_id,
+ base::OnceCallback<void(absl::optional<bool>)> callback);
+
+ void SynchronouslyResetDBHelperForTesting() {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+ db_helper_.SynchronouslyResetForTest(); // IN-TEST
+ }
+
+// // Computes information needed by the FirstPartySetsAccessDelegate in order
+// // to update the browser's list of First-Party Sets to respect a profile's
+// // setting for the per-profile FirstPartySetsOverrides policy.
+// static net::FirstPartySetsContextConfig ComputeEnterpriseContextConfig(
+// const net::GlobalFirstPartySets& browser_sets,
+// const FirstPartySetParser::ParsedPolicySetLists& policy);
+
+ protected:
+ FirstPartySetsHandlerImpl(base::PassKey<ScopedMockFirstPartySetsHandler> key,
+ bool enabled,
+ bool embedder_will_provide_public_sets);
+
+ private:
+ friend class base::NoDestructor<FirstPartySetsHandlerImpl>;
+
+ FirstPartySetsHandlerImpl(bool enabled,
+ bool embedder_will_provide_public_sets);
+
+ // Sets the global First-Party Sets data. Must be called exactly once.
+ void SetCompleteSets(net::GlobalFirstPartySets sets);
+
+ // Sets `db_helper_`, which will initialize the underlying First-Party Sets
+ // database under `user_data_dir`. Must be called exactly once.
+ void SetDatabase(const base::FilePath& user_data_dir);
+
+ // Enqueues a task to be performed once initialization is complete.
+ void EnqueuePendingTask(base::OnceClosure run_task);
+
+ // Invokes any pending queries.
+ void InvokePendingQueries();
+
+ // Returns the global First-Party Sets. This clones the underlying
+ // data.
+ //
+ // Must be called after the list has been initialized.
+ net::GlobalFirstPartySets GetGlobalSetsSync() const;
+
+ // Performs the actual state clearing for the given context. Must not be
+ // called until initialization is complete.
+ void ClearSiteDataOnChangedSetsForContextInternal(
+ base::RepeatingCallback<BrowserContext*()> browser_context_getter,
+ const std::string& browser_context_id,
+ net::FirstPartySetsContextConfig context_config,
+ base::OnceCallback<void(net::FirstPartySetsContextConfig,
+ net::FirstPartySetsCacheFilter)> callback);
+
+ // Like ComputeFirstPartySetMetadata, but passes the result into the provided
+ // callback. Must not be called before `global_sets_` has been set.
+ void ComputeFirstPartySetMetadataInternal(
+ const net::SchemefulSite& site,
+ const absl::optional<net::SchemefulSite>& top_frame_site,
+ const std::set<net::SchemefulSite>& party_context,
+ const net::FirstPartySetsContextConfig& config,
+ const base::ElapsedTimer& timer,
+ base::OnceCallback<void(net::FirstPartySetMetadata)> callback) const;
+
+ // Parses the policy and computes the config that represents the changes
+ // needed to apply `policy` to `global_sets_`.
+ net::FirstPartySetsContextConfig GetContextConfigForPolicyInternal(
+ const base::Value::Dict& policy,
+ const absl::optional<base::ElapsedTimer>& timer) const;
+
+ void OnGetSitesToClear(
+ base::RepeatingCallback<BrowserContext*()> browser_context_getter,
+ const std::string& browser_context_id,
+ net::FirstPartySetsContextConfig context_config,
+ base::OnceCallback<void(net::FirstPartySetsContextConfig,
+ net::FirstPartySetsCacheFilter)> callback,
+ std::pair<std::vector<net::SchemefulSite>, net::FirstPartySetsCacheFilter>
+ sites_to_clear) const;
+
+ // `failed_data_types` is a bitmask used to indicate data types from
+ // BrowsingDataRemover::DataType enum that were failed to remove. 0 indicates
+ // success.
+ void DidClearSiteDataOnChangedSetsForContext(
+ const std::string& browser_context_id,
+ net::FirstPartySetsContextConfig context_config,
+ net::FirstPartySetsCacheFilter cache_filter,
+ base::OnceCallback<void(net::FirstPartySetsContextConfig,
+ net::FirstPartySetsCacheFilter)> callback,
+ uint64_t failed_data_types) const;
+
+ // Whether Init has been called already or not.
+ bool initialized_ = false;
+
+ // The global First-Party Sets, after parsing and validation.
+ //
+ // This is nullopt until all of the required inputs have been received.
+ absl::optional<net::GlobalFirstPartySets> global_sets_
+ GUARDED_BY_CONTEXT(sequence_checker_);
+
+ bool enabled_ GUARDED_BY_CONTEXT(sequence_checker_);
+ bool embedder_will_provide_public_sets_ GUARDED_BY_CONTEXT(sequence_checker_);
+
+ // We use a OnceCallback to ensure we only pass along the sets once
+ // during Chrome's lifetime (modulo reconfiguring the network service).
+ base::circular_deque<base::OnceClosure> on_sets_ready_callbacks_
+ GUARDED_BY_CONTEXT(sequence_checker_);
+
+ std::unique_ptr<FirstPartySetsLoader> sets_loader_
+ GUARDED_BY_CONTEXT(sequence_checker_);
+
+ // Timer starting when the first async task was enqueued, if any. Used for
+ // metrics.
+ absl::optional<base::ElapsedTimer> first_async_task_timer_
+ GUARDED_BY_CONTEXT(sequence_checker_);
+
+ // Access the underlying DB on a database sequence to make sure none of DB
+ // operations that support blocking are called directly on the main thread.
+ base::SequenceBound<FirstPartySetsHandlerDatabaseHelper> db_helper_;
+
+ SEQUENCE_CHECKER(sequence_checker_);
};
} // namespace content
diff --git a/content/browser/first_party_sets/first_party_sets_handler_impl_instance.h b/content/browser/first_party_sets/first_party_sets_handler_impl_instance.h
deleted file mode 100644
index 974da5d2b26053e5624a5a9aa6cee183692b7863..0000000000000000000000000000000000000000
--- a/content/browser/first_party_sets/first_party_sets_handler_impl_instance.h
+++ /dev/null
@@ -1,221 +0,0 @@
-// Copyright 2023 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef CONTENT_BROWSER_FIRST_PARTY_SETS_FIRST_PARTY_SETS_HANDLER_IMPL_INSTANCE_H_
-#define CONTENT_BROWSER_FIRST_PARTY_SETS_FIRST_PARTY_SETS_HANDLER_IMPL_INSTANCE_H_
-
-#include <optional>
-#include <string>
-#include <utility>
-
-#include "base/containers/circular_deque.h"
-#include "base/files/file.h"
-#include "base/files/file_path.h"
-#include "base/functional/callback.h"
-#include "base/no_destructor.h"
-#include "base/sequence_checker.h"
-#include "base/thread_annotations.h"
-#include "base/threading/sequence_bound.h"
-#include "base/timer/elapsed_timer.h"
-#include "base/values.h"
-#include "base/version.h"
-#include "content/browser/first_party_sets/first_party_sets_handler_database_helper.h"
-#include "content/browser/first_party_sets/first_party_sets_handler_impl.h"
-#include "content/browser/first_party_sets/first_party_sets_loader.h"
-#include "content/common/content_export.h"
-#include "net/first_party_sets/first_party_sets_cache_filter.h"
-#include "net/first_party_sets/first_party_sets_context_config.h"
-#include "net/first_party_sets/global_first_party_sets.h"
-#include "net/first_party_sets/local_set_declaration.h"
-
-namespace net {
-class FirstPartySetEntry;
-class SchemefulSite;
-} // namespace net
-
-namespace content {
-
-class BrowserContext;
-
-// Class FirstPartySetsHandlerImplInstance is a singleton, it allows an embedder
-// to provide First-Party Sets inputs from custom sources, then parses/merges
-// the inputs to form the current First-Party Sets data, compares them with the
-// persisted First-Party Sets data used during the last browser session to get
-// a list of sites that changed the First-Party Set they are part of, invokes
-// the provided callback with the current First-Party Sets data, and writes
-// the current First-Party Sets data to disk.
-class CONTENT_EXPORT FirstPartySetsHandlerImplInstance
- : public FirstPartySetsHandlerImpl {
- public:
- ~FirstPartySetsHandlerImplInstance() override;
-
- FirstPartySetsHandlerImplInstance(const FirstPartySetsHandlerImplInstance&) =
- delete;
- FirstPartySetsHandlerImplInstance& operator=(
- const FirstPartySetsHandlerImplInstance&) = delete;
-
- // Factory method that exposes the ctor for testing.
- static FirstPartySetsHandlerImplInstance CreateForTesting(
- bool enabled,
- bool embedder_will_provide_public_sets);
-
- // FirstPartySetsHandlerImpl:
- void Init(const base::FilePath& user_data_dir,
- const net::LocalSetDeclaration& local_set) override;
- [[nodiscard]] std::optional<net::GlobalFirstPartySets> GetSets(
- base::OnceCallback<void(net::GlobalFirstPartySets)> callback) override;
-
- // FirstPartySetsHandler:
- bool IsEnabled() const override;
- void SetPublicFirstPartySets(const base::Version& version,
- base::File sets_file) override;
- std::optional<net::FirstPartySetEntry> FindEntry(
- const net::SchemefulSite& site,
- const net::FirstPartySetsContextConfig& config) const override;
- void GetContextConfigForPolicy(
- const base::Value::Dict* policy,
- base::OnceCallback<void(net::FirstPartySetsContextConfig)> callback)
- override;
- void ClearSiteDataOnChangedSetsForContext(
- base::RepeatingCallback<BrowserContext*()> browser_context_getter,
- const std::string& browser_context_id,
- net::FirstPartySetsContextConfig context_config,
- base::OnceCallback<void(net::FirstPartySetsContextConfig,
- net::FirstPartySetsCacheFilter)> callback)
- override;
- void ComputeFirstPartySetMetadata(
- const net::SchemefulSite& site,
- const net::SchemefulSite* top_frame_site,
- const net::FirstPartySetsContextConfig& config,
- base::OnceCallback<void(net::FirstPartySetMetadata)> callback) override;
- bool ForEachEffectiveSetEntry(
- const net::FirstPartySetsContextConfig& config,
- base::FunctionRef<bool(const net::SchemefulSite&,
- const net::FirstPartySetEntry&)> f) const override;
- void GetPersistedSetsForTesting(
- const std::string& browser_context_id,
- base::OnceCallback<
- void(std::optional<std::pair<net::GlobalFirstPartySets,
- net::FirstPartySetsContextConfig>>)>
- callback);
- void HasBrowserContextClearedForTesting(
- const std::string& browser_context_id,
- base::OnceCallback<void(std::optional<bool>)> callback);
-
- void SynchronouslyResetDBHelperForTesting() {
- DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
- db_helper_.SynchronouslyResetForTest(); // IN-TEST
- }
-
- private:
- friend class base::NoDestructor<FirstPartySetsHandlerImplInstance>;
-
- FirstPartySetsHandlerImplInstance(bool enabled,
- bool embedder_will_provide_public_sets);
-
- // Sets the global First-Party Sets data. Must be called exactly once.
- void SetCompleteSets(net::GlobalFirstPartySets sets);
-
- // Sets `db_helper_`, which will initialize the underlying First-Party Sets
- // database under `user_data_dir`. Must be called exactly once.
- void SetDatabase(const base::FilePath& user_data_dir);
-
- // Enqueues a task to be performed once initialization is complete.
- void EnqueuePendingTask(base::OnceClosure run_task);
-
- // Invokes any pending queries.
- void InvokePendingQueries();
-
- // Returns the global First-Party Sets. This clones the underlying
- // data.
- //
- // Must be called after the list has been initialized.
- net::GlobalFirstPartySets GetGlobalSetsSync() const;
-
- // Performs the actual state clearing for the given context. Must not be
- // called until initialization is complete.
- void ClearSiteDataOnChangedSetsForContextInternal(
- base::RepeatingCallback<BrowserContext*()> browser_context_getter,
- const std::string& browser_context_id,
- net::FirstPartySetsContextConfig context_config,
- base::OnceCallback<void(net::FirstPartySetsContextConfig,
- net::FirstPartySetsCacheFilter)> callback);
-
- // Like ComputeFirstPartySetMetadata, but passes the result into the provided
- // callback. Must not be called before `global_sets_` has been set.
- void ComputeFirstPartySetMetadataInternal(
- const net::SchemefulSite& site,
- const std::optional<net::SchemefulSite>& top_frame_site,
- const net::FirstPartySetsContextConfig& config,
- const base::ElapsedTimer& timer,
- base::OnceCallback<void(net::FirstPartySetMetadata)> callback) const;
-
- // Parses the policy and computes the config that represents the changes
- // needed to apply `policy` to `global_sets_`.
- net::FirstPartySetsContextConfig GetContextConfigForPolicyInternal(
- const base::Value::Dict& policy,
- const std::optional<base::ElapsedTimer>& timer) const;
-
- void OnGetSitesToClear(
- base::RepeatingCallback<BrowserContext*()> browser_context_getter,
- const std::string& browser_context_id,
- net::FirstPartySetsContextConfig context_config,
- base::OnceCallback<void(net::FirstPartySetsContextConfig,
- net::FirstPartySetsCacheFilter)> callback,
- std::pair<std::vector<net::SchemefulSite>, net::FirstPartySetsCacheFilter>
- sites_to_clear) const;
-
- // `failed_data_types` is a bitmask used to indicate data types from
- // BrowsingDataRemover::DataType enum that were failed to remove. 0 indicates
- // success.
- void DidClearSiteDataOnChangedSetsForContext(
- const std::string& browser_context_id,
- net::FirstPartySetsContextConfig context_config,
- net::FirstPartySetsCacheFilter cache_filter,
- base::OnceCallback<void(net::FirstPartySetsContextConfig,
- net::FirstPartySetsCacheFilter)> callback,
- uint64_t failed_data_types) const;
-
- // Whether Init has been called already or not.
- bool initialized_ = false;
-
- // The global First-Party Sets, after parsing and validation.
- //
- // This is nullopt until all of the required inputs have been received.
- std::optional<net::GlobalFirstPartySets> global_sets_
- GUARDED_BY_CONTEXT(sequence_checker_);
-
- // Whether the First-Party Sets feature should behave as "enabled" or not,
- // according to the embedder.
- const bool enabled_ GUARDED_BY_CONTEXT(sequence_checker_);
-
- // A queue of tasks waiting to run once this instance has the full
- // GlobalFirstPartySets instance. If `enabled_` is true, then this queue is
- // non-null until `global_sets_` is non-nullopt. Otherwise, it is always
- // nullptr.
- std::unique_ptr<base::circular_deque<base::OnceClosure>>
- on_sets_ready_callbacks_ GUARDED_BY_CONTEXT(sequence_checker_);
-
- // A helper object to handle loading and combining First-Party Sets from
- // different sources (i.e. the command-line flag and the list provided by the
- // embedder). This is nullptr if `enabled_` is false; and it is nullptr after
- // `global_sets_` has been set.
- std::unique_ptr<FirstPartySetsLoader> sets_loader_
- GUARDED_BY_CONTEXT(sequence_checker_);
-
- // Timer starting when the first async task was enqueued, if any. Used for
- // metrics.
- std::optional<base::ElapsedTimer> first_async_task_timer_
- GUARDED_BY_CONTEXT(sequence_checker_);
-
- // Access the underlying DB on a database sequence to make sure none of DB
- // operations that support blocking are called directly on the main thread.
- base::SequenceBound<FirstPartySetsHandlerDatabaseHelper> db_helper_;
-
- SEQUENCE_CHECKER(sequence_checker_);
-};
-
-} // namespace content
-
-#endif // CONTENT_BROWSER_FIRST_PARTY_SETS_FIRST_PARTY_SETS_HANDLER_IMPL_INSTANCE_H_
diff --git a/content/browser/first_party_sets/first_party_sets_loader.cc b/content/browser/first_party_sets/first_party_sets_loader.cc
index 21371a4962adf787595b6acba0d4656db563ae52..ca4c810401b3cac0728efd832d876c85fe0ecd76 100644
--- a/content/browser/first_party_sets/first_party_sets_loader.cc
+++ b/content/browser/first_party_sets/first_party_sets_loader.cc
@@ -28,6 +28,19 @@ std::string ReadSetsFile(base::File sets_file) {
return base::ReadStreamToString(file.get(), &raw_sets) ? raw_sets : "";
}
+void DisposeFile(base::File sets_file) {
+ if (!sets_file.IsValid()) {
+ return;
+ }
+ base::ThreadPool::PostTask(
+ FROM_HERE, {base::MayBlock(), base::TaskPriority::BEST_EFFORT},
+ base::BindOnce(
+ [](base::File sets_file) {
+ // Run `sets_file`'s dtor in the threadpool.
+ },
+ std::move(sets_file)));
+}
+
} // namespace
FirstPartySetsLoader::FirstPartySetsLoader(
@@ -41,9 +54,6 @@ FirstPartySetsLoader::~FirstPartySetsLoader() {
void FirstPartySetsLoader::SetManuallySpecifiedSet(
const net::LocalSetDeclaration& local_set) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
- if (manually_specified_set_.has_value()) {
- return;
- }
manually_specified_set_ = local_set;
UmaHistogramTimes(
"Cookie.FirstPartySets.InitializationDuration.ReadCommandLineSet2",
@@ -76,20 +86,6 @@ void FirstPartySetsLoader::SetComponentSets(base::Version version,
weak_factory_.GetWeakPtr(), std::move(version)));
}
-// static
-void FirstPartySetsLoader::DisposeFile(base::File file) {
- if (!file.IsValid()) {
- return;
- }
- base::ThreadPool::PostTask(
- FROM_HERE, {base::MayBlock(), base::TaskPriority::BEST_EFFORT},
- base::BindOnce(
- [](base::File file) {
- // Run `file`'s dtor in the threadpool.
- },
- std::move(file)));
-}
-
void FirstPartySetsLoader::OnReadSetsFile(base::Version version,
const std::string& raw_sets) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
diff --git a/content/browser/first_party_sets/first_party_sets_loader.h b/content/browser/first_party_sets/first_party_sets_loader.h
index 1c49082d2fb462b0974ddf1bf46c4a254582b3c4..662a3164c13acb6eda354c3d9cb9061a3e7e7266 100644
--- a/content/browser/first_party_sets/first_party_sets_loader.h
+++ b/content/browser/first_party_sets/first_party_sets_loader.h
@@ -47,9 +47,6 @@ class CONTENT_EXPORT FirstPartySetsLoader {
// invocations are ignored.
void SetComponentSets(base::Version version, base::File sets_file);
- // Closes the given file safely.
- static void DisposeFile(base::File file);
-
private:
// Parses the contents of `raw_sets` as a collection of First-Party Set
// declarations, and stores the result.
diff --git a/content/browser/media/android/media_resource_getter_impl.cc b/content/browser/media/android/media_resource_getter_impl.cc
index 1272f4b36aa27c08fd9cd57f3c89845533fbfae1..f853d1ff148a52f66ec7cf597eae01c82eef98cf 100644
--- a/content/browser/media/android/media_resource_getter_impl.cc
+++ b/content/browser/media/android/media_resource_getter_impl.cc
@@ -61,7 +61,7 @@ GetRestrictedCookieManagerForContext(
site_for_cookies.IsFirstParty(top_frame_origin.GetURL()));
net::IsolationInfo isolation_info = net::IsolationInfo::Create(
net::IsolationInfo::RequestType::kOther, top_frame_origin,
- top_frame_origin, site_for_cookies);
+ top_frame_origin, site_for_cookies, absl::nullopt);
mojo::PendingRemote<network::mojom::RestrictedCookieManager> pipe;
static_cast<StoragePartitionImpl*>(storage_partition)
diff --git a/content/browser/renderer_host/cookie_utils.cc b/content/browser/renderer_host/cookie_utils.cc
index b26798dd429a3de3221911c3e5fe6df3e7e2f1e6..08d4ef9a657a2f77e2a7f82ce08a2846ffeb7c19 100644
--- a/content/browser/renderer_host/cookie_utils.cc
+++ b/content/browser/renderer_host/cookie_utils.cc
@@ -190,6 +190,8 @@ void RecordSchemefulContextDowngradeUKM(
bool ShouldReportDevToolsIssueForStatus(
const net::CookieInclusionStatus& status) {
return status.ShouldWarn() ||
+ status.HasExclusionReason(
+ net::CookieInclusionStatus::EXCLUDE_INVALID_SAMEPARTY) ||
status.HasExclusionReason(
net::CookieInclusionStatus::EXCLUDE_DOMAIN_NON_ASCII) ||
status.HasExclusionReason(
@@ -229,6 +231,10 @@ void EmitCookieWarningsAndMetricsOnce(
bool breaking_context_downgrade = false;
bool lax_allow_unsafe_cookies = false;
+ bool same_party = false;
+ bool same_party_exclusion_overruled_samesite = false;
+ bool same_party_inclusion_overruled_samesite = false;
+
bool samesite_cookie_inclusion_changed_by_cross_site_redirect = false;
bool partitioned_cookies_exist = false;
@@ -286,6 +292,22 @@ void EmitCookieWarningsAndMetricsOnce(
net::CookieInclusionStatus::
WARN_SAMESITE_UNSPECIFIED_LAX_ALLOW_UNSAFE);
+ same_party = same_party ||
+ status.HasWarningReason(
+ net::CookieInclusionStatus::WARN_TREATED_AS_SAMEPARTY);
+
+ same_party_exclusion_overruled_samesite =
+ same_party_exclusion_overruled_samesite ||
+ status.HasWarningReason(
+ net::CookieInclusionStatus::
+ WARN_SAMEPARTY_EXCLUSION_OVERRULED_SAMESITE);
+
+ same_party_inclusion_overruled_samesite =
+ same_party_inclusion_overruled_samesite ||
+ status.HasWarningReason(
+ net::CookieInclusionStatus::
+ WARN_SAMEPARTY_INCLUSION_OVERRULED_SAMESITE);
+
samesite_cookie_inclusion_changed_by_cross_site_redirect =
samesite_cookie_inclusion_changed_by_cross_site_redirect ||
status.HasWarningReason(
@@ -385,6 +407,23 @@ void EmitCookieWarningsAndMetricsOnce(
rfh, blink::mojom::WebFeature::kLaxAllowingUnsafeCookies);
}
+ if (same_party) {
+ GetContentClient()->browser()->LogWebFeatureForCurrentPage(
+ rfh, blink::mojom::WebFeature::kSamePartyCookieAttribute);
+ }
+
+ if (same_party_exclusion_overruled_samesite) {
+ GetContentClient()->browser()->LogWebFeatureForCurrentPage(
+ rfh,
+ blink::mojom::WebFeature::kSamePartyCookieExclusionOverruledSameSite);
+ }
+
+ if (same_party_inclusion_overruled_samesite) {
+ GetContentClient()->browser()->LogWebFeatureForCurrentPage(
+ rfh,
+ blink::mojom::WebFeature::kSamePartyCookieInclusionOverruledSameSite);
+ }
+
if (samesite_cookie_inclusion_changed_by_cross_site_redirect) {
GetContentClient()->browser()->LogWebFeatureForCurrentPage(
rfh, blink::mojom::WebFeature::
diff --git a/content/browser/renderer_host/render_frame_host_impl.cc b/content/browser/renderer_host/render_frame_host_impl.cc
index bafeeb41441d7a585bef170f81d69fe54417da63..e9385fef76514e7b341a1b35adfafc3e4f16189d 100644
--- a/content/browser/renderer_host/render_frame_host_impl.cc
+++ b/content/browser/renderer_host/render_frame_host_impl.cc
@@ -4157,7 +4157,10 @@ net::IsolationInfo RenderFrameHostImpl::ComputeIsolationInfoInternal(
net::SiteForCookies candidate_site_for_cookies(top_frame_site);
- // Walk up the frame tree to check SiteForCookies.
+ std::set<net::SchemefulSite> party_context;
+
+ // Walk up the frame tree to check SiteForCookies and compute the
+ // |party_context|.
//
// If |request_type| is kOther, then IsolationInfo is being computed
// for subresource requests. Check/compute starting from the frame itself.
@@ -4175,6 +4178,9 @@ net::IsolationInfo RenderFrameHostImpl::ComputeIsolationInfoInternal(
rfh == this ? frame_origin : rfh->last_committed_origin_;
net::SchemefulSite cur_site = net::SchemefulSite(cur_origin);
+ if (top_frame_site != cur_site) {
+ party_context.insert(cur_site);
+ }
candidate_site_for_cookies.CompareWithFrameTreeSiteAndRevise(cur_site);
}
@@ -4192,7 +4198,7 @@ net::IsolationInfo RenderFrameHostImpl::ComputeIsolationInfoInternal(
ComputeNonce(is_credentialless, fenced_frame_nonce_for_navigation);
return net::IsolationInfo::Create(request_type, top_frame_origin,
frame_origin, candidate_site_for_cookies,
- nonce);
+ std::move(party_context), nonce);
}
std::optional<base::UnguessableToken> RenderFrameHostImpl::ComputeNonce(
diff --git a/content/browser/service_worker/service_worker_main_resource_loader_interceptor.cc b/content/browser/service_worker/service_worker_main_resource_loader_interceptor.cc
index 055ee2779362331aa87699131d6ba226003b03aa..1d2641a247dfbda1885b2567759b98167bf39eb6 100644
--- a/content/browser/service_worker/service_worker_main_resource_loader_interceptor.cc
+++ b/content/browser/service_worker/service_worker_main_resource_loader_interceptor.cc
@@ -221,7 +221,7 @@ void ServiceWorkerMainResourceLoaderInterceptor::MaybeCreateLoader(
isolation_info_ = net::IsolationInfo::Create(
isolation_info_.request_type(),
isolation_info_.top_frame_origin().value(), new_origin,
- new_site_for_cookies, isolation_info_.nonce());
+ new_site_for_cookies, absl::nullopt, isolation_info_.nonce());
// Attempt to get the storage key from |RenderFrameHostImpl|. This correctly
// accounts for extension URLs. The absence of this logic was a potential
diff --git a/content/browser/worker_host/shared_worker_host.cc b/content/browser/worker_host/shared_worker_host.cc
index 2cf831086db9eb7b65c3a53da70a295448af0121..b0ef55c6351a8f7e07f8626416a7ce92496715ba 100644
--- a/content/browser/worker_host/shared_worker_host.cc
+++ b/content/browser/worker_host/shared_worker_host.cc
@@ -428,7 +428,14 @@ SharedWorkerHost::CreateNetworkFactoryParamsForSubresources() {
}
network::mojom::URLLoaderFactoryParamsPtr factory_params =
URLLoaderFactoryParamsHelper::CreateForWorker(
- GetProcessHost(), origin, GetStorageKey().ToPartialNetIsolationInfo(),
+ GetProcessHost(), origin,
+ net::IsolationInfo::Create(
+ net::IsolationInfo::RequestType::kOther,
+ // TODO(https://crbug.com/1147281): We
+ // should pass the top_level_site from
+ // `GetStorageKey()` instead.
+ origin, origin, net::SiteForCookies::FromOrigin(origin),
+ /*party_context=*/absl::nullopt, GetStorageKey().nonce()),
std::move(coep_reporter),
/*url_loader_network_observer=*/mojo::NullRemote(),
/*devtools_observer=*/mojo::NullRemote(),
diff --git a/content/browser/worker_host/shared_worker_service_impl.cc b/content/browser/worker_host/shared_worker_service_impl.cc
index fd286b84287d247ad04cb62f4e7560db3d5040d5..3f1c9919d207dab6da985778bcbfe29f2c5ea708 100644
--- a/content/browser/worker_host/shared_worker_service_impl.cc
+++ b/content/browser/worker_host/shared_worker_service_impl.cc
@@ -370,7 +370,11 @@ SharedWorkerHost* SharedWorkerServiceImpl::CreateWorker(
worker_process_host->GetID(), host->token(), host->instance().url(),
&creator, &creator, host->instance().storage_key().ToNetSiteForCookies(),
host->instance().storage_key().origin(), host->instance().storage_key(),
- host->instance().storage_key().ToPartialNetIsolationInfo(),
+ net::IsolationInfo::Create(net::IsolationInfo::RequestType::kOther,
+ worker_origin, worker_origin,
+ net::SiteForCookies::FromOrigin(worker_origin),
+ /*party_context=*/absl::nullopt,
+ host->instance().storage_key().nonce()),
creator.BuildClientSecurityStateForWorkers(), credentials_mode,
std::move(outside_fetch_client_settings_object),
network::mojom::RequestDestination::kSharedWorker,
diff --git a/content/public/browser/content_browser_client.h b/content/public/browser/content_browser_client.h
index c7f6be5f6a651647cc3f6814f25ce4cb0bf1e4ef..a86f719d115fb400bceb1e1bdf213b8b4f667700 100644
--- a/content/public/browser/content_browser_client.h
+++ b/content/public/browser/content_browser_client.h
@@ -1913,7 +1913,8 @@ class CONTENT_EXPORT ContentBrowserClient {
// |site_for_cookies| is empty, no domains are first-party).
// |top_frame_origin| held by |isolation_info| represents the domain for
// top-level frame, and can be used to look up preferences that are dependent
- // on that.
+ // on that. |party_context| hold by |isolation_info| is for the purposes of
+ // SameParty cookies inclusion calculation.
//
// |*receiver| is always valid upon entry.
//
diff --git a/content/public/browser/first_party_sets_handler.h b/content/public/browser/first_party_sets_handler.h
index 9ec93b54956dc17e0ad9e7383830761812199e2f..029c546412649326ac74e0de47b773efe747e3d6 100644
--- a/content/public/browser/first_party_sets_handler.h
+++ b/content/public/browser/first_party_sets_handler.h
@@ -194,6 +194,7 @@ class CONTENT_EXPORT FirstPartySetsHandler {
virtual void ComputeFirstPartySetMetadata(
const net::SchemefulSite& site,
const net::SchemefulSite* top_frame_site,
+ const std::set<net::SchemefulSite>& party_context,
const net::FirstPartySetsContextConfig& config,
base::OnceCallback<void(net::FirstPartySetMetadata)> callback) = 0;
diff --git a/google_apis/gaia/oauth2_mint_token_flow.cc b/google_apis/gaia/oauth2_mint_token_flow.cc
index 4d7761e1d66665d4dbaa9d6b46f38fb0e2cfb288..b87dafb0ec13529ad1719d013430ac3166c84db2 100644
--- a/google_apis/gaia/oauth2_mint_token_flow.cc
+++ b/google_apis/gaia/oauth2_mint_token_flow.cc
@@ -475,6 +475,7 @@ bool OAuth2MintTokenFlow::ParseRemoteConsentResponse(
is_http_only ? *is_http_only : false,
net::StringToCookieSameSite(same_site ? *same_site : ""),
net::COOKIE_PRIORITY_DEFAULT,
+ /* same_partyy */ false,
/* partition_key */ std::nullopt);
cookies.push_back(*cookie);
}
diff --git a/google_apis/gaia/oauth_multilogin_result.cc b/google_apis/gaia/oauth_multilogin_result.cc
index 22a559059bc033ab3b2abd8a39e10671dd445cd4..6a2a32a7e2adf0448131d0ca6aa71977c3081bc2 100644
--- a/google_apis/gaia/oauth_multilogin_result.cc
+++ b/google_apis/gaia/oauth_multilogin_result.cc
@@ -138,6 +138,7 @@ void OAuthMultiloginResult::TryParseCookiesFromValue(
/*last_access=*/now, /*last_update=*/now, is_secure.value_or(true),
is_http_only.value_or(true), samesite_mode,
net::StringToCookiePriority(priority ? *priority : "medium"),
+ /* same_party */ false,
/*partition_key=*/std::nullopt, net::CookieSourceScheme::kUnset,
url::PORT_UNSPECIFIED);
// If the unique_ptr is null, it means the cookie was not canonical.
diff --git a/net/BUILD.gn b/net/BUILD.gn
index 65bc73a317778a9ec085bb5744a50c8bf271215a..74091a15dfe0dcea453120ed099b00665373327c 100644
--- a/net/BUILD.gn
+++ b/net/BUILD.gn
@@ -514,6 +514,8 @@ component("net") {
"first_party_sets/global_first_party_sets.h",
"first_party_sets/local_set_declaration.cc",
"first_party_sets/local_set_declaration.h",
+ "first_party_sets/same_party_context.cc",
+ "first_party_sets/same_party_context.h",
"first_party_sets/sets_mutation.cc",
"first_party_sets/sets_mutation.h",
"http/alternative_service.cc",
diff --git a/net/base/features.cc b/net/base/features.cc
index 84235adad43d182bd0471b9465557d293bffed1d..f5eae4b9b02f6297772de9062e138ee26871e946 100644
--- a/net/base/features.cc
+++ b/net/base/features.cc
@@ -230,6 +230,10 @@ BASE_FEATURE(kCookieSameSiteConsidersRedirectChain,
"CookieSameSiteConsidersRedirectChain",
base::FEATURE_DISABLED_BY_DEFAULT);
+BASE_FEATURE(kSamePartyAttributeEnabled,
+ "SamePartyAttributeEnabled",
+ base::FEATURE_DISABLED_BY_DEFAULT);
+
BASE_FEATURE(kWaitForFirstPartySetsInit,
"WaitForFirstPartySetsInit",
base::FEATURE_DISABLED_BY_DEFAULT);
diff --git a/net/base/features.h b/net/base/features.h
index f4afe84de8dc3b1f4cc1692af6fd0f7f7ae77816..79420b79a3e812140f1d02fee6a26760bb458b1c 100644
--- a/net/base/features.h
+++ b/net/base/features.h
@@ -282,6 +282,12 @@ NET_EXPORT BASE_DECLARE_FEATURE(kUdpSocketPosixAlwaysUpdateBytesReceived);
// See spec changes in https://github.com/httpwg/http-extensions/pull/1348
NET_EXPORT BASE_DECLARE_FEATURE(kCookieSameSiteConsidersRedirectChain);
+// When this feature is enabled, the SameParty attribute is enabled. (Note that
+// when this feature is disabled, the SameParty attribute is still parsed and
+// saved for cookie-sets, but it has no associated semantics (when setting or
+// reading cookies).)
+NET_EXPORT BASE_DECLARE_FEATURE(kSamePartyAttributeEnabled);
+
// When this feature is enabled, the network service will wait until First-Party
// Sets are initialized before issuing requests that use the HTTP cache or
// cookies.
diff --git a/net/base/isolation_info.cc b/net/base/isolation_info.cc
index c58b63d3fc63a29b374ef60c80134354f28bf18d..0605cb419b0e2fcc58867ebc0cda129de038c136 100644
--- a/net/base/isolation_info.cc
+++ b/net/base/isolation_info.cc
@@ -48,11 +48,13 @@ bool IsConsistent(IsolationInfo::RequestType request_type,
const std::optional<url::Origin>& top_frame_origin,
const std::optional<url::Origin>& frame_origin,
const SiteForCookies& site_for_cookies,
+ absl::optional<std::set<SchemefulSite>> party_context,
const std::optional<base::UnguessableToken>& nonce) {
// Check for the default-constructed case.
if (!top_frame_origin) {
return request_type == IsolationInfo::RequestType::kOther &&
- !frame_origin && !nonce && site_for_cookies.IsNull();
+ !frame_origin && !nonce && site_for_cookies.IsNull() &&
+ !party_context;
}
// As long as there is a |top_frame_origin|, |site_for_cookies| must be
@@ -74,6 +76,9 @@ bool IsConsistent(IsolationInfo::RequestType request_type,
//
// TODO(https://crbug.com/1060631): Once CreatePartial() is removed,
// check if SiteForCookies is non-null if the scheme is HTTP or HTTPS.
+ //
+ // TODO(https://crbug.com/1151947): Once CreatePartial() is removed,
+ // check if party_context is non-null and empty.
break;
case IsolationInfo::RequestType::kSubFrame:
// For subframe navigations, the subframe's origin may not be consistent
@@ -95,7 +100,8 @@ IsolationInfo::IsolationInfo()
/*top_frame_origin=*/std::nullopt,
/*frame_origin=*/std::nullopt,
SiteForCookies(),
- /*nonce=*/std::nullopt) {}
+ /*nonce=*/std::nullopt,
+ /*party_context=*/std::nullopt) {}
IsolationInfo::IsolationInfo(const IsolationInfo&) = default;
IsolationInfo::IsolationInfo(IsolationInfo&&) = default;
@@ -107,13 +113,15 @@ IsolationInfo IsolationInfo::CreateForInternalRequest(
const url::Origin& top_frame_origin) {
return IsolationInfo(RequestType::kOther, top_frame_origin, top_frame_origin,
SiteForCookies::FromOrigin(top_frame_origin),
- /*nonce=*/std::nullopt);
+ /*nonce=*/std::nullopt,
+ /*party_context=*/std::set<SchemefulSite>());
}
IsolationInfo IsolationInfo::CreateTransient() {
url::Origin opaque_origin;
return IsolationInfo(RequestType::kOther, opaque_origin, opaque_origin,
- SiteForCookies(), /*nonce=*/std::nullopt);
+ SiteForCookies(), /*nonce=*/std::nullopt,
+ /*party_context=*/std::nullopt);
}
std::optional<IsolationInfo> IsolationInfo::Deserialize(
@@ -130,11 +138,19 @@ std::optional<IsolationInfo> IsolationInfo::Deserialize(
if (proto.has_frame_origin())
frame_origin = url::Origin::Create(GURL(proto.frame_origin()));
+ absl::optional<std::set<SchemefulSite>> party_context;
+ if (proto.has_party_context()) {
+ party_context = std::set<SchemefulSite>();
+ for (const auto& site : proto.party_context().site()) {
+ party_context->insert(SchemefulSite::Deserialize(site));
+ }
+ }
+
return IsolationInfo::CreateIfConsistent(
static_cast<RequestType>(proto.request_type()),
std::move(top_frame_origin), std::move(frame_origin),
SiteForCookies::FromUrl(GURL(proto.site_for_cookies())),
- /*nonce=*/std::nullopt);
+ std::move(party_context), /*nonce=*/std::nullopt);
}
IsolationInfo IsolationInfo::Create(
@@ -142,9 +158,10 @@ IsolationInfo IsolationInfo::Create(
const url::Origin& top_frame_origin,
const url::Origin& frame_origin,
const SiteForCookies& site_for_cookies,
+ std::optional<std::set<SchemefulSite>> party_context,
const std::optional<base::UnguessableToken>& nonce) {
return IsolationInfo(request_type, top_frame_origin, frame_origin,
- site_for_cookies, nonce);
+ site_for_cookies, nonce, std::move(party_context));
}
IsolationInfo IsolationInfo::DoNotUseCreatePartialFromNak(
@@ -173,7 +190,8 @@ IsolationInfo IsolationInfo::DoNotUseCreatePartialFromNak(
auto isolation_info = IsolationInfo::Create(
IsolationInfo::RequestType::kOther, top_frame_origin,
- frame_origin.value(), SiteForCookies(), nonce);
+ frame_origin.value(), SiteForCookies(),
+ /*party_context=*/std::nullopt, nonce);
// TODO(crbug/1343856): DCHECK isolation info is fully populated.
return isolation_info;
}
@@ -183,13 +201,14 @@ std::optional<IsolationInfo> IsolationInfo::CreateIfConsistent(
const std::optional<url::Origin>& top_frame_origin,
const std::optional<url::Origin>& frame_origin,
const SiteForCookies& site_for_cookies,
+ std::optional<std::set<SchemefulSite>> party_context,
const std::optional<base::UnguessableToken>& nonce) {
if (!IsConsistent(request_type, top_frame_origin, frame_origin,
- site_for_cookies, nonce)) {
+ site_for_cookies, party_context, nonce)) {
return std::nullopt;
}
return IsolationInfo(request_type, top_frame_origin, frame_origin,
- site_for_cookies, nonce);
+ site_for_cookies, nonce, std::move(party_context));
}
IsolationInfo IsolationInfo::CreateForRedirect(
@@ -199,12 +218,14 @@ IsolationInfo IsolationInfo::CreateForRedirect(
if (request_type_ == RequestType::kSubFrame) {
return IsolationInfo(request_type_, top_frame_origin_, new_origin,
- site_for_cookies_, nonce_);
+ site_for_cookies_, nonce_, party_context_);
}
DCHECK_EQ(RequestType::kMainFrame, request_type_);
+ DCHECK(!party_context_ || party_context_->empty());
return IsolationInfo(request_type_, new_origin, new_origin,
- SiteForCookies::FromOrigin(new_origin), nonce_);
+ SiteForCookies::FromOrigin(new_origin), nonce_,
+ party_context_);
}
const std::optional<url::Origin>& IsolationInfo::frame_origin() const {
@@ -223,7 +244,8 @@ bool IsolationInfo::IsEqualForTesting(const IsolationInfo& other) const {
network_isolation_key_ == other.network_isolation_key_ &&
network_anonymization_key_ == other.network_anonymization_key_ &&
nonce_ == other.nonce_ &&
- site_for_cookies_.IsEquivalent(other.site_for_cookies_));
+ site_for_cookies_.IsEquivalent(other.site_for_cookies_) &&
+ party_context_ == other.party_context_);
}
std::string IsolationInfo::Serialize() const {
@@ -242,6 +264,13 @@ std::string IsolationInfo::Serialize() const {
info.set_site_for_cookies(site_for_cookies_.RepresentativeUrl().spec());
+ if (party_context_) {
+ auto* pc = info.mutable_party_context();
+ for (const auto& site : *party_context_) {
+ pc->add_site(site.Serialize());
+ }
+ }
+
return info.SerializeAsString();
}
@@ -280,6 +309,18 @@ std::string IsolationInfo::DebugString() const {
s += "; network_isolation_key: ";
s += network_isolation_key_.ToDebugString();
+ s += "; party_context: ";
+ if (party_context_) {
+ s += "{";
+ for (auto& site : party_context_.value()) {
+ s += site.GetDebugString();
+ s += ", ";
+ }
+ s += "}";
+ } else {
+ s += "(none)";
+ }
+
s += "; nonce: ";
if (nonce_) {
s += nonce_.value().ToString();
@@ -308,11 +349,13 @@ IsolationInfo::CreateNetworkAnonymizationKeyForIsolationInfo(
frame_site, nonce);
}
-IsolationInfo::IsolationInfo(RequestType request_type,
- const std::optional<url::Origin>& top_frame_origin,
- const std::optional<url::Origin>& frame_origin,
- const SiteForCookies& site_for_cookies,
- const std::optional<base::UnguessableToken>& nonce)
+IsolationInfo::IsolationInfo(
+ RequestType request_type,
+ const std::optional<url::Origin>& top_frame_origin,
+ const std::optional<url::Origin>& frame_origin,
+ const SiteForCookies& site_for_cookies,
+ const std::optional<base::UnguessableToken>& nonce,
+ std::optional<std::set<SchemefulSite>> party_context)
: request_type_(request_type),
top_frame_origin_(top_frame_origin),
frame_origin_(frame_origin),
@@ -327,9 +370,13 @@ IsolationInfo::IsolationInfo(RequestType request_type,
frame_origin,
nonce)),
site_for_cookies_(site_for_cookies),
- nonce_(nonce) {
+ nonce_(nonce),
+ party_context_(party_context.has_value() &&
+ party_context->size() > kPartyContextMaxSize
+ ? absl::nullopt
+ : party_context) {
DCHECK(IsConsistent(request_type_, top_frame_origin_, frame_origin_,
- site_for_cookies_, nonce));
+ site_for_cookies_, party_context_, nonce));
}
} // namespace net
diff --git a/net/base/isolation_info.h b/net/base/isolation_info.h
index c391feb3dab23ebe8ea600846d0d7d79b9b96369..7d685b469f514520457a4fee44c4caba359fb0da 100644
--- a/net/base/isolation_info.h
+++ b/net/base/isolation_info.h
@@ -67,8 +67,11 @@ class NET_EXPORT IsolationInfo {
kOther,
};
+ // Bound the party_context size with a reasonable number.
+ static constexpr size_t kPartyContextMaxSize = 20;
+
// Default constructor returns an IsolationInfo with empty origins, a null
- // SiteForCookies(), and a RequestType of kOther.
+ // SiteForCookies(), null |party_context|, and a RequestType of kOther.
IsolationInfo();
IsolationInfo(const IsolationInfo&);
IsolationInfo(IsolationInfo&&);
@@ -80,7 +83,7 @@ class NET_EXPORT IsolationInfo {
// Simple constructor for internal requests. Sets |frame_origin| and
// |site_for_cookies| match |top_frame_origin|. Sets |request_type| to
// kOther. Will only send SameSite cookies to the site associated with
- // the passed in origin.
+ // the passed in origin. |party_context| is set to be an empty set.
static IsolationInfo CreateForInternalRequest(
const url::Origin& top_frame_origin);
@@ -105,6 +108,7 @@ class NET_EXPORT IsolationInfo {
// * If |request_type| is kOther, |top_frame_origin| and
// |frame_origin| must be first party with respect to |site_for_cookies|, or
// |site_for_cookies| must be null.
+ // * If |party_context| is not empty, |top_frame_origin| must not be null.
// * If |nonce| is specified, then |top_frame_origin| must not be null.
//
// Note that the |site_for_cookies| consistency checks are skipped when
@@ -114,6 +118,7 @@ class NET_EXPORT IsolationInfo {
const url::Origin& top_frame_origin,
const url::Origin& frame_origin,
const SiteForCookies& site_for_cookies,
+ std::optional<std::set<SchemefulSite>> party_context = std::nullopt,
const std::optional<base::UnguessableToken>& nonce = std::nullopt);
// TODO(crbug/1372769): Remove this and create a safer way to ensure NIKs
@@ -132,6 +137,7 @@ class NET_EXPORT IsolationInfo {
const std::optional<url::Origin>& top_frame_origin,
const std::optional<url::Origin>& frame_origin,
const SiteForCookies& site_for_cookies,
+ std::optional<std::set<SchemefulSite>> party_context = std::nullopt,
const std::optional<base::UnguessableToken>& nonce = std::nullopt);
// Create a new IsolationInfo for a redirect to the supplied origin. |this| is
@@ -174,6 +180,15 @@ class NET_EXPORT IsolationInfo {
// Do not use outside of testing. Returns the `frame_origin_`.
const std::optional<url::Origin>& frame_origin_for_testing() const;
+ // Return |party_context| which exclude the top frame origin and the frame
+ // origin.
+ // TODO(mmenke): Make this function PartyContextForTesting() after switching
+ // RenderFrameHostImpl to use the parent IsolationInfo to create the child
+ // IsolationInfo instead of walking through all parent frames.
+ const absl::optional<std::set<SchemefulSite>>& party_context() const {
+ return party_context_;
+ }
+
bool IsEqualForTesting(const IsolationInfo& other) const;
NetworkAnonymizationKey CreateNetworkAnonymizationKeyForIsolationInfo(
@@ -192,7 +207,8 @@ class NET_EXPORT IsolationInfo {
const std::optional<url::Origin>& top_frame_origin,
const std::optional<url::Origin>& frame_origin,
const SiteForCookies& site_for_cookies,
- const std::optional<base::UnguessableToken>& nonce);
+ const std::optional<base::UnguessableToken>& nonce,
+ std::optional<std::set<SchemefulSite>> party_context);
RequestType request_type_;
@@ -211,7 +227,26 @@ class NET_EXPORT IsolationInfo {
// for non-opaque origins.
std::optional<base::UnguessableToken> nonce_;
- // Mojo serialization code needs to access internal fields.
+ // This will hold the list of distinct sites in the form of SchemefulSite to
+ // be used for First-Party-Sets check.
+ //
+ // For |request_type_| being either RequestType::kMainFrame or
+ // RequestType::kSubFrame, |party_context| holds the set of the sites
+ // of the frames in between the current frame and the top frame (i.e. not
+ // considering the current frame or the top frame).
+ //
+ // For |request_type_| being RequestType::kOther, |party_context_| holds the
+ // above, and also the site of the current frame.
+ //
+ // Note that if an intermediate frame shares a site with the top frame, that
+ // frame's site is not reflected in the |party_context_|. Also note that if an
+ // intermediate frame shares a site with the current frame, that frame's site
+ // is still included in the set. The top frame's site is excluded because it
+ // is redundant with the |top_frame_origin_| field. The current frame is
+ // excluded to make it easier to update on subframe redirects.
+ absl::optional<std::set<SchemefulSite>> party_context_;
+
+ // Mojo serialization code needs to access internal party_context_ field.
friend struct mojo::StructTraits<network::mojom::IsolationInfoDataView,
IsolationInfo>;
};
diff --git a/net/base/isolation_info.proto b/net/base/isolation_info.proto
index 249f7bf414d44c0345650f72ffdfe99414073a2a..9a769ab079c0f2d632fcc46527d5d1b9b7838fad 100644
--- a/net/base/isolation_info.proto
+++ b/net/base/isolation_info.proto
@@ -14,6 +14,6 @@ message IsolationInfo {
optional string frame_origin = 3;
optional string site_for_cookies = 4;
- reserved 5;
- reserved "party_context";
+ message PartyContext { repeated string site = 1; }
+ optional PartyContext party_context = 5;
}
\ No newline at end of file
diff --git a/net/base/network_delegate.cc b/net/base/network_delegate.cc
index 6aad5c02def9fd6556bb9e65b0b53e138f71481f..33b1a0010ddd63381bd46bd5f74c5c953d77f876 100644
--- a/net/base/network_delegate.cc
+++ b/net/base/network_delegate.cc
@@ -168,6 +168,16 @@ bool NetworkDelegate::CanUseReportingClient(const url::Origin& origin,
return OnCanUseReportingClient(origin, endpoint);
}
+absl::optional<FirstPartySetsCacheFilter::MatchInfo>
+NetworkDelegate::GetFirstPartySetsCacheFilterMatchInfoMaybeAsync(
+ const SchemefulSite& request_site,
+ base::OnceCallback<void(FirstPartySetsCacheFilter::MatchInfo)> callback)
+ const {
+ DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+ return OnGetFirstPartySetsCacheFilterMatchInfoMaybeAsync(request_site,
+ std::move(callback));
+}
+
// static
void NetworkDelegate::ExcludeAllCookies(
net::CookieInclusionStatus::ExclusionReason reason,
diff --git a/net/base/network_delegate.h b/net/base/network_delegate.h
index d1bd6c2cfcd02bb3cb030e82693c91013deebe99..145933ebd1fe6de0c9b3b05deedbeff7c4f1cccd 100644
--- a/net/base/network_delegate.h
+++ b/net/base/network_delegate.h
@@ -48,6 +48,7 @@ class CookieInclusionStatus;
class HttpRequestHeaders;
class HttpResponseHeaders;
class IPEndPoint;
+class SchemefulSite;
class URLRequest;
class NET_EXPORT NetworkDelegate {
@@ -121,6 +122,20 @@ class NET_EXPORT NetworkDelegate {
bool CanUseReportingClient(const url::Origin& origin,
const GURL& endpoint) const;
+ // Gets the First-Party Sets cache filter info, which is used to mark the
+ // cache and determine if the previously stored cache of `request_site` can be
+ // accessed.
+ //
+ // The result may be returned synchronously, or `callback` may be invoked
+ // asynchronously with the result. The callback will be invoked iff the return
+ // value is nullopt; i.e. a result will be provided via return value or
+ // callback, but not both, and not neither.
+ absl::optional<FirstPartySetsCacheFilter::MatchInfo>
+ GetFirstPartySetsCacheFilterMatchInfoMaybeAsync(
+ const SchemefulSite& request_site,
+ base::OnceCallback<void(FirstPartySetsCacheFilter::MatchInfo)> callback)
+ const;
+
protected:
// Adds the given ExclusionReason to all cookies in
// `mayble_included_cookies`, and moves the contents of
@@ -291,6 +306,12 @@ class NET_EXPORT NetworkDelegate {
virtual bool OnCanUseReportingClient(const url::Origin& origin,
const GURL& endpoint) const = 0;
+
+ virtual absl::optional<FirstPartySetsCacheFilter::MatchInfo>
+ OnGetFirstPartySetsCacheFilterMatchInfoMaybeAsync(
+ const SchemefulSite& request_site,
+ base::OnceCallback<void(FirstPartySetsCacheFilter::MatchInfo)> callback)
+ const = 0;
};
} // namespace net
diff --git a/net/base/network_delegate_impl.cc b/net/base/network_delegate_impl.cc
index d4c02a79b022afcce7dffc6f42940bb93df6957f..fc89f9d80d786511ecf04acf89b58100b254a282 100644
--- a/net/base/network_delegate_impl.cc
+++ b/net/base/network_delegate_impl.cc
@@ -98,4 +98,12 @@ bool NetworkDelegateImpl::OnCanUseReportingClient(const url::Origin& origin,
return true;
}
+absl::optional<FirstPartySetsCacheFilter::MatchInfo>
+NetworkDelegateImpl::OnGetFirstPartySetsCacheFilterMatchInfoMaybeAsync(
+ const SchemefulSite& request_site,
+ base::OnceCallback<void(FirstPartySetsCacheFilter::MatchInfo)> callback)
+ const {
+ return {FirstPartySetsCacheFilter::MatchInfo()};
+}
+
} // namespace net
diff --git a/net/base/network_delegate_impl.h b/net/base/network_delegate_impl.h
index 812f471436512a3e1f7ddef3d243b91b46a4288c..dd9ea88cf43f332668aec575ab135b9e3be55446 100644
--- a/net/base/network_delegate_impl.h
+++ b/net/base/network_delegate_impl.h
@@ -28,6 +28,7 @@ class Origin;
namespace net {
+class SchemefulSite;
class CookieOptions;
class CookieInclusionStatus;
class HttpRequestHeaders;
@@ -101,6 +102,12 @@ class NET_EXPORT NetworkDelegateImpl : public NetworkDelegate {
bool OnCanUseReportingClient(const url::Origin& origin,
const GURL& endpoint) const override;
+
+ absl::optional<FirstPartySetsCacheFilter::MatchInfo>
+ OnGetFirstPartySetsCacheFilterMatchInfoMaybeAsync(
+ const SchemefulSite& request_site,
+ base::OnceCallback<void(FirstPartySetsCacheFilter::MatchInfo)> callback)
+ const override;
};
} // namespace net
diff --git a/net/cookies/canonical_cookie.cc b/net/cookies/canonical_cookie.cc
index 02636104eae6d2223d637a11a89f92fcaf1497cc..1b76fecd6b10291ab027f61f38fdca2a9bacfcc8 100644
--- a/net/cookies/canonical_cookie.cc
+++ b/net/cookies/canonical_cookie.cc
@@ -364,9 +364,11 @@ void HistogramSessionCookieAge(const CanonicalCookie& cookie) {
} // namespace
CookieAccessParams::CookieAccessParams(CookieAccessSemantics access_semantics,
- bool delegate_treats_url_as_trustworthy)
+ bool delegate_treats_url_as_trustworthy,
+ CookieSamePartyStatus same_party_status)
: access_semantics(access_semantics),
- delegate_treats_url_as_trustworthy(delegate_treats_url_as_trustworthy) {}
+ delegate_treats_url_as_trustworthy(delegate_treats_url_as_trustworthy),
+ same_party_status(same_party_status) {}
CanonicalCookie::CanonicalCookie() = default;
@@ -393,6 +395,7 @@ CanonicalCookie::CanonicalCookie(
bool httponly,
CookieSameSite same_site,
CookiePriority priority,
+ bool same_party,
absl::optional<CookiePartitionKey> partition_key,
CookieSourceScheme source_scheme,
int source_port)
@@ -408,6 +411,7 @@ CanonicalCookie::CanonicalCookie(
httponly_(httponly),
same_site_(same_site),
priority_(priority),
+ same_party_(same_party),
partition_key_(std::move(partition_key)),
source_scheme_(source_scheme),
source_port_(source_port) {}
@@ -641,6 +645,12 @@ std::unique_ptr<CanonicalCookie> CanonicalCookie::Create(
status->AddExclusionReason(CookieInclusionStatus::EXCLUDE_INVALID_PREFIX);
}
+ bool is_same_party_valid = IsCookieSamePartyValid(parsed_cookie);
+ if (!is_same_party_valid) {
+ status->AddExclusionReason(
+ CookieInclusionStatus::EXCLUDE_INVALID_SAMEPARTY);
+ }
+
bool partition_has_nonce = CookiePartitionKey::HasNonce(cookie_partition_key);
bool is_partitioned_valid =
IsCookiePartitionedValid(url, parsed_cookie, partition_has_nonce);
@@ -715,7 +725,8 @@ std::unique_ptr<CanonicalCookie> CanonicalCookie::Create(
cookie_expires, creation_time,
/*last_update=*/base::Time::Now(), parsed_cookie.IsSecure(),
parsed_cookie.IsHttpOnly(), samesite, parsed_cookie.Priority(),
- cookie_partition_key, source_scheme, source_port);
+ parsed_cookie.IsSameParty(), cookie_partition_key, source_scheme,
+ source_port);
// TODO(chlily): Log metrics.
if (!cc->IsCanonical()) {
@@ -762,6 +773,7 @@ std::unique_ptr<CanonicalCookie> CanonicalCookie::CreateSanitizedCookie(
bool http_only,
CookieSameSite same_site,
CookiePriority priority,
+ bool same_party,
absl::optional<CookiePartitionKey> partition_key,
CookieInclusionStatus* status) {
// Put a pointer on the stack so the rest of the function can assign to it if
@@ -909,6 +921,10 @@ std::unique_ptr<CanonicalCookie> CanonicalCookie::CreateSanitizedCookie(
net::CookieInclusionStatus::EXCLUDE_INVALID_PREFIX);
}
+ if (!IsCookieSamePartyValid(same_party, secure, same_site)) {
+ status->AddExclusionReason(
+ net::CookieInclusionStatus::EXCLUDE_INVALID_SAMEPARTY);
+ }
if (!IsCookiePartitionedValid(url, secure,
/*is_partitioned=*/partition_key.has_value(),
/*partition_has_nonce=*/
@@ -931,7 +947,7 @@ std::unique_ptr<CanonicalCookie> CanonicalCookie::CreateSanitizedCookie(
base::PassKey<CanonicalCookie>(), name, value, cookie_domain,
encoded_cookie_path, creation_time, expiration_time, last_access_time,
/*last_update=*/base::Time::Now(), secure, http_only, same_site, priority,
- partition_key, source_scheme, source_port);
+ same_party, partition_key, source_scheme, source_port);
DCHECK(cc->IsCanonical());
return cc;
@@ -951,6 +967,7 @@ std::unique_ptr<CanonicalCookie> CanonicalCookie::FromStorage(
bool httponly,
CookieSameSite same_site,
CookiePriority priority,
+ bool same_party,
absl::optional<CookiePartitionKey> partition_key,
CookieSourceScheme source_scheme,
int source_port) {
@@ -965,8 +982,8 @@ std::unique_ptr<CanonicalCookie> CanonicalCookie::FromStorage(
auto cc = std::make_unique<CanonicalCookie>(
base::PassKey<CanonicalCookie>(), std::move(name), std::move(value),
std::move(domain), std::move(path), creation, expiration, last_access,
- last_update, secure, httponly, same_site, priority, partition_key,
- source_scheme, validated_port);
+ last_update, secure, httponly, same_site, priority, same_party,
+ partition_key, source_scheme, validated_port);
if (cc->IsCanonicalForFromStorage()) {
// This will help capture the number of times a cookie is canonical but does
@@ -996,13 +1013,14 @@ std::unique_ptr<CanonicalCookie> CanonicalCookie::CreateUnsafeCookieForTesting(
bool httponly,
CookieSameSite same_site,
CookiePriority priority,
+ bool same_party,
absl::optional<CookiePartitionKey> partition_key,
CookieSourceScheme source_scheme,
int source_port) {
return std::make_unique<CanonicalCookie>(
base::PassKey<CanonicalCookie>(), name, value, domain, path, creation,
expiration, last_access, last_update, secure, httponly, same_site,
- priority, partition_key, source_scheme, source_port);
+ priority, same_party, partition_key, source_scheme, source_port);
}
bool CanonicalCookie::IsFirstPartyPartitioned() const {
@@ -1252,10 +1270,56 @@ CookieAccessResult CanonicalCookie::IncludeForRequestURL(
CookieInclusionStatus::EXCLUDE_SAMESITE_NONE_INSECURE);
}
- ApplySameSiteCookieWarningToStatus(SameSite(), effective_same_site,
- IsSecure(),
- options.same_site_cookie_context(),
- &status, false /* is_cookie_being_set */);
+ switch (params.same_party_status) {
+ case CookieSamePartyStatus::kEnforceSamePartyExclude:
+ DCHECK(IsSameParty());
+ status.AddExclusionReason(
+ CookieInclusionStatus::EXCLUDE_SAMEPARTY_CROSS_PARTY_CONTEXT);
+ [[fallthrough]];
+ case CookieSamePartyStatus::kEnforceSamePartyInclude: {
+ status.AddWarningReason(CookieInclusionStatus::WARN_TREATED_AS_SAMEPARTY);
+ // Remove any SameSite exclusion reasons, since SameParty overrides
+ // SameSite.
+ DCHECK(!status.HasExclusionReason(
+ CookieInclusionStatus::EXCLUDE_SAMESITE_STRICT));
+ DCHECK_NE(effective_same_site, CookieEffectiveSameSite::STRICT_MODE);
+ bool included_by_samesite =
+ !status.HasExclusionReason(
+ CookieInclusionStatus::EXCLUDE_SAMESITE_LAX) &&
+ !status.HasExclusionReason(
+ CookieInclusionStatus::
+ EXCLUDE_SAMESITE_UNSPECIFIED_TREATED_AS_LAX);
+ if (!included_by_samesite) {
+ status.RemoveExclusionReasons({
+ CookieInclusionStatus::EXCLUDE_SAMESITE_LAX,
+ CookieInclusionStatus::EXCLUDE_SAMESITE_UNSPECIFIED_TREATED_AS_LAX,
+ });
+ }
+
+ // Update metrics.
+ if (status.HasOnlyExclusionReason(
+ CookieInclusionStatus::EXCLUDE_SAMEPARTY_CROSS_PARTY_CONTEXT) &&
+ included_by_samesite) {
+ status.AddWarningReason(
+ CookieInclusionStatus::WARN_SAMEPARTY_EXCLUSION_OVERRULED_SAMESITE);
+ }
+ if (status.IsInclude()) {
+ if (!included_by_samesite) {
+ status.AddWarningReason(
+ CookieInclusionStatus::
+ WARN_SAMEPARTY_INCLUSION_OVERRULED_SAMESITE);
+ }
+ }
+ break;
+ }
+ case CookieSamePartyStatus::kNoSamePartyEnforcement:
+ // Only apply SameSite-related warnings if SameParty is not in effect.
+ ApplySameSiteCookieWarningToStatus(
+ SameSite(), effective_same_site, IsSecure(),
+ options.same_site_cookie_context(), &status,
+ false /* is_cookie_being_set */);
+ break;
+ }
if (status.IsInclude()) {
UMA_HISTOGRAM_ENUMERATION("Cookie.IncludedRequestEffectiveSameSite",
@@ -1425,10 +1489,59 @@ CookieAccessResult CanonicalCookie::IsSetPermittedInContext(
break;
}
- ApplySameSiteCookieWarningToStatus(
- SameSite(), access_result.effective_same_site, IsSecure(),
- options.same_site_cookie_context(), &access_result.status,
- true /* is_cookie_being_set */);
+ switch (params.same_party_status) {
+ case CookieSamePartyStatus::kEnforceSamePartyExclude:
+ DCHECK(IsSameParty());
+ access_result.status.AddExclusionReason(
+ CookieInclusionStatus::EXCLUDE_SAMEPARTY_CROSS_PARTY_CONTEXT);
+ [[fallthrough]];
+ case CookieSamePartyStatus::kEnforceSamePartyInclude: {
+ DCHECK(IsSameParty());
+ access_result.status.AddWarningReason(
+ CookieInclusionStatus::WARN_TREATED_AS_SAMEPARTY);
+ // Remove any SameSite exclusion reasons, since SameParty overrides
+ // SameSite.
+ DCHECK(!access_result.status.HasExclusionReason(
+ CookieInclusionStatus::EXCLUDE_SAMESITE_STRICT));
+ DCHECK_NE(access_result.effective_same_site,
+ CookieEffectiveSameSite::STRICT_MODE);
+ bool included_by_samesite =
+ !access_result.status.HasExclusionReason(
+ CookieInclusionStatus::EXCLUDE_SAMESITE_LAX) &&
+ !access_result.status.HasExclusionReason(
+ CookieInclusionStatus::
+ EXCLUDE_SAMESITE_UNSPECIFIED_TREATED_AS_LAX);
+ if (!included_by_samesite) {
+ access_result.status.RemoveExclusionReasons({
+ CookieInclusionStatus::EXCLUDE_SAMESITE_LAX,
+ CookieInclusionStatus::EXCLUDE_SAMESITE_UNSPECIFIED_TREATED_AS_LAX,
+ });
+ }
+
+ // Update metrics.
+ if (access_result.status.HasOnlyExclusionReason(
+ CookieInclusionStatus::EXCLUDE_SAMEPARTY_CROSS_PARTY_CONTEXT) &&
+ included_by_samesite) {
+ access_result.status.AddWarningReason(
+ CookieInclusionStatus::WARN_SAMEPARTY_EXCLUSION_OVERRULED_SAMESITE);
+ }
+ if (access_result.status.IsInclude()) {
+ if (!included_by_samesite) {
+ access_result.status.AddWarningReason(
+ CookieInclusionStatus::
+ WARN_SAMEPARTY_INCLUSION_OVERRULED_SAMESITE);
+ }
+ }
+ break;
+ }
+ case CookieSamePartyStatus::kNoSamePartyEnforcement:
+ // Only apply SameSite-related warnings if SameParty is not in effect.
+ ApplySameSiteCookieWarningToStatus(
+ SameSite(), access_result.effective_same_site, IsSecure(),
+ options.same_site_cookie_context(), &access_result.status,
+ true /* is_cookie_being_set */);
+ break;
+ }
if (access_result.status.IsInclude()) {
UMA_HISTOGRAM_ENUMERATION("Cookie.IncludedResponseEffectiveSameSite",
@@ -1537,6 +1650,9 @@ bool CanonicalCookie::IsCanonicalForFromStorage() const {
if (name_ == "" && HasHiddenPrefixName(value_))
return false;
+ if (!IsCookieSamePartyValid(same_party_, secure_, same_site_))
+ return false;
+
if (IsPartitioned()) {
if (CookiePartitionKey::HasNonce(partition_key_))
return true;
@@ -1783,6 +1899,23 @@ bool CanonicalCookie::IsRecentlyCreated(base::TimeDelta age_threshold) const {
return (base::Time::Now() - creation_date_) <= age_threshold;
}
+// static
+bool CanonicalCookie::IsCookieSamePartyValid(
+ const ParsedCookie& parsed_cookie) {
+ return IsCookieSamePartyValid(parsed_cookie.IsSameParty(),
+ parsed_cookie.IsSecure(),
+ parsed_cookie.SameSite());
+}
+
+// static
+bool CanonicalCookie::IsCookieSamePartyValid(bool is_same_party,
+ bool is_secure,
+ CookieSameSite same_site) {
+ if (!is_same_party)
+ return true;
+ return is_secure && (same_site != CookieSameSite::STRICT_MODE);
+}
+
// static
bool CanonicalCookie::IsCookiePartitionedValid(
const GURL& url,
diff --git a/net/cookies/canonical_cookie.h b/net/cookies/canonical_cookie.h
index 599b17b834e6816f05f029014e2e53cb067a318d..272f70b3a25a2f93bd99b042db423591b03b0e54 100644
--- a/net/cookies/canonical_cookie.h
+++ b/net/cookies/canonical_cookie.h
@@ -43,7 +43,8 @@ using CookieAccessResultList = std::vector<CookieWithAccessResult>;
struct NET_EXPORT CookieAccessParams {
CookieAccessParams() = delete;
CookieAccessParams(CookieAccessSemantics access_semantics,
- bool delegate_treats_url_as_trustworthy);
+ bool delegate_treats_url_as_trustworthy,
+ CookieSamePartyStatus same_party_status);
// |access_semantics| is the access mode of the cookie access check.
CookieAccessSemantics access_semantics = CookieAccessSemantics::UNKNOWN;
@@ -51,6 +52,10 @@ struct NET_EXPORT CookieAccessParams {
// CookieAccessDelegate has authorized access to secure cookies from URLs
// which might not otherwise be able to do so.
bool delegate_treats_url_as_trustworthy = false;
+ // |same_party_status| indicates whether, and how, SameParty restrictions
+ // should be enforced.
+ CookieSamePartyStatus same_party_status =
+ CookieSamePartyStatus::kNoSamePartyEnforcement;
};
class NET_EXPORT CanonicalCookie {
@@ -107,6 +112,7 @@ class NET_EXPORT CanonicalCookie {
bool httponly,
CookieSameSite same_site,
CookiePriority priority,
+ bool same_party,
absl::optional<CookiePartitionKey> partition_key,
CookieSourceScheme scheme_secure = CookieSourceScheme::kUnset,
int source_port = url::PORT_UNSPECIFIED);
@@ -167,6 +173,7 @@ class NET_EXPORT CanonicalCookie {
bool http_only,
CookieSameSite same_site,
CookiePriority priority,
+ bool same_party,
absl::optional<CookiePartitionKey> partition_key,
CookieInclusionStatus* status = nullptr);
@@ -190,6 +197,7 @@ class NET_EXPORT CanonicalCookie {
bool httponly,
CookieSameSite same_site,
CookiePriority priority,
+ bool same_party,
absl::optional<CookiePartitionKey> partition_key,
CookieSourceScheme source_scheme,
int source_port);
@@ -209,6 +217,7 @@ class NET_EXPORT CanonicalCookie {
bool httponly,
CookieSameSite same_site,
CookiePriority priority,
+ bool same_party,
absl::optional<CookiePartitionKey> partition_key = absl::nullopt,
CookieSourceScheme scheme_secure = CookieSourceScheme::kUnset,
int source_port = url::PORT_UNSPECIFIED);
@@ -240,6 +249,7 @@ class NET_EXPORT CanonicalCookie {
bool IsHttpOnly() const { return httponly_; }
CookieSameSite SameSite() const { return same_site_; }
CookiePriority Priority() const { return priority_; }
+ bool IsSameParty() const { return same_party_; }
bool IsPartitioned() const { return partition_key_.has_value(); }
const absl::optional<CookiePartitionKey>& PartitionKey() const {
return partition_key_;
@@ -366,7 +376,7 @@ class NET_EXPORT CanonicalCookie {
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_ &&
+ 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_ &&
@@ -380,8 +390,9 @@ class NET_EXPORT CanonicalCookie {
auto f = [](const CanonicalCookie& c) {
return std::tie(c.creation_date_, c.last_access_date_, c.expiry_date_,
c.secure_, c.httponly_, c.same_site_, c.priority_,
- c.partition_key_, c.name_, c.value_, c.domain_, c.path_,
- c.last_update_date_, c.source_scheme_, c.source_port_);
+ c.same_party_, c.partition_key_, c.name_, c.value_,
+ c.domain_, c.path_, c.last_update_date_, c.source_scheme_,
+ c.source_port_);
};
return f(*this) < f(other);
}
@@ -603,6 +614,14 @@ class NET_EXPORT CanonicalCookie {
// Returns whether the cookie was created at most |age_threshold| ago.
bool IsRecentlyCreated(base::TimeDelta age_threshold) const;
+ // Returns true iff the cookie does not violate any rules associated with
+ // creating a cookie with the SameParty attribute. In particular, if a cookie
+ // has SameParty, then it must be Secure and must not be SameSite=Strict.
+ static bool IsCookieSamePartyValid(const ParsedCookie& parsed_cookie);
+ static bool IsCookieSamePartyValid(bool is_same_party,
+ bool is_secure,
+ CookieSameSite same_site);
+
// Returns true iff the cookie is a partitioned cookie with a nonce or that
// does not violate the semantics of the Partitioned attribute:
// - Must have the Secure attribute OR the cookie partition contains a nonce.
@@ -628,6 +647,7 @@ class NET_EXPORT CanonicalCookie {
bool httponly_{false};
CookieSameSite same_site_{CookieSameSite::NO_RESTRICTION};
CookiePriority priority_{COOKIE_PRIORITY_MEDIUM};
+ bool same_party_{false};
// This will be absl::nullopt for all cookies not set with the Partitioned
// attribute or without a nonce. If the value is non-null, then the cookie
// will only be delivered when the top-frame site matches the partition key
diff --git a/net/cookies/canonical_cookie_fuzzer.cc b/net/cookies/canonical_cookie_fuzzer.cc
index c2113af5acc47b720e13d9ddf605a1e223bcc420..0df06e5022a675de884ee35e69b5fcd17efa7ca7 100644
--- a/net/cookies/canonical_cookie_fuzzer.cc
+++ b/net/cookies/canonical_cookie_fuzzer.cc
@@ -66,7 +66,7 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
url, name, value, domain, path, creation, expiration, last_access,
data_provider.ConsumeBool() /* secure */,
data_provider.ConsumeBool() /* httponly */, same_site, priority,
- partition_key);
+ data_provider.ConsumeBool() /* same_party */, partition_key);
if (sanitized_cookie) {
CHECK(sanitized_cookie->IsCanonical());
diff --git a/net/cookies/cookie_access_delegate.cc b/net/cookies/cookie_access_delegate.cc
index 256dd856cce9fb482926f7a0c8bb80676a37e0e7..9811f944fff0a20a1a7b431e214f3578ed30785b 100644
--- a/net/cookies/cookie_access_delegate.cc
+++ b/net/cookies/cookie_access_delegate.cc
@@ -4,7 +4,13 @@
#include "net/cookies/cookie_access_delegate.h"
-class GURL;
+#include <set>
+
+#include "base/functional/callback.h"
+#include "net/base/schemeful_site.h"
+#include "net/cookies/cookie_partition_key.h"
+#include "net/first_party_sets/first_party_set_entry.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
namespace net {
diff --git a/net/cookies/cookie_access_delegate.h b/net/cookies/cookie_access_delegate.h
index 0eec2486fae7b793ee522954cd3c05a1047b2776..ee8e3d0fab27ef7f087194ad8fcfb6f17b042592 100644
--- a/net/cookies/cookie_access_delegate.h
+++ b/net/cookies/cookie_access_delegate.h
@@ -5,6 +5,8 @@
#ifndef NET_COOKIES_COOKIE_ACCESS_DELEGATE_H_
#define NET_COOKIES_COOKIE_ACCESS_DELEGATE_H_
+#include <set>
+
#include "base/containers/flat_map.h"
#include "base/containers/flat_set.h"
#include "base/functional/callback_forward.h"
@@ -15,7 +17,7 @@
#include "net/cookies/cookie_partition_key.h"
#include "net/first_party_sets/first_party_set_entry.h"
#include "net/first_party_sets/first_party_set_metadata.h"
-#include "net/first_party_sets/first_party_sets_cache_filter.h"
+#include "net/first_party_sets/same_party_context.h"
#include "third_party/abseil-cpp/absl/types/optional.h"
#include "url/gurl.h"
@@ -50,23 +52,21 @@ class NET_EXPORT CookieAccessDelegate {
const GURL& url,
const SiteForCookies& site_for_cookies) const = 0;
- // Calls `callback` with First-Party Sets metadata about `site` and
- // `top_frame_site`, and cache filter info for `site`. Cache filter info is
- // used to determine if the existing HTTP cache entries for `site` are allowed
- // to be accessed.
+ // Calls `callback` with metadata indicating whether `site` is same-party with
+ // `party_context` and `top_frame_site`; and `site`'s owner, if applicable..
+ // If `top_frame_site` is nullptr, then `site` will be checked only against
+ // `party_context`.
//
// This may return a result synchronously, or asynchronously invoke `callback`
// with the result. The callback will be invoked iff the return value is
// nullopt; i.e. a result will be provided via return value or callback, but
// not both, and not neither.
- [[nodiscard]] virtual absl::optional<
- std::pair<FirstPartySetMetadata, FirstPartySetsCacheFilter::MatchInfo>>
+ [[nodiscard]] virtual absl::optional<FirstPartySetMetadata>
ComputeFirstPartySetMetadataMaybeAsync(
const net::SchemefulSite& site,
const net::SchemefulSite* top_frame_site,
- base::OnceCallback<void(FirstPartySetMetadata,
- FirstPartySetsCacheFilter::MatchInfo)> callback)
- const = 0;
+ const std::set<net::SchemefulSite>& party_context,
+ base::OnceCallback<void(FirstPartySetMetadata)> callback) const = 0;
// Returns the entries of a set of sites if the sites are in non-trivial sets.
// If a given site is not in a non-trivial set, the output does not contain a
diff --git a/net/cookies/cookie_constants.h b/net/cookies/cookie_constants.h
index 33dd35c7cc3f4ce03fe359f33afc3f9a3b700476..9d8a2fa9e5255d4dc18fd1e7d5b8bc5e24728a95 100644
--- a/net/cookies/cookie_constants.h
+++ b/net/cookies/cookie_constants.h
@@ -118,6 +118,16 @@ enum class CookieAccessSemantics {
LEGACY,
};
+enum class CookieSamePartyStatus {
+ // Used when there should be no SameParty enforcement (either because the
+ // cookie is not marked SameParty, or the enforcement is irrelevant).
+ kNoSamePartyEnforcement = 0,
+ // Used when SameParty enforcement says to exclude the cookie.
+ kEnforceSamePartyExclude = 1,
+ // Used when SameParty enforcement says to include the cookie.
+ kEnforceSamePartyInclude = 2,
+};
+
// What scheme was used in the setting of a cookie.
// Do not renumber.
enum class CookieSourceScheme {
diff --git a/net/cookies/cookie_inclusion_status.cc b/net/cookies/cookie_inclusion_status.cc
index b6cf92b53dfc6d74933c2bbb2dc068a74cb70f5b..56b4796e11a4b4354d028bae30793f6631e81202 100644
--- a/net/cookies/cookie_inclusion_status.cc
+++ b/net/cookies/cookie_inclusion_status.cc
@@ -259,6 +259,9 @@ std::string CookieInclusionStatus::GetDebugString() const {
{EXCLUDE_DISALLOWED_CHARACTER, "EXCLUDE_DISALLOWED_CHARACTER"},
{EXCLUDE_THIRD_PARTY_PHASEOUT, "EXCLUDE_THIRD_PARTY_PHASEOUT"},
{EXCLUDE_NO_COOKIE_CONTENT, "EXCLUDE_NO_COOKIE_CONTENT"},
+ {EXCLUDE_SAMEPARTY_CROSS_PARTY_CONTEXT,
+ "EXCLUDE_SAMEPARTY_CROSS_PARTY_CONTEXT"},
+ {EXCLUDE_INVALID_SAMEPARTY, "EXCLUDE_INVALID_SAMEPARTY"},
};
static_assert(
std::size(exclusion_reasons) == ExclusionReason::NUM_EXCLUSION_REASONS,
@@ -308,6 +311,11 @@ std::string CookieInclusionStatus::GetDebugString() const {
"WARN_TENTATIVELY_ALLOWING_SECURE_SOURCE_SCHEME"},
{WARN_SHADOWING_DOMAIN, "WARN_SHADOWING_DOMAIN"},
{WARN_THIRD_PARTY_PHASEOUT, "WARN_THIRD_PARTY_PHASEOUT"},
+ {WARN_TREATED_AS_SAMEPARTY, "WARN_TREATED_AS_SAMEPARTY"},
+ {WARN_SAMEPARTY_EXCLUSION_OVERRULED_SAMESITE,
+ "WARN_SAMEPARTY_EXCLUSION_OVERRULED_SAMESITE"},
+ {WARN_SAMEPARTY_INCLUSION_OVERRULED_SAMESITE,
+ "WARN_SAMEPARTY_INCLUSION_OVERRULED_SAMESITE"},
};
static_assert(
std::size(warning_reasons) == WarningReason::NUM_WARNING_REASONS,
diff --git a/net/cookies/cookie_inclusion_status.h b/net/cookies/cookie_inclusion_status.h
index 98d8012e544a537da8f0bd51a03bcda230178d78..36b86cb7fcea4d18a86dd6ed1c62888de92d5d0d 100644
--- a/net/cookies/cookie_inclusion_status.h
+++ b/net/cookies/cookie_inclusion_status.h
@@ -108,6 +108,12 @@ class NET_EXPORT CookieInclusionStatus {
EXCLUDE_THIRD_PARTY_PHASEOUT = 25,
// Cookie contains no content or only whitespace.
EXCLUDE_NO_COOKIE_CONTENT = 26,
+ // The cookie specified SameParty, but was used in a cross-party context.
+ EXCLUDE_SAMEPARTY_CROSS_PARTY_CONTEXT = 27,
+ // Cookie was set with an invalid SameParty attribute in combination with
+ // other attributes. (SameParty is invalid if Secure is not present, or if
+ // SameSite=Strict is present.)
+ EXCLUDE_INVALID_SAMEPARTY = 28,
// This should be kept last.
NUM_EXCLUSION_REASONS
@@ -227,6 +233,24 @@ class NET_EXPORT CookieInclusionStatus {
// This cookie will be blocked for third-party cookie phaseout.
WARN_THIRD_PARTY_PHASEOUT = 16,
+ // The cookie was treated as SameParty. This is different from looking at
+ // whether the cookie has the SameParty attribute, since we may choose to
+ // ignore that attribute for one reason or another. E.g., we ignore the
+ // SameParty attribute if the site is not a member of a nontrivial
+ // First-Party Set.
+ WARN_TREATED_AS_SAMEPARTY = 17,
+
+ // The cookie was excluded solely for SameParty reasons (i.e. it was in
+ // cross-party context), but would have been included by SameSite. (This can
+ // only occur in cross-party, cross-site contexts, for cookies that are
+ // 'SameParty; SameSite=None'.)
+ WARN_SAMEPARTY_EXCLUSION_OVERRULED_SAMESITE = 18,
+
+ // The cookie was included due to SameParty, even though it would have been
+ // excluded by SameSite. (This can only occur in same-party, cross-site
+ // contexts, for cookies that are 'SameParty; SameSite=Lax'.)
+ WARN_SAMEPARTY_INCLUSION_OVERRULED_SAMESITE = 19,
+
// This should be kept last.
NUM_WARNING_REASONS
};
diff --git a/net/cookies/cookie_monster.cc b/net/cookies/cookie_monster.cc
index 643279afbb341f792832086c73ee2a231170ffa1..f88a61ac69e24d553ad6220a9ba3f76fde40cd41 100644
--- a/net/cookies/cookie_monster.cc
+++ b/net/cookies/cookie_monster.cc
@@ -377,7 +377,9 @@ CookieMonster::CookieMonster(scoped_refptr<PersistentCookieStore> store,
CookieMonster::CookieMonster(scoped_refptr<PersistentCookieStore> store,
base::TimeDelta last_access_threshold,
NetLog* net_log)
- : change_dispatcher_(this),
+ : same_party_attribute_enabled_(base::FeatureList::IsEnabled(
+ net::features::kSamePartyAttributeEnabled)),
+ change_dispatcher_(this, same_party_attribute_enabled_),
net_log_(NetLogWithSource::Make(net_log, NetLogSourceType::COOKIE_STORE)),
store_(std::move(store)),
last_access_threshold_(last_access_threshold),
@@ -739,9 +741,13 @@ bool CookieMonster::MatchCookieDeletionInfo(
delete_info.url.value());
}
+ // Deletion uses all inclusive options, so it's ok to get the
+ // `CookieSamePartyStatus` wrong here.
return delete_info.Matches(
- cookie, CookieAccessParams{GetAccessSemanticsForCookie(cookie),
- delegate_treats_url_as_trustworthy});
+ cookie,
+ CookieAccessParams{GetAccessSemanticsForCookie(cookie),
+ delegate_treats_url_as_trustworthy,
+ CookieSamePartyStatus::kNoSamePartyEnforcement});
}
void CookieMonster::DeleteCanonicalCookie(const CanonicalCookie& cookie,
@@ -1283,8 +1289,11 @@ void CookieMonster::FilterCookiesWithOptions(
// cookie |options|.
CookieAccessResult access_result = cookie_ptr->IncludeForRequestURL(
url, options,
- CookieAccessParams{GetAccessSemanticsForCookie(*cookie_ptr),
- delegate_treats_url_as_trustworthy});
+ CookieAccessParams{
+ GetAccessSemanticsForCookie(*cookie_ptr),
+ delegate_treats_url_as_trustworthy,
+ cookie_util::GetSamePartyStatus(*cookie_ptr, options,
+ same_party_attribute_enabled_)});
cookies_and_access_results.emplace_back(cookie_ptr, access_result);
// Record the names of all origin cookies that would be included if both
@@ -1629,7 +1638,9 @@ void CookieMonster::SetCanonicalCookie(
CookieAccessResult access_result = cc->IsSetPermittedInContext(
source_url, options,
CookieAccessParams(GetAccessSemanticsForCookie(*cc),
- delegate_treats_url_as_trustworthy),
+ delegate_treats_url_as_trustworthy,
+ cookie_util::GetSamePartyStatus(
+ *cc, options, same_party_attribute_enabled_)),
cookieable_schemes_, cookie_access_result);
const std::string key(GetKey(cc->Domain()));
diff --git a/net/cookies/cookie_monster.h b/net/cookies/cookie_monster.h
index 6871ed30edce4645a913d36c61a3e75ef84aae65..bf4a25a85bc51167d99897848959b1dbc117e941 100644
--- a/net/cookies/cookie_monster.h
+++ b/net/cookies/cookie_monster.h
@@ -746,6 +746,8 @@ class NET_EXPORT CookieMonster : public CookieStore {
// Cookie jar sizes per partition.
std::map<CookiePartitionKey, size_t> bytes_per_cookie_partition_;
+ bool same_party_attribute_enabled_ = false;
+
CookieMonsterChangeDispatcher change_dispatcher_;
// Indicates whether the cookie store has been initialized.
diff --git a/net/cookies/cookie_monster_change_dispatcher.cc b/net/cookies/cookie_monster_change_dispatcher.cc
index 61bd41e5a273e9d8d467deb587794600257feae6..34448cfee892b93558016c4fba22123c9522f6c4 100644
--- a/net/cookies/cookie_monster_change_dispatcher.cc
+++ b/net/cookies/cookie_monster_change_dispatcher.cc
@@ -37,6 +37,7 @@ CookieMonsterChangeDispatcher::Subscription::Subscription(
std::string name_key,
GURL url,
CookiePartitionKeyCollection cookie_partition_key_collection,
+ bool same_party_attribute_enabled,
net::CookieChangeCallback callback)
: change_dispatcher_(std::move(change_dispatcher)),
domain_key_(std::move(domain_key)),
@@ -45,6 +46,7 @@ CookieMonsterChangeDispatcher::Subscription::Subscription(
cookie_partition_key_collection_(
std::move(cookie_partition_key_collection)),
callback_(std::move(callback)),
+ same_party_attribute_enabled_(same_party_attribute_enabled),
task_runner_(base::SingleThreadTaskRunner::GetCurrentDefault()) {
DCHECK(url_.is_valid() || url_.is_empty());
DCHECK_EQ(url_.is_empty(), domain_key_ == kGlobalDomainKey);
@@ -73,11 +75,14 @@ void CookieMonsterChangeDispatcher::Subscription::DispatchChange(
cookie_access_delegate &&
cookie_access_delegate->ShouldTreatUrlAsTrustworthy(url_);
CookieOptions options = CookieOptions::MakeAllInclusive();
+ CookieSamePartyStatus same_party_status = cookie_util::GetSamePartyStatus(
+ cookie, options, same_party_attribute_enabled_);
if (!cookie
.IncludeForRequestURL(
url_, options,
CookieAccessParams{change.access_result.access_semantics,
- delegate_treats_url_as_trustworthy})
+ delegate_treats_url_as_trustworthy,
+ same_party_status})
.status.IsInclude()) {
return;
}
@@ -111,8 +116,10 @@ void CookieMonsterChangeDispatcher::Subscription::DoDispatchChange(
}
CookieMonsterChangeDispatcher::CookieMonsterChangeDispatcher(
- const CookieMonster* cookie_monster)
- : cookie_monster_(cookie_monster) {}
+ const CookieMonster* cookie_monster,
+ bool same_party_attribute_enabled)
+ : cookie_monster_(cookie_monster),
+ same_party_attribute_enabled_(same_party_attribute_enabled) {}
CookieMonsterChangeDispatcher::~CookieMonsterChangeDispatcher() {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
@@ -154,7 +161,7 @@ CookieMonsterChangeDispatcher::AddCallbackForCookie(
std::unique_ptr<Subscription> subscription = std::make_unique<Subscription>(
weak_ptr_factory_.GetWeakPtr(), DomainKey(url), NameKey(name), url,
CookiePartitionKeyCollection::FromOptional(cookie_partition_key),
- std::move(callback));
+ same_party_attribute_enabled_, std::move(callback));
LinkSubscription(subscription.get());
return subscription;
@@ -171,7 +178,7 @@ CookieMonsterChangeDispatcher::AddCallbackForUrl(
weak_ptr_factory_.GetWeakPtr(), DomainKey(url),
std::string(kGlobalNameKey), url,
CookiePartitionKeyCollection::FromOptional(cookie_partition_key),
- std::move(callback));
+ same_party_attribute_enabled_, std::move(callback));
LinkSubscription(subscription.get());
return subscription;
@@ -185,7 +192,8 @@ CookieMonsterChangeDispatcher::AddCallbackForAllChanges(
std::unique_ptr<Subscription> subscription = std::make_unique<Subscription>(
weak_ptr_factory_.GetWeakPtr(), std::string(kGlobalDomainKey),
std::string(kGlobalNameKey), GURL(""),
- CookiePartitionKeyCollection::ContainsAll(), std::move(callback));
+ CookiePartitionKeyCollection::ContainsAll(),
+ same_party_attribute_enabled_, std::move(callback));
LinkSubscription(subscription.get());
return subscription;
diff --git a/net/cookies/cookie_monster_change_dispatcher.h b/net/cookies/cookie_monster_change_dispatcher.h
index d6f449140b0244263b0c248e9163eb9940407541..31ca1e979ce1133dd6a78a7d8d09a6aba8f0ee44 100644
--- a/net/cookies/cookie_monster_change_dispatcher.h
+++ b/net/cookies/cookie_monster_change_dispatcher.h
@@ -33,7 +33,8 @@ class CookieMonsterChangeDispatcher : public CookieChangeDispatcher {
base::RepeatingCallbackList<void(const CookieChangeInfo&)>;
// Expects |cookie_monster| to outlive this.
- explicit CookieMonsterChangeDispatcher(const CookieMonster* cookie_monster);
+ CookieMonsterChangeDispatcher(const CookieMonster* cookie_monster,
+ bool same_party_attribute_enabled);
CookieMonsterChangeDispatcher(const CookieMonsterChangeDispatcher&) = delete;
CookieMonsterChangeDispatcher& operator=(
@@ -78,6 +79,7 @@ class CookieMonsterChangeDispatcher : public CookieChangeDispatcher {
std::string name_key,
GURL url,
CookiePartitionKeyCollection cookie_partition_key_collection,
+ bool same_party_attribute_enabled,
net::CookieChangeCallback callback);
Subscription(const Subscription&) = delete;
@@ -105,6 +107,7 @@ class CookieMonsterChangeDispatcher : public CookieChangeDispatcher {
const GURL url_; // empty() means no URL-based filtering.
const CookiePartitionKeyCollection cookie_partition_key_collection_;
const net::CookieChangeCallback callback_;
+ bool same_party_attribute_enabled_;
void DoDispatchChange(const CookieChangeInfo& change) const;
@@ -154,6 +157,8 @@ class CookieMonsterChangeDispatcher : public CookieChangeDispatcher {
CookieDomainMap cookie_domain_map_;
+ const bool same_party_attribute_enabled_;
+
THREAD_CHECKER(thread_checker_);
// Vends weak pointers to subscriptions.
diff --git a/net/cookies/cookie_options.cc b/net/cookies/cookie_options.cc
index d3e96410b39057e3a8d20078d69cbd728f7c8bde..42eb151094e5b26671ea576bf402f8a877e862a3 100644
--- a/net/cookies/cookie_options.cc
+++ b/net/cookies/cookie_options.cc
@@ -8,7 +8,9 @@
#include <tuple>
+#include "base/metrics/histogram_functions.h"
#include "net/cookies/cookie_util.h"
+#include "net/first_party_sets/same_party_context.h"
namespace net {
@@ -99,6 +101,8 @@ CookieOptions CookieOptions::MakeAllInclusive() {
options.set_include_httponly();
options.set_same_site_cookie_context(SameSiteCookieContext::MakeInclusive());
options.set_do_not_update_access_time();
+ options.set_same_party_context(SamePartyContext::MakeInclusive());
+ options.set_is_in_nontrivial_first_party_set(true);
return options;
}
diff --git a/net/cookies/cookie_options.h b/net/cookies/cookie_options.h
index b240265067e9ee72f8f15092a5fb63aed9c0d563..ff5e940d81ff18ef8d13e6b5721c68720ecd3030 100644
--- a/net/cookies/cookie_options.h
+++ b/net/cookies/cookie_options.h
@@ -10,8 +10,11 @@
#include <ostream>
#include <string>
-#include "base/check_op.h"
#include "net/base/net_export.h"
+#include "net/cookies/cookie_constants.h"
+#include "net/cookies/cookie_inclusion_status.h"
+#include "net/first_party_sets/same_party_context.h"
+#include "url/gurl.h"
namespace net {
@@ -264,6 +267,26 @@ class NET_EXPORT CookieOptions {
void unset_return_excluded_cookies() { return_excluded_cookies_ = false; }
bool return_excluded_cookies() const { return return_excluded_cookies_; }
+ void set_same_party_context(const SamePartyContext& context) {
+ same_party_context_ = context;
+ }
+ const SamePartyContext& same_party_context() const {
+ return same_party_context_;
+ }
+
+ // Getter/setter of |full_party_context_size_| for logging purposes.
+ void set_full_party_context_size(uint32_t len) {
+ full_party_context_size_ = len;
+ }
+ uint32_t full_party_context_size() const { return full_party_context_size_; }
+
+ void set_is_in_nontrivial_first_party_set(bool is_member) {
+ is_in_nontrivial_first_party_set_ = is_member;
+ }
+ bool is_in_nontrivial_first_party_set() const {
+ return is_in_nontrivial_first_party_set_;
+ }
+
// Convenience method for where you need a CookieOptions that will
// work for getting/setting all types of cookies, including HttpOnly and
// SameSite cookies. Also specifies not to update the access time, because
@@ -279,6 +302,19 @@ class NET_EXPORT CookieOptions {
SameSiteCookieContext same_site_cookie_context_;
bool update_access_time_ = true;
bool return_excluded_cookies_ = false;
+
+ SamePartyContext same_party_context_;
+
+ // The size of the isolation_info.party_context plus the top-frame site.
+ // Stored for logging purposes.
+ uint32_t full_party_context_size_ = 0;
+ // Whether the site requesting cookie access (as opposed to e.g. the
+ // `site_for_cookies`) is a member (or owner) of a nontrivial First-Party
+ // Set.
+ // This is included here temporarily, for the purpose of ignoring SameParty
+ // for sites that are not participating in the Origin Trial.
+ // TODO(https://crbug.com/1163990): remove this field.
+ bool is_in_nontrivial_first_party_set_ = false;
};
NET_EXPORT bool operator==(
diff --git a/net/cookies/cookie_util.cc b/net/cookies/cookie_util.cc
index db5099ac3dd70410a4bb266cd078a06b1818ab20..d02ec0664fedc634b17afd857645fdf59e31b21a 100644
--- a/net/cookies/cookie_util.cc
+++ b/net/cookies/cookie_util.cc
@@ -32,7 +32,7 @@
#include "net/cookies/cookie_monster.h"
#include "net/cookies/cookie_options.h"
#include "net/first_party_sets/first_party_set_metadata.h"
-#include "net/first_party_sets/first_party_sets_cache_filter.h"
+#include "net/first_party_sets/same_party_context.h"
#include "net/http/http_util.h"
#include "url/gurl.h"
#include "url/url_constants.h"
@@ -899,24 +899,23 @@ bool IsSchemefulSameSiteEnabled() {
return base::FeatureList::IsEnabled(features::kSchemefulSameSite);
}
-absl::optional<
- std::pair<FirstPartySetMetadata, FirstPartySetsCacheFilter::MatchInfo>>
-ComputeFirstPartySetMetadataMaybeAsync(
+absl::optional<FirstPartySetMetadata> ComputeFirstPartySetMetadataMaybeAsync(
const SchemefulSite& request_site,
const IsolationInfo& isolation_info,
const CookieAccessDelegate* cookie_access_delegate,
- base::OnceCallback<void(FirstPartySetMetadata,
- FirstPartySetsCacheFilter::MatchInfo)> callback) {
- if (cookie_access_delegate) {
+ bool force_ignore_top_frame_party,
+ base::OnceCallback<void(FirstPartySetMetadata)> callback) {
+ if (isolation_info.party_context().has_value() && cookie_access_delegate) {
return cookie_access_delegate->ComputeFirstPartySetMetadataMaybeAsync(
request_site,
- base::OptionalToPtr(
- isolation_info.network_isolation_key().GetTopFrameSite()),
- std::move(callback));
+ force_ignore_top_frame_party
+ ? nullptr
+ : base::OptionalToPtr(
+ isolation_info.network_isolation_key().GetTopFrameSite()),
+ isolation_info.party_context().value(), std::move(callback));
}
- return std::make_pair(FirstPartySetMetadata(),
- FirstPartySetsCacheFilter::MatchInfo());
+ return FirstPartySetMetadata();
}
CookieOptions::SameSiteCookieContext::ContextMetadata::HttpMethod
@@ -945,6 +944,23 @@ HttpMethodStringToEnum(const std::string& in) {
return HttpMethod::kUnknown;
}
+CookieSamePartyStatus GetSamePartyStatus(
+ const CanonicalCookie& cookie,
+ const CookieOptions& options,
+ const bool same_party_attribute_enabled) {
+ if (!same_party_attribute_enabled || !cookie.IsSameParty() ||
+ !options.is_in_nontrivial_first_party_set()) {
+ return CookieSamePartyStatus::kNoSamePartyEnforcement;
+ }
+
+ switch (options.same_party_context().context_type()) {
+ case SamePartyContext::Type::kCrossParty:
+ return CookieSamePartyStatus::kEnforceSamePartyExclude;
+ case SamePartyContext::Type::kSameParty:
+ return CookieSamePartyStatus::kEnforceSamePartyInclude;
+ };
+}
+
bool IsCookieAccessResultInclude(CookieAccessResult cookie_access_result) {
return cookie_access_result.status.IsInclude();
}
diff --git a/net/cookies/cookie_util.h b/net/cookies/cookie_util.h
index 096ddb1b3c0c6c73ee0f918d9124813814dc7abf..e170851338dd98015392cb3212824c70054028e5 100644
--- a/net/cookies/cookie_util.h
+++ b/net/cookies/cookie_util.h
@@ -17,7 +17,6 @@
#include "net/cookies/cookie_options.h"
#include "net/cookies/site_for_cookies.h"
#include "net/first_party_sets/first_party_set_metadata.h"
-#include "net/first_party_sets/first_party_sets_cache_filter.h"
#include "third_party/abseil-cpp/absl/types/optional.h"
#include "url/origin.h"
@@ -291,27 +290,37 @@ NET_EXPORT bool IsTimeLimitedInsecureCookiesEnabled();
// Returns whether the respective feature is enabled.
NET_EXPORT bool IsSchemefulSameSiteEnabled();
-// Computes the First-Party Sets metadata and cache match information.
-// `isolation_info` must be fully populated.
+// Computes the First-Party Sets metadata, determining which of the cookies for
+// `request_site` can be accessed. `isolation_info` must be fully populated. If
+// `force_ignore_top_frame_party` is true, the top frame from `isolation_info`
+// will be assumed to be same-party with `request_site`, regardless of what it
+// is.
//
// The result may be returned synchronously, or `callback` may be invoked
// asynchronously with the result. The callback will be invoked iff the return
// value is nullopt; i.e. a result will be provided via return value or
// callback, but not both, and not neither.
-[[nodiscard]] NET_EXPORT absl::optional<
- std::pair<FirstPartySetMetadata, FirstPartySetsCacheFilter::MatchInfo>>
+[[nodiscard]] NET_EXPORT absl::optional<FirstPartySetMetadata>
ComputeFirstPartySetMetadataMaybeAsync(
const SchemefulSite& request_site,
const IsolationInfo& isolation_info,
const CookieAccessDelegate* cookie_access_delegate,
- base::OnceCallback<void(FirstPartySetMetadata,
- FirstPartySetsCacheFilter::MatchInfo)> callback);
+ bool force_ignore_top_frame_party,
+ base::OnceCallback<void(FirstPartySetMetadata)> callback);
// Converts a string representing the http request method to its enum
// representation.
NET_EXPORT CookieOptions::SameSiteCookieContext::ContextMetadata::HttpMethod
HttpMethodStringToEnum(const std::string& in);
+// Get the SameParty inclusion status. If the cookie is not SameParty, returns
+// kNoSamePartyEnforcement; if the cookie is SameParty but does not have a
+// valid context, returns kEnforceSamePartyExclude.
+NET_EXPORT CookieSamePartyStatus
+GetSamePartyStatus(const CanonicalCookie& cookie,
+ const CookieOptions& options,
+ bool same_party_attribute_enabled);
+
// Takes a CookieAccessResult and returns a bool, returning true if the
// CookieInclusionStatus in CookieAccessResult was set to "include", else
// returning false.
diff --git a/net/cookies/parse_cookie_line_fuzzer.cc b/net/cookies/parse_cookie_line_fuzzer.cc
index ebc3fc7278667217816fd24993b3edc5579e2454..14097347b08ebf3e8d2ab41ae8ad59e94ef55ddf 100644
--- a/net/cookies/parse_cookie_line_fuzzer.cc
+++ b/net/cookies/parse_cookie_line_fuzzer.cc
@@ -80,6 +80,9 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
GetArbitraryAttributeValueString(&data_provider));
break;
case 11:
+ parsed_cookie.SetIsSameParty(data_provider.ConsumeBool());
+ break;
+ case 12:
parsed_cookie.SetIsPartitioned(data_provider.ConsumeBool());
break;
}
diff --git a/net/cookies/parsed_cookie.cc b/net/cookies/parsed_cookie.cc
index 24b36dd569a325e20354ce90d07035c2f112864c..8998acd3e25ac39013c835fb046f37e6d9fa007d 100644
--- a/net/cookies/parsed_cookie.cc
+++ b/net/cookies/parsed_cookie.cc
@@ -63,6 +63,7 @@ const char kSecureTokenName[] = "secure";
const char kHttpOnlyTokenName[] = "httponly";
const char kSameSiteTokenName[] = "samesite";
const char kPriorityTokenName[] = "priority";
+const char kSamePartyTokenName[] = "sameparty";
const char kPartitionedTokenName[] = "partitioned";
const char kTerminator[] = "\n\r\0";
@@ -270,6 +271,10 @@ bool ParsedCookie::SetPriority(const std::string& priority) {
return SetString(&priority_index_, kPriorityTokenName, priority);
}
+bool ParsedCookie::SetIsSameParty(bool is_same_party) {
+ return SetBool(&same_party_index_, kSamePartyTokenName, is_same_party);
+}
+
bool ParsedCookie::SetIsPartitioned(bool is_partitioned) {
return SetBool(&partitioned_index_, kPartitionedTokenName, is_partitioned);
}
@@ -285,6 +290,7 @@ std::string ParsedCookie::ToCookieLine() const {
// we need to consider whether the name component is a special token.
if (it == pairs_.begin() ||
(it->first != kSecureTokenName && it->first != kHttpOnlyTokenName &&
+ it->first != kSamePartyTokenName &&
it->first != kPartitionedTokenName)) {
out.append("=");
out.append(it->second);
@@ -668,6 +674,8 @@ void ParsedCookie::SetupAttributes() {
same_site_index_ = i;
} else if (pairs_[i].first == kPriorityTokenName) {
priority_index_ = i;
+ } else if (pairs_[i].first == kSamePartyTokenName) {
+ same_party_index_ = i;
} else if (pairs_[i].first == kPartitionedTokenName) {
partitioned_index_ = i;
} else {
@@ -741,10 +749,10 @@ void ParsedCookie::ClearAttributePair(size_t index) {
if (index == 0)
return;
- size_t* indexes[] = {
- &path_index_, &domain_index_, &expires_index_,
- &maxage_index_, &secure_index_, &httponly_index_,
- &same_site_index_, &priority_index_, &partitioned_index_};
+ size_t* indexes[] = {&path_index_, &domain_index_, &expires_index_,
+ &maxage_index_, &secure_index_, &httponly_index_,
+ &same_site_index_, &priority_index_, &same_party_index_,
+ &partitioned_index_};
for (size_t* attribute_index : indexes) {
if (*attribute_index == index)
*attribute_index = 0;
diff --git a/net/cookies/parsed_cookie.h b/net/cookies/parsed_cookie.h
index ed84c8fc4328b5c96b84b40d3a56974cecb64c42..3fe624444cdf28a180cffce920dd3fd8057e368c 100644
--- a/net/cookies/parsed_cookie.h
+++ b/net/cookies/parsed_cookie.h
@@ -85,6 +85,7 @@ class NET_EXPORT ParsedCookie {
CookieSameSite SameSite(
CookieSameSiteString* samesite_string = nullptr) const;
CookiePriority Priority() const;
+ bool IsSameParty() const { return same_party_index_ != 0; }
bool IsPartitioned() const { return partitioned_index_ != 0; }
bool HasInternalHtab() const { return internal_htab_; }
TruncatingCharacterInCookieStringType
@@ -115,6 +116,7 @@ class NET_EXPORT ParsedCookie {
bool SetIsHttpOnly(bool is_http_only);
bool SetSameSite(const std::string& same_site);
bool SetPriority(const std::string& priority);
+ bool SetIsSameParty(bool is_same_party);
bool SetIsPartitioned(bool is_partitioned);
// Returns the cookie description as it appears in a HTML response header.
@@ -211,6 +213,7 @@ class NET_EXPORT ParsedCookie {
size_t httponly_index_ = 0;
size_t same_site_index_ = 0;
size_t priority_index_ = 0;
+ size_t same_party_index_ = 0;
size_t partitioned_index_ = 0;
TruncatingCharacterInCookieStringType truncating_char_in_cookie_string_type_ =
TruncatingCharacterInCookieStringType::kTruncatingCharNone;
diff --git a/net/extras/sqlite/sqlite_persistent_cookie_store.cc b/net/extras/sqlite/sqlite_persistent_cookie_store.cc
index 0adc6857266535320dedbeee8881f50b14d213f1..4addebdf963f0b09561a7abd5c4d56b66fb4a415 100644
--- a/net/extras/sqlite/sqlite_persistent_cookie_store.cc
+++ b/net/extras/sqlite/sqlite_persistent_cookie_store.cc
@@ -991,6 +991,7 @@ bool SQLitePersistentCookieStore::Backend::MakeCookiesFromSQLStatement(
statement.ColumnInt(14))), // samesite
DBCookiePriorityToCookiePriority(static_cast<DBCookiePriority>(
statement.ColumnInt(12))), // priority
+ statement.ColumnBool(17), // is_same_party
std::move(cookie_partition_key), // top_frame_site_key
DBToCookieSourceScheme(statement.ColumnInt(15)), // source_scheme
statement.ColumnInt(16)); // source_port
@@ -1444,7 +1445,8 @@ void SQLitePersistentCookieStore::Backend::DoCommit() {
14, CookieSameSiteToDBCookieSameSite(po->cc().SameSite()));
add_statement.BindInt(15, static_cast<int>(po->cc().SourceScheme()));
add_statement.BindInt(16, po->cc().SourcePort());
- add_statement.BindTime(17, po->cc().LastUpdateDate());
+ add_statement.BindBool(17, po->cc().IsSameParty());
+ add_statement.BindTime(18, po->cc().LastUpdateDate());
if (!add_statement.Run()) {
DLOG(WARNING) << "Could not add a cookie to the DB.";
RecordCookieCommitProblem(COOKIE_COMMIT_PROBLEM_ADD);
diff --git a/net/first_party_sets/first_party_set_metadata.cc b/net/first_party_sets/first_party_set_metadata.cc
index ba6fc9679184fda286fe32f7dd963b7371f0d8d5..fb2303c5851d547150e7a47266a9a602e6ea9f91 100644
--- a/net/first_party_sets/first_party_set_metadata.cc
+++ b/net/first_party_sets/first_party_set_metadata.cc
@@ -13,9 +13,11 @@ namespace net {
FirstPartySetMetadata::FirstPartySetMetadata() = default;
FirstPartySetMetadata::FirstPartySetMetadata(
+ const SamePartyContext& context,
const FirstPartySetEntry* frame_entry,
const FirstPartySetEntry* top_frame_entry)
- : frame_entry_(base::OptionalFromPtr(frame_entry)),
+ : context_(context),
+ frame_entry_(base::OptionalFromPtr(frame_entry)),
top_frame_entry_(base::OptionalFromPtr(top_frame_entry)) {}
FirstPartySetMetadata::FirstPartySetMetadata(FirstPartySetMetadata&&) = default;
@@ -25,14 +27,18 @@ FirstPartySetMetadata& FirstPartySetMetadata::operator=(
FirstPartySetMetadata::~FirstPartySetMetadata() = default;
bool FirstPartySetMetadata::operator==(
- const FirstPartySetMetadata& other) const = default;
+ const FirstPartySetMetadata& other) const {
+ return std::tie(context_, frame_entry_, top_frame_entry_) ==
+ std::tie(other.context_, other.frame_entry_, other.top_frame_entry_);
+}
bool FirstPartySetMetadata::operator!=(
const FirstPartySetMetadata& other) const = default;
std::ostream& operator<<(std::ostream& os,
const FirstPartySetMetadata& metadata) {
- os << "{" << base::OptionalToPtr(metadata.frame_entry()) << ", "
+ os << "{" << metadata.context() << ", "
+ << base::OptionalToPtr(metadata.frame_entry()) << ", "
<< base::OptionalToPtr(metadata.top_frame_entry()) << "}";
return os;
}
diff --git a/net/first_party_sets/first_party_set_metadata.h b/net/first_party_sets/first_party_set_metadata.h
index b23e52575bc2ae0634075890782eb46716116217..77cf13c75599522be3efda658b8685b21532c41c 100644
--- a/net/first_party_sets/first_party_set_metadata.h
+++ b/net/first_party_sets/first_party_set_metadata.h
@@ -7,6 +7,7 @@
#include "net/base/net_export.h"
#include "net/first_party_sets/first_party_set_entry.h"
+#include "net/first_party_sets/same_party_context.h"
#include "third_party/abseil-cpp/absl/types/optional.h"
namespace net {
@@ -20,7 +21,8 @@ class NET_EXPORT FirstPartySetMetadata {
// `frame_entry` and `top_frame_entry` must live for the duration of the ctor;
// nullptr indicates that there's no First-Party Set that's associated with
// the current frame or the top frame, respectively, in the given context.
- FirstPartySetMetadata(const FirstPartySetEntry* frame_entry,
+ FirstPartySetMetadata(const SamePartyContext& context,
+ const FirstPartySetEntry* frame_entry,
const FirstPartySetEntry* top_frame_entry);
FirstPartySetMetadata(FirstPartySetMetadata&&);
@@ -31,6 +33,10 @@ class NET_EXPORT FirstPartySetMetadata {
bool operator==(const FirstPartySetMetadata& other) const;
bool operator!=(const FirstPartySetMetadata& other) const;
+ const SamePartyContext& context() const { return context_; }
+
+ // Returns a optional<T>& instead of a T* so that operator== can be defined
+ // more easily.
const absl::optional<FirstPartySetEntry>& frame_entry() const {
return frame_entry_;
}
@@ -43,6 +49,7 @@ class NET_EXPORT FirstPartySetMetadata {
bool AreSitesInSameFirstPartySet() const;
private:
+ SamePartyContext context_ = SamePartyContext();
absl::optional<FirstPartySetEntry> frame_entry_ = absl::nullopt;
absl::optional<FirstPartySetEntry> top_frame_entry_ = absl::nullopt;
};
diff --git a/net/first_party_sets/global_first_party_sets.cc b/net/first_party_sets/global_first_party_sets.cc
index c916b0f4e106afdef92b3cd4d95cff3edf4d5594..23cf2755f9c77bf5ddb3d6ff59650ecb9614ecfc 100644
--- a/net/first_party_sets/global_first_party_sets.cc
+++ b/net/first_party_sets/global_first_party_sets.cc
@@ -29,6 +29,13 @@ namespace {
using FlattenedSets = base::flat_map<SchemefulSite, FirstPartySetEntry>;
using SingleSet = base::flat_map<SchemefulSite, FirstPartySetEntry>;
+// Converts WS to HTTP, and WSS to HTTPS.
+SchemefulSite NormalizeScheme(const SchemefulSite& site) {
+ SchemefulSite normalized_site = site;
+ normalized_site.ConvertWebSocketToHttp();
+ return normalized_site;
+}
+
// Converts a list of First-Party Sets from a SingleSet to a FlattenedSet
// representation.
FlattenedSets SetListToFlattenedSets(const std::vector<SingleSet>& set_list) {
@@ -60,6 +67,11 @@ const SchemefulSite& ProjectKey(
return p.first;
}
+SamePartyContext::Type ContextTypeFromBool(bool is_same_party) {
+ return is_same_party ? SamePartyContext::Type::kSameParty
+ : SamePartyContext::Type::kCrossParty;
+}
+
} // namespace
GlobalFirstPartySets::GlobalFirstPartySets() = default;
@@ -127,9 +139,11 @@ absl::optional<FirstPartySetEntry> GlobalFirstPartySets::FindEntry(
absl::optional<FirstPartySetEntry> GlobalFirstPartySets::FindEntry(
const SchemefulSite& site,
const FirstPartySetsContextConfig* config) const {
- // Check if `site` can be found in the customizations first.
+ const SchemefulSite normalized_site = NormalizeScheme(site);
+
+ // Check if `normalized_site` can be found in the customizations first.
if (config) {
- if (const auto override = config->FindOverride(site);
+ if (const auto override = config->FindOverride(normalized_site);
override.has_value()) {
return override->IsDeletion() ? absl::nullopt
: absl::make_optional(override->GetEntry());
@@ -137,7 +151,7 @@ absl::optional<FirstPartySetEntry> GlobalFirstPartySets::FindEntry(
}
// Now see if it's in the manual config (with or without a manual alias).
- if (const auto manual_override = manual_config_.FindOverride(site);
+ if (const auto manual_override = manual_config_.FindOverride(normalized_site);
manual_override.has_value()) {
return manual_override->IsDeletion()
? absl::nullopt
@@ -145,9 +159,9 @@ absl::optional<FirstPartySetEntry> GlobalFirstPartySets::FindEntry(
}
// Finally, look up in `entries_`, applying an alias if applicable.
- const auto canonical_it = aliases_.find(site);
+ const auto canonical_it = aliases_.find(normalized_site);
const SchemefulSite& canonical_site =
- canonical_it == aliases_.end() ? site : canonical_it->second;
+ canonical_it == aliases_.end() ? normalized_site : canonical_it->second;
if (const auto entry_it = entries_.find(canonical_site);
entry_it != entries_.end()) {
return entry_it->second;
@@ -173,16 +187,48 @@ GlobalFirstPartySets::FindEntries(
FirstPartySetMetadata GlobalFirstPartySets::ComputeMetadata(
const SchemefulSite& site,
const SchemefulSite* top_frame_site,
+ const std::set<net::SchemefulSite>& party_context,
const FirstPartySetsContextConfig& fps_context_config) const {
+ SamePartyContext::Type context_type =
+ ContextTypeFromBool(IsContextSamePartyWithSite(
+ site, top_frame_site, party_context, fps_context_config));
+
+ SamePartyContext context(context_type);
+
absl::optional<FirstPartySetEntry> top_frame_entry =
top_frame_site ? FindEntry(*top_frame_site, fps_context_config)
: absl::nullopt;
return FirstPartySetMetadata(
- base::OptionalToPtr(FindEntry(site, fps_context_config)),
+ context, base::OptionalToPtr(FindEntry(site, fps_context_config)),
base::OptionalToPtr(top_frame_entry));
}
+bool GlobalFirstPartySets::IsContextSamePartyWithSite(
+ const SchemefulSite& site,
+ const SchemefulSite* top_frame_site,
+ const std::set<net::SchemefulSite>& party_context,
+ const FirstPartySetsContextConfig& fps_context_config) const {
+ const absl::optional<FirstPartySetEntry> site_entry =
+ FindEntry(site, fps_context_config);
+ if (!site_entry.has_value())
+ return false;
+
+ const auto is_in_same_set_as_frame_site =
+ [this, &site_entry,
+ &fps_context_config](const SchemefulSite& context_site) -> bool {
+ const absl::optional<FirstPartySetEntry> context_entry =
+ FindEntry(context_site, fps_context_config);
+ return context_entry.has_value() &&
+ context_entry->primary() == site_entry->primary();
+ };
+
+ if (top_frame_site && !is_in_same_set_as_frame_site(*top_frame_site))
+ return false;
+
+ return base::ranges::all_of(party_context, is_in_same_set_as_frame_site);
+}
+
void GlobalFirstPartySets::ApplyManuallySpecifiedSet(
const LocalSetDeclaration& local_set_declaration) {
CHECK(manual_config_.empty());
diff --git a/net/first_party_sets/global_first_party_sets.h b/net/first_party_sets/global_first_party_sets.h
index d87aaec1747738a29dc03cdfcb9266c48ef8b699..d4cfd163cae7f2bd66249afcc57b55c748704126 100644
--- a/net/first_party_sets/global_first_party_sets.h
+++ b/net/first_party_sets/global_first_party_sets.h
@@ -5,6 +5,8 @@
#ifndef NET_FIRST_PARTY_SETS_GLOBAL_FIRST_PARTY_SETS_H_
#define NET_FIRST_PARTY_SETS_GLOBAL_FIRST_PARTY_SETS_H_
+#include <set>
+
#include "base/containers/flat_map.h"
#include "base/containers/flat_set.h"
#include "base/functional/function_ref.h"
@@ -76,6 +78,7 @@ class NET_EXPORT GlobalFirstPartySets {
FirstPartySetMetadata ComputeMetadata(
const SchemefulSite& site,
const SchemefulSite* top_frame_site,
+ const std::set<net::SchemefulSite>& party_context,
const FirstPartySetsContextConfig& fps_context_config) const;
// Modifies this instance such that it will respect the given
@@ -159,6 +162,18 @@ class NET_EXPORT GlobalFirstPartySets {
const std::vector<base::flat_map<SchemefulSite, FirstPartySetEntry>>&
addition_sets) const;
+ // Returns whether `site` is same-party with `party_context`, and
+ // `top_frame_site` (if it is not nullptr). That is, is `site`'s primary the
+ // same as the primaries of every member of `party_context` and of
+ // `top_frame_site`? Note: if `site` is not a member of a First-Party Set,
+ // then this returns false. If `top_frame_site` is nullptr, then it is
+ // ignored.
+ bool IsContextSamePartyWithSite(
+ const SchemefulSite& site,
+ const SchemefulSite* top_frame_site,
+ const std::set<net::SchemefulSite>& party_context,
+ const FirstPartySetsContextConfig& fps_context_config) const;
+
// Same as the public version of ForEachEffectiveSetEntry, but is allowed to
// omit the `config` argument (i.e. pass nullptr instead of a reference).
bool ForEachEffectiveSetEntry(
diff --git a/net/first_party_sets/same_party_context.cc b/net/first_party_sets/same_party_context.cc
new file mode 100644
index 0000000000000000000000000000000000000000..afa9ddf645cc8776d7c9303da9d93725b945a5d7
--- /dev/null
+++ b/net/first_party_sets/same_party_context.cc
@@ -0,0 +1,28 @@
+// Copyright 2021 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/first_party_sets/same_party_context.h"
+
+#include <ostream>
+
+namespace net {
+
+SamePartyContext::SamePartyContext(Type context_type)
+ : context_type_(context_type) {}
+
+bool SamePartyContext::operator==(const SamePartyContext& other) const {
+ return context_type_ == other.context_type_;
+}
+
+std::ostream& operator<<(std::ostream& os, const SamePartyContext& spc) {
+ os << "{" << static_cast<int>(spc.context_type()) << "}";
+ return os;
+}
+
+// static
+SamePartyContext SamePartyContext::MakeInclusive() {
+ return SamePartyContext(Type::kSameParty);
+}
+
+} // namespace net
diff --git a/net/first_party_sets/same_party_context.h b/net/first_party_sets/same_party_context.h
new file mode 100644
index 0000000000000000000000000000000000000000..448f000d8ab368f071d73da887e0e857c33e8973
--- /dev/null
+++ b/net/first_party_sets/same_party_context.h
@@ -0,0 +1,51 @@
+// Copyright 2021 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef NET_FIRST_PARTY_SETS_SAME_PARTY_CONTEXT_H_
+#define NET_FIRST_PARTY_SETS_SAME_PARTY_CONTEXT_H_
+
+#include <ostream>
+
+#include "net/base/net_export.h"
+
+namespace net {
+
+// This struct bundles together a few different notions of same-party-ness.
+// `context_type()` gives the notion of same-party-ness that Chromium should use
+// in all cases except metrics; other accessors are just for metrics purposes,
+// to explore the impact of different definitions of "same-party".
+class NET_EXPORT SamePartyContext {
+ public:
+ // Computed for every cookie access attempt but is only relevant for SameParty
+ // cookies.
+ enum class Type {
+ // The opposite to kSameParty. Should be the default value.
+ kCrossParty = 0,
+ // If the request URL is in the same First-Party Sets as the top-frame site
+ // and each member of the isolation_info.party_context.
+ kSameParty = 1,
+ };
+
+ SamePartyContext() = default;
+ explicit SamePartyContext(Type context_type);
+
+ bool operator==(const SamePartyContext& other) const;
+
+ // How trusted is the current browser environment when it comes to accessing
+ // SameParty cookies. Default is not trusted, e.g. kCrossParty.
+ Type context_type() const { return context_type_; }
+
+ // Creates a SamePartyContext that is as permissive as possible.
+ static SamePartyContext MakeInclusive();
+
+ private:
+ Type context_type_ = Type::kCrossParty;
+};
+
+NET_EXPORT std::ostream& operator<<(std::ostream& os,
+ const SamePartyContext& spc);
+
+} // namespace net
+
+#endif // NET_FIRST_PARTY_SETS_SAME_PARTY_CONTEXT_H_
diff --git a/net/url_request/url_request.cc b/net/url_request/url_request.cc
index 1bd70e772aaba0e04a9b82140109b8d1e511db15..7f8d7a6ab265c2f0d1144429a5f3c6f08c7c38e3 100644
--- a/net/url_request/url_request.cc
+++ b/net/url_request/url_request.cc
@@ -1212,7 +1212,7 @@ IsolationInfo URLRequest::CreateIsolationInfoFromNetworkAnonymizationKey(
auto isolation_info = IsolationInfo::Create(
IsolationInfo::RequestType::kOther, top_frame_origin,
frame_origin.value(), SiteForCookies(),
- network_anonymization_key.GetNonce());
+ /*party_context=*/absl::nullopt, network_anonymization_key.GetNonce());
// TODO(crbug/1343856): DCHECK isolation info is fully populated.
return isolation_info;
}
diff --git a/net/url_request/url_request.h b/net/url_request/url_request.h
index ce7074e0474ec80fc105d9668201c95d9dc020d2..ba2991b94361773b63803678a518b6f4e63c67ad 100644
--- a/net/url_request/url_request.h
+++ b/net/url_request/url_request.h
@@ -315,6 +315,16 @@ class NET_EXPORT URLRequest : public base::SupportsUserData {
force_ignore_site_for_cookies_ = attach;
}
+ // Indicates whether the top frame party will be considered same-party to the
+ // request URL (regardless of what it is), for the purpose of SameParty
+ // cookies.
+ bool force_ignore_top_frame_party_for_cookies() const {
+ return force_ignore_top_frame_party_for_cookies_;
+ }
+ void set_force_ignore_top_frame_party_for_cookies(bool force) {
+ force_ignore_top_frame_party_for_cookies_ = force;
+ }
+
// Indicates if the request should be treated as a main frame navigation for
// SameSite cookie computations. This flag overrides the IsolationInfo
// request type associated with fetches from a service worker context.
@@ -971,6 +981,7 @@ class NET_EXPORT URLRequest : public base::SupportsUserData {
absl::optional<CookiePartitionKey> cookie_partition_key_ = absl::nullopt;
bool force_ignore_site_for_cookies_ = false;
+ bool force_ignore_top_frame_party_for_cookies_ = false;
bool force_main_frame_for_same_site_cookies_ = false;
CookieSettingOverrides cookie_setting_overrides_;
diff --git a/net/url_request/url_request_http_job.cc b/net/url_request/url_request_http_job.cc
index 3afc5526490ed43cd57636a3174a8b2166adc868..3a8302b4e57d8d621a7df0c22474d9cac6407491 100644
--- a/net/url_request/url_request_http_job.cc
+++ b/net/url_request/url_request_http_job.cc
@@ -63,7 +63,7 @@
#include "net/filter/zstd_source_stream.h"
#include "net/first_party_sets/first_party_set_entry.h"
#include "net/first_party_sets/first_party_set_metadata.h"
-#include "net/first_party_sets/first_party_sets_cache_filter.h"
+#include "net/first_party_sets/same_party_context.h"
#include "net/http/http_content_disposition.h"
#include "net/http/http_log_util.h"
#include "net/http/http_network_session.h"
@@ -180,11 +180,22 @@ void LogTrustAnchor(const net::HashValueVector& spki_hashes) {
}
net::CookieOptions CreateCookieOptions(
- net::CookieOptions::SameSiteCookieContext same_site_context) {
+ net::CookieOptions::SameSiteCookieContext same_site_context,
+ const net::SamePartyContext& same_party_context,
+ const net::IsolationInfo& isolation_info,
+ bool is_in_nontrivial_first_party_set) {
net::CookieOptions options;
options.set_return_excluded_cookies();
options.set_include_httponly();
options.set_same_site_cookie_context(same_site_context);
+ options.set_same_party_context(same_party_context);
+ if (isolation_info.party_context().has_value()) {
+ // Count the top-frame site since it's not in the party_context.
+ options.set_full_party_context_size(isolation_info.party_context()->size() +
+ 1);
+ }
+ options.set_is_in_nontrivial_first_party_set(
+ is_in_nontrivial_first_party_set);
return options;
}
@@ -380,33 +391,53 @@ void URLRequestHttpJob::Start() {
IsSameSiteIgnoringWebSocketProtocol(request_initiator_site().value(),
request()->url()));
+ bool should_add_cookie_header = ShouldAddCookieHeader();
UMA_HISTOGRAM_BOOLEAN("Net.HttpJob.CanIncludeCookies",
- ShouldAddCookieHeader());
-
- CookieStore* cookie_store = request()->context()->cookie_store();
- const CookieAccessDelegate* delegate =
- cookie_store ? cookie_store->cookie_access_delegate() : nullptr;
+ should_add_cookie_header);
request_->net_log().BeginEvent(NetLogEventType::FIRST_PARTY_SETS_METADATA);
- absl::optional<
- std::pair<FirstPartySetMetadata, FirstPartySetsCacheFilter::MatchInfo>>
- maybe_metadata = cookie_util::ComputeFirstPartySetMetadataMaybeAsync(
+ if (!should_add_cookie_header) {
+ OnGotFirstPartySetMetadata(FirstPartySetMetadata());
+ return;
+ }
+
+ absl::optional<FirstPartySetMetadata> metadata =
+ cookie_util::ComputeFirstPartySetMetadataMaybeAsync(
SchemefulSite(request()->url()), request()->isolation_info(),
- delegate,
+ request()->context()->cookie_store()->cookie_access_delegate(),
+ request()->force_ignore_top_frame_party_for_cookies(),
base::BindOnce(&URLRequestHttpJob::OnGotFirstPartySetMetadata,
weak_factory_.GetWeakPtr()));
- if (maybe_metadata.has_value()) {
- auto [metadata, match_info] = std::move(maybe_metadata).value();
- OnGotFirstPartySetMetadata(std::move(metadata), std::move(match_info));
- }
+ if (metadata.has_value())
+ OnGotFirstPartySetMetadata(std::move(metadata.value()));
}
void URLRequestHttpJob::OnGotFirstPartySetMetadata(
- FirstPartySetMetadata first_party_set_metadata,
- FirstPartySetsCacheFilter::MatchInfo match_info) {
+ FirstPartySetMetadata first_party_set_metadata) {
first_party_set_metadata_ = std::move(first_party_set_metadata);
+
+ if (!request()->network_delegate()) {
+ OnGotFirstPartySetCacheFilterMatchInfo(
+ net::FirstPartySetsCacheFilter::MatchInfo());
+ return;
+ }
+ absl::optional<FirstPartySetsCacheFilter::MatchInfo> match_info =
+ request()
+ ->network_delegate()
+ ->GetFirstPartySetsCacheFilterMatchInfoMaybeAsync(
+ SchemefulSite(request()->url()),
+ base::BindOnce(
+ &URLRequestHttpJob::OnGotFirstPartySetCacheFilterMatchInfo,
+ weak_factory_.GetWeakPtr()));
+
+ if (match_info.has_value())
+ OnGotFirstPartySetCacheFilterMatchInfo(std::move(match_info.value()));
+}
+
+void URLRequestHttpJob::OnGotFirstPartySetCacheFilterMatchInfo(
+ FirstPartySetsCacheFilter::MatchInfo match_info) {
request_info_.fps_cache_filter = match_info.clear_at_run_id;
request_info_.browser_run_id = match_info.browser_run_id;
@@ -733,7 +764,11 @@ void URLRequestHttpJob::AddCookieHeaderAndStart() {
request_->site_for_cookies(), request_->initiator(),
is_main_frame_navigation, force_ignore_site_for_cookies);
- CookieOptions options = CreateCookieOptions(same_site_context);
+ bool is_in_nontrivial_first_party_set =
+ first_party_set_metadata_.frame_entry().has_value();
+ CookieOptions options = CreateCookieOptions(
+ same_site_context, first_party_set_metadata_.context(),
+ request_->isolation_info(), is_in_nontrivial_first_party_set);
cookie_store->GetCookieListWithOptionsAsync(
request_->url(), options,
@@ -963,7 +998,11 @@ void URLRequestHttpJob::SaveCookiesAndNotifyHeadersComplete(int result) {
request_->initiator(), is_main_frame_navigation,
force_ignore_site_for_cookies);
- CookieOptions options = CreateCookieOptions(same_site_context);
+ bool is_in_nontrivial_first_party_set =
+ first_party_set_metadata_.frame_entry().has_value();
+ CookieOptions options = CreateCookieOptions(
+ same_site_context, first_party_set_metadata_.context(),
+ request_->isolation_info(), is_in_nontrivial_first_party_set);
// Set all cookies, without waiting for them to be set. Any subsequent
// read will see the combined result of all cookie operation.
diff --git a/net/url_request/url_request_http_job.h b/net/url_request/url_request_http_job.h
index 943ed021a633e7d9ad33487108f4fd55c33805d0..433d317ba37c6bfb9ce1847f06cc9ff26e05bc82 100644
--- a/net/url_request/url_request_http_job.h
+++ b/net/url_request/url_request_http_job.h
@@ -215,7 +215,11 @@ class NET_EXPORT_PRIVATE URLRequestHttpJob : public URLRequestJob {
// Called after getting the FirstPartySetMetadata during Start for this job.
void OnGotFirstPartySetMetadata(
- FirstPartySetMetadata first_party_set_metadata,
+ FirstPartySetMetadata first_party_set_metadata);
+
+ // Called after getting the FirstPartySetsCacheFilter match info during Start
+ // for this job.
+ void OnGotFirstPartySetCacheFilterMatchInfo(
FirstPartySetsCacheFilter::MatchInfo match_info);
// Returns true iff this request leg should include the Cookie header. Note
diff --git a/services/network/cookie_access_delegate_impl.cc b/services/network/cookie_access_delegate_impl.cc
index 5e0d27953bf8d8770ad72559183ea5f56258cfa0..9d8c6c16d97fb1f4e7393656d40907b457d89818 100644
--- a/services/network/cookie_access_delegate_impl.cc
+++ b/services/network/cookie_access_delegate_impl.cc
@@ -13,7 +13,6 @@
#include "net/cookies/cookie_constants.h"
#include "net/cookies/cookie_util.h"
#include "net/first_party_sets/first_party_set_metadata.h"
-#include "net/first_party_sets/first_party_sets_cache_filter.h"
#include "services/network/public/cpp/is_potentially_trustworthy.h"
#include "third_party/abseil-cpp/absl/types/optional.h"
@@ -61,20 +60,16 @@ bool CookieAccessDelegateImpl::ShouldIgnoreSameSiteRestrictions(
return false;
}
-absl::optional<std::pair<net::FirstPartySetMetadata,
- net::FirstPartySetsCacheFilter::MatchInfo>>
+absl::optional<net::FirstPartySetMetadata>
CookieAccessDelegateImpl::ComputeFirstPartySetMetadataMaybeAsync(
const net::SchemefulSite& site,
const net::SchemefulSite* top_frame_site,
- base::OnceCallback<void(net::FirstPartySetMetadata,
- net::FirstPartySetsCacheFilter::MatchInfo)>
- callback) const {
- if (!first_party_sets_access_delegate_) {
- return std::make_pair(net::FirstPartySetMetadata(),
- net::FirstPartySetsCacheFilter::MatchInfo());
- }
+ const std::set<net::SchemefulSite>& party_context,
+ base::OnceCallback<void(net::FirstPartySetMetadata)> callback) const {
+ if (!first_party_sets_access_delegate_)
+ return {net::FirstPartySetMetadata()};
return first_party_sets_access_delegate_->ComputeMetadata(
- site, top_frame_site, std::move(callback));
+ site, top_frame_site, party_context, std::move(callback));
}
absl::optional<FirstPartySetsAccessDelegate::EntriesResult>
diff --git a/services/network/cookie_access_delegate_impl.h b/services/network/cookie_access_delegate_impl.h
index 72a96c033d0c3eb3555cff718c66d7775cf4800c..50c3a309db86e5f044e2bfc429ca82fc6586d084 100644
--- a/services/network/cookie_access_delegate_impl.h
+++ b/services/network/cookie_access_delegate_impl.h
@@ -5,7 +5,10 @@
#ifndef SERVICES_NETWORK_COOKIE_ACCESS_DELEGATE_IMPL_H_
#define SERVICES_NETWORK_COOKIE_ACCESS_DELEGATE_IMPL_H_
+#include <set>
+
#include "base/component_export.h"
+#include "base/containers/flat_map.h"
#include "base/containers/flat_set.h"
#include "base/functional/callback_forward.h"
#include "base/memory/raw_ptr.h"
@@ -13,7 +16,6 @@
#include "net/cookies/cookie_access_delegate.h"
#include "net/cookies/cookie_constants.h"
#include "net/first_party_sets/first_party_set_metadata.h"
-#include "net/first_party_sets/first_party_sets_cache_filter.h"
#include "services/network/cookie_settings.h"
#include "services/network/first_party_sets/first_party_sets_access_delegate.h"
#include "services/network/public/mojom/cookie_manager.mojom.h"
@@ -53,15 +55,13 @@ class COMPONENT_EXPORT(NETWORK_SERVICE) CookieAccessDelegateImpl
bool ShouldIgnoreSameSiteRestrictions(
const GURL& url,
const net::SiteForCookies& site_for_cookies) const override;
- [[nodiscard]] absl::optional<
- std::pair<net::FirstPartySetMetadata,
- net::FirstPartySetsCacheFilter::MatchInfo>>
+ [[nodiscard]] absl::optional<net::FirstPartySetMetadata>
ComputeFirstPartySetMetadataMaybeAsync(
const net::SchemefulSite& site,
const net::SchemefulSite* top_frame_site,
- base::OnceCallback<void(net::FirstPartySetMetadata,
- net::FirstPartySetsCacheFilter::MatchInfo)>
- callback) const override;
+ const std::set<net::SchemefulSite>& party_context,
+ base::OnceCallback<void(net::FirstPartySetMetadata)> callback)
+ const override;
[[nodiscard]] absl::optional<FirstPartySetsAccessDelegate::EntriesResult>
FindFirstPartySetEntries(
const base::flat_set<net::SchemefulSite>& sites,
diff --git a/services/network/cookie_manager.cc b/services/network/cookie_manager.cc
index e5e410525d8a6ffe8f653fee3de4be1eaca504a9..7c11eb0558368aa2d94cf4e4f15d5b09f665719e 100644
--- a/services/network/cookie_manager.cc
+++ b/services/network/cookie_manager.cc
@@ -141,8 +141,8 @@ void CookieManager::SetCanonicalCookie(const net::CanonicalCookie& cookie,
cookie.Name(), cookie.Value(), cookie.Domain(), cookie.Path(),
cookie.CreationDate(), adjusted_expiry_date, cookie.LastAccessDate(),
cookie.LastUpdateDate(), cookie.IsSecure(), cookie.IsHttpOnly(),
- cookie.SameSite(), cookie.Priority(), cookie_partition_key,
- cookie.SourceScheme(), cookie.SourcePort());
+ cookie.SameSite(), cookie.Priority(), cookie.IsSameParty(),
+ cookie_partition_key, cookie.SourceScheme(), cookie.SourcePort());
if (!cookie_ptr) {
std::move(callback).Run(
net::CookieAccessResult(net::CookieInclusionStatus(
diff --git a/services/network/first_party_sets/first_party_sets_access_delegate.cc b/services/network/first_party_sets/first_party_sets_access_delegate.cc
index ed35e2dc6f5b0e2867cfdc5fb118e0dd45324a13..c07f0dc2d8a146cf187b204db4f2a9bceca6ceac 100644
--- a/services/network/first_party_sets/first_party_sets_access_delegate.cc
+++ b/services/network/first_party_sets/first_party_sets_access_delegate.cc
@@ -65,24 +65,20 @@ void FirstPartySetsAccessDelegate::SetEnabled(bool enabled) {
enabled_ = enabled;
}
-absl::optional<std::pair<net::FirstPartySetMetadata,
- net::FirstPartySetsCacheFilter::MatchInfo>>
+absl::optional<net::FirstPartySetMetadata>
FirstPartySetsAccessDelegate::ComputeMetadata(
const net::SchemefulSite& site,
const net::SchemefulSite* top_frame_site,
- base::OnceCallback<void(net::FirstPartySetMetadata,
- net::FirstPartySetsCacheFilter::MatchInfo)>
- callback) {
+ const std::set<net::SchemefulSite>& party_context,
+ base::OnceCallback<void(net::FirstPartySetMetadata)> callback) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (!enabled_) {
- return std::make_pair(net::FirstPartySetMetadata(),
- net::FirstPartySetsCacheFilter::MatchInfo());
+ return {net::FirstPartySetMetadata()};
}
if (!ready_event_.has_value()) {
if (!wait_for_init_) {
- return std::make_pair(net::FirstPartySetMetadata(),
- net::FirstPartySetsCacheFilter::MatchInfo());
+ return {net::FirstPartySetMetadata()};
}
// base::Unretained() is safe because `this` owns `pending_queries_` and
// `pending_queries_` will not run the enqueued callbacks after `this` is
@@ -90,29 +86,12 @@ FirstPartySetsAccessDelegate::ComputeMetadata(
EnqueuePendingQuery(base::BindOnce(
&FirstPartySetsAccessDelegate::ComputeMetadataAndInvoke,
base::Unretained(this), site, base::OptionalFromPtr(top_frame_site),
- std::move(callback)));
+ party_context, std::move(callback)));
return absl::nullopt;
}
- net::FirstPartySetsCacheFilter::MatchInfo match_info(
- cache_filter()->GetMatchInfo(site));
-
- absl::optional<net::FirstPartySetMetadata> metadata =
- manager_->ComputeMetadata(
- site, top_frame_site, *context_config(),
- base::BindOnce(
- [](base::OnceCallback<void(
- net::FirstPartySetMetadata,
- net::FirstPartySetsCacheFilter::MatchInfo)> callback,
- net::FirstPartySetsCacheFilter::MatchInfo info,
- net::FirstPartySetMetadata metadata) {
- std::move(callback).Run(std::move(metadata), std::move(info));
- },
- std::move(callback), match_info));
-
- return metadata.has_value() ? absl::make_optional(std::make_pair(
- std::move(metadata).value(), match_info))
- : absl::nullopt;
+ return manager_->ComputeMetadata(site, top_frame_site, party_context,
+ *context_config(), std::move(callback));
}
absl::optional<FirstPartySetsAccessDelegate::EntriesResult>
@@ -141,13 +120,37 @@ FirstPartySetsAccessDelegate::FindEntries(
return manager_->FindEntries(sites, *context_config(), std::move(callback));
}
+absl::optional<net::FirstPartySetsCacheFilter::MatchInfo>
+FirstPartySetsAccessDelegate::GetCacheFilterMatchInfo(
+ const net::SchemefulSite& site,
+ base::OnceCallback<void(net::FirstPartySetsCacheFilter::MatchInfo)>
+ callback) {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+
+ if (!enabled_)
+ return {net::FirstPartySetsCacheFilter::MatchInfo()};
+
+ if (!ready_event_.has_value()) {
+ if (!wait_for_init_) {
+ return {net::FirstPartySetsCacheFilter::MatchInfo()};
+ }
+ // base::Unretained() is safe because `this` owns `pending_queries_` and
+ // `pending_queries_` will not run the enqueued callbacks after `this` is
+ // destroyed.
+ EnqueuePendingQuery(base::BindOnce(
+ &FirstPartySetsAccessDelegate::GetCacheFilterMatchInfoAndInvoke,
+ base::Unretained(this), site, std::move(callback)));
+ return absl::nullopt;
+ }
+
+ return cache_filter()->GetMatchInfo(site);
+}
+
void FirstPartySetsAccessDelegate::ComputeMetadataAndInvoke(
const net::SchemefulSite& site,
const absl::optional<net::SchemefulSite> top_frame_site,
- base::OnceCallback<void(net::FirstPartySetMetadata,
- net::FirstPartySetsCacheFilter::MatchInfo)>
- callback) const {
- using CallbackType = decltype(callback);
+ const std::set<net::SchemefulSite>& party_context,
+ base::OnceCallback<void(net::FirstPartySetMetadata)> callback) const {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
CHECK(context_config());
// NB: since `ComputeMetadata` returns early if the delegate is disabled,
@@ -155,26 +158,17 @@ void FirstPartySetsAccessDelegate::ComputeMetadataAndInvoke(
// enabled when the query was received. However, the delegate may have been
// disabled between then and now, so we have no guarantees re: `enabled_` now.
- std::pair<CallbackType, CallbackType> callbacks =
- base::SplitOnceCallback(std::move(callback));
-
- net::FirstPartySetsCacheFilter::MatchInfo match_info(
- cache_filter()->GetMatchInfo(site));
+ std::pair<base::OnceCallback<void(net::FirstPartySetMetadata)>,
+ base::OnceCallback<void(net::FirstPartySetMetadata)>>
+ callbacks = base::SplitOnceCallback(std::move(callback));
absl::optional<net::FirstPartySetMetadata> sync_result =
- manager_->ComputeMetadata(
- site, base::OptionalToPtr(top_frame_site), *context_config(),
- base::BindOnce(
- [](CallbackType callback,
- net::FirstPartySetsCacheFilter::MatchInfo match_info,
- net::FirstPartySetMetadata metadata) {
- std::move(callback).Run(std::move(metadata), match_info);
- },
- std::move(callbacks.first), match_info));
-
- if (sync_result.has_value()) {
- std::move(callbacks.second).Run(std::move(sync_result.value()), match_info);
- }
+ manager_->ComputeMetadata(site, base::OptionalToPtr(top_frame_site),
+ party_context, *context_config(),
+ std::move(callbacks.first));
+
+ if (sync_result.has_value())
+ std::move(callbacks.second).Run(std::move(sync_result.value()));
}
void FirstPartySetsAccessDelegate::FindEntriesAndInvoke(
@@ -201,6 +195,20 @@ void FirstPartySetsAccessDelegate::FindEntriesAndInvoke(
std::move(callbacks.second).Run(sync_result.value());
}
+void FirstPartySetsAccessDelegate::GetCacheFilterMatchInfoAndInvoke(
+ const net::SchemefulSite& site,
+ base::OnceCallback<void(net::FirstPartySetsCacheFilter::MatchInfo)>
+ callback) const {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+ CHECK(cache_filter());
+ // NB: since `GetCacheFilterMatchInfo` returns early if the delegate is
+ // disabled, we're guaranteed that for any queued query, the delegate must
+ // have been enabled when the query was received. However, the delegate may
+ // have been disabled between then and now, so we have no guarantees re:
+ // `enabled_` now.
+ std::move(callback).Run(cache_filter()->GetMatchInfo(site));
+}
+
void FirstPartySetsAccessDelegate::InvokePendingQueries() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
CHECK(ready_event_.has_value());
diff --git a/services/network/first_party_sets/first_party_sets_access_delegate.h b/services/network/first_party_sets/first_party_sets_access_delegate.h
index d8a717465c10ed97863aec940987c1dd5814ed83..67df37ef7dfc344bff9bb569fdc58c29967aeb22 100644
--- a/services/network/first_party_sets/first_party_sets_access_delegate.h
+++ b/services/network/first_party_sets/first_party_sets_access_delegate.h
@@ -53,22 +53,17 @@ class FirstPartySetsAccessDelegate
void NotifyReady(mojom::FirstPartySetsReadyEventPtr ready_event) override;
void SetEnabled(bool enabled) override;
- // Computes the First-Party Set metadata and cache filter match info related
- // to the given context.
+ // Computes the First-Party Set metadata related to the given context.
//
// This may return a result synchronously, or asynchronously invoke `callback`
// with the result. The callback will be invoked iff the return value is
// nullopt; i.e. a result will be provided via return value or callback, but
// not both, and not neither.
- [[nodiscard]] absl::optional<
- std::pair<net::FirstPartySetMetadata,
- net::FirstPartySetsCacheFilter::MatchInfo>>
- ComputeMetadata(
+ [[nodiscard]] absl::optional<net::FirstPartySetMetadata> ComputeMetadata(
const net::SchemefulSite& site,
const net::SchemefulSite* top_frame_site,
- base::OnceCallback<void(net::FirstPartySetMetadata,
- net::FirstPartySetsCacheFilter::MatchInfo)>
- callback);
+ const std::set<net::SchemefulSite>& party_context,
+ base::OnceCallback<void(net::FirstPartySetMetadata)> callback);
// Calls FirstPartySetsManager::FindEntries either asynchronously or
// synchronously, once initialization is complete.
@@ -81,15 +76,24 @@ class FirstPartySetsAccessDelegate
const base::flat_set<net::SchemefulSite>& sites,
base::OnceCallback<void(EntriesResult)> callback);
+ // This may return a result synchronously, or asynchronously invoke `callback`
+ // with the result. The callback will be invoked iff the return value is
+ // nullopt; i.e. a result will be provided via return value or callback, but
+ // not both, and not neither.
+ [[nodiscard]] absl::optional<net::FirstPartySetsCacheFilter::MatchInfo>
+ GetCacheFilterMatchInfo(
+ const net::SchemefulSite& site,
+ base::OnceCallback<void(net::FirstPartySetsCacheFilter::MatchInfo)>
+ callback);
+
private:
// Same as `ComputeMetadata`, but plumbs the result into the callback. Must
// only be called once the instance is fully initialized.
void ComputeMetadataAndInvoke(
const net::SchemefulSite& site,
const absl::optional<net::SchemefulSite> top_frame_site,
- base::OnceCallback<void(net::FirstPartySetMetadata,
- net::FirstPartySetsCacheFilter::MatchInfo)>
- callback) const;
+ const std::set<net::SchemefulSite>& party_context,
+ base::OnceCallback<void(net::FirstPartySetMetadata)> callback) const;
// Same as `FindEntries`, but plumbs the result into the callback. Must only
// be called once the instance is fully initialized.
@@ -97,6 +101,13 @@ class FirstPartySetsAccessDelegate
const base::flat_set<net::SchemefulSite>& sites,
base::OnceCallback<void(EntriesResult)> callback) const;
+ // Same as `GetCacheFilterMatchInfo`, but plumbs the result into the
+ // callback. Must only be called once the instance is fully initialized.
+ void GetCacheFilterMatchInfoAndInvoke(
+ const net::SchemefulSite& site,
+ base::OnceCallback<void(net::FirstPartySetsCacheFilter::MatchInfo)>
+ callback) const;
+
// Runs all pending queries. Must not be called until the instance is fully
// initialized.
void InvokePendingQueries();
diff --git a/services/network/first_party_sets/first_party_sets_manager.cc b/services/network/first_party_sets/first_party_sets_manager.cc
index 2ffd2a8be03efcf3900e7bbb4056c722e0dd9130..6cbd2bd9e09b6b7cb4fdb4d853959a1009cbf4a8 100644
--- a/services/network/first_party_sets/first_party_sets_manager.cc
+++ b/services/network/first_party_sets/first_party_sets_manager.cc
@@ -15,6 +15,7 @@
#include "base/feature_list.h"
#include "base/metrics/histogram_functions.h"
#include "base/metrics/histogram_macros.h"
+#include "base/ranges/algorithm.h"
#include "base/sequence_checker.h"
#include "base/time/time.h"
#include "base/timer/elapsed_timer.h"
@@ -24,6 +25,7 @@
#include "net/first_party_sets/first_party_set_entry.h"
#include "net/first_party_sets/first_party_set_metadata.h"
#include "net/first_party_sets/global_first_party_sets.h"
+#include "net/first_party_sets/same_party_context.h"
#include "third_party/abseil-cpp/absl/types/optional.h"
namespace network {
@@ -48,6 +50,7 @@ absl::optional<net::FirstPartySetMetadata>
FirstPartySetsManager::ComputeMetadata(
const net::SchemefulSite& site,
const net::SchemefulSite* top_frame_site,
+ const std::set<net::SchemefulSite>& party_context,
const net::FirstPartySetsContextConfig& fps_context_config,
base::OnceCallback<void(net::FirstPartySetMetadata)> callback) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
@@ -59,16 +62,19 @@ FirstPartySetsManager::ComputeMetadata(
EnqueuePendingQuery(base::BindOnce(
&FirstPartySetsManager::ComputeMetadataAndInvoke,
weak_factory_.GetWeakPtr(), site, base::OptionalFromPtr(top_frame_site),
- fps_context_config.Clone(), std::move(callback), base::ElapsedTimer()));
+ party_context, fps_context_config.Clone(), std::move(callback),
+ base::ElapsedTimer()));
return absl::nullopt;
}
- return ComputeMetadataInternal(site, top_frame_site, fps_context_config);
+ return ComputeMetadataInternal(site, top_frame_site, party_context,
+ fps_context_config);
}
void FirstPartySetsManager::ComputeMetadataAndInvoke(
const net::SchemefulSite& site,
const absl::optional<net::SchemefulSite> top_frame_site,
+ const std::set<net::SchemefulSite>& party_context,
const net::FirstPartySetsContextConfig& fps_context_config,
base::OnceCallback<void(net::FirstPartySetMetadata)> callback,
base::ElapsedTimer timer) const {
@@ -78,18 +84,21 @@ void FirstPartySetsManager::ComputeMetadataAndInvoke(
UMA_HISTOGRAM_TIMES("Cookie.FirstPartySets.EnqueueingDelay.ComputeMetadata2",
timer.Elapsed());
- std::move(callback).Run(ComputeMetadataInternal(
- site, base::OptionalToPtr(top_frame_site), fps_context_config));
+ std::move(callback).Run(
+ ComputeMetadataInternal(site, base::OptionalToPtr(top_frame_site),
+ party_context, fps_context_config));
}
net::FirstPartySetMetadata FirstPartySetsManager::ComputeMetadataInternal(
const net::SchemefulSite& site,
const net::SchemefulSite* top_frame_site,
+ const std::set<net::SchemefulSite>& party_context,
const net::FirstPartySetsContextConfig& fps_context_config) const {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
CHECK(sets_.has_value());
- return sets_->ComputeMetadata(site, top_frame_site, fps_context_config);
+ return sets_->ComputeMetadata(site, top_frame_site, party_context,
+ fps_context_config);
}
absl::optional<net::FirstPartySetEntry> FirstPartySetsManager::FindEntry(
diff --git a/services/network/first_party_sets/first_party_sets_manager.h b/services/network/first_party_sets/first_party_sets_manager.h
index 9754dd5f6b0087c6630285b1f592e43cda4ba8d6..b04a98f42fc96a5f2e85cd1bc8ea841fb170e4f2 100644
--- a/services/network/first_party_sets/first_party_sets_manager.h
+++ b/services/network/first_party_sets/first_party_sets_manager.h
@@ -15,6 +15,7 @@
#include "base/functional/callback.h"
#include "base/sequence_checker.h"
#include "base/thread_annotations.h"
+#include "base/time/time.h"
#include "base/timer/elapsed_timer.h"
#include "net/base/schemeful_site.h"
#include "net/first_party_sets/first_party_set_entry.h"
@@ -52,6 +53,7 @@ class FirstPartySetsManager {
[[nodiscard]] absl::optional<net::FirstPartySetMetadata> ComputeMetadata(
const net::SchemefulSite& site,
const net::SchemefulSite* top_frame_site,
+ const std::set<net::SchemefulSite>& party_context,
const net::FirstPartySetsContextConfig& fps_context_config,
base::OnceCallback<void(net::FirstPartySetMetadata)> callback);
@@ -84,6 +86,7 @@ class FirstPartySetsManager {
void ComputeMetadataAndInvoke(
const net::SchemefulSite& site,
const absl::optional<net::SchemefulSite> top_frame_site,
+ const std::set<net::SchemefulSite>& party_context,
const net::FirstPartySetsContextConfig& fps_context_config,
base::OnceCallback<void(net::FirstPartySetMetadata)> callback,
base::ElapsedTimer timer) const;
@@ -93,6 +96,7 @@ class FirstPartySetsManager {
net::FirstPartySetMetadata ComputeMetadataInternal(
const net::SchemefulSite& site,
const net::SchemefulSite* top_frame_site,
+ const std::set<net::SchemefulSite>& party_context,
const net::FirstPartySetsContextConfig& fps_context_config) const;
// Returns `site`'s entry, or `nullopt` if `site` has no entry.
diff --git a/services/network/network_service_network_delegate.cc b/services/network/network_service_network_delegate.cc
index d1e91772d48c45b039e349cf76c84a49ffd2b2cd..301a15c02b6b6ded56e231116aaa18c126d1bf95 100644
--- a/services/network/network_service_network_delegate.cc
+++ b/services/network/network_service_network_delegate.cc
@@ -18,6 +18,7 @@
#include "net/base/load_flags.h"
#include "net/base/net_errors.h"
#include "net/cookies/cookie_setting_override.h"
+#include "net/first_party_sets/same_party_context.h"
#include "net/url_request/clear_site_data.h"
#include "net/url_request/referrer_policy.h"
#include "net/url_request/url_request.h"
@@ -331,6 +332,16 @@ bool NetworkServiceNetworkDelegate::OnCanUseReportingClient(
origin, net::CookieSettingOverrides());
}
+absl::optional<net::FirstPartySetsCacheFilter::MatchInfo>
+NetworkServiceNetworkDelegate::
+ OnGetFirstPartySetsCacheFilterMatchInfoMaybeAsync(
+ const net::SchemefulSite& request_site,
+ base::OnceCallback<void(net::FirstPartySetsCacheFilter::MatchInfo)>
+ callback) const {
+ return network_context_->first_party_sets_access_delegate()
+ .GetCacheFilterMatchInfo(request_site, std::move(callback));
+}
+
int NetworkServiceNetworkDelegate::HandleClearSiteDataHeader(
net::URLRequest* request,
net::CompletionOnceCallback callback,
diff --git a/services/network/network_service_network_delegate.h b/services/network/network_service_network_delegate.h
index acfc4db5bf05115d8a3dcd274ba08838f7db5ee8..25c230b8d0d65c95ba6524c61ad44ee3828326f9 100644
--- a/services/network/network_service_network_delegate.h
+++ b/services/network/network_service_network_delegate.h
@@ -19,6 +19,7 @@
#include "third_party/abseil-cpp/absl/types/optional.h"
namespace net {
+class SchemefulSite;
class CookieInclusionStatus;
} // namespace net
@@ -94,6 +95,11 @@ class COMPONENT_EXPORT(NETWORK_SERVICE) NetworkServiceNetworkDelegate
const GURL& endpoint) const override;
bool OnCanUseReportingClient(const url::Origin& origin,
const GURL& endpoint) const override;
+ absl::optional<net::FirstPartySetsCacheFilter::MatchInfo>
+ OnGetFirstPartySetsCacheFilterMatchInfoMaybeAsync(
+ const net::SchemefulSite& request_site,
+ base::OnceCallback<void(net::FirstPartySetsCacheFilter::MatchInfo)>
+ callback) const override;
int HandleClearSiteDataHeader(
net::URLRequest* request,
diff --git a/services/network/public/cpp/cookie_manager_mojom_traits.cc b/services/network/public/cpp/cookie_manager_mojom_traits.cc
index bdf07a173b84ab6a4c3222ecbb2da84130950d06..2505b01a13c62f49ce73a539d5d452ea6ae340bd 100644
--- a/services/network/public/cpp/cookie_manager_mojom_traits.cc
+++ b/services/network/public/cpp/cookie_manager_mojom_traits.cc
@@ -586,6 +586,17 @@ bool StructTraits<network::mojom::CookieOptionsDataView, net::CookieOptions>::
else
cookie_options->unset_return_excluded_cookies();
+ net::SamePartyContext same_party_context;
+ if (!mojo_options.ReadSamePartyContext(&same_party_context))
+ return false;
+ cookie_options->set_same_party_context(same_party_context);
+
+ cookie_options->set_full_party_context_size(
+ mojo_options.full_party_context_size());
+
+ cookie_options->set_is_in_nontrivial_first_party_set(
+ mojo_options.is_in_nontrivial_first_party_set());
+
return true;
}
@@ -692,8 +703,8 @@ bool StructTraits<
std::move(name), std::move(value), std::move(domain), std::move(path),
std::move(creation_time), std::move(expiry_time),
std::move(last_access_time), std::move(last_update_time), cookie.secure(),
- cookie.httponly(), site_restrictions, priority, partition_key,
- source_scheme, cookie.source_port());
+ cookie.httponly(), site_restrictions, priority, cookie.same_party(),
+ partition_key, source_scheme, cookie.source_port());
if (!cc)
return false;
*out = *cc;
diff --git a/services/network/public/cpp/cookie_manager_mojom_traits.h b/services/network/public/cpp/cookie_manager_mojom_traits.h
index 3f7d6369a4e107b71f9c20fe5e212a8ea7d45701..d562b702b0f5a87017268e80d5941585609ccc3b 100644
--- a/services/network/public/cpp/cookie_manager_mojom_traits.h
+++ b/services/network/public/cpp/cookie_manager_mojom_traits.h
@@ -16,6 +16,8 @@
#include "net/cookies/cookie_inclusion_status.h"
#include "net/cookies/cookie_options.h"
#include "net/cookies/cookie_partition_key_collection.h"
+#include "net/first_party_sets/first_party_set_entry.h"
+#include "net/first_party_sets/same_party_context.h"
#include "services/network/public/cpp/cookie_manager_shared_mojom_traits.h"
#include "services/network/public/mojom/cookie_manager.mojom-forward.h"
#include "services/network/public/mojom/cookie_manager.mojom.h"
@@ -194,6 +196,18 @@ struct StructTraits<network::mojom::CookieOptionsDataView, net::CookieOptions> {
return o.return_excluded_cookies();
}
+ static net::SamePartyContext same_party_context(const net::CookieOptions& o) {
+ return o.same_party_context();
+ }
+
+ static uint32_t full_party_context_size(const net::CookieOptions& o) {
+ return o.full_party_context_size();
+ }
+
+ static bool is_in_nontrivial_first_party_set(const net::CookieOptions& o) {
+ return o.is_in_nontrivial_first_party_set();
+ }
+
static bool Read(network::mojom::CookieOptionsDataView mojo_options,
net::CookieOptions* cookie_options);
};
@@ -270,6 +284,9 @@ struct StructTraits<network::mojom::CanonicalCookieDataView,
static net::CookieSourceScheme source_scheme(const net::CanonicalCookie& c) {
return c.SourceScheme();
}
+ static bool same_party(const net::CanonicalCookie& c) {
+ return c.IsSameParty();
+ }
static const absl::optional<net::CookiePartitionKey>& partition_key(
const net::CanonicalCookie& c) {
return c.PartitionKey();
diff --git a/services/network/public/cpp/first_party_sets_mojom_traits.cc b/services/network/public/cpp/first_party_sets_mojom_traits.cc
index b5d5d26e7d2362ba2a9d17be393cb70cb001b827..7a04526edc7b09b1e1d2cb0f9fc75a2898359282 100644
--- a/services/network/public/cpp/first_party_sets_mojom_traits.cc
+++ b/services/network/public/cpp/first_party_sets_mojom_traits.cc
@@ -18,6 +18,7 @@
#include "net/first_party_sets/first_party_sets_cache_filter.h"
#include "net/first_party_sets/first_party_sets_context_config.h"
#include "net/first_party_sets/global_first_party_sets.h"
+#include "net/first_party_sets/same_party_context.h"
#include "services/network/public/cpp/schemeful_site_mojom_traits.h"
#include "services/network/public/mojom/first_party_sets.mojom-shared.h"
@@ -83,10 +84,55 @@ bool StructTraits<network::mojom::FirstPartySetEntryDataView,
return true;
}
+bool EnumTraits<network::mojom::SamePartyCookieContextType,
+ net::SamePartyContext::Type>::
+ FromMojom(network::mojom::SamePartyCookieContextType context_type,
+ net::SamePartyContext::Type* out) {
+ switch (context_type) {
+ case network::mojom::SamePartyCookieContextType::kCrossParty:
+ *out = net::SamePartyContext::Type::kCrossParty;
+ return true;
+ case network::mojom::SamePartyCookieContextType::kSameParty:
+ *out = net::SamePartyContext::Type::kSameParty;
+ return true;
+ }
+ return false;
+}
+
+network::mojom::SamePartyCookieContextType
+EnumTraits<network::mojom::SamePartyCookieContextType,
+ net::SamePartyContext::Type>::ToMojom(net::SamePartyContext::Type
+ context_type) {
+ switch (context_type) {
+ case net::SamePartyContext::Type::kCrossParty:
+ return network::mojom::SamePartyCookieContextType::kCrossParty;
+ case net::SamePartyContext::Type::kSameParty:
+ return network::mojom::SamePartyCookieContextType::kSameParty;
+ }
+ NOTREACHED();
+ return network::mojom::SamePartyCookieContextType::kCrossParty;
+}
+
+bool StructTraits<network::mojom::SamePartyContextDataView,
+ net::SamePartyContext>::
+ Read(network::mojom::SamePartyContextDataView context,
+ net::SamePartyContext* out) {
+ net::SamePartyContext::Type context_type;
+ if (!context.ReadContextType(&context_type))
+ return false;
+
+ *out = net::SamePartyContext(context_type);
+ return true;
+}
+
bool StructTraits<network::mojom::FirstPartySetMetadataDataView,
net::FirstPartySetMetadata>::
Read(network::mojom::FirstPartySetMetadataDataView metadata,
net::FirstPartySetMetadata* out_metadata) {
+ net::SamePartyContext context;
+ if (!metadata.ReadContext(&context))
+ return false;
+
absl::optional<net::FirstPartySetEntry> frame_entry;
if (!metadata.ReadFrameEntry(&frame_entry))
return false;
@@ -95,8 +141,9 @@ bool StructTraits<network::mojom::FirstPartySetMetadataDataView,
if (!metadata.ReadTopFrameEntry(&top_frame_entry))
return false;
- *out_metadata = net::FirstPartySetMetadata(
- base::OptionalToPtr(frame_entry), base::OptionalToPtr(top_frame_entry));
+ *out_metadata =
+ net::FirstPartySetMetadata(context, base::OptionalToPtr(frame_entry),
+ base::OptionalToPtr(top_frame_entry));
return true;
}
diff --git a/services/network/public/cpp/first_party_sets_mojom_traits.h b/services/network/public/cpp/first_party_sets_mojom_traits.h
index 8a03b2f3216fdce84c868d71188b8a702b51ad61..07acf08940fa3c69e7e1d9f07da6eb97ba3e1896 100644
--- a/services/network/public/cpp/first_party_sets_mojom_traits.h
+++ b/services/network/public/cpp/first_party_sets_mojom_traits.h
@@ -16,6 +16,7 @@
#include "net/first_party_sets/first_party_sets_cache_filter.h"
#include "net/first_party_sets/first_party_sets_context_config.h"
#include "net/first_party_sets/global_first_party_sets.h"
+#include "net/first_party_sets/same_party_context.h"
#include "services/network/public/mojom/first_party_sets.mojom-shared.h"
namespace mojo {
@@ -61,10 +62,38 @@ struct COMPONENT_EXPORT(FIRST_PARTY_SETS_MOJOM_TRAITS)
net::FirstPartySetEntry* out);
};
+template <>
+struct COMPONENT_EXPORT(FIRST_PARTY_SETS_MOJOM_TRAITS)
+ EnumTraits<network::mojom::SamePartyCookieContextType,
+ net::SamePartyContext::Type> {
+ static network::mojom::SamePartyCookieContextType ToMojom(
+ net::SamePartyContext::Type context_type);
+
+ static bool FromMojom(network::mojom::SamePartyCookieContextType context_type,
+ net::SamePartyContext::Type* out);
+};
+
+template <>
+struct COMPONENT_EXPORT(FIRST_PARTY_SETS_MOJOM_TRAITS)
+ StructTraits<network::mojom::SamePartyContextDataView,
+ net::SamePartyContext> {
+ static net::SamePartyContext::Type context_type(
+ const net::SamePartyContext& s) {
+ return s.context_type();
+ }
+
+ static bool Read(network::mojom::SamePartyContextDataView bundle,
+ net::SamePartyContext* out);
+};
+
template <>
struct COMPONENT_EXPORT(FIRST_PARTY_SETS_MOJOM_TRAITS)
StructTraits<network::mojom::FirstPartySetMetadataDataView,
net::FirstPartySetMetadata> {
+ static net::SamePartyContext context(const net::FirstPartySetMetadata& m) {
+ return m.context();
+ }
+
static absl::optional<net::FirstPartySetEntry> frame_entry(
const net::FirstPartySetMetadata& m) {
return m.frame_entry();
diff --git a/services/network/public/cpp/isolation_info_mojom_traits.cc b/services/network/public/cpp/isolation_info_mojom_traits.cc
index cc91befdc7da5e88eac6cea0f05eef0707bb4b04..a5a3d0af67c958766bacc8a02cacf490d3c5b5e4 100644
--- a/services/network/public/cpp/isolation_info_mojom_traits.cc
+++ b/services/network/public/cpp/isolation_info_mojom_traits.cc
@@ -53,6 +53,7 @@ bool StructTraits<network::mojom::IsolationInfoDataView, net::IsolationInfo>::
absl::optional<base::UnguessableToken> nonce;
net::SiteForCookies site_for_cookies;
net::IsolationInfo::RequestType request_type;
+ absl::optional<std::vector<net::SchemefulSite>> mojo_party_context;
if (!data.ReadTopFrameOrigin(&top_frame_origin)) {
network::debug::SetDeserializationCrashKeyString("isolation_top_origin");
@@ -63,14 +64,23 @@ bool StructTraits<network::mojom::IsolationInfoDataView, net::IsolationInfo>::
return false;
}
if (!data.ReadNonce(&nonce) || !data.ReadSiteForCookies(&site_for_cookies) ||
- !data.ReadRequestType(&request_type)) {
+ !data.ReadRequestType(&request_type) ||
+ !data.ReadPartyContext(&mojo_party_context)) {
return false;
}
+ absl::optional<std::set<net::SchemefulSite>> party_context;
+ if (mojo_party_context.has_value()) {
+ party_context = std::set<net::SchemefulSite>(mojo_party_context->begin(),
+ mojo_party_context->end());
+ if (party_context->size() != mojo_party_context->size())
+ return false;
+ }
+
absl::optional<net::IsolationInfo> isolation_info =
net::IsolationInfo::CreateIfConsistent(request_type, top_frame_origin,
frame_origin, site_for_cookies,
- nonce);
+ std::move(party_context), nonce);
if (!isolation_info) {
network::debug::SetDeserializationCrashKeyString("isolation_inconsistent");
return false;
diff --git a/services/network/public/cpp/isolation_info_mojom_traits.h b/services/network/public/cpp/isolation_info_mojom_traits.h
index 40c5582de6832132b9e32de65cdffb9d58b99b36..1d41304d38061f7fb1b219a2289af5e41cbd1353 100644
--- a/services/network/public/cpp/isolation_info_mojom_traits.h
+++ b/services/network/public/cpp/isolation_info_mojom_traits.h
@@ -57,6 +57,11 @@ struct COMPONENT_EXPORT(NETWORK_CPP_BASE)
return input.site_for_cookies();
}
+ static const absl::optional<std::set<net::SchemefulSite>>& party_context(
+ const net::IsolationInfo& input) {
+ return input.party_context_;
+ }
+
static bool Read(network::mojom::IsolationInfoDataView data,
net::IsolationInfo* out);
};
diff --git a/services/network/public/mojom/BUILD.gn b/services/network/public/mojom/BUILD.gn
index 4cb2d01d09a2c0ba3acb3672b20b543cbb033aca..cacca86a2e8638d3e12d2a36f1404480e1ac1173 100644
--- a/services/network/public/mojom/BUILD.gn
+++ b/services/network/public/mojom/BUILD.gn
@@ -1015,6 +1015,14 @@ mojom("mojom_first_party_sets") {
mojom = "network.mojom.FirstPartySetEntry"
cpp = "::net::FirstPartySetEntry"
},
+ {
+ mojom = "network.mojom.SamePartyCookieContextType"
+ cpp = "::net::SamePartyContext::Type"
+ },
+ {
+ mojom = "network.mojom.SamePartyContext"
+ cpp = "::net::SamePartyContext"
+ },
{
mojom = "network.mojom.FirstPartySetMetadata"
cpp = "::net::FirstPartySetMetadata"
diff --git a/services/network/public/mojom/cookie_manager.mojom b/services/network/public/mojom/cookie_manager.mojom
index de434341bd6328f5876fb8057d652582dbbf4802..011f7b0e95bdbb2f3a370211cc38ba547d881540 100644
--- a/services/network/public/mojom/cookie_manager.mojom
+++ b/services/network/public/mojom/cookie_manager.mojom
@@ -166,6 +166,12 @@ struct CookieOptions {
CookieSameSiteContext same_site_cookie_context;
bool update_access_time = true;
bool return_excluded_cookies = false;
+ SamePartyContext same_party_context;
+ // The size of the isolation_info.party_context plus the top-frame site for
+ // logging purposes.
+ uint32 full_party_context_size = 0;
+ // Whether the site is a member of a nontrivial First-Party Set.
+ bool is_in_nontrivial_first_party_set = false;
};
// See net/cookies/canonical_cookie.{h,cc} for documentation.
@@ -184,6 +190,7 @@ struct CanonicalCookie {
CookieSameSite site_restrictions = NO_RESTRICTION;
CookiePriority priority = MEDIUM;
CookieSourceScheme source_scheme = kUnset;
+ bool same_party = false;
CookiePartitionKey? partition_key;
// -1 because of url::PORT_UNSPECIFIED
// url/third_party/mozilla/url_parse.h
diff --git a/services/network/public/mojom/first_party_sets.mojom b/services/network/public/mojom/first_party_sets.mojom
index 875138dee43ec8739c35fbab27e27ca0fc502e69..015510d0920eceb89e92f174292403160f725a23 100644
--- a/services/network/public/mojom/first_party_sets.mojom
+++ b/services/network/public/mojom/first_party_sets.mojom
@@ -27,9 +27,25 @@ struct FirstPartySetEntry {
SiteIndex? site_index;
};
+// Computed for every cookie access attempt but is only relevant for SameParty
+// cookies.
+enum SamePartyCookieContextType {
+ // The opposite to kSameParty. Should be the default value.
+ kCrossParty,
+ // If the request URL is in the same First-Party Sets as the top-frame site
+ // and each member of the isolation_info.party_context.
+ kSameParty,
+};
+
+// Keep defaults in here in sync with net/cookies/same_party_context.cc.
+struct SamePartyContext {
+ SamePartyCookieContextType context_type = kCrossParty;
+};
+
// This struct must match the class fields defined in
// //net/first_party_sets/first_party_set_metadata.h.
struct FirstPartySetMetadata {
+ SamePartyContext context;
// absl::nullopt indicates that the frame's site is not associated with any
// First-Party Set.
FirstPartySetEntry? frame_entry;
diff --git a/services/network/public/mojom/isolation_info.mojom b/services/network/public/mojom/isolation_info.mojom
index 60b726cce8f05a8d0a89a310d00b467be519badc..407ea92cb29712fd89f7c40c6bb210900da8987d 100644
--- a/services/network/public/mojom/isolation_info.mojom
+++ b/services/network/public/mojom/isolation_info.mojom
@@ -26,4 +26,5 @@ struct IsolationInfo {
url.mojom.Origin? frame_origin;
mojo_base.mojom.UnguessableToken? nonce;
SiteForCookies site_for_cookies;
+ array<SchemefulSite>? party_context;
};
diff --git a/services/network/restricted_cookie_manager.cc b/services/network/restricted_cookie_manager.cc
index b5b48a0ce69c7bbe7ce61028fc5d832875e59b7c..57c62912126a000ab15d1658af2177031630738b 100644
--- a/services/network/restricted_cookie_manager.cc
+++ b/services/network/restricted_cookie_manager.cc
@@ -38,7 +38,6 @@
#include "net/cookies/cookie_util.h"
#include "net/cookies/site_for_cookies.h"
#include "net/first_party_sets/first_party_set_metadata.h"
-#include "net/first_party_sets/first_party_sets_cache_filter.h"
#include "services/network/ad_heuristic_cookie_overrides.h"
#include "services/network/cookie_settings.h"
#include "services/network/public/cpp/features.h"
@@ -109,11 +108,20 @@ constexpr base::TimeDelta kCookiesAccessedTimeout = base::Milliseconds(100);
constexpr size_t kMaxCookieCacheCount = 32u;
constexpr size_t kIncreasedMaxCookieCacheCount = 100u;
+// TODO(cfredric): the `force_ignore_top_frame_party` param being false prevents
+// `document.cookie` access for same-party scripts embedded in an extension
+// frame. It would be better if we allowed that similarly to how we allow
+// SameParty cookies for requests in same-party contexts embedded in top-level
+// extension frames.
+const bool kForceIgnoreTopFrameParty = false;
+
net::CookieOptions MakeOptionsForSet(
mojom::RestrictedCookieManagerRole role,
const GURL& url,
const net::SiteForCookies& site_for_cookies,
- const CookieSettings& cookie_settings) {
+ const net::IsolationInfo& isolation_info,
+ const CookieSettings& cookie_settings,
+ const net::FirstPartySetMetadata& first_party_set_metadata) {
net::CookieOptions options;
bool force_ignore_site_for_cookies =
cookie_settings.ShouldIgnoreSameSiteRestrictions(url, site_for_cookies);
@@ -129,6 +137,14 @@ net::CookieOptions MakeOptionsForSet(
net::cookie_util::ComputeSameSiteContextForSubresource(
url, site_for_cookies, force_ignore_site_for_cookies));
}
+ options.set_same_party_context(first_party_set_metadata.context());
+ if (isolation_info.party_context().has_value()) {
+ // Count the top-frame site since it's not in the party_context.
+ options.set_full_party_context_size(isolation_info.party_context()->size() +
+ 1);
+ }
+ options.set_is_in_nontrivial_first_party_set(
+ first_party_set_metadata.frame_entry().has_value());
return options;
}
@@ -137,7 +153,9 @@ net::CookieOptions MakeOptionsForGet(
mojom::RestrictedCookieManagerRole role,
const GURL& url,
const net::SiteForCookies& site_for_cookies,
- const CookieSettings& cookie_settings) {
+ const net::IsolationInfo& isolation_info,
+ const CookieSettings& cookie_settings,
+ const net::FirstPartySetMetadata& first_party_set_metadata) {
// TODO(https://crbug.com/925311): Wire initiator here.
net::CookieOptions options;
bool force_ignore_site_for_cookies =
@@ -155,6 +173,14 @@ net::CookieOptions MakeOptionsForGet(
net::cookie_util::ComputeSameSiteContextForSubresource(
url, site_for_cookies, force_ignore_site_for_cookies));
}
+ options.set_same_party_context(first_party_set_metadata.context());
+ if (isolation_info.party_context().has_value()) {
+ // Count the top-frame site since it's not in the party_context.
+ options.set_full_party_context_size(isolation_info.party_context()->size() +
+ 1);
+ }
+ options.set_is_in_nontrivial_first_party_set(
+ first_party_set_metadata.frame_entry().has_value());
return options;
}
@@ -196,21 +222,13 @@ void RestrictedCookieManager::ComputeFirstPartySetMetadata(
std::pair<base::OnceCallback<void(net::FirstPartySetMetadata)>,
base::OnceCallback<void(net::FirstPartySetMetadata)>>
callbacks = base::SplitOnceCallback(std::move(callback));
- absl::optional<std::pair<net::FirstPartySetMetadata,
- net::FirstPartySetsCacheFilter::MatchInfo>>
- metadata_and_match_info =
- net::cookie_util::ComputeFirstPartySetMetadataMaybeAsync(
- /*request_site=*/net::SchemefulSite(origin), isolation_info,
- cookie_store->cookie_access_delegate(),
- base::BindOnce([](net::FirstPartySetMetadata metadata,
- net::FirstPartySetsCacheFilter::MatchInfo
- match_info) {
- return metadata;
- }).Then(std::move(callbacks.first)));
- if (metadata_and_match_info.has_value()) {
- std::move(callbacks.second)
- .Run(std::move(metadata_and_match_info.value().first));
- }
+ absl::optional<net::FirstPartySetMetadata> metadata =
+ net::cookie_util::ComputeFirstPartySetMetadataMaybeAsync(
+ /*request_site=*/net::SchemefulSite(origin), isolation_info,
+ cookie_store->cookie_access_delegate(), kForceIgnoreTopFrameParty,
+ std::move(callbacks.first));
+ if (metadata.has_value())
+ std::move(callbacks.second).Run(std::move(metadata.value()));
}
bool CookieWithAccessResultComparer::operator()(
@@ -342,7 +360,8 @@ class RestrictedCookieManager::Listener : public base::LinkNode<Listener> {
bool has_storage_access,
const absl::optional<net::CookiePartitionKey>& cookie_partition_key,
net::CookieOptions options,
- mojo::PendingRemote<mojom::CookieChangeListener> mojo_listener)
+ mojo::PendingRemote<mojom::CookieChangeListener> mojo_listener,
+ bool same_party_attribute_enabled)
: cookie_store_(cookie_store),
restricted_cookie_manager_(restricted_cookie_manager),
url_(url),
@@ -350,7 +369,8 @@ class RestrictedCookieManager::Listener : public base::LinkNode<Listener> {
top_frame_origin_(top_frame_origin),
has_storage_access_(has_storage_access),
options_(options),
- mojo_listener_(std::move(mojo_listener)) {
+ mojo_listener_(std::move(mojo_listener)),
+ same_party_attribute_enabled_(same_party_attribute_enabled) {
// TODO(pwnall): add a constructor w/options to net::CookieChangeDispatcher.
cookie_store_subscription_ =
cookie_store->GetChangeDispatcher().AddCallbackForUrl(
@@ -386,11 +406,16 @@ class RestrictedCookieManager::Listener : public base::LinkNode<Listener> {
// CookieChangeDispatcher doesn't check for inclusion against `options_`, so
// we need to double-check that.
+ net::CookieSamePartyStatus same_party_status =
+ net::cookie_util::GetSamePartyStatus(change.cookie, options_,
+ same_party_attribute_enabled_);
+
if (!change.cookie
.IncludeForRequestURL(
url_, options_,
net::CookieAccessParams{change.access_result.access_semantics,
- delegate_treats_url_as_trustworthy})
+ delegate_treats_url_as_trustworthy,
+ same_party_status})
.status.IsInclude()) {
return;
}
@@ -442,6 +467,8 @@ class RestrictedCookieManager::Listener : public base::LinkNode<Listener> {
mojo::Remote<mojom::CookieChangeListener> mojo_listener_;
+ bool same_party_attribute_enabled_;
+
SEQUENCE_CHECKER(sequence_checker_);
};
@@ -468,6 +495,8 @@ RestrictedCookieManager::RestrictedCookieManager(
cookie_partition_key_collection_(
net::CookiePartitionKeyCollection::FromOptional(
cookie_partition_key_)),
+ same_party_attribute_enabled_(base::FeatureList::IsEnabled(
+ net::features::kSamePartyAttributeEnabled)),
receiver_(this),
metrics_updater_(metrics_updater),
max_cookie_cache_count_(
@@ -594,7 +623,8 @@ void RestrictedCookieManager::GetAllForUrl(
// TODO(morlovich): Try to validate site_for_cookies as well.
net::CookieOptions net_options =
- MakeOptionsForGet(role_, url, site_for_cookies, cookie_settings());
+ MakeOptionsForGet(role_, url, site_for_cookies, isolation_info_,
+ cookie_settings(), first_party_set_metadata_);
// TODO(https://crbug.com/977040): remove set_return_excluded_cookies() once
// removing deprecation warnings.
net_options.set_return_excluded_cookies();
@@ -855,8 +885,8 @@ void RestrictedCookieManager::SetCanonicalCookie(
net::CanonicalCookie::FromStorage(
cookie.Name(), cookie.Value(), cookie.Domain(), cookie.Path(), now,
cookie.ExpiryDate(), now, now, cookie.IsSecure(), cookie.IsHttpOnly(),
- cookie.SameSite(), cookie.Priority(), cookie_partition_key,
- source_scheme, origin_.port());
+ cookie.SameSite(), cookie.Priority(), cookie.IsSameParty(),
+ cookie_partition_key, source_scheme, origin_.port());
DCHECK(sanitized_cookie);
// FromStorage() uses a less strict version of IsCanonical(), we need to check
// the stricter version as well here.
@@ -867,7 +897,8 @@ void RestrictedCookieManager::SetCanonicalCookie(
net::CanonicalCookie cookie_copy = *sanitized_cookie;
net::CookieOptions options =
- MakeOptionsForSet(role_, url, site_for_cookies, cookie_settings());
+ MakeOptionsForSet(role_, url, site_for_cookies, isolation_info_,
+ cookie_settings(), first_party_set_metadata_);
net::CookieAccessResult cookie_access_result(status);
cookie_store_->SetCanonicalCookieAsync(
@@ -925,11 +956,12 @@ void RestrictedCookieManager::AddChangeListener(
}
net::CookieOptions net_options =
- MakeOptionsForGet(role_, url, site_for_cookies, cookie_settings());
+ MakeOptionsForGet(role_, url, site_for_cookies, isolation_info_,
+ cookie_settings(), first_party_set_metadata_);
auto listener = std::make_unique<Listener>(
cookie_store_, this, url, site_for_cookies, top_frame_origin,
has_storage_access, cookie_partition_key_, net_options,
- std::move(mojo_listener));
+ std::move(mojo_listener), same_party_attribute_enabled_);
listener->mojo_listener().set_disconnect_handler(
base::BindOnce(&RestrictedCookieManager::RemoveChangeListener,
diff --git a/services/network/restricted_cookie_manager.h b/services/network/restricted_cookie_manager.h
index 6e32b7e8c32e03fc5de0c6ddba9e8e65ccc790f9..b0e3bdd3246e2ee64781f44679c98e9989a88df4 100644
--- a/services/network/restricted_cookie_manager.h
+++ b/services/network/restricted_cookie_manager.h
@@ -337,6 +337,8 @@ class COMPONENT_EXPORT(NETWORK_SERVICE) RestrictedCookieManager
// update filtering.
CookieAccessesByURLAndSite recent_cookie_accesses_;
+ bool same_party_attribute_enabled_;
+
// This class can optionally bind its Receiver. If that's the case it's stored
// done with this variable.
mojo::Receiver<mojom::RestrictedCookieManager> receiver_;
diff --git a/services/network/url_loader.cc b/services/network/url_loader.cc
index 87c4c342355df9c5c7a5a01c095fc0d81365cb49..57ce6818a788acc036a3c89f030eda6cc2ebb305 100644
--- a/services/network/url_loader.cc
+++ b/services/network/url_loader.cc
@@ -291,7 +291,7 @@ bool ShouldNotifyAboutCookie(net::CookieInclusionStatus status) {
status.HasExclusionReason(
net::CookieInclusionStatus::EXCLUDE_USER_PREFERENCES) ||
status.HasExclusionReason(
- net::CookieInclusionStatus::EXCLUDE_THIRD_PARTY_PHASEOUT) ||
+ net::CookieInclusionStatus::EXCLUDE_INVALID_SAMEPARTY) ||
status.HasExclusionReason(
net::CookieInclusionStatus::EXCLUDE_DOMAIN_NON_ASCII);
}
@@ -636,6 +636,9 @@ URLLoader::URLLoader(
DCHECK(!url_request_->isolation_info().IsEmpty());
}
+ if (ShouldForceIgnoreTopFramePartyForCookies())
+ url_request_->set_force_ignore_top_frame_party_for_cookies(true);
+
// When a service worker forwards a navigation request it uses the
// service worker's IsolationInfo. This causes the cookie code to fail
// to send SameSite=Lax cookies for main-frame navigations passed through
@@ -2846,6 +2849,36 @@ bool URLLoader::ShouldForceIgnoreSiteForCookies(
return false;
}
+bool URLLoader::ShouldForceIgnoreTopFramePartyForCookies() const {
+ const net::IsolationInfo& isolation_info = url_request_->isolation_info();
+ const absl::optional<url::Origin>& top_frame_origin =
+ isolation_info.top_frame_origin();
+
+ if (!top_frame_origin || top_frame_origin->opaque())
+ return false;
+
+ const absl::optional<std::set<net::SchemefulSite>>& party_context =
+ isolation_info.party_context();
+ if (!party_context)
+ return false;
+
+ // The top frame origin must have access to the request URL.
+ if (cors::OriginAccessList::AccessState::kAllowed !=
+ origin_access_list_->CheckAccessState(*top_frame_origin,
+ url_request_->url())) {
+ return false;
+ }
+
+ // The top frame origin must have access to each site in the party_context.
+ return base::ranges::all_of(
+ *party_context,
+ [this, &top_frame_origin](const net::SchemefulSite& site) {
+ return origin_access_list_->CheckAccessState(*top_frame_origin,
+ site.GetURL()) ==
+ cors::OriginAccessList::AccessState::kAllowed;
+ });
+}
+
void URLLoader::SetRequestCredentials(const GURL& url) {
bool coep_allow_credentials = CoepAllowCredentials(url);
diff --git a/services/network/url_loader.h b/services/network/url_loader.h
index f06a802a31f3fa66d687d532532b29dc27651d14..9ee2dc0581e492b7d1395cbe0a4c24ca455c7f6a 100644
--- a/services/network/url_loader.h
+++ b/services/network/url_loader.h
@@ -549,6 +549,10 @@ class COMPONENT_EXPORT(NETWORK_SERVICE) URLLoader
// Whether `force_ignore_site_for_cookies` should be set on net::URLRequest.
bool ShouldForceIgnoreSiteForCookies(const ResourceRequest& request);
+ // Whether `force_ignore_top_frame_party_for_cookies` should be set on
+ // net::URLRequest.
+ bool ShouldForceIgnoreTopFramePartyForCookies() const;
+
// Applies Private Network Access checks to the current request.
//
// Helper for `OnConnected()`.
diff --git a/third_party/blink/common/storage_key/storage_key.cc b/third_party/blink/common/storage_key/storage_key.cc
index 5b863520514c1cb5011c6afa08d231a4047282e9..2712538e887b39035ac0b2718a98c5ee812087f9 100644
--- a/third_party/blink/common/storage_key/storage_key.cc
+++ b/third_party/blink/common/storage_key/storage_key.cc
@@ -788,7 +788,8 @@ const net::IsolationInfo StorageKey::ToPartialNetIsolationInfo() const {
: url::Origin::Create(top_level_site_.GetURL());
return net::IsolationInfo::Create(net::IsolationInfo::RequestType::kOther,
top_frame_origin, origin_,
- ToNetSiteForCookies(), nonce_);
+ ToNetSiteForCookies(),
+ /*party_context=*/absl::nullopt, nonce_);
}
// static
diff --git a/third_party/blink/renderer/modules/cookie_store/cookie_store.cc b/third_party/blink/renderer/modules/cookie_store/cookie_store.cc
index ba3ed0252770c90b83721752356d3e1146fcd9f5..15dff565c4841047f7625635345461ec2ffeeff3 100644
--- a/third_party/blink/renderer/modules/cookie_store/cookie_store.cc
+++ b/third_party/blink/renderer/modules/cookie_store/cookie_store.cc
@@ -167,7 +167,7 @@ std::unique_ptr<net::CanonicalCookie> ToCanonicalCookie(
path.Utf8(), base::Time() /*creation*/, expires,
base::Time() /*last_access*/, true /*secure*/, false /*http_only*/,
same_site, net::CookiePriority::COOKIE_PRIORITY_DEFAULT,
- cookie_partition_key, &status_out);
+ false /*same_party*/, cookie_partition_key, &status_out);
// TODO(crbug.com/1310444): Improve serialization validation comments and
// associate them with ExceptionState codes.