mirror of https://github.com/electron/electron
5411 lines
242 KiB
Diff
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
|