tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

FrameProperties.h (15028B)


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