Skip to content

Commit

Permalink
Add setBid function to FLEDGE generateBid worklet
Browse files Browse the repository at this point in the history
Bug: 1307885
Change-Id: I66a3b97693561596969188a7f7c4d1cc8c9339d0
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/3536593
Reviewed-by: Maks Orlovich <morlovich@chromium.org>
Commit-Queue: Russ Hamilton <behamilton@google.com>
Cr-Commit-Position: refs/heads/main@{#985469}
  • Loading branch information
brusshamilton authored and Chromium LUCI CQ committed Mar 25, 2022
1 parent 6b07996 commit 13abbb3
Show file tree
Hide file tree
Showing 9 changed files with 762 additions and 215 deletions.
2 changes: 2 additions & 0 deletions content/services/auction_worklet/BUILD.gn
Expand Up @@ -54,6 +54,8 @@ source_set("auction_worklet") {
"report_bindings.h",
"seller_worklet.cc",
"seller_worklet.h",
"set_bid_bindings.cc",
"set_bid_bindings.h",
"trusted_signals.cc",
"trusted_signals.h",
"trusted_signals_request_manager.cc",
Expand Down
183 changes: 25 additions & 158 deletions content/services/auction_worklet/bidder_worklet.cc
Expand Up @@ -22,6 +22,7 @@
#include "content/services/auction_worklet/for_debugging_only_bindings.h"
#include "content/services/auction_worklet/public/mojom/auction_worklet_service.mojom.h"
#include "content/services/auction_worklet/report_bindings.h"
#include "content/services/auction_worklet/set_bid_bindings.h"
#include "content/services/auction_worklet/trusted_signals.h"
#include "content/services/auction_worklet/trusted_signals_request_manager.h"
#include "content/services/auction_worklet/worklet_loader.h"
Expand Down Expand Up @@ -118,32 +119,6 @@ bool CreateAdVector(AuctionV8Helper* v8_helper,
return true;
}

// Checks that `url` is a valid URL and is in `ads`. Appends an error to
// `out_errors` if not. `script_source_url` is used in output error messages
// only.
bool IsAllowedAdUrl(const GURL& url,
const GURL& script_source_url,
const char* argument_name,
const std::vector<blink::InterestGroup::Ad>& ads,
std::vector<std::string>& out_errors) {
if (!url.is_valid() || !url.SchemeIs(url::kHttpsScheme)) {
out_errors.push_back(
base::StrCat({script_source_url.spec(), " generateBid() returned ",
argument_name, " URL that isn't a valid https:// URL."}));
return false;
}

for (const auto& ad : ads) {
if (url == ad.render_url)
return true;
}
out_errors.push_back(base::StrCat({script_source_url.spec(),
" generateBid() returned ", argument_name,
" URL that isn't one "
"of the registered creative URLs."}));
return false;
}

} // namespace

BidderWorklet::BidderWorklet(
Expand Down Expand Up @@ -191,10 +166,6 @@ BidderWorklet::~BidderWorklet() {
debug_id_->AbortDebuggerPauses();
}

bool BidderWorklet::IsValidBid(double bid) {
return !std::isnan(bid) && std::isfinite(bid) && bid > 0;
}

int BidderWorklet::context_group_id_for_testing() const {
return debug_id_->context_group_id();
}
Expand Down Expand Up @@ -442,6 +413,11 @@ void BidderWorklet::V8State::GenerateBid(
v8::ObjectTemplate::New(isolate);
ForDebuggingOnlyBindings for_debugging_only_bindings(v8_helper_.get(),
global_template);
SetBidBindings set_bid_bindings(
v8_helper_.get(), global_template, start,
browser_signal_top_level_seller_origin.has_value(),
bidder_worklet_non_shared_params->ads,
bidder_worklet_non_shared_params->ad_components);

// Short lived context, to avoid leaking data at global scope between either
// repeated calls to this worklet, or to calls to any other worklet.
Expand Down Expand Up @@ -562,143 +538,34 @@ void BidderWorklet::V8State::GenerateBid(
std::vector<std::string> errors_out;
v8_helper_->MaybeTriggerInstrumentationBreakpoint(
*debug_id_, "beforeBidderWorkletBiddingStart");
if (!v8_helper_
->RunScript(context, worklet_script_.Get(isolate), debug_id_.get(),
"generateBid", args, std::move(per_buyer_timeout),
errors_out)
.ToLocal(&generate_bid_result)) {
PostErrorBidCallbackToUserThread(std::move(callback),
std::move(errors_out));
return;
}

if (!generate_bid_result->IsObject()) {
errors_out.push_back(
base::StrCat({script_source_url_.spec(),
" generateBid() return value not an object."}));
PostErrorBidCallbackToUserThread(std::move(callback),
std::move(errors_out));
return;
}

gin::Dictionary result_dict(isolate, generate_bid_result.As<v8::Object>());

v8::Local<v8::Value> ad_object;
std::string ad_json;
double bid;
std::string render_url_string;
// Parse and validate values.
if (!result_dict.Get("ad", &ad_object) ||
!v8_helper_->ExtractJson(context, ad_object, &ad_json) ||
!result_dict.Get("bid", &bid) ||
!result_dict.Get("render", &render_url_string)) {
errors_out.push_back(
base::StrCat({script_source_url_.spec(),
" generateBid() return value has incorrect structure."}));
PostErrorBidCallbackToUserThread(std::move(callback),
std::move(errors_out));
return;
}
bool got_return_value =
v8_helper_
->RunScript(context, worklet_script_.Get(isolate), debug_id_.get(),
"generateBid", args, std::move(per_buyer_timeout),
errors_out)
.ToLocal(&generate_bid_result);

if (browser_signal_top_level_seller_origin) {
bool allow_component_auction;
if (!result_dict.Get("allowComponentAuction", &allow_component_auction) ||
!allow_component_auction) {
errors_out.push_back(base::StrCat(
{script_source_url_.spec(),
" generateBid() return value does not have allowComponentAuction "
"set to true. Bid dropped from component auction."}));
PostErrorBidCallbackToUserThread(std::move(callback),
std::move(errors_out));
return;
}
if (got_return_value) {
set_bid_bindings.SetBid(
generate_bid_result,
base::StrCat({script_source_url_.spec(), " generateBid() "}),
errors_out);
}

if (!IsValidBid(bid)) {
if (!set_bid_bindings.has_bid()) {
// If we either don't have a valid return value, or we have no return value
// and no intermediate result was given through setBid, return an error.
PostErrorBidCallbackToUserThread(std::move(callback),
std::move(errors_out));
return;
}

GURL render_url(render_url_string);
if (!IsAllowedAdUrl(render_url, script_source_url_, "render",
*bidder_worklet_non_shared_params->ads, errors_out)) {
PostErrorBidCallbackToUserThread(std::move(callback),
std::move(errors_out));
return;
}

absl::optional<std::vector<GURL>> ad_component_urls;
v8::Local<v8::Value> ad_components;
if (result_dict.Get("adComponents", &ad_components) &&
!ad_components->IsNullOrUndefined()) {
if (!bidder_worklet_non_shared_params->ad_components) {
errors_out.push_back(
base::StrCat({script_source_url_.spec(),
" generateBid() return value contains adComponents but "
"InterestGroup has no adComponents."}));
PostErrorBidCallbackToUserThread(std::move(callback),
std::move(errors_out));
return;
}

if (!ad_components->IsArray()) {
errors_out.push_back(base::StrCat(
{script_source_url_.spec(),
" generateBid() returned adComponents value must be an array."}));
PostErrorBidCallbackToUserThread(std::move(callback),
std::move(errors_out));
return;
}

v8::Local<v8::Array> ad_components_array = ad_components.As<v8::Array>();
if (ad_components_array->Length() > blink::kMaxAdAuctionAdComponents) {
errors_out.push_back(base::StringPrintf(
"%s generateBid() returned adComponents with over %zu items.",
script_source_url_.spec().c_str(), blink::kMaxAdAuctionAdComponents));
PostErrorBidCallbackToUserThread(std::move(callback),
std::move(errors_out));
return;
}

ad_component_urls.emplace();
for (size_t i = 0; i < ad_components_array->Length(); ++i) {
std::string url_string;
if (!gin::ConvertFromV8(
isolate, ad_components_array->Get(context, i).ToLocalChecked(),
&url_string)) {
errors_out.push_back(
base::StrCat({script_source_url_.spec(),
" generateBid() returned adComponents value must be "
"an array of strings."}));
PostErrorBidCallbackToUserThread(std::move(callback),
std::move(errors_out));
return;
}

GURL ad_component_url(url_string);
if (!IsAllowedAdUrl(ad_component_url, script_source_url_, "adComponents",
*bidder_worklet_non_shared_params->ad_components,
errors_out)) {
PostErrorBidCallbackToUserThread(std::move(callback),
std::move(errors_out));
return;
}
ad_component_urls->emplace_back(std::move(ad_component_url));
}
}

user_thread_->PostTask(
FROM_HERE,
base::BindOnce(std::move(callback),
mojom::BidderWorkletBid::New(
std::move(ad_json), bid, std::move(render_url),
std::move(ad_component_urls),
/*bid_duration=*/base::TimeTicks::Now() - start),
bidding_signals_data_version,
for_debugging_only_bindings.TakeLossReportUrl(),
for_debugging_only_bindings.TakeWinReportUrl(),
std::move(errors_out)));
FROM_HERE, base::BindOnce(std::move(callback), set_bid_bindings.TakeBid(),
bidding_signals_data_version,
for_debugging_only_bindings.TakeLossReportUrl(),
for_debugging_only_bindings.TakeWinReportUrl(),
std::move(errors_out)));
}

void BidderWorklet::V8State::ConnectDevToolsAgent(
Expand Down
5 changes: 0 additions & 5 deletions content/services/auction_worklet/bidder_worklet.h
Expand Up @@ -79,11 +79,6 @@ class BidderWorklet : public mojom::BidderWorklet {
~BidderWorklet() override;
BidderWorklet& operator=(const BidderWorklet&) = delete;

// Returns true if `bid` is a valid bid value. Bids of "0" and other values
// that mean no bid was offered are considered invalid, for the purposes of
// this method.
static bool IsValidBid(double bid);

// Sets the callback to be invoked on errors which require closing the pipe.
// Callback will also immediately delete `this`. Not an argument to
// constructor because the Mojo ReceiverId needs to be bound to the callback,
Expand Down

0 comments on commit 13abbb3

Please sign in to comment.