blob: 5e526f90e0e3327187c99623111cb5cbf9f1aa3d [file] [log] [blame]
// Copyright 2021 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_utils.h"
#include "base/check.h"
#include "base/check_op.h"
#include "base/containers/span.h"
#include "base/json/json_writer.h"
#include "base/time/time.h"
#include "base/values.h"
#include "components/attribution_reporting/source_type.mojom.h"
#include "content/browser/attribution_reporting/common_source_info.h"
#include "third_party/abseil-cpp/absl/types/optional.h"
namespace content {
namespace {
using ::attribution_reporting::mojom::SourceType;
constexpr base::TimeDelta kWindowDeadlineOffset = base::Hours(1);
base::span<const base::TimeDelta> EarlyDeadlines(SourceType source_type) {
static constexpr base::TimeDelta kEarlyDeadlinesNavigation[] = {
base::Days(2),
base::Days(7),
};
switch (source_type) {
case SourceType::kNavigation:
return kEarlyDeadlinesNavigation;
case SourceType::kEvent:
return base::span<const base::TimeDelta>();
}
}
base::TimeDelta ExpiryDeadline(base::Time source_time,
base::Time event_report_window_time) {
DCHECK_GT(event_report_window_time, source_time);
return event_report_window_time - source_time;
}
base::Time ReportTimeFromDeadline(base::Time source_time,
base::TimeDelta deadline) {
// Valid conversion reports should always have a valid reporting deadline.
DCHECK(!deadline.is_zero());
return source_time + deadline + kWindowDeadlineOffset;
}
} // namespace
base::Time ComputeReportTime(const CommonSourceInfo& source,
base::Time event_report_window_time,
base::Time trigger_time) {
base::TimeDelta expiry_deadline =
ExpiryDeadline(source.source_time(), event_report_window_time);
// After the initial impression, a schedule of reporting windows and deadlines
// associated with that impression begins. The time between impression time
// and impression expiry is split into multiple reporting windows whose values
// are defined in `kEarlyDeadlinesNavigation`. At the end of each window, the
// browser will send all scheduled reports for that impression.
//
// Each reporting window has a deadline and only conversions registered before
// that deadline are sent in that window. Each deadline is at the window
// report time. The deadlines relative to impression time are <first report
// window, second report window, impression expiry>. The impression expiry
// window is only used for conversions that occur after the second report
// window. For example, a conversion which happens one hour after an
// impression with an expiry of two hours, is still reported in the first
// report window.
//
// Note that only navigation (not event) sources have early reporting
// deadlines.
base::TimeDelta deadline_to_use = expiry_deadline;
// Given a conversion that happened at `trigger_time`, find the first
// applicable reporting window this conversion should be reported at.
for (base::TimeDelta early_deadline : EarlyDeadlines(source.source_type())) {
// If this window is valid for the conversion, use it.
// |trigger_time| is roughly ~now.
if (source.source_time() + early_deadline >= trigger_time &&
early_deadline < deadline_to_use) {
deadline_to_use = early_deadline;
break;
}
}
return ReportTimeFromDeadline(source.source_time(), deadline_to_use);
}
int NumReportWindows(SourceType source_type) {
// Add 1 for the expiry deadline.
return 1 + EarlyDeadlines(source_type).size();
}
base::Time ReportTimeAtWindow(const CommonSourceInfo& source,
base::Time event_report_window_time,
int window_index) {
DCHECK_GE(window_index, 0);
DCHECK_LT(window_index, NumReportWindows(source.source_type()));
base::span<const base::TimeDelta> early_deadlines =
EarlyDeadlines(source.source_type());
base::TimeDelta deadline =
static_cast<size_t>(window_index) < early_deadlines.size()
? early_deadlines[window_index]
: ExpiryDeadline(source.source_time(), event_report_window_time);
return ReportTimeFromDeadline(source.source_time(), deadline);
}
base::Time LastTriggerTimeForReportTime(base::Time report_time) {
return report_time - kWindowDeadlineOffset;
}
std::string SerializeAttributionJson(base::ValueView body, bool pretty_print) {
int options = pretty_print ? base::JSONWriter::OPTIONS_PRETTY_PRINT : 0;
std::string output_json;
bool success =
base::JSONWriter::WriteWithOptions(body, options, &output_json);
DCHECK(success);
return output_json;
}
base::Time ComputeReportWindowTime(
absl::optional<base::Time> report_window_time,
base::Time expiry_time) {
return report_window_time.has_value() &&
report_window_time.value() <= expiry_time
? report_window_time.value()
: expiry_time;
}
} // namespace content