// 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. #ifndef THIRD_PARTY_BLINK_PUBLIC_COMMON_PRIVACY_BUDGET_IDENTIFIABLE_TOKEN_H_ #define THIRD_PARTY_BLINK_PUBLIC_COMMON_PRIVACY_BUDGET_IDENTIFIABLE_TOKEN_H_ #include <cstdint> #include <string_view> #include <type_traits> #include "base/containers/span.h" #include "base/numerics/safe_conversions.h" #include "third_party/blink/public/common/privacy_budget/identifiability_internal_templates.h" #include "third_party/blink/public/common/privacy_budget/identifiability_metrics.h" namespace blink { // Constructs a token that can be used for reporting a metric or constructing an // identifiable surface. // // The token construction is a single step conversion that takes one of several // constrained inputs and emits a value. The method by which the value is // constructed intentionally cannot be chained. If such behavior is required, // then this class should be modified to accommodate the new use case rather // than implementing custom chaining schemes at call sites. // // Once constructed, a token can only be consumed by // IdentifiabiltyMetricsBuilder and IdentifiableSurface. For all others, it is a // copyable, opaque token. // // Reliance on implicit conversion imposes limitations on how // IdentifiableToken class is to be used. For example the following works: // // std::string foo = ....; // IdentifiableToken sample(foo); // // .. due to the following implicit conversion: // // 1. std::string -> const std::string& // : lvalue -> lvalue reference + cv-qualification // 2. const std::string& -> std::string_view // : user-defined conversion via constructor // std::string_view(const std::string&) // // However, when used within a builder expression, the user-defined conversion // doesn't occur due to there not being a single user defined conversion from // std::string -> IdentifiableToken. I.e. the following does not work: // // std::string foo = ....; // IdentifiabilityMetricBuilder(...).Set(surface, foo); // ^^^ // The compiler can't deduce a two step user-defined conversion for |foo|. // // All overrides of the constructor should ensure that there exists a unique // representation of the data type being sampled, and that the sample value is // constructed based on this unique representation. // // TODO(asanka): Also require that the representation be portable. // // Extending IdentifiableToken to support more data types: // ----------------------------------------------------------- // // This class is intentionally placed in blink/public/common due to the // requirement that these primitives be made available to both the renderer and // the browser. However, it would be desirable to have renderer or browser // specific functions for mapping common types in either domain into a sample. // // The recommended methods to do so are (one-of): // // 1. Use an existing byte span representation. // // E.g.: Assuming |v| is a WTF::Vector // IdentifiabilityMetricBuilder(...).Set(..., // base::as_bytes(base::make_span(v.Data(), v.Size()))); // // Note again that serializing to a stream of bytes may not be sufficient // if the underlying types don't have a unique representation. // // 2. Construct a byte-wise unique representation and invoke // IdentifiableToken(ByteSpan) either explicitly or implicitly via // user-defined conversions. // // Note: Avoid doing template magic. There's already too much here. Templates // make it difficult to verify that the correct stable representation is // the one getting ingested into the reporting workflow. // // Instead, explicitly invoke some wrapper that emits a ByteSpan (a.k.a. // base::span<const uint8_t>. class IdentifiableToken { … }; } // namespace blink #endif // THIRD_PARTY_BLINK_PUBLIC_COMMON_PRIVACY_BUDGET_IDENTIFIABLE_TOKEN_H_