blob: f88d1e5c0ee430d6beb065b59eba2a8ecc7a55bd [file] [log] [blame]
// Copyright 2020 The Chromium Authors. All rights reserved.
// 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_report.h"
#include <algorithm>
#include <string>
#include <utility>
#include "base/check.h"
#include "base/check_op.h"
#include "base/memory/raw_ptr.h"
#include "base/strings/strcat.h"
#include "base/strings/string_number_conversions.h"
#include "base/values.h"
#include "content/browser/attribution_reporting/attribution_source_type.h"
#include "content/browser/attribution_reporting/common_source_info.h"
#include "net/base/schemeful_site.h"
#include "third_party/abseil-cpp/absl/types/variant.h"
#include "url/gurl.h"
#include "url/url_canon.h"
namespace content {
AttributionReport::EventLevelData::EventLevelData(
uint64_t trigger_data,
int64_t priority,
double randomized_trigger_rate,
Id id)
: trigger_data(trigger_data),
priority(priority),
randomized_trigger_rate(randomized_trigger_rate),
id(id) {
DCHECK_GE(randomized_trigger_rate, 0);
DCHECK_LE(randomized_trigger_rate, 1);
}
AttributionReport::EventLevelData::EventLevelData(const EventLevelData& other) =
default;
AttributionReport::EventLevelData& AttributionReport::EventLevelData::operator=(
const EventLevelData& other) = default;
AttributionReport::EventLevelData::EventLevelData(EventLevelData&& other) =
default;
AttributionReport::EventLevelData& AttributionReport::EventLevelData::operator=(
EventLevelData&& other) = default;
AttributionReport::EventLevelData::~EventLevelData() = default;
AttributionReport::AggregatableAttributionData::AggregatableAttributionData(
std::vector<AggregatableHistogramContribution> contributions,
Id id,
base::Time initial_report_time)
: contributions(std::move(contributions)),
id(id),
initial_report_time(initial_report_time) {}
AttributionReport::AggregatableAttributionData::AggregatableAttributionData(
const AggregatableAttributionData&) = default;
AttributionReport::AggregatableAttributionData&
AttributionReport::AggregatableAttributionData::operator=(
const AggregatableAttributionData&) = default;
AttributionReport::AggregatableAttributionData::AggregatableAttributionData(
AggregatableAttributionData&&) = default;
AttributionReport::AggregatableAttributionData&
AttributionReport::AggregatableAttributionData::operator=(
AggregatableAttributionData&&) = default;
AttributionReport::AggregatableAttributionData::~AggregatableAttributionData() =
default;
base::CheckedNumeric<int64_t>
AttributionReport::AggregatableAttributionData::BudgetRequired() const {
base::CheckedNumeric<int64_t> budget_required = 0;
for (const AggregatableHistogramContribution& contribution : contributions) {
budget_required += contribution.value();
}
return budget_required;
}
AttributionReport::AttributionReport(
AttributionInfo attribution_info,
base::Time report_time,
base::GUID external_report_id,
absl::variant<EventLevelData, AggregatableAttributionData> data)
: attribution_info_(std::move(attribution_info)),
report_time_(report_time),
external_report_id_(std::move(external_report_id)),
data_(std::move(data)) {
DCHECK(external_report_id_.is_valid());
}
AttributionReport::AttributionReport(const AttributionReport& other) = default;
AttributionReport& AttributionReport::operator=(
const AttributionReport& other) = default;
AttributionReport::AttributionReport(AttributionReport&& other) = default;
AttributionReport& AttributionReport::operator=(AttributionReport&& other) =
default;
AttributionReport::~AttributionReport() = default;
GURL AttributionReport::ReportURL(bool debug) const {
static constexpr char kBasePath[] = "/.well-known/attribution-reporting/";
static constexpr char kDebugPath[] = "debug/";
const char* endpoint_path;
switch (GetReportType()) {
case ReportType::kEventLevel:
endpoint_path = "report-event-attribution";
break;
case ReportType::kAggregatableAttribution:
endpoint_path = "report-aggregate-attribution";
break;
}
std::string path =
base::StrCat({kBasePath, debug ? kDebugPath : "", endpoint_path});
GURL::Replacements replacements;
replacements.SetPathStr(path);
return attribution_info_.source.common_info()
.reporting_origin()
.GetURL()
.ReplaceComponents(replacements);
}
base::Value::Dict AttributionReport::ReportBody() const {
struct Visitor {
raw_ptr<const AttributionReport> report;
base::Value::Dict operator()(const EventLevelData& data) {
base::Value::Dict dict;
const CommonSourceInfo& common_source_info =
report->attribution_info().source.common_info();
dict.Set("attribution_destination",
common_source_info.ConversionDestination().Serialize());
// The API denotes these values as strings; a `uint64_t` cannot be put in
// a dict as an integer in order to be opaque to various API
// configurations.
dict.Set("source_event_id",
base::NumberToString(common_source_info.source_event_id()));
dict.Set("trigger_data", base::NumberToString(data.trigger_data));
dict.Set("source_type",
AttributionSourceTypeToString(common_source_info.source_type()));
dict.Set("report_id", report->external_report_id().AsLowercaseString());
dict.Set("randomized_trigger_rate", data.randomized_trigger_rate);
if (absl::optional<uint64_t> debug_key = common_source_info.debug_key())
dict.Set("source_debug_key", base::NumberToString(*debug_key));
if (absl::optional<uint64_t> debug_key =
report->attribution_info().debug_key) {
dict.Set("trigger_debug_key", base::NumberToString(*debug_key));
}
return dict;
}
base::Value::Dict operator()(const AggregatableAttributionData& data) {
base::Value::Dict dict;
if (data.assembled_report.has_value()) {
dict = data.assembled_report->GetAsJson();
} else {
// This generally should only be called when displaying the report for
// debugging/internals.
dict.Set("shared_info", "not generated prior to send");
dict.Set("aggregation_service_payloads", "not generated prior to send");
}
const CommonSourceInfo& common_info =
report->attribution_info().source.common_info();
if (absl::optional<uint64_t> debug_key = common_info.debug_key())
dict.Set("source_debug_key", base::NumberToString(*debug_key));
if (absl::optional<uint64_t> debug_key =
report->attribution_info().debug_key) {
dict.Set("trigger_debug_key", base::NumberToString(*debug_key));
}
return dict;
}
};
return absl::visit(Visitor{.report = this}, data_);
}
AttributionReport::Id AttributionReport::ReportId() const {
return absl::visit([](const auto& v) { return Id(v.id); }, data_);
}
void AttributionReport::set_report_time(base::Time report_time) {
report_time_ = report_time;
}
void AttributionReport::set_failed_send_attempts(int failed_send_attempts) {
DCHECK_GE(failed_send_attempts, 0);
failed_send_attempts_ = failed_send_attempts;
}
void AttributionReport::SetExternalReportIdForTesting(
base::GUID external_report_id) {
DCHECK(external_report_id.is_valid());
external_report_id_ = std::move(external_report_id);
}
// static
absl::optional<base::Time> AttributionReport::MinReportTime(
absl::optional<base::Time> a,
absl::optional<base::Time> b) {
if (!a.has_value())
return b;
if (!b.has_value())
return a;
return std::min(*a, *b);
}
} // namespace content