Line data Source code
1 : /* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
2 : * This Source Code Form is subject to the terms of the Mozilla Public
3 : * License, v. 2.0. If a copy of the MPL was not distributed with this
4 : * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5 :
6 : #ifndef FRAMEPROPERTIES_H_
7 : #define FRAMEPROPERTIES_H_
8 :
9 : #include "mozilla/DebugOnly.h"
10 : #include "mozilla/MemoryReporting.h"
11 : #include "mozilla/TypeTraits.h"
12 : #include "mozilla/Unused.h"
13 : #include "nsTArray.h"
14 : #include "nsThreadUtils.h"
15 :
16 : class nsIFrame;
17 :
18 : namespace mozilla {
19 :
20 : struct FramePropertyDescriptorUntyped
21 : {
22 : /**
23 : * mDestructor will be called if it's non-null.
24 : */
25 : typedef void UntypedDestructor(void* aPropertyValue);
26 : UntypedDestructor* mDestructor;
27 : /**
28 : * mDestructorWithFrame will be called if it's non-null and mDestructor
29 : * is null. WARNING: The frame passed to mDestructorWithFrame may
30 : * be a dangling frame pointer, if this is being called during
31 : * presshell teardown. Do not use it except to compare against
32 : * other frame pointers. No frame will have been allocated with
33 : * the same address yet.
34 : */
35 : typedef void UntypedDestructorWithFrame(const nsIFrame* aFrame,
36 : void* aPropertyValue);
37 : UntypedDestructorWithFrame* mDestructorWithFrame;
38 : /**
39 : * mDestructor and mDestructorWithFrame may both be null, in which case
40 : * no value destruction is a no-op.
41 : */
42 :
43 : protected:
44 : /**
45 : * At most one destructor should be passed in. In general, you should
46 : * just use the static function FramePropertyDescriptor::New* below
47 : * instead of using this constructor directly.
48 : */
49 : constexpr FramePropertyDescriptorUntyped(
50 : UntypedDestructor* aDtor, UntypedDestructorWithFrame* aDtorWithFrame)
51 : : mDestructor(aDtor)
52 : , mDestructorWithFrame(aDtorWithFrame)
53 : {}
54 : };
55 :
56 : /**
57 : * A pointer to a FramePropertyDescriptor serves as a unique property ID.
58 : * The FramePropertyDescriptor stores metadata about the property.
59 : * Currently the only metadata is a destructor function. The destructor
60 : * function is called on property values when they are overwritten or
61 : * deleted.
62 : *
63 : * To use this class, declare a global (i.e., file, class or function-scope
64 : * static member) FramePropertyDescriptor and pass its address as
65 : * aProperty in the FrameProperties methods.
66 : */
67 : template<typename T>
68 : struct FramePropertyDescriptor : public FramePropertyDescriptorUntyped
69 : {
70 : typedef void Destructor(T* aPropertyValue);
71 : typedef void DestructorWithFrame(const nsIFrame* aaFrame,
72 : T* aPropertyValue);
73 :
74 : template<Destructor Dtor>
75 : static constexpr const FramePropertyDescriptor<T> NewWithDestructor()
76 : {
77 : return { Destruct<Dtor>, nullptr };
78 : }
79 :
80 : template<DestructorWithFrame Dtor>
81 : static constexpr
82 : const FramePropertyDescriptor<T> NewWithDestructorWithFrame()
83 : {
84 : return { nullptr, DestructWithFrame<Dtor> };
85 : }
86 :
87 : static constexpr const FramePropertyDescriptor<T> NewWithoutDestructor()
88 : {
89 : return { nullptr, nullptr };
90 : }
91 :
92 : private:
93 : constexpr FramePropertyDescriptor(
94 : UntypedDestructor* aDtor, UntypedDestructorWithFrame* aDtorWithFrame)
95 : : FramePropertyDescriptorUntyped(aDtor, aDtorWithFrame)
96 : {}
97 :
98 : template<Destructor Dtor>
99 180 : static void Destruct(void* aPropertyValue)
100 : {
101 180 : Dtor(static_cast<T*>(aPropertyValue));
102 180 : }
103 :
104 : template<DestructorWithFrame Dtor>
105 : static void DestructWithFrame(const nsIFrame* aFrame, void* aPropertyValue)
106 : {
107 : Dtor(aFrame, static_cast<T*>(aPropertyValue));
108 : }
109 : };
110 :
111 : // SmallValueHolder<T> is a placeholder intended to be used as template
112 : // argument of FramePropertyDescriptor for types which can fit into the
113 : // size of a pointer directly. This class should never be defined, so
114 : // that we won't use it for unexpected purpose by mistake.
115 : template<typename T>
116 : class SmallValueHolder;
117 :
118 : namespace detail {
119 :
120 : template<typename T>
121 : struct FramePropertyTypeHelper
122 : {
123 : typedef T* Type;
124 : };
125 : template<typename T>
126 : struct FramePropertyTypeHelper<SmallValueHolder<T>>
127 : {
128 : typedef T Type;
129 : };
130 :
131 : }
132 :
133 : /**
134 : * The FrameProperties class is optimized for storing 0 or 1 properties on
135 : * a given frame. Storing very large numbers of properties on a single
136 : * frame will not be efficient.
137 : *
138 : * Property values are passed as void* but do not actually have to be
139 : * valid pointers. You can use NS_INT32_TO_PTR/NS_PTR_TO_INT32 to
140 : * store int32_t values. Null/zero values can be stored and retrieved.
141 : * Of course, the destructor function (if any) must handle such values
142 : * correctly.
143 : */
144 : class FrameProperties
145 : {
146 : public:
147 : template<typename T>
148 : using Descriptor = const FramePropertyDescriptor<T>*;
149 : using UntypedDescriptor = const FramePropertyDescriptorUntyped*;
150 :
151 : template<typename T>
152 : using PropertyType = typename detail::FramePropertyTypeHelper<T>::Type;
153 :
154 666 : explicit FrameProperties()
155 666 : {
156 666 : }
157 :
158 126 : ~FrameProperties()
159 126 : {
160 126 : MOZ_ASSERT(mProperties.Length() == 0, "forgot to delete properties");
161 126 : }
162 :
163 : /**
164 : * Return true if we have no properties, otherwise return false.
165 : */
166 76 : bool IsEmpty() const { return mProperties.IsEmpty(); }
167 :
168 : /**
169 : * Set a property value. This requires a linear search through
170 : * the properties of the frame. Any existing value for the property
171 : * is destroyed.
172 : */
173 : template<typename T>
174 469 : void Set(Descriptor<T> aProperty, PropertyType<T> aValue,
175 : const nsIFrame* aFrame)
176 : {
177 469 : void* ptr = ReinterpretHelper<T>::ToPointer(aValue);
178 469 : SetInternal(aProperty, ptr, aFrame);
179 469 : }
180 :
181 : /**
182 : * Add a property value; the descriptor MUST NOT already be present.
183 : */
184 : template<typename T>
185 126 : void Add(Descriptor<T> aProperty, PropertyType<T> aValue)
186 : {
187 126 : MOZ_ASSERT(!Has(aProperty), "duplicate frame property");
188 126 : void* ptr = ReinterpretHelper<T>::ToPointer(aValue);
189 126 : AddInternal(aProperty, ptr);
190 126 : }
191 :
192 : /**
193 : * @return true if @aProperty is set. This requires a linear search through the
194 : * properties of the frame.
195 : *
196 : * In most cases, this shouldn't be used outside of assertions, because if
197 : * you're doing a lookup anyway it would be far more efficient to call Get()
198 : * or Remove() and check the aFoundResult outparam to find out whether the
199 : * property is set. Legitimate non-assertion uses include:
200 : *
201 : * - Checking if a frame property is set in cases where that's all we want
202 : * to know (i.e., we don't intend to read the actual value or remove the
203 : * property).
204 : *
205 : * - Calling Has() before Set() in cases where we don't want to overwrite
206 : * an existing value for the frame property.
207 : *
208 : * The HasSkippingBitCheck variant doesn't test NS_FRAME_HAS_PROPERTIES
209 : * on aFrame, so it is safe to call after aFrame has been destroyed as
210 : * long as, since that destruction happened, it isn't possible for a
211 : * new frame to have been created and the same property added.
212 : */
213 : template<typename T>
214 126 : bool Has(Descriptor<T> aProperty) const
215 : {
216 : return mProperties.IndexOf(aProperty, 0, PropertyComparator())
217 126 : != nsTArray<PropertyValue>::NoIndex;
218 : }
219 :
220 : /**
221 : * Get a property value. This requires a linear search through
222 : * the properties of the frame. If the frame has no such property,
223 : * returns zero-filled result, which means null for pointers and
224 : * zero for integers and floating point types.
225 : * @param aFoundResult if non-null, receives a value 'true' iff
226 : * the frame has a value for the property. This lets callers
227 : * disambiguate a null result, which can mean 'no such property' or
228 : * 'property value is null'.
229 : */
230 : template<typename T>
231 10004 : PropertyType<T> Get(Descriptor<T> aProperty,
232 : bool* aFoundResult = nullptr) const
233 : {
234 10004 : void* ptr = GetInternal(aProperty, aFoundResult);
235 10004 : return ReinterpretHelper<T>::FromPointer(ptr);
236 : }
237 :
238 : /**
239 : * Remove a property value. This requires a linear search through
240 : * the properties of the frame. The old property value is returned
241 : * (and not destroyed). If the frame has no such property,
242 : * returns zero-filled result, which means null for pointers and
243 : * zero for integers and floating point types.
244 : * @param aFoundResult if non-null, receives a value 'true' iff
245 : * the frame had a value for the property. This lets callers
246 : * disambiguate a null result, which can mean 'no such property' or
247 : * 'property value is null'.
248 : */
249 : template<typename T>
250 108 : PropertyType<T> Remove(Descriptor<T> aProperty,
251 : bool* aFoundResult = nullptr)
252 : {
253 108 : void* ptr = RemoveInternal(aProperty, aFoundResult);
254 108 : return ReinterpretHelper<T>::FromPointer(ptr);
255 : }
256 :
257 : /**
258 : * Remove and destroy a property value. This requires a linear search
259 : * through the properties of the frame. If the frame has no such
260 : * property, nothing happens.
261 : */
262 : template<typename T>
263 12412 : void Delete(Descriptor<T> aProperty, const nsIFrame* aFrame)
264 : {
265 12412 : DeleteInternal(aProperty, aFrame);
266 12412 : }
267 :
268 : /**
269 : * Call @aFunction for each property or until @aFunction returns false.
270 : */
271 : template<class F>
272 4844 : void ForEach(F aFunction) const
273 : {
274 : #ifdef DEBUG
275 4844 : size_t len = mProperties.Length();
276 : #endif
277 7198 : for (const auto& prop : mProperties) {
278 2354 : bool shouldContinue = aFunction(prop.mProperty, prop.mValue);
279 2354 : MOZ_ASSERT(len == mProperties.Length(),
280 : "frame property list was modified by ForEach callback!");
281 2354 : if (!shouldContinue) {
282 0 : return;
283 : }
284 : }
285 : }
286 :
287 : /**
288 : * Remove and destroy all property values for the frame.
289 : */
290 252 : void DeleteAll(const nsIFrame* aFrame) {
291 504 : mozilla::DebugOnly<size_t> len = mProperties.Length();
292 290 : for (auto& prop : mProperties) {
293 38 : prop.DestroyValueFor(aFrame);
294 38 : MOZ_ASSERT(mProperties.Length() == len);
295 : }
296 252 : mProperties.Clear();
297 252 : }
298 :
299 124 : size_t SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) const {
300 : // We currently report only the shallow size of the mProperties array.
301 : // As for the PropertyValue entries: we don't need to measure the mProperty
302 : // field of because it always points to static memory, and we can't measure
303 : // mValue because the type is opaque.
304 : // XXX Can we do better, e.g. with a method on the descriptor?
305 124 : return mProperties.ShallowSizeOfExcludingThis(aMallocSizeOf);
306 : }
307 :
308 : private:
309 : // Prevent copying of FrameProperties; we should always return/pass around
310 : // references to it, not copies!
311 : FrameProperties(const FrameProperties&) = delete;
312 : FrameProperties& operator=(const FrameProperties&) = delete;
313 :
314 : inline void
315 : SetInternal(UntypedDescriptor aProperty, void* aValue,
316 : const nsIFrame* aFrame);
317 :
318 : inline void
319 : AddInternal(UntypedDescriptor aProperty, void* aValue);
320 :
321 : inline void*
322 : GetInternal(UntypedDescriptor aProperty, bool* aFoundResult) const;
323 :
324 : inline void*
325 : RemoveInternal(UntypedDescriptor aProperty, bool* aFoundResult);
326 :
327 : inline void
328 : DeleteInternal(UntypedDescriptor aProperty, const nsIFrame* aFrame);
329 :
330 : template<typename T>
331 : struct ReinterpretHelper
332 : {
333 : static_assert(sizeof(PropertyType<T>) <= sizeof(void*),
334 : "size of the value must never be larger than a pointer");
335 :
336 595 : static void* ToPointer(PropertyType<T> aValue)
337 : {
338 595 : void* ptr = nullptr;
339 595 : memcpy(&ptr, &aValue, sizeof(aValue));
340 595 : return ptr;
341 : }
342 :
343 10112 : static PropertyType<T> FromPointer(void* aPtr)
344 : {
345 : PropertyType<T> value;
346 10112 : memcpy(&value, &aPtr, sizeof(value));
347 10112 : return value;
348 : }
349 : };
350 :
351 : template<typename T>
352 : struct ReinterpretHelper<T*>
353 : {
354 : static void* ToPointer(T* aValue)
355 : {
356 : return static_cast<void*>(aValue);
357 : }
358 :
359 : static T* FromPointer(void* aPtr)
360 : {
361 : return static_cast<T*>(aPtr);
362 : }
363 : };
364 :
365 : /**
366 : * Stores a property descriptor/value pair.
367 : */
368 : struct PropertyValue {
369 : PropertyValue() : mProperty(nullptr), mValue(nullptr) {}
370 486 : PropertyValue(UntypedDescriptor aProperty, void* aValue)
371 486 : : mProperty(aProperty), mValue(aValue) {}
372 :
373 272 : void DestroyValueFor(const nsIFrame* aFrame) {
374 272 : if (mProperty->mDestructor) {
375 180 : mProperty->mDestructor(mValue);
376 92 : } else if (mProperty->mDestructorWithFrame) {
377 0 : mProperty->mDestructorWithFrame(aFrame, mValue);
378 : }
379 272 : }
380 :
381 : UntypedDescriptor mProperty;
382 : void* mValue;
383 : };
384 :
385 : /**
386 : * Used with an array of PropertyValues to allow lookups that compare
387 : * only on the FramePropertyDescriptor.
388 : */
389 : class PropertyComparator {
390 : public:
391 : bool Equals(const PropertyValue& a, const PropertyValue& b) const {
392 : return a.mProperty == b.mProperty;
393 : }
394 : bool Equals(UntypedDescriptor a, const PropertyValue& b) const {
395 : return a == b.mProperty;
396 : }
397 16568 : bool Equals(const PropertyValue& a, UntypedDescriptor b) const {
398 16568 : return a.mProperty == b;
399 : }
400 : };
401 :
402 : nsTArray<PropertyValue> mProperties;
403 : };
404 :
405 :
406 : inline void*
407 10004 : FrameProperties::GetInternal(UntypedDescriptor aProperty,
408 : bool* aFoundResult) const
409 : {
410 10004 : MOZ_ASSERT(aProperty, "Null property?");
411 :
412 10004 : auto index = mProperties.IndexOf(aProperty, 0, PropertyComparator());
413 10004 : if (index == nsTArray<PropertyValue>::NoIndex) {
414 4672 : if (aFoundResult) {
415 1304 : *aFoundResult = false;
416 : }
417 4672 : return nullptr;
418 : }
419 :
420 5332 : if (aFoundResult) {
421 0 : *aFoundResult = true;
422 : }
423 5332 : return mProperties.ElementAt(index).mValue;
424 : }
425 :
426 : inline void
427 469 : FrameProperties::SetInternal(UntypedDescriptor aProperty, void* aValue,
428 : const nsIFrame* aFrame)
429 : {
430 469 : MOZ_ASSERT(NS_IsMainThread());
431 469 : MOZ_ASSERT(aProperty, "Null property?");
432 :
433 469 : auto index = mProperties.IndexOf(aProperty, 0, PropertyComparator());
434 469 : if (index != nsTArray<PropertyValue>::NoIndex) {
435 109 : PropertyValue* pv = &mProperties.ElementAt(index);
436 109 : pv->DestroyValueFor(aFrame);
437 109 : pv->mValue = aValue;
438 109 : return;
439 : }
440 :
441 360 : mProperties.AppendElement(PropertyValue(aProperty, aValue));
442 : }
443 :
444 : inline void
445 126 : FrameProperties::AddInternal(UntypedDescriptor aProperty, void* aValue)
446 : {
447 126 : MOZ_ASSERT(NS_IsMainThread());
448 126 : MOZ_ASSERT(aProperty, "Null property?");
449 :
450 126 : mProperties.AppendElement(PropertyValue(aProperty, aValue));
451 126 : }
452 :
453 : inline void*
454 108 : FrameProperties::RemoveInternal(UntypedDescriptor aProperty, bool* aFoundResult)
455 : {
456 108 : MOZ_ASSERT(NS_IsMainThread());
457 108 : MOZ_ASSERT(aProperty, "Null property?");
458 :
459 108 : auto index = mProperties.IndexOf(aProperty, 0, PropertyComparator());
460 108 : if (index == nsTArray<PropertyValue>::NoIndex) {
461 107 : if (aFoundResult) {
462 0 : *aFoundResult = false;
463 : }
464 107 : return nullptr;
465 : }
466 :
467 1 : if (aFoundResult) {
468 0 : *aFoundResult = true;
469 : }
470 :
471 1 : void* result = mProperties.ElementAt(index).mValue;
472 1 : mProperties.RemoveElementAt(index);
473 :
474 1 : return result;
475 : }
476 :
477 : inline void
478 12412 : FrameProperties::DeleteInternal(UntypedDescriptor aProperty,
479 : const nsIFrame* aFrame)
480 : {
481 12412 : MOZ_ASSERT(NS_IsMainThread());
482 12412 : MOZ_ASSERT(aProperty, "Null property?");
483 :
484 12412 : auto index = mProperties.IndexOf(aProperty, 0, PropertyComparator());
485 12412 : if (index != nsTArray<PropertyValue>::NoIndex) {
486 125 : mProperties.ElementAt(index).DestroyValueFor(aFrame);
487 125 : mProperties.RemoveElementAt(index);
488 : }
489 12412 : }
490 :
491 : } // namespace mozilla
492 :
493 : #endif /* FRAMEPROPERTIES_H_ */
|