Bug 1573938 - Never collect wrapper JSObjects when recording/replaying, r=mccr8.
authorBrian Hackett <bhackett1024@gmail.com>
Fri, 16 Aug 2019 20:51:12 +0000
changeset 488604 de8dd803924f6502ea2bcb021b4c9395291dd54d
parent 488603 b66eec7d2e55d339b4c6fedc7cb1a3bed409557d
child 488605 a73fd0e7e3dad2b92f2febc57610debfa53e5533
push id113914
push usernbeleuzu@mozilla.com
push dateSat, 17 Aug 2019 21:54:15 +0000
treeherdermozilla-inbound@addba1395642 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmccr8
bugs1573938
milestone70.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 1573938 - Never collect wrapper JSObjects when recording/replaying, r=mccr8. Differential Revision: https://phabricator.services.mozilla.com/D42011
dom/base/nsWrapperCache.cpp
dom/base/nsWrapperCache.h
dom/bindings/BindingUtils.h
dom/xbl/nsXBLBinding.cpp
js/xpconnect/src/SandboxPrivate.h
js/xpconnect/src/XPCWrappedNative.cpp
js/xpconnect/src/XPCWrappedNativeProto.cpp
mfbt/RecordReplay.cpp
mfbt/RecordReplay.h
toolkit/recordreplay/ProcessRecordReplay.cpp
toolkit/recordreplay/ProcessRecordReplay.h
toolkit/recordreplay/Trigger.cpp
toolkit/recordreplay/Trigger.h
toolkit/recordreplay/WeakPointer.cpp
toolkit/recordreplay/WeakPointer.h
toolkit/recordreplay/ipc/JSControl.cpp
toolkit/recordreplay/moz.build
xpcom/base/CycleCollectedJSRuntime.cpp
xpcom/base/DeferredFinalize.cpp
xpcom/base/DeferredFinalize.h
--- a/dom/base/nsWrapperCache.cpp
+++ b/dom/base/nsWrapperCache.cpp
@@ -34,19 +34,20 @@ void nsWrapperCache::HoldJSObjects(void*
 void nsWrapperCache::SetWrapperJSObject(JSObject* aWrapper) {
   mWrapper = aWrapper;
   UnsetWrapperFlags(kWrapperFlagsMask);
 
   if (aWrapper && !JS::ObjectIsTenured(aWrapper)) {
     CycleCollectedJSRuntime::Get()->NurseryWrapperAdded(this);
   }
 
-  if (mozilla::recordreplay::IsReplaying()) {
-    mozilla::recordreplay::SetWeakPointerJSRoot(this, aWrapper);
-  }
+  // Never collect the wrapper object while recording or replaying, to avoid
+  // non-deterministic behaviors if the cache is emptied and then refilled at
+  // a different point when replaying.
+  recordreplay::HoldJSObject(aWrapper);
 }
 
 void nsWrapperCache::ReleaseWrapper(void* aScriptObjectHolder) {
   // If the behavior here changes in a substantive way, you may need
   // to update css::Rule::UnlinkDeclarationWrapper as well.
   if (PreservingWrapper()) {
     SetPreservingWrapper(false);
     cyclecollector::DropJSObjectsImpl(aScriptObjectHolder);
@@ -93,22 +94,16 @@ static void DebugWrapperTraceCallback(JS
       static_cast<DebugWrapperTraversalCallback*>(aClosure);
   if (aPtr.is<JSObject>()) {
     callback->NoteJSChild(aPtr);
   }
 }
 
 void nsWrapperCache::CheckCCWrapperTraversal(void* aScriptObjectHolder,
                                              nsScriptObjectTracer* aTracer) {
-  // Skip checking if we are recording or replaying, as calling
-  // GetWrapperPreserveColor() can cause the cache's wrapper to be cleared.
-  if (recordreplay::IsRecordingOrReplaying()) {
-    return;
-  }
-
   JSObject* wrapper = GetWrapperPreserveColor();
   if (!wrapper) {
     return;
   }
 
   DebugWrapperTraversalCallback callback(wrapper);
 
   // The CC traversal machinery cannot trigger GC; however, the analysis cannot
--- a/dom/base/nsWrapperCache.h
+++ b/dom/base/nsWrapperCache.h
@@ -77,40 +77,32 @@ static_assert(sizeof(void*) == 4, "Only 
  * necessary. Instead a class hook (objectMovedOp) is provided that is called
  * when an object is moved and is responsible for ensuring pointers are
  * updated. It does this by calling UpdateWrapper() on the wrapper
  * cache. SetWrapper() asserts that the hook is implemented for any wrapper set.
  *
  * A number of the methods are implemented in nsWrapperCacheInlines.h because we
  * have to include some JS headers that don't play nicely with the rest of the
  * codebase. Include nsWrapperCacheInlines.h if you need to call those methods.
- *
- * When recording or replaying an execution, wrapper caches are instrumented so
- * that they behave consistently even if the GC executes at different points
- * and collects different objects.
  */
 
 class nsWrapperCache {
  public:
   NS_DECLARE_STATIC_IID_ACCESSOR(NS_WRAPPERCACHE_IID)
 
   nsWrapperCache()
       : mWrapper(nullptr),
         mFlags(0)
 #ifdef BOOL_FLAGS_ON_WRAPPER_CACHE
         ,
         mBoolFlags(0)
 #endif
   {
   }
   ~nsWrapperCache() {
-    // Clear any JS root associated with this cache while replaying.
-    if (mozilla::recordreplay::IsReplaying()) {
-      mozilla::recordreplay::SetWeakPointerJSRoot(this, nullptr);
-    }
     // Preserved wrappers should never end up getting cleared, but this can
     // happen during shutdown when a leaked wrapper object is finalized, causing
     // its wrapper to be cleared.
     MOZ_ASSERT(!PreservingWrapper() || js::RuntimeIsBeingDestroyed(),
                "Destroying cache with a preserved wrapper!");
   }
 
   /**
@@ -140,33 +132,16 @@ class nsWrapperCache {
    * of being finalized.
    *
    * This should only be called if you really need to see the raw contents of
    * this cache, for example as part of finalization. Don't store the result
    * anywhere or pass it into JSAPI functions that may cause the value to
    * escape.
    */
   JSObject* GetWrapperMaybeDead() const {
-    // Keep track of accesses on the cache when recording or replaying an
-    // execution. Accesses during a GC (when thread events are disallowed)
-    // fetch the underlying object without making sure the returned value
-    // is consistent between recording and replay.
-    if (mozilla::recordreplay::IsRecordingOrReplaying() &&
-        !mozilla::recordreplay::AreThreadEventsDisallowed() &&
-        !mozilla::recordreplay::HasDivergedFromRecording()) {
-      bool success = mozilla::recordreplay::RecordReplayValue(!!mWrapper);
-      if (mozilla::recordreplay::IsReplaying()) {
-        if (success) {
-          MOZ_RELEASE_ASSERT(mWrapper);
-        } else {
-          const_cast<nsWrapperCache*>(this)->ClearWrapper();
-        }
-      }
-    }
-
     return mWrapper;
   }
 
 #ifdef DEBUG
  private:
   static bool HasJSObjectMovedOp(JSObject* aWrapper);
 
  public:
--- a/dom/bindings/BindingUtils.h
+++ b/dom/bindings/BindingUtils.h
@@ -1363,42 +1363,29 @@ inline mozilla::dom::ReflectionScope Get
 
 inline mozilla::dom::ReflectionScope GetReflectionScope(
     const ParentObject& aParentObject) {
   return aParentObject.mReflectionScope;
 }
 
 template <class T>
 inline void ClearWrapper(T* p, nsWrapperCache* cache, JSObject* obj) {
-  // Skip clearing the wrapper when replaying. This method is called during
-  // finalization of |obj|, and when replaying a strong reference is kept on
-  // the contents of the cache: since |obj| is being finalized, the cache
-  // cannot point to |obj|, and clearing here won't do anything.
-  // Additionally, the reference held on the cache may have already been
-  // released, if we are finalizing later than we did while recording, and the
-  // cache may have already been deleted.
-  if (!recordreplay::IsReplaying()) {
-    MOZ_ASSERT(cache->GetWrapperMaybeDead() == obj ||
-               (js::RuntimeIsBeingDestroyed() && !cache->GetWrapperMaybeDead()));
-    cache->ClearWrapper(obj);
-  }
+  MOZ_ASSERT(cache->GetWrapperMaybeDead() == obj ||
+             (js::RuntimeIsBeingDestroyed() && !cache->GetWrapperMaybeDead()));
+  cache->ClearWrapper(obj);
 }
 
 template <class T>
 inline void ClearWrapper(T* p, void*, JSObject* obj) {
   // QueryInterface to nsWrapperCache can't GC, we hope.
   JS::AutoSuppressGCAnalysis nogc;
 
-  // Skip clearing the wrapper when replaying, for the same reason as in the
-  // overload above: |p| may have been deleted and we cannot QI it.
-  if (!recordreplay::IsReplaying()) {
-    nsWrapperCache* cache;
-    CallQueryInterface(p, &cache);
-    ClearWrapper(p, cache, obj);
-  }
+  nsWrapperCache* cache;
+  CallQueryInterface(p, &cache);
+  ClearWrapper(p, cache, obj);
 }
 
 template <class T>
 inline void UpdateWrapper(T* p, nsWrapperCache* cache, JSObject* obj,
                           const JSObject* old) {
   JS::AutoAssertGCCallback inCallback;
   cache->UpdateWrapper(obj, old);
 }
@@ -2557,21 +2544,16 @@ bool ToSupportsIsOnPrimaryInheritanceCha
 // Get the size of allocated memory to associate with a binding JSObject for a
 // native object. This is supplied to the JS engine to allow it to schedule GC
 // when necessary.
 //
 // This function supplies a default value and is overloaded for specific native
 // object types.
 inline size_t BindingJSObjectMallocBytes(void* aNativePtr) { return 0; }
 
-// Register a thing which DeferredFinalize might be called on during GC
-// finalization. See DeferredFinalize.h
-template <class T>
-static void RecordReplayRegisterDeferredFinalize(T* aObject);
-
 // The BindingJSObjectCreator class is supposed to be used by a caller that
 // wants to create and initialise a binding JSObject. After initialisation has
 // been successfully completed it should call ForgetObject().
 // The BindingJSObjectCreator object will root the JSObject until ForgetObject()
 // is called on it. If the native object for the binding is refcounted it will
 // also hold a strong reference to it, that reference is transferred to the
 // JSObject (which holds the native in a slot) when ForgetObject() is called. If
 // the BindingJSObjectCreator object is destroyed and ForgetObject() was never
@@ -2627,17 +2609,20 @@ class MOZ_STACK_CLASS BindingJSObjectCre
       JS::AddAssociatedMemory(aReflector, mallocBytes,
                               JS::MemoryUse::DOMBinding);
     }
   }
 
   void InitializationSucceeded() {
     T* pointer;
     mNative.forget(&pointer);
-    RecordReplayRegisterDeferredFinalize<T>(pointer);
+
+    // Never collect binding objects while recording or replaying, to avoid
+    // non-deterministically releasing references during finalization.
+    recordreplay::HoldJSObject(mReflector);
 
     mReflector = nullptr;
   }
 
  private:
   struct OwnedNative {
     // Make sure the native objects inherit from NonRefcountedDOMObject so
     // that we log their ctor and dtor.
@@ -2722,45 +2707,30 @@ struct DeferredFinalizerImpl {
 
 template <class T, bool isISupports = IsBaseOf<nsISupports, T>::value>
 struct DeferredFinalizer {
   static void AddForDeferredFinalization(T* aObject) {
     typedef DeferredFinalizerImpl<T> Impl;
     DeferredFinalize(Impl::AppendDeferredFinalizePointer,
                      Impl::DeferredFinalize, aObject);
   }
-
-  static void RecordReplayRegisterDeferredFinalize(T* aObject) {
-    typedef DeferredFinalizerImpl<T> Impl;
-    RecordReplayRegisterDeferredFinalizeThing(
-        Impl::AppendDeferredFinalizePointer, Impl::DeferredFinalize, aObject);
-  }
 };
 
 template <class T>
 struct DeferredFinalizer<T, true> {
   static void AddForDeferredFinalization(T* aObject) {
     DeferredFinalize(reinterpret_cast<nsISupports*>(aObject));
   }
-
-  static void RecordReplayRegisterDeferredFinalize(T* aObject) {
-    RecordReplayRegisterDeferredFinalizeThing(nullptr, nullptr, aObject);
-  }
 };
 
 template <class T>
 static void AddForDeferredFinalization(T* aObject) {
   DeferredFinalizer<T>::AddForDeferredFinalization(aObject);
 }
 
-template <class T>
-static void RecordReplayRegisterDeferredFinalize(T* aObject) {
-  DeferredFinalizer<T>::RecordReplayRegisterDeferredFinalize(aObject);
-}
-
 // This returns T's CC participant if it participates in CC and does not inherit
 // from nsISupports. Otherwise, it returns null. QI should be used to get the
 // participant if T inherits from nsISupports.
 template <class T, bool isISupports = IsBaseOf<nsISupports, T>::value>
 class GetCCParticipant {
   // Helper for GetCCParticipant for classes that participate in CC.
   template <class U>
   static constexpr nsCycleCollectionParticipant* GetHelper(
@@ -2860,17 +2830,16 @@ bool CreateGlobal(JSContext* aCx, T* aNa
 
   JSAutoRealm ar(aCx, aGlobal);
 
   {
     js::SetReservedSlot(aGlobal, DOM_OBJECT_SLOT, JS::PrivateValue(aNative));
     NS_ADDREF(aNative);
 
     aCache->SetWrapper(aGlobal);
-    RecordReplayRegisterDeferredFinalize<T>(aNative);
 
     dom::AllocateProtoAndIfaceCache(
         aGlobal, CreateGlobalOptions<T>::ProtoAndIfaceCacheKind);
 
     if (!CreateGlobalOptions<T>::PostCreateGlobal(aCx, aGlobal)) {
       return false;
     }
   }
--- a/dom/xbl/nsXBLBinding.cpp
+++ b/dom/xbl/nsXBLBinding.cpp
@@ -858,19 +858,22 @@ nsresult nsXBLBinding::DoInitJSClass(JSC
     // we can guarantee that in XBLFinalize this will be non-null.
     // Note that we can't just store aProtoBinding in the private and
     // addref/release the nsXBLDocumentInfo through it, because cycle
     // collection doesn't seem to work right if the private is not an
     // nsISupports.
     nsXBLDocumentInfo* docInfo = aProtoBinding->XBLDocumentInfo();
     ::JS_SetPrivate(proto, docInfo);
     NS_ADDREF(docInfo);
-    RecordReplayRegisterDeferredFinalize(docInfo);
     JS_SetReservedSlot(proto, 0, JS::PrivateValue(aProtoBinding));
 
+    // Don't collect the proto while recording/replaying, to avoid
+    // non-deterministically releasing the docInfo reference.
+    recordreplay::HoldJSObject(proto);
+
     // Next, enter the realm of the property holder, wrap the proto, and
     // stick it on.
     JSAutoRealm ar3(cx, holder);
     if (!JS_WrapObject(cx, &proto) ||
         !JS_DefineUCProperty(cx, holder, aClassName.get(), -1, proto,
                              JSPROP_READONLY | JSPROP_PERMANENT)) {
       return NS_ERROR_OUT_OF_MEMORY;
     }
--- a/js/xpconnect/src/SandboxPrivate.h
+++ b/js/xpconnect/src/SandboxPrivate.h
@@ -28,18 +28,21 @@ class SandboxPrivate : public nsIGlobalO
     RefPtr<SandboxPrivate> sbp = new SandboxPrivate(principal);
     sbp->SetWrapper(global);
     sbp->PreserveWrapper(ToSupports(sbp.get()));
 
     // Pass on ownership of sbp to |global|.
     // The type used to cast to void needs to match the one in GetPrivate.
     nsIScriptObjectPrincipal* sop =
         static_cast<nsIScriptObjectPrincipal*>(sbp.forget().take());
-    mozilla::RecordReplayRegisterDeferredFinalizeThing(nullptr, nullptr, sop);
     JS_SetPrivate(global, sop);
+
+    // Never collect the global while recording or replaying, so that the
+    // principal reference is not released at a non-deterministic point.
+    mozilla::recordreplay::HoldJSObject(global);
   }
 
   static SandboxPrivate* GetPrivate(JSObject* obj) {
     // The type used to cast to void needs to match the one in Create.
     return static_cast<SandboxPrivate*>(
         static_cast<nsIScriptObjectPrincipal*>(JS_GetPrivate(obj)));
   }
 
--- a/js/xpconnect/src/XPCWrappedNative.cpp
+++ b/js/xpconnect/src/XPCWrappedNative.cpp
@@ -481,35 +481,31 @@ static nsresult FinishCreate(JSContext* 
 
 // This ctor is used if this object will have a proto.
 XPCWrappedNative::XPCWrappedNative(already_AddRefed<nsISupports>&& aIdentity,
                                    XPCWrappedNativeProto* aProto)
     : mMaybeProto(aProto), mSet(aProto->GetSet()) {
   MOZ_ASSERT(NS_IsMainThread());
 
   mIdentity = aIdentity;
-  RecordReplayRegisterDeferredFinalizeThing(nullptr, nullptr, mIdentity);
-
   mFlatJSObject.setFlags(FLAT_JS_OBJECT_VALID);
 
   MOZ_ASSERT(mMaybeProto, "bad ctor param");
   MOZ_ASSERT(mSet, "bad ctor param");
 }
 
 // This ctor is used if this object will NOT have a proto.
 XPCWrappedNative::XPCWrappedNative(already_AddRefed<nsISupports>&& aIdentity,
                                    XPCWrappedNativeScope* aScope,
                                    already_AddRefed<XPCNativeSet>&& aSet)
 
     : mMaybeScope(TagScope(aScope)), mSet(aSet) {
   MOZ_ASSERT(NS_IsMainThread());
 
   mIdentity = aIdentity;
-  RecordReplayRegisterDeferredFinalizeThing(nullptr, nullptr, mIdentity);
-
   mFlatJSObject.setFlags(FLAT_JS_OBJECT_VALID);
 
   MOZ_ASSERT(aScope, "bad ctor param");
   MOZ_ASSERT(mSet, "bad ctor param");
 }
 
 XPCWrappedNative::~XPCWrappedNative() { Destroy(); }
 
@@ -521,23 +517,18 @@ void XPCWrappedNative::Destroy() {
   XPCWrappedNativeScope* scope = GetScope();
   if (scope) {
     Native2WrappedNativeMap* map = scope->GetWrappedNativeMap();
     MOZ_ASSERT(map->Find(GetIdentityObject()) != this);
   }
 #endif
 
   if (mIdentity) {
-    // Either release mIdentity immediately or defer the release. When
-    // recording or replaying the release must always be deferred, so that
-    // DeferredFinalize matches the earlier call to
-    // RecordReplayRegisterDeferredFinalizeThing.
     XPCJSRuntime* rt = GetRuntime();
-    if ((rt && rt->GetDoingFinalization()) ||
-        recordreplay::IsRecordingOrReplaying()) {
+    if (rt && rt->GetDoingFinalization()) {
       DeferredFinalize(mIdentity.forget().take());
     } else {
       mIdentity = nullptr;
     }
   }
 
   mMaybeScope = nullptr;
 }
@@ -550,16 +541,20 @@ inline void XPCWrappedNative::SetFlatJSO
   MOZ_ASSERT(!mFlatJSObject);
   MOZ_ASSERT(object);
 
   JS::AddAssociatedMemory(object, sizeof(*this) * GCMemoryFactor,
                           JS::MemoryUse::XPCWrappedNative);
 
   mFlatJSObject = object;
   mFlatJSObject.setFlags(FLAT_JS_OBJECT_VALID);
+
+  // Never collect the wrapper object while recording or replaying, to avoid
+  // non-deterministically releasing references during finalization.
+  recordreplay::HoldJSObject(object);
 }
 
 inline void XPCWrappedNative::UnsetFlatJSObject() {
   MOZ_ASSERT(mFlatJSObject);
 
   JS::RemoveAssociatedMemory(mFlatJSObject.unbarrieredGetPtr(),
                              sizeof(*this) * GCMemoryFactor,
                              JS::MemoryUse::XPCWrappedNative);
@@ -757,20 +752,18 @@ void XPCWrappedNative::FlatJSObjectFinal
 #ifdef DEBUG
       JS_UpdateWeakPointerAfterGCUnbarriered(&jso);
       MOZ_ASSERT(!jso);
 #endif
       to->JSObjectFinalized();
     }
 
     // We also need to release any native pointers held...
-    // As for XPCWrappedNative::Destroy, when recording or replaying the
-    // release must always be deferred.
     RefPtr<nsISupports> native = to->TakeNative();
-    if (native && (GetRuntime() || recordreplay::IsRecordingOrReplaying())) {
+    if (native && GetRuntime()) {
       DeferredFinalize(native.forget().take());
     }
 
     to->SetInterface(nullptr);
   }
 
   nsWrapperCache* cache = nullptr;
   CallQueryInterface(mIdentity, &cache);
@@ -1028,17 +1021,16 @@ nsresult XPCWrappedNative::InitTearOff(J
 
   if (!mSet->HasInterface(aInterface) && !ExtendSet(cx, aInterface)) {
     aTearOff->SetInterface(nullptr);
     return NS_ERROR_NO_INTERFACE;
   }
 
   aTearOff->SetInterface(aInterface);
   aTearOff->SetNative(qiResult);
-  RecordReplayRegisterDeferredFinalizeThing(nullptr, nullptr, qiResult);
 
   if (needJSObject && !InitTearOffJSObject(cx, aTearOff)) {
     return NS_ERROR_OUT_OF_MEMORY;
   }
 
   return NS_OK;
 }
 
--- a/js/xpconnect/src/XPCWrappedNativeProto.cpp
+++ b/js/xpconnect/src/XPCWrappedNativeProto.cpp
@@ -23,18 +23,16 @@ XPCWrappedNativeProto::XPCWrappedNativeP
   // by finalization of the JSObject (or explicitly if Init fails).
 
   MOZ_COUNT_CTOR(XPCWrappedNativeProto);
   MOZ_ASSERT(mScope);
 
 #ifdef DEBUG
   gDEBUG_LiveProtoCount++;
 #endif
-
-  RecordReplayRegisterDeferredFinalizeThing(nullptr, nullptr, mClassInfo);
 }
 
 XPCWrappedNativeProto::~XPCWrappedNativeProto() {
   MOZ_ASSERT(!mJSProtoObject, "JSProtoObject still alive");
 
   MOZ_COUNT_DTOR(XPCWrappedNativeProto);
 
 #ifdef DEBUG
@@ -52,16 +50,20 @@ bool XPCWrappedNativeProto::Init(JSConte
   mScriptable = scriptable;
 
   JS::RootedObject proto(cx, JS::GetRealmObjectPrototype(cx));
   mJSProtoObject = JS_NewObjectWithUniqueType(cx, &XPC_WN_Proto_JSClass, proto);
 
   bool success = !!mJSProtoObject;
   if (success) {
     JS_SetPrivate(mJSProtoObject, this);
+
+    // Never collect the proto object while recording or replaying, to avoid
+    // non-deterministically releasing references during finalization.
+    recordreplay::HoldJSObject(mJSProtoObject);
   }
 
   return success;
 }
 
 void XPCWrappedNativeProto::JSProtoObjectFinalized(JSFreeOp* fop,
                                                    JSObject* obj) {
   MOZ_ASSERT(obj == mJSProtoObject, "huh?");
--- a/mfbt/RecordReplay.cpp
+++ b/mfbt/RecordReplay.cpp
@@ -57,26 +57,18 @@ namespace recordreplay {
           Macro(                                                               \
               InternalInvalidateRecording, (const char* aWhy),                 \
               (aWhy)) Macro(InternalDestroyPLDHashTableCallbacks,              \
                             (const PLDHashTableOps* aOps),                     \
                             (aOps)) Macro(InternalMovePLDHashTableContents,    \
                                           (const PLDHashTableOps* aFirstOps,   \
                                            const PLDHashTableOps* aSecondOps), \
                                           (aFirstOps, aSecondOps))             \
-              Macro(SetWeakPointerJSRoot,                                      \
-                    (const void* aPtr, JSObject* aJSObj), (aPtr, aJSObj))      \
-                  Macro(RegisterTrigger,                                       \
-                        (void* aObj, const std::function<void()>& aCallback),  \
-                        (aObj,                                                 \
-                         aCallback)) Macro(UnregisterTrigger, (void* aObj),    \
-                                           (aObj)) Macro(ActivateTrigger,      \
-                                                         (void* aObj), (aObj)) \
-                      Macro(ExecuteTriggers, (), ()) Macro(                    \
-                          InternalRecordReplayAssert,                          \
+              Macro(InternalHoldJSObject, (JSObject* aJSObj), (aJSObj))        \
+                  Macro(InternalRecordReplayAssert,                            \
                           (const char* aFormat, va_list aArgs),                \
                           (aFormat,                                            \
                            aArgs)) Macro(InternalRecordReplayAssertBytes,      \
                                          (const void* aData, size_t aSize),    \
                                          (aData, aSize))                       \
                           Macro(InternalRegisterThing, (void* aThing),         \
                                 (aThing)) Macro(InternalUnregisterThing,       \
                                                 (void* aThing), (aThing))      \
--- a/mfbt/RecordReplay.h
+++ b/mfbt/RecordReplay.h
@@ -182,64 +182,21 @@ static inline void InvalidateRecording(c
 static inline const PLDHashTableOps* GeneratePLDHashTableCallbacks(
     const PLDHashTableOps* aOps);
 static inline const PLDHashTableOps* UnwrapPLDHashTableCallbacks(
     const PLDHashTableOps* aOps);
 static inline void DestroyPLDHashTableCallbacks(const PLDHashTableOps* aOps);
 static inline void MovePLDHashTableContents(const PLDHashTableOps* aFirstOps,
                                             const PLDHashTableOps* aSecondOps);
 
-// Associate an arbitrary pointer with a JS object root while replaying. This
-// is useful for replaying the behavior of weak pointers.
-MFBT_API void SetWeakPointerJSRoot(const void* aPtr, JSObject* aJSObj);
-
-// API for ensuring that a function executes at a consistent point when
-// recording or replaying. This is primarily needed for finalizers and other
-// activity during a GC that can perform recorded events (because GCs can
-// occur at different times and behave differently between recording and
-// replay, thread events are disallowed during a GC). Triggers can be
-// registered at a point where thread events are allowed, then activated at
-// a point where thread events are not allowed. When recording, the trigger's
-// callback will execute at the next point when ExecuteTriggers is called on
-// the thread which originally registered the trigger (typically at the top of
-// the thread's event loop), and when replaying the callback will execute at
-// the same point, even if it was never activated.
-//
-// Below is an example of how this API can be used.
-//
-// // This structure's lifetime is managed by the GC.
-// struct GarbageCollectedHolder {
-//   GarbageCollectedHolder() {
-//     RegisterTrigger(this, [=]() { this->DestroyContents(); });
-//   }
-//   ~GarbageCollectedHolder() {
-//     UnregisterTrigger(this);
-//   }
-//
-//   void Finalize() {
-//     // During finalization, thread events are disallowed.
-//     if (IsRecordingOrReplaying()) {
-//       ActivateTrigger(this);
-//     } else {
-//       DestroyContents();
-//     }
-//   }
-//
-//   // This is free to release resources held by the system, communicate with
-//   // other threads or processes, and so forth. When replaying, this may
-//   // be called before the GC has actually collected this object, but since
-//   // the GC will have already collected this object at this point in the
-//   // recording, this object will never be accessed again.
-//   void DestroyContents();
-// };
-MFBT_API void RegisterTrigger(void* aObj,
-                              const std::function<void()>& aCallback);
-MFBT_API void UnregisterTrigger(void* aObj);
-MFBT_API void ActivateTrigger(void* aObj);
-MFBT_API void ExecuteTriggers();
+// Prevent a JS object from ever being collected while recording or replaying.
+// GC behavior is non-deterministic when recording/replaying, and preventing
+// an object from being collected ensures that finalizers which might interact
+// with the recording will not execute.
+static inline void HoldJSObject(JSObject* aJSObj);
 
 // Some devtools operations which execute in a replaying process can cause code
 // to run which did not run while recording. For example, the JS debugger can
 // run arbitrary JS while paused at a breakpoint, by doing an eval(). In such
 // cases we say that execution has diverged from the recording, and if recorded
 // events are encountered the associated devtools operation fails. This API can
 // be used to test for such cases and avoid causing the operation to fail.
 static inline bool HasDivergedFromRecording();
@@ -414,25 +371,18 @@ MOZ_MAKE_RECORD_REPLAY_WRAPPER(UnwrapPLD
 MOZ_MAKE_RECORD_REPLAY_WRAPPER_VOID(DestroyPLDHashTableCallbacks,
                                     (const PLDHashTableOps* aOps), (aOps))
 MOZ_MAKE_RECORD_REPLAY_WRAPPER_VOID(MovePLDHashTableContents,
                                     (const PLDHashTableOps* aFirstOps,
                                      const PLDHashTableOps* aSecondOps),
                                     (aFirstOps, aSecondOps))
 MOZ_MAKE_RECORD_REPLAY_WRAPPER_VOID(InvalidateRecording, (const char* aWhy),
                                     (aWhy))
-MOZ_MAKE_RECORD_REPLAY_WRAPPER_VOID(
-    RegisterWeakPointer,
-    (const void* aPtr, const std::function<void(bool)>& aCallback),
-    (aPtr, aCallback))
-MOZ_MAKE_RECORD_REPLAY_WRAPPER_VOID(UnregisterWeakPointer, (const void* aPtr),
-                                    (aPtr))
-MOZ_MAKE_RECORD_REPLAY_WRAPPER_VOID(WeakPointerAccess,
-                                    (const void* aPtr, bool aSuccess),
-                                    (aPtr, aSuccess))
+MOZ_MAKE_RECORD_REPLAY_WRAPPER_VOID(HoldJSObject, (JSObject* aObject),
+                                    (aObject))
 MOZ_MAKE_RECORD_REPLAY_WRAPPER_VOID(RecordReplayAssertBytes,
                                     (const void* aData, size_t aSize),
                                     (aData, aSize))
 MOZ_MAKE_RECORD_REPLAY_WRAPPER_VOID(RegisterThing, (void* aThing), (aThing))
 MOZ_MAKE_RECORD_REPLAY_WRAPPER_VOID(UnregisterThing, (void* aThing), (aThing))
 MOZ_MAKE_RECORD_REPLAY_WRAPPER(ThingIndex, size_t, 0, (void* aThing), (aThing))
 MOZ_MAKE_RECORD_REPLAY_WRAPPER(VirtualThingName, const char*, nullptr,
                                (void* aThing), (aThing))
--- a/toolkit/recordreplay/ProcessRecordReplay.cpp
+++ b/toolkit/recordreplay/ProcessRecordReplay.cpp
@@ -2,28 +2,27 @@
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* 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 "ProcessRecordReplay.h"
 
 #include "ipc/ChildInternal.h"
+#include "mozilla/dom/ScriptSettings.h"
 #include "mozilla/Compression.h"
 #include "mozilla/Maybe.h"
 #include "mozilla/Sprintf.h"
 #include "mozilla/StaticMutex.h"
 #include "DirtyMemoryHandler.h"
 #include "Lock.h"
 #include "MemorySnapshot.h"
 #include "ProcessRedirect.h"
 #include "ProcessRewind.h"
-#include "Trigger.h"
 #include "ValueIndex.h"
-#include "WeakPointer.h"
 #include "pratom.h"
 
 #include <fcntl.h>
 #include <unistd.h>
 
 namespace mozilla {
 namespace recordreplay {
 
@@ -144,18 +143,16 @@ MOZ_EXPORT void RecordReplayInterface_In
   Thread::InitializeThreads();
 
   Thread* thread = Thread::GetById(MainThreadId);
   MOZ_ASSERT(thread->Id() == MainThreadId);
 
   thread->BindToCurrent();
   thread->SetPassThrough(true);
 
-  InitializeTriggers();
-  InitializeWeakPointers();
   InitializeMemorySnapshots();
   Thread::SpawnAllThreads();
   InitializeCountdownThread();
   SetupDirtyMemoryHandler();
   InitializeMiddlemanCalls();
   Lock::InitializeLocks();
 
   // Don't create a stylo thread pool when recording or replaying.
@@ -387,12 +384,20 @@ MOZ_EXPORT size_t RecordReplayInterface_
 
 MOZ_EXPORT const char* RecordReplayInterface_InternalVirtualThingName(
     void* aThing) {
   void* vtable = *(void**)aThing;
   const char* name = SymbolNameRaw(vtable);
   return name ? name : "(unknown)";
 }
 
+MOZ_EXPORT void RecordReplayInterface_InternalHoldJSObject(JSObject* aJSObj) {
+  if (aJSObj) {
+    JSContext* cx = dom::danger::GetJSContext();
+    JS::PersistentRootedObject* root = new JS::PersistentRootedObject(cx);
+    *root = aJSObj;
+  }
+}
+
 }  // extern "C"
 
 }  // namespace recordreplay
 }  // namespace mozilla
--- a/toolkit/recordreplay/ProcessRecordReplay.h
+++ b/toolkit/recordreplay/ProcessRecordReplay.h
@@ -50,26 +50,17 @@ namespace recordreplay {
       /* Executed a nested callback (see Callback.h). */                  \
       _Macro(ExecuteCallback)                                             \
                                                                           \
       /* Finished executing nested callbacks in a library API (see        \
          Callback.h). */                                                  \
       _Macro(CallbacksFinished)                                           \
                                                                           \
       /* Restoring a data pointer used in a callback (see Callback.h). */ \
-      _Macro(RestoreCallbackData)                                         \
-                                                                          \
-      /* Called RegisterTrigger. */                                       \
-      _Macro(RegisterTrigger)                                             \
-                                                                          \
-      /* Executed a trigger within a call to ExecuteTriggers. */          \
-      _Macro(ExecuteTrigger)                                              \
-                                                                          \
-      /* Finished executing triggers within a call to ExecuteTriggers. */ \
-      _Macro(ExecuteTriggersFinished)
+      _Macro(RestoreCallbackData)
 
 // ID of an event in a thread's event stream. Each ID in the stream is followed
 // by data associated with the event.
 enum class ThreadEvent : uint32_t {
 #define DefineEnum(Kind) Kind,
   ForEachThreadEvent(DefineEnum)
 #undef DefineEnum
 
deleted file mode 100644
--- a/toolkit/recordreplay/Trigger.cpp
+++ /dev/null
@@ -1,189 +0,0 @@
-/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* vim: set ts=8 sts=2 et sw=2 tw=80: */
-/* 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 "Trigger.h"
-
-#include "ipc/ChildIPC.h"
-#include "mozilla/Maybe.h"
-#include "mozilla/StaticMutex.h"
-#include "mozilla/RecordReplay.h"
-#include "InfallibleVector.h"
-#include "ProcessRewind.h"
-#include "Thread.h"
-#include "ValueIndex.h"
-
-namespace mozilla {
-namespace recordreplay {
-
-// Information about each trigger.
-struct TriggerInfo {
-  // ID of the thread which registered this trigger.
-  size_t mThreadId;
-
-  // Callback to execute when the trigger is activated.
-  std::function<void()> mCallback;
-
-  // Number of times this trigger has been activated.
-  size_t mRegisterCount;
-
-  TriggerInfo(size_t aThreadId, const std::function<void()>& aCallback)
-      : mThreadId(aThreadId), mCallback(aCallback), mRegisterCount(1) {}
-};
-
-// All registered triggers.
-static ValueIndex* gTriggers;
-
-typedef std::unordered_map<void*, TriggerInfo> TriggerInfoMap;
-static TriggerInfoMap* gTriggerInfoMap;
-
-// Triggers which have been activated. This is protected by the global lock.
-static StaticInfallibleVector<size_t> gActivatedTriggers;
-
-static StaticMutexNotRecorded gTriggersMutex;
-
-void InitializeTriggers() {
-  gTriggers = new ValueIndex();
-  gTriggerInfoMap = new TriggerInfoMap();
-}
-
-extern "C" {
-
-MOZ_EXPORT void RecordReplayInterface_RegisterTrigger(
-    void* aObj, const std::function<void()>& aCallback) {
-  MOZ_RELEASE_ASSERT(aObj);
-  MOZ_RELEASE_ASSERT(!AreThreadEventsPassedThrough());
-
-  Thread* thread = Thread::Current();
-  if (thread->HasDivergedFromRecording()) {
-    return;
-  }
-  MOZ_RELEASE_ASSERT(thread->CanAccessRecording());
-
-  size_t id;
-  {
-    AutoOrderedAtomicAccess order(gTriggers);
-    StaticMutexAutoLock lock(gTriggersMutex);
-
-    TriggerInfoMap::iterator iter = gTriggerInfoMap->find(aObj);
-    if (iter != gTriggerInfoMap->end()) {
-      id = gTriggers->GetIndex(aObj);
-      MOZ_RELEASE_ASSERT(iter->second.mThreadId == thread->Id());
-      iter->second.mCallback = aCallback;
-      iter->second.mRegisterCount++;
-    } else {
-      id = gTriggers->Insert(aObj);
-      TriggerInfo info(thread->Id(), aCallback);
-      gTriggerInfoMap->insert(TriggerInfoMap::value_type(aObj, info));
-    }
-  }
-
-  RecordingEventSection res(thread);
-  MOZ_RELEASE_ASSERT(res.CanAccessEvents());
-
-  thread->Events().RecordOrReplayThreadEvent(ThreadEvent::RegisterTrigger);
-  thread->Events().CheckInput(id);
-}
-
-MOZ_EXPORT void RecordReplayInterface_UnregisterTrigger(void* aObj) {
-  MOZ_ASSERT(IsRecordingOrReplaying());
-  MOZ_RELEASE_ASSERT(!AreThreadEventsPassedThrough());
-
-  StaticMutexAutoLock lock(gTriggersMutex);
-
-  TriggerInfoMap::iterator iter = gTriggerInfoMap->find(aObj);
-  MOZ_RELEASE_ASSERT(iter != gTriggerInfoMap->end());
-  if (--iter->second.mRegisterCount == 0) {
-    gTriggerInfoMap->erase(iter);
-    gTriggers->Remove(aObj);
-  }
-}
-
-MOZ_EXPORT void RecordReplayInterface_ActivateTrigger(void* aObj) {
-  if (!IsRecording()) {
-    return;
-  }
-
-  StaticMutexAutoLock lock(gTriggersMutex);
-
-  size_t id = gTriggers->GetIndex(aObj);
-  gActivatedTriggers.emplaceBack(id);
-}
-
-static void InvokeTriggerCallback(size_t aId) {
-  void* obj;
-  std::function<void()> callback;
-  {
-    StaticMutexAutoLock lock(gTriggersMutex);
-    obj = const_cast<void*>(gTriggers->GetValue(aId));
-    TriggerInfoMap::iterator iter = gTriggerInfoMap->find(obj);
-    MOZ_RELEASE_ASSERT(iter != gTriggerInfoMap->end());
-    MOZ_RELEASE_ASSERT(iter->second.mThreadId == Thread::Current()->Id());
-    MOZ_RELEASE_ASSERT(iter->second.mRegisterCount);
-    MOZ_RELEASE_ASSERT(iter->second.mCallback);
-    callback = iter->second.mCallback;
-  }
-
-  callback();
-}
-
-static Maybe<size_t> RemoveTriggerCallbackForThreadId(size_t aThreadId) {
-  StaticMutexAutoLock lock(gTriggersMutex);
-  for (size_t i = 0; i < gActivatedTriggers.length(); i++) {
-    size_t id = gActivatedTriggers[i];
-    void* obj = const_cast<void*>(gTriggers->GetValue(id));
-    TriggerInfoMap::iterator iter = gTriggerInfoMap->find(obj);
-    MOZ_RELEASE_ASSERT(iter != gTriggerInfoMap->end());
-    if (iter->second.mThreadId == aThreadId) {
-      gActivatedTriggers.erase(&gActivatedTriggers[i]);
-      return Some(id);
-    }
-  }
-  return Nothing();
-}
-
-MOZ_EXPORT void RecordReplayInterface_ExecuteTriggers() {
-  Thread* thread = Thread::Current();
-  RecordingEventSection res(thread);
-  if (!res.CanAccessEvents()) {
-    return;
-  }
-
-  if (IsRecording()) {
-    // Invoke the callbacks for any triggers waiting for execution, including
-    // any whose callbacks are triggered by earlier callback invocations.
-    while (true) {
-      Maybe<size_t> id = RemoveTriggerCallbackForThreadId(thread->Id());
-      if (id.isNothing()) {
-        break;
-      }
-
-      thread->Events().WriteScalar((size_t)ThreadEvent::ExecuteTrigger);
-      thread->Events().WriteScalar(id.ref());
-      InvokeTriggerCallback(id.ref());
-    }
-    thread->Events().WriteScalar((size_t)ThreadEvent::ExecuteTriggersFinished);
-  } else {
-    // Execute the same callbacks which were executed at this point while
-    // recording.
-    while (true) {
-      ThreadEvent ev = (ThreadEvent)thread->Events().ReadScalar();
-      if (ev != ThreadEvent::ExecuteTrigger) {
-        if (ev != ThreadEvent::ExecuteTriggersFinished) {
-          child::ReportFatalError(Nothing(), "ExecuteTrigger Mismatch");
-          Unreachable();
-        }
-        break;
-      }
-      size_t id = thread->Events().ReadScalar();
-      InvokeTriggerCallback(id);
-    }
-  }
-}
-
-}  // extern "C"
-
-}  // namespace recordreplay
-}  // namespace mozilla
deleted file mode 100644
--- a/toolkit/recordreplay/Trigger.h
+++ /dev/null
@@ -1,21 +0,0 @@
-/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* vim: set ts=8 sts=2 et sw=2 tw=80: */
-/* 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 mozilla_recordreplay_Trigger_h
-#define mozilla_recordreplay_Trigger_h
-
-namespace mozilla {
-namespace recordreplay {
-
-// See RecordReplay.h for a description of the record/replay trigger API.
-
-// Initialize trigger state at the beginning of recording or replaying.
-void InitializeTriggers();
-
-}  // namespace recordreplay
-}  // namespace mozilla
-
-#endif  // mozilla_recordreplay_Trigger_h
deleted file mode 100644
--- a/toolkit/recordreplay/WeakPointer.cpp
+++ /dev/null
@@ -1,61 +0,0 @@
-/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* vim: set ts=8 sts=2 et sw=2 tw=80: */
-/* 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 "WeakPointer.h"
-
-#include "mozilla/dom/ScriptSettings.h"
-#include "mozilla/StaticMutex.h"
-#include "jsapi.h"
-
-#include <unordered_map>
-
-namespace mozilla {
-namespace recordreplay {
-
-typedef std::unordered_map<const void*, UniquePtr<JS::PersistentRootedObject>>
-    WeakPointerRootMap;
-static WeakPointerRootMap* gWeakPointerRootMap;
-
-static StaticMutexNotRecorded gWeakPointerMutex;
-
-static UniquePtr<JS::PersistentRootedObject> NewRoot(JSObject* aJSObj) {
-  MOZ_RELEASE_ASSERT(aJSObj);
-  JSContext* cx = dom::danger::GetJSContext();
-  UniquePtr<JS::PersistentRootedObject> root =
-      MakeUnique<JS::PersistentRootedObject>(cx);
-  *root = aJSObj;
-  return root;
-}
-
-extern "C" {
-
-MOZ_EXPORT void RecordReplayInterface_SetWeakPointerJSRoot(const void* aPtr,
-                                                           JSObject* aJSObj) {
-  MOZ_RELEASE_ASSERT(IsReplaying());
-
-  StaticMutexAutoLock lock(gWeakPointerMutex);
-
-  auto iter = gWeakPointerRootMap->find(aPtr);
-  if (iter != gWeakPointerRootMap->end()) {
-    if (aJSObj) {
-      *iter->second = aJSObj;
-    } else {
-      gWeakPointerRootMap->erase(aPtr);
-    }
-  } else if (aJSObj) {
-    gWeakPointerRootMap->insert(
-        WeakPointerRootMap::value_type(aPtr, NewRoot(aJSObj)));
-  }
-}
-
-}  // extern "C"
-
-void InitializeWeakPointers() {
-  gWeakPointerRootMap = new WeakPointerRootMap();
-}
-
-}  // namespace recordreplay
-}  // namespace mozilla
deleted file mode 100644
--- a/toolkit/recordreplay/WeakPointer.h
+++ /dev/null
@@ -1,21 +0,0 @@
-/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* vim: set ts=8 sts=2 et sw=2 tw=80: */
-/* 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 mozilla_recordreplay_WeakPointer_h
-#define mozilla_recordreplay_WeakPointer_h
-
-namespace mozilla {
-namespace recordreplay {
-
-// See RecordReplay.h for a description of the record/replay weak pointer API.
-
-// Initialize weak pointer state.
-void InitializeWeakPointers();
-
-}  // namespace recordreplay
-}  // namespace mozilla
-
-#endif  // mozilla_recordreplay_WeakPointer_h
--- a/toolkit/recordreplay/ipc/JSControl.cpp
+++ b/toolkit/recordreplay/ipc/JSControl.cpp
@@ -1,23 +1,25 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
  * vim: set ts=8 sts=2 et sw=2 tw=80:
  * 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 "JSControl.h"
 
+#include "mozilla/Base64.h"
 #include "mozilla/ClearOnShutdown.h"
 #include "mozilla/StaticPtr.h"
 #include "js/CharacterEncoding.h"
 #include "js/Conversions.h"
 #include "js/JSON.h"
 #include "js/PropertySpec.h"
 #include "ChildInternal.h"
+#include "MemorySnapshot.h"
 #include "ParentInternal.h"
 #include "nsImportModule.h"
 #include "rrIControl.h"
 #include "rrIReplay.h"
 #include "xpcprivate.h"
 
 using namespace JS;
 
--- a/toolkit/recordreplay/moz.build
+++ b/toolkit/recordreplay/moz.build
@@ -26,19 +26,17 @@ if CONFIG['OS_ARCH'] == 'Darwin' and CON
         'Lock.cpp',
         'MemorySnapshot.cpp',
         'MiddlemanCall.cpp',
         'ProcessRecordReplay.cpp',
         'ProcessRedirectDarwin.cpp',
         'ProcessRewind.cpp',
         'Thread.cpp',
         'ThreadSnapshot.cpp',
-        'Trigger.cpp',
         'ValueIndex.cpp',
-        'WeakPointer.cpp',
     ]
     SOURCES += [
         # ProcessRedirect includes udis86 directly and will not compile if the
         # udis86 headers are included elsewhere in the same compilation unit.
         'ProcessRedirect.cpp',
     ]
 else:
     UNIFIED_SOURCES += [
--- a/xpcom/base/CycleCollectedJSRuntime.cpp
+++ b/xpcom/base/CycleCollectedJSRuntime.cpp
@@ -1353,23 +1353,16 @@ void CycleCollectedJSRuntime::FinalizeDe
     mFinalizeRunnable->ReleaseNow(false);
     if (mFinalizeRunnable) {
       // If we re-entered ReleaseNow, we couldn't delete mFinalizeRunnable and
       // we need to just continue processing it.
       return;
     }
   }
 
-  // When recording or replaying, execute triggers that were activated recently
-  // by mozilla::DeferredFinalize. This will populate the deferred finalizer
-  // table with a consistent set of entries between the recording and replay.
-  if (recordreplay::IsRecordingOrReplaying()) {
-    recordreplay::ExecuteTriggers();
-  }
-
   if (mDeferredFinalizerTable.Count() == 0) {
     return;
   }
 
   mFinalizeRunnable =
       new IncrementalFinalizeRunnable(this, mDeferredFinalizerTable);
 
   // Everything should be gone now.
--- a/xpcom/base/DeferredFinalize.cpp
+++ b/xpcom/base/DeferredFinalize.cpp
@@ -7,53 +7,17 @@
 #include "mozilla/DeferredFinalize.h"
 
 #include "mozilla/Assertions.h"
 #include "mozilla/CycleCollectedJSRuntime.h"
 
 void mozilla::DeferredFinalize(nsISupports* aSupports) {
   CycleCollectedJSRuntime* rt = CycleCollectedJSRuntime::Get();
   MOZ_ASSERT(rt, "Should have a CycleCollectedJSRuntime by now");
-  if (mozilla::recordreplay::IsRecordingOrReplaying()) {
-    // RecordReplayRegisterDeferredFinalizeThing should have been called when
-    // the reference on this object was added earlier. Cause the reference to
-    // be released soon, at a consistent point in the recording and replay.
-    mozilla::recordreplay::ActivateTrigger(aSupports);
-  } else {
-    rt->DeferredFinalize(aSupports);
-  }
+  rt->DeferredFinalize(aSupports);
 }
 
 void mozilla::DeferredFinalize(DeferredFinalizeAppendFunction aAppendFunc,
                                DeferredFinalizeFunction aFunc, void* aThing) {
   CycleCollectedJSRuntime* rt = CycleCollectedJSRuntime::Get();
   MOZ_ASSERT(rt, "Should have a CycleCollectedJSRuntime by now");
-  if (mozilla::recordreplay::IsRecordingOrReplaying()) {
-    // As above, cause the finalization action to occur soon, at a consistent
-    // point in the recording and replay.
-    mozilla::recordreplay::ActivateTrigger(aThing);
-  } else {
-    rt->DeferredFinalize(aAppendFunc, aFunc, aThing);
-  }
+  rt->DeferredFinalize(aAppendFunc, aFunc, aThing);
 }
-
-static void RecordReplayDeferredFinalize(
-    DeferredFinalizeAppendFunction aAppendFunc, DeferredFinalizeFunction aFunc,
-    void* aThing) {
-  mozilla::recordreplay::UnregisterTrigger(aThing);
-
-  CycleCollectedJSRuntime* rt = CycleCollectedJSRuntime::Get();
-  if (aAppendFunc) {
-    rt->DeferredFinalize(aAppendFunc, aFunc, aThing);
-  } else {
-    rt->DeferredFinalize(reinterpret_cast<nsISupports*>(aThing));
-  }
-}
-
-void mozilla::RecordReplayRegisterDeferredFinalizeThing(
-    DeferredFinalizeAppendFunction aAppendFunc, DeferredFinalizeFunction aFunc,
-    void* aThing) {
-  if (mozilla::recordreplay::IsRecordingOrReplaying()) {
-    mozilla::recordreplay::RegisterTrigger(aThing, [=]() {
-      RecordReplayDeferredFinalize(aAppendFunc, aFunc, aThing);
-    });
-  }
-}
--- a/xpcom/base/DeferredFinalize.h
+++ b/xpcom/base/DeferredFinalize.h
@@ -22,25 +22,11 @@ typedef void* (*DeferredFinalizeAppendFu
 // function should free aData.
 typedef bool (*DeferredFinalizeFunction)(uint32_t aSlice, void* aData);
 
 void DeferredFinalize(DeferredFinalizeAppendFunction aAppendFunc,
                       DeferredFinalizeFunction aFunc, void* aThing);
 
 void DeferredFinalize(nsISupports* aSupports);
 
-// When recording or replaying, deferred finalizers are forced to run at the
-// same point during replay that they ran at while recording, even if there is
-// a JSObject associated with the reference which has not been collected yet
-// (since at this point the JSObject has been collected during the recording,
-// that JSObject will never be used again and its reference can be released).
-//
-// This requires that RecordReplayRegisterDeferredFinalizeThing() be called for
-// every thing which DeferredFinalize() will be called for at a later time.
-// Calls to these functions must be 1:1. When not recording or replaying, this
-// function is a no-op.
-void RecordReplayRegisterDeferredFinalizeThing(
-    DeferredFinalizeAppendFunction aAppendFunc, DeferredFinalizeFunction aFunc,
-    void* aThing);
-
 }  // namespace mozilla
 
 #endif  // mozilla_DeferredFinalize_h