blob: be437c879cfefec292a6080ab7d21dbc79dd9fd8 [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 <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_