910e62b5创建于 1月15日历史提交
// Copyright 2023 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "components/attribution_reporting/destination_set.h"

#include <algorithm>
#include <optional>
#include <string>
#include <utility>

#include "base/check.h"
#include "base/containers/flat_set.h"
#include "base/types/expected.h"
#include "base/types/expected_macros.h"
#include "base/values.h"
#include "components/attribution_reporting/constants.h"
#include "components/attribution_reporting/source_registration_error.mojom-shared.h"
#include "components/attribution_reporting/suitable_origin.h"
#include "mojo/public/cpp/bindings/default_construct_tag.h"
#include "net/base/schemeful_site.h"
#include "third_party/abseil-cpp/absl/functional/overload.h"

namespace attribution_reporting {

namespace {

using ::attribution_reporting::mojom::SourceRegistrationError;

bool DestinationsValid(const DestinationSet::Destinations& destinations) {
  return !destinations.empty() && destinations.size() <= kMaxDestinations &&
         std::ranges::all_of(destinations, &IsSitePotentiallySuitable);
}

}  // namespace

// static
std::optional<DestinationSet> DestinationSet::Create(
    Destinations destinations) {
  if (!DestinationsValid(destinations)) {
    return std::nullopt;
  }
  return DestinationSet(std::move(destinations));
}

// static
base::expected<DestinationSet, SourceRegistrationError>
DestinationSet::FromJSON(const base::Value* v) {
  if (!v) {
    return base::unexpected(SourceRegistrationError::kDestinationMissing);
  }

  // Although we build this set iteratively, which results in O(n^2)
  // construction, n is very small, so this is fine.
  static_assert(kMaxDestinations == 3,
                "Consider using more performant set construction if the size "
                "limit increases.");
  base::flat_set<net::SchemefulSite> destination_sites;

  using AppendIfValidResult = base::expected<void, SourceRegistrationError>;

  const auto append_if_valid =
      [&](const std::string& str,
          SourceRegistrationError error) -> AppendIfValidResult {
    auto origin = SuitableOrigin::Deserialize(str);
    if (!origin.has_value()) {
      return base::unexpected(error);
    }
    destination_sites.emplace(*origin);
    return base::ok();
  };

  RETURN_IF_ERROR(v->Visit(absl::Overload{
      [&](const std::string& str) {
        return append_if_valid(
            str, SourceRegistrationError::kDestinationUntrustworthy);
      },
      [&](const base::Value::List& list) -> AppendIfValidResult {
        if (list.empty()) {
          return base::unexpected(
              SourceRegistrationError::kDestinationWrongType);
        }

        destination_sites.reserve(std::min(list.size(), kMaxDestinations));

        for (const auto& item : list) {
          const std::string* str = item.GetIfString();
          if (!str) {
            return base::unexpected(
                SourceRegistrationError::kDestinationWrongType);
          }
          RETURN_IF_ERROR(append_if_valid(
              *str, SourceRegistrationError::kDestinationListUntrustworthy));

          if (destination_sites.size() > kMaxDestinations) {
            return base::unexpected(
                SourceRegistrationError::kDestinationWrongType);
          }
        }

        return base::ok();
      },
      [](const auto&) -> AppendIfValidResult {
        return base::unexpected(SourceRegistrationError::kDestinationWrongType);
      },
  }));

  return DestinationSet(std::move(destination_sites));
}

DestinationSet::DestinationSet(Destinations destinations)
    : destinations_(std::move(destinations)) {
  CHECK(IsValid());
}

DestinationSet::DestinationSet(mojo::DefaultConstruct::Tag) {
  CHECK(!IsValid());
}

DestinationSet::~DestinationSet() = default;

DestinationSet::DestinationSet(const DestinationSet&) = default;

DestinationSet::DestinationSet(DestinationSet&&) = default;

DestinationSet& DestinationSet::operator=(const DestinationSet&) = default;

DestinationSet& DestinationSet::operator=(DestinationSet&&) = default;

bool DestinationSet::IsValid() const {
  return DestinationsValid(destinations_);
}

base::Value DestinationSet::ToJson() const {
  CHECK(IsValid());
  if (destinations_.size() == 1) {
    return base::Value(destinations_.begin()->Serialize());
  }

  auto list = base::Value::List::with_capacity(destinations_.size());
  for (const auto& destination : destinations_) {
    list.Append(destination.Serialize());
  }
  return base::Value(std::move(list));
}

}  // namespace attribution_reporting