| // 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. |
| |
| #ifndef CONTENT_SERVICES_AUCTION_WORKLET_TRUSTED_SIGNALS_H_ |
| #define CONTENT_SERVICES_AUCTION_WORKLET_TRUSTED_SIGNALS_H_ |
| |
| #include <map> |
| #include <memory> |
| #include <set> |
| #include <string> |
| #include <vector> |
| |
| #include "base/callback.h" |
| #include "base/memory/ref_counted.h" |
| #include "base/memory/scoped_refptr.h" |
| #include "net/http/http_response_headers.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 "v8/include/v8-forward.h" |
| |
| namespace auction_worklet { |
| |
| class AuctionDownloader; |
| class AuctionV8Helper; |
| |
| // Represents the trusted bidding/scoring signals that are part of the FLEDGE |
| // bidding system (https://github.com/WICG/turtledove/blob/main/FLEDGE.md). |
| // Fetches and parses the hosted JSON data files needed by worklets. There are |
| // separate methods for fetching bidding and scoring signals. A single |
| // TrustedSignals object can only be used to fetch bidding signals or scoring |
| // signals, even if a single URL is used for both types of signals. |
| // |
| // TODO(mmenke): This class currently does 4 copies when loading the data (To V8 |
| // string, use V8's JSON parser, split data into V8 JSON subcomponent strings, |
| // convert to C++ strings), and 2 copies of each substring to use the data (To |
| // V8 per-key JSON string, use V8's JSON parser). Keeping the data stored as V8 |
| // JSON subcomponents would remove 2 copies, without too much complexity. Could |
| // even implement V8 deep-copy logic, to remove two more copies (counting the |
| // clone operation as a copy). |
| class TrustedSignals { |
| public: |
| // Contains the values returned by the server. |
| // |
| // This can be created and destroyed on any thread, but GetSignals() can only |
| // be used on the V8 thread. |
| class Result : public base::RefCountedThreadSafe<Result> { |
| public: |
| // Constructor for bidding signals. |
| Result(std::map<std::string, std::string> bidder_json_data, |
| absl::optional<uint32_t> data_version); |
| |
| // Constructor for scoring signals. |
| Result(std::map<std::string, std::string> render_url_json_data, |
| std::map<std::string, std::string> ad_component_json_data, |
| absl::optional<uint32_t> data_version); |
| |
| explicit Result(const Result&) = delete; |
| Result& operator=(const Result&) = delete; |
| |
| // Retrieves the trusted bidding signals associated with the passed in keys, |
| // in the format expected by a worklet's generateBid() method. `this` must |
| // have been generated by fetching bidding signals. `v8_helper`'s Isolate |
| // must be active (in particular, this must be on the v8 thread), and |
| // `context` must be the active context. `bidding_signals_keys` must be |
| // subsets of the keys provided when creating the TrustedSignals object. |
| // Always returns a non-empty value. |
| v8::Local<v8::Object> GetBiddingSignals( |
| AuctionV8Helper* v8_helper, |
| v8::Local<v8::Context> context, |
| const std::vector<std::string>& bidding_signals_keys) const; |
| |
| // Retrieves the trusted scoring signals associated with the passed in urls, |
| // in the format expected by a worklet's scoreAd() method. `this` must have |
| // been generated by fetching scoring signals. `v8_helper`'s Isolate must be |
| // active (in particular, this must be on the v8 thread), and `context` must |
| // be the active context. `render_url` and `ad_component_render_urls` must |
| // be subsets of the corresponding sets of GURLs provided when creating the |
| // TrustedSignals object. Always returns a non-empty value. |
| v8::Local<v8::Object> GetScoringSignals( |
| AuctionV8Helper* v8_helper, |
| v8::Local<v8::Context> context, |
| const GURL& render_url, |
| const std::vector<std::string>& ad_component_render_urls) const; |
| |
| absl::optional<uint32_t> GetDataVersion() const { return data_version_; } |
| |
| private: |
| friend class base::RefCountedThreadSafe<Result>; |
| |
| ~Result(); |
| |
| // Map of keys to the associated JSON data for trusted bidding signals. |
| const absl::optional<std::map<std::string, std::string>> bidder_json_data_; |
| |
| // Map of keys to the associated JSON data for trusted scoring signals. |
| const absl::optional<std::map<std::string, std::string>> |
| render_url_json_data_; |
| const absl::optional<std::map<std::string, std::string>> |
| ad_component_json_data_; |
| |
| // Data version associated with the trusted signals. |
| const absl::optional<uint32_t> data_version_; |
| }; |
| |
| using LoadSignalsCallback = |
| base::OnceCallback<void(scoped_refptr<Result> result, |
| absl::optional<std::string> error_msg)>; |
| |
| explicit TrustedSignals(const TrustedSignals&) = delete; |
| TrustedSignals& operator=(const TrustedSignals&) = delete; |
| ~TrustedSignals(); |
| |
| // Constructs a TrustedSignals for fetching bidding signals, and starts the |
| // fetch. `trusted_bidding_signals_url` must be the base URL (no query params |
| // added). Callback will be invoked asynchronously once the data has been |
| // fetched or an error has occurred. De-duplicates keys when assembling the |
| // full URL for the fetch. Fails if the URL already has a query param (or has |
| // a location or embedded credentials) or if the response is not JSON. If some |
| // or all of the render URLs are missing, still succeeds, and GetSignals() |
| // will populate them with nulls. |
| // |
| // There are no lifetime constraints of `url_loader_factory`. |
| static std::unique_ptr<TrustedSignals> LoadBiddingSignals( |
| network::mojom::URLLoaderFactory* url_loader_factory, |
| std::set<std::string> bidding_signals_keys, |
| const std::string& hostname, |
| const GURL& trusted_bidding_signals_url, |
| scoped_refptr<AuctionV8Helper> v8_helper, |
| LoadSignalsCallback load_signals_callback); |
| |
| // Just like LoadBiddingSignals() above, but for fetching seller signals. |
| static std::unique_ptr<TrustedSignals> LoadScoringSignals( |
| network::mojom::URLLoaderFactory* url_loader_factory, |
| std::set<std::string> render_urls, |
| std::set<std::string> ad_component_render_urls, |
| const std::string& hostname, |
| const GURL& trusted_scoring_signals_url, |
| scoped_refptr<AuctionV8Helper> v8_helper, |
| LoadSignalsCallback load_signals_callback); |
| |
| private: |
| TrustedSignals(absl::optional<std::set<std::string>> bidding_signals_keys, |
| absl::optional<std::set<std::string>> render_urls, |
| absl::optional<std::set<std::string>> ad_component_render_urls, |
| const GURL& trusted_signals_url, |
| scoped_refptr<AuctionV8Helper> v8_helper, |
| LoadSignalsCallback load_signals_callback); |
| |
| // Starts downloading `url`, which should be the bidding or scoring signals |
| // URL with the query parameter correctly set. |
| void StartDownload(network::mojom::URLLoaderFactory* url_loader_factory, |
| const GURL& full_signals_url); |
| |
| void OnDownloadComplete(std::unique_ptr<std::string> body, |
| scoped_refptr<net::HttpResponseHeaders> headers, |
| absl::optional<std::string> error_msg); |
| |
| // Parses the response body on the V8 thread, and extracts values associated |
| // with the requested keys. |
| static void HandleDownloadResultOnV8Thread( |
| scoped_refptr<AuctionV8Helper> v8_helper, |
| const GURL& signals_url, |
| absl::optional<std::set<std::string>> bidding_signals_keys, |
| absl::optional<std::set<std::string>> render_urls, |
| absl::optional<std::set<std::string>> ad_component_render_urls, |
| std::unique_ptr<std::string> body, |
| scoped_refptr<net::HttpResponseHeaders> headers, |
| absl::optional<std::string> error_msg, |
| scoped_refptr<base::SequencedTaskRunner> user_thread_task_runner, |
| base::WeakPtr<TrustedSignals> weak_instance); |
| |
| // Called from V8 thread. |
| static void PostCallbackToUserThread( |
| scoped_refptr<base::SequencedTaskRunner> user_thread_task_runner, |
| base::WeakPtr<TrustedSignals> weak_instance, |
| scoped_refptr<Result> result, |
| absl::optional<std::string> error_msg); |
| |
| // Called on user thread. |
| void DeliverCallbackOnUserThread(scoped_refptr<Result>, |
| absl::optional<std::string> error_msg); |
| |
| // Keys being fetched. For bidding signals, only `bidding_signals_keys_` is |
| // non-null. For scoring signals, only `render_urls_` and |
| // `ad_component_render_urls_` are non-null. These are cleared and ownership |
| // is passed to the V8 thread once the download completes, as they're no |
| // longer on the main thread after that point. |
| absl::optional<std::set<std::string>> bidding_signals_keys_; |
| absl::optional<std::set<std::string>> render_urls_; |
| absl::optional<std::set<std::string>> ad_component_render_urls_; |
| |
| const GURL trusted_signals_url_; // original, for error messages. |
| const scoped_refptr<AuctionV8Helper> v8_helper_; |
| |
| LoadSignalsCallback load_signals_callback_; |
| std::unique_ptr<AuctionDownloader> auction_downloader_; |
| |
| base::WeakPtrFactory<TrustedSignals> weak_ptr_factory{this}; |
| }; |
| |
| } // namespace auction_worklet |
| |
| #endif // CONTENT_SERVICES_AUCTION_WORKLET_TRUSTED_SIGNALS_H_ |