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

#include <stddef.h>
#include <stdint.h>

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

#include "base/check_op.h"
#include "base/containers/flat_set.h"
#include "base/notreached.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/parsing_utils.h"
#include "components/attribution_reporting/source_registration_error.mojom.h"
#include "components/attribution_reporting/trigger_registration_error.mojom.h"

namespace attribution_reporting {

namespace {

using ::attribution_reporting::mojom::SourceRegistrationError;
using ::attribution_reporting::mojom::TriggerRegistrationError;

enum class AttributionScopesError {
  kListWrongType,
  kScopeWrongType,
  kSetTooLong,
  kScopeTooLong,
};

base::expected<AttributionScopesSet, AttributionScopesError> ScopesFromJSON(
    base::Value::List* list,
    size_t max_string_size,
    size_t max_set_size) {
  if (!list) {
    return base::unexpected(AttributionScopesError::kListWrongType);
  }

  ASSIGN_OR_RETURN(
      base::flat_set<std::string> attribution_scopes,
      ExtractStringSet(std::move(*list), max_string_size, max_set_size)
          .transform_error([](StringSetError error) {
            switch (error) {
              case StringSetError::kSetTooLong:
                return AttributionScopesError::kSetTooLong;
              case StringSetError::kWrongType:
                return AttributionScopesError::kScopeWrongType;
              case StringSetError::kStringTooLong:
                return AttributionScopesError::kScopeTooLong;
            }
          }));

  return AttributionScopesSet(std::move(attribution_scopes));
}

void Serialize(const base::flat_set<std::string>& scopes,
               std::string_view key,
               base::Value::Dict& dict) {
  if (scopes.empty()) {
    return;
  }
  auto list = base::Value::List::with_capacity(scopes.size());
  for (const auto& scope : scopes) {
    list.Append(scope);
  }
  dict.Set(key, std::move(list));
}

}  // namespace

// static
base::expected<AttributionScopesSet, SourceRegistrationError>
AttributionScopesSet::FromJSON(base::Value::Dict& reg,
                               uint32_t attribution_scope_limit) {
  base::Value* scopes_value = reg.Find(kValues);
  if (!scopes_value) {
    return base::unexpected(
        SourceRegistrationError::kAttributionScopesListInvalid);
  }

  base::Value::List* scopes_list = scopes_value->GetIfList();
  if (!scopes_list || scopes_list->empty()) {
    return base::unexpected(
        SourceRegistrationError::kAttributionScopesListInvalid);
  }

  const size_t max_set_size = std::min(
      kMaxScopesPerSource, static_cast<size_t>(attribution_scope_limit));

  return ScopesFromJSON(scopes_list, kMaxLengthPerAttributionScope,
                        max_set_size)
      .transform_error([](AttributionScopesError error) {
        switch (error) {
          case AttributionScopesError::kListWrongType:
          case AttributionScopesError::kSetTooLong:
            return SourceRegistrationError::kAttributionScopesListInvalid;
          case AttributionScopesError::kScopeWrongType:
          case AttributionScopesError::kScopeTooLong:
            return SourceRegistrationError::kAttributionScopesListValueInvalid;
        }
      });
}

// static
base::expected<AttributionScopesSet, TriggerRegistrationError>
AttributionScopesSet::FromJSON(base::Value::Dict& reg) {
  base::Value* scopes_value = reg.Find(kAttributionScopes);
  if (!scopes_value) {
    return AttributionScopesSet();
  }

  base::Value::List* scopes_list = scopes_value->GetIfList();
  if (!scopes_list) {
    return base::unexpected(
        TriggerRegistrationError::kAttributionScopesInvalid);
  }

  return ScopesFromJSON(scopes_list,
                        /*max_string_size=*/std::numeric_limits<size_t>::max(),
                        /*max_set_size=*/std::numeric_limits<size_t>::max())
      .transform_error([](AttributionScopesError error) {
        switch (error) {
          case AttributionScopesError::kListWrongType:
            return TriggerRegistrationError::kAttributionScopesInvalid;
          case AttributionScopesError::kScopeWrongType:
            return TriggerRegistrationError::kAttributionScopesValueInvalid;
          case AttributionScopesError::kSetTooLong:
          case AttributionScopesError::kScopeTooLong:
            NOTREACHED();
        }
      });
}

AttributionScopesSet::AttributionScopesSet(Scopes scopes)
    : scopes_(std::move(scopes)) {}

AttributionScopesSet::AttributionScopesSet() = default;

AttributionScopesSet::~AttributionScopesSet() = default;

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

AttributionScopesSet::AttributionScopesSet(AttributionScopesSet&&) = default;

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

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

bool AttributionScopesSet::IsValidForSource(uint32_t scope_limit) const {
  CHECK_GT(scope_limit, 0u);
  return scopes_.size() <=
             std::min(kMaxScopesPerSource, static_cast<size_t>(scope_limit)) &&
         std::ranges::all_of(scopes_, [](const std::string& scope) {
           return scope.length() <= kMaxLengthPerAttributionScope;
         });
}

void AttributionScopesSet::SerializeForSource(base::Value::Dict& dict) const {
  Serialize(scopes_, kValues, dict);
}

void AttributionScopesSet::SerializeForTrigger(base::Value::Dict& dict) const {
  Serialize(scopes_, kAttributionScopes, dict);
}

// Rather than retrieving the whole intersection and checking its size using
// `std::set_intersection`, we iterate through and compare each element and
// early exit when two matching elements are found.
bool AttributionScopesSet::HasIntersection(
    const AttributionScopesSet& other_scopes) const {
  const auto& scopes_2 = other_scopes.scopes();
  if (scopes_.empty() || scopes_2.empty()) {
    return false;
  }

  AttributionScopesSet::Scopes::const_iterator it_1 = scopes_.begin(),
                                               it_1_end = scopes_.end();
  AttributionScopesSet::Scopes::const_iterator it_2 = scopes_2.begin(),
                                               it_2_end = scopes_2.end();

  if (*it_1 > *scopes_2.rbegin() || *it_2 > *scopes_.rbegin()) {
    return false;
  }

  while (it_1 != it_1_end && it_2 != it_2_end) {
    if (*it_1 == *it_2) {
      return true;
    }
    if (*it_1 < *it_2) {
      it_1++;
    } else {
      it_2++;
    }
  }
  return false;
}

}  // namespace attribution_reporting