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"
14 #include "nsThreadUtils.h"
20 struct FramePropertyDescriptorUntyped
{
22 * mDestructor will be called if it's non-null.
24 typedef void UntypedDestructor(void* aPropertyValue
);
25 UntypedDestructor
* mDestructor
;
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
;
38 * mDestructor and mDestructorWithFrame may both be null, in which case
39 * no value destruction is a no-op.
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
) {}
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
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.
65 struct FramePropertyDescriptor
: public FramePropertyDescriptorUntyped
{
66 typedef void Destructor(T
* aPropertyValue
);
67 typedef void DestructorWithFrame(const nsIFrame
* aFrame
, 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};
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 directly into our
102 // internal value slot (i.e. types that can fit in 64 bits). This class should
103 // never be defined, so that we won't use it for unexpected purpose by mistake.
104 template <typename T
>
105 class SmallValueHolder
;
109 template <typename T
>
110 struct FramePropertyTypeHelper
{
113 template <typename T
>
114 struct FramePropertyTypeHelper
<SmallValueHolder
<T
>> {
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 class FrameProperties
{
127 template <typename T
>
128 using Descriptor
= const FramePropertyDescriptor
<T
>*;
129 using UntypedDescriptor
= const FramePropertyDescriptorUntyped
*;
131 template <typename T
>
132 using PropertyType
= typename
detail::FramePropertyTypeHelper
<T
>::Type
;
134 explicit FrameProperties() = default;
137 MOZ_ASSERT(mProperties
.Length() == 0, "forgot to delete properties");
141 * Return true if we have no properties, otherwise return false.
143 bool IsEmpty() const { return mProperties
.IsEmpty(); }
146 * Set a property value. This requires a linear search through
147 * the properties of the frame. Any existing value for the property
150 template <typename T
>
151 void Set(Descriptor
<T
> aProperty
, PropertyType
<T
> aValue
,
152 const nsIFrame
* aFrame
) {
153 uint64_t v
= ReinterpretHelper
<T
>::ToInternalValue(aValue
);
154 SetInternal(aProperty
, v
, aFrame
);
158 * Add a property value; the descriptor MUST NOT already be present.
160 template <typename T
>
161 void Add(Descriptor
<T
> aProperty
, PropertyType
<T
> aValue
) {
162 MOZ_ASSERT(!Has(aProperty
), "duplicate frame property");
163 uint64_t v
= ReinterpretHelper
<T
>::ToInternalValue(aValue
);
164 AddInternal(aProperty
, v
);
168 * @return true if @aProperty is set. This requires a linear search through
169 * the properties of the frame.
171 * In most cases, this shouldn't be used outside of assertions, because if
172 * you're doing a lookup anyway it would be far more efficient to call Get()
173 * or Take() and check the aFoundResult outparam to find out whether the
174 * property is set. Legitimate non-assertion uses include:
176 * - Checking if a frame property is set in cases where that's all we want
177 * to know (i.e., we don't intend to read the actual value or remove the
180 * - Calling Has() before Set() in cases where we don't want to overwrite
181 * an existing value for the frame property.
183 template <typename T
>
184 bool Has(Descriptor
<T
> aProperty
) const {
185 return mProperties
.Contains(aProperty
, PropertyComparator());
189 * Get a property value. This requires a linear search through
190 * the properties of the frame. If the frame has no such property,
191 * returns zero-filled result, which means null for pointers and
192 * zero for integers and floating point types.
193 * @param aFoundResult if non-null, receives a value 'true' iff
194 * the frame has a value for the property. This lets callers
195 * disambiguate a null result, which can mean 'no such property' or
196 * 'property value is null'.
198 template <typename T
>
199 PropertyType
<T
> Get(Descriptor
<T
> aProperty
,
200 bool* aFoundResult
= nullptr) const {
201 uint64_t v
= GetInternal(aProperty
, aFoundResult
);
202 return ReinterpretHelper
<T
>::FromInternalValue(v
);
206 * Remove a property value, and return it without destroying it.
208 * This requires a linear search through the properties of the frame.
209 * If the frame has no such property, returns zero-filled result, which means
210 * null for pointers and zero for integers and floating point types.
211 * @param aFoundResult if non-null, receives a value 'true' iff
212 * the frame had a value for the property. This lets callers
213 * disambiguate a null result, which can mean 'no such property' or
214 * 'property value is null'.
216 template <typename T
>
217 PropertyType
<T
> Take(Descriptor
<T
> aProperty
, bool* aFoundResult
= nullptr) {
218 uint64_t v
= TakeInternal(aProperty
, aFoundResult
);
219 return ReinterpretHelper
<T
>::FromInternalValue(v
);
223 * Remove and destroy a property value. This requires a linear search through
224 * the properties of the frame. If the frame has no such property, nothing
227 template <typename T
>
228 void Remove(Descriptor
<T
> aProperty
, const nsIFrame
* aFrame
) {
229 RemoveInternal(aProperty
, aFrame
);
233 * Call @aFunction for each property or until @aFunction returns false.
236 void ForEach(F aFunction
) const {
238 size_t len
= mProperties
.Length();
240 for (const auto& prop
: mProperties
) {
241 bool shouldContinue
= aFunction(prop
.mProperty
, prop
.mValue
);
242 MOZ_ASSERT(len
== mProperties
.Length(),
243 "frame property list was modified by ForEach callback!");
244 if (!shouldContinue
) {
251 * Remove and destroy all property values for the frame.
253 void RemoveAll(const nsIFrame
* aFrame
) {
254 nsTArray
<PropertyValue
> toDelete
= std::move(mProperties
);
255 for (auto& prop
: toDelete
) {
256 prop
.DestroyValueFor(aFrame
);
258 MOZ_ASSERT(mProperties
.IsEmpty(), "a property dtor added new properties");
261 size_t SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf
) const {
262 // We currently report only the shallow size of the mProperties array.
263 // As for the PropertyValue entries: we don't need to measure the mProperty
264 // field of because it always points to static memory, and we can't measure
265 // mValue because the type is opaque.
266 // XXX Can we do better, e.g. with a method on the descriptor?
267 return mProperties
.ShallowSizeOfExcludingThis(aMallocSizeOf
);
271 // Prevent copying of FrameProperties; we should always return/pass around
272 // references to it, not copies!
273 FrameProperties(const FrameProperties
&) = delete;
274 FrameProperties
& operator=(const FrameProperties
&) = delete;
276 inline void SetInternal(UntypedDescriptor aProperty
, uint64_t aValue
,
277 const nsIFrame
* aFrame
);
279 inline void AddInternal(UntypedDescriptor aProperty
, uint64_t aValue
);
281 inline uint64_t GetInternal(UntypedDescriptor aProperty
,
282 bool* aFoundResult
) const;
284 inline uint64_t TakeInternal(UntypedDescriptor aProperty
, bool* aFoundResult
);
286 inline void RemoveInternal(UntypedDescriptor aProperty
,
287 const nsIFrame
* aFrame
);
289 template <typename T
>
290 struct ReinterpretHelper
{
291 static_assert(sizeof(PropertyType
<T
>) <= sizeof(uint64_t),
292 "size of the value must never be larger than 64 bits");
294 static uint64_t ToInternalValue(PropertyType
<T
> aValue
) {
296 memcpy(&v
, &aValue
, sizeof(aValue
));
300 static PropertyType
<T
> FromInternalValue(uint64_t aInternalValue
) {
301 PropertyType
<T
> value
;
302 memcpy(&value
, &aInternalValue
, sizeof(value
));
308 * Stores a property descriptor/value pair.
310 struct PropertyValue
{
311 PropertyValue() : mProperty(nullptr), mValue(0) {}
312 PropertyValue(UntypedDescriptor aProperty
, uint64_t aValue
)
313 : mProperty(aProperty
), mValue(aValue
) {}
315 // NOTE: This function converts our internal 64-bit-integer representation
316 // to a pointer-type representation. This is lossy on 32-bit systems, but it
317 // should be fine, as long as we *only* do this in cases where we're sure
318 // that the stored property-value is in fact a pointer. And we should have
319 // that assurance, since only pointer-typed frame properties are expected to
321 void DestroyValueFor(const nsIFrame
* aFrame
) {
322 if (mProperty
->mDestructor
) {
323 mProperty
->mDestructor(
324 ReinterpretHelper
<void*>::FromInternalValue(mValue
));
325 } else if (mProperty
->mDestructorWithFrame
) {
326 mProperty
->mDestructorWithFrame(
327 aFrame
, ReinterpretHelper
<void*>::FromInternalValue(mValue
));
331 UntypedDescriptor mProperty
;
336 * Used with an array of PropertyValues to allow lookups that compare
337 * only on the FramePropertyDescriptor.
339 class PropertyComparator
{
341 bool Equals(const PropertyValue
& a
, const PropertyValue
& b
) const {
342 return a
.mProperty
== b
.mProperty
;
344 bool Equals(UntypedDescriptor a
, const PropertyValue
& b
) const {
345 return a
== b
.mProperty
;
347 bool Equals(const PropertyValue
& a
, UntypedDescriptor b
) const {
348 return a
.mProperty
== b
;
352 nsTArray
<PropertyValue
> mProperties
;
355 inline uint64_t FrameProperties::GetInternal(UntypedDescriptor aProperty
,
356 bool* aFoundResult
) const {
357 MOZ_ASSERT(aProperty
, "Null property?");
359 return mProperties
.ApplyIf(
360 aProperty
, 0, PropertyComparator(),
361 [&aFoundResult
](const PropertyValue
& aPV
) -> uint64_t {
363 *aFoundResult
= true;
367 [&aFoundResult
]() -> uint64_t {
369 *aFoundResult
= false;
375 inline void FrameProperties::SetInternal(UntypedDescriptor aProperty
,
377 const nsIFrame
* aFrame
) {
378 MOZ_ASSERT(NS_IsMainThread());
379 MOZ_ASSERT(aProperty
, "Null property?");
382 aProperty
, 0, PropertyComparator(),
383 [&](PropertyValue
& aPV
) {
384 aPV
.DestroyValueFor(aFrame
);
387 [&]() { mProperties
.AppendElement(PropertyValue(aProperty
, aValue
)); });
390 inline void FrameProperties::AddInternal(UntypedDescriptor aProperty
,
392 MOZ_ASSERT(NS_IsMainThread());
393 MOZ_ASSERT(aProperty
, "Null property?");
395 mProperties
.AppendElement(PropertyValue(aProperty
, aValue
));
398 inline uint64_t FrameProperties::TakeInternal(UntypedDescriptor aProperty
,
399 bool* aFoundResult
) {
400 MOZ_ASSERT(NS_IsMainThread());
401 MOZ_ASSERT(aProperty
, "Null property?");
403 auto index
= mProperties
.IndexOf(aProperty
, 0, PropertyComparator());
404 if (index
== nsTArray
<PropertyValue
>::NoIndex
) {
406 *aFoundResult
= false;
412 *aFoundResult
= true;
415 uint64_t result
= mProperties
.Elements()[index
].mValue
;
416 mProperties
.RemoveElementAtUnsafe(index
);
421 inline void FrameProperties::RemoveInternal(UntypedDescriptor aProperty
,
422 const nsIFrame
* aFrame
) {
423 MOZ_ASSERT(NS_IsMainThread());
424 MOZ_ASSERT(aProperty
, "Null property?");
426 auto index
= mProperties
.IndexOf(aProperty
, 0, PropertyComparator());
427 if (index
!= nsTArray
<PropertyValue
>::NoIndex
) {
428 mProperties
.Elements()[index
].DestroyValueFor(aFrame
);
429 mProperties
.RemoveElementAtUnsafe(index
);
433 } // namespace mozilla
435 #endif /* FRAMEPROPERTIES_H_ */