Bug 1646700 [wpt PR 24235] - Update picture-in-picture idlharness test, a=testonly
[gecko.git] / layout / base / FrameProperties.h
blob0d52e488d813b948ceec5bad13e22924b559e94d
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 FRAMEPROPERTIES_H_
8 #define FRAMEPROPERTIES_H_
10 #include "mozilla/DebugOnly.h"
11 #include "mozilla/MemoryReporting.h"
12 #include "mozilla/Unused.h"
13 #include "nsTArray.h"
14 #include "nsThreadUtils.h"
16 class nsIFrame;
18 namespace mozilla {
20 struct FramePropertyDescriptorUntyped {
21 /**
22 * mDestructor will be called if it's non-null.
24 typedef void UntypedDestructor(void* aPropertyValue);
25 UntypedDestructor* mDestructor;
26 /**
27 * mDestructorWithFrame will be called if it's non-null and mDestructor
28 * is null. WARNING: The frame passed to mDestructorWithFrame may
29 * be a dangling frame pointer, if this is being called during
30 * presshell teardown. Do not use it except to compare against
31 * other frame pointers. No frame will have been allocated with
32 * the same address yet.
34 typedef void UntypedDestructorWithFrame(const nsIFrame* aFrame,
35 void* aPropertyValue);
36 UntypedDestructorWithFrame* mDestructorWithFrame;
37 /**
38 * mDestructor and mDestructorWithFrame may both be null, in which case
39 * no value destruction is a no-op.
42 protected:
43 /**
44 * At most one destructor should be passed in. In general, you should
45 * just use the static function FramePropertyDescriptor::New* below
46 * instead of using this constructor directly.
48 constexpr FramePropertyDescriptorUntyped(
49 UntypedDestructor* aDtor, UntypedDestructorWithFrame* aDtorWithFrame)
50 : mDestructor(aDtor), mDestructorWithFrame(aDtorWithFrame) {}
53 /**
54 * A pointer to a FramePropertyDescriptor serves as a unique property ID.
55 * The FramePropertyDescriptor stores metadata about the property.
56 * Currently the only metadata is a destructor function. The destructor
57 * function is called on property values when they are overwritten or
58 * deleted.
60 * To use this class, declare a global (i.e., file, class or function-scope
61 * static member) FramePropertyDescriptor and pass its address as
62 * aProperty in the FrameProperties methods.
64 template <typename T>
65 struct FramePropertyDescriptor : public FramePropertyDescriptorUntyped {
66 typedef void Destructor(T* aPropertyValue);
67 typedef void DestructorWithFrame(const nsIFrame* aaFrame, T* aPropertyValue);
69 template <Destructor Dtor>
70 static constexpr const FramePropertyDescriptor<T> NewWithDestructor() {
71 return {Destruct<Dtor>, nullptr};
74 template <DestructorWithFrame Dtor>
75 static constexpr const FramePropertyDescriptor<T>
76 NewWithDestructorWithFrame() {
77 return {nullptr, DestructWithFrame<Dtor>};
80 static constexpr const FramePropertyDescriptor<T> NewWithoutDestructor() {
81 return {nullptr, nullptr};
84 private:
85 constexpr FramePropertyDescriptor(UntypedDestructor* aDtor,
86 UntypedDestructorWithFrame* aDtorWithFrame)
87 : FramePropertyDescriptorUntyped(aDtor, aDtorWithFrame) {}
89 template <Destructor Dtor>
90 static void Destruct(void* aPropertyValue) {
91 Dtor(static_cast<T*>(aPropertyValue));
94 template <DestructorWithFrame Dtor>
95 static void DestructWithFrame(const nsIFrame* aFrame, void* aPropertyValue) {
96 Dtor(aFrame, static_cast<T*>(aPropertyValue));
100 // SmallValueHolder<T> is a placeholder intended to be used as template
101 // argument of FramePropertyDescriptor for types which can fit into the
102 // size of a pointer directly. This class should never be defined, so
103 // that we won't use it for unexpected purpose by mistake.
104 template <typename T>
105 class SmallValueHolder;
107 namespace detail {
109 template <typename T>
110 struct FramePropertyTypeHelper {
111 typedef T* Type;
113 template <typename T>
114 struct FramePropertyTypeHelper<SmallValueHolder<T>> {
115 typedef T Type;
118 } // namespace detail
121 * The FrameProperties class is optimized for storing 0 or 1 properties on
122 * a given frame. Storing very large numbers of properties on a single
123 * frame will not be efficient.
125 * Property values are passed as void* but do not actually have to be
126 * valid pointers. You can use NS_INT32_TO_PTR/NS_PTR_TO_INT32 to
127 * store int32_t values. Null/zero values can be stored and retrieved.
128 * Of course, the destructor function (if any) must handle such values
129 * correctly.
131 class FrameProperties {
132 public:
133 template <typename T>
134 using Descriptor = const FramePropertyDescriptor<T>*;
135 using UntypedDescriptor = const FramePropertyDescriptorUntyped*;
137 template <typename T>
138 using PropertyType = typename detail::FramePropertyTypeHelper<T>::Type;
140 explicit FrameProperties() = default;
142 ~FrameProperties() {
143 MOZ_ASSERT(mProperties.Length() == 0, "forgot to delete properties");
147 * Return true if we have no properties, otherwise return false.
149 bool IsEmpty() const { return mProperties.IsEmpty(); }
152 * Set a property value. This requires a linear search through
153 * the properties of the frame. Any existing value for the property
154 * is destroyed.
156 template <typename T>
157 void Set(Descriptor<T> aProperty, PropertyType<T> aValue,
158 const nsIFrame* aFrame) {
159 void* ptr = ReinterpretHelper<T>::ToPointer(aValue);
160 SetInternal(aProperty, ptr, aFrame);
164 * Add a property value; the descriptor MUST NOT already be present.
166 template <typename T>
167 void Add(Descriptor<T> aProperty, PropertyType<T> aValue) {
168 MOZ_ASSERT(!Has(aProperty), "duplicate frame property");
169 void* ptr = ReinterpretHelper<T>::ToPointer(aValue);
170 AddInternal(aProperty, ptr);
174 * @return true if @aProperty is set. This requires a linear search through
175 * the properties of the frame.
177 * In most cases, this shouldn't be used outside of assertions, because if
178 * you're doing a lookup anyway it would be far more efficient to call Get()
179 * or Take() and check the aFoundResult outparam to find out whether the
180 * property is set. Legitimate non-assertion uses include:
182 * - Checking if a frame property is set in cases where that's all we want
183 * to know (i.e., we don't intend to read the actual value or remove the
184 * property).
186 * - Calling Has() before Set() in cases where we don't want to overwrite
187 * an existing value for the frame property.
189 * The HasSkippingBitCheck variant doesn't test NS_FRAME_HAS_PROPERTIES
190 * on aFrame, so it is safe to call after aFrame has been destroyed as
191 * long as, since that destruction happened, it isn't possible for a
192 * new frame to have been created and the same property added.
194 template <typename T>
195 bool Has(Descriptor<T> aProperty) const {
196 return mProperties.Contains(aProperty, PropertyComparator());
200 * Get a property value. This requires a linear search through
201 * the properties of the frame. If the frame has no such property,
202 * returns zero-filled result, which means null for pointers and
203 * zero for integers and floating point types.
204 * @param aFoundResult if non-null, receives a value 'true' iff
205 * the frame has a value for the property. This lets callers
206 * disambiguate a null result, which can mean 'no such property' or
207 * 'property value is null'.
209 template <typename T>
210 PropertyType<T> Get(Descriptor<T> aProperty,
211 bool* aFoundResult = nullptr) const {
212 void* ptr = GetInternal(aProperty, aFoundResult);
213 return ReinterpretHelper<T>::FromPointer(ptr);
217 * Remove a property value, and return it without destroying it.
219 * This requires a linear search through the properties of the frame.
220 * If the frame has no such property, returns zero-filled result, which means
221 * null for pointers and zero for integers and floating point types.
222 * @param aFoundResult if non-null, receives a value 'true' iff
223 * the frame had a value for the property. This lets callers
224 * disambiguate a null result, which can mean 'no such property' or
225 * 'property value is null'.
227 template <typename T>
228 PropertyType<T> Take(Descriptor<T> aProperty, bool* aFoundResult = nullptr) {
229 void* ptr = TakeInternal(aProperty, aFoundResult);
230 return ReinterpretHelper<T>::FromPointer(ptr);
234 * Remove and destroy a property value. This requires a linear search through
235 * the properties of the frame. If the frame has no such property, nothing
236 * happens.
238 template <typename T>
239 void Remove(Descriptor<T> aProperty, const nsIFrame* aFrame) {
240 RemoveInternal(aProperty, aFrame);
244 * Call @aFunction for each property or until @aFunction returns false.
246 template <class F>
247 void ForEach(F aFunction) const {
248 #ifdef DEBUG
249 size_t len = mProperties.Length();
250 #endif
251 for (const auto& prop : mProperties) {
252 bool shouldContinue = aFunction(prop.mProperty, prop.mValue);
253 MOZ_ASSERT(len == mProperties.Length(),
254 "frame property list was modified by ForEach callback!");
255 if (!shouldContinue) {
256 return;
262 * Remove and destroy all property values for the frame.
264 void RemoveAll(const nsIFrame* aFrame) {
265 nsTArray<PropertyValue> toDelete;
266 toDelete.SwapElements(mProperties);
267 for (auto& prop : toDelete) {
268 prop.DestroyValueFor(aFrame);
270 MOZ_ASSERT(mProperties.IsEmpty(), "a property dtor added new properties");
273 size_t SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) const {
274 // We currently report only the shallow size of the mProperties array.
275 // As for the PropertyValue entries: we don't need to measure the mProperty
276 // field of because it always points to static memory, and we can't measure
277 // mValue because the type is opaque.
278 // XXX Can we do better, e.g. with a method on the descriptor?
279 return mProperties.ShallowSizeOfExcludingThis(aMallocSizeOf);
282 private:
283 // Prevent copying of FrameProperties; we should always return/pass around
284 // references to it, not copies!
285 FrameProperties(const FrameProperties&) = delete;
286 FrameProperties& operator=(const FrameProperties&) = delete;
288 inline void SetInternal(UntypedDescriptor aProperty, void* aValue,
289 const nsIFrame* aFrame);
291 inline void AddInternal(UntypedDescriptor aProperty, void* aValue);
293 inline void* GetInternal(UntypedDescriptor aProperty,
294 bool* aFoundResult) const;
296 inline void* TakeInternal(UntypedDescriptor aProperty, bool* aFoundResult);
298 inline void RemoveInternal(UntypedDescriptor aProperty,
299 const nsIFrame* aFrame);
301 template <typename T>
302 struct ReinterpretHelper {
303 static_assert(sizeof(PropertyType<T>) <= sizeof(void*),
304 "size of the value must never be larger than a pointer");
306 static void* ToPointer(PropertyType<T> aValue) {
307 void* ptr = nullptr;
308 memcpy(&ptr, &aValue, sizeof(aValue));
309 return ptr;
312 static PropertyType<T> FromPointer(void* aPtr) {
313 PropertyType<T> value;
314 memcpy(&value, &aPtr, sizeof(value));
315 return value;
319 template <typename T>
320 struct ReinterpretHelper<T*> {
321 static void* ToPointer(T* aValue) { return static_cast<void*>(aValue); }
323 static T* FromPointer(void* aPtr) { return static_cast<T*>(aPtr); }
327 * Stores a property descriptor/value pair.
329 struct PropertyValue {
330 PropertyValue() : mProperty(nullptr), mValue(nullptr) {}
331 PropertyValue(UntypedDescriptor aProperty, void* aValue)
332 : mProperty(aProperty), mValue(aValue) {}
334 void DestroyValueFor(const nsIFrame* aFrame) {
335 if (mProperty->mDestructor) {
336 mProperty->mDestructor(mValue);
337 } else if (mProperty->mDestructorWithFrame) {
338 mProperty->mDestructorWithFrame(aFrame, mValue);
342 UntypedDescriptor mProperty;
343 void* mValue;
347 * Used with an array of PropertyValues to allow lookups that compare
348 * only on the FramePropertyDescriptor.
350 class PropertyComparator {
351 public:
352 bool Equals(const PropertyValue& a, const PropertyValue& b) const {
353 return a.mProperty == b.mProperty;
355 bool Equals(UntypedDescriptor a, const PropertyValue& b) const {
356 return a == b.mProperty;
358 bool Equals(const PropertyValue& a, UntypedDescriptor b) const {
359 return a.mProperty == b;
363 nsTArray<PropertyValue> mProperties;
366 inline void* FrameProperties::GetInternal(UntypedDescriptor aProperty,
367 bool* aFoundResult) const {
368 MOZ_ASSERT(aProperty, "Null property?");
370 return mProperties.ApplyIf(
371 aProperty, 0, PropertyComparator(),
372 [&aFoundResult](const PropertyValue& aPV) -> void* {
373 if (aFoundResult) {
374 *aFoundResult = true;
376 return aPV.mValue;
378 [&aFoundResult]() -> void* {
379 if (aFoundResult) {
380 *aFoundResult = false;
382 return nullptr;
386 inline void FrameProperties::SetInternal(UntypedDescriptor aProperty,
387 void* aValue, const nsIFrame* aFrame) {
388 MOZ_ASSERT(NS_IsMainThread());
389 MOZ_ASSERT(aProperty, "Null property?");
391 mProperties.ApplyIf(
392 aProperty, 0, PropertyComparator(),
393 [&](PropertyValue& aPV) {
394 aPV.DestroyValueFor(aFrame);
395 aPV.mValue = aValue;
397 [&]() { mProperties.AppendElement(PropertyValue(aProperty, aValue)); });
400 inline void FrameProperties::AddInternal(UntypedDescriptor aProperty,
401 void* aValue) {
402 MOZ_ASSERT(NS_IsMainThread());
403 MOZ_ASSERT(aProperty, "Null property?");
405 mProperties.AppendElement(PropertyValue(aProperty, aValue));
408 inline void* FrameProperties::TakeInternal(UntypedDescriptor aProperty,
409 bool* aFoundResult) {
410 MOZ_ASSERT(NS_IsMainThread());
411 MOZ_ASSERT(aProperty, "Null property?");
413 auto index = mProperties.IndexOf(aProperty, 0, PropertyComparator());
414 if (index == nsTArray<PropertyValue>::NoIndex) {
415 if (aFoundResult) {
416 *aFoundResult = false;
418 return nullptr;
421 if (aFoundResult) {
422 *aFoundResult = true;
425 void* result = mProperties.Elements()[index].mValue;
426 mProperties.RemoveElementAt(index);
428 return result;
431 inline void FrameProperties::RemoveInternal(UntypedDescriptor aProperty,
432 const nsIFrame* aFrame) {
433 MOZ_ASSERT(NS_IsMainThread());
434 MOZ_ASSERT(aProperty, "Null property?");
436 auto index = mProperties.IndexOf(aProperty, 0, PropertyComparator());
437 if (index != nsTArray<PropertyValue>::NoIndex) {
438 mProperties.Elements()[index].DestroyValueFor(aFrame);
439 mProperties.RemoveElementAt(index);
443 } // namespace mozilla
445 #endif /* FRAMEPROPERTIES_H_ */