Bug 1365982 - Attach frame properties to each frame instead of looking them up in a hashtable on the prescontext. r=mats
authorJonathan Kew <jkew@mozilla.com>
Sat, 27 May 2017 12:36:00 +0100
changeset 409129 b37e4d256cd6c88b48b0223113375f889a748982
parent 409128 c132697561b071b4f6636e6ec9e0ee35ba4530d3
child 409130 2ba8ded0fe690cc55a55e574effff0d41daefaad
push id7391
push usermtabara@mozilla.com
push dateMon, 12 Jun 2017 13:08:53 +0000
treeherdermozilla-beta@2191d7f87e2e [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmats
bugs1365982
milestone55.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1365982 - Attach frame properties to each frame instead of looking them up in a hashtable on the prescontext. r=mats
layout/base/FrameProperties.cpp
layout/base/FrameProperties.h
layout/base/FramePropertyTable.cpp
layout/base/FramePropertyTable.h
layout/base/GeckoRestyleManager.cpp
layout/base/OverflowChangedTracker.h
layout/base/PresShell.cpp
layout/base/RestyleManager.cpp
layout/base/RestyleManager.h
layout/base/moz.build
layout/base/nsBidiPresUtils.cpp
layout/base/nsCSSFrameConstructor.cpp
layout/base/nsLayoutUtils.cpp
layout/base/nsPresContext.cpp
layout/base/nsPresContext.h
layout/forms/nsTextControlFrame.cpp
layout/forms/nsTextControlFrame.h
layout/generic/ReflowInput.cpp
layout/generic/RubyUtils.cpp
layout/generic/StickyScrollContainer.cpp
layout/generic/nsBlockFrame.cpp
layout/generic/nsBlockFrame.h
layout/generic/nsBulletFrame.cpp
layout/generic/nsCanvasFrame.cpp
layout/generic/nsCanvasFrame.h
layout/generic/nsContainerFrame.cpp
layout/generic/nsContainerFrame.h
layout/generic/nsFlexContainerFrame.cpp
layout/generic/nsFloatManager.cpp
layout/generic/nsFontInflationData.cpp
layout/generic/nsFrame.cpp
layout/generic/nsFrameStateBits.h
layout/generic/nsGridContainerFrame.cpp
layout/generic/nsGridContainerFrame.h
layout/generic/nsIFrame.h
layout/generic/nsInlineFrame.cpp
layout/generic/nsLineLayout.cpp
layout/generic/nsPlaceholderFrame.cpp
layout/generic/nsTextFrame.cpp
layout/mathml/nsMathMLContainerFrame.cpp
layout/mathml/nsMathMLmtableFrame.cpp
layout/painting/ActiveLayerTracker.cpp
layout/painting/nsCSSRendering.cpp
layout/painting/nsDisplayList.cpp
layout/painting/nsDisplayList.h
layout/svg/SVGTextFrame.cpp
layout/svg/nsSVGEffects.cpp
layout/svg/nsSVGEffects.h
layout/svg/nsSVGFilterFrame.cpp
layout/svg/nsSVGGradientFrame.cpp
layout/svg/nsSVGIntegrationUtils.cpp
layout/svg/nsSVGPatternFrame.cpp
layout/svg/nsSVGUtils.cpp
layout/tables/nsTableFrame.cpp
layout/tables/nsTableRowFrame.cpp
layout/tables/nsTableRowGroupFrame.cpp
layout/tables/nsTableWrapperFrame.cpp
layout/xul/nsBox.cpp
layout/xul/nsMenuFrame.cpp
rename from layout/base/FramePropertyTable.cpp
rename to layout/base/FrameProperties.cpp
--- a/layout/base/FramePropertyTable.cpp
+++ b/layout/base/FrameProperties.cpp
@@ -1,273 +1,92 @@
 /* -*- 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 "FrameProperties.h"
 
 #include "mozilla/MemoryReporting.h"
 #include "mozilla/ServoStyleSet.h"
 #include "nsThreadUtils.h"
 
 namespace mozilla {
 
 void
-FramePropertyTable::SetInternal(
-  nsIFrame* aFrame, UntypedDescriptor aProperty, void* aValue)
+FrameProperties::SetInternal(UntypedDescriptor aProperty, void* aValue,
+                             const nsIFrame* aFrame)
 {
   MOZ_ASSERT(NS_IsMainThread());
-  NS_ASSERTION(aFrame, "Null frame?");
-  NS_ASSERTION(aProperty, "Null property?");
-
-  if (mLastFrame != aFrame || !mLastEntry) {
-    mLastFrame = aFrame;
-    mLastEntry = mEntries.PutEntry(aFrame);
-    aFrame->AddStateBits(NS_FRAME_HAS_PROPERTIES);
-  }
-  Entry* entry = mLastEntry;
+  MOZ_ASSERT(aProperty, "Null property?");
 
-  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());
+    mProperties.IndexOf(aProperty, 0, PropertyComparator());
   if (index != nsTArray<PropertyValue>::NoIndex) {
-    PropertyValue* pv = &array->ElementAt(index);
+    PropertyValue* pv = &mProperties.ElementAt(index);
     pv->DestroyValueFor(aFrame);
     pv->mValue = aValue;
     return;
   }
 
-  array->AppendElement(PropertyValue(aProperty, aValue));
+  mProperties.AppendElement(PropertyValue(aProperty, aValue));
+#ifdef DEBUG
+  mMaxLength = std::max<uint32_t>(mMaxLength, mProperties.Length());
+#endif
 }
 
 void*
-FramePropertyTable::GetInternal(
-  const nsIFrame* aFrame, UntypedDescriptor aProperty, bool aSkipBitCheck,
-  bool* aFoundResult)
-{
-  NS_ASSERTION(aFrame, "Null frame?");
-  NS_ASSERTION(aProperty, "Null property?");
-
-  if (aFoundResult) {
-    *aFoundResult = false;
-  }
-
-  if (!aSkipBitCheck && !(aFrame->GetStateBits() & NS_FRAME_HAS_PROPERTIES)) {
-    return nullptr;
-  }
-
-  // We can end up here during parallel style traversal, in which case the main
-  // thread is blocked. Reading from the cache is fine on any thread, but we
-  // only want to write to it in the main-thread case.
-  bool cacheHit = mLastFrame == aFrame;
-  Entry* entry = cacheHit ? mLastEntry : mEntries.GetEntry(aFrame);
-  if (!cacheHit && !ServoStyleSet::IsInServoTraversal()) {
-    mLastFrame = aFrame;
-    mLastEntry = entry;
-  }
-
-  MOZ_ASSERT(entry || aSkipBitCheck,
-             "NS_FRAME_HAS_PROPERTIES bit should match whether entry exists");
-  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(
-  nsIFrame* aFrame, UntypedDescriptor aProperty, bool aSkipBitCheck,
-  bool* aFoundResult)
+FrameProperties::RemoveInternal(UntypedDescriptor aProperty, bool* aFoundResult)
 {
   MOZ_ASSERT(NS_IsMainThread());
-  NS_ASSERTION(aFrame, "Null frame?");
-  NS_ASSERTION(aProperty, "Null property?");
-
-  if (aFoundResult) {
-    *aFoundResult = false;
-  }
-
-  if (!aSkipBitCheck && !(aFrame->GetStateBits() & NS_FRAME_HAS_PROPERTIES)) {
-    return nullptr;
-  }
-
-  if (mLastFrame != aFrame) {
-    mLastFrame = aFrame;
-    mLastEntry = mEntries.GetEntry(aFrame);
-  }
-  Entry* entry = mLastEntry;
-  MOZ_ASSERT(entry || aSkipBitCheck,
-             "NS_FRAME_HAS_PROPERTIES bit should match whether entry exists");
-  if (!entry)
-    return nullptr;
+  MOZ_ASSERT(aProperty, "Null property?");
 
-  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);
-    aFrame->RemoveStateBits(NS_FRAME_HAS_PROPERTIES);
-    mLastEntry = nullptr;
+  nsTArray<PropertyValue>::index_type index =
+    mProperties.IndexOf(aProperty, 0, PropertyComparator());
+  if (index == nsTArray<PropertyValue>::NoIndex) {
     if (aFoundResult) {
-      *aFoundResult = true;
+      *aFoundResult = false;
     }
-    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);
+  void* result = mProperties.ElementAt(index).mValue;
+  mProperties.RemoveElementAt(index);
 
-  if (last == 1) {
-    PropertyValue pv = array->ElementAt(0);
-    array->~nsTArray<PropertyValue>();
-    entry->mProp = pv;
-  }
-  
   return result;
 }
 
 void
-FramePropertyTable::DeleteInternal(
-  nsIFrame* aFrame, UntypedDescriptor aProperty, bool aSkipBitCheck)
+FrameProperties::DeleteInternal(UntypedDescriptor aProperty,
+                                const nsIFrame* aFrame)
 {
   MOZ_ASSERT(NS_IsMainThread());
-  NS_ASSERTION(aFrame, "Null frame?");
-  NS_ASSERTION(aProperty, "Null property?");
+  MOZ_ASSERT(aProperty, "Null property?");
 
   bool found;
-  void* v = RemoveInternal(aFrame, aProperty, aSkipBitCheck, &found);
+  void* v = RemoveInternal(aProperty, &found);
   if (found) {
     PropertyValue pv(aProperty, v);
     pv.DestroyValueFor(aFrame);
   }
 }
 
-/* static */ void
-FramePropertyTable::DeleteAllForEntry(Entry* aEntry)
+void
+FrameProperties::DeleteAll(const nsIFrame* aFrame)
 {
-  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(nsIFrame* aFrame)
-{
-  NS_ASSERTION(aFrame, "Null frame?");
-
-  if (!(aFrame->GetStateBits() & NS_FRAME_HAS_PROPERTIES)) {
-    return;
+  for (uint32_t i = 0; i < mProperties.Length(); ++i) {
+    mProperties.ElementAt(i).DestroyValueFor(aFrame);
   }
 
-  Entry* entry = mEntries.GetEntry(aFrame);
-  MOZ_ASSERT(entry,
-             "NS_FRAME_HAS_PROPERTIES bit should match whether entry exists");
-  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);
-
-  // Don't bother unsetting NS_FRAME_HAS_PROPERTIES, since aFrame is going away
-}
-
-void
-FramePropertyTable::DeleteAll()
-{
-  mLastFrame = nullptr;
-  mLastEntry = nullptr;
-
-  for (auto iter = mEntries.Iter(); !iter.Done(); iter.Next()) {
-    DeleteAllForEntry(iter.Get());
-  }
-  mEntries.Clear();
+  mProperties.Clear();
 }
 
 size_t
-FramePropertyTable::SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) const
+FrameProperties::SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) const
 {
-  return mEntries.SizeOfExcludingThis(aMallocSizeOf);
+  return mProperties.Length() * sizeof(PropertyValue);
 }
 
 } // namespace mozilla
rename from layout/base/FramePropertyTable.h
rename to layout/base/FrameProperties.h
--- a/layout/base/FramePropertyTable.h
+++ b/layout/base/FrameProperties.h
@@ -1,22 +1,20 @@
 /* -*- 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_
+#ifndef FRAMEPROPERTIES_H_
+#define FRAMEPROPERTIES_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
 {
   /**
@@ -126,61 +124,65 @@ template<typename T>
 struct FramePropertyTypeHelper<SmallValueHolder<T>>
 {
   typedef T Type;
 };
 
 }
 
 /**
- * The FramePropertyTable is optimized for storing 0 or 1 properties on
+ * 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 FramePropertyTable {
+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;
 
-  FramePropertyTable() : mLastFrame(nullptr), mLastEntry(nullptr)
+  explicit FrameProperties()
+#ifdef DEBUG
+    : mMaxLength(0)
+#endif
   {
   }
-  ~FramePropertyTable()
+
+  ~FrameProperties()
   {
-    DeleteAll();
+    MOZ_ASSERT(mMaxLength > 0, "redundant FrameProperties!");
+    MOZ_ASSERT(mProperties.Length() == 0, "forgot to delete properties");
   }
 
   /**
-   * 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
+   * 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(nsIFrame* aFrame, Descriptor<T> aProperty,
-           PropertyType<T> aValue)
+  void Set(Descriptor<T> aProperty, PropertyType<T> aValue,
+           const nsIFrame* aFrame)
   {
     void* ptr = ReinterpretHelper<T>::ToPointer(aValue);
-    SetInternal(aFrame, aProperty, ptr);
+    SetInternal(aProperty, ptr, aFrame);
   }
 
   /**
-   * @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.
+   * @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
@@ -190,115 +192,93 @@ public:
    *     an existing value for the frame property.
    *
    * The HasSkippingBitCheck variant doesn't test NS_FRAME_HAS_PROPERTIES
    * on aFrame, so it is safe to call after aFrame has been destroyed as
    * long as, since that destruction happened, it isn't possible for a
    * new frame to have been created and the same property added.
    */
   template<typename T>
-  bool Has(const nsIFrame* aFrame, Descriptor<T> aProperty)
+  bool Has(Descriptor<T> aProperty) const
   {
-    bool foundResult = false;
-    mozilla::Unused << GetInternal(aFrame, aProperty, false, &foundResult);
-    return foundResult;
-  }
-
-  template<typename T>
-  bool HasSkippingBitCheck(const nsIFrame* aFrame, Descriptor<T> aProperty)
-  {
-    bool foundResult = false;
-    mozilla::Unused << GetInternal(aFrame, aProperty, true, &foundResult);
-    return foundResult;
+    return mProperties.IndexOf(aProperty, 0, PropertyComparator()) != nsTArray<PropertyValue>::NoIndex;
   }
 
   /**
-   * 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,
+   * Get a property value. This requires 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(const nsIFrame* aFrame, Descriptor<T> aProperty,
-                      bool* aFoundResult = nullptr)
+  PropertyType<T> Get(Descriptor<T> aProperty,
+                      bool* aFoundResult = nullptr) const
   {
-    void* ptr = GetInternal(aFrame, aProperty, false, aFoundResult);
+    void* ptr = GetInternal(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
+   * 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(nsIFrame* aFrame, Descriptor<T> aProperty,
+  PropertyType<T> Remove(Descriptor<T> aProperty,
                          bool* aFoundResult = nullptr)
   {
-    void* ptr = RemoveInternal(aFrame, aProperty, false, aFoundResult);
+    void* ptr = RemoveInternal(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
+   * 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.
-   *
-   * The DeleteSkippingBitCheck variant doesn't test
-   * NS_FRAME_HAS_PROPERTIES on aFrame, so it is safe to call after
-   * aFrame has been destroyed as long as, since that destruction
-   * happened, it isn't possible for a new frame to have been created
-   * and the same property added.
    */
   template<typename T>
-  void Delete(nsIFrame* aFrame, Descriptor<T> aProperty)
+  void Delete(Descriptor<T> aProperty, const nsIFrame* aFrame)
   {
-    DeleteInternal(aFrame, aProperty, false);
+    DeleteInternal(aProperty, aFrame);
   }
 
-  template<typename T>
-  void DeleteSkippingBitCheck(nsIFrame* aFrame, Descriptor<T> aProperty)
-  {
-    DeleteInternal(aFrame, aProperty, true);
-  }
   /**
-   * Remove and destroy all property values for a frame. This requires one
-   * hashtable lookup (using the frame as the key).
+   * Remove and destroy all property values for the frame.
    */
-  void DeleteAllFor(nsIFrame* aFrame);
-  /**
-   * Remove and destroy all property values for all frames.
-   */
-  void DeleteAll();
+  void DeleteAll(const nsIFrame* aFrame);
 
   size_t SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) const;
 
-protected:
-  void SetInternal(nsIFrame* aFrame, UntypedDescriptor aProperty,
-                   void* aValue);
+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;
 
-  void* GetInternal(const nsIFrame* aFrame, UntypedDescriptor aProperty,
-                    bool aSkipBitCheck, bool* aFoundResult);
+  void SetInternal(UntypedDescriptor aProperty, void* aValue,
+                   const nsIFrame* aFrame);
 
-  void* RemoveInternal(nsIFrame* aFrame, UntypedDescriptor aProperty,
-                       bool aSkipBitCheck, bool* aFoundResult);
+  inline void*
+  GetInternal(UntypedDescriptor aProperty, bool* aFoundResult) const;
 
-  void DeleteInternal(nsIFrame* aFrame, UntypedDescriptor aProperty,
-                      bool aSkipBitCheck);
+  void* RemoveInternal(UntypedDescriptor aProperty, bool* aFoundResult);
+
+  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)
@@ -326,53 +306,31 @@ protected:
 
     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.
+   * Stores a property descriptor/value pair.
    */
   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.
    */
@@ -384,112 +342,38 @@ protected:
     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;
+  AutoTArray<PropertyValue,1> mProperties;
+#ifdef DEBUG
+  uint32_t mMaxLength;
+#endif
 };
 
-/**
- * The FrameProperties/ConstFrameProperties class encapsulates the
- * properties of a frame.
- *
- * However, since frame properties are like member variables, we have
- * different versions for whether the frame is |const|, sharing a common
- * base class.
- *
- * CVnsIFrame is either |nsIFrame| or |const nsIFrame|.
- */
-template<class CVnsIFrame>
-class FramePropertiesBase {
-public:
-  template<typename T> using Descriptor = FramePropertyTable::Descriptor<T>;
-  template<typename T> using PropertyType = FramePropertyTable::PropertyType<T>;
+inline void*
+FrameProperties::GetInternal(UntypedDescriptor aProperty,
+                             bool* aFoundResult) const
+{
+  MOZ_ASSERT(aProperty, "Null property?");
 
-  template<typename T>
-  bool Has(Descriptor<T> aProperty) const
-  {
-    return mTable->Has(mFrame, aProperty);
+  nsTArray<PropertyValue>::index_type index =
+    mProperties.IndexOf(aProperty, 0, PropertyComparator());
+  if (index == nsTArray<PropertyValue>::NoIndex) {
+    if (aFoundResult) {
+      *aFoundResult = false;
+    }
+    return nullptr;
   }
 
-  template<typename T>
-  PropertyType<T> Get(Descriptor<T> aProperty,
-                      bool* aFoundResult = nullptr) const
-  {
-    return mTable->Get(mFrame, aProperty, aFoundResult);
-  }
-
-protected:
-  FramePropertiesBase(FramePropertyTable* aTable, CVnsIFrame* aFrame)
-    : mTable(aTable), mFrame(aFrame) {}
-
-  FramePropertyTable* const mTable;
-  CVnsIFrame* const mFrame;
-};
-
-class ConstFrameProperties : public FramePropertiesBase<const nsIFrame> {
-public:
-  ConstFrameProperties(FramePropertyTable* aTable, const nsIFrame* aFrame)
-    : FramePropertiesBase(aTable, aFrame)
-  {
+  if (aFoundResult) {
+    *aFoundResult = true;
   }
-};
-
-class FrameProperties : public FramePropertiesBase<nsIFrame> {
-public:
-  FrameProperties(FramePropertyTable* aTable, nsIFrame* aFrame)
-    : FramePropertiesBase(aTable, aFrame)
-  {
-  }
-
-  template<typename T>
-  void Set(Descriptor<T> aProperty, PropertyType<T> aValue) const
-  {
-    mTable->Set(mFrame, aProperty, aValue);
-  }
-
-  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) const
-  {
-    mTable->Delete(mFrame, aProperty);
-  }
-
-};
+  return mProperties.ElementAt(index).mValue;
+}
 
 } // namespace mozilla
 
-#endif /* FRAMEPROPERTYTABLE_H_ */
+#endif /* FRAMEPROPERTIES_H_ */
--- a/layout/base/GeckoRestyleManager.cpp
+++ b/layout/base/GeckoRestyleManager.cpp
@@ -814,20 +814,20 @@ GetPrevContinuationWithPossiblySameStyle
   // sibling of that is either another block-in-inline wrapper block box
   // or null.
   nsIFrame* prevContinuation = aFrame->GetPrevContinuation();
   if (!prevContinuation &&
       (aFrame->GetStateBits() & NS_FRAME_PART_OF_IBSPLIT)) {
     // 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());
     }
   }
 
   NS_ASSERTION(!prevContinuation ||
                prevContinuation->GetContent() == aFrame->GetContent(),
                "unexpected content mismatch");
 
   return prevContinuation;
@@ -1009,18 +1009,17 @@ GeckoRestyleManager::ReparentStyleContex
       // If this frame is part of an IB split, then the style context of
       // the next part of the split might be a child of our style context.
       // Reparent its style context just in case one of our ancestors
       // (split or not) hasn't done so already). It's not a problem to
       // reparent the same frame twice because the "if (newContext !=
       // 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);
         }
       }
 
       // do additional contexts
       int32_t contextIndex = 0;
       for (nsStyleContext* oldExtraContext;
@@ -3023,32 +3022,31 @@ ElementRestyler::ComputeStyleChangeFor(n
   // restyling the first two block-in-inline splits and no
   // continuations, and skipping everything else.  However, when we have
   // a style change targeted at an element inside a context where styles
   // vary between continuations (e.g., a style change on an element that
   // extends from inside a styled ::first-line to outside of that first
   // line), we might restyle more than that.
 
   nsPresContext* presContext = aFrame->PresContext();
-  FramePropertyTable* propTable = presContext->PropertyTable();
 
   TreeMatchContext treeMatchContext(true,
                                     nsRuleWalker::eRelevantLinkUnvisited,
                                     presContext->Document());
   Element* parent =
     content ? content->GetParentElementCrossingShadowRoot() : nullptr;
   treeMatchContext.InitAncestors(parent);
   nsTArray<nsCSSSelector*> selectorsForDescendants;
   selectorsForDescendants.AppendElements(
       aRestyleHintData.mSelectorsForDescendants);
   nsTArray<nsIContent*> visibleKidsOfHiddenElement;
   nsIFrame* nextIBSibling;
   for (nsIFrame* ibSibling = aFrame; ibSibling; ibSibling = nextIBSibling) {
     nextIBSibling =
-      GeckoRestyleManager::GetNextBlockInInlineSibling(propTable, ibSibling);
+      GeckoRestyleManager::GetNextBlockInInlineSibling(ibSibling);
 
     if (nextIBSibling) {
       // Don't allow some ib-split siblings to be processed with
       // RestyleResult::eStopWithStyleChange and others not.
       aRestyleHint |= eRestyle_Force;
     }
 
     // Outer loop over ib-split siblings
--- a/layout/base/OverflowChangedTracker.h
+++ b/layout/base/OverflowChangedTracker.h
@@ -108,22 +108,22 @@ public:
       if (entry->mChangeKind == CHILDREN_CHANGED) {
         // Need to union the overflow areas of the children.
         // Only update the parent if the overflow changes.
         overflowChanged = frame->UpdateOverflow();
       } else {
         // 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.
           nsOverflowAreas overflowCopy = *overflow;
           frame->FinishAndStoreOverflow(overflowCopy, frame->GetSize());
         } else {
           nsRect bounds(nsPoint(0, 0), frame->GetSize());
           nsOverflowAreas boundsOverflow;
--- a/layout/base/PresShell.cpp
+++ b/layout/base/PresShell.cpp
@@ -1378,29 +1378,16 @@ PresShell::Destroy()
   }
 
   CancelAllPendingReflows();
   CancelPostedReflowCallbacks();
 
   // 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(!mAutoWeakFrames && mWeakFrames.IsEmpty(),
                        "Weak frames alive after destroying FrameManager");
   while (mAutoWeakFrames) {
     mAutoWeakFrames->Clear(this);
   }
   nsTArray<WeakFrame*> toRemove(mWeakFrames.Count());
   for (auto iter = mWeakFrames.Iter(); !iter.Done(); iter.Next()) {
     toRemove.AppendElement(iter.Get()->GetKey());
@@ -2089,17 +2076,17 @@ PresShell::NotifyDestroyingFrame(nsIFram
     for (int32_t idx = mDirtyRoots.Length(); idx; ) {
       --idx;
       if (mDirtyRoots[idx] == aFrame) {
         mDirtyRoots.RemoveElementAt(idx);
       }
     }
 
     // Remove frame properties
-    mPresContext->NotifyDestroyingFrame(aFrame);
+    aFrame->DeleteAllProperties();
 
     if (aFrame == mCurrentEventFrame) {
       mCurrentEventContent = aFrame->GetContent();
       mCurrentEventFrame = nullptr;
     }
 
   #ifdef DEBUG
     if (aFrame == mDrawEventTargetFrame) {
--- a/layout/base/RestyleManager.cpp
+++ b/layout/base/RestyleManager.cpp
@@ -665,18 +665,16 @@ RestyleManager::DebugVerifyStyleTree(nsI
   }
   if (aFrame) {
     VerifyStyleTree(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.
  * Calls DoApplyRenderingChangeToTree on all aFrame's out-of-flow descendants
  * (following placeholders), if aChange has nsChangeHint_RepaintFrame.
  * aFrame should be some combination of nsChangeHint_SyncFrameView,
  * nsChangeHint_RepaintFrame, nsChangeHint_UpdateOpacityLayer and
  * nsChangeHint_SchedulePaint, nothing else.
@@ -801,20 +799,19 @@ RecomputePosition(nsIFrame* aFrame)
                      newOffsets.top == -newOffsets.bottom,
                      "ComputeRelativeOffsets should return valid results");
 
         // ReflowInput::ApplyRelativePositioning would work here, but
         // since we've already checked mPosition and aren't changing the frame's
         // 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));
       }
     }
 
     return true;
   }
@@ -1019,29 +1016,27 @@ RestyleManager::GetNearestAncestorFrame(
        ancestor && !ancestorFrame;
        ancestor = ancestor->GetParent()) {
     ancestorFrame = ancestor->GetPrimaryFrame();
   }
   return ancestorFrame;
 }
 
 /* static */ nsIFrame*
-RestyleManager::GetNextBlockInInlineSibling(FramePropertyTable* aPropTable,
-                                            nsIFrame* aFrame)
+RestyleManager::GetNextBlockInInlineSibling(nsIFrame* aFrame)
 {
   NS_ASSERTION(!aFrame->GetPrevContinuation(),
                "must start with the first continuation");
   // Might we have ib-split siblings?
   if (!(aFrame->GetStateBits() & NS_FRAME_PART_OF_IBSPLIT)) {
     // nothing more to do here
     return nullptr;
   }
 
-  return static_cast<nsIFrame*>
-    (aPropTable->Get(aFrame, nsIFrame::IBSplitSibling()));
+  return aFrame->GetProperty(nsIFrame::IBSplitSibling());
 }
 
 static void
 DoApplyRenderingChangeToTree(nsIFrame* aFrame,
                              nsChangeHint aChange)
 {
   NS_PRECONDITION(gInApplyRenderingChangeToTree,
                   "should only be called within ApplyRenderingChangeToTree");
@@ -1304,20 +1299,20 @@ RestyleManager::GetNextContinuationWithS
   // See GetPrevContinuationWithSameStyle about {ib} splits.
 
   nsIFrame* nextContinuation = aFrame->GetNextContinuation();
   if (!nextContinuation &&
       (aFrame->GetStateBits() & NS_FRAME_PART_OF_IBSPLIT)) {
     // 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());
     }
   }
 
   if (!nextContinuation) {
     return nullptr;
   }
 
   NS_ASSERTION(nextContinuation->GetContent() == aFrame->GetContent(),
@@ -1337,24 +1332,28 @@ RestyleManager::GetNextContinuationWithS
   return nextContinuation;
 }
 
 void
 RestyleManager::ProcessRestyledFrames(nsStyleChangeList& aChangeList)
 {
   NS_ASSERTION(!nsContentUtils::IsSafeToRunScript(),
                "Someone forgot a script blocker");
-  if (aChangeList.IsEmpty())
+  MOZ_ASSERT(!mDestroyedFrames);
+
+  if (aChangeList.IsEmpty()) {
     return;
+  }
+
+  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
   // scrollbars on the viewport, or upgrading the change hint to frame-reconstruct.
   for (nsStyleChangeData& data : aChangeList) {
     if (data.mHint & nsChangeHint_CSSOverflowChange) {
       data.mHint &= ~nsChangeHint_CSSOverflowChange;
       bool doReconstruct = true; // assume the worst
@@ -1412,25 +1411,16 @@ RestyleManager::ProcessRestyledFrames(ns
       }
     }
   }
 
   // Make sure to not rebuild quote or counter lists while we're
   // 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 (size_t i = 0; i < aChangeList.Length(); ++i) {
 
     // Collect and coalesce adjacent siblings for lazy frame construction.
     // Eventually it would be even better to make RecreateFramesForContent
     // accept a range and coalesce all adjacent reconstructs (bug 1344139).
     size_t lazyRangeStart = i;
@@ -1470,21 +1460,17 @@ RestyleManager::ProcessRestyledFrames(ns
     nsChangeHint hint = data.mHint;
     bool didReflowThisFrame = false;
 
     NS_ASSERTION(!(hint & nsChangeHint_AllReflowHints) ||
                  (hint & nsChangeHint_NeedReflow),
                  "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->HasSkippingBitCheck(frame, ChangeListProperty())) {
-      // Null out the pointer since the frame was already destroyed.
-      // This is important so we don't try to delete its
-      // ChangeListProperty() below.
-      mutable_data.mFrame = nullptr;
+    if (frame && mDestroyedFrames->Contains(frame)) {
       continue;
     }
 
     if (frame && frame->GetContent() != content) {
       // XXXbz this is due to image maps messing with the primary frame of
       // <area>s.  See bug 135040.  Remove this block once that's fixed.
       frame = nullptr;
       if (!(hint & nsChangeHint_ReconstructFrame)) {
@@ -1542,21 +1528,16 @@ RestyleManager::ProcessRestyledFrames(ns
         }
         // Don't remove NS_FRAME_MAY_BE_TRANSFORMED since it may still be
         // transformed by other means. It's OK to have the bit even if it's
         // not needed.
       }
     }
 
     if (hint & nsChangeHint_ReconstructFrame) {
-      // We're about to destroy data.mFrame, so null out the pointer.
-      // This is important so we don't try to delete its
-      // ChangeListProperty() below.
-      mutable_data.mFrame = nullptr;
-
       // If we ever start passing true here, be careful of restyles
       // that involve a reframe and animations.  In particular, if the
       // restyle we're processing here is an animation restyle, but
       // the style resolution we will do for the frame construction
       // happens async when we're not in an animation restyle already,
       // problems could arise.
       // We could also have problems with triggering of CSS transitions
       // on elements whose frames are reconstructed, since we depend on
@@ -1730,38 +1711,35 @@ RestyleManager::ProcessRestyledFrames(ns
       if ((hint & nsChangeHint_UpdateCursor) && !didUpdateCursor) {
         presContext->PresShell()->SynthesizeMouseMove(false);
         didUpdateCursor = true;
       }
     }
   }
 
   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->DeleteSkippingBitCheck(data.mFrame, ChangeListProperty());
-    }
+  mDestroyedFrames.reset(nullptr);
 
 #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();
       if (frame) {
         DebugVerifyStyleTree(frame);
       }
     } else if (!data.mFrame || !data.mFrame->IsViewportFrame()) {
       NS_WARNING("Unable to test style tree integrity -- no content node "
                  "(and not a viewport frame)");
     }
+  }
 #endif
-  }
 
   aChangeList.Clear();
 }
 
 /* static */ uint64_t
 RestyleManager::GetAnimationGenerationForFrame(nsIFrame* aFrame)
 {
   EffectSet* effectSet = EffectSet::GetEffectSet(aFrame);
--- a/layout/base/RestyleManager.h
+++ b/layout/base/RestyleManager.h
@@ -60,16 +60,21 @@ public:
   void FlushOverflowChangedTracker() {
     mOverflowChangedTracker.Flush();
   }
 
   // Should be called when a frame is going to be destroyed and
   // 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
   // ProcessRestyledFrames call in a view update batch and a script blocker.
   // This function does not call ProcessAttachedQueue() on the binding manager.
   // If the caller wants that to happen synchronously, it needs to handle that
   // itself.
   void ProcessRestyledFrames(nsStyleChangeList& aChangeList);
@@ -244,16 +249,21 @@ protected:
     return PresContext()->FrameConstructor();
   }
 
 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;
+
   const StyleBackendType mType;
 
 protected:
   // True if we're in the middle of a nsRefreshDriver refresh
   bool mInStyleRefresh;
 
   // The total number of animation flushes by this frame constructor.
   // Used to keep the layer and animation manager in sync.
@@ -264,17 +274,17 @@ protected:
   /**
    * These are protected static methods that help with the change hint
    * processing bits of the restyle managers.
    */
   static nsIFrame*
   GetNearestAncestorFrame(nsIContent* aContent);
 
   static nsIFrame*
-  GetNextBlockInInlineSibling(FramePropertyTable* aPropTable, nsIFrame* aFrame);
+  GetNextBlockInInlineSibling(nsIFrame* aFrame);
 
   /**
    * Get the next continuation or similar ib-split sibling (assuming
    * block/inline alternation), conditionally on it having the same style.
    *
    * Since this is used when deciding to copy the new style context, it
    * takes as an argument the old style context to check if the style is
    * the same.  When it is used in other contexts (i.e., where the next
--- a/layout/base/moz.build
+++ b/layout/base/moz.build
@@ -29,17 +29,17 @@ if CONFIG['MOZ_DEBUG']:
     UNIFIED_SOURCES += [
         'nsAutoLayoutPhase.cpp',
     ]
 
 XPIDL_MODULE = 'layout_base'
 
 EXPORTS += [
     'CaretAssociationHint.h',
-    'FramePropertyTable.h',
+    'FrameProperties.h',
     'LayoutLogging.h',
     'nsArenaMemoryStats.h',
     'nsBidi.h',
     'nsBidiPresUtils.h',
     'nsCaret.h',
     'nsChangeHint.h',
     'nsCompatibility.h',
     'nsCSSFrameConstructor.h',
@@ -82,17 +82,17 @@ EXPORTS.mozilla += [
     'ShapeUtils.h',
     'StaticPresData.h',
 ]
 
 UNIFIED_SOURCES += [
     'AccessibleCaret.cpp',
     'AccessibleCaretEventHub.cpp',
     'AccessibleCaretManager.cpp',
-    'FramePropertyTable.cpp',
+    'FrameProperties.cpp',
     'GeckoRestyleManager.cpp',
     'GeometryUtils.cpp',
     'LayoutLogging.cpp',
     'MobileViewportManager.cpp',
     'nsBidiPresUtils.cpp',
     'nsCaret.cpp',
     'nsCounterManager.cpp',
     'nsCSSColorUtils.cpp',
--- a/layout/base/nsBidiPresUtils.cpp
+++ b/layout/base/nsBidiPresUtils.cpp
@@ -790,17 +790,16 @@ nsBidiPresUtils::ResolveParagraph(BidiPa
   int32_t     frameIndex     = -1;  // index to the frames in mLogicalFrames
   int32_t     frameCount     = aBpd->FrameCount();
   int32_t     contentOffset  = 0;   // offset of current frame in its content node
   bool        isTextFrame    = false;
   nsIFrame*   frame = nullptr;
   nsIContent* content = nullptr;
   int32_t     contentTextLength = 0;
 
-  FramePropertyTable* propTable = aBpd->mPresContext->PropertyTable();
   nsLineBox* currentLine = nullptr;
   
 #ifdef DEBUG
 #ifdef NOISY_BIDI
   printf("Before Resolve(), mCurrentBlock=%p, mBuffer='%s', frameCount=%d, runCount=%d\n",
          (void*)aBpd->mCurrentBlock, NS_ConvertUTF16toUTF8(aBpd->mBuffer).get(), frameCount, runCount);
 #ifdef REALLY_NOISY_BIDI
   printf(" block frame tree=:\n");
@@ -846,17 +845,17 @@ nsBidiPresUtils::ResolveParagraph(BidiPa
     if (precedingControl >= embeddingLevel ||
         precedingControl >= lastEmbedingLevel) {
       bidiData.precedingControl = kBidiLevelNone;
     } else {
       bidiData.precedingControl = precedingControl;
     }
     precedingControl = kBidiLevelNone;
     lastEmbedingLevel = embeddingLevel;
-    propTable->Set(frame, nsIFrame::BidiDataProperty(), bidiData);
+    frame->SetProperty(nsIFrame::BidiDataProperty(), bidiData);
   };
 
   for (; ;) {
     if (fragmentLength <= 0) {
       // Get the next frame from mLogicalFrames
       if (++frameIndex >= frameCount) {
         break;
       }
@@ -1315,17 +1314,17 @@ nsBidiPresUtils::ChildListMayRequireBidi
     }
 
     if (IsBidiLeaf(frame)) {
       if (frame->IsTextFrame()) {
         // If the frame already has a BidiDataProperty, we know we need to
         // perform bidi resolution (even if no bidi content is NOW present --
         // we might need to remove the property set by a previous reflow, if
         // content has changed; see bug 1366623).
-        if (frame->Properties().Has(nsIFrame::BidiDataProperty())) {
+        if (frame->HasProperty(nsIFrame::BidiDataProperty())) {
           return true;
         }
 
         // Check whether the text frame has any RTL characters; if so, bidi
         // resolution will be needed.
         nsIContent* content = frame->GetContent();
         if (content != *aCurrContent) {
           *aCurrContent = content;
@@ -1877,17 +1876,17 @@ nsBidiPresUtils::RemoveBidiContinuation(
 {
   FrameBidiData bidiData = aFrame->GetBidiData();
   bidiData.precedingControl = kBidiLevelNone;
   for (int32_t index = aFirstIndex + 1; index <= aLastIndex; index++) {
     nsIFrame* frame = aBpd->FrameAt(index);
     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();
         if (prev) {
           MakeContinuationFluid(prev, frame);
           frame = frame->GetParent();
         } else {
           break;
--- a/layout/base/nsCSSFrameConstructor.cpp
+++ b/layout/base/nsCSSFrameConstructor.cpp
@@ -516,30 +516,28 @@ IsFramePartOfIBSplit(nsIFrame* aFrame)
 }
 
 static nsContainerFrame* GetIBSplitSibling(nsIFrame* aFrame)
 {
   NS_PRECONDITION(IsFramePartOfIBSplit(aFrame), "Shouldn't call this");
 
   // 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)
 {
   NS_PRECONDITION(IsFramePartOfIBSplit(aFrame), "Shouldn't call this");
 
   // 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*
 GetLastIBSplitSibling(nsIFrame* aFrame, bool aReturnEmptyTrailingInline)
 {
   for (nsIFrame *frame = aFrame, *next; ; frame = next) {
     next = GetIBSplitSibling(frame);
     if (!next ||
@@ -550,17 +548,17 @@ GetLastIBSplitSibling(nsIFrame* aFrame, 
       return static_cast<nsContainerFrame*>(frame);
     }
   }
   NS_NOTREACHED("unreachable code");
   return nullptr;
 }
 
 static void
-SetFrameIsIBSplit(nsContainerFrame* aFrame, nsIFrame* aIBSplitSibling)
+SetFrameIsIBSplit(nsContainerFrame* aFrame, nsContainerFrame* aIBSplitSibling)
 {
   NS_PRECONDITION(aFrame, "bad args!");
 
   // We should be the only continuation
   NS_ASSERTION(!aFrame->GetPrevContinuation(),
                "assigning ib-split sibling to other than first continuation!");
   NS_ASSERTION(!aFrame->GetNextContinuation() ||
                IsFramePartOfIBSplit(aFrame->GetNextContinuation()),
@@ -571,19 +569,18 @@ SetFrameIsIBSplit(nsContainerFrame* aFra
 
   if (aIBSplitSibling) {
     NS_ASSERTION(!aIBSplitSibling->GetPrevContinuation(),
                  "assigning something other than the first continuation as the "
                  "ib-split sibling");
 
     // 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);
   }
 }
 
 static nsIFrame*
 GetIBContainingBlockFor(nsIFrame* aFrame)
 {
   NS_PRECONDITION(IsFramePartOfIBSplit(aFrame),
                   "GetIBContainingBlockFor() should only be called on known IB frames");
@@ -6171,21 +6168,21 @@ static void
 AddGenConPseudoToFrame(nsIFrame* aOwnerFrame, nsIContent* aContent)
 {
   NS_ASSERTION(nsLayoutUtils::IsFirstContinuationOrIBSplitSibling(aOwnerFrame),
                "property should only be set on first continuation/ib-sibling");
 
   // FIXME(emilio): Remove this property, and use the frame of the generated
   // content itself to tear the content down? It should be quite simpler.
 
-  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);
 }
 
 /**
  * Return true if the frame construction item pointed to by aIter will
  * create a frame adjacent to a line boundary in the frame tree, and that
  * line boundary is induced by a content node adjacent to the frame's
@@ -6431,17 +6428,17 @@ AdjustAppendParentForAfterContent(nsFram
                                   nsIContent* aChild,
                                   nsIFrame** aAfterFrame)
 {
   // If the parent frame has any pseudo-elements or aContainer is a
   // display:contents node then we need to walk through the child
   // frames to find the first one that is either a ::after frame for an
   // ancestor of aChild or a frame that is for a node later in the
   // document than aChild and return that in aAfterFrame.
-  if (aParentFrame->Properties().Get(nsIFrame::GenConProperty()) ||
+  if (aParentFrame->GetProperty(nsIFrame::GenConProperty()) ||
       nsLayoutUtils::HasPseudoStyle(aContainer, aParentFrame->StyleContext(),
                                     CSSPseudoElementType::after,
                                     aParentFrame->PresContext()) ||
       aFrameManager->GetDisplayContentsStyleFor(aContainer)) {
     nsIFrame* afterFrame = nullptr;
     nsContainerFrame* parent =
       static_cast<nsContainerFrame*>(aParentFrame->LastContinuation());
     bool done = false;
@@ -8572,17 +8569,17 @@ nsCSSFrameConstructor::ContentRemoved(ns
     MOZ_ASSERT(ancestor, "display: contents on the root?");
     while (!ancestor->GetPrimaryFrame()) {
       // FIXME(emilio): Should this use the flattened tree parent instead?
       ancestor = ancestor->GetParent();
       MOZ_ASSERT(ancestor, "we can't have a display: contents subtree root!");
     }
 
     nsIFrame* ancestorFrame = ancestor->GetPrimaryFrame();
-    if (ancestorFrame->Properties().Get(nsIFrame::GenConProperty())) {
+    if (ancestorFrame->GetProperty(nsIFrame::GenConProperty())) {
       *aDidReconstruct = true;
       LAYOUT_PHASE_TEMP_EXIT();
 
       // XXXmats Can we recreate frames only for the ::after/::before content?
       // XXX Perhaps even only those that belong to the aChild sub-tree?
       RecreateFramesForContent(ancestor, false, aFlags, aDestroyedFramesFor);
       LAYOUT_PHASE_TEMP_REENTER();
       return;
--- a/layout/base/nsLayoutUtils.cpp
+++ b/layout/base/nsLayoutUtils.cpp
@@ -2112,23 +2112,23 @@ nsLayoutUtils::IsFixedPosFrameInDisplayP
   return ViewportHasDisplayPort(aFrame->PresContext());
 }
 
 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
 nsIScrollableFrame*
 nsLayoutUtils::GetNearestScrollableFrameForDirection(nsIFrame* aFrame,
                                                      Direction aDirection)
 {
   NS_ASSERTION(aFrame, "GetNearestScrollableFrameForDirection expects a non-null frame");
@@ -4517,66 +4517,66 @@ nsLayoutUtils::GetNextContinuationOrIBSp
   if (result)
     return result;
 
   if ((aFrame->GetStateBits() & NS_FRAME_PART_OF_IBSPLIT) != 0) {
     // We only store the ib-split sibling annotation with the first
     // 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;
 }
 
 nsIFrame*
 nsLayoutUtils::FirstContinuationOrIBSplitSibling(nsIFrame *aFrame)
 {
   nsIFrame *result = aFrame->FirstContinuation();
   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;
     }
   }
 
   return result;
 }
 
 nsIFrame*
 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;
     }
   }
 
   result = result->LastContinuation();
 
   return result;
 }
 
 bool
 nsLayoutUtils::IsFirstContinuationOrIBSplitSibling(nsIFrame *aFrame)
 {
   if (aFrame->GetPrevContinuation()) {
     return false;
   }
   if ((aFrame->GetStateBits() & NS_FRAME_PART_OF_IBSPLIT) &&
-      aFrame->Properties().Get(nsIFrame::IBSplitPrevSibling())) {
+      aFrame->GetProperty(nsIFrame::IBSplitPrevSibling())) {
     return false;
   }
 
   return true;
 }
 
 bool
 nsLayoutUtils::IsViewportScrollbarFrame(nsIFrame* aFrame)
--- a/layout/base/nsPresContext.cpp
+++ b/layout/base/nsPresContext.cpp
@@ -2954,18 +2954,17 @@ nsPresContext::GetPrimaryFrameFor(nsICon
     return aContent->GetPrimaryFrame();
   }
   return nullptr;
 }
 
 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.
 }
 
 bool
 nsPresContext::IsRootContentDocument() const
 {
--- a/layout/base/nsPresContext.h
+++ b/layout/base/nsPresContext.h
@@ -19,17 +19,16 @@
 #include "nsFont.h"
 #include "gfxFontConstants.h"
 #include "nsIAtom.h"
 #include "nsIObserver.h"
 #include "nsITimer.h"
 #include "nsCRT.h"
 #include "nsIWidgetListener.h"
 #include "nsLanguageAtomService.h"
-#include "FramePropertyTable.h"
 #include "nsGkAtoms.h"
 #include "nsCycleCollectionParticipant.h"
 #include "nsChangeHint.h"
 #include <algorithm>
 // This also pulls in gfxTypes.h, which we cannot include directly.
 #include "gfxRect.h"
 #include "nsTArray.h"
 #include "nsAutoPtr.h"
@@ -121,17 +120,16 @@ enum nsLayoutPhase {
 class nsRootPresContext;
 
 // An interface for presentation contexts. Presentation contexts are
 // objects that provide an outer context for a presentation shell.
 
 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;
 
   NS_DECL_CYCLE_COLLECTING_ISUPPORTS
   NS_DECL_NSIOBSERVER
   NS_DECL_CYCLE_COLLECTION_CLASS(nsPresContext)
   MOZ_DECLARE_WEAKREFERENCE_TYPENAME(nsPresContext)
@@ -887,19 +885,16 @@ public:
    */
   void SysColorChanged();
 
   /** Printing methods below should only be used for Medium() == print **/
   void SetPrintSettings(nsIPrintSettings *aPrintSettings);
 
   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.
   */
   bool EnsureVisible();
 
 #ifdef MOZ_REFLOW_PERF
   void CountReflows(const char * aName,
@@ -1114,21 +1109,16 @@ public:
   /**
    * If we have a presshell, and if the given content's current
    * document is the same as our presshell's document, return the
    * content's primary frame.  Otherwise, return null.  Only use this
    * if you care about which presshell the primary frame is in.
    */
   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);
   }
 
   bool IsRootContentDocument() const;
 
   bool HadNonBlankPaint() const {
@@ -1353,18 +1343,16 @@ protected:
 
   nsCOMPtr<nsITheme> mTheme;
   nsLanguageAtomService* mLangService;
   nsCOMPtr<nsIPrintSettings> mPrintSettings;
   nsCOMPtr<nsITimer>    mPrefChangedTimer;
 
   mozilla::UniquePtr<nsBidi> mBidiEngine;
 
-  FramePropertyTable    mPropertyTable;
-
   struct TransactionInvalidations {
     uint64_t mTransactionId;
     nsTArray<nsRect> mInvalidations;
   };
   AutoTArray<TransactionInvalidations, 4> mTransactions;
 
   // text performance metrics
   nsAutoPtr<gfxTextPerfMetrics>   mTextPerf;
--- a/layout/forms/nsTextControlFrame.cpp
+++ b/layout/forms/nsTextControlFrame.cpp
@@ -119,20 +119,20 @@ nsTextControlFrame::~nsTextControlFrame(
 {
 }
 
 void
 nsTextControlFrame::DestroyFrom(nsIFrame* aDestructRoot)
 {
   mScrollEvent.Revoke();
 
-  EditorInitializer* initializer = Properties().Get(TextControlInitializer());
+  EditorInitializer* initializer = GetProperty(TextControlInitializer());
   if (initializer) {
     initializer->Revoke();
-    Properties().Delete(TextControlInitializer());
+    DeleteProperty(TextControlInitializer());
   }
 
   // Unbind the text editor state object from the frame.  The editor will live
   // on, but things like controllers will be released.
   nsCOMPtr<nsITextControlElement> txtCtrl = do_QueryInterface(GetContent());
   NS_ASSERTION(txtCtrl, "Content not a text control element");
   txtCtrl->UnbindFromFrame(this);
 
@@ -389,22 +389,22 @@ nsTextControlFrame::CreateAnonymousConte
       // so are input text controls with spellcheck=true
       element->GetSpellcheck(&initEagerly);
     }
   }
 
   if (initEagerly) {
     NS_ASSERTION(!nsContentUtils::IsSafeToRunScript(),
                  "Someone forgot a script blocker?");
-    EditorInitializer* initializer = Properties().Get(TextControlInitializer());
+    EditorInitializer* initializer = GetProperty(TextControlInitializer());
     if (initializer) {
       initializer->Revoke();
     }
     initializer = new EditorInitializer(this);
-    Properties().Set(TextControlInitializer(),initializer);
+    SetProperty(TextControlInitializer(),initializer);
     nsContentUtils::AddScriptRunner(initializer);
   }
 
   return NS_OK;
 }
 
 void
 nsTextControlFrame::AppendAnonymousContentTo(nsTArray<nsIContent*>& aElements,
@@ -1126,26 +1126,26 @@ nsTextControlFrame::SetInitialChildList(
   // than descending from the root frame of the frame hierarchy.
   if (nsIFrame* first = PrincipalChildList().FirstChild()) {
     first->AddStateBits(NS_FRAME_REFLOW_ROOT);
 
     nsCOMPtr<nsITextControlElement> txtCtrl = do_QueryInterface(GetContent());
     NS_ASSERTION(txtCtrl, "Content not a text control element");
     txtCtrl->InitializeKeyboardEventListeners();
 
-    nsPoint* contentScrollPos = Properties().Get(ContentScrollPos());
+    nsPoint* contentScrollPos = GetProperty(ContentScrollPos());
     if (contentScrollPos) {
       // If we have a scroll pos stored to be passed to our anonymous
       // div, do it here!
       nsIStatefulFrame* statefulFrame = do_QueryFrame(first);
       NS_ASSERTION(statefulFrame, "unexpected type of frame for the anonymous div");
       nsPresState fakePresState;
       fakePresState.SetScrollState(*contentScrollPos);
       statefulFrame->RestoreState(&fakePresState);
-      Properties().Remove(ContentScrollPos());
+      RemoveProperty(ContentScrollPos());
       delete contentScrollPos;
     }
   }
 }
 
 void
 nsTextControlFrame::SetValueChanged(bool aValueChanged)
 {
@@ -1285,17 +1285,17 @@ nsTextControlFrame::RestoreState(nsPresS
     if (scrollStateFrame) {
       return scrollStateFrame->RestoreState(aState);
     }
   }
 
   // Most likely, we don't have our anonymous content constructed yet, which
   // would cause us to end up here.  In this case, we'll just store the scroll
   // pos ourselves, and forward it to the scroll frame later when it's created.
-  Properties().Set(ContentScrollPos(), new nsPoint(aState->GetScrollPosition()));
+  SetProperty(ContentScrollPos(), new nsPoint(aState->GetScrollPosition()));
   return NS_OK;
 }
 
 nsresult
 nsTextControlFrame::PeekOffset(nsPeekOffsetStruct *aPos)
 {
   return NS_ERROR_FAILURE;
 }
--- a/layout/forms/nsTextControlFrame.h
+++ b/layout/forms/nsTextControlFrame.h
@@ -316,17 +316,17 @@ private:
    *
    * XXXbz This function is slow.  Very slow.  Consider using
    * EnsureEditorInitialized() if you need that, and
    * nsITextControlElement::GetRootEditorNode on our content if you need that.
    */
   nsresult GetRootNodeAndInitializeEditor(nsIDOMElement **aRootElement);
 
   void FinishedInitializer() {
-    Properties().Delete(TextControlInitializer());
+    DeleteProperty(TextControlInitializer());
   }
 
 private:
   // Our first baseline, or NS_INTRINSIC_WIDTH_UNKNOWN if we have a pending
   // Reflow.
   nscoord mFirstBaseline;
 
   // these packed bools could instead use the high order bits on mState, saving 4 bytes 
--- a/layout/generic/ReflowInput.cpp
+++ b/layout/generic/ReflowInput.cpp
@@ -1010,47 +1010,48 @@ ReflowInput::ComputeRelativeOffsets(Writ
                                  position->mOffset.Get(blockStart));
 
     // Computed value for 'blockEnd' is minus the value of 'blockStart'
     offsets.BEnd(aWM) = -offsets.BStart(aWM);
   }
 
   // Convert the offsets to physical coordinates and store them on the frame
   aComputedOffsets = offsets.GetPhysicalMargin(aWM);
-  FrameProperties props = aFrame->Properties();
-  nsMargin* physicalOffsets = props.Get(nsIFrame::ComputedOffsetProperty());
+  nsMargin* physicalOffsets =
+    aFrame->GetProperty(nsIFrame::ComputedOffsetProperty());
   if (physicalOffsets) {
     *physicalOffsets = aComputedOffsets;
   } else {
-    props.Set(nsIFrame::ComputedOffsetProperty(),
-              new nsMargin(aComputedOffsets));
+    aFrame->SetProperty(nsIFrame::ComputedOffsetProperty(),
+                        new nsMargin(aComputedOffsets));
   }
 }
 
 /* static */ void
 ReflowInput::ApplyRelativePositioning(nsIFrame* aFrame,
                                             const nsMargin& aComputedOffsets,
                                             nsPoint* aPosition)
 {
   if (!aFrame->IsRelativelyPositioned()) {
-    NS_ASSERTION(!aFrame->Properties().Get(nsIFrame::NormalPositionProperty()),
+    NS_ASSERTION(!aFrame->GetProperty(nsIFrame::NormalPositionProperty()),
                  "We assume that changing the 'position' property causes "
                  "frame reconstruction.  If that ever changes, this code "
                  "should call "
-                 "props.Delete(nsIFrame::NormalPositionProperty())");
+                 "aFrame->DeleteProperty(nsIFrame::NormalPositionProperty())");
     return;
   }
 
   // Store the normal position
-  FrameProperties props = aFrame->Properties();
-  nsPoint* normalPosition = props.Get(nsIFrame::NormalPositionProperty());
+  nsPoint* normalPosition =
+    aFrame->GetProperty(nsIFrame::NormalPositionProperty());
   if (normalPosition) {
     *normalPosition = *aPosition;
   } else {
-    props.Set(nsIFrame::NormalPositionProperty(), new nsPoint(*aPosition));
+    aFrame->SetProperty(nsIFrame::NormalPositionProperty(),
+                        new nsPoint(*aPosition));
   }
 
   const nsStyleDisplay* display = aFrame->StyleDisplay();
   if (NS_STYLE_POSITION_RELATIVE == display->mPosition) {
     *aPosition += nsPoint(aComputedOffsets.left, aComputedOffsets.top);
   } else if (NS_STYLE_POSITION_STICKY == display->mPosition &&
              !aFrame->GetNextContinuation() &&
              !aFrame->GetPrevContinuation() &&
@@ -2503,30 +2504,30 @@ ReflowInput::InitConstraints(nsPresConte
           !alignCB->IsFlexOrGridContainer()) {
         CalculateBlockSideMargins(aFrameType);
       }
     }
   }
 }
 
 static void
-UpdateProp(FrameProperties& aProps,
+UpdateProp(nsIFrame* aFrame,
            const FramePropertyDescriptor<nsMargin>* aProperty,
            bool aNeeded,
            nsMargin& aNewValue)
 {
   if (aNeeded) {
-    nsMargin* propValue = aProps.Get(aProperty);
+    nsMargin* propValue = aFrame->GetProperty(aProperty);
     if (propValue) {
       *propValue = aNewValue;
     } else {
-      aProps.Set(aProperty, new nsMargin(aNewValue));
+      aFrame->SetProperty(aProperty, new nsMargin(aNewValue));
     }
   } else {
-    aProps.Delete(aProperty);
+    aFrame->DeleteProperty(aProperty);
   }
 }
 
 void
 SizeComputationInput::InitOffsets(WritingMode aWM,
                                   const LogicalSize& aPercentBasis,
                                   LayoutFrameType aFrameType,
                                   ReflowInputFlags aFlags,
@@ -2534,29 +2535,28 @@ SizeComputationInput::InitOffsets(Writin
                                   const nsMargin* aPadding,
                                   const nsStyleDisplay* aDisplay)
 {
   DISPLAY_INIT_OFFSETS(mFrame, this, aPercentBasis, aBorder, aPadding);
 
   // Since we are in reflow, we don't need to store these properties anymore
   // unless they are dependent on width, in which case we store the new value.
   nsPresContext *presContext = mFrame->PresContext();
-  FrameProperties props(presContext->PropertyTable(), mFrame);
-  props.Delete(nsIFrame::UsedBorderProperty());
+  mFrame->DeleteProperty(nsIFrame::UsedBorderProperty());
 
   // Compute margins from the specified margin style information. These
   // become the default computed values, and may be adjusted below
   // XXX fix to provide 0,0 for the top&bottom margins for
   // inline-non-replaced elements
   bool needMarginProp = ComputeMargin(aWM, aPercentBasis);
   // XXX We need to include 'auto' horizontal margins in this too!
   // ... but if we did that, we'd need to fix nsFrame::GetUsedMargin
   // to use it even when the margins are all zero (since sometimes
   // they get treated as auto)
-  ::UpdateProp(props, nsIFrame::UsedMarginProperty(), needMarginProp,
+  ::UpdateProp(mFrame, nsIFrame::UsedMarginProperty(), needMarginProp,
                ComputedPhysicalMargin());
 
 
   const nsStyleDisplay* disp = mFrame->StyleDisplayWithOptionalParam(aDisplay);
   bool isThemed = mFrame->IsThemed(disp);
   bool needPaddingProp;
   nsIntMargin widget;
   if (isThemed &&
@@ -2582,17 +2582,17 @@ SizeComputationInput::InitOffsets(Writin
     needPaddingProp = ComputePadding(aWM, aPercentBasis, aFrameType);
   }
 
   // Add [align|justify]-content:baseline padding contribution.
   typedef const FramePropertyDescriptor<SmallValueHolder<nscoord>>* Prop;
   auto ApplyBaselinePadding = [this, &needPaddingProp]
          (LogicalAxis aAxis, Prop aProp) {
     bool found;
-    nscoord val = mFrame->Properties().Get(aProp, &found);
+    nscoord val = mFrame->GetProperty(aProp, &found);
     if (found) {
       NS_ASSERTION(val != nscoord(0), "zero in this property is useless");
       WritingMode wm = GetWritingMode();
       LogicalSide side;
       if (val > 0) {
         side = MakeLogicalSide(aAxis, eLogicalEdgeStart);
       } else {
         side = MakeLogicalSide(aAxis, eLogicalEdgeEnd);
@@ -2655,17 +2655,17 @@ SizeComputationInput::InitOffsets(Writin
     // by the associated scrollframe, in which case we must not report
     // any padding or border.
     nsSize size(mFrame->GetSize());
     if (size.width == 0 || size.height == 0) {
       ComputedPhysicalPadding().SizeTo(0,0,0,0);
       ComputedPhysicalBorderPadding().SizeTo(0,0,0,0);
     }
   }
-  ::UpdateProp(props, nsIFrame::UsedPaddingProperty(), needPaddingProp,
+  ::UpdateProp(mFrame, nsIFrame::UsedPaddingProperty(), needPaddingProp,
                ComputedPhysicalPadding());
 }
 
 // This code enforces section 10.3.3 of the CSS2 spec for this formula:
 //
 // 'margin-left' + 'border-left-width' + 'padding-left' + 'width' +
 //   'padding-right' + 'border-right-width' + 'margin-right'
 //   = width of containing block 
--- a/layout/generic/RubyUtils.cpp
+++ b/layout/generic/RubyUtils.cpp
@@ -14,31 +14,31 @@
 using namespace mozilla;
 
 NS_DECLARE_FRAME_PROPERTY_SMALL_VALUE(ReservedISize, nscoord)
 
 /* static */ void
 RubyUtils::SetReservedISize(nsIFrame* aFrame, nscoord aISize)
 {
   MOZ_ASSERT(IsExpandableRubyBox(aFrame));
-  aFrame->Properties().Set(ReservedISize(), aISize);
+  aFrame->SetProperty(ReservedISize(), aISize);
 }
 
 /* static */ void
 RubyUtils::ClearReservedISize(nsIFrame* aFrame)
 {
   MOZ_ASSERT(IsExpandableRubyBox(aFrame));
-  aFrame->Properties().Remove(ReservedISize());
+  aFrame->RemoveProperty(ReservedISize());
 }
 
 /* static */ nscoord
 RubyUtils::GetReservedISize(nsIFrame* aFrame)
 {
   MOZ_ASSERT(IsExpandableRubyBox(aFrame));
-  return aFrame->Properties().Get(ReservedISize());
+  return aFrame->GetProperty(ReservedISize());
 }
 
 AutoRubyTextContainerArray::AutoRubyTextContainerArray(
   nsRubyBaseContainerFrame* aBaseContainer)
 {
   for (nsIFrame* frame = aBaseContainer->GetNextSibling();
        frame && frame->IsRubyTextContainerFrame();
        frame = frame->GetNextSibling()) {
--- a/layout/generic/StickyScrollContainer.cpp
+++ b/layout/generic/StickyScrollContainer.cpp
@@ -40,22 +40,22 @@ StickyScrollContainer::GetStickyScrollCo
     nsLayoutUtils::GetNearestScrollableFrame(aFrame->GetParent(),
       nsLayoutUtils::SCROLLABLE_SAME_DOC |
       nsLayoutUtils::SCROLLABLE_INCLUDE_HIDDEN);
   if (!scrollFrame) {
     // We might not find any, for instance in the case of
     // <html style="position: fixed">
     return nullptr;
   }
-  FrameProperties props = static_cast<nsIFrame*>(do_QueryFrame(scrollFrame))->
-    Properties();
-  StickyScrollContainer* s = props.Get(StickyScrollContainerProperty());
+  auto frame = static_cast<nsIFrame*>(do_QueryFrame(scrollFrame));
+  StickyScrollContainer* s =
+    frame->GetProperty(StickyScrollContainerProperty());
   if (!s) {
     s = new StickyScrollContainer(scrollFrame);
-    props.Set(StickyScrollContainerProperty(), s);
+    frame->SetProperty(StickyScrollContainerProperty(), s);
   }
   return s;
 }
 
 // static
 void
 StickyScrollContainer::NotifyReparentedFrameAcrossScrollFrameBoundary(nsIFrame* aFrame,
                                                                       nsIFrame* aOldParent)
@@ -64,19 +64,20 @@ StickyScrollContainer::NotifyReparentedF
     nsLayoutUtils::GetNearestScrollableFrame(aOldParent,
       nsLayoutUtils::SCROLLABLE_SAME_DOC |
       nsLayoutUtils::SCROLLABLE_INCLUDE_HIDDEN);
   if (!oldScrollFrame) {
     // XXX maybe aFrame has sticky descendants that can be sticky now, but
     // we aren't going to handle that.
     return;
   }
-  FrameProperties props = static_cast<nsIFrame*>(do_QueryFrame(oldScrollFrame))->
-    Properties();
-  StickyScrollContainer* oldSSC = props.Get(StickyScrollContainerProperty());
+
+  StickyScrollContainer* oldSSC =
+    static_cast<nsIFrame*>(do_QueryFrame(oldScrollFrame))->
+      GetProperty(StickyScrollContainerProperty());
   if (!oldSSC) {
     // aOldParent had no sticky descendants, so aFrame doesn't have any sticky
     // descendants, and we're done here.
     return;
   }
 
   auto i = oldSSC->mFrames.Length();
   while (i-- > 0) {
@@ -90,18 +91,17 @@ StickyScrollContainer::NotifyReparentedF
     }
   }
 }
 
 // static
 StickyScrollContainer*
 StickyScrollContainer::GetStickyScrollContainerForScrollFrame(nsIFrame* aFrame)
 {
-  FrameProperties props = aFrame->Properties();
-  return props.Get(StickyScrollContainerProperty());
+  return aFrame->GetProperty(StickyScrollContainerProperty());
 }
 
 static nscoord
 ComputeStickySideOffset(Side aSide, const nsStyleSides& aOffset,
                         nscoord aPercentBasis)
 {
   if (eStyleUnit_Auto == aOffset.GetUnit(aSide)) {
     return NS_AUTOOFFSET;
@@ -136,38 +136,37 @@ StickyScrollContainer::ComputeStickyOffs
   computedOffsets.right  = ComputeStickySideOffset(eSideRight, position->mOffset,
                                                    scrollContainerSize.width);
   computedOffsets.top    = ComputeStickySideOffset(eSideTop, position->mOffset,
                                                    scrollContainerSize.height);
   computedOffsets.bottom = ComputeStickySideOffset(eSideBottom, position->mOffset,
                                                    scrollContainerSize.height);
 
   // Store the offset
-  FrameProperties props = aFrame->Properties();
-  nsMargin* offsets = props.Get(nsIFrame::ComputedOffsetProperty());
+  nsMargin* offsets = aFrame->GetProperty(nsIFrame::ComputedOffsetProperty());
   if (offsets) {
     *offsets = computedOffsets;
   } else {
-    props.Set(nsIFrame::ComputedOffsetProperty(),
-              new nsMargin(computedOffsets));
+    aFrame->SetProperty(nsIFrame::ComputedOffsetProperty(),
+                        new nsMargin(computedOffsets));
   }
 }
 
 void
 StickyScrollContainer::ComputeStickyLimits(nsIFrame* aFrame, nsRect* aStick,
                                            nsRect* aContain) const
 {
   NS_ASSERTION(nsLayoutUtils::IsFirstContinuationOrIBSplitSibling(aFrame),
                "Can't sticky position individual continuations");
 
   aStick->SetRect(nscoord_MIN/2, nscoord_MIN/2, nscoord_MAX, nscoord_MAX);
   aContain->SetRect(nscoord_MIN/2, nscoord_MIN/2, nscoord_MAX, nscoord_MAX);
 
   const nsMargin* computedOffsets = 
-    aFrame->Properties().Get(nsIFrame::ComputedOffsetProperty());
+    aFrame->GetProperty(nsIFrame::ComputedOffsetProperty());
   if (!computedOffsets) {
     // We haven't reflowed the scroll frame yet, so offsets haven't been
     // computed. Bail.
     return;
   }
 
   nsIFrame* scrolledFrame = mScrollFrame->GetScrolledFrame();
   nsIFrame* cbFrame = aFrame->GetContainingBlock();
--- a/layout/generic/nsBlockFrame.cpp
+++ b/layout/generic/nsBlockFrame.cpp
@@ -323,40 +323,38 @@ nsBlockFrame::DestroyFrom(nsIFrame* aDes
   ClearLineCursor();
   DestroyAbsoluteFrames(aDestructRoot);
   mFloats.DestroyFramesFrom(aDestructRoot);
   nsPresContext* presContext = PresContext();
   nsIPresShell* shell = presContext->PresShell();
   nsLineBox::DeleteLineList(presContext, mLines, aDestructRoot,
                             &mFrames);
 
-  FramePropertyTable* props = presContext->PropertyTable();
-
   if (HasPushedFloats()) {
-    SafelyDestroyFrameListProp(aDestructRoot, shell, props,
+    SafelyDestroyFrameListProp(aDestructRoot, shell,
                                PushedFloatProperty());
     RemoveStateBits(NS_BLOCK_HAS_PUSHED_FLOATS);
   }
 
   // destroy overflow lines now
   FrameLines* overflowLines = RemoveOverflowLines();
   if (overflowLines) {
     nsLineBox::DeleteLineList(presContext, overflowLines->mLines,
                               aDestructRoot, &overflowLines->mFrames);
     delete overflowLines;
   }
 
   if (GetStateBits() & NS_BLOCK_HAS_OVERFLOW_OUT_OF_FLOWS) {
-    SafelyDestroyFrameListProp(aDestructRoot, shell, props,
+    SafelyDestroyFrameListProp(aDestructRoot, shell,
                                OverflowOutOfFlowsProperty());
     RemoveStateBits(NS_BLOCK_HAS_OVERFLOW_OUT_OF_FLOWS);
   }
 
   if (HasOutsideBullet()) {
-    SafelyDestroyFrameListProp(aDestructRoot, shell, props,
+    SafelyDestroyFrameListProp(aDestructRoot, shell,
                                OutsideBulletProperty());
     RemoveStateBits(NS_BLOCK_FRAME_HAS_OUTSIDE_BULLET);
   }
 
   nsContainerFrame::DestroyFrom(aDestructRoot);
 }
 
 /* virtual */ nsILineIterator*
@@ -1715,27 +1713,27 @@ nsBlockFrame::ComputeFinalSize(const Ref
     // Currently only used for grid items, but could be used in other contexts.
     // The FragStretchBSizeProperty is our expected non-fragmented block-size
     // we should stretch to (for align-self:stretch etc).  In some fragmentation
     // cases though, the last fragment (this frame since we're complete), needs
     // to have extra size applied because earlier fragments consumed too much of
     // our computed size due to overflowing their containing block.  (E.g. this
     // ensures we fill the last row when a multi-row grid item is fragmented).
     bool found;
-    nscoord bSize = Properties().Get(FragStretchBSizeProperty(), &found);
+    nscoord bSize = GetProperty(FragStretchBSizeProperty(), &found);
     if (found) {
       finalSize.BSize(wm) = std::max(bSize, finalSize.BSize(wm));
     }
   }
 
   // Clamp the content size to fit within the margin-box clamp size, if any.
   if (MOZ_UNLIKELY(aReflowInput.mFlags.mBClampMarginBoxMinSize) &&
       aState.mReflowStatus.IsComplete()) {
     bool found;
-    nscoord cbSize = Properties().Get(BClampMarginBoxMinSizeProperty(), &found);
+    nscoord cbSize = GetProperty(BClampMarginBoxMinSizeProperty(), &found);
     if (found) {
       auto marginBoxBSize = finalSize.BSize(wm) +
                             aReflowInput.ComputedLogicalMargin().BStartEnd(wm);
       auto overflow = marginBoxBSize - cbSize;
       if (overflow > 0) {
         auto contentBSize = finalSize.BSize(wm) - borderPadding.BStartEnd(wm);
         auto newContentBSize = std::max(nscoord(0), contentBSize - overflow);
         // XXXmats deal with percentages better somehow?
@@ -1743,21 +1741,20 @@ nsBlockFrame::ComputeFinalSize(const Ref
       }
     }
   }
 
   // Screen out negative block sizes --- can happen due to integer overflows :-(
   finalSize.BSize(wm) = std::max(0, finalSize.BSize(wm));
   *aBEndEdgeOfChildren = blockEndEdgeOfChildren;
 
-  FrameProperties properties = Properties();
   if (blockEndEdgeOfChildren != finalSize.BSize(wm) - borderPadding.BEnd(wm)) {
-    properties.Set(BlockEndEdgeOfChildrenProperty(), blockEndEdgeOfChildren);
+    SetProperty(BlockEndEdgeOfChildrenProperty(), blockEndEdgeOfChildren);
   } else {
-    properties.Delete(BlockEndEdgeOfChildrenProperty());
+    DeleteProperty(BlockEndEdgeOfChildrenProperty());
   }
 
   aMetrics.SetSize(wm, finalSize);
 
 #ifdef DEBUG_blocks
   if ((CRAZY_SIZE(aMetrics.Width()) || CRAZY_SIZE(aMetrics.Height())) &&
       !GetParent()->IsCrazySizeAssertSuppressed()) {
     ListTag(stdout);
@@ -1880,17 +1877,17 @@ nsBlockFrame::UnionChildOverflow(nsOverf
                                     kPrincipalList | kFloatList);
 }
 
 bool
 nsBlockFrame::ComputeCustomOverflow(nsOverflowAreas& aOverflowAreas)
 {
   bool found;
   nscoord blockEndEdgeOfChildren =
-    Properties().Get(BlockEndEdgeOfChildrenProperty(), &found);
+    GetProperty(BlockEndEdgeOfChildrenProperty(), &found);
   if (found) {
     ConsiderBlockEndEdgeOfChildren(GetWritingMode(),
                                    blockEndEdgeOfChildren, aOverflowAreas);
   }
 
   // Line cursor invariants depend on the overflow areas of the lines, so
   // we must clear the line cursor since those areas may have changed.
   ClearLineCursor();
@@ -5057,44 +5054,44 @@ nsBlockFrame::DrainPushedFloats()
 }
 
 nsBlockFrame::FrameLines*
 nsBlockFrame::GetOverflowLines() const
 {
   if (!HasOverflowLines()) {
     return nullptr;
   }
-  FrameLines* prop = Properties().Get(OverflowLinesProperty());
+  FrameLines* prop = GetProperty(OverflowLinesProperty());
   NS_ASSERTION(prop && !prop->mLines.empty() &&
                prop->mLines.front()->GetChildCount() == 0 ? prop->mFrames.IsEmpty() :
                  prop->mLines.front()->mFirstChild == prop->mFrames.FirstChild(),
                "value should always be stored and non-empty when state set");
   return prop;
 }
 
 nsBlockFrame::FrameLines*
 nsBlockFrame::RemoveOverflowLines()
 {
   if (!HasOverflowLines()) {
     return nullptr;
   }
-  FrameLines* prop = Properties().Remove(OverflowLinesProperty());
+  FrameLines* prop = RemoveProperty(OverflowLinesProperty());
   NS_ASSERTION(prop && !prop->mLines.empty() &&
                prop->mLines.front()->GetChildCount() == 0 ? prop->mFrames.IsEmpty() :
                  prop->mLines.front()->mFirstChild == prop->mFrames.FirstChild(),
                "value should always be stored and non-empty when state set");
   RemoveStateBits(NS_BLOCK_HAS_OVERFLOW_LINES);
   return prop;
 }
 
 void
 nsBlockFrame::DestroyOverflowLines()
 {
   NS_ASSERTION(HasOverflowLines(), "huh?");
-  FrameLines* prop = Properties().Remove(OverflowLinesProperty());
+  FrameLines* prop = RemoveProperty(OverflowLinesProperty());
   NS_ASSERTION(prop && prop->mLines.empty(),
                "value should always be stored but empty when destroying");
   RemoveStateBits(NS_BLOCK_HAS_OVERFLOW_LINES);
   delete prop;
 }
 
 // This takes ownership of aOverflowLines.
 // XXX We should allocate overflowLines from presShell arena!
@@ -5104,20 +5101,19 @@ nsBlockFrame::SetOverflowLines(FrameLine
   NS_ASSERTION(aOverflowLines, "null lines");
   NS_ASSERTION(!aOverflowLines->mLines.empty(), "empty lines");
   NS_ASSERTION(aOverflowLines->mLines.front()->mFirstChild ==
                aOverflowLines->mFrames.FirstChild(),
                "invalid overflow lines / frames");
   NS_ASSERTION(!(GetStateBits() & NS_BLOCK_HAS_OVERFLOW_LINES),
                "Overwriting existing overflow lines");
 
-  FrameProperties props = Properties();
   // Verify that we won't overwrite an existing overflow list
-  NS_ASSERTION(!props.Get(OverflowLinesProperty()), "existing overflow list");
-  props.Set(OverflowLinesProperty(), aOverflowLines);
+  NS_ASSERTION(!GetProperty(OverflowLinesProperty()), "existing overflow list");
+  SetProperty(OverflowLinesProperty(), aOverflowLines);
   AddStateBits(NS_BLOCK_HAS_OVERFLOW_LINES);
 }
 
 nsFrameList*
 nsBlockFrame::GetOverflowOutOfFlows() const
 {
   if (!(GetStateBits() & NS_BLOCK_HAS_OVERFLOW_OUT_OF_FLOWS)) {
     return nullptr;
@@ -5160,17 +5156,17 @@ nsBlockFrame::SetOverflowOutOfFlows(cons
 
 nsBulletFrame*
 nsBlockFrame::GetInsideBullet() const
 {
   if (!HasInsideBullet()) {
     return nullptr;
   }
   NS_ASSERTION(!HasOutsideBullet(), "invalid bullet state");
-  nsBulletFrame* frame = Properties().Get(InsideBulletProperty());
+  nsBulletFrame* frame = GetProperty(InsideBulletProperty());
   NS_ASSERTION(frame && frame->IsBulletFrame(), "bogus inside bullet frame");
   return frame;
 }
 
 nsBulletFrame*
 nsBlockFrame::GetOutsideBullet() const
 {
   nsFrameList* list = GetOutsideBulletList();
@@ -5180,57 +5176,55 @@ nsBlockFrame::GetOutsideBullet() const
 
 nsFrameList*
 nsBlockFrame::GetOutsideBulletList() const
 {
   if (!HasOutsideBullet()) {
     return nullptr;
   }
   NS_ASSERTION(!HasInsideBullet(), "invalid bullet state");
-  nsFrameList* list =
-    Properties().Get(OutsideBulletProperty());
+  nsFrameList* list = GetProperty(OutsideBulletProperty());
   NS_ASSERTION(list && list->GetLength() == 1 &&
                list->FirstChild()->IsBulletFrame(),
                "bogus outside bullet list");
   return list;
 }
 
 nsFrameList*
 nsBlockFrame::GetPushedFloats() const
 {
   if (!HasPushedFloats()) {
     return nullptr;
   }
-  nsFrameList* result =
-    Properties().Get(PushedFloatProperty());
+  nsFrameList* result = GetProperty(PushedFloatProperty());
   NS_ASSERTION(result, "value should always be non-empty when state set");
   return result;
 }
 
 nsFrameList*
 nsBlockFrame::EnsurePushedFloats()
 {
   nsFrameList *result = GetPushedFloats();
   if (result)
     return result;
 
   result = new (PresContext()->PresShell()) nsFrameList;
-  Properties().Set(PushedFloatProperty(), result);
+  SetProperty(PushedFloatProperty(), result);
   AddStateBits(NS_BLOCK_HAS_PUSHED_FLOATS);
 
   return result;
 }
 
 nsFrameList*
 nsBlockFrame::RemovePushedFloats()
 {
   if (!HasPushedFloats()) {
     return nullptr;
   }
-  nsFrameList *result = Properties().Remove(PushedFloatProperty());
+  nsFrameList *result = RemoveProperty(PushedFloatProperty());
   RemoveStateBits(NS_BLOCK_HAS_PUSHED_FLOATS);
   NS_ASSERTION(result, "value should always be non-empty when state set");
   return result;
 }
 
 //////////////////////////////////////////////////////////////////////
 // Frame list manipulation routines
 
@@ -5698,17 +5692,17 @@ nsBlockInFlowLineIterator::nsBlockInFlow
           break;
         }
         ++rline;
       }
     }
     if (mLine != line_end) {
       *aFoundValidLine = true;
       if (mLine != cursor) {
-        aFrame->Properties().Set(nsBlockFrame::LineCursorProperty(), mLine);
+        aFrame->SetProperty(nsBlockFrame::LineCursorProperty(), mLine);
       }
       return;
     }
   } else {
     for (mLine = aFrame->LinesBegin(); mLine != line_end; ++mLine) {
       if (mLine->Contains(child)) {
         *aFoundValidLine = true;
         return;
@@ -6848,56 +6842,54 @@ nsBlockFrame::AccessibleType()
 #endif
 
 void nsBlockFrame::ClearLineCursor()
 {
   if (!(GetStateBits() & NS_BLOCK_HAS_LINE_CURSOR)) {
     return;
   }
 
-  Properties().Delete(LineCursorProperty());
+  DeleteProperty(LineCursorProperty());
   RemoveStateBits(NS_BLOCK_HAS_LINE_CURSOR);
 }
 
 void nsBlockFrame::SetupLineCursor()
 {
   if (GetStateBits() & NS_BLOCK_HAS_LINE_CURSOR
       || mLines.empty()) {
     return;
   }
 
-  Properties().Set(LineCursorProperty(), mLines.front());
+  SetProperty(LineCursorProperty(), mLines.front());
   AddStateBits(NS_BLOCK_HAS_LINE_CURSOR);
 }
 
 nsLineBox* nsBlockFrame::GetFirstLineContaining(nscoord y)
 {
   if (!(GetStateBits() & NS_BLOCK_HAS_LINE_CURSOR)) {
     return nullptr;
   }
 
-  FrameProperties props = Properties();
-
-  nsLineBox* property = props.Get(LineCursorProperty());
+  nsLineBox* property = GetProperty(LineCursorProperty());
   LineIterator cursor = mLines.begin(property);
   nsRect cursorArea = cursor->GetVisualOverflowArea();
 
   while ((cursorArea.IsEmpty() || cursorArea.YMost() > y)
          && cursor != mLines.front()) {
     cursor = cursor.prev();
     cursorArea = cursor->GetVisualOverflowArea();
   }
   while ((cursorArea.IsEmpty() || cursorArea.YMost() <= y)
          && cursor != mLines.back()) {
     cursor = cursor.next();
     cursorArea = cursor->GetVisualOverflowArea();
   }
 
   if (cursor.get() != property) {
-    props.Set(LineCursorProperty(), cursor.get());
+    SetProperty(LineCursorProperty(), cursor.get());
   }
 
   return cursor.get();
 }
 
 /* virtual */ void
 nsBlockFrame::ChildIsDirty(nsIFrame* aChild)
 {
@@ -7102,21 +7094,21 @@ nsBlockFrame::CreateBulletFrameForListIt
   nsBulletFrame* bullet = new (shell) nsBulletFrame(kidSC);
   bullet->Init(mContent, this, nullptr);
 
   // If the list bullet frame should be positioned inside then add
   // it to the flow now.
   if (aListStylePositionInside) {
     nsFrameList bulletList(bullet, bullet);
     AddFrames(bulletList, nullptr);
-    Properties().Set(InsideBulletProperty(), bullet);
+    SetProperty(InsideBulletProperty(), bullet);
     AddStateBits(NS_BLOCK_FRAME_HAS_INSIDE_BULLET);
   } else {
     nsFrameList* bulletList = new (shell) nsFrameList(bullet, bullet);
-    Properties().Set(OutsideBulletProperty(), bulletList);
+    SetProperty(OutsideBulletProperty(), bulletList);
     AddStateBits(NS_BLOCK_FRAME_HAS_OUTSIDE_BULLET);
   }
 }
 
 bool
 nsBlockFrame::BulletIsEmpty() const
 {
   NS_ASSERTION(mContent->GetPrimaryFrame()->StyleDisplay()->mDisplay ==
--- a/layout/generic/nsBlockFrame.h
+++ b/layout/generic/nsBlockFrame.h
@@ -208,17 +208,17 @@ public:
     {
       if (!mOrigCursor) {
         mFrame->SetupLineCursor();
       }
     }
     ~AutoLineCursorSetup()
     {
       if (mOrigCursor) {
-        mFrame->Properties().Set(LineCursorProperty(), mOrigCursor);
+        mFrame->SetProperty(LineCursorProperty(), mOrigCursor);
       } else {
         mFrame->ClearLineCursor();
       }
     }
 
   private:
     nsBlockFrame* mFrame;
     nsLineBox* mOrigCursor;
@@ -410,17 +410,17 @@ protected:
 
 #ifdef DEBUG
   already_AddRefed<nsStyleContext> GetFirstLetterStyle(nsPresContext* aPresContext);
 #endif
 
   NS_DECLARE_FRAME_PROPERTY_WITHOUT_DTOR(LineCursorProperty, nsLineBox)
   bool HasLineCursor() { return GetStateBits() & NS_BLOCK_HAS_LINE_CURSOR; }
   nsLineBox* GetLineCursor() {
-    return HasLineCursor() ? Properties().Get(LineCursorProperty()) : nullptr;
+    return HasLineCursor() ? GetProperty(LineCursorProperty()) : nullptr;
   }
 
   nsLineBox* NewLineBox(nsIFrame* aFrame, bool aIsBlock) {
     return NS_NewLineBox(PresContext()->PresShell(), aFrame, aIsBlock);
   }
   nsLineBox* NewLineBox(nsLineBox* aFromLine, nsIFrame* aFrame, int32_t aCount) {
     return NS_NewLineBox(PresContext()->PresShell(), aFromLine, aFrame, aCount);
   }
--- a/layout/generic/nsBulletFrame.cpp
+++ b/layout/generic/nsBulletFrame.cpp
@@ -1306,32 +1306,32 @@ nsBulletFrame::GetLoadGroup(nsPresContex
 }
 
 float
 nsBulletFrame::GetFontSizeInflation() const
 {
   if (!HasFontSizeInflation()) {
     return 1.0f;
   }
-  return Properties().Get(FontSizeInflationProperty());
+  return GetProperty(FontSizeInflationProperty());
 }
 
 void
 nsBulletFrame::SetFontSizeInflation(float aInflation)
 {
   if (aInflation == 1.0f) {
     if (HasFontSizeInflation()) {
       RemoveStateBits(BULLET_FRAME_HAS_FONT_INFLATION);
-      Properties().Delete(FontSizeInflationProperty());
+      DeleteProperty(FontSizeInflationProperty());
     }
     return;
   }
 
   AddStateBits(BULLET_FRAME_HAS_FONT_INFLATION);
-  Properties().Set(FontSizeInflationProperty(), aInflation);
+  SetProperty(FontSizeInflationProperty(), aInflation);
 }
 
 already_AddRefed<imgIContainer>
 nsBulletFrame::GetImage() const
 {
   if (mImageRequest && StyleList()->GetListStyleImage()) {
     nsCOMPtr<imgIContainer> imageCon;
     mImageRequest->GetImage(getter_AddRefs(imageCon));
--- a/layout/generic/nsCanvasFrame.cpp
+++ b/layout/generic/nsCanvasFrame.cpp
@@ -359,34 +359,34 @@ nsDisplayCanvasBackgroundImage::Paint(ns
   if (IsSingleFixedPositionImage(aBuilder, bgClipRect, &destRect) &&
       aBuilder->IsPaintingToWindow() && !aBuilder->IsCompositingCheap() &&
       !dest->CurrentMatrix().HasNonIntegerTranslation()) {
     // Snap image rectangle to nearest pixel boundaries. This is the right way
     // to snap for this context, because we checked HasNonIntegerTranslation
     // above.
     destRect.Round();
     RefPtr<DrawTarget> dt = 
-      Frame()->Properties().Get(nsIFrame::CachedBackgroundImageDT());
+      Frame()->GetProperty(nsIFrame::CachedBackgroundImageDT());
     DrawTarget* destDT = dest->GetDrawTarget();
     if (dt) {
       BlitSurface(destDT, destRect, dt);
       return;
     }
 
     dt = destDT->CreateSimilarDrawTarget(IntSize::Ceil(destRect.width,
                                                        destRect.height),
                                          SurfaceFormat::B8G8R8A8);
     if (dt && dt->IsValid()) {
       RefPtr<gfxContext> ctx = gfxContext::CreateOrNull(dt);
       MOZ_ASSERT(ctx); // already checked draw target above
       ctx->SetMatrix(ctx->CurrentMatrix().Translate(-destRect.x, -destRect.y));
       nsRenderingContext context(ctx);
       PaintInternal(aBuilder, &context, bgClipRect, &bgClipRect);
       BlitSurface(dest->GetDrawTarget(), destRect, dt);
-      frame->Properties().Set(nsIFrame::CachedBackgroundImageDT(),
+      frame->SetProperty(nsIFrame::CachedBackgroundImageDT(),
                               dt.forget().take());
       return;
     }
   }
 #endif
   PaintInternal(aBuilder, aCtx, mVisibleRect, &bgClipRect);
 }
 
--- a/layout/generic/nsCanvasFrame.h
+++ b/layout/generic/nsCanvasFrame.h
@@ -193,17 +193,17 @@ public:
     : nsDisplayBackgroundImage(aInitData)
   {
   }
 
   virtual void Paint(nsDisplayListBuilder* aBuilder, nsRenderingContext* aCtx) override;
 
   virtual void NotifyRenderingChanged() override
   {
-    mFrame->Properties().Delete(nsIFrame::CachedBackgroundImageDT());
+    mFrame->DeleteProperty(nsIFrame::CachedBackgroundImageDT());
   }
  
   // We still need to paint a background color as well as an image for this item, 
   // so we can't support this yet.
   virtual bool SupportsOptimizingToImage() override { return false; }
 
   bool IsSingleFixedPositionImage(nsDisplayListBuilder* aBuilder,
                                   const nsRect& aClipRect,
--- a/layout/generic/nsContainerFrame.cpp
+++ b/layout/generic/nsContainerFrame.cpp
@@ -76,32 +76,32 @@ nsContainerFrame::SetInitialChildList(Ch
     MOZ_ASSERT(mFrames.IsEmpty(),
                "unexpected second call to SetInitialChildList");
     mFrames.SetFrames(aChildList);
   } else if (aListID == kBackdropList) {
     MOZ_ASSERT(StyleDisplay()->mTopLayer != NS_STYLE_TOP_LAYER_NONE,
                "Only top layer frames should have backdrop");
     MOZ_ASSERT(GetStateBits() & NS_FRAME_OUT_OF_FLOW,
                "Top layer frames should be out-of-flow");
-    MOZ_ASSERT(!Properties().Get(BackdropProperty()),
+    MOZ_ASSERT(!GetProperty(BackdropProperty()),
                "We shouldn't have setup backdrop frame list before");
 #ifdef DEBUG
     {
       nsIFrame* placeholder = aChildList.FirstChild();
       MOZ_ASSERT(aChildList.OnlyChild(), "Should have only one backdrop");
       MOZ_ASSERT(placeholder->IsPlaceholderFrame(),
                 "The frame to be stored should be a placeholder");
       MOZ_ASSERT(static_cast<nsPlaceholderFrame*>(placeholder)->
                 GetOutOfFlowFrame()->IsBackdropFrame(),
                 "The placeholder should points to a backdrop frame");
     }
 #endif
     nsFrameList* list =
       new (PresContext()->PresShell()) nsFrameList(aChildList);
-    Properties().Set(BackdropProperty(), list);
+    SetProperty(BackdropProperty(), list);
   } else {
     MOZ_ASSERT_UNREACHABLE("Unexpected child list");
   }
 }
 
 void
 nsContainerFrame::AppendFrames(ChildListID  aListID,
                                nsFrameList& aFrameList)
@@ -187,28 +187,27 @@ nsContainerFrame::DestroyAbsoluteFrames(
     GetAbsoluteContainingBlock()->DestroyFrames(this, aDestructRoot);
     MarkAsNotAbsoluteContainingBlock();
   }
 }
 
 void
 nsContainerFrame::SafelyDestroyFrameListProp(nsIFrame* aDestructRoot,
                                              nsIPresShell* aPresShell,
-                                             FramePropertyTable* aPropTable,
                                              FrameListPropertyDescriptor aProp)
 {
   // Note that the last frame can be removed through another route and thus
   // delete the property -- that's why we fetch the property again before
   // removing each frame rather than fetching it once and iterating the list.
-  while (nsFrameList* frameList = aPropTable->Get(this, aProp)) {
+  while (nsFrameList* frameList = GetProperty(aProp)) {
     nsIFrame* frame = frameList->RemoveFirstChild();
     if (MOZ_LIKELY(frame)) {
       frame->DestroyFrom(aDestructRoot);
     } else {
-      aPropTable->Remove(this, aProp);
+      RemoveProperty(aProp);
       frameList->Delete(aPresShell);
       return;
     }
   }
 }
 
 void
 nsContainerFrame::DestroyFrom(nsIFrame* aDestructRoot)
@@ -221,32 +220,31 @@ nsContainerFrame::DestroyFrom(nsIFrame* 
   DestroyAbsoluteFrames(aDestructRoot);
 
   // Destroy frames on the principal child list.
   mFrames.DestroyFramesFrom(aDestructRoot);
 
   // Destroy frames on the auxiliary frame lists and delete the lists.
   nsPresContext* pc = PresContext();
   nsIPresShell* shell = pc->PresShell();
-  FramePropertyTable* props = pc->PropertyTable();
-  SafelyDestroyFrameListProp(aDestructRoot, shell, props, OverflowProperty());
+  SafelyDestroyFrameListProp(aDestructRoot, shell, OverflowProperty());
 
   MOZ_ASSERT(IsFrameOfType(nsIFrame::eCanContainOverflowContainers) ||
-             !(props->Get(this, nsContainerFrame::OverflowContainersProperty()) ||
-               props->Get(this, nsContainerFrame::ExcessOverflowContainersProperty())),
+             !(GetProperty(nsContainerFrame::OverflowContainersProperty()) ||
+               GetProperty(nsContainerFrame::ExcessOverflowContainersProperty())),
              "this type of frame should't have overflow containers");
-  SafelyDestroyFrameListProp(aDestructRoot, shell, props,
+  SafelyDestroyFrameListProp(aDestructRoot, shell,
                              OverflowContainersProperty());
-  SafelyDestroyFrameListProp(aDestructRoot, shell, props,
+  SafelyDestroyFrameListProp(aDestructRoot, shell,
                              ExcessOverflowContainersProperty());
 
-  MOZ_ASSERT(!props->Get(this, BackdropProperty()) ||
+  MOZ_ASSERT(!GetProperty(BackdropProperty()) ||
              StyleDisplay()->mTopLayer != NS_STYLE_TOP_LAYER_NONE,
              "only top layer frame may have backdrop");
-  SafelyDestroyFrameListProp(aDestructRoot, shell, props, BackdropProperty());
+  SafelyDestroyFrameListProp(aDestructRoot, shell, BackdropProperty());
 
   nsSplittableFrame::DestroyFrom(aDestructRoot);
 }
 
 /////////////////////////////////////////////////////////////////////////////
 // Child frame enumeration
 
 const nsFrameList&
@@ -276,44 +274,42 @@ nsContainerFrame::GetChildList(ChildList
     }
     default:
       return nsSplittableFrame::GetChildList(aListID);
   }
 }
 
 static void
 AppendIfNonempty(const nsIFrame* aFrame,
-                 FramePropertyTable* aPropTable,
                  nsContainerFrame::FrameListPropertyDescriptor aProperty,
                  nsTArray<nsIFrame::ChildList>* aLists,
                  nsIFrame::ChildListID aListID)
 {
-  if (nsFrameList* list = aPropTable->Get(aFrame, aProperty)) {
+  if (nsFrameList* list = aFrame->GetProperty(aProperty)) {
     list->AppendIfNonempty(aLists, aListID);
   }
 }
 
 void
 nsContainerFrame::GetChildLists(nsTArray<ChildList>* aLists) const
 {
   mFrames.AppendIfNonempty(aLists, kPrincipalList);
-  FramePropertyTable* propTable = PresContext()->PropertyTable();
-  ::AppendIfNonempty(this, propTable, OverflowProperty(),
+  ::AppendIfNonempty(this, OverflowProperty(),
                      aLists, kOverflowList);
   if (IsFrameOfType(nsIFrame::eCanContainOverflowContainers)) {
-    ::AppendIfNonempty(this, propTable, OverflowContainersProperty(),
+    ::AppendIfNonempty(this, OverflowContainersProperty(),
                        aLists, kOverflowContainersList);
-    ::AppendIfNonempty(this, propTable, ExcessOverflowContainersProperty(),
+    ::AppendIfNonempty(this, ExcessOverflowContainersProperty(),
                        aLists, kExcessOverflowContainersList);
   }
   // Bypass BackdropProperty hashtable lookup for any in-flow frames
   // since frames in the top layer (only which can have backdrop) are
   // definitely out-of-flow.
   if (GetStateBits() & NS_FRAME_OUT_OF_FLOW) {
-    ::AppendIfNonempty(this, propTable, BackdropProperty(),
+    ::AppendIfNonempty(this, BackdropProperty(),
                        aLists, kBackdropList);
   }
   nsSplittableFrame::GetChildLists(aLists);
 }
 
 /////////////////////////////////////////////////////////////////////////////
 // Painting/Events
 
@@ -1199,62 +1195,60 @@ nsContainerFrame::DisplayOverflowContain
   if (overflowconts) {
     for (nsIFrame* frame : *overflowconts) {
       BuildDisplayListForChild(aBuilder, frame, aDirtyRect, aLists);
     }
   }
 }
 
 static bool
-TryRemoveFrame(nsIFrame* aFrame, FramePropertyTable* aPropTable,
+TryRemoveFrame(nsIFrame* aFrame,
                nsContainerFrame::FrameListPropertyDescriptor aProp,
                nsIFrame* aChildToRemove)
 {
-  nsFrameList* list = aPropTable->Get(aFrame, aProp);
+  nsFrameList* list = aFrame->GetProperty(aProp);
   if (list && list->StartRemoveFrame(aChildToRemove)) {
     // aChildToRemove *may* have been removed from this list.
     if (list->IsEmpty()) {
-      aPropTable->Remove(aFrame, aProp);
+      aFrame->RemoveProperty(aProp);
       list->Delete(aFrame->PresContext()->PresShell());
     }
     return true;
   }
   return false;
 }
 
 bool
 nsContainerFrame::MaybeStealOverflowContainerFrame(nsIFrame* aChild)
 {
   bool removed = false;
   if (MOZ_UNLIKELY(aChild->GetStateBits() & NS_FRAME_IS_OVERFLOW_CONTAINER)) {
-    FramePropertyTable* propTable = PresContext()->PropertyTable();
     // Try removing from the overflow container list.
-    removed = ::TryRemoveFrame(this, propTable, OverflowContainersProperty(),
+    removed = ::TryRemoveFrame(this, OverflowContainersProperty(),
                                aChild);
     if (!removed) {
       // It might be in the excess overflow container list.
-      removed = ::TryRemoveFrame(this, propTable,
+      removed = ::TryRemoveFrame(this,
                                  ExcessOverflowContainersProperty(),
                                  aChild);
     }
   }
   return removed;
 }
 
 nsresult
 nsContainerFrame::StealFrame(nsIFrame* aChild)
 {
 #ifdef DEBUG
   if (!mFrames.ContainsFrame(aChild)) {
     nsFrameList* list = GetOverflowFrames();
     if (!list || !list->ContainsFrame(aChild)) {
-      FramePropertyTable* propTable = PresContext()->PropertyTable();
-      list = propTable->Get(this, OverflowContainersProperty());
+      list = GetProperty(OverflowContainersProperty());
       if (!list || !list->ContainsFrame(aChild)) {
-        list = propTable->Get(this, ExcessOverflowContainersProperty());
+        list = GetProperty(ExcessOverflowContainersProperty());
         MOZ_ASSERT(list && list->ContainsFrame(aChild), "aChild isn't our child"
                    " or on a frame list not supported by StealFrame");
       }
     }
   }
 #endif
 
   bool removed = MaybeStealOverflowContainerFrame(aChild);
@@ -1400,44 +1394,44 @@ nsContainerFrame::DeleteNextInFlowChild(
 void
 nsContainerFrame::SetOverflowFrames(const nsFrameList& aOverflowFrames)
 {
   NS_PRECONDITION(aOverflowFrames.NotEmpty(), "Shouldn't be called");
 
   nsPresContext* pc = PresContext();
   nsFrameList* newList = new (pc->PresShell()) nsFrameList(aOverflowFrames);
 
-  pc->PropertyTable()->Set(this, OverflowProperty(), newList);
+  SetProperty(OverflowProperty(), newList);
 }
 
 nsFrameList*
 nsContainerFrame::GetPropTableFrames(
   FrameListPropertyDescriptor aProperty) const
 {
-  return PresContext()->PropertyTable()->Get(this, aProperty);
+  return GetProperty(aProperty);
 }
 
 nsFrameList*
 nsContainerFrame::RemovePropTableFrames(FrameListPropertyDescriptor aProperty)
 {
-  return PresContext()->PropertyTable()->Remove(this, aProperty);
+  return RemoveProperty(aProperty);
 }
 
 void
 nsContainerFrame::SetPropTableFrames(nsFrameList* aFrameList,
                                      FrameListPropertyDescriptor aProperty)
 {
   NS_PRECONDITION(aProperty && aFrameList, "null ptr");
   NS_PRECONDITION(
     (aProperty != nsContainerFrame::OverflowContainersProperty() &&
      aProperty != nsContainerFrame::ExcessOverflowContainersProperty()) ||
     IsFrameOfType(nsIFrame::eCanContainOverflowContainers),
     "this type of frame can't have overflow containers");
   MOZ_ASSERT(!GetPropTableFrames(aProperty));
-  PresContext()->PropertyTable()->Set(this, aProperty, aFrameList);
+  SetProperty(aProperty, aFrameList);
 }
 
 /**
  * Push aFromChild and its next siblings to the next-in-flow. Change the
  * geometric parent of each frame that's pushed. If there is no next-in-flow
  * the frames are placed on the overflow list (and the geometric parent is
  * left unchanged).
  *
@@ -2114,23 +2108,21 @@ nsOverflowContinuationTracker::BeginFini
 
 void
 nsOverflowContinuationTracker::EndFinish(nsIFrame* aChild)
 {
   if (!mOverflowContList) {
     return;
   }
   // Forget mOverflowContList if it was deleted.
-  nsPresContext* pc = aChild->PresContext();
-  FramePropertyTable* propTable = pc->PropertyTable();
-  nsFrameList* eoc = propTable->Get(
-    mParent, nsContainerFrame::ExcessOverflowContainersProperty());
+  nsFrameList* eoc = mParent->GetProperty
+    (nsContainerFrame::ExcessOverflowContainersProperty());
   if (eoc != mOverflowContList) {
-    nsFrameList* oc = static_cast<nsFrameList*>(propTable->Get(mParent,
-                        nsContainerFrame::OverflowContainersProperty()));
+    nsFrameList* oc = static_cast<nsFrameList*>(mParent->GetProperty
+                        (nsContainerFrame::OverflowContainersProperty()));
     if (oc != mOverflowContList) {
       // mOverflowContList was deleted
       mPrevOverflowCont = nullptr;
       mSentry = nullptr;
       mParent = aChild->GetParent();
       mOverflowContList = nullptr;
       SetupOverflowContList();
       return;
--- a/layout/generic/nsContainerFrame.h
+++ b/layout/generic/nsContainerFrame.h
@@ -19,19 +19,16 @@
 #define NS_FRAME_NO_MOVE_FRAME        (0x0002 | NS_FRAME_NO_MOVE_VIEW)
 #define NS_FRAME_NO_SIZE_VIEW         0x0004
 #define NS_FRAME_NO_VISIBILITY        0x0008
 // Only applies to ReflowChild; if true, don't delete the next-in-flow, even
 // if the reflow is fully complete.
 #define NS_FRAME_NO_DELETE_NEXT_IN_FLOW_CHILD 0x0010
 
 class nsOverflowContinuationTracker;
-namespace mozilla {
-class FramePropertyTable;
-} // namespace mozilla
 
 // Some macros for container classes to do sanity checking on
 // width/height/x/y values computed during reflow.
 // NOTE: AppUnitsPerCSSPixel value hardwired here to remove the
 // dependency on nsDeviceContext.h.  It doesn't matter if it's a
 // little off.
 #ifdef DEBUG
 // 10 million pixels, converted to app units. Note that this a bit larger
@@ -523,17 +520,17 @@ public:
   NS_DECLARE_FRAME_PROPERTY_FRAMELIST(OverflowContainersProperty)
   NS_DECLARE_FRAME_PROPERTY_FRAMELIST(ExcessOverflowContainersProperty)
   NS_DECLARE_FRAME_PROPERTY_FRAMELIST(BackdropProperty)
 
 #ifdef DEBUG
   // Use this to suppress the CRAZY_SIZE assertions.
   NS_DECLARE_FRAME_PROPERTY_SMALL_VALUE(DebugReflowingWithInfiniteISize, bool)
   bool IsCrazySizeAssertSuppressed() const {
-    return Properties().Get(DebugReflowingWithInfiniteISize());
+    return GetProperty(DebugReflowingWithInfiniteISize());
   }
 #endif
 
 protected:
   nsContainerFrame(nsStyleContext* aContext, ClassID aID)
     : nsSplittableFrame(aContext, aID)
   {}
 
@@ -694,17 +691,16 @@ protected:
 
   /**
    * Safely destroy the frames on the nsFrameList stored on aProp for this
    * frame then remove the property and delete the frame list.
    * Nothing happens if the property doesn't exist.
    */
   void SafelyDestroyFrameListProp(nsIFrame* aDestructRoot,
                                   nsIPresShell* aPresShell,
-                                  mozilla::FramePropertyTable* aPropTable,
                                   FrameListPropertyDescriptor aProp);
 
   // ==========================================================================
 
   // Helper used by Progress and Meter frames. Returns true if the bar should
   // be rendered vertically, based on writing-mode and -moz-orient properties.
   bool ResolvedOrientationIsVertical();
 
@@ -878,26 +874,26 @@ private:
   /* Tells us whether to pay attention to OOF frames or non-OOF frames */
   bool mWalkOOFFrames;
 };
 
 inline
 nsFrameList*
 nsContainerFrame::GetOverflowFrames() const
 {
-  nsFrameList* list = Properties().Get(OverflowProperty());
+  nsFrameList* list = GetProperty(OverflowProperty());
   NS_ASSERTION(!list || !list->IsEmpty(), "Unexpected empty overflow list");
   return list;
 }
 
 inline
 nsFrameList*
 nsContainerFrame::StealOverflowFrames()
 {
-  nsFrameList* list = Properties().Remove(OverflowProperty());
+  nsFrameList* list = RemoveProperty(OverflowProperty());
   NS_ASSERTION(!list || !list->IsEmpty(), "Unexpected empty overflow list");
   return list;
 }
 
 inline void
 nsContainerFrame::DestroyOverflowList()
 {
   nsFrameList* list = RemovePropTableFrames(OverflowProperty());
--- a/layout/generic/nsFlexContainerFrame.cpp
+++ b/layout/generic/nsFlexContainerFrame.cpp
@@ -1641,18 +1641,18 @@ NS_DECLARE_FRAME_PROPERTY_DELETABLE(Cach
                                     CachedMeasuringReflowResult);
 
 const CachedMeasuringReflowResult&
 nsFlexContainerFrame::MeasureAscentAndHeightForFlexItem(
   FlexItem& aItem,
   nsPresContext* aPresContext,
   ReflowInput& aChildReflowInput)
 {
-  const FrameProperties props = aItem.Frame()->Properties();
-  if (const auto* cachedResult = props.Get(CachedFlexMeasuringReflow())) {
+  if (const auto* cachedResult =
+        aItem.Frame()->GetProperty(CachedFlexMeasuringReflow())) {
     if (cachedResult->IsValidFor(aChildReflowInput)) {
       return *cachedResult;
     }
   }
 
   ReflowOutput childDesiredSize(aChildReflowInput);
   nsReflowStatus childReflowStatus;
 
@@ -1672,25 +1672,25 @@ nsFlexContainerFrame::MeasureAscentAndHe
   // Tell the child we're done with its initial reflow.
   // (Necessary for e.g. GetBaseline() to work below w/out asserting)
   FinishReflowChild(aItem.Frame(), aPresContext,
                     childDesiredSize, &aChildReflowInput, 0, 0, flags);
 
   auto result =
     new CachedMeasuringReflowResult(aChildReflowInput, childDesiredSize);
 
-  props.Set(CachedFlexMeasuringReflow(), result);
+  aItem.Frame()->SetProperty(CachedFlexMeasuringReflow(), result);
   return *result;
 }
 
 /* virtual */ void
 nsFlexContainerFrame::MarkIntrinsicISizesDirty()
 {
   for (nsIFrame* childFrame : mFrames) {
-    childFrame->Properties().Delete(CachedFlexMeasuringReflow());
+    childFrame->DeleteProperty(CachedFlexMeasuringReflow());
   }
   nsContainerFrame::MarkIntrinsicISizesDirty();
 }
 
 nscoord
 nsFlexContainerFrame::
   MeasureFlexItemContentHeight(nsPresContext* aPresContext,
                                FlexItem& aFlexItem,
@@ -4039,35 +4039,36 @@ private:
 // better to avoid property-table accesses.  So, where possible, we communicate
 // the resolved main-size to the child via modifying its reflow state directly,
 // instead of using this class.)
 class MOZ_RAII AutoFlexItemMainSizeOverride final
 {
 public:
   explicit AutoFlexItemMainSizeOverride(FlexItem& aItem
                                         MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
-    : mItemProps(aItem.Frame()->Properties())
+    : mItemFrame(aItem.Frame())
   {
     MOZ_GUARD_OBJECT_NOTIFIER_INIT;
 
-    MOZ_ASSERT(!mItemProps.Has(nsIFrame::FlexItemMainSizeOverride()),
+    MOZ_ASSERT(!mItemFrame->HasProperty(nsIFrame::FlexItemMainSizeOverride()),
                "FlexItemMainSizeOverride prop shouldn't be set already; "
                "it should only be set temporarily (& not recursively)");
     NS_ASSERTION(aItem.HasIntrinsicRatio(),
                  "This should only be needed for items with an aspect ratio");
 
-    mItemProps.Set(nsIFrame::FlexItemMainSizeOverride(), aItem.GetMainSize());
+    mItemFrame->SetProperty(nsIFrame::FlexItemMainSizeOverride(),
+                            aItem.GetMainSize());
   }
 
   ~AutoFlexItemMainSizeOverride() {
-    mItemProps.Remove(nsIFrame::FlexItemMainSizeOverride());
+    mItemFrame->RemoveProperty(nsIFrame::FlexItemMainSizeOverride());
   }
 
 private:
-  const FrameProperties mItemProps;
+  nsIFrame* mItemFrame;
   MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
 };
 
 void
 nsFlexContainerFrame::CalculatePackingSpace(uint32_t aNumThingsToPack,
                                             uint8_t aAlignVal,
                                             nscoord* aFirstSubjectOffset,
                                             uint32_t* aNumPackingSpacesRemaining,
@@ -4471,18 +4472,17 @@ nsFlexContainerFrame::MoveFlexItemToFina
   LogicalPoint& aFramePos,
   const nsSize& aContainerSize)
 {
   WritingMode outerWM = aReflowInput.GetWritingMode();
 
   // If item is relpos, look up its offsets (cached from prev reflow)
   LogicalMargin logicalOffsets(outerWM);
   if (NS_STYLE_POSITION_RELATIVE == aItem.Frame()->StyleDisplay()->mPosition) {
-    FrameProperties props = aItem.Frame()->Properties();
-    nsMargin* cachedOffsets = props.Get(nsIFrame::ComputedOffsetProperty());
+    nsMargin* cachedOffsets = aItem.Frame()->GetProperty(nsIFrame::ComputedOffsetProperty());
     MOZ_ASSERT(cachedOffsets,
                "relpos previously-reflowed frame should've cached its offsets");
     logicalOffsets = LogicalMargin(outerWM, *cachedOffsets);
   }
   ReflowInput::ApplyRelativePositioning(aItem.Frame(), outerWM,
                                               logicalOffsets, &aFramePos,
                                               aContainerSize);
   aItem.Frame()->SetPosition(outerWM, aFramePos, aContainerSize);
--- a/layout/generic/nsFloatManager.cpp
+++ b/layout/generic/nsFloatManager.cpp
@@ -314,40 +314,39 @@ nsFloatManager::CalculateRegionFor(Writi
 
 NS_DECLARE_FRAME_PROPERTY_DELETABLE(FloatRegionProperty, nsMargin)
 
 LogicalRect
 nsFloatManager::GetRegionFor(WritingMode aWM, nsIFrame* aFloat,
                              const nsSize& aContainerSize)
 {
   LogicalRect region = aFloat->GetLogicalRect(aWM, aContainerSize);
-  void* storedRegion = aFloat->Properties().Get(FloatRegionProperty());
+  void* storedRegion = aFloat->GetProperty(FloatRegionProperty());
   if (storedRegion) {
     nsMargin margin = *static_cast<nsMargin*>(storedRegion);
     region.Inflate(aWM, LogicalMargin(aWM, margin));
   }
   return region;
 }
 
 void
 nsFloatManager::StoreRegionFor(WritingMode aWM, nsIFrame* aFloat,
                                const LogicalRect& aRegion,
                                const nsSize& aContainerSize)
 {
   nsRect region = aRegion.GetPhysicalRect(aWM, aContainerSize);
   nsRect rect = aFloat->GetRect();
-  FrameProperties props = aFloat->Properties();
   if (region.IsEqualEdges(rect)) {
-    props.Delete(FloatRegionProperty());
+    aFloat->DeleteProperty(FloatRegionProperty());
   }
   else {
-    nsMargin* storedMargin = props.Get(FloatRegionProperty());
+    nsMargin* storedMargin = aFloat->GetProperty(FloatRegionProperty());
     if (!storedMargin) {
       storedMargin = new nsMargin();
-      props.Set(FloatRegionProperty(), storedMargin);
+      aFloat->SetProperty(FloatRegionProperty(), storedMargin);
     }
     *storedMargin = region - rect;
   }
 }
 
 nsresult
 nsFloatManager::RemoveTrailingRegions(nsIFrame* aFrameList)
 {
--- a/layout/generic/nsFontInflationData.cpp
+++ b/layout/generic/nsFontInflationData.cpp
@@ -1,17 +1,17 @@
 /* vim: set shiftwidth=2 tabstop=8 autoindent cindent expandtab: */
 /* 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/. */
 
 /* Per-block-formatting-context manager of font size inflation for pan and zoom UI. */
 
 #include "nsFontInflationData.h"
-#include "FramePropertyTable.h"
+#include "FrameProperties.h"
 #include "nsTextControlFrame.h"
 #include "nsListControlFrame.h"
 #include "nsComboboxControlFrame.h"
 #include "mozilla/ReflowInput.h"
 #include "nsTextFrameUtils.h"
 
 using namespace mozilla;
 using namespace mozilla::layout;
@@ -22,35 +22,34 @@ NS_DECLARE_FRAME_PROPERTY_DELETABLE(Font
 /* static */ nsFontInflationData*
 nsFontInflationData::FindFontInflationDataFor(const nsIFrame *aFrame)
 {
   // We have one set of font inflation data per block formatting context.
   const nsIFrame *bfc = FlowRootFor(aFrame);
   NS_ASSERTION(bfc->GetStateBits() & NS_FRAME_FONT_INFLATION_FLOW_ROOT,
                "should have found a flow root");
 
-  return bfc->Properties().Get(FontInflationDataProperty());
+  return bfc->GetProperty(FontInflationDataProperty());
 }
 
 /* static */ bool
 nsFontInflationData::UpdateFontInflationDataISizeFor(const ReflowInput& aReflowInput)
 {
   nsIFrame *bfc = aReflowInput.mFrame;
   NS_ASSERTION(bfc->GetStateBits() & NS_FRAME_FONT_INFLATION_FLOW_ROOT,
                "should have been given a flow root");
-  FrameProperties bfcProps(bfc->Properties());
-  nsFontInflationData *data = bfcProps.Get(FontInflationDataProperty());
+  nsFontInflationData *data = bfc->GetProperty(FontInflationDataProperty());
   bool oldInflationEnabled;
   nscoord oldNCAISize;
   if (data) {
     oldNCAISize = data->mNCAISize;
     oldInflationEnabled = data->mInflationEnabled;
   } else {
     data = new nsFontInflationData(bfc);
-    bfcProps.Set(FontInflationDataProperty(), data);
+    bfc->SetProperty(FontInflationDataProperty(), data);
     oldNCAISize = -1;
     oldInflationEnabled = true; /* not relevant */
   }
 
   data->UpdateISize(aReflowInput);
 
   if (oldInflationEnabled != data->mInflationEnabled)
     return true;
@@ -60,18 +59,17 @@ nsFontInflationData::UpdateFontInflation
 }
 
 /* static */ void
 nsFontInflationData::MarkFontInflationDataTextDirty(nsIFrame *aBFCFrame)
 {
   NS_ASSERTION(aBFCFrame->GetStateBits() & NS_FRAME_FONT_INFLATION_FLOW_ROOT,
                "should have been given a flow root");
 
-  FrameProperties bfcProps(aBFCFrame->Properties());
-  nsFontInflationData *data = bfcProps.Get(FontInflationDataProperty());
+  nsFontInflationData *data = aBFCFrame->GetProperty(FontInflationDataProperty());
   if (data) {
     data->MarkTextDirty();
   }
 }
 
 nsFontInflationData::nsFontInflationData(nsIFrame *aBFCFrame)
   : mBFCFrame(aBFCFrame)
   , mNCAISize(0)
--- a/layout/generic/nsFrame.cpp
+++ b/layout/generic/nsFrame.cpp
@@ -197,23 +197,22 @@ static void RefreshContentFrames(nsPresC
 
 #include "prenv.h"
 
 NS_DECLARE_FRAME_PROPERTY_DELETABLE(BoxMetricsProperty, nsBoxLayoutMetrics)
 
 static void
 InitBoxMetrics(nsIFrame* aFrame, bool aClear)
 {
-  FrameProperties props = aFrame->Properties();
   if (aClear) {
-    props.Delete(BoxMetricsProperty());
+    aFrame->DeleteProperty(BoxMetricsProperty());
   }
 
   nsBoxLayoutMetrics* metrics = new nsBoxLayoutMetrics();
-  props.Set(BoxMetricsProperty(), metrics);
+  aFrame->SetProperty(BoxMetricsProperty(), metrics);
 
   static_cast<nsFrame*>(aFrame)->nsFrame::MarkIntrinsicISizesDirty();
   metrics->mBlockAscent = 0;
   metrics->mLastSize.SizeTo(0, 0);
 }
 
 static bool
 IsXULBoxWrapped(const nsIFrame* aFrame)
@@ -328,44 +327,44 @@ NS_DECLARE_FRAME_PROPERTY_DELETABLE(Abso
 bool
 nsIFrame::HasAbsolutelyPositionedChildren() const {
   return IsAbsoluteContainer() && GetAbsoluteContainingBlock()->HasAbsoluteFrames();
 }
 
 nsAbsoluteContainingBlock*
 nsIFrame::GetAbsoluteContainingBlock() const {
   NS_ASSERTION(IsAbsoluteContainer(), "The frame is not marked as an abspos container correctly");
-  nsAbsoluteContainingBlock* absCB = Properties().Get(AbsoluteContainingBlockProperty());
+  nsAbsoluteContainingBlock* absCB = GetProperty(AbsoluteContainingBlockProperty());
   NS_ASSERTION(absCB, "The frame is marked as an abspos container but doesn't have the property");
   return absCB;
 }
 
 void
 nsIFrame::MarkAsAbsoluteContainingBlock()
 {
   MOZ_ASSERT(GetStateBits() & NS_FRAME_CAN_HAVE_ABSPOS_CHILDREN);
-  NS_ASSERTION(!Properties().Get(AbsoluteContainingBlockProperty()),
+  NS_ASSERTION(!GetProperty(AbsoluteContainingBlockProperty()),
                "Already has an abs-pos containing block property?");
   NS_ASSERTION(!HasAnyStateBits(NS_FRAME_HAS_ABSPOS_CHILDREN),
                "Already has NS_FRAME_HAS_ABSPOS_CHILDREN state bit?");
   AddStateBits(NS_FRAME_HAS_ABSPOS_CHILDREN);
-  Properties().Set(AbsoluteContainingBlockProperty(), new nsAbsoluteContainingBlock(GetAbsoluteListID()));
+  SetProperty(AbsoluteContainingBlockProperty(), new nsAbsoluteContainingBlock(GetAbsoluteListID()));
 }
 
 void
 nsIFrame::MarkAsNotAbsoluteContainingBlock()
 {
   NS_ASSERTION(!HasAbsolutelyPositionedChildren(), "Think of the children!");
-  NS_ASSERTION(Properties().Get(AbsoluteContainingBlockProperty()),
+  NS_ASSERTION(GetProperty(AbsoluteContainingBlockProperty()),
                "Should have an abs-pos containing block property");
   NS_ASSERTION(HasAnyStateBits(NS_FRAME_HAS_ABSPOS_CHILDREN),
                "Should have NS_FRAME_HAS_ABSPOS_CHILDREN state bit");
   MOZ_ASSERT(HasAnyStateBits(NS_FRAME_CAN_HAVE_ABSPOS_CHILDREN));
   RemoveStateBits(NS_FRAME_HAS_ABSPOS_CHILDREN);
-  Properties().Delete(AbsoluteContainingBlockProperty());
+  DeleteProperty(AbsoluteContainingBlockProperty());
 }
 
 bool
 nsIFrame::CheckAndClearPaintedState()
 {
   bool result = (GetStateBits() & NS_FRAME_PAINTED_THEBES);
   RemoveStateBits(NS_FRAME_PAINTED_THEBES);
   
@@ -737,42 +736,40 @@ nsFrame::DestroyFrom(nsIFrame* aDestruct
                  "this might mean we have a stray placeholder in the tree.");
     if (placeholder) {
       shell->FrameManager()->UnregisterPlaceholderFrame(placeholder);
       placeholder->SetOutOfFlowFrame(nullptr);
     }
   }
 
   // If we have any IB split siblings, clear their references to us.
-  // (Note: This has to happen before we call shell->NotifyDestroyingFrame,
-  // because that clears our Properties() table.)
+  // (Note: This has to happen before we clear our Properties() table.)
   if (mState & NS_FRAME_PART_OF_IBSPLIT) {
     // Delete previous sibling's reference to me.
-    nsIFrame* prevSib = Properties().Get(nsIFrame::IBSplitPrevSibling());
+    nsIFrame* prevSib = GetProperty(nsIFrame::IBSplitPrevSibling());
     if (prevSib) {
       NS_WARNING_ASSERTION(
-        this == prevSib->Properties().Get(nsIFrame::IBSplitSibling()),
+        this == prevSib->GetProperty(nsIFrame::IBSplitSibling()),
         "IB sibling chain is inconsistent");
-      prevSib->Properties().Delete(nsIFrame::IBSplitSibling());
+      prevSib->DeleteProperty(nsIFrame::IBSplitSibling());
     }
 
     // Delete next sibling's reference to me.
-    nsIFrame* nextSib = Properties().Get(nsIFrame::IBSplitSibling());
+    nsIFrame* nextSib = GetProperty(nsIFrame::IBSplitSibling());
     if (nextSib) {
       NS_WARNING_ASSERTION(
-        this == nextSib->Properties().Get(nsIFrame::IBSplitPrevSibling()),
+        this == nextSib->GetProperty(nsIFrame::IBSplitPrevSibling()),
         "IB sibling chain is inconsistent");
-      nextSib->Properties().Delete(nsIFrame::IBSplitPrevSibling());
+      nextSib->DeleteProperty(nsIFrame::IBSplitPrevSibling());
     }
   }
 
   bool isPrimaryFrame = (mContent && mContent->GetPrimaryFrame() == this);
   if (isPrimaryFrame) {
-    // This needs to happen before shell->NotifyDestroyingFrame because
-    // that clears our Properties() table.
+    // This needs to happen before we clear our Properties() table.
     ActiveLayerTracker::TransferActivityToContent(this, mContent);
 
     // Unfortunately, we need to do this for all frames being reframed
     // and not only those whose current style involves CSS transitions,
     // because what matters is whether the new style (not the old)
     // specifies CSS transitions.
     if (presContext->RestyleManager()->IsGecko()) {
       // stylo: ServoRestyleManager does not handle transitions yet, and when
@@ -793,19 +790,18 @@ nsFrame::DestroyFrom(nsIFrame* aDestruct
     RestyleManager::AnimationsWithDestroyedFrame* adf =
       presContext->RestyleManager()->GetAnimationsWithDestroyedFrame();
     // AnimationsWithDestroyedFrame only lives during the restyling process.
     if (adf) {
       adf->Put(mContent, mStyleContext);
     }
   }
 
-  // Disable visibility tracking. Note that we have to do this before calling
-  // NotifyDestroyingFrame(), which will clear frame properties and make us lose
-  // track of whether we were previously visible or not.
+  // Disable visibility tracking. Note that we have to do this before we clear
+  // frame properties and lose track of whether we were previously visible.
   // XXX(seth): It'd be ideal to assert that we're already marked nonvisible
   // here, but it's unfortunately tricky to guarantee in the face of things like
   // frame reconstruction induced by style changes.
   DisableVisibilityTracking();
 
   // Ensure that we're not in the approximately visible list anymore.
   PresContext()->GetPresShell()->RemoveFrameFromApproximatelyVisibleList(this);
 
@@ -821,16 +817,20 @@ nsFrame::DestroyFrom(nsIFrame* aDestruct
     view->Destroy();
   }
 
   // Make sure that our deleted frame can't be returned from GetPrimaryFrame()
   if (isPrimaryFrame) {
     mContent->SetPrimaryFrame(nullptr);
   }
 
+  // Delete all properties attached to the frame, to ensure any property
+  // destructors that need the frame pointer are handled properly.
+  DeleteAllProperties();
+
   // Must retrieve the object ID before calling destructors, so the
   // vtable is still valid.
   //
   // Note to future tweakers: having the method that returns the
   // object size call the destructor will not avoid an indirect call;
   // the compiler cannot devirtualize the call to the destructor even
   // if it's from a method defined in the same class.
 
@@ -939,42 +939,41 @@ nsFrame::DidSetStyleContext(nsStyleConte
   AddAndRemoveImageAssociations(this, oldLayers, newLayers);
 
   if (aOldStyleContext) {
     // If we detect a change on margin, padding or border, we store the old
     // values on the frame itself between now and reflow, so if someone
     // calls GetUsed(Margin|Border|Padding)() before the next reflow, we
     // can give an accurate answer.
     // We don't want to set the property if one already exists.
-    FrameProperties props = Properties();
     nsMargin oldValue(0, 0, 0, 0);
     nsMargin newValue(0, 0, 0, 0);
     const nsStyleMargin* oldMargin = aOldStyleContext->PeekStyleMargin();
     if (oldMargin && oldMargin->GetMargin(oldValue)) {
       if ((!StyleMargin()->GetMargin(newValue) || oldValue != newValue) &&
-          !props.Get(UsedMarginProperty())) {
-        props.Set(UsedMarginProperty(), new nsMargin(oldValue));
+          !GetProperty(UsedMarginProperty())) {
+        SetProperty(UsedMarginProperty(), new nsMargin(oldValue));
       }
     }
 
     const nsStylePadding* oldPadding = aOldStyleContext->PeekStylePadding();
     if (oldPadding && oldPadding->GetPadding(oldValue)) {
       if ((!StylePadding()->GetPadding(newValue) || oldValue != newValue) &&
-          !props.Get(UsedPaddingProperty())) {
-        props.Set(UsedPaddingProperty(), new nsMargin(oldValue));
+          !GetProperty(UsedPaddingProperty())) {
+        SetProperty(UsedPaddingProperty(), new nsMargin(oldValue));
       }
     }
 
     const nsStyleBorder* oldBorder = aOldStyleContext->PeekStyleBorder();
     if (oldBorder) {
       oldValue = oldBorder->GetComputedBorder();
       newValue = StyleBorder()->GetComputedBorder();
       if (oldValue != newValue &&
-          !props.Get(UsedBorderProperty())) {
-        props.Set(UsedBorderProperty(), new nsMargin(oldValue));
+          !GetProperty(UsedBorderProperty())) {
+        SetProperty(UsedBorderProperty(), new nsMargin(oldValue));
       }
     }
   }
 
   ImageLoader* imageLoader = PresContext()->Document()->StyleImageLoader();
   imgIRequest *oldBorderImage = aOldStyleContext
     ? aOldStyleContext->StyleBorder()->GetBorderImageRequest()
     : nullptr;
@@ -1157,17 +1156,17 @@ const nsIFrame::ChildListID nsIFrame::kN
 nsIFrame::GetUsedMargin() const
 {
   nsMargin margin(0, 0, 0, 0);
   if (((mState & NS_FRAME_FIRST_REFLOW) &&
        !(mState & NS_FRAME_IN_REFLOW)) ||
       nsSVGUtils::IsInSVGTextSubtree(this))
     return margin;
 
-  nsMargin *m = Properties().Get(UsedMarginProperty());
+  nsMargin *m = GetProperty(UsedMarginProperty());
   if (m) {
     margin = *m;
   } else {
     if (!StyleMargin()->GetMargin(margin)) {
       // If we get here, our caller probably shouldn't be calling us...
       NS_ERROR("Returning bogus 0-sized margin, because this margin "
                "depends on layout & isn't cached!");
     }
@@ -1196,17 +1195,17 @@ nsIFrame::GetUsedBorder() const
                                              &result);
     border.left = presContext->DevPixelsToAppUnits(result.left);
     border.top = presContext->DevPixelsToAppUnits(result.top);
     border.right = presContext->DevPixelsToAppUnits(result.right);
     border.bottom = presContext->DevPixelsToAppUnits(result.bottom);
     return border;
   }
 
-  nsMargin *b = Properties().Get(UsedBorderProperty());
+  nsMargin *b = GetProperty(UsedBorderProperty());
   if (b) {
     border = *b;
   } else {
     border = StyleBorder()->GetComputedBorder();
   }
   return border;
 }
 
@@ -1233,17 +1232,17 @@ nsIFrame::GetUsedPadding() const
       padding.top = presContext->DevPixelsToAppUnits(widget.top);
       padding.right = presContext->DevPixelsToAppUnits(widget.right);
       padding.bottom = presContext->DevPixelsToAppUnits(widget.bottom);
       padding.left = presContext->DevPixelsToAppUnits(widget.left);
       return padding;
     }
   }
 
-  nsMargin *p = Properties().Get(UsedPaddingProperty());
+  nsMargin *p = GetProperty(UsedPaddingProperty());
   if (p) {
     padding = *p;
   } else {
     if (!StylePadding()->GetPadding(padding)) {
       // If we get here, our caller probably shouldn't be calling us...
       NS_ERROR("Returning bogus 0-sized padding, because this padding "
                "depends on layout & isn't cached!");
     }
@@ -1754,18 +1753,17 @@ nsIFrame::GetCrossDocChildLists(nsTArray
 Visibility
 nsIFrame::GetVisibility() const
 {
   if (!(GetStateBits() & NS_FRAME_VISIBILITY_IS_TRACKED)) {
     return Visibility::UNTRACKED;
   }
 
   bool isSet = false;
-  ConstFrameProperties props = Properties();
-  uint32_t visibleCount = props.Get(VisibilityStateProperty(), &isSet);
+  uint32_t visibleCount = GetProperty(VisibilityStateProperty(), &isSet);
 
   MOZ_ASSERT(isSet, "Should have a VisibilityStateProperty value "
                     "if NS_FRAME_VISIBILITY_IS_TRACKED is set");
 
   return visibleCount > 0
        ? Visibility::APPROXIMATELY_VISIBLE
        : Visibility::APPROXIMATELY_NONVISIBLE;
 }
@@ -1828,25 +1826,24 @@ nsIFrame::UpdateVisibilitySynchronously(
 
 void
 nsIFrame::EnableVisibilityTracking()
 {
   if (GetStateBits() & NS_FRAME_VISIBILITY_IS_TRACKED) {
     return;  // Nothing to do.
   }
 
-  FrameProperties props = Properties();
-  MOZ_ASSERT(!props.Has(VisibilityStateProperty()),
+  MOZ_ASSERT(!HasProperty(VisibilityStateProperty()),
              "Shouldn't have a VisibilityStateProperty value "
              "if NS_FRAME_VISIBILITY_IS_TRACKED is not set");
 
   // Add the state bit so we know to track visibility for this frame, and
   // initialize the frame property.
   AddStateBits(NS_FRAME_VISIBILITY_IS_TRACKED);
-  props.Set(VisibilityStateProperty(), 0);
+  SetProperty(VisibilityStateProperty(), 0);
 
   nsIPresShell* presShell = PresContext()->PresShell();
   if (!presShell) {
     return;
   }
 
   // Schedule a visibility update. This method will virtually always be called
   // when layout has changed anyway, so it's very unlikely that any additional
@@ -1858,18 +1855,17 @@ nsIFrame::EnableVisibilityTracking()
 void
 nsIFrame::DisableVisibilityTracking()
 {
   if (!(GetStateBits() & NS_FRAME_VISIBILITY_IS_TRACKED)) {
     return;  // Nothing to do.
   }
 
   bool isSet = false;
-  FrameProperties props = Properties();
-  uint32_t visibleCount = props.Remove(VisibilityStateProperty(), &isSet);
+  uint32_t visibleCount = RemoveProperty(VisibilityStateProperty(), &isSet);
 
   MOZ_ASSERT(isSet, "Should have a VisibilityStateProperty value "
                     "if NS_FRAME_VISIBILITY_IS_TRACKED is set");
 
   RemoveStateBits(NS_FRAME_VISIBILITY_IS_TRACKED);
 
   if (visibleCount == 0) {
     return;  // We were nonvisible.
@@ -1881,48 +1877,46 @@ nsIFrame::DisableVisibilityTracking()
 
 void
 nsIFrame::DecApproximateVisibleCount(const Maybe<OnNonvisible>& aNonvisibleAction
                                        /* = Nothing() */)
 {
   MOZ_ASSERT(GetStateBits() & NS_FRAME_VISIBILITY_IS_TRACKED);
 
   bool isSet = false;
-  FrameProperties props = Properties();
-  uint32_t visibleCount = props.Get(VisibilityStateProperty(), &isSet);
+  uint32_t visibleCount = GetProperty(VisibilityStateProperty(), &isSet);
 
   MOZ_ASSERT(isSet, "Should have a VisibilityStateProperty value "
                     "if NS_FRAME_VISIBILITY_IS_TRACKED is set");
   MOZ_ASSERT(visibleCount > 0, "Frame is already nonvisible and we're "
                                "decrementing its visible count?");
 
   visibleCount--;
-  props.Set(VisibilityStateProperty(), visibleCount);
+  SetProperty(VisibilityStateProperty(), visibleCount);
   if (visibleCount > 0) {
     return;
   }
 
   // We just became nonvisible, so send an OnVisibilityChange() notification.
   OnVisibilityChange(Visibility::APPROXIMATELY_NONVISIBLE, aNonvisibleAction);
 }
 
 void
 nsIFrame::IncApproximateVisibleCount()
 {
   MOZ_ASSERT(GetStateBits() & NS_FRAME_VISIBILITY_IS_TRACKED);
 
   bool isSet = false;
-  FrameProperties props = Properties();
-  uint32_t visibleCount = props.Get(VisibilityStateProperty(), &isSet);
+  uint32_t visibleCount = GetProperty(VisibilityStateProperty(), &isSet);
 
   MOZ_ASSERT(isSet, "Should have a VisibilityStateProperty value "
                     "if NS_FRAME_VISIBILITY_IS_TRACKED is set");
 
   visibleCount++;
-  props.Set(VisibilityStateProperty(), visibleCount);
+  SetProperty(VisibilityStateProperty(), visibleCount);
   if (visibleCount > 1) {
     return;
   }
 
   // We just became visible, so send an OnVisibilityChange() notification.
   OnVisibilityChange(Visibility::APPROXIMATELY_VISIBLE);
 }
 
@@ -5365,20 +5359,19 @@ nsFrame::ComputeSizeWithIntrinsicDimensi
       GetParent()->StylePosition()->mFlexDirection;
     isInlineFlexItem =
       flexDirection == NS_STYLE_FLEX_DIRECTION_ROW ||
       flexDirection == NS_STYLE_FLEX_DIRECTION_ROW_REVERSE;
 
     // If FlexItemMainSizeOverride frame-property is set, then that means the
     // flex container is imposing a main-size on this flex item for it to use
     // as its size in the container's main axis.
-    FrameProperties props = Properties();
     bool didImposeMainSize;
     nscoord imposedMainSize =
-      props.Get(nsIFrame::FlexItemMainSizeOverride(), &didImposeMainSize);
+      GetProperty(nsIFrame::FlexItemMainSizeOverride(), &didImposeMainSize);
     if (didImposeMainSize) {
       imposedMainSizeStyleCoord.emplace(imposedMainSize,
                                         nsStyleCoord::CoordConstructor);
       if (isInlineFlexItem) {
         inlineStyleCoord = imposedMainSizeStyleCoord.ptr();
       } else {
         blockStyleCoord = imposedMainSizeStyleCoord.ptr();
       }
@@ -6487,17 +6480,17 @@ static void InvalidateFrameInternal(nsIF
   }
   if (!aHasDisplayItem) {
     return;
   }
   if (needsSchedulePaint) {
     SchedulePaintInternal(aFrame);
   }
   if (aFrame->HasAnyStateBits(NS_FRAME_HAS_INVALID_RECT)) {
-    aFrame->Properties().Delete(nsIFrame::InvalidationRect());
+    aFrame->DeleteProperty(nsIFrame::InvalidationRect());
     aFrame->RemoveStateBits(NS_FRAME_HAS_INVALID_RECT);
   }
 }
 
 void
 nsIFrame::InvalidateFrameSubtree(uint32_t aDisplayItemKey)
 {
   bool hasDisplayItem = 
@@ -6562,23 +6555,23 @@ nsIFrame::InvalidateFrameWithRect(const 
   } else {
     alreadyInvalid = true;
   } 
 
   if (!hasDisplayItem) {
     return;
   }
 
-  nsRect* rect = Properties().Get(InvalidationRect());
+  nsRect* rect = GetProperty(InvalidationRect());
   if (!rect) {
     if (alreadyInvalid) {
       return;
     }
     rect = new nsRect();
-    Properties().Set(InvalidationRect(), rect);
+    SetProperty(InvalidationRect(), rect);
     AddStateBits(NS_FRAME_HAS_INVALID_RECT);
   }
 
   *rect = rect->Union(aRect);
 }
 
 /*static*/ uint8_t nsIFrame::sLayerIsPrerenderedDataKey;
 
@@ -6669,17 +6662,17 @@ nsIFrame::TryUpdateTransformOnly(Layer**
 bool 
 nsIFrame::IsInvalid(nsRect& aRect)
 {
   if (!HasAnyStateBits(NS_FRAME_NEEDS_PAINT)) {
     return false;
   }
   
   if (HasAnyStateBits(NS_FRAME_HAS_INVALID_RECT)) {
-    nsRect* rect = Properties().Get(InvalidationRect());
+    nsRect* rect = GetProperty(InvalidationRect());
     NS_ASSERTION(rect, "Must have an invalid rect if NS_FRAME_HAS_INVALID_RECT is set!");
     aRect = *rect;
   } else {
     aRect.SetEmpty();
   }
   return true;
 }
 
@@ -6757,18 +6750,18 @@ ComputeEffectsRect(nsIFrame* aFrame, con
 {
   nsRect r = aOverflowRect;
 
   if (aFrame->GetStateBits() & NS_FRAME_SVG_LAYOUT) {
     // For SVG frames, we only need to account for filters.
     // TODO: We could also take account of clipPath and mask to reduce the
     // visual overflow, but that's not essential.
     if (aFrame->StyleEffects()->HasFilters()) {
-      aFrame->Properties().
-        Set(nsIFrame::PreEffectsBBoxProperty(), new nsRect(r));
+      aFrame->SetProperty
+        (nsIFrame::PreEffectsBBoxProperty(), new nsRect(r));
       r = nsSVGUtils::GetPostFilterVisualOverflowRect(aFrame, aOverflowRect);
     }
     return r;
   }
 
   // box-shadow
   r.UnionRect(r, nsLayoutUtils::GetBoxShadowRectForFrame(aFrame, aNewSize));
 
@@ -6796,57 +6789,57 @@ ComputeEffectsRect(nsIFrame* aFrame, con
   // Note that we don't remove the outlineInnerRect if a frame loses outline
   // style. That would require an extra property lookup for every frame,
   // or a new frame state bit to track whether a property had been stored,
   // or something like that. It's not worth doing that here. At most it's
   // only one heap-allocated rect per frame and it will be cleaned up when
   // the frame dies.
 
   if (nsSVGIntegrationUtils::UsingEffectsForFrame(aFrame)) {
-    aFrame->Properties().
-      Set(nsIFrame::PreEffectsBBoxProperty(), new nsRect(r));
+    aFrame->SetProperty
+      (nsIFrame::PreEffectsBBoxProperty(), new nsRect(r));
     r = nsSVGIntegrationUtils::ComputePostEffectsVisualOverflowRect(aFrame, r);
   }
 
   return r;
 }
 
 void
 nsIFrame::MovePositionBy(const nsPoint& aTranslation)
 {
   nsPoint position = GetNormalPosition() + aTranslation;
 
   const nsMargin* computedOffsets = nullptr;
   if (IsRelativelyPositioned()) {
-    computedOffsets = Properties().Get(nsIFrame::ComputedOffsetProperty());
+    computedOffsets = GetProperty(nsIFrame::ComputedOffsetProperty());
   }
   ReflowInput::ApplyRelativePositioning(this, computedOffsets ?
                                               *computedOffsets : nsMargin(),
                                               &position);
   SetPosition(position);
 }
 
 nsRect
 nsIFrame::GetNormalRect() const
 {
   // It might be faster to first check
   // StyleDisplay()->IsRelativelyPositionedStyle().
-  nsPoint* normalPosition = Properties().Get(NormalPositionProperty());
+  nsPoint* normalPosition = GetProperty(NormalPositionProperty());
   if (normalPosition) {
     return nsRect(*normalPosition, GetSize());
   }
   return GetRect();
 }
 
 nsPoint
 nsIFrame::GetNormalPosition() const
 {
   // It might be faster to first check
   // StyleDisplay()->IsRelativelyPositionedStyle().
-  nsPoint* normalPosition = Properties().Get(NormalPositionProperty());
+  nsPoint* normalPosition = GetProperty(NormalPositionProperty());
   if (normalPosition) {
     return *normalPosition;
   }
   return GetPosition();
 }
 
 nsPoint
 nsIFrame::GetPositionIgnoringScrolling()
@@ -6895,17 +6888,17 @@ nsIFrame::GetOverflowAreas() const
                          nsRect(nsPoint(0, 0), GetSize()));
 }
 
 nsOverflowAreas
 nsIFrame::GetOverflowAreasRelativeToSelf() const
 {
   if (IsTransformed()) {
     nsOverflowAreas* preTransformOverflows =
-      Properties().Get(PreTransformOverflowAreasProperty());
+      GetProperty(PreTransformOverflowAreasProperty());
     if (preTransformOverflows) {
       return nsOverflowAreas(preTransformOverflows->VisualOverflow(),
                              preTransformOverflows->ScrollableOverflow());
     }
   }
   return nsOverflowAreas(GetVisualOverflowRect(),
                          GetScrollableOverflowRect());
 }
@@ -6922,39 +6915,39 @@ nsIFrame::GetVisualOverflowRectRelativeT
   return GetVisualOverflowRect() + mRect.TopLeft();
 }
 
 nsRect
 nsIFrame::GetScrollableOverflowRectRelativeToSelf() const
 {
   if (IsTransformed()) {
     nsOverflowAreas* preTransformOverflows =
-      Properties().Get(PreTransformOverflowAreasProperty());
+      GetProperty(PreTransformOverflowAreasProperty());
     if (preTransformOverflows)
       return preTransformOverflows->ScrollableOverflow();
   }
   return GetScrollableOverflowRect();
 }
 
 nsRect
 nsIFrame::GetVisualOverflowRectRelativeToSelf() const
 {
   if (IsTransformed()) {
     nsOverflowAreas* preTransformOverflows =
-      Properties().Get(PreTransformOverflowAreasProperty());
+      GetProperty(PreTransformOverflowAreasProperty());
     if (preTransformOverflows)
       return preTransformOverflows->VisualOverflow();
   }
   return GetVisualOverflowRect();
 }
 
 nsRect
 nsIFrame::GetPreEffectsVisualOverflowRect() const
 {
-  nsRect* r = Properties().Get(nsIFrame::PreEffectsBBoxProperty());
+  nsRect* r = GetProperty(nsIFrame::PreEffectsBBoxProperty());
   return r ? *r : GetVisualOverflowRectRelativeToSelf();
 }
 
 bool
 nsIFrame::UpdateOverflow()
 {
   MOZ_ASSERT(FrameMaintainsOverflow(),
              "Non-display SVG do not maintain visual overflow rects");
@@ -7147,21 +7140,21 @@ nsIFrame::ListGeneric(nsACString& aTo, c
     aTo += nsPrintfCString(" prev-%s=%p", fluid?"in-flow":"continuation",
             static_cast<void*>(GetPrevContinuation()));
   }
   if (GetNextContinuation()) {
     bool fluid = GetNextInFlow() == GetNextContinuation();
     aTo += nsPrintfCString(" next-%s=%p", fluid?"in-flow":"continuation",
             static_cast<void*>(GetNextContinuation()));
   }
-  void* IBsibling = Properties().Get(IBSplitSibling());
+  void* IBsibling = GetProperty(IBSplitSibling());
   if (IBsibling) {
     aTo += nsPrintfCString(" IBSplitSibling=%p", IBsibling);
   }
-  void* IBprevsibling = Properties().Get(IBSplitPrevSibling());
+  void* IBprevsibling = GetProperty(IBSplitPrevSibling());
   if (IBprevsibling) {
     aTo += nsPrintfCString(" IBSplitPrevSibling=%p", IBprevsibling);
   }
   aTo += nsPrintfCString(" {%d,%d,%d,%d}", mRect.x, mRect.y, mRect.width, mRect.height);
 
   mozilla::WritingMode wm = GetWritingMode();
   if (wm.IsVertical() || !wm.IsBidiLTR()) {
     aTo += nsPrintfCString(" wm=%s: logical size={%d,%d}", wm.DebugString(),
@@ -7191,17 +7184,17 @@ nsIFrame::ListGeneric(nsACString& aTo, c
     nsRect so = f->GetScrollableOverflowRect();
     if (!so.IsEqualEdges(mRect)) {
       aTo += nsPrintfCString(" scr-overflow=%d,%d,%d,%d", so.x, so.y, so.width, so.height);
     }
   }
   if (0 != mState) {
     aTo += nsPrintfCString(" [state=%016llx]", (unsigned long long)mState);
   }
-  if (Properties().Has(BidiDataProperty())) {
+  if (HasProperty(BidiDataProperty())) {
     FrameBidiData bidi = GetBidiData();
     aTo += nsPrintfCString(" bidi(%d,%d,%d)", bidi.baseLevel,
                            bidi.embeddingLevel, bidi.precedingControl);
   }
   if (IsTransformed()) {
     aTo += nsPrintfCString(" transformed");
   }
   if (ChildrenHavePerspective()) {
@@ -7560,18 +7553,17 @@ nsFrame::GetPointFromOffset(int32_t inOf
       int32_t newOffset = newContent->IndexOf(mContent);
 
       // Find the direction of the frame from the EmbeddingLevelProperty,
       // which is the resolved bidi level set in
       // nsBidiPresUtils::ResolveParagraph (odd levels = right-to-left).
       // If the embedding level isn't set, just use the CSS direction
       // property.
       bool hasBidiData;
-      FrameBidiData bidiData =
-        Properties().Get(BidiDataProperty(), &hasBidiData);
+      FrameBidiData bidiData = GetProperty(BidiDataProperty(), &hasBidiData);
       bool isRTL = hasBidiData
         ? IS_LEVEL_RTL(bidiData.embeddingLevel)
         : StyleVisibility()->mDirection == NS_STYLE_DIRECTION_RTL;
       if ((!isRTL && inOffset > newOffset) ||
           (isRTL && inOffset <= newOffset)) {
         pt = contentRect.TopRight();
       }
     }
@@ -8703,51 +8695,50 @@ NS_DECLARE_FRAME_PROPERTY_DELETABLE(Over
 
 bool
 nsIFrame::ClearOverflowRects()
 {
   if (mOverflow.mType == NS_FRAME_OVERFLOW_NONE) {
     return false;
   }
   if (mOverflow.mType == NS_FRAME_OVERFLOW_LARGE) {
-    Properties().Delete(OverflowAreasProperty());
+    DeleteProperty(OverflowAreasProperty());
   }
   mOverflow.mType = NS_FRAME_OVERFLOW_NONE;
   return true;
 }
 
 /** Create or retrieve the previously stored overflow area, if the frame does 
  * not overflow and no creation is required return nullptr.
  * @return pointer to the overflow area rectangle 
  */
 nsOverflowAreas*
 nsIFrame::GetOverflowAreasProperty()
 {
-  FrameProperties props = Properties();
-  nsOverflowAreas* overflow = props.Get(OverflowAreasProperty());
+  nsOverflowAreas* overflow = GetProperty(OverflowAreasProperty());
 
   if (overflow) {
     return overflow; // the property already exists
   }
 
   // The property isn't set yet, so allocate a new rect, set the property,
   // and return the newly allocated rect
   overflow = new nsOverflowAreas;
-  props.Set(OverflowAreasProperty(), overflow);
+  SetProperty(OverflowAreasProperty(), overflow);
   return overflow;
 }
 
 /** Set the overflowArea rect, storing it as deltas or a separate rect
  * depending on its size in relation to the primary frame rect.
  */
 bool
 nsIFrame::SetOverflowAreas(const nsOverflowAreas& aOverflowAreas)
 {
   if (mOverflow.mType == NS_FRAME_OVERFLOW_LARGE) {
-    nsOverflowAreas* overflow = Properties().Get(OverflowAreasProperty());
+    nsOverflowAreas* overflow = GetProperty(OverflowAreasProperty());
     bool changed = *overflow != aOverflowAreas;
     *overflow = aOverflowAreas;
 
     // Don't bother with converting to the deltas form if we already
     // have a property.
     return changed;
   }
 
@@ -8973,17 +8964,17 @@ ComputeAndIncludeOutlineArea(nsIFrame* a
         }
       }
 
       innerRect.UnionRect(innerRect, r);
     }
   }
 
   // Keep this code in sync with GetOutlineInnerRect in nsCSSRendering.cpp.
-  aFrame->Properties().Set(nsIFrame::OutlineInnerRectProperty(),
+  aFrame->SetProperty(nsIFrame::OutlineInnerRectProperty(),
                            new nsRect(innerRect));
   const nscoord offset = outline->mOutlineOffset;
   nsRect outerRect(innerRect);
   bool useOutlineAuto = false;
   if (nsLayoutUtils::IsOutlineStyleAutoEnabled()) {
     useOutlineAuto = outline->mOutlineStyle == NS_STYLE_BORDER_STYLE_AUTO;
     if (MOZ_UNLIKELY(useOutlineAuto)) {
       nsPresContext* presContext = aFrame->PresContext();
@@ -9021,32 +9012,32 @@ nsIFrame::FinishAndStoreOverflow(nsOverf
 
   nsRect bounds(nsPoint(0, 0), aNewSize);
   // Store the passed in overflow area if we are a preserve-3d frame or we have
   // a transform, and it's not just the frame bounds.
   if (hasTransform || Combines3DTransformWithAncestors(disp)) {
     if (!aOverflowAreas.VisualOverflow().IsEqualEdges(bounds) ||
         !aOverflowAreas.ScrollableOverflow().IsEqualEdges(bounds)) {
       nsOverflowAreas* initial =
-        Properties().Get(nsIFrame::InitialOverflowProperty());
+        GetProperty(nsIFrame::InitialOverflowProperty());
       if (!initial) {
-        Properties().Set(nsIFrame::InitialOverflowProperty(),
+        SetProperty(nsIFrame::InitialOverflowProperty(),
                          new nsOverflowAreas(aOverflowAreas));
       } else if (initial != &aOverflowAreas) {
         *initial = aOverflowAreas;
       }
     } else {
-      Properties().Delete(nsIFrame::InitialOverflowProperty());
+      DeleteProperty(nsIFrame::InitialOverflowProperty());
     }
 #ifdef DEBUG
-    Properties().Set(nsIFrame::DebugInitialOverflowPropertyApplied(), true);
+    SetProperty(nsIFrame::DebugInitialOverflowPropertyApplied(), true);
 #endif
   } else {
 #ifdef DEBUG
-    Properties().Delete(nsIFrame::DebugInitialOverflowPropertyApplied());
+    DeleteProperty(nsIFrame::DebugInitialOverflowPropertyApplied());
 #endif
   }
 
   // This is now called FinishAndStoreOverflow() instead of 
   // StoreOverflow() because frame-generic ways of adding overflow
   // can happen here, e.g. CSS2 outline and native theme.
   // If the overflow area width or height is nscoord_MAX, then a
   // saturating union may have encounted an overflow, so the overflow may not
@@ -9129,18 +9120,18 @@ nsIFrame::FinishAndStoreOverflow(nsOverf
   SetSize(aNewSize);
 
   if (ChildrenHavePerspective(disp) && sizeChanged) {
     nsRect newBounds(nsPoint(0, 0), aNewSize);
     RecomputePerspectiveChildrenOverflow(this, effectSet);
   }
 
   if (hasTransform) {
-    Properties().Set(nsIFrame::PreTransformOverflowAreasProperty(),
-                     new nsOverflowAreas(aOverflowAreas));
+    SetProperty(nsIFrame::PreTransformOverflowAreasProperty(),
+                new nsOverflowAreas(aOverflowAreas));
 
     if (Combines3DTransformWithAncestors(disp)) {
       /* If we're a preserve-3d leaf frame, then our pre-transform overflow should be correct. Our
        * post-transform overflow is empty though, because we only contribute to the overflow area
        * of the preserve-3d root frame.
        * If we're an intermediate frame then the pre-transform overflow should contain all our
        * non-preserve-3d children, which is what we want. Again we have no post-transform overflow.
        */
@@ -9155,17 +9146,17 @@ nsIFrame::FinishAndStoreOverflow(nsOverf
        * the participants. This won't have happened yet as the code above set their overflow
        * area to empty. Manually collect these overflow areas now.
        */
       if (Extend3DContext(disp, effectSet)) {
         ComputePreserve3DChildrenOverflow(aOverflowAreas);
       }
     }
   } else {
-    Properties().Delete(nsIFrame::PreTransformOverflowAreasProperty());
+    DeleteProperty(nsIFrame::PreTransformOverflowAreasProperty());
   }
 
   /* Revert the size change in case some caller is depending on this. */
   SetSize(oldSize);
 
   bool anyOverflowChanged;
   if (aOverflowAreas != nsOverflowAreas(bounds, bounds)) {
     anyOverflowChanged = SetOverflowAreas(aOverflowAreas);
@@ -9188,17 +9179,17 @@ nsIFrame::RecomputePerspectiveChildrenOv
     nsFrameList::Enumerator childFrames(lists.CurrentList());
     for (; !childFrames.AtEnd(); childFrames.Next()) {
       nsIFrame* child = childFrames.get();
       if (!child->FrameMaintainsOverflow()) {
         continue; // frame does not maintain overflow rects
       }
       if (child->HasPerspective(aEffectSet)) {
         nsOverflowAreas* overflow = 
-          child->Properties().Get(nsIFrame::InitialOverflowProperty());
+          child->GetProperty(nsIFrame::InitialOverflowProperty());
         nsRect bounds(nsPoint(0, 0), child->GetSize());
         if (overflow) {
           nsOverflowAreas overflowCopy = *overflow;
           child->FinishAndStoreOverflow(overflowCopy, bounds.Size());
         } else {
           nsOverflowAreas boundsOverflow;
           boundsOverflow.SetAllTo(bounds);
           child->FinishAndStoreOverflow(boundsOverflow, bounds.Size());
@@ -9300,17 +9291,17 @@ GetIBSplitSiblingForAnonymousBlock(const
   // being O(N^2) when it is called O(N) times.)
   aFrame = aFrame->FirstContinuation();
 
   /*
    * Now look up the nsGkAtoms::IBSplitPrevSibling
    * property.
    */
   nsIFrame *ibSplitSibling =
-    aFrame->Properties().Get(nsIFrame::IBSplitPrevSibling());
+    aFrame->GetProperty(nsIFrame::IBSplitPrevSibling());
   NS_ASSERTION(ibSplitSibling, "Broken frame tree?");
   return ibSplitSibling;
 }
 
 /**
  * Get the parent, corrected for the mangled frame tree resulting from
  * having a block within an inline.  The result only differs from the
  * result of |GetParent| when |GetParent| returns an anonymous block
@@ -10243,17 +10234,17 @@ nsFrame::BoxReflow(nsBoxLayoutState&    
 #ifdef DEBUG_REFLOW
   gIndent2--;
 #endif
 }
 
 nsBoxLayoutMetrics*
 nsFrame::BoxMetrics() const
 {
-  nsBoxLayoutMetrics* metrics = Properties().Get(BoxMetricsProperty());
+  nsBoxLayoutMetrics* metrics = GetProperty(BoxMetricsProperty());
   NS_ASSERTION(metrics, "A box layout method was called but InitBoxMetrics was never called");
   return metrics;
 }
 
 void
 nsFrame::UpdateStyleOfChildAnonBox(nsIFrame* aChildFrame,
                                    ServoStyleSet& aStyleSet,
                                    nsStyleChangeList& aChangeList,
--- a/layout/generic/nsFrameStateBits.h
+++ b/layout/generic/nsFrameStateBits.h
@@ -265,18 +265,18 @@ FRAME_STATE_BIT(Generic, 53, NS_FRAME_IS
 
 // Frame has a LayerActivityProperty property
 FRAME_STATE_BIT(Generic, 54, NS_FRAME_HAS_LAYER_ACTIVITY_PROPERTY)
 
 // Frame owns anonymous boxes whose style contexts it will need to update during
 // a stylo tree traversal.
 FRAME_STATE_BIT(Generic, 55, NS_FRAME_OWNS_ANON_BOXES)
 
-// Frame has properties in the nsIFrame::Properties() hash.
-FRAME_STATE_BIT(Generic, 56, NS_FRAME_HAS_PROPERTIES)
+// ** currently unused Generic bit **
+// FRAME_STATE_BIT(Generic, 56, ...)
 
 // The display list of the frame can be handled by the shortcut for
 // COMMON CASE.
 FRAME_STATE_BIT(Generic, 57, NS_FRAME_SIMPLE_DISPLAYLIST)
 
 // Set for all descendants of MathML sub/supscript elements (other than the
 // base frame) to indicate that the SSTY font feature should be used.
 FRAME_STATE_BIT(Generic, 58, NS_FRAME_MATHML_SCRIPT_DESCENDANT)
--- a/layout/generic/nsGridContainerFrame.cpp
+++ b/layout/generic/nsGridContainerFrame.cpp
@@ -1750,17 +1750,17 @@ struct MOZ_STACK_CLASS nsGridContainerFr
     // of fragments before this so that we can figure out our start row below.
     uint32_t fragment = 0;
     nsIFrame* firstInFlow = aGridContainerFrame;
     for (auto pif = aGridContainerFrame->GetPrevInFlow();
          pif; pif = pif->GetPrevInFlow()) {
       ++fragment;
       firstInFlow = pif;
     }
-    mSharedGridData = firstInFlow->Properties().Get(SharedGridData::Prop());
+    mSharedGridData = firstInFlow->GetProperty(SharedGridData::Prop());
     MOZ_ASSERT(mSharedGridData, "first-in-flow must have SharedGridData");
 
     // Find the start row for this fragment and undo breaks after that row
     // since the breaks might be different from the last reflow.
     auto& rowSizes = mSharedGridData->mRows.mSizes;
     const uint32_t numRows = rowSizes.Length();
     mStartRow = numRows;
     for (uint32_t row = 0, breakCount = 0; row < numRows; ++row) {
@@ -2559,17 +2559,17 @@ NS_NewGridContainerFrame(nsIPresShell* a
 // nsGridContainerFrame Method Implementations
 // ===========================================
 
 /*static*/ const nsRect&
 nsGridContainerFrame::GridItemCB(nsIFrame* aChild)
 {
   MOZ_ASSERT((aChild->GetStateBits() & NS_FRAME_OUT_OF_FLOW) &&
              aChild->IsAbsolutelyPositioned());
-  nsRect* cb = aChild->Properties().Get(GridItemContainingBlockRect());
+  nsRect* cb = aChild->GetProperty(GridItemContainingBlockRect());
   MOZ_ASSERT(cb, "this method must only be called on grid items, and the grid "
                  "container should've reflowed this item by now and set up cb");
   return *cb;
 }
 
 void
 nsGridContainerFrame::AddImplicitNamedAreas(
   const nsTArray<nsTArray<nsString>>& aLineNameLists)
@@ -2586,17 +2586,17 @@ nsGridContainerFrame::AddImplicitNamedAr
       if (Grid::IsNameWithStartSuffix(name, &indexOfSuffix) ||
           Grid::IsNameWithEndSuffix(name, &indexOfSuffix)) {
         // Extract the name that was found earlier.
         nsDependentSubstring areaName(name, 0, indexOfSuffix);
 
         // Lazily create the ImplicitNamedAreas.
         if (!areas) {
           areas = new ImplicitNamedAreas;
-          Properties().Set(ImplicitNamedAreasProperty(), areas);
+          SetProperty(ImplicitNamedAreasProperty(), areas);
         }
 
         mozilla::css::GridNamedArea area;
         if (!areas->Get(areaName, &area)) {
           // Not found, so prep the newly-seen area with a name and empty
           // boundary information, which will get filled in later.
           area.mName = areaName;
           area.mRowStart = 0;
@@ -2618,17 +2618,17 @@ nsGridContainerFrame::InitImplicitNamedA
   if (areas) {
     // Clear it, but reuse the hashtable itself for now.  We'll remove it
     // below if it isn't needed anymore.
     areas->Clear();
   }
   AddImplicitNamedAreas(aStyle->mGridTemplateColumns.mLineNameLists);
   AddImplicitNamedAreas(aStyle->mGridTemplateRows.mLineNameLists);
   if (areas && areas->Count() == 0) {
-    Properties().Delete(ImplicitNamedAreasProperty());
+    DeleteProperty(ImplicitNamedAreasProperty());
   }
 }
 
 int32_t
 nsGridContainerFrame::Grid::ResolveLine(const nsStyleGridLine& aLine,
                                         int32_t aNth,
                                         uint32_t aFromIndex,
                                         const LineNameMap& aNameMap,
@@ -3461,33 +3461,33 @@ MeasuringReflow(nsIFrame*           aChi
     dummyParentState.emplace(pc, parent, aRC,
                              LogicalSize(parent->GetWritingMode(), 0,
                                          NS_UNCONSTRAINEDSIZE),
                              ReflowInput::DUMMY_PARENT_REFLOW_STATE);
     rs = dummyParentState.ptr();
   }
 #ifdef DEBUG
   // This will suppress various CRAZY_SIZE warnings for this reflow.
-  parent->Properties().Set(
+  parent->SetProperty(
     nsContainerFrame::DebugReflowingWithInfiniteISize(), true);
 #endif
   auto wm = aChild->GetWritingMode();
   uint32_t riFlags = ReflowInput::COMPUTE_SIZE_USE_AUTO_BSIZE;
   if (aAvailableSize.ISize(wm) == INFINITE_ISIZE_COORD) {
     riFlags |= ReflowInput::COMPUTE_SIZE_SHRINK_WRAP;
   }
   if (aIMinSizeClamp != NS_MAXSIZE) {
     riFlags |= ReflowInput::I_CLAMP_MARGIN_BOX_MIN_SIZE;
   }
   if (aBMinSizeClamp != NS_MAXSIZE) {
     riFlags |= ReflowInput::B_CLAMP_MARGIN_BOX_MIN_SIZE;
-    aChild->Properties().Set(nsIFrame::BClampMarginBoxMinSizeProperty(),
+    aChild->SetProperty(nsIFrame::BClampMarginBoxMinSizeProperty(),
                              aBMinSizeClamp);
   } else {
-    aChild->Properties().Delete(nsIFrame::BClampMarginBoxMinSizeProperty());
+    aChild->DeleteProperty(nsIFrame::BClampMarginBoxMinSizeProperty());
   }
   ReflowInput childRI(pc, *rs, aChild, aAvailableSize, &aCBSize, riFlags);
 
   // Because we pass ReflowInput::COMPUTE_SIZE_USE_AUTO_BSIZE, and the
   // previous reflow of the child might not have, set the child's
   // block-resize flag to true.
   // FIXME (perf): It would be faster to do this only if the previous
   // reflow of the child was not a measuring reflow, and only if the
@@ -3498,17 +3498,17 @@ MeasuringReflow(nsIFrame*           aChi
   ReflowOutput childSize(childRI);
   nsReflowStatus childStatus;
   const uint32_t flags = NS_FRAME_NO_MOVE_FRAME | NS_FRAME_NO_SIZE_VIEW;
   parent->ReflowChild(aChild, pc, childSize, childRI, wm,
                       LogicalPoint(wm), nsSize(), flags, childStatus);
   parent->FinishReflowChild(aChild, pc, childSize, &childRI, wm,
                             LogicalPoint(wm), nsSize(), flags);
 #ifdef DEBUG
-    parent->Properties().Delete(nsContainerFrame::DebugReflowingWithInfiniteISize());
+    parent->DeleteProperty(nsContainerFrame::DebugReflowingWithInfiniteISize());
 #endif
   return childSize.BSize(wm);
 }
 
 /**
  * Return the [min|max]-content contribution of aChild to its parent (i.e.
  * the child's margin-box) in aAxis.
  */
@@ -5012,19 +5012,19 @@ nsGridContainerFrame::ReflowInFlowChild(
         // This happens when the subtree overflows its track.
         // XXX spec issue? it's unclear how to handle this.
         baselineAdjust = nscoord(0);
       } else if (baselineAdjust > nscoord(0) &&
                  (state & ItemState::eLastBaseline)) {
         baselineAdjust = -baselineAdjust;
       }
       if (baselineAdjust != nscoord(0)) {
-        aChild->Properties().Set(aProp, baselineAdjust);
+        aChild->SetProperty(aProp, baselineAdjust);
       } else {
-        aChild->Properties().Delete(aProp);
+        aChild->DeleteProperty(aProp);
       }
     };
     SetProp(eLogicalAxisBlock, isOrthogonal ? IBaselinePadProperty() :
                                               BBaselinePadProperty());
     SetProp(eLogicalAxisInline, isOrthogonal ? BBaselinePadProperty() :
                                                IBaselinePadProperty());
   } else {
     // By convention, for frames that perform CSS Box Alignment, we position
@@ -5051,20 +5051,20 @@ nsGridContainerFrame::ReflowInFlowChild(
   if (aGridItemInfo) {
     auto childIAxis = isOrthogonal ? eLogicalAxisBlock : eLogicalAxisInline;
     if (aGridItemInfo->mState[childIAxis] & ItemState::eClampMarginBoxMinSize) {
       flags |= ReflowInput::I_CLAMP_MARGIN_BOX_MIN_SIZE;
     }
     auto childBAxis = GetOrthogonalAxis(childIAxis);
     if (aGridItemInfo->mState[childBAxis] & ItemState::eClampMarginBoxMinSize) {
       flags |= ReflowInput::B_CLAMP_MARGIN_BOX_MIN_SIZE;
-      aChild->Properties().Set(BClampMarginBoxMinSizeProperty(),
-                               childCBSize.BSize(childWM));
+      aChild->SetProperty(BClampMarginBoxMinSizeProperty(),
+                          childCBSize.BSize(childWM));
     } else {
-      aChild->Properties().Delete(BClampMarginBoxMinSizeProperty());
+      aChild->DeleteProperty(BClampMarginBoxMinSizeProperty());
     }
     if ((aGridItemInfo->mState[childIAxis] & ItemState::eApplyAutoMinSize)) {
       flags |= ReflowInput::I_APPLY_AUTO_MIN_SIZE;
     }
   }
 
   if (!isConstrainedBSize) {
     childCBSize.BSize(childWM) = NS_UNCONSTRAINEDSIZE;
@@ -5081,21 +5081,21 @@ nsGridContainerFrame::ReflowInFlowChild(
   // reflow of the child was a measuring reflow, and only if the child
   // does some of the things that are affected by
   // ReflowInput::COMPUTE_SIZE_USE_AUTO_BSIZE.
   childRI.SetBResize(true);
 
   // A table-wrapper needs to propagate the CB size we give it to its
   // inner table frame later.  @see nsTableWrapperFrame::InitChildReflowInput.
   if (aChild->IsTableWrapperFrame()) {
-    const auto& props = aChild->Properties();
-    LogicalSize* cb = props.Get(nsTableWrapperFrame::GridItemCBSizeProperty());
+    LogicalSize* cb =
+      aChild->GetProperty(nsTableWrapperFrame::GridItemCBSizeProperty());
     if (!cb) {
       cb = new LogicalSize(childWM);
-      props.Set(nsTableWrapperFrame::GridItemCBSizeProperty(), cb);
+      aChild->SetProperty(nsTableWrapperFrame::GridItemCBSizeProperty(), cb);
     }
     *cb = percentBasis;
   }
 
   // If the child is stretching in its block axis, and we might be fragmenting
   // it in that axis, then setup a frame property to tell
   // nsBlockFrame::ComputeFinalSize the size.
   if (isConstrainedBSize && !wm.IsOrthogonalTo(childWM)) {
@@ -5105,19 +5105,19 @@ nsGridContainerFrame::ReflowInFlowChild(
       auto blockAxisAlignment =
         childRI.mStylePosition->UsedAlignSelf(StyleContext());
       if (blockAxisAlignment == NS_STYLE_ALIGN_NORMAL ||
           blockAxisAlignment == NS_STYLE_ALIGN_STRETCH) {
         stretch = true;
       }
     }
     if (stretch) {
-      aChild->Properties().Set(FragStretchBSizeProperty(), *aStretchBSize);
+      aChild->SetProperty(FragStretchBSizeProperty(), *aStretchBSize);
     } else {
-      aChild->Properties().Delete(FragStretchBSizeProperty());
+      aChild->DeleteProperty(FragStretchBSizeProperty());
     }
   }
 
   // We need the width of the child before we can correctly convert
   // the writing-mode of its origin, so we reflow at (0, 0) using a dummy
   // aContainerSize, and then pass the correct position to FinishReflowChild.
   ReflowOutput childSize(childRI);
   const nsSize dummyContainerSize;
@@ -5719,20 +5719,20 @@ nsGridContainerFrame::ReflowChildren(Gri
       for (nsFrameList::Enumerator e(children); !e.AtEnd(); e.Next(), ++i) {
         nsIFrame* child = e.get();
         MOZ_ASSERT(i < aState.mAbsPosItems.Length());
         MOZ_ASSERT(aState.mAbsPosItems[i].mFrame == child);
         GridArea& area = aState.mAbsPosItems[i].mArea;
         LogicalRect itemCB =
           aState.ContainingBlockForAbsPos(area, gridOrigin, gridCB);
         // nsAbsoluteContainingBlock::Reflow uses physical coordinates.
-        nsRect* cb = child->Properties().Get(GridItemContainingBlockRect());
+        nsRect* cb = child->GetProperty(GridItemContainingBlockRect());
         if (!cb) {
           cb = new nsRect;
-          child->Properties().Set(GridItemContainingBlockRect(), cb);
+          child->SetProperty(GridItemContainingBlockRect(), cb);
         }
         *cb = itemCB.GetPhysicalRect(wm, gridCBPhysicalSize);
       }
       // We pass a dummy rect as CB because each child has its own CB rect.
       // The eIsGridContainerCB flag tells nsAbsoluteContainingBlock::Reflow to
       // use those instead.
       nsRect dummyRect;
       AbsPosReflowFlags flags =
@@ -5812,17 +5812,17 @@ nsGridContainerFrame::Reflow(nsPresConte
           nsIFrame* pif = f->GetPrevInFlow();
           if (pif && pif->GetParent() == this) {
             overflowContainers->RemoveFrame(f);
             moveToEOC.AppendFrame(nullptr, f);
           }
           f = next;
         }
         if (overflowContainers->IsEmpty()) {
-          Properties().Delete(OverflowContainersProperty());
+          DeleteProperty(OverflowContainersProperty());
         }
         MergeSortedExcessOverflowContainers(moveToEOC);
       }
     }
   }
 
   // Merge our own overflow frames into our principal child list,
   // except those that are a next-in-flow for one of our items.
@@ -6123,17 +6123,17 @@ nsGridContainerFrame::Reflow(nsPresConte
       gridReflowInput.mColFunctions.NumExplicitTracks(),
       0,
       col,
       Move(colTrackPositions),
       Move(colTrackSizes),
       Move(colTrackStates),
       Move(colRemovedRepeatTracks),
       gridReflowInput.mColFunctions.mRepeatAutoStart);
-    Properties().Set(GridColTrackInfo(), colInfo);
+    SetProperty(GridColTrackInfo(), colInfo);
 
     uint32_t rowTrackCount = gridReflowInput.mRows.mSizes.Length();
     nsTArray<nscoord> rowTrackPositions(rowTrackCount);
     nsTArray<nscoord> rowTrackSizes(rowTrackCount);
     nsTArray<uint32_t> rowTrackStates(rowTrackCount);
     nsTArray<bool> rowRemovedRepeatTracks(
       gridReflowInput.mRowFunctions.mRemovedRepeatTracks);
     uint32_t row = 0;
@@ -6158,26 +6158,26 @@ nsGridContainerFrame::Reflow(nsPresConte
       gridReflowInput.mRowFunctions.NumExplicitTracks(),
       gridReflowInput.mStartRow,
       row,
       Move(rowTrackPositions),
       Move(rowTrackSizes),
       Move(rowTrackStates),
       Move(rowRemovedRepeatTracks),
       gridReflowInput.mRowFunctions.mRepeatAutoStart);
-    Properties().Set(GridRowTrackInfo(), rowInfo);
+    SetProperty(GridRowTrackInfo(), rowInfo);
 
     if (prevInFlow) {
       // This frame is fragmenting rows from a previous frame, so patch up
       // the prior GridRowTrackInfo with a new end row.
 
       // FIXME: This can be streamlined and/or removed when bug 1151204 lands.
 
       ComputedGridTrackInfo* priorRowInfo =
-        prevInFlow->Properties().Get(GridRowTrackInfo());
+        prevInFlow->GetProperty(GridRowTrackInfo());
 
       // Adjust track positions based on the first track in this fragment.
       if (priorRowInfo->mPositions.Length() >
           priorRowInfo->mStartFragmentTrack) {
         nscoord delta =
           priorRowInfo->mPositions[priorRowInfo->mStartFragmentTrack];
         for (nscoord& pos : priorRowInfo->mPositions) {
           pos -= delta;
@@ -6189,17 +6189,17 @@ nsGridContainerFrame::Reflow(nsPresConte
         priorRowInfo->mNumExplicitTracks,
         priorRowInfo->mStartFragmentTrack,
         gridReflowInput.mStartRow,
         Move(priorRowInfo->mPositions),
         Move(priorRowInfo->mSizes),
         Move(priorRowInfo->mStates),
         Move(priorRowInfo->mRemovedRepeatTracks),
         priorRowInfo->mRepeatFirstTrack);
-      prevInFlow->Properties().Set(GridRowTrackInfo(), revisedPriorRowInfo);
+      prevInFlow->SetProperty(GridRowTrackInfo(), revisedPriorRowInfo);
     }
 
     // Generate the line info properties. We need to provide the number of
     // repeat tracks produced in the reflow. Only explicit names are assigned
     // to lines here; the mozilla::dom::GridLines class will later extract
     // implicit names from grid areas and assign them to the appropriate lines.
 
     // Generate column lines first.
@@ -6216,17 +6216,17 @@ nsGridContainerFrame::Reflow(nsPresConte
           col - gridReflowInput.mColFunctions.mExplicitGridOffset);
 
       columnLineNames.AppendElement(explicitNames);
     }
     ComputedGridLineInfo* columnLineInfo = new ComputedGridLineInfo(
       Move(columnLineNames),
       gridColTemplate.mRepeatAutoLineNameListBefore,
       gridColTemplate.mRepeatAutoLineNameListAfter);
-    Properties().Set(GridColumnLineInfo(), columnLineInfo);
+    SetProperty(GridColumnLineInfo(), columnLineInfo);
 
     // Generate row lines next.
     capacity = gridReflowInput.mRows.mSizes.Length();
     const nsStyleGridTemplate& gridRowTemplate =
       gridReflowInput.mGridStyle->mGridTemplateRows;
     nsTArray<nsTArray<nsString>> rowLineNames(capacity);
     for (row = 0; row <= gridReflowInput.mRows.mSizes.Length(); row++) {
       // Offset row by the explicit grid offset, to get the original names.
@@ -6237,35 +6237,35 @@ nsGridContainerFrame::Reflow(nsPresConte
           row - gridReflowInput.mRowFunctions.mExplicitGridOffset);
 
       rowLineNames.AppendElement(explicitNames);
     }
     ComputedGridLineInfo* rowLineInfo = new ComputedGridLineInfo(
       Move(rowLineNames),
       gridRowTemplate.mRepeatAutoLineNameListBefore,
       gridRowTemplate.mRepeatAutoLineNameListAfter);
-    Properties().Set(GridRowLineInfo(), rowLineInfo);
+    SetProperty(GridRowLineInfo(), rowLineInfo);
 
     // Generate area info for explicit areas. Implicit areas are handled
     // elsewhere.
     if (gridReflowInput.mGridStyle->mGridTemplateAreas) {
       nsTArray<css::GridNamedArea>* areas = new nsTArray<css::GridNamedArea>(
           gridReflowInput.mGridStyle->mGridTemplateAreas->mNamedAreas);
-      Properties().Set(ExplicitNamedAreasProperty(), areas);
+      SetProperty(ExplicitNamedAreasProperty(), areas);
     } else {
-      Properties().Delete(ExplicitNamedAreasProperty());
+      DeleteProperty(ExplicitNamedAreasProperty());
     }
   }
 
   if (!prevInFlow) {
-    SharedGridData* sharedGridData = Properties().Get(SharedGridData::Prop());
+    SharedGridData* sharedGridData = GetProperty(SharedGridData::Prop());
     if (!aStatus.IsFullyComplete()) {
       if (!sharedGridData) {
         sharedGridData = new SharedGridData;
-        Properties().Set(SharedGridData::Prop(), sharedGridData);
+        SetProperty(SharedGridData::Prop(), sharedGridData);
       }
       sharedGridData->mCols.mSizes.Clear();
       sharedGridData->mCols.mSizes.SwapElements(gridReflowInput.mCols.mSizes);
       sharedGridData->mCols.mContentBoxSize = gridReflowInput.mCols.mContentBoxSize;
       sharedGridData->mCols.mBaselineSubtreeAlign[0] =
         gridReflowInput.mCols.mBaselineSubtreeAlign[0];
       sharedGridData->mCols.mBaselineSubtreeAlign[1] =
         gridReflowInput.mCols.mBaselineSubtreeAlign[1];
@@ -6290,17 +6290,17 @@ nsGridContainerFrame::Reflow(nsPresConte
       sharedGridData->mGridItems.Clear();
       sharedGridData->mGridItems.SwapElements(gridReflowInput.mGridItems);
       sharedGridData->mAbsPosItems.Clear();
       sharedGridData->mAbsPosItems.SwapElements(gridReflowInput.mAbsPosItems);
 
       sharedGridData->mGenerateComputedGridInfo =
           HasAnyStateBits(NS_STATE_GRID_GENERATE_COMPUTED_VALUES);
     } else if (sharedGridData && !GetNextInFlow()) {
-      Properties().Delete(SharedGridData::Prop());
+      DeleteProperty(SharedGridData::Prop());
     }
   }
 
   FinishAndStoreOverflow(&aDesiredSize);
   NS_FRAME_SET_TRUNCATION(aStatus, aReflowInput, aDesiredSize);
 }
 
 nscoord
@@ -6903,39 +6903,39 @@ nsGridContainerFrame::GetGridFrameWithCo
       }
     }
     return gridFrame;
   };
 
   nsGridContainerFrame* gridFrame = GetGridContainerFrame(aFrame);
   if (gridFrame) {
     // if any of our properties are missing, generate them
-    bool reflowNeeded = (!gridFrame->Properties().Has(GridColTrackInfo()) ||
-                         !gridFrame->Properties().Has(GridRowTrackInfo()) ||
-                         !gridFrame->Properties().Has(GridColumnLineInfo()) ||
-                         !gridFrame->Properties().Has(GridRowLineInfo()));
+    bool reflowNeeded = (!gridFrame->HasProperty(GridColTrackInfo()) ||
+                         !gridFrame->HasProperty(GridRowTrackInfo()) ||
+                         !gridFrame->HasProperty(GridColumnLineInfo()) ||
+                         !gridFrame->HasProperty(GridRowLineInfo()));
 
     if (reflowNeeded) {
       // Trigger a reflow that generates additional grid property data.
       nsIPresShell* shell = gridFrame->PresContext()->PresShell();
       gridFrame->AddStateBits(NS_STATE_GRID_GENERATE_COMPUTED_VALUES);
       shell->FrameNeedsReflow(gridFrame,
                               nsIPresShell::eResize,
                               NS_FRAME_IS_DIRTY);
       shell->FlushPendingNotifications(FlushType::Layout);
 
       // Since the reflow may have side effects, get the grid frame again.
       gridFrame = GetGridContainerFrame(aFrame);
 
       // Assert the grid properties are present
       MOZ_ASSERT(!gridFrame ||
-                  gridFrame->Properties().Has(GridColTrackInfo()));
+                  gridFrame->HasProperty(GridColTrackInfo()));
       MOZ_ASSERT(!gridFrame ||
-                  gridFrame->Properties().Has(GridRowTrackInfo()));
+                  gridFrame->HasProperty(GridRowTrackInfo()));
       MOZ_ASSERT(!gridFrame ||
-                  gridFrame->Properties().Has(GridColumnLineInfo()));
+                  gridFrame->HasProperty(GridColumnLineInfo()));
       MOZ_ASSERT(!gridFrame ||
-                  gridFrame->Properties().Has(GridRowLineInfo()));
+                  gridFrame->HasProperty(GridRowLineInfo()));
     }
   }
 
   return gridFrame;
 }
--- a/layout/generic/nsGridContainerFrame.h
+++ b/layout/generic/nsGridContainerFrame.h
@@ -165,59 +165,59 @@ public:
   /**
    * These properties are created by a call to
    * nsGridContainerFrame::GetGridFrameWithComputedInfo, typically from
    * Element::GetGridFragments.
    */
   NS_DECLARE_FRAME_PROPERTY_DELETABLE(GridColTrackInfo, ComputedGridTrackInfo)
   const ComputedGridTrackInfo* GetComputedTemplateColumns()
   {
-    const ComputedGridTrackInfo* info = Properties().Get(GridColTrackInfo());
+    const ComputedGridTrackInfo* info = GetProperty(GridColTrackInfo());
     MOZ_ASSERT(info, "Property generation wasn't requested.");
     return info;
   }
 
   NS_DECLARE_FRAME_PROPERTY_DELETABLE(GridRowTrackInfo, ComputedGridTrackInfo)
   const ComputedGridTrackInfo* GetComputedTemplateRows()
   {
-    const ComputedGridTrackInfo* info = Properties().Get(GridRowTrackInfo());
+    const ComputedGridTrackInfo* info = GetProperty(GridRowTrackInfo());
     MOZ_ASSERT(info, "Property generation wasn't requested.");
     return info;
   }
 
   NS_DECLARE_FRAME_PROPERTY_DELETABLE(GridColumnLineInfo, ComputedGridLineInfo)
   const ComputedGridLineInfo* GetComputedTemplateColumnLines()
   {
-    const ComputedGridLineInfo* info = Properties().Get(GridColumnLineInfo());
+    const ComputedGridLineInfo* info = GetProperty(GridColumnLineInfo());
     MOZ_ASSERT(info, "Property generation wasn't requested.");
     return info;
   }
 
   NS_DECLARE_FRAME_PROPERTY_DELETABLE(GridRowLineInfo, ComputedGridLineInfo)
   const ComputedGridLineInfo* GetComputedTemplateRowLines()
   {
-    const ComputedGridLineInfo* info = Properties().Get(GridRowLineInfo());
+    const ComputedGridLineInfo* info = GetProperty(GridRowLineInfo());
     MOZ_ASSERT(info, "Property generation wasn't requested.");
     return info;
   }
 
   typedef nsBaseHashtable<nsStringHashKey,
                           mozilla::css::GridNamedArea,
                           mozilla::css::GridNamedArea> ImplicitNamedAreas;
   NS_DECLARE_FRAME_PROPERTY_DELETABLE(ImplicitNamedAreasProperty,
                                       ImplicitNamedAreas)
   ImplicitNamedAreas* GetImplicitNamedAreas() const {
-    return Properties().Get(ImplicitNamedAreasProperty());
+    return GetProperty(ImplicitNamedAreasProperty());
   }
 
   typedef nsTArray<mozilla::css::GridNamedArea> ExplicitNamedAreas;
   NS_DECLARE_FRAME_PROPERTY_DELETABLE(ExplicitNamedAreasProperty,
                                       ExplicitNamedAreas)
   ExplicitNamedAreas* GetExplicitNamedAreas() const {
-    return Properties().Get(ExplicitNamedAreasProperty());
+    return GetProperty(ExplicitNamedAreasProperty());
   }
 
   /**
    * Return a containing grid frame, and ensure it has computed grid info
    * @return nullptr if aFrame has no grid container, or frame was destroyed
    * @note this might destroy layout/style data since it may flush layout
    */
   static nsGridContainerFrame* GetGridFrameWithComputedInfo(nsIFrame* aFrame);
--- a/layout/generic/nsIFrame.h
+++ b/layout/generic/nsIFrame.h
@@ -19,17 +19,17 @@
    going to be eliminated, and all callers will use nsFrame instead.  At the moment
    we're midway through this process, so you will see inlined functions and member
    variables in this file.  -dwh */
 
 #include <algorithm>
 #include <stdio.h>
 
 #include "CaretAssociationHint.h"
-#include "FramePropertyTable.h"
+#include "FrameProperties.h"
 #include "mozilla/layout/FrameChildList.h"
 #include "mozilla/Maybe.h"
 #include "mozilla/SmallPointerArray.h"
 #include "mozilla/WritingModes.h"
 #include "nsDirection.h"
 #include "nsFrameList.h"
 #include "nsFrameState.h"
 #include "mozilla/ReflowOutput.h"
@@ -585,17 +585,16 @@ public:
   using OnNonvisible = mozilla::OnNonvisible;
   template<typename T=void>
   using PropertyDescriptor = const mozilla::FramePropertyDescriptor<T>*;
   using ReflowInput = mozilla::ReflowInput;
   using ReflowOutput = mozilla::ReflowOutput;
   using Visibility = mozilla::Visibility;
 
   typedef mozilla::FrameProperties FrameProperties;
-  typedef mozilla::ConstFrameProperties ConstFrameProperties;
   typedef mozilla::layers::Layer Layer;
   typedef mozilla::layout::FrameChildList ChildList;
   typedef mozilla::layout::FrameChildListID ChildListID;
   typedef mozilla::layout::FrameChildListIDs ChildListIDs;
   typedef mozilla::layout::FrameChildListIterator ChildListIterator;
   typedef mozilla::layout::FrameChildListArrayIterator ChildListArrayIterator;
   typedef mozilla::gfx::DrawTarget DrawTarget;
   typedef mozilla::gfx::Matrix Matrix;
@@ -1127,18 +1126,18 @@ public:
                            "be destroyed by the FramePropertyTable"); \
   }                                                                   \
   NS_DECLARE_FRAME_PROPERTY_WITH_DTOR(prop, type,                     \
                                       AssertOnDestroyingProperty##prop)
 
 #define NS_DECLARE_FRAME_PROPERTY_SMALL_VALUE(prop, type) \
   NS_DECLARE_FRAME_PROPERTY_WITHOUT_DTOR(prop, mozilla::SmallValueHolder<type>)
 
-  NS_DECLARE_FRAME_PROPERTY_WITHOUT_DTOR(IBSplitSibling, nsIFrame)
-  NS_DECLARE_FRAME_PROPERTY_WITHOUT_DTOR(IBSplitPrevSibling, nsIFrame)
+  NS_DECLARE_FRAME_PROPERTY_WITHOUT_DTOR(IBSplitSibling, nsContainerFrame)
+  NS_DECLARE_FRAME_PROPERTY_WITHOUT_DTOR(IBSplitPrevSibling, nsContainerFrame)
 
   NS_DECLARE_FRAME_PROPERTY_DELETABLE(NormalPositionProperty, nsPoint)
   NS_DECLARE_FRAME_PROPERTY_DELETABLE(ComputedOffsetProperty, nsMargin)
 
   NS_DECLARE_FRAME_PROPERTY_DELETABLE(OutlineInnerRectProperty, nsRect)
   NS_DECLARE_FRAME_PROPERTY_DELETABLE(PreEffectsBBoxProperty, nsRect)
   NS_DECLARE_FRAME_PROPERTY_DELETABLE(PreTransformOverflowAreasProperty,
                                       nsOverflowAreas)
@@ -1182,18 +1181,17 @@ public:
   NS_DECLARE_FRAME_PROPERTY_WITH_DTOR(GenConProperty, ContentArray,
                                       DestroyContentArray)
 
   NS_DECLARE_FRAME_PROPERTY_SMALL_VALUE(BidiDataProperty, mozilla::FrameBidiData)
 
   mozilla::FrameBidiData GetBidiData() const
   {
     bool exists;
-    mozilla::FrameBidiData bidiData =
-      Properties().Get(BidiDataProperty(), &exists);
+    mozilla::FrameBidiData bidiData = GetProperty(BidiDataProperty(), &exists);
     if (!exists) {
       bidiData.precedingControl = mozilla::kBidiLevelNone;
     }
     return bidiData;
   }
 
   nsBidiLevel GetBaseLevel() const
   {
@@ -1658,17 +1656,17 @@ public:
   void BuildDisplayListForChild(nsDisplayListBuilder*   aBuilder,
                                 nsIFrame*               aChild,
                                 const nsRect&           aDirtyRect,
                                 const nsDisplayListSet& aLists,
                                 uint32_t                aFlags = 0);
 
   bool RefusedAsyncAnimation() const
   {
-    return Properties().Get(RefusedAsyncAnimationProperty());
+    return GetProperty(RefusedAsyncAnimationProperty());
   }
 
   /**
    * Returns true if this frame is transformed (e.g. has CSS or SVG transforms)
    * or if its parent is an SVG frame that has children-only transforms (e.g.
    * an SVG viewBox attribute) or if its transform-style is preserve-3d or
    * the frame has transform animations.
    *
@@ -3374,22 +3372,81 @@ public:
    *
    * @param aParentContent the content node corresponding to the parent frame
    * @return whether the frame is a pseudo frame
    */
   bool IsPseudoFrame(const nsIContent* aParentContent) {
     return mContent == aParentContent;
   }
 
-  FrameProperties Properties() {
-    return FrameProperties(PresContext()->PropertyTable(), this);
+  /**
+   * Support for reading and writing properties on the frame.
+   * These call through to the frame's FrameProperties object, if it
+   * exists, but avoid creating it if no property is ever set.
+   */
+  template<typename T>
+  FrameProperties::PropertyType<T>
+  GetProperty(FrameProperties::Descriptor<T> aProperty,
+              bool* aFoundResult = nullptr) const
+  {
+    if (mProperties) {
+      return mProperties->Get(aProperty, aFoundResult);
+    }
+    if (aFoundResult) {
+      *aFoundResult = false;
+    }
+    return FrameProperties::ReinterpretHelper<T>::FromPointer(nullptr);
+  }
+
+  template<typename T>
+  bool HasProperty(FrameProperties::Descriptor<T> aProperty) const
+  {
+    if (mProperties) {
+      return mProperties->Has(aProperty);
+    }
+    return false;
   }
 
-  ConstFrameProperties Properties() const {
-    return ConstFrameProperties(PresContext()->PropertyTable(), this);
+  template<typename T>
+  void SetProperty(FrameProperties::Descriptor<T> aProperty,
+                   FrameProperties::PropertyType<T> aValue)
+  {
+    if (!mProperties) {
+      mProperties = mozilla::MakeUnique<FrameProperties>();
+    }
+    mProperties->Set(aProperty, aValue, this);
+  }
+
+  template<typename T>
+  FrameProperties::PropertyType<T>
+  RemoveProperty(FrameProperties::Descriptor<T> aProperty,
+                 bool* aFoundResult = nullptr)
+  {
+    if (mProperties) {
+      return mProperties->Remove(aProperty, aFoundResult);
+    }
+    if (aFoundResult) {
+      *aFoundResult = false;
+    }
+    return FrameProperties::ReinterpretHelper<T>::FromPointer(nullptr);
+  }
+
+  template<typename T>
+  void DeleteProperty(FrameProperties::Descriptor<T> aProperty)
+  {
+    if (mProperties) {
+      mProperties->Delete(aProperty, this);
+    }
+  }
+
+  void DeleteAllProperties()
+  {
+    if (mProperties) {
+      mProperties->DeleteAll(this);
+    }
   }
 
   /**
    * Return true if and only if this frame obeys visibility:hidden.
    * if it does not, then nsContainerFrame will hide its view even though
    * this means children can't be made visible again.
    */
   virtual bool SupportsVisibilityHidden() { return true; }
@@ -3738,26 +3795,26 @@ public:
   static bool IsFrameListSorted(nsFrameList& aFrameList);
 
   /**
    * Return true if aFrame is in an {ib} split and is NOT one of the
    * continuations of the first inline in it.
    */
   bool FrameIsNonFirstInIBSplit() const {
     return (GetStateBits() & NS_FRAME_PART_OF_IBSPLIT) &&
-      FirstContinuation()->Properties().Get(nsIFrame::IBSplitPrevSibling());
+      FirstContinuation()->GetProperty(nsIFrame::IBSplitPrevSibling());
   }
 
   /**
    * Return true if aFrame is in an {ib} split and is NOT one of the
    * continuations of the last inline in it.
    */
   bool FrameIsNonLastInIBSplit() const {
     return (GetStateBits() & NS_FRAME_PART_OF_IBSPLIT) &&
-      FirstContinuation()->Properties().Get(nsIFrame::IBSplitSibling());
+      FirstContinuation()->GetProperty(nsIFrame::IBSplitSibling());
   }
 
   /**
    * Return whether this is a frame whose width is used when computing
    * the font size inflation of its descendants.
    */
   bool IsContainerForFontSizeInflation() const {
     return GetStateBits() & NS_FRAME_FONT_INFLATION_CONTAINER;
@@ -3856,21 +3913,21 @@ private:
   // Stores weak references to all the PresShells that were painted during
   // the last paint event so that we can increment their paint count during
   // empty transactions
   NS_DECLARE_FRAME_PROPERTY_WITH_DTOR(PaintedPresShellsProperty,
                                       nsTArray<nsWeakPtr>,
                                       DestroyPaintedPresShellList)
 
   nsTArray<nsWeakPtr>* PaintedPresShellList() {
-    nsTArray<nsWeakPtr>* list = Properties().Get(PaintedPresShellsProperty());
+    nsTArray<nsWeakPtr>* list = GetProperty(PaintedPresShellsProperty());
 
     if (!list) {
       list = new nsTArray<nsWeakPtr>();
-      Properties().Set(PaintedPresShellsProperty(), list);
+      SetProperty(PaintedPresShellsProperty(), list);
     }
 
     return list;
   }
 
 protected:
   /**
    * Copies aRootElemWM to mWritingMode on 'this' and all its ancestors.
@@ -3882,16 +3939,22 @@ protected:
     // bug 81268
     NS_ASSERTION(!(mState & NS_FRAME_IN_REFLOW), "frame is already in reflow");
 #endif
     mState |= NS_FRAME_IN_REFLOW;
   }
 
   nsFrameState     mState;
 
+  /**
+   * List of properties attached to the frame, or null if no properties have
+   * been set.
+   */
+  mozilla::UniquePtr<FrameProperties> mProperties;
+
   // When there is an overflow area only slightly larger than mRect,
   // we store a set of four 1-byte deltas from the edges of mRect
   // rather than allocating a whole separate rectangle property.
   // Note that these are unsigned values, all measured "outwards"
   // from the edges of mRect, so /mLeft/ and /mTop/ are reversed from
   // our normal coordinate system.
   // If mOverflow.mType == NS_FRAME_OVERFLOW_LARGE, then the
   // delta values are not meaningful and the overflow area is stored
--- a/layout/generic/nsInlineFrame.cpp
+++ b/layout/generic/nsInlineFrame.cpp
@@ -1025,23 +1025,17 @@ nsInlineFrame::DoUpdateStyleOfOwnedAnonB
   // Note: this assert _looks_ expensive, but it's cheap in all the cases when
   // it passes!
   MOZ_ASSERT(nsLayoutUtils::FirstContinuationOrIBSplitSibling(this) == this,
              "Only the primary frame of the inline in a block-inside-inline "
              "split should have NS_FRAME_OWNS_ANON_BOXES");
   MOZ_ASSERT(mContent->GetPrimaryFrame() == this,
              "We should be the primary frame for our element");
 
-  nsPresContext* presContext = PresContext();
-  // Get the FramePropertyTable up front, since we are likely to need it
-  // multiple times.  The other option would be to just call
-  // nsIFrame::Properties() every time we need to do a lookup, but that does
-  // more pointer-chasing.
-  FramePropertyTable* propTable = presContext->PropertyTable();
-  nsIFrame* blockFrame = propTable->Get(this, nsIFrame::IBSplitSibling());
+  nsIFrame* blockFrame = GetProperty(nsIFrame::IBSplitSibling());
   MOZ_ASSERT(blockFrame, "Why did we have an IB split?");
 
   // The anonymous block's style inherits from ours, and we already have our new
   // style context.
   RefPtr<nsStyleContext> newContext =
     aStyleSet.ResolveInheritingAnonymousBoxStyle(
       nsCSSAnonBoxes::mozBlockInsideInlineWrapper, StyleContext());
 
@@ -1062,19 +1056,19 @@ nsInlineFrame::DoUpdateStyleOfOwnedAnonB
     // We _could_ just walk along using GetNextContinuationWithSameStyle here,
     // but it would involve going back to the first continuation every so often,
     // which is a bit silly when we can just keep track of our first
     // continuations.
     for (nsIFrame* cont = blockFrame; cont; cont = cont->GetNextContinuation()) {
       cont->SetStyleContext(newContext);
     }
 
-    nsIFrame* nextInline = propTable->Get(blockFrame, nsIFrame::IBSplitSibling());
+    nsIFrame* nextInline = blockFrame->GetProperty(nsIFrame::IBSplitSibling());
     MOZ_ASSERT(nextInline, "There is always a trailing inline in an IB split");
-    blockFrame = propTable->Get(nextInline, nsIFrame::IBSplitSibling());
+    blockFrame = nextInline->GetProperty(nsIFrame::IBSplitSibling());
   }
 }
 
 //////////////////////////////////////////////////////////////////////
 
 // nsLineFrame implementation
 
 nsFirstLineFrame*
--- a/layout/generic/nsLineLayout.cpp
+++ b/layout/generic/nsLineLayout.cpp
@@ -796,21 +796,21 @@ nsLineLayout::ReflowFrame(nsIFrame* aFra
 #ifdef REALLY_NOISY_REFLOW
   nsFrame::IndentBy(stdout, mSpanDepth);
   printf("%p: Begin ReflowFrame pfd=%p ", psd, pfd);
   nsFrame::ListTag(stdout, aFrame);
   printf("\n");
 #endif
 
   if (mCurrentSpan == mRootSpan) {
-    pfd->mFrame->Properties().Remove(nsIFrame::LineBaselineOffset());
+    pfd->mFrame->RemoveProperty(nsIFrame::LineBaselineOffset());
   } else {
 #ifdef DEBUG
     bool hasLineOffset;
-    pfd->mFrame->Properties().Get(nsIFrame::LineBaselineOffset(), &hasLineOffset);
+    pfd->mFrame->GetProperty(nsIFrame::LineBaselineOffset(), &hasLineOffset);
     NS_ASSERTION(!hasLineOffset, "LineBaselineOffset was set but was not expected");
 #endif
   }
 
   mJustificationInfo = JustificationInfo();
 
   // Stash copies of some of the computed state away for later
   // (block-direction alignment, for example)
--- a/layout/generic/nsPlaceholderFrame.cpp
+++ b/layout/generic/nsPlaceholderFrame.cpp
@@ -123,17 +123,17 @@ nsPlaceholderFrame::Reflow(nsPresContext
     // Unfortunately, this can currently happen when the placeholder is in a
     // later continuation or later IB-split sibling than its out-of-flow (as
     // is the case in some of our existing unit tests). So for now, in that
     // case, we'll warn instead of asserting.
     bool isInContinuationOrIBSplit = false;
     nsIFrame* ancestor = this;
     while ((ancestor = ancestor->GetParent())) {
       if (ancestor->GetPrevContinuation() ||
-          ancestor->Properties().Get(IBSplitPrevSibling())) {
+          ancestor->GetProperty(IBSplitPrevSibling())) {
         isInContinuationOrIBSplit = true;
         break;
       }
     }
 
     if (isInContinuationOrIBSplit) {
       NS_WARNING("Out-of-flow frame got reflowed before its placeholder");
     } else {
--- a/layout/generic/nsTextFrame.cpp
+++ b/layout/generic/nsTextFrame.cpp
@@ -2836,19 +2836,19 @@ nsTextFrame::EnsureTextRun(TextRunType a
     }
     textRun = GetTextRun(aWhichTextRun);
     if (!textRun) {
       // A text run was not constructed for this frame. This is bad. The caller
       // will check mTextRun.
       return gfxSkipCharsIterator(gfxPlatform::
                                   GetPlatform()->EmptySkipChars(), 0);
     }
-    TabWidthStore* tabWidths = Properties().Get(TabWidthProperty());
+    TabWidthStore* tabWidths = GetProperty(TabWidthProperty());
     if (tabWidths && tabWidths->mValidForContentOffset != GetContentOffset()) {
-      Properties().Delete(TabWidthProperty());
+      DeleteProperty(TabWidthProperty());
     }
   }
 
   if (textRun->GetFlags2() & nsTextFrameUtils::Flags::TEXT_IS_SIMPLE_FLOW) {
     if (aFlowEndInTextRun) {
       *aFlowEndInTextRun = textRun->GetLength();
     }
     return gfxSkipCharsIterator(textRun->GetSkipChars(), 0, mContentOffset);
@@ -3488,17 +3488,17 @@ PropertyProvider::CalcTabWidths(Range aR
 
   if (!mTabWidths) {
     if (mReflowing && !mLineContainer) {
       // Intrinsic width computation does its own tab processing. We
       // just don't do anything here.
       return;
     }
     if (!mReflowing) {
-      mTabWidths = mFrame->Properties().Get(TabWidthProperty());
+      mTabWidths = mFrame->GetProperty(TabWidthProperty());
 #ifdef DEBUG
       // If we're not reflowing, we should have already computed the
       // tab widths; check that they're available as far as the last
       // tab character present (if any)
       for (uint32_t i = aRange.end; i > aRange.start; --i) {
         if (mTextRun->CharIsTab(i - 1)) {
           uint32_t startOffset = mStart.GetSkippedOffset();
           NS_ASSERTION(mTabWidths && mTabWidths->mLimit + startOffset >= i,
@@ -3533,17 +3533,17 @@ PropertyProvider::CalcTabWidths(Range aR
             ++clusterEnd;
           }
           mOffsetFromBlockOriginForTabs +=
             mTextRun->GetAdvanceWidth(Range(i, clusterEnd), nullptr);
         }
       } else {
         if (!mTabWidths) {
           mTabWidths = new TabWidthStore(mFrame->GetContentOffset());
-          mFrame->Properties().Set(TabWidthProperty(), mTabWidths);
+          mFrame->SetProperty(TabWidthProperty(), mTabWidths);
         }
         double nextTab = AdvanceToNextTab(mOffsetFromBlockOriginForTabs,
                                           aTabWidth);
         mTabWidths->mWidths.AppendElement(TabWidth(i - startOffset,
                 NSToIntRound(nextTab - mOffsetFromBlockOriginForTabs)));
         mOffsetFromBlockOriginForTabs = nextTab;
       }
 
@@ -3552,17 +3552,17 @@ PropertyProvider::CalcTabWidths(Range aR
 
     if (mTabWidths) {
       mTabWidths->mLimit = aRange.end - startOffset;
     }
   }
 
   if (!mTabWidths) {
     // Delete any stale property that may be left on the frame
-    mFrame->Properties().Delete(TabWidthProperty());
+    mFrame->DeleteProperty(TabWidthProperty());
     mTabWidthsAnalyzedLimit = std::max(mTabWidthsAnalyzedLimit,
                                        aRange.end - startOffset);
   }
 }
 
 gfxFloat
 PropertyProvider::GetHyphenWidth() const
 {
@@ -4360,17 +4360,17 @@ nsTextFrame::ClearFrameOffsetCache()
   // See if we need to remove ourselves from the offset cache
   if (GetStateBits() & TEXT_IN_OFFSET_CACHE) {
     nsIFrame* primaryFrame = mContent->GetPrimaryFrame();
     if (primaryFrame) {
       // The primary frame might be null here.  For example, nsLineBox::DeleteLineList
       // just destroys the frames in order, which means that the primary frame is already
       // dead if we're a continuing text frame, in which case, all of its properties are
       // gone, and we don't need to worry about deleting this property here.
-      primaryFrame->Properties().Delete(OffsetToFrameProperty());
+      primaryFrame->DeleteProperty(OffsetToFrameProperty());
     }
     RemoveStateBits(TEXT_IN_OFFSET_CACHE);
   }
 }
 
 void
 nsTextFrame::DestroyFrom(nsIFrame* aDestructRoot)
 {
@@ -4474,17 +4474,17 @@ nsContinuingTextFrame::Init(nsIContent* 
       if (uninflatedTextRun) {
         SetTextRun(uninflatedTextRun, nsTextFrame::eNotInflated, 1.0f);
       }
     }
   }
   if (aPrevInFlow->GetStateBits() & NS_FRAME_IS_BIDI) {
     FrameBidiData bidiData = aPrevInFlow->GetBidiData();
     bidiData.precedingControl = kBidiLevelNone;
-    Properties().Set(BidiDataProperty(), bidiData);
+    SetProperty(BidiDataProperty(), bidiData);
 
     if (nextContinuation) {
       SetNextContinuation(nextContinuation);
       nextContinuation->SetPrevContinuation(this);
       // Adjust next-continuations' content offset as needed.
       while (nextContinuation &&
              nextContinuation->GetContentOffset() < mContentOffset) {
 #ifdef DEBUG
@@ -4705,17 +4705,17 @@ nsTextFrame::InvalidateFrameWithRect(con
     return;
   }
   nsFrame::InvalidateFrameWithRect(aRect, aDisplayItemKey);
 }
 
 gfxTextRun*
 nsTextFrame::GetUninflatedTextRun()
 {
-  return Properties().Get(UninflatedTextRunProperty());
+  return GetProperty(UninflatedTextRunProperty());
 }
 
 void
 nsTextFrame::SetTextRun(gfxTextRun* aTextRun, TextRunType aWhichTextRun,
                         float aInflation)
 {
   NS_ASSERTION(aTextRun, "must have text run");
 
@@ -4731,17 +4731,17 @@ nsTextFrame::SetTextRun(gfxTextRun* aTex
     }
     SetFontSizeInflation(aInflation);
   } else {
     MOZ_ASSERT(aInflation == 1.0f, "unexpected inflation");
     if (HasFontSizeInflation()) {
       // Setting the property will not automatically increment the textrun's
       // reference count, so we need to do it here.
       aTextRun->AddRef();
-      Properties().Set(UninflatedTextRunProperty(), aTextRun);
+      SetProperty(UninflatedTextRunProperty(), aTextRun);
       return;
     }
     // fall through to setting mTextRun
   }
 
   mTextRun = aTextRun;
 
   // FIXME: Add assertions testing the relationship between
@@ -4751,20 +4751,19 @@ nsTextFrame::SetTextRun(gfxTextRun* aTex
 
 bool
 nsTextFrame::RemoveTextRun(gfxTextRun* aTextRun)
 {
   if (aTextRun == mTextRun) {
     mTextRun = nullptr;
     return true;
   }
-  FrameProperties props = Properties();
   if ((GetStateBits() & TEXT_HAS_FONT_INFLATION) &&
-      props.Get(UninflatedTextRunProperty()) == aTextRun) {
-    props.Delete(UninflatedTextRunProperty());
+      GetProperty(UninflatedTextRunProperty()) == aTextRun) {
+    DeleteProperty(UninflatedTextRunProperty());
     return true;
   }
   return false;
 }
 
 void
 nsTextFrame::ClearTextRun(nsTextFrame* aStartContinuation,
                           TextRunType aWhichTextRun)
@@ -4772,27 +4771,27 @@ nsTextFrame::ClearTextRun(nsTextFrame* a
   RefPtr<gfxTextRun> textRun = GetTextRun(aWhichTextRun);
   if (!textRun) {
     return;
   }
 
   DebugOnly<bool> checkmTextrun = textRun == mTextRun;
   UnhookTextRunFromFrames(textRun, aStartContinuation);
   MOZ_ASSERT(checkmTextrun ? !mTextRun
-                           : !Properties().Get(UninflatedTextRunProperty()));
+                           : !GetProperty(UninflatedTextRunProperty()));
 }
 
 void
 nsTextFrame::DisconnectTextRuns()
 {
   MOZ_ASSERT(!IsInTextRunUserData(),
              "Textrun mentions this frame in its user data so we can't just disconnect");
   mTextRun = nullptr;
   if ((GetStateBits() & TEXT_HAS_FONT_INFLATION)) {
-    Properties().Delete(UninflatedTextRunProperty());
+    DeleteProperty(UninflatedTextRunProperty());
   }
 }
 
 nsresult
 nsTextFrame::CharacterDataChanged(CharacterDataChangeInfo* aInfo)
 {
   mContent->DeleteProperty(nsGkAtoms::newline);
   if (PresContext()->BidiEnabled()) {
@@ -5066,17 +5065,17 @@ nsDisplayText::ComputeInvalidationRegion
   }
 }
 
 NS_DECLARE_FRAME_PROPERTY_SMALL_VALUE(TextCombineScaleFactorProperty, float)
 
 static float
 GetTextCombineScaleFactor(nsTextFrame* aFrame)
 {
-  float factor = aFrame->Properties().Get(TextCombineScaleFactorProperty());
+  float factor = aFrame->GetProperty(TextCombineScaleFactorProperty());
   return factor ? factor : 1.0f;
 }
 
 nsDisplayText::nsDisplayText(nsDisplayListBuilder* aBuilder, nsTextFrame* aFrame,
                              const Maybe<bool>& aIsSelected)
   : nsCharClipDisplayItem(aBuilder, aFrame)
   , mOpacity(1.0f)
   , mDisableSubpixelAA(false)
@@ -5330,34 +5329,34 @@ PaintSelectionBackground(DrawTarget& aDr
 }
 
 // Attempt to get the LineBaselineOffset property of aChildFrame
 // If not set, calculate this value for all child frames of aBlockFrame
 static nscoord
 LazyGetLineBaselineOffset(nsIFrame* aChildFrame, nsBlockFrame* aBlockFrame)
 {
   bool offsetFound;
-  nscoord offset = aChildFrame->Properties().Get(
+  nscoord offset = aChildFrame->GetProperty(
     nsIFrame::LineBaselineOffset(), &offsetFound);
 
   if (!offsetFound) {
     for (nsBlockFrame::LineIterator line = aBlockFrame->LinesBegin(),
                                     line_end = aBlockFrame->LinesEnd();
          line != line_end; line++) {
       if (line->IsInline()) {
         int32_t n = line->GetChildCount();
         nscoord lineBaseline = line->BStart() + line->GetLogicalAscent();
         for (nsIFrame* lineFrame = line->mFirstChild;
              n > 0; lineFrame = lineFrame->GetNextSibling(), --n) {
           offset = lineBaseline - lineFrame->GetNormalPosition().y;
-          lineFrame->Properties().Set(nsIFrame::LineBaselineOffset(), offset);
+          lineFrame->SetProperty(nsIFrame::LineBaselineOffset(), offset);
         }
       }
     }
-    return aChildFrame->Properties().Get(
+    return aChildFrame->GetProperty(
       nsIFrame::LineBaselineOffset(), &offsetFound);
   } else {
     return offset;
   }
 }
 
 static bool IsUnderlineRight(nsIFrame* aFrame)
 {
@@ -5594,17 +5593,17 @@ FindFurthestInlineRubyAncestor(nsTextFra
   return rubyFrame;
 }
 
 nsRect
 nsTextFrame::UpdateTextEmphasis(WritingMode aWM, PropertyProvider& aProvider)
 {
   const nsStyleText* styleText = StyleText();
   if (!styleText->HasTextEmphasis()) {
-    Properties().Delete(EmphasisMarkProperty());
+    DeleteProperty(EmphasisMarkProperty());
     return nsRect();
   }
 
   nsStyleContext* styleContext = StyleContext();
   bool isTextCombined = styleContext->IsTextCombined();
   if (isTextCombined) {
     styleContext = GetParent()->StyleContext();
   }
@@ -5646,17 +5645,17 @@ nsTextFrame::UpdateTextEmphasis(WritingM
     overflowRect.BStart(aWM) = frameSize.BSize(aWM) + leadings.mEnd;
   }
   // If text combined, fix the gap between the text frame and its parent.
   if (isTextCombined) {
     nscoord gap = (baseFontMetrics->MaxHeight() - frameSize.BSize(aWM)) / 2;
     overflowRect.BStart(aWM) += gap * (side == eLogicalSideBStart ? -1 : 1);
   }
 
-  Properties().Set(EmphasisMarkProperty(), info);
+  SetProperty(EmphasisMarkProperty(), info);
   return overflowRect.GetPhysicalRect(aWM, frameSize.GetPhysicalSize(aWM));
 }
 
 void
 nsTextFrame::UnionAdditionalOverflow(nsPresContext* aPresContext,
                                      nsIFrame* aBlock,
                                      PropertyProvider& aProvider,
                                      nsRect* aVisualOverflowRect,
@@ -6663,17 +6662,17 @@ nsTextFrame::PaintTextWithSelection(
 
 void
 nsTextFrame::DrawEmphasisMarks(gfxContext* aContext, WritingMode aWM,
                                const gfxPoint& aTextBaselinePt,
                                const gfxPoint& aFramePt, Range aRange,
                                const nscolor* aDecorationOverrideColor,
                                PropertyProvider* aProvider)
 {
-  const EmphasisMarkInfo* info = Properties().Get(EmphasisMarkProperty());
+  const EmphasisMarkInfo* info = GetProperty(EmphasisMarkProperty());
   if (!info) {
     return;
   }
 
   bool isTextCombined = StyleContext()->IsTextCombined();
   nscolor color = aDecorationOverrideColor ? *aDecorationOverrideColor :
     nsLayoutUtils::GetColor(this, &nsStyleText::mTextEmphasisColor);
   aContext->SetColor(Color::FromABGR(color));
@@ -7808,17 +7807,17 @@ nsTextFrame::GetChildFrameContainingOffs
     return primaryFrame->GetChildFrameContainingOffset(aContentOffset, aHint,
                                                        aOutOffset, aOutFrame);
   }
 
   nsTextFrame* f = this;
   int32_t offset = mContentOffset;
 
   // Try to look up the offset to frame property
-  nsTextFrame* cachedFrame = Properties().Get(OffsetToFrameProperty());
+  nsTextFrame* cachedFrame = GetProperty(OffsetToFrameProperty());
 
   if (cachedFrame) {
     f = cachedFrame;
     offset = f->GetContentOffset();
 
     f->RemoveStateBits(TEXT_IN_OFFSET_CACHE);
   }
 
@@ -7856,17 +7855,17 @@ nsTextFrame::GetChildFrameContainingOffs
       f = prev;
     }
   }
 
   *aOutOffset = aContentOffset - f->GetContentOffset();
   *aOutFrame = f;
 
   // cache the frame we found
-  Properties().Set(OffsetToFrameProperty(), f);
+  SetProperty(OffsetToFrameProperty(), f);
   f->AddStateBits(TEXT_IN_OFFSET_CACHE);
 
   return NS_OK;
 }
 
 nsIFrame::FrameSearchResult
 nsTextFrame::PeekOffsetNoAmount(bool aForward, int32_t* aOffset)
 {
@@ -8390,32 +8389,32 @@ FindStartAfterSkippingWhitespace(Propert
 }
 
 float
 nsTextFrame::GetFontSizeInflation() const
 {
   if (!HasFontSizeInflation()) {
     return 1.0f;
   }
-  return Properties().Get(FontSizeInflationProperty());
+  return GetProperty(FontSizeInflationProperty());
 }
 
 void
 nsTextFrame::SetFontSizeInflation(float aInflation)
 {
   if (aInflation == 1.0f) {
     if (HasFontSizeInflation()) {
       RemoveStateBits(TEXT_HAS_FONT_INFLATION);
-      Properties().Delete(FontSizeInflationProperty());
+      DeleteProperty(FontSizeInflationProperty());
     }
     return;
   }
 
   AddStateBits(TEXT_HAS_FONT_INFLATION);
-  Properties().Set(FontSizeInflationProperty(), aInflation);
+  SetProperty(FontSizeInflationProperty(), aInflation);
 }
 
 /* virtual */
 void nsTextFrame::MarkIntrinsicISizesDirty()
 {
   ClearTextRuns();
   nsFrame::MarkIntrinsicISizesDirty();
 }
@@ -9545,19 +9544,19 @@ nsTextFrame::ReflowText(nsLineLayout& aL
     finalSize.BSize(wm) = aMetrics.BlockStartAscent() + descent;
   }
   if (StyleContext()->IsTextCombined()) {
     nsFontMetrics* fm = provider.GetFontMetrics();
     gfxFloat width = finalSize.ISize(wm);
     gfxFloat em = fm->EmHeight();
     // Compress the characters in horizontal axis if necessary.
     if (width <= em) {
-      Properties().Remove(TextCombineScaleFactorProperty());
+      RemoveProperty(TextCombineScaleFactorProperty());
     } else {
-      Properties().Set(TextCombineScaleFactorProperty(), em / width);
+      SetProperty(TextCombineScaleFactorProperty(), em / width);
       finalSize.ISize(wm) = em;
     }
     // Make the characters be in an 1em square.
     if (finalSize.BSize(wm) != em) {
       aMetrics.SetBlockStartAscent(aMetrics.BlockStartAscent() +
                                    (em - finalSize.BSize(wm)) / 2);
       finalSize.BSize(wm) = em;
     }
@@ -10273,23 +10272,23 @@ NS_DECLARE_FRAME_PROPERTY_SMALL_VALUE(Ju
 void
 nsTextFrame::AssignJustificationGaps(
     const mozilla::JustificationAssignment& aAssign)
 {
   int32_t encoded = (aAssign.mGapsAtStart << 8) | aAssign.mGapsAtEnd;
   static_assert(sizeof(aAssign) == 1,
                 "The encoding might be broken if JustificationAssignment "
                 "is larger than 1 byte");
-  Properties().Set(JustificationAssignmentProperty(), encoded);
+  SetProperty(JustificationAssignmentProperty(), encoded);
 }
 
 mozilla::JustificationAssignment
 nsTextFrame::GetJustificationAssignment() const
 {
-  int32_t encoded = Properties().Get(JustificationAssignmentProperty());
+  int32_t encoded = GetProperty(JustificationAssignmentProperty());
   mozilla::JustificationAssignment result;
   result.mGapsAtStart = encoded >> 8;
   result.mGapsAtEnd = encoded & 0xFF;
   return result;
 }
 
 uint32_t
 nsTextFrame::CountGraphemeClusters() const
--- a/layout/mathml/nsMathMLContainerFrame.cpp
+++ b/layout/mathml/nsMathMLContainerFrame.cpp
@@ -127,30 +127,30 @@ NS_DECLARE_FRAME_PROPERTY_DELETABLE(HTML
 
 /* static */ void
 nsMathMLContainerFrame::SaveReflowAndBoundingMetricsFor(nsIFrame*                  aFrame,
                                                         const ReflowOutput& aReflowOutput,
                                                         const nsBoundingMetrics&   aBoundingMetrics)
 {
   ReflowOutput* reflowOutput = new ReflowOutput(aReflowOutput);
   reflowOutput->mBoundingMetrics = aBoundingMetrics;
-  aFrame->Properties().Set(HTMLReflowOutputProperty(), reflowOutput);
+  aFrame->SetProperty(HTMLReflowOutputProperty(), reflowOutput);
 }
 
 // helper method to facilitate getting the reflow and bounding metrics
 /* static */ void
 nsMathMLContainerFrame::GetReflowAndBoundingMetricsFor(nsIFrame*            aFrame,
                                                        ReflowOutput& aReflowOutput,
                                                        nsBoundingMetrics&   aBoundingMetrics,
                                                        eMathMLFrameType*    aMathMLFrameType)
 {
   NS_PRECONDITION(aFrame, "null arg");
 
   ReflowOutput* reflowOutput =
-    aFrame->Properties().Get(HTMLReflowOutputProperty());
+    aFrame->GetProperty(HTMLReflowOutputProperty());
 
   // IMPORTANT: This function is only meant to be called in Place() methods
   // where it is assumed that SaveReflowAndBoundingMetricsFor has recorded the
   // information.
   NS_ASSERTION(reflowOutput, "Didn't SaveReflowAndBoundingMetricsFor frame!");
   if (reflowOutput) {
     aReflowOutput = *reflowOutput;
     aBoundingMetrics = reflowOutput->mBoundingMetrics;
@@ -168,19 +168,18 @@ nsMathMLContainerFrame::GetReflowAndBoun
   }
 
 }
 
 void
 nsMathMLContainerFrame::ClearSavedChildMetrics()
 {
   nsIFrame* childFrame = mFrames.FirstChild();
-  FramePropertyTable* props = PresContext()->PropertyTable();
   while (childFrame) {
-    props->Delete(childFrame, HTMLReflowOutputProperty());
+    childFrame->DeleteProperty(HTMLReflowOutputProperty());
     childFrame = childFrame->GetNextSibling();
   }
 }
 
 // helper to get the preferred size that a container frame should use to fire
 // the stretch on its stretchy child frames.
 void
 nsMathMLContainerFrame::GetPreferredStretchSize(DrawTarget*          aDrawTarget,
--- a/layout/mathml/nsMathMLmtableFrame.cpp
+++ b/layout/mathml/nsMathMLmtableFrame.cpp
@@ -162,18 +162,17 @@ AttributeToProperty(nsIAtom* aAttribute)
 static nsTArray<int8_t>*
 FindCellProperty(const nsIFrame* aCellFrame,
                  const FramePropertyDescriptor<nsTArray<int8_t>>* aFrameProperty)
 {
   const nsIFrame* currentFrame = aCellFrame;
   nsTArray<int8_t>* propertyData = nullptr;
 
   while (currentFrame) {
-    ConstFrameProperties props = currentFrame->Properties();
-    propertyData = props.Get(aFrameProperty);
+    propertyData = currentFrame->GetProperty(aFrameProperty);
     bool frameIsTable = (currentFrame->IsTableFrame());
 
     if (propertyData || frameIsTable)
       currentFrame = nullptr; // A null frame pointer exits the loop
     else
       currentFrame = currentFrame->GetParent(); // Go to the parent frame
   }
 
@@ -356,18 +355,17 @@ ParseFrameAttribute(nsIFrame* aFrame,
     nsTArray<int8_t>* valueList =
       ExtractStyleValues(attrValue, aAttribute, aAllowMultiValues);
 
     // If valueList is null, that indicates a problem with the attribute value.
     // Only set properties on a valid attribute value.
     if (valueList) {
       // The code reading the property assumes that this list is nonempty.
       NS_ASSERTION(valueList->Length() >= 1, "valueList should not be empty!");
-      FrameProperties props = aFrame->Properties();
-      props.Set(AttributeToProperty(aAttribute), valueList);
+      aFrame->SetProperty(AttributeToProperty(aAttribute), valueList);
     } else {
       ReportParseError(aFrame, aAttribute->GetUTF16String(), attrValue.get());
     }
   }
 }
 
 // rowspacing
 //
@@ -764,18 +762,17 @@ nsMathMLmtableWrapperFrame::AttributeCha
       ParseSpacingAttribute(mathMLmtableFrame, aAttribute);
       mathMLmtableFrame->SetUseCSSSpacing();
     }
   } else if (aAttribute == nsGkAtoms::rowalign_ ||
              aAttribute == nsGkAtoms::rowlines_ ||
              aAttribute == nsGkAtoms::columnalign_ ||
              aAttribute == nsGkAtoms::columnlines_) {
     // clear any cached property list for this table
-    presContext->PropertyTable()->
-      Delete(tableFrame, AttributeToProperty(aAttribute));
+    tableFrame->DeleteProperty(AttributeToProperty(aAttribute));
     // Reparse the new attribute on the table.
     ParseFrameAttribute(tableFrame, aAttribute, true);
   } else {
     // Ignore attributes that do not affect layout.
     return NS_OK;
   }
 
   // Explicitly request a reflow in our subtree to pick up any changes
@@ -1116,17 +1113,17 @@ nsMathMLmtrFrame::AttributeChanged(int32
 
   nsPresContext* presContext = PresContext();
 
   if (aAttribute != nsGkAtoms::rowalign_ &&
       aAttribute != nsGkAtoms::columnalign_) {
     return NS_OK;
   }
 
-  presContext->PropertyTable()->Delete(this, AttributeToProperty(aAttribute));
+  DeleteProperty(AttributeToProperty(aAttribute));
 
   bool allowMultiValues = (aAttribute == nsGkAtoms::columnalign_);
 
   // Reparse the new attribute.
   ParseFrameAttribute(this, aAttribute, allowMultiValues);
 
   // Explicitly request a reflow in our subtree to pick up any changes
   presContext->PresShell()->
@@ -1215,18 +1212,17 @@ nsMathMLmtdFrame::AttributeChanged(int32
   // rowalign    : here
   // columnalign : here
   // rowspan     : here
   // columnspan  : here
 
   if (aAttribute == nsGkAtoms::rowalign_ ||
       aAttribute == nsGkAtoms::columnalign_) {
 
-    nsPresContext* presContext = PresContext();
-    presContext->PropertyTable()->Delete(this, AttributeToProperty(aAttribute));
+    DeleteProperty(AttributeToProperty(aAttribute));
 
     // Reparse the attribute.
     ParseFrameAttribute(this, aAttribute, false);
     return NS_OK;
   }
 
   if (aAttribute == nsGkAtoms::rowspan ||
       aAttribute == nsGkAtoms::columnspan_) {
--- a/layout/painting/ActiveLayerTracker.cpp
+++ b/layout/painting/ActiveLayerTracker.cpp
@@ -174,66 +174,63 @@ LayerActivityTracker::NotifyExpired(Laye
 
   if (f) {
     // The pres context might have been detached during the delay -
     // that's fine, just skip the paint.
     if (f->PresContext()->GetContainerWeak()) {
       f->SchedulePaint();
     }
     f->RemoveStateBits(NS_FRAME_HAS_LAYER_ACTIVITY_PROPERTY);
-    f->Properties().Delete(LayerActivityProperty());
+    f->DeleteProperty(LayerActivityProperty());
   } else {
     c->DeleteProperty(nsGkAtoms::LayerActivity);
   }
 }
 
 static LayerActivity*
 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 {
     if (!gLayerActivityTracker) {
       gLayerActivityTracker = new LayerActivityTracker(
         SystemGroup::EventTargetFor(TaskCategory::Other));
     }
     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;
 }
 
 static void
 IncrementMutationCount(uint8_t* aCount)
 {
   *aCount = uint8_t(std::min(0xFF, *aCount + 1));
 }
 
 /* static */ void
 ActiveLayerTracker::TransferActivityToContent(nsIFrame* aFrame, nsIContent* aContent)
 {
   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;
   }
   layerActivity->mFrame = nullptr;
   layerActivity->mContent = aContent;
   aContent->SetProperty(nsGkAtoms::LayerActivity, layerActivity,
                         nsINode::DeleteProperty<LayerActivity>, true);
@@ -245,17 +242,17 @@ ActiveLayerTracker::TransferActivityToFr
   LayerActivity* layerActivity = static_cast<LayerActivity*>(
     aContent->UnsetProperty(nsGkAtoms::LayerActivity));
   if (!layerActivity) {
     return;
   }
   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
 IncrementScaleRestyleCountIfNeeded(nsIFrame* aFrame, LayerActivity* aActivity)
 {
   const nsStyleDisplay* display = aFrame->StyleDisplay();
   if (!display->mSpecifiedTransform) {
     // The transform was removed.
--- a/layout/painting/nsCSSRendering.cpp
+++ b/layout/painting/nsCSSRendering.cpp
@@ -290,39 +290,39 @@ protected:
   }
 
   nsIFrame* GetPrevContinuation(nsIFrame* aFrame)
   {
     nsIFrame* prevCont = aFrame->GetPrevContinuation();
     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?");
       }
     }
     return prevCont;
   }
 
   nsIFrame* GetNextContinuation(nsIFrame* aFrame)
   {
     nsIFrame* nextCont = aFrame->GetNextContinuation();
     if (!nextCont &&
         (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?");
       }
     }
     return nextCont;
   }
 
   void Init(nsIFrame* aFrame)
   {
@@ -935,17 +935,17 @@ nsCSSRendering::CreateBorderRendererWith
   }
   return Some(br);
 }
 
 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");
   return nsRect(nsPoint(0, 0), aFrame->GetSize());
 }
 
 Maybe<nsCSSBorderRenderer>
 nsCSSRendering::CreateBorderRendererForOutline(nsPresContext* aPresContext,
--- a/layout/painting/nsDisplayList.cpp
+++ b/layout/painting/nsDisplayList.cpp
@@ -803,17 +803,17 @@ nsDisplayListBuilder::AddAnimationsAndTr
 
   // If the frame is not prerendered, bail out.
   // Do this check only during layer construction; during updating the
   // caller is required to check it appropriately.
   if (aItem && !aItem->CanUseAsyncAnimations(aBuilder)) {
     // 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.
     aFrame->SchedulePaint();
     return;
   }
 
   AnimationData data;
@@ -1084,25 +1084,23 @@ void nsDisplayListBuilder::MarkOutOfFlow
 
   // mClipState.GetClipChainForContainingBlockDescendants can return pointers
   // to objects on the stack, so we need to clone the chain.
   const DisplayItemClipChain* clipChain =
     CopyWholeChain(mClipState.GetClipChainForContainingBlockDescendants());
   const DisplayItemClipChain* combinedClipChain = mClipState.GetCurrentCombinedClipChain(this);
   const ActiveScrolledRoot* asr = mCurrentActiveScrolledRoot;
   OutOfFlowDisplayData* data = new OutOfFlowDisplayData(clipChain, combinedClipChain, asr, 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)) {
     if (!(f->GetStateBits() & NS_FRAME_FORCE_DISPLAY_LIST_DESCEND_INTO))
       return;
     f->RemoveStateBits(NS_FRAME_FORCE_DISPLAY_LIST_DESCEND_INTO);
   }
 }
--- a/layout/painting/nsDisplayList.h
+++ b/layout/painting/nsDisplayList.h
@@ -1245,17 +1245,17 @@ public:
     nsRect mDirtyRect;
   };
 
   NS_DECLARE_FRAME_PROPERTY_DELETABLE(OutOfFlowDisplayDataProperty,
                                       OutOfFlowDisplayData)
 
   static OutOfFlowDisplayData* GetOutOfFlowData(nsIFrame* aFrame)
   {
-    return aFrame->Properties().Get(OutOfFlowDisplayDataProperty());
+    return aFrame->GetProperty(OutOfFlowDisplayDataProperty());
   }
 
   nsPresContext* CurrentPresContext() {
     return CurrentPresShellState()->mPresShell->GetPresContext();
   }
 
   OutOfFlowDisplayData* GetCurrentFixedBackgroundDisplayData()
   {
--- a/layout/svg/SVGTextFrame.cpp
+++ b/layout/svg/SVGTextFrame.cpp
@@ -1324,17 +1324,17 @@ NS_DECLARE_FRAME_PROPERTY_DELETABLE(Text
 
 /**
  * Returns the number of undisplayed characters before the specified
  * nsTextFrame.
  */
 static uint32_t
 GetUndisplayedCharactersBeforeFrame(nsTextFrame* aFrame)
 {
-  void* value = aFrame->Properties().Get(TextNodeCorrespondenceProperty());
+  void* value = aFrame->GetProperty(TextNodeCorrespondenceProperty());
   TextNodeCorrespondence* correspondence =
     static_cast<TextNodeCorrespondence*>(value);
   if (!correspondence) {
     NS_NOTREACHED("expected a TextNodeCorrespondenceProperty on nsTextFrame "
                   "used for SVG text");
     return 0;
   }
   return correspondence->mUndisplayedCharacters;
@@ -1513,18 +1513,18 @@ TextNodeCorrespondenceRecorder::Traverse
     }
     // If the current text frame starts at a non-zero content offset, then those
     // earlier characters are also undisplayed.
     undisplayed += frame->GetContentOffset();
     NextNode();
   }
 
   // Set the frame property.
-  frame->Properties().Set(TextNodeCorrespondenceProperty(),
-                          new TextNodeCorrespondence(undisplayed));
+  frame->SetProperty(TextNodeCorrespondenceProperty(),
+                     new TextNodeCorrespondence(undisplayed));
 
   // Remember how far into the current nsTextNode we are.
   mNodeCharIndex = frame->GetContentEnd();
 }
 
 // ----------------------------------------------------------------------------
 // TextFrameIterator
 
@@ -3395,17 +3395,17 @@ SVGTextFrame::HandleAttributeChangeInDes
         aAttribute == nsGkAtoms::startOffset) {
       NotifyGlyphMetricsChange();
     } else if ((aNameSpaceID == kNameSpaceID_XLink ||
                 aNameSpaceID == kNameSpaceID_None) &&
                aAttribute == nsGkAtoms::href) {
       // Blow away our reference, if any
       nsIFrame* childElementFrame = aElement->GetPrimaryFrame();
       if (childElementFrame) {
-        childElementFrame->Properties().Delete(
+        childElementFrame->DeleteProperty(
           nsSVGEffects::HrefAsTextPathProperty());
         NotifyGlyphMetricsChange();
       }
     }
   } else {
     if (aNameSpaceID == kNameSpaceID_None &&
         IsGlyphPositioningAttribute(aAttribute)) {
       NotifyGlyphMetricsChange();
@@ -4821,17 +4821,17 @@ SVGTextFrame::AdjustPositionsForClusters
     it.Next();
   }
 }
 
 SVGPathElement*
 SVGTextFrame::GetTextPathPathElement(nsIFrame* aTextPathFrame)
 {
   nsSVGTextPathProperty *property =
-    aTextPathFrame->Properties().Get(nsSVGEffects::HrefAsTextPathProperty());
+    aTextPathFrame->GetProperty(nsSVGEffects::HrefAsTextPathProperty());
 
   if (!property) {
     nsIContent* content = aTextPathFrame->GetContent();
     dom::SVGTextPathElement* tp = static_cast<dom::SVGTextPathElement*>(content);
     nsAutoString href;
     if (tp->mStringAttributes[dom::SVGTextPathElement::HREF].IsExplicitlySet()) {
       tp->mStringAttributes[dom::SVGTextPathElement::HREF]
         .GetAnimValue(href, tp);
--- a/layout/svg/nsSVGEffects.cpp
+++ b/layout/svg/nsSVGEffects.cpp
@@ -477,55 +477,53 @@ nsSVGPaintingProperty::DoUpdate()
 
 static nsSVGFilterProperty*
 GetOrCreateFilterProperty(nsIFrame* aFrame)
 {
   const nsStyleEffects* effects = aFrame->StyleEffects();
   if (!effects->HasFilters())
     return nullptr;
 
-  FrameProperties props = aFrame->Properties();
-  nsSVGFilterProperty *prop = props.Get(nsSVGEffects::FilterProperty());
+  nsSVGFilterProperty *prop =
+    aFrame->GetProperty(nsSVGEffects::FilterProperty());
   if (prop)
     return prop;
   prop = new nsSVGFilterProperty(effects->mFilters, aFrame);
   NS_ADDREF(prop);
-  props.Set(nsSVGEffects::FilterProperty(), prop);
+  aFrame->SetProperty(nsSVGEffects::FilterProperty(), prop);
   return prop;
 }
 
 static nsSVGMaskProperty*
 GetOrCreateMaskProperty(nsIFrame* aFrame)
 {
-  FrameProperties props = aFrame->Properties();
-  nsSVGMaskProperty *prop = props.Get(nsSVGEffects::MaskProperty());
+  nsSVGMaskProperty *prop = aFrame->GetProperty(nsSVGEffects::MaskProperty());
   if (prop)
     return prop;
 
   prop = new nsSVGMaskProperty(aFrame);
   NS_ADDREF(prop);
-  props.Set(nsSVGEffects::MaskProperty(), prop);
+  aFrame->SetProperty(nsSVGEffects::MaskProperty(), prop);
   return prop;
 }
 
 template<class T>
 static T*
 GetEffectProperty(nsIURI* aURI, nsIFrame* aFrame,
   const mozilla::FramePropertyDescriptor<T>* aProperty)
 {
   if (!aURI)
     return nullptr;
 
-  FrameProperties props = aFrame->Properties();
-  T* prop = props.Get(aProperty);
+  T* prop = aFrame->GetProperty(aProperty);
   if (prop)
     return prop;
   prop = new T(aURI, aFrame, false);
   NS_ADDREF(prop);
-  props.Set(aProperty, prop);
+  aFrame->SetProperty(aProperty, prop);
   return prop;
 }
 
 nsSVGMarkerProperty*
 nsSVGEffects::GetMarkerProperty(nsIURI* aURI, nsIFrame* aFrame,
   const mozilla::FramePropertyDescriptor<nsSVGMarkerProperty>* aProperty)
 {
   MOZ_ASSERT(aFrame->IsSVGGeometryFrame() &&
@@ -550,21 +548,21 @@ nsSVGEffects::GetPaintingProperty(nsIURI
 
 nsSVGPaintingProperty*
 nsSVGEffects::GetPaintingPropertyForURI(nsIURI* aURI, nsIFrame* aFrame,
   URIObserverHashtablePropertyDescriptor aProperty)
 {
   if (!aURI)
     return nullptr;
 
-  FrameProperties props = aFrame->Properties();
-  nsSVGEffects::URIObserverHashtable *hashtable = props.Get(aProperty);
+  nsSVGEffects::URIObserverHashtable *hashtable =
+    aFrame->GetProperty(aProperty);
   if (!hashtable) {
     hashtable = new nsSVGEffects::URIObserverHashtable();
-    props.Set(aProperty, hashtable);
+    aFrame->SetProperty(aProperty, hashtable);
   }
   nsSVGPaintingProperty* prop =
     static_cast<nsSVGPaintingProperty*>(hashtable->GetWeak(aURI));
   if (!prop) {
     bool watchImage = aProperty == nsSVGEffects::BackgroundImageProperty();
     prop = new nsSVGPaintingProperty(aURI, aFrame, watchImage);
     hashtable->Put(aURI, prop);
   }
@@ -706,26 +704,25 @@ nsSVGEffects::EffectProperties::HasNoOrV
 }
 
 void
 nsSVGEffects::UpdateEffects(nsIFrame* aFrame)
 {
   NS_ASSERTION(aFrame->GetContent()->IsElement(),
                "aFrame's content should be an element");
 
-  FrameProperties props = aFrame->Properties();
-  props.Delete(FilterProperty());
-  props.Delete(MaskProperty());
-  props.Delete(ClipPathProperty());
-  props.Delete(MarkerBeginProperty());
-  props.Delete(MarkerMiddleProperty());
-  props.Delete(MarkerEndProperty());
-  props.Delete(FillProperty());
-  props.Delete(StrokeProperty());
-  props.Delete(BackgroundImageProperty());
+  aFrame->DeleteProperty(FilterProperty());
+  aFrame->DeleteProperty(MaskProperty());
+  aFrame->DeleteProperty(ClipPathProperty());
+  aFrame->DeleteProperty(MarkerBeginProperty());
+  aFrame->DeleteProperty(MarkerMiddleProperty());
+  aFrame->DeleteProperty(MarkerEndProperty());
+  aFrame->DeleteProperty(FillProperty());
+  aFrame->DeleteProperty(StrokeProperty());
+  aFrame->DeleteProperty(BackgroundImageProperty());
 
   // Ensure that the filter is repainted correctly
   // We can't do that in DoUpdate as the referenced frame may not be valid
   GetOrCreateFilterProperty(aFrame);
 
   if (aFrame->IsSVGGeometryFrame() &&
       static_cast<SVGGeometryElement*>(aFrame->GetContent())->IsMarkable()) {
     // Set marker properties here to avoid reference loops
@@ -742,17 +739,17 @@ nsSVGEffects::UpdateEffects(nsIFrame* aF
 nsSVGFilterProperty*
 nsSVGEffects::GetFilterProperty(nsIFrame* aFrame)
 {
   NS_ASSERTION(!aFrame->GetPrevContinuation(), "aFrame should be first continuation");
 
   if (!aFrame->StyleEffects()->HasFilters())
     return nullptr;
 
-  return aFrame->Properties().Get(FilterProperty());
+  return aFrame->GetProperty(FilterProperty());
 }
 
 void
 nsSVGRenderingObserverList::InvalidateAll()
 {
   if (mObservers.Count() == 0)
     return;
 
@@ -852,17 +849,17 @@ nsSVGEffects::InvalidateRenderingObserve
 {
   NS_ASSERTION(!aFrame->GetPrevContinuation(), "aFrame must be first continuation");
 
   nsIContent* content = aFrame->GetContent();
   if (!content || !content->IsElement())
     return;
 
   // If the rendering has changed, the bounds may well have changed too:
-  aFrame->Properties().Delete(nsSVGUtils::ObjectBoundingBoxProperty());
+  aFrame->DeleteProperty(nsSVGUtils::ObjectBoundingBoxProperty());
 
   nsSVGRenderingObserverList *observerList =
     GetObserverList(content->AsElement());
   if (observerList) {
     observerList->InvalidateAll();
     return;
   }
 
@@ -881,17 +878,17 @@ nsSVGEffects::InvalidateRenderingObserve
 }
 
 void
 nsSVGEffects::InvalidateDirectRenderingObservers(Element* aElement, uint32_t aFlags /* = 0 */)
 {
   nsIFrame* frame = aElement->GetPrimaryFrame();
   if (frame) {
     // If the rendering has changed, the bounds may well have changed too:
-    frame->Properties().Delete(nsSVGUtils::ObjectBoundingBoxProperty());
+    frame->DeleteProperty(nsSVGUtils::ObjectBoundingBoxProperty());
   }
 
   if (aElement->HasRenderingObservers()) {
     nsSVGRenderingObserverList *observerList = GetObserverList(aElement);
     if (observerList) {
       if (aFlags & INVALIDATE_REFLOW) {
         observerList->InvalidateAllForReflow();
       } else {
--- a/layout/svg/nsSVGEffects.h
+++ b/layout/svg/nsSVGEffects.h
@@ -2,17 +2,17 @@
 /* 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 NSSVGEFFECTS_H_
 #define NSSVGEFFECTS_H_
 
 #include "mozilla/Attributes.h"
-#include "FramePropertyTable.h"
+#include "FrameProperties.h"
 #include "mozilla/dom/Element.h"
 #include "nsHashKeys.h"
 #include "nsID.h"
 #include "nsIFrame.h"
 #include "nsIMutationObserver.h"
 #include "nsInterfaceHashtable.h"
 #include "nsISupportsBase.h"
 #include "nsISupportsImpl.h"
--- a/layout/svg/nsSVGFilterFrame.cpp
+++ b/layout/svg/nsSVGFilterFrame.cpp
@@ -112,17 +112,17 @@ nsSVGFilterFrame::GetFilterContent(nsICo
 
 nsSVGFilterFrame *
 nsSVGFilterFrame::GetReferencedFilter()
 {
   if (mNoHRefURI)
     return nullptr;
 
   nsSVGPaintingProperty *property =
-    Properties().Get(nsSVGEffects::HrefAsPaintingProperty());
+    GetProperty(nsSVGEffects::HrefAsPaintingProperty());
 
   if (!property) {
     // Fetch our Filter element's href or xlink:href attribute
     SVGFilterElement *filter = static_cast<SVGFilterElement *>(mContent);
     nsAutoString href;
     if (filter->mStringAttributes[SVGFilterElement::HREF].IsExplicitlySet()) {
       filter->mStringAttributes[SVGFilterElement::HREF]
         .GetAnimValue(href, filter);
@@ -172,17 +172,17 @@ nsSVGFilterFrame::AttributeChanged(int32
        aAttribute == nsGkAtoms::height ||
        aAttribute == nsGkAtoms::filterUnits ||
        aAttribute == nsGkAtoms::primitiveUnits)) {
     nsSVGEffects::InvalidateDirectRenderingObservers(this);
   } else if ((aNameSpaceID == kNameSpaceID_XLink ||
               aNameSpaceID == kNameSpaceID_None) &&
              aAttribute == nsGkAtoms::href) {
     // Blow away our reference, if any
-    Properties().Delete(nsSVGEffects::HrefAsPaintingProperty());
+    DeleteProperty(nsSVGEffects::HrefAsPaintingProperty());
     mNoHRefURI = false;
     // And update whoever references us
     nsSVGEffects::InvalidateDirectRenderingObservers(this);
   }
   return nsSVGContainerFrame::AttributeChanged(aNameSpaceID,
                                                aAttribute, aModType);
 }
 
--- a/layout/svg/nsSVGGradientFrame.cpp
+++ b/layout/svg/nsSVGGradientFrame.cpp
@@ -46,17 +46,17 @@ nsSVGGradientFrame::AttributeChanged(int
       (aAttribute == nsGkAtoms::gradientUnits ||
        aAttribute == nsGkAtoms::gradientTransform ||
        aAttribute == nsGkAtoms::spreadMethod)) {
     nsSVGEffects::InvalidateDirectRenderingObservers(this);
   } else if ((aNameSpaceID == kNameSpaceID_XLink ||
               aNameSpaceID == kNameSpaceID_None) &&
              aAttribute == nsGkAtoms::href) {
     // Blow away our reference, if any
-    Properties().Delete(nsSVGEffects::HrefAsPaintingProperty());
+    DeleteProperty(nsSVGEffects::HrefAsPaintingProperty());
     mNoHRefURI = false;
     // And update whoever references us
     nsSVGEffects::InvalidateDirectRenderingObservers(this);
   }
 
   return nsSVGPaintServerFrame::AttributeChanged(aNameSpaceID,
                                                  aAttribute, aModType);
 }
@@ -330,17 +330,17 @@ nsSVGGradientFrame::GetPaintServerPatter
 
 nsSVGGradientFrame *
 nsSVGGradientFrame::GetReferencedGradient()
 {
   if (mNoHRefURI)
     return nullptr;
 
   nsSVGPaintingProperty *property =
-    Properties().Get(nsSVGEffects::HrefAsPaintingProperty());
+    GetProperty(nsSVGEffects::HrefAsPaintingProperty());
 
   if (!property) {
     // Fetch our gradient element's href or xlink:href attribute
     dom::SVGGradientElement* grad =
       static_cast<dom::SVGGradientElement*>(mContent);
     nsAutoString href;
     if (grad->mStringAttributes[dom::SVGGradientElement::HREF]
           .IsExplicitlySet()) {
--- a/layout/svg/nsSVGIntegrationUtils.cpp
+++ b/layout/svg/nsSVGIntegrationUtils.cpp
@@ -76,34 +76,34 @@ public:
   nsRect GetResult() const {
     return mResult;
   }
 
 private:
 
   static nsRect GetPreEffectsVisualOverflowRect(nsIFrame* aFrame,
                                                 bool aInReflow) {
-    nsRect* r = aFrame->Properties().Get(nsIFrame::PreEffectsBBoxProperty());
+    nsRect* r = aFrame->GetProperty(nsIFrame::PreEffectsBBoxProperty());
     if (r) {
       return *r;
     }
 
 #ifdef DEBUG
     // Having PreTransformOverflowAreasProperty cached means
     // GetVisualOverflowRect() will return post-effect rect, which is not what
     // we want. This function intentional reports pre-effect rect. But it does
     // not matter if there is no SVG effect on this frame, since no effect
     // means post-effect rect matches pre-effect rect.
     //
     // This function may be called during reflow or painting. We should only
     // do this check in painting process since the PreEffectsBBoxProperty of
     // continuations are not set correctly while reflowing.
     if (nsSVGIntegrationUtils::UsingEffectsForFrame(aFrame) && !aInReflow) {
       nsOverflowAreas* preTransformOverflows =
-        aFrame->Properties().Get(aFrame->PreTransformOverflowAreasProperty());
+        aFrame->GetProperty(aFrame->PreTransformOverflowAreasProperty());
 
       MOZ_ASSERT(!preTransformOverflows,
                  "GetVisualOverflowRect() won't return the pre-effects rect!");
     }
 #endif
     return aFrame->GetVisualOverflowRect();
   }
 
--- a/layout/svg/nsSVGPatternFrame.cpp
+++ b/layout/svg/nsSVGPatternFrame.cpp
@@ -63,17 +63,17 @@ nsSVGPatternFrame::AttributeChanged(int3
        aAttribute == nsGkAtoms::viewBox)) {
     nsSVGEffects::InvalidateDirectRenderingObservers(this);
   }
 
   if ((aNameSpaceID == kNameSpaceID_XLink ||
        aNameSpaceID == kNameSpaceID_None) &&
       aAttribute == nsGkAtoms::href) {
     // Blow away our reference, if any
-    Properties().Delete(nsSVGEffects::HrefAsPaintingProperty());
+    DeleteProperty(nsSVGEffects::HrefAsPaintingProperty());
     mNoHRefURI = false;
     // And update whoever references us
     nsSVGEffects::InvalidateDirectRenderingObservers(this);
   }
 
   return nsSVGPaintServerFrame::AttributeChanged(aNameSpaceID,
                                                  aAttribute, aModType);
 }
@@ -572,17 +572,17 @@ nsSVGPatternFrame::GetLengthValue(uint32
 // Private (helper) methods
 nsSVGPatternFrame *
 nsSVGPatternFrame::GetReferencedPattern()
 {
   if (mNoHRefURI)
     return nullptr;
 
   nsSVGPaintingProperty *property =
-    Properties().Get(nsSVGEffects::HrefAsPaintingProperty());
+    GetProperty(nsSVGEffects::HrefAsPaintingProperty());
 
   if (!property) {
     // Fetch our pattern element's href or xlink:href attribute
     SVGPatternElement *pattern = static_cast<SVGPatternElement *>(mContent);
     nsAutoString href;
     if (pattern->mStringAttributes[SVGPatternElement::HREF].IsExplicitlySet()) {
       pattern->mStringAttributes[SVGPatternElement::HREF]
         .GetAnimValue(href, pattern);
--- a/layout/svg/nsSVGUtils.cpp
+++ b/layout/svg/nsSVGUtils.cpp
@@ -1121,22 +1121,20 @@ nsSVGUtils::GetBBox(nsIFrame* aFrame, ui
   MOZ_ASSERT(svg);
 
   nsIContent* content = aFrame->GetContent();
   if (content->IsSVGElement() &&
       !static_cast<const nsSVGElement*>(content)->HasValidDimensions()) {
     return gfxRect();
   }
 
-  FrameProperties props = aFrame->Properties();
-
   if (aFlags == eBBoxIncludeFillGeometry &&
       // We only cache bbox in element's own user space
       !aToBoundsSpace) {
-    gfxRect* prop = props.Get(ObjectBoundingBoxProperty());
+    gfxRect* prop = aFrame->GetProperty(ObjectBoundingBoxProperty());
     if (prop) {
       return *prop;
     }
   }
 
   gfxMatrix matrix;
   if (aToBoundsSpace) {
     matrix = *aToBoundsSpace;
@@ -1205,17 +1203,17 @@ nsSVGUtils::GetBBox(nsIFrame* aFrame, ui
     }
   }
 
   if (aFlags == eBBoxIncludeFillGeometry &&
       // We only cache bbox in element's own user space
       !aToBoundsSpace) {
     // Obtaining the bbox for objectBoundingBox calculations is common so we
     // cache the result for future calls, since calculation can be expensive:
-    props.Set(ObjectBoundingBoxProperty(), new gfxRect(bbox));
+    aFrame->SetProperty(ObjectBoundingBoxProperty(), new gfxRect(bbox));
   }
 
   return bbox;
 }
 
 gfxPoint
 nsSVGUtils::FrameSpaceInCSSPxToUserSpaceOffset(nsIFrame *aFrame)
 {
--- a/layout/tables/nsTableFrame.cpp
+++ b/layout/tables/nsTableFrame.cpp
@@ -262,23 +262,22 @@ nsTableFrame::RegisterPositionedTablePar
     }
   }
 
   nsTableFrame* tableFrame = nsTableFrame::GetTableFrame(aFrame);
   MOZ_ASSERT(tableFrame, "Should have a table frame here");
   tableFrame = static_cast<nsTableFrame*>(tableFrame->FirstContinuation());
 
   // Retrieve the positioned parts array for this table.
-  FrameProperties props = tableFrame->Properties();
-  FrameTArray* positionedParts = props.Get(PositionedTablePartArray());
+  FrameTArray* positionedParts = tableFrame->GetProperty(PositionedTablePartArray());
 
   // Lazily create the array if it doesn't exist yet.
   if (!positionedParts) {
     positionedParts = new FrameTArray;
-    props.Set(PositionedTablePartArray(), positionedParts);
+    tableFrame->SetProperty(PositionedTablePartArray(), positionedParts);
   }
 
   // Add this frame to the list.
   positionedParts->AppendElement(aFrame);
 }
 
 /* static */ void
 nsTableFrame::UnregisterPositionedTablePart(nsIFrame* aFrame,
@@ -292,18 +291,17 @@ nsTableFrame::UnregisterPositionedTableP
     // The table frame will be destroyed, and it's the first im flow (and thus
     // owning the PositionedTablePartArray), so we don't need to do
     // anything.
     return;
   }
   tableFrame = static_cast<nsTableFrame*>(tableFrame->FirstContinuation());
 
   // Retrieve the positioned parts array for this table.
-  FrameProperties props = tableFrame->Properties();
-  FrameTArray* positionedParts = props.Get(PositionedTablePartArray());
+  FrameTArray* positionedParts = tableFrame->GetProperty(PositionedTablePartArray());
 
   // Remove the frame.
   MOZ_ASSERT(positionedParts && positionedParts->Contains(aFrame),
              "Asked to unregister a positioned table part that wasn't registered");
   if (positionedParts) {
     positionedParts->RemoveElement(aFrame);
   }
 }
@@ -2121,17 +2119,17 @@ nsTableFrame::Reflow(nsPresContext*     
   NS_FRAME_SET_TRUNCATION(aStatus, aReflowInput, aDesiredSize);
 }
 
 void
 nsTableFrame::FixupPositionedTableParts(nsPresContext*           aPresContext,
                                         ReflowOutput&     aDesiredSize,
                                         const ReflowInput& aReflowInput)
 {
-  FrameTArray* positionedParts = Properties().Get(PositionedTablePartArray());
+  FrameTArray* positionedParts = GetProperty(PositionedTablePartArray());
   if (!positionedParts) {
     return;
   }
 
   OverflowChangedTracker overflowTracker;
   overflowTracker.SetSubtreeRoot(this);
 
   for (size_t i = 0; i < positionedParts->Length(); ++i) {
@@ -2784,27 +2782,26 @@ nsTableFrame::GetUsedMargin() const
   return nsMargin(0, 0, 0, 0);
 }
 
 NS_DECLARE_FRAME_PROPERTY_DELETABLE(TableBCProperty, BCPropertyData)
 
 BCPropertyData*
 nsTableFrame::GetBCProperty() const
 {
-  return Properties().Get(TableBCProperty());
+  return GetProperty(TableBCProperty());
 }
 
 BCPropertyData*
 nsTableFrame::GetOrCreateBCProperty()
 {
-  FrameProperties props = Properties();
-  BCPropertyData* value = props.Get(TableBCProperty());
+  BCPropertyData* value = GetProperty(TableBCProperty());
   if (!value) {
     value = new BCPropertyData();
-    props.Set(TableBCProperty(), value);
+    SetProperty(TableBCProperty(), value);
   }
 
   return value;
 }
 
 static void
 DivideBCBorderSize(BCPixelSize  aPixelSize,
                    BCPixelSize& aSmallHalf,
@@ -4448,17 +4445,17 @@ struct BCMapCellInfo
 
 };
 
 
 BCMapCellInfo::BCMapCellInfo(nsTableFrame* aTableFrame)
   : mTableFrame(aTableFrame)
   , mNumTableRows(aTableFrame->GetRowCount())
   , mNumTableCols(aTableFrame->GetColCount())
-  , mTableBCData(mTableFrame->Properties().Get(TableBCProperty()))
+  , mTableBCData(mTableFrame->GetProperty(TableBCProperty()))
   , mTableWM(aTableFrame->StyleContext())
 {
   ResetCellInfo();
 }
 
 void
 BCMapCellInfo::ResetCellInfo()
 {
--- a/layout/tables/nsTableRowFrame.cpp
+++ b/layout/tables/nsTableRowFrame.cpp
@@ -937,17 +937,17 @@ nsTableRowFrame::ReflowChildren(nsPresCo
       if (kidReflowInput) {
         // We reflowed. Apply relative positioning in the normal way.
         kidReflowInput->ApplyRelativePositioning(&kidPosition, containerSize);
       } else if (kidFrame->IsRelativelyPositioned()) {
         // We didn't reflow.  Do the positioning part of what
         // MovePositionBy does internally.  (This codepath should really
         // be merged into the else below if we can.)
         nsMargin* computedOffsetProp =
-          kidFrame->Properties().Get(nsIFrame::ComputedOffsetProperty());
+          kidFrame->GetProperty(nsIFrame::ComputedOffsetProperty());
         // Bug 975644: a position:sticky kid can end up with a null
         // property value here.
         LogicalMargin computedOffsets(wm, computedOffsetProp ?
                                             *computedOffsetProp : nsMargin());
         ReflowInput::ApplyRelativePositioning(kidFrame, wm, computedOffsets,
                                                     &kidPosition, containerSize);
       }
 
@@ -1371,26 +1371,24 @@ nsTableRowFrame::GetNextRow() const
 
 NS_DECLARE_FRAME_PROPERTY_SMALL_VALUE(RowUnpaginatedHeightProperty, nscoord)
 
 void
 nsTableRowFrame::SetUnpaginatedBSize(nsPresContext* aPresContext,
                                      nscoord        aValue)
 {
   NS_ASSERTION(!GetPrevInFlow(), "program error");
-  // Get the property
-  aPresContext->PropertyTable()->
-    Set(this, RowUnpaginatedHeightProperty(), aValue);
+  // Set the property
+  SetProperty(RowUnpaginatedHeightProperty(), aValue);
 }
 
 nscoord
 nsTableRowFrame::GetUnpaginatedBSize()
 {
-  FrameProperties props = FirstInFlow()->Properties();
-  return props.Get(RowUnpaginatedHeightProperty());
+  return GetProperty(RowUnpaginatedHeightProperty());
 }
 
 void nsTableRowFrame::SetContinuousBCBorderWidth(LogicalSide aForSide,
                                                  BCPixelSize aPixelValue)
 {
   switch (aForSide) {
     case eLogicalSideIEnd:
       mIEndContBorderWidth = aPixelValue;
--- a/layout/tables/nsTableRowGroupFrame.cpp
+++ b/layout/tables/nsTableRowGroupFrame.cpp
@@ -1897,17 +1897,17 @@ NS_DECLARE_FRAME_PROPERTY_DELETABLE(RowC
 void
 nsTableRowGroupFrame::ClearRowCursor()
 {
   if (!HasAnyStateBits(NS_ROWGROUP_HAS_ROW_CURSOR)) {
     return;
   }
 
   RemoveStateBits(NS_ROWGROUP_HAS_ROW_CURSOR);
-  Properties().Delete(RowCursorProperty());
+  DeleteProperty(RowCursorProperty());
 }
 
 nsTableRowGroupFrame::FrameCursorData*
 nsTableRowGroupFrame::SetupRowCursor()
 {
   if (HasAnyStateBits(NS_ROWGROUP_HAS_ROW_CURSOR)) {
     // We already have a valid row cursor. Don't waste time rebuilding it.
     return nullptr;
@@ -1921,29 +1921,29 @@ nsTableRowGroupFrame::SetupRowCursor()
   if (!f) {
     // Less than MIN_ROWS_NEEDING_CURSOR rows, so just don't bother
     return nullptr;
   }
 
   FrameCursorData* data = new FrameCursorData();
   if (!data)
     return nullptr;
-  Properties().Set(RowCursorProperty(), data);
+  SetProperty(RowCursorProperty(), data);
   AddStateBits(NS_ROWGROUP_HAS_ROW_CURSOR);
   return data;
 }
 
 nsIFrame*
 nsTableRowGroupFrame::GetFirstRowContaining(nscoord aY, nscoord* aOverflowAbove)
 {
   if (!HasAnyStateBits(NS_ROWGROUP_HAS_ROW_CURSOR)) {
     return nullptr;
   }
 
-  FrameCursorData* property = Properties().Get(RowCursorProperty());
+  FrameCursorData* property = GetProperty(RowCursorProperty());
   uint32_t cursorIndex = property->mCursorIndex;
   uint32_t frameCount = property->mFrames.Length();
   if (cursorIndex >= frameCount)
     return nullptr;
   nsIFrame* cursorFrame = property->mFrames[cursorIndex];
 
   // The cursor's frame list excludes frames with empty overflow-area, so
   // we don't need to check that here.
--- a/layout/tables/nsTableWrapperFrame.cpp
+++ b/layout/tables/nsTableWrapperFrame.cpp
@@ -245,17 +245,17 @@ nsTableWrapperFrame::InitChildReflowInpu
     if (InnerTableFrame()->IsBorderCollapse()) {
       LogicalMargin border = InnerTableFrame()->GetIncludedOuterBCBorder(wm);
       collapseBorder = border.GetPhysicalMargin(wm);
       pCollapseBorder = &collapseBorder;
       pCollapsePadding = &collapsePadding;
     }
     // Propagate our stored CB size if present, minus any margins.
     if (!HasAnyStateBits(NS_FRAME_OUT_OF_FLOW)) {
-      LogicalSize* cb = Properties().Get(GridItemCBSizeProperty());
+      LogicalSize* cb = GetProperty(GridItemCBSizeProperty());
       if (cb) {
         cbSize.emplace(*cb);
         *cbSize -= aReflowInput.ComputedLogicalMargin().Size(wm);
       }
     }
   }
   aReflowInput.Init(&aPresContext, cbSize.ptrOr(nullptr), pCollapseBorder,
                     pCollapsePadding);
@@ -951,17 +951,17 @@ nsTableWrapperFrame::Reflow(nsPresContex
     captionSize.ISize(wm) = captionMet->ISize(wm);
     captionSize.BSize(wm) = captionMet->BSize(wm);
     captionMargin =
       captionRI->ComputedLogicalMargin().ConvertTo(wm, captionWM);
     // Now that we know the bsize of the caption, reduce the available bsize
     // for the table frame if we are bsize constrained and the caption is above
     // or below the inner table.  Also reduce the CB size that we store for
     // our children in case we're a grid item, by the same amount.
-    LogicalSize* cbSize = Properties().Get(GridItemCBSizeProperty());
+    LogicalSize* cbSize = GetProperty(GridItemCBSizeProperty());
     if (NS_UNCONSTRAINEDSIZE != aOuterRI.AvailableBSize() || cbSize) {
       nscoord captionBSize = 0;
       nscoord captionISize = 0;
       switch (captionSide) {
         case NS_STYLE_CAPTION_SIDE_TOP:
         case NS_STYLE_CAPTION_SIDE_BOTTOM:
         case NS_STYLE_CAPTION_SIDE_TOP_OUTSIDE:
         case NS_STYLE_CAPTION_SIDE_BOTTOM_OUTSIDE:
--- a/layout/xul/nsBox.cpp
+++ b/layout/xul/nsBox.cpp
@@ -139,20 +139,19 @@ nsBox::BeginXULLayout(nsBoxLayoutState& 
     // does this too).
     nsIFrame* box;
     for (box = GetChildXULBox(this); box; box = GetNextXULBox(box))
       box->AddStateBits(NS_FRAME_IS_DIRTY);
   }
 
   // Another copy-over from ReflowInput.
   // Since we are in reflow, we don't need to store these properties anymore.
-  FrameProperties props = Properties();
-  props.Delete(UsedBorderProperty());
-  props.Delete(UsedPaddingProperty());
-  props.Delete(UsedMarginProperty());
+  DeleteProperty(UsedBorderProperty());
+  DeleteProperty(UsedPaddingProperty());
+  DeleteProperty(UsedMarginProperty());
 
 #ifdef DEBUG_LAYOUT
   PropagateDebug(aState);
 #endif
 
   return NS_OK;
 }
 
--- a/layout/xul/nsMenuFrame.cpp
+++ b/layout/xul/nsMenuFrame.cpp
@@ -258,44 +258,44 @@ nsMenuFrame::GetPopup()
 }
 
 nsFrameList*
 nsMenuFrame::GetPopupList() const
 {
   if (!HasPopup()) {
     return nullptr;
   }
-  nsFrameList* prop = Properties().Get(PopupListProperty());
+  nsFrameList* prop = GetProperty(PopupListProperty());
   NS_ASSERTION(prop && prop->GetLength() == 1 &&
                prop->FirstChild()->IsMenuPopupFrame(),
                "popup list should have exactly one nsMenuPopupFrame");
   return prop;
 }
 
 void
 nsMenuFrame::DestroyPopupList()
 {
   NS_ASSERTION(HasPopup(), "huh?");
-  nsFrameList* prop = Properties().Remove(PopupListProperty());
+  nsFrameList* prop = RemoveProperty(PopupListProperty());
   NS_ASSERTION(prop && prop->IsEmpty(),
                "popup list must exist and be empty when destroying");
   RemoveStateBits(NS_STATE_MENU_HAS_POPUP_LIST);
   prop->Delete(PresContext()->PresShell());
 }
 
 void
 nsMenuFrame::SetPopupFrame(nsFrameList& aFrameList)
 {
   for (nsFrameList::Enumerator e(aFrameList); !e.AtEnd(); e.Next()) {
     nsMenuPopupFrame* popupFrame = do_QueryFrame(e.get());
     if (popupFrame) {
       // Remove the frame from the list and store it in a nsFrameList* property.
       aFrameList.RemoveFrame(popupFrame);
       nsFrameList* popupList = new (PresContext()->PresShell()) nsFrameList(popupFrame, popupFrame);
-      Properties().Set(PopupListProperty(), popupList);
+      SetProperty(PopupListProperty(), popupList);
       AddStateBits(NS_STATE_MENU_HAS_POPUP_LIST);
       break;
     }
   }
 }
 
 void
 nsMenuFrame::SetInitialChildList(ChildListID     aListID,