910e62b5创建于 1月15日历史提交
// Copyright 2020 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/attribution_reporting/attribution_internals_handler_impl.h"

#include <stdint.h>

#include <algorithm>
#include <iterator>
#include <optional>
#include <string>
#include <utility>
#include <variant>
#include <vector>

#include "base/check.h"
#include "base/check_op.h"
#include "base/containers/flat_map.h"
#include "base/containers/to_vector.h"
#include "base/functional/bind.h"
#include "base/functional/callback.h"
#include "base/functional/callback_helpers.h"
#include "base/memory/raw_ref.h"
#include "base/memory/weak_ptr.h"
#include "base/notreached.h"
#include "base/numerics/safe_conversions.h"
#include "base/time/time.h"
#include "base/values.h"
#include "components/attribution_reporting/aggregation_keys.h"
#include "components/attribution_reporting/os_registration.h"
#include "components/attribution_reporting/parsing_utils.h"
#include "components/attribution_reporting/source_registration.h"
#include "components/attribution_reporting/suitable_origin.h"
#include "components/attribution_reporting/trigger_config.h"
#include "components/attribution_reporting/trigger_registration.h"
#include "content/browser/attribution_reporting/aggregatable_debug_report.h"
#include "content/browser/attribution_reporting/aggregatable_named_budget_pair.h"
#include "content/browser/attribution_reporting/attribution_debug_report.h"
#include "content/browser/attribution_reporting/attribution_info.h"
#include "content/browser/attribution_reporting/attribution_internals.mojom.h"
#include "content/browser/attribution_reporting/attribution_manager.h"
#include "content/browser/attribution_reporting/attribution_report.h"
#include "content/browser/attribution_reporting/attribution_reporting.mojom-forward.h"
#include "content/browser/attribution_reporting/attribution_trigger.h"
#include "content/browser/attribution_reporting/attribution_utils.h"
#include "content/browser/attribution_reporting/common_source_info.h"
#include "content/browser/attribution_reporting/create_report_result.h"
#include "content/browser/attribution_reporting/os_registration.h"
#include "content/browser/attribution_reporting/send_result.h"
#include "content/browser/attribution_reporting/storable_source.h"
#include "content/browser/attribution_reporting/stored_source.h"
#include "content/browser/web_contents/web_contents_impl.h"
#include "content/public/browser/browser_context.h"
#include "content/public/browser/content_browser_client.h"
#include "content/public/browser/web_contents.h"
#include "content/public/browser/web_ui.h"
#include "content/public/common/content_client.h"
#include "net/base/net_errors.h"
#include "third_party/abseil-cpp/absl/functional/overload.h"
#include "third_party/abseil-cpp/absl/numeric/int128.h"
#include "url/gurl.h"
#include "url/origin.h"

namespace content {

namespace {

using Attributability =
    ::attribution_internals::mojom::WebUISource::Attributability;

using Empty = ::attribution_internals::mojom::Empty;
using ReportStatus = ::attribution_internals::mojom::ReportStatus;
using ReportStatusPtr = ::attribution_internals::mojom::ReportStatusPtr;

using ::attribution_internals::mojom::WebUIAggregatableDebugReport;
using ::attribution_internals::mojom::WebUIDebugReport;

std::string SerializeBudgetsMap(
    const StoredSource::AggregatableNamedBudgets& map) {
  base::Value::Dict dict;
  for (const auto& [key, value] : map) {
    base::Value::Dict inner_dict;
    inner_dict.Set("original_budget", value.original_budget());
    inner_dict.Set("remaining_budget", value.remaining_budget());
    dict.Set(key, std::move(inner_dict));
  }

  return SerializeAttributionJson(dict, /*pretty_print=*/true);
}

attribution_internals::mojom::WebUISourcePtr WebUISource(
    const StoredSource& source,
    Attributability attributability) {
  const CommonSourceInfo& common_info = source.common_info();
  return attribution_internals::mojom::WebUISource::New(
      *source.source_id(), source.source_event_id(),
      common_info.source_origin(), source.destination_sites(),
      common_info.reporting_origin(),
      source.source_time().InMillisecondsFSinceUnixEpoch(),
      source.expiry_time().InMillisecondsFSinceUnixEpoch(),
      source.event_report_windows(),
      base::ToVector(source.trigger_data().trigger_data()),
      source.max_event_level_reports(),
      source.aggregatable_report_window_time().InMillisecondsFSinceUnixEpoch(),
      common_info.source_type(), source.priority(), source.debug_key(),
      source.dedup_keys(), source.filter_data(),
      base::MakeFlatMap<std::string, std::string>(
          source.aggregation_keys().keys(), {},
          [](const auto& key) {
            return std::make_pair(
                key.first,
                attribution_reporting::HexEncodeAggregationKey(key.second));
          }),
      source.remaining_aggregatable_attribution_budget(),
      source.aggregatable_dedup_keys(), source.trigger_data_matching(),
      source.event_level_epsilon(),
      source.common_info().cookie_based_debug_allowed(),
      source.remaining_aggregatable_debug_budget(),
      attribution_reporting::HexEncodeAggregationKey(
          source.aggregatable_debug_key_piece()),
      source.attribution_scopes_data().has_value()
          ? SerializeAttributionJson(source.attribution_scopes_data()->ToJson(),
                                     /*pretty_print=*/true)
          : "null",
      SerializeBudgetsMap(source.aggregatable_named_budgets()),
      attributability);
}

std::vector<attribution_internals::mojom::WebUISourcePtr> ToWebUISources(
    const std::vector<StoredSource>& active_sources) {
  return base::ToVector(active_sources, [](const StoredSource& source) {
    Attributability attributability;
    switch (source.attribution_logic()) {
      case StoredSource::AttributionLogic::kTruthfully:
        attributability = Attributability::kAttributable;
        break;
      case StoredSource::AttributionLogic::kNever:
        attributability = Attributability::kNoisedNever;
        break;
      case StoredSource::AttributionLogic::kFalsely:
        attributability = Attributability::kNoisedFalsely;
        break;
    }

    if (attributability == Attributability::kAttributable) {
      switch (source.active_state()) {
        case StoredSource::ActiveState::kActive:
          attributability = Attributability::kAttributable;
          break;
        case StoredSource::ActiveState::kReachedEventLevelAttributionLimit:
          attributability = Attributability::kReachedEventLevelAttributionLimit;
          break;
        case StoredSource::ActiveState::kInactive:
          NOTREACHED();
      }
    }

    return WebUISource(source, attributability);
  });
}

attribution_internals::mojom::WebUIReportPtr WebUIReport(
    const AttributionReport& report,
    bool is_debug_report,
    ReportStatusPtr status) {
  namespace ai_mojom = attribution_internals::mojom;

  const AttributionInfo& attribution_info = report.attribution_info();

  ai_mojom::WebUIReportDataPtr data = std::visit(
      absl::Overload{
          [](const AttributionReport::EventLevelData& event_level_data) {
            return ai_mojom::WebUIReportData::NewEventLevelData(
                ai_mojom::WebUIReportEventLevelData::New(
                    event_level_data.priority,
                    event_level_data.attributed_truthfully));
          },

          [](const AttributionReport::AggregatableData& aggregatable_data) {
            std::vector<ai_mojom::AggregatableHistogramContributionPtr>
                contributions;

            if (aggregatable_data.is_null()) {
              contributions.push_back(
                  ai_mojom::AggregatableHistogramContribution::New(
                      attribution_reporting::HexEncodeAggregationKey(0),
                      /*value=*/0,
                      /*filtering_id=*/0));
            } else {
              contributions = base::ToVector(
                  aggregatable_data.contributions(),
                  [](const auto& contribution) {
                    return ai_mojom::AggregatableHistogramContribution::New(
                        attribution_reporting::HexEncodeAggregationKey(
                            contribution.bucket),
                        base::checked_cast<uint32_t>(contribution.value),
                        contribution.filtering_id.value_or(0));
                  });
            }

            return ai_mojom::WebUIReportData::NewAggregatableAttributionData(
                ai_mojom::WebUIReportAggregatableAttributionData::New(
                    std::move(contributions),
                    aggregatable_data.aggregation_coordinator_origin()
                        ? aggregatable_data.aggregation_coordinator_origin()
                              ->Serialize()
                        : "",
                    aggregatable_data.is_null()));
          },
      },
      report.data());

  return attribution_internals::mojom::WebUIReport::New(
      report.id(), report.ReportURL(is_debug_report),
      /*trigger_time=*/attribution_info.time.InMillisecondsFSinceUnixEpoch(),
      /*report_time=*/report.report_time().InMillisecondsFSinceUnixEpoch(),
      SerializeAttributionJson(report.ReportBody(), /*pretty_print=*/true),
      std::move(status), std::move(data));
}

std::vector<attribution_internals::mojom::WebUIReportPtr> ToWebUIReports(
    const std::vector<AttributionReport>& pending_reports) {
  return base::ToVector(pending_reports, [](const AttributionReport& report) {
    return WebUIReport(report, /*is_debug_report=*/false,
                       ReportStatus::NewPending(Empty::New()));
  });
}

attribution_internals::mojom::NetworkStatusPtr NetworkStatus(int status) {
  return status >= 0
             ? attribution_internals::mojom::NetworkStatus::NewHttpResponseCode(
                   status)
             : attribution_internals::mojom::NetworkStatus::NewNetworkError(
                   net::ErrorToShortString(status));
}

}  // namespace

AttributionInternalsHandlerImpl::AttributionInternalsHandlerImpl(
    WebUI* web_ui,
    mojo::PendingRemote<attribution_internals::mojom::Observer> observer,
    mojo::PendingReceiver<attribution_internals::mojom::Handler> handler)
    : web_ui_(raw_ref<WebUI>::from_ptr(web_ui)),
      observer_(std::move(observer)),
      handler_(this, std::move(handler)) {
  if (auto* manager =
          AttributionManager::FromWebContents(web_ui_->GetWebContents())) {
    manager_observation_.Observe(manager);
    observer_.set_disconnect_handler(
        base::BindOnce(&AttributionInternalsHandlerImpl::OnObserverDisconnected,
                       base::Unretained(this)));
    OnSourcesChanged();
    OnReportsChanged();
  }
}

AttributionInternalsHandlerImpl::~AttributionInternalsHandlerImpl() = default;

void AttributionInternalsHandlerImpl::IsAttributionReportingEnabled(
    attribution_internals::mojom::Handler::IsAttributionReportingEnabledCallback
        callback) {
  content::WebContents* contents = web_ui_->GetWebContents();
  bool attribution_reporting_enabled =
      AttributionManager::FromWebContents(contents) &&
      GetContentClient()->browser()->IsAttributionReportingOperationAllowed(
          contents->GetBrowserContext(),
          ContentBrowserClient::AttributionReportingOperation::kAny,
          /*rfh=*/nullptr, /*source_origin=*/nullptr,
          /*destination_origin=*/nullptr, /*reporting_origin=*/nullptr,
          /*can_bypass=*/nullptr);

  std::move(callback).Run(
      attribution_reporting_enabled,
      static_cast<WebContentsImpl*>(contents)->GetAttributionSupport());
}

void AttributionInternalsHandlerImpl::OnDebugModeChanged(bool debug_mode) {
  observer_->OnDebugModeChanged(debug_mode);
}

void AttributionInternalsHandlerImpl::OnSourcesChanged() {
  if (AttributionManager* manager =
          AttributionManager::FromWebContents(web_ui_->GetWebContents())) {
    manager->GetActiveSourcesForWebUI(base::BindOnce(
        [](base::WeakPtr<AttributionInternalsHandlerImpl> handler,
           std::vector<StoredSource> sources) {
          if (handler) {
            handler->observer_->OnSourcesChanged(ToWebUISources(sources));
          }
        },
        weak_ptr_factory_.GetWeakPtr()));
  }
}

void AttributionInternalsHandlerImpl::OnReportsChanged() {
  if (AttributionManager* manager =
          AttributionManager::FromWebContents(web_ui_->GetWebContents())) {
    manager->GetPendingReportsForInternalUse(
        /*limit=*/1000,
        base::BindOnce(
            [](base::WeakPtr<AttributionInternalsHandlerImpl> handler,
               std::vector<AttributionReport> reports) {
              if (handler) {
                handler->observer_->OnReportsChanged(ToWebUIReports(reports));
              }
            },
            weak_ptr_factory_.GetWeakPtr()));
  }
}

void AttributionInternalsHandlerImpl::SendReport(
    AttributionReport::Id id,
    attribution_internals::mojom::Handler::SendReportCallback callback) {
  if (AttributionManager* manager =
          AttributionManager::FromWebContents(web_ui_->GetWebContents())) {
    manager->SendReportForWebUI(id, std::move(callback));
  } else {
    std::move(callback).Run();
  }
}

void AttributionInternalsHandlerImpl::ClearStorage(
    attribution_internals::mojom::Handler::ClearStorageCallback callback) {
  if (AttributionManager* manager =
          AttributionManager::FromWebContents(web_ui_->GetWebContents())) {
    manager->ClearData(base::Time::Min(), base::Time::Max(),
                       /*filter=*/base::NullCallback(),
                       /*filter_builder=*/nullptr,
                       /*delete_rate_limit_data=*/true, std::move(callback));
  } else {
    std::move(callback).Run();
  }
}

namespace {

using WebUISourceRegistration =
    ::attribution_internals::mojom::WebUISourceRegistration;

attribution_internals::mojom::WebUIRegistrationPtr GetRegistration(
    base::Time time,
    const attribution_reporting::SuitableOrigin& context_origin,
    const attribution_reporting::SuitableOrigin& reporting_origin,
    std::string registration_json,
    std::optional<uint64_t> cleared_debug_key) {
  auto reg = attribution_internals::mojom::WebUIRegistration::New();
  reg->time = time.InMillisecondsFSinceUnixEpoch();
  reg->context_origin = context_origin;
  reg->reporting_origin = reporting_origin;
  reg->registration_json = std::move(registration_json);
  reg->cleared_debug_key = cleared_debug_key;
  return reg;
}

}  // namespace

void AttributionInternalsHandlerImpl::OnSourceHandled(
    const StorableSource& source,
    base::Time source_time,
    std::optional<uint64_t> cleared_debug_key,
    attribution_reporting::mojom::StoreSourceResult result) {
  auto web_ui_source = WebUISourceRegistration::New();
  web_ui_source->registration =
      GetRegistration(source_time, source.common_info().source_origin(),
                      source.common_info().reporting_origin(),
                      SerializeAttributionJson(source.registration().ToJson(),
                                               /*pretty_print=*/true),
                      cleared_debug_key);
  web_ui_source->type = source.common_info().source_type();
  web_ui_source->status = std::move(result);

  observer_->OnSourceHandled(std::move(web_ui_source));
}

void AttributionInternalsHandlerImpl::OnReportSent(
    const AttributionReport& report,
    bool is_debug_report,
    const SendResult& info) {
  ReportStatusPtr status = std::visit(
      absl::Overload{
          [](SendResult::Sent sent) {
            return ReportStatus::NewNetworkStatus(NetworkStatus(sent.status));
          },
          [](SendResult::Expired) {
            return ReportStatus::NewExpired(Empty::New());
          },
          [](SendResult::Dropped) {
            return ReportStatus::NewProhibitedByBrowserPolicy(Empty::New());
          },
          [](SendResult::AssemblyFailure) {
            return ReportStatus::NewFailedToAssemble(Empty::New());
          },
      },
      info.result);

  observer_->OnReportHandled(
      WebUIReport(report, is_debug_report, std::move(status)));
}

void AttributionInternalsHandlerImpl::OnDebugReportSent(
    const AttributionDebugReport& report,
    int status,
    base::Time time) {
  auto web_report = WebUIDebugReport::New();
  web_report->url = report.ReportUrl();
  web_report->time = time.InMillisecondsFSinceUnixEpoch();
  web_report->body =
      SerializeAttributionJson(report.ReportBody(), /*pretty_print=*/true);
  web_report->status = NetworkStatus(status);

  observer_->OnDebugReportSent(std::move(web_report));
}

void AttributionInternalsHandlerImpl::OnAggregatableDebugReportSent(
    const AggregatableDebugReport& report,
    base::ValueView report_body,
    attribution_reporting::mojom::ProcessAggregatableDebugReportResult
        process_result,
    const SendAggregatableDebugReportResult& send_result) {
  auto web_report = WebUIAggregatableDebugReport::New();
  web_report->url = report.ReportUrl();
  web_report->time =
      report.scheduled_report_time().InMillisecondsFSinceUnixEpoch();
  web_report->body =
      SerializeAttributionJson(report_body, /*pretty_print=*/true);
  web_report->process_result = process_result;

  web_report->send_result = std::visit(
      absl::Overload{
          [](const SendAggregatableDebugReportResult::Sent& sent) {
            return attribution_internals::mojom::
                SendAggregatableDebugReportResult::NewNetworkStatus(
                    NetworkStatus(sent.status));
          },
          [](const SendAggregatableDebugReportResult::AssemblyFailed&) {
            return attribution_internals::mojom::
                SendAggregatableDebugReportResult::NewAssemblyFailed(
                    Empty::New());
          },
      },
      send_result.result);

  observer_->OnAggregatableDebugReportSent(std::move(web_report));
}

void AttributionInternalsHandlerImpl::OnOsRegistration(
    base::Time time,
    const attribution_reporting::OsRegistrationItem& registration,
    const url::Origin& top_level_origin,
    attribution_reporting::mojom::RegistrationType type,
    bool is_debug_key_allowed,
    attribution_reporting::mojom::OsRegistrationResult result) {
  auto web_ui_os_registration =
      attribution_internals::mojom::WebUIOsRegistration::New();
  web_ui_os_registration->time =
      time.InMillisecondsFSinceUnixEpochIgnoringNull();
  web_ui_os_registration->registration_url = registration.url;
  web_ui_os_registration->top_level_origin = top_level_origin;
  web_ui_os_registration->is_debug_key_allowed = is_debug_key_allowed;
  web_ui_os_registration->debug_reporting = registration.debug_reporting;
  web_ui_os_registration->type = type;
  web_ui_os_registration->result = result;

  observer_->OnOsRegistration(std::move(web_ui_os_registration));
}

void AttributionInternalsHandlerImpl::OnTriggerHandled(
    const std::optional<uint64_t> cleared_debug_key,
    const CreateReportResult& result) {
  const AttributionTrigger& trigger = result.trigger();
  const attribution_reporting::TriggerRegistration& registration =
      trigger.registration();

  auto web_ui_trigger = attribution_internals::mojom::WebUITrigger::New();
  web_ui_trigger->registration =
      GetRegistration(result.trigger_time(), trigger.destination_origin(),
                      trigger.reporting_origin(),
                      SerializeAttributionJson(registration.ToJson(),
                                               /*pretty_print=*/true),
                      cleared_debug_key);
  web_ui_trigger->event_level_result = result.event_level_status();
  web_ui_trigger->aggregatable_result = result.aggregatable_status();

  observer_->OnTriggerHandled(std::move(web_ui_trigger));

  if (const AttributionReport* report = result.replaced_event_level_report()) {
    CHECK_EQ(
        result.event_level_status(),
        AttributionTrigger::EventLevelResult::kSuccessDroppedLowerPriority);
    CHECK(result.new_event_level_report());

    observer_->OnReportHandled(
        WebUIReport(*report, /*is_debug_report=*/false,
                    ReportStatus::NewReplacedByHigherPriorityReport(
                        result.new_event_level_report()
                            ->external_report_id()
                            .AsLowercaseString())));
  }
}

void AttributionInternalsHandlerImpl::OnObserverDisconnected() {
  manager_observation_.Reset();
}

}  // namespace content