0

Clickiness: Add viewAndClickCountsProviders IG field

Bug: 394108643
Change-Id: I4e7b3c66c03935d62f1f320552935683323754a7
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/6248184
Commit-Queue: Caleb Raitto <caraitto@chromium.org>
Reviewed-by: Yao Xiao <yaoxia@chromium.org>
Reviewed-by: Jeremy Roman <jbroman@chromium.org>
Reviewed-by: Ayu Ishii <ayui@chromium.org>
Reviewed-by: Maks Orlovich <morlovich@chromium.org>
Reviewed-by: Giovanni Ortuno Urquidi <ortuno@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1422203}
This commit is contained in:
Caleb Raitto
2025-02-19 14:17:03 -08:00
committed by Chromium LUCI CQ
parent f28f5e714c
commit 6bb1cf26eb
29 changed files with 571 additions and 61 deletions

@ -1592,6 +1592,7 @@ TEST_F(AdAuctionServiceImplTest, UpdateAllUpdatableFields) {
"trustedBiddingSignalsSlotSizeMode": "slot-size",
"maxTrustedBiddingSignalsURLLength": 8000,
"trustedBiddingSignalsCoordinator": "https://trusted-bidding-signals.coordinator-b.test",
"viewAndClickCountsProviders": ["https://view-and-click-counts-provider-b.test"],
"userBiddingSignals": {"test":10},
"updateURL": "%s/interest_group/new_daily_update_partial.json",
"ads": [{"renderURL": "%s/new_ad_render_url",
@ -1651,6 +1652,8 @@ TEST_F(AdAuctionServiceImplTest, UpdateAllUpdatableFields) {
interest_group.max_trusted_bidding_signals_url_length = 10000;
interest_group.trusted_bidding_signals_coordinator = url::Origin::Create(
GURL("https://trusted-bidding-signals.coordinator-a.test"));
interest_group.view_and_click_counts_providers = {{url::Origin::Create(
GURL("https://view-and-click-counts-provider-a.test"))}};
interest_group.ads.emplace();
std::vector<url::Origin> allowed_reporting_origins = {kOriginF};
blink::InterestGroup::Ad ad(
@ -1738,6 +1741,10 @@ TEST_F(AdAuctionServiceImplTest, UpdateAllUpdatableFields) {
EXPECT_EQ(group.max_trusted_bidding_signals_url_length, 8000);
EXPECT_EQ(group.trusted_bidding_signals_coordinator->Serialize(),
"https://trusted-bidding-signals.coordinator-b.test");
ASSERT_TRUE(group.view_and_click_counts_providers.has_value());
ASSERT_EQ(group.view_and_click_counts_providers->size(), 1u);
EXPECT_EQ(group.view_and_click_counts_providers.value()[0].Serialize(),
"https://view-and-click-counts-provider-b.test");
ASSERT_TRUE(group.user_bidding_signals.has_value());
EXPECT_EQ(group.user_bidding_signals.value(), "{\"test\":10}");

@ -5337,6 +5337,46 @@ IN_PROC_BROWSER_TEST_F(
EvalJs(shell(), JsReplace(kScriptTemplate, origin_string.c_str())));
}
class InterestGroupClickinessBrowserTest : public InterestGroupBrowserTest {
public:
InterestGroupClickinessBrowserTest() {
feature_list_.InitWithFeatures({blink::features::kFledgeClickiness},
/*disabled_features=*/{});
}
protected:
base::test::ScopedFeatureList feature_list_;
};
IN_PROC_BROWSER_TEST_F(InterestGroupClickinessBrowserTest,
JoinInterestGroupNonOriginViewAndClickCountsProviders) {
const char kScriptTemplate[] = R"(
(async function() {
try {
await navigator.joinAdInterestGroup(
{
name: 'cars',
owner: $1,
viewAndClickCountsProviders: ['hi'],
},
/*joinDurationSec=*/10000);
} catch (e) {
return e.toString();
}
return 'done';
})())";
GURL url = embedded_https_test_server().GetURL("a.test", "/echo");
std::string origin_string = url::Origin::Create(url).Serialize();
ASSERT_TRUE(NavigateToURL(shell(), url));
EXPECT_EQ(
"TypeError: Failed to execute 'joinAdInterestGroup' on 'Navigator': "
"viewAndClickCountsProviders 'hi' for AuctionAdInterestGroup with name "
"'cars' must be a valid https origin.",
EvalJs(shell(), JsReplace(kScriptTemplate, origin_string.c_str())));
}
IN_PROC_BROWSER_TEST_F(InterestGroupBrowserTest, RunAdAuctionInvalidSeller) {
ASSERT_TRUE(NavigateToURL(
shell(), embedded_https_test_server().GetURL("a.test", "/echo")));

@ -202,6 +202,7 @@ const base::FilePath::CharType kDatabasePath[] =
// Version 30 - 2024/08 - crrev.com/c/5707491
// Version 31 - 2025/01 - crrev.com/c/6084483
// Version 32 - 2025/02 - crrev.com/c/6239846
// Version 33 - 2025/02 - crrev.com/c/6248184
//
// Version 1 adds a table for interest groups.
// Version 2 adds a column for rate limiting interest group updates.
@ -243,14 +244,15 @@ const base::FilePath::CharType kDatabasePath[] =
// Version 30 compresses the AdsProto field using Snappy compression and runs a
// VACUUM command.
// Version 31 adds creative_scanning_metadata field to ad object.
// Version 32 adds duration column to the debug report lockout table, and rename
// its last_report_sent_time column to starting_time.
// Version 32 adds duration column to the debug report lockout table, and
// renames its last_report_sent_time column to starting_time.
// Version 33 adds view_and_click_counts_providers interest_groups field.
const int kCurrentVersionNumber = 32;
const int kCurrentVersionNumber = 33;
// Earliest version of the code which can use a |kCurrentVersionNumber| database
// without failing.
const int kCompatibleVersionNumber = 32;
const int kCompatibleVersionNumber = 33;
// Latest version of the database that cannot be upgraded to
// |kCurrentVersionNumber| without razing the database.
@ -718,6 +720,56 @@ std::optional<std::vector<std::string>> DeserializeStringVector(
return result;
}
std::string Serialize(const std::optional<std::vector<url::Origin>>& origins) {
std::string serialized;
ListOfOrigins list_of_origins;
if (origins) {
for (const url::Origin& o : origins.value()) {
list_of_origins.add_origins(o.Serialize());
}
}
if (list_of_origins.SerializeToString(&serialized)) {
base::UmaHistogramEnumeration(
"Storage.InterestGroup.ProtoSerializationResult.ListOfOrigins",
InterestGroupStorageProtoSerializationResult::kSucceeded);
} else {
base::UmaHistogramEnumeration(
"Storage.InterestGroup.ProtoSerializationResult.ListOfOrigins",
InterestGroupStorageProtoSerializationResult::kFailed);
// TODO(crbug.com/355010821): Consider bubbling out the failure.
}
return serialized;
}
std::optional<std::vector<url::Origin>> DeserializeOriginVector(
const std::string& serialized_vector) {
ListOfOrigins list_of_origins;
bool success = list_of_origins.ParseFromString(serialized_vector);
if (success) {
UMA_HISTOGRAM_ENUMERATION(
"Storage.InterestGroup.ProtoDeserializationResult.ListOfOrigins",
InterestGroupStorageProtoDeserializationResult::kSucceeded);
} else {
UMA_HISTOGRAM_ENUMERATION(
"Storage.InterestGroup.ProtoDeserializationResult.ListOfOrigins",
InterestGroupStorageProtoDeserializationResult::kFailed);
// TODO(crbug.com/355010821): Consider bubbling out the failure.
}
if (!success || list_of_origins.origins().empty()) {
return std::nullopt;
}
std::vector<url::Origin> result;
result.reserve(list_of_origins.origins_size());
for (const std::string& origin_string : list_of_origins.origins()) {
result.emplace_back(DeserializeOrigin(origin_string));
}
return result;
}
int64_t Serialize(SellerCapabilitiesType capabilities) {
uint64_t result = capabilities.ToEnumBitmask();
// Supporting 64 or more seller capabilities will require a different
@ -886,7 +938,7 @@ std::set<std::string> GetAllKanonKeys(
// Adds indices to the `interest_group` table.
// Call this function after the table has been created,
// both when creating a new database in CreateVxxSchema
// both when creating a new database in CreateCurrentSchema
// and after dropping/recreating the `interest_groups` table
// in the *latest* UpgradeVxxSchemaToVxx function to do so.
bool CreateInterestGroupIndices(sql::Database& db) {
@ -953,7 +1005,7 @@ bool CreateInterestGroupIndices(sql::Database& db) {
// Adds indices to the `kanon` table.
// Call this function after the table has been created,
// both when creating a new database in CreateVxxSchema
// both when creating a new database in CreateCurrentSchema
// and after dropping/recreating the `kanon` table
// in the *latest* UpgradeVxxSchemaToVxx function to do so.
bool CreateKAnonIndices(sql::Database& db) {
@ -1063,6 +1115,7 @@ bool CreateCurrentSchema(sql::Database& db) {
"trusted_bidding_signals_slot_size_mode INTEGER NOT NULL,"
"max_trusted_bidding_signals_url_length INTEGER NOT NULL,"
"trusted_bidding_signals_coordinator TEXT,"
"view_and_click_counts_providers TEXT,"
"user_bidding_signals TEXT,"
"ads_pb BLOB NOT NULL,"
"ad_components_pb BLOB NOT NULL,"
@ -1204,6 +1257,115 @@ bool VacuumDB(sql::Database& db) {
return db.Execute(kVacuum);
}
bool UpgradeV32SchemaToV33(sql::Database& db, sql::MetaTable& meta_table) {
// Make a table with new column `view_and_click_counts_providers`.
static const char kInterestGroupTableSql[] =
// clang-format off
"CREATE TABLE new_interest_groups("
"expiration INTEGER NOT NULL,"
"last_updated INTEGER NOT NULL,"
"next_update_after INTEGER NOT NULL,"
"owner TEXT NOT NULL,"
"joining_origin TEXT NOT NULL,"
"exact_join_time INTEGER NOT NULL,"
"name TEXT NOT NULL,"
"priority DOUBLE NOT NULL,"
"enable_bidding_signals_prioritization INTEGER NOT NULL,"
"priority_vector TEXT NOT NULL,"
"priority_signals_overrides TEXT NOT NULL,"
"seller_capabilities TEXT NOT NULL,"
"all_sellers_capabilities INTEGER NOT NULL,"
"execution_mode INTEGER NOT NULL,"
"joining_url TEXT NOT NULL,"
"bidding_url TEXT NOT NULL,"
"bidding_wasm_helper_url TEXT NOT NULL,"
"update_url TEXT NOT NULL,"
"trusted_bidding_signals_url TEXT NOT NULL,"
"trusted_bidding_signals_keys TEXT NOT NULL,"
"trusted_bidding_signals_slot_size_mode INTEGER NOT NULL,"
"max_trusted_bidding_signals_url_length INTEGER NOT NULL,"
"trusted_bidding_signals_coordinator TEXT,"
"view_and_click_counts_providers TEXT,"
"user_bidding_signals TEXT,"
"ads_pb BLOB NOT NULL,"
"ad_components_pb BLOB NOT NULL,"
"ad_sizes TEXT NOT NULL,"
"size_groups TEXT NOT NULL,"
"auction_server_request_flags INTEGER NOT NULL,"
"additional_bid_key BLOB NOT NULL,"
"aggregation_coordinator_origin TEXT,"
"storage_size INTEGER NOT NULL,"
"last_k_anon_updated_time INTEGER NOT NULL, "
"kanon_keys BLOB NOT NULL,"
"PRIMARY KEY(owner,name))";
// clang-format on
if (!db.Execute(kInterestGroupTableSql)) {
return false;
}
static const char kCopyInterestGroupTableSql[] =
// clang-format off
"INSERT INTO new_interest_groups "
"SELECT expiration,"
"last_updated,"
"next_update_after,"
"owner,"
"joining_origin,"
"exact_join_time,"
"name,"
"priority,"
"enable_bidding_signals_prioritization,"
"priority_vector,"
"priority_signals_overrides,"
"seller_capabilities,"
"all_sellers_capabilities,"
"execution_mode,"
"joining_url,"
"bidding_url,"
"bidding_wasm_helper_url,"
"update_url,"
"trusted_bidding_signals_url,"
"trusted_bidding_signals_keys,"
"trusted_bidding_signals_slot_size_mode,"
"max_trusted_bidding_signals_url_length,"
"trusted_bidding_signals_coordinator,"
"NULL," // view_and_click_counts_providers
"user_bidding_signals,"
"ads_pb,"
"ad_components_pb,"
"ad_sizes,"
"size_groups,"
"auction_server_request_flags,"
"additional_bid_key,"
"aggregation_coordinator_origin,"
"storage_size,"
"last_k_anon_updated_time,"
"kanon_keys "
"FROM interest_groups";
// clang-format on
if (!db.Execute(kCopyInterestGroupTableSql)) {
return false;
}
static const char kDropInterestGroupTableSql[] = "DROP TABLE interest_groups";
if (!db.Execute(kDropInterestGroupTableSql)) {
return false;
}
static const char kRenameInterestGroupTableSql[] =
// clang-format off
"ALTER TABLE new_interest_groups "
"RENAME TO interest_groups";
// clang-format on
if (!db.Execute(kRenameInterestGroupTableSql)) {
return false;
}
return CreateInterestGroupIndices(db);
}
bool UpgradeV31SchemaToV32(sql::Database& db, sql::MetaTable& meta_table) {
// Adds duration column to the debug report lockout table, and rename its
// last_report_sent_time column to starting_time.
@ -3114,6 +3276,11 @@ bool UpgradeDB(sql::Database& db,
if (!UpgradeV31SchemaToV32(db, meta_table)) {
return false;
}
[[fallthrough]];
case 32:
if (!UpgradeV32SchemaToV33(db, meta_table)) {
return false;
}
if (!meta_table.SetVersionNumber(kCurrentVersionNumber)) {
return false;
}
@ -3333,6 +3500,7 @@ std::optional<std::vector<std::string>> DoClearOriginJoinedInterestGroups(
"trusted_bidding_signals_slot_size_mode," \
"max_trusted_bidding_signals_url_length," \
"trusted_bidding_signals_coordinator," \
"view_and_click_counts_providers," \
"user_bidding_signals," /* opaque data */ \
"ads_pb," \
"ad_components_pb," \
@ -3387,28 +3555,32 @@ void PopulateInterestGroupFromQueryResult(sql::Statement& load,
DeserializeOrigin(load.ColumnString(19));
}
if (load.GetColumnType(20) != sql::ColumnType::kNull) {
group.interest_group.user_bidding_signals = load.ColumnString(20);
group.interest_group.view_and_click_counts_providers =
DeserializeOriginVector(load.ColumnString(20));
}
if (load.GetColumnType(21) != sql::ColumnType::kNull) {
group.interest_group.user_bidding_signals = load.ColumnString(21);
}
group.interest_group.ads = DecompressAndDeserializeInterestGroupAdVectorProto(
passkey, load.ColumnString(21));
passkey, load.ColumnString(22));
group.interest_group.ad_components =
DecompressAndDeserializeInterestGroupAdVectorProto(passkey,
load.ColumnString(22));
load.ColumnString(23));
group.interest_group.ad_sizes =
DeserializeStringSizeMap(load.ColumnString(23));
DeserializeStringSizeMap(load.ColumnString(24));
group.interest_group.size_groups =
DeserializeStringStringVectorMap(load.ColumnString(24));
DeserializeStringStringVectorMap(load.ColumnString(25));
group.interest_group.auction_server_request_flags =
DeserializeAuctionServerRequestFlags(load.ColumnInt64(25));
DeserializeAuctionServerRequestFlags(load.ColumnInt64(26));
group.interest_group.additional_bid_key =
DeserializeAdditionalBidKey(load.ColumnBlob(26));
if (load.GetColumnType(27) != sql::ColumnType::kNull) {
DeserializeAdditionalBidKey(load.ColumnBlob(27));
if (load.GetColumnType(28) != sql::ColumnType::kNull) {
group.interest_group.aggregation_coordinator_origin =
DeserializeOrigin(load.ColumnString(27));
DeserializeOrigin(load.ColumnString(28));
}
group.last_k_anon_updated = load.ColumnTime(28);
group.last_k_anon_updated = load.ColumnTime(29);
KAnonKeyProtos keys_proto;
if (keys_proto.ParseFromString(load.ColumnString(29))) {
if (keys_proto.ParseFromString(load.ColumnString(30))) {
base::UmaHistogramEnumeration(
"Storage.InterestGroup.ProtoDeserializationResult.KAnonKeyProtos",
InterestGroupStorageProtoDeserializationResult::kSucceeded);
@ -3602,6 +3774,7 @@ std::optional<InterestGroupKanonUpdateParameter> DoJoinInterestGroup(
"trusted_bidding_signals_slot_size_mode,"
"max_trusted_bidding_signals_url_length,"
"trusted_bidding_signals_coordinator,"
"view_and_click_counts_providers,"
"user_bidding_signals," // opaque data
"ads_pb,"
"ad_components_pb,"
@ -3614,7 +3787,7 @@ std::optional<InterestGroupKanonUpdateParameter> DoJoinInterestGroup(
"last_k_anon_updated_time,"
"kanon_keys) "
"VALUES("
"?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)"
"?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)"
));
// clang-format on
@ -3651,24 +3824,29 @@ std::optional<InterestGroupKanonUpdateParameter> DoJoinInterestGroup(
} else {
join_group.BindNull(22);
}
if (data.user_bidding_signals) {
join_group.BindString(23, data.user_bidding_signals.value());
if (data.view_and_click_counts_providers) {
join_group.BindString(23, Serialize(*data.view_and_click_counts_providers));
} else {
join_group.BindNull(23);
}
join_group.BindBlob(24, Serialize(data.ads));
join_group.BindBlob(25, Serialize(data.ad_components));
join_group.BindString(26, Serialize(data.ad_sizes));
join_group.BindString(27, Serialize(data.size_groups));
join_group.BindInt64(28, Serialize(data.auction_server_request_flags));
join_group.BindBlob(29, Serialize(data.additional_bid_key));
if (data.aggregation_coordinator_origin) {
join_group.BindString(30, Serialize(*data.aggregation_coordinator_origin));
if (data.user_bidding_signals) {
join_group.BindString(24, data.user_bidding_signals.value());
} else {
join_group.BindNull(30);
join_group.BindNull(24);
}
join_group.BindInt64(31, data.EstimateSize());
join_group.BindTime(32, last_k_anon_updated);
join_group.BindBlob(25, Serialize(data.ads));
join_group.BindBlob(26, Serialize(data.ad_components));
join_group.BindString(27, Serialize(data.ad_sizes));
join_group.BindString(28, Serialize(data.size_groups));
join_group.BindInt64(29, Serialize(data.auction_server_request_flags));
join_group.BindBlob(30, Serialize(data.additional_bid_key));
if (data.aggregation_coordinator_origin) {
join_group.BindString(31, Serialize(*data.aggregation_coordinator_origin));
} else {
join_group.BindNull(31);
}
join_group.BindInt64(32, data.EstimateSize());
join_group.BindTime(33, last_k_anon_updated);
KAnonKeyProtos key_proto;
*key_proto.mutable_keys() = {positive_kanon_keys.begin(),
positive_kanon_keys.end()};
@ -3683,7 +3861,7 @@ std::optional<InterestGroupKanonUpdateParameter> DoJoinInterestGroup(
InterestGroupStorageProtoSerializationResult::kFailed);
// TODO(crbug.com/355010821): Consider bubbling out the failure.
}
join_group.BindBlob(33, key_proto_str);
join_group.BindBlob(34, key_proto_str);
if (!join_group.Run()) {
return std::nullopt;
@ -3745,6 +3923,7 @@ bool DoStoreInterestGroupUpdate(
"trusted_bidding_signals_slot_size_mode=?,"
"max_trusted_bidding_signals_url_length=?,"
"trusted_bidding_signals_coordinator=?,"
"view_and_click_counts_providers=?,"
"user_bidding_signals=?,"
"ads_pb=?,"
"ad_components_pb=?,"
@ -3787,24 +3966,30 @@ bool DoStoreInterestGroupUpdate(
} else {
store_group.BindNull(16);
}
if (group.user_bidding_signals) {
store_group.BindString(17, group.user_bidding_signals.value());
if (group.view_and_click_counts_providers) {
store_group.BindString(17,
Serialize(*group.view_and_click_counts_providers));
} else {
store_group.BindNull(17);
}
store_group.BindBlob(18, Serialize(group.ads));
store_group.BindBlob(19, Serialize(group.ad_components));
store_group.BindString(20, Serialize(group.ad_sizes));
store_group.BindString(21, Serialize(group.size_groups));
store_group.BindInt64(22, Serialize(group.auction_server_request_flags));
store_group.BindBlob(23, Serialize(group.additional_bid_key));
if (group.user_bidding_signals) {
store_group.BindString(18, group.user_bidding_signals.value());
} else {
store_group.BindNull(18);
}
store_group.BindBlob(19, Serialize(group.ads));
store_group.BindBlob(20, Serialize(group.ad_components));
store_group.BindString(21, Serialize(group.ad_sizes));
store_group.BindString(22, Serialize(group.size_groups));
store_group.BindInt64(23, Serialize(group.auction_server_request_flags));
store_group.BindBlob(24, Serialize(group.additional_bid_key));
if (group.aggregation_coordinator_origin) {
store_group.BindString(24,
store_group.BindString(25,
Serialize(*group.aggregation_coordinator_origin));
} else {
store_group.BindNull(24);
store_group.BindNull(25);
}
store_group.BindInt64(25, group.EstimateSize());
store_group.BindInt64(26, group.EstimateSize());
KAnonKeyProtos key_proto;
*key_proto.mutable_keys() = {positive_kanon_keys.begin(),
@ -3820,10 +4005,10 @@ bool DoStoreInterestGroupUpdate(
InterestGroupStorageProtoSerializationResult::kFailed);
// TODO(crbug.com/355010821): Consider bubbling out the failure.
}
store_group.BindBlob(26, key_proto_str);
store_group.BindBlob(27, key_proto_str);
store_group.BindString(27, Serialize(group.owner));
store_group.BindString(28, group.name);
store_group.BindString(28, Serialize(group.owner));
store_group.BindString(29, group.name);
return store_group.Run();
}
@ -3919,6 +4104,10 @@ std::optional<InterestGroupKanonUpdateParameter> DoUpdateInterestGroup(
updated_group.trusted_bidding_signals_coordinator =
std::move(update.trusted_bidding_signals_coordinator.value());
}
if (update.view_and_click_counts_providers.has_value()) {
updated_group.view_and_click_counts_providers =
std::move(update.view_and_click_counts_providers.value());
}
if (update.user_bidding_signals) {
updated_group.user_bidding_signals = std::move(update.user_bidding_signals);
}
@ -4864,7 +5053,7 @@ std::optional<std::vector<StorageInterestGroup>> DoGetInterestGroupsForOwner(
load.BindTime(1, now);
while (load.Step()) {
std::string name = load.ColumnString(30);
std::string name = load.ColumnString(31);
StorageInterestGroup& db_interest_group = interest_group_by_name[name];
db_interest_group.bidding_browser_signals =
blink::mojom::BiddingBrowserSignals::New();

@ -34,3 +34,7 @@ message BiddingAndAuctionServerKeyProtos {
message KAnonKeyProtos {
repeated bytes keys = 1;
}
message ListOfOrigins {
repeated string origins = 1;
}

@ -212,12 +212,16 @@ class InterestGroupStorageTest : public testing::Test {
// instance.
switch (version_number) {
case 33:
result.view_and_click_counts_providers = {{url::Origin::Create(
GURL("https://view-and-click-counts-provider.test"))}};
[[fallthrough]];
case 32:
[[fallthrough]];
case 31:
result.ads.value()[0].creative_scanning_metadata = "scan 1";
result.ad_components.value()[1].creative_scanning_metadata = "scan 2";
ABSL_FALLTHROUGH_INTENDED;
[[fallthrough]];
case 30:
// Compressed AdsProto, but introduced no new fields.
[[fallthrough]];
@ -3583,6 +3587,16 @@ TEST_F(InterestGroupStorageTest, MultiVersionUpgradeTest) {
}
}
// Make sure the metadata table got upgraded correctly.
{
sql::Database raw_db(sql::test::kTestTag);
EXPECT_TRUE(raw_db.Open(db_path()));
sql::MetaTable meta;
ASSERT_TRUE(meta.Init(&raw_db, 1, 1));
EXPECT_EQ(InterestGroupStorage::GetCurrentVersionNumberForTesting(),
meta.GetVersionNumber());
}
// Delete the database in case we loop again, creating the database from
// another .sql file.
base::DeleteFile(db_path());
@ -3597,6 +3611,26 @@ TEST_F(InterestGroupStorageTest, MultiVersionUpgradeTest) {
InterestGroupStorage::GetCurrentVersionNumberForTesting()));
ASSERT_TRUE(base::PathExists(file_path))
<< "Missing " << file_path << " -- " << kMisssingFileError;
// Also, make sure that the current version matches ProduceAllFields() -- they
// might not match if the storage format changed after the initial DB dump for
// the current version (that is, it changed before the CL introducing the new
// version landed).
{
ASSERT_TRUE(sql::test::CreateDatabaseFromSQL(db_path(), file_path));
blink::InterestGroup expected = ProduceAllFields();
std::unique_ptr<InterestGroupStorage> storage = CreateStorage();
std::vector<StorageInterestGroup> interest_groups =
storage->GetAllInterestGroupsUnfilteredForTesting();
ASSERT_EQ(1u, interest_groups.size());
const blink::InterestGroup& actual = interest_groups[0].interest_group;
// Don't compare `expiry` as it changes every test run.
expected.expiry = actual.expiry;
IgExpectEqualsForTesting(actual, expected);
// Delete the database before the next testcase.
base::DeleteFile(db_path());
}
}
TEST_F(InterestGroupStorageTest,

@ -57,6 +57,7 @@ struct CONTENT_EXPORT InterestGroupUpdate {
// when the value is `null` in JSON, allowing for a downgrade from KVv2 to
// KVv1.
std::optional<std::optional<url::Origin>> trusted_bidding_signals_coordinator;
std::optional<std::vector<url::Origin>> view_and_click_counts_providers;
std::optional<std::string> user_bidding_signals;
std::optional<std::vector<blink::InterestGroup::Ad>> ads, ad_components;
std::optional<base::flat_map<std::string, blink::AdSize>> ad_sizes;

@ -424,6 +424,48 @@ constexpr net::NetworkTrafficAnnotationTag kTrafficAnnotation =
return true;
}
// Copies the viewAndClickCountsProviders JSON field into
// `view_and_click_counts_providers`.
[[nodiscard]] bool TryToCopyViewAndClickCountsProviders(
const base::Value::Dict& dict,
InterestGroupUpdate& interest_group_update) {
const base::Value* maybe_view_and_click_counts_providers =
dict.Find("viewAndClickCountsProviders");
// No `viewAndClickCountsProviders` field in the update JSON.
if (!maybe_view_and_click_counts_providers) {
return true;
}
// `viewAndClickCountsProviders` field is `null` in the update JSON.
if (maybe_view_and_click_counts_providers->is_none()) {
interest_group_update.view_and_click_counts_providers = std::nullopt;
return true;
}
// If `view_and_click_counts_providers` is present and not null, it must
// be a valid list of URL origin strings.
if (!maybe_view_and_click_counts_providers->is_list()) {
return false;
}
const base::Value::List& view_and_click_counts_providers =
maybe_view_and_click_counts_providers->GetList();
interest_group_update.view_and_click_counts_providers.emplace();
interest_group_update.view_and_click_counts_providers->reserve(
view_and_click_counts_providers.size());
for (const base::Value& provider : view_and_click_counts_providers) {
if (!provider.is_string()) {
return false;
}
interest_group_update.view_and_click_counts_providers->emplace_back(
url::Origin::Create(GURL(provider.GetString())));
}
return true;
}
// Helper for TryToCopyAds() and TryToCopyAdComponents().
[[nodiscard]] std::optional<std::vector<blink::InterestGroup::Ad>> ExtractAds(
const base::Value::List& ads_list,
@ -782,6 +824,9 @@ std::optional<InterestGroupUpdate> ParseUpdateJson(
interest_group_update)) {
return std::nullopt;
}
if (!TryToCopyViewAndClickCountsProviders(*dict, interest_group_update)) {
return std::nullopt;
}
if (!TryToCopyUserBiddingSignals(*dict, interest_group_update)) {
return std::nullopt;
}

@ -7331,6 +7331,7 @@ data/interest_group/autogenSchemaV29.sql
data/interest_group/autogenSchemaV30.sql
data/interest_group/autogenSchemaV31.sql
data/interest_group/autogenSchemaV32.sql
data/interest_group/autogenSchemaV33.sql
data/interest_group/bidding_argument_validator.js
data/interest_group/bidding_argument_validator.js.mock-http-headers
data/interest_group/bidding_logic.js

@ -0,0 +1,24 @@
PRAGMA foreign_keys=OFF;
BEGIN TRANSACTION;
CREATE TABLE meta(key LONGVARCHAR NOT NULL UNIQUE PRIMARY KEY, value LONGVARCHAR);
INSERT INTO meta VALUES('mmap_status','-1');
INSERT INTO meta VALUES('version','33');
INSERT INTO meta VALUES('last_compatible_version','33');
CREATE TABLE interest_groups(expiration INTEGER NOT NULL,last_updated INTEGER NOT NULL,next_update_after INTEGER NOT NULL,owner TEXT NOT NULL,joining_origin TEXT NOT NULL,exact_join_time INTEGER NOT NULL,name TEXT NOT NULL,priority DOUBLE NOT NULL,enable_bidding_signals_prioritization INTEGER NOT NULL,priority_vector TEXT NOT NULL,priority_signals_overrides TEXT NOT NULL,seller_capabilities TEXT NOT NULL,all_sellers_capabilities INTEGER NOT NULL,execution_mode INTEGER NOT NULL,joining_url TEXT NOT NULL,bidding_url TEXT NOT NULL,bidding_wasm_helper_url TEXT NOT NULL,update_url TEXT NOT NULL,trusted_bidding_signals_url TEXT NOT NULL,trusted_bidding_signals_keys TEXT NOT NULL,trusted_bidding_signals_slot_size_mode INTEGER NOT NULL,max_trusted_bidding_signals_url_length INTEGER NOT NULL,trusted_bidding_signals_coordinator TEXT,view_and_click_counts_providers TEXT,user_bidding_signals TEXT,ads_pb BLOB NOT NULL,ad_components_pb BLOB NOT NULL,ad_sizes TEXT NOT NULL,size_groups TEXT NOT NULL,auction_server_request_flags INTEGER NOT NULL,additional_bid_key BLOB NOT NULL,aggregation_coordinator_origin TEXT,storage_size INTEGER NOT NULL,last_k_anon_updated_time INTEGER NOT NULL,kanon_keys BLOB NOT NULL,PRIMARY KEY(owner,name));
INSERT INTO interest_groups VALUES(11648709739634000,11646117739634000,-9223372036854775808,'https://full.example.com','https://full.example.com',11646117739634000,'full',1.0,1,'{"a":2.0,"b":-2.2}','{"a":-2.0,"c":10.0,"d":1.2}','{"https://full.example.com":"1","https://partial.example.com":"2"}',3,2,'https://full.example.com/','https://full.example.com/bid','https://full.example.com/bid_wasm','https://full.example.com/update','https://full.example.com/signals','["a","b","c","d"]',2,8000,'https://coordinator.test',replace('\n+https://view-and-click-counts-provider.test','\n',char(10)),'foo',X'd401f0580a92010a1c68747470733a2f2f66756c6c2e6578616d706c652e636f6d2f616431120767726f75705f311a096d6574616461746131220862757965725f69642a097368617265645f6964320a616452656e64657249643a15680d53207265706f7274696e6701503c420e73656c65637461626c655f6964313a100034324a067363616e20310a3d0a1c680d410066469400003211940c321a096d0d942c32220962757965725f696432',X'9101f04c0a490a2568747470733a2f2f66756c6c2e6578616d706c652e636f6d2f6164636f6d706f6e656e7431120767726f75705f311a0a6d657461646174613163320b616452656e6465724964320a44964b000032114b04321a154b2432634a067363616e2032','{"size_1":"{\"height\":150.0,\"height_units\":1,\"width\":300.0,\"width_units\":1}","size_2":"{\"height\":480.0,\"height_units\":1,\"width\":640.0,\"width_units\":1}","size_3":"{\"height\":100.0,\"height_units\":2,\"width\":100.0,\"width_units\":2}"}','{"group_1":"[\"size_1\"]","group_2":"[\"size_1\",\"size_2\"]","group_3":"[\"size_3\"]"}',3,X'','https://coordinator.test',822,-9223372036854775808,X'');
CREATE TABLE joined_k_anon(hashed_key BLOB NOT NULL,last_reported_to_anon_server_time INTEGER NOT NULL,PRIMARY KEY(hashed_key));
CREATE TABLE join_history(owner TEXT NOT NULL,name TEXT NOT NULL,join_time INTEGER NOT NULL,count INTEGER NOT NULL,PRIMARY KEY(owner, name, join_time) FOREIGN KEY(owner,name) REFERENCES interest_groups);
INSERT INTO join_history VALUES('https://full.example.com','full',11646115200000000,1);
CREATE TABLE bid_history(owner TEXT NOT NULL,name TEXT NOT NULL,bid_time INTEGER NOT NULL,count INTEGER NOT NULL,PRIMARY KEY(owner, name, bid_time) FOREIGN KEY(owner,name) REFERENCES interest_groups);
CREATE TABLE win_history(owner TEXT NOT NULL,name TEXT NOT NULL,win_time INTEGER NOT NULL,ad TEXT NOT NULL,FOREIGN KEY(owner,name) REFERENCES interest_groups);
CREATE TABLE lockout_debugging_only_report(id INTEGER NOT NULL,starting_time INTEGER NOT NULL,duration INTEGER NOT NULL,PRIMARY KEY(id));
CREATE TABLE cooldown_debugging_only_report(origin TEXT NOT NULL,starting_time INTEGER NOT NULL,type INTEGER NOT NULL,PRIMARY KEY(origin));
CREATE TABLE bidding_and_auction_server_keys(coordinator TEXT NOT NULL,keys BLOB NOT NULL,expiration INTEGER NOT NULL,PRIMARY KEY(coordinator));
CREATE INDEX interest_group_expiration ON interest_groups(expiration DESC, owner, name);
CREATE INDEX interest_group_owner ON interest_groups(owner,expiration DESC,next_update_after ASC,name);
CREATE INDEX interest_group_owner_and_type ON interest_groups(LENGTH(additional_bid_key) == 0,owner,expiration DESC,name);
CREATE INDEX interest_group_owner_size ON interest_groups(owner,expiration DESC,storage_size);
CREATE INDEX interest_group_joining_origin ON interest_groups(joining_origin, expiration DESC, owner, name);
CREATE INDEX k_anon_last_server_time_idx ON joined_k_anon(last_reported_to_anon_server_time DESC);
CREATE INDEX win_history_index ON win_history(owner,name,win_time DESC);
COMMIT;

@ -504,6 +504,8 @@ base::Value::Dict SerializeInterestGroupForDevtools(const InterestGroup& ig) {
ig.max_trusted_bidding_signals_url_length, result);
SerializeIntoDict("trustedBiddingSignalsCoordinator",
ig.trusted_bidding_signals_coordinator, result);
SerializeIntoDict("viewAndClickCountsProviders",
ig.view_and_click_counts_providers, result);
SerializeIntoDict("userBiddingSignals", ig.user_bidding_signals, result);
SerializeIntoDict("ads", ig.ads, result);
SerializeIntoDict("adComponents", ig.ad_components, result);

@ -265,6 +265,8 @@ TEST(SerializeInterestGroupTest, Basic) {
ig.max_trusted_bidding_signals_url_length = 100;
ig.trusted_bidding_signals_coordinator =
url::Origin::Create(GURL("https://example.test"));
ig.view_and_click_counts_providers = {
{url::Origin::Create(GURL("https://example.test"))}};
ig.user_bidding_signals = "hello";
ig.ads = {
{blink::InterestGroup::Ad(
@ -309,6 +311,7 @@ TEST(SerializeInterestGroupTest, Basic) {
"trustedBiddingSignalsSlotSizeMode": "all-slots-requested-sizes",
"maxTrustedBiddingSignalsURLLength": 100,
"trustedBiddingSignalsCoordinator": "https://example.test",
"viewAndClickCountsProviders": ["https://example.test"],
"userBiddingSignals": "hello",
"ads": [ {
"adRenderId": "ad_render_id",

@ -316,6 +316,14 @@ bool InterestGroup::IsValid() const {
}
}
if (view_and_click_counts_providers) {
for (const url::Origin& provider : *view_and_click_counts_providers) {
if (provider.scheme() != url::kHttpsScheme) {
return false;
}
}
}
if (ads) {
std::optional<size_t> selectable_buyer_and_seller_reporting_ids_hard_limit;
if (base::FeatureList::IsEnabled(
@ -504,6 +512,11 @@ size_t InterestGroup::EstimateSize() const {
if (trusted_bidding_signals_coordinator) {
size += trusted_bidding_signals_coordinator->Serialize().size();
}
if (view_and_click_counts_providers) {
for (const url::Origin& provider : *view_and_click_counts_providers) {
size += provider.Serialize().size();
}
}
if (user_bidding_signals) {
size += user_bidding_signals->size();
}

@ -81,6 +81,8 @@ bool StructTraits<blink::mojom::InterestGroupDataView, blink::InterestGroup>::
!data.ReadTrustedBiddingSignalsKeys(&out->trusted_bidding_signals_keys) ||
!data.ReadTrustedBiddingSignalsCoordinator(
&out->trusted_bidding_signals_coordinator) ||
!data.ReadViewAndClickCountsProviders(
&out->view_and_click_counts_providers) ||
!data.ReadUserBiddingSignals(&out->user_bidding_signals) ||
!data.ReadAds(&out->ads) || !data.ReadAdComponents(&out->ad_components) ||
!data.ReadAdSizes(&out->ad_sizes) ||

@ -290,6 +290,23 @@ TEST(InterestGroupMojomTraitsTest,
"trustedBiddingSignalsCoordinator");
}
TEST(InterestGroupMojomTraitsTest,
SerializeAndDeserializeViewAndClickCountsProviders) {
InterestGroup interest_group = CreateInterestGroup();
interest_group.view_and_click_counts_providers = {
{url::Origin::Create(GURL("https://example.test"))}};
SerializeAndDeserializeAndCompare(interest_group);
}
TEST(InterestGroupMojomTraitsTest,
SerializeAndDeserializeInvalidViewAndClickCountsProviders) {
InterestGroup interest_group = CreateInterestGroup();
interest_group.view_and_click_counts_providers = {
{url::Origin::Create(GURL("http://example.test"))}};
SerializeAndDeserializeExpectFailure(interest_group,
"viewAndClickCountsProviders");
}
TEST(InterestGroupMojomTraitsTest, SerializeAndDeserializeUserBiddingSignals) {
InterestGroup interest_group = CreateInterestGroup();
interest_group.user_bidding_signals = "[]";

@ -204,6 +204,12 @@ void InterestGroupCompare(const blink::InterestGroup& actual,
expected.max_trusted_bidding_signals_url_length);
IG_COMPARE(actual.trusted_bidding_signals_coordinator,
expected.trusted_bidding_signals_coordinator);
auto compare_origins = [&](const url::Origin& actual,
const url::Origin& expected) {
IG_COMPARE(actual, expected);
};
IG_COMPARE_VEC(actual.view_and_click_counts_providers,
expected.view_and_click_counts_providers, compare_origins);
IG_COMPARE(actual.user_bidding_signals, expected.user_bidding_signals);
auto compare_ads = [&](const blink::InterestGroup::Ad& actual,
const blink::InterestGroup::Ad& expected) {
@ -218,10 +224,6 @@ void InterestGroupCompare(const blink::InterestGroup& actual,
compare_strings);
IG_COMPARE(actual.ad_render_id, expected.ad_render_id);
auto compare_origins = [&](const url::Origin& actual,
const url::Origin& expected) {
IG_COMPARE(actual, expected);
};
IG_COMPARE_VEC(actual.allowed_reporting_origins,
expected.allowed_reporting_origins, compare_origins);
IG_COMPARE(actual.creative_scanning_metadata,

@ -143,7 +143,15 @@ TestInterestGroupBuilder&
TestInterestGroupBuilder::SetTrustedBiddingSignalsCoordinator(
std::optional<url::Origin> trusted_bidding_signals_coordinator) {
interest_group_.trusted_bidding_signals_coordinator =
trusted_bidding_signals_coordinator;
std::move(trusted_bidding_signals_coordinator);
return *this;
}
TestInterestGroupBuilder&
TestInterestGroupBuilder::SetViewAndClickCountsProviders(
std::optional<std::vector<url::Origin>> view_and_click_counts_providers) {
interest_group_.view_and_click_counts_providers =
std::move(view_and_click_counts_providers);
return *this;
}
@ -194,7 +202,8 @@ TestInterestGroupBuilder& TestInterestGroupBuilder::SetAdditionalBidKey(
TestInterestGroupBuilder&
TestInterestGroupBuilder::SetAggregationCoordinatorOrigin(
std::optional<url::Origin> agg_coordinator_origin) {
interest_group_.aggregation_coordinator_origin = agg_coordinator_origin;
interest_group_.aggregation_coordinator_origin =
std::move(agg_coordinator_origin);
return *this;
}

@ -170,6 +170,7 @@ struct BLINK_COMMON_EXPORT InterestGroup {
TrustedBiddingSignalsSlotSizeMode::kNone;
int32_t max_trusted_bidding_signals_url_length = 0;
std::optional<url::Origin> trusted_bidding_signals_coordinator;
std::optional<std::vector<url::Origin>> view_and_click_counts_providers;
std::optional<std::string> user_bidding_signals;
std::optional<std::vector<InterestGroup::Ad>> ads, ad_components;
std::optional<base::flat_map<std::string, blink::AdSize>> ad_sizes;
@ -181,7 +182,7 @@ struct BLINK_COMMON_EXPORT InterestGroup {
std::optional<AdditionalBidKey> additional_bid_key;
std::optional<url::Origin> aggregation_coordinator_origin;
static_assert(__LINE__ == 184, R"(
static_assert(__LINE__ == 185, R"(
If modifying InterestGroup fields, make sure to also modify:
* IsValid(), EstimateSize(), and in this class
@ -189,13 +190,13 @@ If modifying InterestGroup fields, make sure to also modify:
* SerializeInterestGroupForDevtools()
(in devtools_serialization.cc; test in devtools_serialization_unittest.cc)
* auction_ad_interest_group.idl
* navigator_auction.cc
* navigator_auction.cc (test logic in interest_group_browsertest.cc)
* interest_group_types.mojom
* validate_blink_interest_group.cc (includes blink version of EstimateSize)
* validate_blink_interest_group_test.cc
(at least CreateFullyPopulatedInterestGroup)
* test_interest_group_builder[.h/.cc]
* interest_group_mojom_traits[.h/.cc/.test]
* interest_group_mojom_traits[.h/.cc/_test.cc]
* bidder_lazy_filler.cc (to pass the InterestGroup to generateBid())
* shared_storage_worklet_global_scope.cc (interestGroups())
* shared_storage_worklet_unittest.cc (SharedStorageWorkletTest.InterestGroups)

@ -206,6 +206,11 @@ struct BLINK_COMMON_EXPORT
return interest_group.trusted_bidding_signals_coordinator;
}
static const std::optional<std::vector<url::Origin>>&
view_and_click_counts_providers(const blink::InterestGroup& interest_group) {
return interest_group.view_and_click_counts_providers;
}
static const std::optional<std::string>& user_bidding_signals(
const blink::InterestGroup& interest_group) {
return interest_group.user_bidding_signals;

@ -63,6 +63,8 @@ class TestInterestGroupBuilder {
int32_t max_trusted_bidding_signals_url_length);
TestInterestGroupBuilder& SetTrustedBiddingSignalsCoordinator(
std::optional<url::Origin> trusted_bidding_signals_coordinator);
TestInterestGroupBuilder& SetViewAndClickCountsProviders(
std::optional<std::vector<url::Origin>> view_and_click_counts_providers);
TestInterestGroupBuilder& SetUserBiddingSignals(
std::optional<std::string> user_bidding_signals);
TestInterestGroupBuilder& SetAds(

@ -179,6 +179,9 @@ struct InterestGroup {
// An optional field for matching the encryption key, and indicating which
// version of the key-value server this interest group uses.
url.mojom.Origin? trusted_bidding_signals_coordinator;
// Stores the set of origins that are allowed to provide view and click
// counts to this interest group's generateBid() function.
array<url.mojom.Origin>? view_and_click_counts_providers;
// Opaque JSON data, persisted, then passed as object to auction worklet.
string? user_bidding_signals;
array<InterestGroupAd>? ads;

@ -67,6 +67,8 @@ dictionary AuctionAdInterestGroup {
long maxTrustedBiddingSignalsURLLength;
[RuntimeEnabled=FledgeTrustedSignalsKVv2Support]
USVString trustedBiddingSignalsCoordinator;
[RuntimeEnabled=FledgeClickiness]
sequence<USVString> viewAndClickCountsProviders;
any userBiddingSignals;
sequence<AuctionAd> ads;
sequence<AuctionAd> adComponents;

@ -942,6 +942,31 @@ bool CopyTrustedBiddingSignalsCoordinatorFromIdlToMojo(
return true;
}
bool CopyViewAndClickCountsProvidersFromIdlToMojo(
ExceptionState& exception_state,
const AuctionAdInterestGroup& input,
mojom::blink::InterestGroup& output) {
if (!input.hasViewAndClickCountsProviders()) {
return true;
}
Vector<scoped_refptr<const SecurityOrigin>> view_and_click_counts_providers;
for (const String& provider : input.viewAndClickCountsProviders()) {
scoped_refptr<const SecurityOrigin> parsed_provider = ParseOrigin(provider);
if (!parsed_provider) {
exception_state.ThrowTypeError(String::Format(
"viewAndClickCountsProviders '%s' for AuctionAdInterestGroup "
"with name '%s' must be a valid https origin.",
provider.Utf8().c_str(), input.name().Utf8().c_str()));
return false;
}
}
output.view_and_click_counts_providers =
std::move(view_and_click_counts_providers);
return true;
}
bool CopyUserBiddingSignalsFromIdlToMojo(const ScriptState& script_state,
ExceptionState& exception_state,
const AuctionAdInterestGroup& input,
@ -3464,6 +3489,8 @@ ScriptPromise<IDLUndefined> NavigatorAuction::joinAdInterestGroup(
exception_state, *group, *mojo_group) ||
!CopyTrustedBiddingSignalsCoordinatorFromIdlToMojo(exception_state,
*group, *mojo_group) ||
!CopyViewAndClickCountsProvidersFromIdlToMojo(exception_state, *group,
*mojo_group) ||
!CopyUserBiddingSignalsFromIdlToMojo(*script_state, exception_state,
*group, *mojo_group) ||
!CopyAdsFromIdlToMojo(*context, *script_state, exception_state, *group,

@ -123,6 +123,12 @@ size_t EstimateBlinkInterestGroupSize(
if (group.trusted_bidding_signals_coordinator) {
size += group.trusted_bidding_signals_coordinator->ToString().length();
}
if (group.view_and_click_counts_providers) {
for (const scoped_refptr<const SecurityOrigin>& provider :
*group.view_and_click_counts_providers) {
size += provider->ToString().length();
}
}
size += group.user_bidding_signals.length();
if (group.ads) {
@ -317,6 +323,18 @@ bool ValidateBlinkInterestGroup(const mojom::blink::InterestGroup& group,
}
}
if (group.view_and_click_counts_providers) {
for (const scoped_refptr<const SecurityOrigin>& provider :
*group.view_and_click_counts_providers) {
if (provider->Protocol() != url::kHttpsScheme) {
error_field_name = "viewAndClickCountsProviders";
error_field_value = provider->ToString();
error = "viewAndClickCountsProviders origin must be HTTPS.";
return false;
}
}
}
if (group.ads) {
std::optional<WTF::wtf_size_t>
selectable_buyer_and_seller_reporting_ids_hard_limit;

@ -179,6 +179,9 @@ class ValidateBlinkInterestGroupTest : public testing::Test {
blink_interest_group->max_trusted_bidding_signals_url_length = 8000;
blink_interest_group->trusted_bidding_signals_coordinator =
kCoordinatorOrigin;
blink_interest_group->view_and_click_counts_providers.emplace();
blink_interest_group->view_and_click_counts_providers->emplace_back(
kOrigin);
blink_interest_group->user_bidding_signals =
String::FromUTF8("\"This field isn't actually validated\"");
@ -1549,4 +1552,32 @@ TEST_F(ValidateBlinkInterestGroupTest,
"trustedBiddingSignalsCoordinator origin must be HTTPS."));
}
TEST_F(ValidateBlinkInterestGroupTest, InvalidViewAndClickCountsProviders) {
mojom::blink::InterestGroupPtr blink_interest_group =
CreateMinimalInterestGroup();
blink_interest_group->view_and_click_counts_providers.emplace();
blink_interest_group->view_and_click_counts_providers->emplace_back(
SecurityOrigin::CreateFromString(
String::FromUTF8("http://origin.test/")));
ExpectInterestGroupIsNotValid(
blink_interest_group,
/*expected_error_field_name=*/
String::FromUTF8("viewAndClickCountsProviders"),
/*expected_error_field_value=*/String::FromUTF8("http://origin.test"),
/*expected_error=*/
String::FromUTF8("viewAndClickCountsProviders origin must be HTTPS."));
blink_interest_group->view_and_click_counts_providers.emplace();
blink_interest_group->view_and_click_counts_providers->emplace_back(
SecurityOrigin::CreateFromString(String::FromUTF8("data:,foo")));
// Data URLs have opaque origins, which are mapped to the string "null".
ExpectInterestGroupIsNotValid(
blink_interest_group,
/*expected_error_field_name=*/
String::FromUTF8("viewAndClickCountsProviders"),
/*expected_error_field_value=*/String::FromUTF8("null"),
/*expected_error=*/
String::FromUTF8("viewAndClickCountsProviders origin must be HTTPS."));
}
} // namespace blink

@ -876,6 +876,22 @@ SharedStorageWorkletGlobalScope::interestGroups(
->trusted_bidding_signals_coordinator->ToString());
}
if (mojom_group->interest_group
->view_and_click_counts_providers) {
Vector<String> view_and_click_counts_providers;
view_and_click_counts_providers.reserve(
mojom_group->interest_group->view_and_click_counts_providers
->size());
for (const scoped_refptr<const blink::SecurityOrigin>& origin :
*mojom_group->interest_group
->view_and_click_counts_providers) {
view_and_click_counts_providers.emplace_back(
origin->ToString());
}
group->setViewAndClickCountsProviders(
std::move(view_and_click_counts_providers));
}
if (mojom_group->interest_group->user_bidding_signals) {
group->setUserBiddingSignals(JsonStringToScriptValue(
resolver->GetScriptState(),

@ -1942,6 +1942,8 @@ TEST_F(SharedStorageWorkletTest, InterestGroups) {
ig.max_trusted_bidding_signals_url_length = 100;
ig.trusted_bidding_signals_coordinator =
url::Origin::Create(GURL("https://example.test"));
ig.view_and_click_counts_providers = {
{url::Origin::Create(GURL("https://example.test"))}};
ig.user_bidding_signals = "\"hello\"";
ig.ads = {
{blink::InterestGroup::Ad(
@ -2193,7 +2195,8 @@ TEST_F(SharedStorageWorkletTest, InterestGroups) {
"trustedBiddingSignalsUrl": "https://example.org/trust.json",
"updateURL": "https://example.org/ig_update.json",
"updateUrl": "https://example.org/ig_update.json",
"userBiddingSignals": "hello"
"userBiddingSignals": "hello",
"viewAndClickCountsProviders": ["https://example.test"]
}
];

@ -2099,6 +2099,10 @@
{
name: "FledgeBiddingAndAuctionServerAPIMultiSeller",
},
{
name: "FledgeClickiness",
status: "test",
},
{
name: "FledgeCustomMaxAuctionAdComponents",
status: "stable",

@ -380,16 +380,20 @@ _CONFIG = [
},
{
'paths': [
'third_party/blink/common/interest_group/interest_group.cc',
'third_party/blink/public/common/interest_group/interest_group.h'
'third_party/blink/common/interest_group/',
'third_party/blink/public/common/interest_group/',
],
'allowed': [
# For hashing of k-anonymity keys
'crypto::SHA256HashString',
# Types used to compute k-anonymity keys.
# Types used to compute k-anonymity keys, also many IG fields are
# origins and URLs.
"url::Origin",
"GURL",
# For checking if origins in interest group are https.
"url::kHttpsScheme",
],
},
{

@ -27,6 +27,7 @@ chromium-metrics-reviews@google.com.
<variant name="BiddingAndAuctionServerKeyProtos"
summary="BiddingAndAuctionServerKeyProtos"/>
<variant name="KAnonKeyProtos" summary="KAnonKeyProtos"/>
<variant name="ListOfOrigins" summary="ListOfOrigins"/>
</variants>
<histogram name="API.EffectiveStorageAccess.AllowedByStorageAccessType"