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

#include <stddef.h>
#include <stdint.h>

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

#include "base/functional/callback.h"
#include "base/memory/raw_ref.h"
#include "base/numerics/safe_conversions.h"
#include "base/time/time.h"
#include "base/timer/elapsed_timer.h"
#include "content/browser/aggregation_service/aggregatable_report.h"
#include "content/browser/private_aggregation/private_aggregation_budget_key.h"
#include "content/browser/private_aggregation/private_aggregation_budgeter.h"
#include "content/browser/private_aggregation/private_aggregation_caller_api.h"
#include "content/browser/private_aggregation/private_aggregation_pending_contributions.h"
#include "content/common/content_export.h"
#include "mojo/public/cpp/bindings/pending_receiver.h"
#include "mojo/public/cpp/bindings/receiver_set.h"
#include "third_party/blink/public/common/shared_storage/shared_storage_utils.h"
#include "third_party/blink/public/mojom/private_aggregation/private_aggregation_host.mojom.h"

namespace base {
class ElapsedTimer;
class Uuid;
}  // namespace base
namespace url {
class Origin;
}  // namespace url

namespace content {

class AggregatableReportRequest;
class BrowserContext;

// UI thread class responsible for implementing the mojo interface used by
// worklets and renderers to request reports be sent and maintaining the
// receiver set for this interface. It is responsible for validating the
// messages received and then passing them on for budget approval.
class CONTENT_EXPORT PrivateAggregationHost
    : public blink::mojom::PrivateAggregationHost {
 public:
  // These values are persisted to logs. Entries should not be renumbered and
  // numeric values should never be reused.
  enum class PipeResult {
    kReportSuccess = 0,
    kReportSuccessButTruncatedDueToTooManyContributions = 1,
    kNoReportButNoError = 2,
    kApiDisabledInSettings = 3,
    kEnableDebugModeCalledMultipleTimes = 4,
    kNegativeValue = 5,
    kFilteringIdInvalid = 6,
    kNecessaryFeatureNotEnabled = 7,
    kMaxValue = kNecessaryFeatureNotEnabled,
  };

  // These values are persisted to logs. Entries should not be renumbered and
  // numeric values should never be reused.
  enum class TimeoutResult {
    kOccurredBeforeRemoteDisconnection = 0,
    kOccurredAfterRemoteDisconnection = 1,
    kCanceledDueToError = 2,
    kStillScheduledOnShutdown = 3,

    kMaxValue = kStillScheduledOnShutdown,
  };

  // These values are persisted to logs. Entries should not be renumbered and
  // numeric values should never be reused.
  enum class FilteringIdStatus {
    kNoFilteringIdWithDefaultMaxBytes = 0,
    kFilteringIdProvidedWithDefaultMaxBytes = 1,
    kNoFilteringIdWithCustomMaxBytes = 2,
    kFilteringIdProvidedWithCustomMaxBytes = 3,

    kMaxValue = kFilteringIdProvidedWithCustomMaxBytes,
  };

  // Indicates the desired behavior when a report has no contributions, such as
  // when budget is denied or `contributeToHistogram()` was not called.
  enum class NullReportBehavior {
    // Still send a report, but without any contributions.
    kSendNullReport,

    // Drop the report.
    kDontSendReport,
  };

  using ReportRequestGenerator = base::OnceCallback<AggregatableReportRequest(
      std::vector<blink::mojom::AggregatableReportHistogramContribution>)>;

  // Version string for the reports generated by this API.
  static constexpr char kApiReportVersion[] = "1.0";

  static constexpr size_t kDefaultFilteringIdMaxBytes = 1;

  // The maximum allowed context_id string length.
  static constexpr int kMaxContextIdLength = 64;
  static_assert(kMaxContextIdLength ==
                    blink::kPrivateAggregationApiContextIdMaxLength,
                "Maximum length of context_id should be aligned between Shared "
                "Storage and Private Aggregation.");

  // The duration of time that `SendReportOnTimeoutOrDisconnect()`
  // unconditionally adds to the scheduled report time. Marked public for
  // testing.
  static constexpr base::TimeDelta kTimeForLocalProcessing =
      base::Milliseconds(100);

  // Returns the effective maximum number of contributions that can go in an
  // `AggregatableReport` after merging. The `requested_max_contributions`
  // parameter comes from the web-visible `maxContributions` field.
  //
  // This method is marked public for testing; this enables golden report
  // unittests to match the browser's actual behavior.
  static base::StrictNumeric<size_t> GetEffectiveMaxContributions(
      PrivateAggregationCallerApi caller_api,
      std::optional<size_t> requested_max_contributions);

  // `on_report_request_details_received` and `browser_context` must be
  // non-null.
  PrivateAggregationHost(
      base::RepeatingCallback<
          void(ReportRequestGenerator,
               PrivateAggregationPendingContributions::Wrapper,
               PrivateAggregationBudgetKey,
               NullReportBehavior)> on_report_request_details_received,
      BrowserContext* browser_context);
  PrivateAggregationHost(const PrivateAggregationHost&) = delete;
  PrivateAggregationHost& operator=(const PrivateAggregationHost&) = delete;
  ~PrivateAggregationHost() override;

  // See `PrivateAggregationManager::BindNewReceiver()`.
  [[nodiscard]] virtual bool BindNewReceiver(
      url::Origin worklet_origin,
      url::Origin top_frame_origin,
      PrivateAggregationCallerApi caller_api,
      std::optional<std::string> context_id,
      std::optional<base::TimeDelta> timeout,
      std::optional<url::Origin> aggregation_coordinator_origin,
      size_t filtering_id_max_bytes,
      std::optional<size_t> max_contributions,
      mojo::PendingReceiver<blink::mojom::PrivateAggregationHost>
          pending_receiver);

  bool IsDebugModeAllowed(const url::Origin& top_frame_origin,
                          const url::Origin& reporting_origin);

  // blink::mojom::PrivateAggregationHost:
  void ContributeToHistogram(
      std::vector<blink::mojom::AggregatableReportHistogramContributionPtr>
          contribution_ptrs) override;
  void ContributeToHistogramOnEvent(
      blink::mojom::PrivateAggregationErrorEvent error_event,
      std::vector<blink::mojom::AggregatableReportHistogramContributionPtr>
          contribution_ptrs) override;
  void EnableDebugMode(blink::mojom::DebugKeyPtr debug_key) override;

  void FlushReceiverSetForTesting() { receiver_set_.FlushForTesting(); }

 private:
  friend class PrivateAggregationReportGoldenLatestVersionTest;

  struct ReceiverContext;

  static AggregatableReportRequest GenerateReportRequest(
      base::ElapsedTimer timeout_or_disconnect_timer,
      blink::mojom::DebugModeDetailsPtr debug_mode_details,
      base::Time scheduled_report_time,
      AggregatableReportRequest::DelayType delay_type,
      base::Uuid report_id,
      const url::Origin& reporting_origin,
      PrivateAggregationCallerApi caller_api,
      std::optional<std::string> context_id,
      std::optional<url::Origin> aggregation_coordinator_origin,
      size_t specified_filtering_id_max_bytes,
      size_t max_contributions,
      std::vector<blink::mojom::AggregatableReportHistogramContribution>
          contributions);

  void CloseCurrentPipe(PipeResult pipe_result);

  void OnTimeoutBeforeDisconnect(mojo::ReceiverId id);

  void OnReceiverDisconnected();

  void SendReportOnTimeoutOrDisconnect(
      ReceiverContext& current_context,
      base::TimeDelta remaining_timeout,
      PrivateAggregationPendingContributions::TimeoutOrDisconnect
          timeout_or_disconnect);

  // Performs shared validation for `ContributeToHistogram()` and
  // `ContributeToHistogramOnEvent()` calls.
  bool ValidateContributeCall(
      const std::vector<
          blink::mojom::AggregatableReportHistogramContributionPtr>&
          contribution_ptrs);

  // Set iff the private aggregation developer mode is set.
  bool should_not_delay_reports_;

  base::RepeatingCallback<void(ReportRequestGenerator,
                               PrivateAggregationPendingContributions::Wrapper,
                               PrivateAggregationBudgetKey,
                               NullReportBehavior)>
      on_report_request_details_received_;

  mojo::ReceiverSet<blink::mojom::PrivateAggregationHost, ReceiverContext>
      receiver_set_;

  // `this` is indirectly owned by the StoragePartitionImpl, which itself is
  // owned by `browser_context_`.
  raw_ref<BrowserContext> browser_context_;
};

}  // namespace content

#endif  // CONTENT_BROWSER_PRIVATE_AGGREGATION_PRIVATE_AGGREGATION_HOST_H_