910e62b5创建于 1月15日历史提交
// Copyright 2024 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_SERVICES_AUCTION_WORKLET_TRUSTED_SIGNALS_KVV2_MANAGER_H_
#define CONTENT_SERVICES_AUCTION_WORKLET_TRUSTED_SIGNALS_KVV2_MANAGER_H_

#include <map>
#include <memory>

#include "base/memory/raw_ptr.h"
#include "base/memory/scoped_refptr.h"
#include "base/memory/weak_ptr.h"
#include "base/types/expected.h"
#include "base/types/optional_ref.h"
#include "components/cbor/values.h"
#include "content/common/content_export.h"
#include "content/services/auction_worklet/public/mojom/trusted_signals_cache.mojom.h"
#include "content/services/auction_worklet/trusted_signals.h"
#include "content/services/auction_worklet/trusted_signals_kvv2_helper.h"
#include "content/services/auction_worklet/trusted_signals_request_manager.h"
#include "mojo/public/cpp/base/big_buffer.h"
#include "mojo/public/cpp/bindings/pending_remote.h"
#include "mojo/public/cpp/bindings/receiver_set.h"
#include "mojo/public/cpp/bindings/remote.h"
#include "url/origin.h"

namespace auction_worklet {

class AuctionV8Helper;

// Class to manage and parse requests for trusted key-value server version 2
// requests from the browser-side TrustedSignalsCache. Each instance is scoped
// to an AuctionWorkletService, and may be used to fetch results that come from
// different trusted signals URLs, and a single instance may be used by multiple
// BidderWorklets/SellerWorklets.
//
// Callers request signals using a `compression_group_token` provided by the
// browser process. This class merges requests for the same compression group.
// Once the requested compression group has been provided by the browser process
// as a compressed CBOR string, this class decompresses and parses it. It then
// distributes the data to all consumers that requested it, and keeps the parsed
// data cached as long as there's any live consumer of the data, handing it out
// to satisfy new requests.
//
// All fields of all partitions in all requested compression groups are
// currently parsed up front, rather than on first use, so they can safely be
// reused on different V8 threads.
//
// TODO(crbug.com/365957549): Implement some way to not have to parse an entire
// compression group when only limited data is needed from it.
class CONTENT_EXPORT TrustedSignalsKVv2Manager
    : public mojom::TrustedSignalsCacheClient {
 public:
  using Result = TrustedSignals::Result;
  using SignalsType = TrustedSignalsKVv2ResponseParser::SignalsType;
  using PartitionMapOrError =
      TrustedSignalsKVv2ResponseParser::PartitionMapOrError;

  using ResultOrError = base::expected<scoped_refptr<Result>, std::string>;

  // Use common callback type to make it easy to use both this and a
  // TrustedSignalsRequestManager. Once TrustedSignalsRequestManager has been
  // removed, may make sense to switch to a callback that takes a ResultOrError
  // instead.
  using LoadSignalsCallback = TrustedSignalsRequestManager::LoadSignalsCallback;

  // Represents a single pending request for TrustedSignals from a consumer.
  // Destroying it cancels the request. All live Requests must be destroyed
  // before the TrustedSignalsKVv2Manager.
  class Request {
   public:
    Request(Request&) = delete;
    Request& operator=(Request&) = delete;
    virtual ~Request() = default;

   protected:
    Request() = default;
  };

  TrustedSignalsKVv2Manager(
      mojo::PendingRemote<mojom::TrustedSignalsCache> trusted_signals_cache,
      scoped_refptr<AuctionV8Helper> v8_helper);
  TrustedSignalsKVv2Manager(TrustedSignalsKVv2Manager&) = delete;

  ~TrustedSignalsKVv2Manager() override;

  TrustedSignalsKVv2Manager& operator=(TrustedSignalsKVv2Manager&) = delete;

  // Requests signals from the cache with `compression_group_token`, and parses
  // the `partition_id` partition as `signals_type` signals. The signals for the
  // specified partition will asynchronously be passed back to the passed in
  // callback.
  std::unique_ptr<Request> RequestSignals(
      SignalsType signals_type,
      base::UnguessableToken compression_group_token,
      int partition_id,
      LoadSignalsCallback load_signals_callback);

 private:
  // Private implementation of Request.
  class RequestImpl;

  // Tracks the data for a particular compression group, and all pending
  // Requests associated with it. Created when the first request with a new
  // UnguessableToken is received, at which point, the data is requested, and
  // destroyed when all Requests associated with it have been destroyed. Once
  // the data is received and has been parsed, it's still kept alive to
  // distribute the parsed data (or error) to new incoming requests for the same
  // compression group.
  struct CompressionGroup;

  // Map of the IDs identifying each compression group to each CompressionGroup.
  using CompressionGroupMap =
      std::map<base::UnguessableToken, CompressionGroup>;

  // A map of partition IDs to the result of parsing each partition.
  using PartitionMap = std::map<int, scoped_refptr<Result>>;

  // mojom::TrustedSignalsCacheClient implementation:
  void OnSuccess(mojom::TrustedSignalsCompressionScheme compression_scheme,
                 mojo_base::BigBuffer compression_group_data) override;
  void OnError(const std::string& error_message) override;

  // Called by OnComplete() once parsing has completed. Distributed the result
  // to all waiting requests, and stores it for future incoming requests.
  void OnComplete(base::UnguessableToken compression_group_token,
                  PartitionMapOrError parsed_compression_group_result);

  // Closes the Mojo pipe in `compression_group_pipes_` associated with the
  // specified CompressionGroup and clears the compression group's
  // mojo::ReceiverId. The passed in CompressionGroup must have a live pipe.
  void ClosePipe(CompressionGroupMap::iterator compression_group_it);

  // Called by RequestImpl when it's destroyed. Removes association between the
  // request and the CompressionGroup, destroying the group if it has no more
  // associated requests.
  void OnRequestDestroyed(RequestImpl* request,
                          CompressionGroupMap::iterator compression_group_it);

  // Retrieves the result for `partition_id` from `compression_group`. Even if
  // the compression group was fetched and parsed successfully, may return an
  // error if the partition is missing from the group.
  static ResultOrError GetResultForPartition(
      const CompressionGroup& compression_group,
      int partition_id);

  // Map of compression group IDs to CompressionGroups. A compression group is
  // destroyed when all Requests associated with it have been destroyed.
  CompressionGroupMap compression_groups_;

  mojo::Remote<mojom::TrustedSignalsCache> trusted_signals_cache_;

  // Set of receiver pipes associated with each CompressionGroup that's waiting
  // on data.
  mojo::ReceiverSet<mojom::TrustedSignalsCacheClient,
                    CompressionGroupMap::iterator>
      compression_group_pipes_;

  const scoped_refptr<AuctionV8Helper> v8_helper_;

  base::WeakPtrFactory<TrustedSignalsKVv2Manager> weak_ptr_factory_{this};
};

}  // namespace auction_worklet

#endif  // CONTENT_SERVICES_AUCTION_WORKLET_TRUSTED_SIGNALS_KVV2_MANAGER_H_