blob: 51ffea3ffed5cfa87e9d36f98d4ec7901f411be1 [file] [log] [blame]
// 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 <optional>
#include <string>
#include <vector>
#include "base/functional/callback.h"
#include "base/memory/raw_ref.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/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,
kMaxValue = kFilteringIdInvalid,
};
// 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,
};
using ReportRequestGenerator = base::OnceCallback<AggregatableReportRequest(
std::vector<blink::mojom::AggregatableReportHistogramContribution>)>;
// Version string for the reports generated by this API. Varies based on
// whether the filtering ID feature is enabled. See crbug.com/330744610.
static constexpr char kApiReportVersionWithoutFilteringId[] = "0.1";
static constexpr char kApiReportVersionWithFilteringId[] = "1.0";
// The maximum number of contributions that can go in an `AggregatableReport`,
// after merging. Aligns with
// `attribution_reporting::kMaxAggregationKeysPerSource`.
static constexpr size_t kMaxNumberOfContributions = 20;
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);
// `on_report_request_details_received` and `browser_context` must be
// non-null.
PrivateAggregationHost(
base::RepeatingCallback<void(
ReportRequestGenerator,
std::vector<blink::mojom::AggregatableReportHistogramContribution>,
PrivateAggregationBudgetKey,
PrivateAggregationBudgeter::BudgetDeniedBehavior)>
on_report_request_details_received,
BrowserContext* browser_context);
PrivateAggregationHost(const PrivateAggregationHost&) = delete;
PrivateAggregationHost& operator=(const PrivateAggregationHost&) = delete;
~PrivateAggregationHost() override;
// Binds a new pending receiver for a worklet, allowing messages to be sent
// and processed. However, the receiver is not bound if the `worklet_origin`
// is not potentially trustworthy or if `context_id` is too long. If
// `timeout` is set, the report will be sent as if the pipe closed after the
// timeout, regardless of when the disconnection actually happens. `timeout`
// must be positive if set. If `timeout` is set, `context_id` must be set too.
// If `aggregation_coordinator_origin` is set, the origin must be on the
// allowlist. `filtering_id_max_bytes` must be positive and no greater than
// `AggregationServicePayloadContents::kMaximumFilteringIdMaxBytes`. The
// return value indicates whether the receiver was accepted. Virtual for
// testing.
[[nodiscard]] virtual bool BindNewReceiver(
url::Origin worklet_origin,
url::Origin top_frame_origin,
PrivateAggregationBudgetKey::Api api_for_budgeting,
std::optional<std::string> context_id,
std::optional<base::TimeDelta> timeout,
std::optional<url::Origin> aggregation_coordinator_origin,
size_t filtering_id_max_bytes,
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 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,
PrivateAggregationBudgetKey::Api api_for_budgeting,
std::optional<std::string> context_id,
std::optional<url::Origin> aggregation_coordinator_origin,
size_t specified_filtering_id_max_bytes,
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);
// Set iff the private aggregation developer mode is set.
bool should_not_delay_reports_;
base::RepeatingCallback<void(
ReportRequestGenerator,
std::vector<blink::mojom::AggregatableReportHistogramContribution>,
PrivateAggregationBudgetKey,
PrivateAggregationBudgeter::BudgetDeniedBehavior)>
on_report_request_details_received_;
mojo::ReceiverSet<blink::mojom::PrivateAggregationHost, ReceiverContext>
receiver_set_;
// The number of open pipes where a timeout was specified.
int pipes_with_timeout_count_ = 0;
// `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_