Bug 1751497 - adjust wpt test-verify and test-coverage tasks to be fission only....
[gecko.git] / docshell / base / SyncedContext.h
blobf620acc83eb4a70005109d3b6e5496dba7107976
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
10 #include <array>
11 #include <type_traits>
12 #include <utility>
13 #include "mozilla/Attributes.h"
14 #include "mozilla/BitSet.h"
15 #include "mozilla/EnumSet.h"
16 #include "nsStringFwd.h"
17 #include "nscore.h"
19 // Referenced via macro definitions
20 #include "mozilla/ErrorResult.h"
22 class PickleIterator;
24 namespace IPC {
25 class Message;
26 } // namespace IPC
28 namespace mozilla {
29 namespace ipc {
30 class IProtocol;
31 class IPCResult;
32 template <typename T>
33 struct IPDLParamTraits;
34 } // namespace ipc
36 namespace dom {
37 class ContentParent;
38 class ContentChild;
39 template <typename T>
40 class MaybeDiscarded;
42 namespace syncedcontext {
44 template <size_t I>
45 using Index = typename std::integral_constant<size_t, I>;
47 template <typename Context>
48 class Transaction {
49 public:
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);
58 mModified += I;
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);
88 private:
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);
112 template <size_t I>
113 static uint64_t& FieldEpoch(Index<I>, Context* aContext) {
114 return std::get<I>(aContext->mFields.mEpochs);
117 typename Context::FieldValues mValues;
118 IndexSet mModified;
121 template <typename Base, size_t Count>
122 class FieldValues : public Base {
123 public:
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.
129 using Base::Get;
131 // Calls a generic lambda with an `Index<I>` for each index less than the
132 // field count.
133 template <typename F>
134 static void EachIndex(F&& aCallback) {
135 EachIndexInner(std::make_index_sequence<count>(),
136 std::forward<F>(aCallback));
139 private:
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,
148 F&& aCallback) {
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>
156 class FieldStorage {
157 public:
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.
168 template <size_t I>
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.
189 template <size_t I>
190 auto& GetNonSyncingReference() {
191 return RawValues().Get(Index<I>{});
194 FieldStorage() = default;
195 explicit FieldStorage(Values&& aInit) : mValues(std::move(aInit)) {}
197 private:
198 template <typename Context>
199 friend class Transaction;
201 // Data Members
202 std::array<uint64_t, Values::count> mEpochs{};
203 Values mValues;
206 // Alternative return type enum for `CanSet` validators which allows specifying
207 // more behaviour.
208 enum class CanSetResult : uint8_t {
209 // The set attempt is denied. This is equivalent to returning `false`.
210 Deny,
211 // The set attempt is allowed. This is equivalent to returning `true`.
212 Allow,
213 // The set attempt is reverted non-fatally.
214 Revert,
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 {
225 using SetterArg = T;
227 template <>
228 struct GetFieldSetterType<nsString> {
229 using SetterArg = const nsAString&;
231 template <>
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 */ \
245 type m##name{};
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) { \
251 Transaction txn; \
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) \
270 case IDX_##name: \
271 return #name;
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) \
279 public: \
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 \
283 }; \
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) \
292 }; \
293 using FieldValues = \
294 typename ::mozilla::dom::syncedcontext::FieldValues<BaseFieldValues, \
295 SYNCED_FIELD_COUNT>; \
297 protected: \
298 friend class ::mozilla::dom::syncedcontext::Transaction<clazz>; \
299 ::mozilla::dom::syncedcontext::FieldStorage<FieldValues> mFields; \
301 public: \
302 /* Transaction types for bulk mutations */ \
303 using BaseTransaction = ::mozilla::dom::syncedcontext::Transaction<clazz>; \
304 class Transaction final : public BaseTransaction { \
305 public: \
306 eachfield(MOZ_DECL_SYNCED_CONTEXT_TRANSACTION_SET) \
307 }; \
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
317 } // namespace dom
319 namespace ipc {
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);
351 } // namespace ipc
352 } // namespace mozilla
354 #endif // !defined(mozilla_dom_SyncedContext_h)