no bug - Import translations from android-l10n r=release a=l10n CLOSED TREE
[gecko.git] / layout / base / FrameProperties.h
blob1a8021c3878a0fa5deb287b4666209631b0a662f
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* 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};
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 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;
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 class FrameProperties {
126 public:
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;
136 ~FrameProperties() {
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
148 * is destroyed.
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
178 * property).
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
225 * happens.
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.
235 template <class F>
236 void ForEach(F aFunction) const {
237 #ifdef DEBUG
238 size_t len = mProperties.Length();
239 #endif
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) {
245 return;
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);
270 private:
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) {
295 uint64_t v = 0;
296 memcpy(&v, &aValue, sizeof(aValue));
297 return v;
300 static PropertyType<T> FromInternalValue(uint64_t aInternalValue) {
301 PropertyType<T> value;
302 memcpy(&value, &aInternalValue, sizeof(value));
303 return 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
320 // have a destructor
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;
332 uint64_t mValue;
336 * Used with an array of PropertyValues to allow lookups that compare
337 * only on the FramePropertyDescriptor.
339 class PropertyComparator {
340 public:
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 {
362 if (aFoundResult) {
363 *aFoundResult = true;
365 return aPV.mValue;
367 [&aFoundResult]() -> uint64_t {
368 if (aFoundResult) {
369 *aFoundResult = false;
371 return 0;
375 inline void FrameProperties::SetInternal(UntypedDescriptor aProperty,
376 uint64_t aValue,
377 const nsIFrame* aFrame) {
378 MOZ_ASSERT(NS_IsMainThread());
379 MOZ_ASSERT(aProperty, "Null property?");
381 mProperties.ApplyIf(
382 aProperty, 0, PropertyComparator(),
383 [&](PropertyValue& aPV) {
384 aPV.DestroyValueFor(aFrame);
385 aPV.mValue = aValue;
387 [&]() { mProperties.AppendElement(PropertyValue(aProperty, aValue)); });
390 inline void FrameProperties::AddInternal(UntypedDescriptor aProperty,
391 uint64_t aValue) {
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) {
405 if (aFoundResult) {
406 *aFoundResult = false;
408 return 0;
411 if (aFoundResult) {
412 *aFoundResult = true;
415 uint64_t result = mProperties.Elements()[index].mValue;
416 mProperties.RemoveElementAtUnsafe(index);
418 return result;
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_ */