blob: da426ee2b1d1363c4db2ea07d42aeca979d56f12 [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 "content/browser/attribution_reporting/attribution_source_type.h"
#include "content/browser/attribution_reporting/common_source_info.h"
namespace content {
namespace {
constexpr base::TimeDelta kWindowDeadlineOffset = base::Hours(1);
base::span<const base::TimeDelta> EarlyDeadlines(
AttributionSourceType source_type) {
static constexpr base::TimeDelta kEarlyDeadlinesNavigation[] = {
base::Days(2),
base::Days(7),
};
switch (source_type) {
case AttributionSourceType::kNavigation:
return kEarlyDeadlinesNavigation;
case AttributionSourceType::kEvent:
return base::span<const base::TimeDelta>();
}
}
base::TimeDelta ExpiryDeadline(const CommonSourceInfo& source) {
return source.event_report_window_time() - source.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 trigger_time) {
base::TimeDelta expiry_deadline = ExpiryDeadline(source);
// 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. 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 one hour prior to
// the window report time. The deadlines relative to impression time are <2
// days minus 1 hour, 7 days minus 1 hour, impression expiry>. The impression
// expiry window is only used for conversions that occur after the 7 day
// deadline. For example, a conversion which happens one hour after an
// impression with an expiry of two hours, is still reported in the 2 day
// 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(AttributionSourceType source_type) {
// Add 1 for the expiry deadline.
return 1 + EarlyDeadlines(source_type).size();
}
base::Time ReportTimeAtWindow(const CommonSourceInfo& source,
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);
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;
}
} // namespace content