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/. */
7 #ifndef mozilla_dom_SyncedContext_h
8 #define mozilla_dom_SyncedContext_h
11 #include <type_traits>
13 #include "mozilla/Attributes.h"
14 #include "mozilla/BitSet.h"
15 #include "mozilla/EnumSet.h"
16 #include "nsStringFwd.h"
19 // Referenced via macro definitions
20 #include "mozilla/ErrorResult.h"
33 struct IPDLParamTraits
;
42 namespace syncedcontext
{
45 using Index
= typename
std::integral_constant
<size_t, I
>;
47 template <typename Context
>
50 using IndexSet
= EnumSet
<size_t, BitSet
<Context::FieldValues::count
>>;
52 // Set a field at the given index in this `Transaction`. Creating a
53 // `Transaction` object and setting multiple fields on it allows for
54 // multiple mutations to be performed atomically.
55 template <size_t I
, typename U
>
56 void Set(U
&& aValue
) {
57 mValues
.Get(Index
<I
>{}) = std::forward
<U
>(aValue
);
61 // Apply the changes from this transaction to the specified Context in all
62 // processes. This method will call the correct `CanSet` and `DidSet` methods,
63 // as well as move the value.
65 // If the target has been discarded, changes will be ignored.
67 // NOTE: This method mutates `this`, clearing the modified field set.
68 [[nodiscard
]] nsresult
Commit(Context
* aOwner
);
70 // Called from `ContentParent` in response to a transaction from content.
71 mozilla::ipc::IPCResult
CommitFromIPC(const MaybeDiscarded
<Context
>& aOwner
,
72 ContentParent
* aSource
);
74 // Called from `ContentChild` in response to a transaction from the parent.
75 mozilla::ipc::IPCResult
CommitFromIPC(const MaybeDiscarded
<Context
>& aOwner
,
76 uint64_t aEpoch
, ContentChild
* aSource
);
78 // Apply the changes from this transaction to the specified Context WITHOUT
79 // syncing the changes to other processes.
81 // Unlike `Commit`, this method will NOT call the corresponding `CanSet` or
82 // `DidSet` methods, and can be performed when the target context is
83 // unattached or discarded.
85 // NOTE: YOU PROBABLY DO NOT WANT TO USE THIS METHOD
86 void CommitWithoutSyncing(Context
* aOwner
);
89 friend struct mozilla::ipc::IPDLParamTraits
<Transaction
<Context
>>;
91 void Write(IPC::Message
* aMsg
, mozilla::ipc::IProtocol
* aActor
) const;
92 bool Read(const IPC::Message
* aMsg
, PickleIterator
* aIter
,
93 mozilla::ipc::IProtocol
* aActor
);
95 // You probably don't want to directly call this method - instead call
96 // `Commit`, which will perform the necessary synchronization.
98 // `Validate` must be called before calling this method.
99 void Apply(Context
* aOwner
, bool aFromIPC
);
101 // Returns the set of fields which failed to validate, or an empty set if
102 // there were no validation errors.
104 // NOTE: This method mutates `this` if any changes were reverted.
105 IndexSet
Validate(Context
* aOwner
, ContentParent
* aSource
);
107 template <typename F
>
108 static void EachIndex(F
&& aCallback
) {
109 Context::FieldValues::EachIndex(aCallback
);
113 static uint64_t& FieldEpoch(Index
<I
>, Context
* aContext
) {
114 return std::get
<I
>(aContext
->mFields
.mEpochs
);
117 typename
Context::FieldValues mValues
;
121 template <typename Base
, size_t Count
>
122 class FieldValues
: public Base
{
124 // The number of fields stored by this type.
125 static constexpr size_t count
= Count
;
127 // The base type will define a series of `Get` methods for looking up a field
128 // by its field index.
131 // Calls a generic lambda with an `Index<I>` for each index less than the
133 template <typename F
>
134 static void EachIndex(F
&& aCallback
) {
135 EachIndexInner(std::make_index_sequence
<count
>(),
136 std::forward
<F
>(aCallback
));
140 friend struct mozilla::ipc::IPDLParamTraits
<FieldValues
<Base
, Count
>>;
142 void Write(IPC::Message
* aMsg
, mozilla::ipc::IProtocol
* aActor
) const;
143 bool Read(const IPC::Message
* aMsg
, PickleIterator
* aIter
,
144 mozilla::ipc::IProtocol
* aActor
);
146 template <typename F
, size_t... Indexes
>
147 static void EachIndexInner(std::index_sequence
<Indexes
...> aIndexes
,
149 (aCallback(Index
<Indexes
>()), ...);
153 // Storage related to synchronized context fields. Contains both a tuple of
154 // individual field values, and epoch information for field synchronization.
155 template <typename Values
>
158 // Unsafely grab a reference directly to the internal values structure which
159 // can be modified without telling other processes about the change.
161 // This is only sound in specific code which is already messaging other
162 // processes, and doesn't need to worry about epochs or other properties of
163 // field synchronization.
164 Values
& RawValues() { return mValues
; }
165 const Values
& RawValues() const { return mValues
; }
167 // Get an individual field by index.
169 const auto& Get() const {
170 return RawValues().Get(Index
<I
>{});
173 // Set the value of a field without telling other processes about the change.
175 // This is only sound in specific code which is already messaging other
176 // processes, and doesn't need to worry about epochs or other properties of
177 // field synchronization.
178 template <size_t I
, typename U
>
179 void SetWithoutSyncing(U
&& aValue
) {
180 GetNonSyncingReference
<I
>() = std::move(aValue
);
183 // Get a reference to a field that can modify without telling other
184 // processes about the change.
186 // This is only sound in specific code which is already messaging other
187 // processes, and doesn't need to worry about epochs or other properties of
188 // field synchronization.
190 auto& GetNonSyncingReference() {
191 return RawValues().Get(Index
<I
>{});
194 FieldStorage() = default;
195 explicit FieldStorage(Values
&& aInit
) : mValues(std::move(aInit
)) {}
198 template <typename Context
>
199 friend class Transaction
;
202 std::array
<uint64_t, Values::count
> mEpochs
{};
206 // Alternative return type enum for `CanSet` validators which allows specifying
208 enum class CanSetResult
: uint8_t {
209 // The set attempt is denied. This is equivalent to returning `false`.
211 // The set attempt is allowed. This is equivalent to returning `true`.
213 // The set attempt is reverted non-fatally.
217 // Helper type traits to use concrete types rather than generic forwarding
218 // references for the `SetXXX` methods defined on the synced context type.
220 // This helps avoid potential issues where someone accidentally declares an
221 // overload of these methods with slightly different types and different
222 // behaviours. See bug 1659520.
223 template <typename T
>
224 struct GetFieldSetterType
{
228 struct GetFieldSetterType
<nsString
> {
229 using SetterArg
= const nsAString
&;
232 struct GetFieldSetterType
<nsCString
> {
233 using SetterArg
= const nsACString
&;
235 template <typename T
>
236 using FieldSetterType
= typename GetFieldSetterType
<T
>::SetterArg
;
238 #define MOZ_DECL_SYNCED_CONTEXT_FIELD_INDEX(name, type) IDX_##name,
239 #define MOZ_DECL_SYNCED_CONTEXT_FIELDS_DECL(name, type) \
240 /* index based field lookup */ \
241 type& Get(FieldIndex<IDX_##name>) { return m##name; } \
242 const type& Get(FieldIndex<IDX_##name>) const { return m##name; } \
244 /* storage for the field */ \
246 #define MOZ_DECL_SYNCED_CONTEXT_FIELD_GETSET(name, type) \
247 const type& Get##name() const { return mFields.template Get<IDX_##name>(); } \
249 [[nodiscard]] nsresult Set##name( \
250 ::mozilla::dom::syncedcontext::FieldSetterType<type> aValue) { \
252 txn.template Set<IDX_##name>(std::move(aValue)); \
253 return txn.Commit(this); \
255 void Set##name(::mozilla::dom::syncedcontext::FieldSetterType<type> aValue, \
256 ErrorResult& aRv) { \
257 nsresult rv = this->Set##name(std::move(aValue)); \
258 if (NS_FAILED(rv)) { \
259 aRv.ThrowInvalidStateError("cannot set synced field '" #name \
260 "': context is discarded"); \
264 #define MOZ_DECL_SYNCED_CONTEXT_TRANSACTION_SET(name, type) \
265 template <typename U> \
266 void Set##name(U&& aValue) { \
267 this->template Set<IDX_##name>(std::forward<U>(aValue)); \
269 #define MOZ_DECL_SYNCED_CONTEXT_INDEX_TO_NAME(name, type) \
273 // Declare a type as a synced context type.
275 // clazz is the name of the type being declared, and `eachfield` is a macro
276 // which, when called with the name of the macro, will call that macro once for
277 // each field in the synced context.
278 #define MOZ_DECL_SYNCED_CONTEXT(clazz, eachfield) \
280 /* Index constants for referring to each field in generic code */ \
281 enum FieldIndexes { \
282 eachfield(MOZ_DECL_SYNCED_CONTEXT_FIELD_INDEX) SYNCED_FIELD_COUNT \
285 /* Helper for overloading methods like `CanSet` and `DidSet` */ \
286 template <size_t I> \
287 using FieldIndex = typename ::mozilla::dom::syncedcontext::Index<I>; \
289 /* Struct containing the data for all synced fields as members */ \
290 struct BaseFieldValues { \
291 eachfield(MOZ_DECL_SYNCED_CONTEXT_FIELDS_DECL) \
293 using FieldValues = \
294 typename ::mozilla::dom::syncedcontext::FieldValues<BaseFieldValues, \
295 SYNCED_FIELD_COUNT>; \
298 friend class ::mozilla::dom::syncedcontext::Transaction<clazz>; \
299 ::mozilla::dom::syncedcontext::FieldStorage<FieldValues> mFields; \
302 /* Transaction types for bulk mutations */ \
303 using BaseTransaction = ::mozilla::dom::syncedcontext::Transaction<clazz>; \
304 class Transaction final : public BaseTransaction { \
306 eachfield(MOZ_DECL_SYNCED_CONTEXT_TRANSACTION_SET) \
309 /* Field name getter by field index */ \
310 static const char* FieldIndexToName(size_t aIndex) { \
311 switch (aIndex) { eachfield(MOZ_DECL_SYNCED_CONTEXT_INDEX_TO_NAME) } \
312 return "<unknown>"; \
314 eachfield(MOZ_DECL_SYNCED_CONTEXT_FIELD_GETSET)
316 } // namespace syncedcontext
321 template <typename Context
>
322 struct IPDLParamTraits
<dom::syncedcontext::Transaction
<Context
>> {
323 typedef dom::syncedcontext::Transaction
<Context
> paramType
;
325 static void Write(IPC::Message
* aMsg
, IProtocol
* aActor
,
326 const paramType
& aParam
) {
327 aParam
.Write(aMsg
, aActor
);
330 static bool Read(const IPC::Message
* aMsg
, PickleIterator
* aIter
,
331 IProtocol
* aActor
, paramType
* aResult
) {
332 return aResult
->Read(aMsg
, aIter
, aActor
);
336 template <typename Base
, size_t Count
>
337 struct IPDLParamTraits
<dom::syncedcontext::FieldValues
<Base
, Count
>> {
338 typedef dom::syncedcontext::FieldValues
<Base
, Count
> paramType
;
340 static void Write(IPC::Message
* aMsg
, IProtocol
* aActor
,
341 const paramType
& aParam
) {
342 aParam
.Write(aMsg
, aActor
);
345 static bool Read(const IPC::Message
* aMsg
, PickleIterator
* aIter
,
346 IProtocol
* aActor
, paramType
* aResult
) {
347 return aResult
->Read(aMsg
, aIter
, aActor
);
352 } // namespace mozilla
354 #endif // !defined(mozilla_dom_SyncedContext_h)