blob: ffb832e5e0a31cb6a89448c98f43e3b5ff70d3df [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_manager_impl.h"
#include <utility>
#include "base/barrier_closure.h"
#include "base/bind.h"
#include "base/command_line.h"
#include "base/memory/ptr_util.h"
#include "base/time/default_clock.h"
#include "content/browser/conversions/conversion_reporter_impl.h"
#include "content/browser/conversions/conversion_storage_delegate_impl.h"
#include "content/browser/conversions/conversion_storage_sql.h"
#include "content/browser/storage_partition_impl.h"
#include "content/public/browser/browser_context.h"
#include "content/public/browser/storage_partition.h"
#include "content/public/browser/web_contents.h"
#include "content/public/common/content_switches.h"
namespace content {
const constexpr base::TimeDelta kConversionManagerQueueReportsInterval =
base::TimeDelta::FromMinutes(30);
ConversionManager* ConversionManagerProviderImpl::GetManager(
WebContents* web_contents) const {
return static_cast<StoragePartitionImpl*>(
BrowserContext::GetDefaultStoragePartition(
web_contents->GetBrowserContext()))
->GetConversionManager();
}
// static
void ConversionManagerImpl::RunInMemoryForTesting() {
ConversionStorageSql::RunInMemoryForTesting();
}
// static
std::unique_ptr<ConversionManagerImpl> ConversionManagerImpl::CreateForTesting(
std::unique_ptr<ConversionReporter> reporter,
std::unique_ptr<ConversionPolicy> policy,
const base::Clock* clock,
const base::FilePath& user_data_directory) {
return base::WrapUnique<ConversionManagerImpl>(new ConversionManagerImpl(
std::move(reporter), std::move(policy), clock, user_data_directory));
}
ConversionManagerImpl::ConversionManagerImpl(
StoragePartition* storage_partition,
const base::FilePath& user_data_directory)
: ConversionManagerImpl(
std::make_unique<ConversionReporterImpl>(
storage_partition,
base::DefaultClock::GetInstance()),
std::make_unique<ConversionPolicy>(
base::CommandLine::ForCurrentProcess()->HasSwitch(
switches::kConversionsDebugMode)),
base::DefaultClock::GetInstance(),
user_data_directory) {}
ConversionManagerImpl::ConversionManagerImpl(
std::unique_ptr<ConversionReporter> reporter,
std::unique_ptr<ConversionPolicy> policy,
const base::Clock* clock,
const base::FilePath& user_data_directory)
: debug_mode_(base::CommandLine::ForCurrentProcess()->HasSwitch(
switches::kConversionsDebugMode)),
clock_(clock),
reporter_(std::move(reporter)),
conversion_storage_context_(
base::MakeRefCounted<ConversionStorageContext>(
user_data_directory,
std::make_unique<ConversionStorageDelegateImpl>(debug_mode_),
clock_)),
conversion_policy_(std::move(policy)),
weak_factory_(this) {
// Once the database is loaded, get all reports that may have expired while
// Chrome was not running and handle these specially. It is safe to post tasks
// to the storage context as soon as it is created.
GetAndHandleReports(
base::BindOnce(&ConversionManagerImpl::HandleReportsExpiredAtStartup,
weak_factory_.GetWeakPtr()),
clock_->Now() + kConversionManagerQueueReportsInterval);
// Start a repeating timer that will fetch reports once every
// |kConversionManagerQueueReportsInterval| and add them to |reporter_|.
get_and_queue_reports_timer_.Start(
FROM_HERE, kConversionManagerQueueReportsInterval, this,
&ConversionManagerImpl::GetAndQueueReportsForNextInterval);
}
ConversionManagerImpl::~ConversionManagerImpl() = default;
void ConversionManagerImpl::HandleImpression(
const StorableImpression& impression) {
// Add the impression to storage.
conversion_storage_context_->StoreImpression(impression);
}
void ConversionManagerImpl::HandleConversion(
const StorableConversion& conversion) {
// TODO(https://crbug.com/1043345): Add UMA for the number of conversions we
// are logging to storage, and the number of new reports logged to storage.
conversion_storage_context_->MaybeCreateAndStoreConversionReports(
conversion, base::DoNothing::Once<int>());
// If we are running in debug mode, we should also schedule a task to
// gather and send any new reports.
if (debug_mode_)
GetAndQueueReportsForNextInterval();
}
void ConversionManagerImpl::GetActiveImpressionsForWebUI(
base::OnceCallback<void(std::vector<StorableImpression>)> callback) {
conversion_storage_context_->GetActiveImpressions(std::move(callback));
}
void ConversionManagerImpl::GetReportsForWebUI(
base::OnceCallback<void(std::vector<ConversionReport>)> callback,
base::Time max_report_time) {
GetAndHandleReports(std::move(callback), max_report_time);
}
void ConversionManagerImpl::SendReportsForWebUI(base::OnceClosure done) {
GetAndHandleReports(
base::BindOnce(&ConversionManagerImpl::HandleReportsSentFromWebUI,
weak_factory_.GetWeakPtr(), std::move(done)),
base::Time::Max());
}
const ConversionPolicy& ConversionManagerImpl::GetConversionPolicy() const {
return *conversion_policy_;
}
void ConversionManagerImpl::ClearData(
base::Time delete_begin,
base::Time delete_end,
base::RepeatingCallback<bool(const url::Origin&)> filter,
base::OnceClosure done) {
conversion_storage_context_->ClearData(delete_begin, delete_end,
std::move(filter), std::move(done));
}
void ConversionManagerImpl::GetAndHandleReports(
ReportsHandlerFunc handler_function,
base::Time max_report_time) {
conversion_storage_context_->GetConversionsToReport(
max_report_time, std::move(handler_function));
}
void ConversionManagerImpl::GetAndQueueReportsForNextInterval() {
// Get all the reports that will be reported in the next interval and them to
// the |reporter_|.
GetAndHandleReports(base::BindOnce(&ConversionManagerImpl::QueueReports,
weak_factory_.GetWeakPtr()),
clock_->Now() + kConversionManagerQueueReportsInterval);
}
void ConversionManagerImpl::QueueReports(
std::vector<ConversionReport> reports) {
if (!reports.empty()) {
// |reporter_| is owned by |this|, so base::Unretained() is safe as the
// reporter and callbacks will be deleted first.
reporter_->AddReportsToQueue(
std::move(reports),
base::BindRepeating(&ConversionManagerImpl::OnReportSent,
base::Unretained(this)));
}
}
void ConversionManagerImpl::HandleReportsExpiredAtStartup(
std::vector<ConversionReport> reports) {
// Add delay to all reports that expired while the browser was not running so
// they are not temporally join-able.
base::Time current_time = clock_->Now();
for (ConversionReport& report : reports) {
if (report.report_time > current_time)
continue;
base::Time updated_report_time =
conversion_policy_->GetReportTimeForExpiredReportAtStartup(
current_time);
report.extra_delay = updated_report_time - report.report_time;
report.report_time = updated_report_time;
}
QueueReports(std::move(reports));
}
void ConversionManagerImpl::HandleReportsSentFromWebUI(
base::OnceClosure done,
std::vector<ConversionReport> reports) {
if (reports.empty()) {
std::move(done).Run();
return;
}
// All reports should be sent immediately.
for (ConversionReport& report : reports) {
report.report_time = base::Time::Min();
}
// Wraps |done| so that is will run once all of the reports have finished
// sending.
base::RepeatingClosure all_reports_sent =
base::BarrierClosure(reports.size(), std::move(done));
// |reporter_| is owned by |this|, so base::Unretained() is safe as the
// reporter and callbacks will be deleted first.
reporter_->AddReportsToQueue(
std::move(reports),
base::BindRepeating(&ConversionManagerImpl::OnReportSentFromWebUI,
base::Unretained(this), std::move(all_reports_sent)));
}
void ConversionManagerImpl::OnReportSent(int64_t conversion_id) {
conversion_storage_context_->DeleteConversion(conversion_id,
base::DoNothing::Once<bool>());
}
void ConversionManagerImpl::OnReportSentFromWebUI(
base::OnceClosure reports_sent_barrier,
int64_t conversion_id) {
// |reports_sent_barrier| is a OnceClosure view of a RepeatingClosure obtained
// by base::BarrierClosure().
conversion_storage_context_->DeleteConversion(
conversion_id,
base::BindOnce([](base::OnceClosure callback,
bool result) { std::move(callback).Run(); },
std::move(reports_sent_barrier)));
}
} // namespace content