From 00812e30dfa70f9b1a752cf0d09de00f6d401c85 Mon Sep 17 00:00:00 2001
From: win7-7 <win7-7@users.noreply.github.com>
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/ActiveLayerTracker.cpp    |  15 +-
 layout/base/FrameLayerBuilder.cpp     |  32 ++-
 layout/base/FrameProperties.h         | 440 +++++++++++++++++++++++++++++++++
 layout/base/FramePropertyTable.cpp    | 239 ------------------
 layout/base/FramePropertyTable.h      | 442 ----------------------------------
 layout/base/OverflowChangedTracker.h  |   4 +-
 layout/base/RestyleManager.cpp        |  10 +-
 layout/base/RestyleManagerBase.cpp    |  88 ++++---
 layout/base/RestyleManagerBase.h      |  13 +-
 layout/base/moz.build                 |   3 +-
 layout/base/nsBidiPresUtils.cpp       |   5 +-
 layout/base/nsCSSFrameConstructor.cpp |  23 +-
 layout/base/nsCSSRendering.cpp        |  10 +-
 layout/base/nsDisplayList.cpp         |   8 +-
 layout/base/nsDisplayList.h           |   2 +-
 layout/base/nsIPresShell.h            |  11 +-
 layout/base/nsLayoutUtils.cpp         |  16 +-
 layout/base/nsPresContext.cpp         |   3 +-
 layout/base/nsPresContext.h           |  11 -
 layout/base/nsPresShell.cpp           |  35 ++-
 layout/base/nsPresShell.h             |  11 +-
 21 files changed, 590 insertions(+), 831 deletions(-)
 create mode 100644 layout/base/FrameProperties.h
 delete mode 100644 layout/base/FramePropertyTable.cpp
 delete mode 100644 layout/base/FramePropertyTable.h

(limited to 'layout/base')

diff --git a/layout/base/ActiveLayerTracker.cpp b/layout/base/ActiveLayerTracker.cpp
index 4f60f82d7..ecee4897a 100644
--- a/layout/base/ActiveLayerTracker.cpp
+++ b/layout/base/ActiveLayerTracker.cpp
@@ -178,7 +178,7 @@ LayerActivityTracker::NotifyExpired(LayerActivity* aObject)
       f->SchedulePaint();
     }
     f->RemoveStateBits(NS_FRAME_HAS_LAYER_ACTIVITY_PROPERTY);
-    f->Properties().Delete(LayerActivityProperty());
+    f->DeleteProperty(LayerActivityProperty());
   } else {
     c->DeleteProperty(nsGkAtoms::LayerActivity);
   }
@@ -190,15 +190,13 @@ GetLayerActivity(nsIFrame* aFrame)
   if (!aFrame->HasAnyStateBits(NS_FRAME_HAS_LAYER_ACTIVITY_PROPERTY)) {
     return nullptr;
   }
-  FrameProperties properties = aFrame->Properties();
-  return properties.Get(LayerActivityProperty());
+  return aFrame->GetProperty(LayerActivityProperty());
 }
 
 static LayerActivity*
 GetLayerActivityForUpdate(nsIFrame* aFrame)
 {
-  FrameProperties properties = aFrame->Properties();
-  LayerActivity* layerActivity = properties.Get(LayerActivityProperty());
+  LayerActivity* layerActivity = aFrame->GetProperty(LayerActivityProperty());
   if (layerActivity) {
     gLayerActivityTracker->MarkUsed(layerActivity);
   } else {
@@ -208,7 +206,7 @@ GetLayerActivityForUpdate(nsIFrame* aFrame)
     layerActivity = new LayerActivity(aFrame);
     gLayerActivityTracker->AddObject(layerActivity);
     aFrame->AddStateBits(NS_FRAME_HAS_LAYER_ACTIVITY_PROPERTY);
-    properties.Set(LayerActivityProperty(), layerActivity);
+    aFrame->SetProperty(LayerActivityProperty(), layerActivity);
   }
   return layerActivity;
 }
@@ -225,8 +223,7 @@ ActiveLayerTracker::TransferActivityToContent(nsIFrame* aFrame, nsIContent* aCon
   if (!aFrame->HasAnyStateBits(NS_FRAME_HAS_LAYER_ACTIVITY_PROPERTY)) {
     return;
   }
-  FrameProperties properties = aFrame->Properties();
-  LayerActivity* layerActivity = properties.Remove(LayerActivityProperty());
+  LayerActivity* layerActivity = aFrame->RemoveProperty(LayerActivityProperty());
   aFrame->RemoveStateBits(NS_FRAME_HAS_LAYER_ACTIVITY_PROPERTY);
   if (!layerActivity) {
     return;
@@ -248,7 +245,7 @@ ActiveLayerTracker::TransferActivityToFrame(nsIContent* aContent, nsIFrame* aFra
   layerActivity->mContent = nullptr;
   layerActivity->mFrame = aFrame;
   aFrame->AddStateBits(NS_FRAME_HAS_LAYER_ACTIVITY_PROPERTY);
-  aFrame->Properties().Set(LayerActivityProperty(), layerActivity);
+  aFrame->SetProperty(LayerActivityProperty(), layerActivity);
 }
 
 static void
diff --git a/layout/base/FrameLayerBuilder.cpp b/layout/base/FrameLayerBuilder.cpp
index 9aaa28fb5..e87d9dc09 100644
--- a/layout/base/FrameLayerBuilder.cpp
+++ b/layout/base/FrameLayerBuilder.cpp
@@ -165,10 +165,10 @@ FrameLayerBuilder::DisplayItemData::AddFrame(nsIFrame* aFrame)
   mFrameList.AppendElement(aFrame);
 
   nsTArray<DisplayItemData*>* array =
-    aFrame->Properties().Get(FrameLayerBuilder::LayerManagerDataProperty());
+    aFrame->GetProperty(FrameLayerBuilder::LayerManagerDataProperty());
   if (!array) {
     array = new nsTArray<DisplayItemData*>();
-    aFrame->Properties().Set(FrameLayerBuilder::LayerManagerDataProperty(), array);
+    aFrame->SetProperty(FrameLayerBuilder::LayerManagerDataProperty(), array);
   }
   array->AppendElement(this);
 }
@@ -181,7 +181,7 @@ FrameLayerBuilder::DisplayItemData::RemoveFrame(nsIFrame* aFrame)
   MOZ_RELEASE_ASSERT(result, "Can't remove a frame that wasn't added!");
 
   nsTArray<DisplayItemData*>* array =
-    aFrame->Properties().Get(FrameLayerBuilder::LayerManagerDataProperty());
+    aFrame->GetProperty(FrameLayerBuilder::LayerManagerDataProperty());
   MOZ_RELEASE_ASSERT(array, "Must be already stored on the frame!");
   array->RemoveElement(this);
 }
@@ -268,7 +268,7 @@ FrameLayerBuilder::DisplayItemData::~DisplayItemData()
       continue;
     }
     nsTArray<DisplayItemData*> *array =
-      reinterpret_cast<nsTArray<DisplayItemData*>*>(frame->Properties().Get(LayerManagerDataProperty()));
+      reinterpret_cast<nsTArray<DisplayItemData*>*>(frame->GetProperty(LayerManagerDataProperty()));
     array->RemoveElement(this);
   }
 
@@ -390,8 +390,7 @@ public:
 /* static */ void
 FrameLayerBuilder::DestroyDisplayItemDataFor(nsIFrame* aFrame)
 {
-  FrameProperties props = aFrame->Properties();
-  props.Delete(LayerManagerDataProperty());
+  aFrame->DeleteProperty(LayerManagerDataProperty());
 }
 
 struct AssignedDisplayItem
@@ -1823,7 +1822,7 @@ FrameLayerBuilder::DisplayItemData*
 FrameLayerBuilder::GetDisplayItemData(nsIFrame* aFrame, uint32_t aKey)
 {
   const nsTArray<DisplayItemData*>* array =
-    aFrame->Properties().Get(LayerManagerDataProperty());
+    aFrame->GetProperty(LayerManagerDataProperty());
   if (array) {
     for (uint32_t i = 0; i < array->Length(); i++) {
       DisplayItemData* item = AssertDisplayItemData(array->ElementAt(i));
@@ -2052,7 +2051,7 @@ FrameLayerBuilder::GetDisplayItemDataForManager(nsDisplayItem* aItem,
                                                 LayerManager* aManager)
 {
   const nsTArray<DisplayItemData*>* array =
-    aItem->Frame()->Properties().Get(LayerManagerDataProperty());
+    aItem->Frame()->GetProperty(LayerManagerDataProperty());
   if (array) {
     for (uint32_t i = 0; i < array->Length(); i++) {
       DisplayItemData* item = AssertDisplayItemData(array->ElementAt(i));
@@ -2069,7 +2068,7 @@ bool
 FrameLayerBuilder::HasRetainedDataFor(nsIFrame* aFrame, uint32_t aDisplayItemKey)
 {
   const nsTArray<DisplayItemData*>* array =
-    aFrame->Properties().Get(LayerManagerDataProperty());
+    aFrame->GetProperty(LayerManagerDataProperty());
   if (array) {
     for (uint32_t i = 0; i < array->Length(); i++) {
       if (AssertDisplayItemData(array->ElementAt(i))->mDisplayItemKey == aDisplayItemKey) {
@@ -2084,7 +2083,7 @@ void
 FrameLayerBuilder::IterateRetainedDataFor(nsIFrame* aFrame, DisplayItemDataCallback aCallback)
 {
   const nsTArray<DisplayItemData*>* array =
-    aFrame->Properties().Get(LayerManagerDataProperty());
+    aFrame->GetProperty(LayerManagerDataProperty());
   if (!array) {
     return;
   }
@@ -2151,7 +2150,7 @@ FrameLayerBuilder::ClearCachedGeometry(nsDisplayItem* aItem)
 FrameLayerBuilder::GetDebugOldLayerFor(nsIFrame* aFrame, uint32_t aDisplayItemKey)
 {
   const nsTArray<DisplayItemData*>* array =
-    aFrame->Properties().Get(LayerManagerDataProperty());
+    aFrame->GetProperty(LayerManagerDataProperty());
 
   if (!array) {
     return nullptr;
@@ -2171,7 +2170,7 @@ FrameLayerBuilder::GetDebugOldLayerFor(nsIFrame* aFrame, uint32_t aDisplayItemKe
 FrameLayerBuilder::GetDebugSingleOldPaintedLayerForFrame(nsIFrame* aFrame)
 {
   const nsTArray<DisplayItemData*>* array =
-    aFrame->Properties().Get(LayerManagerDataProperty());
+    aFrame->GetProperty(LayerManagerDataProperty());
 
   if (!array) {
     return nullptr;
@@ -5656,7 +5655,7 @@ FrameLayerBuilder::InvalidateAllLayers(LayerManager* aManager)
 FrameLayerBuilder::InvalidateAllLayersForFrame(nsIFrame *aFrame)
 {
   const nsTArray<DisplayItemData*>* array =
-    aFrame->Properties().Get(LayerManagerDataProperty());
+    aFrame->GetProperty(LayerManagerDataProperty());
   if (array) {
     for (uint32_t i = 0; i < array->Length(); i++) {
       AssertDisplayItemData(array->ElementAt(i))->mParent->mInvalidateAllLayers = true;
@@ -5673,7 +5672,7 @@ FrameLayerBuilder::GetDedicatedLayer(nsIFrame* aFrame, uint32_t aDisplayItemKey)
   // in the secondary manager
 
   const nsTArray<DisplayItemData*>* array =
-    aFrame->Properties().Get(LayerManagerDataProperty());
+    aFrame->GetProperty(LayerManagerDataProperty());
   if (array) {
     for (uint32_t i = 0; i < array->Length(); i++) {
       DisplayItemData *element = AssertDisplayItemData(array->ElementAt(i));
@@ -5729,7 +5728,7 @@ FrameLayerBuilder::GetPaintedLayerScaleForFrame(nsIFrame* aFrame)
     }
 
     const nsTArray<DisplayItemData*>* array =
-      f->Properties().Get(LayerManagerDataProperty());
+      f->GetProperty(LayerManagerDataProperty());
     if (!array) {
       continue;
     }
@@ -6165,9 +6164,8 @@ FrameLayerBuilder::GetMostRecentGeometry(nsDisplayItem* aItem)
   typedef nsTArray<DisplayItemData*> DataArray;
 
   // Retrieve the array of DisplayItemData associated with our frame.
-  FrameProperties properties = aItem->Frame()->Properties();
   const DataArray* dataArray =
-    properties.Get(LayerManagerDataProperty());
+    aItem->Frame()->GetProperty(LayerManagerDataProperty());
   if (!dataArray) {
     return nullptr;
   }
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<typename T>
+struct FramePropertyDescriptor : public FramePropertyDescriptorUntyped
+{
+  typedef void Destructor(T* aPropertyValue);
+  typedef void DestructorWithFrame(const nsIFrame* aaFrame,
+                                   T* aPropertyValue);
+
+  template<Destructor Dtor>
+  static constexpr const FramePropertyDescriptor<T> NewWithDestructor()
+  {
+    return { Destruct<Dtor>, nullptr };
+  }
+
+  template<DestructorWithFrame Dtor>
+  static constexpr
+  const FramePropertyDescriptor<T> NewWithDestructorWithFrame()
+  {
+    return { nullptr, DestructWithFrame<Dtor> };
+  }
+
+  static constexpr const FramePropertyDescriptor<T> NewWithoutDestructor()
+  {
+    return { nullptr, nullptr };
+  }
+
+private:
+  constexpr FramePropertyDescriptor(
+    UntypedDestructor* aDtor, UntypedDestructorWithFrame* aDtorWithFrame)
+    : FramePropertyDescriptorUntyped(aDtor, aDtorWithFrame)
+  {}
+
+  template<Destructor Dtor>
+  static void Destruct(void* aPropertyValue)
+  {
+    Dtor(static_cast<T*>(aPropertyValue));
+  }
+
+  template<DestructorWithFrame Dtor>
+  static void DestructWithFrame(const nsIFrame* aFrame, void* aPropertyValue)
+  {
+    Dtor(aFrame, static_cast<T*>(aPropertyValue));
+  }
+};
+
+// SmallValueHolder<T> 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<typename T>
+class SmallValueHolder;
+
+namespace detail {
+
+template<typename T>
+struct FramePropertyTypeHelper
+{
+  typedef T* Type;
+};
+template<typename T>
+struct FramePropertyTypeHelper<SmallValueHolder<T>>
+{
+  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<typename T>
+  using Descriptor = const FramePropertyDescriptor<T>*;
+  using UntypedDescriptor = const FramePropertyDescriptorUntyped*;
+
+  template<typename T>
+  using PropertyType = typename detail::FramePropertyTypeHelper<T>::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<typename T>
+  void Set(Descriptor<T> aProperty, PropertyType<T> aValue,
+           const nsIFrame* aFrame)
+  {
+    void* ptr = ReinterpretHelper<T>::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<typename T>
+  bool Has(Descriptor<T> aProperty) const
+  {
+  return mProperties.IndexOf(aProperty, 0, PropertyComparator()) != nsTArray<PropertyValue>::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<typename T>
+  PropertyType<T> Get(Descriptor<T> aProperty,
+                      bool* aFoundResult = nullptr) const
+  {
+    void* ptr = GetInternal(aProperty, aFoundResult);
+    return ReinterpretHelper<T>::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<typename T>
+  PropertyType<T> Remove(Descriptor<T> aProperty,
+                         bool* aFoundResult = nullptr)
+  {
+    void* ptr = RemoveInternal(aProperty, aFoundResult);
+    return ReinterpretHelper<T>::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<typename T>
+  void Delete(Descriptor<T> aProperty, const nsIFrame* aFrame)
+  {
+    DeleteInternal(aProperty, aFrame);
+  }
+  /**
+   * Remove and destroy all property values for the frame.
+   */
+  void DeleteAll(const nsIFrame* aFrame) {
+    mozilla::DebugOnly<size_t> 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<typename T>
+  struct ReinterpretHelper
+  {
+    static_assert(sizeof(PropertyType<T>) <= sizeof(void*),
+                  "size of the value must never be larger than a pointer");
+
+    static void* ToPointer(PropertyType<T> aValue)
+    {
+      void* ptr = nullptr;
+      memcpy(&ptr, &aValue, sizeof(aValue));
+      return ptr;
+    }
+
+    static PropertyType<T> FromPointer(void* aPtr)
+    {
+      PropertyType<T> value;
+      memcpy(&value, &aPtr, sizeof(value));
+      return value;
+    }
+  };
+
+  template<typename T>
+  struct ReinterpretHelper<T*>
+  {
+    static void* ToPointer(T* aValue)
+    {
+      return static_cast<void*>(aValue);
+    }
+
+    static T* FromPointer(void* aPtr)
+    {
+      return static_cast<T*>(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<PropertyValue> 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<PropertyValue>::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<PropertyValue>::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<PropertyValue>::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<PropertyValue>::NoIndex) {
+    mProperties.ElementAt(index).DestroyValueFor(aFrame);
+    mProperties.RemoveElementAt(index);
+  }
+}
+
+} // namespace mozilla
+
+#endif /* FRAMEPROPERTIES_H_ */
diff --git a/layout/base/FramePropertyTable.cpp b/layout/base/FramePropertyTable.cpp
deleted file mode 100644
index 0fd9b1c37..000000000
--- a/layout/base/FramePropertyTable.cpp
+++ /dev/null
@@ -1,239 +0,0 @@
-/* -*- 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/. */
-
-#include "FramePropertyTable.h"
-
-#include "mozilla/MemoryReporting.h"
-
-namespace mozilla {
-
-void
-FramePropertyTable::SetInternal(
-  const nsIFrame* aFrame, UntypedDescriptor aProperty, void* aValue)
-{
-  NS_ASSERTION(aFrame, "Null frame?");
-  NS_ASSERTION(aProperty, "Null property?");
-
-  if (mLastFrame != aFrame || !mLastEntry) {
-    mLastFrame = aFrame;
-    mLastEntry = mEntries.PutEntry(aFrame);
-  }
-  Entry* entry = mLastEntry;
-
-  if (!entry->mProp.IsArray()) {
-    if (!entry->mProp.mProperty) {
-      // Empty entry, so we can just store our property in the empty slot
-      entry->mProp.mProperty = aProperty;
-      entry->mProp.mValue = aValue;
-      return;
-    }
-    if (entry->mProp.mProperty == aProperty) {
-      // Just overwrite the current value
-      entry->mProp.DestroyValueFor(aFrame);
-      entry->mProp.mValue = aValue;
-      return;
-    }
-
-    // We need to expand the single current entry to an array
-    PropertyValue current = entry->mProp;
-    entry->mProp.mProperty = nullptr;
-    static_assert(sizeof(nsTArray<PropertyValue>) <= sizeof(void *),
-                  "Property array must fit entirely within entry->mProp.mValue");
-    new (&entry->mProp.mValue) nsTArray<PropertyValue>(4);
-    entry->mProp.ToArray()->AppendElement(current);
-  }
-
-  nsTArray<PropertyValue>* array = entry->mProp.ToArray();
-  nsTArray<PropertyValue>::index_type index =
-    array->IndexOf(aProperty, 0, PropertyComparator());
-  if (index != nsTArray<PropertyValue>::NoIndex) {
-    PropertyValue* pv = &array->ElementAt(index);
-    pv->DestroyValueFor(aFrame);
-    pv->mValue = aValue;
-    return;
-  }
-
-  array->AppendElement(PropertyValue(aProperty, aValue));
-}
-
-void*
-FramePropertyTable::GetInternal(
-  const nsIFrame* aFrame, UntypedDescriptor aProperty, bool* aFoundResult)
-{
-  NS_ASSERTION(aFrame, "Null frame?");
-  NS_ASSERTION(aProperty, "Null property?");
-
-  if (aFoundResult) {
-    *aFoundResult = false;
-  }
-
-  if (mLastFrame != aFrame) {
-    mLastFrame = aFrame;
-    mLastEntry = mEntries.GetEntry(mLastFrame);
-  }
-  Entry* entry = mLastEntry;
-  if (!entry)
-    return nullptr;
-
-  if (entry->mProp.mProperty == aProperty) {
-    if (aFoundResult) {
-      *aFoundResult = true;
-    }
-    return entry->mProp.mValue;
-  }
-  if (!entry->mProp.IsArray()) {
-    // There's just one property and it's not the one we want, bail
-    return nullptr;
-  }
-
-  nsTArray<PropertyValue>* array = entry->mProp.ToArray();
-  nsTArray<PropertyValue>::index_type index =
-    array->IndexOf(aProperty, 0, PropertyComparator());
-  if (index == nsTArray<PropertyValue>::NoIndex)
-    return nullptr;
-
-  if (aFoundResult) {
-    *aFoundResult = true;
-  }
-
-  return array->ElementAt(index).mValue;
-}
-
-void*
-FramePropertyTable::RemoveInternal(
-  const nsIFrame* aFrame, UntypedDescriptor aProperty, bool* aFoundResult)
-{
-  NS_ASSERTION(aFrame, "Null frame?");
-  NS_ASSERTION(aProperty, "Null property?");
-
-  if (aFoundResult) {
-    *aFoundResult = false;
-  }
-
-  if (mLastFrame != aFrame) {
-    mLastFrame = aFrame;
-    mLastEntry = mEntries.GetEntry(aFrame);
-  }
-  Entry* entry = mLastEntry;
-  if (!entry)
-    return nullptr;
-
-  if (entry->mProp.mProperty == aProperty) {
-    // There's only one entry and it's the one we want
-    void* value = entry->mProp.mValue;
-
-    // Here it's ok to use RemoveEntry() -- which may resize mEntries --
-    // because we null mLastEntry at the same time.
-    mEntries.RemoveEntry(entry);
-    mLastEntry = nullptr;
-    if (aFoundResult) {
-      *aFoundResult = true;
-    }
-    return value;
-  }
-  if (!entry->mProp.IsArray()) {
-    // There's just one property and it's not the one we want, bail
-    return nullptr;
-  }
-
-  nsTArray<PropertyValue>* array = entry->mProp.ToArray();
-  nsTArray<PropertyValue>::index_type index =
-    array->IndexOf(aProperty, 0, PropertyComparator());
-  if (index == nsTArray<PropertyValue>::NoIndex) {
-    // No such property, bail
-    return nullptr;
-  }
-
-  if (aFoundResult) {
-    *aFoundResult = true;
-  }
-
-  void* result = array->ElementAt(index).mValue;
-
-  uint32_t last = array->Length() - 1;
-  array->ElementAt(index) = array->ElementAt(last);
-  array->RemoveElementAt(last);
-
-  if (last == 1) {
-    PropertyValue pv = array->ElementAt(0);
-    array->~nsTArray<PropertyValue>();
-    entry->mProp = pv;
-  }
-  
-  return result;
-}
-
-void
-FramePropertyTable::DeleteInternal(
-  const nsIFrame* aFrame, UntypedDescriptor aProperty)
-{
-  NS_ASSERTION(aFrame, "Null frame?");
-  NS_ASSERTION(aProperty, "Null property?");
-
-  bool found;
-  void* v = RemoveInternal(aFrame, aProperty, &found);
-  if (found) {
-    PropertyValue pv(aProperty, v);
-    pv.DestroyValueFor(aFrame);
-  }
-}
-
-/* static */ void
-FramePropertyTable::DeleteAllForEntry(Entry* aEntry)
-{
-  if (!aEntry->mProp.IsArray()) {
-    aEntry->mProp.DestroyValueFor(aEntry->GetKey());
-    return;
-  }
-
-  nsTArray<PropertyValue>* array = aEntry->mProp.ToArray();
-  for (uint32_t i = 0; i < array->Length(); ++i) {
-    array->ElementAt(i).DestroyValueFor(aEntry->GetKey());
-  }
-  array->~nsTArray<PropertyValue>();
-}
-
-void
-FramePropertyTable::DeleteAllFor(const nsIFrame* aFrame)
-{
-  NS_ASSERTION(aFrame, "Null frame?");
-
-  Entry* entry = mEntries.GetEntry(aFrame);
-  if (!entry)
-    return;
-
-  if (mLastFrame == aFrame) {
-    // Flush cache. We assume DeleteAllForEntry will be called before
-    // a frame is destroyed.
-    mLastFrame = nullptr;
-    mLastEntry = nullptr;
-  }
-
-  DeleteAllForEntry(entry);
-
-  // mLastEntry points into mEntries, so we use RawRemoveEntry() which will not
-  // resize mEntries.
-  mEntries.RawRemoveEntry(entry);
-}
-
-void
-FramePropertyTable::DeleteAll()
-{
-  mLastFrame = nullptr;
-  mLastEntry = nullptr;
-
-  for (auto iter = mEntries.Iter(); !iter.Done(); iter.Next()) {
-    DeleteAllForEntry(iter.Get());
-  }
-  mEntries.Clear();
-}
-
-size_t
-FramePropertyTable::SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) const
-{
-  return mEntries.SizeOfExcludingThis(aMallocSizeOf);
-}
-
-} // namespace mozilla
diff --git a/layout/base/FramePropertyTable.h b/layout/base/FramePropertyTable.h
deleted file mode 100644
index e9847efbf..000000000
--- a/layout/base/FramePropertyTable.h
+++ /dev/null
@@ -1,442 +0,0 @@
-/* -*- 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 "mozilla/TypeTraits.h"
-#include "mozilla/Unused.h"
-#include "nsTArray.h"
-#include "nsTHashtable.h"
-#include "nsHashKeys.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<typename T>
-struct FramePropertyDescriptor : public FramePropertyDescriptorUntyped
-{
-  typedef void Destructor(T* aPropertyValue);
-  typedef void DestructorWithFrame(const nsIFrame* aaFrame,
-                                   T* aPropertyValue);
-
-  template<Destructor Dtor>
-  static constexpr const FramePropertyDescriptor<T> NewWithDestructor()
-  {
-    return { Destruct<Dtor>, nullptr };
-  }
-
-  template<DestructorWithFrame Dtor>
-  static constexpr
-  const FramePropertyDescriptor<T> NewWithDestructorWithFrame()
-  {
-    return { nullptr, DestructWithFrame<Dtor> };
-  }
-
-  static constexpr const FramePropertyDescriptor<T> NewWithoutDestructor()
-  {
-    return { nullptr, nullptr };
-  }
-
-private:
-  constexpr FramePropertyDescriptor(
-    UntypedDestructor* aDtor, UntypedDestructorWithFrame* aDtorWithFrame)
-    : FramePropertyDescriptorUntyped(aDtor, aDtorWithFrame)
-  {}
-
-  template<Destructor Dtor>
-  static void Destruct(void* aPropertyValue)
-  {
-    Dtor(static_cast<T*>(aPropertyValue));
-  }
-
-  template<DestructorWithFrame Dtor>
-  static void DestructWithFrame(const nsIFrame* aFrame, void* aPropertyValue)
-  {
-    Dtor(aFrame, static_cast<T*>(aPropertyValue));
-  }
-};
-
-// SmallValueHolder<T> 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<typename T>
-class SmallValueHolder;
-
-namespace detail {
-
-template<typename T>
-struct FramePropertyTypeHelper
-{
-  typedef T* Type;
-};
-template<typename T>
-struct FramePropertyTypeHelper<SmallValueHolder<T>>
-{
-  typedef T Type;
-};
-
-}
-
-/**
- * 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:
-  template<typename T>
-  using Descriptor = const FramePropertyDescriptor<T>*;
-  using UntypedDescriptor = const FramePropertyDescriptorUntyped*;
-
-  template<typename T>
-  using PropertyType = typename detail::FramePropertyTypeHelper<T>::Type;
-
-  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.
-   */
-  template<typename T>
-  void Set(const nsIFrame* aFrame, Descriptor<T> aProperty,
-           PropertyType<T> aValue)
-  {
-    void* ptr = ReinterpretHelper<T>::ToPointer(aValue);
-    SetInternal(aFrame, aProperty, ptr);
-  }
-
-  /**
-   * @return true if @aProperty is set for @aFrame. This requires one hashtable
-   * lookup (using the frame as the key) and a linear search through the
-   * properties of that 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<typename T>
-  bool Has(const nsIFrame* aFrame, Descriptor<T> aProperty)
-  {
-    bool foundResult = false;
-    mozilla::Unused << GetInternal(aFrame, aProperty, &foundResult);
-    return foundResult;
-  }
-
-  /**
-   * 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 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<typename T>
-  PropertyType<T> Get(const nsIFrame* aFrame, Descriptor<T> aProperty,
-                      bool* aFoundResult = nullptr)
-  {
-    void* ptr = GetInternal(aFrame, aProperty, aFoundResult);
-    return ReinterpretHelper<T>::FromPointer(ptr);
-  }
-  /**
-   * 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 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<typename T>
-  PropertyType<T> Remove(const nsIFrame* aFrame, Descriptor<T> aProperty,
-                         bool* aFoundResult = nullptr)
-  {
-    void* ptr = RemoveInternal(aFrame, aProperty, aFoundResult);
-    return ReinterpretHelper<T>::FromPointer(ptr);
-  }
-  /**
-   * 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.
-   */
-  template<typename T>
-  void Delete(const nsIFrame* aFrame, Descriptor<T> aProperty)
-  {
-    DeleteInternal(aFrame, aProperty);
-  }
-  /**
-   * Remove and destroy all property values for a frame. This requires one
-   * hashtable lookup (using the frame as the key).
-   */
-  void DeleteAllFor(const nsIFrame* aFrame);
-  /**
-   * Remove and destroy all property values for all frames.
-   */
-  void DeleteAll();
-
-  size_t SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) const;
-
-protected:
-  void SetInternal(const nsIFrame* aFrame, UntypedDescriptor aProperty,
-                   void* aValue);
-
-  void* GetInternal(const nsIFrame* aFrame, UntypedDescriptor aProperty,
-                    bool* aFoundResult);
-
-  void* RemoveInternal(const nsIFrame* aFrame, UntypedDescriptor aProperty,
-                       bool* aFoundResult);
-
-  void DeleteInternal(const nsIFrame* aFrame, UntypedDescriptor aProperty);
-
-  template<typename T>
-  struct ReinterpretHelper
-  {
-    static_assert(sizeof(PropertyType<T>) <= sizeof(void*),
-                  "size of the value must never be larger than a pointer");
-
-    static void* ToPointer(PropertyType<T> aValue)
-    {
-      void* ptr = nullptr;
-      memcpy(&ptr, &aValue, sizeof(aValue));
-      return ptr;
-    }
-
-    static PropertyType<T> FromPointer(void* aPtr)
-    {
-      PropertyType<T> value;
-      memcpy(&value, &aPtr, sizeof(value));
-      return value;
-    }
-  };
-
-  template<typename T>
-  struct ReinterpretHelper<T*>
-  {
-    static void* ToPointer(T* aValue)
-    {
-      return static_cast<void*>(aValue);
-    }
-
-    static T* FromPointer(void* aPtr)
-    {
-      return static_cast<T*>(aPtr);
-    }
-  };
-
-  /**
-   * 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(UntypedDescriptor aProperty, void* aValue)
-      : mProperty(aProperty), mValue(aValue) {}
-
-    bool IsArray() { return !mProperty && mValue; }
-    nsTArray<PropertyValue>* ToArray()
-    {
-      NS_ASSERTION(IsArray(), "Must be array");
-      return reinterpret_cast<nsTArray<PropertyValue>*>(&mValue);
-    }
-
-    void DestroyValueFor(const 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<PropertyValue>* array = ToArray();
-        n += array->ShallowSizeOfExcludingThis(aMallocSizeOf);
-      }
-      return n;
-    }
-
-    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;
-    }
-  };
-
-  /**
-   * Our hashtable entry. The key is an nsIFrame*, the value is a
-   * PropertyValue representing one or more property/value pairs.
-   */
-  class Entry : public nsPtrHashKey<const nsIFrame>
-  {
-  public:
-    explicit Entry(KeyTypePointer aKey) : nsPtrHashKey<const nsIFrame>(aKey) {}
-    Entry(const Entry &toCopy) :
-      nsPtrHashKey<const nsIFrame>(toCopy), mProp(toCopy.mProp) {}
-
-    size_t SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) {
-      return mProp.SizeOfExcludingThis(aMallocSizeOf);
-    }
-
-    PropertyValue mProp;
-  };
-
-  static void DeleteAllForEntry(Entry* aEntry);
-
-  // Note that mLastEntry points into mEntries, so we need to be careful about
-  // not triggering a resize of mEntries, e.g. use RawRemoveEntry() instead of
-  // RemoveEntry() in some places.
-  nsTHashtable<Entry> mEntries;
-  const nsIFrame* mLastFrame;
-  Entry* mLastEntry;
-};
-
-/**
- * This class encapsulates the properties of a frame.
- */
-class FrameProperties {
-public:
-  template<typename T> using Descriptor = FramePropertyTable::Descriptor<T>;
-  template<typename T> using PropertyType = FramePropertyTable::PropertyType<T>;
-
-  FrameProperties(FramePropertyTable* aTable, const nsIFrame* aFrame)
-    : mTable(aTable), mFrame(aFrame) {}
-
-  template<typename T>
-  void Set(Descriptor<T> aProperty, PropertyType<T> aValue) const
-  {
-    mTable->Set(mFrame, aProperty, aValue);
-  }
-
-  template<typename T>
-  bool Has(Descriptor<T> aProperty) const
-  {
-    return mTable->Has(mFrame, aProperty);
-  }
-
-  template<typename T>
-  PropertyType<T> Get(Descriptor<T> aProperty,
-                      bool* aFoundResult = nullptr) const
-  {
-    return mTable->Get(mFrame, aProperty, aFoundResult);
-  }
-  template<typename T>
-  PropertyType<T> Remove(Descriptor<T> aProperty,
-                         bool* aFoundResult = nullptr) const
-  {
-    return mTable->Remove(mFrame, aProperty, aFoundResult);
-  }
-  template<typename T>
-  void Delete(Descriptor<T> aProperty)
-  {
-    mTable->Delete(mFrame, aProperty);
-  }
-
-private:
-  FramePropertyTable* mTable;
-  const nsIFrame* mFrame;
-};
-
-} // namespace mozilla
-
-#endif /* FRAMEPROPERTYTABLE_H_ */
diff --git a/layout/base/OverflowChangedTracker.h b/layout/base/OverflowChangedTracker.h
index a18d64b46..40145c65c 100644
--- a/layout/base/OverflowChangedTracker.h
+++ b/layout/base/OverflowChangedTracker.h
@@ -112,12 +112,12 @@ public:
         // Take a faster path that doesn't require unioning the overflow areas
         // of our children.
 
-        NS_ASSERTION(frame->Properties().Get(
+        NS_ASSERTION(frame->GetProperty(
                        nsIFrame::DebugInitialOverflowPropertyApplied()),
                      "InitialOverflowProperty must be set first.");
 
         nsOverflowAreas* overflow =
-          frame->Properties().Get(nsIFrame::InitialOverflowProperty());
+          frame->GetProperty(nsIFrame::InitialOverflowProperty());
         if (overflow) {
           // FinishAndStoreOverflow will change the overflow areas passed in,
           // so make a copy.
diff --git a/layout/base/RestyleManager.cpp b/layout/base/RestyleManager.cpp
index de8f10224..124b5535e 100644
--- a/layout/base/RestyleManager.cpp
+++ b/layout/base/RestyleManager.cpp
@@ -1122,10 +1122,10 @@ GetPrevContinuationWithPossiblySameStyle(nsIFrame* aFrame)
     // We're the first continuation, so we can just get the frame
     // property directly
     prevContinuation =
-      aFrame->Properties().Get(nsIFrame::IBSplitPrevSibling());
+      aFrame->GetProperty(nsIFrame::IBSplitPrevSibling());
     if (prevContinuation) {
       prevContinuation =
-        prevContinuation->Properties().Get(nsIFrame::IBSplitPrevSibling());
+        prevContinuation->GetProperty(nsIFrame::IBSplitPrevSibling());
     }
   }
 
@@ -1313,8 +1313,7 @@ RestyleManager::ReparentStyleContext(nsIFrame* aFrame)
       // oldContext)" check will prevent us from redoing work.
       if ((aFrame->GetStateBits() & NS_FRAME_PART_OF_IBSPLIT) &&
           !aFrame->GetPrevContinuation()) {
-        nsIFrame* sib =
-          aFrame->Properties().Get(nsIFrame::IBSplitSibling());
+        nsIFrame* sib = aFrame->GetProperty(nsIFrame::IBSplitSibling());
         if (sib) {
           ReparentStyleContext(sib);
         }
@@ -3349,7 +3348,6 @@ ElementRestyler::ComputeStyleChangeFor(nsIFrame*          aFrame,
   // line), we might restyle more than that.
 
   nsPresContext* presContext = aFrame->PresContext();
-  FramePropertyTable* propTable = presContext->PropertyTable();
 
   TreeMatchContext treeMatchContext(true,
                                     nsRuleWalker::eRelevantLinkUnvisited,
@@ -3363,7 +3361,7 @@ ElementRestyler::ComputeStyleChangeFor(nsIFrame*          aFrame,
   nsTArray<nsIContent*> visibleKidsOfHiddenElement;
   nsIFrame* nextIBSibling;
   for (nsIFrame* ibSibling = aFrame; ibSibling; ibSibling = nextIBSibling) {
-    nextIBSibling = RestyleManager::GetNextBlockInInlineSibling(propTable, ibSibling);
+    nextIBSibling = RestyleManager::GetNextBlockInInlineSibling(ibSibling);
 
     if (nextIBSibling) {
       // Don't allow some ib-split siblings to be processed with
diff --git a/layout/base/RestyleManagerBase.cpp b/layout/base/RestyleManagerBase.cpp
index d96d9dbbb..6770f9464 100644
--- a/layout/base/RestyleManagerBase.cpp
+++ b/layout/base/RestyleManagerBase.cpp
@@ -385,8 +385,6 @@ RestyleManagerBase::DebugVerifyStyleTree(nsIFrame* aFrame)
 
 #endif // DEBUG
 
-NS_DECLARE_FRAME_PROPERTY_SMALL_VALUE(ChangeListProperty, bool)
-
 /**
  * Sync views on aFrame and all of aFrame's descendants (following placeholders),
  * if aChange has nsChangeHint_SyncFrameView.
@@ -521,10 +519,9 @@ RecomputePosition(nsIFrame* aFrame)
         // normal position, go ahead and add the offsets directly.
         // First, we need to ensure that the normal position is stored though.
         nsPoint normalPosition = cont->GetNormalPosition();
-        auto props = cont->Properties();
-        const auto& prop = nsIFrame::NormalPositionProperty();
-        if (!props.Get(prop)) {
-          props.Set(prop, new nsPoint(normalPosition));
+        if (!cont->GetProperty(nsIFrame::NormalPositionProperty())) {
+          cont->SetProperty(nsIFrame::NormalPositionProperty(),
+                            new nsPoint(normalPosition));
         }
         cont->SetPosition(normalPosition +
                           nsPoint(newOffsets.left, newOffsets.top));
@@ -739,8 +736,7 @@ RestyleManagerBase::GetNearestAncestorFrame(nsIContent* aContent)
 }
 
 /* static */ nsIFrame*
-RestyleManagerBase::GetNextBlockInInlineSibling(FramePropertyTable* aPropTable,
-                                                nsIFrame* aFrame)
+RestyleManagerBase::GetNextBlockInInlineSibling(nsIFrame* aFrame)
 {
   NS_ASSERTION(!aFrame->GetPrevContinuation(),
                "must start with the first continuation");
@@ -750,8 +746,7 @@ RestyleManagerBase::GetNextBlockInInlineSibling(FramePropertyTable* aPropTable,
     return nullptr;
   }
 
-  return static_cast<nsIFrame*>
-    (aPropTable->Get(aFrame, nsIFrame::IBSplitSibling()));
+  return aFrame->GetProperty(nsIFrame::IBSplitSibling());
 }
 
 static void
@@ -1028,10 +1023,10 @@ RestyleManagerBase::GetNextContinuationWithSameStyle(
     // We're the last continuation, so we have to hop back to the first
     // before getting the frame property
     nextContinuation =
-      aFrame->FirstContinuation()->Properties().Get(nsIFrame::IBSplitSibling());
+      aFrame->FirstContinuation()->GetProperty(nsIFrame::IBSplitSibling());
     if (nextContinuation) {
       nextContinuation =
-        nextContinuation->Properties().Get(nsIFrame::IBSplitSibling());
+        nextContinuation->GetProperty(nsIFrame::IBSplitSibling());
     }
   }
 
@@ -1060,14 +1055,52 @@ RestyleManagerBase::ProcessRestyledFrames(nsStyleChangeList& aChangeList)
 {
   NS_ASSERTION(!nsContentUtils::IsSafeToRunScript(),
                "Someone forgot a script blocker");
-  if (aChangeList.IsEmpty())
-    return NS_OK;
+
+// See bug 1378219 comment 9:
+// Recursive calls here are a bit worrying, but apparently do happen in the
+// wild (although not currently in any of our automated tests). Try to get a
+// stack from Nightly/Dev channel to figure out what's going on and whether
+// it's OK.
+MOZ_DIAGNOSTIC_ASSERT(!mDestroyedFrames, "ProcessRestyledFrames recursion");
+
+if (aChangeList.IsEmpty())
+  return NS_OK;
+
+// If mDestroyedFrames is null, we want to create a new hashtable here
+// and destroy it on exit; but if it is already non-null (because we're in
+// a recursive call), we will continue to use the existing table to
+// accumulate destroyed frames, and NOT clear mDestroyedFrames on exit.
+// We use a MaybeClearDestroyedFrames helper to conditionally reset the
+// mDestroyedFrames pointer when this method returns.
+typedef decltype(mDestroyedFrames) DestroyedFramesT;
+class MOZ_RAII MaybeClearDestroyedFrames
+{
+private:
+  DestroyedFramesT& mDestroyedFramesRef; // ref to caller's mDestroyedFrames
+  const bool        mResetOnDestruction;
+public:
+  explicit MaybeClearDestroyedFrames(DestroyedFramesT& aTarget)
+    : mDestroyedFramesRef(aTarget)
+    , mResetOnDestruction(!aTarget) // reset only if target starts out null
+  {
+  }
+  ~MaybeClearDestroyedFrames()
+  {
+    if (mResetOnDestruction) {
+      mDestroyedFramesRef.reset(nullptr);
+    }
+  }
+};
+
+MaybeClearDestroyedFrames maybeClear(mDestroyedFrames);
+if (!mDestroyedFrames) {
+  mDestroyedFrames = MakeUnique<nsTHashtable<nsPtrHashKey<const nsIFrame>>>();
+}
 
   PROFILER_LABEL("RestyleManager", "ProcessRestyledFrames",
                  js::ProfileEntry::Category::CSS);
 
   nsPresContext* presContext = PresContext();
-  FramePropertyTable* propTable = presContext->PropertyTable();
   nsCSSFrameConstructor* frameConstructor = presContext->FrameConstructor();
 
   // Handle nsChangeHint_CSSOverflowChange, by either updating the
@@ -1135,15 +1168,6 @@ RestyleManagerBase::ProcessRestyledFrames(nsStyleChangeList& aChangeList)
   // processing restyles
   frameConstructor->BeginUpdate();
 
-  // Mark frames so that we skip frames that die along the way, bug 123049.
-  // A frame can be in the list multiple times with different hints. Further
-  // optmization is possible if nsStyleChangeList::AppendChange could coalesce
-  for (const nsStyleChangeData& data : aChangeList) {
-    if (data.mFrame) {
-      propTable->Set(data.mFrame, ChangeListProperty(), true);
-    }
-  }
-
   bool didUpdateCursor = false;
 
   for (const nsStyleChangeData& data : aChangeList) {
@@ -1157,7 +1181,7 @@ RestyleManagerBase::ProcessRestyledFrames(nsStyleChangeList& aChangeList)
                  "Reflow hint bits set without actually asking for a reflow");
 
     // skip any frame that has been destroyed due to a ripple effect
-    if (frame && !propTable->Get(frame, ChangeListProperty())) {
+    if (frame && mDestroyedFrames->Contains(frame)) {
       continue;
     }
 
@@ -1409,15 +1433,11 @@ RestyleManagerBase::ProcessRestyledFrames(nsStyleChangeList& aChangeList)
 
   frameConstructor->EndUpdate();
 
-  // cleanup references and verify the style tree.  Note that the latter needs
-  // to happen once we've processed the whole list, since until then the tree
-  // is not in fact in a consistent state.
-  for (const nsStyleChangeData& data : aChangeList) {
-    if (data.mFrame) {
-      propTable->Delete(data.mFrame, ChangeListProperty());
-    }
-
 #ifdef DEBUG
+  // Verify the style tree.  Note that this needs to happen once we've
+  // processed the whole list, since until then the tree is not in fact in a
+  // consistent state.
+  for (const nsStyleChangeData& data : aChangeList) {
     // reget frame from content since it may have been regenerated...
     if (data.mContent) {
       nsIFrame* frame = data.mContent->GetPrimaryFrame();
@@ -1429,8 +1449,8 @@ RestyleManagerBase::ProcessRestyledFrames(nsStyleChangeList& aChangeList)
       NS_WARNING("Unable to test style tree integrity -- no content node "
                  "(and not a viewport frame)");
     }
-#endif
   }
+#endif
 
   aChangeList.Clear();
   return NS_OK;
diff --git a/layout/base/RestyleManagerBase.h b/layout/base/RestyleManagerBase.h
index f81f5e73f..d92c3d1f7 100644
--- a/layout/base/RestyleManagerBase.h
+++ b/layout/base/RestyleManagerBase.h
@@ -72,6 +72,11 @@ public:
   // WillDestroyFrameTree hasn't been called yet.
   void NotifyDestroyingFrame(nsIFrame* aFrame) {
     mOverflowChangedTracker.RemoveFrame(aFrame);
+    // If ProcessRestyledFrames is tracking frames which have been
+    // destroyed (to avoid re-visiting them), add this one to its set.
+    if (mDestroyedFrames) {
+      mDestroyedFrames->PutEntry(aFrame);
+    }
   }
 
   // Note: It's the caller's responsibility to make sure to wrap a
@@ -127,6 +132,12 @@ private:
   nsPresContext* mPresContext; // weak, can be null after Disconnect().
   uint32_t mRestyleGeneration;
   uint32_t mHoverGeneration;
+  
+  // Used to keep track of frames that have been destroyed during
+  // ProcessRestyledFrames, so we don't try to touch them again even if
+  // they're referenced again later in the changelist.
+  mozilla::UniquePtr<nsTHashtable<nsPtrHashKey<const nsIFrame>>> mDestroyedFrames;
+
   // True if we're already waiting for a refresh notification.
   bool mObservingRefreshDriver;
 
@@ -146,7 +157,7 @@ protected:
   GetNearestAncestorFrame(nsIContent* aContent);
 
   static nsIFrame*
-  GetNextBlockInInlineSibling(FramePropertyTable* aPropTable, nsIFrame* aFrame);
+  GetNextBlockInInlineSibling(nsIFrame* aFrame);
 
   /**
    * Get the next continuation or similar ib-split sibling (assuming
diff --git a/layout/base/moz.build b/layout/base/moz.build
index d3e417f16..4308a6e4d 100644
--- a/layout/base/moz.build
+++ b/layout/base/moz.build
@@ -61,7 +61,7 @@ EXPORTS += [
     'DisplayItemScrollClip.h',
     'DisplayListClipState.h',
     'FrameLayerBuilder.h',
-    'FramePropertyTable.h',
+    'FrameProperties.h',
     'LayerState.h',
     'LayoutLogging.h',
     'nsArenaMemoryStats.h',
@@ -126,7 +126,6 @@ UNIFIED_SOURCES += [
     'DisplayListClipState.cpp',
     'DottedCornerFinder.cpp',
     'FrameLayerBuilder.cpp',
-    'FramePropertyTable.cpp',
     'GeometryUtils.cpp',
     'LayoutLogging.cpp',
     'MaskLayerImageCache.cpp',
diff --git a/layout/base/nsBidiPresUtils.cpp b/layout/base/nsBidiPresUtils.cpp
index b3c20aabb..887563504 100644
--- a/layout/base/nsBidiPresUtils.cpp
+++ b/layout/base/nsBidiPresUtils.cpp
@@ -753,7 +753,6 @@ nsBidiPresUtils::ResolveParagraph(BidiParagraphData* aBpd)
   nsIContent* content = nullptr;
   int32_t     contentTextLength = 0;
 
-  FramePropertyTable* propTable = aBpd->mPresContext->PropertyTable();
   nsLineBox* currentLine = nullptr;
   
 #ifdef DEBUG
@@ -809,7 +808,7 @@ nsBidiPresUtils::ResolveParagraph(BidiParagraphData* aBpd)
     }
     precedingControl = kBidiLevelNone;
     lastEmbedingLevel = embeddingLevel;
-    propTable->Set(frame, nsIFrame::BidiDataProperty(), bidiData);
+    frame->SetProperty(nsIFrame::BidiDataProperty(), bidiData);
   };
 
   for (; ;) {
@@ -1787,7 +1786,7 @@ nsBidiPresUtils::RemoveBidiContinuation(BidiParagraphData *aBpd,
     if (frame != NS_BIDI_CONTROL_FRAME) {
       // Make the frame and its continuation ancestors fluid,
       // so they can be reused or deleted by normal reflow code
-      frame->Properties().Set(nsIFrame::BidiDataProperty(), bidiData);
+      frame->SetProperty(nsIFrame::BidiDataProperty(), bidiData);
       frame->AddStateBits(NS_FRAME_IS_BIDI);
       while (frame) {
         nsIFrame* prev = frame->GetPrevContinuation();
diff --git a/layout/base/nsCSSFrameConstructor.cpp b/layout/base/nsCSSFrameConstructor.cpp
index 07a5b80e7..ec676ca92 100644
--- a/layout/base/nsCSSFrameConstructor.cpp
+++ b/layout/base/nsCSSFrameConstructor.cpp
@@ -492,9 +492,8 @@ static nsContainerFrame* GetIBSplitSibling(nsIFrame* aFrame)
 
   // We only store the "ib-split sibling" annotation with the first
   // frame in the continuation chain. Walk back to find that frame now.
-  return static_cast<nsContainerFrame*>
-    (aFrame->FirstContinuation()->
-       Properties().Get(nsIFrame::IBSplitSibling()));
+  return aFrame->FirstContinuation()->
+           GetProperty(nsIFrame::IBSplitSibling());
 }
 
 static nsContainerFrame* GetIBSplitPrevSibling(nsIFrame* aFrame)
@@ -503,9 +502,8 @@ static nsContainerFrame* GetIBSplitPrevSibling(nsIFrame* aFrame)
 
   // We only store the ib-split sibling annotation with the first
   // frame in the continuation chain. Walk back to find that frame now.
-  return static_cast<nsContainerFrame*>
-    (aFrame->FirstContinuation()->
-       Properties().Get(nsIFrame::IBSplitPrevSibling()));
+  return aFrame->FirstContinuation()->
+           GetProperty(nsIFrame::IBSplitPrevSibling());
 }
 
 static nsContainerFrame*
@@ -526,7 +524,7 @@ GetLastIBSplitSibling(nsIFrame* aFrame, bool aReturnEmptyTrailingInline)
 }
 
 static void
-SetFrameIsIBSplit(nsContainerFrame* aFrame, nsIFrame* aIBSplitSibling)
+SetFrameIsIBSplit(nsContainerFrame* aFrame, nsContainerFrame* aIBSplitSibling)
 {
   NS_PRECONDITION(aFrame, "bad args!");
 
@@ -547,9 +545,8 @@ SetFrameIsIBSplit(nsContainerFrame* aFrame, nsIFrame* aIBSplitSibling)
 
     // Store the ib-split sibling (if we were given one) with the
     // first frame in the flow.
-    FramePropertyTable* props = aFrame->PresContext()->PropertyTable();
-    props->Set(aFrame, nsIFrame::IBSplitSibling(), aIBSplitSibling);
-    props->Set(aIBSplitSibling, nsIFrame::IBSplitPrevSibling(), aFrame);
+    aFrame->SetProperty(nsIFrame::IBSplitSibling(), aIBSplitSibling);
+    aIBSplitSibling->SetProperty(nsIFrame::IBSplitPrevSibling(), aFrame);
   }
 }
 
@@ -6075,11 +6072,11 @@ AddGenConPseudoToFrame(nsIFrame* aOwnerFrame, nsIContent* aContent)
   NS_ASSERTION(nsLayoutUtils::IsFirstContinuationOrIBSplitSibling(aOwnerFrame),
                "property should only be set on first continuation/ib-sibling");
 
-  FrameProperties props = aOwnerFrame->Properties();
-  nsIFrame::ContentArray* value = props.Get(nsIFrame::GenConProperty());
+  nsIFrame::ContentArray* value =
+    aOwnerFrame->GetProperty(nsIFrame::GenConProperty());
   if (!value) {
     value = new nsIFrame::ContentArray;
-    props.Set(nsIFrame::GenConProperty(), value);
+    aOwnerFrame->SetProperty(nsIFrame::GenConProperty(), value);
   }
   value->AppendElement(aContent);
 }
diff --git a/layout/base/nsCSSRendering.cpp b/layout/base/nsCSSRendering.cpp
index ff9edf742..119c6c8a2 100644
--- a/layout/base/nsCSSRendering.cpp
+++ b/layout/base/nsCSSRendering.cpp
@@ -293,13 +293,13 @@ protected:
     if (!prevCont &&
         (aFrame->GetStateBits() & NS_FRAME_PART_OF_IBSPLIT)) {
       nsIFrame* block =
-        aFrame->Properties().Get(nsIFrame::IBSplitPrevSibling());
+        aFrame->GetProperty(nsIFrame::IBSplitPrevSibling());
       if (block) {
         // The {ib} properties are only stored on first continuations
         NS_ASSERTION(!block->GetPrevContinuation(),
                      "Incorrect value for IBSplitPrevSibling");
         prevCont =
-          block->Properties().Get(nsIFrame::IBSplitPrevSibling());
+          block->GetProperty(nsIFrame::IBSplitPrevSibling());
         NS_ASSERTION(prevCont, "How did that happen?");
       }
     }
@@ -313,9 +313,9 @@ protected:
         (aFrame->GetStateBits() & NS_FRAME_PART_OF_IBSPLIT)) {
       // The {ib} properties are only stored on first continuations
       aFrame = aFrame->FirstContinuation();
-      nsIFrame* block = aFrame->Properties().Get(nsIFrame::IBSplitSibling());
+      nsIFrame* block = aFrame->GetProperty(nsIFrame::IBSplitSibling());
       if (block) {
-        nextCont = block->Properties().Get(nsIFrame::IBSplitSibling());
+        nextCont = block->GetProperty(nsIFrame::IBSplitSibling());
         NS_ASSERTION(nextCont, "How did that happen?");
       }
     }
@@ -842,7 +842,7 @@ static nsRect
 GetOutlineInnerRect(nsIFrame* aFrame)
 {
   nsRect* savedOutlineInnerRect =
-    aFrame->Properties().Get(nsIFrame::OutlineInnerRectProperty());
+    aFrame->GetProperty(nsIFrame::OutlineInnerRectProperty());
   if (savedOutlineInnerRect)
     return *savedOutlineInnerRect;
   NS_NOTREACHED("we should have saved a frame property");
diff --git a/layout/base/nsDisplayList.cpp b/layout/base/nsDisplayList.cpp
index a55ec1e39..e35e027e3 100644
--- a/layout/base/nsDisplayList.cpp
+++ b/layout/base/nsDisplayList.cpp
@@ -666,7 +666,7 @@ nsDisplayListBuilder::AddAnimationsAndTransitionsToLayer(Layer* aLayer,
     // EffectCompositor needs to know that we refused to run this animation
     // asynchronously so that it will not throttle the main thread
     // animation.
-    aFrame->Properties().Set(nsIFrame::RefusedAsyncAnimationProperty(), true);
+    aFrame->SetProperty(nsIFrame::RefusedAsyncAnimationProperty(), true);
 
     // We need to schedule another refresh driver run so that EffectCompositor
     // gets a chance to unthrottle the animation.
@@ -902,15 +902,13 @@ void nsDisplayListBuilder::MarkOutOfFlowFrameForDisplay(nsIFrame* aDirtyFrame,
   const DisplayItemClip* oldClip = mClipState.GetClipForContainingBlockDescendants();
   const DisplayItemScrollClip* sc = mClipState.GetCurrentInnermostScrollClip();
   OutOfFlowDisplayData* data = new OutOfFlowDisplayData(oldClip, sc, dirty);
-  aFrame->Properties().Set(nsDisplayListBuilder::OutOfFlowDisplayDataProperty(), data);
+  aFrame->SetProperty(nsDisplayListBuilder::OutOfFlowDisplayDataProperty(), data);
 
   MarkFrameForDisplay(aFrame, aDirtyFrame);
 }
 
 static void UnmarkFrameForDisplay(nsIFrame* aFrame) {
-  nsPresContext* presContext = aFrame->PresContext();
-  presContext->PropertyTable()->
-    Delete(aFrame, nsDisplayListBuilder::OutOfFlowDisplayDataProperty());
+  aFrame->DeleteProperty(nsDisplayListBuilder::OutOfFlowDisplayDataProperty());
 
   for (nsIFrame* f = aFrame; f;
        f = nsLayoutUtils::GetParentOrPlaceholderFor(f)) {
diff --git a/layout/base/nsDisplayList.h b/layout/base/nsDisplayList.h
index fcdc9e4fc..c81d34fac 100644
--- a/layout/base/nsDisplayList.h
+++ b/layout/base/nsDisplayList.h
@@ -1003,7 +1003,7 @@ public:
 
   static OutOfFlowDisplayData* GetOutOfFlowData(nsIFrame* aFrame)
   {
-    return aFrame->Properties().Get(OutOfFlowDisplayDataProperty());
+    return aFrame->GetProperty(OutOfFlowDisplayDataProperty());
   }
 
   nsPresContext* CurrentPresContext() {
diff --git a/layout/base/nsIPresShell.h b/layout/base/nsIPresShell.h
index 4016cc0a9..865f5534c 100644
--- a/layout/base/nsIPresShell.h
+++ b/layout/base/nsIPresShell.h
@@ -1533,11 +1533,12 @@ public:
                                       bool aFlushOnHoverChange) = 0;
 
   virtual void AddSizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf,
-                                      nsArenaMemoryStats *aArenaObjectsSize,
-                                      size_t *aPresShellSize,
-                                      size_t *aStyleSetsSize,
-                                      size_t *aTextRunsSize,
-                                      size_t *aPresContextSize) = 0;
+                                      nsArenaMemoryStats* aArenaObjectsSize,
+                                      size_t* aPresShellSize,
+                                      size_t* aStyleSetsSize,
+                                      size_t* aTextRunsSize,
+                                      size_t* aPresContextSize,
+                                      size_t* aFramePropertiesSize) = 0;
 
   /**
    * Methods that retrieve the cached font inflation preferences.
diff --git a/layout/base/nsLayoutUtils.cpp b/layout/base/nsLayoutUtils.cpp
index 07befdc81..06690b208 100644
--- a/layout/base/nsLayoutUtils.cpp
+++ b/layout/base/nsLayoutUtils.cpp
@@ -2057,13 +2057,13 @@ NS_DECLARE_FRAME_PROPERTY_SMALL_VALUE(ScrollbarThumbLayerized, bool)
 /* static */ void
 nsLayoutUtils::SetScrollbarThumbLayerization(nsIFrame* aThumbFrame, bool aLayerize)
 {
-  aThumbFrame->Properties().Set(ScrollbarThumbLayerized(), aLayerize);
+  aThumbFrame->SetProperty(ScrollbarThumbLayerized(), aLayerize);
 }
 
 bool
 nsLayoutUtils::IsScrollbarThumbLayerized(nsIFrame* aThumbFrame)
 {
-  return aThumbFrame->Properties().Get(ScrollbarThumbLayerized());
+  return aThumbFrame->GetProperty(ScrollbarThumbLayerized());
 }
 
 // static
@@ -4427,7 +4427,7 @@ nsLayoutUtils::GetNextContinuationOrIBSplitSibling(nsIFrame *aFrame)
     // frame in the continuation chain. Walk back to find that frame now.
     aFrame = aFrame->FirstContinuation();
 
-    return aFrame->Properties().Get(nsIFrame::IBSplitSibling());
+    return aFrame->GetProperty(nsIFrame::IBSplitSibling());
   }
 
   return nullptr;
@@ -4440,7 +4440,7 @@ nsLayoutUtils::FirstContinuationOrIBSplitSibling(nsIFrame *aFrame)
   if (result->GetStateBits() & NS_FRAME_PART_OF_IBSPLIT) {
     while (true) {
       nsIFrame* f =
-        result->Properties().Get(nsIFrame::IBSplitPrevSibling());
+        result->GetProperty(nsIFrame::IBSplitPrevSibling());
       if (!f)
         break;
       result = f;
@@ -4456,10 +4456,10 @@ nsLayoutUtils::LastContinuationOrIBSplitSibling(nsIFrame *aFrame)
   nsIFrame *result = aFrame->FirstContinuation();
   if (result->GetStateBits() & NS_FRAME_PART_OF_IBSPLIT) {
     while (true) {
-      nsIFrame* f =
-        result->Properties().Get(nsIFrame::IBSplitSibling());
-      if (!f)
+      nsIFrame* f = result->GetProperty(nsIFrame::IBSplitSibling());
+      if (!f) {
         break;
+      }
       result = f;
     }
   }
@@ -4476,7 +4476,7 @@ nsLayoutUtils::IsFirstContinuationOrIBSplitSibling(nsIFrame *aFrame)
     return false;
   }
   if ((aFrame->GetStateBits() & NS_FRAME_PART_OF_IBSPLIT) &&
-      aFrame->Properties().Get(nsIFrame::IBSplitPrevSibling())) {
+      aFrame->GetProperty(nsIFrame::IBSplitPrevSibling())) {
     return false;
   }
 
diff --git a/layout/base/nsPresContext.cpp b/layout/base/nsPresContext.cpp
index 3106ff386..befb5deb2 100644
--- a/layout/base/nsPresContext.cpp
+++ b/layout/base/nsPresContext.cpp
@@ -2741,8 +2741,7 @@ nsPresContext::GetPrimaryFrameFor(nsIContent* aContent)
 size_t
 nsPresContext::SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const
 {
-  return mPropertyTable.SizeOfExcludingThis(aMallocSizeOf) +
-         mLangGroupFontPrefs.SizeOfExcludingThis(aMallocSizeOf);
+  return mLangGroupFontPrefs.SizeOfExcludingThis(aMallocSizeOf);
 
   // Measurement of other members may be added later if DMD finds it is
   // worthwhile.
diff --git a/layout/base/nsPresContext.h b/layout/base/nsPresContext.h
index d8f876291..a2b9bb533 100644
--- a/layout/base/nsPresContext.h
+++ b/layout/base/nsPresContext.h
@@ -22,7 +22,6 @@
 #include "nsITimer.h"
 #include "nsCRT.h"
 #include "nsIWidgetListener.h"
-#include "FramePropertyTable.h"
 #include "nsGkAtoms.h"
 #include "nsCycleCollectionParticipant.h"
 #include "nsChangeHint.h"
@@ -140,7 +139,6 @@ class nsRootPresContext;
 class nsPresContext : public nsIObserver,
                       public mozilla::SupportsWeakPtr<nsPresContext> {
 public:
-  typedef mozilla::FramePropertyTable FramePropertyTable;
   typedef mozilla::LangGroupFontPrefs LangGroupFontPrefs;
   typedef mozilla::ScrollbarStyles ScrollbarStyles;
   typedef mozilla::StaticPresData StaticPresData;
@@ -867,9 +865,6 @@ public:
 
   nsIPrintSettings* GetPrintSettings() { return mPrintSettings; }
 
-  /* Accessor for table of frame properties */
-  FramePropertyTable* PropertyTable() { return &mPropertyTable; }
-
   /* Helper function that ensures that this prescontext is shown in its
      docshell if it's the most recent prescontext for the docshell.  Returns
      whether the prescontext is now being shown.
@@ -1064,11 +1059,6 @@ public:
    */
   nsIFrame* GetPrimaryFrameFor(nsIContent* aContent);
 
-  void NotifyDestroyingFrame(nsIFrame* aFrame)
-  {
-    PropertyTable()->DeleteAllFor(aFrame);
-  }
-
   virtual size_t SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) const;
   virtual size_t SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const {
     return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf);
@@ -1294,7 +1284,6 @@ protected:
   nsCOMPtr<nsIPrintSettings> mPrintSettings;
   nsCOMPtr<nsITimer>    mPrefChangedTimer;
 
-  FramePropertyTable    mPropertyTable;
 
   nsInvalidateRequestList mInvalidateRequestsSinceLastPaint;
   nsInvalidateRequestList mUndeliveredInvalidateRequestsBeforeLastPaint;
diff --git a/layout/base/nsPresShell.cpp b/layout/base/nsPresShell.cpp
index d4fbebbf2..dacc6603b 100644
--- a/layout/base/nsPresShell.cpp
+++ b/layout/base/nsPresShell.cpp
@@ -1306,19 +1306,6 @@ PresShell::Destroy()
   // Destroy the frame manager. This will destroy the frame hierarchy
   mFrameConstructor->WillDestroyFrameTree();
 
-  // Destroy all frame properties (whose destruction was suppressed
-  // while destroying the frame tree, but which might contain more
-  // frames within the properties.
-  if (mPresContext) {
-    // Clear out the prescontext's property table -- since our frame tree is
-    // now dead, we shouldn't be looking up any more properties in that table.
-    // We want to do this before we call DetachShell() on the prescontext, so
-    // property destructors can usefully call GetPresShell() on the
-    // prescontext.
-    mPresContext->PropertyTable()->DeleteAll();
-  }
-
-
   NS_WARNING_ASSERTION(!mWeakFrames,
                        "Weak frames alive after destroying FrameManager");
   while (mWeakFrames) {
@@ -2047,7 +2034,7 @@ PresShell::NotifyDestroyingFrame(nsIFrame* aFrame)
     }
 
     // Remove frame properties
-    mPresContext->NotifyDestroyingFrame(aFrame);
+    aFrame->DeleteAllProperties();
 
     if (aFrame == mCurrentEventFrame) {
       mCurrentEventContent = aFrame->GetContent();
@@ -2076,8 +2063,7 @@ PresShell::NotifyDestroyingFrame(nsIFrame* aFrame)
     // frame from FrameLayerBuilder::DisplayItemData::mFrameList -- otherwise
     // the DisplayItemData destructor will use the destroyed frame when it
     // tries to remove it from the (array) value of this property.
-    mPresContext->PropertyTable()->
-      Delete(aFrame, FrameLayerBuilder::LayerManagerDataProperty());
+      aFrame->DeleteProperty( FrameLayerBuilder::LayerManagerDataProperty());
   }
 }
 
@@ -10929,11 +10915,12 @@ PresShell::GetRootPresShell()
 
 void
 PresShell::AddSizeOfIncludingThis(MallocSizeOf aMallocSizeOf,
-                                  nsArenaMemoryStats *aArenaObjectsSize,
-                                  size_t *aPresShellSize,
-                                  size_t *aStyleSetsSize,
-                                  size_t *aTextRunsSize,
-                                  size_t *aPresContextSize)
+                                  nsArenaMemoryStats* aArenaObjectsSize,
+                                  size_t* aPresShellSize,
+                                  size_t* aStyleSetsSize,
+                                  size_t* aTextRunsSize,
+                                  size_t* aPresContextSize,
+                                  size_t* aFramePropertiesSize)
 {
   mFrameArena.AddSizeOfExcludingThis(aMallocSizeOf, aArenaObjectsSize);
   *aPresShellSize += aMallocSizeOf(this);
@@ -10953,6 +10940,12 @@ PresShell::AddSizeOfIncludingThis(MallocSizeOf aMallocSizeOf,
   *aTextRunsSize += SizeOfTextRuns(aMallocSizeOf);
 
   *aPresContextSize += mPresContext->SizeOfIncludingThis(aMallocSizeOf);
+
+  nsIFrame* rootFrame = mFrameConstructor->GetRootFrame();
+  if (rootFrame) {
+    *aFramePropertiesSize +=
+      rootFrame->SizeOfFramePropertiesForTree(aMallocSizeOf);
+  }
 }
 
 size_t
diff --git a/layout/base/nsPresShell.h b/layout/base/nsPresShell.h
index 1a8dd3fef..10548880a 100644
--- a/layout/base/nsPresShell.h
+++ b/layout/base/nsPresShell.h
@@ -384,11 +384,12 @@ public:
   virtual void LoadComplete() override;
 
   void AddSizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf,
-                              nsArenaMemoryStats *aArenaObjectsSize,
-                              size_t *aPresShellSize,
-                              size_t *aStyleSetsSize,
-                              size_t *aTextRunsSize,
-                              size_t *aPresContextSize) override;
+                              nsArenaMemoryStats* aArenaObjectsSize,
+                              size_t* aPresShellSize,
+                              size_t* aStyleSetsSize,
+                              size_t* aTextRunsSize,
+                              size_t* aPresContextSize,
+                              size_t* aFramePropertiesSize) override;
   size_t SizeOfTextRuns(mozilla::MallocSizeOf aMallocSizeOf) const;
 
   virtual void AddInvalidateHiddenPresShellObserver(nsRefreshDriver *aDriver) override;
-- 
cgit v1.2.3