blob: 5382a85cf40072c192e13af8028d6ef505a19b17 [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/conversions/conversion_reporter_impl.h"
#include "base/bind.h"
#include "base/callback.h"
#include "base/rand_util.h"
#include "base/time/clock.h"
#include "content/browser/conversions/conversion_manager.h"
#include "content/browser/conversions/conversion_network_sender_impl.h"
#include "content/browser/storage_partition_impl.h"
#include "content/public/browser/content_browser_client.h"
#include "content/public/common/content_client.h"
namespace content {
ConversionReporterImpl::ConversionReporterImpl(
StoragePartition* storage_partition,
const base::Clock* clock)
: clock_(clock),
partition_(static_cast<StoragePartitionImpl*>(storage_partition)),
network_sender_(
std::make_unique<ConversionNetworkSenderImpl>(storage_partition)) {}
ConversionReporterImpl::~ConversionReporterImpl() = default;
void ConversionReporterImpl::AddReportsToQueue(
std::vector<ConversionReport> reports,
base::RepeatingCallback<void(int64_t)> report_sent_callback) {
DCHECK(!reports.empty());
std::vector<std::unique_ptr<ConversionReport>> swappable_reports;
for (ConversionReport& report : reports) {
swappable_reports.push_back(
std::make_unique<ConversionReport>(std::move(report)));
}
// Shuffle new reports to provide plausible deniability on the ordering of
// reports that share the same |report_time|. This is important because
// multiple conversions for the same impression share the same report time if
// they are within the same reporting window, and we do not want to allow
// ordering on their conversion metadata bits.
base::RandomShuffle(swappable_reports.begin(), swappable_reports.end());
for (std::unique_ptr<ConversionReport>& report : swappable_reports) {
// If the given report is already being processed, ignore it.
bool inserted = conversion_report_callbacks_
.emplace(*(report->conversion_id), report_sent_callback)
.second;
if (inserted)
report_queue_.push(std::move(report));
}
MaybeScheduleNextReport();
}
void ConversionReporterImpl::SetNetworkSenderForTesting(
std::unique_ptr<NetworkSender> network_sender) {
network_sender_ = std::move(network_sender);
}
void ConversionReporterImpl::SendNextReport() {
// Send the next report and remove it from the queue. Bind the conversion id
// to the sent callback so we know which conversion report has finished
// sending.
ConversionReport* report = report_queue_.top().get();
if (GetContentClient()->browser()->IsConversionMeasurementOperationAllowed(
partition_->browser_context(),
ContentBrowserClient::ConversionMeasurementOperation::kReport,
&report->impression.impression_origin(),
&report->impression.conversion_origin(),
&report->impression.reporting_origin())) {
network_sender_->SendReport(
report_queue_.top().get(),
base::BindOnce(&ConversionReporterImpl::OnReportSent,
base::Unretained(this), report->conversion_id.value()));
} else {
// If measurement is disallowed, just drop the report on the floor. We need
// to make sure we forward that the report was "sent" to ensure it is
// deleted from storage, etc. This simulate sending the report through a
// null channel.
OnReportSent(*report_queue_.top()->conversion_id);
}
report_queue_.pop();
MaybeScheduleNextReport();
}
void ConversionReporterImpl::MaybeScheduleNextReport() {
if (report_queue_.empty())
return;
send_report_timer_.Stop();
base::Time current_time = clock_->Now();
base::Time report_time = report_queue_.top()->report_time;
// Start a timer to wait until the next report is ready to be sent. This
// purposefully yields the thread for every report that gets scheduled.
// Unretained is safe because the task should never actually be posted if the
// timer itself is destroyed
send_report_timer_.Start(
FROM_HERE,
(report_time < current_time) ? base::TimeDelta()
: report_time - current_time,
base::BindOnce(&ConversionReporterImpl::SendNextReport,
base::Unretained(this)));
}
void ConversionReporterImpl::OnReportSent(int64_t conversion_id) {
auto it = conversion_report_callbacks_.find(conversion_id);
DCHECK(it != conversion_report_callbacks_.end());
std::move(it->second).Run(conversion_id);
conversion_report_callbacks_.erase(it);
}
bool ConversionReporterImpl::ReportComparator::operator()(
const std::unique_ptr<ConversionReport>& a,
const std::unique_ptr<ConversionReport>& b) const {
// Returns whether a should appear before b in ordering. Because
// std::priority_queue is max priority queue, we used greater then to make a
// min priority queue.
return a->report_time > b->report_time;
}
} // namespace content