michael@0: /* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*- michael@0: * This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #ifndef FRAMEPROPERTYTABLE_H_ michael@0: #define FRAMEPROPERTYTABLE_H_ michael@0: michael@0: #include "mozilla/MemoryReporting.h" michael@0: #include "nsTArray.h" michael@0: #include "nsTHashtable.h" michael@0: #include "nsHashKeys.h" michael@0: michael@0: class nsIFrame; michael@0: michael@0: namespace mozilla { michael@0: michael@0: struct FramePropertyDescriptor; michael@0: michael@0: typedef void (*FramePropertyDestructor)(void* aPropertyValue); michael@0: typedef void (*FramePropertyDestructorWithFrame)(nsIFrame* aFrame, michael@0: void* aPropertyValue); michael@0: michael@0: /** michael@0: * A pointer to a FramePropertyDescriptor serves as a unique property ID. michael@0: * The FramePropertyDescriptor stores metadata about the property. michael@0: * Currently the only metadata is a destructor function. The destructor michael@0: * function is called on property values when they are overwritten or michael@0: * deleted. michael@0: * michael@0: * To use this class, declare a global (i.e., file, class or function-scope michael@0: * static member) FramePropertyDescriptor and pass its address as michael@0: * aProperty in the FramePropertyTable methods. michael@0: */ michael@0: struct FramePropertyDescriptor { michael@0: /** michael@0: * mDestructor will be called if it's non-null. michael@0: */ michael@0: FramePropertyDestructor mDestructor; michael@0: /** michael@0: * mDestructorWithFrame will be called if it's non-null and mDestructor michael@0: * is null. WARNING: The frame passed to mDestructorWithFrame may michael@0: * be a dangling frame pointer, if this is being called during michael@0: * presshell teardown. Do not use it except to compare against michael@0: * other frame pointers. No frame will have been allocated with michael@0: * the same address yet. michael@0: */ michael@0: FramePropertyDestructorWithFrame mDestructorWithFrame; michael@0: /** michael@0: * mDestructor and mDestructorWithFrame may both be null, in which case michael@0: * no value destruction is a no-op. michael@0: */ michael@0: }; michael@0: michael@0: /** michael@0: * The FramePropertyTable is optimized for storing 0 or 1 properties on michael@0: * a given frame. Storing very large numbers of properties on a single michael@0: * frame will not be efficient. michael@0: * michael@0: * Property values are passed as void* but do not actually have to be michael@0: * valid pointers. You can use NS_INT32_TO_PTR/NS_PTR_TO_INT32 to michael@0: * store int32_t values. Null/zero values can be stored and retrieved. michael@0: * Of course, the destructor function (if any) must handle such values michael@0: * correctly. michael@0: */ michael@0: class FramePropertyTable { michael@0: public: michael@0: FramePropertyTable() : mLastFrame(nullptr), mLastEntry(nullptr) michael@0: { michael@0: } michael@0: ~FramePropertyTable() michael@0: { michael@0: DeleteAll(); michael@0: } michael@0: michael@0: /** michael@0: * Set a property value on a frame. This requires one hashtable michael@0: * lookup (using the frame as the key) and a linear search through michael@0: * the properties of that frame. Any existing value for the property michael@0: * is destroyed. michael@0: */ michael@0: void Set(nsIFrame* aFrame, const FramePropertyDescriptor* aProperty, michael@0: void* aValue); michael@0: /** michael@0: * Get a property value for a frame. This requires one hashtable michael@0: * lookup (using the frame as the key) and a linear search through michael@0: * the properties of that frame. If the frame has no such property, michael@0: * returns null. michael@0: * @param aFoundResult if non-null, receives a value 'true' iff michael@0: * the frame has a value for the property. This lets callers michael@0: * disambiguate a null result, which can mean 'no such property' or michael@0: * 'property value is null'. michael@0: */ michael@0: void* Get(const nsIFrame* aFrame, const FramePropertyDescriptor* aProperty, michael@0: bool* aFoundResult = nullptr); michael@0: /** michael@0: * Remove a property value for a frame. This requires one hashtable michael@0: * lookup (using the frame as the key) and a linear search through michael@0: * the properties of that frame. The old property value is returned michael@0: * (and not destroyed). If the frame has no such property, michael@0: * returns null. michael@0: * @param aFoundResult if non-null, receives a value 'true' iff michael@0: * the frame had a value for the property. This lets callers michael@0: * disambiguate a null result, which can mean 'no such property' or michael@0: * 'property value is null'. michael@0: */ michael@0: void* Remove(nsIFrame* aFrame, const FramePropertyDescriptor* aProperty, michael@0: bool* aFoundResult = nullptr); michael@0: /** michael@0: * Remove and destroy a property value for a frame. This requires one michael@0: * hashtable lookup (using the frame as the key) and a linear search michael@0: * through the properties of that frame. If the frame has no such michael@0: * property, nothing happens. michael@0: */ michael@0: void Delete(nsIFrame* aFrame, const FramePropertyDescriptor* aProperty); michael@0: /** michael@0: * Remove and destroy all property values for a frame. This requires one michael@0: * hashtable lookup (using the frame as the key). michael@0: */ michael@0: void DeleteAllFor(nsIFrame* aFrame); michael@0: /** michael@0: * Remove and destroy all property values for all frames. michael@0: */ michael@0: void DeleteAll(); michael@0: michael@0: size_t SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) const; michael@0: michael@0: protected: michael@0: /** michael@0: * Stores a property descriptor/value pair. It can also be used to michael@0: * store an nsTArray of PropertyValues. michael@0: */ michael@0: struct PropertyValue { michael@0: PropertyValue() : mProperty(nullptr), mValue(nullptr) {} michael@0: PropertyValue(const FramePropertyDescriptor* aProperty, void* aValue) michael@0: : mProperty(aProperty), mValue(aValue) {} michael@0: michael@0: bool IsArray() { return !mProperty && mValue; } michael@0: nsTArray* ToArray() michael@0: { michael@0: NS_ASSERTION(IsArray(), "Must be array"); michael@0: return reinterpret_cast*>(&mValue); michael@0: } michael@0: michael@0: void DestroyValueFor(nsIFrame* aFrame) { michael@0: if (mProperty->mDestructor) { michael@0: mProperty->mDestructor(mValue); michael@0: } else if (mProperty->mDestructorWithFrame) { michael@0: mProperty->mDestructorWithFrame(aFrame, mValue); michael@0: } michael@0: } michael@0: michael@0: size_t SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) { michael@0: size_t n = 0; michael@0: // We don't need to measure mProperty because it always points to static michael@0: // memory. As for mValue: if it's a single value we can't measure it, michael@0: // because the type is opaque; if it's an array, we measure the array michael@0: // storage, but we can't measure the individual values, again because michael@0: // their types are opaque. michael@0: if (IsArray()) { michael@0: nsTArray* array = ToArray(); michael@0: n += array->SizeOfExcludingThis(aMallocSizeOf); michael@0: } michael@0: return n; michael@0: } michael@0: michael@0: const FramePropertyDescriptor* mProperty; michael@0: void* mValue; michael@0: }; michael@0: michael@0: /** michael@0: * Used with an array of PropertyValues to allow lookups that compare michael@0: * only on the FramePropertyDescriptor. michael@0: */ michael@0: class PropertyComparator { michael@0: public: michael@0: bool Equals(const PropertyValue& a, const PropertyValue& b) const { michael@0: return a.mProperty == b.mProperty; michael@0: } michael@0: bool Equals(const FramePropertyDescriptor* a, const PropertyValue& b) const { michael@0: return a == b.mProperty; michael@0: } michael@0: bool Equals(const PropertyValue& a, const FramePropertyDescriptor* b) const { michael@0: return a.mProperty == b; michael@0: } michael@0: }; michael@0: michael@0: /** michael@0: * Our hashtable entry. The key is an nsIFrame*, the value is a michael@0: * PropertyValue representing one or more property/value pairs. michael@0: */ michael@0: class Entry : public nsPtrHashKey michael@0: { michael@0: public: michael@0: Entry(KeyTypePointer aKey) : nsPtrHashKey(aKey) {} michael@0: Entry(const Entry &toCopy) : michael@0: nsPtrHashKey(toCopy), mProp(toCopy.mProp) {} michael@0: michael@0: PropertyValue mProp; michael@0: }; michael@0: michael@0: static void DeleteAllForEntry(Entry* aEntry); michael@0: static PLDHashOperator DeleteEnumerator(Entry* aEntry, void* aArg); michael@0: michael@0: static size_t SizeOfPropertyTableEntryExcludingThis(Entry* aEntry, michael@0: mozilla::MallocSizeOf aMallocSizeOf, void *); michael@0: michael@0: nsTHashtable mEntries; michael@0: nsIFrame* mLastFrame; michael@0: Entry* mLastEntry; michael@0: }; michael@0: michael@0: /** michael@0: * This class encapsulates the properties of a frame. michael@0: */ michael@0: class FrameProperties { michael@0: public: michael@0: FrameProperties(FramePropertyTable* aTable, nsIFrame* aFrame) michael@0: : mTable(aTable), mFrame(aFrame) {} michael@0: FrameProperties(FramePropertyTable* aTable, const nsIFrame* aFrame) michael@0: : mTable(aTable), mFrame(const_cast(aFrame)) {} michael@0: michael@0: void Set(const FramePropertyDescriptor* aProperty, void* aValue) const michael@0: { michael@0: mTable->Set(mFrame, aProperty, aValue); michael@0: } michael@0: void* Get(const FramePropertyDescriptor* aProperty, michael@0: bool* aFoundResult = nullptr) const michael@0: { michael@0: return mTable->Get(mFrame, aProperty, aFoundResult); michael@0: } michael@0: void* Remove(const FramePropertyDescriptor* aProperty, michael@0: bool* aFoundResult = nullptr) const michael@0: { michael@0: return mTable->Remove(mFrame, aProperty, aFoundResult); michael@0: } michael@0: void Delete(const FramePropertyDescriptor* aProperty) michael@0: { michael@0: mTable->Delete(mFrame, aProperty); michael@0: } michael@0: michael@0: private: michael@0: FramePropertyTable* mTable; michael@0: nsIFrame* mFrame; michael@0: }; michael@0: michael@0: } michael@0: michael@0: #endif /* FRAMEPROPERTYTABLE_H_ */