| // Copyright 2020 The Chromium Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "content/browser/attribution_reporting/attribution_test_utils.h" |
| |
| #include <stdint.h> |
| |
| #include <algorithm> |
| #include <optional> |
| #include <tuple> |
| #include <utility> |
| #include <variant> |
| #include <vector> |
| |
| #include "base/check.h" |
| #include "base/strings/string_number_conversions.h" |
| #include "base/time/time.h" |
| #include "components/attribution_reporting/aggregatable_debug_reporting_config.h" |
| #include "components/attribution_reporting/aggregatable_dedup_key.h" |
| #include "components/attribution_reporting/aggregatable_filtering_id_max_bytes.h" |
| #include "components/attribution_reporting/aggregatable_named_budget_candidate.h" |
| #include "components/attribution_reporting/aggregatable_named_budget_defs.h" |
| #include "components/attribution_reporting/aggregatable_trigger_config.h" |
| #include "components/attribution_reporting/aggregatable_trigger_data.h" |
| #include "components/attribution_reporting/aggregatable_values.h" |
| #include "components/attribution_reporting/attribution_scopes_data.h" |
| #include "components/attribution_reporting/constants.h" |
| #include "components/attribution_reporting/destination_set.h" |
| #include "components/attribution_reporting/event_report_windows.h" |
| #include "components/attribution_reporting/event_trigger_data.h" |
| #include "components/attribution_reporting/filters.h" |
| #include "components/attribution_reporting/max_event_level_reports.h" |
| #include "components/attribution_reporting/os_registration.h" |
| #include "components/attribution_reporting/source_type.mojom.h" |
| #include "components/attribution_reporting/suitable_origin.h" |
| #include "components/attribution_reporting/test_utils.h" |
| #include "components/attribution_reporting/trigger_config.h" |
| #include "components/attribution_reporting/trigger_data_matching.mojom-forward.h" |
| #include "components/attribution_reporting/trigger_registration.h" |
| #include "content/browser/attribution_reporting/aggregatable_attribution_utils.h" |
| #include "content/browser/attribution_reporting/aggregatable_named_budget_pair.h" |
| #include "content/browser/attribution_reporting/attribution_observer.h" |
| #include "content/browser/attribution_reporting/attribution_reporting.mojom.h" |
| #include "content/browser/attribution_reporting/attribution_trigger.h" |
| #include "content/browser/attribution_reporting/attribution_utils.h" |
| #include "content/browser/attribution_reporting/os_registration.h" |
| #include "content/browser/attribution_reporting/rate_limit_result.h" |
| #include "content/browser/attribution_reporting/send_result.h" |
| #include "content/browser/attribution_reporting/stored_source.h" |
| #include "content/public/browser/attribution_data_model.h" |
| #include "net/base/net_errors.h" |
| #include "net/base/schemeful_site.h" |
| #include "net/http/structured_headers.h" |
| #include "services/metrics/public/cpp/ukm_source_id.h" |
| #include "testing/gtest/include/gtest/gtest.h" |
| #include "third_party/abseil-cpp/absl/functional/overload.h" |
| #include "third_party/abseil-cpp/absl/numeric/int128.h" |
| #include "url/gurl.h" |
| #include "url/origin.h" |
| |
| namespace content { |
| |
| namespace { |
| |
| using ::attribution_reporting::FilterPair; |
| using ::attribution_reporting::OsRegistrationItem; |
| using ::attribution_reporting::SuitableOrigin; |
| using ::attribution_reporting::mojom::SourceType; |
| |
| const char kDefaultSourceOrigin[] = "https://impression.test/"; |
| const char kDefaultDestinationOrigin[] = "https://sub.conversion.test/"; |
| const char kDefaultReportOrigin[] = "https://report.test/"; |
| |
| } // namespace |
| |
| base::Uuid DefaultExternalReportID() { |
| return base::Uuid::ParseLowercase("21abd97f-73e8-4b88-9389-a9fee6abda5e"); |
| } |
| |
| // Builds an impression with default values. This is done as a builder because |
| // all values needed to be provided at construction time. |
| SourceBuilder::SourceBuilder(base::Time time) |
| : source_time_(time), |
| source_origin_(*SuitableOrigin::Deserialize(kDefaultSourceOrigin)), |
| registration_(*attribution_reporting::DestinationSet::Create( |
| {net::SchemefulSite::Deserialize(kDefaultDestinationOrigin)})), |
| reporting_origin_(*SuitableOrigin::Deserialize(kDefaultReportOrigin)) { |
| registration_.source_event_id = 123; |
| registration_.trigger_data = |
| attribution_reporting::TriggerDataSet(source_type_); |
| registration_.max_event_level_reports = |
| attribution_reporting::MaxEventLevelReports::Max(); |
| } |
| |
| SourceBuilder::~SourceBuilder() = default; |
| |
| SourceBuilder::SourceBuilder(const SourceBuilder&) = default; |
| |
| SourceBuilder::SourceBuilder(SourceBuilder&&) = default; |
| |
| SourceBuilder& SourceBuilder::operator=(const SourceBuilder&) = default; |
| |
| SourceBuilder& SourceBuilder::operator=(SourceBuilder&&) = default; |
| |
| SourceBuilder& SourceBuilder::SetExpiry(base::TimeDelta delta) { |
| registration_.expiry = delta; |
| return *this; |
| } |
| |
| SourceBuilder& SourceBuilder::SetAggregatableReportWindow( |
| base::TimeDelta delta) { |
| registration_.aggregatable_report_window = delta; |
| return *this; |
| } |
| |
| SourceBuilder& SourceBuilder::SetSourceEventId(uint64_t source_event_id) { |
| registration_.source_event_id = source_event_id; |
| return *this; |
| } |
| |
| SourceBuilder& SourceBuilder::SetSourceOrigin(SuitableOrigin origin) { |
| source_origin_ = std::move(origin); |
| return *this; |
| } |
| |
| SourceBuilder& SourceBuilder::SetDestinationSites( |
| base::flat_set<net::SchemefulSite> sites) { |
| registration_.destination_set = |
| *attribution_reporting::DestinationSet::Create(std::move(sites)); |
| return *this; |
| } |
| |
| SourceBuilder& SourceBuilder::SetReportingOrigin(SuitableOrigin origin) { |
| reporting_origin_ = std::move(origin); |
| return *this; |
| } |
| |
| SourceBuilder& SourceBuilder::SetSourceType(SourceType source_type) { |
| source_type_ = source_type; |
| registration_.trigger_data = |
| attribution_reporting::TriggerDataSet(source_type_); |
| registration_.max_event_level_reports = |
| attribution_reporting::MaxEventLevelReports(source_type); |
| return *this; |
| } |
| |
| SourceBuilder& SourceBuilder::SetPriority(int64_t priority) { |
| registration_.priority = priority; |
| return *this; |
| } |
| |
| SourceBuilder& SourceBuilder::SetFilterData( |
| attribution_reporting::FilterData filter_data) { |
| registration_.filter_data = std::move(filter_data); |
| return *this; |
| } |
| |
| SourceBuilder& SourceBuilder::SetDebugKey(std::optional<uint64_t> debug_key) { |
| registration_.debug_key = debug_key; |
| return *this; |
| } |
| |
| SourceBuilder& SourceBuilder::SetAttributionLogic( |
| StoredSource::AttributionLogic attribution_logic) { |
| attribution_logic_ = attribution_logic; |
| return *this; |
| } |
| |
| SourceBuilder& SourceBuilder::SetActiveState( |
| StoredSource::ActiveState active_state) { |
| active_state_ = active_state; |
| return *this; |
| } |
| |
| SourceBuilder& SourceBuilder::SetSourceId(StoredSource::Id source_id) { |
| source_id_ = source_id; |
| return *this; |
| } |
| |
| SourceBuilder& SourceBuilder::SetDedupKeys(std::vector<uint64_t> dedup_keys) { |
| dedup_keys_ = std::move(dedup_keys); |
| return *this; |
| } |
| |
| SourceBuilder& SourceBuilder::SetAggregationKeys( |
| attribution_reporting::AggregationKeys aggregation_keys) { |
| registration_.aggregation_keys = std::move(aggregation_keys); |
| return *this; |
| } |
| |
| SourceBuilder& SourceBuilder::SetRemainingAggregatableAttributionBudget( |
| int remaining_aggregatable_attribution_budget) { |
| remaining_aggregatable_attribution_budget_ = |
| remaining_aggregatable_attribution_budget; |
| return *this; |
| } |
| |
| SourceBuilder& SourceBuilder::SetRemainingAggregatableDebugBudget( |
| int remaining_aggregatable_debug_budget) { |
| remaining_aggregatable_debug_budget_ = remaining_aggregatable_debug_budget; |
| return *this; |
| } |
| |
| SourceBuilder& SourceBuilder::SetRandomizedResponseRate( |
| double randomized_response_rate) { |
| randomized_response_rate_ = randomized_response_rate; |
| return *this; |
| } |
| |
| SourceBuilder& SourceBuilder::SetAggregatableDedupKeys( |
| std::vector<uint64_t> dedup_keys) { |
| aggregatable_dedup_keys_ = std::move(dedup_keys); |
| return *this; |
| } |
| |
| SourceBuilder& SourceBuilder::SetIsWithinFencedFrame( |
| bool is_within_fenced_frame) { |
| is_within_fenced_frame_ = is_within_fenced_frame; |
| return *this; |
| } |
| |
| SourceBuilder& SourceBuilder::SetDebugReporting(bool debug_reporting) { |
| registration_.debug_reporting = debug_reporting; |
| return *this; |
| } |
| |
| SourceBuilder& SourceBuilder::SetTriggerData( |
| attribution_reporting::TriggerDataSet trigger_data) { |
| registration_.trigger_data = std::move(trigger_data); |
| return *this; |
| } |
| |
| SourceBuilder& SourceBuilder::SetEventReportWindows( |
| attribution_reporting::EventReportWindows event_report_windows) { |
| registration_.event_report_windows = std::move(event_report_windows); |
| return *this; |
| } |
| |
| SourceBuilder& SourceBuilder::SetMaxEventLevelReports( |
| int max_event_level_reports) { |
| registration_.max_event_level_reports = |
| attribution_reporting::MaxEventLevelReports(max_event_level_reports); |
| return *this; |
| } |
| |
| SourceBuilder& SourceBuilder::SetTriggerDataMatching( |
| attribution_reporting::mojom::TriggerDataMatching trigger_data_matching) { |
| registration_.trigger_data_matching = trigger_data_matching; |
| return *this; |
| } |
| |
| SourceBuilder& SourceBuilder::SetCookieBasedDebugAllowed( |
| bool cookie_based_debug_allowed) { |
| cookie_based_debug_allowed_ = cookie_based_debug_allowed; |
| return *this; |
| } |
| |
| SourceBuilder& SourceBuilder::SetAggregatableDebugReportingConfig( |
| attribution_reporting::SourceAggregatableDebugReportingConfig |
| aggregatable_debug_reporting_config) { |
| registration_.aggregatable_debug_reporting_config = |
| std::move(aggregatable_debug_reporting_config); |
| return *this; |
| } |
| |
| SourceBuilder& SourceBuilder::SetDestinationLimitPriority(int64_t priority) { |
| registration_.destination_limit_priority = priority; |
| return *this; |
| } |
| |
| SourceBuilder& SourceBuilder::SetAttributionScopesData( |
| attribution_reporting::AttributionScopesData attribution_scopes) { |
| registration_.attribution_scopes_data = std::move(attribution_scopes); |
| return *this; |
| } |
| |
| SourceBuilder& SourceBuilder::SetAggregatableNamedBudgetDefs( |
| attribution_reporting::AggregatableNamedBudgetDefs budgets) { |
| registration_.aggregatable_named_budget_defs = std::move(budgets); |
| return *this; |
| } |
| |
| StorableSource SourceBuilder::Build() const { |
| StorableSource source(reporting_origin_, registration_, source_origin_, |
| source_type_, is_within_fenced_frame_, |
| ukm::kInvalidSourceId); |
| source.set_cookie_based_debug_allowed(cookie_based_debug_allowed_); |
| return source; |
| } |
| |
| StoredSource SourceBuilder::BuildStored() const { |
| base::Time expiry_time = source_time_ + registration_.expiry; |
| StoredSource::AggregatableNamedBudgets named_budgets = |
| ConvertNamedBudgetsMap(registration_.aggregatable_named_budget_defs); |
| StoredSource source = *StoredSource::Create( |
| CommonSourceInfo(source_origin_, reporting_origin_, source_type_, |
| cookie_based_debug_allowed_), |
| registration_.source_event_id, registration_.destination_set, |
| source_time_, expiry_time, registration_.trigger_data, |
| registration_.event_report_windows, registration_.max_event_level_reports, |
| source_time_ + registration_.aggregatable_report_window, |
| registration_.priority, registration_.filter_data, |
| registration_.debug_key, registration_.aggregation_keys, |
| attribution_logic_, active_state_, source_id_, |
| remaining_aggregatable_attribution_budget_, randomized_response_rate_, |
| registration_.trigger_data_matching, registration_.event_level_epsilon, |
| registration_.aggregatable_debug_reporting_config.config().key_piece, |
| remaining_aggregatable_debug_budget_, |
| registration_.attribution_scopes_data, named_budgets); |
| source.dedup_keys() = dedup_keys_; |
| source.aggregatable_dedup_keys() = aggregatable_dedup_keys_; |
| return source; |
| } |
| |
| AttributionTrigger DefaultTrigger() { |
| return TriggerBuilder().Build(); |
| } |
| |
| TriggerBuilder::TriggerBuilder() |
| : destination_origin_( |
| *SuitableOrigin::Deserialize(kDefaultDestinationOrigin)), |
| reporting_origin_(*SuitableOrigin::Deserialize(kDefaultReportOrigin)) {} |
| |
| TriggerBuilder::~TriggerBuilder() = default; |
| |
| TriggerBuilder::TriggerBuilder(const TriggerBuilder&) = default; |
| |
| TriggerBuilder::TriggerBuilder(TriggerBuilder&&) = default; |
| |
| TriggerBuilder& TriggerBuilder::operator=(const TriggerBuilder&) = default; |
| |
| TriggerBuilder& TriggerBuilder::operator=(TriggerBuilder&&) = default; |
| |
| TriggerBuilder& TriggerBuilder::SetTriggerData(uint64_t trigger_data) { |
| trigger_data_ = trigger_data; |
| return *this; |
| } |
| |
| TriggerBuilder& TriggerBuilder::SetDestinationOrigin(SuitableOrigin origin) { |
| destination_origin_ = std::move(origin); |
| return *this; |
| } |
| |
| TriggerBuilder& TriggerBuilder::SetReportingOrigin(SuitableOrigin origin) { |
| reporting_origin_ = std::move(origin); |
| return *this; |
| } |
| |
| TriggerBuilder& TriggerBuilder::SetPriority(int64_t priority) { |
| priority_ = priority; |
| return *this; |
| } |
| |
| TriggerBuilder& TriggerBuilder::SetDedupKey(std::optional<uint64_t> dedup_key) { |
| dedup_key_ = dedup_key; |
| return *this; |
| } |
| |
| TriggerBuilder& TriggerBuilder::SetDebugKey(std::optional<uint64_t> debug_key) { |
| debug_key_ = debug_key; |
| return *this; |
| } |
| |
| TriggerBuilder& TriggerBuilder::SetAggregatableTriggerData( |
| std::vector<attribution_reporting::AggregatableTriggerData> |
| aggregatable_trigger_data) { |
| aggregatable_trigger_data_ = std::move(aggregatable_trigger_data); |
| return *this; |
| } |
| |
| TriggerBuilder& TriggerBuilder::SetAggregatableValues( |
| std::vector<attribution_reporting::AggregatableValues> |
| aggregatable_values) { |
| aggregatable_values_ = std::move(aggregatable_values); |
| return *this; |
| } |
| |
| TriggerBuilder& TriggerBuilder::SetAggregatableDedupKey( |
| std::optional<uint64_t> aggregatable_dedup_key) { |
| aggregatable_dedup_key_ = aggregatable_dedup_key; |
| return *this; |
| } |
| |
| TriggerBuilder& TriggerBuilder::SetIsWithinFencedFrame( |
| bool is_within_fenced_frame) { |
| is_within_fenced_frame_ = is_within_fenced_frame; |
| return *this; |
| } |
| |
| TriggerBuilder& TriggerBuilder::SetDebugReporting(bool debug_reporting) { |
| debug_reporting_ = debug_reporting; |
| return *this; |
| } |
| |
| TriggerBuilder& TriggerBuilder::SetAggregationCoordinatorOrigin( |
| SuitableOrigin aggregation_coordinator_origin) { |
| aggregation_coordinator_origin_ = std::move(aggregation_coordinator_origin); |
| return *this; |
| } |
| |
| TriggerBuilder& TriggerBuilder::SetSourceRegistrationTimeConfig( |
| attribution_reporting::mojom::SourceRegistrationTimeConfig |
| source_registration_time_config) { |
| source_registration_time_config_ = source_registration_time_config; |
| return *this; |
| } |
| |
| TriggerBuilder& TriggerBuilder::SetFilterPair( |
| attribution_reporting::FilterPair filter_pair) { |
| filter_pair_ = std::move(filter_pair); |
| return *this; |
| } |
| |
| TriggerBuilder& TriggerBuilder::SetAggregatableDedupKeyFilterPair( |
| attribution_reporting::FilterPair filter_pair) { |
| aggregatable_dedup_key_filter_pair_ = std::move(filter_pair); |
| return *this; |
| } |
| |
| TriggerBuilder& TriggerBuilder::SetTriggerContextId( |
| std::string trigger_context_id) { |
| trigger_context_id_ = std::move(trigger_context_id); |
| return *this; |
| } |
| |
| TriggerBuilder& TriggerBuilder::SetAggregatableDebugReportingConfig( |
| attribution_reporting::AggregatableDebugReportingConfig |
| aggregatable_trigger_config) { |
| aggregatable_debug_reporting_config_ = std::move(aggregatable_trigger_config); |
| return *this; |
| } |
| |
| TriggerBuilder& TriggerBuilder::SetAggregatableFilteringIdMaxBytes( |
| attribution_reporting::AggregatableFilteringIdsMaxBytes max_bytes) { |
| aggregatable_filtering_id_max_bytes_ = max_bytes; |
| return *this; |
| } |
| |
| TriggerBuilder& TriggerBuilder::SetAttributionScopes( |
| attribution_reporting::AttributionScopesSet attribution_scopes) { |
| attribution_scopes_ = std::move(attribution_scopes); |
| return *this; |
| } |
| |
| TriggerBuilder& TriggerBuilder::SetAggregatableNamedBudgetCandidates( |
| std::vector<attribution_reporting::AggregatableNamedBudgetCandidate> |
| budgets) { |
| aggregatable_named_budget_candidates_ = std::move(budgets); |
| return *this; |
| } |
| |
| AttributionTrigger TriggerBuilder::Build( |
| bool generate_event_trigger_data) const { |
| attribution_reporting::TriggerRegistration reg; |
| reg.filters = filter_pair_; |
| reg.debug_key = debug_key_; |
| reg.aggregatable_dedup_keys.emplace_back( |
| /*dedup_key=*/aggregatable_dedup_key_, |
| aggregatable_dedup_key_filter_pair_); |
| |
| if (generate_event_trigger_data) { |
| reg.event_triggers.emplace_back(trigger_data_, priority_, dedup_key_, |
| FilterPair()); |
| } |
| |
| reg.aggregatable_trigger_data = aggregatable_trigger_data_; |
| reg.aggregatable_values = aggregatable_values_; |
| reg.debug_reporting = debug_reporting_; |
| reg.aggregation_coordinator_origin = aggregation_coordinator_origin_; |
| reg.aggregatable_trigger_config = |
| *attribution_reporting::AggregatableTriggerConfig::Create( |
| source_registration_time_config_, trigger_context_id_, |
| aggregatable_filtering_id_max_bytes_); |
| reg.aggregatable_debug_reporting_config = |
| aggregatable_debug_reporting_config_; |
| reg.attribution_scopes = attribution_scopes_; |
| reg.aggregatable_named_budget_candidates = |
| aggregatable_named_budget_candidates_; |
| |
| return AttributionTrigger(reporting_origin_, std::move(reg), |
| destination_origin_, is_within_fenced_frame_, |
| ukm::kInvalidSourceId); |
| } |
| |
| AttributionInfoBuilder::AttributionInfoBuilder(SuitableOrigin context_origin) |
| : context_origin_(std::move(context_origin)) {} |
| |
| AttributionInfoBuilder::~AttributionInfoBuilder() = default; |
| |
| AttributionInfoBuilder& AttributionInfoBuilder::SetTime(base::Time time) { |
| time_ = time; |
| return *this; |
| } |
| |
| AttributionInfoBuilder& AttributionInfoBuilder::SetDebugKey( |
| std::optional<uint64_t> debug_key) { |
| debug_key_ = debug_key; |
| return *this; |
| } |
| |
| AttributionInfo AttributionInfoBuilder::Build() const { |
| return AttributionInfo(time_, debug_key_, context_origin_); |
| } |
| |
| ReportBuilder::ReportBuilder(AttributionInfo attribution_info, |
| StoredSource source) |
| : attribution_info_(std::move(attribution_info)), |
| source_(std::move(source)), |
| external_report_id_(DefaultExternalReportID()) {} |
| |
| ReportBuilder::~ReportBuilder() = default; |
| |
| ReportBuilder& ReportBuilder::SetTriggerData(uint64_t trigger_data) { |
| trigger_data_ = trigger_data; |
| return *this; |
| } |
| |
| ReportBuilder& ReportBuilder::SetReportTime(base::Time time) { |
| report_time_ = time; |
| return *this; |
| } |
| |
| ReportBuilder& ReportBuilder::SetPriority(int64_t priority) { |
| priority_ = priority; |
| return *this; |
| } |
| |
| ReportBuilder& ReportBuilder::SetExternalReportId( |
| base::Uuid external_report_id) { |
| external_report_id_ = std::move(external_report_id); |
| return *this; |
| } |
| |
| ReportBuilder& ReportBuilder::SetReportId(AttributionReport::Id id) { |
| report_id_ = id; |
| return *this; |
| } |
| |
| ReportBuilder& ReportBuilder::SetAggregatableHistogramContributions( |
| std::vector<blink::mojom::AggregatableReportHistogramContribution> |
| contributions) { |
| DCHECK(!contributions.empty()); |
| contributions_ = std::move(contributions); |
| return *this; |
| } |
| |
| ReportBuilder& ReportBuilder::SetAggregationCoordinatorOrigin( |
| SuitableOrigin aggregation_coordinator_origin) { |
| aggregation_coordinator_origin_ = std::move(aggregation_coordinator_origin); |
| return *this; |
| } |
| |
| ReportBuilder& ReportBuilder::SetSourceRegistrationTimeConfig( |
| attribution_reporting::mojom::SourceRegistrationTimeConfig |
| source_registration_time_config) { |
| source_registration_time_config_ = source_registration_time_config; |
| return *this; |
| } |
| |
| ReportBuilder& ReportBuilder::SetAggregatableFilteringIdsMaxBytes( |
| attribution_reporting::AggregatableFilteringIdsMaxBytes max_bytes) { |
| aggregatable_filtering_ids_max_bytes_ = max_bytes; |
| return *this; |
| } |
| |
| ReportBuilder& ReportBuilder::SetTriggerContextId( |
| std::string trigger_context_id) { |
| trigger_context_id_ = std::move(trigger_context_id); |
| return *this; |
| } |
| |
| AttributionReport ReportBuilder::Build() const { |
| return AttributionReport( |
| attribution_info_, report_id_, report_time_, |
| /*initial_report_time=*/report_time_, external_report_id_, |
| /*failed_send_attempts=*/0, |
| AttributionReport::EventLevelData(trigger_data_, priority_, source_), |
| source_.common_info().reporting_origin(), source_.debug_key()); |
| } |
| |
| AttributionReport ReportBuilder::BuildAggregatableAttribution() const { |
| return AttributionReport( |
| attribution_info_, report_id_, report_time_, |
| /*initial_report_time=*/report_time_, external_report_id_, |
| /*failed_send_attempts=*/0, |
| AttributionReport::AggregatableData( |
| aggregation_coordinator_origin_, |
| *attribution_reporting::AggregatableTriggerConfig::Create( |
| source_registration_time_config_, trigger_context_id_, |
| aggregatable_filtering_ids_max_bytes_), |
| source_.source_time(), contributions_, |
| source_.common_info().source_origin()), |
| source_.common_info().reporting_origin(), source_.debug_key()); |
| } |
| |
| AttributionReport ReportBuilder::BuildNullAggregatable() const { |
| return AttributionReport( |
| attribution_info_, report_id_, report_time_, |
| /*initial_report_time=*/report_time_, external_report_id_, |
| /*failed_send_attempts=*/0, |
| AttributionReport::AggregatableData( |
| aggregation_coordinator_origin_, |
| *attribution_reporting::AggregatableTriggerConfig::Create( |
| source_registration_time_config_, trigger_context_id_, |
| attribution_reporting::AggregatableFilteringIdsMaxBytes()), |
| source_.source_time(), |
| /*contributions=*/{}, /*source_origin=*/std::nullopt), |
| source_.common_info().reporting_origin(), |
| /*source_debug_key=*/std::nullopt); |
| } |
| |
| // Does not compare source IDs, as they are set by the underlying sqlite DB and |
| // should not be tested. |
| bool operator==(const StoredSource& a, const StoredSource& b) { |
| const auto tie = [](const StoredSource& source) { |
| return std::make_tuple( |
| source.common_info(), source.source_event_id(), |
| source.destination_sites(), source.source_time(), source.expiry_time(), |
| source.trigger_data(), source.aggregatable_report_window_time(), |
| source.priority(), source.filter_data(), source.debug_key(), |
| source.aggregation_keys(), source.attribution_logic(), |
| source.active_state(), source.dedup_keys(), |
| source.remaining_aggregatable_attribution_budget(), |
| source.aggregatable_dedup_keys(), source.randomized_response_rate(), |
| source.trigger_data_matching(), source.event_level_epsilon(), |
| source.aggregatable_debug_key_piece(), |
| source.remaining_aggregatable_debug_budget(), |
| source.attribution_scopes_data()); |
| }; |
| return tie(a) == tie(b); |
| } |
| |
| // Does not compare the assembled report as it is returned by the |
| // aggregation service from all the other data. |
| bool operator==(const AttributionReport::AggregatableData& a, |
| const AttributionReport::AggregatableData& b) { |
| const auto tie = [](const AttributionReport::AggregatableData& data) { |
| return std::make_tuple(data.aggregation_coordinator_origin(), |
| data.aggregatable_trigger_config(), |
| data.source_time(), data.contributions(), |
| data.source_origin()); |
| }; |
| return tie(a) == tie(b); |
| } |
| |
| // Does not compare source or report IDs, as they are set by the underlying |
| // sqlite DB and should not be tested. |
| bool operator==(const AttributionReport& a, const AttributionReport& b) { |
| const auto tie = [](const AttributionReport& report) { |
| return std::make_tuple( |
| report.attribution_info(), report.report_time(), |
| report.initial_report_time(), report.external_report_id(), |
| report.failed_send_attempts(), report.data(), report.reporting_origin(), |
| report.source_debug_key()); |
| }; |
| return tie(a) == tie(b); |
| } |
| |
| std::ostream& operator<<(std::ostream& out, RateLimitResult result) { |
| switch (result) { |
| case RateLimitResult::kAllowed: |
| return out << "kAllowed"; |
| case RateLimitResult::kNotAllowed: |
| return out << "kNotAllowed"; |
| case RateLimitResult::kError: |
| return out << "kError"; |
| } |
| } |
| |
| std::ostream& operator<<(std::ostream& out, |
| StoredSource::AttributionLogic attribution_logic) { |
| switch (attribution_logic) { |
| case StoredSource::AttributionLogic::kNever: |
| return out << "kNever"; |
| case StoredSource::AttributionLogic::kTruthfully: |
| return out << "kTruthfully"; |
| case StoredSource::AttributionLogic::kFalsely: |
| return out << "kFalsely"; |
| } |
| } |
| |
| std::ostream& operator<<(std::ostream& out, |
| StoredSource::ActiveState active_state) { |
| switch (active_state) { |
| case StoredSource::ActiveState::kActive: |
| return out << "kActive"; |
| case StoredSource::ActiveState::kInactive: |
| return out << "kInactive"; |
| case StoredSource::ActiveState::kReachedEventLevelAttributionLimit: |
| return out << "kReachedEventLevelAttributionLimit"; |
| } |
| } |
| |
| std::ostream& operator<<(std::ostream& out, |
| StoredSource::AggregatableNamedBudgets budgets) { |
| out << "{"; |
| for (const auto& [key, value] : budgets) { |
| out << key << ": { original_budget=" << value.original_budget() |
| << ", remaining_budget=" << value.remaining_budget() << " }, "; |
| } |
| return out << "}"; |
| } |
| |
| std::ostream& operator<<(std::ostream& out, |
| const AttributionTrigger& conversion) { |
| return out << "{registration=" << conversion.registration() |
| << ",destination_origin=" << conversion.destination_origin() |
| << ",is_within_fenced_frame=" |
| << conversion.is_within_fenced_frame() << "}"; |
| } |
| |
| std::ostream& operator<<(std::ostream& out, const CommonSourceInfo& source) { |
| return out << "{source_origin=" << source.source_origin() |
| << "reporting_origin=" << source.reporting_origin() |
| << ",source_type=" << source.source_type() |
| << ",cookie_based_debug_allowed=" |
| << source.cookie_based_debug_allowed() << "}"; |
| } |
| |
| std::ostream& operator<<(std::ostream& out, |
| const AttributionInfo& attribution_info) { |
| return out << "{time=" << attribution_info.time << ",debug_key=" |
| << (attribution_info.debug_key |
| ? base::NumberToString(*attribution_info.debug_key) |
| : "null") |
| << ",context_origin=" << attribution_info.context_origin << "}"; |
| } |
| |
| std::ostream& operator<<(std::ostream& out, const StorableSource& source) { |
| return out << "{registration=" << source.registration().ToJson() |
| << ",common_info=" << source.common_info() |
| << ",is_within_fenced_frame=" << source.is_within_fenced_frame() |
| << "}"; |
| } |
| |
| std::ostream& operator<<(std::ostream& out, const StoredSource& source) { |
| out << "{common_info=" << source.common_info() |
| << ",source_event_id=" << source.source_event_id() |
| << ",destination_sites=" << source.destination_sites() |
| << ",source_time=" << source.source_time() |
| << ",expiry_time=" << source.expiry_time() |
| << ",trigger_data=" << source.trigger_data() |
| << ",aggregatable_report_window_time=" |
| << source.aggregatable_report_window_time() |
| << ",priority=" << source.priority() |
| << ",filter_data=" << source.filter_data() << ",debug_key=" |
| << (source.debug_key() ? base::NumberToString(*source.debug_key()) |
| : "null") |
| << ",aggregation_keys=" << source.aggregation_keys() |
| << ",attribution_logic=" << source.attribution_logic() |
| << ",active_state=" << source.active_state() |
| << ",source_id=" << *source.source_id() |
| << ",remaining_aggregatable_attribution_budget=" |
| << source.remaining_aggregatable_attribution_budget() |
| << ",randomized_response_rate=" << source.randomized_response_rate() |
| << ",event_level_epsilon=" << source.event_level_epsilon() |
| << ",trigger_data_matching=" << source.trigger_data_matching() |
| << ",aggregatable_debug_key_piece=" |
| << source.aggregatable_debug_key_piece() |
| << ",remaining_aggregatable_debug_budget=" |
| << source.remaining_aggregatable_debug_budget() |
| << ",attribution_scopes_data=" |
| << (source.attribution_scopes_data().has_value() |
| ? SerializeAttributionJson( |
| source.attribution_scopes_data()->ToJson()) |
| : "null") |
| << ",aggregatable_named_budgets=" << source.aggregatable_named_budgets() |
| << ",dedup_keys=["; |
| |
| const char* separator = ""; |
| for (int64_t dedup_key : source.dedup_keys()) { |
| out << separator << dedup_key; |
| separator = ", "; |
| } |
| |
| out << "],aggregatable_dedup_keys=["; |
| |
| separator = ""; |
| for (int64_t dedup_key : source.aggregatable_dedup_keys()) { |
| out << separator << dedup_key; |
| separator = ","; |
| } |
| |
| return out << "]}"; |
| } |
| |
| std::ostream& operator<<( |
| std::ostream& out, |
| const blink::mojom::AggregatableReportHistogramContribution& contribution) { |
| return out << "{bucket=" << contribution.bucket |
| << ",value=" << contribution.value << "}"; |
| } |
| |
| std::ostream& operator<<(std::ostream& out, |
| const AttributionReport::EventLevelData& data) { |
| out << "{trigger_data=" << data.trigger_data << ",priority=" << data.priority |
| << ",source_origin=" << data.source_origin |
| << ",destinations=" << data.destinations |
| << ",source_event_id=" << data.source_event_id |
| << ",source_type=" << data.source_type << ",source_debug_key="; |
| |
| return out << ",randomized_response_rate=" << data.randomized_response_rate |
| << ",attributed_truthfully=" << data.attributed_truthfully << "}"; |
| } |
| |
| std::ostream& operator<<(std::ostream& out, |
| const AttributionReport::AggregatableData& data) { |
| out << "{aggregation_coordinator_origin=" |
| << (data.aggregation_coordinator_origin().has_value() |
| ? data.aggregation_coordinator_origin()->Serialize() |
| : "null") |
| << ",aggregatable_trigger_config=" << data.aggregatable_trigger_config() |
| << ",source_time=" << data.source_time() << ",contributions=["; |
| |
| const char* separator = ""; |
| for (const auto& contribution : data.contributions()) { |
| out << separator << contribution; |
| separator = ", "; |
| } |
| |
| out << "],source_origin="; |
| |
| if (data.source_origin().has_value()) { |
| out << *data.source_origin(); |
| } else { |
| out << "null"; |
| } |
| |
| return out << "}"; |
| } |
| |
| namespace { |
| std::ostream& operator<<(std::ostream& out, |
| const AttributionReport::Data& data) { |
| std::visit([&out](const auto& v) { out << v; }, data); |
| return out; |
| } |
| } // namespace |
| |
| std::ostream& operator<<(std::ostream& out, const AttributionReport& report) { |
| out << "{attribution_info=" << report.attribution_info() |
| << ",id=" << *report.id() << ",report_time=" << report.report_time() |
| << ",initial_report_time=" << report.initial_report_time() |
| << ",external_report_id=" << report.external_report_id() |
| << ",failed_send_attempts=" << report.failed_send_attempts() |
| << ",data=" << report.data() |
| << ",reporting_origin=" << report.reporting_origin() |
| << ",source_debug_key="; |
| |
| if (report.source_debug_key().has_value()) { |
| out << *report.source_debug_key(); |
| } else { |
| out << "null"; |
| } |
| |
| return out << "}"; |
| } |
| |
| std::ostream& operator<<(std::ostream& out, SendResult::Status status) { |
| switch (status) { |
| case SendResult::Status::kSent: |
| return out << "kSent"; |
| case SendResult::Status::kTransientFailure: |
| return out << "kTransientFailure"; |
| case SendResult::Status::kFailure: |
| return out << "kFailure"; |
| case SendResult::Status::kExpired: |
| return out << "kExpired"; |
| case SendResult::Status::kDropped: |
| return out << "kDropped"; |
| case SendResult::Status::kAssemblyFailure: |
| return out << "kAssemblyFailure"; |
| case SendResult::Status::kTransientAssemblyFailure: |
| return out << "kTransientAssemblyFailure"; |
| } |
| } |
| |
| std::ostream& operator<<(std::ostream& out, const SendResult& info) { |
| std::visit(absl::Overload{ |
| [&](SendResult::Sent sent) { out << sent; }, |
| [&](SendResult::Dropped) { out << "{Dropped={}}"; }, |
| [&](SendResult::Expired) { out << "{Expired={}}"; }, |
| [&](SendResult::AssemblyFailure failure) { |
| out << "{AssemblyFailure={transient=" << failure.transient |
| << "}}"; |
| }, |
| }, |
| info.result); |
| return out; |
| } |
| |
| std::ostream& operator<<(std::ostream& out, SendResult::Sent sent) { |
| out << "{Sent={result="; |
| switch (sent.result) { |
| case SendResult::Sent::Result::kSent: |
| out << "kSent"; |
| break; |
| case SendResult::Sent::Result::kTransientFailure: |
| out << "kTransientFailure"; |
| break; |
| case SendResult::Sent::Result::kFailure: |
| out << "kFailure"; |
| break; |
| } |
| out << ",status="; |
| if (sent.status < 0) { |
| out << net::ErrorToShortString(sent.status); |
| } else { |
| out << sent.status; |
| } |
| return out << "}}"; |
| } |
| |
| std::ostream& operator<<(std::ostream& out, |
| const AttributionDataModel::DataKey& key) { |
| return out << "{reporting_origin=" << key.reporting_origin() << "}"; |
| } |
| |
| TestAggregatableSourceProvider::TestAggregatableSourceProvider(size_t size) { |
| attribution_reporting::AggregationKeys::Keys::container_type keys; |
| keys.reserve(size); |
| for (size_t i = 0; i < size; ++i) { |
| keys.emplace_back(base::NumberToString(i), i); |
| } |
| |
| auto source = |
| attribution_reporting::AggregationKeys::FromKeys(std::move(keys)); |
| DCHECK(source.has_value()); |
| source_ = *std::move(source); |
| } |
| |
| TestAggregatableSourceProvider::~TestAggregatableSourceProvider() = default; |
| |
| SourceBuilder TestAggregatableSourceProvider::GetBuilder( |
| base::Time source_time) const { |
| return SourceBuilder(source_time).SetAggregationKeys(source_); |
| } |
| |
| TriggerBuilder DefaultAggregatableTriggerBuilder( |
| const std::vector<uint32_t>& histogram_values) { |
| std::vector<attribution_reporting::AggregatableTriggerData> |
| aggregatable_trigger_data; |
| |
| attribution_reporting::AggregatableValues::Values aggregatable_values; |
| |
| for (size_t i = 0; i < histogram_values.size(); ++i) { |
| std::string key_id = base::NumberToString(i); |
| aggregatable_trigger_data.push_back( |
| attribution_reporting::AggregatableTriggerData( |
| absl::MakeUint128(/*high=*/i, /*low=*/0), |
| /*source_keys=*/{key_id}, FilterPair())); |
| aggregatable_values.emplace( |
| std::move(key_id), |
| *attribution_reporting::AggregatableValuesValue::Create( |
| histogram_values[i], attribution_reporting::kDefaultFilteringId)); |
| } |
| |
| return TriggerBuilder() |
| .SetAggregatableTriggerData(std::move(aggregatable_trigger_data)) |
| .SetAggregatableValues( |
| {*attribution_reporting::AggregatableValues::Create( |
| std::move(aggregatable_values), FilterPair())}); |
| } |
| |
| std::vector<blink::mojom::AggregatableReportHistogramContribution> |
| DefaultAggregatableHistogramContributions( |
| const std::vector<int32_t>& histogram_values) { |
| std::vector<blink::mojom::AggregatableReportHistogramContribution> |
| contributions; |
| for (size_t i = 0; i < histogram_values.size(); ++i) { |
| contributions.emplace_back(absl::MakeUint128(i, i), histogram_values[i], |
| attribution_reporting::kDefaultFilteringId); |
| } |
| return contributions; |
| } |
| |
| bool operator==(const OsRegistration& a, const OsRegistration& b) { |
| const auto tie = [](const OsRegistration& r) { |
| return std::make_tuple(r.registration_items, r.top_level_origin, |
| r.GetType()); |
| }; |
| return tie(a) == tie(b); |
| } |
| |
| std::ostream& operator<<(std::ostream& out, const OsRegistration& r) { |
| out << "{registration_items=["; |
| const char* separator = ""; |
| for (const OsRegistrationItem& item : r.registration_items) { |
| out << separator << item; |
| separator = ","; |
| } |
| return out << "],top_level_origin=" << r.top_level_origin |
| << ",type=" << r.GetType() << "}"; |
| } |
| |
| namespace { |
| |
| void CheckAttributionReportingHeader( |
| const std::string& header, |
| const std::vector<std::string>& required_keys, |
| const std::vector<std::string>& prohibited_keys) { |
| auto dict = net::structured_headers::ParseDictionary(header); |
| EXPECT_TRUE(dict.has_value()); |
| if (!dict.has_value()) { |
| return; |
| } |
| |
| for (const auto& key : required_keys) { |
| EXPECT_TRUE(dict->contains(key)) << key; |
| } |
| |
| for (const auto& key : prohibited_keys) { |
| EXPECT_FALSE(dict->contains(key)) << key; |
| } |
| } |
| |
| } // namespace |
| |
| void ExpectValidAttributionReportingEligibleHeaderForEventBeacon( |
| const std::string& header) { |
| CheckAttributionReportingHeader( |
| header, |
| /*required_keys=*/{"event-source"}, |
| /*prohibited_keys=*/{"navigation-source", "trigger"}); |
| } |
| |
| void ExpectValidAttributionReportingEligibleHeaderForImg( |
| const std::string& header) { |
| CheckAttributionReportingHeader(header, |
| /*required_keys=*/{"event-source", "trigger"}, |
| /*prohibited_keys=*/{"navigation-source"}); |
| } |
| |
| void ExpectValidAttributionReportingEligibleHeaderForNavigation( |
| const std::string& header) { |
| CheckAttributionReportingHeader( |
| header, |
| /*required_keys=*/{"navigation-source"}, |
| /*prohibited_keys=*/{"event-source", "trigger"}); |
| } |
| |
| void ExpectEmptyAttributionReportingEligibleHeader(const std::string& header) { |
| CheckAttributionReportingHeader( |
| header, |
| /*required_keys=*/{}, |
| /*prohibited_keys=*/{"navigation-source", "event-source", "trigger"}); |
| } |
| |
| void ExpectValidAttributionReportingSupportHeader(const std::string& header, |
| bool web_expected, |
| bool os_expected) { |
| std::vector<std::string> required_keys; |
| std::vector<std::string> prohibited_keys; |
| |
| if (web_expected) { |
| required_keys.emplace_back("web"); |
| } else { |
| prohibited_keys.emplace_back("web"); |
| } |
| |
| if (os_expected) { |
| required_keys.emplace_back("os"); |
| } else { |
| prohibited_keys.emplace_back("os"); |
| } |
| |
| CheckAttributionReportingHeader(header, required_keys, prohibited_keys); |
| } |
| |
| } // namespace content |