From 00812e30dfa70f9b1a752cf0d09de00f6d401c85 Mon Sep 17 00:00:00 2001 From: win7-7 Date: Wed, 26 Jun 2019 01:51:45 +0300 Subject: Attach FrameProperties to each frame instead of using a shared hashtable Dispense the shared hashtable and instead attach the frame property list directly to nsIFrame. --- layout/base/FrameProperties.h | 440 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 440 insertions(+) create mode 100644 layout/base/FrameProperties.h (limited to 'layout/base/FrameProperties.h') diff --git a/layout/base/FrameProperties.h b/layout/base/FrameProperties.h new file mode 100644 index 000000000..bba3ee06b --- /dev/null +++ b/layout/base/FrameProperties.h @@ -0,0 +1,440 @@ +/* -*- 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 FRAMEPROPERTIES_H_ +#define FRAMEPROPERTIES_H_ + +#include "mozilla/DebugOnly.h" +#include "mozilla/MemoryReporting.h" +#include "mozilla/TypeTraits.h" +#include "mozilla/Unused.h" +#include "nsTArray.h" +#include "nsThreadUtils.h" + +class nsIFrame; + +namespace mozilla { + +struct FramePropertyDescriptorUntyped +{ + /** + * mDestructor will be called if it's non-null. + */ + typedef void UntypedDestructor(void* aPropertyValue); + UntypedDestructor* 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. + */ + typedef void UntypedDestructorWithFrame(const nsIFrame* aFrame, + void* aPropertyValue); + UntypedDestructorWithFrame* mDestructorWithFrame; + /** + * mDestructor and mDestructorWithFrame may both be null, in which case + * no value destruction is a no-op. + */ + +protected: + /** + * At most one destructor should be passed in. In general, you should + * just use the static function FramePropertyDescriptor::New* below + * instead of using this constructor directly. + */ + constexpr FramePropertyDescriptorUntyped( + UntypedDestructor* aDtor, UntypedDestructorWithFrame* aDtorWithFrame) + : mDestructor(aDtor) + , mDestructorWithFrame(aDtorWithFrame) + {} +}; + +/** + * 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. + */ +template +struct FramePropertyDescriptor : public FramePropertyDescriptorUntyped +{ + typedef void Destructor(T* aPropertyValue); + typedef void DestructorWithFrame(const nsIFrame* aaFrame, + T* aPropertyValue); + + template + static constexpr const FramePropertyDescriptor NewWithDestructor() + { + return { Destruct, nullptr }; + } + + template + static constexpr + const FramePropertyDescriptor NewWithDestructorWithFrame() + { + return { nullptr, DestructWithFrame }; + } + + static constexpr const FramePropertyDescriptor NewWithoutDestructor() + { + return { nullptr, nullptr }; + } + +private: + constexpr FramePropertyDescriptor( + UntypedDestructor* aDtor, UntypedDestructorWithFrame* aDtorWithFrame) + : FramePropertyDescriptorUntyped(aDtor, aDtorWithFrame) + {} + + template + static void Destruct(void* aPropertyValue) + { + Dtor(static_cast(aPropertyValue)); + } + + template + static void DestructWithFrame(const nsIFrame* aFrame, void* aPropertyValue) + { + Dtor(aFrame, static_cast(aPropertyValue)); + } +}; + +// SmallValueHolder is a placeholder intended to be used as template +// argument of FramePropertyDescriptor for types which can fit into the +// size of a pointer directly. This class should never be defined, so +// that we won't use it for unexpected purpose by mistake. +template +class SmallValueHolder; + +namespace detail { + +template +struct FramePropertyTypeHelper +{ + typedef T* Type; +}; +template +struct FramePropertyTypeHelper> +{ + typedef T Type; +}; + +} + +/** + * The FrameProperties class 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 FrameProperties +{ +public: + template + using Descriptor = const FramePropertyDescriptor*; + using UntypedDescriptor = const FramePropertyDescriptorUntyped*; + + template + using PropertyType = typename detail::FramePropertyTypeHelper::Type; + + explicit FrameProperties() + { + } + + ~FrameProperties() + { + MOZ_ASSERT(mProperties.Length() == 0, "forgot to delete properties"); + } + + /** + * Set a property value. This requires a linear search through + * the properties of the frame. Any existing value for the property + * is destroyed. + */ + template + void Set(Descriptor aProperty, PropertyType aValue, + const nsIFrame* aFrame) + { + void* ptr = ReinterpretHelper::ToPointer(aValue); + SetInternal(aProperty, ptr, aFrame); + } + + /** + * @return true if @aProperty is set. This requires a linear search through the + * properties of the frame. + * + * In most cases, this shouldn't be used outside of assertions, because if + * you're doing a lookup anyway it would be far more efficient to call Get() + * or Remove() and check the aFoundResult outparam to find out whether the + * property is set. Legitimate non-assertion uses include: + * + * - Checking if a frame property is set in cases where that's all we want + * to know (i.e., we don't intend to read the actual value or remove the + * property). + * + * - Calling Has() before Set() in cases where we don't want to overwrite + * an existing value for the frame property. + */ + template + bool Has(Descriptor aProperty) const + { + return mProperties.IndexOf(aProperty, 0, PropertyComparator()) != nsTArray::NoIndex; + } + + /** + * Get a property value. This requires a linear search through + * lookup (using the frame as the key) and a linear search through + * the properties of the frame. If the frame has no such property, + * returns zero-filled result, which means null for pointers and + * zero for integers and floating point types. + * @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'. + */ + template + PropertyType Get(Descriptor aProperty, + bool* aFoundResult = nullptr) const + { + void* ptr = GetInternal(aProperty, aFoundResult); + return ReinterpretHelper::FromPointer(ptr); + } + /** + * Remove a property value. This requires a linear search through + * the properties of the frame. The old property value is returned + * (and not destroyed). If the frame has no such property, + * returns zero-filled result, which means null for pointers and + * zero for integers and floating point types. + * @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'. + */ + template + PropertyType Remove(Descriptor aProperty, + bool* aFoundResult = nullptr) + { + void* ptr = RemoveInternal(aProperty, aFoundResult); + return ReinterpretHelper::FromPointer(ptr); + } + /** + * Remove and destroy a property value. This requires a linear search + * through the properties of the frame. If the frame has no such + * property, nothing happens. + */ + template + void Delete(Descriptor aProperty, const nsIFrame* aFrame) + { + DeleteInternal(aProperty, aFrame); + } + /** + * Remove and destroy all property values for the frame. + */ + void DeleteAll(const nsIFrame* aFrame) { + mozilla::DebugOnly len = mProperties.Length(); + for (auto& prop : mProperties) { + prop.DestroyValueFor(aFrame); + MOZ_ASSERT(mProperties.Length() == len); + } + mProperties.Clear(); + } + + size_t SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) const { + // We currently report only the shallow size of the mProperties array. + // As for the PropertyValue entries: we don't need to measure the mProperty + // field of because it always points to static memory, and we can't measure + // mValue because the type is opaque. + // XXX Can we do better, e.g. with a method on the descriptor? + return mProperties.ShallowSizeOfExcludingThis(aMallocSizeOf); + } + +private: + friend class ::nsIFrame; + + // Prevent copying of FrameProperties; we should always return/pass around + // references to it, not copies! + FrameProperties(const FrameProperties&) = delete; + FrameProperties& operator=(const FrameProperties&) = delete; + + inline void + SetInternal(UntypedDescriptor aProperty, void* aValue, + const nsIFrame* aFrame); + + inline void* + GetInternal(UntypedDescriptor aProperty, bool* aFoundResult) const; + + inline void* + RemoveInternal(UntypedDescriptor aProperty, bool* aFoundResult); + + inline void + DeleteInternal(UntypedDescriptor aProperty, const nsIFrame* aFrame); + + template + struct ReinterpretHelper + { + static_assert(sizeof(PropertyType) <= sizeof(void*), + "size of the value must never be larger than a pointer"); + + static void* ToPointer(PropertyType aValue) + { + void* ptr = nullptr; + memcpy(&ptr, &aValue, sizeof(aValue)); + return ptr; + } + + static PropertyType FromPointer(void* aPtr) + { + PropertyType value; + memcpy(&value, &aPtr, sizeof(value)); + return value; + } + }; + + template + struct ReinterpretHelper + { + static void* ToPointer(T* aValue) + { + return static_cast(aValue); + } + + static T* FromPointer(void* aPtr) + { + return static_cast(aPtr); + } + }; + + /** + * Stores a property descriptor/value pair. + */ + struct PropertyValue { + PropertyValue() : mProperty(nullptr), mValue(nullptr) {} + PropertyValue(UntypedDescriptor aProperty, void* aValue) + : mProperty(aProperty), mValue(aValue) {} + + void DestroyValueFor(const nsIFrame* aFrame) { + if (mProperty->mDestructor) { + mProperty->mDestructor(mValue); + } else if (mProperty->mDestructorWithFrame) { + mProperty->mDestructorWithFrame(aFrame, mValue); + } + } + + UntypedDescriptor 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(UntypedDescriptor a, const PropertyValue& b) const { + return a == b.mProperty; + } + bool Equals(const PropertyValue& a, UntypedDescriptor b) const { + return a.mProperty == b; + } + }; + + nsTArray mProperties; +}; + +/** + * This class encapsulates the properties of a frame. + */ +inline void* +FrameProperties::GetInternal(UntypedDescriptor aProperty, + bool* aFoundResult) const +{ + MOZ_ASSERT(aProperty, "Null property?"); + + auto index = mProperties.IndexOf(aProperty, 0, PropertyComparator()); + if (index == nsTArray::NoIndex) { + if (aFoundResult) { + *aFoundResult = false; + } + return nullptr; + } + + if (aFoundResult) { + *aFoundResult = true; + } + +return mProperties.ElementAt(index).mValue; +} + +inline void +FrameProperties::SetInternal(UntypedDescriptor aProperty, void* aValue, + const nsIFrame* aFrame) +{ + MOZ_ASSERT(aProperty, "Null property?"); + + auto index = mProperties.IndexOf(aProperty, 0, PropertyComparator()); + if (index != nsTArray::NoIndex) { + PropertyValue* pv = &mProperties.ElementAt(index); + pv->DestroyValueFor(aFrame); + pv->mValue = aValue; + return; + } + + mProperties.AppendElement(PropertyValue(aProperty, aValue)); +} + +inline void* +FrameProperties::RemoveInternal(UntypedDescriptor aProperty, bool* aFoundResult) +{ + MOZ_ASSERT(aProperty, "Null property?"); + + auto index = mProperties.IndexOf(aProperty, 0, PropertyComparator()); + if (index == nsTArray::NoIndex) { + if (aFoundResult) { + *aFoundResult = false; + } + return nullptr; + } + +if (aFoundResult) { + *aFoundResult = true; +} + +void* result = mProperties.ElementAt(index).mValue; +mProperties.RemoveElementAt(index); + +return result; +} + +inline void +FrameProperties::DeleteInternal(UntypedDescriptor aProperty, + const nsIFrame* aFrame) +{ + MOZ_ASSERT(aProperty, "Null property?"); + + auto index = mProperties.IndexOf(aProperty, 0, PropertyComparator()); + if (index != nsTArray::NoIndex) { + mProperties.ElementAt(index).DestroyValueFor(aFrame); + mProperties.RemoveElementAt(index); + } +} + +} // namespace mozilla + +#endif /* FRAMEPROPERTIES_H_ */ -- cgit v1.2.3