Reland "Clickiness: Wire response from URLLoader to DB, add e2e tests"
This reverts commitbcc9fcd9e7
. Reason for revert: Fixed ubsan failures The fix diff can be reviewed here: crrev.com/c/6360858/1..2 Original change's description: > Revert "Clickiness: Wire response from URLLoader to DB, add e2e tests" > > This reverts commitc321aa7496
. > > Reason for revert: > LUCI Bisection has identified this change as the cause of a test failure. See the analysis: https://ci.chromium.org/ui/p/chromium/bisection/test-analysis/b/4996809668689920 > > Sample build with failed test: https://ci.chromium.org/b/8720390641933726001 > Affected test(s): > [ninja://chrome/test:unit_tests/CRLSetComponentInstallerTest.ConfiguresOnInstall](https://ci.chromium.org/ui/test/chromium/ninja:%2F%2Fchrome%2Ftest:unit_tests%2FCRLSetComponentInstallerTest.ConfiguresOnInstall?q=VHash%3A799aae891109c818) > [ninja://chrome/test:unit_tests/CRLSetComponentInstallerTest.StillConfiguredAfterNetworkServiceRestartWithCRLSet](https://ci.chromium.org/ui/test/chromium/ninja:%2F%2Fchrome%2Ftest:unit_tests%2FCRLSetComponentInstallerTest.StillConfiguredAfterNetworkServiceRestartWithCRLSet?q=VHash%3A799aae891109c818) > [ninja://chrome/test:unit_tests/DevToolsUIDataSourceWithTaskEnvTest.GotDataCallbackOwnsDevToolsDataSource](https://ci.chromium.org/ui/test/chromium/ninja:%2F%2Fchrome%2Ftest:unit_tests%2FDevToolsUIDataSourceWithTaskEnvTest.GotDataCallbackOwnsDevToolsDataSource?q=VHash%3A799aae891109c818) > [ninja://chrome/test:unit_tests/EmbeddedPolicyTestServerTest.HandleRequest_Success](https://ci.chromium.org/ui/test/chromium/ninja:%2F%2Fchrome%2Ftest:unit_tests%2FEmbeddedPolicyTestServerTest.HandleRequest_Success?q=VHash%3A799aae891109c818) > [ninja://chrome/test:unit_tests/PerformNetworkContextPrefetchRecorderTest.Style](https://ci.chromium.org/ui/test/chromium/ninja:%2F%2Fchrome%2Ftest:unit_tests%2FPerformNetworkContextPrefetchRecorderTest.Style?q=VHash%3A799aae891109c818) > and 7 more ... > > If this is a false positive, please report it at http://b.corp.google.com/createIssue?component=1199205&description=Analysis%3A+https%3A%2F%2Fci.chromium.org%2Fui%2Fp%2Fchromium%2Fbisection%2Ftest-analysis%2Fb%2F4996809668689920&format=PLAIN&priority=P3&title=Wrongly+blamed+https%3A%2F%2Fchromium-review.googlesource.com%2Fc%2Fchromium%2Fsrc%2F%2B%2F6336937&type=BUG > > Original change's description: > > Clickiness: Wire response from URLLoader to DB, add e2e tests > > > > Checking if origins are allowed (allowed by user settings, attested, > > etc.) will come in a subsequent CL. > > > > Doesn't include the redirection logic -- that will land in crrev.com/c/6353624. > > > > Bug: 394108643 > > Change-Id: Ic3897262c17afa7a7cbae967b9f266277ea77a80 > > Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/6336937 > > Auto-Submit: Caleb Raitto <caraitto@chromium.org> > > Commit-Queue: Yuwei Huang <yuweih@chromium.org> > > Reviewed-by: Maks Orlovich <morlovich@chromium.org> > > Reviewed-by: Kenichi Ishibashi <bashi@chromium.org> > > Reviewed-by: James Cook <jamescook@chromium.org> > > Reviewed-by: Yuwei Huang <yuweih@chromium.org> > > Reviewed-by: Austin Sullivan <asully@chromium.org> > > Reviewed-by: Ken Buchanan <kenrb@chromium.org> > > Cr-Commit-Position: refs/heads/main@{#1432935} > > > > Bug: 394108643 > No-Presubmit: true > No-Tree-Checks: true > No-Try: true > Change-Id: Iab03915999352e60653433ae4f460df9effdd323 > Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/6356884 > Commit-Queue: Austin Sullivan <asully@chromium.org> > Owners-Override: Austin Sullivan <asully@chromium.org> > Bot-Commit: Rubber Stamper <rubber-stamper@appspot.gserviceaccount.com> > Reviewed-by: Austin Sullivan <asully@chromium.org> > Cr-Commit-Position: refs/heads/main@{#1433038} Bug: 394108643 Fixed: 403652961 Change-Id: Ic6eb6bee6008e67afe247f28131ff8ce347074ea Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/6360858 Reviewed-by: Yuwei Huang <yuweih@chromium.org> Reviewed-by: Austin Sullivan <asully@chromium.org> Auto-Submit: Caleb Raitto <caraitto@chromium.org> Reviewed-by: James Cook <jamescook@chromium.org> Reviewed-by: Kenichi Ishibashi <bashi@chromium.org> Reviewed-by: Ken Buchanan <kenrb@chromium.org> Reviewed-by: Maks Orlovich <morlovich@chromium.org> Commit-Queue: Caleb Raitto <caraitto@chromium.org> Cr-Commit-Position: refs/heads/main@{#1434209}
This commit is contained in:

committed by
Chromium LUCI CQ

parent
a5d566bc2d
commit
aa907661b5
chrome/browser/ash/bruschetta
content/browser
interest_group
interest_group_browsertest.ccinterest_group_caching_storage.ccinterest_group_caching_storage.hinterest_group_manager_impl.ccinterest_group_manager_impl.h
network_service_client.ccnetwork_service_client.hstorage_partition_impl.ccstorage_partition_impl.hremoting/base
services/network
testing/variations
third_party/blink/renderer/modules/ad_auction
@@ -268,6 +268,9 @@ void BruschettaNetworkContext::OnSharedStorageHeaderReceived(
|
|||||||
std::move(callback).Run();
|
std::move(callback).Run();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void BruschettaNetworkContext::OnAdAuctionEventRecordHeaderReceived(
|
||||||
|
network::AdAuctionEventRecord event_record) {}
|
||||||
|
|
||||||
void BruschettaNetworkContext::Clone(
|
void BruschettaNetworkContext::Clone(
|
||||||
mojo::PendingReceiver<network::mojom::URLLoaderNetworkServiceObserver>
|
mojo::PendingReceiver<network::mojom::URLLoaderNetworkServiceObserver>
|
||||||
observer) {
|
observer) {
|
||||||
|
@@ -92,6 +92,8 @@ class BruschettaNetworkContext
|
|||||||
methods_with_options,
|
methods_with_options,
|
||||||
const std::optional<std::string>& with_lock,
|
const std::optional<std::string>& with_lock,
|
||||||
OnSharedStorageHeaderReceivedCallback callback) override;
|
OnSharedStorageHeaderReceivedCallback callback) override;
|
||||||
|
void OnAdAuctionEventRecordHeaderReceived(
|
||||||
|
network::AdAuctionEventRecord event_record) override;
|
||||||
void Clone(
|
void Clone(
|
||||||
mojo::PendingReceiver<network::mojom::URLLoaderNetworkServiceObserver>
|
mojo::PendingReceiver<network::mojom::URLLoaderNetworkServiceObserver>
|
||||||
listener) override;
|
listener) override;
|
||||||
|
@@ -154,6 +154,12 @@ constexpr char kLegitimateAdAuctionSignals[] =
|
|||||||
// throwing an exception.
|
// throwing an exception.
|
||||||
const char kSuccess[] = "success";
|
const char kSuccess[] = "success";
|
||||||
|
|
||||||
|
// A path for an update registered by `RegisterNoOpUpdate()` that returns an
|
||||||
|
// empty dict, which is a valid update, but doesn't change the interest group.
|
||||||
|
// Can be used with
|
||||||
|
// WaitForInterestGroupsSatisfyingInvalidatingCacheByUpdating().
|
||||||
|
constexpr char kNoOpUpdatePath[] = "/interest_group/no_op_update_path.json";
|
||||||
|
|
||||||
// Returns a string that declares a "maybePromise()" Javascript function, which
|
// Returns a string that declares a "maybePromise()" Javascript function, which
|
||||||
// takes an argument and either returns it (if `use_promise` is false) or
|
// takes an argument and either returns it (if `use_promise` is false) or
|
||||||
// returns a promise that will be resolved with that value in a millisecond (if
|
// returns a promise that will be resolved with that value in a millisecond (if
|
||||||
@@ -458,12 +464,14 @@ class NetworkResponder {
|
|||||||
void RegisterNetworkResponse(const std::string& url_path,
|
void RegisterNetworkResponse(const std::string& url_path,
|
||||||
std::string_view body,
|
std::string_view body,
|
||||||
std::string_view mime_type = "application/json",
|
std::string_view mime_type = "application/json",
|
||||||
ResponseHeaders extra_response_headers = {}) {
|
ResponseHeaders extra_response_headers = {},
|
||||||
|
net::HttpStatusCode code = net::HTTP_OK) {
|
||||||
base::AutoLock auto_lock(response_map_lock_);
|
base::AutoLock auto_lock(response_map_lock_);
|
||||||
Response response;
|
Response response;
|
||||||
response.body = body;
|
response.body = body;
|
||||||
response.mime_type = mime_type;
|
response.mime_type = mime_type;
|
||||||
response.extra_response_headers = std::move(extra_response_headers);
|
response.extra_response_headers = std::move(extra_response_headers);
|
||||||
|
response.code = code;
|
||||||
response_map_[url_path] = std::move(response);
|
response_map_[url_path] = std::move(response);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -621,6 +629,7 @@ function generateBid(
|
|||||||
std::string body;
|
std::string body;
|
||||||
std::string mime_type;
|
std::string mime_type;
|
||||||
ResponseHeaders extra_response_headers;
|
ResponseHeaders extra_response_headers;
|
||||||
|
net::HttpStatusCode code;
|
||||||
};
|
};
|
||||||
|
|
||||||
std::unique_ptr<net::test_server::HttpResponse> RequestHandler(
|
std::unique_ptr<net::test_server::HttpResponse> RequestHandler(
|
||||||
@@ -632,7 +641,7 @@ function generateBid(
|
|||||||
}
|
}
|
||||||
auto response = std::make_unique<net::test_server::BasicHttpResponse>();
|
auto response = std::make_unique<net::test_server::BasicHttpResponse>();
|
||||||
response->AddCustomHeader(kFledgeHeader, "true");
|
response->AddCustomHeader(kFledgeHeader, "true");
|
||||||
response->set_code(net::HTTP_OK);
|
response->set_code(it->second.code);
|
||||||
response->set_content(it->second.body);
|
response->set_content(it->second.body);
|
||||||
response->set_content_type(it->second.mime_type);
|
response->set_content_type(it->second.mime_type);
|
||||||
for (const auto& header : it->second.extra_response_headers) {
|
for (const auto& header : it->second.extra_response_headers) {
|
||||||
@@ -767,7 +776,9 @@ class InterestGroupBrowserTest : public ContentBrowserTest {
|
|||||||
{blink::features::kFledgeDirectFromSellerSignalsWebBundles, {}},
|
{blink::features::kFledgeDirectFromSellerSignalsWebBundles, {}},
|
||||||
{blink::features::kFledgeTrustedSignalsKVv2Support, {}},
|
{blink::features::kFledgeTrustedSignalsKVv2Support, {}},
|
||||||
{blink::features::kFledgeTrustedSignalsKVv1CreativeScanning, {}},
|
{blink::features::kFledgeTrustedSignalsKVv1CreativeScanning, {}},
|
||||||
{features::kFledgeTextConversionHelpers, {}}},
|
{features::kFledgeTextConversionHelpers, {}},
|
||||||
|
{network::features::kAdAuctionEventRegistration, {}},
|
||||||
|
{blink::features::kFledgeClickiness, {}}},
|
||||||
/*disabled_features=*/
|
/*disabled_features=*/
|
||||||
{blink::features::kFencedFrames,
|
{blink::features::kFencedFrames,
|
||||||
blink::features::kFledgeEnforceKAnonymity,
|
blink::features::kFledgeEnforceKAnonymity,
|
||||||
@@ -969,6 +980,14 @@ class InterestGroupBrowserTest : public ContentBrowserTest {
|
|||||||
dict.Set("trustedBiddingSignalsCoordinator",
|
dict.Set("trustedBiddingSignalsCoordinator",
|
||||||
group.trusted_bidding_signals_coordinator->Serialize());
|
group.trusted_bidding_signals_coordinator->Serialize());
|
||||||
}
|
}
|
||||||
|
if (group.view_and_click_counts_providers) {
|
||||||
|
base::Value::List providers;
|
||||||
|
for (const url::Origin& provider :
|
||||||
|
*group.view_and_click_counts_providers) {
|
||||||
|
providers.Append(provider.Serialize());
|
||||||
|
}
|
||||||
|
dict.Set("viewAndClickCountsProviders", std::move(providers));
|
||||||
|
}
|
||||||
if (group.user_bidding_signals) {
|
if (group.user_bidding_signals) {
|
||||||
dict.Set("userBiddingSignals", JsonToValue(*group.user_bidding_signals));
|
dict.Set("userBiddingSignals", JsonToValue(*group.user_bidding_signals));
|
||||||
}
|
}
|
||||||
@@ -1517,6 +1536,41 @@ function provideAdditionalBids(seller, nonce, bidStringList,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Waits until the `condition` callback over the interest groups returns
|
||||||
|
// true, running an update on the owner's groups in order to force interest
|
||||||
|
// group cache invalidation.
|
||||||
|
//
|
||||||
|
// This can be useful for loading things like clickiness data, whose updates
|
||||||
|
// intentionally don't invalidate cache.
|
||||||
|
//
|
||||||
|
// **REQUIREMENT**: At least one interest group owned by `owner` must have a
|
||||||
|
// valid updateURL and response registered, but the update response can be an
|
||||||
|
// empty dict. RegisterNoOpUpdate() and kNoOpUpdatePath can be used for this.
|
||||||
|
//
|
||||||
|
// Also, the current top-level page origin should be the same as `owner`.
|
||||||
|
void WaitForInterestGroupsSatisfyingInvalidatingCacheByUpdating(
|
||||||
|
const url::Origin& owner,
|
||||||
|
base::RepeatingCallback<bool(scoped_refptr<StorageInterestGroups>)>
|
||||||
|
condition) {
|
||||||
|
ASSERT_EQ(owner, shell()
|
||||||
|
->web_contents()
|
||||||
|
->GetPrimaryMainFrame()
|
||||||
|
->GetLastCommittedOrigin());
|
||||||
|
while (true) {
|
||||||
|
EXPECT_EQ("done", UpdateInterestGroupsInJS());
|
||||||
|
if (condition.Run(GetInterestGroupsForOwner(owner))) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Registers an update at kNoOpUpdatePath that returns an empty dict, which
|
||||||
|
// is a valid update, but doesn't change the interest group. Can be used with
|
||||||
|
// WaitForInterestGroupsSatisfyingInvalidatingCacheByUpdating().
|
||||||
|
void RegisterNoOpUpdate() {
|
||||||
|
network_responder_->RegisterNetworkResponse(kNoOpUpdatePath, "{}");
|
||||||
|
}
|
||||||
|
|
||||||
// Waits for `url` to be requested by `embedded_https_test_server()`, or any
|
// Waits for `url` to be requested by `embedded_https_test_server()`, or any
|
||||||
// other server that OnHttpsTestServerRequestMonitor() has been configured to
|
// other server that OnHttpsTestServerRequestMonitor() has been configured to
|
||||||
// monitor. `url`'s hostname is replaced with "127.0.0.1", since the embedded
|
// monitor. `url`'s hostname is replaced with "127.0.0.1", since the embedded
|
||||||
@@ -7891,6 +7945,219 @@ IN_PROC_BROWSER_TEST_F(InterestGroupBrowserTest,
|
|||||||
EXPECT_TRUE(console_observer.Wait());
|
EXPECT_TRUE(console_observer.Wait());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
IN_PROC_BROWSER_TEST_F(InterestGroupBrowserTest, Clickiness_CaptureView) {
|
||||||
|
constexpr char kRecordViewClickPath[] =
|
||||||
|
"/interest_group/record_view_click_event.html";
|
||||||
|
|
||||||
|
GURL test_url_a = embedded_https_test_server().GetURL(
|
||||||
|
"a.test", "/attribution_reporting/page_with_impression_creator.html");
|
||||||
|
url::Origin test_origin_a = url::Origin::Create(test_url_a);
|
||||||
|
ASSERT_TRUE(test_url_a.SchemeIs(url::kHttpsScheme));
|
||||||
|
ASSERT_TRUE(NavigateToURL(shell(), test_url_a));
|
||||||
|
|
||||||
|
const std::string record_event_response = base::StringPrintf(
|
||||||
|
"type=\"view\", eligible-origins=(\"%s\")", test_origin_a.Serialize());
|
||||||
|
|
||||||
|
network_responder_->RegisterNetworkResponse(
|
||||||
|
kRecordViewClickPath, "Throwaway response", "image/jpeg",
|
||||||
|
/*extra_response_headers=*/
|
||||||
|
{{"Ad-Auction-Record-Event", record_event_response}});
|
||||||
|
|
||||||
|
GURL record_event_url =
|
||||||
|
embedded_https_test_server().GetURL("c.test", kRecordViewClickPath);
|
||||||
|
|
||||||
|
EXPECT_TRUE(ExecJs(web_contents(), JsReplace("createAttributionSrcImg($1);",
|
||||||
|
record_event_url)));
|
||||||
|
|
||||||
|
// This join should succeed. Register a no-op update URL to use
|
||||||
|
// WaitForInterestGroupsSatisfyingInvalidatingCacheByUpdating().
|
||||||
|
RegisterNoOpUpdate();
|
||||||
|
EXPECT_EQ(kSuccess, JoinInterestGroupAndVerify(
|
||||||
|
blink::TestInterestGroupBuilder(test_origin_a, "cars")
|
||||||
|
.SetViewAndClickCountsProviders(
|
||||||
|
{{url::Origin::Create(record_event_url)}})
|
||||||
|
.SetUpdateUrl(embedded_https_test_server().GetURL(
|
||||||
|
"a.test", kNoOpUpdatePath))
|
||||||
|
.Build()));
|
||||||
|
|
||||||
|
WaitForInterestGroupsSatisfyingInvalidatingCacheByUpdating(
|
||||||
|
test_origin_a,
|
||||||
|
base::BindLambdaForTesting(
|
||||||
|
[](scoped_refptr<StorageInterestGroups> groups) {
|
||||||
|
EXPECT_EQ(groups->size(), 1u);
|
||||||
|
const StorageInterestGroup& group = *groups->GetInterestGroups()[0];
|
||||||
|
const blink::mojom::ViewAndClickCountsPtr& view_and_click_counts =
|
||||||
|
group.bidding_browser_signals->view_and_click_counts;
|
||||||
|
EXPECT_EQ(group.interest_group.name, "cars");
|
||||||
|
return view_and_click_counts->view_counts->past_hour == 1 &&
|
||||||
|
view_and_click_counts->click_counts->past_hour == 0;
|
||||||
|
}));
|
||||||
|
|
||||||
|
// TODO(crbug.com/394108643): Also check generateBid() once the plumbing is
|
||||||
|
// hooked up.
|
||||||
|
}
|
||||||
|
|
||||||
|
IN_PROC_BROWSER_TEST_F(InterestGroupBrowserTest, Clickiness_CaptureClick) {
|
||||||
|
constexpr char kRecordViewClickPath[] =
|
||||||
|
"/interest_group/record_view_click_event.html";
|
||||||
|
|
||||||
|
GURL test_url_a = embedded_https_test_server().GetURL(
|
||||||
|
"a.test", "/attribution_reporting/page_with_impression_creator.html");
|
||||||
|
url::Origin test_origin_a = url::Origin::Create(test_url_a);
|
||||||
|
ASSERT_TRUE(test_url_a.SchemeIs(url::kHttpsScheme));
|
||||||
|
ASSERT_TRUE(NavigateToURL(shell(), test_url_a));
|
||||||
|
|
||||||
|
const std::string record_event_response = base::StringPrintf(
|
||||||
|
"type=\"click\", eligible-origins=(\"%s\")", test_origin_a.Serialize());
|
||||||
|
|
||||||
|
network_responder_->RegisterNetworkResponse(
|
||||||
|
kRecordViewClickPath, "Throwaway response", "image/jpeg",
|
||||||
|
/*extra_response_headers=*/
|
||||||
|
{{"Ad-Auction-Record-Event", record_event_response}});
|
||||||
|
|
||||||
|
GURL record_event_url =
|
||||||
|
embedded_https_test_server().GetURL("c.test", kRecordViewClickPath);
|
||||||
|
|
||||||
|
EXPECT_TRUE(
|
||||||
|
ExecJs(web_contents(),
|
||||||
|
JsReplace(R"(
|
||||||
|
createAttributionSrcAnchor({id: 'link',
|
||||||
|
url: $1,
|
||||||
|
attributionsrc: $2,
|
||||||
|
target: $3});)",
|
||||||
|
embedded_https_test_server().GetURL("a.test", "/echo"),
|
||||||
|
record_event_url, "_top")));
|
||||||
|
TestNavigationObserver observer(web_contents());
|
||||||
|
EXPECT_TRUE(ExecJs(web_contents(), "simulateClick('link');"));
|
||||||
|
observer.Wait();
|
||||||
|
|
||||||
|
// This join should succeed. Register a no-op update URL to use
|
||||||
|
// WaitForInterestGroupsSatisfyingInvalidatingCacheByUpdating().
|
||||||
|
RegisterNoOpUpdate();
|
||||||
|
EXPECT_EQ(kSuccess, JoinInterestGroupAndVerify(
|
||||||
|
blink::TestInterestGroupBuilder(test_origin_a, "cars")
|
||||||
|
.SetViewAndClickCountsProviders(
|
||||||
|
{{url::Origin::Create(record_event_url)}})
|
||||||
|
.SetUpdateUrl(embedded_https_test_server().GetURL(
|
||||||
|
"a.test", kNoOpUpdatePath))
|
||||||
|
.Build()));
|
||||||
|
|
||||||
|
WaitForInterestGroupsSatisfyingInvalidatingCacheByUpdating(
|
||||||
|
test_origin_a,
|
||||||
|
base::BindLambdaForTesting(
|
||||||
|
[](scoped_refptr<StorageInterestGroups> groups) {
|
||||||
|
EXPECT_EQ(groups->size(), 1u);
|
||||||
|
const StorageInterestGroup& group = *groups->GetInterestGroups()[0];
|
||||||
|
const blink::mojom::ViewAndClickCountsPtr& view_and_click_counts =
|
||||||
|
group.bidding_browser_signals->view_and_click_counts;
|
||||||
|
EXPECT_EQ(group.interest_group.name, "cars");
|
||||||
|
return view_and_click_counts->view_counts->past_hour == 0 &&
|
||||||
|
view_and_click_counts->click_counts->past_hour == 1;
|
||||||
|
}));
|
||||||
|
|
||||||
|
// TODO(crbug.com/394108643): Also check generateBid() once the plumbing is
|
||||||
|
// hooked up.
|
||||||
|
}
|
||||||
|
|
||||||
|
IN_PROC_BROWSER_TEST_F(InterestGroupBrowserTest, Clickiness_NotStructuredDict) {
|
||||||
|
constexpr char kRecordViewClickPath[] =
|
||||||
|
"/interest_group/record_view_click_event.html";
|
||||||
|
|
||||||
|
GURL test_url_a = embedded_https_test_server().GetURL(
|
||||||
|
"a.test", "/attribution_reporting/page_with_impression_creator.html");
|
||||||
|
url::Origin test_origin_a = url::Origin::Create(test_url_a);
|
||||||
|
ASSERT_TRUE(test_url_a.SchemeIs(url::kHttpsScheme));
|
||||||
|
ASSERT_TRUE(NavigateToURL(shell(), test_url_a));
|
||||||
|
|
||||||
|
// Double equals isn't valid in structured headers -- this message should be
|
||||||
|
// rejected.
|
||||||
|
const std::string record_event_response = base::StringPrintf(
|
||||||
|
"type==\"view\", eligible-origins=(\"%s\")", test_origin_a.Serialize());
|
||||||
|
|
||||||
|
network_responder_->RegisterNetworkResponse(
|
||||||
|
kRecordViewClickPath, "Throwaway response", "image/jpeg",
|
||||||
|
/*extra_response_headers=*/
|
||||||
|
{{"Ad-Auction-Record-Event", record_event_response}});
|
||||||
|
|
||||||
|
GURL record_event_url =
|
||||||
|
embedded_https_test_server().GetURL("c.test", kRecordViewClickPath);
|
||||||
|
|
||||||
|
EXPECT_TRUE(ExecJs(web_contents(), JsReplace("createAttributionSrcImg($1);",
|
||||||
|
record_event_url)));
|
||||||
|
|
||||||
|
// This join should succeed.
|
||||||
|
base::RunLoop().RunUntilIdle();
|
||||||
|
EXPECT_EQ(kSuccess, JoinInterestGroupAndVerify(
|
||||||
|
blink::TestInterestGroupBuilder(test_origin_a, "cars")
|
||||||
|
.SetViewAndClickCountsProviders(
|
||||||
|
{{url::Origin::Create(record_event_url)}})
|
||||||
|
.Build()));
|
||||||
|
|
||||||
|
// No change to view or click counts. Note that this is somewhat racy, as if
|
||||||
|
// the product code erroneously records a view or a click, it could happen
|
||||||
|
// after the join -- in that case, failures may be flaky. However, correct
|
||||||
|
// product code shouldn't result in flakes.
|
||||||
|
scoped_refptr<StorageInterestGroups> groups =
|
||||||
|
GetInterestGroupsForOwner(test_origin_a);
|
||||||
|
ASSERT_EQ(groups->size(), 1u);
|
||||||
|
const StorageInterestGroup& group = *groups->GetInterestGroups()[0];
|
||||||
|
const blink::mojom::ViewAndClickCountsPtr& view_and_click_counts =
|
||||||
|
group.bidding_browser_signals->view_and_click_counts;
|
||||||
|
EXPECT_EQ(group.interest_group.name, "cars");
|
||||||
|
EXPECT_EQ(view_and_click_counts->view_counts->past_hour, 0);
|
||||||
|
EXPECT_EQ(view_and_click_counts->click_counts->past_hour, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
IN_PROC_BROWSER_TEST_F(InterestGroupBrowserTest, Clickiness_InvalidType) {
|
||||||
|
constexpr char kRecordViewClickPath[] =
|
||||||
|
"/interest_group/record_view_click_event.html";
|
||||||
|
|
||||||
|
GURL test_url_a = embedded_https_test_server().GetURL(
|
||||||
|
"a.test", "/attribution_reporting/page_with_impression_creator.html");
|
||||||
|
url::Origin test_origin_a = url::Origin::Create(test_url_a);
|
||||||
|
ASSERT_TRUE(test_url_a.SchemeIs(url::kHttpsScheme));
|
||||||
|
ASSERT_TRUE(NavigateToURL(shell(), test_url_a));
|
||||||
|
|
||||||
|
// A valid structured dictionary, but not-view-or-click isn't a valid type --
|
||||||
|
// this message should be rejected.
|
||||||
|
const std::string record_event_response = base::StringPrintf(
|
||||||
|
"type=\"not-view-or-click\", eligible-origins=(\"%s\")",
|
||||||
|
test_origin_a.Serialize());
|
||||||
|
|
||||||
|
network_responder_->RegisterNetworkResponse(
|
||||||
|
kRecordViewClickPath, "Throwaway response", "image/jpeg",
|
||||||
|
/*extra_response_headers=*/
|
||||||
|
{{"Ad-Auction-Record-Event", record_event_response}});
|
||||||
|
|
||||||
|
GURL record_event_url =
|
||||||
|
embedded_https_test_server().GetURL("c.test", kRecordViewClickPath);
|
||||||
|
|
||||||
|
EXPECT_TRUE(ExecJs(web_contents(), JsReplace("createAttributionSrcImg($1);",
|
||||||
|
record_event_url)));
|
||||||
|
|
||||||
|
// This join should succeed.
|
||||||
|
base::RunLoop().RunUntilIdle();
|
||||||
|
EXPECT_EQ(kSuccess, JoinInterestGroupAndVerify(
|
||||||
|
blink::TestInterestGroupBuilder(test_origin_a, "cars")
|
||||||
|
.SetViewAndClickCountsProviders(
|
||||||
|
{{url::Origin::Create(record_event_url)}})
|
||||||
|
.Build()));
|
||||||
|
|
||||||
|
// No change to view or click counts. Note that this is somewhat racy, as if
|
||||||
|
// the product code erroneously records a view or a click, it could happen
|
||||||
|
// after the join -- in that case, failures may be flaky. However, correct
|
||||||
|
// product code shouldn't result in flakes.
|
||||||
|
scoped_refptr<StorageInterestGroups> groups =
|
||||||
|
GetInterestGroupsForOwner(test_origin_a);
|
||||||
|
ASSERT_EQ(groups->size(), 1u);
|
||||||
|
const StorageInterestGroup& group = *groups->GetInterestGroups()[0];
|
||||||
|
const blink::mojom::ViewAndClickCountsPtr& view_and_click_counts =
|
||||||
|
group.bidding_browser_signals->view_and_click_counts;
|
||||||
|
EXPECT_EQ(group.interest_group.name, "cars");
|
||||||
|
EXPECT_EQ(view_and_click_counts->view_counts->past_hour, 0);
|
||||||
|
EXPECT_EQ(view_and_click_counts->click_counts->past_hour, 0);
|
||||||
|
}
|
||||||
|
|
||||||
IN_PROC_BROWSER_TEST_F(InterestGroupBrowserTest,
|
IN_PROC_BROWSER_TEST_F(InterestGroupBrowserTest,
|
||||||
RunAdAuctionBuyersNoInterestGroup) {
|
RunAdAuctionBuyersNoInterestGroup) {
|
||||||
GURL test_url = embedded_https_test_server().GetURL("a.test", "/echo");
|
GURL test_url = embedded_https_test_server().GetURL("a.test", "/echo");
|
||||||
|
@@ -363,6 +363,23 @@ void InterestGroupCachingStorage::RecordDebugReportCooldown(
|
|||||||
.WithArgs(origin, cooldown_start, cooldown_type);
|
.WithArgs(origin, cooldown_start, cooldown_type);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void InterestGroupCachingStorage::RecordViewClick(
|
||||||
|
network::AdAuctionEventRecord event_record) {
|
||||||
|
// Cached interest groups containing stale view / click counts are
|
||||||
|
// intentionally not evicted -- views especially occur frequently, and would
|
||||||
|
// result in many evictions, limiting the usefulness of this cache. So, for
|
||||||
|
// performance, it is better to return view / click data that's slightly
|
||||||
|
// stale.
|
||||||
|
//
|
||||||
|
// TODO(crbug.com/394108643): Cap the time duration of this staleness with a
|
||||||
|
// new timer that evicts groups loaded more than say 120 seconds ago. Without
|
||||||
|
// this, in the rare case that auctions that each load a given IG are running
|
||||||
|
// constantly, back-to-back, the view click data for that IG could become
|
||||||
|
// arbitrarily stale.
|
||||||
|
interest_group_storage_.AsyncCall(&InterestGroupStorage::RecordViewClick)
|
||||||
|
.WithArgs(std::move(event_record));
|
||||||
|
}
|
||||||
|
|
||||||
void InterestGroupCachingStorage::UpdateKAnonymity(
|
void InterestGroupCachingStorage::UpdateKAnonymity(
|
||||||
const blink::InterestGroupKey& interest_group_key,
|
const blink::InterestGroupKey& interest_group_key,
|
||||||
const std::vector<std::string>& positive_hashed_keys,
|
const std::vector<std::string>& positive_hashed_keys,
|
||||||
|
@@ -225,6 +225,9 @@ class CONTENT_EXPORT InterestGroupCachingStorage {
|
|||||||
void RecordDebugReportCooldown(const url::Origin& origin,
|
void RecordDebugReportCooldown(const url::Origin& origin,
|
||||||
base::Time cooldown_start,
|
base::Time cooldown_start,
|
||||||
DebugReportCooldownType cooldown_type);
|
DebugReportCooldownType cooldown_type);
|
||||||
|
// Records a view or a click event. Aggregate time bucketed view and click
|
||||||
|
// information is provided to bidder's browsing signals in generateBid().
|
||||||
|
void RecordViewClick(network::AdAuctionEventRecord event_record);
|
||||||
// Records a K-anonymity update for an interest group. If
|
// Records a K-anonymity update for an interest group. If
|
||||||
// `replace_existing_values` is true, this update will store the new
|
// `replace_existing_values` is true, this update will store the new
|
||||||
// `update_time` and `positive_hashed_values`, replacing the interest
|
// `update_time` and `positive_hashed_values`, replacing the interest
|
||||||
|
@@ -524,6 +524,19 @@ void InterestGroupManagerImpl::RecordDebugReportCooldown(
|
|||||||
cooldown_type);
|
cooldown_type);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void InterestGroupManagerImpl::RecordViewClick(
|
||||||
|
network::AdAuctionEventRecord event_record) {
|
||||||
|
// TODO(crbug.com/394108643): Check against
|
||||||
|
// ContentBrowserClient::IsInterestGroupAPIAllowed(). This will require
|
||||||
|
// getting an RFH; we could use something like
|
||||||
|
// url_loader_network_observers_.current_context().navigation_or_document()
|
||||||
|
// in the StoragePartitionImpl, but the issue is that for clicks, we'll
|
||||||
|
// probably get a navigation handle instead of an RFH? Perhaps we can
|
||||||
|
// override WebContentsObserver::DidFinishNavigation() to wait until the new
|
||||||
|
// RFH is ready?
|
||||||
|
caching_storage_.RecordViewClick(std::move(event_record));
|
||||||
|
}
|
||||||
|
|
||||||
void InterestGroupManagerImpl::RegisterAdKeysAsJoined(
|
void InterestGroupManagerImpl::RegisterAdKeysAsJoined(
|
||||||
base::flat_set<std::string> hashed_keys) {
|
base::flat_set<std::string> hashed_keys) {
|
||||||
k_anonymity_manager_->RegisterAdKeysAsJoined(std::move(hashed_keys));
|
k_anonymity_manager_->RegisterAdKeysAsJoined(std::move(hashed_keys));
|
||||||
|
@@ -280,6 +280,10 @@ class CONTENT_EXPORT InterestGroupManagerImpl : public InterestGroupManager {
|
|||||||
base::Time cooldown_start,
|
base::Time cooldown_start,
|
||||||
DebugReportCooldownType cooldown_type);
|
DebugReportCooldownType cooldown_type);
|
||||||
|
|
||||||
|
// Records a view or a click event. Aggregate time bucketed view and click
|
||||||
|
// information is provided to bidder's browsing signals in generateBid().
|
||||||
|
void RecordViewClick(network::AdAuctionEventRecord event_record);
|
||||||
|
|
||||||
// Reports the ad keys to the k-anonymity service. Should be called when
|
// Reports the ad keys to the k-anonymity service. Should be called when
|
||||||
// FLEDGE selects an ad.
|
// FLEDGE selects an ad.
|
||||||
void RegisterAdKeysAsJoined(base::flat_set<std::string> hashed_keys);
|
void RegisterAdKeysAsJoined(base::flat_set<std::string> hashed_keys);
|
||||||
|
@@ -342,6 +342,9 @@ void NetworkServiceClient::OnSharedStorageHeaderReceived(
|
|||||||
std::move(callback).Run();
|
std::move(callback).Run();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void NetworkServiceClient::OnAdAuctionEventRecordHeaderReceived(
|
||||||
|
network::AdAuctionEventRecord event_record) {}
|
||||||
|
|
||||||
void NetworkServiceClient::Clone(
|
void NetworkServiceClient::Clone(
|
||||||
mojo::PendingReceiver<network::mojom::URLLoaderNetworkServiceObserver>
|
mojo::PendingReceiver<network::mojom::URLLoaderNetworkServiceObserver>
|
||||||
observer) {
|
observer) {
|
||||||
|
@@ -132,6 +132,8 @@ class NetworkServiceClient
|
|||||||
methods_with_options,
|
methods_with_options,
|
||||||
const std::optional<std::string>& with_lock,
|
const std::optional<std::string>& with_lock,
|
||||||
OnSharedStorageHeaderReceivedCallback callback) override;
|
OnSharedStorageHeaderReceivedCallback callback) override;
|
||||||
|
void OnAdAuctionEventRecordHeaderReceived(
|
||||||
|
network::AdAuctionEventRecord event_record) override;
|
||||||
void Clone(
|
void Clone(
|
||||||
mojo::PendingReceiver<network::mojom::URLLoaderNetworkServiceObserver>
|
mojo::PendingReceiver<network::mojom::URLLoaderNetworkServiceObserver>
|
||||||
listener) override;
|
listener) override;
|
||||||
|
@@ -2370,6 +2370,11 @@ void StoragePartitionImpl::OnSharedStorageHeaderReceived(
|
|||||||
std::move(callback), mojo::GetBadMessageCallback(), /*can_defer=*/true);
|
std::move(callback), mojo::GetBadMessageCallback(), /*can_defer=*/true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void StoragePartitionImpl::OnAdAuctionEventRecordHeaderReceived(
|
||||||
|
network::AdAuctionEventRecord event_record) {
|
||||||
|
interest_group_manager_->RecordViewClick(std::move(event_record));
|
||||||
|
}
|
||||||
|
|
||||||
void StoragePartitionImpl::Clone(
|
void StoragePartitionImpl::Clone(
|
||||||
mojo::PendingReceiver<network::mojom::URLLoaderNetworkServiceObserver>
|
mojo::PendingReceiver<network::mojom::URLLoaderNetworkServiceObserver>
|
||||||
observer) {
|
observer) {
|
||||||
|
@@ -398,6 +398,8 @@ class CONTENT_EXPORT StoragePartitionImpl
|
|||||||
methods_with_options,
|
methods_with_options,
|
||||||
const std::optional<std::string>& with_lock,
|
const std::optional<std::string>& with_lock,
|
||||||
OnSharedStorageHeaderReceivedCallback callback) override;
|
OnSharedStorageHeaderReceivedCallback callback) override;
|
||||||
|
void OnAdAuctionEventRecordHeaderReceived(
|
||||||
|
network::AdAuctionEventRecord event_record) override;
|
||||||
|
|
||||||
SharedStorageHeaderObserver* shared_storage_header_observer() {
|
SharedStorageHeaderObserver* shared_storage_header_observer() {
|
||||||
return shared_storage_header_observer_.get();
|
return shared_storage_header_observer_.get();
|
||||||
|
@@ -157,6 +157,9 @@ void UrlLoaderNetworkServiceObserver::OnSharedStorageHeaderReceived(
|
|||||||
std::move(callback).Run();
|
std::move(callback).Run();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void UrlLoaderNetworkServiceObserver::OnAdAuctionEventRecordHeaderReceived(
|
||||||
|
network::AdAuctionEventRecord event_record) {}
|
||||||
|
|
||||||
void UrlLoaderNetworkServiceObserver::Clone(
|
void UrlLoaderNetworkServiceObserver::Clone(
|
||||||
mojo::PendingReceiver<network::mojom::URLLoaderNetworkServiceObserver>
|
mojo::PendingReceiver<network::mojom::URLLoaderNetworkServiceObserver>
|
||||||
observer) {
|
observer) {
|
||||||
|
@@ -81,6 +81,8 @@ class UrlLoaderNetworkServiceObserver
|
|||||||
methods_with_options,
|
methods_with_options,
|
||||||
const std::optional<std::string>& with_lock,
|
const std::optional<std::string>& with_lock,
|
||||||
OnSharedStorageHeaderReceivedCallback callback) override;
|
OnSharedStorageHeaderReceivedCallback callback) override;
|
||||||
|
void OnAdAuctionEventRecordHeaderReceived(
|
||||||
|
network::AdAuctionEventRecord event_record) override;
|
||||||
void Clone(
|
void Clone(
|
||||||
mojo::PendingReceiver<network::mojom::URLLoaderNetworkServiceObserver>
|
mojo::PendingReceiver<network::mojom::URLLoaderNetworkServiceObserver>
|
||||||
listener) override;
|
listener) override;
|
||||||
|
@@ -332,6 +332,7 @@ component("network_service") {
|
|||||||
"//mojo/public/cpp/system",
|
"//mojo/public/cpp/system",
|
||||||
"//net",
|
"//net",
|
||||||
"//net:extras",
|
"//net:extras",
|
||||||
|
"//services/network/ad_auction",
|
||||||
"//services/network/attribution",
|
"//services/network/attribution",
|
||||||
"//services/network/first_party_sets:first_party_sets_manager",
|
"//services/network/first_party_sets:first_party_sets_manager",
|
||||||
"//services/network/public/cpp",
|
"//services/network/public/cpp",
|
||||||
|
26
services/network/ad_auction/BUILD.gn
Normal file
26
services/network/ad_auction/BUILD.gn
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
# Copyright 2025 The Chromium Authors
|
||||||
|
# Use of this source code is governed by a BSD-style license that can be
|
||||||
|
# found in the LICENSE file.
|
||||||
|
|
||||||
|
source_set("ad_auction") {
|
||||||
|
visibility = [
|
||||||
|
":*",
|
||||||
|
"//services/network:network_service",
|
||||||
|
"//services/network:test_support",
|
||||||
|
"//services/network:tests",
|
||||||
|
]
|
||||||
|
configs += [ "//build/config/compiler:wexit_time_destructors" ]
|
||||||
|
defines = [ "IS_NETWORK_SERVICE_IMPL" ]
|
||||||
|
sources = [
|
||||||
|
"event_record_request_helper.cc",
|
||||||
|
"event_record_request_helper.h",
|
||||||
|
]
|
||||||
|
deps = [
|
||||||
|
"//base",
|
||||||
|
"//net",
|
||||||
|
"//services/network/public/cpp",
|
||||||
|
"//services/network/public/cpp:ad_auction_support",
|
||||||
|
"//services/network/public/mojom",
|
||||||
|
"//url",
|
||||||
|
]
|
||||||
|
}
|
75
services/network/ad_auction/event_record_request_helper.cc
Normal file
75
services/network/ad_auction/event_record_request_helper.cc
Normal file
@@ -0,0 +1,75 @@
|
|||||||
|
// Copyright 2025 The Chromium Authors
|
||||||
|
// Use of this source code is governed by a BSD-style license that can be
|
||||||
|
// found in the LICENSE file.
|
||||||
|
|
||||||
|
#include "services/network/ad_auction/event_record_request_helper.h"
|
||||||
|
|
||||||
|
#include <optional>
|
||||||
|
#include <string>
|
||||||
|
#include <string_view>
|
||||||
|
#include <utility>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include "base/feature_list.h"
|
||||||
|
#include "base/notreached.h"
|
||||||
|
#include "net/http/structured_headers.h"
|
||||||
|
#include "net/url_request/url_request.h"
|
||||||
|
#include "services/network/public/cpp/ad_auction/event_record.h"
|
||||||
|
#include "services/network/public/cpp/features.h"
|
||||||
|
#include "services/network/public/mojom/ad_auction.mojom.h"
|
||||||
|
#include "services/network/public/mojom/attribution.mojom.h"
|
||||||
|
#include "services/network/public/mojom/url_loader_network_service_observer.mojom.h"
|
||||||
|
#include "url/gurl.h"
|
||||||
|
#include "url/origin.h"
|
||||||
|
|
||||||
|
namespace network {
|
||||||
|
|
||||||
|
void AdAuctionEventRecordRequestHelper::HandleResponse(
|
||||||
|
const net::URLRequest& request) {
|
||||||
|
if (!base::FeatureList::IsEnabled(features::kAdAuctionEventRegistration) ||
|
||||||
|
!url_loader_network_observer_) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
AdAuctionEventRecord::Type expected_type;
|
||||||
|
switch (attribution_reporting_eligibility_) {
|
||||||
|
case mojom::AttributionReportingEligibility::kEventSource:
|
||||||
|
case mojom::AttributionReportingEligibility::kEventSourceOrTrigger:
|
||||||
|
expected_type = AdAuctionEventRecord::Type::kView;
|
||||||
|
break;
|
||||||
|
case mojom::AttributionReportingEligibility::kNavigationSource:
|
||||||
|
expected_type = AdAuctionEventRecord::Type::kClick;
|
||||||
|
break;
|
||||||
|
case mojom::AttributionReportingEligibility::kUnset:
|
||||||
|
case mojom::AttributionReportingEligibility::kEmpty:
|
||||||
|
case mojom::AttributionReportingEligibility::kTrigger:
|
||||||
|
// Nothing to do for these requests, they're not eligible for click or
|
||||||
|
// view events.
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::optional<std::string> ad_auction_record_event_header =
|
||||||
|
AdAuctionEventRecord::GetAdAuctionRecordEventHeader(
|
||||||
|
request.response_headers());
|
||||||
|
if (!ad_auction_record_event_header) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::optional<net::structured_headers::Dictionary> dict =
|
||||||
|
net::structured_headers::ParseDictionary(*ad_auction_record_event_header);
|
||||||
|
if (!dict) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::optional<AdAuctionEventRecord> maybe_parsed =
|
||||||
|
AdAuctionEventRecord::MaybeCreateFromStructuredDict(
|
||||||
|
*dict, /*expected_type=*/expected_type,
|
||||||
|
/*providing_origin=*/url::Origin::Create(request.url()));
|
||||||
|
if (!maybe_parsed) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
url_loader_network_observer_->OnAdAuctionEventRecordHeaderReceived(
|
||||||
|
std::move(*maybe_parsed));
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace network
|
48
services/network/ad_auction/event_record_request_helper.h
Normal file
48
services/network/ad_auction/event_record_request_helper.h
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
// Copyright 2025 The Chromium Authors
|
||||||
|
// Use of this source code is governed by a BSD-style license that can be
|
||||||
|
// found in the LICENSE file.
|
||||||
|
|
||||||
|
#ifndef SERVICES_NETWORK_AD_AUCTION_EVENT_RECORD_REQUEST_HELPER_H_
|
||||||
|
#define SERVICES_NETWORK_AD_AUCTION_EVENT_RECORD_REQUEST_HELPER_H_
|
||||||
|
|
||||||
|
#include <string_view>
|
||||||
|
|
||||||
|
#include "base/memory/raw_ptr.h"
|
||||||
|
#include "net/url_request/url_request.h"
|
||||||
|
#include "services/network/public/mojom/ad_auction.mojom-forward.h"
|
||||||
|
#include "services/network/public/mojom/attribution.mojom.h"
|
||||||
|
#include "services/network/public/mojom/url_loader_network_service_observer.mojom-forward.h"
|
||||||
|
|
||||||
|
namespace network {
|
||||||
|
|
||||||
|
// When constructed for requests with eligible values of
|
||||||
|
// `attribution_reporting_eligibility`, parses Ad-Auction-Record-Event response
|
||||||
|
// headers headers and sends the result, if successful, to
|
||||||
|
// `OnAdAuctionEventRecordHeaderReceived()` on `url_loader_network_observer_`.
|
||||||
|
class AdAuctionEventRecordRequestHelper {
|
||||||
|
public:
|
||||||
|
// `url_loader_network_observer` may be null -- if non-null, it must outlive
|
||||||
|
// this instance.
|
||||||
|
AdAuctionEventRecordRequestHelper(
|
||||||
|
mojom::AttributionReportingEligibility attribution_reporting_eligibility,
|
||||||
|
mojom::URLLoaderNetworkServiceObserver* url_loader_network_observer)
|
||||||
|
: attribution_reporting_eligibility_(attribution_reporting_eligibility),
|
||||||
|
url_loader_network_observer_(url_loader_network_observer) {}
|
||||||
|
|
||||||
|
// If `attribution_reporting_eligibility_` is one of the eligible values,
|
||||||
|
// parses the Ad-Auction-Record-Event header in `request`, sending the result
|
||||||
|
// to `OnAdAuctionEventRecordHeaderReceived()` on
|
||||||
|
// `url_loader_network_observer_`.
|
||||||
|
void HandleResponse(const net::URLRequest& request);
|
||||||
|
|
||||||
|
private:
|
||||||
|
mojom::AttributionReportingEligibility attribution_reporting_eligibility_ =
|
||||||
|
mojom::AttributionReportingEligibility::kUnset;
|
||||||
|
|
||||||
|
const raw_ptr<mojom::URLLoaderNetworkServiceObserver>
|
||||||
|
url_loader_network_observer_;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace network
|
||||||
|
|
||||||
|
#endif // SERVICES_NETWORK_AD_AUCTION_EVENT_RECORD_REQUEST_HELPER_H_
|
@@ -25,6 +25,7 @@ mojom("mojom_ad_auction") {
|
|||||||
{
|
{
|
||||||
mojom = "network.mojom.AdAuctionEventRecord"
|
mojom = "network.mojom.AdAuctionEventRecord"
|
||||||
cpp = "::network::AdAuctionEventRecord"
|
cpp = "::network::AdAuctionEventRecord"
|
||||||
|
move_only = true
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
traits_headers = [
|
traits_headers = [
|
||||||
@@ -636,6 +637,7 @@ mojom("url_loader_base") {
|
|||||||
|
|
||||||
public_deps = [
|
public_deps = [
|
||||||
":cookies_mojom",
|
":cookies_mojom",
|
||||||
|
":mojom_ad_auction",
|
||||||
":mojom_attribution",
|
":mojom_attribution",
|
||||||
":mojom_content_security_policy",
|
":mojom_content_security_policy",
|
||||||
":mojom_integrity_algorithm",
|
":mojom_integrity_algorithm",
|
||||||
|
@@ -7,6 +7,7 @@ module network.mojom;
|
|||||||
import "mojo/public/mojom/base/string16.mojom";
|
import "mojo/public/mojom/base/string16.mojom";
|
||||||
import "mojo/public/mojom/base/time.mojom";
|
import "mojo/public/mojom/base/time.mojom";
|
||||||
import "mojo/public/mojom/base/unguessable_token.mojom";
|
import "mojo/public/mojom/base/unguessable_token.mojom";
|
||||||
|
import "services/network/public/mojom/ad_auction.mojom";
|
||||||
import "services/network/public/mojom/cookie_partition_key.mojom";
|
import "services/network/public/mojom/cookie_partition_key.mojom";
|
||||||
import "services/network/public/mojom/ip_address.mojom";
|
import "services/network/public/mojom/ip_address.mojom";
|
||||||
import "services/network/public/mojom/ip_address_space.mojom";
|
import "services/network/public/mojom/ip_address_space.mojom";
|
||||||
@@ -195,8 +196,16 @@ interface URLLoaderNetworkServiceObserver {
|
|||||||
// triggered by response headers and Shared Storage calls from the same
|
// triggered by response headers and Shared Storage calls from the same
|
||||||
// renderer process where this request originated.
|
// renderer process where this request originated.
|
||||||
OnSharedStorageHeaderReceived(url.mojom.Origin request_origin,
|
OnSharedStorageHeaderReceived(url.mojom.Origin request_origin,
|
||||||
array<SharedStorageModifierMethodWithOptions> methods_with_options,
|
array<SharedStorageModifierMethodWithOptions> methods_with_options,
|
||||||
network.mojom.LockName? with_lock) => ();
|
network.mojom.LockName? with_lock) => ();
|
||||||
|
|
||||||
|
// Notifies the browser of the results of successful parsing of
|
||||||
|
// a Ad-Auction-Record-Event header. The browser stores information from
|
||||||
|
// `ad_auction_event_record` in the interest group database, for use in
|
||||||
|
// generateBid() browser signals during interest group auctions, providing
|
||||||
|
// bidders view and click signals.
|
||||||
|
OnAdAuctionEventRecordHeaderReceived(
|
||||||
|
AdAuctionEventRecord ad_auction_event_record);
|
||||||
|
|
||||||
// Used by the NetworkService to create a copy of this observer.
|
// Used by the NetworkService to create a copy of this observer.
|
||||||
// (e.g. when creating an observer for URLLoader from URLLoaderFactory's
|
// (e.g. when creating an observer for URLLoader from URLLoaderFactory's
|
||||||
|
@@ -87,6 +87,9 @@ void TestURLLoaderNetworkObserver::OnSharedStorageHeaderReceived(
|
|||||||
std::move(callback).Run();
|
std::move(callback).Run();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void TestURLLoaderNetworkObserver::OnAdAuctionEventRecordHeaderReceived(
|
||||||
|
network::AdAuctionEventRecord event_record) {}
|
||||||
|
|
||||||
void TestURLLoaderNetworkObserver::Clone(
|
void TestURLLoaderNetworkObserver::Clone(
|
||||||
mojo::PendingReceiver<URLLoaderNetworkServiceObserver> observer) {
|
mojo::PendingReceiver<URLLoaderNetworkServiceObserver> observer) {
|
||||||
receivers_.Add(this, std::move(observer));
|
receivers_.Add(this, std::move(observer));
|
||||||
|
@@ -72,6 +72,8 @@ class TestURLLoaderNetworkObserver
|
|||||||
methods_with_options,
|
methods_with_options,
|
||||||
const std::optional<std::string>& with_lock,
|
const std::optional<std::string>& with_lock,
|
||||||
OnSharedStorageHeaderReceivedCallback callback) override;
|
OnSharedStorageHeaderReceivedCallback callback) override;
|
||||||
|
void OnAdAuctionEventRecordHeaderReceived(
|
||||||
|
network::AdAuctionEventRecord event_record) override;
|
||||||
void Clone(
|
void Clone(
|
||||||
mojo::PendingReceiver<URLLoaderNetworkServiceObserver> observer) override;
|
mojo::PendingReceiver<URLLoaderNetworkServiceObserver> observer) override;
|
||||||
void OnWebSocketConnectedToPrivateNetwork(
|
void OnWebSocketConnectedToPrivateNetwork(
|
||||||
|
@@ -717,6 +717,9 @@ URLLoader::URLLoader(
|
|||||||
std::make_unique<SharedStorageRequestHelper>(
|
std::make_unique<SharedStorageRequestHelper>(
|
||||||
shared_storage_writable_eligible,
|
shared_storage_writable_eligible,
|
||||||
url_loader_network_observer_)),
|
url_loader_network_observer_)),
|
||||||
|
ad_auction_event_record_request_helper_(
|
||||||
|
request.attribution_reporting_eligibility,
|
||||||
|
url_loader_network_observer_),
|
||||||
has_fetch_streaming_upload_body_(HasFetchStreamingUploadBody(&request)),
|
has_fetch_streaming_upload_body_(HasFetchStreamingUploadBody(&request)),
|
||||||
accept_ch_frame_observer_(std::move(accept_ch_frame_observer)),
|
accept_ch_frame_observer_(std::move(accept_ch_frame_observer)),
|
||||||
allow_cookies_from_browser_(
|
allow_cookies_from_browser_(
|
||||||
@@ -2029,17 +2032,24 @@ void URLLoader::ProcessInboundSharedStorageInterceptorOnResponseStarted() {
|
|||||||
|
|
||||||
void URLLoader::ProcessInboundAttributionInterceptorOnResponseStarted() {
|
void URLLoader::ProcessInboundAttributionInterceptorOnResponseStarted() {
|
||||||
if (!attribution_request_helper_) {
|
if (!attribution_request_helper_) {
|
||||||
ProcessInboundSharedStorageInterceptorOnResponseStarted();
|
ProcessInboundAdAuctionEventRecordInterceptorOnResponseStarted();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
attribution_request_helper_->Finalize(
|
attribution_request_helper_->Finalize(
|
||||||
*response_,
|
*response_,
|
||||||
base::BindOnce(
|
base::BindOnce(
|
||||||
&URLLoader::ProcessInboundSharedStorageInterceptorOnResponseStarted,
|
&URLLoader::
|
||||||
|
ProcessInboundAdAuctionEventRecordInterceptorOnResponseStarted,
|
||||||
weak_ptr_factory_.GetWeakPtr()));
|
weak_ptr_factory_.GetWeakPtr()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void URLLoader::
|
||||||
|
ProcessInboundAdAuctionEventRecordInterceptorOnResponseStarted() {
|
||||||
|
ad_auction_event_record_request_helper_.HandleResponse(*url_request_);
|
||||||
|
ProcessInboundSharedStorageInterceptorOnResponseStarted();
|
||||||
|
}
|
||||||
|
|
||||||
void URLLoader::OnResponseStarted(net::URLRequest* url_request, int net_error) {
|
void URLLoader::OnResponseStarted(net::URLRequest* url_request, int net_error) {
|
||||||
DCHECK(url_request == url_request_.get());
|
DCHECK(url_request == url_request_.get());
|
||||||
has_received_response_ = true;
|
has_received_response_ = true;
|
||||||
|
@@ -36,6 +36,7 @@
|
|||||||
#include "net/socket/socket_tag.h"
|
#include "net/socket/socket_tag.h"
|
||||||
#include "net/traffic_annotation/network_traffic_annotation.h"
|
#include "net/traffic_annotation/network_traffic_annotation.h"
|
||||||
#include "net/url_request/url_request.h"
|
#include "net/url_request/url_request.h"
|
||||||
|
#include "services/network/ad_auction/event_record_request_helper.h"
|
||||||
#include "services/network/attribution/attribution_request_helper.h"
|
#include "services/network/attribution/attribution_request_helper.h"
|
||||||
#include "services/network/keepalive_statistics_recorder.h"
|
#include "services/network/keepalive_statistics_recorder.h"
|
||||||
#include "services/network/network_service.h"
|
#include "services/network/network_service.h"
|
||||||
@@ -412,8 +413,8 @@ class COMPONENT_EXPORT(NETWORK_SERVICE) URLLoader
|
|||||||
// Storage operations to call
|
// Storage operations to call
|
||||||
// - If the request has not received the `kSharedStorageWriteHeader` response
|
// - If the request has not received the `kSharedStorageWriteHeader` response
|
||||||
// header, or if parsing fails to produce any valid operations, then
|
// header, or if parsing fails to produce any valid operations, then
|
||||||
// immediately call `ContinueOnReceivedRedirect`
|
// immediately call `ContinueOnReceiveRedirect`
|
||||||
// - Otherwise, `ContinueOnReceivedRedirect` will be run asynchronously after
|
// - Otherwise, `ContinueOnReceiveRedirect` will be run asynchronously after
|
||||||
// forwarding the operations to `URLLoaderNetworkServiceObserver` to queue via
|
// forwarding the operations to `URLLoaderNetworkServiceObserver` to queue via
|
||||||
// Mojo
|
// Mojo
|
||||||
//
|
//
|
||||||
@@ -448,7 +449,7 @@ class COMPONENT_EXPORT(NETWORK_SERVICE) URLLoader
|
|||||||
//
|
//
|
||||||
// Start in `ProcessOutboundAttributionInterceptor`
|
// Start in `ProcessOutboundAttributionInterceptor`
|
||||||
// - If `attribution_request_helper_` is not defined, immediately
|
// - If `attribution_request_helper_` is not defined, immediately
|
||||||
// calls`ScheduleStart`.
|
// calls `ScheduleStart`.
|
||||||
// - Otherwise:
|
// - Otherwise:
|
||||||
// - Execute `AttributionRequestHelper::Begin`
|
// - Execute `AttributionRequestHelper::Begin`
|
||||||
// - On Begin's callback, calls `ScheduleStart`
|
// - On Begin's callback, calls `ScheduleStart`
|
||||||
@@ -466,18 +467,44 @@ class COMPONENT_EXPORT(NETWORK_SERVICE) URLLoader
|
|||||||
// Inbound control flow:
|
// Inbound control flow:
|
||||||
//
|
//
|
||||||
// Start in `ProcessInboundAttributionInterceptorOnResponseStarted`
|
// Start in `ProcessInboundAttributionInterceptorOnResponseStarted`
|
||||||
// - If `attribution_request_helper_` is not defined, immediately
|
// - If `attribution_request_helper_` is not defined,
|
||||||
// calls`ProcessInboundSharedStorageInterceptorOnResponseStarted`.
|
// immediately calls
|
||||||
|
// `ProcessInboundAdAuctionEventRecordInterceptorOnResponseStarted`.
|
||||||
// - Otherwise:
|
// - Otherwise:
|
||||||
// - Execute `AttributionRequestHelper::Finalize`
|
// - Execute `AttributionRequestHelper::Finalize`
|
||||||
// - On Finalize's callback, calls
|
// - On Finalize's callback, calls
|
||||||
// `ProcessInboundSharedStorageInterceptorOnResponseStarted`
|
// `ProcessInboundAdAuctionEventRecordInterceptorOnResponseStarted`.
|
||||||
void ProcessOutboundAttributionInterceptor();
|
void ProcessOutboundAttributionInterceptor();
|
||||||
void ProcessInboundAttributionInterceptorOnReceivedRedirect(
|
void ProcessInboundAttributionInterceptorOnReceivedRedirect(
|
||||||
const ::net::RedirectInfo& redirect_info,
|
const ::net::RedirectInfo& redirect_info,
|
||||||
mojom::URLResponseHeadPtr response);
|
mojom::URLResponseHeadPtr response);
|
||||||
void ProcessInboundAttributionInterceptorOnResponseStarted();
|
void ProcessInboundAttributionInterceptorOnResponseStarted();
|
||||||
|
|
||||||
|
// All inbound responses will invoke
|
||||||
|
// `ad_auction_event_record_request_helper_.HandleResponse()`, which
|
||||||
|
// always returns immediately without blocking, and also exits early for
|
||||||
|
// ineligible responses.
|
||||||
|
//
|
||||||
|
// Outbound control flow:
|
||||||
|
//
|
||||||
|
// There are no outbound flow methods as the request headers are set by
|
||||||
|
// ComputeAttributionReportingHeaders() in the URLLoader() constructor.
|
||||||
|
//
|
||||||
|
// Redirection control flow:
|
||||||
|
//
|
||||||
|
// Redirection isn't handled yet. TODO(crbug.com/394108643): Support
|
||||||
|
// capturing headers on redirection responses.
|
||||||
|
//
|
||||||
|
// Inbound control flow:
|
||||||
|
//
|
||||||
|
// Start in
|
||||||
|
// `ProcessInboundAdAuctionEventRecordInterceptorOnResponseStarted()`
|
||||||
|
// - Execute
|
||||||
|
// `ad_auction_event_record_request_helper_::HandleResponse()`.
|
||||||
|
// - Afterwards, execute
|
||||||
|
// `ProcessInboundSharedStorageInterceptorOnResponseStarted()`.
|
||||||
|
void ProcessInboundAdAuctionEventRecordInterceptorOnResponseStarted();
|
||||||
|
|
||||||
// Continuation of `OnReceivedRedirect` after possibly asynchronously
|
// Continuation of `OnReceivedRedirect` after possibly asynchronously
|
||||||
// concluding the request's Attribution and/or Shared Storage operations.
|
// concluding the request's Attribution and/or Shared Storage operations.
|
||||||
void ContinueOnReceiveRedirect(const ::net::RedirectInfo& redirect_info,
|
void ContinueOnReceiveRedirect(const ::net::RedirectInfo& redirect_info,
|
||||||
@@ -847,6 +874,11 @@ class COMPONENT_EXPORT(NETWORK_SERVICE) URLLoader
|
|||||||
// (https://github.com/WICG/shared-storage#from-response-headers).
|
// (https://github.com/WICG/shared-storage#from-response-headers).
|
||||||
std::unique_ptr<SharedStorageRequestHelper> shared_storage_request_helper_;
|
std::unique_ptr<SharedStorageRequestHelper> shared_storage_request_helper_;
|
||||||
|
|
||||||
|
// Request helper responsible for processing Ad Auction record event
|
||||||
|
// headers.
|
||||||
|
// (https://github.com/WICG/turtledove/pull/1279)
|
||||||
|
AdAuctionEventRecordRequestHelper ad_auction_event_record_request_helper_;
|
||||||
|
|
||||||
// Indicates |url_request_| is fetch upload request and that has streaming
|
// Indicates |url_request_| is fetch upload request and that has streaming
|
||||||
// body.
|
// body.
|
||||||
const bool has_fetch_streaming_upload_body_;
|
const bool has_fetch_streaming_upload_body_;
|
||||||
|
@@ -18728,6 +18728,25 @@
|
|||||||
]
|
]
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
"ProtectedAudienceClickiness": [
|
||||||
|
{
|
||||||
|
"platforms": [
|
||||||
|
"android",
|
||||||
|
"chromeos",
|
||||||
|
"linux",
|
||||||
|
"mac",
|
||||||
|
"windows"
|
||||||
|
],
|
||||||
|
"experiments": [
|
||||||
|
{
|
||||||
|
"name": "Enabled",
|
||||||
|
"enable_features": [
|
||||||
|
"AdAuctionEventRegistration"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
"ProtectedAudienceDealsSupport": [
|
"ProtectedAudienceDealsSupport": [
|
||||||
{
|
{
|
||||||
"platforms": [
|
"platforms": [
|
||||||
|
@@ -960,6 +960,7 @@ bool CopyViewAndClickCountsProvidersFromIdlToMojo(
|
|||||||
provider.Utf8().c_str(), input.name().Utf8().c_str()));
|
provider.Utf8().c_str(), input.name().Utf8().c_str()));
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
view_and_click_counts_providers.push_back(parsed_provider);
|
||||||
}
|
}
|
||||||
|
|
||||||
output.view_and_click_counts_providers =
|
output.view_and_click_counts_providers =
|
||||||
|
Reference in New Issue
Block a user