910e62b5创建于 1月15日历史提交
// 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