910e62b5创建于 1月15日历史提交
// Copyright 2021 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef CONTENT_BROWSER_INTEREST_GROUP_AUCTION_WORKLET_MANAGER_H_
#define CONTENT_BROWSER_INTEREST_GROUP_AUCTION_WORKLET_MANAGER_H_

#include <stdint.h>

#include <map>
#include <optional>
#include <string>
#include <vector>

#include "base/functional/callback.h"
#include "base/memory/raw_ptr.h"
#include "base/memory/scoped_refptr.h"
#include "base/observer_list_types.h"
#include "base/types/expected.h"
#include "content/browser/interest_group/auction_process_manager.h"
#include "content/browser/interest_group/bidding_and_auction_server_key_fetcher.h"
#include "content/browser/interest_group/subresource_url_builder.h"
#include "content/common/content_export.h"
#include "content/public/browser/frame_tree_node_id.h"
#include "content/services/auction_worklet/public/mojom/auction_shared_storage_host.mojom-forward.h"
#include "content/services/auction_worklet/public/mojom/bidder_worklet.mojom.h"
#include "content/services/auction_worklet/public/mojom/seller_worklet.mojom.h"
#include "services/network/public/mojom/client_security_state.mojom-forward.h"
#include "services/network/public/mojom/url_loader_factory.mojom-forward.h"
#include "url/gurl.h"
#include "url/origin.h"

namespace net {
class NetworkAnonymizationKey;
}

namespace content {

class AuctionMetricsRecorder;
class AuctionSharedStorageHost;
class AuctionNetworkEventsProxy;
class GroupByOriginKeyMapper;
class RenderFrameHostImpl;
class SiteInstance;
class SubresourceUrlAuthorizations;
class SubresourceUrlBuilder;

// Per-frame manager of auction worklets. Manages creation and sharing of
// worklets. Worklets may be reused if they share URLs for scripts and trusted
// signals (and thus share an owner), share top frame origins, and are in the
// same frame. The frame requirement is needed by DevTools and URLLoaderFactory
// hooks, the others by the current worklet implementations.
//
// If a worklet fails to load or crashes, information about the error is
// broadcast to all consumers of the worklet. After a load failure or crash, the
// worklet will not be able to invoke any pending callbacks passed over the Mojo
// interface.
//
// AuctionWorkletManager does not implement any prioritization, apart from
// invoking callbacks that are sharing a worklet in FIFO order. The
// AuctionProcessManager handles prioritization for process creation. Once a
// process is created for a worklet, the worklet is created immediately.
class CONTENT_EXPORT AuctionWorkletManager {
 public:
  using WorkletType = AuctionProcessManager::WorkletType;

  // How many callbacks are dispatched at once, if splitting up notifications is
  // on.
  static const size_t kBatchSize = 10;

  // Types of fatal error that can prevent a worklet from all further execution.
  enum class FatalErrorType {
    kScriptLoadFailed,
    kWorkletCrash,
  };

  using FatalErrorCallback =
      base::OnceCallback<void(FatalErrorType fatal_error_type,
                              const std::vector<std::string>& errors)>;

  FrameTreeNodeId GetFrameTreeNodeID();

  // Delegate class to allow dependency injection in tests. Note that passed in
  // URLLoaderFactories can crash and be restarted, so passing in raw pointers
  // would be problematic.
  class Delegate {
   public:
    // Returns the URLLoaderFactory of the associated frame. Used to load the
    // seller worklet scripts in the context of the parent frame, since unlike
    // other worklet types, it has no first party opt-in, and it's not a
    // cross-origin leak if the parent from knows its URL, since the parent
    // frame provided the URL in the first place.
    virtual network::mojom::URLLoaderFactory* GetFrameURLLoaderFactory() = 0;

    // Trusted URLLoaderFactory used to load bidder worklets, and seller scoring
    // signals.
    virtual network::mojom::URLLoaderFactory* GetTrustedURLLoaderFactory() = 0;

    // Preconnects a single uncredentialed socket with the provided parameters.
    virtual void PreconnectSocket(
        const GURL& url,
        const net::NetworkAnonymizationKey& network_anonymization_key) = 0;

    // Get containing frame. (Passed to debugging hooks, and also used to get
    // the renderer process ID for subresource loading).
    virtual RenderFrameHostImpl* GetFrame() = 0;

    // Returns the SiteInstance representing the frame running the auction.
    virtual scoped_refptr<SiteInstance> GetFrameSiteInstance() = 0;

    // Returns the ClientSecurityState associated with the frame, for use in
    // bidder worklet and signals fetches.
    virtual network::mojom::ClientSecurityStatePtr GetClientSecurityState() = 0;

    virtual void GetTrustedKeyValueServerKey(
        const url::Origin& scope_origin,
        const std::optional<url::Origin>& coordinator,
        base::OnceCallback<void(base::expected<BiddingAndAuctionServerKey,
                                               std::string>)> callback) = 0;
  };

  // Internal class that owns and creates worklets. It also tracks pending
  // requests that the worklet will be provided upon creation. Refcounted and
  // owned by one or more worklet handles, rather than the
  // AuctionWorkletManager.
  class WorkletOwner;

  // Enough information to uniquely ID a worklet. If these fields match for two
  // worklets (and they're loaded in the same frame, as this class is
  // frame-scoped), the worklets can use the same Mojo Worklet object.
  //
  // TODO(xtlsheep): Currently, trusted KVv2 signals use a non-cached version,
  // where bidder and seller worklets construct v2 requests. Once we transition
  // to cached version, `trusted_signals_coordinator` and `contextual_data` can
  // be removed from the WorkletKey.
  struct CONTENT_EXPORT WorkletKey {
    WorkletKey(WorkletType type,
               const GURL& script_url,
               const std::optional<GURL>& wasm_url,
               const std::optional<GURL>& signals_url,
               bool needs_cors_for_additional_bid,
               std::optional<bool> send_creative_scanning_metadata,
               std::optional<uint16_t> experiment_group_id,
               const std::string& trusted_bidding_signals_slot_size_param,
               const std::optional<url::Origin>& trusted_signals_coordinator,
               const std::optional<std::string>& contextual_data);
    WorkletKey(const WorkletKey&);
    WorkletKey(WorkletKey&&);
    ~WorkletKey();

    // Fast, non-cryptographic hash to count unique worklets for UKM.
    size_t GetHash() const;

    bool operator<(const WorkletKey& other) const;

    WorkletType type;
    GURL script_url;
    std::optional<GURL> wasm_url;
    std::optional<GURL> signals_url;

    // `needs_cors_for_additional_bid` is set for buyer reporting for additional
    // bids; those need to perform a CORS check others don't.
    bool needs_cors_for_additional_bid;

    std::optional<bool> send_creative_scanning_metadata;

    std::optional<uint16_t> experiment_group_id;
    std::string trusted_bidding_signals_slot_size_param;
    std::optional<url::Origin> trusted_signals_coordinator;
    std::optional<std::string> contextual_data;
  };

  // Class that tracks a request for a Worklet, and helps manage the lifetime of
  // the returned Worklet once the request receives one. Destroying the handle
  // will abort a pending request and potentially release any worklets or
  // processes it is keeping alive, so consumers should destroy these as soon as
  // they are no longer needed. Handles should not outlive the
  // AuctionWorkletManager.
  class CONTENT_EXPORT WorkletHandle : public base::CheckedObserver {
   public:
    WorkletHandle(const WorkletHandle&) = delete;
    WorkletHandle& operator=(const WorkletHandle&) = delete;
    ~WorkletHandle() override;

    // Authorizes subresource bundle subresource URLs that the worklet may
    // request as long as this WorkletHandle instance is live (refcounting
    // allows multiple WorkletHandle instances to authorize the same URLs).
    //
    // This must be called manually before the worklet is asked to do anything
    // involving fetching those subresources, but after the worklet is
    // available. Calls after the first one will be ignored.
    void AuthorizeSubresourceUrls(
        const SubresourceUrlBuilder& subresource_url_builder);

    // Retrieves the corresponding Worklet Mojo interface for the requested
    // worklet. Only the method corresponding to the worklet type `this` was
    // created with my be invoked. Once the worklet is created, will never
    // return null. Even in the case of process crash or load error, it will
    // return an interface with a broken pipe.
    //
    // Note that calls in the returned Mojo worklet cannot currently be
    // cancelled, so calls should always use weak pointers. Since there's no way
    // for pages to cancel auctions, anyways, and these are currently scoped
    // per-frame, supporting cancellation seems not useful (And the weak
    // pointers are probably not strictly necessary, since everything is torn
    // down before Mojo has a chance to invoke callbacks, but using weak
    // pointers still seems the safest thing to do).
    auction_worklet::mojom::BidderWorklet* GetBidderWorklet();
    auction_worklet::mojom::SellerWorklet* GetSellerWorklet();

    // Whether the trusted scoring signals URL is allowed. May only be called
    // when this is a seller worklet with a KVv2 trusted scoring signals URL,
    // and KVv2 signals and the KVv2 cache are enabled.
    bool TrustedScoringSignalsUrlAllowed() const;

    // Returns KVv2 trusted scoring signals public key if one is in used.
    // Must only be called after the worklet available callback has been called.
    const auction_worklet::mojom::TrustedSignalsPublicKey*
    GetTrustedSignalsPublicKey() const;

    // Returns a helper for assigning group-by-origin grouping IDs for the given
    // worklet.
    GroupByOriginKeyMapper& GetGroupByOriginKeyMapper();

    const SubresourceUrlAuthorizations&
    GetSubresourceUrlAuthorizationsForTesting();

    // Returns devtools IDs of all auctions that are using the worklet pointed
    // to by this handle.
    std::vector<std::string> GetDevtoolsAuctionIdsForTesting();

   private:
    friend class AuctionWorkletManager;
    friend class WorkletOwner;

    // These are only created by AuctionWorkletManager.
    WorkletHandle(std::string devtools_auction_id,
                  scoped_refptr<WorkletOwner> worklet_owner,
                  base::OnceClosure process_assigned_callback,
                  base::OnceClosure worklet_available_callback,
                  FatalErrorCallback fatal_error_callback);

    // These methods are invoked by WorkletOwner and call the corresponding
    // callback. OnProcessAssigned() must be invoked before either of the others
    // if there's a callback for it.
    void OnProcessAssigned();
    void OnWorkletAvailable();
    void OnFatalError(FatalErrorType type,
                      const std::vector<std::string>& errors);

    bool has_process_assignment_callback() const {
      return !process_assigned_callback_.is_null();
    }

    scoped_refptr<WorkletOwner> worklet_owner_;
    std::string devtools_auction_id_;

    base::OnceClosure process_assigned_callback_;
    base::OnceClosure worklet_available_callback_;
    FatalErrorCallback fatal_error_callback_;

    uint64_t seq_num_;
    bool authorized_subresources_ = false;
  };

  // `delegate` and `auction_process_manager` must outlive the created
  // AuctionWorkletManager.
  AuctionWorkletManager(AuctionProcessManager* auction_process_manager,
                        url::Origin top_window_origin,
                        url::Origin frame_origin,
                        Delegate* delegate);
  AuctionWorkletManager(const AuctionWorkletManager&) = delete;
  AuctionWorkletManager& operator=(const AuctionWorkletManager&) = delete;
  ~AuctionWorkletManager();

  // Computes the key for bidder worklet with given params.
  // RequestBidderWorklet(...) is RequestWorkletByKey(BidderWorkletKey(...))
  static WorkletKey BidderWorkletKey(
      const GURL& bidding_logic_url,
      const std::optional<GURL>& wasm_url,
      const std::optional<GURL>& trusted_bidding_signals_url,
      bool needs_cors_for_additional_bid,
      std::optional<uint16_t> experiment_group_id,
      const std::string& trusted_bidding_signals_slot_size_param,
      const std::optional<url::Origin>& trusted_bidding_signals_coordinator,
      const std::optional<std::string>& contextual_data);

  // Requests a worklet with the specified properties. The top frame origin and
  // debugging information are obtained from the Delegate's RenderFrameHost.
  //
  // `devtools_auction_id` will be used to related network events to given
  // auction. It serves no other purpose and does not affect worklet sharing.
  //
  // The AuctionWorkletManager will handle requesting a process, hooking up
  // DevTools, and merging requests with the same parameters so they can share a
  // single worklet.
  //
  // `process_assigned_callback`, if provided, will be invoked once a process
  // has been assigned. It's only a parameter when requesting seller worklets.
  // In the case of a cross-origin seller signals URL,
  // `worklet_available_callback` will only be invoked once it's known if
  // cross-origin signals are allowed, so having an additional callback allows
  // earlier notifications so that component auctions can start loading the
  // top-level seller worklet earlier. It will always be invoked before the
  // `worklet_available_callback`. During the callback, like all other
  // callbacks, it's safe to call back into the AuctionWorkletManager to request
  // a worklet, or to destroy an existing one. `process_assigned_callback` will
  // always be invoked before either of the other two callbacks.
  //
  // Will invoke `worklet_available_callback` when the service pointer can
  // be retrieved from `handle`. Multiple instances of the callback can be
  // invoked synchronously right after each other, but none will be invoked from
  // within the Request... call itself. `worklet_available_callback` invocations
  // that refer to the same underlying worklet will be in the same order as the
  // Request... call, but those that don't share worklets are unordered with
  // respect to each other.
  //
  // `fatal_error_callback` is invoked in the case of a fatal error. It may be
  // invoked both if the worklet creation outright failed, or after a successful
  // creation that invoked `worklet_available_callback_`, so long as the
  // WorkletHandle lives. It is called to indicate the worklet failed to
  // load or crashed.  Callbacks from multiple calls may be invoked right after
  // another without returning to the event loop (but not within the Request...
  // call itself); but unlike for success callback there are no ordering
  // guarantees about ordering of failures corresponding to different Request...
  // calls.
  //
  // The callbacks should not delete the AuctionWorkletManager itself, but are
  // free to release any WorkletHandle they wish.
  void RequestBidderWorklet(
      std::string devtools_auction_id,
      const GURL& bidding_logic_url,
      const std::optional<GURL>& wasm_url,
      const std::optional<GURL>& trusted_bidding_signals_url,
      bool needs_cors_for_additional_bid,
      std::optional<uint16_t> experiment_group_id,
      const std::string& trusted_bidding_signals_slot_size_param,
      const std::optional<url::Origin>& trusted_bidding_signals_coordinator,
      const std::optional<std::string>& contextual_data,
      base::OnceClosure worklet_available_callback,
      FatalErrorCallback fatal_error_callback,
      std::unique_ptr<WorkletHandle>& out_worklet_handle,
      AuctionMetricsRecorder* auction_metrics_recorder);
  void RequestSellerWorklet(
      std::string devtools_auction_id,
      const GURL& decision_logic_url,
      const std::optional<GURL>& trusted_scoring_signals_url,
      std::optional<uint16_t> experiment_group_id,
      const std::optional<url::Origin>& trusted_scoring_signals_coordinator,
      std::optional<bool> send_creative_scanning_metadata,
      base::OnceClosure process_assigned_callback,
      base::OnceClosure worklet_available_callback,
      FatalErrorCallback fatal_error_callback,
      std::unique_ptr<WorkletHandle>& out_worklet_handle,
      AuctionMetricsRecorder* auction_metrics_recorder);

  // Requests a worklet with the specified `worklet_info`. This method handles
  // the creation of a new worklet if no existing instance matches the specified
  // `worklet_info`.
  //
  // If a new bidder worklet ends up being created, `number_of_bidder_threads`
  // specifies the number of threads to allocate to the bidder.
  void RequestWorkletByKey(WorkletKey worklet_info,
                           std::string devtools_auction_id,
                           base::OnceClosure process_assigned_callback,
                           base::OnceClosure worklet_available_callback,
                           FatalErrorCallback fatal_error_callback,
                           std::unique_ptr<WorkletHandle>& out_worklet_handle,
                           size_t number_of_bidder_threads,
                           AuctionMetricsRecorder* auction_metrics_recorder,
                           std::optional<uint64_t> trace_id);

  // Start an anticipatory process for an origin if we have not yet
  // done so and are able.
  //
  // Refer to AuctionProcessManager::MaybeStartAnticipatoryProcess
  // for more details.
  void MaybeStartAnticipatoryProcess(const url::Origin& origin,
                                     WorkletType worklet_type);

 private:
  void OnWorkletNoLongerUsable(WorkletOwner* worklet);

  mojo::PendingRemote<auction_worklet::mojom::AuctionSharedStorageHost>
  MaybeBindAuctionSharedStorageHost(RenderFrameHostImpl* auction_runner_rfh,
                                    const url::Origin& worklet_origin);

  // Accessors used by inner classes. Not strictly needed, but makes it clear
  // which fields they can access.
  AuctionProcessManager* auction_process_manager() {
    return auction_process_manager_;
  }
  const url::Origin& top_window_origin() const { return top_window_origin_; }
  const url::Origin& frame_origin() const { return frame_origin_; }
  Delegate* delegate() { return delegate_; }

  const raw_ptr<AuctionProcessManager> auction_process_manager_;
  const url::Origin top_window_origin_;
  const url::Origin frame_origin_;
  raw_ptr<Delegate> const delegate_;

  std::unique_ptr<AuctionNetworkEventsProxy> auction_network_events_proxy_;
  std::unique_ptr<AuctionSharedStorageHost> auction_shared_storage_host_;

  std::map<WorkletKey, raw_ptr<WorkletOwner, CtnExperimental>> worklets_;
};

}  // namespace content

#endif  // CONTENT_BROWSER_INTEREST_GROUP_AUCTION_WORKLET_MANAGER_H_