tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

SyncedContext.h (16540B)


      1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
      2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
      3 /* This Source Code Form is subject to the terms of the Mozilla Public
      4 * License, v. 2.0. If a copy of the MPL was not distributed with this
      5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      6 
      7 #ifndef mozilla_dom_SyncedContext_h
      8 #define mozilla_dom_SyncedContext_h
      9 
     10 #include <array>
     11 #include <type_traits>
     12 #include <utility>
     13 #include "mozilla/BitSet.h"
     14 #include "mozilla/EnumSet.h"
     15 #include "nsStringFwd.h"
     16 #include "nscore.h"
     17 
     18 // Referenced via macro definitions
     19 #include "mozilla/ErrorResult.h"
     20 
     21 class PickleIterator;
     22 
     23 namespace IPC {
     24 class Message;
     25 class MessageReader;
     26 class MessageWriter;
     27 template <typename T>
     28 struct ParamTraits;
     29 }  // namespace IPC
     30 
     31 namespace mozilla {
     32 namespace ipc {
     33 class IProtocol;
     34 class IPCResult;
     35 }  // namespace ipc
     36 
     37 namespace dom {
     38 class ContentParent;
     39 class ContentChild;
     40 template <typename T>
     41 class MaybeDiscarded;
     42 
     43 namespace syncedcontext {
     44 
     45 template <size_t I>
     46 using Index = typename std::integral_constant<size_t, I>;
     47 
     48 // We're going to use the empty base optimization for synced fields of different
     49 // sizes, so we define an empty class for that purpose.
     50 template <size_t I, size_t S>
     51 struct Empty {};
     52 
     53 // A templated container for a synced field. I is the index and T is the type.
     54 template <size_t I, typename T>
     55 struct Field {
     56  T mField{};
     57 };
     58 
     59 // SizedField is a Field with a helper to define either an "empty" field, or a
     60 // field of a given type.
     61 template <size_t I, typename T, size_t S>
     62 using SizedField = std::conditional_t<((sizeof(T) > 8) ? 8 : sizeof(T)) == S,
     63                                      Field<I, T>, Empty<I, S>>;
     64 
     65 template <typename Context>
     66 class Transaction {
     67 public:
     68  using IndexSet = EnumSet<size_t, BitSet<Context::FieldValues::count>>;
     69 
     70  // Set a field at the given index in this `Transaction`. Creating a
     71  // `Transaction` object and setting multiple fields on it allows for
     72  // multiple mutations to be performed atomically.
     73  template <size_t I, typename U>
     74  void Set(U&& aValue) {
     75    mValues.Get(Index<I>{}) = std::forward<U>(aValue);
     76    mModified += I;
     77  }
     78 
     79  // Apply the changes from this transaction to the specified Context in all
     80  // processes. This method will call the correct `CanSet` and `DidSet` methods,
     81  // as well as move the value.
     82  //
     83  // If the target has been discarded, changes will be ignored.
     84  //
     85  // NOTE: This method mutates `this`, clearing the modified field set.
     86  [[nodiscard]] nsresult Commit(Context* aOwner);
     87 
     88  // Called from `ContentParent` in response to a transaction from content.
     89  mozilla::ipc::IPCResult CommitFromIPC(const MaybeDiscarded<Context>& aOwner,
     90                                        ContentParent* aSource);
     91 
     92  // Called from `ContentChild` in response to a transaction from the parent.
     93  mozilla::ipc::IPCResult CommitFromIPC(const MaybeDiscarded<Context>& aOwner,
     94                                        uint64_t aEpoch, ContentChild* aSource);
     95 
     96  // Apply the changes from this transaction to the specified Context WITHOUT
     97  // syncing the changes to other processes.
     98  //
     99  // Unlike `Commit`, this method will NOT call the corresponding `CanSet` or
    100  // `DidSet` methods, and can be performed when the target context is
    101  // unattached or discarded.
    102  //
    103  // NOTE: YOU PROBABLY DO NOT WANT TO USE THIS METHOD
    104  void CommitWithoutSyncing(Context* aOwner);
    105 
    106 private:
    107  friend struct IPC::ParamTraits<Transaction<Context>>;
    108 
    109  void Write(IPC::MessageWriter* aWriter) const;
    110  bool Read(IPC::MessageReader* aReader);
    111 
    112  // You probably don't want to directly call this method - instead call
    113  // `Commit`, which will perform the necessary synchronization.
    114  //
    115  // `Validate` must be called before calling this method.
    116  void Apply(Context* aOwner, bool aFromIPC);
    117 
    118  // Returns the set of fields which failed to validate, or an empty set if
    119  // there were no validation errors.
    120  //
    121  // NOTE: This method mutates `this` if any changes were reverted.
    122  IndexSet Validate(Context* aOwner, ContentParent* aSource);
    123 
    124  template <typename F>
    125  static void EachIndex(F&& aCallback) {
    126    Context::FieldValues::EachIndex(aCallback);
    127  }
    128 
    129  template <size_t I>
    130  static uint64_t& FieldEpoch(Index<I>, Context* aContext) {
    131    return std::get<I>(aContext->mFields.mEpochs);
    132  }
    133 
    134  typename Context::FieldValues mValues;
    135  IndexSet mModified;
    136 };
    137 
    138 template <typename Base, size_t Count>
    139 class FieldValues : public Base {
    140 public:
    141  // The number of fields stored by this type.
    142  static constexpr size_t count = Count;
    143 
    144  // The base type will define a series of `Get` methods for looking up a field
    145  // by its field index.
    146  using Base::Get;
    147 
    148  // Calls a generic lambda with an `Index<I>` for each index less than the
    149  // field count.
    150  template <typename F>
    151  static void EachIndex(F&& aCallback) {
    152    EachIndexInner(std::make_index_sequence<count>(),
    153                   std::forward<F>(aCallback));
    154  }
    155 
    156 private:
    157  friend struct IPC::ParamTraits<FieldValues<Base, Count>>;
    158 
    159  void Write(IPC::MessageWriter* aWriter) const;
    160  bool Read(IPC::MessageReader* aReader);
    161 
    162  template <typename F, size_t... Indexes>
    163  static void EachIndexInner(std::index_sequence<Indexes...> aIndexes,
    164                             F&& aCallback) {
    165    (aCallback(Index<Indexes>()), ...);
    166  }
    167 };
    168 
    169 // Storage related to synchronized context fields. Contains both a tuple of
    170 // individual field values, and epoch information for field synchronization.
    171 template <typename Values>
    172 class FieldStorage {
    173 public:
    174  // Unsafely grab a reference directly to the internal values structure which
    175  // can be modified without telling other processes about the change.
    176  //
    177  // This is only sound in specific code which is already messaging other
    178  // processes, and doesn't need to worry about epochs or other properties of
    179  // field synchronization.
    180  Values& RawValues() { return mValues; }
    181  const Values& RawValues() const { return mValues; }
    182 
    183  // Get an individual field by index.
    184  template <size_t I>
    185  const auto& Get() const {
    186    return RawValues().Get(Index<I>{});
    187  }
    188 
    189  // Set the value of a field without telling other processes about the change.
    190  //
    191  // This is only sound in specific code which is already messaging other
    192  // processes, and doesn't need to worry about epochs or other properties of
    193  // field synchronization.
    194  template <size_t I, typename U>
    195  void SetWithoutSyncing(U&& aValue) {
    196    GetNonSyncingReference<I>() = std::move(aValue);
    197  }
    198 
    199  // Get a reference to a field that can modify without telling other
    200  // processes about the change.
    201  //
    202  // This is only sound in specific code which is already messaging other
    203  // processes, and doesn't need to worry about epochs or other properties of
    204  // field synchronization.
    205  template <size_t I>
    206  auto& GetNonSyncingReference() {
    207    return RawValues().Get(Index<I>{});
    208  }
    209 
    210  FieldStorage() = default;
    211  explicit FieldStorage(Values&& aInit) : mValues(std::move(aInit)) {}
    212 
    213 private:
    214  template <typename Context>
    215  friend class Transaction;
    216 
    217  // Data Members
    218  std::array<uint64_t, Values::count> mEpochs{};
    219  Values mValues;
    220 };
    221 
    222 // Alternative return type enum for `CanSet` validators which allows specifying
    223 // more behaviour.
    224 enum class CanSetResult : uint8_t {
    225  // The set attempt is denied. This is equivalent to returning `false`.
    226  Deny,
    227  // The set attempt is allowed. This is equivalent to returning `true`.
    228  Allow,
    229  // The set attempt is reverted non-fatally.
    230  Revert,
    231 };
    232 
    233 // Helper type traits to use concrete types rather than generic forwarding
    234 // references for the `SetXXX` methods defined on the synced context type.
    235 //
    236 // This helps avoid potential issues where someone accidentally declares an
    237 // overload of these methods with slightly different types and different
    238 // behaviours. See bug 1659520.
    239 template <typename T>
    240 struct GetFieldSetterType {
    241  using SetterArg = T;
    242 };
    243 template <>
    244 struct GetFieldSetterType<nsString> {
    245  using SetterArg = const nsAString&;
    246 };
    247 template <>
    248 struct GetFieldSetterType<nsCString> {
    249  using SetterArg = const nsACString&;
    250 };
    251 template <typename T>
    252 using FieldSetterType = typename GetFieldSetterType<T>::SetterArg;
    253 
    254 #define MOZ_DECL_SYNCED_CONTEXT_FIELD_INDEX(name, type) IDX_##name,
    255 
    256 #define MOZ_DECL_SYNCED_CONTEXT_FIELD_GETSET(name, type)                       \
    257  const type& Get##name() const { return mFields.template Get<IDX_##name>(); } \
    258                                                                               \
    259  [[nodiscard]] nsresult Set##name(                                            \
    260      ::mozilla::dom::syncedcontext::FieldSetterType<type> aValue) {           \
    261    Transaction txn;                                                           \
    262    txn.template Set<IDX_##name>(std::move(aValue));                           \
    263    return txn.Commit(this);                                                   \
    264  }                                                                            \
    265  void Set##name(::mozilla::dom::syncedcontext::FieldSetterType<type> aValue,  \
    266                 ErrorResult& aRv) {                                           \
    267    nsresult rv = this->Set##name(std::move(aValue));                          \
    268    if (NS_FAILED(rv)) {                                                       \
    269      aRv.ThrowInvalidStateError("cannot set synced field '" #name             \
    270                                 "': context is discarded");                   \
    271    }                                                                          \
    272  }
    273 
    274 #define MOZ_DECL_SYNCED_CONTEXT_TRANSACTION_SET(name, type)  \
    275  template <typename U>                                      \
    276  void Set##name(U&& aValue) {                               \
    277    this->template Set<IDX_##name>(std::forward<U>(aValue)); \
    278  }
    279 #define MOZ_DECL_SYNCED_CONTEXT_INDEX_TO_NAME(name, type) \
    280  case IDX_##name:                                        \
    281    return #name;
    282 
    283 #define MOZ_DECL_SYNCED_FIELD_INHERIT(name, type) \
    284 public                                           \
    285  syncedcontext::SizedField<IDX_##name, type, Size>,
    286 
    287 #define MOZ_DECL_SYNCED_CONTEXT_BASE_FIELD_GETTER(name, type) \
    288  type& Get(FieldIndex<IDX_##name>) {                         \
    289    return Field<IDX_##name, type>::mField;                   \
    290  }                                                           \
    291  const type& Get(FieldIndex<IDX_##name>) const {             \
    292    return Field<IDX_##name, type>::mField;                   \
    293  }
    294 
    295 // Declare a type as a synced context type.
    296 //
    297 // clazz is the name of the type being declared, and `eachfield` is a macro
    298 // which, when called with the name of the macro, will call that macro once for
    299 // each field in the synced context.
    300 #define MOZ_DECL_SYNCED_CONTEXT(clazz, eachfield)                              \
    301 public:                                                                       \
    302  /* Index constants for referring to each field in generic code */            \
    303  enum FieldIndexes {                                                          \
    304    eachfield(MOZ_DECL_SYNCED_CONTEXT_FIELD_INDEX) SYNCED_FIELD_COUNT          \
    305  };                                                                           \
    306                                                                               \
    307  /* Helper for overloading methods like `CanSet` and `DidSet` */              \
    308  template <size_t I>                                                          \
    309  using FieldIndex = typename ::mozilla::dom::syncedcontext::Index<I>;         \
    310                                                                               \
    311  /* Fields contain all synced fields defined by                               \
    312   * `eachfield(MOZ_DECL_SYNCED_FIELD_INHERIT)`, but only those where the size \
    313   * of the field is equal to size Size will be present. We use SizedField to  \
    314   * remove fields of the wrong size. */                                       \
    315  template <size_t Size>                                                       \
    316  struct Fields : eachfield(MOZ_DECL_SYNCED_FIELD_INHERIT)                     \
    317                      syncedcontext::Empty<SYNCED_FIELD_COUNT, Size>{};        \
    318                                                                               \
    319  /* Struct containing the data for all synced fields as members. We filter    \
    320   * sizes to lay out fields of size 1, then 2, then 4 and last 8 or greater.  \
    321   * This way we don't need to consider packing when defining fields, but      \
    322   * we'll just reorder them here.                                             \
    323   */                                                                          \
    324  struct BaseFieldValues : public Fields<1>,                                   \
    325                           public Fields<2>,                                   \
    326                           public Fields<4>,                                   \
    327                           public Fields<8> {                                  \
    328    template <size_t I>                                                        \
    329    auto& Get() {                                                              \
    330      return Get(FieldIndex<I>{});                                             \
    331    }                                                                          \
    332    template <size_t I>                                                        \
    333    const auto& Get() const {                                                  \
    334      return Get(FieldIndex<I>{});                                             \
    335    }                                                                          \
    336    eachfield(MOZ_DECL_SYNCED_CONTEXT_BASE_FIELD_GETTER)                       \
    337  };                                                                           \
    338  using FieldValues =                                                          \
    339      typename ::mozilla::dom::syncedcontext::FieldValues<BaseFieldValues,     \
    340                                                          SYNCED_FIELD_COUNT>; \
    341                                                                               \
    342 protected:                                                                    \
    343  friend class ::mozilla::dom::syncedcontext::Transaction<clazz>;              \
    344  ::mozilla::dom::syncedcontext::FieldStorage<FieldValues> mFields;            \
    345                                                                               \
    346 public:                                                                       \
    347  /* Transaction types for bulk mutations */                                   \
    348  using BaseTransaction = ::mozilla::dom::syncedcontext::Transaction<clazz>;   \
    349  class Transaction final : public BaseTransaction {                           \
    350   public:                                                                     \
    351    eachfield(MOZ_DECL_SYNCED_CONTEXT_TRANSACTION_SET)                         \
    352  };                                                                           \
    353                                                                               \
    354  /* Field name getter by field index */                                       \
    355  static const char* FieldIndexToName(size_t aIndex) {                         \
    356    switch (aIndex) { eachfield(MOZ_DECL_SYNCED_CONTEXT_INDEX_TO_NAME) }       \
    357    return "<unknown>";                                                        \
    358  }                                                                            \
    359  eachfield(MOZ_DECL_SYNCED_CONTEXT_FIELD_GETSET)
    360 
    361 }  // namespace syncedcontext
    362 }  // namespace dom
    363 }  // namespace mozilla
    364 
    365 namespace IPC {
    366 
    367 template <typename Context>
    368 struct ParamTraits<mozilla::dom::syncedcontext::Transaction<Context>> {
    369  using paramType = mozilla::dom::syncedcontext::Transaction<Context>;
    370 
    371  static void Write(MessageWriter* aWriter, const paramType& aParam) {
    372    aParam.Write(aWriter);
    373  }
    374 
    375  static bool Read(MessageReader* aReader, paramType* aResult) {
    376    return aResult->Read(aReader);
    377  }
    378 };
    379 
    380 template <typename Base, size_t Count>
    381 struct ParamTraits<mozilla::dom::syncedcontext::FieldValues<Base, Count>> {
    382  using paramType = mozilla::dom::syncedcontext::FieldValues<Base, Count>;
    383 
    384  static void Write(MessageWriter* aWriter, const paramType& aParam) {
    385    aParam.Write(aWriter);
    386  }
    387 
    388  static bool Read(MessageReader* aReader, paramType* aResult) {
    389    return aResult->Read(aReader);
    390  }
    391 };
    392 
    393 }  // namespace IPC
    394 
    395 #endif  // !defined(mozilla_dom_SyncedContext_h)