[Protected Audiences] Rework seller signals cross-origin delay logic.
Previously, the SellerWorklet called a Pause() method on its TrustedSignalsManager until it had verified the seller would let it send requests cross-origin, at which point it resumed them. This CL removes TrustedSignalsManager::Pause(), and instead has the SellerWorklet delay sending requests to the TrustedSignalsManager until it has validated the seller is ok with sending the information cross-origin. This puts fewer requirements on the TrustedSignalsManager (Like the expectation it's only used by a single SellerWorklet). When the SellerWorklet learns it can send requests cross-origin, it also now immediately flushes all pending seller signals requests, rather than adhering to the send request delay or relying on tracking the most recent SendPendingSignalsRequests() call. While we could respect those, it doesn't seem worth the effort. Bug: None Change-Id: Iafdfe9fc05d2c501a573aba32a222ef4781195e2 Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/5522414 Reviewed-by: Maks Orlovich <morlovich@chromium.org> Commit-Queue: mmenke <mmenke@chromium.org> Cr-Commit-Position: refs/heads/main@{#1298786}
This commit is contained in:

committed by
Chromium LUCI CQ

parent
2fb4d62534
commit
e487958e3e
@ -442,14 +442,6 @@ SellerWorklet::SellerWorklet(
|
||||
: nullptr);
|
||||
trusted_signals_relation_ = ClassifyTrustedSignals(
|
||||
script_source_url_, trusted_scoring_signals_origin_);
|
||||
// If trusted seller signals are cross-origin, we cannot start fetching them
|
||||
// until we have confirmation they are authorized by guaranteed-same-origin
|
||||
// script, as otherwise we may end up sending sensitive IG information to an
|
||||
// unrelated this party.
|
||||
if (trusted_signals_relation_ ==
|
||||
SignalsOriginRelation::kUnknownPermissionCrossOriginSignals) {
|
||||
trusted_signals_request_manager_->Pause();
|
||||
}
|
||||
|
||||
v8_state_ = std::unique_ptr<V8State, base::OnTaskRunnerDeleter>(
|
||||
new V8State(v8_helper_, debug_id_, std::move(shared_storage_host_remote),
|
||||
@ -580,14 +572,14 @@ void SellerWorklet::ScoreAd(
|
||||
// If `trusted_signals_request_manager_` exists, there's a trusted scoring
|
||||
// signals URL which needs to be fetched before the auction can be run.
|
||||
if (trusted_signals_request_manager_) {
|
||||
score_ad_task->trusted_scoring_signals_request =
|
||||
trusted_signals_request_manager_->RequestScoringSignals(
|
||||
browser_signal_render_url,
|
||||
score_ad_task->browser_signal_ad_components,
|
||||
score_ad_task->auction_ad_config_non_shared_params
|
||||
.max_trusted_scoring_signals_url_length,
|
||||
base::BindOnce(&SellerWorklet::OnTrustedScoringSignalsDownloaded,
|
||||
base::Unretained(this), score_ad_task));
|
||||
// Can only start fetching trusted seller signals if they're same-origin or
|
||||
// we have confirmation they are authorized by guaranteed-same-origin
|
||||
// script, as otherwise we may end up sending sensitive IG information to an
|
||||
// unrelated third party.
|
||||
if (trusted_signals_relation_ !=
|
||||
SignalsOriginRelation::kUnknownPermissionCrossOriginSignals) {
|
||||
StartFetchingSignalsForTask(score_ad_task);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
@ -1784,26 +1776,32 @@ void SellerWorklet::OnGotCrossOriginTrustedSignalsPermissions(
|
||||
DCHECK_EQ(trusted_signals_relation_,
|
||||
SignalsOriginRelation::kUnknownPermissionCrossOriginSignals);
|
||||
|
||||
for (const auto& permitted_origin : permit_origins) {
|
||||
if (trusted_scoring_signals_origin_->IsSameOriginWith(permitted_origin)) {
|
||||
// Cross-origin trusted signals fetch authorized, so let it start.
|
||||
trusted_signals_relation_ =
|
||||
SignalsOriginRelation::kPermittedCrossOriginSignals;
|
||||
trusted_signals_request_manager_->Resume();
|
||||
return;
|
||||
if (std::any_of(permit_origins.begin(), permit_origins.end(),
|
||||
[&](const auto& permitted_origin) {
|
||||
return trusted_scoring_signals_origin_->IsSameOriginWith(
|
||||
permitted_origin);
|
||||
})) {
|
||||
// Cross-origin trusted signals fetch authorized. Update
|
||||
// `trusted_signals_relation_` accordingly and start fetches.
|
||||
trusted_signals_relation_ =
|
||||
SignalsOriginRelation::kPermittedCrossOriginSignals;
|
||||
for (auto it = score_ad_tasks_.begin(); it != score_ad_tasks_.end(); ++it) {
|
||||
StartFetchingSignalsForTask(it);
|
||||
}
|
||||
// Rather than keep track of whether there was a
|
||||
// SendPendingSignalsRequests() call while waiting to check the cross-origin
|
||||
// trusted signals permissions, unconditionally flush pending signals
|
||||
// requests, to keep things simple.
|
||||
SendPendingSignalsRequests();
|
||||
return;
|
||||
}
|
||||
|
||||
// Trusted scoring signals fetch disallowed; remove them from the queue and
|
||||
// proceed without them.
|
||||
// Trusted scoring signals fetch disallowed.
|
||||
trusted_signals_relation_ =
|
||||
SellerWorklet::SignalsOriginRelation::kForbiddenCrossOriginSignals;
|
||||
for (auto& task : score_ad_tasks_) {
|
||||
task.trusted_scoring_signals_request.reset();
|
||||
}
|
||||
|
||||
// Also remove the `trusted_signals_request_manager_` so we don't try to
|
||||
// fetch any more.
|
||||
// Remove the `trusted_signals_request_manager_` so we don't try to fetch any
|
||||
// more.
|
||||
trusted_signals_request_manager_.reset();
|
||||
|
||||
// If we're here, we don't actually have to worry about kicking off scoreAd()
|
||||
@ -1812,6 +1810,22 @@ void SellerWorklet::OnGotCrossOriginTrustedSignalsPermissions(
|
||||
DCHECK(!IsCodeReady());
|
||||
}
|
||||
|
||||
void SellerWorklet::StartFetchingSignalsForTask(
|
||||
ScoreAdTaskList::iterator score_ad_task) {
|
||||
CHECK(trusted_signals_relation_ ==
|
||||
SignalsOriginRelation::kSameOriginSignals ||
|
||||
trusted_signals_relation_ ==
|
||||
SignalsOriginRelation::kPermittedCrossOriginSignals);
|
||||
score_ad_task->trusted_scoring_signals_request =
|
||||
trusted_signals_request_manager_->RequestScoringSignals(
|
||||
score_ad_task->browser_signal_render_url,
|
||||
score_ad_task->browser_signal_ad_components,
|
||||
score_ad_task->auction_ad_config_non_shared_params
|
||||
.max_trusted_scoring_signals_url_length,
|
||||
base::BindOnce(&SellerWorklet::OnTrustedScoringSignalsDownloaded,
|
||||
base::Unretained(this), score_ad_task));
|
||||
}
|
||||
|
||||
void SellerWorklet::OnTrustedScoringSignalsDownloaded(
|
||||
ScoreAdTaskList::iterator task,
|
||||
scoped_refptr<TrustedSignals::Result> result,
|
||||
@ -1876,7 +1890,10 @@ void SellerWorklet::OnDirectFromSellerAuctionSignalsDownloadedScoreAd(
|
||||
}
|
||||
|
||||
bool SellerWorklet::IsReadyToScoreAd(const ScoreAdTask& task) const {
|
||||
return !task.trusted_scoring_signals_request &&
|
||||
// The first check should be implied by IsCodeReady(), but best to be safe.
|
||||
return trusted_signals_relation_ !=
|
||||
SignalsOriginRelation::kUnknownPermissionCrossOriginSignals &&
|
||||
!task.trusted_scoring_signals_request &&
|
||||
!task.direct_from_seller_request_seller_signals &&
|
||||
!task.direct_from_seller_request_auction_signals && IsCodeReady();
|
||||
}
|
||||
|
@ -480,6 +480,9 @@ class CONTENT_EXPORT SellerWorklet : public mojom::SellerWorklet {
|
||||
void OnGotCrossOriginTrustedSignalsPermissions(
|
||||
std::vector<url::Origin> permit_origins);
|
||||
|
||||
// Starts fetching signals for `score_ad_task`.
|
||||
void StartFetchingSignalsForTask(ScoreAdTaskList::iterator score_ad_task);
|
||||
|
||||
// Called when trusted scoring signals have finished downloading, or when
|
||||
// there are no scoring signals to download. Starts running scoreAd() on the
|
||||
// V8 thread.
|
||||
|
@ -6424,38 +6424,53 @@ TEST_F(SellerWorkletCrossOriginTrustedSignalsTest, AllowedCrossOriginTiming) {
|
||||
throw actual + "!" + expected;
|
||||
)";
|
||||
|
||||
base::RunLoop run_loop;
|
||||
auto seller_worklet = CreateWorklet();
|
||||
RunScoreAdOnWorkletAsync(
|
||||
seller_worklet.get(),
|
||||
/*expected_score=*/7,
|
||||
/*expected_errors=*/{}, mojom::ComponentAuctionModifiedBidParamsPtr(),
|
||||
/*expected_data_version=*/5,
|
||||
/*expected_debug_loss_report_url=*/std::nullopt,
|
||||
/*expected_debug_win_report_url=*/std::nullopt,
|
||||
mojom::RejectReason::kNotAvailable,
|
||||
/*expected_pa_requests=*/{},
|
||||
/*expected_real_time_contributions=*/{},
|
||||
/*expected_bid_in_seller_currency=*/std::nullopt,
|
||||
/*expected_score_ad_timeout=*/false,
|
||||
/*expected_signals_fetch_latency=*/std::nullopt,
|
||||
/*expected_code_ready_latency=*/std::nullopt, run_loop.QuitClosure());
|
||||
for (base::TimeDelta delay :
|
||||
{base::Seconds(0), TrustedSignalsRequestManager::kAutoSendDelay}) {
|
||||
base::RunLoop run_loop;
|
||||
auto seller_worklet = CreateWorklet();
|
||||
RunScoreAdOnWorkletAsync(
|
||||
seller_worklet.get(),
|
||||
/*expected_score=*/7,
|
||||
/*expected_errors=*/{}, mojom::ComponentAuctionModifiedBidParamsPtr(),
|
||||
/*expected_data_version=*/5,
|
||||
/*expected_debug_loss_report_url=*/std::nullopt,
|
||||
/*expected_debug_win_report_url=*/std::nullopt,
|
||||
mojom::RejectReason::kNotAvailable,
|
||||
/*expected_pa_requests=*/{},
|
||||
/*expected_real_time_contributions=*/{},
|
||||
/*expected_bid_in_seller_currency=*/std::nullopt,
|
||||
/*expected_score_ad_timeout=*/false,
|
||||
/*expected_signals_fetch_latency=*/std::nullopt,
|
||||
/*expected_code_ready_latency=*/std::nullopt, run_loop.QuitClosure());
|
||||
|
||||
// Only the script fetch must have started now... and after auto-send delay.
|
||||
task_environment_.FastForwardBy(TrustedSignalsRequestManager::kAutoSendDelay);
|
||||
EXPECT_EQ(1, url_loader_factory_.NumPending());
|
||||
EXPECT_TRUE(url_loader_factory_.IsPending(decision_logic_url_.spec()));
|
||||
// Only the script fetch must have started now... and after any (or no)
|
||||
// delay.
|
||||
if (delay != base::TimeDelta()) {
|
||||
task_environment_.FastForwardBy(delay);
|
||||
} else {
|
||||
// RunUntilIdle() does not advance the mock clock.
|
||||
task_environment_.RunUntilIdle();
|
||||
}
|
||||
EXPECT_EQ(1, url_loader_factory_.NumPending());
|
||||
EXPECT_TRUE(url_loader_factory_.IsPending(decision_logic_url_.spec()));
|
||||
|
||||
AddJavascriptResponse(&url_loader_factory_, decision_logic_url_,
|
||||
CreateScoreAdScript("3", kValidate), extra_js_headers_);
|
||||
task_environment_.FastForwardBy(TrustedSignalsRequestManager::kAutoSendDelay);
|
||||
AddJavascriptResponse(&url_loader_factory_, decision_logic_url_,
|
||||
CreateScoreAdScript("3", kValidate),
|
||||
extra_js_headers_);
|
||||
// Run pending tasks without advancing time. The queued signals fetch should
|
||||
// be requested immediately, regardless of whether "kAutoSendDelay" has
|
||||
// passed or not.
|
||||
task_environment_.RunUntilIdle();
|
||||
|
||||
// Now the trusted signals fetch must be pending, too.
|
||||
EXPECT_EQ(1, url_loader_factory_.NumPending());
|
||||
EXPECT_TRUE(url_loader_factory_.IsPending(kNoComponentSignalsUrl.spec()));
|
||||
AddVersionedJsonResponse(&url_loader_factory_, kNoComponentSignalsUrl,
|
||||
kTrustedScoringSignalsResponse, /*data_version=*/5);
|
||||
run_loop.Run();
|
||||
// Now the trusted signals fetch must be pending, too.
|
||||
EXPECT_EQ(1, url_loader_factory_.NumPending());
|
||||
EXPECT_TRUE(url_loader_factory_.IsPending(kNoComponentSignalsUrl.spec()));
|
||||
AddVersionedJsonResponse(&url_loader_factory_, kNoComponentSignalsUrl,
|
||||
kTrustedScoringSignalsResponse,
|
||||
/*data_version=*/5);
|
||||
run_loop.Run();
|
||||
url_loader_factory_.ClearResponses();
|
||||
}
|
||||
}
|
||||
|
||||
// Handling of errors in trusted signals other than the cross-origin permission;
|
||||
|
@ -373,11 +373,6 @@ void TrustedSignalsRequestManager::StartBatchedTrustedSignalsRequest() {
|
||||
// No need to continue running the timer, if it's running.
|
||||
timer_.Stop();
|
||||
|
||||
if (fetches_paused_) {
|
||||
deferred_start_batch_due_to_fetch_pause_ = true;
|
||||
return;
|
||||
}
|
||||
|
||||
std::unique_ptr<TrustedSignalsUrlBuilder> url_builder;
|
||||
bool split_fetch = base::FeatureList::IsEnabled(
|
||||
blink::features::kFledgeSplitTrustedSignalsFetchingURL);
|
||||
@ -406,19 +401,6 @@ void TrustedSignalsRequestManager::StartBatchedTrustedSignalsRequest() {
|
||||
return;
|
||||
}
|
||||
|
||||
void TrustedSignalsRequestManager::Pause() {
|
||||
fetches_paused_ = true;
|
||||
}
|
||||
|
||||
void TrustedSignalsRequestManager::Resume() {
|
||||
DCHECK(fetches_paused_);
|
||||
fetches_paused_ = false;
|
||||
if (deferred_start_batch_due_to_fetch_pause_) {
|
||||
deferred_start_batch_due_to_fetch_pause_ = false;
|
||||
StartBatchedTrustedSignalsRequest();
|
||||
}
|
||||
}
|
||||
|
||||
TrustedSignalsRequestManager::RequestImpl::RequestImpl(
|
||||
TrustedSignalsRequestManager* trusted_signals_request_manager,
|
||||
const std::string& interest_group_name,
|
||||
@ -536,9 +518,13 @@ void TrustedSignalsRequestManager::OnRequestDestroyed(RequestImpl* request) {
|
||||
}
|
||||
|
||||
void TrustedSignalsRequestManager::QueueRequest(RequestImpl* request) {
|
||||
// If the timer is not running, then either `automatically_send_requests_`
|
||||
// is false, or no requests should be in `queued_requests_`.
|
||||
DCHECK_EQ(timer_.IsRunning(),
|
||||
automatically_send_requests_ && !queued_requests_.empty());
|
||||
|
||||
queued_requests_.insert(request);
|
||||
if (automatically_send_requests_ &&
|
||||
!deferred_start_batch_due_to_fetch_pause_ && !timer_.IsRunning()) {
|
||||
if (automatically_send_requests_ && !timer_.IsRunning()) {
|
||||
timer_.Start(
|
||||
FROM_HERE, kAutoSendDelay,
|
||||
base::BindOnce(
|
||||
|
@ -137,12 +137,6 @@ class CONTENT_EXPORT TrustedSignalsRequestManager {
|
||||
|
||||
const GURL& trusted_signals_url() const { return trusted_signals_url_; }
|
||||
|
||||
// If Pause() is called, no actual fetches will happen until Resume() is
|
||||
// invoked. If any fetches would have started during the window between
|
||||
// Pause() and Resume(), Resume() will kick things off immediately.
|
||||
void Pause();
|
||||
void Resume();
|
||||
|
||||
private:
|
||||
struct BatchedTrustedSignalsRequest;
|
||||
|
||||
@ -256,16 +250,6 @@ class CONTENT_EXPORT TrustedSignalsRequestManager {
|
||||
base::UniquePtrComparator>
|
||||
batched_requests_;
|
||||
|
||||
// If this is true, outgoing requests will not be made until Resume() is
|
||||
// called; instead we merely denote that we were going to do so in
|
||||
// `deferred_start_batch_due_to_fetch_pause_`.
|
||||
bool fetches_paused_ = false;
|
||||
|
||||
// This is set if StartBatchedTrustedSignalsRequest() got called when
|
||||
// Pause()d, and denotes that queued fetches should be issued as soon as
|
||||
// Resume() is called.
|
||||
bool deferred_start_batch_due_to_fetch_pause_ = false;
|
||||
|
||||
base::OneShotTimer timer_;
|
||||
|
||||
mojo::Remote<auction_worklet::mojom::AuctionNetworkEventsHandler>
|
||||
|
@ -955,110 +955,6 @@ TEST_F(TrustedSignalsRequestManagerTest, ScoringSignalsBatchedRequests) {
|
||||
kAdComponentRenderUrls3));
|
||||
}
|
||||
|
||||
// Test of Pause()/Resume() with explicitly controlled batches.
|
||||
TEST_F(TrustedSignalsRequestManagerTest, ScoringSignalsBatchedRequestsPause) {
|
||||
scoring_request_manager_.Pause();
|
||||
|
||||
const GURL kRenderUrl1 = GURL("https://foo.test/");
|
||||
const std::vector<std::string> kAdComponentRenderUrls1{
|
||||
"https://foosub.test/", "https://bazsub.test/"};
|
||||
|
||||
const GURL kRenderUrl2 = GURL("https://bar.test/");
|
||||
const std::vector<std::string> kAdComponentRenderUrls2{
|
||||
"https://barsub.test/", "https://bazsub.test/"};
|
||||
|
||||
base::RunLoop run_loop1;
|
||||
scoped_refptr<TrustedSignals::Result> signals1;
|
||||
std::optional<std::string> error_msg1;
|
||||
auto request1 = scoring_request_manager_.RequestScoringSignals(
|
||||
kRenderUrl1, kAdComponentRenderUrls1,
|
||||
/*max_trusted_scoring_signals_url_length=*/0,
|
||||
base::BindOnce(&LoadSignalsCallback, &signals1, &error_msg1,
|
||||
run_loop1.QuitClosure()));
|
||||
|
||||
base::RunLoop run_loop2;
|
||||
scoped_refptr<TrustedSignals::Result> signals2;
|
||||
std::optional<std::string> error_msg2;
|
||||
auto request2 = scoring_request_manager_.RequestScoringSignals(
|
||||
kRenderUrl2, kAdComponentRenderUrls2,
|
||||
/*max_trusted_scoring_signals_url_length=*/0,
|
||||
base::BindOnce(&LoadSignalsCallback, &signals2, &error_msg2,
|
||||
run_loop2.QuitClosure()));
|
||||
|
||||
scoring_request_manager_.StartBatchedTrustedSignalsRequest();
|
||||
task_environment_.RunUntilIdle();
|
||||
// Nothing issued yet despite explicit StartBatchedTrustedSignalsRequest()
|
||||
// since we're paused.
|
||||
EXPECT_EQ(0, url_loader_factory_.NumPending());
|
||||
|
||||
// Queued fetches get issued after resume.
|
||||
scoring_request_manager_.Resume();
|
||||
EXPECT_EQ(1, url_loader_factory_.NumPending());
|
||||
|
||||
AddJsonResponse(
|
||||
&url_loader_factory_,
|
||||
GURL("https://url.test/"
|
||||
"?hostname=publisher&renderUrls=https%3A%2F%2Fbar.test%2F,https%3A%"
|
||||
"2F%2Ffoo.test%2F&adComponentRenderUrls=https%3A%2F%2Fbarsub.test%"
|
||||
"2F,https%3A%2F%2Fbazsub.test%2F,https%3A%2F%2Ffoosub.test%2F"),
|
||||
kBaseScoringJson);
|
||||
|
||||
run_loop1.Run();
|
||||
EXPECT_FALSE(error_msg1);
|
||||
ASSERT_TRUE(signals1);
|
||||
EXPECT_EQ(R"({"renderURL":{"https://foo.test/":1},)"
|
||||
R"("renderUrl":{"https://foo.test/":1},)"
|
||||
R"("adComponentRenderURLs":{"https://foosub.test/":2,)"
|
||||
R"("https://bazsub.test/":"4"},)"
|
||||
R"("adComponentRenderUrls":{"https://foosub.test/":2,)"
|
||||
R"("https://bazsub.test/":"4"}})",
|
||||
ExtractScoringSignals(signals1.get(), kRenderUrl1,
|
||||
kAdComponentRenderUrls1));
|
||||
|
||||
run_loop2.Run();
|
||||
EXPECT_FALSE(error_msg2);
|
||||
ASSERT_TRUE(signals2);
|
||||
EXPECT_EQ(R"({"renderURL":{"https://bar.test/":[2]},)"
|
||||
R"("renderUrl":{"https://bar.test/":[2]},)"
|
||||
R"("adComponentRenderURLs":{"https://barsub.test/":[3],)"
|
||||
R"("https://bazsub.test/":"4"},)"
|
||||
R"("adComponentRenderUrls":{"https://barsub.test/":[3],)"
|
||||
R"("https://bazsub.test/":"4"}})",
|
||||
ExtractScoringSignals(signals2.get(), kRenderUrl2,
|
||||
kAdComponentRenderUrls2));
|
||||
|
||||
const GURL kRenderUrl3 = GURL("https://foo.test/");
|
||||
const std::vector<std::string> kAdComponentRenderUrls3{};
|
||||
|
||||
base::RunLoop run_loop3;
|
||||
scoped_refptr<TrustedSignals::Result> signals3;
|
||||
std::optional<std::string> error_msg3;
|
||||
auto request3 = scoring_request_manager_.RequestScoringSignals(
|
||||
kRenderUrl3, kAdComponentRenderUrls3,
|
||||
/*max_trusted_scoring_signals_url_length=*/0,
|
||||
base::BindOnce(&LoadSignalsCallback, &signals3, &error_msg3,
|
||||
run_loop3.QuitClosure()));
|
||||
|
||||
// Since we're not paused anymore, issuing happens at
|
||||
// StartBatchedTrustedSignalsRequest call.
|
||||
EXPECT_EQ(0, url_loader_factory_.NumPending());
|
||||
scoring_request_manager_.StartBatchedTrustedSignalsRequest();
|
||||
EXPECT_EQ(1, url_loader_factory_.NumPending());
|
||||
|
||||
AddJsonResponse(
|
||||
&url_loader_factory_,
|
||||
GURL("https://url.test/"
|
||||
"?hostname=publisher&renderUrls=https%3A%2F%2Ffoo.test%2F"),
|
||||
kBaseScoringJson);
|
||||
run_loop3.Run();
|
||||
EXPECT_FALSE(error_msg3);
|
||||
ASSERT_TRUE(signals3);
|
||||
EXPECT_EQ(R"({"renderURL":{"https://foo.test/":1},)"
|
||||
R"("renderUrl":{"https://foo.test/":1}})",
|
||||
ExtractScoringSignals(signals3.get(), kRenderUrl3,
|
||||
kAdComponentRenderUrls3));
|
||||
}
|
||||
|
||||
// Make two requests, cancel both, then try to start a network request. No
|
||||
// requests should be made. Only test bidders, since sellers have no significant
|
||||
// differences in this path.
|
||||
@ -1299,381 +1195,6 @@ TEST_F(TrustedSignalsRequestManagerTest, AutomaticallySendRequestsEnabled) {
|
||||
EXPECT_EQ(R"({"key3":"3"})", ExtractBiddingSignals(signals3.get(), kKeys3));
|
||||
}
|
||||
|
||||
TEST_F(TrustedSignalsRequestManagerTest, AutomaticallySendRequestsPause) {
|
||||
const GURL kRenderUrl1 = GURL("https://foo.test/");
|
||||
const GURL kRenderUrl2 = GURL("https://bar.test/");
|
||||
|
||||
// Create a new scoring request manager with `automatically_send_requests`
|
||||
// enabled.
|
||||
TrustedSignalsRequestManager scoring_request_manager(
|
||||
TrustedSignalsRequestManager::Type::kScoringSignals, &url_loader_factory_,
|
||||
/*auction_network_events_handler=*/
|
||||
auction_network_events_handler_.CreateRemote(),
|
||||
/*automatically_send_requests=*/true,
|
||||
url::Origin::Create(GURL(kTopLevelOrigin)), trusted_signals_url_,
|
||||
/*experiment_group_id=*/std::nullopt,
|
||||
/*trusted_bidding_signals_slot_size_param=*/"", v8_helper_.get());
|
||||
scoring_request_manager.Pause();
|
||||
|
||||
// Create one Request.
|
||||
base::RunLoop run_loop1;
|
||||
scoped_refptr<TrustedSignals::Result> signals1;
|
||||
std::optional<std::string> error_msg1;
|
||||
auto request1 = scoring_request_manager.RequestScoringSignals(
|
||||
kRenderUrl1, /*ad_component_render_urls=*/{},
|
||||
/*max_trusted_scoring_signals_url_length=*/0,
|
||||
base::BindOnce(&LoadSignalsCallback, &signals1, &error_msg1,
|
||||
run_loop1.QuitClosure()));
|
||||
|
||||
// Wait until just before the timer triggers. No network requests should be
|
||||
// made.
|
||||
task_environment_.FastForwardBy(TrustedSignalsRequestManager::kAutoSendDelay -
|
||||
kTinyTime);
|
||||
EXPECT_EQ(0, url_loader_factory_.NumPending());
|
||||
|
||||
// Nor after the timer, since this is paused.
|
||||
task_environment_.FastForwardBy(kTinyTime);
|
||||
ASSERT_EQ(0, url_loader_factory_.NumPending());
|
||||
|
||||
// Create another Request.
|
||||
base::RunLoop run_loop2;
|
||||
scoped_refptr<TrustedSignals::Result> signals2;
|
||||
std::optional<std::string> error_msg2;
|
||||
auto request2 = scoring_request_manager.RequestScoringSignals(
|
||||
kRenderUrl2, /*ad_component_render_urls=*/{},
|
||||
/*max_trusted_scoring_signals_url_length=*/0,
|
||||
base::BindOnce(&LoadSignalsCallback, &signals2, &error_msg2,
|
||||
run_loop2.QuitClosure()));
|
||||
|
||||
// Wait until almost the timer time, and Resume. This means the next timer
|
||||
// firing will be shortly after.
|
||||
ASSERT_EQ(0, url_loader_factory_.NumPending());
|
||||
task_environment_.FastForwardBy(TrustedSignalsRequestManager::kAutoSendDelay -
|
||||
kTinyTime);
|
||||
EXPECT_EQ(0, url_loader_factory_.NumPending());
|
||||
|
||||
// Upon resume, the first two requests are issued.
|
||||
scoring_request_manager.Resume();
|
||||
EXPECT_EQ(1, url_loader_factory_.NumPending());
|
||||
|
||||
AddJsonResponse(&url_loader_factory_,
|
||||
GURL("https://url.test/"
|
||||
"?hostname=publisher&renderUrls=https%3A%2F%2Fbar.test%"
|
||||
"2F,https%3A%2F%2Ffoo.test%2F"),
|
||||
kBaseScoringJson);
|
||||
|
||||
run_loop1.Run();
|
||||
EXPECT_FALSE(error_msg1) << *error_msg1;
|
||||
ASSERT_TRUE(signals1);
|
||||
EXPECT_EQ(R"({"renderURL":{"https://foo.test/":1},)"
|
||||
R"("renderUrl":{"https://foo.test/":1}})",
|
||||
ExtractScoringSignals(signals1.get(), kRenderUrl1,
|
||||
/*ad_component_render_urls=*/{}));
|
||||
|
||||
run_loop2.Run();
|
||||
EXPECT_FALSE(error_msg2) << *error_msg2;
|
||||
ASSERT_TRUE(signals2);
|
||||
EXPECT_EQ(R"({"renderURL":{"https://bar.test/":[2]},)"
|
||||
R"("renderUrl":{"https://bar.test/":[2]}})",
|
||||
ExtractScoringSignals(signals2.get(), kRenderUrl2,
|
||||
/*ad_component_render_urls=*/{}));
|
||||
|
||||
// Queue up a third request.
|
||||
const std::vector<std::string> kAdComponentRenderUrls{"https://foosub.test/",
|
||||
"https://bazsub.test/"};
|
||||
base::RunLoop run_loop3;
|
||||
scoped_refptr<TrustedSignals::Result> signals3;
|
||||
std::optional<std::string> error_msg3;
|
||||
auto request3 = scoring_request_manager.RequestScoringSignals(
|
||||
kRenderUrl1, kAdComponentRenderUrls,
|
||||
/*max_trusted_scoring_signals_url_length=*/0,
|
||||
base::BindOnce(&LoadSignalsCallback, &signals3, &error_msg3,
|
||||
run_loop3.QuitClosure()));
|
||||
|
||||
// Not issued yet.
|
||||
EXPECT_EQ(0, url_loader_factory_.NumPending());
|
||||
|
||||
// Going ahead by kTinyTime isn't enough either; the timer is started on first
|
||||
// post-Resume request, not kept through the pause period.
|
||||
task_environment_.FastForwardBy(kTinyTime);
|
||||
EXPECT_EQ(0, url_loader_factory_.NumPending());
|
||||
|
||||
task_environment_.FastForwardBy(TrustedSignalsRequestManager::kAutoSendDelay -
|
||||
kTinyTime);
|
||||
EXPECT_EQ(1, url_loader_factory_.NumPending());
|
||||
|
||||
// Complete the request.
|
||||
AddJsonResponse(&url_loader_factory_,
|
||||
GURL("https://url.test/"
|
||||
"?hostname=publisher&renderUrls=https%3A%2F%2Ffoo.test%"
|
||||
"2F&adComponentRenderUrls=https%3A%2F%2Fbazsub.test%2F,"
|
||||
"https%3A%2F%2Ffoosub.test%2F"),
|
||||
kBaseScoringJson);
|
||||
run_loop3.Run();
|
||||
EXPECT_FALSE(error_msg3) << *error_msg3;
|
||||
ASSERT_TRUE(signals3);
|
||||
EXPECT_EQ(R"({"renderURL":{"https://foo.test/":1},)"
|
||||
R"("renderUrl":{"https://foo.test/":1},)"
|
||||
R"("adComponentRenderURLs":{"https://foosub.test/":2,)"
|
||||
R"("https://bazsub.test/":"4"},)"
|
||||
R"("adComponentRenderUrls":{"https://foosub.test/":2,)"
|
||||
R"("https://bazsub.test/":"4"}})",
|
||||
ExtractScoringSignals(signals3.get(), kRenderUrl1,
|
||||
kAdComponentRenderUrls));
|
||||
}
|
||||
|
||||
TEST_F(TrustedSignalsRequestManagerTest, AutomaticallySendRequestsLatePause) {
|
||||
const GURL kRenderUrl1 = GURL("https://foo.test/");
|
||||
const GURL kRenderUrl2 = GURL("https://bar.test/");
|
||||
|
||||
// Create a new scoring request manager with `automatically_send_requests`
|
||||
// enabled.
|
||||
TrustedSignalsRequestManager scoring_request_manager(
|
||||
TrustedSignalsRequestManager::Type::kScoringSignals, &url_loader_factory_,
|
||||
/*auction_network_events_handler=*/
|
||||
auction_network_events_handler_.CreateRemote(),
|
||||
/*automatically_send_requests=*/true,
|
||||
url::Origin::Create(GURL(kTopLevelOrigin)), trusted_signals_url_,
|
||||
/*experiment_group_id=*/std::nullopt,
|
||||
/*trusted_bidding_signals_slot_size_param=*/"", v8_helper_.get());
|
||||
|
||||
// Create one Request.
|
||||
base::RunLoop run_loop1;
|
||||
scoped_refptr<TrustedSignals::Result> signals1;
|
||||
std::optional<std::string> error_msg1;
|
||||
auto request1 = scoring_request_manager.RequestScoringSignals(
|
||||
kRenderUrl1, /*ad_component_render_urls=*/{},
|
||||
/*max_trusted_scoring_signals_url_length=*/0,
|
||||
base::BindOnce(&LoadSignalsCallback, &signals1, &error_msg1,
|
||||
run_loop1.QuitClosure()));
|
||||
|
||||
// Wait until a bit before the timer triggers. No network requests should be
|
||||
// made.
|
||||
task_environment_.FastForwardBy(TrustedSignalsRequestManager::kAutoSendDelay -
|
||||
kTinyTime - kTinyTime);
|
||||
EXPECT_EQ(0, url_loader_factory_.NumPending());
|
||||
|
||||
// Pause and resume at this point.
|
||||
scoring_request_manager.Pause();
|
||||
scoring_request_manager.Resume();
|
||||
|
||||
// Nothing is issued still, since we haven't gotten to batch end.
|
||||
ASSERT_EQ(0, url_loader_factory_.NumPending());
|
||||
|
||||
// Not yet.
|
||||
task_environment_.FastForwardBy(kTinyTime);
|
||||
ASSERT_EQ(0, url_loader_factory_.NumPending());
|
||||
|
||||
// Create another Request.
|
||||
base::RunLoop run_loop2;
|
||||
scoped_refptr<TrustedSignals::Result> signals2;
|
||||
std::optional<std::string> error_msg2;
|
||||
auto request2 = scoring_request_manager.RequestScoringSignals(
|
||||
kRenderUrl2, /*ad_component_render_urls=*/{},
|
||||
/*max_trusted_scoring_signals_url_length=*/0,
|
||||
base::BindOnce(&LoadSignalsCallback, &signals2, &error_msg2,
|
||||
run_loop2.QuitClosure()));
|
||||
|
||||
// Now it's up to timer time.
|
||||
task_environment_.FastForwardBy(kTinyTime);
|
||||
|
||||
EXPECT_EQ(1, url_loader_factory_.NumPending());
|
||||
|
||||
AddJsonResponse(&url_loader_factory_,
|
||||
GURL("https://url.test/"
|
||||
"?hostname=publisher&renderUrls=https%3A%2F%2Fbar.test%"
|
||||
"2F,https%3A%2F%2Ffoo.test%2F"),
|
||||
kBaseScoringJson);
|
||||
|
||||
run_loop1.Run();
|
||||
EXPECT_FALSE(error_msg1) << *error_msg1;
|
||||
ASSERT_TRUE(signals1);
|
||||
EXPECT_EQ(R"({"renderURL":{"https://foo.test/":1},)"
|
||||
R"("renderUrl":{"https://foo.test/":1}})",
|
||||
ExtractScoringSignals(signals1.get(), kRenderUrl1,
|
||||
/*ad_component_render_urls=*/{}));
|
||||
|
||||
run_loop2.Run();
|
||||
EXPECT_FALSE(error_msg2) << *error_msg2;
|
||||
ASSERT_TRUE(signals2);
|
||||
EXPECT_EQ(R"({"renderURL":{"https://bar.test/":[2]},)"
|
||||
R"("renderUrl":{"https://bar.test/":[2]}})",
|
||||
ExtractScoringSignals(signals2.get(), kRenderUrl2,
|
||||
/*ad_component_render_urls=*/{}));
|
||||
}
|
||||
|
||||
TEST_F(TrustedSignalsRequestManagerTest, AutomaticallySendRequestsEarlyResume) {
|
||||
const GURL kRenderUrl1 = GURL("https://foo.test/");
|
||||
const GURL kRenderUrl2 = GURL("https://bar.test/");
|
||||
|
||||
// Create a new scoring request manager with `automatically_send_requests`
|
||||
// enabled.
|
||||
TrustedSignalsRequestManager scoring_request_manager(
|
||||
TrustedSignalsRequestManager::Type::kScoringSignals, &url_loader_factory_,
|
||||
/*auction_network_events_handler=*/
|
||||
auction_network_events_handler_.CreateRemote(),
|
||||
/*automatically_send_requests=*/true,
|
||||
url::Origin::Create(GURL(kTopLevelOrigin)), trusted_signals_url_,
|
||||
/*experiment_group_id=*/std::nullopt,
|
||||
/*trusted_bidding_signals_slot_size_param=*/"", v8_helper_.get());
|
||||
scoring_request_manager.Pause();
|
||||
|
||||
// Create one Request.
|
||||
base::RunLoop run_loop1;
|
||||
scoped_refptr<TrustedSignals::Result> signals1;
|
||||
std::optional<std::string> error_msg1;
|
||||
auto request1 = scoring_request_manager.RequestScoringSignals(
|
||||
kRenderUrl1, /*ad_component_render_urls=*/{},
|
||||
/*max_trusted_scoring_signals_url_length=*/0,
|
||||
base::BindOnce(&LoadSignalsCallback, &signals1, &error_msg1,
|
||||
run_loop1.QuitClosure()));
|
||||
|
||||
// Wait until a bit before the timer triggers. No network requests should be
|
||||
// made.
|
||||
task_environment_.FastForwardBy(TrustedSignalsRequestManager::kAutoSendDelay -
|
||||
kTinyTime - kTinyTime);
|
||||
EXPECT_EQ(0, url_loader_factory_.NumPending());
|
||||
|
||||
scoring_request_manager.Resume();
|
||||
|
||||
// Nothing is issued still, since we haven't gotten to batch end.
|
||||
ASSERT_EQ(0, url_loader_factory_.NumPending());
|
||||
|
||||
// Not yet.
|
||||
task_environment_.FastForwardBy(kTinyTime);
|
||||
ASSERT_EQ(0, url_loader_factory_.NumPending());
|
||||
|
||||
// Create another Request.
|
||||
base::RunLoop run_loop2;
|
||||
scoped_refptr<TrustedSignals::Result> signals2;
|
||||
std::optional<std::string> error_msg2;
|
||||
auto request2 = scoring_request_manager.RequestScoringSignals(
|
||||
kRenderUrl2, /*ad_component_render_urls=*/{},
|
||||
/*max_trusted_scoring_signals_url_length=*/0,
|
||||
base::BindOnce(&LoadSignalsCallback, &signals2, &error_msg2,
|
||||
run_loop2.QuitClosure()));
|
||||
|
||||
// Now it's up to timer time.
|
||||
task_environment_.FastForwardBy(kTinyTime);
|
||||
|
||||
EXPECT_EQ(1, url_loader_factory_.NumPending());
|
||||
|
||||
AddJsonResponse(&url_loader_factory_,
|
||||
GURL("https://url.test/"
|
||||
"?hostname=publisher&renderUrls=https%3A%2F%2Fbar.test%"
|
||||
"2F,https%3A%2F%2Ffoo.test%2F"),
|
||||
kBaseScoringJson);
|
||||
|
||||
run_loop1.Run();
|
||||
EXPECT_FALSE(error_msg1) << *error_msg1;
|
||||
ASSERT_TRUE(signals1);
|
||||
EXPECT_EQ(R"({"renderURL":{"https://foo.test/":1},)"
|
||||
R"("renderUrl":{"https://foo.test/":1}})",
|
||||
ExtractScoringSignals(signals1.get(), kRenderUrl1,
|
||||
/*ad_component_render_urls=*/{}));
|
||||
|
||||
run_loop2.Run();
|
||||
EXPECT_FALSE(error_msg2) << *error_msg2;
|
||||
ASSERT_TRUE(signals2);
|
||||
EXPECT_EQ(R"({"renderURL":{"https://bar.test/":[2]},)"
|
||||
R"("renderUrl":{"https://bar.test/":[2]}})",
|
||||
ExtractScoringSignals(signals2.get(), kRenderUrl2,
|
||||
/*ad_component_render_urls=*/{}));
|
||||
}
|
||||
|
||||
TEST_F(TrustedSignalsRequestManagerTest,
|
||||
AutomaticallySendRequestsPauseWithExplicitFlush) {
|
||||
const GURL kRenderUrl1 = GURL("https://foo.test/");
|
||||
const GURL kRenderUrl2 = GURL("https://bar.test/");
|
||||
|
||||
// Create a new scoring request manager with `automatically_send_requests`
|
||||
// enabled.
|
||||
TrustedSignalsRequestManager scoring_request_manager(
|
||||
TrustedSignalsRequestManager::Type::kScoringSignals, &url_loader_factory_,
|
||||
/*auction_network_events_handler=*/
|
||||
auction_network_events_handler_.CreateRemote(),
|
||||
/*automatically_send_requests=*/true,
|
||||
url::Origin::Create(GURL(kTopLevelOrigin)), trusted_signals_url_,
|
||||
/*experiment_group_id=*/std::nullopt,
|
||||
/*trusted_bidding_signals_slot_size_param=*/"", v8_helper_.get());
|
||||
|
||||
// Create one Request.
|
||||
base::RunLoop run_loop1;
|
||||
scoped_refptr<TrustedSignals::Result> signals1;
|
||||
std::optional<std::string> error_msg1;
|
||||
auto request1 = scoring_request_manager.RequestScoringSignals(
|
||||
kRenderUrl1, /*ad_component_render_urls=*/{},
|
||||
/*max_trusted_scoring_signals_url_length=*/0,
|
||||
base::BindOnce(&LoadSignalsCallback, &signals1, &error_msg1,
|
||||
run_loop1.QuitClosure()));
|
||||
|
||||
// Pause before it gets chance to issue.
|
||||
scoring_request_manager.Pause();
|
||||
|
||||
// Explicitly ask for a send; still doesn't send.
|
||||
scoring_request_manager.StartBatchedTrustedSignalsRequest();
|
||||
EXPECT_EQ(0, url_loader_factory_.NumPending());
|
||||
|
||||
// Wait until just before the timer triggers. No network requests should be
|
||||
// made.
|
||||
task_environment_.FastForwardBy(TrustedSignalsRequestManager::kAutoSendDelay -
|
||||
kTinyTime);
|
||||
EXPECT_EQ(0, url_loader_factory_.NumPending());
|
||||
|
||||
scoring_request_manager.Resume();
|
||||
|
||||
// The request got sent now.
|
||||
ASSERT_EQ(1, url_loader_factory_.NumPending());
|
||||
AddJsonResponse(
|
||||
&url_loader_factory_,
|
||||
GURL("https://url.test/"
|
||||
"?hostname=publisher&renderUrls=https%3A%2F%2Ffoo.test%2F"),
|
||||
kBaseScoringJson);
|
||||
|
||||
run_loop1.Run();
|
||||
EXPECT_FALSE(error_msg1) << *error_msg1;
|
||||
ASSERT_TRUE(signals1);
|
||||
EXPECT_EQ(R"({"renderURL":{"https://foo.test/":1},)"
|
||||
R"("renderUrl":{"https://foo.test/":1}})",
|
||||
ExtractScoringSignals(signals1.get(), kRenderUrl1,
|
||||
/*ad_component_render_urls=*/{}));
|
||||
|
||||
// Create another Request.
|
||||
base::RunLoop run_loop2;
|
||||
scoped_refptr<TrustedSignals::Result> signals2;
|
||||
std::optional<std::string> error_msg2;
|
||||
auto request2 = scoring_request_manager.RequestScoringSignals(
|
||||
kRenderUrl2, /*ad_component_render_urls=*/{},
|
||||
/*max_trusted_scoring_signals_url_length=*/0,
|
||||
base::BindOnce(&LoadSignalsCallback, &signals2, &error_msg2,
|
||||
run_loop2.QuitClosure()));
|
||||
|
||||
// Up to timer time from initial request, it's still not sent.
|
||||
task_environment_.FastForwardBy(kTinyTime);
|
||||
EXPECT_EQ(0, url_loader_factory_.NumPending());
|
||||
|
||||
// Need to get to timer time after resume.
|
||||
task_environment_.FastForwardBy(TrustedSignalsRequestManager::kAutoSendDelay -
|
||||
kTinyTime - kTinyTime);
|
||||
EXPECT_EQ(0, url_loader_factory_.NumPending());
|
||||
task_environment_.FastForwardBy(kTinyTime);
|
||||
EXPECT_EQ(1, url_loader_factory_.NumPending());
|
||||
|
||||
AddJsonResponse(
|
||||
&url_loader_factory_,
|
||||
GURL("https://url.test/"
|
||||
"?hostname=publisher&renderUrls=https%3A%2F%2Fbar.test%2F"),
|
||||
kBaseScoringJson);
|
||||
|
||||
run_loop2.Run();
|
||||
EXPECT_FALSE(error_msg2) << *error_msg2;
|
||||
ASSERT_TRUE(signals2);
|
||||
EXPECT_EQ(R"({"renderURL":{"https://bar.test/":[2]},)"
|
||||
R"("renderUrl":{"https://bar.test/":[2]}})",
|
||||
ExtractScoringSignals(signals2.get(), kRenderUrl2,
|
||||
/*ad_component_render_urls=*/{}));
|
||||
}
|
||||
|
||||
TEST_F(TrustedSignalsRequestManagerTest,
|
||||
AutomaticallySendRequestsCancelAllRequestsRestartsTimer) {
|
||||
const std::vector<std::string> kKeys1{"key1"};
|
||||
|
Reference in New Issue
Block a user