blob: bef42c6dd0c1024843ecd4904300e31cca1d8f61 [file] [log] [blame]
// 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 <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/ranges/algorithm.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)) &&
base::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