Bug 1839316: part 5) Guard the "fetchpriority" attribute behind a pref. r=kershaw...
[gecko.git] / docshell / base / SyncedContext.h
blob679e07edc29af3ba8dd75f066933f088d23d4f6e
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 class MessageReader;
27 class MessageWriter;
28 } // namespace IPC
30 namespace mozilla {
31 namespace ipc {
32 class IProtocol;
33 class IPCResult;
34 template <typename T>
35 struct IPDLParamTraits;
36 } // namespace ipc
38 namespace dom {
39 class ContentParent;
40 class ContentChild;
41 template <typename T>
42 class MaybeDiscarded;
44 namespace syncedcontext {
46 template <size_t I>
47 using Index = typename std::integral_constant<size_t, I>;
49 // We're going to use the empty base optimization for synced fields of different
50 // sizes, so we define an empty class for that purpose.
51 template <size_t I, size_t S>
52 struct Empty {};
54 // A templated container for a synced field. I is the index and T is the type.
55 template <size_t I, typename T>
56 struct Field {
57 T mField{};
60 // SizedField is a Field with a helper to define either an "empty" field, or a
61 // field of a given type.
62 template <size_t I, typename T, size_t S>
63 using SizedField = std::conditional_t<((sizeof(T) > 8) ? 8 : sizeof(T)) == S,
64 Field<I, T>, Empty<I, S>>;
66 template <typename Context>
67 class Transaction {
68 public:
69 using IndexSet = EnumSet<size_t, BitSet<Context::FieldValues::count>>;
71 // Set a field at the given index in this `Transaction`. Creating a
72 // `Transaction` object and setting multiple fields on it allows for
73 // multiple mutations to be performed atomically.
74 template <size_t I, typename U>
75 void Set(U&& aValue) {
76 mValues.Get(Index<I>{}) = std::forward<U>(aValue);
77 mModified += I;
80 // Apply the changes from this transaction to the specified Context in all
81 // processes. This method will call the correct `CanSet` and `DidSet` methods,
82 // as well as move the value.
84 // If the target has been discarded, changes will be ignored.
86 // NOTE: This method mutates `this`, clearing the modified field set.
87 [[nodiscard]] nsresult Commit(Context* aOwner);
89 // Called from `ContentParent` in response to a transaction from content.
90 mozilla::ipc::IPCResult CommitFromIPC(const MaybeDiscarded<Context>& aOwner,
91 ContentParent* aSource);
93 // Called from `ContentChild` in response to a transaction from the parent.
94 mozilla::ipc::IPCResult CommitFromIPC(const MaybeDiscarded<Context>& aOwner,
95 uint64_t aEpoch, ContentChild* aSource);
97 // Apply the changes from this transaction to the specified Context WITHOUT
98 // syncing the changes to other processes.
100 // Unlike `Commit`, this method will NOT call the corresponding `CanSet` or
101 // `DidSet` methods, and can be performed when the target context is
102 // unattached or discarded.
104 // NOTE: YOU PROBABLY DO NOT WANT TO USE THIS METHOD
105 void CommitWithoutSyncing(Context* aOwner);
107 private:
108 friend struct mozilla::ipc::IPDLParamTraits<Transaction<Context>>;
110 void Write(IPC::MessageWriter* aWriter,
111 mozilla::ipc::IProtocol* aActor) const;
112 bool Read(IPC::MessageReader* aReader, mozilla::ipc::IProtocol* aActor);
114 // You probably don't want to directly call this method - instead call
115 // `Commit`, which will perform the necessary synchronization.
117 // `Validate` must be called before calling this method.
118 void Apply(Context* aOwner, bool aFromIPC);
120 // Returns the set of fields which failed to validate, or an empty set if
121 // there were no validation errors.
123 // NOTE: This method mutates `this` if any changes were reverted.
124 IndexSet Validate(Context* aOwner, ContentParent* aSource);
126 template <typename F>
127 static void EachIndex(F&& aCallback) {
128 Context::FieldValues::EachIndex(aCallback);
131 template <size_t I>
132 static uint64_t& FieldEpoch(Index<I>, Context* aContext) {
133 return std::get<I>(aContext->mFields.mEpochs);
136 typename Context::FieldValues mValues;
137 IndexSet mModified;
140 template <typename Base, size_t Count>
141 class FieldValues : public Base {
142 public:
143 // The number of fields stored by this type.
144 static constexpr size_t count = Count;
146 // The base type will define a series of `Get` methods for looking up a field
147 // by its field index.
148 using Base::Get;
150 // Calls a generic lambda with an `Index<I>` for each index less than the
151 // field count.
152 template <typename F>
153 static void EachIndex(F&& aCallback) {
154 EachIndexInner(std::make_index_sequence<count>(),
155 std::forward<F>(aCallback));
158 private:
159 friend struct mozilla::ipc::IPDLParamTraits<FieldValues<Base, Count>>;
161 void Write(IPC::MessageWriter* aWriter,
162 mozilla::ipc::IProtocol* aActor) const;
163 bool Read(IPC::MessageReader* aReader, mozilla::ipc::IProtocol* aActor);
165 template <typename F, size_t... Indexes>
166 static void EachIndexInner(std::index_sequence<Indexes...> aIndexes,
167 F&& aCallback) {
168 (aCallback(Index<Indexes>()), ...);
172 // Storage related to synchronized context fields. Contains both a tuple of
173 // individual field values, and epoch information for field synchronization.
174 template <typename Values>
175 class FieldStorage {
176 public:
177 // Unsafely grab a reference directly to the internal values structure which
178 // can be modified without telling other processes about the change.
180 // This is only sound in specific code which is already messaging other
181 // processes, and doesn't need to worry about epochs or other properties of
182 // field synchronization.
183 Values& RawValues() { return mValues; }
184 const Values& RawValues() const { return mValues; }
186 // Get an individual field by index.
187 template <size_t I>
188 const auto& Get() const {
189 return RawValues().Get(Index<I>{});
192 // Set the value of a field without telling other processes about the change.
194 // This is only sound in specific code which is already messaging other
195 // processes, and doesn't need to worry about epochs or other properties of
196 // field synchronization.
197 template <size_t I, typename U>
198 void SetWithoutSyncing(U&& aValue) {
199 GetNonSyncingReference<I>() = std::move(aValue);
202 // Get a reference to a field that can modify without telling other
203 // processes about the change.
205 // This is only sound in specific code which is already messaging other
206 // processes, and doesn't need to worry about epochs or other properties of
207 // field synchronization.
208 template <size_t I>
209 auto& GetNonSyncingReference() {
210 return RawValues().Get(Index<I>{});
213 FieldStorage() = default;
214 explicit FieldStorage(Values&& aInit) : mValues(std::move(aInit)) {}
216 private:
217 template <typename Context>
218 friend class Transaction;
220 // Data Members
221 std::array<uint64_t, Values::count> mEpochs{};
222 Values mValues;
225 // Alternative return type enum for `CanSet` validators which allows specifying
226 // more behaviour.
227 enum class CanSetResult : uint8_t {
228 // The set attempt is denied. This is equivalent to returning `false`.
229 Deny,
230 // The set attempt is allowed. This is equivalent to returning `true`.
231 Allow,
232 // The set attempt is reverted non-fatally.
233 Revert,
236 // Helper type traits to use concrete types rather than generic forwarding
237 // references for the `SetXXX` methods defined on the synced context type.
239 // This helps avoid potential issues where someone accidentally declares an
240 // overload of these methods with slightly different types and different
241 // behaviours. See bug 1659520.
242 template <typename T>
243 struct GetFieldSetterType {
244 using SetterArg = T;
246 template <>
247 struct GetFieldSetterType<nsString> {
248 using SetterArg = const nsAString&;
250 template <>
251 struct GetFieldSetterType<nsCString> {
252 using SetterArg = const nsACString&;
254 template <typename T>
255 using FieldSetterType = typename GetFieldSetterType<T>::SetterArg;
257 #define MOZ_DECL_SYNCED_CONTEXT_FIELD_INDEX(name, type) IDX_##name,
259 #define MOZ_DECL_SYNCED_CONTEXT_FIELD_GETSET(name, type) \
260 const type& Get##name() const { return mFields.template Get<IDX_##name>(); } \
262 [[nodiscard]] nsresult Set##name( \
263 ::mozilla::dom::syncedcontext::FieldSetterType<type> aValue) { \
264 Transaction txn; \
265 txn.template Set<IDX_##name>(std::move(aValue)); \
266 return txn.Commit(this); \
268 void Set##name(::mozilla::dom::syncedcontext::FieldSetterType<type> aValue, \
269 ErrorResult& aRv) { \
270 nsresult rv = this->Set##name(std::move(aValue)); \
271 if (NS_FAILED(rv)) { \
272 aRv.ThrowInvalidStateError("cannot set synced field '" #name \
273 "': context is discarded"); \
277 #define MOZ_DECL_SYNCED_CONTEXT_TRANSACTION_SET(name, type) \
278 template <typename U> \
279 void Set##name(U&& aValue) { \
280 this->template Set<IDX_##name>(std::forward<U>(aValue)); \
282 #define MOZ_DECL_SYNCED_CONTEXT_INDEX_TO_NAME(name, type) \
283 case IDX_##name: \
284 return #name;
286 #define MOZ_DECL_SYNCED_FIELD_INHERIT(name, type) \
287 public \
288 syncedcontext::SizedField<IDX_##name, type, Size>,
290 #define MOZ_DECL_SYNCED_CONTEXT_BASE_FIELD_GETTER(name, type) \
291 type& Get(FieldIndex<IDX_##name>) { \
292 return Field<IDX_##name, type>::mField; \
294 const type& Get(FieldIndex<IDX_##name>) const { \
295 return Field<IDX_##name, type>::mField; \
298 // Declare a type as a synced context type.
300 // clazz is the name of the type being declared, and `eachfield` is a macro
301 // which, when called with the name of the macro, will call that macro once for
302 // each field in the synced context.
303 #define MOZ_DECL_SYNCED_CONTEXT(clazz, eachfield) \
304 public: \
305 /* Index constants for referring to each field in generic code */ \
306 enum FieldIndexes { \
307 eachfield(MOZ_DECL_SYNCED_CONTEXT_FIELD_INDEX) SYNCED_FIELD_COUNT \
308 }; \
310 /* Helper for overloading methods like `CanSet` and `DidSet` */ \
311 template <size_t I> \
312 using FieldIndex = typename ::mozilla::dom::syncedcontext::Index<I>; \
314 /* Fields contain all synced fields defined by \
315 * `eachfield(MOZ_DECL_SYNCED_FIELD_INHERIT)`, but only those where the size \
316 * of the field is equal to size Size will be present. We use SizedField to \
317 * remove fields of the wrong size. */ \
318 template <size_t Size> \
319 struct Fields : eachfield(MOZ_DECL_SYNCED_FIELD_INHERIT) \
320 syncedcontext::Empty<SYNCED_FIELD_COUNT, Size> {}; \
322 /* Struct containing the data for all synced fields as members. We filter \
323 * sizes to lay out fields of size 1, then 2, then 4 and last 8 or greater. \
324 * This way we don't need to consider packing when defining fields, but \
325 * we'll just reorder them here. \
326 */ \
327 struct BaseFieldValues : public Fields<1>, \
328 public Fields<2>, \
329 public Fields<4>, \
330 public Fields<8> { \
331 template <size_t I> \
332 auto& Get() { \
333 return Get(FieldIndex<I>{}); \
335 template <size_t I> \
336 const auto& Get() const { \
337 return Get(FieldIndex<I>{}); \
339 eachfield(MOZ_DECL_SYNCED_CONTEXT_BASE_FIELD_GETTER) \
340 }; \
341 using FieldValues = \
342 typename ::mozilla::dom::syncedcontext::FieldValues<BaseFieldValues, \
343 SYNCED_FIELD_COUNT>; \
345 protected: \
346 friend class ::mozilla::dom::syncedcontext::Transaction<clazz>; \
347 ::mozilla::dom::syncedcontext::FieldStorage<FieldValues> mFields; \
349 public: \
350 /* Transaction types for bulk mutations */ \
351 using BaseTransaction = ::mozilla::dom::syncedcontext::Transaction<clazz>; \
352 class Transaction final : public BaseTransaction { \
353 public: \
354 eachfield(MOZ_DECL_SYNCED_CONTEXT_TRANSACTION_SET) \
355 }; \
357 /* Field name getter by field index */ \
358 static const char* FieldIndexToName(size_t aIndex) { \
359 switch (aIndex) { eachfield(MOZ_DECL_SYNCED_CONTEXT_INDEX_TO_NAME) } \
360 return "<unknown>"; \
362 eachfield(MOZ_DECL_SYNCED_CONTEXT_FIELD_GETSET)
364 } // namespace syncedcontext
365 } // namespace dom
367 namespace ipc {
369 template <typename Context>
370 struct IPDLParamTraits<dom::syncedcontext::Transaction<Context>> {
371 typedef dom::syncedcontext::Transaction<Context> paramType;
373 static void Write(IPC::MessageWriter* aWriter, IProtocol* aActor,
374 const paramType& aParam) {
375 aParam.Write(aWriter, aActor);
378 static bool Read(IPC::MessageReader* aReader, IProtocol* aActor,
379 paramType* aResult) {
380 return aResult->Read(aReader, aActor);
384 template <typename Base, size_t Count>
385 struct IPDLParamTraits<dom::syncedcontext::FieldValues<Base, Count>> {
386 typedef dom::syncedcontext::FieldValues<Base, Count> paramType;
388 static void Write(IPC::MessageWriter* aWriter, IProtocol* aActor,
389 const paramType& aParam) {
390 aParam.Write(aWriter, aActor);
393 static bool Read(IPC::MessageReader* aReader, IProtocol* aActor,
394 paramType* aResult) {
395 return aResult->Read(aReader, aActor);
399 } // namespace ipc
400 } // namespace mozilla
402 #endif // !defined(mozilla_dom_SyncedContext_h)