0

Add new PublicSets class.

This lets us share a common query-backend implementation between the
network service and the browser process, since both need to perform queries for various reasons.

Bug: 1358959
Change-Id: I58f9af2ce5cb39a83e62f691f9564cb288e3def3
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/3869050
Commit-Queue: Chris Fredrickson <cfredric@chromium.org>
Auto-Submit: Chris Fredrickson <cfredric@chromium.org>
Reviewed-by: Will Harris <wfh@chromium.org>
Reviewed-by: Matt Menke <mmenke@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1044557}
This commit is contained in:
Chris Fredrickson
2022-09-08 15:20:37 +00:00
committed by Chromium LUCI CQ
parent 37885e0318
commit 878d06b45d
23 changed files with 597 additions and 218 deletions

@ -29,7 +29,7 @@
#include "content/public/common/content_client.h"
#include "net/base/schemeful_site.h"
#include "net/first_party_sets/first_party_set_entry.h"
#include "services/network/public/mojom/first_party_sets.mojom.h"
#include "net/first_party_sets/public_sets.h"
#include "third_party/abseil-cpp/absl/types/optional.h"
namespace content {
@ -74,19 +74,20 @@ void UpdateCustomizationMap(
void AddIfPolicySetOverlaps(
const net::SchemefulSite& site,
size_t policy_set_index,
const FlattenedSets& existing_sets,
const net::PublicSets& existing_sets,
base::flat_map<net::SchemefulSite, base::flat_set<size_t>>&
policy_set_overlaps) {
// Check `site` for membership in `existing_sets`.
if (auto it = existing_sets.find(site); it != existing_sets.end()) {
if (auto entry = existing_sets.FindEntry(site, /*config=*/nullptr);
entry.has_value()) {
// Add the index of `site`'s policy set to the list of policy set indices
// that also overlap with site_owner.
policy_set_overlaps[it->second.primary()].insert(policy_set_index);
policy_set_overlaps[entry->primary()].insert(policy_set_index);
}
}
std::vector<SingleSet> NormalizeAdditionSets(
const network::mojom::PublicFirstPartySetsPtr& public_sets,
const net::PublicSets& public_sets,
const std::vector<SingleSet>& addition_sets) {
// Create a mapping from an owner site in `existing_sets` to all policy sets
// that intersect with the set that it owns.
@ -94,7 +95,7 @@ std::vector<SingleSet> NormalizeAdditionSets(
policy_set_overlaps;
for (size_t set_idx = 0; set_idx < addition_sets.size(); set_idx++) {
for (const auto& site_and_entry : addition_sets[set_idx]) {
AddIfPolicySetOverlaps(site_and_entry.first, set_idx, public_sets->sets,
AddIfPolicySetOverlaps(site_and_entry.first, set_idx, public_sets,
policy_set_overlaps);
}
}
@ -174,7 +175,7 @@ void FirstPartySetsHandlerImpl::GetCustomizationForPolicy(
callback) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
PolicyCustomization customization;
if (!public_sets_.is_null()) {
if (public_sets_.has_value()) {
std::move(callback).Run(GetCustomizationForPolicyInternal(policy));
return;
}
@ -191,7 +192,7 @@ void FirstPartySetsHandlerImpl::GetCustomizationForPolicy(
FirstPartySetsHandlerImpl::PolicyCustomization
FirstPartySetsHandlerImpl::ComputeEnterpriseCustomizations(
const network::mojom::PublicFirstPartySetsPtr& public_sets,
const net::PublicSets& public_sets,
const FirstPartySetParser::ParsedPolicySetLists& policy) {
// Maps a site to its new entry if it has one.
base::flat_map<net::SchemefulSite, absl::optional<net::FirstPartySetEntry>>
@ -216,10 +217,11 @@ FirstPartySetsHandlerImpl::ComputeEnterpriseCustomizations(
base::flat_map<net::SchemefulSite, net::FirstPartySetEntry>
addition_intersected_owners;
for (const auto& [new_member, new_entry] : flattened_additions) {
if (const auto entry = public_sets->sets.find(new_member);
entry != public_sets->sets.end()) {
if (const auto entry =
public_sets.FindEntry(new_member, /*config=*/nullptr);
entry.has_value()) {
// Found an overlap with the existing list of sets.
addition_intersected_owners.emplace(entry->second.primary(), new_entry);
addition_intersected_owners.emplace(entry->primary(), new_entry);
}
}
@ -229,13 +231,12 @@ FirstPartySetsHandlerImpl::ComputeEnterpriseCustomizations(
for (const auto& [member, set_entry] : flattened_replacements) {
if (member == set_entry.primary())
continue;
if (auto entry = public_sets->sets.find(member);
entry != public_sets->sets.end() && entry->second.primary() != member) {
const net::FirstPartySetEntry& existing_entry = entry->second;
if (!addition_intersected_owners.contains(existing_entry.primary()) &&
!flattened_additions.contains(existing_entry.primary()) &&
!flattened_replacements.contains(existing_entry.primary())) {
potential_singletons[existing_entry.primary()].insert(member);
if (auto existing_entry = public_sets.FindEntry(member, /*config=*/nullptr);
existing_entry.has_value() && existing_entry->primary() != member) {
if (!addition_intersected_owners.contains(existing_entry->primary()) &&
!flattened_additions.contains(existing_entry->primary()) &&
!flattened_replacements.contains(existing_entry->primary())) {
potential_singletons[existing_entry->primary()].insert(member);
}
}
}
@ -245,8 +246,8 @@ FirstPartySetsHandlerImpl::ComputeEnterpriseCustomizations(
// that those members are involved in).
base::flat_set<net::SchemefulSite> replaced_existing_owners;
for (const auto& [site, unused_owner] : flattened_replacements) {
if (const auto entry = public_sets->sets.find(site);
entry != public_sets->sets.end() && entry->second.primary() == site) {
if (const auto entry = public_sets.FindEntry(site, /*config=*/nullptr);
entry.has_value() && entry->primary() == site) {
// Site was an owner in the existing sets.
bool inserted = replaced_existing_owners.emplace(site).second;
DCHECK(inserted);
@ -256,7 +257,7 @@ FirstPartySetsHandlerImpl::ComputeEnterpriseCustomizations(
// Find out which potential singletons are actually singletons; delete
// members whose owners left; and reparent the sets that intersected with
// an addition set.
for (const auto& [member, set_entry] : public_sets->sets) {
for (const auto& [member, set_entry] : public_sets.entries()) {
// Reparent all sites in any intersecting addition sets.
if (auto entry = addition_intersected_owners.find(set_entry.primary());
entry != addition_intersected_owners.end() &&
@ -311,11 +312,11 @@ FirstPartySetsHandlerImpl::FirstPartySetsHandlerImpl(
FirstPartySetsHandlerImpl::~FirstPartySetsHandlerImpl() = default;
absl::optional<network::mojom::PublicFirstPartySetsPtr>
FirstPartySetsHandlerImpl::GetSets(SetsReadyOnceCallback callback) {
absl::optional<net::PublicSets> FirstPartySetsHandlerImpl::GetSets(
SetsReadyOnceCallback callback) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
DCHECK(IsEnabled());
if (!public_sets_.is_null())
if (public_sets_.has_value())
return public_sets_->Clone();
if (!callback.is_null()) {
@ -343,7 +344,7 @@ void FirstPartySetsHandlerImpl::Init(const base::FilePath& user_data_dir,
sets_loader_->SetComponentSets(base::File());
}
} else {
SetCompleteSets(network::mojom::PublicFirstPartySets::New());
SetCompleteSets(net::PublicSets());
}
}
@ -374,7 +375,7 @@ void FirstPartySetsHandlerImpl::ResetForTesting() {
// this is a static singleton.
base::Unretained(this)));
on_sets_ready_callbacks_.clear();
public_sets_ = nullptr;
public_sets_ = absl::nullopt;
db_helper_.Reset();
}
@ -388,11 +389,9 @@ void FirstPartySetsHandlerImpl::GetPersistedPublicSetsForTesting(
.Then(std::move(callback));
}
void FirstPartySetsHandlerImpl::SetCompleteSets(
network::mojom::PublicFirstPartySetsPtr public_sets) {
void FirstPartySetsHandlerImpl::SetCompleteSets(net::PublicSets public_sets) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
DCHECK(public_sets_.is_null());
DCHECK(!public_sets.is_null());
DCHECK(!public_sets_.has_value());
public_sets_ = std::move(public_sets);
ClearSiteDataOnChangedSets();
@ -427,22 +426,21 @@ void FirstPartySetsHandlerImpl::InvokePendingQueries() {
on_sets_ready_callbacks_.shrink_to_fit();
}
network::mojom::PublicFirstPartySetsPtr FirstPartySetsHandlerImpl::GetSetsSync()
const {
net::PublicSets FirstPartySetsHandlerImpl::GetSetsSync() const {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
DCHECK(!public_sets_.is_null());
DCHECK(public_sets_.has_value());
return public_sets_->Clone();
}
void FirstPartySetsHandlerImpl::ClearSiteDataOnChangedSets() const {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
DCHECK(!public_sets_.is_null());
DCHECK(public_sets_.has_value());
if (!db_helper_.is_null()) {
// TODO(shuuran@chromium.org): Implement site state clearing.
db_helper_
.AsyncCall(&FirstPartySetsHandlerDatabaseHelper::PersistPublicSets)
.WithArgs(public_sets_->sets);
.WithArgs(public_sets_->entries());
}
}
@ -457,7 +455,7 @@ FirstPartySetsHandlerImpl::GetCustomizationForPolicyInternal(
// Provide empty customization if the policy is malformed.
return parsed_policy.has_value()
? FirstPartySetsHandlerImpl::ComputeEnterpriseCustomizations(
public_sets_, parsed_policy.value())
public_sets_.value(), parsed_policy.value())
: FirstPartySetsHandlerImpl::PolicyCustomization();
}

@ -27,7 +27,7 @@
#include "content/public/browser/first_party_sets_handler.h"
#include "net/base/schemeful_site.h"
#include "net/first_party_sets/first_party_set_entry.h"
#include "services/network/public/mojom/first_party_sets.mojom.h"
#include "net/first_party_sets/public_sets.h"
#include "third_party/abseil-cpp/absl/types/optional.h"
namespace content {
@ -43,8 +43,7 @@ class CONTENT_EXPORT FirstPartySetsHandlerImpl : public FirstPartySetsHandler {
public:
using FlattenedSets =
base::flat_map<net::SchemefulSite, net::FirstPartySetEntry>;
using SetsReadyOnceCallback =
base::OnceCallback<void(network::mojom::PublicFirstPartySetsPtr)>;
using SetsReadyOnceCallback = base::OnceCallback<void(net::PublicSets)>;
using PolicyCustomization = FirstPartySetsHandler::PolicyCustomization;
static FirstPartySetsHandlerImpl* GetInstance();
@ -77,7 +76,7 @@ class CONTENT_EXPORT FirstPartySetsHandlerImpl : public FirstPartySetsHandler {
// data is not ready yet.
//
// Must not be called if First-Party Sets is disabled.
[[nodiscard]] absl::optional<network::mojom::PublicFirstPartySetsPtr> GetSets(
[[nodiscard]] absl::optional<net::PublicSets> GetSets(
SetsReadyOnceCallback callback);
// FirstPartySetsHandler
@ -107,7 +106,7 @@ class CONTENT_EXPORT FirstPartySetsHandlerImpl : public FirstPartySetsHandler {
// to update the browser's list of First-Party Sets to respect a profile's
// setting for the per-profile FirstPartySetsOverrides policy.
static PolicyCustomization ComputeEnterpriseCustomizations(
const network::mojom::PublicFirstPartySetsPtr& public_sets,
const net::PublicSets& public_sets,
const FirstPartySetParser::ParsedPolicySetLists& policy);
private:
@ -117,7 +116,7 @@ class CONTENT_EXPORT FirstPartySetsHandlerImpl : public FirstPartySetsHandler {
bool embedder_will_provide_public_sets);
// Sets the public First-Party Sets data. Must be called exactly once.
void SetCompleteSets(network::mojom::PublicFirstPartySetsPtr public_sets);
void SetCompleteSets(net::PublicSets public_sets);
// Sets `db_helper_`, which will initialize the underlying First-Party Sets
// database under `user_data_dir`. Must be called exactly once.
@ -126,10 +125,11 @@ class CONTENT_EXPORT FirstPartySetsHandlerImpl : public FirstPartySetsHandler {
// Invokes any pending queries.
void InvokePendingQueries();
// Returns the list of public First-Party Sets.
// Returns the list of public First-Party Sets. This clones the underlying
// data.
//
// Must be called after the list has been initialized.
network::mojom::PublicFirstPartySetsPtr GetSetsSync() const;
net::PublicSets GetSetsSync() const;
// TODO(shuuran@chromium.org): Implement the code to clear site state.
void ClearSiteDataOnChangedSets() const;
@ -144,8 +144,8 @@ class CONTENT_EXPORT FirstPartySetsHandlerImpl : public FirstPartySetsHandler {
// The public First-Party Sets, after parsing and validation.
//
// This is nullptr until all of the required inputs have been received.
network::mojom::PublicFirstPartySetsPtr public_sets_
// This is nullopt until all of the required inputs have been received.
absl::optional<net::PublicSets> public_sets_
GUARDED_BY_CONTEXT(sequence_checker_);
bool enabled_ GUARDED_BY_CONTEXT(sequence_checker_);

@ -18,7 +18,7 @@
#include "content/public/browser/first_party_sets_handler.h"
#include "net/base/schemeful_site.h"
#include "net/first_party_sets/first_party_set_entry.h"
#include "services/network/public/mojom/first_party_sets.mojom.h"
#include "net/first_party_sets/public_sets.h"
#include "testing/gmock/include/gmock/gmock-matchers.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
@ -45,9 +45,9 @@ MATCHER_P(SerializesTo, want, "") {
}
MATCHER_P(PublicSetsAre, sets_matcher, "") {
const network::mojom::PublicFirstPartySetsPtr& public_sets = arg;
const net::PublicSets& public_sets = arg;
const base::flat_map<net::SchemefulSite, net::FirstPartySetEntry>& sets =
public_sets->sets;
public_sets.entries();
return testing::ExplainMatchResult(sets_matcher, sets, result_listener);
}
@ -107,20 +107,13 @@ FirstPartySetParser::ParsedPolicySetLists MakeParsedPolicyFromMap(
return result;
}
network::mojom::PublicFirstPartySetsPtr GetSetsAndWait() {
base::test::TestFuture<network::mojom::PublicFirstPartySetsPtr> future;
absl::optional<network::mojom::PublicFirstPartySetsPtr> result =
net::PublicSets GetSetsAndWait() {
base::test::TestFuture<net::PublicSets> future;
absl::optional<net::PublicSets> result =
FirstPartySetsHandlerImpl::GetInstance()->GetSets(future.GetCallback());
return result.has_value() ? std::move(result).value() : future.Take();
}
network::mojom::PublicFirstPartySetsPtr MakePublicFirstPartySets(
FlattenedSets sets) {
network::mojom::PublicFirstPartySetsPtr public_sets =
network::mojom::PublicFirstPartySets::New();
public_sets->sets = std::move(sets);
return public_sets;
}
} // namespace
TEST(FirstPartySetsHandlerImpl, ValidateEnterprisePolicy_ValidPolicy) {
@ -365,7 +358,7 @@ TEST_F(FirstPartySetsHandlerImplEnabledTest,
->SetEmbedderWillProvidePublicSetsForTesting(true);
// Call GetSets before the sets are ready, and before Init has been called.
base::test::TestFuture<network::mojom::PublicFirstPartySetsPtr> future;
base::test::TestFuture<net::PublicSets> future;
EXPECT_EQ(
FirstPartySetsHandlerImpl::GetInstance()->GetSets(future.GetCallback()),
absl::nullopt);
@ -526,20 +519,25 @@ TEST_F(FirstPartySetsHandlerGetCustomizationForPolicyTest,
}
TEST(FirstPartySetsProfilePolicyCustomizations, EmptyPolicySetLists) {
EXPECT_THAT(
FirstPartySetsHandlerImpl::ComputeEnterpriseCustomizations(
MakePublicFirstPartySets(MakeFlattenedSetsFromMap(
{{"https://primary1.test", {"https://associatedsite1.test"}}})),
MakeParsedPolicyFromMap({}, {})),
FirstPartySetsHandlerImpl::PolicyCustomization());
EXPECT_THAT(FirstPartySetsHandlerImpl::ComputeEnterpriseCustomizations(
net::PublicSets(
/*entries=*/MakeFlattenedSetsFromMap(
{{"https://primary1.test",
{"https://associatedsite1.test"}}}),
/*aliases=*/{}),
MakeParsedPolicyFromMap({}, {})),
FirstPartySetsHandlerImpl::PolicyCustomization());
}
TEST(FirstPartySetsProfilePolicyCustomizations,
Replacements_NoIntersection_NoRemoval) {
PolicyCustomization customization =
FirstPartySetsHandlerImpl::ComputeEnterpriseCustomizations(
MakePublicFirstPartySets(MakeFlattenedSetsFromMap(
{{"https://primary1.test", {"https://associatedsite1.test"}}})),
net::PublicSets(
/*entries=*/MakeFlattenedSetsFromMap(
{{"https://primary1.test",
{"https://associatedsite1.test"}}}),
/*aliases=*/{}),
MakeParsedPolicyFromMap(
/*replacements=*/{{"https://primary2.test",
{"https://associatedsite2.test"}}},
@ -562,10 +560,12 @@ TEST(FirstPartySetsProfilePolicyCustomizations,
Replacements_ReplacesExistingAssociatedSite_RemovedFromFormerSet) {
PolicyCustomization customization =
FirstPartySetsHandlerImpl::ComputeEnterpriseCustomizations(
MakePublicFirstPartySets(
MakeFlattenedSetsFromMap({{"https://primary1.test",
{"https://associatedsite1a.test",
"https://associatedsite1b.test"}}})),
net::PublicSets(
/*entries=*/MakeFlattenedSetsFromMap(
{{"https://primary1.test",
{"https://associatedsite1a.test",
"https://associatedsite1b.test"}}}),
/*aliases=*/{}),
MakeParsedPolicyFromMap(
/*replacements=*/{{"https://primary2.test",
{"https://associatedsite1b.test"}}},
@ -588,10 +588,12 @@ TEST(FirstPartySetsProfilePolicyCustomizations,
Replacements_ReplacesExistingPrimary_RemovesFormerAssociatedSites) {
PolicyCustomization customization =
FirstPartySetsHandlerImpl::ComputeEnterpriseCustomizations(
MakePublicFirstPartySets(
MakeFlattenedSetsFromMap({{"https://primary1.test",
{"https://associatedsite1a.test",
"https://associatedsite1b.test"}}})),
net::PublicSets(
/*entries=*/MakeFlattenedSetsFromMap(
{{"https://primary1.test",
{"https://associatedsite1a.test",
"https://associatedsite1b.test"}}}),
/*aliases=*/{}),
MakeParsedPolicyFromMap(
/*replacements=*/{{"https://primary1.test",
{"https://associatedsite2.test"}}},
@ -617,8 +619,11 @@ TEST(FirstPartySetsProfilePolicyCustomizations,
Replacements_ReplacesExistingAssociatedSite_RemovesSingletons) {
PolicyCustomization customization =
FirstPartySetsHandlerImpl::ComputeEnterpriseCustomizations(
MakePublicFirstPartySets(MakeFlattenedSetsFromMap(
{{"https://primary1.test", {"https://associatedsite1.test"}}})),
net::PublicSets(
/*entries=*/MakeFlattenedSetsFromMap(
{{"https://primary1.test",
{"https://associatedsite1.test"}}}),
/*aliases=*/{}),
MakeParsedPolicyFromMap(
/*replacements=*/{{"https://primary3.test",
{"https://associatedsite1.test"}}},
@ -642,8 +647,11 @@ TEST(FirstPartySetsProfilePolicyCustomizations,
Additions_NoIntersection_AddsWithoutUpdating) {
PolicyCustomization customization =
FirstPartySetsHandlerImpl::ComputeEnterpriseCustomizations(
MakePublicFirstPartySets(MakeFlattenedSetsFromMap(
{{"https://primary1.test", {"https://associatedsite1.test"}}})),
net::PublicSets(
/*entries=*/MakeFlattenedSetsFromMap(
{{"https://primary1.test",
{"https://associatedsite1.test"}}}),
/*aliases=*/{}),
MakeParsedPolicyFromMap(
/*replacements=*/{},
/*additions=*/{{"https://primary2.test",
@ -668,8 +676,11 @@ TEST(
Additions_PolicyPrimaryIsExistingAssociatedSite_PolicySetAbsorbsExistingSet) {
PolicyCustomization customization =
FirstPartySetsHandlerImpl::ComputeEnterpriseCustomizations(
MakePublicFirstPartySets(MakeFlattenedSetsFromMap(
{{"https://primary1.test", {"https://associatedsite2.test"}}})),
net::PublicSets(
/*entries=*/MakeFlattenedSetsFromMap(
{{"https://primary1.test",
{"https://associatedsite2.test"}}}),
/*aliases=*/{}),
MakeParsedPolicyFromMap(
/*replacements=*/{},
/*additions=*/{{"https://associatedsite2.test",
@ -704,10 +715,12 @@ TEST(
Additions_PolicyPrimaryIsExistingPrimary_PolicySetAbsorbsExistingAssociatedSites) {
PolicyCustomization customization =
FirstPartySetsHandlerImpl::ComputeEnterpriseCustomizations(
MakePublicFirstPartySets(
MakeFlattenedSetsFromMap({{"https://primary1.test",
{"https://associatedsite1.test",
"https://associatedsite3.test"}}})),
net::PublicSets(
/*entries=*/MakeFlattenedSetsFromMap(
{{"https://primary1.test",
{"https://associatedsite1.test",
"https://associatedsite3.test"}}}),
/*aliases=*/{}),
MakeParsedPolicyFromMap(
/*replacements=*/{},
/*additions=*/{{"https://primary1.test",
@ -748,8 +761,10 @@ TEST(FirstPartySetsProfilePolicyCustomizations,
// sets are unaffected.
EXPECT_THAT(
FirstPartySetsHandlerImpl::ComputeEnterpriseCustomizations(
MakePublicFirstPartySets(MakeFlattenedSetsFromMap(
{{"https://primary1.test", {"https://primary2.test"}}})),
net::PublicSets(
/*entries=*/MakeFlattenedSetsFromMap(
{{"https://primary1.test", {"https://primary2.test"}}}),
/*aliases=*/{}),
FirstPartySetParser::ParsedPolicySetLists(
/*replacement_list=*/{},
{
@ -825,8 +840,10 @@ TEST(FirstPartySetsProfilePolicyCustomizations,
// sets are unaffected.
EXPECT_THAT(
FirstPartySetsHandlerImpl::ComputeEnterpriseCustomizations(
MakePublicFirstPartySets(MakeFlattenedSetsFromMap(
{{"https://primary2.test", {"https://primary1.test"}}})),
net::PublicSets(
/*entries=*/MakeFlattenedSetsFromMap(
{{"https://primary2.test", {"https://primary1.test"}}}),
/*aliases=*/{}),
FirstPartySetParser::ParsedPolicySetLists(
/*replacement_list=*/{},
{
@ -891,10 +908,12 @@ TEST(FirstPartySetsProfilePolicyCustomizations,
ReplacementsAndAdditions_SetListsOverlapWithSameExistingSet) {
PolicyCustomization customization =
FirstPartySetsHandlerImpl::ComputeEnterpriseCustomizations(
MakePublicFirstPartySets(
MakeFlattenedSetsFromMap({{"https://primary1.test",
{"https://associatedsite1.test",
"https://associatedsite2.test"}}})),
net::PublicSets(
/*entries=*/MakeFlattenedSetsFromMap(
{{"https://primary1.test",
{"https://associatedsite1.test",
"https://associatedsite2.test"}}}),
/*aliases=*/{}),
MakeParsedPolicyFromMap(
/*replacements=*/{{"https://primary0.test",
{"https://associatedsite1.test"}}},

@ -6,6 +6,7 @@
#include <iterator>
#include <set>
#include <sstream>
#include <utility>
#include <vector>
@ -21,7 +22,7 @@
#include "content/browser/first_party_sets/local_set_declaration.h"
#include "net/base/schemeful_site.h"
#include "net/first_party_sets/first_party_set_entry.h"
#include "services/network/public/mojom/first_party_sets.mojom.h"
#include "net/first_party_sets/public_sets.h"
#include "third_party/abseil-cpp/absl/types/optional.h"
namespace content {
@ -156,11 +157,8 @@ void FirstPartySetsLoader::MaybeFinishLoading() {
if (!HasAllInputs())
return;
ApplyManuallySpecifiedSet();
network::mojom::PublicFirstPartySetsPtr public_sets =
network::mojom::PublicFirstPartySets::New();
public_sets->sets = std::move(sets_);
public_sets->aliases = std::move(aliases_);
std::move(on_load_complete_).Run(std::move(public_sets));
std::move(on_load_complete_)
.Run(net::PublicSets(std::move(sets_), std::move(aliases_)));
}
bool FirstPartySetsLoader::HasAllInputs() const {

@ -13,7 +13,7 @@
#include "content/browser/first_party_sets/first_party_set_parser.h"
#include "content/browser/first_party_sets/local_set_declaration.h"
#include "content/common/content_export.h"
#include "services/network/public/mojom/first_party_sets.mojom-forward.h"
#include "net/first_party_sets/public_sets.h"
#include "third_party/abseil-cpp/absl/types/optional.h"
namespace content {
@ -25,8 +25,7 @@ namespace content {
// `SetManuallySpecifiedSet`.
class CONTENT_EXPORT FirstPartySetsLoader {
public:
using LoadCompleteOnceCallback =
base::OnceCallback<void(network::mojom::PublicFirstPartySetsPtr)>;
using LoadCompleteOnceCallback = base::OnceCallback<void(net::PublicSets)>;
using FlattenedSets = FirstPartySetParser::SetsMap;
using SingleSet = FirstPartySetParser::SingleSet;

@ -21,7 +21,7 @@
#include "content/browser/first_party_sets/local_set_declaration.h"
#include "net/base/schemeful_site.h"
#include "net/first_party_sets/first_party_set_entry.h"
#include "services/network/public/mojom/first_party_sets.mojom.h"
#include "net/first_party_sets/public_sets.h"
#include "testing/gmock/include/gmock/gmock-matchers.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
@ -45,9 +45,9 @@ MATCHER_P(SerializesTo, want, "") {
}
MATCHER_P(PublicSetsAre, sets_matcher, "") {
const network::mojom::PublicFirstPartySetsPtr& public_sets = arg;
const net::PublicSets& public_sets = arg;
const base::flat_map<net::SchemefulSite, net::FirstPartySetEntry>& sets =
public_sets->sets;
public_sets.entries();
return testing::ExplainMatchResult(sets_matcher, sets, result_listener);
}
@ -70,13 +70,11 @@ class FirstPartySetsLoaderTest : public ::testing::Test {
FirstPartySetsLoader& loader() { return loader_; }
network::mojom::PublicFirstPartySetsPtr WaitAndGetResult() {
return future_.Take();
}
net::PublicSets WaitAndGetResult() { return future_.Take(); }
private:
base::test::TaskEnvironment env_;
base::test::TestFuture<network::mojom::PublicFirstPartySetsPtr> future_;
base::test::TestFuture<net::PublicSets> future_;
FirstPartySetsLoader loader_;
};

@ -52,6 +52,7 @@
#include "mojo/public/cpp/bindings/remote.h"
#include "mojo/public/cpp/bindings/self_owned_receiver.h"
#include "net/base/features.h"
#include "net/first_party_sets/public_sets.h"
#include "net/log/net_log_util.h"
#include "sandbox/policy/features.h"
#include "services/cert_verifier/cert_verifier_service_factory.h"
@ -621,13 +622,11 @@ network::mojom::NetworkService* GetNetworkService() {
}
if (FirstPartySetsHandlerImpl::GetInstance()->IsEnabled()) {
if (absl::optional<network::mojom::PublicFirstPartySetsPtr> sets =
if (absl::optional<net::PublicSets> sets =
FirstPartySetsHandlerImpl::GetInstance()->GetSets(
base::BindOnce(
[](network::mojom::PublicFirstPartySetsPtr sets) {
GetNetworkService()->SetFirstPartySets(
std::move(sets));
}));
base::BindOnce([](net::PublicSets sets) {
GetNetworkService()->SetFirstPartySets(std::move(sets));
}));
sets.has_value()) {
g_network_service_remote->get()->SetFirstPartySets(
std::move(sets.value()));

@ -544,6 +544,8 @@ component("net") {
"first_party_sets/first_party_set_metadata.h",
"first_party_sets/first_party_sets_context_config.cc",
"first_party_sets/first_party_sets_context_config.h",
"first_party_sets/public_sets.cc",
"first_party_sets/public_sets.h",
"first_party_sets/same_party_context.cc",
"first_party_sets/same_party_context.h",
"http/alternative_service.cc",
@ -4156,6 +4158,7 @@ test("net_unittests") {
"extras/sqlite/sqlite_persistent_cookie_store_unittest.cc",
"filter/filter_source_stream_unittest.cc",
"filter/gzip_source_stream_unittest.cc",
"first_party_sets/public_sets_unittest.cc",
"http/alternative_service_unittest.cc",
"http/bidirectional_stream_unittest.cc",
"http/broken_alternative_services_unittest.cc",

@ -0,0 +1,90 @@
// Copyright 2022 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "net/first_party_sets/public_sets.h"
#include <tuple>
#include "net/base/schemeful_site.h"
#include "net/first_party_sets/first_party_set_entry.h"
#include "net/first_party_sets/first_party_sets_context_config.h"
namespace net {
namespace {
// Converts WS to HTTP, and WSS to HTTPS.
SchemefulSite NormalizeScheme(const SchemefulSite& site) {
SchemefulSite normalized_site = site;
normalized_site.ConvertWebSocketToHttp();
return normalized_site;
}
} // namespace
PublicSets::PublicSets() = default;
PublicSets::PublicSets(
base::flat_map<SchemefulSite, FirstPartySetEntry> entries,
base::flat_map<SchemefulSite, SchemefulSite> aliases)
: entries_(std::move(entries)), aliases_(std::move(aliases)) {}
PublicSets::PublicSets(PublicSets&&) = default;
PublicSets& PublicSets::operator=(PublicSets&&) = default;
PublicSets::~PublicSets() = default;
bool PublicSets::operator==(const PublicSets& other) const {
return std::tie(entries_, aliases_) ==
std::tie(other.entries_, other.aliases_);
}
bool PublicSets::operator!=(const PublicSets& other) const {
return !(*this == other);
}
PublicSets PublicSets::Clone() const {
return PublicSets(entries_, aliases_);
}
absl::optional<FirstPartySetEntry> PublicSets::FindEntry(
const SchemefulSite& site,
const FirstPartySetsContextConfig* fps_context_config) const {
SchemefulSite normalized_site = NormalizeScheme(site);
// Check if `normalized_site` can be found in the customizations first.
// If not, fall back to look up in `entries_`.
if (fps_context_config) {
if (const auto config_it =
fps_context_config->customizations().find(normalized_site);
config_it != fps_context_config->customizations().end()) {
return config_it->second;
}
}
const auto canonical_it = aliases_.find(normalized_site);
const SchemefulSite& canonical_site =
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;
}
return absl::nullopt;
}
std::ostream& operator<<(std::ostream& os, const PublicSets& ps) {
os << "entries = {";
for (const auto& [site, entry] : ps.entries()) {
os << "{" << site.Serialize() << ": " << entry << "}, ";
}
os << "}, aliases = {";
for (const auto& [alias, canonical] : ps.aliases()) {
os << "{" << alias.Serialize() << ": " << canonical.Serialize() << "}, ";
}
os << "}";
return os;
}
} // namespace net

@ -0,0 +1,67 @@
// Copyright 2022 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef NET_FIRST_PARTY_SETS_PUBLIC_SETS_H_
#define NET_FIRST_PARTY_SETS_PUBLIC_SETS_H_
#include "base/containers/flat_map.h"
#include "net/base/net_export.h"
#include "net/base/schemeful_site.h"
#include "net/first_party_sets/first_party_set_entry.h"
namespace net {
class FirstPartySetsContextConfig;
// This class holds all of the info associated with the public First-Party
// Sets, after they've been parsed. This is suitable for plumbing from the
// browser process to the network service, or for answering queries.
class NET_EXPORT PublicSets {
public:
PublicSets();
PublicSets(base::flat_map<SchemefulSite, FirstPartySetEntry> entries,
base::flat_map<SchemefulSite, SchemefulSite> aliases);
PublicSets(PublicSets&&);
PublicSets& operator=(PublicSets&&);
~PublicSets();
bool operator==(const PublicSets& other) const;
bool operator!=(const PublicSets& other) const;
const base::flat_map<SchemefulSite, FirstPartySetEntry>& entries() const {
return entries_;
}
const base::flat_map<SchemefulSite, SchemefulSite>& aliases() const {
return aliases_;
}
// Creates a clone of this instance.
PublicSets Clone() const;
// Returns the entry corresponding to the given `site`, if one exists.
// Respects any customization/overlay specified by `config`. This is
// semi-agnostic to scheme: it just cares whether the scheme is secure or
// insecure.
absl::optional<FirstPartySetEntry> FindEntry(
const SchemefulSite& site,
const FirstPartySetsContextConfig* config) const;
private:
// Represents the mapping of site -> entry, where keys are sites within sets,
// and values are entries of the sets.
base::flat_map<SchemefulSite, FirstPartySetEntry> entries_;
// The site aliases. Used to normalize a given SchemefulSite into its
// canonical representative, before looking it up in `entries_`.
base::flat_map<SchemefulSite, SchemefulSite> aliases_;
};
NET_EXPORT std::ostream& operator<<(std::ostream& os, const PublicSets& ps);
} // namespace net
#endif // NET_FIRST_PARTY_SETS_FIRST_PARTY_SET_ENTRY_H_

@ -0,0 +1,167 @@
// Copyright 2022 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "net/first_party_sets/public_sets.h"
#include <set>
#include <string>
#include "base/containers/flat_map.h"
#include "net/base/schemeful_site.h"
#include "net/first_party_sets/first_party_set_entry.h"
#include "net/first_party_sets/first_party_sets_context_config.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::Optional;
namespace net {
class PublicSetsTest : public ::testing::Test {
public:
PublicSetsTest() = default;
FirstPartySetsContextConfig* config() { return &fps_context_config_; }
private:
FirstPartySetsContextConfig fps_context_config_;
};
TEST_F(PublicSetsTest, FindEntry_Nonexistent) {
SchemefulSite example(GURL("https://example.test"));
EXPECT_THAT(PublicSets().FindEntry(example, config()), absl::nullopt);
}
TEST_F(PublicSetsTest, FindEntry_Exists) {
SchemefulSite example(GURL("https://example.test"));
SchemefulSite decoy_site(GURL("https://decoy.test"));
FirstPartySetEntry entry(example, SiteType::kPrimary, absl::nullopt);
FirstPartySetEntry decoy_entry(example, SiteType::kAssociated, 1);
EXPECT_THAT(PublicSets(
{
{example, entry},
{decoy_site, decoy_entry},
},
{})
.FindEntry(example, config()),
Optional(entry));
}
TEST_F(PublicSetsTest, FindEntry_ExistsWhenNormalized) {
SchemefulSite https_example(GURL("https://example.test"));
SchemefulSite wss_example(GURL("wss://example.test"));
FirstPartySetEntry entry(https_example, SiteType::kPrimary, absl::nullopt);
EXPECT_THAT(PublicSets(
{
{https_example, entry},
},
{})
.FindEntry(wss_example, config()),
Optional(entry));
}
TEST_F(PublicSetsTest, FindEntry_ExistsViaOverride) {
SchemefulSite example(GURL("https://example.test"));
FirstPartySetEntry public_entry(example, SiteType::kPrimary, absl::nullopt);
FirstPartySetEntry override_entry(example, SiteType::kAssociated, 1);
config()->SetCustomizations({{example, override_entry}});
EXPECT_THAT(PublicSets(
{
{example, public_entry},
},
{})
.FindEntry(example, config()),
Optional(override_entry));
}
TEST_F(PublicSetsTest, FindEntry_RemovedViaOverride) {
SchemefulSite example(GURL("https://example.test"));
FirstPartySetEntry public_entry(example, SiteType::kPrimary, absl::nullopt);
config()->SetCustomizations({{example, absl::nullopt}});
EXPECT_THAT(PublicSets(
{
{example, public_entry},
},
{})
.FindEntry(example, config()),
absl::nullopt);
}
TEST_F(PublicSetsTest, FindEntry_ExistsViaAlias) {
SchemefulSite example(GURL("https://example.test"));
SchemefulSite example_cctld(GURL("https://example.cctld"));
FirstPartySetEntry entry(example, SiteType::kPrimary, absl::nullopt);
EXPECT_THAT(PublicSets(
{
{example, entry},
},
{{example_cctld, example}})
.FindEntry(example_cctld, config()),
Optional(entry));
}
TEST_F(PublicSetsTest, FindEntry_ExistsViaOverrideWithDecoyAlias) {
SchemefulSite example(GURL("https://example.test"));
SchemefulSite example_cctld(GURL("https://example.cctld"));
FirstPartySetEntry public_entry(example, SiteType::kPrimary, absl::nullopt);
FirstPartySetEntry override_entry(example, SiteType::kAssociated, 1);
config()->SetCustomizations({{example_cctld, override_entry}});
EXPECT_THAT(PublicSets(
{
{example, public_entry},
},
{{example_cctld, example}})
.FindEntry(example_cctld, config()),
Optional(override_entry));
}
TEST_F(PublicSetsTest, FindEntry_RemovedViaOverrideWithDecoyAlias) {
SchemefulSite example(GURL("https://example.test"));
SchemefulSite example_cctld(GURL("https://example.cctld"));
FirstPartySetEntry public_entry(example, SiteType::kPrimary, absl::nullopt);
config()->SetCustomizations({{example_cctld, absl::nullopt}});
EXPECT_THAT(PublicSets(
{
{example, public_entry},
},
{{example_cctld, example}})
.FindEntry(example_cctld, config()),
absl::nullopt);
}
TEST_F(PublicSetsTest, FindEntry_AliasesIgnoredForConfig) {
SchemefulSite example(GURL("https://example.test"));
SchemefulSite example_cctld(GURL("https://example.cctld"));
FirstPartySetEntry public_entry(example, SiteType::kPrimary, absl::nullopt);
FirstPartySetEntry override_entry(example, SiteType::kAssociated, 1);
config()->SetCustomizations({{example, override_entry}});
// FindEntry should ignore aliases when using the customizations. Public
// aliases only apply to sites in the public sets.
EXPECT_THAT(PublicSets(
{
{example, public_entry},
},
{{example_cctld, example}})
.FindEntry(example_cctld, config()),
public_entry);
}
} // namespace net

@ -16,8 +16,8 @@
#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/public_sets.h"
#include "net/first_party_sets/same_party_context.h"
#include "services/network/public/mojom/first_party_sets.mojom.h"
#include "services/network/public/mojom/first_party_sets_access_delegate.mojom.h"
#include "testing/gmock/include/gmock/gmock-matchers.h"
#include "testing/gmock/include/gmock/gmock.h"
@ -60,14 +60,6 @@ mojom::FirstPartySetsReadyEventPtr CreateFirstPartySetsReadyEvent(
return ready_event;
}
mojom::PublicFirstPartySetsPtr CreatePublicFirstPartySets(
FirstPartySetsAccessDelegate::FlattenedSets sets) {
mojom::PublicFirstPartySetsPtr public_sets =
mojom::PublicFirstPartySets::New();
public_sets->sets = sets;
return public_sets;
}
} // namespace
// No-op FirstPartySetsAccessDelegate should just pass queries to
@ -80,18 +72,23 @@ class NoopFirstPartySetsAccessDelegateTest : public ::testing::Test {
/*receiver=*/mojo::NullReceiver(),
/*params=*/nullptr,
&first_party_sets_manager_) {
first_party_sets_manager_.SetCompleteSets(CreatePublicFirstPartySets({
{kSet1Member1,
net::FirstPartySetEntry(kSet1Owner, net::SiteType::kAssociated, 0)},
{kSet1Member2,
net::FirstPartySetEntry(kSet1Owner, net::SiteType::kAssociated, 1)},
{kSet1Owner, net::FirstPartySetEntry(
kSet1Owner, net::SiteType::kPrimary, absl::nullopt)},
{kSet2Member1,
net::FirstPartySetEntry(kSet2Owner, net::SiteType::kAssociated, 0)},
{kSet2Owner, net::FirstPartySetEntry(
kSet2Owner, net::SiteType::kPrimary, absl::nullopt)},
}));
first_party_sets_manager_.SetCompleteSets(net::PublicSets(
/*entries=*/
{
{kSet1Member1, net::FirstPartySetEntry(
kSet1Owner, net::SiteType::kAssociated, 0)},
{kSet1Member2, net::FirstPartySetEntry(
kSet1Owner, net::SiteType::kAssociated, 1)},
{kSet1Owner,
net::FirstPartySetEntry(kSet1Owner, net::SiteType::kPrimary,
absl::nullopt)},
{kSet2Member1, net::FirstPartySetEntry(
kSet2Owner, net::SiteType::kAssociated, 0)},
{kSet2Owner,
net::FirstPartySetEntry(kSet2Owner, net::SiteType::kPrimary,
absl::nullopt)},
},
/*aliases=*/{}));
}
FirstPartySetsAccessDelegate& delegate() { return delegate_; }
@ -132,18 +129,23 @@ class FirstPartySetsAccessDelegateTest : public ::testing::Test {
delegate_(delegate_remote_.BindNewPipeAndPassReceiver(),
CreateFirstPartySetsAccessDelegateParams(enabled),
&first_party_sets_manager_) {
first_party_sets_manager_.SetCompleteSets(CreatePublicFirstPartySets({
{kSet1Member1,
net::FirstPartySetEntry(kSet1Owner, net::SiteType::kAssociated, 0)},
{kSet1Member2,
net::FirstPartySetEntry(kSet1Owner, net::SiteType::kAssociated, 1)},
{kSet1Owner, net::FirstPartySetEntry(
kSet1Owner, net::SiteType::kPrimary, absl::nullopt)},
{kSet2Member1,
net::FirstPartySetEntry(kSet2Owner, net::SiteType::kAssociated, 0)},
{kSet2Owner, net::FirstPartySetEntry(
kSet2Owner, net::SiteType::kPrimary, absl::nullopt)},
}));
first_party_sets_manager_.SetCompleteSets(net::PublicSets(
/*entries=*/
{
{kSet1Member1, net::FirstPartySetEntry(
kSet1Owner, net::SiteType::kAssociated, 0)},
{kSet1Member2, net::FirstPartySetEntry(
kSet1Owner, net::SiteType::kAssociated, 1)},
{kSet1Owner,
net::FirstPartySetEntry(kSet1Owner, net::SiteType::kPrimary,
absl::nullopt)},
{kSet2Member1, net::FirstPartySetEntry(
kSet2Owner, net::SiteType::kAssociated, 0)},
{kSet2Owner,
net::FirstPartySetEntry(kSet2Owner, net::SiteType::kPrimary,
absl::nullopt)},
},
/*aliases=*/{}));
}
net::FirstPartySetMetadata ComputeMetadataAndWait(

@ -24,8 +24,8 @@
#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/public_sets.h"
#include "net/first_party_sets/same_party_context.h"
#include "services/network/public/mojom/first_party_sets.mojom.h"
#include "third_party/abseil-cpp/absl/types/optional.h"
namespace network {
@ -45,7 +45,7 @@ FirstPartySetsManager::FirstPartySetsManager(bool enabled)
enabled ? std::make_unique<base::circular_deque<base::OnceClosure>>()
: nullptr) {
if (!enabled)
SetCompleteSets(mojom::PublicFirstPartySets::New());
SetCompleteSets(net::PublicSets());
}
FirstPartySetsManager::~FirstPartySetsManager() {
@ -154,29 +154,9 @@ absl::optional<net::FirstPartySetEntry> FirstPartySetsManager::FindEntry(
DCHECK(sets_.has_value());
const base::ElapsedTimer timer;
net::SchemefulSite normalized_site = site;
normalized_site.ConvertWebSocketToHttp();
absl::optional<net::FirstPartySetEntry> entry;
if (is_enabled()) {
// Check if `normalized_site` can be found in the customizations first.
// If not, fall back to look up in `sets_`.
if (const auto customizations_it =
fps_context_config.customizations().find(normalized_site);
customizations_it != fps_context_config.customizations().end()) {
entry = customizations_it->second;
} else {
const auto canonical_it = aliases_.find(normalized_site);
const net::SchemefulSite& canonical_site = canonical_it == aliases_.end()
? normalized_site
: canonical_it->second;
if (const auto sets_it = sets_->find(canonical_site);
sets_it != sets_->end()) {
entry = sets_it->second;
}
}
}
absl::optional<net::FirstPartySetEntry> entry =
is_enabled() ? sets_->FindEntry(site, &fps_context_config)
: absl::nullopt;
UMA_HISTOGRAM_CUSTOM_MICROSECONDS_TIMES(
"Cookie.FirstPartySets.FindOwner.Latency", timer.Elapsed(),
@ -260,13 +240,11 @@ void FirstPartySetsManager::InvokePendingQueries() {
pending_queries_ = nullptr;
}
void FirstPartySetsManager::SetCompleteSets(
mojom::PublicFirstPartySetsPtr public_sets) {
void FirstPartySetsManager::SetCompleteSets(net::PublicSets public_sets) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (sets_.has_value())
return;
sets_ = std::move(public_sets->sets);
aliases_ = std::move(public_sets->aliases);
sets_ = std::move(public_sets);
InvokePendingQueries();
}

@ -21,7 +21,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/first_party_sets_context_config.h"
#include "services/network/public/mojom/first_party_sets.mojom.h"
#include "net/first_party_sets/public_sets.h"
#include "third_party/abseil-cpp/absl/types/optional.h"
namespace network {
@ -63,7 +63,7 @@ class FirstPartySetsManager {
//
// Only the first call to SetCompleteSets can have any effect; subsequent
// invocations are ignored.
void SetCompleteSets(mojom::PublicFirstPartySetsPtr public_sets);
void SetCompleteSets(net::PublicSets public_sets);
// Sets the enabled_ attribute for testing.
void SetEnabledForTesting(bool enabled);
@ -146,18 +146,11 @@ class FirstPartySetsManager {
// initialized.
void InvokePendingQueries();
// Represents the mapping of site -> site, where keys are members of sets, and
// values are owners of the sets. Owners are explicitly represented as members
// of the set.
// The actual public sets data.
//
// Optional because it is unset until all of the required inputs have been
// received.
absl::optional<FlattenedSets> sets_ GUARDED_BY_CONTEXT(sequence_checker_);
// The site aliases. Used to normalize a given SchemefulSite into its
// canonical representative, before looking it up in `sets_`.
base::flat_map<net::SchemefulSite, net::SchemefulSite> aliases_
GUARDED_BY_CONTEXT(sequence_checker_);
// Optional because it is unset until the data has been received from the
// browser process.
absl::optional<net::PublicSets> sets_ GUARDED_BY_CONTEXT(sequence_checker_);
bool enabled_ GUARDED_BY_CONTEXT(sequence_checker_) = false;

@ -14,8 +14,8 @@
#include "net/cookies/cookie_constants.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/public_sets.h"
#include "net/first_party_sets/same_party_context.h"
#include "services/network/public/mojom/first_party_sets.mojom.h"
#include "testing/gmock/include/gmock/gmock-matchers.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
@ -46,11 +46,7 @@ class FirstPartySetsManagerTest : public ::testing::Test {
const base::flat_map<net::SchemefulSite, net::FirstPartySetEntry>&
content,
const base::flat_map<net::SchemefulSite, net::SchemefulSite>& aliases) {
network::mojom::PublicFirstPartySetsPtr public_sets =
network::mojom::PublicFirstPartySets::New();
public_sets->sets = content;
public_sets->aliases = aliases;
manager_.SetCompleteSets(std::move(public_sets));
manager_.SetCompleteSets(net::PublicSets(content, aliases));
}
net::FirstPartySetMetadata ComputeMetadataAndWait(

@ -51,6 +51,7 @@
#include "net/dns/public/doh_provider_entry.h"
#include "net/dns/system_dns_config_change_notifier.h"
#include "net/dns/test_dns_config_service.h"
#include "net/first_party_sets/public_sets.h"
#include "net/http/http_auth_handler_factory.h"
#include "net/log/file_net_log_observer.h"
#include "net/log/net_log.h"
@ -73,7 +74,6 @@
#include "services/network/public/cpp/load_info_util.h"
#include "services/network/public/cpp/network_switches.h"
#include "services/network/public/cpp/parsed_headers.h"
#include "services/network/public/mojom/first_party_sets.mojom.h"
#include "services/network/public/mojom/key_pinning.mojom.h"
#include "services/network/public/mojom/network_service_test.mojom.h"
#include "services/network/url_loader.h"
@ -790,7 +790,7 @@ void NetworkService::BindTestInterface(
}
}
void NetworkService::SetFirstPartySets(mojom::PublicFirstPartySetsPtr sets) {
void NetworkService::SetFirstPartySets(net::PublicSets sets) {
first_party_sets_manager_->SetCompleteSets(std::move(sets));
}

@ -32,6 +32,7 @@
#include "net/dns/host_resolver.h"
#include "net/dns/public/dns_over_https_config.h"
#include "net/dns/public/secure_dns_mode.h"
#include "net/first_party_sets/public_sets.h"
#include "net/http/transport_security_state.h"
#include "net/log/net_log.h"
#include "net/log/trace_net_log_observer.h"
@ -41,7 +42,6 @@
#include "services/network/network_change_manager.h"
#include "services/network/network_quality_estimator_manager.h"
#include "services/network/public/cpp/network_service_buildflags.h"
#include "services/network/public/mojom/first_party_sets.mojom.h"
#include "services/network/public/mojom/host_resolver.mojom.h"
#include "services/network/public/mojom/key_pinning.mojom.h"
#include "services/network/public/mojom/net_log.mojom.h"
@ -212,7 +212,7 @@ class COMPONENT_EXPORT(NETWORK_SERVICE) NetworkService
#endif
void BindTestInterface(
mojo::PendingReceiver<mojom::NetworkServiceTest> receiver) override;
void SetFirstPartySets(mojom::PublicFirstPartySetsPtr sets) override;
void SetFirstPartySets(net::PublicSets sets) override;
void SetExplicitlyAllowedPorts(const std::vector<uint16_t>& ports) override;
// Returns an HttpAuthHandlerFactory for the given NetworkContext.

@ -4,12 +4,14 @@
#include "services/network/public/cpp/first_party_sets_mojom_traits.h"
#include "base/containers/flat_map.h"
#include "base/stl_util.h"
#include "base/types/optional_util.h"
#include "mojo/public/cpp/bindings/enum_traits.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/public_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"
@ -140,4 +142,21 @@ bool StructTraits<network::mojom::FirstPartySetMetadataDataView,
return true;
}
bool StructTraits<network::mojom::PublicFirstPartySetsDataView,
net::PublicSets>::
Read(network::mojom::PublicFirstPartySetsDataView public_sets,
net::PublicSets* out_public_sets) {
base::flat_map<net::SchemefulSite, net::FirstPartySetEntry> entries;
if (!public_sets.ReadSets(&entries))
return false;
base::flat_map<net::SchemefulSite, net::SchemefulSite> aliases;
if (!public_sets.ReadAliases(&aliases))
return false;
*out_public_sets = net::PublicSets(entries, aliases);
return true;
}
} // namespace mojo

@ -5,10 +5,12 @@
#ifndef SERVICES_NETWORK_PUBLIC_CPP_FIRST_PARTY_SETS_MOJOM_TRAITS_H_
#define SERVICES_NETWORK_PUBLIC_CPP_FIRST_PARTY_SETS_MOJOM_TRAITS_H_
#include "base/containers/flat_map.h"
#include "mojo/public/cpp/bindings/enum_traits.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/public_sets.h"
#include "net/first_party_sets/same_party_context.h"
#include "services/network/public/mojom/first_party_sets.mojom-shared.h"
@ -101,6 +103,24 @@ struct COMPONENT_EXPORT(FIRST_PARTY_SETS_MOJOM_TRAITS)
net::FirstPartySetMetadata* out);
};
template <>
struct COMPONENT_EXPORT(FIRST_PARTY_SETS_MOJOM_TRAITS)
StructTraits<network::mojom::PublicFirstPartySetsDataView,
net::PublicSets> {
static const base::flat_map<net::SchemefulSite, net::FirstPartySetEntry>&
sets(const net::PublicSets& p) {
return p.entries();
}
static const base::flat_map<net::SchemefulSite, net::SchemefulSite>& aliases(
const net::PublicSets& p) {
return p.aliases();
}
static bool Read(network::mojom::PublicFirstPartySetsDataView public_sets,
net::PublicSets* out_public_sets);
};
} // namespace mojo
#endif // SERVICES_NETWORK_PUBLIC_CPP_FIRST_PARTY_SETS_MOJOM_TRAITS_H_

@ -4,11 +4,13 @@
#include "services/network/public/cpp/first_party_sets_mojom_traits.h"
#include "base/containers/flat_map.h"
#include "base/test/gtest_util.h"
#include "mojo/public/cpp/test_support/test_utils.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/public_sets.h"
#include "services/network/public/mojom/first_party_sets.mojom.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "url/gurl.h"
@ -115,5 +117,32 @@ TEST(FirstPartySetsTraitsTest, Roundtrips_FirstPartySetMetadata) {
EXPECT_EQ(round_tripped, make_metadata());
}
TEST(FirstPartySetsTraitsTest, RoundTrips_PublicFirstPartySets) {
net::SchemefulSite a(GURL("https://a.test"));
net::SchemefulSite b(GURL("https://b.test"));
net::SchemefulSite b_cctld(GURL("https://b.cctld"));
net::SchemefulSite c(GURL("https://c.test"));
const net::PublicSets original(
{
{a,
net::FirstPartySetEntry(a, net::SiteType::kPrimary, absl::nullopt)},
{b, net::FirstPartySetEntry(a, net::SiteType::kAssociated, 0)},
{c,
net::FirstPartySetEntry(a, net::SiteType::kService, absl::nullopt)},
},
{{b_cctld, b}});
net::PublicSets round_tripped;
EXPECT_TRUE(
mojo::test::SerializeAndDeserialize<network::mojom::PublicFirstPartySets>(
original, round_tripped));
EXPECT_EQ(original, round_tripped);
EXPECT_FALSE(round_tripped.entries().empty());
EXPECT_FALSE(round_tripped.aliases().empty());
}
} // namespace
} // namespace network

@ -776,6 +776,11 @@ mojom("mojom_first_party_sets") {
cpp = "::net::FirstPartySetMetadata"
move_only = true
},
{
mojom = "network.mojom.PublicFirstPartySets"
cpp = "::net::PublicSets"
move_only = true
},
]
traits_headers = [
"//services/network/public/cpp/first_party_sets_mojom_traits.h",

@ -53,9 +53,8 @@ struct FirstPartySetMetadata {
FirstPartySetEntry? top_frame_entry;
};
// This struct holds all of the info associated with the public First-Party
// Sets, after they've been parsed. This is suitable for plumbing from the
// browser process to the network service.
// This struct must match the class fields defined in
// //net/first_party_sets/public_sets.h.
struct PublicFirstPartySets {
// The mapping from site to FPS entry.
map<SchemefulSite, FirstPartySetEntry> sets;

@ -34,13 +34,13 @@
#include "net/cookies/site_for_cookies.h"
#include "net/cookies/test_cookie_access_delegate.h"
#include "net/first_party_sets/first_party_set_metadata.h"
#include "net/first_party_sets/public_sets.h"
#include "net/first_party_sets/same_party_context.h"
#include "services/network/cookie_access_delegate_impl.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_access_observer.mojom.h"
#include "services/network/public/mojom/cookie_manager.mojom.h"
#include "services/network/public/mojom/first_party_sets.mojom.h"
#include "services/network/test/test_network_context_client.h"
#include "testing/gmock/include/gmock/gmock-matchers.h"
#include "testing/gtest/include/gtest/gtest.h"
@ -461,19 +461,19 @@ class SamePartyEnabledRestrictedCookieManagerTest
.BindNewPipeAndPassReceiver(),
CreateFirstPartySetsAccessDelegateParams(),
&first_party_sets_manager_) {
network::mojom::PublicFirstPartySetsPtr public_sets =
network::mojom::PublicFirstPartySets::New();
public_sets->sets = {
{net::SchemefulSite(GURL("https://example.com")),
net::FirstPartySetEntry(
net::SchemefulSite(GURL("https://example.com")),
net::SiteType::kPrimary, absl::nullopt)},
{net::SchemefulSite(GURL("https://member1.com")),
net::FirstPartySetEntry(
net::SchemefulSite(GURL("https://example.com")),
net::SiteType::kAssociated, 0)},
};
first_party_sets_manager_.SetCompleteSets(std::move(public_sets));
first_party_sets_manager_.SetCompleteSets(net::PublicSets(
/*entries=*/
{
{net::SchemefulSite(GURL("https://example.com")),
net::FirstPartySetEntry(
net::SchemefulSite(GURL("https://example.com")),
net::SiteType::kPrimary, absl::nullopt)},
{net::SchemefulSite(GURL("https://member1.com")),
net::FirstPartySetEntry(
net::SchemefulSite(GURL("https://example.com")),
net::SiteType::kAssociated, 0)},
},
/*aliases=*/{}));
first_party_sets_access_delegate_remote_->NotifyReady(
mojom::FirstPartySetsReadyEvent::New());
auto cookie_access_delegate = std::make_unique<CookieAccessDelegateImpl>(