mirror of
				https://github.com/mozilla/gecko-dev.git
				synced 2025-10-31 16:28:05 +02:00 
			
		
		
		
	This new version of clang 17 also slightly changed the formatting. # ignore-this-changeset Differential Revision: https://phabricator.services.mozilla.com/D215914
		
			
				
	
	
		
			402 lines
		
	
	
	
		
			16 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			402 lines
		
	
	
	
		
			16 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 | |
| /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 | |
| /* This Source Code Form is subject to the terms of the Mozilla Public
 | |
|  * License, v. 2.0. If a copy of the MPL was not distributed with this
 | |
|  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 | |
| 
 | |
| #ifndef mozilla_dom_SyncedContext_h
 | |
| #define mozilla_dom_SyncedContext_h
 | |
| 
 | |
| #include <array>
 | |
| #include <type_traits>
 | |
| #include <utility>
 | |
| #include "mozilla/Attributes.h"
 | |
| #include "mozilla/BitSet.h"
 | |
| #include "mozilla/EnumSet.h"
 | |
| #include "nsStringFwd.h"
 | |
| #include "nscore.h"
 | |
| 
 | |
| // Referenced via macro definitions
 | |
| #include "mozilla/ErrorResult.h"
 | |
| 
 | |
| class PickleIterator;
 | |
| 
 | |
| namespace IPC {
 | |
| class Message;
 | |
| class MessageReader;
 | |
| class MessageWriter;
 | |
| }  // namespace IPC
 | |
| 
 | |
| namespace mozilla {
 | |
| namespace ipc {
 | |
| class IProtocol;
 | |
| class IPCResult;
 | |
| template <typename T>
 | |
| struct IPDLParamTraits;
 | |
| }  // namespace ipc
 | |
| 
 | |
| namespace dom {
 | |
| class ContentParent;
 | |
| class ContentChild;
 | |
| template <typename T>
 | |
| class MaybeDiscarded;
 | |
| 
 | |
| namespace syncedcontext {
 | |
| 
 | |
| template <size_t I>
 | |
| using Index = typename std::integral_constant<size_t, I>;
 | |
| 
 | |
| // We're going to use the empty base optimization for synced fields of different
 | |
| // sizes, so we define an empty class for that purpose.
 | |
| template <size_t I, size_t S>
 | |
| struct Empty {};
 | |
| 
 | |
| // A templated container for a synced field. I is the index and T is the type.
 | |
| template <size_t I, typename T>
 | |
| struct Field {
 | |
|   T mField{};
 | |
| };
 | |
| 
 | |
| // SizedField is a Field with a helper to define either an "empty" field, or a
 | |
| // field of a given type.
 | |
| template <size_t I, typename T, size_t S>
 | |
| using SizedField = std::conditional_t<((sizeof(T) > 8) ? 8 : sizeof(T)) == S,
 | |
|                                       Field<I, T>, Empty<I, S>>;
 | |
| 
 | |
| template <typename Context>
 | |
| class Transaction {
 | |
|  public:
 | |
|   using IndexSet = EnumSet<size_t, BitSet<Context::FieldValues::count>>;
 | |
| 
 | |
|   // Set a field at the given index in this `Transaction`. Creating a
 | |
|   // `Transaction` object and setting multiple fields on it allows for
 | |
|   // multiple mutations to be performed atomically.
 | |
|   template <size_t I, typename U>
 | |
|   void Set(U&& aValue) {
 | |
|     mValues.Get(Index<I>{}) = std::forward<U>(aValue);
 | |
|     mModified += I;
 | |
|   }
 | |
| 
 | |
|   // Apply the changes from this transaction to the specified Context in all
 | |
|   // processes. This method will call the correct `CanSet` and `DidSet` methods,
 | |
|   // as well as move the value.
 | |
|   //
 | |
|   // If the target has been discarded, changes will be ignored.
 | |
|   //
 | |
|   // NOTE: This method mutates `this`, clearing the modified field set.
 | |
|   [[nodiscard]] nsresult Commit(Context* aOwner);
 | |
| 
 | |
|   // Called from `ContentParent` in response to a transaction from content.
 | |
|   mozilla::ipc::IPCResult CommitFromIPC(const MaybeDiscarded<Context>& aOwner,
 | |
|                                         ContentParent* aSource);
 | |
| 
 | |
|   // Called from `ContentChild` in response to a transaction from the parent.
 | |
|   mozilla::ipc::IPCResult CommitFromIPC(const MaybeDiscarded<Context>& aOwner,
 | |
|                                         uint64_t aEpoch, ContentChild* aSource);
 | |
| 
 | |
|   // Apply the changes from this transaction to the specified Context WITHOUT
 | |
|   // syncing the changes to other processes.
 | |
|   //
 | |
|   // Unlike `Commit`, this method will NOT call the corresponding `CanSet` or
 | |
|   // `DidSet` methods, and can be performed when the target context is
 | |
|   // unattached or discarded.
 | |
|   //
 | |
|   // NOTE: YOU PROBABLY DO NOT WANT TO USE THIS METHOD
 | |
|   void CommitWithoutSyncing(Context* aOwner);
 | |
| 
 | |
|  private:
 | |
|   friend struct mozilla::ipc::IPDLParamTraits<Transaction<Context>>;
 | |
| 
 | |
|   void Write(IPC::MessageWriter* aWriter,
 | |
|              mozilla::ipc::IProtocol* aActor) const;
 | |
|   bool Read(IPC::MessageReader* aReader, mozilla::ipc::IProtocol* aActor);
 | |
| 
 | |
|   // You probably don't want to directly call this method - instead call
 | |
|   // `Commit`, which will perform the necessary synchronization.
 | |
|   //
 | |
|   // `Validate` must be called before calling this method.
 | |
|   void Apply(Context* aOwner, bool aFromIPC);
 | |
| 
 | |
|   // Returns the set of fields which failed to validate, or an empty set if
 | |
|   // there were no validation errors.
 | |
|   //
 | |
|   // NOTE: This method mutates `this` if any changes were reverted.
 | |
|   IndexSet Validate(Context* aOwner, ContentParent* aSource);
 | |
| 
 | |
|   template <typename F>
 | |
|   static void EachIndex(F&& aCallback) {
 | |
|     Context::FieldValues::EachIndex(aCallback);
 | |
|   }
 | |
| 
 | |
|   template <size_t I>
 | |
|   static uint64_t& FieldEpoch(Index<I>, Context* aContext) {
 | |
|     return std::get<I>(aContext->mFields.mEpochs);
 | |
|   }
 | |
| 
 | |
|   typename Context::FieldValues mValues;
 | |
|   IndexSet mModified;
 | |
| };
 | |
| 
 | |
| template <typename Base, size_t Count>
 | |
| class FieldValues : public Base {
 | |
|  public:
 | |
|   // The number of fields stored by this type.
 | |
|   static constexpr size_t count = Count;
 | |
| 
 | |
|   // The base type will define a series of `Get` methods for looking up a field
 | |
|   // by its field index.
 | |
|   using Base::Get;
 | |
| 
 | |
|   // Calls a generic lambda with an `Index<I>` for each index less than the
 | |
|   // field count.
 | |
|   template <typename F>
 | |
|   static void EachIndex(F&& aCallback) {
 | |
|     EachIndexInner(std::make_index_sequence<count>(),
 | |
|                    std::forward<F>(aCallback));
 | |
|   }
 | |
| 
 | |
|  private:
 | |
|   friend struct mozilla::ipc::IPDLParamTraits<FieldValues<Base, Count>>;
 | |
| 
 | |
|   void Write(IPC::MessageWriter* aWriter,
 | |
|              mozilla::ipc::IProtocol* aActor) const;
 | |
|   bool Read(IPC::MessageReader* aReader, mozilla::ipc::IProtocol* aActor);
 | |
| 
 | |
|   template <typename F, size_t... Indexes>
 | |
|   static void EachIndexInner(std::index_sequence<Indexes...> aIndexes,
 | |
|                              F&& aCallback) {
 | |
|     (aCallback(Index<Indexes>()), ...);
 | |
|   }
 | |
| };
 | |
| 
 | |
| // Storage related to synchronized context fields. Contains both a tuple of
 | |
| // individual field values, and epoch information for field synchronization.
 | |
| template <typename Values>
 | |
| class FieldStorage {
 | |
|  public:
 | |
|   // Unsafely grab a reference directly to the internal values structure which
 | |
|   // can be modified without telling other processes about the change.
 | |
|   //
 | |
|   // This is only sound in specific code which is already messaging other
 | |
|   // processes, and doesn't need to worry about epochs or other properties of
 | |
|   // field synchronization.
 | |
|   Values& RawValues() { return mValues; }
 | |
|   const Values& RawValues() const { return mValues; }
 | |
| 
 | |
|   // Get an individual field by index.
 | |
|   template <size_t I>
 | |
|   const auto& Get() const {
 | |
|     return RawValues().Get(Index<I>{});
 | |
|   }
 | |
| 
 | |
|   // Set the value of a field without telling other processes about the change.
 | |
|   //
 | |
|   // This is only sound in specific code which is already messaging other
 | |
|   // processes, and doesn't need to worry about epochs or other properties of
 | |
|   // field synchronization.
 | |
|   template <size_t I, typename U>
 | |
|   void SetWithoutSyncing(U&& aValue) {
 | |
|     GetNonSyncingReference<I>() = std::move(aValue);
 | |
|   }
 | |
| 
 | |
|   // Get a reference to a field that can modify without telling other
 | |
|   // processes about the change.
 | |
|   //
 | |
|   // This is only sound in specific code which is already messaging other
 | |
|   // processes, and doesn't need to worry about epochs or other properties of
 | |
|   // field synchronization.
 | |
|   template <size_t I>
 | |
|   auto& GetNonSyncingReference() {
 | |
|     return RawValues().Get(Index<I>{});
 | |
|   }
 | |
| 
 | |
|   FieldStorage() = default;
 | |
|   explicit FieldStorage(Values&& aInit) : mValues(std::move(aInit)) {}
 | |
| 
 | |
|  private:
 | |
|   template <typename Context>
 | |
|   friend class Transaction;
 | |
| 
 | |
|   // Data Members
 | |
|   std::array<uint64_t, Values::count> mEpochs{};
 | |
|   Values mValues;
 | |
| };
 | |
| 
 | |
| // Alternative return type enum for `CanSet` validators which allows specifying
 | |
| // more behaviour.
 | |
| enum class CanSetResult : uint8_t {
 | |
|   // The set attempt is denied. This is equivalent to returning `false`.
 | |
|   Deny,
 | |
|   // The set attempt is allowed. This is equivalent to returning `true`.
 | |
|   Allow,
 | |
|   // The set attempt is reverted non-fatally.
 | |
|   Revert,
 | |
| };
 | |
| 
 | |
| // Helper type traits to use concrete types rather than generic forwarding
 | |
| // references for the `SetXXX` methods defined on the synced context type.
 | |
| //
 | |
| // This helps avoid potential issues where someone accidentally declares an
 | |
| // overload of these methods with slightly different types and different
 | |
| // behaviours. See bug 1659520.
 | |
| template <typename T>
 | |
| struct GetFieldSetterType {
 | |
|   using SetterArg = T;
 | |
| };
 | |
| template <>
 | |
| struct GetFieldSetterType<nsString> {
 | |
|   using SetterArg = const nsAString&;
 | |
| };
 | |
| template <>
 | |
| struct GetFieldSetterType<nsCString> {
 | |
|   using SetterArg = const nsACString&;
 | |
| };
 | |
| template <typename T>
 | |
| using FieldSetterType = typename GetFieldSetterType<T>::SetterArg;
 | |
| 
 | |
| #define MOZ_DECL_SYNCED_CONTEXT_FIELD_INDEX(name, type) IDX_##name,
 | |
| 
 | |
| #define MOZ_DECL_SYNCED_CONTEXT_FIELD_GETSET(name, type)                       \
 | |
|   const type& Get##name() const { return mFields.template Get<IDX_##name>(); } \
 | |
|                                                                                \
 | |
|   [[nodiscard]] nsresult Set##name(                                            \
 | |
|       ::mozilla::dom::syncedcontext::FieldSetterType<type> aValue) {           \
 | |
|     Transaction txn;                                                           \
 | |
|     txn.template Set<IDX_##name>(std::move(aValue));                           \
 | |
|     return txn.Commit(this);                                                   \
 | |
|   }                                                                            \
 | |
|   void Set##name(::mozilla::dom::syncedcontext::FieldSetterType<type> aValue,  \
 | |
|                  ErrorResult& aRv) {                                           \
 | |
|     nsresult rv = this->Set##name(std::move(aValue));                          \
 | |
|     if (NS_FAILED(rv)) {                                                       \
 | |
|       aRv.ThrowInvalidStateError("cannot set synced field '" #name             \
 | |
|                                  "': context is discarded");                   \
 | |
|     }                                                                          \
 | |
|   }
 | |
| 
 | |
| #define MOZ_DECL_SYNCED_CONTEXT_TRANSACTION_SET(name, type)  \
 | |
|   template <typename U>                                      \
 | |
|   void Set##name(U&& aValue) {                               \
 | |
|     this->template Set<IDX_##name>(std::forward<U>(aValue)); \
 | |
|   }
 | |
| #define MOZ_DECL_SYNCED_CONTEXT_INDEX_TO_NAME(name, type) \
 | |
|   case IDX_##name:                                        \
 | |
|     return #name;
 | |
| 
 | |
| #define MOZ_DECL_SYNCED_FIELD_INHERIT(name, type) \
 | |
|  public                                           \
 | |
|   syncedcontext::SizedField<IDX_##name, type, Size>,
 | |
| 
 | |
| #define MOZ_DECL_SYNCED_CONTEXT_BASE_FIELD_GETTER(name, type) \
 | |
|   type& Get(FieldIndex<IDX_##name>) {                         \
 | |
|     return Field<IDX_##name, type>::mField;                   \
 | |
|   }                                                           \
 | |
|   const type& Get(FieldIndex<IDX_##name>) const {             \
 | |
|     return Field<IDX_##name, type>::mField;                   \
 | |
|   }
 | |
| 
 | |
| // Declare a type as a synced context type.
 | |
| //
 | |
| // clazz is the name of the type being declared, and `eachfield` is a macro
 | |
| // which, when called with the name of the macro, will call that macro once for
 | |
| // each field in the synced context.
 | |
| #define MOZ_DECL_SYNCED_CONTEXT(clazz, eachfield)                              \
 | |
|  public:                                                                       \
 | |
|   /* Index constants for referring to each field in generic code */            \
 | |
|   enum FieldIndexes {                                                          \
 | |
|     eachfield(MOZ_DECL_SYNCED_CONTEXT_FIELD_INDEX) SYNCED_FIELD_COUNT          \
 | |
|   };                                                                           \
 | |
|                                                                                \
 | |
|   /* Helper for overloading methods like `CanSet` and `DidSet` */              \
 | |
|   template <size_t I>                                                          \
 | |
|   using FieldIndex = typename ::mozilla::dom::syncedcontext::Index<I>;         \
 | |
|                                                                                \
 | |
|   /* Fields contain all synced fields defined by                               \
 | |
|    * `eachfield(MOZ_DECL_SYNCED_FIELD_INHERIT)`, but only those where the size \
 | |
|    * of the field is equal to size Size will be present. We use SizedField to  \
 | |
|    * remove fields of the wrong size. */                                       \
 | |
|   template <size_t Size>                                                       \
 | |
|   struct Fields : eachfield(MOZ_DECL_SYNCED_FIELD_INHERIT)                     \
 | |
|                       syncedcontext::Empty<SYNCED_FIELD_COUNT, Size>{};        \
 | |
|                                                                                \
 | |
|   /* Struct containing the data for all synced fields as members. We filter    \
 | |
|    * sizes to lay out fields of size 1, then 2, then 4 and last 8 or greater.  \
 | |
|    * This way we don't need to consider packing when defining fields, but      \
 | |
|    * we'll just reorder them here.                                             \
 | |
|    */                                                                          \
 | |
|   struct BaseFieldValues : public Fields<1>,                                   \
 | |
|                            public Fields<2>,                                   \
 | |
|                            public Fields<4>,                                   \
 | |
|                            public Fields<8> {                                  \
 | |
|     template <size_t I>                                                        \
 | |
|     auto& Get() {                                                              \
 | |
|       return Get(FieldIndex<I>{});                                             \
 | |
|     }                                                                          \
 | |
|     template <size_t I>                                                        \
 | |
|     const auto& Get() const {                                                  \
 | |
|       return Get(FieldIndex<I>{});                                             \
 | |
|     }                                                                          \
 | |
|     eachfield(MOZ_DECL_SYNCED_CONTEXT_BASE_FIELD_GETTER)                       \
 | |
|   };                                                                           \
 | |
|   using FieldValues =                                                          \
 | |
|       typename ::mozilla::dom::syncedcontext::FieldValues<BaseFieldValues,     \
 | |
|                                                           SYNCED_FIELD_COUNT>; \
 | |
|                                                                                \
 | |
|  protected:                                                                    \
 | |
|   friend class ::mozilla::dom::syncedcontext::Transaction<clazz>;              \
 | |
|   ::mozilla::dom::syncedcontext::FieldStorage<FieldValues> mFields;            \
 | |
|                                                                                \
 | |
|  public:                                                                       \
 | |
|   /* Transaction types for bulk mutations */                                   \
 | |
|   using BaseTransaction = ::mozilla::dom::syncedcontext::Transaction<clazz>;   \
 | |
|   class Transaction final : public BaseTransaction {                           \
 | |
|    public:                                                                     \
 | |
|     eachfield(MOZ_DECL_SYNCED_CONTEXT_TRANSACTION_SET)                         \
 | |
|   };                                                                           \
 | |
|                                                                                \
 | |
|   /* Field name getter by field index */                                       \
 | |
|   static const char* FieldIndexToName(size_t aIndex) {                         \
 | |
|     switch (aIndex) { eachfield(MOZ_DECL_SYNCED_CONTEXT_INDEX_TO_NAME) }       \
 | |
|     return "<unknown>";                                                        \
 | |
|   }                                                                            \
 | |
|   eachfield(MOZ_DECL_SYNCED_CONTEXT_FIELD_GETSET)
 | |
| 
 | |
| }  // namespace syncedcontext
 | |
| }  // namespace dom
 | |
| 
 | |
| namespace ipc {
 | |
| 
 | |
| template <typename Context>
 | |
| struct IPDLParamTraits<dom::syncedcontext::Transaction<Context>> {
 | |
|   typedef dom::syncedcontext::Transaction<Context> paramType;
 | |
| 
 | |
|   static void Write(IPC::MessageWriter* aWriter, IProtocol* aActor,
 | |
|                     const paramType& aParam) {
 | |
|     aParam.Write(aWriter, aActor);
 | |
|   }
 | |
| 
 | |
|   static bool Read(IPC::MessageReader* aReader, IProtocol* aActor,
 | |
|                    paramType* aResult) {
 | |
|     return aResult->Read(aReader, aActor);
 | |
|   }
 | |
| };
 | |
| 
 | |
| template <typename Base, size_t Count>
 | |
| struct IPDLParamTraits<dom::syncedcontext::FieldValues<Base, Count>> {
 | |
|   typedef dom::syncedcontext::FieldValues<Base, Count> paramType;
 | |
| 
 | |
|   static void Write(IPC::MessageWriter* aWriter, IProtocol* aActor,
 | |
|                     const paramType& aParam) {
 | |
|     aParam.Write(aWriter, aActor);
 | |
|   }
 | |
| 
 | |
|   static bool Read(IPC::MessageReader* aReader, IProtocol* aActor,
 | |
|                    paramType* aResult) {
 | |
|     return aResult->Read(aReader, aActor);
 | |
|   }
 | |
| };
 | |
| 
 | |
| }  // namespace ipc
 | |
| }  // namespace mozilla
 | |
| 
 | |
| #endif  // !defined(mozilla_dom_SyncedContext_h)
 |