blob: 89bb6b0a3e810f2ac41ec92fbb1b09d6d0a05892 [file] [log] [blame]
// Copyright 2021 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "content/services/auction_worklet/trusted_signals_request_manager.h"
#include <memory>
#include <set>
#include <string>
#include <vector>
#include "base/callback.h"
#include "base/check.h"
#include "base/containers/unique_ptr_adapters.h"
#include "base/memory/raw_ptr.h"
#include "base/memory/ref_counted.h"
#include "base/memory/scoped_refptr.h"
#include "base/memory/weak_ptr.h"
#include "base/notreached.h"
#include "content/services/auction_worklet/auction_v8_helper.h"
#include "content/services/auction_worklet/trusted_signals.h"
#include "services/network/public/mojom/url_loader_factory.mojom-forward.h"
#include "third_party/abseil-cpp/absl/types/optional.h"
#include "url/gurl.h"
#include "url/origin.h"
namespace auction_worklet {
TrustedSignalsRequestManager::TrustedSignalsRequestManager(
Type type,
network::mojom::URLLoaderFactory* url_loader_factory,
bool automatically_send_requests,
const url::Origin& top_level_origin,
const GURL& trusted_signals_url,
AuctionV8Helper* v8_helper)
: type_(type),
url_loader_factory_(url_loader_factory),
automatically_send_requests_(automatically_send_requests),
top_level_origin_(top_level_origin),
trusted_signals_url_(trusted_signals_url),
v8_helper_(v8_helper) {}
TrustedSignalsRequestManager::~TrustedSignalsRequestManager() {
// All outstanding Requests should have been destroyed before `this`.
DCHECK(queued_requests_.empty());
DCHECK(batched_requests_.empty());
}
std::unique_ptr<TrustedSignalsRequestManager::Request>
TrustedSignalsRequestManager::RequestBiddingSignals(
const std::vector<std::string>& keys,
LoadSignalsCallback load_signals_callback) {
DCHECK_EQ(Type::kBiddingSignals, type_);
std::unique_ptr<RequestImpl> request = std::make_unique<RequestImpl>(
this, std::set<std::string>(keys.begin(), keys.end()),
std::move(load_signals_callback));
QueueRequest(request.get());
return request;
}
std::unique_ptr<TrustedSignalsRequestManager::Request>
TrustedSignalsRequestManager::RequestScoringSignals(
const GURL& render_url,
const std::vector<std::string>& ad_component_render_urls,
LoadSignalsCallback load_signals_callback) {
DCHECK_EQ(Type::kScoringSignals, type_);
std::unique_ptr<RequestImpl> request = std::make_unique<RequestImpl>(
this, render_url,
std::set<std::string>(ad_component_render_urls.begin(),
ad_component_render_urls.end()),
std::move(load_signals_callback));
QueueRequest(request.get());
return request;
}
void TrustedSignalsRequestManager::StartBatchedTrustedSignalsRequest() {
if (queued_requests_.empty()) {
// The timer should never be running when there are no pending requests.
DCHECK(!timer_.IsRunning());
return;
}
// No need to continue running the timer, if it's running.
timer_.Stop();
BatchedTrustedSignalsRequest* batched_request =
batched_requests_
.emplace(std::make_unique<BatchedTrustedSignalsRequest>())
.first->get();
batched_request->requests = std::move(queued_requests_);
if (type_ == Type::kBiddingSignals) {
// Append all keys into a single set, and clear each request's list of keys,
// as it's no longer needed. Consumers provide their own list of keys again
// when they request v8 objects from the TrustedSignals::Results returned by
// `this`.
std::set<std::string> keys;
for (RequestImpl* request : batched_request->requests) {
keys.insert(request->bidder_keys_->begin(), request->bidder_keys_->end());
request->bidder_keys_.reset();
request->batched_request_ = batched_request;
}
batched_request->trusted_signals = TrustedSignals::LoadBiddingSignals(
url_loader_factory_, std::move(keys), top_level_origin_.host(),
trusted_signals_url_, v8_helper_,
base::BindOnce(&TrustedSignalsRequestManager::OnSignalsLoaded,
base::Unretained(this), batched_request));
return;
}
DCHECK_EQ(type_, Type::kScoringSignals);
// Append urls into two sets, and clear each request's URLs, as they're no
// longer needed.
std::set<std::string> render_urls;
std::set<std::string> ad_component_render_urls;
for (RequestImpl* request : batched_request->requests) {
render_urls.insert(request->render_url_->spec());
ad_component_render_urls.insert(request->ad_component_render_urls_->begin(),
request->ad_component_render_urls_->end());
request->render_url_.reset();
request->ad_component_render_urls_.reset();
request->batched_request_ = batched_request;
}
batched_request->trusted_signals = TrustedSignals::LoadScoringSignals(
url_loader_factory_, std::move(render_urls),
std::move(ad_component_render_urls), top_level_origin_.host(),
trusted_signals_url_, v8_helper_,
base::BindOnce(&TrustedSignalsRequestManager::OnSignalsLoaded,
base::Unretained(this), batched_request));
}
TrustedSignalsRequestManager::RequestImpl::RequestImpl(
TrustedSignalsRequestManager* trusted_signals_request_manager,
std::set<std::string> bidder_keys,
LoadSignalsCallback load_signals_callback)
: bidder_keys_(std::move(bidder_keys)),
load_signals_callback_(std::move(load_signals_callback)),
trusted_signals_request_manager_(trusted_signals_request_manager) {
DCHECK(!bidder_keys_->empty());
}
TrustedSignalsRequestManager::RequestImpl::RequestImpl(
TrustedSignalsRequestManager* trusted_signals_request_manager,
const GURL& render_url,
std::set<std::string> ad_component_render_urls,
LoadSignalsCallback load_signals_callback)
: render_url_(render_url),
ad_component_render_urls_(std::move(ad_component_render_urls)),
load_signals_callback_(std::move(load_signals_callback)),
trusted_signals_request_manager_(trusted_signals_request_manager) {}
TrustedSignalsRequestManager::RequestImpl::~RequestImpl() {
if (trusted_signals_request_manager_)
trusted_signals_request_manager_->OnRequestDestroyed(this);
}
TrustedSignalsRequestManager::BatchedTrustedSignalsRequest::
BatchedTrustedSignalsRequest() = default;
TrustedSignalsRequestManager::BatchedTrustedSignalsRequest::
~BatchedTrustedSignalsRequest() = default;
void TrustedSignalsRequestManager::OnSignalsLoaded(
BatchedTrustedSignalsRequest* batched_request,
scoped_refptr<Result> result,
absl::optional<std::string> error_msg) {
DCHECK(batched_requests_.find(batched_request) != batched_requests_.end());
for (RequestImpl* request : batched_request->requests) {
DCHECK_EQ(request->batched_request_, batched_request);
// Remove association with `this` and `batched_request` before invoking
// callback, which may destroy the Request.
request->trusted_signals_request_manager_ = nullptr;
request->batched_request_ = nullptr;
// It is illegal for this this to destroy another request, so
// `batched_request->requests` should not be affected by invoking this,
// other than the current element's pointer potentially now pointing to a
// destroyed object.
std::move(request->load_signals_callback_).Run(result, error_msg);
}
batched_requests_.erase(batched_requests_.find(batched_request));
}
void TrustedSignalsRequestManager::OnRequestDestroyed(RequestImpl* request) {
// If the request is not assigned to a BatchedTrustedSignalsRequest, it's
// still in `queued_requests_`, so remove it from that.
if (!request->batched_request_) {
size_t removed = queued_requests_.erase(request);
DCHECK_EQ(removed, 1u);
// If there are no more requests, stop the timer.
if (queued_requests_.empty())
timer_.Stop();
return;
}
// Otherwise, it should not be in `queued_requests_`.
DCHECK_EQ(queued_requests_.count(request), 0u);
// But it should be in the `requests` set of the BatchedTrustedSignalsRequest
// it's pointing to.
size_t removed = request->batched_request_->requests.erase(request);
DCHECK_EQ(removed, 1u);
// Cancel and delete the corresponding BatchedTrustedSignalsRequest if it's
// no longer associated with any live requests.
if (request->batched_request_->requests.empty())
batched_requests_.erase(
batched_requests_.find(request->batched_request_.get()));
}
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_ && !timer_.IsRunning()) {
timer_.Start(
FROM_HERE, kAutoSendDelay,
base::BindOnce(
&TrustedSignalsRequestManager::StartBatchedTrustedSignalsRequest,
base::Unretained(this)));
}
}
} // namespace auction_worklet