electron/patches/chromium/revert_same_party_cookie_at...

5411 lines
242 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 8736a91998f6b42e7fd30dbf1fa01a2249832fe5..41ea76a26292937d5f5a20f48bf261e081934ff3 100644
--- a/chrome/browser/about_flags.cc
+++ b/chrome/browser/about_flags.cc
@@ -9009,6 +9009,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 d9c675c8aca73a6a8411735a6c6a350795fa308f..36716e675892fa7aea585a1f5e0c2bb408454466 100644
--- a/chrome/browser/chrome_content_browser_client.cc
+++ b/chrome/browser/chrome_content_browser_client.cc
@@ -7900,8 +7900,7 @@ bool ChromeContentBrowserClient::ShouldDisableOriginAgentClusterDefault(
bool ChromeContentBrowserClient::WillProvidePublicFirstPartySets() {
#if BUILDFLAG(ENABLE_COMPONENT_UPDATER)
- return !is_minimal_mode_ &&
- !base::CommandLine::ForCurrentProcess()->HasSwitch(
+ return !base::CommandLine::ForCurrentProcess()->HasSwitch(
switches::kDisableComponentUpdate) &&
base::FeatureList::IsEnabled(features::kFirstPartySets);
#else
@@ -8198,10 +8197,6 @@ bool ChromeContentBrowserClient::
return true;
}
-void ChromeContentBrowserClient::SetIsMinimalMode(bool minimal) {
- is_minimal_mode_ = minimal;
-}
-
#if !BUILDFLAG(IS_ANDROID)
void ChromeContentBrowserClient::BindVideoEffectsManager(
const std::string& device_id,
diff --git a/chrome/browser/chrome_content_browser_client.h b/chrome/browser/chrome_content_browser_client.h
index 7c47a56574e7a89d960273a9e08a7959225bf700..49f7e434f451468bbceff87664bc5c05b1a0c25a 100644
--- a/chrome/browser/chrome_content_browser_client.h
+++ b/chrome/browser/chrome_content_browser_client.h
@@ -953,8 +953,6 @@ class ChromeContentBrowserClient : public content::ContentBrowserClient {
bool ShouldAllowBackForwardCacheForCacheControlNoStorePage(
content::BrowserContext* browser_context) override;
- void SetIsMinimalMode(bool minimal) override;
-
#if !BUILDFLAG(IS_ANDROID)
void BindVideoEffectsManager(
const std::string& device_id,
@@ -1117,10 +1115,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 af69cae172317e657ed069b4fb65162919d9f2d3..b0d202ff7047cc57644e675c46f48d4f5c9465c0 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");
@@ -155,8 +155,8 @@ void FirstPartySetsComponentInstallerPolicy::ComponentReady(
if (install_dir.empty() || GetConfigPathInstance().has_value())
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);
@@ -200,12 +200,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/first_party_sets/first_party_sets_policy_service.cc b/chrome/browser/first_party_sets/first_party_sets_policy_service.cc
index 869638cc9700b7253d574bf7075bd48d7539082a..12589a819c216213ea237f2ff397569420e2cbec 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
@@ -131,6 +131,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()) {
@@ -142,17 +143,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 absl::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());
@@ -163,7 +165,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 9bc786819114dc1f12e590afa3323f067c785fad..4a1915904119bd2b0a7027ffd5046404123a0311 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
@@ -50,6 +50,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
@@ -168,6 +169,7 @@ class FirstPartySetsPolicyService : public KeyedService {
void ComputeFirstPartySetMetadataInternal(
const net::SchemefulSite& site,
const absl::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 400505f9d64e64d762b0d6de67e7ca970391ec12..7ede5b8cd7576c77195897772baf7b30a4565dda 100644
--- a/chrome/browser/flag-metadata.json
+++ b/chrome/browser/flag-metadata.json
@@ -2677,6 +2677,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 f92c5df0fc6001dc1bbba183ff9c520b1bec1ab1..b8077e0e8b9d90fcfa6ed1262b438db7a91a892d 100644
--- a/chrome/browser/flag_descriptions.cc
+++ b/chrome/browser/flag_descriptions.cc
@@ -386,6 +386,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 kSHA1ServerSignatureName[] = "Allow SHA-1 server signatures in TLS.";
const char kSHA1ServerSignatureDescription[] =
"When enabled, Chrome will allow the use of SHA-1 in signatures from the "
diff --git a/chrome/browser/flag_descriptions.h b/chrome/browser/flag_descriptions.h
index 71eae84724eabc70afd1be087495f0f21b2b21b9..73f70d9df8697714f8fb58c0c66cef9d0bf234b9 100644
--- a/chrome/browser/flag_descriptions.h
+++ b/chrome/browser/flag_descriptions.h
@@ -506,6 +506,9 @@ extern const char kUseDnsHttpsSvcbAlpnDescription[];
extern const char kEditContextName[];
extern const char kEditContextDescription[];
+extern const char kEnableFirstPartySetsName[];
+extern const char kEnableFirstPartySetsDescription[];
+
extern const char kSHA1ServerSignatureName[];
extern const char kSHA1ServerSignatureDescription[];
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 ce5b56d772a81eb8cbad354bf53c8b53c459239f..be3f50d95024d1ef84857d047f0b1208710e72da 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
@@ -298,7 +298,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 e004114e17b2b8f6ca80c6e1e8711c6d1b15264b..7f157bd3fb974d711ba22fe211c9b2d33a4720e0 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
@@ -94,7 +94,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/content/app/content_main_runner_impl.cc b/content/app/content_main_runner_impl.cc
index 5b6c92952de30bb9a80b9595ab57344323484eea..caa5767ca6d042022c562006629d19dc8c3fdaac 100644
--- a/content/app/content_main_runner_impl.cc
+++ b/content/app/content_main_runner_impl.cc
@@ -60,8 +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/first_party_sets/local_set_declaration.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"
@@ -1256,14 +1254,8 @@ int ContentMainRunnerImpl::RunBrowser(MainFunctionParams main_params,
AndroidBatteryMetrics::CreateInstance();
#endif
- GetContentClient()->browser()->SetIsMinimalMode(start_minimal_browser);
- if (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(), LocalSetDeclaration());
- }
discardable_shared_memory_manager_ =
std::make_unique<discardable_memory::DiscardableSharedMemoryManager>();
diff --git a/content/browser/BUILD.gn b/content/browser/BUILD.gn
index 0757d84fc6da0be30d2497dae36b0170a33808f7..713190fa1b037df00c2f5e9761572873ea1a3538 100644
--- a/content/browser/BUILD.gn
+++ b/content/browser/BUILD.gn
@@ -999,9 +999,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_site_data_remover.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/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 0855f5f8d6d092de96e6b1dc7c2e49402351ea5b..caae433e3d14eae35c73873c6a1fcab74d3dcb2e 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 <string>
@@ -18,7 +18,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_site_data_remover.h"
#include "content/browser/first_party_sets/local_set_declaration.h"
@@ -79,16 +78,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) {
@@ -113,19 +111,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;
}
@@ -138,49 +134,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(
- 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());
- }
+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);
}
-FirstPartySetsHandlerImplInstance::~FirstPartySetsHandlerImplInstance() =
- default;
+FirstPartySetsHandlerImpl::FirstPartySetsHandlerImpl(
+ base::PassKey<ScopedMockFirstPartySetsHandler>,
+ bool enabled,
+ bool embedder_will_provide_public_sets)
+ : FirstPartySetsHandlerImpl(enabled, embedder_will_provide_public_sets) {}
-absl::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)));
}
@@ -188,33 +192,35 @@ FirstPartySetsHandlerImplInstance::GetSets(
return absl::nullopt;
}
-void FirstPartySetsHandlerImplInstance::Init(
- const base::FilePath& user_data_dir,
- const LocalSetDeclaration& local_set) {
+void FirstPartySetsHandlerImpl::Init(const base::FilePath& user_data_dir,
+ const 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;
}
@@ -222,7 +228,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(absl::optional<std::pair<net::GlobalFirstPartySets,
@@ -241,7 +247,7 @@ void FirstPartySetsHandlerImplInstance::GetPersistedSetsForTesting(
.Then(std::move(callback));
}
-void FirstPartySetsHandlerImplInstance::HasBrowserContextClearedForTesting(
+void FirstPartySetsHandlerImpl::HasBrowserContextClearedForTesting(
const std::string& browser_context_id,
base::OnceCallback<void(absl::optional<bool>)> callback) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
@@ -257,17 +263,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());
@@ -282,26 +289,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());
@@ -315,11 +318,9 @@ void FirstPartySetsHandlerImplInstance::InvokePendingQueries() {
queue.pop_front();
std::move(callback).Run();
}
- on_sets_ready_callbacks_.reset();
}
-absl::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 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 absl::optional<base::ElapsedTimer>& timer) const {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
@@ -522,22 +521,16 @@ FirstPartySetsHandlerImplInstance::GetContextConfigForPolicyInternal(
timer->Elapsed());
}
- if (!enabled_) {
- return net::FirstPartySetsContextConfig();
- }
-
auto [parsed, warnings] =
FirstPartySetParser::ParseSetsFromEnterprisePolicy(policy);
return parsed.has_value()
- ? global_sets_.value().ComputeConfig(
- /*replacement_sets=*/parsed.value().replacements,
- /*addition_sets=*/
- parsed.value().additions)
+ ? FirstPartySetsHandlerImpl::ComputeEnterpriseContextConfig(
+ global_sets_.value(), parsed.value())
: 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 9696bbb0281be8502c78d19de3ca7b9090f8771c..aec4e931eb4e21a660577432d12488c1316e2f2c 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,25 +5,64 @@
#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 <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/browser/first_party_sets/local_set_declaration.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 "third_party/abseil-cpp/absl/types/optional.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).
@@ -32,9 +71,14 @@ class CONTENT_EXPORT FirstPartySetsHandlerImpl : public FirstPartySetsHandler {
// persisted sets, since we may still need to clear data from a previous
// 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 LocalSetDeclaration& local_set) = 0;
+ // Must be called exactly once.
+ void Init(const base::FilePath& user_data_dir,
+ const 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 absl::optional if it's already
@@ -49,7 +93,163 @@ class CONTENT_EXPORT FirstPartySetsHandlerImpl : public FirstPartySetsHandler {
// If First-Party Sets is disabled, this returns a populated optional with an
// empty GlobalFirstPartySets instance.
[[nodiscard]] virtual absl::optional<net::GlobalFirstPartySets> GetSets(
- base::OnceCallback<void(net::GlobalFirstPartySets)> callback) = 0;
+ 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 a4f5090c8dfffdb8fca5e1e99862b4fa29134557..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 <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/browser/first_party_sets/local_set_declaration.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 "third_party/abseil-cpp/absl/types/optional.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 LocalSetDeclaration& local_set) override;
- [[nodiscard]] absl::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;
- 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 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
- }
-
- 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 absl::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 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_);
-
- // 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.
- 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
-
-#endif // CONTENT_BROWSER_FIRST_PARTY_SETS_FIRST_PARTY_SETS_HANDLER_IMPL_H_
diff --git a/content/browser/first_party_sets/first_party_sets_handler_impl_instance_unittest.cc b/content/browser/first_party_sets/first_party_sets_handler_impl_instance_unittest.cc
deleted file mode 100644
index 41c4400326de46d2fe623aa58c8495c50aff76e7..0000000000000000000000000000000000000000
--- a/content/browser/first_party_sets/first_party_sets_handler_impl_instance_unittest.cc
+++ /dev/null
@@ -1,925 +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.
-
-#include "content/browser/first_party_sets/first_party_sets_handler_impl_instance.h"
-
-#include <string>
-#include <utility>
-#include <vector>
-
-#include "base/files/file_path.h"
-#include "base/files/file_util.h"
-#include "base/files/scoped_temp_dir.h"
-#include "base/functional/callback_helpers.h"
-#include "base/json/json_reader.h"
-#include "base/notreached.h"
-#include "base/run_loop.h"
-#include "base/test/bind.h"
-#include "base/test/gmock_expected_support.h"
-#include "base/test/metrics/histogram_tester.h"
-#include "base/test/scoped_feature_list.h"
-#include "base/test/task_environment.h"
-#include "base/test/test_future.h"
-#include "base/version.h"
-#include "content/browser/first_party_sets/first_party_set_parser.h"
-#include "content/browser/first_party_sets/local_set_declaration.h"
-#include "content/public/browser/first_party_sets_handler.h"
-#include "content/public/common/content_features.h"
-#include "content/public/test/browser_task_environment.h"
-#include "content/public/test/test_browser_context.h"
-#include "net/base/schemeful_site.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/first_party_sets_context_config.h"
-#include "net/first_party_sets/global_first_party_sets.h"
-#include "testing/gmock/include/gmock/gmock-matchers.h"
-#include "testing/gmock/include/gmock/gmock.h"
-#include "testing/gtest/include/gtest/gtest.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
-#include "url/gurl.h"
-
-using ::testing::_;
-using ::testing::Eq;
-using ::testing::IsEmpty;
-using ::testing::Not;
-using ::testing::Optional;
-using ::testing::Pair;
-using ::testing::SizeIs;
-using ::testing::UnorderedElementsAre;
-
-// Some of these tests overlap with FirstPartySetParser unittests, but
-// overlapping test coverage isn't the worst thing.
-namespace content {
-
-namespace {
-
-using ParseErrorType = FirstPartySetsHandler::ParseErrorType;
-using ParseWarningType = FirstPartySetsHandler::ParseWarningType;
-
-constexpr char kAdditionsField[] = "additions";
-constexpr char kPrimaryField[] = "primary";
-constexpr char kCctldsField[] = "ccTLDs";
-
-constexpr char kFirstPartySetsClearSiteDataOutcomeHistogram[] =
- "FirstPartySets.Initialization.ClearSiteDataOutcome";
-
-constexpr char kDelayedQueriesCountHistogram[] =
- "Cookie.FirstPartySets.Browser.DelayedQueriesCount";
-constexpr char kMostDelayedQueryDeltaHistogram[] =
- "Cookie.FirstPartySets.Browser.MostDelayedQueryDelta";
-
-} // namespace
-
-TEST(FirstPartySetsHandlerImplInstance, ValidateEnterprisePolicy_ValidPolicy) {
- base::Value input = base::JSONReader::Read(R"(
- {
- "replacements": [
- {
- "primary": "https://primary1.test",
- "associatedSites": ["https://associatedsite1.test"]
- }
- ],
- "additions": [
- {
- "primary": "https://primary2.test",
- "associatedSites": ["https://associatedsite2.test"]
- }
- ]
- }
- )")
- .value();
- // Validation doesn't fail with an error and there are no warnings to output.
- auto [success, warnings] =
- FirstPartySetsHandler::ValidateEnterprisePolicy(input.GetDict());
- EXPECT_TRUE(success.has_value());
- EXPECT_THAT(warnings, IsEmpty());
-}
-
-TEST(FirstPartySetsHandlerImplInstance,
- ValidateEnterprisePolicy_ValidPolicyWithWarnings) {
- // Some input that matches our policies schema but returns non-fatal warnings.
- base::Value input = base::JSONReader::Read(R"(
- {
- "replacements": [],
- "additions": [
- {
- "primary": "https://primary1.test",
- "associatedSites": ["https://associatedsite1.test"],
- "ccTLDs": {
- "https://non-canonical.test": ["https://primary1.test"]
- }
- }
- ]
- }
- )")
- .value();
- // Validation succeeds without errors.
- auto [success, warnings] =
- FirstPartySetsHandler::ValidateEnterprisePolicy(input.GetDict());
- EXPECT_TRUE(success.has_value());
- // Outputs metadata that can be used to surface a descriptive warning.
- EXPECT_THAT(
- warnings,
- UnorderedElementsAre(FirstPartySetsHandler::ParseWarning(
- ParseWarningType::kCctldKeyNotCanonical,
- {kAdditionsField, 0, kCctldsField, "https://non-canonical.test"})));
-}
-
-TEST(FirstPartySetsHandlerImplInstance,
- ValidateEnterprisePolicy_InvalidPolicy) {
- // Some input that matches our policies schema but breaks FPS invariants.
- // For more test coverage, see the ParseSetsFromEnterprisePolicy unit tests.
- base::Value input = base::JSONReader::Read(R"(
- {
- "replacements": [
- {
- "primary": "https://primary1.test",
- "associatedSites": ["https://associatedsite1.test"]
- }
- ],
- "additions": [
- {
- "primary": "https://primary1.test",
- "associatedSites": ["https://associatedsite2.test"]
- }
- ]
- }
- )")
- .value();
- // Validation fails with an error and an appropriate ParseError is returned.
- EXPECT_THAT(
- FirstPartySetsHandler::ValidateEnterprisePolicy(input.GetDict()).first,
- base::test::ErrorIs(FirstPartySetsHandler::ParseError(
- ParseErrorType::kNonDisjointSets,
- {kAdditionsField, 0, kPrimaryField})));
-}
-
-class FirstPartySetsHandlerImplTest : public ::testing::Test {
- public:
- explicit FirstPartySetsHandlerImplTest(bool enabled)
- : handler_(FirstPartySetsHandlerImplInstance::CreateForTesting(
- /*enabled=*/enabled,
- /*embedder_will_provide_public_sets=*/enabled)) {
- CHECK(scoped_dir_.CreateUniqueTempDir());
- CHECK(PathExists(scoped_dir_.GetPath()));
- }
-
- base::File WritePublicSetsFile(base::StringPiece content) {
- base::FilePath path =
- scoped_dir_.GetPath().Append(FILE_PATH_LITERAL("sets_file.json"));
- CHECK(base::WriteFile(path, content));
-
- return base::File(path, base::File::FLAG_OPEN | base::File::FLAG_READ);
- }
-
- net::GlobalFirstPartySets GetSetsAndWait(
- FirstPartySetsHandlerImplInstance& handler) {
- base::test::TestFuture<net::GlobalFirstPartySets> future;
- absl::optional<net::GlobalFirstPartySets> result =
- handler.GetSets(future.GetCallback());
- return result.has_value() ? std::move(result).value() : future.Take();
- }
-
- net::FirstPartySetsContextConfig GetContextConfigForPolicy(
- const base::Value::Dict* policy) {
- base::test::TestFuture<net::FirstPartySetsContextConfig> future;
- handler().GetContextConfigForPolicy(policy, future.GetCallback());
- return future.Take();
- }
-
- void ClearSiteDataOnChangedSetsForContextAndWait(
- FirstPartySetsHandlerImplInstance& handler,
- BrowserContext* context,
- const std::string& browser_context_id,
- net::FirstPartySetsContextConfig context_config) {
- base::RunLoop run_loop;
- handler.ClearSiteDataOnChangedSetsForContext(
- base::BindLambdaForTesting([&]() { return context; }),
- browser_context_id, std::move(context_config),
- base::BindLambdaForTesting(
- [&](net::FirstPartySetsContextConfig,
- net::FirstPartySetsCacheFilter) { run_loop.Quit(); }));
- run_loop.Run();
- }
-
- absl::optional<
- std::pair<net::GlobalFirstPartySets, net::FirstPartySetsContextConfig>>
- GetPersistedSetsAndWait(FirstPartySetsHandlerImplInstance& handler,
- const std::string& browser_context_id) {
- base::test::TestFuture<absl::optional<
- std::pair<net::GlobalFirstPartySets, net::FirstPartySetsContextConfig>>>
- future;
- handler.GetPersistedSetsForTesting(browser_context_id,
- future.GetCallback());
- return future.Take();
- }
-
- absl::optional<bool> HasEntryInBrowserContextsClearedAndWait(
- FirstPartySetsHandlerImplInstance& handler,
- const std::string& browser_context_id) {
- base::test::TestFuture<absl::optional<bool>> future;
- handler.HasBrowserContextClearedForTesting(browser_context_id,
- future.GetCallback());
- return future.Take();
- }
-
- net::GlobalFirstPartySets GetSetsAndWait() {
- return GetSetsAndWait(handler());
- }
-
- void ClearSiteDataOnChangedSetsForContextAndWait(
- BrowserContext* context,
- const std::string& browser_context_id,
- net::FirstPartySetsContextConfig context_config) {
- ClearSiteDataOnChangedSetsForContextAndWait(
- handler(), context, browser_context_id, std::move(context_config));
- }
-
- absl::optional<
- std::pair<net::GlobalFirstPartySets, net::FirstPartySetsContextConfig>>
- GetPersistedSetsAndWait(const std::string& browser_context_id) {
- return GetPersistedSetsAndWait(handler(), browser_context_id);
- }
-
- base::HistogramTester& histogram_tester() { return histogram_tester_; }
-
- FirstPartySetsHandlerImplInstance& handler() { return handler_; }
-
- BrowserContext* context() { return &context_; }
-
- protected:
- base::ScopedTempDir scoped_dir_;
-
- private:
- BrowserTaskEnvironment env_;
- TestBrowserContext context_;
- base::HistogramTester histogram_tester_;
- FirstPartySetsHandlerImplInstance handler_;
-};
-
-class FirstPartySetsHandlerImplDisabledTest
- : public FirstPartySetsHandlerImplTest {
- public:
- FirstPartySetsHandlerImplDisabledTest()
- : FirstPartySetsHandlerImplTest(/*enabled=*/false) {}
-};
-
-TEST_F(FirstPartySetsHandlerImplDisabledTest, InitMetrics) {
- histogram_tester().ExpectTotalCount(kDelayedQueriesCountHistogram, 1);
- histogram_tester().ExpectTotalCount(kMostDelayedQueryDeltaHistogram, 1);
-}
-
-TEST_F(FirstPartySetsHandlerImplDisabledTest, InitImmediately) {
- // Should already be able to answer queries, even before Init is called.
- EXPECT_THAT(handler().GetSets(base::NullCallback()), Optional(_));
-
- EXPECT_EQ(GetContextConfigForPolicy(nullptr),
- net::FirstPartySetsContextConfig());
-
- base::Value policy = base::JSONReader::Read(R"(
- {
- "replacements": [
- {
- "primary": "https://primary.test",
- "associatedSites": ["https://associated.test"]
- }
- ]
- }
- )")
- .value();
- EXPECT_EQ(GetContextConfigForPolicy(&policy.GetDict()),
- net::FirstPartySetsContextConfig());
-
- // The local set declaration should be ignored, since the handler is disabled.
- handler().Init(
- /*user_data_dir=*/{},
- LocalSetDeclaration(
- R"({"primary": "https://example.test",)"
- R"("associatedSites": ["https://associatedsite1.test"]})"));
-
- // The public sets should be ignored, since the handler is disabled.
- handler().SetPublicFirstPartySets(
- base::Version("0.0.1"),
- WritePublicSetsFile(
- R"({"primary": "https://example.test", )"
- R"("associatedSites": ["https://associatedsite2.test"]})"));
-
- EXPECT_THAT(GetSetsAndWait().FindEntries(
- {
- net::SchemefulSite(GURL("https://example.test")),
- net::SchemefulSite(GURL("https://associatedsite1.test")),
- net::SchemefulSite(GURL("https://associatedsite2.test")),
- },
- net::FirstPartySetsContextConfig()),
- IsEmpty());
-}
-
-class FirstPartySetsHandlerImplEnabledTest
- : public FirstPartySetsHandlerImplTest {
- public:
- FirstPartySetsHandlerImplEnabledTest()
- : FirstPartySetsHandlerImplTest(/*enabled=*/true) {}
-};
-
-TEST_F(FirstPartySetsHandlerImplEnabledTest, EmptyDBPath) {
- net::SchemefulSite example(GURL("https://example.test"));
- net::SchemefulSite associated(GURL("https://associatedsite1.test"));
-
- handler().SetPublicFirstPartySets(base::Version("0.0.1"),
- WritePublicSetsFile(""));
-
- // Empty `user_data_dir` will fail to load persisted sets, but that will not
- // prevent `on_sets_ready` from being invoked.
- handler().Init(
- /*user_data_dir=*/{},
- LocalSetDeclaration(
- R"({"primary": "https://example.test",)"
- R"("associatedSites": ["https://associatedsite1.test"]})"));
-
- EXPECT_THAT(
- GetSetsAndWait().FindEntries({example, associated},
- net::FirstPartySetsContextConfig()),
- UnorderedElementsAre(
- Pair(example, net::FirstPartySetEntry(
- example, net::SiteType::kPrimary, absl::nullopt)),
- Pair(associated, net::FirstPartySetEntry(
- example, net::SiteType::kAssociated, 0))));
-}
-
-TEST_F(FirstPartySetsHandlerImplEnabledTest,
- ClearSiteDataOnChangedSetsForContext_FeatureNotEnabled) {
- base::test::ScopedFeatureList features;
- features.InitAndEnableFeatureWithParameters(
- features::kFirstPartySets,
- {{features::kFirstPartySetsClearSiteDataOnChangedSets.name, "false"}});
- base::HistogramTester histogram;
- net::SchemefulSite foo(GURL("https://foo.test"));
- net::SchemefulSite associated(GURL("https://associatedsite.test"));
-
- const std::string browser_context_id = "profile";
- const std::string input =
- R"({"primary": "https://foo.test", )"
- R"("associatedSites": ["https://associatedsite.test"]})";
- ASSERT_TRUE(base::JSONReader::Read(input));
- handler().SetPublicFirstPartySets(base::Version("0.0.1"),
- WritePublicSetsFile(input));
-
- handler().Init(scoped_dir_.GetPath(), LocalSetDeclaration());
- ASSERT_THAT(GetSetsAndWait().FindEntries({foo, associated},
- net::FirstPartySetsContextConfig()),
- UnorderedElementsAre(
- Pair(foo, net::FirstPartySetEntry(
- foo, net::SiteType::kPrimary, absl::nullopt)),
- Pair(associated, net::FirstPartySetEntry(
- foo, net::SiteType::kAssociated, 0))));
-
- histogram.ExpectTotalCount(kDelayedQueriesCountHistogram, 1);
- histogram.ExpectTotalCount(kMostDelayedQueryDeltaHistogram, 1);
-
- ClearSiteDataOnChangedSetsForContextAndWait(
- context(), browser_context_id, net::FirstPartySetsContextConfig());
-
- absl::optional<
- std::pair<net::GlobalFirstPartySets, net::FirstPartySetsContextConfig>>
- persisted = GetPersistedSetsAndWait(browser_context_id);
- EXPECT_TRUE(persisted.has_value());
- EXPECT_THAT(
- persisted->first.FindEntries({foo, associated}, persisted->second),
- IsEmpty());
- // Should not be recorded.
- histogram.ExpectTotalCount(kFirstPartySetsClearSiteDataOutcomeHistogram, 0);
-}
-
-TEST_F(FirstPartySetsHandlerImplEnabledTest,
- ClearSiteDataOnChangedSetsForContext_ManualSet_Successful) {
- base::test::ScopedFeatureList features;
- features.InitAndEnableFeatureWithParameters(
- features::kFirstPartySets,
- {{features::kFirstPartySetsClearSiteDataOnChangedSets.name, "true"}});
-
- net::SchemefulSite foo(GURL("https://foo.test"));
- net::SchemefulSite associated(GURL("https://associatedsite.test"));
- net::SchemefulSite associated2(GURL("https://associatedsite2.test"));
-
- const std::string browser_context_id = "profile";
-
- base::HistogramTester histogram;
- FirstPartySetsHandlerImplInstance handler =
- FirstPartySetsHandlerImplInstance::CreateForTesting(true, false);
- const std::string input =
- R"({"primary": "https://foo.test", )"
- R"("associatedSites": ["https://associatedsite.test"]})";
- ASSERT_TRUE(base::JSONReader::Read(input));
-
- handler.Init(scoped_dir_.GetPath(), LocalSetDeclaration(input));
-
- // Should not yet be recorded.
- histogram.ExpectTotalCount(kFirstPartySetsClearSiteDataOutcomeHistogram, 0);
- ClearSiteDataOnChangedSetsForContextAndWait(
- handler, context(), browser_context_id,
- net::FirstPartySetsContextConfig());
-
- absl::optional<
- std::pair<net::GlobalFirstPartySets, net::FirstPartySetsContextConfig>>
- persisted = GetPersistedSetsAndWait(handler, browser_context_id);
- EXPECT_TRUE(persisted.has_value());
- EXPECT_THAT(
- persisted->first.FindEntries({foo, associated}, persisted->second),
- UnorderedElementsAre(
- Pair(foo, net::FirstPartySetEntry(foo, net::SiteType::kPrimary,
- absl::nullopt)),
- Pair(associated,
- net::FirstPartySetEntry(foo, net::SiteType::kAssociated,
- absl::nullopt))));
- histogram.ExpectUniqueSample(kFirstPartySetsClearSiteDataOutcomeHistogram,
- /*sample=*/true, 1);
- histogram.ExpectTotalCount(kDelayedQueriesCountHistogram, 1);
- histogram.ExpectTotalCount(kMostDelayedQueryDeltaHistogram, 1);
-}
-
-TEST_F(FirstPartySetsHandlerImplEnabledTest,
- ClearSiteDataOnChangedSetsForContext_PublicSetsWithDiff_Successful) {
- base::test::ScopedFeatureList features;
- features.InitAndEnableFeatureWithParameters(
- features::kFirstPartySets,
- {{features::kFirstPartySetsClearSiteDataOnChangedSets.name, "true"}});
-
- net::SchemefulSite foo(GURL("https://foo.test"));
- net::SchemefulSite associated(GURL("https://associatedsite.test"));
- net::SchemefulSite associated2(GURL("https://associatedsite2.test"));
-
- const std::string browser_context_id = "profile";
-
- {
- base::HistogramTester histogram;
- FirstPartySetsHandlerImplInstance handler =
- FirstPartySetsHandlerImplInstance::CreateForTesting(true, true);
- const std::string input =
- R"({"primary": "https://foo.test", )"
- R"("associatedSites": ["https://associatedsite.test"]})";
- ASSERT_TRUE(base::JSONReader::Read(input));
- handler.SetPublicFirstPartySets(base::Version("0.0.1"),
- WritePublicSetsFile(input));
-
- handler.Init(scoped_dir_.GetPath(), LocalSetDeclaration());
-
- EXPECT_THAT(
- HasEntryInBrowserContextsClearedAndWait(handler, browser_context_id),
- Optional(false));
-
- // Should not yet be recorded.
- histogram.ExpectTotalCount(kFirstPartySetsClearSiteDataOutcomeHistogram, 0);
- ClearSiteDataOnChangedSetsForContextAndWait(
- handler, context(), browser_context_id,
- net::FirstPartySetsContextConfig());
- absl::optional<
- std::pair<net::GlobalFirstPartySets, net::FirstPartySetsContextConfig>>
- persisted = GetPersistedSetsAndWait(handler, browser_context_id);
- EXPECT_TRUE(persisted.has_value());
- EXPECT_THAT(
- persisted->first.FindEntries({foo, associated}, persisted->second),
- UnorderedElementsAre(
- Pair(foo, net::FirstPartySetEntry(foo, net::SiteType::kPrimary,
- absl::nullopt)),
- Pair(associated,
- net::FirstPartySetEntry(foo, net::SiteType::kAssociated,
- absl::nullopt))));
- EXPECT_THAT(
- HasEntryInBrowserContextsClearedAndWait(handler, browser_context_id),
- Optional(true));
-
- histogram.ExpectUniqueSample(kFirstPartySetsClearSiteDataOutcomeHistogram,
- /*sample=*/true, 1);
-
- // Make sure the database is closed properly before being opened again.
- handler.SynchronouslyResetDBHelperForTesting();
- }
-
- // Verify FPS transition clearing is working for non-empty sites-to-clear
- // list.
- {
- base::HistogramTester histogram;
- FirstPartySetsHandlerImplInstance handler =
- FirstPartySetsHandlerImplInstance::CreateForTesting(true, true);
- const std::string input =
- R"({"primary": "https://foo.test", )"
- R"("associatedSites": ["https://associatedsite2.test"]})";
- ASSERT_TRUE(base::JSONReader::Read(input));
- // The new public sets need to be associated with a different version.
- handler.SetPublicFirstPartySets(base::Version("0.0.2"),
- WritePublicSetsFile(input));
-
- handler.Init(scoped_dir_.GetPath(), LocalSetDeclaration());
-
- // Should not yet be recorded.
- histogram.ExpectTotalCount(kFirstPartySetsClearSiteDataOutcomeHistogram, 0);
- ClearSiteDataOnChangedSetsForContextAndWait(
- handler, context(), browser_context_id,
- net::FirstPartySetsContextConfig());
- absl::optional<
- std::pair<net::GlobalFirstPartySets, net::FirstPartySetsContextConfig>>
- persisted = GetPersistedSetsAndWait(handler, browser_context_id);
- EXPECT_TRUE(persisted.has_value());
- EXPECT_THAT(
- persisted->first.FindEntries({foo, associated2}, persisted->second),
- UnorderedElementsAre(
- Pair(foo, net::FirstPartySetEntry(foo, net::SiteType::kPrimary,
- absl::nullopt)),
- Pair(associated2,
- net::FirstPartySetEntry(foo, net::SiteType::kAssociated,
- absl::nullopt))));
- EXPECT_THAT(
- HasEntryInBrowserContextsClearedAndWait(handler, browser_context_id),
- Optional(true));
-
- histogram.ExpectUniqueSample(kFirstPartySetsClearSiteDataOutcomeHistogram,
- /*sample=*/true, 1);
- }
-}
-
-TEST_F(FirstPartySetsHandlerImplEnabledTest,
- ClearSiteDataOnChangedSetsForContext_EmptyDBPath) {
- base::test::ScopedFeatureList features;
- features.InitAndEnableFeatureWithParameters(
- features::kFirstPartySets,
- {{features::kFirstPartySetsClearSiteDataOnChangedSets.name, "true"}});
-
- base::HistogramTester histogram;
- net::SchemefulSite foo(GURL("https://foo.test"));
- net::SchemefulSite associated(GURL("https://associatedsite.test"));
-
- const std::string browser_context_id = "profile";
- const std::string input =
- R"({"primary": "https://foo.test", )"
- R"("associatedSites": ["https://associatedsite.test"]})";
- ASSERT_TRUE(base::JSONReader::Read(input));
- handler().SetPublicFirstPartySets(base::Version("0.0.1"),
- WritePublicSetsFile(input));
-
- handler().Init(
- /*user_data_dir=*/{}, LocalSetDeclaration());
- ASSERT_THAT(GetSetsAndWait().FindEntries({foo, associated},
- net::FirstPartySetsContextConfig()),
- UnorderedElementsAre(
- Pair(foo, net::FirstPartySetEntry(
- foo, net::SiteType::kPrimary, absl::nullopt)),
- Pair(associated, net::FirstPartySetEntry(
- foo, net::SiteType::kAssociated, 0))));
-
- ClearSiteDataOnChangedSetsForContextAndWait(
- context(), browser_context_id, net::FirstPartySetsContextConfig());
-
- EXPECT_EQ(GetPersistedSetsAndWait(browser_context_id), absl::nullopt);
- // Should not be recorded.
- histogram.ExpectTotalCount(kFirstPartySetsClearSiteDataOutcomeHistogram, 0);
- histogram.ExpectTotalCount(kDelayedQueriesCountHistogram, 1);
- histogram.ExpectTotalCount(kMostDelayedQueryDeltaHistogram, 1);
-}
-
-TEST_F(FirstPartySetsHandlerImplEnabledTest,
- ClearSiteDataOnChangedSetsForContext_BeforeSetsReady) {
- base::test::ScopedFeatureList features;
- features.InitAndEnableFeatureWithParameters(
- features::kFirstPartySets,
- {{features::kFirstPartySetsClearSiteDataOnChangedSets.name, "true"}});
-
- base::HistogramTester histogram;
-
- handler().Init(scoped_dir_.GetPath(), LocalSetDeclaration());
-
- const std::string browser_context_id = "profile";
- base::test::TestFuture<net::FirstPartySetsContextConfig,
- net::FirstPartySetsCacheFilter>
- future;
- handler().ClearSiteDataOnChangedSetsForContext(
- base::BindLambdaForTesting([&]() { return context(); }),
- browser_context_id, net::FirstPartySetsContextConfig(),
- future.GetCallback());
-
- handler().SetPublicFirstPartySets(
- base::Version("0.0.1"),
- WritePublicSetsFile(
- R"({"primary": "https://foo.test", )"
- R"("associatedSites": ["https://associatedsite.test"]})"));
-
- EXPECT_TRUE(future.Wait());
-
- net::SchemefulSite foo(GURL("https://foo.test"));
- net::SchemefulSite associated(GURL("https://associatedsite.test"));
-
- absl::optional<
- std::pair<net::GlobalFirstPartySets, net::FirstPartySetsContextConfig>>
- persisted = GetPersistedSetsAndWait(browser_context_id);
- EXPECT_TRUE(persisted.has_value());
- EXPECT_THAT(
- persisted->first.FindEntries({foo, associated}, persisted->second),
- UnorderedElementsAre(
- Pair(foo, net::FirstPartySetEntry(foo, net::SiteType::kPrimary,
- absl::nullopt)),
- Pair(associated,
- net::FirstPartySetEntry(foo, net::SiteType::kAssociated,
- absl::nullopt))));
- histogram.ExpectUniqueSample(kFirstPartySetsClearSiteDataOutcomeHistogram,
- /*sample=*/true, 1);
- histogram.ExpectTotalCount(kDelayedQueriesCountHistogram, 1);
- histogram.ExpectTotalCount(kMostDelayedQueryDeltaHistogram, 1);
-}
-
-TEST_F(FirstPartySetsHandlerImplEnabledTest,
- GetSetsIfEnabledAndReady_AfterSetsReady) {
- net::SchemefulSite example(GURL("https://example.test"));
- net::SchemefulSite associated(GURL("https://associatedsite.test"));
-
- const std::string input =
- R"({"primary": "https://example.test", )"
- R"("associatedSites": ["https://associatedsite.test"]})";
- ASSERT_TRUE(base::JSONReader::Read(input));
- handler().SetPublicFirstPartySets(base::Version("1.2.3"),
- WritePublicSetsFile(input));
-
- handler().Init(scoped_dir_.GetPath(), LocalSetDeclaration());
-
- // Wait until initialization is complete.
- GetSetsAndWait();
-
- EXPECT_THAT(
- handler()
- .GetSets(base::NullCallback())
- .value()
- .FindEntries({example, associated},
- net::FirstPartySetsContextConfig()),
- UnorderedElementsAre(
- Pair(example, net::FirstPartySetEntry(
- example, net::SiteType::kPrimary, absl::nullopt)),
- Pair(associated, net::FirstPartySetEntry(
- example, net::SiteType::kAssociated, 0))));
-}
-
-TEST_F(FirstPartySetsHandlerImplEnabledTest,
- GetSetsIfEnabledAndReady_BeforeSetsReady) {
- net::SchemefulSite example(GURL("https://example.test"));
- net::SchemefulSite associated(GURL("https://associatedsite.test"));
-
- // Call GetSets before the sets are ready, and before Init has been called.
- base::test::TestFuture<net::GlobalFirstPartySets> future;
- EXPECT_EQ(handler().GetSets(future.GetCallback()), absl::nullopt);
-
- handler().Init(scoped_dir_.GetPath(), LocalSetDeclaration());
-
- const std::string input =
- R"({"primary": "https://example.test", )"
- R"("associatedSites": ["https://associatedsite.test"]})";
- ASSERT_TRUE(base::JSONReader::Read(input));
- handler().SetPublicFirstPartySets(base::Version("1.2.3"),
- WritePublicSetsFile(input));
-
- EXPECT_THAT(
- future.Get().FindEntries({example, associated},
- net::FirstPartySetsContextConfig()),
- UnorderedElementsAre(
- Pair(example, net::FirstPartySetEntry(
- example, net::SiteType::kPrimary, absl::nullopt)),
- Pair(associated, net::FirstPartySetEntry(
- example, net::SiteType::kAssociated, 0))));
-
- EXPECT_THAT(
- handler()
- .GetSets(base::NullCallback())
- .value()
- .FindEntries({example, associated},
- net::FirstPartySetsContextConfig()),
- UnorderedElementsAre(
- Pair(example, net::FirstPartySetEntry(
- example, net::SiteType::kPrimary, absl::nullopt)),
- Pair(associated, net::FirstPartySetEntry(
- example, net::SiteType::kAssociated, 0))));
-}
-
-TEST_F(FirstPartySetsHandlerImplEnabledTest,
- ComputeFirstPartySetMetadata_SynchronousResult) {
- handler().Init(scoped_dir_.GetPath(), LocalSetDeclaration());
-
- handler().SetPublicFirstPartySets(
- base::Version("1.2.3"),
- WritePublicSetsFile(
- R"({"primary": "https://example.test", )"
- R"("associatedSites": ["https://associatedsite.test"]})"));
-
- // Exploit another helper to wait until the public sets file has been read.
- GetSetsAndWait();
-
- net::SchemefulSite example(GURL("https://example.test"));
- net::SchemefulSite associated(GURL("https://associatedsite.test"));
-
- base::test::TestFuture<net::FirstPartySetMetadata> future;
- handler().ComputeFirstPartySetMetadata(example, &associated,
- net::FirstPartySetsContextConfig(),
- future.GetCallback());
- EXPECT_TRUE(future.IsReady());
- EXPECT_NE(future.Take(), net::FirstPartySetMetadata());
-}
-
-TEST_F(FirstPartySetsHandlerImplEnabledTest,
- ComputeFirstPartySetMetadata_AsynchronousResult) {
- // Send query before the sets are ready.
- base::test::TestFuture<net::FirstPartySetMetadata> future;
- net::SchemefulSite example(GURL("https://example.test"));
- net::SchemefulSite associated(GURL("https://associatedsite.test"));
- handler().ComputeFirstPartySetMetadata(example, &associated,
- net::FirstPartySetsContextConfig(),
- future.GetCallback());
- EXPECT_FALSE(future.IsReady());
-
- handler().Init(scoped_dir_.GetPath(), LocalSetDeclaration());
-
- handler().SetPublicFirstPartySets(
- base::Version("1.2.3"),
- WritePublicSetsFile(
- R"({"primary": "https://example.test", )"
- R"("associatedSites": ["https://associatedsite.test"]})"));
-
- EXPECT_NE(future.Get(), net::FirstPartySetMetadata());
-}
-
-TEST_F(FirstPartySetsHandlerImplEnabledTest,
- ForEachEffectiveSetEntry_BeforeSetsReady) {
- net::SchemefulSite example(GURL("https://example.test"));
- net::SchemefulSite associated(GURL("https://associatedsite.test"));
-
- // Verifies calling ForEachEffectiveSetEntry before the sets are ready returns
- // false.
- EXPECT_FALSE(handler().ForEachEffectiveSetEntry(
- net::FirstPartySetsContextConfig(),
- [&](const net::SchemefulSite& site,
- const net::FirstPartySetEntry& entry) {
- NOTREACHED_NORETURN();
- return true;
- }));
-
- handler().Init(scoped_dir_.GetPath(), LocalSetDeclaration());
-
- const std::string input =
- R"({"primary": "https://example.test", )"
- R"("associatedSites": ["https://associatedsite.test"]})";
- ASSERT_TRUE(base::JSONReader::Read(input));
- handler().SetPublicFirstPartySets(base::Version("1.2.3"),
- WritePublicSetsFile(input));
- // Wait for initialization is done.
- GetSetsAndWait();
-
- std::vector<std::pair<net::SchemefulSite, net::FirstPartySetEntry>>
- set_entries;
- EXPECT_TRUE(handler().ForEachEffectiveSetEntry(
- net::FirstPartySetsContextConfig(),
- [&](const net::SchemefulSite& site,
- const net::FirstPartySetEntry& entry) {
- set_entries.emplace_back(site, entry);
- return true;
- }));
- EXPECT_THAT(
- set_entries,
- UnorderedElementsAre(
- Pair(example, net::FirstPartySetEntry(
- example, net::SiteType::kPrimary, absl::nullopt)),
- Pair(associated, net::FirstPartySetEntry(
- example, net::SiteType::kAssociated, 0))));
-}
-
-TEST_F(FirstPartySetsHandlerImplEnabledTest,
- ForEachEffectiveSetEntry_WithNonEmptyConfig) {
- net::SchemefulSite example(GURL("https://example.test"));
- net::SchemefulSite associated1(GURL("https://associatedsite1.test"));
- net::SchemefulSite associated2(GURL("https://associatedsite2.test"));
-
- handler().Init(scoped_dir_.GetPath(), LocalSetDeclaration());
-
- const std::string input =
- R"({"primary": "https://example.test", )"
- R"("associatedSites": ["https://associatedsite1.test"]})";
- ASSERT_TRUE(base::JSONReader::Read(input));
- handler().SetPublicFirstPartySets(base::Version("1.2.3"),
- WritePublicSetsFile(input));
- // Wait for initialization is done.
- GetSetsAndWait();
-
- std::vector<std::pair<net::SchemefulSite, net::FirstPartySetEntry>>
- set_entries;
- // Calling ForEachEffectiveSetEntry with context config which add a new
- // associated site https://associatedsite2.test to the above set.
- EXPECT_TRUE(handler().ForEachEffectiveSetEntry(
- net::FirstPartySetsContextConfig(
- {{associated2,
- net::FirstPartySetEntryOverride(net::FirstPartySetEntry(
- example, net::SiteType::kAssociated, absl::nullopt))}}),
- [&](const net::SchemefulSite& site,
- const net::FirstPartySetEntry& entry) {
- set_entries.emplace_back(site, entry);
- return true;
- }));
- EXPECT_THAT(
- set_entries,
- UnorderedElementsAre(
- Pair(example, net::FirstPartySetEntry(
- example, net::SiteType::kPrimary, absl::nullopt)),
- Pair(associated1,
- net::FirstPartySetEntry(example, net::SiteType::kAssociated, 0)),
- Pair(associated2,
- net::FirstPartySetEntry(example, net::SiteType::kAssociated,
- absl::nullopt))));
-}
-
-class FirstPartySetsHandlerGetContextConfigForPolicyTest
- : public FirstPartySetsHandlerImplEnabledTest {
- public:
- FirstPartySetsHandlerGetContextConfigForPolicyTest() {
- handler().Init(scoped_dir_.GetPath(), LocalSetDeclaration());
- }
-
- // Writes the public list of First-Party Sets which GetContextConfigForPolicy
- // awaits.
- //
- // Initializes the First-Party Sets with the following relationship:
- //
- // [
- // {
- // "primary": "https://primary1.test",
- // "associatedSites": ["https://associatedsite1.test",
- // "https://associatedsite2.test"]
- // }
- // ]
- void InitPublicFirstPartySets() {
- net::SchemefulSite primary1(GURL("https://primary1.test"));
- net::SchemefulSite associated1(GURL("https://associatedsite1.test"));
- net::SchemefulSite associated2(GURL("https://associatedsite2.test"));
-
- const std::string input =
- R"({"primary": "https://primary1.test", )"
- R"("associatedSites": ["https://associatedsite1.test", "https://associatedsite2.test"]})";
- ASSERT_TRUE(base::JSONReader::Read(input));
- handler().SetPublicFirstPartySets(base::Version("1.2.3"),
- WritePublicSetsFile(input));
-
- ASSERT_THAT(
- GetSetsAndWait().FindEntries({primary1, associated1, associated2},
- net::FirstPartySetsContextConfig()),
- SizeIs(3));
- }
-};
-
-TEST_F(FirstPartySetsHandlerGetContextConfigForPolicyTest,
- DefaultOverridesPolicy_DefaultContextConfigs) {
- base::Value policy = base::JSONReader::Read(R"({})").value();
- base::test::TestFuture<net::FirstPartySetsContextConfig> future;
- handler().GetContextConfigForPolicy(&policy.GetDict(), future.GetCallback());
-
- InitPublicFirstPartySets();
- EXPECT_EQ(future.Take(), net::FirstPartySetsContextConfig());
-}
-
-TEST_F(FirstPartySetsHandlerGetContextConfigForPolicyTest,
- MalformedOverridesPolicy_DefaultContextConfigs) {
- base::Value policy = base::JSONReader::Read(R"({
- "replacements": 123,
- "additions": true
- })")
- .value();
- base::test::TestFuture<net::FirstPartySetsContextConfig> future;
- handler().GetContextConfigForPolicy(&policy.GetDict(), future.GetCallback());
-
- InitPublicFirstPartySets();
- EXPECT_EQ(future.Take(), net::FirstPartySetsContextConfig());
-}
-
-TEST_F(FirstPartySetsHandlerGetContextConfigForPolicyTest,
- NonDefaultOverridesPolicy_NonDefaultContextConfigs) {
- base::Value policy = base::JSONReader::Read(R"(
- {
- "replacements": [
- {
- "primary": "https://associatedsite1.test",
- "associatedSites": ["https://primary3.test"]
- }
- ],
- "additions": [
- {
- "primary": "https://primary2.test",
- "associatedSites": ["https://associatedsite2.test"]
- }
- ]
- }
- )")
- .value();
- base::test::TestFuture<net::FirstPartySetsContextConfig> future;
- handler().GetContextConfigForPolicy(&policy.GetDict(), future.GetCallback());
-
- InitPublicFirstPartySets();
- // We don't care what the customizations are, here; we only care that they're
- // not a no-op.
- EXPECT_FALSE(future.Take().empty());
- EXPECT_EQ(GetContextConfigForPolicy(nullptr),
- net::FirstPartySetsContextConfig());
-}
-
-} // namespace content
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 eecd574ad30bc6e19b33eabbf2609890f72af286..ff8299ab75711411a94b6ecf54a141b1cfd548a0 100644
--- a/content/browser/first_party_sets/first_party_sets_loader.cc
+++ b/content/browser/first_party_sets/first_party_sets_loader.cc
@@ -31,6 +31,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(
@@ -44,9 +57,6 @@ FirstPartySetsLoader::~FirstPartySetsLoader() {
void FirstPartySetsLoader::SetManuallySpecifiedSet(
const 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",
@@ -79,20 +89,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 46b50bb18cec8562427af581f57e525ca48a5f95..0c51c9baa6fc0c43bba60dbb9e5d6df61a615374 100644
--- a/content/browser/first_party_sets/first_party_sets_loader.h
+++ b/content/browser/first_party_sets/first_party_sets_loader.h
@@ -35,7 +35,7 @@ class CONTENT_EXPORT FirstPartySetsLoader {
FirstPartySetsLoader& operator=(const FirstPartySetsLoader&) = delete;
// Stores the First-Party Set that was provided via the `kUseFirstPartySet`
- // flag/switch. Only the first call has any effect.
+ // flag/switch.
void SetManuallySpecifiedSet(const LocalSetDeclaration& local_set);
// Asynchronously parses and stores the sets from `sets_file`, and merges with
@@ -46,9 +46,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/first_party_sets/first_party_sets_loader_unittest.cc b/content/browser/first_party_sets/first_party_sets_loader_unittest.cc
index dd86cf08266ba537f2443a0188d516419d35d01c..51b9c6b7b82a84bab0a7945caaadd47f697dcd5b 100644
--- a/content/browser/first_party_sets/first_party_sets_loader_unittest.cc
+++ b/content/browser/first_party_sets/first_party_sets_loader_unittest.cc
@@ -162,30 +162,4 @@ TEST_F(FirstPartySetsLoaderTest, SetsManuallySpecified) {
net::SiteType::kAssociated, 0)));
}
-TEST_F(FirstPartySetsLoaderTest, SetsManuallySpecified_Idempotent) {
- loader().SetManuallySpecifiedSet(LocalSetDeclaration(
- R"({"primary": "https://bar.test",)"
- R"("associatedSites": ["https://associatedsite1.test"]})"));
-
- // All but the first SetManuallySpecifiedSet call should be ignored.
- loader().SetManuallySpecifiedSet(LocalSetDeclaration(
- R"({"primary": "https://bar.test",)"
- R"("associatedSites": ["https://associatedsite2.test"]})"));
-
- SetComponentSets(loader(), base::Version(), "");
-
- const net::SchemefulSite associated1(GURL("https://associatedsite1.test"));
-
- EXPECT_THAT(WaitAndGetResult().FindEntries(
- {
- associated1,
- net::SchemefulSite(GURL("https://associatedsite2.test")),
- },
- net::FirstPartySetsContextConfig()),
- UnorderedElementsAre(Pair(
- associated1, net::FirstPartySetEntry(
- net::SchemefulSite(GURL("https://bar.test")),
- net::SiteType::kAssociated, 0))));
-}
-
} // namespace content
diff --git a/content/browser/media/android/media_resource_getter_impl.cc b/content/browser/media/android/media_resource_getter_impl.cc
index d169b757c5e531328ba6ed33f6fe36f9b532f633..20fe591ed1f518bf1afc268f1d366bc3358af67d 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 79bf9ececffae5ae45ee7a6c6f57e8856116cfaa..3d6d0a37926a7f5f9f7e1f089f1095c4dc0178ab 100644
--- a/content/browser/renderer_host/cookie_utils.cc
+++ b/content/browser/renderer_host/cookie_utils.cc
@@ -178,6 +178,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(
@@ -203,6 +205,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;
@@ -247,6 +253,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(
@@ -346,6 +368,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 f1510ce378fe9aef9d58a943c0ab3173a23ed636..ade645c625be61660bca0c9ed6f20773547bc4b7 100644
--- a/content/browser/renderer_host/render_frame_host_impl.cc
+++ b/content/browser/renderer_host/render_frame_host_impl.cc
@@ -4123,7 +4123,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.
@@ -4141,6 +4144,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);
}
@@ -4158,7 +4164,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);
}
absl::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 c526f29785e3523cb173457223a2fecc494cb5df..e6707d9c23466f943abbfa54a9e50c9b05e142c2 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
@@ -217,7 +217,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 1700732bf1a9becc6b4d9613250fccb26403e217..2919285638cf5f46f6d22587fc36e47a06f0fea3 100644
--- a/content/browser/worker_host/shared_worker_host.cc
+++ b/content/browser/worker_host/shared_worker_host.cc
@@ -426,7 +426,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 4aa539bce808abef08959f547d02d3aed53959bb..0ebc898a7e94dac56bf1b580e755be6c92b150f5 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 caeb4874b8bf85f445a5aa2eab4e96916f16de69..983e423adaff9e75e965277a2bd82413e7c54005 100644
--- a/content/public/browser/content_browser_client.h
+++ b/content/public/browser/content_browser_client.h
@@ -1915,7 +1915,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.
//
@@ -2725,10 +2726,6 @@ class CONTENT_EXPORT ContentBrowserClient {
virtual bool ShouldAllowBackForwardCacheForCacheControlNoStorePage(
content::BrowserContext* browser_context);
- // Set whether the browser is running in minimal mode (where most subsystems
- // are left uninitialized).
- virtual void SetIsMinimalMode(bool minimal) {}
-
#if !BUILDFLAG(IS_ANDROID)
// Allows the embedder to correlate backend media services with profile-keyed
// effect settings.
diff --git a/content/public/browser/first_party_sets_handler.h b/content/public/browser/first_party_sets_handler.h
index 55dbe0afcfae7df771f17dc5ce56efd096a083a3..3e531dd05fc2268c93cb63f8f0bbaeb36e2371e2 100644
--- a/content/public/browser/first_party_sets_handler.h
+++ b/content/public/browser/first_party_sets_handler.h
@@ -197,6 +197,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/net/BUILD.gn b/net/BUILD.gn
index e745a74d0446ea55c9e22cd7628ae0d6ac8a3982..eeff6d53e392f9ba90521ebcc94f7162cf53a7fa 100644
--- a/net/BUILD.gn
+++ b/net/BUILD.gn
@@ -584,6 +584,8 @@ component("net") {
"first_party_sets/first_party_sets_context_config.h",
"first_party_sets/global_first_party_sets.cc",
"first_party_sets/global_first_party_sets.h",
+ "first_party_sets/same_party_context.cc",
+ "first_party_sets/same_party_context.h",
"http/alternative_service.cc",
"http/alternative_service.h",
"http/bidirectional_stream.cc",
diff --git a/net/base/features.cc b/net/base/features.cc
index 3053df9162e31c7abeadb6e2f0b634ddda148355..159f91fc38aad31742add00ea5b1e16a0811911c 100644
--- a/net/base/features.cc
+++ b/net/base/features.cc
@@ -243,6 +243,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 af1b135ef9bf4a0dae6292a4d952187f7c345f70..a951f3a27046fa3737e28eddfe0189b6b82a4aca 100644
--- a/net/base/features.h
+++ b/net/base/features.h
@@ -297,6 +297,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 e337af60aa40bd31dd8d7549d963719278298c37..aac71c1c5d8c5892c98ee327198f0276b03e66e5 100644
--- a/net/base/isolation_info.cc
+++ b/net/base/isolation_info.cc
@@ -48,11 +48,13 @@ bool IsConsistent(IsolationInfo::RequestType request_type,
const absl::optional<url::Origin>& top_frame_origin,
const absl::optional<url::Origin>& frame_origin,
const SiteForCookies& site_for_cookies,
+ absl::optional<std::set<SchemefulSite>> party_context,
const absl::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=*/absl::nullopt,
/*frame_origin=*/absl::nullopt,
SiteForCookies(),
- /*nonce=*/absl::nullopt) {}
+ /*nonce=*/absl::nullopt,
+ /*party_context=*/absl::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=*/absl::nullopt);
+ /*nonce=*/absl::nullopt,
+ /*party_context=*/std::set<SchemefulSite>());
}
IsolationInfo IsolationInfo::CreateTransient() {
url::Origin opaque_origin;
return IsolationInfo(RequestType::kOther, opaque_origin, opaque_origin,
- SiteForCookies(), /*nonce=*/absl::nullopt);
+ SiteForCookies(), /*nonce=*/absl::nullopt,
+ /*party_context=*/absl::nullopt);
}
absl::optional<IsolationInfo> IsolationInfo::Deserialize(
@@ -130,11 +138,19 @@ absl::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=*/absl::nullopt);
+ std::move(party_context), /*nonce=*/absl::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,
+ absl::optional<std::set<SchemefulSite>> party_context,
const absl::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=*/absl::nullopt, nonce);
// TODO(crbug/1343856): DCHECK isolation info is fully populated.
return isolation_info;
}
@@ -183,13 +201,14 @@ absl::optional<IsolationInfo> IsolationInfo::CreateIfConsistent(
const absl::optional<url::Origin>& top_frame_origin,
const absl::optional<url::Origin>& frame_origin,
const SiteForCookies& site_for_cookies,
+ absl::optional<std::set<SchemefulSite>> party_context,
const absl::optional<base::UnguessableToken>& nonce) {
if (!IsConsistent(request_type, top_frame_origin, frame_origin,
- site_for_cookies, nonce)) {
+ site_for_cookies, party_context, nonce)) {
return absl::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 absl::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();
@@ -313,7 +354,8 @@ IsolationInfo::IsolationInfo(
const absl::optional<url::Origin>& top_frame_origin,
const absl::optional<url::Origin>& frame_origin,
const SiteForCookies& site_for_cookies,
- const absl::optional<base::UnguessableToken>& nonce)
+ const absl::optional<base::UnguessableToken>& nonce,
+ absl::optional<std::set<SchemefulSite>> party_context)
: request_type_(request_type),
top_frame_origin_(top_frame_origin),
frame_origin_(frame_origin),
@@ -328,9 +370,13 @@ IsolationInfo::IsolationInfo(
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 8de58d607dcc6835027bc8f0295a2f5cc783e5f4..dc45e9354fe024609dc65e84b3c4e8b2cb53b500 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,
+ absl::optional<std::set<SchemefulSite>> party_context = absl::nullopt,
const absl::optional<base::UnguessableToken>& nonce = absl::nullopt);
// TODO(crbug/1372769): Remove this and create a safer way to ensure NIKs
@@ -132,6 +137,7 @@ class NET_EXPORT IsolationInfo {
const absl::optional<url::Origin>& top_frame_origin,
const absl::optional<url::Origin>& frame_origin,
const SiteForCookies& site_for_cookies,
+ absl::optional<std::set<SchemefulSite>> party_context = absl::nullopt,
const absl::optional<base::UnguessableToken>& nonce = absl::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 absl::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 absl::optional<url::Origin>& top_frame_origin,
const absl::optional<url::Origin>& frame_origin,
const SiteForCookies& site_for_cookies,
- const absl::optional<base::UnguessableToken>& nonce);
+ const absl::optional<base::UnguessableToken>& nonce,
+ absl::optional<std::set<SchemefulSite>> party_context);
RequestType request_type_;
@@ -211,7 +227,26 @@ class NET_EXPORT IsolationInfo {
// for non-opaque origins.
absl::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 653f2acf2d53fdc45944271972c6a42cf69329f4..7b08eecb6d4a00797fc2b11ecb7b7d9fee2dbd1c 100644
--- a/net/base/network_delegate.cc
+++ b/net/base/network_delegate.cc
@@ -165,6 +165,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 c9c6cf72a5303f7d2848e5e09aee2c38cc3c2db4..ba2df571f61556dc1239d557fa7fafcaa6ea110d 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 {
@@ -120,6 +121,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
@@ -288,6 +303,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 c14f48ddf635784492c030a8a0089eacbc83b35c..a8ed45ed263ea1a7592aa48f4cd0081155ead17c 100644
--- a/net/base/network_delegate_impl.cc
+++ b/net/base/network_delegate_impl.cc
@@ -97,4 +97,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 a43507f98641a626712ccfa1549512abca908502..9bc72d4ef0ab2cb7d52ddd3cf484c2933829da58 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;
@@ -99,6 +100,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 b81e1656003f7676c2f0151612cec9648ebd9cbc..2e34df420cc6aaf59d3e638af7cd0cbcfa99afc3 100644
--- a/net/cookies/canonical_cookie.cc
+++ b/net/cookies/canonical_cookie.cc
@@ -340,9 +340,11 @@ bool HasValidHostPrefixAttributes(const GURL& url,
} // 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;
@@ -615,6 +617,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);
@@ -880,6 +888,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=*/
@@ -1200,11 +1212,56 @@ CookieAccessResult CanonicalCookie::IncludeForRequestURL(
CookieInclusionStatus::EXCLUDE_SAMESITE_NONE_INSECURE);
}
- // 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 */);
+ 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",
@@ -1374,11 +1431,59 @@ CookieAccessResult CanonicalCookie::IsSetPermittedInContext(
break;
}
- // 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 */);
+ 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",
@@ -1485,6 +1590,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;
@@ -1731,6 +1839,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 56b919c8e965182e2e5621ee123e8afea3565e36..9dbc2a30c85c3e70e7a995e1bc8336b49e2ed7a8 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 {
@@ -565,6 +570,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.
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 5be49154795380ee37258a9f2d24256d406af03f..4fba69d9ea8c9e94f8f8c75536584089589cd633 100644
--- a/net/cookies/cookie_inclusion_status.cc
+++ b/net/cookies/cookie_inclusion_status.cc
@@ -244,6 +244,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,
@@ -293,6 +296,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 1eefc3db602f5bb419ad3dbcb167dcd9742e1114..87ad6f7049560ac7f17a23047d983e4b17ec5bfa 100644
--- a/net/cookies/cookie_inclusion_status.h
+++ b/net/cookies/cookie_inclusion_status.h
@@ -109,6 +109,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
@@ -228,6 +234,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 0c3ce2e289b5944d38315790cca829e851c31e19..5c4352bb9fc1fd85373ee413b59a12967bd02710 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,
@@ -1205,8 +1211,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
@@ -1549,7 +1558,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 3ca2c691cfd00acf34f7648dfc6c9f6692be470a..6086953c99f55bd8a1034f0fa33ea77e4af9ec0d 100644
--- a/net/cookies/cookie_monster.h
+++ b/net/cookies/cookie_monster.h
@@ -734,6 +734,8 @@ class NET_EXPORT CookieMonster : public CookieStore {
// nonce.
size_t num_nonced_partitioned_cookie_bytes_ = 0u;
+ 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 183c1f61c1fbe94c8c85dd4f9a27695a04aad816..fc7cddad43a74945d5af6c2100893720f3895734 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 {
@@ -266,6 +269,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
@@ -281,6 +304,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 b479ab1deb4c54d14ccf87c1d923f6ab7fad6876..e2670576d72bfcc2267d8cc9b1865210d8162a4f 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"
@@ -894,24 +894,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
@@ -940,6 +939,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 d7072331e89a898ea2e8940117200274d01424ce..1b81e660ab8e699b326f85ca0bdb653ca3cfc806 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"
@@ -287,27 +286,37 @@ NET_EXPORT bool IsSchemeBoundCookiesEnabled();
// 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/first_party_sets/first_party_set_metadata.cc b/net/first_party_sets/first_party_set_metadata.cc
index 75f41ad79ac067f806d4bea8b687a4781778e37a..26521fa5f9b4a93f892212cbd45e77ce41fc0f29 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;
@@ -26,8 +28,8 @@ FirstPartySetMetadata::~FirstPartySetMetadata() = default;
bool FirstPartySetMetadata::operator==(
const FirstPartySetMetadata& other) const {
- return std::tie(frame_entry_, top_frame_entry_) ==
- std::tie(other.frame_entry_, other.top_frame_entry_);
+ return std::tie(context_, frame_entry_, top_frame_entry_) ==
+ std::tie(other.context_, other.frame_entry_, other.top_frame_entry_);
}
bool FirstPartySetMetadata::operator!=(
@@ -37,7 +39,8 @@ bool FirstPartySetMetadata::operator!=(
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 29d34cd442ae19006d399b09b766c19dd5b12c76..d90a01fde8f9692825e5b7bb0c90c32b48d60e65 100644
--- a/net/first_party_sets/global_first_party_sets.cc
+++ b/net/first_party_sets/global_first_party_sets.cc
@@ -28,6 +28,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) {
@@ -59,6 +66,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;
@@ -126,9 +138,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());
@@ -136,7 +150,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
@@ -144,9 +158,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;
@@ -172,16 +186,48 @@ GlobalFirstPartySets::FindEntries(
FirstPartySetMetadata GlobalFirstPartySets::ComputeMetadata(
const SchemefulSite& site,
const SchemefulSite* top_frame_site,
+ const std::set<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<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 base::flat_map<SchemefulSite, FirstPartySetEntry>& manual_entries) {
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 f4f4e99189e0f3b8accfa884e136a127d0128bab..cc2b42c89deb47fda879a5bef2c4a7f9989c139d 100644
--- a/net/first_party_sets/global_first_party_sets.h
+++ b/net/first_party_sets/global_first_party_sets.h
@@ -83,6 +83,7 @@ class NET_EXPORT GlobalFirstPartySets {
FirstPartySetMetadata ComputeMetadata(
const SchemefulSite& site,
const SchemefulSite* top_frame_site,
+ const std::set<SchemefulSite>& party_context,
const FirstPartySetsContextConfig& fps_context_config) const;
// Modifies this instance such that it will respect the given
@@ -166,6 +167,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<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 b5e18cf187f1ca0618eaac4b1453fa4ffdb3bb9d..d4a5e60a60f372fccbf54ead93666f9856b972de 100644
--- a/net/url_request/url_request.cc
+++ b/net/url_request/url_request.cc
@@ -1216,7 +1216,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 acd9a595a6c83bf58153354da91cb1d23e0f7235..9668aed7e3dde807e040ab71b442d81ca7e1c9b7 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.
@@ -961,6 +971,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 fcb821c562aa1ff431565705b22791076fd9429b..624e78829ca0af70bba8681c18e032e0c08ed691 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"
@@ -166,11 +166,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;
}
@@ -366,33 +377,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;
@@ -724,7 +755,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,
@@ -938,7 +973,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 a629e89cef3675c4ffe1ad56e17d4d595a51e3bd..78c8fbb6bfa39535c4fc2ae49dafa5a4ad001cd5 100644
--- a/net/url_request/url_request_http_job.h
+++ b/net/url_request/url_request_http_job.h
@@ -216,7 +216,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/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 82829ecd4005812b9973fbf4bd1a1fcee7ed5d5e..f2bdb481687b5e37fcaa5f6b12c9d754fb4f653f 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"
@@ -330,6 +331,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 cfcd91f7e47e4d6869c97ba1eb9dd58e5b69cfff..ea8be1d29ad4ab384df0cf451d155551a6455842 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
@@ -92,6 +93,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 5269c91580bc8d9cf93c3323f7b96ac748cd5972..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;
}
diff --git a/services/network/public/cpp/cookie_manager_mojom_traits.h b/services/network/public/cpp/cookie_manager_mojom_traits.h
index 5531222e359de032cb45b28e3451b5622a731069..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);
};
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 0c20fd9b054b6bdf989adde7d9c03f4f784215d9..4d1e584278722eb449012ef4f2f8f2162223711f 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 b255f56ead022bb237296e7560c73f692a7d7ee3..644aab23aef7fb37c573add9e4180b743c755a12 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 74ef53f30788fbc4a308b189562b3c63ac824d44..183d9a63935ea2cb6b8b53af0f9e97d83dc94218 100644
--- a/services/network/public/mojom/BUILD.gn
+++ b/services/network/public/mojom/BUILD.gn
@@ -1017,6 +1017,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 7aa887883985c86bb6ceda023225a35d1bc07f38..ee009f2425455d4617d21af0be3e47a7b695665d 100644
--- a/services/network/public/mojom/cookie_manager.mojom
+++ b/services/network/public/mojom/cookie_manager.mojom
@@ -165,6 +165,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.
diff --git a/services/network/public/mojom/first_party_sets.mojom b/services/network/public/mojom/first_party_sets.mojom
index 0ea745cd2450c2d92002c3a0113295b0d4bbd40e..150fed986ded1c82ca3b965c0218193dc938d7a1 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 e12ebed92ffb4018f5ea3bb0a2da6e32aeb5ce5c..0f1816c1a7912829433fa6063fb3b69bd81aeda0 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"
@@ -106,11 +105,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);
@@ -126,6 +134,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;
}
@@ -134,7 +150,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 =
@@ -152,6 +170,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;
}
@@ -170,21 +196,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()(
@@ -316,7 +334,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),
@@ -324,7 +343,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(
@@ -360,11 +380,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;
}
@@ -415,6 +440,8 @@ class RestrictedCookieManager::Listener : public base::LinkNode<Listener> {
mojo::Remote<mojom::CookieChangeListener> mojo_listener_;
+ bool same_party_attribute_enabled_;
+
SEQUENCE_CHECKER(sequence_checker_);
};
@@ -441,6 +468,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_(
@@ -567,7 +596,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();
@@ -839,7 +869,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(
@@ -897,11 +928,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 dd5bcfc92d8ffa85d9c70ece32b82c8458633e5b..d21cb3348878e4b60fd03deb01066e51e92111be 100644
--- a/services/network/url_loader.cc
+++ b/services/network/url_loader.cc
@@ -287,7 +287,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);
}
@@ -588,6 +588,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
@@ -2702,6 +2705,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 bd3893b29ce6808544d17c672b174308d93365af..9038e79547d9081a04f463222ab3d067d0e99d80 100644
--- a/services/network/url_loader.h
+++ b/services/network/url_loader.h
@@ -547,6 +547,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