blob: b08dd2d9708acee1680411f5bfd08829d1cb05a3 [file] [log] [blame]
// Copyright 2022 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/private_aggregation/private_aggregation_internals_handler_impl.h"
#include <algorithm>
#include <iterator>
#include <optional>
#include <string>
#include <utility>
#include <vector>
#include "base/barrier_closure.h"
#include "base/check.h"
#include "base/functional/bind.h"
#include "base/functional/callback_helpers.h"
#include "base/json/json_writer.h"
#include "base/time/time.h"
#include "base/values.h"
#include "content/browser/aggregation_service/aggregatable_report.h"
#include "content/browser/aggregation_service/aggregation_service.h"
#include "content/browser/aggregation_service/aggregation_service_storage.h"
#include "content/browser/private_aggregation/private_aggregation_internals.mojom.h"
#include "content/browser/private_aggregation/private_aggregation_manager.h"
#include "content/public/browser/web_contents.h"
#include "content/public/browser/web_ui.h"
namespace content {
namespace {
AggregationService* GetAggregationService(WebContents* web_contents) {
CHECK(web_contents);
return AggregationService::GetService(web_contents->GetBrowserContext());
}
PrivateAggregationManager* GetPrivateAggregationManager(
WebContents* web_contents) {
CHECK(web_contents);
BrowserContext* browser_context = web_contents->GetBrowserContext();
CHECK(browser_context);
return PrivateAggregationManager::GetManager(*browser_context);
}
private_aggregation_internals::mojom::WebUIAggregatableReportPtr
CreateWebUIAggregatableReport(
const AggregatableReportRequest& request,
std::optional<AggregationServiceStorage::RequestId> id,
std::optional<base::Time> actual_report_time,
private_aggregation_internals::mojom::ReportStatus status,
const std::optional<AggregatableReport>& report) {
std::vector<private_aggregation_internals::mojom::
AggregatableHistogramContributionPtr>
contributions;
std::ranges::transform(request.payload_contents().contributions,
std::back_inserter(contributions),
[](const auto& contribution) {
return private_aggregation_internals::mojom::
AggregatableHistogramContribution::New(
contribution.bucket, contribution.value);
});
base::Value::Dict report_body;
if (report.has_value()) {
report_body = report->GetAsJson();
} else {
report_body = AggregatableReport(
/*payloads=*/{}, request.shared_info().SerializeAsJson(),
request.debug_key(), request.additional_fields(),
request.payload_contents().aggregation_coordinator_origin)
.GetAsJson();
constexpr char kAggregationServicePayloadsKey[] =
"aggregation_service_payloads";
CHECK(!report_body.Find(kAggregationServicePayloadsKey));
report_body.Set(kAggregationServicePayloadsKey,
"Not generated prior to send");
}
std::string output_json;
bool success = base::JSONWriter::WriteWithOptions(
report_body, base::JSONWriter::OPTIONS_PRETTY_PRINT, &output_json);
CHECK(success);
base::Time report_time =
actual_report_time.value_or(request.shared_info().scheduled_report_time);
return private_aggregation_internals::mojom::WebUIAggregatableReport::New(
id, report_time.InMillisecondsFSinceUnixEpoch(),
request.shared_info().api_identifier, request.shared_info().api_version,
request.GetReportingUrl(), std::move(contributions), status, output_json);
}
void ForwardReportsToWebUI(
private_aggregation_internals::mojom::Handler::GetReportsCallback
web_ui_callback,
std::vector<AggregationServiceStorage::RequestAndId> requests_and_ids) {
std::vector<private_aggregation_internals::mojom::WebUIAggregatableReportPtr>
web_ui_reports;
web_ui_reports.reserve(requests_and_ids.size());
for (const AggregationServiceStorage::RequestAndId& request_and_id :
requests_and_ids) {
web_ui_reports.push_back(CreateWebUIAggregatableReport(
request_and_id.request, request_and_id.id,
/*actual_report_time=*/std::nullopt,
private_aggregation_internals::mojom::ReportStatus::kPending,
/*report=*/std::nullopt));
}
std::move(web_ui_callback).Run(std::move(web_ui_reports));
}
} // namespace
PrivateAggregationInternalsHandlerImpl::PrivateAggregationInternalsHandlerImpl(
WebUI* web_ui,
mojo::PendingRemote<private_aggregation_internals::mojom::Observer>
observer,
mojo::PendingReceiver<private_aggregation_internals::mojom::Handler>
handler)
: web_ui_(web_ui),
observer_(std::move(observer)),
handler_(this, std::move(handler)) {
CHECK(web_ui);
if (AggregationService* aggregation_service =
GetAggregationService(web_ui_->GetWebContents())) {
aggregation_service_observer_.Observe(aggregation_service);
// `base::Unretained()` is safe because the observer is owned by `this`
// and the callback will only be called while `observer_` is still alive.
observer_.set_disconnect_handler(base::BindOnce(
&PrivateAggregationInternalsHandlerImpl::OnObserverDisconnected,
base::Unretained(this)));
}
}
PrivateAggregationInternalsHandlerImpl::
~PrivateAggregationInternalsHandlerImpl() = default;
void PrivateAggregationInternalsHandlerImpl::GetReports(
private_aggregation_internals::mojom::Handler::GetReportsCallback
callback) {
if (AggregationService* aggregation_service =
GetAggregationService(web_ui_->GetWebContents())) {
aggregation_service->GetPendingReportRequestsForWebUI(
base::BindOnce(&ForwardReportsToWebUI, std::move(callback)));
} else {
std::move(callback).Run({});
}
}
void PrivateAggregationInternalsHandlerImpl::SendReports(
const std::vector<AggregationServiceStorage::RequestId>& ids,
private_aggregation_internals::mojom::Handler::SendReportsCallback
callback) {
if (AggregationService* aggregation_service =
GetAggregationService(web_ui_->GetWebContents())) {
aggregation_service->SendReportsForWebUI(ids, std::move(callback));
} else {
std::move(callback).Run();
}
}
void PrivateAggregationInternalsHandlerImpl::ClearStorage(
private_aggregation_internals::mojom::Handler::ClearStorageCallback
callback) {
// Only run `callback` after we've cleared the aggregation service data *and*
// the private aggregation budget data.
auto barrier = base::BarrierClosure(/*num_closures=*/2, std::move(callback));
if (AggregationService* aggregation_service =
GetAggregationService(web_ui_->GetWebContents())) {
aggregation_service->ClearData(/*delete_begin=*/base::Time::Min(),
/*delete_end=*/base::Time::Max(),
/*filter=*/base::NullCallback(), barrier);
} else {
barrier.Run();
}
if (PrivateAggregationManager* private_aggregation_manager =
GetPrivateAggregationManager(web_ui_->GetWebContents())) {
private_aggregation_manager->ClearBudgetData(
/*delete_begin=*/base::Time::Min(),
/*delete_end=*/base::Time::Max(),
/*filter=*/base::NullCallback(), std::move(barrier));
} else {
std::move(barrier).Run();
}
}
void PrivateAggregationInternalsHandlerImpl::OnRequestStorageModified() {
observer_->OnRequestStorageModified();
}
void PrivateAggregationInternalsHandlerImpl::OnReportHandled(
const AggregatableReportRequest& request,
std::optional<AggregationServiceStorage::RequestId> id,
const std::optional<AggregatableReport>& report,
base::Time actual_report_time,
AggregationServiceObserver::ReportStatus status) {
private_aggregation_internals::mojom::ReportStatus web_report_status;
switch (status) {
case AggregationServiceObserver::ReportStatus::kSent:
web_report_status =
private_aggregation_internals::mojom::ReportStatus::kSent;
break;
case AggregationServiceObserver::ReportStatus::kFailedToAssemble:
web_report_status =
private_aggregation_internals::mojom::ReportStatus::kFailedToAssemble;
break;
case AggregationServiceObserver::ReportStatus::kFailedToSend:
web_report_status =
private_aggregation_internals::mojom::ReportStatus::kFailedToSend;
break;
}
observer_->OnReportHandled(CreateWebUIAggregatableReport(
request, id, actual_report_time, web_report_status, report));
}
void PrivateAggregationInternalsHandlerImpl::OnObserverDisconnected() {
aggregation_service_observer_.Reset();
}
} // namespace content