blob: f2e9a140c2e5c58a21780eb0a82a6858b32d06f2 [file] [log] [blame]
// 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.
#ifndef COMPONENTS_ATTRIBUTION_REPORTING_TRIGGER_CONFIG_H_
#define COMPONENTS_ATTRIBUTION_REPORTING_TRIGGER_CONFIG_H_
#include <stdint.h>
#include <cstddef>
#include <iterator>
#include <utility>
#include <vector>
#include "base/component_export.h"
#include "base/containers/flat_map.h"
#include "base/memory/raw_ref.h"
#include "base/types/expected.h"
#include "base/values.h"
#include "components/attribution_reporting/event_report_windows.h"
#include "components/attribution_reporting/max_event_level_reports.h"
#include "components/attribution_reporting/source_registration_error.mojom-forward.h"
#include "components/attribution_reporting/source_type.mojom-forward.h"
#include "components/attribution_reporting/trigger_data_matching.mojom-forward.h"
namespace base {
class TimeDelta;
} // namespace base
namespace attribution_reporting {
class COMPONENT_EXPORT(ATTRIBUTION_REPORTING) TriggerSpec {
public:
TriggerSpec();
explicit TriggerSpec(EventReportWindows);
~TriggerSpec();
TriggerSpec(const TriggerSpec&);
TriggerSpec& operator=(const TriggerSpec&);
TriggerSpec(TriggerSpec&&);
TriggerSpec& operator=(TriggerSpec&&);
const EventReportWindows& event_report_windows() const {
return event_report_windows_;
}
base::Value::Dict ToJson() const;
friend bool operator==(const TriggerSpec&, const TriggerSpec&) = default;
private:
EventReportWindows event_report_windows_;
};
// Conceptually a map from `uint32_t` trigger data values to `TriggerSpec`s.
class COMPONENT_EXPORT(ATTRIBUTION_REPORTING) TriggerSpecs {
public:
using TriggerDataIndices = base::flat_map<uint32_t, uint8_t>;
using value_type = std::pair<uint32_t, const TriggerSpec&>;
// TODO: Merge `ParseTopLevelTriggerData()` into this function and rename it
// to `Parse()`.
static base::expected<TriggerSpecs, mojom::SourceRegistrationError>
ParseFullFlexForTesting(const base::Value::Dict&,
mojom::SourceType,
base::TimeDelta expiry,
EventReportWindows default_report_windows,
mojom::TriggerDataMatching);
// Parses the top-level `trigger_data` field. The resulting value is either
// `empty()` or `SingleSharedSpec()`.
static base::expected<TriggerSpecs, mojom::SourceRegistrationError>
ParseTopLevelTriggerData(const base::Value::Dict&,
mojom::SourceType,
EventReportWindows default_report_windows,
mojom::TriggerDataMatching);
static std::optional<TriggerSpecs> Create(TriggerDataIndices,
std::vector<TriggerSpec>,
MaxEventLevelReports);
// Creates specs matching no trigger data.
TriggerSpecs();
// Creates specs with the default trigger data cardinality for the given
// source type.
TriggerSpecs(mojom::SourceType, EventReportWindows, MaxEventLevelReports);
~TriggerSpecs();
TriggerSpecs(const TriggerSpecs&);
TriggerSpecs& operator=(const TriggerSpecs&);
TriggerSpecs(TriggerSpecs&&);
TriggerSpecs& operator=(TriggerSpecs&&);
bool empty() const { return trigger_data_indices_.empty(); }
size_t size() const { return trigger_data_indices_.size(); }
// Will return nullptr if there is not a single shared spec.
const TriggerSpec* SingleSharedSpec() const;
base::Value::List ToJson() const;
void Serialize(base::Value::Dict&) const;
class COMPONENT_EXPORT(ATTRIBUTION_REPORTING) Iterator {
public:
using iterator_category = std::forward_iterator_tag;
using difference_type = std::ptrdiff_t;
using value_type = TriggerSpecs::value_type;
// This is *not* a reference type because the values are produced on demand.
using reference = value_type;
using pointer = void;
reference operator*() const {
return value_type(it_->first, specs_->specs()[it_->second]);
}
Iterator& operator++() {
it_++;
return *this;
}
Iterator operator++(int) {
Iterator tmp = *this;
++(*this);
return tmp;
}
// Returns the index of the current trigger_data in the sorted list of all
// trigger_data.
uint8_t index() const {
return base::checked_cast<uint8_t>(
std::distance(specs_->trigger_data_indices().cbegin(), it_));
}
explicit operator bool() const {
return it_ != specs_->trigger_data_indices().end();
}
friend bool operator==(const Iterator& a, const Iterator& b) {
return a.it_ == b.it_;
}
friend bool operator!=(const Iterator& a, const Iterator& b) {
return a.it_ != b.it_;
}
private:
friend TriggerSpecs;
Iterator(const TriggerSpecs&, TriggerDataIndices::const_iterator);
const raw_ref<const TriggerSpecs> specs_;
TriggerDataIndices::const_iterator it_;
};
using const_iterator = Iterator;
const_iterator begin() const {
return Iterator(*this, trigger_data_indices_.cbegin());
}
const_iterator end() const {
return Iterator(*this, trigger_data_indices_.cend());
}
// Returns the matching trigger spec and its associated trigger data, if any.
// Returns `TriggerSpecs::end()` if there is no match.
//
// Accepts a 64-bit integer instead of a 32-bit one for backward
// compatibility with pre-Flex triggers that supply the full range.
//
// Note: `TriggerDataMatching::kModulus` can still be applied
// even if the trigger data does not form a contiguous range starting at 0.
// Such a combination is prohibited by `TriggerSpecs::Parse()`, but there is
// still a well-defined meaning for it for arbitrary trigger data, so we do
// not bother preventing it here, though we may do so in the future.
const_iterator find(uint64_t trigger_data, mojom::TriggerDataMatching) const;
const TriggerDataIndices& trigger_data_indices() const {
return trigger_data_indices_;
}
const std::vector<TriggerSpec>& specs() const { return specs_; }
MaxEventLevelReports max_event_level_reports() const {
return max_event_level_reports_;
}
void SetMaxEventLevelReportsForTesting(
MaxEventLevelReports max_event_level_reports) {
max_event_level_reports_ = max_event_level_reports;
}
friend bool operator==(const TriggerSpecs&, const TriggerSpecs&) = default;
private:
TriggerSpecs(TriggerDataIndices,
std::vector<TriggerSpec>,
MaxEventLevelReports);
// These two fields effectively act as a compressed `base::flat_map<uint32_t,
// scoped_refptr<TriggerSpec>>`, optimized for the fact that there are at most
// 32 specs, meaning that their indices fit into a `uint8_t`. Storing the
// `TriggerSpec`s in a separate vector also makes it easier to regroup the
// individual keys by spec, because those specs will have the same index in
// `specs_`. Without that, serialization, e.g. in the
// `TriggerSpecs::Serialize()` method and the forthcoming analogue for
// persistence on disk, would have to perform an additional group-by operation
// using `scoped_refptr` address.
TriggerDataIndices trigger_data_indices_;
std::vector<TriggerSpec> specs_;
MaxEventLevelReports max_event_level_reports_;
};
COMPONENT_EXPORT(ATTRIBUTION_REPORTING)
base::expected<mojom::TriggerDataMatching, mojom::SourceRegistrationError>
ParseTriggerDataMatching(const base::Value::Dict&);
COMPONENT_EXPORT(ATTRIBUTION_REPORTING)
void Serialize(base::Value::Dict&, mojom::TriggerDataMatching);
} // namespace attribution_reporting
#endif // COMPONENTS_ATTRIBUTION_REPORTING_TRIGGER_CONFIG_H_