blob: 3a5b3c61a580752b636c8be4ca89d6d9df1c7f99 [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"
namespace content {
ConversionReporterImpl::ConversionReporterImpl(
StoragePartition* storage_partition,
ConversionManager* conversion_manager,
const base::Clock* clock)
: conversion_manager_(conversion_manager),
clock_(clock),
network_sender_(
std::make_unique<ConversionNetworkSenderImpl>(storage_partition)) {
DCHECK(conversion_manager_);
}
ConversionReporterImpl::~ConversionReporterImpl() = default;
void ConversionReporterImpl::AddReportsToQueue(
std::vector<ConversionReport> reports) {
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_ids_being_processed_.insert(*(report->conversion_id)).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.
network_sender_->SendReport(
report_queue_.top().get(),
base::BindOnce(&ConversionReporterImpl::OnReportSent,
base::Unretained(this),
*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::BindOnce(&ConversionReporterImpl::SendNextReport,
base::Unretained(this)));
}
void ConversionReporterImpl::OnReportSent(int64_t conversion_id) {
conversion_ids_being_processed_.erase(conversion_id);
conversion_manager_->HandleSentReport(conversion_id);
}
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