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